Claude Code Status Line: Setup, JSON Fields and 5 Ready-to-Use Scripts

Updated 2026-10-15 · by Murilo Prataviera, maker of Aki

You have three Claude Code sessions open. One is on the wrong branch, one is at 85% context and about to compact, and you're at 90% of your weekly limit. None of that is on screen until something breaks. The status line fixes this. It's one row at the bottom of Claude Code that runs your own script and shows whatever it prints. This guide covers how to turn it on, every field Claude Code hands your script, and five scripts you can copy. Each one was tested with sample input.

Quick answer

  • Fastest way: type /statusline show model, git branch and context % and Claude Code writes the script and the setting for you.
  • By hand: add "statusLine": {"type": "command", "command": "~/.claude/statusline.sh"} to ~/.claude/settings.json.

  • Input: your script gets a JSON object on stdin with model, workspace, cost, context_window, rate_limits, session_id and more. Whatever it prints to stdout is shown.

  • Costs nothing: the status line runs locally and uses no API tokens.

  • Multi-line and colors work: each printed line is a row; ANSI colors are supported.

What the status line is

It's a bar at the bottom of Claude Code, in its own row above the built-in footer. Claude Code runs your command, passes session data as JSON on stdin, and displays stdout. It can show context use, cost, git state, your plan limits, or anything a script can compute.

One side effect: with a custom status line, Claude Code stops showing most footer hints, such as esc to interrupt and ? for shortcuts.

How to turn it on

Option 1: /statusline

Describe what you want in plain English:

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

Claude Code writes a script in ~/.claude/ and updates your settings. Approve the file edits when asked. To remove it, run /statusline delete (or clear, or remove it).

Option 2: settings.json

Add a statusLine block to ~/.claude/settings.json (all projects) or to a project's settings:

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

Make the script executable (chmod +x ~/.claude/statusline.sh). Claude Code reloads settings on save.

When it updates

The script runs when a session starts or resumes, and again when a new assistant message arrives, /compact finishes, the permission mode or vim mode changes, a refreshInterval timer fires, or a rate-limit window reaches its resets_at time. Updates are debounced at 300 ms. If a new update comes while your script is still running, the old run is cancelled.

The output only shows once the script exits with code 0. A slow or failing script means a stale or blank bar.

The JSON Claude Code sends

These are the fields in the current docs. The ones you'll use most:

Field What it holds
model.id, model.display_name Current model, e.g. "Opus"
workspace.current_dir, workspace.project_dir Current folder and the folder Claude Code started in
workspace.git_worktree, workspace.repo.owner/name Linked worktree name; repo parsed from origin
cost.total_cost_usd Estimated session cost at list price. Resets on /clear
cost.total_duration_ms, cost.total_lines_added/removed Session time and lines changed
context_window.used_percentage Context used, input tokens only. Can be null early
context_window.context_window_size 200,000 by default, 1,000,000 for extended context
rate_limits.five_hour.used_percentage / .resets_at 5-hour window, 0 to 100, reset in Unix seconds
rate_limits.seven_day.used_percentage / .resets_at Weekly limit, same format
session_id, session_name, transcript_path Session identity and the transcript file
version, output_style.name, effort.level Claude Code version, output style, reasoning effort
pr.number, pr.review_state Open PR for the branch, when there is one
prompt_cache.warm, prompt_cache.hit_ratio Prompt cache state

Two rules keep scripts from breaking:

/context can read a bit higher than used_percentage, because it adds an estimate for messages since the last response.

Five tested scripts

Save each as ~/.claude/statusline.sh (or .py), chmod +x it and point command at it. The bash ones need >jq.

1. Model, folder and git branch

#!/bin/bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
DIR=$(echo "$input" | jq -r '.workspace.current_dir')
BRANCH=$(git -C "$DIR" branch --show-current 2>/dev/null)
echo "[$MODEL] ${DIR##*/}${BRANCH:+ on $BRANCH}"

Output: [Opus] my-app on feature/login. Outside a repo, the branch part disappears.

2. Context use with a colored bar

#!/bin/bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
SIZE=$(echo "$input" | jq -r '.context_window.context_window_size // 200000')

GREEN='\033[32m'; YELLOW='\033[33m'; RED='\033[31m'; RESET='\033[0m'
if   [ "$PCT" -ge 80 ]; then COLOR=$RED
elif [ "$PCT" -ge 60 ]; then COLOR=$YELLOW
else COLOR=$GREEN; fi

FILLED=$((PCT / 10)); EMPTY=$((10 - FILLED))
BAR=$(printf '%*s' "$FILLED" '' | tr ' ' '#')$(printf '%*s' "$EMPTY" '' | tr ' ' '-')

printf '%b\n' "[$MODEL] ${COLOR}${BAR} ${PCT}%${RESET} of $((SIZE / 1000))k"

Output: [Opus] #######--- 74% of 200k, in yellow. \033[...m codes are standard ANSI colors.

3. 5-hour and weekly limits with reset times

