I caught myself typing /usage for the fourth time in an hour. Not because I was close to any limit. Just because I didnβt know if I was close to a limit. The uncertainty was doing the damage, not the actual usage.
Then /context right after it to check how much of the window Iβd burned through. Two commands, both breaking my flow, both giving me a number Iβd glance at and immediately forget. Repeat in fifteen minutes.
So I did what any developer would do when something starts annoying them enough: I spent an evening automating it away.
The always-visible dashboard
Claude Code has a statusline feature. A small bar at the bottom of your terminal that runs a shell script you configure. It refreshes after every assistant message, so the numbers stay current without you lifting a finger. No commands to type, no flow to break.
Hereβs what mine shows:
Context window usage with color coding. A progress bar that goes from green to yellow to red as you eat through your context. The number is always there. I never have to ask for it.
5-hour session limit with countdown. How much of my current rate limit window Iβve used, plus a timer showing when it resets. No more guessing whether I should pace myself or push harder.
7-day weekly limit with budget tracking. This is the one Iβm most proud of. More on this below.
Reasoning effort and git branch. Small details, but useful. I can see at a glance if I accidentally switched effort levels or if Iβm on the wrong branch before I start a long task.
The budget indicator
The 7-day limit is the tricky one. Knowing youβve used 41% of your weekly limit means nothing without context. 41% on a Monday? Youβre burning through it. 41% on a Thursday? Youβre doing great.
My statusline does the math. It calculates what your usage should be if you spread it evenly across the full seven days, then compares that to your actual usage.
β-14.0% vs budgetβ means Iβm 14 percentage points below my expected daily pace. I can push harder. If the number turns positive, I know Iβm eating into tomorrowβs share and should maybe ease off β or at least make a conscious choice about it rather than finding out when I hit a wall.
Itβs a tiny thing. But it turns a vague anxiety β am I using too much? should I slow down? β into a concrete number. And concrete numbers donβt interrupt your work the way uncertainty does.
How to build your own
The setup takes about five minutes. Add this to your ~/.claude/settings.json:
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh",
"padding": 2
}
}
Then create the script at ~/.claude/statusline.sh. Hereβs a minimal version to get you started:
#!/bin/bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
DIR=$(echo "$input" | jq -r '.workspace.current_dir')
echo "[$MODEL] ${DIR##*/} | ${PCT}% ctx"
Make it executable with chmod +x ~/.claude/statusline.sh and youβre done.
The script receives JSON on stdin with everything youβd want to display: model name, context window percentage, working directory, session cost, rate limit usage for both the 5-hour and 7-day windows. You can make it as minimal or as detailed as you want.
My full version adds color-coded progress bars, the budget calculation, git branch detection, and lines changed β all in a two-line display. Hereβs the exact script I use daily:
#!/usr/bin/env bash
# Claude Code status line β focused on context, limits, and effort
input=$(cat)
# ββ Extract all JSON fields ββββββββββββββββββββββββββββββββββββββββββββββββββ
model=$(echo "$input" | jq -r '.model.display_name // ""')
used_pct=$(echo "$input" | jq -r '.context_window.used_percentage // 0')
cwd=$(echo "$input" | jq -r '.workspace.current_dir // .cwd // ""')
five_h=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty')
five_h_resets=$(echo "$input" | jq -r '.rate_limits.five_hour.resets_at // empty')
seven_d=$(echo "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty')
seven_d_resets=$(echo "$input" | jq -r '.rate_limits.seven_day.resets_at // empty')
# ββ Effort level from settings βββββββββββββββββββββββββββββββββββββββββββββββ
effort=$(jq -r '.effortLevel // "default"' ~/.claude/settings.json 2>/dev/null)
# ββ Git branch (cached 5s) βββββββββββββββββββββββββββββββββββββββββββββββββββ
CACHE_FILE="/tmp/claude-sl-git-$(echo "$cwd" | tr '/' '_')"
now=$(date +%s)
branch=""
if [ -f "$CACHE_FILE" ] && [ $(( now - $(stat -f %m "$CACHE_FILE" 2>/dev/null || stat -c %Y "$CACHE_FILE" 2>/dev/null || echo 0) )) -lt 5 ]; then
branch=$(cat "$CACHE_FILE")
else
git -C "$cwd" rev-parse --git-dir >/dev/null 2>&1 && branch=$(git -C "$cwd" branch --show-current 2>/dev/null)
echo "$branch" > "$CACHE_FILE"
fi
# ββ Context progress bar βββββββββββββββββββββββββββββββββββββββββββββββββββββ
pct=$(printf '%.0f' "${used_pct:-0}" 2>/dev/null || echo 0)
[ "$pct" -gt 100 ] && pct=100
bar_w=20
filled=$(( pct * bar_w / 100 ))
empty=$(( bar_w - filled ))
if [ "$pct" -ge 90 ]; then c="\033[31m" # red
elif [ "$pct" -ge 70 ]; then c="\033[33m" # yellow
else c="\033[32m"; fi # green
bar=""; i=0; while [ $i -lt $filled ]; do bar+="β"; i=$(( i + 1 )); done
i=0; while [ $i -lt $empty ]; do bar+="β"; i=$(( i + 1 )); done
# ββ LINE 1: context bar | effort | branch ββββββββββββββββββββββββββββββββββββ
line1="${c}${bar}\033[0m ${pct}%"
line1+=" \033[35mβ‘${effort}\033[0m"
[ -n "$branch" ] && line1+=" \033[2mπΏ ${branch}\033[0m"
# ββ LINE 2: 5h limit | 7d limit with daily pace | model βββββββββββββββββββββ
line2=""
# 5-hour session limit
if [ -n "$five_h" ]; then
five_int=$(printf '%.0f' "$five_h")
if [ "$five_int" -ge 80 ]; then fc="\033[31m"
elif [ "$five_int" -ge 50 ]; then fc="\033[33m"
else fc="\033[32m"; fi
line2+="${fc}5h: ${five_int}%\033[0m"
if [ -n "$five_h_resets" ]; then
reset_in=$(echo "$five_h_resets $now" | awk '{
secs = $1 - $2
if (secs < 0) secs = 0
h = int(secs / 3600)
m = int((secs % 3600) / 60)
if (h > 0) printf "%dh%02dm", h, m
else printf "%dm", m
}')
line2+=" \033[2mβΊ${reset_in}\033[0m"
fi
fi
# 7-day limit vs relative budget (expected = days_elapsed/7 * 100%)
if [ -n "$seven_d" ]; then
seven_int=$(printf '%.0f' "$seven_d")
[ -n "$line2" ] && line2+=" "
if [ -n "$seven_d_resets" ]; then
delta_info=$(echo "$seven_d $seven_d_resets $now" | awk '{
actual = $1
days_left = ($2 - $3) / 86400
if (days_left < 0) days_left = 0
days_elapsed = 7 - days_left
if (days_elapsed < 0) days_elapsed = 0
expected = days_elapsed / 7 * 100
delta = actual - expected
if (delta > 5) color = "31"
else if (delta > 0) color = "33"
else color = "32"
sign = (delta >= 0) ? "+" : ""
printf "%s|%s%.1f%%", color, sign, delta
}')
delta_color="${delta_info%%|*}"
delta_str="${delta_info##*|}"
line2+="\033[${delta_color}m7d: ${seven_int}%\033[0m \033[2m(\033[0m\033[${delta_color}m${delta_str}\033[0m\033[2m vs budget)\033[0m"
else
line2+="7d: ${seven_int}%"
fi
fi
# Lines changed
lines_add=$(echo "$input" | jq -r '.cost.total_lines_added // 0')
lines_rm=$(echo "$input" | jq -r '.cost.total_lines_removed // 0')
if [ "$lines_add" -gt 0 ] || [ "$lines_rm" -gt 0 ]; then
[ -n "$line2" ] && line2+=" "
line2+="\033[32m+${lines_add}\033[0m/\033[31m-${lines_rm}\033[0m"
fi
# Model name (dimmed)
if [ -n "$model" ]; then
[ -n "$line2" ] && line2+=" "
line2+="\033[2m${model}\033[0m"
fi
# ββ Output ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
printf '%b\n' "$line1"
[ -n "$line2" ] && printf '%b\n' "$line2"
You can find the full statusline documentation here or just run the /statusline command to generate one interactively.
The meta-lesson
This is the same pattern I keep coming back to. Donβt build tooling because a tutorial told you to. Build it because something in your workflow kept interrupting you and you got tired of it.
I didnβt set out to build a dashboard. I set out to stop typing /usage four times an hour. The statusline was the smallest possible fix for a real, repeated friction.
If youβre checking the same thing manually more than twice a session, youβre not being diligent. Youβre being distracted.