195 lines
7.2 KiB
Bash
195 lines
7.2 KiB
Bash
#!/bin/bash
|
|
|
|
# ANSI color codes
|
|
GREEN='\033[0;32m'
|
|
RED='\033[0;31m'
|
|
PURPLE='\033[0;35m'
|
|
GRAY='\033[0;90m'
|
|
LIGHT_GRAY='\033[0;37m'
|
|
RESET='\033[0m'
|
|
|
|
# Read JSON input from stdin
|
|
input=$(cat)
|
|
|
|
# Extract current session ID and model info from Claude Code input
|
|
session_id=$(echo "$input" | jq -r '.session_id // empty')
|
|
model_name=$(echo "$input" | jq -r '.model.display_name // empty')
|
|
current_dir=$(echo "$input" | jq -r '.workspace.current_dir // empty')
|
|
cwd=$(echo "$input" | jq -r '.cwd // empty')
|
|
|
|
# Get current git branch with error handling
|
|
if git rev-parse --git-dir >/dev/null 2>&1; then
|
|
branch=$(git branch --show-current 2>/dev/null || echo "detached")
|
|
if [ -z "$branch" ]; then
|
|
branch="detached"
|
|
fi
|
|
|
|
# Check for pending changes (staged or unstaged)
|
|
if ! git diff-index --quiet HEAD -- 2>/dev/null || ! git diff-index --quiet --cached HEAD -- 2>/dev/null; then
|
|
# Get line changes for unstaged and staged changes
|
|
unstaged_stats=$(git diff --numstat 2>/dev/null | awk '{added+=$1; deleted+=$2} END {print added+0, deleted+0}')
|
|
staged_stats=$(git diff --cached --numstat 2>/dev/null | awk '{added+=$1; deleted+=$2} END {print added+0, deleted+0}')
|
|
|
|
# Parse the stats
|
|
unstaged_added=$(echo $unstaged_stats | cut -d' ' -f1)
|
|
unstaged_deleted=$(echo $unstaged_stats | cut -d' ' -f2)
|
|
staged_added=$(echo $staged_stats | cut -d' ' -f1)
|
|
staged_deleted=$(echo $staged_stats | cut -d' ' -f2)
|
|
|
|
# Total changes
|
|
total_added=$((unstaged_added + staged_added))
|
|
total_deleted=$((unstaged_deleted + staged_deleted))
|
|
|
|
# Build the branch display with changes (with colors)
|
|
changes=""
|
|
if [ $total_added -gt 0 ]; then
|
|
changes="${GREEN}+$total_added${RESET}"
|
|
fi
|
|
if [ $total_deleted -gt 0 ]; then
|
|
if [ -n "$changes" ]; then
|
|
changes="$changes ${RED}-$total_deleted${RESET}"
|
|
else
|
|
changes="${RED}-$total_deleted${RESET}"
|
|
fi
|
|
fi
|
|
|
|
if [ -n "$changes" ]; then
|
|
branch="$branch${PURPLE}*${RESET} ($changes)"
|
|
else
|
|
branch="$branch${PURPLE}*${RESET}"
|
|
fi
|
|
fi
|
|
else
|
|
branch="no-git"
|
|
fi
|
|
|
|
# Get basename of current directory
|
|
dir_name=$(basename "$current_dir")
|
|
|
|
# Get today's date in YYYYMMDD format
|
|
today=$(date +%Y%m%d)
|
|
|
|
# Function to format numbers
|
|
format_cost() {
|
|
printf "%.2f" "$1"
|
|
}
|
|
|
|
format_tokens() {
|
|
local tokens=$1
|
|
if [ "$tokens" -ge 1000000 ]; then
|
|
printf "%.1fM" "$(echo "scale=1; $tokens / 1000000" | bc -l)"
|
|
elif [ "$tokens" -ge 1000 ]; then
|
|
printf "%.1fK" "$(echo "scale=1; $tokens / 1000" | bc -l)"
|
|
else
|
|
printf "%d" "$tokens"
|
|
fi
|
|
}
|
|
|
|
format_time() {
|
|
local minutes=$1
|
|
local hours=$((minutes / 60))
|
|
local mins=$((minutes % 60))
|
|
if [ "$hours" -gt 0 ]; then
|
|
printf "%dh %dm" "$hours" "$mins"
|
|
else
|
|
printf "%dm" "$mins"
|
|
fi
|
|
}
|
|
|
|
# Initialize variables with defaults
|
|
session_cost="0.00"
|
|
session_tokens=0
|
|
daily_cost="0.00"
|
|
block_cost="0.00"
|
|
remaining_time="N/A"
|
|
|
|
# Get current session data by finding the session JSONL file
|
|
if command -v ccusage >/dev/null 2>&1 && [ -n "$session_id" ] && [ "$session_id" != "empty" ]; then
|
|
# Look for the session JSONL file in Claude project directories
|
|
session_jsonl_file=""
|
|
|
|
# Check common Claude paths
|
|
claude_paths=(
|
|
"$HOME/.config/claude"
|
|
"$HOME/.claude"
|
|
)
|
|
|
|
for claude_path in "${claude_paths[@]}"; do
|
|
if [ -d "$claude_path/projects" ]; then
|
|
# Use find to search for the session file
|
|
session_jsonl_file=$(find "$claude_path/projects" -name "${session_id}.jsonl" -type f 2>/dev/null | head -1)
|
|
if [ -n "$session_jsonl_file" ]; then
|
|
break
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# Parse the session file if found
|
|
if [ -n "$session_jsonl_file" ] && [ -f "$session_jsonl_file" ]; then
|
|
# Count lines and estimate cost (simple approximation)
|
|
# Each line is a usage entry, we can count tokens and estimate
|
|
session_tokens=0
|
|
session_entries=0
|
|
|
|
while IFS= read -r line; do
|
|
if [ -n "$line" ]; then
|
|
session_entries=$((session_entries + 1))
|
|
# Extract token usage from message.usage field (only count input + output tokens)
|
|
# Cache tokens shouldn't be added up as they're reused/shared across messages
|
|
input_tokens=$(echo "$line" | jq -r '.message.usage.input_tokens // 0' 2>/dev/null || echo "0")
|
|
output_tokens=$(echo "$line" | jq -r '.message.usage.output_tokens // 0' 2>/dev/null || echo "0")
|
|
|
|
line_tokens=$((input_tokens + output_tokens))
|
|
session_tokens=$((session_tokens + line_tokens))
|
|
fi
|
|
done < "$session_jsonl_file"
|
|
|
|
# Use ccusage statusline to get the accurate cost for this session
|
|
ccusage_statusline=$(echo "$input" | ccusage statusline 2>/dev/null)
|
|
current_session_cost=$(echo "$ccusage_statusline" | sed -n 's/.*💰 \([^[:space:]]*\) session.*/\1/p')
|
|
|
|
if [ -n "$current_session_cost" ] && [ "$current_session_cost" != "N/A" ]; then
|
|
session_cost=$(echo "$current_session_cost" | sed 's/\$//g')
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if command -v ccusage >/dev/null 2>&1; then
|
|
# Get daily data
|
|
daily_data=$(ccusage daily --json --since "$today" 2>/dev/null)
|
|
if [ $? -eq 0 ] && [ -n "$daily_data" ]; then
|
|
daily_cost=$(echo "$daily_data" | jq -r '.totals.totalCost // 0')
|
|
fi
|
|
|
|
# Get active block data
|
|
block_data=$(ccusage blocks --active --json 2>/dev/null)
|
|
if [ $? -eq 0 ] && [ -n "$block_data" ]; then
|
|
active_block=$(echo "$block_data" | jq -r '.blocks[] | select(.isActive == true) // empty')
|
|
if [ -n "$active_block" ] && [ "$active_block" != "null" ]; then
|
|
block_cost=$(echo "$active_block" | jq -r '.costUSD // 0')
|
|
remaining_minutes=$(echo "$active_block" | jq -r '.projection.remainingMinutes // 0')
|
|
if [ "$remaining_minutes" != "0" ] && [ "$remaining_minutes" != "null" ]; then
|
|
remaining_time=$(format_time "$remaining_minutes")
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Format the output
|
|
formatted_session_cost=$(format_cost "$session_cost")
|
|
formatted_daily_cost=$(format_cost "$daily_cost")
|
|
formatted_block_cost=$(format_cost "$block_cost")
|
|
formatted_tokens=$(format_tokens "$session_tokens")
|
|
|
|
# Build the status line with colors (light gray as default)
|
|
status_line="${LIGHT_GRAY}🌿 $branch ${GRAY}|${LIGHT_GRAY} 📁 $dir_name ${GRAY}|${LIGHT_GRAY} 🤖 $model_name ${GRAY}|${LIGHT_GRAY} 💰 \$$formatted_session_cost ${GRAY}/${LIGHT_GRAY} 📅 \$$formatted_daily_cost ${GRAY}/${LIGHT_GRAY} 🧊 \$$formatted_block_cost"
|
|
|
|
if [ "$remaining_time" != "N/A" ]; then
|
|
status_line="$status_line ($remaining_time left)"
|
|
fi
|
|
|
|
status_line="$status_line ${GRAY}|${LIGHT_GRAY} 🧩 ${formatted_tokens} ${GRAY}tokens${RESET}"
|
|
|
|
printf "%b\n" "$status_line"
|
|
|