agentleFS
janrocnik791janrocnik791/mac-cleanup/SKILL.md

mac-cleanup

Skill0 starsChanged 3 months agoMITView on GitHub

Use when someone asks to clean up their Mac, free disk space, find where storage went, remove unused apps, clear caches, or deduplicate Downloads. Measures first, then trashes in approved batches.

8 other projects use a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

9 rules. 0 are common, 6 are distinctive, and 3 are very specific to this project.

  • 0

    Common

    Most of the 4 edited versions we read keep these.

  • 6

    Distinctive

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Create ~/Cleanup-<YYYY-MM-DD>/ and write baseline.txt containing the output of the measurement pass below.

    line 43
  2. Distinctive:

    Before proposing any deletion, show the user a table of where their space actually is.

    line 89
  3. Distinctive:

    Run in ascending order of risk.

    line 95
  4. Distinctive:

    Do this efficiently — never hash the whole disk:

    line 122
  5. Very specific:

    When an app is approved for removal, take its satellites too — but list them first: ~/Library/Application Support/<name>, …

    line 151
  6. Distinctive:

    After each batch: confirm every path in the manifest no longer resolves at its original location.

    line 201
  7. Very specific:

    After Categories 4–6: launch the affected apps.

    line 206
  8. Distinctive:

    When a user pushes back that the reclaimed total looks small, treat it as a signal the survey was wrong, not as a request to delete more.

    line 237
  9. Distinctive:

    Prefer being wrong toward keeping.

    line 240

“Common” is measured: kept by at least 6 in 10 of the 4 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

What This Skill Does

Finds where disk space actually went on a Mac, then removes clutter in reviewed batches. Nothing is deleted permanently — everything goes to ~/.Trash and stays recoverable via Finder → Put Back until the user empties it themselves.

The skill is measurement-first. Most cleanup advice fails because the survey is wrong, not because the deletion is wrong. Half of this document is about not measuring badly.

$1 may narrow scope (downloads, apps, caches, dev). If empty, run all categories.

Non-Negotiable Rules

  1. Trash only. Move files with Finder so Put Back is preserved: osascript -e 'tell application "Finder" to delete POSIX file "/abs/path"' A raw mv to ~/.Trash destroys the Put Back record. Never use rm.
  2. Never empty the Trash. That is permanent deletion and it is the user's action, not yours. Say so explicitly when handing back.
  3. Never auto-accept a software licence. Many .dmg files block hdiutil attach behind an SLA. Do not agree on the user's behalf — identify the file another way (see Category 1).
  4. Per-category approval. Present each batch as a table of paths + sizes + why-flagged, with a total, and wait for a yes. One batch, one approval.
  5. Never touch /System, /Library, anything needing sudo, Keychain, browser profiles, or iCloud/Dropbox-synced directories (a local delete propagates to their other devices).
  6. User project directories are report-only. Source trees, git checkouts and documents get listed with sizes and reasons. The user decides file by file. The one exception, if they approve it: node_modules and build output, which a package manager regenerates.

Step 0 — Set Up the Audit Trail

Create ~/Cleanup-<YYYY-MM-DD>/ and write baseline.txt containing the output of the measurement pass below. Every subsequent batch appends to manifest.md in that directory: path, size, mtime, and the reason it was flagged. This is the record the user needs to reverse a decision after the Trash is emptied.

Also record a KEPT section — things you flagged and then cleared. It is as valuable as the removal log, because it stops the next run re-investigating the same false positives.

Step 1 — Measure Correctly

Four traps, all of which produce a wrong survey:

