Mole — macOS cleanup, uninstall, analyze, optimize, monitor
Mole is a terminal-first macOS maintenance toolkit: a Bash CLI (mole, aliased
mo) plus two Go/Bubble Tea TUI binaries (analyze-go, status-go). It
reclaims disk space, removes apps together with their remnants, purges
rebuildable project artifacts, and reports system health — with dry-run
previews, path protection, and an operations log on every destructive path.
It deletes files on someone's live machine. That single fact drives every
rule below.
When to use this skill
- A user wants to free disk space, find what is eating their disk, or fully
uninstall a Mac app including leftovers
- Reading Mole's machine-readable output (
--json / --watch / the dry-run
list file) instead of scraping a TUI
- Installing, updating, or removing Mole, or untangling a Homebrew-vs-script
install-channel conflict
- Configuring whitelists, purge scan paths, shell completion, or Touch ID
- Diagnosing a Mole run that skipped everything, hung, or asked for permissions
- Contributing to the
tw93/Mole repo (Bash 3.2 + Go, bats tests, the
# SAFE: deletion contract)
When not to use this skill
- Non-macOS cleanup, or Linux/Windows disk tooling → Mole is Darwin-only
- Questions about the paid Mole for Mac GUI (mole.fit) → separate
closed-source product, not this repo; the CLI is not a feature mirror of it
- Generic "delete these files for me" work with no Mole involved → use plain
shell; do not route arbitrary deletions through Mole
- Package management, app installation, or background monitoring → explicitly
out of scope per the project's own product filter
Instructions
Step 0: The five rules (non-negotiable)
These come from the project's own agent guide
(.claude/skills/mole/SKILL.md upstream) and override convenience:
- Preview before you delete. Always. Every destructive command takes
--dry-run. Run it, show the user what would go, then offer the real run.
The dry-run is the undo.
- The user runs the destructive command, not you — unless they asked for
it in this turn. "Clean my Mac" is such an ask; "why is my disk full" is not.
- Never parse a TUI frame.
mo analyze and TTY-attached mo status are
full-screen Bubble Tea programs whose output is drawn, not printed. Use the
JSON surfaces.
- Never invent flags. The surface is small. If it is not in
references/commands.md, run mo <command> --help. There is no --yes
and no --force on the cleanup commands.
- Protection is a whitelist, not an argument. To keep a cache, use
mo clean --whitelist — never a hand-rolled find/rm around the safety
layer.
Step 1: Pick exactly one mode
| Mode |
Use when |
Go to |
run-command |
the user wants space freed, an app gone, projects purged |
Step 2 |
automate |
you need data: disk map, health, what was deleted |
Step 3 |
install-update |
install, update, nightly, remove, channel conflict |
Step 4 |
configure |
whitelist, purge paths, completion, Touch ID, launchers |
Step 5 |
troubleshoot |
permissions, "did nothing", missing binary, recovery |
Step 6 |
contribute |
editing the tw93/Mole repo itself |
Step 7 |
Step 2: run-command — map the question to one command, preview, hand over
| The user asks |
Command |
| "What is eating my disk?" |
mo analyze --json (or scope it: mo analyze ~/Library --json) |
| "Free up space" |
mo clean --dry-run → review → mo clean |
| "Remove this app completely" |
mo uninstall --dry-run <app> → mo uninstall <app> |
| "My Mac feels slow" / caches look broken |
mo optimize --dry-run → mo optimize |
| "Clean up my old projects" |
mo purge --dry-run → mo purge |
| "Get rid of downloaded installers" |
mo installer --dry-run → mo installer |
| "What did Mole delete?" |
mo history --json --limit 20 |
Two things to say out loud before a real run:
mo clean and mo purge delete permanently. mo uninstall and
mo analyze route through Trash. Say which one applies.
mo optimize is the destructive command whose effect is not "files
disappear" — it flushes DNS, rebuilds Finder/icon caches, and touches
system services. Describe what it will do first.
For mo purge, distinguish locally rebuildable output (target/, build/,
dist/, .next/) from network-restorable dependencies (node_modules/,
Pods/, venv/, vendor/). The second kind is not recoverable offline.
Step 3: automate — use the four machine-readable surfaces
mo analyze --json ~/Library # one JSON object: entries[], large_files[], totals
mo status --json # one health snapshot
mo status --watch --interval 1s # NDJSON stream — BOUND IT, then terminate
mo history --json --limit 20 # sessions[] + the log paths
cat ~/.config/mole/clean-list.txt # every candidate from the last `mo clean --dry-run`
~/.config/mole/clean-list.txt is written by mo clean --dry-run only.
mo purge --dry-run and mo installer --dry-run print candidates to the
terminal and write no file.
mo status auto-switches to JSON when stdout is not a TTY, but pass --json
explicitly in scripts so intent stays obvious. Never leave --watch running
unbounded in the background.
Schemas and flags: references/commands.md.
Read-only helper: bash .agent-skills/mole/scripts/mole.sh doctor.
Step 4: install-update — pick the channel and stay on it
brew install mole # homebrew-core
curl -fsSL https://raw.githubusercontent.com/tw93/mole/main/install.sh | bash # script
Channel rules that cause most install failures:
install.sh refuses when it detects a Homebrew-owned install. Use
brew upgrade mole, or brew uninstall --force mole first.
mo update --nightly is script-install only. Homebrew users upgrade
with brew upgrade mole.
- The positional token
latest is a legacy alias for main — it installs
unreleased code, not the newest stable release. Pass a real tag
(1.51.0 / V1.51.0) if you want a pinned release.
- Installing to the default
/usr/local/bin prompts for an admin password on
every update. --prefix "$HOME/.local/bin" keeps future mo update
password-free.
install.sh is fail-closed: a checksum or attestation mismatch aborts and says
why; it never silently downgrades to a source build. Do not work around that.
Uninstall Mole itself with mo remove (--dry-run supported).
Step 5: configure — everything is a file under ~/.config/mole/
mo clean --whitelist # protected caches -> ~/.config/mole/whitelist
mo optimize --whitelist # protected maintenance -> ~/.config/mole/whitelist_optimize
mo purge --paths # scan dirs -> ~/.config/mole/purge_paths
mo completion # auto-detect shell and install
mo touchid enable|disable|status
Default purge scan dirs when purge_paths is unset: ~/Projects, ~/GitHub,
~/dev. Once custom paths are configured, only those are scanned.
Optional Raycast/Alfred launchers:
curl -fsSL https://raw.githubusercontent.com/tw93/Mole/main/scripts/setup-quick-launchers.sh | bash
Environment knobs (full list in references/commands.md): MO_DEBUG=1,
MO_NO_OPLOG=1, MOLE_OPLOG_PATH, MO_USE_FIND, MO_LAUNCHER_APP,
MOLE_ENABLE_DISK_VERIFY=1.
Step 6: troubleshoot — check the four usual causes first
- Permissions. Most "it skipped everything" reports are TCC. Grant Full
Disk Access to the terminal app in System Settings; Trash failures may also
need App Management or App Data.
- "Bundled analyzer binary not found."
bin/analyze-go / status-go are
missing — reinstall or mo update.
- Channel conflict. See Step 4;
install.sh refusing a Homebrew install
is intentional.
- "Did Mole take my file?" Do not guess.
mo history --json names the
deletions log; every deletion is one tab-separated line (timestamp, mode,
size, status, path). Read the actual line, then add the path to
mo clean --whitelist so the next run leaves it alone.
Add --debug to any command when it silently did nothing. Do not leave it on.
Known limits and the full protection model: references/safety.md.
Step 7: contribute — the safety contract is the review gate
git clone https://github.com/tw93/Mole.git && cd Mole
brew install shfmt shellcheck bats-core golangci-lint
git config core.hooksPath .githooks
make build # -> bin/analyze-go, bin/status-go
./scripts/check.sh --format
MOLE_TEST_NO_AUTH=1 ./scripts/test.sh
go test ./...
Read AGENTS.md in the repo first — CLAUDE.md is a symlink to it and it is
the cross-agent source of truth. Hard rules: route deletions through
mole_delete / safe_remove in lib/core/file_ops.sh; raw rm -rf needs an
inline # SAFE: <reason> annotation that CI checks for; Bash 3.2 compatible
with BSD (not GNU) command flags; never let verification block on a real sudo
or osascript prompt — use MOLE_TEST_NO_AUTH=1.
Details and hotspot ownership: references/contributing.md.
Best practices
- Dry-run, then hand the keyboard back. The preview is the only step the
user can veto, and for
mo clean / mo purge it is the only undo.
- Read
~/.config/mole/clean-list.txt, not the terminal summary, when you
need to reason about or show exactly what a real mo clean would remove.
- Say which deletion mode applies before every real run: permanent
(
clean, purge, installer) vs Trash (uninstall, analyze).
- Never scrape the TUI.
--json / --watch exist precisely so you do not
have to; a drawn frame is not a stable interface.
- Bound
--watch. Collect the samples the question needs, then terminate.
Never leave a monitor running in the background.
- Stay on one install channel. Homebrew and script installs do not mix,
and
--nightly only exists on the script channel.
- Whitelist instead of narrowing the command. Mole's protection lists are
the supported way to spare something; hand-rolled deletion around them
loses path validation, Trash routing, and the operations log.
- Do not run
mo update on a user's behalf unless they asked — and never
--nightly, which installs unreleased main.
- Trust the refusals. "When Mole cannot prove an item is safe to change,
it skips or refuses it." A skip with a reason is the product working, not a
bug to route around.
References
- references/commands.md — every command, flag, JSON schema, env var, and config/log path
- references/safety.md — the 5-layer protection model, protected prefixes and bundles, undo/Trash semantics, audit logs, known limitations
- references/contributing.md — build, test, release flow, the
# SAFE: contract, Bash 3.2 rules, hotspot ownership
- scripts/mole.sh — read-only
doctor / surfaces / json helper; never deletes, installs, or updates
- Mole repository · mole.fit (paid GUI, separate product)
- Upstream agent contract: AGENTS.md · SECURITY.md · docs/SECURITY_DESIGN.md
- Project standards:
.agent-skills/skill-standardization/SKILL.md
Examples
Example 1: "Why is my disk full?" — read-only, no deletion offered
bash .agent-skills/mole/scripts/mole.sh json analyze ~/Library
Report the largest entries[] and any insight: true rows. Do not chain
into mo clean — the user asked a question, not for a cleanup.
Example 2: "Clean my Mac" — preview, show, then hand over
mo clean --dry-run
cat ~/.config/mole/clean-list.txt
Summarize the candidate list by category and total size, warn that mo clean
deletes permanently, then let the user run mo clean themselves.
Example 3: Fully uninstall an app
mo uninstall --list # exact name Mole accepts
mo uninstall --dry-run slack # review app + leftovers
mo uninstall slack # routes through Trash
Example 4: Short diagnostic time series
mo status --json # one snapshot
timeout 10 mo status --watch --interval 1s # ~10 NDJSON lines, then stop
Example 5: Check the environment before recommending anything
bash .agent-skills/mole/scripts/mole.sh doctor
bash .agent-skills/mole/scripts/mole.sh surfaces
doctor reports macOS/arch, whether mo is installed and by which channel,
version, config/log presence, and optional fd — without installing,
updating, or deleting anything.
1---2name: mole3description: Drive Mole (`mo`), tw93's GPL-3.0 macOS maintenance CLI that cleans caches and app leftovers, uninstalls apps with their remnants, purges rebuildable project artifacts, removes downloaded installers, explores disk usage, runs bounded system optimization, and reports live health. Routes one request to one mode: run a command safely (`--dry-run` first, the user runs the destructive step), consume the JSON/NDJSON agent surfaces (`mo analyze --json`, `mo status --json` / `--watch`, `mo history --json`, `~/.config/mole/clean-list.txt`), install/update/remove on the right channel, configure whitelists and scan paths, troubleshoot, or contribute to the repo. Use when a user wants to free Mac disk space or fully uninstall a Mac app. Triggers on: mole, `mo clean`, `mo uninstall`, `mo analyze`, `mo purge`, `mo status`, tw93/Mole, mole.fit, clean my Mac, what is eating my disk, CleanMyMac / AppCleaner / DaisyDisk alternative, brew install mole.4---56# Mole — macOS cleanup, uninstall, analyze, optimize, monitor78Mole is a terminal-first macOS maintenance toolkit: a Bash CLI (`mole`, aliased9`mo`) plus two Go/Bubble Tea TUI binaries (`analyze-go`, `status-go`). It10reclaims disk space, removes apps together with their remnants, purges11rebuildable project artifacts, and reports system health — with dry-run12previews, path protection, and an operations log on every destructive path.1314**It deletes files on someone's live machine.** That single fact drives every15rule below.1617## When to use this skill1819- A user wants to free disk space, find what is eating their disk, or fully20 uninstall a Mac app including leftovers21- Reading Mole's machine-readable output (`--json` / `--watch` / the dry-run22 list file) instead of scraping a TUI23- Installing, updating, or removing Mole, or untangling a Homebrew-vs-script24 install-channel conflict25- Configuring whitelists, purge scan paths, shell completion, or Touch ID26- Diagnosing a Mole run that skipped everything, hung, or asked for permissions27- Contributing to the `tw93/Mole` repo (Bash 3.2 + Go, bats tests, the28 `# SAFE:` deletion contract)2930## When not to use this skill3132- Non-macOS cleanup, or Linux/Windows disk tooling → Mole is Darwin-only33- Questions about the paid **Mole for Mac** GUI (mole.fit) → separate34 closed-source product, not this repo; the CLI is not a feature mirror of it35- Generic "delete these files for me" work with no Mole involved → use plain36 shell; do not route arbitrary deletions through Mole37- Package management, app installation, or background monitoring → explicitly38 out of scope per the project's own product filter3940## Instructions4142### Step 0: The five rules (non-negotiable)4344These come from the project's own agent guide45(`.claude/skills/mole/SKILL.md` upstream) and override convenience:46471. **Preview before you delete. Always.** Every destructive command takes48 `--dry-run`. Run it, show the user what would go, then offer the real run.49 The dry-run *is* the undo.502. **The user runs the destructive command, not you** — unless they asked for51 it in this turn. "Clean my Mac" is such an ask; "why is my disk full" is not.523. **Never parse a TUI frame.** `mo analyze` and TTY-attached `mo status` are53 full-screen Bubble Tea programs whose output is drawn, not printed. Use the54 JSON surfaces.554. **Never invent flags.** The surface is small. If it is not in56 `references/commands.md`, run `mo <command> --help`. There is no `--yes`57 and no `--force` on the cleanup commands.585. **Protection is a whitelist, not an argument.** To keep a cache, use59 `mo clean --whitelist` — never a hand-rolled `find`/`rm` around the safety60 layer.6162### Step 1: Pick exactly one mode6364| Mode | Use when | Go to |65|---|---|---|66| `run-command` | the user wants space freed, an app gone, projects purged | Step 2 |67| `automate` | you need data: disk map, health, what was deleted | Step 3 |68| `install-update` | install, update, nightly, remove, channel conflict | Step 4 |69| `configure` | whitelist, purge paths, completion, Touch ID, launchers | Step 5 |70| `troubleshoot` | permissions, "did nothing", missing binary, recovery | Step 6 |71| `contribute` | editing the `tw93/Mole` repo itself | Step 7 |7273### Step 2: `run-command` — map the question to one command, preview, hand over7475| The user asks | Command |76|---|---|77| "What is eating my disk?" | `mo analyze --json` (or scope it: `mo analyze ~/Library --json`) |78| "Free up space" | `mo clean --dry-run` → review → `mo clean` |79| "Remove this app completely" | `mo uninstall --dry-run <app>` → `mo uninstall <app>` |80| "My Mac feels slow" / caches look broken | `mo optimize --dry-run` → `mo optimize` |81| "Clean up my old projects" | `mo purge --dry-run` → `mo purge` |82| "Get rid of downloaded installers" | `mo installer --dry-run` → `mo installer` |83| "What did Mole delete?" | `mo history --json --limit 20` |8485Two things to say out loud before a real run:8687- **`mo clean` and `mo purge` delete permanently.** `mo uninstall` and88 `mo analyze` route through Trash. Say which one applies.89- **`mo optimize` is the destructive command whose effect is not "files90 disappear"** — it flushes DNS, rebuilds Finder/icon caches, and touches91 system services. Describe what it will do first.9293For `mo purge`, distinguish locally rebuildable output (`target/`, `build/`,94`dist/`, `.next/`) from network-restorable dependencies (`node_modules/`,95`Pods/`, `venv/`, `vendor/`). The second kind is not recoverable offline.9697### Step 3: `automate` — use the four machine-readable surfaces9899```bash100mo analyze --json ~/Library # one JSON object: entries[], large_files[], totals101mo status --json # one health snapshot102mo status --watch --interval 1s # NDJSON stream — BOUND IT, then terminate103mo history --json --limit 20 # sessions[] + the log paths104cat ~/.config/mole/clean-list.txt # every candidate from the last `mo clean --dry-run`105```106107`~/.config/mole/clean-list.txt` is written by `mo clean --dry-run` only.108`mo purge --dry-run` and `mo installer --dry-run` print candidates to the109terminal and write no file.110111`mo status` auto-switches to JSON when stdout is not a TTY, but pass `--json`112explicitly in scripts so intent stays obvious. Never leave `--watch` running113unbounded in the background.114115Schemas and flags: `references/commands.md`.116Read-only helper: `bash .agent-skills/mole/scripts/mole.sh doctor`.117118### Step 4: `install-update` — pick the channel and stay on it119120```bash121brew install mole # homebrew-core122curl -fsSL https://raw.githubusercontent.com/tw93/mole/main/install.sh | bash # script123```124125Channel rules that cause most install failures:126127- `install.sh` **refuses** when it detects a Homebrew-owned install. Use128 `brew upgrade mole`, or `brew uninstall --force mole` first.129- `mo update --nightly` is **script-install only**. Homebrew users upgrade130 with `brew upgrade mole`.131- The positional token `latest` is a **legacy alias for `main`** — it installs132 unreleased code, not the newest stable release. Pass a real tag133 (`1.51.0` / `V1.51.0`) if you want a pinned release.134- Installing to the default `/usr/local/bin` prompts for an admin password on135 every update. `--prefix "$HOME/.local/bin"` keeps future `mo update`136 password-free.137138`install.sh` is fail-closed: a checksum or attestation mismatch aborts and says139why; it never silently downgrades to a source build. Do not work around that.140141Uninstall Mole itself with `mo remove` (`--dry-run` supported).142143### Step 5: `configure` — everything is a file under `~/.config/mole/`144145```bash146mo clean --whitelist # protected caches -> ~/.config/mole/whitelist147mo optimize --whitelist # protected maintenance -> ~/.config/mole/whitelist_optimize148mo purge --paths # scan dirs -> ~/.config/mole/purge_paths149mo completion # auto-detect shell and install150mo touchid enable|disable|status151```152153Default purge scan dirs when `purge_paths` is unset: `~/Projects`, `~/GitHub`,154`~/dev`. Once custom paths are configured, **only** those are scanned.155156Optional Raycast/Alfred launchers:157158```bash159curl -fsSL https://raw.githubusercontent.com/tw93/Mole/main/scripts/setup-quick-launchers.sh | bash160```161162Environment knobs (full list in `references/commands.md`): `MO_DEBUG=1`,163`MO_NO_OPLOG=1`, `MOLE_OPLOG_PATH`, `MO_USE_FIND`, `MO_LAUNCHER_APP`,164`MOLE_ENABLE_DISK_VERIFY=1`.165166### Step 6: `troubleshoot` — check the four usual causes first1671681. **Permissions.** Most "it skipped everything" reports are TCC. Grant Full169 Disk Access to the terminal app in System Settings; Trash failures may also170 need App Management or App Data.1712. **"Bundled analyzer binary not found."** `bin/analyze-go` / `status-go` are172 missing — reinstall or `mo update`.1733. **Channel conflict.** See Step 4; `install.sh` refusing a Homebrew install174 is intentional.1754. **"Did Mole take my file?"** Do not guess. `mo history --json` names the176 deletions log; every deletion is one tab-separated line (timestamp, mode,177 size, status, path). Read the actual line, then add the path to178 `mo clean --whitelist` so the next run leaves it alone.179180Add `--debug` to any command when it silently did nothing. Do not leave it on.181182Known limits and the full protection model: `references/safety.md`.183184### Step 7: `contribute` — the safety contract is the review gate185186```bash187git clone https://github.com/tw93/Mole.git && cd Mole188brew install shfmt shellcheck bats-core golangci-lint189git config core.hooksPath .githooks190make build # -> bin/analyze-go, bin/status-go191./scripts/check.sh --format192MOLE_TEST_NO_AUTH=1 ./scripts/test.sh193go test ./...194```195196Read `AGENTS.md` in the repo first — `CLAUDE.md` is a symlink to it and it is197the cross-agent source of truth. Hard rules: route deletions through198`mole_delete` / `safe_remove` in `lib/core/file_ops.sh`; raw `rm -rf` needs an199inline `# SAFE: <reason>` annotation that CI checks for; Bash 3.2 compatible200with BSD (not GNU) command flags; never let verification block on a real sudo201or `osascript` prompt — use `MOLE_TEST_NO_AUTH=1`.202203Details and hotspot ownership: `references/contributing.md`.204205## Best practices2062071. **Dry-run, then hand the keyboard back.** The preview is the only step the208 user can veto, and for `mo clean` / `mo purge` it is the only undo.2092. **Read `~/.config/mole/clean-list.txt`, not the terminal summary**, when you210 need to reason about or show exactly what a real `mo clean` would remove.2113. **Say which deletion mode applies** before every real run: permanent212 (`clean`, `purge`, `installer`) vs Trash (`uninstall`, `analyze`).2134. **Never scrape the TUI.** `--json` / `--watch` exist precisely so you do not214 have to; a drawn frame is not a stable interface.2155. **Bound `--watch`.** Collect the samples the question needs, then terminate.216 Never leave a monitor running in the background.2176. **Stay on one install channel.** Homebrew and script installs do not mix,218 and `--nightly` only exists on the script channel.2197. **Whitelist instead of narrowing the command.** Mole's protection lists are220 the supported way to spare something; hand-rolled deletion around them221 loses path validation, Trash routing, and the operations log.2228. **Do not run `mo update` on a user's behalf** unless they asked — and never223 `--nightly`, which installs unreleased `main`.2249. **Trust the refusals.** "When Mole cannot prove an item is safe to change,225 it skips or refuses it." A skip with a reason is the product working, not a226 bug to route around.227228## References229230- [references/commands.md](references/commands.md) — every command, flag, JSON schema, env var, and config/log path231- [references/safety.md](references/safety.md) — the 5-layer protection model, protected prefixes and bundles, undo/Trash semantics, audit logs, known limitations232- [references/contributing.md](references/contributing.md) — build, test, release flow, the `# SAFE:` contract, Bash 3.2 rules, hotspot ownership233- [scripts/mole.sh](scripts/mole.sh) — read-only `doctor` / `surfaces` / `json` helper; never deletes, installs, or updates234- [Mole repository](https://github.com/tw93/Mole) · [mole.fit](https://mole.fit) (paid GUI, separate product)235- Upstream agent contract: [AGENTS.md](https://github.com/tw93/Mole/blob/main/AGENTS.md) · [SECURITY.md](https://github.com/tw93/Mole/blob/main/SECURITY.md) · [docs/SECURITY_DESIGN.md](https://github.com/tw93/Mole/blob/main/docs/SECURITY_DESIGN.md)236- Project standards: `.agent-skills/skill-standardization/SKILL.md`237238## Examples239240### Example 1: "Why is my disk full?" — read-only, no deletion offered241242```bash243bash .agent-skills/mole/scripts/mole.sh json analyze ~/Library244```245246Report the largest `entries[]` and any `insight: true` rows. Do **not** chain247into `mo clean` — the user asked a question, not for a cleanup.248249### Example 2: "Clean my Mac" — preview, show, then hand over250251```bash252mo clean --dry-run253cat ~/.config/mole/clean-list.txt254```255256Summarize the candidate list by category and total size, warn that `mo clean`257deletes permanently, then let the user run `mo clean` themselves.258259### Example 3: Fully uninstall an app260261```bash262mo uninstall --list # exact name Mole accepts263mo uninstall --dry-run slack # review app + leftovers264mo uninstall slack # routes through Trash265```266267### Example 4: Short diagnostic time series268269```bash270mo status --json # one snapshot271timeout 10 mo status --watch --interval 1s # ~10 NDJSON lines, then stop272```273274### Example 5: Check the environment before recommending anything275276```bash277bash .agent-skills/mole/scripts/mole.sh doctor278bash .agent-skills/mole/scripts/mole.sh surfaces279```280281`doctor` reports macOS/arch, whether `mo` is installed and by which channel,282version, config/log presence, and optional `fd` — without installing,283updating, or deleting anything.