fix(shell): add PowerShell UTF-8 command wrapper on Windows#31985
fix(shell): add PowerShell UTF-8 command wrapper on Windows#31985senguangd wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR updates Windows PowerShell execution to use -EncodedCommand and a UTF-8 console-encoding prelude, and centralizes PowerShell argument construction in shared helpers to improve reliability of command execution on Windows.
Changes:
- Route Windows PowerShell invocation through
Shell.args(...)instead of hardcoded flags. - Add
-EncodedCommandgeneration for PowerShell, including setting console input/output encoding to UTF-8. - Update the core
bashtool to detect PowerShell shells and spawn them with encoded commands.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| packages/opencode/src/tool/shell.ts | Switches Windows PowerShell spawn args to the shared Shell.args(...) helper. |
| packages/opencode/src/shell/shell.ts | Introduces Base64 UTF-16LE -EncodedCommand generation and updates PowerShell args accordingly. |
| packages/core/src/tool/bash.ts | Adds PowerShell detection and uses -EncodedCommand spawning on Windows. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| function encodedPowerShellCommand(command: string) { | ||
| return Buffer.from( | ||
| ` | ||
| [Console]::InputEncoding = [System.Text.UTF8Encoding]::new($false); | ||
| [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false); | ||
| $OutputEncoding = [Console]::OutputEncoding; | ||
| ${command} | ||
| `, | ||
| "utf16le", | ||
| ).toString("base64") | ||
| } |
There was a problem hiding this comment.
Good catch — this was a real issue. I updated the implementation so the UTF-8 setup is no longer prepended directly to the user-provided PowerShell script text.
The encoded command is now a small wrapper that sets the console encodings first, then decodes the original user command and invokes it as a separate scriptblock. That preserves script-level constructs like leading param(...) blocks and #requires directives, while still keeping the UTF-8 behavior.
| const POWERSHELL_SHELLS = new Set(["powershell", "powershell.exe", "pwsh", "pwsh.exe"]) | ||
|
|
||
| const isPowerShell = (shell: string) => { | ||
| const name = path.basename(shell.trim().replace(/^["']|["']$/g, "")).toLowerCase() | ||
| return POWERSHELL_SHELLS.has(name) | ||
| } | ||
|
|
||
| function encodedPowerShellCommand(command: string) { | ||
| return Buffer.from( | ||
| ` | ||
| [Console]::InputEncoding = [System.Text.UTF8Encoding]::new($false); | ||
| [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false); | ||
| $OutputEncoding = [Console]::OutputEncoding; | ||
| ${command} | ||
| `, | ||
| "utf16le", | ||
| ).toString("base64") | ||
| } |
There was a problem hiding this comment.
Agreed. I extracted the PowerShell argument/encoding construction into a shared helper under @opencode-ai/core, and updated both execution paths to use it.
packages/opencode/src/shell/shell.ts now uses the shared PowerShell helper from Shell.args(...), and packages/opencode/src/tool/shell.ts reuses Shell.args(...) instead of building its own PowerShell argv. The core BashTool also uses the same shared helper, so the -EncodedCommand wrapper behavior stays consistent across the direct shell, shell tool, and core BashTool paths.
39201fe to
2243bee
Compare
140649d to
727d78a
Compare
|
You mentioned that using the |
96a0fd0 to
18c5177
Compare
|
@Thalynor Good question — I rechecked this and updated the PR to make the scope more precise. The latest version no longer uses The actual issue this PR is addressing is the output decoding path: opencode captures child process output as bytes and decodes it as UTF-8, while PowerShell may write stdout/stderr using the active console code page unless The fix now does this instead:
The inner Base64 is not meant to justify So the PR description has been updated to describe this as a PowerShell UTF-8 I/O wrapper fix rather than a |
18c5177 to
f4eded7
Compare
On Windows, PowerShell commands need proper UTF-8 encoding setup to avoid corruption when the console code page is not UTF-8 (e.g. GBK/CP936 on zh-CN systems). This adds a shared PowerShell module that: - Sets [Console]::InputEncoding and OutputEncoding to UTF-8 - Uses inner Base64 + [scriptblock]::Create() to preserve user command semantics (param(), #requires must be at script start) Used by bash tool, shell tool, and TUI direct shell mode.
f4eded7 to
0689b9d
Compare
Issue for this PR
Closes #23636
Closes #31187
Closes #30205
Closes #31830
Closes #26882
Supersedes #31925 (which was closed before the third code path was added)
Type of change
What does this PR do?
On Windows, opencode captures shell output as bytes and decodes it as UTF-8. PowerShell, however, can write stdout/stderr using the active console code page when
[Console]::OutputEncodingis not set explicitly. On non-UTF-8 systems, such as GBK/CP936 or Shift-JIS/CP932 environments, this can cause PowerShell output to be decoded incorrectly by opencode.This PR adds a shared PowerShell command wrapper that:
[Console]::InputEncodingto UTF-8[Console]::OutputEncodingto UTF-8The user command is stored inside the wrapper as an inner UTF-8 Base64 payload, then decoded and passed to
[scriptblock]::Create(). The Base64 payload is used to avoid quoting and escaping issues when embedding arbitrary user command text inside the wrapper. The separate scriptblock invocation avoids prepending the UTF-8 setup directly to the user script, preserving script-level constructs such as leadingparam(...)blocks and#requiresdirectives.Files changed (all three PowerShell execution paths):
packages/core/src/shell/powershell.tspackages/core/src/tool/bash.tspackages/core/src/shell.tsShell.args(...)packages/opencode/src/tool/shell.tsShell.args(...)instead of hardcoding argvHow did you verify your code works?
Write-Output "测试中文输出"renders correctly in opencode shell output on Windows with a non-UTF-8 code pagepowershell5.1 andpwsh7+!shell mode (! echo 你好世界) — Chinese output displays correctlytsgo --noEmiton all 23 packages)Screenshots / recordings
N/A — this is a shell encoding fix, not a UI change.
Checklist