skill-graveyard / mcp / memory / github ↗ npm ↗ install

skill-graveyard

audit which Claude Code skills you actually use — same parser surfaces both dead installs and hallucinated invocations, so it's not just a graveyard, it's a two-sided audit of where your setup is over- and under-provisioned.

$ npx skill-graveyard
local-only. no network, no telemetry. node ≥ 18. zero config. mit.

01 what it sorts

Four buckets. One parser.

Each skill name that appears in your local ~/.claude/projects/**/*.jsonl ends up in exactly one of these.

active · keep

installed AND invoked

The skill is present in ~/.claude/skills/, an ~/.agents/skills/ dir, a plugin install path, or a project-scoped .claude/skills/ — and Claude actually called it within the audit window.

dead · removal candidate

installed but zero invocations

Sits on disk loading its description into every session's context — but Claude never calls it. prune emits source-aware removal commands; for plugins where every skill is dead, it rolls up to a single uninstall.

?missing · likely external

invoked but source not located

The call resolved successfully but no SKILL.md matches in any scanned path. Usually a skill registered by another framework Claude Code runs inside (paperclip, custom orchestrators) or installed somewhere unconventional.

hallucinated · model error

invoked and the runtime errored

Mostly Claude confusing tool names with skill names (bash, read, edit). suggest classifies these into actionable groups: tool/skill confusion, likely typo, external framework, unclassified.

02 live audit

Run it once and see the shape of your skill library.

Below: the actual output from the author's machine. 160 sessions, 379 skill calls, 30-day window.

~ $ skill-graveyard --days 30
────────────────────────────────────────────────────────────
 skill-graveyard — 30d audit
 65 installed   13 active · 52 dead · 4 missing
              ██████                            20% used
 160 sessions, 379 calls, 93 errored
 +29 hallucinated names (telemetry)
────────────────────────────────────────────────────────────

ACTIVE (13)  installed and invoked — keep
  superpowers:brainstorming                ████████████████  83  just now  plugin:superpowers
  superpowers:writing-plans                ███████████       56  just now  plugin:superpowers
  superpowers:subagent-driven-development  ██████████        53  just now  plugin:superpowers
  superpowers:systematic-debugging         ███               13  16h ago   plugin:superpowers
  frontend-design:frontend-design          ▏                  3  just now  plugin:frontend-design
  task-management                          ▏                  1  15h ago   userMISSING (4)  resolved but source not located — project-scoped or external framework
  paperclip                47 calls  (14d ago)
  para-memory-files        15 calls  (14d ago)
  paperclip-create-agent    5 calls  (14d ago)
  update-config             3 calls  (4h ago)
  → skill-graveyard suggest    for actionable classification

HALLUCINATED (29 names, 91 calls)  Claude→tool/skill confusion, mostly noise
  top: bash (18), read (18), batch (6), commit (6), edit (6)
  → skill-graveyard suggest    for actionable classification

DEAD (52)  installed, 0 invocations in 30d — removal candidates

  plugin rollups (4)  every skill of the plugin is dead — uninstall whole plugin
    plugin:figma          9 skills  claude /plugin remove figma@claude-plugins-official
    plugin:claude-mem     6 skills  claude /plugin remove claude-mem@thedotmack
    plugin:supabase       2 skills  claude /plugin remove supabase@claude-plugins-official
    plugin:skill-creator  1 skill   claude /plugin remove skill-creator@claude-plugins-official

  individual (34)
    user  (26)
      adr-skill, ai-elements, ai-sdk, canvas-design, docker-expert,
      github-actions-docs, helm-chart-scaffolding, helm-debugging,
      …
      → skill-graveyard prune --only user

captured 2026-04-29 from sfrangulov's machine. the headline answer: 20% of installed skills carry their weight; 80% are inert metadata in every session's context.

03 five subcommands

Audit, then act.

audit is the default and is what you usually run. The other four turn its output into actions.

audit [--days N] [--only active|dead|missing|hallucinated] [--json]

Default command — shown above. Sorts every skill name into the four buckets, with a plugin-rollup pass that collapses fully-dead plugins into single uninstall rows.

prune [--apply] [--only user|agents|plugin]

Reads the audit and emits a removal plan. Source-aware: user/agents get unlink (executed with --apply); plugin gets the slash command to paste into Claude Code (always print-only — invoking /plugin remove from outside the runtime is fragile); project is left alone.

