Pearcleaner

repository·main·Indexed 10 days ago

https://github.com/alienator88/pearcleaner

A free, source-available macOS application for cleaning up apps and orphaned files. It features a Sentinel Monitor for automated cleanup, architecture stripping (App Lipo), and a CLI tool (`pear`) for managing uninstalls and orphaned file removal. Requires macOS 13.0 (Ventura) or later.

Tokens
2.7K
Snippets
13
Records
18
Agent score
96%

What's inside Pearcleaner

  1. Overview of Pearcleaner features

    main

    Pearcleaner is a free, source-available Mac app cleaner. Its capabilities are divided into three main areas:

    Core Features

    • App Uninstallation & File Search: Drag/drop apps, CLI support, and deep link automation.
    • Management Tools: Development Environment Manager, Homebrew Manager, PKG Manager, Plugin Manager, and Services Manager.
    • App Utilities: App Lipo (stripping architectures), Apps Updater, and Orphaned File Search.
    • Finder Extension: Enables right-click uninstallation directly from Finder.

    Utilities

    • Sentinel Monitor: Automatically cleans up files when apps are moved to the Trash (~2MB RAM usage).
    • Translation Pruning: Removes unused app translations, keeping only preferred languages.
    • Exporting: Export app bundles and file lists.
    • Steam Support: Basic support for Steam games.

    Customization

    • Theme System: Supports custom colors.
    • Search Control: Ability to include/exclude specific directories and adjust search sensitivity.
  2. Use the Pearcleaner CLI

    main

    The pear command provides a command-line interface for managing application uninstallation and cleaning orphaned files. It supports listing files, uninstalling applications (either just the bundle or the bundle plus related files), and removing orphaned files.

    Note on Privileged Operations: Some commands (like uninstall-all or remove-orphaned) may detect protected files that require root privileges. In such cases, you should prepend sudo to the command.

    # Example: Uninstall an application and all its related files
    sudo pear uninstall-all /Applications/ExampleApp.app
  3. How the Pearcleaner Sentinel monitors the Trash

    main

    The Sentinel process runs a FileWatcher that monitors the user's ~/.Trash directory.

    When a file event occurs in the Trash, the Sentinel performs the following logic:

    1. Checks if the file is an .app bundle.
    2. Verifies the bundle identifier is not com.alienator88.Pearcleaner (to avoid self-processing).
    3. Confirms the file is actually located within the Trash using `FileManager.isInTrash(_:)".
    4. If all conditions are met, it triggers a custom URL scheme to open the application in Pearcleaner via: pear://openApp?path={file_path}.

    This mechanism ensures that when a user moves an application to the Trash, Pearcleaner can automatically intercept it to offer cleaning services.

  4. System requirements for Pearcleaner

    main

    Pearcleaner requires macOS 13.0 (Ventura) or later. It does not support versions prior to macOS 13.0 due to dependencies on specific Swift/SwiftUI APIs.

    Permissions Required:

    • Full Disk Access: Necessary to search for files across the system.
    • Privileged Helper: Required to perform actions within system folders.
  5. Use the HelperToolProtocol for XPC communication

    main

    The HelperToolProtocol defines the interface for communicating with the Pearcleaner helper tool via XPC. This protocol allows a client process to execute privileged commands, perform binary thinning, or perform bundle thinning.

    Methods available via the protocol:

    • runCommand(command:withReply:): Executes a shell command using /bin/bash -c. Returns a Bool indicating success and a String containing the command output or error message.
    • runThinning(atPath:withReply:): Performs binary thinning on a specific file path. Returns a Bool and a String status.
    • runBundleThinning(bundlePath:withReply:): Performs thinning on an entire app bundle. Returns a Bool status, a String message, and a dictionary [String: UInt64] mapping file paths to their sizes.
    // Example of how a client might interact with the protocol
    // (Note: This requires an established NSXPCConnection to the service)
    
    // 1. Run a shell command
    connection.remoteObjectProxy.runCommand(command: "ls -la") { success, output in
        print("Success: \(success), Output: \(output)")
    }
    
    // 2. Run thinning on a single binary
    connection.remoteObjectProxy.runThinning(atPath: "/path/to/binary") { success, message in
        print("Thinning status: \(success), Message: \(message)")
    }
    
    // 3. Run bundle thinning
    connection.remoteObjectProxy.runBundleThinning(bundlePath: "/Applications/MyApp.app") { success, message, sizes in
        print("Success: \(success), Message: \(message), Sizes: \(sizes)")
    }
  6. Control the Pearcleaner Sentinel via Distributed Notifications

    main

    The Pearcleaner Sentinel process can be controlled remotely using DistributedNotificationCenter. This allows other applications or system processes to trigger the file watcher lifecycle.

    Use the following notification names:

    • Pearcleaner.StartFileWatcher: Triggers the global file watcher to start monitoring the user's Trash directory.
    • Pearcleaner.StopFileWatcher: Stops the global file watcher.

    These notifications are broadcasted via the DistributedNotificationCenter.default() center.

  7. HelperToolProtocol method signatures

    main

    The following methods are exported by the HelperToolProtocol for XPC communication:

    MethodParametersReply CallbackDescription
    runCommandcommand: String(Bool, String) -> VoidExecutes a command via /bin/bash -c.
    runThinningatPath: String(Bool, String) -> VoidThins a binary at the specified path.
    runBundleThinningbundlePath: String(Bool, String, [String: UInt64]) -> VoidThins an entire app bundle and returns file sizes.
    @objc(HelperToolProtocol)
    public protocol HelperToolProtocol {
        func runCommand(command: String, withReply reply: @escaping (Bool, String) -> Void)
        func runThinning(atPath: String, withReply reply: @escaping (Bool, String) -> Void)
        func runBundleThinning(bundlePath: String, withReply reply: @escaping (Bool, String, [String: UInt64]) -> Void)
    }
  8. Request password for sudo operations with `ask-password`

    main

    The ask-password command is used to obtain a password for privileged operations. It attempts to retrieve a cached password from the keychain first. If not found, it either requests the password from the running Pearcleaner main app via distributed notifications or displays a native macOS password dialog.

    pear ask-password