ccstatusline-config/statusline.sh
zaksal58 9168133823 네이티브 statusline 스크립트 및 자동 설치/업데이트 도구 추가
Claude Code 네이티브 statusLine 방식의 4줄 상태바 스크립트와
어느 머신에서든 한 줄로 설치·업데이트할 수 있는 스크립트 추가.
크로스플랫폼(Linux/macOS) stat 호환 처리 포함.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 00:26:02 +09:00

209 lines
6.6 KiB
Bash
Executable File

#!/usr/bin/env bash
# Claude Code 4-line statusline
# Reads JSON from stdin, outputs ANSI-colored 4-line status bar
# --- Color definitions (bright variants for dark backgrounds) ---
RED=$'\033[91m'
GREEN=$'\033[92m'
YELLOW=$'\033[93m'
BLUE=$'\033[94m'
MAGENTA=$'\033[95m'
CYAN=$'\033[96m'
GRAY=$'\033[37m'
WHITE=$'\033[97m'
BOLD=$'\033[1m'
DIM=$'\033[2m'
RESET=$'\033[0m'
SEP="${GRAY}${RESET}"
# --- Read all JSON input ---
input=$(cat)
# --- Single jq call: extract all 16 fields newline-separated ---
# Using newlines instead of tabs to avoid bash IFS merging consecutive delimiters
mapfile -t _fields < <(echo "$input" | jq -r '
(.model.display_name // .model.id // "?"),
(.model.id // "?"),
(.context_window.used_percentage // 0 | floor | tostring),
(.context_window.context_window_size // 0 | tostring),
(.context_window.total_input_tokens // 0 | tostring),
(.context_window.total_output_tokens // 0 | tostring),
(.cost.total_cost_usd // 0 | . * 100 | floor | . / 100 | tostring),
(.cost.total_duration_ms // 0 | tostring),
(.cost.total_lines_added // 0 | tostring),
(.cost.total_lines_removed // 0 | tostring),
(if .session_id == null or .session_id == "" then "--------" else .session_id end),
(.version // "?.?.?"),
(if .output_style.name == null or .output_style.name == "" then "default" else .output_style.name end),
(.workspace.current_dir // "~"),
(.vim.mode // ""),
(.agent.name // "")
')
model_name="${_fields[0]}"
model_id="${_fields[1]}"
ctx_pct="${_fields[2]}"
ctx_size="${_fields[3]}"
total_in_tok="${_fields[4]}"
total_out_tok="${_fields[5]}"
cost="${_fields[6]}"
dur_ms="${_fields[7]}"
lines_add="${_fields[8]}"
lines_rm="${_fields[9]}"
session_id="${_fields[10]}"
version="${_fields[11]}"
style="${_fields[12]}"
cwd="${_fields[13]}"
vim_mode="${_fields[14]}"
agent_name="${_fields[15]}"
# --- Hostname (cached in variable, never changes) ---
HOSTNAME_SHORT=$(hostname -s 2>/dev/null || echo "unknown")
# --- Context progress bar (12 chars wide) ---
pct=${ctx_pct:-0}
filled=$(( pct * 12 / 100 ))
empty=$(( 12 - filled ))
if [ "$pct" -ge 70 ]; then
bar_color="$RED"
elif [ "$pct" -ge 50 ]; then
bar_color="$YELLOW"
else
bar_color="$GREEN"
fi
bar_filled=""
bar_empty=""
for (( i=0; i<filled; i++ )); do bar_filled+="█"; done
for (( i=0; i<empty; i++ )); do bar_empty+="░"; done
progress_bar="${bar_color}${bar_filled}${GRAY}${bar_empty}${RESET}"
pct_display="${bar_color}${pct}%${RESET}"
# --- Cost color ---
cost_val=${cost:-0}
# Compare using awk for floating-point
cost_display=$(awk -v c="$cost_val" '
BEGIN {
if (c+0 >= 5) col="\033[91m";
else if (c+0 >= 2) col="\033[93m";
else col="\033[92m";
printf "%s$%.2f\033[0m", col, c+0
}')
# --- Token formatting ---
format_tokens() {
local tok=$1
if [ "$tok" -ge 1000000 ] 2>/dev/null; then
echo "$(( tok / 1000000 ))M"
elif [ "$tok" -ge 1000 ] 2>/dev/null; then
echo "$(( tok / 1000 ))k"
else
echo "${tok}"
fi
}
in_tok_display=$(format_tokens "${total_in_tok:-0}")
# --- Model tier symbol ---
case "$model_id" in
*opus*) tier_sym="${MAGENTA}${BOLD}${RESET}"; model_color="${MAGENTA}${BOLD}" ;;
*sonnet*) tier_sym="${BLUE}${BOLD}${RESET}"; model_color="${BLUE}${BOLD}" ;;
*haiku*) tier_sym="${GREEN}${BOLD}${RESET}"; model_color="${GREEN}${BOLD}" ;;
*) tier_sym="${GRAY}${RESET}"; model_color="${GRAY}" ;;
esac
# --- Session ID (first 8 chars) ---
session_short="${session_id:0:8}"
# --- Duration formatting ---
dur_total_sec=$(( ${dur_ms:-0} / 1000 ))
if [ "$dur_total_sec" -ge 3600 ]; then
dur_h=$(( dur_total_sec / 3600 ))
dur_m=$(( (dur_total_sec % 3600) / 60 ))
dur_display="${dur_h}h ${dur_m}m"
elif [ "$dur_total_sec" -ge 60 ]; then
dur_m=$(( dur_total_sec / 60 ))
dur_s=$(( dur_total_sec % 60 ))
dur_display="${dur_m}m ${dur_s}s"
else
dur_display="${dur_total_sec}s"
fi
# --- Git branch (cached with 5s TTL) ---
CACHE_FILE="/tmp/claude-statusline-git-cache"
NOW=$(date +%s)
GIT_BRANCH=""
if [ -f "$CACHE_FILE" ]; then
CACHE_AGE=$(( NOW - $(stat -c %Y "$CACHE_FILE" 2>/dev/null || stat -f %m "$CACHE_FILE" 2>/dev/null || echo 0) ))
if [ "$CACHE_AGE" -lt 5 ]; then
GIT_BRANCH=$(cat "$CACHE_FILE")
fi
fi
if [ -z "$GIT_BRANCH" ]; then
GIT_BRANCH=$(cd "${cwd:-$HOME}" 2>/dev/null && git symbolic-ref --short HEAD 2>/dev/null || echo "")
echo "$GIT_BRANCH" > "$CACHE_FILE"
fi
# ============================================================
# LINE 1: Identity & Resources
# ============================================================
line1="\xf0\x9f\x8e\xa8 ${WHITE}${style}${RESET}"
line1+="${SEP}"
line1+="\xf0\x9f\x96\xa5\xef\xb8\x8f ${CYAN}${BOLD}${HOSTNAME_SHORT}${RESET}"
line1+="${SEP}"
line1+="\xf0\x9f\xa7\xa0 [${progress_bar}] ${pct_display}"
line1+="${SEP}"
line1+="\xf0\x9f\x92\xb0 ${cost_display}"
line1+=" ${GRAY}${in_tok_display} tokens${RESET}"
# ============================================================
# LINE 2: Model & Session
# ============================================================
line2="\xf0\x9f\xa4\x96 ${tier_sym} ${model_color}${model_name}${RESET}"
line2+="${SEP}"
line2+="\xf0\x9f\x94\x91 ${GRAY}${session_short}${RESET}"
line2+="${SEP}"
line2+="\xf0\x9f\x93\x9f ${CYAN}v${version}${RESET}"
# ============================================================
# LINE 3: Workspace & Tools
# ============================================================
line3="\xf0\x9f\x93\x81 ${CYAN}${cwd}${RESET}"
if [ -n "$GIT_BRANCH" ]; then
line3+="${SEP}"
line3+="\xf0\x9f\x8c\xbf ${GREEN}${BOLD}${GIT_BRANCH}${RESET}"
fi
line3+="${SEP}"
line3+="\xf0\x9f\x93\x8a ${GREEN}▲+${lines_add}${RESET} ${RED}▼-${lines_rm}${RESET}"
line3+="${SEP}"
line3+="\xe2\x8f\xb1\xef\xb8\x8f ${YELLOW}${dur_display}${RESET}"
# ============================================================
# LINE 4: Mode & Status (conditional)
# ============================================================
line4="\xe2\x8f\xb5\xe2\x8f\xb5 ${BOLD}${style}${RESET}"
if [ -n "$vim_mode" ]; then
vim_upper=$(echo "$vim_mode" | tr '[:lower:]' '[:upper:]')
case "$vim_upper" in
NORMAL) vim_color="$BLUE" ;;
INSERT) vim_color="$GREEN" ;;
*) vim_color="$WHITE" ;;
esac
line4+="${SEP}"
line4+="\xf0\x9f\x94\xb2 ${vim_color}${vim_upper}${RESET}"
fi
if [ -n "$agent_name" ]; then
line4+="${SEP}"
line4+="\xf0\x9f\x8f\xb7\xef\xb8\x8f ${MAGENTA}${agent_name}${RESET}"
fi
# ============================================================
# Output all 4 lines
# ============================================================
printf '%b\n' "$line1"
printf '%b\n' "$line2"
printf '%b\n' "$line3"
printf '%b\n' "$line4"