claude-restarter
v0.3.0
Published
Automatically wait out Claude Code usage limits and resume the session (reset time + a 2-minute margin).
Maintainers
Readme
claude-restarter
Claude Code stops when you hit a usage limit — a red banner like:
You've hit your limit · resets 3pm (Europe/Dublin)claude-restarter watches for that banner, waits until the printed reset time
(plus a 2-minute safety margin), and automatically types a message to resume
the session. Leave it running — even overnight or AFK — and come back to a
session that kept going.
Quick start
claude-restarter installReload your shell:
source ~/.zshrc # or open a new terminalThen just use claude like you always have:
claudeThat's it. The wrapper is transparent — same command, same TUI. When a usage limit banner shows up, the monitor takes over in the background, waits out the reset, and resumes the session for you.
How it works
installadds a shell function wrapper aroundclaudeto your shell rc file. You keep typingclaudeexactly as before.- The wrapper runs Claude inside a tmux session (creating one automatically if you're not already in tmux). tmux is what lets the session survive SSH drops or a closed terminal.
- A background monitor polls the tmux pane (every 5s by default), strips ANSI codes, and looks — only in the live bottom region of the pane, never scrollback — for a usage-limit banner (a "limit" line near a "resets" line).
- When it finds one, it parses the reset time (timezone-aware, DST-safe; also
understands relative forms like "try again in 5 hours"), computes
wait = reset time + margin, and waits. - Before resuming, it runs a foreground-safety check: it only types into the pane if Claude/Node still owns it, so it will never type into a shell you're actively using instead.
- It sends the retry message via
tmux send-keysto continue the session, and repeats for subsequent limit windows, up tomaxRetries.
claude (wrapped) background monitor
───────────────── ──────────────────
runs inside tmux ──────────▶ polls pane every 5s
strips ANSI, scans live region
│
▼
limit banner found?
│ yes
▼
parse reset time (tz/DST-safe)
wait = reset + 2min margin
│
▼
foreground-safety check
│ pane still owned by claude/node
▼
tmux send-keys: retry message
│
▼
loop (up to maxRetries)There is also a headless -p / --print mode for scripted use: it buffers
output and re-runs automatically when a limit is hit.
Requirements
- Node.js >= 18
- tmux (required for interactive auto-resume —
brew install tmuxon macOS) - Claude Code (
claude) on yourPATH
Commands
| Command | What it does |
|---|---|
| claude-restarter install | Adds the wrapper to your shell rc (~/.zshrc or ~/.bashrc, chosen from $SHELL). Idempotent. Requires tmux. |
| claude-restarter uninstall | Removes the wrapper block from your shell rc. |
| claude-restarter disable | Turns off auto-resume everywhere, immediately — no shell reload, shell rc untouched. |
| claude-restarter enable | Turns it back on. Monitors that stood down resume within one poll. |
| claude-restarter message | Shows the retry message. Pass text to set it, --reset to restore the default. |
| claude-restarter status | Shows recent monitor activity (tails per-pane logs). |
| claude-restarter doctor | Checks prerequisites (node, claude, tmux) and whether the wrapper is installed. |
| claude-restarter --version | Prints the installed version. |
After install, reload your shell (source ~/.zshrc or open a new
terminal), then just use claude normally.
Turning it off temporarily
claude-restarter disable # `claude` runs unwrapped, everywhere, right away
claude-restarter enable # back to normaldisable writes a marker file at ~/.claude-restarter/disabled. The shell
wrapper checks for it before doing anything else, so the change lands in every
open terminal with no reload, and a disabled wrapper costs nothing — Node is
never started. Monitors already watching a session notice within one poll
(5s by default) and stand down — they stay alive but stop detecting and will
not type, so a resume scheduled for hours from now does not fire.
enable is symmetric: a monitor that stood down picks watching back up within
one poll, with no need to restart claude. Any schedule it was holding is
discarded and re-derived from what the pane shows now, so it never fires against
a reset window that passed while it was parked.
This is different from uninstall, which removes the wrapper from your shell rc
and needs a reload. disable leaves the rc alone; install state and enabled
state are independent.
For a single unwrapped run without changing any state, command claude … works
too — command skips the shell function.
If you installed the wrapper before this feature existed, re-run
claude-restarter install once so the block picks up the marker check.
enable and disable will tell you if that applies to you.
Changing the retry message
claude-restarter message # show the current one
claude-restarter message "Resume. Be brief." # set it
claude-restarter message --reset # back to the defaultWrites retryMessage to ~/.claude-restarter.json, leaving every other key in
that file alone. Running monitors re-read the config each poll, so a new message
applies to sessions already being watched — no restart. --reset removes the key
rather than writing today's default into the file.
Install from npm
npm install -g claude-restarter
claude-restarter installA global install is the sturdiest option: install bakes the absolute path of
bin/cli.js into your shell rc, and a package under lib/node_modules stays
put. A path inside a clone you might later move or rename does not.
Install from a clone
node bin/cli.js installor link it so the claude-restarter command is on your PATH:
npm link
claude-restarter installEither way, don't move or rename the directory afterwards without re-running
install — the shell wrapper points at the path it was installed from.
Configuration
Optional JSON file at ~/.claude-restarter.json. All keys are validated —
invalid values fall back to their defaults.
| Key | Default | Meaning |
|---|---|---|
| marginSeconds | 120 | Seconds to wait past the reset time before resuming (the 2-minute buffer). |
| pollIntervalSeconds | 5 | How often the pane is polled. |
| maxRetries | 100 | Max number of limit windows to sit through before giving up. |
| fallbackWaitHours | 5 | Wait used when a banner is detected but its reset time can't be parsed. |
| retryMessage | "Continue where you left off. The previous turn hit the usage limit." | Text typed to resume the session. Set it with claude-restarter message. |
| customPatterns | [] | Extra regex strings (as JSON strings) that also count as a limit banner. |
| foregroundCommands | node/claude/npx/tsx/bun/deno | Pane commands considered safe to send keys into. |
Example:
{
"marginSeconds": 120,
"pollIntervalSeconds": 5,
"maxRetries": 100,
"fallbackWaitHours": 5,
"retryMessage": "Continue where you left off. The previous turn hit the usage limit.",
"customPatterns": [],
"foregroundCommands": ["node", "claude", "npx", "tsx", "bun", "deno"]
}Logs
Per-pane logs are written under ~/.claude-restarter/logs/. claude-restarter status tails the recent activity from these logs. The enable/disable marker lives beside them at ~/.claude-restarter/disabled.
Limitations
- Requires tmux for the interactive auto-resume path.
- Weekly limits are detected but never precisely scheduled. The banner
names a time of day and not a day —
resets 6am, orresets Oct 9, 10am— and a weekly reset can be days out, so resolving6amto tomorrow morning would be a confident guess at the wrong day. The tool falls back to thefallbackWaitHourscadence and retries until the reset actually passes. Session-limit clock times ("resets 3pm") and explicit durations ("try again in 5 hours") are parsed precisely. - Detection is a heuristic over a human-facing terminal render, not an official API signal.
Attribution
The pane-detection heuristics and the timezone/DST-safe reset-time parsing are adapted from the MIT-licensed project claude-auto-retry; see NOTICE for details. License: MIT (see LICENSE).
Development
npm testRuns the test suite (node --test test/*.test.js). Tests cover pane
detection, time parsing, and the full detect → wait → resume monitor state
machine, driven with a fake tmux adapter and a fake clock.
Releasing
npm test # also runs automatically via prepublishOnly
npm pack --dry-run # inspect exactly what ships
npm publish # requires `npm login`files in package.json limits the tarball to bin/, src/, shell/,
NOTICE, and CHANGELOG.md; npm adds README.md, LICENSE, and
package.json. Tests, specs, and plans are not published. shell/wrapper.sh
must stay in the tarball — install reads it at runtime to build the rc
block.
