# Roadmap
<p align="center">
<img src="../assets/github-readme-banner.png" alt="dev-prune — gigabytes back, nothing you can't rebuild" width="800" />
</p>
Everything else in `docs/` describes what the code does **now** — that rule is in
[`CLAUDE.md`](../CLAUDE.md) and it holds. This page is the one sanctioned exception: the
directions that have been considered, so they are not re-litigated from scratch every
time someone has the idea again, and so nobody mistakes a parked idea for a shipped
feature.
Nothing here is a promise and nothing here is a date. What it does say is **which group
something is in**, because "we have not built that" and "we are not going to build that"
each cover several very different situations, and flattening them into one list is how a
settled decision gets re-argued every quarter.
| [**In flight**](#in-flight) | Built, and waiting on somebody outside this repository. |
| [**Next**](#next) | Agreed direction, understood work, nothing in the way but time. |
| [**Standing orders**](#standing-orders) | Permanently open. Not a queue but a recipe, plus an invitation to use it. |
| [**On request**](#on-request) | Understood, and deliberately unbuilt until one person asks for it by name. |
| [**Waiting on a shape**](#waiting-on-a-shape) | Wanted. The design is not right yet, and getting it wrong here is expensive. |
| [**Not planned**](#not-planned) | Declined, grouped by *why* — the reasons are not interchangeable, and the reason is what says whether it can ever be reopened. |
An item leaves this page when it ships — [`CHANGELOG.md`](../CHANGELOG.md) is the record
of what happened, not this file. If you are looking for something that used to be here,
that is where it went.
---
## In flight
Work that is built and waiting on a party that is not this repository. Nothing here is
blocked on code, and nothing here blocks a release: every one of these channels reports
`skipped` and the others publish without it.
- **Icon-theme pull requests** for the `.devprune.json` file icon, awaiting maintainer
review upstream:
[material-icon-theme#3567](https://github.com/material-extensions/vscode-material-icon-theme/pull/3567)
and [vscode-icons#4223](https://github.com/vscode-icons/vscode-icons/pull/4223). Icon
themes always win over an extension's own contribution, so this is the only route.
- **Switching on the WinGet submission.** The release job that raises the `winget-pkgs`
pull request is built and gated the same way npm is: it reports `skipped` until the
`WINGET_PUBLISH` variable and the `WINGET_TOKEN` secret both exist. What is still
outstanding is a person signing Microsoft's CLA once and the first submission
([microsoft/winget-pkgs#422809](https://github.com/microsoft/winget-pkgs/pull/422809))
being merged, because until that identifier is in the catalog there is nothing for
later versions to be a new version *of*. See [`RELEASING.md`](RELEASING.md).
- **homebrew-core.** Plain `brew install dev-prune`, with no tap prefix. The audit weighs
GitHub stars, forks and watchers against a bar commonly cited as roughly 75, so it opens
when the project is popular enough and not before. Nothing to build — the named tap
already covers the same install — which is what makes it a waiting item rather than a
work item.
## Next
Decided, understood, and not yet written. Nothing is in it.
The two items that stood here through 1.8.0 — an install receipt beside the managed
binary, and the interactive half of the install flow — were the second half of the
install work 1.8.0 split deliberately, the guarantees first and the convenience after.
Both shipped in 1.9.0. Nothing has moved up to replace them, and an empty queue is not a
promise that it stays empty: this is where a direction lands once it is settled, and the
groups below are where directions come from. [**On request**](#on-request) is waiting on
one person to ask by name; [**Waiting on a shape**](#waiting-on-a-shape) is waiting on a
design that is right.
## Standing orders
Permanently open. These do not complete and they do not get scheduled; the work is
written down, and the answer to "will you add X" is yes, here is the recipe.
- **More adapters.** The trait, registration and test recipe are in
[`ADDING_ADAPTERS.md`](ADDING_ADAPTERS.md), and the opt-in mechanism (`opt_in()`,
`enable_*` settings, `build_idle_days`) already exists for anything whose deletion
costs a recompile rather than a download. Twenty-three ship as of 1.9.0, and the obvious
ecosystems are covered — what is left is the awkward one. **Nix**: `result` symlinks
are already refused for being symlinks, and a real adapter would have to reason about
the store, which is a different kind of problem from "a lockfile says this comes back".
Beyond that this is a standing invitation rather than a queue: name the manager and the
recipe is written down.
- **More `--agent` targets** as editors standardise their rules files. Sixteen ship
today; adding the seventeenth is four small changes documented in
[`IDE_INTEGRATION.md`](IDE_INTEGRATION.md), and an editor that adopts the `AGENTS.md`
convention needs no code at all.
## On request
Understood well enough to start, small enough not to argue about, and left unbuilt
because nobody has asked. One person asking by name moves any of these to **Next** —
that is the whole entry requirement, and the reason each is written down rather than
merely thought about.
- **Chocolatey.** Moderated review on every release, and unlike WinGet nothing is
generated for it today, so it is a packaging format to write as well as a queue to wait
in. Worth it only if Windows users ask for it by name.
- **JetBrains plugin publishing.** The icon micro-plugin in `editors/jetbrains/` builds;
the marketplace listing is the remaining step. It needs a JDK and downloads the IntelliJ
platform on first build, so it is deliberately outside the repository gate.
- **Man page packaging.** `devp man --dir` generates the pages; installing them pre-placed
is a per-channel packaging question (deb, rpm, a Homebrew formula) rather than a CLI
one, so it arrives with those channels or not at all.
## Waiting on a shape
Wanted, and stuck on design rather than effort. The distinction matters: everything above
could be started this afternoon by someone who decided to. These cannot, because the
first workable-looking version of each is worse than not having it, and the cost of
finding that out in production is measured in somebody's deleted work.
- **A user-declared prune list, as the only sane route to build outputs.** Parked
deliberately, the way shared caches were parked before `devp caches` found the right
shape. The tempting version — teach adapters to recognise `dist/`, `.next/`, `.plasmo/`
and delete them under a framework marker — fails on a case that is common rather than
exotic: a built browser extension loaded unpacked from `dist/` is *in use* while the
source beside it is mid-refactor and does not currently build. The directory is not
reproducible from the tree that produced it, and recovering it from git history is not
a sane instruction to give anyone. That is the opposite of the property every tier-one
adapter has, where the lockfile makes the rebuild certain.
So the guess is the wrong mechanism. What would work is declaration: the user, or the
agent reading the repository, names the directories that are safe to prune — in
`.devprune.json`, per repository, with the rebuild command recorded alongside. Nobody
has to infer whether `dist/` is disposable, because whoever knows has said so. It needs
a config key, a schema entry, and a decision about whether a declared directory is
exempt from lockfile verification or merely has a different proof; none of that is
urgent, and the failure mode of getting it wrong is unrecoverable data loss. Revisit
when the shape is obvious, not before.
## Not planned
Declined, so they stay declined — but not all for the same reason, and the reason is the
part that matters. A thing declined because it would break a promise cannot be reopened
by anyone; a thing declined because the case has not appeared reopens the day it does.
The four headings below say which is which.
### Because it would break a promise the tool makes
There is no version of these that is compatible with what dev-prune tells people it is.
Reopening one means changing the promise on the front page first, and the promise is the
product.
- **Build outputs and gitignore-driven deletion** — `dist/`, `.next/`, `.nuxt/`, and any
rule of the form "delete what git ignores". No lockfile or manifest can prove an output
directory is reproducible, and "delete whatever is gitignored" deletes `.env` files.
This is the boundary the whole tool is built around, not a gap in it. Gradle's `build/`
and Maven's `target/` are not an exception to it: they are opt-in, off by default, and
gated behind their own longer idle window precisely because they are rebuilt rather
than re-downloaded. The one route that could ever work is declaration, not detection —
see [Waiting on a shape](#waiting-on-a-shape).
- **A bypass flag for any safety invariant.** The seven in
[`SAFETY_INVARIANTS.md`](SAFETY_INVARIANTS.md) — the `.git` boundary, lockfile
pre-verification, symlink refusal, atomic state writes and the rest — have no escape
hatch and must not acquire one. A flag that turns off the proof turns dev-prune into
`rm -rf` with extra steps, and the proof is the entire product.
- **Clearing a cache from anything that runs on its own** — a prune pass, the scheduler,
a Git hook. A cache is shared by every project on the machine, so no single lockfile
can prove it recoverable, which is the bar every automatic deletion in dev-prune has to
clear. `devp caches clear <manager>` exists and empties one after asking, but only ever
because you typed it.
- **Accounts, cloud sync, telemetry, a subscription tier, ads, or a web dashboard.**
Proposed as growth surface. dev-prune is a local binary that deletes local directories:
it has no server, sends nothing anywhere, and nothing about reclaiming disk space needs
a login. Any of them would mean the tool learns which repositories exist on your machine
— precisely the thing a disk-cleaning utility has no business knowing off your disk.
- **One channel overwriting another channel's binary.** This is narrower than it sounds,
and it is worth stating precisely because the obvious reading is wrong. `devp update`
always updates the copy that is running: it works out which channel installed that copy
and runs *that* channel's upgrade command — `uv tool upgrade dev-prune` for a uv
install, `npm install -g dev-prune@latest` for an npm one, a fresh download for the
installer's managed copy. Nothing is refused and nothing needs a second tool.
What is declined is having one channel write over a file another channel owns. The
binary is not the whole install: uv, pipx, npm and cargo each keep a manifest saying
which version they put there. Overwrite the file behind their back and the manifest
still says the old version — so the next `uv tool upgrade` reports "already up to date"
and, on the next reinstall, quietly puts the old binary back. The file would be new and
the package manager's record of it would be a lie. Delegating to the owning channel
keeps the file and the record saying the same thing.
### Because it would be a claim nobody could check
Each of these is a number or a label that reads as evidence and is not. The objection is
not modesty; it is that a reader has no way to verify any of them, and every one would go
on saying the same thing on the day it became false.
- **A trust score, a letter grade, or a "Trust level: HIGH" badge.** Suggested for both
`devp trust` and the site. A grade is a summary that replaces the evidence it
summarises: it reads as a rating the tool awarded itself, a reader cannot check it, and
it would go on saying HIGH on the day a bug made it false. So `devp trust` prints the
guarantees and the actual state of *this* machine instead — which schedulers are
registered, whether the Git hook is installed, what the config currently permits —
because every line of that is something you can go and verify. Nothing is condensed
into a number.
- **A live "your projects are wasting 47.3 GB" figure on the homepage.** The site cannot
see your disk, so a number there is either invented or somebody's average presented as
your situation. `devp status` computes the real one, on your machine, in seconds. A
landing page that fabricates the number in order to sell the tool that measures it
honestly is arguing against itself.
- **Download counts, star counts, "trusted by N developers", or any adoption badge.**
Proposed for the README and the site. Every one of them would be a number nobody is
actually counting: registries report installs rather than people, a download badge
inflates on CI reruns and therefore measures CI, and Open VSX publishes downloads —
labelling that figure "users" would simply be false. If this project ever states
something about its own adoption, it will be a number the reader can look up at the
source that publishes it.
- **"AI-powered cleanup."** There is no judgement call in a prune pass for a model to
make. A lockfile either proves a directory is recoverable or it does not, and the check
is the same check every time. Selling that deterministic test as AI would be a lie
about the one thing the tool is for.
### Because it is a different tool's job
These are real needs. They are not *this* binary's needs, and the test is the same one
every time: does a lockfile prove it comes back? If any of them ever gets built, it is a
separate tool with a separate promise on its own front page — never a mode of `devp`,
because a command whose guarantee holds for some of its arguments has no guarantee.
- **Deleting Docker images, volumes or build cache.** Nothing about them is
lockfile-recoverable. An image layer may be irreproducible the moment an upstream tag
moves, and a volume is data, not a cache. `docker system prune` exists, is well
understood, and its consequences are the user's to accept. Reporting what is there was
always a different question, and it shipped in 1.9.0: `devp caches docker` sizes it,
says what the engine calls reclaimable, and prints those commands for you to run. There
is no flag that makes dev-prune run one, and there will not be.
- **Growing into a general "developer storage manager"** — Xcode DerivedData, simulator
runtimes, Android SDK images, browser and IDE caches, the Downloads folder. The review
framed it as the obvious next market. The problem is that dev-prune's whole claim is
that it deletes nothing it cannot prove comes back from a lockfile, and none of those
have one. Shipping them under the same command would mean the promise on the front page
no longer covered everything the command does, which is a worse outcome than not
shipping them. Machine-wide stores that *are* package-manager caches are reported —
never deleted on a schedule — by `devp caches`, and that is the boundary.
- **A desktop GUI.** The interactive parts of dev-prune are terminal views sitting next
to the terminal work they belong to, and the VS Code extension already covers "I want a
button" inside the editor that is open anyway. A standalone window would be a second
application to install, sign, update and support, serving a moment that already has an
answer in both places people are already sitting.
### Because the case for it has never appeared
The door is not bolted. Each of these names the specific thing that would reopen it, and
that thing has not happened — which is different from the entries above, where nothing
could.
- **A unified `devp analyze` storage view** across repositories and shared caches. Asked
again in 1.4.0 and parked again, for the same reason: `devp status` already ranks the
repositories and `devp caches` already ranks the machine-wide stores, both largest first
with totals. A third command that adds the two lists together would be a new name, a new
`--json` shape and a third place for the numbers to disagree, in exchange for a sum a
reader can do. **Reopens when** somebody shows a question the two existing reports
genuinely cannot answer.
- **32-bit builds beyond Windows** — 32-bit Linux, 32-bit macOS or 32-bit ARM anywhere.
`i686-pc-windows-msvc` ships as of 1.4.0 because it is a plain rustup target on a runner
that already exists and Windows is the one place 32-bit hardware is still in service.
The others are not the same trade: macOS has been unable to run a 32-bit binary since
Catalina, `i686-unknown-linux-musl` needs a cross musl toolchain to serve a desktop
population that has effectively gone, and 32-bit ARM has no runner at all. Nothing in
the source is 64-bit-only, so `cargo install dev-prune` on any of those toolchains works
today. **Reopens when** a hosted runner and a real population exist for one of them at
the same time.