Proactive 5h usage guard for Claude Code (macOS). Pause before extra wallet on long batch/subagent runs. Account-level window visibility. /usage-guard skill + daemon. Not affiliated with Anthropic.
SaferSkills independently audited usage-guard (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Proactive usage guard for long Claude Code sessions (Desktop Code tab or CLI). Invoke once at session start before heavy batch, subagent, or overnight work — especially when the account window is already partly used.
Each Claude session only sees its own usage. The shared 5-hour window is account-level — you can start at 58% in one session while another tab or earlier work already pushed the window to 94%. The daemon polls the OAuth usage API (same as Desktop Settings) so the guard sees the real window, not just this chat's view.
Arms do not survive Desktop quit, crash, or daemon exit. At the start of every new sitting, run /usage-guard (or /usage-guard resume if continuing a paused task). If arm.sh prints "warning", the previous guard was stale — proceed with the new arm.
/usage-guardWhen the user says "use usage-guard", "pay attention to usage limits", or names this skill but does not run the slash command:
arm.sh via bash before heavy work (same as ARM MODE below)./usage-guard is the normal entry point; with disable-model-invocation it may not show in the slash picker — typing `/usage-guard` still works.Do not auto-arm on your own initiative without the user asking for usage protection.
/usage-guardresume (or starts with resume): RESUME MODE (skip arm, go to section 4).Run:
bash "${CLAUDE_SKILL_DIR}/scripts/arm.sh" {{ARGUMENTS}}If arm fails, stop and tell the user to run usage-guard doctor in Terminal. Do not start heavy work without a successful arm.
Read ~/.usage-guard/control.json once and confirm:
armed: truephase is normal (or five_hour_percent is set) — not stuck on waiting_first_poll without a live daemon"warning", tell the user the prior guard was not active and this is a fresh armfive_hour_percent, five_hour_reset_local, seconds_until_five_hour_reset, state — not raw UTC stringsweekly_percent is present (weekly monitoring enabled in config), also report weekly % and weekly_reset_local / seconds_until_weekly_reset. Weekly can trigger PAUSE even when 5h looks safe.extra_used_credits, extra_monthly_limit, extra_utilization, extra_currency (amounts are in cents; usage-guard status formats them), or run usage-guard poll for a live snapshotIf arm JSON has `"already_armed": true` (daemon from a prior sitting):
--force restart unless user asks.join.sh --task "label" (not arm with a task — task is ignored on already_armed; JSON includes task_ignored if you tried).checkpoint.sh writes to sitting_session_id automatically (stored in control.json). Do not use primary session_id unless you pass --session-id intentionally./usage-guard resume (not join, not fresh arm).Decision tree: already_armed + same sitting → proceed on checkpoint_writes_target | new sitting same arc → join.sh | paused task → resume | dead daemon → arm fresh
Warmup: right after arm, phase may be waiting_first_poll with null telemetry while the daemon fetches usage. arm.sh blocks up to ~45s for the first poll. If phase is still waiting_first_poll after arm returns, the daemon is warming up — do not start heavy subagent batches yet; re-read control.json in a few seconds. If five_hour_percent stays null and daemon is dead (usage-guard status → daemon not running), stop and run usage-guard doctor.
You are now usage-guard armed. Follow this for the rest of this session:
~/.usage-guard/control.jsonRUN = continue, PAUSE / COOLDOWN / UNKNOWN = stop dispatching new work.pause_reason (five_hour, weekly, or both) in control.json.valid_until. Do not start heavy work; tell user to run claude login or usage-guard doctor. Prefer effective_state if present — it already fails closed on staleness/blindness.now is past valid_until (or stale: true), treat as UNKNOWN even when state still says RUN (daemon may have died while numbers looked healthy).five_hour_percent yourself.five_hour_resets_at, sleep_until, etc.). UTC trips up models. Use precomputed fields only:seconds_until_five_hour_reset — positive = reset not yet; negative or five_hour_reset_pending: false = reset time passedfive_hour_reset_local — for user-facing reports (local timezone, not UTC)weekly_percent, weekly_reset_local, seconds_until_weekly_reset — when weekly monitoring is enabled (OAuth seven_day = Desktop "All models" weekly bar)state is `RUN`, continue — do not call percent "stale" because a UTC clock comparison looked past. Wrong timezone math caused false "reset already passed" bugs.five_hour_reset_pending: false + non-null five_hour_percent (e.g. 88% with reset time an hour ago) means the old window's percent until the daemon re-polls — not a reason to pause. Check awaiting_post_reset_poll and percent_note; trust `state`. Expect correction at daemon_next_poll_at / daemon_next_poll_local.five_hour_percent may be null until the daemon's next poll. If state is RUN, trust state (check last_reset_at if present) — do not wait for the user to return./loop guard ticks while armed. During PAUSE/COOLDOWN, prefer `sleep_until` instead. Daemon polling is separate (daemon_next_poll_at).~/.usage-guard/sessions/<session_id>/checkpoint.json (session_id from control.json)done, next, note).bash "${CLAUDE_SKILL_DIR}/scripts/checkpoint.sh" --quiet --json '{"done":["item-id"],"next":"next-item","note":""}'Or flags: checkpoint.sh --quiet --done item-id --next "next-item" --note "" --task "short label for status"
Set `task` on the first checkpoint (via /usage-guard <task> at arm, or checkpoint.sh --task) so usage-guard status stays readable.
PAUSE/RUN is account-level (one daemon). Checkpoints are per `session_id`.
/usage-guard <task> (arms daemon; sets primary session_id and sitting_session_id).join.sh --task "this sitting's label" — sets sitting_session_id in control.json; all `checkpoint.sh` writes go there until the next arm/join.--session-id <existing> to checkpoint.sh explicitly.active_session_ids lists all sittings; sitting_session_id is the active checkpoint write target.
usage-guard status in Terminal to see progress if Desktop UI is stuck.Read control.json only at these times:
/loop iteration dedicated to guard checksNever rely on the user sending a chat message mid-run — messages queue during subagent work.
state == PAUSE or COOLDOWNdone, next, clear note.pause_reason); daemon may macOS-notify at reset. Do not treat that as a substitute for scheduling your own wake-up below.COOLDOWN, use `sleep_until` / seconds_until_five_hour_reset for wait duration — do not poll every 5 minutes.Before ending this turn, you must schedule a wake-up. Forbidden: ending with "ping me when reset", "tell me when you're back", or "message me after the window resets".
Pick one path (in order of preference):
A. `/loop` guard tick (preferred) — any session type:
/loop Read ~/.usage-guard/control.json. If PAUSE/COOLDOWN: checkpoint if needed; sleep until sleep_until (cap 59m per iteration; chain). If RUN: continue from checkpoint next.Dynamic /loop (no interval) uses ScheduleWakeup internally after each iteration.
B. One-shot wake at reset — if you are not starting /loop:
in N minutes, re-read ~/.usage-guard/control.json; if RUN continue checkpoint next; if still COOLDOWN schedule againCronCreate one-shot pinned to five_hour_reset_local (+ ~3 min margin)ScheduleWakeup or CronCreate — either is fineCOOLDOWN after fire, schedule the next check (cap 59m steps)C. Non-negotiable: schedule something before ending the turn. Platform docs emphasize ScheduleWakeup for dynamic /loop, but it often works for one-shot waits too — never fall back to asking the user to ping you.
five_hour_reset_local — checkpoint is still safe for /usage-guard resume.state == RUN with existing checkpointContinue from next in checkpoint; skip items in done.
RUN, do not dispatch.At session end (or before a long pause), tell the user:
five_hour_percent, five_hour_reset_local, stateweekly_percent, weekly_reset_local, pause_reasonextra_used_credits / extra_monthly_limit / extra_utilization from control.json, or usage-guard status for formatted amountsIf the user provided a task in /usage-guard <task>, begin it under the contract above using self-paced /loop.
Example loop prompt:
/loop Read ~/.usage-guard/control.json. If PAUSE/COOLDOWN: checkpoint and stop new work; sleep until sleep_until (or session_check_seconds). If RUN: continue task from checkpoint./usage-guard resume~/.usage-guard/control.json — if state is not RUN, tell user to wait (show five_hour_reset_local / sleep_until_local, or seconds_until_*).sitting_session_id (or checkpoint_writes_target / primary session_id if no join).next; dedupe using done.armed is false: run arm.sh again./usage-guard resume in a new session (re-arms daemon if needed)./usage-guard or /usage-guard resume at the start of a sitting.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.