use anyhow::Result;
use kodegraf_core::config::Config;
use kodegraf_core::graph::GraphStore;
use kodegraf_core::signals::models::{InsightsSummary, TrendComparison};
use kodegraf_core::signals::store::SignalStore;
use kodegraf_core::signals::analyzer::detect_patterns;
pub fn run(json: bool) -> Result<()> {
let repo_root = kodegraf_core::config::find_repo_root()?;
let db_path = Config::graph_db_path(&repo_root);
if !db_path.exists() {
anyhow::bail!("Graph not built. Run `kodegraf init && kodegraf build` first.");
}
let store = GraphStore::open(&db_path)?;
let signal_store = SignalStore::new(store.connection());
let (total, sessions, errors, warnings) = signal_store.get_total_counts()?;
if total == 0 {
if json {
println!("{{\"status\": \"no_data\", \"message\": \"No signals collected yet.\"}}");
} else {
println!("No data yet. Start a coding session with DiffGuard active.");
println!();
println!("DiffGuard collects signals automatically via PostToolUse hooks.");
println!("Run `kodegraf install` to configure hooks for your AI coding tool.");
}
return Ok(());
}
let self_correction_rate = signal_store.get_self_correction_rate()?;
let breakdown = signal_store.get_check_type_breakdown()?;
let (current_errors, previous_errors) = signal_store.get_trend()?;
let patterns = detect_patterns(store.connection(), 3)?;
let active_rules = signal_store.get_active_rules()?;
let direction = if current_errors < previous_errors {
"improving"
} else if current_errors > previous_errors {
"worsening"
} else {
"stable"
};
let summary = InsightsSummary {
total_signals: total,
total_sessions: sessions,
total_errors: errors,
total_warnings: warnings,
self_correction_rate,
top_failure_patterns: patterns.into_iter().take(10).collect(),
active_rules: active_rules.clone(),
check_type_breakdown: breakdown.clone(),
recent_trend: TrendComparison {
current_period_errors: current_errors,
previous_period_errors: previous_errors,
direction: direction.to_string(),
},
};
if json {
println!("{}", serde_json::to_string_pretty(&summary)?);
return Ok(());
}
println!("Kodegraf Insights");
println!("=================");
println!();
println!("Sessions: {} Signals: {} Errors: {} Warnings: {}",
sessions, total, errors, warnings);
println!("Self-correction rate: {:.0}%", self_correction_rate * 100.0);
println!();
let trend_icon = match direction {
"improving" => "v (improving)",
"worsening" => "^ (worsening)",
_ => "= (stable)",
};
println!("Trend (7d): {} errors (prev: {}) {}",
current_errors, previous_errors, trend_icon);
println!();
if !breakdown.is_empty() {
println!("Errors by check type:");
for ct in &breakdown {
println!(" {:20} {}", ct.check_type, ct.count);
}
println!();
}
if !summary.top_failure_patterns.is_empty() {
println!("Top failure patterns:");
for (i, pattern) in summary.top_failure_patterns.iter().enumerate() {
let wrote = if pattern.wrote_values.is_empty() {
"?".to_string()
} else {
pattern.wrote_values.join(", ")
};
println!(
" {}. {} in {} — wrote `{}` ({} times, {} sessions)",
i + 1,
pattern.check_type,
pattern.region,
wrote,
pattern.occurrence_count,
pattern.sessions_count,
);
}
println!();
}
if !active_rules.is_empty() {
println!("Active rules ({}):", active_rules.len());
for rule in &active_rules {
println!(" {}", rule.rule_text);
}
println!();
} else {
println!("No active rules. Rules are generated when patterns recur 3+ times.");
println!("Run `kodegraf generate-rules` to generate rules from current data.");
println!();
}
Ok(())
}