Three-Line Cockpit

Model, context, lines changed and branch on the first line; a context bar with token counts on the second; session cost on the third. No network calls, just stdin and local git.

MIT licensed Β· original source

1 copy

Using the Claude desktop app? Add your status line with statusline-anywhere.

Preview

Clean repo
πŸ€– Opus 4.8 β”‚ πŸ“Š 22% β”‚ ✏️ +128/-34 β”‚ πŸ”€ main πŸ“ CTX β–°β–°β–±β–±β–±β–±β–±β–±β–±β–± 22% 44K / 200K tokens πŸ’° $0.41
New session
πŸ€– Opus 4.8 β”‚ πŸ“Š 0% β”‚ πŸ”€ main πŸ“ CTX β–±β–±β–±β–±β–±β–±β–±β–±β–±β–± 0% 0 / 200K tokens πŸ’° --
Dirty branch
πŸ€– Sonnet 4.6 β”‚ πŸ“Š 48% β”‚ ✏️ +128/-34 β”‚ πŸ”€ feat/auth πŸ“ CTX β–°β–°β–°β–°β–°β–±β–±β–±β–±β–± 48% 96K / 200K tokens πŸ’° $0.41
Near-full
πŸ€– Opus 4.8 β”‚ πŸ“Š 91% β”‚ ✏️ +128/-34 β”‚ πŸ”€ main πŸ“ CTX β–°β–°β–°β–°β–°β–°β–°β–°β–°β–± 91% 182K / 200K tokens πŸ’° $4.12
1M context
πŸ€– Fable 5 β”‚ πŸ“Š 64% β”‚ ✏️ +128/-34 β”‚ πŸ”€ main πŸ“ CTX β–°β–°β–°β–°β–°β–°β–±β–±β–±β–± 64% 640K / 1M tokens πŸ’° $0.41
Post-compact
πŸ€– Haiku 4.5 β”‚ πŸ“Š 0% β”‚ ✏️ +128/-34 β”‚ πŸ”€ main πŸ“ CTX β–±β–±β–±β–±β–±β–±β–±β–±β–±β–± 0% 0 / 200K tokens πŸ’° $0.41
Worktree
πŸ€– Opus 4.8 β”‚ πŸ“Š 37% β”‚ ✏️ +128/-34 β”‚ πŸ”€ worktree-feature πŸ“ CTX β–°β–°β–°β–°β–±β–±β–±β–±β–±β–± 37% 74K / 200K tokens πŸ’° $0.41
Non-git
πŸ€– Opus 4.8 β”‚ πŸ“Š 22% β”‚ ✏️ +128/-34 πŸ“ CTX β–°β–°β–±β–±β–±β–±β–±β–±β–±β–± 22% 44K / 200K tokens πŸ’° $0.41

Source

#!/bin/bash
# Claude Code statusline script (ToS-safe version)
# Line 1: Model | Context% | +added/-removed | git branch
# Line 2: Context window progress bar (detailed)
# Line 3: Session cost | Version
#
# This version uses ONLY stdin data provided by Claude Code.
# No external API calls, no OAuth token extraction, no keychain access.
# Fully compliant with Anthropic Consumer Terms of Service.

set -euo pipefail

# ---------- Read stdin (Claude Code provides JSON) ----------
input=$(cat)

# ---------- ANSI Colors ----------
readonly GREEN=$'\e[38;2;151;201;195m'
readonly YELLOW=$'\e[38;2;229;192;123m'
readonly RED=$'\e[38;2;224;108;117m'
readonly GRAY=$'\e[38;2;74;88;92m'
readonly RESET=$'\e[0m'
readonly DIM=$'\e[2m'

