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        /// Verify hooks are working (tests hook invocation)
160        #[arg(short, long)]
161        check: bool,
162    },
163
164    /// Launch interactive TUI
165    Tui(TuiArgs),
166
167    /// Display event stream
168    Watch {
169        /// Filter by session ID
170        #[arg(short, long)]
171        session: Option<String>,
172
173        /// Filter by event type
174        #[arg(short = 't', long = "type")]
175        event_type: Option<String>,
176
177        /// Filter by permission mode
178        #[arg(short, long)]
179        mode: Option<String>,
180
181        /// Filter by framework
182        #[arg(long, short = 'f')]
183        framework: Option<String>,
184
185        /// Poll interval in milliseconds
186        #[arg(long, default_value = "500")]
187        poll_ms: u64,
188    },
189
190    /// Run cleanup to remove old data
191    #[command(hide = true)]
192    Cleanup {
193        /// Show what would be removed without actually removing
194        #[arg(long = "dry", visible_alias = "dry-run")]
195        dry_run: bool,
196    },
197
198    /// Manage the OpenTelemetry server
199    #[command(hide = true, subcommand)]
200    Otel(OtelCommands),
201
202    /// Upgrade mi6 to the latest version
203    Upgrade {
204        /// Target version (only for cargo installs)
205        #[arg(short = 'V', long)]
206        version: Option<String>,
207
208        /// Skip confirmation prompt
209        #[arg(short, long)]
210        yes: bool,
211
212        /// Check for updates without installing
213        #[arg(long = "dry", visible_alias = "dry-run")]
214        dry_run: bool,
215
216        /// Installation method to use (defaults to current method)
217        #[arg(short, long, value_enum)]
218        method: Option<UpgradeMethod>,
219
220        /// Source path for cargo_source method
221        #[arg(long = "source-path", requires_if("cargo_source", "method"))]
222        source_path: Option<PathBuf>,
223    },
224
225    /// Uninstall mi6 from the system
226    Uninstall {
227        /// Skip all confirmation prompts (auto-delete data)
228        #[arg(short, long)]
229        yes: bool,
230
231        /// Keep mi6 data (database, config, TUI settings)
232        #[arg(long)]
233        keep_data: bool,
234
235        /// Show what would happen without actually doing it
236        #[arg(long = "dry", visible_alias = "dry-run")]
237        dry_run: bool,
238    },
239}
240
241/// Subcommands for ingesting data into mi6.
242#[derive(Subcommand, Debug)]
243pub enum IngestCommands {
244    /// Ingest an event from AI framework hooks
245    Event {
246        /// Event type (SessionStart, PreToolUse, etc.)
247        event_type: Option<String>,
248
249        /// JSON payload (reads from stdin if not provided)
250        json_payload: Option<String>,
251
252        /// AI framework logging the event
253        #[arg(long, short = 'f')]
254        framework: Option<String>,
255
256        /// Verify hook invocation without processing (for status --check)
257        #[arg(long, hide = true)]
258        ping: bool,
259    },
260
261    /// Ingest events from a transcript file
262    Transcript {
263        /// Path to the JSONL transcript file
264        path: PathBuf,
265    },
266
267    /// Ingest events from a Codex session file
268    CodexSession {
269        /// Session ID (UUID from filename) or file path
270        session_or_path: String,
271
272        /// Treat the argument as a file path instead of session ID
273        #[arg(long, short = 'p')]
274        file: bool,
275    },
276
277    /// Ingest OpenTelemetry data from stdin
278    Otel,
279}
280
281/// Installation method for upgrading mi6.
282#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq)]
283pub enum UpgradeMethod {
284    /// Homebrew (brew install mi6)
285    Brew,
286    /// GitHub releases (standalone binary)
287    Github,
288    /// Cargo from local source (cargo install --path)
289    CargoSource,
290    /// Cargo from crates.io (cargo install mi6)
291    CargoCrates,
292}
293
294/// Subcommands for managing the OpenTelemetry server.
295#[derive(Subcommand, Debug)]
296pub enum OtelCommands {
297    /// Start the OTel server (daemonized)
298    Start {
299        /// Port to listen on
300        #[arg(short, long, default_value = "4318")]
301        port: u16,
302
303        /// Processing mode
304        #[arg(short, long)]
305        mode: Option<OtelMode>,
306    },
307
308    /// Stop the running OTel server
309    Stop {
310        /// Port to stop
311        #[arg(short, long, default_value = "4318")]
312        port: u16,
313    },
314
315    /// Restart the OTel server
316    Restart {
317        /// Port to restart on
318        #[arg(short, long, default_value = "4318")]
319        port: u16,
320
321        /// Processing mode
322        #[arg(short, long)]
323        mode: Option<OtelMode>,
324    },
325
326    /// Show OTel server status
327    Status {
328        /// Port to check
329        #[arg(short, long, default_value = "4318")]
330        port: u16,
331    },
332
333    /// Run OTel server in foreground (for debugging)
334    Run {
335        /// Port to listen on
336        #[arg(short, long, default_value = "4318")]
337        port: u16,
338
339        /// Processing mode
340        #[arg(short, long)]
341        mode: Option<OtelMode>,
342    },
343}