#!/bin/bash
input=$(cat)
LIMITS=$(echo "$input" | jq -r '
  [ (.rate_limits.five_hour  | select(. != null) | "5h \(.used_percentage | floor)% (resets \(.resets_at | strflocaltime("%H:%M")))"),
    (.rate_limits.seven_day | select(. != null) | "7d \(.used_percentage | floor)% (resets \(.resets_at | strflocaltime("%a %H:%M")))")
  ] | join(" | ")')
echo "${LIMITS:-limits: n/a}"

Output: 5h 23% (resets 19:53) | 7d 91% (resets Mon 05:13). With an API key, or before the first reply, it prints limits: n/a. jq's strflocaltime formats the time the same way on macOS and Linux. For what these limits mean, see Claude Code usage limits explained.

4. Session cost, time and lines changed

#!/bin/bash
input=$(cat)
COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
MS=$(echo "$input" | jq -r '.cost.total_duration_ms // 0')
ADDED=$(echo "$input" | jq -r '.cost.total_lines_added // 0')
REMOVED=$(echo "$input" | jq -r '.cost.total_lines_removed // 0')
printf '$%.2f | %dm %ds | +%s -%s\n' "$COST" $((MS / 60000)) $((MS % 60000 / 1000)) "$ADDED" "$REMOVED"

Output: $1.23 | 12m 34s | +156 -23. On Pro or Max this dollar figure is an estimate at API prices, not what you pay.

5. Everything on two lines, in Python (no jq needed)

#!/usr/bin/env python3
import json, os, subprocess, sys, time

data = json.load(sys.stdin)
GREEN, YELLOW, RED, DIM, RESET = "\033[32m", "\033[33m", "\033[31m", "\033[2m", "\033[0m"

def color(pct):
    return RED if pct >= 80 else YELLOW if pct >= 60 else GREEN

model = data.get("model", {}).get("display_name", "?")
cwd = data.get("workspace", {}).get("current_dir") or data.get("cwd", "")
try:
    branch = subprocess.run(["git", "-C", cwd, "branch", "--show-current"],
                            capture_output=True, text=True, timeout=1).stdout.strip()
except Exception:
    branch = ""

# Line 1: where you are
line1 = f"{model} {DIM}|{RESET} {os.path.basename(cwd)}"
if branch:
    line1 += f" {DIM}on{RESET} {branch}"

# Line 2: context, cost, limits
ctx = int((data.get("context_window") or {}).get("used_percentage") or 0)
cost = (data.get("cost") or {}).get("total_cost_usd") or 0
parts = [f"ctx {color(ctx)}{ctx}%{RESET}", f"${cost:.2f}"]

for key, label in (("five_hour", "5h"), ("seven_day", "7d")):
    window = (data.get("rate_limits") or {}).get(key)
    if not window:
        continue
    pct = int(window.get("used_percentage") or 0)
    left = max(0, int(window.get("resets_at", 0) - time.time()))
    hours = left // 3600
    reset = f"{hours // 24}d{hours % 24}h" if hours >= 24 else f"{hours}h{left % 3600 // 60:02d}m"
    parts.append(f"{label} {color(pct)}{pct}%{RESET} {DIM}({reset}){RESET}")

print(line1)
print(f" {DIM}|{RESET} ".join(parts))

Output:

Opus | my-app on feature/login
ctx 74% | $1.23 | 5h 23% (1h59m) | 7d 91% (3d11h)

Each print is a separate row. Since the reset is a countdown, add "refreshInterval": 60 so it keeps ticking while you're idle.

Test any script before you use it

Pipe fake JSON into it:

echo '{"model":{"display_name":"Opus"},"workspace":{"current_dir":"/tmp/my-app"},"context_window":{"used_percentage":74},"cost":{"total_cost_usd":1.23},"rate_limits":{"seven_day":{"used_percentage":41,"resets_at":1893456000}},"session_id":"test"}' | ~/.claude/statusline.sh

Community status lines

If you'd rather not write a script:

Common problems

Does Codex have a status line?

Yes, but you pick from built-in items. You can't run your own script. Type /statusline in the Codex CLI, toggle and reorder items, and confirm. The choice is saved in ~/.codex/config.toml:

[tui]
status_line = ["model-with-reasoning", "current-dir", "git-branch", "context-used", "five-hour-limit", "weekly-limit"]

Setting status_line = null turns it off. /title does the same for the terminal tab title (tui.terminal_title). The item names come from the Codex source and can change between versions, so /statusline is the safest way to see what your version supports.

Where Aki fits

Aki is a free, source-available macOS app. Its sidebar on the edge of the screen shows one ring per Claude Code and Codex session, and how much of each account's weekly limit is used, without asking a server:

bash sid=$(echo "$input" | jq -r '.session_id // empty') [ -n "$sid" ] && mkdir -p ~/.aki/status && printf '%s' "$input" > ~/.aki/status/$sid.json

From those files it takes the model, context use and the rate_limits windows. Without a status line, it falls back to the usage the Claude desktop app keeps in its local cache. - Codex: it reads the latest rate_limits Codex writes to its session logs in ~/.codex/sessions.

Aki's main job is something else: you point at things on screen and it sends them to the right agent session. Mac only (Apple Silicon, macOS 14+), no account. Code at >github.com/muriloprataviera/aki.

FAQ

How do I enable the status line in Claude Code?

Type /statusline followed by what you want to see, or add a statusLine block with "type": "command" and your script path to ~/.claude/settings.json.

What data does the Claude Code status line receive?

A JSON object on stdin with the model, working directory, session cost and duration, context window use, 5-hour and weekly rate limits (Pro and Max), session ID, transcript path, version, output style and more.

Can the status line show my weekly limit?

Yes, on Pro and Max. Read rate_limits.seven_day.used_percentage and rate_limits.seven_day.resets_at. They appear after the first response of a session.

Does the status line use tokens?

No. It runs locally and doesn't consume API tokens.

Can the status line have more than one line?

Yes. Each line your script prints is shown as its own row.

Why is my status line blank?

Usually the script isn't executable, exits with an error, prints nothing, or the folder hasn't been trusted yet. Run it by hand with test JSON, or start Claude Code with claude --debug.

Is there a status line in Codex?

Yes. Use /statusline in the Codex CLI to choose built-in items, saved to tui.status_line in ~/.codex/config.toml. Custom commands aren't supported.

Sources