Add claude statusline to my dotfiles
This commit is contained in:
parent
2d18961ebc
commit
f9e4c65001
2 changed files with 154 additions and 0 deletions
|
|
@ -41,6 +41,10 @@ test -L $HOME/.config/nvim/init.lua || ln -f -s $BASEDIR/neovim/init.lua $HOME/.
|
||||||
test -d $HOME/.config/nvim/lsp || mkdir -p $HOME/.config/nvim/lsp
|
test -d $HOME/.config/nvim/lsp || mkdir -p $HOME/.config/nvim/lsp
|
||||||
test -L $HOME/.config/nvim/lsp/phpactor.lua || ln -f -s $BASEDIR/neovim/lsp/phpactor.lua $HOME/.config/nvim/lsp/phpactor.lua
|
test -L $HOME/.config/nvim/lsp/phpactor.lua || ln -f -s $BASEDIR/neovim/lsp/phpactor.lua $HOME/.config/nvim/lsp/phpactor.lua
|
||||||
|
|
||||||
|
# Claude Code statusline
|
||||||
|
test -d $HOME/.claude || mkdir $HOME/.claude
|
||||||
|
test -L $HOME/.claude/statusline.sh || ln -f -s $BASEDIR/claude/statusline.sh $HOME/.claude/statusline.sh
|
||||||
|
|
||||||
# .gitconfig gets edited by .extra so we won't symlink it, but copy it
|
# .gitconfig gets edited by .extra so we won't symlink it, but copy it
|
||||||
echo "For compatibility we shall copy the global gitconfig"
|
echo "For compatibility we shall copy the global gitconfig"
|
||||||
cp $BASEDIR/gitconfig $HOME/.gitconfig
|
cp $BASEDIR/gitconfig $HOME/.gitconfig
|
||||||
|
|
|
||||||
150
claude/statusline.sh
Executable file
150
claude/statusline.sh
Executable file
|
|
@ -0,0 +1,150 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Status line command for Claude Code
|
||||||
|
# Two-line display:
|
||||||
|
# Line 1: [Opus 4.6] 📁 project-name | 🌿 branch
|
||||||
|
# Line 2: █████░░░░░ 42% | 19.8k tok | ✅ Max | ⏱️ 7m 3s (API thinking time, not wall-clock)
|
||||||
|
|
||||||
|
# Read JSON input from Claude Code
|
||||||
|
input=$(cat)
|
||||||
|
|
||||||
|
# Debug mode: set CLAUDE_STATUSLINE_DEBUG=1 to dump JSON input
|
||||||
|
if [ -n "$CLAUDE_STATUSLINE_DEBUG" ]; then
|
||||||
|
echo "$input" | jq '.' > /tmp/claude-statusline-debug.json 2>/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract data from JSON (single jq call for performance)
|
||||||
|
eval "$(echo "$input" | jq -r '
|
||||||
|
@sh "cwd=\(.workspace.current_dir)",
|
||||||
|
@sh "model_display=\(.model.display_name)",
|
||||||
|
@sh "model_id=\(.model.id)",
|
||||||
|
@sh "used_pct_raw=\(.context_window.used_percentage // 0)",
|
||||||
|
@sh "total_input=\(.context_window.total_input_tokens // 0)",
|
||||||
|
@sh "total_output=\(.context_window.total_output_tokens // 0)",
|
||||||
|
@sh "duration_ms=\(.cost.total_api_duration_ms // 0)",
|
||||||
|
@sh "total_cost=\(.cost.total_cost_usd // 0)"
|
||||||
|
')"
|
||||||
|
|
||||||
|
dir_name=$(basename "$cwd")
|
||||||
|
|
||||||
|
# Truncate percentage to integer
|
||||||
|
used_pct=${used_pct_raw%%.*}
|
||||||
|
: "${used_pct:=0}"
|
||||||
|
|
||||||
|
# Use display_name directly — it already includes the version (e.g. "Opus 4.6")
|
||||||
|
# Only extract from model_id as fallback if display_name has no version
|
||||||
|
if [[ "$model_display" =~ [0-9] ]]; then
|
||||||
|
model_full="$model_display"
|
||||||
|
else
|
||||||
|
version=""
|
||||||
|
if [[ "$model_id" =~ claude-[a-z]+-([0-9]+)-([0-9]+) ]]; then
|
||||||
|
version="${BASH_REMATCH[1]}.${BASH_REMATCH[2]}"
|
||||||
|
fi
|
||||||
|
if [ -n "$version" ]; then
|
||||||
|
model_full="$model_display $version"
|
||||||
|
else
|
||||||
|
model_full="$model_display"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Git branch with caching (5 second TTL)
|
||||||
|
git_branch=""
|
||||||
|
cache_dir="/tmp/claude-statusline"
|
||||||
|
cache_file="${cache_dir}/git-branch-$(echo "$cwd" | md5 -q)"
|
||||||
|
cache_ttl=5
|
||||||
|
|
||||||
|
if git -C "$cwd" rev-parse --git-dir > /dev/null 2>&1; then
|
||||||
|
now=$(date +%s)
|
||||||
|
file_mtime=$(stat -c %Y "$cache_file" 2>/dev/null || stat -f %m "$cache_file" 2>/dev/null || echo 0)
|
||||||
|
cache_age=$((now - file_mtime))
|
||||||
|
if [ -f "$cache_file" ] && [ "$cache_age" -lt "$cache_ttl" ]; then
|
||||||
|
git_branch=$(cat "$cache_file")
|
||||||
|
else
|
||||||
|
git_branch=$(git -C "$cwd" branch --show-current 2>/dev/null || echo 'detached')
|
||||||
|
mkdir -p "$cache_dir" && echo "$git_branch" > "$cache_file"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Format tokens (pure bash integer math)
|
||||||
|
total_tokens=$(( ${total_input%%.*} + ${total_output%%.*} ))
|
||||||
|
if [ "$total_tokens" -ge 1000000 ]; then
|
||||||
|
whole=$((total_tokens / 1000000))
|
||||||
|
frac=$(( (total_tokens % 1000000) / 100000 ))
|
||||||
|
token_display="${whole}.${frac}M tok"
|
||||||
|
elif [ "$total_tokens" -ge 1000 ]; then
|
||||||
|
whole=$((total_tokens / 1000))
|
||||||
|
frac=$(( (total_tokens % 1000) / 100 ))
|
||||||
|
if [ "$whole" -ge 100 ]; then
|
||||||
|
token_display="${whole}k tok"
|
||||||
|
else
|
||||||
|
token_display="${whole}.${frac}k tok"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
token_display="${total_tokens} tok"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Billing mode detection (cached 60s)
|
||||||
|
billing_cache="/tmp/.claude-statusline-billing-cache"
|
||||||
|
billing_ttl=60
|
||||||
|
billing_mode=""
|
||||||
|
|
||||||
|
now_bill=$(date +%s)
|
||||||
|
bill_mtime=$(stat -c %Y "$billing_cache" 2>/dev/null || stat -f %m "$billing_cache" 2>/dev/null || echo 0)
|
||||||
|
bill_age=$((now_bill - bill_mtime))
|
||||||
|
|
||||||
|
if [ -f "$billing_cache" ] && [ "$bill_age" -lt "$billing_ttl" ]; then
|
||||||
|
billing_mode=$(cat "$billing_cache")
|
||||||
|
else
|
||||||
|
if security find-generic-password -s "Claude Code-credentials" >/dev/null 2>&1; then
|
||||||
|
billing_mode="max"
|
||||||
|
elif [ -n "$ANTHROPIC_API_KEY" ]; then
|
||||||
|
billing_mode="api"
|
||||||
|
else
|
||||||
|
billing_mode="unknown"
|
||||||
|
fi
|
||||||
|
echo "$billing_mode" > "$billing_cache"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$billing_mode" = "max" ]; then
|
||||||
|
billing_display='\e[32m✅ Max\e[0m'
|
||||||
|
elif [ "$billing_mode" = "api" ]; then
|
||||||
|
billing_display='\e[1;31m⚠️ API\e[0m'
|
||||||
|
else
|
||||||
|
billing_display='\e[33m? Auth\e[0m'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Format cost
|
||||||
|
cost_display=$(printf '$%.2f' "$total_cost")
|
||||||
|
|
||||||
|
# Format duration from milliseconds
|
||||||
|
duration_sec=$((${duration_ms%%.*} / 1000))
|
||||||
|
duration_min=$((duration_sec / 60))
|
||||||
|
duration_rem=$((duration_sec % 60))
|
||||||
|
|
||||||
|
# Build progress bar (10 chars wide)
|
||||||
|
filled=$((used_pct * 10 / 100))
|
||||||
|
empty=$((10 - filled))
|
||||||
|
|
||||||
|
# Color-code based on usage
|
||||||
|
if [ "$used_pct" -lt 70 ]; then
|
||||||
|
bar_color='\e[32m' # Green
|
||||||
|
elif [ "$used_pct" -lt 90 ]; then
|
||||||
|
bar_color='\e[33m' # Yellow
|
||||||
|
else
|
||||||
|
bar_color='\e[31m' # Red
|
||||||
|
fi
|
||||||
|
|
||||||
|
bar=""
|
||||||
|
for ((i=0; i<filled; i++)); do bar="${bar}█"; done
|
||||||
|
for ((i=0; i<empty; i++)); do bar="${bar}░"; done
|
||||||
|
|
||||||
|
# Line 1: [Opus 4.6] 📁 project-name | 🌿 branch
|
||||||
|
printf '\e[36m[%s]\e[0m 📁 %s' "$model_full" "$dir_name"
|
||||||
|
if [ -n "$git_branch" ]; then
|
||||||
|
printf ' \e[2m|\e[0m 🌿 %s' "$git_branch"
|
||||||
|
fi
|
||||||
|
printf '\n'
|
||||||
|
|
||||||
|
# Line 2: █████░░░░░ 42% | 19.8k tok | $1.23 | ✅ Max | ⏱️ 7m 3s
|
||||||
|
printf '%b%s\e[0m %d%% \e[2m|\e[0m \e[36m%s\e[0m \e[2m|\e[0m \e[33m%s\e[0m \e[2m|\e[0m %b \e[2m|\e[0m ⏱️ %dm %ds' \
|
||||||
|
"$bar_color" "$bar" "$used_pct" "$token_display" "$cost_display" "$billing_display" "$duration_min" "$duration_rem"
|
||||||
Loading…
Reference in a new issue