Skip to main content

Crate cruftkill

Crate cruftkill 

Source
Expand description

§cruftkill — polyglot dev-cache reaper

Find and delete node_modules, .venv, target, DerivedData, __pycache__, obj, .gradle, .next, .turbo, and the rest of your build cruft from a fast terminal UI.

 ██████╗███████╗████████╗
██╔════╝██╔════╝╚══██╔══╝
██║     █████╗     ██║
██║     ██╔══╝     ██║
╚██████╗██║        ██║
 ╚═════╝╚═╝        ╚═╝

Inspired by voidcosmos/npkill — rewritten in Rust with a parallel async scanner and extended to 17 ecosystems. Single binary, no runtime dependencies.

⚠️ cft deletes recursively without a recycle bin. Always review the list before pressing d. Run with --dry-run first if you want a preview.

§Quick start

cargo install cruftkill

cft                            # scan current dir with `node` profile
cft ~/Projects                 # scan a specific directory
cft -p rust ~/code             # use the `rust` profile (matches `target/`)
cft -p node -p python ~/code   # combine profiles
cft -p all ~/                  # every ecosystem at once
cft --dry-run ~/Projects       # preview without deleting
cft --no-tui ~/Projects | jq   # scriptable NDJSON output

§What gets deleted (17 ecosystems)

ProfileMatches
node (default)node_modules, .npm, .pnpm-store, .next, .nuxt, .angular, .svelte-kit, .vite, .nx, .turbo, .parcel-cache, .cache, coverage, .jest, …
python__pycache__, .pytest_cache, .mypy_cache, .ruff_cache, .tox, .venv, venv, …
rusttarget
javatarget, .gradle, out
swiftDerivedData, .swiftpm
dotnetobj, TestResults, .vs
cppCMakeFiles, cmake-build-debug, cmake-build-release
android.cxx, externalNativeBuild
ruby.bundle
elixir_build, deps, cover
haskelldist-newstyle, .stack-work
scala.bloop, .metals, target
unityLibrary, Temp, Obj
unrealIntermediate, DerivedDataCache, Binaries
godot.import, .godot
data-science.ipynb_checkpoints, .dvc, .mlruns, outputs, …
infra.serverless, .vercel, .netlify, .terraform, .sass-cache, …
allunion of every profile above

§Interactive TUI

KeyAction
/ k · / jmove cursor
d / Space / Enteropen delete confirm
y / n / Escconfirm / cancel modal
stoggle sort by size (desc default)
ntoggle sort by name (asc default)
mtoggle sort by last-used (desc default)
r / F5rescan
q / Ctrl-Cquit

The header surfaces live progress (dirs scanned, releasable space, reclaimed space) and the modal shows risk reasons for paths that shouldn’t normally be deleted (~/.config, AppData, .app bundles, …).

§Safety

Two layered guards run before any filesystem mutation:

  1. Basename guard — the path’s basename must appear in the resolved target list. Catches “I typed the wrong path” mistakes.
  2. Containment guard — both the scan root and the target path are canonicalised (symlinks resolved). The canonical target must starts_with the canonical root. Catches symlink-escape attacks even if the link is named like a legitimate target.

Underneath, std::fs::remove_dir_all is hardened against symlink traversal (CVE-2022-21658).

§Library usage

cruftkill is also a library — the cft binary is just a TUI on top. Import the core module to embed scanning, sizing, risk analysis, and safe deletion in your own tool.

use cruftkill::core::{scanner, types::ScanOptions};
use cruftkill::core::profiles::resolve_targets;

let targets = resolve_targets(&["node", "rust"]);
let opts = ScanOptions {
    targets,
    exclude: vec![],
    sort_by: None,
    perform_risk_analysis: true,
};
let mut handle = scanner::start_scan("/home/me/Projects".into(), opts);

while let Some(found) = handle.results.recv().await {
    println!("found: {} (sensitive: {:?})",
        found.path.display(),
        found.risk_analysis.as_ref().map(|r| r.is_sensitive));
}

§Architecture

cft binary (src/main.rs)
   │
   ├── TUI mode  ──►  src/tui/   (ratatui + crossterm + tokio::select!)
   │                     │
   └── --no-tui  ──►  run_no_tui → NDJSON
                         │
                         ▼
                  src/core/
                     ├── scanner       parallel tokio worker pool
                     ├── size          refcounted async sum, 60s timeout
                     ├── risk          pure path classifier
                     ├── safe_delete   basename guard
                     ├── delete        canonicalize + remove_dir_all
                     ├── profiles      17 ecosystem profiles
                     ├── sort          path / size / age comparators
                     ├── filter        case-insensitive substring
                     ├── ignore        GLOBAL_IGNORE set (skip-on-walk)
                     ├── types         ScanOptions, FolderResult, …
                     └── error         CruftError (thiserror)

§License

MIT.

Re-exports§

pub use crate::core::error::CruftError;
pub use crate::core::types::DeleteResult;
pub use crate::core::types::FolderResult;
pub use crate::core::types::RiskAnalysis;
pub use crate::core::types::ScanFoundFolder;
pub use crate::core::types::ScanOptions;
pub use crate::core::types::SortBy;
pub use crate::core::types::SortDirection;

Modules§

cli
CLI argument parsing for the cft binary.
core
Core library — public, framework-agnostic API.
tui
Interactive ratatui-based UI for the cft binary.