fix: avoid claude cmd shim on windows

This commit is contained in:
lucaronin
2026-05-11 11:23:39 +02:00
parent 21c68a1138
commit 6c21aa1087

View File

@@ -486,7 +486,7 @@ where
emitted_text: false,
};
let cmd = build_claude_command(request.bin, attempt_args, request.cwd);
let cmd = build_claude_command(request.bin, attempt_args, request.cwd)?;
let run = crate::cli_agent_runtime::run_json_line_process(
cmd,
"claude",
@@ -517,12 +517,16 @@ where
}
fn build_claude_command(
bin: &PathBuf,
bin: &Path,
args: &[String],
cwd: Option<&str>,
) -> std::process::Command {
let mut cmd = crate::hidden_command(bin);
) -> Result<std::process::Command, String> {
let target = crate::cli_agent_runtime::command_target_avoiding_windows_cmd_shim(bin)?;
let mut cmd = crate::hidden_command(&target.program);
crate::cli_agent_runtime::configure_agent_command_environment(&mut cmd, bin);
if let Some(first_arg) = target.first_arg {
cmd.arg(first_arg);
}
cmd.args(args)
.env_remove("CLAUDECODE") // prevent "nested session" guard
.stdin(Stdio::null())
@@ -531,7 +535,7 @@ fn build_claude_command(
if let Some(dir) = cwd {
cmd.current_dir(dir);
}
cmd
Ok(cmd)
}
fn format_failed_claude_exit(stderr_output: &str, status: ExitStatus) -> String {
@@ -1344,7 +1348,7 @@ mod tests {
fn build_claude_command_keeps_streaming_process_contract() {
let bin = PathBuf::from("claude");
let args = vec!["-p".to_string(), "hello".to_string()];
let command = build_claude_command(&bin, &args, Some("/tmp/vault"));
let command = build_claude_command(&bin, &args, Some("/tmp/vault")).unwrap();
let actual_args: Vec<OsString> = command.get_args().map(OsStr::to_os_string).collect();
let claude_code_env = command
.get_envs()
@@ -1367,6 +1371,46 @@ mod tests {
);
}
#[test]
fn build_claude_command_avoids_windows_cmd_shim_for_prompt_args() {
let dir = tempfile::tempdir().unwrap();
let shim = dir.path().join("claude.cmd");
let script = dir
.path()
.join("node_modules")
.join("@anthropic-ai")
.join("claude-code")
.join("cli.js");
std::fs::create_dir_all(script.parent().unwrap()).unwrap();
std::fs::write(&script, "console.log('claude')\n").unwrap();
std::fs::write(
&shim,
r#"@ECHO off
"%_prog%" "%~dp0\node_modules\@anthropic-ai\claude-code\cli.js" %*
"#,
)
.unwrap();
let args = vec![
"-p".to_string(),
"Rename the note after reading the active vault".to_string(),
];
let command = build_claude_command(&shim, &args, Some("/tmp/vault")).unwrap();
let actual_args = command.get_args().collect::<Vec<_>>();
assert_ne!(
command.get_program(),
shim.as_os_str(),
"Claude npm .cmd shims cannot safely receive prompt args directly"
);
assert_eq!(actual_args.first().copied(), Some(script.as_os_str()));
assert!(actual_args.iter().any(|arg| *arg == OsStr::new("-p")));
assert!(actual_args
.iter()
.any(|arg| *arg == OsStr::new("Rename the note after reading the active vault")));
assert_eq!(command.get_current_dir(), Some(Path::new("/tmp/vault")));
}
#[cfg(unix)]
fn run_mock_script(script: &str) -> (Result<String, String>, Vec<ClaudeStreamEvent>) {
run_mock_script_with_args(script, &[])