--- name: scheduled-jobs description: Use when user asks about scheduled jobs, cron jobs, automated tasks, or wants to create/list/modify/delete scheduled tasks on this Mac. Also triggers on "what runs on my computer", "schedule this script", "add a nightly job", "list my jobs". --- # Scheduled Jobs Manager **Note:** This skill is macOS-only. It uses launchd (Launch Agents), which is specific to macOS. ## Overview All scheduled jobs on this Mac use **macOS launchd** (Launch Agents). We do NOT use cron -- it silently drops missed jobs when the Mac is asleep. Launchd retries missed jobs when the Mac wakes. **Naming convention:** `com.yourname.` **Plist location:** `~/Library/LaunchAgents/com.yourname.*.plist` **Logs:** `YOUR_LOG_DIR/.log` ## Configuration Before using this skill, set your preferences: ``` # Replace these with your values LABEL_PREFIX: com.yourname # e.g., com.jsmith, com.mycompany LOG_DIR: ~/logs/scheduled-jobs # Where job logs are written WRAPPER_SCRIPT: (optional) # Path to a wrapper script for logging/notifications ``` If you use a wrapper script, it should handle PATH setup, logging, and optionally notifications (e.g., Slack alerts on failure). If you don't have a wrapper, jobs can call scripts directly. ## Listing Jobs ```bash # List plist files ls ~/Library/LaunchAgents/com.yourname.*.plist # Show loaded status launchctl list | grep yourname # Check recent log output for a job tail -20 YOUR_LOG_DIR/.log ``` ## Creating a New Job ### 1. Create the plist File: `~/Library/LaunchAgents/com.yourname..plist` Template (without wrapper): ```xml Label com.yourname. ProgramArguments /path/to/interpreter /path/to/script StartCalendarInterval Hour 2 Minute 0 StandardOutPath YOUR_LOG_DIR/.stdout.log StandardErrorPath YOUR_LOG_DIR/.stderr.log ``` Template (with wrapper script): ```xml Label com.yourname. ProgramArguments /bin/bash YOUR_WRAPPER_SCRIPT_PATH /path/to/interpreter /path/to/script StartCalendarInterval Hour 2 Minute 0 ``` **Important notes:** - Use full absolute paths for everything -- no `~` or `$HOME` in plists - For Python scripts, use the real Python binary path (e.g., `/usr/local/bin/python3`), not a shim or symlink - For shell scripts, use `/bin/bash` ### 2. Schedule types **Daily at a specific time:** ```xml StartCalendarInterval Hour 14 Minute 30 ``` **Multiple times per day** (use an array of dicts): ```xml StartCalendarInterval Hour 9 Minute 0 Hour 17 Minute 0 ``` **Every N seconds** (use instead of StartCalendarInterval): ```xml StartInterval 3600 ``` **Specific days of the week** (0=Sunday, 1=Monday, ...): ```xml StartCalendarInterval Weekday 1 Hour 9 Minute 0 ``` ### 3. Load the job ```bash launchctl load ~/Library/LaunchAgents/com.yourname..plist ``` ### 4. Verify it loaded ```bash launchctl list | grep ``` ## Modifying a Job ```bash # Unload first launchctl unload ~/Library/LaunchAgents/com.yourname..plist # Edit the plist... # Reload launchctl load ~/Library/LaunchAgents/com.yourname..plist ``` ## Deleting a Job ```bash launchctl unload ~/Library/LaunchAgents/com.yourname..plist rm ~/Library/LaunchAgents/com.yourname..plist ``` ## Testing a Job Manually ```bash # Run the script directly to test /path/to/interpreter /path/to/script [args...] # Or through the wrapper if you use one /bin/bash YOUR_WRAPPER_SCRIPT_PATH /path/to/command [args...] # Check the log tail -20 YOUR_LOG_DIR/.log ``` ## Common Mistakes | Mistake | Fix | |---------|-----| | Using cron | Always use launchd. Cron silently drops missed jobs when Mac sleeps. | | Using `~` in plist paths | Use full absolute paths: `/Users/yourname/...` | | Using a Python shim in plist | Use the real python binary path, not pyenv/asdf shims | | Forgetting to load after creating | Run `launchctl load` on the plist | | Editing without unload/reload | Changes don't take effect until reloaded | | Inconsistent naming | Stick to `com.yourname.` for all jobs | ## Current Jobs Check live with: `ls ~/Library/LaunchAgents/com.yourname.*.plist`