Command lineClean up
Cleaning up
Delete the safe tier, choose from what needs review, delete exact paths from a selection file, empty the Trash, and uninstall apps.
census cleanDeletes
Usage
census clean [PATH ...] [SCOPE] [--safe] [--progress] [--json]Rescans the scope, deletes the safe findings (caches, build output and logs that rebuild themselves), and saves a review set of everything that needs a person. In a terminal it lists the items and asks [y/N].
| Option | Description |
|---|---|
--scope KIND | drive (the default) or data |
-p, --path PATH | Clean only within this folder |
--safe | Delete without asking. Required without a terminal. |
--progress | Write scan and deletion progress as NDJSON to stderr |
--json | Write one JSON response |
# List the safe items, ask, delete
census clean
# From a script or an agent, after the person agreed
census clean --safe --json
{
"schema_version": 5,
"command": "clean",
"status": "ok",
"exit_code": 0,
"errors": [],
"warnings": [],
"results": {
"freed_bytes": 2041856000,
"used_before_bytes": 50146312192,
"used_after_bytes": 48104456192,
"audit": {
"display": "/Users/me/Library/Application Support/census/audit/clean-1790950000000.ndjson",
"lossy": false
},
"items": [
{
"path": { "display": "/Users/me/.npm/_cacache", "lossy": false },
"rule": "npm.cache",
"outcome": { "result": "deleted", "bytes": 44953600 }
},
{
"path": { "display": "/Users/me/Library/Caches/com.example.app", "lossy": false },
"rule": "app.caches",
"outcome": {
"result": "skipped",
"reason": { "kind": "in_use", "detail": "Example is running" }
}
},
"…"
],
"review": {
"review_id": "rv-1790950000000-1a2b3c4d",
"items": 14,
"allocated_bytes": 6300000000
}
}
}
used_before_bytes and used_after_bytes are the volume’s used space around the run. With APFS snapshots or clones, used space can fall by less than freed_bytes. Item outcomes lists every result and reason. Only one cleanup runs at a time (cleanup_running), and clean refuses to run as root.
Clean only some safe items
With PATH arguments, only those safe findings are deleted. Write each exactly as census findings --json shows its path. If any one isn’t a safe finding of the fresh scan, the whole run is rejected (invalid_selection) and nothing is deleted.
census clean --safe --json "/Users/me/.npm/_cacache"
census reviewDeletes
Usage
census review [SCOPE] [--refresh] [--progress] [--json]Shows the newest review set for the scope, if it is at most 7 days old, and builds one from a fresh scan if there is none. In a terminal it is a numbered menu: Delete which? (1,3-5 · Enter for none), then Type delete.
| Option | Description |
|---|---|
--scope KIND | drive (the default) or data |
-p, --path PATH | A folder’s own scan |
--refresh | Build a new review set from a fresh scan |
--progress | With --refresh, write scan progress as NDJSON to stderr |
--json | Write the review set. Choose from it with census apply. |
$ census review
Needs review · 14 items · 6.3 GB
1 3.1 GB ~/proj/foo/target Cargo build output
2 1.4 GB ~/Downloads/Xcode_15.xip Installer in Downloads
Delete which? (1,3-5 · Enter for none): 2
Permanently delete 1 item (1.4 GB)? Type delete: delete
Freed 1.4 GB · used 44.7 GB → 43.3 GB
review --json only lists. It never deletes:
census review --json
{
"schema_version": 5,
"command": "review",
"status": "ok",
"exit_code": 0,
"errors": [],
"warnings": [],
"results": {
"review_id": "rv-1790950000000-1a2b3c4d",
"created_at_unix_ms": 1790950000000,
"scan_id": "scan_1790949990000-41234",
"items": [
{
"path": "/Users/me/proj/foo/target",
"selectable": true,
"kind": "folder",
"rule": "cargo.target",
"title": "Cargo build output",
"reason": { "kind": "needs_review" },
"allocated_bytes": 3100000000
},
{
"path": "/Users/me/Downloads/Xcode_15.xip",
"selectable": true,
"kind": "file",
"rule": "installers.downloads",
"title": "Installer in Downloads",
"reason": { "kind": "needs_review" },
"allocated_bytes": 1400000000
},
"…"
]
}
}
path is a plain string here: selection files use exactly these strings. selectable: false marks a path that isn’t valid UTF-8, which only the terminal menu can choose.
census applyDeletes
Usage
census apply FILE [SCOPE] [--progress] [--json]Deletes the paths a selection file lists. This is how scripts and agents delete what a person chose, without a terminal menu.
| Option | Description |
|---|---|
--scope KIND | drive (the default) or data |
-p, --path PATH | A folder’s own scan |
--progress | Write scan and deletion progress as NDJSON to stderr |
--json | Write one JSON response |
Write a selection file
A selection file is a JSON array of paths, copied exactly from one of three listings of the same scope:
- a review item’s
path, fromcensus review --json; - a copy’s
members[].path.display, from the newestcensus duplicatessearch; - an item’s
items[].path.display, from the newestcensus largelisting.
["/Users/me/Downloads/Xcode_15.xip"]
census apply selection.json --json
The whole file is rejected, and nothing is deleted, if it isn’t a non-empty array of distinct, absolute, normalized paths, or if any path isn’t in one of those listings (invalid_selection, exit code 1). A listing older than 7 days is review_stale (exit code 1); no listing at all is no_review (exit code 7). Paths you leave out are kept.
What apply checks
apply rescans, then deletes each path only if it still qualifies:
- A review item must still be a finding of the rule the review set lists. Otherwise it is skipped as
missing. - A duplicate copy is deleted only if the same search, run again, still finds another copy of its content that stays. If every copy was chosen, or the others changed, it is skipped as
no_other_copy. A clone of a copy that stays is deleted withbytes: 0. - A Large Files item must still be the same file or folder and pass the same checks. Otherwise it is skipped as
missing,changed_since_scanornot_listed.
The result has the same fields as clean‘s, without review.
census empty-trashDeletes
Usage
census empty-trash [PATH ...] [--yes] [--progress] [--json]Shows what is in every Trash you own, then deletes it permanently after one confirmation. That covers this Mac’s Trash (~/.Trash), iCloud Drive’s, and the Trash on each local, writable drive in /Volumes.
| Option | Description |
|---|---|
--yes | Empty without asking. Required without a terminal. |
--progress | Write deletion progress as NDJSON to stderr |
--json | Write one JSON response. Without --yes, it only lists. |
# Shows the count and size, then asks:
# Permanently delete 1,218 items (8.5 GB)? [y/N]
census empty-trash
In a terminal it deletes exactly the items it showed; anything trashed after that stays. --json without --yes lists and deletes nothing:
census empty-trash --json
{
"schema_version": 5,
"command": "empty-trash",
"status": "ok",
"exit_code": 0,
"errors": [],
"warnings": [],
"results": {
"count": 1218,
"allocated_bytes": 8512000000,
"trashes": [
{
"name": "This Mac",
"path": { "display": "/Users/me/.Trash", "lossy": false },
"count": 1190,
"allocated_bytes": 8200000000,
"items": [
{
"path": { "display": "/Users/me/.Trash/Xcode_15.xip", "lossy": false },
"kind": "file",
"allocated_bytes": 6900000000,
"trashed_unix_ms": 1790950000000
},
"…"
]
},
{
"name": "iCloud Drive",
"path": { "display": "/Users/me/Library/Mobile Documents/com~apple~CloudDocs/.Trash", "lossy": false },
"count": 14,
"allocated_bytes": 12000000
}
]
}
}
To delete only some of it, pass the items’ items[].path or whole Trashes’ trashes[].path as arguments, exactly as --json lists them:
census empty-trash --yes --json "/Users/me/.Trash/Xcode_15.xip"
- Nothing is forced. Locked items, items an app has open (
in_use), and folders another user owns stay, and are listed. - A Trash census can’t read stops the command before anything is deleted (
trash_unreadable, exit code 2). Reading~/.Trashneeds Full Disk Access for the app that runs census. - iCloud items that are only in the cloud are deleted without being downloaded.
--yes lists the Trashes again before deleting, so it deletes what is there by then, as Finder does. With --yes --json, the result is clean‘s without review, with rule: "user.trash" on every item.
census uninstallDeletes
Usage
census uninstall NAME [PATH ...] [SCOPE] [--yes] [--progress] [--json]Rescans, then removes an app and the data named after it. NAME matches as census app does, by name or bundle ID. In a terminal it lists what goes and asks [y/N].
| Option | Description |
|---|---|
--scope KIND | drive (the default) or data |
-p, --path PATH | A folder’s own scan |
--yes | Uninstall without asking. Required without a terminal. |
--progress | Write scan and deletion progress as NDJSON to stderr |
--json | Write one JSON response |
# See what an uninstall would remove
census app Slack
# Remove the app and its data, after one confirmation
census uninstall Slack
- What goes. The app, and its data in private locations: folders named after its bundle ID, its helper’s ID, or its name, with
highormediumconfidence, not shared, and not needing an administrator. WithPATHarguments, exactly those paths go, ascensus app NAME --jsonshows them, and the app itself must be one of them. - What stays. Everything else, listed in
results.keptwith areason:shared(a group container other apps from the developer may use),needs_admin(under/Library),may_belong_to_another_app,not_selectedornot_verifiable. - Before anything is deleted. A part of macOS is refused (
system_app). The app’s launch agents in~/Library/LaunchAgentsare unloaded. If a program inside the app is still running, nothing is deleted (app_running): census never quits apps. - Order. The app goes first. Its data goes only if the app was deleted in full.
census uninstall Slack --yes --json
{
"schema_version": 5,
"command": "uninstall",
"status": "ok",
"exit_code": 0,
"errors": [],
"warnings": [],
"results": {
"app": {
"name": "Slack",
"bundle_id": "com.tinyspeck.slackmacgap",
"version": "4.41",
"path": { "display": "/Applications/Slack.app", "lossy": false }
},
"freed_bytes": 612000000,
"used_before_bytes": 50146312192,
"used_after_bytes": 49534312192,
"audit": {
"display": "/Users/me/Library/Application Support/census/audit/uninstall-1790950000000.ndjson",
"lossy": false
},
"items": [
{
"path": { "display": "/Applications/Slack.app", "lossy": false },
"rule": "app.bundle",
"outcome": { "result": "deleted", "bytes": 560000000 }
},
{
"path": { "display": "/Users/me/Library/Caches/com.tinyspeck.slackmacgap", "lossy": false },
"rule": "app.data",
"outcome": { "result": "deleted", "bytes": 52000000 }
}
],
"kept": [
{
"path": {
"display": "/Users/me/Library/Group Containers/BQR82RBBHL.com.tinyspeck.slackmacgap",
"lossy": false
},
"location": "Shared group container",
"confidence": "medium",
"allocated_bytes": 310000000,
"reason": "shared"
}
],
"background_items": [],
"not_inspected": []
}
}
If the app isn’t deleted in full, the exit code is 4 with app_not_removed, and its data is left as it was. EPERM on the app usually means macOS’s App Management protection: allow the app that runs census in System Settings › Privacy & Security.