Skip to content

Instantly share code, notes, and snippets.

@robertdevore
Last active September 1, 2026 01:43
Show Gist options
  • Select an option

  • Save robertdevore/817b627800d90450eee6bdb8657b6cf4 to your computer and use it in GitHub Desktop.

Select an option

Save robertdevore/817b627800d90450eee6bdb8657b6cf4 to your computer and use it in GitHub Desktop.

Great Delete

A macOS-focused cleanup script for developers who accumulate a ridiculous amount of rebuildable cruft.

Great Delete scans your home directory for things like:

  • Rust target/ directories
  • node_modules/
  • .next
  • .nuxt
  • .turbo
  • .vite
  • .parcel-cache
  • coverage output
  • npm / pnpm / Yarn / Bun caches
  • Cargo registry and Git caches
  • Rustup downloads
  • Python / pip / uv caches
  • Go build caches
  • Gradle caches
  • Xcode DerivedData
  • Playwright browsers
  • Homebrew leftovers
  • Codex runtime/cache data
  • TypeScript caches
  • unused Docker images, containers, networks, and build cache

It also reports other potentially large areas without blindly deleting them, including Docker volumes, Rust toolchains, Git worktrees, Xcode archives, managed application runtimes, and individual files larger than 1 GB.

Important

This script is intentionally focused on rebuildable development artifacts and caches.

It does not intentionally delete:

  • source repositories
  • Git history
  • Cargo.toml
  • Cargo.lock
  • package.json
  • lockfiles
  • Docker volumes
  • Xcode Archives
  • Maven repositories
  • Rust toolchains
  • Git worktrees themselves
  • application-managed runtime directories

Project build artifacts may need to be regenerated afterward.

For example:

cargo build

or:

npm install

or:

pnpm install

Install

Download or copy great-delete.sh, then make it executable:

chmod +x great-delete.sh

You can optionally move it somewhere on your PATH:

mkdir -p ~/.local/bin
mv great-delete.sh ~/.local/bin/great-delete

Then run it with:

great-delete

Dry Run

Great Delete defaults to a dry run.

./great-delete.sh --days 4 --aggressive --docker

Nothing is deleted.

The script reports what it finds, what would be deleted, what is being skipped, and where disk usage is concentrated.

This is a good first step before using --apply.

Recommended Cleanup

Delete rebuildable artifacts from projects that have not been active recently:

./great-delete.sh --days 4 --apply --aggressive --docker

This is a good general-purpose cleanup command for development machines that accumulate build output quickly.

14-Day Cleanup

For a more conservative cleanup:

./great-delete.sh --days 14 --apply --aggressive --docker

Maximum Project Cleanup

To ignore project age and remove all eligible rebuildable project artifacts:

./great-delete.sh --all --apply --aggressive --docker

--all does not disable every safeguard.

The script will still avoid project artifacts when it detects things such as:

  • uncommitted Git changes
  • an active process referencing the repository
  • an explicitly protected project

Maximum Cleanup Including macOS User Caches

./great-delete.sh --all --apply --aggressive --docker --user-caches

This additionally clears the contents of:

~/Library/Caches

Applications will recreate these caches as needed.

It is a good idea to close major applications before using this option.

Options

--days N

Only clean eligible project artifacts from repositories whose latest Git commit is older than N days.

Example:

./great-delete.sh --days 7 --apply

Default:

14 days

--all

Ignore project age.

./great-delete.sh --all --apply

Other safeguards remain active.

--apply

Actually perform deletions.

Without --apply, Great Delete is a dry run.

./great-delete.sh --apply

--aggressive

Also clean deeper reproducible development caches such as Cargo, Rustup, Gradle, Go module caches, and similar tooling data.

./great-delete.sh --days 4 --apply --aggressive

--docker

Run:

docker system prune -af

This removes unused Docker:

  • containers
  • images
  • networks
  • build cache

Docker volumes are intentionally not deleted.

./great-delete.sh --apply --docker

--user-caches

Clear the contents of:

~/Library/Caches

Example:

./great-delete.sh --apply --user-caches

--protect PATH

Explicitly protect a project from project-artifact cleanup.

Example:

./great-delete.sh \
  --days 4 \
  --protect ~/Code/important-project \
  --apply

You can provide --protect multiple times:

./great-delete.sh \
  --protect ~/Code/project-one \
  --protect ~/Code/project-two \
  --all \
  --apply

