use tga::classify::ClassificationPipeline;
use tga::core::config::{ClassificationConfig, Config};
use tga::core::db::{CheckpointMode, Database};
use crate::commands::date_range::resolve_date_range;
use crate::ClassifyArgs;
pub async fn run(config: Config, db: &mut Database, args: ClassifyArgs) -> anyhow::Result<()> {
let mut cfg = config;
if cfg.classification.is_none() && (args.rules.is_some() || args.use_llm) {
cfg.classification = Some(ClassificationConfig::default());
}
if let Some(ref mut c) = cfg.classification {
if let Some(rules) = args.rules {
c.rules_files.insert(0, rules);
}
if args.use_llm {
c.use_llm = true;
}
if args.no_external {
c.no_external = true;
}
}
let (resolved_since, resolved_until) = resolve_date_range(
args.weeks,
args.since.as_deref(),
args.until.as_deref(),
None,
)?;
let effective_since = resolved_since;
let effective_until = resolved_until;
if (effective_since.is_some() || effective_until.is_some()) && !args.force {
tracing::warn!(
"--since/--until/--weeks was supplied without --force; ignoring date window. \
Pass --force to re-classify commits already in the DB."
);
}
let pipeline = ClassificationPipeline::new(cfg)
.with_force(args.force)
.with_since(effective_since.clone())
.with_until(effective_until.clone())
.with_repos(args.repos.clone());
if args.backfill_complexity {
let updated = pipeline.backfill_complexity(db).await?;
println!("Backfilled complexity for {updated} commit(s)");
if let Err(e) = db.wal_checkpoint(CheckpointMode::Truncate) {
tracing::warn!(error = %e, "WAL TRUNCATE checkpoint failed after backfill");
}
return Ok(());
}
let stats = pipeline.run(db).await?;
if let Err(e) = db.wal_checkpoint(CheckpointMode::Truncate) {
tracing::warn!(error = %e, "WAL TRUNCATE checkpoint failed after classify — \
the WAL may not be flushed; run `sqlite3 <db> 'PRAGMA wal_checkpoint(TRUNCATE)'` manually");
}
println!(
"Classified {}/{} commits ({:.1}% coverage)",
stats.classified, stats.total_commits, stats.coverage_pct
);
if !stats.by_method.is_empty() {
println!("By method:");
for (method, count) in &stats.by_method {
println!(" {method}: {count}");
}
}
if !stats.by_category.is_empty() {
println!("By category:");
for (category, count) in &stats.by_category {
println!(" {category}: {count}");
}
}
Ok(())
}