{"record":{"id":"980886505294fc05","repo":"garrytan/gstack","slug":"skill-name-has-no-script-test-ts-at-testfil","errorCode":null,"errorMessage":"Skill \"${name}\" has no script.test.ts at ${testFile}","messagePattern":"Skill \"(.+?)\" has no script\\.test\\.ts at (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"browse/src/browser-skill-commands.ts","lineNumber":185,"sourceCode":"    const err = new Error(`Skill \"${name}\" failed: ${summary}\\n--- stderr ---\\n${result.stderr.slice(0, 4096)}`);\n    (err as any).exitCode = result.exitCode || 1;\n    throw err;\n  }\n  return result.stdout;\n}\n\n// ─── test ───────────────────────────────────────────────────────\n\nasync function handleTest(args: string[], ctx: SkillCommandContext): Promise<string> {\n  const name = args[0];\n  if (!name) throw new Error('Usage: $B skill test <name>');\n  const tiers = ctx.tiers ?? defaultTierPaths();\n  const skill = readBrowserSkill(name, tiers);\n  if (!skill) throw new Error(`Skill \"${name}\" not found.`);\n\n  const testFile = path.join(skill.dir, 'script.test.ts');\n  if (!fs.existsSync(testFile)) {\n    throw new Error(`Skill \"${name}\" has no script.test.ts at ${testFile}`);\n  }\n\n  const proc = Bun.spawn(['bun', 'test', testFile], {\n    cwd: skill.dir,\n    stdout: 'pipe',\n    stderr: 'pipe',\n    env: process.env,\n  });\n  const exitCode = await proc.exited;\n  const stdout = proc.stdout ? await new Response(proc.stdout).text() : '';\n  const stderr = proc.stderr ? await new Response(proc.stderr).text() : '';\n  if (exitCode !== 0) {\n    throw new Error(`Skill \"${name}\" tests failed (exit ${exitCode}).\\n${stderr}`);\n  }\n  return stderr || stdout || `tests passed for \"${name}\"`;\n}\n\n// ─── rm ─────────────────────────────────────────────────────────","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/browse/src/browser-skill-commands.ts#L167-L203","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: <skill.dir>/ contains SKILL.md, script.ts only\n// after: add <skill.dir>/script.test.ts\nimport { test, expect } from 'bun:test';\ntest('skill runs', async () => { expect(true).toBe(true); });","handlingStrategy":"validation","validationCode":"import * as fs from 'fs';\nimport * as path from 'path';\nimport { readBrowserSkill } from './browser-skills';\n\nfunction assertTestFile(name: string): string {\n  const skill = readBrowserSkill(name);\n  if (!skill) throw new Error(`Skill \"${name}\" not found.`);\n  const testFile = path.join(skill.dir, 'script.test.ts');\n  if (!fs.existsSync(testFile)) {\n    throw new Error(`Skill \"${name}\" has no script.test.ts at ${testFile}`);\n  }\n  return testFile;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["skill-management","filesystem","testing","gstack"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}