statuslin.es

One Dark Triptych

Three lines in One Dark: model, thinking effort and a context bar up top; cost, quota, session clock and token throughput in the middle; weather, free memory and a rotating kaomoji below.

Updated 2026-09-07

0 copies

Preview

Clean repo
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
New session
Model: Opus 4.8 | Thinking: high | Context: [▱▱▱▱▱▱▱▱▱▱] 0/200k (0%) | cwd: ~/app | ⎇ main | (+0,-0) Cost: $0.00 | Session: 10m | In: 0.0 t/s | Out: 0.0 t/s Mem: 0.8G/1.0G | q(❂‿❂)p | Session ID: b8e1c0d2-4a6f-4e2a-9c1b-3f5d7a9e2c10
Dirty branch
Model: Sonnet 4.6 | Thinking: medium | Context: [▰▰▰▰▱▱▱▱▱▱] 97k/200k (48%) | cwd: ~/app | ⎇ feat/auth | (+1,-0) Cost: $0.41 | Block: 1hr 11m | Weekly: 18.0% | Session: 10m | In: 2000.0 t/s | Out: 29.2 t/s | Cached: 2k | Session: [▰▰▱▱▱] 40.0% Mem: 0.8G/1.0G | ꒰˘̩̩̩⌣˘̩̩̩๑꒱♡ | Session ID: b8e1c0d2-4a6f-4e2a-9c1b-3f5d7a9e2c10
Near-full
Model: Opus 4.8 | Thinking: max | Context: [▰▰▰▰▰▰▰▰▰▱] 183k/200k (91%) | cwd: ~/app | ⎇ main | (+0,-0) Cost: $4.12 | Block: 17m | Weekly: 61.0% | Session: 10m | In: 3791.7 t/s | Out: 29.2 t/s | Cached: 2k | Session: [▰▰▰▰▱] 88.0% Mem: 0.8G/1.0G | ヽ(͡◕ ͜ʖ ͡◕)ノ | Session ID: b8e1c0d2-4a6f-4e2a-9c1b-3f5d7a9e2c10
1M context
Model: Fable 5 | Thinking: xhigh | Context: [▰▰▰▰▰▰▱▱▱▱] 641k/1.0M (64%) | cwd: ~/app | ⎇ main | (+0,-0) Cost: $0.41 | Block: 3hr 29m | Weekly: 80.0% | Session: 10m | In: 13333.3 t/s | Out: 29.2 t/s | Cached: 2k | Session: [▰▱▱▱▱] 33.0% Mem: 0.8G/1.0G | ヽ(⌐■_■)ノ♪ | Session ID: b8e1c0d2-4a6f-4e2a-9c1b-3f5d7a9e2c10
Post-compact
Model: Haiku 4.5 | Context: [▱▱▱▱▱▱▱▱▱▱] 0/200k (0%) | cwd: ~/app | ⎇ main | (+0,-0) Cost: $0.41 | Block: 4hr 1m | Session: 10m | In: 0.0 t/s | Out: 0.0 t/s | Session: [▰▰▱▱▱] 52.0% Mem: 0.8G/1.0G | ᕦ(ò_óˇ)ᕤ | Session ID: b8e1c0d2-4a6f-4e2a-9c1b-3f5d7a9e2c10
Worktree
Model: Opus 4.8 | Thinking: low | Context: [▰▰▰▱▱▱▱▱▱▱] 75k/200k (37%) | cwd: ~/./feature | ⎇ worktree-feature | (+0,-0) Cost: $0.41 | Block: 2hr 49m | Weekly: 20.0% | Session: 10m | In: 1541.7 t/s | Out: 29.2 t/s | Cached: 2k | Session: [▰▰▱▱▱] 44.0% Mem: 0.8G/1.0G | (ノ◕ヮ◕)ノ*:・゚✧ | Session ID: b8e1c0d2-4a6f-4e2a-9c1b-3f5d7a9e2c10
Non-git
Model: Opus 4.8 | Thinking: high | Context: [▰▰▱▱▱▱▱▱▱▱] 45k/200k (22%) | cwd: ~/scratch Cost: $0.41 | Session: 10m | In: 916.7 t/s | Out: 29.2 t/s | Cached: 2k Mem: 0.8G/1.0G | ¯\_(ツ)_/¯ | refactor-pass | Session ID: b8e1c0d2-4a6f-4e2a-9c1b-3f5d7a9e2c10

Source

