Your AI agents’ crontab, as markdown.
OpenRoutine is the open-source alternative to Claude Code Routines and ChatGPT’s scheduled tasks — the same unattended-agent idea, with the vendor locks removed. One markdown file per task, crontab syntax in the frontmatter, any AI coding agent underneath. Tasks live in your repo, run on your machine, and answer to no account, plan, or cap.
brew tap soulmachine/tap https://github.com/soulmachine/openroutine
brew trust --formula soulmachine/tap/openroutine
brew install openroutine
openroutine init # a config, and a sample task to copy
A task is a file
The frontmatter says what, when, and who. The body is the prompt. There is no second place to look.
---
name: todo-digest
description: Nightly TODO/FIXME triage
cron: "0 2 * * *"
agent: claude
---
Review all open TODO and FIXME comments in this repository.
For any that are trivially fixable, fix them and open a pull
request. Summarize everything else in reports/todo-digest.md.
Save that in your repo as todo-digest.md, register it once with
openroutine add, and it is the complete definition — diffed,
code-reviewed, and greppable like everything else you commit.
A single Rust daemon runs your registered task files: it schedules everything in-process, runs each task through the agent CLI you configure, and serves a REST fire endpoint plus a local web UI for history, logs, and pause/resume.
Swap agent: claude for agent: codex — one line in a diff —
and the same task runs on the other vendor’s agent, which is precisely the move
neither vendor’s scheduler will ever offer.
Built on four positions
A scheduled agent task is just a prompt with a schedule attached. Everything else follows from taking that literally.
- Tasks live in your repo
-
Every field — schedule, agent,
disabled— is frontmatter in a markdown file. Turning a task off is a commit your reviewer can see, not invisible state in someone’s account. - The scheduler is the daemon
-
Not launchd, not crontab — those run agents in a stripped environment with no
PATHor API keys, and are where agent jobs quietly go to die. Owning the scheduler buys identical behaviour on macOS and Linux, real environments, and honest bookkeeping. - Agent-agnostic by construction
- OpenRoutine never talks to a model API. An agent is just a command template, so any CLI that accepts a prompt works — Claude Code, Codex, Gemini, or your own.
- One binary, zero databases
- The scheduler, the API, and the UI in one static Rust binary. All durable data is two plain-text formats: markdown you own, JSON the machine owns.
Compared to the vendors
The major vendors proved that coding agents are good enough to work unattended. Each one locks the recurring work to its own stack.
| Capability | Claude Code Routines | ChatGPT scheduled tasks | OpenRoutine |
|---|---|---|---|
| Where it runs | Anthropic-managed cloud (or org-hosted environments) | OpenAI cloud; project tasks run locally, but only while the desktop app is running | Your machine, under a headless daemon supervised by launchd/systemd |
| Where tasks are defined | Web UI / /schedule, stored in your claude.ai account |
Chat or the Scheduled page, stored in your OpenAI account | Markdown files in your repo |
| Agents | Claude Code only | GPT models only | Any agent CLI: Claude Code, Codex, Gemini, your own |
| Triggers | Schedules, API endpoint, GitHub events | Schedules and change-monitoring, hourly at most | Cron and one-shot schedules; REST fire endpoint |
| Management UI | claude.ai web UI | Scheduled page in the app | Local web UI, no account |
| Limits | Subscription usage, daily run caps | 3–15 active tasks by plan; unattended tasks may auto-pause | Whatever your hardware tolerates |
| Availability | Research preview, paid plans | Paid plans | Open source |
The trade is honest in every direction: Routines gives you cloud execution, GitHub-event triggers, and managed sandboxing; ChatGPT gives you a polished cross-device inbox and change-monitoring; OpenRoutine gives you local files, local execution, no caps, and the freedom to swap the agent.
Using it
Requires a Rust toolchain; macOS and Linux only (Windows is an explicit non-goal).
openroutine list # every task, its schedule, and its health
openroutine status # is the daemon up, and what does it hold
openroutine run <task> --dry-run # exactly what a run would do, spawning nothing
openroutine run <task> # fire one now, through the daemon
openroutine logs <task> --follow # tail the latest run
openroutine pause --all # stop everything firing, keep the daemon up
openroutine dashboard # the local web UI, no account
Tasks are files, so everything else is ordinary editing: register one with
openroutine add, then edit it freely — a task re-reads its own file as it is
about to run, so the prompt you just fixed is the one that runs. Nothing is watched or
polled; openroutine reload is what makes an edit visible everywhere else, and
what a task that isn’t going to run — finished, broken, or switched off with
disabled — needs before it notices at all.
To leave it running on a machine you don’t sit at, openroutine install
registers the daemon with launchd or systemd — a per-user service, never sudo.
Deployment covers the details, including the login-shell
PATH trap that catches everyone once.
Anything can fire a task
The daemon serves a REST API on 127.0.0.1:7373, guarded by a bearer token, so
alerting systems, deploy pipelines, and git hooks can start a run.
curl -X POST http://127.0.0.1:7373/v1/tasks/todo-digest/fire \
-H "Authorization: Bearer $(openroutine token)" \
-d '{"text": "Sentry alert SEN-4521 fired in prod."}'
The optional text reaches the agent labelled as caller-supplied context, not
as instructions — anyone who can reach the endpoint can send text, so text must not be able
to redefine the task.
Status
v1 is implemented: scheduler, runner, REST API, and web UI, in one binary.
Known limits, all deliberate: no GitHub-event triggers and no notifications (the fire endpoint is the integration point); state is written atomically against a killed process but is not fsynced against power loss; the web UI’s rendering is verified by hand rather than by a browser harness.