Skip to content

Commit d32a379

Browse files
committed
Allow setting model for Claude Code
1 parent 5647deb commit d32a379

File tree

4 files changed

+77
-6
lines changed

4 files changed

+77
-6
lines changed

src/backend.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,31 +24,40 @@ pub async fn query_with_options(prompt: &str, options: QueryOptions) -> Result<(
2424
let config = Config::load()?;
2525

2626
match config.backend {
27-
AiBackend::Claude => query_claude(prompt, options).await,
28-
AiBackend::Cursor => query_cursor(prompt, options).await,
27+
AiBackend::Claude => query_claude(prompt, options, &config).await,
28+
AiBackend::Cursor => query_cursor(prompt, options, &config).await,
2929
}
3030
}
3131

3232
/// Query Claude Code
33-
async fn query_claude(prompt: &str, options: QueryOptions) -> Result<(String, String)> {
33+
async fn query_claude(
34+
prompt: &str,
35+
options: QueryOptions,
36+
config: &Config,
37+
) -> Result<(String, String)> {
3438
let claude_options = claude::QueryOptions {
3539
system_prompt: options.system_prompt,
3640
resume_session: options.resume_session,
3741
cwd: options.cwd,
3842
skip_permissions: options.skip_permissions,
43+
model: config.claude.model.clone(),
3944
};
4045

4146
claude::query_with_options(prompt, claude_options).await
4247
}
4348

4449
/// Query Cursor CLI
45-
async fn query_cursor(prompt: &str, options: QueryOptions) -> Result<(String, String)> {
50+
async fn query_cursor(
51+
prompt: &str,
52+
options: QueryOptions,
53+
config: &Config,
54+
) -> Result<(String, String)> {
4655
let cursor_options = cursor::QueryOptions {
4756
context: options.system_prompt,
4857
resume_session: options.resume_session,
4958
cwd: options.cwd,
5059
force: options.skip_permissions,
51-
model: None,
60+
model: config.cursor.model.clone(),
5261
};
5362

5463
cursor::query_with_options(prompt, cursor_options).await

src/claude.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ pub struct QueryOptions {
3030
pub cwd: Option<String>,
3131
/// Skip permission prompts (for automated flows)
3232
pub skip_permissions: bool,
33+
/// Model alias or full model name (e.g. "sonnet", "opus")
34+
pub model: Option<String>,
3335
}
3436

3537
/// Query Claude with a prompt and return the response
@@ -100,6 +102,11 @@ pub async fn query_with_options(prompt: &str, options: QueryOptions) -> Result<(
100102
cmd.args(["--resume", session_id]);
101103
}
102104

105+
// Model selection
106+
if let Some(ref model) = options.model {
107+
cmd.args(["--model", model]);
108+
}
109+
103110
// Set working directory
104111
if let Some(ref cwd) = options.cwd {
105112
cmd.current_dir(cwd);

src/cmd/init.rs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1014,6 +1014,55 @@ async fn setup_claude(existing_config: Option<Config>) -> Result<()> {
10141014
config.claude.vertex_credentials_path = None;
10151015
}
10161016

1017+
// Model selection
1018+
println!();
1019+
println!("Claude Code supports multiple models.");
1020+
println!("Aliases always point to the latest version.");
1021+
println!();
1022+
1023+
let model_choices = vec![
1024+
"default Recommended for your account type",
1025+
"sonnet Latest Sonnet (fast, daily coding)",
1026+
"opus Latest Opus (complex reasoning)",
1027+
"haiku Latest Haiku (fast, simple tasks)",
1028+
"opusplan Opus for planning, Sonnet for execution",
1029+
"Custom Enter a full model name",
1030+
];
1031+
1032+
let current_model_idx = config
1033+
.claude
1034+
.model
1035+
.as_deref()
1036+
.map(|m| match m {
1037+
"default" => 0,
1038+
"sonnet" => 1,
1039+
"opus" => 2,
1040+
"haiku" => 3,
1041+
"opusplan" => 4,
1042+
_ => 5,
1043+
})
1044+
.unwrap_or(0);
1045+
1046+
let model_selection = Select::with_theme(&ColorfulTheme::default())
1047+
.with_prompt("Which model would you like to use?")
1048+
.items(&model_choices)
1049+
.default(current_model_idx)
1050+
.interact()?;
1051+
1052+
config.claude.model = match model_selection {
1053+
0 => None,
1054+
1 => Some("sonnet".to_string()),
1055+
2 => Some("opus".to_string()),
1056+
3 => Some("haiku".to_string()),
1057+
4 => Some("opusplan".to_string()),
1058+
_ => {
1059+
let custom: String = Input::with_theme(&ColorfulTheme::default())
1060+
.with_prompt("Model name (e.g. claude-sonnet-4-5-20250929)")
1061+
.interact_text()?;
1062+
Some(custom.trim().to_string())
1063+
}
1064+
};
1065+
10171066
// Ask whether to switch if another backend was active
10181067
if was_using_cursor {
10191068
println!();
@@ -1037,9 +1086,13 @@ async fn setup_claude(existing_config: Option<Config>) -> Result<()> {
10371086
AiBackend::Claude => "Claude Code",
10381087
AiBackend::Cursor => "Cursor CLI",
10391088
};
1089+
let model_display = config.claude.model.as_deref().unwrap_or("default");
10401090

10411091
println!();
1042-
println!("Setup complete! Active backend: {}", active);
1092+
println!(
1093+
"Setup complete! Active backend: {} (model: {})",
1094+
active, model_display
1095+
);
10431096
println!();
10441097
println!("Config saved to: {}", paths.config_file.display());
10451098
println!();

src/config.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,8 @@ impl Config {
262262
pub struct ClaudeConfig {
263263
/// Anthropic API key or OAuth token (used when not using Vertex AI)
264264
pub api_key: Option<String>,
265+
/// Model alias or full model name (e.g. "sonnet", "opus", "claude-sonnet-4-5-20250929")
266+
pub model: Option<String>,
265267
/// Use Google Vertex AI instead of Anthropic API
266268
#[serde(default)]
267269
pub use_vertex: bool,

0 commit comments

Comments
 (0)