Skip to content

Repository files navigation

dashline

A Claude Code status line configured in settings.json. The default shows the context window on the left and plan usage on the right. Change it by editing a list of fields.

Dashline in a Claude Code terminal across its ok, high, and compact states

Open the dashline builder CI status Follow @_ordinarynerds on X

Open the builder → — drag widgets into zones, set colors and thresholds, and copy the config straight into settings.json. Nothing to install to try it, and the preview renders byte-for-byte what your terminal prints.

Why

dashline keeps two numbers on screen: how full the context window is, and how much of the session and weekly rate limit is used. Both otherwise live behind the /usage command. Any other field in the payload can be added by name.

Install

A Claude Code plugin cannot set the main status line on its own. Both routes below end with settings.json pointing at dashline; the plugin route does it for you.

As a plugin (recommended)

/plugin marketplace add ordinarynerds/dashline
/plugin install dashline@ordinarynerds
/dashline:install

Manual

git clone https://github.com/ordinarynerds/dashline.git ~/.claude/dashline
cd ~/.claude/dashline && npm install && npm run build
./scripts/install.sh

The installer backs up settings.json, points statusLine at node dist/dashline.js, and leaves a settings.json.bak-dashline-* file. Undo with ./scripts/install.sh --uninstall. Start a new session or run /statusline to see it. Requires Node 18+.

Updating

Plugin: enable auto-update in /plugin (Marketplaces tab), or pull on demand:

/plugin marketplace update
/reload-plugins

Manual:

cd ~/.claude/dashline && ./scripts/install.sh --update

Quick start

The config is a dashline key in ~/.claude/settings.json. Each entry in lines is one row. This is the default:

{
  "dashline": {
    "lines": [
      { "left": ["branch", "model", "context"], "right": ["session", "weekly"] }
    ]
  }
}

An item is one of six shapes:

"branch"                                       // widget
["model", "cyan"]                              // widget + color
["cwd", "basename"]                            // widget + variant
["session", { "bar": "fine", "label": "5h" }]  // widget + options
{ "text": "api", "color": "dim" }              // literal text
"kache stat"                                   // any unrecognized string runs as a shell command

A row is either a bare array like ["branch", "model"] (left-aligned), or a { "left": [...], "center": [...], "right": [...] } object spread across the width.

The reference lists every widget and option.

Recipes

Each block is a value for dashline.lines and the status line it renders. Paste one into your settings.

Cost and PR on the right

Cost and PR on the right

{ "left": ["branch", "model", "context"], "right": ["cost", "pr", "session"] }

Usage as bars

Usage as bars

{
  "left": ["branch", "context"],
  "right": [
    ["session", "bar"],
    ["weekly", "bar"]
  ]
}

Browse all 10 recipes

Reference

Widgets

Each widget reads one field of the payload; the right column is its type. A widget with no data hides itself, and an empty row is skipped. An unrecognized bare string runs as a shell command, and a { "text": ... } item is printed literally.

Widget Example Displays Type
branch ⎇ main git branch label
dirty +2 *3 ?1 staged, unstaged, untracked files label
sync ↑2↓3 commits ahead of and behind upstream label
sha a1b2c3d short commit hash label
stash ⚑2 stash entries label
diff +42 -10 working-tree churn against HEAD delta
model Opus 4.8 model name label
context 44% ████░░░░░░ (440k/1.0M) · high model context percent
session session 61% (↻2h11m) session usage and reset percent
weekly All 74% weekly usage percent
cost $2.69 cost, per period money
rate $4.10/h spend per hour money
duration 37m wall-clock this session duration
lines +156 -23 lines added and removed delta
pr PR #702 open PR number label
review pending PR review state label
worktree ⌂ hotfix linked worktree label
cwd ~/Development/dashline working directory label
repo dashline repository name label
effort high reasoning effort label
name celestial-vega session name label
output /default output style label
version v2.1.90 Claude Code version label
burn → /compact ~18m projected time to critical context label
fast fast fast mode flag
thinking thinking extended thinking flag
vim NORMAL vim mode label
agent security-reviewer active subagent label
host workbench machine name label
time 14:32 clock at the last render label

context, session, and weekly color themselves by fill (green to red). The usage pair appears on Pro and Max once the payload carries rate limits. The burn widget, the history variant, and the trend option read a short history of the session, so they appear after a few refreshes.

diff counts what the working tree has changed against HEAD; lines counts what this session wrote, from the payload. They answer different questions and often disagree.

cost is what it has spent and rate is how fast; rate waits for a minute of wall clock, below which the number is noise.

cost takes a periodsession by default, or week or month for the total across every session:

["cost", { "period": "month" }]     // month $128.40

The two cross-session windows are a tally dashline keeps itself, because nothing hands it one: the payload carries a cost for the current session and a percentage for the weekly quota, but no money figure spanning sessions. Each session's running total goes to a single file in the state directory and the entries are summed, so it counts the sessions it saw — spend from before you installed dashline, or from a session that ran without a status line, is not in it. week follows the weekly quota reset the payload advertises, so it clears at the same moment weekly returns to zero; month runs from the first, locally. Only these two read the ledger; a plain cost costs nothing extra.

time is the clock at the moment of the last render, not a ticking one — the status line only redraws when Claude Code refreshes it.

Widget variants

A few widgets read more than one field. These pick which:

Item Shows Item Shows
["model", "full"] Opus 4.8 (1M context) ["repo", "full"] owner/repo
["model", "id"] claude-opus-5 ["repo", "owner"] owner
["name", "id"] abcd1234 ["repo", "host"] github.com
["context", "left"] 560k left ["host", "ssh"] only when remote
["time", "seconds"] 14:32:07 ["time", "hm12"] 2:32pm

model trims the trailing parenthetical by default, so full is how you keep the 1M/200k distinction on screen.

Working-tree variants

dirty and sync each read several numbers, and a label carries one color, so every part is also its own variant. Use one item for a combined reading, or several to color each part:

Item Shows Item Shows
dirty +2 *3 ?1 sync ↑2↓3
["dirty", "flags"] +*? ["sync", "ahead"] ↑2
["dirty", "staged"] +2 ["sync", "behind"] ↓3
["dirty", "unstaged"] *3 ["sync", "synced"]
["dirty", "untracked"] ?1
["dirty", "conflicts"] !1
["dirty", "clean"]

Each hides when its count is zero, so a clean tree shows nothing and ["dirty", "clean"] is how you ask for a positive signal instead. sync hides entirely when the branch has no upstream, rather than claiming it is level with nothing.

Presentations, by type

A presentation works by type, not by widget: any percent widget takes any percent presentation. Pass it as the item's variant. The first in each row is the default.

Type Presentations
percent pct (44%), bar (████░░░░░░), gauge (▕████░░▏), ratio, tokens ((440k/1.0M)), history (▁▂▃▅▆█, context only), plus bar styles
duration short (37m), long (0h37m), clock (0:37:00)
money usd ($2.69), cents (269c), round ($3)
delta pair (+156 -23), sum (+133), added (+156)
label text, basename, upper, lower, truncate:N
flag on (hidden when off), onoff (fast:off)

The percent default draws the number, bar, tokens, and countdown when each is present. The reductive presentations (bar, pct, tokens) draw only that part.

Data options

Object-form keys that change what a widget shows rather than how. Combine them with variant, bar, and color.

Option Types Effect
countdown percent set false to drop the reset countdown
period cost session (default), week, or month
warningAt, criticalAt percent color thresholds for this item, overriding the global ones
trend context append a ↑/↓/→ arrow, read from session history
width percent bar width in columns
bar percent bar glyph style (see bar styles)
truncate label shorten the text to N characters with an ellipsis
icon any a glyph placed before the item
label any a word placed before the item, such as session or spend
color any a fixed color (see below)
bg text, label a background color, for badges
bold, italic, underline any set true to add that text attribute, alongside any color
variant any which presentation to draw

icon and label are drawn around every widget, whatever its type — they are trimmings, not part of how a value is rendered, so they are applied once for all of them. A widget that names its own (branch's , session's session) has that used as the default, and either option overrides it. An icon or label you set survives a variant; a widget's own label does not, because a variant asks for one piece of the widget in isolation:

["cost", { "icon": "$", "label": "spend" }]   // $ spend $2.69
["session", { "label": "5h" }]                 // 5h 61% (↻2h11m)
["session", "bar"]                             // ██████░░░░  — the widget's own label steps aside
["context", { "variant": "bar", "label": "ctx" }]  // ctx ████░░░░░░

trend is the exception that really is type-bound: session history records only the context percentage, so there is no series for session or weekly to compare against.

Colors

A color term is one or more of these words, so "bold red" is valid:

red · green · yellow · blue · magenta · cyan · gray · black · white · dim · bold · italic · underline

A term can also be a hex value for 24-bit color: "#4EC9D6", or "#fff" in shorthand. Mix it with attributes, so "bold #4EC9D6" works.

The bg option fills the background of a text or label item, which makes a badge: { "text": " PR ", "color": "black", "bg": "#C678DD" }.

A color on an item overrides its default styling. context, session, and weekly normally color themselves by fill; a fixed color removes that signal.

Bar styles

Any percent bar takes a bar glyph style. Every style is single-cell, so the bar stays the same width whichever you pick.

