@saasontools/strauss-kb 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mcp.ts","../src/commands.ts","../src/compose.ts","../src/kb-record.schema.ts","../src/record-types.ts","../src/decision-record.ts","../src/json-schema.ts","../src/kb-log.ts","../src/trace.ts","../src/validate.ts","../src/kb-store.ts","../src/markdown.ts","../src/errors.ts","../src/kb-errors.ts","../src/kb-index.ts","../src/adjudicate.ts","../src/search-index.ts","../src/mcp-main.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { KB_COMMANDS } from \"./commands.js\";\nimport { KbStore } from \"./kb-store.js\";\n\n/**\n * A knowledge base's own MCP server, over stdio.\n *\n * Standalone because a base is self-contained: a directory of markdown that\n * needs no database, no HTTP surface, and no running service to read. Folding\n * these tools into a larger server would make every consumer start that server\n * to open files it could open itself.\n *\n * Every tool is a projection of `KB_COMMANDS`, which the CLI also projects, so\n * the two cannot drift.\n */\nexport function createKbMcpServer(): McpServer {\n const server = new McpServer({ name: \"strauss-kb\", version: \"0.1.0\" });\n const store = new KbStore({\n warn: (entry) => process.stderr.write(`${JSON.stringify(entry)}\\n`),\n });\n const ctx = {\n store,\n actor: process.env.STRAUSS_KB_ACTOR ?? \"mcp\",\n now: () => new Date().toISOString(),\n };\n\n for (const command of KB_COMMANDS) {\n server.registerTool(\n command.tool,\n { description: command.description, inputSchema: command.input.shape },\n async (args: unknown) => {\n const result = await command.run(ctx, command.input.parse(args));\n return {\n content: [\n {\n type: \"text\" as const,\n text:\n typeof result === \"string\"\n ? result\n : JSON.stringify(result, null, 2),\n },\n ],\n };\n },\n );\n }\n\n return server;\n}\n\nexport async function runKbMcpServer(): Promise<void> {\n await createKbMcpServer().connect(new StdioServerTransport());\n}\n","import { z } from \"zod\";\nimport { composeInputSchema, composeRecord } from \"./compose.js\";\nimport {\n composeDecisionRecord,\n composeNoDecisionRecord,\n decisionInputSchema,\n DECISION_TYPE,\n} from \"./decision-record.js\";\nimport { kbJsonSchemas } from \"./json-schema.js\";\nimport {\n KB_RECORD_STATUSES,\n KB_RECORD_TYPES,\n type KbRecordType,\n} from \"./kb-record.schema.js\";\nimport type { KbStore } from \"./kb-store.js\";\nimport { RECORD_TYPES } from \"./record-types.js\";\nimport { TRACE_EDGES } from \"./trace.js\";\nimport { validateBundle } from \"./validate.js\";\n\n/**\n * Every operation a knowledge base exposes, defined once.\n *\n * The CLI and the MCP server are both projections of this list. Kept apart they\n * drift within a day — fourteen commands against six tools — which is the same\n * failure as a schema restated in prose beside the code that enforces it, one\n * level up. A command added here appears in both surfaces or in neither, and a\n * test asserts exactly that.\n *\n * The two differ only in how arguments arrive: MCP passes an object matching\n * `input`, while the CLI has to turn positional argv into the same object.\n * `fromArgv` is that adapter and is the only per-surface code a command needs.\n */\nexport type KbCommandContext = {\n store: KbStore;\n actor: string;\n now: () => string;\n};\n\nexport type KbCommand<Shape extends z.ZodRawShape = z.ZodRawShape> = {\n /** CLI verb. */\n name: string;\n /** MCP tool name. */\n tool: string;\n /** Argument spelling for CLI usage output. */\n usage: string;\n /** Shown to an agent choosing a tool, so it carries the judgment too. */\n description: string;\n input: z.ZodObject<Shape>;\n /** Positional argv → the same object MCP receives. */\n fromArgv(\n argv: string[],\n bundlePath: string,\n stdin: () => Promise<string>,\n ): Promise<unknown> | unknown;\n run(\n ctx: KbCommandContext,\n input: z.infer<z.ZodObject<Shape>>,\n ): Promise<unknown>;\n /**\n * Turns a result into a non-zero exit for the CLI. A check that reports a\n * problem has succeeded as a command and failed as a check, and a shell\n * caller can only see the difference through the exit code.\n */\n failsWhen?(result: unknown): boolean;\n};\n\nconst bundlePath = z\n .string()\n .min(1)\n .describe(\"Absolute path to the knowledge base directory.\");\n\nconst conceptId = z.string().min(1).describe(\"e.g. decision.cursor-v2\");\n\nfunction define<Shape extends z.ZodRawShape>(\n command: KbCommand<Shape>,\n): KbCommand<z.ZodRawShape> {\n return command as unknown as KbCommand<z.ZodRawShape>;\n}\n\nexport const KB_COMMANDS: KbCommand<z.ZodRawShape>[] = [\n define({\n name: \"write\",\n tool: \"kb_write\",\n usage: \"write <type> < record.json\",\n description: [\n \"Write one record. Search first — the same knowledge filed twice under different slugs is how a base rots, and a duplicate concept id is rejected rather than overwritten. Call kb_types for the sections each type accepts.\",\n \"\",\n \"Judgment the tool cannot enforce for you:\",\n \"- An unsourced claim is an `assumption` record with assumption: true, never a `fact` with a vague source. The distinction is what lets a later reader separate what was established from what was guessed.\",\n \"- When two records conflict, say so in a `risk`, an `open-question`, or a superseding `decision`. Quietly picking a winner destroys the disagreement, which is usually the useful part.\",\n \"- Prefer a new record over overloading an existing one, and keep each short. A record nobody finishes reading is not durable memory.\",\n \"- Records are never deleted; supersede instead, so the earlier reasoning stays inspectable.\",\n ].join(\"\\n\"),\n input: z.object({\n bundlePath,\n type: z.enum(KB_RECORD_TYPES),\n input: composeInputSchema,\n }),\n fromArgv: async (argv, path, stdin) => ({\n bundlePath: path,\n type: argv[1],\n input: JSON.parse(await stdin()) as unknown,\n }),\n run: async ({ store, actor, now }, { bundlePath: path, type, input }) => {\n const record = await store.write(\n path,\n composeRecord(type as KbRecordType, input, actor, now()),\n actor,\n );\n return { conceptId: record.conceptId };\n },\n }),\n\n define({\n name: \"write-decision\",\n tool: \"kb_write_decision\",\n usage: \"write-decision < decision.json\",\n description: [\n \"Write a decision. Takes `alternative` and `impact` as fields rather than free sections, because what was rejected is the part a later reader cannot reconstruct from the code — a heading is too easy to leave empty.\",\n \"\",\n \"What belongs in one:\",\n '- Record a decision when a later reader would otherwise \"simplify\" the constraint away. If the diff already answers the question, there is nothing here to write.',\n \"- `alternative` is what you turned down and why, not a list of everything considered.\",\n \"- A reference to material you read goes in `sources`; a reference to code goes in `anchors`; a reference to another record goes in `relatedConceptIds`.\",\n ].join(\"\\n\"),\n input: z.object({ bundlePath, input: decisionInputSchema }),\n fromArgv: async (_argv, path, stdin) => ({\n bundlePath: path,\n input: JSON.parse(await stdin()) as unknown,\n }),\n run: async ({ store, actor, now }, { bundlePath: path, input }) => {\n const record = await store.write(\n path,\n composeDecisionRecord(input, actor, now()),\n actor,\n );\n return { conceptId: record.conceptId };\n },\n }),\n\n define({\n name: \"no-decision\",\n tool: \"kb_no_decision\",\n usage: \"no-decision <reason...>\",\n description:\n 'Claim in one sentence that there was nothing to decide. Gating on \"did you write a decision?\" rewards writing a junk one; gating on \"did you answer?\" does not, so silence has to be expressible. Idempotent — restating it is not a collision.',\n input: z.object({ bundlePath, reason: z.string().min(1) }),\n fromArgv: (argv, path) => ({\n bundlePath: path,\n reason: argv.slice(1).join(\" \").trim(),\n }),\n run: async ({ store, actor, now }, { bundlePath: path, reason }) => {\n const record = await store.write(\n path,\n { ...composeNoDecisionRecord(reason, actor, now()), overwrite: true },\n actor,\n );\n return { conceptId: record.conceptId };\n },\n }),\n\n define({\n name: \"status\",\n tool: \"kb_status\",\n usage: \"status <concept-id> <status>\",\n description:\n \"Move a record's status, leaving everything else alone. Uses a compare-and-swap, so a concurrent change fails loudly rather than being overwritten.\",\n input: z.object({\n bundlePath,\n conceptId,\n status: z.enum(KB_RECORD_STATUSES),\n }),\n fromArgv: (argv, path) => ({\n bundlePath: path,\n conceptId: argv[1],\n status: argv[2],\n }),\n run: async (\n { store, actor },\n { bundlePath: path, conceptId: id, status },\n ) => {\n const record = await store.setStatus(path, id, status, actor);\n return { conceptId: record.conceptId, status };\n },\n }),\n\n define({\n name: \"supersede\",\n tool: \"kb_supersede\",\n usage: \"supersede <concept-id> <replacement-id>\",\n description:\n \"Mark a record superseded by another, linking both directions. Use this rather than editing a record whose meaning changed — a record that quietly becomes something else invalidates every reference to it, and the earlier understanding is what a later trace needs.\",\n input: z.object({ bundlePath, conceptId, replacementId: conceptId }),\n fromArgv: (argv, path) => ({\n bundlePath: path,\n conceptId: argv[1],\n replacementId: argv[2],\n }),\n run: async (\n { store, actor },\n { bundlePath: path, conceptId: id, replacementId },\n ) => {\n await store.supersede(path, id, replacementId, actor);\n return { superseded: id, replacedBy: replacementId };\n },\n }),\n\n define({\n name: \"answer\",\n tool: \"kb_answer\",\n usage: \"answer <concept-id> <answer...>\",\n description:\n \"Resolve an open question: sets the status, stamps who answered and when, and appends an Answer section. If the answer overturns an assumption or a decision, that is a supersession — do it explicitly.\",\n input: z.object({ bundlePath, conceptId, answer: z.string().min(1) }),\n fromArgv: (argv, path) => ({\n bundlePath: path,\n conceptId: argv[1],\n answer: argv.slice(2).join(\" \").trim(),\n }),\n run: async (\n { store, actor },\n { bundlePath: path, conceptId: id, answer },\n ) => {\n const record = await store.answer(path, id, answer, actor);\n return { conceptId: record.conceptId };\n },\n }),\n\n define({\n name: \"load\",\n tool: \"kb_load\",\n usage: \"load [type] [--budget N]\",\n description:\n \"Load the whole knowledge base at once, each record with its standing. Prefer this over searching: these bases run to a few thousand tokens, and a reader holding all of it has perfect recall and knows why it is asking, which no ranker does. Superseded records arrive under `superseded` as name, replacement and date only — their bodies no longer hold, and reading one later in a long session is the mistake this prevents; pass the id to kb_trace when you need the history. Rejected and unresolved records arrive whole: what was turned down, and what is still open, is the part a diff cannot show you. Refuses with a count rather than truncating when the base is too large — a truncated base is indistinguishable from a complete one, and would have you conclude something was never decided from a slice you did not know was a slice.\",\n input: z.object({\n bundlePath,\n type: z.enum(KB_RECORD_TYPES).optional(),\n budgetTokens: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\"Approximate token ceiling. Defaults to 25000.\"),\n }),\n fromArgv: (argv, path) => {\n const at = argv.indexOf(\"--budget\");\n return {\n bundlePath: path,\n ...(argv[1] && argv[1] !== \"--budget\" ? { type: argv[1] } : {}),\n ...(at !== -1 && argv[at + 1]\n ? { budgetTokens: Number(argv[at + 1]) }\n : {}),\n };\n },\n run: async ({ store }, { bundlePath: path, type, budgetTokens }) => {\n const result = await store.load(path, {\n ...(type ? { type } : {}),\n ...(budgetTokens ? { budgetTokens } : {}),\n });\n if (!result.loaded) return result;\n return {\n ...result,\n records: result.records.map((hit) => ({\n conceptId: hit.record.conceptId,\n title: hit.record.frontmatter.title ?? null,\n standing: hit.standing,\n supersededBy: hit.heads.map((head) => head.conceptId),\n warnings: hit.warnings,\n anchors: hit.record.frontmatter.strauss_anchors ?? [],\n body: hit.record.body,\n })),\n };\n },\n }),\n\n define({\n name: \"query\",\n tool: \"kb_query\",\n usage: \"query <text...>\",\n description:\n \"Search and return each match with its standing. Results are flagged, never filtered: a superseded record comes back alongside whatever replaced it, and a rejected one is marked as something explicitly not adopted. Prefer this over reading record files directly — relevance and standing are different questions, and a bare match answers only the first.\",\n input: z.object({\n bundlePath,\n text: z.string().optional(),\n type: z.enum(KB_RECORD_TYPES).optional(),\n includeNonCurrent: z.boolean().optional(),\n }),\n fromArgv: (argv, path) => ({\n bundlePath: path,\n text: argv.slice(1).join(\" \").trim(),\n includeNonCurrent: true,\n }),\n run: async (\n { store },\n { bundlePath: path, text, type, includeNonCurrent },\n ) =>\n (\n await store.query(path, text ?? \"\", {\n ...(type ? { type } : {}),\n includeNonCurrent: includeNonCurrent === true,\n })\n ).map((hit) => ({\n conceptId: hit.record.conceptId,\n title: hit.record.frontmatter.title ?? null,\n description: hit.record.frontmatter.description ?? null,\n standing: hit.standing,\n supersededBy: hit.heads.map((head) => head.conceptId),\n warnings: hit.warnings,\n body: hit.record.body,\n })),\n }),\n\n define({\n name: \"trace\",\n tool: \"kb_trace\",\n usage: \"trace <concept-id> [edges...]\",\n description:\n 'How a position was arrived at, as a timeline ordered by when each record was written. Deliberately includes rejected, draft, and superseded records — in a history those are the content, not noise. Follows supersession, shared code anchors, and shared sources. Use when the question is \"why is this the way it is\" rather than \"what do we hold now\".',\n input: z.object({\n bundlePath,\n conceptId,\n edges: z.array(z.enum(TRACE_EDGES)).optional(),\n depth: z.number().int().positive().optional(),\n }),\n fromArgv: (argv, path) => ({\n bundlePath: path,\n conceptId: argv[1],\n edges: argv\n .slice(2)\n .filter((edge) => (TRACE_EDGES as readonly string[]).includes(edge)),\n }),\n run: async ({ store }, { bundlePath: path, conceptId: id, edges, depth }) =>\n (\n await store.trace(path, id, {\n ...(edges?.length ? { edges } : {}),\n ...(depth ? { depth } : {}),\n })\n ).map((step) => ({\n conceptId: step.record.conceptId,\n at: step.record.frontmatter.generated?.at ?? null,\n status: step.record.frontmatter.strauss_status,\n title: step.record.frontmatter.title ?? null,\n depth: step.depth,\n via: step.via,\n body: step.record.body,\n })),\n }),\n\n define({\n name: \"list\",\n tool: \"kb_list\",\n usage: \"list [type]\",\n description:\n \"Every record, optionally narrowed to one type. Use kb_query when you have a question; this is for enumerating.\",\n input: z.object({ bundlePath, type: z.enum(KB_RECORD_TYPES).optional() }),\n fromArgv: (argv, path) => ({ bundlePath: path, type: argv[1] }),\n run: async ({ store }, { bundlePath: path, type }) =>\n (await store.list(path, type)).map((record) => ({\n conceptId: record.conceptId,\n title: record.frontmatter.title ?? null,\n description: record.frontmatter.description ?? null,\n status: record.frontmatter.strauss_status,\n anchors: record.frontmatter.strauss_anchors ?? [],\n })),\n }),\n\n define({\n name: \"index\",\n tool: \"kb_index\",\n usage: \"index\",\n description:\n \"The index, rebuilt if it disagrees with the records. One call gives the whole shape of the base: title, type, status, and description per record.\",\n input: z.object({ bundlePath }),\n fromArgv: (_argv, path) => ({ bundlePath: path }),\n run: ({ store }, { bundlePath: path }) => store.readIndex(path),\n }),\n\n define({\n name: \"log\",\n tool: \"kb_log\",\n usage: \"log\",\n description:\n \"What touched what, and when. The only artifact here that cannot be reconstructed from the records, so malformed lines are reported rather than repaired.\",\n input: z.object({ bundlePath }),\n fromArgv: (_argv, path) => ({ bundlePath: path }),\n run: ({ store }, { bundlePath: path }) => store.readLog(path),\n }),\n\n define({\n name: \"validate\",\n tool: \"kb_validate\",\n usage: \"validate\",\n description:\n \"Check pointers no single record can see: supersession links that disagree between the two records, and assumptions that cite sources. Per-record shape is enforced on every read, so a problem here means someone edited a file by hand.\",\n input: z.object({ bundlePath }),\n fromArgv: (_argv, path) => ({ bundlePath: path }),\n run: async ({ store }, { bundlePath: path }) =>\n validateBundle(await store.list(path)),\n failsWhen: (result) => Array.isArray(result) && result.length > 0,\n }),\n\n define({\n name: \"schema\",\n tool: \"kb_schema\",\n usage: \"schema\",\n description:\n \"JSON Schema for the frontmatter, the write input, and log entries — generated from the code that enforces them, so it cannot drift from what a write will accept.\",\n input: z.object({}),\n fromArgv: () => ({}),\n run: () => Promise.resolve(kbJsonSchemas()),\n }),\n\n define({\n name: \"types\",\n tool: \"kb_types\",\n usage: \"types\",\n description:\n \"The twelve record types with their purpose, body sections, and starting status. Read this before writing rather than guessing headings — a section the type does not define is rejected.\",\n input: z.object({}),\n fromArgv: () => ({}),\n run: () => Promise.resolve(RECORD_TYPES),\n }),\n];\n\nexport const KB_COMMANDS_BY_NAME = new Map(\n KB_COMMANDS.map((command) => [command.name, command]),\n);\n\nexport { DECISION_TYPE };\n","import { z } from \"zod\";\nimport {\n kbAnchorSchema,\n kbConceptIdSchema,\n kbSourceSchema,\n KB_CONFIDENCES,\n KB_MATERIALITIES,\n type KbRecordFrontmatter,\n type KbRecordType,\n} from \"./kb-record.schema.js\";\nimport { RECORD_TYPES } from \"./record-types.js\";\n\nexport const composeInputSchema = z\n .object({\n slug: z.string().min(1),\n /** One line, in the reader's terms. Becomes OKF `title`. */\n title: z.string().min(1),\n /** The consequence — what breaks if this is wrong. Becomes `description`. */\n why: z.string().min(1),\n /** Keyed by section heading from the type's spec. Unknown keys rejected. */\n sections: z.record(z.string(), z.string().min(1)).optional(),\n anchors: z.array(kbAnchorSchema).optional(),\n sources: z.array(kbSourceSchema).optional(),\n /** No source exists, as a claim rather than a sentinel in `sources`. */\n assumption: z.boolean().optional(),\n verify: z.array(z.string().min(1)).optional(),\n tags: z.array(z.string().min(1)).optional(),\n /** Concept ids this record relates to; rendered as body links. */\n relatedConceptIds: z.array(kbConceptIdSchema).optional(),\n /** Concept ids this record replaces. The store settles the backlinks. */\n supersedes: z.array(kbConceptIdSchema).optional(),\n materiality: z.enum(KB_MATERIALITIES).optional(),\n confidence: z.enum(KB_CONFIDENCES).optional(),\n owner: z.string().min(1).optional(),\n })\n .strict();\n\nexport type ComposeInput = z.infer<typeof composeInputSchema>;\n\nexport type ComposedRecord = {\n type: string;\n slug: string;\n frontmatter: Omit<KbRecordFrontmatter, \"type\">;\n body: string;\n};\n\n/**\n * Builds one record's frontmatter and body from its type's spec.\n *\n * Edges go in the body rather than frontmatter because that is where OKF puts\n * them: consumers read markdown links as directed but untyped relationships,\n * with \"the specific kind conveyed by the surrounding prose, not by the link\n * itself\". Broken links are explicitly legal, which matters here — records are\n * routinely written before the ones they point at exist.\n */\nexport function composeRecord(\n type: KbRecordType,\n input: ComposeInput,\n writtenBy: string,\n writtenAt: string,\n): ComposedRecord {\n // Parsed here rather than trusted from the caller: the CLI validates its own\n // stdin, but a library caller has no such gate, and `relatedConceptIds` is\n // interpolated into a markdown link unescaped a few lines down.\n const parsed = composeInputSchema.parse(input);\n const spec = RECORD_TYPES[type];\n const sections = parsed.sections ?? {};\n\n const unknown = Object.keys(sections).filter(\n (heading) => !spec.sections.includes(heading),\n );\n if (unknown.length) {\n throw new Error(\n `kb: ${type} has no section ${unknown.join(\", \")} — expected one of ${spec.sections.join(\", \")}`,\n );\n }\n\n const frontmatter: Omit<KbRecordFrontmatter, \"type\"> = {\n title: parsed.title,\n description: parsed.why,\n generated: { by: writtenBy, at: writtenAt },\n // Empty rather than absent: a later verification pass appends here, and an\n // empty list says \"not yet verified\" where a missing key would only say\n // \"this producer didn't think about it\".\n verified: [],\n strauss_status: spec.initialStatus,\n };\n if (parsed.anchors?.length) frontmatter.strauss_anchors = parsed.anchors;\n if (parsed.verify?.length) frontmatter.strauss_verify = parsed.verify;\n if (parsed.tags?.length) frontmatter.tags = parsed.tags;\n if (parsed.sources?.length) frontmatter.sources = parsed.sources;\n if (parsed.assumption) frontmatter.strauss_assumption = true;\n if (parsed.materiality) frontmatter.strauss_materiality = parsed.materiality;\n if (parsed.confidence) frontmatter.strauss_confidence = parsed.confidence;\n if (parsed.owner) frontmatter.strauss_owner = parsed.owner;\n if (parsed.supersedes?.length)\n frontmatter.strauss_supersedes = parsed.supersedes;\n\n const blocks: string[] = [];\n for (const heading of spec.sections) {\n const text = sections[heading];\n // Omitted rather than stubbed: an empty \"## Evidence\" reads as evidence\n // that was sought and not found.\n if (text) blocks.push(`## ${heading}\\n\\n${text}`);\n }\n if (!blocks.length) blocks.push(parsed.why);\n\n for (const related of parsed.relatedConceptIds ?? []) {\n blocks.push(`Relates to [${related}](${related}.md).`);\n }\n if (parsed.sources?.length) {\n blocks.push(\n parsed.sources\n .map((source) => `[^${source.id}]: ${source.title ?? source.resource}`)\n .join(\"\\n\"),\n );\n }\n\n return {\n type,\n slug: parsed.slug,\n frontmatter,\n body: `${blocks.join(\"\\n\\n\")}\\n`,\n };\n}\n","import { z } from \"zod\";\n\n/**\n * Knowledge records, shaped as OKF v0.2 concepts.\n *\n * OKF (Google Cloud `knowledge-catalog`) requires exactly one key — `type` —\n * and explicitly permits extension: \"Producers MAY include any additional keys.\n * Consumers SHOULD preserve unknown keys when round-tripping and MUST NOT\n * reject documents with unrecognized fields.\"\n *\n * A record's identity is its `concept_id`: the file path within the bundle with\n * `.md` removed. `<type>.<slug>.md` therefore yields `decision.some-slug`.\n *\n * Keys prefixed `strauss_` are this package's extensions rather than\n * conformance, and are namespaced so a later OKF version defining the same\n * names cannot collide: OKF names files through path-valued `resource` fields\n * and has no notion of a span, so anchoring a concept to a range of code has no\n * standard spelling, and standing has none either.\n */\n\n/** A source the record draws on. Footnotes in the body key to `id`. */\nexport const kbSourceSchema = z\n .object({\n id: z.string().min(1),\n resource: z.string().min(1),\n title: z.string().min(1).optional(),\n author: z.string().min(1).optional(),\n last_modified: z.string().min(1).optional(),\n })\n .passthrough();\n\n/** An actor/time pair — OKF's shape for both `generated` and `verified[]`. */\nexport const kbActorStampSchema = z\n .object({\n by: z.string().min(1),\n at: z.string().min(1),\n })\n .passthrough();\n\n/**\n * Where a record attaches in the code.\n *\n * Symbolic on purpose. These are written while the code is still moving: a\n * `line: 379` recorded at minute five is wrong by minute forty, but\n * `OrderService.cancel` survives every edit that does not rename it. A later\n * pass resolves symbols to line ranges once the change has settled, and records\n * that resolution as a `verified[]` entry.\n */\nexport const kbAnchorSchema = z\n .object({\n file: z.string().min(1),\n symbol: z.string().min(1).optional(),\n })\n .strict();\n\nexport const KB_RECORD_TYPES = [\n \"fact\",\n \"requirement\",\n \"constraint\",\n \"decision\",\n \"assumption\",\n \"open-question\",\n \"risk\",\n \"contract\",\n \"flow\",\n \"affected-system\",\n \"test-obligation\",\n \"source-note\",\n] as const;\n\nexport type KbRecordType = (typeof KB_RECORD_TYPES)[number];\n\n/** Both halves of `<type>.<slug>` are kebab-case, and neither may be empty. */\nexport const KB_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\nexport const KB_CONCEPT_ID_PATTERN =\n /^[a-z0-9]+(?:-[a-z0-9]+)*\\.[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\n/**\n * Concept ids are rendered into markdown links unescaped, so an id carrying a\n * `]` or `)` would emit a broken edge rather than fail. Validating at the entry\n * point keeps the renderer from having to care.\n */\nexport const kbConceptIdSchema = z.string().regex(KB_CONCEPT_ID_PATTERN, {\n message: \"concept id must be <type>.<slug>, both kebab-case\",\n});\n\n/**\n * Standing, not freshness.\n *\n * OKF's `verified[]` and `stale_after` answer \"is this still true?\"; nothing in\n * the spec answers \"is this settled, and does it still apply?\". A base\n * supersedes its own conclusions as work proceeds, so that second question\n * needs an answer, and it is this package's to define — hence `strauss_`.\n */\nexport const KB_RECORD_STATUSES = [\n \"draft\",\n \"proposed\",\n \"accepted\",\n \"open\",\n \"resolved\",\n \"rejected\",\n \"superseded\",\n] as const;\n\nexport type KbRecordStatus = (typeof KB_RECORD_STATUSES)[number];\n\nexport const KB_MATERIALITIES = [\n \"blocking\",\n \"important\",\n \"non-blocking\",\n] as const;\nexport const KB_CONFIDENCES = [\"low\", \"medium\", \"high\"] as const;\n\nexport const kbRecordFrontmatterSchema = z\n .object({\n // OKF: the only always-required key. A concept carrying just `type` is\n // fully conformant, so everything below stays optional.\n type: z.string().min(1),\n\n // OKF recommended.\n title: z.string().min(1).optional(),\n description: z.string().min(1).optional(),\n resource: z.string().min(1).optional(),\n tags: z.array(z.string()).optional(),\n\n // OKF optional: provenance and freshness.\n sources: z.array(kbSourceSchema).optional(),\n generated: kbActorStampSchema.optional(),\n verified: z.array(kbActorStampSchema).optional(),\n stale_after: z.string().min(1).optional(),\n\n // strauss extensions — see the module comment.\n strauss_anchors: z.array(kbAnchorSchema).optional(),\n strauss_verify: z.array(z.string().min(1)).optional(),\n\n // Total after parsing, tolerant before it. Our producers must supply a\n // status — an absent one would leave every reader inventing its own default\n // — but OKF calls a concept carrying only `type` fully conformant, so\n // rejecting a foreign record for the lack of one would put us outside the\n // spec. The default resolves it in the single place that can: here.\n strauss_status: z.enum(KB_RECORD_STATUSES).default(\"draft\"),\n strauss_supersedes: z.array(z.string().min(1)).optional(),\n strauss_superseded_by: z.string().min(1).optional(),\n strauss_answered: kbActorStampSchema.optional(),\n strauss_materiality: z.enum(KB_MATERIALITIES).optional(),\n strauss_confidence: z.enum(KB_CONFIDENCES).optional(),\n strauss_owner: z.string().min(1).optional(),\n\n // \"No source exists\" as a field rather than a sentinel entry inside\n // `sources`. A sentinel in a reference list is a value doing work a field\n // should do; as a field, `sources` may be legitimately empty.\n strauss_assumption: z.boolean().optional(),\n })\n // Unknown keys are kept rather than stripped: OKF requires consumers to\n // preserve them when round-tripping, and a producer we don't know about may\n // be writing into the same bundle.\n .passthrough();\n\nexport type KbSource = z.infer<typeof kbSourceSchema>;\nexport type KbActorStamp = z.infer<typeof kbActorStampSchema>;\nexport type KbAnchor = z.infer<typeof kbAnchorSchema>;\nexport type KbRecordFrontmatter = z.infer<typeof kbRecordFrontmatterSchema>;\n\nexport type KbRecord = {\n /** Path minus `.md`, relative to the bundle root. OKF's concept identity. */\n conceptId: string;\n frontmatter: KbRecordFrontmatter;\n body: string;\n};\n","import type { KbRecordStatus, KbRecordType } from \"./kb-record.schema.js\";\n\n/**\n * What each record type is for, and the shape of its body.\n *\n * A table rather than twelve composer modules. The types differ only in which\n * questions their body answers and where they start in the lifecycle; encoding\n * that as data keeps the one composer honest and makes adding a type an edit\n * rather than a file.\n *\n * `sections` are ordered. A section the caller leaves empty is omitted rather\n * than rendered with a placeholder — an empty \"## Evidence\" reads as evidence\n * that was looked for and not found.\n */\nexport type KbRecordTypeSpec = {\n /** One line, for `INDEX.md` legends and CLI help. */\n purpose: string;\n /** Ordered body headings. The first is the record's central claim. */\n sections: readonly string[];\n /** Where a freshly written record of this type starts. */\n initialStatus: KbRecordStatus;\n};\n\nexport const RECORD_TYPES: Readonly<Record<KbRecordType, KbRecordTypeSpec>> = {\n fact: {\n purpose: \"Observed or sourced fact\",\n sections: [\"Claim\", \"Evidence\", \"Implication\"],\n initialStatus: \"accepted\",\n },\n requirement: {\n purpose: \"Required behavior or outcome\",\n sections: [\"Claim\", \"Evidence\", \"Implication\"],\n initialStatus: \"proposed\",\n },\n constraint: {\n purpose: \"Limitation, compatibility boundary, policy, or restriction\",\n sections: [\"Claim\", \"Evidence\", \"Implication\"],\n initialStatus: \"accepted\",\n },\n decision: {\n purpose: \"Chosen or proposed direction\",\n sections: [\"Decision\", \"Rationale\", \"Rejected\", \"Impact\"],\n initialStatus: \"accepted\",\n },\n assumption: {\n purpose: \"Unsourced or not-yet-confirmed working assumption\",\n sections: [\"Claim\", \"Why we think so\", \"What would falsify it\"],\n initialStatus: \"draft\",\n },\n \"open-question\": {\n purpose: \"Question needing resolution\",\n sections: [\"Question\", \"Why it matters\", \"Default assumption\"],\n initialStatus: \"open\",\n },\n risk: {\n purpose: \"Something that can go wrong\",\n sections: [\"Risk\", \"Why it matters\", \"Mitigation\", \"Verification\"],\n initialStatus: \"open\",\n },\n contract: {\n purpose: \"API, data, event, schema, or permission contract\",\n sections: [\"Contract\", \"Producer\", \"Consumer\", \"Compatibility\"],\n initialStatus: \"proposed\",\n },\n flow: {\n purpose: \"Sequence, lifecycle, or state behavior\",\n sections: [\"Flow\", \"Trigger\", \"Steps\", \"Failure modes\"],\n initialStatus: \"accepted\",\n },\n \"affected-system\": {\n purpose: \"Component, service, package, integration, or external system\",\n sections: [\"System\", \"How it is affected\", \"Blast radius\"],\n initialStatus: \"accepted\",\n },\n \"test-obligation\": {\n purpose: \"Behavior or contract that must be verified\",\n sections: [\"Obligation\", \"Why it matters\", \"How to verify\"],\n initialStatus: \"open\",\n },\n \"source-note\": {\n purpose: \"Extracted note from source material\",\n sections: [\"Note\", \"Where it came from\"],\n initialStatus: \"accepted\",\n },\n};\n\nexport function isKbRecordType(value: string): value is KbRecordType {\n return Object.prototype.hasOwnProperty.call(RECORD_TYPES, value);\n}\n","import { z } from \"zod\";\nimport {\n composeInputSchema,\n composeRecord,\n type ComposedRecord,\n} from \"./compose.js\";\nimport type { KbRecord } from \"./kb-record.schema.js\";\n\n/**\n * The record written while a change is being made: why it is shaped the way it\n * is, anchored to the symbols it touches.\n *\n * A decision is the one thing a later pass cannot recover. The diff shows what\n * changed; nothing in it says which alternative was rejected, or which\n * constraint a future reader would otherwise \"simplify\" away. Everything else a\n * review needs — categories, moves, formatting — is derivable from the finished\n * diff and does not belong here.\n */\nexport const DECISION_TYPE = \"decision\";\n\n/**\n * Slug for the explicit \"nothing to record here\" answer.\n *\n * Gating on \"did you write a decision?\" rewards writing a junk decision. Gating\n * on \"did you answer?\" does not, so silence has to be expressible as a claim:\n * one record, one sentence, auditable after the fact. Work that genuinely\n * needed no decision says so; work that says nothing at all is the case worth\n * surfacing.\n */\nexport const NO_DECISION_SLUG = \"none\";\n\n/**\n * Decisions keep a typed input of their own where the generic composer takes a\n * section map. `alternative` is not a nicety here — \"what was rejected\" is the\n * part of a decision that a later reader cannot reconstruct, so it gets a field\n * rather than a heading a writer may forget to fill.\n */\nexport const decisionInputSchema = composeInputSchema\n .omit({ sections: true })\n .extend({\n alternative: z.string().min(1).optional(),\n impact: z.string().min(1).optional(),\n })\n .strict();\n\nexport type DecisionInput = z.infer<typeof decisionInputSchema>;\n\nexport function composeDecisionRecord(\n input: DecisionInput,\n writtenBy: string,\n writtenAt: string,\n): ComposedRecord {\n const { alternative, impact, ...rest } = input;\n return composeRecord(\n DECISION_TYPE,\n {\n ...rest,\n sections: {\n Decision: input.title,\n Rationale: input.why,\n ...(alternative ? { Rejected: alternative } : {}),\n ...(impact ? { Impact: impact } : {}),\n },\n },\n writtenBy,\n writtenAt,\n );\n}\n\n/** The explicit no-decision claim, so an absence and an answer stay distinct. */\nexport function composeNoDecisionRecord(\n reason: string,\n writtenBy: string,\n writtenAt: string,\n): ComposedRecord {\n return composeRecord(\n DECISION_TYPE,\n {\n slug: NO_DECISION_SLUG,\n title: \"No decision to record\",\n why: reason,\n sections: { Decision: reason },\n },\n writtenBy,\n writtenAt,\n );\n}\n\n/** Whether a record is the explicit no-decision claim rather than a decision. */\nexport function isNoDecisionRecord(record: KbRecord): boolean {\n return record.conceptId === `${DECISION_TYPE}.${NO_DECISION_SLUG}`;\n}\n\n/**\n * Decisions in the bundle, excluding the no-decision claim.\n *\n * Callers asking \"what was decided\" must not be handed the record that exists\n * precisely to say nothing was.\n */\nexport function selectDecisions(records: KbRecord[]): KbRecord[] {\n return records.filter(\n (record) =>\n record.conceptId.startsWith(`${DECISION_TYPE}.`) &&\n !isNoDecisionRecord(record),\n );\n}\n","import { z } from \"zod\";\nimport { composeInputSchema } from \"./compose.js\";\nimport { kbLogEntrySchema } from \"./kb-log.js\";\nimport { kbRecordFrontmatterSchema } from \"./kb-record.schema.js\";\n\n/**\n * The frontmatter contract, emitted rather than restated.\n *\n * Prose describing a schema drifts from the code that enforces it; a generated\n * artifact cannot. Documentation points at this, a YAML language server\n * validates hand-edited records against it, and a consumer that is not\n * TypeScript has something to check.\n *\n * `io: 'input'` on purpose. `strauss_status` carries a default, so the output\n * type marks it required while the *document* may legitimately omit it — and\n * the document is what this schema is used to validate.\n */\nexport function kbJsonSchemas(): Record<string, unknown> {\n return {\n recordFrontmatter: z.toJSONSchema(kbRecordFrontmatterSchema, {\n io: \"input\",\n }),\n composeInput: z.toJSONSchema(composeInputSchema, { io: \"input\" }),\n logEntry: z.toJSONSchema(kbLogEntrySchema, { io: \"input\" }),\n };\n}\n","import { z } from \"zod\";\n\nexport const LOG_FILE = \"log.jsonl\";\n\nexport const kbLogEntrySchema = z\n .object({\n at: z.string().min(1),\n by: z.string().min(1),\n operation: z.string().min(1),\n conceptId: z.string().min(1),\n /** Second concept id, where the operation relates two — supersession. */\n target: z.string().min(1).optional(),\n })\n .strict();\n\nexport type KbLogEntry = z.infer<typeof kbLogEntrySchema>;\n\n/**\n * The log is the bundle's only primary artifact, and the reason it is handled\n * unlike `INDEX.md`.\n *\n * The index is derived: lose it and the records rebuild it. The log records\n * events — which agent wrote what, and when — that leave no trace in the record\n * set, so it cannot be regenerated from anything. Repair therefore means detect\n * and report, never rewrite: rewriting an append-only log destroys the only\n * copy of what it holds.\n *\n * JSONL rather than a markdown list. An earlier version rendered entries as\n * `- <at> · <by> · <op> · <id>` and parsed them by splitting on the separator —\n * a hand-written parser for a format invented here, which fails the first time\n * a value contains the separator. JSON needs no parser and the schema below\n * needs no separator to be unambiguous. Humans read the log through\n * `strauss-kb log`, as they read everything else.\n *\n * One line per entry, appended with `O_APPEND`: POSIX makes the offset update\n * atomic, and writes this size do not interleave on a local filesystem.\n */\nexport function renderLogEntry(entry: KbLogEntry): string {\n return `${JSON.stringify(kbLogEntrySchema.parse(entry))}\\n`;\n}\n\nexport type KbLogReadResult = {\n entries: KbLogEntry[];\n /** Lines that did not parse, with their 1-based position. Never rewritten. */\n malformed: { line: number; text: string }[];\n};\n\nexport function parseLog(raw: string): KbLogReadResult {\n const entries: KbLogEntry[] = [];\n const malformed: { line: number; text: string }[] = [];\n\n raw.split(\"\\n\").forEach((text, index) => {\n if (!text.trim()) return;\n let value: unknown;\n try {\n value = JSON.parse(text) as unknown;\n } catch {\n malformed.push({ line: index + 1, text });\n return;\n }\n const parsed = kbLogEntrySchema.safeParse(value);\n if (!parsed.success) {\n malformed.push({ line: index + 1, text });\n return;\n }\n entries.push(parsed.data);\n });\n\n return { entries, malformed };\n}\n","import type { KbRecord } from \"./kb-record.schema.js\";\n\n/**\n * Edges a trace may follow.\n *\n * Two more are conceivable and absent: `strauss_answered` carries no target id,\n * so a question's resolution lives in its own body rather than in another\n * record; and following OKF's body markdown links would need a markdown AST\n * pass this package does not yet do.\n */\nexport const TRACE_EDGES = [\"supersession\", \"anchor\", \"source\"] as const;\nexport type KbTraceEdge = (typeof TRACE_EDGES)[number];\n\nexport type KbTraceStep = {\n record: KbRecord;\n /** Hops from the seed. 0 is the seed itself. */\n depth: number;\n /** Why this record was reached. Empty for the seed. */\n via: KbTraceEdge[];\n};\n\nexport type KbTraceOptions = {\n edges?: readonly KbTraceEdge[];\n /** Body links alone can reach the whole bundle, so a trace is always bounded. */\n depth?: number;\n};\n\n/**\n * How a position was arrived at, as a timeline.\n *\n * The inverse of a point query, and the reason the two cannot be one call with\n * a flag: there, a `rejected` record is the most dangerous thing retrievable —\n * here it is the content. A trace that drops the rejected alternatives and the\n * superseded earlier understanding has removed the answer and kept the\n * conclusion, which is what reading a diff already gives you.\n *\n * Ordered by `generated.at` rather than by relevance. Ranking a history is\n * meaningless when the sequence is the point.\n */\nexport function trace(\n seedId: string,\n bundle: KbRecord[],\n options: KbTraceOptions = {},\n): KbTraceStep[] {\n const edges = options.edges?.length ? options.edges : TRACE_EDGES;\n const maxDepth = options.depth ?? 3;\n const byId = new Map(bundle.map((record) => [record.conceptId, record]));\n const seed = byId.get(seedId);\n if (!seed) return [];\n\n const reached = new Map<string, KbTraceStep>([\n [seedId, { record: seed, depth: 0, via: [] }],\n ]);\n let frontier: KbRecord[] = [seed];\n\n for (let depth = 1; depth <= maxDepth && frontier.length; depth += 1) {\n const next: KbRecord[] = [];\n for (const from of frontier) {\n for (const edge of edges) {\n for (const record of neighbours(from, bundle, edge)) {\n const existing = reached.get(record.conceptId);\n if (existing) {\n // Reached twice by different edges: keep the shorter path, but\n // record both reasons — \"shares an anchor and replaces it\" is more\n // informative than either alone. The seed keeps an empty `via`,\n // since it was not reached by anything.\n if (existing.depth > 0 && !existing.via.includes(edge)) {\n existing.via.push(edge);\n }\n continue;\n }\n reached.set(record.conceptId, { record, depth, via: [edge] });\n next.push(record);\n }\n }\n }\n frontier = next;\n }\n\n return [...reached.values()].sort(byGeneratedAt);\n}\n\nfunction neighbours(\n from: KbRecord,\n bundle: KbRecord[],\n edge: KbTraceEdge,\n): KbRecord[] {\n switch (edge) {\n case \"supersession\":\n return bundle.filter(\n (candidate) =>\n candidate.conceptId !== from.conceptId &&\n (candidate.conceptId === from.frontmatter.strauss_superseded_by ||\n from.frontmatter.strauss_supersedes?.includes(\n candidate.conceptId,\n ) ||\n candidate.frontmatter.strauss_superseded_by === from.conceptId ||\n candidate.frontmatter.strauss_supersedes?.includes(from.conceptId)),\n );\n\n // The edge that answers \"why is this code shaped this way\": every record\n // attached to the same file or symbol, whatever its standing.\n case \"anchor\": {\n const mine = from.frontmatter.strauss_anchors ?? [];\n if (!mine.length) return [];\n return bundle.filter(\n (candidate) =>\n candidate.conceptId !== from.conceptId &&\n (candidate.frontmatter.strauss_anchors ?? []).some((theirs) =>\n mine.some((ours) => anchorsTouch(ours, theirs)),\n ),\n );\n }\n\n case \"source\": {\n const mine = new Set((from.frontmatter.sources ?? []).map((s) => s.id));\n if (!mine.size) return [];\n return bundle.filter(\n (candidate) =>\n candidate.conceptId !== from.conceptId &&\n (candidate.frontmatter.sources ?? []).some((source) =>\n mine.has(source.id),\n ),\n );\n }\n }\n}\n\n/**\n * Two anchors touch when they name the same file and do not name different\n * symbols within it.\n *\n * An anchor without a symbol means \"this record is about this file\", so it\n * relates to everything anchored inside it. Requiring an exact match instead\n * would hide the file-level record from every symbol-level trace, which is the\n * direction a reviewer actually reads.\n */\nfunction anchorsTouch(\n left: { file: string; symbol?: string },\n right: { file: string; symbol?: string },\n): boolean {\n if (left.file !== right.file) return false;\n if (!left.symbol || !right.symbol) return true;\n return left.symbol === right.symbol;\n}\n\nfunction byGeneratedAt(left: KbTraceStep, right: KbTraceStep): number {\n const at = (step: KbTraceStep) => step.record.frontmatter.generated?.at ?? \"\";\n return at(left).localeCompare(at(right)) || left.depth - right.depth;\n}\n","import type { KbRecord } from \"./kb-record.schema.js\";\nimport { isKbRecordType } from \"./record-types.js\";\n\nexport type KbValidationProblem = {\n check: string;\n conceptId: string;\n note: string;\n};\n\n/**\n * Checks that only hold across the whole bundle.\n *\n * Per-record shape is the schema's job and is enforced on every read, so\n * nothing here re-states it. What a schema cannot see is whether one record's\n * pointers agree with another's — and since `supersede()` now writes both\n * directions, a disagreement means someone edited a file by hand.\n */\nexport function validateBundle(records: KbRecord[]): KbValidationProblem[] {\n const byId = new Map(records.map((record) => [record.conceptId, record]));\n const problems: KbValidationProblem[] = [];\n const report = (check: string, conceptId: string, note: string) =>\n problems.push({ check, conceptId, note });\n\n for (const record of records) {\n const { conceptId, frontmatter: fm } = record;\n\n // OKF permits any `type`, so an unrecognised one is a note, not a failure:\n // another producer may legitimately be writing into this bundle.\n if (!isKbRecordType(fm.type)) {\n report(\"type\", conceptId, `unrecognised type \"${fm.type}\"`);\n }\n\n if (fm.strauss_status === \"superseded\") {\n const by = fm.strauss_superseded_by;\n if (!by) {\n report(\"superseded_by\", conceptId, \"superseded with no replacement\");\n } else if (!byId.has(by)) {\n report(\"superseded_by\", conceptId, `replacement ${by} is missing`);\n } else if (\n !byId.get(by)?.frontmatter.strauss_supersedes?.includes(conceptId)\n ) {\n report(\"backlink\", by, `does not list ${conceptId} in supersedes`);\n }\n }\n\n for (const old of fm.strauss_supersedes ?? []) {\n const previous = byId.get(old);\n if (!previous) {\n report(\"supersedes\", conceptId, `target ${old} is missing`);\n } else if (previous.frontmatter.strauss_status !== \"superseded\") {\n report(\"supersedes\", conceptId, `${old} is not marked superseded`);\n }\n }\n\n // An assumption with sources is a fact that forgot to change its mind.\n if (fm.strauss_assumption && fm.sources?.length) {\n report(\"assumption\", conceptId, \"marked an assumption but cites sources\");\n }\n }\n\n return problems;\n}\n","import { createHash } from \"node:crypto\";\nimport {\n appendFile,\n link,\n mkdir,\n readdir,\n readFile,\n rename,\n unlink,\n writeFile,\n} from \"node:fs/promises\";\nimport { join, resolve, sep } from \"node:path\";\nimport {\n parseMarkdownWithFrontmatter,\n stringifyMarkdownWithFrontmatter,\n} from \"./markdown.js\";\nimport {\n kbRecordFrontmatterSchema,\n KB_SLUG_PATTERN,\n type KbRecord,\n type KbRecordFrontmatter,\n type KbRecordStatus,\n} from \"./kb-record.schema.js\";\nimport {\n KbInvalidConceptIdError,\n KbRecordAlreadyExistsError,\n KbRecordNotFoundError,\n KbWriteConflictError,\n} from \"./kb-errors.js\";\nimport { INDEX_FILE, indexIsStale, renderIndex } from \"./kb-index.js\";\nimport { adjudicate, type KbAdjudicated } from \"./adjudicate.js\";\nimport { resolveHits, searchBase, SEARCH_INDEX_FILE } from \"./search-index.js\";\nimport { trace, type KbTraceOptions, type KbTraceStep } from \"./trace.js\";\nimport {\n LOG_FILE,\n parseLog,\n renderLogEntry,\n type KbLogEntry,\n} from \"./kb-log.js\";\n\n/**\n * Default bundle, relative to the working directory. A scratch base lives here\n * and is meant to be gitignored; a base worth keeping is written to a committed\n * path instead, passed explicitly. Nothing promotes one to the other.\n */\nexport const KB_DIR = join(\".strauss\", \"kb\");\n\nconst STORE_OWNED = new Set([INDEX_FILE, LOG_FILE, SEARCH_INDEX_FILE]);\n\nexport type KbLogger = {\n info?(entry: Record<string, unknown>): void;\n warn?(entry: Record<string, unknown>): void;\n};\n\n/** Roughly an eighth of a large context window — generous, and overridable. */\nconst DEFAULT_LOAD_BUDGET = 25_000;\n\n/**\n * A superseded record, named but not spelled out.\n *\n * Standing is a qualifier on a body, and over a long session the body outlives\n * the qualifier — the reader keeps what the record said and loses that it no\n * longer holds. A stub has nothing left to act on, so the failure cannot occur,\n * and `trace` still reaches the content through the id.\n */\nexport type KbSupersededStub = {\n conceptId: string;\n title: string | null;\n supersededBy: string[];\n at: string | null;\n};\n\nexport type KbLoadResult =\n | {\n loaded: true;\n records: KbAdjudicated[];\n /** Named only. Their bodies are reachable through `trace`. */\n superseded: KbSupersededStub[];\n recordCount: number;\n approxTokens: number;\n budgetTokens: number;\n }\n | {\n loaded: false;\n recordCount: number;\n approxTokens: number;\n budgetTokens: number;\n };\n\nexport type KbWriteInput = {\n type: string;\n slug: string;\n frontmatter: Omit<KbRecordFrontmatter, \"type\">;\n body: string;\n /** Replace an existing record rather than failing on the collision. */\n overwrite?: boolean;\n};\n\n/**\n * Reads and writes a knowledge bundle.\n *\n * One record per file, deliberately. Several agents run in parallel against the\n * same bundle, and a shared file would need merging — a file-per-record store\n * has no write conflict to resolve, only distinct filenames to choose.\n *\n * The bundle is addressed by path rather than fixed to one directory. A base\n * belongs to whatever prompted it — a worktree, an investigation, a document —\n * and one hardcoded location cannot be all of those.\n *\n * Framework-free on purpose. The consumers are a library caller, a CLI an agent\n * shells out to, and an MCP server; the store takes a logger rather than\n * reaching for one, because only some of those have anything to reach into.\n */\nexport class KbStore {\n constructor(private readonly logger: KbLogger = {}) {}\n\n /**\n * Writes one record. `type` and `slug` compose both the filename and the\n * concept id, so a caller cannot produce a file whose identity disagrees with\n * its contents.\n */\n async write(\n bundlePath: string,\n input: KbWriteInput,\n actor = \"unknown\",\n ): Promise<KbRecord> {\n if (!KB_SLUG_PATTERN.test(input.slug)) {\n throw new KbInvalidConceptIdError(\"slug must be kebab-case\", {\n slug: input.slug,\n });\n }\n if (!KB_SLUG_PATTERN.test(input.type)) {\n throw new KbInvalidConceptIdError(\"type must be kebab-case\", {\n type: input.type,\n });\n }\n\n const frontmatter = kbRecordFrontmatterSchema.parse({\n ...input.frontmatter,\n type: input.type,\n });\n const conceptId = `${input.type}.${input.slug}`;\n const root = this.root(bundlePath);\n const target = this.recordPath(bundlePath, conceptId);\n\n await mkdir(root, { recursive: true });\n await this.publish(\n target,\n stringifyMarkdownWithFrontmatter(input.body, frontmatter),\n input.overwrite ?? false,\n conceptId,\n );\n\n await this.record(root, {\n operation: input.overwrite ? \"overwrite\" : \"write\",\n conceptId,\n by: actor,\n });\n\n this.logger.info?.({\n operation: \"kb.write\",\n bundlePath: root,\n conceptId,\n anchors: frontmatter.strauss_anchors?.length ?? 0,\n });\n\n return { conceptId, frontmatter, body: input.body };\n }\n\n /** One record by concept id, or null when it does not exist. */\n async read(bundlePath: string, conceptId: string): Promise<KbRecord | null> {\n // Resolved outside the try: the `catch` exists to turn a missing file into\n // `null`, and must not also swallow the concept-id validation below it.\n const target = this.recordPath(bundlePath, conceptId);\n let raw: string;\n try {\n raw = await readFile(target, \"utf8\");\n } catch {\n return null;\n }\n return this.parse(conceptId, raw);\n }\n\n /**\n * Every record in the bundle, optionally narrowed to one type.\n *\n * A file that fails to parse is skipped and logged rather than thrown: one\n * malformed record — hand-edited, or written by a producer we don't know —\n * must not make the whole bundle unreadable.\n */\n async list(bundlePath: string, type?: string): Promise<KbRecord[]> {\n const root = this.root(bundlePath);\n let names: string[];\n try {\n names = await readdir(root);\n } catch {\n return [];\n }\n\n // The type filter runs on the filename, so a narrowed list never opens a\n // record it is going to discard. What remains is read concurrently: the\n // cost here is per-file syscall latency, not CPU.\n const wanted = names\n .sort()\n // Both store-owned files are markdown and neither is a record; without\n // this they parse to null and are dropped, but not before logging a\n // warning that says the bundle contains a malformed record.\n .filter((name) => name.endsWith(\".md\") && !STORE_OWNED.has(name))\n .map((name) => ({ name, conceptId: name.slice(0, -\".md\".length) }))\n .filter(({ conceptId }) => !type || conceptId.startsWith(`${type}.`));\n\n const records = await Promise.all(\n wanted.map(async ({ name, conceptId }) =>\n this.parse(conceptId, await readFile(join(root, name), \"utf8\")),\n ),\n );\n return records.filter((record): record is KbRecord => record !== null);\n }\n\n /**\n * Moves a record's status, preserving everything else.\n *\n * Read-modify-write on one file is the one place two agents genuinely race,\n * and the fix is a compare-and-swap rather than a lock: hash on read, verify\n * the file is unchanged immediately before writing, fail if it moved. A lock\n * would buy the same guarantee and add a stale-lock failure mode — a writer\n * killed mid-hold blocks every later one until someone reasons about\n * timeouts.\n */\n async setStatus(\n bundlePath: string,\n conceptId: string,\n status: KbRecordStatus,\n actor = \"unknown\",\n ): Promise<KbRecord> {\n return this.mutate(\n bundlePath,\n conceptId,\n (frontmatter) => ({ ...frontmatter, strauss_status: status }),\n { operation: `status:${status}`, by: actor },\n );\n }\n\n /**\n * Marks `conceptId` superseded by `replacementId`, and links both directions.\n *\n * Writing one side and letting a validator notice the other is missing was\n * the previous arrangement; doing both here means the backlink cannot drift\n * in normal use, and validation drops to catching hand-edits.\n */\n async supersede(\n bundlePath: string,\n conceptId: string,\n replacementId: string,\n actor = \"unknown\",\n ): Promise<KbRecord> {\n const replacement = await this.read(bundlePath, replacementId);\n if (!replacement) throw new KbRecordNotFoundError(replacementId);\n\n const superseded = await this.mutate(\n bundlePath,\n conceptId,\n (frontmatter) => ({\n ...frontmatter,\n strauss_status: \"superseded\" as const,\n strauss_superseded_by: replacementId,\n }),\n { operation: \"supersede\", by: actor, target: replacementId },\n );\n\n await this.mutate(\n bundlePath,\n replacementId,\n (frontmatter) => ({\n ...frontmatter,\n strauss_supersedes: [\n ...new Set([...(frontmatter.strauss_supersedes ?? []), conceptId]),\n ],\n }),\n { operation: \"supersedes\", by: actor, target: conceptId },\n );\n\n return superseded;\n }\n\n /** Resolves an open question, stamping who answered and when. */\n async answer(\n bundlePath: string,\n conceptId: string,\n answer: string,\n actor = \"unknown\",\n at = new Date().toISOString(),\n ): Promise<KbRecord> {\n return this.mutate(\n bundlePath,\n conceptId,\n (frontmatter) => ({\n ...frontmatter,\n strauss_status: \"resolved\" as const,\n strauss_answered: { by: actor, at },\n }),\n { operation: \"answer\", by: actor },\n (body) => `${body.trimEnd()}\\n\\n## Answer\\n\\n${answer}\\n`,\n );\n }\n\n /**\n * Records matching a text query, each carrying its standing.\n *\n * Relevance comes from qmd's BM25 where an index is available and from a\n * substring scan where it is not. What never moves to the ranker is the\n * adjudication below it: a ranker answers relevance, and relevance is not\n * standing — a superseded record is the older, longer, more general one, so\n * ranking alone prefers what is no longer true.\n *\n * The fallback is deliberate. A search index is an optimisation, so losing it\n * degrades recall and must never change the answer's shape or fail the call.\n */\n async query(\n bundlePath: string,\n text: string,\n options: { type?: string; includeNonCurrent?: boolean } = {},\n ): Promise<KbAdjudicated[]> {\n const bundle = await this.list(bundlePath);\n const needle = text.trim();\n const hits = needle ? await this.rank(bundlePath, needle, bundle) : bundle;\n const adjudicated = adjudicate(\n options.type\n ? hits.filter((r) => r.frontmatter.type === options.type)\n : hits,\n bundle,\n );\n\n if (options.includeNonCurrent) return adjudicated;\n // Superseded records are dropped only once their replacement is in the\n // result too — otherwise the caller loses the thread entirely rather than\n // being handed a newer version of it.\n const present = new Set(adjudicated.map((hit) => hit.record.conceptId));\n return adjudicated.filter(\n (hit) =>\n hit.standing !== \"superseded\" ||\n !hit.heads.some((head) => present.has(head.conceptId)),\n );\n }\n\n private async rank(\n bundlePath: string,\n needle: string,\n bundle: KbRecord[],\n ): Promise<KbRecord[]> {\n const ranked = await searchBase(this.root(bundlePath), needle, {\n logger: this.logger,\n });\n if (ranked) {\n const found = resolveHits(ranked, bundle);\n if (found.length) return found;\n }\n const lowered = needle.toLowerCase();\n return bundle.filter((record) => matches(record, lowered));\n }\n\n /**\n * The whole base, adjudicated, when it is small enough to hand over.\n *\n * At the sizes these reach — twenty records is about three thousand tokens —\n * loading everything beats searching it, and measurably: on nine questions\n * whose wording appears in no record, a reader holding the base answered\n * eight against an embedding search's four. Two of those differences are\n * structural. A reader can say no record answers the question; vector search\n * returns its nearest neighbour whatever the distance. And a reader picks the\n * record that answers the question rather than the one nearest the topic.\n * See the README's retrieval section for the measurements.\n *\n * Load it for a question, not for a session — a base read into a long\n * conversation is summarised away by the end of it.\n *\n * Refuses rather than truncates when the base is too large. A truncated base\n * is indistinguishable from a complete one, so a caller would answer \"that\n * was never decided\" from a slice it did not know was a slice.\n */\n async load(\n bundlePath: string,\n options: { budgetTokens?: number; type?: string } = {},\n ): Promise<KbLoadResult> {\n const budgetTokens = options.budgetTokens ?? DEFAULT_LOAD_BUDGET;\n const bundle = await this.list(bundlePath);\n const wanted = options.type\n ? bundle.filter((record) => record.frontmatter.type === options.type)\n : bundle;\n\n // Adjudicated against the whole base, not the filtered slice: a record's\n // replacement may be of another type.\n const adjudicated = adjudicate(wanted, bundle);\n const records = adjudicated.filter((hit) => hit.standing !== \"superseded\");\n const superseded = adjudicated\n .filter((hit) => hit.standing === \"superseded\")\n .map(stub);\n\n // Measured over what is actually handed back. Costing the full bodies would\n // refuse bases that fit comfortably once the superseded ones are stubs.\n const approxTokens =\n records.reduce((total, hit) => total + estimateTokens(hit.record), 0) +\n superseded.reduce((total, entry) => total + estimateStubTokens(entry), 0);\n\n if (approxTokens > budgetTokens) {\n return {\n loaded: false,\n recordCount: wanted.length,\n approxTokens,\n budgetTokens,\n };\n }\n\n return {\n loaded: true,\n recordCount: wanted.length,\n approxTokens,\n budgetTokens,\n records,\n superseded,\n };\n }\n\n /** How a position was arrived at, as a timeline. See `trace.ts`. */\n async trace(\n bundlePath: string,\n seedId: string,\n options: KbTraceOptions = {},\n ): Promise<KbTraceStep[]> {\n return trace(seedId, await this.list(bundlePath), options);\n }\n\n /**\n * The stored index, rebuilt if it disagrees with the records.\n *\n * Repair on read is what makes the lock-free write path safe: a writer whose\n * scan predated another writer's record publishes a momentarily stale index,\n * and the next reader through here settles it.\n */\n async readIndex(bundlePath: string): Promise<string> {\n const root = this.root(bundlePath);\n const expected = renderIndex(await this.list(bundlePath));\n const stored = await readFile(join(root, INDEX_FILE), \"utf8\").catch(\n () => null,\n );\n\n if (indexIsStale(stored, expected)) {\n await this.publish(join(root, INDEX_FILE), expected, true, INDEX_FILE);\n this.logger.info?.({\n operation: \"kb.index.repair\",\n bundlePath: root,\n reason: stored === null ? \"missing\" : \"stale\",\n });\n }\n return expected;\n }\n\n /**\n * The log, with unparseable lines reported rather than repaired.\n *\n * The log is the bundle's only artifact that cannot be reconstructed — the\n * records rebuild the index, and the code outlives both, but nothing else\n * knows which agent touched what. So a bad line is surfaced and left alone.\n */\n async readLog(bundlePath: string): Promise<ReturnType<typeof parseLog>> {\n const raw = await readFile(\n join(this.root(bundlePath), LOG_FILE),\n \"utf8\",\n ).catch(() => \"\");\n const result = parseLog(raw);\n for (const bad of result.malformed) {\n this.logger.warn?.({\n operation: \"kb.log.parse\",\n line: bad.line,\n outcome: \"skipped\",\n });\n }\n return result;\n }\n\n private async mutate(\n bundlePath: string,\n conceptId: string,\n change: (frontmatter: KbRecordFrontmatter) => KbRecordFrontmatter,\n entry: Omit<KbLogEntry, \"at\" | \"conceptId\"> & { target?: string },\n changeBody: (body: string) => string = (body) => body,\n ): Promise<KbRecord> {\n const target = this.recordPath(bundlePath, conceptId);\n const before = await readFile(target, \"utf8\").catch(() => null);\n if (before === null) throw new KbRecordNotFoundError(conceptId);\n\n const parsed = this.parse(conceptId, before);\n if (!parsed) throw new KbRecordNotFoundError(conceptId);\n\n const frontmatter = change(parsed.frontmatter);\n const body = changeBody(parsed.body);\n const contents = stringifyMarkdownWithFrontmatter(body, frontmatter);\n\n // Optimistic check, not a hard guarantee: a writer landing between this\n // read and the publish below still wins silently. It narrows the window\n // from \"the whole compose\" to two adjacent syscalls, which is as far as a\n // filesystem goes without a lock — and a lock's stale-hold failure mode is\n // worse than the residue.\n const witness = await readFile(target, \"utf8\").catch(() => null);\n if (witness === null || digest(witness) !== digest(before)) {\n throw new KbWriteConflictError(conceptId);\n }\n await this.publish(target, contents, true, conceptId);\n await this.record(this.root(bundlePath), { ...entry, conceptId });\n\n return { conceptId, frontmatter, body };\n }\n\n /**\n * Two guarantees, both about writers running in parallel.\n *\n * The record is written to a staging file and only then published, so a\n * concurrent reader sees the whole record or no record — never half of one. A\n * plain write is not atomic, and `list()` skips what it cannot parse, so a\n * torn read would be silently reported as a malformed record.\n *\n * Publishing uses `link` rather than `rename` unless the caller asked to\n * overwrite: `link` fails with EEXIST instead of replacing, which turns \"two\n * writers chose the same concept id\" from silent data loss into a collision\n * the caller has to answer. Both are atomic; only `rename` clobbers.\n *\n * The staging name deliberately does not end in `.md` — `list()` would\n * otherwise try to read it mid-write.\n */\n private async publish(\n target: string,\n contents: string,\n overwrite: boolean,\n conceptId: string,\n ): Promise<void> {\n const staging = `${target}.${process.pid}.tmp`;\n await writeFile(staging, contents, \"utf8\");\n\n try {\n if (overwrite) {\n await rename(staging, target);\n return;\n }\n await link(staging, target);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"EEXIST\") {\n throw new KbRecordAlreadyExistsError(conceptId);\n }\n throw error;\n } finally {\n // `rename` consumed the staging file; `link` left it behind.\n await unlink(staging).catch(() => undefined);\n }\n }\n\n /** Appends one log line. Failing to log must not fail the mutation. */\n private async record(\n root: string,\n entry: Omit<KbLogEntry, \"at\"> & { at?: string },\n ): Promise<void> {\n const line = renderLogEntry({ at: new Date().toISOString(), ...entry });\n await appendFile(join(root, LOG_FILE), line, \"utf8\").catch((error) => {\n this.logger.warn?.({\n operation: \"kb.log.append\",\n outcome: \"failed\",\n error: error instanceof Error ? error.message : \"unknown\",\n });\n });\n }\n\n private parse(conceptId: string, raw: string): KbRecord | null {\n const parsed = parseMarkdownWithFrontmatter(raw, kbRecordFrontmatterSchema);\n if (!parsed.frontmatter.success) {\n this.logger.warn?.({\n operation: \"kb.parse\",\n conceptId,\n outcome: \"skipped\",\n error: parsed.frontmatter.error.issues[0]?.message ?? \"invalid\",\n });\n return null;\n }\n return {\n conceptId,\n frontmatter: parsed.frontmatter.data,\n body: parsed.content,\n };\n }\n\n private root(bundlePath: string): string {\n return resolve(bundlePath);\n }\n\n // Concept ids are `<type>.<slug>` and map to a single file directly under the\n // bundle root; anything carrying a separator would escape it.\n private recordPath(bundlePath: string, conceptId: string): string {\n if (conceptId.includes(sep) || conceptId.includes(\"/\")) {\n throw new KbInvalidConceptIdError(\n \"concept id must not contain a path separator\",\n { conceptId },\n );\n }\n return join(this.root(bundlePath), `${conceptId}.md`);\n }\n}\n\n/**\n * Deliberately crude, and labelled `approx` everywhere it surfaces. A real\n * tokeniser would be a dependency carried to decide whether to avoid carrying\n * dependencies, and four characters per token is close enough to choose\n * between \"hand it over\" and \"do not\".\n */\nfunction estimateTokens(record: KbRecord): number {\n return Math.ceil(\n (record.body.length + JSON.stringify(record.frontmatter).length) / 4,\n );\n}\n\nfunction estimateStubTokens(entry: KbSupersededStub): number {\n return Math.ceil(JSON.stringify(entry).length / 4);\n}\n\n/**\n * `heads` is where the supersession chain ends, which is what a reader needs:\n * pointing at an intermediate record that is itself superseded would answer\n * \"what replaced this\" with something else that no longer holds. A broken or\n * forked chain resolves to no head, and the warning that says so travels with\n * the head record rather than here.\n */\nfunction stub(hit: KbAdjudicated): KbSupersededStub {\n return {\n conceptId: hit.record.conceptId,\n title: hit.record.frontmatter.title ?? null,\n supersededBy: hit.heads.map((head) => head.conceptId),\n at: hit.record.frontmatter.generated?.at ?? null,\n };\n}\n\nfunction matches(record: KbRecord, needle: string): boolean {\n const { title, description } = record.frontmatter;\n // The concept id is searched too: a slug is chosen to describe the record, so\n // a base where `decision.cursor-v2` does not answer \"cursor\" is surprising in\n // a way no caller would think to work around.\n return [record.conceptId, title, description, record.body].some((field) =>\n field?.toLowerCase().includes(needle),\n );\n}\n\nfunction digest(contents: string): string {\n return createHash(\"sha256\").update(contents).digest(\"hex\");\n}\n","import matter from \"gray-matter\";\nimport type { z } from \"zod\";\n\n/**\n * Frontmatter round-tripping, thin over gray-matter.\n *\n * Thin on purpose. A record is a YAML block and a markdown body, and every\n * hand-rolled reader of that shape eventually meets a nested map — an OKF\n * `generated`, a `sources[]`, a `verified[]` — and misreads it. The parser is\n * therefore borrowed and the only thing added is the schema gate below.\n */\nexport function stringifyMarkdownWithFrontmatter(\n content: string,\n frontmatter: Record<string, unknown>,\n): string {\n return matter.stringify(content, frontmatter);\n}\n\nexport function splitMarkdownFrontmatter(text: string): {\n content: string;\n prefix: string;\n raw: Record<string, unknown>;\n} {\n const file = matter(text);\n\n return {\n content: file.content,\n // Everything gray-matter consumed: the fences, the YAML, and the blank line\n // after them. Kept so a caller can rewrite a body without touching the head.\n prefix: text.slice(0, text.length - file.content.length),\n raw: file.data as Record<string, unknown>,\n };\n}\n\n/**\n * Splits, then validates the frontmatter against a schema.\n *\n * The result is a `safeParse` outcome rather than a throw: one malformed record\n * must not make a whole directory unreadable, so the caller decides whether to\n * skip it or fail.\n */\nexport function parseMarkdownWithFrontmatter<S extends z.ZodType>(\n text: string,\n schema: S,\n): {\n content: string;\n prefix: string;\n raw: Record<string, unknown>;\n frontmatter: ReturnType<S[\"safeParse\"]>;\n} {\n const { content, prefix, raw } = splitMarkdownFrontmatter(text);\n\n return {\n content,\n prefix,\n raw,\n frontmatter: schema.safeParse(raw) as ReturnType<S[\"safeParse\"]>,\n };\n}\n","/**\n * The error shape the store throws.\n *\n * Every caller here is an agent, reached through a CLI or a stdio MCP server,\n * so a bare `Error` arrives as an opaque string. What a caller has to act on is\n * carried as fields instead: `code` separates \"pick a different slug and retry\"\n * from \"this input was never going to work\", and `retriable` says whether\n * re-running the same call could succeed.\n *\n * No dependency, deliberately. This is four fields and a constructor; an error\n * library would be a runtime dependency in aid of that.\n */\nexport enum Fault {\n /** The environment is wrong — a path, a permission, a missing directory. */\n Configuration = \"Configuration\",\n /** Nothing the caller did; retrying may work. */\n System = \"System\",\n /** The call was malformed or asked for something impossible. */\n User = \"User\",\n}\n\n/** Machine-readable discriminant, stable across message rewording. */\nexport enum ErrorTypes {\n KbRecordAlreadyExists = \"KbRecordAlreadyExists\",\n KbInvalidConceptId = \"KbInvalidConceptId\",\n KbRecordNotFound = \"KbRecordNotFound\",\n KbWriteConflict = \"KbWriteConflict\",\n}\n\nexport type ErrorDetails = Record<\n string,\n string | boolean | number | string[] | boolean[] | number[]\n>;\n\nexport interface ErrorProps {\n message: string;\n errorType?: ErrorTypes;\n details?: ErrorDetails;\n name?: string;\n code?: number;\n fault?: Fault;\n retriable?: boolean;\n reportToUser?: boolean;\n}\n\nexport class BaseError extends Error {\n code: number;\n errorType?: ErrorTypes;\n fault?: Fault;\n retriable: boolean;\n reportToUser: boolean;\n details?: ErrorDetails;\n\n constructor(props: ErrorProps) {\n super(props.message);\n this.name = props.name ?? this.constructor.name;\n this.code = props.code ?? 500;\n this.errorType = props.errorType;\n this.fault = props.fault;\n this.retriable = props.retriable ?? true;\n this.reportToUser = props.reportToUser ?? false;\n this.details = props.details;\n }\n}\n","import { BaseError, ErrorTypes, Fault } from \"./errors.js\";\n\n/**\n * Every caller of this store is an agent, reached through a CLI or a stdio MCP\n * server, so a bare `Error` reaches it as an opaque string. The `code` carries\n * the distinction the caller has to act on: 409 means pick a different slug and\n * retry, 400 means the input was never going to work.\n */\nexport class KbRecordAlreadyExistsError extends BaseError {\n constructor(readonly conceptId: string) {\n super({\n message: `kb: ${conceptId} already exists — choose a more specific slug, or write with overwrite`,\n errorType: ErrorTypes.KbRecordAlreadyExists,\n code: 409,\n fault: Fault.User,\n retriable: false,\n reportToUser: true,\n details: { conceptId },\n });\n }\n}\n\nexport class KbRecordNotFoundError extends BaseError {\n constructor(readonly conceptId: string) {\n super({\n message: `kb: ${conceptId} does not exist`,\n errorType: ErrorTypes.KbRecordNotFound,\n code: 404,\n fault: Fault.User,\n retriable: false,\n reportToUser: true,\n details: { conceptId },\n });\n }\n}\n\n/** Retriable, unlike the others: re-reading and re-applying usually succeeds. */\nexport class KbWriteConflictError extends BaseError {\n constructor(readonly conceptId: string) {\n super({\n message: `kb: ${conceptId} changed while it was being updated — re-read and retry`,\n errorType: ErrorTypes.KbWriteConflict,\n code: 409,\n fault: Fault.System,\n retriable: true,\n reportToUser: true,\n details: { conceptId },\n });\n }\n}\n\nexport class KbInvalidConceptIdError extends BaseError {\n constructor(message: string, details: Record<string, string>) {\n super({\n message: `kb: ${message}`,\n errorType: ErrorTypes.KbInvalidConceptId,\n code: 400,\n fault: Fault.User,\n retriable: false,\n reportToUser: true,\n details,\n });\n }\n}\n","import type { KbRecord } from \"./kb-record.schema.js\";\n\nexport const INDEX_FILE = \"INDEX.md\";\n\nconst HEADING = \"# KB Index\";\n\n/**\n * `INDEX.md` is a projection — every byte recomputable from record frontmatter.\n *\n * That is what lets parallel writers regenerate it without a lock: they compute\n * the same function of the same records, so two concurrent regenerations differ\n * only in how recent each writer's scan was, and the next read settles it. The\n * file is therefore eventually correct rather than always correct, which is the\n * right trade for something nothing reads transactionally.\n *\n * Lines carry `description`, not just a title. A reader consults the index to\n * decide what is worth opening, and a list of titles does not answer that.\n */\nexport function renderIndex(records: KbRecord[]): string {\n const lines = [...records]\n .sort((left, right) => left.conceptId.localeCompare(right.conceptId))\n .map((record) => {\n const { frontmatter: fm } = record;\n const parts = [fm.type, fm.strauss_status];\n if (fm.tags?.length) parts.push(`tags: ${fm.tags.join(\", \")}`);\n if (fm.description) parts.push(fm.description);\n return `- [${fm.title ?? record.conceptId}](${record.conceptId}.md) — ${parts.join(\" · \")}`;\n });\n\n return `${HEADING}\\n\\n${lines.join(\"\\n\")}\\n`;\n}\n\n/** Whether the stored projection still matches the records it claims to index. */\nexport function indexIsStale(stored: string | null, expected: string): boolean {\n return stored !== expected;\n}\n","import type { KbRecord, KbRecordStatus } from \"./kb-record.schema.js\";\n\n/**\n * Why a matched record must not be read as a plain answer.\n *\n * Relevance and standing are different questions, and ranking answers only the\n * first. A superseded record is usually the older, longer, more general one and\n * its replacement is usually a narrowing, so any similarity measure favours the\n * record that is no longer true. Every hit therefore carries its standing, and\n * the caller is never handed a bare match.\n */\nexport type KbWarning =\n /** Explicitly not adopted. The most dangerous status to return unmarked: a\n * well-formed assertion of what someone decided *not* to do. */\n | { kind: \"rejected\" }\n | { kind: \"superseded\"; by: string[] }\n /** Not settled. Acting on a proposal as though it were a decision is a defect. */\n | { kind: \"unsettled\"; status: KbRecordStatus }\n /** Says a matter is unresolved. Valuable as a result, never as an answer. */\n | { kind: \"unresolved-question\" }\n /** `strauss_superseded_by` names a record that is not in the bundle. */\n | { kind: \"broken-chain\"; missing: string }\n | { kind: \"chain-cycle\"; through: string[] }\n /** Two records claim to replace this one; picking either would be a guess. */\n | { kind: \"forked-chain\"; heads: string[] }\n | { kind: \"stale\"; staleAfter: string }\n | { kind: \"unverified\" };\n\nexport type KbStanding =\n \"current\" | \"superseded\" | \"rejected\" | \"unsettled\" | \"open\";\n\nexport type KbAdjudicated = {\n record: KbRecord;\n standing: KbStanding;\n /** Where the supersession chain ends. Empty when it is broken or cyclic. */\n heads: KbRecord[];\n warnings: KbWarning[];\n};\n\nconst STANDING: Record<KbRecordStatus, KbStanding> = {\n accepted: \"current\",\n resolved: \"current\",\n draft: \"unsettled\",\n proposed: \"unsettled\",\n open: \"open\",\n rejected: \"rejected\",\n superseded: \"superseded\",\n};\n\n/**\n * Attaches standing to records a search returned.\n *\n * Adjudicating rather than filtering, deliberately. A filtered result set is\n * invisible: the caller cannot tell it missed anything, so a dropped record is\n * worse than a flagged one — it turns a knowable gap into an unknowable one.\n */\nexport function adjudicate(\n hits: KbRecord[],\n bundle: KbRecord[],\n now = new Date(),\n): KbAdjudicated[] {\n const byId = new Map(bundle.map((record) => [record.conceptId, record]));\n return hits.map((record) => {\n const status = record.frontmatter.strauss_status;\n const warnings: KbWarning[] = [];\n let heads: KbRecord[] = [];\n\n if (status === \"superseded\") {\n const resolved = resolveHeads(record, byId);\n heads = resolved.heads;\n warnings.push(...resolved.warnings);\n if (heads.length) {\n warnings.push({\n kind: \"superseded\",\n by: heads.map((head) => head.conceptId),\n });\n }\n } else if (status === \"rejected\") {\n warnings.push({ kind: \"rejected\" });\n } else if (status === \"draft\" || status === \"proposed\") {\n warnings.push({ kind: \"unsettled\", status });\n } else if (status === \"open\") {\n warnings.push({ kind: \"unresolved-question\" });\n }\n\n const staleAfter = record.frontmatter.stale_after;\n if (staleAfter && Date.parse(staleAfter) < now.getTime()) {\n warnings.push({ kind: \"stale\", staleAfter });\n }\n if (!record.frontmatter.verified?.length) {\n warnings.push({ kind: \"unverified\" });\n }\n\n return { record, standing: STANDING[status], heads, warnings };\n });\n}\n\n/**\n * Walks a supersession chain to whatever currently stands in its place.\n *\n * Both directions are followed, not just `strauss_superseded_by`. `supersede()`\n * writes the pair, but a hand-edit can leave one side behind, and a walk that\n * trusts only the forward pointer would silently return a record that something\n * in the bundle openly claims to replace.\n *\n * Resolution happens here rather than being denormalised onto records at write\n * time: a stored head would have to be rewritten on every ancestor whenever a\n * chain grows, which is derived state that goes stale — the failure this design\n * keeps avoiding elsewhere.\n */\nexport function resolveHeads(\n from: KbRecord,\n byId: Map<string, KbRecord>,\n): { heads: KbRecord[]; warnings: KbWarning[] } {\n const warnings: KbWarning[] = [];\n const heads = new Map<string, KbRecord>();\n const seen = new Set<string>([from.conceptId]);\n const queue: KbRecord[] = [from];\n\n while (queue.length) {\n const current = queue.shift() as KbRecord;\n const next = successors(current, byId);\n\n for (const missing of next.missing) {\n warnings.push({ kind: \"broken-chain\", missing });\n }\n if (!next.records.length) {\n if (current.conceptId !== from.conceptId)\n heads.set(current.conceptId, current);\n continue;\n }\n for (const record of next.records) {\n if (seen.has(record.conceptId)) {\n warnings.push({ kind: \"chain-cycle\", through: [...seen] });\n continue;\n }\n seen.add(record.conceptId);\n queue.push(record);\n }\n }\n\n if (heads.size > 1) {\n warnings.push({ kind: \"forked-chain\", heads: [...heads.keys()] });\n }\n return { heads: [...heads.values()], warnings };\n}\n\nfunction successors(\n record: KbRecord,\n byId: Map<string, KbRecord>,\n): { records: KbRecord[]; missing: string[] } {\n const ids = new Set<string>();\n const forward = record.frontmatter.strauss_superseded_by;\n if (forward) ids.add(forward);\n for (const [id, candidate] of byId) {\n if (candidate.frontmatter.strauss_supersedes?.includes(record.conceptId)) {\n ids.add(id);\n }\n }\n\n const records: KbRecord[] = [];\n const missing: string[] = [];\n for (const id of ids) {\n const found = byId.get(id);\n if (found) records.push(found);\n else missing.push(id);\n }\n return { records, missing };\n}\n","import { stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { INDEX_FILE } from \"./kb-index.js\";\nimport { LOG_FILE } from \"./kb-log.js\";\n\nexport const SEARCH_INDEX_FILE = \".index.sqlite\";\n\nconst COLLECTION = \"kb\";\n\n/**\n * BM25 over one knowledge base, via qmd's SDK.\n *\n * Reached only when a base is too large to hand over whole — see `load()`,\n * which is the first thing a reader should try.\n *\n * Lexical only. `searchLex` is BM25 and needs no model. Measured against the\n * substring scan it replaces it wins on word forms and little else: `pages`\n * finds a record saying only `page`, and eight of nine probe queries returned\n * exactly what substring returned.\n *\n * The vector tier does close the semantic gap — \"why not just use a mutex\"\n * finds the record about compare-and-swap, which no lexical match can. It stays\n * off because its scores do not separate right from wrong: a wrong hit scored\n * 0.318 against a correct one at 0.295, and a query about a subject absent from\n * the base still returns its nearest record rather than nothing.\n *\n * The index is derived and disposable — one file per base, gitignored, deleted\n * and rebuilt at will. That is only true because a base is self-contained: an\n * index covering exactly one directory can always be rebuilt from it.\n *\n * qmd is used as a **library**, and is an optional peer dependency: with it\n * absent every path here still answers, `searchBase` returns null, and the\n * store falls back to a substring scan. Its own MCP server would let an agent\n * reach a base without passing the store, and its default markdown glob would\n * return `INDEX.md` as a search hit — the case `list()` already excludes,\n * reintroduced by a reader this package does not control. Hence the explicit\n * `ignore` below.\n */\n/**\n * A hit as qmd reports it — by its own normalised path, not by our identity.\n *\n * qmd rewrites `decision.cursor-keyset.md` to `decision-cursor-keyset.md`,\n * which destroys the separator between a record's type and its slug and cannot\n * be undone from the string alone: `decision-cursor-keyset` could be the type\n * `decision` or a type `decision-cursor`. The caller maps these back through\n * the records it already holds rather than parsing them.\n */\nexport type SearchHit = { displayPath: string; score: number };\n\ntype QmdStore = {\n searchLex(\n query: string,\n options?: { limit?: number; collection?: string },\n ): Promise<{ displayPath?: string; filepath?: string; score?: number }[]>;\n addCollection(\n name: string,\n opts: { path: string; pattern?: string; ignore?: string[] },\n ): Promise<void>;\n listCollections(): Promise<unknown[]>;\n update(options?: { collections?: string[] }): Promise<unknown>;\n close(): Promise<void>;\n};\n\nexport type KbSearchLogger = {\n warn?(entry: Record<string, unknown>): void;\n};\n\n/** What this module needs of qmd, which is all it is allowed to assume. */\nexport type QmdModule = { createStore(options: unknown): Promise<unknown> };\n\nexport type SearchOptions = {\n limit?: number;\n logger?: KbSearchLogger;\n /**\n * The backend, supplied rather than imported. Production passes nothing and\n * gets the dynamic import below; a caller that already holds qmd — or a test\n * covering the present-backend branch on a machine where the optional peer is\n * not installed — passes it here.\n */\n qmd?: QmdModule;\n};\n\n/**\n * Opens (or creates) the base's index and answers a query against it.\n *\n * Re-indexes when the index is older than the newest record rather than on a\n * schedule or a write hook: the same repair-on-read rule `INDEX.md` follows,\n * and for the same reason — a derived artifact that can rebuild itself does not\n * need anyone to remember to rebuild it.\n */\nexport async function searchBase(\n bundlePath: string,\n query: string,\n options: SearchOptions = {},\n): Promise<SearchHit[] | null> {\n const qmd = options.qmd ?? (await loadQmd(options.logger));\n if (!qmd) return null;\n\n let store: QmdStore | null = null;\n try {\n store = (await qmd.createStore({\n dbPath: join(bundlePath, SEARCH_INDEX_FILE),\n config: {\n collections: {\n [COLLECTION]: {\n path: bundlePath,\n pattern: \"**/*.md\",\n // Both store-owned files are markdown and neither is a record.\n ignore: [INDEX_FILE, LOG_FILE],\n },\n },\n },\n })) as QmdStore;\n\n if (await isStale(bundlePath)) {\n await store.update({ collections: [COLLECTION] });\n }\n\n const hits = await store.searchLex(query, {\n collection: COLLECTION,\n ...(options.limit ? { limit: options.limit } : {}),\n });\n return hits\n .map((hit) => ({\n displayPath: hit.displayPath ?? hit.filepath ?? \"\",\n score: hit.score ?? 0,\n }))\n .filter((hit) => hit.displayPath.length > 0);\n } catch (error) {\n // A search index is an optimisation. Losing it must degrade recall, never\n // the answer — `query()` falls back to substring rather than failing.\n options.logger?.warn?.({\n operation: \"kb.search\",\n outcome: \"unavailable\",\n error: error instanceof Error ? error.message : \"unknown\",\n });\n return null;\n } finally {\n await store?.close().catch(() => undefined);\n }\n}\n\n/** Whether any record is newer than the index. */\nasync function isStale(bundlePath: string): Promise<boolean> {\n const indexAt = await stat(join(bundlePath, SEARCH_INDEX_FILE))\n .then((s) => s.mtimeMs)\n .catch(() => 0);\n if (!indexAt) return true;\n\n const { readdir } = await import(\"node:fs/promises\");\n const names = await readdir(bundlePath).catch(() => [] as string[]);\n for (const name of names) {\n if (!name.endsWith(\".md\") || name === INDEX_FILE) continue;\n const at = await stat(join(bundlePath, name))\n .then((s) => s.mtimeMs)\n .catch(() => 0);\n if (at > indexAt) return true;\n }\n return false;\n}\n\n/**\n * Maps qmd's normalised paths back onto real records.\n *\n * Comparing on the same normalisation rather than trying to invert it: a dot in\n * a concept id and a dash in a slug are indistinguishable once qmd has rewritten\n * the name, so the only reliable direction is forwards, from ids we hold.\n */\nexport function resolveHits<T extends { conceptId: string }>(\n hits: SearchHit[],\n records: T[],\n): T[] {\n const byName = new Map<string, T>();\n for (const record of records) {\n const name = flatten(record.conceptId);\n // A collision would make the mapping a guess; drop both rather than pick.\n if (byName.has(name)) byName.delete(name);\n else byName.set(name, record);\n }\n\n const resolved: T[] = [];\n for (const hit of hits) {\n const file = hit.displayPath.split(\"/\").pop() ?? \"\";\n const name = file.endsWith(\".md\") ? file.slice(0, -\".md\".length) : file;\n const record = byName.get(flatten(name));\n if (record) resolved.push(record);\n }\n return resolved;\n}\n\nfunction flatten(value: string): string {\n return value.replace(/[.]/g, \"-\").toLowerCase();\n}\n\n/**\n * Held in a variable rather than written inline. qmd is not a dependency of\n * this package, so a literal specifier sends the compiler looking for types\n * that are not installed; the indirection keeps the import dynamic at runtime\n * and invisible at build time, which is what an optional engine needs.\n */\nconst QMD_MODULE = \"@tobilu/qmd\";\n\n/**\n * Loaded on demand so a caller that never searches pays nothing for a\n * dependency that pulls in native SQLite bindings and a llama runtime — and\n * returns null rather than throwing when it is not installed at all, which is\n * the normal case for an optional peer.\n */\nexport async function loadQmd(\n logger?: KbSearchLogger,\n): Promise<QmdModule | null> {\n try {\n return (await import(QMD_MODULE)) as unknown as QmdModule;\n } catch {\n logger?.warn?.({ operation: \"kb.search\", outcome: \"qmd-unavailable\" });\n return null;\n }\n}\n","#!/usr/bin/env node\nimport { runKbMcpServer } from \"./mcp.js\";\n\nrunKbMcpServer().catch((error: unknown) => {\n process.stderr.write(\n `${error instanceof Error ? error.message : String(error)}\\n`,\n );\n process.exit(1);\n});\n"],"mappings":"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","names":["import_zod","import_zod","import_zod","import_zod","import_zod","conceptId","import_promises","import_node_path","matter","conceptId","bundlePath","readdir","bundlePath","conceptId"]}
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runKbMcpServer
4
+ } from "./chunk-WFHYWZX5.js";
5
+ import "./chunk-ZSYSHJVZ.js";
6
+
7
+ // src/mcp-main.ts
8
+ runKbMcpServer().catch((error) => {
9
+ process.stderr.write(
10
+ `${error instanceof Error ? error.message : String(error)}
11
+ `
12
+ );
13
+ process.exit(1);
14
+ });
15
+ //# sourceMappingURL=mcp-main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mcp-main.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { runKbMcpServer } from \"./mcp.js\";\n\nrunKbMcpServer().catch((error: unknown) => {\n process.stderr.write(\n `${error instanceof Error ? error.message : String(error)}\\n`,\n );\n process.exit(1);\n});\n"],"mappings":";;;;;;;AAGA,eAAe,EAAE,MAAM,CAAC,UAAmB;AACzC,UAAQ,OAAO;AAAA,IACb,GAAG,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA;AAAA,EAC3D;AACA,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "@saasontools/strauss-kb",
3
+ "version": "0.1.0",
4
+ "description": "Knowledge base of markdown records with standing, supersession and trace: library, CLI, and MCP server over one command set",
5
+ "license": "MIT",
6
+ "author": "Assaf Kamil",
7
+ "type": "module",
8
+ "sideEffects": false,
9
+ "main": "./dist/index.cjs",
10
+ "module": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "import": {
15
+ "types": "./dist/index.d.ts",
16
+ "default": "./dist/index.js"
17
+ },
18
+ "require": {
19
+ "types": "./dist/index.d.cts",
20
+ "default": "./dist/index.cjs"
21
+ }
22
+ },
23
+ "./package.json": "./package.json"
24
+ },
25
+ "bin": {
26
+ "strauss-kb": "./dist/cli-main.js",
27
+ "strauss-kb-mcp": "./dist/mcp-main.js"
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "README.md",
32
+ "LICENSE"
33
+ ],
34
+ "keywords": [
35
+ "mcp",
36
+ "knowledge-base",
37
+ "okf",
38
+ "markdown",
39
+ "agent"
40
+ ],
41
+ "engines": {
42
+ "node": ">=22"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "repository": {
48
+ "type": "git",
49
+ "url": "git+https://github.com/saasontools/strauss-agent-tools.git",
50
+ "directory": "packages/strauss-kb"
51
+ },
52
+ "scripts": {
53
+ "prepack": "tsup",
54
+ "build": "tsup",
55
+ "typecheck": "tsc --noEmit",
56
+ "test": "vitest run",
57
+ "lint": "eslint ."
58
+ },
59
+ "dependencies": {
60
+ "@modelcontextprotocol/sdk": "^1.30.0",
61
+ "gray-matter": "^4.0.3",
62
+ "zod": "^4.4.3"
63
+ },
64
+ "peerDependencies": {
65
+ "@tobilu/qmd": "*"
66
+ },
67
+ "peerDependenciesMeta": {
68
+ "@tobilu/qmd": {
69
+ "optional": true
70
+ }
71
+ }
72
+ }