~ $ skill-graveyard prune
skill-graveyard prune — 30d window  (mode: dry-run)
plan: 26 unlinks, 4 plugin removals

WOULD UNLINK (26)
  user (26)
    ~/.claude/skills/adr-skill
    ~/.claude/skills/ai-elements
    ~/.claude/skills/ai-sdk
    ~/.claude/skills/canvas-design
    ~/.claude/skills/docker-expert
    ~/.claude/skills/github-actions-docs
    …

PLUGIN REMOVALS (4)  run inside Claude Code:
  claude /plugin remove figma@claude-plugins-official  # 9 skills, all dead
  claude /plugin remove claude-mem@thedotmack          # 6 skills, all dead
  claude /plugin remove supabase@claude-plugins-official  # 2 skills
  claude /plugin remove skill-creator@claude-plugins-official  # 1 skill

re-run with --apply to execute the unlinks (plugin removals always print only)
suggest [--days N] [--json]

Classifies missing and hallucinated rows into actionable buckets so the noise becomes triage-able.

~ $ skill-graveyard suggest
skill-graveyard suggest — 30d window
33 unique names, 161 calls, 3 actionable groups

EXTERNAL FRAMEWORK (3 names, 67 calls)
  registered by another framework Claude Code runs inside — document in CLAUDE.md
  ~/.paperclip/  3 names, 67 calls
    paperclip, para-memory-files, paperclip-create-agent

TOOL/SKILL CONFUSION (9 names, 56 calls)
  Claude invoked a built-in CC tool name as a skill — known model failure mode
  bash (18), read (18), edit (6), Read (6), Bash (3), agent (2),
  grep (1), write (1), Write (1)

UNCLASSIFIED (21 names, 38 calls)
  no matching pattern — review manually
  batch (6), commit (6), update-config (3), pdf (3), agents (2),
  help (2), shell (2), code (1), …
projects [--days N] [--json]

Same parser, different cut: groups every skill invocation by the cwd recorded in the session log. Surfaces project-scoped vs. global usage and per-project hallucinations.

~ $ skill-graveyard projects
────────────────────────────────────────────────────────────
 skill-graveyard projects — 30d
 28 projects   167 sessions, 379 skill calls
────────────────────────────────────────────────────────────

~/projects/client-analytics  53 ses, 127 calls, 11 skills
    superpowers:brainstorming                  50×
    superpowers:writing-plans                  32×
    superpowers:subagent-driven-development    31×
  ? update-config                               3×
    find-skills                                 1×
    frontend-design:frontend-design             1×
  …and 3 more

~/.claude-mem/observer-sessions  33 ses, 93 calls, 30 skills · 93 errored
   bash      18×
   read      18×
   batch commit…and 26 more

~/projects/clientco/web-platform  18 ses, 52 calls, 8 skills
    superpowers:brainstorming                  18×
    superpowers:writing-plans                  12×
    …

sorted by total skill calls. ✗ = hallucinated (errored). ? = invoked but not installed.
cost [--days N] [--json]

Estimates how many tokens your skill metadata consumes per session vs. how many of those tokens cover skills Claude actually invokes. Each SKILL.md description is loaded into the Skill tool definition on every API request — even if the skill is never invoked.

~ $ skill-graveyard cost
────────────────────────────────────────────────────────────
 skill-graveyard cost — 30d window
 65 skills × avg 48 desc tokens = ~3.1K loaded per session
 × 160 sessions ≈ 501.4K of skill-metadata loaded over window
 of which ~497.2K (99%) loaded for skills never invoked (52 dead)
────────────────────────────────────────────────────────────

HOOK INJECTIONS  per-session text from SessionStart hooks
  SessionStart   2541 t/sess  × 72 sessions  = 182.9K

TOP WASTERS (15 of 64)  desc tokens × sessions where never invoked
  playwright-best-practices               user            207 t × 0/160     33.1K
  figma:figma-generate-design             plugin:figma    185 t × 0/160     29.6K
  ai-sdk                                  user            152 t × 0/160     24.3K
  supabase:supabase                       plugin:supabase 125 t × 0/160     20.0K
  figma:figma-use                         plugin:figma    108 t × 0/160     17.3KEARNING THEIR KEEP  top by invocation rate
  superpowers:brainstorming   plugin:superpowers  34 t × 62/160     3.3K
  superpowers:writing-plans   plugin:superpowers  17 t × 49/160     1.9K
  superpowers:subagent…       plugin:superpowers  12 t × 49/160     1.3K

