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,
}
})