/loopify — Set up an agent loop skill

When you want to set up an agent loop, cron-scheduled task, or recurring workflow that runs autonomously.

by coreyhaines31·MIT license·★ 855 Stars on the repo·GitHub ↗

Use now

Inside this skill

SKILL.md · the author's own words
# /loopify — Set up an agent loop

Wizard for going from *"this task should run periodically"* to a working loop with the right pacing, idempotency, and bail-out. Reference (…

## Step 0 — Confirm what you're looping

Ask if not obvious from context: *"What task should this loop do each iteration?"*

Then get the essentials:

| Question | Why it matters |
|---|---|
| **How often?** | Determines cron vs dynamic vs one-shot |
| **When to stop?** | Bail-out condition — loops must have one |
| **What's the loop body doing?** | Determines idempotency requirements |
| **Where does output go?** | File / notification / commit / nothing |

Word-for-word excerpt, not a result. Read the full SKILL.md ↓

Files of /loopify — Set up an agent loop

coreyhaines31/main1 file shown
SKILL.md
Show the full text221 lines

/loopify — Set up an agent loop

Wizard for going from "this task should run periodically" to a working loop with the right pacing, idempotency, and bail-out. Reference (Claude Code): ScheduleWakeup (dynamic pacing), CronCreate (fixed schedule), and the built-in /loop (dynamic self-paced re-entry). Other hosts: see "On hosts other than Claude Code" in Step 4.

Step 0 — Confirm what you're looping

Ask if not obvious from context: "What task should this loop do each iteration?"

Then get the essentials:

Question Why it matters
How often? Determines cron vs dynamic vs one-shot
When to stop? Bail-out condition — loops must have one
What's the loop body doing? Determines idempotency requirements
Where does output go? File / notification / commit / nothing
What's the failure mode if it runs twice? Idempotency validation

Step 1 — Pick the pattern

Three primary patterns. Route by the answer to "how often":

Pattern A — Cron (fixed schedule)

Use when: task runs at predictable intervals — daily at 8am, weekly on Fridays, hourly on the hour.

Tool: CronCreate — schedules a recurring task with a cron expression.

CronCreate({
  schedule: "0 8 * * *",           // daily at 8am local
  prompt: "<loop body prompt>",
  timezone: "America/Los_Angeles"
})

Common cron patterns:

  • 0 8 * * * — daily at 8am
  • 0 9 * * 1 — Mondays at 9am
  • 0 9 * * 5 — Fridays at 9am
  • 0 */2 * * * — every 2 hours
  • */15 * * * * — every 15 minutes

Trade-offs:

  • ✅ Predictable, human-readable, easy to reason about
  • ✅ Best for time-of-day-dependent tasks (morning brief, EOD summary)
  • ❌ Runs at the scheduled time even if the last run isn't done — need idempotent body
  • ❌ No self-pacing — over-schedules if the task duration varies wildly
Pattern B — Dynamic pacing (self-scheduled)

Use when: task should react to state, not the clock. Monitor-until-condition-met patterns. Waiting on an external event.

Tool: ScheduleWakeup — the current run schedules its own next wake-up.

ScheduleWakeup({
  delaySeconds: 270,               // stay in cache window (< 5min)
  reason: "checking build status; sleeping under 5min to stay cache-warm",
  prompt: "<same task, re-entered>"
})

Critical delay rules (from ScheduleWakeup docs — internalized in the wizard):

Delay range Use for Cache impact
60s–270s Active work — polling build, waiting for state that's about to change Stays in 5-min prompt cache — fast + cheap
300s ❌ DON'T USE THIS Worst of both worlds — pay cache miss without amortizing
300s–3600s Waiting on something that takes minutes to change Pay cache miss but justified
1200s–1800s (20–30 min) Idle ticks with no specific signal Default for autonomous loops

Never pick 300s literally — either drop to 270 (cache stays warm) or commit to 1200+ (cache miss buys longer wait).

Trade-offs:

  • ✅ Adaptive — sleeps longer when idle, shorter when active
  • ✅ Cache-optimal when tuned right
  • ❌ Requires the loop body to know when to schedule next (extra logic)
  • ❌ Harder to reason about when it'll run
Pattern C — One-shot loop (until-condition)

Use when: task runs until a condition is met, then stops. No recurrence after that.

Tool: /loop (built-in) with an exit condition in the prompt itself.

/loop
Check if the deploy is healthy. If yes → stop. If no → wait 5 min and check again.
Max 10 iterations. If still failing after 10, alert and stop.

Trade-offs:

  • ✅ Simplest for check-until-condition
  • ✅ Bounded — always eventually terminates
  • ❌ Not for indefinite recurrence — that's Pattern A or B

Step 2 — Design the loop body for idempotency

Idempotent = running the loop twice produces the same result as running it once. Non-negotiable for cron and dynamic patterns because they'll fire while the previous iteration is still running or partially complete.

Idempotency patterns:

  • Use "already done" markers: e.g., commit a state file <vault>/.loopify/<name>-last-run.txt with the timestamp of last successful run. Loop body checks the timestamp before doing work.
  • Use dedupe keys: if the loop writes to a DB or file, key by content-hash or timestamp so re-runs are no-ops.
  • Use transactions: DB writes in the loop body should be atomic — either all commit or all roll back.
  • Query before mutate: check current state before applying the change. If already applied, skip.

Show the user the loop body draft, highlighting the idempotency check. If none exists, add one.

Step 3 — Bail-out condition

Every loop needs one. Options:

Bail-out When to use
Max iterations (e.g., stop after 100 runs) Cron loops — prevents runaway
State-based (e.g., stop when metric X drops below Y) Monitoring loops
Time-based (e.g., stop after 24 hours) Bounded monitoring
Error-based (e.g., stop on 3 consecutive failures) All loops — catches degradation

If the loop is truly indefinite (e.g., a weekly cron with no end), still add a manual bail-out via CronDelete. Document it in the SKILL/loop notes so the user knows how to stop it.

Step 4 — Set the schedule

Based on the pattern from Step 1:

Cron (Pattern A):

CronCreate({
  schedule: "<expression>",
  timezone: "<tz>",
  prompt: "<loop body>",
})

Report the cron_id returned so the user can CronDelete later.

Dynamic (Pattern B): Wrap the loop body prompt so it ends with a ScheduleWakeup call:

<do the work>
Then: ScheduleWakeup({delaySeconds: <tuned per Step 1>, prompt: "<same body>", reason: "<why this cadence>"})

One-shot (Pattern C): Just run /loop <prompt with exit condition>.

On hosts other than Claude Code

CronCreate, ScheduleWakeup, and /loop are Claude Code features. Everything else in this skill (pattern choice, idempotency, bail-out, verification) applies to any agent. On other hosts, the scheduler lives outside the agent and calls a non-interactive agent run:

Scheduler When to use
The host's own scheduled tasks, if it has them First choice. Check the host's docs
System cron / launchd / systemd timer Machine is always on; loop needs local files
GitHub Actions schedule: Loop works on a repo (vault compile, upstream check); survives a closed laptop

The job runs a headless agent command with the loop body as the prompt, e.g. claude -p "<body>", codex exec "<body>", or cursor-agent -p "<body>". Use whatever non-interactive mode your agent's CLI has.

  • Dynamic pacing (Pattern B) has no direct equivalent. Use a fixed schedule whose body exits early when there's nothing to do.
  • One-shot (Pattern C) becomes a shell while loop around the headless command, with the bail-out check in the loop condition.
  • Headless runs can't ask for approval, so give the job the narrowest permissions that work, and have it write output to a file or notification rather than waiting on a prompt.

Step 5 — Verify the first run

Wait for the first iteration (or trigger it manually via /loop with the same prompt for a dry-run). Confirm:

  • Output landed where expected
  • Idempotency check works (run twice — second should be a no-op)
  • Bail-out condition would fire correctly if triggered
  • Log/notification appears if configured

Step 6 — Report + follow-ups

Report:

  • Pattern picked (A/B/C) + why
  • Cron ID or wakeup pattern registered
  • Bail-out condition set
  • Idempotency mechanism in place
  • How to stop the loop (CronDelete <cron_id> or "just don't call the wakeup" for dynamic)

Offer:

  • "Save this loop configuration as a skill via skillify from-chat?"
  • "Want to also register a weekly-review or daily-startup loop while we're here?"
  • "Should the loop write to second-brain outputs when it runs?"

Common loop recipes

Templates for frequent loop types (fill in as they're used):

  • references/daily-brief.md — morning routine loop (calendar + priorities + overnight)
  • references/weekly-review.md — Friday portfolio pulse
  • references/upstream-check.md — periodic check for changes to an adapted skill's upstream
  • references/vault-compile.md — periodic raw/ → wiki/ compilation
  • references/metric-monitor.md — poll a metric until it crosses a threshold, then alert

Composes with

  • skillify — sibling in -ify trifecta. Use skillify to author a new SKILL.md — use loopify when the goal is a scheduled task, not a skill.
  • toolify — sibling. Use toolify for adding an integration — use loopify when the goal is running something on top of an already-integrated tool on a schedule.
  • second-brain — many loops write to the vault (raw/ or outputs/). The vault auto-commit pattern applies.
  • pm — daily-brief and weekly-review loops often read from pm before generating output.

Notes on quality

  • Never pick 300s for delaySeconds. Worst of both worlds. Drop to 270 or commit to 1200+.
  • Every loop needs a bail-out. Even indefinite ones need a documented manual stop.
  • Idempotency is non-negotiable for cron + dynamic. Assume the loop will fire twice while a previous iteration is running.
  • Prefer dynamic pacing over over-frequent cron. Cron every 15 min wastes tokens if the work isn't ready; dynamic pacing scales down when idle.
  • Document the cron_id. Otherwise the loop is orphaned and hard to stop.
  • Log every iteration briefly — even a single line ("2026-06-30 08:00 daily-brief: ran, 3 items") makes debugging drift trivial.
  • Loops that touch external APIs need rate-limit respect. If the vendor has a 100/day limit, don't schedule 500/day.
  • Bounded > unbounded when uncertain. If unsure whether to run for a week or a month, start with a week — extend after seeing it work.
1---
2name: loopify
3description: When you want to set up an agent loop, cron-scheduled task, or recurring workflow that runs autonomously. Judgment layer on top of Claude Code's ScheduleWakeup, CronCreate, and /loop — or, on other hosts, cron/GitHub Actions plus a headless agent CLI. Picks dynamic pacing, fixed cron, or a one-shot loop; designs idempotent loop bodies; sets bail-out conditions so loops don't run forever. Examples — weekly review pulse, daily brief, hourly metric monitor, periodic vault compile, upstream-check for an adapted skill. Triggers on "/loopify," "set up a loop," "schedule this task," "run this daily," "run this weekly," "cron this," "make this recurring," "automate this on a schedule," "keep this running until X." Part of the -ify trifecta (skillify / toolify / loopify). NOT for authoring a new skill — that's skillify. NOT for adding a tool/integration — that's toolify.
4metadata:
5 version: 0.2.0
6---
7 
8# /loopify — Set up an agent loop
9 
10Wizard for going from *"this task should run periodically"* to a working loop with the right pacing, idempotency, and bail-out. Reference (Claude Code): `ScheduleWakeup` (dynamic pacing), `CronCreate` (fixed schedule), and the built-in `/loop` (dynamic self-paced re-entry). Other hosts: see "On hosts other than Claude Code" in Step 4.
11 
12## Step 0 — Confirm what you're looping
13 
14Ask if not obvious from context: *"What task should this loop do each iteration?"*
15 
16Then get the essentials:
17 
18| Question | Why it matters |
19|---|---|
20| **How often?** | Determines cron vs dynamic vs one-shot |
21| **When to stop?** | Bail-out condition — loops must have one |
22| **What's the loop body doing?** | Determines idempotency requirements |
23| **Where does output go?** | File / notification / commit / nothing |
24| **What's the failure mode if it runs twice?** | Idempotency validation |
25 
26## Step 1 — Pick the pattern
27 
28Three primary patterns. Route by the answer to "how often":
29 
30### Pattern A — Cron (fixed schedule)
31 
32**Use when**: task runs at predictable intervals — daily at 8am, weekly on Fridays, hourly on the hour.
33 
34Tool: `CronCreate` — schedules a recurring task with a cron expression.
35 
36```
37CronCreate({
38 schedule: "0 8 * * *", // daily at 8am local
39 prompt: "<loop body prompt>",
40 timezone: "America/Los_Angeles"
41})
42```
43 
44Common cron patterns:
45- `0 8 * * *` — daily at 8am
46- `0 9 * * 1` — Mondays at 9am
47- `0 9 * * 5` — Fridays at 9am
48- `0 */2 * * *` — every 2 hours
49- `*/15 * * * *` — every 15 minutes
50 
51**Trade-offs:**
52- ✅ Predictable, human-readable, easy to reason about
53- ✅ Best for time-of-day-dependent tasks (morning brief, EOD summary)
54- ❌ Runs at the scheduled time even if the last run isn't done — need idempotent body
55- ❌ No self-pacing — over-schedules if the task duration varies wildly
56 
57### Pattern B — Dynamic pacing (self-scheduled)
58 
59**Use when**: task should react to state, not the clock. Monitor-until-condition-met patterns. Waiting on an external event.
60 
61Tool: `ScheduleWakeup` — the current run schedules its own next wake-up.
62 
63```
64ScheduleWakeup({
65 delaySeconds: 270, // stay in cache window (< 5min)
66 reason: "checking build status; sleeping under 5min to stay cache-warm",
67 prompt: "<same task, re-entered>"
68})
69```
70 
71**Critical delay rules** (from `ScheduleWakeup` docs — internalized in the wizard):
72 
73| Delay range | Use for | Cache impact |
74|---|---|---|
75| **60s–270s** | Active work — polling build, waiting for state that's about to change | Stays in 5-min prompt cache — fast + cheap |
76| **300s** ❌ | **DON'T USE THIS** | Worst of both worlds — pay cache miss without amortizing |
77| **300s–3600s** | Waiting on something that takes minutes to change | Pay cache miss but justified |
78| **1200s–1800s** (20–30 min) | Idle ticks with no specific signal | Default for autonomous loops |
79 
80Never pick 300s literally — either drop to 270 (cache stays warm) or commit to 1200+ (cache miss buys longer wait).
81 
82**Trade-offs:**
83- ✅ Adaptive — sleeps longer when idle, shorter when active
84- ✅ Cache-optimal when tuned right
85- ❌ Requires the loop body to know when to schedule next (extra logic)
86- ❌ Harder to reason about when it'll run
87 
88### Pattern C — One-shot loop (until-condition)
89 
90**Use when**: task runs until a condition is met, then stops. No recurrence after that.
91 
92Tool: `/loop` (built-in) with an exit condition in the prompt itself.
93 
94```
95/loop
96Check if the deploy is healthy. If yes → stop. If no → wait 5 min and check again.
97Max 10 iterations. If still failing after 10, alert and stop.
98```
99 
100**Trade-offs:**
101- ✅ Simplest for check-until-condition
102- ✅ Bounded — always eventually terminates
103- ❌ Not for indefinite recurrence — that's Pattern A or B
104 
105## Step 2 — Design the loop body for idempotency
106 
107Idempotent = running the loop twice produces the same result as running it once. **Non-negotiable for cron and dynamic patterns** because they'll fire while the previous iteration is still running or partially complete.
108 
109Idempotency patterns:
110 
111- **Use "already done" markers**: e.g., commit a state file `<vault>/.loopify/<name>-last-run.txt` with the timestamp of last successful run. Loop body checks the timestamp before doing work.
112- **Use dedupe keys**: if the loop writes to a DB or file, key by content-hash or timestamp so re-runs are no-ops.
113- **Use transactions**: DB writes in the loop body should be atomic — either all commit or all roll back.
114- **Query before mutate**: check current state before applying the change. If already applied, skip.
115 
116Show the user the loop body draft, highlighting the idempotency check. If none exists, add one.
117 
118## Step 3 — Bail-out condition
119 
120Every loop needs one. Options:
121 
122| Bail-out | When to use |
123|---|---|
124| **Max iterations** (e.g., stop after 100 runs) | Cron loops — prevents runaway |
125| **State-based** (e.g., stop when metric X drops below Y) | Monitoring loops |
126| **Time-based** (e.g., stop after 24 hours) | Bounded monitoring |
127| **Error-based** (e.g., stop on 3 consecutive failures) | All loops — catches degradation |
128 
129If the loop is truly indefinite (e.g., a weekly cron with no end), still add a manual bail-out via `CronDelete`. Document it in the SKILL/loop notes so the user knows how to stop it.
130 
131## Step 4 — Set the schedule
132 
133Based on the pattern from Step 1:
134 
135**Cron (Pattern A):**
136```
137CronCreate({
138 schedule: "<expression>",
139 timezone: "<tz>",
140 prompt: "<loop body>",
141})
142```
143Report the `cron_id` returned so the user can `CronDelete` later.
144 
145**Dynamic (Pattern B):**
146Wrap the loop body prompt so it ends with a `ScheduleWakeup` call:
147```
148<do the work>
149Then: ScheduleWakeup({delaySeconds: <tuned per Step 1>, prompt: "<same body>", reason: "<why this cadence>"})
150```
151 
152**One-shot (Pattern C):**
153Just run `/loop <prompt with exit condition>`.
154 
155### On hosts other than Claude Code
156 
157`CronCreate`, `ScheduleWakeup`, and `/loop` are Claude Code features. Everything else in this skill (pattern choice, idempotency, bail-out, verification) applies to any agent. On other hosts, the scheduler lives outside the agent and calls a non-interactive agent run:
158 
159| Scheduler | When to use |
160|---|---|
161| **The host's own scheduled tasks**, if it has them | First choice. Check the host's docs |
162| **System cron / launchd / systemd timer** | Machine is always on; loop needs local files |
163| **GitHub Actions `schedule:`** | Loop works on a repo (vault compile, upstream check); survives a closed laptop |
164 
165The job runs a headless agent command with the loop body as the prompt, e.g. `claude -p "<body>"`, `codex exec "<body>"`, or `cursor-agent -p "<body>"`. Use whatever non-interactive mode your agent's CLI has.
166 
167- Dynamic pacing (Pattern B) has no direct equivalent. Use a fixed schedule whose body exits early when there's nothing to do.
168- One-shot (Pattern C) becomes a shell `while` loop around the headless command, with the bail-out check in the loop condition.
169- Headless runs can't ask for approval, so give the job the narrowest permissions that work, and have it write output to a file or notification rather than waiting on a prompt.
170 
171## Step 5 — Verify the first run
172 
173Wait for the first iteration (or trigger it manually via `/loop` with the same prompt for a dry-run). Confirm:
174 
175- Output landed where expected
176- Idempotency check works (run twice — second should be a no-op)
177- Bail-out condition would fire correctly if triggered
178- Log/notification appears if configured
179 
180## Step 6 — Report + follow-ups
181 
182Report:
183- Pattern picked (A/B/C) + why
184- Cron ID or wakeup pattern registered
185- Bail-out condition set
186- Idempotency mechanism in place
187- How to stop the loop (`CronDelete <cron_id>` or "just don't call the wakeup" for dynamic)
188 
189Offer:
190- *"Save this loop configuration as a skill via `skillify from-chat`?"*
191- *"Want to also register a `weekly-review` or `daily-startup` loop while we're here?"*
192- *"Should the loop write to `second-brain` outputs when it runs?"*
193 
194## Common loop recipes
195 
196Templates for frequent loop types (fill in as they're used):
197 
198- `references/daily-brief.md` — morning routine loop (calendar + priorities + overnight)
199- `references/weekly-review.md` — Friday portfolio pulse
200- `references/upstream-check.md` — periodic check for changes to an adapted skill's upstream
201- `references/vault-compile.md` — periodic raw/ → wiki/ compilation
202- `references/metric-monitor.md` — poll a metric until it crosses a threshold, then alert
203 
204## Composes with
205 
206- **`skillify`** — sibling in `-ify` trifecta. Use `skillify` to author a new SKILL.md — use `loopify` when the goal is a scheduled task, not a skill.
207- **`toolify`** — sibling. Use `toolify` for adding an integration — use `loopify` when the goal is running something on top of an already-integrated tool on a schedule.
208- **`second-brain`** — many loops write to the vault (raw/ or outputs/). The vault auto-commit pattern applies.
209- **`pm`** — daily-brief and weekly-review loops often read from pm before generating output.
210 
211## Notes on quality
212 
213- **Never pick 300s for `delaySeconds`.** Worst of both worlds. Drop to 270 or commit to 1200+.
214- **Every loop needs a bail-out.** Even indefinite ones need a documented manual stop.
215- **Idempotency is non-negotiable for cron + dynamic.** Assume the loop will fire twice while a previous iteration is running.
216- **Prefer dynamic pacing over over-frequent cron.** Cron every 15 min wastes tokens if the work isn't ready; dynamic pacing scales down when idle.
217- **Document the cron_id.** Otherwise the loop is orphaned and hard to stop.
218- **Log every iteration briefly** — even a single line ("2026-06-30 08:00 daily-brief: ran, 3 items") makes debugging drift trivial.
219- **Loops that touch external APIs need rate-limit respect.** If the vendor has a 100/day limit, don't schedule 500/day.
220- **Bounded > unbounded when uncertain.** If unsure whether to run for a week or a month, start with a week — extend after seeing it work.
221 

Discussion

Alternatives

Advisor orchestrator workerUse when a task is too large for one model pass, needs parallel research or generation across many subtasks (like researching a dozen competitors at once), or the user asks to orchestrate multiple models, split work across a model team, run an advisor-worker loop, have a stronger model review the plan while cheap workers execute, or says "too big for one model" or "fan this out". Not for single-file edits or tasks one model handles in one pass.Coding · Apache-2.0CCS Align — Worker Watch seat (Phases 0–2 shipped; Phase 3 = verify / sign-off)Run the CCS Align seat's hourly breathing cycle — prove the local claude-mem worker is healthy, pull needle observations through search → timeline → get_observations, land them in a seat-owned middle cache via atomic grab → append → filter exclude-marks → replace, manage exclude marks, and walk house → project → seat rules to detect conflicts (SHADOW_HOUSE, DENY_ALLOW, DRIFT, CLOCK_HEADER) with an append-only rules-report.md. Use when asked to run CCS Align, breathe the alignment seat, refresh the middle cache, exclude or restore an observation, walk rules, check rules conflicts, or check the Worker Watch board.Coding · Apache-2.0Catchup — Async-Team Return BriefingSummarize and review what changed while you were away. Use after a weekend, vacation, or flight to check missed PRs, git commits, Linear tickets, and meetings — one prioritized brief, not a firehose.Coding · MITAsync python patternsMaster Python asyncio, concurrent programming, and async/await patterns for high-performance applications. Use when building async APIs, concurrent systems, or I/O-bound applications requiring non-blocking operations.Coding · MIT