claudy 0.2.0

Modern multi-provider launcher for Claude CLI
use clap::Parser;
use std::io::{self, Write};

pub fn run_cli_session(argv0: &str, args: &[String]) -> anyhow::Result<i32> {
    if argv0 == "claude" {
        return run_claude_shim(args);
    }

    let (launcher_profile, is_launcher) =
        crate::routing::resolver::detect_symlink_invocation(argv0);
    if is_launcher {
        return run_launcher(&launcher_profile, args);
    }

    if !args.is_empty() {
        let cmd_or_profile = &args[0];
        if !is_builtin_subcommand(cmd_or_profile) && !cmd_or_profile.starts_with('-') {
            return run_profile_direct(cmd_or_profile, &args[1..]);
        }
    }

    let cli = crate::adapters::cli::args::Cli::parse();
    let paths = crate::config::layout::discover()?;
    let catalog = crate::providers::index::load_index()?;
    let secrets = crate::config::vault::load_vault(&paths.secrets_file)?;
    let mut cfg = crate::config::registry::open_registry(&paths.config_file)?;
    cfg.ingest_legacy_secrets(&secrets, &catalog);
    cfg.compact(&catalog);

    let mut output =
        crate::adapters::ui::output::Output::new(crate::adapters::ui::output::Format::Human, false);
    let prompt = crate::adapters::ui::prompt::Prompter::new(io::stdin(), io::stdout());

    let show_update = !is_install_command(&cli.command);
    if let Some(msg) =
        crate::adapters::update::check::maybe_message(&paths, crate::adapters::version::VALUE)
            .ok()
            .flatten()
            .filter(|_| show_update)
    {
        writeln!(output.stderr, "{}", msg)?;
    }

    let mut ctx = crate::domain::context::Context {
        paths,
        config: cfg,
        secrets,
        catalog,
        output: Box::new(output),
        prompt: Box::new(prompt),
        options: crate::domain::context::Options {
            help: false,
            version: false,
        },
    };

    if let Some(cmd) = cli.command {
        let gateway = crate::adapters::commands::LegacyCommandAdapter;
        let domain_cmd = crate::adapters::commands::map_cli_to_domain(cmd);

        crate::application::command_bus::dispatch_command(&gateway, &mut ctx, domain_cmd)
    } else {
        crate::adapters::cli::help::show_brief(ctx.output.as_mut())?;
        Ok(0)
    }
}

fn run_claude_shim(args: &[String]) -> anyhow::Result<i32> {
    let paths = crate::config::layout::discover()?;
    crate::launcher::shim::run_claude_shim(&paths, args)
}

fn run_profile_direct(profile: &str, args: &[String]) -> anyhow::Result<i32> {
    let paths = crate::config::layout::discover()?;
    let catalog = crate::providers::index::load_index()?;
    let secrets = crate::config::vault::load_vault(&paths.secrets_file)?;
    let mut cfg = crate::config::registry::open_registry(&paths.config_file)?;
    cfg.ingest_legacy_secrets(&secrets, &catalog);
    cfg.compact(&catalog);
    crate::application::entrypoint::launch_profile_session(
        &paths, &catalog, &cfg, &secrets, profile, args,
    )
}

fn run_launcher(profile: &str, args: &[String]) -> anyhow::Result<i32> {
    let paths = crate::config::layout::discover()?;
    let catalog = crate::providers::index::load_index()?;
    let secrets = crate::config::vault::load_vault(&paths.secrets_file)?;
    let mut cfg = crate::config::registry::open_registry(&paths.config_file)?;
    cfg.ingest_legacy_secrets(&secrets, &catalog);
    cfg.compact(&catalog);
    crate::application::entrypoint::launch_profile_session(
        &paths, &catalog, &cfg, &secrets, profile, args,
    )
}

fn is_install_command(cmd: &Option<crate::adapters::cli::args::Commands>) -> bool {
    matches!(
        cmd,
        Some(
            crate::adapters::cli::args::Commands::Sync
                | crate::adapters::cli::args::Commands::Uninstall
                | crate::adapters::cli::args::Commands::Update
        )
    )
}

fn is_builtin_subcommand(name: &str) -> bool {
    matches!(
        name,
        "ls" | "list"
            | "setup"
            | "config"
            | "show"
            | "info"
            | "ping"
            | "test"
            | "doctor"
            | "status"
            | "sync"
            | "install"
            | "update"
            | "uninstall"
            | "mode"
            | "channel"
            | "mcp"
            | "analytics"
            | "help"
    )
}