#!/usr/bin/env bash
# One Dark Triptych — a three-line Claude Code status line.
#
#   Model: Opus 5 | Thinking: high | Context: [▰▰▱▱▱▱▱▱▱▱] 123k/1.0M (12%) | cwd: ~/P/h/my-repo | ⎇ dev | (+12,-3)
#   Cost: $2.73 | Block: 3hr 42m | Weekly: 1.0% | Session: 12m | In: 0.5 t/s | Out: 119.7 t/s | Cached: 5.4M | Session: [▰▱▱▱▱] 10.0%
#   🌧 25°C feels 26°C 💨4.0m/s | Mem: 5.8G/16.0G | (*¯︶¯*) | Session ID: 93f52d0c-…
#
# Requires: jq. Optional: git, curl.
#
# Weather is opt-in. Without a key the widget is omitted and nothing touches the network:
#   export OPENWEATHER_API_KEY=...       # free key from openweathermap.org
#   export OPENWEATHER_CITY="Berlin,DE"  # defaults to London,GB

set -uo pipefail

input=$(cat)

command -v jq >/dev/null 2>&1 || { printf 'status line needs jq on PATH\n'; exit 0; }

# ── One Dark palette ────────────────────────────────────────────────────────
BLUE=$'\033[38;2;97;175;239m'
RED=$'\033[38;2;224;108;117m'
GREEN=$'\033[38;2;152;195;121m'
GREY=$'\033[38;2;171;178;191m'
PURPLE=$'\033[38;2;198;120;221m'
YELLOW=$'\033[38;2;229;192;123m'
ORANGE=$'\033[38;2;209;154;102m'
CYAN=$'\033[38;2;86;182;194m'
DIM=$'\033[38;2;92;99;112m'
RESET=$'\033[0m'
SEP="${DIM} | ${RESET}"

# ── Helpers ────────────────────────────────────────────────────────────────
int() { printf '%.0f' "${1:-0}" 2>/dev/null || printf '0'; }

# 1234 -> 1.2k, 5400000 -> 5.4M
human() {
  awk -v n="${1:-0}" 'BEGIN {
    if (n >= 1000000) printf "%.1fM", n / 1000000
    else if (n >= 1000) printf "%.0fk", n / 1000
    else printf "%d", n
  }'
}

# 10 -> ▰▱▱▱▱▱▱▱▱▱
bar() {
  awk -v p="${1:-0}" -v w="${2:-10}" 'BEGIN {
    if (p < 0) p = 0; if (p > 100) p = 100
    f = int(p * w / 100)
    for (i = 0; i < w; i++) printf (i < f ? "▰" : "▱")
  }'
}

# 3720 -> "1hr 2m", 720 -> "12m"
dur() {
  awk -v s="${1:-0}" 'BEGIN {
    h = int(s / 3600); m = int((s % 3600) / 60)
    if (h > 0) printf "%dhr %dm", h, m
    else if (m > 0) printf "%dm", m
    else printf "%ds", s
  }'
}

