Skip to main content

claudex_cli/commands/
timeline.rs

1use anyhow::Result;
2
3use crate::cli::ResolvedFilter;
4use crate::commands::sessions::format_duration;
5use crate::ui;
6use claudex::index::IndexStore;
7use claudex::providers::enabled_default;
8
9pub fn run(weekly: bool, limit: usize, json: bool, filter: &ResolvedFilter) -> Result<()> {
10    let providers = enabled_default()?;
11    let mut idx = IndexStore::open()?;
12    idx.ensure_fresh(&providers)?;
13    idx.ensure_pr_links_fresh(&providers)?;
14    let rows = idx.query_timeline(filter, weekly, limit)?;
15
16    if json {
17        let output: Vec<_> = rows
18            .iter()
19            .map(|r| {
20                serde_json::json!({
21                    "bucket": r.bucket,
22                    "sessions": r.session_count,
23                    "cost_usd": r.cost_usd,
24                    "input_tokens": r.input_tokens,
25                    "output_tokens": r.output_tokens,
26                    "cache_creation_tokens": r.cache_creation_tokens,
27                    "cache_read_tokens": r.cache_read_tokens,
28                    "tool_calls": r.tool_calls,
29                    "pr_count": r.pr_count,
30                    "avg_turn_duration_ms": r.avg_turn_duration_ms,
31                })
32            })
33            .collect();
34        println!("{}", serde_json::to_string_pretty(&output)?);
35        return Ok(());
36    }
37
38    if rows.is_empty() {
39        println!("No timeline data found.");
40        return Ok(());
41    }
42
43    let mut table = ui::table();
44    table.set_header(ui::header([
45        if weekly { "Week" } else { "Day" },
46        "Sessions",
47        "Cost",
48        "Tokens",
49        "Tools",
50        "PRs",
51        "Avg Turn",
52    ]));
53    ui::right_align(&mut table, &[1, 2, 3, 4, 5, 6]);
54    for r in &rows {
55        let total_tokens =
56            r.input_tokens + r.output_tokens + r.cache_creation_tokens + r.cache_read_tokens;
57        let avg_turn = r
58            .avg_turn_duration_ms
59            .map(|ms| format_duration(ms.round() as u64))
60            .unwrap_or_else(|| "-".to_string());
61        table.add_row([
62            ui::cell_dim(&r.bucket),
63            ui::cell_count(r.session_count as u64),
64            ui::cell_cost(r.cost_usd),
65            ui::cell_count(total_tokens as u64),
66            ui::cell_count(r.tool_calls as u64),
67            ui::cell_count(r.pr_count as u64),
68            ui::cell_plain(avg_turn),
69        ]);
70    }
71    println!("{table}");
72    Ok(())
73}