@orquentra/codebuddy 2.6.2 → 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.
- package/dist/{chunk-IBLJOHJL.js → chunk-3QGW53GD.js} +2 -2
- package/dist/{chunk-NERKZSSE.js → chunk-5EDGAZQN.js} +438 -68
- package/dist/chunk-5EDGAZQN.js.map +1 -0
- package/dist/{chunk-JFDRBPYF.js → chunk-6JCKMIQY.js} +2 -2
- package/dist/{chunk-2XDPKFUW.js → chunk-AK2UAE32.js} +46 -20
- package/dist/chunk-AK2UAE32.js.map +1 -0
- package/dist/{chunk-GDJH7XJC.js → chunk-AL6VBTNA.js} +77 -3
- package/dist/chunk-AL6VBTNA.js.map +1 -0
- package/dist/{chunk-QBDJXGG5.js → chunk-AQHPEJR6.js} +93 -22
- package/dist/chunk-AQHPEJR6.js.map +1 -0
- package/dist/{chunk-OWWCDRXW.js → chunk-BQM7FJ7D.js} +6 -6
- package/dist/chunk-BQM7FJ7D.js.map +1 -0
- package/dist/{chunk-XBJQKCLU.js → chunk-CAEPHLHC.js} +13 -9
- package/dist/chunk-CAEPHLHC.js.map +1 -0
- package/dist/chunk-CG5B3LYG.js +66 -0
- package/dist/chunk-CG5B3LYG.js.map +1 -0
- package/dist/{chunk-IHQM2ZDV.js → chunk-DFV2SK6C.js} +1 -1
- package/dist/{chunk-IHQM2ZDV.js.map → chunk-DFV2SK6C.js.map} +1 -1
- package/dist/{chunk-IMFUYEZA.js → chunk-EEUFU7KV.js} +81 -54
- package/dist/chunk-EEUFU7KV.js.map +1 -0
- package/dist/{chunk-5MQOE4J6.js → chunk-ESZXCZIN.js} +3 -3
- package/dist/{chunk-J2EAZ4MA.js → chunk-GKNYMNFM.js} +22 -25
- package/dist/chunk-GKNYMNFM.js.map +1 -0
- package/dist/{chunk-JPHJBCZB.js → chunk-HPOR6YXA.js} +3 -2
- package/dist/chunk-HPOR6YXA.js.map +1 -0
- package/dist/{chunk-S6VEG4S2.js → chunk-JUAFCLRB.js} +28 -5
- package/dist/chunk-JUAFCLRB.js.map +1 -0
- package/dist/{chunk-5KZY6OTG.js → chunk-KNR2P2UY.js} +33 -83
- package/dist/chunk-KNR2P2UY.js.map +1 -0
- package/dist/{chunk-B6CO63SQ.js → chunk-LCSBL5TT.js} +21 -9
- package/dist/chunk-LCSBL5TT.js.map +1 -0
- package/dist/{chunk-5FW3PMNP.js → chunk-MSUAVR3R.js} +76 -11
- package/dist/chunk-MSUAVR3R.js.map +1 -0
- package/dist/{chunk-RAJJSCIP.js → chunk-SFMIMLXR.js} +2 -2
- package/dist/chunk-TGFTKWOR.js +16 -0
- package/dist/chunk-TGFTKWOR.js.map +1 -0
- package/dist/chunk-TUR6YKEE.js +55 -0
- package/dist/chunk-TUR6YKEE.js.map +1 -0
- package/dist/chunk-Y7D7PUBE.js +321 -0
- package/dist/chunk-Y7D7PUBE.js.map +1 -0
- package/dist/{chunk-R7CD6EXS.js → chunk-YJIA2MFL.js} +572 -377
- package/dist/chunk-YJIA2MFL.js.map +1 -0
- package/dist/cli/index.js +5 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/{cloud-7WM3YLI4.js → cloud-LXZUR3U2.js} +3 -3
- package/dist/{codebuddy-B_pCaQrV.d.ts → codebuddy-DO7ZhoWn.d.ts} +15 -1
- package/dist/config-GEZFLXK6.js +12 -0
- package/dist/index.d.ts +46 -3
- package/dist/index.js +22 -19
- package/dist/langgraph/index.d.ts +1 -1
- package/dist/lean-command-CWJAVQ5G.js +18 -0
- package/dist/{local-5MZURLCZ.js → local-ST4WMRCG.js} +3 -3
- package/dist/{main-MTBQZGJV.js → main-AHNGHC4L.js} +213 -183
- package/dist/main-AHNGHC4L.js.map +1 -0
- package/dist/{memory-KIC5FJVW.js → memory-TGHVTBME.js} +9 -8
- package/dist/memory-TGHVTBME.js.map +1 -0
- package/dist/operations-7BTAAUHF.js +26 -0
- package/dist/pretool-command-3SXQSMAO.js +13 -0
- package/dist/{rerank-5QZ3PNUW.js → rerank-IQAEWLQR.js} +6 -5
- package/dist/rerank-IQAEWLQR.js.map +1 -0
- package/dist/{runtime-CUBOOPZO.js → runtime-E7JQZ2BZ.js} +14 -11
- package/dist/summaries-XV3JGGCC.js +20 -0
- package/dist/{zvec-MSRKT7IV.js → zvec-KP4KNB7Y.js} +10 -3
- package/package.json +1 -1
- package/src/db/migrations/0006_fact_idempotency_key.sql +2 -0
- package/src/db/migrations/meta/0006_snapshot.json +1258 -0
- package/src/db/migrations/meta/_journal.json +7 -0
- package/dist/chunk-2XDPKFUW.js.map +0 -1
- package/dist/chunk-5FW3PMNP.js.map +0 -1
- package/dist/chunk-5KZY6OTG.js.map +0 -1
- package/dist/chunk-B6CO63SQ.js.map +0 -1
- package/dist/chunk-GDJH7XJC.js.map +0 -1
- package/dist/chunk-IMFUYEZA.js.map +0 -1
- package/dist/chunk-J2EAZ4MA.js.map +0 -1
- package/dist/chunk-JPHJBCZB.js.map +0 -1
- package/dist/chunk-NERKZSSE.js.map +0 -1
- package/dist/chunk-OWWCDRXW.js.map +0 -1
- package/dist/chunk-PQK3GKZH.js +0 -98
- package/dist/chunk-PQK3GKZH.js.map +0 -1
- package/dist/chunk-QBDJXGG5.js.map +0 -1
- package/dist/chunk-R7CD6EXS.js.map +0 -1
- package/dist/chunk-S6VEG4S2.js.map +0 -1
- package/dist/chunk-XBJQKCLU.js.map +0 -1
- package/dist/config-DEVHUDIU.js +0 -11
- package/dist/lean-command-HTQCPH76.js +0 -15
- package/dist/main-MTBQZGJV.js.map +0 -1
- package/dist/memory-KIC5FJVW.js.map +0 -1
- package/dist/operations-POCHY3WR.js +0 -25
- package/dist/pretool-command-AX6JXP25.js +0 -10
- package/dist/rerank-5QZ3PNUW.js.map +0 -1
- package/dist/summaries-SB5L2TSW.js +0 -18
- /package/dist/{chunk-IBLJOHJL.js.map → chunk-3QGW53GD.js.map} +0 -0
- /package/dist/{chunk-JFDRBPYF.js.map → chunk-6JCKMIQY.js.map} +0 -0
- /package/dist/{chunk-5MQOE4J6.js.map → chunk-ESZXCZIN.js.map} +0 -0
- /package/dist/{chunk-RAJJSCIP.js.map → chunk-SFMIMLXR.js.map} +0 -0
- /package/dist/{cloud-7WM3YLI4.js.map → cloud-LXZUR3U2.js.map} +0 -0
- /package/dist/{config-DEVHUDIU.js.map → config-GEZFLXK6.js.map} +0 -0
- /package/dist/{lean-command-HTQCPH76.js.map → lean-command-CWJAVQ5G.js.map} +0 -0
- /package/dist/{local-5MZURLCZ.js.map → local-ST4WMRCG.js.map} +0 -0
- /package/dist/{operations-POCHY3WR.js.map → operations-7BTAAUHF.js.map} +0 -0
- /package/dist/{pretool-command-AX6JXP25.js.map → pretool-command-3SXQSMAO.js.map} +0 -0
- /package/dist/{runtime-CUBOOPZO.js.map → runtime-E7JQZ2BZ.js.map} +0 -0
- /package/dist/{summaries-SB5L2TSW.js.map → summaries-XV3JGGCC.js.map} +0 -0
- /package/dist/{zvec-MSRKT7IV.js.map → zvec-KP4KNB7Y.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/retrieval/zvec.ts"],"sourcesContent":["import { execFile, spawn } from \"node:child_process\";\nimport { mkdir, readFile, rm, stat } from \"node:fs/promises\";\nimport { dirname, isAbsolute, join, relative, resolve, sep } from \"node:path\";\nimport { readConfigFile } from \"../core/config-file.js\";\nimport { writeAtomic } from \"../core/markdown-store-fs.js\";\n\n/**\n * Optional semantic candidate source backed by zvec-grep (`zg`), a local\n * code-search CLI. It only *suggests* files: hits are normalized to\n * repository-relative paths, checked against the working tree, and fused with\n * the keyword ranking by the caller. Risk, policy and incident evidence are\n * never derived from it.\n *\n * Off unless `.codebuddy/config.json` sets `zvec.enabled` (or\n * `CODEBUDDY_ZVEC=1`). It never builds an index implicitly: an unready index,\n * a missing or unsupported binary, a timeout, or unparseable output all return\n * no candidates plus a reason, and callers fall back to keyword ranking.\n *\n * Queries exclude non-source paths (docs, Markdown, evals, scripts, tests,\n * CodeBuddy's own state) unless the task asks about them: otherwise prose that\n * quotes a task, such as audits or eval prompts, outranks the code it describes.\n *\n * Benchmarked: vector-only queries beat zg's default hybrid route on code\n * tasks, so only `--vector` is used. Results are parsed from zg's agent\n * markdown (`#N matchedBy=… path:start-end`), as 0.2.x has no JSON output.\n *\n * Queries use `--mode auto`: a running `zg server` answers in ~200ms, otherwise\n * zg searches directly (~400ms). Auto mode never starts the daemon. The\n * version/readiness check (~400ms) is cached on disk for a few minutes so the\n * per-prompt hook process doesn't repeat it. A failed check (not installed,\n * index not ready, timeout) is cached too, but only for 30 s and only until the\n * workspace's `.zvec-grep/` changes, so an unready index costs one probe per\n * window instead of one per call while a fresh `zg index` is still picked up.\n *\n * Freshness: queries keep `--refresh off` so the prompt path never waits on\n * embedding. Instead `refreshZvecIndex` (run by project sync after index\n * passes, off the request path) and `codebuddy zvec index` record when the zg\n * index was last brought up to date, and hits whose file changed after that are\n * dropped rather than presented with their old meaning.\n */\n\n/** zvec-grep versions whose CLI and output format this adapter was tested against. */\nconst SUPPORTED = /^0\\.2\\.\\d+$/;\nconst DEFAULT_TIMEOUT_MS = 1_500;\nconst READY_CACHE_MS = 10 * 60_000;\n/** A failed readiness check is reused this long, so a fresh `zg index` is noticed quickly. */\nconst NOT_READY_CACHE_MS = 30_000;\n/** An incremental `zg index` runs in the background; only a runaway is killed. */\nconst REFRESH_TIMEOUT_MS = 10 * 60_000;\n/** Small, fast local model; indexes this repo in seconds. */\nexport const DEFAULT_ZVEC_MODEL = \"local/potion-code-16m-v2\";\nconst DOC_GLOBS = [\"!**/*.md\", \"!**/*.mdx\", \"!docs/**\"];\nconst TEST_GLOBS = [\n \"!**/*.test.*\",\n \"!**/*.spec.*\",\n \"!**/__tests__/**\",\n \"!**/test/**\",\n \"!**/tests/**\",\n];\nconst ALWAYS_EXCLUDED_GLOBS = [\"!evals/**\", \"!scripts/**\", \"!.codebuddy/**\", \"!**/fixtures/**\"];\n\n/** zg `-g` exclusions for a task: docs and tests stay searchable when the task names them. */\nexport function zvecExcludeGlobs(task: string): string[] {\n return [\n ...(/\\b(docs?|readme|documentation|markdown)\\b/i.test(task) ? [] : DOC_GLOBS),\n ...(/\\b(tests?|spec)\\b/i.test(task) ? [] : TEST_GLOBS),\n ...ALWAYS_EXCLUDED_GLOBS,\n ];\n}\n\nconst EXCLUDED_PREFIXES = [\".codebuddy/cache/\", \".zvec-grep/\", \".git/\", \"node_modules/\"];\n\nexport type ZvecStatus =\n | \"ok\"\n | \"disabled\"\n | \"not-installed\"\n | \"unsupported-version\"\n | \"index-not-ready\"\n | \"timeout\"\n | \"failed\";\n\n/** zg provides no content hash: a valid range alone cannot prove index freshness. */\nexport type RetrievalHit = {\n path: string;\n startLine: number;\n endLine: number;\n rank: number;\n /** `anchor`: a line the task itself names (path:line, stack frame, error string). */\n source: \"zvec\" | \"anchor\";\n freshness: \"unverified\";\n};\n\nexport type ZvecCandidates = {\n status: ZvecStatus;\n /** Repo-relative paths of existing files, best first. */\n paths: string[];\n /** Every chunk hit inside `paths`, in rank order (several per file). */\n hits: RetrievalHit[];\n reason: string;\n /** Hits dropped because the file no longer exists (stale index). */\n stale: number;\n /** Hits dropped because the file changed after the zg index was refreshed. */\n changed: number;\n};\n\nexport async function zvecCandidates(\n repositoryRoot: string,\n task: string,\n options: { limit?: number } = {},\n): Promise<ZvecCandidates> {\n const root = resolve(repositoryRoot);\n const config = await zvecConfig(root);\n if (!config.enabled) return none(\"disabled\", \"zvec-grep not enabled\");\n const run = (args: string[]) => runZg(config.binary, args, root, config.timeoutMs);\n\n const cached = await readCachedReadiness(root, config.binary);\n if (cached !== \"ready\") {\n if (cached) return cached;\n const readiness = await checkReadiness(run);\n await markReadiness(root, config.binary, readiness);\n if (readiness) return readiness;\n }\n\n // zg ranks chunks, and one file can own many of them: oversample chunks so\n // `limit` distinct files survive de-duplication.\n const limit = Math.min(20, Math.max(1, Math.floor(options.limit ?? 10)));\n const chunkLimit = limit * 8;\n const globs = zvecExcludeGlobs(task);\n const result = await run([\n \"query\",\n \"--vector\",\n task,\n \"--mode\",\n \"auto\",\n \"--refresh\",\n \"off\",\n \"--limit\",\n String(chunkLimit),\n ...globs.flatMap((glob) => [\"-g\", glob]),\n ]);\n if (result.error) {\n await resetZvecCache(root);\n return fromError(result.error, \"zvec-grep query\");\n }\n\n const hits = parseHits(result.stdout).slice(0, chunkLimit);\n // With no index, zg answers with a \"run zg index\" hint instead of hits.\n if (hits.length === 0 && /\\bzg index\\b/.test(result.stdout)) {\n const notReady = none(\"index-not-ready\", NOT_READY);\n await markReadiness(root, config.binary, notReady);\n return notReady;\n }\n const indexedAt = await readIndexedAt(root);\n const paths: string[] = [];\n const candidates: RetrievalHit[] = [];\n const seen = new Set<string>();\n let stale = 0;\n let changed = 0;\n for (const hit of hits) {\n const path = toRepoRelative(root, hit.path);\n if (!path) continue;\n // Later chunks of an accepted file keep their spans; a rejected file stays rejected.\n if (seen.has(path)) {\n if (paths.includes(path)) candidates.push({ ...hit, path });\n continue;\n }\n if (paths.length >= limit) continue;\n seen.add(path);\n if (EXCLUDED_PREFIXES.some((prefix) => path.startsWith(prefix))) continue;\n try {\n const info = await stat(resolve(root, path));\n if (!info.isFile()) continue;\n // ctime too: a rename over the path keeps the old mtime.\n if (indexedAt !== null && Math.max(info.mtimeMs, info.ctimeMs) > indexedAt) {\n changed++;\n continue;\n }\n } catch {\n stale++;\n continue;\n }\n paths.push(path);\n candidates.push({ ...hit, path });\n }\n return {\n status: \"ok\",\n paths,\n hits: candidates,\n stale,\n changed,\n reason:\n `zvec-grep vector search: ${paths.length} file(s)` +\n (globs.length > 0 ? \"; non-source paths excluded\" : \"\") +\n (stale > 0 ? `, ${stale} stale hit(s) dropped` : \"\") +\n (changed > 0 ? `, ${changed} hit(s) changed since the zg index dropped` : \"\") +\n (indexedAt === null\n ? \"; index age unknown, hits unverified (run `codebuddy zvec index`)\"\n : \"\"),\n };\n}\n\nexport type ZvecRefresh = { status: ZvecStatus; reason: string };\n\n/**\n * Incrementally refresh an existing zg index (no `--rebuild`, no embedding\n * flag) and record when it started, which is the freshness bound applied to\n * later hits. Never builds a missing index and never starts a zg server; every\n * failure is returned as a status for the caller to log.\n */\nexport async function refreshZvecIndex(\n repositoryRoot: string,\n options: { signal?: AbortSignal } = {},\n): Promise<ZvecRefresh> {\n const root = resolve(repositoryRoot);\n const config = await zvecConfig(root);\n if (!config.enabled) return { status: \"disabled\", reason: \"zvec-grep not enabled\" };\n const readiness = await checkReadiness((args) =>\n runZg(config.binary, args, root, config.timeoutMs),\n );\n if (readiness) return { status: readiness.status, reason: readiness.reason };\n const startedAt = Date.now();\n const result = await runZg(\n config.binary,\n [\"index\", root, \"--mode\", \"auto\"],\n root,\n REFRESH_TIMEOUT_MS,\n options.signal,\n );\n if (result.error) {\n const failure = fromError(result.error, \"zvec-grep refresh\");\n return { status: failure.status, reason: failure.reason };\n }\n await markIndexed(root, startedAt);\n return { status: \"ok\", reason: \"zvec-grep index refreshed\" };\n}\n\n/** Extract bounded, validated line ranges in retrieval rank order. */\nexport function parseHits(output: string): RetrievalHit[] {\n return [...output.matchAll(/^#(\\d+) matchedBy=\\S+ (.+?):(\\d+)(?:-(\\d+))?\\s*$/gm)]\n .flatMap((match) => {\n const rank = Number(match[1]);\n const startLine = Number(match[3]);\n const endLine = Number(match[4] ?? match[3]);\n if (startLine < 1 || endLine < startLine) return [];\n return [\n {\n path: match[2] as string,\n rank,\n startLine,\n endLine,\n source: \"zvec\" as const,\n freshness: \"unverified\" as const,\n },\n ];\n })\n .sort((left, right) => left.rank - right.rank);\n}\n\n/** Forget the cached readiness check, e.g. after `zg index` or a failed query. */\nexport async function resetZvecCache(repositoryRoot: string): Promise<void> {\n await rm(readyPath(resolve(repositoryRoot)), { force: true });\n}\n\nexport type ZvecReport = {\n enabled: boolean;\n binary: string;\n version: string | null;\n supported: boolean;\n indexReady: boolean;\n server: \"running\" | \"stopped\" | \"unknown\";\n reason: string;\n};\n\n/** Everything `codebuddy zvec status` shows. Read-only; never builds an index. */\nexport async function zvecStatus(repositoryRoot: string): Promise<ZvecReport> {\n const root = resolve(repositoryRoot);\n const config = await zvecConfig(root);\n const run = (args: string[]) =>\n runZg(config.binary, args, root, Math.max(config.timeoutMs, 5_000));\n const version = await run([\"version\"]);\n const found = version.error ? null : (version.stdout.trim().split(/\\s+/).pop() ?? null);\n const supported = found !== null && SUPPORTED.test(found);\n const readiness = supported ? await checkReadiness(run) : null;\n const server = supported ? await run([\"server\", \"status\"]) : null;\n return {\n enabled: config.enabled,\n binary: config.binary,\n version: found,\n supported,\n indexReady: supported && readiness === null,\n server:\n server === null || server.error\n ? \"unknown\"\n : /running|ready/i.test(server.stdout)\n ? \"running\"\n : \"stopped\",\n reason: version.error\n ? fromError(version.error, \"zvec-grep version check\").reason\n : !supported\n ? `zvec-grep ${found ?? \"unknown\"} is not 0.2.x`\n : (readiness?.reason ?? \"ready\"),\n };\n}\n\n/**\n * Build or incrementally refresh the zg index for this repository, streaming\n * zg's progress. Explicit by design: CodeBuddy never indexes implicitly.\n */\nexport async function buildZvecIndex(\n repositoryRoot: string,\n options: {\n model?: string;\n rebuild?: boolean /** Discard zg's progress output. */;\n quiet?: boolean;\n } = {},\n): Promise<number> {\n const root = resolve(repositoryRoot);\n const config = await zvecConfig(root);\n const args = [\n \"index\",\n root,\n \"--embedding\",\n options.model ?? DEFAULT_ZVEC_MODEL,\n \"--mode\",\n \"auto\",\n ];\n if (options.rebuild) args.push(\"--rebuild\");\n const startedAt = Date.now();\n const code = await new Promise<number>((resolveRun, reject) => {\n const child = spawn(config.binary, args, {\n cwd: root,\n stdio: options.quiet ? \"ignore\" : \"inherit\",\n });\n child.on(\"error\", reject);\n child.on(\"close\", (exit) => resolveRun(exit ?? 1));\n });\n await resetZvecCache(root);\n if (code === 0) await markIndexed(root, startedAt);\n return code;\n}\n\nconst NOT_READY = \"zvec-grep index missing or not ready; run `codebuddy zvec index`\";\n\n/** Version + index readiness. Returns null when ready, else the failure. */\nasync function checkReadiness(\n run: (args: string[]) => Promise<RunResult>,\n): Promise<ZvecCandidates | null> {\n const version = await run([\"version\"]);\n if (version.error) return fromError(version.error, \"zvec-grep version check\");\n const found = version.stdout.trim().split(/\\s+/).pop() ?? \"\";\n if (!SUPPORTED.test(found)) {\n return none(\"unsupported-version\", `zvec-grep ${found || \"unknown\"} is not 0.2.x`);\n }\n const ready = await run([\"status\", \"--check-ready\", \"--mode\", \"auto\"]);\n if (!ready.error) return null;\n return ready.error.code === \"ETIMEDOUT\"\n ? none(\"timeout\", \"zvec-grep status timed out\")\n : none(\"index-not-ready\", NOT_READY);\n}\n\nfunction readyPath(root: string): string {\n return join(root, \".codebuddy\", \"cache\", \"zvec-ready.json\");\n}\n\ntype CachedReadiness = {\n binary?: string;\n checkedAt?: string;\n /** Absent in caches written before failures were cached: those were always ready. */\n failure?: { status: ZvecStatus; reason: string };\n};\n\n/**\n * \"ready\", a cached failure (reason marked as cached), or null when the check\n * must run. A failure is dropped once `.zvec-grep/` changed after it was\n * recorded, i.e. someone (re)built the index outside CodeBuddy.\n */\nasync function readCachedReadiness(\n root: string,\n binary: string,\n): Promise<\"ready\" | ZvecCandidates | null> {\n let cached: CachedReadiness;\n try {\n cached = JSON.parse(await readFile(readyPath(root), \"utf8\")) as CachedReadiness;\n } catch {\n return null;\n }\n const checkedAt = Date.parse(cached.checkedAt ?? \"\");\n const age = Date.now() - checkedAt;\n if (cached.binary !== binary || !(age >= 0)) return null;\n if (!cached.failure) return age <= READY_CACHE_MS ? \"ready\" : null;\n if (age > NOT_READY_CACHE_MS) return null;\n const workspace = await stat(join(root, \".zvec-grep\")).catch(() => null);\n if (workspace && workspace.mtimeMs > checkedAt) return null;\n const { status, reason } = cached.failure;\n return none(status, `${reason}; cached check from ${Math.round(age / 1000)}s ago`);\n}\n\n/** Cache a readiness result: null = ready. */\nasync function markReadiness(\n root: string,\n binary: string,\n failure: ZvecCandidates | null,\n): Promise<void> {\n try {\n const path = readyPath(root);\n await mkdir(dirname(path), { recursive: true, mode: 0o700 });\n const record: CachedReadiness = {\n binary,\n checkedAt: new Date().toISOString(),\n ...(failure ? { failure: { status: failure.status, reason: failure.reason } } : {}),\n };\n await writeAtomic(path, `${JSON.stringify(record)}\\n`);\n } catch {\n // A read-only cache only costs a repeated readiness check.\n }\n}\n\nfunction indexedPath(root: string): string {\n return join(root, \".codebuddy\", \"cache\", \"zvec-index.json\");\n}\n\n/** When the zg index was last refreshed by CodeBuddy, or null if unknown. */\nasync function readIndexedAt(root: string): Promise<number | null> {\n try {\n const parsed = JSON.parse(await readFile(indexedPath(root), \"utf8\")) as { indexedAt?: string };\n const at = Date.parse(parsed.indexedAt ?? \"\");\n return Number.isFinite(at) ? at : null;\n } catch {\n return null;\n }\n}\n\nasync function markIndexed(root: string, at: number): Promise<void> {\n try {\n const path = indexedPath(root);\n await mkdir(dirname(path), { recursive: true, mode: 0o700 });\n await writeAtomic(path, `${JSON.stringify({ indexedAt: new Date(at).toISOString() })}\\n`);\n } catch {\n // Without the record, later hits are reported as unverified, not trusted.\n }\n}\n\nasync function zvecConfig(\n root: string,\n): Promise<{ enabled: boolean; binary: string; timeoutMs: number }> {\n let file: Awaited<ReturnType<typeof readConfigFile>> = {};\n try {\n file = await readConfigFile(root);\n } catch {\n // A broken config must not break context assembly; treat zvec as disabled.\n }\n const env = process.env.CODEBUDDY_ZVEC;\n return {\n enabled: env !== undefined ? env === \"1\" || env === \"true\" : (file.zvec?.enabled ?? false),\n binary: file.zvec?.binary ?? \"zg\",\n timeoutMs: file.zvec?.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n };\n}\n\ntype RunResult = {\n stdout: string;\n error: (Error & { code?: string | number | null | undefined }) | null;\n};\n\nfunction runZg(\n binary: string,\n args: string[],\n cwd: string,\n timeoutMs: number,\n signal?: AbortSignal,\n): Promise<RunResult> {\n return new Promise((resolveRun) => {\n execFile(\n binary,\n args,\n {\n cwd,\n timeout: timeoutMs,\n maxBuffer: 1024 * 1024,\n killSignal: \"SIGKILL\",\n ...(signal ? { signal } : {}),\n },\n (error, stdout) => {\n if (error && (error as { killed?: boolean }).killed) {\n resolveRun({ stdout: \"\", error: Object.assign(error, { code: \"ETIMEDOUT\" }) });\n return;\n }\n resolveRun({ stdout: String(stdout), error });\n },\n );\n });\n}\n\nfunction fromError(error: NonNullable<RunResult[\"error\"]>, what: string): ZvecCandidates {\n if (error.code === \"ENOENT\") return none(\"not-installed\", \"zvec-grep (`zg`) not found on PATH\");\n if (error.code === \"ETIMEDOUT\") return none(\"timeout\", `${what} timed out`);\n return none(\"failed\", `${what} failed`);\n}\n\nfunction none(status: ZvecStatus, reason: string): ZvecCandidates {\n return { status, paths: [], hits: [], reason, stale: 0, changed: 0 };\n}\n\nfunction toRepoRelative(root: string, path: string): string | null {\n const absolute = isAbsolute(path) ? path : resolve(root, path);\n const rel = relative(root, absolute);\n if (rel === \"\" || rel === \"..\" || rel.startsWith(`..${sep}`) || isAbsolute(rel)) return null;\n return rel.split(sep).join(\"/\");\n}\n"],"mappings":";;;;;;AAAA,SAAS,UAAU,aAAa;AAChC,SAAS,OAAO,UAAU,IAAI,YAAY;AAC1C,SAAS,SAAS,YAAY,MAAM,UAAU,SAAS,WAAW;AAwClE,IAAM,YAAY;AAClB,IAAM,qBAAqB;AAC3B,IAAM,iBAAiB,KAAK;AAE5B,IAAM,qBAAqB;AAE3B,IAAM,qBAAqB,KAAK;AAEzB,IAAM,qBAAqB;AAClC,IAAM,YAAY,CAAC,YAAY,aAAa,UAAU;AACtD,IAAM,aAAa;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AACA,IAAM,wBAAwB,CAAC,aAAa,eAAe,kBAAkB,iBAAiB;AAGvF,SAAS,iBAAiB,MAAwB;AACvD,SAAO;AAAA,IACL,GAAI,6CAA6C,KAAK,IAAI,IAAI,CAAC,IAAI;AAAA,IACnE,GAAI,qBAAqB,KAAK,IAAI,IAAI,CAAC,IAAI;AAAA,IAC3C,GAAG;AAAA,EACL;AACF;AAEA,IAAM,oBAAoB,CAAC,qBAAqB,eAAe,SAAS,eAAe;AAmCvF,eAAsB,eACpB,gBACA,MACA,UAA8B,CAAC,GACN;AACzB,QAAM,OAAO,QAAQ,cAAc;AACnC,QAAM,SAAS,MAAM,WAAW,IAAI;AACpC,MAAI,CAAC,OAAO,QAAS,QAAO,KAAK,YAAY,uBAAuB;AACpE,QAAM,MAAM,CAAC,SAAmB,MAAM,OAAO,QAAQ,MAAM,MAAM,OAAO,SAAS;AAEjF,QAAM,SAAS,MAAM,oBAAoB,MAAM,OAAO,MAAM;AAC5D,MAAI,WAAW,SAAS;AACtB,QAAI,OAAQ,QAAO;AACnB,UAAM,YAAY,MAAM,eAAe,GAAG;AAC1C,UAAM,cAAc,MAAM,OAAO,QAAQ,SAAS;AAClD,QAAI,UAAW,QAAO;AAAA,EACxB;AAIA,QAAM,QAAQ,KAAK,IAAI,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,QAAQ,SAAS,EAAE,CAAC,CAAC;AACvE,QAAM,aAAa,QAAQ;AAC3B,QAAM,QAAQ,iBAAiB,IAAI;AACnC,QAAM,SAAS,MAAM,IAAI;AAAA,IACvB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO,UAAU;AAAA,IACjB,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC;AAAA,EACzC,CAAC;AACD,MAAI,OAAO,OAAO;AAChB,UAAM,eAAe,IAAI;AACzB,WAAO,UAAU,OAAO,OAAO,iBAAiB;AAAA,EAClD;AAEA,QAAM,OAAO,UAAU,OAAO,MAAM,EAAE,MAAM,GAAG,UAAU;AAEzD,MAAI,KAAK,WAAW,KAAK,eAAe,KAAK,OAAO,MAAM,GAAG;AAC3D,UAAM,WAAW,KAAK,mBAAmB,SAAS;AAClD,UAAM,cAAc,MAAM,OAAO,QAAQ,QAAQ;AACjD,WAAO;AAAA,EACT;AACA,QAAM,YAAY,MAAM,cAAc,IAAI;AAC1C,QAAM,QAAkB,CAAC;AACzB,QAAM,aAA6B,CAAC;AACpC,QAAM,OAAO,oBAAI,IAAY;AAC7B,MAAI,QAAQ;AACZ,MAAI,UAAU;AACd,aAAW,OAAO,MAAM;AACtB,UAAM,OAAO,eAAe,MAAM,IAAI,IAAI;AAC1C,QAAI,CAAC,KAAM;AAEX,QAAI,KAAK,IAAI,IAAI,GAAG;AAClB,UAAI,MAAM,SAAS,IAAI,EAAG,YAAW,KAAK,EAAE,GAAG,KAAK,KAAK,CAAC;AAC1D;AAAA,IACF;AACA,QAAI,MAAM,UAAU,MAAO;AAC3B,SAAK,IAAI,IAAI;AACb,QAAI,kBAAkB,KAAK,CAAC,WAAW,KAAK,WAAW,MAAM,CAAC,EAAG;AACjE,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,QAAQ,MAAM,IAAI,CAAC;AAC3C,UAAI,CAAC,KAAK,OAAO,EAAG;AAEpB,UAAI,cAAc,QAAQ,KAAK,IAAI,KAAK,SAAS,KAAK,OAAO,IAAI,WAAW;AAC1E;AACA;AAAA,MACF;AAAA,IACF,QAAQ;AACN;AACA;AAAA,IACF;AACA,UAAM,KAAK,IAAI;AACf,eAAW,KAAK,EAAE,GAAG,KAAK,KAAK,CAAC;AAAA,EAClC;AACA,SAAO;AAAA,IACL,QAAQ;AAAA,IACR;AAAA,IACA,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA,QACE,4BAA4B,MAAM,MAAM,cACvC,MAAM,SAAS,IAAI,gCAAgC,OACnD,QAAQ,IAAI,KAAK,KAAK,0BAA0B,OAChD,UAAU,IAAI,KAAK,OAAO,+CAA+C,OACzE,cAAc,OACX,sEACA;AAAA,EACR;AACF;AAUA,eAAsB,iBACpB,gBACA,UAAoC,CAAC,GACf;AACtB,QAAM,OAAO,QAAQ,cAAc;AACnC,QAAM,SAAS,MAAM,WAAW,IAAI;AACpC,MAAI,CAAC,OAAO,QAAS,QAAO,EAAE,QAAQ,YAAY,QAAQ,wBAAwB;AAClF,QAAM,YAAY,MAAM;AAAA,IAAe,CAAC,SACtC,MAAM,OAAO,QAAQ,MAAM,MAAM,OAAO,SAAS;AAAA,EACnD;AACA,MAAI,UAAW,QAAO,EAAE,QAAQ,UAAU,QAAQ,QAAQ,UAAU,OAAO;AAC3E,QAAM,YAAY,KAAK,IAAI;AAC3B,QAAM,SAAS,MAAM;AAAA,IACnB,OAAO;AAAA,IACP,CAAC,SAAS,MAAM,UAAU,MAAM;AAAA,IAChC;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,EACV;AACA,MAAI,OAAO,OAAO;AAChB,UAAM,UAAU,UAAU,OAAO,OAAO,mBAAmB;AAC3D,WAAO,EAAE,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,OAAO;AAAA,EAC1D;AACA,QAAM,YAAY,MAAM,SAAS;AACjC,SAAO,EAAE,QAAQ,MAAM,QAAQ,4BAA4B;AAC7D;AAGO,SAAS,UAAU,QAAgC;AACxD,SAAO,CAAC,GAAG,OAAO,SAAS,oDAAoD,CAAC,EAC7E,QAAQ,CAAC,UAAU;AAClB,UAAM,OAAO,OAAO,MAAM,CAAC,CAAC;AAC5B,UAAM,YAAY,OAAO,MAAM,CAAC,CAAC;AACjC,UAAM,UAAU,OAAO,MAAM,CAAC,KAAK,MAAM,CAAC,CAAC;AAC3C,QAAI,YAAY,KAAK,UAAU,UAAW,QAAO,CAAC;AAClD,WAAO;AAAA,MACL;AAAA,QACE,MAAM,MAAM,CAAC;AAAA,QACb;AAAA,QACA;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,QACR,WAAW;AAAA,MACb;AAAA,IACF;AAAA,EACF,CAAC,EACA,KAAK,CAAC,MAAM,UAAU,KAAK,OAAO,MAAM,IAAI;AACjD;AAGA,eAAsB,eAAe,gBAAuC;AAC1E,QAAM,GAAG,UAAU,QAAQ,cAAc,CAAC,GAAG,EAAE,OAAO,KAAK,CAAC;AAC9D;AAaA,eAAsB,WAAW,gBAA6C;AAC5E,QAAM,OAAO,QAAQ,cAAc;AACnC,QAAM,SAAS,MAAM,WAAW,IAAI;AACpC,QAAM,MAAM,CAAC,SACX,MAAM,OAAO,QAAQ,MAAM,MAAM,KAAK,IAAI,OAAO,WAAW,GAAK,CAAC;AACpE,QAAM,UAAU,MAAM,IAAI,CAAC,SAAS,CAAC;AACrC,QAAM,QAAQ,QAAQ,QAAQ,OAAQ,QAAQ,OAAO,KAAK,EAAE,MAAM,KAAK,EAAE,IAAI,KAAK;AAClF,QAAM,YAAY,UAAU,QAAQ,UAAU,KAAK,KAAK;AACxD,QAAM,YAAY,YAAY,MAAM,eAAe,GAAG,IAAI;AAC1D,QAAM,SAAS,YAAY,MAAM,IAAI,CAAC,UAAU,QAAQ,CAAC,IAAI;AAC7D,SAAO;AAAA,IACL,SAAS,OAAO;AAAA,IAChB,QAAQ,OAAO;AAAA,IACf,SAAS;AAAA,IACT;AAAA,IACA,YAAY,aAAa,cAAc;AAAA,IACvC,QACE,WAAW,QAAQ,OAAO,QACtB,YACA,iBAAiB,KAAK,OAAO,MAAM,IACjC,YACA;AAAA,IACR,QAAQ,QAAQ,QACZ,UAAU,QAAQ,OAAO,yBAAyB,EAAE,SACpD,CAAC,YACC,aAAa,SAAS,SAAS,kBAC9B,WAAW,UAAU;AAAA,EAC9B;AACF;AAMA,eAAsB,eACpB,gBACA,UAII,CAAC,GACY;AACjB,QAAM,OAAO,QAAQ,cAAc;AACnC,QAAM,SAAS,MAAM,WAAW,IAAI;AACpC,QAAM,OAAO;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ,SAAS;AAAA,IACjB;AAAA,IACA;AAAA,EACF;AACA,MAAI,QAAQ,QAAS,MAAK,KAAK,WAAW;AAC1C,QAAM,YAAY,KAAK,IAAI;AAC3B,QAAM,OAAO,MAAM,IAAI,QAAgB,CAAC,YAAY,WAAW;AAC7D,UAAM,QAAQ,MAAM,OAAO,QAAQ,MAAM;AAAA,MACvC,KAAK;AAAA,MACL,OAAO,QAAQ,QAAQ,WAAW;AAAA,IACpC,CAAC;AACD,UAAM,GAAG,SAAS,MAAM;AACxB,UAAM,GAAG,SAAS,CAAC,SAAS,WAAW,QAAQ,CAAC,CAAC;AAAA,EACnD,CAAC;AACD,QAAM,eAAe,IAAI;AACzB,MAAI,SAAS,EAAG,OAAM,YAAY,MAAM,SAAS;AACjD,SAAO;AACT;AAEA,IAAM,YAAY;AAGlB,eAAe,eACb,KACgC;AAChC,QAAM,UAAU,MAAM,IAAI,CAAC,SAAS,CAAC;AACrC,MAAI,QAAQ,MAAO,QAAO,UAAU,QAAQ,OAAO,yBAAyB;AAC5E,QAAM,QAAQ,QAAQ,OAAO,KAAK,EAAE,MAAM,KAAK,EAAE,IAAI,KAAK;AAC1D,MAAI,CAAC,UAAU,KAAK,KAAK,GAAG;AAC1B,WAAO,KAAK,uBAAuB,aAAa,SAAS,SAAS,eAAe;AAAA,EACnF;AACA,QAAM,QAAQ,MAAM,IAAI,CAAC,UAAU,iBAAiB,UAAU,MAAM,CAAC;AACrE,MAAI,CAAC,MAAM,MAAO,QAAO;AACzB,SAAO,MAAM,MAAM,SAAS,cACxB,KAAK,WAAW,4BAA4B,IAC5C,KAAK,mBAAmB,SAAS;AACvC;AAEA,SAAS,UAAU,MAAsB;AACvC,SAAO,KAAK,MAAM,cAAc,SAAS,iBAAiB;AAC5D;AAcA,eAAe,oBACb,MACA,QAC0C;AAC1C,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,MAAM,SAAS,UAAU,IAAI,GAAG,MAAM,CAAC;AAAA,EAC7D,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,YAAY,KAAK,MAAM,OAAO,aAAa,EAAE;AACnD,QAAM,MAAM,KAAK,IAAI,IAAI;AACzB,MAAI,OAAO,WAAW,UAAU,EAAE,OAAO,GAAI,QAAO;AACpD,MAAI,CAAC,OAAO,QAAS,QAAO,OAAO,iBAAiB,UAAU;AAC9D,MAAI,MAAM,mBAAoB,QAAO;AACrC,QAAM,YAAY,MAAM,KAAK,KAAK,MAAM,YAAY,CAAC,EAAE,MAAM,MAAM,IAAI;AACvE,MAAI,aAAa,UAAU,UAAU,UAAW,QAAO;AACvD,QAAM,EAAE,QAAQ,OAAO,IAAI,OAAO;AAClC,SAAO,KAAK,QAAQ,GAAG,MAAM,uBAAuB,KAAK,MAAM,MAAM,GAAI,CAAC,OAAO;AACnF;AAGA,eAAe,cACb,MACA,QACA,SACe;AACf,MAAI;AACF,UAAM,OAAO,UAAU,IAAI;AAC3B,UAAM,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAC3D,UAAM,SAA0B;AAAA,MAC9B;AAAA,MACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,GAAI,UAAU,EAAE,SAAS,EAAE,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,OAAO,EAAE,IAAI,CAAC;AAAA,IACnF;AACA,UAAM,YAAY,MAAM,GAAG,KAAK,UAAU,MAAM,CAAC;AAAA,CAAI;AAAA,EACvD,QAAQ;AAAA,EAER;AACF;AAEA,SAAS,YAAY,MAAsB;AACzC,SAAO,KAAK,MAAM,cAAc,SAAS,iBAAiB;AAC5D;AAGA,eAAe,cAAc,MAAsC;AACjE,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,MAAM,SAAS,YAAY,IAAI,GAAG,MAAM,CAAC;AACnE,UAAM,KAAK,KAAK,MAAM,OAAO,aAAa,EAAE;AAC5C,WAAO,OAAO,SAAS,EAAE,IAAI,KAAK;AAAA,EACpC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,YAAY,MAAc,IAA2B;AAClE,MAAI;AACF,UAAM,OAAO,YAAY,IAAI;AAC7B,UAAM,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAC3D,UAAM,YAAY,MAAM,GAAG,KAAK,UAAU,EAAE,WAAW,IAAI,KAAK,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;AAAA,CAAI;AAAA,EAC1F,QAAQ;AAAA,EAER;AACF;AAEA,eAAe,WACb,MACkE;AAClE,MAAI,OAAmD,CAAC;AACxD,MAAI;AACF,WAAO,MAAM,eAAe,IAAI;AAAA,EAClC,QAAQ;AAAA,EAER;AACA,QAAM,MAAM,QAAQ,IAAI;AACxB,SAAO;AAAA,IACL,SAAS,QAAQ,SAAY,QAAQ,OAAO,QAAQ,SAAU,KAAK,MAAM,WAAW;AAAA,IACpF,QAAQ,KAAK,MAAM,UAAU;AAAA,IAC7B,WAAW,KAAK,MAAM,aAAa;AAAA,EACrC;AACF;AAOA,SAAS,MACP,QACA,MACA,KACA,WACA,QACoB;AACpB,SAAO,IAAI,QAAQ,CAAC,eAAe;AACjC;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,QACE;AAAA,QACA,SAAS;AAAA,QACT,WAAW,OAAO;AAAA,QAClB,YAAY;AAAA,QACZ,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,MAC7B;AAAA,MACA,CAAC,OAAO,WAAW;AACjB,YAAI,SAAU,MAA+B,QAAQ;AACnD,qBAAW,EAAE,QAAQ,IAAI,OAAO,OAAO,OAAO,OAAO,EAAE,MAAM,YAAY,CAAC,EAAE,CAAC;AAC7E;AAAA,QACF;AACA,mBAAW,EAAE,QAAQ,OAAO,MAAM,GAAG,MAAM,CAAC;AAAA,MAC9C;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEA,SAAS,UAAU,OAAwC,MAA8B;AACvF,MAAI,MAAM,SAAS,SAAU,QAAO,KAAK,iBAAiB,oCAAoC;AAC9F,MAAI,MAAM,SAAS,YAAa,QAAO,KAAK,WAAW,GAAG,IAAI,YAAY;AAC1E,SAAO,KAAK,UAAU,GAAG,IAAI,SAAS;AACxC;AAEA,SAAS,KAAK,QAAoB,QAAgC;AAChE,SAAO,EAAE,QAAQ,OAAO,CAAC,GAAG,MAAM,CAAC,GAAG,QAAQ,OAAO,GAAG,SAAS,EAAE;AACrE;AAEA,SAAS,eAAe,MAAc,MAA6B;AACjE,QAAM,WAAW,WAAW,IAAI,IAAI,OAAO,QAAQ,MAAM,IAAI;AAC7D,QAAM,MAAM,SAAS,MAAM,QAAQ;AACnC,MAAI,QAAQ,MAAM,QAAQ,QAAQ,IAAI,WAAW,KAAK,GAAG,EAAE,KAAK,WAAW,GAAG,EAAG,QAAO;AACxF,SAAO,IAAI,MAAM,GAAG,EAAE,KAAK,GAAG;AAChC;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/markdown-store-fs.ts","../src/core/config-file.ts"],"sourcesContent":["import { lstat, open, rename } from \"node:fs/promises\";\nimport { join, relative, resolve, sep } from \"node:path\";\n\n/**\n * Filesystem helpers shared by the Markdown-backed stores\n * (`MemoryFileStore`, `PlanFileStore`). These enforce the security\n * invariants that keep `.codebuddy/` writes inside the repository root:\n * no path traversal, no symlinked path components, and crash-safe atomic\n * writes. Keep this module small and audited; both stores depend on it.\n */\n\n/**\n * Resolve `path` and assert it stays inside `root`. Returns the absolute\n * path. Throws if the resolved path escapes the root.\n */\nexport function resolveWithinRoot(root: string, path: string): string {\n const absoluteRoot = resolve(root);\n const absolute = resolve(path);\n const relativePath = relative(absoluteRoot, absolute);\n if (relativePath === \"..\" || relativePath.startsWith(`..${sep}`) || relativePath === \"\") {\n throw new Error(`Path escapes repository root: ${path}`);\n }\n return absolute;\n}\n\n/**\n * Walk each path component between `root` and `target` and refuse if any\n * component is a symbolic link. Prevents a planted symlink from\n * redirecting a write outside the repository. Missing components are\n * fine — the caller creates them.\n */\nexport async function assertNoSymlinkBetween(root: string, target: string): Promise<void> {\n const absoluteRoot = resolve(root);\n const absoluteTarget = resolve(target);\n const relativeTarget = relative(absoluteRoot, absoluteTarget);\n if (relativeTarget === \"..\" || relativeTarget.startsWith(`..${sep}`)) {\n throw new Error(`Path escapes repository root: ${target}`);\n }\n\n let current = absoluteRoot;\n const parts = relativeTarget.split(sep).filter(Boolean);\n for (const part of parts) {\n current = join(current, part);\n try {\n const info = await lstat(current);\n if (info.isSymbolicLink()) {\n throw new Error(`Refusing to use symlinked path: ${current}`);\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return;\n throw error;\n }\n }\n}\n\n/**\n * Write `document` to `path` atomically: write to a unique temp file with\n * an exclusive (`wx`) create, fsync, then rename into place. A crash mid-\n * write leaves either the old file or nothing — never a partial file.\n *\n * `mode` sets permissions on NEWLY created files (an existing file keeps its\n * mode, matching rename semantics). Secrets-bearing configs should use 0o600.\n */\nexport async function writeAtomic(\n path: string,\n document: string,\n options?: { mode?: number },\n): Promise<void> {\n const temporaryPath = `${path}.${process.pid}.${Date.now()}.tmp`;\n const handle = await open(temporaryPath, \"wx\", options?.mode ?? 0o600);\n try {\n await handle.writeFile(document, \"utf8\");\n await handle.sync();\n } finally {\n await handle.close();\n }\n await rename(temporaryPath, path);\n}\n\n/**\n * Split a Markdown document with leading YAML front matter into its\n * front-matter text and body. Throws on malformed or unterminated\n * front matter so hand-edited files fail loudly rather than silently\n * losing fields.\n */\nexport function parseFrontMatter(document: string): { frontMatter: string; content: string } {\n if (!document.startsWith(\"---\\n\")) {\n throw new Error(\"Markdown file must start with YAML front matter.\");\n }\n const closingIndex = document.indexOf(\"\\n---\\n\", 4);\n if (closingIndex === -1) {\n throw new Error(\"Markdown file has unterminated YAML front matter.\");\n }\n return {\n frontMatter: document.slice(4, closingIndex),\n content: document.slice(closingIndex + 5),\n };\n}\n\n/**\n * Compose a Markdown document from serialized front-matter YAML and a\n * body. Ensures exactly one trailing newline on the body so files are\n * stable across rewrites (minimal git diffs).\n */\nexport function composeMarkdown(frontMatterYaml: string, body: string): string {\n const trimmedFrontMatter = frontMatterYaml.trimEnd();\n const content = body.endsWith(\"\\n\") ? body : `${body}\\n`;\n return `---\\n${trimmedFrontMatter}\\n---\\n${content}`;\n}\n","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 })\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 /**\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 })\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 },\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":["join","resolve","join","resolve"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/lean.ts","../src/hooks/lean-command.ts"],"sourcesContent":["/**\n * Lean mode: the prompt hook's only job is to cut the agent's token spend. It\n * injects ranked file pointers (and code spans for read-only questions) and\n * nothing else — no MCP tools, rules block, or memory capture.\n */\nimport { createHash } from \"node:crypto\";\nimport { appendFile, mkdir } from \"node:fs/promises\";\nimport { extname, join } from \"node:path\";\nimport {\n discoverTaskPaths,\n loadProjectArchitectureMap,\n NON_SOURCE_PATH,\n readBoundedSource,\n taskTerms,\n} from \"../context/discovery.js\";\nimport { type ContextCapsule, SessionCapsuleLedger } from \"../context/session-ledger.js\";\nimport { detectTestCommand, findCoveringTest } from \"../context/tests.js\";\nimport { loadFreshIndex } from \"../indexer/engine.js\";\nimport { estimateTokens } from \"../savings/tokens.js\";\nimport { hookRoot } from \"./root.js\";\nimport type { HookInput } from \"./runtime.js\";\n\nexport const LEAN_POINTER_PREFIX = \"CodeBuddy likely files: \";\nexport const LEAN_LIMITS = {\n paths: 6,\n symbolsPerFile: 3,\n wholeFileLines: 80,\n spanTokens: 1200,\n} as const;\n\nexport type LeanPointer = {\n path: string;\n symbols: Array<{ name: string; startLine: number; endLine: number }>;\n /** The test file that covers this path, when one follows a known convention. */\n test?: string;\n};\n\ntype LoadedPointer = LeanPointer & { lines: string[] };\n\n/** A lean block finished later than this is dropped: the host may already have moved on. */\nexport const LEAN_HOOK_BUDGET_MS = 1500;\n/** Rendering after the BYOK rerank needs this much of the budget. */\nconst RENDER_RESERVE_MS = 300;\n\n/**\n * UserPromptSubmit in lean mode → ranked file pointers, or nothing. Never\n * throws: an error is appended to `.codebuddy/cache/hooks/errors.log` and the\n * turn proceeds as if CodeBuddy were not installed.\n */\nexport async function runLeanHook(\n input: HookInput,\n emit?: (block: string) => Promise<void>,\n budgetMs = LEAN_HOOK_BUDGET_MS,\n): Promise<string> {\n const started = Date.now();\n const repositoryRoot = hookRoot(input.cwd);\n const prompt = input.prompt?.trim() ?? \"\";\n if (prompt === \"\" || prompt.startsWith(\"/\")) return \"\";\n const sessionId = input.session_id ?? \"default\";\n try {\n const { manifest } = await loadFreshIndex(repositoryRoot);\n const discovery = await discoverTaskPaths(\n repositoryRoot,\n prompt,\n manifest,\n loadProjectArchitectureMap(repositoryRoot, manifest),\n // The rerank must answer while rendering still fits in the hook budget.\n { semantic: false, llmDeadlineAt: started + budgetMs - RENDER_RESERVE_MS },\n );\n const terms = taskTerms(prompt);\n const indexed = new Set(Object.keys(manifest.entries));\n const pointers: LeanPointer[] = discovery.paths\n .filter((path) => !NON_SOURCE_PATH.test(path))\n .map((path) => {\n const test = findCoveringTest(path, indexed);\n const table = manifest.entries[path]?.symbolTable ?? [];\n // Symbols the BYOK rerank named come first, then term matches.\n const picked = discovery.symbols?.[path] ?? [];\n const ordered = [\n ...picked.flatMap((name) => table.filter((symbol) => symbol.name === name)),\n ...table.filter(\n (symbol) =>\n !picked.includes(symbol.name) &&\n terms.some((term) => symbol.name.toLowerCase().includes(term)),\n ),\n ];\n return {\n path,\n symbols: ordered.map(({ name, startLine, endLine }) => ({ name, startLine, endLine })),\n ...(test ? { test } : {}),\n };\n });\n const ledger = new SessionCapsuleLedger(repositoryRoot);\n // The host keeps the block for this turn only, so nothing counts as already sent.\n if (input.ephemeral_context) await ledger.reset(sessionId);\n const { text, emitted } = await renderLeanBlock({\n repositoryRoot,\n prompt,\n pointers,\n ledger,\n sessionId,\n testCommand: pointers.length > 0 ? await detectTestCommand(repositoryRoot) : null,\n });\n if (Date.now() - started >= budgetMs) return \"\";\n if (text) await emit?.(text);\n await ledger.markSent(sessionId, emitted);\n return text;\n } catch (error) {\n await logHookError(repositoryRoot, sessionId, error).catch(() => {});\n return \"\";\n }\n}\n\nasync function logHookError(root: string, sessionId: string, error: unknown): Promise<void> {\n const directory = join(root, \".codebuddy\", \"cache\", \"hooks\");\n await mkdir(directory, { recursive: true });\n const message = (error as Error)?.message ?? String(error);\n await appendFile(\n join(directory, \"errors.log\"),\n `${new Date().toISOString()} ${message.replace(/\\s+/g, \" \")} session=${sessionId}\\n`,\n );\n}\n\n/**\n * The lean block for one prompt: a single pointer line, plus current-source\n * spans when the prompt is read-only. Capsules already delivered this session\n * are left out; the caller marks `emitted` as sent once the block is written.\n */\nexport async function renderLeanBlock(input: {\n repositoryRoot: string;\n prompt: string;\n pointers: LeanPointer[];\n ledger: SessionCapsuleLedger;\n sessionId: string;\n /** Sent once per session, after the pointers. */\n testCommand?: string | null;\n}): Promise<{ text: string; emitted: ContextCapsule[] }> {\n const loaded: LoadedPointer[] = [];\n for (const pointer of input.pointers) {\n if (loaded.length === LEAN_LIMITS.paths) break;\n const source = await readBoundedSource(input.repositoryRoot, pointer.path);\n if (source === null) continue; // deleted, oversized or binary since indexing\n loaded.push({\n path: pointer.path,\n symbols: pointer.symbols.slice(0, LEAN_LIMITS.symbolsPerFile),\n ...(pointer.test ? { test: pointer.test } : {}),\n lines: source.split(/\\r?\\n/),\n });\n }\n if (loaded.length === 0) return { text: \"\", emitted: [] };\n\n const capsules: ContextCapsule[] = [\n {\n key: \"lean:pointers\",\n text: LEAN_POINTER_PREFIX + loaded.map(describePointer).join(\", \"),\n },\n ...(input.testCommand\n ? [{ key: \"lean:test-command\", text: `CodeBuddy test command: ${input.testCommand}` }]\n : []),\n ];\n if (isReadOnlyPrompt(input.prompt)) {\n let tokens = 0;\n for (const span of loaded.flatMap(spansOf)) {\n const cost = estimateTokens(span.text);\n if (tokens + cost > LEAN_LIMITS.spanTokens) break;\n tokens += cost;\n capsules.push(span);\n }\n }\n const seen = await input.ledger.seen(input.sessionId, capsules);\n const emitted = capsules.filter((capsule) => !seen.has(capsule.key));\n return { text: emitted.map((capsule) => capsule.text).join(\"\\n\"), emitted };\n}\n\nfunction describePointer(pointer: LoadedPointer): string {\n const ranges = pointer.symbols.map((s) => `${s.name} L${s.startLine}-${s.endLine}`).join(\", \");\n const test = pointer.test ? `test: ${pointer.test}` : \"\";\n const detail = [ranges, test].filter(Boolean).join(\"; \");\n return detail ? `${pointer.path} (${detail})` : pointer.path;\n}\n\n/** Whole small files; otherwise the matched symbol spans. */\nfunction spansOf(pointer: LoadedPointer): ContextCapsule[] {\n const ranges =\n pointer.lines.length <= LEAN_LIMITS.wholeFileLines\n ? [{ start: 1, end: pointer.lines.length }]\n : pointer.symbols.map((s) => ({ start: s.startLine, end: s.endLine }));\n const language = extname(pointer.path).slice(1);\n return ranges.map(({ start, end }) => {\n const body = pointer.lines.slice(start - 1, end).join(\"\\n\");\n const hash = createHash(\"sha1\").update(body).digest(\"hex\").slice(0, 12);\n return {\n key: `lean:span:${pointer.path}:${start}-${end}:${hash}`,\n text: `${pointer.path}:L${start}-${end}\\n\\`\\`\\`${language}\\n${body}\\n\\`\\`\\``,\n };\n });\n}\n\nconst FORCED_READ_ONLY =\n /\\b(without editing|without changing|don't change|do not change|don't edit|do not edit)\\b/i;\nconst QUESTION_START = /^(how|what|why|where|which|who|explain|describe|summarize)\\b/i;\nconst EDIT_VERB =\n /\\b(add|change|fix|implement|update|remove|delete|rename|refactor|write|create|make|edit|replace|move|migrate|support|build)\\b/i;\n\n/**\n * Whether a prompt asks a question without asking for edits. Conservative: a\n * false positive costs up to the span budget, a false negative costs nothing.\n */\nexport function isReadOnlyPrompt(prompt: string): boolean {\n const text = prompt.trim();\n if (text === \"\" || text.startsWith(\"/\")) return false;\n if (FORCED_READ_ONLY.test(text)) return true;\n const asks = QUESTION_START.test(text) || text.endsWith(\"?\");\n return asks && !EDIT_VERB.test(text);\n}\n","import { runLeanHook } from \"./lean.js\";\nimport { failSoft, readHookInput, writeStdout } from \"./stdio.js\";\n\n/** `codebuddy hooks lean`: read the hook payload, print the lean block, never fail. */\nexport async function runLeanHookCommand(): Promise<void> {\n await failSoft(async () => {\n await runLeanHook(await readHookInput(), writeStdout);\n });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/memory-extract/sensitive.ts"],"sourcesContent":["/**\n * Sensitive-content scan. Secrets and personal data must never be auto-saved\n * as durable memory — a matched candidate is forced into the review queue,\n * flagged, so a human decides. This is a conservative detector: false positives\n * (an extra review item) are acceptable; false negatives (a leaked key in\n * committed Markdown) are not.\n *\n * The scan only ever *routes to review* — it never blocks a turn, deletes\n * content, or rejects on its own. Adding a reason at most moves a candidate from\n * \"auto-save\" to \"human decides\".\n */\n\nexport type SensitivityResult = {\n sensitive: boolean;\n reasons: string[];\n};\n\nconst PATTERNS: Array<{ label: string; regex: RegExp }> = [\n { label: \"private-key\", regex: /-----BEGIN(?: [A-Z0-9]+)* PRIVATE KEY-----/ },\n { label: \"aws-access-key\", regex: /\\b(?:AKIA|ASIA)[0-9A-Z]{16}\\b/ },\n { label: \"hf-token\", regex: /\\bhf_[A-Za-z0-9]{16,}\\b/ },\n { label: \"github-token\", regex: /\\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{20,}\\b/ },\n // GitHub fine-grained personal access tokens.\n {\n label: \"github-fine-grained-pat\",\n regex: /\\bgithub_pat_[A-Za-z0-9_]{22,255}_[A-Za-z0-9]{22,255}\\b/,\n },\n { label: \"npm-token\", regex: /\\bnpm_[A-Za-z0-9]{36}\\b/ },\n { label: \"gitlab-token\", regex: /\\bglpat-[A-Za-z0-9_-]{20,}\\b/ },\n { label: \"slack-token\", regex: /\\bxox[baprs]-[A-Za-z0-9-]{10,}\\b/ },\n { label: \"bearer-token\", regex: /\\bBearer\\s+[A-Za-z0-9._-]{12,}\\b/ },\n // Vendor API keys with distinctive, low-false-positive prefixes.\n { label: \"anthropic-key\", regex: /\\bsk-ant-[A-Za-z0-9_-]{20,}\\b/ },\n { label: \"openai-key\", regex: /\\bsk-(?!ant-)[A-Za-z0-9_-]{20,}\\b/ },\n { label: \"google-api-key\", regex: /\\bAIza[0-9A-Za-z_-]{35}\\b/ },\n { label: \"stripe-key\", regex: /\\b(?:sk|rk|pk)_(?:live|test)_[A-Za-z0-9]{16,}\\b/ },\n // A JWT: three base64url segments. Common in auth-related pastes.\n { label: \"jwt\", regex: /\\beyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}\\b/ },\n // Credentials embedded in a connection URL: scheme://user:password@host.\n {\n label: \"connection-string-credentials\",\n regex: /\\b[a-z][a-z0-9+.-]*:\\/\\/[^\\s:/@]+:[^\\s:/@]+@/i,\n },\n {\n // Catches `password: x`, `api_key = x`, and the prose form `password is x`.\n // Deliberately eager — an over-flagged review item is cheaper than a leak.\n label: \"credential-assignment\",\n regex:\n /\\b(?:api[_-]?key|secret|token|password|passwd|pwd|access[_-]?key|authorization|auth[_-]?token|credential[s]?)\\b\\s*(?:[:=]|\\bis\\b|\\bwas\\b|\\bequals\\b)\\s*\\S{6,}/i,\n },\n // HTTP Basic-auth style inline credentials outside a URL scheme\n // (`Authorization: Basic dXNlcjpwYXNz`).\n {\n label: \"basic-auth-header\",\n regex: /\\bAuthorization\\s*[:=]\\s*Basic\\s+[A-Za-z0-9+/=]{8,}/i,\n },\n { label: \"email\", regex: /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\\b/ },\n];\n\n/**\n * Deterministic, review-oriented high-entropy heuristic. Flags a token that\n * looks like a random secret — long, mixed character classes, high Shannon\n * entropy — that the prefix-based patterns above wouldn't catch. Tuned to stay\n * quiet on ordinary code and prose: identifiers are usually low-entropy words\n * (`camelCase`, `snake_case`), and hex hashes / SHAs lack the required mix of\n * upper, lower, and digit. Never a hard block; at most one extra review item.\n * OpenCode session/message/part ids (`ses_` + 12 hex + 14 random base62) are\n * host identifiers recorded as provenance, not secrets, so they are skipped;\n * otherwise every OpenCode capture would be queued for review.\n */\nconst OPENCODE_ID = /^(?:ses|msg|prt)_[0-9a-f]{12}[0-9A-Za-z]{14}$/;\n\nexport function hasHighEntropyToken(text: string): boolean {\n for (const token of text.split(/[^A-Za-z0-9+/_=-]+/)) {\n if (token.length < 24 || token.length > 200 || OPENCODE_ID.test(token)) continue;\n if (!/[a-z]/.test(token) || !/[A-Z]/.test(token) || !/[0-9]/.test(token)) continue;\n if (shannonEntropy(token) >= 3.5) return true;\n }\n return false;\n}\n\nfunction shannonEntropy(value: string): number {\n const counts = new Map<string, number>();\n for (const char of value) counts.set(char, (counts.get(char) ?? 0) + 1);\n let entropy = 0;\n for (const count of counts.values()) {\n const p = count / value.length;\n entropy -= p * Math.log2(p);\n }\n return entropy;\n}\n\nexport function scanSensitive(text: string): SensitivityResult {\n const reasons: string[] = [];\n for (const { label, regex } of PATTERNS) {\n if (regex.test(text) && !reasons.includes(label)) reasons.push(label);\n }\n if (hasHighEntropyToken(text)) reasons.push(\"high-entropy-string\");\n return { sensitive: reasons.length > 0, reasons };\n}\n\nexport const REDACTED = \"[REDACTED SENSITIVE CONTENT]\";\n\n/** Whole-field redaction: a field that scans sensitive is replaced, never partially kept. */\nexport function redactSensitive(text: string): string {\n return scanSensitive(text).sensitive ? REDACTED : text;\n}\n\n/**\n * The claim: fields that together state one memory. A credential can be split\n * across them (\"password\" / \"is\" / \"hunter2secret\"), so they are scanned\n * jointly and redacted as a unit.\n */\nexport const CLAIM_KEYS = [\"subject\", \"predicate\", \"object\", \"content\"] as const;\n\n/** Independent free-text provenance fields (string or string[]), redacted one by one. */\nexport const PROVENANCE_TEXT_KEYS = [\n \"paths\",\n \"verification\",\n \"reason\",\n \"createdByAgent\",\n \"sourcePlanId\",\n \"sourceSessionId\",\n \"introducedBy\",\n] as const;\n\n/** The claim fields joined in reading order, for a joint scan. */\nexport function claimText(record: Partial<Record<(typeof CLAIM_KEYS)[number], unknown>>): string {\n return CLAIM_KEYS.map((key) => record[key])\n .filter((value): value is string => typeof value === \"string\")\n .join(\" \");\n}\n\n/**\n * Persistence-boundary redaction: returns a copy of `record` with every\n * free-text field redacted. If any claim field, or the joined claim, scans\n * sensitive, ALL claim fields are replaced (a sibling may hold the value half\n * of the credential). Provenance fields are redacted individually. Idempotent,\n * so re-persisting (e.g. approving a queued item) never re-introduces a raw\n * value. Structural fields (ids, fingerprint, namespace, timestamps, enums)\n * are left alone.\n */\nexport function redactPersisted<T extends object>(record: T): T {\n const out = { ...record } as Record<string, unknown>;\n const present = CLAIM_KEYS.filter((key) => typeof out[key] === \"string\");\n const claimSensitive =\n present.length > 0 &&\n (scanSensitive(claimText(out)).sensitive ||\n present.some((key) => scanSensitive(out[key] as string).sensitive));\n if (claimSensitive) for (const key of present) out[key] = REDACTED;\n for (const key of PROVENANCE_TEXT_KEYS) {\n const value = out[key];\n if (typeof value === \"string\") out[key] = redactSensitive(value);\n else if (Array.isArray(value)) {\n out[key] = value.map((item) => (typeof item === \"string\" ? redactSensitive(item) : item));\n }\n }\n return out as T;\n}\n\n/** Scan several surfaces separately and merge the reason labels (never the matched text). */\nexport function scanFields(fields: Array<string | null | undefined>): SensitivityResult {\n const reasons: string[] = [];\n for (const field of fields) {\n if (!field) continue;\n for (const reason of scanSensitive(field).reasons) {\n if (!reasons.includes(reason)) reasons.push(reason);\n }\n }\n return { sensitive: reasons.length > 0, reasons };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/migrate-to-files.ts","../src/mcp/tools/index.ts","../src/context/delivery.ts","../src/suggest/engine.ts","../src/suggest/types.ts","../src/mcp/tool-result.ts","../src/mcp/server.ts","../src/automation/project-sync.ts","../src/indexer/watch.ts","../src/core/project-root.ts","../src/mcp/database.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { redactPersisted } from \"../memory-extract/sensitive.js\";\nimport type { MemoryFileStore } from \"./memory-file-store.js\";\nimport type { MemoryRepository } from \"./repository.js\";\n\nexport type MigrationConflict = {\n kind: \"fact\" | \"summary\";\n id: string;\n databaseChecksum: string;\n fileChecksum: string;\n};\n\nexport type MigrationFailure = {\n kind: \"fact\" | \"summary\";\n id: string;\n error: string;\n};\n\nexport type MigrationPreview = {\n namespace: string;\n dryRun: boolean;\n facts: { found: number; existing: number; toWrite: number; conflicts: number; written: number };\n summaries: {\n found: number;\n existing: number;\n toWrite: number;\n conflicts: number;\n written: number;\n };\n conflicts: MigrationConflict[];\n failures: MigrationFailure[];\n};\n\nexport async function migrateMemoryToFiles(input: {\n repository: MemoryRepository;\n store: MemoryFileStore;\n namespace: string;\n write: boolean;\n}): Promise<MigrationPreview> {\n const namespace = await input.repository.getNamespaceByName(input.namespace);\n if (!namespace) {\n return {\n namespace: input.namespace,\n dryRun: !input.write,\n facts: { found: 0, existing: 0, toWrite: 0, conflicts: 0, written: 0 },\n summaries: { found: 0, existing: 0, toWrite: 0, conflicts: 0, written: 0 },\n conflicts: [],\n failures: [],\n };\n }\n\n const [facts, summaries, existingFacts, existingSummaries] = await Promise.all([\n input.repository.listFacts({ namespaceId: namespace.id, limit: 100_000 }),\n input.repository.listSummaries({ namespaceId: namespace.id, limit: 100_000 }),\n input.store.listFacts(),\n input.store.listSummaries(),\n ]);\n // The DB reads are hard-capped at 100k rows; a namespace at that ceiling\n // may be silently truncated. Say so instead of implying completeness.\n const warnings =\n facts.length >= 100_000 || summaries.length >= 100_000\n ? [\n \"namespace is at or above the 100,000-row read cap; the migration may be partial — prune or export in batches\",\n ]\n : [];\n const existingFactsById = new Map(existingFacts.map((fact) => [fact.id, fact]));\n const existingSummariesById = new Map(existingSummaries.map((summary) => [summary.id, summary]));\n const conflicts: MigrationConflict[] = [];\n const failures: MigrationFailure[] = [];\n const factsToWrite = facts.filter((fact) => {\n const existing = existingFactsById.get(fact.id);\n if (!existing) return true;\n // The file holds the redacted form, so compare against that.\n const databaseChecksum = checksum(redactPersisted(fact).content);\n const fileChecksum = checksum(existing.content);\n if (databaseChecksum !== fileChecksum) {\n conflicts.push({ kind: \"fact\", id: fact.id, databaseChecksum, fileChecksum });\n }\n return false;\n });\n const summariesToWrite = summaries.filter((summary) => {\n const existing = existingSummariesById.get(summary.id);\n if (!existing) return true;\n const databaseChecksum = checksum(summary.content);\n const fileChecksum = checksum(existing.content);\n if (databaseChecksum !== fileChecksum) {\n conflicts.push({ kind: \"summary\", id: summary.id, databaseChecksum, fileChecksum });\n }\n return false;\n });\n let factsWritten = 0;\n let summariesWritten = 0;\n\n if (input.write) {\n for (const fact of factsToWrite) {\n try {\n await input.store.writeFact({\n id: fact.id,\n namespace: input.namespace,\n subject: fact.subject,\n predicate: fact.predicate,\n object: fact.object,\n confidence: fact.confidence,\n createdAt: fact.createdAt.toISOString(),\n createdByAgent: fact.createdByAgent,\n sourceInteractionId: fact.sourceInteractionId,\n sourceDeleted: fact.sourceDeleted,\n supersededBy: fact.supersededBy,\n content: fact.content,\n });\n factsWritten += 1;\n } catch (error) {\n failures.push({ kind: \"fact\", id: fact.id, error: (error as Error).message });\n }\n }\n for (const summary of summariesToWrite) {\n try {\n await input.store.writeSummary({\n id: summary.id,\n namespace: input.namespace,\n sessionId: summary.sessionId,\n version: summary.version,\n tokenCount: summary.tokenCount,\n createdAt: summary.createdAt.toISOString(),\n createdByAgent: summary.createdByAgent,\n content: summary.content,\n });\n summariesWritten += 1;\n } catch (error) {\n failures.push({ kind: \"summary\", id: summary.id, error: (error as Error).message });\n }\n }\n }\n\n const factConflicts = conflicts.filter((conflict) => conflict.kind === \"fact\").length;\n const summaryConflicts = conflicts.length - factConflicts;\n return {\n namespace: input.namespace,\n dryRun: !input.write,\n ...(warnings.length > 0 ? { warnings } : {}),\n facts: {\n found: facts.length,\n existing: facts.length - factsToWrite.length - factConflicts,\n toWrite: factsToWrite.length,\n conflicts: factConflicts,\n written: factsWritten,\n },\n summaries: {\n found: summaries.length,\n existing: summaries.length - summariesToWrite.length - summaryConflicts,\n toWrite: summariesToWrite.length,\n conflicts: summaryConflicts,\n written: summariesWritten,\n },\n conflicts,\n failures,\n };\n}\n\nfunction checksum(content: string): string {\n return createHash(\"sha256\").update(content).digest(\"hex\");\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { applySessionDelivery, type DeliveryOptions } from \"../../context/delivery.js\";\nimport { buildBeforeEditContext, buildBootstrapContext } from \"../../context/engine.js\";\nimport type { CodeBuddy } from \"../../core/codebuddy.js\";\nimport { loadPlanPolicy } from \"../../core/config-file.js\";\nimport { listFactsPage } from \"../../core/operations.js\";\nimport { PlanFileStore } from \"../../core/plan-file-store.js\";\nimport { PlanLifecycle } from \"../../core/plan-lifecycle.js\";\nimport type { MemoryRepository } from \"../../core/repository.js\";\nimport type { CodeBuddyConfig, RecallInput, RememberInput } from \"../../core/types.js\";\nimport { llmMemoryOptions } from \"../../hooks/runtime.js\";\nimport { configuredReranker } from \"../../hooks/semantic.js\";\nimport { loadFreshIndex } from \"../../indexer/engine.js\";\nimport { loadArchitectureMap } from \"../../map/cache.js\";\nimport { buildArchitectureMap, neighbours, queryMap } from \"../../map/indexer.js\";\nimport { captureAfterTurn } from \"../../memory-extract/engine.js\";\nimport { assessRisk } from \"../../risk/service.js\";\nimport { estimateTokens } from \"../../savings/tokens.js\";\nimport { generateSuggestions } from \"../../suggest/engine.js\";\nimport { recordTrace } from \"../../tracing/log.js\";\nimport type { DatabaseServices, LazyDatabase } from \"../database.js\";\nimport { serializeToolResult } from \"../tool-result.js\";\n\n/**\n * Input caps. The output side is bounded (LIMITS in the context engine,\n * list_facts ≤ 100); the input side must be too — one tool call should never\n * push megabytes of content into Postgres, the embedding queue, or a plan\n * file that later flows back into agent context.\n */\nconst INPUT_LIMITS = {\n contentChars: 32_000,\n batchItems: 100,\n summaryChars: 32_000,\n planBodyChars: 64_000,\n planBriefChars: 4_000,\n titleChars: 300,\n arrayItems: 100,\n pathChars: 1_024,\n taskChars: 2_000,\n agentIdChars: 200,\n} as const;\n\n/** Optional session-aware delivery fields shared by the context tools. */\nconst deliverySchema = {\n sessionId: z.string().min(1).max(200).optional(),\n ack: z\n .string()\n .regex(/^dlv_[a-z0-9]+$/)\n .optional(),\n full: z.boolean().optional(),\n};\n\nconst rememberSchema = {\n sessionId: z.string().min(1).max(200).optional(),\n content: z.string().min(1).max(INPUT_LIMITS.contentChars),\n type: z.enum([\"interaction\", \"fact\", \"summary\"]).optional(),\n idempotencyKey: z.string().min(1).max(200).optional(),\n agentId: z.string().min(1).max(INPUT_LIMITS.agentIdChars).optional(),\n};\n\nconst recallSchema = {\n sessionId: z.string().min(1).max(200),\n query: z.string().min(1).max(INPUT_LIMITS.taskChars),\n budget: z.number().int().positive().optional(),\n callerModel: z.string().min(1).max(200).optional(),\n conflictMode: z.enum([\"all\", \"latest\", \"highest_confidence\"]).optional(),\n};\n\nconst shareSchema = {\n from: z.string().min(1).max(INPUT_LIMITS.agentIdChars).optional(),\n to_namespace: z.string().min(1).max(INPUT_LIMITS.agentIdChars),\n factIds: z.array(z.string().min(1).max(100)).min(1).max(INPUT_LIMITS.arrayItems),\n mode: z.enum([\"reference\", \"snapshot\"]).optional(),\n /** Snapshot mode: the target project's root; its Markdown store receives the copy. */\n to_project_root: z.string().min(1).max(INPUT_LIMITS.pathChars).optional(),\n agentId: z.string().min(1).max(INPUT_LIMITS.agentIdChars).optional(),\n};\n\nconst unshareSchema = {\n to_namespace: z.string().min(1).max(INPUT_LIMITS.agentIdChars),\n factIds: z.array(z.string().min(1).max(100)).min(1).max(INPUT_LIMITS.arrayItems),\n agentId: z.string().min(1).max(INPUT_LIMITS.agentIdChars).optional(),\n};\n\nconst plannedFileSchema = z.object({\n path: z.string().min(1).max(INPUT_LIMITS.pathChars),\n action: z.enum([\"create\", \"edit\", \"delete\"]),\n notes: z.string().max(INPUT_LIMITS.taskChars).optional(),\n});\nconst plannedTestSchema = z.object({\n path: z.string().min(1).max(INPUT_LIMITS.pathChars),\n description: z.string().max(INPUT_LIMITS.taskChars).optional(),\n});\nconst planPatchSchema = {\n title: z.string().min(1).max(INPUT_LIMITS.titleChars).optional(),\n brief: z.string().max(INPUT_LIMITS.planBriefChars).optional(),\n body: z.string().max(INPUT_LIMITS.planBodyChars).optional(),\n filesToTouch: z.array(plannedFileSchema).max(INPUT_LIMITS.arrayItems).optional(),\n testsToAdd: z.array(plannedTestSchema).max(INPUT_LIMITS.arrayItems).optional(),\n outOfScope: z\n .array(z.string().max(INPUT_LIMITS.pathChars))\n .max(INPUT_LIMITS.arrayItems)\n .optional(),\n risks: z.array(z.string().max(INPUT_LIMITS.taskChars)).max(INPUT_LIMITS.arrayItems).optional(),\n};\n\nexport const mcpToolInputSchemas = {\n remember: z.object(rememberSchema),\n remember_batch: z.object({ items: z.array(z.object(rememberSchema)).min(1) }),\n recall: z.object(recallSchema),\n list_facts: z.object({\n subject: z.string().optional(),\n limit: z.number().int().positive().max(100).default(50),\n cursor: z.string().optional(),\n }),\n list_namespaces: z.object({}),\n forget: z.object({ id: z.string().min(1) }),\n share: z.object(shareSchema),\n unshare: z.object(unshareSchema),\n plan_current: z.object({}),\n plan_create: z.object({\n title: z.string().min(1).max(INPUT_LIMITS.titleChars),\n brief: z.string().min(1).max(INPUT_LIMITS.planBriefChars),\n body: z.string().max(INPUT_LIMITS.planBodyChars).optional(),\n filesToTouch: z.array(plannedFileSchema).max(INPUT_LIMITS.arrayItems).optional(),\n testsToAdd: z.array(plannedTestSchema).max(INPUT_LIMITS.arrayItems).optional(),\n outOfScope: z\n .array(z.string().max(INPUT_LIMITS.pathChars))\n .max(INPUT_LIMITS.arrayItems)\n .optional(),\n risks: z.array(z.string().max(INPUT_LIMITS.taskChars)).max(INPUT_LIMITS.arrayItems).optional(),\n agentId: z.string().min(1).max(INPUT_LIMITS.agentIdChars).optional(),\n }),\n plan_amend: z.object({ id: z.string().min(1), patch: z.object(planPatchSchema) }),\n plan_status: z.object({\n id: z.string().min(1),\n status: z.enum([\"approved\", \"executing\", \"complete\", \"abandoned\"]),\n commitSha: z.string().optional(),\n notes: z.string().optional(),\n reason: z.string().optional(),\n }),\n risk_assess: z.object({\n planId: z.string().min(1).max(100).optional(),\n paths: z\n .array(z.string().min(1).max(INPUT_LIMITS.pathChars))\n .max(INPUT_LIMITS.arrayItems)\n .optional(),\n evidence: z.enum([\"overview\", \"source\"]).optional(),\n useGit: z.boolean().optional(),\n limit: z.number().int().positive().max(100).optional(),\n }),\n map_query: z.object({\n from: z.string().min(1).max(INPUT_LIMITS.pathChars).optional(),\n to: z.string().min(1).max(INPUT_LIMITS.pathChars).optional(),\n limit: z.number().int().positive().max(100).optional(),\n }),\n map_neighbours: z.object({\n module: z.string().min(1).max(INPUT_LIMITS.pathChars),\n direction: z.enum([\"in\", \"out\", \"both\"]).default(\"both\"),\n }),\n context_bootstrap: z.object({ ...deliverySchema }),\n context_before_edit: z.object({\n task: z.string().min(1).max(INPUT_LIMITS.taskChars).optional(),\n paths: z\n .array(z.string().min(1).max(INPUT_LIMITS.pathChars))\n .max(INPUT_LIMITS.arrayItems)\n .optional(),\n planId: z.string().min(1).max(100).optional(),\n evidence: z.enum([\"overview\", \"source\"]).optional(),\n useGit: z.boolean().optional(),\n tokenBudget: z.number().int().positive().max(100_000).optional(),\n ...deliverySchema,\n }),\n context_pack: z.object({\n task: z.string().min(1).max(INPUT_LIMITS.taskChars),\n paths: z\n .array(z.string().min(1).max(INPUT_LIMITS.pathChars))\n .max(INPUT_LIMITS.arrayItems)\n .optional(),\n planId: z.string().min(1).max(100).optional(),\n evidence: z.enum([\"overview\", \"source\"]).optional(),\n useGit: z.boolean().optional(),\n tokenBudget: z.number().int().positive().max(100_000).optional(),\n ...deliverySchema,\n }),\n context_after_turn: z.object({\n summary: z.string().min(1).max(INPUT_LIMITS.summaryChars),\n changedFiles: z\n .array(z.string().min(1).max(INPUT_LIMITS.pathChars))\n .max(INPUT_LIMITS.arrayItems)\n .optional(),\n planId: z.string().min(1).max(100).optional(),\n taskType: z.string().min(1).max(100).optional(),\n agentId: z.string().min(1).max(INPUT_LIMITS.agentIdChars).optional(),\n sessionId: z.string().min(1).max(200).optional(),\n verification: z.array(z.string().min(1).max(500)).max(20).optional(),\n commitSha: z.string().min(7).max(64).optional(),\n }),\n code_suggestions: z.object({\n paths: z\n .array(z.string().min(1).max(INPUT_LIMITS.pathChars))\n .max(INPUT_LIMITS.arrayItems)\n .optional(),\n planId: z.string().min(1).max(100).optional(),\n evidence: z.enum([\"overview\", \"source\"]).optional(),\n useGit: z.boolean().optional(),\n limit: z.number().int().positive().max(100).optional(),\n }),\n health: z.object({}),\n} as const;\n\n/** Tools that need PostgreSQL; every other tool is file-based. */\nconst DATABASE_TOOLS = [\n \"remember\",\n \"remember_batch\",\n \"recall\",\n \"list_facts\",\n \"list_namespaces\",\n \"forget\",\n \"share\",\n \"unshare\",\n];\nconst FILE_TOOLS = Object.keys(mcpToolInputSchemas).filter(\n (name) => !DATABASE_TOOLS.includes(name),\n);\nconst DEGRADED_FALLBACK =\n \"PostgreSQL is unavailable, so this call did nothing. File-based tools still work; read the repository directly and say recall is unavailable instead of guessing. Run `codebuddy db doctor` to diagnose; the next call after retryInMs reconnects.\";\n\n/**\n * Client tool profiles (#45). `full` (default) lists every tool; `core` lists\n * only the automatic context workflow, so a client pays a fraction of the\n * tool-schema tokens per session. Set `CODEBUDDY_TOOL_PROFILE=core` in the\n * client's MCP server environment.\n */\nexport type ToolProfile = \"core\" | \"full\";\nexport const CORE_TOOLS = [\n \"context_pack\",\n \"context_bootstrap\",\n \"context_before_edit\",\n \"context_after_turn\",\n \"code_suggestions\",\n \"health\",\n];\n\nexport function resolveToolProfile(value = process.env.CODEBUDDY_TOOL_PROFILE): ToolProfile {\n if (value === undefined || value === \"\" || value === \"full\") return \"full\";\n if (value === \"core\") return \"core\";\n throw new Error(`CODEBUDDY_TOOL_PROFILE must be \"core\" or \"full\", got \"${value}\"`);\n}\n\nexport type RegisterCodeBuddyToolsOptions =\n | { memory: CodeBuddy; repository: MemoryRepository }\n | {\n /** File-based tools read only namespace/projectRoot/tokenBudget from this. */\n config: Pick<CodeBuddyConfig, \"namespace\"> & {\n projectRoot?: string | undefined;\n tokenBudget?: number | undefined;\n };\n /** Optional PostgreSQL services; database tools degrade while it is unavailable. */\n database: Pick<LazyDatabase, \"get\" | \"status\">;\n };\n\n/** Cap plan bodies returned over MCP so a huge plan can't blow the context. */\nconst PLAN_BODY_LIMIT = 8_000;\n\nfunction serializePlan(plan: import(\"../../core/plan-file-store.js\").PlanSpec | null) {\n if (!plan) return { plan: null };\n const body =\n plan.body.length > PLAN_BODY_LIMIT\n ? `${plan.body.slice(0, PLAN_BODY_LIMIT)}\\n\\n…[truncated]`\n : plan.body;\n return { plan: { ...plan, body } };\n}\n\nexport function registerCodeBuddyTools(\n server: McpServer,\n options: RegisterCodeBuddyToolsOptions,\n profile: ToolProfile = \"full\",\n) {\n const exposed = (name: string) => profile === \"full\" || CORE_TOOLS.includes(name);\n // Every tool is defined once; tools outside the profile are dropped from tools/list.\n const config = \"database\" in options ? options.config : options.memory.config;\n const traceRoot = config.projectRoot ?? process.cwd();\n // Every tool is traced here, once, for `codebuddy dashboard`.\n const register: McpServer[\"registerTool\"] = (name, toolConfig, callback) => {\n const traced = (async (...args: unknown[]) => {\n const started = Date.now();\n const input = args.length > 1 ? (args[0] as { sessionId?: unknown }) : undefined;\n const trace = (ok: boolean, output?: string) =>\n void recordTrace(traceRoot, {\n kind: \"tool\",\n name,\n ms: Date.now() - started,\n ok,\n tokensIn: estimateTokens(input),\n tokensOut: estimateTokens(output),\n ...(typeof input?.sessionId === \"string\" ? { session: input.sessionId } : {}),\n });\n try {\n const result = (await (callback as (...a: unknown[]) => Promise<unknown>)(...args)) as {\n content?: Array<{ text?: string }>;\n isError?: boolean;\n };\n trace(result?.isError !== true, result?.content?.map((part) => part.text ?? \"\").join(\"\"));\n return result;\n } catch (error) {\n trace(false);\n throw error;\n }\n }) as typeof callback;\n const tool = server.registerTool(name, toolConfig, traced);\n if (!exposed(name)) tool.remove();\n return tool;\n };\n const json = serializeToolResult;\n\n const database: Pick<LazyDatabase, \"get\" | \"status\"> =\n \"database\" in options\n ? options.database\n : { get: async () => options, status: () => ({ available: true, state: \"ready\" }) };\n /** Run a database tool, or return an explicit, bounded degraded result. */\n const viaDatabase = async (tool: string, run: (db: DatabaseServices) => Promise<unknown>) => {\n let db: DatabaseServices;\n try {\n db = await database.get();\n } catch {\n return {\n ...json({ degraded: true, tool, database: database.status(), fallback: DEGRADED_FALLBACK }),\n isError: true,\n };\n }\n return json(await run(db));\n };\n\n const namespace = config.namespace;\n const projectRoot = config.projectRoot ?? process.cwd();\n const rankContextFacts = async (\n task: string,\n candidates: Array<{ id: string; text: string }>,\n ) => {\n // File context never starts or waits for an unavailable database connection.\n if (database.status().state !== \"ready\") return {};\n try {\n const { memory } = await database.get();\n return await memory.rankContextFacts(\n task,\n candidates.map(({ id }) => id),\n );\n } catch {\n return {};\n }\n };\n // Read once per server: `rerank` changes take effect on the next restart.\n const reranker = configuredReranker(projectRoot).catch(() => undefined);\n const rerankerOption = async () => {\n const configured = await reranker;\n return configured ? { reranker: configured } : {};\n };\n const planStore = new PlanFileStore(projectRoot);\n const planLifecycle = new PlanLifecycle(planStore);\n const toFiles = (\n items?: Array<{\n path: string;\n action: \"create\" | \"edit\" | \"delete\";\n notes?: string | undefined;\n }>,\n ) => items?.map((item) => ({ path: item.path, action: item.action, notes: item.notes ?? \"\" }));\n const toTests = (items?: Array<{ path: string; description?: string | undefined }>) =>\n items?.map((item) => ({ path: item.path, description: item.description ?? \"\" }));\n\n register(\n \"remember\",\n {\n title: \"Remember\",\n description: \"Store one CodeBuddy memory item.\",\n inputSchema: mcpToolInputSchemas.remember.shape,\n },\n async (input) => viaDatabase(\"remember\", (db) => db.memory.remember(toRememberInput(input))),\n );\n\n register(\n \"remember_batch\",\n {\n title: \"Remember batch\",\n description: \"Store multiple CodeBuddy memory items.\",\n inputSchema: mcpToolInputSchemas.remember_batch.shape,\n },\n async (input) =>\n viaDatabase(\"remember_batch\", async (db) => ({\n items: await db.memory.rememberBatch(input.items.map(toRememberInput)),\n })),\n );\n\n register(\n \"recall\",\n {\n title: \"Recall\",\n description: \"Recall prompt-ready CodeBuddy context.\",\n inputSchema: mcpToolInputSchemas.recall.shape,\n },\n async (input) => viaDatabase(\"recall\", (db) => db.memory.recall(toRecallInput(input))),\n );\n\n register(\n \"list_facts\",\n {\n title: \"List facts\",\n description: \"List facts in the configured namespace.\",\n inputSchema: mcpToolInputSchemas.list_facts.shape,\n },\n async (input) =>\n viaDatabase(\"list_facts\", (db) =>\n listFactsPage(\n db.memory,\n db.repository,\n cleanOptional(input) as { subject?: string; limit?: number; cursor?: string },\n ),\n ),\n );\n\n register(\n \"list_namespaces\",\n {\n title: \"List namespaces\",\n description: \"List CodeBuddy namespaces.\",\n inputSchema: mcpToolInputSchemas.list_namespaces.shape,\n },\n async () =>\n viaDatabase(\"list_namespaces\", async (db) => ({\n namespaces: await db.repository.listNamespaces(),\n })),\n );\n\n register(\n \"forget\",\n {\n title: \"Forget\",\n description: \"Delete an interaction or fact by id.\",\n inputSchema: mcpToolInputSchemas.forget.shape,\n },\n async (input) => viaDatabase(\"forget\", (db) => db.memory.forget(input.id)),\n );\n\n register(\n \"share\",\n {\n title: \"Share\",\n description:\n \"Share facts from the configured namespace to another namespace. Reference (default) is a live, revocable grant; snapshot copies the fact into the target project's Markdown store (requires to_project_root).\",\n inputSchema: mcpToolInputSchemas.share.shape,\n },\n async (input) =>\n viaDatabase(\"share\", (db) =>\n db.memory.share({\n from: input.from ?? namespace,\n to: input.to_namespace,\n factIds: input.factIds,\n ...(input.mode ? { mode: input.mode } : {}),\n ...(input.to_project_root ? { targetProjectRoot: input.to_project_root } : {}),\n ...(input.agentId ? { agentId: input.agentId } : {}),\n }),\n ),\n );\n\n register(\n \"unshare\",\n {\n title: \"Unshare\",\n description:\n \"Revoke live reference shares of configured-namespace facts to another namespace.\",\n inputSchema: mcpToolInputSchemas.unshare.shape,\n },\n async (input) =>\n viaDatabase(\"unshare\", (db) =>\n db.memory.unshare({\n to: input.to_namespace,\n factIds: input.factIds,\n ...(input.agentId ? { agentId: input.agentId } : {}),\n }),\n ),\n );\n\n // ── Plan tools (Proposal 02.6) ────────────────────────────────────\n register(\n \"plan_current\",\n {\n title: \"Current plan\",\n description: \"Return the active (approved or executing) plan for this namespace, or null.\",\n inputSchema: mcpToolInputSchemas.plan_current.shape,\n },\n async () => {\n const [current, policy] = await Promise.all([\n planLifecycle.current(namespace),\n loadPlanPolicy(projectRoot),\n ]);\n return json({ ...serializePlan(current), policy });\n },\n );\n\n register(\n \"plan_create\",\n {\n title: \"Create plan\",\n description: \"Create a draft plan in this namespace.\",\n inputSchema: mcpToolInputSchemas.plan_create.shape,\n },\n async (input) => {\n const files = toFiles(input.filesToTouch);\n const tests = toTests(input.testsToAdd);\n const created = await planLifecycle.create({\n namespace,\n title: input.title,\n brief: input.brief,\n ...(input.body !== undefined ? { body: input.body } : {}),\n ...(files ? { filesToTouch: files } : {}),\n ...(tests ? { testsToAdd: tests } : {}),\n ...(input.outOfScope !== undefined ? { outOfScope: input.outOfScope } : {}),\n ...(input.risks !== undefined ? { risks: input.risks } : {}),\n ...(input.agentId !== undefined ? { createdByAgent: input.agentId } : {}),\n });\n return json(serializePlan(created));\n },\n );\n\n register(\n \"plan_amend\",\n {\n title: \"Amend plan\",\n description: \"Amend a draft or executing plan's content fields.\",\n inputSchema: mcpToolInputSchemas.plan_amend.shape,\n },\n async (input) => {\n const patch = input.patch;\n const files = toFiles(patch.filesToTouch);\n const tests = toTests(patch.testsToAdd);\n const amended = await planLifecycle.amend(input.id, {\n ...(patch.title !== undefined ? { title: patch.title } : {}),\n ...(patch.brief !== undefined ? { brief: patch.brief } : {}),\n ...(patch.body !== undefined ? { body: patch.body } : {}),\n ...(files ? { filesToTouch: files } : {}),\n ...(tests ? { testsToAdd: tests } : {}),\n ...(patch.outOfScope !== undefined ? { outOfScope: patch.outOfScope } : {}),\n ...(patch.risks !== undefined ? { risks: patch.risks } : {}),\n });\n return json(serializePlan(amended));\n },\n );\n\n register(\n \"plan_status\",\n {\n title: \"Transition plan\",\n description: \"Move a plan through its lifecycle: approved, executing, complete, abandoned.\",\n inputSchema: mcpToolInputSchemas.plan_status.shape,\n },\n async (input) => {\n let next: import(\"../../core/plan-file-store.js\").PlanSpec;\n if (input.status === \"approved\") next = await planLifecycle.approve(input.id);\n else if (input.status === \"executing\") next = await planLifecycle.start(input.id);\n else if (input.status === \"complete\")\n next = await planLifecycle.complete(input.id, {\n ...(input.commitSha !== undefined ? { commitSha: input.commitSha } : {}),\n ...(input.notes !== undefined ? { notes: input.notes } : {}),\n });\n else\n next = await planLifecycle.abandon(\n input.id,\n input.reason !== undefined ? { reason: input.reason } : {},\n );\n return json(serializePlan(next));\n },\n );\n\n register(\n \"risk_assess\",\n {\n title: \"Assess risk\",\n description: \"Assess evidence-backed risk for a plan, explicit paths, or git changes.\",\n inputSchema: mcpToolInputSchemas.risk_assess.shape,\n },\n async (input) =>\n json(\n await assessRisk({\n repositoryRoot: projectRoot,\n namespace,\n ...(input.planId !== undefined ? { planId: input.planId } : {}),\n ...(input.paths !== undefined ? { paths: input.paths } : {}),\n ...(input.useGit !== undefined ? { useGit: input.useGit } : {}),\n ...(input.limit !== undefined ? { limit: input.limit } : {}),\n }),\n ),\n );\n\n register(\n \"map_query\",\n {\n title: \"Query architecture map\",\n description: \"Query lightweight module import edges in the current repository.\",\n inputSchema: mcpToolInputSchemas.map_query.shape,\n },\n async (input) => {\n const graph = await cachedMap();\n return json(\n queryMap(graph, {\n ...(input.from !== undefined ? { from: input.from } : {}),\n ...(input.to !== undefined ? { to: input.to } : {}),\n ...(input.limit !== undefined ? { limit: input.limit } : {}),\n }),\n );\n },\n );\n\n register(\n \"map_neighbours\",\n {\n title: \"Module neighbours\",\n description: \"Return inbound/outbound neighbours for a repository module.\",\n inputSchema: mcpToolInputSchemas.map_neighbours.shape,\n },\n async (input) => json(neighbours(await cachedMap(), input.module, input.direction)),\n );\n\n // ── Context engine tools (Proposal 04.2) ──────────────────────────\n const tokenBudget = config.tokenBudget;\n\n /**\n * The map cache (`.codebuddy/cache/architecture.json`) is the same source the\n * context engine uses. The index is validated against current source first\n * so edited imports are re-read; fall back to a fresh build only when no\n * cache exists yet.\n */\n const cachedMap = async () => {\n const { manifest } = await loadFreshIndex(projectRoot);\n const cached = await loadArchitectureMap(projectRoot, manifest);\n if (cached.map.modules.length > 0) return cached.map;\n return buildArchitectureMap(projectRoot);\n };\n\n register(\n \"context_bootstrap\",\n {\n title: \"Bootstrap project context\",\n description:\n \"Return compact project awareness for the start of a turn: identity, active plan and policy, top policy rules, incident hotspots, and an architecture summary. Prefer this over a manual recall at session start. Optional sessionId enables session-aware delivery: pass the previous response's delivery.deliveryId as ack and unchanged sections are omitted (listed in delivery.unchanged); pass full=true to get everything again.\",\n inputSchema: mcpToolInputSchemas.context_bootstrap.shape,\n },\n async (input) =>\n json(\n await applySessionDelivery(\n projectRoot,\n await buildBootstrapContext({\n repositoryRoot: projectRoot,\n namespace,\n ...(tokenBudget !== undefined ? { tokenBudget } : {}),\n }),\n deliveryOptions(input),\n ),\n ),\n );\n\n register(\n \"context_before_edit\",\n {\n title: \"Context before edit\",\n description:\n \"Assemble relevant context before changing code: resolves target files from paths, a plan, or git, then returns the relevant plan, matching policy rules, incident memory, an evidence-backed risk assessment, and import neighbours. Optional sessionId enables session-aware delivery: pass the previous response's delivery.deliveryId as ack and unchanged sections are omitted (listed in delivery.unchanged); pass full=true to get everything again.\",\n inputSchema: mcpToolInputSchemas.context_before_edit.shape,\n },\n async (input) =>\n json(\n await applySessionDelivery(\n projectRoot,\n await buildBeforeEditContext({\n repositoryRoot: projectRoot,\n namespace,\n semanticRanker: rankContextFacts,\n ...(await rerankerOption()),\n ...(input.evidence ? { evidence: input.evidence } : {}),\n ...(input.task !== undefined ? { task: input.task } : {}),\n ...(input.paths !== undefined ? { paths: input.paths } : {}),\n ...(input.planId !== undefined ? { planId: input.planId } : {}),\n ...(input.useGit !== undefined ? { useGit: input.useGit } : {}),\n ...(input.tokenBudget !== undefined ? { tokenBudget: input.tokenBudget } : {}),\n ...(tokenBudget !== undefined && input.tokenBudget === undefined\n ? { tokenBudget }\n : {}),\n }),\n deliveryOptions(input),\n ),\n ),\n );\n\n register(\n \"context_pack\",\n {\n title: \"Build task context pack\",\n description:\n \"Build the smallest useful context pack for a coding task. Automatically finds relevant files from the local index when paths are omitted, then includes only bounded symbols, policies, risks, incidents, architecture neighbours, and relevant team facts within the requested token budget. Use this once at the start of a task instead of manually chaining context, risk, map, and memory tools. Request evidence=source with exact paths when short excerpts omit needed behavior; source excerpts remain bounded by the token budget. Optional sessionId enables session-aware delivery: pass the previous response's delivery.deliveryId as ack and unchanged sections are omitted (listed in delivery.unchanged); pass full=true to get everything again.\",\n inputSchema: mcpToolInputSchemas.context_pack.shape,\n },\n async (input) =>\n json(\n await applySessionDelivery(\n projectRoot,\n await buildBeforeEditContext({\n repositoryRoot: projectRoot,\n namespace,\n task: input.task,\n semanticRanker: rankContextFacts,\n ...(await rerankerOption()),\n ...(input.evidence ? { evidence: input.evidence } : {}),\n ...(input.paths !== undefined ? { paths: input.paths } : {}),\n ...(input.planId !== undefined ? { planId: input.planId } : {}),\n ...(input.useGit !== undefined ? { useGit: input.useGit } : {}),\n tokenBudget: input.tokenBudget ?? tokenBudget ?? 4_000,\n }),\n deliveryOptions(input),\n ),\n ),\n );\n\n register(\n \"context_after_turn\",\n {\n title: \"Capture context after a turn\",\n description:\n \"Extract durable knowledge from a turn summary. Confident facts/decisions/incidents are auto-saved as Markdown; temporary notes, low-confidence, and sensitive items are queued for review under .codebuddy/memory/review/; chatter is ignored. Never saves sensitive content automatically. Turns that changed files or report verification also record a bounded worklog handoff under .codebuddy/worklog/ so later chats can recover the work.\",\n inputSchema: mcpToolInputSchemas.context_after_turn.shape,\n },\n async (input) => {\n const result = await captureAfterTurn(\n {\n summary: input.summary,\n namespace,\n ...(input.changedFiles !== undefined ? { changedFiles: input.changedFiles } : {}),\n ...(input.planId !== undefined ? { planId: input.planId } : {}),\n ...(input.taskType !== undefined ? { taskType: input.taskType } : {}),\n ...(input.agentId !== undefined ? { agentId: input.agentId } : {}),\n ...(input.sessionId !== undefined ? { sessionId: input.sessionId } : {}),\n ...(input.verification !== undefined ? { verification: input.verification } : {}),\n ...(input.commitSha !== undefined ? { commitSha: input.commitSha } : {}),\n },\n {\n repositoryRoot: projectRoot,\n ...((input.changedFiles?.length ?? 0) > 0 ? await llmMemoryOptions(projectRoot) : {}),\n },\n );\n // Capture writes reviewable Markdown first. Import it immediately so\n // database recall and the embedding worker see the same memory without\n // requiring a restart or a manual `codebuddy reindex`. Without a\n // database the Markdown is still saved and imported on reconnect.\n const db = await database.get().catch(() => undefined);\n await db?.memory.reindexMemory({ respace: false });\n return json({\n saved: result.saved.map((d) => ({\n id: d.ref,\n class: d.candidate.class,\n subject: d.candidate.subject,\n })),\n queuedForReview: result.queuedForReview.map((d) => ({\n id: d.ref,\n class: d.candidate.class,\n reason: d.reason,\n sensitive: d.sensitive,\n })),\n ignored: result.ignored.length,\n reasons: result.reasons,\n extractor: result.extractor,\n worklogId: result.worklogId,\n ...(db ? {} : { degraded: true, database: database.status() }),\n });\n },\n );\n\n register(\n \"code_suggestions\",\n {\n title: \"Code suggestions\",\n description:\n \"Return read-only, evidence-backed code-quality suggestions for a change set (paths, a plan, or git). Composes risk, plan divergence, architecture blast radius, incident history, missing tests, and stale policies. Never edits code.\",\n inputSchema: mcpToolInputSchemas.code_suggestions.shape,\n },\n async (input) =>\n json(\n await generateSuggestions({\n repositoryRoot: projectRoot,\n namespace,\n ...(input.paths !== undefined ? { paths: input.paths } : {}),\n ...(input.planId !== undefined ? { planId: input.planId } : {}),\n ...(input.useGit !== undefined ? { useGit: input.useGit } : {}),\n ...(input.limit !== undefined ? { limit: input.limit } : {}),\n }),\n ),\n );\n\n register(\n \"health\",\n {\n title: \"Health\",\n description:\n \"Report which CodeBuddy tools are available right now: file-based tools always work; database tools (recall, remember, facts) need PostgreSQL. Does not wait on a connection attempt.\",\n inputSchema: mcpToolInputSchemas.health.shape,\n },\n async () =>\n json({\n database: database.status(),\n tools: {\n profile,\n file: FILE_TOOLS.filter(exposed),\n database: DATABASE_TOOLS.filter(exposed),\n },\n }),\n );\n}\n\nfunction deliveryOptions(input: {\n sessionId?: string | undefined;\n ack?: string | undefined;\n full?: boolean | undefined;\n}): DeliveryOptions {\n return cleanOptional(input as Record<string, unknown>) as DeliveryOptions;\n}\n\nfunction toRememberInput(input: z.infer<typeof mcpToolInputSchemas.remember>): RememberInput {\n return cleanOptional(input) as RememberInput;\n}\n\nfunction toRecallInput(input: z.infer<typeof mcpToolInputSchemas.recall>): RecallInput {\n return cleanOptional(input) as RecallInput;\n}\n\nfunction cleanOptional(input: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== undefined));\n}\n","import { createHash } from \"node:crypto\";\nimport { estimateTokens } from \"../savings/tokens.js\";\nimport { enforceContextBudget } from \"./budget.js\";\nimport { SessionCapsuleLedger } from \"./session-ledger.js\";\n\n/**\n * Session-aware delivery for the MCP context tools. With a `sessionId`, a\n * section whose exact content the client already confirmed receiving (via\n * `ack`) in this session is omitted and listed in `delivery.unchanged`; the\n * ledger keeps a few acknowledged versions per section, so switching back to\n * an earlier task does not resend what the client still holds. Without a\n * `sessionId` the payload is returned untouched, so clients without session\n * support always get complete context.\n *\n * Safety evidence that changes per call (target paths, risks, diffs) is never\n * omitted. Any ledger failure falls back to the full payload.\n */\n\n/** Delivery contract version, bumped on any incompatible change. */\nexport const DELIVERY_VERSION = 1;\n\n/** Sections that may be omitted when unchanged. Everything else is always sent. */\nconst OMITTABLE_SECTIONS = [\n \"plan\",\n \"policyRules\",\n \"incidents\",\n \"facts\",\n \"rules\",\n \"handoffs\",\n \"architecture\",\n \"symbols\",\n \"neighbours\",\n] as const;\n\n/**\n * An acknowledged section is only treated as already-seen for this long; after\n * that it is resent, bounding how stale a client's copy can get.\n */\nconst SESSION_TTL_MS = 30 * 60 * 1000;\n\nexport type DeliveryOptions = {\n sessionId?: string;\n /** The `deliveryId` of the previous response the client actually received. */\n ack?: string;\n /** Resend every section, e.g. after the client's context was compacted. */\n full?: boolean;\n};\n\nexport type DeliveryInfo = {\n version: typeof DELIVERY_VERSION;\n sessionId: string;\n deliveryId: string;\n ack: \"none\" | \"accepted\" | \"unknown\";\n unchanged: string[];\n avoidedTokens: number;\n note: string;\n};\n\nexport async function applySessionDelivery(\n repositoryRoot: string,\n payload: object,\n options: DeliveryOptions,\n): Promise<Record<string, unknown>> {\n let output: Record<string, unknown> = { ...payload };\n if (!options.sessionId) return output;\n\n // Keys stay `mcp:`-prefixed on purpose, separate from the hook capsule keys\n // (`plan`, `fact:<id>`, ...) in the same session ledger. The hook block sends\n // bounded one-line renderings marked on write; these hashes cover the full\n // JSON section and count only once acknowledged. A shared key would let a\n // clipped hook line suppress a full section it never carried, or the two\n // paths overwrite each other's records. The paths share only reset.\n const sections = OMITTABLE_SECTIONS.filter((key) => output[key] !== undefined).map((key) => ({\n key: `mcp:${key}`,\n hash: createHash(\"sha256\").update(JSON.stringify(output[key])).digest(\"hex\"),\n }));\n\n try {\n await new SessionCapsuleLedger(repositoryRoot).exchange(options.sessionId, sections, {\n ...(options.ack !== undefined ? { ack: options.ack } : {}),\n ttlMs: SESSION_TTL_MS,\n ...(options.full ? { full: true } : {}),\n prepare: (exchange) => {\n const unchanged = exchange.unchanged.map((key) => key.slice(\"mcp:\".length));\n let avoidedTokens = 0;\n for (const key of unchanged) {\n avoidedTokens += estimateTokens(output[key]);\n delete output[key];\n }\n const delivery: DeliveryInfo = {\n version: DELIVERY_VERSION,\n sessionId: options.sessionId as string,\n deliveryId: exchange.deliveryId,\n ack: exchange.ack,\n unchanged,\n avoidedTokens,\n note: \"Pass deliveryId as ack. Use full=true after losing context.\",\n };\n output.delivery = delivery;\n output = enforceContextBudget(output);\n // Hash what was actually delivered. A section shortened or omitted by\n // the budget must never acknowledge its original, complete version.\n return OMITTABLE_SECTIONS.filter((key) => output[key] !== undefined).map((key) => ({\n key: `mcp:${key}`,\n hash: createHash(\"sha256\").update(JSON.stringify(output[key])).digest(\"hex\"),\n }));\n },\n });\n return output;\n } catch {\n return enforceContextBudget({ ...payload });\n }\n}\n","import { constants } from \"node:fs\";\nimport { access } from \"node:fs/promises\";\nimport { extname, join, resolve } from \"node:path\";\nimport { MemoryFileStore } from \"../core/memory-file-store.js\";\nimport { PlanFileStore, type PlanSpec } from \"../core/plan-file-store.js\";\nimport { PlanLifecycle } from \"../core/plan-lifecycle.js\";\nimport { buildArchitectureMap } from \"../map/indexer.js\";\nimport type { ArchitectureMap } from \"../map/types.js\";\nimport { assessRisk, resolveRiskPaths } from \"../risk/service.js\";\nimport { readPolicyFile } from \"../risk/signals/policy.js\";\nimport { SEVERITY_RANK, type Suggestion, type SuggestResult } from \"./types.js\";\n\n/**\n * Deterministic, read-only suggestion generation. Nothing here writes to disk\n * or edits code — it only reads the working tree and CodeBuddy's own artifacts.\n * Each category attaches its own evidence; anything without evidence is not\n * emitted. Findings are de-duplicated per (category, path) and ranked by\n * severity so the few important ones surface first.\n */\n\nconst RISK_SUGGEST_THRESHOLD = 50;\nconst BLAST_RADIUS_MEDIUM = 5;\nconst BLAST_RADIUS_HIGH = 10;\nconst CODE_EXTENSIONS = new Set([\".ts\", \".tsx\", \".js\", \".jsx\", \".mjs\", \".cjs\"]);\n\nexport type SuggestInput = {\n repositoryRoot?: string;\n namespace: string;\n paths?: string[];\n planId?: string;\n useGit?: boolean;\n limit?: number;\n};\n\nexport async function generateSuggestions(input: SuggestInput): Promise<SuggestResult> {\n const repositoryRoot = resolve(input.repositoryRoot ?? process.cwd());\n const namespace = input.namespace;\n\n const targetPaths = await resolveRiskPaths({\n repositoryRoot,\n ...(input.paths !== undefined ? { paths: input.paths } : {}),\n ...(input.planId !== undefined ? { planId: input.planId } : {}),\n ...(input.useGit !== undefined ? { useGit: input.useGit } : {}),\n });\n\n const planStore = new PlanFileStore(repositoryRoot);\n const memoryStore = new MemoryFileStore(repositoryRoot);\n\n const [risks, plan, incidents, map, policyFile] = await Promise.all([\n assessRisk({\n repositoryRoot,\n namespace,\n ...(input.paths !== undefined ? { paths: input.paths } : {}),\n ...(input.planId !== undefined ? { planId: input.planId } : {}),\n ...(input.useGit !== undefined ? { useGit: input.useGit } : {}),\n }),\n resolveRelevantPlan(planStore, namespace, input.planId),\n memoryStore.findIncidentFactsForPaths({ namespace, paths: targetPaths }),\n buildArchitectureMap(repositoryRoot),\n readPolicyFile(join(repositoryRoot, \".codebuddy\", \"policies.yaml\")),\n ]);\n\n const suggestions: Suggestion[] = [];\n\n // 1. Risk — surface the change set's highest-scoring paths.\n for (const item of risks.items) {\n if (item.score < RISK_SUGGEST_THRESHOLD) continue;\n suggestions.push({\n id: `risk:${item.path}`,\n category: \"risk\",\n severity: item.score >= 75 ? \"high\" : \"medium\",\n title: `Review high-risk change to ${item.path}`,\n detail: item.reason,\n path: item.path,\n evidence: [\n { kind: \"risk_score\", detail: `score ${item.score} (${item.category})` },\n ...(item.evidence.length > 0\n ? [{ kind: \"risk_evidence\", detail: `${item.evidence.length} evidence item(s)` }]\n : []),\n ],\n commandHint: `codebuddy risk assess --paths ${item.path} --explain`,\n });\n }\n\n // 2. Incident history — files with an unresolved past incident.\n for (const incident of incidents) {\n const path = incident.paths[0] ?? \"(unknown)\";\n suggestions.push({\n id: `incident:${path}`,\n category: \"incident\",\n severity: \"high\",\n title: `Recurring incident hotspot: ${path}`,\n detail: `${incident.subject} — proceed carefully; this area has an unresolved incident.`,\n path,\n evidence: [{ kind: \"incident\", detail: `${incident.id} severity=${incident.severity}` }],\n });\n }\n\n // 3. Architecture — high blast radius (many dependents).\n const inbound = inboundDegrees(map);\n for (const path of targetPaths) {\n const dependents = inbound.get(path) ?? 0;\n if (dependents < BLAST_RADIUS_MEDIUM) continue;\n suggestions.push({\n id: `architecture:${path}`,\n category: \"architecture\",\n severity: dependents >= BLAST_RADIUS_HIGH ? \"high\" : \"medium\",\n title: `High blast radius: ${path}`,\n detail: `${dependents} module(s) import ${path}; a change here ripples widely.`,\n path,\n evidence: [{ kind: \"dependents\", detail: `${dependents} inbound import(s)` }],\n commandHint: `codebuddy map dependents ${path}`,\n });\n }\n\n // 4. Plan divergence — editing files the active plan does not cover.\n if (plan) {\n const planned = new Set(plan.filesToTouch.map((file) => normalize(file.path)));\n const strays = targetPaths.filter((path) => !planned.has(normalize(path)));\n if (planned.size > 0 && strays.length > 0) {\n suggestions.push({\n id: `plan_divergence:${plan.id}`,\n category: \"plan_divergence\",\n severity: \"medium\",\n title: `Change set diverges from plan ${plan.id}`,\n detail: `${strays.length} target file(s) are outside the active plan's scope: ${strays\n .slice(0, 5)\n .join(\", \")}.`,\n evidence: [\n { kind: \"plan\", detail: `plan ${plan.id} (${plan.status})` },\n { kind: \"out_of_scope\", detail: strays.slice(0, 5).join(\", \") },\n ],\n commandHint: `codebuddy plan show ${plan.id}`,\n });\n }\n }\n\n // 5. Missing tests — changed code files with no discoverable test.\n for (const path of targetPaths) {\n if (!isTestableCode(path)) continue;\n if (await hasSiblingTest(repositoryRoot, path)) continue;\n suggestions.push({\n id: `missing_test:${path}`,\n category: \"missing_test\",\n severity: \"low\",\n title: `No test found for ${path}`,\n detail: \"Consider adding a test alongside this change.\",\n path,\n evidence: [{ kind: \"no_test\", detail: \"no *.test.* / *.spec.* sibling found\" }],\n });\n }\n\n // 6. Stale policy — a rule whose target directory no longer exists.\n for (const rule of policyFile.rules) {\n const prefix = literalPrefix(rule.pattern);\n if (!prefix) continue;\n if (await pathExists(join(repositoryRoot, prefix))) continue;\n suggestions.push({\n id: `stale_policy:${rule.id}`,\n category: \"stale_policy\",\n severity: \"low\",\n title: `Stale policy rule \"${rule.id}\"`,\n detail: `Pattern \"${rule.pattern}\" targets \"${prefix}\", which does not exist.`,\n evidence: [{ kind: \"policy_rule\", detail: `${rule.id} → ${rule.pattern}` }],\n });\n }\n\n const deduped = dedupe(suggestions).sort(compareSuggestions);\n const limited = input.limit ? deduped.slice(0, input.limit) : deduped;\n\n return {\n suggestions: limited,\n stats: buildStats(targetPaths.length, limited),\n };\n}\n\n// ── helpers ─────────────────────────────────────────────────────────\n\nasync function resolveRelevantPlan(\n store: PlanFileStore,\n namespace: string,\n planId?: string,\n): Promise<PlanSpec | null> {\n if (planId) {\n try {\n return await store.readPlan(planId);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw error;\n }\n }\n return new PlanLifecycle(store).current(namespace);\n}\n\nfunction inboundDegrees(map: ArchitectureMap): Map<string, number> {\n const inbound = new Map<string, number>();\n for (const edge of map.edges) inbound.set(edge.to, (inbound.get(edge.to) ?? 0) + 1);\n return inbound;\n}\n\nfunction isTestableCode(path: string): boolean {\n if (/\\.(test|spec)\\.[cm]?[jt]sx?$/.test(path)) return false;\n if (path.endsWith(\".d.ts\")) return false;\n if (path.includes(\"__tests__/\")) return false;\n return CODE_EXTENSIONS.has(extname(path).toLowerCase());\n}\n\nasync function hasSiblingTest(repositoryRoot: string, path: string): Promise<boolean> {\n const ext = extname(path);\n const base = path.slice(0, path.length - ext.length);\n const dir = base.includes(\"/\") ? base.slice(0, base.lastIndexOf(\"/\")) : \"\";\n const name = base.includes(\"/\") ? base.slice(base.lastIndexOf(\"/\") + 1) : base;\n const candidates = [\n `${base}.test${ext}`,\n `${base}.spec${ext}`,\n `${dir ? `${dir}/` : \"\"}__tests__/${name}${ext}`,\n `${dir ? `${dir}/` : \"\"}__tests__/${name}.test${ext}`,\n ];\n for (const candidate of candidates) {\n if (await pathExists(join(repositoryRoot, candidate))) return true;\n }\n return false;\n}\n\n/**\n * The literal directory prefix of a glob — everything before the first\n * wildcard, trimmed to a whole path segment. `src/auth/**` → `src/auth`,\n * `src/**/*.ts` → `src`, `*.env` → `` (no literal prefix to check).\n */\nfunction literalPrefix(pattern: string): string {\n const normalized = normalize(pattern);\n const wildcard = normalized.search(/[*?[]/);\n if (wildcard === -1) return normalized.replace(/\\/+$/, \"\");\n const beforeWildcard = normalized.slice(0, wildcard);\n const lastSlash = beforeWildcard.lastIndexOf(\"/\");\n return lastSlash === -1 ? \"\" : beforeWildcard.slice(0, lastSlash);\n}\n\nfunction dedupe(suggestions: Suggestion[]): Suggestion[] {\n const byId = new Map<string, Suggestion>();\n for (const suggestion of suggestions) {\n const existing = byId.get(suggestion.id);\n if (!existing || SEVERITY_RANK[suggestion.severity] > SEVERITY_RANK[existing.severity]) {\n byId.set(suggestion.id, suggestion);\n }\n }\n return [...byId.values()];\n}\n\nfunction compareSuggestions(left: Suggestion, right: Suggestion): number {\n const severity = SEVERITY_RANK[right.severity] - SEVERITY_RANK[left.severity];\n if (severity !== 0) return severity;\n if (left.category !== right.category) return left.category.localeCompare(right.category);\n return (left.path ?? \"\").localeCompare(right.path ?? \"\");\n}\n\nfunction buildStats(pathsConsidered: number, suggestions: Suggestion[]) {\n const byCategory: Record<string, number> = {};\n const bySeverity: Record<string, number> = {};\n for (const suggestion of suggestions) {\n byCategory[suggestion.category] = (byCategory[suggestion.category] ?? 0) + 1;\n bySeverity[suggestion.severity] = (bySeverity[suggestion.severity] ?? 0) + 1;\n }\n return { pathsConsidered, produced: suggestions.length, byCategory, bySeverity };\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\nfunction normalize(path: string): string {\n return path.replace(/\\\\/g, \"/\").replace(/^\\.\\//, \"\");\n}\n","/**\n * Suggestion model (Proposal 04 Phase 04.6). The suggestion engine reads the\n * signals CodeBuddy already computes — risk, the active plan, the architecture\n * map, incident memory — plus a couple of deterministic checks (missing tests,\n * stale policies) and turns them into a short list of evidence-backed, severity-\n * ranked suggestions. It is strictly read-only: it never edits code, and it\n * never emits a suggestion without evidence. The bias is a careful senior\n * reviewer, not a noisy linter — few strong findings over many weak ones.\n */\n\nexport const SUGGESTION_CATEGORIES = [\n \"risk\",\n \"plan_divergence\",\n \"architecture\",\n \"incident\",\n \"missing_test\",\n \"stale_policy\",\n] as const;\nexport type SuggestionCategory = (typeof SUGGESTION_CATEGORIES)[number];\n\nexport const SUGGESTION_SEVERITIES = [\"info\", \"low\", \"medium\", \"high\"] as const;\nexport type SuggestionSeverity = (typeof SUGGESTION_SEVERITIES)[number];\n\n/** Every suggestion cites at least one of these. No evidence, no suggestion. */\nexport type SuggestionEvidence = {\n kind: string;\n detail: string;\n};\n\nexport type Suggestion = {\n /** Stable id (`category:path`) so a UI can dedupe and track across runs. */\n id: string;\n category: SuggestionCategory;\n severity: SuggestionSeverity;\n title: string;\n detail: string;\n path?: string;\n evidence: SuggestionEvidence[];\n /** Optional follow-up command, e.g. `codebuddy risk assess --paths …`. */\n commandHint?: string;\n};\n\nexport type SuggestionStats = {\n pathsConsidered: number;\n produced: number;\n byCategory: Record<string, number>;\n bySeverity: Record<string, number>;\n};\n\nexport type SuggestResult = {\n suggestions: Suggestion[];\n stats: SuggestionStats;\n};\n\nexport const SEVERITY_RANK: Record<SuggestionSeverity, number> = {\n high: 3,\n medium: 2,\n low: 1,\n info: 0,\n};\n","/** Preserve compatibility by default; hosts forwarding both forms can select text. */\nexport function serializeToolResult(\n value: unknown,\n format = process.env.CODEBUDDY_RESPONSE_FORMAT ?? \"both\",\n) {\n if (format !== \"text\" && format !== \"both\") {\n throw new Error('CODEBUDDY_RESPONSE_FORMAT must be \"text\" or \"both\"');\n }\n return {\n content: [{ type: \"text\" as const, text: JSON.stringify(value) }],\n ...(format === \"both\" && value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? { structuredContent: value as Record<string, unknown> }\n : {}),\n };\n}\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport pino from \"pino\";\nimport { startProjectSync } from \"../automation/project-sync.js\";\nimport type { CodeBuddy } from \"../core/codebuddy.js\";\nimport { codeBuddyConfigSchema } from \"../core/config.js\";\nimport { loadRuntimeConfig } from \"../core/config-file.js\";\nimport { createRuntime } from \"../core/operations.js\";\nimport { resolveInitializedProjectRoot } from \"../core/project-root.js\";\nimport type { CodeBuddyConfig } from \"../core/types.js\";\nimport { VERSION } from \"../version.js\";\nimport { createLazyDatabase } from \"./database.js\";\nimport { serializeToolResult } from \"./tool-result.js\";\nimport { registerCodeBuddyTools, resolveToolProfile } from \"./tools/index.js\";\n\nexport type StartMcpServerOptions = {\n config?: CodeBuddyConfig;\n};\n\nexport async function startMcpServer(options: StartMcpServerOptions = {}): Promise<void> {\n const logger = pino({ level: process.env.LOG_LEVEL ?? \"info\" }, process.stderr);\n let projectRoot: string;\n try {\n projectRoot = await resolveInitializedProjectRoot(\n options.config?.projectRoot ?? process.env.CODEBUDDY_PROJECT_ROOT,\n );\n } catch (error) {\n // Exiting here shows up in the client only as \"server failed to connect\".\n // Stay connected instead, scan nothing, and explain the fix through the\n // `health` tool and the server instructions the agent reads.\n logger.error((error as Error).message);\n await startSetupServer((error as Error).message);\n return;\n }\n const config = {\n ...(options.config ?? (await loadRuntimeConfig(projectRoot, { requireDatabase: false }))),\n projectRoot,\n };\n // Invalid config is a startup error; a missing or unreachable database is not.\n (config.postgresUrl\n ? codeBuddyConfigSchema\n : codeBuddyConfigSchema.omit({ postgresUrl: true })\n ).parse(config);\n const toolProfile = resolveToolProfile();\n // PostgreSQL is optional: file-based tools are served immediately and the\n // database tools report a degraded result until a connection succeeds.\n // A failed or partial memory import must not hide the healthy file-only\n // context, so it is logged, never thrown.\n // `respace` only on connect (server start): a hand edit re-importing live\n // must not re-queue another install's vectors on every file change.\n const importMemory = async (memory: CodeBuddy, respace: boolean) => {\n try {\n const result = await memory.reindexMemory(respace ? {} : { respace: false });\n for (const failure of result.failures) {\n logger.error(`[sync] memory file skipped (${failure.id}): ${failure.error}`);\n }\n } catch (error) {\n logger.error(`[sync] Markdown memory import failed: ${(error as Error).message}`);\n }\n };\n const database = createLazyDatabase(async () => {\n try {\n const runtime = await createRuntime(config, { autoBootstrap: true }).catch(\n (error: unknown) => {\n // The bundled Docker Postgres may simply be stopped (Docker restarted,\n // container removed): start it once in the background (an image pull\n // can take a minute) and fail this attempt now, so database tools\n // answer \"degraded\" at once instead of waiting; once it is up, a\n // retry connects.\n void startBundledPostgres(config.postgresUrl, error, logger).then((started) => {\n if (started) retryUntilConnected();\n });\n throw error;\n },\n );\n await importMemory(runtime.memory, true);\n logger.info(\"PostgreSQL connected; memory tools available\");\n return runtime;\n } catch (error) {\n logger.warn(\n `PostgreSQL unavailable; serving file-based tools only: ${(error as Error).message}`,\n );\n throw error;\n }\n });\n void database.get().catch(() => undefined);\n // After starting the container: PostgreSQL needs a few seconds to accept\n // connections, so retry (past the lazy cooldown) for up to two minutes.\n const retryUntilConnected = (attempt = 0) => {\n if (attempt >= 20 || database.status().state === \"ready\") return;\n setTimeout(() => {\n void database.get().then(\n () => undefined,\n () => retryUntilConnected(attempt + 1),\n );\n }, 6_000).unref();\n };\n const sync = startProjectSync({\n repositoryRoot: projectRoot,\n ...(config.graphify ? { graphify: config.graphify } : {}),\n logger,\n // Hand edits re-import live once PostgreSQL is connected; until then the\n // next successful connect imports everything.\n onMemoryChange: async () => {\n if (database.status().state === \"ready\") {\n await importMemory((await database.get()).memory, false);\n }\n },\n });\n const server = new McpServer({ name: \"codebuddy\", version: VERSION });\n registerCodeBuddyTools(server, { config, database }, toolProfile);\n\n const shutdown = async () => {\n logger.info(\"codebuddy MCP server shutting down\");\n await server.close();\n await sync.close();\n await database.close();\n };\n let stopping: Promise<void> | undefined;\n const stop = () => {\n stopping ??= shutdown().finally(() => process.exit(0));\n };\n process.once(\"SIGINT\", stop);\n process.once(\"SIGTERM\", stop);\n // EOF on stdin means the client is gone; without this the server outlives it.\n process.stdin.once(\"end\", stop);\n\n try {\n await sync.ready;\n await server.connect(new StdioServerTransport());\n } catch (error) {\n await sync.close();\n await database.close();\n throw error;\n }\n logger.info(`codebuddy MCP server started over stdio (tool profile: ${toolProfile})`);\n}\n\n/**\n * Connected, but unconfigured: one `health` tool and MCP instructions that say\n * why no project is served and how to fix it. No file is indexed or read.\n */\nasync function startSetupServer(problem: string): Promise<void> {\n const fix = [\n \"Open your project folder in a terminal and run `codebuddy use` (or `codebuddy init`) if it has no .codebuddy/config.json.\",\n \"Then register CodeBuddy from that folder so the client starts it there: `codebuddy claude install` (Claude Desktop) or `codebuddy codex install` (Codex), and restart the client.\",\n \"Or set CODEBUDDY_PROJECT_ROOT to the project's absolute path in this server's env.\",\n ];\n const server = new McpServer(\n { name: \"codebuddy\", version: VERSION },\n {\n instructions: `CodeBuddy is connected but not serving a project: ${problem} To fix it: ${fix.join(\" \")}`,\n },\n );\n server.registerTool(\n \"health\",\n {\n title: \"Health\",\n description:\n \"Report CodeBuddy's status. This server has no project yet; the result says why and how to fix it.\",\n },\n async () => serializeToolResult({ ok: false, project: null, problem, fix }),\n );\n const stop = () => void server.close().finally(() => process.exit(0));\n process.once(\"SIGINT\", stop);\n process.once(\"SIGTERM\", stop);\n process.stdin.once(\"end\", stop);\n await server.connect(new StdioServerTransport());\n}\n\nlet triedBundledPostgres = false;\n\n/**\n * When PostgreSQL refuses connections at the bundled default address\n * (localhost:5432, user codebuddy), start the bundled container once per\n * process: `docker start codebuddy-postgres`, or `docker compose up -d` when\n * the container was removed (its data volume is reused). Never starts Docker\n * itself. CODEBUDDY_DOCKER_AUTOSTART=0 turns this off.\n */\nexport async function startBundledPostgres(\n postgresUrl: string,\n error: unknown,\n logger: Pick<pino.Logger, \"info\" | \"warn\">,\n): Promise<boolean> {\n if (triedBundledPostgres || process.env.CODEBUDDY_DOCKER_AUTOSTART === \"0\") return false;\n if (!isBundledPostgres(postgresUrl) || !isConnectionRefused(error)) return false;\n triedBundledPostgres = true;\n const { isDockerDaemonRunning } = await import(\"../cli/commands/docker-autostart.js\");\n if (!(await isDockerDaemonRunning())) {\n logger.warn(\n \"PostgreSQL is down and Docker is not running; start Docker Desktop, then `codebuddy postgres up`.\",\n );\n return false;\n }\n const { execFile } = await import(\"node:child_process\");\n const started = await new Promise<boolean>((done) =>\n execFile(\"docker\", [\"start\", \"codebuddy-postgres\"], { timeout: 30_000 }, (failed) =>\n done(failed === null),\n ),\n );\n if (started) {\n logger.info(\"Started the stopped codebuddy-postgres container; retrying PostgreSQL.\");\n return true;\n }\n const { postgresUp } = await import(\"../cli/commands/postgres-docker.js\");\n const result = await postgresUp().catch(() => ({ code: 1, stderr: \"docker compose failed\" }));\n if (result.code === 0) {\n logger.info(\"Started the bundled PostgreSQL container; retrying PostgreSQL.\");\n return true;\n }\n logger.warn(\n `Could not start the bundled PostgreSQL container: ${String(result.stderr).trim().slice(0, 200)}`,\n );\n return false;\n}\n\n/** The address `codebuddy use` / docker-compose.yml set up. */\nexport function isBundledPostgres(postgresUrl: string): boolean {\n try {\n const url = new URL(postgresUrl);\n return (\n [\"localhost\", \"127.0.0.1\", \"::1\", \"[::1]\"].includes(url.hostname) &&\n (url.port === \"\" || url.port === \"5432\") &&\n url.username === \"codebuddy\"\n );\n } catch {\n return false;\n }\n}\n\nfunction isConnectionRefused(error: unknown): boolean {\n for (\n let current = error as { code?: unknown; cause?: unknown; errors?: unknown[] } | undefined;\n current;\n ) {\n if (current.code === \"ECONNREFUSED\") return true;\n if (\n Array.isArray(current.errors) &&\n current.errors.some((inner) => isConnectionRefused(inner))\n ) {\n return true;\n }\n current = current.cause as typeof current;\n }\n return false;\n}\n","import { type FSWatcher, watch as fsWatch } from \"node:fs\";\nimport { join, relative, resolve, sep } from \"node:path\";\nimport { indexRepository } from \"../indexer/engine.js\";\nimport { createCoalescer, watchRepository } from \"../indexer/watch.js\";\nimport { generateGraphifyGraph } from \"../integrations/graphify.js\";\nimport { refreshZvecIndex, type ZvecRefresh } from \"../retrieval/zvec.js\";\n\nexport type ProjectSyncOptions = {\n repositoryRoot: string;\n graphify?: { enabled: boolean; graphPath?: string | undefined };\n logger?: Pick<Console, \"info\" | \"error\">;\n /**\n * Called (coalesced) when a managed Markdown memory file changes. The owner\n * of the database runtime re-imports memory; sync itself never needs it.\n */\n onMemoryChange?: () => Promise<void>;\n};\n\nexport type ProjectSyncHandle = {\n ready: Promise<void>;\n close(): Promise<void>;\n};\n\n/**\n * Owns all derived project state. A running MCP server should never require a\n * user or an agent to remember `index`, `graphify`, or `reindex` commands.\n */\nexport function startProjectSync(options: ProjectSyncOptions): ProjectSyncHandle {\n const repositoryRoot = resolve(options.repositoryRoot);\n const logger = options.logger ?? console;\n const controller = new AbortController();\n let watcher: Promise<void> | null = null;\n let closing = false;\n let memoryWatcher: FSWatcher | null = null;\n let memoryRefresh: ReturnType<typeof createCoalescer> | null = null;\n\n /** Run a derived-state refresh in the background, coalescing overlapping requests. */\n const backgroundRefresh = (task: () => Promise<void>) => {\n let current: Promise<void> | null = null;\n let pending = false;\n const schedule = () => {\n if (closing) return;\n if (current) {\n pending = true;\n return;\n }\n current = (async () => {\n do {\n pending = false;\n await task();\n } while (pending && !closing);\n })().finally(() => {\n current = null;\n if (pending && !closing) schedule();\n });\n };\n return { schedule, settled: async () => current?.catch(() => undefined) };\n };\n\n const graphRefresh = backgroundRefresh(() =>\n refreshGraph().catch((error) =>\n logger.error(`[sync] Graphify refresh failed: ${(error as Error).message}`),\n ),\n );\n // Queries never refresh zg on the prompt path (`--refresh off`); this keeps\n // the zg index incrementally current instead, and is a no-op unless enabled.\n // Closing aborts a running `zg index` rather than waiting it out.\n const zvecRefresh = backgroundRefresh(async () => {\n const result = await refreshZvecIndex(repositoryRoot, { signal: controller.signal }).catch(\n (error): ZvecRefresh => ({\n status: \"failed\",\n reason: (error as Error).message,\n }),\n );\n if (result.status !== \"disabled\") logger.info(`[sync] ${result.reason}`);\n });\n\n const ready = (async () => {\n // The index is the useful fallback if Graphify is unavailable in a\n // pre-made repository. Markdown memory is imported by the database\n // runtime when (and whenever) PostgreSQL connects.\n await indexRepository({ repositoryRoot });\n zvecRefresh.schedule();\n watchMemory();\n if (options.graphify?.enabled) {\n await refreshGraph().catch((error) =>\n logger.error(`[sync] Graphify startup refresh failed: ${(error as Error).message}`),\n );\n }\n\n watcher = watchRepository({\n repositoryRoot,\n signal: controller.signal,\n ...(options.graphify?.enabled\n ? {\n ignoredPaths: [\n options.graphify.graphPath\n ? relative(repositoryRoot, resolve(repositoryRoot, options.graphify.graphPath))\n .split(sep)\n .join(\"/\")\n : \"graphify-out\",\n ],\n }\n : {}),\n onIndex: (result) => {\n if (result.added + result.changed + result.removed === 0) return;\n zvecRefresh.schedule();\n if (options.graphify?.enabled) graphRefresh.schedule();\n },\n onError: (error) => logger.error(`[sync] repository watch failed: ${error.message}`),\n });\n // watchRepository performs its own initial index. It is intentionally\n // detached after the awaited startup pass so MCP can accept requests.\n // A platform without recursive fs.watch still gets a healthy MCP server\n // and the completed startup index.\n void watcher.catch((error) =>\n logger.error(`[sync] live repository watch unavailable: ${(error as Error).message}`),\n );\n })().catch((error) => {\n logger.error(`[sync] startup synchronization failed: ${(error as Error).message}`);\n throw error;\n });\n\n // The repository watcher ignores `.codebuddy/`; Markdown memory is the\n // source of truth, so hand edits and deletions are re-imported live.\n function watchMemory(): void {\n const onMemoryChange = options.onMemoryChange;\n if (closing || !onMemoryChange) return;\n try {\n memoryRefresh = createCoalescer(\n () =>\n onMemoryChange().catch((error) =>\n logger.error(`[sync] memory reindex failed: ${(error as Error).message}`),\n ),\n 300,\n );\n memoryWatcher = fsWatch(\n join(repositoryRoot, \".codebuddy\", \"memory\"),\n { recursive: true },\n (_event, filename) => {\n const name = filename?.replace(/\\\\/g, \"/\") ?? \"\";\n if (/^(facts\\/fact|summaries\\/sum)_[a-z0-9]+\\.md$/.test(name)) memoryRefresh?.trigger();\n },\n );\n memoryWatcher.on(\"error\", () => memoryWatcher?.close());\n } catch (error) {\n logger.error(`[sync] live memory watch unavailable: ${(error as Error).message}`);\n }\n }\n\n async function refreshGraph(): Promise<void> {\n const result = await generateGraphifyGraph({\n repositoryRoot,\n ...(options.graphify?.graphPath ? { graphPath: options.graphify.graphPath } : {}),\n });\n logger.info(`[sync] Graphify updated (${result.nodes} nodes, ${result.edges} edges)`);\n }\n\n return {\n ready,\n async close() {\n closing = true;\n controller.abort();\n memoryRefresh?.stop();\n memoryWatcher?.close();\n await watcher?.catch(() => undefined);\n await Promise.all([graphRefresh.settled(), zvecRefresh.settled()]);\n },\n };\n}\n","import { watch as fsWatch } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { type IndexOptions, indexRepository } from \"./engine.js\";\nimport type { IndexResult } from \"./types.js\";\n\n/**\n * `codebuddy watch` is an explicit, foreground incremental indexer — not a\n * hidden daemon (Proposal 04.3 non-goal). It coalesces bursts of file-save\n * events into a single index pass so a formatter rewriting fifty files triggers\n * one refresh, not fifty.\n */\n\nconst DEFAULT_DEBOUNCE_MS = 300;\n\n/**\n * A trailing debouncer: `trigger()` schedules `fn` to run after `delayMs` of\n * quiet, collapsing any calls made in that window into one. Runs are\n * serialized. A trigger marks the work dirty; a run clears the mark when it\n * starts, so an event that lands while a pass is in flight is never lost — the\n * pass reschedules itself when it settles. `stop()` cancels pending work and\n * resolves once any in-flight pass has finished. Exposed on its own so the\n * coalescing logic is testable without touching the filesystem.\n */\nexport function createCoalescer(\n fn: () => Promise<void>,\n delayMs: number,\n): { trigger: () => void; stop: () => Promise<void> } {\n let timer: NodeJS.Timeout | null = null;\n let running: Promise<void> | null = null;\n let dirty = false;\n let stopped = false;\n // Trailing debounce alone starves under sustained event streams (every new\n // event resets the window). maxWait forces a run this long after the FIRST\n // deferred trigger, so continuous activity still indexes eventually.\n let firstTriggerAt: number | null = null;\n let maxWaitTimer: NodeJS.Timeout | null = null;\n\n const clearTimers = () => {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n if (maxWaitTimer) {\n clearTimeout(maxWaitTimer);\n maxWaitTimer = null;\n }\n firstTriggerAt = null;\n };\n\n const run = () => {\n // A busy run keeps `dirty` set; its settle step reschedules.\n if (stopped || running) return;\n clearTimers();\n dirty = false;\n running = (async () => {\n try {\n await fn();\n } finally {\n running = null;\n if (dirty) schedule();\n }\n })();\n };\n\n const schedule = () => {\n if (stopped) return;\n dirty = true;\n if (firstTriggerAt === null) {\n firstTriggerAt = Date.now();\n maxWaitTimer = setTimeout(run, MAX_WAIT_MS);\n }\n if (timer) clearTimeout(timer);\n timer = setTimeout(run, delayMs);\n };\n\n return {\n trigger: schedule,\n stop: async () => {\n stopped = true;\n clearTimers();\n await running?.catch(() => undefined);\n },\n };\n}\n\n/** Hard ceiling on how long a burst of events can defer a run. */\nconst MAX_WAIT_MS = 5_000;\n\nexport type WatchOptions = IndexOptions & {\n debounceMs?: number;\n ignoredPaths?: string[];\n onIndex?: (result: IndexResult) => void;\n onError?: (error: Error) => void;\n /** Abort to stop the watcher (e.g. wired to SIGINT). */\n signal?: AbortSignal;\n};\n\n/** Ignore churn we cause ourselves or that git would ignore. */\nexport function isIgnoredEvent(filename: string | null, ignoredPaths: string[] = []): boolean {\n if (!filename) return false;\n const normalized = filename.replace(/\\\\/g, \"/\");\n return (\n ignoredPaths.some((path) => normalized === path || normalized.startsWith(`${path}/`)) ||\n normalized.startsWith(\".codebuddy/\") ||\n normalized.includes(\"/.git/\") ||\n normalized.startsWith(\".git/\") ||\n normalized.includes(\"/node_modules/\") ||\n normalized.startsWith(\"node_modules/\") ||\n normalized.endsWith(\".tmp\")\n );\n}\n\n/**\n * Watch `repositoryRoot` recursively and run an incremental index on change.\n * Resolves when the signal aborts. Does an initial index pass on start so the\n * manifest is fresh before the first edit.\n */\nexport async function watchRepository(options: WatchOptions = {}): Promise<void> {\n const repositoryRoot = resolve(options.repositoryRoot ?? process.cwd());\n const debounceMs = options.debounceMs ?? DEFAULT_DEBOUNCE_MS;\n\n const runIndex = async () => {\n try {\n const result = await indexRepository({\n repositoryRoot,\n ...(options.now ? { now: options.now } : {}),\n });\n options.onIndex?.(result);\n } catch (error) {\n options.onError?.(error as Error);\n }\n };\n\n await runIndex();\n\n const coalescer = createCoalescer(runIndex, debounceMs);\n const watcher = fsWatch(repositoryRoot, { recursive: true }, (_event, filename) => {\n if (isIgnoredEvent(filename, options.ignoredPaths)) return;\n coalescer.trigger();\n });\n\n return new Promise<void>((resolvePromise) => {\n const stop = () => {\n watcher.close();\n // Resolve only after an in-flight pass settles, so callers that stop the\n // watcher never race a manifest write.\n void coalescer.stop().then(() => resolvePromise());\n };\n if (options.signal) {\n if (options.signal.aborted) return stop();\n options.signal.addEventListener(\"abort\", stop, { once: true });\n }\n watcher.on(\"error\", () => stop());\n });\n}\n","import { realpath, stat } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { isAbsolute, parse, resolve } from \"node:path\";\nimport { configPath } from \"./config-file.js\";\n\n/** Resolve a server's project before any indexing, watching, or database work. */\nexport async function resolveInitializedProjectRoot(\n configuredRoot?: string,\n cwd = process.cwd(),\n): Promise<string> {\n if (configuredRoot !== undefined && !isAbsolute(configuredRoot)) {\n throw new Error(\"CODEBUDDY_PROJECT_ROOT must be an absolute path to an initialized project.\");\n }\n const selected = resolve(configuredRoot ?? cwd);\n const root = await realpath(selected);\n if (root === parse(root).root || root === (await realpath(homedir()))) {\n throw new Error(\n \"Select an initialized project folder, not the filesystem root or home folder.\",\n );\n }\n const initialized = await stat(configPath(root)).then(\n (info) => info.isFile(),\n () => false,\n );\n if (!initialized) {\n throw new Error(\n `CodeBuddy will not scan ${root}: run codebuddy use (or codebuddy init) in your project folder first. ` +\n \"For a desktop client, re-register CodeBuddy from that folder to save its project path.\",\n );\n }\n return root;\n}\n","import type { CodeBuddy } from \"../core/codebuddy.js\";\nimport { redactSecrets } from \"../core/config-file.js\";\nimport type { RuntimeHandle } from \"../core/operations.js\";\nimport type { MemoryRepository } from \"../core/repository.js\";\n\nexport type DatabaseServices = { memory: CodeBuddy; repository: MemoryRepository };\n\nexport type DatabaseStatus = {\n available: boolean;\n state: \"idle\" | \"connecting\" | \"ready\" | \"unavailable\";\n error?: string;\n retryInMs?: number;\n};\n\nexport type LazyDatabase = {\n /** Resolve the runtime, connecting (or retrying after the cooldown) on demand. */\n get(): Promise<DatabaseServices>;\n status(): DatabaseStatus;\n close(): Promise<void>;\n};\n\n/**\n * Owns the optional PostgreSQL runtime for the MCP server so file-based tools\n * never wait on it. One connection attempt runs at a time; a failure is cached\n * for `retryMs` so database tools answer fast while Postgres is down, and the\n * first call after the cooldown retries. Once connected, the runtime (and its\n * embedding worker) is created exactly once.\n */\nexport function createLazyDatabase(\n connect: () => Promise<RuntimeHandle>,\n options: { retryMs?: number } = {},\n): LazyDatabase {\n const retryMs = options.retryMs ?? 5_000;\n let runtime: RuntimeHandle | undefined;\n let pending: Promise<RuntimeHandle> | undefined;\n let failure: { error: string; at: number } | undefined;\n let closed = false;\n\n return {\n get() {\n if (runtime) return Promise.resolve(runtime);\n if (closed) return Promise.reject(new Error(\"CodeBuddy MCP server is shutting down.\"));\n if (pending) return pending;\n if (failure && Date.now() - failure.at < retryMs) {\n return Promise.reject(new Error(failure.error));\n }\n pending = connect()\n .then(\n async (handle) => {\n if (closed) {\n await handle.close();\n throw new Error(\"CodeBuddy MCP server is shutting down.\");\n }\n runtime = handle;\n failure = undefined;\n return handle;\n },\n (error: unknown) => {\n failure = { error: describeError(error), at: Date.now() };\n throw error;\n },\n )\n .finally(() => {\n pending = undefined;\n });\n return pending;\n },\n status() {\n if (runtime) return { available: true, state: \"ready\" };\n if (pending) return { available: false, state: \"connecting\" };\n if (!failure) return { available: false, state: \"idle\" };\n return {\n available: false,\n state: \"unavailable\",\n error: failure.error,\n retryInMs: Math.max(0, failure.at + retryMs - Date.now()),\n };\n },\n async close() {\n closed = true;\n await pending?.catch(() => undefined);\n const current = runtime;\n runtime = undefined;\n await current?.close();\n },\n };\n}\n\n/** Bounded, credential-free error text (connection errors can echo the URL). */\nfunction describeError(error: unknown): string {\n const err = error as { message?: string; code?: string } | undefined;\n const text = err?.message || err?.code || String(error);\n return String(redactSecrets(text)).slice(0, 300);\n}\n"],"mappings":"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","names":["createHash","createHash","fsWatch","join","resolve","resolve","resolve","resolve","fsWatch","join","resolve","resolve"]}
|