Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Changelog

## [1.48.1.0] - 2026-05-28

**`/setup` now mirrors support files and asset directories alongside SKILL.md so skills can read their own assets without path hacks.**

Skills like `/review` and `/qa` ship with sidecars — `checklist.md`, `specialists/`, `bin/` — that their SKILL.md reads at runtime via `.claude/skills/<skill>/<file>`. Before this fix, `link_claude_skill_dirs` only symlinked `SKILL.md`, leaving every sidecar unreachable. Now `setup` iterates over sibling `.md` files and qualifying subdirectories and calls `_link_or_copy` for each, preserving Windows compatibility.

### Itemized changes

#### Fixed
- `setup`: `link_claude_skill_dirs` now mirrors supporting `.md` files and subdirectories via `_link_or_copy` — fixes #1499

#### Added
- `test/gen-skill-docs.test.ts`: two new invariant tests asserting support file and directory mirroring

## [1.48.0.0] - 2026-05-26

## **Agents stop dropping AskUserQuestion options when there are 5+.** A new canonical preamble rule + runtime gate makes Conductor's 4-option cap a split-or-batch decision, not a silent trim.
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.48.0.0
1.48.1.0
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "gstack",
"version": "1.48.0.0",
"version": "1.48.1.0",
"description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.",
"license": "MIT",
"type": "module",
Expand Down
22 changes: 22 additions & 0 deletions setup
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,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
_link_or_copy "$gstack_dir/$dir_name/SKILL.md" "$target/SKILL.md"
# Mirror supporting .md files (checklist.md, greptile-triage.md, etc.)
# so SKILL.md can read them via .claude/skills/<skill>/<file>.
# Excludes SKILL.md (already linked above) and *.tmpl 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
_link_or_copy "$support_file" "$target/$fname"
done
# Mirror 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
_link_or_copy "$gstack_dir/$dir_name/$sname" "$target/$sname"
done
linked+=("$link_name")
fi
done
Expand Down
27 changes: 27 additions & 0 deletions test/gen-skill-docs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2287,6 +2287,33 @@ describe('setup script validation', () => {
expect(claudeSection).toContain('link_claude_root_skill_alias "$SOURCE_GSTACK_DIR" "$INSTALL_SKILLS_DIR"');
});

// 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/<skill>/<file> without requiring gstack-prefixed paths.
test('link_claude_skill_dirs mirrors 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 use _link_or_copy (not raw ln -snf) for Windows compatibility
expect(fnBody).toContain('_link_or_copy "$support_file" "$target/$fname"');
});

test('link_claude_skill_dirs mirrors 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 use _link_or_copy (not raw ln -snf) for Windows compatibility
expect(fnBody).toContain('_link_or_copy "$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');
Expand Down