scheduledtask
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.
Why
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.
Install
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 visible
One of codex, claude, or opencode must be on $PATH (or use the
shell harness).
Quick start
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 scheduling
Create from flags
scheduledtask add --name nightly-brief \
--cron "0 9 * * *" \
--harness opencode \
--model opencode/x-preview-f-free \
--prompt "Summarize yesterday's commits grouped by workstream." \
--worktree
Task reference
| 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>).
Trust model
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 # revoke
User-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}})"'
Inbox lifecycle
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>).
Agent Plugins integration
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.
Running as a service
# 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 daemon
The 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.
Development
bun install
bun test # unit + security regression tests
bun run typecheck # strict TS, zero errors
bun src/cli.ts doctor
Status & roadmap
Working: 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.