{"slug": "catch-the-5-hour-cap-before-you-hit-it-token-budget-advisor-and-status-line", "title": "Catch the 5-Hour Cap Before You Hit It: token-budget-advisor and Status-Line Integration", "summary": "A developer created token-budget-advisor.sh, a tool that warns users before hitting Claude MAX's 5-hour output token cap by detecting dangerous thresholds around 800k tokens and critical levels past 1.2M tokens. The script integrates into a dashboard and status line, reading from Claude's stdin and ccusage blocks to provide absolute token counts rather than percentages, preventing abrupt session halts during unattended background jobs.", "body_md": "My last piece, \"[Auto-resuming a session that stalled on a rate limit](https://zenn.dev/bokuwalily/articles/ratelimit-resume),\" was about what to do *after* a 5-hour block stops you. This one is about the machinery that lets you **notice before it stops you**.\n\nClaude MAX's 5-hour block halts your session the instant output tokens hit the ceiling. Even with `🕐 5h 82%`\n\nvisible in the status line, you have no idea how many tokens that remaining 18% actually represents. `token-budget-advisor.sh`\n\ndetects the boundary in absolute terms — **\"getting dangerous around 800k, already cooked past 1.2M\"** — and this article covers its design, its integration into `dashboard.sh`\n\n, and how it gets wired into the status line.\n\nThe Claude Code status line shows `🕐 5h N%`\n\n. `~/.claude/scripts/statusline.sh`\n\nreads this straight from Claude's stdin.\n\n```\nH5=$(j '.rate_limits.five_hour.used_percentage // empty')\nH5R=$(j '.rate_limits.five_hour.resets_at // empty')\n```\n\nThe `pcolor`\n\nfunction turns the value yellow at 50%+ and red at 80%+, but that's **the percentage Claude computed**, not an absolute token count. What 80% translates to in tokens shifts depending on your Max plan's contract state, and if you keep going with long completions you hit the cap and get abruptly stopped from the next turn onward.\n\nTo check, you have to run `ccusage blocks`\n\nevery single time, and if you forget and then kick off a heavy task, you'll cleanly burn through your 5-hour window. When an unattended job is running in the background, everything can slip into an all-SKIP state before a human even notices.\n\nThe policy in the header of `~/.claude/scripts/token-budget-advisor.sh`\n\ndoubles as its design.\n\n```\n# しきい値:\n#   5h block:  output > 800k  → warn (>1.2M で critical)\n#   weekly:    cost  > $3000  → warn\n#   sessions:  >5/day         → warn (集中作業の疑い)\n#\n# 失敗時は exit 0 で fail-open (dashboard 統合のため)\n# bash 3.2 互換\n```\n\nIt first tries `ccusage blocks --json`\n\n.\n\n```\nif command -v ccusage >/dev/null 2>&1; then\n  CC_JSON=$(ccusage blocks --json 2>/dev/null || true)\n  if [ -n \"$CC_JSON\" ]; then\n    EXTRACTED=$(printf '%s' \"$CC_JSON\" | python3 -c \"\nimport sys, json\ntry:\n    d = json.load(sys.stdin)\n    active = [b for b in d.get('blocks', []) if b.get('isActive')]\n    if active:\n        b = active[0]\n        tc = b.get('tokenCounts', {}) or {}\n        out = int(tc.get('outputTokens', 0))\n        cost = float(b.get('costUSD', 0))\n        print(f'{out}|{cost}')\n    ...\")\n```\n\nIn environments where ccusage isn't present, it runs on `cost-log.jsonl`\n\nalone. When both are available, it prefers ccusage's values and records the discrepancy as `source_diff_pct`\n\n.\n\n```\n# ccusage の値が有効ならそちらを優先 (transcript 計算より信頼できる)\nown_out_5h = out_5h\nif cc_out is not None and cc_out > 0:\n    out_5h = cc_out\n\n# 比較 (検証用)\ndiff_pct = None\nif cc_out is not None and own_out_5h > 0:\n    diff_pct = round(abs(cc_out - own_out_5h) / max(cc_out, own_out_5h) * 100, 1)\n```\n\nWhen the gap is large, there may be a problem with the cost-log side's aggregation logic, and `source_diff_pct`\n\nserves as a signal for that.\n\ncost-log.jsonl gets written across multiple lines under the same `(session_id, transcript)`\n\nkey (by design, the cumulative value mid-session is appended). Summing all lines double-counts the tokens.\n\n```\n# cost-log は session_id × transcript ごとに累積値で書かれる仕様。\n# 最新行のみ採用するため、(session_id, transcript) で最終行を取り直す。\nlatest = {}\nwith open(log_path) as f:\n    for line in f:\n        r = json.loads(line)\n        key = (r.get(\"session_id\", \"\"), r.get(\"transcript\", \"\"))\n        prev = latest.get(key)\n        if (prev is None) or (t > prev[0]):\n            latest[key] = (t, r)\n```\n\nYou adopt only the final line, then filter by the 5h/7d windows. Sum every line without knowing this and you'll get numbers 2–3x the real value.\n\n```\nTHRESH_5H_WARN     = 800_000      # output tokens\nTHRESH_5H_CRIT     = 1_200_000\nTHRESH_WEEK_WARN   = 3000         # USD\nTHRESH_SESS_PER_DAY = 5\n\nif out_5h >= THRESH_5H_CRIT:\n    s5 = \"critical\"\nelif out_5h >= THRESH_5H_WARN:\n    s5 = \"warn\"\nelse:\n    s5 = \"ok\"\n```\n\nThe intensive-work check is made from \"average over the last 3 days > 5 sess/day.\"\n\n```\nrecent_days = sorted(by_day.keys())[-3:]\navg_sess = sum(by_day[d] for d in recent_days) / max(1, len(recent_days))\nburst = avg_sess > THRESH_SESS_PER_DAY\n```\n\n`--short`\n\nmode\nThe `_short`\n\nfield is generated on the Python side.\n\n```\n\"_short\": f\"{icon} {label} (5h:{out_5h/1000:.0f}k tok ${cost_5h:.1f} / 7d:${cost_7d:.0f})\",\n```\n\nExample outputs per state (actual token counts will vary).\n\n```\n🟢 OK (5h:234k tok $0.3 / 7d:$2)\n🟡 burst (5h:841k tok $1.2 / 7d:$8)\n🔴 cap-near (5h:1243k tok $2.1 / 7d:$12)\n```\n\nSince you can match just the icon with `grep -qE '🔴|cap-near'`\n\n, you can drop it directly into a shell-script gate. On error it prints `⚫ n/a`\n\nand returns with exit 0 (fail-open design), so the caller doesn't get halted.\n\nThe `## 💰 Cost (7d)`\n\nsection of `~/.claude/scripts/dashboard.sh`\n\nlooks like this.\n\n```\necho \"## 💰 Cost (7d)\"\n~/.claude/scripts/cost-summary.sh --short\necho \"  budget: $(~/.claude/scripts/token-budget-advisor.sh --short)\"\n```\n\nIt gets written out to `~/.claude/dashboard.md`\n\n, so at the start of a project you just open the file to see the budget situation.\n\nHere's the config for `~/Library/LaunchAgents/com.shun.dashboard.plist`\n\n.\n\n```\n<key>Label</key>\n<string>com.shun.dashboard</string>\n<key>StartInterval</key>\n<integer>14400</integer>\n```\n\n`14400`\n\nseconds = 4 hours. While the Mac is awake, `dashboard.sh`\n\nruns once every 4 hours and the budget line gets rewritten automatically. Without manually running `ccusage blocks`\n\n, you can check the most recent aggregate just by looking at the file.\n\nIn unattended scripts like autopilot, you stop a job ahead of time using the `--short`\n\noutput (the wiring I introduced in the `claude-autopilot`\n\narticle).\n\n```\nBUDGET=$(~/.claude/scripts/token-budget-advisor.sh --short)\nif echo \"$BUDGET\" | grep -qE '🔴|critical|cap-near'; then\n  log \"ABORT: budget critical\"; exit 0\nfi\n```\n\nNote\n\nLabel matching alone can let something slip through right at the boundary. For critical decisions, it's safer to pull the`5h_output_tokens`\n\nfield out of the JSON output (without`--short`\n\n) as a number and check`>= 1200000`\n\ndirectly.\n\n`(session_id, transcript)`\n\nkey`command -v ccusage`\n\nand keep a fallback path that runs on cost-log.jsonl alone when it's missing`⚫ n/a`\n\noutput to protect the dashboard's other sections`ProgramArguments`\n\nthrough `/bin/zsh -c`\n\nso it picks up zsh's path`THRESH_SESS_PER_DAY`\n\ndepending on your usage`rate_limits`\n\nis `--`\n\nbefore the first turn`token-budget-advisor.sh`\n\naggregates from a `source_diff_pct`\n\n`(session_id, transcript)`\n\nkey, cost-log gives you a `--short`\n\nmode compresses down to a single `🟢/🟡/🔴`\n\nline, usable directly in shell gates and dashboard embedding`dashboard.sh`\n\nauto-updates on launchd's StartInterval 14400 (4h), keeping the budget line always currentNext time I'll write about log rotation for the `cost-log.jsonl`\n\nthis dashboard references, and compressing old entries before aggregation gets heavy.\n\n*Written by **Lily** — I ship iOS apps and automate my content stack with Claude Code.\n\nFollow along: [Portfolio](https://bokuwalily.com) · [X](https://x.com/bokuwalily) · [GitHub](https://github.com/bokuwalily)*", "url": "https://wpnews.pro/news/catch-the-5-hour-cap-before-you-hit-it-token-budget-advisor-and-status-line", "canonical_source": "https://dev.to/bokuwalily/catch-the-5-hour-cap-before-you-hit-it-token-budget-advisor-and-status-line-integration-14ab", "published_at": "2026-07-17 11:00:05+00:00", "updated_at": "2026-07-17 11:02:59.420542+00:00", "lang": "en", "topics": ["developer-tools", "large-language-models", "ai-tools"], "entities": ["Claude MAX", "token-budget-advisor.sh", "dashboard.sh", "statusline.sh", "ccusage"], "alternates": {"html": "https://wpnews.pro/news/catch-the-5-hour-cap-before-you-hit-it-token-budget-advisor-and-status-line", "markdown": "https://wpnews.pro/news/catch-the-5-hour-cap-before-you-hit-it-token-budget-advisor-and-status-line.md", "text": "https://wpnews.pro/news/catch-the-5-hour-cap-before-you-hit-it-token-budget-advisor-and-status-line.txt", "jsonld": "https://wpnews.pro/news/catch-the-5-hour-cap-before-you-hit-it-token-budget-advisor-and-status-line.jsonld"}}