Skip to content
cd ..

My Claude Code Status Line

Date
Reading time
5 min

Context usage, cost, git status – things you want to keep an eye on while working with Claude Code, without having to ask for them. The status line shows that persistently at the bottom of the terminal. After each response, Claude Code runs a shell script that receives session data as JSON on stdin and outputs whatever you want to see.

Here’s what mine looks like:

[Claude Sonnet 4.6]  my-project |main ~2 | +41 -7
▓▓▓░░░░░░░ 31% ctx | $0.042 | ⧗ 3m 12s | 5h: 37% 7d: 23% | in:12k out:3.2k | cache w:8k r:21k

The script lives at ~/.claude/statusline.sh, wired up in ~/.claude/settings.json:

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

##Line 1 – Model, directory, git

[Claude Sonnet 4.6]  my-project |main ~2 | +41 -7
  • [Claude Sonnet 4.6] – active model name, so it’s always clear which model is running
  • my-project – last component of the current working directory, no full /home/... path
  • ⑂ main ~2 – git branch; +N for staged files, ~N for modified files
  • +41 -7 – lines added/removed across the entire session, not just the last commit

Git data is cached for 10 seconds (/tmp/claude-statusline-git-cache). Claude Code runs the script after every response – without caching, every git call would introduce noticeable lag.

##Line 2 – Context, cost, tokens

▓▓▓░░░░░░░ 31% ctx | $0.042 | ⧗ 3m 12s | 5h: 37% 7d: 23% | in:12k out:3.2k | cache w:8k r:21k
  • ▓▓▓░░░░░░░ 31% ctx – context bar (10 chars) with percentage; green below 50%, yellow up to 75%, red above
  • $0.042 – total session cost in USD so far
  • ⧗ 3m 12s – total session duration
  • 5h: 37% 7d: 23% – rate limit usage over 5 hours and 7 days; only available on Claude.ai Pro/Max
  • in:12k out:3.2k – input and output tokens for the current request, formatted compactly (e.g. 1200 → 1.2k)
  • cache w:8k r:21k – cache tokens written and read; high cache reads are good because cached tokens cost significantly less

##The full script

#!/bin/bash

input=$(cat)

# --- model & session ---
MODEL=$(echo "$input"       | jq -r '.model.display_name // "Claude"')
DIR=$(echo "$input"         | jq -r '.workspace.current_dir // ""')
DIR_NAME="${DIR##*/}"
COST=$(echo "$input"        | jq -r '.cost.total_cost_usd // 0')
DURATION_MS=$(echo "$input" | jq -r '.cost.total_duration_ms // 0')
LINES_ADDED=$(echo "$input" | jq -r '.cost.total_lines_added // 0')
LINES_REMOVED=$(echo "$input" | jq -r '.cost.total_lines_removed // 0')

# --- context window ---
PCT_RAW=$(echo "$input"     | jq -r '.context_window.used_percentage // 0')
PCT=$(echo "$PCT_RAW"       | cut -d. -f1)
IN_TOKENS=$(echo "$input"   | jq -r '.context_window.current_usage.input_tokens // empty')
OUT_TOKENS=$(echo "$input"  | jq -r '.context_window.current_usage.output_tokens // empty')
CACHE_W=$(echo "$input"     | jq -r '.context_window.current_usage.cache_creation_input_tokens // empty')
CACHE_R=$(echo "$input"     | jq -r '.context_window.current_usage.cache_read_input_tokens // empty')

# --- rate limits ---
FIVE_H=$(echo "$input"      | jq -r '.rate_limits.five_hour.used_percentage // empty')
SEVEN_D=$(echo "$input"     | jq -r '.rate_limits.seven_day.used_percentage // empty')

# --- colors ---
GREEN='\033[32m'  YELLOW='\033[33m'  RED='\033[31m'
CYAN='\033[36m'   BOLD='\033[1m'     DIM='\033[2m'
RESET='\033[0m'

# compact token format: 1234 -> 1.2k, 12345 -> 12k
fmt_tok() {
    local n="$1"
    [ -z "$n" ] || [ "$n" = "0" ] && { echo "0"; return; }
    if [ "$n" -ge 1000 ]; then
        local k=$(( n / 1000 )) r=$(( (n % 1000) / 100 ))
        [ "$r" -gt 0 ] && [ "$k" -lt 10 ] && echo "${k}.${r}k" || echo "${k}k"
    else
        echo "$n"
    fi
}

# --- git (cached 10s) ---
CACHE_FILE="/tmp/claude-statusline-git-cache"

