Kipt

Claude Code statusline: set up a custom status line

Set up a Claude Code statusline with /statusline or settings.json, learn the JSON fields your script gets, and copy bash scripts for git, context and cost.

By the Kipt team · Updated 23 October 2026

What is the Claude Code statusline?

The Claude Code statusline is a customizable bar at the bottom of the terminal that runs a shell script you choose and shows whatever it prints. Claude Code sends the script JSON about the session on stdin, such as the model, folder, context usage and cost, so you can keep that information visible while you work. You set it up with the /statusline command or a statusLine entry in settings.json.

The official statusline docs suggest it for four jobs: watching context window usage, tracking session cost, telling several sessions apart, and keeping the git branch in view. It runs locally and does not consume API tokens.

Your status line renders in its own row above the built-in footer and does not replace it. One trade-off: with a custom status line, Claude Code stops showing most of the footer's keyboard hints, such as esc to interrupt.

The fast way: the /statusline command

/statusline takes a plain-language description and does the work for you. Claude Code generates a script file in ~/.claude/ and updates your settings.

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

Approve the file edit prompts if Claude Code asks. Run /statusline with no description and, according to the commands reference, it auto-configures from your shell prompt.

To turn it off later, ask the same command: /statusline delete, /statusline clear or /statusline remove it all work.

Configure statusLine in settings.json

If you want to write the script yourself, add a statusLine field to your user settings at ~/.claude/settings.json, or to project settings if the whole team should share it.

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

The fields, as listed in the settings reference:

Field Required What it does
type Yes Always "command"
command Yes A script path or an inline shell command
padding No Extra horizontal spacing in characters. Defaults to 0
refreshInterval No Also re-run every N seconds (minimum 1), for clocks or data that changes while the session is idle
hideVimModeIndicator No Set true to hide the built-in -- INSERT -- text when your script shows the Vim mode itself

Because command runs in a shell, you can skip the script entirely for something simple. This one-liner from the official docs uses jq to print the model and context percentage:

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

Claude Code reloads settings automatically, so the status line appears as soon as you save.

What JSON does the status line receive?

Claude Code pipes a JSON object to your script's stdin every time it runs. These are the fields most people use; the official page lists the full schema.

Field What it holds
model.id, model.display_name Current model ID and display name
workspace.current_dir Current working directory (same value as cwd)
workspace.project_dir Folder where Claude Code was launched
workspace.repo.owner, workspace.repo.name Repo identity from the origin remote, when there is one
context_window.used_percentage Percentage of the context window used
context_window.remaining_percentage Percentage remaining
context_window.context_window_size Max context size in tokens
cost.total_cost_usd Estimated session cost in USD, computed on your machine
cost.total_duration_ms Wall-clock time the session has run
cost.total_lines_added, cost.total_lines_removed Lines of code changed
effort.level Current effort level, when the model supports it
rate_limits.five_hour.used_percentage Share of the 5-hour limit used (Pro and Max)
rate_limits.seven_day.used_percentage Share of the 7-day limit used (Pro and Max)
session_id, session_name Session ID, and its name if it has one
pr.number, pr.url Open pull request for the current branch, when found
vim.mode Vim mode, when Vim editing is on
version Claude Code version

Two details save debugging time:

  • Some fields can be missing. rate_limits appears only for claude.ai Pro and Max subscribers (or behind a gateway with spend limits) and only after the first API response. effort, vim, pr, session_name and workspace.repo also appear only when they apply.
  • Some fields can be null. context_window.used_percentage may be null early in a session, and context_window.current_usage is null before the first API call and right after /compact.

In jq, use // 0 for a numeric fallback or // empty to print nothing when a field is absent. Note that used_percentage counts input tokens only, not output tokens. Also, cost.total_cost_usd is an estimate at list price and may differ from your bill.

Example scripts

Each example below is a bash script that uses jq, a command-line JSON parser you may need to install. To use one, save it as ~/.claude/statusline.sh, make it executable, and point statusLine.command at it.

chmod +x ~/.claude/statusline.sh

Model, folder and context

