Customizable Claude Code statusline in Rust. Templating, up to 3 lines, ANSI colors, and a rate-limit bar that shows whether you're over or under the expected burn curve (red = ahead of curve, green = buffer, with Δ%, Δtime, and peak/off-peak timer).
# Homebrew
brew install Team-MaRo/tap/cc-statusline
# or
brew tap Team-MaRo/tap
brew install cc-statusline
# Cargo
cargo install --git https://github.com/Team-MaRo/cc-statusline
# Nix (flake)
nix run github:Team-MaRo/cc-statusline -- --version
nix profile install github:Team-MaRo/cc-statuslineAsset names are versionless, so releases/latest/download/<asset> always grabs the newest build:
# Linux x86-64 (musl, static — runs on any distro)
curl -L https://github.com/Team-MaRo/cc-statusline/releases/latest/download/cc-statusline-linux-amd64-musl -o cc-statusline
chmod +x cc-statusline| OS / arch | Asset |
|---|---|
| Linux x86-64 (glibc) | cc-statusline-linux-amd64-gnu |
| Linux arm64 (glibc) | cc-statusline-linux-arm64-gnu |
| Linux x86-64 (musl, static) | cc-statusline-linux-amd64-musl |
| Linux arm64 (musl, static) | cc-statusline-linux-arm64-musl |
| macOS x86-64 | cc-statusline-darwin-amd64 |
| macOS arm64 (Apple Silicon) | cc-statusline-darwin-arm64 |
| Windows x64 | cc-statusline-windows-amd64-msvc.exe |
To verify, compare against the sha256 digest GitHub shows next to each asset on the releases page (or gh api repos/Team-MaRo/cc-statusline/releases/latest --jq '.assets[] | "\(.name) \(.digest)"'): shasum -a 256 cc-statusline-linux-amd64-musl.
macOS: the binaries are ad-hoc signed (so they execute on Apple Silicon), but not notarized. A curl/wget download runs as-is; a binary downloaded via a browser is quarantined by Gatekeeper — clear it once with xattr -d com.apple.quarantine ./cc-statusline (or right-click → Open). For zero friction, install via Homebrew instead.
macOS post-copy note: if you manually cp the binary anywhere (e.g. from target/release/ into ~/.cargo/bin/), macOS tags the destination with com.apple.provenance and gatekeeper will SIGKILL the binary on every spawn — your statusline goes blank and cc-statusline --version exits 137. Fix with an ad-hoc re-sign:
codesign --remove-signature ~/.cargo/bin/cc-statusline 2>/dev/null
codesign -s - ~/.cargo/bin/cc-statuslinecargo install and brew install write directly and don't trigger this.
Edit ~/.claude/settings.json. Each positional arg = one output line (max 3). Literal \n inside an arg also splits. Zero args = empty output — you pick every token you want.
On Windows the file lives at %USERPROFILE%\.claude\settings.json. Windows launches the command via cmd, which does not honor single quotes as string delimiters — use double quotes and escape inner double quotes, or double literal percents (%%) if a token name clashes with an env var.
Three lines: model/effort/context, session/peak/weekly rate-limit curves, cwd/git/cost/tokens/speed. Dim labels, colored values, red-over / green-buffer bars with Δ%, Δtime, and peak timer.
Smart defaults (used when no :style is given on a token):
%effort—low/minimal/nonegreen,mediumyellow,highred, anything else bright_red.%ctx_tokens/%ctx_tokens_used/%ctx_tokens_usable— "used" portion goes red whenexceeds_200k_tokensis true, else green.%ctx_tokens_maxis never auto-colored.%ctx_bar/%ctx_bar_usable— filled bar cells beyond the 200k mark are red, the rest stays uncolored.%ctx_pct(%cup) and%ctx_pct_usable(%cpu) — gradient against the auto-compact threshold (100% usable = compact): green below 70%, yellow 70–85%, bright_yellow 85–95%, red at 95%+. Ignoresexceeds_200k_tokens. Usable budget =context_window_size × 0.98for models with ≥500k context (matches Claude Code's "X% context used" footer at ~98% of size); falls back to× 0.80for legacy 200k models. The composite%ctx/%cudisplays the raw % but colors it with the same gradient, so the pct portion turns red as auto-compact nears.%diff— added count green, removed count red.
Any explicit :style on a token overrides these defaults.
{
"statusLine": {
"type": "command",
"command": "cc-statusline '%[dim Model:] %model:cyan %[dim Effort:] %effort %[dim Context:] %ctx_usable' '%[dim Session:] %rate5h %peak %[dim Weekly:] %rate7d' '%[dim cwd:] %cwd %branch:yellow %diff %[dim Cost:] %cost:green %[dim Σd:] %cost_day:green %[dim Σm:] %cost_month:green %[dim Σ:] %cost_all:green %[dim ΣTokens:] %tokens_total:bright_cyan %[dim Speed:] %total_speed:bright_cyan'",
"padding": 0
}
}On Windows, swap the single quotes around each line for double quotes (escaped as \" inside JSON), since cmd launches the command and does not treat ' as a string delimiter:
{
"statusLine": {
"type": "command",
"command": "cc-statusline \"%[dim Model:] %model:cyan %[dim Effort:] %effort %[dim Context:] %ctx_usable\" \"%[dim Session:] %rate5h %peak %[dim Weekly:] %rate7d\" \"%[dim cwd:] %cwd %branch:yellow %diff %[dim Cost:] %cost:green %[dim Σd:] %cost_day:green %[dim Σm:] %cost_month:green %[dim Σ:] %cost_all:green %[dim ΣTokens:] %tokens_total:bright_cyan %[dim Speed:] %total_speed:bright_cyan\"",
"padding": 0
}
}Set the whole statusLine block (type + command + padding) in one shot. jq can't edit in place, hence the temp-file shuffle:
read -r -d '' CMD <<'EOF'
cc-statusline '%[dim Model:] %model:cyan %[dim Effort:] %effort %[dim Context:] %ctx_usable' '%[dim Session:] %rate5h %peak %[dim Weekly:] %rate7d' '%[dim cwd:] %cwd %branch:yellow %diff %[dim Cost:] %cost:green %[dim Σd:] %cost_day:green %[dim Σm:] %cost_month:green %[dim Σ:] %cost_all:green %[dim ΣTokens:] %tokens_total:bright_cyan %[dim Speed:] %total_speed:bright_cyan'
EOF
jq --arg cmd "$CMD" '.statusLine = {type: "command", command: $cmd, padding: 0}' \
~/.claude/settings.json > ~/.claude/settings.json.tmp \
&& mv ~/.claude/settings.json.tmp ~/.claude/settings.jsonOverwrite only the command (keep existing type/padding):
jq --arg cmd "$CMD" '.statusLine.command = $cmd' \
~/.claude/settings.json > ~/.claude/settings.json.tmp \
&& mv ~/.claude/settings.json.tmp ~/.claude/settings.json%name— render token%name:style[:style...]— apply ANSI styles (colors and/or modifiers)%%— literal%\n— line break inside an arg
Unknown tokens render as %name verbatim with a stderr warning. Tokens whose data is missing render empty; one surrounding space collapses so separators don't double.
Colors: black red green yellow blue magenta cyan white gray + bright_red … bright_white.
Modifiers: bold dim italic underline.
Order-independent. Combine freely: %m:cyan:bold.
Honors NO_COLOR env and --no-color flag.
| Short | Long | Source | Output |
|---|---|---|---|
%m |
%model |
model.display_name |
Opus 4.7 (1M context) |
%mid |
%model_id |
model.id |
claude-opus-4-7[1m] |
%e |
%effort / %thinking |
~/.claude/settings.json effortLevel |
medium |
%f |
%fast |
fast_mode |
fast or empty |
%cu |
%ctx / %context |
context vs full window | [█▒░░░░░░░░] 156k/1M (16%) |
%cuu |
%ctx_usable |
context vs usable budget (pre-auto-compact) | [█▒░░░░░░░░] 156k/980k (16%) |
%cup |
%ctx_pct |
16% |
|
%cpu |
%ctx_pct_usable |
% of usable budget (matches CC "X% context used") | 19.5% |
%cub |
%ctx_bar |
[█▒░░░░░░░░] |
|
%cubu |
%ctx_bar_usable |
bar vs usable budget | [█▒░░░░░░░░] |
%ct |
%ctx_tokens |
used/full pair | 40k/1M |
%ctpu |
%ctx_tokens_usable |
used/usable pair | 40k/980k |
%ctu / %ctm |
%ctx_tokens_used / %ctx_tokens_max |
used or max alone | 156k / 1M |
%tt |
%tokens_total |
cumulative session tokens — sum of total_input + total_output peaks per compaction segment; persisted in $XDG_CACHE_HOME/cc-statusline/sessions.json. Retained forever unless --prune (or CC_STATUSLINE_STATE_TTL_SECONDS) is set. |
4.8M |
%rl5 |
%rate5h |
5-hour rate limit | rl5h:▕bar▏N%(Δ%) 3h27m(Δ±t) |
%rl5p / rl5b / rl5r / rl5d |
rate5h_pct / _bar / _reset / _delta |
parts of the above | |
%rl7 |
%rate7d |
7-day rate limit | same shape |
%rl7p / rl7b / rl7r / rl7d |
same suffixes | ||
%pk |
%peak |
current time | peak 3h27m (red) / off-peak 5h12m (green); window 5–11 AM US/Pacific Mon–Fri (DST-aware) |
%b |
%branch |
git rev-parse in cwd |
⎇ main |
%bn |
%branch_name |
main |
|
%d |
%diff |
git diff --numstat HEAD |
(+12,-3) |
%cwd |
%cwd |
cwd, tilde-abbreviated |
~/Projects/foo |
%cwdb |
%cwd_base |
foo |
|
%c |
%cost |
this chat's cumulative cost — persisted, so it survives Claude resetting total_cost_usd mid-chat (sum of cost-segment peaks, never goes backwards) |
$0.35 |
%cd |
%cost_day |
today's cost — summed across every chat's per-day ledger | $48.10 |
%cm |
%cost_month |
current local calendar month's cost — summed across every chat's per-day ledger | $310.20 |
%ca |
%cost_all |
all-time cost — summed across every chat's per-day ledger (a chat's history is dropped if its session is --pruned) |
$2014.50 |
%la / %lr |
%lines_added / %lines_removed |
+42 / -7 |
|
%dur / %apidur |
%duration / %api_duration |
14m12s |
|
%os |
%output_style |
default |
|
%v |
%version |
Claude Code version | 2.1.118 |
%sid |
%session_id |
first 8 chars | d6486277 |
%vim |
%vim |
$CC_VIM_MODE env |
NORMAL (if set) |
Each rate-limit window has two curves:
- Used % — what you've consumed.
- Expected % — linear time elapsed through the window (0 at reset−window, 100 at reset).
Each cell of the bar fills progressively as ░ → ▒ → ▓ → █ (3 sub-levels per cell, 30 sub-units across 10 cells). Used drives the glyph; cells the used hasn't reached but expected has are shown as a green ghost at the matching sub-level. When used > expected, the over-portion is red. Δ% is signed percentage-point difference; Δt compares actual remaining time to the time you'd have left if you were exactly on the expected curve. During the peak window (5–11 AM US/Pacific Mon–Fri, DST-aware) the timer is colored red.
--no-color/NO_COLOR=1--now <unix>/NOW=<unix>(for testing)--prune <DUR>drop persisted sessions older thanDUR(30d,12h,4w, or bare seconds; current session always kept). Off by default — state is retained forever unless set. MirrorsCC_STATUSLINE_STATE_TTL_SECONDS.--debugdump stdin JSON to stderr--passthroughwrite stdin JSON unchanged to stdout while still updating the MCP state file — chain another statusline tool downstream (see Using a different statusline renderer)-V/--version,-h/--help
| Variable | Default | Effect |
|---|---|---|
NO_COLOR |
unset | Any value disables all ANSI styling (mirrors --no-color). Standard cross-tool convention. |
NOW |
unset | Override "current time" as a unix timestamp. Used for deterministic peak-hour / rate-limit math in tests; mirrors --now. |
CC_VIM_MODE |
unset | Value rendered by the %vim token (e.g. NORMAL, INSERT). Typically set by a vim-mode integration. |
CLAUDE_CONFIG_DIR |
~/.claude |
Where to read settings.json from (for %effort / effortLevel). |
XDG_CACHE_HOME |
~/.cache |
Base directory for cc-statusline/sessions.json (the %tokens_total state file). |
CC_STATUSLINE_STATE_TTL_SECONDS |
unset (no pruning) | Opt-in retention: when set (seconds), session entries older than this are pruned on each invocation (current session always kept). Equivalent to the --prune flag, which takes precedence. Each session carries its own cost ledger, so pruning a session also drops its contribution to the day/month/all-time totals. |
cc-statusline mcp runs a stdio MCP server that exposes the persisted statusline state to any MCP client — letting Claude (or another assistant) ask for its own context usage / rate-limit headroom mid-conversation. The server is read-only and uses the same ~/.cache/cc-statusline/sessions.json file the statusline writes; no live stdin needed.
Register with Claude Code:
claude mcp add cc-statusline -s user cc-statusline mcpTools:
| Tool | Args | Returns |
|---|---|---|
context_usage |
session_id? (defaults to most-recently-updated) |
session's segment list + current peak + total + context_window_size + used_percent + usable_percent + remaining_usable_tokens |
rate_limits |
— | latest 5-hour and 7-day rate-limit snapshot + remaining_seconds until reset + snapshot_age_seconds |
session_summary |
session_id? |
merge of both |
Manual smoke:
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{}}}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"session_summary","arguments":{}}}' \
| cc-statusline mcpThe state file is populated by your regular statusline renders — the first time you load this you'll see entries for every active session.
If you prefer another statusline tool but still want the MCP server's context_usage / rate_limits / session_summary data, chain cc-statusline --passthrough in front of it:
--passthrough reads Claude Code's JSON from stdin, updates ~/.cache/cc-statusline/sessions.json (so the MCP server has fresh data), then writes the JSON unchanged to stdout for the downstream tool to render. Any positional format args are ignored in this mode.
cargo test
cargo build --release
echo '<stdin json>' | ./target/release/cc-statusline '%m %cu'Regenerate the preview SVG after changing rendering:
cargo build --release --bin cc-statusline
cargo run --release --example gen_svgInstalls the release binary to ~/.cargo/bin/cc-statusline (make sure that's on your $PATH):
cargo install --path . --force
cc-statusline -VInstall straight from the repo without cloning:
cargo install --git https://github.com/Team-MaRo/cc-statuslineRun the full pre-commit check (fmt + clippy-as-error + tests) before pushing:
cargo fmt --check && cargo clippy --all-targets --all-features -- -D warnings && cargo testClippy lints move between Rust releases. If rust-analyzer flags something the CLI misses, update the toolchain: rustup update stable.
Build with the default x86_64-pc-windows-msvc toolchain. It needs the Visual Studio Build Tools (MSVC linker + Windows SDK) — install them once with:
winget install --id Microsoft.VisualStudio.2022.BuildTools -e \
--override "--quiet --wait --norestart --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended"Then build from a shell where MSVC cl/link are on PATH. A plain prompt won't have them — source vcvars64.bat first (or use the "Developer Command Prompt for VS"):
cmd /c '"C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvars64.bat" && cargo install --path . --force'The GNU toolchain (
stable-x86_64-pc-windows-gnu) is not supported: a transitive dependency (chrono/windows-link) makes it invokedlltool, which needs an assembler (as.exe) that rustup's bundled MinGW doesn't ship.
If linking fails with link: extra operand ..., Git Bash's /usr/bin/link.exe (coreutils) is shadowing MSVC's link.exe — build from PowerShell or a Developer Command Prompt instead.
Please read CONTRIBUTING.md for details on our code of conduct and the process for submitting pull requests.
This project uses Conventional Commits for automated releases and changelog generation.
We use SemVer for versioning. For available versions, see the tags on this repository.
- Manuele - D3strukt0r
- Oliver - Oliver-Zieschang
See also the full list of contributors who participated in this project.
We're currently looking for contributions for the following:
- Bug fixes
- Translations
- etc...
For more information, please refer to our CONTRIBUTING.md guide.
This project is licensed under the MIT License - see the LICENSE.txt file for details.
This project currently uses no third-party libraries or copied code.