/heartbeat
This skill should be used when the user asks to "run a heartbeat", "run the agent loop", "process GitHub issues", "check for work", or runs the /heartbeat command. Executes the WoterClip heartbeat — picks up GitHub issues, resolves personas, does work, and reports back.
$ npx -y skills add wotai-dev/woterclip --skill heartbeat --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.
- You can call itInvoke it directly when you want it.
- Slash command
/heartbeat
Context preview
The summary Claude sees to decide when to auto-load this skill.
This skill should be used when the user asks to "run a heartbeat", "run the agent loop", "process GitHub issues", "check for work", or runs the /heartbeat command. Executes the WoterClip heartbeat — picks up GitHub issues, resolves personas, does work, and reports back.
SKILL.md
heartbeat.SKILL.mdname: heartbeat
description: This skill should be used when the user asks to "run a heartbeat", "run the agent loop", "process GitHub issues", "check for work", or runs the /heartbeat command. Executes the WoterClip heartbeat — picks up GitHub issues, resolves personas, does work, and reports back.
version: 0.1.0
WoterClip Heartbeat
Execute the WoterClip heartbeat cycle: pick up assigned GitHub issues, resolve the right persona, do the work, and report back with structured comments. All GitHub operations go through the `gh` CLI — no MCP server required.
**Arguments:**
- `--dry-run` — Show what would be picked up without doing work
- `--persona <name>` — Only pick issues matching a specific persona
**Reference files** (consult as needed during execution):
- `${CLAUDE_PLUGIN_ROOT}/references/comment-format.md` — Comment templates and rules
- `${CLAUDE_PLUGIN_ROOT}/references/label-conventions.md` — Label lifecycle and atomic label operations
- `${CLAUDE_PLUGIN_ROOT}/references/status-mapping.md` — GitHub state model, sort order, inbox filtering
- `${CLAUDE_PLUGIN_ROOT}/references/sub-issues.md` — Canonical create/attach/verify procedure for sub-issues
- `${CLAUDE_PLUGIN_ROOT}/references/persona-dispatch.md` — Subagent dispatch, outcome contract, and fallback rules
- `${CLAUDE_PLUGIN_ROOT}/references/beat-economics.md` — Clock capture, time ceiling, stop reasons, log fields
All `gh issue` / `gh api` calls below target the repo from config `github.repo` (pass `--repo <owner/name>` explicitly — never rely on the working directory's default remote).
Step 1: Load Config & Lock
1. Read `.woterclip/config.yaml`. If missing, stop and instruct the user to run `/woterclip-init`. Nothing is created yet, so this exit records nothing. 2. Check for lockfile at `.woterclip/.heartbeat-lock`.
- Exists and **less than** `stale_lock_hours` old → stop: "Previous heartbeat still active. Skipping." **This beat never took the lock — do not delete it and do not record a beat line.**
- Exists and **older than** `stale_lock_hours` → delete it, log: "Cleaned stale lockfile."
- No lockfile → proceed.
3. Take the lock. This one command creates it **and prints it**, so the beat observes its own id and start epoch:
printf '{"beat_id":"%s","started_at":"%s","started_epoch":%s}\n' \
"$(date -u +%s)-$$" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$(date -u +%s)" \
| tee .woterclip/.heartbeat-lockCarry the printed `beat_id` and `started_epoch` forward — they are the beat's identity and its clock. 4. **Ownership rule, applied at every exit from here on.** Re-read `.woterclip/.heartbeat-lock`. Delete it **only if it still carries this beat's `beat_id`**; if it is missing or carries another id, a later beat cleaned and re-took it — leave it alone. Deleting a lock this beat does not own hands two beats the same repo. 5. **Every exit from here on** records one beat line (step 9 format) naming that exit's stop reason, then applies the ownership rule. `--dry-run` and a superseded beat record none. The exit-to-reason map is in `${CLAUDE_PLUGIN_ROOT}/references/beat-economics.md`. Beat elapsed is `$(date -u +%s)` minus `started_epoch`.
Check quiet hours: if `quiet_hours.enabled` and current time is within the quiet window:
- `behavior: "skip"` → record a `quiet_hours` beat line, apply the ownership rule, exit: "Quiet hours active. Skipping."
- `behavior: "triage-only"` → proceed but only load Orchestrator persona (skip worker personas in step 3).
Step 2: Check Inbox
1. Fetch open issues assigned to the authenticated user:
gh issue list --repo <owner/name> --assignee @me --state open \
--json number,title,labels,createdAt --limit 1002. Filter and sort client-side per `${CLAUDE_PLUGIN_ROOT}/references/status-mapping.md` § Filter Rules and § Sort Order (labels come from the JSON above). 3. Detect stale `agent-working` labels: if an issue has `agent-working` but no heartbeat comment within `stale_lock_hours`, clean the stale label (`gh issue edit N --remove-label agent-working`, post cleanup comment).
Step 3: Pick Issue
1. If `--persona <name>` flag is set, filter to only issues matching that persona's label. 2. Pick the first issue from the sorted inbox. 3. If `--dry-run`, report what would be picked, then apply the ownership rule and exit. No beat occurred, so record **no** beat line:
Dry run — would pick:
#12 [backend] "Issue title" (in-progress, priority:high)
Queue:
#15 [frontend] "Other issue" (todo)4. If no issues match → record a beat line (`queue_empty` when zero issues were worked this beat, otherwise `complete` with the running count), apply the ownership rule, exit: "No issues in queue. Heartbeat complete."
Step 4: Resolve Persona
1. Read the issue's labels. Find the persona label by matching against the `personas` map in config. 2. No persona label found → load the persona with `is_default: true` (typically Orchestrator). 3. Load persona files from `.woterclip/<persona.path>/`:
- `SOUL.md` → inject into context as identity instructions
- `TOOLS.md` → inject into context as tool guidance
- `config.yaml` → read runtime settings
The persona's `config.yaml` supplies `model`, `thinking_effort`, `max_turns`, and `enable_chrome`; how each feeds the step 8 dispatch is defined in `${CLAUDE_PLUGIN_ROOT}/references/persona-dispatch.md`.
Step 5: Validate Tools
Read `required_tools` from persona config. Verify each entry by its kind:
- `gh` → verify with `gh auth status` (exit 0) — this proves both the CLI and authentication
- `mcp__*` entries (e.g., `mcp__neon`) → these are MCP tool prefixes, not executables: verify by checking whether any tool starting with that prefix is available in the current session. Never run `command -v` on an `mcp__*` name.
- Other executables (e.g., `docker`) → verify with `command -v <tool>`
**If `gh` itself is unavailable or unauthenticated:** no GitHub mutation is
Read more
name: heartbeat description: This skill should be used when the user asks to "run a heartbeat", "run the agent loop", "process GitHub issues", "check for work", or runs the /heartbeat command. Executes the WoterClip heartbeat — picks up GitHub issues, resolves personas, does work, and reports back. version: 0.1.0
WoterClip Heartbeat
Execute the WoterClip heartbeat cycle: pick up assigned GitHub issues, resolve the right persona, do the work, and report back with structured comments. All GitHub operations go through the `gh` CLI — no MCP server required.
**Arguments:**
- `--dry-run` — Show what would be picked up without doing work
- `--persona <name>` — Only pick issues matching a specific persona
**Reference files** (consult as needed during execution):
- `${CLAUDE_PLUGIN_ROOT}/references/comment-format.md` — Comment templates and rules
- `${CLAUDE_PLUGIN_ROOT}/references/label-conventions.md` — Label lifecycle and atomic label operations
- `${CLAUDE_PLUGIN_ROOT}/references/status-mapping.md` — GitHub state model, sort order, inbox filtering
- `${CLAUDE_PLUGIN_ROOT}/references/sub-issues.md` — Canonical create/attach/verify procedure for sub-issues
- `${CLAUDE_PLUGIN_ROOT}/references/persona-dispatch.md` — Subagent dispatch, outcome contract, and fallback rules
- `${CLAUDE_PLUGIN_ROOT}/references/beat-economics.md` — Clock capture, time ceiling, stop reasons, log fields
All `gh issue` / `gh api` calls below target the repo from config `github.repo` (pass `--repo <owner/name>` explicitly — never rely on the working directory's default remote).
Step 1: Load Config & Lock
1. Read `.woterclip/config.yaml`. If missing, stop and instruct the user to run `/woterclip-init`. Nothing is created yet, so this exit records nothing. 2. Check for lockfile at `.woterclip/.heartbeat-lock`.
- Exists and **less than** `stale_lock_hours` old → stop: "Previous heartbeat still active. Skipping." **This beat never took the lock — do not delete it and do not record a beat line.**
- Exists and **older than** `stale_lock_hours` → delete it, log: "Cleaned stale lockfile."
- No lockfile → proceed.
3. Take the lock. This one command creates it **and prints it**, so the beat observes its own id and start epoch:
printf '{"beat_id":"%s","started_at":"%s","started_epoch":%s}\n' \
"$(date -u +%s)-$$" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$(date -u +%s)" \
| tee .woterclip/.heartbeat-lockCarry the printed `beat_id` and `started_epoch` forward — they are the beat's identity and its clock. 4. **Ownership rule, applied at every exit from here on.** Re-read `.woterclip/.heartbeat-lock`. Delete it **only if it still carries this beat's `beat_id`**; if it is missing or carries another id, a later beat cleaned and re-took it — leave it alone. Deleting a lock this beat does not own hands two beats the same repo. 5. **Every exit from here on** records one beat line (step 9 format) naming that exit's stop reason, then applies the ownership rule. `--dry-run` and a superseded beat record none. The exit-to-reason map is in `${CLAUDE_PLUGIN_ROOT}/references/beat-economics.md`. Beat elapsed is `$(date -u +%s)` minus `started_epoch`.
Check quiet hours: if `quiet_hours.enabled` and current time is within the quiet window:
- `behavior: "skip"` → record a `quiet_hours` beat line, apply the ownership rule, exit: "Quiet hours active. Skipping."
- `behavior: "triage-only"` → proceed but only load Orchestrator persona (skip worker personas in step 3).
Step 2: Check Inbox
1. Fetch open issues assigned to the authenticated user:
gh issue list --repo <owner/name> --assignee @me --state open \
--json number,title,labels,createdAt --limit 1002. Filter and sort client-side per `${CLAUDE_PLUGIN_ROOT}/references/status-mapping.md` § Filter Rules and § Sort Order (labels come from the JSON above). 3. Detect stale `agent-working` labels: if an issue has `agent-working` but no heartbeat comment within `stale_lock_hours`, clean the stale label (`gh issue edit N --remove-label agent-working`, post cleanup comment).
Step 3: Pick Issue
1. If `--persona <name>` flag is set, filter to only issues matching that persona's label. 2. Pick the first issue from the sorted inbox. 3. If `--dry-run`, report what would be picked, then apply the ownership rule and exit. No beat occurred, so record **no** beat line:
Dry run — would pick:
#12 [backend] "Issue title" (in-progress, priority:high)
Queue:
#15 [frontend] "Other issue" (todo)4. If no issues match → record a beat line (`queue_empty` when zero issues were worked this beat, otherwise `complete` with the running count), apply the ownership rule, exit: "No issues in queue. Heartbeat complete."
Step 4: Resolve Persona
1. Read the issue's labels. Find the persona label by matching against the `personas` map in config. 2. No persona label found → load the persona with `is_default: true` (typically Orchestrator). 3. Load persona files from `.woterclip/<persona.path>/`:
- `SOUL.md` → inject into context as identity instructions
- `TOOLS.md` → inject into context as tool guidance
- `config.yaml` → read runtime settings
The persona's `config.yaml` supplies `model`, `thinking_effort`, `max_turns`, and `enable_chrome`; how each feeds the step 8 dispatch is defined in `${CLAUDE_PLUGIN_ROOT}/references/persona-dispatch.md`.
Step 5: Validate Tools
Read `required_tools` from persona config. Verify each entry by its kind:
- `gh` → verify with `gh auth status` (exit 0) — this proves both the CLI and authentication
- `mcp__*` entries (e.g., `mcp__neon`) → these are MCP tool prefixes, not executables: verify by checking whether any tool starting with that prefix is available in the current session. Never run `command -v` on an `mcp__*` name.
- Other executables (e.g., `docker`) → verify with `command -v <tool>`
**If `gh` itself is unavailable or unauthenticated:** no GitHub mutation is
Showing the first part of this file.
GitHub Issues-backed agent orchestration for Claude Code. A single Claude instance wears different "hats" (personas) based on GitHub issue labels – an Orchestrator routes work, a CEO makes strategic calls, and worker personas execute.
Other skills on woterclip.
- /heartbeat-log
This skill should be used when the user asks to "show heartbeat log", "heartbeat history", "what has woterclip done", "show agent activity", "summarize heartbeats", or wants to analyze past heartbeat activity. Parses heartbeat-log.jsonl for summaries and analytics.
Open skill - /init
This skill should be used when the user asks to "initialize woterclip", "set up woterclip", "woterclip init", "configure woterclip for this repo", or runs the /woterclip-init command. Scaffolds a repo with WoterClip config, persona directories, and GitHub labels.
Open skill - /persona-create
This skill should be used when the user asks to "create a persona", "add a new persona", "set up a new agent role", "add a woterclip persona", or runs the /persona-create command. Interactively creates a new WoterClip persona with SOUL.md, TOOLS.md, and config.yaml.
Open skill - /persona-import
This skill should be used when the user asks to "import a paperclip agent", "convert paperclip to woterclip", "migrate from paperclip", "import persona from paperclip", or wants to convert existing Paperclip agent directories into WoterClip persona format.
Open skill - /persona-list
This skill should be used when the user asks to "list personas", "show personas", "what personas are configured", "show woterclip agents", or runs the /persona-list command. Lists all configured WoterClip personas with their runtime settings.
Open skill - /status
This skill should be used when the user asks to "check woterclip status", "show agent status", "what is woterclip doing", "show heartbeat status", "what's in the queue", or runs the /woterclip-status command. Shows current WoterClip state, issue queue, and blocked items.
Open skill

