Status line · Claude Code
How much Claude Code have you got left?
Two lines under your prompt: both rate-limit windows, context used, prompt size, cache hit rate and session cost. Written in Go — it reads Claude Code's JSON on stdin, prints, and exits.
🧠 Opus 5·1M │ 📁 claude-status-line-go │ 🌿 feature/cache-stats ●3
🟡5h ░░░░░░░░░░ 0% ↺ 5h0m │ CTX ░░░░░░░░░░ 0% │ Σ0 ↓0 ⚡0% │ 📅7d ░░░░░░░░░░ 0% ↺ 2d4h │ $0.00
go install github.com/williamokano/claude-status-line-go/cmd/claude-status-line-go@latest
claude-status-line-go install
Anatomy
Every glyph is doing a job.
Eight segments, left to right. Turn any of them off — the ones you keep close up the gaps.
Model
The model Claude Code reports, then its context window — 200k or 1M. It survives a mid-turn model switch instead of going blank.
Project
The working directory's folder name, so a second window is never a guess.
Branch
Current branch, and ●3 for three files with uncommitted changes. Hide the dot count on its own with show_git_dirty.
5-hour window
How much of the rolling five-hour limit you've spent, and how long until it resets.
Context
Share of the context window in use. Its own thresholds, separate from the rate limits — ctx_warn and ctx_crit.
Tokens
Σ the whole prompt, ↓ tokens generated, ⚡ the share of the prompt served from cache.
Σ sums all three input fields — Claude Code's input_tokens counts only what missed the cache,
so on a warm conversation it reads as a couple of tokens while the real prompt is 100k+.
Weekly window
Same shape as the five-hour window, same colors. It stays out of the way until it hits weekly_show_at — 60% by default — and its countdown switches to 2d4h form once the reset is more than a day out.
Cost
What this session has cost so far.
Thresholds
Green until it isn't.
Every bar crosses the same two lines. Both rate-limit windows share
limit_warn and
limit_crit;
context has its own pair. Move them wherever your nerve sits.
Under 60%
Plenty of room. Nothing to do.
60% — limit_warn
Past the warning line. Worth knowing before you start something large.
85% — limit_crit
Close to the ceiling. Check the reset countdown next to the bar.
Configure
Change what it shows.
A config file, an environment variable, or a format string — whichever fits how you already manage your shell. Environment variables win over the file.
Don't go hunting for the path
claude-status-line-go config init
nvim $(claude-status-line-go config path)
config init writes a fully commented file — every option, plus two
ready-to-uncomment plugin examples. It's all already the default, so an untouched
file changes nothing, and it won't overwrite an existing config without
--force. config path prints the location on its own,
which is why the second line works.
# ~/.config/claude-status-line-go/claude-status-line.yaml show_cost: true show_weekly: true show_tokens: true show_git: true show_git_dirty: true bar_size: 10 # characters per progress bar limit_warn: 60 # 5-hour and weekly limit_crit: 85 ctx_warn: 60 ctx_crit: 85 weekly_show_at: 60 # reveal the weekly bar at this %
Plugins live in this file too. Precedence runs
defaults, then this file, then the environment — so a
CSL_ variable always wins.
Colors also switch off with no_color, the --no-color flag, or a NO_COLOR environment variable.
- CSL_SHOW_COST
- true
- CSL_SHOW_WEEKLY
- true
- CSL_SHOW_TOKENS
- true
- CSL_SHOW_GIT
- true
- CSL_SHOW_GIT_DIRTY
- true
- CSL_BAR_SIZE
- 10
- CSL_LIMIT_WARN
- 60
- CSL_LIMIT_CRIT
- 85
- CSL_CTX_WARN
- 60
- CSL_CTX_CRIT
- 85
- CSL_WEEKLY_SHOW_AT
- 60
- CSL_FORMAT
- —
- NO_COLOR
- —
# CSL_FORMAT, or the `format` field in the config file "{cyan}🧠 {model}·{ctx_size}{reset} {dim}│ 📁 {project}{reset} {dim}│ 🌿 {branch}{reset}{dirty}"
Placeholders
{model}{ctx_size}{project}{branch}{dirty} {limit_bar}{limit_pct}{limit_color}{limit_reset} {ctx_bar}{ctx_pct}{ctx_color} {tokens_total}{tokens_out}{cache_hit_pct}{tokens_in}{tokens_cache} {weekly_bar}{weekly_pct}{weekly_color}{weekly_reset} {cost} {reset}{dim}{bold} {red}{green}{yellow}{blue}{magenta}{cyan}{white}
Every configured plugin adds {plugin.<name>}
for its finished segment, plus .value,
.max,
.pct,
.bar,
.text,
.label and any extra key it reported.
Plugins
Add your own segments.
A plugin reports data — a value, an optional maximum, maybe a label — and
this tool draws it. So your segment gets the same glyphs, the same
bar_size and the same
NO_COLOR handling as
everything else on the line.
🧠 Opus 5·1M │ 📁 claude-status-line-go │ 🌿 main
🟡5h ████░░░░░░ 41% ↺ 3h2m │ CTX ███░░░░░░░ 32% │ 🎯 ███░░░░░░░ 31/100 │ $2.14
Roughly this
plugins: - name: issues command: >- printf '{"value":%s,"max":100}' "$(gh issue list --json number --jq 'length')" interval: 60s icon: "🎯" bar: true
A command never runs while the status line is drawing — its result is
cached and refreshed behind you, so a 350 ms
gh call costs the line nothing.
Read the full plugin guide → — both source kinds, the
output contract, thresholds, hide_when,
a worked example, what a slow command does turn by turn, and how to debug one
that won't show up.
Install
Two commands, or one download.
Both paths end in the same place. Pick the one that matches what's already on your machine.
With Go
go install github.com/williamokano/claude-status-line-go/cmd/claude-status-line-go@latest
claude-status-line-go install
The second command writes the statusLine entry into
~/.claude/settings.json and leaves everything else in that file
alone. Add --project to register it in the current repo's
.claude/settings.json instead.
@latest resolves through the Go module proxy, so a freshly pushed
tag can take a few minutes to appear. Check what you got with
--version.
No Go toolchain
curl -fsSL https://okano.dev/claude-status-line-go/install.sh | sh
Works out your OS and architecture, resolves the newest release, checks the
SHA-256 against checksums.txt, and installs to
/usr/local/bin — or ~/.local/bin if that isn't
yours to write to. It never calls sudo.
Read it first if you'd rather.
Pin a version or pick the destination:
curl -fsSL … | VERSION=1.4.0 INSTALL_DIR=~/bin sh
Prefer not to pipe into a shell?
OS=$(uname -s | tr '[:upper:]' '[:lower:]') ARCH=$(uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/') VERSION=$(curl -fsSLI -o /dev/null -w '%{url_effective}' \ https://github.com/williamokano/claude-status-line-go/releases/latest | sed 's#.*/v##') FILE="claude-status-line-go_${VERSION}_${OS}_${ARCH}.tar.gz" BASE="https://github.com/williamokano/claude-status-line-go/releases/download/v${VERSION}" curl -fsSLO "${BASE}/${FILE}" curl -fsSLO "${BASE}/checksums.txt" grep " ${FILE}$" checksums.txt | sha256sum -c - # macOS: shasum -a 256 -c - tar -xzf "$FILE" claude-status-line-go sudo install -m 755 claude-status-line-go /usr/local/bin/
$ErrorActionPreference = 'Stop' $ProgressPreference = 'SilentlyContinue' # or Invoke-WebRequest crawls $Repo = 'williamokano/claude-status-line-go' $Version = (Invoke-RestMethod "https://api.github.com/repos/$Repo/releases/latest").tag_name.TrimStart('v') $Arch = if ($env:PROCESSOR_ARCHITECTURE -eq 'ARM64') { 'arm64' } else { 'amd64' } $File = "claude-status-line-go_${Version}_windows_${Arch}.tar.gz" $Base = "https://github.com/$Repo/releases/download/v$Version" Invoke-WebRequest "$Base/$File" -OutFile $File Invoke-WebRequest "$Base/checksums.txt" -OutFile checksums.txt $want = (Select-String checksums.txt -Pattern ([regex]::Escape($File))).Line.Split(' ')[0] $got = (Get-FileHash $File -Algorithm SHA256).Hash.ToLower() if ($want -ne $got) { throw 'checksum mismatch' } $Dest = "$env:LOCALAPPDATA\Programs\claude-status-line-go" New-Item -ItemType Directory -Force -Path $Dest | Out-Null tar -xzf $File -C $Dest claude-status-line-go.exe
The Windows archive holds claude-status-line-go.exe.
tar ships with Windows 10 build 17063 and later, so nothing
else is needed. Add $Dest to your PATH to finish.
There's no Chocolatey or WinGet package — both want a published, moderated manifest rather than a link to a release.
Shell completions print to stdout for bash, zsh and fish:
eval "$(claude-status-line-go --completion zsh)"