statuslin.es

A Powerline-style status line with nerd-font separators and a Dracula palette: colored directory, git branch, a per-model icon, and a faded context progress bar with an arrow tail.

MIT licensed · original source

Updated 2026-06-29

8 copies

Preview

Clean repo
app  main  Opus 4.8 ━━ 22%  128  34
New session
app  main  Opus 4.8  0  0
Dirty branch
app  feat/auth  Sonnet 4.6 ━━━━ 48% 󰦓 2  128  34
Near-full
app  main  Opus 4.8 ━━━━━━━━━ 91%  128  34
1M context
app  main  Fable 5 ━━━━━━ 64%  128  34
Post-compact
app  main  Haiku 4.5  128  34
Worktree
…/feature  worktree-feature  Opus 4.8 ━━━ 37%  128  34
Non-git
scratch  Opus 4.8 ━━ 22%  128  34

Source

#!/bin/bash

# Statusline polls run frequently (multiple times per Claude refresh).
# Without this, `git status` below would try to refresh the index stat
# cache and race the user's own git operations for .git/index.lock.
export GIT_OPTIONAL_LOCKS=0

input=$(cat)
current_dir=$(echo "$input" | jq -r '.workspace.current_dir // .cwd')
ARROW_RIGHT=$(printf '\xee\x82\xb0')
ARROW_LEFT=$(printf '\xee\x82\xb2')
ARROW_ROUND=$(printf '\xee\x82\xb6')
ICON_PLUS=$(printf '\xef\x81\x95')
ICON_MINUS=$(printf '\xef\x81\x96')
ICON_SONNET=$(printf '\xee\xb8\xb4')
ICON_OPUS=$(printf '\xee\xb5\xa2')
ICON_HAIKU=$(printf '\xee\x9f\x95')
ICON_GIT=$(printf '\xee\x82\xa0')
ICON_DIFF=$(printf '\xf3\xb0\xa6\x93')
FG="40;42;54"
BG="40;42;54"
COLOR_DIR="189;147;249"
COLOR_GIT="255;121;198"
COLOR_MODEL="139;233;253"
COLOR_CTX="80;250;123"
COLOR_LINES="255;184;108"

if [ "$current_dir" = "$HOME" ]; then
  dir_display="~"
