Skip to main content

chasm/
cli.rs

1// Copyright (c) 2024-2026 Nervosys LLC
2// SPDX-License-Identifier: AGPL-3.0-only
3//! CLI argument definitions using clap derive macros
4
5use clap::{Parser, Subcommand};
6
7/// CHAt System Manager (chasm) - Manage and merge chat sessions across workspaces
8#[derive(Parser)]
9#[command(name = "chasm")]
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 (Agent Launcher + TUI)
115    // ============================================================================
116    /// Launch an AI coding agent with auto-save, or run interactive tools
117    ///
118    /// Supported agents: claude, open, claw, cursor, codex, droid, gemini
119    Run {
120        #[command(subcommand)]
121        command: RunCommands,
122    },
123
124    // ============================================================================
125    // Watch Command (File-System Monitor)
126    // ============================================================================
127    /// Watch agent session directories for changes and auto-harvest
128    ///
129    /// Monitors session storage paths for new or modified files.
130    /// Default: watches all known agent directories.
131    #[command(visible_alias = "w")]
132    Watch {
133        /// Watch a specific agent's session directory (e.g., claude, gemini)
134        #[arg(short, long)]
135        agent: Option<String>,
136
137        /// Watch a custom path instead of agent directories
138        #[arg(short, long)]
139        path: Option<String>,
140
141        /// Debounce interval in seconds before harvesting (default: 3)
142        #[arg(short, long, default_value = "3")]
143        debounce: u64,
144
145        /// Detect changes without harvesting (dry-run)
146        #[arg(long)]
147        no_harvest: bool,
148
149        /// Show detailed file change events
150        #[arg(short, long)]
151        verbose: bool,
152    },
153
154    // ============================================================================
155    // Provider Commands
156    // ============================================================================
157    /// Manage LLM providers (Ollama, vLLM, Foundry, Cursor, etc.)
158    Provider {
159        #[command(subcommand)]
160        command: ProviderCommands,
161    },
162
163    // ============================================================================
164    // Detect Commands
165    // ============================================================================
166    /// Auto-detect workspace and provider information
167    Detect {
168        #[command(subcommand)]
169        command: Option<DetectCommands>,
170    },
171
172    // ============================================================================
173    // Register Commands
174    // ============================================================================
175    /// Add on-disk sessions to VS Code's database index (makes orphaned sessions visible)
176    Register {
177        #[command(subcommand)]
178        command: RegisterCommands,
179    },
180
181    // ============================================================================
182    // Sync Commands (shortcut to harvest sync)
183    // ============================================================================
184    /// Sync sessions between the harvest database and provider workspaces
185    Sync {
186        /// Path to the harvest database
187        #[arg(long)]
188        path: Option<String>,
189
190        /// Push sessions from database to provider workspaces (restore)
191        #[arg(long)]
192        push: bool,
193
194        /// Pull sessions from provider workspaces into database (backup)
195        #[arg(long)]
196        pull: bool,
197
198        /// Filter by provider name
199        #[arg(long)]
200        provider: Option<String>,
201
202        /// Filter by workspace/project path
203        #[arg(long)]
204        workspace: Option<String>,
205
206        /// Session IDs to sync (space-separated)
207        #[arg(long, num_args = 1..)]
208        sessions: Option<Vec<String>>,
209
210        /// Target format for push: auto (detect from provider), jsonl, json
211        #[arg(long, default_value = "auto")]
212        format: String,
213
214        /// Overwrite existing files without prompting
215        #[arg(long)]
216        force: bool,
217
218        /// Dry run - show what would be synced without making changes
219        #[arg(long)]
220        dry_run: bool,
221    },
222
223    // ============================================================================
224    // Harvest Commands
225    // ============================================================================
226    /// Harvest chat sessions from all providers into a unified database
227    Harvest {
228        #[command(subcommand)]
229        command: HarvestCommands,
230    },
231
232    // ============================================================================
233    // Recover Commands
234    // ============================================================================
235    /// Recover lost chat sessions from backups, recording state, or corrupted files
236    #[command(visible_alias = "restore")]
237    Recover {
238        #[command(subcommand)]
239        command: RecoverCommands,
240    },
241
242    // ============================================================================
243    // API Server Commands
244    // ============================================================================
245    /// Start the HTTP API server for the web frontend
246    #[command(visible_alias = "serve")]
247    Api {
248        #[command(subcommand)]
249        command: ApiCommands,
250    },
251
252    // ============================================================================
253    // Agency Commands
254    // ============================================================================
255    /// Agent Development Kit - manage agents and orchestration
256    Agency {
257        #[command(subcommand)]
258        command: AgencyCommands,
259    },
260
261    // ============================================================================
262    // Telemetry Commands
263    // ============================================================================
264    /// Manage anonymous usage data collection (opt-in by default)
265    Telemetry {
266        #[command(subcommand)]
267        command: Option<TelemetryCommands>,
268    },
269
270    // ============================================================================
271    // Completions Command
272    // ============================================================================
273    /// Generate shell completions for bash, zsh, fish, or PowerShell
274    Completions {
275        /// Shell to generate completions for
276        #[arg(value_enum)]
277        shell: CompletionShell,
278    },
279
280    // ============================================================================
281    // Shard Commands
282    // ============================================================================
283    /// Split oversized sessions into linked shards (by request count or file size)
284    Shard {
285        #[command(subcommand)]
286        command: ShardCommands,
287    },
288
289    // ============================================================================
290    // Doctor Command
291    // ============================================================================
292    /// Check system environment, providers, and configuration health
293    #[command(visible_alias = "check")]
294    Doctor {
295        /// Run all checks including network connectivity
296        #[arg(long)]
297        full: bool,
298
299        /// Output format: text, json
300        #[arg(long, default_value = "text")]
301        format: String,
302
303        /// Attempt to fix detected issues automatically
304        #[arg(long)]
305        fix: bool,
306    },
307
308    // ============================================================================
309    // Easter Egg
310    // ============================================================================
311    /// Show banner
312    #[command(hide = true)]
313    Banner,
314}
315
316// ============================================================================
317// List Subcommands
318// ============================================================================
319
320#[derive(Subcommand)]
321pub enum ListCommands {
322    /// List all VS Code workspaces
323    #[command(visible_alias = "ws")]
324    Workspaces,
325
326    /// List all chat sessions
327    #[command(visible_alias = "s")]
328    Sessions {
329        /// Filter by project path
330        #[arg(long)]
331        project_path: Option<String>,
332
333        /// Show file sizes
334        #[arg(long, short = 's')]
335        size: bool,
336
337        /// Filter by provider (vscode, cursor, claudecode, opencode, openclaw, antigravity)
338        #[arg(long, short = 'p')]
339        provider: Option<String>,
340
341        /// Include all providers
342        #[arg(long)]
343        all_providers: bool,
344    },
345
346    /// List available AI coding agents and their installation status
347    #[command(visible_alias = "a")]
348    Agents,
349
350    /// List agent mode sessions (Copilot Edits / chatEditingSessions)
351    #[command(visible_alias = "e")]
352    Edits {
353        /// Filter by project path
354        #[arg(long)]
355        project_path: Option<String>,
356
357        /// Show file sizes
358        #[arg(long, short = 's')]
359        size: bool,
360
361        /// Filter by provider (vscode, cursor, claudecode, opencode, openclaw, antigravity)
362        #[arg(long, short = 'p')]
363        provider: Option<String>,
364    },
365
366    /// List sessions for a specific project path
367    Path {
368        /// Project path (default: current directory)
369        project_path: Option<String>,
370    },
371
372    /// List unregistered sessions (exist on disk but invisible to VS Code)
373    Orphaned {
374        /// Project path (default: current directory)
375        #[arg(long)]
376        path: Option<String>,
377    },
378}
379
380// ============================================================================
381// Find Subcommands
382// ============================================================================
383
384#[derive(Subcommand)]
385pub enum FindCommands {
386    /// Search workspaces by name pattern (defaults to current directory name)
387    #[command(visible_alias = "ws")]
388    Workspace {
389        /// Text pattern to match (case-insensitive, defaults to current directory name)
390        pattern: Option<String>,
391    },
392
393    /// Search sessions by title, content, or ID pattern
394    #[command(visible_alias = "s")]
395    Session {
396        /// Text pattern to match (case-insensitive, defaults to current directory name)
397        pattern: Option<String>,
398
399        /// Filter by project path or workspace name
400        #[arg(long, short = 'w')]
401        workspace: Option<String>,
402
403        /// Only search in session titles (faster, skip content search)
404        #[arg(long, short = 't')]
405        title_only: bool,
406
407        /// Include message content in search (slower)
408        #[arg(long, short = 'c')]
409        content: bool,
410
411        /// Filter sessions modified after this date (YYYY-MM-DD)
412        #[arg(long)]
413        after: Option<String>,
414
415        /// Filter sessions modified before this date (YYYY-MM-DD)
416        #[arg(long)]
417        before: Option<String>,
418
419        /// Filter by internal message timestamp date (YYYY-MM-DD)
420        #[arg(long)]
421        date: Option<String>,
422
423        /// Search across all workspaces (not just current project)
424        #[arg(long, short = 'a')]
425        all: bool,
426
427        /// Filter by provider (vscode, cursor, claudecode, opencode, openclaw, antigravity)
428        #[arg(long, short = 'p')]
429        provider: Option<String>,
430
431        /// Search across all providers
432        #[arg(long)]
433        all_providers: bool,
434
435        /// Limit number of results
436        #[arg(long, short = 'n', default_value = "50")]
437        limit: usize,
438    },
439
440    /// Search sessions within a specific project path
441    Path {
442        /// Search pattern (case-insensitive, defaults to current directory name)
443        pattern: Option<String>,
444
445        /// Project path (default: current directory)
446        #[arg(long)]
447        project_path: Option<String>,
448    },
449}
450
451// ============================================================================
452// Show Subcommands
453// ============================================================================
454
455#[derive(Subcommand)]
456pub enum ShowCommands {
457    /// Show workspace details
458    #[command(visible_alias = "ws")]
459    Workspace {
460        /// Workspace name or hash
461        workspace: String,
462    },
463
464    /// Show session details
465    #[command(visible_alias = "s")]
466    Session {
467        /// Session ID or filename
468        session_id: String,
469
470        /// Project path to search in
471        #[arg(long)]
472        project_path: Option<String>,
473    },
474
475    /// Show agent mode session details (Copilot Edits)
476    #[command(visible_alias = "a")]
477    Agent {
478        /// Agent session ID
479        session_id: String,
480
481        /// Project path to search in
482        #[arg(long)]
483        project_path: Option<String>,
484    },
485
486    /// Show the VS Code session index (state.vscdb) for a workspace
487    #[command(visible_alias = "idx")]
488    Index {
489        /// Project path (default: current directory)
490        #[arg(long)]
491        path: Option<String>,
492
493        /// Show indexes for all workspaces that have chat sessions
494        #[arg(long, short)]
495        all: bool,
496    },
497
498    /// Show chat history timeline for a project path
499    Path {
500        /// Path to the project (default: current directory)
501        project_path: Option<String>,
502    },
503
504    /// Show timeline of session activity with gaps visualization
505    Timeline {
506        /// Path to the project (default: current directory)
507        project_path: Option<String>,
508
509        /// Include agent mode sessions
510        #[arg(long, short = 'a')]
511        agents: bool,
512
513        /// Filter by provider (vscode, cursor, claudecode, opencode, openclaw, antigravity)
514        #[arg(long, short = 'p')]
515        provider: Option<String>,
516
517        /// Include all providers (aggregate timeline)
518        #[arg(long)]
519        all_providers: bool,
520    },
521}
522
523// ============================================================================
524// Fetch Subcommands
525// ============================================================================
526
527#[derive(Subcommand)]
528pub enum FetchCommands {
529    /// Fetch sessions from workspaces matching a pattern
530    #[command(visible_alias = "ws")]
531    Workspace {
532        /// Workspace name pattern (case-insensitive)
533        workspace_name: String,
534
535        /// Target project path (default: current directory)
536        #[arg(long)]
537        target_path: Option<String>,
538
539        /// Overwrite existing sessions
540        #[arg(long)]
541        force: bool,
542
543        /// Don't register sessions in VS Code index
544        #[arg(long)]
545        no_register: bool,
546    },
547
548    /// Fetch specific sessions by ID
549    #[command(visible_alias = "s")]
550    Session {
551        /// Session IDs to fetch (space-separated)
552        #[arg(required = true, num_args = 1..)]
553        session_ids: Vec<String>,
554
555        /// Target project path (default: current directory)
556        #[arg(long)]
557        target_path: Option<String>,
558
559        /// Overwrite existing sessions
560        #[arg(long)]
561        force: bool,
562
563        /// Don't register sessions in VS Code index
564        #[arg(long)]
565        no_register: bool,
566    },
567
568    /// Fetch chat sessions from other workspaces by project path
569    Path {
570        /// Path to the project (default: current directory)
571        project_path: Option<String>,
572
573        /// Overwrite existing sessions and skip VS Code running check
574        #[arg(long)]
575        force: bool,
576
577        /// Don't register sessions in VS Code index
578        #[arg(long)]
579        no_register: bool,
580    },
581}
582
583// ============================================================================
584// Merge Subcommands
585// ============================================================================
586
587#[derive(Subcommand)]
588pub enum MergeCommands {
589    /// Merge sessions from workspaces matching a name pattern
590    #[command(visible_alias = "ws")]
591    Workspace {
592        /// Workspace name pattern to search for (case-insensitive)
593        workspace_name: String,
594
595        /// Title for the merged session
596        #[arg(short, long)]
597        title: Option<String>,
598
599        /// Target project path to save the merged session (default: current directory)
600        #[arg(long)]
601        target_path: Option<String>,
602
603        /// Skip VS Code running check
604        #[arg(long)]
605        force: bool,
606
607        /// Don't create backup of current sessions
608        #[arg(long)]
609        no_backup: bool,
610    },
611
612    /// Merge sessions from multiple workspace name patterns
613    #[command(visible_alias = "wss")]
614    Workspaces {
615        /// Workspace name patterns to search for (space-separated, case-insensitive)
616        #[arg(required = true, num_args = 1..)]
617        workspace_names: Vec<String>,
618
619        /// Title for the merged session
620        #[arg(short, long)]
621        title: Option<String>,
622
623        /// Target project path to save the merged session (default: current directory)
624        #[arg(long)]
625        target_path: Option<String>,
626
627        /// Skip VS Code running check
628        #[arg(long)]
629        force: bool,
630
631        /// Don't create backup of current sessions
632        #[arg(long)]
633        no_backup: bool,
634    },
635
636    /// Merge specific sessions by their IDs or filenames
637    #[command(visible_alias = "s")]
638    Sessions {
639        /// Session IDs or filenames (comma-separated or space-separated)
640        #[arg(required = true, num_args = 1..)]
641        sessions: Vec<String>,
642
643        /// Title for the merged session
644        #[arg(short, long)]
645        title: Option<String>,
646
647        /// Target project path to save the merged session (default: current directory)
648        #[arg(long)]
649        target_path: Option<String>,
650
651        /// Skip VS Code running check
652        #[arg(long)]
653        force: bool,
654
655        /// Don't create backup of current sessions
656        #[arg(long)]
657        no_backup: bool,
658    },
659
660    /// Merge all sessions for a project path into one unified chat
661    Path {
662        /// Path to the project (default: current directory)
663        project_path: Option<String>,
664
665        /// Title for the merged session
666        #[arg(short, long)]
667        title: Option<String>,
668
669        /// Skip VS Code running check
670        #[arg(long)]
671        force: bool,
672
673        /// Don't create backup of current sessions
674        #[arg(long)]
675        no_backup: bool,
676    },
677
678    /// Merge sessions from an LLM provider (Ollama, Cursor, etc.)
679    Provider {
680        /// Provider name (copilot, cursor, ollama, vllm, foundry, etc.)
681        provider_name: String,
682
683        /// Title for the merged session
684        #[arg(short, long)]
685        title: Option<String>,
686
687        /// Target project path to save the merged session (default: current directory)
688        #[arg(long)]
689        target_path: Option<String>,
690
691        /// Session IDs from the provider to include (omit for all)
692        #[arg(long)]
693        sessions: Option<Vec<String>>,
694
695        /// Skip VS Code running check
696        #[arg(long)]
697        force: bool,
698
699        /// Don't create backup of current sessions
700        #[arg(long)]
701        no_backup: bool,
702    },
703
704    /// Merge sessions from multiple providers
705    #[command(name = "providers")]
706    Providers {
707        /// Provider names (space-separated: copilot cursor ollama)
708        #[arg(required = true, num_args = 1..)]
709        providers: Vec<String>,
710
711        /// Title for the merged session
712        #[arg(short, long)]
713        title: Option<String>,
714
715        /// Target project path to save the merged session (default: current directory)
716        #[arg(long)]
717        target_path: Option<String>,
718
719        /// Filter by workspace name pattern (applies to providers that support workspaces)
720        #[arg(long)]
721        workspace: Option<String>,
722
723        /// Skip VS Code running check
724        #[arg(long)]
725        force: bool,
726
727        /// Don't create backup of current sessions
728        #[arg(long)]
729        no_backup: bool,
730    },
731
732    /// Merge all sessions across all available providers
733    All {
734        /// Title for the merged session
735        #[arg(short, long)]
736        title: Option<String>,
737
738        /// Target project path to save the merged session (default: current directory)
739        #[arg(long)]
740        target_path: Option<String>,
741
742        /// Filter by workspace name pattern (applies to providers that support workspaces)
743        #[arg(long)]
744        workspace: Option<String>,
745
746        /// Skip VS Code running check
747        #[arg(long)]
748        force: bool,
749
750        /// Don't create backup of current sessions
751        #[arg(long)]
752        no_backup: bool,
753    },
754}
755
756// ============================================================================
757// Export Subcommands
758// ============================================================================
759
760#[derive(Subcommand)]
761pub enum ExportCommands {
762    /// Export sessions from a workspace by hash
763    #[command(visible_alias = "ws")]
764    Workspace {
765        /// Destination directory for exported sessions
766        destination: String,
767
768        /// Source workspace hash
769        hash: String,
770    },
771
772    /// Export specific sessions by ID
773    #[command(visible_alias = "s")]
774    Sessions {
775        /// Destination directory for exported sessions
776        destination: String,
777
778        /// Session IDs to export (space-separated)
779        #[arg(required = true, num_args = 1..)]
780        session_ids: Vec<String>,
781
782        /// Source project path
783        #[arg(long)]
784        project_path: Option<String>,
785    },
786
787    /// Export chat sessions from a project path
788    Path {
789        /// Destination directory for exported sessions
790        destination: String,
791
792        /// Source project path (default: current directory)
793        project_path: Option<String>,
794    },
795
796    /// Export chat sessions from multiple project paths (batch operation)
797    Batch {
798        /// Base destination directory (subdirectories created per project)
799        destination: String,
800
801        /// Project paths to export (space-separated)
802        #[arg(required = true, num_args = 1..)]
803        project_paths: Vec<String>,
804    },
805}
806
807// ============================================================================
808// Import Subcommands
809// ============================================================================
810
811#[derive(Subcommand)]
812pub enum ImportCommands {
813    /// Copy session files from external directory into a workspace
814    #[command(visible_alias = "ws")]
815    Workspace {
816        /// Source directory containing session JSON files to import
817        source: String,
818
819        /// Target workspace hash
820        hash: String,
821
822        /// Overwrite existing sessions
823        #[arg(long)]
824        force: bool,
825    },
826
827    /// Copy specific session files into a workspace
828    #[command(visible_alias = "s")]
829    Sessions {
830        /// Session files to import (space-separated paths)
831        #[arg(required = true, num_args = 1..)]
832        session_files: Vec<String>,
833
834        /// Target project path (default: current directory)
835        #[arg(long)]
836        target_path: Option<String>,
837
838        /// Overwrite existing sessions
839        #[arg(long)]
840        force: bool,
841    },
842
843    /// Copy session files from external directory into a project workspace
844    Path {
845        /// Source directory containing session JSON files to import
846        source: String,
847
848        /// Target project path (default: current directory)
849        target_path: Option<String>,
850
851        /// Overwrite existing sessions
852        #[arg(long)]
853        force: bool,
854    },
855}
856
857// ============================================================================
858// Move Subcommands
859// ============================================================================
860
861#[derive(Subcommand)]
862pub enum MoveCommands {
863    /// Move all sessions from one workspace to another
864    #[command(visible_alias = "ws")]
865    Workspace {
866        /// Source workspace hash
867        source_hash: String,
868
869        /// Target workspace hash or project path
870        target: String,
871    },
872
873    /// Move specific sessions by ID
874    #[command(visible_alias = "s")]
875    Sessions {
876        /// Session IDs to move (space-separated)
877        #[arg(required = true, num_args = 1..)]
878        session_ids: Vec<String>,
879
880        /// Target project path
881        target_path: String,
882    },
883
884    /// Move sessions from a source path to target path
885    Path {
886        /// Source project path
887        source_path: String,
888
889        /// Target project path
890        target_path: String,
891    },
892}
893
894// ============================================================================
895// Git Subcommands
896// ============================================================================
897
898#[derive(Subcommand)]
899pub enum GitCommands {
900    /// Configure git settings for chat sessions
901    Config {
902        /// Git user name
903        #[arg(long)]
904        name: Option<String>,
905
906        /// Git user email
907        #[arg(long)]
908        email: Option<String>,
909
910        /// Project path
911        #[arg(long)]
912        path: Option<String>,
913    },
914
915    /// Initialize git versioning for chat sessions
916    Init {
917        /// Project path
918        path: String,
919    },
920
921    /// Add chat sessions to git (stage and optionally commit)
922    Add {
923        /// Project path
924        path: String,
925
926        /// Also commit the changes
927        #[arg(long)]
928        commit: bool,
929
930        /// Commit message (requires --commit)
931        #[arg(short, long)]
932        message: Option<String>,
933    },
934
935    /// Show git status of chat sessions
936    Status {
937        /// Project path
938        path: String,
939    },
940
941    /// Create a git tag snapshot of chat sessions
942    Snapshot {
943        /// Project path
944        path: String,
945
946        /// Tag name (auto-generated if not provided)
947        #[arg(long)]
948        tag: Option<String>,
949
950        /// Snapshot message
951        #[arg(short, long)]
952        message: Option<String>,
953    },
954
955    /// Track chat sessions together with associated file changes
956    Track {
957        /// Project path
958        path: String,
959
960        /// Commit message describing the changes
961        #[arg(short, long)]
962        message: Option<String>,
963
964        /// Include all staged and unstaged file changes
965        #[arg(long)]
966        all: bool,
967
968        /// Include specific files in addition to chat sessions
969        #[arg(long)]
970        files: Option<Vec<String>>,
971
972        /// Create a tag for this tracked state
973        #[arg(long)]
974        tag: Option<String>,
975    },
976
977    /// Show history of chat session commits with associated file changes
978    Log {
979        /// Project path
980        path: String,
981
982        /// Number of commits to show
983        #[arg(short = 'n', long, default_value = "10")]
984        count: usize,
985
986        /// Show only commits that include chat session changes
987        #[arg(long)]
988        sessions_only: bool,
989    },
990
991    /// Diff chat sessions between commits or current state
992    Diff {
993        /// Project path
994        path: String,
995
996        /// First commit (default: HEAD)
997        #[arg(long)]
998        from: Option<String>,
999
1000        /// Second commit (default: working directory)
1001        #[arg(long)]
1002        to: Option<String>,
1003
1004        /// Show associated file changes alongside chat diffs
1005        #[arg(long)]
1006        with_files: bool,
1007    },
1008
1009    /// Restore chat sessions from a specific commit
1010    Restore {
1011        /// Project path
1012        path: String,
1013
1014        /// Commit hash, tag, or reference to restore from
1015        commit: String,
1016
1017        /// Also restore associated files from the same commit
1018        #[arg(long)]
1019        with_files: bool,
1020
1021        /// Create a backup before restoring
1022        #[arg(long)]
1023        backup: bool,
1024    },
1025}
1026
1027// ============================================================================
1028// Migration Subcommands
1029// ============================================================================
1030
1031#[derive(Subcommand)]
1032pub enum MigrationCommands {
1033    /// Create a migration package for moving to a new machine
1034    Create {
1035        /// Output directory for migration package
1036        output: String,
1037
1038        /// Comma-separated list of project paths to include
1039        #[arg(long)]
1040        projects: Option<String>,
1041
1042        /// Include all workspaces with chat sessions
1043        #[arg(long)]
1044        all: bool,
1045    },
1046
1047    /// Restore a migration package on a new machine
1048    Restore {
1049        /// Path to migration package directory
1050        package: String,
1051
1052        /// Project path mapping: 'old1:new1;old2:new2'
1053        #[arg(long)]
1054        mapping: Option<String>,
1055
1056        /// Show what would be done without doing it
1057        #[arg(long)]
1058        dry_run: bool,
1059    },
1060}
1061
1062// ============================================================================
1063// Run Subcommands
1064// ============================================================================
1065
1066#[derive(Subcommand)]
1067pub enum RunCommands {
1068    /// Launch interactive TUI (Text User Interface)
1069    Tui,
1070
1071    /// Launch Claude Code with auto-save
1072    #[command(visible_aliases = ["claude-code", "claudecode"])]
1073    Claude {
1074        /// Extra arguments to pass to the agent
1075        #[arg(last = true)]
1076        args: Vec<String>,
1077        /// Disable auto-save
1078        #[arg(long)]
1079        no_save: bool,
1080        /// Verbose output
1081        #[arg(short, long)]
1082        verbose: bool,
1083    },
1084
1085    /// Launch OpenCode with auto-save
1086    #[command(visible_aliases = ["opencode", "open-code"])]
1087    Open {
1088        #[arg(last = true)]
1089        args: Vec<String>,
1090        #[arg(long)]
1091        no_save: bool,
1092        #[arg(short, long)]
1093        verbose: bool,
1094    },
1095
1096    /// Launch OpenClaw (ClawdBot) with auto-save
1097    #[command(visible_aliases = ["openclaw", "clawdbot"])]
1098    Claw {
1099        #[arg(last = true)]
1100        args: Vec<String>,
1101        #[arg(long)]
1102        no_save: bool,
1103        #[arg(short, long)]
1104        verbose: bool,
1105    },
1106
1107    /// Launch Cursor CLI with auto-save
1108    Cursor {
1109        #[arg(last = true)]
1110        args: Vec<String>,
1111        #[arg(long)]
1112        no_save: bool,
1113        #[arg(short, long)]
1114        verbose: bool,
1115    },
1116
1117    /// Launch Codex CLI (OpenAI) with auto-save
1118    #[command(visible_aliases = ["codex-cli", "codexcli"])]
1119    Codex {
1120        #[arg(last = true)]
1121        args: Vec<String>,
1122        #[arg(long)]
1123        no_save: bool,
1124        #[arg(short, long)]
1125        verbose: bool,
1126    },
1127
1128    /// Launch Droid CLI (Factory) with auto-save
1129    #[command(visible_aliases = ["droid-cli", "droidcli", "factory"])]
1130    Droid {
1131        #[arg(last = true)]
1132        args: Vec<String>,
1133        #[arg(long)]
1134        no_save: bool,
1135        #[arg(short, long)]
1136        verbose: bool,
1137    },
1138
1139    /// Launch Gemini CLI (Google) with auto-save
1140    #[command(visible_aliases = ["gemini-cli", "geminicli"])]
1141    Gemini {
1142        #[arg(last = true)]
1143        args: Vec<String>,
1144        #[arg(long)]
1145        no_save: bool,
1146        #[arg(short, long)]
1147        verbose: bool,
1148    },
1149}
1150
1151// ============================================================================
1152// Provider Subcommands
1153// ============================================================================
1154
1155#[derive(Subcommand)]
1156pub enum ProviderCommands {
1157    /// List all discovered LLM providers
1158    List,
1159
1160    /// Show detailed info about a specific provider
1161    Info {
1162        /// Provider name (copilot, cursor, ollama, vllm, foundry, lm-studio, etc.)
1163        provider: String,
1164    },
1165
1166    /// Configure a provider
1167    Config {
1168        /// Provider name
1169        provider: String,
1170
1171        /// API endpoint URL
1172        #[arg(long)]
1173        endpoint: Option<String>,
1174
1175        /// API key
1176        #[arg(long)]
1177        api_key: Option<String>,
1178
1179        /// Default model
1180        #[arg(long)]
1181        model: Option<String>,
1182
1183        /// Enable or disable the provider
1184        #[arg(long)]
1185        enabled: Option<bool>,
1186    },
1187
1188    /// Import sessions from another provider
1189    Import {
1190        /// Source provider name
1191        #[arg(long)]
1192        from: String,
1193
1194        /// Target project path (or current directory)
1195        #[arg(long)]
1196        path: Option<String>,
1197
1198        /// Session ID to import (omit for all)
1199        #[arg(long)]
1200        session: Option<String>,
1201    },
1202
1203    /// Test connection to a provider
1204    Test {
1205        /// Provider name
1206        provider: String,
1207    },
1208}
1209
1210// ============================================================================
1211// Detect Subcommands
1212// ============================================================================
1213
1214#[derive(Subcommand)]
1215pub enum DetectCommands {
1216    /// Detect workspace for a path
1217    Workspace {
1218        /// Project path (default: current directory)
1219        path: Option<String>,
1220    },
1221
1222    /// Detect available providers
1223    Providers {
1224        /// Only show providers with sessions
1225        #[arg(long)]
1226        with_sessions: bool,
1227    },
1228
1229    /// Detect which provider a session belongs to
1230    Session {
1231        /// Session ID or filename
1232        session_id: String,
1233
1234        /// Project path to search in
1235        #[arg(long)]
1236        path: Option<String>,
1237    },
1238
1239    /// Detect everything (workspace, providers, sessions) for a path
1240    All {
1241        /// Project path (default: current directory)
1242        path: Option<String>,
1243
1244        /// Show detailed information
1245        #[arg(long)]
1246        verbose: bool,
1247    },
1248
1249    /// Find all workspace hashes for a project path (including orphaned workspaces with sessions)
1250    Orphaned {
1251        /// Project path (default: current directory)
1252        path: Option<String>,
1253
1254        /// Automatically recover orphaned sessions by copying to active workspace
1255        #[arg(long, short)]
1256        recover: bool,
1257    },
1258}
1259
1260// ============================================================================
1261// Register Subcommands
1262// ============================================================================
1263
1264#[derive(Subcommand)]
1265pub enum RegisterCommands {
1266    /// Register all on-disk sessions into VS Code's index (fixes orphaned sessions)
1267    All {
1268        /// Project path (default: current directory)
1269        #[arg(long)]
1270        path: Option<String>,
1271
1272        /// Merge all sessions into one before registering
1273        #[arg(long, short)]
1274        merge: bool,
1275
1276        /// Force registration even if VS Code is running
1277        #[arg(long, short)]
1278        force: bool,
1279
1280        /// Close VS Code before registering (ensures index is not overwritten by cache)
1281        #[arg(long)]
1282        close_vscode: bool,
1283
1284        /// Reopen VS Code after registering (implies --close-vscode)
1285        #[arg(long)]
1286        reopen: bool,
1287    },
1288
1289    /// Register specific sessions by ID or title into VS Code's index
1290    #[command(visible_alias = "s")]
1291    Session {
1292        /// Session IDs or filenames (without .json extension)
1293        #[arg(required_unless_present = "title")]
1294        ids: Vec<String>,
1295
1296        /// Match sessions by title instead of ID
1297        #[arg(long, short, num_args = 1.., value_delimiter = ' ')]
1298        title: Option<Vec<String>>,
1299
1300        /// Project path (default: current directory)
1301        #[arg(long)]
1302        path: Option<String>,
1303
1304        /// Force registration even if VS Code is running
1305        #[arg(long, short)]
1306        force: bool,
1307    },
1308
1309    /// Recursively walk directories to find and register orphaned sessions for all workspaces
1310    #[command(visible_alias = "r")]
1311    Recursive {
1312        /// Root path to start recursive search (default: current directory)
1313        path: Option<String>,
1314
1315        /// Maximum directory depth to recurse (default: unlimited)
1316        #[arg(long, short)]
1317        depth: Option<usize>,
1318
1319        /// Force registration even if VS Code is running
1320        #[arg(long, short)]
1321        force: bool,
1322
1323        /// Only show what would be registered without making changes
1324        #[arg(long)]
1325        dry_run: bool,
1326
1327        /// Skip directories matching these patterns (can be used multiple times)
1328        #[arg(long, short = 'x')]
1329        exclude: Vec<String>,
1330    },
1331
1332    /// Repair sessions: compact large JSONL files and rebuild the index with correct metadata
1333    #[command(visible_alias = "fix")]
1334    Repair {
1335        /// Project path (default: current directory)
1336        #[arg(long)]
1337        path: Option<String>,
1338
1339        /// Repair all workspaces that have chat sessions
1340        #[arg(long, short)]
1341        all: bool,
1342
1343        /// Recursively scan a directory tree for workspaces and repair all discovered sessions
1344        #[arg(long, short)]
1345        recursive: bool,
1346
1347        /// Maximum directory depth when using --recursive (default: unlimited)
1348        #[arg(long, short)]
1349        depth: Option<usize>,
1350
1351        /// Skip directories matching these patterns when using --recursive
1352        #[arg(long, short = 'x')]
1353        exclude: Vec<String>,
1354
1355        /// Only show what would be repaired without making changes
1356        #[arg(long)]
1357        dry_run: bool,
1358
1359        /// Force even if VS Code is running
1360        #[arg(long, short)]
1361        force: bool,
1362
1363        /// Close VS Code before repairing (ensures index is not overwritten)
1364        #[arg(long)]
1365        close_vscode: bool,
1366
1367        /// Reopen VS Code after repairing (implies --close-vscode)
1368        #[arg(long)]
1369        reopen: bool,
1370    },
1371
1372    /// Trim oversized sessions by keeping only the most recent requests
1373    #[command(visible_alias = "t")]
1374    Trim {
1375        /// Project path (default: current directory)
1376        #[arg(long)]
1377        path: Option<String>,
1378
1379        /// Number of recent requests to keep (default: 20)
1380        #[arg(long, short, default_value = "20")]
1381        keep: usize,
1382
1383        /// Session ID to trim (default: largest session)
1384        #[arg(long, short)]
1385        session: Option<String>,
1386
1387        /// Trim all sessions over the size threshold
1388        #[arg(long, short)]
1389        all: bool,
1390
1391        /// Only trim sessions larger than this size in MB (default: 10)
1392        #[arg(long, default_value = "10")]
1393        threshold_mb: u64,
1394
1395        /// Force even if VS Code is running
1396        #[arg(long, short)]
1397        force: bool,
1398    },
1399}
1400
1401// ============================================================================
1402// Harvest Subcommands
1403// ============================================================================
1404
1405#[derive(Subcommand)]
1406pub enum HarvestCommands {
1407    /// Initialize a harvest database
1408    Init {
1409        /// Path to the database file (default: ./chat_sessions.db)
1410        #[arg(long)]
1411        path: Option<String>,
1412
1413        /// Initialize git tracking for the database
1414        #[arg(long)]
1415        git: bool,
1416    },
1417
1418    /// Scan for available providers and sessions
1419    Scan {
1420        /// Show individual sessions
1421        #[arg(long)]
1422        sessions: bool,
1423
1424        /// Scan for web-based LLM providers (ChatGPT, Claude, etc.)
1425        #[arg(long)]
1426        web: bool,
1427
1428        /// Timeout in seconds for web provider checks (default: 5)
1429        #[arg(long, default_value = "5")]
1430        timeout: u64,
1431
1432        /// Show verbose debug output for browser scanning
1433        #[arg(long, short)]
1434        verbose: bool,
1435    },
1436
1437    /// Run the harvest to collect sessions from all providers
1438    Run {
1439        /// Path to the harvest database
1440        #[arg(long)]
1441        path: Option<String>,
1442
1443        /// Only include specific providers (comma-separated: copilot,cursor,ollama)
1444        #[arg(long, value_delimiter = ',')]
1445        providers: Option<Vec<String>>,
1446
1447        /// Exclude specific providers (comma-separated)
1448        #[arg(long, value_delimiter = ',')]
1449        exclude: Option<Vec<String>>,
1450
1451        /// Only harvest sessions changed since last run
1452        #[arg(long)]
1453        incremental: bool,
1454
1455        /// Auto-commit changes to git after harvest
1456        #[arg(long)]
1457        commit: bool,
1458
1459        /// Commit message (requires --commit)
1460        #[arg(short, long)]
1461        message: Option<String>,
1462    },
1463
1464    /// Show harvest database status
1465    Status {
1466        /// Path to the harvest database
1467        #[arg(long)]
1468        path: Option<String>,
1469    },
1470
1471    /// List sessions in the harvest database
1472    List {
1473        /// Path to the harvest database
1474        #[arg(long)]
1475        path: Option<String>,
1476
1477        /// Filter by provider name
1478        #[arg(long)]
1479        provider: Option<String>,
1480
1481        /// Maximum number of sessions to show
1482        #[arg(long, default_value = "20")]
1483        limit: usize,
1484
1485        /// Search sessions by title or ID
1486        #[arg(long)]
1487        search: Option<String>,
1488    },
1489
1490    /// Export sessions from the harvest database
1491    Export {
1492        /// Output file path
1493        output: String,
1494
1495        /// Path to the harvest database
1496        #[arg(long)]
1497        path: Option<String>,
1498
1499        /// Export format: json, jsonl, md (markdown)
1500        #[arg(long, default_value = "json")]
1501        format: String,
1502
1503        /// Filter by provider name
1504        #[arg(long)]
1505        provider: Option<String>,
1506
1507        /// Export specific sessions by ID (comma-separated)
1508        #[arg(long, value_delimiter = ',')]
1509        sessions: Option<Vec<String>>,
1510    },
1511
1512    /// Import a shared chat session from a URL
1513    Share {
1514        /// Share link URL (ChatGPT, Claude, etc.)
1515        url: String,
1516
1517        /// Path to the harvest database
1518        #[arg(long)]
1519        path: Option<String>,
1520
1521        /// Custom name for the imported session
1522        #[arg(long)]
1523        name: Option<String>,
1524
1525        /// Associate with a workspace path
1526        #[arg(long)]
1527        workspace: Option<String>,
1528    },
1529
1530    /// List pending or imported share links
1531    Shares {
1532        /// Path to the harvest database
1533        #[arg(long)]
1534        path: Option<String>,
1535
1536        /// Filter by status: pending, imported, failed, expired
1537        #[arg(long)]
1538        status: Option<String>,
1539
1540        /// Maximum number of links to show
1541        #[arg(long, default_value = "20")]
1542        limit: usize,
1543    },
1544
1545    /// Create a checkpoint (version snapshot) of a session
1546    Checkpoint {
1547        /// Session ID to checkpoint
1548        session: String,
1549
1550        /// Path to the harvest database
1551        #[arg(long)]
1552        path: Option<String>,
1553
1554        /// Checkpoint description message
1555        #[arg(short, long)]
1556        message: Option<String>,
1557    },
1558
1559    /// List checkpoints for a session
1560    Checkpoints {
1561        /// Session ID to list checkpoints for
1562        session: String,
1563
1564        /// Path to the harvest database
1565        #[arg(long)]
1566        path: Option<String>,
1567    },
1568
1569    /// Revert a session to a previous checkpoint
1570    Revert {
1571        /// Session ID to revert
1572        session: String,
1573
1574        /// Checkpoint number to revert to
1575        checkpoint: i64,
1576
1577        /// Path to the harvest database
1578        #[arg(long)]
1579        path: Option<String>,
1580    },
1581
1582    /// Sync sessions between the harvest database and provider workspaces
1583    Sync {
1584        /// Path to the harvest database
1585        #[arg(long)]
1586        path: Option<String>,
1587
1588        /// Push sessions from database to provider workspaces (restore)
1589        #[arg(long)]
1590        push: bool,
1591
1592        /// Pull sessions from provider workspaces into database (backup)
1593        #[arg(long)]
1594        pull: bool,
1595
1596        /// Filter by provider name
1597        #[arg(long)]
1598        provider: Option<String>,
1599
1600        /// Filter by workspace/project path
1601        #[arg(long)]
1602        workspace: Option<String>,
1603
1604        /// Session IDs to sync (space-separated)
1605        #[arg(long, num_args = 1..)]
1606        sessions: Option<Vec<String>>,
1607
1608        /// Target format for push: auto (detect from provider), jsonl, json
1609        #[arg(long, default_value = "auto")]
1610        format: String,
1611
1612        /// Overwrite existing files without prompting
1613        #[arg(long)]
1614        force: bool,
1615
1616        /// Dry run - show what would be synced without making changes
1617        #[arg(long)]
1618        dry_run: bool,
1619    },
1620
1621    /// Compact the database by stripping session_json blobs (data preserved in messages_v2)
1622    #[command(visible_alias = "gc")]
1623    Compact {
1624        /// Path to the harvest database
1625        #[arg(long)]
1626        path: Option<String>,
1627
1628        /// Show what would be compacted without making changes
1629        #[arg(long)]
1630        dry_run: bool,
1631    },
1632
1633    /// Rebuild the full-text search index
1634    Rebuild {
1635        /// Path to the harvest database
1636        #[arg(long)]
1637        path: Option<String>,
1638    },
1639
1640    /// Search messages across all sessions (full-text search)
1641    Search {
1642        /// Search query
1643        query: String,
1644
1645        /// Path to the harvest database
1646        #[arg(long)]
1647        path: Option<String>,
1648
1649        /// Filter by provider
1650        #[arg(long)]
1651        provider: Option<String>,
1652
1653        /// Maximum results to show
1654        #[arg(long, default_value = "20")]
1655        limit: usize,
1656    },
1657
1658    /// Git operations for the harvest database
1659    Git {
1660        #[command(subcommand)]
1661        command: HarvestGitCommands,
1662    },
1663}
1664
1665#[derive(Subcommand)]
1666pub enum HarvestGitCommands {
1667    /// Initialize git tracking for the harvest database
1668    Init {
1669        /// Path to the harvest database
1670        #[arg(long)]
1671        path: Option<String>,
1672    },
1673
1674    /// Commit changes to the harvest database
1675    Commit {
1676        /// Path to the harvest database
1677        #[arg(long)]
1678        path: Option<String>,
1679
1680        /// Commit message
1681        #[arg(short, long)]
1682        message: Option<String>,
1683    },
1684
1685    /// Show git log for the harvest database
1686    Log {
1687        /// Path to the harvest database
1688        #[arg(long)]
1689        path: Option<String>,
1690
1691        /// Number of commits to show
1692        #[arg(short = 'n', long, default_value = "10")]
1693        count: usize,
1694    },
1695
1696    /// Show changes to the harvest database
1697    Diff {
1698        /// Path to the harvest database
1699        #[arg(long)]
1700        path: Option<String>,
1701
1702        /// Compare against specific commit
1703        #[arg(long)]
1704        commit: Option<String>,
1705    },
1706
1707    /// Restore harvest database from a commit
1708    Restore {
1709        /// Commit hash to restore from
1710        commit: String,
1711
1712        /// Path to the harvest database
1713        #[arg(long)]
1714        path: Option<String>,
1715    },
1716}
1717
1718// ============================================================================
1719// Recover Subcommands
1720// ============================================================================
1721
1722#[derive(Subcommand)]
1723pub enum RecoverCommands {
1724    /// Scan for recoverable sessions from various sources
1725    Scan {
1726        /// Provider to scan: vscode, cursor, all (default: all)
1727        #[arg(long, default_value = "all")]
1728        provider: String,
1729
1730        /// Show detailed information about each session
1731        #[arg(short, long)]
1732        verbose: bool,
1733
1734        /// Include sessions older than normal retention period
1735        #[arg(long)]
1736        include_old: bool,
1737    },
1738
1739    /// Recover sessions from the recording API server
1740    Recording {
1741        /// Server URL (default: http://localhost:8787)
1742        #[arg(long, default_value = "http://localhost:8787")]
1743        server: String,
1744
1745        /// Only recover specific session ID
1746        #[arg(long)]
1747        session: Option<String>,
1748
1749        /// Output directory for recovered sessions
1750        #[arg(short, long)]
1751        output: Option<String>,
1752    },
1753
1754    /// Recover sessions from SQLite database backups
1755    Database {
1756        /// Path to the database backup file
1757        backup: String,
1758
1759        /// Extract specific session by ID
1760        #[arg(long)]
1761        session: Option<String>,
1762
1763        /// Output directory for recovered sessions
1764        #[arg(short, long)]
1765        output: Option<String>,
1766
1767        /// Output format: json, jsonl, md (default: json)
1768        #[arg(long, default_value = "json")]
1769        format: String,
1770    },
1771
1772    /// Recover sessions from incomplete/corrupted JSONL files
1773    Jsonl {
1774        /// Path to the JSONL file to repair
1775        file: String,
1776
1777        /// Output file for recovered sessions (default: same name with .recovered suffix)
1778        #[arg(short, long)]
1779        output: Option<String>,
1780
1781        /// Attempt aggressive recovery (may produce incomplete sessions)
1782        #[arg(long)]
1783        aggressive: bool,
1784    },
1785
1786    /// List sessions from VS Code's workspaceStorage that may be orphaned
1787    Orphans {
1788        /// Provider to check: vscode, cursor, all (default: all)
1789        #[arg(long, default_value = "all")]
1790        provider: String,
1791
1792        /// Show sessions not in the SQLite state database
1793        #[arg(long)]
1794        unindexed: bool,
1795
1796        /// Check if files actually exist on disk
1797        #[arg(long)]
1798        verify: bool,
1799    },
1800
1801    /// Repair corrupted session files in place
1802    Repair {
1803        /// Path to the session directory or file
1804        path: String,
1805
1806        /// Create backup before repair
1807        #[arg(long, default_value = "true")]
1808        backup: bool,
1809
1810        /// Dry run - show what would be repaired without making changes
1811        #[arg(long)]
1812        dry_run: bool,
1813    },
1814
1815    /// Show recovery status and recommendations
1816    Status {
1817        /// Provider to check: vscode, cursor, all (default: all)
1818        #[arg(long, default_value = "all")]
1819        provider: String,
1820
1821        /// Check disk space and file system health
1822        #[arg(long)]
1823        system: bool,
1824    },
1825
1826    /// Convert session files between JSON and JSONL formats
1827    Convert {
1828        /// Input file to convert (.json or .jsonl)
1829        input: String,
1830
1831        /// Output file (auto-detects format from extension, or uses --format)
1832        #[arg(short, long)]
1833        output: Option<String>,
1834
1835        /// Output format: json, jsonl (default: opposite of input)
1836        #[arg(long)]
1837        format: Option<String>,
1838
1839        /// VS Code version compatibility: legacy (< 1.109), modern (>= 1.109), both
1840        #[arg(long, default_value = "both")]
1841        compat: String,
1842    },
1843
1844    /// Extract sessions from a VS Code workspace by project path
1845    Extract {
1846        /// Project directory path (will find corresponding workspace hash)
1847        path: String,
1848
1849        /// Output directory for extracted sessions
1850        #[arg(short, long)]
1851        output: Option<String>,
1852
1853        /// Include both JSON and JSONL formats if available
1854        #[arg(long)]
1855        all_formats: bool,
1856
1857        /// Include editing session fragments (agent mode work)
1858        #[arg(long)]
1859        include_edits: bool,
1860    },
1861
1862    /// Detect and display session format and version information
1863    Detect {
1864        /// Session file to analyze (.json or .jsonl)
1865        file: String,
1866
1867        /// Show raw format detection details
1868        #[arg(long)]
1869        verbose: bool,
1870
1871        /// Output detection result as JSON
1872        #[arg(long)]
1873        json: bool,
1874    },
1875
1876    /// Upgrade session files to the current provider format (JSON to JSONL for VS Code 1.109+)
1877    Upgrade {
1878        /// Project paths to upgrade (space-separated)
1879        #[arg(required = true, num_args = 1..)]
1880        project_paths: Vec<String>,
1881
1882        /// Provider to use: vscode, cursor, auto (default: auto-detect)
1883        #[arg(long, default_value = "auto")]
1884        provider: String,
1885
1886        /// Target format: jsonl (VS Code 1.109+), json (legacy). Default: jsonl
1887        #[arg(long, default_value = "jsonl")]
1888        target_format: String,
1889
1890        /// Skip creating backup of original files
1891        #[arg(long)]
1892        no_backup: bool,
1893
1894        /// Dry run - show what would be upgraded without making changes
1895        #[arg(long)]
1896        dry_run: bool,
1897    },
1898}
1899
1900// ============================================================================
1901// Shard Subcommands
1902// ============================================================================
1903
1904#[derive(Subcommand)]
1905pub enum ShardCommands {
1906    /// Shard a single session file into linked parts
1907    Session {
1908        /// Path to the session file (.json or .jsonl)
1909        file: String,
1910
1911        /// Maximum requests per shard (default: 50). Mutually exclusive with --max-size
1912        #[arg(long, short = 'n')]
1913        max_requests: Option<usize>,
1914
1915        /// Maximum file size per shard (e.g. "10MB", "500KB"). Mutually exclusive with --max-requests
1916        #[arg(long, short = 's')]
1917        max_size: Option<String>,
1918
1919        /// Output directory for shard files (default: same directory as input)
1920        #[arg(long, short = 'o')]
1921        output: Option<String>,
1922
1923        /// Update the VS Code session index after sharding
1924        #[arg(long)]
1925        update_index: bool,
1926
1927        /// Workspace hash or project path (for --update-index; auto-detected from file path if omitted)
1928        #[arg(long, short = 'w')]
1929        workspace: Option<String>,
1930
1931        /// Show what would be done without writing any files
1932        #[arg(long)]
1933        dry_run: bool,
1934
1935        /// Skip creating a .oversized backup of the original file
1936        #[arg(long)]
1937        no_backup: bool,
1938    },
1939
1940    /// Shard all oversized sessions in a workspace
1941    Workspace {
1942        /// Workspace hash or project path (default: current directory)
1943        #[arg(long, short = 'w')]
1944        workspace: Option<String>,
1945
1946        /// Maximum requests per shard (default: 50). Mutually exclusive with --max-size
1947        #[arg(long, short = 'n')]
1948        max_requests: Option<usize>,
1949
1950        /// Maximum file size per shard (e.g. "10MB", "500KB"). Mutually exclusive with --max-requests
1951        #[arg(long, short = 's')]
1952        max_size: Option<String>,
1953
1954        /// Show what would be done without writing any files
1955        #[arg(long)]
1956        dry_run: bool,
1957
1958        /// Skip creating .oversized backups of original files
1959        #[arg(long)]
1960        no_backup: bool,
1961    },
1962
1963    /// Show shard metadata for a session file
1964    Info {
1965        /// Path to the session file (.json or .jsonl)
1966        file: String,
1967    },
1968}
1969
1970// ============================================================================
1971// API Server Subcommands
1972// ============================================================================
1973
1974#[derive(Subcommand)]
1975pub enum ApiCommands {
1976    /// Start the API server
1977    Serve {
1978        /// Host to bind to (default: 0.0.0.0 for all interfaces)
1979        #[arg(long, default_value = "0.0.0.0")]
1980        host: String,
1981
1982        /// Port to listen on (default: 8787)
1983        #[arg(short, long, default_value = "8787")]
1984        port: u16,
1985
1986        /// Path to the database file
1987        #[arg(long)]
1988        database: Option<String>,
1989    },
1990}
1991
1992// ============================================================================
1993// Agency (Agent Development Kit) Subcommands
1994// ============================================================================
1995
1996#[derive(Subcommand)]
1997pub enum AgencyCommands {
1998    /// List available agents and their roles
1999    List {
2000        /// Show detailed information
2001        #[arg(short, long)]
2002        verbose: bool,
2003    },
2004
2005    /// Show agent information
2006    Info {
2007        /// Agent name or ID
2008        name: String,
2009    },
2010
2011    /// List supported orchestration modes
2012    Modes,
2013
2014    /// Run an agent with a prompt
2015    Run {
2016        /// Agent name to run
2017        #[arg(short, long, default_value = "assistant")]
2018        agent: String,
2019
2020        /// Prompt or task for the agent
2021        prompt: String,
2022
2023        /// Model to use (e.g., gemini-2.0-flash, gpt-4o)
2024        #[arg(short, long)]
2025        model: Option<String>,
2026
2027        /// Orchestration mode (single, sequential, parallel, swarm)
2028        #[arg(long, default_value = "single")]
2029        orchestration: String,
2030
2031        /// Enable verbose output
2032        #[arg(short, long)]
2033        verbose: bool,
2034    },
2035
2036    /// Create a new agent configuration
2037    Create {
2038        /// Agent name
2039        name: String,
2040
2041        /// Agent role (coordinator, researcher, coder, reviewer, executor, writer, tester, custom)
2042        #[arg(short, long, default_value = "custom")]
2043        role: String,
2044
2045        /// System instruction for the agent
2046        #[arg(short, long)]
2047        instruction: Option<String>,
2048
2049        /// Model to use
2050        #[arg(short, long)]
2051        model: Option<String>,
2052    },
2053
2054    /// List available tools
2055    Tools,
2056
2057    /// Show swarm templates
2058    Templates,
2059}
2060
2061// ============================================================================
2062// Telemetry Subcommands
2063// ============================================================================
2064
2065#[derive(Subcommand)]
2066pub enum TelemetryCommands {
2067    /// Show telemetry status and what data is collected
2068    #[command(visible_alias = "status")]
2069    Info,
2070
2071    /// Enable anonymous usage data collection (this is the default)
2072    #[command(visible_alias = "enable")]
2073    OptIn,
2074
2075    /// Disable anonymous usage data collection
2076    #[command(visible_alias = "disable")]
2077    OptOut,
2078
2079    /// Reset telemetry ID (generates new anonymous identifier)
2080    Reset,
2081
2082    /// Record structured data for later AI analysis
2083    #[command(visible_alias = "log")]
2084    Record {
2085        /// Event category (e.g., 'workflow', 'error', 'performance', 'usage')
2086        #[arg(short, long, default_value = "custom")]
2087        category: String,
2088
2089        /// Event name or type
2090        #[arg(short, long)]
2091        event: String,
2092
2093        /// JSON data payload (or use --kv for key=value pairs)
2094        #[arg(short, long)]
2095        data: Option<String>,
2096
2097        /// Key-value pairs (can be repeated: -k foo=bar -k baz=123)
2098        #[arg(short = 'k', long = "kv", value_parser = parse_key_value)]
2099        kv: Vec<(String, String)>,
2100
2101        /// Add tags for filtering (can be repeated: -t important -t session-123)
2102        #[arg(short, long)]
2103        tags: Vec<String>,
2104
2105        /// Optional session or context ID to associate with
2106        #[arg(long)]
2107        context: Option<String>,
2108
2109        /// Print recorded event details
2110        #[arg(short, long)]
2111        verbose: bool,
2112    },
2113
2114    /// Show recorded telemetry data
2115    #[command(visible_alias = "logs")]
2116    Show {
2117        /// Filter by category
2118        #[arg(short, long)]
2119        category: Option<String>,
2120
2121        /// Filter by event name
2122        #[arg(short, long)]
2123        event: Option<String>,
2124
2125        /// Filter by tag
2126        #[arg(short, long)]
2127        tag: Option<String>,
2128
2129        /// Maximum number of records to show
2130        #[arg(short = 'n', long, default_value = "20")]
2131        limit: usize,
2132
2133        /// Output format: table, json, jsonl
2134        #[arg(short, long, default_value = "table")]
2135        format: String,
2136
2137        /// Show records after this date (YYYY-MM-DD)
2138        #[arg(long)]
2139        after: Option<String>,
2140
2141        /// Show records before this date (YYYY-MM-DD)
2142        #[arg(long)]
2143        before: Option<String>,
2144    },
2145
2146    /// Export recorded data for AI analysis
2147    Export {
2148        /// Output file path
2149        output: String,
2150
2151        /// Export format: json, jsonl, csv
2152        #[arg(short, long, default_value = "jsonl")]
2153        format: String,
2154
2155        /// Filter by category
2156        #[arg(short, long)]
2157        category: Option<String>,
2158
2159        /// Include installation metadata in export
2160        #[arg(long)]
2161        with_metadata: bool,
2162    },
2163
2164    /// Clear recorded telemetry data
2165    Clear {
2166        /// Skip confirmation prompt
2167        #[arg(short, long)]
2168        force: bool,
2169
2170        /// Only clear records older than N days
2171        #[arg(long)]
2172        older_than: Option<u32>,
2173    },
2174
2175    /// Configure remote telemetry endpoint
2176    Config {
2177        /// Set the remote endpoint URL
2178        #[arg(long)]
2179        endpoint: Option<String>,
2180
2181        /// Set the API key for authentication
2182        #[arg(long)]
2183        api_key: Option<String>,
2184
2185        /// Enable remote telemetry sending
2186        #[arg(long)]
2187        enable_remote: bool,
2188
2189        /// Disable remote telemetry sending
2190        #[arg(long)]
2191        disable_remote: bool,
2192    },
2193
2194    /// Sync telemetry records to remote server
2195    Sync {
2196        /// Maximum number of records to sync
2197        #[arg(short = 'n', long)]
2198        limit: Option<usize>,
2199
2200        /// Clear local records after successful sync
2201        #[arg(long)]
2202        clear_after: bool,
2203    },
2204
2205    /// Test connection to remote telemetry server
2206    Test,
2207}
2208
2209/// Parse key=value pairs for telemetry record command
2210fn parse_key_value(s: &str) -> std::result::Result<(String, String), String> {
2211    let pos = s
2212        .find('=')
2213        .ok_or_else(|| format!("invalid key=value pair: no '=' found in '{s}'"))?;
2214    Ok((s[..pos].to_string(), s[pos + 1..].to_string()))
2215}
2216
2217// ============================================================================
2218// Shell Completion Enum
2219// ============================================================================
2220
2221/// Supported shells for completion generation
2222#[derive(Clone, Debug, clap::ValueEnum)]
2223pub enum CompletionShell {
2224    /// Bash shell
2225    Bash,
2226    /// Zsh shell
2227    Zsh,
2228    /// Fish shell
2229    Fish,
2230    /// PowerShell
2231    Powershell,
2232    /// Elvish shell
2233    Elvish,
2234}