Skip to main content

cc_switch/cli/
cli.rs

1use clap::{Parser, Subcommand};
2
3/// Command-line interface for managing Claude API configurations
4#[derive(Parser)]
5#[command(name = "cc-switch")]
6#[command(about = "A CLI tool for managing Claude API configurations")]
7#[command(version)]
8#[command(disable_help_subcommand = true)]
9#[command(
10    long_about = "cc-switch helps you manage multiple Claude API configurations and switch between them easily.
11
12EXAMPLES:
13    cc-switch add my-config sk-ant-xxx https://api.anthropic.com
14    cc-switch add my-config -t sk-ant-xxx -u https://api.anthropic.com
15    cc-switch add my-config -t sk-ant-xxx -u https://api.anthropic.com -m claude-3-5-sonnet-20241022
16    cc-switch add my-config -t sk-ant-xxx -u https://api.anthropic.com --small-fast-model claude-3-haiku-20240307
17    cc-switch add my-config -t sk-ant-xxx -u https://api.anthropic.com --max-thinking-tokens 8192
18    cc-switch add my-config -i                       # Interactive mode
19    cc-switch add my-config --from-file              # Import from ~/.claude/settings.json
20    cc-switch add my-config --from-file ./other.json # Import from an explicit JSON file
21    cc-switch add my-config --force  # Overwrite existing config
22    cc-switch list
23    cc-switch remove config1 config2 config3
24    cc-switch current  # Interactive mode to view and switch configurations
25    cc-switch  # Enter interactive mode (same as 'current' without arguments)
26
27CODEX CONFIGURATIONS:
28    cc-switch codex add work --from-file                       # Import from ~/.codex/auth.json
29    cc-switch codex add work --from-file ~/other/auth.json     # Import from an explicit path
30    cc-switch codex add personal -i  # Interactive mode
31    cc-switch codex list
32    cc-switch codex use work  # Switch and launch Codex
33    cc-switch codex remove work
34
35SHELL COMPLETION AND ALIASES:
36    cc-switch completion fish  # Generates shell completions
37    cc-switch alias fish       # Generates aliases for eval
38
39    These aliases are available:
40    - cs='cc-switch'                              # Quick access to cc-switch
41    - ccd='claude --dangerously-skip-permissions' # Quick Claude launch
42
43    To use aliases immediately:
44    eval \"$(cc-switch alias fish)\"    # Add aliases to current session
45
46    Or add them permanently:
47    cc-switch completion fish > ~/.config/fish/completions/cc-switch.fish
48    echo \"alias cs='cc-switch'\" >> ~/.config/fish/config.fish
49    echo \"alias ccd='claude --dangerously-skip-permissions'\" >> ~/.config/fish/config.fish
50
51    Then use:
52    cs current    # Instead of cc-switch current
53    ccd           # Quick Claude launch"
54)]
55pub struct Cli {
56    #[command(subcommand)]
57    pub command: Option<Commands>,
58
59    /// List available configuration aliases (for shell completion)
60    #[arg(long = "list-aliases", hide = true)]
61    pub list_aliases: bool,
62
63    /// List available Codex configuration aliases (for shell completion)
64    #[arg(long = "list-codex-aliases", hide = true)]
65    pub list_codex_aliases: bool,
66
67    /// Migrate old config path (~/.cc_auto_switch/configurations.json) to new path
68    #[arg(
69        long = "migrate",
70        help = "Migrate old config path to new path and exit"
71    )]
72    pub migrate: bool,
73
74    /// Storage mode for writing configuration (env or config)
75    #[arg(
76        long = "store",
77        help = "Storage mode for writing configuration (env: write to env field, config: write to root with camelCase)",
78        global = true
79    )]
80    pub store: Option<String>,
81}
82
83/// Available subcommands for configuration management
84#[derive(Subcommand)]
85#[allow(clippy::large_enum_variant)]
86pub enum Commands {
87    /// Add a new Claude API configuration
88    ///
89    /// Stores a new configuration with alias, API token, base URL, and optional model settings
90    Add {
91        /// Configuration alias name (used to identify this config)
92        #[arg(help = "Configuration alias name (cannot be 'cc')")]
93        alias_name: String,
94
95        /// ANTHROPIC_AUTH_TOKEN value (your Claude API token)
96        #[arg(
97            long = "token",
98            short = 't',
99            help = "API token (optional if not using interactive mode)"
100        )]
101        token: Option<String>,
102
103        /// ANTHROPIC_BASE_URL value (API endpoint URL)
104        #[arg(
105            long = "url",
106            short = 'u',
107            help = "API endpoint URL (optional if not using interactive mode)"
108        )]
109        url: Option<String>,
110
111        /// ANTHROPIC_MODEL value (custom model name)
112        #[arg(long = "model", short = 'm', help = "Custom model name (optional)")]
113        model: Option<String>,
114
115        /// ANTHROPIC_SMALL_FAST_MODEL value (Haiku-class model for background tasks)
116        #[arg(
117            long = "small-fast-model",
118            help = "Haiku-class model for background tasks (optional)"
119        )]
120        small_fast_model: Option<String>,
121
122        /// ANTHROPIC_MAX_THINKING_TOKENS value (Maximum thinking tokens limit)
123        #[arg(
124            long = "max-thinking-tokens",
125            help = "Maximum thinking tokens limit (optional)"
126        )]
127        max_thinking_tokens: Option<u32>,
128
129        /// API timeout in milliseconds
130        #[arg(
131            long = "api-timeout-ms",
132            help = "API timeout in milliseconds (optional)"
133        )]
134        api_timeout_ms: Option<u32>,
135
136        /// Disable non-essential traffic flag
137        #[arg(
138            long = "disable-nonessential-traffic",
139            help = "Disable non-essential traffic flag (optional)"
140        )]
141        claude_code_disable_nonessential_traffic: Option<u32>,
142
143        /// Default Sonnet model name
144        #[arg(
145            long = "default-sonnet-model",
146            help = "Default Sonnet model name (optional)"
147        )]
148        anthropic_default_sonnet_model: Option<String>,
149
150        /// Default Opus model name
151        #[arg(
152            long = "default-opus-model",
153            help = "Default Opus model name (optional)"
154        )]
155        anthropic_default_opus_model: Option<String>,
156
157        /// Default Haiku model name
158        #[arg(
159            long = "default-haiku-model",
160            help = "Default Haiku model name (optional)"
161        )]
162        anthropic_default_haiku_model: Option<String>,
163
164        /// CLAUDE_CODE_SUBAGENT_MODEL value (model for subagents)
165        #[arg(long = "subagent-model", help = "Subagent model name (optional)")]
166        claude_code_subagent_model: Option<String>,
167
168        /// CLAUDE_CODE_DISABLE_NONSTREAMING_FALLBACK flag
169        #[arg(
170            long = "disable-nonstreaming-fallback",
171            help = "Disable non-streaming fallback flag (optional)"
172        )]
173        claude_code_disable_nonstreaming_fallback: Option<u32>,
174
175        /// CLAUDE_CODE_EFFORT_LEVEL value
176        #[arg(
177            long = "effort-level",
178            help = "Effort level for Claude Code (optional, e.g., 'max')"
179        )]
180        claude_code_effort_level: Option<String>,
181
182        /// DISABLE_PROMPT_CACHING flag
183        #[arg(
184            long = "disable-prompt-caching",
185            help = "Disable prompt caching flag (optional)"
186        )]
187        disable_prompt_caching: Option<u32>,
188
189        /// CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS flag
190        #[arg(
191            long = "disable-experimental-betas",
192            help = "Disable experimental betas flag (optional)"
193        )]
194        claude_code_disable_experimental_betas: Option<u32>,
195
196        /// DISABLE_AUTOUPDATER flag
197        #[arg(
198            long = "disable-autoupdater",
199            help = "Disable auto-updater flag (optional)"
200        )]
201        disable_autoupdater: Option<u32>,
202
203        /// Force overwrite existing configuration
204        #[arg(
205            long = "force",
206            short = 'f',
207            help = "Overwrite existing configuration with same alias"
208        )]
209        force: bool,
210
211        /// Interactive mode for entering configuration values
212        #[arg(
213            long = "interactive",
214            short = 'i',
215            help = "Enter configuration values interactively"
216        )]
217        interactive: bool,
218
219        /// Positional token argument (for backward compatibility)
220        #[arg(help = "API token (if not using -t flag)")]
221        token_arg: Option<String>,
222
223        /// Positional URL argument (for backward compatibility)
224        #[arg(help = "API endpoint URL (if not using -u flag)")]
225        url_arg: Option<String>,
226
227        /// Import configuration from a JSON file
228        ///
229        /// With no value, imports from `~/.claude/settings.json`.
230        /// With a value, imports from the given path.
231        #[arg(
232            long = "from-file",
233            num_args = 0..=1,
234            value_name = "PATH",
235            help = "Import configuration from JSON file (defaults to ~/.claude/settings.json if no path)"
236        )]
237        from_file: Option<Option<String>>,
238    },
239    /// Remove one or more configurations by alias name
240    ///
241    /// Deletes stored configurations by their alias names
242    Remove {
243        /// Configuration alias name(s) to remove (one or more)
244        #[arg(required = true)]
245        alias_names: Vec<String>,
246    },
247    /// List all stored configurations
248    ///
249    /// Displays all saved configurations with their aliases, tokens, and URLs
250    List {
251        /// Output in plain text format (default is JSON)
252        #[arg(long = "plain", short = 'p')]
253        plain: bool,
254        /// Show only name and URL
255        #[arg(long = "name", short = 'n')]
256        name: bool,
257    },
258    /// Generate shell completion scripts
259    ///
260    /// Generates completion scripts for supported shells
261    #[command(alias = "C")]
262    Completion {
263        /// Shell type (fish, zsh, bash, elvish, powershell)
264        #[arg(default_value = "fish")]
265        shell: String,
266    },
267    /// Switch to a configuration and optionally send a prompt to Claude
268    ///
269    /// Quickly switches to the specified configuration and launches Claude.
270    /// Any additional arguments after the alias name are joined and sent as a prompt.
271    /// Use --resume to resume a previous Claude session by ID.
272    /// Use --continue to continue the most recent Claude session.
273    #[command(trailing_var_arg = true)]
274    Use {
275        /// Configuration alias name to switch to
276        alias_name: String,
277
278        /// Resume a previous Claude session by ID
279        #[arg(long, short = 'r')]
280        resume: Option<String>,
281
282        /// Continue the most recent Claude session
283        #[arg(long, short = 'c')]
284        r#continue: bool,
285
286        /// Prompt to send to Claude (all remaining arguments)
287        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
288        prompt: Vec<String>,
289    },
290    /// Manage Codex (OpenAI CLI) configurations
291    Codex {
292        #[command(subcommand)]
293        command: Option<CodexCommands>,
294    },
295    /// Manage the ccs-proxy daemon (start/stop/status/restart)
296    ///
297    /// The daemon supervises one local ccs-proxy per unique upstream URL,
298    /// transparently capturing all Claude API traffic for the dashboard.
299    Daemon {
300        #[command(subcommand)]
301        command: DaemonCommands,
302    },
303    /// Manage statusLine integration with Claude Code
304    ///
305    /// Installs a wrapper script that displays the current cc-switch alias name
306    /// in Claude Code's statusLine, alongside the original statusLine content.
307    ///
308    /// Usage:
309    ///   cc-switch statusline install    # Install/update the wrapper
310    ///   cc-switch statusline uninstall  # Remove the wrapper
311    #[command(name = "statusline")]
312    Statusline {
313        /// Action to perform (install or uninstall)
314        #[arg(value_enum, default_value = "install")]
315        action: StatuslineAction,
316    },
317}
318
319/// Actions for the statusline subcommand
320#[derive(Clone, Copy, clap::ValueEnum)]
321pub enum StatuslineAction {
322    /// Install the statusLine wrapper script
323    Install,
324    /// Uninstall the statusLine wrapper script
325    Uninstall,
326}
327
328/// Subcommands for `cc-switch daemon`
329#[derive(Subcommand)]
330pub enum DaemonCommands {
331    /// Start the daemon (double-forks into background by default)
332    Start {
333        /// Run in the foreground (don't daemonize). Useful for debugging.
334        #[arg(long)]
335        foreground: bool,
336
337        /// Log level: error, warn, info, debug, trace
338        #[arg(long = "log-level", value_name = "LEVEL")]
339        log_level: Option<String>,
340
341        /// Increase verbosity (-v info, -vv debug, -vvv trace)
342        #[arg(short = 'v', long = "verbose", action = clap::ArgAction::Count)]
343        verbose: u8,
344    },
345    /// Stop the running daemon
346    Stop,
347    /// Show daemon status and proxy health
348    Status {
349        /// Output as JSON instead of a human-readable table
350        #[arg(long)]
351        json: bool,
352    },
353    /// Stop then start the daemon (picks up configuration changes)
354    Restart {
355        /// Run in the foreground after restart
356        #[arg(long)]
357        foreground: bool,
358
359        /// Log level: error, warn, info, debug, trace
360        #[arg(long = "log-level", value_name = "LEVEL")]
361        log_level: Option<String>,
362
363        /// Increase verbosity (-v info, -vv debug, -vvv trace)
364        #[arg(short = 'v', long = "verbose", action = clap::ArgAction::Count)]
365        verbose: u8,
366    },
367}
368
369/// Available subcommands for Codex configuration management
370#[derive(Subcommand)]
371pub enum CodexCommands {
372    /// Add a new Codex (OpenAI CLI) configuration
373    Add {
374        #[arg(help = "Configuration alias name")]
375        alias_name: String,
376        #[arg(long = "api-key", help = "OpenAI API key (optional)")]
377        api_key: Option<String>,
378        #[arg(long = "force", short = 'f', help = "Overwrite existing configuration")]
379        force: bool,
380        #[arg(
381            long = "interactive",
382            short = 'i',
383            help = "Enter configuration values interactively"
384        )]
385        interactive: bool,
386        /// Import from existing auth.json file
387        ///
388        /// With no value, imports from `~/.codex/auth.json`.
389        /// With a value, imports from the given path.
390        #[arg(
391            long = "from-file",
392            num_args = 0..=1,
393            value_name = "PATH",
394            help = "Import from auth.json (defaults to ~/.codex/auth.json if no path)"
395        )]
396        from_file: Option<Option<String>>,
397    },
398    List {
399        #[arg(long = "plain", short = 'p')]
400        plain: bool,
401        #[arg(long = "name", short = 'n', help = "Show only name and auth mode")]
402        name: bool,
403    },
404    #[command(trailing_var_arg = true)]
405    Use {
406        alias_name: String,
407        #[arg(long = "continue", short = 'c')]
408        r#continue: bool,
409        #[arg(long = "resume", short = 'r')]
410        resume: Option<String>,
411        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
412        prompt: Vec<String>,
413    },
414    Remove {
415        #[arg(required = true)]
416        alias_names: Vec<String>,
417    },
418}