Skip to content

Commit 26571f8

Browse files
fschrhuntclaude
andcommitted
Add 'vo optimize' — full Mole task parity on macOS, plus a real Linux equivalent set
macOS: ports all 21 of Mole's optimize tasks (DNS/Spotlight, Finder/icon caches, saved app states, corrupted preferences, Mail/Safari/Messages database vacuum, LaunchServices repair, Dock refresh, .DS_Store prevention, memory pressure, network stack, disk permissions, periodic maintenance, shared file lists, login items audit, quarantine cleanup, broken Launch Agents, Notification Center, usage tracking data), plus three genuinely new checks Mole doesn't have: battery health, Homebrew health, and a SMART-based disk health check that replaces Mole's diskutil verifyVolume (disabled by default there because it can hang the system on APFS inconsistencies). Linux: a from-scratch equivalent set mapped to real Linux concepts, not a stub — desktop/MIME database refresh is the actual Linux analog of LaunchServices' 'Open with' repair, locate db refresh is the analog of Spotlight indexing, autostart/launcher audits are the analog of broken login items + Launch Agents. Also adds SSD TRIM (fstrim), which has no macOS/Mole equivalent since APFS handles it transparently — a genuine Linux-only win. Shared engine: --dry-run/--only=/--skip=, OPTIMIZE_SKIP config default, --list, sudo requested once up front (tasks degrade cleanly without it), phoenix animation and history logging on completion. Bugs found and fixed via real hardware testing (Mac Mini, MacBook, Linux server), not just review: - grep -c '.' piped into `|| echo 0` double-prints on zero matches, producing a two-line value that crashes $((...)) arithmetic — this is the common case (healthy system, 0 broken things), so it would have broken on nearly every run. Fixed across 6 call sites; replaced with `|| true` (grep -c always emits a literal "0" on its own). - Linux .desktop Exec= parser choked on quoted binary paths (Exec="/path with spaces/app" %u, spec-legal syntax), flagging a perfectly valid launcher as broken. Added quote-aware parsing. - get_lsregister_path was dropped from base.sh during the original Linux port and never restored, so LaunchServices repair silently errored out on macOS ('command not found'). - macOS login items audit leaked a raw AppleScript permission error to the terminal because `$(cmd) 2>/dev/null` does not redirect the substituted command's stderr (only `$(cmd 2>/dev/null)` does) — a real command-substitution stderr-scoping gotcha. - That same heredoc, once redirected internally, failed to *parse* under real bash 3.2 (macOS's shipped bash) purely because its body contained an apostrophe (AppleScript's) — a documented old-bash heredoc-inside- command-substitution bug. Rewrote the AppleScript to avoid the possessive apostrophe entirely; verified against the bash:3.2 image. - battery_health returned early without reporting anything on battery-less Macs (Mac Mini), silently triggering the section's generic 'Nothing to tidy' fallback instead of a clear 'no battery' message. - disk_health blamed 'admin access' for smartctl failures that were actually an unsupported USB-bridge device type; now distinguishes 'sudo not available' from 'sudo worked, but couldn't read health'. Verified: full syntax + shellcheck pass, bash 3.2 parse + behavior via Docker, two consecutive real (non-dry) runs on this Linux server confirming idempotency, and --dry-run confirmed correct with real findings on both Macs (3 genuinely stale login items on the MacBook, a leftover Mole.app GUI install spotted along the way). Live (non-dry) run against the Macs intentionally deferred pending explicit go-ahead, since it performs sudo-elevated system changes over a remote session. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 1454e1d commit 26571f8

6 files changed

Lines changed: 1210 additions & 13 deletions

File tree

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ shorthand (Mole has `mo`; Vole has `vo`). Requires bash and git — nothing else
3737
| `vo` | Interactive menu |
3838
| `vo clean` | Free disk space: user/app/dev caches, old trash, old logs, brew cleanup. Linux adds flatpak leftovers and an opt-in sudo deep clean (APT cache, journal logs, old snap revisions, crash dumps); macOS adds Xcode DerivedData and simulator caches. `--dry-run` previews. |
3939
| `vo update` | Scan **every** package manager in parallel and update what you pick. `vo update all` skips the menu; `vo update dry run` lists every pending package, then offers to apply; `vo update apt brew` targets specific managers. |
40+
| `vo optimize` | Tune and repair the system: caches, corrupted config files, database vacuuming, network stack, broken launchers/agents, disk and battery health, and more — see below. `--dry-run` previews; `--list` shows every task; `--only=`/`--skip=` target specific tasks. |
4041
| `vo uninstall` | One selector for apt / snap / flatpak / brew / casks. Removes packages, orphaned dependencies, and residual configs. |
4142
| `vo purge` | Find files over 100MB (`--min=<MB>`) and pick what to remove — to the trash when possible. |
4243
| `vo analyze` | Explore disk usage. Delegates to `gdu`/`ncdu` when installed. |
@@ -46,6 +47,33 @@ shorthand (Mole has `mo`; Vole has `vo`). Requires bash and git — nothing else
4647
| `vo config` | Settings: animations, purge floor, system-clean behavior, retention windows (`vo config edit`). |
4748
| `vo fda` | macOS: check Full Disk Access, register Vole's toggle with the system, and open the exact Settings pane (points at the Remote Login switch for SSH sessions). On Linux: nothing needed. |
4849

50+
## Optimize
51+
52+
`vo optimize` matches Mole's own optimize task list on macOS (all 21 of its
53+
checks: DNS/Spotlight, Finder & icon caches, saved app states, corrupted
54+
preferences, Mail/Safari/Messages database vacuuming, LaunchServices repair,
55+
Dock refresh, .DS_Store prevention, memory pressure, network stack, disk
56+
permissions, periodic maintenance, shared file lists, login items, Gatekeeper
57+
quarantine history, broken Launch Agents, Notification Center, and usage
58+
tracking data) — plus battery health, Homebrew health, and a SMART-based disk
59+
health check that replaces Mole's `diskutil verifyVolume` (which can hang the
60+
system on APFS inconsistencies) with something that never touches the
61+
filesystem.
62+
63+
On Linux it's a from-scratch equivalent set, not a stub: DNS cache, font/icon
64+
caches, the desktop/MIME database (the real Linux analog of "Open with"
65+
repair), autostart and application-launcher audits, browser database
66+
vacuuming, systemd failed-unit cleanup, memory pressure relief, network stack
67+
refresh, home directory ownership repair, `locate` database refresh (the
68+
Linux analog of Spotlight indexing), disk and package-database health, and
69+
journal disk usage. **SSD TRIM** (`fstrim`) has no macOS/Mole equivalent at
70+
all — APFS handles it transparently — and is a genuine Linux-only win: a real
71+
optimization most desktops leave to a cron job nobody ever sees run.
72+
73+
Every task is safe to re-run — a second pass reports "already healthy"
74+
instead of repeating work — and every sudo-gated task degrades cleanly
75+
if admin access isn't available.
76+
4977
## Update sources
5078

5179
| | Linux | macOS |

bin/optimize.sh

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#!/bin/bash
2+
# Vole - Optimize
3+
# System tuning and repair: caches, databases, broken configs, network
4+
# stack, disk health. Every task is safe to re-run.
5+
6+
set -euo pipefail
7+
8+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9+
source "$SCRIPT_DIR/../lib/core/common.sh"
10+
source "$SCRIPT_DIR/../lib/optimize/tasks.sh"
11+
12+
trap cleanup_temp_files EXIT INT TERM
13+
14+
ONLY_KEYS=""
15+
SKIP_KEYS=""
16+
for arg in "$@"; do
17+
case "$arg" in
18+
--dry-run | -n) DRY_RUN=true ;;
19+
--only=*) ONLY_KEYS="${arg#--only=}" ;;
20+
--skip=*) SKIP_KEYS="${arg#--skip=}" ;;
21+
--list | -l) LIST_ONLY=true ;;
22+
--help | -h)
23+
echo "Usage: vole optimize [--dry-run] [--only=<tasks>] [--skip=<tasks>] [--list]"
24+
echo " --dry-run Show what would change without changing anything"
25+
echo " --only=a,b Run only these tasks (see --list for keys)"
26+
echo " --skip=a,b Run everything except these tasks"
27+
echo " --list List available tasks and exit"
28+
exit 0
29+
;;
30+
esac
31+
done
32+
33+
ensure_vole_dirs
34+
load_whitelist
35+
36+
# ============================================================================
37+
# Task registry — key|label, in run order. macOS and Linux each get their own
38+
# list since almost every task is implemented differently per platform.
39+
# ============================================================================
40+
if is_macos; then
41+
TASKS=(
42+
"dns_and_network|DNS & Network Cache"
43+
"finder_cache|Finder Cache"
44+
"app_state_cleanup|App Saved States"
45+
"broken_prefs|Preference File Repair"
46+
"database_vacuum|Mail, Safari & Messages Databases"
47+
"launch_services|LaunchServices (\"Open with\")"
48+
"dock_refresh|Dock Refresh"
49+
"dsstore_prevention|.DS_Store Prevention"
50+
"memory_pressure|Memory Pressure"
51+
"network_stack|Network Stack"
52+
"disk_permissions|Disk Permissions"
53+
"periodic_maintenance|Periodic Maintenance"
54+
"shared_file_lists|Shared File Lists"
55+
"login_items|Login Items"
56+
"quarantine_cleanup|Quarantine Database"
57+
"launch_agents_cleanup|Launch Agents"
58+
"notification_cleanup|Notification Center"
59+
"usage_data_cleanup|Usage Tracking Data"
60+
"disk_health|Disk Health"
61+
"battery_health|Battery Health"
62+
"homebrew_health|Homebrew Health"
63+
)
64+
else
65+
TASKS=(
66+
"dns_cache|DNS Cache"
67+
"font_icon_cache|Font & Icon Cache"
68+
"desktop_database|Desktop & MIME Database"
69+
"autostart_audit|Autostart Entries"
70+
"broken_desktop_entries|Application Launchers"
71+
"database_vacuum|Browser Databases"
72+
"systemd_failed_units|Systemd Unit Health"
73+
"memory_pressure|Memory Pressure"
74+
"network_stack|Network Stack"
75+
"home_ownership|Home Directory Ownership"
76+
"locate_db|locate Database"
77+
"ssd_trim|SSD TRIM"
78+
"disk_health|Disk Health"
79+
"package_health|Package Database Health"
80+
"journal_health|Journal Disk Usage"
81+
)
82+
fi
83+
84+
if [[ "${LIST_ONLY:-false}" == "true" ]]; then
85+
echo "Available tasks:"
86+
for t in "${TASKS[@]}"; do
87+
printf ' %-24s %s\n' "${t%%|*}" "${t#*|}"
88+
done
89+
exit 0
90+
fi
91+
92+
# Merge CLI --skip with the configured default skip list.
93+
effective_skip=",$SKIP_KEYS,${VOLE_CFG_OPTIMIZE_SKIP:-},"
94+
95+
task_selected() {
96+
local key="$1"
97+
if [[ -n "$ONLY_KEYS" ]]; then
98+
case ",$ONLY_KEYS," in *",$key,"*) return 0 ;; *) return 1 ;; esac
99+
fi
100+
case "$effective_skip" in *",$key,"*) return 1 ;; esac
101+
return 0
102+
}
103+
104+
echo ""
105+
if [[ "$DRY_RUN" == "true" ]]; then
106+
echo -e "${PURPLE_BOLD}${ICON_ARROW} Vole Optimize${NC} ${GRAY}— dry run, nothing will change${NC}"
107+
else
108+
echo -e "${PURPLE_BOLD}${ICON_ARROW} Vole Optimize${NC}"
109+
fi
110+
fda_hint
111+
112+
# One sudo prompt up front; tasks that need it just skip cleanly if declined.
113+
if [[ "$DRY_RUN" != "true" ]]; then
114+
ensure_sudo "Some optimizations need administrator access" || true
115+
fi
116+
117+
OPT_PREFIX="linux"
118+
is_macos && OPT_PREFIX="mac"
119+
120+
RUN_COUNT=0
121+
for t in "${TASKS[@]}"; do
122+
key="${t%%|*}"
123+
label="${t#*|}"
124+
task_selected "$key" || continue
125+
RUN_COUNT=$((RUN_COUNT + 1))
126+
start_section "$label"
127+
"opt_${OPT_PREFIX}_${key}" || true
128+
end_section
129+
done
130+
131+
echo ""
132+
if ((RUN_COUNT == 0)); then
133+
echo -e "${YELLOW}${ICON_WARNING}${NC} No tasks selected"
134+
elif [[ "$DRY_RUN" == "true" ]]; then
135+
echo -e "${GREEN}${ICON_SUCCESS}${NC} Dry run complete — $RUN_COUNT task(s) checked"
136+
else
137+
vole_phoenix
138+
echo -e "${GREEN}${ICON_SUCCESS}${NC} Optimized — $RUN_COUNT task(s) checked"
139+
log_history "optimize" "$RUN_COUNT tasks" 0
140+
fi
141+
echo ""

lib/core/base.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,27 @@ esac
6868
is_macos() { [[ "$VOLE_OS" == "macos" ]]; }
6969
is_linux() { [[ "$VOLE_OS" == "linux" ]]; }
7070

71+
# ============================================================================
72+
# LaunchServices Utility (macOS)
73+
# ============================================================================
74+
75+
# Locate the lsregister binary (path varies across macOS versions).
76+
get_lsregister_path() {
77+
local -a candidates=(
78+
"/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister"
79+
"/System/Library/CoreServices/Frameworks/LaunchServices.framework/Support/lsregister"
80+
)
81+
local candidate=""
82+
for candidate in "${candidates[@]}"; do
83+
if [[ -x "$candidate" ]]; then
84+
echo "$candidate"
85+
return 0
86+
fi
87+
done
88+
echo ""
89+
return 0
90+
}
91+
7192
# ============================================================================
7293
# Global Configuration Constants
7394
# ============================================================================

lib/core/common.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ VOLE_CFG_UPDATE_PRESELECT="pending" # managers preselected in update menu: pendi
5555
VOLE_CFG_TRASH_AGE_DAYS="" # override trash retention (default 7)
5656
VOLE_CFG_LOG_AGE_DAYS="" # override log retention (default 7)
5757
VOLE_CFG_FDA_HINT="true" # macOS: nudge about limited disk access
58+
VOLE_CFG_OPTIMIZE_SKIP="" # comma-separated 'vole optimize' task keys to always skip
5859

5960
load_config() {
6061
[[ -f "$VOLE_CONFIG_FILE" ]] || return 0
@@ -72,6 +73,7 @@ load_config() {
7273
TRASH_AGE_DAYS) [[ "$value" =~ ^[0-9]+$ ]] && VOLE_CFG_TRASH_AGE_DAYS="$value" ;;
7374
LOG_AGE_DAYS) [[ "$value" =~ ^[0-9]+$ ]] && VOLE_CFG_LOG_AGE_DAYS="$value" ;;
7475
FDA_HINT) VOLE_CFG_FDA_HINT="$value" ;;
76+
OPTIMIZE_SKIP) VOLE_CFG_OPTIMIZE_SKIP="$value" ;;
7577
esac
7678
done < "$VOLE_CONFIG_FILE"
7779
}

0 commit comments

Comments
 (0)