Skip to main content

lean_ctx/cli/
tee_cmd.rs

1pub fn cmd_tee(args: &[String]) {
2    let tee_dir = match crate::core::paths::state_dir() {
3        Ok(d) => d.join("tee"),
4        Err(e) => {
5            eprintln!("Cannot determine state directory: {e}");
6            std::process::exit(1);
7        }
8    };
9
10    let action = args.first().map_or("list", std::string::String::as_str);
11    match action {
12        "list" | "ls" => {
13            if !tee_dir.exists() {
14                println!("No tee logs found (~/.lean-ctx/tee/ does not exist)");
15                return;
16            }
17            let mut entries: Vec<_> = std::fs::read_dir(&tee_dir)
18                .unwrap_or_else(|e| {
19                    eprintln!("Error: {e}");
20                    std::process::exit(1);
21                })
22                .filter_map(std::result::Result::ok)
23                .filter(|e| e.path().extension().and_then(|x| x.to_str()) == Some("log"))
24                .collect();
25            entries.sort_by_key(std::fs::DirEntry::file_name);
26
27            if entries.is_empty() {
28                println!("No tee logs found.");
29                return;
30            }
31
32            println!("Tee logs ({}):\n", entries.len());
33            for entry in &entries {
34                let size = entry.metadata().map_or(0, |m| m.len());
35                let name = entry.file_name();
36                let size_str = if size > 1024 {
37                    format!("{}K", size / 1024)
38                } else {
39                    format!("{size}B")
40                };
41                println!("  {:<60} {}", name.to_string_lossy(), size_str);
42            }
43            println!("\nUse 'lean-ctx tee clear' to delete all logs.");
44        }
45        "clear" | "purge" => {
46            if !tee_dir.exists() {
47                println!("No tee logs to clear.");
48                return;
49            }
50            let mut count = 0u32;
51            if let Ok(entries) = std::fs::read_dir(&tee_dir) {
52                for entry in entries.flatten() {
53                    if entry.path().extension().and_then(|x| x.to_str()) == Some("log")
54                        && std::fs::remove_file(entry.path()).is_ok()
55                    {
56                        count += 1;
57                    }
58                }
59            }
60            println!("Cleared {count} tee log(s) from {}", tee_dir.display());
61        }
62        "show" => {
63            let Some(filename) = args.get(1) else {
64                eprintln!("Usage: lean-ctx tee show <filename>");
65                std::process::exit(1);
66            };
67            let fname = filename.as_str();
68            let basename = std::path::Path::new(fname).file_name().unwrap_or_default();
69            if basename.is_empty()
70                || basename != fname
71                || fname == "."
72                || fname == ".."
73                || fname.contains(std::path::MAIN_SEPARATOR)
74            {
75                eprintln!("Error: filename must be a plain basename (no path separators or '..')");
76                std::process::exit(1);
77            }
78            let path = tee_dir.join(basename);
79            match crate::tools::ctx_read::read_file_lossy(&path.to_string_lossy()) {
80                Ok(content) => print!("{content}"),
81                Err(e) => {
82                    eprintln!("Error reading {}: {e}", path.display());
83                    std::process::exit(1);
84                }
85            }
86        }
87        "last" => {
88            if !tee_dir.exists() {
89                println!("No tee logs found.");
90                return;
91            }
92            let mut entries: Vec<_> = std::fs::read_dir(&tee_dir)
93                .ok()
94                .into_iter()
95                .flat_map(|d| d.filter_map(std::result::Result::ok))
96                .filter(|e| e.path().extension().and_then(|x| x.to_str()) == Some("log"))
97                .collect();
98            entries.sort_by_key(|e| {
99                e.metadata()
100                    .and_then(|m| m.modified())
101                    .unwrap_or(std::time::SystemTime::UNIX_EPOCH)
102            });
103            match entries.last() {
104                Some(entry) => {
105                    let path = entry.path();
106                    println!(
107                        "--- {} ---\n",
108                        path.file_name().unwrap_or_default().to_string_lossy()
109                    );
110                    match crate::tools::ctx_read::read_file_lossy(&path.to_string_lossy()) {
111                        Ok(content) => print!("{content}"),
112                        Err(e) => eprintln!("Error: {e}"),
113                    }
114                }
115                None => println!("No tee logs found."),
116            }
117        }
118        _ => {
119            eprintln!("Usage: lean-ctx tee [list|clear|show <file>|last]");
120            std::process::exit(1);
121        }
122    }
123}
124
125pub fn cmd_filter(args: &[String]) {
126    let action = args.first().map_or("list", std::string::String::as_str);
127    match action {
128        "list" | "ls" => {
129            if let Some(engine) = crate::core::filters::FilterEngine::load() {
130                let rules = engine.list_rules();
131                println!("Loaded {} filter rule(s):\n", rules.len());
132                for rule in &rules {
133                    println!("{rule}");
134                }
135            } else {
136                println!("No custom filters found.");
137                println!("Create one: lean-ctx filter init");
138            }
139        }
140        "validate" => {
141            let Some(path) = args.get(1) else {
142                eprintln!("Usage: lean-ctx filter validate <file.toml>");
143                std::process::exit(1);
144            };
145            match crate::core::filters::validate_filter_file(path) {
146                Ok(count) => println!("Valid: {count} rule(s) parsed successfully."),
147                Err(e) => {
148                    eprintln!("Validation failed: {e}");
149                    std::process::exit(1);
150                }
151            }
152        }
153        "init" => match crate::core::filters::create_example_filter() {
154            Ok(path) => {
155                println!("Created example filter: {path}");
156                println!("Edit it to add your custom compression rules.");
157            }
158            Err(e) => {
159                eprintln!("{e}");
160                std::process::exit(1);
161            }
162        },
163        _ => {
164            eprintln!("Usage: lean-ctx filter [list|validate <file>|init]");
165            std::process::exit(1);
166        }
167    }
168}
169
170pub fn cmd_slow_log(args: &[String]) {
171    use crate::core::slow_log;
172
173    let action = args.first().map_or("list", std::string::String::as_str);
174    match action {
175        "list" | "ls" | "" => println!("{}", slow_log::list()),
176        "clear" | "purge" => println!("{}", slow_log::clear()),
177        _ => {
178            eprintln!("Usage: lean-ctx slow-log [list|clear]");
179            std::process::exit(1);
180        }
181    }
182}