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