The devdoctor CLI. Global flag: -v / --verbose (debug logging to stderr). Sizes accept suffixes like 100M, 2G.
scan — read-only inventoryPrints a Rich table of all known caches, sorted by size. Never deletes anything. Prefer --json when piping or capturing (the table truncates).
devdoctor scan
devdoctor scan --json # structured Report JSON to stdout
devdoctor scan --min-size 100M # hide entries below this size
devdoctor scan --risk safe,reclaimable # include only these risks
devdoctor scan --provider docker # run only these providers
devdoctor scan --older-than 6mo # only entries untouched for that long
devdoctor scan --sort age # longest untouched first (default: size)
devdoctor scan --coverage # also list what no provider accounts for
--risk accepts safe, reclaimable, dangerous, repeatable or comma-separated.
--provider is repeatable or comma-separated and decides what runs: the other providers are never started, so a scoped scan takes as long as its providers do. An unknown name is an error; devdoctor providers lists them.
--older-than takes whole hours, days, weeks, months (30 days) or years: 12h, 90d, 2w, 6mo, 1y. Age is the newest modification of anything inside the entry, not of the directory itself, whose own timestamp does not move when a file deep inside changes. For a git worktree it is the last commit on its branch; for the Time Machine bundle, the youngest snapshot it deletes. Entries whose age is unknown are left out, because "old enough" has to be shown, not assumed. The table's Age column shows the same figure.
Every unfiltered scan ends with a coverage line: how much of the used space on the volume holding your home directory the scan accounts for. A scan reports what its providers know about, which is rarely most of the disk, and the line says so instead of letting the total read as "that is all there is". --coverage goes further: it sizes the home directory outside every classified path and lists the ten largest directories no provider accounts for, three levels deep. It walks what the scan did not, so expect it to take longer than the scan itself. It needs an unfiltered scan. Paths macOS will not let the terminal read are counted and reported, not guessed at.
scan --json contract| Key | Meaning |
|---|---|
schema_version, kind | Report format version and kind |
scanned_at, started_at, duration_ms | Timing |
hostname, platform | Where the scan ran |
total_bytes, total_footprint_bytes, total_reclaimable_bytes, total_shared_bytes | Totals (footprint vs. reclaimable vs. shared — see FAQ) |
entry_count, unknown_reclaimable_entries | Entry count; entries whose reclaim is unknown |
per_provider | Per-provider totals: name, bytes, entries, duration_ms, footprint/reclaimable/shared bytes |
entries | One object per entry (omitted for auto snapshots) |
diagnostics | Unreadable paths and provider warnings |
coverage | used_bytes, classified_bytes, ratio; with --coverage also unclassified (path, bytes, files_only) and skipped. null on a filtered scan |
Each entry carries provider, id, path (may be null), label, size_bytes, footprint/reclaimable/shared bytes, apparent_bytes (the files' summed lengths where measured, else null; far above the footprint for a sparse VM disk image, and never reclaimable), risk, recipe lines, actions, file metadata (uid, gid, mode, owner, group, perms) and mtime.
clean — preview by default, act with --executedevdoctor clean # preview: table, no prompts, no shell calls
devdoctor clean --execute # plan prints, per-entry prompts, one confirmation, then runs
devdoctor clean --execute --yes-safe # safe entries need no per-entry prompt
devdoctor clean --execute --yes-safe --yes # unattended: plan still prints
devdoctor clean --execute --risk safe --provider uv-cache # scope a run
devdoctor clean --execute --allow-dangerous # include dangerous (explicit opt-in)
| Flag | Effect |
|---|---|
--execute | Actually run. Without it: preview only. Refuses without --yes when stdin is not a terminal. |
--yes-safe | Auto-approve safe entries; reclaimable still prompted. |
--yes | Skip the final confirmation (plan still prints). Unattended runs also need --yes-safe. |
--risk | Include only these risks (repeatable or comma-separated). |
--provider | Run only these providers (repeatable or comma-separated). |
--older-than | Include only entries untouched for at least this long (12h, 90d, 2w, 6mo, 1y). |
--allow-dangerous | Offer dangerous entries too; otherwise skipped. Never pass without explicit user say-so. |
Per-entry lines: ✓ done, ✗ failed (with the error), - skipped (with reason), · advice-only. Covering entries (e.g. the Time Machine all-but-newest bundle) are prompted before what they cover.
recipe — reviewable cleanup scriptdevdoctor recipe # all providers, to stdout
devdoctor recipe --provider ollama # one provider
devdoctor recipe -o /tmp/cleanup.sh # write to file
Every line commented out — review, uncomment what you want, run it yourself.
snapshot / diff — track usage over timedevdoctor snapshot --note "before cleanup"
devdoctor diff # latest two snapshots
devdoctor diff --to live # last snapshot vs. current state
history — past cleanup runsdevdoctor history # past runs, newest first
devdoctor history 1 # the newest run, entry by entry
providers — what is available hereLists registered providers and whether each is available on this machine.
serve — local web UIdevdoctor serve # needs the web extra; opens a browser tab
devdoctor serve --port 8731 --no-browser
--port N binds a specific port (0 = random free port, the default); --no-browser skips auto-opening. See the Web UI guide.
Next: Tutorial · Web UI guide · Provider catalogue · FAQ