use crate::changes;
use crate::commands;
use crate::commit;
use crate::common::CommonParams;
use crate::llm::get_available_provider_names;
use crate::log_debug;
use crate::ui;
use clap::builder::{styling::AnsiColor, Styles};
use clap::{crate_version, Parser, Subcommand};
const LOG_FILE: &str = "git-iris-debug.log";
#[derive(Parser)]
#[command(
author,
version = crate_version!(),
about = "AI-assisted Git commit message generator",
long_about = None,
disable_version_flag = true,
after_help = get_dynamic_help(),
styles = get_styles(),
)]
pub struct Cli {
#[command(subcommand)]
pub command: Option<Commands>,
#[arg(
short = 'l',
long = "log",
global = true,
help = "Log debug messages to a file"
)]
pub log: bool,
#[arg(
short = 'v',
long = "version",
global = true,
help = "Display the version"
)]
pub version: bool,
}
#[derive(Subcommand)]
pub enum Commands {
#[command(
about = "Generate a commit message using AI",
long_about = "Generate a commit message using AI based on the current Git context.",
after_help = get_dynamic_help()
)]
Gen {
#[command(flatten)]
common: CommonParams,
#[arg(short, long, help = "Automatically commit with the generated message")]
auto_commit: bool,
#[arg(long, help = "Disable Gitmoji for this commit")]
no_gitmoji: bool,
#[arg(short, long, help = "Print the generated message to stdout and exit")]
print: bool,
#[arg(long, help = "Skip verification steps (pre/post commit hooks)")]
no_verify: bool,
},
#[command(about = "Configure the AI-assisted Git commit message generator")]
Config {
#[command(flatten)]
common: CommonParams,
#[arg(long, help = "Set API key for the specified provider")]
api_key: Option<String>,
#[arg(long, help = "Set model for the specified provider")]
model: Option<String>,
#[arg(long, help = "Set token limit for the specified provider")]
token_limit: Option<usize>,
#[arg(
long,
help = "Set additional parameters for the specified provider (key=value)"
)]
param: Option<Vec<String>>,
},
#[command(about = "List available instruction presets")]
ListPresets,
#[command(
about = "Generate a changelog",
long_about = "Generate a changelog between two specified Git references."
)]
Changelog {
#[command(flatten)]
common: CommonParams,
#[arg(long, required = true)]
from: String,
#[arg(long)]
to: Option<String>,
},
#[command(
about = "Generate release notes",
long_about = "Generate comprehensive release notes between two specified Git references."
)]
ReleaseNotes {
#[command(flatten)]
common: CommonParams,
#[arg(long, required = true)]
from: String,
#[arg(long)]
to: Option<String>,
},
}
fn get_styles() -> Styles {
Styles::styled()
.header(AnsiColor::Magenta.on_default().bold())
.usage(AnsiColor::Cyan.on_default().bold())
.literal(AnsiColor::Green.on_default().bold())
.placeholder(AnsiColor::Yellow.on_default())
.valid(AnsiColor::Blue.on_default().bold())
.invalid(AnsiColor::Red.on_default().bold())
.error(AnsiColor::Red.on_default().bold())
}
pub fn parse_args() -> Cli {
Cli::parse()
}
fn get_dynamic_help() -> String {
let providers = get_available_provider_names().join(", ");
format!("Available providers: {}", providers)
}
pub async fn main() -> anyhow::Result<()> {
let cli = parse_args();
if cli.version {
ui::print_version(crate_version!());
return Ok(());
}
if cli.log {
crate::logger::enable_logging();
crate::logger::set_log_file(LOG_FILE)?;
} else {
crate::logger::disable_logging();
}
match cli.command {
Some(command) => handle_command(command).await,
None => {
let _ = Cli::parse_from(&["git-iris", "--help"]);
Ok(())
}
}
}
pub async fn handle_command(command: Commands) -> anyhow::Result<()> {
match command {
Commands::Gen {
common,
auto_commit,
no_gitmoji,
print,
no_verify
} => {
log_debug!(
"Handling 'gen' command with common: {:?}, auto_commit: {}, no_gitmoji: {}, print: {}, no_verify: {}",
common, auto_commit, no_gitmoji, print, no_verify
);
ui::print_version(crate_version!());
println!();
commit::handle_gen_command(common, auto_commit, !no_gitmoji, print, !no_verify).await?;
}
Commands::Config {
common,
api_key,
model,
token_limit,
param,
} => {
log_debug!(
"Handling 'config' command with common: {:?}, api_key: {:?}, model: {:?}, token_limit: {:?}, param: {:?}",
common, api_key, model, token_limit, param
);
commands::handle_config_command(common, api_key, model, token_limit, param)?;
}
Commands::ListPresets => {
log_debug!("Handling 'list_presets' command");
commands::handle_list_presets_command()?;
}
Commands::Changelog { common, from, to } => {
log_debug!(
"Handling 'changelog' command with common: {:?}, from: {}, to: {:?}",
common,
from,
to
);
changes::handle_changelog_command(common, from, to).await?;
}
Commands::ReleaseNotes { common, from, to } => {
log_debug!(
"Handling 'release-notes' command with common: {:?}, from: {}, to: {:?}",
common,
from,
to
);
changes::handle_release_notes_command(common, from, to).await?;
}
}
Ok(())
}