Usage, limits and cost

Claude Code Status Line: Setup and 8 Statusline Examples

Set up the Claude Code status line in two minutes, see every field it receives, and copy 8 statusline examples for context, cost, git and plan limits.

On this page
  1. Set up the status line
  2. What data the script receives
  3. When the status line updates
  4. 8 Claude Code statusline examples
  5. Test a script before you use it
  6. On Windows
  7. Status line not showing?
  8. When one terminal isn't enough
  9. FAQ

The Claude Code status line is a bar at the bottom of the session that runs a script you choose and shows whatever it prints. Claude Code sends the script JSON about the session (model, context use, cost, git worktree, and on Pro and Max your 5-hour and weekly limits), so a few lines of Bash give you a live dashboard. The quickest setup is /statusline plus a description of what you want; the eight statusline examples below are complete scripts you can paste instead.

Everything here follows the official status line docs, checked on October 3, 2026. Each script below was run against sample JSON with Bash and jq 1.7 on Linux; they use nothing macOS-specific, but they haven't been run on a Mac.

Set up the status line

The quick way: /statusline

Type /statusline and describe what you want:

Text
/statusline show model name, context percentage with a progress bar, and session cost

Claude Code writes a script in ~/.claude/ and updates your settings. Approve the file edits when it asks. Run /statusline with no arguments to build one from your shell prompt, and /statusline remove it to turn it off.

By hand

  1. Save a script as ~/.claude/statusline.sh (any of the examples below).
  2. Make it executable with chmod +x ~/.claude/statusline.sh.
  3. Add the statusLine setting:
~/.claude/settings.json
{
  "statusLine": {
    "type": "command",
    "command": "~/.claude/statusline.sh",
    "padding": 0
  }
}

Claude Code reloads settings automatically and runs the new command right away. The Bash examples need jq (brew install jq on macOS, your package manager on Linux). Optional fields: padding adds spaces of indentation, refreshInterval re-runs the command every N seconds (minimum 1) for clocks and other time-based output, and hideVimModeIndicator hides the built-in -- INSERT -- text if your script shows the vim mode itself. A project's .claude/settings.json can set its own status line too.

What data the script receives

The script gets one JSON object on stdin each time it runs. The fields you'll use most:

FieldWhat it holds
model.display_name, model.idCurrent model, such as Opus and claude-opus-5-5
workspace.current_dir, workspace.project_dirCurrent folder and the folder Claude Code started in
context_window.used_percentageHow full the context window is (input tokens only); can be null early on
context_window.context_window_size200000, or 1000000 for models with a 1M window
cost.total_cost_usdEstimated session cost at API list price; resets on /clear
cost.total_duration_msWall-clock time of the session
cost.total_lines_added, cost.total_lines_removedLines changed this session
rate_limits.five_hour.used_percentage, .resets_atPro and Max: 5-hour limit used, and reset time in epoch seconds
rate_limits.seven_day.used_percentage, .resets_atPro and Max: weekly limit used, and reset time
prompt_cache.warm, .hit_ratio, .expires_atWhether the prompt cache is warm, its hit ratio, when it goes cold (v2.1.251+)
effort.levellow to max; absent on models without effort
session_id, session_nameUnique ID and the session's name or AI title
pr.number, pr.review_stateOpen pull request for the branch, when there is one
worktree.name, worktree.branchPresent in a worktree session
transcript_path, versionPath to the session transcript and the Claude Code version
The Claude Code status line docs table of available data fields such as model.display_name, workspace.current_dir and cost.total_cost_usd
The start of the field list in the status line docs.

Several fields are missing until they apply: rate_limits appears only for Pro and Max subscribers (or behind a gateway with a spend limit) and only after the first response; pr, worktree, effort and vim appear only when relevant. Always read them with a fallback, such as // empty or // 0 in jq. The full schema lists every field.

When the status line updates

The script runs when a session starts or resumes, after each new assistant message, after /compact, when the permission mode or vim mode changes, when a rate-limit window reaches its reset time, and when a warm prompt cache reaches its expiry. Updates are debounced at 300 ms, and if a new update arrives while your script is still running, the old run is cancelled. Add refreshInterval if you need updates while the session is idle.

The status line runs locally and doesn't consume API tokens. It hides during some UI moments, such as permission prompts. Because Claude Code captures the output, tput cols can't see the terminal width; read the COLUMNS environment variable instead.

