AgentStack
SKILL verified MIT Self-run

Otsukare

skill-willhsiaonyc-claude-code-skill-otsukare-claude-code-skill-otsukare · by WillHsiaoNYC

Use when starting a long or token-heavy task that might exhaust the Claude Code 5-hour or 7-day usage limit mid-run. Wraps the work in a usage-limit guardrail — arms a safety-net resume at the start, monitors the limits at natural seams, pauses safely (drains subagents, writes a checkpoint, commits WIP) at a 90% soft / 97% hard threshold, then auto-resumes just after the binding limit resets. Tri…

No reviews yet
0 installs
8 views
0.0% view→install

Install

$ agentstack add skill-willhsiaonyc-claude-code-skill-otsukare-claude-code-skill-otsukare

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Otsukare? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

otsukare — usage-limit guardrail

お疲れ — "good work, go rest," then pick the work back up. Invoke this at the start of a long or token-heavy task. otsukare does not do the work itself; it wraps it in a guardrail that watches the 5h/7d rate limits, pauses safely before a limit is exhausted, and auto-resumes just after the limit resets — even if the session is hard-cut by a sudden usage spike.

> Prerequisite: otsukare can only see your usage if your statusline mirrors it to the > data file — last-statusline-input.json in your Claude config directory. See the project > README ("Expose your limits to your local agent"). If that file is missing, tell the user > and stop — otsukare is blind without it.

Constants (edit to taste)

  • Helper: ` — the absolute otsukare_usage.py` path you pin at preflight (see Resolve your tools once below)
  • Data file (read-only): the mirror path returned by --resolve-paths (the statusline rewrites it on every render)
  • State dir: the state_dir returned by --resolve-paths — holds the checkpoint, the .heartbeat, and the .state.json run-metrics file
  • SOFT=90, HARD=97, STALE_BUFFER=8, STALE_AGE=120s, RESUME_OFFSET_MIN=10, retry tick every 5 min, HEARTBEAT_STALE=15min

The helper already applies SOFT/HARD/STALE_BUFFER/STALE_AGE/RESUME_OFFSET_MIN; override via --soft, --stale-buffer, etc. or OTSUKARE_* env vars if needed.

Hard constraint on the wrapped work

Do not dispatch run_in_background subagents during an otsukare run. Background agents return control immediately and finish later, so you cannot block on them at a seam — their output could land after the checkpoint and be lost. Use foreground/blocking subagents only, so "let in-flight subagents finish" is actually enforceable. If a background agent is unavoidable, record its task ID in the checkpoint and reconcile it in resume mode.

At start (preflight, refresh, then arm the safety net)

Resolve your tools once, reuse them below. Different OSes expose Python under different names and PowerShell does not expand ~, so pin these tokens before running anything else and reuse them verbatim:

  • ` — your Python launcher. Try py (the Windows launcher), then python3, then python; keep the FIRST whose --version` exits 0. Never run a bare interpreter with no script — it opens a REPL or, on Windows, hits the dead Microsoft Store stub.
  • ` — the absolute path to otsukareusage.py, i.e. /otsukare/scripts/otsukareusage.py with any ~` already expanded.
  • ` / / — once Step 0 confirms otsukare applies, run --resolve-paths --session-id --task-slug **once** and keep the returned absolute paths (the call also emits home, state_dir, and mirror). Take from the statusline mirror blob and from a short kebab of the task. Use these tokens everywhere below — never write a ~/...` path by hand.

Step 0 — confirm otsukare even applies here. It only guards subscription 5h/7d limits:

  --preflight
  • applicable: true → continue.
  • applicable: falseshow the user the message verbatim and STOP — do not arm, checkpoint, or wrap anything. Reasons: no_rate_limits = an API / usage-billed plan (metered per token, no rolling windows) — otsukare has nothing to guard; no_mirror = the statusline mirror isn't wired (see README); stale = the mirror isn't updating.

Once applicable, set up the dead-man's switch so a sudden usage spike that hard-cuts the session still auto-resumes:

  1. Block until usage is fresh — do this FIRST. It is the guard against reading a stale reset/usage left by a previous session or window:

`` --wait-fresh ` This blocks (up to 30s) until the statusline has re-rendered the mirror file with a **current 5h window** (reset in the future), then returns {"fresh": true, "resets_at": , ...}`. The statusline re-renders every few seconds while the session is active, so this normally returns in under a second.

  • fresh: true → proceed; the subsequent --arm and decision reads now see current data.
  • timed_out: true / fresh: false → the mirror isn't updating (statusline not wired, or the session is idle). Tell the user usage couldn't be refreshed, then proceed on the conservative path (--arm will return provisional: true and the first seam will re-point it).

