Remote Control assumes you are there, watching. Scheduling assumes you are not. If what you actually want is for Claude to check on something every few minutes and tell you when it changes, a scheduled task is the tool, and there are three quite different implementations of it.
Quick version: /loop 5m check the deploy polls while your session stays open. For scheduling that survives a closed terminal, you want Routines (cloud) or Desktop scheduled tasks instead.
Three implementations, one idea
| Cloud (Routines) | Desktop tasks | /loop | |
|---|---|---|---|
| Runs on | Anthropic cloud | 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 if unexpired |
| Local files | No, fresh clone | Yes | Yes |
| Permission prompts | None, runs autonomously | Configurable per task | Inherits from session |
| Minimum interval | 1 hour | 1 minute | 1 minute |
Pick by the third row. /loop is session-scoped, so it stops the moment the session does. That is fine for polling a build you are waiting on, and useless for a nightly job. If you need durable scheduling, the answer is Routines or Desktop tasks, not a longer loop.
Polling inside a session with /loop
Both the interval and the prompt are optional, and which ones you supply changes the behaviour:
| You provide | Example | Result |
|---|---|---|
| Interval and prompt | /loop 5m check the deploy | Runs on a fixed cron schedule |
| Prompt only | /loop check the deploy | Claude picks the interval each iteration |
| Neither | /loop | Runs the built-in maintenance prompt, or your loop.md |
Units are s, m, h, d. The interval can lead as a bare token or trail as a clause, so /loop 30m ... and /loop ... every 2 hours both work. Seconds round up to the nearest minute because cron has one-minute granularity, and awkward intervals like 7m or 90m get rounded to something cron can express, with Claude telling you what it chose.
Letting Claude pick the interval
Omit the interval and Claude chooses a delay between one minute and one hour after each iteration, based on what it saw. Short waits while a build is finishing, longer ones once things go quiet. It prints the delay and its reasoning at the end of each pass.
/loop check whether CI passed and address any review comments
Worth knowing: for this kind of request Claude may reach for the Monitor tool instead, which runs a background script and streams each line back. That avoids polling altogether and is usually both faster to react and cheaper in tokens.
The built-in maintenance prompt
A bare /loop runs a prompt Anthropic ships rather than one you wrote. Each iteration works through, in order: continuing unfinished work from the conversation, tending the current branch’s pull request (review comments, failed CI, merge conflicts), then cleanup passes like bug hunts when nothing else is pending.
It deliberately does not start new initiatives, and irreversible actions such as pushing only proceed when they continue something the transcript already authorised. That constraint is what makes a bare /loop reasonable to leave running.
Replacing the default with loop.md
Drop a loop.md file in place and it replaces the built-in prompt. Project level wins over user level:
| Path | Scope |
|---|---|
.claude/loop.md | Project. Takes precedence when both exist |
~/.claude/loop.md | User. Applies in any project without its own |
It is plain Markdown with no required structure, written as if you were typing the prompt. Edits take effect on the next iteration, so you can refine it while the loop runs. Content past 25,000 bytes is truncated. Note that it defines the single default for bare /loop, not a list of jobs, and is ignored whenever you pass a prompt yourself.
Stopping one
Press Esc while the loop waits for its next iteration. That clears the pending wakeup. Tasks you created by asking Claude in natural language are not affected by Esc and stay until you delete them.
In self-paced mode Claude can also end the loop itself once the work is done, by calling ScheduleWakeup with stop: true. If an iteration ends without either rescheduling or stopping, Claude Code schedules one fallback wakeup roughly 20 minutes later and ends the loop if that one does not reschedule either.
One-off reminders
No command needed. Describe it and Claude schedules a single-fire task that deletes itself after running.
remind me at 3pm to push the release branch
in 45 minutes, check whether the integration tests passed
Listing and cancelling
Ask in plain language, or reference the tools directly. Each task gets an 8-character ID, and a session holds up to 50 at once.
| Tool | Purpose |
|---|---|
CronCreate | Schedule a task from a 5-field cron expression, a prompt, and whether it recurs |
CronList | List tasks with IDs, schedules, and prompts |
CronDelete | Cancel a task by ID |
Two behaviours that will surprise you
Jitter
The scheduler deliberately offsets fire times so every session does not hit the API on the same wall-clock second. Recurring tasks fire up to 30 minutes after the scheduled time, or up to half the interval for anything more frequent than hourly. One-shot tasks set for the top or bottom of the hour fire up to 90 seconds early.
The offset comes from the task ID, so it is stable for a given task. If exact timing matters, avoid :00 and :30: schedule 3 9 * * * rather than 0 9 * * * and the one-shot jitter does not apply.
Seven-day expiry
Recurring tasks expire seven days after creation. The task fires one last time and deletes itself, which bounds how long a forgotten loop can run. That includes dynamically paced loops. For anything that needs to outlive a week, use Routines or Desktop tasks rather than recreating the loop.
Cron reference
CronCreate takes standard 5-field expressions: minute hour day-of-month month day-of-week.
| Expression | Meaning |
|---|---|
*/5 * * * * | Every 5 minutes |
0 * * * * | Hourly on the hour |
7 * * * * | Hourly at 7 past |
0 9 * * * | Daily at 9am local |
0 9 * * 1-5 | Weekdays at 9am local |
30 14 15 3 * | 15 March at 2:30pm local |
All times are your local timezone, not UTC. Day-of-week is 0 or 7 for Sunday through 6 for Saturday. Extended syntax such as L, W, ? and name aliases like MON is not supported. When both day-of-month and day-of-week are constrained, a date matches if either field matches, following vixie-cron semantics.
Limitations
- Tasks fire only while Claude Code is running and idle. A scheduled prompt lands between turns, never mid-response. Close the terminal and nothing fires.
- No catch-up. If the scheduled time passes while Claude is busy, the task fires once when Claude goes idle, not once per missed interval.
- A fresh conversation clears everything.
--resumeor--continuerestores recurring tasks created within seven days and one-shots whose time has not passed. Background Bash and monitor tasks are never restored. - Symlinked
.claudedirectories fail. From v2.1.216 scheduling errors out when that directory or the task file is a symlink, where earlier versions wrote through the link.
To disable the scheduler entirely, set CLAUDE_CODE_DISABLE_CRON=1. The cron tools and /loop both become unavailable and existing tasks stop firing.
How this sits next to Remote Control
They solve opposite halves of the same problem. Scheduling handles work that should happen without you. Remote Control handles work you want to watch and redirect from wherever you are. The pairing that actually gets used: leave a /loop running in a session, attach Remote Control to it, and check the loop’s findings from your phone.
Since /loop needs the session open, it inherits the same constraint as Remote Control, so an always-on machine helps both. To react to events instead of polling for them, see channels.