Scheduled tasks and /loop
Re-run prompts on an interval, let Claude pace its own polling, set one-off reminders and manage session-scoped cron jobs in Claude Code.
A lot of waiting happens during development: for CI, for a deploy, for a reviewer. Scheduled tasks let the session do the waiting. You give Claude a prompt and a cadence (or let it pick one), and it re-runs the prompt between your turns, for example checking a pipeline every few minutes and fixing whatever breaks.
These tasks belong to the session. If you need something that runs whether or not a terminal is open, use a different tool (compared below). If you would rather react to events than poll for them, look at channels, and if you want Claude to keep going turn after turn until a condition holds, see /goal.
Picking the right scheduler
| Cloud routines | Desktop scheduled tasks | /loop and session tasks | |
|---|---|---|---|
| Where it runs | Anthropic-managed cloud by default | Your machine | Your machine |
| Machine must be on | No | Yes | Yes |
| Session must be open | No | No | Yes |
| Survives restarts | Yes | Yes | Restored on --resume, with exceptions |
| Local files | No, a fresh clone | Yes | Yes |
| MCP servers | Connectors chosen per task | Config files and connectors | Whatever the session has |
| Permission prompts | None, runs autonomously | Configurable per task | Same as the session |
| Custom schedule | Via /schedule in the CLI | Yes | Yes |
| Shortest interval | 1 hour | 1 minute | 1 minute |
My rule of thumb: routines for anything that must run reliably while my laptop is shut, desktop tasks for recurring jobs that need my local toolchain, and /loop for "keep an eye on this while I work on something else".
/loop in three forms
/loop is a bundled skill. Both arguments are optional and the combination you give changes its behaviour:
| You type | Example | Result |
|---|---|---|
| Interval and prompt | /loop 10m is the staging deploy healthy yet? | Fixed cron schedule |
| Prompt only | /loop watch the PR and deal with review feedback | Claude chooses the gap after each run |
| Interval only, or nothing | /loop or /loop 30m | The built-in maintenance prompt, or your loop.md |
The prompt can be a skill invocation, such as /loop 15m /triage-inbox. On scheduled fires, only skills Claude may invoke by itself actually execute. These arrive as plain text instead:
- Built-in commands like
/model,/permissionsor/clear. - Skills with
disable-model-invocation: true(the bundled/verifyskill is one). - Skills hidden from Claude by a
skillOverridessetting or aSkilldeny rule. - MCP prompts such as
/mcp__jira__my_open_issues.
See Skills for how invocation control works.
Fixed intervals
Give an interval and Claude turns it into a cron expression, schedules it, and replies with the cadence and a job ID.
/loop 3m tail the migration job logs and tell me the moment it errors or completes
The interval can come first as a bare token (45m) or at the end as a phrase (every 2 hours). Units are s, m, h and d. Cron works in whole minutes, so seconds round up, and awkward values like 7m or 90m are rounded to the nearest interval cron can express cleanly; Claude tells you what it chose.
Self-paced loops
Leave the interval out and Claude decides how long to wait after each iteration, anywhere from one minute to one hour, depending on what it saw. A build in progress means short waits; a quiet PR means long ones. Each iteration ends with the chosen delay and the reason.
/loop keep the nightly-import PR green: rerun flaky jobs once, fix real failures, answer review comments
Where the Monitor tool is available, Claude may use it instead: Monitor runs a background script and streams each line of output back, which avoids polling and tends to be cheaper and quicker to react.
Self-paced loops show up in the task list and can be cancelled like any other. Jitter does not apply to them, but the seven-day expiry does.
The built-in maintenance prompt
A bare /loop runs Claude's own housekeeping prompt at a self-chosen interval (add an interval, such as /loop 20m, for a fixed cadence). Each pass, in priority order, it:
- Continues unfinished work from the conversation.
- Looks after the current branch's pull request: review comments, failing CI, merge conflicts.
- Does clean-up passes (bug hunting, simplification) when nothing else is waiting.
It does not start new initiatives, and irreversible steps such as pushing or deleting only happen when they continue something the conversation already approved.
Self-paced loops and the maintenance prompt work on every provider and with feature-flag fetching turned off. On Amazon Bedrock, Claude Platform on AWS, Google Vertex AI and Microsoft Foundry, or with fetching off, both need v2.1.248 or later.
Your own default with loop.md
Write a loop.md to replace the maintenance prompt for bare /loop. It is one default prompt, not a list of jobs, and it is ignored whenever you pass a prompt on the command line.
| Path | Scope |
|---|---|
.claude/loop.md | This project; wins if both exist |
~/.claude/loop.md | Every project without its own |
It is free-form Markdown. Mine for a client project looks like this:
Look at open PRs I authored on this repo.
- Failing checks: read the log, fix if the cause is in our code, otherwise comment with the cause.
- Unanswered review comments: address them and reply on the thread.
- Nothing to do: reply "all quiet" and nothing else.
Never force-push.
Changes apply from the next iteration, so you can tune it while the loop runs. Anything past 25,000 bytes is truncated.
Stopping a loop
- Self-paced: press
Escwhile it is waiting to cancel the pending wake-up. Claude can also end the loop itself when the job is done by calling theScheduleWakeuptool withstop: true. If an iteration neither reschedules nor stops, Claude Code sets a single fallback wake-up about 20 minutes later and ends the loop if that run does not reschedule either. - Fixed interval: runs until you cancel it (see below) or seven days pass.
Escdoes not affect tasks you scheduled by asking Claude directly.
One-off reminders
For a single future action, just say it:
at 17:30 remind me to switch the feature flag back off
in 20 minutes, check whether the Lighthouse run finished and summarise the scores
Claude schedules a one-shot task pinned to that hour and minute, confirms the time, and the task deletes itself after firing.
Listing and cancelling
Ask in plain language ("what's scheduled?", "cancel the deploy watcher"). Under the hood Claude uses three tools:
| Tool | Does |
|---|---|
CronCreate | Creates a task from a five-field cron expression, a prompt, and whether it recurs or fires once |
CronList | Shows every task with its ID, schedule and prompt |
CronDelete | Removes a task by ID |
IDs are eight characters. A session can hold at most 50 tasks.
How firing works
- The scheduler checks every second and queues due tasks at low priority.
- A task fires between turns, never in the middle of a response. If Claude is busy, it waits for the turn to finish.
- Times use your local time zone:
0 9 * * *is 09:00 where you are, not UTC.
Jitter
To stop every session in the world calling the API at the same instant, fire times get a fixed offset derived from the task ID:
- Recurring: up to 30 minutes late, or up to half the interval for jobs more frequent than hourly. An hourly job at
:00might fire as late as:30. - One-shot at
:00or:30: up to 90 seconds early.
If timing matters, avoid the round numbers. 4 9 * * * instead of 0 9 * * * sidesteps the one-shot adjustment.
Seven-day expiry
Recurring tasks fire one last time seven days after creation and then delete themselves, so a forgotten loop cannot run forever. For longer schedules, recreate before expiry or move to routines or desktop tasks.
Cron syntax
Five fields: minute hour day-of-month month day-of-week. Each field accepts *, a single value, steps (*/10), ranges (1-5) and lists (0,20,40).
| Expression | Fires |
|---|---|
*/10 * * * * | Every 10 minutes |
15 * * * * | Quarter past every hour |
0 8 * * 1-5 | 08:00 on weekdays |
30 18 * * 5 | 18:30 every Friday |
0 0 1 * * | Midnight on the first of each month |
45 11 24 12 * | 11:45 on 24 December |
Sunday is 0 or 7, Saturday 6. No L, W, ? or names like MON/JAN. If both day-of-month and day-of-week are restricted, a date matches when either does (standard vixie-cron behaviour).
Turning the scheduler off
Set CLAUDE_CODE_DISABLE_CRON=1. The cron tools and /loop disappear and existing tasks stop firing. Other disable switches are listed in Environment variables.
Limitations
- Tasks fire only while Claude Code is running and idle. Close the terminal and they stop. Backgrounding the session (see agent view) carries
/looptasks into a background session that keeps going without a terminal. - Missed fires are not replayed. If several intervals pass during a long request, the task fires once when Claude is free.
claude --resumeandclaude --continuerestoreCronCreatetasks, except expired recurring tasks and one-shots whose time has passed. Self-paced/loopis not restored, so start it again. Background Bash and monitor tasks are never restored.- With feature-flag fetching off, a task you asked to keep across sessions is saved to the project's
.claude/scheduled_tasks.json. If.claudeor that file is a symlink, scheduling fails with an error. Saved tasks only run in the folder where you created them; copying the file into another folder (a new worktree, say) lists the tasks there but does not run them.
For unattended cron-style automation, use routines, a schedule trigger in GitHub Actions, or desktop scheduled tasks.