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 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}