Never read the plain decision/--arm for the initial reset time without clearing this barrier first.

  1. Write the initial checkpoint to `` (the path you resolved above). Use the template in Pause protocol step 3, filling Goal / Session ID / Task slug / cwd / branch and leaving Done/Next to be updated at each seam.
  2. Bump the heartbeat: --touch-heartbeat
  3. Initialize run metrics (baselines for the end-of-task summary):

`` --state --state-action init --task "" ``

  1. Arm the safety-net cron. Get the target + cron string:

`` --arm ` This prints {"safety_target": epoch, "cron": "/5 H D M ", "provisional": bool, ...}. Call the **CronCreate** tool with cron = that string, recurring: true, durable: true, and prompt = the resume prompt (see *Schedule the resume*). Record the returned job ID in the checkpoint. If provisional: true`, note that the first seam will re-point it.

Then begin the work.

Run loop

Do the wrapped work in steps. After each natural seam — a foreground subagent batch returns, a plan/todo step completes, or you make a commit (never mid-edit) — do a seam check:

  1. Emit a one-line seam note to the user, e.g. ✓ step 3/8 done — checking usage. This is required, not cosmetic: emitting text triggers a statusline render, which refreshes the data file so the next read is fresh.
  2. Update progress + heartbeat: rewrite the checkpoint's ## Done / ## Next, then bump the heartbeat — --touch-heartbeat .
  3. Run the helper:

```

`` It prints JSON: {"decision": "...", "now": epoch, "stale": bool, "stalewindow": bool, "limits": {...}, "bindingresetsat": epoch|null, "resumetarget": epoch|null}`.

  1. If stale_window is true, the reading is from an already-reset window — refresh before trusting it. A resets_at at or before now means the mirror still holds a previous window's blob (its usage % is pre-reset and no longer applies), so the helper has flagged that limit rolled and ignored its percentage. This is exactly the "it paused at 97% right after the limit reset" failure. Block for a fresh render and re-read, then act on the refreshed decision:

``` --wait-fresh

`` If --wait-fresh still times out (fresh: false`), the statusline isn't updating — do not pause on the stale numbers; keep working and re-check at the next seam.

  1. Re-point the safety net if the 5h window rolled. If limits.five_hour.resets_at is later than the currently-armed target minus the offset (or the armed target was provisional), CronDelete the old job and re-arm via --arm so the net always points at the next reset. Update the job ID in the checkpoint.
  2. Act on decision:
  • continue → keep working.
  • soft → if the remaining work is near a finish line (last step, or one more small foreground batch), push through to finish — but re-check at every seam and never cross hard. If substantial work remains, run the Pause protocol.
  • hard → run the Pause protocol at this seam, no exceptions.

If stale is true (file is old but the window is still current), the helper has already added STALE_BUFFER to the effective usage (it rounds against you). Trust the decision. stale_window is the different case above — an expired window whose percentage is discarded, not buffered.

Pause protocol

When otsukare decides to pause proactively:

  1. Drain, never hard-kill. Stop dispatching new subagents; let the current foreground subagent finish (control returns naturally). Do not TaskStop mid-work.
  2. Reach a clean seam.
  3. Finalize the checkpoint ``:

```markdown # otsukare checkpoint —

  • Goal:
  • Session ID:
  • Task slug:
  • Absolute cwd:
  • Branch:
  • WIP SHA:
  • Binding limit: , resets_at ()
  • Resume target: ()
  • Safety-net cron job ID:
  • Outstanding background agents:

## Done

## Next

```

  1. Commit WIP and record the SHA into the checkpoint:

`` git add -A git commit -m "wip(otsukare): checkpoint before usage pause" git rev-parse HEAD # paste into the checkpoint's "WIP SHA" line ``

  1. Re-point the safety net to the binding reset if it differs from the armed target (e.g. the 7d limit binds, days later): CronDelete the old job, then CronCreate with the cron from --cron-for . Update the job ID in the checkpoint.
  2. Record the pause in run metrics:

`` --state --state-action pause --pct ``

  1. Notify (single message): current used %, where you stopped, the checkpoint path, the binding limit, and the resume target as local time.

Schedule the resume

The resume prompt used by both the safety-net cron and the proactive re-point:

> otsukare resume mode. Checkpoint: . Binding reset epoch: . Follow the Resume mode section of the otsukare skill.

