Skip to content
cargo-broom
MITDocumentation
Cargo subcommand · Rust · CLI

Clean up without it becoming a filesystem operation

cargo clean knows one project. cargo-broom finds the target directories across a whole directory tree, judges them by age and size, and first shows what a run would remove. Nothing happens without a confirmation.

4
commands that only analyse
3
ways to confirm: --dry-run, --interactive, --yes
0.1.0
safety-first alpha
cargo-broom 0.1.0
$ cargo broom --dry-run --keep-days 0 --keep-size 50MB ~/GitHub/kestrel
cargo-broom Summary (Dry Run)
ℹ cargo-broom

  Reclaimable: 5.97 GiB

● Level A — Full Target Clean (Coarse) (1)
  - kestrel-core (5.97 GiB) at /Users/maxmustermann/GitHub/kestrel/target
Run of 16 Sep 2026, shortenedDry run · nothing deleted
Foundation
  • Rust 1.89+
  • Cargo subcommand
  • mtime, not atime
  • Dry run
  • OS trash
  • JSON reports
  • runemark
  • launchd · systemd
  • MIT
As of September 2026 · v0.1.0
4
analysis commands: inspect, doctor, toolchains, budget
9
cleanup modes and modifiers
14
days of rolling history with --history
1.89
minimum Rust version
01 — Overview

cargo clean does not answer the question you actually have.

For one known project cargo clean is enough. Once dozens of checkouts sit on the disk the question changes: which of them take the space, which have gone cold, and what would a scheduled run remove tonight? cargo-broom adds recursive discovery, age and size policies, dry runs, interactive selection and machine-readable reports.

github.com/casoon/broom →
  1. 01Look firstinspect and --dry-run touch nothing
  2. 02Then confirm — without --dry-run, --interactive or --yes the run fails before the scan
  3. 03Reversible--trash moves targets to the OS trash instead of deleting them
  4. 04Traceable--format json for scripts, reports and scheduled dry runs
02 — Output

What the terminal prints.

cargo broom inspectInventory
$ cargo broom inspect ~/GitHub
cargo-broom Inspect — /Users/maxmustermann/GitHub
Found 3 Rust target directories occupying 7.89 GiB

 • kestrel-core                5.97 GiB  /Users/maxmustermann/GitHub/kestrel/target
 • parser-core                 1.19 GiB  /Users/maxmustermann/GitHub/parser-core/target
 • templates                 750.55 MiB  /Users/maxmustermann/GitHub/report-cli/crates/templates/target
Level ADry run
$ cargo broom --dry-run --keep-days 0 --keep-size 50MB ~/GitHub/kestrel
cargo-broom Summary (Dry Run)
 cargo-broom

  Reclaimable: 5.97 GiBLevel A — Full Target Clean (Coarse) (1)
  - kestrel-core (5.97 GiB) at /Users/maxmustermann/GitHub/kestrel/target

Next steps:
  • Run `cargo broom -y` to reclaim 5.97 GiB
    $ cargo broom -y
Level BDry run
$ cargo broom --dry-run --fine-only --clean-incremental --clean-doc ~/GitHub/kestrel
cargo-broom Summary (Dry Run)
 cargo-broom

  Reclaimable: 1.53 GiBLevel B — Selective / Experimental Fine Clean (1)
  - kestrel-core (pruned 0 stale fingerprints, 1 files) — 1.53 GiB at /Users/maxmustermann/GitHub/kestrel/target

Next steps:
  • Run `cargo broom -y` to reclaim 1.53 GiB
    $ cargo broom -y
03 — Modes

What each mode removes.

