1#![cfg_attr(
2 test,
3 allow(
4 clippy::unwrap_used,
5 clippy::expect_used,
6 clippy::panic,
7 clippy::many_single_char_names
8 )
9)]
10
11use clap::FromArgMatches;
12
13pub mod cli;
14pub mod commands;
15mod dispatch;
16pub mod error;
17pub mod hook_cache;
18pub mod hook_forward;
19pub mod hook_runtime;
20pub mod hooks;
21pub mod mcp_install;
22pub mod runtime;
23pub mod style;
24
25use cli::{Cli, Commands, StatusLane};
26
27pub async fn run() {
28 let _ = style::detect_color_support();
30
31 let matches = cli::build_cli().get_matches();
32 let cli = match Cli::from_arg_matches(&matches) {
33 Ok(c) => c,
34 Err(e) => {
35 e.exit();
36 }
37 };
38 let _ = difflore_core::startup::ensure_ready(false).await;
41
42 if let Err(e) = difflore_core::migration::run_if_needed().await {
47 eprintln!(
48 "[difflore] warning: retired per-project index migration refused old state ({e}). \
49 Run `difflore doctor --report` to inspect."
50 );
51 }
52
53 let command = if let Some(command) = cli.command {
54 command
55 } else {
56 match commands::welcome::first_run_path(cli.no_interactive).await {
60 commands::welcome::FirstRunPath::LaunchWizard => {
61 if !commands::welcome::run_wizard().await.should_continue_tui() {
62 return;
63 }
64 }
65 commands::welcome::FirstRunPath::ShowWelcome => {
66 if !commands::welcome::show_welcome_then_continue()
67 .await
68 .should_continue_tui()
69 {
70 return;
71 }
72 }
73 commands::welcome::FirstRunPath::Skip => {}
74 }
75 Commands::Status {
76 json: false,
77 lane: StatusLane::All,
78 }
79 };
80 Box::pin(dispatch::dispatch(command)).await;
81}