Agent-First Mail v0.1: Your Inbox as a Git Worktree
Agent-First Mail gives each mailbox a local, file-first workspace: pull mail into files, work cases and drafts offline, and push remote effects only when you explicitly confirm.
You want your agent to work your inbox. So you hand it an IMAP client. Now every action your agent takes — opening a message, replying, archiving, deleting — happens against a live, mutable mailbox, through the same controls, with no boundary between “let me read this” and “I just moved 40 messages.” The read surface is a UI: sorting, threading, and selection state are behavior, not durable inputs. There is nowhere stable to write a case note or a draft reply. And after the fact, you can’t tell a suggestion apart from a mutation.
afmail takes the useful part of the git mental model and applies it to a mailbox: a remote, a local worktree, and an explicit push boundary. Files are the read interface; the CLI is the effect interface. Your agent reads and works entirely in local files, and the mailbox only changes when you say --confirm.
The model: pull, work locally, push deliberately
Each mailbox account is its own workspace directory — work inside it like a git worktree:
mkdir work-mail
cd work-mail
afmail init
afmail config set imap.host imap.example.com
afmail config set imap.username me@example.com
afmail config set imap.password_secret env:AFMAIL_IMAP_PASSWORD_SECRET
afmail pull
afmail status
afmail pull is like git pull: it reads your configured mailbox ids, imports message evidence, and refreshes generated read views. It is read-only IMAP — it does not mark messages seen, move them, tag them, delete them, append drafts, or send anything. Acquisition and effects are different verbs.
After a pull, the workspace separates active attention, archived work, machine evidence, queued effects, and audit history:
work-mail/
triage/
message_<id>.md # generated read views — open them directly
cases/open/
<case_uid>-<name>/ # case.md, notes.md, messages/, drafts/, files/
archive/
cases/ notifications/
.afmail/
messages/ # raw .eml, cleaned .txt, metadata .json
push/ # the queue of pending remote effects
logs/events.jsonl # append-only audit log
Your agent — or you — reads triage/message_*.md as plain Markdown. No live UI, no selection state, no API round-trip to look at mail.
The boundary is the point
Everything an agent does by default is local. Filing a case, writing a draft, archiving, marking spam — all of it mutates the workspace, not the mailbox.
afmail triage list
afmail case create --name "Customer issue" --message MESSAGE_ID --reason "needs follow-up"
afmail case c20260606001 reply MESSAGE_ID # drafts a reply into the case — no network
afmail case c20260606001 draft validate reply-1
afmail case c20260606001 compose reply-1 # queues the outbound draft locally
None of that has touched the remote mailbox yet. The queued effects sit in .afmail/push/. To see them, you preview — bare push commands are dry runs:
afmail push list # inspect everything queued
afmail push archive # preview the archive moves
afmail push drafts-send # preview the outbound sends
The mailbox changes at exactly one moment: when you add --confirm.
afmail push archive --confirm
afmail push drafts-send --confirm
This is the line afmail draws and the reason it exists. In an agent workflow, “send”, “reply”, and “forward” mean local drafting unless you explicitly ask to push. The agent can classify, summarize, draft, and file all day; your mailbox stays untouched until a human confirms the remote effect. Every confirmed effect lands in .afmail/logs/events.jsonl, so a suggestion is never confused with a mutation after the fact.
Files are the read interface; the CLI is for effects
Each message is imported once as durable evidence: raw .eml, a cleaned .txt, and a metadata .json under .afmail/messages/. From those, afmail generates Markdown read views in triage/, cases/, and archive/. Refs are stable and meaningful: message_id for mail, case_uid (cYYYYMMDDNNN) for cases, archive_uid (aYYYYMMDDNNN) for direct archive categories. You can rebuild every generated view with afmail render refresh — a local-only operation that never contacts IMAP/SMTP or changes membership state.
Bring your own skills
afmail does not try to be the agent. It gives an agent skill a stable mailbox workspace to operate on — somewhere to read messages, write case notes, draft replies, and queue effects, all behind the push boundary. The workspace AGENTS.md holds mailbox-specific policy (priorities, reply style, escalation rules, labels). The installed skills/agent-first-mail.md is the reusable behavior contract for any agent driving the CLI.
afmail skill install
afmail skill status
Install
afmail is pre-release (v0.1.0) — build it from source:
cargo install --path spores/agent-first-mail
afmail skill install
afmail skill status
Restart your agent after installing the skill so it reloads afmail’s behavior rules.
Or just hand the project to your agent and let it decide. Paste this:
Read the Agent-First Mail README,
skills/agent-first-mail.md, anddocs/design-principles.mdin this repo, then tell me in plain terms what it would do for me and whether it fits what I’m working on. If it’s a fit, build it from source after a quick review, then runafmail skill installso you follow its behavior rules.