<< All versions

Skill v1.0.0

currentLLM-judged scan90/100
tjbai/dotfiles/managing-launchd-jobs
──Details
PublishedOctober 21, 2026 at 07:41 AM
Content Hashsha256:6017864d4c40988d...
Git SHAf89d67870554
──Files
Files (1 file, 3.0 KB)
SKILL.md3.0 KBactive
SKILL.md · 34 lines · 3.0 KB

version: "1.0.0" name: managing-launchd-jobs description: Defines, applies, and inspects launchd user agents on TJ's Macs (the always-on work mac mini work, and others) through the dotfiles-managed lj tool. Use when asked to add a background job, cron, or daemon on one of his machines, to check whether a launchd job is running or healthy, or to read a job's log. Never hand-write ~/Library/LaunchAgents plists.


Managing launchd jobs

All user launchd agents on this machine are defined in ~/dev/dotfiles/launchd/jobs/<name>/ and rendered into ~/Library/LaunchAgents/com.tjbai.<name>.plist by lj (~/dev/dotfiles/launchd/lj, on PATH). The repo is the source of truth; lj owns every com.tjbai.* label and nothing else.

Inspect

  • lj ls — repo jobs with loaded state, pid, last exit, last run, run count. --all adds every other agent in the user domain, read-only.
  • lj status <name> — full launchctl print for one job (also accepts any label).
  • lj log <name> [-f] — ~/Library/Logs/tjbai/<name>.log; each run is bracketed by START and END exit=N dur=Ns lines written by the wrapper.
  • lj doctor — exit 1 if any job has drifted from its plist, is unloaded, failed its last run, is overdue for its interval, has a huge log, fails its check hook, or if an orphan com.tjbai.* plist exists.
  • lj run <name> — start a run now (kickstart -k).

Define a job

Create ~/dev/dotfiles/launchd/jobs/<name>/ with:

  • job — bash-sourced spec. Exactly one trigger: INTERVAL=<seconds>, CALENDAR="Hour=5 Minute=0" (any StartCalendarInterval keys), or KEEP_ALIVE=1 (daemon, restarted on exit). Optional RUN_AT_LOAD=1, CWD=<dir>, DESC="...".
  • run — the executable (chmod +x). It runs straight from the checkout via lj _exec <name>, which sets PATH, logs, records ~/.local/state/lj/<name>.last, and notifies on non-zero exit. Edits to run are live on the next tick; only changes to job need lj apply.
  • check — optional executable doctor hook; exit non-zero with a one-line reason to make lj doctor flag the job.

Then lj apply (or lj apply --prune to also unload labels the repo no longer defines), lj ls to confirm it loaded, and commit the dotfiles change. Do not push unless asked.

Rules

  • Never write or edit ~/Library/LaunchAgents/*.plist directly, and never launchctl load a hand-made plist. Define the job in the repo and lj apply.
  • lj disable <name> / lj enable <name> to pause a job; deleting its directory plus lj apply --prune removes it.
  • Existing jobs: amp-runner (keeps the work Amp runner alive, host work only; amp self-updates and re-execs in place via amp.runner.autoUpdate.enabled, so the check hook compares the mapped binary inode, not mtime vs process start) and scratch-fold (hourly; folds the scratch repo's inbox branch into main, one commit per project dir per closed 4h window — protocol in the using-scratch skill). Do not stop amp-runner from a remote thread; it is the remote thread's only path to this machine.
All versions