@orquentra/codebuddy 2.6.0 → 2.7.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.
Files changed (117) hide show
  1. package/CLAUDE_CODEX.md +6 -4
  2. package/WORKFLOW.md +5 -5
  3. package/dist/{chunk-U4HAJLIB.js → chunk-3QGW53GD.js} +16 -3
  4. package/dist/chunk-3QGW53GD.js.map +1 -0
  5. package/dist/{chunk-L5AYZC3F.js → chunk-5EDGAZQN.js} +438 -68
  6. package/dist/chunk-5EDGAZQN.js.map +1 -0
  7. package/dist/chunk-5UH3BEQR.js +32 -0
  8. package/dist/chunk-5UH3BEQR.js.map +1 -0
  9. package/dist/{chunk-JFDRBPYF.js → chunk-6JCKMIQY.js} +2 -2
  10. package/dist/{chunk-UN57FKQX.js → chunk-AK2UAE32.js} +152 -862
  11. package/dist/chunk-AK2UAE32.js.map +1 -0
  12. package/dist/{chunk-GDJH7XJC.js → chunk-AL6VBTNA.js} +77 -3
  13. package/dist/chunk-AL6VBTNA.js.map +1 -0
  14. package/dist/{chunk-WHZSVY2L.js → chunk-AQHPEJR6.js} +94 -23
  15. package/dist/chunk-AQHPEJR6.js.map +1 -0
  16. package/dist/{chunk-PCE4STAY.js → chunk-BQM7FJ7D.js} +6 -6
  17. package/dist/chunk-BQM7FJ7D.js.map +1 -0
  18. package/dist/{chunk-XBJQKCLU.js → chunk-CAEPHLHC.js} +13 -9
  19. package/dist/chunk-CAEPHLHC.js.map +1 -0
  20. package/dist/chunk-CG5B3LYG.js +66 -0
  21. package/dist/chunk-CG5B3LYG.js.map +1 -0
  22. package/dist/chunk-CSRD5OQA.js +79 -0
  23. package/dist/chunk-CSRD5OQA.js.map +1 -0
  24. package/dist/{chunk-IHQM2ZDV.js → chunk-DFV2SK6C.js} +1 -1
  25. package/dist/{chunk-IHQM2ZDV.js.map → chunk-DFV2SK6C.js.map} +1 -1
  26. package/dist/chunk-EEUFU7KV.js +1634 -0
  27. package/dist/chunk-EEUFU7KV.js.map +1 -0
  28. package/dist/{chunk-UFZD3JAA.js → chunk-ESZXCZIN.js} +3 -3
  29. package/dist/{chunk-PYLH4MH5.js → chunk-FJOTP632.js} +1 -1
  30. package/dist/{chunk-SYWJG5NA.js → chunk-GKNYMNFM.js} +17 -7
  31. package/dist/{chunk-SYWJG5NA.js.map → chunk-GKNYMNFM.js.map} +1 -1
  32. package/dist/{chunk-JPHJBCZB.js → chunk-HPOR6YXA.js} +3 -2
  33. package/dist/chunk-HPOR6YXA.js.map +1 -0
  34. package/dist/chunk-JUAFCLRB.js +1014 -0
  35. package/dist/chunk-JUAFCLRB.js.map +1 -0
  36. package/dist/{chunk-776EQXDV.js → chunk-KNR2P2UY.js} +110 -83
  37. package/dist/chunk-KNR2P2UY.js.map +1 -0
  38. package/dist/{chunk-N34XHWBA.js → chunk-LCSBL5TT.js} +24 -11
  39. package/dist/chunk-LCSBL5TT.js.map +1 -0
  40. package/dist/{chunk-QDBDYCM2.js → chunk-MSUAVR3R.js} +76 -11
  41. package/dist/chunk-MSUAVR3R.js.map +1 -0
  42. package/dist/{chunk-6MKEIDX5.js → chunk-SFMIMLXR.js} +2 -2
  43. package/dist/chunk-TGFTKWOR.js +16 -0
  44. package/dist/chunk-TGFTKWOR.js.map +1 -0
  45. package/dist/chunk-TUR6YKEE.js +55 -0
  46. package/dist/chunk-TUR6YKEE.js.map +1 -0
  47. package/dist/chunk-Y7D7PUBE.js +321 -0
  48. package/dist/chunk-Y7D7PUBE.js.map +1 -0
  49. package/dist/{chunk-VAKBJV26.js → chunk-YJIA2MFL.js} +918 -2238
  50. package/dist/chunk-YJIA2MFL.js.map +1 -0
  51. package/dist/cli/index.js +14 -3
  52. package/dist/cli/index.js.map +1 -1
  53. package/dist/{cloud-7WM3YLI4.js → cloud-LXZUR3U2.js} +3 -3
  54. package/dist/{codebuddy-DM-i0Fu5.d.ts → codebuddy-DO7ZhoWn.d.ts} +37 -3
  55. package/dist/config-GEZFLXK6.js +12 -0
  56. package/dist/docker-autostart-KDME7TVC.js +12 -0
  57. package/dist/index.d.ts +48 -3
  58. package/dist/index.js +32 -25
  59. package/dist/langgraph/index.d.ts +1 -1
  60. package/dist/lean-command-CWJAVQ5G.js +18 -0
  61. package/dist/local-ST4WMRCG.js +17 -0
  62. package/dist/{main-HFHLGID4.js → main-AHNGHC4L.js} +327 -329
  63. package/dist/main-AHNGHC4L.js.map +1 -0
  64. package/dist/{memory-4RGVLPKG.js → memory-TGHVTBME.js} +9 -8
  65. package/dist/memory-TGHVTBME.js.map +1 -0
  66. package/dist/operations-7BTAAUHF.js +26 -0
  67. package/dist/postgres-docker-QQZQ2ABK.js +16 -0
  68. package/dist/pretool-command-3SXQSMAO.js +13 -0
  69. package/dist/{rerank-3P3C2UHN.js → rerank-IQAEWLQR.js} +6 -5
  70. package/dist/rerank-IQAEWLQR.js.map +1 -0
  71. package/dist/runtime-E7JQZ2BZ.js +46 -0
  72. package/dist/runtime-E7JQZ2BZ.js.map +1 -0
  73. package/dist/stdio-HOV4P3DQ.js +12 -0
  74. package/dist/stdio-HOV4P3DQ.js.map +1 -0
  75. package/dist/summaries-XV3JGGCC.js +20 -0
  76. package/dist/summaries-XV3JGGCC.js.map +1 -0
  77. package/dist/{zvec-MAPSJQSR.js → zvec-KP4KNB7Y.js} +10 -3
  78. package/dist/zvec-KP4KNB7Y.js.map +1 -0
  79. package/package.json +7 -10
  80. package/src/db/migrations/0006_fact_idempotency_key.sql +2 -0
  81. package/src/db/migrations/meta/0006_snapshot.json +1258 -0
  82. package/src/db/migrations/meta/_journal.json +7 -0
  83. package/dist/chunk-776EQXDV.js.map +0 -1
  84. package/dist/chunk-GDJH7XJC.js.map +0 -1
  85. package/dist/chunk-JPHJBCZB.js.map +0 -1
  86. package/dist/chunk-L5AYZC3F.js.map +0 -1
  87. package/dist/chunk-N34XHWBA.js.map +0 -1
  88. package/dist/chunk-PCE4STAY.js.map +0 -1
  89. package/dist/chunk-PQK3GKZH.js +0 -98
  90. package/dist/chunk-PQK3GKZH.js.map +0 -1
  91. package/dist/chunk-QDBDYCM2.js.map +0 -1
  92. package/dist/chunk-U4HAJLIB.js.map +0 -1
  93. package/dist/chunk-UN57FKQX.js.map +0 -1
  94. package/dist/chunk-VAKBJV26.js.map +0 -1
  95. package/dist/chunk-WHZSVY2L.js.map +0 -1
  96. package/dist/chunk-XBJQKCLU.js.map +0 -1
  97. package/dist/config-KFEHLV6W.js +0 -11
  98. package/dist/lean-command-C5YPFKHP.js +0 -15
  99. package/dist/local-OTOY56B4.js +0 -13
  100. package/dist/main-HFHLGID4.js.map +0 -1
  101. package/dist/memory-4RGVLPKG.js.map +0 -1
  102. package/dist/operations-LOBAQRLO.js +0 -24
  103. package/dist/pretool-command-MQQ2UNQM.js +0 -10
  104. package/dist/rerank-3P3C2UHN.js.map +0 -1
  105. package/dist/summaries-ZBV5IN7Y.js +0 -18
  106. /package/dist/{chunk-JFDRBPYF.js.map → chunk-6JCKMIQY.js.map} +0 -0
  107. /package/dist/{chunk-UFZD3JAA.js.map → chunk-ESZXCZIN.js.map} +0 -0
  108. /package/dist/{chunk-PYLH4MH5.js.map → chunk-FJOTP632.js.map} +0 -0
  109. /package/dist/{chunk-6MKEIDX5.js.map → chunk-SFMIMLXR.js.map} +0 -0
  110. /package/dist/{cloud-7WM3YLI4.js.map → cloud-LXZUR3U2.js.map} +0 -0
  111. /package/dist/{config-KFEHLV6W.js.map → config-GEZFLXK6.js.map} +0 -0
  112. /package/dist/{lean-command-C5YPFKHP.js.map → docker-autostart-KDME7TVC.js.map} +0 -0
  113. /package/dist/{local-OTOY56B4.js.map → lean-command-CWJAVQ5G.js.map} +0 -0
  114. /package/dist/{operations-LOBAQRLO.js.map → local-ST4WMRCG.js.map} +0 -0
  115. /package/dist/{pretool-command-MQQ2UNQM.js.map → operations-7BTAAUHF.js.map} +0 -0
  116. /package/dist/{summaries-ZBV5IN7Y.js.map → postgres-docker-QQZQ2ABK.js.map} +0 -0
  117. /package/dist/{zvec-MAPSJQSR.js.map → pretool-command-3SXQSMAO.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../package.json","../src/version.ts","../src/core/memory-file-store.ts","../src/core/memory-lifecycle.ts","../src/db/schema.ts","../src/core/memory-search.ts"],"sourcesContent":["{\n \"name\": \"@orquentra/codebuddy\",\n \"version\": \"2.7.0\",\n \"description\": \"Local-first project context and memory for AI coding agents.\",\n \"type\": \"module\",\n \"license\": \"MIT\",\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"author\": \"Orquentra Inc\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/Orquentra-Inc/CodeBuddy\"\n },\n \"engines\": {\n \"node\": \">=20\"\n },\n \"bin\": {\n \"codebuddy\": \"./dist/cli/index.js\"\n },\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n },\n \"./langgraph\": {\n \"types\": \"./dist/langgraph/index.d.ts\",\n \"import\": \"./dist/langgraph/index.js\"\n }\n },\n \"files\": [\n \"dist\",\n \"src/db/migrations\",\n \"README.md\",\n \"CLAUDE_CODEX.md\",\n \"ARCHITECTURE.md\",\n \"WORKFLOW.md\",\n \"LICENSE\",\n \"docker-compose.yml\",\n \"examples\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsx src/cli/index.ts\",\n \"db:generate\": \"drizzle-kit generate\",\n \"db:migrate\": \"drizzle-kit migrate\",\n \"typecheck\": \"tsc --noEmit\",\n \"test\": \"vitest run --passWithNoTests\",\n \"test:pg\": \"bash scripts/test-pg-recovery.sh\",\n \"eval:agent-ab\": \"tsx scripts/eval-agent-ab.mts\",\n \"eval:memory-ab\": \"tsx scripts/eval-memory-ab.mts\",\n \"eval:retrieval\": \"tsx scripts/eval-retrieval.mts\",\n \"eval:savings\": \"tsx scripts/eval-task-savings.mts\",\n \"branches:prune\": \"bash scripts/prune-merged-branches.sh\",\n \"lint\": \"biome check .\",\n \"format\": \"biome format --write .\",\n \"prepublishOnly\": \"node scripts/check-release.mjs && npm run typecheck && npm test && npm run build\",\n \"eval:semantic\": \"tsx scripts/eval-semantic.mts\"\n },\n \"keywords\": [\n \"mcp\",\n \"memory\",\n \"agent\",\n \"multi-agent\",\n \"huggingface\",\n \"pgvector\",\n \"postgres\",\n \"langgraph\",\n \"claude\"\n ],\n \"homepage\": \"https://github.com/Orquentra-Inc/CodeBuddy\",\n \"bugs\": {\n \"url\": \"https://github.com/Orquentra-Inc/CodeBuddy/issues\"\n },\n \"dependencies\": {\n \"@clack/prompts\": \"^1.8.1\",\n \"@huggingface/inference\": \"^4.13.19\",\n \"@modelcontextprotocol/sdk\": \"^1.17.5\",\n \"commander\": \"^14.0.0\",\n \"drizzle-orm\": \"^0.45.2\",\n \"js-tiktoken\": \"^1.0.21\",\n \"p-queue\": \"^10.0.0\",\n \"p-retry\": \"^8.0.1\",\n \"p-timeout\": \"^8.1.0\",\n \"picocolors\": \"^1.1.1\",\n \"pino\": \"^10.4.0\",\n \"postgres\": \"^3.4.7\",\n \"yaml\": \"^2.9.0\",\n \"zod\": \"^4.6.7\"\n },\n \"peerDependencies\": {\n \"@langchain/langgraph\": \"^0.2.0 || ^0.3.0\"\n },\n \"peerDependenciesMeta\": {\n \"@langchain/langgraph\": {\n \"optional\": true\n }\n },\n \"devDependencies\": {\n \"@biomejs/biome\": \"^2.5.0\",\n \"@types/node\": \"^24.3.0\",\n \"drizzle-kit\": \"^0.31.10\",\n \"tsup\": \"^8.5.0\",\n \"tsx\": \"^4.20.5\",\n \"typescript\": \"^5.9.2\",\n \"vitest\": \"^4.1.12\"\n },\n \"optionalDependencies\": {\n \"@huggingface/transformers\": \"^4.3.2\"\n }\n}\n","import pkg from \"../package.json\" with { type: \"json\" };\n\n/**\n * The single source of truth for the product version. `package.json` is the\n * canonical value (npm reads it for the published package); the CLI `--version`\n * flag and the MCP server handshake both import it from here so a version bump\n * only ever touches `package.json`. The bundler inlines this at build time, so\n * there is no runtime file read.\n */\nexport const VERSION: string = pkg.version;\n","import { mkdir, readdir, readFile, rm, stat } from \"node:fs/promises\";\nimport { dirname, join, relative, resolve, sep } from \"node:path\";\nimport { parse as parseYaml, stringify as stringifyYaml } from \"yaml\";\nimport { z } from \"zod\";\nimport { redactPersisted } from \"../core/sensitive.js\";\nimport { VERSION } from \"../version.js\";\nimport {\n assertNoSymlinkBetween,\n composeMarkdown,\n parseFrontMatter,\n writeAtomic,\n} from \"./markdown-store-fs.js\";\nimport { isEligibleMemory } from \"./memory-lifecycle.js\";\n\nexport const FACT_CATEGORIES = [\"general\", \"incident\"] as const;\nexport type FactCategory = (typeof FACT_CATEGORIES)[number];\nexport const INCIDENT_SEVERITIES = [\"low\", \"medium\", \"high\", \"critical\"] as const;\nexport type IncidentSeverity = (typeof INCIDENT_SEVERITIES)[number];\n\nconst factFrontMatterSchema = z.object({\n schemaVersion: z.literal(1),\n id: z.string().regex(/^fact_[a-z0-9]+$/),\n namespace: z.string().min(1),\n type: z.literal(\"fact\"),\n subject: z.string(),\n predicate: z.string(),\n object: z.string(),\n /** Explicit single-valued claim identity; facts sharing it are rival assertions. */\n propositionKey: z.string().min(1).nullable().default(null),\n /** The caller's `remember` idempotency key, so a retry after reindex dedupes. */\n idempotencyKey: z.string().min(1).max(200).nullable().default(null),\n confidence: z.number().min(0).max(1),\n createdAt: z.preprocess(\n (value) => (value instanceof Date ? value.toISOString() : value),\n z.string().datetime(),\n ),\n createdByAgent: z.string().nullable(),\n sourceInteractionId: z.string().nullable(),\n sourceDeleted: z.boolean(),\n category: z.enum(FACT_CATEGORIES).default(\"general\"),\n paths: z.array(z.string().min(1)).default([]),\n severity: z.enum(INCIDENT_SEVERITIES).nullable().default(null),\n introducedBy: z.string().nullable().default(null),\n resolvedBy: z.string().nullable().default(null),\n fingerprint: z\n .string()\n .regex(/^[a-f0-9]{64}$/)\n .nullable()\n .default(null),\n sourcePlanId: z.string().nullable().default(null),\n sourceSessionId: z.string().nullable().default(null),\n verification: z.array(z.string()).default([]),\n supersededBy: z.string().nullable().default(null),\n /** Namespaces granted a live reference to this fact (durable form of reference shares). */\n sharedWith: z.array(z.string().min(1)).default([]),\n});\n\nconst summaryFrontMatterSchema = z.object({\n schemaVersion: z.literal(1),\n id: z.string().regex(/^sum_[a-z0-9]+$/),\n namespace: z.string().min(1),\n type: z.literal(\"summary\"),\n sessionId: z.string().min(1),\n version: z.number().int().positive(),\n tokenCount: z.number().int().nonnegative(),\n createdAt: z.preprocess(\n (value) => (value instanceof Date ? value.toISOString() : value),\n z.string().datetime(),\n ),\n createdByAgent: z.string().nullable(),\n});\n\nexport type FactFile = z.infer<typeof factFrontMatterSchema> & {\n content: string;\n path: string;\n};\n\nexport type FactFileWrite = Omit<\n FactFile,\n | \"path\"\n | \"schemaVersion\"\n | \"type\"\n | \"category\"\n | \"paths\"\n | \"severity\"\n | \"introducedBy\"\n | \"resolvedBy\"\n | \"fingerprint\"\n | \"sourcePlanId\"\n | \"sourceSessionId\"\n | \"verification\"\n | \"supersededBy\"\n | \"sharedWith\"\n | \"propositionKey\"\n | \"idempotencyKey\"\n> &\n Partial<\n Pick<\n FactFile,\n | \"category\"\n | \"paths\"\n | \"severity\"\n | \"introducedBy\"\n | \"resolvedBy\"\n | \"fingerprint\"\n | \"sourcePlanId\"\n | \"sourceSessionId\"\n | \"verification\"\n | \"supersededBy\"\n | \"sharedWith\"\n | \"propositionKey\"\n | \"idempotencyKey\"\n >\n >;\nexport type IncidentFactFile = FactFile & {\n category: \"incident\";\n severity: IncidentSeverity;\n};\nexport type SummaryFile = z.infer<typeof summaryFrontMatterSchema> & {\n content: string;\n path: string;\n};\nexport type SummaryFileWrite = Omit<SummaryFile, \"path\" | \"schemaVersion\" | \"type\">;\n/** A managed memory file that exists on disk but could not be parsed. */\nexport type InvalidMemoryFile = { id: string; path: string; error: string };\n\n/** Files read concurrently while scanning. */\nconst SCAN_BATCH = 64;\n\n/** Parsed memory files by absolute path, valid while mtime and size match. */\nconst parsedFiles = new Map<string, { mtimeMs: number; size: number; item: { id: string } }>();\nconst loadedCaches = new Set<string>();\n\n/**\n * The parse cache also lives on disk (`.codebuddy/cache/parsed-<dir>.json`,\n * gitignored and rebuildable like the index manifest), so each short-lived\n * hook process re-parses only files that changed since any earlier process.\n * Entries are still checked against each file's mtime and size, the cache is\n * discarded when the CodeBuddy version changes (the parsed shape may differ),\n * and a missing or corrupt cache only costs a full parse.\n */\nfunction parseCachePath(root: string, directory: string): string {\n return join(\n root,\n \".codebuddy\",\n \"cache\",\n `parsed-${directory.split(sep).at(-1) ?? \"memory\"}.json`,\n );\n}\n\nasync function loadParseCache(root: string, directory: string): Promise<void> {\n if (loadedCaches.has(directory)) return;\n loadedCaches.add(directory);\n try {\n const raw = JSON.parse(await readFile(parseCachePath(root, directory), \"utf8\")) as {\n version: string;\n files: Record<string, { mtimeMs: number; size: number; item: { id: string } }>;\n };\n if (raw.version !== VERSION) return;\n for (const [name, entry] of Object.entries(raw.files)) {\n const path = join(directory, name);\n if (!parsedFiles.has(path)) parsedFiles.set(path, entry);\n }\n } catch {\n // No cache yet, or unreadable: parse from the files.\n }\n}\n\nasync function saveParseCache(root: string, directory: string): Promise<void> {\n const files: Record<string, unknown> = {};\n for (const [path, entry] of parsedFiles) {\n if (dirname(path) === directory) files[relative(directory, path)] = entry;\n }\n try {\n const path = parseCachePath(root, directory);\n await mkdir(dirname(path), { recursive: true });\n await writeAtomic(path, JSON.stringify({ version: VERSION, files }));\n } catch {\n // A cache that cannot be written only costs a re-parse next time.\n }\n}\n\nexport class MemoryFileStore {\n readonly repositoryRoot: string;\n readonly factsDirectory: string;\n readonly summariesDirectory: string;\n\n constructor(repositoryRoot = process.cwd()) {\n this.repositoryRoot = resolve(repositoryRoot);\n this.factsDirectory = join(this.repositoryRoot, \".codebuddy\", \"memory\", \"facts\");\n this.summariesDirectory = join(this.repositoryRoot, \".codebuddy\", \"memory\", \"summaries\");\n }\n\n async writeFact(raw: FactFileWrite): Promise<string> {\n // Persistence boundary: every fact writer (capture, remember, outcomes,\n // migration, resolve/supersede rewrites) is redacted here.\n const input = redactPersisted(raw);\n const path = this.factPath(input.id);\n await this.prepareDirectory(this.factsDirectory);\n await assertNoSymlinkBetween(this.repositoryRoot, dirname(path));\n\n const metadata = {\n schemaVersion: 1,\n id: input.id,\n namespace: input.namespace,\n type: \"fact\",\n subject: input.subject,\n predicate: input.predicate,\n object: input.object,\n propositionKey: input.propositionKey,\n idempotencyKey: input.idempotencyKey,\n confidence: input.confidence,\n createdAt: input.createdAt,\n createdByAgent: input.createdByAgent,\n sourceInteractionId: input.sourceInteractionId,\n sourceDeleted: input.sourceDeleted,\n category: input.category,\n paths: input.paths,\n severity: input.severity,\n introducedBy: input.introducedBy,\n resolvedBy: input.resolvedBy,\n fingerprint: input.fingerprint,\n sourcePlanId: input.sourcePlanId,\n sourceSessionId: input.sourceSessionId,\n verification: input.verification,\n supersededBy: input.supersededBy,\n sharedWith: input.sharedWith,\n };\n const frontMatter = stringifyYaml(stripUndefinedAndDefaultIncidentFields(metadata));\n\n await writeAtomic(path, composeMarkdown(frontMatter, input.content));\n return path;\n }\n\n async writeSummary(input: SummaryFileWrite): Promise<string> {\n const path = this.summaryPath(input.id);\n await this.prepareDirectory(this.summariesDirectory);\n await assertNoSymlinkBetween(this.repositoryRoot, dirname(path));\n const metadata = stringifyYaml({\n schemaVersion: 1,\n id: input.id,\n namespace: input.namespace,\n type: \"summary\",\n sessionId: input.sessionId,\n version: input.version,\n tokenCount: input.tokenCount,\n createdAt: input.createdAt,\n createdByAgent: input.createdByAgent,\n });\n await writeAtomic(path, composeMarkdown(metadata, input.content));\n return path;\n }\n\n async readFact(pathOrId: string): Promise<FactFile> {\n return this.readFactFile(pathOrId, false);\n }\n\n /** `trusted`: the caller (scan) already proved the path is a regular file in a checked directory. */\n private async readFactFile(pathOrId: string, trusted: boolean): Promise<FactFile> {\n const path = pathOrId.endsWith(\".md\") ? this.safePath(pathOrId) : this.factPath(pathOrId);\n if (!trusted) await assertNoSymlinkBetween(this.repositoryRoot, path);\n const parsed = parseFrontMatter(await readFile(path, \"utf8\"));\n const metadata = factFrontMatterSchema.parse(parseYaml(parsed.frontMatter));\n return {\n ...metadata,\n content: parsed.content.trim(),\n path,\n };\n }\n\n async listFacts(): Promise<FactFile[]> {\n return (await this.scanFacts()).facts;\n }\n\n /**\n * Parse every managed fact file. Hand-written markdown is ignored, and a\n * schema-invalid/corrupt fact is reported in `invalid` (not thrown) so one\n * bad file can't break recall, capture or reindexing for the whole project.\n * Invalid files stay on disk for manual repair.\n */\n async scanFacts(): Promise<{ facts: FactFile[]; invalid: InvalidMemoryFile[] }> {\n const scan = await this.scan(this.factsDirectory, /^fact_[a-z0-9]+\\.md$/, (path, trusted) =>\n this.readFactFile(path, trusted),\n );\n return { facts: scan.items, invalid: scan.invalid };\n }\n\n /**\n * List incident facts for the namespace, ranked by severity then recency.\n * Unlike {@link findIncidentFactsForPaths} this does not filter by path, so\n * callers (e.g. the context engine's project bootstrap) can surface the\n * repository's incident hotspots before any specific edit target is known.\n */\n async listIncidentFacts(\n input: { namespace?: string; includeResolved?: boolean } = {},\n ): Promise<IncidentFactFile[]> {\n const incidents = (await this.listFacts()).filter(\n (fact): fact is IncidentFactFile =>\n fact.category === \"incident\" && Boolean(fact.severity) && isEligibleMemory(fact, input),\n );\n\n return incidents.sort((left, right) => {\n const severity = severityRank(right.severity) - severityRank(left.severity);\n if (severity !== 0) return severity;\n return right.createdAt.localeCompare(left.createdAt);\n });\n }\n\n async findIncidentFactsForPaths(input: {\n namespace?: string;\n paths: string[];\n includeResolved?: boolean;\n }): Promise<IncidentFactFile[]> {\n const wanted = new Set(input.paths.map(normalizeMemoryPath));\n if (wanted.size === 0) return [];\n\n return (\n await this.listIncidentFacts({\n ...(input.namespace !== undefined ? { namespace: input.namespace } : {}),\n ...(input.includeResolved !== undefined ? { includeResolved: input.includeResolved } : {}),\n })\n ).filter((fact) => fact.paths.map(normalizeMemoryPath).some((path) => wanted.has(path)));\n }\n\n async readSummary(pathOrId: string): Promise<SummaryFile> {\n const path = pathOrId.endsWith(\".md\") ? this.safePath(pathOrId) : this.summaryPath(pathOrId);\n await assertNoSymlinkBetween(this.repositoryRoot, path);\n const parsed = parseFrontMatter(await readFile(path, \"utf8\"));\n return {\n ...summaryFrontMatterSchema.parse(parseYaml(parsed.frontMatter)),\n content: parsed.content.trim(),\n path,\n };\n }\n\n async listSummaries(): Promise<SummaryFile[]> {\n return (await this.scanSummaries()).summaries;\n }\n\n async scanSummaries(): Promise<{ summaries: SummaryFile[]; invalid: InvalidMemoryFile[] }> {\n const scan = await this.scan(this.summariesDirectory, /^sum_[a-z0-9]+\\.md$/, (path) =>\n this.readSummary(path),\n );\n return { summaries: scan.items, invalid: scan.invalid };\n }\n\n /**\n * Parse every managed file in `directory`. Hot path: the prompt hook and\n * capture scan facts several times per process, and the MCP server for its\n * lifetime, so each parsed file is cached by path, mtime and size (a changed\n * file is re-read, from any process) and files are read in parallel batches.\n * The directory chain is checked for symlinks once; a `withFileTypes` entry\n * that is a symlink is never `isFile()`, so no file escapes through one.\n */\n private async scan<T extends { id: string }>(\n directory: string,\n managedName: RegExp,\n read: (path: string, trusted: boolean) => Promise<T>,\n ): Promise<{ items: T[]; invalid: InvalidMemoryFile[] }> {\n try {\n await this.prepareDirectory(directory);\n await assertNoSymlinkBetween(this.repositoryRoot, directory);\n await loadParseCache(this.repositoryRoot, directory);\n let reparsed = false;\n const entries = (await readdir(directory, { withFileTypes: true }))\n .filter((entry) => entry.isFile() && managedName.test(entry.name))\n .sort((left, right) => left.name.localeCompare(right.name));\n // Forget files that were deleted, so the cache never outgrows the directory.\n const live = new Set(entries.map((entry) => join(directory, entry.name)));\n for (const path of [...parsedFiles.keys()]) {\n if (dirname(path) === directory && !live.has(path)) {\n parsedFiles.delete(path);\n reparsed = true;\n }\n }\n const outcomes: Array<{ item: T } | { invalid: InvalidMemoryFile }> = [];\n for (let start = 0; start < entries.length; start += SCAN_BATCH) {\n const batch = entries.slice(start, start + SCAN_BATCH);\n outcomes.push(\n ...(await Promise.all(\n batch.map(async (entry) => {\n const path = join(directory, entry.name);\n const id = entry.name.slice(0, -\".md\".length);\n try {\n const info = await stat(path);\n const cached = parsedFiles.get(path);\n const fresh =\n cached !== undefined &&\n cached.mtimeMs === info.mtimeMs &&\n cached.size === info.size;\n const item = fresh ? (cached.item as T) : await read(path, true);\n if (!fresh) reparsed = true;\n if (item.id !== id) {\n throw new Error(`file ID ${id} differs from front matter ID ${item.id}`);\n }\n parsedFiles.set(path, { mtimeMs: info.mtimeMs, size: info.size, item });\n return { item: { ...item } };\n } catch (error) {\n parsedFiles.delete(path);\n return {\n invalid: {\n id,\n path,\n error: (error as Error).message.split(\"\\n\")[0]?.slice(0, 300) ?? \"unreadable\",\n },\n };\n }\n }),\n )),\n );\n }\n if (reparsed) await saveParseCache(this.repositoryRoot, directory);\n const result: { items: T[]; invalid: InvalidMemoryFile[] } = { items: [], invalid: [] };\n for (const outcome of outcomes) {\n if (\"item\" in outcome) result.items.push(outcome.item);\n else result.invalid.push(outcome.invalid);\n }\n return result;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return { items: [], invalid: [] };\n throw error;\n }\n }\n\n async deleteFact(id: string): Promise<void> {\n await rm(this.factPath(id), { force: true });\n }\n\n async markIncidentResolved(id: string, resolvedBy: string): Promise<void> {\n const fact = await this.readFact(id);\n await this.writeFact(copyFact(fact, { resolvedBy }));\n }\n\n async markFactSuperseded(id: string, supersededBy: string): Promise<void> {\n const fact = await this.readFact(id);\n await this.writeFact(copyFact(fact, { supersededBy }));\n }\n\n async setFactSharedWith(id: string, sharedWith: string[]): Promise<void> {\n const fact = await this.readFact(id);\n await this.writeFact(copyFact(fact, { sharedWith: [...new Set(sharedWith)].sort() }));\n }\n\n /** Persist interaction deletion in Markdown before the derived DB is updated. */\n async markSourceInteractionDeleted(\n namespace: string,\n sourceInteractionId: string,\n ): Promise<void> {\n const { facts } = await this.scanFacts();\n for (const fact of facts) {\n if (fact.namespace !== namespace || fact.sourceInteractionId !== sourceInteractionId)\n continue;\n if (fact.sourceDeleted) continue;\n await this.writeFact(copyFact(fact, { sourceDeleted: true }));\n }\n }\n\n /** Persist that a fact's source interaction was forgotten, keeping its provenance id. */\n async markFactSourceDeleted(id: string): Promise<void> {\n const fact = await this.readFact(id);\n await this.writeFact(copyFact(fact, { sourceDeleted: true }));\n }\n\n private factPath(id: string): string {\n if (!/^fact_[a-z0-9]+$/.test(id)) {\n throw new Error(`Invalid fact id: ${id}`);\n }\n return this.safePath(join(this.factsDirectory, `${id}.md`));\n }\n\n private summaryPath(id: string): string {\n if (!/^sum_[a-z0-9]+$/.test(id)) throw new Error(`Invalid summary id: ${id}`);\n return this.safePath(join(this.summariesDirectory, `${id}.md`));\n }\n\n private safePath(path: string): string {\n const absolute = resolve(path);\n const relativePath = relative(this.repositoryRoot, absolute);\n if (relativePath === \"..\" || relativePath.startsWith(`..${sep}`) || relativePath === \"\") {\n throw new Error(`Memory path escapes repository root: ${path}`);\n }\n return absolute;\n }\n\n private async prepareDirectory(directory: string): Promise<void> {\n await assertNoSymlinkBetween(this.repositoryRoot, dirname(directory));\n await mkdir(directory, { recursive: true, mode: 0o700 });\n }\n}\n\nfunction copyFact(\n fact: FactFile,\n patch: Partial<Pick<FactFile, \"resolvedBy\" | \"supersededBy\" | \"sourceDeleted\" | \"sharedWith\">>,\n): FactFileWrite {\n const { path: _path, schemaVersion: _schemaVersion, type: _type, ...write } = fact;\n return { ...write, ...patch };\n}\n\nfunction stripUndefinedAndDefaultIncidentFields(\n metadata: Record<string, unknown>,\n): Record<string, unknown> {\n return Object.fromEntries(\n Object.entries(metadata).filter(([key, value]) => {\n if (value === undefined) return false;\n if (key === \"category\" && value === \"general\") return false;\n if ((key === \"paths\" || key === \"sharedWith\") && Array.isArray(value) && value.length === 0)\n return false;\n if (\n [\"severity\", \"introducedBy\", \"resolvedBy\", \"propositionKey\", \"idempotencyKey\"].includes(\n key,\n ) &&\n (value === null || value === undefined)\n ) {\n return false;\n }\n return true;\n }),\n );\n}\n\nfunction normalizeMemoryPath(path: string): string {\n return path.replace(/\\\\/g, \"/\").replace(/^\\.\\//, \"\");\n}\n\nfunction severityRank(severity: IncidentSeverity): number {\n switch (severity) {\n case \"critical\":\n return 4;\n case \"high\":\n return 3;\n case \"medium\":\n return 2;\n case \"low\":\n return 1;\n }\n}\n","/** Lifecycle fields shared by Markdown facts and indexed (database) fact rows. */\nexport type MemoryLifecycle = {\n namespace?: string;\n sourceDeleted: boolean;\n supersededBy?: string | null;\n category?: string;\n resolvedBy?: string | null;\n};\n\n/**\n * The single eligibility rule for durable memory evidence. A fact is eligible\n * when it belongs to the requested namespace, its source was not deleted, and\n * it was not superseded. A resolved incident is eligible only for historical\n * inspection (`includeResolved`). File context, risk signals, and database\n * recall apply this before ranking or deduplication, so ineligible evidence\n * can never displace or suppress eligible evidence.\n */\nexport function isEligibleMemory(\n fact: MemoryLifecycle,\n options: { namespace?: string; includeResolved?: boolean } = {},\n): boolean {\n if (options.namespace && fact.namespace !== options.namespace) return false;\n if (fact.sourceDeleted || fact.supersededBy) return false;\n if (fact.category === \"incident\" && fact.resolvedBy && !options.includeResolved) return false;\n return true;\n}\n","import { sql } from \"drizzle-orm\";\nimport {\n boolean,\n customType,\n index,\n integer,\n jsonb,\n numeric,\n pgEnum,\n pgTable,\n text,\n timestamp,\n uniqueIndex,\n} from \"drizzle-orm/pg-core\";\n\nexport const VECTOR_DIMENSIONS = 384;\nexport const HNSW_M = 16;\nexport const HNSW_EF_CONSTRUCTION = 64;\n\nconst vector = customType<{ data: string; driverData: string }>({\n dataType() {\n return `vector(${VECTOR_DIMENSIONS})`;\n },\n});\n\nexport const memoryTypeEnum = pgEnum(\"memory_type\", [\"interaction\", \"fact\", \"summary\"]);\nexport const interactionRoleEnum = pgEnum(\"interaction_role\", [\"user\", \"assistant\", \"system\"]);\nexport const embeddingOwnerTypeEnum = pgEnum(\"embedding_owner_type\", [\n \"interaction\",\n \"fact\",\n \"summary\",\n]);\nexport const embeddingStatusEnum = pgEnum(\"embedding_status\", [\n \"pending\",\n \"processing\",\n \"ready\",\n \"failed\",\n]);\nexport const shareModeEnum = pgEnum(\"share_mode\", [\"reference\", \"snapshot\"]);\nexport const modelCallTypeEnum = pgEnum(\"model_call_type\", [\"embedding\", \"llm\"]);\nexport const modelCallStatusEnum = pgEnum(\"model_call_status\", [\n \"success\",\n \"failed\",\n \"timeout\",\n \"rate_limited\",\n \"gated\",\n \"fallback\",\n]);\nexport const auditActionEnum = pgEnum(\"audit_action\", [\n \"remember\",\n \"remember_batch\",\n \"share\",\n \"forget\",\n \"config\",\n]);\n\nconst createdAt = timestamp(\"created_at\", { withTimezone: true }).notNull().defaultNow();\nconst updatedAt = timestamp(\"updated_at\", { withTimezone: true }).notNull().defaultNow();\n\nexport const namespaces = pgTable(\n \"namespaces\",\n {\n id: text(\"id\").primaryKey(),\n name: text(\"name\").notNull(),\n createdAt,\n updatedAt,\n },\n (table) => ({\n nameUnique: uniqueIndex(\"namespaces_name_unique\").on(table.name),\n }),\n);\n\nexport const interactions = pgTable(\n \"interactions\",\n {\n id: text(\"id\").primaryKey(),\n namespaceId: text(\"namespace_id\")\n .notNull()\n .references(() => namespaces.id, { onDelete: \"cascade\" }),\n sessionId: text(\"session_id\").notNull(),\n role: interactionRoleEnum(\"role\").notNull().default(\"user\"),\n content: text(\"content\").notNull(),\n contentHash: text(\"content_hash\").notNull(),\n tokenCount: integer(\"token_count\").notNull().default(0),\n createdByAgent: text(\"created_by_agent\"),\n createdAt,\n },\n (table) => ({\n namespaceContentHashUnique: uniqueIndex(\"interactions_namespace_content_hash_unique\").on(\n table.namespaceId,\n table.contentHash,\n ),\n namespaceSessionCreatedIdx: index(\"interactions_namespace_session_created_idx\").on(\n table.namespaceId,\n table.sessionId,\n table.createdAt,\n ),\n }),\n);\n\nexport const sessionSummaries = pgTable(\n \"session_summaries\",\n {\n id: text(\"id\").primaryKey(),\n namespaceId: text(\"namespace_id\")\n .notNull()\n .references(() => namespaces.id, { onDelete: \"cascade\" }),\n sessionId: text(\"session_id\").notNull(),\n content: text(\"content\").notNull(),\n version: integer(\"version\").notNull().default(1),\n tokenCount: integer(\"token_count\").notNull().default(0),\n createdByAgent: text(\"created_by_agent\"),\n /** See facts.fileBacked. */\n fileBacked: boolean(\"file_backed\").notNull().default(false),\n createdAt,\n },\n (table) => ({\n namespaceSessionVersionIdx: index(\"session_summaries_namespace_session_version_idx\").on(\n table.namespaceId,\n table.sessionId,\n table.version,\n ),\n }),\n);\n\nexport const facts = pgTable(\n \"facts\",\n {\n id: text(\"id\").primaryKey(),\n namespaceId: text(\"namespace_id\")\n .notNull()\n .references(() => namespaces.id, { onDelete: \"cascade\" }),\n subject: text(\"subject\").notNull(),\n predicate: text(\"predicate\").notNull(),\n object: text(\"object\").notNull(),\n propositionKey: text(\"proposition_key\"),\n /** Kept apart from content_hash so a reindex (which recomputes the hash) keeps it. */\n idempotencyKey: text(\"idempotency_key\"),\n content: text(\"content\").notNull(),\n contentHash: text(\"content_hash\").notNull(),\n confidence: numeric(\"confidence\", { precision: 5, scale: 4 }).notNull().default(\"1\"),\n /**\n * Historical provenance only — deliberately not a foreign key. Interactions\n * are ephemeral DB rows; Markdown facts must re-import after a rebuild that\n * lost them, and the ID must survive so `sourceDeleted` can be tracked.\n */\n sourceInteractionId: text(\"source_interaction_id\"),\n sourceDeleted: boolean(\"source_deleted\").notNull().default(false),\n supersededBy: text(\"superseded_by\"),\n /**\n * True once a reindex imported this row from its Markdown file. Only such\n * rows are deleted when the file disappears; DB-only legacy rows are kept.\n */\n fileBacked: boolean(\"file_backed\").notNull().default(false),\n embeddingModel: text(\"embedding_model\"),\n createdByAgent: text(\"created_by_agent\"),\n createdAt,\n updatedAt,\n },\n (table) => ({\n namespaceContentHashUnique: uniqueIndex(\"facts_namespace_content_hash_unique\").on(\n table.namespaceId,\n table.contentHash,\n ),\n namespaceSubjectIdx: index(\"facts_namespace_subject_idx\").on(table.namespaceId, table.subject),\n namespaceIdempotencyIdx: index(\"facts_namespace_idempotency_idx\").on(\n table.namespaceId,\n table.idempotencyKey,\n ),\n sourceInteractionIdx: index(\"facts_source_interaction_idx\").on(table.sourceInteractionId),\n }),\n);\n\nexport const embeddings = pgTable(\n \"embeddings\",\n {\n id: text(\"id\").primaryKey(),\n namespaceId: text(\"namespace_id\")\n .notNull()\n .references(() => namespaces.id, { onDelete: \"cascade\" }),\n ownerType: embeddingOwnerTypeEnum(\"owner_type\").notNull(),\n ownerId: text(\"owner_id\").notNull(),\n vector: vector(\"vector\").notNull(),\n embeddingModel: text(\"embedding_model\").notNull(),\n embeddingSpace: text(\"embedding_space\"),\n status: embeddingStatusEnum(\"status\").notNull().default(\"pending\"),\n attempts: integer(\"attempts\").notNull().default(0),\n lastError: text(\"last_error\"),\n /** Lease deadline while status='processing'; null otherwise. */\n leaseUntil: timestamp(\"lease_until\", { withTimezone: true }),\n leaseToken: text(\"lease_token\"),\n createdAt,\n updatedAt,\n },\n (table) => ({\n ownerUnique: uniqueIndex(\"embeddings_owner_unique\").on(table.ownerType, table.ownerId),\n namespaceModelStatusIdx: index(\"embeddings_namespace_model_status_idx\").on(\n table.namespaceId,\n table.embeddingModel,\n table.status,\n ),\n namespaceSpaceStatusIdx: index(\"embeddings_namespace_space_status_idx\").on(\n table.namespaceId,\n table.embeddingSpace,\n table.status,\n ),\n vectorHnswIdx: index(\"embeddings_vector_hnsw_idx\").using(\n \"hnsw\",\n sql`${table.vector} vector_cosine_ops`,\n ),\n }),\n);\n\nexport const shares = pgTable(\n \"shares\",\n {\n id: text(\"id\").primaryKey(),\n fromNamespaceId: text(\"from_namespace_id\")\n .notNull()\n .references(() => namespaces.id, { onDelete: \"cascade\" }),\n toNamespaceId: text(\"to_namespace_id\")\n .notNull()\n .references(() => namespaces.id, { onDelete: \"cascade\" }),\n sourceFactId: text(\"source_fact_id\")\n .notNull()\n .references(() => facts.id, { onDelete: \"cascade\" }),\n snapshotFactId: text(\"snapshot_fact_id\").references(() => facts.id, { onDelete: \"set null\" }),\n mode: shareModeEnum(\"mode\").notNull().default(\"reference\"),\n tombstoned: boolean(\"tombstoned\").notNull().default(false),\n createdByAgent: text(\"created_by_agent\"),\n createdAt,\n },\n (table) => ({\n shareUnique: uniqueIndex(\"shares_target_source_unique\").on(\n table.toNamespaceId,\n table.sourceFactId,\n table.mode,\n ),\n fromNamespaceIdx: index(\"shares_from_namespace_idx\").on(table.fromNamespaceId),\n }),\n);\n\nexport const checkpoints = pgTable(\n \"checkpoints\",\n {\n id: text(\"id\").primaryKey(),\n namespaceId: text(\"namespace_id\")\n .notNull()\n .references(() => namespaces.id, { onDelete: \"cascade\" }),\n threadId: text(\"thread_id\").notNull(),\n checkpointId: text(\"checkpoint_id\").notNull(),\n parentId: text(\"parent_id\"),\n channelValues: jsonb(\"channel_values\").notNull().default(sql`'{}'::jsonb`),\n metadata: jsonb(\"metadata\").notNull().default(sql`'{}'::jsonb`),\n createdAt,\n },\n (table) => ({\n checkpointUnique: uniqueIndex(\"checkpoints_thread_checkpoint_unique\").on(\n table.namespaceId,\n table.threadId,\n table.checkpointId,\n ),\n }),\n);\n\nexport const modelCalls = pgTable(\n \"model_calls\",\n {\n id: text(\"id\").primaryKey(),\n namespaceId: text(\"namespace_id\").references(() => namespaces.id, { onDelete: \"set null\" }),\n model: text(\"model\").notNull(),\n type: modelCallTypeEnum(\"type\").notNull(),\n latencyMs: integer(\"latency_ms\"),\n tokens: integer(\"tokens\"),\n status: modelCallStatusEnum(\"status\").notNull(),\n error: text(\"error\"),\n metadata: jsonb(\"metadata\").notNull().default(sql`'{}'::jsonb`),\n createdAt,\n },\n (table) => ({\n modelCreatedIdx: index(\"model_calls_model_created_idx\").on(table.model, table.createdAt),\n namespaceCreatedIdx: index(\"model_calls_namespace_created_idx\").on(\n table.namespaceId,\n table.createdAt,\n ),\n }),\n);\n\nexport const auditLog = pgTable(\n \"audit_log\",\n {\n id: text(\"id\").primaryKey(),\n namespaceId: text(\"namespace_id\").references(() => namespaces.id, { onDelete: \"set null\" }),\n action: auditActionEnum(\"action\").notNull(),\n entityType: text(\"entity_type\").notNull(),\n entityId: text(\"entity_id\"),\n createdByAgent: text(\"created_by_agent\"),\n metadata: jsonb(\"metadata\").notNull().default(sql`'{}'::jsonb`),\n createdAt,\n },\n (table) => ({\n namespaceActionCreatedIdx: index(\"audit_log_namespace_action_created_idx\").on(\n table.namespaceId,\n table.action,\n table.createdAt,\n ),\n }),\n);\n","import { VECTOR_DIMENSIONS } from \"../db/schema.js\";\n\n/** Bound both DB hydration and the lexical query work independently of caller input. */\nexport function candidateLimit(limit: number): number {\n if (!Number.isFinite(limit)) return 256;\n return Math.max(1, Math.min(1024, Math.trunc(limit)));\n}\n\n/** pgvector stores float4, so values must stay finite after float4 rounding. */\nexport function validSearchVector(vector: number[]): boolean {\n return (\n vector.length === VECTOR_DIMENSIONS &&\n vector.every((value) => Number.isFinite(Math.fround(value))) &&\n vector.some((value) => value !== 0)\n );\n}\n\n/** Common prompt scaffolding should not make unrelated facts look relevant. */\nconst QUERY_STOPWORDS = new Set([\n \"a\",\n \"about\",\n \"after\",\n \"again\",\n \"all\",\n \"also\",\n \"always\",\n \"an\",\n \"and\",\n \"any\",\n \"are\",\n \"as\",\n \"at\",\n \"back\",\n \"be\",\n \"been\",\n \"before\",\n \"being\",\n \"but\",\n \"by\",\n \"can\",\n \"could\",\n \"did\",\n \"do\",\n \"does\",\n \"done\",\n \"each\",\n \"even\",\n \"ever\",\n \"for\",\n \"from\",\n \"get\",\n \"gets\",\n \"got\",\n \"had\",\n \"has\",\n \"have\",\n \"here\",\n \"how\",\n \"i\",\n \"if\",\n \"in\",\n \"into\",\n \"is\",\n \"it\",\n \"its\",\n \"just\",\n \"like\",\n \"make\",\n \"me\",\n \"more\",\n \"most\",\n \"my\",\n \"never\",\n \"no\",\n \"not\",\n \"now\",\n \"of\",\n \"on\",\n \"only\",\n \"or\",\n \"other\",\n \"our\",\n \"out\",\n \"over\",\n \"please\",\n \"really\",\n \"same\",\n \"should\",\n \"so\",\n \"some\",\n \"sometimes\",\n \"still\",\n \"such\",\n \"than\",\n \"that\",\n \"the\",\n \"their\",\n \"them\",\n \"then\",\n \"there\",\n \"these\",\n \"they\",\n \"this\",\n \"those\",\n \"through\",\n \"to\",\n \"too\",\n \"under\",\n \"up\",\n \"us\",\n \"very\",\n \"was\",\n \"we\",\n \"were\",\n \"what\",\n \"when\",\n \"where\",\n \"which\",\n \"while\",\n \"who\",\n \"why\",\n \"will\",\n \"with\",\n \"would\",\n \"yet\",\n \"you\",\n \"your\",\n]);\n\n/** Literal, case-insensitive terms; no SQL wildcard or query-language syntax. */\nexport function lexicalTerms(query: string): string[] {\n return [\n ...new Set(\n query\n .slice(0, 4096)\n .toLowerCase()\n .match(/[\\p{L}\\p{N}_]+/gu) ?? [],\n ),\n ]\n .filter((term) => !QUERY_STOPWORDS.has(term))\n .slice(0, 32)\n .map((term) => term.slice(0, 64));\n}\n\n/**\n * Everyday coding words. Shared alone they do not say which fact a prompt is\n * about; in a small fact corpus they would otherwise look rare.\n */\nconst GENERIC_TERMS = new Set(\n (\n \"add added adding bug build call change changed changes check code config configure configuration \" +\n \"create data default delete error errors feature file files fix fixed handle issue line load logic \" +\n \"new option options problem project read remove result return run running save set setting settings \" +\n \"show step support test tests thing time update updated use used value values version want way work \" +\n \"works write\"\n ).split(\" \"),\n);\n/** A lone shared term admits a fact only if at most this share of the facts hold it… */\nconst RARE_TERM_SHARE = 0.005;\n/** …or at most this many (so small corpora are not held to a fraction of one fact). */\nconst RARE_TERM_MIN_DF = 2;\n\n/**\n * Lexical admission for per-prompt fact recall (#9): at least two distinct\n * non-generic shared terms, or one that is rare across the fact corpus\n * (document frequency ≤ max(2, 0.5% of facts)) and not generic. One common\n * word in common (\"session\" in an unrelated deploy prompt) is a coincidence.\n */\nexport function admitsLexically(\n shared: string[],\n documentFrequency: Map<string, number>,\n corpusSize: number,\n): boolean {\n const specific = shared.filter((term) => !GENERIC_TERMS.has(term));\n if (specific.length >= 2) return true;\n const rare = Math.max(RARE_TERM_MIN_DF, Math.floor(RARE_TERM_SHARE * corpusSize));\n return specific.some((term) => (documentFrequency.get(term) ?? 0) <= rare);\n}\n\n/** Words every captured decision shares (\"we decided to use …\"); they carry no claim. */\nconst CLAIM_BOILERPLATE = new Set([\n \"decided\",\n \"decide\",\n \"decision\",\n \"made\",\n \"use\",\n \"uses\",\n \"used\",\n \"using\",\n \"now\",\n \"going\",\n \"will\",\n]);\n\n/**\n * Jaccard overlap of two texts' content words (stopwords dropped), 0..1.\n * Used to spot near-duplicate facts: at capture (so a reworded decision is not\n * saved twice) and at recall (so two copies never take two of the few slots).\n */\nexport function termOverlap(a: string, b: string): number {\n const terms = (text: string) =>\n new Set(\n (text.toLowerCase().match(/[\\p{L}\\p{N}_]+/gu) ?? []).filter(\n (term) => !QUERY_STOPWORDS.has(term) && !CLAIM_BOILERPLATE.has(term),\n ),\n );\n const left = terms(a);\n const right = terms(b);\n if (left.size === 0 || right.size === 0) return 0;\n let shared = 0;\n for (const term of left) if (right.has(term)) shared += 1;\n return shared / (left.size + right.size - shared);\n}\n\n/** Score complete-word evidence, favoring explicit fact fields over free text. */\nexport function lexicalFactScore(\n fact: { subject: string; object: string; content: string },\n terms: string[],\n): number {\n if (terms.length === 0) return 0;\n const structured = new Set(\n `${fact.subject} ${fact.object}`.toLowerCase().match(/[\\p{L}\\p{N}_]+/gu) ?? [],\n );\n const prose = new Set(fact.content.toLowerCase().match(/[\\p{L}\\p{N}_]+/gu) ?? []);\n const points = terms.reduce(\n (total, term) => total + (structured.has(term) ? 2 : 0) + (prose.has(term) ? 1 : 0),\n 0,\n );\n return points / (3 * terms.length);\n}\n\nexport function cosine(a: number[], b: number[]): number {\n if (a.length !== b.length) return 0;\n let dot = 0;\n let na = 0;\n let nb = 0;\n for (let i = 0; i < a.length; i++) {\n const av = a[i] ?? 0;\n const bv = b[i] ?? 0;\n dot += av * bv;\n na += av * av;\n nb += bv * bv;\n }\n const denom = Math.sqrt(na) * Math.sqrt(nb);\n return denom === 0 ? 0 : dot / denom;\n}\n"],"mappings":"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","names":["facts","vector","text"]}
