chub_cli/commands/
stats.rs1use clap::Args;
2use owo_colors::OwoColorize;
3
4use chub_core::team::analytics;
5
6#[derive(Args)]
7pub struct StatsArgs {
8 #[arg(long, default_value = "30")]
10 days: u64,
11}
12
13pub fn run(args: StatsArgs, json: bool) {
14 let stats = analytics::get_stats(args.days);
15
16 if json {
17 println!(
18 "{}",
19 serde_json::to_string_pretty(&stats).unwrap_or_default()
20 );
21 } else {
22 eprintln!(
23 "{}\n",
24 format!("Usage stats (last {} days):", stats.period_days).bold()
25 );
26
27 eprintln!(
28 " Events: {} (fetches: {}, searches: {}, builds: {}, MCP calls: {}, annotations: {}, feedback: {})",
29 stats.total_events,
30 stats.total_fetches,
31 stats.total_searches,
32 stats.total_builds,
33 stats.total_mcp_calls,
34 stats.total_annotations,
35 stats.total_feedback,
36 );
37
38 if stats.total_events == 0 {
39 eprintln!("\n{}", "No events recorded.".dimmed());
40 return;
41 }
42
43 if !stats.most_fetched.is_empty() {
45 eprintln!("\n{}", "Most fetched docs:".bold());
46 for (i, (id, count)) in stats.most_fetched.iter().take(10).enumerate() {
47 eprintln!(" {}. {} — {} fetches", i + 1, id.bold(), count);
48 }
49 }
50
51 if !stats.top_queries.is_empty() {
53 eprintln!("\n{}", "Top search queries:".bold());
54 for (i, (query, count)) in stats.top_queries.iter().take(10).enumerate() {
55 eprintln!(" {}. \"{}\" — {} times", i + 1, query, count);
56 }
57 if stats.total_searches > 0 {
58 eprintln!(" Avg results per search: {:.1}", stats.avg_search_results);
59 }
60 }
61
62 if !stats.top_mcp_tools.is_empty() {
64 eprintln!("\n{}", "Top MCP tools:".bold());
65 for (tool, count) in stats.top_mcp_tools.iter().take(10) {
66 eprintln!(" {} — {} calls", tool, count);
67 }
68 }
69
70 if !stats.agents.is_empty() {
72 eprintln!("\n{}", "Agents:".bold());
73 for (agent, count) in &stats.agents {
74 eprintln!(" {} — {} events", agent, count);
75 }
76 }
77
78 if !stats.never_fetched_pins.is_empty() {
80 eprintln!("\n{}", "Never fetched (pinned but unused):".yellow());
81 for id in &stats.never_fetched_pins {
82 eprintln!(" - {}", id.dimmed());
83 }
84 eprintln!(
85 "\n{}",
86 "Suggestion: unpin unused docs to reduce noise.".dimmed()
87 );
88 }
89
90 let size = analytics::journal_size_bytes();
92 if size > 0 {
93 eprintln!("\n Journal: {:.1} KB", size as f64 / 1024.0);
94 }
95 }
96}