148 lines
3.2 KiB
Bash
Executable File
148 lines
3.2 KiB
Bash
Executable File
#!/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
|