8 Claude Code statusline examples

1. Model and context, in one line of settings

No script file needed. jq reads stdin directly:

~/.claude/settings.json
{
  "statusLine": {
    "type": "command",
    "command": "jq -r '\"[\\(.model.display_name)] \\(.context_window.used_percentage // 0 | floor)% context\"'"
  }
}

Output: [Opus] 18% context.

2. Context bar with warning colors

A ten-block bar that turns yellow at 70% and red at 90%, so you know when to /compact or /clear:

~/.claude/statusline.sh
#!/bin/bash
# Model name plus a 10-block context bar that turns yellow at 70% and red at 90%
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0 | floor')

if [ "$PCT" -ge 90 ]; then COLOR='\033[31m'
elif [ "$PCT" -ge 70 ]; then COLOR='\033[33m'
else COLOR='\033[32m'; fi
RESET='\033[0m'

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

printf '%b\n' "[$MODEL] ${COLOR}${BAR}${RESET} ${PCT}% context"

Output: [Opus] █░░░░░░░░░ 18% context, in green.

Terminal output: the context bar script prints a green bar at 18 percent, a yellow bar at 74 percent and a red bar at 93 percent
Run here on Linux: example 2 fed sample JSON at 18%, 74% and 93% context.

3. Session cost, time and lines changed

One jq call, read into variables with @tsv:

