@orquentra/codebuddy 2.15.0 → 2.15.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/ARCHITECTURE.md +12 -7
  2. package/README.md +2 -2
  3. package/dist/{chunk-YMXFSHXT.js → chunk-3L5IR6OW.js} +2 -2
  4. package/dist/{chunk-YMXFSHXT.js.map → chunk-3L5IR6OW.js.map} +1 -1
  5. package/dist/{chunk-JJQDVGF7.js → chunk-3TZNQJTH.js} +3 -3
  6. package/dist/{chunk-4BSO3HST.js → chunk-4LMBZWIF.js} +48 -13
  7. package/dist/chunk-4LMBZWIF.js.map +1 -0
  8. package/dist/{chunk-WHKXUXHE.js → chunk-4TKKLQLH.js} +5 -5
  9. package/dist/chunk-4TKKLQLH.js.map +1 -0
  10. package/dist/{chunk-KQCP3ZYH.js → chunk-5ZLNHYJH.js} +31 -15
  11. package/dist/chunk-5ZLNHYJH.js.map +1 -0
  12. package/dist/{chunk-U7KDM62P.js → chunk-6KSSJJZO.js} +17 -9
  13. package/dist/chunk-6KSSJJZO.js.map +1 -0
  14. package/dist/{chunk-6YI2D5F5.js → chunk-7IXPARDM.js} +3 -3
  15. package/dist/{chunk-UTPC66MV.js → chunk-FEFOZ2ZW.js} +23 -4
  16. package/dist/chunk-FEFOZ2ZW.js.map +1 -0
  17. package/dist/{chunk-ITXE27SZ.js → chunk-FGJ2KPPN.js} +104 -22
  18. package/dist/chunk-FGJ2KPPN.js.map +1 -0
  19. package/dist/{chunk-D5E7SGI5.js → chunk-KGDRGA2L.js} +14 -14
  20. package/dist/{chunk-D5E7SGI5.js.map → chunk-KGDRGA2L.js.map} +1 -1
  21. package/dist/{chunk-UR3DF74I.js → chunk-LZTYPJ5I.js} +36 -18
  22. package/dist/chunk-LZTYPJ5I.js.map +1 -0
  23. package/dist/{chunk-UATJCD2T.js → chunk-R5E2YGLE.js} +14 -8
  24. package/dist/chunk-R5E2YGLE.js.map +1 -0
  25. package/dist/chunk-R5JRKMEP.js +55 -0
  26. package/dist/chunk-R5JRKMEP.js.map +1 -0
  27. package/dist/{chunk-RZ266NHE.js → chunk-SPQU2422.js} +5 -5
  28. package/dist/{chunk-RZ266NHE.js.map → chunk-SPQU2422.js.map} +1 -1
  29. package/dist/{chunk-BTTQWSDS.js → chunk-SXVH74NE.js} +36 -6
  30. package/dist/chunk-SXVH74NE.js.map +1 -0
  31. package/dist/{chunk-KYRM3HPP.js → chunk-TIMS3PB7.js} +3 -3
  32. package/dist/{chunk-M75SN4D4.js → chunk-Y4QQML24.js} +3 -3
  33. package/dist/{chunk-M75SN4D4.js.map → chunk-Y4QQML24.js.map} +1 -1
  34. package/dist/cli/index.js +4 -4
  35. package/dist/{config-TXQJCCVI.js → config-32X53LWY.js} +4 -4
  36. package/dist/{expand-KWYGNCKZ.js → expand-VFYOWNZS.js} +5 -5
  37. package/dist/{file-vectors-MKXTSGGH.js → file-vectors-UBOKGYXX.js} +4 -3
  38. package/dist/{index-facts-4MTEHKMN.js → index-facts-ZTUANWBF.js} +7 -7
  39. package/dist/index-pass-S6NC25P5.js +15 -0
  40. package/dist/index.js +9 -8
  41. package/dist/{install-E3JWINTN.js → install-ZTE3H7QM.js} +8 -7
  42. package/dist/{lean-MLTAFS5L.js → lean-5N6BOONS.js} +5 -4
  43. package/dist/{local-75DJECVR.js → local-5EYLEBEP.js} +4 -2
  44. package/dist/{local-llm-MECVJTVU.js → local-llm-ESMVP5LE.js} +4 -4
  45. package/dist/{main-RLMKQZW4.js → main-B3HZCGVE.js} +61 -50
  46. package/dist/main-B3HZCGVE.js.map +1 -0
  47. package/dist/{memory-PFFVMTCH.js → memory-CI7HIGHN.js} +5 -5
  48. package/dist/{operations-STR7S5UO.js → operations-MW52HIQ3.js} +4 -4
  49. package/dist/{rerank-XXKWDI5Q.js → rerank-O4S5BC45.js} +5 -5
  50. package/dist/{runtime-SWFWGTQ4.js → runtime-SZAWIXEB.js} +6 -5
  51. package/dist/{summaries-QXWFFDUZ.js → summaries-GCHZGIIA.js} +11 -8
  52. package/dist/{zvec-F73IBWO5.js → zvec-V34P24WF.js} +11 -6
  53. package/package.json +1 -1
  54. package/dist/chunk-4BSO3HST.js.map +0 -1
  55. package/dist/chunk-BTTQWSDS.js.map +0 -1
  56. package/dist/chunk-ITXE27SZ.js.map +0 -1
  57. package/dist/chunk-KQCP3ZYH.js.map +0 -1
  58. package/dist/chunk-U7KDM62P.js.map +0 -1
  59. package/dist/chunk-UATJCD2T.js.map +0 -1
  60. package/dist/chunk-UR3DF74I.js.map +0 -1
  61. package/dist/chunk-UTPC66MV.js.map +0 -1
  62. package/dist/chunk-WHKXUXHE.js.map +0 -1
  63. package/dist/index-pass-TBDUEZCB.js +0 -14
  64. package/dist/main-RLMKQZW4.js.map +0 -1
  65. /package/dist/{chunk-JJQDVGF7.js.map → chunk-3TZNQJTH.js.map} +0 -0
  66. /package/dist/{chunk-6YI2D5F5.js.map → chunk-7IXPARDM.js.map} +0 -0
  67. /package/dist/{chunk-KYRM3HPP.js.map → chunk-TIMS3PB7.js.map} +0 -0
  68. /package/dist/{config-TXQJCCVI.js.map → config-32X53LWY.js.map} +0 -0
  69. /package/dist/{expand-KWYGNCKZ.js.map → expand-VFYOWNZS.js.map} +0 -0
  70. /package/dist/{file-vectors-MKXTSGGH.js.map → file-vectors-UBOKGYXX.js.map} +0 -0
  71. /package/dist/{index-facts-4MTEHKMN.js.map → index-facts-ZTUANWBF.js.map} +0 -0
  72. /package/dist/{index-pass-TBDUEZCB.js.map → index-pass-S6NC25P5.js.map} +0 -0
  73. /package/dist/{install-E3JWINTN.js.map → install-ZTE3H7QM.js.map} +0 -0
  74. /package/dist/{lean-MLTAFS5L.js.map → lean-5N6BOONS.js.map} +0 -0
  75. /package/dist/{local-75DJECVR.js.map → local-5EYLEBEP.js.map} +0 -0
  76. /package/dist/{local-llm-MECVJTVU.js.map → local-llm-ESMVP5LE.js.map} +0 -0
  77. /package/dist/{memory-PFFVMTCH.js.map → memory-CI7HIGHN.js.map} +0 -0
  78. /package/dist/{operations-STR7S5UO.js.map → operations-MW52HIQ3.js.map} +0 -0
  79. /package/dist/{rerank-XXKWDI5Q.js.map → rerank-O4S5BC45.js.map} +0 -0
  80. /package/dist/{runtime-SWFWGTQ4.js.map → runtime-SZAWIXEB.js.map} +0 -0
  81. /package/dist/{summaries-QXWFFDUZ.js.map → summaries-GCHZGIIA.js.map} +0 -0
  82. /package/dist/{zvec-F73IBWO5.js.map → zvec-V34P24WF.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  chatJson
3
- } from "./chunk-6YI2D5F5.js";
3
+ } from "./chunk-7IXPARDM.js";
4
4
  import {
5
5
  memoryCandidateSchema
6
6
  } from "./chunk-DFV2SK6C.js";
@@ -9,10 +9,10 @@ import {
9
9
  } from "./chunk-AL6VBTNA.js";
