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        /// Force overwrite existing configuration
183        #[arg(
184            long = "force",
185            short = 'f',
186            help = "Overwrite existing configuration with same alias"
187        )]
188        force: bool,
189
190        /// Interactive mode for entering configuration values
191        #[arg(
192            long = "interactive",
193            short = 'i',
194            help = "Enter configuration values interactively"
195        )]
196        interactive: bool,
197
198        /// Positional token argument (for backward compatibility)
199        #[arg(help = "API token (if not using -t flag)")]
200        token_arg: Option<String>,
201
202        /// Positional URL argument (for backward compatibility)
203        #[arg(help = "API endpoint URL (if not using -u flag)")]
204        url_arg: Option<String>,
205
206        /// Import configuration from a JSON file
207        ///
208        /// With no value, imports from `~/.claude/settings.json`.
209        /// With a value, imports from the given path.
210        #[arg(
211            long = "from-file",
212            num_args = 0..=1,
213            value_name = "PATH",
214            help = "Import configuration from JSON file (defaults to ~/.claude/settings.json if no path)"
215        )]
216        from_file: Option<Option<String>>,
217    },
218    /// Remove one or more configurations by alias name
219    ///
220    /// Deletes stored configurations by their alias names
221    Remove {
222        /// Configuration alias name(s) to remove (one or more)
223        #[arg(required = true)]
224        alias_names: Vec<String>,
225    },
226    /// List all stored configurations
227    ///
228    /// Displays all saved configurations with their aliases, tokens, and URLs
229    List {
230        /// Output in plain text format (default is JSON)
231        #[arg(long = "plain", short = 'p')]
232        plain: bool,
233        /// Show only name and URL
234        #[arg(long = "name", short = 'n')]
235        name: bool,
236    },
237    /// Generate shell completion scripts
238    ///
239    /// Generates completion scripts for supported shells
240    #[command(alias = "C")]
241    Completion {
242        /// Shell type (fish, zsh, bash, elvish, powershell)
243        #[arg(default_value = "fish")]
244        shell: String,
245    },
246    /// Switch to a configuration and optionally send a prompt to Claude
247    ///
248    /// Quickly switches to the specified configuration and launches Claude.
249    /// Any additional arguments after the alias name are joined and sent as a prompt.
250    /// Use --resume to resume a previous Claude session by ID.
251    /// Use --continue to continue the most recent Claude session.
252    #[command(trailing_var_arg = true)]
253    Use {
254        /// Configuration alias name to switch to
255        alias_name: String,
256
257        /// Resume a previous Claude session by ID
258        #[arg(long, short = 'r')]
259        resume: Option<String>,
260
261        /// Continue the most recent Claude session
262        #[arg(long, short = 'c')]
263        r#continue: bool,
264
265        /// Prompt to send to Claude (all remaining arguments)
266        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
267        prompt: Vec<String>,
268    },
269    /// Manage Codex (OpenAI CLI) configurations
270    Codex {
271        #[command(subcommand)]
272        command: Option<CodexCommands>,
273    },
274    /// Manage statusLine integration with Claude Code
275    ///
276    /// Installs a wrapper script that displays the current cc-switch alias name
277    /// in Claude Code's statusLine, alongside the original statusLine content.
278    ///
279    /// Usage:
280    ///   cc-switch statusline install    # Install/update the wrapper
281    ///   cc-switch statusline uninstall  # Remove the wrapper
282    #[command(name = "statusline")]
283    Statusline {
284        /// Action to perform (install or uninstall)
285        #[arg(value_enum, default_value = "install")]
286        action: StatuslineAction,
287    },
288}
289
290/// Actions for the statusline subcommand
291#[derive(Clone, Copy, clap::ValueEnum)]
292pub enum StatuslineAction {
293    /// Install the statusLine wrapper script
294    Install,
295    /// Uninstall the statusLine wrapper script
296    Uninstall,
297}
298
299/// Available subcommands for Codex configuration management
300#[derive(Subcommand)]
301pub enum CodexCommands {
302    /// Add a new Codex (OpenAI CLI) configuration
303    Add {
304        #[arg(help = "Configuration alias name")]
305        alias_name: String,
306        #[arg(long = "api-key", help = "OpenAI API key (optional)")]
307        api_key: Option<String>,
308        #[arg(long = "force", short = 'f', help = "Overwrite existing configuration")]
309        force: bool,
310        #[arg(
311            long = "interactive",
312            short = 'i',
313            help = "Enter configuration values interactively"
314        )]
315        interactive: bool,
316        /// Import from existing auth.json file
317        ///
318        /// With no value, imports from `~/.codex/auth.json`.
319        /// With a value, imports from the given path.
320        #[arg(
321            long = "from-file",
322            num_args = 0..=1,
323            value_name = "PATH",
324            help = "Import from auth.json (defaults to ~/.codex/auth.json if no path)"
325        )]
326        from_file: Option<Option<String>>,
327    },
328    List {
329        #[arg(long = "plain", short = 'p')]
330        plain: bool,
331        #[arg(long = "name", short = 'n', help = "Show only name and auth mode")]
332        name: bool,
333    },
334    #[command(trailing_var_arg = true)]
335    Use {
336        alias_name: String,
337        #[arg(long = "continue", short = 'c')]
338        r#continue: bool,
339        #[arg(long = "resume", short = 'r')]
340        resume: Option<String>,
341        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
342        prompt: Vec<String>,
343    },
344    Remove {
345        #[arg(required = true)]
346        alias_names: Vec<String>,
347    },
348}