Repository navigation
[CLAUDE ROUTINE]: CX enhancement — honor NO_COLOR (and add --no-color) so CLI output reads cleanly in CI logs and non-ANSI terminals #251
Description
Summary
The interactive installer and hook warnings emit raw ANSI escape codes (\x1B[33m…, \x1B[?25l, etc.) without checking whether the destination terminal can render them. Customers piping failproofai policies --install into a logger, running it inside a CI container, or using a screen reader currently see literal [33m characters mixed into messages.
Adopting the well-known NO_COLOR convention (plus a --no-color flag for parity) gives users a clean fallback while keeping the lovely interactive UX for everyone else.
Where
Heaviest concentration of escapes:
src/hooks/install-prompt.ts— lines 63, 69, 113, 119, 155, 161-164, 173, 260, 267, 359-440 (selector UI, status warnings, cursor hide/show)- Various
hookLogWarn/hookLogInfocallsites (lib/logger.ts) - The
\x1B[?25l/\x1B[?25hcursor-hide pair will leave a CI terminal with no cursor if the process exits before\x1B[?25hruns
Today there is no central helper, so colour decisions are scattered as inline literals.
Why this helps users
journey
title `failproofai policies --install` in different terminals
section TTY w/ colour (today)
Run command: 5: User
See styled selector: 5: User
section CI / piped (today)
Run command: 3: User
See literal '[33m...': 1: User
Grep logs for warnings: 2: User
section CI w/ NO_COLOR (proposed)
Run command: 5: User
See plain readable text: 5: User
Grep logs cleanly: 5: User
- CI ergonomics — GitHub Actions, Docker logs, Datadog log views render plain ASCII much better than escape soup.
- Accessibility — screen readers (especially on Windows + WSL) handle plain text predictably.
- Industry-standard contract —
NO_COLOR=1is the de facto convention; supporting it is one less surprise. - Defensive safety — wraps
\x1B[?25l(hide cursor) in a single helper that always restores on exit, avoiding the "invisible cursor" complaint that occasionally hits Ctrl-C users.
Proposed enhancement
-
Tiny helper in
lib/logger.ts(or a newsrc/cli-color.ts):const colorEnabled = (() => { if (process.env.NO_COLOR) return false; if (process.env.FORCE_COLOR) return true; if (process.argv.includes("--no-color")) return false; return process.stdout.isTTY === true; })(); export const c = { yellow: (s: string) => colorEnabled ? `\x1B[33m${s}\x1B[0m` : s, dim: (s: string) => colorEnabled ? `\x1B[2m${s}\x1B[0m` : s, cyan: (s: string) => colorEnabled ? `\x1B[36m${s}\x1B[0m` : s, // … green, magenta, bold, reverse }; export function hideCursor() { if (colorEnabled) process.stdout.write("\x1B[?25l"); } export function showCursor() { if (colorEnabled) process.stdout.write("\x1B[?25h"); }
-
Sweep
install-prompt.tsonce and replace inline\x1B[..mwithc.yellow(...),c.dim(...), etc. -
When
colorEnabled === false, also bypass the alternate-screen / line-redraw logic and fall back to a single non-interactive prompt — a TTY-less environment can't redraw anyway. -
Document
NO_COLORand--no-colorinfailproofai --helpoutput andREADME.md → Configuration → Environment variables.
Acceptance criteria
-
NO_COLOR=1 failproofai policies --installproduces no escape sequences in stdout/stderr. -
--no-colorflag behaves identically. -
process.stdout.isTTY === false(piped output) auto-disables colour. - Cursor is restored on exit even when interrupted (
process.on("exit", showCursor)). - Snapshot test of
install-promptrendering inNO_COLORmode. -
CHANGELOG.mdentry under Unreleased → Features.
Severity
Low–medium — every CI user sees this. The fix is mechanical and removes a recurring papercut.
Activity
NiveditJain commentedon May 1, 2026
Tracked by open PR #256 (
no-color cli output), which adds both theNO_COLORsetting and the--no-colorflag described here. Leaving this issue open — it will auto-close when #256 merges.