1ac1efca87
althing 3.4.0 ships the segment as a console script on PATH, so the twelve lines that existed in three hand-maintained copies now exist in one. A script, unlike a document, has somewhere to be installed, and installing it makes drift impossible rather than merely visible. Two real defects left this file with the block, both silent: the handle was resolved as the most recent launch in a directory, so a directory hosting a claude and a codex seat reports the codex handle's unread count to a Claude session as soon as the codex pane relaunches last; and the post-office address was hardcoded, which survives until the post office moves and then reports an outage that is really a stale constant. Taken from the live path rather than pushed to it. ~/.claude/statusline- command.sh had been migrated directly and was AHEAD of this tracked copy, with a better `command -v` guard; a reflexive cp from the repo would have destroyed it. Repo and live are now byte-identical. The deploy instructions and the v3-cutover history move from the file header into docs/runbooks/althing-deploy.md, where they cannot drift against the script they describe, along with the diff-before-you-copy-in-both-directions warning that this near-miss earned.
146 lines
6.0 KiB
Bash
Executable File
146 lines
6.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Claude Code statusline. Layout:
|
|
# [📬N] [🔔/🔕/📵] | <proj> ⎇<branch> *<dirty> ↑<unpushed> | <model> | ctx:<pct> <toks> | $<session-cost> | 5h:% 7d:%
|
|
#
|
|
# 🔔 reachable — the post office will push to this session
|
|
# 🔕 pull-only — nothing will poke it; mail waits until it looks
|
|
# 📵 the post office could not be asked — an OUTAGE, not an empty inbox
|
|
# ctx% and rate-limit %s are threshold-colored: green <60, yellow 60-90, red >90.
|
|
# All segments degrade gracefully (missing tool / non-git dir / no handle => segment omitted).
|
|
input=$(cat)
|
|
|
|
# --- threshold color: $1=numeric pct, $2=display text -> colored text ---
|
|
color_pct() {
|
|
local p="$1" txt="$2" c
|
|
if awk "BEGIN{exit !($p < 60)}"; then c=$'\033[32m' # green <60
|
|
elif awk "BEGIN{exit !($p > 90)}"; then c=$'\033[31m' # red >90
|
|
else c=$'\033[33m' # yellow 60-90
|
|
fi
|
|
printf '%s%s\033[0m' "$c" "$txt"
|
|
}
|
|
|
|
# --- reset countdown: $1=unix ts -> "1d3h"/"3h20m"/"45m" (2-unit; "now"/empty edge) ---
|
|
reset_in() {
|
|
local ts="$1" now delta d h m
|
|
[ -z "$ts" ] && return
|
|
now=$(date +%s)
|
|
delta=$(( ts - now ))
|
|
[ "$delta" -le 0 ] && { printf 'now'; return; }
|
|
if [ "$delta" -ge 86400 ]; then
|
|
d=$(( delta / 86400 )); h=$(( (delta % 86400) / 3600 ))
|
|
if [ "$h" -gt 0 ]; then printf '%dd%dh' "$d" "$h"; else printf '%dd' "$d"; fi
|
|
elif [ "$delta" -ge 3600 ]; then
|
|
h=$(( delta / 3600 )); m=$(( (delta % 3600) / 60 ))
|
|
if [ "$m" -gt 0 ]; then printf '%dh%dm' "$h" "$m"; else printf '%dh' "$h"; fi
|
|
else
|
|
printf '%dm' $(( delta / 60 ))
|
|
fi
|
|
}
|
|
|
|
# --- one jq pass for every payload field ---
|
|
# \x1f (unit separator) delimiter, NOT tab: tab is IFS-whitespace so `read` would
|
|
# collapse consecutive tabs and shift every field after an empty one (e.g. a
|
|
# session with no rate_limits). \x1f is non-whitespace -> empty fields preserved.
|
|
IFS=$'\x1f' read -r model used_pct input_tok five_pct week_pct cwd fast cost_usd model_id five_reset week_reset < <(
|
|
printf '%s' "$input" | jq -r '[
|
|
(.model.display_name // "unknown"),
|
|
(.context_window.used_percentage // ""),
|
|
(.context_window.total_input_tokens // 0),
|
|
(.rate_limits.five_hour.used_percentage // ""),
|
|
(.rate_limits.seven_day.used_percentage // ""),
|
|
(.cwd // .workspace.current_dir // ""),
|
|
(.fast_mode // false),
|
|
(.cost.total_cost_usd // 0),
|
|
(.model.id // ""),
|
|
(.rate_limits.five_hour.resets_at // ""),
|
|
(.rate_limits.seven_day.resets_at // "")
|
|
] | map(tostring) | join("")'
|
|
)
|
|
[ -z "$model" ] && model="unknown"
|
|
|
|
# --- model (compact) + fast-mode flag ---
|
|
model="${model%% (*}" # "Opus 4.8 (1M context)" -> "Opus 4.8"
|
|
[ "$fast" = "true" ] && model="⚡$model"
|
|
|
|
# --- context % (colored) + absolute input tokens ---
|
|
if [ -n "$used_pct" ]; then
|
|
ctx_seg=$(color_pct "$used_pct" "ctx:$(printf '%.0f%%' "$used_pct")")
|
|
else
|
|
ctx_seg="ctx:--"
|
|
fi
|
|
if [ "${input_tok:-0}" -ge 1000 ] 2>/dev/null; then
|
|
toks=$(awk "BEGIN{printf \"%.0fk\", ${input_tok}/1000}")
|
|
else
|
|
toks="${input_tok:-0}"
|
|
fi
|
|
|
|
# --- per-session cost (Claude Code's own cache/model-aware accounting) ---
|
|
# adaptive precision: whole dollars once it's real money, cents when small.
|
|
cost=$(awk "BEGIN{c=${cost_usd:-0}; if(c>=100) printf \"%.0f\",c; else if(c>=10) printf \"%.1f\",c; else printf \"%.2f\",c}")
|
|
|
|
# --- rate limits (each % colored on the same thresholds, + reset countdown) ---
|
|
_rl() { # $1=pct $2=label $3=reset_ts -> "<label>:NN%·<reset>"
|
|
local seg r; seg=$(color_pct "$1" "$2:$(printf '%.0f' "$1")%")
|
|
r=$(reset_in "$3"); [ -n "$r" ] && seg="$seg·$r"
|
|
printf '%s' "$seg"
|
|
}
|
|
rate=""
|
|
[ -n "$five_pct" ] && rate=$(_rl "$five_pct" "5h" "$five_reset")
|
|
if [ -n "$week_pct" ]; then
|
|
wk=$(_rl "$week_pct" "7d" "$week_reset")
|
|
[ -n "$rate" ] && rate="$rate "
|
|
rate="${rate}${wk}"
|
|
fi
|
|
|
|
# --- project tag + git state (branch, dirty, unpushed) ---
|
|
proj=""; gitseg=""
|
|
if [ -n "$cwd" ]; then
|
|
proj=$(basename "$cwd")
|
|
if git -C "$cwd" rev-parse --git-dir >/dev/null 2>&1; then
|
|
br=$(git -C "$cwd" branch --show-current 2>/dev/null)
|
|
[ -z "$br" ] && br=$(git -C "$cwd" rev-parse --short HEAD 2>/dev/null)
|
|
dirty=$(git -C "$cwd" status --porcelain 2>/dev/null | grep -c .)
|
|
ahead=$(git -C "$cwd" rev-list --count '@{upstream}..HEAD' 2>/dev/null)
|
|
gitseg="⎇${br:-?}"
|
|
[ "${dirty:-0}" -gt 0 ] 2>/dev/null && gitseg="$gitseg *$dirty"
|
|
[ -n "$ahead" ] && [ "$ahead" -gt 0 ] 2>/dev/null && gitseg="$gitseg ↑$ahead"
|
|
fi
|
|
fi
|
|
|
|
# --- althing: unread count (📬 N) + reachability (🔔 push / 🔕 pull / 📵 outage) ---
|
|
# NOT implemented here any more. `althing-statusline` is the canonical segment,
|
|
# shipped as a console script by the althing package (3.4.0+).
|
|
#
|
|
# This block used to be one of THREE hand-maintained copies of the same twelve
|
|
# lines -- this file, althing's plugin/scripts/statusline.sh, and infra-ops'
|
|
# copy in eshpfi-management. An audit on 2026-09-02 found DIFFERENT defects in
|
|
# each and all three were fixed separately, by hand, on the same day. That is a
|
|
# drift surface with a countdown on it, and it is the same failure that left
|
|
# this very block dead for a month after the v3 cutover deleted the binary it
|
|
# gated on. A script, unlike a document, has somewhere to be installed: PATH.
|
|
#
|
|
# The payload goes in on STDIN -- the segment resolves the handle from the cwd
|
|
# it carries, and falls back to nothing rather than guessing.
|
|
#
|
|
# The outer `timeout` is deliberately LOOSER than the program's own 2s budget.
|
|
# If the outer one fired first we would get an empty segment, which reads as
|
|
# "not an althing directory" -- the outage conflation, reintroduced by the
|
|
# guard meant to prevent a hang.
|
|
althing=""; mon=""
|
|
if command -v althing-statusline >/dev/null 2>&1; then
|
|
althing=$(printf '%s' "$input" | timeout 5 althing-statusline 2>/dev/null)
|
|
fi
|
|
|
|
# --- assemble ---
|
|
lead="$althing"
|
|
[ -n "$mon" ] && lead="${lead:+$lead }$mon"
|
|
pg="$proj"
|
|
[ -n "$gitseg" ] && pg="${pg:+$pg }$gitseg"
|
|
|
|
parts="$lead"
|
|
[ -n "$pg" ] && parts="${parts:+$parts | }$pg"
|
|
parts="${parts:+$parts | }$model | $ctx_seg $toks | \$$cost"
|
|
[ -n "$rate" ] && parts="$parts | $rate"
|
|
|
|
printf '%s' "$parts"
|