garrytan/gstack · error · Error
Skill "${name}" has no script.test.ts at ${testFile}
Error message
Skill "${name}" has no script.test.ts at ${testFile} What it means
Thrown by handleTest after the skill resolved successfully but path.join(skill.dir, 'script.test.ts') does not exist (fs.existsSync returned false). The skill is registered and its SKILL.md parses, but it has no bun-testable test file. Bundled or hand-written skills without tests hit this; /skillify-authored skills always include one.
Source
Thrown at browse/src/browser-skill-commands.ts:185
const err = new Error(`Skill "${name}" failed: ${summary}\n--- stderr ---\n${result.stderr.slice(0, 4096)}`);
(err as any).exitCode = result.exitCode || 1;
throw err;
}
return result.stdout;
}
// ─── test ───────────────────────────────────────────────────────
async function handleTest(args: string[], ctx: SkillCommandContext): Promise<string> {
const name = args[0];
if (!name) throw new Error('Usage: $B skill test <name>');
const tiers = ctx.tiers ?? defaultTierPaths();
const skill = readBrowserSkill(name, tiers);
if (!skill) throw new Error(`Skill "${name}" not found.`);
const testFile = path.join(skill.dir, 'script.test.ts');
if (!fs.existsSync(testFile)) {
throw new Error(`Skill "${name}" has no script.test.ts at ${testFile}`);
}
const proc = Bun.spawn(['bun', 'test', testFile], {
cwd: skill.dir,
stdout: 'pipe',
stderr: 'pipe',
env: process.env,
});
const exitCode = await proc.exited;
const stdout = proc.stdout ? await new Response(proc.stdout).text() : '';
const stderr = proc.stderr ? await new Response(proc.stderr).text() : '';
if (exitCode !== 0) {
throw new Error(`Skill "${name}" tests failed (exit ${exitCode}).\n${stderr}`);
}
return stderr || stdout || `tests passed for "${name}"`;
}
// ─── rm ─────────────────────────────────────────────────────────View on GitHub (pinned to 94993f7401)
Solutions
- Confirm the skill is meant to be testable; not all skills ship tests.
- If tests are expected, add `<skill.dir>/script.test.ts` (bun test format).
- Re-run /skillify to regenerate the skill with its test file.
- Restore the full directory from <tier>/.tombstones/ if the restore was partial.
Example fix
// before: <skill.dir>/ contains SKILL.md, script.ts only
// after: add <skill.dir>/script.test.ts
import { test, expect } from 'bun:test';
test('skill runs', async () => { expect(true).toBe(true); }); Defensive patterns
Strategy: validation
Validate before calling
import * as fs from 'fs';
import * as path from 'path';
import { readBrowserSkill } from './browser-skills';
function assertTestFile(name: string): string {
const skill = readBrowserSkill(name);
if (!skill) throw new Error(`Skill "${name}" not found.`);
const testFile = path.join(skill.dir, 'script.test.ts');
if (!fs.existsSync(testFile)) {
throw new Error(`Skill "${name}" has no script.test.ts at ${testFile}`);
}
return testFile;
} Prevention
- Ship script.test.ts alongside script.ts in every authorable skill.
- /skillify-authored skills always include tests; prefer /skillify over hand authoring.
- After restoring a tombstone, verify the full file tree is present.
When it happens
Trigger: handleTest for a skill whose directory lacks script.test.ts. Common for bundled skills shipped only with SKILL.md + script.ts, or for skills partially restored from a tombstone.
Common situations: User runs `$B skill test` on a skill that was never given tests; a skill was hand-authored without a test file; a /skillify run was interrupted after SKILL.md/script.ts were written but before script.test.ts landed; tombstone restore copied an incomplete tree.
Related errors
- Skill "${name}" not found in any tier.
- Skill "${name}" not found.
- Usage: $B skill test <name>
- Skill "${name}" tests failed (exit ${exitCode}).\n${stderr}
- Skill "${opts.skill.name}" missing script.ts at ${scriptPath
AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12).
Data as JSON: /api/errors/980886505294fc05.
Report an issue: GitHub.