lc/cli/
definitions.rs

1//! CLI definitions and command structures
2//! This file contains all the CLI struct and enum definitions
3
4use clap::{Parser, Subcommand};
5
6// Helper function to parse environment variable KEY=VALUE pairs
7fn parse_env_var(s: &str) -> Result<(String, String), String> {
8    let parts: Vec<&str> = s.splitn(2, '=').collect();
9    if parts.len() != 2 {
10        return Err(format!(
11            "Invalid environment variable format: '{}'. Expected 'KEY=VALUE'",
12            s
13        ));
14    }
15    Ok((parts[0].to_string(), parts[1].to_string()))
16}
17
18#[derive(Parser)]
19#[command(name = "lc")]
20#[command(
21    about = "LLM Client - A fast Rust-based LLM CLI tool with PDF support and RAG capabilities"
22)]
23#[command(version = "0.1.0")]
24pub struct Cli {
25    /// Direct prompt to send to the default model
26    #[arg(value_name = "PROMPT")]
27    pub prompt: Vec<String>,
28
29    /// Provider to use for the prompt
30    #[arg(short = 'p', long = "provider", global = true)]
31    pub provider: Option<String>,
32
33    /// Model to use for the prompt
34    #[arg(short = 'm', long = "model", global = true)]
35    pub model: Option<String>,
36
37    /// System prompt to use (when used with direct prompt)
38    #[arg(short = 's', long = "system")]
39    pub system_prompt: Option<String>,
40
41    /// Max tokens override (supports 'k' suffix, e.g., '2k' for 2000)
42    #[arg(long = "max-tokens")]
43    pub max_tokens: Option<String>,
44
45    /// Temperature override (0.0 to 2.0)
46    #[arg(long = "temperature")]
47    pub temperature: Option<String>,
48
49    /// Attach file(s) to the prompt (supports text files, PDFs with 'pdf' feature)
50    #[arg(short = 'a', long = "attach")]
51    pub attachments: Vec<String>,
52
53    /// Attach image(s) to the prompt (supports jpg, png, gif, webp, or URLs)
54    #[arg(short = 'i', long = "image")]
55    pub images: Vec<String>,
56
57    /// Attach audio file(s) for transcription (supports mp3, wav, flac, etc.)
58    #[arg(short = 'u', long = "audio")]
59    pub audio_files: Vec<String>,
60
61    /// Include tools from MCP server(s) (comma-separated server names)
62    #[arg(short = 't', long = "tools")]
63    pub tools: Option<String>,
64
65    /// Vector database name for RAG (Retrieval-Augmented Generation)
66    #[arg(short = 'v', long = "vectordb")]
67    pub vectordb: Option<String>,
68
69    /// Enable debug/verbose logging
70    #[arg(short = 'd', long = "debug")]
71    pub debug: bool,
72
73    /// Continue the current session (use existing session ID)
74    #[arg(short = 'c', long = "continue")]
75    pub continue_session: bool,
76
77    /// Chat ID to use or continue (alternative to --continue)
78    #[arg(long = "cid")]
79    pub chat_id: Option<String>,
80
81    /// Use search results as context (format: provider or provider:query)
82    #[arg(long = "use-search")]
83    pub use_search: Option<String>,
84
85    /// Enable streaming output for prompt responses
86    #[arg(long = "stream")]
87    pub stream: bool,
88
89    #[command(subcommand)]
90    pub command: Option<Commands>,
91}
92
93#[derive(clap::ValueEnum, Clone, Debug)]
94pub enum CompletionShell {
95    /// Bash shell
96    Bash,
97    /// Zsh shell
98    Zsh,
99    /// Fish shell
100    Fish,
101    /// PowerShell
102    PowerShell,
103    /// Elvish shell
104    Elvish,
105}
106
107#[derive(clap::ValueEnum, Clone, Debug)]
108pub enum McpServerType {
109    /// Standard I/O based MCP server
110    Stdio,
111    /// Server-Sent Events MCP server
112    Sse,
113    /// Streamable HTTP MCP server
114    Streamable,
115}
116
117#[derive(Subcommand)]
118pub enum Commands {
119    /// Provider management (alias: p)
120    #[command(alias = "p")]
121    Providers {
122        #[command(subcommand)]
123        command: ProviderCommands,
124    },
125    /// API key management (alias: k)
126    #[command(alias = "k")]
127    Keys {
128        #[command(subcommand)]
129        command: KeyCommands,
130    },
131    /// Log management (alias: l)
132    #[command(alias = "l")]
133    Logs {
134        #[command(subcommand)]
135        command: LogCommands,
136    },
137    /// Usage statistics and analytics (alias: u)
138    #[command(alias = "u")]
139    Usage {
140        #[command(subcommand)]
141        command: Option<UsageCommands>,
142        /// Show usage for the last N days
143        #[arg(short = 'd', long = "days")]
144        days: Option<u32>,
145        /// Show only token usage (default shows both tokens and requests)
146        #[arg(short = 't', long = "tokens")]
147        tokens_only: bool,
148        /// Show only request counts
149        #[arg(short = 'r', long = "requests")]
150        requests_only: bool,
151        /// Maximum number of items to show in charts
152        #[arg(short = 'n', long = "limit", default_value = "10")]
153        limit: usize,
154    },
155    /// Configuration management (alias: co)
156    #[command(alias = "co")]
157    Config {
158        #[command(subcommand)]
159        command: Option<ConfigCommands>,
160    },
161    /// Interactive chat mode (alias: c)
162    #[command(alias = "c")]
163    Chat {
164        /// Model to use for the chat
165        #[arg(short, long)]
166        model: Option<String>,
167        /// Provider to use for the chat
168        #[arg(short, long)]
169        provider: Option<String>,
170        /// Chat ID to use or continue
171        #[arg(long)]
172        cid: Option<String>,
173        /// Include tools from MCP server(s) (comma-separated server names)
174        #[arg(short = 't', long = "tools")]
175        tools: Option<String>,
176        /// Vector database name for RAG (Retrieval-Augmented Generation)
177        #[arg(short = 'v', long = "vectordb")]
178        database: Option<String>,
179        /// Enable debug/verbose logging
180        #[arg(short = 'd', long = "debug")]
181        debug: bool,
182        /// Attach image(s) to the chat (supports jpg, png, gif, webp, or URLs)
183        #[arg(short = 'i', long = "image")]
184        images: Vec<String>,
185    },
186    /// Global models management (alias: m)
187    #[command(alias = "m")]
188    Models {
189        #[command(subcommand)]
190        command: Option<ModelsCommands>,
191        /// Search query for models (case-insensitive)
192        #[arg(short = 'q', long = "query")]
193        query: Option<String>,
194        /// Filter models that support tools/function calling
195        #[arg(long = "tools")]
196        tools: bool,
197        /// Filter models that support reasoning
198        #[arg(long = "reasoning")]
199        reasoning: bool,
200        /// Filter models that support vision
201        #[arg(long = "vision")]
202        vision: bool,
203        /// Filter models that support audio
204        #[arg(long = "audio")]
205        audio: bool,
206        /// Filter models that support code generation
207        #[arg(long = "code")]
208        code: bool,
209        /// Filter models with minimum context length (e.g., 128k)
210        #[arg(long = "ctx")]
211        context_length: Option<String>,
212        /// Filter models with minimum input token length (e.g., 128k)
213        #[arg(long = "input")]
214        input_length: Option<String>,
215        /// Filter models with minimum output token length (e.g., 128k)
216        #[arg(long = "output")]
217        output_length: Option<String>,
218        /// Filter models with maximum input price per million tokens
219        #[arg(long = "input-price")]
220        input_price: Option<f64>,
221        /// Filter models with maximum output price per million tokens
222        #[arg(long = "output-price")]
223        output_price: Option<f64>,
224    },
225    /// Model alias management (alias: a)
226    #[command(alias = "a")]
227    Alias {
228        #[command(subcommand)]
229        command: AliasCommands,
230    },
231    /// Template management (alias: t)
232    #[command(alias = "t")]
233    Templates {
234        #[command(subcommand)]
235        command: TemplateCommands,
236    },
237    /// Proxy server (alias: pr)
238    #[command(alias = "pr")]
239    Proxy {
240        /// Port to listen on
241        #[arg(short = 'p', long = "port", default_value = "6789")]
242        port: u16,
243        /// Host to bind to
244        #[arg(long = "host", default_value = "127.0.0.1")]
245        host: String,
246        /// Filter by provider
247        #[arg(long = "provider")]
248        provider: Option<String>,
249        /// Filter by specific model (can be provider:model or alias)
250        #[arg(short = 'm', long = "model")]
251        model: Option<String>,
252        /// API key for authentication
253        #[arg(short = 'k', long = "key")]
254        api_key: Option<String>,
255        /// Generate a random API key
256        #[arg(short = 'g', long = "generate-key")]
257        generate_key: bool,
258    },
259    /// MCP server management
260    Mcp {
261        #[command(subcommand)]
262        command: McpCommands,
263    },
264    /// Generate embeddings for text (alias: e)
265    #[command(alias = "e")]
266    Embed {
267        /// Model to use for embeddings
268        #[arg(short, long)]
269        model: String,
270        /// Provider to use for embeddings
271        #[arg(short, long)]
272        provider: Option<String>,
273        /// Vector database name to store embeddings
274        #[arg(short = 'v', long = "vectordb")]
275        database: Option<String>,
276        /// Files to embed (supports glob patterns, including PDFs with 'pdf' feature)
277        #[arg(short = 'f', long = "files")]
278        files: Vec<String>,
279        /// Text to embed (optional if files are provided)
280        text: Option<String>,
281        /// Enable debug/verbose logging
282        #[arg(short = 'd', long = "debug")]
283        debug: bool,
284    },
285    /// Find similar text using vector similarity (alias: s)
286    #[command(alias = "s")]
287    Similar {
288        /// Model to use for embeddings (optional if database has existing model)
289        #[arg(short, long)]
290        model: Option<String>,
291        /// Provider to use for embeddings (optional if database has existing model)
292        #[arg(short, long)]
293        provider: Option<String>,
294        /// Vector database name to search
295        #[arg(short = 'v', long = "vectordb")]
296        database: String,
297        /// Number of similar results to return
298        #[arg(short, long, default_value = "5")]
299        limit: usize,
300        /// Query text to find similar content
301        query: String,
302    },
303    /// Vector database management (alias: v)
304    #[command(alias = "v")]
305    Vectors {
306        #[command(subcommand)]
307        command: VectorCommands,
308    },
309    /// Web chat proxy for non-OpenAI compatible services (alias: w)
310    #[command(alias = "w")]
311    WebChatProxy {
312        #[command(subcommand)]
313        command: WebChatProxyCommands,
314    },
315    /// Sync configuration files to/from cloud providers (alias: sy)
316    #[command(alias = "sy")]
317    Sync {
318        #[command(subcommand)]
319        command: SyncCommands,
320    },
321    /// Search provider management (alias: se)
322    #[command(alias = "se")]
323    Search {
324        #[command(subcommand)]
325        command: SearchCommands,
326    },
327    /// Generate images from text prompts (alias: img)
328    #[command(alias = "img")]
329    Image {
330        /// Text prompt for image generation
331        prompt: String,
332        /// Model to use for image generation
333        #[arg(short, long)]
334        model: Option<String>,
335        /// Provider to use for image generation
336        #[arg(short, long)]
337        provider: Option<String>,
338        /// Image size (e.g., "1024x1024", "512x512")
339        #[arg(short, long, default_value = "1024x1024")]
340        size: String,
341        /// Number of images to generate
342        #[arg(short, long, default_value = "1")]
343        count: u32,
344        /// Output directory for generated images
345        #[arg(short, long)]
346        output: Option<String>,
347        /// Enable debug/verbose logging
348        #[arg(short = 'd', long = "debug")]
349        debug: bool,
350    },
351    /// Transcribe audio to text (alias: tr)
352    #[command(alias = "tr")]
353    Transcribe {
354        /// Audio file(s) to transcribe (supports mp3, wav, flac, etc.)
355        audio_files: Vec<String>,
356        /// Model to use for transcription
357        #[arg(short, long)]
358        model: Option<String>,
359        /// Provider to use for transcription
360        #[arg(short, long)]
361        provider: Option<String>,
362        /// Language of the audio (ISO-639-1 format, e.g., "en", "es")
363        #[arg(short = 'l', long)]
364        language: Option<String>,
365        /// Optional prompt to guide the transcription
366        #[arg(long)]
367        prompt: Option<String>,
368        /// Response format (json, text, srt, verbose_json, vtt)
369        #[arg(short = 'f', long, default_value = "text")]
370        format: String,
371        /// Temperature for transcription (0.0 to 1.0)
372        #[arg(long)]
373        temperature: Option<f32>,
374        /// Output file for transcription (optional, prints to stdout if not specified)
375        #[arg(short, long)]
376        output: Option<String>,
377        /// Enable debug/verbose logging
378        #[arg(short = 'd', long = "debug")]
379        debug: bool,
380    },
381    /// Convert text to speech
382    TTS {
383        /// Text to convert to speech
384        text: String,
385        /// Model to use for TTS
386        #[arg(short, long)]
387        model: Option<String>,
388        /// Provider to use for TTS
389        #[arg(short, long)]
390        provider: Option<String>,
391        /// Voice to use (e.g., alloy, echo, fable, onyx, nova, shimmer)
392        #[arg(short = 'v', long, default_value = "alloy")]
393        voice: String,
394        /// Output audio format (mp3, opus, aac, flac, wav, pcm)
395        #[arg(short = 'f', long, default_value = "mp3")]
396        format: String,
397        /// Speech speed (0.25 to 4.0)
398        #[arg(short = 's', long)]
399        speed: Option<f32>,
400        /// Output file for audio (required)
401        #[arg(short, long)]
402        output: String,
403        /// Enable debug/verbose logging
404        #[arg(short = 'd', long = "debug")]
405        debug: bool,
406    },
407    /// Dump metadata JSON from models cache (alias: dump)
408    #[command(alias = "dump")]
409    DumpMetadata {
410        /// Specific provider to dump (optional - dumps all if not specified)
411        provider: Option<String>,
412        /// List available cached metadata files
413        #[arg(short, long)]
414        list: bool,
415    },
416    /// Generate shell completions
417    Completions {
418        /// Shell to generate completions for
419        #[arg(value_enum)]
420        shell: CompletionShell,
421    },
422}
423
424// Command enums
425#[derive(Subcommand)]
426pub enum ModelsCommands {
427    /// Refresh the models cache (alias: r)
428    #[command(alias = "r")]
429    Refresh,
430    /// Show cache information (alias: i)
431    #[command(alias = "i")]
432    Info,
433    /// Dump raw /models responses to JSON files (alias: d)
434    #[command(alias = "d")]
435    Dump,
436    /// List embedding models (alias: e)
437    #[command(alias = "e")]
438    Embed,
439    /// Manage model paths for extraction (alias: p)
440    #[command(alias = "p")]
441    Path {
442        #[command(subcommand)]
443        command: ModelsPathCommands,
444    },
445    /// Manage model tags and extraction rules (alias: t)
446    #[command(alias = "t")]
447    Tags {
448        #[command(subcommand)]
449        command: ModelsTagsCommands,
450    },
451    /// Filter models by tags (alias: f)
452    #[command(alias = "f")]
453    Filter {
454        /// Tags to filter by (comma-separated)
455        #[arg(short = 't', long = "tag")]
456        tags: String,
457    },
458}
459
460#[derive(Subcommand)]
461pub enum ModelsPathCommands {
462    /// List all model extraction paths (alias: l)
463    #[command(alias = "l")]
464    List,
465    /// Add a new model extraction path (alias: a)
466    #[command(alias = "a")]
467    Add {
468        /// JQ-style path to add
469        path: String,
470    },
471    /// Delete a model extraction path (alias: d)
472    #[command(alias = "d")]
473    Delete {
474        /// Path to delete
475        path: String,
476    },
477}
478
479#[derive(Subcommand)]
480pub enum ModelsTagsCommands {
481    /// List all tags and their rules (alias: l)
482    #[command(alias = "l")]
483    List,
484    /// Add a rule to a tag (alias: a)
485    #[command(alias = "a")]
486    Add {
487        /// Tag name
488        tag: String,
489        /// Extraction rule (JQ-style path or search pattern)
490        rule: String,
491    },
492}
493
494#[derive(Subcommand)]
495pub enum AliasCommands {
496    /// Add a new alias (alias: a)
497    #[command(alias = "a")]
498    Add {
499        /// Alias name
500        name: String,
501        /// Provider and model in format provider:model
502        target: String,
503    },
504    /// Remove an alias (alias: d)
505    #[command(alias = "d")]
506    Delete {
507        /// Alias name to remove
508        name: String,
509    },
510    /// List all aliases (alias: l)
511    #[command(alias = "l")]
512    List,
513}
514
515#[derive(Subcommand)]
516pub enum TemplateCommands {
517    /// Add a new template (alias: a)
518    #[command(alias = "a")]
519    Add {
520        /// Template name
521        name: String,
522        /// Template prompt content
523        prompt: String,
524    },
525    /// Remove a template (alias: d)
526    #[command(alias = "d")]
527    Delete {
528        /// Template name to remove
529        name: String,
530    },
531    /// List all templates (alias: l)
532    #[command(alias = "l")]
533    List,
534}
535
536#[derive(Subcommand)]
537pub enum ProviderCommands {
538    /// Install a provider from the registry (alias: i)
539    #[command(alias = "i")]
540    Install {
541        /// Provider name to install
542        name: String,
543        /// Force reinstall even if already installed
544        #[arg(short = 'f', long = "force")]
545        force: bool,
546    },
547    /// Update installed providers (alias: up)
548    #[command(alias = "up")]
549    Upgrade {
550        /// Provider name to update (updates all if not specified)
551        name: Option<String>,
552    },
553    /// Uninstall a provider (alias: un)
554    #[command(alias = "un")]
555    Uninstall {
556        /// Provider name to uninstall
557        name: String,
558    },
559    /// List available providers from registry (alias: av)
560    #[command(alias = "av")]
561    Available {
562        /// Show only official providers
563        #[arg(long = "official")]
564        official: bool,
565        /// Filter by tag
566        #[arg(short = 't', long = "tag")]
567        tag: Option<String>,
568    },
569    /// Add a new provider (alias: a)
570    #[command(alias = "a")]
571    Add {
572        /// Provider name
573        name: String,
574        /// Provider endpoint URL
575        url: String,
576        /// Custom models endpoint path (default: /models)
577        #[arg(short = 'm', long = "models-path")]
578        models_path: Option<String>,
579        /// Custom chat completions endpoint path (default: /chat/completions)
580        #[arg(short = 'c', long = "chat-path")]
581        chat_path: Option<String>,
582    },
583    /// Update an existing provider (alias: u)
584    #[command(alias = "u")]
585    Update {
586        /// Provider name
587        name: String,
588        /// Provider endpoint URL
589        url: String,
590    },
591    /// Remove a provider (alias: r)
592    #[command(alias = "r")]
593    Remove {
594        /// Provider name
595        name: String,
596    },
597    /// List all providers (alias: l)
598    #[command(alias = "l")]
599    List,
600    /// List available models for a provider (alias: m)
601    #[command(alias = "m")]
602    Models {
603        /// Provider name
604        name: String,
605        /// Refresh the models cache for this provider (alias: r)
606        #[arg(short = 'r', long = "refresh")]
607        refresh: bool,
608    },
609    /// Manage custom headers for a provider (alias: h)
610    #[command(alias = "h")]
611    Headers {
612        /// Provider name
613        provider: String,
614        #[command(subcommand)]
615        command: HeaderCommands,
616    },
617    /// Manage provider variables for path templating (alias: v)
618    #[command(alias = "v")]
619    Vars {
620        /// Provider name
621        provider: String,
622        #[command(subcommand)]
623        command: ProviderVarsCommands,
624    },
625    /// Set token URL for a provider (alias: t)
626    #[command(alias = "t")]
627    TokenUrl {
628        /// Provider name
629        provider: String,
630        /// Token URL for dynamic token retrieval
631        url: String,
632    },
633    /// Manage provider API paths (alias: path)
634    #[command(alias = "path")]
635    Paths {
636        /// Provider name
637        provider: String,
638        #[command(subcommand)]
639        command: ProviderPathCommands,
640    },
641}
642
643#[derive(Subcommand)]
644pub enum ProviderVarsCommands {
645    /// Set a provider variable (alias: s)
646    #[command(alias = "s")]
647    Set {
648        /// Variable key (e.g., project, location)
649        key: String,
650        /// Variable value
651        value: String,
652    },
653    /// Get a provider variable (alias: g)
654    #[command(alias = "g")]
655    Get {
656        /// Variable key
657        key: String,
658    },
659    /// List all provider variables (alias: l)
660    #[command(alias = "l")]
661    List,
662}
663
664#[derive(Subcommand)]
665pub enum ProviderPathCommands {
666    /// Add or update a provider path (alias: a)
667    #[command(alias = "a")]
668    Add {
669        /// Models path
670        #[arg(short = 'm', long = "models")]
671        models_path: Option<String>,
672        /// Chat completions path
673        #[arg(short = 'c', long = "chat")]
674        chat_path: Option<String>,
675        /// Image generations path
676        #[arg(short = 'i', long = "images")]
677        images_path: Option<String>,
678        /// Embeddings path
679        #[arg(short = 'e', long = "embeddings")]
680        embeddings_path: Option<String>,
681    },
682    /// Delete a provider path (alias: d)
683    #[command(alias = "d")]
684    Delete {
685        /// Delete models path
686        #[arg(short = 'm', long = "models")]
687        models: bool,
688        /// Delete chat completions path
689        #[arg(short = 'c', long = "chat")]
690        chat: bool,
691        /// Delete image generations path
692        #[arg(short = 'i', long = "images")]
693        images: bool,
694        /// Delete embeddings path
695        #[arg(short = 'e', long = "embeddings")]
696        embeddings: bool,
697    },
698    /// List all provider paths (alias: l)
699    #[command(alias = "l")]
700    List,
701}
702
703#[derive(Subcommand)]
704pub enum HeaderCommands {
705    /// Add a custom header (alias: a)
706    #[command(alias = "a")]
707    Add {
708        /// Header name
709        name: String,
710        /// Header value
711        value: String,
712    },
713    /// Remove a custom header (alias: d)
714    #[command(alias = "d")]
715    Delete {
716        /// Header name
717        name: String,
718    },
719    /// List all custom headers (alias: l)
720    #[command(alias = "l")]
721    List,
722}
723
724#[derive(Subcommand)]
725pub enum KeyCommands {
726    /// Add API key for a provider (alias: a)
727    #[command(alias = "a")]
728    Add {
729        /// Provider name
730        name: String,
731    },
732    /// List providers with API keys (alias: l)
733    #[command(alias = "l")]
734    List,
735    /// Get API key for a provider (alias: g)
736    #[command(alias = "g")]
737    Get {
738        /// Provider name
739        name: String,
740    },
741    /// Remove API key for a provider (alias: r)
742    #[command(alias = "r")]
743    Remove {
744        /// Provider name
745        name: String,
746    },
747}
748
749#[derive(Subcommand)]
750pub enum LogCommands {
751    /// Show all logs (alias: sh)
752    #[command(alias = "sh")]
753    Show {
754        /// Show minimal table format
755        #[arg(long)]
756        minimal: bool,
757    },
758    /// Show recent logs (alias: r)
759    #[command(alias = "r")]
760    Recent {
761        #[command(subcommand)]
762        command: Option<RecentCommands>,
763        /// Number of recent entries to show
764        #[arg(short, long, default_value = "10")]
765        count: usize,
766    },
767    /// Show current session logs (alias: c)
768    #[command(alias = "c")]
769    Current,
770    /// Show database statistics (alias: s)
771    #[command(alias = "s")]
772    Stats,
773    /// Purge all logs (alias: p)
774    #[command(alias = "p")]
775    Purge {
776        /// Confirm purge without prompt
777        #[arg(long)]
778        yes: bool,
779        /// Purge logs older than N days
780        #[arg(long)]
781        older_than_days: Option<u32>,
782        /// Keep only the most recent N entries
783        #[arg(long)]
784        keep_recent: Option<usize>,
785        /// Purge when database exceeds N MB
786        #[arg(long)]
787        max_size_mb: Option<u64>,
788    },
789}
790
791#[derive(Subcommand)]
792pub enum RecentCommands {
793    /// Get last answer from LLM (alias: a)
794    #[command(alias = "a")]
795    Answer {
796        #[command(subcommand)]
797        command: Option<AnswerCommands>,
798    },
799    /// Get last question/prompt asked to LLM (alias: q)
800    #[command(alias = "q")]
801    Question,
802    /// Get model used in last interaction (alias: m)
803    #[command(alias = "m")]
804    Model,
805    /// Get session ID of last interaction (alias: s)
806    #[command(alias = "s")]
807    Session,
808}
809
810#[derive(Subcommand)]
811pub enum UsageCommands {
812    /// Show daily usage statistics (alias: d)
813    #[command(alias = "d")]
814    Daily {
815        /// Number of days to show
816        #[arg(short = 'n', long = "count", default_value = "30")]
817        count: usize,
818    },
819    /// Show weekly usage statistics (alias: w)
820    #[command(alias = "w")]
821    Weekly {
822        /// Number of weeks to show
823        #[arg(short = 'n', long = "count", default_value = "12")]
824        count: usize,
825    },
826    /// Show monthly usage statistics (alias: m)
827    #[command(alias = "m")]
828    Monthly {
829        /// Number of months to show
830        #[arg(short = 'n', long = "count", default_value = "12")]
831        count: usize,
832    },
833    /// Show yearly usage statistics (alias: y)
834    #[command(alias = "y")]
835    Yearly {
836        /// Number of years to show
837        #[arg(short = 'n', long = "count", default_value = "5")]
838        count: usize,
839    },
840    /// Show top models by usage (alias: models)
841    #[command(alias = "models")]
842    Models {
843        /// Number of models to show
844        #[arg(short = 'n', long = "count", default_value = "10")]
845        count: usize,
846    },
847}
848
849#[derive(Subcommand)]
850pub enum AnswerCommands {
851    /// Extract code blocks from last answer (alias: c)
852    #[command(alias = "c")]
853    Code,
854}
855
856#[derive(Subcommand)]
857pub enum ConfigCommands {
858    /// Set configuration values (alias: s)
859    #[command(alias = "s")]
860    Set {
861        #[command(subcommand)]
862        command: SetCommands,
863    },
864    /// Get configuration values (alias: g)
865    #[command(alias = "g")]
866    Get {
867        #[command(subcommand)]
868        command: GetCommands,
869    },
870    /// Delete/unset configuration values (alias: d)
871    #[command(alias = "d")]
872    Delete {
873        #[command(subcommand)]
874        command: DeleteCommands,
875    },
876    /// Show configuration directory path (alias: p)
877    #[command(alias = "p")]
878    Path,
879}
880
881#[derive(Subcommand)]
882pub enum SetCommands {
883    /// Set default provider (alias: p)
884    #[command(alias = "p")]
885    Provider {
886        /// Provider name
887        name: String,
888    },
889    /// Set default model (alias: m)
890    #[command(alias = "m")]
891    Model {
892        /// Model name
893        name: String,
894    },
895    /// Set system prompt (alias: s)
896    #[command(alias = "s")]
897    SystemPrompt {
898        /// System prompt text
899        prompt: String,
900    },
901    /// Set max tokens (alias: mt)
902    #[command(alias = "mt")]
903    MaxTokens {
904        /// Max tokens value (supports 'k' suffix, e.g., '2k' for 2000)
905        value: String,
906    },
907    /// Set temperature (alias: te)
908    #[command(alias = "te")]
909    Temperature {
910        /// Temperature value (0.0 to 2.0)
911        value: String,
912    },
913    /// Set default search provider (alias: se)
914    #[command(alias = "se")]
915    Search {
916        /// Search provider name
917        name: String,
918    },
919    /// Set streaming output preference (alias: st)
920    #[command(alias = "st")]
921    Stream {
922        /// Stream output (true/false)
923        value: String,
924    },
925}
926
927#[derive(Subcommand)]
928pub enum GetCommands {
929    /// Get default provider (alias: p)
930    #[command(alias = "p")]
931    Provider,
932    /// Get default model (alias: m)
933    #[command(alias = "m")]
934    Model,
935    /// Get system prompt (alias: s)
936    #[command(alias = "s")]
937    SystemPrompt,
938    /// Get max tokens (alias: mt)
939    #[command(alias = "mt")]
940    MaxTokens,
941    /// Get temperature (alias: te)
942    #[command(alias = "te")]
943    Temperature,
944    /// Get default search provider (alias: se)
945    #[command(alias = "se")]
946    Search,
947    /// Get streaming output preference (alias: st)
948    #[command(alias = "st")]
949    Stream,
950}
951
952#[derive(Subcommand)]
953pub enum DeleteCommands {
954    /// Delete default provider (alias: p)
955    #[command(alias = "p")]
956    Provider,
957    /// Delete default model (alias: m)
958    #[command(alias = "m")]
959    Model,
960    /// Delete system prompt (alias: s)
961    #[command(alias = "s")]
962    SystemPrompt,
963    /// Delete max tokens (alias: mt)
964    #[command(alias = "mt")]
965    MaxTokens,
966    /// Delete temperature (alias: te)
967    #[command(alias = "te")]
968    Temperature,
969    /// Delete default search provider (alias: se)
970    #[command(alias = "se")]
971    Search,
972    /// Delete streaming output preference (alias: st)
973    #[command(alias = "st")]
974    Stream,
975}
976
977#[derive(Subcommand)]
978pub enum VectorCommands {
979    /// List all vector databases (alias: l)
980    #[command(alias = "l")]
981    List,
982    /// Create a new vector database (alias: c)
983    #[command(alias = "c")]
984    Create {
985        /// Database name
986        name: String,
987    },
988    /// Delete a vector database (alias: d)
989    #[command(alias = "d")]
990    Delete {
991        /// Database name
992        name: String,
993        /// Confirm deletion without prompt
994        #[arg(long)]
995        yes: bool,
996    },
997    /// Show database information (alias: i)
998    #[command(alias = "i")]
999    Info {
1000        /// Database name
1001        name: String,
1002    },
1003    /// Show database statistics (alias: s)
1004    #[command(alias = "s")]
1005    Stats {
1006        /// Database name
1007        name: String,
1008    },
1009    /// Clear all embeddings from database (alias: cl)
1010    #[command(alias = "cl")]
1011    Clear {
1012        /// Database name
1013        name: String,
1014        /// Confirm clear without prompt
1015        #[arg(long)]
1016        yes: bool,
1017    },
1018}
1019
1020#[derive(Subcommand)]
1021pub enum WebChatProxyCommands {
1022    /// Start web chat proxy server (alias: s)
1023    #[command(alias = "s")]
1024    Start {
1025        /// Port to listen on
1026        #[arg(short = 'p', long = "port", default_value = "8080")]
1027        port: u16,
1028        /// Host to bind to
1029        #[arg(long = "host", default_value = "127.0.0.1")]
1030        host: String,
1031        /// Enable CORS for cross-origin requests
1032        #[arg(long = "cors")]
1033        cors: bool,
1034    },
1035}
1036
1037#[derive(Subcommand)]
1038pub enum SyncCommands {
1039    /// List supported cloud providers (alias: p)
1040    #[command(alias = "p")]
1041    Providers,
1042    /// Configure cloud provider settings (alias: c)
1043    #[command(alias = "c")]
1044    Configure {
1045        /// Cloud provider name (e.g., s3, cloudflare, backblaze)
1046        provider: String,
1047        #[command(subcommand)]
1048        command: Option<ConfigureCommands>,
1049    },
1050    /// Sync configuration to cloud provider
1051    To {
1052        /// Cloud provider name (e.g., s3, cloudflare, backblaze)
1053        provider: String,
1054        /// Encrypt files before uploading
1055        #[arg(short = 'e', long = "encrypted")]
1056        encrypted: bool,
1057        /// Skip confirmation prompt
1058        #[arg(short = 'y', long = "yes")]
1059        yes: bool,
1060    },
1061    /// Sync configuration from cloud provider
1062    From {
1063        /// Cloud provider name (e.g., s3, cloudflare, backblaze)
1064        provider: String,
1065        /// Decrypt files after downloading
1066        #[arg(short = 'e', long = "encrypted")]
1067        encrypted: bool,
1068        /// Skip confirmation prompt
1069        #[arg(short = 'y', long = "yes")]
1070        yes: bool,
1071    },
1072}
1073
1074#[derive(Subcommand)]
1075pub enum ConfigureCommands {
1076    /// Initial sync setup (alias: s)
1077    #[command(alias = "s")]
1078    Setup,
1079    /// Show current sync configuration (alias: sh)
1080    #[command(alias = "sh")]
1081    Show,
1082    /// Remove sync configuration (alias: r)
1083    #[command(alias = "r")]
1084    Remove,
1085}
1086
1087#[derive(Subcommand)]
1088pub enum SearchCommands {
1089    /// Manage search providers (alias: p)
1090    #[command(alias = "p")]
1091    Provider {
1092        #[command(subcommand)]
1093        command: SearchProviderCommands,
1094    },
1095    /// Query a search provider directly (alias: q)
1096    #[command(alias = "q")]
1097    Query {
1098        /// Search provider name
1099        provider: String,
1100        /// Search query
1101        query: String,
1102        /// Output format (json or md/markdown)
1103        #[arg(short = 'f', long = "format", default_value = "md")]
1104        format: String,
1105        /// Number of results to return
1106        #[arg(short = 'n', long = "count", default_value = "5")]
1107        count: usize,
1108    },
1109}
1110
1111#[derive(Subcommand)]
1112pub enum SearchProviderCommands {
1113    /// Add a search provider (alias: a)
1114    #[command(alias = "a")]
1115    Add {
1116        /// Provider name
1117        name: String,
1118        /// Provider URL (auto-detects type)
1119        url: String,
1120    },
1121    /// List all search providers (alias: l)
1122    #[command(alias = "l")]
1123    List,
1124    /// Delete a search provider (alias: d)
1125    #[command(alias = "d")]
1126    Delete {
1127        /// Provider name
1128        name: String,
1129    },
1130    /// Set provider headers/configuration (alias: s)
1131    #[command(alias = "s")]
1132    Set {
1133        /// Provider name
1134        provider: String,
1135        /// Header name (e.g., X-API-KEY, Authorization)
1136        header_name: String,
1137        /// Header value
1138        header_value: String,
1139    },
1140}
1141
1142#[derive(Subcommand)]
1143pub enum McpCommands {
1144    /// Add a new MCP server (alias: a)
1145    #[command(alias = "a")]
1146    Add {
1147        /// Server name
1148        name: String,
1149        /// Command or URL for the MCP server
1150        command_or_url: String,
1151        /// MCP server type
1152        #[arg(long = "type", value_enum)]
1153        server_type: McpServerType,
1154        /// Environment variables (can be specified multiple times as KEY=VALUE)
1155        #[arg(short = 'e', long = "env", value_parser = parse_env_var)]
1156        env: Vec<(String, String)>,
1157    },
1158    /// Delete an MCP server configuration (alias: d)
1159    #[command(alias = "d")]
1160    Delete {
1161        /// Server name
1162        name: String,
1163    },
1164    /// List all configured MCP servers (alias: l)
1165    #[command(alias = "l")]
1166    List,
1167    /// Stop an MCP server connection (alias: st)
1168    #[command(alias = "st")]
1169    Stop {
1170        /// Server name
1171        name: String,
1172    },
1173    /// List functions exposed by a running MCP server (alias: f)
1174    #[command(alias = "f")]
1175    Functions {
1176        /// Server name
1177        name: String,
1178    },
1179    /// Invoke a function from a running MCP server (alias: i)
1180    #[command(alias = "i")]
1181    Invoke {
1182        /// Server name
1183        name: String,
1184        /// Function name
1185        function: String,
1186        /// Function arguments
1187        args: Vec<String>,
1188    },
1189    /// Start an MCP server (alias: s)
1190    #[command(alias = "s")]
1191    Start {
1192        /// Server name
1193        name: String,
1194        /// Server command (optional - uses stored configuration if not provided)
1195        command: Option<String>,
1196        /// Server arguments
1197        #[arg(short = 'a', long = "args")]
1198        args: Vec<String>,
1199    },
1200    /// Show MCP server status (alias: st)
1201    #[command(alias = "stat")]
1202    Status {
1203        /// Server name (optional, shows all if not specified)
1204        name: Option<String>,
1205    },
1206}