feat(cli): folders command, search --all-folders, empty-search hint
Fixes from testing email search (docs/enhancements-2026-07-07.md): - New `folders` agent command lists the account's mailboxes (name, delimiter, selectable), INBOX first, so agents can discover archived mail outside INBOX. - `search --all-folders` sweeps every selectable mailbox; each hit carries a `folder` field, `skipped_folders` reports mailboxes the server refused, and --limit caps visible results across the sweep. The sweep deliberately skips EnsureFolderBaseline so a read-only search never mutates list --new state. - Empty search results include a generic `data.hint` with next steps. The hint is a fixed constant per mode, so the invisibility invariant holds: absent and policy-filtered mail produce byte-identical envelopes (codified in TestSearchEmptyHintIndistinguishableFromFiltered). - Skill and user docs: document `--text` full-text search as best-effort (server-dependent); recommend --subject-contains/--from. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+34
-8
@@ -36,7 +36,7 @@ This manual is for **using and administering** `emcli`. It assumes you have the
|
||||
- **Admin commands** (`init`, `account add/edit/remove`, `whitelist`, `config`, `audit`) require
|
||||
`EMCLI_ADMIN_KEY` and are for *you*, the human. They print human-readable text or open an
|
||||
interactive form. (`account list` is the one exception — it is also an agent command; see below.)
|
||||
- **Agent commands** (`list`, `get`, `search`, `ack`, `send`, `doctor`) require `EMCLI_KEY` (or
|
||||
- **Agent commands** (`list`, `get`, `search`, `folders`, `ack`, `send`, `doctor`) require `EMCLI_KEY` (or
|
||||
`EMCLI_ADMIN_KEY` as a superset) and are for the *agent*. They print one line of JSON and
|
||||
nothing else, so a program can consume them reliably. (`doctor` prints human-readable text but
|
||||
is authorised by the agent key — `EMCLI_KEY` alone is sufficient; `EMCLI_ADMIN_KEY` also works
|
||||
@@ -103,7 +103,7 @@ reconfigure accounts, whitelists, or audit settings.
|
||||
| Key | Holder | Authorises |
|
||||
|---|---|---|
|
||||
| `EMCLI_ADMIN_KEY` | Human / secrets manager | ALL commands (`account`, `whitelist`, `config`, `audit`, `init`, plus all agent commands) |
|
||||
| `EMCLI_KEY` | Agent orchestrator | Agent commands only (`list`, `get`, `search`, `ack`, `send`, `doctor`) |
|
||||
| `EMCLI_KEY` | Agent orchestrator | Agent commands only (`list`, `get`, `search`, `folders`, `ack`, `send`, `doctor`) |
|
||||
|
||||
`EMCLI_ADMIN_KEY` is a strict superset: a process with only the admin key can run agent commands
|
||||
too. A process with only `EMCLI_KEY` is refused with `emcli: this command requires EMCLI_ADMIN_KEY
|
||||
@@ -125,7 +125,7 @@ DEK for an admin command, even if it somehow knows the agent key.
|
||||
|
||||
| Command | Role required |
|
||||
|---|---|
|
||||
| `list`, `get`, `search`, `ack`, `send`, `doctor`, `account list` | Agent (`EMCLI_KEY` or `EMCLI_ADMIN_KEY`) |
|
||||
| `list`, `get`, `search`, `folders`, `ack`, `send`, `doctor`, `account list` | Agent (`EMCLI_KEY` or `EMCLI_ADMIN_KEY`) |
|
||||
| `account add/edit/remove`, `whitelist`, `config`, `audit` | Admin (`EMCLI_ADMIN_KEY` required) |
|
||||
|
||||
`account list` is dual-role: with the admin key it prints the full `NAME MODE IMAP USER` table;
|
||||
@@ -353,7 +353,7 @@ Clear it by passing an empty string (`--subject-regex ''`) or using the interact
|
||||
These are what the agent runs. Each prints exactly one JSON object (see section 8). They all take
|
||||
`--account` and most take `--folder` (default `INBOX`).
|
||||
|
||||
> **Reading never changes state.** `list`, `get`, and `search` are read-only. Only `ack` advances
|
||||
> **Reading never changes state.** `list`, `get`, `search`, and `folders` are read-only. Only `ack` advances
|
||||
> "what's been processed."
|
||||
|
||||
### `list` — message headers
|
||||
@@ -392,19 +392,44 @@ emcli search --account gmail --from boss@example.com
|
||||
emcli search --account gmail --subject-contains invoice
|
||||
emcli search --account gmail --text "quarterly report"
|
||||
emcli search --account gmail --since-date 2026-01-01T00:00:00Z --before-date 2026-02-01T00:00:00Z
|
||||
emcli search --account gmail --subject-contains invoice --all-folders # sweep every mailbox
|
||||
```
|
||||
|
||||
| Flag | Meaning |
|
||||
|---|---|
|
||||
| `--from` | Sender contains |
|
||||
| `--subject-contains` | Subject contains |
|
||||
| `--text` | Full-text search |
|
||||
| `--text` | Full-text search (best-effort — see below) |
|
||||
| `--all-folders` | Search every selectable mailbox (mutually exclusive with `--folder`) |
|
||||
| `--since-date` / `--before-date` | Date bounds, RFC 3339 (e.g. `2026-06-01T00:00:00Z`) |
|
||||
| `--limit` | Max results (default `50`) |
|
||||
|
||||
Returns the same headers-only shape as `list`. Searches the whole folder, regardless of
|
||||
new/acked state. Filtered (whitelisted-out) mail never appears.
|
||||
|
||||
With `--all-folders` each returned message also carries a `folder` field (UIDs are only unique
|
||||
per folder — pass it back via `--folder` when using `get`/`ack` on a hit), and
|
||||
`data.skipped_folders` lists any mailboxes the server refused to search. `--limit` caps total
|
||||
results across the sweep.
|
||||
|
||||
`--text` depends on the server having a full-text index; some IMAP servers return zero results
|
||||
for mail that `--subject-contains` finds. Prefer `--subject-contains`/`--from`; treat `--text` as
|
||||
best-effort.
|
||||
|
||||
When a search returns no messages, `data.hint` suggests next steps (list folders, try
|
||||
`--all-folders`, adjust criteria). The hint is deliberately generic: it is identical whether
|
||||
matching mail is absent, in another folder, or hidden by the inbound whitelist.
|
||||
|
||||
### `folders` — list mailboxes
|
||||
|
||||
```bash
|
||||
emcli folders --account gmail
|
||||
```
|
||||
|
||||
Returns every mailbox the account can see: `name`, `delimiter`, and `selectable` (folders with
|
||||
`selectable: false` are hierarchy placeholders and can't be used with `--folder`). INBOX sorts
|
||||
first. Useful before searching mail that may be archived outside INBOX.
|
||||
|
||||
### `ack` — mark messages processed
|
||||
|
||||
```bash
|
||||
@@ -543,7 +568,7 @@ server settings are right.
|
||||
|
||||
### Audit log
|
||||
|
||||
Every agent action (`list`, `get`, `search`, `ack`, `send`) — allowed or blocked — is recorded.
|
||||
Every agent action (`list`, `get`, `search`, `folders`, `ack`, `send`) — allowed or blocked — is recorded.
|
||||
|
||||
```bash
|
||||
emcli audit list # most recent 50
|
||||
@@ -573,7 +598,7 @@ are rejected by `config get` and `config set`.
|
||||
## 11. Troubleshooting
|
||||
|
||||
**"EMCLI_KEY is not set" / "must be base64 of exactly 32 bytes".** Set `EMCLI_KEY` to a valid
|
||||
base64-encoded 32-byte key (section 2). Agent commands (`list`, `get`, `search`, `ack`, `send`,
|
||||
base64-encoded 32-byte key (section 2). Agent commands (`list`, `get`, `search`, `folders`, `ack`, `send`,
|
||||
`doctor`) need this key.
|
||||
|
||||
**"this command requires EMCLI_ADMIN_KEY (admin privilege)".** Set `EMCLI_ADMIN_KEY` (section 2).
|
||||
@@ -633,7 +658,8 @@ emcli version
|
||||
emcli doctor [N | --account N] # connectivity/auth check
|
||||
emcli list --account N [--folder F] [--new] [--limit K] [--before U] [--since U]
|
||||
emcli get --account N [--folder F] --uid U
|
||||
emcli search --account N [--folder F] [--from A] [--subject-contains S] [--text S] [--since-date D] [--before-date D] [--limit K]
|
||||
emcli search --account N [--folder F | --all-folders] [--from A] [--subject-contains S] [--text S] [--since-date D] [--before-date D] [--limit K]
|
||||
emcli folders --account N
|
||||
emcli ack --account N [--folder F] --uid-list U1,U2,U3
|
||||
emcli send --account N --to A [--cc A] [--bcc A] --subject S --body B [--attach P]… [--reply-to U [--folder F]]
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user