Chisle
The coding-agent plugin that bills 52% of a bare model across 20 live tasks. Terse persona + tool-output compressor + context diet.
翻訳待ち
Schedule macOS background jobs without writing plists — a friendly CLI for launchd LaunchAgents and LaunchDaemons
翻訳待ち
Loading the full README.
Read the full README ↗Schedule and manage macOS background jobs without writing a single plist.
launchd is powerful but painful to use directly. Summond wraps it with a CLI that handles plist generation, log management, and job lifecycle — so you can focus on what the job actually does.
brew install --cask joshgummersall/summond/summond
Or install from source (requires Go 1.24+):
go install github.com/joshgummersall/summond/cmd/summond@latest
Jobs run from a copy of summond in its state directory. After brew install or brew upgrade, the cask replaces that copy so jobs run the new version. If you have daemon jobs, this step asks for your password. If it can't run (for example, an unattended upgrade with no terminal), the next summond apply or sudo summond apply replaces the copy.
Jobs run from a copy of summond in Application Support, so they keep running after brew uninstall. To remove every job, plist, log, and state directory:
brew uninstall --zap summond
Zap runs summond uninstall twice: once as you, for agent jobs, and once with sudo, for daemon jobs. It also removes the newsyslog config that summond v1 installed.
Or run summond uninstall (agents) and sudo summond uninstall (daemons) before brew uninstall. Each run removes one scope. Homebrew runs a cask's plain uninstall steps on every upgrade too, so only --zap removes jobs.
Define jobs in a TOML file and apply them:
[jobs.cleanup]
command = "/usr/local/bin/my-script"
schedule = "daily"
hour = 3
minute = 0
[jobs.cleanup.env]
MODE = "nightly"
summond apply
That's it. There is no separate setup step: Summond creates its state directory on first use, generates and loads the launchd plist, creates and rotates log files, and tracks execution history. When you delete a job from the config, summond apply --prune removes it cleanly.
command, args, shell_command, working_dir, watch_paths, and env values all support $VAR/${VAR} expansion, resolved against summond's own environment when you run apply. Referencing an unset variable is an error rather than silently resolving to an empty string:
[jobs.cleanup]
command = "$HOME/bin/my-script"
working_dir = "$HOME/projects/foo"
# Run a binary on a schedule
summond add cleanup --schedule daily --hour 3 --minute 0 -- /usr/local/bin/my-script
# Run a shell snippet
summond add rotate-logs --schedule daily --hour 3 --minute 30 <<'EOF'
find /tmp -type f -mtime +7 -delete
EOF
login, boot, hourly, daily, weekly, interval, and calendar — with --hour, --minute, --weekday, and --interval-minutes flags to tune them. Omit the timing flags and summond deterministically seeds them from the job spec to spread load.
daily and weekly schedules also accept a window, which constrains the seeded hour to a named time-of-day range instead of the full day:
| Window | Default hours |
|---|---|
morning | 05:00-11:59 |
afternoon | 12:00-16:59 |
evening | 17:00-21:59 |
[jobs.report]
command = "/usr/local/bin/weekly-report"
schedule = "weekly"
weekday = 1
window = "morning"
Override the default window bounds in summond.toml:
[windows]
morning = { start_hour = 6, end_hour = 10 }
Run a job when files change instead of on a schedule:
[jobs.on-config-change]
command = "/usr/local/bin/reload"
trigger = "on_change"
[jobs.on-config-change.watch]
paths = ["./config.json"]
throttle_seconds = 2 # debounce delay; defaults to 2
The flat keys watch_paths and throttle_interval_seconds are also accepted for backwards compatibility.
The job receives a SUMMOND_CHANGED_PATHS environment variable — a colon-separated list of entries from watch_paths that changed since the last successful run. Note that these are the paths you listed in watch_paths, so a watched directory appears as the directory itself, not the individual file that changed within it. On the first execution (no prior baseline), all watch_paths are included.
# shell_command example
for path in ${SUMMOND_CHANGED_PATHS//:/ }; do
reload "$path"
done
Run a job whenever a specific USB device is plugged in — for example, reapplying a webcam setting that doesn't persist across unplug/replug:
[jobs.brio-zoom]
shell_command = "uvc-util -I 0 -s zoom-abs=150"
trigger = "on_usb_attach"
[jobs.brio-zoom.usb]
vendor_id = "0x046d" # Logitech
product_id = "0x085e" # Brio
vendor_id and product_id identify the device and are both required. They can be hex strings ("0x046d"), TOML hex integers (0x046d), or decimal (1133). Find them with system_profiler SPUSBDataType, ioreg -p IOUSB -l, or uvc-util --list-devices.
Under the hood this renders a launchd LaunchEvents > com.apple.iokit.matching subscription, so launchd itself watches for the device — no summond process stays resident. The trigger fires whenever the device enumerates, including behind a hub or dock. Two things to know:
on_usb_detach: launchd only delivers IOKit matching (arrival) events, not termination events, and watching for removal would require a resident daemon. To react to an unmounted volume, watch its mount point with the on_change trigger instead.Like on_change, the trigger debounces via throttle_interval_seconds (default 2) in case the device enumerates more than once on attach.
summond tui
Opens a two-pane terminal UI. The left pane lists all jobs; the right pane shows details for the selected job across three tabs:
This is the beginning of the README. Read the rest at the original. Read the full README ↗