Codex-automations-style scheduled tasks for every agent harness and every model — open source and harness-agnostic.
scheduledtask runs recurring prompts headlessly on a schedule, captures the results in an inbox, isolates risky runs in git worktrees, and stays quiet when nothing changed. It works with OpenAI Codex CLI, Claude Code, opencode, or any command you can shell out to.
Packaged as an Agent Plugins v1.0.0 plugin with an agent-facing skill, so your coding agent can manage schedules for you.
OpenAI ships automations only inside the ChatGPT desktop app; Anthropic ships its own inside Claude Code. Everyone else gets nothing. scheduledtask is the missing open layer:
- Any harness — codex / claude-code / opencode / arbitrary shell commands.
- Any model — pass
-m-style model ids straight through (gpt-5.6-luna,opencode/x-preview-f-free, whatever your harness supports). - Two run modes, matching Codex automations:
standalone— fresh session every run; results land separately in the inbox.thread— resumes the previous session so each check builds on the last.
- Real schedules — cron expressions and RFC 5545 RRULEs
(
FREQ=MONTHLY;BYMONTHDAY=1;BYHOUR=9), evaluated in local wall-clock time. - Worktree isolation — each run can execute in a detached-HEAD worktree so background changes never touch your dirty checkout.
- Dedup + triage inbox — identical findings collapse to silent
no-changeruns; unread markers, archive, pin. - Missed-run policies — laptop asleep? Choose
latest,all, orskip. - Notifications — macOS Notification Center out of the box.
Requires Bun — bins are Bun-native TypeScript.
git clone https://github.com/Dhruv2mars/scheduledtask ~/.scheduledtask-cli
echo 'alias scheduledtask="bun ~/.scheduledtask-cli/src/cli.ts"' >> ~/.zshrc
scheduledtask doctor # verify harnesses are visibleOne of codex, claude, or opencode must be on $PATH (or use the
shell harness).
cd my-project
scheduledtask init # creates .scheduledtask/tasks/example.yaml
$EDITOR .scheduledtask/tasks/example.yaml # set prompt + enabled: true
scheduledtask run example # test it manually FIRST
scheduledtask inbox # read the report
scheduledtask daemon # start schedulingscheduledtask add --name nightly-brief \
--cron "0 9 * * *" \
--harness opencode \
--model opencode/x-preview-f-free \
--prompt "Summarize yesterday's commits grouped by workstream." \
--worktree| Field | Values | Default |
|---|---|---|
schedule.cron |
cron string | — |
schedule.rrule |
RFC 5545 RRULE string | — |
harness |
claude-code | codex | opencode | shell |
codex |
model |
harness-native model id | harness default |
effort |
low | medium | high |
harness default |
sandbox |
read-only | workspace-write | full |
workspace-write |
mode |
standalone | thread |
standalone |
worktree |
boolean | false |
cleanup_worktree |
remove worktree even with changes | false |
projects |
list of dirs — one run per project per slot | task's repo |
dedup |
collapse identical findings | true |
catch_up |
latest | all | skip after downtime |
latest |
notify |
[macos] |
[macos] |
timeout_minutes |
kill switch per run | 30 |
Sandbox mapping is honest per-harness, not lowest-common-denominator:
| sandbox | codex | claude-code | opencode |
|---|---|---|---|
read-only |
-s read-only |
default | default |
workspace-write |
-s workspace-write |
--permission-mode acceptEdits |
--auto |
full |
bypass approvals+sandbox | --dangerously-skip-permissions |
--auto (no stronger escalation exists) |
Thread mode uses native resume where available (codex exec resume <id>,
claude --resume <id>, opencode -s <id>).
Tasks found in your repo (.scheduledtask/tasks/) or inside plugins
(dev.scheduledtask/tasks/) are checked-in code that will execute unattended.
scheduledtask therefore refuses to run them until you mark their tree safe:
scheduledtask trust # allow repo/plugin tasks under cwd
scheduledtask untrust # revokeUser-authored tasks (~/.scheduledtask/tasks/) always run. sandbox: full
is rejected at load time for anything not user-authored.
The shell harness never interpolates prompt text into the command line
(prompts embed model output and repo content). Use {{prompt_file}}, which
expands to a path holding the prompt:
harness: shell
command: 'run-analysis.sh "$(cat {{prompt_file}})"'Runs land unread; show marks them read; read-all bulk-clears.
Archiving an unpinned run releases its worktree; pinned runs keep theirs;
scheduledtask gc reclaims archived-unpinned worktrees and prunes history
(200 runs/task by default, override with gc --keep <n>).
This repo is itself a valid Agent Plugin (plugin.json). Tasks can also be
distributed inside other plugins:
my-plugin/
├── plugin.json
└── dev.scheduledtask/
└── tasks/
└── nightly-bugfix.yaml
Discovery scans <repo>/.agents/plugins/*/dev.scheduledtask/tasks/ and
~/.agents/plugins/*/dev.scheduledtask/tasks/ automatically. The bundled skill
(skills/manage-scheduledtask) teaches any Agent Skills-compatible harness how
to create and operate tasks through the CLI.
# macOS launchd (keep the daemon alive across logins)
brew install services # not required; use a plist pointing at:
bun /path/to/scheduledtask/src/cli.ts daemonThe daemon sweeps every 30 s and handles missed slots on wake via each task's
catch_up policy. For servers, wire scheduledtask sweep into systemd timers
or cron as a watchdog.
bun install
bun test # unit + security regression tests
bun run typecheck # strict TS, zero errors
bun src/cli.ts doctorWorking: standalone + thread modes, cron/RRULE, worktrees, dedup inbox, notifications, multi-project tasks, plugin-distributed tasks, trust gate for checked-in tasks, three first-class adapters plus shell escape hatch.
Next: TUI inbox browser, Slack/ntfy notification channels, cloud catch-up
runner for sleeping laptops, tasks/ component-type proposal upstream to the
Agent Plugins spec.
MIT licensed. Not affiliated with OpenAI, Anthropic, or SST.