What Gets Deleted?

Typical project artifacts include:

target/
node_modules/
.next/
.nuxt/
.turbo/
.parcel-cache/
.vite/
.svelte-kit/
coverage/

These are normally generated from source code, manifests, and lockfiles.

For Rust projects, deleting:

target/

does not delete your Rust source code.

Cargo simply rebuilds it.

For Node projects, deleting:

node_modules/

does not delete your project.

Your package manager can reinstall dependencies from the project's manifest and lockfile.

Git Safety

Great Delete attempts to identify the Git repository that owns each build artifact.

By default, project artifacts may be skipped when:

  • the repository has uncommitted changes
  • the repository has recent commits according to --days
  • an active process appears to reference the repository
  • the repository is explicitly protected

This is intended to make aggressive cleanup practical without simply running recursive rm -rf commands across every development directory.

Rust

Rust build directories can become enormous.

It is not unusual for a heavily developed Rust repository to accumulate tens of gigabytes inside:

target/

Great Delete identifies and reports these separately.

It may also clean reproducible Cargo caches when --aggressive is enabled.

Installed Rust toolchains are reported but are not automatically removed.

Node.js

Great Delete can find and clean project-level:

node_modules/

It also cleans or prunes supported package-manager caches for tools such as:

npm
pnpm
Yarn
Bun

Application-managed node_modules directories belonging to software such as editors or desktop applications are intentionally treated more conservatively.

Docker

Docker can quietly consume tens or hundreds of gigabytes.

With:

--docker

Great Delete prunes unused Docker data.

It deliberately does not run:

docker volume prune

Volumes may contain databases or other persistent application data.

Inspect them manually with:

docker system df -v

Git Worktrees

Git worktrees can multiply development disk usage because every worktree may have its own:

target/
node_modules/
.next/

Great Delete reports worktree candidates and can clean eligible build artifacts inside them.

It does not automatically remove the worktrees themselves.

Inspect worktrees with:

git worktree list

Clean stale Git worktree metadata with:

git worktree prune

Remove an actual worktree manually with:

git worktree remove /path/to/worktree

Why This Exists

Modern development environments generate a surprising amount of disposable data.

A few large Rust repositories, multiple worktrees, Node projects, Docker builds, browser runtimes, package-manager caches, and AI development tools can consume huge amounts of disk space without making it obvious where the storage went.

Great Delete is meant to provide one repeatable cleanup command while still showing you what is consuming the space.

Example Workflow

Start with:

./great-delete.sh --days 4 --aggressive --docker

Review the report.

Then run:

./great-delete.sh --days 4 --apply --aggressive --docker

If you need to reclaim more:

./great-delete.sh --all --apply --aggressive --docker

And if you're seriously low on space:

./great-delete.sh \
  --all \
  --apply \
  --aggressive \
  --docker \
  --user-caches

Disclaimer

This script performs destructive filesystem operations when --apply is used.

Read the script, run a dry run first, and make sure you understand what will be removed.

Caches and build artifacts are generally reproducible, but deleting them may require dependencies, toolchains, browsers, or build outputs to be downloaded or rebuilt again.

Use it at your own risk.

