Slash-command/skill discovery walks node_modules, polluting context with hundreds of fake skills

Resolved 💬 1 comment Opened Apr 27, 2026 by nickmeinhold Closed May 29, 2026

Problem

Claude Code's slash-command / skill discovery recursively walks the directory tree of ~/.claude/commands (or wherever commandsDirectory resolves to) and treats every markdown-ish file (README, CHANGELOG, LICENSE, SECURITY) as a candidate skill. There's no exclusion mechanism for node_modules/, dist/, build/, .history/, etc.

This is a real footgun for anyone whose slash commands shell out to a Node/TypeScript tool: their skills directory contains a package.json for the tool, npm install populates node_modules/, and then every npm package's documentation files get exposed as fake skills.

Concrete example

My setup: ~/.claude/commands is a symlink to ~/git/individuals/nickmeinhold/claude-skills/. That repo has ~16 legitimate .md slash-commands (/ship, /cage-match, /consolidate, etc.) and a package.json for the /slides command which is implemented in TypeScript.

After npm install, the available-skills list injected into every system-reminder ballooned with entries like:

- node_modules:is-docker:readme: is-docker
- node_modules:typescript:README: TypeScript
- node_modules:googleapis:README: <img src="https://avatars0.githubusercontent.com/...
- node_modules:vitest:LICENSE: Vitest core license
- node_modules:node-domexception:.history:README_20210527213345: DOMException
... (~315 entries total)

Cost

  • ~315 fake skill entries, ~80 chars each = ~6,000 tokens per system-reminder injection
  • In a long session with 10+ user turns, that compounds to 60k+ tokens of pure noise
  • The legitimate skills (~30 entries) get visually buried beneath the noise

What I checked before filing

My workaround

Move the slides build (and its node_modules/) into a sibling git repo (~/git/individuals/nickmeinhold/claude-slides/) outside the symlinked discovery tree. Update the slash-command's npx --prefix to point at the new location.

This works but feels architecturally weird — it forces the implementation of a slash-command to live outside the directory you'd naturally store the slash-command in.

Proposed fix

One of:

  1. Auto-exclude conventional dependency/build directories during command discovery: node_modules/, dist/, build/, .history/, coverage/, .git/, target/, __pycache__/, .next/, vendor/. Low-config, high-leverage.
  1. Honor .gitignore within the discovery tree (matching git's semantics). Already-trained intuition for any developer.
  1. Add a commandIgnore / skillIgnore setting in settings.json taking glob patterns:

``json
{
"skillIgnore": ["**/node_modules/**", "**/dist/**", "**/.history/**"]
}
``

Of these, (1) plus (2) seems best — sensible defaults, with .gitignore for project-specific tweaks.

Severity

Medium. Doesn't break functionality, but silently consumes a large fraction of every long session's context budget. Easy to miss because the system-reminder block is collapsed/compressed in the UI.

Environment

  • Claude Code CLI (recent version, late April 2026)
  • macOS Darwin 25.3.0
  • Affected since I added a TypeScript slash-command tool. Estimated impact: any user whose ~/.claude/commands includes a Node/Python project.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