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