10
10
  import {
11
11
  resolveLlmConfig
12
- } from "./chunk-WHKXUXHE.js";
13
- import "./chunk-M75SN4D4.js";
12
+ } from "./chunk-4TKKLQLH.js";
13
+ import "./chunk-Y4QQML24.js";
14
14
  import "./chunk-CG5B3LYG.js";
15
- import "./chunk-BTTQWSDS.js";
15
+ import "./chunk-SXVH74NE.js";
16
16
  import "./chunk-3QGW53GD.js";
17
17
  import "./chunk-HPOR6YXA.js";
18
18
  import "./chunk-PZ5AY32C.js";
@@ -58,4 +58,4 @@ export {
58
58
  createLlmMemoryExtractor,
59
59
  llmCaptureOptions
60
60
  };
61
- //# sourceMappingURL=memory-PFFVMTCH.js.map
61
+ //# sourceMappingURL=memory-CI7HIGHN.js.map
@@ -5,13 +5,13 @@ import {
5
5
  inspectNamespace,
6
6
  listFactsPage,
7
7
  runDoctor
8
- } from "./chunk-KYRM3HPP.js";
8
+ } from "./chunk-TIMS3PB7.js";
9
9
  import "./chunk-57642NGU.js";
10
- import "./chunk-YMXFSHXT.js";
10
+ import "./chunk-3L5IR6OW.js";
11
11
  import "./chunk-EMWGLKPR.js";
12
12
  import "./chunk-AL6VBTNA.js";
13
13
  import "./chunk-6JCKMIQY.js";
14
- import "./chunk-M75SN4D4.js";
14
+ import "./chunk-Y4QQML24.js";
15
15
  import "./chunk-CG5B3LYG.js";
16
16
  import "./chunk-3QGW53GD.js";
17
17
  import "./chunk-HPOR6YXA.js";
@@ -24,4 +24,4 @@ export {
24
24
  listFactsPage,
25
25
  runDoctor
26
26
  };
27
- //# sourceMappingURL=operations-STR7S5UO.js.map
27
+ //# sourceMappingURL=operations-MW52HIQ3.js.map
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  chatJson
3
- } from "./chunk-6YI2D5F5.js";
3
+ } from "./chunk-7IXPARDM.js";
4
4
  import {
5
5
  redactSensitive
6
6
  } from "./chunk-AL6VBTNA.js";
7
- import "./chunk-WHKXUXHE.js";
8
- import "./chunk-M75SN4D4.js";
7
+ import "./chunk-4TKKLQLH.js";
8
+ import "./chunk-Y4QQML24.js";
9
9
  import "./chunk-CG5B3LYG.js";
10
- import "./chunk-BTTQWSDS.js";
10
+ import "./chunk-SXVH74NE.js";
11
11
  import "./chunk-3QGW53GD.js";
12
12
  import "./chunk-HPOR6YXA.js";
13
13
  import "./chunk-PZ5AY32C.js";
@@ -61,4 +61,4 @@ export {
61
61
  RERANK_PROMPT_VERSION,
62
62
  rerankCandidates
63
63
  };
64
- //# sourceMappingURL=rerank-XXKWDI5Q.js.map
64
+ //# sourceMappingURL=rerank-O4S5BC45.js.map
@@ -17,19 +17,20 @@ import {
17
17
  turnUserPrompts,
18
18
  turnVerification,
19
19
  verificationLines
20
- } from "./chunk-KQCP3ZYH.js";
20
+ } from "./chunk-5ZLNHYJH.js";
21
21
  import "./chunk-YN7TEIUH.js";
22
22
  import "./chunk-7IPSDDO5.js";
23
23
  import "./chunk-TGFTKWOR.js";
24
24
  import "./chunk-GQKWUHMA.js";
25
25
  import "./chunk-DFV2SK6C.js";
26
- import "./chunk-UR3DF74I.js";
26
+ import "./chunk-LZTYPJ5I.js";
27
27
  import "./chunk-Y7D7PUBE.js";
28
+ import "./chunk-R5JRKMEP.js";
28
29
  import "./chunk-57642NGU.js";
29
- import "./chunk-YMXFSHXT.js";
30
+ import "./chunk-3L5IR6OW.js";
30
31
  import "./chunk-EMWGLKPR.js";
31
32
  import "./chunk-AL6VBTNA.js";
32
- import "./chunk-M75SN4D4.js";
33
+ import "./chunk-Y4QQML24.js";
33
34
  import "./chunk-CG5B3LYG.js";
34
35
  import "./chunk-HPOR6YXA.js";
35
36
  import "./chunk-PZ5AY32C.js";
@@ -53,4 +54,4 @@ export {
53
54
  turnVerification,
54
55
  verificationLines
55
56
  };
56
- //# sourceMappingURL=runtime-SWFWGTQ4.js.map
57
+ //# sourceMappingURL=runtime-SZAWIXEB.js.map
@@ -1,26 +1,29 @@
1
1
  import {
2
2
  SUMMARY_PROMPT_VERSION,
3
+ brokenCardText,
3
4
  cardEligible,
4
5
  cardProgress,
5
6
  cardText,
6
7
  cardWords,
7
8
  loadLlmSummaries,
8
9
  summarizeRepository
9
- } from "./chunk-ITXE27SZ.js";
10
- import "./chunk-6YI2D5F5.js";
11
- import "./chunk-4BSO3HST.js";
12
- import "./chunk-UR3DF74I.js";
10
+ } from "./chunk-FGJ2KPPN.js";
11
+ import "./chunk-7IXPARDM.js";
12
+ import "./chunk-4LMBZWIF.js";
13
+ import "./chunk-LZTYPJ5I.js";
13
14
  import "./chunk-Y7D7PUBE.js";
15
+ import "./chunk-R5JRKMEP.js";
14
16
  import "./chunk-AL6VBTNA.js";
15
- import "./chunk-WHKXUXHE.js";
16
- import "./chunk-M75SN4D4.js";
17
+ import "./chunk-4TKKLQLH.js";
18
+ import "./chunk-Y4QQML24.js";
17
19
  import "./chunk-CG5B3LYG.js";
18
- import "./chunk-BTTQWSDS.js";
20
+ import "./chunk-SXVH74NE.js";
19
21
  import "./chunk-3QGW53GD.js";
20
22
  import "./chunk-HPOR6YXA.js";
21
23
  import "./chunk-PZ5AY32C.js";
