Schedule recurring and one-time AI tasks with Claude Code — powered by native OS schedulers (launchd / crontab).
Scheduling
- Natural language — "every weekday at 9am", "daily at 5pm", "every Monday at 10am"
- Cron expressions — standard 5-field cron syntax with full validation
- One-time tasks — schedule a task to run once at a specific time
Execution
- Worktree isolation — run tasks in isolated git worktrees to avoid interfering with your working copy
- Shared executor — single Node.js executor for all tasks, reading config at runtime (no per-task wrapper scripts)
- Run-to-run memory — optional context injection from previous output so recurring tasks focus on new/changed items
Operations
- Execution history — JSONL-based history with filtering by status, task, and project
- Log management — stdout/stderr capture with rotation and cleanup
- Security built-in — env blocklist, sensitive file detection, shell escaping, trust boundary enforcement
- Node.js >= 18
- Claude Code CLI (
claude) - macOS (launchd) or Linux (crontab)
claude plugin install @dortort/schedulerThen in any Claude Code session:
"Schedule a daily code review at 9am"
The plugin handles cron setup, OS scheduler registration, and worktree isolation automatically.
Or for local development:
claude --plugin-dir /path/to/claude-code-scheduler| Command | Description |
|---|---|
/scheduler:add |
Add a new scheduled task (NL or cron) |
/scheduler:list |
List all configured tasks with status |
/scheduler:edit |
Modify a task's schedule, command, or settings |
/scheduler:remove |
Remove a task by ID or name |
/scheduler:enable |
Enable a disabled task |
/scheduler:disable |
Disable a task without removing it |
/scheduler:status |
Health check for the scheduling system |
/scheduler:run |
Manually trigger a task |
/scheduler:logs |
View stdout/stderr logs for a task |
/scheduler:history |
View execution history with filters |
The plugin is organized as a set of focused modules:
src/
types.ts Zod schemas, task factory, validation
config.ts Config load/save/merge with trust boundaries
index.ts Public API re-exports
cli/
executor.ts Shared task executor (direct + worktree modes)
index.ts CLI entry point (add, remove, update, sync, init, migrate)
platform.ts Platform-aware OS scheduler registration
commands/ CLI subcommands (add, remove, update, sync, init, migrate)
cron/
parser.ts Cron validation, NL-to-cron, next runs
humanizer.ts Cron-to-human-readable, date/duration formatting
logs/
index.ts Log dir management, rotation, cleanup
history/
index.ts JSONL execution history, querying, cleanup
vcs/
index.ts Git worktree lifecycle, sensitive file detection
schedulers/
base.ts Shared scheduler utilities
darwin.ts macOS launchd plist generation
linux.ts Linux crontab management with markers
index.ts Platform detection factory
utils/
shell.ts Shell escaping, input sanitization
exec.ts Thin child_process wrapper with DI
Tasks are stored in JSON config files:
- Global:
~/.claude/schedules.json— your personal tasks - Project:
<project>/.claude/schedules.json— shared team tasks
Global config takes precedence on ID collision. Project configs cannot set skipPermissions.
npm install
npm test # Unit/integration tests
npm run test:e2e # E2E tests via Claude CLI subprocess (~9 min)
npm run lint # ESLint with typescript-eslint
npm run typecheck # Type checking only
npm run build # TypeScript compilation
npm run test:coverage # Tests with coverage reportCI runs automatically on every push and PR to main (lint, typecheck, test on Node 18+22, build).
The test suite has two tiers:
- Unit/Integration (
npm test) — fast tests covering library functions with no external dependencies. - E2E (
npm run test:e2e) — subprocess tests that invoke each plugin command throughclaude --plugin-dir. Requires theclaudeCLI to be installed and takes ~9 minutes. Skipped automatically if the CLI is not available. E2E tests use temp directories with fixture data and assert on output patterns (not exact strings) to handle Claude's non-deterministic phrasing.
Publishing is automated via GitHub Actions when a GitHub Release is created. The release workflow requires:
- An
npmenvironment configured in the repository settings - An
NPM_TOKENsecret with publish access
Enable branch protection on main requiring these status checks to pass:
- Lint
- Typecheck
- Test (Node 18)
- Test (Node 22)
- Build
- Fork the repo and create a feature branch from
main - Install dependencies:
npm install - Make your changes — the project uses TypeScript in strict mode with ESLint
- Run checks before submitting:
npm run lint # ESLint npm run typecheck # TypeScript npm test # Unit/integration tests npm run build # Compilation
- Open a pull request against
main
MIT