@@ -1,66 +1,12 @@
1
- // src/core/markdown-store-fs.ts
2
- import { lstat, open, rename } from "fs/promises";
3
- import { join, relative, resolve, sep } from "path";
4
- async function assertNoSymlinkBetween(root, target) {
5
- const absoluteRoot = resolve(root);
6
- const absoluteTarget = resolve(target);
7
- const relativeTarget = relative(absoluteRoot, absoluteTarget);
8
- if (relativeTarget === ".." || relativeTarget.startsWith(`..${sep}`)) {
9
- throw new Error(`Path escapes repository root: ${target}`);
10
- }
11
- let current = absoluteRoot;
12
- const parts = relativeTarget.split(sep).filter(Boolean);
13
- for (const part of parts) {
14
- current = join(current, part);
15
- try {
16
- const info = await lstat(current);
17
- if (info.isSymbolicLink()) {
18
- throw new Error(`Refusing to use symlinked path: ${current}`);
19
- }
20
- } catch (error) {
21
- if (error.code === "ENOENT") return;
22
- throw error;
23
- }
24
- }
25
- }
26
- async function writeAtomic(path, document, options) {
27
- const temporaryPath = `${path}.${process.pid}.${Date.now()}.tmp`;
28
- const handle = await open(temporaryPath, "wx", options?.mode ?? 384);
29
- try {
30
- await handle.writeFile(document, "utf8");
31
- await handle.sync();
32
- } finally {
33
- await handle.close();
34
- }
35
- await rename(temporaryPath, path);
36
- }
37
- function parseFrontMatter(document) {
38
- if (!document.startsWith("---\n")) {
39
- throw new Error("Markdown file must start with YAML front matter.");
40
- }
41
- const closingIndex = document.indexOf("\n---\n", 4);
42
- if (closingIndex === -1) {
43
- throw new Error("Markdown file has unterminated YAML front matter.");
44
- }
45
- return {
46
- frontMatter: document.slice(4, closingIndex),
47
- content: document.slice(closingIndex + 5)
48
- };
49
- }
50
- function composeMarkdown(frontMatterYaml, body) {
51
- const trimmedFrontMatter = frontMatterYaml.trimEnd();
52
- const content = body.endsWith("\n") ? body : `${body}
53
- `;
54
- return `---
55
- ${trimmedFrontMatter}
56
- ---
57
- ${content}`;
58
- }
1
+ import {
2
+ writeAtomic
3
+ } from "./chunk-CG5B3LYG.js";
59
4
 
