Skip to content

fix(cli): switch Windows console to UTF-8 on startup - #236

Open
savvadesogle wants to merge 1 commit into
AtomicBot-ai:mainfrom
savvadesogle:fix/windows-console-utf8
Open

fix(cli): switch Windows console to UTF-8 on startup#236
savvadesogle wants to merge 1 commit into
AtomicBot-ai:mainfrom
savvadesogle:fix/windows-console-utf8

Conversation

@savvadesogle

Copy link
Copy Markdown

Summary

On Windows, switch the console code page to UTF-8 (65001) at CLI startup so Cyrillic (and other non-ASCII) input, output and copy/paste no longer turn into mojibake.

Motivation

The Windows console defaults to the CP866 OEM code page, while Node and atomic-agent emit/consume UTF-8. The mismatch corrupts Russian text in three places at once:

  • Input — what the user types is misread by readline;
  • Output — what the agent prints (reply/status lines) displays garbled;
  • Copy/paste — text copied from the terminal is pasted as mojibake (╨Я╤А╨╕╨▓╨╡╤В for "Привет").

Changes

  • src/cli/index.ts — add ensureUtf8Console(), called at the top of main(). On Windows only it runs chcp 65001 >nul (via execSync) and swallows failures (some hosts disallow changing the code page).

Design constraint honored here: the fix does not touch process.stdout / process.stderr. An earlier attempt that overrode write to emit raw UTF-8 bytes broke the interactive TUI (ink/readline frame rendering) — output lines "jumped" and the screen corrupted. Changing only the console code page fixes all three symptoms while leaving the stream mechanics untouched.

Verification

  • npm run lint — clean.
  • npm test — full suite: 20 failed / 5939 passed, identical to the pre-existing main baseline (same 20 environment-specific Windows failures; none introduced by this change).
  • Manual (Windows): after launch chcp reports 65001; Russian input, output and copy/paste are correct.

Notes

  • Orthogonal to the --use-env-proxy flag (documented separately); this change affects only the console code page.
  • The execSync is wrapped in try/catch because chcp can fail on hosts that forbid changing the code page (non-interactive sessions, some terminals).

plombeer31 added a commit that referenced this pull request Aug 25, 2026
…ee (#243)

Reported on Windows 10 / v0.4.1: "UI is shaking and some line broken".

**The shaking is frame tearing.** Ink paints by writing a whole frame to
stdout — cursor home, every line, the trailing clear. On a terminal that
renders as bytes arrive, the interval between the first line and the
last is an interval where the screen holds half of the old frame and
half of the new. That is not a Windows bug; macOS terminals just coalesce
hard enough to hide it, which is how a cross-platform behaviour arrived
as a Windows report.

DEC private mode 2026 is the fix the terminal side already implemented:
`CSI ? 2026 h` holds rendering until `CSI ? 2026 l`, so a frame appears
whole or not at all. Every frame is now bracketed in one write — one,
not three, because three would put the stream's own chunking between a
marker and the frame it is meant to bracket.

Sent unconditionally on a TTY, and safe to: an unrecognised DEC private
mode is ignored, which is the whole point of "private", and it is why
terminals without 2026 have taken these bytes from other TUIs for years.
No probe, for the same reason `alt-screen.ts` does not probe — asking
costs a round trip on a stdin something else is reading, and the
fallback is exactly today's behaviour. `ATOMIC_AGENT_NO_SYNC_OUTPUT=1`
opts out.

Installed by patching `stdout.write` rather than by handing Ink a
wrapper stream: Ink reads `columns`, `rows` and `resize` off that same
object, and a proxy that forwards those subtly wrong breaks resize
handling in a way far harder to see than tearing.

**And there was a repaint going nowhere.** `useRotatingPlaceholder` ran
its interval unconditionally, but the phrase is drawn only while the
composer is empty. From the first character typed, the timer went on
firing every four seconds — a `setState` at the root, a full Ink frame,
for a string nobody could see — for the rest of the session. It is now
gated on visibility, the same way `useSpinner` and `useAtomField`
already were. The cheapest frame is the one that is never painted, and
on a tearing terminal it is also the one that cannot flicker.

**The broken line is not this.** That is glyph width under a non-UTF-8
Windows console code page, which PR #236 (`fix(cli): switch Windows
console to UTF-8 on startup`) addresses directly. Deliberately left
alone here so the two do not collide.

I do not have a Windows 10 machine, so the tearing fix is reasoned and
unit-tested rather than observed. The exact bytes reaching the terminal
are asserted; whether Windows Terminal stops shaking needs someone on
Windows to run the branch.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant