Skip to content

Instantly share code, notes, and snippets.

@fabn
Last active August 7, 2026 15:34
Show Gist options
  • Select an option

  • Save fabn/47b4bca04e9258c3f78040201d2ffc38 to your computer and use it in GitHub Desktop.

Select an option

Save fabn/47b4bca04e9258c3f78040201d2ffc38 to your computer and use it in GitHub Desktop.
ccstatusline: 3-line powerline layout for Claude Code (nord-aurora, auto-aligned columns)

ccstatusline config — 3-line powerline layout

Three-line ccstatusline layouts for Claude Code.

Lines 1 and 2 are the same in both variants:

  • Line 1 — working dir, owner/repo, git branch, diff stat, CI status, PR status
  • Line 2 — model, thinking effort, context bar, input / cached / output tokens

Line 3 is what differs, and picking the right one depends on the account:

File Line 3 Use when
ccstatusline-settings-usage.json account email, 5h block reset timer, session usage, weekly usage, weekly reset timer, compaction counter the default — every segment populates on an ordinary account
ccstatusline-settings.json worktree, worktree branch, original branch, 5h block reset timer, extra usage used / left you have a spending cap configured and let Claude Code manage your worktrees

The extra-usage-* widgets read n/a without a spending cap on the account, and the worktree-branch / worktree-original-branch widgets need Claude Code to be managing the worktree — so on a plain account the second variant leaves most of line 3 empty. See Widgets that may render nothing below.

Both variants keep six columns per line, and that constraint drives the whole layout. With autoAlign on, every column is padded to the widest entry in that column across all three lines, so one long segment inflates the column and opens a gap on the two lines that have something short in the same position. Two rules follow:

  • Equal widget counts per line. A seventh widget on one line leaves the others with an empty cell in that column, and the padding shows up as a void rather than a gap.
  • Put the widest segment in column 1. Column 1 already holds the working directory and the model name, so it absorbs a long value; the same widget parked in a trailing column sits opposite short ones like (no PR) and tears a hole in the row. The account email is the widest thing here, which is why it leads line 3 instead of closing it.

Powerline enabled with the nord-aurora theme, rounded caps, truecolor, and column auto-alignment so segments line up vertically across the three lines.

Requirements

  • A Nerd Font in your terminal. Without one the powerline separators render as tofu.
  • Truecolor terminal support (the config uses colorLevel: 3; drop to 2 for 256 colors).

Install

Back up whatever you have first:

cp ~/.config/ccstatusline/settings.json ~/.config/ccstatusline/settings.json.bak-$(date +%Y%m%d-%H%M%S)

Then drop the new config in — pick the variant from the table above:

# usage variant (recommended)
mkdir -p ~/.config/ccstatusline && curl -fsSL https://gist.githubusercontent.com/fabn/47b4bca04e9258c3f78040201d2ffc38/raw/ccstatusline-settings-usage.json -o ~/.config/ccstatusline/settings.json

# worktree variant
mkdir -p ~/.config/ccstatusline && curl -fsSL https://gist.githubusercontent.com/fabn/47b4bca04e9258c3f78040201d2ffc38/raw/ccstatusline-settings.json -o ~/.config/ccstatusline/settings.json

Wire it into Claude Code in ~/.claude/settings.json if it isn't already:

{
  "statusLine": {
    "type": "command",
    "command": "npx -y ccstatusline@latest",
    "padding": 0,
    "refreshInterval": 10
  }
}

The status line picks the new config up on the next refresh — no restart needed.

Restore

cp ~/.config/ccstatusline/settings.json.bak-<timestamp> ~/.config/ccstatusline/settings.json

Verify without waiting

Render it against a fake payload:

echo '{"session_id":"t","cwd":"'$PWD'","model":{"id":"claude-opus-5","display_name":"Opus 5"},"workspace":{"current_dir":"'$PWD'"}}' \
  | CCSTATUSLINE_WIDTH=150 npx -y ccstatusline@latest

CCSTATUSLINE_WIDTH forces the render width, which matters because flex layout depends on it.

That minimal payload is enough to check colours and separators, but the context, token and compaction widgets read the session transcript and will show [Timeout], n/a or nothing without it. To exercise those, point transcript_path at a real session log:

TP=$(ls -t ~/.claude/projects/*/*.jsonl | head -1)
echo '{"session_id":"'$(basename "$TP" .jsonl)'","transcript_path":"'$TP'","cwd":"'$PWD'","model":{"id":"claude-opus-5","display_name":"Opus 5"},"workspace":{"current_dir":"'$PWD'"}}' \
  | CCSTATUSLINE_WIDTH=200 npx -y ccstatusline@latest

Do not read a [Timeout] from the minimal payload as a broken widget — re-test with a transcript before changing the config.

Note that neither XDG_CONFIG_HOME nor CLAUDE_CONFIG_DIR redirects where ccstatusline reads its settings — it resolves the path from the home directory. There is no way to test an alternative config in isolation, which is why the backup step above matters.

Gotcha: the powerline glyphs

powerline.separators, startCaps and endCaps hold Private Use Area characters:

Field Codepoint Shape
separators U+E0B4 right half circle
startCaps U+E0B6 left half circle
endCaps U+E0B4 right half circle

These do not survive copy/paste through most browsers and chat clients — they get silently stripped to "", and powerline then renders with no separators at all. Always download the JSON with curl rather than pasting it. If you do end up with empty strings, put them back by codepoint:

python3 - <<'EOF'
import json, os
p = os.path.expanduser('~/.config/ccstatusline/settings.json')
c = json.load(open(p))
c['powerline']['separators'] = [""]
c['powerline']['startCaps']  = [""]
c['powerline']['endCaps']    = [""]
json.dump(c, open(p, 'w'), indent=2, ensure_ascii=False)
EOF

Tuning

Everything below is editable from the interactive TUI (npx -y ccstatusline@latest), which is easier than hand-editing JSON.

Setting Value here Notes
flexMode full-until-compact full terminal width minus 6, dropping to minus 40 once context use crosses compactThreshold. Other values: full, full-minus-40. There is no percentage width option.
compactThreshold 60 the context-usage percentage that triggers the narrower width above
powerline.autoAlign true pads each column to the widest entry in that column across lines, so boundaries line up. The padding is inside the segment, so it is coloured, not a gap. Toggle with a on the Powerline screen.
current-working-dirsegments 1 shows only the last path component (~/.../project). Raise it to widen. Note ~ counts as a component. Keys h / s / f on the widget.
git-branchmaxWidth 20 truncates long branch names with an ellipsis so they cannot dominate the line. Only some widgets support maxWidthgit-branch, git-root-dir, git-untracked-files, git-is-fork, output-style, custom-command.
claude-account-email column 1 of line 3 worth having to tell a personal account apart from a work one. It is the widest segment in the layout, so it goes first, where the working directory and the model name already make the column wide — see the column rules above. Drop it and the whole grid narrows.
compaction-counter ↻ 0 counts compact_boundary records in the session transcript, i.e. how many times the context was compacted. metric also accepts auto, manual, unknown and reclaimed (tokens recovered); format switches between ↻ N, Compactions: N and a bare number. Set hideZero to make it disappear until the first compaction.
reset-timer 2h3m despite the generic name this is the block reset timer — time left in the current 5-hour usage window. Its sibling block-timer shows time elapsed in the same window, so the two disagree by design; putting both on a line is confusing rather than redundant.
git-worktreehideNoGit true shows which working tree you are in, derived from git rev-parse --git-dir rather than from the payload, so it works with hand-made worktrees. Two states worth knowing: main is a hardcoded literal meaning "the main working tree" — not the branch, and it stays main while you are on feature/x. Anything else is the worktree's git name, normally the directory basename, again not the branch. hideNoGit suppresses the no git placeholder outside a repo.

There is no minimum-width setting for a widget or a column. Column width is purely a function of the widest content in that column.

Widgets that may render nothing

Several widgets hide themselves when their data source is empty, so the line silently gets shorter rather than showing a placeholder. That is expected, not a broken config:

  • git-ci-status and git-review need a PR on the current branch (and the gh CLI authenticated). They show - and (no PR) otherwise.
  • extra-usage-remaining needs a spending cap to be configured on the account; with no cap there is nothing to subtract from and the widget disappears.
  • the worktree-mode, worktree-name, worktree-branch and worktree-original-branch widgets read a worktree object out of the JSON payload Claude Code sends on stdin — they do not ask git. A worktree you created yourself with git worktree add does not put that object in the payload, so all four render nothing even while you are sitting in the worktree. They light up when Claude Code is managing the worktree itself. Use git-worktree instead if you make your worktrees by hand — see below.
  • remote-control-status renders only when remote control state exists for the session.
  • session-cost, session-name and fable-weekly-usage render nothing at all on a plain account; cache-hit-rate reads Cache Hit: n/a.
  • weekly-opus-usage and weekly-sonnet-usage sit at a permanent 0.0% even while weekly-usage climbs. These map to the seven_day_opus / seven_day_sonnet rate-limit fields, which are nullable in the payload schema where plain seven_day is not — they are per-model windows that not every plan populates. Prefer the aggregate weekly-usage.

session-usage, weekly-usage and weekly-reset-timer, on the other hand, do populate on an ordinary individual account — no team or org membership needed. If line 3 of the worktree variant looks dead, that is the worktree and spending-cap widgets above, not a quota-data limitation. Switch to the usage variant rather than giving up on the row.

Export / import from the TUI

npx -y ccstatusline@latestExport Config writes the current in-memory config to ~/ccstatusline-config.json, including unsaved edits. Import Config validates, migrates older schema versions, previews the effective diff, then applies either Replace All (omitted fields go back to defaults) or Merge (only the fields present in the file are overlaid). Both preserve machine-local installation metadata.

{
"version": 3,
"lines": [
[
{
"id": "l1w1",
"type": "current-working-dir",
"color": "gradient:atlas",
"rawValue": true,
"metadata": {
"abbreviateHome": "true",
"segments": "1"
}
},
{
"id": "l1w2",
"type": "git-origin-owner-repo"
},
{
"id": "l1w3",
"type": "git-branch",
"maxWidth": 20
},
{
"id": "l1w4",
"type": "git-changes"
},
{
"id": "l1w5",
"type": "git-ci-status"
},
{
"id": "l1w6",
"type": "git-review"
}
],
[
{
"id": "l2w1",
"type": "model",
"bold": true,
"rawValue": true
},
{
"id": "l2w2",
"type": "thinking-effort",
"bold": true,
"rawValue": true
},
{
"id": "l2w3",
"type": "context-bar",
"bold": false,
"rawValue": true,
"metadata": {
"display": "progress-short"
}
},
{
"id": "l2w4",
"type": "tokens-input",
"rawValue": false
},
{
"id": "l2w5",
"type": "tokens-cached",
"rawValue": false
},
{
"id": "l2w6",
"type": "tokens-output",
"rawValue": false
}
],
[
{
"id": "l3w1",
"type": "claude-account-email"
},
{
"id": "l3w2",
"type": "reset-timer",
"rawValue": true,
"metadata": {
"display": "time",
"compact": "true"
}
},
{
"id": "l3w3",
"type": "session-usage"
},
{
"id": "l3w4",
"type": "weekly-usage"
},
{
"id": "l3w5",
"type": "weekly-reset-timer"
},
{
"id": "l3w6",
"type": "compaction-counter"
}
]
],
"flexMode": "full-until-compact",
"compactThreshold": 60,
"colorLevel": 3,
"inheritSeparatorColors": false,
"globalBold": false,
"gitCacheTtlSeconds": 5,
"minimalistMode": false,
"defaultPaddingSide": "both",
"defaultPadding": " ",
"powerline": {
"enabled": true,
"separators": [
""
],
"separatorInvertBackground": [
false
],
"startCaps": [
""
],
"endCaps": [
""
],
"autoAlign": true,
"continueThemeAcrossLines": false,
"theme": "nord-aurora"
}
}
{
"version": 3,
"lines": [
[
{
"id": "l1w1",
"type": "current-working-dir",
"color": "gradient:atlas",
"rawValue": true,
"metadata": {
"abbreviateHome": "true",
"segments": "1"
}
},
{
"id": "l1w2",
"type": "git-origin-owner-repo"
},
{
"id": "l1w3",
"type": "git-branch",
"maxWidth": 20
},
{
"id": "l1w4",
"type": "git-changes"
},
{
"id": "l1w5",
"type": "git-ci-status"
},
{
"id": "l1w6",
"type": "git-review"
}
],
[
{
"id": "l2w1",
"type": "model",
"bold": true,
"rawValue": true
},
{
"id": "l2w2",
"type": "thinking-effort",
"bold": true,
"rawValue": true
},
{
"id": "l2w3",
"type": "context-bar",
"bold": false,
"rawValue": true,
"metadata": {
"display": "progress-short"
}
},
{
"id": "l2w4",
"type": "tokens-input",
"rawValue": false
},
{
"id": "l2w5",
"type": "tokens-cached",
"rawValue": false
},
{
"id": "l2w6",
"type": "tokens-output",
"rawValue": false
}
],
[
{
"id": "l3w1",
"type": "git-worktree",
"metadata": {
"hideNoGit": "true"
}
},
{
"id": "l3w2",
"type": "worktree-branch"
},
{
"id": "l3w3",
"type": "worktree-original-branch"
},
{
"id": "l3w4",
"type": "reset-timer",
"rawValue": true,
"metadata": {
"display": "time",
"compact": "true"
}
},
{
"id": "l3w5",
"type": "extra-usage-used"
},
{
"id": "l3w6",
"type": "extra-usage-remaining"
}
]
],
"flexMode": "full-until-compact",
"compactThreshold": 60,
"colorLevel": 3,
"inheritSeparatorColors": false,
"globalBold": false,
"gitCacheTtlSeconds": 5,
"minimalistMode": false,
"defaultPaddingSide": "both",
"defaultPadding": " ",
"powerline": {
"enabled": true,
"separators": [
""
],
"separatorInvertBackground": [
false
],
"startCaps": [
""
],
"endCaps": [
""
],
"autoAlign": true,
"continueThemeAcrossLines": false,
"theme": "nord-aurora"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment