March 20, 2026
Why a Custom Status Bar?
By default, Claude Code shows only basic information in its status line. With a simple Bash script, this can be significantly enhanced: model name, Git branch with staged/unstaged counters, context window usage as a progress bar, session cost and duration – all at a glance.
The result looks like this:
[Claude Sonnet 4.6] my-project | main +2 ~1
████████████░░░░░░░░ 60% | $0.42 | 3m 12s
Prerequisites
- Claude Code installed
- jq (JSON parser):
brew install jq - A terminal with ANSI color support (iTerm2, WezTerm, Ghostty, etc.)
Display Elements
| Element | Description |
|---|---|
[Claude Sonnet 4.6] | Currently active model |
my-project | Directory name (clickable link to repo) |
main | Git branch (clickable link to branch) |
+2 | Number of staged changes (green) |
~1 | Number of unstaged changes (yellow) |
| Progress bar | Context window usage |
60% | Context window utilization |
$0.42 | Total session cost |
3m 12s | Session duration so far |
Context Bar Colors
| Color | Range | Meaning |
|---|---|---|
| Green | 0 – 69% | All good |
| Yellow | 70 – 89% | Consider starting a new session soon |
| Red | 90%+ | Context nearly full |
Installation
1. Create the Script
Create the file ~/.claude/statusline.sh with the following content:
#!/bin/bash
# Claude Code status line - two lines: git info + context/cost
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
DIR=$(echo "$input" | jq -r '.workspace.current_dir')
COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
DURATION_MS=$(echo "$input" | jq -r '.cost.total_duration_ms // 0')
CYAN='\033[36m'; GREEN='\033[32m'; YELLOW='\033[33m'; RED='\033[31m'; RESET='\033[0m'
# Git info with caching (refresh every 5s)
CACHE_FILE="/tmp/claude-statusline-git-cache"
CACHE_MAX_AGE=5
cache_is_stale() {
[ ! -f "$CACHE_FILE" ] || \
[ $(($(date +%s) - $(stat -f %m "$CACHE_FILE" 2>/dev/null || echo 0))) -gt $CACHE_MAX_AGE ]
}
BRANCH=""
GIT_STATUS=""
REMOTE_URL=""
if cache_is_stale; then
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 ' ')
REMOTE=$(git remote get-url origin 2>/dev/null \
| sed 's/git@github\.com:/https:\/\/github.com\//' \
| sed 's/\.git$//')
echo "$BRANCH|$STAGED|$MODIFIED|$REMOTE" > "$CACHE_FILE"
else
echo "|||" > "$CACHE_FILE"
fi
fi
IFS='|' read -r BRANCH STAGED MODIFIED REMOTE_URL < "$CACHE_FILE"
if [ -n "$BRANCH" ]; then
[ "$STAGED" -gt 0 ] && GIT_STATUS="${GREEN}+${STAGED}${RESET}"
[ "$MODIFIED" -gt 0 ] && GIT_STATUS="${GIT_STATUS} ${YELLOW}~${MODIFIED}${RESET}"
if [ -n "$REMOTE_URL" ]; then
BRANCH_LINK="\e]8;;${REMOTE_URL}/tree/${BRANCH}\a${BRANCH}\e]8;;\a"
else
BRANCH_LINK="${BRANCH}"
fi
GIT_INFO=" | ${BRANCH_LINK} ${GIT_STATUS}"
else
GIT_INFO=""
fi
# Line 1: model, directory (clickable repo link), git branch
DIR_NAME="${DIR##*/}"
if [ -n "$REMOTE_URL" ]; then
DIR_LINK="\e]8;;${REMOTE_URL}\a${DIR_NAME}\e]8;;\a"
else
DIR_LINK="${DIR_NAME}"
fi
printf '%b\n' "${CYAN}[${MODEL}]${RESET} ${DIR_LINK}${GIT_INFO}"
# Line 2: context bar + cost + duration
if [ "$PCT" -ge 90 ]; then BAR_COLOR="$RED"
elif [ "$PCT" -ge 70 ]; then BAR_COLOR="$YELLOW"
else BAR_COLOR="$GREEN"; fi
FILLED=$((PCT / 5)); EMPTY=$((20 - FILLED))
BAR=""
[ "$FILLED" -gt 0 ] && BAR=$(printf "%${FILLED}s" | tr ' ' '█')
[ "$EMPTY" -gt 0 ] && BAR="${BAR}$(printf "%${EMPTY}s" | tr ' ' '░')"
COST_FMT=$(printf '$%.2f' "$COST")
MINS=$((DURATION_MS / 60000)); SECS=$(((DURATION_MS % 60000) / 1000))
echo -e "${BAR_COLOR}${BAR}${RESET} ${PCT}% | ${YELLOW}${COST_FMT}${RESET} | ${MINS}m ${SECS}s"
2. Make the Script Executable
chmod +x ~/.claude/statusline.sh
3. Update settings.json
Open ~/.claude/settings.json and add the statusLine configuration:
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh"
}
}
4. Restart Claude Code
After restarting, the new status bar will appear at the bottom of the terminal.
How the Script Works
Claude Code passes a JSON object via stdin to the configured script on every status line update. This object includes:
model.display_name– the active modelworkspace.current_dir– the current working directorycost.total_cost_usd– total session cost so farcontext_window.used_percentage– context utilizationcost.total_duration_ms– session duration in milliseconds
The script parses these values with jq and assembles two output lines.
Git Caching
Since the status line updates frequently, the script caches Git information in /tmp/claude-statusline-git-cache. The cache refreshes every 5 seconds, keeping the display responsive without running git commands on every update.
Clickable Links
In terminals with OSC 8 support (iTerm2, WezTerm, Ghostty), both the directory name and branch name are rendered as clickable links pointing directly to the repository or branch on GitHub.
Windows Variant
On Windows, the same can be achieved with a PowerShell script. Instead of jq, the built-in ConvertFrom-Json cmdlet is used. The settings.json configuration looks like this:
{
"statusLine": {
"type": "command",
"command": "powershell -NoProfile -NonInteractive -File ~/.claude/statusline.ps1"
}
}
Notes
- No Git repo: Outside of a Git repository, only the model and directory name are displayed.
- Terminal compatibility: Older terminals (e.g., macOS Terminal.app) will show OSC 8 links as plain text.
- Enterprise GitHub: The script can automatically convert SSH URLs to HTTPS links – adjust the
sedcommand in the script to match your domain.