source dump of claude code
at main 92 lines 4.7 kB view raw
1import { 2 CRON_CREATE_TOOL_NAME, 3 CRON_DELETE_TOOL_NAME, 4 DEFAULT_MAX_AGE_DAYS, 5 isKairosCronEnabled, 6} from '../../tools/ScheduleCronTool/prompt.js' 7import { registerBundledSkill } from '../bundledSkills.js' 8 9const DEFAULT_INTERVAL = '10m' 10 11const USAGE_MESSAGE = `Usage: /loop [interval] <prompt> 12 13Run a prompt or slash command on a recurring interval. 14 15Intervals: Ns, Nm, Nh, Nd (e.g. 5m, 30m, 2h, 1d). Minimum granularity is 1 minute. 16If no interval is specified, defaults to ${DEFAULT_INTERVAL}. 17 18Examples: 19 /loop 5m /babysit-prs 20 /loop 30m check the deploy 21 /loop 1h /standup 1 22 /loop check the deploy (defaults to ${DEFAULT_INTERVAL}) 23 /loop check the deploy every 20m` 24 25function buildPrompt(args: string): string { 26 return `# /loop — schedule a recurring prompt 27 28Parse the input below into \`[interval] <prompt…>\` and schedule it with ${CRON_CREATE_TOOL_NAME}. 29 30## Parsing (in priority order) 31 321. **Leading token**: if the first whitespace-delimited token matches \`^\\d+[smhd]$\` (e.g. \`5m\`, \`2h\`), that's the interval; the rest is the prompt. 332. **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. 343. **Default**: otherwise, interval is \`${DEFAULT_INTERVAL}\` and the entire input is the prompt. 35 36If the resulting prompt is empty, show usage \`/loop [interval] <prompt>\` and stop — do not call ${CRON_CREATE_TOOL_NAME}. 37 38Examples: 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 48Supported 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 621. 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\` 662. 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). 673. **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 74export function registerLoopSkill(): void { 75 registerBundledSkill({ 76 name: 'loop', 77 description: 78 'Run a prompt or slash command on a recurring interval (e.g. /loop 5m /foo, defaults to 10m)', 79 whenToUse: 80 'When the user wants to set up a recurring task, poll for status, or run something repeatedly on an interval (e.g. "check the deploy every 5 minutes", "keep running /babysit-prs"). Do NOT invoke for one-off tasks.', 81 argumentHint: '[interval] <prompt>', 82 userInvocable: true, 83 isEnabled: isKairosCronEnabled, 84 async getPromptForCommand(args) { 85 const trimmed = args.trim() 86 if (!trimmed) { 87 return [{ type: 'text', text: USAGE_MESSAGE }] 88 } 89 return [{ type: 'text', text: buildPrompt(trimmed) }] 90 }, 91 }) 92}