Claude usage watchdog

One prompt. It builds a thing that watches how much Claude you've used and texts you before you run out, not after.

  1. Open Cowork on your Mac. Use the desktop app, not the browser one. It needs to reach your keychain and your files, which the cloud version can't do.
  2. Hit Copy prompt below and paste the whole thing into a new session.
  3. Let it build. It'll ask before it changes anything that matters, and it'll run each piece to check it works.
  4. When it's done it schedules itself twice a day and goes quiet. No news is the good outcome. You'll only hear from it when a number is worth acting on.

If it says the token is dead: open Terminal, run claude login, and tell it to try again. That's the one thing it can't fix on its own.

The prompt
Build me a Claude usage watchdog on this Mac. I'm running Cowork locally, Claude Code is installed, and I'm logged in. Work in ~/claude-budget (create it). Do all shell work yourself and verify each piece by running it before you tell me it works.

PART 1 — the meter reader (usage-probe.sh)

There IS a programmatic read of my usage percentages; don't build around the assumption that there isn't. The CLI's /usage screen calls GET https://api.anthropic.com/api/oauth/usage with headers "Authorization: Bearer <token>" and "anthropic-beta: oauth-2025-04-20". The token is the Claude Code OAuth credential in my login keychain: service "Claude Code-credentials", account = my macOS short username (confirm with: security find-generic-password -s "Claude Code-credentials" | grep acct). The keychain value is JSON with claudeAiOauth.accessToken, .refreshToken and .expiresAt (ms).

Write a script that reads it and prints ONE parseable line, e.g.:
session=30%(resets Sat-19:29) weekly=34%(resets Fri-23:59) weekly_fable=18%

From the payload: five_hour = the 5-hour rolling window, seven_day = the weekly cap (this is the one that matters), limits[] entries with kind "weekly_scoped" = per-model weekly caps, extra_usage = overage dollars vs monthly cap (only print it if is_enabled is true). Support a --json flag that dumps the raw payload.

Make it self-heal an expired token: if the access token is expired or the API returns 401/403, POST {"grant_type":"refresh_token","refresh_token":<rt>,"client_id":"9d1c250a-e61b-44d9-88ed-5944d1962f5e"} to https://platform.claude.com/v1/oauth/token, then write the new credential back into the SAME keychain service+account with `security add-generic-password -U` after backing up the old value to a 0600 file. Exit codes: 0 ok, 2 no token, 3 network, 4 auth dead (means I need to run `claude login` in Terminal).

Hard rule, no exceptions: never print, log, echo, or transmit the token itself. Not in output, not in a debug line, not in a file you show me.

This costs zero usage — it's a metadata read, not a model call.

PART 2 — the log

Each run, append "<ISO timestamp> <that line>" to ~/claude-budget/.usage-log, and read the previous line so you can compare against it. One line per run, so there's a trend and not just a snapshot.

PART 3 — the burn breakdown

Use ccusage, but invoke it correctly. A bare `ccusage daily` only reads ~/.claude and is therefore blind to Cowork and desktop chat, which is where most of the burn actually is. ccusage reads $CLAUDE_CONFIG_DIR, which takes a COMMA-SEPARATED list of roots. So:

cfg="$HOME/.claude"; base="$HOME/Library/Application Support/Claude/local-agent-mode-sessions"
for d in "$base"/*/*/*/.claude; do cfg="$cfg,$d"; done
CLAUDE_CONFIG_DIR="$cfg" npx -y ccusage@latest daily --since $(date -v-7d +%Y%m%d) --json

Compare today and yesterday against the trailing 7-day average of totalTokens. Also: `ccusage session --since` is a trap — it picks which sessions to show by date but reports each one's FULL LIFETIME cost, which overstates a single day by ~2.5x. Don't use it for per-day numbers.

PART 4 — when to speak

Silence is the correct output. On a normal run, post nothing, write nothing but the log line, and exit. Alert me ONLY if:
- session >= 80%, or it jumped >= 30 points since the last check
- weekly >= 80% at any time, or >= 60% with more than a day left before it resets
- extra usage (if enabled at all) >= 80% of the monthly cap, or grew >= $15 since last check
- today's burn is more than 2x the trailing 7-day average

Alerts go to me as an iMessage, and they have to be short enough to read on a phone. Never send a bare percentage — always pair it with the reset time, because a number with no deadline is useless. "Weekly 82%, resets tonight" and "weekly 82%, resets Friday" are completely different situations. Lead with the number plus when it resets, then the single most useful thing I could do about it.

Then set it up as a scheduled task that runs twice a day, around 9am and 6pm.

PART 5 — what NOT to build

Do not build a pace or projection layer — no "at this rate you'll cross 100% by Monday", no burst-rate detection, no hysteresis state file. Thresholds only. If I want the trend version later I'll ask for it.

Never disable or pause anything to save usage. This alerts; it does not act. If a reading looks bad, tell me and change nothing.

Finally: check whether extra usage is enabled on my account (extra_usage.is_enabled in the payload) and tell me. If it's false there's no overage cushion, which means hitting 100% weekly is a hard stop until reset, not a slide into billing — and that makes every threshold above more expensive to miss.