The starter script from the official docs. ${DIR##*/} keeps just the folder name.

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

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

echo "[$MODEL] ${DIR##*/} | ${PCT}% context"

Git branch with colors

This adds the branch plus counts of staged (green) and modified (yellow) files, using ANSI color codes.

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

MODEL=$(echo "$input" | jq -r '.model.display_name')
DIR=$(echo "$input" | jq -r '.workspace.current_dir')

GREEN='\033[32m'
YELLOW='\033[33m'
RESET='\033[0m'

if git rev-parse --git-dir > /dev/null 2>&1; then
    BRANCH=$(git branch --show-current 2>/dev/null)
    STAGED=$(git diff --cached --numstat 2>/dev/null | wc -l | tr -d ' ')
    MODIFIED=$(git diff --numstat 2>/dev/null | wc -l | tr -d ' ')

    GIT_STATUS=""
    [ "$STAGED" -gt 0 ] && GIT_STATUS="${GREEN}+${STAGED}${RESET}"
    [ "$MODIFIED" -gt 0 ] && GIT_STATUS="${GIT_STATUS}${YELLOW}~${MODIFIED}${RESET}"

    echo -e "[$MODEL] ${DIR##*/} | $BRANCH $GIT_STATUS"
else
    echo "[$MODEL] ${DIR##*/}"
fi

Cost and duration

Formats the estimated cost as dollars and the session time as minutes and seconds.

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

MODEL=$(echo "$input" | jq -r '.model.display_name')
COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
DURATION_MS=$(echo "$input" | jq -r '.cost.total_duration_ms // 0')

COST_FMT=$(printf '$%.2f' "$COST")
DURATION_SEC=$((DURATION_MS / 1000))
MINS=$((DURATION_SEC / 60))
SECS=$((DURATION_SEC % 60))

echo "[$MODEL] $COST_FMT | ${MINS}m ${SECS}s"

Plan usage limits

For Pro and Max subscribers, this shows how much of the 5-hour and 7-day limits you have used. // empty keeps it quiet when the fields are absent.

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

MODEL=$(echo "$input" | jq -r '.model.display_name')
FIVE_H=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty')
WEEK=$(echo "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty')

LIMITS=""
[ -n "$FIVE_H" ] && LIMITS="5h: $(printf '%.0f' "$FIVE_H")%"
[ -n "$WEEK" ] && LIMITS="${LIMITS:+$LIMITS }7d: $(printf '%.0f' "$WEEK")%"

[ -n "$LIMITS" ] && echo "[$MODEL] | $LIMITS" || echo "[$MODEL]"

Each echo prints a separate row, so you can combine these into a two-line status line: identity and git on the first line, context and cost on the second.

When does the status line update?

Your script runs when a session starts or resumes, then again when:

  • a new assistant message arrives
  • /compact finishes
  • the permission mode or Vim mode changes
  • you change the command in your settings
  • a refreshInterval timer elapses, if you set one
  • a rate-limit window in the last data your script received reaches its resets_at time
  • a warm prompt cache in the last data your script received reaches its expires_at time

Claude Code debounces updates at 300ms, so a burst of changes triggers one run. If a new update arrives while your script is still running, the old run is cancelled. Edits to your script show up the next time something triggers a run.

Tips for a fast, readable status line

  • Test with mock input before wiring it up. The official docs suggest piping sample JSON straight into the script:
echo '{"model":{"display_name":"Opus"},"workspace":{"current_dir":"/home/user/project"},"context_window":{"used_percentage":25},"session_id":"test-session-abc"}' | ./statusline.sh
  • Keep it short. Long output gets truncated or wraps badly, especially on narrow terminals where notifications share the row.
  • Keep it fast. The script runs often. Slow commands like git status in a big repo can make it lag; the official page has a caching example that stores git info in a temp file keyed by session_id and refreshes it every 5 seconds.
  • Read the width from the environment. tput cols cannot see the terminal from inside the script. Use the COLUMNS and LINES variables, which Claude Code sets before running it.
  • Use Python or Node if you prefer. The docs show the same examples in both. Anything that reads stdin and prints to stdout works.

If you prefer something ready-made, the official page points to community projects such as ccstatusline and starship-claude.

Why is my status line blank?

Work through these in order:

  1. Is the script executable? Run chmod +x ~/.claude/statusline.sh.
  2. Does it print to stdout? Output on stderr is not shown.
  3. Does it exit with code 0? A non-zero exit or empty output leaves the bar blank.
  4. Have you trusted the folder? The status line runs a shell command, so it waits for the workspace trust dialog, the same rule that applies to hooks.
  5. Is disableAllHooks set? Outside managed settings, it disables your status line too. If your organization sets allowManagedHooksOnly, only a managed status line runs.
  6. Still stuck? Start with claude --debug to log the script's stderr, or ask Claude to read your settings and run the statusLine command for you.

If values show as empty or --, they are probably null before the first API response. Add fallbacks such as // 0.

Status lines for subagents

There is a second, related setting. subagentStatusLine replaces the default row shown for each running subagent in the panel below the prompt. It takes the same type and command shape, receives a tasks array on stdin, and expects one JSON line back per row you want to override. Most people never need it, but it is handy if you run several subagents and want to see their model or token use at a glance.

Once you have a status line you like, keep the script with the rest of your setup. Kipt stores prompts and skills as Markdown in a private GitHub repo you own, which makes it a natural home for the /statusline description that produced a layout you want to reuse on another machine.

Questions

How do I add a status line to Claude Code?

Run /statusline inside a session and describe what you want, for example /statusline show model name and context percentage. Claude Code writes a script in ~/.claude/ and updates your settings. You can also add a statusLine entry to ~/.claude/settings.json by hand.

What data does the Claude Code status line get?

Your script receives a JSON object on stdin with fields such as model.display_name, workspace.current_dir, context_window.used_percentage, cost.total_cost_usd, session_id and, for Pro and Max subscribers, rate_limits. Whatever the script prints to stdout is shown.

Does the status line use tokens?

No. The official docs say the status line runs locally and does not consume API tokens. It is just a shell command that Claude Code runs on your machine.

How do I remove the status line?

Run /statusline and ask it to remove or clear the status line, for example /statusline delete. Or delete the statusLine field from your settings.json.

Why is my Claude Code status line blank?

The usual causes are a script that is not executable, a script that prints to stderr instead of stdout, a non-zero exit code, or a folder you have not trusted yet. Run the script by hand with mock JSON, and start Claude Code with --debug to see its errors.

More in Claude Code

All guides

Keep your prompts and skills in one place, in your own GitHub. Install every skill with one command.

Get started

Kipt is an independent product, not affiliated with or endorsed by Anthropic, OpenAI, Cursor or GitHub. Claude, ChatGPT, Codex, Cursor and GitHub are trademarks of their owners.