use clap::{Args, Parser, Subcommand};
use std::path::PathBuf;
#[derive(Parser, Debug)]
#[command(name = "git-agent", version, args_conflicts_with_subcommands = true)]
pub struct Cli {
#[command(subcommand)]
pub command: Option<Command>,
#[command(flatten)]
pub generate: GenerateArgs,
#[command(flatten)]
pub settings: SettingArgs,
}
#[derive(Args, Debug)]
pub struct GenerateArgs {
#[arg(short, long, conflicts_with = "raw")]
pub commit: bool,
#[arg(short, long, conflicts_with = "raw")]
pub push: bool,
#[arg(short, long)]
pub raw: bool,
#[arg(short, long)]
pub all: bool,
#[arg(short = 'H', long, value_name = "TEXT")]
pub hint: Option<String>,
#[arg(last = true, value_name = "GIT_COMMIT_ARGS")]
pub git_args: Vec<String>,
}
#[derive(Args, Debug, Default)]
pub struct SettingArgs {
#[arg(long, global = true, env = "GIT_AGENT_CONFIG", value_name = "PATH")]
pub config: Option<PathBuf>,
#[arg(short, long, global = true, env = "GIT_AGENT_MODEL")]
pub model: Option<String>,
#[arg(
long,
global = true,
env = "GIT_AGENT_CONTEXT_LENGTH",
value_name = "TOKENS"
)]
pub context_length: Option<usize>,
#[arg(long, global = true, env = "GIT_AGENT_BASE_URL", value_name = "URL")]
pub base_url: Option<String>,
#[arg(long, global = true, env = "GIT_AGENT_API_KEY", hide_env_values = true)]
pub api_key: Option<String>,
#[arg(short, long, global = true)]
pub temperature: Option<f32>,
}
#[derive(Subcommand, Debug)]
pub enum Command {
Models,
#[command(subcommand)]
Config(ConfigCommand),
}
#[derive(Subcommand, Debug)]
pub enum ConfigCommand {
Init {
#[arg(long)]
force: bool,
},
Show,
}
#[cfg(test)]
mod tests {
use super::*;
use clap::CommandFactory;
#[test]
fn cli_definition_is_valid() {
Cli::command().debug_assert();
}
#[test]
fn passes_trailing_args_to_git_commit() {
let cli = Cli::try_parse_from(["git-agent", "-c", "--", "--no-verify", "-s"]).unwrap();
assert!(cli.generate.commit);
assert_eq!(cli.generate.git_args, ["--no-verify", "-s"]);
}
#[test]
fn push_combines_with_commit_but_not_raw() {
let cli = Cli::try_parse_from(["git-agent", "-cp"]).unwrap();
assert!(cli.generate.commit && cli.generate.push);
assert!(Cli::try_parse_from(["git-agent", "--push", "--raw"]).is_err());
}
#[test]
fn setting_flags_work_with_subcommands() {
let cli = Cli::try_parse_from(["git-agent", "models", "--model", "qwen3"]).unwrap();
assert!(matches!(cli.command, Some(Command::Models)));
assert_eq!(cli.settings.model.as_deref(), Some("qwen3"));
let cli = Cli::try_parse_from(["git-agent", "--context-length", "8192"]).unwrap();
assert_eq!(cli.settings.context_length, Some(8192));
}
}