Plugin SKILL.md scan should be scoped to declared skill paths, not recursive

Resolved 💬 3 comments Opened May 9, 2026 by nicksonnenberg Closed May 13, 2026

Problem

Claude Code's harness scans plugin caches for SKILL.md more aggressively than the plugin spec implies. When a plugin ships duplicate SKILL.md files outside the canonical skills/<name>/ path (intentionally or otherwise), each gets registered as the same skill name. The second registration silently drops the description in the available-skills list — keyword auto-trigger breaks but the skill remains invocable via the Skill tool.

Real-world examples

  • vercel/vercel-plugin ships skills/<name>/upstream/SKILL.md for 10 customized skills (preserved upstream-source copies). Both register; second loses description.
  • JuliusBrussee/caveman ships SKILL.md files in skills/, plugins/caveman/skills/, .cursor/skills/, .windsurf/skills/, plus top-level dirs. Each skill registers 4-5 times.

In one user's setup this dropped descriptions for ~30 skills, blocking auto-routing for many vercel and caveman skills until manual cache cleanup.

Suggested fix

Option A (strict): only scan <plugin_root>/skills/<name>/SKILL.md (one level deep under skills/). Reject anything deeper.

Option B (declare): respect a skills: field in plugin.json listing exact paths; scan only those.

Option C (ignore-list): respect a .claudeignore or skip directories named upstream, .cursor, .windsurf by default.

Option A is cleanest and matches the documented plugin layout.

Detection

find ~/.claude/plugins/cache -name SKILL.md \
  | awk '{n=split($0,p,"/"); print p[n-1]}' \
  | sort | uniq -c | sort -rn | awk '$1>1'

Same skill name appearing >1 → silent description-drop on second-pass registration.

Workaround

Local SessionStart hook prunes known-bad paths. Cache repopulates on plugin update so the hook re-runs each session.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