add runtime empty-directory cleanup command

This commit is contained in:
Jason Woltje
2026-02-17 12:12:58 -06:00
parent db2ec9524f
commit 947d4e808d
2 changed files with 155 additions and 0 deletions

View File

@@ -73,6 +73,14 @@ Prune migrated legacy backups from runtime folders (dry-run by default):
~/.mosaic/bin/mosaic-prune-legacy-runtime --runtime claude --apply
```
Clean empty legacy runtime directories:
```bash
~/.mosaic/bin/mosaic-clean-runtime --runtime claude
~/.mosaic/bin/mosaic-clean-runtime --runtime claude --apply
~/.mosaic/bin/mosaic-clean-runtime --runtime claude --all-empty --apply
```
Audit runtime drift:
```bash

147
bin/mosaic-clean-runtime Executable file
View File

@@ -0,0 +1,147 @@
#!/usr/bin/env bash
set -euo pipefail
RUNTIME="claude"
APPLY=0
ALL_EMPTY=0
usage() {
cat <<USAGE
Usage: $(basename "$0") [options]
Remove empty runtime directories created by migration/drift.
Default mode only checks managed legacy surfaces. Use --all-empty for broader cleanup.
Options:
--runtime <name> Runtime to clean (default: claude)
--all-empty Scan all runtime directories (except protected paths)
--apply Perform deletions (default: dry-run)
-h, --help Show help
USAGE
}
while [[ $# -gt 0 ]]; do
case "$1" in
--runtime)
[[ $# -lt 2 ]] && { echo "Missing value for --runtime" >&2; exit 1; }
RUNTIME="$2"
shift 2
;;
--all-empty)
ALL_EMPTY=1
shift
;;
--apply)
APPLY=1
shift
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown argument: $1" >&2
usage >&2
exit 1
;;
esac
done
case "$RUNTIME" in
claude)
TARGET_ROOT="$HOME/.claude"
managed_roots=(
"$HOME/.claude/agent-guides"
"$HOME/.claude/scripts"
"$HOME/.claude/templates"
"$HOME/.claude/presets"
"$HOME/.claude/skills"
"$HOME/.claude/agents"
"$HOME/.claude/agents.bak"
)
protected_roots=(
"$HOME/.claude/.git"
"$HOME/.claude/debug"
"$HOME/.claude/file-history"
"$HOME/.claude/projects"
"$HOME/.claude/session-env"
"$HOME/.claude/tasks"
"$HOME/.claude/todos"
"$HOME/.claude/plugins"
"$HOME/.claude/statsig"
"$HOME/.claude/logs"
"$HOME/.claude/shell-snapshots"
"$HOME/.claude/paste-cache"
"$HOME/.claude/plans"
"$HOME/.claude/ide"
"$HOME/.claude/cache"
)
;;
*)
echo "Unsupported runtime: $RUNTIME" >&2
exit 1
;;
esac
[[ -d "$TARGET_ROOT" ]] || { echo "[mosaic-clean] Runtime dir missing: $TARGET_ROOT" >&2; exit 1; }
is_protected() {
local path="$1"
for p in "${protected_roots[@]}"; do
[[ -e "$p" ]] || continue
case "$path" in
"$p"|"$p"/*)
return 0
;;
esac
done
return 1
}
collect_empty_dirs() {
if [[ $ALL_EMPTY -eq 1 ]]; then
find "$TARGET_ROOT" -depth -type d -empty
else
for r in "${managed_roots[@]}"; do
[[ -d "$r" ]] || continue
find "$r" -depth -type d -empty
done
fi
}
count_candidates=0
count_deletable=0
while IFS= read -r d; do
[[ -n "$d" ]] || continue
count_candidates=$((count_candidates + 1))
# Never remove runtime root.
[[ "$d" == "$TARGET_ROOT" ]] && continue
if is_protected "$d"; then
continue
fi
count_deletable=$((count_deletable + 1))
if [[ $APPLY -eq 1 ]]; then
rmdir "$d" 2>/dev/null || true
if [[ ! -d "$d" ]]; then
echo "[mosaic-clean] deleted: $d"
fi
else
echo "[mosaic-clean] would delete: $d"
fi
done < <(collect_empty_dirs | sort -u)
mode="managed"
[[ $ALL_EMPTY -eq 1 ]] && mode="all-empty"
if [[ $APPLY -eq 1 ]]; then
echo "[mosaic-clean] complete: mode=$mode deleted_or_attempted=$count_deletable candidates=$count_candidates runtime=$RUNTIME"
else
echo "[mosaic-clean] dry-run: mode=$mode deletable=$count_deletable candidates=$count_candidates runtime=$RUNTIME"
echo "[mosaic-clean] re-run with --apply to delete"
fi