Skip to content

fix: detect terminal width on Windows via parent console#331

Open
jcmecham wants to merge 1 commit intosirmalloc:mainfrom
jcmecham:fix/windows-terminal-width
Open

fix: detect terminal width on Windows via parent console#331
jcmecham wants to merge 1 commit intosirmalloc:mainfrom
jcmecham:fix/windows-terminal-width

Conversation

@jcmecham
Copy link
Copy Markdown
Contributor

@jcmecham jcmecham commented Apr 22, 2026

Summary

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 PR makes width detection work on Windows by walking up the process tree to the claude.exe ancestor (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-Type JIT). The result is cached at %TEMP%/ccstatusline-win-width-cache.json with 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

File Change
src/utils/terminal.ts Windows branch added to getTerminalWidth(). Shells out to the probe, caches the result at %TEMP%/ccstatusline-win-width-<ppid>.json with a 60s TTL, returns null on any failure (no regression). In TUI mode (stdout is a real TTY) it returns process.stdout.columns directly and skips the probe. Non-Windows paths untouched.
scripts/windows-width-probe.ps1 (new) PowerShell + C# P/Invoke probe. Walks PPIDs with CreateToolhelp32Snapshot, AttachConsoles to the owning process, reads CONSOLE_SCREEN_BUFFER_INFO.dwSize.X. Invoked via spawnSync -EncodedCommand to avoid cmd.exe arg truncation.
src/tui/components/ItemsEditor.tsx Removed the "Terminal width detection is currently unavailable in your environment" warning — it existed to explain the broken Windows behavior this PR resolves.
src/utils/__tests__/terminal.test.ts New tests for probe parsing, failure modes, and cache-hit behavior. Existing Unix tests now mock process.platform explicitly so they pass on Windows dev boxes.
package.json build script copies the probe into dist/ 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.
  • Manual: bun run build, point statusLine.command at dist/ccstatusline.js, add a Flex Separator, resize Windows Terminal — line spans full width, reflows on resize within the 60s TTL.
  • Manual: open the Items Editor in the TUI and hold down-arrow — instant response, no multi-second lag.

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.
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.

2 participants