# Mole Admin & Architecture Guide

This guide is the deep reference for administrators, power users, contributors, and architects who need to understand how Mole works internally: its architecture, configuration surface, security model, operational behavior, build and release pipeline, and the design constraints that shape it. For task-oriented usage, see the companion **User Guide**.

---

## Table of Contents

1. [Executive Summary](#1-executive-summary)
2. [Architecture Overview](#2-architecture-overview)
3. [Component Inventory](#3-component-inventory)
4. [System Requirements](#4-system-requirements)
5. [Installation & Filesystem Layout](#5-installation--filesystem-layout)
6. [Configuration Reference](#6-configuration-reference)
7. [Security Architecture](#7-security-architecture)
8. [Operations](#8-operations)
9. [Build & Release Pipeline](#9-build--release-pipeline)
10. [Module Reference (Internals)](#10-module-reference-internals)
11. [The Go Binaries](#11-the-go-binaries)
12. [Testing Strategy](#12-testing-strategy)
13. [Code Quality & Conventions](#13-code-quality--conventions)
14. [Potential Improvements](#14-potential-improvements)
15. [Quick Reference](#15-quick-reference)

---

## 1. Executive Summary

**Mole** is a macOS system-maintenance toolkit delivered as a command-line program. It consolidates disk cleanup, application uninstallation, system optimization, disk-usage analysis, and live system monitoring into a single tool. It is licensed MIT and authored by tw93.

The codebase is a **hybrid of Bash and Go**:

- A **Bash core** implements the CLI dispatcher, the destructive cleanup/uninstall/optimize/purge/installer flows, the safety and path-validation logic, the operation logs, and the interactive menus.
- Two **Go binaries** (`analyze`, `status`) implement the two interactive TUIs that benefit from concurrency and rich rendering — the disk-space explorer and the live system dashboard — built on the [Bubble Tea](https://github.com/charmbracelet/bubbletea) / [Lip Gloss](https://github.com/charmbracelet/lipgloss) stack with system metrics from [gopsutil](https://github.com/shirou/gopsutil).

The central engineering principle, stated explicitly in the source and its design docs, is **safety over speed**: because Mole deletes files and shells out to system tools, every destructive path passes through layered validation, an extensive protected-bundle/protected-path list, Trash-by-default semantics for uninstalls, and a forensic deletion log.

| Attribute | Value |
|-----------|-------|
| Version (documented) | `1.42.0` |
| Platform | macOS only (Intel & Apple Silicon) |
| Language | Bash (CLI + safety) + Go `1.25` (TUIs) |
| License | MIT |
| Distribution | Homebrew formula + `install.sh` script |
| Tagline | *Deep clean and optimize your Mac.* |

---

## 2. Architecture Overview

Mole's runtime is a layered shell program with two compiled helpers invoked as subprocesses.

```
                          mo  (alias)  ──exec──►  mole  (main entrypoint)
                                                    │
                          ┌─────────────────────────┼──────────────────────────────┐
                          │      mole: arg parse, dispatch, update/remove flows,     │
                          │      interactive 5-item menu, version/help               │
                          └─────────────────────────┬──────────────────────────────┘
                                                    │ exec $SCRIPT_DIR/bin/<cmd>.sh
            ┌───────────────┬───────────────┬───────┴───────┬───────────────┬───────────────┐
            ▼               ▼               ▼               ▼               ▼               ▼
       bin/clean.sh   bin/uninstall.sh bin/optimize.sh  bin/purge.sh   bin/installer.sh  bin/{analyze,status}.sh
            │               │               │               │               │               │
            │  source       │  source       │  source       │  source       │               │ exec
            ▼               ▼               ▼               ▼               ▼               ▼
   ┌──────────────────────────────────────────────────────────────────┐   ┌─────────────────────┐
   │                       lib/  (sourced Bash modules)                │   │  bin/analyze-go     │
   │  core/  clean/  optimize/  uninstall/  manage/  check/  ui/       │   │  bin/status-go      │
   │  ── safety, file_ops, app_protection, logging, timeouts, menus ── │   │  (Go / Bubble Tea)  │
   └──────────────────────────────────────────────────────────────────┘   └─────────────────────┘
```

### Key flows

- **Dispatch.** `mole` collects args (peeling off the global `--debug`), then a single `case` statement routes the first token to the matching `bin/<cmd>.sh` via `exec`. `history` is dispatched *early* (before sourcing the full library) for speed. Unknown tokens print a usage hint and exit non-zero.
- **Library loading.** Each `bin/*.sh` sources `lib/core/common.sh`, which is the single include point that pulls in `base.sh`, `log.sh`, `timeout.sh`, `timeouts.sh`, `file_ops.sh`, `help.sh`, `ui.sh`, `app_protection.sh`, `bundle_resolver.sh`, `pkg_receipts.sh`, and `sudo.sh`. Every module guards against double-sourcing with a `*_LOADED` sentinel.
- **The filesystem is the state.** There is no daemon or database. Configuration, channel metadata, whitelist, and logs live as plain files under `~/.config/mole`, `~/.cache/mole`, and `~/Library/Logs/mole`.
- **Go helpers are leaf processes.** `bin/analyze.sh` and `bin/status.sh` are 15-line wrappers that `exec` the bundled `analyze-go` / `status-go` binaries and print a "reinstall / `mo update`" hint if the binary is missing.

> [!NOTE]
> The `mole` script is relocatable. At install time the installer rewrites the `SCRIPT_DIR=` line to point at the installed library root (`~/.config/mole`), so the single dispatcher in `/usr/local/bin` finds its sourced modules regardless of where it was installed from.

---

## 3. Component Inventory

| Path | Type | Responsibility |
|------|------|----------------|
| `mole` | Bash | Main entrypoint: dispatch, menu, update/remove, version/help |
| `mo` | Bash | Thin alias → `exec mole "$@"` |
| `install.sh` | Bash | Standalone installer/updater (fetch, verify, place files, build binaries) |
| `bin/clean.sh` | Bash | `clean` command (largest flow, ~51 KB) |
| `bin/uninstall.sh` | Bash | `uninstall` command (~68 KB) |
| `bin/optimize.sh` | Bash | `optimize` command |
| `bin/purge.sh` | Bash | `purge` command (project artifacts) |
| `bin/installer.sh` | Bash | `installer` command (installer-file cleanup) |
| `bin/history.sh` | Bash | `history` command (log reader/renderer) |
| `bin/completion.sh` | Bash | `completion` command (bash/zsh/fish) |
| `bin/touchid.sh` | Bash | `touchid` command (PAM `sudo` configuration) |
| `bin/analyze.sh` / `bin/status.sh` | Bash | Thin launchers for the Go binaries |
| `bin/analyze-go` / `bin/status-go` | Go binary | Compiled TUIs (built/downloaded at install) |
| `lib/core/` | Bash (15 files) | Safety, file ops, app protection, logging, timeouts, UI primitives |
| `lib/clean/` | Bash (12 files) | Cleanup flows per category (apps, browsers, dev, system, …) |
| `lib/optimize/` | Bash (3 files) | Diagnostics, maintenance, tasks |
| `lib/uninstall/` | Bash | Batch + Homebrew uninstall helpers |
| `lib/manage/` | Bash | Whitelist and purge-path management |
| `lib/check/` | Bash | Health JSON, diagnostics |
| `lib/ui/` | Bash | Reusable menus, paginated/simple selectors, app selector |
| `cmd/analyze/` | Go (24 files) | Disk analyzer: scanner, cache, delete, model, view, JSON |
| `cmd/status/` | Go (24 files) | Status dashboard: metrics collectors, process watch, view |
| `internal/units/` | Go | Byte-formatting helpers |
| `tests/` | Bats + shell (49 files) | Behavior, safety, fuzz corpora |
| `scripts/` | Bash | `check.sh`, `test.sh`, release/tap helpers |

---

## 4. System Requirements

| Requirement | Detail |
|-------------|--------|
| Operating system | macOS (Intel `amd64` or Apple Silicon `arm64`). The installer hard-fails on non-Darwin. |
| Shell | Bash. The code is carefully written to remain **Bash 3.2-compatible** (the macOS system default) — no `${var,,}`, no namerefs; lowercasing uses `tr`, indirect writes use `eval`. |
| `curl` or `wget` | For fetching the installer and updates. |
| Go toolchain | Only needed to build the helper binaries from source (e.g. edge installs without prebuilt assets). `go.mod` requires Go `1.25.0`. |
| Homebrew | Optional, for the Homebrew install/update path. |
| `fd` | Optional but recommended for `purge` performance. |
| `gh` (GitHub CLI) | Optional, enables Sigstore build-provenance verification of release assets. |
| Full Disk Access | Recommended for the terminal so deep cleans can reach protected cache locations. |

### Go dependencies (direct)

| Module | Version | Use |
|--------|---------|-----|
| `github.com/charmbracelet/bubbletea` | `v1.3.10` | TUI event loop (Elm-style) |
| `github.com/charmbracelet/lipgloss` | `v1.1.0` | Terminal styling/layout |
| `github.com/shirou/gopsutil/v4` | `v4.26.5` | CPU/mem/disk/net/process metrics |
| `github.com/cespare/xxhash/v2` | `v2.3.0` | Fast hashing (scan cache keys) |
| `golang.org/x/sync` | `v0.20.0` | Concurrency primitives |

---

## 5. Installation & Filesystem Layout

### Install methods

| Method | Command | Channel |
|--------|---------|---------|
| Homebrew | `brew install mole` | stable |
| Script (stable) | `curl -fsSL …/install.sh \| bash` | stable |
| Script (edge/nightly) | `… \| bash -s latest` | nightly (`main`) |
| Script (pinned) | `… \| bash -s 1.17.0` | that tag |

`install.sh` flags: `--prefix DIR` (install dir, default `/usr/local/bin`), `--config DIR` (config root, default `$HOME/.config/mole`), `--update`, `--verbose`/`-v`. Version tokens `latest`/`main`/`dev`/`<tag>` select the channel.

### On-disk layout after install

```
/usr/local/bin/
  mole                       # dispatcher (SCRIPT_DIR rewritten to config root)
  mo                         # alias

~/.config/mole/
  bin/                       # analyze-go, status-go helper binaries
  lib/                       # full sourced library tree (core, clean, …)
  install_channel            # CHANNEL=stable|nightly|dev [+ COMMIT_HASH=...]
  purge_paths                # user-configured purge scan roots
  status_prefs               # mo status preferences (e.g. cat_hidden=true)
  whitelist                  # protected paths for `clean`
  whitelist_optimize         # protected items for `optimize`
  README.md, LICENSE, install.sh

~/.cache/mole/
  version_check, update_message   # background update-notice cache

~/Library/Logs/mole/
  mole.log                   # general log
  operations.log             # every destructive op (clean/uninstall/…)
  deletions.log              # forensic Trash/permanent deletion audit (TSV)
```

> [!IMPORTANT]
> File installs are **atomic**: the installer copies to `mole.new`, `chmod +x`, then `mv -f` into place, so an interrupted update never leaves a half-written dispatcher. The same pattern is used for the `mo` alias.

---

## 6. Configuration Reference

Mole has no config-file DSL; behavior is governed by a small set of files plus environment variables, with the bulk of internal tuning held as readonly constants in `lib/core/base.sh` and `lib/core/timeouts.sh`.

### 6.1 User-facing environment variables

| Variable | Default | Effect |
|----------|---------|--------|
| `NO_COLOR` | unset | Any non-empty value disables all ANSI color (honors no-color.org) |
| `MO_NO_OPLOG` | unset | `1` disables operation logging to `operations.log` |
| `MO_DEBUG` | `0` | `1` enables verbose debug logging (equivalent to `--debug`) |
| `MO_LAUNCHER_APP` | auto | Override terminal auto-detection for quick launchers |
| `MO_ANALYZE_PATH` | unset | Default target path for `mo analyze` |
| `MOLE_VERSION` | unset | Installer channel/version selector (`main`/`dev`/tag) |
| `MOLE_EDGE_INSTALL` | unset | `true` forces the edge/nightly install path |
| `MOLE_REQUIRE_ATTESTATION` | `0` | `1` makes update fail unless the build-provenance attestation verifies |
| `MOLE_DRY_RUN` | `0` | `1` routes all deletions through dry-run logging (set by `--dry-run`) |
| `MOLE_DELETE_MODE` | `permanent` | `permanent` or `trash`; selects the deletion contract in `mole_delete()` |

### 6.2 Operational tuning constants (selected, from `base.sh`)

| Constant | Value | Meaning |
|----------|-------|---------|
| `MOLE_TEMP_FILE_AGE_DAYS` | 7 | Temp-file retention |
| `MOLE_ORPHAN_AGE_DAYS` | 30 | Orphaned app-data retention |
| `MOLE_DOTDIR_ORPHAN_AGE_DAYS` | 60 | Orphan dotfile hint threshold |
| `MOLE_MAX_PARALLEL_JOBS` | 15 | Parallel job ceiling |
| `MOLE_LOG_AGE_DAYS` | 7 | Log retention |
| `MOLE_CRASH_REPORT_AGE_DAYS` | 7 | Crash-report retention |
| `MOLE_SAVED_STATE_AGE_DAYS` | 30 | Saved-state retention |
| `MOLE_GPU_CACHE_AGE_DAYS` | 1 | Rebuildable GPU cache retention |
| `MOLE_TM_BACKUP_SAFE_HOURS` | 48 | Time Machine backup safety window |
| `MOLE_MAX_DS_STORE_FILES` | 500 | Max `.DS_Store` per scan |
| `MOLE_MAX_ORPHAN_ITERATIONS` | 100 | Orphan-scan iteration cap |
| `MOLE_HISTORY_DEFAULT_LIMIT` | 20 | Default `mo history` entries |
| `MOLE_HISTORY_MAX_LIMIT` | 200 | Max `mo history` entries |

> [!NOTE]
> Dozens of additional `MOLE_*` variables exist as internal timeouts, caches, and test-mode toggles (e.g. `MOLE_TEST_MODE`, `MOLE_TEST_NO_AUTH`, `MOLE_TIMEOUT_*`). These are implementation details and test seams, not a supported public configuration surface.

### 6.3 Whitelists

Two whitelist files protect paths from cleanup and optimization:

- `~/.config/mole/whitelist` — managed by `mo clean --whitelist`
- `~/.config/mole/whitelist_optimize` — managed by `mo optimize --whitelist` (legacy: `whitelist_checks`)

Whitelist entries support globs and are matched with normalization (trailing/duplicate slashes collapsed) and parent/child awareness, so protecting a child path also protects its parent directory entry. A built-in `DEFAULT_WHITELIST_PATTERNS` array additionally protects high-value caches out of the box, including:

```
~/Library/Caches/ms-playwright*      ~/.gradle/caches/*       ~/.ollama/models/*
~/.cache/huggingface*                ~/.gradle/daemon/*       ~/Library/Caches/JetBrains*
~/.m2/repository/*                    ~/Library/Caches/pypoetry/virtualenvs*
~/Library/Application Support/JetBrains*   ~/Library/Caches/com.jetbrains.toolbox*
```

---

## 7. Security Architecture

Mole treats deletion boundaries, symlink handling, sudo behavior, and release integrity as security-sensitive. The design is documented in `docs/SECURITY_DESIGN.md` and reviewed in `SECURITY_AUDIT.md`.

### 7.1 Threat model

Mole is a user-invoked CLI performing three classes of destructive operation: **cleanup** (regenerable caches/logs), **uninstall** (app bundle + data), and **Trash routing** (`mo analyze`). It assumes the user runs it intentionally but **makes mistakes** (typos, stale config, wrong menu choice), and that third-party apps may use arbitrary, non-Apple naming under `~/Library`.

It explicitly does **not** defend against a user who deliberately runs `sudo mole` with malicious flags, a host with SIP disabled and `/System` writable, or supply-chain compromise of the binary itself (covered separately by signed releases).

### 7.2 Hard deletion boundaries

`validate_path_for_deletion()` (in `lib/core/file_ops.sh`) is the chokepoint every removal passes through. It rejects:

- Empty or non-absolute paths.
- `..` path-traversal components (while still allowing legitimate names that merely contain `..`).
- Paths with control characters or newlines.
- Critical system paths: `/`, `/bin`, `/sbin`, `/usr/*`, `/System/*`, `/etc`, `/private/etc`, `/Library/Apple`, `/Library/Extensions`, `/Library/Keychains`, `/var/db`, `/var/audit`, `/Applications/Finder.app`, `/Applications/Safari.app`, `/Users` roots, and more.
- Symlinks whose resolved target lands inside a protected system path.

A curated allowlist permits a few known-safe deep paths (e.g. `coresymbolicationd` cache, `/private/tmp`, `/private/var/log`, receipt `.bom`/`.plist`).

### 7.3 Application & data protection

`lib/core/app_protection.sh` plus the data-only `app_protection_data.sh` hold large pattern arrays:

- `should_protect_path()` blocks System Settings, Control Center, Finder, Dock, Keychains, Mail, Contacts, Calendars, iCloud Drive (`Mobile Documents`), NetworkExtension/VPN state, CoreAudio caches, audio plug-in licenses (iZotope, Native Instruments, iLok), and more.
- `should_protect_data()` protects the data directories of password managers (1Password, Bitwarden, LastPass, Dashlane), dev tools (JetBrains, VS Code, Xcode), AI tools (Claude, Codex, Ollama), proxy/VPN apps (Surge, Clash family), and Apple subsystems.
- A distinct **uninstall mode** (`MOLE_UNINSTALL_MODE=1`) relaxes data protection — when the user explicitly chooses to remove an app, its data may go too — while still protecting system-critical components.
- Independent-CLI guard (`_path_belongs_to_independent_cli`) avoids nuking standalone CLI state (`~/.claude`, `~/.codex`, `~/.gemini`, `~/.opencode`) when uninstalling a same-named GUI app.

### 7.4 Deletion routing & forensic log

`mole_delete()` is the unified destructive helper for high-risk paths (e.g. uninstall). It honors `MOLE_DELETE_MODE`:

- **`trash`** moves the path to the macOS Trash via the `trash` CLI, an AppleScript/Finder fallback, or (for sudo-owned paths) a careful move into the invoking user's `~/.Trash`. If Trash is unavailable it **fails closed** rather than silently deleting.
- **`permanent`** delegates to the validated `safe_remove` / `safe_sudo_remove` / `safe_remove_symlink` helpers.

Every call appends a tab-separated line to `~/Library/Logs/mole/deletions.log`: `timestamp, mode, size_kb, status, path`. Size is recorded as `unknown` (never `0`) when it cannot be measured, so the audit trail never understates a large delete. Log-write failures are surfaced once per session.

### 7.5 Privilege & authentication

- `sudo` is requested only when needed; a keepalive prevents mid-operation credential expiry during updates.
- `mo touchid` configures PAM (`pam_tid.so`), preferring `/etc/pam.d/sudo_local` on Sonoma+ so it survives OS updates.
- Test modes (`MOLE_TEST_MODE`, `MOLE_TEST_NO_AUTH`) block all real auth and destructive sudo so the Bats suite never touches the host.

### 7.6 Release integrity

`install.sh` verifies downloaded release assets two ways before trusting them:

1. **Checksum** against the release's published `SHA256SUMS`.
2. **Sigstore build-provenance attestation** via `gh attestation verify --owner tw93 --deny-self-hosted-runners`. Verifying `SHA256SUMS` anchors every binary's checksum to an attested file. With `gh` absent the install falls through to checksum-only (matching prior behavior) unless `MOLE_REQUIRE_ATTESTATION=1` forces failure.

> [!CAUTION]
> The `gosec` linter is intentionally configured to exclude rules like `G204` (subprocess with variable) and `G304`/`G703` (file inclusion / path traversal). This is deliberate: shelling out to `du`/`mdls`/`osascript`/`mdfind` on user-supplied paths is Mole's core function. Safety is enforced structurally by `validatePath` (fuzz-tested) and the protection lists — **not** by avoiding these operations. Contributors must keep the validation chokepoints intact rather than relaxing them.

---

## 8. Operations

### 8.1 Channels & updates

| Channel | Source | Selected by |
|---------|--------|-------------|
| `stable` | Latest tagged release | default |
| `nightly` | Newest `main` commit | `mo update --nightly`, or install `latest`/`main` |
| `dev` | `dev` branch | install `dev` |

The active channel is recorded in `~/.config/mole/install_channel`. For nightly, the file also stores `COMMIT_HASH`, and update checks compare commit hashes rather than version numbers. The background update notice is written to `~/.cache/mole/update_message` and shown in the menu.

`update_mole()` resolves the *invoked* Mole (never just `PATH`) so update/remove act on the copy the user actually ran. Homebrew installs update via `brew upgrade mole`; nightly is script-install-only. The updater downloads the channel-appropriate `install.sh` from GitHub raw and runs it with `--prefix`/`--config` pointing at the existing install.

### 8.2 Logs & auditing

| File | Format | Written by |
|------|--------|-----------|
| `operations.log` | line-based | every `clean`/`uninstall`/`optimize`/`purge`/`installer`/`remove` op |
| `deletions.log` | TSV | `mole_delete()` for Trash/permanent routing |
| `mole.log` / `mole_debug_session.log` | line-based | general + debug session logs |

Review with `mo history [--json] [--limit N]` (N = 1–200). Disable operation logging with `MO_NO_OPLOG=1`.

### 8.3 Rollback / recovery

- Uninstalls default to Trash — recover from there until emptied.
- An interrupted install never corrupts the dispatcher (atomic `mv`).
- `mo remove` cleanly removes binaries plus `~/.config/mole`, `~/.cache/mole`, and `~/Library/Logs/mole`; `--dry-run` previews the exact targets.

---

## 9. Build & Release Pipeline

The `Makefile` drives Go builds; the shell side ships as-is.

| Target | Action |
|--------|--------|
| `make build` | Local-arch build of `bin/analyze-go` and `bin/status-go` (`-ldflags="-s -w"`) |
| `make mod-download` | `go mod download` with retry/backoff for flaky proxies |
| `make release-amd64` / `release-arm64` | Cross-compile per-arch release binaries (`GOOS=darwin GOARCH=…`) run natively for CGO support |
| `make check` | `scripts/check.sh --no-format` (lint) |
| `make format` | `scripts/check.sh --format` |
| `make test` | `MOLE_TEST_NO_AUTH=1 scripts/test.sh` (Bats) |
| `make test-go` | `go test ./...` |
| `make verify` | `check` + `test-go` |
| `make clean` | Remove built binaries |

Release assets are named `analyze-darwin-<arch>` / `status-darwin-<arch>`. The release workflow generates `actions/attest-build-provenance` attestations covering `SHA256SUMS`, the per-arch binaries, and the Homebrew tarballs — the trust root the installer verifies (Section 7.6).

Linting (`.golangci.yml`) enables `govet` (all checks except `shadow`/`fieldalignment`), `staticcheck`, `errcheck`, `ineffassign`, `unused`, `modernize`, `revive`, `unparam`, and `gosec` (with the documented core-function exclusions). Secret scanning uses `gitleaks` (`.gitleaks.toml`) with allowlists for test fixtures and the password/Touch-ID UI strings that are not credentials.

---

## 10. Module Reference (Internals)

### `lib/core/` — the shared foundation

| File | Responsibility |
|------|----------------|
| `common.sh` | Single include point; sources all core modules; `update_via_homebrew()`, `remove_apps_from_dock()` |
| `base.sh` | Colors, icons, global tuning constants, default whitelist, LaunchServices locator |
| `file_ops.sh` | `validate_path_for_deletion()`, `safe_remove*`, `safe_sudo_remove`, `mole_delete()`, Trash routing, size calc, find-and-delete with whitelist enforcement |
| `app_protection.sh` | `should_protect_path/data()`, uninstall-mode logic, vendor/shared/nested path discovery for `find_app_files()` |
| `app_protection_data.sh` | Data-only readonly bundle-ID / pattern arrays |
| `bundle_resolver.sh` | `bundle_has_installed_app()` — Spotlight + filesystem scan to detect orphaned helpers/agents |
| `log.sh` | Operation logging, debug logging, log rotation |
| `timeout.sh` / `timeouts.sh` | `run_with_timeout()` and centralized timeout constants |
| `history.sh` | Parse + render `operations.log`/`deletions.log` (text + JSON) |
| `pkg_receipts.sh` | Read macOS package receipts (`/var/db/receipts`) |
| `sudo.sh` | sudo request + keepalive, Touch ID detection |
| `ui.sh` | Cursor, spinner, key-reading primitives |

### `lib/clean/` — cleanup flows

Per-category modules: `app_caches.sh`, `apps.sh`, `brew.sh`, `caches.sh`, `dev.sh`, `hints.sh`, `launch_services.sh`, `maven.sh`, `project.sh`, `purge_shared.sh`, `system.sh`, `user.sh`. Each contributes a category to `mo clean` (and `project.sh` backs `mo purge`).

### `lib/optimize/` — `diagnostics.sh`, `maintenance.sh`, `tasks.sh`
Drives the rebuild-databases / reset-network / refresh-Finder-Dock / clear-logs / rebuild-LaunchServices-Spotlight task set, with JSON parsed in pure Bash (no `jq` dependency).

### `lib/uninstall/` — `batch.sh`, `brew.sh`
Batch app removal and Homebrew-managed uninstall helpers.

### `lib/manage/` — `whitelist.sh`, `purge_paths.sh`
Whitelist persistence/matching and purge scan-path configuration.

### `lib/ui/` — `app_selector.sh`, `menu_paginated.sh`, `menu_simple.sh`
Reusable interactive selectors.

---

## 11. The Go Binaries

Both Go programs are macOS-gated (`//go:build darwin`) and follow the Bubble Tea Model/Update/View pattern.

### `cmd/analyze` — disk-space explorer

- Entry `main.go` is bootstrap only; `model.go` holds the `model` struct and ~12 message types; `update.go` holds the Bubble Tea update chain; `view.go` renders.
- `scanner.go` measures directory sizes (preferring `du`, with `mdls` logical size for `.app` bundles), guarded by timeouts. `cache.go` caches overview snapshots (schema-versioned, with staleness/expiry checks). `delete.go` performs Trash moves with the same `validatePath` discipline as the shell side, fuzz-tested in `delete_fuzz_test.go`.
- Two modes: a TUI (`tea.WithAltScreen()`) and a one-shot `--json` exporter. The default target is `/` (system overview) unless a path or `MO_ANALYZE_PATH` is given.

### `cmd/status` — live system dashboard

- A `Collector` (`metrics.go`) gathers CPU, GPU, memory, disk, disk I/O, network, battery/power, thermal, Bluetooth, and process metrics via `gopsutil` and macOS tools (`system_profiler`, `diskutil`, `uptime`, `osascript`). Per-domain collectors live in `metrics_*.go`.
- Three collection cadences balance freshness against cost: `collectionFast` (every tick), `collectionProcess` (process watch), and `collectionFull` (every 30 s). A `ProcessWatcher` (`process_watch.go`) raises sustained-high-CPU alerts per the `--proc-cpu-*` flags.
- Output auto-switches to JSON when stdout is not a character device (piped), so `mo status | jq …` works without `--json`. A `cat_hidden` preference persists to `~/.config/mole/status_prefs`.

> [!NOTE]
> AST/CLI extraction reports **zero** "CLI commands" for this repo because neither Go binary uses a command framework (Cobra/urfave) — they use stdlib `flag` with a couple of flags each, and the real command surface is the shell dispatcher's `case` statement. The authoritative command list is `MOLE_COMMANDS` in `lib/core/commands.sh`.

---

## 12. Testing Strategy

Testing is dual-stack and safety-focused:

- **Bats (shell):** 40+ `.bats` files under `tests/` cover clean/uninstall/optimize/purge/installer flows, path validation, whitelist behavior, installer integrity, Bash 3.2 compatibility, and regressions. Run with `make test` (sets `MOLE_TEST_NO_AUTH=1` so no real auth or destructive sudo occurs).
- **Go:** `go test ./...` (`make test-go`) covers the analyzer and status collectors, including `*_test.go` for metrics parsing and disk logic.
- **Fuzzing:** `cmd/analyze/delete_fuzz_test.go` (`FuzzValidatePath`) and `tests/path_validation_fuzz.bats` (fed by `tests/fuzz_corpus/dangerous_paths.txt`) property-test the path-validation boundary — the single most safety-critical surface.
- **Performance:** `tests/core_performance.bats` guards per-PR performance; `scripts/audit_bundle_drift.sh` backs a monthly bundle-protection audit.

> [!TIP]
> When adding a new `clean_*` flow, route deletions through `safe_find_delete` / `safe_sudo_find_delete`. Whitelist and protection enforcement is built into those helpers, so new flows inherit it structurally rather than re-implementing (and possibly missing) it — a recurring class of past bugs (#710, #724, #738, #744, #757).

---

## 13. Code Quality & Conventions

- **Bash 3.2 floor.** The macOS system Bash is 3.2; the code avoids `${var,,}`, namerefs, and `mapfile`, using `tr`, `eval`-by-name, and explicit loops. New contributions must keep this floor.
- **Single chokepoints.** Path validation (`validate_path_for_deletion`), deletion (`mole_delete`/`safe_*`), and protection (`should_protect_*`) are centralized so safety can be reasoned about and fuzzed in one place.
- **Data/logic split.** Protection *data* lives in `app_protection_data.sh`; *logic* in `app_protection.sh`.
- **Idempotent sourcing.** Every module short-circuits on a `*_LOADED` sentinel.
- **No-color & non-TTY awareness.** Spinners and alt-screen behavior degrade gracefully when output is piped or `NO_COLOR` is set.
- **Pure-Bash JSON.** Optimize/health parsing avoids a `jq` dependency.
- **Go layering.** `main.go` bootstrap-only; types/accessors in `model.go`; update chain in `update.go` — a deliberate separation the contributor guide enforces.

---

## 14. Potential Improvements

These are observations from the source, not defects:

- **Sensor coverage gaps.** Several status sensors (temperature, fan, battery detail) depend on `system_profiler`/hardware availability and degrade silently. Community reports (#1058, #1066, #1060) suggest model-specific handling could be expanded.
- **Scan robustness.** Reports of scans stalling on huge or vanished directories (#1053, #1049) point to opportunities for tighter per-item timeouts and progress feedback in the long-tail cases.
- **Sudo prompt timing.** Issue #1059 (prompt appearing to skip input) suggests the interactive sudo flow could pre-flight credentials more consistently; Touch ID side-steps it.
- **LaunchServices hygiene.** Issue #1057 proposes cleaning stale LaunchServices registrations during `mo clean` — a natural extension of the existing `launch_services.sh` flow.
- **Windows divergence.** The experimental `windows` branch is separate; keeping the safety chokepoints portable would ease eventual convergence.

---

## 15. Quick Reference

### Command surface (`MOLE_COMMANDS`)

```
clean uninstall optimize analyze status history purge installer
touchid completion update remove help version
```

### Key paths

```
/usr/local/bin/{mole,mo}              # executables
~/.config/mole/{bin,lib,install_channel,purge_paths,status_prefs,whitelist*}
~/.cache/mole/{version_check,update_message}
~/Library/Logs/mole/{mole.log,operations.log,deletions.log}
/etc/pam.d/sudo[_local]               # Touch ID (pam_tid.so)
```

### Critical functions

| Function | File | Role |
|----------|------|------|
| `main()` / dispatch `case` | `mole` | Route subcommands |
| `validate_path_for_deletion()` | `lib/core/file_ops.sh` | Deletion boundary |
| `mole_delete()` | `lib/core/file_ops.sh` | Trash/permanent routing + audit |
| `should_protect_path()` / `should_protect_data()` | `lib/core/app_protection.sh` | Protection policy |
| `find_app_files()` | `lib/core/app_protection.sh` | Uninstall leftover discovery |
| `verify_release_asset_checksum()` | `install.sh` | Release integrity |
| `Collector.Collect()` | `cmd/status/metrics.go` | System metrics |

### Build/test

```bash
make build       # local Go binaries
make verify      # lint + go test
make test        # Bats suite (MOLE_TEST_NO_AUTH=1)
```

---

_Generated June 2026 from tw93/Mole at 470667c. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
