cron-claude
v2.2.7
Published
Schedule Claude jobs in agent loop mode — no daemon, full tool access
Maintainers
Readme
cron-claude
Schedule recurring Claude jobs with cron expressions. Claude Code acts as the executor — no daemon, no background process. Jobs run as subagents with full tool access.
How It Works
Claude Code runs an agent loop via the /cron-loop slash command. Every 60 seconds it checks for due jobs, spawns a subagent for each one, and each subagent executes the job's prompt with full MCP tool access (bash, files, APIs, etc.). Job state is managed on disk to prevent duplicate execution.
Quickstart
npm install -g cron-claude
cron-claude install # sets up MCP server + slash commands
# Add a job
cron-claude add --id daily-standup --schedule "0 9 * * 1-5" --prompt "Post standup summary to Slack"
# In Claude Code, type:
/cron-loopThat's it. The loop runs indefinitely, checking for due jobs every 60 seconds.
Setup
cron-claude install does two things:
- MCP server — adds
cron-claude-mcpto~/.claude/settings.json - Slash commands — installs
/cronand/cron-loopinto~/.claude/commands/
You can also run these separately:
cron-claude mcp install # just the MCP server
cron-claude skill install # just the slash commandsSlash Commands
| Command | Purpose |
|---------|---------|
| /cron | Manage jobs via natural language (add, list, remove, enable, disable) |
| /cron-loop | Start the agent loop executor |
CLI Reference
cron-claude list
cron-claude add --id <id> --schedule "<cron>" --prompt "<prompt>" [--prompt-file <path>] [--model <model>] [--disabled]
cron-claude edit <id> [--schedule "<cron>"] [--prompt "<prompt>"] [--prompt-file <path>] [--model <model>] [--enable] [--disable]
cron-claude show <id>
cron-claude remove <id>
cron-claude enable <id>
cron-claude disable <id>
cron-claude status
cron-claude install # one-time setup: MCP + slash commands
cron-claude mcp install # MCP server only
cron-claude skill install # slash commands only
cron-claude start-loop # print loop operating instructions
cron-claude get-due-jobs # get due jobs as JSON (used by loop)
cron-claude mark-job-run <id> [--output <text>] [--error <text>] # mark job complete (used by loop)add flags
| Flag | Required | Default | Description |
|------|----------|---------|-------------|
| --id <id> | ✅ | — | Unique job identifier |
| --schedule "<cron>" | ✅ | — | Cron expression (5-field: min hour day month weekday) |
| --prompt "<prompt>" | ✅* | — | Inline prompt text (mutually exclusive with --prompt-file) |
| --prompt-file <path> | ✅* | — | Path to a .md/.txt file containing the prompt (mutually exclusive with --prompt) |
| --model <model> | — | sonnet | Model for the job |
| --disabled | — | false | Create in disabled state |
*One of --prompt or --prompt-file is required.
edit flags
Edit a job in place — only the flags you provide are updated; everything else stays as-is.
cron-claude edit my-job --schedule "0 10 * * *"
cron-claude edit my-job --prompt "New prompt text"
cron-claude edit my-job --prompt-file ./prompts/daily.md --model opus --enable| Flag | Description |
|------|-------------|
| --schedule "<cron>" | New cron schedule |
| --prompt "<prompt>" | New inline prompt (clears --prompt-file if set) |
| --prompt-file <path> | New prompt file path (clears inline prompt if set) |
| --model <model> | New model |
| --enable | Enable the job |
| --disable | Disable the job |
show
Show full details of a single job, including the complete untruncated prompt. If the job uses a prompt file, shows the file path and its contents.
cron-claude show my-joblist truncates prompts to 40 characters — use show to see the full text.
Cron schedule examples
| Expression | Meaning |
|-----------|---------|
| 0 9 * * 1-5 | Weekdays at 9:00 AM |
| 0 14 * * * | Daily at 2:00 PM |
| */30 * * * * | Every 30 minutes |
| 0 0 * * 0 | Sundays at midnight |
MCP Tools
Available when the MCP server is installed:
| Tool | Description |
|------|-------------|
| start_loop | Returns loop operating instructions. Call this first. |
| get_due_jobs | Returns jobs due now as JSON. Atomically marks them running on disk. Call every 60s. |
| mark_job_run | Records job completion. Clears running status and writes log. Subagents must call this. |
| add_cron | Add a scheduled job |
| remove_cron | Remove a job by ID |
| enable_cron | Enable a job |
| disable_cron | Disable a job |
| list_crons | List all jobs (JSON) |
| edit_cron | Edit a job in place (partial update — only provided fields change) |
| show_cron | Show full details of a single job including complete prompt/file contents |
| get_status | Job counts (total, enabled, disabled, running) |
Job State & Duplicate Prevention
Jobs are stored in ~/.cron-claude/jobs.json. Each job has a status field (idle or running) and a lastRunAt timestamp.
How duplicates are prevented:
get_due_jobsatomically marks returned jobs asrunningbefore returning them- Jobs with
status: "running"are skipped by subsequentget_due_jobscalls - A 55-second
lastRunAtguard prevents the same job from firing twice in one cycle - The cron match uses a 90-second window from the previous cron fire time
mark_job_runresets the job toidleand updateslastRunAt
If a subagent crashes without calling mark_job_run, the job stays locked as running. To reset it, call mark_job_run (or mark-job-run via CLI) with an error message for the stuck job.
Logs are written to ~/.cron-claude/logs/<job-id>_<timestamp>.log.
The Agent Loop Protocol
When /cron-loop is invoked, Claude Code:
- Calls
start_loop(MCP tool) to get operating instructions - Every 60 seconds, calls
get_due_jobs - For each due job, spawns a subagent with the job's prompt
- Each subagent has full tool access and must call
mark_job_runwhen done - Subagents run in parallel — the loop doesn't wait for completion
- The loop runs indefinitely until the user stops it
get_due_jobs reads live from disk on every call. Jobs added by other agents or via the CLI appear automatically in the next cycle — no restart needed.
Editing a Job
Use edit to modify a job in place — only the fields you specify are changed:
cron-claude edit my-job --schedule "0 10 * * *"
cron-claude edit my-job --prompt "New prompt" --model opusOr via MCP: call edit_cron with the job ID and any fields to update.
For Claude
If you're an AI agent reading this README:
- To run scheduled jobs: Call
start_loop, then loop forever callingget_due_jobsevery 60 seconds. Spawn subagents for each job. Each subagent MUST callmark_job_runwhen done. - Empty results are normal.
get_due_jobsreturningcount: 0does NOT mean stop. Wait 60 seconds and call again. New jobs can appear at any time. - Never execute jobs in the main loop. Always delegate to subagents so the loop stays responsive.
- The
nextActionfield inget_due_jobsresponses tells you exactly what to do next. Follow it.
License
MIT