Embed the absolute checkpoint path (the resolved ``) directly in the prompt — the future session reads it before it has resolved anything of its own.

For the proactive re-point, get the cron string with:

  --cron-for 

It prints */5 H D M * — every 5 min, but only during the target hour/date, so it retries ~12× around the reset and does not fire on other days.

Resume mode

When a scheduled prompt fires (either the safety net or a proactive re-point):

First, re-pin your tools and paths. A cron prompt starts a fresh session, so re-resolve ` and exactly as at preflight, then recover the original run's paths: the prompt carries the checkpoint's absolute path, so open it and read its Session ID and Task slug fields, then run --resolve-paths --session-id --task-slug to get back / / `.

  1. Is the work still alive? Check the heartbeat:

`` --heartbeat ` It prints {"alive": bool, "age_seconds": ...}`.

  • alive: true → the main run is still going; exit quietly (it will handle its own pausing). Let the next tick re-check.
  • alive: false (or checkpoint missing / marked complete) → proceed to take over. If the checkpoint is missing or its ## Next is empty, CronDelete the job and exit — nothing to resume.
  1. Confirm the limit actually cleared. First block for a fresh render so you don't read a pre-reset file, then run the clear-check:

`` --wait-fresh --wait-timeout 20 --resume-check ``

  • {"status":"wait", ...}stop; the next retry tick will try again.
  • {"status":"clear", ...} → proceed.
  1. Re-establish context (a cron prompt fires in whatever cwd/branch the REPL holds):
  • cd to the checkpoint's recorded absolute cwd.
  • git checkout . If the working tree has uncommitted WIP from a hard cut, commit it with git add -A followed by git commit -m "wip(otsukare): recovered uncommitted work" (two separate commands; PowerShell rejects &&). Then verify the base is sane; if the recorded WIP SHA is set, confirm git rev-parse HEAD is that SHA or a descendant of it. If the branch/state is clearly wrong, abort and report — do not continue on the wrong base.
  1. Reload the ## Next list from the checkpoint.
  2. Record the resume in run metrics. ` is keyed by the **original** session_id` (re-resolved above from the checkpoint), so a dead-man's-switch takeover in a fresh session still updates the same metrics:

`` --state --state-action resume ``

  1. Notify (one line): resuming , 5h now at X%.
  2. Re-arm a fresh safety net for the new window (--arm) and continue the work from ## Next, re-entering this run loop.

On completion (summary + cleanup)

When the wrapped work finishes:

  1. Print the run summary — continues, run time, tokens, and cost, all measured as the delta over the otsukare span:

`` --state --state-action summary ` Show its output to the user verbatim, e.g.: ` お疲れ! otsukare summary — Continued: 2× (91%, 96%) Run time: 4h 18m total · 1h 51m active · 2h 27m waiting Tokens: 412K in/out · 4.1M cache (4.5M total) Cost: $3.84 ` (If the run never paused, Continued: 0×` — still a tidy receipt of time/tokens/cost.)

  1. CronDelete the safety-net job (using the ID in the checkpoint).
  2. Mark the checkpoint done — delete ` or write STATUS: complete` at the top.
  3. Remove the heartbeat and state file: --cleanup --heartbeat --state .

A run that never hits a limit thus creates the safety-net job at start and deletes it at the end — net zero lingering jobs.

Notes & guards

  • The data file updates only when the statusline renders; in practice that is every few seconds while the session is active. The --wait-fresh barrier at start and resume blocks until a render lands with a current 5h window, so the initial reset time is never read from a stale file. For mid-run seam reads, the helper guards itself: usage within a window only rises, so an old-but-current-window file (stale: true) is handled by the stale buffer, while an expired-window file (stale_window: true, resets_at ≤ now) has its pre-reset percentage discarded rather than buffered — so a read taken right after a reset can never force a false pause. When stale_window is true, refresh via --wait-fresh and re-read to get the new window's real usage.
  • Usage is account-global — it reflects whichever session rendered last. That is why resume mode requires the file to be fresher than the reset before trusting "cleared."
  • If both 5h and 7d are over threshold, the helper binds to the later reset (you must wait for both).
  • The safety net is the failsafe for a sudden swarm-to-100% that hard-cuts the session before a seam: it is armed up front, so a resume is scheduled even if the pause protocol never runs. The heartbeat ensures it only takes over when the main run is actually dead, never duplicating a live session.
  • If there is no work left when a pause would trigger, just finish normally — run the cleanup, no resume scheduled.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.