# I built a statusline because /usage is a distraction

> Stop interrupting yourself to check how much you have left.

- **Published:** 2026-04-07
- **Tags:** ai, engineering
- **Canonical:** https://nbarwicki.com/blog/17-i-built-a-statusline-because-usage-commands-are-a-distraction/

---

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`:

```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:

```bash
#!/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:

```bash
#!/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](https://code.claude.com/docs/en/statusline) 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.**