else
  if [[ "$current_dir" == "$HOME"/* ]]; then
    rel_path="${current_dir#$HOME/}"
    depth=$(echo "$rel_path" | tr -cd '/' | wc -c | tr -d ' ')
    if [ "$depth" -gt 0 ]; then
      dir_display="…/$(basename "$current_dir")"
    else
      dir_display="$(basename "$current_dir")"
    fi
  else
    dir_display="$(basename "$current_dir")"
  fi
fi

git_branch=""
git_changes="0"
git_root=""
if git -C "$current_dir" rev-parse --git-dir > /dev/null 2>&1; then
  git_root=$(git -C "$current_dir" rev-parse --show-toplevel 2>/dev/null)
  git_branch=$(git -C "$git_root" symbolic-ref --short HEAD 2>/dev/null || git -C "$git_root" rev-parse --short HEAD 2>/dev/null)
  modified_count=$(git -C "$git_root" status --short --untracked-files=all 2>/dev/null | wc -l | tr -d ' ')
  git_changes="$modified_count"
fi

model_display=$(echo "$input" | jq -r '.model.display_name // "Unknown"')
model_id=$(echo "$input" | jq -r '.model.id // ""')

if echo "$model_display" | grep -qi "sonnet" || echo "$model_id" | grep -qi "sonnet"; then
  model_icon="$ICON_SONNET"
elif echo "$model_display" | grep -qi "haiku" || echo "$model_id" | grep -qi "haiku"; then
  model_icon="$ICON_HAIKU"
elif echo "$model_display" | grep -qi "opus" || echo "$model_id" | grep -qi "opus"; then
  model_icon="$ICON_OPUS"
else
  model_icon="$ICON_SONNET"
fi

context_data=$(echo "$input" | jq '.context_window.current_usage')
context_percentage=""

if [ "$context_data" != "null" ]; then
  input_tokens=$(echo "$context_data" | jq '.input_tokens // 0')
  cache_creation=$(echo "$context_data" | jq '.cache_creation_input_tokens // 0')
  cache_read=$(echo "$context_data" | jq '.cache_read_input_tokens // 0')
  context_size=$(echo "$input" | jq '.context_window.context_window_size // 200000')

  current_usage=$((input_tokens + cache_creation + cache_read))
  percentage=$((current_usage * 100 / context_size))
  context_percentage="$percentage"

  filled_segments=$((percentage / 10))
  if [ $filled_segments -gt 10 ]; then
    filled_segments=10
  fi
  empty_segments=$((10 - filled_segments))

  progress_bar=""
  for i in $(seq 1 $filled_segments); do
    progress_bar="${progress_bar}━"
  done

  fade_idx=0
  for i in $(seq 1 $empty_segments); do
    case $fade_idx in
      0) progress_bar="${progress_bar}\033[38;2;68;71;100m━" ;;
      1) progress_bar="${progress_bar}\033[38;2;68;71;90m━" ;;
      2) progress_bar="${progress_bar}\033[38;2;60;63;80m━" ;;
      3) progress_bar="${progress_bar}\033[38;2;52;55;70m━" ;;
      *) progress_bar="${progress_bar}\033[38;2;44;47;60m━" ;;
    esac
    fade_idx=$((fade_idx + 1))
  done
  progress_bar="${progress_bar}\033[38;2;${FG}m"
fi

lines_added=$(echo "$input" | jq -r '.cost.total_lines_added // 0')
lines_removed=$(echo "$input" | jq -r '.cost.total_lines_removed // 0')

printf "\033[38;2;${COLOR_DIR}m${ARROW_ROUND}\033[0m"
printf "\033[38;2;${FG}m\033[48;2;${COLOR_DIR}m $dir_display \033[0m"

if [ -n "$git_branch" ]; then
  printf "\033[38;2;${COLOR_DIR}m\033[48;2;${COLOR_GIT}m${ARROW_RIGHT}\033[0m"
  printf "\033[38;2;${FG}m\033[48;2;${COLOR_GIT}m $ICON_GIT $git_branch \033[0m"
  printf "\033[38;2;${COLOR_GIT}m\033[48;2;${COLOR_MODEL}m${ARROW_RIGHT}\033[0m"
else
  printf "\033[38;2;${COLOR_DIR}m\033[48;2;${COLOR_MODEL}m${ARROW_RIGHT}\033[0m"
fi

printf "\033[38;2;${FG}m\033[48;2;${COLOR_MODEL}m $model_icon $model_display \033[0m"

if [ -n "$context_percentage" ]; then
  printf "\033[38;2;${COLOR_MODEL}m\033[48;2;${COLOR_CTX}m${ARROW_RIGHT}\033[0m"
  printf "\033[38;2;${FG}m\033[48;2;${COLOR_CTX}m "
  printf "%b" "$progress_bar"
  printf " ${context_percentage}%% \033[0m"

  if [ "$lines_added" != "0" ] || [ "$lines_removed" != "0" ] || [ -n "$git_changes" ]; then
    printf "\033[38;2;${COLOR_CTX}m\033[48;2;${COLOR_LINES}m${ARROW_RIGHT}\033[0m"
    files_part=""
    [ "$git_changes" != "0" ] && files_part="$ICON_DIFF $git_changes "
    printf "\033[38;2;${FG}m\033[48;2;${COLOR_LINES}m ${files_part}$ICON_PLUS $lines_added $ICON_MINUS $lines_removed \033[0m"
    printf "\033[38;2;${COLOR_LINES}m\033[48;2;180;130;80m${ARROW_RIGHT}\033[0m"
    printf "\033[38;2;180;130;80m\033[48;2;120;85;55m${ARROW_RIGHT}\033[0m"
    printf "\033[38;2;120;85;55m\033[48;2;70;50;35m${ARROW_RIGHT}\033[0m"
    printf "\033[38;2;70;50;35m${ARROW_RIGHT}\033[0m\n"
  else
    printf "\033[38;2;${COLOR_CTX}m\033[48;2;55;180;90m${ARROW_RIGHT}\033[0m"
    printf "\033[38;2;55;180;90m\033[48;2;40;130;65m${ARROW_RIGHT}\033[0m"
    printf "\033[38;2;40;130;65m\033[48;2;30;90;48m${ARROW_RIGHT}\033[0m"
    printf "\033[38;2;30;90;48m${ARROW_RIGHT}\033[0m\n"
  fi
else
  if [ "$lines_added" != "0" ] || [ "$lines_removed" != "0" ] || [ -n "$git_changes" ]; then
    printf "\033[38;2;${COLOR_MODEL}m\033[48;2;${COLOR_LINES}m${ARROW_RIGHT}\033[0m"
    files_part=""
    [ "$git_changes" != "0" ] && files_part="$ICON_DIFF $git_changes "
    printf "\033[38;2;${FG}m\033[48;2;${COLOR_LINES}m ${files_part}$ICON_PLUS $lines_added $ICON_MINUS $lines_removed \033[0m"
    printf "\033[38;2;${COLOR_LINES}m\033[48;2;180;130;80m${ARROW_RIGHT}\033[0m"
    printf "\033[38;2;180;130;80m\033[48;2;120;85;55m${ARROW_RIGHT}\033[0m"
    printf "\033[38;2;120;85;55m\033[48;2;70;50;35m${ARROW_RIGHT}\033[0m"
    printf "\033[38;2;70;50;35m${ARROW_RIGHT}\033[0m\n"
  else
    printf "\033[38;2;${COLOR_MODEL}m\033[48;2;100;180;210m${ARROW_RIGHT}\033[0m"
    printf "\033[38;2;100;180;210m\033[48;2;70;130;160m${ARROW_RIGHT}\033[0m"
    printf "\033[38;2;70;130;160m\033[48;2;50;90;110m${ARROW_RIGHT}\033[0m"
    printf "\033[38;2;50;90;110m${ARROW_RIGHT}\033[0m\n"
  fi
fi

What it shows

  • Current directory name, shortened to ~ for the home directory and to …/dirname for paths nested more than one level under home
  • Current git branch name, falling back to the short commit hash when HEAD is detached
  • Count of changed and untracked files in the repo, shown with a diff icon when nonzero
  • Model display name with a Nerd Font icon chosen per model family (Sonnet, Haiku, or Opus)
  • Context window usage as a percentage, computed from input, cache-creation, and cache-read tokens against the context window size
  • A ten-segment context progress bar whose unfilled tail fades to darker shades
  • Total lines added and removed in the session, each with its own icon

Requirements

  • bash
  • jq, used to parse the JSON that Claude Code sends on stdin
  • git, used for branch detection and change counts
  • A Nerd Font, for the Powerline separators and the git, model, diff, plus, and minus icons
  • A terminal that supports 24-bit truecolor ANSI escape codes
  • No network access; the script declares no hosts and runs with network access off

Behavior notes

  • The pink git segment disappears entirely when the current directory is not a git repository, as in the scratch-directory scenario
  • The green context bar and percentage segment are omitted when context_window.current_usage is null, as in the brand-new-session and post-compact scenarios
  • The file-change count and diff icon appear only when the repo has uncommitted changes; the dirty-branch scenario shows a count of 2 while clean-repo scenarios show only the lines added and removed
  • The lines segment renders even when both counts are zero, as in the fresh-session scenario, because the file-change count variable is initialized to 0 before the git-repo check runs, regardless of whether the directory is a git repo
  • The progress bar fills one segment per 10 percent used: two segments at 22 percent, nine at 91 percent
  • The model icon switches by matching sonnet, haiku, or opus in the model name or ID; unrecognized models like Fable 5 fall back to the Sonnet icon
  • The directory shortens to …/feature in the worktree scenario because the path sits more than one level below home
  • The branch name comes from the actual git checkout, so the worktree scenario shows worktree-feature and the dirty-branch scenario shows feat/auth regardless of the JSON payload
  • The status line ends with a four-arrow tail that fades through darker shades of the last segment's color
  • The script sets GIT_OPTIONAL_LOCKS=0 so its frequent git status polls do not compete with the user's own git commands for the index lock
  • Fields in the JSON with no matching code, such as vim mode, effort level, rate limits, PR info, and session name, do not appear in the output

More status lines

Dreambase Panel

150 copies

╭─ CONTEXT ────────────────────────────────────────────╮ ████▊░░░░░░░░░░░░░░░░░ 22% of 200K · GOOD ├─ STATS ──────────────────────────────────────────────┤ 44.0K 1.4K $0.41 ⏱ 10m 12s +128/-34 ├─ REPO ───────────────────────────────────────────────┤ Opus 4.8 · main · 16:21 ╰──────────────────────────────────────────────────────╯
app · main · Opus-4.8 · effort high · ctx 22% · 5h 26% ↻2h6m · 7d 7% ↻2d0h
Opus 4.8 [high] ~/app main 16:20:55 | ⛁ ██░░░░░░░░ 22% | 5h ●◔○○○ 26% ↻2h7m | 7d ◔○○○○○○ 7% ↻2d1h │ $0.41 ⏱ 10m
Opus 4.8 main app 44k/200k $0.41

Activity Feed

36 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
████▊░░░░░░░░░░░░░░░░░ 22% of 200K · GOOD 44.0K 1.4K $0.41 ⏱ 10m 12s +128/-34 Opus 4.8 · main · 16:22