1#![allow(clippy::print_stdout, clippy::print_stderr)]
3
4mod args;
5pub mod commands;
6pub mod display;
7mod error;
8mod help;
9pub mod process;
10
11pub use help::{print_command_help, print_help, print_subcommand_help};
12
13use anyhow::{Context, Result};
14use mi6_core::{Storage, StorageError, TranscriptScanner};
15
16pub use args::{Cli, Commands, IngestCommands, OtelCommands, TuiArgs};
17
18#[derive(Debug, Clone, PartialEq, Eq)]
20pub enum RunResult {
21 Success,
23 ExitWithCode(i32),
25 RunTui(TuiArgs),
27}
28
29pub fn run<S, F>(cli: Cli, storage_factory: F) -> Result<RunResult>
34where
35 S: Storage,
36 F: FnOnce() -> Result<S, StorageError>,
37{
38 let command = match cli.command {
40 None => return Ok(RunResult::RunTui(cli.tui_args)),
41 Some(Commands::Tui(args)) => return Ok(RunResult::RunTui(args)),
42 Some(cmd) => cmd,
43 };
44
45 match command {
46 Commands::Tui(_) => unreachable!("handled above"),
47
48 Commands::Enable {
49 frameworks,
50 local,
51 settings_local,
52 print,
53 db_only,
54 hooks_only,
55 otel,
56 otel_port,
57 no_otel,
58 } => {
59 let result = commands::run_enable(commands::EnableCliOptions {
60 frameworks,
61 local,
62 settings_local,
63 print,
64 db_only,
65 hooks_only,
66 otel,
67 otel_port,
68 no_otel,
69 })?;
70
71 if result.should_init_db
72 && let Some(db_path) = result.db_path
73 {
74 let _ = storage_factory().context("failed to initialize storage")?;
75 eprintln!("Database initialized at: {}", db_path.display());
76 }
77
78 if !print {
79 eprintln!(
80 "\nRemember to reload hooks in your AI assistant to enable the new configuration."
81 );
82 }
83
84 Ok(RunResult::Success)
85 }
86
87 Commands::Disable {
88 frameworks,
89 local,
90 settings_local,
91 print,
92 } => {
93 commands::run_disable(commands::DisableOptions {
94 frameworks,
95 local,
96 settings_local,
97 print,
98 })?;
99 Ok(RunResult::Success)
100 }
101
102 Commands::Ingest(ingest_cmd) => run_ingest(ingest_cmd, storage_factory),
103
104 Commands::Session {
105 session_or_pid,
106 machine,
107 json,
108 fields,
109 } => {
110 let storage = storage_factory().context("failed to open storage")?;
111 commands::run_session(&storage, session_or_pid, machine, json, fields)?;
112 Ok(RunResult::Success)
113 }
114
115 Commands::Status { json } => {
116 commands::run_status_command(json)?;
117 Ok(RunResult::Success)
118 }
119
120 Commands::Watch {
121 session,
122 event_type,
123 mode,
124 framework,
125 poll_ms,
126 } => {
127 let storage = storage_factory().context("failed to open storage")?;
128 commands::run_watch(
129 &storage,
130 commands::WatchOptions {
131 session,
132 event_type,
133 permission_mode: mode,
134 framework,
135 poll_ms,
136 },
137 )?;
138 Ok(RunResult::Success)
139 }
140
141 Commands::Gc { dry_run, retention } => {
142 let storage = storage_factory().context("failed to open storage")?;
143 commands::run_gc(&storage, dry_run, retention)?;
144 Ok(RunResult::Success)
145 }
146
147 Commands::Otel(otel_cmd) => run_otel(otel_cmd, storage_factory),
148
149 Commands::Upgrade {
150 version,
151 yes,
152 dry_run,
153 } => {
154 commands::run_upgrade(commands::UpgradeOptions {
155 version,
156 yes,
157 dry_run,
158 })?;
159 Ok(RunResult::Success)
160 }
161
162 Commands::Uninstall {
163 confirm,
164 keep_data,
165 dry_run,
166 } => {
167 commands::run_uninstall(commands::UninstallOptions {
168 confirm,
169 keep_data,
170 dry_run,
171 })?;
172 Ok(RunResult::Success)
173 }
174 }
175}
176
177fn run_ingest<S, F>(cmd: IngestCommands, storage_factory: F) -> Result<RunResult>
179where
180 S: Storage,
181 F: FnOnce() -> Result<S, StorageError>,
182{
183 match cmd {
184 IngestCommands::Event {
185 event_type,
186 json_payload,
187 framework,
188 } => {
189 let result: Result<(), anyhow::Error> = (|| {
193 let storage = storage_factory().context("failed to open storage")?;
194 let log_result = commands::run_log(&storage, event_type, json_payload, framework)?;
195
196 if let Some(ref path) = log_result.transcript_path {
199 let scanner = TranscriptScanner::new(&storage, &log_result.machine_id);
200 let _ = scanner.scan_file(std::path::Path::new(path));
201
202 let _ = scanner.backfill_initial_prompt(
205 &log_result.session_id,
206 std::path::Path::new(path),
207 );
208 }
209 Ok(())
210 })();
211
212 if let Err(e) = result {
213 eprintln!("mi6: {:#}", e);
214 }
215 Ok(RunResult::ExitWithCode(0))
216 }
217
218 IngestCommands::Transcript { path } => {
219 let storage = storage_factory().context("failed to open storage")?;
220 commands::run_scan(&storage, &path)?;
221 Ok(RunResult::Success)
222 }
223
224 IngestCommands::Otel => {
225 let storage = storage_factory().context("failed to open storage")?;
226 commands::run_otel_ingest(&storage)?;
227 Ok(RunResult::Success)
228 }
229 }
230}
231
232fn run_otel<S, F>(cmd: OtelCommands, storage_factory: F) -> Result<RunResult>
234where
235 S: Storage,
236 F: FnOnce() -> Result<S, StorageError>,
237{
238 match cmd {
239 OtelCommands::Start { port, mode } => {
240 if commands::ensure_running(port, false, mode)? {
241 eprintln!("OTel server is running on port {}", port);
242 }
243 Ok(RunResult::Success)
244 }
245 OtelCommands::Stop { port } => {
246 commands::stop_server(port)?;
247 Ok(RunResult::Success)
248 }
249 OtelCommands::Restart { port, mode } => {
250 if commands::ensure_running(port, true, mode)? {
251 eprintln!("OTel server restarted on port {}", port);
252 }
253 Ok(RunResult::Success)
254 }
255 OtelCommands::Status { port } => {
256 commands::get_status(port)?;
257 Ok(RunResult::Success)
258 }
259 OtelCommands::Run { port, mode } => {
260 let storage = storage_factory().context("failed to open storage")?;
261 commands::run_server(&storage, port, mode)?;
262 Ok(RunResult::Success)
263 }
264 }
265}