agent_first_mail/cli/
root.rs1#[cfg(feature = "ui")]
2use super::UiCommand;
3use super::{
4 ArchiveAction, CaseCommand, ConfigAction, ContactCommand, DoctorAction, LogAction,
5 MessageAction, PurgeAction, PushAction, RemoteAction, RenderAction, SkillAction, TriageAction,
6};
7use clap::{Parser, Subcommand};
8
9#[derive(Parser, Debug)]
10#[command(name = "afmail", disable_version_flag = true, verbatim_doc_comment)]
11#[doc = r#"Agent-First Mail: local-first email case workspace for agents.
12
13### Interface Policy
14
15- Files are the read interface; CLI is for effects.
16- One workspace represents one mailbox account.
17- Message commands use `afmail message ACTION MESSAGE_ID ...`.
18- Case commands use `afmail case ACTION CASE_REF ...`.
19- Active and archived cases are readable with `afmail case show REF` and
20 `afmail archive case show REF`.
21- stdout carries structured Agent-First Data events; stderr is not a protocol channel.
22
23### Workspace Shape
24
25```text
26.afmail/messages/ raw .eml plus durable remote sidecars
27messages/ rebuildable parsed message cache
28.afmail/logs/events.jsonl append-only audit log
29.afmail/transactions/ transient local write transaction sentinels
30.afmail/workspace.progress.json latest push/pull runtime snapshot
31templates/ user-editable generated Markdown templates
32triage/message_*.md active unprocessed message views
33spam/*.md generated spam review views
34trash/*.md generated trash review views
35deleted/*.md generated remote-deleted review views
36cases/<group>/<case_uid>-<name>/case.md generated case entry view
37cases/<group>/<case_uid>-<name>/data/ canonical case state
38cases/<group>/<case_uid>-<name>/views/ generated case detail views
39contacts/<group>/<contact_uid>-<name>.md canonical contact cards
40archive/contacts/<contact_uid>-<name>.md archived contact cards
41archive/cases/<case_uid>-<name>/ archived case workspaces
42archive/notifications/<archive_uid>-<name>/archive.md generated archive entry view
43archive/notifications/<archive_uid>-<name>/data/ canonical archive state
44archive/notifications/<archive_uid>-<name>/views/ generated archive detail views
45```
46
47### Examples
48
49```text
50afmail init
51afmail init email-a
52afmail skill status
53afmail skill install --agent codex --scope workspace
54afmail status
55afmail doctor
56afmail pull
57afmail pull sent archive
58afmail remote folders
59afmail case create --name 应用反馈-肥料登记 --message message_inbox_607146690_21 --group open --reason "new feedback thread"
60afmail case show c20260603001
61afmail case add c20260603001 message_inbox_607146690_22 --reason "follow-up belongs to same feedback case"
62afmail archive message create --name 服务通知 --message message_inbox_607146690_23 --summary "billing notification" --reason "billing notification"
63afmail archive message show a20260603001
64afmail archive message restore a20260603001 message_inbox_607146690_23 --reason "needs triage again"
65afmail message spam message_inbox_607146690_23 --reason "phishing attempt"
66afmail message trash message_inbox_607146690_24 --reason "duplicate no longer needed"
67afmail render refresh
68afmail doctor repair --confirm
69afmail case move c20260603001 waiting
70afmail case archive c20260603001 --reason "feedback handled"
71afmail archive case restore c20260603001 --group open --reason "customer replied"
72afmail case tag c20260603001 legal --reason "legal review needed"
73afmail contact create --name "Zhang San" --email zhang@example.com --org Acme --role Buyer
74afmail contact list --group people
75afmail contact email add p20260603001 zhang-alt@example.com
76afmail contact rename p20260603001 --name "Zhang Sanfeng"
77afmail contact extract --from-triage
78afmail contact archive p20260603001 --reason "left the company"
79afmail case draft reply c20260603001 message_inbox_607146690_22
80afmail case draft attach c20260603001 reply-message_inbox_607146690_22.md ./screenshot.png
81afmail case draft change c20260603001 reply-message_inbox_607146690_22.md --body "Thanks, I will check this."
82afmail case draft validate c20260603001 reply-message_inbox_607146690_22.md
83afmail case draft send c20260603001 reply-message_inbox_607146690_22.md
84afmail case draft remove c20260603001 reply-message_inbox_607146690_22.md --reason "mistaken draft"
85afmail push list
86afmail push
87afmail push --confirm
88afmail purge
89afmail purge spam --older-than-days 30
90afmail purge deleted
91afmail log list --limit 20
92afmail message show message_inbox_607146690_21
93afmail message attachment fetch message_inbox_607146690_21 2
94```
95
96### Exit Codes
97
98- `0`: command completed successfully
99- `1`: runtime/store/protocol error
100- `2`: invalid CLI arguments
101"#]
102pub struct Cli {
103 #[arg(long, default_value = "json", global = true)]
105 pub output: String,
106
107 #[arg(long, value_delimiter = ',', global = true)]
109 pub log: Vec<String>,
110
111 #[arg(long, global = true)]
113 pub verbose: bool,
114
115 #[arg(short = 'V', long, global = true)]
117 pub version: bool,
118
119 #[command(subcommand)]
120 pub command: Option<Command>,
121}
122
123#[derive(Subcommand, Debug)]
124pub enum Command {
125 Init {
127 path: Option<String>,
129 },
130 Pull {
132 ids: Vec<String>,
134 },
135 Config {
137 #[command(subcommand)]
138 action: ConfigAction,
139 },
140 Remote {
142 #[command(subcommand)]
143 action: RemoteAction,
144 },
145 Push {
147 #[arg(long)]
149 dry_run: bool,
150 #[arg(long)]
152 confirm: bool,
153 #[command(subcommand)]
154 action: Option<PushAction>,
155 },
156 Status,
158 Doctor {
160 #[command(subcommand)]
161 action: Option<DoctorAction>,
162 },
163 Purge {
165 #[arg(long = "older-than-days", value_name = "DAYS")]
167 older_than_days: Option<u64>,
168 #[command(subcommand)]
169 action: Option<PurgeAction>,
170 },
171 Skill {
173 #[command(subcommand)]
174 action: SkillAction,
175 },
176 Triage {
178 #[command(subcommand)]
179 action: TriageAction,
180 },
181 Message {
183 #[command(subcommand)]
184 action: MessageAction,
185 },
186 Case {
188 #[command(subcommand)]
189 action: CaseCommand,
190 },
191 Contact {
193 #[command(subcommand)]
194 action: ContactCommand,
195 },
196 Archive {
198 #[command(subcommand)]
199 action: ArchiveAction,
200 },
201 Render {
203 #[command(subcommand)]
204 action: RenderAction,
205 },
206 Log {
208 #[command(subcommand)]
209 action: LogAction,
210 },
211 #[cfg(feature = "ui")]
213 Ui(UiCommand),
214}