Problem
installSkills in packages/agents/src/commands/install.ts (the enumeration loop, ~line 192) iterates content/skills/ entries and skips only _platforms and dotfiles. The shared skills-partials directory content/skills/_partials/ is therefore passed to installSkillEntry, and its .md files are expanded, link-rewritten, and written to the target as ~/.claude/skills/_partials/.
The existing _partials exclusions (preExpandSkillDirectory/collectExpansions ~line 354, writeExpandedSkillDir ~line 380) only match _partials as a child entry during a directory walk; they never fire when _partials is itself the walk root, which is the case here. So the per-skill exclusion works (a _partials/ nested inside a normal skill is skipped), but the top-level shared skills/_partials/ directory is not.
Effect: a stray ~/.claude/skills/_partials/ directory containing duplicated, link-rewritten copies of each shared partial. It has no SKILL.md, so it is not a registered skill — impact is install-tree clutter and a confusing duplicate of content meant to exist only as an include target. Confirmed live: ~/.claude/skills/_partials/acceptance-criteria-scaffold.md and plan-template.md (the latter added by #774) both already exist.
Context
content/skills/ has three reserved underscore-prefixed top-level directories, and they are not interchangeable:
_platforms/ — already skipped by the enumeration loop (handled by dedicated platform-specific logic).
_partials/ — an install-time include target. Its content is inlined into including skills via 11 <!-- include: --> directives; it has zero runtime references. The installed copy is pure redundancy. This is the bug.
_data/ — a runtime dependency. 36 source skills link ../_data/*.md, which the path-rewriter resolves to absolute ~/.claude/skills/_data/... paths the skills read when they execute. It must continue to install.
This distinction is the reason the fix targets _partials by name rather than generalizing to all _-prefixed entries: a blanket entry.startsWith('_') skip would stop installing _data/ and break every skill that reads it.
Proposed approach
Add '_partials' to the skip condition in the installSkills enumeration loop (alongside '_platforms' and the dotfile check), mirroring the per-skill walk exclusion. Do not generalize to an _-prefix skip, per the _data distinction above.
Cleanup of any pre-existing stray ~/.claude/skills/_partials/ is out of scope: dedicated cleanup code would be dead after its first run, and orphans are removed manually. The fix prevents recreation only.
Acceptance criteria
Must have
Problem
installSkillsinpackages/agents/src/commands/install.ts(the enumeration loop, ~line 192) iteratescontent/skills/entries and skips only_platformsand dotfiles. The shared skills-partials directorycontent/skills/_partials/is therefore passed toinstallSkillEntry, and its.mdfiles are expanded, link-rewritten, and written to the target as~/.claude/skills/_partials/.The existing
_partialsexclusions (preExpandSkillDirectory/collectExpansions~line 354,writeExpandedSkillDir~line 380) only match_partialsas a child entry during a directory walk; they never fire when_partialsis itself the walk root, which is the case here. So the per-skill exclusion works (a_partials/nested inside a normal skill is skipped), but the top-level sharedskills/_partials/directory is not.Effect: a stray
~/.claude/skills/_partials/directory containing duplicated, link-rewritten copies of each shared partial. It has noSKILL.md, so it is not a registered skill — impact is install-tree clutter and a confusing duplicate of content meant to exist only as an include target. Confirmed live:~/.claude/skills/_partials/acceptance-criteria-scaffold.mdandplan-template.md(the latter added by #774) both already exist.Context
content/skills/has three reserved underscore-prefixed top-level directories, and they are not interchangeable:_platforms/— already skipped by the enumeration loop (handled by dedicated platform-specific logic)._partials/— an install-time include target. Its content is inlined into including skills via 11<!-- include: -->directives; it has zero runtime references. The installed copy is pure redundancy. This is the bug._data/— a runtime dependency. 36 source skills link../_data/*.md, which the path-rewriter resolves to absolute~/.claude/skills/_data/...paths the skills read when they execute. It must continue to install.This distinction is the reason the fix targets
_partialsby name rather than generalizing to all_-prefixed entries: a blanketentry.startsWith('_')skip would stop installing_data/and break every skill that reads it.Proposed approach
Add
'_partials'to the skip condition in theinstallSkillsenumeration loop (alongside'_platforms'and the dotfile check), mirroring the per-skill walk exclusion. Do not generalize to an_-prefix skip, per the_datadistinction above.Cleanup of any pre-existing stray
~/.claude/skills/_partials/is out of scope: dedicated cleanup code would be dead after its first run, and orphans are removed manually. The fix prevents recreation only.Acceptance criteria
Must have
_partialsentry in the target skills directory (e.g., no~/.claude/skills/_partials/).skills/_partials/directory is treated purely as an include target, consistent with the existing per-skill_partials/exclusion._data/) continue to install unchanged.skills/_partials/directory during install, mirroring the existing per-skill_partialsexclusion test.