agent-builder/scripts/templates/budget/budget-report.sh
Kjell Tore Guttormsen ec6f7c150e feat(templates): add budget tracking templates (Paperclip pattern)
Session 4 step 16 — post-hoc enforcement via PostToolUse hook with
PAUSED flag, budget-report.sh aggregates spend against window limit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 06:55:33 +02:00

84 lines
2.3 KiB
Bash

#!/bin/bash
# Budget report: summarize cost events and compare against policy.
# Bash 3.2 compatible. Uses python3 for aggregation.
#
# Usage: ./budget-report.sh
#
# Placeholders:
# {{WORKING_DIR}} - absolute path to project directory
WORKING_DIR="{{WORKING_DIR}}"
COST_LOG="$WORKING_DIR/budget/cost-events.jsonl"
BUDGET_FILE="$WORKING_DIR/BUDGET.md"
PAUSED_FLAG="$WORKING_DIR/budget/PAUSED"
if [ ! -f "$COST_LOG" ]; then
echo "No cost events recorded yet."
exit 0
fi
COST_LOG="$COST_LOG" BUDGET_FILE="$BUDGET_FILE" PAUSED_FLAG="$PAUSED_FLAG" python3 -c "
import json, re, os
from collections import defaultdict
cost_log = os.environ.get('COST_LOG', '')
budget_file = os.environ.get('BUDGET_FILE', '')
paused_flag = os.environ.get('PAUSED_FLAG', '')
# Read events
events = []
with open(cost_log) as f:
for line in f:
line = line.strip()
if line:
try:
events.append(json.loads(line))
except:
pass
# Aggregate
by_agent = defaultdict(int)
by_day = defaultdict(int)
by_tool = defaultdict(int)
for e in events:
agent = e.get('agent', 'unknown')
day = e.get('timestamp', '')[:10]
tool = e.get('tool_name', 'unknown')
by_agent[agent] += 1
by_day[day] += 1
by_tool[tool] += 1
print('BUDGET REPORT')
print('=' * 50)
print('Total events: ' + str(len(events)))
print()
# Per-agent breakdown
print('By Agent:')
for agent, count in sorted(by_agent.items(), key=lambda x: -x[1]):
print(' ' + agent + ': ' + str(count) + ' events')
print()
# Per-day breakdown (last 7 days)
print('By Day (last 7):')
for day, count in sorted(by_day.items())[-7:]:
print(' ' + day + ': ' + str(count) + ' events')
print()
# Budget comparison
if os.path.exists(budget_file):
content = open(budget_file).read()
limit_m = re.search(r'limit:\s*(\d+)\s*cents', content)
if limit_m:
limit = int(limit_m.group(1))
est_cents = len(events) # rough proxy
pct = (est_cents / limit * 100) if limit > 0 else 0
print('Budget: ~' + str(est_cents) + '/' + str(limit) + ' cents (' + str(round(pct)) + '%)')
# Paused status
if os.path.exists(paused_flag):
print('')
print('!! AGENT PAUSED: ' + open(paused_flag).read().strip())
print(' Remove ' + paused_flag + ' to resume')
"