Heartbeat
Watchdog plugin that detects stuck, looping, or stalled AI coding agents and nudges them back on track.
Zero-config. Zero LLM cost. Just pattern matching.
Works with Claude Code and GitHub Copilot CLI.
Install
Claude Code:
claude plugin install matantsach/heartbeat
GitHub Copilot CLI:
copilot plugin install matantsach/heartbeat
That's it. No config file needed. Heartbeat starts watching immediately.
What It Detects
| Pattern | Name | Trigger |
|---|
| Same file edited N+ times | Edit-Undo Cycle | Rolling window fingerprint match |
| Same search repeated N+ times | Grep Spiral | Rolling window fingerprint match |
| Same failing command retried N+ times | Permission Hammer | Rolling window fingerprint match |
| N+ consecutive tool failures | Error Cascade | Consecutive error counter |
| Context window usage > threshold | Context Cliff | Cumulative output byte tracking |
| No tool activity for N seconds | Stall | Background timer + desktop notification |
How It Works
PostToolUse → detect pattern → set intervention flag
↓
PreToolUse (next call) → read flag → block tool (exit 2)
↓
Agent sees: "Heartbeat: Edit-Undo Cycle detected. You've edited
'src/auth.ts' 3+ times. Try a different approach."
↓
Agent adjusts strategy automatically
Escalation ladder:
- Nudge — block tool call with actionable message (agent self-corrects)
- Notification — desktop alert after max nudges (human intervenes)
- Tombstone — write failure context so restarted agents don't repeat the mistake
Configuration
All settings via environment variables. Defaults work out of the box.
| Variable | Default | Description |
|---|
HEARTBEAT_LOOP_THRESHOLD | 3 | Repeated fingerprints before detection |
HEARTBEAT_STALL_TIMEOUT | 120 | Seconds of inactivity before stall alert |
HEARTBEAT_ERROR_THRESHOLD | 5 | Consecutive failures before error cascade |
HEARTBEAT_CONTEXT_PCT | 80 | Context usage % before pressure warning |
HEARTBEAT_MAX_NUDGES | 3 | Nudges before escalating to notification |
HEARTBEAT_WINDOW_SIZE | 20 | Tool calls tracked in rolling window |
HEARTBEAT_DRY_RUN | 0 | Set to 1 to detect without blocking |
HEARTBEAT_ALLOWLIST | (empty) | Comma-separated fingerprints to skip (e.g. Bash:npm test,Read:*) |
HEARTBEAT_WEBHOOK_URL | (empty) | URL to POST intervention events (Slack, PagerDuty, etc.) |
HEARTBEAT_CI | (auto) | Set to 1 for CI mode (auto-detected from CI env var) |
Team Config
Check in a .heartbeat.yml to share settings across your team:
# .heartbeat.yml
loop_threshold: 5
stall_timeout: 180
error_threshold: 10
max_nudges: 5
dry_run: 0
allowlist: Bash:npm test,Bash:npm run build
webhook_url: https://hooks.slack.com/services/YOUR/WEBHOOK/URL
Environment variables override .heartbeat.yml values.
Dry-Run Mode
Run Heartbeat in observe-only mode to evaluate before enabling enforcement:
HEARTBEAT_DRY_RUN=1 claude
Heartbeat detects and logs all patterns but never blocks. Session summary shows (dry-run). Review .heartbeat/incidents.jsonl to tune thresholds before going live.
CI/CD Mode
When CI=true (auto-detected) or HEARTBEAT_CI=1:
- Writes machine-readable
.heartbeat/result.json
- Skips desktop notifications
- Still blocks tool calls (agents fail fast)
{"status":"blocked","pattern":"edit-undo-cycle","message":"...","intervention_count":1}
Webhooks
POST intervention events to Slack, PagerDuty, or any endpoint:
HEARTBEAT_WEBHOOK_URL=https://hooks.slack.com/services/T.../B.../xxx claude
Payload:
{
"event": "intervention",
"pattern": "edit-undo-cycle",
"message": "...",
"timestamp": "2026-03-14T10:30:00Z",
"host": "dev-laptop",
"cwd": "/home/user/project"
}
Session Tombstones
When an agent is stuck beyond recovery (max nudges exceeded), Heartbeat writes a tombstone to .heartbeat/tombstones/. On the next session start, the agent sees:
Heartbeat: Previous session died from 'edit-undo-cycle'.
Avoid repeating: You've edited 'src/auth.ts' 3+ times...
This prevents the restarted agent from walking into the same wall.
Session Output
Heartbeat: 47 tool calls, 12m, no issues
Heartbeat: 93 tool calls, 28m, 2 interventions
Heartbeat: 15 tool calls, 3m, 1 intervention (dry-run)
Incident Log
All events logged to .heartbeat/incidents.jsonl:
{"timestamp":"2026-03-14T10:30:00Z","pattern":"edit-undo-cycle","action":"tool_blocked","details":"...","tool_call_number":15}
Custom Patterns
Add pattern files to .heartbeat/patterns/ in your project: