diff --git a/design-shotgun/SKILL.md b/design-shotgun/SKILL.md index 6c3b3f07..080754e6 100644 --- a/design-shotgun/SKILL.md +++ b/design-shotgun/SKILL.md @@ -594,6 +594,7 @@ After all agents complete: image list from whatever variant files actually exist, not a hardcoded A/B/C list: ```bash +setopt +o nomatch 2>/dev/null || true # zsh compat _IMAGES=$(ls "$_DESIGN_DIR"/variant-*.png 2>/dev/null | tr '\n' ',' | sed 's/,$//') ``` diff --git a/design-shotgun/SKILL.md.tmpl b/design-shotgun/SKILL.md.tmpl index 3e3984e2..436c8bc6 100644 --- a/design-shotgun/SKILL.md.tmpl +++ b/design-shotgun/SKILL.md.tmpl @@ -246,6 +246,7 @@ After all agents complete: image list from whatever variant files actually exist, not a hardcoded A/B/C list: ```bash +setopt +o nomatch 2>/dev/null || true # zsh compat _IMAGES=$(ls "$_DESIGN_DIR"/variant-*.png 2>/dev/null | tr '\n' ',' | sed 's/,$//') ``` diff --git a/package.json b/package.json index 7d68f04d..55f7a9fb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gstack", - "version": "0.13.0.0", + "version": "0.13.3.0", "description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.", "license": "MIT", "type": "module", diff --git a/test/gen-skill-docs.test.ts b/test/gen-skill-docs.test.ts index 3091429f..3bbc1869 100644 --- a/test/gen-skill-docs.test.ts +++ b/test/gen-skill-docs.test.ts @@ -1276,16 +1276,27 @@ describe('Codex generation (--host codex)', () => { }); // Dynamic discovery of expected Codex skills: all templates except /codex + // Also excludes skills where .agents/skills/{name} is a symlink back to the repo root + // (vendored dev mode — gen-skill-docs skips these to avoid overwriting Claude SKILL.md) const CODEX_SKILLS = (() => { const skills: Array<{ dir: string; codexName: string }> = []; + const isSymlinkLoop = (codexName: string): boolean => { + const agentSkillDir = path.join(ROOT, '.agents', 'skills', codexName); + try { + return fs.realpathSync(agentSkillDir) === fs.realpathSync(ROOT); + } catch { return false; } + }; if (fs.existsSync(path.join(ROOT, 'SKILL.md.tmpl'))) { - skills.push({ dir: '.', codexName: 'gstack' }); + if (!isSymlinkLoop('gstack')) { + skills.push({ dir: '.', codexName: 'gstack' }); + } } for (const entry of fs.readdirSync(ROOT, { withFileTypes: true })) { if (!entry.isDirectory() || entry.name.startsWith('.') || entry.name === 'node_modules') continue; if (entry.name === 'codex') continue; // /codex is excluded from Codex output if (!fs.existsSync(path.join(ROOT, entry.name, 'SKILL.md.tmpl'))) continue; const codexName = entry.name.startsWith('gstack-') ? entry.name : `gstack-${entry.name}`; + if (isSymlinkLoop(codexName)) continue; skills.push({ dir: entry.name, codexName }); } return skills;