Give your coding agent a calendar. ⏰
Schedule jcode sessions like cron jobs, except they catch up after your
laptop wakes, they remember their session so you can jump back in, and you can
ask the agent to schedule its own follow-ups.
Install · How it works · Commands · Let jcode schedule itself
Close your laptop at 9:29 AM. Open it at lunch. Your 9:30 routine already ran.
jcsched add --name "Morning triage" --cron "30 9 * * 1-5" \
--prompt "Review open PRs and summarize anything risky or stale."That is the whole idea. Create a routine once and let it run quietly in the background. When your Mac wakes after missing a scheduled time, the scheduler catches up according to the policy you chose.
| 🍎 Properly native | Menu bar item, real .app, macOS notifications |
| 😴 Sleep-aware | Missed runs are caught up on wake, not silently dropped |
| ↩️ Resumable | Every run keeps its jcode session, so you can pick it up |
| 🤖 Self-scheduling | An MCP server, so jcode can book its own follow-ups |
| 🪶 Featherweight | Rust, zero external crates, one ~800KB binary |
| 🔒 Yours alone | Loopback only, plain files under ~/.jcode/scheduler |
- Install
- How scheduling works
- Commands
- Picking up a session
- Letting jcode schedule its own routines
- Menu bar
- Dashboard
- Configuration
- Design notes
Requires macOS, the jcode CLI on your PATH, and Rust to build.
git clone https://github.com/tayoonabule/jcode-scheduler.git && cd jcode-scheduler
cargo build --release
./target/release/jcsched installOr grab a prebuilt binary from Releases, verify it, and install:
shasum -a 256 -c jcsched-*.tar.gz.sha256
tar -xzf jcsched-*.tar.gz && cd jcsched-*/ && ./jcsched installinstall copies the binary to ~/.local/bin/jcsched, registers a macOS
LaunchAgent that starts at login and survives sleep, installs the
schedule-routine agent skill, then prints the dashboard URL. Run
jcsched doctor if anything looks wrong.
Add ~/.local/bin to your PATH if it is not there already:
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc && exec zshjcsched app # adds "jcode Scheduler" to ~/Applications
jcsched menubar --install # keeps the menu bar item across loginsjcsched app builds a small .app around the same binary, icon and all, so
you can launch the scheduler from Spotlight or Launchpad like anything else.
Opening it starts the daemon if needed, puts up the menu bar item, and opens
the dashboard. The icon is rendered from code at install time, so there are no
binary assets in this repo.
jcsched add --name "Morning triage" --cron "30 9 * * 1-5" \
--cwd ~/code/app \
--prompt "Review open PRs and summarize the risks"
jcsched openOpen the local dashboard with jcsched open or visit the printed URL. Use
Create with jcode when you want to describe an outcome in plain language.
It drafts a routine, opens the normal editor for review, and never saves until
you explicitly press Save routine. Start manually opens the same editor
without an assistant draft.
| Surface | Best for |
|---|---|
| Dashboard | Creating routines, previewing schedules, and reading logs |
| Menu bar | Quick actions, catch-up, pause/resume, and recent runs |
| CLI | Scripting, automation, and troubleshooting |
| MCP | Asking jcode to create or manage routines for you |
Every routine has one required primary folder and may include optional
additional folders. The primary folder is the default jcode working directory;
additional folders are passed into the run prompt as absolute paths so a routine
can intentionally work across multiple projects without changing the existing
single-folder behavior.
In the dashboard, enter one additional absolute folder per line. From the CLI, use a comma-separated list:
jcsched add --name "Cross-project review" --every 1d \
--cwd ~/code/app \
--additional-cwds ~/code/api,~/code/docs \
--prompt "Review the application, API, and docs together"The MCP schedule_routine tool accepts the same additional_cwds array while
keeping cwd as the primary folder.
Four schedule kinds:
| Kind | Example | Meaning |
|---|---|---|
| Cron | --cron "30 9 * * 1-5" |
Standard 5-field cron, weekdays at 9:30 AM |
| Interval | --every 2h |
Every two hours from the last run |
| One-shot | --at "tomorrow 9am" |
Runs once, then stops |
| Manual | (default) | Only when you trigger it |
Cron supports ranges, steps, lists, month and day names, and the @daily
family. Day-of-week and day-of-month follow Vixie cron union semantics: if both
are restricted, either one matching fires the job.
Every job has a catch-up policy.
once(default) runs the job promptly if one or more occurrences were missed while the machine was asleep or off. It runs once, not once per missed slot.skipignores what was missed and waits for the next scheduled time.
The daemon detects a wall-clock gap of more than 90 seconds between ticks and treats it as a wake event, so opening your laptop triggers the sweep.
jcsched install | uninstall | start | stop | restart | status | doctor
jcsched menubar Run the menu bar app
jcsched app Create the app in ~/Applications
jcsched open Open the dashboard
jcsched list List jobs
jcsched add [options] Create a job
jcsched rm <id> Delete a job
jcsched enable <id> | disable <id>
jcsched run <id> [--follow] Run now, optionally streaming the log
jcsched logs <run-id> Print a run log
jcsched logs <job-id> --last Print the latest log for a job
jcsched resume <run-id> Reopen that run's jcode session in your terminal
jcsched wake Force a catch-up sweep
Every run records the jcode session it created, so a scheduled run is not a
dead end. Open it back up and keep going interactively:
jcsched resume r_abc123 # a specific run
jcsched resume j_xyz789 --last # the latest run of a job
jcsched resume r_abc123 --in tmux # override the terminal onceThe dashboard and the menu bar offer the same thing on any run that reached the
model. Under the hood it is just jcode --resume <session-id>, so you can copy
that command and run it yourself if you prefer.
Set "Open sessions in" under Settings, or the terminal field in state.json.
Leave it empty and the scheduler detects what you have.
| Value | Behavior |
|---|---|
cmux |
New cmux workspace in your current window |
tmux |
New tmux session, then attached |
ghostty |
New Ghostty window |
iterm |
New iTerm window |
terminal |
New Terminal window |
| anything else | Treated as a command template |
A template gets {cmd} and {cwd} substituted, so any terminal works:
wezterm start --cwd {cwd} -- /bin/sh -lc {cmd}
kitty --directory {cwd} /bin/sh -lc {cmd}
jcsched resume opens the terminal from the CLI process itself, so running it
from a cmux window works without any setup.
The dashboard and menu bar ask the background daemon to do the opening, and cmux refuses connections from processes that did not start inside cmux. To use those surfaces with cmux, set a socket password in cmux Settings and hand it to the agent:
launchctl setenv CMUX_SOCKET_PASSWORD "your-password"
jcsched restartAny other terminal works from every surface with no configuration.
--name <text> Job name (required)
--prompt <text> Prompt sent to jcode (required)
--cwd <path> Working directory (default: current directory)
--additional-cwds <paths> Optional comma-separated extra folders
--cron "<expr>" 5-field cron expression
--every <duration> Interval: 30m, 2h, 1d, 1h30m
--at <when> One shot: "2026-08-11 09:00", "tomorrow 9am", "in 2h"
--provider <name> jcode provider override
--model <name> jcode model override
--timeout <duration> Kill the run after this long
--catch-up <mode> once (default) or skip
The scheduler ships an MCP server, so an agent can create a routine itself instead of telling you to go do it. Ask jcode to "check the deploy again in two hours" or "review my PRs every weekday morning" and it will schedule it.
Register it once, using an absolute path (jcsched status prints the binary's
location, and most MCP clients do not expand ~):
The dashboard's Integrations panel shows this snippet with your real path filled in, ready to copy.
Tools exposed: schedule_routine, list_routines, pause_routine,
cancel_routine, run_routine_now.
jcsched install also writes a skill to
~/.agents/skills/schedule-routine/SKILL.md that
teaches the model when to reach for these, and how to write a prompt that still
makes sense when it runs a week later with no conversation around it.
jcsched mcp # run the server directly, for debuggingjcsched menubar puts a small item in the macOS menu bar showing how many runs
are active. The dropdown lists upcoming jobs, lets you run one immediately,
reopens a recent session, forces a catch-up, and pauses everything.
To keep it there across logins:
jcsched menubar --install # start it at every login
jcsched menubar --uninstall # stop doing thathttp://127.0.0.1:4573, loopback only. It lists jobs, shows the next fire
times, streams live run output, and keeps run history with full logs. The
schedule editor previews the next five runs as you type.
~/.jcode/scheduler/
state.json jobs, run index, settings
logs/ one file per run
daemon.json pid, port, start time
daemon.log daemon stdout and stderr
~/Library/LaunchAgents/com.jcode.scheduler.plist
Settings live in the dashboard, or edit state.json and restart.
| Setting | Default | Meaning |
|---|---|---|
port |
4573 | Dashboard port |
max_concurrent |
2 | Jobs allowed to run at once |
keep_runs_per_job |
50 | Run history depth before pruning |
notify_on_failure |
true | macOS notification when a run fails |
notify_on_success |
false | macOS notification when a run succeeds |
terminal |
auto | Terminal used to reopen sessions |
Set JCSCHED_HOME to relocate state, and JCODE_BIN to point at a specific
jcode binary.
Start here, always:
jcsched doctorIt checks the binary, the LaunchAgent plist, whether the agent is loaded,
whether the daemon answers, and whether jcode is on the launchd PATH. Every
failure prints the command that fixes it.
| Symptom | Fix |
|---|---|
| Menu bar says "Scheduler not running" | jcsched start. If that does not stick, jcsched install rewrites the LaunchAgent. |
| Runs fail instantly | jcode is not on the launchd PATH. Put it in ~/.local/bin and rerun jcsched install. |
| "cannot bind 127.0.0.1:4573" | Another process holds the port. Change port in Settings, then jcsched restart. |
| Nothing ran while the lid was closed | Expected. Missed runs fire on wake, per each job's catch-up policy. |
| The app icon looks generic | jcsched app rebuilds the bundle and re-registers it with Launch Services. |
| Something else | tail -f ~/.jcode/scheduler/daemon.log |
To remove it completely:
jcsched uninstall # stop and remove the background agent
jcsched menubar --uninstall # remove the menu bar item
jcsched app --uninstall # remove the app
rm -rf ~/.jcode/scheduler # remove jobs, run history, and logsEach run is a real jcode run subprocess in its own process group, so timeouts
and cancels take the whole tree down. Output streams to disk as it arrives, so
a long run never grows the daemon's memory. State is written atomically through
a temp file and a rename, so a crash mid-write cannot corrupt your jobs.
Bug reports and pull requests are welcome. See CONTRIBUTING.md for how to set up a scratch environment, and CHANGELOG.md for what changed when.
MIT. See LICENSE.