fix(shell): force UTF-8 output on Windows via chcp 65001 (#1018)

Prefixes Windows shell commands with `chcp 65001 >/dev/null & ` so subprocess output is UTF-8 instead of the system ANSI code page (e.g. GBK on Chinese-locale machines). The `display_command` helper strips the prefix so transcripts and approval prompts show the original command.

Closes #982. Thanks to @chnjames for the fix and the test update — the prefix-strip in `display_command` is exactly the right symmetric move.
This commit is contained in:
John Doe
2026-05-08 01:43:48 +08:00
committed by GitHub
parent c64eef7939
commit 493dc0d6e8
+20 -7
View File
@@ -78,10 +78,14 @@ impl CommandSpec {
/// Create a `CommandSpec` for running a shell command via the platform shell.
pub fn shell(command: &str, cwd: PathBuf, timeout: Duration) -> Self {
#[cfg(windows)]
let (program, args) = (
"cmd".to_string(),
vec!["/C".to_string(), command.to_string()],
);
let (program, args) = {
// Force UTF-8 output on Windows by running `chcp 65001` before the
// actual command. Without this, subprocesses output in the system's
// ANSI code page (e.g. GBK for Chinese locales), causing garbled
// text in the shell output panel. See issue #982.
let cmd = format!("chcp 65001 >NUL & {command}");
("cmd".to_string(), vec!["/C".to_string(), cmd])
};
#[cfg(not(windows))]
let (program, args) = (
"sh".to_string(),
@@ -145,7 +149,12 @@ impl CommandSpec {
&& self.args.len() == 2
&& self.args[0].eq_ignore_ascii_case("/C")
{
self.args[1].clone()
// Strip the `chcp 65001 >NUL & ` prefix we add on Windows for
// UTF-8 output (issue #982).
let raw = &self.args[1];
raw.strip_prefix("chcp 65001 >NUL & ")
.unwrap_or(raw)
.to_string()
} else {
// For other commands, join program and args
let mut parts = vec![self.program.clone()];
@@ -530,7 +539,11 @@ mod tests {
fn expected_shell_command(command: &str) -> Vec<String> {
#[cfg(windows)]
{
vec!["cmd".to_string(), "/C".to_string(), command.to_string()]
vec![
"cmd".to_string(),
"/C".to_string(),
format!("chcp 65001 >NUL & {command}"),
]
}
#[cfg(not(windows))]
{
@@ -545,7 +558,7 @@ mod tests {
#[cfg(windows)]
{
assert_eq!(spec.program, "cmd");
assert_eq!(spec.args, vec!["/C", "echo hello"]);
assert_eq!(spec.args, vec!["/C", "chcp 65001 >NUL & echo hello"]);
}
#[cfg(not(windows))]
{