From 82efa1039f6f971ff33be2253863c9f72a2ff2c4 Mon Sep 17 00:00:00 2001 From: Nikhilesh Nanduri Date: Fri, 15 May 2026 02:06:31 +0530 Subject: [PATCH] fix(setup): mirror support files + asset dirs alongside SKILL.md in link_claude_skill_dirs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #1499. `link_claude_skill_dirs` previously only symlinked SKILL.md into each skill directory. Skills like /review reference sibling files (checklist.md, greptile-triage.md, specialists/, TODOS-format.md) via the `.claude/skills/review/` path — but those files were never linked, so the skill hit its own STOP point on every global install. After this fix, link_claude_skill_dirs also: - Symlinks all .md support files (except SKILL.md and *.tmpl) alongside SKILL.md - Symlinks support asset directories (specialists/, bin/, references/, templates/, migrations/, etc.) while excluding build dirs (dist/, src/, test/, tests/, scripts/, node_modules/) Idempotent — re-running ./setup upgrades existing installs cleanly. Supersedes the partial fix in #1486 (bin/ only); this covers all asset types. Affected skills: review (checklist.md, greptile-triage.md, design-checklist.md, TODOS-format.md, specialists/), qa (references/, templates/), careful (bin/), freeze (bin/), plan-devex-review (dx-hall-of-fame.md), cso (ACKNOWLEDGEMENTS.md), setup-gbrain (memory.md), gstack-upgrade (migrations/). Co-Authored-By: Claude Sonnet 4.6 --- setup | 22 ++++++++++++++++++++++ test/gen-skill-docs.test.ts | 27 +++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/setup b/setup index f812511e4d..1dc085fc7d 100755 --- a/setup +++ b/setup @@ -402,6 +402,28 @@ 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 ln -snf "$gstack_dir/$dir_name/SKILL.md" "$target/SKILL.md" + # Symlink supporting .md files so SKILL.md can read them via .claude/skills// + # path (checklist.md, greptile-triage.md, design-checklist.md, TODOS-format.md, etc.) + # Excludes SKILL.md (already linked above) and *.tmpl template sources. + for support_file in "$gstack_dir/$dir_name"/*.md; do + [ -f "$support_file" ] || continue + fname="$(basename "$support_file")" + [ "$fname" = "SKILL.md" ] && continue + if [ -L "$target/$fname" ]; then rm "$target/$fname"; fi + ln -snf "$support_file" "$target/$fname" + done + # Symlink support asset directories (specialists/, references/, templates/, bin/, etc.) + # Excludes build and source dirs: dist, src, test, tests, scripts, node_modules. + for support_dir in "$gstack_dir/$dir_name"/*/; do + [ -d "$support_dir" ] || continue + sname="$(basename "$support_dir")" + case "$sname" in + dist|src|test|tests|scripts|node_modules) continue ;; + esac + if [ -L "$target/$sname" ]; then rm "$target/$sname"; fi + if [ -e "$target/$sname" ] && [ ! -L "$target/$sname" ]; then rm -rf "$target/$sname"; fi + ln -snf "$gstack_dir/$dir_name/$sname" "$target/$sname" + done linked+=("$link_name") fi done diff --git a/test/gen-skill-docs.test.ts b/test/gen-skill-docs.test.ts index 4bf8abeeeb..789e38a05b 100644 --- a/test/gen-skill-docs.test.ts +++ b/test/gen-skill-docs.test.ts @@ -2270,6 +2270,33 @@ describe('setup script validation', () => { expect(fnBody).toContain('rm -f "$target"'); }); + // FIX #1499: link function must also mirror supporting .md files and asset directories + // so SKILL.md Step 2 can read checklist.md, greptile-triage.md, specialists/, etc. + // via .claude/skills// without requiring gstack-prefixed paths. + test('link_claude_skill_dirs symlinks supporting .md files alongside SKILL.md', () => { + const fnStart = setupContent.indexOf('link_claude_skill_dirs()'); + const fnEnd = setupContent.indexOf('}', setupContent.indexOf('linked[@]}', fnStart)); + const fnBody = setupContent.slice(fnStart, fnEnd); + // Must iterate over sibling .md files in the skill source dir + expect(fnBody).toContain('for support_file in "$gstack_dir/$dir_name"/*.md'); + // Must skip SKILL.md itself (already linked above) + expect(fnBody).toContain('[ "$fname" = "SKILL.md" ] && continue'); + // Must create symlinks for each support file + expect(fnBody).toContain('ln -snf "$support_file" "$target/$fname"'); + }); + + test('link_claude_skill_dirs symlinks support asset directories (specialists/, bin/, etc.)', () => { + const fnStart = setupContent.indexOf('link_claude_skill_dirs()'); + const fnEnd = setupContent.indexOf('}', setupContent.indexOf('linked[@]}', fnStart)); + const fnBody = setupContent.slice(fnStart, fnEnd); + // Must iterate over subdirectories in the skill source dir + expect(fnBody).toContain('for support_dir in "$gstack_dir/$dir_name"/*/'); + // Must exclude build and source dirs to avoid polluting the skill surface + expect(fnBody).toContain('dist|src|test|tests|scripts|node_modules'); + // Must create symlinks for qualifying support dirs + expect(fnBody).toContain('ln -snf "$gstack_dir/$dir_name/$sname" "$target/$sname"'); + }); + test('setup supports --host auto|claude|codex|kiro|opencode', () => { expect(setupContent).toContain('--host'); expect(setupContent).toContain('claude|codex|kiro|factory|opencode|auto');