# ── Pull every field in one jq pass ────────────────────────────────────────
# One value per line rather than @tsv: tab counts as whitespace to `read`, so a
# tab-separated row silently collapses empty fields and shifts every later value.
F=()
while IFS= read -r line; do F+=("$line"); done < <(
  printf '%s' "$input" | jq -r '[
    .model.display_name                                    // "?",
    .effort.level                                          // "",
    (.thinking.enabled                                     // false | tostring),
    (.context_window.used_percentage                       // 0),
    ((.context_window.total_input_tokens // 0) + (.context_window.total_output_tokens // 0)),
    (.context_window.context_window_size                   // 200000),
    .workspace.current_dir                                 // .cwd // "",
    (.cost.total_cost_usd                                  // 0),
    (.cost.total_duration_ms                               // 0),
    (.cost.total_api_duration_ms                           // 0),
    (.context_window.total_input_tokens                    // 0),
    (.context_window.total_output_tokens                   // 0),
    (.context_window.current_usage.cache_read_input_tokens // 0),
    .session_id                                            // "",
    .session_name                                          // "",
    (.rate_limits.five_hour.used_percentage                // ""),
    (.rate_limits.five_hour.resets_at                      // ""),
    (.rate_limits.seven_day.used_percentage                // "")
  ] | .[] | tostring'
)

MODEL="${F[0]:-?}"     EFFORT="${F[1]:-}"    THINKING="${F[2]:-false}"
PCT="${F[3]:-0}"       TOK_TOTAL="${F[4]:-0}" CTX_SIZE="${F[5]:-200000}"
CWD="${F[6]:-}"        COST="${F[7]:-0}"     DUR_MS="${F[8]:-0}"
API_MS="${F[9]:-0}"    TOK_IN="${F[10]:-0}"  TOK_OUT="${F[11]:-0}"
CACHED="${F[12]:-0}"   SID="${F[13]:-}"      SNAME="${F[14]:-}"
H5_PCT="${F[15]:-}"    H5_RESET="${F[16]:-}" D7_PCT="${F[17]:-}"

# ── Line 1: model, thinking, context, location ─────────────────────────────
L1="${BLUE}Model: ${MODEL}${RESET}"

if [[ -n "$EFFORT" ]]; then
  L1+="${SEP}${RED}Thinking: ${EFFORT}${RESET}"
elif [[ "$THINKING" == "true" ]]; then
  L1+="${SEP}${RED}Thinking: on${RESET}"
fi

PCT_I=$(int "$PCT")
if   (( PCT_I >= 90 )); then CTX_COLOR="$RED"
elif (( PCT_I >= 70 )); then CTX_COLOR="$YELLOW"
else                         CTX_COLOR="$GREEN"
fi
L1+="${SEP}${CTX_COLOR}Context: [$(bar "$PCT_I" 10)] $(human "$TOK_TOTAL")/$(human "$CTX_SIZE") (${PCT_I}%)${RESET}"

# Fish-style path: ~ for home, every parent squashed to one character.
if [[ -n "$CWD" ]]; then
  SHORT="${CWD/#$HOME/\~}"
  IFS='/' read -r -a PARTS <<<"$SHORT"
  LAST=$(( ${#PARTS[@]} - 1 ))
  FISH=""
  for i in "${!PARTS[@]}"; do
    P="${PARTS[$i]}"
    [[ -z "$P" ]] && continue
    if   (( i == LAST ));  then FISH+="/${P}"
    elif [[ "$P" == "~" ]]; then FISH+="~"
    else                        FISH+="/${P:0:1}"
    fi
  done
  [[ "$SHORT" == /* ]] || FISH="${FISH#/}"
  L1+="${SEP}${GREY}cwd: ${FISH}${RESET}"
fi

if command -v git >/dev/null 2>&1 && git -C "${CWD:-.}" rev-parse --git-dir >/dev/null 2>&1; then
  BRANCH=$(git -C "$CWD" branch --show-current 2>/dev/null)
  [[ -z "$BRANCH" ]] && BRANCH=$(git -C "$CWD" rev-parse --short HEAD 2>/dev/null)
  [[ -n "$BRANCH" ]] && L1+="${SEP}${PURPLE}⎇ ${BRANCH}${RESET}"

  read -r ADDED REMOVED <<<"$(
    git -C "$CWD" diff --numstat HEAD 2>/dev/null |
      awk '$1 ~ /^[0-9]+$/ { a += $1 } $2 ~ /^[0-9]+$/ { r += $2 } END { print a+0, r+0 }'
  )"
  L1+="${SEP}${YELLOW}(+${ADDED:-0},-${REMOVED:-0})${RESET}"
fi

# ── Line 2: spend, quota, throughput ───────────────────────────────────────
L2="${GREEN}Cost: $(printf '$%.2f' "${COST:-0}")${RESET}"

if [[ -n "$H5_RESET" ]]; then
  LEFT=$(( $(int "$H5_RESET") - $(date +%s) ))
  # A five-hour window never has more than five hours left; anything larger is a
  # stale or bogus timestamp, so drop it rather than render "2281018hr".
  (( LEFT > 0 && LEFT <= 18000 )) && L2+="${SEP}${ORANGE}Block: $(dur "$LEFT")${RESET}"
fi

[[ -n "$D7_PCT" ]] && L2+="${SEP}${BLUE}Weekly: $(awk -v p="$D7_PCT" 'BEGIN { printf "%.1f", p }')%${RESET}"

L2+="${SEP}${CYAN}Session: $(dur $(( $(int "$DUR_MS") / 1000 )))${RESET}"

# Throughput, averaged over time actually spent inside API calls.
API_MS_I=$(int "$API_MS")
if (( API_MS_I > 0 )); then
  L2+="${SEP}${CYAN}In: $(awk -v t="$TOK_IN" -v ms="$API_MS_I" 'BEGIN { printf "%.1f", t / (ms / 1000) }') t/s${RESET}"
  L2+="${SEP}${BLUE}Out: $(awk -v t="$TOK_OUT" -v ms="$API_MS_I" 'BEGIN { printf "%.1f", t / (ms / 1000) }') t/s${RESET}"
fi

CACHED_I=$(int "$CACHED")
(( CACHED_I > 0 )) && L2+="${SEP}${DIM}Cached: $(human "$CACHED_I")${RESET}"

if [[ -n "$H5_PCT" ]]; then
  L2+="${SEP}${PURPLE}Session: [$(bar "$(int "$H5_PCT")" 5)] $(awk -v p="$H5_PCT" 'BEGIN { printf "%.1f", p }')%${RESET}"
fi

# ── Line 3: ambience ───────────────────────────────────────────────────────
L3=""

# Weather, cached for ten minutes so the API sees at most six calls an hour.
if [[ -n "${OPENWEATHER_API_KEY:-}" ]] && command -v curl >/dev/null 2>&1; then
  CITY="${OPENWEATHER_CITY:-London,GB}"
  CACHE="${TMPDIR:-/tmp}/statusline_wx_$(printf '%s' "$CITY" | tr -c 'a-zA-Z0-9' '_')"
  WX=""
  if [[ -f "$CACHE" ]]; then
    MTIME=$(stat -f %m "$CACHE" 2>/dev/null || stat -c %Y "$CACHE" 2>/dev/null || echo 0)
    (( $(date +%s) - MTIME < 600 )) && WX=$(cat "$CACHE")
  fi
  if [[ -z "$WX" ]]; then
    RESP=$(curl -fsS --max-time 3 \
      "https://api.openweathermap.org/data/2.5/weather?q=${CITY}&appid=${OPENWEATHER_API_KEY}&units=metric" 2>/dev/null)
    if [[ -n "$RESP" ]] && [[ "$(jq -r '.cod | tostring' <<<"$RESP" 2>/dev/null)" == "200" ]]; then
      read -r TEMP FEELS WIND CODE <<<"$(
        jq -r '[(.main.temp | round), (.main.feels_like | round), .wind.speed, .weather[0].id] | @tsv' <<<"$RESP"
      )"
      if   (( CODE >= 200 && CODE < 300 )); then ICON="⛈"
      elif (( CODE >= 300 && CODE < 500 )); then ICON="🌦"
      elif (( CODE >= 500 && CODE < 600 )); then ICON="🌧"
      elif (( CODE >= 600 && CODE < 700 )); then ICON="❄"
      elif (( CODE >= 700 && CODE < 800 )); then ICON="🌫"
      elif (( CODE == 800 ));                then ICON="☀"
      else                                        ICON="☁"
      fi
      WX="${ICON} ${TEMP}°C feels ${FEELS}°C 💨$(awk -v w="$WIND" 'BEGIN { printf "%.1f", w }')m/s"
      printf '%s' "$WX" >"$CACHE" 2>/dev/null
    elif [[ -f "$CACHE" ]]; then
      WX=$(cat "$CACHE")
    fi
  fi
  [[ -n "$WX" ]] && L3+="${CYAN}${WX}${RESET}"
fi

# Free / total memory, read the native way on each platform.
read -r MEM_FREE MEM_TOTAL <<<"$(
  if [[ -r /proc/meminfo ]]; then
    awk '/^MemAvailable:/ { f = $2 } /^MemTotal:/ { t = $2 } END { print f / 1048576, t / 1048576 }' /proc/meminfo
  elif command -v vm_stat >/dev/null 2>&1; then
    TOTAL_B=$(sysctl -n hw.memsize 2>/dev/null || echo 0)
    vm_stat | awk -v tb="$TOTAL_B" '
      /page size of/ { match($0, /[0-9]+/); ps = substr($0, RSTART, RLENGTH) }
      /Pages free/ || /Pages inactive/ { gsub(/\./, "", $NF); pages += $NF }
      END { if (ps > 0) print pages * ps / 1073741824, tb / 1073741824 }'
  fi
)"
if [[ -n "${MEM_TOTAL:-}" ]] && awk -v t="$MEM_TOTAL" 'BEGIN { exit !(t > 0) }'; then
  [[ -n "$L3" ]] && L3+="$SEP"
  L3+="${RED}Mem: $(awk -v f="$MEM_FREE" 'BEGIN { printf "%.1fG", f }')/$(awk -v t="$MEM_TOTAL" 'BEGIN { printf "%.1fG", t }')${RESET}"
fi

# A kaomoji that advances every second, so the bar always looks alive.
VIBES=(
  'ヽ(⌐■_■)ノ♪' 'ᕦ(ò_óˇ)ᕤ' '(ノ◕ヮ◕)ノ*:・゚✧' '¯\_(ツ)_/¯' 'ʕ•ᴥ•ʔ'
  '(╯°□°)╯︵ ┻━┻' '┬─┬ノ(º_ºノ)' '(☞゚ヮ゚)☞' 'ʕっ•ᴥ•ʔっ' '(•_•) ( •_•)>⌐■-■'
  'ヾ(•ω•)o' '(づ。◕‿‿◕。)づ' '( ˘ω˘ )zzZ' '٩(◕‿◕。)۶' 'o(≧▽≦)o'
  '(ò_ó)' 'щ(ಠ益ಠщ)' '∠( ᐛ 」∠)_' '(´。• ω •。)' 'ᕙ(⇀‸↼‶)ᕗ'
  '(˵ ͡° ͜ʖ ͡°˵)' '╰(*°▽°*)╯' '(ง •_•)ง' '¿ⓧ_ⓧミ' '(*¯︶¯*)'
  'ヽ(*・ω・)ノ' '(੭ˊ꒳ˋ)੭✧' 'ʕ·͡ˑ·ཻʔ' '(灬º‿º灬)♡' '( ͡~ ͜ʖ ͡°)'
  '\(٥⁀▽⁀ )/' '(¬‿¬)' '(*^▽^)/' 'ヽ(´▽`)/' '( •_•)>⌐▪-▪'
  '凸(¬‿¬)凸' '(ノ^_^)ノ' '~(>_<~)' '(ó﹏ò。)' '৲(͡ ˡ ͜ʖ ͡ˡ)৳'
  'ʕ •ₒ• ʔ' '(っ˘ڡ˘ς)' '( ͡° ͜ʖ ͡°)╭∩╮' 'ᕦ(ò‸ó)ᕥ' '(*゚∀゚*)'
  '(◕‿◕✿)' 'q(❂‿❂)p' '꒰˘̩̩̩⌣˘̩̩̩๑꒱♡' 'ヽ(͡◕ ͜ʖ ͡◕)ノ' '( ˶ˆᗜˆ˵ )'
)
FRAME=$(( (10#$(date +%M) * 60 + 10#$(date +%S)) % ${#VIBES[@]} ))
[[ -n "$L3" ]] && L3+="$SEP"
L3+="${GREY}${VIBES[$FRAME]}${RESET}"

[[ -n "$SNAME" ]] && L3+="${SEP}${PURPLE}${SNAME}${RESET}"
[[ -n "$SID" ]] && L3+="${SEP}${CYAN}Session ID: ${SID}${RESET}"

printf '%s\n%s\n%s\n' "$L1" "$L2" "$L3"

What it shows

  • Model display name, labeled Model:
  • Thinking effort when stdin includes effort.level
  • Context window as a 10-cell bar, used and total tokens, and a percent
  • Working directory shortened fish-style, with home as ~
  • Git branch and (+added,-removed) counts when the directory is a repo
  • Session cost in dollars
  • Time left in the five-hour window, labeled Block, when a reset timestamp is present and at most five hours away
  • Weekly quota as a percent
  • Session duration
  • Input and output token rates in t/s, averaged over API time
  • Cached token count when cache-read tokens are above zero
  • Five-hour usage as a 5-cell bar, also labeled Session
  • Free and total memory
  • A kaomoji that advances with the clock
  • Session name, when Claude Code sends one
  • The full session ID
  • Opt-in weather (icon, C, feels-like, wind) from OpenWeatherMap when OPENWEATHER_API_KEY is set

Requirements

  • bash
  • jq on PATH; without it the status line prints a one-line error and exits 0
  • git, optional, for branch and diff counts
  • curl plus OPENWEATHER_API_KEY to show weather; OPENWEATHER_CITY defaults to London,GB
  • Declared network host api.openweathermap.org, used only when the weather key is set
  • A 24-bit color terminal for the One Dark RGB palette
  • Writes a ten-minute weather cache under TMPDIR or /tmp

Behavior notes

  • Always three lines: identity and context, spend and quota, then memory, a kaomoji, and the session ID
  • A brand-new session still prints a zeroed context bar and $0.00, and drops Block, Weekly, Cached, and the five-hour Session bar because rate_limits are missing
  • After compact, Thinking disappears (no effort field) and Weekly is omitted; the five-hour Block and Session bar remain
  • Outside a git repo the branch and (+/-) counts vanish; a named session such as refactor-pass shows up on line 3
  • Context color is green below 70%, yellow from 70, red from 90; the 91% preview went red and 64% stayed green
  • Dirty feat/auth showed (+1,-0); clean main showed (+0,-0)
  • The kaomoji is different in every preview because it is keyed off minute and second
  • Weather never appeared in the sandbox previews; there is no OpenWeather key in that environment
  • The worktree preview printed cwd: ~/./feature and branch worktree-feature

More status lines

Dreambase Panel

171 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

43 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