60
5
  // src/core/config-file.ts
6
+ import { execFile } from "child_process";
61
7
  import { constants } from "fs";
62
- import { access, chmod, mkdir, readFile, stat, writeFile } from "fs/promises";
63
- import { basename, join as join2 } from "path";
8
+ import { access, chmod, mkdir, readdir, readFile, stat, writeFile } from "fs/promises";
9
+ import { basename, join, resolve } from "path";
64
10
  import { z } from "zod";
65
11
  var CONFIG_DIR = ".codebuddy";
66
12
  var CONFIG_FILE = "config.json";
@@ -98,7 +44,8 @@ var fileSchema = z.object({
98
44
  type: z.enum(["local", "huggingface", "openrouter"]).default("local"),
99
45
  apiKey: z.string().min(1).optional(),
100
46
  model: z.string().min(1).optional(),
101
- relevanceFloor: z.number().min(-1).max(1).optional()
47
+ relevanceFloor: z.number().min(-1).max(1).optional(),
48
+ recallFloor: z.number().min(-1).max(1).optional()
102
49
  }).optional(),
103
50
  /** Optional cross-encoder that orders recalled facts (off by default). */
104
51
  rerank: z.object({
@@ -112,6 +59,11 @@ var fileSchema = z.object({
112
59
  enabled: z.boolean(),
113
60
  graphPath: z.string().min(1).optional()
114
61
  }).optional(),
62
+ /** Git churn risk signal (`risk/signals/churn.ts`); defaults keep the 30-day window. */
63
+ risk: z.object({
64
+ churnWindowDays: z.number().int().positive().max(3650).optional(),
65
+ churnMode: z.enum(["window", "lifetime-size"]).optional()
66
+ }).optional(),
115
67
  /**
116
68
  * Optional hosted small-model layer (bring your own key). Keys are read from
117
69
  * the environment only, never from this file (`src/llm/config.ts`).
@@ -133,18 +85,20 @@ var fileSchema = z.object({
133
85
  zvec: z.object({
134
86
  enabled: z.boolean(),
135
87
  binary: z.string().min(1).optional(),
136
- timeoutMs: z.number().int().positive().max(1e4).optional()
88
+ timeoutMs: z.number().int().positive().max(1e4).optional(),
89
+ /** Task IDF coverage a vector-only file needs (0..1, default 0.3; docs/zvec-floor.md). */
90
+ minScore: z.number().min(0).max(1).optional()
137
91
  }).optional()
