feat(oscompat): improve PowerShell script for Claude CLI compatibility on Windows

- Added UTF-8 encoding settings for input and output streams to prevent JSON corruption on CJK Windows.
- Implemented a search for common installation paths of claude.exe across user profiles to ensure accessibility when running as a different user.
- Updated logging in the Stream function to include the working directory for better context during execution.
This commit is contained in:
Max 2026-03-17 18:49:15 +08:00
parent b037475c14
commit 0e64d05dbf
2 changed files with 18 additions and 1 deletions

View file

@ -146,6 +146,23 @@ func (e *osEnv) buildPowerShellScript(args []string, systemPrompt, inputJSONL, w
var b strings.Builder
noBOM := "(New-Object System.Text.UTF8Encoding $false)"
// Force UTF-8 for both input and output streams.
// On CJK Windows the default codepage is often GBK/GB2312 (936)
// which corrupts Claude CLI's UTF-8 JSON output.
b.WriteString("[Console]::InputEncoding = [System.Text.Encoding]::UTF8\n")
b.WriteString("[Console]::OutputEncoding = [System.Text.Encoding]::UTF8\n")
b.WriteString("$OutputEncoding = [System.Text.Encoding]::UTF8\n")
// Ensure claude.exe can be found even when Tai runs as a different user.
// Claude CLI is typically installed per-user (e.g. C:\Users\X\.local\bin)
// which isn't in the PATH when Tai runs as a service or another account.
// Scan all user profiles for common install locations.
b.WriteString("foreach ($d in (Get-ChildItem 'C:\\Users' -Directory -ErrorAction SilentlyContinue)) {\n")
b.WriteString(" $p = Join-Path $d.FullName '.local\\bin'\n")
b.WriteString(" if (Test-Path (Join-Path $p 'claude.exe')) { $env:PATH = \"$p;$env:PATH\"; break }\n")
b.WriteString("}\n")
b.WriteString("if ($env:APPDATA) { $env:PATH = \"$env:APPDATA\\npm;$env:PATH\" }\n")
yaoDir := e.pathJoin(workDir, ".yao")
b.WriteString(fmt.Sprintf("if (!(Test-Path '%s')) { New-Item -ItemType Directory -Path '%s' -Force | Out-Null }\n", yaoDir, yaoDir))

View file

@ -104,7 +104,7 @@ func (r *ClaudeRunner) Stream(ctx context.Context, req *types.StreamRequest, han
streamOpts = append(streamOpts, infra.WithStdin(stdin))
}
fmt.Fprintf(os.Stderr, "[claude] Stream cmd=%v hasMCP=%v isContinuation=%v stdinLen=%d\n", cmd, r.hasMCP, isContinuation, len(stdin))
fmt.Fprintf(os.Stderr, "[claude] Stream cmd=%v hasMCP=%v isContinuation=%v stdinLen=%d workDir=%q\n", cmd, r.hasMCP, isContinuation, len(stdin), oe.WorkDir)
execStream, err := computer.Stream(ctx, cmd, streamOpts...)
if err != nil {