87 lines
3.5 KiB
Bash
Executable File
87 lines
3.5 KiB
Bash
Executable File
#!/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 '<command-name>/handoff</command-name>' \
|
|
-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"
|