mi6_cli/args.rs
1use clap::{Args, Parser, Subcommand, ValueEnum};
2use mi6_core::OtelMode;
3use std::path::PathBuf;
4use std::sync::LazyLock;
5
6/// Version string constructed at runtime.
7/// Shows "X.Y.Z (commit)" for git installs, or just "X.Y.Z" for crates.io installs.
8static VERSION: LazyLock<String> = LazyLock::new(|| {
9 let version = env!("CARGO_PKG_VERSION");
10 let commit = env!("MI6_GIT_COMMIT");
11 if commit.is_empty() {
12 version.to_string()
13 } else {
14 format!("{version} ({commit})")
15 }
16});
17
18/// Build the long version string with git commit (if available).
19fn long_version() -> &'static str {
20 &VERSION
21}
22
23/// A unified CLI for monitoring and managing agentic coding sessions.
24///
25/// Run without arguments to launch the interactive TUI.
26#[derive(Parser, Debug)]
27#[command(name = "mi6", version = long_version(), about)]
28#[command(args_conflicts_with_subcommands = true)]
29#[command(disable_help_subcommand = true)]
30pub struct Cli {
31 #[command(subcommand)]
32 pub command: Option<Commands>,
33
34 /// TUI arguments (used when no subcommand is provided)
35 #[command(flatten)]
36 pub tui_args: TuiArgs,
37}
38
39/// Arguments for the TUI interface.
40#[derive(Args, Debug, Default, Clone, PartialEq, Eq)]
41pub struct TuiArgs {
42 /// Refresh interval in milliseconds
43 #[arg(short, long, value_name = "MS")]
44 pub interval: Option<u64>,
45
46 /// Transcript poll interval in milliseconds
47 #[arg(long = "transcript-poll", value_name = "MS")]
48 pub transcript_poll: Option<u64>,
49
50 /// Show all sessions including dead ones
51 #[arg(short, long)]
52 pub all: bool,
53
54 /// Color theme (default, minimal, dracula)
55 #[arg(short, long, value_name = "THEME")]
56 pub theme: Option<String>,
57
58 /// Columns to display (comma or space separated)
59 #[arg(short = 'c', long, value_name = "COLS", num_args = 1..)]
60 pub columns: Option<Vec<String>>,
61
62 /// Enable expert mode (shows mi6 process stats)
63 #[arg(long, hide = true)]
64 pub expert: bool,
65}
66
67#[derive(Subcommand, Debug)]
68pub enum Commands {
69 /// Enable mi6 for agent framework(s)
70 Enable {
71 /// Frameworks to enable (claude, cursor, codex, etc.)
72 #[arg(value_name = "FRAMEWORKS")]
73 frameworks: Vec<String>,
74
75 /// Install hooks to project config instead of global
76 #[arg(long, conflicts_with = "settings_local")]
77 local: bool,
78
79 /// Install hooks to project local config (not committed to git)
80 #[arg(long, conflicts_with = "local")]
81 settings_local: bool,
82
83 /// Print hooks to stdout instead of writing to file
84 #[arg(long)]
85 print: bool,
86
87 /// Only initialize database, skip hook installation
88 #[arg(long)]
89 db_only: bool,
90
91 /// Only install hooks, skip database initialization
92 #[arg(long)]
93 hooks_only: bool,
94
95 /// Configure OpenTelemetry for token tracking
96 #[arg(long, conflicts_with = "no_otel")]
97 otel: bool,
98
99 /// Port for OTel server
100 #[arg(long, default_value = "4318")]
101 otel_port: u16,
102
103 /// Remove OTel configuration
104 #[arg(long, conflicts_with = "otel")]
105 no_otel: bool,
106 },
107
108 /// Disable mi6 for agent framework(s)
109 Disable {
110 /// Frameworks to disable
111 #[arg(value_name = "FRAMEWORKS")]
112 frameworks: Vec<String>,
113
114 /// Remove from project config instead of global
115 #[arg(long, conflicts_with = "settings_local")]
116 local: bool,
117
118 /// Remove from project local config
119 #[arg(long, conflicts_with = "local")]
120 settings_local: bool,
121
122 /// Print what would be removed without modifying files
123 #[arg(long)]
124 print: bool,
125 },
126
127 /// Ingest data into mi6
128 #[command(subcommand)]
129 Ingest(IngestCommands),
130
131 /// Show session details
132 Session {
133 /// Session ID or PID
134 session_or_pid: String,
135
136 /// Filter by machine ID (for multi-machine scenarios)
137 #[arg(long, short = 'm')]
138 machine: Option<String>,
139
140 /// Output as JSON (all fields)
141 #[arg(long)]
142 json: bool,
143
144 /// Filter to specific fields (comma-separated)
145 #[arg(long, value_delimiter = ',')]
146 fields: Option<Vec<String>>,
147 },
148
149 /// Show mi6 status
150 Status {
151 /// Output as JSON
152 #[arg(long)]
153 json: bool,
154
155 /// Show additional details (framework config paths)
156 #[arg(short, long)]
157 verbose: bool,
158 },
159
160 /// Launch interactive TUI
161 Tui(TuiArgs),
162
163 /// Display event stream
164 Watch {
165 /// Filter by session ID
166 #[arg(short, long)]
167 session: Option<String>,
168
169 /// Filter by event type
170 #[arg(short = 't', long = "type")]
171 event_type: Option<String>,
172
173 /// Filter by permission mode
174 #[arg(short, long)]
175 mode: Option<String>,
176
177 /// Filter by framework
178 #[arg(long, short = 'f')]
179 framework: Option<String>,
180
181 /// Poll interval in milliseconds
182 #[arg(long, default_value = "500")]
183 poll_ms: u64,
184 },
185
186 /// Run garbage collection to remove old data
187 #[command(hide = true)]
188 Gc {
189 /// Show what would be removed without actually removing
190 #[arg(long = "dry", visible_alias = "dry-run")]
191 dry_run: bool,
192 },
193
194 /// Manage the OpenTelemetry server
195 #[command(hide = true, subcommand)]
196 Otel(OtelCommands),
197
198 /// Upgrade mi6 to the latest version
199 Upgrade {
200 /// Target version (only for cargo installs)
201 #[arg(short = 'V', long)]
202 version: Option<String>,
203
204 /// Skip confirmation prompt
205 #[arg(short, long)]
206 yes: bool,
207
208 /// Check for updates without installing
209 #[arg(long = "dry", visible_alias = "dry-run")]
210 dry_run: bool,
211
212 /// Installation method to use (defaults to current method)
213 #[arg(short, long, value_enum)]
214 method: Option<UpgradeMethod>,
215
216 /// Source path for cargo_source method
217 #[arg(long = "source-path", requires_if("cargo_source", "method"))]
218 source_path: Option<PathBuf>,
219 },
220
221 /// Uninstall mi6 from the system
222 Uninstall {
223 /// Skip confirmation prompt
224 #[arg(long)]
225 confirm: bool,
226
227 /// Keep mi6 data (database, config, TUI settings)
228 #[arg(long)]
229 keep_data: bool,
230
231 /// Show what would happen without actually doing it
232 #[arg(long = "dry", visible_alias = "dry-run")]
233 dry_run: bool,
234 },
235}
236
237/// Subcommands for ingesting data into mi6.
238#[derive(Subcommand, Debug)]
239pub enum IngestCommands {
240 /// Ingest an event from AI framework hooks
241 Event {
242 /// Event type (SessionStart, PreToolUse, etc.)
243 event_type: Option<String>,
244
245 /// JSON payload (reads from stdin if not provided)
246 json_payload: Option<String>,
247
248 /// AI framework logging the event
249 #[arg(long, short = 'f')]
250 framework: Option<String>,
251 },
252
253 /// Ingest events from a transcript file
254 Transcript {
255 /// Path to the JSONL transcript file
256 path: PathBuf,
257 },
258
259 /// Ingest events from a Codex session file
260 CodexSession {
261 /// Session ID (UUID from filename) or file path
262 session_or_path: String,
263
264 /// Treat the argument as a file path instead of session ID
265 #[arg(long, short = 'f')]
266 file: bool,
267 },
268
269 /// Ingest OpenTelemetry data from stdin
270 Otel,
271}
272
273/// Installation method for upgrading mi6.
274#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq)]
275pub enum UpgradeMethod {
276 /// Homebrew (brew install mi6)
277 Brew,
278 /// GitHub releases (standalone binary)
279 Github,
280 /// Cargo from local source (cargo install --path)
281 CargoSource,
282 /// Cargo from crates.io (cargo install mi6)
283 CargoCrates,
284}
285
286/// Subcommands for managing the OpenTelemetry server.
287#[derive(Subcommand, Debug)]
288pub enum OtelCommands {
289 /// Start the OTel server (daemonized)
290 Start {
291 /// Port to listen on
292 #[arg(short, long, default_value = "4318")]
293 port: u16,
294
295 /// Processing mode
296 #[arg(short, long)]
297 mode: Option<OtelMode>,
298 },
299
300 /// Stop the running OTel server
301 Stop {
302 /// Port to stop
303 #[arg(short, long, default_value = "4318")]
304 port: u16,
305 },
306
307 /// Restart the OTel server
308 Restart {
309 /// Port to restart on
310 #[arg(short, long, default_value = "4318")]
311 port: u16,
312
313 /// Processing mode
314 #[arg(short, long)]
315 mode: Option<OtelMode>,
316 },
317
318 /// Show OTel server status
319 Status {
320 /// Port to check
321 #[arg(short, long, default_value = "4318")]
322 port: u16,
323 },
324
325 /// Run OTel server in foreground (for debugging)
326 Run {
327 /// Port to listen on
328 #[arg(short, long, default_value = "4318")]
329 port: u16,
330
331 /// Processing mode
332 #[arg(short, long)]
333 mode: Option<OtelMode>,
334 },
335}