agent_first_mail/cli/
root.rs1#[cfg(feature = "ui")]
2use super::UiCommand;
3use super::{
4 ArchiveAction, CaseCommand, ConfigAction, DoctorAction, LogAction, MessageAction, PurgeAction,
5 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
39archive/cases/<case_uid>-<name>/ archived case workspaces
40archive/notifications/<archive_uid>-<name>/archive.md generated archive entry view
41archive/notifications/<archive_uid>-<name>/data/ canonical archive state
42archive/notifications/<archive_uid>-<name>/views/ generated archive detail views
43```
44
45### Examples
46
47```text
48afmail init
49afmail init email-a
50afmail skill status
51afmail skill install --agent codex --scope workspace
52afmail status
53afmail doctor
54afmail pull
55afmail pull sent archive
56afmail remote folders
57afmail case create --name 应用反馈-肥料登记 --message message_inbox_607146690_21 --group open --reason "new feedback thread"
58afmail case show c20260603001
59afmail case add c20260603001 message_inbox_607146690_22 --reason "follow-up belongs to same feedback case"
60afmail archive message create --name 服务通知 --message message_inbox_607146690_23 --summary "billing notification" --reason "billing notification"
61afmail archive message show a20260603001
62afmail archive message restore a20260603001 message_inbox_607146690_23 --reason "needs triage again"
63afmail message spam message_inbox_607146690_23 --reason "phishing attempt"
64afmail message trash message_inbox_607146690_24 --reason "duplicate no longer needed"
65afmail render refresh
66afmail doctor repair --confirm
67afmail case move c20260603001 waiting
68afmail case archive c20260603001 --reason "feedback handled"
69afmail archive case restore c20260603001 --group open --reason "customer replied"
70afmail case tag c20260603001 legal --reason "legal review needed"
71afmail case draft reply c20260603001 message_inbox_607146690_22
72afmail case draft attach c20260603001 reply-message_inbox_607146690_22.md ./screenshot.png
73afmail case draft change c20260603001 reply-message_inbox_607146690_22.md --body "Thanks, I will check this."
74afmail case draft validate c20260603001 reply-message_inbox_607146690_22.md
75afmail case draft send c20260603001 reply-message_inbox_607146690_22.md
76afmail case draft remove c20260603001 reply-message_inbox_607146690_22.md --reason "mistaken draft"
77afmail push list
78afmail push
79afmail push --confirm
80afmail purge
81afmail purge spam --older-than-days 30
82afmail purge deleted
83afmail log list --limit 20
84afmail message show message_inbox_607146690_21
85afmail message attachment fetch message_inbox_607146690_21 2
86```
87
88### Exit Codes
89
90- `0`: command completed successfully
91- `1`: runtime/store/protocol error
92- `2`: invalid CLI arguments
93"#]
94pub struct Cli {
95 #[arg(long, default_value = "json", global = true)]
97 pub output: String,
98
99 #[arg(long, value_delimiter = ',', global = true)]
101 pub log: Vec<String>,
102
103 #[arg(long, global = true)]
105 pub verbose: bool,
106
107 #[arg(short = 'V', long, global = true)]
109 pub version: bool,
110
111 #[command(subcommand)]
112 pub command: Option<Command>,
113}
114
115#[derive(Subcommand, Debug)]
116pub enum Command {
117 Init {
119 path: Option<String>,
121 },
122 Pull {
124 ids: Vec<String>,
126 },
127 Config {
129 #[command(subcommand)]
130 action: ConfigAction,
131 },
132 Remote {
134 #[command(subcommand)]
135 action: RemoteAction,
136 },
137 Push {
139 #[arg(long)]
141 dry_run: bool,
142 #[arg(long)]
144 confirm: bool,
145 #[command(subcommand)]
146 action: Option<PushAction>,
147 },
148 Status,
150 Doctor {
152 #[command(subcommand)]
153 action: Option<DoctorAction>,
154 },
155 Purge {
157 #[arg(long = "older-than-days", value_name = "DAYS")]
159 older_than_days: Option<u64>,
160 #[command(subcommand)]
161 action: Option<PurgeAction>,
162 },
163 Skill {
165 #[command(subcommand)]
166 action: SkillAction,
167 },
168 Triage {
170 #[command(subcommand)]
171 action: TriageAction,
172 },
173 Message {
175 #[command(subcommand)]
176 action: MessageAction,
177 },
178 Case {
180 #[command(subcommand)]
181 action: CaseCommand,
182 },
183 Archive {
185 #[command(subcommand)]
186 action: ArchiveAction,
187 },
188 Render {
190 #[command(subcommand)]
191 action: RenderAction,
192 },
193 Log {
195 #[command(subcommand)]
196 action: LogAction,
197 },
198 #[cfg(feature = "ui")]
200 Ui(UiCommand),
201}