[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6C2NDP26GGZ6BR2C8ZPQ5RJ":3,"skill-same:01M6C2NDP26GGZ6BR2C8ZPQ5RJ":104},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":43,"linkOnly":44,"markdownContent":45,"markdownBody":46,"frontMatter":47,"refName":59,"commitSha":60,"skillDirectory":61,"forksCount":62,"repositoryHtmlUrl":63,"repositoryHomepageUrl":6,"ownerHtmlUrl":64,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":65,"install":66,"resources":79},"01M6C2NDP26GGZ6BR2C8ZPQ5RJ","launchd-auto-cleanup",null,"Create macOS launchd cleanup agents with dry-run verification. Writes cleanup script + plist installer, verifies with --dry-run, then installs and runs live.","jleechanorg\u002Fjleechan-skills","jleechanorg","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F221404966?v=4","01M6C1VJ99XG950X1DWPEBCBGX","01M6C1VJDK5YVDNT3362RT5WSB",6,"MIT","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills_archive\u002F2026-08-27-historical-zero-use\u002Flaunchd-auto-cleanup\u002FSKILL.md",[],[18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42],"cleanup","launchd auto cleanup","launchd","auto","edit","when","inline","dry","live","clean","files","install","recurring","user","directory","write","read","schedule","automate","Bash","cache","verification","run","create","macos","2026-11-10 10:12:30",false,"---\nname: launchd-auto-cleanup\ndescription: Create macOS launchd cleanup agents with dry-run verification. Writes cleanup script + plist installer, verifies with --dry-run, then installs and runs live.\nwhen_to_use: When user wants to automate recurring cleanup of a directory, cache, or temp files on macOS via launchd\ntriggers:\n  - \"automate cleanup\"\n  - \"launchd cleanup job\"\n  - \"schedule cleanup\"\n  - \"cleanup job\"\n  - \"auto clean\"\n  - \"recurring cleanup\"\n  - \"create launchd agent\"\n  - \"install launchd plist\"\nallowed-tools: Read, Write, Edit, Bash\ncontext: inline\n---\n\n# launchd-auto-cleanup\n\n## Contract\n\nThis skill guarantees:\n1. Every generated cleanup script has a `--dry-run` flag and runs a dry-run before any live deletion\n2. The generated script passes `validate-cleanup-script.sh` (no hardcoded paths, uses system tools only, has safety guards)\n3. The plist installer uses `StartCalendarInterval` (not `StartInterval`) for human-readable schedules\n4. The install sequence runs `launchctl bootout` (with `|| true`) before `bootstrap` to handle reinstalls\n5. `validate-cleanup-script.sh` is always run before reporting done\n\n## 10-Item Checklist Status\n\n| # | Item | Status |\n|---|------|--------|\n| 1 | SKILL.md | ✓ present |\n| 2 | Code (validate-cleanup-script.sh) | ✓ present |\n| 3 | Unit tests (test_validate.sh) | ✓ present |\n| 4 | Integration tests | N\u002FA — scripts are deterministic, LLM generates them; no live endpoints |\n| 5 | LLM evals | N\u002FA — cleanup targets vary by user; contract is structural not semantic |\n| 6 | Resolver trigger | pending — no RESOLVER.md found at ~\u002F.claude\u002Fskills\u002FRESOLVER.md |\n| 7 | Resolver trigger eval | pending (depends on #6) |\n| 8 | check-resolvable | pending (depends on #6) |\n| 9 | E2E test | pending — requires macOS launchd context |\n| 10 | Brain filing | N\u002FA — skill does not write to brain |\n\nItems 4–5 are intentionally N\u002FA: the skill's output is structurally deterministic (a bash script); quality is verified by `validate-cleanup-script.sh` and dry-run, not LLM evals. Items 6–9 are pending RESOLVER.md infrastructure.\n\n---\n\n## Phases\n\n### Phase 1: Audit Target\n\nBefore writing any files, gather:\n- **Target path**: directory, file pattern, or cache location to clean (e.g. `~\u002F.cache\u002Fopencode\u002Fbin\u002F`, `\u002Ftmp\u002F*.log`)\n- **Retention rule**: age threshold in days (e.g. \"older than 30 days\") or size threshold\n- **Schedule**: human schedule (e.g. \"every Sunday at 3am\", \"daily at 2am\")\n- **Label**: reverse-DNS launchd label (e.g. `com.user.cleanup-opencode-bin`)\n- **Script path**: where to install the cleanup script (e.g. `~\u002F.local\u002Fbin\u002Fcleanup-opencode-bin.sh`)\n\nAsk the user for any missing items before writing files.\n\n### Phase 2: Write Cleanup Script\n\nWrite `\u003Cscript_path>` with:\n\n```bash\n#!\u002Fusr\u002Fbin\u002Fenv bash\n# cleanup-\u003Ctarget>.sh — Auto-generated by launchd-auto-cleanup skill\n# Usage: cleanup-\u003Ctarget>.sh [--dry-run]\nset -euo pipefail\n\nDRY_RUN=false\n[[ \"${1:-}\" == \"--dry-run\" ]] && DRY_RUN=true\n\nTARGET_DIR=\"$HOME\u002F\u003Crelative\u002Fpath>\"\nRETENTION_DAYS=\u003CN>\nLOG_PREFIX=\"[cleanup-\u003Ctarget>]\"\n\nif [[ ! -d \"$TARGET_DIR\" ]]; then\n  echo \"$LOG_PREFIX Target dir does not exist: $TARGET_DIR — skipping\"\n  exit 0\nfi\n\necho \"$LOG_PREFIX Starting. DRY_RUN=$DRY_RUN\"\necho \"$LOG_PREFIX Target: $TARGET_DIR (older than ${RETENTION_DAYS} days)\"\n\n# Find candidates\nmapfile -t CANDIDATES \u003C \u003C(\n  \u002Fusr\u002Fbin\u002Ffind \"$TARGET_DIR\" -maxdepth 1 -mindepth 1 \\\n    -mtime +\"$RETENTION_DAYS\" 2>\u002Fdev\u002Fnull\n)\n\nif [[ ${#CANDIDATES[@]} -eq 0 ]]; then\n  echo \"$LOG_PREFIX No candidates found — nothing to do\"\n  exit 0\nfi\n\necho \"$LOG_PREFIX Found ${#CANDIDATES[@]} candidate(s):\"\nfor item in \"${CANDIDATES[@]}\"; do\n  echo \"  $item\"\ndone\n\nif $DRY_RUN; then\n  echo \"$LOG_PREFIX DRY RUN — no deletions performed\"\n  exit 0\nfi\n\nfor item in \"${CANDIDATES[@]}\"; do\n  if [[ -e \"$item\" ]]; then\n    \u002Fbin\u002Frm -rf \"$item\"\n    echo \"$LOG_PREFIX Deleted: $item\"\n  fi\ndone\n\necho \"$LOG_PREFIX Done\"\n```\n\nKey rules for the script body:\n- Use `$HOME` not `~` in variable assignments (both work in bash but `$HOME` is explicit and safe in all contexts)\n- Use absolute paths for all external commands: `\u002Fusr\u002Fbin\u002Ffind`, `\u002Fbin\u002Frm`, `\u002Fusr\u002Fbin\u002Fwc`, etc.\n- `-maxdepth 1 -mindepth 1` to avoid deleting the target dir itself\n- Always check `[[ -d \"$TARGET_DIR\" ]]` before operating\n- Log every action to stdout (plist's `StandardOutPath` captures it)\n\n### Phase 3: Write Install Script\n\nWrite `install-\u003Clabel>.sh`:\n\n```bash\n#!\u002Fusr\u002Fbin\u002Fenv bash\n# install-\u003Clabel>.sh — Installs launchd agent for cleanup-\u003Ctarget>\nset -euo pipefail\n\nLABEL=\"\u003Ccom.user.label>\"\nSCRIPT_SRC=\"$(cd \"$(dirname \"$0\")\" && pwd)\u002Fcleanup-\u003Ctarget>.sh\"\nSCRIPT_DST=\"$HOME\u002F.local\u002Fbin\u002Fcleanup-\u003Ctarget>.sh\"\nPLIST=\"$HOME\u002FLibrary\u002FLaunchAgents\u002F${LABEL}.plist\"\nLOG_DIR=\"$HOME\u002FLibrary\u002FLogs\"\n\n# Install script\n\u002Fbin\u002Fmkdir -p \"$HOME\u002F.local\u002Fbin\"\n\u002Fusr\u002Fbin\u002Finstall -m 0755 \"$SCRIPT_SRC\" \"$SCRIPT_DST\"\n\n# Write plist\n\u002Fbin\u002Fcat > \"$PLIST\" \u003C\u003C PLIST_EOF\n\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\u003C!DOCTYPE plist PUBLIC \"-\u002F\u002FApple\u002F\u002FDTD PLIST 1.0\u002F\u002FEN\"\n  \"http:\u002F\u002Fwww.apple.com\u002FDTDs\u002FPropertyList-1.0.dtd\">\n\u003Cplist version=\"1.0\">\n\u003Cdict>\n  \u003Ckey>Label\u003C\u002Fkey>\n  \u003Cstring>${LABEL}\u003C\u002Fstring>\n  \u003Ckey>ProgramArguments\u003C\u002Fkey>\n  \u003Carray>\n    \u003Cstring>\u002Fbin\u002Fbash\u003C\u002Fstring>\n    \u003Cstring>${SCRIPT_DST}\u003C\u002Fstring>\n  \u003C\u002Farray>\n  \u003Ckey>StartCalendarInterval\u003C\u002Fkey>\n  \u003Cdict>\n    \u003Ckey>Weekday\u003C\u002Fkey>\n    \u003Cinteger>0\u003C\u002Finteger>\n    \u003Ckey>Hour\u003C\u002Fkey>\n    \u003Cinteger>3\u003C\u002Finteger>\n    \u003Ckey>Minute\u003C\u002Fkey>\n    \u003Cinteger>0\u003C\u002Finteger>\n  \u003C\u002Fdict>\n  \u003Ckey>StandardOutPath\u003C\u002Fkey>\n  \u003Cstring>${LOG_DIR}\u002F${LABEL}.log\u003C\u002Fstring>\n  \u003Ckey>StandardErrorPath\u003C\u002Fkey>\n  \u003Cstring>${LOG_DIR}\u002F${LABEL}.error.log\u003C\u002Fstring>\n\u003C\u002Fdict>\n\u003C\u002Fplist>\nPLIST_EOF\n\necho \"Installed plist: $PLIST\"\necho \"Installed script: $SCRIPT_DST\"\n\n# Load agent (bootout first to handle reinstall)\n\u002Fbin\u002Flaunchctl bootout \"gui\u002F$(\u002Fusr\u002Fbin\u002Fid -u)\u002F${LABEL}\" 2>\u002Fdev\u002Fnull || true\n\u002Fbin\u002Flaunchctl bootstrap \"gui\u002F$(\u002Fusr\u002Fbin\u002Fid -u)\" \"$PLIST\"\necho \"Agent loaded: $LABEL\"\n```\n\n### Phase 4: Dry-Run Verify\n\nRun the cleanup script with `--dry-run` and review output:\n\n```bash\nbash \u003Cscript_path> --dry-run\n```\n\nExpected output:\n- Lists candidate files\u002Fdirs it would delete\n- Ends with \"DRY RUN — no deletions performed\"\n- Zero exit code\n\nIf dry-run output shows unexpected targets (e.g. the opencode bin\u002F itself, not just its contents), fix `find` flags (`-mindepth 1`) before proceeding.\n\nAlso run the validator:\n```bash\n~\u002F.claude\u002Fskills\u002Flaunchd-auto-cleanup\u002Fvalidate-cleanup-script.sh \u003Cscript_path>\n```\n\nDo not proceed to Phase 5 until both pass.\n\n### Phase 5: Install and Run Live\n\n```bash\nbash \u003Cinstall_script_path>\n```\n\nVerify agent is loaded:\n```bash\n\u002Fbin\u002Flaunchctl list | grep \u003Clabel>\n```\n\nRun live once to confirm no errors:\n```bash\nbash \u003Cscript_path>\n```\n\nCheck log:\n```bash\ntail -20 ~\u002FLibrary\u002FLogs\u002F\u003Clabel>.log\n```\n\n### Output Format\n\nReport at completion:\n```\nlaunchd-auto-cleanup complete\n  Script:    ~\u002F.local\u002Fbin\u002F\u003Cname>.sh\n  Plist:     ~\u002FLibrary\u002FLaunchAgents\u002F\u003Clabel>.plist\n  Schedule:  \u003Chuman schedule>\n  Dry-run:   PASS (\u003CN> candidates listed)\n  Validate:  PASS\n  Live run:  PASS (deleted N items) | PASS (nothing to delete)\n  Agent:     loaded (launchctl list shows \u003Clabel>)\n```\n\n---\n\n## Dry-Run Gate Protocol\n\n**Every cleanup script must be verified with `--dry-run` before any live run.** This is a hard gate — not a suggestion.\n\n### What a compliant dry-run looks like\n\n```\n[2026-05-22 04:05:01] === Section 1: ~\u002F.gemini\u002Ftmp\u002F ===\n[2026-05-22 04:05:01] gemini tmp: before 3.0G ($HOME\u002F.gemini\u002Ftmp)\n[2026-05-22 04:05:01] gemini tmp: [dry-run] would delete contents of $HOME\u002F.gemini\u002Ftmp\n...\n[2026-05-22 04:05:02] === DRY-RUN complete — no files deleted ===\n```\n\nKey signals to check in dry-run output:\n- Every intended target appears in the output\n- No unexpected targets (especially parent dirs, binaries, or active data dirs)\n- \"DRY-RUN complete\" or \"DRY RUN — no deletions performed\" at the end\n- Exit code 0\n\n### Red flags that require stopping and fixing before live run\n\n| Red flag | Root cause | Fix |\n|---|---|---|\n| Target dir itself listed as candidate | Missing `-mindepth 1` in `find` | Add `-mindepth 1` to exclude the root |\n| Binary or `bin\u002F` subdir listed | Over-broad `find` scope | Use explicit `SAFE_SUBDIRS` allowlist |\n| Protected path appears | No protection check | Add guard: `[[ \"$item\" == *\"\u002F.codex\u002Fsessions\"* ]] && continue` |\n| No output at all | Wrong path, wrong mtime, dir empty | Verify with `ls $TARGET` manually |\n| Non-zero exit | Script syntax\u002Flogic error | Fix before proceeding |\n\n### Dry-run enforcement in `validate-cleanup-script.sh`\n\nThe validator (`~\u002F.claude\u002Fskills\u002Flaunchd-auto-cleanup\u002Fvalidate-cleanup-script.sh`) checks that `--dry-run` is present in the script source. Run it before installing any script:\n\n```bash\n~\u002F.claude\u002Fskills\u002Flaunchd-auto-cleanup\u002Fvalidate-cleanup-script.sh \u003Cscript_path>\n```\n\nIf the validator passes but dry-run output looks wrong, the validator cannot catch runtime behavior — review the dry-run output manually.\n\n---\n\n## Safety Guard Checklist\n\nEvery generated cleanup script MUST satisfy all of the following. `validate-cleanup-script.sh` checks the mechanical ones:\n\n- [ ] `set -euo pipefail` at top of script\n- [ ] `--dry-run` flag handled and respected (no deletions when set)\n- [ ] All paths use `$HOME` not `~` in variable assignments\n- [ ] All external commands use absolute paths: `\u002Fusr\u002Fbin\u002Ffind`, `\u002Fbin\u002Frm`, `\u002Fusr\u002Fbin\u002Fwc`, etc.\n- [ ] No homebrew tools (`brew`, `nvm`, `npm`, `node`, `python3` via homebrew) — launchd PATH is `\u002Fusr\u002Fbin:\u002Fbin:\u002Fusr\u002Fsbin:\u002Fsbin` only\n- [ ] Existence check before `rm -rf` (check `[[ -e \"$item\" ]]` in the deletion loop)\n- [ ] Directory existence check before operating on target\n- [ ] Never delete `~\u002F.codex\u002Fsessions`, `~\u002F.claude\u002Fprojects`, or any path modified in last 14 days without `APPROVED` env var set\n- [ ] `.git\u002F` guard: only delete a directory if you intend to delete a git clone; add `[[ -d \"$item\u002F.git\" ]]` guard for git-clone targets\n- [ ] Log every deletion to stdout (caller redirects to log file via plist)\n- [ ] `-mindepth 1` in find to avoid deleting the target directory itself\n\n---\n\n## Common launchd Failure Patterns\n\n**1. StartInterval vs StartCalendarInterval**\n- `StartInterval` takes seconds (integer) — fires every N seconds from boot\n- `StartCalendarInterval` takes a dict (Hour, Minute, Weekday, Day, Month) — cron-style\n- Use `StartCalendarInterval` for human schedules (\"every Sunday at 3am\")\n- Common mistake: using `StartInterval 604800` (weekly) looks right but fires from boot time, not at a fixed clock time\n\n**2. RunAtLoad trap**\n- `RunAtLoad: true` fires the script on every login AND on the schedule\n- Omit `RunAtLoad` unless you explicitly want the job to run at login\u002Fbootstrap\n- If present accidentally, it causes unexpected runs on every reboot\n\n**3. PATH is minimal**\n- launchd agent PATH: `\u002Fusr\u002Fbin:\u002Fbin:\u002Fusr\u002Fsbin:\u002Fsbin` only\n- `brew`, `nvm`, `npm`, `node`, `python3` (homebrew), `rtk` — all unavailable\n- Fix: use absolute paths for every external command in the script\n\n**4. Script not executable**\n- `chmod +x` is not sufficient for cross-machine installs\n- Use `\u002Fusr\u002Fbin\u002Finstall -m 0755 src dst` in the install script\n\n**5. Log dir must exist**\n- `StandardOutPath` and `StandardErrorPath` dirs must exist before the agent runs\n- `~\u002FLibrary\u002FLogs\u002F` always exists on macOS — safe default\n\n**6. bootout before bootstrap for reinstalls**\n- If agent is already loaded, `launchctl bootstrap` errors\n- Pattern: `launchctl bootout \"gui\u002F$(id -u)\u002F\u003Clabel>\" 2>\u002Fdev\u002Fnull || true` before bootstrap\n\n**7. GUI vs system agents**\n- User agents: `~\u002FLibrary\u002FLaunchAgents\u002F` bootstrapped under `gui\u002F\u003Cuid>`\n- System agents: `\u002FLibrary\u002FLaunchDaemons\u002F` bootstrapped under `system`\n- Never use `launchctl load\u002Funload` (deprecated in macOS 10.11+)\n\n**8. The opencode bin\u002F pattern (real incident)**\n- Target was `~\u002F.cache\u002Fopencode\u002Fbin\u002F` (directory contents, not the dir itself)\n- Script used `find $TARGET -mtime +30` with no `-mindepth 1`\n- Dry-run showed `~\u002F.cache\u002Fopencode\u002Fbin` itself as a candidate (find with depth 0 includes the root)\n- Fix: always add `-mindepth 1` to exclude the target directory from results\n","# launchd-auto-cleanup\n\n## Contract\n\nThis skill guarantees:\n1. Every generated cleanup script has a `--dry-run` flag and runs a dry-run before any live deletion\n2. The generated script passes `validate-cleanup-script.sh` (no hardcoded paths, uses system tools only, has safety guards)\n3. The plist installer uses `StartCalendarInterval` (not `StartInterval`) for human-readable schedules\n4. The install sequence runs `launchctl bootout` (with `|| true`) before `bootstrap` to handle reinstalls\n5. `validate-cleanup-script.sh` is always run before reporting done\n\n## 10-Item Checklist Status\n\n| # | Item | Status |\n|---|------|--------|\n| 1 | SKILL.md | ✓ present |\n| 2 | Code (validate-cleanup-script.sh) | ✓ present |\n| 3 | Unit tests (test_validate.sh) | ✓ present |\n| 4 | Integration tests | N\u002FA — scripts are deterministic, LLM generates them; no live endpoints |\n| 5 | LLM evals | N\u002FA — cleanup targets vary by user; contract is structural not semantic |\n| 6 | Resolver trigger | pending — no RESOLVER.md found at ~\u002F.claude\u002Fskills\u002FRESOLVER.md |\n| 7 | Resolver trigger eval | pending (depends on #6) |\n| 8 | check-resolvable | pending (depends on #6) |\n| 9 | E2E test | pending — requires macOS launchd context |\n| 10 | Brain filing | N\u002FA — skill does not write to brain |\n\nItems 4–5 are intentionally N\u002FA: the skill's output is structurally deterministic (a bash script); quality is verified by `validate-cleanup-script.sh` and dry-run, not LLM evals. Items 6–9 are pending RESOLVER.md infrastructure.\n\n---\n\n## Phases\n\n### Phase 1: Audit Target\n\nBefore writing any files, gather:\n- **Target path**: directory, file pattern, or cache location to clean (e.g. `~\u002F.cache\u002Fopencode\u002Fbin\u002F`, `\u002Ftmp\u002F*.log`)\n- **Retention rule**: age threshold in days (e.g. \"older than 30 days\") or size threshold\n- **Schedule**: human schedule (e.g. \"every Sunday at 3am\", \"daily at 2am\")\n- **Label**: reverse-DNS launchd label (e.g. `com.user.cleanup-opencode-bin`)\n- **Script path**: where to install the cleanup script (e.g. `~\u002F.local\u002Fbin\u002Fcleanup-opencode-bin.sh`)\n\nAsk the user for any missing items before writing files.\n\n### Phase 2: Write Cleanup Script\n\nWrite `\u003Cscript_path>` with:\n\n```bash\n#!\u002Fusr\u002Fbin\u002Fenv bash\n# cleanup-\u003Ctarget>.sh — Auto-generated by launchd-auto-cleanup skill\n# Usage: cleanup-\u003Ctarget>.sh [--dry-run]\nset -euo pipefail\n\nDRY_RUN=false\n[[ \"${1:-}\" == \"--dry-run\" ]] && DRY_RUN=true\n\nTARGET_DIR=\"$HOME\u002F\u003Crelative\u002Fpath>\"\nRETENTION_DAYS=\u003CN>\nLOG_PREFIX=\"[cleanup-\u003Ctarget>]\"\n\nif [[ ! -d \"$TARGET_DIR\" ]]; then\n  echo \"$LOG_PREFIX Target dir does not exist: $TARGET_DIR — skipping\"\n  exit 0\nfi\n\necho \"$LOG_PREFIX Starting. DRY_RUN=$DRY_RUN\"\necho \"$LOG_PREFIX Target: $TARGET_DIR (older than ${RETENTION_DAYS} days)\"\n\n# Find candidates\nmapfile -t CANDIDATES \u003C \u003C(\n  \u002Fusr\u002Fbin\u002Ffind \"$TARGET_DIR\" -maxdepth 1 -mindepth 1 \\\n    -mtime +\"$RETENTION_DAYS\" 2>\u002Fdev\u002Fnull\n)\n\nif [[ ${#CANDIDATES[@]} -eq 0 ]]; then\n  echo \"$LOG_PREFIX No candidates found — nothing to do\"\n  exit 0\nfi\n\necho \"$LOG_PREFIX Found ${#CANDIDATES[@]} candidate(s):\"\nfor item in \"${CANDIDATES[@]}\"; do\n  echo \"  $item\"\ndone\n\nif $DRY_RUN; then\n  echo \"$LOG_PREFIX DRY RUN — no deletions performed\"\n  exit 0\nfi\n\nfor item in \"${CANDIDATES[@]}\"; do\n  if [[ -e \"$item\" ]]; then\n    \u002Fbin\u002Frm -rf \"$item\"\n    echo \"$LOG_PREFIX Deleted: $item\"\n  fi\ndone\n\necho \"$LOG_PREFIX Done\"\n```\n\nKey rules for the script body:\n- Use `$HOME` not `~` in variable assignments (both work in bash but `$HOME` is explicit and safe in all contexts)\n- Use absolute paths for all external commands: `\u002Fusr\u002Fbin\u002Ffind`, `\u002Fbin\u002Frm`, `\u002Fusr\u002Fbin\u002Fwc`, etc.\n- `-maxdepth 1 -mindepth 1` to avoid deleting the target dir itself\n- Always check `[[ -d \"$TARGET_DIR\" ]]` before operating\n- Log every action to stdout (plist's `StandardOutPath` captures it)\n\n### Phase 3: Write Install Script\n\nWrite `install-\u003Clabel>.sh`:\n\n```bash\n#!\u002Fusr\u002Fbin\u002Fenv bash\n# install-\u003Clabel>.sh — Installs launchd agent for cleanup-\u003Ctarget>\nset -euo pipefail\n\nLABEL=\"\u003Ccom.user.label>\"\nSCRIPT_SRC=\"$(cd \"$(dirname \"$0\")\" && pwd)\u002Fcleanup-\u003Ctarget>.sh\"\nSCRIPT_DST=\"$HOME\u002F.local\u002Fbin\u002Fcleanup-\u003Ctarget>.sh\"\nPLIST=\"$HOME\u002FLibrary\u002FLaunchAgents\u002F${LABEL}.plist\"\nLOG_DIR=\"$HOME\u002FLibrary\u002FLogs\"\n\n# Install script\n\u002Fbin\u002Fmkdir -p \"$HOME\u002F.local\u002Fbin\"\n\u002Fusr\u002Fbin\u002Finstall -m 0755 \"$SCRIPT_SRC\" \"$SCRIPT_DST\"\n\n# Write plist\n\u002Fbin\u002Fcat > \"$PLIST\" \u003C\u003C PLIST_EOF\n\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\u003C!DOCTYPE plist PUBLIC \"-\u002F\u002FApple\u002F\u002FDTD PLIST 1.0\u002F\u002FEN\"\n  \"http:\u002F\u002Fwww.apple.com\u002FDTDs\u002FPropertyList-1.0.dtd\">\n\u003Cplist version=\"1.0\">\n\u003Cdict>\n  \u003Ckey>Label\u003C\u002Fkey>\n  \u003Cstring>${LABEL}\u003C\u002Fstring>\n  \u003Ckey>ProgramArguments\u003C\u002Fkey>\n  \u003Carray>\n    \u003Cstring>\u002Fbin\u002Fbash\u003C\u002Fstring>\n    \u003Cstring>${SCRIPT_DST}\u003C\u002Fstring>\n  \u003C\u002Farray>\n  \u003Ckey>StartCalendarInterval\u003C\u002Fkey>\n  \u003Cdict>\n    \u003Ckey>Weekday\u003C\u002Fkey>\n    \u003Cinteger>0\u003C\u002Finteger>\n    \u003Ckey>Hour\u003C\u002Fkey>\n    \u003Cinteger>3\u003C\u002Finteger>\n    \u003Ckey>Minute\u003C\u002Fkey>\n    \u003Cinteger>0\u003C\u002Finteger>\n  \u003C\u002Fdict>\n  \u003Ckey>StandardOutPath\u003C\u002Fkey>\n  \u003Cstring>${LOG_DIR}\u002F${LABEL}.log\u003C\u002Fstring>\n  \u003Ckey>StandardErrorPath\u003C\u002Fkey>\n  \u003Cstring>${LOG_DIR}\u002F${LABEL}.error.log\u003C\u002Fstring>\n\u003C\u002Fdict>\n\u003C\u002Fplist>\nPLIST_EOF\n\necho \"Installed plist: $PLIST\"\necho \"Installed script: $SCRIPT_DST\"\n\n# Load agent (bootout first to handle reinstall)\n\u002Fbin\u002Flaunchctl bootout \"gui\u002F$(\u002Fusr\u002Fbin\u002Fid -u)\u002F${LABEL}\" 2>\u002Fdev\u002Fnull || true\n\u002Fbin\u002Flaunchctl bootstrap \"gui\u002F$(\u002Fusr\u002Fbin\u002Fid -u)\" \"$PLIST\"\necho \"Agent loaded: $LABEL\"\n```\n\n### Phase 4: Dry-Run Verify\n\nRun the cleanup script with `--dry-run` and review output:\n\n```bash\nbash \u003Cscript_path> --dry-run\n```\n\nExpected output:\n- Lists candidate files\u002Fdirs it would delete\n- Ends with \"DRY RUN — no deletions performed\"\n- Zero exit code\n\nIf dry-run output shows unexpected targets (e.g. the opencode bin\u002F itself, not just its contents), fix `find` flags (`-mindepth 1`) before proceeding.\n\nAlso run the validator:\n```bash\n~\u002F.claude\u002Fskills\u002Flaunchd-auto-cleanup\u002Fvalidate-cleanup-script.sh \u003Cscript_path>\n```\n\nDo not proceed to Phase 5 until both pass.\n\n### Phase 5: Install and Run Live\n\n```bash\nbash \u003Cinstall_script_path>\n```\n\nVerify agent is loaded:\n```bash\n\u002Fbin\u002Flaunchctl list | grep \u003Clabel>\n```\n\nRun live once to confirm no errors:\n```bash\nbash \u003Cscript_path>\n```\n\nCheck log:\n```bash\ntail -20 ~\u002FLibrary\u002FLogs\u002F\u003Clabel>.log\n```\n\n### Output Format\n\nReport at completion:\n```\nlaunchd-auto-cleanup complete\n  Script:    ~\u002F.local\u002Fbin\u002F\u003Cname>.sh\n  Plist:     ~\u002FLibrary\u002FLaunchAgents\u002F\u003Clabel>.plist\n  Schedule:  \u003Chuman schedule>\n  Dry-run:   PASS (\u003CN> candidates listed)\n  Validate:  PASS\n  Live run:  PASS (deleted N items) | PASS (nothing to delete)\n  Agent:     loaded (launchctl list shows \u003Clabel>)\n```\n\n---\n\n## Dry-Run Gate Protocol\n\n**Every cleanup script must be verified with `--dry-run` before any live run.** This is a hard gate — not a suggestion.\n\n### What a compliant dry-run looks like\n\n```\n[2026-05-22 04:05:01] === Section 1: ~\u002F.gemini\u002Ftmp\u002F ===\n[2026-05-22 04:05:01] gemini tmp: before 3.0G ($HOME\u002F.gemini\u002Ftmp)\n[2026-05-22 04:05:01] gemini tmp: [dry-run] would delete contents of $HOME\u002F.gemini\u002Ftmp\n...\n[2026-05-22 04:05:02] === DRY-RUN complete — no files deleted ===\n```\n\nKey signals to check in dry-run output:\n- Every intended target appears in the output\n- No unexpected targets (especially parent dirs, binaries, or active data dirs)\n- \"DRY-RUN complete\" or \"DRY RUN — no deletions performed\" at the end\n- Exit code 0\n\n### Red flags that require stopping and fixing before live run\n\n| Red flag | Root cause | Fix |\n|---|---|---|\n| Target dir itself listed as candidate | Missing `-mindepth 1` in `find` | Add `-mindepth 1` to exclude the root |\n| Binary or `bin\u002F` subdir listed | Over-broad `find` scope | Use explicit `SAFE_SUBDIRS` allowlist |\n| Protected path appears | No protection check | Add guard: `[[ \"$item\" == *\"\u002F.codex\u002Fsessions\"* ]] && continue` |\n| No output at all | Wrong path, wrong mtime, dir empty | Verify with `ls $TARGET` manually |\n| Non-zero exit | Script syntax\u002Flogic error | Fix before proceeding |\n\n### Dry-run enforcement in `validate-cleanup-script.sh`\n\nThe validator (`~\u002F.claude\u002Fskills\u002Flaunchd-auto-cleanup\u002Fvalidate-cleanup-script.sh`) checks that `--dry-run` is present in the script source. Run it before installing any script:\n\n```bash\n~\u002F.claude\u002Fskills\u002Flaunchd-auto-cleanup\u002Fvalidate-cleanup-script.sh \u003Cscript_path>\n```\n\nIf the validator passes but dry-run output looks wrong, the validator cannot catch runtime behavior — review the dry-run output manually.\n\n---\n\n## Safety Guard Checklist\n\nEvery generated cleanup script MUST satisfy all of the following. `validate-cleanup-script.sh` checks the mechanical ones:\n\n- [ ] `set -euo pipefail` at top of script\n- [ ] `--dry-run` flag handled and respected (no deletions when set)\n- [ ] All paths use `$HOME` not `~` in variable assignments\n- [ ] All external commands use absolute paths: `\u002Fusr\u002Fbin\u002Ffind`, `\u002Fbin\u002Frm`, `\u002Fusr\u002Fbin\u002Fwc`, etc.\n- [ ] No homebrew tools (`brew`, `nvm`, `npm`, `node`, `python3` via homebrew) — launchd PATH is `\u002Fusr\u002Fbin:\u002Fbin:\u002Fusr\u002Fsbin:\u002Fsbin` only\n- [ ] Existence check before `rm -rf` (check `[[ -e \"$item\" ]]` in the deletion loop)\n- [ ] Directory existence check before operating on target\n- [ ] Never delete `~\u002F.codex\u002Fsessions`, `~\u002F.claude\u002Fprojects`, or any path modified in last 14 days without `APPROVED` env var set\n- [ ] `.git\u002F` guard: only delete a directory if you intend to delete a git clone; add `[[ -d \"$item\u002F.git\" ]]` guard for git-clone targets\n- [ ] Log every deletion to stdout (caller redirects to log file via plist)\n- [ ] `-mindepth 1` in find to avoid deleting the target directory itself\n\n---\n\n## Common launchd Failure Patterns\n\n**1. StartInterval vs StartCalendarInterval**\n- `StartInterval` takes seconds (integer) — fires every N seconds from boot\n- `StartCalendarInterval` takes a dict (Hour, Minute, Weekday, Day, Month) — cron-style\n- Use `StartCalendarInterval` for human schedules (\"every Sunday at 3am\")\n- Common mistake: using `StartInterval 604800` (weekly) looks right but fires from boot time, not at a fixed clock time\n\n**2. RunAtLoad trap**\n- `RunAtLoad: true` fires the script on every login AND on the schedule\n- Omit `RunAtLoad` unless you explicitly want the job to run at login\u002Fbootstrap\n- If present accidentally, it causes unexpected runs on every reboot\n\n**3. PATH is minimal**\n- launchd agent PATH: `\u002Fusr\u002Fbin:\u002Fbin:\u002Fusr\u002Fsbin:\u002Fsbin` only\n- `brew`, `nvm`, `npm`, `node`, `python3` (homebrew), `rtk` — all unavailable\n- Fix: use absolute paths for every external command in the script\n\n**4. Script not executable**\n- `chmod +x` is not sufficient for cross-machine installs\n- Use `\u002Fusr\u002Fbin\u002Finstall -m 0755 src dst` in the install script\n\n**5. Log dir must exist**\n- `StandardOutPath` and `StandardErrorPath` dirs must exist before the agent runs\n- `~\u002FLibrary\u002FLogs\u002F` always exists on macOS — safe default\n\n**6. bootout before bootstrap for reinstalls**\n- If agent is already loaded, `launchctl bootstrap` errors\n- Pattern: `launchctl bootout \"gui\u002F$(id -u)\u002F\u003Clabel>\" 2>\u002Fdev\u002Fnull || true` before bootstrap\n\n**7. GUI vs system agents**\n- User agents: `~\u002FLibrary\u002FLaunchAgents\u002F` bootstrapped under `gui\u002F\u003Cuid>`\n- System agents: `\u002FLibrary\u002FLaunchDaemons\u002F` bootstrapped under `system`\n- Never use `launchctl load\u002Funload` (deprecated in macOS 10.11+)\n\n**8. The opencode bin\u002F pattern (real incident)**\n- Target was `~\u002F.cache\u002Fopencode\u002Fbin\u002F` (directory contents, not the dir itself)\n- Script used `find $TARGET -mtime +30` with no `-mindepth 1`\n- Dry-run showed `~\u002F.cache\u002Fopencode\u002Fbin` itself as a candidate (find with depth 0 includes the root)\n- Fix: always add `-mindepth 1` to exclude the target directory from results\n",{"name":5,"context":24,"triggers":48,"description":7,"when_to_use":57,"allowed-tools":58},[49,50,51,52,53,54,55,56],"automate cleanup","launchd cleanup job","schedule cleanup","cleanup job","auto clean","recurring cleanup","create launchd agent","install launchd plist","When user wants to automate recurring cleanup of a directory, cache, or temp files on macOS via launchd","Read, Write, Edit, Bash","main","42ccd02cbf5e8e47839dc3eab6fc754dc6b339cf",".claude\u002Fskills_archive\u002F2026-08-27-historical-zero-use\u002Flaunchd-auto-cleanup",0,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills","https:\u002F\u002Fgithub.com\u002Fjleechanorg","2026-11-14 03:18:13",{"source":8,"skillName":5,"commands":67,"prompt":77,"zipAvailable":78},[68,71,74],{"tool":69,"command":70},"npx","npx skills add jleechanorg\u002Fjleechan-skills --skill launchd-auto-cleanup",{"tool":72,"command":73},"bunx","bunx skills add jleechanorg\u002Fjleechan-skills --skill launchd-auto-cleanup",{"tool":75,"command":76},"pnpm","pnpm dlx skills add jleechanorg\u002Fjleechan-skills --skill launchd-auto-cleanup","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills_archive\u002F2026-08-27-historical-zero-use\u002Flaunchd-auto-cleanup\u002FSKILL.md",true,[80,86,90,94,100],{"path":81,"resourceType":82,"resourceTypeDesc":83,"sizeBytes":84,"sourceHtmlUrl":85},"manifest.json","REFERENCE","参考文档",86,"https:\u002F\u002Fgithub.com\u002Fmanagedcode\u002Fdotnet-skills\u002Fblob\u002Fmain\u002Fcatalog\u002FFrameworks\u002FEntity-Framework-6\u002Fskills\u002Fentity-framework6\u002Fmanifest.json",{"path":87,"resourceType":82,"resourceTypeDesc":83,"sizeBytes":88,"sourceHtmlUrl":89},"references\u002Fmigration.md",6063,"https:\u002F\u002Fgithub.com\u002Fmanagedcode\u002Fdotnet-skills\u002Fblob\u002Fmain\u002Fcatalog\u002FFrameworks\u002FEntity-Framework-6\u002Fskills\u002Fentity-framework6\u002Freferences\u002Fmigration.md",{"path":91,"resourceType":82,"resourceTypeDesc":83,"sizeBytes":92,"sourceHtmlUrl":93},"references\u002Fpatterns.md",8833,"https:\u002F\u002Fgithub.com\u002Fmanagedcode\u002Fdotnet-skills\u002Fblob\u002Fmain\u002Fcatalog\u002FFrameworks\u002FEntity-Framework-6\u002Fskills\u002Fentity-framework6\u002Freferences\u002Fpatterns.md",{"path":95,"resourceType":96,"resourceTypeDesc":97,"sizeBytes":98,"sourceHtmlUrl":99},"tests\u002Ftest_validate.sh","SCRIPT","脚本",6067,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills_archive\u002F2026-08-27-historical-zero-use\u002Flaunchd-auto-cleanup\u002Ftests\u002Ftest_validate.sh",{"path":101,"resourceType":96,"resourceTypeDesc":97,"sizeBytes":102,"sourceHtmlUrl":103},"validate-cleanup-script.sh",2755,"https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills_archive\u002F2026-08-27-historical-zero-use\u002Flaunchd-auto-cleanup\u002Fvalidate-cleanup-script.sh",[105,120,142,156,170,186],{"skillNo":106,"name":107,"nameZh":6,"description":108,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":109,"categories":110,"keywords":111,"publishedAt":43},"01M716Y5MTJ6GYJZ2ATXCJK8G4","instinct","Use when the user invokes \u002Finstinct or \u002Fin, or asks to \"message Instinct\", \"ask Instinct\", \"tell Instinct\", \"check Instinct\", or read replies from the configured peer assistant. iMessage first, WhatsApp Web as fallback.","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Finstinct\u002FSKILL.md",[],[107,31,23,112,113,114,115,116,117,34,118,119],"replies","check","use","identity","whatsapp","gmail","message","ask",{"skillNo":121,"name":122,"nameZh":6,"description":123,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":124,"categories":125,"keywords":131,"publishedAt":43},"01M716Y4HF7FVVMJSA2CCVN5VF","hr","Compatibility alias for history-resume; produce the same fresh-state, provenance-aware resume briefing.","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fhr\u002FSKILL.md",[126],{"categoryNo":127,"categoryName":128,"taxonomyType":129,"taxonomyTypeDesc":130},"01JBSEEDCAT000000000000038","人力资源","PROFESSION","适用职业",[122,132,133,134,135,136,137,138,139,140,141],"resume","compatibility","provenance","for","history","produce","alias","briefing","state","fresh",{"skillNo":143,"name":144,"nameZh":6,"description":145,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":146,"categories":147,"keywords":149,"publishedAt":43},"01M716Y1FFNFKFJYYKF8HQA6T8","history-resume","Resume briefing for the current repository branch or worktree using fresh state and admitted history sources.","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fhistory-resume\u002FSKILL.md",[148],{"categoryNo":127,"categoryName":128,"taxonomyType":129,"taxonomyTypeDesc":130},[136,132,150,151,31,152,139,153,135,154,155,140,141],"history resume","branch","analysis","repository","worktree","sources",{"skillNo":157,"name":158,"nameZh":6,"description":159,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":160,"categories":161,"keywords":162,"publishedAt":43},"01M716XWXJ8EPHK4RH9T14TR85","grok","Alias for the canonical grokbot browser conversation workflow; \u002Fgrok talks to the Grok bot the same way \u002Fdot talks to Dot.","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fgrok\u002FSKILL.md",[],[158,163,164,165,135,166,138,167,168,169],"dot","bot","workflow","canonical","conversation","browser","grokbot",{"skillNo":171,"name":172,"nameZh":6,"description":173,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":174,"categories":175,"keywords":176,"publishedAt":43},"01M70N9Q07DTE0EW5KFXMMMSJ0","codexa-bench","Compare `codexa` and normal `codex` on a fair split of comparable work by default; use `--strict` for duplicate runs of the exact same task.","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fcodexa-bench\u002FSKILL.md",[],[177,178,179,114,180,135,181,182,183,184,185],"codexa bench","work","split","default","duplicate","task","compare","normal","codex",{"skillNo":187,"name":188,"nameZh":6,"description":189,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":190,"licenseFileUrl":191,"sourceHtmlUrl":192,"categories":193,"keywords":205,"publishedAt":43},"01M6C24S2X60SP8BBFGAEKPEGW","frontend-design","Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts, posters, or applications (examples include websites, landing pages, dashboards, React components, HTML\u002FCSS layouts, or when styling\u002Fbeautifying any web UI). Generates creative, polished code and UI design that avoids generic AI aesthetics.","Apache-2.0","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Ffrontend-design\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fjleechanorg\u002Fjleechan-skills\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Ffrontend-design\u002FSKILL.md",[194,199,202],{"categoryNo":195,"categoryName":196,"taxonomyType":197,"taxonomyTypeDesc":198},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",{"categoryNo":200,"categoryName":201,"taxonomyType":197,"taxonomyTypeDesc":198},"01JBSEEDCAT000000000000015","视觉设计",{"categoryNo":203,"categoryName":204,"taxonomyType":129,"taxonomyTypeDesc":130},"01JBSEEDCAT000000000000032","设计师",[206,207,208,114,209,210,211,31,212,213,214,215,216,217,218,219,220,221,222,223,224,41,225,226,227,228,23,229],"frontend","design","frontend design","generic","ui design","react","html","components","code","artifacts","ui","aesthetics","Production","web components","landing","applications","quality","web","css","examples","with","react components","web ui","pages"]