Jesse A. Green / notebook

Mole Admin & Architecture Guide

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.

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
  2. Architecture Overview
  3. Component Inventory
  4. System Requirements
  5. Installation & Filesystem Layout
  6. Configuration Reference
  7. Security Architecture
  8. Operations
  9. Build & Release Pipeline
  10. Module Reference (Internals)
  11. The Go Binaries
  12. Testing Strategy
  13. Code Quality & Conventions
  14. Potential Improvements
  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 / Lip Gloss stack with system metrics from 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

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