# ---------- Color by percentage ----------
color_for_pct() {
  local pct="${1:-0}"
  if [[ -z "$pct" || "$pct" == "null" ]]; then
    printf '%s' "$GRAY"
    return
  fi
  local ipct
  ipct=$(printf "%.0f" "$pct" 2>/dev/null) || ipct=0
  if (( ipct >= 80 )); then
    printf '%s' "$RED"
  elif (( ipct >= 50 )); then
    printf '%s' "$YELLOW"
  else
    printf '%s' "$GREEN"
  fi
}

# ---------- Progress bar (10 segments) ----------
progress_bar() {
  local pct="${1:-0}"
  local filled
  filled=$(awk "BEGIN{v=int($pct / 10 + 0.5); if(v>10)v=10; if(v<0)v=0; printf \"%d\", v}" 2>/dev/null) || filled=0
  local bar=""
  local i
  for (( i=1; i<=10; i++ )); do
    if (( i <= filled )); then
      bar+="β–°"
    else
      bar+="β–±"
    fi
  done
  printf '%s' "$bar"
}

# ---------- Safe JSON parsing (no eval) ----------
parse_json() {
  local json="$1"
  local query="$2"
  local default="${3:-}"
  local result
  result=$(printf '%s' "$json" | jq -r "$query" 2>/dev/null) || result=""
  if [[ -z "$result" || "$result" == "null" ]]; then
    printf '%s' "$default"
  else
    printf '%s' "$result"
  fi
}

# ---------- Parse stdin fields ----------
model_name=$(parse_json "$input" '.model.display_name // empty' 'Unknown')
used_pct=$(parse_json "$input" '.context_window.used_percentage // 0' '0')
remaining_pct=$(parse_json "$input" '.context_window.remaining_percentage // 100' '100')
ctx_size=$(parse_json "$input" '.context_window.context_window_size // 0' '0')
cwd=$(parse_json "$input" '.cwd // empty' '')
lines_added=$(parse_json "$input" '.cost.total_lines_added // 0' '0')
lines_removed=$(parse_json "$input" '.cost.total_lines_removed // 0' '0')
total_cost=$(parse_json "$input" '.cost.total_cost_usd // 0' '0')

# ---------- Git branch ----------
git_branch=""
if [[ -n "$cwd" && -d "$cwd" ]]; then
  git_branch=$(git -C "$cwd" --no-optional-locks rev-parse --abbrev-ref HEAD 2>/dev/null) || true
fi

# ---------- Line stats ----------
git_stats=""
if (( ${lines_added:-0} > 0 )) 2>/dev/null || (( ${lines_removed:-0} > 0 )) 2>/dev/null; then
  git_stats="+${lines_added}/-${lines_removed}"
fi

# ---------- Context window formatting ----------
ctx_pct_int=$(printf "%.0f" "${used_pct:-0}" 2>/dev/null) || ctx_pct_int=0

# Format context size (e.g., 200000 -> 200K)
format_tokens() {
  local tokens="${1:-0}"
  if (( tokens >= 1000000 )); then
    awk "BEGIN{printf \"%.0fM\", $tokens / 1000000}"
  elif (( tokens >= 1000 )); then
    awk "BEGIN{printf \"%.0fK\", $tokens / 1000}"
  else
    printf '%s' "$tokens"
  fi
}

ctx_size_display=$(format_tokens "$ctx_size")

# Used tokens (approximate)
used_tokens=0
if (( ctx_size > 0 )); then
  used_tokens=$(awk "BEGIN{printf \"%.0f\", $ctx_size * $used_pct / 100}" 2>/dev/null) || used_tokens=0
fi
used_tokens_display=$(format_tokens "$used_tokens")

# ---------- Cost formatting ----------
cost_display=""
if [[ -n "$total_cost" && "$total_cost" != "0" ]]; then
  cost_display=$(awk "BEGIN{printf \"\$%.2f\", $total_cost}" 2>/dev/null) || cost_display=""
fi

# ---------- Separator ----------
SEP="${GRAY} β”‚ ${RESET}"

# ========== Line 1: Model | Context% | Changes | Branch ==========
ctx_color=$(color_for_pct "$ctx_pct_int")