22
24
  export {
23
25
  SUMMARY_PROMPT_VERSION,
26
+ brokenCardText,
24
27
  cardEligible,
25
28
  cardProgress,
26
29
  cardText,
@@ -28,4 +31,4 @@ export {
28
31
  loadLlmSummaries,
29
32
  summarizeRepository
30
33
  };
31
- //# sourceMappingURL=summaries-QXWFFDUZ.js.map
34
+ //# sourceMappingURL=summaries-GCHZGIIA.js.map
@@ -1,9 +1,11 @@
1
1
  import {
2
- CARDS_DIR,
3
2
  DEFAULT_CARD_MODEL,
4
3
  DEFAULT_ZVEC_MIN_SCORE,
5
4
  DEFAULT_ZVEC_MODEL,
5
+ LEGACY_CARDS_DIR,
6
+ ZG_MAX_LIMIT,
6
7
  buildZvecIndex,
8
+ cardsDir,
7
9
  ensureZvecServer,
8
10
  parseHits,
9
11
  refreshZvecCardIndex,
@@ -12,20 +14,23 @@ import {
12
14
  zvecCandidates,
13
15
  zvecExcludeGlobs,
14
16
  zvecStatus
15
- } from "./chunk-4BSO3HST.js";
16
- import "./chunk-UR3DF74I.js";
17
+ } from "./chunk-4LMBZWIF.js";
18
+ import "./chunk-LZTYPJ5I.js";
17
19
  import "./chunk-Y7D7PUBE.js";
20
+ import "./chunk-R5JRKMEP.js";
18
21
  import "./chunk-AL6VBTNA.js";
19
- import "./chunk-M75SN4D4.js";
22
+ import "./chunk-Y4QQML24.js";
20
23
  import "./chunk-CG5B3LYG.js";
21
24
  import "./chunk-HPOR6YXA.js";
22
25
  import "./chunk-PZ5AY32C.js";
23
26
  export {
24
- CARDS_DIR,
25
27
  DEFAULT_CARD_MODEL,
26
28
  DEFAULT_ZVEC_MIN_SCORE,
27
29
  DEFAULT_ZVEC_MODEL,
30
+ LEGACY_CARDS_DIR,
31
+ ZG_MAX_LIMIT,
28
32
  buildZvecIndex,
33
+ cardsDir,
29
34
  ensureZvecServer,
30
35
  parseHits,
31
36
  refreshZvecCardIndex,
@@ -35,4 +40,4 @@ export {
35
40
  zvecExcludeGlobs,
36
41
  zvecStatus
37
42
  };
38
- //# sourceMappingURL=zvec-F73IBWO5.js.map
43
+ //# sourceMappingURL=zvec-V34P24WF.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orquentra/codebuddy",
3
- "version": "2.15.0",
3
+ "version": "2.15.3",
4
4
  "description": "CodeBuddy by Orquentra: local-first MCP server and CLI for project context and durable memory in Claude Code, Codex, and other AI coding agents.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/retrieval/zvec.ts"],"sourcesContent":["import { execFile, spawn } from \"node:child_process\";\nimport { mkdir, readFile, rm, stat, writeFile } from \"node:fs/promises\";\nimport { dirname, isAbsolute, join, relative, resolve, sep } from \"node:path\";\nimport { asksForChangelog } from \"../context/discovery.js\";\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\";\n/**\n * Default `zvec.minScore`: a vector-only file must cover this share of the\n * task's IDF mass in its current source. zg 0.2.x prints no similarity (its\n * `--trace` score is 1/(60+rank)), so the floor is CodeBuddy's own measure. 0.3\n * is the content gate's value (`MIN_IDF_COVERAGE`), not calibrated on vector\n * hits: see docs/zvec-floor.md to calibrate it.\n */\nexport const DEFAULT_ZVEC_MIN_SCORE = 0.3;\nconst DOC_GLOBS = [\"!**/*.md\", \"!**/*.mdx\", \"!**/*.rst\", \"!**/*.adoc\", \"!docs/**\"];\n/** zg globs are case-sensitive: the usual spellings of changelogs and release notes. */\nconst CHANGELOG_GLOBS = [\n \"!**/CHANGES*\",\n \"!**/CHANGELOG*\",\n \"!**/HISTORY*\",\n \"!**/NEWS*\",\n \"!**/RELEASE*\",\n \"!**/changelog*\",\n \"!**/Changelog*\",\n \"!**/ChangeLog*\",\n];\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 ...(asksForChangelog(task) ? [] : CHANGELOG_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 | \"no-server\"\n /** Another zg process (a background `zg index`) holds the index lock. */\n | \"busy\";\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 /** `zvec.minScore` for vector-only files; set on every `ok` result. */\n minScore?: number;\n /**\n * Source paths whose intent card (`.codebuddy/cache/cards/<path>.md`, written\n * by the card pass) matched the task in the card workspace, best first.\n * Paths only: a card's line ranges say nothing about its source file.\n */\n cardPaths?: string[];\n};\n\nexport async function zvecCandidates(\n repositoryRoot: string,\n task: string,\n options: {\n limit?: number;\n /**\n * Hard stop (epoch ms) for every zg call: each one is killed at the\n * deadline and the caller falls back to keyword ranking. A miss is not\n * cached, so a slow prompt does not turn zvec off for the next one.\n */\n deadlineAt?: number;\n /** Query only while a `zg server` is running (checked at most every 30 s). */\n requireServer?: boolean;\n } = {},\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 { deadlineAt } = options;\n const run = (args: string[], cwd = root): Promise<RunResult> => {\n const ms =\n deadlineAt === undefined\n ? config.timeoutMs\n : Math.min(config.timeoutMs, deadlineAt - Date.now());\n if (ms <= 0) return Promise.resolve({ stdout: \"\", stderr: \"\", error: deadlineError() });\n return runZg(config.binary, args, cwd, ms);\n };\n const missedDeadline = (status: ZvecStatus) =>\n deadlineAt !== undefined && status === \"timeout\" && Date.now() >= deadlineAt - 5;\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 if (!(readiness && missedDeadline(readiness.status))) {\n await markReadiness(root, config.binary, readiness);\n }\n if (readiness) return readiness;\n }\n if (options.requireServer && !(await serverRunning(root, config.binary, run))) {\n return none(\"no-server\", \"no running `zg server` (this path never starts one)\");\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 query = [\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 // The card workspace is queried in parallel; it never delays or fails the code query.\n const cardQuery = queryCards(root, task, limit, (args, cwd) => run(args, cwd));\n let result = await run(query);\n // A background `zg index` (project sync) holds the lock for a moment: one retry.\n if (\n result.error &&\n isBusy(result) &&\n (deadlineAt === undefined || deadlineAt - Date.now() > 400)\n ) {\n await new Promise((resolveWait) => setTimeout(resolveWait, BUSY_RETRY_MS));\n result = await run(query);\n }\n if (result.error) {\n const failure = fromError(result.error, \"zvec-grep query\", result.stderr);\n if (!missedDeadline(failure.status) && failure.status !== \"busy\") await resetZvecCache(root);\n return failure;\n }\n\n const hits = parseHits(result.stdout).slice(0, chunkLimit);\n const cardPaths = await cardQuery;\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 minScore: config.minScore,\n ...(cardPaths.length > 0 ? { cardPaths } : {}),\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 (cardPaths.length > 0 ? `; ${cardPaths.length} intent card match(es)` : \"\") +\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) {\n // zg's index is binary and rebuildable: keep it out of git (and so out of\n // the uncommitted-file targets) without touching the project's .gitignore.\n await writeFile(join(root, \".zvec-grep\", \".gitignore\"), \"*\\n\", { flag: \"wx\" }).catch(() => {});\n await markIndexed(root, startedAt);\n }\n return code;\n}\n\nconst NOT_READY = \"zvec-grep index missing or not ready; run `codebuddy zvec index`\";\n\n/**\n * Intent cards live in their own zg workspace (`.codebuddy/cache/cards/`, one\n * Markdown file per source file at `<path>.md`), so the project's ignore rules\n * never hide them and the code index never sees them. Cards are plain English,\n * so they get an English sentence model instead of the code model.\n */\nexport const CARDS_DIR = join(\".codebuddy\", \"cache\", \"cards\");\nexport const DEFAULT_CARD_MODEL = \"local/bge-small-en-v1.5\";\n\n/** Source paths whose card matched the task; empty when there is no card index or it fails. */\nasync function queryCards(\n root: string,\n task: string,\n limit: number,\n run: (args: string[], cwd: string) => Promise<RunResult>,\n): Promise<string[]> {\n const cards = join(root, CARDS_DIR);\n if (!(await stat(join(cards, \".zvec-grep\")).catch(() => null))) return [];\n const result = await run(\n [\"query\", \"--vector\", task, \"--mode\", \"auto\", \"--refresh\", \"off\", \"--limit\", String(limit)],\n cards,\n );\n if (result.error) return [];\n const paths: string[] = [];\n for (const hit of parseHits(result.stdout)) {\n const card = toRepoRelative(cards, hit.path);\n if (!card?.endsWith(\".md\")) continue;\n const path = card.slice(0, -3);\n if (paths.includes(path)) continue;\n if (!(await stat(resolve(root, path)).catch(() => null))?.isFile()) continue;\n paths.push(path);\n }\n return paths.slice(0, limit);\n}\n\n/**\n * Build or refresh the card workspace's zg index (background only: after a\n * card pass). No-op without cards or with zvec off; never throws.\n */\nexport async function refreshZvecCardIndex(\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 cards = join(root, CARDS_DIR);\n if (!(await stat(cards).catch(() => null)))\n return { status: \"ok\", reason: \"no intent cards yet\" };\n const exists = await stat(join(cards, \".zvec-grep\")).catch(() => null);\n const result = await runZg(\n config.binary,\n [\n \"index\",\n cards,\n ...(exists ? [] : [\"--embedding\", config.cardModel]),\n \"--no-ignore\",\n \"--mode\",\n \"auto\",\n ],\n cards,\n REFRESH_TIMEOUT_MS,\n options.signal,\n );\n if (result.error) {\n const failure = fromError(result.error, \"zvec-grep card index\", result.stderr);\n return { status: failure.status, reason: failure.reason };\n }\n await writeFile(join(cards, \".zvec-grep\", \".gitignore\"), \"*\\n\", { flag: \"wx\" }).catch(() => {});\n return { status: \"ok\", reason: \"zvec-grep card index refreshed\" };\n}\n\n/**\n * Start the shared `zg server` for this project when it is enabled and ready,\n * so the prompt hook (which only queries a running server) gets vector\n * candidates within its deadline. Returns a stop function that stops the\n * server only if this call started it.\n */\nexport async function ensureZvecServer(repositoryRoot: string): Promise<{\n started: boolean;\n reason: string;\n stop: () => Promise<void>;\n}> {\n const root = resolve(repositoryRoot);\n const config = await zvecConfig(root);\n const noop = async () => {};\n if (!config.enabled) return { started: false, reason: \"zvec-grep not enabled\", stop: noop };\n const run = (args: string[]) =>\n runZg(config.binary, args, root, Math.max(config.timeoutMs, 5_000));\n const readiness = await checkReadiness(run);\n if (readiness) return { started: false, reason: readiness.reason, stop: noop };\n const status = await run([\"server\", \"status\"]);\n if (!status.error && /^Server: ready\\b/m.test(status.stdout)) {\n return { started: false, reason: \"zg server already running (reused)\", stop: noop };\n }\n const on = await run([\"server\", \"on\"]);\n // The hook's 30 s server-status cache would otherwise keep saying \"stopped\".\n await rm(serverPath(root), { force: true });\n if (on.error) {\n return {\n started: false,\n reason: fromError(on.error, \"zg server on\", on.stderr).reason,\n stop: noop,\n };\n }\n return {\n started: true,\n reason: \"zg server started\",\n // ponytail: the daemon is shared; a second `codebuddy serve` that reused it\n // loses vectors when this one exits. Refcount via ZVEC_GREP_HOME if that matters.\n stop: async () => {\n await runZg(config.binary, [\"server\", \"off\"], root, 5_000);\n await rm(serverPath(root), { force: true });\n },\n };\n}\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(root: string): Promise<{\n enabled: boolean;\n binary: string;\n timeoutMs: number;\n minScore: number;\n cardModel: string;\n}> {\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 minScore: file.zvec?.minScore ?? DEFAULT_ZVEC_MIN_SCORE,\n cardModel: file.zvec?.cardModel ?? DEFAULT_CARD_MODEL,\n };\n}\n\ntype RunResult = {\n stdout: string;\n stderr: 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, stderr) => {\n if (error && (error as { killed?: boolean }).killed) {\n resolveRun({\n stdout: \"\",\n stderr: String(stderr),\n error: Object.assign(error, { code: \"ETIMEDOUT\" }),\n });\n return;\n }\n resolveRun({ stdout: String(stdout), stderr: String(stderr), error });\n },\n );\n });\n}\n\nfunction deadlineError(): NonNullable<RunResult[\"error\"]> {\n return Object.assign(new Error(\"hook deadline reached\"), { code: \"ETIMEDOUT\" });\n}\n\nfunction serverPath(root: string): string {\n return join(root, \".codebuddy\", \"cache\", \"zvec-server.json\");\n}\n\n/** `zg server status` says ready; the answer (either way) is reused for 30 s. */\nasync function serverRunning(\n root: string,\n binary: string,\n run: (args: string[]) => Promise<RunResult>,\n): Promise<boolean> {\n try {\n const cached = JSON.parse(await readFile(serverPath(root), \"utf8\")) as {\n binary?: string;\n checkedAt?: number;\n running?: boolean;\n };\n const age = Date.now() - (cached.checkedAt ?? 0);\n if (cached.binary === binary && age >= 0 && age <= NOT_READY_CACHE_MS) {\n return cached.running === true;\n }\n } catch {\n // No record yet: ask zg.\n }\n const status = await run([\"server\", \"status\"]);\n // A deadline miss says nothing about the server: not cached.\n if (status.error?.code === \"ETIMEDOUT\") return false;\n const running = !status.error && /^Server: ready\\b/m.test(status.stdout);\n try {\n await mkdir(dirname(serverPath(root)), { recursive: true, mode: 0o700 });\n await writeAtomic(\n serverPath(root),\n `${JSON.stringify({ binary, checkedAt: Date.now(), running })}\\n`,\n );\n } catch {\n // An unwritable cache only repeats the check.\n }\n return running;\n}\n\nfunction fromError(\n error: NonNullable<RunResult[\"error\"]>,\n what: string,\n stderr = \"\",\n): 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 if (BUSY.test(stderr)) return none(\"busy\", `${what}: index busy (a zg index refresh is running)`);\n // zg prints `Code: ZVEC_GREP.…` on failure; keep it so the explain line is diagnosable.\n const code = stderr.match(/\\bCode:\\s*(\\S+)/)?.[1];\n return none(\"failed\", `${what} failed${code ? ` (${code})` : \"\"}`);\n}\n\n/** zg's lock error while another process (a background `zg index`) writes the index. */\nconst BUSY = /ZVEC_GREP\\.ENGINE\\.LOCK\\.BUSY/;\nconst BUSY_RETRY_MS = 150;\n\nfunction isBusy(result: RunResult): boolean {\n return BUSY.test(result.stderr);\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":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/llm/local.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { createWriteStream } from \"node:fs\";\nimport { mkdir, rename, rm, stat, statfs, utimes, writeFile } from \"node:fs/promises\";\nimport { availableParallelism, freemem, totalmem } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport { z } from \"zod\";\nimport { modelCacheDir } from \"../providers/local.js\";\nimport type { ChatJsonRequest, LlmResult } from \"./client.js\";\n\n/**\n * Local text generation (Qwen3 only: 0.6B default, 1.7B \"better\") for the small-model features (intent cards, index\n * facts, query expansion, and optionally rerank): a GGUF model run by\n * node-llama-cpp (llama.cpp bindings; prebuilt for macOS arm64/x64, Linux\n * x64/arm64 and Windows x64; Metal/CUDA/Vulkan when present, CPU otherwise).\n * See ARCHITECTURE.md for why node-llama-cpp and not Cactus.\n *\n * Installed by setup (`codebuddy use`, skipped with `--no-local-llm` or\n * `CODEBUDDY_LOCAL_LLM=0`) or `codebuddy models add local-llm`: the runtime with\n * npm, the weights checksum-verified into the shared model cache. Without\n * either, every caller gets `disabled` and keeps 2.14.0 behaviour. Nothing\n * here makes a network call except `downloadLocalModel`.\n *\n * Generation runs on llama.cpp's own native threads (capped at cores − 1),\n * so the MCP event loop keeps answering while a card is written; background\n * passes also yield to foreground work through `waitForForeground`.\n */\n\nexport type LocalModelSize = \"default\" | \"better\";\n\nexport type LocalModelSpec = {\n size: LocalModelSize;\n id: string;\n repo: string;\n revision: string;\n file: string;\n bytes: number;\n sha256: string;\n /** Rough resident memory while loaded, for the free-RAM check. */\n ramBytes: number;\n /** Qwen3 thinks unless told not to; the JSON grammar alone does not stop it. */\n noThink: boolean;\n};\n\nconst GB = 1024 ** 3;\n\n/** Pinned revisions and checksums (Hugging Face LFS sha256). */\nexport const LOCAL_MODELS: Record<LocalModelSize, LocalModelSpec> = {\n default: {\n size: \"default\",\n id: \"qwen3-0.6b-q4_k_m\",\n repo: \"unsloth/Qwen3-0.6B-GGUF\",\n revision: \"50968a4468ef4233ed78cd7c3de230dd1d61a56b\",\n file: \"Qwen3-0.6B-Q4_K_M.gguf\",\n bytes: 396_705_472,\n sha256: \"ac2d97712095a558e31573f62f466a3f9d93990898b0ec79d7c974c1780d524a\",\n ramBytes: 1 * GB,\n noThink: true,\n },\n better: {\n size: \"better\",\n id: \"qwen3-1.7b-q4_k_m\",\n repo: \"unsloth/Qwen3-1.7B-GGUF\",\n revision: \"d7f544eead698dbd1f15126ef60b45a1e1933222\",\n file: \"Qwen3-1.7B-Q4_K_M.gguf\",\n bytes: 1_107_409_472,\n sha256: \"b139949c5bd74937ad8ed8c8cf3d9ffb1e99c866c823204dc42c0d91fa181897\",\n ramBytes: 2.2 * GB,\n noThink: true,\n },\n};\n\n/** Exact runtime version the adapter was tested against. */\nexport const LLAMA_RUNTIME = \"node-llama-cpp@3.22.3\";\n\nexport function localModelPath(spec: LocalModelSpec): string {\n return join(modelCacheDir(), \"gguf\", spec.file);\n}\n\n/** The installed model of `preferred` size, else any installed one (default first). */\nexport async function installedLocalModel(\n preferred?: LocalModelSize,\n): Promise<LocalModelSpec | null> {\n if (process.env.CODEBUDDY_LOCAL_LLM === \"0\") return null;\n const order: LocalModelSize[] = [...(preferred ? [preferred] : []), \"default\", \"better\"];\n for (const size of new Set(order)) {\n const spec = LOCAL_MODELS[size];\n const info = await stat(localModelPath(spec)).catch(() => null);\n if (info?.isFile() && info.size === spec.bytes) return spec;\n }\n return null;\n}\n\n/** Whether the node-llama-cpp runtime can be imported from this install. */\nexport async function localRuntimeAvailable(): Promise<boolean> {\n try {\n await importRuntime();\n return true;\n } catch {\n return false;\n }\n}\n\nexport type DownloadCheck = { ok: true } | { ok: false; reason: string };\n\n/**\n * Whether setup should install the model on its own. A low-end machine (under\n * 4 GB of RAM or a single core) would spend most of a session carding at a few\n * tokens per second; it keeps keyword + vector discovery, and\n * `codebuddy models add local-llm` still works there by hand.\n */\nexport function machineCanCard(): DownloadCheck {\n if (totalmem() < 4 * GB) {\n return { ok: false, reason: `${formatBytes(totalmem())} RAM (setup installs it from 4 GB)` };\n }\n if (availableParallelism() < 2) return { ok: false, reason: \"a single CPU core\" };\n return { ok: true };\n}\n\n/** Free disk (model cache volume) and RAM before a download. */\nexport async function checkLocalModelResources(spec: LocalModelSpec): Promise<DownloadCheck> {\n const directory = modelCacheDir();\n await mkdir(directory, { recursive: true });\n const fs = await statfs(directory).catch(() => null);\n if (fs && fs.bavail * fs.bsize < spec.bytes * 1.1) {\n return {\n ok: false,\n reason: `needs ${formatBytes(spec.bytes * 1.1)} free in ${directory}, ${formatBytes(fs.bavail * fs.bsize)} available`,\n };\n }\n if (totalmem() < spec.ramBytes * 2) {\n return {\n ok: false,\n reason: `needs about ${formatBytes(spec.ramBytes)} of RAM while loaded; this machine has ${formatBytes(totalmem())} in total`,\n };\n }\n return { ok: true };\n}\n\n/**\n * Download the pinned GGUF into the model cache, verifying size and sha256\n * before it is moved into place (a partial or corrupt file is never used).\n */\nexport async function downloadLocalModel(\n spec: LocalModelSpec,\n onProgress?: (received: number, total: number) => void,\n fetchImpl: typeof fetch = fetch,\n): Promise<string> {\n const target = localModelPath(spec);\n await mkdir(dirname(target), { recursive: true });\n const partial = `${target}.partial`;\n const url = `https://huggingface.co/${spec.repo}/resolve/${spec.revision}/${spec.file}`;\n const response = await fetchImpl(url);\n if (!response.ok || !response.body) {\n throw new Error(`download failed: HTTP ${response.status} for ${url}`);\n }\n const hash = createHash(\"sha256\");\n let received = 0;\n const source = Readable.fromWeb(response.body as import(\"node:stream/web\").ReadableStream);\n source.on(\"data\", (chunk: Buffer) => {\n hash.update(chunk);\n received += chunk.length;\n onProgress?.(received, spec.bytes);\n });\n try {\n await pipeline(source, createWriteStream(partial));\n const digest = hash.digest(\"hex\");\n if (received !== spec.bytes || digest !== spec.sha256) {\n throw new Error(\n `checksum mismatch for ${spec.file} (got ${received} bytes, sha256 ${digest.slice(0, 12)}…); not installed`,\n );\n }\n await rename(partial, target);\n } finally {\n await rm(partial, { force: true });\n }\n return target;\n}\n\nexport async function removeLocalModels(): Promise<string[]> {\n const removed: string[] = [];\n for (const spec of Object.values(LOCAL_MODELS)) {\n const path = localModelPath(spec);\n if (await stat(path).catch(() => null)) {\n await rm(path, { force: true });\n removed.push(spec.id);\n }\n }\n return removed;\n}\n\n// ── Generation ──────────────────────────────────────────────────────────\n\n/** Minimal structural types for the dynamically imported runtime. */\ntype Llama = {\n loadModel(options: { modelPath: string }): Promise<LlamaModel>;\n createGrammarForJsonSchema(schema: unknown): Promise<unknown>;\n getGrammarFor(type: \"json\"): Promise<unknown>;\n};\ntype LlamaModel = {\n createContext(options: { contextSize: number; threads: number }): Promise<LlamaContext>;\n dispose(): Promise<void>;\n};\ntype LlamaContext = { getSequence(): unknown; dispose(): Promise<void> };\ntype ChatSession = {\n prompt(\n text: string,\n options: {\n grammar?: unknown;\n maxTokens: number;\n temperature: number;\n budgets?: { thoughtTokens: number };\n signal?: AbortSignal;\n stopOnAbortSignal?: boolean;\n },\n ): Promise<string>;\n resetChatHistory(): void;\n setChatHistory?(history: unknown): void;\n dispose(options?: { disposeSequence?: boolean }): void;\n};\ntype Runtime = {\n getLlama(options?: { gpu?: \"auto\" | false }): Promise<Llama>;\n LlamaChatSession: new (options: {\n contextSequence: unknown;\n autoDisposeSequence?: boolean;\n systemPrompt?: string;\n }) => ChatSession;\n};\n\ntype Loaded = {\n spec: LocalModelSpec;\n llama: Llama;\n model: LlamaModel;\n context: LlamaContext;\n sequence: unknown;\n runtime: Runtime;\n};\n\nlet loading: Promise<Loaded> | null = null;\nlet loaded: Loaded | null = null;\n/** One generation at a time: a single context sequence is shared. */\nlet queue: Promise<unknown> = Promise.resolve();\nconst grammars = new Map<unknown, Promise<unknown>>();\n\nasync function importRuntime(): Promise<Runtime> {\n // A variable specifier: not a dependency, never bundled, resolved at run time.\n const specifier = process.env.CODEBUDDY_LLAMA_RUNTIME ?? \"node-llama-cpp\";\n return (await import(specifier)) as Runtime;\n}\n\n/** Context large enough for the bounded card/fact prompts (~1k tokens in, ≤ 400 out). */\nconst CONTEXT_TOKENS = 4096;\n\n/** Physical cores minus one (logical on SMT machines: a ceiling, not an exact count). */\nexport function generationThreads(): number {\n const configured = Number(process.env.CODEBUDDY_LLM_THREADS);\n if (Number.isInteger(configured) && configured > 0) return configured;\n return Math.max(1, availableParallelism() - 1);\n}\n\nasync function load(spec: LocalModelSpec): Promise<Loaded> {\n if (loaded?.spec.id === spec.id) return loaded;\n if (!loading) {\n loading = (async () => {\n const runtime = await importRuntime();\n const llama = await runtime.getLlama({ gpu: \"auto\" });\n const model = await llama.loadModel({ modelPath: localModelPath(spec) });\n const context = await model.createContext({\n contextSize: CONTEXT_TOKENS,\n threads: generationThreads(),\n });\n loaded = { spec, llama, model, context, sequence: context.getSequence(), runtime };\n return loaded;\n })().finally(() => {\n loading = null;\n });\n }\n return loading;\n}\n\n/**\n * The model leaves memory this long after its last generation, so a finished\n * card pass does not keep ~1 GB resident in `codebuddy serve` for the rest of\n * the session. The next pass (or a warm-up) loads it again in under a second.\n */\nexport const IDLE_UNLOAD_MS = 60_000;\nlet idleTimer: NodeJS.Timeout | null = null;\nlet running = 0;\n\nfunction scheduleUnload(): void {\n if (idleTimer) clearTimeout(idleTimer);\n idleTimer = setTimeout(() => void unloadLocalModel(), IDLE_UNLOAD_MS);\n // Never keeps the process alive on its own.\n idleTimer.unref();\n}\n\n/** Free the model and its context now (idle timer, `serve` shutdown). Never throws. */\nexport async function unloadLocalModel(): Promise<void> {\n if (idleTimer) clearTimeout(idleTimer);\n idleTimer = null;\n const current = loaded;\n if (!current || running > 0) return;\n loaded = null;\n grammars.clear();\n await current.context.dispose().catch(() => {});\n await current.model.dispose().catch(() => {});\n}\n\n/** True once the model is resident in this process (query expansion only runs then). */\nexport function isLocalModelWarm(): boolean {\n return loaded !== null;\n}\n\n/** Load the model now (background warm-up); resolves false when it cannot load. */\nexport async function warmLocalModel(spec: LocalModelSpec): Promise<boolean> {\n try {\n await load(spec);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * `chatJson` for the local backend: JSON constrained by a grammar built from\n * the request's zod schema (plain JSON grammar if the schema cannot be\n * converted), then validated with zod. Never throws.\n */\nexport async function localChatJson<T>(\n spec: LocalModelSpec,\n request: ChatJsonRequest<T>,\n): Promise<LlmResult<T>> {\n const started = Date.now();\n const fail = (reason: \"timeout\" | \"invalid\" | \"disabled\", detail: string): LlmResult<T> => ({\n ok: false,\n reason,\n detail: detail.slice(0, 300),\n latencyMs: Date.now() - started,\n });\n let model: Loaded;\n try {\n model = await withDeadline(load(spec), request.deadlineMs);\n } catch (error) {\n return (error as Error).name === \"TimeoutError\"\n ? fail(\"timeout\", \"model still loading\")\n : fail(\"disabled\", `local model unavailable: ${(error as Error).message}`);\n }\n const run = async (): Promise<LlmResult<T>> => {\n running++;\n try {\n return await generate();\n } finally {\n running--;\n scheduleUnload();\n }\n };\n const generate = async (): Promise<LlmResult<T>> => {\n // The idle unload may have run while this call waited in the queue.\n if (!loaded) model = await load(spec);\n const remaining = request.deadlineMs - (Date.now() - started);\n if (remaining <= 0) return fail(\"timeout\", `no answer within ${request.deadlineMs} ms`);\n const grammar = await grammarFor(model.llama, request.jsonSchema ?? request.schema);\n const session = new model.runtime.LlamaChatSession({\n contextSequence: model.sequence,\n autoDisposeSequence: false,\n systemPrompt: request.system,\n });\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), remaining);\n try {\n const text = await session.prompt(`${spec.noThink ? \"/no_think\\n\" : \"\"}${request.user}`, {\n grammar,\n maxTokens: request.maxTokens,\n temperature: 0,\n ...(spec.noThink ? { budgets: { thoughtTokens: 0 } } : {}),\n signal: controller.signal,\n stopOnAbortSignal: true,\n });\n if (controller.signal.aborted) {\n return fail(\"timeout\", `no answer within ${request.deadlineMs} ms`);\n }\n let value: unknown;\n try {\n value = JSON.parse(text);\n } catch {\n return fail(\"invalid\", `output is not JSON: ${text.slice(0, 120)}`);\n }\n const parsed = request.schema.safeParse(value);\n if (!parsed.success)\n return fail(\"invalid\", `output failed validation: ${text.slice(0, 120)}`);\n return {\n ok: true,\n value: parsed.data,\n model: spec.id,\n latencyMs: Date.now() - started,\n tokens: Math.ceil((request.system.length + request.user.length + text.length) / 4),\n cached: false,\n };\n } catch (error) {\n return controller.signal.aborted\n ? fail(\"timeout\", `no answer within ${request.deadlineMs} ms`)\n : fail(\"invalid\", String((error as Error)?.message ?? error));\n } finally {\n clearTimeout(timer);\n session.dispose({ disposeSequence: false });\n }\n };\n const result = queue.then(run, run);\n queue = result.catch(() => undefined);\n return result;\n}\n\nasync function grammarFor(llama: Llama, schema: z.ZodType | object): Promise<unknown> {\n let grammar = grammars.get(schema);\n if (!grammar) {\n grammar = (async () => {\n try {\n const json =\n schema instanceof z.ZodType\n ? z.toJSONSchema(schema, { io: \"input\", unrepresentable: \"any\" })\n : schema;\n return await llama.createGrammarForJsonSchema(stripSchemaMeta(json));\n } catch {\n return llama.getGrammarFor(\"json\");\n }\n })();\n grammars.set(schema, grammar);\n }\n return grammar;\n}\n\n/** llama.cpp's schema grammar wants plain structure: no `$schema`, no defaults. */\nfunction stripSchemaMeta(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(stripSchemaMeta);\n if (!value || typeof value !== \"object\") return value;\n const out: Record<string, unknown> = {};\n for (const [key, child] of Object.entries(value)) {\n if (key === \"$schema\" || key === \"default\" || key === \"additionalProperties\") continue;\n out[key] = stripSchemaMeta(child);\n }\n return out;\n}\n\nfunction withDeadline<T>(promise: Promise<T>, ms: number): Promise<T> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(\n () => reject(Object.assign(new Error(\"deadline\"), { name: \"TimeoutError\" })),\n ms,\n );\n promise.then(\n (value) => {\n clearTimeout(timer);\n resolve(value);\n },\n (error) => {\n clearTimeout(timer);\n reject(error);\n },\n );\n });\n}\n\n// ── Foreground yield ────────────────────────────────────────────────────\n\n/** A prompt hook or MCP tool call touched this within the window: background work waits. */\nconst FOREGROUND_WINDOW_MS = 2_000;\nconst FOREGROUND_MAX_WAIT_MS = 15_000;\n\nfunction foregroundPath(root: string): string {\n return join(root, \".codebuddy\", \"cache\", \"foreground\");\n}\n\n/** Record that a hook or tool call is running (cheap: one utimes). Never throws. */\nexport async function markForeground(root: string): Promise<void> {\n const path = foregroundPath(root);\n const now = new Date();\n try {\n await utimes(path, now, now);\n } catch {\n await mkdir(dirname(path), { recursive: true }).catch(() => {});\n await writeFile(path, \"\").catch(() => {});\n }\n}\n\n/** Wait (bounded) until no foreground work was marked within the window. */\nexport async function waitForForeground(root: string, signal?: AbortSignal): Promise<void> {\n const until = Date.now() + FOREGROUND_MAX_WAIT_MS;\n while (Date.now() < until && !signal?.aborted) {\n const info = await stat(foregroundPath(root)).catch(() => null);\n if (!info || Date.now() - info.mtimeMs > FOREGROUND_WINDOW_MS) return;\n await new Promise((resolve) => setTimeout(resolve, 250));\n }\n}\n\n// ── Status ──────────────────────────────────────────────────────────────\n\nexport type LocalModelStatus = {\n runtime: boolean;\n installed: LocalModelSpec | null;\n path: string | null;\n};\n\nexport async function localModelStatus(preferred?: LocalModelSize): Promise<LocalModelStatus> {\n const installed = await installedLocalModel(preferred);\n return {\n runtime: await localRuntimeAvailable(),\n installed,\n path: installed ? localModelPath(installed) : null,\n };\n}\n\nexport function formatBytes(bytes: number): string {\n return bytes >= GB ? `${(bytes / GB).toFixed(1)} GB` : `${Math.round(bytes / 1024 ** 2)} MB`;\n}\n\n/** Free RAM now (for the `models add` warning; the hard check uses total RAM). */\nexport function freeMemory(): number {\n return freemem();\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/llm/summaries.ts"],"sourcesContent":["import { mkdir, readdir, readFile, rm } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { z } from \"zod\";\nimport {\n CHANGELOG_PATH,\n loadProjectArchitectureMap,\n NON_SOURCE_PATH,\n readBoundedSource,\n TEST_PATH,\n} from \"../context/discovery.js\";\nimport { writeAtomic } from \"../core/markdown-store-fs.js\";\nimport { redactSensitive, scanSensitive } from \"../core/sensitive.js\";\nimport { loadFreshIndex } from \"../indexer/engine.js\";\nimport type { IndexEntry, IndexManifest } from \"../indexer/types.js\";\nimport { CARDS_DIR, refreshZvecCardIndex } from \"../retrieval/zvec.js\";\nimport { chatJson } from \"./client.js\";\nimport { type EnabledLlmConfig, resolveLlmConfig } from \"./config.js\";\nimport { waitForForeground } from \"./local.js\";\n\n/**\n * Intent cards: for each source file, a plain-English summary, the symptoms a\n * bug report would use, the code terms a search should hit, and one-line\n * descriptions of its main symbols. Written off the prompt path by the local\n * model (`provider: \"local\"`) or the BYOK model (`codebuddy index --cards`,\n * `codebuddy serve`, `watch`, `codebuddy llm summarize`) and cached by content\n * hash. Retrieval uses them three ways: a card vector per file\n * (`file-vectors.ts`), a card workspace in zvec-grep (`CARDS_DIR`), and\n * down-weighted card words in the content index. A card alone never admits a\n * file: card evidence counts when another dense signal agrees (see the\n * agreement rule in `context/discovery.ts`).\n */\nexport const SUMMARY_PROMPT_VERSION = 2;\nconst LIMITS = {\n fileChars: 200,\n symbolChars: 100,\n symptomChars: 80,\n termChars: 40,\n symptoms: 6,\n terms: 10,\n symbols: 8,\n signatures: 12,\n neighbours: 5,\n /** ~600 tokens per file: long-line (minified) files are cut, never sent whole. */\n headChars: 1_600,\n signatureChars: 160,\n batchTokens: 5_000,\n batchFiles: 8,\n outputTokensPerFile: 320,\n /** The local grammar bounds every field, so one card always fits. */\n localOutputTokens: 700,\n tokensPerMinute: 6_000,\n timeoutMs: 20_000,\n /** One file per call for the local model: a 0.6B model loses track of batches. */\n localTimeoutMs: 30_000,\n} as const;\n\nexport type LlmSummary = {\n hash: string;\n model: string;\n version: number;\n summary: string;\n symbols: Record<string, string>;\n /** Card fields (prompt version ≥ 2). */\n symptoms?: string[];\n terms?: string[];\n};\n\n/**\n * Source files that get a card: no tests, docs, config, changelogs, vendored,\n * generated, minified or lock files (secret-bearing ones are skipped when read).\n */\nexport function cardEligible(path: string): boolean {\n return (\n !TEST_PATH.test(path) &&\n !NON_SOURCE_PATH.test(path) &&\n !CHANGELOG_PATH.test(path) &&\n !GENERATED_PATH.test(path)\n );\n}\nconst GENERATED_PATH =\n /(^|\\/)(vendor|vendored|third[_-]?party|node_modules|dist|build|generated|__generated__)\\/|\\.(min|bundle|generated|pb)\\.[a-z]+$|_pb2\\.py$|(^|\\/)[^/]*lock[^/]*$/i;\n\n/** The text embedded as a file's card vector (empty for a summary without card fields). */\nexport function cardText(card: LlmSummary): string {\n return [\n card.summary,\n ...(card.symptoms ?? []),\n ...Object.entries(card.symbols).map(([name, text]) => `${name}: ${text}`),\n ]\n .filter(Boolean)\n .join(\"\\n\");\n}\n\n/** Words the content index adds (down-weighted) for a card: terms and symptoms. */\nexport function cardWords(card: LlmSummary): string {\n return [...(card.terms ?? []), ...(card.symptoms ?? [])].join(\" \");\n}\n\ntype Store = Record<string, LlmSummary>;\nconst storePath = (root: string) => join(root, \".codebuddy\", \"cache\", \"llm-summaries.json\");\n\nasync function readStore(root: string): Promise<Store> {\n try {\n return JSON.parse(await readFile(storePath(root), \"utf8\")) as Store;\n } catch {\n return {};\n }\n}\n\nasync function writeStore(root: string, store: Store): Promise<void> {\n await mkdir(join(root, \".codebuddy\", \"cache\"), { recursive: true });\n await writeAtomic(storePath(root), `${JSON.stringify(store)}\\n`);\n}\n\n/** Summaries whose content hash still matches the current index. */\nexport async function loadLlmSummaries(\n root: string,\n manifest: IndexManifest,\n): Promise<Map<string, LlmSummary>> {\n const store = await readStore(root);\n return new Map(\n Object.entries(store).filter(([path, entry]) => manifest.entries[path]?.hash === entry.hash),\n );\n}\n\nconst outputSchema = z.object({\n files: z.array(\n z.object({\n path: z.string(),\n summary: z.string(),\n symptoms: z.array(z.string()).default([]),\n terms: z.array(z.string()).default([]),\n symbols: z.array(z.object({ name: z.string(), summary: z.string() })).default([]),\n }),\n ),\n});\n\n/**\n * Grammar bounds for the local model: without them a 0.6B model writes\n * paragraph summaries and endless arrays until it hits the token cap and the\n * JSON is cut off. Zod still validates (and trims) every answer.\n */\nconst boundedString = (maxLength: number) => ({ type: \"string\", maxLength });\nconst CARD_GRAMMAR = {\n type: \"object\",\n properties: {\n files: {\n type: \"array\",\n minItems: 1,\n maxItems: 1,\n items: {\n type: \"object\",\n properties: {\n path: boundedString(300),\n summary: boundedString(LIMITS.fileChars),\n symptoms: {\n type: \"array\",\n items: boundedString(LIMITS.symptomChars),\n maxItems: LIMITS.symptoms,\n },\n terms: { type: \"array\", items: boundedString(LIMITS.termChars), maxItems: LIMITS.terms },\n symbols: {\n type: \"array\",\n maxItems: LIMITS.symbols,\n items: {\n type: \"object\",\n properties: { name: boundedString(80), summary: boundedString(LIMITS.symbolChars) },\n },\n },\n },\n },\n },\n },\n};\n\nconst SYSTEM = [\n \"You write search cards for source files so that a plain-English bug report or task finds the right file.\",\n 'Answer with JSON only: {\"files\":[{\"path\":\"...\",\"summary\":\"...\",\"symptoms\":[\"...\"],\"terms\":[\"...\"],\"symbols\":[{\"name\":\"...\",\"summary\":\"...\"}]}]}.',\n \"For each input file:\",\n `summary: one plain-English sentence (max ${LIMITS.fileChars} chars) saying what the file does;`,\n `symptoms: 3 to ${LIMITS.symptoms} short phrases a user or bug report would use to describe a problem caused by this file, in everyday words without code identifiers;`,\n `terms: 5 to ${LIMITS.terms} code identifiers and domain words from the file that a search should match;`,\n `symbols: up to ${LIMITS.symbols} main symbols (only names listed in \"signatures\"), each with one short phrase.`,\n \"Symptoms describe what goes wrong for a user of this specific file (a wrong value, an error, nothing happening), never generic phrases.\",\n \"Never invent paths or symbol names.\",\n].join(\" \");\n\nexport type SummarizeResult = {\n summarized: number;\n skipped: number;\n remaining: number;\n stoppedBy: \"done\" | \"limit\" | \"budget\" | \"cooldown\" | \"disabled\" | \"aborted\" | \"error\";\n};\n\nexport async function summarizeRepository(\n root: string,\n options: {\n limit?: number;\n onProgress?: (done: number, total: number) => void;\n signal?: AbortSignal;\n /** Injected config (tests, CLI); defaults to `resolveLlmConfig`. */\n config?: EnabledLlmConfig;\n /** Wait between batches to stay under the free tier's tokens/minute. */\n pace?: boolean;\n batchFiles?: number;\n } = {},\n): Promise<SummarizeResult> {\n const resolved = options.config ?? (await resolveLlmConfig(root));\n if (!resolved.enabled || !resolved.features.summaries) {\n return { summarized: 0, skipped: 0, remaining: 0, stoppedBy: \"disabled\" };\n }\n const config = resolved;\n const { manifest } = await loadFreshIndex(root);\n const store = await readStore(root);\n for (const path of Object.keys(store)) if (!manifest.entries[path]) delete store[path];\n\n const map = await loadProjectArchitectureMap(root, manifest).catch(() => null);\n const pending = orderByImportance(\n map,\n Object.values(manifest.entries).filter((entry) => {\n if (!cardEligible(entry.path)) return false;\n const done = store[entry.path];\n return !(\n done?.hash === entry.hash &&\n done.model === config.model &&\n done.version === SUMMARY_PROMPT_VERSION\n );\n }),\n );\n const local = config.provider === \"local\";\n const batchFiles = local ? 1 : (options.batchFiles ?? LIMITS.batchFiles);\n const limit = options.limit ?? Number.POSITIVE_INFINITY;\n let summarized = 0;\n let skipped = 0;\n let index = 0;\n let stoppedBy: SummarizeResult[\"stoppedBy\"] = \"done\";\n\n while (index < pending.length) {\n if (options.signal?.aborted) {\n stoppedBy = \"aborted\";\n break;\n }\n if (summarized >= limit) {\n stoppedBy = \"limit\";\n break;\n }\n const batch: Array<{ entry: IndexEntry; input: FileInput }> = [];\n let tokens = 0;\n while (index < pending.length && batch.length < batchFiles) {\n if (summarized + batch.length >= limit) break;\n const entry = pending[index] as IndexEntry;\n const input = await fileInput(root, entry, map);\n if (input === null) {\n skipped++;\n index++;\n continue;\n }\n const cost = Math.ceil(JSON.stringify(input).length / 4);\n if (batch.length > 0 && tokens + cost > LIMITS.batchTokens - 600) break;\n batch.push({ entry, input });\n tokens += cost;\n index++;\n }\n if (batch.length === 0) continue;\n\n // A prompt hook or tool call is running: let it have the machine first.\n if (local) await waitForForeground(root, options.signal);\n const started = Date.now();\n const ask = () =>\n chatJson(config, {\n system: SYSTEM,\n user: JSON.stringify({ files: batch.map(({ input }) => input) }),\n schema: outputSchema,\n jsonSchema: CARD_GRAMMAR,\n maxTokens: local ? LIMITS.localOutputTokens : LIMITS.outputTokensPerFile * batch.length,\n deadlineMs: local ? LIMITS.localTimeoutMs : LIMITS.timeoutMs,\n purpose: \"background\",\n repositoryRoot: root,\n });\n let result = await ask();\n // Invalid output gets one retry, then the file is skipped.\n if (!result.ok && result.reason === \"invalid\") result = await ask();\n if (!result.ok) {\n // A batch the provider rejects outright (bad output, or e.g. 413 for one\n // file) is skipped, not retried forever; limits and outages stop the run.\n // A local timeout is one slow file, not an outage.\n if (\n result.reason === \"invalid\" ||\n (result.reason === \"http\" && batch.length === 1) ||\n (local && result.reason === \"timeout\")\n ) {\n skipped += batch.length;\n continue;\n }\n index -= batch.length;\n stoppedBy =\n result.reason === \"budget\"\n ? \"budget\"\n : result.reason === \"cooldown\" || result.reason === \"rate-limited\"\n ? \"cooldown\"\n : \"error\";\n break;\n }\n for (const { entry } of batch) {\n // A one-file batch (the local model) may echo the path imperfectly.\n const answer =\n result.value.files.find((file) => file.path === entry.path) ??\n (batch.length === 1 && result.value.files.length === 1 ? result.value.files[0] : undefined);\n if (!answer?.summary.trim()) {\n skipped++;\n continue;\n }\n store[entry.path] = toCard(entry, config.model, answer);\n summarized++;\n }\n await writeStore(root, store);\n await writeCardFiles(\n root,\n store,\n batch.map(({ entry }) => entry.path),\n );\n options.onProgress?.(index, pending.length);\n if (options.pace !== false) {\n const wait = (result.tokens / LIMITS.tokensPerMinute) * 60_000 - (Date.now() - started);\n if (wait > 0) await new Promise((resolve) => setTimeout(resolve, wait));\n }\n }\n await writeStore(root, store);\n await pruneCardFiles(root, store, manifest);\n if (summarized > 0)\n await refreshZvecCardIndex(root, options.signal ? { signal: options.signal } : {});\n return { summarized, skipped, remaining: pending.length - index, stoppedBy };\n}\n\ntype CardAnswer = z.infer<typeof outputSchema>[\"files\"][number];\n\n/** Bounded, de-duplicated card fields; symbols only from the file's symbol table. */\nfunction toCard(entry: IndexEntry, model: string, answer: CardAnswer): LlmSummary {\n const known = new Set((entry.symbolTable ?? []).map((symbol) => symbol.name));\n const clean = (items: string[], count: number, chars: number) =>\n [\n ...new Set(items.map((item) => redactSensitive(item.trim()).slice(0, chars)).filter(Boolean)),\n ].slice(0, count);\n return {\n hash: entry.hash,\n model,\n version: SUMMARY_PROMPT_VERSION,\n summary: redactSensitive(answer.summary.trim()).slice(0, LIMITS.fileChars),\n symptoms: clean(answer.symptoms, LIMITS.symptoms, LIMITS.symptomChars),\n terms: clean(answer.terms, LIMITS.terms, LIMITS.termChars),\n symbols: Object.fromEntries(\n answer.symbols\n .filter((symbol) => known.has(symbol.name))\n .slice(0, LIMITS.symbols)\n .map((symbol) => [symbol.name, symbol.summary.trim().slice(0, LIMITS.symbolChars)]),\n ),\n };\n}\n\n/** One Markdown card per source file for the zvec-grep card workspace. */\nasync function writeCardFiles(root: string, store: Store, paths: string[]): Promise<void> {\n for (const path of paths) {\n const card = store[path];\n if (!card) continue;\n const target = join(root, CARDS_DIR, `${path}.md`);\n await mkdir(dirname(target), { recursive: true });\n await writeAtomic(\n target,\n [\n \"---\",\n `source: ${JSON.stringify(path)}`,\n `hash: ${card.hash}`,\n \"---\",\n \"\",\n `# ${path}`,\n \"\",\n card.summary,\n \"\",\n ...(card.symptoms?.length ? [\"Symptoms:\", ...card.symptoms.map((s) => `- ${s}`), \"\"] : []),\n ...(card.terms?.length ? [`Terms: ${card.terms.join(\", \")}`, \"\"] : []),\n ...Object.entries(card.symbols).map(([name, text]) => `- ${name}: ${text}`),\n \"\",\n ].join(\"\\n\"),\n );\n }\n}\n\n/** Write missing cards and delete cards whose source is gone or no longer current. */\nasync function pruneCardFiles(root: string, store: Store, manifest: IndexManifest): Promise<void> {\n const directory = join(root, CARDS_DIR);\n const current = new Set(\n Object.entries(store)\n .filter(([path, card]) => manifest.entries[path]?.hash === card.hash && card.symptoms)\n .map(([path]) => `${path}.md`),\n );\n const existing = await readdir(directory, { recursive: true }).catch(() => [] as string[]);\n const present = new Set(existing.map((name) => name.split(\"\\\\\").join(\"/\")));\n for (const name of present) {\n if (name.endsWith(\".md\") && !name.startsWith(\".zvec-grep\") && !current.has(name)) {\n await rm(join(directory, name), { force: true });\n }\n }\n await writeCardFiles(\n root,\n store,\n [...current].filter((name) => !present.has(name)).map((name) => name.slice(0, -3)),\n );\n}\n\n/** Card coverage for `stats`/`storage`/`models`: current cards vs eligible source files. */\nexport async function cardProgress(root: string): Promise<{ carded: number; eligible: number }> {\n const { manifest } = await loadFreshIndex(root);\n const store = await readStore(root);\n const eligible = Object.keys(manifest.entries).filter(cardEligible);\n return {\n carded: eligible.filter(\n (path) =>\n store[path]?.hash === manifest.entries[path]?.hash &&\n store[path]?.version === SUMMARY_PROMPT_VERSION,\n ).length,\n eligible: eligible.length,\n };\n}\n\ntype FileInput = {\n path: string;\n language: string;\n signatures: string[];\n /** Files this one imports and files that import it (paths only). */\n neighbours: string[];\n head: string;\n};\n\ntype ImportMap = Awaited<ReturnType<typeof loadProjectArchitectureMap>> | null;\n\n/** Import lines say little about what a file does; the card reads past them. */\nconst IMPORT_LINE =\n /^\\s*(import\\b|from\\s+\\S+\\s+import\\b|export\\s+(\\*|\\{[^}]*\\})\\s+from\\b|(const|let|var)\\s+\\S+\\s*=\\s*require\\()|^\\s*$/;\n\n/** What is sent for one file; null for unreadable or secret-bearing files. */\nasync function fileInput(\n root: string,\n entry: IndexEntry,\n map: ImportMap,\n): Promise<FileInput | null> {\n const source = await readBoundedSource(root, entry.path);\n if (source === null || scanSensitive(source).sensitive) return null;\n const edges = map?.edges ?? [];\n return {\n path: entry.path,\n language: entry.language,\n // Not the deterministic summary: small models copy its \"ts • 80 lines\" form.\n signatures: (entry.symbolTable ?? [])\n .filter((symbol) => symbol.kind !== \"heading\")\n .slice(0, LIMITS.signatures)\n .map((symbol) => redactSensitive(symbol.signature.slice(0, LIMITS.signatureChars))),\n neighbours: [\n ...new Set([\n ...edges.filter((edge) => edge.from === entry.path).map((edge) => edge.to),\n ...edges.filter((edge) => edge.to === entry.path).map((edge) => edge.from),\n ]),\n ].slice(0, LIMITS.neighbours),\n // The module docstring and first definitions, never the whole file.\n head: redactSensitive(\n source\n .split(/\\r?\\n/)\n .filter((line) => !IMPORT_LINE.test(line))\n .join(\"\\n\")\n .slice(0, LIMITS.headChars),\n ),\n };\n}\n\n/** Most-imported source first. */\nfunction orderByImportance(map: ImportMap, entries: IndexEntry[]): IndexEntry[] {\n const fanIn = new Map<string, number>();\n for (const edge of map?.edges ?? []) fanIn.set(edge.to, (fanIn.get(edge.to) ?? 0) + 1);\n return [...entries].sort(\n (left, right) =>\n (fanIn.get(right.path) ?? 0) - (fanIn.get(left.path) ?? 0) ||\n left.path.localeCompare(right.path),\n );\n}\n"],"mappings":"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","names":[]}