Metadata-Version: 2.5
Name: depclean
Version: 0.1.0
Summary: Terminal UI to find and remove node_modules, Python venvs and other reinstallable dependency folders
Project-URL: Homepage, https://github.com/magnumnighthawk/depclean
Project-URL: Issues, https://github.com/magnumnighthawk/depclean/issues
Author: Karthick Ramakrishnan
License-Expression: MIT
License-File: LICENSE
Keywords: cleanup,cli,disk-space,node_modules,tui,venv
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console :: Curses
Classifier: Intended Audience :: Developers
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: System :: Filesystems
Classifier: Topic :: Utilities
Requires-Python: >=3.9
Requires-Dist: windows-curses>=2.3; sys_platform == 'win32'
Description-Content-Type: text/markdown

# depclean

**Get disk space back from projects you aren't working on.** `depclean` scans a folder of code, finds the dependency and cache folders that a reinstall brings back (`node_modules`, Python virtualenvs, Rust `target`, build caches, …), shows how much space each project would free, and lets you pick what to remove in a keyboard-driven terminal UI.

```
 depclean  ~/code                                                    ? help
 14 projects · 9.8 GB reclaimable · 3 selected (4.1 GB)       ⠼ sizing 2 folders
      PROJECT                       FOLDERS                       SIZE ↓  MODIFIED
▸ [x] old-dashboard                 node_modules, .next           2.3 GB      1.4y
▾ [-] ml-experiments                .venv, __pycache__ ×12        1.9 GB       8mo
      [x] .venv                     python venv                   1.8 GB
      [ ] src/__pycache__           python cache                  2.1 MB
▸ [ ] side-project                  node_modules ×3               1.2 GB       12d
▸ [ ] rust-cli                      target                      780 MB        3mo
 ↑↓ move  space select  a all  enter expand  s sort:size  / filter  d delete  q quit
```

- **Safe by design**: it only matches a folder when a marker proves what it is (for example, `target` only next to a `Cargo.toml`, and a virtualenv only if it contains `pyvenv.cfg`). It never follows symlinks, never touches `.git`, and checks each folder again right before deleting it.
- **Fast**: it shows results right away and measures sizes in the background, updating them live.
- **Honest sizes**: it reports real disk usage. Files hard-linked elsewhere (such as pnpm's store) are left out of "reclaimable", because deleting them frees nothing.
- **Finds stale projects**: the MODIFIED column shows when you last touched a project's own files, ignoring dependency folders. Sort by age to see the forgotten ones first.
- **No dependencies**: it uses only the Python standard library (Python ≥ 3.9).

## Install

With [uv](https://docs.astral.sh/uv/):

```bash
uv tool install git+https://github.com/magnumnighthawk/depclean
```

or with [pipx](https://pipx.pypa.io/):

```bash
pipx install git+https://github.com/magnumnighthawk/depclean
```

Or try it without installing:

```bash
uvx --from git+https://github.com/magnumnighthawk/depclean depclean ~/code
```

It's tested on macOS and Linux. On Windows it should work through `windows-curses`, which is installed automatically, but that hasn't been tested.

## Usage

```bash
depclean                 # scan the current folder
depclean ~/code          # scan a specific folder
depclean --list ~/code   # just print a report, never deletes
depclean --json ~/code   # machine-readable report
```

| Option | |
|---|---|
| `-l, --list` | Print a report and exit. Never deletes. |
| `--json` | Report as JSON (implies `--list`). |
| `--no-size` | Skip size measurement. |
| `-d, --depth N` | How many folder levels to search (default 6). |
| `-x, --exclude GLOB` | Skip matching paths or folder names. Repeatable, e.g. `-x '*/keep-me' -x .tox`. |
| `--force-root` | Allow interactive mode on `/` or your home folder (refused by default). |

### Keys

| Key | Action |
|---|---|
| `↑ ↓` / `j k`, `PgUp PgDn`, `g G` | Move |
| `space` | Select a project (all its folders) or a single folder |
| `a` | Select / clear everything shown |
| `enter` / `→` | Expand a project to pick individual folders |
| `←` | Collapse |
| `s` | Sort by size, age (oldest first) or name |
| `/` | Filter by path or folder name |
| `d` | Delete the selection (asks for confirmation first) |
| `?` | Help |
| `q` / `esc` | Quit |

Deleting is **permanent** (not to the Trash), because a Trash full of `node_modules` frees no space. After deleting, `depclean` shows how much was freed and lists anything it couldn't remove.

## What it removes

| Kind | Folders | Only when | Get it back with |
|---|---|---|---|
| node | `node_modules` | always | `npm` / `pnpm` / `yarn` / `bun install` |
| python venv | any name (`.venv`, `venv`, `env`, …) | the folder contains `pyvenv.cfg` | recreate the venv, then `pip`/`uv`/`poetry install` |
| python cache | `__pycache__`, `.pytest_cache`, `.mypy_cache`, `.ruff_cache`, `.tox`, `.nox` | always | recreated automatically |
| build cache | `.next`, `.nuxt`, `.turbo`, `.parcel-cache`, `.svelte-kit`, `.angular` | always | next build / dev run |
| rust | `target` | next to `Cargo.toml` | `cargo build` |
| cocoapods | `Pods` | next to `Podfile` | `pod install` |
| gradle | `build`, `.gradle` | next to `build.gradle(.kts)` / `settings.gradle(.kts)` | `gradle build` |

It deliberately **does not** remove `dist`, `vendor`, `build` without Gradle, or `target` without Cargo, because those often hold things you can't regenerate.

Results are grouped by project: the nearest enclosing git repository, otherwise the nearest folder with a manifest (`package.json`, `pyproject.toml`, `Cargo.toml`, …).

## Development

```bash
git clone https://github.com/magnumnighthawk/depclean && cd depclean
uv run pytest
uv run ruff check
uv run depclean --list ~/code
```

### Releasing to PyPI

`.github/workflows/release.yml` publishes to PyPI with [trusted publishing](https://docs.pypi.org/trusted-publishers/) whenever a `v*` tag is pushed. One-time setup: on PyPI, add a pending publisher for project `depclean`, repository `magnumnighthawk/depclean`, workflow `release.yml`, environment `pypi`. Then:

```bash
git tag v0.1.0 && git push --tags
```

## License

MIT
