Repository navigation
GUI
python3 -m custodian.guiOn macOS you can also double-click packaging/macos/Unreal Custodian.app — it searches for a Python with modern Tk automatically, so a normal launch never hits the blank-window bug below.
Requires a Python built with tkinter and Tk 8.6 or newer. The Python bundled inside Unreal itself (Engine/Binaries/ThirdParty/Python3/) has no tkinter at all, so it can run the CLI but not the GUI — use your system Python.
macOS: the only tkinter Apple ships is inside Xcode's Python 3.9 (/usr/bin/python3), and its Tk is 8.5 — old enough to hit a known blank/white-window bug on modern macOS: the process runs fine, nothing errors, the window just never draws. If your window is empty, this is almost certainly why. Fix:
brew install python-tk@3.12
/opt/homebrew/opt/python@3.12/bin/python3.12 -m custodian.guiThe GUI itself warns about this in the terminal at startup if it detects Tk older than 8.6, so you don't have to work it out from a blank window. The .app bundle does this search automatically.
Known limitation: the .app's Dock icon and tooltip still say "Python", not "Unreal Custodian" — the window's own title bar is correct, only the Dock badge is affected. Framework Python's own bundled stub re-asserts its identity to macOS on startup no matter what launched it or where its binary is copied to; a shell-script wrapper can't override that. The real fix is packaging with py2app, tracked in ROADMAP.md.
Follows Marshall's (@nocxr) Unreal Project Launcher — projects in a sortable table above, engine installs below, double-click a project to launch it.
The divider between the two tables is a draggable sash (a ttk.PanedWindow, not a fixed split) — with only a couple of engines installed the bottom table doesn't need much vertical room, so drag the sash down and give the project list the space. Column borders in both tables are draggable the same way Finder's and Explorer's are.
Scanning runs on a worker thread and streams results into the table as they complete — measuring directory sizes for a hundred projects isn't instant, and locking the window for minutes (as the original launcher's main-thread scan would, once sizing is added) is not acceptable.
Select a row in the Engine Installations table and hit Clean Selected the same as a project. This matters more than it sounds: on the machine this was developed against, a single source-built engine held more reclaimable output than the entire 96-project library. A launcher/binary install's Binaries are refused outright — same rule as the CLI, enforced at the same layer — so there's no way to accidentally destroy a precompiled engine from here.
Two separate checkboxes, not one, because the risk is not the same:
-
Include Intermediate on source-built engines — tens of GB, but the editor still opens afterward.
Engine/Intermediateis purely the incremental-compile cache; reclaiming it only means the next engine compile is a full rebuild instead of incremental. -
Include Binaries on source-built engines — the editor will not launch until the engine is fully rebuilt (hours).
Engine/Binariesis the compiled executable itself.
Selecting a project row and an engine row at once isn't possible; selecting in one table clears the other, so Clean Selected always acts on an unambiguous set.
Projects table:
- Rescan — re-run discovery and sizing.
- Select All / Select None — acts on currently visible rows (respects the "Hide fully cleaned" filter below).
-
Launch Project / Reveal — open the selected
.uproject, or reveal it in Finder/Explorer. -
Never clean this — writes
{"enabled": false}to that project's.ueclean.json. Click again to remove the file and re-enable. -
Clean targets (N/12)… — opens a checklist of which reclaimable folders (
Intermediate,Saved/Cooked,DerivedDataCache,Binaries,Build, …) are in scope for projects that don't have their own.ueclean.json. Same key list as Configuration'stargetsfield, as checkboxes instead of hand-edited JSON — a project's own file still wins over this if one exists. - Hide fully cleaned — detaches (not deletes) rows with nothing left to reclaim. Unchecking it brings them straight back with no rescan.
Engine table:
- Launch Engine — opens the editor with no project loaded, same as launching Unreal directly from that install. If the engine's Binaries were already reclaimed, this tells you that plainly instead of failing silently.
Bottom action bar (applies to whichever table has a selection):
- Delete permanently instead of to Trash — checkbox, off by default. Checking it shows a confirmation explaining the tradeoff (see Safety-Model and FAQ).
- Clean Selected — the one deliberately larger, bolder button on the panel. Cleans only the selected rows marked "ready to clean", after a confirmation listing exactly what will be reclaimed.
"N selected to reclaim · N reclaimable found · N free" sits bottom-right. The first number is not the total across the machine — it's exactly what clicking Clean Selected would reclaim right now, for whatever is currently selected in either table, and it updates the instant you click a different row. Both the display and the actual clean action read from the same internal selection check, so the number on screen can never say one thing while the button does another.
A real clean run is one to five minutes. It runs on a background thread with a progress bar tracking bytes reclaimed as it goes, a label naming whatever it's currently working on, and a Cancel remaining button — nothing about it should ever look frozen. (An earlier version ran this synchronously on the Tk main thread; a multi-minute run made the whole window unresponsive with no way to tell "still working" from "hung".)
Every run also writes a full log to ~/.local/state/unreal-custodian/logs/ (%LOCALAPPDATA%\unreal-custodian\logs\ on Windows) — every item attempted, every failure, in full. The completion dialog only shows the first 8 failures inline; the log has all of them, and its path is printed in that same dialog so you're never stuck trying to remember what happened after you've already dismissed it.
Both tables color eligible rows red and everything else grey, and a legend directly under each table's buttons spells out what that means: red = ready to clean, grey = not eligible right now, check the Status column for the specific reason (too recent, not writable, already clean, an editor has it open, and so on).
Reads directly from the same resolver the CLI uses — "ready to clean", "used recently (< 14d)", "never clean (.ueclean.json)", "already clean", or the specific reason a directory was skipped (symlink, unwritable, owned by another user). Engine rows use the same statuses, plus the reason a precompiled install's Binaries were refused.