~/.claude/statusline.sh
#!/bin/bash
# Session cost (API list price), wall-clock time and lines changed
input=$(cat)
IFS=$'\t' read -r MODEL COST MS ADDED REMOVED < <(echo "$input" | jq -r '[
  .model.display_name,
  (.cost.total_cost_usd // 0),
  (.cost.total_duration_ms // 0),
  (.cost.total_lines_added // 0),
  (.cost.total_lines_removed // 0)
] | @tsv')

MINS=$((MS / 60000))
printf '[%s] $%.2f · %dm · +%s -%s\n' "$MODEL" "$COST" "$MINS" "$ADDED" "$REMOVED"

Output: [Opus] $3.42 · 45m · +156 -23. On an API key the dollar figure approximates your bill. On Pro and Max it's what the session would have cost at API prices, not a charge; our guide to API value versus what you pay explains the difference.

4. Plan limits with the reset time

For Pro and Max: how much of the 5-hour and weekly limits you've used, and when the 5-hour window resets. strflocaltime needs jq 1.6 or later:

~/.claude/statusline.sh
#!/bin/bash
# Pro and Max only: 5-hour and weekly limit usage, with the 5-hour reset time
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
LIMITS=$(echo "$input" | jq -r '
  [ (.rate_limits.five_hour // empty
      | "5h \(.used_percentage | floor)% (resets \(.resets_at | strflocaltime("%H:%M")))"),
    (.rate_limits.seven_day // empty
      | "7d \(.used_percentage | floor)%") ]
  | join(" · ")')

if [ -n "$LIMITS" ]; then
  echo "[$MODEL] $LIMITS"
else
  echo "[$MODEL]"
fi

Output: [Opus] 5h 73% (resets 23:32) · 7d 41%. On an API key it prints just [Opus]. Our guide to the Claude Code 5-hour limit explains how the two windows work.

5. Git branch and changed files, cached

Git commands can be slow in big repos and the script runs often, so this caches the result for five seconds. The cache file is keyed by session_id, which is stable within a session and unique across sessions:

~/.claude/statusline.sh
#!/bin/bash
# Folder, git branch and changed-file count, cached for 5 seconds per session
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
DIR=$(echo "$input" | jq -r '.workspace.current_dir')
SESSION=$(echo "$input" | jq -r '.session_id')

CACHE="/tmp/statusline-git-$SESSION"
AGE=999
if [ -f "$CACHE" ]; then
  MTIME=$(stat -c %Y "$CACHE" 2>/dev/null || stat -f %m "$CACHE" 2>/dev/null || echo 0)
  AGE=$(( $(date +%s) - MTIME ))
fi

if [ "$AGE" -gt 5 ]; then
  if git -C "$DIR" rev-parse --git-dir > /dev/null 2>&1; then
    BRANCH=$(git -C "$DIR" branch --show-current 2>/dev/null)
    CHANGED=$(git -C "$DIR" status --porcelain 2>/dev/null | wc -l | tr -d ' ')
    echo "$BRANCH|$CHANGED" > "$CACHE"
  else
    echo "|" > "$CACHE"
  fi
fi

IFS='|' read -r BRANCH CHANGED < "$CACHE"
if [ -n "$BRANCH" ]; then
  echo "[$MODEL] ${DIR##*/} on $BRANCH ($CHANGED changed)"
else
  echo "[$MODEL] ${DIR##*/}"
fi

Output: [Opus] shop-api on main (3 changed). The stat -c form (Linux) runs before stat -f (macOS) on purpose: the docs point out that on Linux the macOS form prints a filesystem report before failing, which would break the arithmetic.

6. Prompt cache: warm or cold

On a subscription the main conversation's cache lasts an hour; on an API key, five minutes. A message after it goes cold reprocesses the whole conversation. This shows how long you have (requires Claude Code v2.1.251 or later):

~/.claude/statusline.sh
#!/bin/bash
# Prompt cache: warm or cold, minutes until it expires, and the session hit ratio
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
CACHE=$(echo "$input" | jq -r '
  .prompt_cache // empty
  | if .warm and .expires_at then
      "cache warm \(((.expires_at - now) / 60) | floor)m left"
    else "cache cold" end
    + (if .hit_ratio then " · \((.hit_ratio * 100) | floor)% hits" else "" end)')

echo "[$MODEL]${CACHE:+ $CACHE}"

Output: [Opus] cache warm 39m left · 91% hits. Claude Code re-runs the status line when the cache reaches its expiry, so it flips to "cold" on its own.

7. A two-line dashboard

Where you are on the first line; context, cost and plan limits on the second, each colored by how close it is to full:

~/.claude/statusline.sh
#!/bin/bash
# Two lines: where you are, then context, cost and plan limits
input=$(cat)
IFS=$'\t' read -r MODEL DIR PCT COST FIVE WEEK < <(echo "$input" | jq -r '[
  .model.display_name,
  .workspace.current_dir,
  (.context_window.used_percentage // 0 | floor),
  (.cost.total_cost_usd // 0),
  (.rate_limits.five_hour.used_percentage // -1 | floor),
  (.rate_limits.seven_day.used_percentage // -1 | floor)
] | @tsv')

CYAN='\033[36m'; GREEN='\033[32m'; YELLOW='\033[33m'; RED='\033[31m'; RESET='\033[0m'
color() { if [ "$1" -ge 90 ]; then printf '%s' "$RED"; elif [ "$1" -ge 70 ]; then printf '%s' "$YELLOW"; else printf '%s' "$GREEN"; fi; }

BRANCH=$(git -C "$DIR" branch --show-current 2>/dev/null)
LINE1="${CYAN}[$MODEL]${RESET} ${DIR##*/}${BRANCH:+ on $BRANCH}"

LINE2="$(color "$PCT")ctx ${PCT}%${RESET} · $(printf '$%.2f' "$COST")"
[ "$FIVE" -ge 0 ] && LINE2="$LINE2 · $(color "$FIVE")5h ${FIVE}%${RESET}"
[ "$WEEK" -ge 0 ] && LINE2="$LINE2 · $(color "$WEEK")7d ${WEEK}%${RESET}"

printf '%b\n' "$LINE1" "$LINE2"

Output:

Text
[Opus] shop-api on main
ctx 18% · $3.42 · 5h 73% · 7d 41%

Each printf line becomes its own row. Multi-line output with colors is more prone to rendering glitches than a single plain line, so drop the colors if you see garbling.

8. Python, with no jq

If you'd rather not install jq, Python's standard library covers it. Save as ~/.claude/statusline.py, chmod +x it, and point command at that path:

~/.claude/statusline.py
#!/usr/bin/env python3
# No jq needed: model, context, cost and (on Pro and Max) the 5-hour limit
import json
import sys
import time

data = json.load(sys.stdin)
model = data.get("model", {}).get("display_name", "?")
ctx = int(data.get("context_window", {}).get("used_percentage") or 0)
cost = data.get("cost", {}).get("total_cost_usd") or 0

parts = [f"[{model}]", f"ctx {ctx}%", f"${cost:.2f}"]

five = (data.get("rate_limits") or {}).get("five_hour")
if five:
    mins_left = max(0, int((five["resets_at"] - time.time()) // 60))
    parts.append(f"5h {five['used_percentage']:.0f}% ({mins_left // 60}h{mins_left % 60:02d}m left)")

print(" · ".join(parts))

Output: [Opus] · ctx 18% · $3.42 · 5h 74% (1h29m left).

Test a script before you use it

Pipe sample JSON in, as the docs suggest:

Shell
echo '{"model":{"display_name":"Opus"},"workspace":{"current_dir":"/home/user/project"},"context_window":{"used_percentage":25},"cost":{"total_cost_usd":1.5},"session_id":"test-session-abc"}' | ~/.claude/statusline.sh

Test once with rate_limits in the JSON and once without, so you know the script handles both a subscription and an API key. If it prints nothing or errors, Claude Code shows a blank status line.

On Windows

Claude Code runs the status line through Git Bash when it's installed, or PowerShell otherwise. Git Bash eats unquoted backslashes, so write paths with forward slashes. To run a PowerShell script:

~/.claude/settings.json
{
  "statusLine": {
    "type": "command",
    "command": "powershell -NoProfile -File C:/Users/username/.claude/statusline.ps1"
  }
}
~/.claude/statusline.ps1
$input_json = $input | Out-String | ConvertFrom-Json
$cwd = $input_json.cwd
$model = $input_json.model.display_name
$used = $input_json.context_window.used_percentage
$dirname = Split-Path $cwd -Leaf

if ($used) {
    Write-Host "$dirname [$model] ctx: $used%"
} else {
    Write-Host "$dirname [$model]"
}

That PowerShell script is the one from the docs; it hasn't been run on Windows here.

Status line not showing?

Work down this list from the troubleshooting section:

SymptomLikely causeFix
Nothing appearsScript not executablechmod +x ~/.claude/statusline.sh
Nothing appearsOutput goes to stderr, or the script exits non-zeroPrint to stdout and exit 0; run it by hand with sample JSON
Blank in a new folderWorkspace trust not acceptedRestart and accept the trust dialog; claude --debug logs "workspace trust not accepted"
Disappeared everywheredisableAllHooks: true, or your organization set allowManagedHooksOnlyRemove the setting, or ask your admin
Shows -- or empty valuesFields are null before the first responseUse fallbacks such as // 0
Fails on WindowsBackslashes in the pathUse forward slashes
LaggySlow commands such as git statusCache them, as in example 5
Cut offNotifications share the row on narrow terminalsKeep output short

Run claude --debug to log the script's stderr on every run and its exit code on the first. If you want ready-made themes instead of your own script, the docs link community projects such as ccstatusline and starship-claude.

When one terminal isn't enough

A status line describes the session it's in. If you run several Claude Code sessions in different terminals, each shows only its own numbers, and none of them tells you when another one is waiting for permission. That's the gap a notch app fills: Eddie shows every session as working, needs you or done, in the MacBook notch or at the edge of your Windows desktop, and on a Mac adds tokens and API-equivalent cost per session, project and day. It doesn't replace the status line's plan-limit percentages, so the two work well together. The guide to watching agents from the MacBook notch shows the setup, and our token usage guide covers the other ways to track usage.

FAQ

How do I set up the Claude Code status line?

Run /statusline followed by what you want to see, such as /statusline show model and context percentage, and Claude Code writes the script and settings for you. To do it by hand, add a statusLine object with "type": "command" and a command to ~/.claude/settings.json.

Why is my Claude Code status line not showing?

The usual causes are a script that isn't executable, output going to stderr instead of stdout, a script that exits with an error, or a folder you haven't accepted in the workspace trust dialog. disableAllHooks and an organization's allowManagedHooksOnly also turn custom status lines off. Run claude --debug to see the script's errors.

Can the Claude Code status line show usage limits?

Yes, on Pro and Max. The JSON includes rate_limits.five_hour.used_percentage, rate_limits.seven_day.used_percentage and a resets_at time for each, after the session's first response. On an API key those fields are absent; show cost.total_cost_usd instead.

Does the status line use tokens?

No. The script runs locally on your machine and the docs state it doesn't consume API tokens.

Does the Claude Code status line work on Windows?

Yes. Claude Code runs the command through Git Bash when it's installed, or PowerShell otherwise. Use forward slashes in the script path, or call a PowerShell script with powershell -NoProfile -File C:/Users/you/.claude/statusline.ps1.

Is there a Claude Code statusline plugin?

Community projects such as ccstatusline and starship-claude, linked from the official docs, provide ready-made configurations with themes. A plain script like the examples here needs nothing installed except, for the Bash versions, jq.