What happened
The TUI renders entirely without color on native Windows shells — no status disc tints, no accent, no red exit codes. The same build on WSL is fully colored. This is not a downgrade to 16 colors; not a single escape sequence is emitted.
Cause is the first rule in the detector:
// packages/cli/src/tui-ansi.ts
const term = env.TERM ?? '';
if (term === 'dumb' || term === '') return 0;
PowerShell and cmd.exe do not set TERM at all, so an unset TERM is read as "colorless" before any other signal is considered. Git Bash and WSL do set it, which is exactly where color still works.
The capability is present — this is a detection bug, not a terminal limitation. Measured on the same machine (Windows 11, 10.0.26200, inside Windows Terminal):
| shell |
TERM |
tty.getColorDepth |
current level |
| PowerShell 5.1 |
unset |
24 (truecolor) |
0 |
| cmd.exe |
unset |
24 (truecolor) |
0 |
| Git Bash |
xterm-256color |
24 |
2 |
| WSL2 (Ubuntu) |
xterm-256color |
8 |
2 |
Node reports 24-bit support in the two shells where the TUI emits nothing.
Second reachable case
COLORTERM is ignored whenever TERM is unset, because the empty-TERM return happens first. A terminal that declares COLORTERM=truecolor without setting TERM gets level 0 on every platform, not just Windows.
How to reproduce
- Open PowerShell or cmd.exe (not Git Bash, not WSL) on Windows.
- Start the TUI from source.
- Every tool row, status disc, and diff renders as plain text.
Or, without a terminal, compare the built module under two env shapes:
TERM=xterm-256color → "�[32mX�[39m�[38;5;75mY�[39m…"
COLORTERM=truecolor → "�[38;2;87;163;239m…"
TERM unset → "XY●" ← no escapes at all
Environment
- Maka commit:
f19eede03
- OS: Windows 11 (10.0.26200), Windows Terminal
- Surface: TUI / CLI
- Node.js: v22.18.0
Logs, screenshots, or additional context
This contradicts the detector's own stated benchmark
The function documents what it is modelled on:
Benchmark: codex supports-color3-level; pitheme.ts 256 fallback.
supports-color never treats a missing TERM as colorless. It returns on platform before consulting TERM at all:
if (env.TERM === 'dumb') return min;
if (process.platform === 'win32') {
// Windows 10 build 10586 is the first Windows release that supports 256 colors.
// Windows 10 build 14931 is the first release that supports 16m/TrueColor.
const osRelease = os.release().split('.');
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
return Number(osRelease[2]) >= 14931 ? 3 : 2;
}
return 1;
}
// TERM / COLORTERM heuristics only after this
So the gap is a transcription one: the TERM ladder was carried over, the platform branch was not, and "TERM empty" became an early return 0 instead of falling through.
Node's built-in tty.WriteStream.getColorDepth() implements the same Windows build check, which is why it answers 24 where the hand-rolled ladder answers 0.
Suggested direction
Keep the two intent overrides (NO_COLOR, TERM=dumb) and the explicit truecolor upgrade, then ask the terminal instead of re-deriving capability from TERM:
NO_COLOR non-empty → 0
TERM=dumb → 0
COLORTERM=truecolor|24bit, or TERM ending -truecolor/-24bit → 3
- otherwise map
process.stdout.getColorDepth() — 24 → 3, 8 → 2, 4 → 1, else 0
- when there is no TTY to ask, fall back to today's
TERM ladder
This deletes the hand-maintained platform ladder rather than bolting a Windows special case onto it, and leaves Linux/macOS behavior unchanged. Adopting supports-color as a direct dependency would also work, but it carries third-party-notice obligations that the built-in does not.
packages/cli has no test for the detector today; it is a pure function over an env snapshot and is straightforward to pin.
Investigated with AI assistance (Claude Code); every number above measured on the machine described.
What happened
The TUI renders entirely without color on native Windows shells — no status disc tints, no accent, no red exit codes. The same build on WSL is fully colored. This is not a downgrade to 16 colors; not a single escape sequence is emitted.
Cause is the first rule in the detector:
PowerShell and cmd.exe do not set
TERMat all, so an unsetTERMis read as "colorless" before any other signal is considered. Git Bash and WSL do set it, which is exactly where color still works.The capability is present — this is a detection bug, not a terminal limitation. Measured on the same machine (Windows 11, 10.0.26200, inside Windows Terminal):
TERMtty.getColorDepthxterm-256colorxterm-256colorNode reports 24-bit support in the two shells where the TUI emits nothing.
Second reachable case
COLORTERMis ignored wheneverTERMis unset, because the empty-TERMreturn happens first. A terminal that declaresCOLORTERM=truecolorwithout settingTERMgets level 0 on every platform, not just Windows.How to reproduce
Or, without a terminal, compare the built module under two env shapes:
Environment
f19eede03Logs, screenshots, or additional context
This contradicts the detector's own stated benchmark
The function documents what it is modelled on:
supports-colornever treats a missingTERMas colorless. It returns on platform before consultingTERMat all:So the gap is a transcription one: the
TERMladder was carried over, the platform branch was not, and "TERMempty" became an earlyreturn 0instead of falling through.Node's built-in
tty.WriteStream.getColorDepth()implements the same Windows build check, which is why it answers 24 where the hand-rolled ladder answers 0.Suggested direction
Keep the two intent overrides (
NO_COLOR,TERM=dumb) and the explicit truecolor upgrade, then ask the terminal instead of re-deriving capability fromTERM:NO_COLORnon-empty → 0TERM=dumb→ 0COLORTERM=truecolor|24bit, orTERMending-truecolor/-24bit→ 3process.stdout.getColorDepth()— 24 → 3, 8 → 2, 4 → 1, else 0TERMladderThis deletes the hand-maintained platform ladder rather than bolting a Windows special case onto it, and leaves Linux/macOS behavior unchanged. Adopting
supports-coloras a direct dependency would also work, but it carries third-party-notice obligations that the built-in does not.packages/clihas no test for the detector today; it is a pure function over an env snapshot and is straightforward to pin.Investigated with AI assistance (Claude Code); every number above measured on the machine described.