if [ ! -f "$CACHE_FILE" ] || \
   [ $(( $(date +%s) - $(stat -c %Y "$CACHE_FILE" 2>/dev/null || echo 0) )) -gt 10 ]; then
    if git -C "${DIR:-.}" rev-parse --git-dir > /dev/null 2>&1; then
        BRANCH=$(git -C "${DIR:-.}" branch --show-current 2>/dev/null)
        STAGED=$(git -C "${DIR:-.}" diff --cached --numstat 2>/dev/null | wc -l | tr -d ' ')
        MODIFIED=$(git -C "${DIR:-.}" diff --numstat 2>/dev/null | wc -l | tr -d ' ')
        printf '%s|%s|%s' "$BRANCH" "$STAGED" "$MODIFIED" > "$CACHE_FILE"
    else
        printf '||' > "$CACHE_FILE"
    fi
fi
IFS='|' read -r BRANCH STAGED MODIFIED < "$CACHE_FILE"

# --- line 1: model · dir · git · lines ---
LINE1="${BOLD}${CYAN}[${MODEL}]${RESET} ${DIM}${RESET} ${DIR_NAME}"

if [ -n "$BRANCH" ]; then
    GIT_PART="${DIM}|${RESET}${GREEN}${BRANCH}${RESET}"
    [ "${STAGED:-0}"   -gt 0 ] && GIT_PART="${GIT_PART} ${GREEN}+${STAGED}${RESET}"
    [ "${MODIFIED:-0}" -gt 0 ] && GIT_PART="${GIT_PART} ${YELLOW}~${MODIFIED}${RESET}"
    LINE1="${LINE1} ${GIT_PART}"
fi

if [ "${LINES_ADDED:-0}" -gt 0 ] || [ "${LINES_REMOVED:-0}" -gt 0 ]; then
    LINE1="${LINE1} ${DIM}|${RESET} ${GREEN}+${LINES_ADDED}${RESET} ${RED}-${LINES_REMOVED}${RESET}"
fi

# --- line 2: ctx bar · cost · time · tokens ---
if   [ "${PCT:-0}" -ge 75 ]; then BAR_COLOR="$RED"
elif [ "${PCT:-0}" -ge 50 ]; then BAR_COLOR="$YELLOW"
else                               BAR_COLOR="$GREEN"
fi

BAR_WIDTH=10
FILLED=$(( PCT * BAR_WIDTH / 100 ))
[ "$FILLED" -gt "$BAR_WIDTH" ] && FILLED=$BAR_WIDTH
EMPTY=$(( BAR_WIDTH - FILLED ))

BAR=""
for ((i=0; i<FILLED; i++)); do BAR="${BAR}▓"; done
for ((i=0; i<EMPTY;  i++)); do BAR="${BAR}░"; done

COST_FMT=$(LC_NUMERIC=C printf '$%.3f' "$COST")
MINS=$(( DURATION_MS / 60000 ))
SECS=$(( (DURATION_MS % 60000) / 1000 ))

LINE2="${BAR_COLOR}${BAR}${RESET} ${PCT}% ctx ${DIM}|${RESET} ${YELLOW}${COST_FMT}${RESET} ${DIM}|${RESET}${MINS}m ${SECS}s"

if [ -n "$FIVE_H" ] || [ -n "$SEVEN_D" ]; then
    RATE="${DIM}|${RESET}"
    [ -n "$FIVE_H"  ] && RATE="${RATE} 5h: $(LC_NUMERIC=C printf '%.0f' "$FIVE_H")%"
    [ -n "$SEVEN_D" ] && RATE="${RATE} 7d: $(LC_NUMERIC=C printf '%.0f' "$SEVEN_D")%"
    LINE2="${LINE2} ${RATE}"
fi

if [ -n "$IN_TOKENS" ]; then
    TOKEN_PART="${DIM}|${RESET} in:$(fmt_tok "$IN_TOKENS") out:${CYAN}$(fmt_tok "$OUT_TOKENS")${RESET}"
    TOKEN_PART="${TOKEN_PART} ${DIM}| cache${RESET} w:$(fmt_tok "$CACHE_W") r:${GREEN}$(fmt_tok "$CACHE_R")${RESET}"
    LINE2="${LINE2} ${TOKEN_PART}"
fi

printf '%b\n%b\n' "$LINE1" "$LINE2"

##Conclusion

The script is a starting point. What’s shown here works for me – if you need different fields, different colors, or just less information, adjust it. The available JSON fields are documented in the script, the rest is just shell.