{"record":{"id":"39258eb6e125dd00","repo":"garrytan/gstack","slug":"skill-opts-skill-name-missing-script-ts-at","errorCode":null,"errorMessage":"Skill \"${opts.skill.name}\" missing script.ts at ${scriptPath}","messagePattern":"Skill \"(.+?)\" missing script\\.ts at (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"browse/src/browser-skill-commands.ts","lineNumber":263,"sourceCode":" * 5. On exit/timeout, revoke the token. Always.\n */\nexport async function spawnSkill(opts: SpawnSkillOptions): Promise<SpawnSkillResult> {\n  const spawnId = generateSpawnId();\n  const tokenInfo = mintSkillToken({\n    skillName: opts.skill.name,\n    spawnId,\n    spawnTimeoutSeconds: opts.timeoutSeconds,\n  });\n\n  try {\n    const env = buildSpawnEnv({\n      trusted: opts.trusted,\n      port: opts.port,\n      skillToken: tokenInfo.token,\n    });\n    const scriptPath = path.join(opts.skill.dir, 'script.ts');\n    if (!fs.existsSync(scriptPath)) {\n      throw new Error(`Skill \"${opts.skill.name}\" missing script.ts at ${scriptPath}`);\n    }\n\n    const proc = Bun.spawn(['bun', 'run', scriptPath, '--', ...opts.skillArgs], {\n      cwd: opts.skill.dir,\n      env,\n      stdout: 'pipe',\n      stderr: 'pipe',\n    });\n\n    let timedOut = false;\n    const killer = setTimeout(() => {\n      timedOut = true;\n      try { proc.kill(); } catch {}\n    }, opts.timeoutSeconds * 1000);\n\n    const stdoutPromise = readCapped(proc.stdout, MAX_STDOUT_BYTES);\n    const stderrPromise = readCapped(proc.stderr, MAX_STDOUT_BYTES);\n","sourceCodeStart":245,"sourceCodeEnd":281,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/browse/src/browser-skill-commands.ts#L245-L281","documentation":"Thrown by spawnSkill (the load-bearing spawner) after a skill was resolved by readBrowserSkill but path.join(skill.dir, 'script.ts') does not exist. spawnSkill is called by handleRun and by /skillify's test-then-commit flow, so this surfaces whenever a registered skill lacks its executable entry. Distinct from SKILL.md-missing (skill wouldn't resolve) and script.test.ts-missing (only the test path checks that).","triggerScenarios":"handleRun on a skill whose directory contains SKILL.md but no script.ts; /skillify invoked against a hand-authored skill missing the entry script; partial tombstone restore; skill directory was edited and script.ts renamed or deleted.","commonSituations":"Bundled skill shipped as documentation only; an agent authored SKILL.md via low-level writes but never created script.ts; file system case-sensitivity mismatch (script.ts vs Script.ts) on a case-insensitive FS mounted on Linux.","solutions":["Confirm script.ts exists: `ls <skill.dir>/script.ts`.","If the skill was hand-authored, add a script.ts that exports a default async function or runs the Playwright flow.","Re-run /skillify to regenerate the skill end-to-end.","On case-insensitive filesystems, verify the exact filename casing is `script.ts`."],"exampleFix":"// before: <skill.dir>/ has SKILL.md only\n// after: create <skill.dir>/script.ts\nimport { browse } from './_lib/client';\nexport default async function main() { /* ... */ }","handlingStrategy":"validation","validationCode":"import * as fs from 'fs';\nimport * as path from 'path';\nimport { readBrowserSkill } from './browser-skills';\n\nfunction assertScriptTs(name: string): string {\n  const skill = readBrowserSkill(name);\n  if (!skill) throw new Error(`Skill \"${name}\" not found.`);\n  const scriptPath = path.join(skill.dir, 'script.ts');\n  if (!fs.existsSync(scriptPath)) {\n    throw new Error(`Skill \"${name}\" missing script.ts at ${scriptPath}`);\n  }\n  return scriptPath;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Every runnable skill must contain script.ts as the entry point.","On case-insensitive filesystems mounted on Linux, verify exact casing (`script.ts`).","Re-run /skillify if script.ts was deleted or renamed."],"tags":["skill-management","filesystem","gstack","browser-skills"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}