| name | osascript-launchd |
| description | Create and manage macOS LaunchAgents and LaunchDaemons. Use when setting up background automation, scheduled tasks, file watchers (WatchPaths), interval polling, or persistent daemons that survive logout or reboot. |
osascript-launchd
LaunchAgents (per-user, GUI access) and LaunchDaemons (root, system-wide, survives logout).
All scripts are pure Python using stdlib only.
LaunchAgent vs LaunchDaemon
| LaunchAgent | LaunchDaemon |
|---|
| Runs as | Logged-in user | root |
| Starts when | User logs in | System boot |
| Lives in | ~/Library/LaunchAgents | /Library/LaunchDaemons |
| GUI access | Yes | No |
| TCC restrictions | Yes | No — full system access |
Use LaunchAgent for automation that needs your session (osascript, screen, Chrome).
Use LaunchDaemon for always-on background work that needs root or full disk access.
Trigger Types
| Trigger | Key | Use case |
|---|
| On load/login | RunAtLoad: true | Start immediately |
| Keep alive | KeepAlive: true | Restart on any exit (even kill -9) |
| Interval | StartInterval: N | Every N seconds |
| Calendar | StartCalendarInterval | Cron replacement (catches up after sleep) |
| File change | WatchPaths | Instant trigger on file/dir change (no polling) |
| New files | QueueDirectories | Job queue — fires when files appear |
Scripts
Generate a LaunchAgent plist from parameters:
uv run scripts/generate-plist.py com.user.myjob "/usr/bin/python3 /path/to/script.py" --interval 60
Write plist to ~/Library/LaunchAgents and load it immediately:
uv run scripts/install-agent.py com.user.myjob "/usr/bin/python3 /path/to/script.py" --interval 60
Load, unload, start, stop, or check status of a LaunchAgent:
uv run scripts/launchctl.py status com.user.myjob
uv run scripts/launchctl.py load com.user.myjob
uv run scripts/launchctl.py stop com.user.myjob
Reference
Key Pattern: WatchPaths for iMessage Bot
<key>WatchPaths</key>
<array>
<string>/Users/you/Library/Messages/chat.db</string>
</array>
Fires instantly when chat.db changes. No polling. Daemon reads new message, Claude processes, osascript sends reply.