note: token counts via cl100k_base (proxy for Claude tokenization;
off by 5–15% in practice).
outdated [--no-cache] [--ttl <min>] [--json]

Checks every installed plugin (against its marketplace's marketplace.json) and every git-tracked user/agent skill (against git ls-remote) for newer versions upstream. Network-bound — the only subcommand that calls out. Results cached at ~/.cache/skill-graveyard/outdated/ with a 60-minute TTL by default.

~ $ skill-graveyard outdated
skill-graveyard outdated — checked just now (3 cache hits)
plan: 12 plugins, 1 skill repo · 2 outdated · 9 up-to-date · 2 unknown

OUTDATED (2)
  plugins (1)
    superpowers@claude-plugins-official  b7a8f76 → 6efe32c
      → claude plugin update superpowers@claude-plugins-official
      affects: brainstorming, executing-plans, writing-plans (and 11 more)
  skill repos (1)
    ~/projects/my-skills  abc1234 → def5678
      → git -C ~/projects/my-skills pull --ff-only
      affects: foo, bar

UNKNOWN (2)
  claude-mem@thedotmack  unknown → 1abc234
    → claude plugin remove claude-mem@thedotmack
    → claude plugin install claude-mem@thedotmack
    reason: installed without version metadata; reinstall to refresh

Each plugin is matched against its marketplace entry shape: explicit version, pinned commit SHA, or upstream HEAD via git ls-remote. Plugins recorded as installedVersion: "unknown" get a reinstall hint instead of an update one — Claude Code only stamps the SHA on fresh installs.

all output above is real — captured locally with npx skill-graveyard <cmd> --days 30 --no-color.

04 install

Three ways in.

one-off

No install. Just run it. Best for the first try.

$npx skill-graveyard
global cli

Installs the binary on your PATH.

$npm i -g skill-graveyard
skills.sh

Install as an Agent Skill — Claude auto-discovers it in any session. Same command also installs the sister mcp-graveyard skill.

$npx skills add sfrangulov/skill-graveyard

05 notes

Things worth knowing.

What it reads

~/.claude/projects/**/*.jsonl for invocations and cwd; installed_plugins.json for plugin paths; ~/.claude/skills/, ~/.agents/skills/, plugin install dirs, and <cwd>/.claude/skills/ walking up to $HOME for project-scoped skills.

Token estimates use cl100k_base

Anthropic doesn't ship a public tokenizer for Claude 3+. cl100k_base (gpt-tokenizer) is the closest defensible proxy — expect 5–15% drift. Anthropic prompt caching reduces dollar cost; loaded tokens still consume your context window and rate-limit budget.

It does NOT auto-disable plugin skills

Plugin removal is a Claude Code slash command and invoking it from outside the runtime is fragile, so prune only prints the command. Run it inside Claude Code yourself.

It does NOT touch project-scoped skills

Skills under <project>/.claude/skills/ are intentional per-project artifacts; prune ignores them.

It does NOT phone home — except when you run outdated

Five of the six subcommands are entirely local. outdated is the explicit exception: it fetches each registered marketplace's marketplace.json and runs git ls-remote against marketplace and skill-repo origins. Results are cached at ~/.cache/skill-graveyard/outdated/. Everything else stays local. No telemetry, no hidden uploads.

Pipe to jq

Every subcommand accepts --json for machine-readable output. Custom queries like --json | jq '.rows[] | select(.category=="dead") | .invokeName' just work.

06 companion

Same parser, different graveyard.

A single MCP server can advertise 50 tools — full JSON schemas — adding hundreds of tokens to every API request whether or not Claude calls them. mcp-graveyard surfaces the same dual signal as skill-graveyard: dead servers (configured in ~/.claude.json, never invoked) and hallucinated calls (InputValidationError on tool names that don't exist on the connected server).

one-off

Same install pattern as skill-graveyard.

$npx mcp-graveyard
global cli

Installs the binary on your PATH.

$npm i -g mcp-graveyard
skills.sh

Install as an Agent Skill — Claude auto-discovers it in any session. Same command also installs skill-graveyard.

$npx skills add sfrangulov/skill-graveyard
audit [--days N] [--only active|dead|missing|hallucinated] [--tools <server>] [--json]

Default command. Sorts every MCP server into the four buckets — active (configured and called successfully), dead (configured but never called), missing (called but not in ~/.claude.json), hallucinated (called and rejected with InputValidationError). Server-first; --tools <server> drills down per-tool.

~ $ mcp-graveyard --days 30
mcp-graveyard — 30 days · 8 servers configured · 143 calls · 112 succeeded · 31 errored

ACTIVE (3)  configured and invoked — keep
  plugin_supabase_supabase      47 tools, 5 invoked, 89 calls  last 2026-04-28
  plugin_playwright_playwright   3 tools, 3 invoked, 34 calls  last 2026-04-27
  plugin_claude-mem_mcp-search   4 tools, 4 invoked, 12 calls  last 2026-04-29

DEAD (3)  configured, 0 invocations in 30d — removal candidates
  pencil                        0 tools, 0 invoked, 0 calls   —
  plugin_figma_figma            0 tools, 0 invoked, 0 calls   —
  claude_ai_Gmail               0 tools, 0 invoked, 0 calls   —

HALLUCINATED (1)  tool name returned InputValidationError — server registered but call failed
  plugin_supabase_supabase       0 tools, 0 invoked, 2 calls  last 2026-04-22
  → mcp-graveyard suggest    for actionable classification

MISSING (1)  invoked but not found in ~/.claude.json — external registration or stale log
  old-analytics-server           1 tools, 1 invoked, 3 calls  last 2026-04-16
  → mcp-graveyard suggest    for actionable classification

→ run: mcp-graveyard prune  to clear DEAD servers
prune [--apply] [--only <server>] [--days N]

Reads the audit and emits a removal plan for DEAD servers. Print-only by default. --apply writes a 0o600 backup of the full mcpServers entry (command, args, env — env may contain API keys) to ~/.claude/mcp-graveyard-backup/<ISO>.json, then runs claude mcp remove sequentially per server. Continues on individual failures. Pre-flight checks claude --version before any I/O.

~ $ mcp-graveyard prune
mcp-graveyard prune — plan: 3 servers to remove (0 successful calls in 30 days)

  pencil                                    claude mcp remove pencil
  plugin_figma_figma                        claude mcp remove plugin_figma_figma
  claude_ai_Gmail                           claude mcp remove claude_ai_Gmail

re-run with --apply to execute (backup is automatic)
projects [--days N] [--json]

Same parser, different cut: groups every MCP call by the cwd recorded in the session log. Surfaces servers that are configured globally but only used in one project — a signal to consider moving them to <project>/.mcp.json. Inline (N hallucinated) tags flag projects where Claude is calling tool names that don't exist.

~ $ mcp-graveyard projects
~/projects/client-analytics  17 ses, 39 calls, 2 servers
    plugin_supabase_supabase    31×
    plugin_claude-mem_mcp-search 8×

~/projects/clientco/web-platform  2 ses, 2 calls, 1 server
   plugin_supabase_supabase  2× (2 hallucinated)

~/projects/landing  5 ses, 8 calls, 1 server
    pencil                       8×
suggest [--days N] [--json]

Classifies missing and hallucinated server names into actionable buckets — TYPO (closest configured server within Levenshtein 2), TOOL_CONFUSION (Claude called a built-in CC tool name as if it were an MCP server), UNCLASSIFIED (no pattern matched). Each row carries the reason inline so triage is one-pass.

~ $ mcp-graveyard suggest
TOOL_CONFUSION (2)
  "Bash"  — "Bash" is a built-in CC tool name, not an MCP server
  "Read"  — "Read" is a built-in CC tool name, not an MCP server

TYPO (1)
  supbase  — ≈ "supabase" (distance 1)

UNCLASSIFIED (3)
  — no pattern matched, manual review
  old-analytics-server
  legacy-pdf-tool
  some-fork-of-thing

Full docs and subcommand reference →

07 companion

Same four buckets, applied to memory.

Same four-bucket model, applied to per-project file-based memory — the MEMORY.md index + memory/*.md entries that auto-load into Claude's system prompt. Surfaces dead entries, broken pointers, and (uniquely) entries below the truncation cutoff that Claude can't see until you ask for them.

one-off

Same install pattern as skill-graveyard.

$npx memory-graveyard@latest
global cli

Installs the binary on your PATH.

$npm i -g memory-graveyard
skills.sh

Install as an Agent Skill — Claude auto-discovers it in any session. Same command also installs skill-graveyard and mcp-graveyard.

$npx skills add sfrangulov/skill-graveyard
subcommands

Four subcommands mirror the mcp-graveyard surface.

$npx memory-graveyard@latest lint
audit [--days N] [--only active|dead|missing|hallucinated] [--json]

Default command. Reads the project's MEMORY.md index and cross-references every memory/*.md file against session logs to sort entries into the four buckets — active (indexed and read successfully), dead (indexed but never read), missing (read but not indexed), hallucinated (read and errored). Includes per-entry line numbers for pinpoint triage.

~ $ memory-graveyard --days 30
memory-graveyard — 30 days · 14 entries indexed · 16 on disk · 53 reads · 47 succeeded · 6 errored

ACTIVE (4)  indexed and read — keep
  entry                             reads  errors  last         line
  feedback_release_flow.md          18     0       2026-05-02   12
  project_clientco_platform.md       9     0       2026-05-01   10
  feedback_subagent_models.md        8     0       2026-04-30    8
  project_skill_graveyard.md        12     0       2026-04-29    6

DEAD (8)  indexed, 0 reads in 30d — removal candidates
  arch_decisions_2025.md             0     0       —             3
  onboarding_notes.md                0     0       —            14
  retro_q1.md                        0     0       —            20

HALLUCINATED (2)  entry read but errored — file missing or pointer broken
  feedback_doesnotexist.md           0     2       2026-04-15   34
  → memory-graveyard lint    for static checks including broken pointers

MISSING (2)  on disk but not indexed in MEMORY.md — orphan files
  scratch_20260410.md                1     0       2026-04-10    —
  → memory-graveyard lint    for actionable classification

→ run: memory-graveyard prune  to clear DEAD entries and broken pointers
lint [--json]

Five static checks: broken pointers (indexed entries whose files are missing), orphan files (on-disk entries not in the index), truncation budget (entries that fall below Claude's context window cutoff and are invisible until explicitly requested), index size (total token weight of all indexed entries), and stale dated entries (entries with an embedded date header older than 90 days). Exit code 1 if any check fails.

~ $ memory-graveyard lint
memory-graveyard lint

BROKEN_POINTER (1)
  feedback_doesnotexist.md   — indexed at line 34 but file not found on disk

TRUNCATION_BUDGET (3)
  arch_decisions_2025.md     — 4 312 tok, starts at token 94 208 of 100 000 limit
  onboarding_notes.md        — 2 107 tok, starts at token 96 320 of 100 000 limit
  retro_q1.md                — 1 844 tok, starts at token 98 427 of 100 000 limit

INDEX_SIZE  total 98 271 tok — within budget (limit 100 000)

OK  ORPHAN (0)    STALE_DATE (0)

→ run: memory-graveyard prune  to fix broken pointers and remove dead entries
prune [--apply] [--only <entry>] [--days N]

Reads the audit and emits a removal plan for DEAD entries and broken pointers. Print-only by default. --apply writes a snapshot backup of the full memory/ directory to ~/.claude/memory-graveyard-backup/<ISO>/, then removes the dead entries from MEMORY.md and deletes the corresponding .md files. Continues on individual failures.

~ $ memory-graveyard prune
memory-graveyard prune — plan: 8 entries to remove (0 reads in 30 days)

  arch_decisions_2025.md            remove from MEMORY.md + delete file
  onboarding_notes.md               remove from MEMORY.md + delete file
  retro_q1.md                       remove from MEMORY.md + delete file
  feedback_doesnotexist.md          remove broken pointer from MEMORY.md

re-run with --apply to execute (snapshot backup is automatic)
projects [--days N] [--json]

Cross-project sweep. Groups every memory read by the cwd recorded in the session log and surfaces the per-project memory health — active entries, dead weight, and truncation pressure — across all projects in ~/.claude/projects/.

~ $ memory-graveyard projects
~/projects/client-analytics  12 entries indexed · 39 reads · 4 dead · 0 hallucinated
    feedback_release_flow.md     18×
    project_skill_graveyard.md   12×

~/projects/clientco/web-platform  8 entries indexed · 4 reads · 6 dead · 2 hallucinated
   feedback_doesnotexist.md    2× (2 hallucinated)

~/projects/landing  3 entries indexed · 0 reads · 3 dead · 0 hallucinated
  all 3 entries dead — consider clearing this project's memory

Full docs and subcommand reference →