#!/usr/bin/env bash # burn — a token-burning harness around Claude Code. # Usage: burn TOKENS [--model MODEL] set -euo pipefail FILLER_TOKENS=1000 SYSTEM_OVERHEAD_TOKENS=9000 # measured: claude -p system prompt + tool defs TOKENS_PER_CALL=$((SYSTEM_OVERHEAD_TOKENS + FILLER_TOKENS)) MIN_TOKENS=$TOKENS_PER_CALL # asking for less than one call can't be honored FILLER_LINE="Burn baby burn, this is a disco inferno, burning tokens is for great engineers. " usage() { cat <&2 exit 2 fi model="$2" shift 2 ;; --model=*) model="${1#*=}" shift ;; --) shift break ;; -*) echo "error: unknown flag: $1" >&2 usage >&2 exit 2 ;; *) if [[ -n "$tokens" ]]; then echo "error: unexpected extra argument: $1" >&2 exit 2 fi tokens="$1" shift ;; esac done if [[ -z "$tokens" ]]; then usage >&2 exit 2 fi if ! [[ "$tokens" =~ ^[0-9]+$ ]]; then echo "error: TOKENS must be a positive integer (got '$tokens')" >&2 exit 2 fi if ((tokens < MIN_TOKENS)); then echo "error: tokens must be at least ${MIN_TOKENS} (got ${tokens})" >&2 exit 2 fi if ! command -v claude >/dev/null 2>&1; then echo "error: 'claude' CLI not found on PATH." >&2 echo " install Claude Code first: https://docs.claude.com/claude-code" >&2 exit 1 fi if ! command -v jq >/dev/null 2>&1; then echo "error: 'jq' not found on PATH (used to parse Claude Code's JSON output)." >&2 echo " install with: brew install jq" >&2 exit 1 fi # ~4 chars/token is the standard rule of thumb for English text. target_chars=$((FILLER_TOKENS * 4)) filler_len=${#FILLER_LINE} repeats=$((target_chars / filler_len)) ((repeats < 1)) && repeats=1 filler="" for ((i = 0; i < repeats; i++)); do filler+="$FILLER_LINE" done prompt=$(printf "Reply with the single word 'burned' and nothing else. Ignore the filler text below; it exists only to consume tokens.\n\n%s" "$filler") if [[ -t 1 ]]; then ORANGE=$'\033[1;33m' MAGENTA=$'\033[1;35m' CYAN=$'\033[1;36m' RED=$'\033[1;31m' YELLOW=$'\033[1;93m' DIM=$'\033[2m' RESET=$'\033[0m' HIDE_CURSOR=$'\033[?25l' SHOW_CURSOR=$'\033[?25h' else ORANGE='' MAGENTA='' CYAN='' RED='' YELLOW='' DIM='' RESET='' HIDE_CURSOR='' SHOW_CURSOR='' fi PAD=' ' # Four disco frames cycled to make the dancer sway across the floor while # the ball glitters on the off-beat. Each row is exactly 23 characters wide # so it sits cleanly inside the booth opening. disco_frame() { case "$1_$2" in # frame 0 — centered Travolta: left hand on hip, right arm pointing up-right 0_0) printf '%s' ' | ' ;; 0_1) printf '%s' ' 🪩 ' ;; 0_2) printf '%s' ' /|\ ' ;; 0_3) printf '%s' ' / ' ;; 0_4) printf '%s' ' o/ ' ;; 0_5) printf '%s' ' <| ' ;; 0_6) printf '%s' ' | ' ;; 0_7) printf '%s' ' / \ ' ;; 0_8) printf '%s' ' ā–“ā–’ā–“ā–’ā–“ā–’ā–“ā–’ā–“ā–’ā–“ā–’ā–“ā–’ā–“ā–’ā–“ā–’ā–“ ' ;; # frame 1 — lean left, same Travolta pose; sparkles on the chain 1_0) printf '%s' ' * | * ' ;; 1_1) printf '%s' ' 🪩 ' ;; 1_2) printf '%s' ' \|/ ' ;; 1_3) printf '%s' ' / ' ;; 1_4) printf '%s' ' o/ ' ;; 1_5) printf '%s' ' <| ' ;; 1_6) printf '%s' ' | ' ;; 1_7) printf '%s' ' / \ ' ;; 1_8) printf '%s' ' ā–’ā–“ā–’ā–“ā–’ā–“ā–’ā–“ā–’ā–“ā–’ā–“ā–’ā–“ā–’ā–“ā–’ā–“ā–’ ' ;; # frame 2 — centered mirror: right hand on hip, left arm pointing up-left 2_0) printf '%s' ' | ' ;; 2_1) printf '%s' ' 🪩 ' ;; 2_2) printf '%s' ' /|\ ' ;; 2_3) printf '%s' ' \ ' ;; 2_4) printf '%s' ' \o ' ;; 2_5) printf '%s' ' |> ' ;; 2_6) printf '%s' ' | ' ;; 2_7) printf '%s' ' / \ ' ;; 2_8) printf '%s' ' ā–“ā–’ā–“ā–’ā–“ā–’ā–“ā–’ā–“ā–’ā–“ā–’ā–“ā–’ā–“ā–’ā–“ā–’ā–“ ' ;; # frame 3 — lean right, mirror Travolta; sparkles on the chain 3_0) printf '%s' ' * | * ' ;; 3_1) printf '%s' ' 🪩 ' ;; 3_2) printf '%s' ' \|/ ' ;; 3_3) printf '%s' ' \ ' ;; 3_4) printf '%s' ' \o ' ;; 3_5) printf '%s' ' |> ' ;; 3_6) printf '%s' ' | ' ;; 3_7) printf '%s' ' / \ ' ;; 3_8) printf '%s' ' ā–’ā–“ā–’ā–“ā–’ā–“ā–’ā–“ā–’ā–“ā–’ā–“ā–’ā–“ā–’ā–“ā–’ā–“ā–’ ' ;; esac } disco_color_for_row() { case "$1" in 0 | 2) printf '%s' "$ORANGE" ;; # chain sparkles + light rays 1 | 8) printf '%s' "$MAGENTA" ;; # disco ball + dance floor *) printf '%s' "$CYAN" ;; # dancer (and blank rows) esac } # Render `count` copies of `char` colored across a cycling fire palette # (red → orange → yellow). `offset` rotates the cycle so the strip flickers. fire_strip() { local count=$1 char=$2 offset=${3:-0} local palette=("$RED" "$ORANGE" "$YELLOW") local n=${#palette[@]} local i out="" for ((i = 0; i < count; i++)); do out+="${palette[$(((i + offset) % n))]}${char}" done printf '%s' "$out" } print_disco_row() { local frame=$1 row=$2 content color fl content=$(disco_frame "$frame" "$row") color=$(disco_color_for_row "$row") fl=$(fire_strip 3 'ā–‘' "$frame") printf '%s%s│%s%s│%s%s%s%s%s│%s%s│%s\n' \ "$PAD" "$RED" "$fl" "$YELLOW" "$RESET" \ "$color" "$content" "$RESET" \ "$YELLOW" "$fl" "$RED" "$RESET" } draw_disco() { local frame=${1:-0} local flames fr3 flames=$(fire_strip 31 'ā–²') fr3=$(fire_strip 3 'ā–‘') printf '%s%sā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”%s\n' "$PAD" "$RED" "$RESET" printf '%s%s│%s%s│%s\n' "$PAD" "$RED" "$flames" "$RED" "$RESET" printf '%s%s│%s%sā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”%s%s│%s\n' \ "$PAD" "$RED" "$fr3" "$YELLOW" "$fr3" "$RED" "$RESET" for row in 0 1 2 3 4 5 6 7 8; do print_disco_row "$frame" "$row" done printf '%s%s│%s%sā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜%s%s│%s\n' \ "$PAD" "$RED" "$fr3" "$YELLOW" "$fr3" "$RED" "$RESET" printf '%s%sā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜%s\n' "$PAD" "$RED" "$RESET" } # Distance (in lines) from the progress-bar cursor up to the top inner row: # blank line above bar + outer-bottom + inner-bottom + 9 inner rows = 12. DISCO_OFFSET=12 redraw_disco() { local frame=$1 printf '\033[s' # save cursor printf '\033[%dA\r' "$DISCO_OFFSET" # move up to top inner row for row in 0 1 2 3 4 5 6 7 8; do print_disco_row "$frame" "$row" done printf '\033[u' # restore cursor } burned=0 iteration=0 total_cost=0.000000 BAR_WIDTH=30 claude_pid="" last_output="" claude_cmd=(claude -p --output-format json --no-session-persistence --tools "") if [[ -n "$model" ]]; then claude_cmd+=(--model "$model") fi # `--tools` is variadic, so terminate flag parsing before the prompt. claude_cmd+=(--) draw_bar() { local pct filled empty bar="" i suffix="${1:-}" pct=$((burned * 100 / tokens)) ((pct > 100)) && pct=100 filled=$((pct * BAR_WIDTH / 100)) empty=$((BAR_WIDTH - filled)) for ((i = 0; i < filled; i++)); do bar+='ā–ˆ'; done for ((i = 0; i < empty; i++)); do bar+='ā–‘'; done # \033[K clears any leftover characters from a longer previous draw. printf "\r[%s] %3d%% • %d/%d tokens • \$%s • %d call(s)%s\033[K" \ "$bar" "$pct" "$burned" "$tokens" "$total_cost" "$iteration" "$suffix" } restore_cursor() { printf '%s' "$SHOW_CURSOR" } trap restore_cursor EXIT CELEBRATIONS=( "šŸ”§ You are a great engineer!" "šŸŖ„ You are an AI wizard!" "šŸ•ŗ Tokens burned. Groove restored." "✨ Claude never saw that coming." "šŸš€ You shipped faster than the flame could spread." "šŸ’ƒ The code is hot and the vibes are immaculate." "🪩 Groovy job. Productivity achieved." "⚔ The tokens are gone, but the legend remains." "šŸ† Another glorious victory for human-machine collaboration." "⚔ You turned tokens into pure developer energy." "šŸŽ© Behold: a masterclass in responsible token combustion." "🌈 The code goblins approve this burn." "šŸ› ļø Great work. The token furnace is pleased." ) # Round a dollar amount to the nearest cent. round_cents() { awk -v c="$1" 'BEGIN{printf "%.2f", c}' } on_interrupt() { if [[ -n "$claude_pid" ]]; then kill "$claude_pid" 2>/dev/null || true wait "$claude_pid" 2>/dev/null || true fi local cost_rounded cost_rounded=$(round_cents "$total_cost") printf "\n\nšŸ›‘ stopped early.\n\n" printf " šŸ”„ You torched %s tokens across %s call(s), and spent \$%s\n\n" "$cost_rounded šŸ’°šŸ’°šŸ’°" "$burned" "$iteration" printf " šŸ’° spent " exit 130 } trap on_interrupt INT # Run one claude call in the background while animating flames. On success # leaves the parsed stdout in $last_output. Returns the claude exit status. run_claude() { local output_file output_file=$(mktemp) "${claude_cmd[@]}" "$prompt" >"$output_file" 2>/dev/null & claude_pid=$! local frame=0 while kill -0 "$claude_pid" 2>/dev/null; do redraw_disco $((frame % 4)) frame=$((frame + 1)) sleep 0.4 done if wait "$claude_pid" 2>/dev/null; then last_output=$(<"$output_file") rm -f "$output_file" claude_pid="" return 0 else rm -f "$output_file" claude_pid="" return 1 fi } printf '%s' "$HIDE_CURSOR" echo draw_disco 0 echo draw_bar while ((burned < tokens)); do iteration=$((iteration + 1)) if ! run_claude; then draw_bar " (retrying)" if ! run_claude; then printf "\nerror: two consecutive claude calls failed; aborting.\n" >&2 exit 1 fi fi read -r this_call this_cost < <(jq -r ' [ ((.usage.input_tokens // 0) + (.usage.cache_creation_input_tokens // 0) + (.usage.cache_read_input_tokens // 0) + (.usage.output_tokens // 0)), (.total_cost_usd // 0) ] | @tsv' <<<"$last_output") if ! [[ "$this_call" =~ ^[0-9]+$ ]]; then printf "\nerror: could not parse token usage from claude output\n" >&2 exit 1 fi burned=$((burned + this_call)) total_cost=$(awk -v a="$total_cost" -v b="$this_cost" 'BEGIN{printf "%.6f", a+b}') draw_bar done cost_rounded=$(round_cents "$total_cost") msg="${CELEBRATIONS[RANDOM % ${#CELEBRATIONS[@]}]}" printf "\n\n%s\n\n" "$msg" printf " šŸ”„ torched %s fire tokens across %s call(s) (target: %s)\n" \ "$burned" "$iteration" "$tokens" printf " šŸ’° spent \$%s\n\n" "$cost_rounded"