一键导入
emailops
Query, search, chat with, and compose/draft/send email from a local EmailOps mailbox via emailops-cli.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Query, search, chat with, and compose/draft/send email from a local EmailOps mailbox via emailops-cli.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Per-site visitor & pageview reports from a private (self-hosted) Umami instance.
Check your Google Calendar (read-only): today's / this week's / upcoming events as an agenda.
List your Claude Code sessions for any time window — start, duration, project, message counts, title, models, token usage, API-equivalent cost, and an A–D cost-efficiency grade with per-session recommendations, straight from the local transcripts.
Report the commits you authored across every git repo under one folder, for any time window (grouped by repo, with optional line churn).
Read and write notes in an Obsidian vault — list notes (all or modified in a window), show a note, and save the current chat as a note.
Time-tracking reports (hours per project / per day, tasks completed) from a Super Productivity data file.
| name | emailops |
| description | Query, search, chat with, and compose/draft/send email from a local EmailOps mailbox via emailops-cli. |
| version | 1.1.0 |
| author | community |
| license | MIT |
| platforms | ["macos","linux"] |
| metadata | {"hermes":{"tags":["Email","AI","Local-First","Privacy","Search","Chat","Compose","Drafts","Send","EmailOps"],"homepage":"https://github.com/emailops/emailops","related_skills":["himalaya"]}} |
| prerequisites | {"commands":["emailops-cli"]} |
emailops-cli is the headless front-end for EmailOps, a privacy-first,
AI-native email client. AI runs locally by default (embedded llama.cpp); email
data lives in a local SQLite database. This skill lets the agent read, search,
chat with, and compose / draft / send mail without opening the desktop app.
It complements the himalaya skill: Himalaya speaks raw IMAP/SMTP, while
emailops-cli adds Gmail/Outlook OAuth accounts plus local AI — semantic
chat over your mail, classification, and embeddings.
--jsonEvery command supports --json, which prints exactly one stable envelope
to stdout (logs always go to stderr). Parse this single shape regardless of
success or failure — never scrape the human-readable output:
{ "ok": true, "data": { /* result */ }, "error": null } // success
{ "ok": false, "data": null, "error": { "code": "not_found", "params": {}, "message": "" } } // failure
Add --quiet to suppress the stderr app-log stream when you only want the
envelope.
| Code | Meaning | Code | Meaning |
|---|---|---|---|
| 0 | ok | 5 | network / sync |
| 2 | invalid input | 6 | AI |
| 3 | not found | 130 | cancelled |
| 4 | auth | 1 | otherwise |
emailops-cli on PATH (emailops-cli --version to verify). It is built
from the EmailOps repo with the cli cargo feature; symlink the built
binary into ~/.local/bin if needed.doctor before anything else.Use whenever the user asks about their own email: searching messages, asking
"what did X say about Y?", listing accounts, reading a specific message, or
checking mailbox health. Also use it to write mail — save or edit a draft,
review existing drafts, or send a message. Prefer the cheap read paths
(doctor, accounts, emails, show, search) over chat unless the
question genuinely needs the local LLM.
| Command | Purpose | Cost |
|---|---|---|
emailops-cli doctor --json | Env readiness: DB, accounts, AI config. Loads no model. | cheap |
emailops-cli accounts --json | List configured accounts (id, email, provider). | cheap |
emailops-cli emails --json [--limit N] [--mailbox inbox|sent|spam|trash] | List recent emails. | cheap |
emailops-cli show <ID> --json | Full single email (headers + body). | cheap |
emailops-cli search '<query>' --json [--limit N] [--trace] | Full-text search across an account's mail. | cheap |
emailops-cli chat '<question>' --json [--trace] | Ask the local AI a question against your mail. | loads LLM |
emailops-cli stats --json | Per-account dashboard totals + coverage. | cheap |
emailops-cli compose --to … --subject … (--body …|--body-file …) --json | Save a draft (default) or --send to deliver. | cheap (write) |
emailops-cli drafts --json | List saved drafts, newest-first. | cheap |
emailops-cli draft <ID> --json | Show a single draft (recipients, subject, body, attachments). | cheap |
Scope to an account with --account <ID|EMAIL> (defaults to the single
enabled account). When multiple accounts exist, list them first and pass
--account explicitly.
Health check first (read-only, no model load):
emailops-cli doctor --json
Confirm data.ok == true and data.accountsEnabled > 0. If aiEnabled
is false, avoid chat and stick to search/read commands.
Resolve the account when there's more than one:
emailops-cli accounts --json
Use the returned id or email with --account.
Read paths for lookups (fast, safe even while the desktop app is open):
emailops-cli search 'invoice from acme' --json --limit 5
emailops-cli show <id> --json
emailops-cli emails --json --mailbox inbox --limit 25
AI path for natural-language questions (loads the local model):
emailops-cli chat 'what did the landlord say about the lease renewal?' --json
Add --trace to inspect routing, retrieval, tool calls, and the sources
behind the answer under data.trace.
Multi-turn: carry context across one-shot invocations by passing the
conversationId from a previous chat --json result:
emailops-cli chat 'and what date was that?' --json --conversation <conversationId>
Parse the envelope, branch on ok / exit code, and answer the user
from data. Surface error.message on failure.
compose is the single write path. By default it saves a draft (and, on
Gmail/Outlook accounts, also pushes it to the provider's Drafts folder — no
extra flag). Add --send to deliver immediately instead. Both paths return the
resulting record under data.
🚫 NEVER send without explicit user confirmation. Do not run
compose … --senduntil you have shown the user the full email about to be sent — the sending account, every To/Cc recipient, the subject, the complete body, and any attachments — and the user has explicitly approved that exact message. No exceptions: not for "obvious" replies, not because the user said "send it" earlier about a different draft, not to save a round-trip. A vague or prior "go ahead" is not sufficient — re-confirm after showing the final content. When unsure whether you have approval, save a draft and ask — you never need permission to save a draft, and you always need it to send.
# Save a draft (default). --to / --cc / --attach are repeatable.
emailops-cli --account <ID|EMAIL> compose \
--to a@x.com --to b@y.com --cc c@z.com \
--subject "Q3 numbers" \
--body "Hi — draft here." \
--attach /abs/path/report.pdf --json
# Long/rich body: read it from a file instead of --body (mutually exclusive).
emailops-cli --account <ID|EMAIL> compose \
--to a@x.com --subject "Notes" --body-file /abs/path/body.txt --json
# Update an existing draft in place rather than creating a new one.
emailops-cli --account <ID|EMAIL> compose --draft <DRAFT_ID> \
--subject "Q3 numbers (rev)" --body "Updated." --to a@x.com --json
# Send now — ONLY after showing the full email and getting explicit approval
# (see "Mandatory send procedure" below). Delivers immediately; irreversible.
emailops-cli --account <ID|EMAIL> compose \
--to a@x.com --subject "Q3 numbers" --body "Final." --send --json
Rules and shapes:
--to, --cc, --attach are repeatable; pass the flag once per value.--body XOR --body-file. Supplying both is invalid input
(exit 2, no JSON envelope — it's rejected at the argument layer). --body-file
reads the body verbatim from the path.--body "Hi\n\nText" is stored as the literal characters \n and the
recipient sees \n in the message. Format the body the way a person would:
greeting on its own line, a blank line between paragraphs, and a blank line
before the sign-off. Two reliable ways to pass actual newlines:
--body-file (preferred for anything multi-line): write the body to a
file with real newlines, then reference it —
printf 'Hi there,\n\nFirst paragraph.\n\nThanks,\nGero\n' > /tmp/body.txt
then compose … --body-file /tmp/body.txt.--body $'Hi there,\n\nText'
(the $'…' form makes the shell expand \n into real newlines; ordinary
"…" double quotes do not).filename and mimeType are inferred. Each
returned attachment is {id, draftId, filePath, filename, mimeType}.compose (draft) returns the draft object: id, accountId, status
("draft"), subject, toAddresses[], ccAddresses[], body, attachments[],
providerDraftId (null on IMAP/local; set once pushed to Gmail/Outlook),
createdAt/updatedAt. drafts returns an array of these (newest-first);
draft <ID> returns one.toAddresses is
saved fine) — but confirm the recipient list before --send.Review drafts:
emailops-cli --account <ID|EMAIL> drafts --json # newest-first list
emailops-cli --account <ID|EMAIL> draft <DRAFT_ID> --json # one draft, full
--send is irreversible and there is no undo. Every send MUST follow this
order:
compose …, no --send) — never build the
send command straight from user text.draft <ID> --json: the sending account, all To/Cc recipients, the
subject, the entire body, and the filename of each attachment. Present it
plainly, not just a summary.compose --draft <ID> …) and repeat from step 2 — approval of an earlier
version does not carry over.compose --draft <ID> --send --json (or an equivalent
--send with the identical, already-approved fields).If you cannot show the full message and get a fresh, explicit yes, do not send — leave the draft saved and tell the user it's ready to review.
Heavy writes need the app closed. sync, classify, embed, and the
heavy compose write paths — --send, and provider draft push on
Gmail/Outlook — mutate the SQLite DB (and hit the network); run them only
when the EmailOps desktop app is not running (WAL contention). A plain
save-as-draft on an IMAP/local account is safe even while the app is open
(the draft stays local — providerDraftId is null). Read commands are always
safe.
Don't scrape pretty output. Human output is styled (color/tables) only on
an interactive TTY and is not a stable contract. Always --json.
Logs are on stderr. Don't merge stderr into stdout when parsing JSON;
use --quiet to silence the app-log stream.
No model for doctor. It's intentionally cheap — use it as the gate
before any AI command rather than calling chat blindly.
Never send without explicit user confirmation. --send is irreversible;
always follow the Mandatory send procedure above — save a draft, show the
full email (account, all recipients, subject, body, attachments), get a fresh
explicit yes, then send. When in doubt, leave it as a draft.
Same commands in the REPL. Running emailops-cli with no subcommand opens
an interactive REPL that exposes these as /compose, /drafts, and
/draft <id>. For the agent, prefer the one-shot subcommands with --json.
Double-encoded saved files. When emailops-cli output is written to a file
and ends up double-escaped (the outer JSON has an "output" key whose value
is a string containing the inner JSON), unwrap it first:
cat saved_result.json | jq -r '.output' | jq ...
Without -r .output, jq will try to parse the escaped string as literal
JSON tokens and silently fail or return wrong results.
Outlook/Hotmail control characters break jq. Emails from Outlook/Hotmail
providers can inject raw \r\n sequences that cause jq: parse error: Invalid string: control characters … errors. Pre-filter before piping to jq:
cat output.json | sed 's/\\r//g' | jq ...
emailops-cli doctor --json # expect ok:true, accountsEnabled > 0, exit 0
emailops-cli accounts --json # expect a non-empty data array
If doctor returns ok:true with at least one enabled account, the skill is
correctly wired and downstream commands can be trusted.