Skip to main content

chasm_cli/
cli.rs

1// Copyright (c) 2024-2026 Nervosys LLC
2// SPDX-License-Identifier: Apache-2.0
3//! CLI argument definitions using clap derive macros
4
5use clap::{Parser, Subcommand};
6
7/// Chat System Manager (csm) - Manage and merge chat sessions across workspaces
8#[derive(Parser)]
9#[command(name = "csm")]
10#[command(author = "Nervosys")]
11#[command(version)]
12#[command(about = "Manage and merge chat sessions across workspaces", long_about = None)]
13pub struct Cli {
14    #[command(subcommand)]
15    pub command: Commands,
16}
17
18#[derive(Subcommand)]
19pub enum Commands {
20    // ============================================================================
21    // List Commands
22    // ============================================================================
23    /// List workspaces, sessions, or paths
24    #[command(visible_alias = "ls")]
25    List {
26        #[command(subcommand)]
27        command: Option<ListCommands>,
28    },
29
30    // ============================================================================
31    // Find Commands
32    // ============================================================================
33    /// Search workspaces or sessions by text pattern (title, content, ID)
34    Find {
35        #[command(subcommand)]
36        command: Option<FindCommands>,
37    },
38
39    // ============================================================================
40    // Show Commands
41    // ============================================================================
42    /// Show workspaces, sessions, or paths
43    #[command(visible_alias = "info")]
44    Show {
45        #[command(subcommand)]
46        command: Option<ShowCommands>,
47    },
48
49    // ============================================================================
50    // Fetch Commands
51    // ============================================================================
52    /// Fetch chat sessions from workspaces, sessions, or paths
53    Fetch {
54        #[command(subcommand)]
55        command: Option<FetchCommands>,
56    },
57
58    // ============================================================================
59    // Merge Commands
60    // ============================================================================
61    /// Merge chat sessions from workspaces, sessions, or paths
62    Merge {
63        #[command(subcommand)]
64        command: Option<MergeCommands>,
65    },
66
67    // ============================================================================
68    // Export Commands
69    // ============================================================================
70    /// Export chat sessions from workspaces, sessions, or paths
71    Export {
72        #[command(subcommand)]
73        command: Option<ExportCommands>,
74    },
75
76    // ============================================================================
77    // Import Commands
78    // ============================================================================
79    /// Import session files from external directories into a workspace
80    Import {
81        #[command(subcommand)]
82        command: Option<ImportCommands>,
83    },
84
85    // ============================================================================
86    // Move Commands
87    // ============================================================================
88    /// Move chat sessions between workspaces
89    #[command(visible_alias = "mv")]
90    Move {
91        #[command(subcommand)]
92        command: Option<MoveCommands>,
93    },
94
95    // ============================================================================
96    // Git Integration Commands
97    // ============================================================================
98    /// Git integration for chat session versioning
99    Git {
100        #[command(subcommand)]
101        command: GitCommands,
102    },
103
104    // ============================================================================
105    // Migration Commands
106    // ============================================================================
107    /// Migration commands for moving chat sessions between machines
108    Migration {
109        #[command(subcommand)]
110        command: MigrationCommands,
111    },
112
113    // ============================================================================
114    // Run Commands (TUI)
115    // ============================================================================
116    /// Run interactive tools
117    Run {
118        #[command(subcommand)]
119        command: RunCommands,
120    },
121
122    // ============================================================================
123    // Provider Commands
124    // ============================================================================
125    /// Manage LLM providers (Ollama, vLLM, Foundry, Cursor, etc.)
126    Provider {
127        #[command(subcommand)]
128        command: ProviderCommands,
129    },
130
131    // ============================================================================
132    // Detect Commands
133    // ============================================================================
134    /// Auto-detect workspace and provider information
135    Detect {
136        #[command(subcommand)]
137        command: Option<DetectCommands>,
138    },
139
140    // ============================================================================
141    // Register Commands
142    // ============================================================================
143    /// Add on-disk sessions to VS Code's database index (makes orphaned sessions visible)
144    #[command(visible_alias = "sync")]
145    Register {
146        #[command(subcommand)]
147        command: RegisterCommands,
148    },
149
150    // ============================================================================
151    // Harvest Commands
152    // ============================================================================
153    /// Harvest chat sessions from all providers into a unified database
154    Harvest {
155        #[command(subcommand)]
156        command: HarvestCommands,
157    },
158
159    // ============================================================================
160    // API Server Commands
161    // ============================================================================
162    /// Start the HTTP API server for the web frontend
163    #[command(visible_alias = "serve")]
164    Api {
165        #[command(subcommand)]
166        command: ApiCommands,
167    },
168
169    // ============================================================================
170    // Agency Commands
171    // ============================================================================
172    /// Agent Development Kit - manage agents and orchestration
173    Agency {
174        #[command(subcommand)]
175        command: AgencyCommands,
176    },
177
178    // ============================================================================
179    // Telemetry Commands
180    // ============================================================================
181    /// Manage anonymous usage data collection (opt-in by default)
182    Telemetry {
183        #[command(subcommand)]
184        command: Option<TelemetryCommands>,
185    },
186
187    // ============================================================================
188    // Easter Egg
189    // ============================================================================
190    /// Show banner
191    #[command(hide = true)]
192    Banner,
193}
194
195// ============================================================================
196// List Subcommands
197// ============================================================================
198
199#[derive(Subcommand)]
200pub enum ListCommands {
201    /// List all VS Code workspaces
202    #[command(visible_alias = "ws")]
203    Workspaces,
204
205    /// List all chat sessions
206    #[command(visible_alias = "s")]
207    Sessions {
208        /// Filter by project path
209        #[arg(long)]
210        project_path: Option<String>,
211
212        /// Show file sizes
213        #[arg(long, short = 's')]
214        size: bool,
215
216        /// Filter by provider (vscode, cursor, claudecode, opencode, openclaw, antigravity)
217        #[arg(long, short = 'p')]
218        provider: Option<String>,
219
220        /// Include all providers
221        #[arg(long)]
222        all_providers: bool,
223    },
224
225    /// List agent mode sessions (Copilot Edits / chatEditingSessions)
226    #[command(visible_alias = "a")]
227    Agents {
228        /// Filter by project path
229        #[arg(long)]
230        project_path: Option<String>,
231
232        /// Show file sizes
233        #[arg(long, short = 's')]
234        size: bool,
235
236        /// Filter by provider (vscode, cursor, claudecode, opencode, openclaw, antigravity)
237        #[arg(long, short = 'p')]
238        provider: Option<String>,
239    },
240
241    /// List sessions for a specific project path
242    Path {
243        /// Project path (default: current directory)
244        project_path: Option<String>,
245    },
246
247    /// List unregistered sessions (exist on disk but invisible to VS Code)
248    Orphaned {
249        /// Project path (default: current directory)
250        #[arg(long)]
251        path: Option<String>,
252    },
253}
254
255// ============================================================================
256// Find Subcommands
257// ============================================================================
258
259#[derive(Subcommand)]
260pub enum FindCommands {
261    /// Search workspaces by name pattern (defaults to current directory name)
262    #[command(visible_alias = "ws")]
263    Workspace {
264        /// Text pattern to match (case-insensitive, defaults to current directory name)
265        pattern: Option<String>,
266    },
267
268    /// Search sessions by title, content, or ID pattern
269    #[command(visible_alias = "s")]
270    Session {
271        /// Text pattern to match (case-insensitive, defaults to current directory name)
272        pattern: Option<String>,
273
274        /// Filter by project path or workspace name
275        #[arg(long, short = 'w')]
276        workspace: Option<String>,
277
278        /// Only search in session titles (faster, skip content search)
279        #[arg(long, short = 't')]
280        title_only: bool,
281
282        /// Include message content in search (slower)
283        #[arg(long, short = 'c')]
284        content: bool,
285
286        /// Filter sessions modified after this date (YYYY-MM-DD)
287        #[arg(long)]
288        after: Option<String>,
289
290        /// Filter sessions modified before this date (YYYY-MM-DD)
291        #[arg(long)]
292        before: Option<String>,
293
294        /// Filter by internal message timestamp date (YYYY-MM-DD)
295        #[arg(long)]
296        date: Option<String>,
297
298        /// Search across all workspaces (not just current project)
299        #[arg(long, short = 'a')]
300        all: bool,
301
302        /// Filter by provider (vscode, cursor, claudecode, opencode, openclaw, antigravity)
303        #[arg(long, short = 'p')]
304        provider: Option<String>,
305
306        /// Search across all providers
307        #[arg(long)]
308        all_providers: bool,
309
310        /// Limit number of results
311        #[arg(long, short = 'n', default_value = "50")]
312        limit: usize,
313    },
314
315    /// Search sessions within a specific project path
316    Path {
317        /// Search pattern (case-insensitive, defaults to current directory name)
318        pattern: Option<String>,
319
320        /// Project path (default: current directory)
321        #[arg(long)]
322        project_path: Option<String>,
323    },
324}
325
326// ============================================================================
327// Show Subcommands
328// ============================================================================
329
330#[derive(Subcommand)]
331pub enum ShowCommands {
332    /// Show workspace details
333    #[command(visible_alias = "ws")]
334    Workspace {
335        /// Workspace name or hash
336        workspace: String,
337    },
338
339    /// Show session details
340    #[command(visible_alias = "s")]
341    Session {
342        /// Session ID or filename
343        session_id: String,
344
345        /// Project path to search in
346        #[arg(long)]
347        project_path: Option<String>,
348    },
349
350    /// Show agent mode session details (Copilot Edits)
351    #[command(visible_alias = "a")]
352    Agent {
353        /// Agent session ID
354        session_id: String,
355
356        /// Project path to search in
357        #[arg(long)]
358        project_path: Option<String>,
359    },
360
361    /// Show chat history timeline for a project path
362    Path {
363        /// Path to the project (default: current directory)
364        project_path: Option<String>,
365    },
366
367    /// Show timeline of session activity with gaps visualization
368    Timeline {
369        /// Path to the project (default: current directory)
370        project_path: Option<String>,
371
372        /// Include agent mode sessions
373        #[arg(long, short = 'a')]
374        agents: bool,
375
376        /// Filter by provider (vscode, cursor, claudecode, opencode, openclaw, antigravity)
377        #[arg(long, short = 'p')]
378        provider: Option<String>,
379
380        /// Include all providers (aggregate timeline)
381        #[arg(long)]
382        all_providers: bool,
383    },
384}
385
386// ============================================================================
387// Fetch Subcommands
388// ============================================================================
389
390#[derive(Subcommand)]
391pub enum FetchCommands {
392    /// Fetch sessions from workspaces matching a pattern
393    #[command(visible_alias = "ws")]
394    Workspace {
395        /// Workspace name pattern (case-insensitive)
396        workspace_name: String,
397
398        /// Target project path (default: current directory)
399        #[arg(long)]
400        target_path: Option<String>,
401
402        /// Overwrite existing sessions
403        #[arg(long)]
404        force: bool,
405
406        /// Don't register sessions in VS Code index
407        #[arg(long)]
408        no_register: bool,
409    },
410
411    /// Fetch specific sessions by ID
412    #[command(visible_alias = "s")]
413    Session {
414        /// Session IDs to fetch (space-separated)
415        #[arg(required = true, num_args = 1..)]
416        session_ids: Vec<String>,
417
418        /// Target project path (default: current directory)
419        #[arg(long)]
420        target_path: Option<String>,
421
422        /// Overwrite existing sessions
423        #[arg(long)]
424        force: bool,
425
426        /// Don't register sessions in VS Code index
427        #[arg(long)]
428        no_register: bool,
429    },
430
431    /// Fetch chat sessions from other workspaces by project path
432    Path {
433        /// Path to the project (default: current directory)
434        project_path: Option<String>,
435
436        /// Overwrite existing sessions and skip VS Code running check
437        #[arg(long)]
438        force: bool,
439
440        /// Don't register sessions in VS Code index
441        #[arg(long)]
442        no_register: bool,
443    },
444}
445
446// ============================================================================
447// Merge Subcommands
448// ============================================================================
449
450#[derive(Subcommand)]
451pub enum MergeCommands {
452    /// Merge sessions from workspaces matching a name pattern
453    #[command(visible_alias = "ws")]
454    Workspace {
455        /// Workspace name pattern to search for (case-insensitive)
456        workspace_name: String,
457
458        /// Title for the merged session
459        #[arg(short, long)]
460        title: Option<String>,
461
462        /// Target project path to save the merged session (default: current directory)
463        #[arg(long)]
464        target_path: Option<String>,
465
466        /// Skip VS Code running check
467        #[arg(long)]
468        force: bool,
469
470        /// Don't create backup of current sessions
471        #[arg(long)]
472        no_backup: bool,
473    },
474
475    /// Merge sessions from multiple workspace name patterns
476    #[command(visible_alias = "wss")]
477    Workspaces {
478        /// Workspace name patterns to search for (space-separated, case-insensitive)
479        #[arg(required = true, num_args = 1..)]
480        workspace_names: Vec<String>,
481
482        /// Title for the merged session
483        #[arg(short, long)]
484        title: Option<String>,
485
486        /// Target project path to save the merged session (default: current directory)
487        #[arg(long)]
488        target_path: Option<String>,
489
490        /// Skip VS Code running check
491        #[arg(long)]
492        force: bool,
493
494        /// Don't create backup of current sessions
495        #[arg(long)]
496        no_backup: bool,
497    },
498
499    /// Merge specific sessions by their IDs or filenames
500    #[command(visible_alias = "s")]
501    Sessions {
502        /// Session IDs or filenames (comma-separated or space-separated)
503        #[arg(required = true, num_args = 1..)]
504        sessions: Vec<String>,
505
506        /// Title for the merged session
507        #[arg(short, long)]
508        title: Option<String>,
509
510        /// Target project path to save the merged session (default: current directory)
511        #[arg(long)]
512        target_path: Option<String>,
513
514        /// Skip VS Code running check
515        #[arg(long)]
516        force: bool,
517
518        /// Don't create backup of current sessions
519        #[arg(long)]
520        no_backup: bool,
521    },
522
523    /// Merge all sessions for a project path into one unified chat
524    Path {
525        /// Path to the project (default: current directory)
526        project_path: Option<String>,
527
528        /// Title for the merged session
529        #[arg(short, long)]
530        title: Option<String>,
531
532        /// Skip VS Code running check
533        #[arg(long)]
534        force: bool,
535
536        /// Don't create backup of current sessions
537        #[arg(long)]
538        no_backup: bool,
539    },
540
541    /// Merge sessions from an LLM provider (Ollama, Cursor, etc.)
542    Provider {
543        /// Provider name (copilot, cursor, ollama, vllm, foundry, etc.)
544        provider_name: String,
545
546        /// Title for the merged session
547        #[arg(short, long)]
548        title: Option<String>,
549
550        /// Target project path to save the merged session (default: current directory)
551        #[arg(long)]
552        target_path: Option<String>,
553
554        /// Session IDs from the provider to include (omit for all)
555        #[arg(long)]
556        sessions: Option<Vec<String>>,
557
558        /// Skip VS Code running check
559        #[arg(long)]
560        force: bool,
561
562        /// Don't create backup of current sessions
563        #[arg(long)]
564        no_backup: bool,
565    },
566
567    /// Merge sessions from multiple providers
568    #[command(name = "providers")]
569    Providers {
570        /// Provider names (space-separated: copilot cursor ollama)
571        #[arg(required = true, num_args = 1..)]
572        providers: Vec<String>,
573
574        /// Title for the merged session
575        #[arg(short, long)]
576        title: Option<String>,
577
578        /// Target project path to save the merged session (default: current directory)
579        #[arg(long)]
580        target_path: Option<String>,
581
582        /// Filter by workspace name pattern (applies to providers that support workspaces)
583        #[arg(long)]
584        workspace: Option<String>,
585
586        /// Skip VS Code running check
587        #[arg(long)]
588        force: bool,
589
590        /// Don't create backup of current sessions
591        #[arg(long)]
592        no_backup: bool,
593    },
594
595    /// Merge all sessions across all available providers
596    All {
597        /// Title for the merged session
598        #[arg(short, long)]
599        title: Option<String>,
600
601        /// Target project path to save the merged session (default: current directory)
602        #[arg(long)]
603        target_path: Option<String>,
604
605        /// Filter by workspace name pattern (applies to providers that support workspaces)
606        #[arg(long)]
607        workspace: Option<String>,
608
609        /// Skip VS Code running check
610        #[arg(long)]
611        force: bool,
612
613        /// Don't create backup of current sessions
614        #[arg(long)]
615        no_backup: bool,
616    },
617}
618
619// ============================================================================
620// Export Subcommands
621// ============================================================================
622
623#[derive(Subcommand)]
624pub enum ExportCommands {
625    /// Export sessions from a workspace by hash
626    #[command(visible_alias = "ws")]
627    Workspace {
628        /// Destination directory for exported sessions
629        destination: String,
630
631        /// Source workspace hash
632        hash: String,
633    },
634
635    /// Export specific sessions by ID
636    #[command(visible_alias = "s")]
637    Sessions {
638        /// Destination directory for exported sessions
639        destination: String,
640
641        /// Session IDs to export (space-separated)
642        #[arg(required = true, num_args = 1..)]
643        session_ids: Vec<String>,
644
645        /// Source project path
646        #[arg(long)]
647        project_path: Option<String>,
648    },
649
650    /// Export chat sessions from a project path
651    Path {
652        /// Destination directory for exported sessions
653        destination: String,
654
655        /// Source project path (default: current directory)
656        project_path: Option<String>,
657    },
658}
659
660// ============================================================================
661// Import Subcommands
662// ============================================================================
663
664#[derive(Subcommand)]
665pub enum ImportCommands {
666    /// Copy session files from external directory into a workspace
667    #[command(visible_alias = "ws")]
668    Workspace {
669        /// Source directory containing session JSON files to import
670        source: String,
671
672        /// Target workspace hash
673        hash: String,
674
675        /// Overwrite existing sessions
676        #[arg(long)]
677        force: bool,
678    },
679
680    /// Copy specific session files into a workspace
681    #[command(visible_alias = "s")]
682    Sessions {
683        /// Session files to import (space-separated paths)
684        #[arg(required = true, num_args = 1..)]
685        session_files: Vec<String>,
686
687        /// Target project path (default: current directory)
688        #[arg(long)]
689        target_path: Option<String>,
690
691        /// Overwrite existing sessions
692        #[arg(long)]
693        force: bool,
694    },
695
696    /// Copy session files from external directory into a project workspace
697    Path {
698        /// Source directory containing session JSON files to import
699        source: String,
700
701        /// Target project path (default: current directory)
702        target_path: Option<String>,
703
704        /// Overwrite existing sessions
705        #[arg(long)]
706        force: bool,
707    },
708}
709
710// ============================================================================
711// Move Subcommands
712// ============================================================================
713
714#[derive(Subcommand)]
715pub enum MoveCommands {
716    /// Move all sessions from one workspace to another
717    #[command(visible_alias = "ws")]
718    Workspace {
719        /// Source workspace hash
720        source_hash: String,
721
722        /// Target workspace hash or project path
723        target: String,
724    },
725
726    /// Move specific sessions by ID
727    #[command(visible_alias = "s")]
728    Sessions {
729        /// Session IDs to move (space-separated)
730        #[arg(required = true, num_args = 1..)]
731        session_ids: Vec<String>,
732
733        /// Target project path
734        target_path: String,
735    },
736
737    /// Move sessions from a source path to target path
738    Path {
739        /// Source project path
740        source_path: String,
741
742        /// Target project path
743        target_path: String,
744    },
745}
746
747// ============================================================================
748// Git Subcommands
749// ============================================================================
750
751#[derive(Subcommand)]
752pub enum GitCommands {
753    /// Configure git settings for chat sessions
754    Config {
755        /// Git user name
756        #[arg(long)]
757        name: Option<String>,
758
759        /// Git user email
760        #[arg(long)]
761        email: Option<String>,
762
763        /// Project path
764        #[arg(long)]
765        path: Option<String>,
766    },
767
768    /// Initialize git versioning for chat sessions
769    Init {
770        /// Project path
771        path: String,
772    },
773
774    /// Add chat sessions to git (stage and optionally commit)
775    Add {
776        /// Project path
777        path: String,
778
779        /// Also commit the changes
780        #[arg(long)]
781        commit: bool,
782
783        /// Commit message (requires --commit)
784        #[arg(short, long)]
785        message: Option<String>,
786    },
787
788    /// Show git status of chat sessions
789    Status {
790        /// Project path
791        path: String,
792    },
793
794    /// Create a git tag snapshot of chat sessions
795    Snapshot {
796        /// Project path
797        path: String,
798
799        /// Tag name (auto-generated if not provided)
800        #[arg(long)]
801        tag: Option<String>,
802
803        /// Snapshot message
804        #[arg(short, long)]
805        message: Option<String>,
806    },
807
808    /// Track chat sessions together with associated file changes
809    Track {
810        /// Project path
811        path: String,
812
813        /// Commit message describing the changes
814        #[arg(short, long)]
815        message: Option<String>,
816
817        /// Include all staged and unstaged file changes
818        #[arg(long)]
819        all: bool,
820
821        /// Include specific files in addition to chat sessions
822        #[arg(long)]
823        files: Option<Vec<String>>,
824
825        /// Create a tag for this tracked state
826        #[arg(long)]
827        tag: Option<String>,
828    },
829
830    /// Show history of chat session commits with associated file changes
831    Log {
832        /// Project path
833        path: String,
834
835        /// Number of commits to show
836        #[arg(short = 'n', long, default_value = "10")]
837        count: usize,
838
839        /// Show only commits that include chat session changes
840        #[arg(long)]
841        sessions_only: bool,
842    },
843
844    /// Diff chat sessions between commits or current state
845    Diff {
846        /// Project path
847        path: String,
848
849        /// First commit (default: HEAD)
850        #[arg(long)]
851        from: Option<String>,
852
853        /// Second commit (default: working directory)
854        #[arg(long)]
855        to: Option<String>,
856
857        /// Show associated file changes alongside chat diffs
858        #[arg(long)]
859        with_files: bool,
860    },
861
862    /// Restore chat sessions from a specific commit
863    Restore {
864        /// Project path
865        path: String,
866
867        /// Commit hash, tag, or reference to restore from
868        commit: String,
869
870        /// Also restore associated files from the same commit
871        #[arg(long)]
872        with_files: bool,
873
874        /// Create a backup before restoring
875        #[arg(long)]
876        backup: bool,
877    },
878}
879
880// ============================================================================
881// Migration Subcommands
882// ============================================================================
883
884#[derive(Subcommand)]
885pub enum MigrationCommands {
886    /// Create a migration package for moving to a new machine
887    Create {
888        /// Output directory for migration package
889        output: String,
890
891        /// Comma-separated list of project paths to include
892        #[arg(long)]
893        projects: Option<String>,
894
895        /// Include all workspaces with chat sessions
896        #[arg(long)]
897        all: bool,
898    },
899
900    /// Restore a migration package on a new machine
901    Restore {
902        /// Path to migration package directory
903        package: String,
904
905        /// Project path mapping: 'old1:new1;old2:new2'
906        #[arg(long)]
907        mapping: Option<String>,
908
909        /// Show what would be done without doing it
910        #[arg(long)]
911        dry_run: bool,
912    },
913}
914
915// ============================================================================
916// Run Subcommands
917// ============================================================================
918
919#[derive(Subcommand)]
920pub enum RunCommands {
921    /// Launch interactive TUI (Text User Interface)
922    Tui,
923}
924
925// ============================================================================
926// Provider Subcommands
927// ============================================================================
928
929#[derive(Subcommand)]
930pub enum ProviderCommands {
931    /// List all discovered LLM providers
932    List,
933
934    /// Show detailed info about a specific provider
935    Info {
936        /// Provider name (copilot, cursor, ollama, vllm, foundry, lm-studio, etc.)
937        provider: String,
938    },
939
940    /// Configure a provider
941    Config {
942        /// Provider name
943        provider: String,
944
945        /// API endpoint URL
946        #[arg(long)]
947        endpoint: Option<String>,
948
949        /// API key
950        #[arg(long)]
951        api_key: Option<String>,
952
953        /// Default model
954        #[arg(long)]
955        model: Option<String>,
956
957        /// Enable or disable the provider
958        #[arg(long)]
959        enabled: Option<bool>,
960    },
961
962    /// Import sessions from another provider
963    Import {
964        /// Source provider name
965        #[arg(long)]
966        from: String,
967
968        /// Target project path (or current directory)
969        #[arg(long)]
970        path: Option<String>,
971
972        /// Session ID to import (omit for all)
973        #[arg(long)]
974        session: Option<String>,
975    },
976
977    /// Test connection to a provider
978    Test {
979        /// Provider name
980        provider: String,
981    },
982}
983
984// ============================================================================
985// Detect Subcommands
986// ============================================================================
987
988#[derive(Subcommand)]
989pub enum DetectCommands {
990    /// Detect workspace for a path
991    Workspace {
992        /// Project path (default: current directory)
993        path: Option<String>,
994    },
995
996    /// Detect available providers
997    Providers {
998        /// Only show providers with sessions
999        #[arg(long)]
1000        with_sessions: bool,
1001    },
1002
1003    /// Detect which provider a session belongs to
1004    Session {
1005        /// Session ID or filename
1006        session_id: String,
1007
1008        /// Project path to search in
1009        #[arg(long)]
1010        path: Option<String>,
1011    },
1012
1013    /// Detect everything (workspace, providers, sessions) for a path
1014    All {
1015        /// Project path (default: current directory)
1016        path: Option<String>,
1017
1018        /// Show detailed information
1019        #[arg(long)]
1020        verbose: bool,
1021    },
1022
1023    /// Find all workspace hashes for a project path (including orphaned workspaces with sessions)
1024    Orphaned {
1025        /// Project path (default: current directory)
1026        path: Option<String>,
1027
1028        /// Automatically recover orphaned sessions by copying to active workspace
1029        #[arg(long, short)]
1030        recover: bool,
1031    },
1032}
1033
1034// ============================================================================
1035// Register Subcommands
1036// ============================================================================
1037
1038#[derive(Subcommand)]
1039pub enum RegisterCommands {
1040    /// Register all on-disk sessions into VS Code's index (fixes orphaned sessions)
1041    All {
1042        /// Project path (default: current directory)
1043        #[arg(long)]
1044        path: Option<String>,
1045
1046        /// Merge all sessions into one before registering
1047        #[arg(long, short)]
1048        merge: bool,
1049
1050        /// Force registration even if VS Code is running
1051        #[arg(long, short)]
1052        force: bool,
1053    },
1054
1055    /// Register specific sessions by ID or title into VS Code's index
1056    #[command(visible_alias = "s")]
1057    Session {
1058        /// Session IDs or filenames (without .json extension)
1059        #[arg(required_unless_present = "title")]
1060        ids: Vec<String>,
1061
1062        /// Match sessions by title instead of ID
1063        #[arg(long, short, num_args = 1.., value_delimiter = ' ')]
1064        title: Option<Vec<String>>,
1065
1066        /// Project path (default: current directory)
1067        #[arg(long)]
1068        path: Option<String>,
1069
1070        /// Force registration even if VS Code is running
1071        #[arg(long, short)]
1072        force: bool,
1073    },
1074
1075    /// Recursively walk directories to find and register orphaned sessions for all workspaces
1076    #[command(visible_alias = "r")]
1077    Recursive {
1078        /// Root path to start recursive search (default: current directory)
1079        path: Option<String>,
1080
1081        /// Maximum directory depth to recurse (default: unlimited)
1082        #[arg(long, short)]
1083        depth: Option<usize>,
1084
1085        /// Force registration even if VS Code is running
1086        #[arg(long, short)]
1087        force: bool,
1088
1089        /// Only show what would be registered without making changes
1090        #[arg(long)]
1091        dry_run: bool,
1092
1093        /// Skip directories matching these patterns (can be used multiple times)
1094        #[arg(long, short = 'x')]
1095        exclude: Vec<String>,
1096    },
1097}
1098
1099// ============================================================================
1100// Harvest Subcommands
1101// ============================================================================
1102
1103#[derive(Subcommand)]
1104pub enum HarvestCommands {
1105    /// Initialize a harvest database
1106    Init {
1107        /// Path to the database file (default: ./chat_sessions.db)
1108        #[arg(long)]
1109        path: Option<String>,
1110
1111        /// Initialize git tracking for the database
1112        #[arg(long)]
1113        git: bool,
1114    },
1115
1116    /// Scan for available providers and sessions
1117    Scan {
1118        /// Show individual sessions
1119        #[arg(long)]
1120        sessions: bool,
1121
1122        /// Scan for web-based LLM providers (ChatGPT, Claude, etc.)
1123        #[arg(long)]
1124        web: bool,
1125
1126        /// Timeout in seconds for web provider checks (default: 5)
1127        #[arg(long, default_value = "5")]
1128        timeout: u64,
1129
1130        /// Show verbose debug output for browser scanning
1131        #[arg(long, short)]
1132        verbose: bool,
1133    },
1134
1135    /// Run the harvest to collect sessions from all providers
1136    Run {
1137        /// Path to the harvest database
1138        #[arg(long)]
1139        path: Option<String>,
1140
1141        /// Only include specific providers (comma-separated: copilot,cursor,ollama)
1142        #[arg(long, value_delimiter = ',')]
1143        providers: Option<Vec<String>>,
1144
1145        /// Exclude specific providers (comma-separated)
1146        #[arg(long, value_delimiter = ',')]
1147        exclude: Option<Vec<String>>,
1148
1149        /// Only harvest sessions changed since last run
1150        #[arg(long)]
1151        incremental: bool,
1152
1153        /// Auto-commit changes to git after harvest
1154        #[arg(long)]
1155        commit: bool,
1156
1157        /// Commit message (requires --commit)
1158        #[arg(short, long)]
1159        message: Option<String>,
1160    },
1161
1162    /// Show harvest database status
1163    Status {
1164        /// Path to the harvest database
1165        #[arg(long)]
1166        path: Option<String>,
1167    },
1168
1169    /// List sessions in the harvest database
1170    List {
1171        /// Path to the harvest database
1172        #[arg(long)]
1173        path: Option<String>,
1174
1175        /// Filter by provider name
1176        #[arg(long)]
1177        provider: Option<String>,
1178
1179        /// Maximum number of sessions to show
1180        #[arg(long, default_value = "20")]
1181        limit: usize,
1182
1183        /// Search sessions by title or ID
1184        #[arg(long)]
1185        search: Option<String>,
1186    },
1187
1188    /// Export sessions from the harvest database
1189    Export {
1190        /// Output file path
1191        output: String,
1192
1193        /// Path to the harvest database
1194        #[arg(long)]
1195        path: Option<String>,
1196
1197        /// Export format: json, jsonl, md (markdown)
1198        #[arg(long, default_value = "json")]
1199        format: String,
1200
1201        /// Filter by provider name
1202        #[arg(long)]
1203        provider: Option<String>,
1204
1205        /// Export specific sessions by ID (comma-separated)
1206        #[arg(long, value_delimiter = ',')]
1207        sessions: Option<Vec<String>>,
1208    },
1209
1210    /// Import a shared chat session from a URL
1211    Share {
1212        /// Share link URL (ChatGPT, Claude, etc.)
1213        url: String,
1214
1215        /// Path to the harvest database
1216        #[arg(long)]
1217        path: Option<String>,
1218
1219        /// Custom name for the imported session
1220        #[arg(long)]
1221        name: Option<String>,
1222
1223        /// Associate with a workspace path
1224        #[arg(long)]
1225        workspace: Option<String>,
1226    },
1227
1228    /// List pending or imported share links
1229    Shares {
1230        /// Path to the harvest database
1231        #[arg(long)]
1232        path: Option<String>,
1233
1234        /// Filter by status: pending, imported, failed, expired
1235        #[arg(long)]
1236        status: Option<String>,
1237
1238        /// Maximum number of links to show
1239        #[arg(long, default_value = "20")]
1240        limit: usize,
1241    },
1242
1243    /// Create a checkpoint (version snapshot) of a session
1244    Checkpoint {
1245        /// Session ID to checkpoint
1246        session: String,
1247
1248        /// Path to the harvest database
1249        #[arg(long)]
1250        path: Option<String>,
1251
1252        /// Checkpoint description message
1253        #[arg(short, long)]
1254        message: Option<String>,
1255    },
1256
1257    /// List checkpoints for a session
1258    Checkpoints {
1259        /// Session ID to list checkpoints for
1260        session: String,
1261
1262        /// Path to the harvest database
1263        #[arg(long)]
1264        path: Option<String>,
1265    },
1266
1267    /// Restore a session to a previous checkpoint
1268    Restore {
1269        /// Session ID to restore
1270        session: String,
1271
1272        /// Checkpoint number to restore to
1273        checkpoint: i64,
1274
1275        /// Path to the harvest database
1276        #[arg(long)]
1277        path: Option<String>,
1278    },
1279
1280    /// Rebuild the full-text search index
1281    Rebuild {
1282        /// Path to the harvest database
1283        #[arg(long)]
1284        path: Option<String>,
1285    },
1286
1287    /// Search messages across all sessions (full-text search)
1288    Search {
1289        /// Search query
1290        query: String,
1291
1292        /// Path to the harvest database
1293        #[arg(long)]
1294        path: Option<String>,
1295
1296        /// Filter by provider
1297        #[arg(long)]
1298        provider: Option<String>,
1299
1300        /// Maximum results to show
1301        #[arg(long, default_value = "20")]
1302        limit: usize,
1303    },
1304
1305    /// Git operations for the harvest database
1306    Git {
1307        #[command(subcommand)]
1308        command: HarvestGitCommands,
1309    },
1310}
1311
1312#[derive(Subcommand)]
1313pub enum HarvestGitCommands {
1314    /// Initialize git tracking for the harvest database
1315    Init {
1316        /// Path to the harvest database
1317        #[arg(long)]
1318        path: Option<String>,
1319    },
1320
1321    /// Commit changes to the harvest database
1322    Commit {
1323        /// Path to the harvest database
1324        #[arg(long)]
1325        path: Option<String>,
1326
1327        /// Commit message
1328        #[arg(short, long)]
1329        message: Option<String>,
1330    },
1331
1332    /// Show git log for the harvest database
1333    Log {
1334        /// Path to the harvest database
1335        #[arg(long)]
1336        path: Option<String>,
1337
1338        /// Number of commits to show
1339        #[arg(short = 'n', long, default_value = "10")]
1340        count: usize,
1341    },
1342
1343    /// Show changes to the harvest database
1344    Diff {
1345        /// Path to the harvest database
1346        #[arg(long)]
1347        path: Option<String>,
1348
1349        /// Compare against specific commit
1350        #[arg(long)]
1351        commit: Option<String>,
1352    },
1353
1354    /// Restore harvest database from a commit
1355    Restore {
1356        /// Commit hash to restore from
1357        commit: String,
1358
1359        /// Path to the harvest database
1360        #[arg(long)]
1361        path: Option<String>,
1362    },
1363}
1364
1365// ============================================================================
1366// API Server Subcommands
1367// ============================================================================
1368
1369#[derive(Subcommand)]
1370pub enum ApiCommands {
1371    /// Start the API server
1372    Serve {
1373        /// Host to bind to (default: 0.0.0.0 for all interfaces)
1374        #[arg(long, default_value = "0.0.0.0")]
1375        host: String,
1376
1377        /// Port to listen on (default: 8787)
1378        #[arg(short, long, default_value = "8787")]
1379        port: u16,
1380
1381        /// Path to the database file
1382        #[arg(long)]
1383        database: Option<String>,
1384    },
1385}
1386
1387// ============================================================================
1388// Agency (Agent Development Kit) Subcommands
1389// ============================================================================
1390
1391#[derive(Subcommand)]
1392pub enum AgencyCommands {
1393    /// List available agents and their roles
1394    List {
1395        /// Show detailed information
1396        #[arg(short, long)]
1397        verbose: bool,
1398    },
1399
1400    /// Show agent information
1401    Info {
1402        /// Agent name or ID
1403        name: String,
1404    },
1405
1406    /// List supported orchestration modes
1407    Modes,
1408
1409    /// Run an agent with a prompt
1410    Run {
1411        /// Agent name to run
1412        #[arg(short, long, default_value = "assistant")]
1413        agent: String,
1414
1415        /// Prompt or task for the agent
1416        prompt: String,
1417
1418        /// Model to use (e.g., gemini-2.0-flash, gpt-4o)
1419        #[arg(short, long)]
1420        model: Option<String>,
1421
1422        /// Orchestration mode (single, sequential, parallel, swarm)
1423        #[arg(long, default_value = "single")]
1424        orchestration: String,
1425
1426        /// Enable verbose output
1427        #[arg(short, long)]
1428        verbose: bool,
1429    },
1430
1431    /// Create a new agent configuration
1432    Create {
1433        /// Agent name
1434        name: String,
1435
1436        /// Agent role (coordinator, researcher, coder, reviewer, executor, writer, tester, custom)
1437        #[arg(short, long, default_value = "custom")]
1438        role: String,
1439
1440        /// System instruction for the agent
1441        #[arg(short, long)]
1442        instruction: Option<String>,
1443
1444        /// Model to use
1445        #[arg(short, long)]
1446        model: Option<String>,
1447    },
1448
1449    /// List available tools
1450    Tools,
1451
1452    /// Show swarm templates
1453    Templates,
1454}
1455
1456// ============================================================================
1457// Telemetry Subcommands
1458// ============================================================================
1459
1460#[derive(Subcommand)]
1461pub enum TelemetryCommands {
1462    /// Show telemetry status and what data is collected
1463    #[command(visible_alias = "status")]
1464    Info,
1465
1466    /// Enable anonymous usage data collection (this is the default)
1467    #[command(visible_alias = "enable")]
1468    OptIn,
1469
1470    /// Disable anonymous usage data collection
1471    #[command(visible_alias = "disable")]
1472    OptOut,
1473
1474    /// Reset telemetry ID (generates new anonymous identifier)
1475    Reset,
1476
1477    /// Record structured data for later AI analysis
1478    #[command(visible_alias = "log")]
1479    Record {
1480        /// Event category (e.g., 'workflow', 'error', 'performance', 'usage')
1481        #[arg(short, long, default_value = "custom")]
1482        category: String,
1483
1484        /// Event name or type
1485        #[arg(short, long)]
1486        event: String,
1487
1488        /// JSON data payload (or use --kv for key=value pairs)
1489        #[arg(short, long)]
1490        data: Option<String>,
1491
1492        /// Key-value pairs (can be repeated: -k foo=bar -k baz=123)
1493        #[arg(short = 'k', long = "kv", value_parser = parse_key_value)]
1494        kv: Vec<(String, String)>,
1495
1496        /// Add tags for filtering (can be repeated: -t important -t session-123)
1497        #[arg(short, long)]
1498        tags: Vec<String>,
1499
1500        /// Optional session or context ID to associate with
1501        #[arg(long)]
1502        context: Option<String>,
1503
1504        /// Print recorded event details
1505        #[arg(short, long)]
1506        verbose: bool,
1507    },
1508
1509    /// Show recorded telemetry data
1510    #[command(visible_alias = "logs")]
1511    Show {
1512        /// Filter by category
1513        #[arg(short, long)]
1514        category: Option<String>,
1515
1516        /// Filter by event name
1517        #[arg(short, long)]
1518        event: Option<String>,
1519
1520        /// Filter by tag
1521        #[arg(short, long)]
1522        tag: Option<String>,
1523
1524        /// Maximum number of records to show
1525        #[arg(short = 'n', long, default_value = "20")]
1526        limit: usize,
1527
1528        /// Output format: table, json, jsonl
1529        #[arg(short, long, default_value = "table")]
1530        format: String,
1531
1532        /// Show records after this date (YYYY-MM-DD)
1533        #[arg(long)]
1534        after: Option<String>,
1535
1536        /// Show records before this date (YYYY-MM-DD)
1537        #[arg(long)]
1538        before: Option<String>,
1539    },
1540
1541    /// Export recorded data for AI analysis
1542    Export {
1543        /// Output file path
1544        output: String,
1545
1546        /// Export format: json, jsonl, csv
1547        #[arg(short, long, default_value = "jsonl")]
1548        format: String,
1549
1550        /// Filter by category
1551        #[arg(short, long)]
1552        category: Option<String>,
1553
1554        /// Include installation metadata in export
1555        #[arg(long)]
1556        with_metadata: bool,
1557    },
1558
1559    /// Clear recorded telemetry data
1560    Clear {
1561        /// Skip confirmation prompt
1562        #[arg(short, long)]
1563        force: bool,
1564
1565        /// Only clear records older than N days
1566        #[arg(long)]
1567        older_than: Option<u32>,
1568    },
1569
1570    /// Configure remote telemetry endpoint
1571    Config {
1572        /// Set the remote endpoint URL
1573        #[arg(long)]
1574        endpoint: Option<String>,
1575
1576        /// Set the API key for authentication
1577        #[arg(long)]
1578        api_key: Option<String>,
1579
1580        /// Enable remote telemetry sending
1581        #[arg(long)]
1582        enable_remote: bool,
1583
1584        /// Disable remote telemetry sending
1585        #[arg(long)]
1586        disable_remote: bool,
1587    },
1588
1589    /// Sync telemetry records to remote server
1590    Sync {
1591        /// Maximum number of records to sync
1592        #[arg(short = 'n', long)]
1593        limit: Option<usize>,
1594
1595        /// Clear local records after successful sync
1596        #[arg(long)]
1597        clear_after: bool,
1598    },
1599
1600    /// Test connection to remote telemetry server
1601    Test,
1602}
1603
1604/// Parse key=value pairs for telemetry record command
1605fn parse_key_value(s: &str) -> std::result::Result<(String, String), String> {
1606    let pos = s
1607        .find('=')
1608        .ok_or_else(|| format!("invalid key=value pair: no '=' found in '{s}'"))?;
1609    Ok((s[..pos].to_string(), s[pos + 1..].to_string()))
1610}