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    eprintln!("[DEPRECATED] Use `lean-ctx gain --wrapped`.");
81    println!(
82        "{}",
83        crate::tools::ctx_gain::handle("wrapped", Some(period), None, None)
84    );
85}
86
87pub fn cmd_sessions(args: &[String]) {
88    use crate::core::session::SessionState;
89
90    let action = args.first().map_or("list", std::string::String::as_str);
91
92    match action {
93        "list" | "ls" => {
94            let sessions = SessionState::list_sessions();
95            if sessions.is_empty() {
96                println!("No sessions found.");
97                return;
98            }
99            println!("Sessions ({}):\n", sessions.len());
100            for s in sessions.iter().take(20) {
101                let task = s.task.as_deref().unwrap_or("(no task)");
102                let task_short: String = task.chars().take(50).collect();
103                let date = s.updated_at.format("%Y-%m-%d %H:%M");
104                println!(
105                    "  {} | v{:3} | {:5} calls | {:>8} tok | {} | {}",
106                    s.id,
107                    s.version,
108                    s.tool_calls,
109                    format_tokens_cli(s.tokens_saved),
110                    date,
111                    task_short
112                );
113            }
114            if sessions.len() > 20 {
115                println!("  ... +{} more", sessions.len() - 20);
116            }
117        }
118        "show" => {
119            let id = args.get(1);
120            let session = if let Some(id) = id {
121                SessionState::load_by_id(id)
122            } else {
123                SessionState::load_latest()
124            };
125            match session {
126                Some(s) => println!("{}", s.format_compact()),
127                None => println!("Session not found."),
128            }
129        }
130        "cleanup" => {
131            let days = args.get(1).and_then(|s| s.parse::<i64>().ok()).unwrap_or(7);
132            let removed = SessionState::cleanup_old_sessions(days);
133            println!("Cleaned up {removed} session(s) older than {days} days.");
134        }
135        _ => {
136            eprintln!("Usage: lean-ctx sessions [list|show [id]|cleanup [days]]");
137            std::process::exit(1);
138        }
139    }
140}