From 9791c1800475004059df19d237791a957473662a Mon Sep 17 00:00:00 2001 From: joeyp Date: Wed, 17 Jun 2026 15:07:07 -0700 Subject: [PATCH] fix(setup): link all skill aux files next to SKILL.md, not just sections/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit link_claude_skill_dirs() registered each top-level skill dir with only SKILL.md (+ sections/ since the v2 carve). But a skill's SKILL.md reads its OTHER aux files relative to its own dir — /review does "Read .claude/skills/review/checklist.md" and dispatches review/specialists/*.md, greptile-triage.md, design-checklist.md. Those were left behind in gstack//, so the target dir held only SKILL.md and the reads 404'd. /review aborts at its missing checklist; the same shape hits any aux-bearing skill. On Windows it is most acute: _link_or_copy makes SKILL.md a real COPY whose base dir is the target, never the gstack source. This is the same failure the sections/ special-case already worked around, just not generalized. Replace the sections-only block with a loop that routes EVERY sibling aux entry (except SKILL.md and node_modules) through _link_or_copy — symlink on Unix, fresh copy on Windows that re-syncs on every ./setup. Subsumes the sections/ case. Co-Authored-By: Claude Opus 4.8 (1M context) --- setup | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/setup b/setup index e4d31f881..a1be1463a 100755 --- a/setup +++ b/setup @@ -569,14 +569,22 @@ link_claude_skill_dirs() { # Validate target isn't a symlink before creating the link if [ -L "$target/SKILL.md" ]; then rm "$target/SKILL.md"; fi _link_or_copy "$gstack_dir/$dir_name/SKILL.md" "$target/SKILL.md" - # Link the sections/ subdir for carved skills (v2 plan T9). The prefixed - # Claude skill dir otherwise holds only SKILL.md, so a runtime - # "Read sections/.md" 404s. Route through _link_or_copy so Windows - # gets a fresh copy (and re-copies on every ./setup, refreshing staleness). - if [ -d "$gstack_dir/$dir_name/sections" ]; then - if [ -e "$target/sections" ] || [ -L "$target/sections" ]; then rm -rf "$target/sections"; fi - _link_or_copy "$gstack_dir/$dir_name/sections" "$target/sections" - fi + # Link EVERY sibling aux entry next to SKILL.md, not just sections/. A skill's SKILL.md reads its + # aux files by a path relative to its own dir (e.g. /review's "Read .claude/skills/review/ + # checklist.md", plus specialists/, greptile-triage.md, design-checklist.md; or a carved skill's + # "Read sections/.md"). The target dir otherwise holds only SKILL.md, so those reads 404 — + # on Windows especially, where _link_or_copy makes SKILL.md a real COPY whose base dir is $target, + # not the gstack source. Route each aux entry through _link_or_copy (symlink on Unix, fresh copy on + # Windows that re-syncs on every ./setup). SKILL.md itself and node_modules are skipped. + for _aux in "$gstack_dir/$dir_name"/*; do + [ -e "$_aux" ] || continue + case "$(basename "$_aux")" in + SKILL.md|node_modules) continue ;; + esac + _aux_dst="$target/$(basename "$_aux")" + if [ -e "$_aux_dst" ] || [ -L "$_aux_dst" ]; then rm -rf "$_aux_dst"; fi + _link_or_copy "$_aux" "$_aux_dst" + done linked+=("$link_name") fi done