{
"$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"
}