Skip to main content

A CLI utility that audits your local machine for stuff you forgot and helps you safely reclaim disk space.

Project description

bekas

CI Coverage PyPI Python License

One audit. Every kind of forgotten thing. Nothing deleted you cared about.

bekas is a CLI utility that audits your local machine for stuff you forgot — orphaned Docker images, unused git branches, old node_modules, dangling Python venvs, stale downloads, screenshots from years ago — and helps you safely reclaim disk space. Every destructive action is logged to an SQLite undo log and can be quarantined before permanent deletion.


Table of Contents


Installation

Recommended — use pipx or uv so bekas stays isolated from your project dependencies:

# pipx (most systems)
pipx install bekas

# uv (fastest)
uv tool install bekas

# Or plain pip if you prefer
pip install bekas

Requires Python 3.11+. Some plugins (e.g. Docker, Git) require the corresponding CLI tools to be installed.


Quick Start

# 1. Run a read-only audit
bekas audit

# 2. Preview what a clean would remove
bekas plan

# 3. Clean only the safest items (dry-run by default)
bekas clean --apply --safe-only

Commands

bekas audit

Runs a read-only scan across all available plugins and prints candidates.

bekas audit                    # Human-readable output
bekas audit --json             # JSON output
bekas audit --plugin docker    # Only run Docker plugins
bekas audit --serial           # Run plugins one at a time
bekas audit --sort-by size     # Sort by reclaimable size
bekas audit --top 20           # Only show top 20 candidates

bekas plan

Converts the latest audit into a concrete removal plan. You can save a cryptographically signed plan to disk and apply it later.

bekas plan                     # Preview in human-readable format
bekas plan --safe-only         # Only SAFE-tier items
bekas plan --include-review    # Include REVIEW-tier items
bekas plan --save plan.json    # Save a signed plan file

bekas clean

The only command that actually removes files. Dry-run by default — you must pass --apply to delete anything.

bekas clean --apply --safe-only              # Auto-clean safe items
bekas clean --apply --yes-all                # Skip per-category prompts
bekas clean --apply --non-interactive \
               --accept-categories "docker,tmp" # CI-friendly mode
bekas clean --apply --plan-file plan.json    # Apply a signed plan

Options:

  • --apply — Actually delete (default is dry-run).
  • --safe-only — Only include SAFE-tier candidates.
  • --review / --include-review — Include REVIEW-tier candidates.
  • --yes-all — Skip all interactive prompts.
  • --non-interactive — Requires --accept-categories.
  • --accept-categories — Comma-separated category whitelist for non-interactive runs.
  • --plan-file — Apply a previously saved signed plan instead of re-auditing.

bekas inspect <id>

Show full metadata and reasoning for a single candidate.

bekas inspect ss:Screenshot_2023-01-01.png

bekas history / bekas undo

Every clean --apply is persisted to an SQLite undo log.

bekas history                  # List past runs
bekas history <run_id>         # Show details for one run
bekas undo                     # Undo the most recent run
bekas undo <run_id>            # Undo a specific run

bekas quarantine

Files removed by plugins that support quarantine are moved to a quarantine directory instead of being permanently deleted. You can restore them later.

bekas quarantine list                     # Show quarantined items
bekas quarantine restore <quarantine_id>  # Restore to original path
bekas quarantine purge                    # Empty quarantine immediately

bekas plugins

List, enable, or disable plugins.

bekas plugins list             # Show installed plugins and availability
bekas plugins enable  <name>   # Enable a plugin (edit config)
bekas plugins disable <name>   # Disable a plugin (edit config)

bekas config

Print the current effective configuration.

bekas config
bekas config show --resolved   # Effective config after profile merge
bekas config validate            # Validate config file syntax

bekas doctor

Diagnose the bekas runtime environment.

bekas doctor                   # Human-readable health check
bekas doctor --json            # Machine-readable output
bekas doctor --skip docker     # Skip specific checks

bekas tui

Launch an interactive Textual TUI for browsing candidates and cleaning interactively.

bekas tui

Plugins

Plugin Category What it finds Needs docker / git
docker.images docker.image.* Dangling & unused Docker images docker
docker.containers docker.container.* Stopped containers docker
docker.buildx.cache docker.buildx.cache Stale BuildKit cache entries docker
python.venvs python.venv Orphaned Python virtual environments
python.cache python.cache __pycache__, .pytest_cache, .mypy_cache, .ruff_cache
pip.cache pip.cache Stale pip download & wheel cache
node.modules node.modules Old node_modules directories
rust.target rust.target Old target/ build directories
git.branches git.branch Fully-merged local branches git
downloads downloads.file Old files in ~/Downloads
screenshots screenshots.file Old screenshots (Desktop / Pictures)
system.tmp system.tmp Old temp files owned by you
system.trash system.trash macOS/Linux Trash contents
dotfiles.backups dotfiles.backups Backup files like .zshrc.bak
xcode.derived_data xcode.derived_data Stale Xcode build caches (macOS only)

Plugins are auto-discovered via entry_points in pyproject.toml. You can write your own by subclassing bekas.plugin.Plugin.


Configuration

The first time you run bekas, a config file is created at the platform-specific config directory (e.g. ~/.config/bekas/config.yaml on Linux).

Example:

