Skip to main content

safesweep

CI crates.io PyPI npm license

Clean caches and temp files that are provably safe to delete, and see what is eating your CPU.

Most cleaners guess. safesweep only touches known cache locations, and before deleting anything it proves no running process is using it. On WSL, it also tells you how to give the freed space back to Windows.

Linux and WSL only.

safesweep scan, clean and why

The demo runs against a throwaway home directory with a faked clock; demo/make.sh rebuilds it.

Install

Any one of these:

cargo install safesweep                     # build from source
cargo binstall safesweep                    # prebuilt binary via cargo-binstall
brew install dpologdvinity/tap/safesweep    # Homebrew on Linux
pipx install safesweep                      # or: uv tool install safesweep
npm install -g safesweep                    # or: pnpm add -g / yarn global add / bun add -g
nix run github:dpologdvinity/safesweep      # Nix flake

On Debian or Ubuntu (including WSL), add the apt repository once and get updates with apt upgrade:

sudo install -d -m 0755 /etc/apt/keyrings
curl -fsSL https://dpologdvinity.github.io/safesweep/key.asc | sudo tee /etc/apt/keyrings/safesweep.asc >/dev/null
echo "deb [signed-by=/etc/apt/keyrings/safesweep.asc] https://dpologdvinity.github.io/safesweep stable main" | sudo tee /etc/apt/sources.list.d/safesweep.list
sudo apt update && sudo apt install safesweep

Debian/Ubuntu and Fedora packages, and static Linux binaries for x86_64 and aarch64 (each with a .sha256), are on the releases page:

sudo apt install ./safesweep_*_amd64.deb
sudo dnf install ./safesweep-*.x86_64.rpm

Usage

safesweep                     # list candidates and why each is or isn't safe (same as `safesweep scan`)
safesweep scan --json         # same, as JSON
safesweep clean               # dry run: what would be removed
safesweep clean --apply       # remove it
safesweep --category pip clean --apply   # limit to one rule (repeatable)
safesweep why                 # what is using the CPU right now
safesweep tui                 # browse, select and delete interactively
safesweep completions bash    # also zsh, fish, elvish, powershell

The .deb, .rpm and release tarballs include the man page and bash/zsh/fish completions. After cargo install, load completions with e.g. safesweep completions bash > ~/.local/share/bash-completion/completions/safesweep.

tui: browse and delete interactively

safesweep tui shows the same candidates as scan, biggest first, with a size bar. Safe rows can be selected; skipped rows are greyed out with the reason and cannot be selected.

Key Action
j / k or arrows move
space select or unselect the row
a / n select all safe rows / none
d or enter delete the selection (asks for y to confirm)
q or esc quit

Deleting runs the same guarded path as clean --apply: every candidate is re-checked right before removal and logged to the manifest. The list is rescanned afterwards. --category works here too.

why: what is eating the CPU

safesweep why samples /proc for one second (--interval-ms, at least 100) and prints how busy the machine is, CPU per program (summed across its processes, so twelve rustc jobs show as one line), and the busiest processes with their command lines. Percentages are per core, like top.

On WSL, only Linux processes are visible. If Task Manager shows vmmem busy but why is quiet, the load is on the Windows side.

Every removal is logged to ~/.local/state/safesweep/<unix-time>.log. If that log cannot be written, nothing is deleted.

Exit codes: 0 success, 1 some removals failed or a runtime error (unreadable /proc, terminal failure, unwritable manifest), 2 bad usage (unknown category, unsafe HOME or TMPDIR).

What makes a candidate safe