bar 44% of 10
blocks (default) ████░░░░░░ sharp
shade ▓▓▓▓░░░░░░ softer fill
line ━━━━────── thin
ascii [###-----] brackets counted inside the width
fine ████▍░░░░░ smooth, 8 sub-cell steps per column
gradient ██████░░░░ filled cells ramp green to red by position (truecolor)

The gradient style colors each filled cell along a green-to-red ramp by its position, so a fuller bar reads hotter. It uses truecolor, unlike the single-color styles above.

Themes

theme remaps the named colors to a palette in one move, so every widget that uses a named color, and the fill colors on context, session, and weekly, follow the scheme. Available: nord, dracula, gruvbox, catppuccin, ordinarynerds.

{ "dashline": { "theme": "catppuccin", "lines": [["branch", "model", "context"]] } }

Powerline

powerline: true draws each zone as connected segments joined by arrow glyphs, with each segment's background fading into the next. A segment takes an item's bg, or an alternating neutral tone when it has none, so it composes with badges. The right zone runs the arrows in reverse so the ribbon points back toward the content. Needs a Nerd Font.

Icons

icons: true prefixes label widgets (branch, model, cwd, pr, and so on) with a Nerd Font glyph. An explicit icon on an item wins over the default. Needs a Nerd Font.

Config keys

Alongside lines, the dashline object takes:

Key Default Effect
separator · drawn dim between items in a zone
margin 5 columns kept free at the right edge
powerline false draw each zone as arrow-joined segments (needs a Nerd Font)
theme "" recolor named colors from a palette: nord, dracula, gruvbox, catppuccin, ordinarynerds
icons false prefix label widgets with a Nerd Font glyph
contextWarningAt 40 context turns yellow ("high") at/above this %
contextCriticalAt 50 context turns red with the → /compact nudge at/above this %
usageWarningAt 70 usage widgets turn yellow at/above this %
usageCriticalAt 90 usage widgets turn red at/above this %

Editor autocomplete

The config has a schema at dashline.schema.json. It constrains only the dashline key and leaves the rest of settings.json alone. Point your editor at it for autocomplete and validation. In VS Code, add to your settings:

"json.schemas": [
  {
    "fileMatch": ["**/.claude/settings.json", "**/.claude/settings.local.json"],
    "url": "https://raw.githubusercontent.com/ordinarynerds/dashline/main/dashline.schema.json"
  }
]

How it works

Claude Code passes the status-line command a JSON payload on stdin. dashline reads it, reads the dashline config from the settings files, and prints one line per entry in lines. It makes no network calls and does not read the transcript.

Each widget is a pure function from the payload to a typed value, which a presenter draws. Each command item runs under a 2-second timeout.

Git state is not in the payload, so dashline shells out — but only for what the config asks for, since every call costs time on each refresh:

Config contains Cost
no git widget nothing
branch one rev-parse
any of dirty, sync, stash one git status --porcelain=v2, which also carries the branch and sha
diff one more, git diff --shortstat HEAD
worktree, when the payload omits it one more rev-parse

So branch alone and branch dirty sync stash sha together both cost a single call. Each is capped at one second: a repository slow enough to miss that shows nothing rather than stalling the prompt.

The burn widget, the history variant, and the trend option need a history, so when the config uses one dashline keeps a small dashline-state/ directory next to the settings with recent context samples per session. Otherwise it holds no state.

Security

dashline can run shell commands. Command sources are restricted:

  • Command items run only from your own user settings (~/.claude/settings.json and ~/.claude/settings.local.json). Config that arrives through a project, such as a .claude/settings.json committed to a repository you cloned, may arrange widgets, but any command in it is dropped. Cloning a repo cannot make dashline run code.
  • Dynamic values reach your commands through the environment ($DASHLINE_BRANCH, $DASHLINE_WORKTREE, $DASHLINE_CWD), never spliced into the command text, so a branch named like a shell expression cannot inject anything.
  • dashline's own git lookups run without a shell.
  • Each command runs under a 2-second timeout.
  • A command's output is printed as is, including any terminal escapes it emits. Run only tools you trust.

Develop

npm install
npm test          # node's test runner over src
npm run build     # bundle src to dist/dashline.js
npm run typecheck

Source is in src/: widgets/ holds one file per field, present/ draws each data type, render.ts lays out the zones, layout.ts justifies a line, and config.ts reads and merges settings. Adding a widget is one file plus one line in widgets/registry.ts.

Releases run on release-please: commit with Conventional Commit messages. Merging its release PR bumps the version, writes CHANGELOG.md, and tags a release.

License

MIT © Ordinary Nerds. See LICENSE.

About

A Claude Code opinionated statusline configurable from settings.json

Topics

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages