Skip to main content

agent_first_mail/cli/
root.rs

1use super::{
2    ArchiveAction, CaseCommand, ConfigAction, DoctorAction, LogAction, MessageAction, PurgeAction,
3    PushAction, RemoteAction, RenderAction, SkillAction, TriageAction,
4};
5use clap::{Parser, Subcommand};
6
7#[derive(Parser, Debug)]
8#[command(name = "afmail", disable_version_flag = true, verbatim_doc_comment)]
9#[doc = r#"Agent-First Mail: local-first email case workspace for agents.
10
11### Interface Policy
12
13- Files are the read interface; CLI is for effects.
14- One workspace represents one mailbox account.
15- Message commands use `afmail message ACTION MESSAGE_ID ...`.
16- Case commands use `afmail case ACTION CASE_REF ...`.
17- Active and archived cases are readable with `afmail case show REF` and
18  `afmail archive case show REF`.
19- stdout carries structured Agent-First Data events; stderr is not a protocol channel.
20
21### Workspace Shape
22
23```text
24.afmail/messages/        raw .eml plus durable state/remote sidecars
25messages/               rebuildable parsed message cache
26.afmail/logs/events.jsonl append-only audit log
27.afmail/transactions/    transient local write transaction sentinels
28.afmail/workspace.progress.json latest push/pull runtime snapshot
29triage/message_*.md      active unprocessed message views
30spam/*.md                generated spam review views
31trash/*.md               generated trash review views
32deleted/*.md             generated remote-deleted review views
33cases/<group>/<case_uid>-<name>/case.md generated case entry view
34cases/<group>/<case_uid>-<name>/data/ canonical case state
35cases/<group>/<case_uid>-<name>/views/ generated case detail views
36archive/cases/<case_uid>-<name>/ archived case workspaces
37archive/notifications/<archive_uid>-<name>/archive.md generated archive entry view
38archive/notifications/<archive_uid>-<name>/data/ canonical archive state
39archive/notifications/<archive_uid>-<name>/views/ generated archive detail views
40```
41
42### Examples
43
44```text
45afmail init
46afmail skill status
47afmail skill install
48afmail status
49afmail doctor
50afmail pull
51afmail pull sent archive
52afmail remote folders
53afmail case create --name 应用反馈-肥料登记 --message message_inbox_607146690_21 --group open --reason "new feedback thread"
54afmail case show c20260603001
55afmail case add c20260603001 message_inbox_607146690_22 --reason "follow-up belongs to same feedback case"
56afmail archive message create --name 服务通知 --message message_inbox_607146690_23 --summary "billing notification" --reason "billing notification"
57afmail archive message show a20260603001
58afmail archive message restore a20260603001 message_inbox_607146690_23 --reason "needs triage again"
59afmail message spam message_inbox_607146690_23 --reason "phishing attempt"
60afmail message trash message_inbox_607146690_24 --reason "duplicate no longer needed"
61afmail render refresh
62afmail doctor repair --confirm
63afmail case move c20260603001 waiting
64afmail case archive c20260603001 --reason "feedback handled"
65afmail archive case restore c20260603001 --group open --reason "customer replied"
66afmail case tag c20260603001 legal --reason "legal review needed"
67afmail case reply c20260603001 message_inbox_607146690_22
68afmail case draft attach c20260603001 reply-message_inbox_607146690_22.md ./screenshot.png
69afmail case draft validate c20260603001 reply-message_inbox_607146690_22.md
70afmail case compose c20260603001 reply-message_inbox_607146690_22.md
71afmail case draft remove c20260603001 reply-message_inbox_607146690_22.md --reason "mistaken draft"
72afmail push drafts-send --dry-run
73afmail push drafts-send
74afmail push archive
75afmail push spam
76afmail push trash
77afmail push list
78afmail purge
79afmail purge spam --older-than-days 30
80afmail purge deleted
81afmail log list --limit 20
82afmail message show message_inbox_607146690_21
83afmail message attachment fetch message_inbox_607146690_21 2
84```
85
86### Exit Codes
87
88- `0`: command completed successfully
89- `1`: runtime/store/protocol error
90- `2`: invalid CLI arguments
91"#]
92pub struct Cli {
93    /// Output format: json (default), yaml, plain.
94    #[arg(long, default_value = "json", global = true)]
95    pub output: String,
96
97    /// Log categories (comma-separated): startup, request, progress, retry.
98    #[arg(long, value_delimiter = ',', global = true)]
99    pub log: Vec<String>,
100
101    /// Enable all log categories.
102    #[arg(long, global = true)]
103    pub verbose: bool,
104
105    /// Print structured version event.
106    #[arg(short = 'V', long, global = true)]
107    pub version: bool,
108
109    #[command(subcommand)]
110    pub command: Option<Command>,
111}
112
113#[derive(Subcommand, Debug)]
114pub enum Command {
115    /// Initialize the current directory as an afmail workspace.
116    Init,
117    /// Read configured IMAP mailbox ids into local message files without changing remote mail.
118    Pull {
119        /// Configured mailbox ids to pull. With none, pulls actions.pull.default_mailbox_ids.
120        ids: Vec<String>,
121    },
122    /// Read or update local afmail configuration.
123    Config {
124        #[command(subcommand)]
125        action: ConfigAction,
126    },
127    /// Inspect remote IMAP state for configuring mailboxes.
128    Remote {
129        #[command(subcommand)]
130        action: RemoteAction,
131    },
132    /// Push queued local work or manage the local push queue.
133    Push {
134        /// Show planned push actions without IMAP/SMTP writes. This is the default.
135        #[arg(long)]
136        dry_run: bool,
137        /// Apply queued IMAP/SMTP effects.
138        #[arg(long)]
139        confirm: bool,
140        #[command(subcommand)]
141        action: Option<PushAction>,
142    },
143    /// Report workspace health, counts, and latest pull/push progress.
144    Status,
145    /// Check afmail workspace consistency without inspecting Git.
146    Doctor {
147        #[command(subcommand)]
148        action: Option<DoctorAction>,
149    },
150    /// Permanently delete old local spam, trash, and remote-deleted records.
151    Purge {
152        /// Only purge messages in a discard state at least this many days ago. Defaults to 30.
153        #[arg(long = "older-than-days", value_name = "DAYS")]
154        older_than_days: Option<u64>,
155        #[command(subcommand)]
156        action: Option<PurgeAction>,
157    },
158    /// Manage the Agent-First Mail skill for Codex, Claude Code, and opencode.
159    Skill {
160        #[command(subcommand)]
161        action: SkillAction,
162    },
163    /// Inspect the triage queue.
164    Triage {
165        #[command(subcommand)]
166        action: TriageAction,
167    },
168    /// Operate on any local message id.
169    Message {
170        #[command(subcommand)]
171        action: MessageAction,
172    },
173    /// Create a case or operate on an existing case ref.
174    Case {
175        #[command(subcommand)]
176        action: CaseCommand,
177    },
178    /// Inspect and manage archived cases and direct-message archive categories.
179    Archive {
180        #[command(subcommand)]
181        action: ArchiveAction,
182    },
183    /// Rebuild generated read views from local workspace state.
184    Render {
185        #[command(subcommand)]
186        action: RenderAction,
187    },
188    /// Inspect the workspace audit log.
189    Log {
190        #[command(subcommand)]
191        action: LogAction,
192    },
193}