--- name: emcli description: Read and send email through emcli, a guard-railed CLI gateway that mediates IMAP/SMTP so the agent never holds mail credentials. Use when a task involves checking or reading an inbox, listing or searching messages, fetching a message or its attachments, marking mail as processed, or sending or replying to email on the user's behalf. Works with Gmail and any IMAP/SMTP account; every command prints a single JSON object. Triggers include: check my email, read the inbox, list/search messages, get or download a message, reply to that email, send an email, process new mail. compatibility: Requires the emcli binary (run scripts/install.sh to fetch it; needs curl or wget and a Linux/macOS/Windows shell) and the EMCLI_KEY environment variable, which the orchestrator provides. Needs network access to the configured mail server. metadata: author: steve version: "0.4.0" homepage: "https://gitea.dcglab.co.uk/steve/emcli" --- # emcli — email for agents `emcli` is a command-line gateway for email. You (the agent) call its **agent commands** to read and send mail; the program holds the credentials and enforces the user's rules, so you never see a password and cannot bypass the limits. Every agent command prints exactly **one line of JSON** and sets its exit code to match. ## Security model — read this first - **You only run agent commands:** `list`, `get`, `search`, `ack`, `send`, `doctor`, and `account list` (to discover accounts). You are provided only `EMCLI_KEY` (the agent key), which authorises these and nothing else. Account *setup* (`account add/edit/remove`), passwords, whitelists, and config are the **user's** job (admin commands that require `EMCLI_ADMIN_KEY`) — do not run or suggest running `account add/edit/remove`, `whitelist`, `config`, `audit`, or `init`. `emcli` will refuse those with a privilege error. - **Never touch the secret key.** `EMCLI_KEY` is supplied in the environment by whoever launched you. Do not read it, print it, log it, pass it as an argument, or try to generate one. If it is missing, stop and tell the user (see "Files & first run"). - **Some mail is intentionally invisible.** The user may restrict which senders you can see and who you can email. Blocked or filtered results are normal — handle them, don't try to work around them (see Enforcement). ## Files & first run This skill ships more than this file. Paths like `AGENTIC-MANUAL.md` and `references/commands.md` are relative to this skill's directory; if one isn't on disk, fetch it from the raw base URL + path: ``` https://gitea.dcglab.co.uk/steve/emcli/raw/branch/main/skills/emcli/ ``` - **First-time setup** — installing the binary, the `EMCLI_KEY`, finding accounts: read **`AGENTIC-MANUAL.md`**. Only needed when emcli isn't set up yet. - **Full command detail** — every flag, JSON shapes, error codes: `references/commands.md`. **Per-session preflight** (quick): run `emcli version`; if it's not found, set up via `AGENTIC-MANUAL.md`. Confirm `EMCLI_KEY` is set *without printing it* (`test -n "$EMCLI_KEY"`); if empty, tell the user their orchestrator must provide `EMCLI_KEY` (the agent key). Then get the account name from the user. ## How to read every result Each command prints one JSON object: ```json { "error": false, "error_detail": {}, "data": { } } ``` Always check `error` first. - `error: false` → use `data`. - `error: true` → read `error_detail.code` and `error_detail.message`. The exit code is also non-zero. With `jq`: ```bash out=$(emcli list --account gmail --new) || true echo "$out" | jq -e '.error == false' >/dev/null && echo "$out" | jq '.data.messages' ``` Error codes you may get back: `config`, `db`, `network`, `auth`, `policy`, `not_found`, `usage`. A `policy` error means the user's rules blocked the action; `not_found` is returned both for missing mail **and** for mail you are not allowed to see — treat them the same. ## The core workflow: process new mail ```bash ACC=gmail # the account name the user gave you # 1. See what's new (unprocessed). Headers only — cheap. emcli list --account "$ACC" --new --limit 20 # 2. For a message of interest, fetch the full body + attachments. emcli get --account "$ACC" --uid 70314 # 3. Do the work (summarize, extract, draft a reply, etc.). # 4. Mark it processed so it stops showing under --new. This is deliberate — do it # only when you've actually handled the message. emcli ack --account "$ACC" --uid-list 70314 ``` Acking is the **only** command that changes state; `list`/`get`/`search` never do. Ack is safe to repeat and order-independent (you can ack several UIDs: `--uid-list 70314,70315,70320`). ## Sending mail ```bash emcli send --account "$ACC" \ --to alice@example.com --subject "Hello" --body "Plain-text body." # multiple recipients (repeat or comma-separate), cc/bcc, attachments: emcli send --account "$ACC" --to a@x.com --to b@x.com --cc boss@x.com \ --subject "Report" --body "see attached" --attach ./report.pdf # reply that threads correctly off a message you can see: emcli send --account "$ACC" --to a@x.com --subject "Re: Hi" --body "thanks" \ --reply-to 70314 --folder INBOX ``` Sending only works on read-write accounts. If you get `policy` / `ro_mode`, the account is read-only — tell the user; do not attempt another account without their say-so. ## Command quick reference | Command | Purpose | |---|---| | `emcli list --account A [--folder F] [--new] [--limit N] [--before U] [--since U]` | Message headers, newest first | | `emcli get --account A [--folder F] --uid U` | One full message (body + attachments) | | `emcli search --account A [--folder F] [--from X] [--subject-contains X] [--text X] [--since-date D] [--before-date D]` | Server-side search | | `emcli ack --account A [--folder F] --uid-list U1,U2` | Mark message(s) processed | | `emcli send --account A --to X [--cc X] [--bcc X] --subject S --body B [--attach P]… [--reply-to U]` | Send / reply | | `emcli account list` | Discover accounts: JSON `name` / `from` / `can_send` per account | Defaults: `--folder INBOX`, `--limit 50` (max 500). Dates are RFC 3339 (e.g. `2026-06-01T00:00:00Z`). UIDs come from `list`/`search` output. **Full reference** (every flag, exact JSON shapes for each command, attachment encoding, error codes, and the enforcement rules): `references/commands.md` — read it from disk, or fetch it from the raw base URL in "Files & first run" above if you don't have it locally. ## Enforcement awareness — work *with* the rules The user configures these; you cannot change them and shouldn't try. - **Read-only (RO) accounts** reject `send` (`policy` / `ro_mode`). - **Inbound whitelist / subject filter:** mail from disallowed senders (or non-matching subjects) is invisible — it won't appear in `list`/`search`, and `get`/`ack` on it return `not_found`. - **Outbound whitelist:** if any recipient isn't allowed, the **whole** send is blocked (`policy` / `whitelist_out`) — nothing is sent. Don't retry by dropping recipients silently; surface it to the user. ## Do / Don't - ✅ Check `error` on every call; report `policy`/`not_found`/`auth` outcomes plainly to the user. - ✅ `get` to read, then `ack` only after you've truly processed a message. - ✅ Discover accounts with `emcli account list`, or ask the user; keep bodies plain text. - ❌ Don't read, print, or invent `EMCLI_KEY` or any password. - ❌ Don't run admin commands (`account add/edit/remove`, `whitelist`, `config`, `audit`, `init`) — you have only `EMCLI_KEY` (agent key); `emcli` will refuse them with a privilege error. (`account list` is allowed — use it to discover accounts.) - ❌ Don't treat a blocked send or filtered message as a bug to route around — it's the user's policy.