---
name: 0xjitsu/launchagent-builder
source: https://app.decimal.ai/s/0xjitsu-launchagent-builder@1/SKILL.md
source_sha256: 740d9f3f6e0e
---

# LaunchAgent Builder

Generate, validate, and install macOS LaunchAgent plist files for scheduled tasks and daemons.

## When to Trigger

- User asks to create a LaunchAgent or LaunchDaemon
- User wants to schedule a script to run periodically
- User asks to automate a recurring task on macOS
- User wants to set up a background process or daemon
- User asks to run something "every N minutes/hours" or "daily at X"

## Naming Convention

All agents use the `com.berna.` namespace:

```
com.berna.<descriptive-name>.plist
```

Examples:
- `com.berna.secret-scanner-daily.plist`
- `com.berna.wispr-flow-watchdog.plist`
- `com.berna.git-backup-hourly.plist`

The `Label` value inside the plist MUST match the filename (minus `.plist`).

## Template System

Gather these inputs from the user (or infer from context):

| Parameter          | Required | Default                                    |
|--------------------|----------|--------------------------------------------|
| Script path        | Yes      | —                                          |
| Schedule type      | Yes      | `interval` or `calendar`                   |
| Interval (seconds) | If interval | —                                       |
| Calendar day       | If calendar | — (0=Sun, 1=Mon, …, 6=Sat)             |
| Calendar hour      | If calendar | —                                       |
| Calendar minute    | If calendar | 0                                       |
| Log output path    | No       | `~/Reports/maintenance/<name>.log`         |
| KeepAlive          | No       | `false` (true for daemons)                 |
| RunAtLoad          | No       | `true`                                     |
| Environment vars   | No       | —                                          |
| WatchPaths         | No       | — (for file-triggered agents)              |

## Plist Generation

### Interval-Based Template

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.berna.AGENT_NAME</string>

    <key>ProgramArguments</key>
    <array>
        <string>/bin/bash</string>
        <string>/path/to/script.sh</string>
    </array>

    <key>StartInterval</key>
    <integer>SECONDS</integer>

    <key>RunAtLoad</key>
    <true/>

    <key>StandardOutPath</key>
    <string>/Users/bbmisa/Reports/maintenance/AGENT_NAME.log</string>

    <key>StandardErrorPath</key>
    <string>/Users/bbmisa/Reports/maintenance/AGENT_NAME.log</string>
</dict>
</plist>
```

### Calendar-Based Template

Replace `StartInterval` with:

```xml
    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>HOUR</integer>
        <key>Minute</key>
        <integer>MINUTE</integer>
        <!-- Optional: Day (0=Sunday), Weekday, Month -->
    </dict>
```

For multiple schedules, use an array of dicts:

```xml
    <key>StartCalendarInterval</key>
    <array>
        <dict>
            <key>Hour</key>
            <integer>9</integer>
            <key>Minute</key>
            <integer>0</integer>
        </dict>
        <dict>
            <key>Hour</key>
            <integer>17</integer>
            <key>Minute</key>
            <integer>0</integer>
        </dict>
    </array>
```

### Optional Sections

**KeepAlive (for daemons):**
```xml
    <key>KeepAlive</key>
    <true/>
```

**Environment Variables:**
```xml
    <key>EnvironmentVariables</key>
    <dict>
        <key>PATH</key>
        <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
        <key>CUSTOM_VAR</key>
        <string>value</string>
    </dict>
```

**WatchPaths (file-triggered):**
```xml
    <key>WatchPaths</key>
    <array>
        <string>/path/to/watched/file</string>
    </array>
```

## Validation

Always validate the plist before writing:

```bash
plutil -lint /path/to/generated.plist
```

Expected output: `<file>: OK`

If validation fails, fix the XML and re-validate before proceeding.

## Installation

### Step 1: Write the plist

```bash
# Ensure target directory exists
mkdir -p ~/Library/LaunchAgents

# Write the plist (tool writes the file)
# Target: ~/Library/LaunchAgents/com.berna.<name>.plist
```

### Step 2: Load the agent

```bash
launchctl load ~/Library/LaunchAgents/com.berna.<name>.plist
```

### Step 3: Verify

```bash
launchctl list | grep com.berna.<name>
```

A successful load shows the agent with a PID (if running) or status code.

## Management Commands

### List all Berna agents

```bash
launchctl list | grep com.berna
```

### Unload (stop) an agent

```bash
launchctl unload ~/Library/LaunchAgents/com.berna.<name>.plist
```

### Reload (after editing plist)

```bash
launchctl unload ~/Library/LaunchAgents/com.berna.<name>.plist
launchctl load ~/Library/LaunchAgents/com.berna.<name>.plist
```

### View logs

```bash
tail -f ~/Reports/maintenance/<name>.log
```

### Check last run status

```bash
launchctl list | grep com.berna.<name>
# Column 1: PID (- if not running), Column 2: last exit status (0 = success)
```

## Common Schedules Reference

| Schedule       | StartInterval | StartCalendarInterval          |
|----------------|---------------|--------------------------------|
| Every 5 min    | 300           | —                              |
| Every 30 min   | 1800          | —                              |
| Every hour     | 3600          | —                              |
| Daily at 9 AM  | —             | `Hour: 9, Minute: 0`          |
| Weekdays 8 AM  | —             | `Weekday: 1-5, Hour: 8`       |
| Weekly Sunday   | —            | `Weekday: 0, Hour: 3`         |
| Monthly 1st    | —             | `Day: 1, Hour: 0`             |

## Safety Rules

1. **Always confirm before loading** — show the generated plist and ask for approval
2. **Validate plist syntax** — run `plutil -lint` before writing to `~/Library/LaunchAgents/`
3. **Never overwrite existing agents** — check if a plist with the same name exists first and ask before replacing
4. **Ensure log directory exists** — `mkdir -p ~/Reports/maintenance` before setting log paths
5. **Use absolute paths only** — LaunchAgents run outside shell context, so relative paths will fail
6. **Include PATH in EnvironmentVariables** — scripts that use Homebrew tools need `/opt/homebrew/bin` in PATH
7. **Test scripts independently first** — recommend the user runs the script manually before scheduling it