Claude Code Status Line: Setup, a Working Script, and the Best Tools

When you work in Claude Code all day, two questions keep coming up: which model is this session on, and how full is the context? The status line answers both without typing anything.

The best Claude Code status line options are your own short bash script, statusline-pro, ccstatusline and statusline.sh. Pick the script to keep it tiny, statusline-pro for quiet defaults, ccstatusline for maximum control, and statusline.sh to design it visually.

This guide covers the setting, the data you get, a script you can copy, and the tools that do the work for you. One disclosure up front: I wrote statusline-pro, one of the tools in the comparison. I say where it fits and where it does not.

What is the Claude Code status line?

The Claude Code status line is a customizable bar at the bottom of the terminal UI. Claude Code runs a shell command you configure, pipes JSON session data (model, context usage, cost, directory) to it on stdin, and shows whatever the command prints. It runs locally and uses no API tokens.

It renders in its own row above the built-in footer badges. Per the docs, once a custom status line is configured, Claude Code stops showing most of the footer keyboard hints, such as esc to interrupt and ? for shortcuts.

Claude Code Statusline Setup: /statusline vs the statusLine Setting

You have two options: let Claude write it, or configure it yourself.

The /statusline command. Describe what you want in plain language and Claude Code generates a script in ~/.claude/ and updates your settings:

text
/statusline show model name and context percentage with a progress bar

The statusLine setting. Add a statusLine field to ~/.claude/settings.json (user settings) or to project settings:

json
{
  "statusLine": {
    "type": "command",
    "command": "~/.claude/statusline.sh",
    "padding": 2
  }
}

type is "command". command is a script path or an inline shell command. Three optional fields are worth knowing:

FieldWhat it does
paddingExtra horizontal spacing in characters. Defaults to 0.
refreshIntervalRe-runs the command every N seconds (minimum 1), on top of event-driven updates. Use it for clocks, or when background subagents change git state while the main session is idle.
hideVimModeIndicatorSet to true to hide the built-in -- INSERT -- text when your script renders vim.mode itself.

Make your script executable with chmod +x ~/.claude/statusline.sh, or the status line will not appear.

If you want to remove it later, delete the statusLine entry from settings.

What JSON does the status line script receive?

Claude Code sends one JSON object on stdin each time the script runs. These are the fields I use most, taken from the official status line docs:

FieldMeaning
model.id, model.display_nameCurrent model ID and display name
workspace.current_dir, workspace.project_dirWorking directory, and the directory Claude Code was launched from
workspace.git_worktreeLinked worktree name, absent in the main working tree
cost.total_cost_usdEstimated session cost in USD, computed client-side at list price, so it can differ from your bill
cost.total_duration_msWall-clock session time in milliseconds
cost.total_lines_added, cost.total_lines_removedLines of code changed
context_window.used_percentagePre-calculated percentage of the context window used
context_window.remaining_percentagePre-calculated percentage remaining
context_window.context_window_sizeMax window in tokens (200000 by default, 1000000 for extended-context models)
rate_limits.five_hour.used_percentage, rate_limits.seven_day.used_percentageShare of the 5-hour and 7-day limits consumed (0 to 100)
rate_limits.five_hour.resets_atUnix epoch seconds when the 5-hour window resets
effort.levelReasoning effort, absent if the model does not support it
session_id, session_nameSession identifiers
worktree.name, worktree.branchPresent only in a Claude Code worktree session
pr.number, pr.url, pr.review_stateOpen pull request for the current branch, when one exists

The docs list more, including prompt cache statistics and vim mode. Two warnings from the docs matter in practice. Some fields can be missing or null early in a session, so give every value a fallback. And rate_limits only appears for claude.ai Pro and Max subscribers (or behind a Claude apps gateway with a spend limit), and only after the first API response, so treat it as optional.

There is no git branch field in this list. You get the branch by running git from your script, which the next section does.

A minimal status line script (bash)

This script shows model, a context bar, session cost and the git branch. It needs jq and git.

bash
#!/bin/bash
input=$(cat)

MODEL=$(echo "$input" | jq -r '.model.display_name // "?"')
DIR=$(echo "$input" | jq -r '.workspace.current_dir // .cwd // "."')
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')

