(args: string)
| 23 | /loop check the deploy every 20m` |
| 24 | |
| 25 | function buildPrompt(args: string): string { |
| 26 | return `# /loop — schedule a recurring prompt |
| 27 | |
| 28 | Parse the input below into \`[interval] <prompt…>\` and schedule it with ${CRON_CREATE_TOOL_NAME}. |
| 29 | |
| 30 | ## Parsing (in priority order) |
| 31 | |
| 32 | 1. **Leading token**: if the first whitespace-delimited token matches \`^\\d+[smhd]$\` (e.g. \`5m\`, \`2h\`), that's the interval; the rest is the prompt. |
| 33 | 2. **Trailing "every" clause**: otherwise, if the input ends with \`every <N><unit>\` or \`every <N> <unit-word>\` (e.g. \`every 20m\`, \`every 5 minutes\`, \`every 2 hours\`), extract that as the interval and strip it from the prompt. Only match when what follows "every" is a time expression — \`check every PR\` has no interval. |
| 34 | 3. **Default**: otherwise, interval is \`${DEFAULT_INTERVAL}\` and the entire input is the prompt. |
| 35 | |
| 36 | If the resulting prompt is empty, show usage \`/loop [interval] <prompt>\` and stop — do not call ${CRON_CREATE_TOOL_NAME}. |
| 37 | |
| 38 | Examples: |
| 39 | - \`5m /babysit-prs\` → interval \`5m\`, prompt \`/babysit-prs\` (rule 1) |
| 40 | - \`check the deploy every 20m\` → interval \`20m\`, prompt \`check the deploy\` (rule 2) |
| 41 | - \`run tests every 5 minutes\` → interval \`5m\`, prompt \`run tests\` (rule 2) |
| 42 | - \`check the deploy\` → interval \`${DEFAULT_INTERVAL}\`, prompt \`check the deploy\` (rule 3) |
| 43 | - \`check every PR\` → interval \`${DEFAULT_INTERVAL}\`, prompt \`check every PR\` (rule 3 — "every" not followed by time) |
| 44 | - \`5m\` → empty prompt → show usage |
| 45 | |
| 46 | ## Interval → cron |
| 47 | |
| 48 | Supported suffixes: \`s\` (seconds, rounded up to nearest minute, min 1), \`m\` (minutes), \`h\` (hours), \`d\` (days). Convert: |
| 49 | |
| 50 | | Interval pattern | Cron expression | Notes | |
| 51 | |-----------------------|---------------------|------------------------------------------| |
| 52 | | \`Nm\` where N ≤ 59 | \`*/N * * * *\` | every N minutes | |
| 53 | | \`Nm\` where N ≥ 60 | \`0 */H * * *\` | round to hours (H = N/60, must divide 24)| |
| 54 | | \`Nh\` where N ≤ 23 | \`0 */N * * *\` | every N hours | |
| 55 | | \`Nd\` | \`0 0 */N * *\` | every N days at midnight local | |
| 56 | | \`Ns\` | treat as \`ceil(N/60)m\` | cron minimum granularity is 1 minute | |
| 57 | |
| 58 | **If the interval doesn't cleanly divide its unit** (e.g. \`7m\` → \`*/7 * * * *\` gives uneven gaps at :56→:00; \`90m\` → 1.5h which cron can't express), pick the nearest clean interval and tell the user what you rounded to before scheduling. |
| 59 | |
| 60 | ## Action |
| 61 | |
| 62 | 1. Call ${CRON_CREATE_TOOL_NAME} with: |
| 63 | - \`cron\`: the expression from the table above |
| 64 | - \`prompt\`: the parsed prompt from above, verbatim (slash commands are passed through unchanged) |
| 65 | - \`recurring\`: \`true\` |
| 66 | 2. Briefly confirm: what's scheduled, the cron expression, the human-readable cadence, that recurring tasks auto-expire after ${DEFAULT_MAX_AGE_DAYS} days, and that they can cancel sooner with ${CRON_DELETE_TOOL_NAME} (include the job ID). |
| 67 | 3. **Then immediately execute the parsed prompt now** — don't wait for the first cron fire. If it's a slash command, invoke it via the Skill tool; otherwise act on it directly. |
| 68 | |
| 69 | ## Input |
| 70 | |
| 71 | ${args}` |
| 72 | } |
| 73 | |
| 74 | export function registerLoopSkill(): void { |
| 75 | registerBundledSkill({ |
no outgoing calls
no test coverage detected