# AppJanitor (CLI prototype) AppJanitor is a leftover/stale-file cleanup finder: point it at a directory tree and it walks the tree looking for junk-pattern files (*.tmp, *.bak, *.log, editor backups, .DS_Store, Thumbs.db, or any custom glob list you supply), files whose modification time is older than a threshold you set, and directories that contain no files anywhere in their subtree. It reports candidates with the specific reason each one matched and, on request, clears them out safely by moving matched files into a quarantine directory (never deleting them outright) and removing matched empty directories. The full "App Janitor" product concept also includes registry-aware deep-uninstall leftover cleanup, startup-item control, System Restore point management, and driver rollback -- none of that is implemented here. Those features require privileged, OS-specific APIs (the Windows registry, WMI, Task Scheduler, System Restore, Device Manager) with no portable stdlib equivalent, so they are out of scope for this dependency-free, cross-platform Go CLI and are left on the roadmap. See ../plan.md for the full product plan. ## Build from source Requires Go 1.24+, no external dependencies. go build -o appjanitor . Cross-compile for another platform: GOOS=windows GOARCH=amd64 go build -o appjanitor.exe . GOOS=darwin GOARCH=arm64 go build -o appjanitor . ## Usage appjanitor scan [--older-than 180d] [--pattern "*.tmp,*.bak,*.log,~*,.DS_Store,Thumbs.db"] [--empty-dirs] [--json] Read-only, always a dry run (there is no --apply for scan). Walks and flags candidate files matching ANY of: - filename matches one of the comma-separated glob patterns in --pattern (default: *.tmp,*.bak,*.log,*~,.DS_Store,Thumbs.db), matched against the base filename with filepath.Match - file's mtime is older than --older-than, a duration-like string of digits plus a d/h/m suffix (e.g. 180d, 12h, 45m; 1d = 24h) - (only if --empty-dirs is passed) the directory contains no files anywhere in its subtree -- reported separately as an empty-dir candidate, not counted toward reclaimable bytes Each candidate is printed with the reason it matched (junk-pattern:*.tmp, stale:212d old, empty-dir) and a running total of reclaimable bytes for files. Output ends with a summary line: N files (SIZE reclaimable), M empty dirs. Pass --json to get the same candidate list as JSON instead. appjanitor clean --quarantine [--older-than ...] [--pattern ...] [--empty-dirs] [--apply] Uses the same matching logic as scan. Without --apply it is a dry run: it prints exactly what scan would print, plus a "(dry run -- re-run with --apply)" footer, and touches nothing on disk. With --apply: - every matched FILE is moved into , preserving its path relative to (parent directories are created as needed). Files are always quarantined via a move, never deleted -- a mistaken match is always one `mv` away from undone, never gone. - every matched EMPTY DIRECTORY is removed directly (not quarantined). An empty directory has no content to lose by quarantining, and removing one is reversible in spirit, so os.Remove is used instead of a move. Directories are removed deepest-first so a parent that only becomes empty once its listed empty child is removed still succeeds. --quarantine is required for clean. A summary line reports how many files were moved (and total bytes reclaimed) and how many empty directories were removed. ## Prebuilt binaries See ../downloads/ for prebuilt binaries (Windows/macOS/Linux) and CHECKSUMS.txt for their SHA-256 hashes. These are unsigned indie builds -- Windows SmartScreen and macOS Gatekeeper will warn on first run, which is expected until a code-signing certificate is in place.