BAR_WIDTH=10
FILLED=$((PCT * BAR_WIDTH / 100))
EMPTY=$((BAR_WIDTH - FILLED))
BAR=""
[ "$FILLED" -gt 0 ] && printf -v FILL "%${FILLED}s" && BAR="${FILL// /▓}"
[ "$EMPTY" -gt 0 ] && printf -v PAD "%${EMPTY}s" && BAR="${BAR}${PAD// /░}"

BRANCH=$(git -C "$DIR" branch --show-current 2>/dev/null)
GIT_PART=""
[ -n "$BRANCH" ] && GIT_PART=" | $BRANCH"

printf '[%s] %s %s%% | $%.2f%s\n' "$MODEL" "$BAR" "$PCT" "$COST" "$GIT_PART"

The bar logic is adapted from the example in the official docs. Save it as ~/.claude/statusline.sh, run chmod +x on it, and point the statusLine setting at it. Output looks like this:

text
[Opus] ▓▓▓░░░░░░░ 32% | $1.47 | feat/status-line

A few things the docs call out:

  • Use // 0 fallbacks in jq. Fields can be null before the first API response.
  • Claude Code captures your output, so tput cols does not work. Read the COLUMNS and LINES environment variables instead.
  • Each echo or printf line becomes its own row, so a two-line status line is just two prints.
  • ANSI color codes work if your terminal supports them. OSC 8 links work in terminals like iTerm2, Kitty and WezTerm.
  • Updates are debounced at 300ms, and a script still running when a new update fires gets cancelled. Keep it fast. The docs warn that commands like git status can cause lag and show a caching example. git branch --show-current is cheap. A network call on every update is not.

If you would rather write it in TypeScript with Bun, the logic is identical: read stdin, JSON.parse it, print a string. Bash with jq has the lowest startup cost, which matters when the script runs after every assistant message.

What should a Claude Code status line show?

Every extra segment costs screen width and attention. This is what I think earns its place:

SegmentWhy it mattersSource
ModelYou switch models mid-session. Easy to forget which one is active.model.display_name
Context %The best early warning that a session is about to get worse or need /compact.context_window.used_percentage
CostUseful if you pay per token. Treat it as an estimate.cost.total_cost_usd
Git branchEssential with parallel sessions and worktrees.git branch --show-current
Rate limitsMatters on a subscription plan, where the 5-hour window is the real constraint.rate_limits.*
WorktreeTells you which isolated checkout a session lives in.worktree.name or workspace.git_worktree

I would skip a clock unless you need one, since it forces a refreshInterval. I would also skip token counts when you already show a percentage.

The git branch point connects to something I wrote about separately: running several agents in parallel gets confusing fast. If you do that, see my git worktree guide and background coding agents for how I keep them apart.

Claude Code status line tools compared

You do not have to write the script. Three options come up most, plus the DIY route. I checked each on its GitHub page.

ToolHow you configure itInstallGood forTrade-off
Your own scriptEdit a bash fileCopy the script aboveTotal control, tiny footprintYou maintain it
statusline-proSettings pane with live preview, per-segment auto, always or neverPlugin marketplace (below)Defaults that hide noise, drop-in badge folderPlugin-based, newer project, author's own
ccstatuslineInteractive terminal UInpx -y ccstatusline@latest or bunx -y ccstatusline@latestMost customization, multiple lines, Powerline themesNeeds Node.js or Bun, more to configure
statusline.shBrowser-based visual builderPaste one command from the siteDesigning the bar visuallyBrowser-based, so your layout lives in a generated script

statusline-pro

This is mine, so weigh my opinion accordingly. It is a Claude Code status line that shows model, reasoning effort, context, 5-hour rate limit, 7-day rate limit, directory, git and cost. Each segment has a show_<id> setting with auto, always or never. In auto mode the 7-day limit only appears once usage reaches 60% by default, and cost only appears at $1 by default, so the line stays short until something matters. Gauges change color at 60% and 85% usage (50% and 80% for context).

It also has a drop-in folder. Any script or mod can write a JSON file to ~/.claude/status-segments/<name>.json with a text field (plus optional label and color) and it shows up as a badge. The folder is re-read every 10 seconds and after each turn.

