/loop-from-task — turn a TaskList item into a self-terminating loop
Why this exists
Composing the existing primitives:
TaskList is the durable cross-fire state per CLAUDE.md
/loop schedules cron-driven fires
- The natural pattern "keep working on task X until it's done" needs
glue between them — that's this skill
A loop created via /loop-from-task carries a self-terminating prompt:
each fire checks the task's status, continues work if in_progress
or pending, and /loop-stops itself once completed.
Argument parsing
/loop-from-task <task-id> <interval>
/loop-from-task <task-id> <interval> notes: "<additional context>"
<task-id> — TaskList ID (e.g. 3). Required.
<interval> — same syntax as /loop (5m, 1h, 30s, 2d).
notes: — optional additional context appended to the generated
prompt (e.g. "stop at 5pm", "PR is blocked on review").
If either positional arg is missing: show TaskList, ask which to
loop on and at what cadence.
Steps
Verify the task exists. Call TaskList. If <task-id> isn't
present, error out with the current list.
Verify the task isn't already completed. If status == completed,
tell user "task #N is already done — nothing to loop on" and stop.
Verify no existing loop already wraps this task. Check
CronList + ~/.claude/.paused-loops.json for any prompt containing
loop-from-task: task-<id> (we tag the generated prompt). If found,
tell user, don't duplicate.
Convert interval to cron (same table as /loop):
| Pattern |
Cron expression |
Nm (≤59) |
*/N * * * * |
Nm (≥60) |
0 */H * * * (H = N/60) |
Nh |
0 */N * * * |
Nd |
0 0 */N * * |
Ns |
round up to 1m |
Generate the loop prompt. Template:
/loop-from-task: task-<id>
SELF-CHECK FIRST: call TaskList. Find task #<id> ("<subject>").
If task status == completed:
→ /loop-stop this loop (cron id <self-id-or-placeholder>). Then post a one-line "task #<id> done; loop terminated" and exit.
If task status == in_progress:
→ Continue working on it. Don't restart. Don't fragment progress.
If task status == pending:
→ TaskUpdate status=in_progress, then work on it.
If task is blocked (other tasks blockedBy points to it):
→ Note the blockers in this fire's output; do NOT mark in_progress; wait.
<NOTES BLOCK IF PROVIDED>
Canary: if you canceled or stopped this loop, you should NOT be seeing this message.
The cron-self-id will be substituted in step 6 after CronCreate
returns the actual ID (use <self> as placeholder, then patch
the prompt with the real ID via CronDelete + CronCreate cycle —
OR accept that the placeholder remains and the agent uses /loops
to find its own ID at fire time).
Simpler alternative: don't try to embed the loop's own ID;
the agent at fire time uses /loops or CronList to discover
the cron carrying the matching /loop-from-task: task-<id> tag
and stops THAT specific cron. This is what the skill recommends.
CronCreate with the cron expression + prompt + recurring=true.
Append history event:
{"event":"created","at":"...Z","id":"<new-cron-id>","cron":"...","tag":"loop-from-task","task_id":"<task-id>","label":"task-<id>: <subject first 40 chars>"}
TaskUpdate the task: add metadata loop_cron_id: <new-cron-id>
so future TaskList views show "this task is being looped on by
cron X."
Don't execute the prompt now. The first cron fire (in ≤N
minutes) is when work begins. This differs from raw /loop which
fires immediately — for task-driven loops, we want one clean
work-cycle window, not double-firing.
Confirm to user:
Loop started: cron <id> fires every , working on
task # "" until completed. Use /loop-stop
to cancel manually, /loops to monitor, or just mark the
task completed and the loop self-terminates on next fire.
Don't
- Don't auto-mark the task
in_progress at create time. Let the
first cron fire decide based on task state.
- Don't refuse if the task is
pending — that's the common case.
- Don't try to be smart about multi-task loops. One task per loop;
user creates multiple
/loop-from-task invocations if they want
parallel work.
- Don't ignore the
notes: field — it's how the user injects
per-task constraints the generic prompt can't know.
See also
/loop — the lower-level cron primitive
/loop-stop — manual cancellation (loop-from-task loops also
self-terminate, but manual stop still works)
/loops — find the cron ID currently looping on task #N
1---2name: loop-from-task3description: Wrap a TaskList task as a /loop. Creates a cron-driven loop whose prompt directs the agent to work on the specified task, and whose stop condition is the task's completion. Use when the user says "loop on task4---56# /loop-from-task — turn a TaskList item into a self-terminating loop78## Why this exists910Composing the existing primitives:1112- `TaskList` is the durable cross-fire state per CLAUDE.md13- `/loop` schedules cron-driven fires14- The natural pattern "keep working on task X until it's done" needs15 glue between them — that's this skill1617A loop created via `/loop-from-task` carries a self-terminating prompt:18each fire checks the task's status, continues work if `in_progress`19or `pending`, and `/loop-stop`s itself once `completed`.2021## Argument parsing2223```24/loop-from-task <task-id> <interval>25/loop-from-task <task-id> <interval> notes: "<additional context>"26```2728- `<task-id>` — TaskList ID (e.g. `3`). Required.29- `<interval>` — same syntax as /loop (`5m`, `1h`, `30s`, `2d`).30- `notes:` — optional additional context appended to the generated31 prompt (e.g. "stop at 5pm", "PR is blocked on review").3233If either positional arg is missing: show `TaskList`, ask which to34loop on and at what cadence.3536## Steps37381. **Verify the task exists.** Call `TaskList`. If `<task-id>` isn't39 present, error out with the current list.40412. **Verify the task isn't already completed.** If `status == completed`,42 tell user "task #N is already done — nothing to loop on" and stop.43443. **Verify no existing loop already wraps this task.** Check45 `CronList` + `~/.claude/.paused-loops.json` for any prompt containing46 `loop-from-task: task-<id>` (we tag the generated prompt). If found,47 tell user, don't duplicate.48494. **Convert interval to cron** (same table as /loop):5051 | Pattern | Cron expression |52 |----------|------------------|53 | `Nm` (≤59) | `*/N * * * *` |54 | `Nm` (≥60) | `0 */H * * *` (H = N/60) |55 | `Nh` | `0 */N * * *` |56 | `Nd` | `0 0 */N * *` |57 | `Ns` | round up to 1m |58595. **Generate the loop prompt.** Template:6061 ```62 /loop-from-task: task-<id>63 64 SELF-CHECK FIRST: call TaskList. Find task #<id> ("<subject>").65 66 If task status == completed:67 → /loop-stop this loop (cron id <self-id-or-placeholder>). Then post a one-line "task #<id> done; loop terminated" and exit.68 69 If task status == in_progress:70 → Continue working on it. Don't restart. Don't fragment progress.71 72 If task status == pending:73 → TaskUpdate status=in_progress, then work on it.74 75 If task is blocked (other tasks blockedBy points to it):76 → Note the blockers in this fire's output; do NOT mark in_progress; wait.77 78 <NOTES BLOCK IF PROVIDED>79 80 Canary: if you canceled or stopped this loop, you should NOT be seeing this message.81 ```8283 The cron-self-id will be substituted in step 6 after CronCreate84 returns the actual ID (use `<self>` as placeholder, then patch85 the prompt with the real ID via CronDelete + CronCreate cycle —86 OR accept that the placeholder remains and the agent uses `/loops`87 to find its own ID at fire time).8889 **Simpler alternative**: don't try to embed the loop's own ID;90 the agent at fire time uses `/loops` or `CronList` to discover91 the cron carrying the matching `/loop-from-task: task-<id>` tag92 and stops THAT specific cron. This is what the skill recommends.93946. **CronCreate** with the cron expression + prompt + recurring=true.95967. **Append history event**:97 ```jsonl98 {"event":"created","at":"...Z","id":"<new-cron-id>","cron":"...","tag":"loop-from-task","task_id":"<task-id>","label":"task-<id>: <subject first 40 chars>"}99 ```1001018. **TaskUpdate** the task: add metadata `loop_cron_id: <new-cron-id>`102 so future TaskList views show "this task is being looped on by103 cron X."1041059. **Don't execute the prompt now.** The first cron fire (in ≤N106 minutes) is when work begins. This differs from raw /loop which107 fires immediately — for task-driven loops, we want one clean108 work-cycle window, not double-firing.10911010. **Confirm** to user:111 > Loop started: cron `<id>` fires every <interval>, working on112 > task #<task-id> "<subject>" until completed. Use /loop-stop113 > <id> to cancel manually, /loops to monitor, or just mark the114 > task completed and the loop self-terminates on next fire.115116## Don't117118- Don't auto-mark the task `in_progress` at create time. Let the119 first cron fire decide based on task state.120- Don't refuse if the task is `pending` — that's the common case.121- Don't try to be smart about multi-task loops. One task per loop;122 user creates multiple `/loop-from-task` invocations if they want123 parallel work.124- Don't ignore the `notes:` field — it's how the user injects125 per-task constraints the generic prompt can't know.126127## See also128129- `/loop` — the lower-level cron primitive130- `/loop-stop` — manual cancellation (loop-from-task loops also131 self-terminate, but manual stop still works)132- `/loops` — find the cron ID currently looping on task #N