A candidate is skipped, with the reason shown, if any of these hold:

  • it is a symlink, or resolves outside its rule's directory
  • anything inside is owned by another user (unless run as root)
  • anything inside changed within the rule's age threshold (newer of mtime and ctime, so a tree unpacked with tar or cp -p today counts as new)
  • it contains a socket or FIFO (live tmux or X11 sockets don't show up as open files)
  • anything inside is open, memory-mapped, executing, or the working directory of one of your processes, including processes in containers or other mount namespaces (matched by inode, not by path). Other users' processes are only visible when run as root
  • it contains a .git directory (an editable install or git dependency can hold uncommitted work)
  • any part of it can't be read, it contains a read-only directory (deletion would stop halfway), or it is or contains a mount point
  • /proc can't be read, so open files can't be checked
  • it is in the temp dir and safesweep runs as root (services with a private /tmp and containers can't be checked from outside)

TMPDIR must be an absolute path that is not / and does not contain your home directory; otherwise safesweep refuses to run.

These checks run when scanning and again right before each deletion.

Reported sizes count only files with a single hard link. uv hardlinks its cache into virtualenvs, so deleting those files would free nothing, and safesweep doesn't pretend it would.

Rules

Category Location Minimum age Cost to regenerate
xdg-cache each entry in ~/.cache except pip and uv 7 days network
pip ~/.cache/pip 1 day network
uv ~/.cache/uv 1 day network
npm ~/.npm/_cacache 1 day network
cargo ~/.cargo/registry/cache 1 day network
tmp each entry in $TMPDIR (or /tmp) 7 days free
pycache every __pycache__ under ~ (opt-in: --category pycache) none free
artifacts node_modules, target, .venv in idle git projects (opt-in: --category artifacts) 30 days slow

pycache and artifacts walk your home directory (skipping dot-directories), so they only run when named.

artifacts only lists a build directory when its project has a marker file next to it (package.json for node_modules, Cargo.toml for target, pyproject.toml, requirements.txt or setup.py for .venv) and its git history is idle: nothing touched .git/logs/HEAD or .git/index for 30 days. Git activity includes other branches and linked worktrees. The build directory must also contain a file only the tool creates (pyvenv.cfg, CACHEDIR.TAG, .package-lock.json, ...), so a hand-made folder named target is never touched. The project directory must be unused by any of your processes, not just the build directory, so a long-running service keeps its dependencies. Projects without .git and monorepo members without their own .git are never listed. Every normal safety check still applies, so a build directory used in the last 30 days is skipped even if nothing was committed.

WSL

Deleting files inside WSL doesn't shrink the ext4.vhdx disk image on Windows. After clean --apply frees space on WSL, safesweep prints the steps to compact it: export a backup, wsl --shutdown, then Optimize-VHD or diskpart. It never runs them for you.

It does not recommend wsl --manage --set-sparse. WSL disabled sparse VHDs over data-corruption reports, and a sparse disk can't be compacted with Optimize-VHD.

License

Licensed under either of Apache License 2.0 or MIT license, at your option.

Metadata

Release files for safesweep 0.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for safesweep 0.6.0
File Size Uploaded Provenance
safesweep-0.6.0.tar.gz 59.0 kB Without provenance Details

Built distributions (wheels)

Table of built distributions (wheels) for safesweep 0.6.0
File
safesweep-0.6.0-py3-none-musllinux_1_2_x86_64.whl Python 3 none Linux musl 1.2+ x86-64 Details
safesweep-0.6.0-py3-none-musllinux_1_2_aarch64.whl Python 3 none Linux musl 1.2+ ARM64 Details
safesweep-0.6.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl Python 3 none Linux glibc 2.17+ x86-64 Details
safesweep-0.6.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl Python 3 none Linux glibc 2.17+ ARM64 Details

Total release size: 4.2 MB

Release files / safesweep-0.6.0.tar.gz

Download URL safesweep-0.6.0.tar.gz
Size 59.0 kB
Tags Source
SHA-256 checksum
How to use checksums
09cdb2a0ee73a206f1d4ec590538c0d863e8af7bdd1346bb51f45790ac9ef234
BLAKE2b-256 checksum
How to use checksums
f0a3966f92a38becb3320e6f64e973a15229467f570b191b90c7140ebf275b8b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release files / safesweep-0.6.0-py3-none-musllinux_1_2_x86_64.whl

Download URL safesweep-0.6.0-py3-none-musllinux_1_2_x86_64.whl
Size 1.1 MB
Tags Linux musl 1.2+ x86-64 Python 3
SHA-256 checksum
How to use checksums
f565fd8d2ca3a410644afdd6770f4f771369ac806ad5b8f0f4b100b16e82ac1d
BLAKE2b-256 checksum
How to use checksums
0c522ef5acf4c5d6580153919521945aa68799511546bf256dfa49978e290989
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release files / safesweep-0.6.0-py3-none-musllinux_1_2_aarch64.whl

Download URL safesweep-0.6.0-py3-none-musllinux_1_2_aarch64.whl
Size 1.1 MB
Tags Linux musl 1.2+ ARM64 Python 3
SHA-256 checksum
How to use checksums
9670b7b300edd3e525afbf21501bb77b6bb703cfa77c92c9d2b4ed373659ce36
BLAKE2b-256 checksum
How to use checksums
5a212bf4cc6cd6c3ee3e4ae7b3d995791c6b58b66ef123e6859c5002d3023677
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release files / safesweep-0.6.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL safesweep-0.6.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 1.0 MB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
8c506b237454643978390ed26c0efec4e80c8da8660a55060436a9b551373198
BLAKE2b-256 checksum
How to use checksums
bc976cbbf3e77642a6edd4f77a6d10cd67615e5d76571c409ded67eec1045a9e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release files / safesweep-0.6.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL safesweep-0.6.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 991.6 kB
Tags Linux glibc 2.17+ ARM64 Python 3
SHA-256 checksum
How to use checksums
b9755bfedd4cb03f5ebe5e81c0805e2716db3b351640d901d0b8cbd95d753d83
BLAKE2b-256 checksum
How to use checksums
da3d31140e416ea19774d217698190e131d0aa82a64661a908c56782034e176b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Security for safesweep 0.6.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.6.0 This release

5 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page