#!/usr/bin/env bash set -euo pipefail RUNTIME="claude" APPLY=0 ALL_EMPTY=0 usage() { cat < 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