feat(cli): wire --reasoning-effort flag end-to-end — closes ROADMAP #34

Parse --reasoning-effort <low|medium|high> in parse_args, thread through
CliAction::Prompt and CliAction::Repl, LiveCli::set_reasoning_effort(),
AnthropicRuntimeClient.reasoning_effort field, and MessageRequest.reasoning_effort.

Changes:
- parse_args: new --reasoning-effort / --reasoning-effort=VAL flag arms
- AnthropicRuntimeClient: new reasoning_effort field + set_reasoning_effort() method
- LiveCli: new set_reasoning_effort() that reaches through BuiltRuntime -> ConversationRuntime -> api_client_mut()
- runtime::ConversationRuntime: new pub api_client_mut() accessor
- MessageRequest construction: reasoning_effort: self.reasoning_effort.clone()
- run_repl(): accepts and applies reasoning_effort parameter
- parse_direct_slash_cli_action(): propagates reasoning_effort

All 156 CLI tests pass, all api tests pass, cargo fmt clean.
This commit is contained in:
YeonGyu-Kim
2026-04-09 11:08:00 +09:00
parent b1d76983d2
commit ca8950c26b
2 changed files with 51 additions and 14 deletions

View File

@@ -504,6 +504,10 @@ where
&self.session
}
pub fn api_client_mut(&mut self) -> &mut C {
&mut self.api_client
}
pub fn session_mut(&mut self) -> &mut Session {
&mut self.session
}

View File

