Skip to main content

lean_ctx/cli/
session_cmd.rs

1use crate::core::stats;
2
3use super::common::{format_tokens_cli, load_shell_history};
4
5pub fn cmd_session() {
6    let history = load_shell_history();
7    let gain = stats::load_stats();
8
9    let compressible_commands = [
10        "git ",
11        "npm ",
12        "yarn ",
13        "pnpm ",
14        "cargo ",
15        "docker ",
16        "kubectl ",
17        "gh ",
18        "pip ",
19        "pip3 ",
20        "eslint",
21        "prettier",
22        "ruff ",
23        "go ",
24        "golangci-lint",
25        "curl ",
26        "wget ",
27        "grep ",
28        "rg ",
29        "find ",
30        "ls ",
31    ];
32
33    let mut total = 0u32;
34    let mut via_hook = 0u32;
35
36    for line in &history {
37        let cmd = line.trim().to_lowercase();
38        if cmd.starts_with("lean-ctx") {
39            via_hook += 1;
40            total += 1;
41        } else {
42            for p in &compressible_commands {
43                if cmd.starts_with(p) {
44                    total += 1;
45                    break;
46                }
47            }
48        }
49    }
50
51    let pct = if total > 0 {
52        (via_hook as f64 / total as f64 * 100.0).round() as u32
53    } else {
54        0
55    };
56
57    println!("lean-ctx session statistics\n");
58    println!("Adoption:    {pct}% ({via_hook}/{total} compressible commands)");
59    println!("Saved:       {} tokens total", gain.total_saved);
60    println!("Calls:       {} compressed", gain.total_calls);
61
62    if total > via_hook {
63        let missed = total - via_hook;
64        let est = missed * 150;
65        println!("Missed:      {missed} commands (~{est} tokens saveable)");
66    }
67
68    println!("\nRun 'lean-ctx discover' for details on missed commands.");
69}
70
71pub fn cmd_wrapped(args: &[String]) {
72    let period = if args.iter().any(|a| a == "--month") {
73        "month"
74    } else if args.iter().any(|a| a == "--all") {
75        "all"
76    } else {
77        "week"
78    };
79
80    let report = crate::core::wrapped::WrappedReport::generate(period);
81    println!("{}", report.format_ascii());
82}
83
84pub fn cmd_sessions(args: &[String]) {
85    use crate::core::session::SessionState;
86
87    let action = args.first().map_or("list", std::string::String::as_str);
88
89    match action {
90        "list" | "ls" => {
91            let sessions = SessionState::list_sessions();
92            if sessions.is_empty() {
93                println!("No sessions found.");
94                return;
95            }
96            println!("Sessions ({}):\n", sessions.len());
97            for s in sessions.iter().take(20) {
98                let task = s.task.as_deref().unwrap_or("(no task)");
99                let task_short: String = task.chars().take(50).collect();
100                let date = s.updated_at.format("%Y-%m-%d %H:%M");
101                println!(
102                    "  {} | v{:3} | {:5} calls | {:>8} tok | {} | {}",
103                    s.id,
104                    s.version,
105                    s.tool_calls,
106                    format_tokens_cli(s.tokens_saved),
107                    date,
108                    task_short
109                );
110            }
111            if sessions.len() > 20 {
112                println!("  ... +{} more", sessions.len() - 20);
113            }
114        }
115        "show" => {
116            let id = args.get(1);
117            let session = if let Some(id) = id {
118                SessionState::load_by_id(id)
119            } else {
120                SessionState::load_latest()
121            };
122            match session {
123                Some(s) => println!("{}", s.format_compact()),
124                None => println!("Session not found."),
125            }
126        }
127        "cleanup" => {
128            let days = args.get(1).and_then(|s| s.parse::<i64>().ok()).unwrap_or(7);
129            let removed = SessionState::cleanup_old_sessions(days);
130            println!("Cleaned up {removed} session(s) older than {days} days.");
131        }
132        _ => {
133            eprintln!("Usage: lean-ctx sessions [list|show [id]|cleanup [days]]");
134            std::process::exit(1);
135        }
136    }
137}