git-stash-janitor
Made by JeffSafely mine a repo's accumulated git stashes for content worth landing on the primary branch. Use when a project has piled up stashes ("*N" in the prompt), "clean up my stashes", "stash archaeology", "are any of these stashes worth keeping", or when an agent swarm has left dozens of WIP stashes behind.
Works With
Quality and Utility come from canonical skill metadata. Community ratings appear separately as stars when subscriber reviews exist.
Subscribe to download
Public previews are available, but downloads, CLI sync, and premium content require an active subscription.
Sign In to Subscribe- Full skill content with deterministic versioning
- CLI sync + local search index
- Access to Jeffrey’s premium library
Save to your account
Saved skills live in your cloud list. Run jsm sync on each machine to pull them locally.
What's New with this Skill
4Updates, enhancements, and release notes for this skill.
Updated: git-stash-janitor, Phase 9 cleanup no longer halts after the first bucket
The cleanup plan drops stashes bucket by bucket, so a garbage stash with a low index is dropped before a superseded one with a higher index, which renumbers it. drop-confirmed.sh treated the inventory number as the live position and refused with 'stash list has shifted', so any cleanup spanning more than one bucket stopped partway. It now finds each stash by the SHA recorded in the inventory.
- Bucket-ordered Phase 9 cleanups no longer stop at the first shifted index
- drop-confirmed.sh finds each stash by its inventory SHA and logs the live position it dropped
- Stashes pushed by other agents during cleanup no longer halt the run
- New integration case reproduces the old failure; 44 of 44 pass
- Drop by identity.
drop-confirmed.shlooks up the stash whose commit matches the inventory SHA, drops it at its currentstash@{N}(recorded aspre_drop_indexincleanup_log.tsv), and prints a note when the position moved. It still refuses when that SHA is no longer in the stash list or the message does not match. - Concurrent agents. A stash another agent pushes on top during Phase 9 no longer halts the run.
- Regression test. The integration test now drops n=5 and then n=7, which has moved to
stash@{6}. The old script fails it; the suite passes 44 of 44.
Updated: git-stash-janitor, autostash entries are triaged instead of dropped
The skill treated rebase autostash entries as safe to drop because the reflog records the rebase. It does not record the stashed work: git leaves an autostash in the stash list exactly when reapplying it conflicted, so dropping it can lose uncommitted changes. Autostashes are now triaged like any WIP stash, and several git commands and cass flags were corrected.
- Autostash entries are uncommitted work git could not reapply; they are triaged, not dropped
- The reflog records the rebase, not the autostashed changes
- Untracked-only stashes explain most empty-looking stat output
- git stash list -p for diffs, :(glob) pathspecs for recursive matches
Checked against git 2.50.1 with scratch repositories; the integration test passes 41 of 41 before and after.
- Autostash. A
rebase (finish)reflog entry does not mean the autostash content reached the branch. The classifier prior and the recovery recipe (now thegit fsck --unreachablemethod from the git docs) were corrected, and the verdict comes from the fingerprint and verify-on-main checks. - Empty-looking stashes. The common cause is a stash of untracked files only, which
git stash show --statdoes not display.
Enhanced: git-stash-janitor, prior-run context that reflects what cass actually found
Phase 0.5 counted any JSON cass returned as a match, so zero-hit and timed-out searches were reported as prior runs, while a failed search was skipped. It now separates real matches, complete zero-hit answers, and searches that never completed.
- Zero-hit and timed-out searches are no longer reported as matches from prior runs.
- A search that never completed is marked as such instead of being skipped.
- When nothing matched but some searches failed, the summary reads UNKNOWN, not clean.
- Each hit is written on its own line instead of a truncated JSON dump.
- Searches are bounded by cass's time budget and an outer timeout where one is available.
The cass miner writes cass_findings.md, which later phases use when deciding how to treat stashes. It treated any non-empty cass output as a finding, so a project with no history read "2 queries returned matches", and so did a cass that had timed out. A search that failed outright was skipped, which could leave "Skill-state: clean" on a project that was never checked.
git-stash-janitor: triage 127 stashes left behind by an agent swarm without losing the one fix worth keeping
An agent swarm leaves 127 stashes behind: a mix of autostashes, dead experiments, defensive fixes that never landed, and the occasional novel WIP that solves a real bug. git stash clear would throw the good ones away, and reading them all by hand doesn't scale. This skill triages the pile down to nothing, byte-for-byte reversibly, landing keepers as focused commits on a recovery branch and recording verbatim authorization for every drop.
- Axiom 0: a stash is a merge commit, not a normal commit. git stash show -p --binary <inventory-sha> plus <inventory-sha>^3 for untracked files is the authoritative diff. git format-patch -1 stash@{N} is the footgun everyone hits first and can emit empty or wrong patches.
- Recovery bundle outside the repo: <project-parent>/<basename>-stash-archive-<DATE>/ holds per-stash diffs, meta, index, and untracked-file payloads, and backup refs go under .git/refs/stash-backup/. Phase 3 verifies byte-equality before any destructive logic runs.
- Axiom 5: drop highest-index first within each verdict bucket; stash@{N} shifts after every drop, so any other order drops the wrong stash.
- Five-verdict taxonomy with evidence requirements: superseded, garbage, novel, partially-novel, novel-but-stale. Each citation references a concrete file:line or grep-empty proof.
- Phase 7 partial-split: novel hunks only, applied via git apply --3way against a copy of the bundle's diff. It's the most error-prone phase and gets its own subagent so it doesn't compete with Phase 6's working tree.
- Per-apply quality gates: cargo test, bun tsc --noEmit, pytest, etc. run after every Phase 6 apply, not at the end, so errors can't compound across keepers.
- Axiom 10: verbatim authorization recorded in cleanup_authorization.txt. If the file doesn't exist, the destructive action did not happen.
- Axiom 8: git stash pop and git stash apply are forbidden because they mutate state on conflict and dirty the working tree. The skill operates on the bundle's diff exclusively.
- Working-tree-drift discipline (Axiom 7): per AGENTS.md, never stash, revert, or overwrite changes from concurrent agents. Re-snapshot git status before each Phase 6 apply.
- 11-phase loop with three hard gates (Phase 3 byte-equality, Phase 5 user sign-off, Phase 9 verbatim auth). 23 scripts, 18 subagents, 30+ reference files including FAILURE-MODES, ANTI-PATTERNS, RECOVERY-RECIPES, and the asupersync WORKED-EXAMPLES.
The motivating session. A user looked at their zsh prompt, saw *127, and thought it meant 127 commits ahead. It was 127 stashes, accumulated across many concurrent agent sessions and mixed with autostashes and abandoned experiments. Mining them by hand was hopeless; mass-dropping them meant accepting that some real fix would silently disappear. The skill came out of that session.