fix(gen-skill-docs): keep module sync so test require() still works

Two regressions caught by the full test suite after the v1.28.0.0
landing pass:

1) package.json version mismatch — VERSION was bumped to 1.28.0.0
   but package.json still pinned to 1.27.1.0.
   test/gen-skill-docs.test.ts asserts they match.

2) Top-level await in scripts/gen-llms-txt.ts (CLI entry block) and
   scripts/gen-skill-docs.ts (post-step) made gen-skill-docs an
   async module. test/gen-skill-docs.test.ts uses require() to pull
   extractVoiceTriggers/processVoiceTriggers from gen-skill-docs,
   which Bun rejects on async modules with:
     "TypeError: require() async module ... unsupported.
      use 'await import()' instead."

Fix: wrap the await blocks in void IIFEs so the modules remain sync
from a require() perspective.

After fix: all 379 gen-skill-docs tests pass, all 77 new feature
tests pass (3 skipped on macOS — Linux+Xvfb gates).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-05-07 14:16:51 -07:00
parent 95268abb87
commit 412a996f1b
3 changed files with 40 additions and 29 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "gstack",
"version": "1.27.1.0",
"version": "1.28.0.0",
"description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.",
"license": "MIT",
"type": "module",

View File

@@ -230,24 +230,30 @@ export async function writeLlmsTxt(opts: GenerateOptions & { outputPath?: string
}
// ─── CLI entry ──────────────────────────────────────────────
// Wrapped in an IIFE so top-level await doesn't make this module async-by-
// import (which would break require() consumers like
// test/gen-skill-docs.test.ts that pull writeLlmsTxt indirectly via
// gen-skill-docs).
if (import.meta.main) {
const strict = process.argv.includes('--strict');
const dryRun = process.argv.includes('--dry-run');
const result = dryRun
? await generateLlmsTxt({ strict })
: await writeLlmsTxt({ strict });
void (async () => {
const strict = process.argv.includes('--strict');
const dryRun = process.argv.includes('--dry-run');
const result = dryRun
? await generateLlmsTxt({ strict })
: await writeLlmsTxt({ strict });
for (const w of result.warnings) console.error(`[gen-llms-txt] WARN: ${w}`);
for (const w of result.warnings) console.error(`[gen-llms-txt] WARN: ${w}`);
if (dryRun) {
const existing = fs.existsSync(OUTPUT) ? fs.readFileSync(OUTPUT, 'utf-8') : '';
if (existing !== result.content) {
console.error('[gen-llms-txt] OUT OF DATE — run `bun run gen:skill-docs` to regenerate gstack/llms.txt');
process.exit(1);
if (dryRun) {
const existing = fs.existsSync(OUTPUT) ? fs.readFileSync(OUTPUT, 'utf-8') : '';
if (existing !== result.content) {
console.error('[gen-llms-txt] OUT OF DATE — run `bun run gen:skill-docs` to regenerate gstack/llms.txt');
process.exit(1);
}
console.log('[gen-llms-txt] up to date');
} else {
console.log(`[gen-llms-txt] wrote ${OUTPUT}`);
console.log(`[gen-llms-txt] skills=${result.skills.length} browse=${result.browseCommands.length} design=${result.designCommands.length}`);
}
console.log('[gen-llms-txt] up to date');
} else {
console.log(`[gen-llms-txt] wrote ${OUTPUT}`);
console.log(`[gen-llms-txt] skills=${result.skills.length} browse=${result.browseCommands.length} design=${result.designCommands.length}`);
}
})();
}

View File

@@ -12,6 +12,7 @@
import { COMMAND_DESCRIPTIONS } from '../browse/src/commands';
import { SNAPSHOT_FLAGS } from '../browse/src/snapshot';
import { discoverTemplates } from './discover-skills';
import { writeLlmsTxt } from './gen-llms-txt';
import * as fs from 'fs';
import * as path from 'path';
import type { Host, TemplateContext } from './resolvers/types';
@@ -665,18 +666,22 @@ if (!DRY_RUN) {
// Regenerate gstack/llms.txt — single-file capability index for AI agents.
// Runs after SKILL.md generation so it sees current skill descriptions and
// browse command list. Freshness is asserted in test/llms-txt-shape.test.ts.
// browse command list. Wrapped in an IIFE so the await-import doesn't make
// this module async (test/gen-skill-docs.test.ts uses require() to pull
// extractVoiceTriggers/processVoiceTriggers, which fails on async modules).
// Freshness is asserted in test/llms-txt-shape.test.ts.
if (!DRY_RUN) {
try {
const { writeLlmsTxt } = await import('./gen-llms-txt');
const result = await writeLlmsTxt();
if (result.warnings.length > 0) {
for (const w of result.warnings) console.error(`[gen-llms-txt] WARN: ${w}`);
} else {
console.log(`[gen-llms-txt] gstack/llms.txt: ${result.skills.length} skills, ${result.browseCommands.length} browse commands`);
void (async () => {
try {
const result = await writeLlmsTxt();
if (result.warnings.length > 0) {
for (const w of result.warnings) console.error(`[gen-llms-txt] WARN: ${w}`);
} else {
console.log(`[gen-llms-txt] gstack/llms.txt: ${result.skills.length} skills, ${result.browseCommands.length} browse commands`);
}
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
console.error(`[gen-llms-txt] FAILED: ${msg}`);
}
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
console.error(`[gen-llms-txt] FAILED: ${msg}`);
}
})();
}