#!/usr/bin/env bash
set -u
set -o pipefail
# ============================================================
# GREAT DELETE
# macOS developer-cruft auditor + cleaner
# ============================================================
#
# Default: DRY RUN.
#
# Safety model:
# - Never deletes source repositories.
# - Never deletes Docker volumes.
# - Never deletes Xcode Archives.
# - Never deletes Maven ~/.m2/repository.
# - Never auto-removes Rust toolchains.
# - Skips explicitly protected projects.
# - Skips Git repos with uncommitted work.
# - Skips Git repos with commits newer than --days.
# - Skips project artifacts if an active process appears to reference the repo.
#
# Examples:
# ./great-delete.sh
# ./great-delete.sh --days 4
# ./great-delete.sh --days 4 --apply --aggressive --docker
# ./great-delete.sh --all --apply --aggressive --docker
# ./great-delete.sh --days 4 --protect /path/to/project --apply
STALE_DAYS=14
APPLY=0
AGGRESSIVE=0
DOCKER=0
ALL_PROJECT_ARTIFACTS=0
CLEAR_USER_CACHES=0
# Nothing is protected by default.
# Add protection manually with:
# --protect /path/to/project
PROTECTED_PATHS=()
TOTAL_FOUND_KB=0
TOTAL_DELETED_KB=0
TOTAL_SKIPPED_KB=0
RUST_KB=0
NODE_KB=0
FRAMEWORK_KB=0
TOOLCACHE_KB=0
APP_CACHE_KB=0
usage() {
cat <<'USAGE'
Usage: great-delete.sh [options]
Options:
--days N Project is eligible only if its Git history is older than N
days and it has no uncommitted work. Default: 14.
--all Ignore project age, but STILL honor protected paths,
dirty Git repos, and active-process safeguards.
--protect PATH Never delete project artifacts inside PATH.
May be repeated.
--apply Actually delete.
Without this, the script is a dry run.
--aggressive Clear deeper reproducible language/tool caches.
--docker Prune unused Docker images, containers, networks,
and build cache.
Docker volumes are NEVER deleted.
--user-caches Also clear the CONTENTS of ~/Library/Caches.
More aggressive; applications will rebuild caches.
-h, --help Show help.
Recommended aggressive cleanup:
./great-delete.sh --days 4 --apply --aggressive --docker
Maximum project-artifact cleanup with safeguards:
./great-delete.sh --all --apply --aggressive --docker
Maximum cleanup including user caches:
./great-delete.sh --all --apply --aggressive --docker --user-caches
USAGE
}
while [ "$#" -gt 0 ]; do
case "$1" in
--days)
shift
[ "$#" -gt 0 ] || {
echo "Missing value for --days" >&2
exit 2
}
STALE_DAYS="$1"
;;
--all)
ALL_PROJECT_ARTIFACTS=1
;;
--protect)
shift
[ "$#" -gt 0 ] || {
echo "Missing value for --protect" >&2
exit 2
}
PROTECTED_PATHS+=("${1%/}")
;;
--apply)
APPLY=1
;;
--aggressive)
AGGRESSIVE=1
;;
--docker)
DOCKER=1
;;
--user-caches)
CLEAR_USER_CACHES=1
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown argument: $1" >&2
usage
exit 2
;;
esac
shift
done
case "$STALE_DAYS" in
''|*[!0-9]*)
echo "--days must be a non-negative integer." >&2
exit 2
;;
esac
# ============================================================
# KUJO BINARY SAFETY
# ============================================================
#
# If `kujo` itself resolves inside a repo target directory,
# automatically protect that repo.
#
# Example:
#
# /some/repo/target/release/kujo
#
# If Kujo instead resolves to:
#
# ~/.cargo/bin/kujo
# /usr/local/bin/kujo
# /opt/homebrew/bin/kujo
#
# then the repo target directory is not required to execute
# the installed Kujo binary.
KUJO_BIN=""
if command -v kujo >/dev/null 2>&1; then
KUJO_BIN="$(command -v kujo)"
case "$KUJO_BIN" in
*/target/*)
PROTECTED_PATHS+=("${KUJO_BIN%%/target/*}")
;;
esac
fi
NOW="$(date +%s)"
CUTOFF=$(( NOW - STALE_DAYS * 86400 ))
MODE="DRY RUN"
[ "$APPLY" -eq 1 ] && MODE="DELETE"
before_free_kb="$(
df -Pk "$HOME" |
awk 'NR==2 {print $4}'
)"
# ============================================================
# HELPERS
# ============================================================
human_kb() {
awk -v kb="${1:-0}" 'BEGIN {
bytes = kb * 1024
split("B KB MB GB TB", u, " ")
i = 1
while (bytes >= 1024 && i < 5) {
bytes /= 1024
i++
}
printf "%.1f %s", bytes, u[i]
}'
}
dir_kb() {
du -sk "$1" 2>/dev/null |
awk '{print $1+0}'
}
command_exists() {
command -v "$1" >/dev/null 2>&1
}
canonical_path() {
local p="$1"
if [ -d "$p" ]; then
(
cd "$p" 2>/dev/null &&
pwd -P
) || printf '%s\n' "$p"
else
printf '%s\n' "$p"
fi
}
is_protected() {
local path="$1"
local protected
for protected in "${PROTECTED_PATHS[@]}"; do
[ -n "$protected" ] || continue
protected="$(canonical_path "$protected")"
case "$path" in
"$protected"|"$protected"/*)
return 0
;;
esac
done
return 1
}
find_project_root() {
local start="$1"
local dir
local fallback
dir="$(dirname "$start")"
fallback=""
# Prefer an actual Git root.
if command_exists git; then
local gitroot
gitroot="$(
git -C "$dir" \
rev-parse \
--show-toplevel \
2>/dev/null || true
)"
if [ -n "$gitroot" ]; then
printf '%s\n' "$gitroot"
return 0
fi
fi
# Non-Git project fallback.
while [ "$dir" != "$HOME" ] &&
[ "$dir" != "/" ]; do
if [ -z "$fallback" ] && {
[ -f "$dir/Cargo.toml" ] ||
[ -f "$dir/package.json" ] ||
[ -f "$dir/pyproject.toml" ] ||
[ -f "$dir/go.mod" ]
}; then
fallback="$dir"
fi
dir="$(dirname "$dir")"
done
if [ -n "$fallback" ]; then
printf '%s\n' "$fallback"
else
printf '%s\n' "$(dirname "$start")"
fi
}
repo_has_uncommitted_work() {
local root="$1"
[ -e "$root/.git" ] || return 1
command_exists git || return 1
[
-n "$(
git -C "$root" \
status \
--porcelain \
--untracked-files=normal \
2>/dev/null
)"
]
}
repo_is_recent() {
local root="$1"
local ts
# --all bypasses age only.
[ "$ALL_PROJECT_ARTIFACTS" -eq 1 ] &&
return 1
[ -e "$root/.git" ] ||
return 1
command_exists git ||
return 1
ts="$(
git -C "$root" \
log \
-1 \
--format=%ct \
2>/dev/null || true
)"
[ -n "$ts" ] ||
return 1
[ "$ts" -ge "$CUTOFF" ]
}
repo_has_active_process() {
local root="$1"
# Heuristic safety check:
# if an active process command references the repository
# path, leave its project artifacts alone.
ps ax -o command= 2>/dev/null |
grep -F -- "$root" |
grep -v -F -- "great-delete.sh" \
>/dev/null 2>&1
}
category_for_path() {
local path="$1"
case "$path" in
*/target)
echo "RUST"
;;
*/node_modules)
echo "NODE"
;;
*/.next|*/.nuxt|*/.turbo|*/.parcel-cache|*/.vite|*/.svelte-kit|*/coverage)
echo "FRAMEWORK"
;;
*)
echo "PROJECT"
;;
esac
}
add_category_kb() {
local category="$1"
local kb="$2"
case "$category" in
RUST)
RUST_KB=$(( RUST_KB + kb ))
;;
NODE)
NODE_KB=$(( NODE_KB + kb ))
;;
FRAMEWORK)
FRAMEWORK_KB=$(( FRAMEWORK_KB + kb ))
;;
TOOLCACHE)
TOOLCACHE_KB=$(( TOOLCACHE_KB + kb ))
;;
APPCACHE)
APP_CACHE_KB=$(( APP_CACHE_KB + kb ))
;;
esac
}
# ============================================================
# PROJECT ARTIFACT REPORT / DELETE
# ============================================================
report_project_artifact() {
local path="$1"
local root
local category
local kb
local reason
path="$(canonical_path "$path")"
root="$(find_project_root "$path")"
root="$(canonical_path "$root")"
category="$(category_for_path "$path")"
kb="$(dir_kb "$path")"
[ -n "$kb" ] ||
kb=0
TOTAL_FOUND_KB=$(( TOTAL_FOUND_KB + kb ))
add_category_kb "$category" "$kb"
reason=""
if is_protected "$path" ||
is_protected "$root"; then
reason="PROTECTED"
elif repo_has_uncommitted_work "$root"; then
reason="DIRTY GIT"
elif repo_has_active_process "$root"; then
reason="ACTIVE"
elif repo_is_recent "$root"; then
reason="RECENT"
fi
if [ -n "$reason" ]; then
TOTAL_SKIPPED_KB=$(( TOTAL_SKIPPED_KB + kb ))
printf ' %-10s %-10s %10s %s\n' \
"$category" \
"$reason" \
"$(human_kb "$kb")" \
"$path"
return 0
fi
printf ' %-10s %-10s %10s %s\n' \
"$category" \
"$MODE" \
"$(human_kb "$kb")" \
"$path"
if [ "$APPLY" -eq 1 ]; then
rm -rf -- "$path"
if [ ! -e "$path" ]; then
TOTAL_DELETED_KB=$(( TOTAL_DELETED_KB + kb ))
fi
fi
}
# ============================================================
# CACHE REPORT / DELETE
# ============================================================
report_cache_path() {
local label="$1"
local path="$2"
local kb
[ -e "$path" ] ||
return 0
kb="$(dir_kb "$path")"
[ -n "$kb" ] ||
kb=0
TOTAL_FOUND_KB=$(( TOTAL_FOUND_KB + kb ))
TOOLCACHE_KB=$(( TOOLCACHE_KB + kb ))
printf ' %-12s %-10s %10s %s\n' \
"$label" \
"$MODE" \
"$(human_kb "$kb")" \
"$path"
if [ "$APPLY" -eq 1 ]; then
rm -rf -- "$path"
if [ ! -e "$path" ]; then
TOTAL_DELETED_KB=$(( TOTAL_DELETED_KB + kb ))
fi
fi
}
run_cleanup() {
local label="$1"
shift
printf '\n[%s]\n' "$label"
if [ "$APPLY" -eq 0 ]; then
printf ' DRY RUN command:'
printf ' %q' "$@"
printf '\n'
return 0
fi
"$@" ||
printf \
' warning: cleanup command exited non-zero\n' \
>&2
}
# ============================================================
# HEADER
# ============================================================
print_header() {
echo "============================================================"
echo " GREAT DELETE"
echo " Mode: $MODE"
if [ "$ALL_PROJECT_ARTIFACTS" -eq 1 ]; then
echo " Project cutoff: ALL ages (safeguards still active)"
else
echo " Project cutoff: older than $STALE_DAYS days"
fi
echo " Aggressive caches: $AGGRESSIVE"
echo " Docker prune: $DOCKER"
echo " User caches: $CLEAR_USER_CACHES"
echo " Home: $HOME"
if [ -n "$KUJO_BIN" ]; then
echo " Kujo binary: $KUJO_BIN"
fi
echo "============================================================"
if [ "${#PROTECTED_PATHS[@]}" -gt 0 ]; then
echo
echo "Protected paths:"
local p
for p in "${PROTECTED_PATHS[@]}"; do
echo " $p"
done
else
echo
echo "Protected paths: none"
fi
echo
}
print_header
if [ "$APPLY" -eq 0 ]; then
echo "Nothing will be deleted in this run."
echo
fi
# ============================================================
# 1. PROJECT BUILD / DEPENDENCY ARTIFACTS
# ============================================================
echo "[Project artifacts]"
printf ' %-10s %-10s %10s %s\n' \
"CATEGORY" \
"ACTION" \
"SIZE" \
"PATH"
while IFS= read -r -d '' path; do
report_project_artifact "$path"
done < <(
find "$HOME" \
\( \
-path "$HOME/Library" -o \
-path "$HOME/.Trash" -o \
-path "$HOME/.cargo" -o \
-path "$HOME/.rustup" -o \
-path "$HOME/.npm" -o \
-path "$HOME/.pnpm-store" -o \
-path "$HOME/.cache" -o \
-path "$HOME/.gradle" -o \
-path "$HOME/.m2" -o \
-path "$HOME/.hermes" -o \
-path "$HOME/.pi" -o \
-path "$HOME/.pi-lens" -o \
-path "$HOME/.codex" -o \
-path "$HOME/.vscode" -o \
-path "$HOME/.nvm" \
\) \
-prune -o \
-type d \
\( \
-name node_modules -o \
-name target -o \
-name .next -o \
-name .nuxt -o \
-name .turbo -o \
-name .parcel-cache -o \
-name .vite -o \
-name .svelte-kit -o \
-name coverage \
\) \
-prune \
-print0 \
2>/dev/null
)
# ============================================================
# 2. KNOWN REPRODUCIBLE TOOL CACHES
# ============================================================
echo
echo "[Known reproducible caches]"
printf ' %-12s %-10s %10s %s\n' \
"CATEGORY" \
"ACTION" \
"SIZE" \
"PATH"
report_cache_path \
"TSCACHE" \
"$HOME/Library/Caches/typescript"
report_cache_path \
"NPXTEMP" \
"$HOME/.npm/_npx"
report_cache_path \
"COREPACK" \
"$HOME/.cache/node/corepack"
report_cache_path \
"CODEXTMP" \
"$HOME/.codex/.tmp"
report_cache_path \
"CODEXCACHE" \
"$HOME/.codex/plugins/cache"
report_cache_path \
"CODEXRUNTIME" \
"$HOME/.cache/codex-runtimes"
report_cache_path \
"PLAYWRIGHT" \
"$HOME/Library/Caches/ms-playwright"
report_cache_path \
"PLAYWRIGHT" \
"$HOME/.cache/ms-playwright"
report_cache_path \
"XCODE" \
"$HOME/Library/Developer/Xcode/DerivedData"
if [ "$AGGRESSIVE" -eq 1 ]; then
report_cache_path \
"CARGO" \
"$HOME/.cargo/registry/cache"
report_cache_path \
"CARGO" \
"$HOME/.cargo/registry/src"
report_cache_path \
"CARGO" \
"$HOME/.cargo/git/checkouts"
report_cache_path \
"CARGO" \
"$HOME/.cargo/git/db"
report_cache_path \
"RUSTUP" \
"$HOME/.rustup/downloads"
report_cache_path \
"RUSTUP" \
"$HOME/.rustup/tmp"
report_cache_path \
"GRADLE" \
"$HOME/.gradle/caches"
report_cache_path \
"GRADLE" \
"$HOME/.gradle/daemon"
fi
# ============================================================
# 3. PACKAGE MANAGER CLEANUPS
# ============================================================
if command_exists npm; then
run_cleanup \
"npm cache" \
npm cache clean --force
fi
if command_exists pnpm; then
run_cleanup \
"pnpm store" \
pnpm store prune
fi
if command_exists yarn; then
run_cleanup \
"Yarn cache" \
yarn cache clean
fi
if command_exists bun; then
run_cleanup \
"Bun cache" \
bun pm cache rm
fi
if command_exists python3; then
run_cleanup \
"pip cache" \
python3 -m pip cache purge
fi
if command_exists uv; then
run_cleanup \
"uv cache" \
uv cache clean
fi
if command_exists go; then
run_cleanup \
"Go build/test cache" \
go clean -cache -testcache
if [ "$AGGRESSIVE" -eq 1 ]; then
run_cleanup \
"Go module cache" \
go clean -modcache
fi
fi
if command_exists composer; then
run_cleanup \
"Composer cache" \
composer clear-cache
fi
if command_exists brew; then
run_cleanup \
"Homebrew cleanup" \
brew cleanup -s
fi
if command_exists xcrun; then
run_cleanup \
"Unavailable iOS simulators" \
xcrun simctl delete unavailable
fi
# ============================================================
# 4. OPTIONAL USER APPLICATION CACHES
# ============================================================
if [ "$CLEAR_USER_CACHES" -eq 1 ] &&
[ -d "$HOME/Library/Caches" ]; then
echo
echo "[User application caches]"
cache_kb="$(dir_kb "$HOME/Library/Caches")"
[ -n "$cache_kb" ] ||
cache_kb=0
APP_CACHE_KB=$(( APP_CACHE_KB + cache_kb ))
echo \
" Current ~/Library/Caches size: $(human_kb "$cache_kb")"
echo \
" NOTE: applications will recreate these caches."
if [ "$APPLY" -eq 1 ]; then
find "$HOME/Library/Caches" \
-mindepth 1 \
-maxdepth 1 \
-exec rm -rf -- {} + \
2>/dev/null || true
else
echo \
" DRY RUN: would clear contents of ~/Library/Caches"
fi
fi
# ============================================================
# 5. DOCKER
# ============================================================
if [ "$DOCKER" -eq 1 ] &&
command_exists docker; then
echo
echo "[Docker before prune]"
docker system df 2>/dev/null || true
run_cleanup \
"Docker unused data" \
docker system prune -af
echo \
" Docker volumes were NOT touched."
echo \
" Inspect them separately with: docker system df -v"
else
echo
echo "[Docker]"
echo " skipped (use --docker)"
fi
# ============================================================
# 6. REPORT-ONLY AREAS
# ============================================================
echo
echo "[Report-only: intentionally NOT auto-deleted]"
# Rust toolchains
if [ -d "$HOME/.rustup/toolchains" ]; then
echo \
" Rust toolchains: $(human_kb "$(dir_kb "$HOME/.rustup/toolchains")")"
if command_exists rustup; then
rustup toolchain list 2>/dev/null |
sed 's/^/ /' || true
fi
fi
# Xcode archives
if [ -d "$HOME/Library/Developer/Xcode/Archives" ]; then
echo \
" Xcode Archives: $(human_kb "$(dir_kb "$HOME/Library/Developer/Xcode/Archives")")"
fi
# Maven
if [ -d "$HOME/.m2/repository" ]; then
echo \
" Maven repository: $(human_kb "$(dir_kb "$HOME/.m2/repository")")"
fi
# Docker volumes
if command_exists docker; then
echo \
" Docker volumes/images detail: run 'docker system df -v'"
fi
# Managed runtime / application trees.
# Report size, but do not blindly delete them.
for managed in \
"$HOME/.hermes" \
"$HOME/.pi" \
"$HOME/.pi-lens" \
"$HOME/.nvm" \
"$HOME/.vscode" \
"$HOME/Library/Application Support/Local/addons" \
"$HOME/Library/Application Support/Code/agent-host" \
"$HOME/Library/Application Support/discord"
do
if [ -d "$managed" ]; then
printf \
' Managed/runtime tree: %10s %s\n' \
"$(human_kb "$(dir_kb "$managed")")" \
"$managed"
fi
done
# ============================================================
# 7. GIT WORKTREE AUDIT
# ============================================================
echo
echo "[Git worktree candidates]"
echo \
" Build artifacts inside worktrees may be cleaned."
echo \
" Worktrees themselves are NOT automatically removed."
find "$HOME" \
\( \
-path "$HOME/Library" -o \
-path "$HOME/.Trash" -o \
-path "$HOME/.cache" \
\) \
-prune -o \
-type f \
-name .git \
-print \
2>/dev/null |
while IFS= read -r gitfile; do
if grep -q '/worktrees/' "$gitfile" 2>/dev/null; then
worktree="$(dirname "$gitfile")"
printf \
' %10s %s\n' \
"$(human_kb "$(dir_kb "$worktree")")" \
"$worktree"
fi
done
# ============================================================
# 8. DISK MAP
# ============================================================
echo
echo "[Largest top-level home directories]"
du -hd 1 "$HOME" 2>/dev/null |
sort -h |
tail -20 || true
if [ -d "$HOME/Library" ]; then
echo
echo "[Largest ~/Library directories]"
du -hd 1 "$HOME/Library" 2>/dev/null |
sort -h |
tail -20 || true
fi
# ============================================================
# 9. INDIVIDUAL HUGE FILES
# ============================================================
echo
echo "[Individual files larger than 1 GB — report only]"
find "$HOME" \
-type f \
-size +1G \
-print \
2>/dev/null |
while IFS= read -r bigfile; do
size="$(
du -h "$bigfile" 2>/dev/null |
awk '{print $1}'
)"
printf \
' %10s %s\n' \
"${size:-?}" \
"$bigfile"
done
# ============================================================
# 10. SUMMARY
# ============================================================
after_free_kb="$(
df -Pk "$HOME" |
awk 'NR==2 {print $4}'
)"
delta_kb=$(( after_free_kb - before_free_kb ))
echo
echo "============================================================"
echo " GREAT DELETE SUMMARY"
echo "------------------------------------------------------------"
echo \
" Rust target artifacts found: $(human_kb "$RUST_KB")"
echo \
" Node modules found: $(human_kb "$NODE_KB")"
echo \
" Framework/build caches found: $(human_kb "$FRAMEWORK_KB")"
echo \
" Known tool caches found: $(human_kb "$TOOLCACHE_KB")"
if [ "$CLEAR_USER_CACHES" -eq 1 ]; then
echo \
" User Library caches observed: $(human_kb "$APP_CACHE_KB")"
fi
echo "------------------------------------------------------------"
echo \
" Project/tool artifacts observed: $(human_kb "$TOTAL_FOUND_KB")"
echo \
" Protected/recent/active skipped: $(human_kb "$TOTAL_SKIPPED_KB")"
if [ "$APPLY" -eq 1 ]; then
echo \
" Tracked deletions completed: $(human_kb "$TOTAL_DELETED_KB")"
if [ "$delta_kb" -gt 0 ]; then
echo \
" Filesystem space reclaimed: $(human_kb "$delta_kb")"
else
echo \
" Filesystem free-space delta: $(human_kb "$delta_kb")"
fi
else
echo \
" DRY RUN: nothing deleted."
fi
echo "============================================================"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment