Claude Code statusline

Claude Code statusline 配置

效果如下

!/bin/bash

for cmd in jq bc npx; do
command -v "$cmd" >/dev/null 2>&1 || { echo "statusline: missing dependency: $cmd" >&2; exit 1; }
done

INPUT=$(cat)
CACHE_DIR="/tmp/ccusage_cache"
mkdir -p "$CACHE_DIR" 2>/dev/null

# ── Colors ──

BLUE='\033[94m'
MAGENTA='\033[35m'
YELLOW='\033[33m'
GREEN='\033[32m'
RST='\033[0m'

# ── Helpers ──

jq_get() { echo "$INPUT" | jq -r "$1 // $2" 2>/dev/null; }

fmt_tokens() {
local t=$1
[[ "$t" =~ ^[0-9]+$ ]] || t=0
if (( t >= 1000000000 )); then
printf "%.1fB" "$(echo "scale=1; $t/1000000000" | bc)"
elif (( t >= 1000000 )); then
printf "%.1fM" "$(echo "scale=1; $t/1000000" | bc)"
elif (( t >= 1000 )); then
printf "%.0fk" "$(echo "scale=0; $t/1000" | bc)"
else
printf "%d" "$t"
fi
}

fmt_cost() { printf '$%.2f' "$1"; }

fmt_reset() {
local ts="$1"
[[ -z "$ts" || "$ts" == "null" || "$ts" == "0" ]] && return
if [[ "$ts" =~ ^[0-9]+$ ]]; then
date -j -f "%s" "$ts" "+%m-%d %H:%M" 2>/dev/null && return
date -d "@$ts" "+%m-%d %H:%M" 2>/dev/null && return
fi
}

fmt_rate() {
local label="$1" used="$2" reset_ts="$3"
if [[ -z "$used" || "$used" == "null" ]]; then
echo "${label}: --"
else
local s="${label}: $(printf "%.2f" "${used:-0}")%"
local reset=$(fmt_reset "$reset_ts")
[[ -n "$reset" ]] && s="$s @${reset}"
echo "$s"
fi
}

get_mtime() {
if [[ "$(uname)" == "Darwin" ]]; then
stat -f %m "$1" 2>/dev/null || echo 0
else
stat -c %Y "$1" 2>/dev/null || echo 0
fi
}

row() { local c="$1"; echo -e "${c}$(printf "%-${W}s" "$2")${RST} | ${c}$(printf "%-${W}s" "$3")${RST} | ${c}$(printf "%-${W}s" "$4")${RST}"; }

# ── Parse CC JSON ──

MODEL_NAME=$(jq_get '.model.display_name' '"Claude"')
INPUT_TOKENS=$(jq_get '.context_window.total_input_tokens' '0')
OUTPUT_TOKENS=$(jq_get '.context_window.total_output_tokens' '0')
SESSION_COST=$(jq_get '.cost.total_cost_usd' '0')
CTX_USED=$(jq_get '.context_window.used_percentage' '0')
RATE_5H_USED=$(jq_get '.rate_limits.five_hour.used_percentage' '""')
RATE_5H_RESET=$(jq_get '.rate_limits.five_hour.resets_at' '""')
RATE_7D_USED=$(jq_get '.rate_limits.seven_day.used_percentage' '""')
RATE_7D_RESET=$(jq_get '.rate_limits.seven_day.resets_at' '""')

SESSION_TOKENS=$(( INPUT_TOKENS + OUTPUT_TOKENS ))

# ── ccusage cache (non-blocking) ──

CACHE_FILE="$CACHE_DIR/daily.json"
CACHE_LOCK="$CACHE_DIR/daily.lock"
CACHE_TTL=300
LOCK_TTL=60
now=$(date +%s)
cache_age=999999

if [[ -f "$CACHE_FILE" ]]; then
cache_age=$(( now - $(get_mtime "$CACHE_FILE") ))
fi

if [[ -f "$CACHE_LOCK" ]]; then
lock_age=$(( now - $(get_mtime "$CACHE_LOCK") ))
(( lock_age > LOCK_TTL )) && rm -f "$CACHE_LOCK"
fi

if (( cache_age > CACHE_TTL )) && ! [[ -f "$CACHE_LOCK" ]]; then
(
touch "$CACHE_LOCK"
npx ccusage@latest daily --json --since "$(date +%Y%m01)" \
> "${CACHE_FILE}.tmp" 2>/dev/null \
&& mv "${CACHE_FILE}.tmp" "$CACHE_FILE"
rm -f "$CACHE_LOCK"
) &
disown
fi

# ── Read cached usage data ──

TODAY_COST="0"; TODAY_TOKENS="0"; MONTH_COST="0"; MONTH_TOKENS="0"

if [[ -f "$CACHE_FILE" ]]; then
td=$(jq -r --arg d "$(date +%Y-%m-%d)" '.daily[]? | select(.date == $d)' "$CACHE_FILE" 2>/dev/null)
if [[ -n "$td" ]]; then
TODAY_COST=$(echo "$td" | jq -r '.totalCost // 0')
TODAY_TOKENS=$(echo "$td" | jq -r '.totalTokens // 0')
fi
mt=$(jq -r '.totals // empty' "$CACHE_FILE" 2>/dev/null)
if [[ -n "$mt" ]]; then
MONTH_COST=$(echo "$mt" | jq -r '.totalCost // 0')
MONTH_TOKENS=$(echo "$mt" | jq -r '.totalTokens // 0')
fi
fi

# ── Git stats ──

IS_GIT_REPO=0
FILES_CHANGED=0
if git rev-parse --git-dir >/dev/null 2>&1; then
IS_GIT_REPO=1
unstaged=$(git diff --numstat 2>/dev/null | wc -l)
staged=$(git diff --cached --numstat 2>/dev/null | wc -l)
FILES_CHANGED=$(( unstaged + staged ))
LINES_ADD=$(jq_get '.cost.total_lines_added' '0')
LINES_DEL=$(jq_get '.cost.total_lines_removed' '0')
fi

# ── Output ──

W=32

row "$BLUE" "[${MODEL_NAME}]" \
"$(printf "in: %-6s" "$(fmt_tokens "$INPUT_TOKENS")")" \
"$(printf "out: %-6s" "$(fmt_tokens "$OUTPUT_TOKENS")")"

row "$MAGENTA" "$(fmt_rate "5h" "$RATE_5H_USED" "$RATE_5H_RESET")" \
"$(fmt_rate "1w" "$RATE_7D_USED" "$RATE_7D_RESET")" \
"ctx: $(printf "%.2f" "${CTX_USED:-0}")%"

row "$YELLOW" "$(printf "session: %s (%s)" "$(fmt_cost "$SESSION_COST")" "$(fmt_tokens "$SESSION_TOKENS")")" \
"$(printf "today: %s (%s)" "$(fmt_cost "$TODAY_COST")" "$(fmt_tokens "$TODAY_TOKENS")")" \
"$(printf "month: %s (%s)" "$(fmt_cost "$MONTH_COST")" "$(fmt_tokens "$MONTH_TOKENS")")"

if (( IS_GIT_REPO == 1 )); then
row "$GREEN" "$(printf "%d files changed" "$FILES_CHANGED")" \
"$(printf "+%d added" "$LINES_ADD")" \
"$(printf -- "-%d removed" "$LINES_DEL")"
fi
"statusLine": {
"type": "command",
"command": "bash ~/.claude/statusline.sh"
},