pavel 1ar.ionov 1ar.io

Custom Claude Code Statusline

My custom single-line statusline for Claude Code with context bar, model info, git branch, and tab title status indicators

Updated

This is how my statusline looks like:

custom claude code statusline

Shows model name (and subagent if active), current directory, git branch, and a color-coded context window bar — all in one line.

The context bar changes color as usage grows: grey up to 60%, amber from 61-80%, red at 81%+.

Important: keep output to a single line. Claude Code uses the second status line row for background task buttons and system notifications. If your script outputs two lines, those UI elements disappear.

Save to ~/.claude/statusline-command.sh:

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

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

CYAN='\033[36m'
YELLOW='\033[33m'
RED='\033[31m'
RESET='\033[0m'

# pick bar color based on context usage
if [ "$PCT" -ge 81 ]; then
  BAR_COLOR="$RED"
elif [ "$PCT" -ge 61 ]; then
  BAR_COLOR="$YELLOW"
else
  BAR_COLOR='\033[90m'
fi

FILLED=$((PCT / 10))
EMPTY=$((10 - FILLED))
BAR=$(printf "%${FILLED}s" | tr ' ' '█')
BAR+=$(printf "%${EMPTY}s" | tr ' ' '░')

BRANCH=""
if git rev-parse --git-dir > /dev/null 2>&1; then
  BRANCH=" | $(git branch --show-current 2>/dev/null)"
fi

MODEL_DISPLAY="$MODEL"
[ -n "$AGENT" ] && MODEL_DISPLAY="$MODEL / $AGENT"

# single line only
# second line is reserved for background tasks UI
printf "%b" \
  "${CYAN}[${MODEL_DISPLAY}]${RESET}" \
  " ${DIR##*/}${BRANCH}" \
  " ${BAR_COLOR}${BAR}${RESET} ${PCT}%\n"

Add to ~/.claude/settings.json:

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

Check out the official statusline docs for all available options and fields.

Tab title status indicators

Using Claude Code hooks, the terminal tab title can reflect what Claude is doing: working, waiting for input, or idle. Useful when you have multiple Claude Code sessions in different tabs.

StateHook eventTab title
WorkingPreToolUse<folder> · claude code · WIP
Needs inputNotification<folder> · claude code · ??
IdleStop<folder> · claude code

claude code tab status indicators

Save these three scripts to ~/.claude/hooks/:

working-tab.sh:

#!/bin/bash
printf '\033]0;%s · claude code · WIP\007' "${PWD##*/}" > /dev/tty

notify-tab.sh:

#!/bin/bash
printf '\033]0;%s · claude code · ??\007' "${PWD##*/}" > /dev/tty

idle-tab.sh:

#!/bin/bash
printf '\033]0;%s · claude code\007' "${PWD##*/}" > /dev/tty

Add hooks to ~/.claude/settings.json:

{
  "env": {
    "CLAUDE_CODE_DISABLE_TERMINAL_TITLE": "1"
  },
  "hooks": {
    "PreToolUse": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash ~/.claude/hooks/working-tab.sh",
            "async": true
          }
        ]
      }
    ],
    "Notification": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash ~/.claude/hooks/notify-tab.sh",
            "async": true
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash ~/.claude/hooks/idle-tab.sh",
            "async": true
          }
        ]
      }
    ]
  }
}

CLAUDE_CODE_DISABLE_TERMINAL_TITLE prevents Claude Code from overriding the tab title with its own default. The > /dev/tty redirect is required because async hooks run detached from the terminal — without it, the escape sequence goes nowhere.

Note: this is slightly flaky — the title updates don’t always fire reliably for every state transition, but it works well enough to be useful at a glance.