Your disk is bloated. Let's fix that.
Two ways to clean and monitor your Mac:
- BloatMac — native SwiftUI desktop app. CleanMyMac-class feature surface, no subscription.
- bloat — htop-style terminal UI. Disk analysis, smart cleanup, live system monitor, plugin system.
Both are real-detection tools — every module reads actual disk / system state, not mocks.
Native SwiftUI cleanup + maintenance app. 19 screens. CleanMyMac-class feature surface, no subscription.
Storage
- Smart Care · one-click scan + apply
- Storage treemap · Large files · Duplicates (exact + visually-similar via Vision)
- Unused & old · Downloads & cache
- Uninstaller (full leftover sweep) · Updater (Homebrew · MAS · Sparkle)
- System junk · Privacy · Cloud (iCloud · Drive · Dropbox · OneDrive)
System
- Memory · Startup items · Battery · Network
- Maintenance · Schedules · Disk health · Permissions audit
- Real
NSStatusItemmenu-bar widget — storage %, memory pressure, network rates, quick-scan
Dashboard
Network
brew install --cask akhil-gautam/tap/bloatmacOr grab the .dmg from the releases page (tags starting with bloatmac-v…).
Releases are Developer ID signed and Apple-notarized — Gatekeeper opens cleanly, no quarantine workaround.
Requires macOS 26 (Tahoe). The dashboard's AI briefing uses Foundation Models on macOS 26+ and falls back to a deterministic heuristic on older releases.
See bloatmac/README.md for the full module list, project layout, and roadmap.
An htop-style terminal UI for macOS that combines disk storage analysis, smart cleanup, and real-time system monitoring in one tool. Scan folders to find what's eating your disk, clean up caches/build artifacts/duplicates with tiered safety, and monitor CPU, memory, network, GPU, and processes — all without leaving the terminal.
Built in Rust with ratatui.
curl -fsSL https://raw.githubusercontent.com/akhil-gautam/bloat/main/install.sh | bashDownloads the latest release binary for your Mac (Apple Silicon or Intel) and installs to /usr/local/bin/.
Download the latest binary from Releases:
| Platform | Download |
|---|---|
| macOS Universal (ARM64 + Intel) | bloat-vX.Y.Z-macos-universal.tar.gz |
| macOS Apple Silicon | bloat-vX.Y.Z-macos-arm64.tar.gz |
| macOS Intel | bloat-vX.Y.Z-macos-x86_64.tar.gz |
tar -xzf bloat-*.tar.gz
chmod +x bloat
sudo mv bloat /usr/local/bin/git clone https://github.com/akhil-gautam/bloat.git
cd bloat
cargo build --release
./target/release/bloatRequires Rust 1.70+ and macOS.
bloat # Launch interactive TUI
bloat scan ~/ # Quick scan summary
bloat top 10 # Top 10 largest items
bloat clean --dry-run # Preview what can be cleanedOn launch, pick which folders to scan:
bloat — your disk is bloated. let's fix that.
↑↓ navigate Space select Enter scan a all q quit s system monitor
┌ Select folders to scan ────────────────────────┐
│ [ ] Desktop │
│ [x] Downloads │
│ [ ] Documents │
│ [ ] Library │
│ [ ] Applications │
│ [ ] Entire Home Directory │
└─────────────────────────────────────────────────┘
Disk usage dashboard with segmented category bar, top space consumers (selectable + deletable), and reclaimable space summary.
j/k— Navigate top consumersSpace— Select items for deletiond— Delete selected (with confirmation dialog)
Interactive directory tree with proportional size bars.
j/k/↑/↓— NavigateEnter/→/l— Expand directory←/h— Collapse directory
Smart cleanup with 20+ detection rules, categorized by safety level. Split view with detail panel showing description, impact, and file paths.
j/k— Navigate itemsSpace— Toggle checkboxa— Select/deselect allEnter— Clean selected items
Detection Rules:
| Category | Rules | Safety |
|---|---|---|
| Developer | node_modules, Xcode DerivedData, cargo target/, .gradle/build, Python __pycache__/.venv/.tox, CocoaPods cache, Swift .build |
Safe - Caution |
| System | ~/Library/Caches, old log files, Trash contents, iOS device backups, Time Machine snapshots |
Safe - Caution |
| Applications | Browser caches (Chrome/Safari/Firefox/Arc), Slack/Discord/Teams, Spotify, Docker, Homebrew, npm/pip/cargo caches | Safe - Caution |
| Media | Duplicate files (BLAKE3 hash), large unused files (>1GB, 90+ days) | Caution - Risky |
Safety Levels:
- Safe (green) — Automatically moved to Trash. Caches, build artifacts — regenerated on next use.
- Caution (yellow) — Confirmation required, then moved to Trash. May cause slower rebuilds.
- Risky (red) — Confirmation required, choose Trash or permanent delete. Review carefully.
Full deletion history with timestamps, file names, sizes, method (Trash/Permanent), and success/fail status.
Full htop-style system monitor accessible from any screen via s. Press Esc to return.
┌ CPU ────────────────────────┐┌ Memory ─────────────────────┐
│ 0 ████░░░░ 23% 2.4GHz ││ RAM ████████░░ 12/16 GiB │
│ 1 ██░░░░░░ 12% 2.4GHz ││ wired:2G active:6G compr:3G│
│ Hist: C0:▂▃▅▇ C1:▁▂▃▄ ││ Pressure: Normal (72%) │
│ usr:12% sys:8% idle:80% ││ Swp 0B/2G Load 3.2 2.8 │
│ Temp: 52°C 3.5 GHz ││ Tasks:551 Thr:2341 │
├ Network ────────────────────┤├ Battery ─────────────────────┤
│ en0: ▲ 1.2 MB/s ▼ 340 KB/s ││ ████████░░ 82% charging 1:22│
│ Chrome ▲12M ▼89M → goog ││ │
│ Slack ▲1M ▼4M → amzn │├ Volumes ─────────────────────┤
├ Disk I/O ───────────────────┤│ / ████████░ 342/460 GiB │
│ Throughput: 4.1 MiB/s ││ │
│ Latency: 0.12ms R / 0.08ms │├ Containers ──────────────────┤
├ GPU ────────────────────────┤│ postgres:15 running :5432 │
│ Apple M4 Device:13% Tiler:8%││ redis:7 running :6379 │
│ Renderer:12% Mem:420M/2.4G ││ │
└─────────────────────────────┘└──────────────────────────────┘
┌ Processes (sorted by CPU ▼ | e: expand threads) ───────────┐
│ PID USER CPU% MEM R/s W/s COMMAND │
│ 1234 akhilgautam 45.1 1.2 GiB 2 MiB 1 MiB node │
│ ├── Thread 0x1a3 8.2% running │
│ └── Thread 0x1a4 3.1% sleeping │
│ 5678 akhilgautam 2.1 120 MiB 0 B 0 B ruby │
│ [Ruby] Rails :3000 │
└─────────────────────────────────────────────────────────────┘
| Key | Action |
|---|---|
c |
Sort by CPU% |
m |
Sort by Memory |
p |
Sort by PID |
n |
Sort by Name |
/ |
Fuzzy search processes |
t |
Toggle tree view (parent → child hierarchy) |
g |
Cycle grouping: None → By App → By User |
e |
Expand/collapse process threads |
d |
Toggle process diff (new/exited/spikes in last 5s) |
K |
Kill selected process (with confirmation) |
Space |
Pause — enter history playback mode |
←/→ |
Scrub through 5 minutes of history (when paused) |
Esc |
Exit filter / cancel / unpause / back |
CPU
- Per-core usage bars with frequency (MHz)
- Per-core sparkline history (4 hottest cores)
- User / System / Idle breakdown
- Temperature + thermal throttle detection
Memory
- Segmented bar: wired (red), active (yellow), compressed (magenta), inactive (gray)
- Memory pressure indicator (Normal / Warning / Critical)
- Swap, load average, uptime, task & thread counts
Network
- Interface throughput with upload/download sparklines
- Per-app bandwidth, connections, and remote hosts
- Network anomaly detection (spike > 3x rolling average)
Disk I/O
- Live throughput (1-second samples via iostat)
- NVMe latency estimation (read/write)
GPU (Apple Silicon)
- Device / Tiler / Renderer utilization %
- GPU memory usage (in-use / allocated)
[GPU]badge on GPU-using processes
Battery — charge %, charging/discharging, time remaining
Volumes — all mounted disks with usage bars
Containers — Docker containers with CPU/mem/ports, Kubernetes context
Process Intelligence
- Runtime detection:
[Node][Python][Ruby][Java][Go][Rust][Elixir] - Service labels: Rails, Sidekiq, Webpack, Vite, Gunicorn, Redis, PostgreSQL, Nginx
- Port mapping:
:3000:6379:5432 [GPU]badge for GPU-using processes- Per-process disk read/write rates
Smart Features
- Threshold alerts (CPU > 90%, memory > 80% for 10s) with macOS desktop notifications
- Anomaly detection: CPU spikes (z-score > 2), memory jumps (> 500MB), new heavy processes, network spikes
- 5-minute history playback with anomaly markers on timeline
Launches the interactive TUI.
Scan and print a summary to stdout.
bloat scan ~/Downloads
bloat scan ~/Downloads --json # Machine-readable output
bloat scan --path /tmpClean detected junk.
bloat clean --dry-run # Preview what would be cleaned
bloat clean --safe # Auto-clean all Safe items, no prompts
bloat clean # Interactive cleanup with prompts
bloat clean --path ~/projectsShow the N largest items.
bloat top # Top 10 (default)
bloat top 20 # Top 20
bloat top 5 --path ~/Downloads| Flag | Description |
|---|---|
--json |
Machine-readable JSON output |
--no-color |
Disable colored output |
--path <dir> |
Override scan directory (default: ~) |
--min-size <size> |
Hide items below threshold (e.g. 100MB) |
Press ? in the TUI to see this overlay:
── Global ──
q Quit
? Toggle this help
1 / 2 / 3 / 4 Switch tabs (Overview / Explorer / Cleanup / Logs)
Tab Next tab
s System monitor (htop)
r Rescan filesystem
Esc Cancel scan / back
── Overview (Tab 1) ──
j / k Navigate top consumers
Space Select item for deletion
d / Enter Delete selected (with confirmation)
── Explorer (Tab 2) ──
j / k / ↑↓ Navigate tree
l / → / Enter Expand directory
h / ← Collapse directory
── Cleanup (Tab 3) ──
j / k Navigate items
Space Toggle item checkbox
a Select / deselect all
Enter Clean selected items
── System Monitor (s) ──
c / m / p / n Sort by CPU / MEM / PID / Name
/ Fuzzy search processes
t Toggle tree view (parent → child)
g Cycle grouping (None → App → User)
e Expand process threads
d Toggle process diff (changes in 5s)
K (shift+k) Kill selected process
Space Pause / resume (enter playback)
← / → Scrub history (when paused)
Esc Exit filter / cancel / unpause
bloat has a three-layer plugin system — from zero-code config to full Lua scripting. Plugins add custom panels to the System Monitor tab.
Create ~/.config/bloat/plugins.toml:
[[panel]]
name = "Redis"
command = "redis-cli info memory | grep used_memory_human"
interval = 5
position = "right"
color = "red"
[[panel]]
name = "Postgres"
command = "psql -c 'SELECT count(*) FROM pg_stat_activity;' -t 2>/dev/null || echo 'PG not running'"
interval = 10
position = "right"
color = "blue"
[[panel]]
name = "Ollama"
command = "curl -s localhost:11434/api/tags 2>/dev/null | python3 -c 'import sys,json; [print(m[\"name\"]) for m in json.load(sys.stdin).get(\"models\",[])]' || echo 'Ollama not running'"
interval = 30
position = "right"
color = "magenta"
[[panel]]
name = "Docker"
command = "docker ps --format '{{.Names}}\\t{{.Status}}' 2>/dev/null | head -5 || echo 'Docker not running'"
interval = 10
position = "left"
color = "cyan"
[[panel]]
name = "Git"
command = "cd ~/projects && for d in */; do echo \"$d$(cd $d && git status --short 2>/dev/null | wc -l | tr -d ' ') changes\"; done | head -5"
interval = 30
position = "left"
color = "green"| Field | Default | Description |
|---|---|---|
name |
required | Panel title |
command |
required | Shell command to run |
interval |
5 |
Refresh interval in seconds |
position |
"right" |
"left" or "right" column |
color |
none | Border color: red, green, blue, yellow, cyan, magenta |
Plugins are executables in ~/.config/bloat/plugins/ that communicate via JSON-lines over stdin/stdout.
Protocol:
bloat → plugin (stdin): {"type":"tick","cpu_total":45.2,"mem_used":12884901888,"mem_total":17179869184,"processes":[...]}
plugin → bloat (stdout): {"type":"panel","title":"My Panel","rows":[{"text":"Line 1","color":"green"},{"text":"Line 2","bold":true}]}
Example plugin (~/.config/bloat/plugins/redis-monitor, make executable with chmod +x):
#!/bin/bash
while IFS= read -r line; do
info=$(redis-cli info memory 2>/dev/null)
if [ $? -eq 0 ]; then
used=$(echo "$info" | grep "used_memory_human" | cut -d: -f2 | tr -d '\r')
echo "{\"type\":\"panel\",\"title\":\"Redis\",\"rows\":[{\"text\":\"Memory: $used\",\"color\":\"green\"}]}"
else
echo "{\"type\":\"panel\",\"title\":\"Redis\",\"rows\":[{\"text\":\"Not running\",\"color\":\"red\"}]}"
fi
doneOptional manifest.toml in the same directory for per-plugin config:
[redis-monitor]
interval = 5
position = "right"Lua scripts in ~/.config/bloat/lua/*.lua get system data and can do computation, filtering, and conditional logic.
-- ~/.config/bloat/lua/node_monitor.lua
panel = {
name = "Node.js",
interval = 3,
position = "right",
color = "green"
}
function collect(data)
-- data.cpu_total, data.mem_used, data.mem_total
-- data.processes = [{pid, name, cpu, mem}, ...]
local lines = {}
local count = 0
local total_mem = 0
for _, p in ipairs(data.processes) do
if string.find(p.name:lower(), "node") then
count = count + 1
total_mem = total_mem + p.mem
end
end
if count > 0 then
table.insert(lines, {text = count .. " processes", color = "green"})
table.insert(lines, {text = string.format("Mem: %.0f MiB", total_mem / 1048576), color = "cyan"})
else
table.insert(lines, {text = "No Node processes", color = "gray"})
end
return lines
endMore examples:
-- ~/.config/bloat/lua/heavy_hitters.lua
panel = { name = "Heavy Hitters", interval = 2, position = "left", color = "red" }
function collect(data)
local lines = {}
for _, p in ipairs(data.processes) do
if p.cpu > 50 then
table.insert(lines, {
text = string.format("%s (PID %d): %.1f%%", p.name, p.pid, p.cpu),
color = "red", bold = true
})
end
end
if #lines == 0 then
table.insert(lines, {text = "All quiet", color = "green"})
end
return lines
end| Use Case | Best Layer | Example |
|---|---|---|
| Redis queue depth | TOML | redis-cli llen myqueue |
| Postgres connections | TOML | psql -c 'SELECT count(*) FROM pg_stat_activity' -t |
| Sidekiq jobs | TOML | curl -s localhost:3000/sidekiq/stats |
| Ollama running models | TOML | curl -s localhost:11434/api/tags | jq |
| Docker containers | TOML | docker ps --format ... |
| Node.js heap stats | External | Connect to --inspect debug port |
| Puma worker utilization | External | Parse puma stats endpoint |
| PyTorch VRAM | Lua | Filter processes, compute memory |
| ML inference tokens/sec | External | Poll ollama API, compute delta |
| Custom alerting | Lua | Conditional logic on system data |
Create ~/.config/bloat/plugins.toml to monitor your database alongside system metrics:
# Connection states (active, idle, etc.)
[[panel]]
name = "PG Connections"
command = "psql -c \"SELECT state, count(*) FROM pg_stat_activity GROUP BY state ORDER BY count DESC;\" -t 2>/dev/null || echo 'PG not running'"
interval = 5
position = "right"
color = "blue"
# Currently running queries
[[panel]]
name = "PG Active Queries"
command = "psql -c \"SELECT pid, now()-query_start as duration, left(query,50) FROM pg_stat_activity WHERE state='active' AND query NOT LIKE '%pg_stat%' ORDER BY duration DESC LIMIT 5;\" -t 2>/dev/null || echo 'None'"
interval = 5
position = "right"
color = "blue"
# Database sizes
[[panel]]
name = "PG Databases"
command = "psql -c \"SELECT datname, pg_size_pretty(pg_database_size(datname)) as size FROM pg_database WHERE datistemplate=false ORDER BY pg_database_size(datname) DESC LIMIT 8;\" -t 2>/dev/null || echo 'PG not running'"
interval = 30
position = "left"
color = "blue"
# Largest tables in your app database
[[panel]]
name = "PG Top Tables"
command = "psql -d myapp_development -c \"SELECT schemaname||'.'||relname, pg_size_pretty(pg_total_relation_size(relid)) FROM pg_catalog.pg_statio_user_tables ORDER BY pg_total_relation_size(relid) DESC LIMIT 5;\" -t 2>/dev/null || echo 'No tables'"
interval = 30
position = "left"
color = "cyan"
# Redis alongside Postgres
[[panel]]
name = "Redis"
command = "redis-cli info memory 2>/dev/null | grep -E 'used_memory_human|connected_clients' || echo 'Redis not running'"
interval = 5
position = "right"
color = "red"Replace myapp_development with your database name. Add -U username -h hostname to psql if needed.
See examples/ directory for more ready-to-use plugin samples.
- Language: Rust
- TUI: ratatui + crossterm
- Filesystem: jwalk (parallel walker)
- System info: sysinfo
- Hashing: BLAKE3 (duplicate detection)
- Fuzzy search: fuzzy-matcher (skim algorithm)
- Notifications: notify-rust (macOS Notification Center)
- Trash: trash (macOS Trash integration)
- Lua scripting: mlua (Lua 5.4, vendored)
- Config: toml (plugin config parsing)
src/
main.rs Entry point, CLI parsing (clap)
app.rs App state, event loop, key handlers
tree.rs In-memory filesystem tree
scanner.rs Parallel filesystem walker
analyzer.rs Runs cleanup rules against scan tree
cleaner.rs Deletion engine (trash / permanent)
alerts.rs Threshold alert engine + notifications
history.rs Time-series recording + anomaly detection
system_monitor.rs System stats collection (CPU/mem/net/GPU/disk)
rules/
mod.rs CleanupRule trait + registry
dev.rs Developer tool rules
system.rs System cache/log rules
apps.rs Application cache rules
media.rs Duplicate/large file rules
plugins/
mod.rs Plugin module registry
config.rs TOML config-driven panel loader
runner.rs Shell command panel executor
protocol.rs External process JSON-lines protocol
lua_engine.rs Lua scripting engine
ui/
mod.rs Main draw + header + status bar + help overlay
overview.rs Tab 1: disk dashboard
explorer.rs Tab 2: directory tree
cleanup.rs Tab 3: smart cleanup
logs.rs Tab 4: deletion history
htop.rs System monitor (CPU/mem/net/GPU/processes)
~/.config/bloat/ User config directory
plugins.toml TOML panel definitions
plugins/ External process plugins (executables)
manifest.toml Optional per-plugin config
lua/ Lua script plugins
*.lua One file per panel
MIT


