195 lines
6.9 KiB
Markdown
195 lines
6.9 KiB
Markdown
---
|
||
title: "Scheduled Tasks & /loop"
|
||
aliases: [loop, cron-tasks, session-scheduling]
|
||
tags: [claude-code, scheduling, cron, loop, automation]
|
||
sources: [raw/Run prompts on a schedule.md]
|
||
created: 2026-04-17
|
||
updated: 2026-04-17
|
||
---
|
||
|
||
# Scheduled Tasks & /loop
|
||
|
||
Run prompts automatically on a repeating interval or fire one-time reminders inside a Claude Code session. Requires **v2.1.72+** (`claude --version`).
|
||
|
||
Tasks are **session-scoped**: they live in the current conversation. `--resume` / `--continue` restores unexpired tasks; a fresh session clears them.
|
||
|
||
---
|
||
|
||
## Scheduling Options Compared
|
||
|
||
| | Cloud Routines | Desktop Tasks | `/loop` (session) |
|
||
|---|---|---|---|
|
||
| Runs on | Anthropic cloud | Your machine | Your machine |
|
||
| Machine must be on | No | Yes | Yes |
|
||
| Open session required | No | No | Yes |
|
||
| Persistent across restarts | Yes | Yes | Restored if unexpired |
|
||
| Local file access | No (fresh clone) | Yes | Yes |
|
||
| Min interval | 1 hour | 1 minute | 1 minute |
|
||
|
||
- **Cloud** → reliable unattended runs, no local machine needed
|
||
- **Desktop** → local files + tools, no open session needed
|
||
- **`/loop`** → quick in-session polling, simplest to start
|
||
|
||
---
|
||
|
||
## /loop — Quick Reference
|
||
|
||
| Command | Behavior |
|
||
|---------|----------|
|
||
| `/loop 5m check the deploy` | Fixed cron interval, your prompt |
|
||
| `/loop check the deploy` | Dynamic interval Claude chooses each iteration |
|
||
| `/loop` | Built-in maintenance prompt, dynamic interval |
|
||
| `/loop 15m` | Built-in maintenance prompt, fixed interval |
|
||
| `/loop 20m /review-pr 1234` | Re-run another skill on a fixed schedule |
|
||
|
||
### Fixed Interval
|
||
```
|
||
/loop 5m check if the deployment finished
|
||
```
|
||
- Units: `s`, `m`, `h`, `d` — seconds round up to nearest minute
|
||
- Non-clean steps (`7m`, `90m`) are rounded; Claude tells you what it picked
|
||
|
||
### Dynamic Interval
|
||
- Omit the interval; Claude picks 1 min–1 hour based on observed state
|
||
- Short waits when something is active; longer when idle
|
||
- May use the **Monitor tool** internally (streams background script output — more token-efficient than polling)
|
||
- Jitter rules don't apply; 7-day expiry does
|
||
|
||
### Built-in Maintenance Prompt (bare `/loop`)
|
||
Each iteration, in order:
|
||
1. Continue unfinished work from the conversation
|
||
2. Tend to the current branch's PR (review comments, failed CI, merge conflicts)
|
||
3. Run cleanup passes (bug hunts, simplification) when nothing else is pending
|
||
|
||
Irreversible actions (push, delete) only proceed if already authorized in the transcript.
|
||
|
||
### Customize with `loop.md`
|
||
Replaces the built-in prompt for bare `/loop` calls:
|
||
|
||
| Path | Scope |
|
||
|------|-------|
|
||
| `.claude/loop.md` | Project-level (takes precedence) |
|
||
| `~/.claude/loop.md` | User-level fallback |
|
||
|
||
- Plain Markdown, no required structure
|
||
- Edits take effect on the next iteration — refine while running
|
||
- Max 25,000 bytes (content beyond is truncated)
|
||
- Ignored when you supply a prompt on the command line
|
||
|
||
---
|
||
|
||
## One-Time Reminders
|
||
|
||
Describe in natural language — Claude creates a single-fire task that self-deletes after running:
|
||
|
||
```
|
||
remind me at 3pm to push the release branch
|
||
in 45 minutes, check whether the integration tests passed
|
||
```
|
||
|
||
---
|
||
|
||
## Managing Tasks
|
||
|
||
Natural language works:
|
||
```
|
||
what scheduled tasks do I have?
|
||
cancel the deploy check job
|
||
```
|
||
|
||
Underlying tools:
|
||
|
||
| Tool | Purpose |
|
||
|------|---------|
|
||
| `CronCreate` | Schedule a task (5-field cron, prompt, recurring flag) |
|
||
| `CronList` | List tasks with IDs, schedules, prompts |
|
||
| `CronDelete` | Cancel task by ID |
|
||
|
||
- Each task has an **8-character ID** (used with `CronDelete`)
|
||
- Max **50 scheduled tasks** per session
|
||
|
||
---
|
||
|
||
## How Tasks Fire
|
||
|
||
- Scheduler checks every second; tasks enqueue at **low priority**
|
||
- Fires **between turns** — never mid-response
|
||
- If Claude is busy when a task comes due, it waits until the current turn ends
|
||
- All times are **local timezone** (not UTC)
|
||
|
||
### Jitter
|
||
- Recurring: fires up to 10% of period late, capped at 15 min (e.g. hourly → `:00`–`:06`)
|
||
- One-shot at `:00` or `:30`: fires up to 90 s early
|
||
- Offset is deterministic from task ID → same task, same offset
|
||
- To avoid jitter: pick a non-`:00`/`:30` minute (e.g. `3 9 * * *`)
|
||
|
||
### Seven-Day Expiry
|
||
- Recurring tasks auto-expire 7 days after creation; fire once more then self-delete
|
||
- To extend: cancel and recreate, or switch to Routines / Desktop tasks
|
||
|
||
---
|
||
|
||
## Cron Expression Reference
|
||
|
||
5-field format: `minute hour day-of-month month day-of-week`
|
||
|
||
| Expression | Meaning |
|
||
|------------|---------|
|
||
| `*/5 * * * *` | Every 5 minutes |
|
||
| `0 * * * *` | Every hour on the hour |
|
||
| `7 * * * *` | Every hour at :07 |
|
||
| `0 9 * * *` | Daily at 9am local |
|
||
| `0 9 * * 1-5` | Weekdays at 9am local |
|
||
| `30 14 15 3 *` | March 15 at 2:30pm local |
|
||
|
||
- `0`/`7` = Sunday through `6` = Saturday
|
||
- Supports: `*`, single values, steps (`*/15`), ranges (`1-5`), lists (`1,15,30`)
|
||
- No extended syntax: `L`, `W`, `?`, name aliases (`MON`, `JAN`) not supported
|
||
- If both day-of-month and day-of-week are set, a date matches if **either** matches (vixie-cron semantics)
|
||
|
||
---
|
||
|
||
## Disable Scheduling
|
||
|
||
```bash
|
||
CLAUDE_CODE_DISABLE_CRON=1
|
||
```
|
||
|
||
Disables the scheduler entirely. `CronCreate`, `CronList`, `CronDelete`, and `/loop` become unavailable; already-scheduled tasks stop firing.
|
||
|
||
---
|
||
|
||
## Limitations
|
||
|
||
- Tasks only fire while Claude Code is **running and idle** — closing the terminal stops them
|
||
- **No catch-up** for missed fires (fires once when Claude goes idle, not per missed interval)
|
||
- Fresh conversation clears all tasks; `--resume`/`--continue` restores unexpired ones
|
||
- Background Bash and monitor tasks are **never restored** on resume
|
||
- On Bedrock / Vertex AI / Microsoft Foundry: dynamic `/loop` falls back to a fixed 10-min schedule; bare `/loop` prints usage instead of starting maintenance loop
|
||
|
||
---
|
||
|
||
## Key Takeaways
|
||
|
||
- `/loop` is the fastest way to poll during a session — interval optional, prompt optional
|
||
- Omitting the interval gives Claude adaptive waits (short when active, long when idle)
|
||
- Bare `/loop` runs a built-in PR/CI maintenance loop; `loop.md` customizes it per project
|
||
- One-time reminders need no special syntax — just describe them in natural language
|
||
- Session tasks expire after 7 days; use [[wiki/claude-code/overview|Routines or Desktop tasks]] for durable scheduling
|
||
- The `CronCreate` / `CronList` / `CronDelete` tools are what `/loop` calls under the hood
|
||
- Disable entirely with `CLAUDE_CODE_DISABLE_CRON=1`
|
||
|
||
---
|
||
|
||
## Related
|
||
|
||
- [[wiki/claude-code/channels|Channels]] — push events into a session reactively instead of polling
|
||
- [[wiki/claude-code/headless-cli|Headless CLI]] — run Claude Code non-interactively with `-p`
|
||
- [[wiki/claude-code/overview|Claude Code Overview]] — full feature surface including Routines and Desktop tasks
|
||
- [[wiki/claude-code/skills|Skills]] — package reusable workflows that `/loop` can call (e.g. `/loop 20m /review-pr`)
|
||
|
||
---
|
||
|
||
## Sources
|
||
|
||
- `raw/Run prompts on a schedule.md` — clipped from https://code.claude.com/docs/en/scheduled-tasks
|