/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 ↗
npx degit coreyhaines31/makerskills/skills/loopify#main ~/.claude/skills/loopifyChecked ·commit main
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
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 8am0 9 * * 1— Mondays at 9am0 9 * * 5— Fridays at 9am0 */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.txtwith 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
whileloop 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-reviewordaily-startuploop while we're here?" - "Should the loop write to
second-brainoutputs 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 pulsereferences/upstream-check.md— periodic check for changes to an adapted skill's upstreamreferences/vault-compile.md— periodic raw/ → wiki/ compilationreferences/metric-monitor.md— poll a metric until it crosses a threshold, then alert
Composes with
skillify— sibling in-ifytrifecta. Useskillifyto author a new SKILL.md — useloopifywhen the goal is a scheduled task, not a skill.toolify— sibling. Usetoolifyfor adding an integration — useloopifywhen 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 | |
| 2 | name loopify |
| 3 | description 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. |
| 4 | metadata |
| 5 | version 0.2.0 |
| 6 | |
| 7 | |
| 8 | # /loopify — Set up an agent loop |
| 9 | |
| 10 | 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. |
| 11 | |
| 12 | ## Step 0 — Confirm what you're looping |
| 13 | |
| 14 | Ask if not obvious from context: *"What task should this loop do each iteration?"* |
| 15 | |
| 16 | Then 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 | |
| 28 | Three 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 | |
| 34 | Tool: `CronCreate` — schedules a recurring task with a cron expression. |
| 35 | |
| 36 | |
| 37 | CronCreate({ |
| 38 | schedule: "0 8 * * *", // daily at 8am local |
| 39 | prompt: "<loop body prompt>", |
| 40 | timezone: "America/Los_Angeles" |
| 41 | }) |
| 42 | |
| 43 | |
| 44 | Common 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 | |
| 61 | Tool: `ScheduleWakeup` — the current run schedules its own next wake-up. |
| 62 | |
| 63 | |
| 64 | ScheduleWakeup({ |
| 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 | |
| 80 | Never 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 | |
| 92 | Tool: `/loop` (built-in) with an exit condition in the prompt itself. |
| 93 | |
| 94 | |
| 95 | /loop |
| 96 | Check if the deploy is healthy. If yes → stop. If no → wait 5 min and check again. |
| 97 | Max 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 | |
| 107 | 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. |
| 108 | |
| 109 | Idempotency 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 | |
| 116 | Show the user the loop body draft, highlighting the idempotency check. If none exists, add one. |
| 117 | |
| 118 | ## Step 3 — Bail-out condition |
| 119 | |
| 120 | Every 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 | |
| 129 | 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. |
| 130 | |
| 131 | ## Step 4 — Set the schedule |
| 132 | |
| 133 | Based on the pattern from Step 1: |
| 134 | |
| 135 | **Cron (Pattern A):** |
| 136 | |
| 137 | CronCreate({ |
| 138 | schedule: "<expression>", |
| 139 | timezone: "<tz>", |
| 140 | prompt: "<loop body>", |
| 141 | }) |
| 142 | |
| 143 | Report the `cron_id` returned so the user can `CronDelete` later. |
| 144 | |
| 145 | **Dynamic (Pattern B):** |
| 146 | Wrap the loop body prompt so it ends with a `ScheduleWakeup` call: |
| 147 | |
| 148 | <do the work> |
| 149 | Then: ScheduleWakeup({delaySeconds: <tuned per Step 1>, prompt: "<same body>", reason: "<why this cadence>"}) |
| 150 | |
| 151 | |
| 152 | **One-shot (Pattern C):** |
| 153 | Just 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 | |
| 165 | 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. |
| 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 | |
| 173 | Wait 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 | |
| 182 | Report: |
| 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 | |
| 189 | Offer: |
| 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 | |
| 196 | Templates 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
Browse more free Claude skills or everything in Development.