Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mac-hog-hunter

A Claude Code skill that finds out why a Mac is slow, hot, or out of memory — and tells apart the processes that are actually the problem from the ones that merely looked busy for a second.

Why not just open Activity Monitor

A snapshot cannot distinguish these two processes:

  • one compiling something for three seconds, and
  • one that has been quietly burning a full core for two days.

At the instant you look, they are identical. This skill samples repeatedly over a real time window and then asks how often something showed up as heavy — which is the question that separates a sustained hog from a normal burst.

It also hunts for a second, distinct problem: orphaned processes. A dev server, test watcher, or build daemon whose parent terminal or IDE has exited keeps running, reparented to launchd, consuming CPU and RAM for days. Nothing surfaces these in the usual tools, because from the outside they look like any other running process.

What it reports

Bucket Meaning
Consistently heavy Appeared in most samples with a high average — the real ongoing load
One-off spikes Appeared once or twice with a high peak — normal, not an action item
True zombies (STAT=Z) Already dead, using no resources — harmless, listed only for completeness
Orphaned processes Alive, parent gone, reported with working directory and age
Memory hogs by app RSS summed across all helper processes of each app

The last one matters more than it sounds: any Electron or Chromium app spreads its footprint over a dozen-plus helper PIDs, so per-process memory rankings systematically understate them. These scripts group by .app bundle.

Requirements

  • macOS (Apple Silicon first; Intel partially supported)
  • Python 3 (system Python is fine)
  • mac-system-stat installed as a sibling skill — this skill reuses its hoststat helper for CPU/memory/temperature/power readings instead of reimplementing metric collection.

Install

Clone into your Claude Code skills directory, next to mac-system-stat:

git clone https://github.com/procoders/mac-hog-hunter.git ~/.claude/skills/mac-hog-hunter

The layout must end up like this, since the skill calls its sibling by relative path:

~/.claude/skills/
├── mac-hog-hunter/
└── mac-system-stat/     # https://github.com/tomcatzh/mac-system-stat

Then just describe the symptom to Claude Code — "my Mac is slow", "why is it so hot", "what's eating my RAM" — and the skill triggers on its own.

Scripts

Each script is independently useful outside the skill:

Script What it does
scripts/monitor.sh Samples hoststat plus top CPU/RSS processes on an interval into a log
scripts/aggregate.py Reads that log, groups by app bundle, separates sustained load from spikes
scripts/mem_by_app.py One live pass summing RSS per app across all its helper processes
scripts/find_zombies.py Finds orphans and true zombies, with working directory and age
scripts/kill_gracefully.sh SIGTERM, wait, then SIGKILL only for what is still alive

How orphan detection actually works

PPID == 1 alone is not enough. On macOS every process whose parent dies gets reparented to launchd (PID 1) — but launchd also directly parents plenty of legitimate services on purpose, so the signal on its own cannot tell "orphaned" from "supposed to run forever".

find_zombies.py cross-checks against launchctl list, which enumerates the jobs launchd actually manages. It then filters out OS-bundled binaries, app extensions, and crash handlers, since those are designed to detach from their parent — a crash handler specifically must outlive the process it reports on. Without that filter the raw PPID == 1 signal is almost entirely false positives: in testing it returned 92 matches, of which fewer than 10 were real.

The worktree hook (optional)

hooks/worktree-orphan-cleanup.sh addresses the same problem from the other end: prevention.

git worktree remove deletes a directory but does nothing about processes still running with that directory as their working directory. Those become orphans the moment their parent session exits. The hook runs before the removal and kills anything whose cwd is inside the target path.

Install it as a PreToolUse hook in ~/.claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "if": "Bash(git *)",
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/skills/mac-hog-hunter/hooks/worktree-orphan-cleanup.sh",
            "timeout": 15
          }
        ]
      },
      {
        "matcher": "Bash",
        "if": "Bash(rm *)",
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/skills/mac-hog-hunter/hooks/worktree-orphan-cleanup.sh",
            "timeout": 15
          }
        ]
      }
    ]
  }
}

The hook never blocks the command it precedes — it always exits 0. A failure to clean up should not stop your git operation.

Note that it kills matching processes automatically and without confirmation. That is the intended trade-off for a hook, but it is a real behavior change: if a worktree removal catches a process you wanted to keep, it dies.

Notes from building this

A few things that cost real debugging time, recorded so nobody repeats them:

  • macOS ps has no etimes. That is a GNU extension. BSD ps only offers etime in [[DD-]HH:]MM:SS, which the script parses itself.
  • Batch your lsof calls. Querying working directories one PID at a time is roughly a hundred times slower than one batched call across every PID at once. On ~700 processes that is the difference between 0.7 seconds and a multi-minute hang.
  • Resolve symlinks before comparing paths. lsof reports /private/tmp where pwd says /tmp, so path comparisons silently fail without pwd -P.
  • ps aux output can sort into your results. The header row sorts like any other line and will land in a top-N list.

Credits

  • mac-system-stat by tomcatzh — required dependency, MIT licensed
  • devclean by ImL1s — the SIGTERM → wait → SIGKILL sequencing and the PPID == 1 orphan signal

License

MIT — see LICENSE.

About

Claude Code skill that finds why a Mac is slow or hot — separates sustained resource hogs from one-off spikes, and hunts orphaned dev servers still running for days

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages