Skip to content

SparkClean v1.4.0

Choose a tag to compare

@georgekhananaev georgekhananaev released this 20 Jul 09:06
· 5 commits to main since this release

Added

  • feature: Disk Map — accounts for the complete startup APFS container instead of presenting cleanup candidates as whole-disk usage. It separates the Data volume, user/home/Library folders, macOS support volumes, readable file totals, and a truthful protected/APFS-managed residual; the latest bounded audit is saved to ~/Library/Logs/SparkClean/latest-storage-map.json.
  • feature: Time Machine sidebar section — lists APFS local snapshots with guarded multi-select deletion via tmutil (admin-escalated, injection-guarded). The newest snapshot is kept as a restore point and no speculative reclaim amount is shown.
  • feature: Storage Insights sidebar section — a read-only view of stores that grow silently but usually shouldn't be bulk-deleted (messaging apps, iOS backups, Photos, Mail, iCloud Drive, Downloads, Docker, simulators, and virtual machines). Shows current size, change since the prior measurement, partial-scan state, and a Full-Disk-Access badge when a store is unreadable. Measurement is cancellable and bounded by a deadline/watchdog; incomplete results are never written to history.
  • insights: Added Developer Projects, installed applications, Chrome profiles, Claude Desktop data, and JetBrains IDE data so the watched-store total explains substantially more of real disk usage instead of focusing only on cleanup candidates.
  • feature: Full WhatsApp storage visibility and cleanup — the cleanup scan now reports the complete local chat-media store as an opt-in Caution category, alongside separate cache and diagnostic-log categories. Clearing media requires explicit selection and confirmation, refuses to run while WhatsApp is open, preserves WhatsApp's databases, and moves attachments to Trash. Storage Insights measures all current WhatsApp app/group containers and links directly to the cleanup category.
  • diagnostics: The latest cleanup scan is now persisted atomically to ~/Library/Logs/SparkClean/latest-scan.json, including disk totals, enabled scan settings, categories, paths, sizes, selection state, errors, and partial-scan status. The file is private to the user and replaced on every scan instead of growing without bound.
  • feature: Optional menu bar icon (Settings → General) with Open / Scan Now / Open Trash quick actions.
  • scan: App/media/cloud cache coverage — WeChat, OneDrive, Dropbox, Steam shader cache, Apple Music artwork (safe), and Podcasts cache (review). Plus a "Shared Container Caches" sweep covering every sandboxed app's Group-Container cache.
  • scan: Developer-tool cache coverage — new scans for browser-automation binaries (Playwright/Cypress/Puppeteer), uv/pre-commit, compiler caches (ccache/sccache/zig), JS build caches (Turborepo/Nx), IaC & cloud CLI caches (Terraform/Helm/kube/AWS/gcloud), and (review-level, off by default) ML framework caches and Colima/Lima/minikube VM data.
  • scan: Per-category Scan button — each category group can now be rescanned individually without a full scan, preserving every other group's results and selections (#8).
  • scan: Rust target/ build-directory scan — finds target/ dirs next to a Cargo.toml (Maven and other coincidental target/ dirs are excluded), regenerable via cargo build. New "Scan Rust target directories" setting (#3 remainder).
  • scan: Next.js .next build artifacts are now discovered only beside a package.json and offered as rebuildable cleanup. node_modules discovery also follows the narrow .claude/worktrees and .codex/worktrees locations so agent-created dependency copies are no longer hidden, while unrelated hidden state remains excluded.
  • scan: Generic Electron cache discovery for direct Cache, Code Cache, GPU, and Dawn cache directories not already claimed by a dedicated app definition.
  • scan: Optional old-installer discovery for aged DMG, PKG, MPKG, XIP, installer ZIP, and package files, with installed-app hints and per-item review.
  • localization: String Catalog groundwork, stable localized group/safety display names, localizable scan-definition text, and a contributor translation workflow.
  • safety/undo: Restore Last Cleanup (⇧⌘Z, File menu) — cleanup, uninstall, duplicate removal, and Trash Monitor operations now record the Trash location of each item they remove, so the latest operation can be reversed. Restores skip items whose original location is re-occupied and tolerate items that have left the Trash.
  • safety: FileRemover — one deletion service now shared by the main clean pipeline, Uninstaller, Duplicate Finder, and Trash Monitor. Every item passes the same DeletionPolicy gate, delete-time existence/type/identity/cloud/volume checks, incremental undo recording, and bounded audit logging.
  • safety: Uninstaller gains a dedicated policy profile that permits removing whole .app bundles (never their interior) while keeping every other protection.
  • safety: DeletionPolicy — a single, testable set of rules deciding whether any path may be deleted, extracted from the former inline isSafePath. Now also rejects iCloud Drive / cloud-storage mounts (~/Library/CloudStorage, ~/Library/Mobile Documents), the contents of signed .app/.framework bundles, library document bundles (.photoslibrary, .musiclibrary, .fcpbundle, .logicx, .sparsebundle, keychains), and launch-service directories (LaunchDaemons/LaunchAgents/Extensions). Home-injectable for testing.
  • safety: allowedRoots deletion territory — each scan category now confines deletion to the subtree(s) it legitimately owns. Every concrete deletion, including children discovered at delete time, must resolve to a path within that territory, so a scan bug or a symlink escaping into a sibling tree can no longer delete outside the category's declared paths.
  • safety: Delete-time guard skips any item that has become an iCloud/file-provider materialization since it was scanned (prevents corrupting cloud sync state).
  • ci: Tests GitHub Actions workflow running the unit suite on every push/PR to main.
  • tests: DeletionPolicyTests — 23 characterization/expansion/territory tests pinning the exact deletable/undeletable verdicts, including component-boundary matching (/a/bc is not within /a/b), a regression guard that Ruby's ~/.bundle/cache stays deletable, and real-symlink escape tests (into a protected dir, and out of a category's territory).
  • tests: BrokenSymlinkScanTests covering broken/valid detection, excluded-dir skipping, cancellation, depth cap, and the iteration watchdog against isolated fixture trees.
  • tests: Regression coverage for empty deletion territories, delete-time type changes, consumed/retryable undo manifests, strict Time Machine dates/newest selection, bounded Storage Insights measurement, and fixture-based Electron cache discovery.
  • tests: Replaced the placeholder UI launch test with deterministic dashboard smoke assertions for the main window, title, and Scan action.

