Deletes the browser binaries your coding agents leave behind — and kills the ones still burning CPU after their agent died.
cleanup-chromes is a skill for AI agents that reclaims disk space on macOS from throwaway test-browser caches and macOS code_sign_clone leftovers — often 10–60+ GB — and terminates orphaned automation-browser process trees that heat up your Mac while you do nothing. It deletes only hardcoded, regenerable targets and kills only process trees that pass every orphan gate. Real browser profiles, /Applications, and live sessions are hard-refused.
AI coding agents (Claude Code, Codex, Cursor, …) drive real browsers. To do that, Playwright, Puppeteer, Cypress, Selenium, and chrome-devtools-mcp each download their own Chromium builds, and macOS creates extra code-sign copies of Chrome, Brave, Edge, and OpenAI apps on top. None of these tools clean up after themselves. The leftovers pile up in known locations:
~/Library/Caches/ms-playwright,~/.cache/puppeteer,~/Library/Caches/Cypress— downloaded browser builds, several GB each/private/var/folders/.../X/com.google.Chrome.code_sign_clone— macOS code-sign clones; Disk Utility andduwill show these at 38+ GB, but that is the apparent size (APFS shares blocks with the app itself) — the real gain is smaller and is measured only after deletion
If DaisyDisk, du, or Storage settings ("System Data") show gigabytes in places like these, this skill is the fix: it deletes exactly these agent-created leftovers — and nothing else. Every tool re-downloads what it needs on next use.
When a coding agent's session crashes or gets killed, the browser it was driving doesn't die with it. A headless Chrome and its Playwright daemon keep running for hours — spinning at 40–60% CPU on work nothing will ever collect — and on a fanless MacBook Air that alone makes the chassis hot. Busy CPU proves nothing: the orphan's debugging pipe is dead, so it can never be driven again. kill-orphans finds these trees and shuts them down, but only after every gate agrees they are truly abandoned (see 🧟 Orphaned browser processes).
/skill-installer cleanup-chromes from Rayan-and-beyond/cleanup-chromes
Restart Codex if it asks you to.
Manual install — copy the skill folder to ~/.agents/skills/:
git clone https://github.com/Rayan-and-beyond/cleanup-chromes.git /tmp/cc && cp -r /tmp/cc/skills/cleanup-chromes ~/.agents/skills/ && rm -rf /tmp/ccnpx skills add Rayan-and-beyond/cleanup-chromes -a claude-code -gOr Claude Code's native plugin flow:
/plugin marketplace add Rayan-and-beyond/cleanup-chromes
/plugin install cleanup-chromes@cleanup-chromes
Installs to ~/.claude/skills/cleanup-chromes.
npx skills add Rayan-and-beyond/cleanup-chromesGitHub CLI:
gh skill install Rayan-and-beyond/cleanup-chromesAnything that reads the open Agent Skills standard works — copy this folder into that agent's skills directory.
$cleanup-chromes — run a scan of agent browser leftovers
/cleanup-chromes run a scan of agent browser leftovers
Or just ask: "my disk is almost full, run a cleanup-chromes scan" — or "my Mac is hot and nothing is running, check for orphaned agent browsers". Your agent shows you the scan first and asks before deleting or killing anything.
git clone https://github.com/Rayan-and-beyond/cleanup-chromes.git && cd cleanup-chromes
./skills/cleanup-chromes/cleanup-chromes.sh scan # read-only, shows what's reclaimable
./skills/cleanup-chromes/cleanup-chromes.sh delete # removes only what passed every check
./skills/cleanup-chromes/cleanup-chromes.sh kill-orphans # reports orphaned browser trees (dry-run)The scan reports every target with a size and a verdict: SAFE, IN USE, or REFUSED (with the reason). Clone sizes are marked apparent — APFS shares their blocks with the app bundle, so only the delete summary's df-measured freed_mb is the true gain. Deletion ends with a machine-readable line:
SUMMARY mode=<scan|delete> freed_mb=<n> deleted=<n> skipped=<n> failed=<n>
Exit codes: 0 success · 1 one or more deletions failed · 2 invalid argument. Deletion results are appended to cleanup.log.
The second half of the skill. If your Mac runs hot with nothing open, an abandoned agent session is a likely culprit — check with:
./skills/cleanup-chromes/cleanup-chromes.sh kill-orphans # dry-run report
./skills/cleanup-chromes/cleanup-chromes.sh kill-orphans --do-it # terminate confirmed orphansA process tree is confirmed orphaned only when all four gates pass (default-deny):
| Gate | Meaning |
|---|---|
| 1. Browser + profile | command has --headless and its --user-data-dir lives in a temp dir (/var/folders, /tmp) — killing a temp profile can never lose personal browser data |
| 2. Dead launcher | every process above the browser is launchd-adopted (PPID=1) or itself an orphaned automation daemon — a live parent anchors the tree to an active session and rejects it |
| 3. No driver | --remote-debugging-pipe (far end was the dead parent → provably unusable) or --remote-debugging-port with no ESTABLISHED connection in lsof |
| 4. Known fingerprint | some command in the tree matches a known automation stack (Playwright, Puppeteer, Selenium, Cypress, chrome-devtools, rebrowser) — anything else is reported UNRECOGNIZED and never killed |
Kills are graceful (SIGTERM, 5s grace, then SIGKILL), ordered orchestrators-first so nothing respawns mid-cleanup, guarded against PID recycling, and logged to cleanup.log. The summary line:
SUMMARY mode=kill-orphans confirmed=<n> killed=<n> grace_killed=<n> unrecognized=<n> deleted=n/a skipped=0 failed=0
scan/delete never kill processes; kill-orphans never deletes files.
| Location | Usually left by |
|---|---|
~/Library/Caches/ms-playwright |
Playwright |
~/Library/Caches/ms-playwright-go |
Playwright Go |
~/.cache/ms-playwright |
Playwright |
~/.cache/puppeteer |
Puppeteer / Chrome for Testing |
~/Library/Caches/Cypress |
Cypress |
~/.cache/rebrowser-puppeteer |
rebrowser-puppeteer |
~/.cache/selenium |
Selenium |
~/.cache/chrome-devtools-mcp |
chrome-devtools-mcp |
~/Library/Caches/chromium |
Chromium-based test tooling |
/private/var/folders/*/*/X/*.code_sign_clone |
transient macOS code-sign clones |
macOS names each clone root after the owning app's bundle identifier, so the script allowlists exact bundle IDs and maps them to an exact process-name check (pgrep -x — a command that merely mentions e.g. "Codex" in its arguments does not count as Codex running):
| Bundle ID | Owner | Process guard |
|---|---|---|
com.google.Chrome |
Google Chrome | Google Chrome |
com.openai.codex |
Codex | Codex |
com.openai.chat |
ChatGPT | ChatGPT |
com.brave.Browser |
Brave Browser | Brave Browser |
com.microsoft.edgemac |
Microsoft Edge | Microsoft Edge |
Any clone root with an unknown bundle ID is refused and reported — never deleted automatically.
Before anything is deleted, the script:
- considers only the hardcoded throwaway locations above
- hard-refuses real Chrome, Brave, and Edge profiles plus everything in
/Applications - refuses unknown clone owners by default (default-deny)
- checks open files with
lsofand running owner apps with exact process-name matching - re-runs all checks immediately before each individual deletion
Before anything is killed, kill-orphans:
- requires all four orphan gates above to pass (default-deny — unmatched trees are reported, never signalled)
- protects PID 1, the script itself, and its parents
- re-verifies each pid's live command immediately before signalling (a recycled pid is skipped)
- logs every
--do-itrun with the root pid and full kill list
The default mode is scan, so running the script with no argument is non-destructive. kill-orphans is likewise a dry-run until you pass --do-it.
cleanup-chromes is not a general disk cleaner and not a general task manager. It touches only the hardcoded targets above and kills only trees that pass every orphan gate.
Note
If your disk is full for a different reason (Photos, Mail, Xcode, …), or your Mac runs hot for a different reason (a runaway app of your own, malware, …), this skill will not help — and will not touch anything related.
Deleted caches re-download the next time the relevant tool runs. du overstates clone sizes on APFS (copy-on-write shares blocks) — trust the script's measured freed_mb. A tiny check-then-delete race remains theoretically possible if a process starts at exactly the wrong instant; the script minimizes this by refreshing its checks immediately before each removal (and before each kill signal).
- macOS
- Bash (stock
/bin/bash3.2 is fully supported; the test suite targets it specifically) - standard macOS utilities including
lsof,du,df, andpgrep
Run the test suite with stock macOS bash (newer Homebrew bash will not reproduce the Bash 3.2 empty-array bugs):
/bin/bash tests/run_tests.shScan-mode and kill-orphans tests are read-only — fixtures inject fake process tables via the CLEANUP_CHROMES_PS_HOOK and CLEANUP_CHROMES_LSOF_HOOK isolation overrides, so the suite never inspects or signals real processes. Delete-mode tests self-block unless the script-under-test supports the CLEANUP_CHROMES_CLONE_GLOB isolation override, so running the suite against an un-hooked older revision can never touch real data.
New cleanup targets must be regenerable caches or verified clone owners — a real bundle ID plus its exact process name, proven with pgrep -x "<name>" while the app is running.
MIT License