From 809b8914e7862b4c6370aa740406736a891766dd Mon Sep 17 00:00:00 2001 From: Tommy Nguyen Date: Sun, 8 Mar 2026 13:54:43 -0700 Subject: [PATCH 1/2] feat: add claude-loop skill for recurring scheduling on any model provider Three-tier execution strategy: - Tier 1: Load CronCreate via ToolSearch (works across providers) - Tier 2: Background sleep chain fallback for real recurring execution - Tier 3: Execute-once last resort Features: interval parsing, one-shot reminders, job management (list/stop), native-feel output suppression, cross-platform time handling. --- .claude-plugin/marketplace.json | 5 + skills/claude-loop/README.md | 31 ++++++ skills/claude-loop/SKILL.md | 190 ++++++++++++++++++++++++++++++++ 3 files changed, 226 insertions(+) create mode 100644 skills/claude-loop/README.md create mode 100644 skills/claude-loop/SKILL.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 4755824..1b2a2ab 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -18,6 +18,11 @@ "name": "ralph-wiggum", "source": "./plugins/ralph-wiggum" }, + { + "name": "claude-loop", + "description": "Recurring loop & reminder skill for Claude Code. Works on any model provider with three-tier fallback (CronCreate → Background Sleep Chain → Execute Once).", + "source": "./skills/claude-loop" + }, { "name": "google-workspace", "description": "Comprehensive Google Workspace meta-skill covering all 47+ services (Calendar, Gmail, Drive, Docs, Sheets, Slides, Chat, Meet, Tasks, Forms, People, Workflows, and more)", diff --git a/skills/claude-loop/README.md b/skills/claude-loop/README.md new file mode 100644 index 0000000..9eecb11 --- /dev/null +++ b/skills/claude-loop/README.md @@ -0,0 +1,31 @@ +# claude-loop + +Recurring loop & reminder skill for Claude Code. Works on **any model provider** — not just Anthropic. + +## Features + +- Schedule recurring prompts: `/claude-loop 5m check the deploy` +- One-time reminders: `/claude-loop remind me at 3pm to review PRs` +- List/stop active jobs: `/claude-loop list`, `/claude-loop stop ` +- Natural language: "check the deploy every 5 minutes" + +## Three-Tier Execution + +1. **Tier 1: CronCreate** — Loads deferred Cron tools via ToolSearch. Full-featured, identical to built-in `/loop`. +2. **Tier 2: Background Sleep Chain** — When Cron tools are unavailable, uses `Bash run_in_background` sleep notifications for real recurring execution. Output appears inline in the conversation. +3. **Tier 3: Execute Once** — Last resort fallback if neither Tier 1 nor Tier 2 work. + +## Why This Exists + +The built-in `/loop` command relies on `CronCreate`, which is a deferred tool that may not be discovered automatically on custom model providers. This skill explicitly loads Cron tools via `ToolSearch` first, and provides a working fallback (Tier 2) for providers where Cron tools are genuinely unavailable. + +## Intervals + +Supports `Ns`, `Nm`, `Nh`, `Nd`. Defaults to `10m` if no interval specified. Minimum interval: 1 minute. + +## Installation + +```bash +/plugin marketplace add tuannvm/plugins +/plugin install claude-loop@plugins +``` diff --git a/skills/claude-loop/SKILL.md b/skills/claude-loop/SKILL.md new file mode 100644 index 0000000..f8cbf11 --- /dev/null +++ b/skills/claude-loop/SKILL.md @@ -0,0 +1,190 @@ +--- +name: claude-loop +description: Run a prompt or slash command on a recurring interval, or set a one-time reminder, using cron scheduling. Use when the user wants to loop, repeat, poll, schedule, remind, or run something periodically (e.g., "loop every 5m", "check the deploy every 10 minutes", "keep running /babysit-prs", "poll CI status", "remind me at 3pm", "in 45 minutes check tests", "list my loops", "stop all loops"). Supports intervals Ns, Nm, Nh, Nd. Defaults to 10m if no interval. Session-scoped — jobs stop when Claude exits. +--- + +# Recurring Loop & Reminder Skill + +Schedule a prompt to run on a recurring interval, or set a one-time reminder, within this session. + +## Syntax + +``` +/claude-loop [interval] +/claude-loop every +/claude-loop list +/claude-loop stop [job_id | all] +``` + +Also responds to natural language: "remind me at 3pm", "check the deploy every 5 minutes", "what scheduled tasks do I have?", "cancel the deploy check". + +## Parsing Rules + +1. **No args / "help"** → show syntax and examples +2. **"list"** → list active jobs +3. **"stop" / "cancel"** → cancel by ID, prompt substring, or all. Match order: exact ID → ID prefix → prompt substring. If multiple match, list them and ask user to clarify. +4. **One-shot** — "remind me at/in", "at Xpm", "in N minutes" → schedule once (`recurring: false`) +5. **Recurring** — extract interval (`\d+[smhd]` leading or `every ...` trailing), strip from prompt, default `10m` + +## Method Selection (Three-Tier) + +**ALWAYS try Tier 1 first.** Do NOT skip to lower tiers without attempting. + +### Tier 1: Load CronCreate via ToolSearch + +CronCreate is a **deferred tool** in Claude Code — it exists in the infrastructure, not the model. It may be available on any provider but must be loaded first. + +1. Call `ToolSearch("select:CronCreate,CronList,CronDelete")` to load the tools +2. **If loaded** → use the Cron Method below (full-featured, identical to built-in `/loop`) +3. **If ToolSearch fails or tools don't exist** → proceed to Tier 2 + +### Tier 2: Background Sleep Chain + +Use when CronCreate is genuinely unavailable. Provides real recurring execution using background Bash sleep notifications. + +**How it works:** Run `sleep ` via Bash with `run_in_background: true`. When the sleep completes, Claude receives an automatic notification. Claude then executes the prompt inline and starts the next sleep cycle. + +**Detection:** If the first Bash `run_in_background` call errors, fall to Tier 3. + +### Tier 3: Execute Once (last resort) + +Only if both Tier 1 and Tier 2 fail: +1. Execute the prompt once immediately in the conversation +2. Explain the limitation and offer alternatives (system cron, `watch`, separate terminal) + +--- + +## Cron Method (Tier 1) + +### Interval-to-Cron Conversion + +| Interval | Cron | Notes | +|----------|------|-------| +| `Ns` | `*/ceil(N/60) * * * *` | Round up to nearest minute, warn user | +| `1m` | `* * * * *` | | +| `Nm` (2-59) | `*/N * * * *` | | +| `Nm` (60+) | `0 */H * * *` where H=round(N/60) | Must divide 24 evenly; tell user what was picked | +| `Nh` (1-23) | `M */N * * *` | M ∈ 1-59, avoid 0 and 30 | +| `Nh` (24+) | Convert to days | e.g., `48h` → `2d` | +| `Nd` (1-31) | `M H */N * *` | M ∈ 1-59, H ∈ 7-21 | +| `Nd` (32+) | Reject | Tell user max is 31d | + +**Anti-spike:** For hourly+, never use minute 0 or 30 unless user requests exact time (e.g., one-shot "at 3:00pm"). + +### How CronCreate Works + +The `prompt` parameter is a **Claude prompt**, not a bash command. When the job fires, the prompt is fed back into the current Claude Code session on the next idle turn. Claude then interprets and executes it inline — output appears directly in the conversation. + +Example: `CronCreate(* * * * *: "print hello world")` → on each fire, Claude sees "print hello world" and responds with "Hello world!" in the conversation. + +### Execution + +1. Parse interval and prompt +2. Convert to cron expression +3. `CronCreate`: `cron`, `prompt`, `recurring: true` (or `false` for one-shot) +4. Confirm: prompt, interval, cron, job ID, 3-day expiry note + +### One-Time Reminders + +Parse target time → pin to cron fields → `CronCreate` with `recurring: false`. + +### Managing Jobs (Cron) + +| Action | Tool | +|--------|------| +| List | `CronList` | +| Cancel by ID | `CronDelete` (8-char ID) | +| Cancel all | `CronList` → `CronDelete` each | + +Max 50 tasks per session. + +### Runtime Behavior + +- **Session-scoped** — gone when Claude Code exits +- **Fires between turns** — only when idle, not mid-response +- **No catch-up** — missed fires execute once when idle +- **Local timezone** — not UTC +- **3-day expiry** — recurring auto-expire +- **Jitter** — recurring: up to 10% late (max 15 min); one-shot on :00/:30: up to 90s early +- **Disable** — `CLAUDE_CODE_DISABLE_CRON=1` + +### Cron Reference + +Standard 5-field: `minute hour day-of-month month day-of-week`. Supports `*`, values, `*/N` steps, ranges, lists. Day-of-week: 0 or 7 = Sunday. Vixie-cron semantics (either-field match). + +--- + +## Background Sleep Chain (Tier 2) + +Use when CronCreate is genuinely unavailable after ToolSearch attempt. + +### State File + +Each job creates `/tmp/claude-loop-.state` (JSON): +```json +{"id": "<8-char-hex>", "prompt": "", "interval_sec": 60, "recurring": true, "created": "", "session_pid": } +``` + +- Generate `id`: `openssl rand -hex 4` +- **Escape prompt** for JSON: replace `\` → `\\`, `"` → `\"`, newlines → `\n` +- `session_pid`: `echo $PPID` (Claude Code's PID — used for staleness detection) +- `recurring`: `false` for one-shot reminders, `true` for loops + +### One-Time Reminders (Tier 2) + +For absolute times ("at 3pm", "remind me at 14:30"): compute seconds until target time and use as `interval_sec` with `recurring: false`. +- **macOS:** `$(( $(date -j -f '%H:%M' '15:00' '+%s') - $(date '+%s') ))` +- **Linux:** `$(( $(date -d '15:00' '+%s') - $(date '+%s') ))` +- If result is negative, target is tomorrow — add 86400. + +### Starting a Loop + +1. Parse interval and prompt (same rules as Cron method) +2. Convert interval to seconds (`1m`→60, `5m`→300, `1h`→3600). Enforce minimum 60s. +3. Create the state file +4. Run Bash: `sleep && echo 'CLAUDE_LOOP_FIRE '` with `run_in_background: true` + - The echo embeds the job ID so the notification is self-describing +5. Confirm to user: prompt, interval, job ID + +### On Sleep Completion (notification received) + +When you see a background task notification containing `CLAUDE_LOOP_FIRE `: + +**CRITICAL behavioral rules:** +- **MUST output ONLY the result of executing the stored prompt.** Nothing else. +- **MUST NOT output any meta-commentary**, reflection, or speculation about the loop, the user's intent, or what might happen next. NEVER emit phrases like "Loop fired", "reading state file", "next cycle started", "the loop is working", "the user might want to...", or similar. +- **Treat each fire as atomic and isolated.** Do not reflect on previous fires or accumulated loop history. Do not speculate about the user's intentions based on repeated fires. +- If the prompt produces no visible output, output nothing at all. + +**Steps:** + +1. Read `/tmp/claude-loop-.state` (no commentary) +2. If file exists: + - **Execute the prompt** — user sees only the prompt's output, nothing else + - If `recurring: true`: re-arm with `sleep && echo 'CLAUDE_LOOP_FIRE '` (`run_in_background: true`) — no confirmation text + - If `recurring: false`: delete the state file — no confirmation text +3. If file missing → do nothing (loop was cancelled) + +**After executing the prompt and re-arming, STOP. Do not add any trailing commentary or thoughts.** + +### Managing Jobs (Sleep Chain) + +**List:** Glob `/tmp/claude-loop-*.state`, read each, show active ones. Filter stale: if `session_pid` doesn't match a running process (`kill -0 2>/dev/null`), skip and delete. + +**Stop by ID:** Delete `/tmp/claude-loop-.state`. The next sleep notification will find no file and stop. + +**Stop by prompt substring:** Glob all state files, find those whose `prompt` contains the substring, delete matching files. If multiple match, list them and ask user to clarify. + +**Stop all:** Glob and delete all `/tmp/claude-loop-*.state`. + +Max 20 concurrent jobs per session (check count before creating). + +### Runtime Behavior + +- **Session-scoped** — state files go stale when Claude exits; detected via `session_pid` +- **Fires between turns** — notification arrives when idle +- **Output inline** — Claude executes the prompt in the conversation, identical to Cron behavior +- **Minimum interval** — 1 minute (60 seconds) +- **Self-describing notifications** — `CLAUDE_LOOP_FIRE ` in the background output identifies the job +- **No 3-day expiry** — runs until stopped or session ends +- **On prompt error** — log the error inline, continue the loop (don't stop on transient failures) From 9213cd1620dc0ba8950e94cc804d3948099619b8 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen Date: Sun, 8 Mar 2026 13:59:16 -0700 Subject: [PATCH 2/2] refactor: restructure claude-loop from skill to plugin format - Add .claude-plugin/plugin.json with metadata - Split into commands: loop (main), list, stop, help - Remove skills/claude-loop/ directory - Update marketplace.json source path to plugins/claude-loop --- .claude-plugin/marketplace.json | 4 +- .../claude-loop/.claude-plugin/plugin.json | 9 +++ plugins/claude-loop/README.md | 36 ++++++++++ plugins/claude-loop/commands/help.md | 43 ++++++++++++ plugins/claude-loop/commands/list.md | 25 +++++++ .../claude-loop/commands/loop.md | 67 ++++++++----------- plugins/claude-loop/commands/stop.md | 20 ++++++ skills/claude-loop/README.md | 31 --------- 8 files changed, 162 insertions(+), 73 deletions(-) create mode 100644 plugins/claude-loop/.claude-plugin/plugin.json create mode 100644 plugins/claude-loop/README.md create mode 100644 plugins/claude-loop/commands/help.md create mode 100644 plugins/claude-loop/commands/list.md rename skills/claude-loop/SKILL.md => plugins/claude-loop/commands/loop.md (69%) create mode 100644 plugins/claude-loop/commands/stop.md delete mode 100644 skills/claude-loop/README.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 1b2a2ab..ff69264 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -20,8 +20,8 @@ }, { "name": "claude-loop", - "description": "Recurring loop & reminder skill for Claude Code. Works on any model provider with three-tier fallback (CronCreate → Background Sleep Chain → Execute Once).", - "source": "./skills/claude-loop" + "description": "Recurring loop & reminder plugin for Claude Code. Works on any model provider with three-tier fallback (CronCreate → Background Sleep Chain → Execute Once).", + "source": "./plugins/claude-loop" }, { "name": "google-workspace", diff --git a/plugins/claude-loop/.claude-plugin/plugin.json b/plugins/claude-loop/.claude-plugin/plugin.json new file mode 100644 index 0000000..c84d66e --- /dev/null +++ b/plugins/claude-loop/.claude-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "claude-loop", + "version": "0.1.0", + "description": "Recurring loop & reminder scheduling for Claude Code. Works on any model provider with three-tier fallback (CronCreate → Background Sleep Chain → Execute Once).", + "author": { + "name": "Tommy Nguyen", + "url": "https://github.com/tuannvm" + } +} diff --git a/plugins/claude-loop/README.md b/plugins/claude-loop/README.md new file mode 100644 index 0000000..7c03dc3 --- /dev/null +++ b/plugins/claude-loop/README.md @@ -0,0 +1,36 @@ +# claude-loop + +Recurring loop & reminder plugin for Claude Code. Works on **any model provider** — not just Anthropic. + +## Commands + +| Command | Description | +|---------|-------------| +| `/claude-loop:loop [interval] ` | Schedule a recurring prompt | +| `/claude-loop:list` | List active loops | +| `/claude-loop:stop [id \| all]` | Stop loops | +| `/claude-loop:help` | Show usage and examples | + +## Features + +- Schedule recurring prompts: `/claude-loop:loop 5m check the deploy` +- One-time reminders: `/claude-loop:loop remind me at 3pm to review PRs` +- Natural language: "check the deploy every 5 minutes" +- Intervals: `Ns`, `Nm`, `Nh`, `Nd` (default `10m`, minimum `1m`) + +## Three-Tier Execution + +1. **Tier 1: CronCreate** — Loads deferred Cron tools via ToolSearch. Full-featured, identical to built-in `/loop`. +2. **Tier 2: Background Sleep Chain** — When Cron tools are unavailable, uses `Bash run_in_background` sleep notifications for real recurring execution. Output appears inline in the conversation. +3. **Tier 3: Execute Once** — Last resort fallback if neither Tier 1 nor Tier 2 work. + +## Why This Exists + +The built-in `/loop` command relies on `CronCreate`, which is a deferred tool that may not be discovered automatically on custom model providers. This plugin explicitly loads Cron tools via `ToolSearch` first, and provides a working fallback (Tier 2) for providers where Cron tools are genuinely unavailable. + +## Installation + +```bash +/plugin marketplace add tuannvm/plugins +/plugin install claude-loop@plugins +``` diff --git a/plugins/claude-loop/commands/help.md b/plugins/claude-loop/commands/help.md new file mode 100644 index 0000000..fb1a428 --- /dev/null +++ b/plugins/claude-loop/commands/help.md @@ -0,0 +1,43 @@ +--- +description: "Show claude-loop usage and examples" +--- + +# claude-loop Help + +Show the user the following: + +## Usage + +``` +/claude-loop:loop [interval] +/claude-loop:loop every +/claude-loop:list +/claude-loop:stop [job_id | all] +``` + +## Examples + +| Command | Effect | +|---------|--------| +| `/claude-loop:loop 5m check the deploy` | Check deploy every 5 minutes | +| `/claude-loop:loop 1m print hello world` | Print "hello world" every minute | +| `/claude-loop:loop check CI status every 10m` | Check CI every 10 minutes | +| `/claude-loop:loop remind me at 3pm to review PRs` | One-time reminder at 3pm | +| `/claude-loop:loop 30s /babysit-prs` | Run /babysit-prs every minute (30s rounds up) | +| `/claude-loop:list` | Show active loops | +| `/claude-loop:stop all` | Cancel all loops | +| `/claude-loop:stop a1b2c3d4` | Cancel specific loop by ID | + +## Intervals + +`Ns` (seconds, rounds up to 1m minimum), `Nm` (minutes), `Nh` (hours), `Nd` (days). Default: `10m`. + +## How It Works + +Works on **any model provider** via three-tier fallback: + +1. **Tier 1:** Loads CronCreate via ToolSearch — may be available on any provider +2. **Tier 2:** Background sleep chain — real recurring via `Bash run_in_background` notifications +3. **Tier 3:** Execute once — if neither tier works + +Jobs are session-scoped and stop when Claude Code exits. diff --git a/plugins/claude-loop/commands/list.md b/plugins/claude-loop/commands/list.md new file mode 100644 index 0000000..c37af1b --- /dev/null +++ b/plugins/claude-loop/commands/list.md @@ -0,0 +1,25 @@ +--- +description: "List all active recurring loops and reminders" +--- + +# List Active Loops + +Show all active jobs scheduled in this session. + +## Method Selection + +1. Call `ToolSearch("select:CronList")` to load the tool +2. **If loaded** → call `CronList` and display results +3. **If unavailable** → Glob `/tmp/claude-loop-*.state`, read each file, show active ones + +### Stale Detection (Tier 2) + +For each state file, check if `session_pid` matches a running process: +```bash +kill -0 2>/dev/null +``` +If not running, the job is stale — delete the file and skip it. + +### Output Format + +Show a table with: Job ID, Prompt, Interval, Created time. diff --git a/skills/claude-loop/SKILL.md b/plugins/claude-loop/commands/loop.md similarity index 69% rename from skills/claude-loop/SKILL.md rename to plugins/claude-loop/commands/loop.md index f8cbf11..8fe7e50 100644 --- a/skills/claude-loop/SKILL.md +++ b/plugins/claude-loop/commands/loop.md @@ -1,30 +1,25 @@ --- -name: claude-loop -description: Run a prompt or slash command on a recurring interval, or set a one-time reminder, using cron scheduling. Use when the user wants to loop, repeat, poll, schedule, remind, or run something periodically (e.g., "loop every 5m", "check the deploy every 10 minutes", "keep running /babysit-prs", "poll CI status", "remind me at 3pm", "in 45 minutes check tests", "list my loops", "stop all loops"). Supports intervals Ns, Nm, Nh, Nd. Defaults to 10m if no interval. Session-scoped — jobs stop when Claude exits. +description: "Run a prompt on a recurring interval or set a one-time reminder. Works on any model provider." +argument-hint: "[interval] " --- -# Recurring Loop & Reminder Skill +# Recurring Loop & Reminder Schedule a prompt to run on a recurring interval, or set a one-time reminder, within this session. -## Syntax +## Input -``` -/claude-loop [interval] -/claude-loop every -/claude-loop list -/claude-loop stop [job_id | all] -``` +Parse `$ARGUMENTS` into `[interval] `: -Also responds to natural language: "remind me at 3pm", "check the deploy every 5 minutes", "what scheduled tasks do I have?", "cancel the deploy check". +1. **No args / "help"** → show usage and examples, then stop +2. **"list"** → list active jobs (see list command logic below) +3. **"stop" / "cancel"** → cancel jobs (see stop command logic below) +4. **Leading token** matches `^\d+[smhd]$` (e.g. `5m`, `2h`) → that's the interval; rest is prompt +5. **Trailing "every" clause** ends with `every ` → extract interval, strip from prompt. Only match when followed by a time expression — `check every PR` has no interval. +6. **One-shot** — "remind me at/in", "at Xpm", "in N minutes" → schedule once (`recurring: false`) +7. **Default** — interval is `10m`, entire input is the prompt -## Parsing Rules - -1. **No args / "help"** → show syntax and examples -2. **"list"** → list active jobs -3. **"stop" / "cancel"** → cancel by ID, prompt substring, or all. Match order: exact ID → ID prefix → prompt substring. If multiple match, list them and ask user to clarify. -4. **One-shot** — "remind me at/in", "at Xpm", "in N minutes" → schedule once (`recurring: false`) -5. **Recurring** — extract interval (`\d+[smhd]` leading or `every ...` trailing), strip from prompt, default `10m` +If resulting prompt is empty, show usage `/claude-loop:loop [interval] ` and stop. ## Method Selection (Three-Tier) @@ -35,12 +30,12 @@ Also responds to natural language: "remind me at 3pm", "check the deploy every 5 CronCreate is a **deferred tool** in Claude Code — it exists in the infrastructure, not the model. It may be available on any provider but must be loaded first. 1. Call `ToolSearch("select:CronCreate,CronList,CronDelete")` to load the tools -2. **If loaded** → use the Cron Method below (full-featured, identical to built-in `/loop`) +2. **If loaded** → use the Cron Method below 3. **If ToolSearch fails or tools don't exist** → proceed to Tier 2 ### Tier 2: Background Sleep Chain -Use when CronCreate is genuinely unavailable. Provides real recurring execution using background Bash sleep notifications. +Use when CronCreate is genuinely unavailable. **How it works:** Run `sleep ` via Bash with `run_in_background: true`. When the sleep completes, Claude receives an automatic notification. Claude then executes the prompt inline and starts the next sleep cycle. @@ -69,14 +64,12 @@ Only if both Tier 1 and Tier 2 fail: | `Nd` (1-31) | `M H */N * *` | M ∈ 1-59, H ∈ 7-21 | | `Nd` (32+) | Reject | Tell user max is 31d | -**Anti-spike:** For hourly+, never use minute 0 or 30 unless user requests exact time (e.g., one-shot "at 3:00pm"). +**Anti-spike:** For hourly+, never use minute 0 or 30 unless user requests exact time. ### How CronCreate Works The `prompt` parameter is a **Claude prompt**, not a bash command. When the job fires, the prompt is fed back into the current Claude Code session on the next idle turn. Claude then interprets and executes it inline — output appears directly in the conversation. -Example: `CronCreate(* * * * *: "print hello world")` → on each fire, Claude sees "print hello world" and responds with "Hello world!" in the conversation. - ### Execution 1. Parse interval and prompt @@ -84,17 +77,15 @@ Example: `CronCreate(* * * * *: "print hello world")` → on each fire, Claude s 3. `CronCreate`: `cron`, `prompt`, `recurring: true` (or `false` for one-shot) 4. Confirm: prompt, interval, cron, job ID, 3-day expiry note -### One-Time Reminders +### One-Time Reminders (Cron) Parse target time → pin to cron fields → `CronCreate` with `recurring: false`. ### Managing Jobs (Cron) -| Action | Tool | -|--------|------| -| List | `CronList` | -| Cancel by ID | `CronDelete` (8-char ID) | -| Cancel all | `CronList` → `CronDelete` each | +- **List:** `CronList` +- **Cancel by ID:** `CronDelete` (8-char ID) +- **Cancel all:** `CronList` → `CronDelete` each Max 50 tasks per session. @@ -106,11 +97,10 @@ Max 50 tasks per session. - **Local timezone** — not UTC - **3-day expiry** — recurring auto-expire - **Jitter** — recurring: up to 10% late (max 15 min); one-shot on :00/:30: up to 90s early -- **Disable** — `CLAUDE_CODE_DISABLE_CRON=1` ### Cron Reference -Standard 5-field: `minute hour day-of-month month day-of-week`. Supports `*`, values, `*/N` steps, ranges, lists. Day-of-week: 0 or 7 = Sunday. Vixie-cron semantics (either-field match). +Standard 5-field: `minute hour day-of-month month day-of-week`. Supports `*`, values, `*/N` steps, ranges, lists. Day-of-week: 0 or 7 = Sunday. --- @@ -153,7 +143,7 @@ When you see a background task notification containing `CLAUDE_LOOP_FIRE `: **CRITICAL behavioral rules:** - **MUST output ONLY the result of executing the stored prompt.** Nothing else. - **MUST NOT output any meta-commentary**, reflection, or speculation about the loop, the user's intent, or what might happen next. NEVER emit phrases like "Loop fired", "reading state file", "next cycle started", "the loop is working", "the user might want to...", or similar. -- **Treat each fire as atomic and isolated.** Do not reflect on previous fires or accumulated loop history. Do not speculate about the user's intentions based on repeated fires. +- **Treat each fire as atomic and isolated.** Do not reflect on previous fires or accumulated loop history. - If the prompt produces no visible output, output nothing at all. **Steps:** @@ -169,13 +159,10 @@ When you see a background task notification containing `CLAUDE_LOOP_FIRE `: ### Managing Jobs (Sleep Chain) -**List:** Glob `/tmp/claude-loop-*.state`, read each, show active ones. Filter stale: if `session_pid` doesn't match a running process (`kill -0 2>/dev/null`), skip and delete. - -**Stop by ID:** Delete `/tmp/claude-loop-.state`. The next sleep notification will find no file and stop. - -**Stop by prompt substring:** Glob all state files, find those whose `prompt` contains the substring, delete matching files. If multiple match, list them and ask user to clarify. - -**Stop all:** Glob and delete all `/tmp/claude-loop-*.state`. +- **List:** Glob `/tmp/claude-loop-*.state`, read each, show active ones. Filter stale: if `session_pid` doesn't match a running process (`kill -0 2>/dev/null`), skip and delete. +- **Stop by ID:** Delete `/tmp/claude-loop-.state`. The next sleep notification will find no file and stop. +- **Stop by prompt substring:** Glob all state files, match prompt substring, delete. If multiple match, list and ask user to clarify. +- **Stop all:** Glob and delete all `/tmp/claude-loop-*.state`. Max 20 concurrent jobs per session (check count before creating). @@ -185,6 +172,6 @@ Max 20 concurrent jobs per session (check count before creating). - **Fires between turns** — notification arrives when idle - **Output inline** — Claude executes the prompt in the conversation, identical to Cron behavior - **Minimum interval** — 1 minute (60 seconds) -- **Self-describing notifications** — `CLAUDE_LOOP_FIRE ` in the background output identifies the job +- **Self-describing notifications** — `CLAUDE_LOOP_FIRE ` in background output identifies the job - **No 3-day expiry** — runs until stopped or session ends - **On prompt error** — log the error inline, continue the loop (don't stop on transient failures) diff --git a/plugins/claude-loop/commands/stop.md b/plugins/claude-loop/commands/stop.md new file mode 100644 index 0000000..3a910a2 --- /dev/null +++ b/plugins/claude-loop/commands/stop.md @@ -0,0 +1,20 @@ +--- +description: "Stop active loops by ID, prompt substring, or all" +argument-hint: "[job_id | prompt_substring | all]" +--- + +# Stop Loops + +Cancel active jobs. Parse `$ARGUMENTS`: + +- **No args** → list active jobs and ask which to stop +- **"all"** → stop all active jobs +- **Otherwise** → match by: exact ID → ID prefix → prompt substring. If multiple match, list them and ask user to clarify. + +## Method Selection + +1. Call `ToolSearch("select:CronList,CronDelete")` to load the tools +2. **If loaded** → use `CronList` to find jobs, `CronDelete` to cancel +3. **If unavailable** → Glob `/tmp/claude-loop-*.state`, match against argument, delete matching files + +Confirm what was stopped: job ID, prompt, interval. diff --git a/skills/claude-loop/README.md b/skills/claude-loop/README.md deleted file mode 100644 index 9eecb11..0000000 --- a/skills/claude-loop/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# claude-loop - -Recurring loop & reminder skill for Claude Code. Works on **any model provider** — not just Anthropic. - -## Features - -- Schedule recurring prompts: `/claude-loop 5m check the deploy` -- One-time reminders: `/claude-loop remind me at 3pm to review PRs` -- List/stop active jobs: `/claude-loop list`, `/claude-loop stop ` -- Natural language: "check the deploy every 5 minutes" - -## Three-Tier Execution - -1. **Tier 1: CronCreate** — Loads deferred Cron tools via ToolSearch. Full-featured, identical to built-in `/loop`. -2. **Tier 2: Background Sleep Chain** — When Cron tools are unavailable, uses `Bash run_in_background` sleep notifications for real recurring execution. Output appears inline in the conversation. -3. **Tier 3: Execute Once** — Last resort fallback if neither Tier 1 nor Tier 2 work. - -## Why This Exists - -The built-in `/loop` command relies on `CronCreate`, which is a deferred tool that may not be discovered automatically on custom model providers. This skill explicitly loads Cron tools via `ToolSearch` first, and provides a working fallback (Tier 2) for providers where Cron tools are genuinely unavailable. - -## Intervals - -Supports `Ns`, `Nm`, `Nh`, `Nd`. Defaults to `10m` if no interval specified. Minimum interval: 1 minute. - -## Installation - -```bash -/plugin marketplace add tuannvm/plugins -/plugin install claude-loop@plugins -```