use std::path::PathBuf;
use clap::{ColorChoice, Parser, Subcommand, ValueEnum};
#[derive(Parser, Debug)]
#[command(
name = "qli",
version,
about = "Polyglot code-analysis CLI and any-language extension framework.",
after_help = ROOT_AFTER_HELP,
)]
pub struct Cli {
#[arg(short = 'v', long = "verbose", action = clap::ArgAction::Count, global = true)]
pub verbose: u8,
#[arg(short = 'q', long = "quiet", global = true, conflicts_with = "verbose")]
pub quiet: bool,
#[arg(long = "color", value_enum, default_value_t = ColorChoice::Auto, global = true)]
pub color: ColorChoice,
#[arg(short = 'y', long = "yes", global = true)]
pub yes: bool,
#[command(subcommand)]
pub command: Option<Command>,
}
#[derive(Subcommand, Debug)]
pub enum Command {
#[command(after_help = ANALYZE_AFTER_HELP)]
Analyze {
#[arg(value_name = "PATH", required = true)]
paths: Vec<PathBuf>,
#[arg(long, default_value = crate::analyze::DEFAULT_LANG)]
lang: String,
#[arg(long, default_value = crate::analyze::DEFAULT_ANALYZER)]
analyzer: String,
#[arg(long, value_enum, default_value_t = FormatArg::Auto)]
format: FormatArg,
#[arg(long)]
no_cache: bool,
},
#[command(after_help = COMPLETIONS_AFTER_HELP)]
Completions {
shell: clap_complete::Shell,
},
#[command(after_help = INDEX_AFTER_HELP)]
Index {
#[arg(value_name = "PATH", default_value = ".")]
path: PathBuf,
#[arg(long, value_name = "FILE", default_value = crate::index::DEFAULT_OUTPUT)]
output: PathBuf,
#[arg(long, value_name = "IDS", value_delimiter = ',')]
lang: Vec<String>,
},
Ext {
#[command(subcommand)]
action: ExtAction,
},
#[command(after_help = LSP_AFTER_HELP)]
Lsp {
#[arg(long, conflicts_with = "tcp")]
stdio: bool,
#[arg(long, value_name = "PORT")]
tcp: Option<u16>,
},
#[command(after_help = MCP_AFTER_HELP)]
Mcp,
SelfUpdate {
#[arg(long)]
json: bool,
},
}
#[derive(Subcommand, Debug)]
pub enum ExtAction {
List {
#[arg(long)]
json: bool,
},
Which {
group: String,
name: String,
#[arg(long)]
json: bool,
},
InstallDefaults {
#[arg(long)]
force: bool,
},
Cache {
#[command(subcommand)]
action: CacheAction,
},
}
#[derive(Subcommand, Debug)]
pub enum CacheAction {
Clean {
#[arg(long, conflicts_with = "older_than")]
all: bool,
#[arg(long, value_name = "DAYS")]
older_than: Option<u64>,
},
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, ValueEnum)]
pub enum FormatArg {
Auto,
Human,
Jsonl,
}
const ROOT_AFTER_HELP: &str = "\
EXAMPLES:
qli --version Print qli's version.
qli completions zsh > ~/.zsh/_qli Generate zsh completion script.
NO_COLOR=1 qli --help Disable colored output.
";
const ANALYZE_AFTER_HELP: &str = "\
EXAMPLES:
qli analyze src/ Analyze a directory (auto-detect languages).
qli analyze foo.py bar.ts Analyze specific files.
qli analyze --analyzer defrefs src/ Extract definitions and references.
qli analyze foo.py | jq . Machine-readable jsonl (auto when piped).
";
const INDEX_AFTER_HELP: &str = "\
EXAMPLES:
qli index Index the current directory into ./index.scip.
qli index src/ --output out.scip Index a directory, choosing the output file.
qli index --lang python,typescript Index only the named languages.
scip print --json index.scip Inspect the result with the SCIP CLI.
";
const LSP_AFTER_HELP: &str = "\
EXAMPLES:
qli lsp Serve LSP over stdin/stdout (what editors spawn).
qli lsp --stdio -vv Same, with debug logging on stderr.
qli lsp --tcp 9257 Listen on 127.0.0.1:9257 for one client.
";
const MCP_AFTER_HELP: &str = "\
EXAMPLES:
qli mcp Serve MCP over stdin/stdout (what MCP clients spawn).
qli mcp -vv Same, with debug logging on stderr.
claude mcp add qli -- qli mcp
Register the server with Claude Code.
";
const COMPLETIONS_AFTER_HELP: &str = "\
EXAMPLES:
qli completions bash > /usr/local/etc/bash_completion.d/qli
qli completions zsh > \"${fpath[1]}/_qli\"
qli completions fish > ~/.config/fish/completions/qli.fish
";
pub fn apply_color_choice(choice: ColorChoice) {
match choice {
ColorChoice::Always => {
std::env::remove_var("NO_COLOR");
std::env::set_var("CLICOLOR_FORCE", "1");
}
ColorChoice::Never => {
std::env::set_var("NO_COLOR", "1");
}
ColorChoice::Auto => {}
}
}