Are you the author? Sign in to claim
Keep Claude Code alive and autonomous without -p. Heartbeat hook + inbox/outbox pattern.
Stateless autonomous agent for Claude Code — no -p, no SDK credits.
The heartbeat hook turns an interactive Claude Code session into a stateless task processor. Each message from the inbox gets its own fresh session, just like -p — but using your regular subscription.
Anthropic is separating -p and SDK usage into a dedicated credit bucket. If you've been using claude -p for automation, your costs just went up.
The heartbeat hook gives you -p behavior in interactive mode, which uses your regular subscription — not SDK credits.
supervisor.js → claude (interactive) → hooks/heartbeat.js (stop hook)
↓
io/inbox.jsonl ← external events
io/outbox.jsonl → relay → discord/slack/webhook
io/inbox.jsonlio/outbox.jsonlOne message = one session = fresh context every time.
When the inbox is empty, the hook polls internally and blocks with minimal idle ticks (~20 tokens each) to keep the session alive until work arrives.
git clone https://github.com/Siigari/claude-heartbeat.git
cd claude-heartbeat
node supervisor.js
That's it. The hook is already configured in .claude/settings.json. The supervisor launches Claude, and the heartbeat hook handles the rest.
Options:
node supervisor.js sonnet "Read CLAUDE.md" # default
node supervisor.js opus "Read CLAUDE.md" # use opus
From another terminal:
# Plain text works
echo "what is 2+2" >> io/inbox.jsonl
# JSON format also works
echo '{"ts":"2025-01-01","channel":"test","author":"you","content":"hello agent"}' >> io/inbox.jsonl
Messages are processed one at a time. If 5 messages pile up, each gets its own fresh session.
cd relay
npm install discord.js
BOT_TOKEN=your_token node relay.js
# From a cron job
node examples/cron-trigger.js
# From an HTTP webhook
node examples/webhook-receiver.js
-pscreen or tmux)Each line in io/inbox.jsonl is either plain text or a JSON object:
fix the bug in auth.js
{"ts":"2025-05-13T10:00:00Z","channel":"discord","author":"username","content":"message text"}
Lines are consumed one at a time. The hook tracks its position via a byte offset in io/.inbox-offset.
The agent writes responses as JSON lines to io/outbox.jsonl:
{"action":"send","channelId":"123456789","content":"response text"}
┌─────────────┐
│ supervisor │ (restarts on exit)
└──────┬──────┘
│
┌──────▼──────┐
│ claude code │ (interactive session)
└──────┬──────┘
│ stop hook fires after each response
┌──────▼──────┐
│ heartbeat.js │
└──────┬──────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
inbox.jsonl .responded .restart
(read 1 line) (flag) (signal supervisor)
Idle flow: hook polls inbox → nothing → blocks with minimal tick → agent responds . → repeat
Message flow: hook reads one line → blocks with message → agent processes → hook sets .restart → supervisor kills session → restarts fresh
Run multiple terminals, each with their own working directory and inbox/outbox:
# Terminal 1: coding agent
cd ~/agents/coder && node supervisor.js
# Terminal 2: monitoring agent
cd ~/agents/monitor && node supervisor.js
# Terminal 3: research agent
cd ~/agents/research && node supervisor.js
| Variable | Default | Description |
|---|---|---|
HEARTBEAT_INTERVAL | 60 | Seconds between idle ticks |
WATCHDOG_TIMEOUT | 300 | Seconds before supervisor kills a stuck session |
examples/cron-trigger.js — inject tick messages on a scheduleexamples/webhook-receiver.js — HTTP endpoint that writes to inboxrelay/relay.js — Discord relay that sends outbox messagesConvergence — companion AI with memory, personality, and physical connection.
Hooking implementations and supporting tools for various coding agents (Claude, Cursor, Gemini, etc)
Claude Code hook that writes a forward-only why-block (decisions, trade-offs, assumptions, limitations) into your PR des
Blocks dangerous git and shell commands from being executed by AI coding agents
One command to install 6 essential safety hooks in 10 seconds — zero dependencies