Paseo plugin: schedule shell commands on your Mac through launchd, from a sidebar panel
Supported platforms: macOS.
From the plugin's README
launchctl print, whose output is prose. If a macOS release rewords it, those facts go blank
until the parser is updated; the run history and the log do not depend on it.npmjs · Paseo 0.9+
paseo plugin add npm:@gpambrozio/paseo-launchd-jobs@0.6.0GitHub · Paseo 0.8 fallback
paseo plugin add gpambrozio/paseo-plugins --ref 09509ce55ab06758623262e6aa71522ade6fda0c --path launchd-jobsGit security status and fallback installation are pinned to commit 09509ce55ab0.
From the plugin's README
paseo plugin install npm:@gpambrozio/paseo-launchd-jobs
That is the shortest route on Paseo 0.11 or newer, which installs plugins straight from npm; add
@<version> to pin one. On Paseo 0.9 or 0.10, install the last release that supports them:
paseo plugin install npm:@gpambrozio/paseo-launchd-jobs@0.5.0. On 0.8, install the last release that supported it from this repository instead:
paseo plugin add gpambrozio/paseo-plugins --path launchd-jobs
Or from a clone:
cd paseo-plugins/launchd-jobs
npm install
npm run typecheck
paseo plugin install "$PWD"
Requires Paseo 0.11.0 or newer, on the computer running the daemon and on whatever you view the jobs on. Both check the version themselves, so an older one reports the plugin as incompatible rather than half working.
The daemon has to be running on macOS, because that is where the agents live and where the commands run. A daemon on Linux loads the plugin and the screen says so instead of showing a list. The Paseo app can be anywhere.
README
A Paseo plugin that schedules shell commands on your Mac through launchd. It adds a Scheduled jobs screen where you create a job — a name, a command, a working directory, and either a cron expression or a fixed interval — and the plugin writes it as a LaunchAgent. From then on launchd runs it, whether or not Paseo is open. The screen shows what launchd knows about each job, the last twenty runs with their exit codes, and the tail of its log.
It is a front for launchd, not a scheduler of its own. Nothing here has to stay running: the plugin
holds no timers and keeps no job database. The plists in ~/Library/LaunchAgents are the source of
truth, and the screen reads them back on every refresh.
paseo plugin install npm:@gpambrozio/paseo-launchd-jobs
That is the shortest route on Paseo 0.11 or newer, which installs plugins straight from npm; add
@<version> to pin one. On Paseo 0.9 or 0.10, install the last release that supports them:
paseo plugin install npm:@gpambrozio/paseo-launchd-jobs@0.5.0. On 0.8, install the last release that supported it from this repository instead:
paseo plugin add gpambrozio/paseo-plugins --path launchd-jobs
Or from a clone:
cd paseo-plugins/launchd-jobs
npm install
npm run typecheck
paseo plugin install "$PWD"
Requires Paseo 0.11.0 or newer, on the computer running the daemon and on whatever you view the jobs on. Both check the version themselves, so an older one reports the plugin as incompatible rather than half working.
The daemon has to be running on macOS, because that is where the agents live and where the commands run. A daemon on Linux loads the plugin and the screen says so instead of showing a list. The Paseo app can be anywhere.
Each job is one file, ~/Library/LaunchAgents/com.paseo-plugins.launchd-jobs.<name>.plist. The
plugin only ever lists, writes, and removes files whose label starts with that prefix, so your other
LaunchAgents are never touched. A plist you write by hand under the prefix shows up too.
The command runs through /bin/zsh -lc, so it can be anything you would type at a prompt, and
with the PATH your interactive shell reports — launchd's own PATH is /usr/bin:/bin:/usr/sbin:/sbin
and nothing from Homebrew or a version manager, which is the usual reason a LaunchAgent works in
the terminal and fails when scheduled. The PATH is captured when the job is saved, so if you change
it, save the job again.
The working directory is optional and may start with ~/. Without one, launchd starts the command
in your home directory.
Cron expression, five fields: minute, hour, day of month, month, weekday. Lists (1,15),
ranges (9-17), steps (*/15, 9-17/2), and three-letter names (mon, jan) all work. The form
shows the schedule in words as you type — "At 09:00 on Mon–Fri" — and how many launchd entries it
becomes, because launchd has no expression language: every combination of the values you list is
its own entry. 0,30 9-17 * * 1-5 is ninety of them. The cap is a thousand.
One difference from cron: when both a day of month and a weekday are given, cron fires when either matches, launchd only when both do. The preview says "(both must match)" so it is not a surprise.
Fixed interval: every N seconds, minutes, or hours, counted from when the job was loaded.
Every run appends a line to the log with a timestamp, the command's combined output, and the exit
code, and one record to a history file — when it started, how long it took, and how it ended. The
screen shows the last twenty runs and the last 64 KB of the log. Both files live under
$PASEO_HOME/plugin-data/launchd-jobs/ (~/.paseo/plugin-data/launchd-jobs/ by default). A log is rotated
once it passes 1 MB, and the history keeps its last two hundred runs.
Follow shows the log as the job writes it, instead of only when you press Refresh log — useful for a job you have just started, or one that takes a while. Press it again to stop; following also ends when you switch jobs or leave the screen, so nothing is left running.
Following needs somewhere to run, and borrows one of your open workspaces: while it lasts it shows
up in that workspace's terminal list as launchd: <job name>, and it goes away when you stop. With
no workspace open there is nowhere to run it, and the button says so — Refresh log still works.
Run now asks launchd to start the job immediately. It does not change the schedule.
A job that fails at three in the morning is worth knowing about without going looking, so the sidebar says so: a red count appears beside Scheduled jobs, and its icon changes to a crossed-out calendar. Tap the count for the list of failing jobs, and tap one to open it straight on its last run. A job counts as failing when its most recent run ended with a non-zero exit code, which is the same thing the list shows as Failed (exit N).
Opening the job clears it from the count — that is all it takes, there is nothing to dismiss. The alert comes back if the job fails again, because what is remembered is the run you saw, not the job. A job whose next run succeeds drops out of the count on its own.
The count is checked about once a minute, whether or not the jobs screen is open, so the sidebar can be a minute behind a failure that has just happened. Opening the screen and pressing Refresh shows the truth immediately.
With more than one Paseo host, the row shows the jobs of the host you are looking at.
Deleting a job in the screen unloads it, deletes the plist, and deletes its log and history.
Removing the plugin does not: the agents stay installed and keep running, because they are
launchd's, not Paseo's. Delete the jobs first, or delete the plists by hand and run
launchctl bootout gui/$UID/<label> for each.
launchctl print, whose output is prose. If a macOS release rewords it, those facts go blank
until the parser is updated; the run history and the log do not depend on it.Scanned 15 Oct 2026, 12:30 UTC from gpambrozio/paseo-plugins/launchd-jobs.