Changed

  • release: Release builds now enable Hardened Runtime, omit injected debug base entitlements, embed the Apple Events/admin and protected-folder purpose strings in the executable Info.plist, and produce a universal Apple Silicon + Intel binary from the generic macOS destination.
  • safety: CleanupManager.isSafePath delegates to the shared DeletionPolicy, and every filesystem deletion surface now uses that policy through FileRemover.

Fixed

  • permissions: Full Disk Access detection now verifies that a protected directory can actually be enumerated instead of trusting POSIX readability bits, which can report a false positive while macOS privacy controls are still blocking the scan.
  • storage accounting: Cleanup scans and Storage Insights now collapse complete APFS clones by their filesystem content-stream identifier. Clone-heavy stores such as WhatsApp can no longer add the same shared blocks thousands of times and present a logical file total as guaranteed reclaimable disk space.
  • cleanup: Broken-symlink scan no longer stalls on "Scanning broken symlinks…" (#9). The walk now checks for cancellation on every iteration (Cancel responds immediately), skips cloud-storage file-provider trees (CloudStorage, Mobile Documents, Photos, and other heavy provider dirs) that could hang enumeration, caps ~/Library traversal depth, and bails out via an iteration watchdog. Partial results found before an interruption are still surfaced.
  • cleanup: Zero-byte cleanup items such as broken symbolic links can now reach confirmation and cleanup instead of being disabled by a size-only check.
  • cleanup: Confirmation and execution now use the same exhaustive path set. Scanner display caps can no longer authorize hidden paths, unreadable sibling roots are excluded, nested definitions omitted from a parent category's measurement are also preserved during deletion (including one-group rescans), group-level Clean stays scoped to that group, and partially successful safe categories are remeasured instead of retaining stale removed entries.
  • cleanup: Old Downloads no longer duplicates screenshots/installers already owned by another category. A directory is offered only after a complete bounded walk proves every file—including hidden files—is old; unreadable, linked, packaged, cloud-backed, active, or unverifiably large trees are left alone.
  • cleanup: Narrowed legacy “safe” definitions: system logs and saved app/playground state are now opt-in review; active macOS update staging, raw Simulator devices/runtimes, Deno’s installation, Ruby user gems, broad SwiftPM/Xcode product state, Docker CLI plugins/builder configuration, and Steam partial downloads are no longer cleanup targets. Xcode device support, Conda/Maven/NuGet/Dart stores, Spotify offline cache, and legacy Dropbox cache now require review.
  • privacy: Recent Items cleanup now targets only recent application/document/host/server lists and preserves unrelated shared-file-list data such as Finder sidebar favorites.
  • cleanup: Removed the duplicate filesystem Ollama-model category. Models are shown once and removed individually through ollama rm, with working per-model checkboxes.
  • cleanup: Replaced blanket deletion of live temporary-directory contents with an off-by-default review scan limited to non-empty regular files older than seven days directly inside sanctioned temporary roots; directories, links, sockets, cloud items, and recent files are excluded.
  • cleanup: Results now report bytes and categories actually removed. Blocked, failed, skipped, or partially removed categories remain visible with exact errors instead of disappearing as successfully cleaned.
  • cleanup: Removed the unconfirmed one-click Ollama deletion path; model removal now goes through selection, the permanent-action confirmation, per-model result handling, and command audit.
  • docker: Image cleanup now shows and prunes dangling images only, build-cache size uses Docker's reclaimable value, and completed cleanup reports Docker's actual reclaimed bytes instead of the pre-scan estimate.
  • docker: Removed misleading per-image checkboxes from prune-backed categories; Docker selection now matches the category-level CLI operation.
  • cleanup: File-valued definitions are no longer silently skipped by deleteChildrenOnly, and a Trash root can no longer be treated as a child to trash.
  • cleanup: Caution categories always use Trash, even when permanent-delete mode is enabled; confirmation copy now accurately separates recoverable and permanent actions.
  • cleanup: The Trash category now empties selected Trash contents instead of attempting to move those items back into Trash, and is clearly labeled as irreversible.
  • cleanup: Browser/privacy databases are blocked while their owning application is running, preventing live-database corruption.
  • cleanup: Confirmation now identifies selected data owned by running apps and offers Quit Apps & Clean, Skip Running Apps, or Cancel; apps that fail to quit within five seconds remain hard-skipped.
  • cleanup: Administrator moves use a private randomized helper, show the exact path list, revalidate after confirmation, verify every destination, and add successful moves to undo/audit records.
  • cleanup: Empty deletion territories are denied instead of acting as a wildcard; whole application bundles require an explicit uninstaller profile.
  • cleanup: Resolved /private aliases can no longer bypass system-path protection; symlinked scan roots cannot turn their destinations into authorized territories; direct /Users/name/item deletion is denied except for explicitly reviewed shell-history files; and credential/account store descendants are blocked.
  • cleanup: /Volumes paths and items whose device ID is outside the startup/home filesystems are rejected at delete time, including mounts nested under otherwise allowed user folders.
  • cleanup: Restore Last Cleanup now recognizes dangling symlinks in Trash, so broken-link cleanup remains reversible.
  • cleanup: Restore manifests now validate trusted Trash/original territories, local-device ancestry, and the recorded device/inode before moving anything back; edited or replaced entries cannot redirect Restore.
  • cleanup: Dropbox file-provider storage and document/package interiors (including GarageBand projects) are excluded from cleanup.
  • uninstaller: Protected LaunchAgent files are no longer offered to the generic file remover, and known model/VM/database/game/SDK data defaults to unselected review.
  • uninstaller: App crash-report matches are removed as individual files; uninstalling one app can no longer remove the entire DiagnosticReports directory.
  • uninstaller: Confirmation totals now include only selected related locations, and a removed app with failed leftovers is reported as a truthful partial completion instead of a total failure.
  • uninstaller: The final confirmation now resolves the latest checkbox state instead of an older copied app value, and crash-report matching requires an app-name or bundle-ID boundary to prevent substring collisions.
  • uninstaller: Heuristic direct-home dot-directory matches were removed, app-name-only cache/log guesses are clearly labeled and off by default, Group Container matches require identifier boundaries, and related items that the shared deletion policy would reject are no longer shown as removable.
  • uninstaller: The app bundle is now removed and verified before any selected support data; an app permission/authorization failure leaves its preferences and databases untouched.
  • trash-monitor: Leftover cleanup now has per-item checkboxes; generated cache/log locations start selected while preferences, containers, and Application Support require explicit opt-in. A partial operation retains failed items and offers Retry Remaining instead of presenting them as cleaned.
  • duplicates: Cross-folder duplicates are detected correctly; size candidates are retained until all roots have been scanned.
  • duplicates: Package contents (including Photos libraries) are not traversed, hard links do not inflate duplicate counts, inode lookup failures remain eligible, and hashing/image analysis respond to cancellation.
  • duplicates: iCloud/file-provider files are excluded during discovery as well as at delete time, avoiding uncleanable results and unintended cloud materialization.
  • duplicates: Candidate grouping now uses logical byte length, hard links use device-plus-inode identity, and the keeper is rehashed/revalidated before every removal.
  • duplicates: File enumeration has a one-million-entry watchdog and labels cancelled/watchdog results as partial instead of claiming the Mac has no duplicates.
  • duplicates: Exact hashes and visual similarity are revalidated immediately before removal; partial Trash failures remove successful/missing paths from the displayed group while keeping only truthful retryable results. Similar-image groups preserve the largest file, detect singleton-hash pairs, are never batch-selected, and show an explicit non-identical warning.
  • scan: Cancelling a per-category refresh restores that group's previous results and selections rather than replacing them with an incomplete scan.
  • scan: The dashboard now explains that reclaimable scan results are not the same as total disk usage and links to Storage Insights for large app/media stores.
  • time-machine: Snapshot timestamps now reject impossible calendar dates, the newest snapshot is protected independently of list order, and privileged helper scripts use randomized private paths.
  • time-machine: Multi-snapshot deletion attempts every requested timestamp, then refreshes to verify and audit partial success instead of treating an all-or-nothing script exit as the result.
  • maintenance: Removed the duplicate “delete all local snapshots” action; snapshot deletion now lives only in the guarded Time Machine view. APFS defragmentation no longer assumes a fallback disk identifier or promises immediate reclaimed space.
  • maintenance: Removed the fabricated Spotlight-size estimate, fixed “not enabled” APFS status parsing, and made empty successful DNS output produce a useful result message.
  • maintenance: Tasks now report command failures instead of unconditional success, and command execution drains stderr with stdout to prevent another pipe-buffer deadlock.
  • startup-items: Toggle completion now resolves rows by ID after a concurrent rescan, reports launchctl failures, disables in-flight switches, and labels system entries as installed/read-only instead of claiming they are active.
  • updates: Release metadata now requires a valid stable version and non-empty GitHub-hosted DMG. Downloads reject non-success responses and non-GitHub redirects, validate the UDIF trailer, and stage the complete image beside the destination before atomically replacing an existing file.
  • tests: Repaired the unit-test target, which did not compile on main — a stale PathStat.id.uuidString reference (id is now a String), plus drifted assertions in CategoryGroupTests.allCasesExist (6→9 groups) and exportReport (group headers are uppercased).

Download

Download SparkClean-1.4.0.dmg, open it, and drag SparkClean to your Applications folder.

The app is a universal binary (Apple Silicon + Intel) and is ad-hoc signed, not
Developer ID notarized. On first launch macOS Gatekeeper will warn about an
unidentified developer — right-click the app and choose Open, or allow it under
System Settings → Privacy & Security.

Requirements

  • macOS 14.0 (Sonoma) or later
  • Full Disk Access for complete scan coverage