st/cli.rs
1// -----------------------------------------------------------------------------
2// CLI Definitions for Smart Tree
3// All command-line argument parsing happens here using clap.
4// Extracted from main.rs to keep things organized!
5// -----------------------------------------------------------------------------
6
7use anyhow::{Context, Result};
8use chrono::NaiveDate;
9use clap::{Parser, Subcommand, ValueEnum};
10use std::path::PathBuf;
11use std::time::SystemTime;
12
13/// Smart Tree CLI - intelligent directory visualization
14#[derive(Parser, Debug)]
15#[command(
16 name = "st",
17 about = "Smart Tree - An intelligent directory visualization tool. Not just a tree, it's a smart-tree!",
18 author
19)]
20pub struct Cli {
21 // =========================================================================
22 // GETTING STARTED
23 // =========================================================================
24 /// Show the cheatsheet - quick reference for all commands
25 #[arg(long, exclusive = true, help_heading = "Getting Started")]
26 pub cheet: bool,
27
28 /// Show version information and check for updates
29 #[arg(short = 'V', long, exclusive = true, help_heading = "Getting Started")]
30 pub version: bool,
31
32 /// Generate shell completion scripts (bash, zsh, fish, powershell)
33 #[arg(
34 long,
35 exclusive = true,
36 value_name = "SHELL",
37 help_heading = "Getting Started"
38 )]
39 pub completions: Option<clap_complete::Shell>,
40
41 /// Generate the man page
42 #[arg(long, exclusive = true, help_heading = "Getting Started")]
43 pub man: bool,
44
45 /// Check for updates and install the latest version
46 #[arg(long, exclusive = true, help_heading = "Getting Started")]
47 pub update: bool,
48
49 /// Skip the automatic update check on startup
50 #[arg(long, help_heading = "Getting Started")]
51 pub no_update_check: bool,
52
53 // =========================================================================
54 // INTERACTIVE MODES
55 // =========================================================================
56 /// Launch Spicy TUI - interactive file browser with fuzzy search!
57 #[arg(long, help_heading = "Interactive Modes")]
58 pub spicy: bool,
59
60 /// Launch Smart Tree Terminal Interface (STTI)
61 #[arg(long, exclusive = true, help_heading = "Interactive Modes")]
62 pub terminal: bool,
63
64 /// Launch web dashboard (browser-based terminal + file browser)
65 #[arg(long, exclusive = true, help_heading = "Interactive Modes")]
66 pub dashboard: bool,
67
68 /// Open browser automatically when starting dashboard
69 #[arg(long, requires = "dashboard", help_heading = "Interactive Modes")]
70 pub open_browser: bool,
71
72 /// Network CIDR allow-list for dashboard (e.g., 192.168.1.0/24)
73 #[arg(long, value_name = "CIDR", requires = "dashboard", help_heading = "Interactive Modes")]
74 pub allow: Vec<String>,
75
76 /// Start HTTP daemon (MCP over HTTP, LLM proxy, The Custodian)
77 #[arg(long, alias = "daemon", help_heading = "Interactive Modes")]
78 pub http_daemon: bool,
79
80 // =========================================================================
81 // MCP SERVER (via Daemon)
82 // =========================================================================
83 /// Run as MCP server for AI assistants (auto-starts daemon)
84 #[arg(long, exclusive = true, help_heading = "MCP Server")]
85 pub mcp: bool,
86
87 /// Install Smart Tree as MCP server in Claude Desktop
88 #[arg(long, exclusive = true, help_heading = "MCP Server")]
89 pub mcp_install: bool,
90
91 /// Uninstall Smart Tree MCP server from Claude Desktop
92 #[arg(long, exclusive = true, help_heading = "MCP Server")]
93 pub mcp_uninstall: bool,
94
95 /// Check MCP installation status in Claude Desktop
96 #[arg(long, exclusive = true, help_heading = "MCP Server")]
97 pub mcp_status: bool,
98
99 // =========================================================================
100 // DAEMON CONTROL
101 // =========================================================================
102 /// Set the log level
103 #[arg(long, value_enum, help_heading = "Daemon Control")]
104 pub log_level: Option<LogLevel>,
105
106 /// Start the Smart Tree daemon
107 #[arg(long, exclusive = true, help_heading = "Daemon Control")]
108 pub daemon_start: bool,
109
110 /// Stop the Smart Tree daemon
111 #[arg(long, exclusive = true, help_heading = "Daemon Control")]
112 pub daemon_stop: bool,
113
114 /// Show Smart Tree daemon status
115 #[arg(long, exclusive = true, help_heading = "Daemon Control")]
116 pub daemon_status: bool,
117
118 /// Get context from the daemon
119 #[arg(long, exclusive = true, help_heading = "Daemon Control")]
120 pub daemon_context: bool,
121
122 /// List projects tracked by the daemon
123 #[arg(long, exclusive = true, help_heading = "Daemon Control")]
124 pub daemon_projects: bool,
125
126 /// Show Foken credits from daemon
127 #[arg(long, exclusive = true, help_heading = "Daemon Control")]
128 pub daemon_credits: bool,
129
130 /// [DEPRECATED: use `st service install`] Install daemon as a system service
131 #[arg(long, exclusive = true, help_heading = "Daemon Control", hide = true)]
132 pub daemon_install: bool,
133
134 // =========================================================================
135 // CONSCIOUSNESS & MEMORY
136 // =========================================================================
137 /// Save agent consciousness state to .aye_consciousness.m8
138 #[arg(long, exclusive = true, help_heading = "Consciousness & Memory")]
139 pub agent_save: bool,
140
141 /// Restore agent consciousness from .aye_consciousness.m8
142 #[arg(long, exclusive = true, help_heading = "Consciousness & Memory")]
143 pub agent_restore: bool,
144
145 /// Show agent consciousness status and summary
146 #[arg(long, exclusive = true, help_heading = "Consciousness & Memory")]
147 pub agent_context: bool,
148
149 /// Ultra-compressed consciousness restoration format
150 #[arg(long, exclusive = true, help_heading = "Consciousness & Memory")]
151 pub agent_kickstart: bool,
152
153 /// Dump raw consciousness file content for debugging
154 #[arg(long, exclusive = true, help_heading = "Consciousness & Memory")]
155 pub agent_dump: bool,
156
157 /// Anchor a memory: --memory-anchor <TYPE> <KEYWORDS> <CONTEXT>
158 /// Types: insight, decision, pattern, gotcha, todo
159 #[arg(long, num_args = 3, value_names = ["TYPE", "KEYWORDS", "CONTEXT"], help_heading = "Consciousness & Memory")]
160 pub memory_anchor: Option<Vec<String>>,
161
162 /// Find memories by keywords (comma-separated)
163 #[arg(long, value_name = "KEYWORDS", help_heading = "Consciousness & Memory")]
164 pub memory_find: Option<String>,
165
166 /// Show memory statistics
167 #[arg(long, exclusive = true, help_heading = "Consciousness & Memory")]
168 pub memory_stats: bool,
169
170 /// Update .m8 consciousness files for a directory
171 #[arg(long, value_name = "PATH", help_heading = "Consciousness & Memory")]
172 pub update_consciousness: Option<String>,
173
174 // =========================================================================
175 // SECURITY
176 // =========================================================================
177 /// Scan codebase for supply chain attack patterns (default: current dir)
178 #[arg(long, value_name = "PATH", default_missing_value = ".", num_args = 0..=1, help_heading = "Security")]
179 pub security_scan: Option<String>,
180
181 /// Scan a file for prompt injection patterns
182 #[arg(long, value_name = "FILE", help_heading = "Security")]
183 pub guardian_scan: Option<String>,
184
185 /// Run Guardian daemon for system-wide AI protection
186 #[arg(long, exclusive = true, help_heading = "Security")]
187 pub guardian_daemon: bool,
188
189 /// Security cleanup - detect and remove malicious MCP entries
190 #[arg(long, exclusive = true, help_heading = "Security")]
191 pub cleanup: bool,
192
193 // =========================================================================
194 // HOOKS
195 // =========================================================================
196 /// Install Smart Tree hooks to AI Agent settings
197 #[arg(long, exclusive = true, help_heading = "Hooks")]
198 pub hooks_install: bool,
199
200 /// Manage hooks: enable, disable, status
201 #[arg(long, value_name = "ACTION", value_parser = ["enable", "disable", "status"], help_heading = "Hooks")]
202 pub hooks_config: Option<String>,
203
204 // =========================================================================
205 // MEGA SESSIONS
206 // =========================================================================
207 /// Start a mega session (persistent cross-context conversation)
208 #[arg(long, value_name = "NAME", default_missing_value = "", num_args = 0..=1, help_heading = "Mega Sessions")]
209 pub mega_start: Option<String>,
210
211 /// Save current mega session snapshot
212 #[arg(long, exclusive = true, help_heading = "Mega Sessions")]
213 pub mega_save: bool,
214
215 /// List all mega sessions
216 #[arg(long, exclusive = true, help_heading = "Mega Sessions")]
217 pub mega_list: bool,
218
219 /// Show mega session statistics
220 #[arg(long, exclusive = true, help_heading = "Mega Sessions")]
221 pub mega_stats: bool,
222
223 // =========================================================================
224 // ANALYSIS
225 // =========================================================================
226 /// Show tokenization statistics for a path
227 #[arg(long, value_name = "PATH", help_heading = "Analysis")]
228 pub token_stats: Option<String>,
229
230 /// Get wave frequency for a directory
231 #[arg(long, value_name = "PATH", help_heading = "Analysis")]
232 pub get_frequency: Option<String>,
233
234 // =========================================================================
235 // LOGGING & TRANSPARENCY
236 // =========================================================================
237 /// Enable activity logging to JSONL file
238 #[arg(long, value_name = "PATH", help_heading = "Logging & Transparency")]
239 pub log: Option<Option<String>>,
240
241 /// Control smart tips (on/off)
242 #[arg(long, value_name = "STATE", value_parser = ["on", "off"], help_heading = "Logging & Transparency")]
243 pub tips: Option<String>,
244
245 // =========================================================================
246 // TOP-LEVEL COMMANDS
247 // =========================================================================
248 #[command(subcommand)]
249 pub cmd: Option<Cmd>,
250
251 // =========================================================================
252 // SCAN OPTIONS
253 // =========================================================================
254 /// Path to analyze (directory, file, URL, or stream)
255 pub path: Option<String>,
256
257 /// Specify input type explicitly (filesystem, qcp, sse, openapi, mem8)
258 #[arg(long, value_name = "TYPE")]
259 pub input: Option<String>,
260
261 #[command(flatten)]
262 pub scan_opts: ScanArgs,
263}
264
265#[derive(Parser, Debug)]
266pub struct ScanArgs {
267 // =========================================================================
268 // OUTPUT FORMAT
269 // =========================================================================
270 /// Output format (classic, ai, quantum, json, etc.)
271 #[arg(
272 short,
273 long,
274 value_enum,
275 default_value = "auto",
276 help_heading = "Output Format"
277 )]
278 pub mode: OutputMode,
279
280 // =========================================================================
281 // FILTERING - What to include/exclude
282 // =========================================================================
283 /// Find files matching regex pattern (e.g., --find "README\.md")
284 #[arg(long, help_heading = "Filtering")]
285 pub find: Option<String>,
286
287 /// Filter by file extension (e.g., --type rs)
288 #[arg(long = "type", help_heading = "Filtering")]
289 pub filter_type: Option<String>,
290
291 /// Filter by entry type: f (files) or d (directories)
292 #[arg(long = "entry-type", value_parser = ["f", "d"], help_heading = "Filtering")]
293 pub entry_type: Option<String>,
294
295 /// Only files larger than size (e.g., --min-size 1M)
296 #[arg(long, help_heading = "Filtering")]
297 pub min_size: Option<String>,
298
299 /// Only files smaller than size (e.g., --max-size 100K)
300 #[arg(long, help_heading = "Filtering")]
301 pub max_size: Option<String>,
302
303 /// Files newer than date (YYYY-MM-DD)
304 #[arg(long, help_heading = "Filtering")]
305 pub newer_than: Option<String>,
306
307 /// Files older than date (YYYY-MM-DD)
308 #[arg(long, help_heading = "Filtering")]
309 pub older_than: Option<String>,
310
311 // =========================================================================
312 // TRAVERSAL - How to scan
313 // =========================================================================
314 /// Traversal depth (0 = auto, 1 = shallow, 10 = deep)
315 #[arg(short, long, default_value = "0", help_heading = "Traversal")]
316 pub depth: usize,
317
318 /// Ignore .gitignore files
319 #[arg(long, help_heading = "Traversal")]
320 pub no_ignore: bool,
321
322 /// Ignore default patterns (node_modules, __pycache__, etc.)
323 #[arg(long, help_heading = "Traversal")]
324 pub no_default_ignore: bool,
325
326 /// Show hidden files (starting with .)
327 #[arg(long, short = 'a', help_heading = "Traversal")]
328 pub all: bool,
329
330 /// Show ignored directories in brackets
331 #[arg(long, help_heading = "Traversal")]
332 pub show_ignored: bool,
333
334 /// Show EVERYTHING (--all + --no-ignore + --no-default-ignore)
335 #[arg(long, help_heading = "Traversal")]
336 pub everything: bool,
337
338 // =========================================================================
339 // SMART SCANNING - Intelligent context-aware output
340 // =========================================================================
341 /// Enable smart mode - surface what matters, not everything
342 /// Groups output by interest: security, changes, important, background
343 #[arg(long, help_heading = "Smart Scanning")]
344 pub smart: bool,
345
346 /// Only show changes since last scan
347 #[arg(long, help_heading = "Smart Scanning")]
348 pub changes_only: bool,
349
350 /// Minimum interest level (0.0-1.0) to display
351 #[arg(long, default_value = "0.0", help_heading = "Smart Scanning")]
352 pub min_interest: f32,
353
354 /// Disable security scanning during traversal
355 #[arg(long, help_heading = "Smart Scanning")]
356 pub no_security: bool,
357
358 // =========================================================================
359 // DISPLAY - How output looks
360 // =========================================================================
361 /// Show filesystem type indicators (X=XFS, 4=ext4, B=Btrfs)
362 #[arg(long, help_heading = "Display")]
363 pub show_filesystems: bool,
364
365 /// Disable emojis (Trish will miss them!)
366 #[arg(long, help_heading = "Display")]
367 pub no_emoji: bool,
368
369 /// Compress output with zlib (base64 encoded)
370 #[arg(short = 'z', long, help_heading = "Display")]
371 pub compress: bool,
372
373 /// Optimize for MCP/API (compression + no colors/emoji)
374 #[arg(long, help_heading = "Display")]
375 pub mcp_optimize: bool,
376
377 /// Compact JSON (single line)
378 #[arg(long, help_heading = "Display")]
379 pub compact: bool,
380
381 /// Path display: off, relative, or full
382 #[arg(
383 long = "path-mode",
384 value_enum,
385 default_value = "off",
386 help_heading = "Display"
387 )]
388 pub path_mode: PathMode,
389
390 /// Color output: always, never, or auto
391 #[arg(long, value_enum, default_value = "auto", help_heading = "Display")]
392 pub color: ColorMode,
393
394 /// Wrap AI output in JSON structure
395 #[arg(long, help_heading = "Display")]
396 pub ai_json: bool,
397
398 // =========================================================================
399 // STREAMING - Real-time output
400 // =========================================================================
401 /// Stream output as files are scanned
402 #[arg(long, help_heading = "Streaming")]
403 pub stream: bool,
404
405 /// Start SSE server for real-time monitoring
406 #[arg(long, help_heading = "Streaming")]
407 pub sse_server: bool,
408
409 /// SSE server port (also used as daemon port)
410 #[arg(long, alias = "daemon-port", default_value = "28428", help_heading = "Streaming")]
411 pub sse_port: u16,
412
413 // =========================================================================
414 // SEARCH & ANALYSIS
415 // =========================================================================
416 /// Search file contents (e.g., --search "TODO")
417 #[arg(long, help_heading = "Search & Analysis")]
418 pub search: Option<String>,
419
420 /// Group by semantic similarity
421 #[arg(long, help_heading = "Search & Analysis")]
422 pub semantic: bool,
423
424 /// Focus analysis on specific file (relations mode)
425 #[arg(long, value_name = "FILE", help_heading = "Search & Analysis")]
426 pub focus: Option<PathBuf>,
427
428 /// Filter relationships: imports, calls, types, tests, coupled
429 #[arg(long, value_name = "TYPE", help_heading = "Search & Analysis")]
430 pub relations_filter: Option<String>,
431
432 // =========================================================================
433 // SORTING
434 // =========================================================================
435 /// Sort by: a-to-z, z-to-a, largest, smallest, newest, oldest, type
436 #[arg(long, value_enum, help_heading = "Sorting")]
437 pub sort: Option<SortField>,
438
439 /// Show only top N results (use with --sort)
440 #[arg(long, value_name = "N", help_heading = "Sorting")]
441 pub top: Option<usize>,
442
443 // =========================================================================
444 // MERMAID & MARKDOWN OPTIONS
445 // =========================================================================
446 /// Mermaid style: flowchart, mindmap, gitgraph, treemap
447 #[arg(
448 long,
449 value_enum,
450 default_value = "flowchart",
451 help_heading = "Mermaid & Markdown"
452 )]
453 pub mermaid_style: MermaidStyleArg,
454
455 /// Exclude mermaid diagrams from markdown
456 #[arg(long, help_heading = "Mermaid & Markdown")]
457 pub no_markdown_mermaid: bool,
458
459 /// Exclude tables from markdown
460 #[arg(long, help_heading = "Mermaid & Markdown")]
461 pub no_markdown_tables: bool,
462
463 /// Exclude pie charts from markdown
464 #[arg(long, help_heading = "Mermaid & Markdown")]
465 pub no_markdown_pie_charts: bool,
466
467 // =========================================================================
468 // ADVANCED
469 // =========================================================================
470 /// Index code to SmartPastCode registry
471 #[arg(long, value_name = "URL", help_heading = "Advanced")]
472 pub index_registry: Option<String>,
473
474 /// Show private functions in docs (function-markdown mode)
475 #[arg(long, help_heading = "Advanced")]
476 pub show_private: bool,
477
478 /// View Smart Edit diffs from .st folder
479 #[arg(long, help_heading = "Advanced")]
480 pub view_diffs: bool,
481
482 /// Clean up old diffs, keep last N per file
483 #[arg(long, value_name = "N", help_heading = "Advanced")]
484 pub cleanup_diffs: Option<usize>,
485}
486
487#[derive(Debug, Subcommand)]
488pub enum Cmd {
489 /// Manage the smart-tree daemon (Linux: systemd, macOS: launchctl, Windows: Task Scheduler)
490 #[command(subcommand)]
491 Service(Service),
492
493 /// Manage project tags
494 #[command(subcommand, name = "project-tags")]
495 ProjectTags(ProjectTags),
496}
497
498#[derive(Debug, Subcommand)]
499pub enum Service {
500 /// Install the smart-tree daemon as a system service
501 Install,
502 /// Uninstall the service
503 Uninstall,
504 /// Start the service for the current project
505 Start,
506 /// Stop the service
507 Stop,
508 /// Show service status
509 Status,
510 /// Show service logs
511 Logs,
512}
513
514#[derive(Debug, Subcommand)]
515pub enum ProjectTags {
516 /// Add a tag to the project
517 Add {
518 /// The tag to add
519 #[arg(required = true)]
520 tag: String,
521 },
522 /// Remove a tag from the project
523 Remove {
524 /// The tag to remove
525 #[arg(required = true)]
526 tag: String,
527 },
528}
529
530/// Sort field options with intuitive names
531#[derive(Debug, Clone, Copy, ValueEnum)]
532pub enum SortField {
533 /// Sort alphabetically A to Z
534 #[value(name = "a-to-z")]
535 AToZ,
536 /// Sort alphabetically Z to A
537 #[value(name = "z-to-a")]
538 ZToA,
539 /// Sort by size, largest files first
540 #[value(name = "largest")]
541 Largest,
542 /// Sort by size, smallest files first
543 #[value(name = "smallest")]
544 Smallest,
545 /// Sort by modification date, newest first
546 #[value(name = "newest")]
547 Newest,
548 /// Sort by modification date, oldest first
549 #[value(name = "oldest")]
550 Oldest,
551 /// Sort by file type/extension
552 #[value(name = "type")]
553 Type,
554 /// Legacy aliases for backward compatibility
555 #[value(name = "name", alias = "alpha")]
556 Name,
557 #[value(name = "size")]
558 Size,
559 #[value(name = "date", alias = "modified")]
560 Date,
561}
562
563/// Enum for mermaid style argument
564#[derive(Debug, Clone, Copy, ValueEnum)]
565pub enum MermaidStyleArg {
566 /// Traditional flowchart (default)
567 Flowchart,
568 /// Mind map style
569 Mindmap,
570 /// Git graph style
571 Gitgraph,
572 /// Treemap style (shows file sizes visually)
573 Treemap,
574}
575
576/// Color mode for output
577#[derive(Debug, Clone, Copy, ValueEnum)]
578pub enum ColorMode {
579 /// Always use colors
580 Always,
581 /// Never use colors
582 Never,
583 /// Auto-detect (colors if terminal)
584 Auto,
585}
586
587/// Path display mode
588#[derive(Debug, Clone, Copy, ValueEnum)]
589pub enum PathMode {
590 /// Show only filenames (default)
591 Off,
592 /// Show paths relative to scan root
593 Relative,
594 /// Show full absolute paths
595 Full,
596}
597
598/// Output format mode
599#[derive(Debug, Clone, Copy, ValueEnum, PartialEq)]
600pub enum OutputMode {
601 /// Auto mode - smart default selection based on context
602 Auto,
603 /// Classic tree format with metadata and emojis
604 Classic,
605 /// Hexadecimal format with fixed-width fields
606 Hex,
607 /// HexTree - readable quantum compression with tree structure
608 HexTree,
609 /// JSON output for programmatic use
610 Json,
611 /// Unix ls -Alh format
612 Ls,
613 /// AI-optimized format for LLMs
614 Ai,
615 /// Directory statistics only
616 Stats,
617 /// CSV format
618 Csv,
619 /// TSV format
620 Tsv,
621 /// Super compact digest format
622 Digest,
623 /// Emotional tree - files with feelings!
624 Emotional,
625 /// MEM|8 Quantum format - ultimate compression
626 Quantum,
627 /// Semantic grouping format
628 Semantic,
629 /// Projects discovery mode
630 Projects,
631 /// Mermaid diagram format
632 Mermaid,
633 /// Markdown report format
634 Markdown,
635 /// Interactive summary mode
636 Summary,
637 /// AI-optimized summary mode
638 SummaryAi,
639 /// Context mode for AI conversations
640 Context,
641 /// Code relationship analysis
642 Relations,
643 /// Quantum compression with semantic understanding
644 QuantumSemantic,
645 /// Waste detection and optimization analysis
646 Waste,
647 /// Marqant - Quantum-compressed markdown format
648 Marqant,
649 /// SSE - Server-Sent Events streaming format
650 Sse,
651 /// Function documentation in markdown format
652 FunctionMarkdown,
653}
654
655/// Get the ideal depth for each output mode
656pub fn get_ideal_depth_for_mode(mode: &OutputMode) -> usize {
657 match mode {
658 OutputMode::Auto => 3,
659 OutputMode::Ls => 1,
660 OutputMode::Classic => 3,
661 OutputMode::Ai | OutputMode::Hex => 5,
662 OutputMode::Stats => 10,
663 OutputMode::Digest => 10,
664 OutputMode::Emotional => 5,
665 OutputMode::Quantum | OutputMode::QuantumSemantic | OutputMode::HexTree => 5,
666 OutputMode::Summary | OutputMode::SummaryAi | OutputMode::Context => 4,
667 OutputMode::Waste => 10,
668 OutputMode::Relations => 10,
669 OutputMode::Projects => 5,
670 _ => 4,
671 }
672}
673
674/// Parse a date string (YYYY-MM-DD) into SystemTime
675pub fn parse_date(date_str: &str) -> Result<SystemTime> {
676 let date = NaiveDate::parse_from_str(date_str, "%Y-%m-%d")?;
677 let datetime = date.and_hms_opt(0, 0, 0).context("Invalid time")?;
678 Ok(SystemTime::from(
679 datetime
680 .and_local_timezone(chrono::Local)
681 .single()
682 .context("Invalid timezone")?,
683 ))
684}
685
686#[derive(Debug, Clone, Copy, ValueEnum)]
687pub enum LogLevel {
688 Error,
689 Warn,
690 Info,
691 Debug,
692 Trace,
693}