{"record":{"id":"b3cb1281e347981b","repo":"JuliusBrussee/caveman","slug":"unknown-directive-id","errorCode":null,"errorMessage":"unknown directive '${id}'","messagePattern":"unknown directive '(.+?)'","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":14826,"sourceCode":"      \"the exact file or symbol.\",\n    ],\n  },\n  \"deferred-tool-loading\": {\n    summary: \"load tool descriptions on demand instead of the full catalog\",\n    lines: [\n      \"Directive: defer tool loading.\",\n      \"Do not pre-declare every available tool on every request. Keep the task's core\",\n      \"tool set declared, and load other tools' full descriptions through a\",\n      \"search-then-load surface (e.g. tool search) only when a task needs them.\",\n    ],\n  },\n};\nfunction directiveBlock(id: string): string {\n  // Object.hasOwn, not a truthiness read: id is user input, and a bare\n  // DIRECTIVES[id] would resolve prototype keys ('constructor', '__proto__')\n  // to non-directive objects (review C8).\n  const d = Object.hasOwn(DIRECTIVES, id) ? DIRECTIVES[id] : undefined;\n  if (!d) throw new Error(`unknown directive '${id}'`); // callers validate first; fail closed anyway\n  return [directiveBegin(id), ...d.lines, directiveEnd(id)].join(\"\\n\");\n}\nfunction installDirectiveNote(file: string, id: string): boolean {\n  return installManagedBlock(file, directiveBegin(id), directiveEnd(id), directiveBlock(id));\n}\nfunction removeDirectiveNote(file: string, id: string): \"removed\" | \"absent\" | \"corrupt\" | \"failed\" {\n  return removeManagedBlock(file, directiveBegin(id), directiveEnd(id));\n}\n\n// ── hard tier: opencode plugin ───────────────────────────────────────────────\n// opencode exposes a real pre-exec command rewrite: a plugin's `tool.execute.before`\n// hook can mutate `output.args.command` for the bash tool before it runs (the docs'\n// own example does exactly this). We ship a small plugin that routes a noisy command\n// through `caveman shrink` — reusing this very CLI's `shrink-hook` decision so the\n// allowlist/skip rules stay in one place. Byte-safe: any failure leaves the command\n// unchanged. (opencode's hook does not fire for subagent/MCP tool calls — sst/opencode\n// #5894/#2319 — so the soft note remains a useful complement there.)\nfunction opencodePluginPath(): string {","sourceCodeStart":14808,"sourceCodeEnd":14844,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/cli/src/index.ts#L14808-L14844","documentation":"directiveBlock() looks up a directive id in the DIRECTIVES table and fails closed when the id is absent (or only present via prototype keys, which Object.hasOwn excludes). The comment notes callers normally validate first, so this is a defensive internal error.","triggerScenarios":"Calling directiveBlock/installDirectiveNote/removeDirectiveNote with an id not present as an own property of DIRECTIVES (e.g. 'constructor', '__proto__', a typo, or a directive removed in a newer version).","commonSituations":"Passing a user-supplied directive name straight through without validating against the known set; prototype-pollution style ids; config referencing a directive from an older CLI release.","solutions":["Validate the id against the exported DIRECTIVES keys before calling","Fix the directive name typo in the invoking code/config","Use Object.hasOwn(DIRECTIVES, id) checks in your own code mirroring the library's guard","Upgrade/downgrade the CLI if the directive existed in a different version"],"exampleFix":"// before\ndirectiveBlock(userInput);\n// after\nif (!Object.hasOwn(DIRECTIVES, userInput)) throw new Error(`unknown directive '${userInput}'`);\ndirectiveBlock(userInput);","handlingStrategy":"validation","validationCode":"const isValidDirective = (id: string) => Object.hasOwn(DIRECTIVES, id);\nif (!isValidDirective(id)) throw new Error(`unknown directive '${id}'`);","typeGuard":"const isValidDirective = (id: string): id is keyof typeof DIRECTIVES => Object.hasOwn(DIRECTIVES, id);","tryCatchPattern":"try { installDirectiveNote(file, id) } catch (e) {\n  if (String(e).startsWith('unknown directive')) { /* list valid ids and correct input */ }\n}","preventionTips":["Always validate user-supplied directive ids against DIRECTIVES keys","Use Object.hasOwn, not truthy indexing, to avoid prototype keys","Whitelist directive names in config parsing"],"tags":["directives","lookup","fail-closed"],"backgroundTag":"invalid-identifier","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}