A fast and efficient CLI tool for recursively cleaning development build directories across 11 language ecosystems to reclaim disk space. Supports Rust, Node.js, Python, Go, Java/Kotlin, C/C++, Swift, .NET/C#, Ruby, Elixir, and Deno.
Created and maintained by Tom Planche. The GitHub organization exists solely to host the Homebrew tap alongside the main repository.
Quick Start
# Install from crates.io
# Clean all development directories in current directory
# Preview what would be cleaned (dry run)
# Interactive mode - choose which projects to clean
Features
- Multi-language support: Clean build artifacts across 11 ecosystems — Rust (
target/), Node.js (node_modules/), Python (cache dirs), Go (vendor/), Java/Kotlin (target//build/), C/C++ (build/), Swift (.build/), .NET/C# (bin/+obj/), Ruby (.bundle//vendor/bundle/), Elixir (_build/), and Deno (vendor//node_modules/) - Parallel scanning: Lightning-fast directory traversal using multithreading
- Smart filtering: Filter by project size, modification time, and project type
- Flexible sorting: Sort results by size, age, name, or project type with
--sort - Interactive mode: Choose which projects to clean with an intuitive interface
- Dry-run mode: Preview what would be cleaned without actually deleting anything
- Progress indicators: Real-time feedback during scanning and cleaning operations
- Executable preservation: Keep compiled binaries before cleaning with
--keep-executables - Safe by default: Moves directories to the system trash for recoverable deletions; use
--permanentwhen you wantrm -rf - JSON output: Structured
--jsonoutput for scripting, piping, and dashboard integration - Detailed statistics: See total space that can be reclaimed before cleaning
- Persistent configuration: Set defaults in
~/.config/clean-dev-dirs/config.tomlso you don't repeat flags - Flexible configuration: Combine multiple filters and options for precise control
Inspiration
This project is inspired by cargo-clean-all, a Rust-specific tool for cleaning cargo projects. I've improved upon the original concept with:
- Multi-language support: Extended beyond Rust to support Node.js, Python, Go, Java/Kotlin, C/C++, Swift, .NET/C#, Ruby, Elixir, and Deno projects
- Parallel scanning: Significantly faster directory traversal using multithreading
- Enhanced filtering: More granular control over what gets cleaned
- Cleaner code architecture: Well-structured, modular codebase for better maintainability
Installation
From crates.io (Recommended)
From Source
Requirements
- Rust 2024 edition or later
- Cargo package manager
Usage
Basic Usage
# Clean all development directories in the current directory
# Clean a specific directory
# Preview what would be cleaned (dry run)
# Interactive mode - choose which projects to clean
Project Type Filtering
# Clean only Rust projects
# or use short flag
# Clean only Node.js projects
# Clean only Python projects
# Clean only Go projects
# Clean only Java/Kotlin projects
# Clean only C/C++ projects
# Clean only Swift projects
# Clean only .NET/C# projects
# Clean only Ruby projects
# Clean only Elixir projects
# Clean only Deno projects
# Clean all project types (default)
Size and Time Filtering
# Only clean projects with build dirs larger than 100MB
# Only clean projects not modified in the last 30 days
# Combine size and time filters
Sorting
# Sort projects by size (largest first)
# Sort projects by age (oldest first)
# Sort projects by name (alphabetical)
# Sort projects grouped by type (Go, Node, Python, Rust)
# Reverse any sort order (e.g. smallest first)
# Combine with other options
Keeping Executables
# Preserve compiled binaries before cleaning
# or use short flag
# In interactive mode (-i) without -k, you will be prompted:
# "Keep compiled executables before cleaning? (y/N)"
# Combine with other options
When enabled, compiled outputs are copied to <project>/bin/ before the build directory is deleted:
- Rust: executables from
target/release/andtarget/debug/are copied tobin/release/andbin/debug/ - Python:
.whlfiles fromdist/and.so/.pydC extensions frombuild/are copied tobin/ - Node.js / Go / Java / C++ / Swift / .NET / Ruby / Elixir / Deno: no-op (their cleaned directories contain dependencies or build outputs not easily preservable)
Trash Support (Default)
By default, build directories are moved to the system trash (Trash on macOS/Linux, Recycle Bin on Windows) instead of being permanently removed. This means all deletions are recoverable -- just check your trash.
# Default behavior: moves to trash (safe, recoverable)
# Permanently delete instead (rm -rf style, irreversible)
# Combine permanent deletion with other options
To make permanent deletion the default, set use_trash = false in your config file:
[]
= false
JSON Output
Use --json to get structured output for scripting, piping to jq, or feeding into dashboards:
# List all projects as JSON (dry run)
# Clean and get machine-readable results
# Pipe to jq for further processing
|
# Get total reclaimable space across Rust projects
|
When --json is active, all human-readable output (colors, progress bars, emojis) is suppressed and a single JSON document is printed to stdout. --json is incompatible with --interactive and implies --yes behavior (no confirmation prompts).
Advanced Options
# Use 8 threads for faster scanning
# Show verbose output including scan errors
# Skip specific directories during scanning
# Limit how deep into the directory tree the scanner goes
# Non-interactive mode (auto-confirm)
# Combine multiple options
Configuration File
You can store default settings in a TOML file so you don't have to repeat the same flags every time. CLI arguments always override config file values.
Location: ~/.config/clean-dev-dirs/config.toml (Linux/macOS) or %APPDATA%\clean-dev-dirs\config.toml (Windows)
Use the built-in config subcommand to manage the file without hunting for the path:
# Find out where the config file lives on your platform
# Write a fully commented-out template (does nothing if the file already exists)
# Print every setting with its current value or default
# Default project type filter
= "rust"
# Default directory to scan (~ is expanded)
= "~/Projects"
[]
= "50MB"
= 7
= "size" # "size", "age", "name", or "type"
= false
[]
= 4
= true
= [".cargo", "vendor"]
= [".git"]
= 5 # optional; omit for unlimited depth
[]
= true
= false
= false
= true # default; set to false for permanent deletion
All fields are optional — only set what you need. An absent config file is silently ignored; a malformed one produces an error message.
Layering rules:
| Value type | Behavior |
|---|---|
Scalar (keep_size, threads, project_type, dir, sort, …) |
CLI wins if provided, otherwise config file, otherwise built-in default |
Boolean flag (--dry-run, --verbose, --reverse, …) |
true if the CLI flag is present or the config file sets it to true |
List (skip, ignore) |
Merged — config file entries first, then CLI entries appended |
Examples:
# Uses keep_size = "50MB" from config, overrides project_type on CLI
# CLI --keep-size wins over the config file value
# skip dirs from config (.cargo, vendor) + CLI (node_modules) are all active
Common Use Cases
1. Clean old Rust projects:
2. Preview large Python cache directories:
3. Interactive cleaning of all Node.js projects:
4. Quick cleanup with confirmation:
5. Fast scan with multiple threads:
6. Clean Rust projects but keep the compiled binaries:
7. Find the biggest space hogs:
8. Clean the most stale projects first:
9. Get a JSON report for a CI/CD dashboard:
|
10. Permanently delete (skip the trash):
11. Set up a config file for your usual workflow:
# Generate a commented-out template at the right platform path
# Open it in your editor, uncomment and set what you need, then just run:
Command Reference
Config Subcommand
clean-dev-dirs config <COMMAND>
| Command | Description |
|---|---|
config path |
Print the platform-specific path to the config file |
config show |
Print every setting with its current value or annotated default |
config init |
Write a fully commented-out template if no config file exists yet |
Main Arguments
| Argument | Description |
|---|---|
[DIR] |
Directory to search for projects (default: current directory) |
Project Type Filter
| Option | Short | Values | Description |
|---|---|---|---|
--project-type |
-p |
all, rust, node, python, go, java, cpp, swift, dotnet, ruby, elixir, deno |
Filter by project type (default: all) |
Filtering Options
| Option | Short | Description |
|---|---|---|
--keep-size |
-s |
Ignore projects with build dir smaller than specified size |
--keep-days |
-d |
Ignore projects modified in the last N days |
Sorting Options
| Option | Values | Description |
|---|---|---|
--sort |
size, age, name, type |
Sort projects before display (default: scan order) |
--reverse |
Reverse the sort order |
Default sort directions: size largest first, age oldest first, name A-Z, type alphabetical by type name.
Output Options
| Option | Description |
|---|---|
--json |
Output results as a single JSON object for scripting/piping (incompatible with --interactive) |
Execution Options
| Option | Short | Description |
|---|---|---|
--yes |
-y |
Don't ask for confirmation; clean all detected projects |
--dry-run |
List cleanable projects without actually cleaning | |
--interactive |
-i |
Use interactive project selection |
--keep-executables |
-k |
Copy compiled executables to <project>/bin/ before cleaning |
--permanent |
Permanently delete directories instead of moving them to the system trash |
Scanning Options
| Option | Short | Description |
|---|---|---|
--threads |
-t |
Number of threads for directory scanning (default: CPU cores) |
--verbose |
-v |
Show access errors during scanning |
--skip |
Directories to skip during scanning (can be specified multiple times) | |
--max-depth |
Maximum directory depth to scan (default: unlimited) |
Size Formats
The --keep-size option supports various size formats:
| Format | Example | Description |
|---|---|---|
| Decimal | 100KB, 1.5MB, 2GB |
Base 1000 |
| Binary | 100KiB, 1.5MiB, 2GiB |
Base 1024 |
| Bytes | 1000000 |
Raw byte count |
Examples:
Project Detection
The tool automatically detects development projects by looking for characteristic files and directories:
Rust Projects
- Detection criteria: Both
Cargo.tomlandtarget/directory must exist, and the directory is not a workspace member (workspace members share the roottarget/and are skipped) - Cleans:
target/directory - Name extraction: From
[package] nameinCargo.toml
Node.js Projects
- Detection criteria: Both
package.jsonandnode_modules/directory must exist - Cleans:
node_modules/directory - Name extraction: From
namefield inpackage.json
Python Projects
- Detection criteria:
- At least one config file:
requirements.txt,setup.py,pyproject.toml,setup.cfg,Pipfile,pipenv.lock,poetry.lock - At least one cache/build directory exists
- At least one config file:
- Cleans: All present cache/build directories:
__pycache__.pytest_cachevenv/.venvbuild/dist.eggs/.tox/.coverage- Any
*.egg-infodirectories found in the project root
- Name extraction: From
pyproject.toml(project name or tool.poetry name) orsetup.py
Go Projects
- Detection criteria: Both
go.modandvendor/directory must exist - Cleans:
vendor/directory - Name extraction: From module path in
go.mod
Java/Kotlin Projects
- Detection criteria:
- Maven:
pom.xml+target/directory - Gradle:
build.gradleorbuild.gradle.kts+build/directory
- Maven:
- Cleans:
target/(Maven) orbuild/(Gradle) directory - Name extraction: From
<artifactId>inpom.xml, orrootProject.nameinsettings.gradle
C/C++ Projects
- Detection criteria:
CMakeLists.txtorMakefile+build/directory - Cleans:
build/directory - Name extraction: From
project()inCMakeLists.txt, or falls back to directory name
Swift Projects
- Detection criteria: Both
Package.swiftand.build/directory must exist - Cleans:
.build/directory - Name extraction: From
name:inPackage.swift
.NET/C# Projects
- Detection criteria: At least one
.csprojfile +bin/and/orobj/directories - Cleans: Both
bin/andobj/directories when present - Name extraction: From the
.csprojfilename
Ruby Projects
- Detection criteria: Both
Gemfileand.bundle/orvendor/bundle/directory must exist - Cleans: Both
.bundle/andvendor/bundle/directories when present - Name extraction: From the
namefield in a.gemspecfile, or falls back to directory name
Elixir Projects
- Detection criteria: Both
mix.exsand_build/directory must exist - Cleans:
_build/directory - Name extraction: From
app:atom inmix.exs, or falls back to directory name
Deno Projects
- Detection criteria:
deno.jsonordeno.jsonc+vendor/directory (primary), ornode_modules/withoutpackage.json(secondary) - Cleans:
vendor/ornode_modules/directory - Name extraction: From
namefield indeno.json/deno.jsonc, or falls back to directory name
Safety Features
- Trash by default: Directories are moved to the system trash for recoverable cleanups; use
--permanentto override - Dry-run mode: Preview all operations before execution with
--dry-run - Interactive confirmation: Manually select projects to clean with
--interactive - Intelligent filtering: Skip recently modified or small projects with
--keep-daysand--keep-size - Error handling: Graceful handling of permission errors and inaccessible files
- Read-only scanning: Never modifies files during the scanning phase
- Clear output: Color-coded, human-readable output with project types and sizes
Output
The tool provides beautiful, colored output including:
| Icon | Project Type |
|---|---|
| 🦀 | Rust projects |
| 📦 | Node.js projects |
| 🐍 | Python projects |
| 🐹 | Go projects |
| ☕ | Java/Kotlin projects |
| ⚙️ | C/C++ projects |
| 🐦 | Swift projects |
| 🔷 | .NET/C# projects |
| 💎 | Ruby projects |
| 💧 | Elixir projects |
| 🦕 | Deno projects |
Sample Output
Found 15 projects
📊 Found projects:
🦀 my-rust-app (/home/user/projects/rust-app)
Size: 2.3 GB
📦 web-frontend (/home/user/projects/web-app)
Size: 856 MB
🐍 ml-project (/home/user/projects/python-ml)
Size: 1.2 GB
Total space that can be reclaimed: 4.4 GB
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Adding Language Support
Want to add support for a new programming language? Here's how to extend clean-dev-dirs:
1. Update Project Types
First, add your language to the ProjectType enum in src/project/project.rs:
Don't forget to update the Display implementation to include an appropriate emoji and name.
2. Add CLI Filter Option
Update src/config/filter.rs to add your language to the ProjectFilter enum:
3. Implement Project Detection
Add detection logic in src/scanner.rs by implementing:
- Detection method:
detect_your_language_project()- identifies projects by looking for characteristic files - Name extraction:
extract_your_language_project_name()- parses project configuration files to get the name - Integration: Update
detect_project()to call your detection method
Example detection criteria:
4. Update Directory Exclusions
Add any language-specific directories that should be skipped during scanning to the should_scan_entry() method in src/scanner.rs.
5. Update Documentation
- Add your language to the "Project Detection" section in this README
- Update the CLI help text descriptions
- Add examples in the usage section
6. Testing Considerations
Consider these when testing your implementation:
- Multiple config files: Some languages have different project file formats
- Build directory variations: Different build tools may use different directory names
- Name extraction edge cases: Handle malformed or missing project names gracefully
- Performance: Ensure detection doesn't significantly slow down scanning
7. Example Languages to Add
Some languages that would be great additions:
- PHP: Look for
composer.json+vendor/ - Dart/Flutter: Look for
pubspec.yaml+.dart_tool/orbuild/ - Scala: Look for
build.sbt+target/
8. Pull Request Guidelines
When submitting your language support:
- Test thoroughly: Verify detection works with real projects
- Add examples: Include sample project structures in your PR description
- Update help text: Ensure all user-facing text is clear and consistent
- Follow patterns: Use the same patterns as existing language implementations
- Consider edge cases: Handle projects with unusual structures gracefully
License
This project is dual-licensed under either:
- MIT License - see the LICENSE-MIT file for details
- Apache License 2.0 - see the LICENSE-APACHE file for details
You may choose either license at your option.
Acknowledgments
Built with excellent open-source libraries:
- Clap - Command-line argument parsing with derive macros
- Rayon - Data parallelism for fast directory scanning
- Colored - Beautiful colored terminal output
- Indicatif - Progress bars and spinners
- Inquire - Interactive prompts and selection
- WalkDir - Recursive directory iteration
- Humansize - Human-readable file sizes
- Serde + serde_json + TOML - Serialization, JSON output, and configuration file parsing
- dirs - Cross-platform config directory resolution
- trash - Cross-platform system trash support
Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Crates.io: clean-dev-dirs