When to use this
- Memory usage grows over time in a long-running process and doesn't plateau.
- A heap snapshot/profile is available and needs interpreting.
- Not for a one-off high-memory spike that's expected behavior (e.g. processing a large batch) -- this is for unbounded growth, not a single high-water mark.
The skill file
Copy this verbatim. It's written in the SKILL.md format (YAML frontmatter + markdown instructions) that Claude Code, and increasingly other agent tools, read directly.
--- name: memory-leak-hunter description: Diagnose unbounded memory growth in a long-running process by analyzing heap snapshots or growth patterns to find what's retaining references it shouldn't, rather than guessing at the cause. Use for confirmed memory growth over time, not a single expected high-water mark. --- # Memory Leak Hunter ## Confirm it's actually a leak Growth that plateaus (e.g. cache filling to its configured max, or normal warm-up) isn't a leak. A leak is memory that grows without bound over time under steady-state load. Confirm the pattern with actual measurements over a long enough window before hunting for a cause. ## Find the retainer 1. If heap snapshots are available at two points in time, diff them -- what object type/count grew, and (if the tool supports it) what's holding a reference to those objects (the retainer path). 2. Common real-world causes to check first: event listeners registered but never removed, a cache/map with no eviction policy or size bound, closures capturing more than they need (especially in long-lived callbacks/timers), a connection/resource pool that isn't releasing entries, global/module-level collections that only grow. 3. For a language with manual memory management, also check for missing `free`/`dispose`/`close` calls on the growing resource type. ## Verify the fix After a candidate fix, re-run under the same steady-state load for long enough to confirm growth actually stopped (or plateaued at a sane bound), not just that it grew more slowly. ## Output The retained object type, the retainer chain/reason it's not being released, the fix, and confirmation (or a plan to confirm) that memory plateaus after the fix under sustained load.
Installing it elsewhere
The frontmatter/body split above is Claude Code's convention. Here's how to carry the same instructions into other tools:
Save the file below verbatim (frontmatter included) at that path, project-local or in ~/.claude/skills/ for a user-level skill. Claude Code loads the name/description pair to decide when to pull it in, or you invoke it directly as /memory-leak-hunter.
Convert the YAML frontmatter to Cursor's rule format (description, globs, alwaysApply: false) and keep the markdown body as the rule content. Cursor surfaces it by description match, same idea as Claude Code's auto-load.
Codex CLI (and increasingly other agentic CLIs) read AGENTS.md at the repo root as always-on instructions. Paste the markdown body under a heading like ## {title}; for GitHub Copilot's coding agent, the equivalent file is .github/copilot-instructions.md.
Append the markdown body to .windsurfrules at the repo root. Windsurf treats the whole file as always-on context, so keep only the instructions you want applied on every request.
- Treating a plateauing cache as a leak instead of confirming genuinely unbounded growth.
- Guessing at the cause ("probably event listeners") without a heap diff to confirm which object type is actually growing.
- Declaring victory after a fix reduces growth rate without confirming it actually plateaus over a long enough test window.