version: "0.2.0"
active_profile: default
profiles:
  default:
    enabled_plugins: ["*"]
    quarantine_enabled: true
    # Per-plugin thresholds
    plugin_settings:
      screenshots:
        min_age_days: 90
      downloads:
        min_age_days: 180
      python.venvs:
        min_idle_days: 90
      node.modules:
        min_idle_days: 180
      rust.target:
        min_idle_days: 60
      python.cache:
        min_idle_days: 90
      system.tmp:
        min_age_days: 30
      git.branches:
        min_idle_days: 90
    # Repositories for the git.branches plugin
    git_repos:
      - ~/code
      - ~/projects

Safety Model

  • Read-only by defaultaudit and plan never touch the filesystem.
  • Dry-run by defaultclean without --apply only prints a preview.
  • Confidence tiers — Every candidate is classified as:
    • SAFE — Low risk (e.g. dangling Docker image, no project nearby).
    • REVIEW — Medium risk (e.g. old download, active project but untouched).
    • MANUAL — High risk (e.g. branch on an active project, container in use).
  • Hard exclusions — System paths and sensitive files are automatically excluded. See docs/SAFETY.md for the full exclusion list.
  • Quarantine — Deletable files are moved to a quarantine folder so you can restore them later.
  • Undo log — Every clean --apply is recorded in SQLite with per-candidate results and undo tokens.
  • Signed plans — Saved plan files include a signature to detect tampering.
  • Typed confirmation gateclean --apply requires typing "yes" (or a random token for high-risk plans) before any destructive action.

Architecture & File Structure

bekas/
├── src/bekas/
│   ├── __init__.py              # Package metadata
│   ├── cli.py                   # Click CLI entry point & commands
│   ├── models.py                # Core dataclasses: Candidate, Plan, AuditReport, etc.
│   ├── plugin.py                # Plugin base class & discovery
│   ├── runner.py                # Orchestrates plugin discovery and runs audits
│   ├── clean.py                 # Applies a Plan: removes, quarantines, logs
│   ├── safety.py                # Exclusion engine (path safety, pattern matching)
│   ├── config.py                # Config file I/O and profile resolution
│   ├── formatters.py            # Human, JSON, Markdown output formatters
│   ├── database.py              # SQLite undo log & quarantine registry
│   ├── quarantine.py            # Move / restore / purge quarantined items
│   ├── signing.py               # Plan file signing & verification
│   ├── doctor.py                # Runtime environment diagnostics
│   ├── locking.py               # Single-instance process lock
│   ├── tui.py                   # Textual interactive UI
│   └── plugins/
│       ├── docker_images.py     # Dangling & unused Docker images
│       ├── docker_containers.py # Stopped containers
│       ├── docker_buildx.py     # BuildKit cache
│       ├── python_venvs.py      # Orphaned Python virtual environments
│       ├── python_cache.py      # __pycache__ & tool caches
│       ├── pip_cache.py         # pip download & wheel cache
│       ├── node_modules.py      # Old node_modules directories
│       ├── rust_target.py       # Rust target/ directories
│       ├── git_branches.py      # Stale merged local branches
│       ├── downloads.py         # Old files in ~/Downloads
│       ├── screenshots.py       # Old screenshots
│       ├── system_tmp.py        # Old temp files
│       ├── system_trash.py      # macOS/Linux Trash
│       ├── dotfiles_backups.py  # Dotfile backup files
│       └── xcode_derived_data.py # Xcode DerivedData (macOS)
├── tests/                       # pytest test suite (80%+ coverage)
├── pyproject.toml               # Project metadata, deps, entry points
└── README.md                    # This file

Key design principles

  • Plugin architecture — Every scanner is a Plugin. The runner loads them via entry_points, so third-party plugins can be added without touching core code.
  • Context-driven — Each plugin receives a Context carrying the active profile, dry-run flag, and verbosity. This keeps plugins stateless and testable.
  • Immutable modelsCandidate, Plan, RemovalResult, etc. are dataclasses with strong typing. AuditReport uses Pydantic for JSON serialization.
  • Quarantine-first — Plugins that support quarantine (supports_quarantine() -> True) move items to a staging area instead of deleting immediately. The CLI controls whether quarantine is enabled per profile.
  • Audit traildatabase.py writes every run to an SQLite DB; undo reads it back and restores quarantined items via their undo tokens.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

bekas-0.3.0.tar.gz (84.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

bekas-0.3.0-py3-none-any.whl (75.6 kB view details)

Uploaded Python 3

File details

Details for the file bekas-0.3.0.tar.gz.

File metadata

  • Download URL: bekas-0.3.0.tar.gz
  • Upload date:
  • Size: 84.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for bekas-0.3.0.tar.gz
Algorithm Hash digest
SHA256 c99b5603fb8fde3d638b97042b4fdddd164588e74ffb05e4c3a2ffaeb8aa9ad8
MD5 708594d6013abf7c00cb1bc9a22b1674
BLAKE2b-256 765b0a4fb4a4376907d4cf05d84adb6c79790f9b1ac862066a73b6ffbafc37df

See more details on using hashes here.

File details

Details for the file bekas-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: bekas-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 75.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for bekas-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e190349487bd05867fe78222340d170428c64364c99f0e8ec3ca990e3a3d2001
MD5 802f5c3b7005420b2478078d5ac7dd7a
BLAKE2b-256 1a2e369e460abec7e8ff996bea1561a0195b43da0894df987cf3dce264278fbd

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page