@@ -209,7 +209,7 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
permission_mode,
compact,
base_commit,
..
reasoning_effort,
} => {
run_stale_base_preflight(base_commit.as_deref());
// Only consume piped stdin as prompt context when the permission
@@ -223,11 +223,9 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
None
};
let effective_prompt = merge_prompt_with_stdin(&prompt, stdin_context.as_deref());
LiveCli::new(model, true, allowed_tools, permission_mode)?.run_turn_with_output(
&effective_prompt,
output_format,
compact,
)?;
let mut cli = LiveCli::new(model, true, allowed_tools, permission_mode)?;
cli.set_reasoning_effort(reasoning_effort);
cli.run_turn_with_output(&effective_prompt, output_format, compact)?;
}
CliAction::Login { output_format } => run_login(output_format)?,
CliAction::Logout { output_format } => run_logout(output_format)?,
@@ -244,8 +242,14 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
allowed_tools,
permission_mode,
base_commit,
..
} => run_repl(model, allowed_tools, permission_mode, base_commit)?,
reasoning_effort,
} => run_repl(
model,
allowed_tools,
permission_mode,
base_commit,
reasoning_effort,
)?,
CliAction::HelpTopic(topic) => print_help_topic(topic),
CliAction::Help { output_format } => print_help(output_format)?,
}
@@ -377,6 +381,7 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
let mut allowed_tool_values = Vec::new();
let mut compact = false;
let mut base_commit: Option<String> = None;
let mut reasoning_effort: Option<String> = None;
let mut rest = Vec::new();
let mut index = 0;
@@ -442,6 +447,17 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
base_commit = Some(flag[14..].to_string());
index += 1;
}
"--reasoning-effort" => {
let value = args
.get(index + 1)
.ok_or_else(|| "missing value for --reasoning-effort".to_string())?;
reasoning_effort = Some(value.clone());
index += 2;
}
flag if flag.starts_with("--reasoning-effort=") => {
reasoning_effort = Some(flag[19..].to_string());
index += 1;
}
"-p" => {
// Claw Code compat: -p "prompt" = one-shot prompt
let prompt = args[index + 1..].join(" ");
@@ -457,7 +473,7 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
.unwrap_or_else(default_permission_mode),
compact,
base_commit: base_commit.clone(),
reasoning_effort: None,
reasoning_effort: reasoning_effort.clone(),
});
}
"--print" => {
@@ -516,7 +532,7 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
allowed_tools,
permission_mode,
base_commit,
reasoning_effort: None,
reasoning_effort: reasoning_effort.clone(),
});
}
if rest.first().map(String::as_str) == Some("--resume") {
@@ -555,7 +571,7 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
permission_mode,
compact,
base_commit,
reasoning_effort: None,
reasoning_effort: reasoning_effort.clone(),
}),
SkillSlashDispatch::Local => Ok(CliAction::Skills {
args,
@@ -581,7 +597,7 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
permission_mode,
compact,
base_commit: base_commit.clone(),
reasoning_effort: None,
reasoning_effort: reasoning_effort.clone(),
})
}
other if other.starts_with('/') => parse_direct_slash_cli_action(
@@ -592,6 +608,7 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
permission_mode,
compact,
base_commit,
reasoning_effort,
),
_other => Ok(CliAction::Prompt {
prompt: rest.join(" "),
@@ -601,7 +618,7 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
permission_mode,
compact,
base_commit,
reasoning_effort: None,
reasoning_effort: reasoning_effort.clone(),
}),
}
}
@@ -695,6 +712,7 @@ fn parse_direct_slash_cli_action(
permission_mode: PermissionMode,
compact: bool,
base_commit: Option<String>,
reasoning_effort: Option<String>,
) -> Result<CliAction, String> {
let raw = rest.join(" ");
match SlashCommand::parse(&raw) {
@@ -722,7 +740,7 @@ fn parse_direct_slash_cli_action(
permission_mode,
compact,
base_commit,
reasoning_effort: None,
reasoning_effort: reasoning_effort.clone(),
}),
SkillSlashDispatch::Local => Ok(CliAction::Skills {
args,
@@ -2772,10 +2790,12 @@ fn run_repl(
allowed_tools: Option<AllowedToolSet>,
permission_mode: PermissionMode,
base_commit: Option<String>,
reasoning_effort: Option<String>,
) -> Result<(), Box<dyn std::error::Error>> {
run_stale_base_preflight(base_commit.as_deref());
let resolved_model = resolve_repl_model(model);
let mut cli = LiveCli::new(resolved_model, true, allowed_tools, permission_mode)?;
cli.set_reasoning_effort(reasoning_effort);
let mut editor =
input::LineEditor::new("> ", cli.repl_completion_candidates().unwrap_or_default());
println!("{}", cli.startup_banner());
@@ -3358,6 +3378,12 @@ impl LiveCli {
Ok(cli)
}
fn set_reasoning_effort(&mut self, effort: Option<String>) {
if let Some(rt) = self.runtime.runtime.as_mut() {
rt.api_client_mut().set_reasoning_effort(effort);
}
}
fn startup_banner(&self) -> String {
let cwd = env::current_dir().map_or_else(
|_| "<unknown>".to_string(),
@@ -6380,6 +6406,7 @@ struct AnthropicRuntimeClient {
allowed_tools: Option<AllowedToolSet>,
tool_registry: GlobalToolRegistry,
progress_reporter: Option<InternalPromptProgressReporter>,
reasoning_effort: Option<String>,
}
impl AnthropicRuntimeClient {
@@ -6444,8 +6471,13 @@ impl AnthropicRuntimeClient {
allowed_tools,
tool_registry,
progress_reporter,
reasoning_effort: None,
})
}
fn set_reasoning_effort(&mut self, effort: Option<String>) {
self.reasoning_effort = effort;
}
}
fn resolve_cli_auth_source() -> Result<AuthSource, Box<dyn std::error::Error>> {
@@ -6491,6 +6523,7 @@ impl ApiClient for AnthropicRuntimeClient {
.then(|| filter_tool_specs(&self.tool_registry, self.allowed_tools.as_ref())),
tool_choice: self.enable_tools.then_some(ToolChoice::Auto),
stream: true,
reasoning_effort: self.reasoning_effort.clone(),
..Default::default()
};