fix(cli): ask the terminal for color support instead of inferring it from TERM - #3537
Conversation
…from TERM PowerShell and cmd.exe set no TERM at all, and the detector returned 0 for an unset TERM before considering anything else — so the TUI emitted no ANSI at all on native Windows shells while WSL and Git Bash stayed coloured. The consoles do support truecolor; only the detection was wrong. The same early return also swallowed an explicit COLORTERM whenever TERM was unset, on every platform. The function's own benchmark, `supports-color`, avoids this by returning on process.platform === 'win32' before it reads TERM. Node's getColorDepth() already implements that check, so ask it rather than keeping a second copy of the ladder. NO_COLOR, TERM=dumb and the explicit truecolor upgrade still win, and piped output — which has no stream to ask — keeps the old TERM ladder. Generated-by: Claude Code Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Fixes CLI TUI color detection by using terminal-reported color depth while preserving existing environment fallbacks.
Changes:
- Adds
stdout.getColorDepth()detection. - Preserves
NO_COLOR,TERM=dumb, truecolor, and legacy fallback behavior. - Adds focused detection tests.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
packages/cli/src/tui-ansi.ts |
Uses terminal-reported color depth and retains fallback logic. |
packages/cli/src/__tests__/tui-ansi.test.ts |
Tests detection precedence, depth mapping, and fallback behavior. |
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Reviewed at The problem is real and the diagnosis is precise: PowerShell and What I want to call out, because it is the part most likely to be undervalued in a diff this size: this change removes a duplicated capability ladder rather than adding a special case. The obvious fix here is The precedence order also survives scrutiny. The tests pin each of those branches, including the two that are easy to get backwards — [P3] The function name no longer describes what it does.
On CI: no checks have been reported on this head, so there is no CI evidence for it yet. Nothing looks wrong with the branch; it just has not run. Worth a maintainer kicking it off. One thing I could not verify from here: whether any terminal that sets |
Astro-Han
left a comment
There was a problem hiding this comment.
CI is now terminal green on 287915d3d3ad0c3e3197466b5b285f98a2a9e6d1 (test: completed / success), which matches the exact head this review is bound to.
Re-confirming the earlier review conclusion on this head: querying the terminal for color support instead of inferring it from TERM removes a heuristic rather than adding a layer on top of one — the TERM string stops being a second authority on capability. Scope is small and the change is contained.
Approving.
Summary
detectColorLevelFromEnvreturned 0 for an unsetTERMbefore considering any other signal. PowerShell and cmd.exe set noTERMat all, so the TUI emitted no ANSI whatsoever on native Windows shells while WSL and Git Bash stayed coloured. The same early return also swallowed an explicitCOLORTERM=truecolorwheneverTERMwas unset — on every platform, not just Windows.The function's stated benchmark already handles this.
supports-colorreturns onprocess.platform === 'win32'(by OS build) before it readsTERM; the transcription kept theTERMladder and dropped the platform branch. Node'stty.WriteStream.getColorDepth()implements the same Windows build check, so this asks the terminal instead of maintaining a second copy of the ladder.Order is now:
NO_COLOR→TERM=dumb→ explicit truecolor declaration → reported depth → (no terminal to ask) the pre-existingTERMladder. Linux and macOS behaviour is unchanged; the fallback keeps piped output exactly as it was.Fixes #3536
Verification
End-to-end in a real pty with
TERMunset — the case the bug is about:24-bit output where there was previously not a single escape sequence.
Measured capability on this machine (Windows 11 10.0.26200, Windows Terminal), showing the detector disagreeing with the terminal:
TERMgetColorDepthxterm-256colorxterm-256colorNew
packages/cli/src/__tests__/tui-ansi.test.ts(5 tests) pins the matrix — there was no test for this function before. Checked against a plausible partial fix that adds the depth probe but keeps the empty-TERMearly return: 2 of the 5 go red (expected: 3, actual: 0), including theCOLORTERMcase.Ran: the new suite plus every existing consumer of
tui-ansi(pi-goal,pi-tui-transcript-viewer,pi-transcript) — 84 green;biome checkon both files;npm run check:asf-headers;tsc -p packages/cli.Not run: the rest of the CLI suite, Desktop, Playwright. PowerShell 7 was not available on this machine, so only PowerShell 5.1 and cmd.exe were measured natively.
Review focus
getColorDepthexists only ontty.WriteStream, soprocess.stdout.getColorDepth?.()isundefinedfor piped output. That path deliberately falls through to the originalTERMladder rather than to 0, which keeps redirected output and the existing test environment byte-identical. Making non-TTY output colourless would be defensible, but it is a separate behaviour change and nothing inpackages/clireachestui-ansifrom a non-TUI path today.AI use
Select exactly one:
Tool(s) and scope: Claude Code — investigation, the measurements above, the test, and the fix.
Checklist
Does this PR entail a change in behavior?