#!/bin/sh # RoninForge BudgetClaw statusline for Claude Code. # # Shows: cwd (git branch) | model | per-project spend today # # Reads the JSON event Claude Code sends on stdin and prints a single # status line. Spend totals come from BudgetClaw's local rollup DB; no # API traffic is touched. # # Source: https://roninforge.org/claude/statusline.sh # Install: curl -fsSL https://roninforge.org/claude/install.sh | sh set -u input=$(cat) # Find jq even when PATH is minimal (Claude Code spawns the statusline # with a stripped environment - your shell rcfile is not loaded). JQ="" for p in /opt/homebrew/bin/jq /usr/local/bin/jq /usr/bin/jq; do if [ -x "$p" ]; then JQ="$p"; break; fi done [ -z "$JQ" ] && command -v jq >/dev/null 2>&1 && JQ=$(command -v jq) BUDGETCLAW="" for p in "$HOME/.local/bin/budgetclaw" /opt/homebrew/bin/budgetclaw /usr/local/bin/budgetclaw; do if [ -x "$p" ]; then BUDGETCLAW="$p"; break; fi done [ -z "$BUDGETCLAW" ] && command -v budgetclaw >/dev/null 2>&1 && BUDGETCLAW=$(command -v budgetclaw) # Extract context from Claude Code JSON. if [ -n "$JQ" ]; then cwd=$(printf '%s' "$input" | "$JQ" -r '.workspace.current_dir // empty') model=$(printf '%s' "$input" | "$JQ" -r '.model.display_name // empty') else cwd=$(printf '%s' "$input" | sed -n -E 's/.*"current_dir":"([^"]*)".*/\1/p') model=$(printf '%s' "$input" | sed -n -E 's/.*"display_name":"([^"]*)".*/\1/p') fi git_branch="" if [ -n "$cwd" ] && git -C "$cwd" rev-parse --git-dir >/dev/null 2>&1; then branch=$(git -C "$cwd" --no-optional-locks branch --show-current 2>/dev/null) [ -n "$branch" ] && git_branch=" ($branch)" fi bc_segment="" if [ -n "$BUDGETCLAW" ]; then bc_segment=$("$BUDGETCLAW" status 2>/dev/null | awk ' NR > 1 && $1 != "TOTAL" && NF >= 5 { today = $3; gsub(/\$/, "", today) if (today + 0 > 0) sum[$1] += today } END { n = 0 for (p in sum) items[++n] = sprintf("%s|%.2f", p, sum[p]) for (i = 1; i <= n; i++) { for (j = i + 1; j <= n; j++) { split(items[i], a, "|"); split(items[j], b, "|") if (a[2] + 0 < b[2] + 0) { tmp = items[i]; items[i] = items[j]; items[j] = tmp } } } shown = (n > 5) ? 5 : n out = "" for (i = 1; i <= shown; i++) { split(items[i], a, "|") amt = a[2] + 0 col = "\033[2m" if (amt >= 50) col = "\033[31m" else if (amt >= 10) col = "\033[33m" if (out != "") out = out "\033[2m \xc2\xb7 \033[0m" out = out col a[1] " $" sprintf("%.2f", amt) "\033[0m" } if (n > 5) out = out "\033[2m +" (n - 5) " more\033[0m" print out }') fi if [ -z "$bc_segment" ]; then if [ -n "$BUDGETCLAW" ]; then bc_segment="$(printf '\033[2mBC: no spend today\033[0m')" else bc_segment="$(printf '\033[2mBC: not installed\033[0m')" fi fi printf '\033[32m%s\033[31m%s\033[0m \033[2m|\033[0m \033[34m%s\033[0m \033[2m|\033[0m %s' \ "$cwd" "$git_branch" "$model" "$bc_segment"