line1="πŸ€– ${model_name}${SEP}${ctx_color}πŸ“Š ${ctx_pct_int}%${RESET}"

if [[ -n "$git_stats" ]]; then
  line1+="${SEP}✏️  ${GREEN}${git_stats}${RESET}"
fi

if [[ -n "$git_branch" ]]; then
  line1+="${SEP}πŸ”€ ${git_branch}"
fi

# ========== Line 2: Context window progress bar ==========
ctx_bar_color=$(color_for_pct "$ctx_pct_int")
ctx_bar=$(progress_bar "$ctx_pct_int")

line2="${ctx_bar_color}πŸ“ CTX  ${ctx_bar}  ${ctx_pct_int}%${RESET}"
if (( ctx_size > 0 )); then
  line2+="  ${DIM}${used_tokens_display} / ${ctx_size_display} tokens${RESET}"
fi

# ========== Line 3: Session cost ==========
line3=""
if [[ -n "$cost_display" ]]; then
  line3="${GREEN}πŸ’° ${cost_display}${RESET}"
else
  line3="${DIM}πŸ’° --${RESET}"
fi

# ---------- Output ----------
printf '%s\n' "$line1"
printf '%s\n' "$line2"
printf '%s' "$line3"

What it shows

  • Model display name
  • Context window usage as a percentage
  • Total lines added and removed in the session (+added/-removed)
  • Current git branch of the working directory
  • A 10-segment context window progress bar
  • Approximate used tokens and total context window size (e.g. 44K / 200K tokens)
  • Total session cost in US dollars

Requirements

  • Bash
  • jq, which the script uses for all JSON parsing
  • git, used to read the current branch
  • awk, used for rounding, token math, and cost formatting
  • A terminal that supports 24-bit ANSI color escape codes
  • A font that renders emoji (πŸ€– πŸ“Š ✏️ πŸ”€ πŸ“ πŸ’°) and the β–°/β–± bar characters
  • No network access; the script reads only stdin and the local git repository

Behavior notes

  • The context percentage and bar are green below 50%, yellow from 50% to 79%, and red at 80% or above (48% renders green, 64% yellow, 91% red).
  • The +added/-removed segment disappears when both line counts are zero, as in the brand-new session scenario.
  • The git branch segment disappears when the working directory is not a git repository, as in the scratch-directory scenario.
  • When used_percentage is null (brand-new session and just-after-/compact scenarios), the script falls back to 0%, an empty bar, and a 0-token count.
  • When total cost is zero, line 3 shows a dim 'πŸ’° --' instead of a dollar amount.
  • Token counts are shortened with K and M suffixes; the 1M-context scenario renders as 640K / 1M tokens.
  • The used-token figure is computed from the context window size times the used percentage, not from the input-token fields in the JSON.
  • The branch name comes from running git in the session's cwd, not from the JSON, so the worktree scenario shows the worktree's actual branch (worktree-feature).
  • Fields such as rate limits, vim mode, effort level, PR info, and output style vary across the scenarios but never appear in the output.

More status lines

Opus 4.8 [high] ~/app main 16:20:55 | ⛁ β–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ 22% | 5h ●◔○○○ 26% ↻2h7m | 7d β—”β—‹β—‹β—‹β—‹β—‹β—‹ 7% ↻2d1h β”‚ $0.41 ⏱ 10m

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

Pace Arrows

5 copies

Opus 4.8 (200K) high | app (main) β–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ 22% 200K | 5h 26% ⇣32% 2h 7d 7% ⇣63% 2d
app Β· main Β· Opus 4.8 Β· high ctx 22% 44k Β· 5h 26% ↻2h6m Β· 7d 7% ↻2d0h Β· cost $0.41 Β· msg $0.757
πŸ“ app 🌿 main 🧠 Opus 🧊 22% [==--------] πŸ’° $0.41 ($2.41/h) ⏳ 5h: 26% [==--------] 7d: 7% [----------]