138
92
  });
139
93
  function configPath(cwd = process.cwd()) {
140
- return join2(cwd, CONFIG_DIR, CONFIG_FILE);
94
+ return join(cwd, CONFIG_DIR, CONFIG_FILE);
141
95
  }
142
96
  async function initProjectScaffold(cwd = process.cwd()) {
143
- const codebuddyDir = join2(cwd, CONFIG_DIR);
144
- await mkdir(join2(codebuddyDir, "memory", "facts"), { recursive: true, mode: 448 });
145
- await mkdir(join2(codebuddyDir, "memory", "summaries"), { recursive: true, mode: 448 });
146
- await mkdir(join2(codebuddyDir, "plans"), { recursive: true, mode: 448 });
147
- const gitignorePath = join2(codebuddyDir, ".gitignore");
97
+ const codebuddyDir = join(cwd, CONFIG_DIR);
98
+ await mkdir(join(codebuddyDir, "memory", "facts"), { recursive: true, mode: 448 });
99
+ await mkdir(join(codebuddyDir, "memory", "summaries"), { recursive: true, mode: 448 });
100
+ await mkdir(join(codebuddyDir, "plans"), { recursive: true, mode: 448 });
101
+ const gitignorePath = join(codebuddyDir, ".gitignore");
148
102
  try {
149
103
  await access(gitignorePath, constants.F_OK);
150
104
  let gitignore = await readFile(gitignorePath, "utf8");
@@ -161,7 +115,7 @@ ${missing.join("\n")}
161
115
  } catch {
162
116
  await writeFile(gitignorePath, PROJECT_GITIGNORE, { mode: 420 });
163
117
  }
164
- const policyPath = join2(codebuddyDir, POLICY_FILE);
118
+ const policyPath = join(codebuddyDir, POLICY_FILE);
165
119
  try {
166
120
  await access(policyPath, constants.F_OK);
167
121
  } catch {
@@ -201,13 +155,53 @@ async function writeConfigFile(config, cwd = process.cwd()) {
201
155
  }
202
156
  async function readConfigFile(cwd = process.cwd()) {
203
157
  const path = configPath(cwd);
158
+ let parsed;
204
159
  try {
205
160
  const raw = await readFile(path, "utf8");
206
- return fileSchema.parse(JSON.parse(raw));
161
+ parsed = fileSchema.parse(JSON.parse(raw));
207
162
  } catch (error) {
208
163
  if (error.code === "ENOENT") return {};
209
164
  throw error;
210
165
  }
166
+ if ((parsed.zvec?.binary !== void 0 || parsed.postgresUrl) && await trackedByGit(cwd)) {
167
+ return withoutRepositorySuppliedSecrets(parsed, path);
168
+ }
169
+ return parsed;
170
+ }
171
+ function withoutRepositorySuppliedSecrets(config, path) {
172
+ const { postgresUrl: _postgresUrl, ...rest } = config;
173
+ const ignored = [
174
+ ...config.postgresUrl ? ["postgresUrl"] : [],
175
+ ...config.zvec?.binary !== void 0 ? ["zvec.binary"] : []
176
+ ];
177
+ if (!warnedTracked.has(path)) {
178
+ warnedTracked.add(path);
179
+ process.stderr.write(
180
+ `codebuddy: ignoring ${ignored.join(" and ")} in ${path}: the file is tracked by git, so it came with the repository. Untrack it (git rm --cached) or set DATABASE_URL.
181
+ `
182
+ );
183
+ }
184
+ if (!rest.zvec) return rest;
185
+ const { binary: _binary, ...zvec } = rest.zvec;
186
+ return { ...rest, zvec };
187
+ }
188
+ var warnedTracked = /* @__PURE__ */ new Set();
189
+ var trackedCache = /* @__PURE__ */ new Map();
190
+ function trackedByGit(root) {
191
+ const key = resolve(root);
192
+ let pending = trackedCache.get(key);
193
+ if (!pending) {
194
+ pending = new Promise((done) => {
195
+ execFile(
196
+ "git",
197
+ ["ls-files", "--error-unmatch", "--", ".codebuddy/config.json"],
198
+ { cwd: key, timeout: 5e3 },
199
+ (error) => done(error === null)
200
+ );
201
+ });
202
+ trackedCache.set(key, pending);
203
+ }
204
+ return pending;
211
205
  }
212
206
  function sanitizeNamespace(value) {
213
207
  return value.replace(/[^a-zA-Z0-9_.:-]/g, "-").slice(0, 128);
@@ -220,6 +214,41 @@ async function resolveNamespaceFrom(cwd = process.cwd()) {
220
214
  const file = await readConfigFile(cwd);
221
215
  return file.namespace ?? defaultNamespaceForCwd(cwd);
222
216
  }
217
+ var LEGACY_NAMESPACE = "default";
218
+ async function migrateLegacyNamespace(cwd = process.cwd()) {
219
+ const target = defaultNamespaceForCwd(cwd);
220
+ if (target === LEGACY_NAMESPACE) return null;
221
+ const path = configPath(cwd);
222
+ let raw;
223
+ try {
224
+ raw = JSON.parse(await readFile(path, "utf8"));
225
+ } catch {
226
+ return null;
227
+ }
228
+ if (raw.namespace !== LEGACY_NAMESPACE) return null;
229
+ if (await trackedByGit(cwd) || await hasMemoryInNamespace(cwd, LEGACY_NAMESPACE)) {
230
+ return null;
231
+ }
232
+ await writeAtomic(path, `${JSON.stringify({ ...raw, namespace: target }, null, 2)}
233
+ `);
234
+ return target;
235
+ }
236
+ async function hasMemoryInNamespace(cwd, namespace) {
237
+ const memoryDir = join(cwd, ".codebuddy", "memory");
238
+ let entries;
239
+ try {
240
+ entries = await readdir(memoryDir, { recursive: true });
241
+ } catch {
242
+ return false;
243
+ }
244
+ const line = new RegExp(`^namespace: ['"]?${namespace}['"]?$`, "m");
245
+ for (const entry of entries) {
246
+ if (!entry.endsWith(".md")) continue;
247
+ const text = await readFile(join(memoryDir, entry), "utf8").catch(() => "");
248
+ if (line.test(text)) return true;
249
+ }
250
+ return false;
251
+ }
223
252
  var MISSING_DATABASE_URL = "DATABASE_URL or .codebuddy/config.json postgresUrl is required.";
224
253
  async function loadRuntimeConfig(cwd = process.cwd(), options = {}) {
225
254
  const projectRoot = process.env.CODEBUDDY_PROJECT_ROOT ?? cwd;
@@ -237,7 +266,8 @@ async function loadRuntimeConfig(cwd = process.cwd(), options = {}) {
237
266
  type: file.provider?.type ?? "local",
238
267
  ...hfToken ? { apiKey: hfToken } : {},
239
268
  ...file.provider?.model ? { model: file.provider.model } : {},
240
- ...file.provider?.relevanceFloor !== void 0 ? { relevanceFloor: file.provider.relevanceFloor } : {}
269
+ ...file.provider?.relevanceFloor !== void 0 ? { relevanceFloor: file.provider.relevanceFloor } : {},
270
+ ...file.provider?.recallFloor !== void 0 ? { recallFloor: file.provider.recallFloor } : {}
241
271
  },
242
272
  ...file.tokenBudget ? { tokenBudget: file.tokenBudget } : {},
243
273
  ...file.graphify ? { graphify: file.graphify } : {}
@@ -249,7 +279,7 @@ async function checkConfigPermissions(cwd = process.cwd()) {
249
279
  const info = await stat(path);
250
280
  const modeNumber = info.mode & 511;
251
281
  const mode = modeNumber.toString(8).padStart(4, "0");
252
- const ok = modeNumber === 384;
282
+ const ok = process.platform === "win32" || modeNumber === 384;
253
283
  return {
254
284
  path,
255
285
  exists: true,
@@ -265,12 +295,12 @@ async function checkConfigPermissions(cwd = process.cwd()) {
265
295
  }
266
296
  }
267
297
  async function checkProjectScaffold(cwd = process.cwd()) {
268
- const codebuddyDir = join2(cwd, CONFIG_DIR);
269
- const memoryFactsDir = join2(codebuddyDir, "memory", "facts");
270
- const memorySummariesDir = join2(codebuddyDir, "memory", "summaries");
271
- const plansDir = join2(codebuddyDir, "plans");
272
- const gitignorePath = join2(codebuddyDir, ".gitignore");
273
- const policyPath = join2(codebuddyDir, POLICY_FILE);
298
+ const codebuddyDir = join(cwd, CONFIG_DIR);
299
+ const memoryFactsDir = join(codebuddyDir, "memory", "facts");
300
+ const memorySummariesDir = join(codebuddyDir, "memory", "summaries");
301
+ const plansDir = join(codebuddyDir, "plans");
302
+ const gitignorePath = join(codebuddyDir, ".gitignore");
303
+ const policyPath = join(codebuddyDir, POLICY_FILE);
274
304
  const [
275
305
  codebuddyDirExists,
276
306
  memoryFactsDirExists,
@@ -409,10 +439,6 @@ async function pathExists(path) {
409
439
  }
410
440
 
411
441
  export {
412
- assertNoSymlinkBetween,
413
- writeAtomic,
414
- parseFrontMatter,
415
- composeMarkdown,
416
442
  PLAN_POLICIES,
417
443
  configPath,
418
444
  initProjectScaffold,
@@ -421,6 +447,7 @@ export {
421
447
  readConfigFile,
422
448
  defaultNamespaceForCwd,
423
449
  resolveNamespaceFrom,
450
+ migrateLegacyNamespace,
424
451
  MISSING_DATABASE_URL,
425
452
  loadRuntimeConfig,
426
453
  checkConfigPermissions,
@@ -432,4 +459,4 @@ export {
432
459
  setConfigBlock,
433
460
  redactSecrets
434
461
  };
435
- //# sourceMappingURL=chunk-776EQXDV.js.map
462
+ //# sourceMappingURL=chunk-KNR2P2UY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/config-file.ts"],"sourcesContent":["import { execFile } from \"node:child_process\";\nimport { constants } from \"node:fs\";\nimport { access, chmod, mkdir, readdir, readFile, stat, writeFile } from \"node:fs/promises\";\nimport { basename, join, resolve } from \"node:path\";\nimport { z } from \"zod\";\nimport { writeAtomic } from \"./markdown-store-fs.js\";\nimport type { CodeBuddyConfig } from \"./types.js\";\n\nexport const CONFIG_DIR = \".codebuddy\";\nexport const CONFIG_FILE = \"config.json\";\nexport const POLICY_FILE = \"policies.yaml\";\n\nconst PROJECT_GITIGNORE = [\n \"# CodeBuddy local-only files\",\n \"/config.json\",\n \"/cache/\",\n \"/memory/review/\",\n \"/memory/review-archive/\",\n \"/plans/.locks/\",\n \"/memory/.locks/\",\n \"\",\n \"# Keep these reviewable project artifacts shareable by default:\",\n \"# /memory/facts/*.md\",\n \"# /memory/summaries/*.md\",\n \"# /plans/*.md\",\n \"# If your project memory is private, ignore /memory/ in the repo root .gitignore.\",\n \"\",\n].join(\"\\n\");\n\nconst DEFAULT_POLICY_FILE = [\n \"rules:\",\n \" - id: auth-sensitive\",\n ' pattern: \"src/auth/**\"',\n ' message: \"Auth changes need careful review.\"',\n \" weight: 70\",\n \"\",\n].join(\"\\n\");\n\n/**\n * Plan policy controls how strongly CodeBuddy nudges agents toward writing\n * a plan before substantive work:\n * - off: plans are never suggested or required.\n * - suggest: agents are reminded to plan, but small tasks stay lightweight (default).\n * - required: agents should refuse substantive changes without an active plan.\n */\nexport const PLAN_POLICIES = [\"off\", \"suggest\", \"required\"] as const;\nexport type PlanPolicy = (typeof PLAN_POLICIES)[number];\n\nconst fileSchema = z.object({\n postgresUrl: z.string().min(1).optional(),\n namespace: z.string().min(1).optional(),\n tokenBudget: z.number().int().positive().optional(),\n provider: z\n .object({\n type: z.enum([\"local\", \"huggingface\", \"openrouter\"]).default(\"local\"),\n apiKey: z.string().min(1).optional(),\n model: z.string().min(1).optional(),\n relevanceFloor: z.number().min(-1).max(1).optional(),\n recallFloor: z.number().min(-1).max(1).optional(),\n })\n .optional(),\n /** Optional cross-encoder that orders recalled facts (off by default). */\n rerank: z\n .object({\n provider: z.enum([\"off\", \"local\", \"huggingface\", \"openrouter\"]).default(\"off\"),\n model: z.string().min(1).optional(),\n })\n .optional(),\n plan: z\n .object({\n policy: z.enum(PLAN_POLICIES).default(\"suggest\"),\n })\n .optional(),\n graphify: z\n .object({\n enabled: z.boolean(),\n graphPath: z.string().min(1).optional(),\n })\n .optional(),\n /** Git churn risk signal (`risk/signals/churn.ts`); defaults keep the 30-day window. */\n risk: z\n .object({\n churnWindowDays: z.number().int().positive().max(3650).optional(),\n churnMode: z.enum([\"window\", \"lifetime-size\"]).optional(),\n })\n .optional(),\n /**\n * Optional hosted small-model layer (bring your own key). Keys are read from\n * the environment only, never from this file (`src/llm/config.ts`).\n */\n llm: z\n .object({\n enabled: z.boolean(),\n provider: z.enum([\"groq\", \"openrouter\"]).optional(),\n model: z.string().min(1).optional(),\n fallback: z\n .object({ provider: z.enum([\"groq\", \"openrouter\"]), model: z.string().min(1) })\n .optional(),\n timeoutMs: z.number().int().min(100).max(5_000).optional(),\n dailyTokenBudget: z.number().int().positive().optional(),\n features: z\n .object({\n summaries: z.boolean().optional(),\n rerank: z.boolean().optional(),\n memory: z.boolean().optional(),\n })\n .optional(),\n })\n .optional(),\n /** Optional local semantic retrieval via zvec-grep (`zg`). Off unless enabled. */\n zvec: z\n .object({\n enabled: z.boolean(),\n binary: z.string().min(1).optional(),\n timeoutMs: z.number().int().positive().max(10_000).optional(),\n /** Task IDF coverage a vector-only file needs (0..1, default 0.3; docs/zvec-floor.md). */\n minScore: z.number().min(0).max(1).optional(),\n })\n .optional(),\n});\n\nexport type CodeBuddyFileConfig = z.infer<typeof fileSchema>;\n\nexport type ConfigPermissionReport = {\n path: string;\n exists: boolean;\n mode: string | null;\n ok: boolean;\n warning?: string;\n};\n\nexport type ProjectScaffoldReport = {\n codebuddyDir: { path: string; exists: boolean; ok: boolean; warning?: string };\n memoryFactsDir: { path: string; exists: boolean; ok: boolean; warning?: string };\n memorySummariesDir: { path: string; exists: boolean; ok: boolean; warning?: string };\n plansDir: { path: string; exists: boolean; ok: boolean; warning?: string };\n gitignore: { path: string; exists: boolean; ok: boolean; warning?: string };\n policies: { path: string; exists: boolean; ok: boolean; warning?: string };\n};\n\nexport function configPath(cwd = process.cwd()): string {\n return join(cwd, CONFIG_DIR, CONFIG_FILE);\n}\n\nexport async function initProjectScaffold(cwd = process.cwd()): Promise<{\n codebuddyDir: string;\n gitignorePath: string;\n policyPath: string;\n}> {\n const codebuddyDir = join(cwd, CONFIG_DIR);\n await mkdir(join(codebuddyDir, \"memory\", \"facts\"), { recursive: true, mode: 0o700 });\n await mkdir(join(codebuddyDir, \"memory\", \"summaries\"), { recursive: true, mode: 0o700 });\n await mkdir(join(codebuddyDir, \"plans\"), { recursive: true, mode: 0o700 });\n\n const gitignorePath = join(codebuddyDir, \".gitignore\");\n try {\n await access(gitignorePath, constants.F_OK);\n let gitignore = await readFile(gitignorePath, \"utf8\");\n const lines = new Set(gitignore.split(/\\r?\\n/));\n const missing = [\"/memory/review/\", \"/memory/review-archive/\"].filter(\n (line) => !lines.has(line),\n );\n if (missing.length > 0) {\n gitignore = `${gitignore.trimEnd()}\\n${missing.join(\"\\n\")}\\n`;\n await writeFile(gitignorePath, gitignore, { mode: 0o644 });\n }\n } catch {\n await writeFile(gitignorePath, PROJECT_GITIGNORE, { mode: 0o644 });\n }\n\n const policyPath = join(codebuddyDir, POLICY_FILE);\n try {\n await access(policyPath, constants.F_OK);\n } catch {\n await writeFile(policyPath, DEFAULT_POLICY_FILE, { mode: 0o644 });\n }\n\n return { codebuddyDir, gitignorePath, policyPath };\n}\n\nexport async function initConfigFile(\n cwd = process.cwd(),\n): Promise<{ path: string; created: boolean }> {\n const path = configPath(cwd);\n await initProjectScaffold(cwd);\n try {\n await access(path, constants.F_OK);\n await chmod(path, 0o600);\n return { path, created: false };\n } catch {\n const template: CodeBuddyFileConfig = {\n postgresUrl: process.env.DATABASE_URL ?? \"postgres://localhost:5432/codebuddy\",\n // Default to the folder name (sanitized) so a fresh scaffold agrees with\n // doctor's expected default instead of warning immediately after init.\n namespace: defaultNamespaceForCwd(cwd),\n provider: { type: \"local\" },\n tokenBudget: 4000,\n };\n await writeFile(path, `${JSON.stringify(template, null, 2)}\\n`, { mode: 0o600 });\n await chmod(path, 0o600);\n return { path, created: true };\n }\n}\n\n/** Persist a complete project config without changing its private permissions. */\nexport async function writeConfigFile(\n config: CodeBuddyFileConfig,\n cwd = process.cwd(),\n): Promise<{ path: string }> {\n const path = configPath(cwd);\n await initProjectScaffold(cwd);\n await writeFile(path, `${JSON.stringify(config, null, 2)}\\n`, { mode: 0o600 });\n await chmod(path, 0o600);\n return { path };\n}\n\nexport async function readConfigFile(cwd = process.cwd()): Promise<CodeBuddyFileConfig> {\n const path = configPath(cwd);\n let parsed: CodeBuddyFileConfig;\n try {\n const raw = await readFile(path, \"utf8\");\n parsed = fileSchema.parse(JSON.parse(raw));\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return {};\n throw error;\n }\n if ((parsed.zvec?.binary !== undefined || parsed.postgresUrl) && (await trackedByGit(cwd))) {\n return withoutRepositorySuppliedSecrets(parsed, path);\n }\n return parsed;\n}\n\n/**\n * `init` gitignores `.codebuddy/config.json`, so a tracked one came with the\n * repository, not from this user. A cloned repository must not choose a\n * program for CodeBuddy to run (`zvec.binary`) or a database to send project\n * memory to (`postgresUrl`); those two keys are ignored in that case and must\n * come from the environment (DATABASE_URL) or an untracked config.\n */\nfunction withoutRepositorySuppliedSecrets(\n config: CodeBuddyFileConfig,\n path: string,\n): CodeBuddyFileConfig {\n const { postgresUrl: _postgresUrl, ...rest } = config;\n const ignored = [\n ...(config.postgresUrl ? [\"postgresUrl\"] : []),\n ...(config.zvec?.binary !== undefined ? [\"zvec.binary\"] : []),\n ];\n if (!warnedTracked.has(path)) {\n warnedTracked.add(path);\n process.stderr.write(\n `codebuddy: ignoring ${ignored.join(\" and \")} in ${path}: the file is tracked by git, so it came with the repository. ` +\n \"Untrack it (git rm --cached) or set DATABASE_URL.\\n\",\n );\n }\n if (!rest.zvec) return rest;\n const { binary: _binary, ...zvec } = rest.zvec;\n return { ...rest, zvec };\n}\n\nconst warnedTracked = new Set<string>();\nconst trackedCache = new Map<string, Promise<boolean>>();\n\n/** Whether `.codebuddy/config.json` under `root` is tracked by git (cached per process). */\nfunction trackedByGit(root: string): Promise<boolean> {\n const key = resolve(root);\n let pending = trackedCache.get(key);\n if (!pending) {\n pending = new Promise((done) => {\n execFile(\n \"git\",\n [\"ls-files\", \"--error-unmatch\", \"--\", \".codebuddy/config.json\"],\n { cwd: key, timeout: 5_000 },\n (error) => done(error === null),\n );\n });\n trackedCache.set(key, pending);\n }\n return pending;\n}\n\n/**\n * The ONE namespace-resolution rule, shared by the DB/MCP runtime, every\n * file-based CLI command, and doctor. Precedence:\n * 1. CODEBUDDY_NAMESPACE env\n * 2. `.codebuddy/config.json` `namespace`\n * 3. the current folder name (sanitized) — matching what doctor reports as\n * the expected default.\n */\nexport function sanitizeNamespace(value: string): string {\n return value.replace(/[^a-zA-Z0-9_.:-]/g, \"-\").slice(0, 128);\n}\n\n/** Folder-name fallback used when neither env nor config specify a namespace. */\nexport function defaultNamespaceForCwd(cwd = process.cwd()): string {\n return sanitizeNamespace(basename(cwd)) || \"default\";\n}\n\nexport async function resolveNamespaceFrom(cwd = process.cwd()): Promise<string> {\n if (process.env.CODEBUDDY_NAMESPACE) return process.env.CODEBUDDY_NAMESPACE;\n const file = await readConfigFile(cwd);\n return file.namespace ?? defaultNamespaceForCwd(cwd);\n}\n\n/** Scaffolds before 2.0 wrote this literal, so every project shared one namespace. */\nconst LEGACY_NAMESPACE = \"default\";\n\n/**\n * Rewrites a legacy `\"namespace\": \"default\"` in `.codebuddy/config.json` to the\n * folder's default and returns the new namespace, or null when nothing changed.\n * Skipped when the file is tracked by git (it belongs to the repository) or\n * when Markdown memory was saved under \"default\" (it would drop out of recall).\n */\nexport async function migrateLegacyNamespace(cwd = process.cwd()): Promise<string | null> {\n const target = defaultNamespaceForCwd(cwd);\n if (target === LEGACY_NAMESPACE) return null;\n const path = configPath(cwd);\n let raw: Record<string, unknown>;\n try {\n raw = JSON.parse(await readFile(path, \"utf8\")) as Record<string, unknown>;\n } catch {\n return null;\n }\n if (raw.namespace !== LEGACY_NAMESPACE) return null;\n if ((await trackedByGit(cwd)) || (await hasMemoryInNamespace(cwd, LEGACY_NAMESPACE))) {\n return null;\n }\n await writeAtomic(path, `${JSON.stringify({ ...raw, namespace: target }, null, 2)}\\n`);\n return target;\n}\n\nasync function hasMemoryInNamespace(cwd: string, namespace: string): Promise<boolean> {\n const memoryDir = join(cwd, \".codebuddy\", \"memory\");\n let entries: string[];\n try {\n entries = await readdir(memoryDir, { recursive: true });\n } catch {\n return false;\n }\n const line = new RegExp(`^namespace: ['\"]?${namespace}['\"]?$`, \"m\");\n for (const entry of entries) {\n if (!entry.endsWith(\".md\")) continue;\n const text = await readFile(join(memoryDir, entry), \"utf8\").catch(() => \"\");\n if (line.test(text)) return true;\n }\n return false;\n}\n\nexport const MISSING_DATABASE_URL =\n \"DATABASE_URL or .codebuddy/config.json postgresUrl is required.\";\n\n/**\n * `requireDatabase: false` (the MCP server) returns an empty `postgresUrl`\n * instead of throwing, so file-based tools still run without PostgreSQL;\n * `createRuntime` rejects the empty URL with the same message.\n */\nexport async function loadRuntimeConfig(\n cwd = process.cwd(),\n options: { requireDatabase?: boolean } = {},\n): Promise<CodeBuddyConfig> {\n const projectRoot = process.env.CODEBUDDY_PROJECT_ROOT ?? cwd;\n const file = await readConfigFile(projectRoot);\n const postgresUrl = process.env.DATABASE_URL ?? file.postgresUrl ?? \"\";\n if (!postgresUrl && options.requireDatabase !== false) {\n throw new Error(MISSING_DATABASE_URL);\n }\n const hfToken = process.env.HF_TOKEN ?? file.provider?.apiKey;\n return {\n postgresUrl,\n namespace:\n process.env.CODEBUDDY_NAMESPACE ?? file.namespace ?? defaultNamespaceForCwd(projectRoot),\n projectRoot,\n provider: {\n type: file.provider?.type ?? \"local\",\n ...(hfToken ? { apiKey: hfToken } : {}),\n ...(file.provider?.model ? { model: file.provider.model } : {}),\n ...(file.provider?.relevanceFloor !== undefined\n ? { relevanceFloor: file.provider.relevanceFloor }\n : {}),\n ...(file.provider?.recallFloor !== undefined\n ? { recallFloor: file.provider.recallFloor }\n : {}),\n },\n ...(file.tokenBudget ? { tokenBudget: file.tokenBudget } : {}),\n ...(file.graphify ? { graphify: file.graphify } : {}),\n };\n}\n\nexport async function checkConfigPermissions(cwd = process.cwd()): Promise<ConfigPermissionReport> {\n const path = configPath(cwd);\n try {\n const info = await stat(path);\n const modeNumber = info.mode & 0o777;\n const mode = modeNumber.toString(8).padStart(4, \"0\");\n // Windows has no POSIX mode bits (stat reports 0o666/0o444 whatever chmod\n // did); the file is protected by the user profile's ACL instead.\n const ok = process.platform === \"win32\" || modeNumber === 0o600;\n return {\n path,\n exists: true,\n mode,\n ok,\n ...(ok ? {} : { warning: \".codebuddy/config.json should be chmod 0600.\" }),\n };\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return { path, exists: false, mode: null, ok: true };\n }\n throw error;\n }\n}\n\nexport async function checkProjectScaffold(cwd = process.cwd()): Promise<ProjectScaffoldReport> {\n const codebuddyDir = join(cwd, CONFIG_DIR);\n const memoryFactsDir = join(codebuddyDir, \"memory\", \"facts\");\n const memorySummariesDir = join(codebuddyDir, \"memory\", \"summaries\");\n const plansDir = join(codebuddyDir, \"plans\");\n const gitignorePath = join(codebuddyDir, \".gitignore\");\n const policyPath = join(codebuddyDir, POLICY_FILE);\n\n const [\n codebuddyDirExists,\n memoryFactsDirExists,\n memorySummariesDirExists,\n plansDirExists,\n gitignoreExists,\n policiesExist,\n ] = await Promise.all([\n pathExists(codebuddyDir),\n pathExists(memoryFactsDir),\n pathExists(memorySummariesDir),\n pathExists(plansDir),\n pathExists(gitignorePath),\n pathExists(policyPath),\n ]);\n\n return {\n codebuddyDir: {\n path: codebuddyDir,\n exists: codebuddyDirExists,\n ok: codebuddyDirExists,\n ...(codebuddyDirExists\n ? {}\n : { warning: \"Run `codebuddy use` or `codebuddy init` to create the project scaffold.\" }),\n },\n memoryFactsDir: {\n path: memoryFactsDir,\n exists: memoryFactsDirExists,\n ok: memoryFactsDirExists,\n ...(memoryFactsDirExists ? {} : { warning: \"Missing .codebuddy/memory/facts directory.\" }),\n },\n memorySummariesDir: {\n path: memorySummariesDir,\n exists: memorySummariesDirExists,\n ok: memorySummariesDirExists,\n ...(memorySummariesDirExists\n ? {}\n : { warning: \"Missing .codebuddy/memory/summaries directory.\" }),\n },\n plansDir: {\n path: plansDir,\n exists: plansDirExists,\n ok: plansDirExists,\n ...(plansDirExists ? {} : { warning: \"Missing .codebuddy/plans directory.\" }),\n },\n gitignore: {\n path: gitignorePath,\n exists: gitignoreExists,\n ok: gitignoreExists,\n ...(gitignoreExists ? {} : { warning: \"Missing .codebuddy/.gitignore.\" }),\n },\n policies: {\n path: policyPath,\n exists: policiesExist,\n ok: policiesExist,\n ...(policiesExist\n ? {}\n : {\n warning:\n \"Missing .codebuddy/policies.yaml. Run `codebuddy use` or `codebuddy init` to scaffold it.\",\n }),\n },\n };\n}\n\n/** Read the plan policy, honoring CODEBUDDY_PLAN_POLICY then the config file. */\nexport async function loadPlanPolicy(cwd = process.cwd()): Promise<PlanPolicy> {\n const fromEnv = process.env.CODEBUDDY_PLAN_POLICY;\n if (fromEnv && (PLAN_POLICIES as readonly string[]).includes(fromEnv)) {\n return fromEnv as PlanPolicy;\n }\n const file = await readConfigFile(cwd);\n return file.plan?.policy ?? \"suggest\";\n}\n\n/** Persist the plan policy into `.codebuddy/config.json`, creating it if needed. */\nexport async function setPlanPolicy(policy: PlanPolicy, cwd = process.cwd()): Promise<void> {\n await initConfigFile(cwd);\n const path = configPath(cwd);\n const raw = JSON.parse(await readFile(path, \"utf8\")) as Record<string, unknown>;\n raw.plan = { ...(raw.plan as Record<string, unknown> | undefined), policy };\n // Atomic rewrite: this file holds the postgresUrl and provider apiKey.\n await writeAtomic(path, `${JSON.stringify(raw, null, 2)}\\n`, { mode: 0o600 });\n}\n\n/** Turn optional zvec-grep retrieval on or off in `.codebuddy/config.json`. */\nexport async function setZvecEnabled(enabled: boolean, cwd = process.cwd()): Promise<void> {\n await initConfigFile(cwd);\n const path = configPath(cwd);\n const raw = JSON.parse(await readFile(path, \"utf8\")) as Record<string, unknown>;\n raw.zvec = { ...(raw.zvec as Record<string, unknown> | undefined), enabled };\n await writeAtomic(path, `${JSON.stringify(raw, null, 2)}\\n`, { mode: 0o600 });\n}\n\n/** Turn the BYOK LLM layer on or off; never writes a key (keys live in the environment). */\nexport async function setLlmEnabled(\n enabled: boolean,\n options: { provider?: \"groq\" | \"openrouter\"; model?: string } = {},\n cwd = process.cwd(),\n): Promise<void> {\n await initConfigFile(cwd);\n const path = configPath(cwd);\n const raw = JSON.parse(await readFile(path, \"utf8\")) as Record<string, unknown>;\n raw.llm = {\n ...(raw.llm as Record<string, unknown> | undefined),\n enabled,\n ...(options.provider ? { provider: options.provider } : {}),\n ...(options.model ? { model: options.model } : {}),\n };\n await writeAtomic(path, `${JSON.stringify(raw, null, 2)}\\n`, { mode: 0o600 });\n}\n\n/**\n * Replace one top-level block of `.codebuddy/config.json` (`provider`,\n * `rerank`), keeping every other key and the private file mode. The result\n * must still parse, so a bad value never reaches disk.\n */\nexport async function setConfigBlock(\n key: \"provider\" | \"rerank\",\n value: Record<string, unknown>,\n cwd = process.cwd(),\n): Promise<void> {\n await initConfigFile(cwd);\n const path = configPath(cwd);\n const raw = JSON.parse(await readFile(path, \"utf8\")) as Record<string, unknown>;\n raw[key] = value;\n fileSchema.parse(raw);\n await writeAtomic(path, `${JSON.stringify(raw, null, 2)}\\n`, { mode: 0o600 });\n}\n\nexport function redactSecrets(value: unknown): unknown {\n if (typeof value === \"string\") {\n let redacted = value;\n // Provider keys (HF, BYOK LLM) literally anywhere in a string.\n for (const key of [\n process.env.HF_TOKEN,\n process.env.GROQ_API_KEY,\n process.env.OPENROUTER_API_KEY,\n process.env.CODEBUDDY_LLM_API_KEY,\n ]) {\n if (key) redacted = redacted.split(key).join(\"[REDACTED]\");\n }\n // Credentials embedded in connection URLs (postgres://user:pass@host…).\n // The password — not the username — is the secret.\n redacted = redacted.replace(\n /(\\b[a-z][a-z0-9+.-]*:\\/\\/[^:/\\s@]+:)[^/\\s@]+(@)/gi,\n \"$1[REDACTED]$2\",\n );\n return redacted;\n }\n if (Array.isArray(value)) return value.map(redactSecrets);\n if (!value || typeof value !== \"object\") return value;\n return Object.fromEntries(\n Object.entries(value as Record<string, unknown>).map(([key, nested]) => [\n key,\n /token|apiKey|authorization|secret/i.test(key) ? \"[REDACTED]\" : redactSecrets(nested),\n ]),\n );\n}\n\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await access(path, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n"],"mappings":"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","names":[]}
@@ -2,13 +2,16 @@ import {
2
2
  failSoft,
3
3
  readHookInput,
4
4
  writeStdout
5
- } from "./chunk-PYLH4MH5.js";
5
+ } from "./chunk-FJOTP632.js";
6
6
  import {
7
7
  SessionCapsuleLedger,
8
8
  detectTestCommand,
9
9
  estimateTokens,
10
10
  findCoveringTest
11
- } from "./chunk-SYWJG5NA.js";
11
+ } from "./chunk-GKNYMNFM.js";
12
+ import {
13
+ hookRoot
14
+ } from "./chunk-TGFTKWOR.js";
12
15
  import {
13
16
  NON_SOURCE_PATH,
14
17
  discoverTaskPaths,
@@ -16,12 +19,16 @@ import {
16
19
  loadProjectArchitectureMap,
17
20
  readBoundedSource,
18
21
  taskTerms
19
- } from "./chunk-L5AYZC3F.js";
22
+ } from "./chunk-5EDGAZQN.js";
23
+ import {
24
+ loadKnownSecretValues,
25
+ redactSourceText
26
+ } from "./chunk-AL6VBTNA.js";
20
27
 
21
28
  // src/hooks/lean.ts
22
29
  import { createHash } from "crypto";
23
30
  import { appendFile, mkdir } from "fs/promises";
24
- import { extname, join, resolve } from "path";
31
+ import { extname, join } from "path";
25
32
  var LEAN_POINTER_PREFIX = "CodeBuddy likely files: ";
26
33
  var LEAN_LIMITS = {
27
34
  paths: 6,
@@ -33,7 +40,7 @@ var LEAN_HOOK_BUDGET_MS = 1500;
33
40
  var RENDER_RESERVE_MS = 300;
34
41
  async function runLeanHook(input, emit, budgetMs = LEAN_HOOK_BUDGET_MS) {
35
42
  const started = Date.now();
36
- const repositoryRoot = resolve(input.cwd ?? process.cwd());
43
+ const repositoryRoot = hookRoot(input.cwd);
37
44
  const prompt = input.prompt?.trim() ?? "";
38
45
  if (prompt === "" || prompt.startsWith("/")) return "";
39
46
  const sessionId = input.session_id ?? "default";
@@ -44,8 +51,13 @@ async function runLeanHook(input, emit, budgetMs = LEAN_HOOK_BUDGET_MS) {
44
51
  prompt,
45
52
  manifest,
46
53
  loadProjectArchitectureMap(repositoryRoot, manifest),
47
- // The rerank must answer while rendering still fits in the hook budget.
48
- { semantic: false, llmDeadlineAt: started + budgetMs - RENDER_RESERVE_MS }
54
+ // The rerank, and zvec-grep (opt-in, only with a running `zg server`),
55
+ // must answer while rendering still fits in the hook budget.
56
+ {
57
+ semantic: false,
58
+ llmDeadlineAt: started + budgetMs - RENDER_RESERVE_MS,
59
+ zvecDeadlineAt: started + budgetMs - RENDER_RESERVE_MS
60
+ }
49
61
  );
50
62
  const terms = taskTerms(prompt);
51
63
  const indexed = new Set(Object.keys(manifest.entries));
@@ -109,6 +121,7 @@ async function renderLeanBlock(input) {
109
121
  });
110
122
  }
111
123
  if (loaded.length === 0) return { text: "", emitted: [] };
124
+ const knownSecrets = await loadKnownSecretValues(input.repositoryRoot);
112
125
  const capsules = [
113
126
  {
114
127
  key: "lean:pointers",
@@ -118,7 +131,7 @@ async function renderLeanBlock(input) {
118
131
  ];
119
132
  if (isReadOnlyPrompt(input.prompt)) {
120
133
  let tokens = 0;
121
- for (const span of loaded.flatMap(spansOf)) {
134
+ for (const span of loaded.flatMap((pointer) => spansOf(pointer, knownSecrets))) {
122
135
  const cost = estimateTokens(span.text);
123
136
  if (tokens + cost > LEAN_LIMITS.spanTokens) break;
124
137
  tokens += cost;
@@ -135,11 +148,11 @@ function describePointer(pointer) {
135
148
  const detail = [ranges, test].filter(Boolean).join("; ");
136
149
  return detail ? `${pointer.path} (${detail})` : pointer.path;
137
150
  }
138
- function spansOf(pointer) {
151
+ function spansOf(pointer, knownSecrets) {
139
152
  const ranges = pointer.lines.length <= LEAN_LIMITS.wholeFileLines ? [{ start: 1, end: pointer.lines.length }] : pointer.symbols.map((s) => ({ start: s.startLine, end: s.endLine }));
140
153
  const language = extname(pointer.path).slice(1);
141
154
  return ranges.map(({ start, end }) => {
142
- const body = pointer.lines.slice(start - 1, end).join("\n");
155
+ const body = redactSourceText(pointer.lines.slice(start - 1, end).join("\n"), knownSecrets);
143
156
  const hash = createHash("sha1").update(body).digest("hex").slice(0, 12);
144
157
  return {
145
158
  key: `lean:span:${pointer.path}:${start}-${end}:${hash}`,
@@ -172,4 +185,4 @@ export {
172
185
  renderLeanBlock,
173
186
  runLeanHookCommand
174
187
  };
175
- //# sourceMappingURL=chunk-N34XHWBA.js.map
188
+ //# sourceMappingURL=chunk-LCSBL5TT.js.map