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 statusLine integration with Claude Code
296    ///
297    /// Installs a wrapper script that displays the current cc-switch alias name
298    /// in Claude Code's statusLine, alongside the original statusLine content.
299    ///
300    /// Usage:
301    ///   cc-switch statusline install    # Install/update the wrapper
302    ///   cc-switch statusline uninstall  # Remove the wrapper
303    #[command(name = "statusline")]
304    Statusline {
305        /// Action to perform (install or uninstall)
306        #[arg(value_enum, default_value = "install")]
307        action: StatuslineAction,
308    },
309}
310
311/// Actions for the statusline subcommand
312#[derive(Clone, Copy, clap::ValueEnum)]
313pub enum StatuslineAction {
314    /// Install the statusLine wrapper script
315    Install,
316    /// Uninstall the statusLine wrapper script
317    Uninstall,
318}
319
320/// Available subcommands for Codex configuration management
321#[derive(Subcommand)]
322pub enum CodexCommands {
323    /// Add a new Codex (OpenAI CLI) configuration
324    Add {
325        #[arg(help = "Configuration alias name")]
326        alias_name: String,
327        #[arg(long = "api-key", help = "OpenAI API key (optional)")]
328        api_key: Option<String>,
329        #[arg(long = "force", short = 'f', help = "Overwrite existing configuration")]
330        force: bool,
331        #[arg(
332            long = "interactive",
333            short = 'i',
334            help = "Enter configuration values interactively"
335        )]
336        interactive: bool,
337        /// Import from existing auth.json file
338        ///
339        /// With no value, imports from `~/.codex/auth.json`.
340        /// With a value, imports from the given path.
341        #[arg(
342            long = "from-file",
343            num_args = 0..=1,
344            value_name = "PATH",
345            help = "Import from auth.json (defaults to ~/.codex/auth.json if no path)"
346        )]
347        from_file: Option<Option<String>>,
348    },
349    List {
350        #[arg(long = "plain", short = 'p')]
351        plain: bool,
352        #[arg(long = "name", short = 'n', help = "Show only name and auth mode")]
353        name: bool,
354    },
355    #[command(trailing_var_arg = true)]
356    Use {
357        alias_name: String,
358        #[arg(long = "continue", short = 'c')]
359        r#continue: bool,
360        #[arg(long = "resume", short = 'r')]
361        resume: Option<String>,
362        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
363        prompt: Vec<String>,
364    },
365    Remove {
366        #[arg(required = true)]
367        alias_names: Vec<String>,
368    },
369}