ModeSelectionWhat it removes
Default / Level ATarget is at least --keep-days old and, when supplied, at least --keep-size largeThe complete standard local target/
--coarse-onlySame policy as Level A, fine operations are rejectedThe complete standard local target/
--clean-incrementalTargets not selected for Level AThe caches under target/*/incremental
--clean-docTargets not selected for Level AGenerated target/doc output
--experimental-fineTargets not selected for Level AFingerprints and matching hashed artifacts, selected by experimental age and duplicate heuristics
--toolchains <list>Modifier for --experimental-fineAlso fingerprints built with a rustc other than the named toolchains
--installedModifier for --experimental-fineThe same, but keeps any currently rustup-installed toolchain
--fine-onlyDisables Level AOnly the explicitly requested fine operations
--trashModifier for Level A, with any policy aboveMoves the target to the OS trash instead of deleting it permanently

Conflicting combinations are rejected by the CLI: --fine-only needs one of the fine operations, and --tests-only, --toolchains and --installed need --experimental-fine.

04 — Safety

Where the tool deliberately stops.

Only standard targets go completely

Whole-target cleanup is limited to a standard local <workspace>/target. Shared targets, configured overrides, symlinks and targets outside the workspace are never removed wholesale.

No fallback to the coarse level

If a fingerprint cannot be parsed, cargo-broom skips fine cleanup for that project. It never falls back to deleting the complete target instead.

mtime, not atime

Staleness is judged by mtime. On noatime and relatime mounts atime is not updated on every read, which made artifacts look falsely recent. mtime is written by every actual Cargo build.

Running builds are skipped

A target currently locked by a Cargo process is skipped entirely at both levels, instead of racing the build for its files.

Confirmation is not configurable

By design: an unattended destructive run has to carry --yes in the command itself. The configuration file cannot shorten that.

Errors stay visible

Filesystem deletion errors are reported and produce a failing exit status. JSON reports go to stdout, process errors to stderr.

05 — Analyse

Four commands that delete nothing.

cargo broom inspect

Inventory

Lists the discovered targets and their disk usage without applying any policy.

cargo broom doctor

Diagnostics

Reports configured target overrides, unusually large targets and target lock findings.

cargo broom toolchains

Toolchains

Shows installed rustup toolchains not referenced by any rust-toolchain file below the directory. It uninstalls nothing.

cargo broom budget

Budget

Totals target disk usage across all discovered projects and, with --limit, flags when the budget is exceeded – along with the largest contributors.

06 — Get started

Look first, then confirm.

  1. 1Install

    From crates.io

    The crate is published as cargo-broom and is available as cargo broom once installed. For the current main branch, install straight from the repository instead. It requires Rust 1.89 or newer.

    cargo install cargo-broom # or straight from the repository cargo install --git https://github.com/casoon/broom.git
  2. 2Look

    inspect and dry run

    Neither command modifies files. inspect shows what is there, the dry run shows what the current policy would select.

    cargo broom inspect ~/GitHub cargo broom --dry-run ~/GitHub
  3. 3Clean

    Confirmed run

    Only --yes or --interactive deletes anything. --trash makes the same run reversible, because the targets end up in the trash.

    cargo broom --yes --keep-days 14 --keep-size 50MB ~/GitHub
07 — Operations

Configuration and a scheduled run.

broom.tomlConfiguration
root_path = "~/GitHub"
keep_days = 14
keep_size_mb = 50

# Unattended runs keep the experimental part switched off.
experimental_fine = false
fine_only = false
coarse_only = false
trash = false
history = false

hidden = false
ignore = ["archived-repo"]
skip = ["node_modules"]
cron · launchd · systemdScheduled
cargo broom --yes --keep-days 14 --keep-size 50MB \
  --clean-incremental --clean-doc --history --color never ~/GitHub
08 — Limits

What cargo-broom is not meant for.

The repository names its limits itself – some of them are the reason for the conservative defaults.

Not for CI caches

A target/ persisted across CI runs grows for a different reason: every dependency bump leaves fingerprints from the previous Cargo.lock behind. That calls for cache-key-based invalidation, not an age heuristic. The README points to Swatinem/rust-cache, sccache and cargo-chef.

Fingerprint pruning is experimental

--experimental-fine is off by default and belongs in a reviewed dry run first – not in an unattended job.

Version 0.1.0

A safety-first alpha. Conservative target cleanup, inspection, JSON reporting and selective cache cleanup are available.

Active projects never pass the age gate

rust-analyzer runs cargo check whenever a project is open in an editor and keeps refreshing the target. Without --clean-incremental and --clean-doc such a project falls through to Skipped and reclaims nothing, however large its target grows.

Registry cleanup only sees below the directory

registry reads the Cargo.lock files below the given directory. Projects outside are not considered – hence the dry run over the broadest relevant root.

Recovery means rebuilding

Only generated Cargo output is removed. It is reproducible, but the next build takes correspondingly longer.

LicenceMIT licensed. The terminal presentation – metrics, finding groups and the Next steps block – comes from runemark, the CASOON library for readable CLI output. The rustc-version hashing behind --toolchains and --installed is ported from cargo-sweep (MIT), which in turn mirrors Cargo’s own internal fingerprint hash.License text in the repository →
09 — FAQ

Frequently asked questions.

Can a run delete something unrecoverable?

Only generated Cargo output is removed, which a build recreates. On top of that, --trash moves targets to the OS trash instead of deleting them permanently. What remains is a longer build after the cleanup.

What happens if I forget the confirmation?

A call without --dry-run, --interactive or --yes fails before the project scan even begins. The confirmation deliberately cannot be stored in the configuration file.

Why does a run reclaim nothing on my active project?

Level A only removes a target once nothing inside it has been touched for keep_days. rust-analyzer keeps refreshing the target while the editor is open, so the age gate never triggers. That is what --clean-incremental and --clean-doc are for: Cargo regenerates both cache directories on demand.

How often should a scheduled run fire?

The README suggests every two days rather than weekly, because incremental caches regrow within hours and the run itself is cheap. On macOS, StartCalendarInterval on odd days is more reliable than StartInterval, whose timer restarts on every reboot.

What exactly does --experimental-fine do?

It removes individual fingerprints and their matching hashed artifacts, selected by age and duplicate heuristics. Each entry is validated against its own fingerprint JSON rather than trusting the .fingerprint directory naming. If no JSON is parseable, the project is marked unsupported instead of guessing.

What does --history record?

One line per target per run with the resulting size, in a rolling 14-day log at ~/.local/state/cargo-broom/history.jsonl. Dry runs are never recorded. From the second entry on, the report gains a History metric – whether a target is growing back despite regular cleanup.

Start with the dry run.

Install it, run inspect over your own project tree and see how much is actually sitting there.