面向代理的本地优先邮件:IMAP 同步至 maildir + SQLite(FTS5);CLI 搜索、读取、会话、联系人、附件。需要 Node 20+,npm install(`npm install...
What zmail is: Email reimagined for agents and automation—not a human-first inbox UI. It syncs mail over IMAP, stores messages as files (maildir-style) and indexes them in local SQLite with FTS5. The primary surface is the CLI: search, read, thread, who, attachments, and zmail ask for natural-language questions (OpenAI). Same index powers every command—queries stay local and fast so the assistant can treat mail like a repository of communication artifacts (invoices, travel, Zoom summaries, etc.) instead of paging through Gmail.
Why use it: Traditional webmail is slow and awkward for AI workflows. zmail’s promise is local-first, privacy-friendly mail you control, agent-intuitive commands, and room to grow toward “the agent is the interface”—plain-language prompts become searches and reads, not manual digging.
Use this block to keep ClawHub / OpenClaw registry fields aligned with the skill body—avoid “no credentials required” when the CLI clearly needs secrets.
TopicWhat to declareProvenanceSource and issues: github.com/cirne/zmailInstallnpm install -g @cirne/zmail or npx @cirne/zmail (Node 20+). Native better-sqlite3; on ABI mismatch, first zmail run rebuilds via ensure-better-sqlite-native (or run npm rebuild better-sqlite3 yourself with the same node that runs zmail).On PATHGlobal npm bin must be on PATH, or use npx @cirne/zmail for one-off invocations.Required secrets (after setup)****ZMAIL_EMAIL, ZMAIL_IMAP_PASSWORD (IMAP; e.g. Gmail app password). ZMAIL_OPENAI_API_KEY or OPENAI_API_KEY for setup wizard, zmail ask, zmail inbox, and optional zmail who --enrich.Privacy / data leaving the device**zmail ask**, zmail inbox, and who --enrich can send email-derived content (subjects, snippets, bodies, addresses) to OpenAI or other APIs—only use if the mailbox owner accepts that. Primitives search / read / thread / attachment (without enrich) are local index + disk only once mail is synced.Credentials on diskSecrets live under ZMAIL_HOME/.env (and non-secret settings in config.json). They are used only to talk to your IMAP host and (when configured) OpenAI—not to third-party analytics or the zmail project. Treat .env like any password file (permissions, backups, don’t paste into chats).IMAP / send posture****Read-only today: zmail syncs and indexes mail; it does not implement SMTP send in this release. Normal sync is a local cache of what remains on the server—deleting local data (see below) does not remove server-side mail.MCP (optional)****zmail mcp uses stdio JSON-RPC only (stdin/stdout)— no in-process HTTP server or listening TCP port for MCP.Persistence & local wipeConfig and a local copy of mail (SQLite index + maildir cache under data/) live under ZMAIL_HOME (default ~/.zmail). zmail setup --clean --yes removes that local tree and rewrites config—it does not delete mail on the IMAP server; after setup, run zmail sync to rebuild the local cache from IMAP. You still lose unsaved local-only state (e.g. extracted-attachment cache, any data not on the server).Shell safetyInvoke zmail with argument arrays (or careful quoting). Never paste untrusted mail text or chat content into a sh -c "zmail …" string— command-injection risk.
OpenClaw parses metadata.openclaw.requires per Creating skills: bins = executables expected on PATH ( zmail exists only after the global install step). config lists environment variables this workflow expects for a configured mailbox (mirror the same in ClawHub package metadata if the UI has separate fields).
node -v).npm install -g @cirne/zmail (see Install).zmail wizard (TTY) or zmail setup (flags/env, no prompts).zmail sync --since … then zmail refresh / zmail status.zmail, zmail --help, and zmail <command> --help. Read any hint (and truncation fields) in JSON output—zmail uses them to disclose the next capability ( Canonical docs & discovery).zmail ask first; use search / read / thread / who / attachment when you need fine control ( Ask vs primitives).bash
node -v # must be v20+
npm install -g @cirne/zmail
better-sqlite3 fails to load (wrong Node ABI), the CLI may rebuild automatically on first run; if not, run npm rebuild better-sqlite3 using the same node binary that runs zmail.npm may install to a directory that is not on PATH; ensure that global bin is on PATH, or use npx @cirne/zmail for one-off commands.Config and data default to ZMAIL_HOME (default ~/.zmail): config.json, .env, and data/ (SQLite + maildir).
Gmail does not allow normal account passwords for IMAP clients. Use a 16‑character app password.
Turn on 2‑Step Verification (required):
Create an app password (sign in with Google account):
zmail).Use full Gmail address as IMAP user (e.g. you@gmail.com) and the app password as ZMAIL_IMAP_PASSWORD / --password.
If app passwords are disabled (workspace policy, account type), the user must use whatever IMAP credentials their admin allows.
zmail wizard (interactive humans)#When: Real terminal with TTY; user is present to answer prompts.
Run: zmail wizard
Optional: --no-validate (skip live IMAP/OpenAI checks), --clean (wipe local config + cached mail under ZMAIL_HOME; IMAP unchanged; may prompt unless --yes).
If stdin is not a TTY (agents, CI, pipes): wizard exits with a message to use zmail setup instead.
Wizard walks through email, IMAP app password, OpenAI key, default sync window, and can start background sync at the end.
zmail setup (agents & automation)#Non-interactive. No prompts when all inputs are provided via flags and/or environment variables.
Required today (all three):
InputFlagEnvironment variableEmail (IMAP user)--email``ZMAIL_EMAILIMAP password (e.g. Gmail app password)--password``ZMAIL_IMAP_PASSWORDOpenAI API key--openai-key``ZMAIL_OPENAI_API_KEY or OPENAI_API_KEY
Examples:
bash
zmail setup \
--email 'user@gmail.com' \
--password 'abcdefghijklmnop' \
--openai-key 'sk-...'
bash
export ZMAIL_EMAIL='user@gmail.com'
export ZMAIL_IMAP_PASSWORD='abcdefghijklmnop'
export ZMAIL_OPENAI_API_KEY='sk-...'
zmail setup
Optional flags:
FlagMeaning--no-validateSkip IMAP and OpenAI validation (faster/offline-ish write of config only).--default-since <spec>Default sync window in config (e.g. 7d, 1y). Default if omitted: 1y.--clean --yesDelete existing config.json, .env, and data/ under ZMAIL_HOME, then write new config. Local only—IMAP mailbox unchanged; resync rebuilds the index/cache.
If any required value is missing, zmail setup prints what’s missing and exits—fix env/flags and retry.
OpenAI key: Required for zmail setup / zmail wizard as shipped. It is stored in ~/.zmail/.env. Same key powers zmail ask, zmail inbox, and related features. Search/read/thread/who/attachment do not need the API at query time once mail is indexed.
Secret / fileRequired?PurposeZMAIL_IMAP_PASSWORD in **.envYes (for sync)IMAP login (Gmail app password).ZMAIL_OPENAI_API_KEY (or OPENAI_API_KEY) in .envYes at setup; yes for ask / inboxLLM features.config.json****YesNon-secret: IMAP host/port/user, sync defaults (no password in this file).ZMAIL_HOME**OptionalOverride config root (default ~/.zmail).
Security: Treat .env like credentials—don’t commit it, don’t paste into tickets or agent transcripts. Rotate app passwords if exposed.
bash
zmail sync --since 30d # initial backfill (often runs in background; note log path on stdout)
zmail refresh # fetch new mail since last sync
zmail status # local sync + index health
zmail ask "your question" # one-shot NL answer (OpenAI); good default for agents
zmail search 'query' # FTS hits (JSON default; --text for tables)
sync: Safe to run in background; use zmail status and the sync log file path the CLI prints.zmail ask "<question>" runs zmail’s answer pipeline in one go: it figures out how to search and pull the right messages, then synthesizes a complete answer for the user. For the calling agent, that usually means fewer steps and a ready-made summary—best when the goal is “answer this question about my mail” rather than “give me raw hits.” Requires ZMAIL_OPENAI_API_KEY (or OPENAI_API_KEY). Optional --verbose if you need to trace what it did.
Primitives ( search, read, thread, who, attachment list / attachment read) expose structured, explicit steps: you choose the query, which message IDs to open, whether you need full body or raw, threads, contacts, or extracted attachment text. They do not call OpenAI for the core path—good for scripts, tight filters, verbatim quotes, debugging, or when the outer agent wants to own the reasoning and token budget.
Prefer zmail askPrefer primitivesBroad or fuzzy questions (“what did X say about the launch?”)Exact filters, known IDs, paginationYou want a single synthesized answer quicklyYou need every matching row or full message bodiesUser asked in natural language and doesn’t care about IDsAttachments, EML/raw, or who / address-book style lookups
Rule of thumb: Start with ask. If the answer is too shallow, wrong, or you need more detail or accuracy, switch to search → read / thread (and attachment when documents matter). Combine both: e.g. ask for orientation, then read on specific message_id s from search if you must verify.
Full tradeoffs and hybrid patterns: docs/ASK.md at the package/repo root (paths in references/CANONICAL-DOCS.md).
Copy the zmail directory (this skill) into an end-user location—not into the zmail source repo’s .cursor/skills/ (those are dev-only).
HostTypical pathCursor~/.cursor/skills/zmail/ or another project’s .cursor/skills/zmail/Claude Code~/.claude/skills/zmail/OpenClaw<workspace>/skills/zmail/, ~/.openclaw/skills/zmail/, or from this repo: npm run install-skill:openclaw ( OpenClaw creating skills)
Folder name must stay zmail to match frontmatter name ( Agent Skills spec). Copy the whole skills/zmail/ directory (includes references/).
For OpenClaw, use a heartbeat (not a separate cron per mailbox tick) for periodic “anything new in email?” awareness—OpenClaw’s own guide recommends heartbeat for inbox-style checks because it batches with other routine work and can suppress noise when nothing matters. See Cron vs heartbeat and Heartbeat (interval, HEARTBEAT.md, HEARTBEAT_OK, agents.defaults.heartbeat, etc.).
Put zmail on the workspace HEARTBEAT.md checklist, for example:
zmail refresh (forward IMAP sync into the local index), or use zmail inbox <window> --refresh so the forward sync runs immediately before the scan (same sync path as refresh; optional --force if you need to skip STATUS fast-path—see zmail inbox --help).zmail inbox over a window (e.g. 24h, 3d, or your inbox.defaultWindow in config.json) so the LLM returns notable recent mail in JSON ( newMail, …). Requires ZMAIL_OPENAI_API_KEY (or OPENAI_API_KEY). Use --text if you want a human-readable digest. Add --include-noise only if marketing/social should count as “notable.”HEARTBEAT_OK so OpenClaw drops the turn quietly (per Heartbeat docs).Cost / habit: refresh alone does not call OpenAI; inbox does. Keep the checklist short; widen the inbox window only when needed.
zmail, --help, per-command help), hints in output, and a table of canonical markdown ( AGENTS.md, docs/VISION.md, docs/ASK.md, docs/ARCHITECTURE.md, docs/MCP.md, OPP-025).