|
#!/bin/sh |
|
# ============================================================================ |
|
# Full colored status line for Claude Code — one-line installer |
|
# ============================================================================ |
|
# |
|
# curl -fsSL https://gist.githubusercontent.com/roeniss/c04ce1db92eb8de7b6b6dc3a52d925ae/raw/claude-code-statusline-install.sh | sh |
|
# |
|
# Shows: <dir> [repo:branch] [model effort] [ctx %] [5h/7d limits] |
|
# [PR #n state] [vim mode] [output style] <session name> |
|
# |
|
# What it does: |
|
# • installs ~/.claude/statusline-command.sh |
|
# • adds "statusLine" to ~/.claude/settings.json (merges; never clobbers |
|
# other keys; backs the file up first if a status line already exists) |
|
# |
|
# Requires: jq (macOS: brew install jq • Debian: sudo apt install jq) |
|
# Safe to re-run. Restart Claude Code afterwards to see it. |
|
# ============================================================================ |
|
set -e |
|
|
|
CLAUDE_DIR="$HOME/.claude" |
|
SCRIPT="$CLAUDE_DIR/statusline-command.sh" |
|
SETTINGS="$CLAUDE_DIR/settings.json" |
|
|
|
# --- dependency check ------------------------------------------------------- |
|
if ! command -v jq >/dev/null 2>&1; then |
|
echo "error: 'jq' is required but not installed." >&2 |
|
echo " macOS: brew install jq" >&2 |
|
echo " Debian: sudo apt install jq" >&2 |
|
exit 1 |
|
fi |
|
|
|
mkdir -p "$CLAUDE_DIR" |
|
|
|
# --- write the status line script ------------------------------------------- |
|
# Quoted heredoc: nothing below is expanded, written verbatim. |
|
cat > "$SCRIPT" <<'STATUSLINE_EOF' |
|
#!/bin/sh |
|
# Full colored statusline for Claude Code. |
|
# Layout: <dir> [repo:branch] [model effort] [ctx N%] [5h:N% 7d:N%] [PR #n state] [vim MODE] [output_style] <session> |
|
input=$(cat) |
|
|
|
cwd=$(printf '%s' "$input" | jq -r '.cwd // .workspace.current_dir // ""') |
|
model=$(printf '%s' "$input" | jq -r '.model.display_name // ""') |
|
effort=$(printf '%s' "$input" | jq -r '.effort.level // empty') |
|
used_pct=$(printf '%s' "$input" | jq -r '.context_window.used_percentage // empty') |
|
repo=$(printf '%s' "$input" | jq -r '.workspace.repo | if . then .owner+"/"+.name else empty end') |
|
branch=$(printf '%s' "$input" | jq -r '.worktree.branch // empty') |
|
git_worktree=$(printf '%s' "$input" | jq -r '.workspace.git_worktree // empty') |
|
pr_number=$(printf '%s' "$input" | jq -r '.pr.number // empty') |
|
pr_state=$(printf '%s' "$input" | jq -r '.pr.review_state // empty') |
|
session_name=$(printf '%s' "$input" | jq -r '.session_name // empty') |
|
five_hour=$(printf '%s' "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty') |
|
seven_day=$(printf '%s' "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty') |
|
vim_mode=$(printf '%s' "$input" | jq -r '.vim.mode // empty') |
|
output_style=$(printf '%s' "$input" | jq -r '.output_style.name // empty') |
|
worktree_name=$(printf '%s' "$input" | jq -r '.worktree.name // empty') |
|
|
|
short_cwd=$(printf '%s' "$cwd" | sed "s|^$HOME|~|") |
|
|
|
ESC=$(printf '\033') |
|
R="${ESC}[0m" # reset |
|
DIM="38;2;120;120;120" # bracket/punctuation color |
|
out="" |
|
|
|
# append joined by two spaces |
|
add() { if [ -n "$out" ]; then out="$out $1"; else out="$1"; fi; } |
|
|
|
# colored "[text]" with dim brackets and given fg color for text |
|
brk() { # $1=fg "r;g;b" $2=text |
|
printf '%s' "${ESC}[${DIM}m[${ESC}[38;2;$1m$2${ESC}[${DIM}m]${R}" |
|
} |
|
|
|
# Directory (no brackets, leading element) |
|
[ -n "$short_cwd" ] && add "${ESC}[38;2;90;150;210m${short_cwd}${R}" |
|
|
|
# Git [repo:branch] or [repo:worktree] |
|
gtxt="$repo" |
|
if [ -n "$branch" ]; then |
|
gtxt="${gtxt:+$gtxt:}$branch" |
|
elif [ -n "$git_worktree" ]; then |
|
gtxt="${gtxt:+$gtxt:}$git_worktree" |
|
fi |
|
[ -n "$worktree_name" ] && gtxt="${gtxt:+$gtxt }(wt:$worktree_name)" |
|
[ -n "$gtxt" ] && add "$(brk "90;190;110" "$gtxt")" |
|
|
|
# Model [model effort] |
|
mtxt="$model" |
|
[ -n "$effort" ] && mtxt="$mtxt $effort" |
|
[ -n "$mtxt" ] && add "$(brk "175;150;230" "$mtxt")" |
|
|
|
# Context [ctx N%] — color scales with pressure |
|
if [ -n "$used_pct" ]; then |
|
ci=$(printf '%.0f' "$used_pct") |
|
if [ "$ci" -ge 80 ]; then cf="225;95;95" |
|
elif [ "$ci" -ge 50 ]; then cf="225;185;70" |
|
else cf="120;170;200"; fi |
|
add "$(brk "$cf" "ctx ${ci}%")" |
|
fi |
|
|
|
# Rate limits [5h:N% 7d:N%] |
|
rate_txt="" |
|
if [ -n "$five_hour" ]; then |
|
fh=$(printf '%.0f' "$five_hour") |
|
rate_txt="5h:${fh}%" |
|
fi |
|
if [ -n "$seven_day" ]; then |
|
sd=$(printf '%.0f' "$seven_day") |
|
rate_txt="${rate_txt:+$rate_txt }7d:${sd}%" |
|
fi |
|
[ -n "$rate_txt" ] && add "$(brk "200;140;90" "$rate_txt")" |
|
|
|
# Open PR [PR #n state] |
|
if [ -n "$pr_number" ]; then |
|
pr_txt="PR #$pr_number" |
|
[ -n "$pr_state" ] && pr_txt="$pr_txt $pr_state" |
|
add "$(brk "220;160;80" "$pr_txt")" |
|
fi |
|
|
|
# Vim mode [vim MODE] |
|
if [ -n "$vim_mode" ]; then |
|
case "$vim_mode" in |
|
INSERT) vc="100;200;100" ;; |
|
NORMAL) vc="100;160;220" ;; |
|
VISUAL*) vc="220;160;220" ;; |
|
*) vc="200;200;100" ;; |
|
esac |
|
add "$(brk "$vc" "$vim_mode")" |
|
fi |
|
|
|
# Output style — only show when not default |
|
if [ -n "$output_style" ] && [ "$output_style" != "default" ]; then |
|
add "$(brk "160;160;160" "$output_style")" |
|
fi |
|
|
|
# Session name (from /rename) |
|
[ -n "$session_name" ] && add "${ESC}[38;2;150;150;150m${session_name}${R}" |
|
|
|
printf '%s' "$out" |
|
STATUSLINE_EOF |
|
|
|
chmod +x "$SCRIPT" |
|
echo "✓ wrote $SCRIPT" |
|
|
|
# --- wire it into settings.json --------------------------------------------- |
|
# Absolute path resolved at install time → works on any machine/username. |
|
STATUSLINE_JSON=$(printf '{"type":"command","command":"sh %s/statusline-command.sh"}' "$CLAUDE_DIR") |
|
|
|
if [ -f "$SETTINGS" ]; then |
|
# Warn if a status line is already configured — it will be overwritten. |
|
existing=$(jq -r '.statusLine.command // empty' "$SETTINGS" 2>/dev/null || true) |
|
if [ -n "$existing" ]; then |
|
echo "" >&2 |
|
echo "⚠ an existing status line is already configured:" >&2 |
|
echo " $existing" >&2 |
|
echo " It will be replaced. Remove any other statusline tooling (e.g. ccstatusline)" >&2 |
|
echo " if you don't want both active." >&2 |
|
backup="$SETTINGS.bak.$$" |
|
cp "$SETTINGS" "$backup" |
|
echo " A backup was saved to: $backup" >&2 |
|
echo "" >&2 |
|
fi |
|
tmp=$(mktemp) |
|
jq --argjson sl "$STATUSLINE_JSON" '.statusLine = $sl' "$SETTINGS" > "$tmp" && mv "$tmp" "$SETTINGS" |
|
echo "✓ merged statusLine into existing $SETTINGS" |
|
else |
|
jq -n --argjson sl "$STATUSLINE_JSON" '{statusLine: $sl}' > "$SETTINGS" |
|
echo "✓ created $SETTINGS" |
|
fi |
|
|
|
echo "" |
|
echo "Done. Restart Claude Code (or start a new session) to see the status line." |
Result: