roundel/policy
Every export of roundel/policy, with its signature and doc comment: colorLevel, outputMode, flown, plus 7 types.
The output policy — one answer to "where is this output going?", read from a
Runtime, never from process (R1, R2, R9 of roundel; U2 of the stack).
Five colour libraries disagreeing about the terminal (clack #286) is what this file
exists to end: every package in the family asks here, and nothing else in it reads
isTTY, NO_COLOR, FORCE_COLOR, CI, CLI_ACCESSIBLE or a --color flag. It also
holds the one record fly() writes and every token reads, because it is the only module
every subpath may import (R7).
The code here is written tight — ternaries where a reader might expect statements —
because ./chalk reaches this file and R8 caps that whole graph at chalk 6's own
9,370 bytes. Comments are stripped from dist, so the prose is free; the statements
are not.
import { colorLevel, outputMode, flown } from 'roundel/policy';Functions
colorLevel
chalk's level, obeying the user's explicit instruction in any mode (R2 revised
2026-09-08): NO_COLOR wins outright; then FORCE_COLOR=0, which supports-color settles
before it looks at any flag, so FORCE_COLOR=0 --color=256 is off and not 2 — an
explicit "colour off" is never undone by a level flag; then the --color flags in
argv, an exact --color=256 beating a numeric FORCE_COLOR as chalk's own suite says;
then FORCE_COLOR. --json is the one output the level never enters: structured text
carries no escapes. The mode decides redraws (U2), never the level.
Accessible mode defaults to 0, on the same footing as a pipe rather than a terminal:
CLI_ACCESSIBLE is itself an explicit instruction from a human, and ANSI colour is noise
to a screen reader. As with a pipe, an explicit colour ask (FORCE_COLOR, --color=…)
still wins and NO_COLOR still beats everything.
With no instruction the order is supports-color's own, and deliberately so — chalk's
level.js asserts it, and a family that disagreed with chalk about a bare pipe would be
the clack #286 bug again. A pipe is 0, because a pipe nobody asked to colour is a file or
another program's stdin; the one exception is Azure Pipelines (TF_BUILD and
AGENT_NAME — TF_BUILD alone is a build without an agent), which supports-color reads
before it gives up on a pipe. Once colour is being detected — a terminal, or a run that
asked — TERM=dumb is the floor, a CI run is its vendor's level (gated on 'CI' in env
as supports-color gates it, so an empty CI= still selects the table), and anything else
is what TERM/COLORTERM report. So the CI user who exports FORCE_COLOR=true to get
coloured logs gets their runner's colours, and nobody else's pipe changes.
ponytail: supports-color also consults the platform, TEAMCITY_VERSION, TERM_PROGRAM and the emulator allow-list; R2 refuses that detection, so those stay unread.
function colorLevel(rt: Runtime, opts?: ModeOptions): ColorLevel;| Parameter | Type |
|---|---|
rt | Runtime |
opts (optional) | ModeOptions |
Returns ColorLevel
Constants
flown
What fly() decided, once, for the process: the colour level and each token's paint.
Every token reads it; nothing writes it but fly(). Until then the level is 0 and every
token is the identity, so a program that never declares its runtime prints plain text.
const flown: {
level: ColorLevel;
paint: Partial<Record<TokenName, Paint>>;
};outputMode
First match wins: json if asked; accessible if CLI_ACCESSIBLE; ci if CI and not
a TTY; pipe if not a TTY; else tty.
const outputMode: (rt: Runtime, opts?: ModeOptions) => OutputMode;Interfaces
ModeOptions
interface ModeOptions {
/** Whether this run was asked for `--json`: the engine's knowledge, not the process's. */
json?: boolean;
}Runtime
The slice of a runtime the policy needs. burgee's processRuntime satisfies it, so does
a two-line literal in a test; nothing here imports a type from anywhere.
interface Runtime {
env: Record<string, string | undefined>;
isTTY: {
stdout: boolean;
};
/**
* The process arguments, when the caller owns them: `--color`, `--no-color` and
* `--color=…` are read in `policy.ts` and nowhere else. A test literal leaves it out.
*/
argv?: readonly string[];
}Types
ColorLevel
chalk's levels: none, 16 colours, 256 colours, truecolor.
type ColorLevel = 0 | 1 | 2 | typeof MAX_LEVEL;Format
One util.styleText format name: 'bold', 'red', 'redBright', 'dim'…
type Format = import('node:util').InspectColor;OutputMode
type OutputMode = 'tty' | 'pipe' | 'json' | 'accessible' | 'ci';Paint
A style as a token paints it: styleText format names, or the SGR parameters of one
foreground colour (38;5;n for 256 colours, 38;2;r;g;b for truecolor), which
styleText cannot express.
type Paint = readonly Format[] | {
readonly sgr: readonly number[];
};TokenName
type TokenName = 'error' | 'warn' | 'ok' | 'hint' | 'muted' | 'command' | 'flag' | 'value' | 'heading';