lean_ctx/cli/
session_cmd.rs1use 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}