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            // #715: resolve partial paths/basenames and report each outcome.
75            let root = std::env::current_dir()
76                .ok()
77                .map(|d| d.to_string_lossy().into_owned());
78            let outcomes = ledger.evict_paths_resolved(&targets, root.as_deref());
79            let removed = outcomes.iter().filter(|o| o.resolved.is_some()).count();
80            ledger.save();
81            let pressure = ledger.pressure();
82            println!(
83                "Evicted {removed}/{} target(s). Pressure now: {:.0}%.",
84                targets.len(),
85                pressure.utilization * 100.0,
86            );
87            for outcome in &outcomes {
88                match (&outcome.resolved, outcome.ambiguous.is_empty()) {
89                    (Some(resolved), _) if resolved != &outcome.target => {
90                        println!("  {} → {resolved}", outcome.target);
91                    }
92                    (Some(_), _) => {}
93                    (None, false) => println!(
94                        "  {} is ambiguous ({}) — use a longer suffix",
95                        outcome.target,
96                        outcome.ambiguous.join(", ")
97                    ),
98                    (None, true) => println!("  {} not in ledger", outcome.target),
99                }
100            }
101        }
102
103        "prune" => {
104            let mut ledger = ContextLedger::load();
105            let pruned = ledger.prune();
106            ledger.save();
107            let pressure = ledger.pressure();
108            println!(
109                "Pruned {pruned} entries. Remaining: {}. Pressure: {:.0}%.",
110                ledger.entries.len(),
111                pressure.utilization * 100.0,
112            );
113        }
114
115        _ => {
116            eprintln!("Usage: lean-ctx ledger <status|reset|evict|prune> [args...]");
117            std::process::exit(1);
118        }
119    }
120}