Two Nerd Font lines: model, a 16-segment context bar, cost and quotas on top; directory, git counts, Python venv and vim mode below.
MIT licensed · original source
0 copies
Using the Claude desktop app? Add your status line with statusline-anywhere.
Preview
Clean repo
Opus 4.8 │ ▰▰▰▰▱▱▱▱▱▱▱▱▱▱▱▱ 22% │ $0.41
app │ mainNew session
Opus 4.8 │ ▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱ 0% │ $0.00
app │ mainDirty branch
Sonnet 4.6 │ ▰▰▰▰▰▰▰▰▱▱▱▱▱▱▱▱ 48% │ $0.41
app │ feat/auth ~1 ?1 │ INSERTNear-full
CTX 91% Opus 4.8 │ ▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▱ │ $4.12
app │ main │ VISUAL1M context
Fable 5 │ ▰▰▰▰▰▰▰▰▰▰▱▱▱▱▱▱ 64% │ $0.41
app │ mainPost-compact
Haiku 4.5 │ ▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱ 0% │ $0.41
app │ main │ NORMALWorktree
Opus 4.8 │ ▰▰▰▰▰▰▱▱▱▱▱▱▱▱▱▱ 37% │ $0.41
feature │ worktree-feature │ VISUAL LINENon-git
Opus 4.8 │ ▰▰▰▰▱▱▱▱▱▱▱▱▱▱▱▱ 22% │ $0.41
scratchSource
#!/bin/bash
# Claude Code Status Line — Two-line layout with Nerd Font icons (MD range)
# Line 1: Model │ Context Bar (16 segs) │ Cost │ 5h usage │ 7d usage
# Line 2: Directory │ Git Branch & Status │ Venv │ Vim
input=$(< /dev/stdin)
now=$(date +%s)
# --- Extract all values in a single jq call ---
eval "$(jq -r '
@sh "model=\(.model.display_name // "?")",
@sh "cwd=\(.workspace.current_dir // ".")",
@sh "used_pct=\(.context_window.used_percentage // 0)",
@sh "cost=\(.cost.total_cost_usd // 0)",
@sh "vim_mode=\(.vim.mode // "")"
' <<< "$input")"
dir_name="${cwd##*/}"
# --- Theme detection ---
# Override with STATUSLINE_THEME=dark|light|auto (default: auto)
detect_theme() {
local theme="${STATUSLINE_THEME:-auto}"
if [ "$theme" != "auto" ]; then echo "$theme"; return; fi
# macOS system appearance
if defaults read -g AppleInterfaceStyle &>/dev/null; then
echo "dark"; return
fi
# COLORFGBG env var (e.g. "15;0" → bg=0 is dark)
if [ -n "$COLORFGBG" ]; then
local bg="${COLORFGBG##*;}"
if [ "$bg" -lt 8 ] 2>/dev/null; then
echo "dark"
else
echo "light"
fi
return
fi
echo "dark"
}
THEME=$(detect_theme)
# --- Colors ---
RST=$'\033[0m'
BOLD=$'\033[1m'
if [ "$THEME" = "light" ]; then
DIM=$'\033[90m' # bright black (gray) — visible on light bg
CYAN=$'\033[36m'
GREEN=$'\033[32m'
YELLOW=$'\033[33m'
RED=$'\033[31m'
MAGENTA=$'\033[35m'
BLUE=$'\033[34m'
BG_RED=$'\033[41m'
WHITE_BOLD=$'\033[1;30m' # bold black — for alert badge text on light bg
else
DIM=$'\033[2m'
CYAN=$'\033[36m'
GREEN=$'\033[32m'
YELLOW=$'\033[33m'
RED=$'\033[31m'
MAGENTA=$'\033[35m'
BLUE=$'\033[34m'
BG_RED=$'\033[41m'
WHITE_BOLD=$'\033[1;37m'
fi
# --- Nerd Font Icons (all MD range, U+F0000+) ---
ICON_MODEL="" # nf-md-robot
ICON_CTX="" # nf-md-memory
ICON_DIR="" # nf-md-folder_outline
ICON_GIT="" # nf-md-source_branch
ICON_COST="" # nf-md-cash
ICON_WARN="" # nf-fa-warning
ICON_VIM="" # nf-md-vim
ICON_VENV="" # nf-md-language_python
SEP="${DIM} │ ${RST}"
BAR_SEGMENTS=16
# --- Color helper for utilization percentage ---
# Thresholds: green < 50, yellow 50-79, red >= 80
pct_color() {
local pct=$1
if [ "$pct" -lt 50 ]; then echo "$GREEN"
elif [ "$pct" -lt 80 ]; then echo "$YELLOW"
else echo "$RED"
fi
}
# --- Context percentage ---
pct_int=${used_pct%.*}
pct_int=${pct_int:-0}
CTX_COLOR=$(pct_color "$pct_int")
# --- Progress bar ---
filled=$(( (pct_int * BAR_SEGMENTS + 50) / 100 ))
[ "$filled" -gt "$BAR_SEGMENTS" ] && filled=$BAR_SEGMENTS
empty=$((BAR_SEGMENTS - filled))
bar=""
for ((i = 0; i < filled; i++)); do bar="${bar}▰"; done
for ((i = 0; i < empty; i++)); do bar="${bar}▱"; done
# --- Cost ---
printf -v cost_str '$%.2f' "$cost"
# --- Git info (single git status --porcelain call) ---
git_info=""
if git -C "$cwd" rev-parse --git-dir > /dev/null 2>&1; then
branch=$(git -C "$cwd" branch --show-current 2>/dev/null)
[ -z "$branch" ] && branch="detached"
staged=0 unstaged=0 untracked=0
while IFS= read -r line; do
x=${line:0:1}
y=${line:1:1}
if [ "$x$y" = "??" ]; then
((untracked++))
else
[ "$x" != " " ] && [ "$x" != "?" ] && ((staged++))
[ "$y" != " " ] && ((unstaged++))
fi
done < <(git -C "$cwd" status --porcelain 2>/dev/null)
status=""
[ "$staged" -gt 0 ] && status="${status} ${GREEN}+${staged}${RST}"
[ "$unstaged" -gt 0 ] && status="${status} ${YELLOW}~${unstaged}${RST}"
[ "$untracked" -gt 0 ] && status="${status} ${DIM}?${untracked}${RST}"
git_info="${SEP}${MAGENTA}${ICON_GIT} ${branch}${RST}${status}"
fi
# --- Python venv detection ---
venv_str=""
venv_name="" py_ver=""
if [ -n "$VIRTUAL_ENV" ]; then
venv_name="${VIRTUAL_ENV##*/}"
else
for venv_dir in "$cwd/.venv" "$cwd/venv" "$cwd/.env"; do
if [ -f "${venv_dir}/bin/python" ]; then
venv_name="${venv_dir##*/}"
py_ver=$("${venv_dir}/bin/python" --version 2>/dev/null | awk '{print $2}' | cut -d. -f1-2)
break
fi
done
fi
if [ -n "$venv_name" ]; then
venv_str="${SEP}${YELLOW}${ICON_VENV} ${venv_name}${py_ver:+ ($py_ver)}${RST}"
fi
# --- Vim mode ---
vim_str=""
if [ -n "$vim_mode" ]; then
[[ "$vim_mode" == "NORMAL" ]] && vim_color=$BLUE || vim_color=$GREEN
vim_str="${SEP}${vim_color}${BOLD}${ICON_VIM} ${vim_mode}${RST}"
fi
# --- Anthropic OAuth usage API (cached, background refresh) ---
CACHE_DIR="$HOME/.claude/statusline-cache"
USAGE_CACHE="$CACHE_DIR/usage.dat"
LOCK_FILE="$CACHE_DIR/usage-update.lock"
CACHE_TTL=60
refresh_usage_cache() {
if ! mkdir "$LOCK_FILE" 2>/dev/null; then
lock_age=$(( now - $(stat -f%m "$LOCK_FILE" 2>/dev/null || echo 0) ))
[ "$lock_age" -gt 60 ] && rm -r "$LOCK_FILE" 2>/dev/null || return
mkdir "$LOCK_FILE" 2>/dev/null || return
fi
(
# Try macOS Keychain first, then fall back to credentials file
token=$(security find-generic-password -s "Claude Code-credentials" -a "$(whoami)" -w 2>/dev/null | jq -r '.claudeAiOauth.accessToken // empty' 2>/dev/null)
[ -z "$token" ] && token=$(jq -r '.claudeAiOauth.accessToken // empty' "$HOME/.claude/.credentials.json" 2>/dev/null)
if [ -n "$token" ]; then
resp=$(curl -s --max-time 5 \
-H "Authorization: Bearer $token" \
-H "anthropic-beta: oauth-2025-04-20" \
https://api.anthropic.com/api/oauth/usage 2>/dev/null)
if echo "$resp" | jq -e '.five_hour' > /dev/null 2>&1; then
echo "$resp" | jq -r '
def to_epoch: split(".")[0] + "Z" | fromdateiso8601;
"\(.five_hour.utilization | floor) \(.five_hour.resets_at | to_epoch) \(.seven_day.utilization | floor) \(.seven_day.resets_at | to_epoch)"
' > "$USAGE_CACHE.tmp" 2>/dev/null && mv "$USAGE_CACHE.tmp" "$USAGE_CACHE"
fi
fi
rm -r "$LOCK_FILE" 2>/dev/null
) &
disown 2>/dev/null
}
format_countdown() {
local diff=$(( $1 - now ))
[ "$diff" -le 0 ] && echo "soon" && return
if [ "$diff" -ge 86400 ]; then
echo "$((diff / 86400))d$((diff % 86400 / 3600))h"
elif [ "$diff" -ge 3600 ]; then
echo "$((diff / 3600))h$((diff % 3600 / 60))m"
else
echo "$((diff / 60))m"
fi
}
usage_segment() {
local label=$1 pct=$2 reset_epoch=$3
local color reset_str
color=$(pct_color "$pct")
reset_str=$(format_countdown "$reset_epoch")
echo "${SEP}${DIM}${label}${RST} ${color}${pct}%${RST} ${DIM}↺ ${reset_str}${RST}"
}
if [ ! -f "$USAGE_CACHE" ]; then
[ -d "$CACHE_DIR" ] || mkdir -p "$CACHE_DIR" 2>/dev/null
refresh_usage_cache
else
cache_age=$(( now - $(stat -f%m "$USAGE_CACHE" 2>/dev/null || echo 0) ))
[ "$cache_age" -gt "$CACHE_TTL" ] && refresh_usage_cache
fi
usage_5h="" usage_7d=""
if [ -f "$USAGE_CACHE" ]; then
read -r pct5h epoch5h pct7d epoch7d < "$USAGE_CACHE" 2>/dev/null
if [ -n "$pct5h" ] && [ "$pct5h" != "-1" ]; then
usage_5h=$(usage_segment "5h" "$pct5h" "$epoch5h")
fi
if [ -n "$pct7d" ] && [ "$pct7d" != "-1" ]; then
usage_7d=$(usage_segment "7d" "$pct7d" "$epoch7d")
fi
fi
# --- Build output ---
line1_tail="${SEP}${DIM}${ICON_COST} ${cost_str}${RST}${usage_5h}${usage_7d}"
if [ "$pct_int" -ge 90 ]; then
line1="${BG_RED}${WHITE_BOLD} ${ICON_WARN} CTX ${pct_int}% ${RST} ${CYAN}${BOLD}${ICON_MODEL} ${model}${RST}${SEP}${CTX_COLOR}${bar}${RST}${line1_tail}"
else
line1="${CYAN}${BOLD}${ICON_MODEL} ${model}${RST}${SEP}${DIM}${ICON_CTX}${RST} ${CTX_COLOR}${bar} ${pct_int}%${RST}${line1_tail}"
fi
line2="${BLUE}${ICON_DIR} ${dir_name}${RST}${git_info}${venv_str}${vim_str}"
printf '%s\n%s' "$line1" "$line2"
What it shows
- Model display name with a robot icon
- Context window usage as a 16-segment progress bar plus a percentage
- A red warning badge with the context percentage when usage reaches 90% or more
- Session cost in US dollars, formatted to two decimal places
- 5-hour and 7-day usage percentages with a countdown to reset, when cached usage data from Anthropic's OAuth usage API is available
- Current directory name (last path segment only)
- Git branch name, or 'detached' when no branch is checked out
- Counts of staged, unstaged, and untracked files in the git repo
- Python virtual environment name, with the Python version when detected from a local .venv, venv, or .env folder
- Vim mode (e.g. NORMAL, INSERT) when Claude Code reports one
Requirements
- Bash
- jq for parsing the stdin JSON, credentials, and the usage API response
- git for branch and working-tree status
- curl for fetching usage data from api.anthropic.com
- A Nerd Font to render the Material Design icon glyphs
- macOS-flavored commands: stat -f%m for cache timestamps, plus defaults (theme detection) and security (Keychain token lookup)
- Network access to api.anthropic.com for the 5h/7d usage segments
- A Claude OAuth token, read from the macOS Keychain or from ~/.claude/.credentials.json
- awk and cut for extracting the Python version
Behavior notes
- When context usage reaches 90%, a red 'CTX 91%' badge replaces the memory icon and inline percentage, and the bar turns red
- Bar and percentage color follows thresholds: green below 50%, yellow from 50-79% (64% renders yellow), red at 80% or above
- When used_percentage is null (new session or just after /compact), the bar renders empty at 0%
- Git file counts only appear when the working tree is dirty; a clean repo shows just the branch name
- In a non-git directory the entire git segment disappears, leaving only the directory name on line two
- The vim segment appears only when a vim mode is reported; NORMAL renders blue, other modes render green
- The branch name comes from running git in the current directory, not from the input JSON
- The 5h/7d usage segments come from a cached API response rather than the rate_limits field on stdin, and did not appear in any rendered preview
- The venv segment did not appear in any preview; it shows only when a virtual environment is active or found in the directory
- Input fields like effort level, thinking, PR details, session name, agent name, and output style vary across scenarios but are not displayed
More status lines
Activity Feed
51 copies
Opus 4.8 high ✦ │ ctx 22% │ app (main) │ PR #1287 ● │ $0.41 · 10m +128 -34
Session ● ● ● ● ● ● ● ● ○ ○ ○ 74% left Resets in 2h 6m
Weekly ● ● ● ● ● ● ● ● ● ● ○ 93% left Resets in 2d 0h
Fable ● ● ● ● ● ● ● ● ● ○ ○ 85% left Resets in 2d 0h
◐ Read:index.ts ✓ Read×1 ✓ Bash×1 ✓ Edit×1 ✓ Grep×1
◐ code-reviewer Review the change for regressions
Usage Dot Bars
20 copies
Opus 4.8 │ ✍️ 22% │ app (main) │ ● high
current ●●○○○○○○○○ 26% ⟳ 6:59pm
weekly ○○○○○○○○○○ 7% ⟳ aug 1, 5:52pmQuota Fallback
14 copies
🌿 main │ Opus 4.8/hi │ 🟢 Ctx ▓▓░░░░ 22% │ ⏳ 🟢 ▓▓░░░░ 26% ↻ 2h7m │ $0.41 ⏱ 10m
One Dark Triptych
13 copies
Model: Opus 4.8 | Thinking: high | Context: [▰▰▱▱▱▱▱▱▱▱] 45k/200k (22%) | cwd: ~/app | ⎇ main | (+0,-0)
Cost: $0.41 | Block: 2hr 6m | Weekly: 7.0% | Session: 10m | In: 916.7 t/s | Out: 29.2 t/s | Cached: 2k | Session: [▰▱▱▱▱] 26.0%
Mem: 0.8G/1.0G | (*゚∀゚*) | Session ID: b8e1c0d2-4a6f-4e2a-9c1b-3f5d7a9e2c10
Overage Watcher
6 copies
Opus 4.8 | app@main | ctx 44k/200k (22%) | cost $0.41 | 5h 26% | 7d 7%Keyblade Status bar
0 copies
✧ ███████████▋ 73% 🗝 Ultima Weapon ✦ app ∙ main
♥ ████████████████████████ 100% 「SAVE POINT」 ✶ Limit Form ◉ 41