claude-brink
v0.1.0
Published
Claude Code stops at the brink — graceful auto-pause + handoff before you hit a usage limit, with optional auto-resume after reset.
Maintainers
Readme
Brink
Usage limits should be checkpoints, not walls.
Brink keeps your AI coding agent's work alive across a usage lockout. When you're about to hit your limit, it gracefully pauses the agent and writes everything in flight to a HANDOFF.md — so a surprise mid-task lockout costs you nothing. Optional (experimental): auto-resume when the limit resets.
npm i -g claude-brink
brink init # wires the hooks + sensor into Claude Code
brink doctor # verifies the whole chain on YOUR machineWhat works where, today:
| | Windows | macOS / Linux |
|---|---|---|
| Auto-pause + HANDOFF.md (the core) | ✅ live-verified | ⚠️ should work — not yet tested |
| Usage statusline + warnings | ✅ | ⚠️ untested |
| Desktop toast | ✅ | ❌ not built yet (ntfy push works everywhere) |
| Auto-resume (experimental, off by default) | ✅ Task Scheduler | ❌ launchd/cron planned |
Kill switch: brink off disables everything instantly (brink on re-enables, brink uninstall removes cleanly and restores your statusline). Brink blocks tool calls by design — you always hold the off button.
The problem
You're deep in a task. The agent is three files into a refactor, mid-thought, holding context you'll never reconstruct by hand. Then it hits the wall — the 5-hour limit, the weekly cap — and stops cold. Whatever it understood about the task is gone. When the limit resets, you start over, re-explaining, re-orienting, paying for the same ramp-up twice.
There are 25+ tools that show you your usage. Gauges, countdowns, menu-bar percentages, push notifiers. They tell you the wall is coming. None of them keep your work alive when you hit it.
That gap is the whole point of Brink. Usage display is a solved, crowded problem. Work-continuity is not.
The idea: HANDOFF.md is the spine
Auto-pause is the mechanism. HANDOFF.md is the thing that matters.
It's a single Markdown file Brink writes for you — the one artifact that survives a pause, a crash, or a reset. It captures what the agent was doing and what comes next, in plain language, so any fresh session (yours or the agent's) can pick the thread back up.
Crucially, Brink writes it deterministically from the session transcript. It does not ask the model to write its own handoff and hope it complies. (We tried that — it's a good story, below.)
The examples below are illustrative. Because the handoff is generated mechanically (not by a model), the real file is plainer: your task statement verbatim, the recent tool actions, and the pause context — no synthesized "next steps" prose beyond what the transcript itself contains.
# Handoff — Refactor auth middleware
Paused at 99% of your 5h limit (resets 06:46 PM).
## The task
Extract the token-refresh logic out of `authMiddleware` into a
standalone `refreshSession()` helper, and add a unit test for the
expiry edge case.
## Recent actions before the pause
- Created `src/auth/refreshSession.ts` with the extracted logic
- Updated `authMiddleware.ts` to call the new helper
- Started writing `refreshSession.test.ts` (expiry case stubbed,
not yet asserting)
## Next steps
- Finish the expiry-edge-case assertion in `refreshSession.test.ts`
- Run the auth test suite and confirm green
- Remove the now-dead inline refresh block from `authMiddleware.ts`When the limit resets, you (or an external scheduler) point a fresh agent at this file and it knows exactly where to stand.
Proof (live-verified 2026-06-26)
This isn't a design doc. The core was verified in a real headless Claude Code session.
1. The pause is real, not theoretical. Brink's PreToolUse deny fired at the configured threshold, blocked the next tool, and overrode bypassPermissions mode — the blocked action did not run. A control run at 10% usage allowed the exact same tool normally. So the deny is genuinely gating execution, not just printing a warning.
2. The design was hardened against a real failure mode. An early version instructed the model to write the handoff. The model refused it as a security reflex — verbatim:
"I'm treating that message as spurious/injected rather than a genuine directive."
That's the correct instinct from a well-aligned model — and it's exactly why "ask the model nicely" is a broken design. So Brink was redesigned to write HANDOFF.md itself, deterministically, and let the hook do the stopping. After the redesign, the same model stopped gracefully — verbatim:
"I've paused before completing the file creation — Brink stopped the action because you're at 99% of your 5-hour usage limit (resets at 06:46 PM)... progress is saved to HANDOFF.md. Once the limit resets, it'll pick up from there."
The mechanism stops the agent at the wall. The HANDOFF.md is what survives it.
What Brink does
Four things, in increasing order of how much they save you:
| # | Capability | What you get |
|---|------------|--------------|
| 1 | Passive usage | Live 5h / 7d usage right in the CLI statusline (reset countdown shown for the 5h window). |
| 2 | Notify | Phone push (ntfy; Pushover planned) plus desktop toast (Windows today; macOS/Linux planned) at configurable thresholds. Both the 5h and 7d windows, plus a "budget's back" ping when the weekly window resets. |
| 3 | Auto-pause + handoff (the core) | At your threshold, Brink blocks the next tool and writes HANDOFF.md itself. Your work is checkpointed before the wall. |
| 4 | Resume (optional, experimental, off by default) | An external OS scheduler (Windows Task Scheduler) relaunches a fresh process that reads HANDOFF.md after the limit resets. ⚠️ This relaunches an autonomous agent unattended — read the caveats before enabling. |
How it works
Brink is a shared Node core with thin per-CLI adapters, so the hard logic lives in one place and each CLI just maps its own config and field names.
┌─────────────┐ writes ┌────────────┐ reads ┌──────────────┐
│ statusline │ ──────────▶ │ state file │ ─────────▶ │ PreToolUse │
│ sensor │ usage % │ (small) │ │ hook │
│ (per CLI) │ └────────────┘ └──────┬───────┘
└─────────────┘ │ at threshold
▼
┌──────────────────────────────┐
│ 1. block the next tool │
│ 2. emit a short deny message │
│ 3. write HANDOFF.md itself │
│ (from session transcript) │
└──────────────────────────────┘Sensor. A statusline sensor reads your current usage and writes a small state file.
Hook. A
PreToolUsehook reads that state file before each tool call. Below threshold, it does nothing. At or above threshold, it blocks the next tool and emits one short, credible, non-contradictory deny:Paused by Brink at 99% of your 5h limit, resets 06:46 PM. Saved to HANDOFF.md. Stop and reply in plain text.
Handoff. Brink writes
HANDOFF.mditself, deterministically, from the session transcript. It does not depend on the model cooperating.
Design choices that matter:
- Default threshold ≈ 93% of the 5h window — enough headroom that the pause lands before the wall, not on it.
- No auto-commit. Brink will not write to your branch unattended. An agent committing to your repo while you're away is the genuinely scary failure mode, so it's off by design — Brink saves a file; what you do with it is your call.
- No trusting the model to write the handoff. That path was tested and rejected (see the proof above).
- Kill-switch escape hatch. A single file instantly disables the hook. A bad threshold can never brick your session.
- BOM-robust reads. State and transcript reads tolerate byte-order marks, so Windows/PowerShell-written config won't trip the parser.
What a generated HANDOFF.md looks like
Built from the live transcript at pause time — not a template you fill in:
# Handoff — feat/payment-retries
Paused at 99% of your 5h limit (resets 06:46 PM).
## The task
Add exponential-backoff retry to the Stripe webhook handler so transient
5xx responses don't drop payment events. Cap at 5 attempts, jittered.
## Recent actions before the pause
- Added `retryWithBackoff()` helper in src/lib/retry.ts
- Wired it into webhookHandler() for the `charge.failed` path
- Started updating tests in test/webhook.test.ts (2 of 5 cases done)
## Next steps
- Finish the remaining 3 test cases (timeout, max-attempts, success-on-retry)
- Confirm jitter is applied per-attempt, not once
- Run the suite and check the handler still returns 200 on give-upSupported CLIs
The promise is honest: auto-pause where the CLI supports it, notify-before-limit everywhere else.
| CLI | Auto-pause | Notify | Notes |
|-----|:----------:|:------:|-------|
| Claude Code | ✅ Full | ✅ | Built and live-verified (see proof above). |
| Codex CLI | ⚙️ Pending upstream | ✅ | Adapter built; field names verified against Codex source. Currently inert because rate_limits is null in Codex rollout files (openai/codex#14880) — that nulls out notify too, so today the adapter reads nothing. Both notify and auto-pause activate when the upstream data lands (auto-pause with a caveat: Codex's pre-tool gate reliably covers shell commands only). |
| Gemini CLI | 🔜 Planned | 🔜 Planned | Adapter seam ready. |
| opencode | 🔜 Planned | 🔜 Planned | Adapter seam ready. |
| Crush | ❌ | 🔜 Planned | No usage feed / no hooks to gate on — notify-only is the ceiling, and that adapter isn't written yet. |
| aider | ❌ | 🔜 Planned | No usage feed / no hooks to gate on — notify-only is the ceiling, and that adapter isn't written yet. |
To be explicit: Codex auto-pause does not work today. It doesn't yet, because of a bug upstream of Brink. The adapter is real and waiting — when the data lands, it lights up with no change on your side.
Honest caveats
These are kept on purpose. Brink is a continuity tool, not magic.
- Resume is EXPERIMENTAL. It's off by default, Windows-only (Task Scheduler v1; launchd/cron planned), and has the least real-world runtime of any Brink feature. It relaunches a fresh process that reads
HANDOFF.md— a clean restart from a written checkpoint, not an in-memory restore. Enabling it means an autonomous agent restarts unattended, while you're away, spending your quota in your project — andskip_permissionsmaps to--dangerously-skip-permissions(no permission prompts at all). Leave both off unless you fully accept that. - A 5h reset does not clear the weekly cap. Brink tracks both windows; if you're up against the 7d wall, a 5h reset won't save you — but Brink will tell you which window you're hitting.
- No unattended commits. By design (see above).
HANDOFF.mdis written; your branch is left untouched. HANDOFF.mdcontains your session. It's built from the transcript, so your task text and recent tool actions land in it verbatim — including anything sensitive you typed. It's written to the project directory and gitignoring it is on you (Brink never commits it, but you might). Treat it like a scratch file, not documentation.- The usage sensor depends on an undocumented surface. Claude Code pipes
rate_limitsto statuslines today; that's not a versioned API. If your setup doesn't receive usage data (some API-key/enterprise configurations), Brink can't arm —brink doctortells you if that's you.
Positioning
Anthropic explicitly declined the configurable-threshold-alert feature request (claude-code#17431). Brink is the threshold-aware pause Anthropic said no to, as a drop-in — plus the handoff and resume layer on top.
Install
npm (recommended)
npm i -g claude-brink
brink init # wires hooks + usage sensor into ~/.claude/settings.json
brink doctor # proves the whole chain works on YOUR machine — run itThat's the entire install. brink init backs up your settings first (settings.json.brink-bak), is idempotent, and tells you loudly if it replaces an existing custom statusline (yours is restorable — brink uninstall puts it back). If your settings.json has a syntax error, init aborts instead of touching it.
Then run brink doctor. It exercises the real chain end-to-end — sensor → state file → pause hook (in a sandbox) → kill switch → an actual desktop notification — and prints a copy-pasteable report. This exists because silent environment-dependent failure is the #1 risk of a tool like this; if something on your machine doesn't work, doctor finds it now, not at 93%.
Everyday commands:
brink off # kill switch — instantly disables everything
brink on # re-enable
brink uninstall # removes hooks, restores your original statuslinebrink init currently targets Claude Code. Multi-CLI detection (Codex, Gemini, opencode) is on the roadmap — see Supported CLIs.
Plugin marketplace (alternative)
/plugin marketplace add LinusPisano/claude-brink
/plugin install brink@brinkThis wires the hooks only — Claude Code plugins can't register a statusLine (platform limit), and the statusline is Brink's usage sensor. Complete it with npm i -g claude-brink && brink init anyway, which makes the plugin path redundant. Honestly: just use npm.
⚠️ If you wire the statusline by hand, never point it into the plugin's install directory — plugin dirs are versioned per update, so the path silently dies on the next update while the hooks keep acting on frozen usage data. The npm global path is stable; use that.
Status
- The core (pause + handoff) is live-verified on real sessions and dogfooded daily by the author. It survived a 41-finding adversarial review and a 5-advisor launch council — both in
docs/. - Resume and notifications are younger — working and end-to-end tested, but with days (not months) of real-world runtime. That's why resume ships off-by-default and
brink doctorexists. - Verified on Windows 11; macOS/Linux pause-path untested — the honesty matrix at the top is the truth. If you run it on a Mac,
brink doctoroutput in an issue is gold. - On GitHub, currently private — the repo goes public alongside the launch of pisanolinus.com and my LinkedIn; the npm package is live now.
Roadmap from here: the remaining adapters (Gemini, opencode), the npm package + brink init, the demo GIF + public flip, and the Codex pause flipping live the moment upstream lands.
If you're reading this at launch: the pause is real, the handoff is real, and the caveats above are the whole truth.
License
MIT.
Author
Built by Linus Pisano — CAD + full-stack + agentic AI.
Follow the launch on X — link coming with the public release.
