How to set up a Claude Code status line

A status line is a bar at the bottom of Claude Code. Claude Code runs your script, pipes it a JSON snapshot of the session, and shows what the script prints.

This guide is for writing your own from scratch. To use one someone already made, install one from the gallery.

Updated 2026-10-23

The fast paths

Run /statusline

Inside Claude Code, describe what you want, like /statusline show model, directory, and context usage. It writes the script to ~/.claude/ and updates your settings.

Each status line there shows its real output before you copy it. Browse Claude Code status lines.

Wire up a script by hand

  1. 1

    Save a minimal working script

    Save this script to ~/.claude/statusline.sh. Three fields, one jq call each:

    #!/bin/bash
    input=$(cat)
    model=$(jq -r '.model.display_name' <<<"$input")
    dir=$(jq -r '.workspace.current_dir' <<<"$input")
    pct=$(jq -r '.context_window.used_percentage // 0' <<<"$input")
    echo "[$model] ${dir##*/} · ${pct}% context"
    
    The script needs jq. Install it with brew install jq on macOS, or sudo apt install jq on Debian and Ubuntu.
  2. 2

    Make it runnable

    Mark it executable, so Claude Code can run it:

    chmod +x ~/.claude/statusline.sh
  3. 3

    Point settings.json at it

    Point the statusLine setting in ~/.claude/settings.json at the script:

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

    Claude Code runs the script as soon as you save. Two optional fields: padding indents the line by N characters, and refreshInterval re-runs the script every N seconds, for a clock or other live data. The official docs list the rest.

  4. 4

    Test it

    To try it outside Claude Code, save the sample JSON below as sample.json and run:

    bash ~/.claude/statusline.sh < sample.json

    For that payload it prints:

    [Opus 5.5] app · 22% context

    This site's tests run this script on every pull request.

The JSON your script receives

Your script gets one JSON object on stdin per update. This is one of the payloads the gallery renders previews against:

{
  "session_id": "b8e1c0d2-4a6f-4e2a-9c1b-3f5d7a9e2c10",
  "transcript_path": "/home/user/.claude/projects/app/transcript.jsonl",
  "fast_mode": false,
  "version": "2.1.307",
  "cwd": "/home/user/app",
  "prompt_id": "7f3c2a91-5d4e-4b8a-a2c6-0e9d1b4f6a83",
  "model": {
    "id": "claude-opus-5-5",
    "display_name": "Opus 5.5"
  },
  "effort": {
    "level": "high"
  },
  "thinking": {
    "enabled": true
  },
  "workspace": {
    "current_dir": "/home/user/app",
    "project_dir": "/home/user/app",
    "repo": {
      "host": "github.com",
      "owner": "acme",
      "name": "app"
    },
    "added_dirs": []
  },
  "cost": {
    "total_cost_usd": 0.41,
    "total_duration_ms": 612000,
    "total_api_duration_ms": 48000,
    "total_lines_added": 128,
    "total_lines_removed": 34
  },
  "context_window": {
    "context_window_size": 200000,
    "used_percentage": 22,
    "remaining_percentage": 78,
    "total_input_tokens": 44000,
    "total_output_tokens": 1400,
    "current_usage": {
      "input_tokens": 37000,
      "output_tokens": 1400,
      "cache_creation_input_tokens": 5000,
      "cache_read_input_tokens": 2000
    }
  },
  "exceeds_200k_tokens": false,
  "prompt_cache": {
    "warm": true,
    "caching_observed": true,
    "ttl": "1h",
    "expires_at": 1782003120,
    "requests": 14,
    "misses": 1,
    "expected_rebuilds": 0,
    "hit_ratio": 0.86,
    "cache_write_tokens": 61000,
    "miss_recache_tokens": 18500,
    "last_miss_at": 1781997720,
    "last_miss_cause": {
      "causes": [
        "tools_changed"
      ],
      "tools_added": 2,
      "tools_removed": 0
    },
    "miss_causes": {
      "tools_changed": 1
    },
    "recache_tokens_if_cold": 44000
  },
  "rate_limits": {
    "five_hour": {
      "used_percentage": 26,
      "resets_at": 1782007620
    },
    "seven_day": {
      "used_percentage": 7,
      "resets_at": 1782176400
    }
  },
  "output_style": {
    "name": "default"
  },
  "pr": {
    "number": 1287,
    "url": "https://github.com/acme/app/pull/1287"
  }
}

Most scripts only need a few fields: model.display_name, workspace.current_dir, context_window.used_percentage, and cost.total_cost_usd. Browse status lines that show token usage, usage limits or git status.

What each field holds, including the ones only some sessions send:

session_id
Unique ID for this session.
transcript_path
Path to the session's transcript file.
fast_mode
Whether fast mode is on.
version
Claude Code version.
cwd
Same value as workspace.current_dir.
prompt_id
ID of the prompt being processed. Absent until your first prompt.
model
id and display_name.
effort
Reasoning effort level. Absent when the model doesn't support it.
thinking
Whether extended thinking is on.
workspace
current_dir, project_dir and added_dirs, plus repo and git_worktree when they apply.
cost
Session cost, duration, and lines added and removed. Most scripts show total_cost_usd.
context_window
Token counts and used_percentage, which can be null early in a session.
exceeds_200k_tokens
Whether the last response passed 200k tokens.
prompt_cache
The main conversation's prompt cache: whether it's warm, hit_ratio, misses, when it expires, and more. Absent until the first response.
rate_limits
five_hour and seven_day windows on Pro and Max plans, or spend_limit behind a Claude apps gateway, each with used_percentage and resets_at in unix seconds. spend_limit can add used_usd, limit_usd and period. Appears after the first response, and a window drops out once its resets_at passes.
output_style
The current output style's name.
pr
Open PR's number, url and review_state, when the branch has one. For a GitLab merge request, kind is "mr".
session_name
The name from --name or /rename, else the AI-generated title. Absent until there is one.
vim
The vim mode, such as NORMAL or INSERT, when vim mode is on.
agent
The agent's name, when running with --agent or agent settings.
worktree
name, path, branch, original_cwd and original_branch, during a worktree session.

Common questions

Why isn't the git branch in the JSON?

Claude Code sends the repository name in workspace.repo and any open PR in pr, but not the branch or changed files. Scripts that show them run git in workspace.current_dir.

Why is used_percentage null?

context_window.used_percentage can be null early in a session. Guard it with // 0 in jq, or the line prints "null" until the first response.

Going further

The tools and resources list has status line tools, themes and usage trackers. If you build a status line you like, submit it to the gallery.

Status line blank in the terminal? Work through the fixes.

Claude Desktop's Code tab doesn't run your status line. Here's how to show it there.

For a pane or a slash command, you need a mod. Compare status lines and mods.