du -sh ~/* silently skips every dotfolder. On a developer's machine the dotfolders are usually the majority of the home directory. Always:

du -sh ~/* ~/.[!.]* 2>/dev/null | sort -rh | head -40

/Applications is not under $HOME. Measure it separately, along with /usr/local, /opt/homebrew, and /Library/Caches.

du overstates physical usage wherever APFS clones or hardlinks exist — very common in node_modules and duplicated extension trees. It reports logical bytes. Treat du totals as an upper bound and never quote them as "space you will get back".

df is the only truth. Record before and after:

df -h /System/Volumes/Data
diskutil info / | grep -E "Free|Used"

Report the real reclaimed number afterwards, including when it undershoots.

Also check for APFS local snapshots (tmutil listlocalsnapshots /) — they hold space that du cannot see and that no file deletion will free.

Then drill down: from the top-level totals, recurse into the largest three or four directories until you can name the specific thing consuming the space. Stop descending when a directory is under ~2% of the total.

Step 2 — Present the Map

Before proposing any deletion, show the user a table of where their space actually is. This usually reframes the whole job — the clutter they asked about is often not where the space went. Get their reaction before proposing batches.

Categories

Run in ascending order of risk. Each is one approval.

Category 1 — Installers whose app is already installed

Lowest risk: the app is on disk, so the installer is redundant.

Match on bundle identifier, never filename. Filenames lie — VSCode-darwin-universal.dmg → Visual Studio Code.app, googlechrome.dmg → Google Chrome.app. Build an index of installed apps first, then match. For .dmg files you cannot mount (SLA), read the download origin instead:

mdls -name kMDItemWhereFroms "$f"

The origin URL names the vendor without agreeing to anything. See reference.md for the full matching pipeline.

Installers with no matching installed app are not duplicates — they belong in Category 3, because the question there is "do you want this app?"

Category 2 — Byte-identical duplicates

Two passes. Within Downloads first (the (1), (2) suffix families), then across the filesystem to implement the general rule: if a file exists elsewhere and also sits in Downloads, the Downloads copy goes.

Do this efficiently — never hash the whole disk:

  1. Hash every file in Downloads over 4 KB.
  2. Collect files elsewhere whose exact byte size matches one of those. Size comparison is cheap and eliminates almost everything.
  3. Hash only that shortlist. Confirm with SHA-256.
  4. Trash the Downloads copy. Never the copy that lives elsewhere.

Watch for versioned series. Files like report (1..7).zip with differing sizes are successive versions, not duplicates. Present them separately and let the user choose.

Category 3 — Unused applications

Three signals, because any one alone is wrong:

  • mdls -name kMDItemLastUsedDate — unreliable alone. A dozen apps sharing an identical timestamp to the second is a login or reindex batch event, not real use. Check for clustered identical times and discard them.
  • mdls -name kMDItemUseCount — misses CLI and login-item launches. An app launched via a shim in /usr/local/bin shows a near-zero count while being used daily. Check for such a shim before believing a low count.
  • Preference-plist mtime (~/Library/Preferences/<bundleid>.plist) — the most honest of the three. An app written to yesterday is in use.

Also weigh what the app is for. Tools used in bursts (IDEs, CAD, video editors) look idle for months and are still wanted. Report the idle period and the app's total footprint; let the user judge.

When an app is approved for removal, take its satellites too — but list them first: ~/Library/Application Support/<name>, ~/Library/Caches/<bundleid>, ~/Library/Preferences/<bundleid>.plist, ~/Library/Saved Application State/, and any large SDK/toolchain directory the app owns (these are frequently far larger than the app itself).

Category 4 — Orphaned support directories

~/Library/Application Support accumulates directories for uninstalled apps.

The folder-name heuristic produces false positives and this is the most dangerous category. Real examples: Code is VS Code, Google is Chrome, open-whispr is OpenWhispr — all installed, all active. Resolve every directory to a running or installed app before flagging it, and check its mtime: a directory written to today is not orphaned regardless of what it is called.

Present the list for confirmation. Never act on the name match alone.

Category 5 — Regenerable caches

Safe in the sense that they rebuild, at the cost of a slow next launch. Quit the owning app first, or it rewrites the directory mid-operation.

Typical: Homebrew (use brew cleanup -s --prune=all, not manual deletion), ~/Library/Caches/*, stale updater caches holding already-applied updates, ~/Library/Developer/Xcode/DerivedData, package-manager caches (~/.npm/_cacache, ~/.cache/uv, ~/.gradle, ~/.cargo/registry).

Tell the user what each cache costs to rebuild — a 4 GB npm cache is cheap to lose, a large VM or container image may be a long re-download.

Category 6 — Editor extensions and versioned installs

Frequently the single largest and least-obvious win. Editors keep every installed version of an extension on disk while registering only the current one. Compare the on-disk directory listing against the editor's own registry (for VS Code: ~/.vscode/extensions/extensions.json) and treat unregistered directories as stale. See reference.md for the exact comparison.

Same pattern applies to cached installer packages (.vsix, .crx, npm tarballs).

Category 7 — Documents and code (report only)

Produce a report, move nothing. Cover: node_modules and build-output trees with sizes, git checkouts with no commits in 12+ months and a clean working tree, large archived project directories, screenshot folders, and cross-directory duplicates. Present findings and let the user decide.

Verification

After each batch: confirm every path in the manifest no longer resolves at its original location. ~/.Trash may not be readable from the agent sandbox — verify by source-absence rather than by listing the Trash, or read it via osascript -e 'tell application "Finder" to get name of every item of trash'.

After Categories 4–6: launch the affected apps. Each should start normally. A cache clear may make the first launch slow; nothing should error or lose settings. Run brew doctor if Homebrew was touched.

At the end: re-run df -h, report actual reclaimed space against the manifest total, and state plainly that emptying the Trash is the point of no return and is theirs to do.

If Emptying the Trash Fails

Finder reports "the item X is in use". Diagnose before suggesting a restart:

lsof 2>/dev/null | grep "\.Trash"

Zero hits means nothing holds it and the lock was transient — retrying usually works. Beware the near-miss: a grep node_modules will match Electron apps' own bundled copies inside /Applications, which look alarming and are unrelated. Match on the .Trash path, not the basename.

lsof +D ~/.Trash is a hollow check — it cannot descend into a directory the caller cannot read, and returns empty either way. Also check for drag-shelf utilities holding a reference, and for mdworker indexing the freshly-moved folder.

Notes

  • Say early whether the disk is actually under pressure. If it is not, the goal is tidiness, and there is no reason to touch anything ambiguous — say that rather than maximising the number.
  • When a user pushes back that the reclaimed total looks small, treat it as a signal the survey was wrong, not as a request to delete more. Re-measure with the Step 1 traps in mind.
  • Prefer being wrong toward keeping. A false positive costs the user real data; a false negative costs some disk space on a disk that usually has plenty.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.