Skip to content

Repository files navigation

jcode scheduler icon

jcode scheduler

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

macOS Rust License


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.

What makes it nice

🍎 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

Contents

Install

Requires macOS, the jcode CLI on your PATH, and Rust to build.

Build and install

git clone https://github.com/tayoonabule/jcode-scheduler.git && cd jcode-scheduler
cargo build --release
./target/release/jcsched install

Or 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 install

install 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 zsh

Get the app and the menu bar item

jcsched app                 # adds "jcode Scheduler" to ~/Applications
jcsched menubar --install    # keeps the menu bar item across logins

jcsched 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.

Start your first routine

jcsched add --name "Morning triage" --cron "30 9 * * 1-5" \
  --cwd ~/code/app \
  --prompt "Review open PRs and summarize the risks"
jcsched open

Open 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.

Pick your surface

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

How scheduling works

Working across multiple folders

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.

Missed runs

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.
  • skip ignores 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.

Commands

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

Picking up a session

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 once

The 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.

Which terminal it opens in

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}

A note on cmux

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 restart

Any other terminal works from every surface with no configuration.

add options

--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

Letting jcode schedule its own routines

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 ~):

// ~/.jcode/mcp.json
{
  "servers": {
    "jcode-scheduler": {
      "command": "/absolute/path/to/.local/bin/jcsched",
      "args": ["mcp"]
    }
  }
}

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 debugging

Menu bar

jcsched 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 that

Dashboard

http://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.

Where things live

~/.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

Configuration

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.

Troubleshooting

Start here, always:

jcsched doctor

It 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 logs

Design notes

Each 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.

Contributing

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.

License

MIT. See LICENSE.

About

Scheduled jcode agent sessions with a local macOS-style dashboard

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages