Workflow

My Claude Code dashboard, in one line

Claude Code statusline: Opus 5.5, OtherProj/abdullah-z-site, master branch, 6% context, 64.5K tokens, $0.54, 43% of 5-hour quota resetting in 2h27m, 37% of weekly quota resetting in 4 days
Model · directory · branch · context & cost · account quota, in one line.

Once an agent is doing most of the typing, the things I glance at change. I stopped caring about the cursor and started caring about how full the context is, what the session has cost, and how much quota is left. Claude Code lets you replace its status line with any shell command, so I wrote one that looks like the Powerlevel10k prompt I already use in zsh.

How it hooks in

In ~/.claude/settings.json, point statusLine at a script. Claude Code pipes a JSON blob describing the session to its stdin and renders whatever it prints.

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

The segments

  • Model: .model.display_name, in magenta.
  • Directory: the last two path components of .workspace.current_dir. Enough to tell worktrees apart without eating the line.
  • Git: the current branch, truncated to 30 characters. Green when the tree is clean, yellow when git status --porcelain has output. Hidden outside a repo.
  • Context & cost: a 10-block bar of .context_window.used_percentage, total tokens, and .cost.total_cost_usd. The segment goes green → yellow at 50% → red past 80%, which is my cue to compact or start fresh.
  • Quota: the 5-hour and 7-day .rate_limits windows with time until reset (↻2h:27m, ↻4d). Coloured by whichever window is fuller.

Matching the terminal prompt

The look comes from two small helpers. seg prints a padded block with 256-colour background and foreground. arrow prints the rounded Nerd Font separator (U+E0B4) using the previous segment's colour as foreground and the next one's as background, so the blocks flow into each other. A leading U+E0B6 cap rounds off the start. Those are the same glyphs set in my ~/.p10k.zsh, so the statusline and the shell prompt read as one system.

seg()   { printf '\033[48;5;%sm\033[38;5;%sm %s ' "$1" "$2" "$3"; }
arrow() {
  if [ -n "$2" ]; then printf '\033[48;5;%sm\033[38;5;%sm%s' "$2" "$1" "$SEP"
  else                 printf '\033[0m\033[38;5;%sm%s' "$1" "$SEP"; fi
}

Optional segments (git, quota) are simply skipped, and the script tracks the last background colour so the end cap always matches whatever came before it.

Small details that mattered

  • Everything is read with jq using // default fallbacks, so a missing field never breaks the line.
  • Token counts are humanised with awk (64.5 K, 1.2 M).
  • White text on the red state; black text reads badly there.
  • Reset times collapse to the coarsest useful unit: days, then h:m, then minutes.

Get it

The whole thing is about 170 lines of bash with no dependencies beyond jq, git and a Nerd Font. The full script is at the end of this post: save it as ~/.claude/statusline.sh, make it executable, and point your settings at it.

If you're going to watch an agent work all day, make the number that matters the one you can't miss.

The full script

#!/usr/bin/env bash
#
# Claude Code custom statusLine — powerline style.
# Segments (left -> right): Model | Directory | Git branch/status | Usage bar
# Colors and rounded separators are matched to the user's ~/.p10k.zsh prompt.

input=$(cat)

model_name=$(printf '%s' "$input" | jq -r '.model.display_name // "Claude"')
cwd=$(printf '%s' "$input" | jq -r '.workspace.current_dir // ""')
cost=$(printf '%s' "$input" | jq -r '.cost.total_cost_usd // 0')
used_pct=$(printf '%s' "$input" | jq -r '.context_window.used_percentage // 0')
input_tokens=$(printf '%s' "$input" | jq -r '.context_window.total_input_tokens // 0')
output_tokens=$(printf '%s' "$input" | jq -r '.context_window.total_output_tokens // 0')
q5_pct=$(printf '%s' "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty')
q5_reset=$(printf '%s' "$input" | jq -r '.rate_limits.five_hour.resets_at // empty')
q7_pct=$(printf '%s' "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty')
q7_reset=$(printf '%s' "$input" | jq -r '.rate_limits.seven_day.resets_at // empty')

# --- Directory: last 2 path components ---
if [ -n "$cwd" ]; then
  dir_display=$(printf '%s' "$cwd" | awk -F/ '{ if (NF>=2) print $(NF-1)"/"$NF; else print $NF }')
else
  dir_display=""
fi

# --- Git branch/status (omit segment entirely if not a git repo) ---
git_bg=""
git_text=""
if [ -n "$cwd" ]; then
  branch=$(git -C "$cwd" branch --show-current 2>/dev/null)
  if [ -n "$branch" ]; then
    # trim to 30 chars, ellipsis included
    [ "${#branch}" -gt 30 ] && branch="${branch:0:29}…"
    if [ -z "$(git -C "$cwd" status --porcelain 2>/dev/null)" ]; then
      git_bg=2   # clean
    else
      git_bg=3   # dirty
    fi
    git_text=$'\xee\x82\xa0'" $branch"
  fi
fi

# --- Usage bar (10 blocks) + percentage + cost ---
used_int=$(printf '%.0f' "$used_pct")
[ "$used_int" -lt 0 ] && used_int=0
filled=$(( used_int / 10 ))
[ "$filled" -lt 0 ] && filled=0
[ "$filled" -gt 10 ] && filled=10
empty=$(( 10 - filled ))

bar=""
for ((i = 0; i < filled; i++)); do bar="${bar}▓"; done
for ((i = 0; i < empty; i++)); do bar="${bar}░"; done

cost_fmt=$(printf '%.2f' "$cost")

total_tokens=$(( ${input_tokens%.*} + ${output_tokens%.*} ))
tokens_fmt=$(awk -v n="$total_tokens" 'BEGIN {
  if (n >= 1000000) printf "%.1f M", n / 1000000;
  else if (n >= 1000) printf "%.1f K", n / 1000;
  else printf "%d", n;
}')

if [ "$used_int" -lt 50 ]; then
  usage_bg=2   # green
  usage_fg=0
elif [ "$used_int" -le 80 ]; then
  usage_bg=3   # yellow
  usage_fg=0
else
  usage_bg=1   # red
  usage_fg=7   # white reads better on red
fi

usage_text="${bar} ${used_int}% ${tokens_fmt} Token \$${cost_fmt}"

# --- Account quota (omit segment entirely if rate_limits is absent) ---
# fmt_reset EPOCH — time until reset: >=1d "3d", >=1h "2h:13m", >=1m "13m", else "<1m"
fmt_reset() {
  local secs=$(( ${1%.*} - $(date +%s) ))
  if [ "$secs" -ge 86400 ]; then
    printf '%dd' $(( secs / 86400 ))
  elif [ "$secs" -ge 3600 ]; then
    printf '%dh:%dm' $(( secs / 3600 )) $(( secs % 3600 / 60 ))
  elif [ "$secs" -ge 60 ]; then
    printf '%dm' $(( secs / 60 ))
  else
    printf '<1m'
  fi
}

quota_text=""
quota_max=0
for win in 5 7; do
  pct_var="q${win}_pct"; reset_var="q${win}_reset"
  pct="${!pct_var}"; reset="${!reset_var}"
  [ -z "$pct" ] && continue
  pct_int=$(printf '%.0f' "$pct")
  [ "$pct_int" -gt "$quota_max" ] && quota_max=$pct_int
  if [ "$win" = 5 ]; then icon=$'\xef\x80\x97'; else icon=$'\xef\x81\xb3'; fi
  part="${icon} ${pct_int}%"
  [ -n "$reset" ] && part="${part} ↻$(fmt_reset "$reset")"
  quota_text="${quota_text:+${quota_text} · }${part}"
done

if [ "$quota_max" -lt 50 ]; then
  quota_bg=2; quota_fg=0
elif [ "$quota_max" -le 80 ]; then
  quota_bg=3; quota_fg=0
else
  quota_bg=1; quota_fg=7
fi

# Rounded separators, matching this user's actual ~/.p10k.zsh:
#   POWERLEVEL9K_LEFT_SEGMENT_SEPARATOR=''               (between segments, and as the end cap)
#   POWERLEVEL9K_LEFT_PROMPT_FIRST_SEGMENT_START_SYMBOL='' (before the first segment)
SEP=$'\xee\x82\xb4'
CAP_START=$'\xee\x82\xb6'

# seg BG FG TEXT — prints a padded powerline block
seg() {
  printf '\033[48;5;%sm\033[38;5;%sm %s ' "$1" "$2" "$3"
}

# arrow LEFT_BG RIGHT_BG — rounded separator between two segments.
# If RIGHT_BG is empty, this is the final cap: reset background, keep fg = LEFT_BG.
arrow() {
  if [ -n "$2" ]; then
    printf '\033[48;5;%sm\033[38;5;%sm%s' "$2" "$1" "$SEP"
  else
    printf '\033[0m\033[38;5;%sm%s' "$1" "$SEP"
  fi
}

# 1) Model
printf '\033[0m\033[38;5;5m%s' "$CAP_START"
seg 5 0 "$model_name"

# 2) Directory
next_bg=4
arrow 5 4
seg 4 254 "$dir_display"

# 3) Git (optional)
if [ -n "$git_bg" ]; then
  arrow 4 "$git_bg"
  seg "$git_bg" 0 "$git_text"
  next_bg="$git_bg"
fi

# 4) Usage
arrow "$next_bg" "$usage_bg"
seg "$usage_bg" "$usage_fg" "$usage_text"

last_bg="$usage_bg"

# 5) Quota (optional)
if [ -n "$quota_text" ]; then
  arrow "$usage_bg" "$quota_bg"
  seg "$quota_bg" "$quota_fg" "$quota_text"
  last_bg="$quota_bg"
fi

# final cap, reset to terminal default background
arrow "$last_bg" ""

printf '\033[0m\n'