nils-codex-cli 0.7.3

CLI crate for nils-codex-cli in the nils-cli workspace.
Documentation
mod cli;
mod completion;

use clap::error::ErrorKind;
use clap::{CommandFactory, Parser};
use codex_cli::{agent, auth, config};

fn main() {
    let exit_code = run();
    std::process::exit(exit_code);
}

fn run() -> i32 {
    if std::env::args_os().nth(1).is_none() {
        let mut cmd = cli::Cli::command();
        if cmd.print_help().is_ok() {
            println!();
            return 0;
        }
        return 1;
    }

    let cli = match cli::Cli::try_parse_from(std::env::args()) {
        Ok(cli) => cli,
        Err(err) => {
            let code = match err.kind() {
                ErrorKind::DisplayHelp | ErrorKind::DisplayVersion => 0,
                _ => 64,
            };
            let _ = err.print();
            return code;
        }
    };

    match cli.command {
        Some(command) => match command {
            cli::Command::Agent(args) => handle_agent(&args),
            cli::Command::Auth(args) => handle_auth(&args),
            cli::Command::Diag(args) => handle_diag(&args),
            cli::Command::Config(args) => handle_config(&args),
            cli::Command::PromptSegment(args) => handle_prompt_segment(&args),
            cli::Command::Completion(args) => handle_completion(&args),
        },
        None => {
            let mut cmd = cli::Cli::command();
            if cmd.print_help().is_ok() {
                println!();
                return 0;
            }
            1
        }
    }
}

fn handle_agent(args: &cli::AgentArgs) -> i32 {
    match &args.command {
        Some(cli::AgentCommand::Prompt { prompt, ephemeral }) => agent::prompt_with_options(
            prompt,
            agent::exec::ExecOptions {
                ephemeral: *ephemeral,
            },
        ),
        Some(cli::AgentCommand::Advice {
            question,
            ephemeral,
        }) => agent::advice_with_options(
            question,
            agent::exec::ExecOptions {
                ephemeral: *ephemeral,
            },
        ),
        Some(cli::AgentCommand::Knowledge { concept, ephemeral }) => agent::knowledge_with_options(
            concept,
            agent::exec::ExecOptions {
                ephemeral: *ephemeral,
            },
        ),
        Some(cli::AgentCommand::Commit {
            push,
            auto_stage,
            ephemeral,
            extra,
        }) => {
            let options = agent::commit::CommitOptions {
                push: *push,
                auto_stage: *auto_stage,
                ephemeral: *ephemeral,
                extra: extra.clone(),
            };
            agent::commit::run(&options).unwrap_or(1)
        }
        None => print_subcommand_help("agent"),
    }
}

fn handle_auth(args: &cli::AuthArgs) -> i32 {
    match &args.command {
        Some(cli::AuthCommand::Login {
            output,
            api_key,
            device_code,
        }) => auth::login::run_with_json(*api_key, *device_code, output.is_json()).unwrap_or(1),
        Some(cli::AuthCommand::Use { output, args }) => {
            if args.len() != 1 || args[0].is_empty() {
                eprintln!("codex-use: usage: codex-use <name|name.json|email>");
                return 64;
            }
            auth::use_secret::run_with_json(&args[0], output.is_json()).unwrap_or(1)
        }
        Some(cli::AuthCommand::Save { output, yes, args }) => {
            if args.len() != 1 || args[0].is_empty() {
                eprintln!("codex-save: usage: codex-save [--yes] <secret|secret.json>");
                return 64;
            }
            auth::save::run_with_json(&args[0], *yes, output.is_json()).unwrap_or(1)
        }
        Some(cli::AuthCommand::Remove { output, yes, args }) => {
            if args.len() != 1 || args[0].is_empty() {
                eprintln!("codex-remove: usage: codex-remove [--yes] <secret|secret.json>");
                return 64;
            }
            auth::remove::run_with_json(&args[0], *yes, output.is_json()).unwrap_or(1)
        }
        Some(cli::AuthCommand::Refresh { output, args }) => {
            if args.len() > 1 {
                eprintln!("codex-refresh: usage: codex-refresh-auth [secret.json]");
                return 64;
            }
            auth::refresh::run_with_json(args, output.is_json()).unwrap_or(1)
        }
        Some(cli::AuthCommand::AutoRefresh { output }) => {
            auth::auto_refresh::run_with_json(output.is_json()).unwrap_or(1)
        }
        Some(cli::AuthCommand::Current { output }) => {
            auth::current::run_with_json(output.is_json()).unwrap_or(1)
        }
        Some(cli::AuthCommand::Sync { output }) => {
            auth::sync::run_with_json(output.is_json()).unwrap_or(1)
        }
        None => print_subcommand_help("auth"),
    }
}

fn handle_diag(args: &cli::DiagArgs) -> i32 {
    match &args.command {
        Some(cli::DiagCommand::RateLimits(rate_args)) => {
            let output_json =
                rate_args.json || matches!(rate_args.format, Some(cli::OutputFormat::Json));
            let options = codex_cli::rate_limits::RateLimitsOptions {
                clear_cache: rate_args.clear_cache,
                debug: rate_args.debug,
                cached: rate_args.cached,
                no_refresh_auth: rate_args.no_refresh_auth,
                json: output_json,
                one_line: rate_args.one_line,
                all: rate_args.all,
                async_mode: rate_args.async_mode,
                watch: rate_args.watch,
                jobs: rate_args.jobs.clone(),
                secret: rate_args.secret.clone(),
            };
            codex_cli::rate_limits::run(&options).unwrap_or(1)
        }
        None => print_subcommand_help("diag"),
    }
}

fn handle_config(args: &cli::ConfigArgs) -> i32 {
    match &args.command {
        Some(cli::ConfigCommand::Show) => config::show(),
        Some(cli::ConfigCommand::Set { key, value }) => config::set(key, value),
        None => print_subcommand_help("config"),
    }
}

fn handle_prompt_segment(args: &cli::PromptSegmentArgs) -> i32 {
    let options = codex_cli::prompt_segment::PromptSegmentOptions {
        no_5h: args.no_5h,
        ttl: args.ttl.clone(),
        time_format: args.time_format.clone(),
        show_timezone: args.show_timezone,
        refresh: args.refresh,
        is_enabled: args.is_enabled,
    };
    codex_cli::prompt_segment::run(&options)
}

fn handle_completion(args: &cli::CompletionArgs) -> i32 {
    completion::run(args.shell)
}

fn print_subcommand_help(name: &str) -> i32 {
    let mut cmd = cli::Cli::command();
    if let Some(subcommand) = cmd.find_subcommand_mut(name)
        && subcommand.print_help().is_ok()
    {
        println!();
        return 0;
    }
    1
}