# Osascript Launchd

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

- Skill: `damionrashford/osascript-launchd` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add damionrashford/osascript-launchd`
- Raw SKILL.md: https://api.skillmd.com/api/skills/damionrashford/osascript-launchd/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: damionrashford (https://skillmd.com/u/damionrashford)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/damionrashford/osascript-launchd

---


# 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:
```bash
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:
```bash
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:
```bash
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

- [reference/07-launchdaemons.md](reference/07-launchdaemons.md) — Full plist anatomy, all trigger types, resource controls, launchctl commands

## Key Pattern: WatchPaths for iMessage Bot

```xml
<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.

