/install eppie-email-cli
Eppie CLI agent skill\r
\r
Use this skill when an autonomous or semi-autonomous agent must operate eppie-console directly.\r
\r
eppie-console is a console mail client that can work with:\r
- Gmail,\r
- Outlook,\r
- Proton Mail,\r
- regular IMAP/SMTP email services,\r
- decentralized peer-to-peer Eppie mail.\r \r
Purpose\r
\r
Use eppie-console in a predictable, automation-friendly way to:\r
- inspect accounts,\r
- inspect folders,\r
- read messages,\r
- send messages,\r
- sync folders,\r
- initialize, unlock, and reset the local vault when needed by the task.\r
\r
This skill focuses on deterministic command execution, structured output handling, and explicit standard-input contracts.\r
\r
For end-to-end validation, coverage, and reproducible test scenarios, use
eppie-cli-agent-test-plan.md.\r \r
How to use this file\r
\r
Treat sections marked Normative as the required contract for agent execution.\r
Treat sections marked Reference as supporting material, examples, or quick navigation aids.\r
\r
Normative sections in this file:\r
Normative defaults for agents\rNormative rules\rNormative command patterns for agents\rNormative stdin contracts\rNormative execution policy for autonomous agents\rNormative error handling\r \r Reference sections in this file:\rReference: preferred installation\rReference: required inputs\rReference: launch modes\rReference: decision guide\rReference: end-to-end agent scenarios\rReference: command-specific notes and examples\rReference: recommended agent workflow\rReference: known limitations\rReference: contributing\r \r
Normative defaults for agents\r
\r
When the task requires executing eppie-console, use these defaults unless the task explicitly requires otherwise:\r
\r
- use
--non-interactive=true\r - use
--output=jsonfor agent automation unless text output is explicitly required\r - use
--unlock-password-stdin=truefor stateful commands that need an existing vault\r - use
--assume-yes=truefor destructive automation\r - provide
stdinexactly in the required order\r - use one consistent working directory per vault\r
\r
Canonical command patterns and exact
stdincontracts are defined later in this file. Use those sections as the single source of truth for agent execution.\r \r
Use this skill when\r
\r
- the task requires executing
eppie-console\r - the agent must inspect accounts, folders, messages, or contacts\r
- the agent must send a message\r
- the agent must synchronize a folder\r
- the agent must initialize or reset the local vault\r
- the agent must work with machine-readable CLI output\r \r
Do not use this skill when\r
\r
- the task does not require calling
eppie-console\r - the task is only to explain behavior conceptually without running commands\r
- the task is primarily about test coverage or validation workflows; use
eppie-cli-agent-test-plan.md\r - an interactive REPL session is not explicitly required but would be the only execution path\r
- the required executable or working directory is unavailable\r \r
Reference: preferred installation\r
\r Prefer the executable published in GitHub Releases.\r You can also build from source.\r \r Latest release page:\r
- https://github.com/Eppie-io/Eppie-CLI/releases/latest\r \r Direct download links:\r \r
Linux\r
- https://github.com/Eppie-io/Eppie-CLI/releases/latest/download/Eppie.CLI-linux-x64.tar.gz\r
- https://github.com/Eppie-io/Eppie-CLI/releases/latest/download/Eppie.CLI-linux-arm64.tar.gz\r \r
macOS\r
- https://github.com/Eppie-io/Eppie-CLI/releases/latest/download/Eppie.CLI-osx-x64.tar.gz\r
- https://github.com/Eppie-io/Eppie-CLI/releases/latest/download/Eppie.CLI-osx-arm64.tar.gz\r \r
Windows\r
- https://github.com/Eppie-io/Eppie-CLI/releases/latest/download/Eppie.CLI-win-x64.zip\r
- https://github.com/Eppie-io/Eppie-CLI/releases/latest/download/Eppie.CLI-win-arm64.zip\r
\r
After extracting the archive, use the
eppie-consoleexecutable directly.\r \r
Reference: required inputs\r
\r
Common inputs\r
- path to
eppie-console\r - working directory\r
- whether the task requires stateful access to an existing vault\r
- whether structured JSON output is required\r \r
Vault access inputs\r
- vault password\r
- whether the vault must be initialized first\r \r
Reading inputs\r
- account identifier for
-a; for agent workflows, use the account address string returned bylist-accountsindata[].address\r - folder name when folder-specific access is needed\r
- message
id\r - message
pk\r - page size and total limit for paged commands\r \r
Sending inputs\r
- sender address\r
- receiver address\r
- subject\r
- message body\r \r
Account creation inputs\r
- account type\r
- vault password\r
- structured JSON payload when required\r
- provider-specific secrets only through standard input when supported\r \r
Reference: launch modes\r
\r
Interactive mode\r
Use only when a REPL session is explicitly needed.\r \r Example:\r
eppie-console\r \r Typical sequence:\ropen\r- vault password\r
list-accounts\rexit\r \ropenis useful in interactive mode to open the local vault inside the current process.\r \r
Non-interactive mode\r
Prefer this mode for agents.\r \r Use:\r
eppie-console --non-interactive=true \x3Ccommand> [command options]\r \r To avoid passing the same startup flags on every call, you can put them inappsettings.jsonnext toeppie-console, for example:\r{ "non-interactive": true, "output": "json", "unlock-password-stdin": true }\r \reppie-consolealso reads configuration from environment variables.\r \r When the default .NET host configuration pipeline is used, environment variables overrideappsettings.json. Command-line arguments override both.\r \r For structured machine-readable output, also use:\r--output=json\r \r JSON responses use a normalized envelope:\rtype:result,status,warning, orerror\rcode: stable machine-readable outcome code\rdata: payload forresultandstatusresponses\rmeta: optional metadata forresultresponses; usuallynull, but paged read commands can return objects such as{"header":"All messages:","returned":5,"hasMore":true}\rmessage: human-readable warning/error text\r \r In--non-interactive=true --output=jsonmode, structured responses are emitted onstdoutwithout a preceding stack trace onstderrfor handled command failures such asunhandledException.\r \r In non-interactive mode, do not useopenas part of the agent workflow. It does not establish reusable state for later process launches. For stateful non-interactive commands, use--unlock-password-stdin=trueinstead.\r \r For all agent examples in this file,\x3Caccount>means the account address returned bylist-accountsindata[].address. Prefer that address string for-ainstead of the numericid, unless a command explicitly documents another identifier format.\r \r Common success-output examples:\r--non-interactive=true --output=json list-accounts(with accounts):\r{"type":"result","code":"accounts","data":[{"id":1,"address":"\x3Caddress>","accountType":"Dec"}],"meta":null}\r
--non-interactive=true --assume-yes=true --output=json reset:\r{"type":"status","code":"reset","data":null}\r
--non-interactive=true --unlock-password-stdin=true --output=json send ...:\r{"type":"status","code":"messageSent","data":{"subject":"\x3Csubject>","to":"\x3Creceiver>","from":"\x3Csender>"}}\r
--non-interactive=true --unlock-password-stdin=true --output=json sync-folder ...:\r{"type":"status","code":"folderSynced","data":{"account":"\x3Caccount>","folder":"Inbox"}}\r
--non-interactive=true --unlock-password-stdin=true --output=json show-all-messages ...:\r{"type":"result","code":"messages","data":[{"id":1,"pk":1,"to":["\x3Creceiver>"],"from":["\x3Csender>"],"folder":"Inbox","subject":"\x3Csubject>"}],"meta":{"header":"All messages:","returned":1,"hasMore":false}}\r \r Additional command-specific JSON result shapes are documented later inReference: command-specific notes and examples.\r \r Common text success-output examples:\r
open:\rThe application was opened successfully.\r
reset:\rThe application has just been reset.\r
add-account -t dec:\rAccount \x3Cgenerated-address> added (Dec).\r
send:\rMessage sent from \x3Csender> to \x3Creceiver>. Subject: \x3Csubject>\r
sync-folder -f Inbox:\rFolder 'Inbox' for account \x3Caccount> synchronized.\r \r
Normative rules\r
\r
- Treat
Normative command patterns for agentsandNormative stdin contractsas normative for agent workflows.\r - Put global launch options before
--and put the startup command after--.\r - Do not assume
opencreates a reusable session for future process launches.\r - Synchronize the relevant folder before reading when the task requires newly arrived messages.\r
- Do not guess provider-specific folder names; call
list-foldersfirst.\r - Use
-l/--limitwhen predictable payload size matters.\r - Do not pass account secrets in command-line arguments when structured standard input is supported.\r
\r
eppie-console openonly opens the vault inside the current process.\r It does not create a reusable session for future process launches.\r \r
Normative command patterns for agents\r
\r
Use this section as the single source of truth for agent command lines. Unless text output is explicitly required, all agent examples below use --output=json.\r
\r
| Task | Canonical command |\r
| --- | --- |\r
| Initialize a vault | --non-interactive=true --output=json -- init |\r
| List accounts | --non-interactive=true --unlock-password-stdin=true --output=json -- list-accounts |\r
| List folders | --non-interactive=true --unlock-password-stdin=true --output=json -- list-folders -a \x3Caccount> |\r
| Show all messages | --non-interactive=true --unlock-password-stdin=true --output=json -- show-all-messages -s 10 -l 10 |\r
| Show one message | --non-interactive=true --unlock-password-stdin=true --output=json -- show-message -a \x3Caccount> -f \x3Cfolder> -i \x3Cid> -k \x3Cpk> |\r
| Delete one message | --non-interactive=true --unlock-password-stdin=true --output=json -- delete-message -a \x3Caccount> -f \x3Cfolder> -i \x3Cid> -k \x3Cpk> |\r
| List contacts | --non-interactive=true --unlock-password-stdin=true --output=json -- list-contacts -s 10 -l 10 |\r
| Show contact messages | --non-interactive=true --unlock-password-stdin=true --output=json -- show-contact-messages -c \x3Ccontact> -s 10 -l 10 |\r
| Show folder messages | --non-interactive=true --unlock-password-stdin=true --output=json -- show-folder-messages -a \x3Caccount> -f \x3Cfolder> -s 10 -l 10 |\r
| Sync a folder | --non-interactive=true --unlock-password-stdin=true --output=json -- sync-folder -a \x3Caccount> -f \x3Cfolder> |\r
| Send a message | --non-interactive=true --unlock-password-stdin=true --output=json -- send -s \x3Csender> -r \x3Creceiver> -t "\x3Csubject>" |\r
| Reset local data | --non-interactive=true --assume-yes=true --output=json -- reset |\r
| Add a DEC account | --non-interactive=true --unlock-password-stdin=true --output=json -- add-account -t dec |\r
| Add a regular IMAP/SMTP account | --non-interactive=true --unlock-password-stdin=true --output=json -- add-account -t email --input-json-stdin |\r
| Add a Proton Mail account | --non-interactive=true --unlock-password-stdin=true --output=json -- add-account -t proton --input-json-stdin |\r
\r
Normative stdin contracts\r
\r
Provide stdin exactly in the documented order. Unless a command explicitly reads until end-of-stream, stop after the documented input has been written.\r
\r
| Command | Exact stdin contract | EOF required |\r
| --- | --- | --- |\r
| --non-interactive=true --output=json -- init | line 1: new vault password | no |\r
| --non-interactive=true --unlock-password-stdin=true --output=json -- list-accounts | line 1: vault password | no |\r
| --non-interactive=true --unlock-password-stdin=true --output=json -- list-folders -a \x3Caccount> | line 1: vault password | no |\r
| --non-interactive=true --unlock-password-stdin=true --output=json -- show-all-messages -s \x3Cpage-size> -l \x3Climit> | line 1: vault password | no |\r
| --non-interactive=true --unlock-password-stdin=true --output=json -- show-message -a \x3Caccount> -f \x3Cfolder> -i \x3Cid> -k \x3Cpk> | line 1: vault password | no |\r
| --non-interactive=true --unlock-password-stdin=true --output=json -- delete-message -a \x3Caccount> -f \x3Cfolder> -i \x3Cid> -k \x3Cpk> | line 1: vault password | no |\r
| --non-interactive=true --unlock-password-stdin=true --output=json -- list-contacts -s \x3Cpage-size> -l \x3Climit> | line 1: vault password | no |\r
| --non-interactive=true --unlock-password-stdin=true --output=json -- show-contact-messages -c \x3Ccontact> -s \x3Cpage-size> -l \x3Climit> | line 1: vault password | no |\r
| --non-interactive=true --unlock-password-stdin=true --output=json -- show-folder-messages -a \x3Caccount> -f \x3Cfolder> -s \x3Cpage-size> -l \x3Climit> | line 1: vault password | no |\r
| --non-interactive=true --unlock-password-stdin=true --output=json -- sync-folder -a \x3Caccount> -f \x3Cfolder> | line 1: vault password | no |\r
| --non-interactive=true --unlock-password-stdin=true --output=json -- send -s \x3Csender> -r \x3Creceiver> -t "\x3Csubject>" | line 1: vault password; line 2 and later: message body; then close stdin | yes |\r
| --non-interactive=true --assume-yes=true --output=json -- reset | no stdin | no |\r
| --non-interactive=true --unlock-password-stdin=true --output=json -- add-account -t dec | line 1: vault password | no |\r
| --non-interactive=true --unlock-password-stdin=true --output=json -- add-account -t email --input-json-stdin | line 1: vault password; remaining bytes: one JSON object | yes |\r
| --non-interactive=true --unlock-password-stdin=true --output=json -- add-account -t proton --input-json-stdin | line 1: vault password; remaining bytes: one JSON object | yes |\r
\r
Command summary\r
\r
| Command group | Needs existing vault | Use --unlock-password-stdin=true | Prefer --output=json | Notes |\r
| --- | --- | --- | --- | --- |\r
| init | no | no | yes for agent workflows | writes a new local vault password from stdin |\r
| reset | no | no | yes | destructive; also use --assume-yes=true |\r
| list-accounts, list-folders | yes | yes | yes | inspect vault content |\r
| show-all-messages, show-message, show-folder-messages, show-contact-messages | yes | yes | yes | use -l when payload size matters |\r
| sync-folder | yes | yes | yes | call before reading when fresh messages are required |\r
| send | yes | yes | yes | password first in stdin, then body until EOF |\r
| add-account -t dec | yes | yes | yes | simple vault-backed account creation |\r
| add-account -t email, add-account -t proton | yes | yes | yes | use --input-json-stdin; do not pass secrets in arguments |\r
\r
Avoid these mistakes\r
\r
- Do not use
openin a non-interactive workflow.\r - Do not guess provider-specific folder names; call
list-foldersfirst.\r - Do not rely only on process exit code when JSON output is available.\r
- Do not omit
-lwhen an agent needs a predictable maximum payload size.\r - Do not pass secrets in command-line arguments when structured standard input is supported.\r \r
Normative execution policy for autonomous agents\r
\r
Output handling\r
- Follow the
Normative defaults for agentsdefaults unless the task explicitly requires otherwise.\r - Parse
type,code,data,meta, andmessagefrom the normalized envelope.\r - Do not rely only on process exit code when structured output is available.\r
- Treat
warninganderrorresponses as non-success outcomes even when the process exits cleanly.\r \r
Standard input handling\r
- Follow
Normative stdin contractsexactly.\r - For
send, closestdinafter the last body line.\r - For commands using
--input-json-stdin, pass the password first when required, then the JSON payload.\r - Do not prepend labels, prompts, or extra blank lines to the documented
stdincontract.\r \r
Working directory handling\r
- Use one consistent working directory for related commands that operate on the same local vault.\r
- Use an isolated working directory when the run is disposable or destructive.\r \r
Account identifier handling\r
- For agent workflows, pass the account address from
list-accountsdata[].addressto-a.\r - Do not assume the numeric
idis accepted interchangeably by all commands.\r - Re-read the current address from
list-accountswhen a workflow depends on a newly created account.\r \r
Pagination handling\r
- Use both
-s/--page-sizeand-l/--limitin agent workflows when payload size must be predictable.\r - Treat
-sas page size only. Treat-las the total maximum number of records returned by the command.\r - If
-lis omitted, paged read commands return up to 20 records by default.\r - Use
meta.returnedto confirm how many records were actually returned.\r - Use
meta.hasMoreto decide whether another read with a higher-lor a narrower filter is needed.\r - Do not assume provider folders or message volume are small enough to omit
-l.\r \r
Reference: decision guide\r
\r
Use Normative command patterns for agents for the exact command line. This guide is only for choosing the next command.\r
\r
- inspect available accounts →
list-accounts\r - inspect available folders for an account →
list-folders\r - inspect recent messages across folders → sync the relevant folders when new messages are required, then use
show-all-messages\r - inspect messages in a specific folder →
list-folders, thensync-folder, thenshow-folder-messageswhen new messages are required\r - inspect one message →
show-message\r - delete one message →
delete-message\r - inspect contacts →
list-contacts\r - inspect messages for one contact →
show-contact-messages\r - send a message →
send\r - sync one folder →
list-folders, thensync-folder\r - initialize a vault →
init\r - reset local data →
reset\r \r
Reference: end-to-end agent scenarios\r
\r
This section is informative. Use Normative command patterns for agents for the exact command line and Normative stdin contracts for the exact stdin shape.\r
\r
Initialize a new vault, add an account, then verify it\r
\r Use this flow when the working directory does not yet contain the required local vault.\r \r
- initialize the vault with
init\r - add the required account with
add-account\r - verify the created account with
list-accounts\r - use the returned
data[].addressas\x3Caccount>in later commands\r \r Typical sequences:\r
- DEC account:
init→add-account -t dec→list-accounts\r - regular IMAP/SMTP account:
init→add-account -t email→list-accounts\r - Proton Mail account:
init→add-account -t proton→list-accounts\r \r Agent notes:\r - keep one consistent working directory across the whole sequence so all commands operate on the same local vault\r
- for
add-account -t emailandadd-account -t proton, provide the structured payload through standard input after the vault password as documented inNormative stdin contracts\r - after account creation, re-read the current address from
list-accountsinstead of assuming another identifier format\r \r
Read the latest message from a specific folder\r
\r Use this flow when the task requires a concrete folder such as inbox or sent mail and the messages may have changed since the last sync.\r \r
- inspect accounts with
list-accounts\r - inspect folders for the chosen account with
list-folders\r - choose the correct folder name from
data[].fullNameordata[].roles\r - synchronize that folder with
sync-folder\r - read a bounded message list with
show-folder-messages\r - open one returned message with
show-messageusing itsidandpk\r \r Typical sequence:\r
list-accounts→list-folders→sync-folder→show-folder-messages→show-message\r \r Agent notes:\r- do not guess the folder name; use the
fullNamereturned bylist-folders\r - use
-sand-lonshow-folder-messageswhen predictable payload size matters\r - when
meta.hasMoreistrue, treat the result as partial and increase-lonly if the task requires a larger set\r \r
Send a message from an existing account\r
\r Use this flow when the local vault already contains the sender account and the task is to deliver one message deterministically.\r \r
- inspect accounts with
list-accounts\r - choose the sender address from
data[].address\r - call
sendwith the sender, receiver, and subject\r - pass the vault password on the first
stdinline\r - pass the message body starting from line 2, then close
stdin\r \r Typical sequence:\r
list-accounts→send\r \r Agent notes:\r- if the sender is unknown or the workflow depends on a newly created account, refresh the account list before sending\r
- in
--non-interactive=truemode,sendreads the body until end-of-stream, so the agent should closestdinafter the last body line\r - prefer
--output=jsonand verify themessageSentstatus response instead of relying only on the process exit code\r \r
Reference: command-specific notes and examples\r
\r
The canonical command lines are defined in Normative command patterns for agents, and the exact inputs are defined in Normative stdin contracts. This section keeps only command-specific notes, result shapes, and examples that are easy to reference during agent execution.\r
\r
Init local vault\r
Notes:\r
- writes a new local vault password from
stdin\r \r
Show all messages\r
Notes:\r
-s/--page-sizecontrols only the page size\r-l/--limitcaps the total number of returned records and should be used by agents to keep payload size predictable\r- if
-lis omitted, paged read commands return up to 20 records by default\r - message items use the same address shape as
show-message:toandfromare arrays\r - paged result metadata can include
returnedandhasMore\r - agents should check
meta.hasMorebefore assuming the returned list is complete\r \r
List folders\r
Expected result shape:\r
{"type":"result","code":"folders","data":[{"fullName":"Inbox","unreadCount":0,"totalCount":0,"roles":["inbox"]}],"meta":{"account":"\x3Caccount>"}}\r \r Notes:\r\x3Caccount>should be the account address fromlist-accountsdata[].address\r- use this command before
show-folder-messagesorsync-folderinstead of guessing provider-specific folder names\r - Proton may expose names such as
All Sent; rely on the returnedfullName\r - use
roleswhen you need a machine-readable way to find folders such as inbox or sent mail\r \r
Delete one message\r
Notes:\r
- when called for a message outside
Trash, the command moves the message toTrash\r - when called for a message already in
Trash, the command deletes it permanently\r \r
Show contact messages\r
Notes:\r
- paged result metadata can include
returnedandhasMore\r \r
Show folder messages\r
Notes:\r
- paged result metadata can include
returnedandhasMore\r \r
Sync folder\r
Expected result shape:\r
{"type":"status","code":"folderSynced","data":{"account":"\x3Caccount>","folder":"Inbox"}}\r \r
Send message\r
Examples:\r
vault password\rHello from automation\rSecond body line\r- close
stdin\r \r Notes:\r - in
--non-interactive=truemode,sendreads the message body until end-of-stream\r - JSON success shape:
{"type":"status","code":"messageSent","data":{"subject":"\x3Csubject>","to":"\x3Creceiver>","from":"\x3Csender>"}}\r \r PowerShell example with the command line and exactstdinin one block:\r \r
@"\r
vault password\r
Hello from automation\r
Second body line\r
"@ | .\eppie-console --non-interactive=true --unlock-password-stdin=true --output=json -- send -s [email protected] -r [email protected] -t "Hello from automation"\r
```\r
\r
Linux / bash example with the command line and exact `stdin` in one block:\r
\r
```bash\r
cat \x3C\x3C'EOF_INPUT' | ./eppie-console --non-interactive=true --unlock-password-stdin=true --output=json -- send -s [email protected] -r [email protected] -t "Hello from automation"\r
vault password\r
Hello from automation\r
Second body line\r
EOF_INPUT\r
```\r
\r
Notes:\r
- on Linux and macOS, use `./eppie-console` after extracting the archive and making sure the file is executable.\r
\r
### Open local vault\r
Use `open` only in interactive mode when the agent is already inside a REPL session and must open the local vault in that same process.\r
\r
Notes:\r
- do not use `open` as part of the non-interactive agent workflow\r
\r
### Reset local data\r
Notes:\r
- `reset` is a destructive command and should be called with `--assume-yes=true` in automation or other non-interactive scripts.\r
\r
### Add DEC account\r
Expected result shape:\r
- `{"type":"result","code":"accountAdded","data":{"address":"\x3Cgenerated-address>","accountType":"Dec"},"meta":null}`\r
\r
### Add regular IMAP/SMTP account\r
Examples:\r
\r
JSON object:\r
\r
```json\r
{\r
"email": "[email protected]",\r
"accountPassword": "account password",\r
"imapServer": "imap.example.com",\r
"imapPort": 993,\r
"smtpServer": "smtp.example.com",\r
"smtpPort": 465\r
}\r
```\r
\r
`stdin` shape:\r
- first line: `vault password`\r
- remaining bytes:\r
\r
```json\r
{"email":"[email protected]","accountPassword":"account password","imapServer":"imap.example.com","imapPort":993,"smtpServer":"smtp.example.com","smtpPort":465}\r
```\r
\r
Notes:\r
- use this mode for regular IMAP/SMTP providers when browser-based OAuth is not needed\r
- do not pass account secrets in command-line arguments\r
- required structured properties are `email`, `accountPassword`, `imapServer`, `imapPort`, `smtpServer`, and `smtpPort`\r
- invalid structured input returns one of these machine-readable errors:\r
- `structuredStandardInputInvalidJson`\r
- `structuredStandardInputMissingProperty`\r
\r
### Add Proton Mail account\r
Examples:\r
\r
JSON object:\r
\r
```json\r
{\r
"email": "[email protected]",\r
"accountPassword": "account password",\r
"mailboxPassword": "mailbox password",\r
"twoFactorCode": "123456"\r
}\r
```\r
\r
`stdin` shape:\r
- first line: `vault password`\r
- remaining bytes:\r
\r
```json\r
{"email":"[email protected]","accountPassword":"account password","mailboxPassword":"mailbox password"}\r
```\r
\r
Notes:\r
- do not pass Proton secrets in command-line arguments\r
- `email` and `accountPassword` are required in structured input\r
- `mailboxPassword` is required only if the Proton flow asks for it\r
- `twoFactorCode` is required only if the Proton flow asks for it\r
- if the mailbox password is the same as the account password, repeat the same value in `mailboxPassword`\r
- invalid structured input returns one of these machine-readable errors:\r
- `structuredStandardInputInvalidJson`\r
- `structuredStandardInputMissingProperty`\r
\r
## Reference: recommended agent workflow\r
\r
1. Start from the defaults in `Normative defaults for agents`.\r
2. Choose the exact command line from `Normative command patterns for agents`.\r
3. Provide `stdin` in the exact shape defined in `Normative stdin contracts`.\r
4. Use the account address from `list-accounts` `data[].address` for `-a`.\r
5. Use an isolated working directory when the run is disposable or destructive.\r
6. For message reading:\r
- start with `list-folders` if the next step requires a specific folder name,\r
- synchronize the relevant folder before reading when you need newly arrived messages,\r
- then use `show-all-messages` when you need recent messages across all folders,\r
- then call `show-message` or `delete-message` with the returned `id` and `pk`.\r
7. In `--non-interactive=true` mode text output stops after the first page instead of prompting.\r
8. For common task chains, use `Reference: end-to-end agent scenarios` as a quick reference.\r
\r
## Reference: known limitations\r
\r
### Some commands still require explicit non-interactive inputs\r
`--non-interactive=true` suppresses interactive prompts, but it does not invent missing data.\r
\r
Commands that normally ask follow-up questions still need explicit arguments or `stdin` data.\r
The main examples are already covered above:\r
- `send` reads the message body until end-of-stream in `--non-interactive=true` mode\r
- `add-account -t email` and `add-account -t proton` should use `--input-json-stdin`\r
- destructive commands such as `reset` still need `--assume-yes=true`\r
- paged read commands still default to 20 records when `-l` is omitted\r
\r
## Normative error handling\r
\r
For agent automation, prefer deterministic handling over implicit retries.\r
\r
### Interpret the normalized envelope\r
\r
When `--output=json` is enabled, handle responses by `type` first:\r
\r
| `type` | Meaning | Agent action |\r
| --- | --- | --- |\r
| `result` | command returned data | parse `data` and `meta` |\r
| `status` | command completed successfully without a list-style result | read `data` when present |\r
| `warning` | handled problem or non-success condition | inspect `code`; do not assume success; stop unless the workflow explicitly defines a recovery step |\r
| `error` | handled failure | inspect `code`, `message`, and `data`; stop unless the input can be corrected deterministically |\r
\r
### Common machine-readable codes\r
\r
| `code` | Meaning | Typical agent response |\r
| --- | --- | --- |\r
| `invalidPassword` | vault password was rejected | stop; do not retry automatically with the same password |\r
| `structuredStandardInputInvalidJson` | structured payload is not valid JSON | fix serialization and retry once with corrected JSON |\r
| `structuredStandardInputMissingProperty` | required JSON property is missing or empty | provide the missing property and retry once |\r
| `unhandledException` | command failed and returned exception details | inspect `data.exceptionType` and command context; do not retry blindly |\r
\r
### Agent response rules\r
\r
- Parse `type` first, then `code`.\r
- Do not treat `warning` as success.\r
- Do not retry `invalidPassword` automatically.\r
- Retry only when the failure is deterministic and local to the request, such as malformed JSON or a missing structured property.\r
- When retrying after `structuredStandardInputInvalidJson` or `structuredStandardInputMissingProperty`, change only the invalid input and keep the command line unchanged.\r
- For `unhandledException`, record the command, the parsed `code`, and `data.exceptionType` when present.\r
- When a paged read returns `meta.hasMore: true`, treat that as an incomplete result set rather than an error.\r
\r
### Error-output examples\r
\r
Invalid password example:\r
- `{"type":"warning","code":"invalidPassword","message":"Warning: Invalid password.","data":null}`\r
\r
Unknown sender account example for `send`:\r
- `{"type":"error","code":"unhandledException","message":"An error has occurred: ...","data":{"exceptionType":"Tuvi.Core.Entities.AccountIsNotExistInDatabaseException"}}`\r
\r
Invalid Proton structured input example:\r
- `{"type":"error","code":"structuredStandardInputInvalidJson","message":"Error: The remaining standard input for the 'add-account' command is not valid JSON.","data":{"commandName":"add-account"}}`\r
\r
Missing Proton structured property example:\r
- `{"type":"error","code":"structuredStandardInputMissingProperty","message":"Error: The structured standard input for the 'add-account' command must contain a non-empty 'email' property.","data":{"commandName":"add-account","propertyName":"email"}}`\r
\r
## Reference: contributing\r
\r
Contributions are welcome.\r
\r
Please open issues for bugs, UX problems, and feature ideas.\r
Pull requests on GitHub are also welcome.\r
\r
Repository:\r
- https://github.com/Eppie-io/Eppie-CLI\r
- Make sure OpenClaw is installed (local or Docker)
- Run the install command in chat:
/install eppie-email-cli - After installation, invoke the skill by name or use
/eppie-email-cli - Provide required inputs per the skill's parameter spec and get structured output
What is Eppie Email CLI?
Control the eppie-console CLI mail client to inspect accounts, folders, read/send messages, sync folders, and manage vaults with JSON output and automation s... It is an AI Agent Skill for Claude Code / OpenClaw, with 103 downloads so far.
How do I install Eppie Email CLI?
Run "/install eppie-email-cli" in the OpenClaw or Claude Code chat to install it in one step — no extra setup required.
Is Eppie Email CLI free?
Yes, Eppie Email CLI is completely free, licensed under MIT-0. You can download, install and use it at no cost.
Which platforms does Eppie Email CLI support?
Eppie Email CLI is cross-platform and runs anywhere OpenClaw / Claude Code is available (cross-platform).
Who created Eppie Email CLI?
It is built and maintained by Eppie (@eppieapp); the current version is v0.2.0.