Skip to main content

lean_ctx/cli/
ledger_cmd.rs

1use crate::core::context_ledger::ContextLedger;
2
3pub fn cmd_ledger(args: &[String]) {
4    let action = args.first().map_or("status", String::as_str);
5
6    match action {
7        "status" => {
8            #[cfg(unix)]
9            if let Some(out) = crate::daemon_client::try_daemon_tool_call_blocking_text(
10                "ctx_ledger",
11                Some(serde_json::json!({ "action": "status" })),
12            ) {
13                println!("{out}");
14                return;
15            }
16            let ledger = ContextLedger::load();
17            let pressure = ledger.pressure();
18            println!(
19                "Context pressure: {:.0}% ({}/{} tokens)",
20                pressure.utilization * 100.0,
21                ledger.total_tokens_sent,
22                ledger.window_size,
23            );
24            println!("Entries: {}", ledger.entries.len());
25            println!("Recommendation: {:?}", pressure.recommendation);
26            let top = ledger.files_by_token_cost();
27            if !top.is_empty() {
28                println!("Top files by cost:");
29                for (path, tokens) in top.iter().take(5) {
30                    println!("  {path} ({tokens} tok)");
31                }
32            }
33        }
34
35        "reset" => {
36            #[cfg(unix)]
37            if let Some(out) = crate::daemon_client::try_daemon_tool_call_blocking_text(
38                "ctx_ledger",
39                Some(serde_json::json!({ "action": "reset" })),
40            ) {
41                println!("{out}");
42                return;
43            }
44            let mut ledger = ContextLedger::load();
45            let prev_entries = ledger.entries.len();
46            let prev_tokens = ledger.total_tokens_sent;
47            ledger.reset();
48            ledger.save();
49            println!(
50                "Ledger reset. Removed {prev_entries} entries, freed {prev_tokens} tracked tokens. Pressure: 0%."
51            );
52        }
53
54        "evict" => {
55            let targets: Vec<&str> = args[1..].iter().map(String::as_str).collect();
56            if targets.is_empty() {
57                eprintln!("Usage: lean-ctx ledger evict <file1> [file2...]");
58                std::process::exit(1);
59            }
60
61            #[cfg(unix)]
62            {
63                let targets_joined = targets.join(", ");
64                if let Some(out) = crate::daemon_client::try_daemon_tool_call_blocking_text(
65                    "ctx_ledger",
66                    Some(serde_json::json!({ "action": "evict", "targets": targets_joined })),
67                ) {
68                    println!("{out}");
69                    return;
70                }
71            }
72
73            let mut ledger = ContextLedger::load();
74            let removed = ledger.evict_paths(&targets);
75            ledger.save();
76            let pressure = ledger.pressure();
77            println!(
78                "Evicted {removed}/{} target(s). Pressure now: {:.0}%.",
79                targets.len(),
80                pressure.utilization * 100.0,
81            );
82        }
83
84        "prune" => {
85            let mut ledger = ContextLedger::load();
86            let pruned = ledger.prune();
87            ledger.save();
88            let pressure = ledger.pressure();
89            println!(
90                "Pruned {pruned} entries. Remaining: {}. Pressure: {:.0}%.",
91                ledger.entries.len(),
92                pressure.utilization * 100.0,
93            );
94        }
95
96        _ => {
97            eprintln!("Usage: lean-ctx ledger <status|reset|evict|prune> [args...]");
98            std::process::exit(1);
99        }
100    }
101}