fix(hooks): keep Cyrillic and other non-Latin-1 text intact in Windows hooks - #139
Merged
Merged
Conversation
Windows PowerShell 5.1 decodes stdin with the console code page and encodes a string -Body as ISO-8859-1, so every Cyrillic character in an AskUserQuestion payload reached the native overlay as "?". The POSIX hook (curl --data-binary) was never affected. Read stdin as raw UTF-8 bytes, POST the body as bytes (sent verbatim on both 5.1 and pwsh), and for injectContext events pass the server's response bytes straight to stdout instead of decoding them and re-serializing via ConvertTo-Json. Adds a regression test on the Windows hook installer and a Cyrillic end-to-end test on the hook API. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Contributor
|
React Doctor found 8 new issues in 8 files · 8 warnings · score 49 / 100 (Critical) · 9 fixed · vs 8 warnings
Reviewed by React Doctor for commit |
…werShell
The PowerShell hook was only ever asserted on by substring, so nothing proved
the generated script parses or runs. Add tests that execute the generated
commands under every PowerShell on PATH (pwsh on the CI runners, plus Windows
PowerShell 5.1 on Windows) against a local server and compare the bytes that
cross the wire in both directions. They skip when no PowerShell is installed.
Running it surfaced a fail-soft gap: `catch {}` swallows the error but leaves
`$?` false, which `-Command` turns into exit code 1, so Claude reported a hook
error on every event whenever Mission Control was unreachable. End the script
with an explicit `exit 0`, matching the POSIX hook's `|| true`.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Contributor
Author
|
Merged. 🙏 Thanks |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes the AskUserQuestion overlay showing every Cyrillic character as
?(#130).Root cause. The overlay text travels from Claude Code to Mission Control through the hook command installed in
.claude/settings.local.json. On Windows that command runs under PowerShell, and Windows PowerShell 5.1 is not UTF-8 at either end:[Console]::In.ReadToEnd()decodes stdin with the console code page (OEM/ANSI), so the UTF-8 payload is mis-decoded before it is sent.-Bodyis encoded as ISO-8859-1, which replaces every code point outside Latin-1 (Cyrillic, CJK, emoji, en dash) with?.The POSIX hook (
curl --data-binary @-) and the server (request.text(), SSE viaTextEncoder) pass bytes through untouched, which is why the terminal behind the dialog rendered the same text correctly.Fix (
src/shared/agent-hooks.ts, PowerShell command only):StreamReaderoverOpenStandardInput().charset=utf-8; byte bodies are sent verbatim on both 5.1 and pwsh.injectContextevents (UserPromptSubmit, SessionStart) write the server's response bytes straight to stdout instead of theInvoke-RestMethoddecode (also ISO-8859-1 on 5.1) plusConvertTo-Jsonround trip. This matches the POSIX hook's verbatim passthrough.Tests
agent-hooks.test.ts: the Windows command reads stdin as UTF-8, posts a byte body, and passes response bytes through only for injectContext events.ask-user-question-api.test.ts: a Cyrillic AskUserQuestion payload posted as raw UTF-8 bytes survives intact through the hook endpoint and the question read endpoint.Review follow-up (second commit)
Authorization,Content-Type, escapedtaskId, and theinjectContextresponse on stdout. They skip when no PowerShell is installed. Verified under PowerShell 7.6.6 on macOS and underpwshon theubuntu-24.04CI runner (the tests ran there, not skipped).catch {}swallows the error but leaves$?false, which-Commandreports as exit code 1 — so Claude saw a hook error on every event whenever Mission Control was unreachable. The script now ends inexit 0, matching the POSIX hook's|| true. This predates the PR but lives in the code it rewrites.[Console]::In.ReadToEnd()mangles Cyrillic while the new raw-byte UTF-8 read returns it intact.MC_TASK_IDisEscapeDataString-escaped, the token stays in theAuthorizationheader and out of the URL and process args, and non-2xx responses never reach stdout (-ErrorAction Stop).Still not verified
Windows PowerShell 5.1 itself was not executed (no Windows host). The new tests pick up
powershell.exeautomatically on Windows, so running the suite there covers it. Only 5.1-compatible syntax is used (::new,Invoke-WebRequest -UseBasicParsing,RawContentStream).The failing Dependency Audit check is unrelated: it fails identically on
main(transitive advisories) and this PR changes no dependencies.Closes #130
🤖 Generated with Claude Code