cli — CLI Output Formatting
CLI Output Formatting
Structured I/O layer for machine-readable output. Provides NDJSON streaming for MCP and plugin integrations, normalized exit points, pretty-printing for human users, and remote output routing for headless sessions.
🔵 Structured I/O 🟢 NDJSON 🟡 Exit Codes 🔴 Remote Routing
📚 What is this?
The cli module is the output formatting layer that sits between Claude Code's internal logic and the outside world. Whether the consumer is a human reading coloured terminal text or a machine parsing NDJSON lines, this module ensures consistent, predictable output with well-defined exit semantics.
💡 Analogy
Think of the CLI module as a multilingual interpreter at a press conference. The speaker (internal logic) says the same thing, but the interpreter (structuredIO) translates it into NDJSON for robots, print formats it with colours for journalists, and remoteIO relays it over a radio link to reporters who couldn't attend in person. Meanwhile exit is the protocol officer ensuring everyone leaves through the right door with the right badge.
Architecture
flowchart LR
subgraph Core["Internal Logic"]
CMD["Command Result"]
ERR["Error Object"]
end
subgraph CLI["cli Module"]
EXIT["exit.ts"]
SIO["structuredIO.ts"]
PRINT["print.ts"]
RIO["remoteIO.ts"]
end
subgraph Outputs["Output Targets"]
STDOUT["stdout (human)"]
NDJSON["NDJSON stream"]
REMOTE["Remote socket"]
end
CMD --> EXIT
ERR --> EXIT
EXIT -->|cliOk| SIO
EXIT -->|cliOk| PRINT
EXIT -->|cliError| SIO
EXIT -->|cliError| PRINT
SIO --> NDJSON
PRINT --> STDOUT
RIO --> REMOTE
SIO --> RIO
Data Flow
flowchart TD
INPUT["Command Execution"] --> RESULT{"Success?"}
RESULT -->|yes| OK["cliOk(data)"]
RESULT -->|no| FAIL["cliError(err)"]
OK --> NORM["Normalize Payload"]
FAIL --> NORM
NORM --> MODE{"Output Mode?"}
MODE -->|interactive| PRETTY["print.ts → ANSI"]
MODE -->|json| STRUCT["structuredIO → NDJSON"]
MODE -->|remote| RELAY["remoteIO → socket"]
PRETTY --> STDOUT["process.stdout"]
STRUCT --> STDOUT
RELAY --> SOCK["WebSocket / pipe"]
NORM --> CODE["Exit Code"]
CODE --> EXIT["process.exit()"]
🚶 Step-by-Step Walkthrough: NDJSON output for an MCP tool call
Trace a tool call result from internal execution to NDJSON output:
- Tool execution — An MCP tool finishes and returns a result object.
- cliOk() —
exit.tswraps the result with exit code 0 and metadata. - Payload normalization — Timestamps, request IDs, and version info are injected.
- Output mode detection — The
--output jsonflag routes tostructuredIO. - NDJSON serialization — Each message is JSON.stringify'd and terminated with
\n. - stdout.write() — The NDJSON line is flushed to stdout in a single write.
- Remote relay — If a remote listener is attached,
remoteIOalso forwards the line. - Exit code — On final output,
process.exit(0)is called for success.
Key Classes & Functions
| Symbol | File | Purpose |
|---|---|---|
cliOk() | exit.ts | Normalized success exit with data payload |
cliError() | exit.ts | Normalized error exit with error code and message |
exitProcess() | exit.ts | Controlled process.exit with cleanup hooks |
StructuredIO | structuredIO.ts | NDJSON writer with line buffering and flush control |
formatNDJSON() | structuredIO.ts | Single-line JSON serializer with error fallback |
prettyPrint() | print.ts | ANSI-coloured human-readable output formatter |
formatTable() | print.ts | Auto-aligned table renderer for list output |
RemoteIO | remoteIO.ts | Forwards output to remote listeners over sockets |
OutputRouter | router.ts | Routes output to correct sink based on mode flags |
parseOutputMode() | flags.ts | Reads --output flag and env vars to pick mode |
ProgressReporter | progress.ts | Streaming progress bar for long operations |
ErrorFormatter | errorFormat.ts | Converts Error objects to structured output |
Directory Map
Source tree for cli
cli/
├── exit.ts ← cliOk(), cliError(), exitProcess()
├── structuredIO.ts ← NDJSON writer with buffering
├── print.ts ← ANSI pretty-print for humans
├── remoteIO.ts ← Remote output relay over sockets
├── router.ts ← Output mode routing logic
├── flags.ts ← --output flag parsing
├── progress.ts ← Streaming progress indicators
├── errorFormat.ts ← Error → structured payload converter
├── types.ts ← Shared interfaces (CliResult, etc.)
├── constants.ts ← Exit codes, format strings
├── __tests__/
│ ├── exit.test.ts
│ ├── structuredIO.test.ts
│ ├── print.test.ts
│ └── remoteIO.test.ts
└── index.ts ← Barrel export
Configuration
| Option | Default | Description |
|---|---|---|
--output | text | Output mode: text (human), json (NDJSON), silent |
CLAUDE_OUTPUT_FORMAT | text | Env var override for output mode |
NO_COLOR | unset | When set, disables ANSI colour codes in text mode |
NDJSON_BUFFER_SIZE | 64 | Max buffered NDJSON lines before forced flush |