#!/usr/bin/env bash # ~/.claude/statusline.sh # Claude Code status line: dir | git branch | model | token count input=$(cat) # 1. Current directory (abbreviate $HOME to ~) cwd=$(printf '%s' "$input" | jq -r '.workspace.current_dir // .cwd // empty' 2>/dev/null) if [ -z "$cwd" ]; then cwd="$PWD" fi cwd="${cwd/#$HOME/\~}" # 2. Git branch (if in a git repo) branch="" if command -v git >/dev/null 2>&1; then branch=$(git -C "${cwd/#\~/$HOME}" rev-parse --abbrev-ref HEAD 2>/dev/null) [ "$branch" = "HEAD" ] && branch=$(git -C "${cwd/#\~/$HOME}" rev-parse --short HEAD 2>/dev/null) fi # 3. Model display name and id (id used to size the context window below) model=$(printf '%s' "$input" | jq -r '.model.display_name // empty' 2>/dev/null) model_id=$(printf '%s' "$input" | jq -r '.model.id // .model.display_name // empty' 2>/dev/null) # 4. Context-window occupancy from transcript JSONL. # Matches /context: input + cache_read + cache_creation of the most recent # request is exactly what currently occupies the context window. The usage # object nests sub-objects, so parse each JSONL line as JSON rather than # grepping (a brace-bounded grep truncates at the first nested "}"). ctx="" transcript=$(printf '%s' "$input" | jq -r '.transcript_path // empty' 2>/dev/null) if [ -n "$transcript" ] && [ -r "$transcript" ] && command -v jq >/dev/null 2>&1; then total=$(tail -n 300 "$transcript" 2>/dev/null | jq -rR ' fromjson? | (.message.usage // .usage) // empty | select(.input_tokens != null) | (.input_tokens + (.cache_read_input_tokens // 0) + (.cache_creation_input_tokens // 0)) ' 2>/dev/null | tail -1) if [ -n "$total" ] && [ "$total" -gt 0 ] 2>/dev/null; then # 1M-context models report a [1m]/"1M" marker in the id/name; else 200k. case "$model_id" in *1m*|*1M*) limit=1000000; label="1m" ;; *) limit=200000; label="200k" ;; esac ctx=$(awk -v t="$total" -v lim="$limit" -v lab="$label" 'BEGIN { if (t >= 1000) ts = sprintf("%.1fk", t/1000); else ts = sprintf("%d", t); printf "%s/%s tokens (%.0f%%)", ts, lab, t*100/lim }') # Turn the token segment red once context reaches 200k; reset after so the # rest of the line keeps its default styling. if [ "$total" -ge 200000 ] 2>/dev/null; then ctx=$(printf '\033[31m%s\033[0m' "$ctx") fi fi fi # 5. Handoff-ritual reminder. Green once /handoff has been invoked this session, # red until then — a nudge to run the session-close ritual (update AGENTS.md, # commit, push). Detected from the transcript: the slash-command marker the # harness writes when /handoff is typed, or the Skill-tool form when it is # invoked programmatically. Note this detects invocation, not completion. handoff=$(printf '\033[31m/handoff NOT RUN YET\033[0m') if [ -n "$transcript" ] && [ -r "$transcript" ] && command -v grep >/dev/null 2>&1; then if grep -qF -e '/handoff' \ -e '"name":"Skill","input":{"skill":"handoff"}' "$transcript" 2>/dev/null; then handoff=$(printf '\033[32m/handoff successfully run\033[0m') fi fi # Assemble the line, separating non-empty segments with " | " parts=() [ -n "$cwd" ] && parts+=("$cwd") [ -n "$branch" ] && parts+=("$branch") [ -n "$model" ] && parts+=("$model") [ -n "$ctx" ] && parts+=("$ctx") [ -n "$handoff" ] && parts+=("$handoff") joined="" for part in "${parts[@]}"; do if [ -z "$joined" ]; then joined="$part" else joined="$joined | $part" fi done printf '%s' "$joined"