diff --git a/README.md b/README.md index 481a86c89..1ccbfdbf2 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,23 @@ sudo apt install xsel ``` +
+Windows: garbled CJK (Chinese/Japanese/Korean) output in the shell + +On Windows with a non-UTF-8 system locale (e.g. zh-CN, whose active code page is 936/GBK), +command output containing CJK characters may appear garbled (mojibake). MiMoCode forces +UTF-8 output for spawned PowerShell/cmd subprocesses. If you still encounter garbled output +in cases this does not yet cover, enable Windows' system-wide UTF-8 support: + +**Settings → Time & language → Language & region → Administrative language settings → +Change system locale → check "Beta: Use Unicode UTF-8 for worldwide language support" → +reboot.** + +This switches the active code page (ACP) to UTF-8 (65001) for all programs, so subprocesses +no longer inherit the legacy code page. Note it is a system-wide Beta toggle and may cause +some older non-Unicode programs to display incorrectly, so treat it as a workaround. +
+ --- ## Core Features diff --git a/README.zh.md b/README.zh.md index eff9aeda4..266aad3c1 100644 --- a/README.zh.md +++ b/README.zh.md @@ -50,6 +50,20 @@ sudo apt install xsel ``` +
+Windows:shell 输出中文(CJK)乱码 + +在系统区域为非 UTF-8 的 Windows 上(如简体中文,活动代码页为 936/GBK),命令输出里的 +中日韩字符可能显示为乱码。MiMoCode 已为 PowerShell/cmd 子进程强制开启 UTF-8 输出。 +如果在尚未覆盖的场景下仍遇到乱码,可以开启 Windows 的系统级 UTF-8 支持: + +**设置 → 时间和语言 → 语言和区域 → 管理语言设置 → 更改系统区域设置 → +勾选「Beta 版: 使用 Unicode UTF-8 提供全球语言支持」→ 重启。** + +这会把活动代码页(ACP)切换为 UTF-8(65001),所有程序都生效,子进程不再继承旧代码页。 +注意这是系统级 Beta 开关,可能导致部分老的非 Unicode 程序显示异常,建议作为临时方案。 +
+ --- ## 核心特性 diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 33afb0f48..b7c33a3ab 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -1256,9 +1256,13 @@ NOTE: At any point in time through this workflow you should feel free to ask the `, ], }, - cmd: { args: ["/c", input.command] }, - powershell: { args: ["-NoProfile", "-Command", input.command] }, - pwsh: { args: ["-NoProfile", "-Command", input.command] }, + cmd: { args: ["/c", `${Shell.CMD_UTF8_PREFIX}${input.command}`] }, + powershell: { + args: ["-NoProfile", "-Command", `${Shell.POWERSHELL_UTF8_PREFIX}${input.command}`], + }, + pwsh: { + args: ["-NoProfile", "-Command", `${Shell.POWERSHELL_UTF8_PREFIX}${input.command}`], + }, "": { args: ["-c", input.command] }, } @@ -1273,7 +1277,11 @@ NOTE: At any point in time through this workflow you should feel free to ask the const cmd = ChildProcess.make(sh, args, { cwd, extendEnv: true, - env: { ...shellEnv.env, TERM: "dumb" }, + env: { + ...shellEnv.env, + ...(process.platform === "win32" ? { PYTHONIOENCODING: "utf-8" } : {}), + TERM: "dumb", + }, stdin: "ignore", forceKillAfter: "3 seconds", }) diff --git a/packages/opencode/src/shell/shell.ts b/packages/opencode/src/shell/shell.ts index 0f0b93f84..96f497c4f 100644 --- a/packages/opencode/src/shell/shell.ts +++ b/packages/opencode/src/shell/shell.ts @@ -107,4 +107,18 @@ export const preferred = lazy(() => select(process.env.SHELL)) export const acceptable = lazy(() => select(process.env.SHELL, { acceptable: true })) +// On non-UTF-8 Windows locales (e.g. zh-CN ACP 936/GBK) shell subprocesses emit +// output in the legacy code page, which we decode as UTF-8 → mojibake. These +// prefixes force UTF-8 output before the user command runs. + +// PowerShell/pwsh: set both the output pipe encoding and the console encoding. +// UTF8Encoding($false) is BOM-less to avoid leading garbage bytes. +export const POWERSHELL_UTF8_PREFIX = + "$OutputEncoding = [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false);" + +// cmd.exe: switch the code page to 65001. `&` (not `&&`) so the user command's +// exit code is preserved and a chcp failure doesn't block it; redirect both +// stdout and stderr so chcp output never pollutes the result. +export const CMD_UTF8_PREFIX = "chcp 65001 >nul 2>nul & " + export * as Shell from "./shell" diff --git a/packages/opencode/src/tool/bash.ts b/packages/opencode/src/tool/bash.ts index 71acf5e6b..6f5236989 100644 --- a/packages/opencode/src/tool/bash.ts +++ b/packages/opencode/src/tool/bash.ts @@ -308,7 +308,8 @@ const ask = Effect.fn("BashTool.ask")(function* (ctx: Tool.Context, scan: Scan) function cmd(shell: string, name: string, command: string, cwd: string, env: NodeJS.ProcessEnv) { if (process.platform === "win32" && PS.has(name)) { - return ChildProcess.make(shell, ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", command], { + const prefixed = `${Shell.POWERSHELL_UTF8_PREFIX}${command}` + return ChildProcess.make(shell, ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", prefixed], { cwd, env, stdin: "ignore", @@ -316,7 +317,10 @@ function cmd(shell: string, name: string, command: string, cwd: string, env: Nod }) } - return ChildProcess.make(command, [], { + const finalCommand = + process.platform === "win32" && name === "cmd" ? `${Shell.CMD_UTF8_PREFIX}${command}` : command + + return ChildProcess.make(finalCommand, [], { shell, cwd, env, @@ -429,6 +433,10 @@ export const BashTool = Tool.define( ) return { ...process.env, + // Python ignores the console code page when stdout is a pipe and falls + // back to the ANSI code page (GBK on zh-CN), producing mojibake. Force + // UTF-8 for child Python processes on Windows. + ...(process.platform === "win32" ? { PYTHONIOENCODING: "utf-8" } : {}), ...extra.env, } })