{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreigvng2e4plnyrdtq6cfwa363zpyz7yar625cedlkwsvujqpmtubya",
    "uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3mpfexkvq7s22"
  },
  "coverImage": {
    "$type": "blob",
    "ref": {
      "$link": "bafkreig3nuuhdwznossj52zi6632ghebd5ale6e6dccyb3t7wcjynqzyju"
    },
    "mimeType": "image/webp",
    "size": 74334
  },
  "path": "/riversea/my-anthropic-bill-dropped-from-312-to-156-after-i-added-two-bash-hooks-to-claude-code-4eei",
  "publishedAt": "2026-06-29T01:11:57.000Z",
  "site": "https://dev.to",
  "tags": [
    "ai",
    "aiagents",
    "mcp",
    "cloudflare",
    "Full post →"
  ],
  "textContent": "60% of a $312 Anthropic bill came from a single pattern: Claude Code hitting a D1 migration failure, then spinning up 7–8 retry Bash calls trying to diagnose what went wrong. Each loop burned 40–60K tokens. Three or four loops per session, and you're looking at $0.50–$0.70 just evaporating.\n\nThe fix wasn't prompt engineering. It was a `PostToolUse` hook that fires the moment `wrangler d1 migrations apply` exits non-zero — before the agent has a chance to start its retry spiral.\n\n\n\n    #!/bin/bash\n    # post_bash_hook.sh\n    COMMAND=\"$1\"\n    EXIT_CODE=\"$2\"\n\n    if echo \"$COMMAND\" | grep -q \"wrangler d1 migrations apply\"; then\n      if [ \"$EXIT_CODE\" != \"0\" ]; then\n        echo \"ALERT: D1 migration failed (exit $EXIT_CODE). Check schema state.\" >&2\n        curl -s -X PUT \"https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/storage/kv/namespaces/$KV_NS/values/migration_failed\" \\\n          -H \"Authorization: Bearer $CF_API_TOKEN\" \\\n          -d \"1\" > /dev/null\n      fi\n    fi\n\n    exit 0\n\n\nA Slack bot polls that KV key every 3 minutes. When it flips to `1`, I get pinged and can intervene before Claude Code decides to investigate further on my token budget. Six months running this setup: zero schema-mismatch incidents, and the next month's bill came in at $156.\n\nThe other half of the chain is a `PreToolUse` hook that blocks `wrangler deploy` whenever the agent is on `main` — learned that one the hard way after a production deploy went out from the wrong branch and left two Workers in a broken state for five minutes. The thing most people miss: when your hook returns `exit 2`, Claude Code reads whatever you wrote to stderr as context. A vague `BLOCK` does nothing useful. `BLOCK: wrangler deploy on main — use staging namespace instead` actually redirects the agent correctly.\n\nThere's also a `pre-commit` hook at the end of the chain that scans staged diffs for hardcoded production binding names and secret key patterns — a last filter before anything reaches git history.\n\nI wrote up the full breakdown — including the exact `.claude/settings.json` structure, how hook matcher patterns work (and where they don't), and the FAQ on execution order guarantees — over on riversealab.com.\n\nFull post →",
  "title": "My Anthropic bill dropped from $312 to $156 after I added two bash hooks to Claude Code"
}