Skip to content

fix(skills): derive paths from nested Claude Code skill roots on import - #2574

Merged
dyoshikawa merged 2 commits into
mainfrom
resolve-issue-2556-nested-skill-paths
Aug 4, 2026
Merged

fix(skills): derive paths from nested Claude Code skill roots on import#2574
dyoshikawa merged 2 commits into
mainfrom
resolve-issue-2556-nested-skill-paths

Conversation

@dyoshikawa

Copy link
Copy Markdown
Owner

Background

Closes #2556.

Claude Code v2.1.178+ loads skills from nested .claude/skills/ directories, and scopes them by location: a skill in apps/web/.claude/skills/deploy is only available while working under apps/web. rulesync import discovers those nested roots, but toRulesyncSkill() emitted them identically to a root skill. Since generation always targets the project-root .claude/skills/, a later generate rewrote the nested skill to the root with global activation (and left the nested original in place, so Claude Code then saw both under qualified names).

Solution

On import, derive the subtree glob from the discovering root and write it as claudecode.paths.

The nested root is already carried on the skill instance: getConfiguredImportRoots returns each root's relativeDirPath (e.g. apps/web/.claude/skills), skills-processor passes it into fromDir, and loadSkillDirContent stores it on the ToolSkill. So no new plumbing was needed — toRulesyncSkill() derives the glob from this.relativeDirPath via the new exported deriveNestedSkillPaths() helper.

Behavior (documented in docs/reference/file-formats.md):

  • A skill discovered under <subtree>/.claude/skills gets paths: ["<subtree>/**"].
  • A paths value the author already declared in frontmatter is kept verbatim and never overwritten (including the empty-string / empty-array forms already covered by tests).
  • Root-discovered skills (.claude/skills) are unchanged — no paths is added.
  • Any root that is not a .claude/skills directory (e.g. .claude/scheduled-tasks, nested or not) derives nothing.

The flat-namespace clash rule is untouched: on a name clash the root skill still wins the import, so a derived paths can never shadow a root skill.

Testing

  • Added unit tests for deriveNestedSkillPaths() and for the toRulesyncSkill() cases above (nested root derives, explicit paths preserved, root skill unchanged, nested scheduled-tasks root unchanged).
  • npx vitest run --config vitest.e2e.config.ts src/e2e/e2e-skills.spec.ts — 130 passed.
  • pnpm cicheck — all green.

🤖 Generated with Claude Code

@dyoshikawa
dyoshikawa merged commit c159039 into main Aug 4, 2026
9 checks passed
@dyoshikawa
dyoshikawa deleted the resolve-issue-2556-nested-skill-paths branch August 4, 2026 03:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Imported nested Claude Code skills lose their subtree scoping (no paths auto-population)

2 participants