A customizable status line formatter for Claude Code CLI. Reads JSON session data from stdin, renders an ANSI-colored status line, and outputs to stdout.
- Single static binary with no runtime dependencies beyond
gitfor the git widgets - 61 configurable widgets (model, tokens, context, git, cost, rate limits, prompt cache, and more)
- Custom subagent rows in Claude Code's agent panel (
subagentStatusLine) - Multi-line status line with flex separator layout
- Threshold colors (e.g. context % turning yellow, then red) and progress bars
- ANSI 16-color support via fatih/color
- Configurable via
~/.config/ccstatus/settings.json - Automatic Claude Code integration via
install/uninstallcommands
brew install moond4rk/tap/ccstatusgo install github.com/moond4rk/ccstatus/cmd/ccstatus@latest# Generate default settings
ccstatus init
# Register in Claude Code (re-run anytime to update; --subagent / --refresh N / --padding N / --hide-vim-indicator optional)
ccstatus install
# Try your layout against sample data, without starting Claude Code
ccstatus previewThat's it. Claude Code will now display the status line after each assistant message.
ccstatus [flags]
ccstatus [command]
| Command | Description |
|---|---|
init |
Generate default settings.json at the ccstatus config directory |
install |
Register ccstatus as the status line (and, with --subagent, subagent row) command in Claude Code's settings.json |
uninstall |
Remove the blocks that run ccstatus from Claude Code's settings.json |
validate |
Validate settings.json for correctness |
preview |
Render your settings against sample data (or --input FILE) |
subagent |
Render subagent rows for Claude Code's subagentStatusLine (Claude Code runs it) |
dump |
Save raw JSON input from Claude Code to a file for debugging |
widgets |
List all registered widget types with descriptions, default colors and prefixes |
completion |
Generate shell autocompletion script (bash, zsh, fish, powershell) |
| Flag | Description |
|---|---|
-h, --help |
Show help for any command |
-v, --version |
Print version |
ccstatus init [--force]Generates a default settings.json at ~/.config/ccstatus/settings.json (under $XDG_CONFIG_HOME when that is set). Use --force to overwrite an existing file.
ccstatus install [--subagent] [--refresh N] [--padding N] [--hide-vim-indicator]Registers ccstatus in Claude Code's ~/.claude/settings.json (or $CLAUDE_CONFIG_DIR/settings.json). A fresh install adds:
{
"statusLine": {
"type": "command",
"command": "ccstatus",
"padding": 0,
"refreshInterval": 30
}
}Every other key keeps its position and value, the file is replaced atomically, and a symlinked settings.json (for example one managed by a dotfiles repo) stays a symlink. Re-running install keeps the padding, refreshInterval and hideVimModeIndicator you set by hand and points command back at ccstatus, which also ends a temporary ccstatus dump.
Optional flags:
--subagent— also render subagent rows in the agent panel (writessubagentStatusLinerunningccstatus subagent;--subagent=falseremoves it). See Subagent Rows.--refresh N— re-run the status line everyNseconds (writesrefreshInterval). A fresh install uses30so time-based widgets (session-clock,block-timer,prompt-cache) and git widgets keep moving while Claude Code is idle;--refresh 0removes it so the status line runs only on Claude Code's events.--padding N— extra horizontal spacing, in characters, that Claude Code adds to the status line (writespadding; a fresh install uses0).--hide-vim-indicator— suppress Claude Code's built-in-- INSERT --line (writeshideVimModeIndicator;--hide-vim-indicator=falseremoves it). Set this if you use thevim-modewidget so the mode isn't shown twice.
ccstatus uninstallRemoves the statusLine and subagentStatusLine blocks whose command runs ccstatus. A block that runs another command is reported and left alone.
ccstatus validateChecks settings.json for JSON errors (reported as path:line:column), missing or unknown widget types, missing or duplicate widget IDs, invalid color names (threshold and override colors included), an unknown flexMode, colorLevel outside 0–3, compactThreshold outside 0–100, a negative maxWidth, metadata.display values and thresholds the widget does not support, and widgets on the wrong kind of line (task-* widgets belong in subagentLine). Exits non-zero when any problem is found, so it works as a CI gate.
ccstatus dump [-o|--output FILE] [--append]Reads JSON from stdin (same as normal mode), saves it to a file, and renders the status line. Default output: /tmp/ccstatus-dump.json. Useful for inspecting what Claude Code sends. With --append, every invocation adds one JSON line — {"ts", "columns", "lines", "input"} — to /tmp/ccstatus-dump.jsonl (or --output), so a whole session can be captured.
ccstatus preview [--input FILE] [--width N] [--subagent]Renders your current settings.json against the full sample payload from the official docs, with its timestamps moved to now, so you can try a layout without starting Claude Code. --input renders a payload of your own (for example a dump capture); --width N renders as if the terminal were N columns wide (the flex-mode margin still applies); --subagent renders subagentLine for a sample task list, at the sample's columns unless --width is set.
ccstatus subagentThe subagentStatusLine command (set up by ccstatus install --subagent). Claude Code pipes the visible subagents as {"columns", "tasks": [...]}; ccstatus renders subagentLine for each task and prints one {"id", "content"} JSON line per row. Rows that render empty, and every row on any error, are left out so Claude Code keeps its default rendering.
ccstatus widgetsLists all registered widget types with their descriptions, default colors and default prefixes.
# Default: read JSON from stdin, render status line
echo '{"model":{"id":"claude-opus-4-7","display_name":"Opus"}}' | ccstatus
# Replay captured dump data
cat /tmp/ccstatus-dump.json | ccstatus
# Preview the configured layout against sample data in a 120-column terminal
ccstatus preview --width 120Settings are stored at ~/.config/ccstatus/settings.json (or $XDG_CONFIG_HOME/ccstatus/settings.json). Edit manually to customize.
A lean 2-line layout. Widgets with nothing to show — effort, fast-mode, git-worktree, rate-limits, session-name — are dropped, so a plain session collapses to model | Ctx% | branch | +/- | $ over cwd | Session.
Line 1: model · effort | Fast | context % | git branch + worktree | lines +/- | session cost (Fast only while fast mode is on)
Line 2: working directory | rate limits (5h/7d and the gateway spend limit, whichever are present) | session clock | session name (the name is capped at 30 columns)
Context % and the subagent task-context share one color scale: gray below 30%, white from 30%, yellow from 60% and red from 90%. Rate limits follow the same steps a shade brighter: white, then bright white from 30%.
Opus 4.7 (1M context) · max | Fast | Ctx: 36% | main feat-x | +12 -3 | $0.34
~/dev/myproj | Limit 5h: 3% / 7d: 12% | Session: 2h15m | Fix login redirect
Sonnet | Ctx: 12% | main | +4 | $0.05
~/dev/myproj | Session: 30m
To customize, edit the lines array in settings.json — e.g. swap rate-limits for the per-window rate-limit-5h (metadata.display: bar/reset/full), or add prompt-cache / cache-hit-rate. Fields you leave out of settings.json keep their defaults. (If line 1 is getting truncated with ..., see terminalWidth below.)
{
"version": 4,
"colorLevel": 2,
"flexMode": "full-until-compact",
"compactThreshold": 90,
"defaultSeparator": "|",
"defaultPadding": " ",
"inheritSeparatorColors": false,
"globalBold": false,
"lines": [
[
{ "id": "1", "type": "model", "color": "cyan" },
{ "id": "2", "type": "separator" },
{
"id": "3",
"type": "context-percentage",
"color": "brightBlack",
"thresholds": [
{ "gte": 30, "color": "white" },
{ "gte": 60, "color": "yellow" },
{ "gte": 90, "color": "red" }
]
}
]
]
}| Field | Type | Default | Description |
|---|---|---|---|
version |
int | 4 |
Config schema version |
colorLevel |
int | 2 |
0 disables color; 1–3 all enable ANSI 16-color output |
flexMode |
string | "full-until-compact" |
Right margin each main line leaves: where the line is cut with ... and how far flex-separator fills (see Flex Modes) |
compactThreshold |
int | 90 |
Context % (0–100) at which full-until-compact switches to the wider (−40 col) right margin |
terminalWidth |
int | (auto) | Override the detected terminal width; the flexMode margin still applies. Leave unset/0 to auto-detect: ccstatus reads the COLUMNS variable that Claude Code (v2.1.153+) sets for status line commands, then falls back to the attached terminal, then 80. |
defaultSeparator |
string | "|" |
Text a separator item shows unless it sets character |
defaultPadding |
string | " " |
Inserted between adjacent widgets, separators included, except after an item with merge |
inheritSeparatorColors |
bool | false |
A separator without its own color takes the color of the widget before it |
overrideForegroundColor |
string | (none) | Force one foreground color on every widget and separator, thresholds included |
overrideBackgroundColor |
string | (none) | Force one background color the same way |
globalBold |
bool | false |
Apply bold to all widgets |
| Field | Type | Description |
|---|---|---|
id |
string | Unique identifier, across lines and subagentLine |
type |
string | Widget type (see list below) |
color |
string | Foreground color name |
backgroundColor |
string | Background color name |
bold |
bool | Bold text |
prefix |
string | Text before widget output; empty keeps the widget's default, such as Ctx: (ccstatus widgets lists them) |
suffix |
string | Text after widget output |
character |
string | The text of a separator, or an icon before the git-branch name |
rawValue |
bool | Use compact/raw output mode |
maxWidth |
number | Cap the widget's text at this many columns, ending it with … (the prefix and suffix are not counted; wide characters count as two) |
merge |
bool/string | Join this widget to the next one without defaultPadding (true, or the legacy "no-padding") |
metadata |
object | Widget-specific options as strings, such as { "display": "bar", "barWidth": "20" }; barWidth sizes every bar display |
customText |
string | The text of custom-text |
commandPath |
string | Shell command for custom-command, run with sh -c and given Claude Code's JSON on stdin; the first line of its output is shown |
timeout |
number | custom-command timeout in milliseconds (default 3000) |
preserveColors |
bool | Keep the ANSI colors in custom-command output |
thresholds |
array | Restyle a numeric widget by value: [{ "gte": 70, "color": "yellow" }, { "gte": 90, "color": "red", "bold": true }]. The highest gte reached wins; below the first, color applies. Each entry may set color, backgroundColor, bold |
Thresholds work on widgets that render a number: the context and remaining percentages, cache-hit-rate, the rate-limit widgets (rate-limits uses the highest of its limits), spend-limit, prompt-cache (minutes until cold, or the hit percentage in hit mode), session-cost, context-length, the token counts, and task-tokens / task-context in subagentLine. ccstatus validate flags thresholds on any other widget.
| Mode | Description |
|---|---|
full |
Terminal width - 10 characters |
full-minus-40 |
Terminal width - 40 characters |
full-until-compact |
Terminal width - 10, switching to - 40 once context % >= compactThreshold (default) |
With ccstatus install --subagent, Claude Code runs ccstatus subagent for the agent panel below the prompt, and each subagent row renders subagentLine — one row of widget items, configured like a line in lines. When subagentLine is absent, this default applies:
"subagentLine": [
{ "id": "s1", "type": "task-name", "color": "cyan" },
{ "id": "s2", "type": "task-status" },
{ "id": "s3", "type": "task-elapsed" },
{ "id": "s4", "type": "separator" },
{ "id": "s5", "type": "task-model" },
{ "id": "s6", "type": "task-effort", "prefix": "· " },
{ "id": "s7", "type": "task-tokens" },
{ "id": "s8", "type": "task-context", "color": "brightBlack", "thresholds": [{ "gte": 30, "color": "white" }, { "gte": 60, "color": "yellow" }, { "gte": 90, "color": "red" }] },
{ "id": "s9", "type": "task-sparkline" },
{ "id": "s10", "type": "separator" },
{ "id": "s11", "type": "task-label" }
]reviewer running 1m23s | Sonnet 5.5 · high 45.2k 23% ▅▄▆▇██▇ | Reading internal/auth/session.go
Explore completed | Haiku 5.5 · 16k 18.4k 9% | Find every caller of Session.Refresh
Rows are sized to the width Claude Code reports, with no flex-mode margin. subagentLine takes the task-* widgets plus separator, flex-separator and custom-text; ccstatus validate flags anything else, and task-* widgets on the main lines. Try it with ccstatus preview --subagent.
8 basic: black, red, green, yellow, blue, magenta, cyan, white
8 bright: brightBlack, brightRed, brightGreen, brightYellow, brightBlue, brightMagenta, brightCyan, brightWhite
| Widget | Source | Description | Default Color |
|---|---|---|---|
model |
JSON | Current Claude model name | cyan |
version |
JSON | Claude Code version | white |
session-id |
JSON | Session ID (8-char short) | white |
session-name |
JSON | Session name (--name / /rename, or the AI-generated title) |
white |
session-cost |
JSON | Session cost in USD | green |
session-clock |
JSON | Session duration | white |
output-style |
JSON | Output style name | white |
git-branch |
Git | Current branch name | magenta |
git-changes |
Git | Uncommitted changes count | yellow |
git-worktree |
JSON/Git | Worktree name | magenta |
tokens-input |
JSON | Input tokens in current context, incl. cache (~= context-length since CC v2.1.132) |
white |
tokens-output |
JSON | Output tokens from the most recent API response | white |
tokens-cached |
JSON | Cached tokens | white |
tokens-total |
JSON | Tokens currently in the context window (in + out) | white |
current-usage-input |
JSON | Current round input tokens | white |
current-usage-output |
JSON | Current round output tokens | white |
cache-creation |
JSON | Cache creation tokens | white |
context-length |
JSON | Context usage in tokens | white |
context-percentage |
JSON | Context usage % (metadata.display: bar for ▓▓▓░░░░░░░ 32%) |
white |
context-percentage-usable |
JSON | Usable context % (heuristic: 80% of max, leaving auto-compact headroom; bar display) |
white |
remaining-percentage |
JSON | Remaining context % (bar display) |
white |
cache-hit-rate |
JSON | Session prompt cache hit ratio (prompt_cache.hit_ratio; older Claude Code: the last request's cache reads; bar display) |
cyan |
prompt-cache |
JSON | Prompt cache state, e.g. warm 42m / cold — green while warm, dim once cold (metadata.display: status/ttl/hit/misses/full, e.g. warm 42m · 91% hit · 2 misses (tools_changed)) |
green |
api-duration |
JSON | Total API response time this session | white |
block-timer |
JSON/JSONL | 5-hour block timer (uses the rate-limit window when available; metadata.display: time/progress/percentage) |
white |
rate-limits |
JSON | Combined limits, e.g. 5h: 3% / 7d: 12% or spend: $314.12/$500.00 behind a Claude apps gateway (each shown only when present) |
yellow |
spend-limit |
JSON | Claude apps gateway spend limit, e.g. $314.12 / $500.00 (63% until the dollar amounts arrive; metadata.display: amount/percent/bar/reset/full) |
yellow |
rate-limit-5h |
JSON | 5-hour rate-limit usage (metadata.display: percent/bar/reset/full; metadata.barWidth) |
yellow |
rate-limit-7d |
JSON | 7-day rate-limit usage (metadata.display: percent/bar/reset/full; metadata.barWidth) |
yellow |
current-working-dir |
JSON | Current directory name (raw: the full path, ~-shortened) |
blue |
project-dir |
JSON | Project root directory name (raw: the full path) | blue |
transcript-path |
JSON | Transcript file name (raw: the full path) | white |
added-dirs |
JSON | Directories added via /add-dir (metadata.display: list for names) |
blue |
repo |
JSON | Repository owner/name from the origin remote (raw: name only) |
blue |
pr |
JSON | Open PR number + review state, e.g. #1234 approved, or !1234 for a GitLab merge request; the number is an OSC 8 link when pr.url is present (raw: number) |
cyan |
lines-changed |
Git | Uncommitted lines changed (+N/-M) | green |
lines-added |
Git | Uncommitted lines added | green |
lines-removed |
Git | Uncommitted lines removed | red |
session-lines |
JSON | Lines Claude added and removed this session (+N/-M) | green |
vim-mode |
JSON | Vim mode indicator | yellow |
agent-name |
JSON | Agent name | cyan |
effort |
JSON | Reasoning effort level | cyan |
thinking |
JSON | Extended thinking indicator | magenta |
fast-mode |
JSON | Fast while fast mode is on |
yellow |
exceeds-200k |
JSON | Warning at 200k tokens | red |
terminal-width |
System | Line width: the terminal width minus the flexMode margin |
white |
custom-text |
- | User-defined static text | white |
custom-command |
System | Shell command output | white |
separator |
- | Visual separator | white |
flex-separator |
- | Fills remaining width | - |
Usable only in subagentLine; each reads the task Claude Code passes for that row.
| Widget | Description | Default Color |
|---|---|---|
task-name |
Subagent name, else its agent type, else the task type | cyan |
task-type |
Agent type, e.g. Explore or a custom agent |
magenta |
task-status |
running / completed / failed / killed, colored yellow / green / red / dim |
by state |
task-label |
Progress summary, else the task description | white |
task-elapsed |
Running time of a running subagent, e.g. 1m23s |
white |
task-model |
Model, e.g. Sonnet 5.5 (raw: model ID) |
cyan |
task-effort |
Configured effort level, or a token budget such as 32k |
cyan |
task-tokens |
Running token count, e.g. 45.2k (thresholds) |
white |
task-context |
Tokens as % of the model's context window (bar display, thresholds) |
white |
task-sparkline |
Tokens a running subagent added per refresh tick, e.g. ▂▅█▁ (▁ for none) |
cyan |
task-cwd |
Base name of the subagent's working directory | blue |
Claude Code pipes JSON session data to stdin on each update (after assistant messages, permission changes, vim mode toggles). ccstatus parses the JSON, renders widgets according to the configured layout, and outputs ANSI-colored text to stdout.
Claude Code --[JSON]--> ccstatus --[ANSI text]--> status line
For the full JSON schema, see the official documentation.
The separate subagentStatusLine command works the same way per subagent row, printing JSON lines instead of text; see Subagent Rows.
ccstatus never exits non-zero while rendering, because Claude Code hides the whole status line when the command fails. Problems show up in the status line instead:
- A
settings.jsonthat fails to parse renders the default layout below a redccstatus: settings.json:LINE:COL: ... (run ccstatus validate)line. Other mistakes, such as an unknown widget type or color name, are skipped without a message;ccstatus validatelists them. - Input that is not JSON renders a single red
ccstatus: invalid input: ...line. - A field whose type changed in a newer Claude Code is coerced when that is unambiguous (a quoted number, a fractional epoch) or dropped otherwise, and the rest still renders; dropped fields are listed on stderr, which
claude --debuglogs.
Use ccstatus dump to capture the raw JSON that Claude Code sends:
# Temporarily switch to dump mode in ~/.claude/settings.json:
# "command": "ccstatus dump" (latest payload only)
# "command": "ccstatus dump --append" (every payload of the session, one JSON line each)
# Then inspect the captured data:
cat /tmp/ccstatus-dump.json | jq .
jq -c '{ts, columns, model: .input.model.display_name}' /tmp/ccstatus-dump.jsonl
# Replay captured data to test rendering:
ccstatus preview --input /tmp/ccstatus-dump.json- Claude Code Status Line Documentation
- ccstatusline - Original TypeScript implementation
- Design RFCs - Architecture decisions and release plans
Apache License 2.0