Install from inside Claude Code:

text
/plugin marketplace add alexcloudstar/statusline-pro
/plugin install statusline-pro@statusline-pro

Or from a terminal:

bash
claude plugin marketplace add alexcloudstar/statusline-pro
claude plugin install statusline-pro@statusline-pro

Open the settings pane with /statusline-pro settings. The README notes it requires a Claude Code build that supports mods, and that it does not remove an existing statusLine entry from settings.json, so delete that entry if you want only one status line. More on the statusline-pro project page and in the repo.

ccstatusline

ccstatusline is the most configurable option I know of. You set it up through an interactive terminal UI, with widgets you can add, remove and reorder across multiple lines. It has widgets for git (branch, status, PR and CI), usage and reset timers, context, cache, cost and Claude service status, plus Powerline mode with themes. Settings are saved to ~/.config/ccstatusline/settings.json. Run it with npx -y ccstatusline@latest or bunx -y ccstatusline@latest. Some widgets need newer Claude Code versions (its README says the refresh interval setting needs 2.1.97 or later), and the PR and CI widgets rely on the GitHub or GitLab CLI.

If you want a status line that looks exactly how you imagine it, start here.

statusline.sh

statusline.sh is a visual builder. You design the bar in the browser, preview it, then paste one command to install on macOS, Linux or Windows. According to its repo, it merges into your existing settings.json and makes a timestamped backup first. It is a good fit if you think visually and do not want to write jq. Note that many unrelated repos also ship a file called statusline.sh, so check which one you are looking at.

Which should you pick?

  • Want to understand how it works, or keep it tiny: write the bash script.
  • Want sensible defaults that stay quiet until a limit gets close: statusline-pro.
  • Want maximum control over layout and themes: ccstatusline.
  • Want to design it by dragging things around: statusline.sh.

None of them is a clear winner for everyone. They solve slightly different problems.

Common status line problems

The official docs have a troubleshooting section. The ones I see most:

  • Nothing shows up. Check that the script is executable and that the path in command is correct. The status line also hides temporarily during some UI interactions such as the help menu and permission prompts.
  • Workspace trust. The status line command does not run until you have accepted the workspace trust dialog for the directory.
  • Values show -- or empty. A field was null before the first API response. Add // 0 or another fallback in jq.
  • It goes blank. The output only shows if the script exits with code 0 and prints something. Run it by hand with mock input: echo '{"model":{"display_name":"Opus"}}' | ./statusline.sh.
  • The script feels slow. Remember it is cancelled and restarted on new updates. Cache anything expensive.
  • Colors look wrong. Your terminal may not support the escape codes you used, or output got garbled by long multi-line escapes. Simplify and test.

Frequently Asked

What is the Claude Code status line? A bar at the bottom of the Claude Code interface that displays the output of a shell command you configure. Claude Code passes session data as JSON on stdin, and your script prints what it should show.

How do I enable a custom status line in Claude Code? Add a statusLine object with type: "command" and a command to ~/.claude/settings.json, or run /statusline and describe what you want. Claude Code then generates the script and updates the settings.

Does the Claude Code status line use tokens? No. The docs state it runs locally and does not consume API tokens.

How do I show context usage in the status line? Read context_window.used_percentage from the JSON on stdin. context_window.remaining_percentage is also available.

How do I show the git branch? The JSON has no branch field, so run git branch --show-current in your script, using workspace.current_dir as the directory. If you use Claude Code worktree sessions, worktree.branch gives the worktree branch.

How often does the status line update? It runs when a session starts, after each assistant message, after /compact, when the permission mode changes, and when vim mode toggles. Updates are debounced at 300ms. Set refreshInterval to also run it on a timer.

Can the status line have multiple lines or colors? Yes. Each printed line is a row, and ANSI escape codes set colors if your terminal supports them.

Why is the cost different from my bill? cost.total_cost_usd is an estimate computed on the client at list price, so it can differ from what you are actually charged.

Is statusline-pro free? Yes, it is a free open-source project. I wrote it, so I am biased, and ccstatusline or your own script may suit you better.

Sources