.claude/skills/scheduled-tasks/SKILL.md
Schedule recurring tasks and heartbeat monitoring using Claude Code's cron system (CronCreate/CronList/CronDelete). Implements the Agentic Heartbeat Pattern for ecosystem health monitoring.
npx skillsauth add oimiragieo/agent-studio scheduled-tasksInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Official docs: https://code.claude.com/docs/en/scheduled-tasks
The primary user-facing way to invoke scheduled tasks is via the /loop command. This is the
recommended interface for most users.
/loop <interval> <prompt> # interval-based
/loop every 5m <prompt> # explicit "every" syntax
/loop 2h <prompt> # shorthand hours
/loop at 8:00am <prompt> # time-based (daily at local time)
2m, 5m, 10m, 15m, 30m # minutes
1h, 2h, 4h, 6h, 12h # hours
at 8:00am, at 3:00pm # daily time triggers (local timezone)
/loop 5m Check for new Telegram messages and reply
/loop 2h Distill recent session learnings into .claude/context/memory/learnings.md
/loop at 8:00am Read issues.md and CHANGELOG.md and give morning briefing
/loop every 4h Run pnpm search:code "indexing" to refresh codebase index
/loop 30m Heartbeat: read agent-health.json, check memory sizes, TaskList for stuck tasks
/loop 20m /review-pr 1234 # run a slash command on a schedule
/loop 2h /heartbeat-start # restart heartbeat ecosystem every 2 hours
| Behavior | Detail |
| ---------------------- | -------------------------------------------------------------------------------- |
| Jitter | Tasks fire up to 10% of their period late (max 15 min) — avoids stampedes |
| No catch-up | Missed fires are NOT replayed after sleep/hibernation |
| Session-scoped | Loops stop automatically when the Claude Code session ends |
| Local timezone | at HH:MMam/pm triggers use local time, not UTC |
| Max 50 loops | Hard cap of 50 concurrent scheduled tasks per session |
| 3-day expiry | All loops silently self-delete 3 days after creation (reschedule before day 2.5) |
| Fire-between-turns | Tasks fire only when Claude is idle, not mid-response |
Use /loop to build a self-improving agent ecosystem that runs continuously in the background:
/loop 2h Read .claude/context/memory/issues.md and recent session context. Extract patterns, workarounds, and decisions. Append to learnings.md and decisions.md.
/loop at 3:00am Review learnings.md and test failures. Use agent-updater skill to improve agent .md files. Run pnpm validate:full before finalizing.
/loop at 8:00am Read CHANGELOG.md and issues.md. Summarize technical debt, flaky tests, and top 2 tasks for today.
/loop 15m Check if all tasks are done. If TaskList shows zero in_progress tasks, summarize session to memory and surface readiness for /clear to user.
/loop 4h Run pnpm code:index:reindex to refresh the BM25 and semantic search index.
Use /heartbeat-start to launch all 7 loops in one command. See .claude/commands/heartbeat-start.md.
Provides patterns for scheduling recurring tasks using Claude Code's built-in cron system (CronCreate/CronList/CronDelete). Also implements the Agentic Heartbeat Pattern for keeping the agent ecosystem healthy.
Core distinction:
TaskCreate instead (cron is for recurring work only).crontab -e) or GitHub Actions for persistence.Need a recurring task?
├── NO → Use TaskCreate (one-shot) or Task()
└── YES
├── Must survive terminal close?
│ ├── YES → Use OS cron (crontab -e) or GitHub Actions
│ └── NO (session-scoped is fine)
│ ├── Time-critical (< 15 min precision required)?
│ │ ├── YES → Use OS cron or GitHub Actions
│ │ └── NO → CronCreate is appropriate ✓
│ └── Is it monitoring/heartbeat?
│ └── YES → CronCreate is ideal ✓
Task or TaskCreate)CRITICAL — Session-Scoped Loops: All
CronCreateloops are registered to the current Claude Code session and are permanently lost when the session ends (terminal close,/clear, or session restart). After any session restart, all loops must be re-registered manually.heartbeat-orchestrator handles this automatically. When the heartbeat ecosystem is running,
heartbeat-orchestratoris responsible for detecting missing loops after session restarts and re-registering them. If you are not usingheartbeat-orchestrator, you must re-register all loops yourself at the start of each new session.To check which loops are active:
CronList(). If loops are missing after a restart, re-run the setup commands or invoke/heartbeat-start.
// Schedule a recurring task
CronCreate({ schedule: '*/30 * * * *', task: 'heartbeat check prompt' });
// List active cron tasks
CronList();
// Remove a scheduled task
CronDelete({ id: 'abc12345' });
| Constraint | Detail |
| ---------------------- | ------------------------------------------------------------------------------------------- |
| Session-scoped | All cron tasks die when the terminal closes — no persistence across restarts |
| 50-task cap | Maximum 50 concurrent scheduled tasks per session |
| 3-day auto-expiry | Recurring tasks self-delete 3 days after creation (SILENT — must reschedule before day 2.5) |
| Fire-between-turns | Tasks fire only when Claude is idle, not mid-response |
| No catch-up | Missed fires are NOT replayed — task fires once at next idle opportunity |
| Jitter | Tasks fire up to 10% of their period late (max 15 min) to avoid API stampedes |
| Timezone | All cron expressions use local system timezone, NOT UTC |
| No extended syntax | L, W, ?, name aliases like MON are NOT supported |
Standard 5-field syntax: minute hour day-of-month month day-of-week
| Expression | Meaning |
| -------------- | ------------------------ |
| */30 * * * * | Every 30 minutes |
| */5 * * * * | Every 5 minutes |
| 0 * * * * | Every hour on the hour |
| 0 2 * * * | Every day at 2am local |
| 0 3 * * 0 | Every Sunday at 3am |
| 0 6 * * 1-5 | Weekdays at 6am |
| 0 0 */2 * * | Every 2 days at midnight |
Cheap file reads FIRST, LLM only when needed. Achieve 60-80% cost reduction for healthy-state ticks.
Execution model:
HEARTBEAT.md checklist (cheap file read — no LLM call)HEARTBEAT_OK immediately, no LLM invocationagent-health.json, task list) — still no LLMHEARTBEAT_OKCheck these signals on each heartbeat tick (in order — cheapest first):
agent-health.json — any agents status: degraded?learnings.md > 40KB? Is decisions.md > 80KB?TaskList() — any tasks in in_progress for > 2 hours?errorRate > 0.05 in agent-health.json?reflection-reminder.txt exist and is stalled?If ALL healthy → return HEARTBEAT_OK (no further LLM invocation needed)
If ANY unhealthy → spawn appropriate agent to fix, then report
Create HEARTBEAT.md in the project root as standing instructions for the heartbeat:
# Agent Studio Heartbeat Checklist
## Every Tick (30 minutes)
### Memory Health
- [ ] learnings.md > 40KB? → trigger rotation via node .claude/lib/memory/memory-rotator.cjs
- [ ] decisions.md > 80KB? → warn user
- [ ] issues.md has unresolved P0 items? → surface to user
### Agent Registry Health
- [ ] agent-health.json has any status: degraded? → alert
- [ ] Any hooks have errorRate > 5%? → alert with hook name
### Task Health
- [ ] Any tasks stuck in in_progress for > 2 hours? → surface task IDs
### Reflection Queue
- [ ] reflection-reminder.txt exists? → alert (reflection queue stalled)
## Self-Maintenance
- [ ] This heartbeat task older than 2.5 days? → reschedule before 3-day expiry
CronCreate({
schedule: '0 2 * * *',
task: 'Rebuild BM25 search index for code discovery. Run: cd /project && pnpm code:index:reindex',
});
CronCreate({
schedule: '0 3 * * 0',
task: 'Check memory file sizes and rotate if needed. Run node .claude/lib/memory/memory-rotator.cjs and report results.',
});
CronCreate({
schedule: '0 6 * * *',
task: 'Regenerate agent registry: pnpm agents:registry and verify 72 agents are registered.',
});
CronCreate({
schedule: '*/30 * * * *',
task: 'Run heartbeat check: Read HEARTBEAT.md checklist, check agent-health.json, memory file sizes, and stuck tasks. Reply HEARTBEAT_OK if all healthy, otherwise describe issues found.',
});
CronCreate({
schedule: '0 0 */2 * *',
task: 'Re-register all scheduled tasks to prevent 3-day auto-expiry. Call CronList() to check what tasks exist, then recreate any that are missing or older than 2.5 days.',
});
When replacing a running cron task with updated settings, ALWAYS use CronCreate FIRST, then CronDelete. This prevents a scheduling gap where no cron task exists between delete and create.
// CORRECT ORDER: Create new before deleting old to prevent scheduling gap
// Step 1: Create the new task FIRST (new task is now running)
CronCreate({
schedule: '*/30 * * * *',
task: 'Updated heartbeat prompt with new checks.',
});
// Step 2: THEN delete the old task (old ID obtained from CronList())
// There is never a window where no heartbeat cron exists
CronDelete({ id: 'old-task-id-from-cronlist' });
// WRONG ORDER (creates a scheduling gap — do NOT do this):
// CronDelete({ id: "old-id" }); // <-- gap begins here
// CronCreate({ ... }); // <-- gap ends here (tasks missed during gap)
CronCreate({
schedule: '0 3 * * 0',
task: 'Run full framework validation: pnpm validate:full and report any errors found.',
});
| Infrastructure | Integration Method | Heartbeat Action |
| -------------------- | ------------------------------------------------------- | ------------------------------------ |
| agent-health.json | Read tool → JSON parse | Check status, errorRate per hook |
| learnings.md | Bash: wc -c or Read + check size | Alert if > 40KB threshold |
| decisions.md | Bash: wc -c or Read + check size | Alert if > 80KB threshold |
| Task system | TaskList() | Surface stuck in_progress tasks |
| Memory rotator | Bash: node .claude/lib/memory/memory-rotator.cjs | Trigger when threshold exceeded |
| BM25 index | Check index mtime | Rebuild if mtime > 24h |
| pnpm validate:full | Bash | Weekly validation, surface errors |
| Reflection queue | Read: .claude/context/runtime/reflection-reminder.txt | Alert if reflection queue stalled |
| Control | Purpose | Recommendation |
| ------------------------- | ----------------------------------- | ------------------------- |
| Cheap checks first | Read files before invoking LLM | Always check files first |
| HEARTBEAT_OK early exit | No LLM when all healthy | Saves 60-80% API costs |
| haiku model | Cheaper model for status-only runs | Use haiku for heartbeats |
| lightContext | Only HEARTBEAT.md, not full session | Reduces context per tick |
| 30m default interval | OpenClaw proven default | Don't go below 15 minutes |
Task/TaskCreate instead)L, W, ?, named days) — not supportedSkill({ skill: 'scheduled-tasks' });
// 1. Heartbeat every 30 minutes
CronCreate({
schedule: '*/30 * * * *',
task: 'Heartbeat: Read .claude/context/memory/agent-health.json, check learnings.md size, run TaskList() for stuck tasks. Reply HEARTBEAT_OK if all healthy.',
});
// 2. Auto-reschedule every 2 days (prevents 3-day expiry)
CronCreate({
schedule: '0 0 */2 * *',
task: 'Self-maintenance: CronList() to check active tasks, recreate any missing scheduled tasks.',
});
// 3. Nightly index rebuild
CronCreate({
schedule: '0 2 * * *',
task: 'Rebuild search index: pnpm code:index:reindex',
});
CronList(); // Verify all expected tasks are registered
// If missing, re-register (3-day expiry may have cleared them silently)
CronList(); // Get task IDs
CronDelete({ id: 'abc12345' }); // Cancel by ID
For tasks that must survive terminal close:
schedule trigger: fully unattended, cloud-run, no terminal requiredcrontab -e): persistent, runs independently of Claude CodeUse Claude Code's CronCreate for session-scoped monitoring only. Use OS-level scheduling for critical production tasks.
| Anti-Pattern | Why It Fails | Correct Approach |
| ------------------------------ | ------------------------------------- | ----------------------------------------------- |
| No auto-reschedule task | 3-day expiry silently kills heartbeat | Add 0 0 */2 * * reschedule cron |
| Heartbeat interval < 5 minutes | API stampede risk, burns token budget | Use 30 min default; minimum 5 min |
| LLM invocation on every tick | 100% API cost with no savings | Cheap checks first, LLM only when needed |
| Mixing heartbeat + maintenance | One failure breaks both | Separate cron tasks per concern |
| Extended cron syntax | Not supported — silent failure | Use standard 5-field syntax only |
| Assuming session persistence | Tasks die on terminal close | Document limitation; use OS cron for durability |
Before starting:
Read .claude/context/memory/learnings.md
After completing:
.claude/context/memory/learnings.md.claude/context/memory/issues.md.claude/context/memory/decisions.mdASSUME INTERRUPTION: If it's not in memory, it didn't happen.
tools
Comprehensive biosignal processing toolkit for analyzing physiological data including ECG, EEG, EDA, RSP, PPG, EMG, and EOG signals. Use this skill when processing cardiovascular signals, brain activity, electrodermal responses, respiratory patterns, muscle activity, or eye movements. Applicable for heart rate variability analysis, event-related potentials, complexity measures, autonomic nervous system assessment, psychophysiology research, and multi-modal physiological signal integration.
tools
Comprehensive toolkit for creating, analyzing, and visualizing complex networks and graphs in Python. Use when working with network/graph data structures, analyzing relationships between entities, computing graph algorithms (shortest paths, centrality, clustering), detecting communities, generating synthetic networks, or visualizing network topologies. Applicable to social networks, biological networks, transportation systems, citation networks, and any domain involving pairwise relationships.
data-ai
Molecular featurization for ML (100+ featurizers). ECFP, MACCS, descriptors, pretrained models (ChemBERTa), convert SMILES to features, for QSAR and molecular ML.
development
Run Python code in the cloud with serverless containers, GPUs, and autoscaling. Use when deploying ML models, running batch processing jobs, scheduling compute-intensive tasks, or serving APIs that require GPU acceleration or dynamic scaling.