Skip to content

Commit 82efa10

Browse files
fix(setup): mirror support files + asset dirs alongside SKILL.md in link_claude_skill_dirs
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 <noreply@anthropic.com>
1 parent 40e34de commit 82efa10

2 files changed

Lines changed: 49 additions & 0 deletions

File tree

setup

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,28 @@ link_claude_skill_dirs() {
402402
# Validate target isn't a symlink before creating the link
403403
if [ -L "$target/SKILL.md" ]; then rm "$target/SKILL.md"; fi
404404
ln -snf "$gstack_dir/$dir_name/SKILL.md" "$target/SKILL.md"
405+
# Symlink supporting .md files so SKILL.md can read them via .claude/skills/<skill>/
406+
# path (checklist.md, greptile-triage.md, design-checklist.md, TODOS-format.md, etc.)
407+
# Excludes SKILL.md (already linked above) and *.tmpl template sources.
408+
for support_file in "$gstack_dir/$dir_name"/*.md; do
409+
[ -f "$support_file" ] || continue
410+
fname="$(basename "$support_file")"
411+
[ "$fname" = "SKILL.md" ] && continue
412+
if [ -L "$target/$fname" ]; then rm "$target/$fname"; fi
413+
ln -snf "$support_file" "$target/$fname"
414+
done
415+
# Symlink support asset directories (specialists/, references/, templates/, bin/, etc.)
416+
# Excludes build and source dirs: dist, src, test, tests, scripts, node_modules.
417+
for support_dir in "$gstack_dir/$dir_name"/*/; do
418+
[ -d "$support_dir" ] || continue
419+
sname="$(basename "$support_dir")"
420+
case "$sname" in
421+
dist|src|test|tests|scripts|node_modules) continue ;;
422+
esac
423+
if [ -L "$target/$sname" ]; then rm "$target/$sname"; fi
424+
if [ -e "$target/$sname" ] && [ ! -L "$target/$sname" ]; then rm -rf "$target/$sname"; fi
425+
ln -snf "$gstack_dir/$dir_name/$sname" "$target/$sname"
426+
done
405427
linked+=("$link_name")
406428
fi
407429
done

test/gen-skill-docs.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2270,6 +2270,33 @@ describe('setup script validation', () => {
22702270
expect(fnBody).toContain('rm -f "$target"');
22712271
});
22722272

2273+
// FIX #1499: link function must also mirror supporting .md files and asset directories
2274+
// so SKILL.md Step 2 can read checklist.md, greptile-triage.md, specialists/, etc.
2275+
// via .claude/skills/<skill>/<file> without requiring gstack-prefixed paths.
2276+
test('link_claude_skill_dirs symlinks supporting .md files alongside SKILL.md', () => {
2277+
const fnStart = setupContent.indexOf('link_claude_skill_dirs()');
2278+
const fnEnd = setupContent.indexOf('}', setupContent.indexOf('linked[@]}', fnStart));
2279+
const fnBody = setupContent.slice(fnStart, fnEnd);
2280+
// Must iterate over sibling .md files in the skill source dir
2281+
expect(fnBody).toContain('for support_file in "$gstack_dir/$dir_name"/*.md');
2282+
// Must skip SKILL.md itself (already linked above)
2283+
expect(fnBody).toContain('[ "$fname" = "SKILL.md" ] && continue');
2284+
// Must create symlinks for each support file
2285+
expect(fnBody).toContain('ln -snf "$support_file" "$target/$fname"');
2286+
});
2287+
2288+
test('link_claude_skill_dirs symlinks support asset directories (specialists/, bin/, etc.)', () => {
2289+
const fnStart = setupContent.indexOf('link_claude_skill_dirs()');
2290+
const fnEnd = setupContent.indexOf('}', setupContent.indexOf('linked[@]}', fnStart));
2291+
const fnBody = setupContent.slice(fnStart, fnEnd);
2292+
// Must iterate over subdirectories in the skill source dir
2293+
expect(fnBody).toContain('for support_dir in "$gstack_dir/$dir_name"/*/');
2294+
// Must exclude build and source dirs to avoid polluting the skill surface
2295+
expect(fnBody).toContain('dist|src|test|tests|scripts|node_modules');
2296+
// Must create symlinks for qualifying support dirs
2297+
expect(fnBody).toContain('ln -snf "$gstack_dir/$dir_name/$sname" "$target/$sname"');
2298+
});
2299+
22732300
test('setup supports --host auto|claude|codex|kiro|opencode', () => {
22742301
expect(setupContent).toContain('--host');
22752302
expect(setupContent).toContain('claude|codex|kiro|factory|opencode|auto');

0 commit comments

Comments
 (0)