diff --git a/gstack/llms.txt b/gstack/llms.txt index 50f3c5814..c6ec5f50b 100644 --- a/gstack/llms.txt +++ b/gstack/llms.txt @@ -15,7 +15,6 @@ Conventions: - [/browse](browse/SKILL.md): Fast headless browser for QA testing and site dogfooding. - [/canary](canary/SKILL.md): Post-deploy canary monitoring. - [/careful](careful/SKILL.md): Safety guardrails for destructive commands. -- [/claude](claude/SKILL.md): Claude Code CLI wrapper for non-Claude hosts - three modes. - [/codex](codex/SKILL.md): OpenAI Codex CLI wrapper — three modes. - [/context-restore](context-restore/SKILL.md): Restore working context saved earlier by /context-save. - [/context-save](context-save/SKILL.md): Save working context. @@ -29,7 +28,7 @@ Conventions: - [/document-generate](document-generate/SKILL.md): Generate missing documentation from scratch for a feature, module, or entire project. - [/document-release](document-release/SKILL.md): Post-ship documentation update. - [/freeze](freeze/SKILL.md): Restrict file edits to a specific directory for the session. -- [/gstack](gstack/SKILL.md): Router for the gstack skill suite. +- [/gstack](SKILL.md): Router for the gstack skill suite. - [/gstack-upgrade](gstack-upgrade/SKILL.md): Upgrade gstack to the latest version. - [/guard](guard/SKILL.md): Full safety mode: destructive command warnings + directory-scoped edits. - [/health](health/SKILL.md): Code quality dashboard. diff --git a/scripts/gen-llms-txt.ts b/scripts/gen-llms-txt.ts index 02e55d12d..73939ffd1 100644 --- a/scripts/gen-llms-txt.ts +++ b/scripts/gen-llms-txt.ts @@ -28,6 +28,7 @@ const OUTPUT = path.join(ROOT, 'gstack', 'llms.txt'); interface SkillEntry { name: string; description: string; + href: string; } /** @@ -113,6 +114,11 @@ function oneLine(text: string): string { return first.replace(/\s+/g, ' ').trim(); } +function skillDirForOutput(output: string): string { + const dir = path.dirname(output); + return dir === '.' ? '.' : dir; +} + interface GenerateOptions { /** Override repo root (for tests). */ root?: string; @@ -135,6 +141,9 @@ export async function generateLlmsTxt(opts: GenerateOptions = {}): Promise a.name.localeCompare(b.name)); @@ -167,7 +176,7 @@ export async function generateLlmsTxt(opts: GenerateOptions = {}): Promise { expect(generated.content).toContain('auto-generated'); }); - test('every skill .tmpl in the repo appears in the index', () => { + test('every generated Claude skill appears in the index', () => { const templates = discoverTemplates(ROOT); + const claudeGeneratedTemplates = templates.filter((t) => path.dirname(t.output) !== 'claude'); // Filter to those that successfully parsed (have name + description). expect(generated.skills.length).toBeGreaterThan(0); - expect(generated.skills.length).toBeLessThanOrEqual(templates.length); + expect(generated.skills.length).toBeLessThanOrEqual(claudeGeneratedTemplates.length); + expect(generated.content).not.toContain('[/claude]'); for (const skill of generated.skills) { expect(generated.content).toMatch(new RegExp(`/${skill.name}\\b`)); } }); + test('every skill link points at an existing generated file', () => { + const skillsSection = generated.content.split('## Skills')[1].split('## Browse Commands')[0]; + const links = [...skillsSection.matchAll(/- \[\/[^\]]+\]\(([^)]+)\):/g)].map((m) => m[1]); + expect(links.length).toBe(generated.skills.length); + + for (const href of links) { + expect(fs.existsSync(path.join(ROOT, href)), href).toBe(true); + } + }); + test('every browse command in COMMAND_DESCRIPTIONS appears in the index', () => { expect(generated.browseCommands.length).toBeGreaterThan(0); for (const cmd of generated.browseCommands) {