@tigorhutasuhut/herdr-claude-retry
v0.1.8
Published
Herdr daemon — monitor Claude CLI sessions and auto-retry on rate-limit and errors
Readme
herdr-claude-retry
Watches Claude CLI panes running inside herdr. When a pane hits Anthropic's
usage/session limit, it detects the on-screen rate-limit banner, cross-checks against Anthropic's
usage API to get the exact reset time, and injects continue automatically once the limit clears.
Zero polling — event-driven via herdr's socket API.
Requirements
- Node.js >= 20
- herdr >= 0.7 (provides the UNIX socket API and
HERDR_SOCKET_PATHenv var) - Claude Code running inside a herdr-managed pane
- A logged-in Claude Code installation with
<CLAUDE_CONFIG_DIR>/.credentials.json(for usage-API detection; without it the daemon degrades gracefully to on-screen text parsing)
Install
npm install -g @tigorhutasuhut/herdr-claude-retryThe installed command is herdr-claude-retry.
Usage
Run as a foreground daemon in its own herdr pane (the daemon excludes its own pane from monitoring):
herdr-claude-retry startOptions
| Flag | Default | Description |
|------|---------|-------------|
| --socket-path <path> | $HERDR_SOCKET_PATH or ~/.config/herdr/herdr.sock | herdr socket path |
| --margin-seconds <n> | 60 | Extra seconds to wait after reset time before injecting |
| --sweep-interval <n> | 300 | Reconcile sweep interval (seconds) |
| --log-level <level> | info | Minimum log level: debug, info, warn, error |
What it watches
Every pane herdr knows about — current and future. New panes are picked up automatically via
herdr's pane.created event subscription. The daemon skips its own pane.
How it works
The daemon runs two concurrent loops:
Event loop — subscribes to
pane.output_matched(regex: rate-limit phrases) andpane.agent_status_changedon all known panes. When herdr fires a match, the pane is immediately checked.Reconcile sweep — runs on startup and every
--sweep-interval-ms. Walks all live panes viaagent.list, prunes gone panes, checks any not already in an active state. Catches panes the event loop might miss (reconnects, races).
Per-pane state machine:
- MONITORING — no banner, idle. On banner detected:
- Account LIMITED via usage API → enter WAITING until
resets_at. - Account CLEARED or UNKNOWN (API down) → parse reset from on-screen text:
- Future reset → enter WAITING.
- Already-past reset + canonical banner at screen bottom → inject
continueimmediately (text-fallback path).
- Account LIMITED via usage API → enter WAITING until
- WAITING — banner gone (pane exited/user resumed) → drop back to MONITORING. Account
cleared or timer elapsed → inject
continue.
Inject sequence: Ctrl+C (clears partial input) → continue + Enter via pane.send_text.
Account resolution (Linux): reads CLAUDE_CONFIG_DIR from the Claude process's /proc
environment, falls back to session UUID → config dir scan, then default ~/.claude. On non-Linux
or when resolution fails, usage=null → text-fallback path.
Development
npm install
npm run typecheck # tsc --noEmit
npm test # node --test (unit tests, no live herdr needed)
npm run build # tsc -> dist/
npm run verify # typecheck + test + build (publish gate)
npm run e2e # acceptance test — needs live herdrThe e2e test (test/e2e/blocked-pane.e2e.ts) verifies live connectivity to the herdr socket:
connects, lists agents, reads all live panes (the core one-shot protocol fix), and runs a daemon
reconcile sweep asserting zero paneRead failures. Skips gracefully if no herdr socket is found.
Publishing
Releases are published to npm by GitHub Actions
(.github/workflows/publish.yml) on push to a v* tag.
Auth uses npm Trusted Publishing (OIDC) — no
NPM_TOKEN secret — and provenance
is attached automatically.
npm publishis a manual human step — the workflow runs on a tagged release you create.
One-time setup
- Bootstrap the package (trusted publishing can only be configured on an existing package).
Publish
0.1.0once from your machine:npm login npm run verify npm publish --provenance=false--provenance=falseis required for local bootstrap: provenance requires OIDC from CI. - Configure trusted publisher on npmjs.com: package → Settings → Trusted Publisher →
GitHub Actions:
- Organization or user:
tigorhutasuhut - Repository:
herdr-claude-retry - Workflow filename:
publish.yml
- Organization or user:
Cutting a release
npm version patch # bump version + create git tag
git push --follow-tags
# workflow triggers on the v* tag pushThe workflow runs npm ci → npm run verify → npm publish --provenance. prepublishOnly
gates the publish on a clean typecheck, test, and build.
