fix: detect terminal width on Windows via parent console#331
Open
jcmecham wants to merge 1 commit intosirmalloc:mainfrom
Open
fix: detect terminal width on Windows via parent console#331jcmecham wants to merge 1 commit intosirmalloc:mainfrom
jcmecham wants to merge 1 commit intosirmalloc:mainfrom
Conversation
On Windows, getTerminalWidth() always returned null, so flex separators
collapsed and long-line truncation was disabled. Claude Code spawns the
statusline with piped stdio and no controlling PTY, so
process.stdout.columns is undefined; there's no columns field in the
statusline JSON and no COLUMNS env var to fall back on; and the
existing Unix ps/stty probe doesn't apply on Windows.
This change walks up the process tree to the claude.exe ancestor (which
owns the real console), attaches to that console via a PowerShell
P/Invoke helper, and reads the width from the Win32 console API. The
probe is invoked via spawnSync('powershell.exe', ['-EncodedCommand',
...]) to dodge cmd.exe argument truncation. Cold calls are ~3-5s
(PowerShell startup + Add-Type JIT); the result is cached at
%TEMP%/ccstatusline-win-width-<ppid>.json with a 60s TTL so warm reads
hit disk in ~5ms.
When ccstatusline runs interactively (the TUI launched via
bunx ccstatusline), the process owns its own console and AttachConsole
fails against every ancestor. Since null probe results aren't cached,
every keystroke previously respawned PowerShell. In TUI mode
process.stdout is a real TTY with columns populated, so the Windows
branch returns process.stdout.columns directly and skips the probe.
The "Terminal width detection is currently unavailable in your
environment" warning in the Items Editor has been removed — it existed
to explain the broken Windows behavior.
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
On Windows,
getTerminalWidth()always returnednull, so flex separators collapsed and long-line truncation was disabled. Claude Code spawns the statusline with piped stdio and no controlling PTY, soprocess.stdout.columnsis undefined; there's nocolumnsfield in the statusline JSON and noCOLUMNSenv var to fall back on; and the existing Unixps/sttyprobe doesn't apply on Windows.This PR makes width detection work on Windows by walking up the process tree to the
claude.exeancestor (which owns the real console), attaching to that console via a PowerShell P/Invoke helper, and reading the width from the Win32 console API.Performance:
The probe runs synchronously inside ccstatusline (not on Claude Code's main thread, so the REPL is unaffected). Cold call: ~5s (PowerShell startup +
Add-TypeJIT). The result is cached at%TEMP%/ccstatusline-win-width-cache.jsonwith a 60s TTL, so subsequent status-line renders read from disk in ~5ms. Trade-off: up to 60s of resize lag before the cache refreshes — acceptable for a status line.Changes
src/utils/terminal.tsgetTerminalWidth(). Shells out to the probe, caches the result at%TEMP%/ccstatusline-win-width-<ppid>.jsonwith a 60s TTL, returnsnullon any failure (no regression). In TUI mode (stdout is a real TTY) it returnsprocess.stdout.columnsdirectly and skips the probe. Non-Windows paths untouched.scripts/windows-width-probe.ps1(new)CreateToolhelp32Snapshot,AttachConsoles to the owning process, readsCONSOLE_SCREEN_BUFFER_INFO.dwSize.X. Invoked viaspawnSync -EncodedCommandto avoidcmd.exearg truncation.src/tui/components/ItemsEditor.tsxsrc/utils/__tests__/terminal.test.tsprocess.platformexplicitly so they pass on Windows dev boxes.package.jsonbuildscript copies the probe intodist/so the published package ships it.Test plan
bun run vitest run src/utils/__tests__/terminal.test.ts— 12 tests pass.bun run lint— clean for touched files.bun run build, pointstatusLine.commandatdist/ccstatusline.js, add a Flex Separator, resize Windows Terminal — line spans full width, reflows on resize within the 60s TTL.