Releases: Life-Experimentalist/dev-prune
Releases · Life-Experimentalist/dev-prune
Release list
v1.0.0
First public release. dev-prune reclaims disk space from idle Git repositories by
deleting dependency and build directories that a lockfile can rebuild — and refuses to
delete anything it cannot prove is recoverable.
Pruning engine
- Lockfile-verified deletion. No directory is removed until its package manager has
confirmed a usable lockfile. Verification cannot be bypassed by any flag or setting. - Read-only verification, everywhere. Every adapter proves the lockfile can rebuild
the tree without writing to it —npm ci --dry-run,pnpm install --lockfile-only --frozen-lockfile,yarn install --immutable,uv lock --locked,cargo metadata --locked,go mod download. A lockfile that has drifted from its manifest is a
refusal, not something to quietly fix: a pass can be started by the OS scheduler, and
it must never leave a modified tracked file behind. The writing form runs in exactly
two cases — no lockfile exists at all, orallow_manifest_rewriteis set, which is
the informed opt-in and now means the same thing in every ecosystem. - Two enforcement tiers. With the manager installed, it resolves the manifest against
the lockfile. With the manager missing but a lockfile on disk, the lockfile is itself
the proof anddevp restorecan rebuild later. With neither, nothing is deleted. command_timeout_secsbounds every verification. Each package-manager command runs
under the configured ceiling (600s by default) and a hang fails the check rather than
the pass.- Idle detection. A repository is a candidate only after
idle_dayswith no commit
and no source modification.--ignore-idlelifts that threshold and nothing else. - Inverse selection.
run --except <repos>prunes everything but the named
repositories, so "clean up but keep the API project" does not mean pruning it and
downloading it back. - Adapter and size filters.
--only,--skipand--min-size, withmin_size_mb
as the persistent form of the last. - Symlink refusal. A symlinked or junctioned bloat directory points at storage the
repository does not own and is never deleted. - Per-directory selection. The interactive selector prunes exactly the directories
left ticked, and starts with every candidate ticked so keeping one is a single
keystroke. - Dry run.
--dry-runreports every candidate and its size without running a
package manager or touching disk. - Machine-readable output.
--jsononrunandstatusemits one document on
stdout and nothing else; every diagnostic goes to stderr, so the output is parseable
even when something went wrong.
Multi-ecosystem repositories
- Eight adapters: npm, pnpm, yarn, bun, uv, pip/venv, cargo, go.
- Any number of managers per repository. A repository may hold uv, npm and cargo in
its root, spread them acrossfrontend/,services/api/andtools/cli/, or mix
both. Every project is discovered, verified and pruned on its own terms, and each
directory is reported by its repository-relative path. - Bounded discovery. The walk descends
scan_depthlevels — six by default,
configurable globally and per repository — and never entersnode_modules,
target,vendor, virtual environments, hidden directories, or nested repositories —
a submodule is pruned as itself, never as part of its parent. - Single owner per directory. When npm, pnpm, yarn or bun all claim the same
node_modules, one is chosen: thepackageManagerfield ofpackage.json, else the
manager whose bookkeeping files are inside the installed tree, else the most recently
written lockfile. uv takes precedence over plain venv for the Python environment. - Virtual environments by marker, not by name. Any directory containing
pyvenv.cfgis recognised, whatever it is called.
Safety
- Deletion is refused when the lockfile is missing, unparseable, or — for
requirements.txt— lists no packages, because the tree could not be rebuilt. ignore.devprune.jsonin a repository root opts it out with a single file-existence
check, before any config is parsed..devprune.jsonholds only inert data: ignore flags, a display name, daemon/hook
opt-outs, and per-repository overrides for the same numeric and boolean settings the
global config takes. There is no key that names a command, a path to execute, or a
binary to run — nothing in a repository-tracked file can cause command execution, which
matters because these files arrive with agit clone.- A
.devprune.jsonthat cannot be parsed skips the repository and reports the syntax
error, rather than falling back to defaults — the unreadable file may have been the
one saying"ignore": true. - Nothing dev-prune installs edits an editor's settings, a shell startup file, or the
system PATH outside the installer scripts.devp config iconregisters the file type
with the OS file manager and prints an editor snippet for you to paste. - A run that fails any repository exits non-zero. Exit codes are
0success,1
failure,2unusable arguments.
Commands
init,link,unlink,undo— register repositories, individually or by scanning
a tree.unlink --missingclears every entry whose directory no longer exists in one
pass, which is what a registry accumulates from deleted clones and moved workspaces.run [PATH]— prune every registered repository, or one target.status— an interactive dashboard of every registered repository, its state,
reclaimable space and last activity, withito ignore andpto prune.
status daemonandstatus hookreport the background integrations.caches [--json]— the answer to "where did my disk actually go?". Finds every
package manager cache and store on the machine — npm, pnpm, yarn, bun, uv, pip, cargo's
registry, Go's module and build caches — sizes each one, orders them largest first, and
prints the command that clears it. Each manager is asked where its cache lives rather
than assumed, so aCARGO_HOMEor a corporate.npmrcis followed; a manager you have
since uninstalled still has its leftover cache reported. It deletes nothing, and no
flag makes it. A cache is shared by every project on the machine, so no single
lockfile can prove it recoverable — and it is what makesrestorefast. Run
devp cacheswhen you want the number, and the clear command yourself when you want
the space more than the speed.restore [PATH] [--last-run]— reinstall dependencies for every project in a tree.
--last-runrestores exactly what the most recent prune pass deleted, wherever those
projects were, so an over-eager pass is one command to undo.doctor [PATH]— a read-only diagnosis. Without a path it checks the installation:
the binary and its PATH entry, the registry and every setting in it, the integrations —
including the binary the scheduler and the hooks will actually run, so one left pointing
at a deleted directory is reported rather than silently doing nothing forever — which
package managers are actually reachable, and the release-check state. With a path
it checks one repository and names the reason a prune pass would skip it. It runs no
package manager and repairs nothing, so it can be run twice to see whether a fix
worked. Warnings exit0; only genuine breakage exits1.config— global settings (get,set,show, and awizardthat walks through
every one of them), per-repository.devprune.json, the OS scheduler, Git hooks, and
the file-manager icon for*.devprune.json.update [--offline]— reports the installed version, asks GitHub's public API for the
latest release, and prints the upgrade command for how it was installed.skill— exportsSKILL.mdfor AI coding assistants.setup [--status]— installs any missing integration;--statusreports without
changing anything.uninstall [--deep]— removes the scheduler and hooks;--deepadditionally clears
configuration after confirming the number of repositories affected.-V— version plus an environment audit: OS, architecture, config path, binary
directory, and PATH activation.- Shorthands.
devp hook,devp daemonanddevp iconreach theconfig
subcommands of the same name, andinstall/uninstall/on/offare accepted
whereverenable/disableare. A misspelled action is rejected instead of quietly
printing status. - Paths.
.means the current directory and is the default wherever a path is
optional. A leading~is expanded by dev-prune itself, not by the shell, so
devp init ~/Codebehaves the same in bash, PowerShell and cmd, quoted or not.
Background automation
A pruner that has to be remembered is a pruner that never runs, so the integrations
install themselves — at install time, and again on the first command after an upgrade if
anything is missing. devp setup is that pass, run by hand; it installs only what is
absent and reports what it declined to touch.
- OS scheduler —
schtaskson Windows, a LaunchAgent on macOS, a systemd user timer
on Linux, each running at the configuredcheck_interval_daysinterval. Scheduled
passes are non-interactive and skip repositories that setdisable_daemon. - Durable paths. The scheduler entry and the hook scripts both outlive the process
that wrote them, so both record the binary in<config>/binrather than wherever the
command happened to be run from. Installing throughnpx dev-pruneoruvx dev-prune
would otherwise register a path inside a cache the package manager deletes, and neither
a scheduled task nor a Git hook has anywhere to report that it has stopped working. - Git hooks —
post-commit,post-checkoutandpost-mergeauto-register the
repository you are working in. Git allows one globalcore.hooksPathand no chaining,
so when husky, pre-commit o...