← back to Claude Code Supervisor
README.md
83 lines
# claude-code-supervisor
Persistent, rate-limit-aware supervisor for a **single** Claude Code process on macOS.
## What it does
- Launches Claude Code as a child and watches its combined stdout/stderr.
- On a rate-limit indicator (`rate limit`, `429`, `too many requests`, `usage
limit`, `overloaded_error`, `quota exceeded`), it terminates **only that
child** (and its direct descendants), waits, and restarts it in the same
working directory.
- Consecutive problems back off: **20s → 40s → 60s → 120s → 300s**, pinned at
300s until a productive run resets the ladder.
- A run lasting ≥ `CLAUDE_SUP_SUCCESS_RESET` (default 180s) is "productive" and
resets the delay to the bottom of the ladder.
- Best-effort: if Claude prints a "try again in Ns" / "resets in Ns" hint, the
supervisor honors the longer of that vs. the backoff delay.
- Keeps per-run logs under `~/.claude/supervisor-logs/` for forensics.
## Two independent recovery paths
1. **This wrapper** = rate-limit-aware restart + backoff for an explicit,
unattended Claude command.
2. **`com.steve.claude-auto-resume`** = watches interactive Claude tmux panes
and sends `RESUME` after a stuck rate-limit screen remains unchanged for 20
seconds.
The included launchd plist keeps this wrapper process available after reboot,
but it intentionally remains inactive until configured with an explicit task.
## Safety
- Never pattern-kills `claude`. It only signals the exact PID it spawned and
`pkill -P` that PID's children — unrelated Claude sessions are untouched.
- `ThrottleInterval` + fast-clean-exit backoff prevent hot restart loops.
## Run it in the foreground (test)
```sh
cd ~/Projects/claude-code-supervisor
./supervisor.sh -- -p "<your unattended task prompt>" --output-format json
```
Tail the logs: `tail -f ~/.claude/supervisor-logs/supervisor.log`
The wrapper intentionally stays inactive when no arguments follow `--`.
Launching bare Claude from `launchd` has no terminal and no task, so it cannot
represent a resumable session. Interactive Claude panes are handled separately
by the existing `com.steve.claude-auto-resume` tmux daemon.
## Install as a launchd LaunchAgent (24/7, survives reboot)
1. Edit `ProgramArguments` in the plist to add your claude args after the
script path (or leave it to supervise bare `claude`).
2. Install:
```sh
cp com.steve.claude-supervisor.plist ~/Library/LaunchAgents/
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.steve.claude-supervisor.plist
launchctl enable gui/$(id -u)/com.steve.claude-supervisor
```
3. Verify: `launchctl list | grep claude-supervisor` (expect a PID, not `-`).
### Uninstall (fully reversible)
```sh
launchctl bootout gui/$(id -u)/com.steve.claude-supervisor
rm ~/Library/LaunchAgents/com.steve.claude-supervisor.plist
```
## Configuration (env vars)
| Var | Default | Meaning |
|-----|---------|---------|
| `CLAUDE_SUP_WORKDIR` | `$HOME` | working dir the child runs in |
| `CLAUDE_SUP_BIN` | `claude` | binary to supervise |
| `CLAUDE_SUP_SUCCESS_RESET` | `180` | seconds a run must last to reset backoff |
| `CLAUDE_SUP_CLEAN_DELAY` | `5` | restart delay after a productive clean exit |
| `CLAUDE_SUP_POLL` | `2` | monitor poll interval (s) |
| `CLAUDE_SUP_PATTERN` | see script | case-insensitive rate-limit regex |
| `CLAUDE_SUP_BACKOFF` | `20,40,60,120,300` | comma-separated retry ladder |
| `CLAUDE_SUP_LOG_DIR` | `~/.claude/supervisor-logs` | log directory |
## Important caveat
Restarting does **not** clear an account-level usage window — if you've hit a
multi-hour usage cap, a restart won't grant more tokens; the supervisor will
simply back off (and honor a printed reset time) until the window reopens. The
restart loop is most valuable for transient `429`s, `overloaded_error`s, and
wedged/stuck states. It also assumes a **headless / long-running** claude
invocation (e.g. print-mode or a scripted loop); a fully interactive TUI that a
human is typing into is not the intended target.