Skip to content

Commit 8ed010b

Browse files
lroolleclaude
andcommitted
feat(skills): add deva-clean workspace cleanup skill
- map bind mounts from .deva + /proc/mounts before touching anything - locate disk pressure: host mount vs container overlay (shared-VM runtimes make overlay df lie) - git-triage local dirs: worktree type, dirty state, pushed test via branch -r --contains HEAD (upstream/ahead is a trap), ref freshness - tiered dry-run report; deletion is a second, approved pass using git worktree remove + prune, never rm -rf on linked worktrees Close #401 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 25d21b6 commit 8ed010b

2 files changed

Lines changed: 127 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
- `skills/deva-clean`: Claude Code skill for mount-safe workspace disk
12+
cleanup. Maps bind mounts from `.deva` + /proc/mounts, git-triages
13+
local worktrees for unpushed/uncommitted work, and reports a tiered
14+
dry-run plan before any deletion (#401)
15+
1016
## [0.13.0] - 2026-07-07
1117

1218
### Changed

skills/deva-clean/SKILL.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
---
2+
name: deva-clean
3+
description: Disk cleanup for deva container workspaces. Maps bind mounts from .deva and /proc/mounts, classifies local dirs vs mounted volumes, git-triages worktrees for unpushed or uncommitted work, then reports a tiered dry-run plan before deleting anything. Use when the user says "clean up the workspace", "disk cleanup", "free up disk", "remove stale worktrees", or when disk fills up inside a deva workspace. Deleting a single known build dir needs no skill - just delete it.
4+
---
5+
6+
# deva-clean: mount-safe workspace disk cleanup
7+
8+
A deva workspace mixes two kinds of directories that look identical in ls:
9+
10+
- bind mounts declared in `.deva` (live host repos - untouchable)
11+
- local dirs created during work (git worktrees, node_modules, package
12+
stores - reclaimable)
13+
14+
rm -rf on the wrong one destroys a live repo on the host. And since the
15+
workspace root is itself a host mount, "local" dirs still consume host
16+
disk - usually the disk that is actually full.
17+
18+
Three rules, no exceptions:
19+
20+
1. Mounts are untouchable. Build the mount map first; everything in it
21+
is off limits.
22+
2. Report before delete. The first pass produces a plan with sizes and
23+
exact commands. Deletion is a second, explicitly approved pass.
24+
3. No worktree dies with unpushed work. Triage git state per directory;
25+
anything unpushed or uncommitted goes to the keep list.
26+
27+
## Process
28+
29+
### 1. Map the mounts
30+
31+
Read `.deva` for declared VOLUME=host:container[:mode] lines, but treat
32+
/proc/mounts as ground truth - .deva has comments and drift:
33+
34+
grep "$(pwd)" /proc/mounts | awk '{print $2}' | sort
35+
36+
The workspace root itself is usually a mount. Everything under it that
37+
is NOT a separate mount is a local dir living on host disk.
38+
39+
### 2. Find where the pressure is
40+
41+
df -h / "$(pwd)"
42+
43+
Container overlay numbers can lie: on shared-VM runtimes (OrbStack and
44+
friends) overlay df reports the whole VM disk. Get container-local truth
45+
with:
46+
47+
sudo du -xh -d1 / | sort -rh | head
48+
49+
If the host mount is the full one, cleanup targets the workspace. If the
50+
overlay is genuinely full, target container caches (~/.cache, package
51+
stores) instead - not the workspace.
52+
53+
### 3. Inventory candidates
54+
55+
Candidates = dirs under the workspace that are not in the mount map.
56+
Typical: trees from `git worktree add`, one-off clones, node_modules,
57+
.pnpm-store, stray files from shell typos ("&1").
58+
59+
du -sh <candidates> | sort -rh
60+
61+
### 4. Git triage per candidate
62+
63+
For each candidate, in order:
64+
65+
- Type: `.git` file -> linked worktree; `.git` dir -> standalone repo;
66+
neither -> plain dir.
67+
- Dirty: `git status --porcelain`. Untracked generated files (lockfiles,
68+
test output) are lower risk than modified source.
69+
- Pushed: `git branch -r --contains HEAD`. Empty output means the
70+
commits exist nowhere but this machine.
71+
Trap: do NOT use upstream config or ahead/behind as the pushed test.
72+
A branch created off origin/develop tracks origin/develop and shows
73+
"ahead 1" - looks fine, but the branch itself was never pushed.
74+
- Freshness: remote refs are only as new as the last fetch. Check
75+
`stat .git/FETCH_HEAD` on the parent repo and flag stale refs in the
76+
report.
77+
- Inside keepers: node_modules, dist, coverage and friends are
78+
separately prunable - offer cache-prune as the low-risk alternative
79+
to deletion.
80+
81+
Also run `git worktree list` on each parent repo: entries marked
82+
"prunable" are stale metadata from already-deleted trees.
83+
84+
### 5. Tiered dry-run report
85+
86+
Produce the plan, then stop for approval:
87+
88+
- Tier 1 (safe): clean + pushed. List with sizes and exact commands.
89+
- Tier 2 (confirm): pushed branch, dirty only with generated artifacts.
90+
Offer full removal and cache-prune-only variants.
91+
- Keep: unpushed commits or uncommitted diffs. Say exactly what would
92+
be lost and suggest pushing the branches.
93+
- Totals: reclaim per tier.
94+
95+
### 6. Execute approved tiers only
96+
97+
- Linked worktrees: `git -C <parent> worktree remove <path>`. It
98+
refuses when the tree is dirty - that refusal is the safety net, so
99+
never --force and never plain rm -rf (rm leaves stale metadata).
100+
- Stale metadata: `git -C <parent> worktree prune`.
101+
- Standalone repos (pushed, clean) and plain dirs: rm -rf.
102+
- Verify: df -h again; `git worktree list` shows no leftovers.
103+
104+
Safe by construction: `git worktree remove` deletes the checkout only.
105+
Branch refs live in the parent repo's .git and survive, so committed
106+
work is never lost - only uncommitted files can be. The metadata edit
107+
inside the parent's .git is normal bookkeeping, not "touching the
108+
mount".
109+
110+
## Anti-patterns
111+
112+
- rm -rf on a linked worktree. Leaves .git/worktrees metadata dangling;
113+
use git worktree remove + prune.
114+
- Trusting upstream/ahead as proof a branch is pushed. Use
115+
`git branch -r --contains HEAD`.
116+
- Trusting .deva over /proc/mounts. Config drifts; the kernel does not.
117+
- Trusting overlay df inside shared-VM container runtimes.
118+
- Deleting anything on the first pass. The dry-run report is the
119+
deliverable; deletion needs explicit approval.
120+
- Cleaning container temp dirs when the full disk is the host mount
121+
(or vice versa). Find the pressure first.

0 commit comments

Comments
 (0)