Guide
Install, the four concepts that explain everything else, and the recipes people actually ask for. For the generated tables of every tile, fill, signal and edge, see REFERENCE.md.
Install
git clone https://github.com/Bravim-Ketan-Purohit/statusline
cd statusline
pnpm install && pnpm build
Check it renders before wiring anything up:
echo '{"model":{"display_name":"Opus 5"}}' \
| COLUMNS=140 node packages/cli/dist/statusline.js render
Wire it into Claude Code
Design a bar in the builder, press Copy install, paste the command. Or by hand:
node packages/cli/dist/statusline.js import "$(cat my-config.b64)"
import patches ~/.claude/settings.json. It reads the existing file, changes only the statusLine key, and shows you a diff before writing. If something else already owns that key it stops and asks.
Wire it into tmux
node packages/cli/dist/statusline.js tmux-conf >> ~/.tmux.conf
tmux source-file ~/.tmux.conf
That snippet enables mouse mode and registers the click binding, so tiles with an action or drill become clickable.
If something's wrong
statusline doctor
It checks the config parses, the settings file points where you think, the daemon is running if any tile needs it, and every custom command is approved — and tells you the fix, not just the fault.
Four concepts
Everything else follows from these.
1. Tiles declare what they cost
A status line runs on every message. So each tile declares a tier, and nothing above T2 ever touches the render path.
| Tier | Cost | Example |
|---|---|---|
| T0 | free — already on stdin | model, cwd, cost |
| T1 | one local file read | git-branch, clock |
| T2 | a subprocess, cached | git-diff, battery |
| T3 | sampled by the daemon | cpu, memory, network |
| T4 | network, needs a credential | ci, sentry-issues |
T3 tiles render nothing unless statusline daemon is running. That's not a bug — sampling on render would make "CPU usage since last message" the metric, which is meaningless. Start it once:
statusline daemon &
2. Priority decides what survives
Terminals resize. Every tile carries a priority integer, and when the row won't fit the solver drops whole tiles, highest number first. It never wraps and never truncates mid-tile.
priority 1 ← last to go (branch, context)
priority 9 ← first to go (clock, battery)
Guaranteed monotonic: a narrower terminal never keeps more tiles.
3. Breakpoints inherit sparsely
Six by default — xs ≥0, sm ≥40, md ≥80, lg ≥120, xl ≥160, 2xl ≥220.
An override only records what *differs* from the next smaller breakpoint:
"responsive": {
"priority": 3,
"sm": { "compact": true }, // from 40 cols up: drop the label
"md": { "compact": false } // from 80 cols up: put it back
}
"md": {} means *inherit sm*, not *reset to default*. If everything looks compact at 200 columns, this is why.4. Signals drive appearance
26 closed-enum signals. Bind one to a colour, a border, a blink, or to visibility:
"style": {
"showOnlyWhen": [{ "signal": "ci.failing" }], // invisible until it matters
"rules": [{
"signal": "ci.failing",
"blink": { "target": "border", "color": "#ff5f5f", "hz": 2 },
"bell": true
}]
}
Later rules win, so layer a general rule then override it.
Recipes
Show CI only when it's broken
{ "type": "ci", "style": { "showOnlyWhen": [{ "signal": "ci.failing" }] } }
Costs zero columns on a good day.
Stop yourself running things against production
{ "type": "kube-context" }, { "type": "aws-profile" }, { "type": "protected-branch" }
The danger match is segment-based, so eks-prod-1 reddens and product-api doesn't. Add your own words under theme.dangerPatterns.
Warn before the context window runs out
{ "type": "context-bar", "style": { "rules": [
{ "signal": "context.above", "threshold": 75, "fg": "#e0a44a" },
{ "signal": "context.above", "threshold": 90, "fg": "#d9604e", "bell": true }
]}}
Rotate several low-value tiles through one slot
"rotation": [{ "tiles": ["verse", "track", "skills"], "every": "minute" }]
Three tiles, one tile's worth of columns. The choice comes from a clock bucket so it never flickers mid-session.
Make the whole bar flow
"theme": { "terminalFill": {
"kind": "gradient", "mode": "plasma", "animated": true, "speed": 0.3,
"stops": [{ "color": "#2b0b52", "pos": 0 }, { "color": "#7b2ff7", "pos": 1 }]
}}
In Claude Code this is a slow pulse — the refresh floor is one second. The web preview animates properly.
Add a tile that runs your own command
{ "type": "command", "props": { "argv": ["kubectl", "get", "po", "-o", "name"] } }
Argv array, never a shell string. Then approve it once:
statusline approve
Editing the command revokes the approval — that's the point.
Troubleshooting
| Symptom | Cause |
|---|---|
| Bar is blank | The script exited non-zero. Run statusline render by hand and read stderr. |
| Metric tiles empty | The daemon isn't running. statusline daemon & |
| Everything compact at wide widths | A sparse override — see concept 3. |
| A custom command does nothing | Not approved. statusline approve |
| Powerline caps show as boxes | Needs a Nerd Font. |
| Overline ignored | Your terminal doesn't support it. It's dropped, not garbled. |
| tmux tiles not clickable | tmux-conf snippet not sourced, or mouse mode off. |
| T4 tiles empty | No credential. statusline creds set <name> |
Deploying your own copy of the site
The repo builds a static site — landing page, docs and the builder:
pnpm build:site # → dist-site/
pnpm preview:site # → http://localhost:5000
On Vercel: import the repo and accept the detected settings. vercel.json already sets the build command, output directory and cache headers. No environment variables and no server runtime — it's fully static.
Demo video
The recordings are not in git — they'd add megabytes to every clone forever. They live on a GitHub Release instead, and the build rewrites the {{MEDIA}} token in the HTML to point at them.
pnpm media:publish # uploads landing-kit/assets/video/*.mp4 to the release
pnpm media:check # HEADs every URL the built site references
site.config.json is the single place that defines where media is served from, so moving to a real CDN later is a one-line change rather than a find-and-replace through the markup.
The build succeeds without the video present — which is what the Vercel checkout looks like, since the files are gitignored. Posters stay committed because they're what paints before the remote video arrives.
Reference
Generated from packages/core. Re-run node scripts/gen-reference.mjs after adding a tile, a fill mode, a signal or an edge style.
Tiles (68)
| id | tier | category | name |
|---|---|---|---|
aws-profile | 1 | environment | AWS profile |
battery | 2 | environment | Battery |
clock | 1 | environment | Clock |
cpu | 3 | environment | CPU usage |
cwd | 0 | environment | Working directory |
disk | 3 | environment | Disk used |
gcp-project | 4 | environment | GCP project |
gpu | 3 | environment | GPU usage |
hostname | 1 | environment | Hostname |
kube-context | 2 | environment | Kubernetes context |
load | 3 | environment | Load average |
memory | 3 | environment | Memory usage |
network | 3 | environment | Network throughput |
node-version | 1 | environment | Node version |
python-version | 1 | environment | Python version |
swap | 3 | environment | Swap in use |
venv | 1 | environment | Active venv |
vram | 3 | environment | VRAM usage |
ci | 4 | git | CI status |
gh-issues | 4 | git | Open issues |
gh-pr-counts | 4 | git | Repo PR counts |
git-ahead-behind | 2 | git | Ahead / behind |
git-branch | 1 | git | Git branch |
git-counts | 2 | git | Git file counts |
git-diff | 2 | git | Uncommitted diff |
git-last-commit | 2 | git | Last commit age |
git-sha | 2 | git | Commit SHA |
git-stash | 2 | git | Stash count |
pr | 0 | git | Branch PR |
protected-branch | 1 | git | Protected branch warning |
repo-slug | 0 | git | Repo slug |
worktree | 0 | git | Worktree |
command | 2 | layout | Custom command |
fill-band | 1 | layout | Fill band |
separator | 1 | layout | Flex separator |
spacer | 1 | layout | Spacer |
text | 1 | layout | Custom text |
media-next | 1 | media | Next track |
media-play | 1 | media | Play / pause |
media-prev | 1 | media | Previous track |
media-vol-down | 1 | media | Volume down |
media-vol-up | 1 | media | Volume up |
now-playing | 2 | media | Now playing |
skills | 2 | personal | Suggested skills |
track | 1 | personal | Playlist track |
verse | 1 | personal | Scripture verse |
agent | 0 | session | Agent |
cc-version | 0 | session | Claude Code version |
context-bar | 0 | session | Context window bar |
context-pct | 0 | session | Context percentage |
cost | 0 | session | Session cost |
deploy-duration | 4 | session | Deploy duration |
deploy-status | 4 | session | Deploy status |
deploy-url | 4 | session | Preview URL |
effort | 0 | session | Thinking effort |
five-hour-bar | 0 | session | 5h limit bar |
linear-assigned | 4 | session | Linear assigned |
linear-review | 4 | session | Linear in review |
linear-started | 4 | session | Linear in progress |
linear-triage | 4 | session | Linear triage queue |
lines-changed | 0 | session | Lines added/removed |
model | 0 | session | Model |
sentry-events | 4 | session | Sentry events 24h |
sentry-issues | 4 | session | Sentry unresolved |
session-duration | 0 | session | Session duration |
session-name | 0 | session | Session name |
seven-day | 0 | session | 7d limit |
vim-mode | 0 | session | Vim mode |
Fill modes (15)
| mode | what it does |
|---|---|
linear | A straight ramp at any angle. |
radial | Out from an origin you can move. |
conic | Swept around the origin like a radar. |
diamond | Manhattan distance; hard rhombic bands. |
wave | A ramp bent by a sine along the rows. |
ripple | Concentric rings travelling outward. |
spiral | Conic and radial combined; it winds. |
barber | Repeating diagonal stripes that climb. |
comet | One bright head with a trailing falloff. |
scan | A single band sweeping edge to edge. |
plasma | Summed sines; the classic demoscene field. |
pulse | The whole bar moves through the ramp at once. |
breathe | Like pulse but eased, so it swells. |
rainbow | Ignores the stops and rotates hue. |
strobe | Snaps between stops with no blend. |
Signals (26)
Used by rules, hideWhen and showOnlyWhen. A signal whose data is absent always returns false, so a missing sampler never fires a threshold.
| signal | threshold | fires when |
|---|---|---|
ci.failing | — | The latest run on this branch concluded in failure. |
ci.passing | — | The latest run concluded successfully. |
ci.running | — | A run is queued or in progress. |
pr.approved | — | The open PR is approved. |
pr.changes | — | A reviewer requested changes. |
pr.pending | — | The PR is open and awaiting review. |
pr.open | — | Any open PR exists for this branch. |
git.conflict | — | At least one conflicted path. |
git.dirty | files (default 1) | Modified or untracked files above the count. |
git.ahead | commits (default 1) | Ahead of upstream by at least this many. |
git.behind | commits (default 1) | Behind by at least this many. |
git.clean | — | Nothing staged, modified, untracked or conflicted. |
context.above | % (default 80) | Context window usage crosses this percentage. |
fivehour.above | % (default 80) | The five-hour window crosses this percentage. |
sevenday.above | % (default 80) | The seven-day window crosses this percentage. |
cost.above | $ (default 20) | Session spend crosses this many dollars. |
battery.below | % (default 20) | Battery drops under this percentage, unplugged. |
review.waiting | — | Pull requests are awaiting your review. |
cpu.above | % (default 85) | Sampled CPU crosses this percentage. |
mem.above | % (default 85) | Memory in use crosses this percentage of total. |
swap.above | MB (default 512) | Swap in use crosses this many megabytes. |
disk.above | % (default 90) | Disk used crosses this percentage. |
load.above | load (default 8) | One-minute load average crosses this value. |
gpu.above | % (default 90) | GPU utilisation crosses this percentage. |
vram.above | % (default 90) | VRAM in use crosses this percentage of total. |
always | — | Unconditional; useful for a steady accent. |
Border edges (7)
An edge is characters and costs columns, which the solver measures. A line (underline / overline) is SGR and costs none.
| edge | columns | note |
|---|---|---|
none | 0 | |
thin | 2 | |
block | 2 | |
bracket | 2 | |
round | 2 | |
angle | 2 | |
powerline | 2 | needs a Nerd Font |
Targets
| target | note |
|---|---|
| Claude Code | Captured stdout. Multi-row, OSC 8 links, no click. |
| tmux | One line. Real click dispatch, no hyperlinks. |
| Web preview | The builder canvas. Everything works here. |