@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.
Files changed (104) hide show
  1. package/dist/{chunk-IBLJOHJL.js → chunk-3QGW53GD.js} +2 -2
  2. package/dist/{chunk-NERKZSSE.js → chunk-5EDGAZQN.js} +438 -68
  3. package/dist/chunk-5EDGAZQN.js.map +1 -0
  4. package/dist/{chunk-JFDRBPYF.js → chunk-6JCKMIQY.js} +2 -2
  5. package/dist/{chunk-2XDPKFUW.js → chunk-AK2UAE32.js} +46 -20
  6. package/dist/chunk-AK2UAE32.js.map +1 -0
  7. package/dist/{chunk-GDJH7XJC.js → chunk-AL6VBTNA.js} +77 -3
  8. package/dist/chunk-AL6VBTNA.js.map +1 -0
  9. package/dist/{chunk-QBDJXGG5.js → chunk-AQHPEJR6.js} +93 -22
  10. package/dist/chunk-AQHPEJR6.js.map +1 -0
  11. package/dist/{chunk-OWWCDRXW.js → chunk-BQM7FJ7D.js} +6 -6
  12. package/dist/chunk-BQM7FJ7D.js.map +1 -0
  13. package/dist/{chunk-XBJQKCLU.js → chunk-CAEPHLHC.js} +13 -9
  14. package/dist/chunk-CAEPHLHC.js.map +1 -0
  15. package/dist/chunk-CG5B3LYG.js +66 -0
  16. package/dist/chunk-CG5B3LYG.js.map +1 -0
  17. package/dist/{chunk-IHQM2ZDV.js → chunk-DFV2SK6C.js} +1 -1
  18. package/dist/{chunk-IHQM2ZDV.js.map → chunk-DFV2SK6C.js.map} +1 -1
  19. package/dist/{chunk-IMFUYEZA.js → chunk-EEUFU7KV.js} +81 -54
  20. package/dist/chunk-EEUFU7KV.js.map +1 -0
  21. package/dist/{chunk-5MQOE4J6.js → chunk-ESZXCZIN.js} +3 -3
  22. package/dist/{chunk-J2EAZ4MA.js → chunk-GKNYMNFM.js} +22 -25
  23. package/dist/chunk-GKNYMNFM.js.map +1 -0
  24. package/dist/{chunk-JPHJBCZB.js → chunk-HPOR6YXA.js} +3 -2
  25. package/dist/chunk-HPOR6YXA.js.map +1 -0
  26. package/dist/{chunk-S6VEG4S2.js → chunk-JUAFCLRB.js} +28 -5
  27. package/dist/chunk-JUAFCLRB.js.map +1 -0
  28. package/dist/{chunk-5KZY6OTG.js → chunk-KNR2P2UY.js} +33 -83
  29. package/dist/chunk-KNR2P2UY.js.map +1 -0
  30. package/dist/{chunk-B6CO63SQ.js → chunk-LCSBL5TT.js} +21 -9
  31. package/dist/chunk-LCSBL5TT.js.map +1 -0
  32. package/dist/{chunk-5FW3PMNP.js → chunk-MSUAVR3R.js} +76 -11
  33. package/dist/chunk-MSUAVR3R.js.map +1 -0
  34. package/dist/{chunk-RAJJSCIP.js → chunk-SFMIMLXR.js} +2 -2
  35. package/dist/chunk-TGFTKWOR.js +16 -0
  36. package/dist/chunk-TGFTKWOR.js.map +1 -0
  37. package/dist/chunk-TUR6YKEE.js +55 -0
  38. package/dist/chunk-TUR6YKEE.js.map +1 -0
  39. package/dist/chunk-Y7D7PUBE.js +321 -0
  40. package/dist/chunk-Y7D7PUBE.js.map +1 -0
  41. package/dist/{chunk-R7CD6EXS.js → chunk-YJIA2MFL.js} +572 -377
  42. package/dist/chunk-YJIA2MFL.js.map +1 -0
  43. package/dist/cli/index.js +5 -5
  44. package/dist/cli/index.js.map +1 -1
  45. package/dist/{cloud-7WM3YLI4.js → cloud-LXZUR3U2.js} +3 -3
  46. package/dist/{codebuddy-B_pCaQrV.d.ts → codebuddy-DO7ZhoWn.d.ts} +15 -1
  47. package/dist/config-GEZFLXK6.js +12 -0
  48. package/dist/index.d.ts +46 -3
  49. package/dist/index.js +22 -19
  50. package/dist/langgraph/index.d.ts +1 -1
  51. package/dist/lean-command-CWJAVQ5G.js +18 -0
  52. package/dist/{local-5MZURLCZ.js → local-ST4WMRCG.js} +3 -3
  53. package/dist/{main-MTBQZGJV.js → main-AHNGHC4L.js} +213 -183
  54. package/dist/main-AHNGHC4L.js.map +1 -0
  55. package/dist/{memory-KIC5FJVW.js → memory-TGHVTBME.js} +9 -8
  56. package/dist/memory-TGHVTBME.js.map +1 -0
  57. package/dist/operations-7BTAAUHF.js +26 -0
  58. package/dist/pretool-command-3SXQSMAO.js +13 -0
  59. package/dist/{rerank-5QZ3PNUW.js → rerank-IQAEWLQR.js} +6 -5
  60. package/dist/rerank-IQAEWLQR.js.map +1 -0
  61. package/dist/{runtime-CUBOOPZO.js → runtime-E7JQZ2BZ.js} +14 -11
  62. package/dist/summaries-XV3JGGCC.js +20 -0
  63. package/dist/{zvec-MSRKT7IV.js → zvec-KP4KNB7Y.js} +10 -3
  64. package/package.json +1 -1
  65. package/src/db/migrations/0006_fact_idempotency_key.sql +2 -0
  66. package/src/db/migrations/meta/0006_snapshot.json +1258 -0
  67. package/src/db/migrations/meta/_journal.json +7 -0
  68. package/dist/chunk-2XDPKFUW.js.map +0 -1
  69. package/dist/chunk-5FW3PMNP.js.map +0 -1
  70. package/dist/chunk-5KZY6OTG.js.map +0 -1
  71. package/dist/chunk-B6CO63SQ.js.map +0 -1
  72. package/dist/chunk-GDJH7XJC.js.map +0 -1
  73. package/dist/chunk-IMFUYEZA.js.map +0 -1
  74. package/dist/chunk-J2EAZ4MA.js.map +0 -1
  75. package/dist/chunk-JPHJBCZB.js.map +0 -1
  76. package/dist/chunk-NERKZSSE.js.map +0 -1
  77. package/dist/chunk-OWWCDRXW.js.map +0 -1
  78. package/dist/chunk-PQK3GKZH.js +0 -98
  79. package/dist/chunk-PQK3GKZH.js.map +0 -1
  80. package/dist/chunk-QBDJXGG5.js.map +0 -1
  81. package/dist/chunk-R7CD6EXS.js.map +0 -1
  82. package/dist/chunk-S6VEG4S2.js.map +0 -1
  83. package/dist/chunk-XBJQKCLU.js.map +0 -1
  84. package/dist/config-DEVHUDIU.js +0 -11
  85. package/dist/lean-command-HTQCPH76.js +0 -15
  86. package/dist/main-MTBQZGJV.js.map +0 -1
  87. package/dist/memory-KIC5FJVW.js.map +0 -1
  88. package/dist/operations-POCHY3WR.js +0 -25
  89. package/dist/pretool-command-AX6JXP25.js +0 -10
  90. package/dist/rerank-5QZ3PNUW.js.map +0 -1
  91. package/dist/summaries-SB5L2TSW.js +0 -18
  92. /package/dist/{chunk-IBLJOHJL.js.map → chunk-3QGW53GD.js.map} +0 -0
  93. /package/dist/{chunk-JFDRBPYF.js.map → chunk-6JCKMIQY.js.map} +0 -0
  94. /package/dist/{chunk-5MQOE4J6.js.map → chunk-ESZXCZIN.js.map} +0 -0
  95. /package/dist/{chunk-RAJJSCIP.js.map → chunk-SFMIMLXR.js.map} +0 -0
  96. /package/dist/{cloud-7WM3YLI4.js.map → cloud-LXZUR3U2.js.map} +0 -0
  97. /package/dist/{config-DEVHUDIU.js.map → config-GEZFLXK6.js.map} +0 -0
  98. /package/dist/{lean-command-HTQCPH76.js.map → lean-command-CWJAVQ5G.js.map} +0 -0
  99. /package/dist/{local-5MZURLCZ.js.map → local-ST4WMRCG.js.map} +0 -0
  100. /package/dist/{operations-POCHY3WR.js.map → operations-7BTAAUHF.js.map} +0 -0
  101. /package/dist/{pretool-command-AX6JXP25.js.map → pretool-command-3SXQSMAO.js.map} +0 -0
  102. /package/dist/{runtime-CUBOOPZO.js.map → runtime-E7JQZ2BZ.js.map} +0 -0
  103. /package/dist/{summaries-SB5L2TSW.js.map → summaries-XV3JGGCC.js.map} +0 -0
  104. /package/dist/{zvec-MSRKT7IV.js.map → zvec-KP4KNB7Y.js.map} +0 -0
@@ -1,15 +1,6 @@
1
1
  import {
2
2
  runPreToolHookCommand
3
- } from "./chunk-QBDJXGG5.js";
4
- import {
5
- loadLlmSummaries,
6
- summarizeRepository
7
- } from "./chunk-OWWCDRXW.js";
8
- import {
9
- chatJson,
10
- coolingDown,
11
- usageToday
12
- } from "./chunk-5MQOE4J6.js";
3
+ } from "./chunk-AQHPEJR6.js";
13
4
  import {
14
5
  isDockerDaemonRunning,
15
6
  tryStartDockerDesktop,
@@ -23,7 +14,7 @@ import {
23
14
  import {
24
15
  renderLeanBlock,
25
16
  runLeanHookCommand
26
- } from "./chunk-B6CO63SQ.js";
17
+ } from "./chunk-LCSBL5TT.js";
27
18
  import {
28
19
  failSoft,
29
20
  readHookInput,
@@ -35,7 +26,12 @@ import {
35
26
  registerCodeBuddyTools,
36
27
  startMcpServer,
37
28
  watchRepository
38
- } from "./chunk-IMFUYEZA.js";
29
+ } from "./chunk-EEUFU7KV.js";
30
+ import {
31
+ DEFAULT_ZVEC_MODEL,
32
+ buildZvecIndex,
33
+ zvecStatus
34
+ } from "./chunk-MSUAVR3R.js";
39
35
  import {
40
36
  PlanFileStore,
41
37
  PlanLifecycle,
@@ -46,37 +42,28 @@ import {
46
42
  buildBeforeEditContext,
47
43
  buildBootstrapContext,
48
44
  openHookSemantics,
49
- parseTraceLines,
50
- readTraces,
51
45
  renderContextBlock,
52
46
  resolveRiskPaths,
53
47
  runCaptureHook,
54
48
  runContextHook,
55
49
  runResetHook,
56
50
  runStageHook,
57
- runSyncHook,
51
+ runSyncHook
52
+ } from "./chunk-YJIA2MFL.js";
53
+ import {
54
+ parseTraceLines,
55
+ readTraces,
58
56
  tracePath
59
- } from "./chunk-R7CD6EXS.js";
60
- import "./chunk-IHQM2ZDV.js";
57
+ } from "./chunk-TUR6YKEE.js";
58
+ import "./chunk-DFV2SK6C.js";
61
59
  import {
62
60
  SessionCapsuleLedger,
63
- estimateTokens,
64
- hookRoot
65
- } from "./chunk-J2EAZ4MA.js";
61
+ detectTestCommand,
62
+ estimateTokens
63
+ } from "./chunk-GKNYMNFM.js";
66
64
  import {
67
- buildArchitectureMap,
68
- generateGraphifyGraph,
69
- indexRepository,
70
- loadFreshIndex,
71
- loadGraphifyMap,
72
- neighbours,
73
- queryMap,
74
- setupGraphify
75
- } from "./chunk-NERKZSSE.js";
76
- import {
77
- extractSymbolTable,
78
- supportsSymbols
79
- } from "./chunk-PQK3GKZH.js";
65
+ hookRoot
66
+ } from "./chunk-TGFTKWOR.js";
80
67
  import {
81
68
  bootstrapDatabase,
82
69
  buildProviderFromConfig,
@@ -88,37 +75,56 @@ import {
88
75
  pingDatabase,
89
76
  runDoctor,
90
77
  runMigrations
91
- } from "./chunk-2XDPKFUW.js";
78
+ } from "./chunk-AK2UAE32.js";
92
79
  import {
93
80
  MemoryFileStore,
94
81
  VERSION
95
- } from "./chunk-S6VEG4S2.js";
96
- import {
97
- redactPersisted
98
- } from "./chunk-GDJH7XJC.js";
82
+ } from "./chunk-JUAFCLRB.js";
99
83
  import {
100
84
  computeFactHash,
101
85
  generateEntityId
102
- } from "./chunk-XBJQKCLU.js";
103
- import "./chunk-JFDRBPYF.js";
86
+ } from "./chunk-CAEPHLHC.js";
87
+ import "./chunk-6JCKMIQY.js";
104
88
  import {
105
89
  localRuntimeInstalled,
106
90
  modelCacheDir
107
- } from "./chunk-IBLJOHJL.js";
91
+ } from "./chunk-3QGW53GD.js";
92
+ import {
93
+ loadLlmSummaries,
94
+ summarizeRepository
95
+ } from "./chunk-BQM7FJ7D.js";
96
+ import {
97
+ chatJson,
98
+ coolingDown,
99
+ usageToday
100
+ } from "./chunk-ESZXCZIN.js";
101
+ import {
102
+ buildArchitectureMap,
103
+ generateGraphifyGraph,
104
+ indexRepository,
105
+ loadFreshIndex,
106
+ loadGraphifyMap,
107
+ neighbours,
108
+ queryMap,
109
+ setupGraphify,
110
+ syncFileVectors
111
+ } from "./chunk-5EDGAZQN.js";
112
+ import {
113
+ extractSymbolTable,
114
+ supportsSymbols
115
+ } from "./chunk-Y7D7PUBE.js";
116
+ import {
117
+ redactPersisted
118
+ } from "./chunk-AL6VBTNA.js";
108
119
  import {
109
120
  DEFAULT_CLOUD_EMBEDDING_MODELS,
110
121
  DEFAULT_RERANK_MODELS,
111
122
  LOCAL_EMBEDDING_MODEL,
112
123
  getModelDefinition
113
- } from "./chunk-JPHJBCZB.js";
114
- import {
115
- DEFAULT_ZVEC_MODEL,
116
- buildZvecIndex,
117
- zvecStatus
118
- } from "./chunk-5FW3PMNP.js";
124
+ } from "./chunk-HPOR6YXA.js";
119
125
  import {
120
126
  resolveLlmConfig
121
- } from "./chunk-RAJJSCIP.js";
127
+ } from "./chunk-SFMIMLXR.js";
122
128
  import {
123
129
  PLAN_POLICIES,
124
130
  configPath,
@@ -135,9 +141,11 @@ import {
135
141
  setLlmEnabled,
136
142
  setPlanPolicy,
137
143
  setZvecEnabled,
138
- writeAtomic,
139
144
  writeConfigFile
140
- } from "./chunk-5KZY6OTG.js";
145
+ } from "./chunk-KNR2P2UY.js";
146
+ import {
147
+ writeAtomic
148
+ } from "./chunk-CG5B3LYG.js";
141
149
  import "./chunk-PZ5AY32C.js";
142
150
 
143
151
  // src/cli/main.ts
@@ -243,9 +251,7 @@ import pc from "picocolors";
243
251
 
244
252
  // src/change/engine.ts
245
253
  import { spawn } from "child_process";
246
- import { constants as constants2 } from "fs";
247
- import { access as access2, readFile } from "fs/promises";
248
- import { extname, join as join2 } from "path";
254
+ import { extname } from "path";
249
255
  var CODE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
250
256
  var DEFAULT_TEST_TIMEOUT_MS = 12e4;
251
257
  var MAX_TEST_OUTPUT_CHARS = 12e3;
@@ -294,7 +300,7 @@ async function buildChangeReport(input) {
294
300
  }
295
301
  async function verifyChange(input) {
296
302
  const report = await buildChangeReport(input);
297
- const detected = input.command ? { command: input.command, source: "explicit" } : await detectTestCommand(input.repositoryRoot ?? process.cwd());
303
+ const detected = input.command ? { command: input.command, source: "explicit" } : await detectedTestCommand(input.repositoryRoot ?? process.cwd());
298
304
  if (!detected) {
299
305
  return {
300
306
  report,
@@ -305,7 +311,7 @@ async function verifyChange(input) {
305
311
  exitCode: null,
306
312
  signal: null,
307
313
  durationMs: 0,
308
- output: "No test command found. Pass --command or add a test script to package.json."
314
+ output: "No test command found. Pass --command, add a test script to package.json, or add pyproject.toml/pytest.ini/setup.cfg, go.mod or Cargo.toml."
309
315
  }
310
316
  };
311
317
  }
@@ -319,38 +325,13 @@ async function verifyChange(input) {
319
325
  )
320
326
  };
321
327
  }
322
- async function detectTestCommand(repositoryRoot) {
323
- const packagePath = join2(repositoryRoot, "package.json");
324
- try {
325
- const packageJson = JSON.parse(await readFile(packagePath, "utf8"));
326
- if (typeof packageJson.scripts?.test === "string") {
327
- return {
328
- command: `${packageManagerName(packageJson.packageManager)} test`,
329
- source: "package_script"
330
- };
331
- }
332
- } catch (error) {
333
- if (error.code !== "ENOENT") throw error;
334
- }
335
- for (const candidate of [
336
- ["pnpm-lock.yaml", "pnpm test"],
337
- ["yarn.lock", "yarn test"],
338
- ["bun.lockb", "bun test"],
339
- ["bun.lock", "bun test"],
340
- ["package-lock.json", "npm test"]
341
- ]) {
342
- try {
343
- await access2(join2(repositoryRoot, candidate[0]), constants2.F_OK);
344
- return { command: candidate[1], source: "package_manager" };
345
- } catch {
346
- }
347
- }
348
- return null;
349
- }
350
- function packageManagerName(value) {
351
- if (typeof value !== "string") return "npm";
352
- const name = value.split(/[\s@]/, 1)[0] ?? "npm";
353
- return ["npm", "pnpm", "yarn", "bun"].includes(name) ? name : "npm";
328
+ async function detectedTestCommand(repositoryRoot) {
329
+ const command = await detectTestCommand(repositoryRoot);
330
+ if (command === null) return null;
331
+ return {
332
+ command,
333
+ source: /^(npm|pnpm|yarn|bun) test$/.test(command) ? "package_script" : "project_marker"
334
+ };
354
335
  }
355
336
  function runTestCommand(command, source, repositoryRoot, timeoutMs) {
356
337
  if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
@@ -606,22 +587,22 @@ function testBadge(status) {
606
587
 
607
588
  // src/cli/commands/claude-desktop.ts
608
589
  import { existsSync } from "fs";
609
- import { mkdir as mkdir2, readFile as readFile2 } from "fs/promises";
610
- import { dirname as dirname2, join as join3, resolve as resolve2 } from "path";
590
+ import { mkdir as mkdir2, readFile } from "fs/promises";
591
+ import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
611
592
  import { fileURLToPath } from "url";
612
593
  function claudeDesktopConfigPath(home = process.env.HOME ?? "") {
613
594
  if (process.platform === "darwin") {
614
- return join3(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
595
+ return join2(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
615
596
  }
616
597
  if (process.platform === "win32") {
617
- return join3(process.env.APPDATA ?? home, "Claude", "claude_desktop_config.json");
598
+ return join2(process.env.APPDATA ?? home, "Claude", "claude_desktop_config.json");
618
599
  }
619
- return join3(home, ".config", "Claude", "claude_desktop_config.json");
600
+ return join2(home, ".config", "Claude", "claude_desktop_config.json");
620
601
  }
621
602
  async function readClaudeConfig(path) {
622
603
  if (!existsSync(path)) return {};
623
604
  try {
624
- return JSON.parse(await readFile2(path, "utf8"));
605
+ return JSON.parse(await readFile(path, "utf8"));
625
606
  } catch (error) {
626
607
  throw new Error(
627
608
  `Failed to parse Claude Desktop config at ${path}: ${error.message}`
@@ -796,25 +777,25 @@ function sharedRulesReaders(client) {
796
777
  // src/clients/cost.ts
797
778
  import { mkdtemp, rm as rm3 } from "fs/promises";
798
779
  import { tmpdir } from "os";
799
- import { join as join8, resolve as resolve7 } from "path";
780
+ import { join as join7, resolve as resolve7 } from "path";
800
781
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
801
782
  import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
802
783
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
803
784
 
804
785
  // src/templates/install.ts
805
- import { readFile as readFile7, rm as rm2 } from "fs/promises";
806
- import { join as join7, resolve as resolve6 } from "path";
786
+ import { readFile as readFile6, rm as rm2 } from "fs/promises";
787
+ import { join as join6, resolve as resolve6 } from "path";
807
788
 
808
789
  // src/hooks/hermes-plugin.ts
809
- import { readFile as readFile4 } from "fs/promises";
810
- import { dirname as dirname4, join as join4, resolve as resolve3 } from "path";
790
+ import { readFile as readFile3 } from "fs/promises";
791
+ import { dirname as dirname4, join as join3, resolve as resolve3 } from "path";
811
792
 
812
793
  // src/hooks/managed-plugin.ts
813
- import { mkdir as mkdir3, readFile as readFile3, rm, rmdir } from "fs/promises";
794
+ import { mkdir as mkdir3, readFile as readFile2, rm, rmdir } from "fs/promises";
814
795
  import { dirname as dirname3 } from "path";
815
796
  async function read(path) {
816
797
  try {
817
- return await readFile3(path, "utf8");
798
+ return await readFile2(path, "utf8");
818
799
  } catch (error) {
819
800
  if (error.code === "ENOENT") return null;
820
801
  throw error;
@@ -1010,7 +991,7 @@ def register(ctx):
1010
991
  }
1011
992
  var HERMES_PLUGIN_SOURCE = renderHermesPlugin("full");
1012
993
  function hermesPluginDir(repositoryRoot) {
1013
- return join4(resolve3(repositoryRoot), ".hermes", "plugins", "codebuddy");
994
+ return join3(resolve3(repositoryRoot), ".hermes", "plugins", "codebuddy");
1014
995
  }
1015
996
  function plugin(repositoryRoot, variant = "full") {
1016
997
  const dir = hermesPluginDir(repositoryRoot);
@@ -1018,10 +999,10 @@ function plugin(repositoryRoot, variant = "full") {
1018
999
  marker: HERMES_PLUGIN_MARKER,
1019
1000
  files: [
1020
1001
  {
1021
- path: join4(dir, "plugin.yaml"),
1002
+ path: join3(dir, "plugin.yaml"),
1022
1003
  content: variant === "lean" ? HERMES_LEAN_MANIFEST : HERMES_PLUGIN_MANIFEST
1023
1004
  },
1024
- { path: join4(dir, "__init__.py"), content: renderHermesPlugin(variant) }
1005
+ { path: join3(dir, "__init__.py"), content: renderHermesPlugin(variant) }
1025
1006
  ],
1026
1007
  dirs: [dir, dirname4(dir), dirname4(dirname4(dir))]
1027
1008
  };
@@ -1034,13 +1015,13 @@ function setHermesPlugin(repositoryRoot, enabled, variant = "full") {
1034
1015
  }
1035
1016
  async function hermesPluginVariant(repositoryRoot) {
1036
1017
  if (!await hermesPluginInstalled(repositoryRoot)) return null;
1037
- const source = await readFile4(join4(hermesPluginDir(repositoryRoot), "__init__.py"), "utf8");
1018
+ const source = await readFile3(join3(hermesPluginDir(repositoryRoot), "__init__.py"), "utf8");
1038
1019
  return source.startsWith(`${HERMES_PLUGIN_MARKER} v1 lean`) ? "lean" : "full";
1039
1020
  }
1040
1021
 
1041
1022
  // src/hooks/opencode-plugin.ts
1042
- import { readFile as readFile5 } from "fs/promises";
1043
- import { dirname as dirname5, join as join5, resolve as resolve4 } from "path";
1023
+ import { readFile as readFile4 } from "fs/promises";
1024
+ import { dirname as dirname5, join as join4, resolve as resolve4 } from "path";
1044
1025
  var OPENCODE_PLUGIN_MARKER = "// codebuddy-managed: opencode-plugin";
1045
1026
  var OPENCODE_RUNTIME = `const ID_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1046
1027
  let lastMs = 0;
@@ -1206,7 +1187,7 @@ export const CodeBuddyPlugin = async ({ directory }) => {
1206
1187
  }
1207
1188
  var OPENCODE_PLUGIN_SOURCE = renderOpencodePlugin("full");
1208
1189
  function opencodePluginPath(repositoryRoot) {
1209
- return join5(resolve4(repositoryRoot), ".opencode", "plugins", "codebuddy.js");
1190
+ return join4(resolve4(repositoryRoot), ".opencode", "plugins", "codebuddy.js");
1210
1191
  }
1211
1192
  function plugin2(repositoryRoot, variant = "full") {
1212
1193
  const path = opencodePluginPath(repositoryRoot);
@@ -1224,17 +1205,17 @@ function setOpencodePlugin(repositoryRoot, enabled, variant = "full") {
1224
1205
  }
1225
1206
  async function opencodePluginVariant(repositoryRoot) {
1226
1207
  if (!await opencodePluginInstalled(repositoryRoot)) return null;
1227
- const source = await readFile5(opencodePluginPath(repositoryRoot), "utf8");
1208
+ const source = await readFile4(opencodePluginPath(repositoryRoot), "utf8");
1228
1209
  return source.startsWith(`${OPENCODE_PLUGIN_MARKER} v1 lean`) ? "lean" : "full";
1229
1210
  }
1230
1211
 
1231
1212
  // src/hooks/settings.ts
1232
- import { mkdir as mkdir4, readFile as readFile6 } from "fs/promises";
1233
- import { dirname as dirname6, join as join6, resolve as resolve5 } from "path";
1213
+ import { mkdir as mkdir4, readFile as readFile5 } from "fs/promises";
1214
+ import { dirname as dirname6, join as join5, resolve as resolve5 } from "path";
1234
1215
  var HOOK_COMMAND_PREFIX = "codebuddy hooks";
1235
1216
  var TOOL_GUARD_DEF = {
1236
1217
  event: "PreToolUse",
1237
- matcher: "Read|Grep",
1218
+ matcher: "Read|Grep|Bash",
1238
1219
  command: `${HOOK_COMMAND_PREFIX} pretool`
1239
1220
  };
1240
1221
  var HOOK_DEFS = [
@@ -1256,7 +1237,7 @@ var LEAN_HOOK_DEFS = [
1256
1237
  TOOL_GUARD_DEF
1257
1238
  ];
1258
1239
  function claudeSettingsPath(repositoryRoot = process.cwd()) {
1259
- return join6(resolve5(repositoryRoot), ".claude", "settings.json");
1240
+ return join5(resolve5(repositoryRoot), ".claude", "settings.json");
1260
1241
  }
1261
1242
  function upsertHooks(settings, variant = "full") {
1262
1243
  const defs = variant === "lean" ? LEAN_HOOK_DEFS : HOOK_DEFS;
@@ -1335,7 +1316,7 @@ function hooksVariant(settings) {
1335
1316
  }
1336
1317
  async function readSettings(path) {
1337
1318
  try {
1338
- return JSON.parse(await readFile6(path, "utf8"));
1319
+ return JSON.parse(await readFile5(path, "utf8"));
1339
1320
  } catch (error) {
1340
1321
  if (error.code === "ENOENT") return {};
1341
1322
  throw error;
@@ -1439,12 +1420,16 @@ var START_HOOKS = [
1439
1420
  "follow the agent-driven flow: `context_pack` first, `context_after_turn` last.",
1440
1421
  "",
1441
1422
  "1. Start from the injected block; do not call `context_bootstrap` for orientation",
1442
- " it already covers.",
1443
- "2. Call `context_pack` (or `context_before_edit` with exact paths) only when the",
1423
+ " it already covers. Its `task files` are CodeBuddy's best match; when it names",
1424
+ " none, call `context_pack` with the task before searching the repository yourself.",
1425
+ "2. Read ranges, not whole files: `Read` with offset/limit, or `sed -n 'A,Bp'`.",
1426
+ " Large files read whole (`Read` without a range, `cat`, `less`) are denied with an",
1427
+ " outline of their symbols and line ranges; read the range you need from it.",
1428
+ "3. Call `context_pack` (or `context_before_edit` with exact paths) when the",
1444
1429
  " task needs symbols, neighbours, or file-level evidence the block lacks. Pass the",
1445
1430
  " block's `session` value as `sessionId` and the previous `delivery.deliveryId`",
1446
1431
  " as `ack` so unchanged sections are not resent.",
1447
- "3. Prior work from earlier chats arrives as handoffs with freshness; re-check",
1432
+ "4. Prior work from earlier chats arrives as handoffs with freshness; re-check",
1448
1433
  " those files before relying on it.",
1449
1434
  ""
1450
1435
  ];
@@ -1547,7 +1532,7 @@ ${END_MARKER}
1547
1532
  }
1548
1533
  async function installWorkflowRules(options) {
1549
1534
  const projectRoot = resolve6(options.projectRoot ?? process.cwd());
1550
- const path = options.targetPath ?? join7(projectRoot, defaultRulesFile(options.client));
1535
+ const path = options.targetPath ?? join6(projectRoot, defaultRulesFile(options.client));
1551
1536
  const existing = await readIfPresent(path);
1552
1537
  const mode = options.mode ?? await defaultRulesMode(options.client, projectRoot);
1553
1538
  if (options.onlyIfManaged && (existing === null || !hasManagedBlock(existing))) {
@@ -1602,7 +1587,7 @@ function removeBlock(existing) {
1602
1587
  }
1603
1588
  async function readIfPresent(path) {
1604
1589
  try {
1605
- return await readFile7(path, "utf8");
1590
+ return await readFile6(path, "utf8");
1606
1591
  } catch (error) {
1607
1592
  if (error.code === "ENOENT") return null;
1608
1593
  throw error;
@@ -1639,7 +1624,7 @@ async function hookBlockTokens(root, namespace) {
1639
1624
  namespace,
1640
1625
  useGit: true
1641
1626
  }).catch(() => null);
1642
- const scratch = await mkdtemp(join8(tmpdir(), "codebuddy-cost-"));
1627
+ const scratch = await mkdtemp(join7(tmpdir(), "codebuddy-cost-"));
1643
1628
  try {
1644
1629
  const ledger = new SessionCapsuleLedger(scratch);
1645
1630
  const render = () => renderContextBlock(bootstrap, beforeEdit, ledger, "cost", true);
@@ -1692,14 +1677,14 @@ async function measureClientCosts(projectRoot = process.cwd(), namespace) {
1692
1677
  }
1693
1678
 
1694
1679
  // src/clients/install.ts
1695
- import { access as access4 } from "fs/promises";
1696
- import { join as join12, resolve as resolve9 } from "path";
1680
+ import { access as access3 } from "fs/promises";
1681
+ import { join as join11, resolve as resolve9 } from "path";
1697
1682
 
1698
1683
  // src/integrations/project-mcp.ts
1699
- import { access as access3, mkdir as mkdir5, readFile as readFile8, rm as rm4 } from "fs/promises";
1700
- import { dirname as dirname7, join as join9, resolve as resolve8 } from "path";
1684
+ import { access as access2, mkdir as mkdir5, readFile as readFile7, rm as rm4 } from "fs/promises";
1685
+ import { dirname as dirname7, join as join8, resolve as resolve8 } from "path";
1701
1686
  async function writeProjectMcpConfig(options) {
1702
- const path = join9(options.repositoryRoot, ".mcp.json");
1687
+ const path = join8(options.repositoryRoot, ".mcp.json");
1703
1688
  const current = await readProjectMcpConfig(path);
1704
1689
  current.mcpServers ??= {};
1705
1690
  current.mcpServers[`codebuddy-${options.namespace}`] = codebuddyMcpServer(
@@ -1736,12 +1721,12 @@ function codebuddyMcpServer(repositoryRoot, namespace) {
1736
1721
  };
1737
1722
  }
1738
1723
  async function projectMcpPath(root, file, format) {
1739
- const path = join9(root, file);
1724
+ const path = join8(root, file);
1740
1725
  if (format !== "opencode" || await exists(path)) return path;
1741
1726
  return await exists(`${path}c`) ? `${path}c` : path;
1742
1727
  }
1743
1728
  async function exists(path) {
1744
- return access3(path).then(
1729
+ return access2(path).then(
1745
1730
  () => true,
1746
1731
  () => false
1747
1732
  );
@@ -1802,7 +1787,7 @@ function parseJsonc(raw) {
1802
1787
  }
1803
1788
  async function readConfig(path) {
1804
1789
  try {
1805
- const { value, comments } = parseJsonc(await readFile8(path, "utf8"));
1790
+ const { value, comments } = parseJsonc(await readFile7(path, "utf8"));
1806
1791
  const config = value && typeof value === "object" ? value : {};
1807
1792
  return { config, comments };
1808
1793
  } catch (error) {
@@ -1816,15 +1801,15 @@ async function readProjectMcpConfig(path) {
1816
1801
 
1817
1802
  // src/cli/commands/codex.ts
1818
1803
  import { existsSync as existsSync2 } from "fs";
1819
- import { mkdir as mkdir6, readFile as readFile9 } from "fs/promises";
1804
+ import { mkdir as mkdir6, readFile as readFile8 } from "fs/promises";
1820
1805
  import { homedir } from "os";
1821
- import { dirname as dirname8, join as join10 } from "path";
1806
+ import { dirname as dirname8, join as join9 } from "path";
1822
1807
  function codexConfigPath(home = homedir()) {
1823
- return join10(home, ".codex", "config.toml");
1808
+ return join9(home, ".codex", "config.toml");
1824
1809
  }
1825
1810
  async function readCodexConfig(path) {
1826
1811
  if (!existsSync2(path)) return "";
1827
- return readFile9(path, "utf8");
1812
+ return readFile8(path, "utf8");
1828
1813
  }
1829
1814
  async function writeCodexConfig(path, content) {
1830
1815
  await mkdir6(dirname8(path), { recursive: true });
@@ -1930,17 +1915,17 @@ function escapeRegExp(value) {
1930
1915
  }
1931
1916
 
1932
1917
  // src/integrations/hermes-config.ts
1933
- import { mkdir as mkdir7, readFile as readFile10 } from "fs/promises";
1918
+ import { mkdir as mkdir7, readFile as readFile9 } from "fs/promises";
1934
1919
  import { homedir as homedir2 } from "os";
1935
- import { dirname as dirname9, join as join11 } from "path";
1920
+ import { dirname as dirname9, join as join10 } from "path";
1936
1921
  import { isDeepStrictEqual } from "util";
1937
1922
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
1938
1923
  function hermesConfigPath(home = homedir2()) {
1939
- return join11(process.env.HERMES_HOME ?? join11(home, ".hermes"), "config.yaml");
1924
+ return join10(process.env.HERMES_HOME ?? join10(home, ".hermes"), "config.yaml");
1940
1925
  }
1941
1926
  async function readRaw(path) {
1942
1927
  try {
1943
- return await readFile10(path, "utf8");
1928
+ return await readFile9(path, "utf8");
1944
1929
  } catch (error) {
1945
1930
  if (error.code === "ENOENT") return "";
1946
1931
  throw error;
@@ -2062,8 +2047,8 @@ async function installClient(options) {
2062
2047
  return apply(options, options.mode, options.mode !== "lean");
2063
2048
  }
2064
2049
  async function ensureIndex(root) {
2065
- const target = join12(root, ".codebuddy", "cache", "index.json");
2066
- const existed = await access4(target).then(
2050
+ const target = join11(root, ".codebuddy", "cache", "index.json");
2051
+ const existed = await access3(target).then(
2067
2052
  () => true,
2068
2053
  () => false
2069
2054
  );
@@ -2083,7 +2068,7 @@ async function apply(options, mode, keepMcp) {
2083
2068
  const shared = sharedRulesReaders(options.client);
2084
2069
  const liveSiblings = mode === "mcp-only" || mode === "lean" ? await installedWithMcp(root, shared, namespace) : [];
2085
2070
  if (liveSiblings.length > 0) {
2086
- const path = join12(root, adapter.rulesFile);
2071
+ const path = join11(root, adapter.rulesFile);
2087
2072
  steps.push({ surface: "rules", target: path, action: "kept" });
2088
2073
  notes.push(
2089
2074
  `Kept the CodeBuddy block in ${adapter.rulesFile}: still used by ${liveSiblings.map((id) => CLIENT_ADAPTERS[id].name).join(", ")}.`
@@ -2163,7 +2148,7 @@ async function setHooks(root, enabled, client = "claude", variant = "full") {
2163
2148
  async function detectClient(client, projectRoot = process.cwd(), namespace) {
2164
2149
  const root = resolve9(projectRoot);
2165
2150
  const adapter = CLIENT_ADAPTERS[client];
2166
- const rulesMode = await readManagedMode(join12(root, adapter.rulesFile));
2151
+ const rulesMode = await readManagedMode(join11(root, adapter.rulesFile));
2167
2152
  const hooks = await clientHooksInstalled(root, client);
2168
2153
  const name = `codebuddy-${namespace ?? await resolveNamespaceFrom(root)}`;
2169
2154
  const mcp = "file" in adapter.mcp ? await hasMcpServer(
@@ -2189,8 +2174,8 @@ async function detectClient(client, projectRoot = process.cwd(), namespace) {
2189
2174
 
2190
2175
  // src/clients/verify.ts
2191
2176
  import { execFile } from "child_process";
2192
- import { readFile as readFile11, realpath } from "fs/promises";
2193
- import { join as join13, resolve as resolve10 } from "path";
2177
+ import { readFile as readFile10, realpath } from "fs/promises";
2178
+ import { join as join12, resolve as resolve10 } from "path";
2194
2179
  import { pathToFileURL } from "url";
2195
2180
  var runHost = (bin, args, cwd) => new Promise((done) => {
2196
2181
  execFile(
@@ -2225,7 +2210,7 @@ var CONFORMANCE = {
2225
2210
  if (!config) return [fail("config", "opencode debug config returned no JSON")];
2226
2211
  const installed = await clientHooksInstalled(root, "opencode");
2227
2212
  const href = pathToFileURL(
2228
- join13(await realpath(root).catch(() => root), ".opencode", "plugins", "codebuddy.js")
2213
+ join12(await realpath(root).catch(() => root), ".opencode", "plugins", "codebuddy.js")
2229
2214
  ).href;
2230
2215
  const loaded = config.plugin?.includes(href) === true;
2231
2216
  return [
@@ -2252,7 +2237,7 @@ var CONFORMANCE = {
2252
2237
  return checks;
2253
2238
  }
2254
2239
  const projectPlugins = /^(1|true|yes|on)$/i.test(env.HERMES_ENABLE_PROJECT_PLUGINS ?? "") || /^\s*HERMES_ENABLE_PROJECT_PLUGINS\s*=\s*["']?(1|true|yes|on)/im.test(
2255
- await readFile11(join13(hermesConfigPath(), "..", ".env"), "utf8").catch(() => "")
2240
+ await readFile10(join12(hermesConfigPath(), "..", ".env"), "utf8").catch(() => "")
2256
2241
  );
2257
2242
  const plugins = (await readHermesConfig(hermesConfigPath()))?.plugins;
2258
2243
  const enabled = Array.isArray(plugins?.enabled) && plugins.enabled.includes("codebuddy");
@@ -2668,13 +2653,16 @@ th.n { text-align: right; }
2668
2653
  .chip { display: inline-block; font: 600 10px var(--mono); text-transform: uppercase; letter-spacing: .06em; padding: 2px 6px; border-radius: 4px; }
2669
2654
  .chip.recall { background: var(--recall-soft); color: var(--recall); }
2670
2655
  .chip.tool { background: var(--tool-soft); color: var(--tool); }
2671
- .chip.capture, .chip.sync, .chip.kw { background: var(--line); color: var(--capture); }
2656
+ .chip.capture, .chip.sync, .chip.guard, .chip.kw { background: var(--line); color: var(--capture); }
2672
2657
  .chip.err { background: var(--bad-soft); color: var(--bad); }
2673
2658
  .feed { display: grid; gap: 10px; max-height: 520px; overflow: auto; }
2674
2659
  .prompt { border: 1px solid var(--line); border-radius: 8px; padding: 10px 12px; }
2675
2660
  .prompt .q { font-weight: 500; overflow-wrap: anywhere; }
2676
2661
  .prompt .meta { font: 12px var(--mono); color: var(--muted); margin-top: 4px; display: flex; flex-wrap: wrap; gap: 4px 12px; overflow-wrap: anywhere; }
2677
2662
  .prompt ul { margin: 6px 0 0; padding-left: 18px; font-size: 13px; }
2663
+ .prompt details { margin-top: 6px; font-size: 12px; }
2664
+ .prompt summary { cursor: pointer; color: var(--muted); }
2665
+ .prompt pre { margin: 6px 0 0; padding: 8px 10px; background: var(--bg); border: 1px solid var(--line); border-radius: 6px; font: 12px/1.45 var(--mono); white-space: pre-wrap; overflow-wrap: anywhere; max-height: 260px; overflow: auto; }
2678
2666
  .prompt .none { font-size: 13px; color: var(--muted); margin-top: 6px; }
2679
2667
  .mono { font-family: var(--mono); }
2680
2668
  .muted { color: var(--muted); }
@@ -2753,7 +2741,10 @@ th.n { text-align: right; }
2753
2741
  function kpis(list) {
2754
2742
  var recalls = list.filter(function (e) { return e.kind === "recall"; });
2755
2743
  var tools = list.filter(function (e) { return e.kind === "tool"; });
2756
- var delivered = list.reduce(function (s, e) { return s + (e.kind === "recall" || e.kind === "tool" ? e.tokensOut || 0 : 0); }, 0);
2744
+ var injected = recalls.reduce(function (s, e) { return s + (e.tokensOut || 0); }, 0);
2745
+ var returned = tools.reduce(function (s, e) { return s + (e.tokensOut || 0); }, 0);
2746
+ var guards = list.filter(function (e) { return e.kind === "guard"; });
2747
+ var avoided = guards.reduce(function (s, e) { return s + ((e.detail && e.detail.avoided) || 0); }, 0);
2757
2748
  var withFacts = recalls.filter(function (e) { return e.detail && e.detail.facts && e.detail.facts.length; });
2758
2749
  var factCount = withFacts.reduce(function (s, e) { return s + e.detail.facts.length; }, 0);
2759
2750
  var dense = recalls.reduce(function (s, e) { return s + ((e.detail && e.detail.dense) || 0); }, 0);
@@ -2764,7 +2755,8 @@ th.n { text-align: right; }
2764
2755
  var tiles = [
2765
2756
  ["Prompts recalled", num(recalls.length), recalls.length ? Math.round(100 * withFacts.length / recalls.length) + "% got facts" : "no prompts yet"],
2766
2757
  ["Facts injected", num(factCount), factCount ? Math.round(100 * dense / factCount) + "% matched by meaning" : "-"],
2767
- ["Tokens delivered", compact(delivered), recalls.length + tools.length ? "\u2248 " + num(delivered / (recalls.length + tools.length)) + " per event" : "-"],
2758
+ ["Tokens added to context", compact(injected + returned), compact(injected) + " by hooks, " + compact(returned) + " by MCP tools"],
2759
+ ["Tokens kept out (measured)", compact(avoided), guards.length ? guards.length + " large read" + (guards.length === 1 ? "" : "s") + " replaced by an outline" : "no large reads blocked"],
2768
2760
  ["MCP tool calls", num(tools.length), tools.length ? new Set(tools.map(function (e) { return e.name; })).size + " distinct tools" : "-"],
2769
2761
  ["Recall latency", lat.length ? ms(pct(lat, 0.5)) : "-", lat.length ? "p95 " + ms(pct(lat, 0.95)) : "-"],
2770
2762
  ["Memories saved", num(saved), captures.length + " capture" + (captures.length === 1 ? "" : "s")],
@@ -2834,6 +2826,7 @@ th.n { text-align: right; }
2834
2826
  (e.ok ? "" : '<span class="chip err">' + esc(d.dropped || "dropped") + "</span>") + "</div>" +
2835
2827
  (facts.length ? "<ul>" + facts.map(function (f) { return "<li>" + esc(f) + "</li>"; }).join("") + "</ul>" : '<div class="none">No new facts: nothing matched, or already sent this session.</div>') +
2836
2828
  (d.files && d.files.length ? '<div class="meta">files: ' + d.files.map(esc).join(", ") + "</div>" : "") +
2829
+ (d.block ? '<details><summary>What the agent received</summary><pre>' + esc(d.block) + "</pre></details>" : "") +
2837
2830
  "</div>";
2838
2831
  }).join("");
2839
2832
  }
@@ -2858,6 +2851,7 @@ th.n { text-align: right; }
2858
2851
  var d = e.detail || {};
2859
2852
  if (e.kind === "recall") return (d.facts ? d.facts.length : 0) + " fact(s), " + (d.files ? d.files.length : 0) + " file(s)";
2860
2853
  if (e.kind === "capture") return (d.saved || 0) + " saved, " + (d.queued || 0) + " queued, " + (d.changedFiles || 0) + " changed file(s)";
2854
+ if (e.kind === "guard") return "denied " + (d.file || "a large read") + ", \u2248" + num(d.avoided || 0) + " tokens kept out";
2861
2855
  if (e.kind === "sync") return (d.imported || 0) + " new, " + (d.updated || 0) + " updated" + (d.reembedding ? ", " + d.reembedding + " re-embedding" : "");
2862
2856
  return e.session ? "session " + e.session.slice(0, 12) : "";
2863
2857
  }
@@ -3082,13 +3076,13 @@ function registerDashboardCommand(program, runSafely2) {
3082
3076
  import pc5 from "picocolors";
3083
3077
 
3084
3078
  // src/eval/runner.ts
3085
- import { readFile as readFile12 } from "fs/promises";
3086
- import { dirname as dirname10, isAbsolute, join as join14, resolve as resolve11 } from "path";
3079
+ import { readFile as readFile11 } from "fs/promises";
3080
+ import { dirname as dirname10, isAbsolute, join as join13, resolve as resolve11 } from "path";
3087
3081
  async function runEvaluation(input = {}) {
3088
3082
  const datasetPath = resolve11(
3089
- input.datasetPath ?? join14(process.cwd(), "evals", "change-readiness.json")
3083
+ input.datasetPath ?? join13(process.cwd(), "evals", "change-readiness.json")
3090
3084
  );
3091
- const dataset = JSON.parse(await readFile12(datasetPath, "utf8"));
3085
+ const dataset = JSON.parse(await readFile11(datasetPath, "utf8"));
3092
3086
  if (dataset.version !== 1 || !Array.isArray(dataset.cases)) {
3093
3087
  throw new Error(`Unsupported evaluation dataset: ${datasetPath}`);
3094
3088
  }
@@ -3197,7 +3191,7 @@ function registerHooksCommands(program, runSafely2) {
3197
3191
  console.log(
3198
3192
  `${changed ? pc7.green("installed") : pc7.dim("unchanged")} CodeBuddy hooks in ${where}`
3199
3193
  );
3200
- await reconcileRules(client, "hooks");
3194
+ await reconcileRules(client, "hooks", { create: true });
3201
3195
  warnIfHookCommandStale();
3202
3196
  syncInBackground(process.cwd(), true);
3203
3197
  console.log(
@@ -3279,7 +3273,7 @@ function registerHooksCommands(program, runSafely2) {
3279
3273
  });
3280
3274
  hooks.command("reset").description("[hook] Forget what context was sent this session (after compaction or /clear).").action(async () => {
3281
3275
  await failSoft(async () => {
3282
- await runResetHook(await readHookInput());
3276
+ await runResetHook(await readHookInput(), writeStdout);
3283
3277
  });
3284
3278
  });
3285
3279
  }
@@ -3293,12 +3287,16 @@ function assertHookClient(value) {
3293
3287
  }
3294
3288
  return client;
3295
3289
  }
3296
- async function reconcileRules(client, mode) {
3297
- const result = await installWorkflowRules({ client, mode, onlyIfManaged: true });
3290
+ async function reconcileRules(client, mode, options = {}) {
3291
+ const result = await installWorkflowRules({
3292
+ client,
3293
+ mode,
3294
+ ...options.create ? {} : { onlyIfManaged: true }
3295
+ });
3298
3296
  if (result.action !== "unchanged") {
3299
3297
  const where = relative2(process.cwd(), result.path) || result.path;
3300
3298
  console.log(
3301
- `${pc7.cyan("updated")} ${where} workflow to ${mode === "hooks" ? "hook" : "agent"}-driven mode`
3299
+ `${pc7.cyan(result.action === "created" ? "added" : "updated")} ${where} workflow (${mode === "hooks" ? "hook" : "agent"}-driven mode)`
3302
3300
  );
3303
3301
  }
3304
3302
  }
@@ -3458,15 +3456,27 @@ function registerLlmCommands(program, runSafely2) {
3458
3456
  }
3459
3457
 
3460
3458
  // src/cli/commands/indexing.ts
3459
+ var VECTOR_BUDGET_MS = 10 * 6e4;
3461
3460
  function registerIndexCommands(program, runSafely2) {
3462
- program.command("index").option("--full", "Re-summarize every file, ignoring existing content hashes.").option("--json", "Print machine-readable JSON.").description("Index the repository: content hashes and lightweight file summaries.").action(async (opts) => {
3461
+ program.command("index").option("--full", "Re-summarize every file, ignoring existing content hashes.").option("--json", "Print machine-readable JSON.").option("--no-vectors", "Skip embedding source files for task \u2192 file ordering.").description("Index the repository: content hashes and lightweight file summaries.").action(async (opts) => {
3463
3462
  await runSafely2(async () => {
3464
3463
  const result = await indexRepository({ ...opts.full ? { full: true } : {} });
3464
+ const local = opts.vectors && (await loadRuntimeConfig(process.cwd(), { requireDatabase: false }).catch(() => null))?.provider.type === "local";
3465
+ const vectors = local ? await syncFileVectors(process.cwd(), VECTOR_BUDGET_MS) : null;
3465
3466
  if (opts.json) {
3466
- console.log(JSON.stringify(result, null, 2));
3467
+ console.log(JSON.stringify({ ...result, ...vectors ? { vectors } : {} }, null, 2));
3467
3468
  return;
3468
3469
  }
3469
3470
  printResult(result);
3471
+ if (vectors) {
3472
+ console.log(
3473
+ pc9.dim(
3474
+ ` file vectors: ${vectors.embedded} embedded, ${vectors.files} current` + (vectors.pending > 0 ? `, ${vectors.pending} left for the next run` : "")
3475
+ )
3476
+ );
3477
+ } else if (local) {
3478
+ console.log(pc9.dim(" file vectors: skipped (local embedding runtime not available)"));
3479
+ }
3470
3480
  });
3471
3481
  });
3472
3482
  program.command("watch").option("--debounce <ms>", "Coalesce file-change bursts within this window.", Number).option("--no-llm", "Do not write BYOK model summaries after index passes.").description("Watch the repository and incrementally re-index on change (Ctrl+C to stop).").action(async (opts) => {
@@ -3862,7 +3872,7 @@ function openEditor(path) {
3862
3872
 
3863
3873
  // src/cli/commands/models.ts
3864
3874
  import { existsSync as existsSync3 } from "fs";
3865
- import { join as join15 } from "path";
3875
+ import { join as join14 } from "path";
3866
3876
  import { spinner as spinner2 } from "@clack/prompts";
3867
3877
  import pc13 from "picocolors";
3868
3878
 
@@ -4111,12 +4121,15 @@ async function calibrateEmbedder(embed) {
4111
4121
  (_, i) => at(Math.round((0.05 + i * 0.01) * 100) / 100, "dev")
4112
4122
  );
4113
4123
  const best = sweep.reduce((left, right) => right.f05 > left.f05 ? right : left);
4124
+ const f2 = ({ precision, recall }) => precision + recall === 0 ? 0 : 5 * precision * recall / (4 * precision + recall);
4125
+ const recallBest = sweep.reduce((left, right) => f2(right) > f2(left) ? right : left);
4114
4126
  return {
4115
4127
  auc,
4116
4128
  recallAt1: answered === 0 ? 0 : hits / answered,
4117
4129
  mrr: answered === 0 ? 0 : reciprocal / answered,
4118
4130
  best,
4119
4131
  test: at(best.floor, "test"),
4132
+ recall: { dev: recallBest, test: at(recallBest.floor, "test") },
4120
4133
  at
4121
4134
  };
4122
4135
  }
@@ -4144,8 +4157,8 @@ function keyPresent(provider, apiKey) {
4144
4157
  return Boolean(process.env[KEY_ENV2[provider]] || provider === "huggingface" && apiKey);
4145
4158
  }
4146
4159
  function localModelDownloaded(id, revision) {
4147
- const dir = join15(modelCacheDir(), id, ...revision ? [revision] : []);
4148
- return existsSync3(join15(dir, "onnx", "model_quantized.onnx"));
4160
+ const dir = join14(modelCacheDir(), id, ...revision ? [revision] : []);
4161
+ return existsSync3(join14(dir, "onnx", "model_quantized.onnx"));
4149
4162
  }
4150
4163
  async function vectorCounts(postgresUrl, namespace, space) {
4151
4164
  const client = createDatabaseClient({ postgresUrl, maxConnections: 1, connectTimeoutSeconds: 3 });
@@ -4358,7 +4371,7 @@ before keeping one on. A reranker never admits a fact, it only orders them.`
4358
4371
  });
4359
4372
  models.command("pull").description("Download the local models now instead of on first use.").action(async () => {
4360
4373
  await runSafely2(async () => {
4361
- const { LocalModelProvider, rerankLocally } = await import("./local-5MZURLCZ.js");
4374
+ const { LocalModelProvider, rerankLocally } = await import("./local-ST4WMRCG.js");
4362
4375
  const file = await readConfigFile();
4363
4376
  let started = Date.now();
4364
4377
  await withSpinner(
@@ -4403,13 +4416,24 @@ before keeping one on. A reranker never admits a fact, it only orders them.`
4403
4416
  `${result.best.floor}: precision ${pct(result.test.precision)}, recall ${pct(result.test.recall)}, silent on ${pct(result.test.abstain)} of unrelated tasks ${pc13.dim("(chosen on dev, measured on test)")}`
4404
4417
  )
4405
4418
  );
4419
+ console.log(
4420
+ row(
4421
+ "recall floor",
4422
+ `${result.recall.dev.floor}: precision ${pct(result.recall.test.precision)}, recall ${pct(result.recall.test.recall)} ` + pc13.dim("(explicit recall: best F2 on dev, measured on test)")
4423
+ )
4424
+ );
4406
4425
  if (opts.dryRun) return;
4407
4426
  const file = await readConfigFile();
4408
4427
  await setConfigBlock("provider", {
4409
4428
  ...file.provider ?? { type: config.provider.type },
4410
- relevanceFloor: result.best.floor
4429
+ relevanceFloor: result.best.floor,
4430
+ recallFloor: result.recall.dev.floor
4411
4431
  });
4412
- console.log(ok(`Saved relevanceFloor ${result.best.floor} to .codebuddy/config.json`));
4432
+ console.log(
4433
+ ok(
4434
+ `Saved relevanceFloor ${result.best.floor} and recallFloor ${result.recall.dev.floor} to .codebuddy/config.json`
4435
+ )
4436
+ );
4413
4437
  });
4414
4438
  });
4415
4439
  }
@@ -4809,7 +4833,7 @@ function severityBadge2(severity) {
4809
4833
  }
4810
4834
 
4811
4835
  // src/cli/commands/symbols.ts
4812
- import { readFile as readFile13 } from "fs/promises";
4836
+ import { readFile as readFile12 } from "fs/promises";
4813
4837
  import { resolve as resolve12 } from "path";
4814
4838
  import pc18 from "picocolors";
4815
4839
  function registerSymbolsCommand(program, runSafely2) {
@@ -4819,8 +4843,8 @@ function registerSymbolsCommand(program, runSafely2) {
4819
4843
  console.log(pc18.dim(`no symbol extraction for ${path} (unsupported language)`));
4820
4844
  return;
4821
4845
  }
4822
- const content = await readFile13(resolve12(path), "utf8");
4823
- const table = extractSymbolTable(content);
4846
+ const content = await readFile12(resolve12(path), "utf8");
4847
+ const table = extractSymbolTable(content, path);
4824
4848
  if (opts.json) {
4825
4849
  console.log(JSON.stringify({ path, symbols: table }, null, 2));
4826
4850
  return;
@@ -4847,9 +4871,9 @@ import { confirm as confirm2, isCancel as isCancel2, log as log2, note as note2,
4847
4871
  import pc19 from "picocolors";
4848
4872
 
4849
4873
  // src/core/global-config.ts
4850
- import { chmod, mkdir as mkdir8, readFile as readFile14, writeFile as writeFile3 } from "fs/promises";
4874
+ import { chmod, mkdir as mkdir8, readFile as readFile13, writeFile as writeFile3 } from "fs/promises";
4851
4875
  import { homedir as homedir3 } from "os";
4852
- import { dirname as dirname11, join as join16 } from "path";
4876
+ import { dirname as dirname11, join as join15 } from "path";
4853
4877
  import { z as z2 } from "zod";
4854
4878
  var GLOBAL_DIR = ".codebuddy";
4855
4879
  var GLOBAL_FILE = "global.json";
@@ -4862,12 +4886,12 @@ var globalConfigSchema = z2.object({
4862
4886
  installPath: z2.string().optional()
4863
4887
  });
4864
4888
  function globalConfigPath(home = homedir3()) {
4865
- return join16(home, GLOBAL_DIR, GLOBAL_FILE);
4889
+ return join15(home, GLOBAL_DIR, GLOBAL_FILE);
4866
4890
  }
4867
4891
  async function readGlobalConfig(home = homedir3()) {
4868
4892
  const path = globalConfigPath(home);
4869
4893
  try {
4870
- const raw = await readFile14(path, "utf8");
4894
+ const raw = await readFile13(path, "utf8");
4871
4895
  return globalConfigSchema.parse(JSON.parse(raw));
4872
4896
  } catch (error) {
4873
4897
  if (error.code === "ENOENT") return {};
@@ -4916,16 +4940,17 @@ async function runUseCommand(options = {}) {
4916
4940
  const sigintHandler = () => bail2();
4917
4941
  process.on("SIGINT", sigintHandler);
4918
4942
  try {
4943
+ const interactive = options.interactive ?? process.stdin.isTTY === true;
4919
4944
  const folderName = basename2(process.cwd());
4920
4945
  const namespace = sanitizeNamespace(options.namespace ?? folderName);
4921
4946
  await initProjectScaffold();
4922
4947
  if (options.namespace === void 0) await migrateLegacyNamespace();
4923
- const graphifyEnabled = options.graphify ?? unwrap2(
4948
+ const graphifyEnabled = options.graphify ?? (interactive && unwrap2(
4924
4949
  await confirm2({
4925
4950
  message: "Set up Graphify for richer architecture context?",
4926
4951
  initialValue: true
4927
4952
  })
4928
- );
4953
+ ));
4929
4954
  note2(
4930
4955
  `Project folder: ${pc19.bold(process.cwd())}
4931
4956
  Namespace: ${pc19.bold(namespace)}`,
@@ -4936,6 +4961,8 @@ Namespace: ${pc19.bold(namespace)}`,
4936
4961
  let embeddings = projectConfig.provider?.type;
4937
4962
  if (embeddings) {
4938
4963
  log2.info(`Embeddings: ${embeddings} (change with ${pc19.cyan("codebuddy models use")}).`);
4964
+ } else if (!interactive) {
4965
+ embeddings = "local";
4939
4966
  } else {
4940
4967
  embeddings = unwrap2(
4941
4968
  await select({
@@ -4975,6 +5002,10 @@ Namespace: ${pc19.bold(namespace)}`,
4975
5002
  } else if (stored.hfToken) {
4976
5003
  hfToken = stored.hfToken;
4977
5004
  log2.info(`Reusing saved Hugging Face token (${pc19.dim("~/.codebuddy/global.json")}).`);
5005
+ } else if (!interactive) {
5006
+ throw new Error(
5007
+ "Hugging Face embeddings need a token: set HF_TOKEN (no terminal to prompt for it)."
5008
+ );
4978
5009
  } else {
4979
5010
  hfToken = unwrap2(
4980
5011
  await password2({
@@ -4984,13 +5015,13 @@ Namespace: ${pc19.bold(namespace)}`,
4984
5015
  if (!hfToken) log2.warn("No token: hosted embeddings will fail until HF_TOKEN is set.");
4985
5016
  }
4986
5017
  const initialUrl = options.postgresUrl ?? process.env.DATABASE_URL ?? stored.databaseUrl ?? DEFAULT_DB_URL;
4987
- const databaseUrl = unwrap2(
5018
+ const databaseUrl = options.postgresUrl ?? (interactive ? unwrap2(
4988
5019
  await text2({
4989
5020
  message: "Postgres URL (press Enter to accept)",
4990
5021
  initialValue: initialUrl,
4991
5022
  validate: (value) => value && value.length > 0 ? void 0 : "Postgres URL is required."
4992
5023
  })
4993
- );
5024
+ ) : initialUrl);
4994
5025
  const dbSpinner = spinner3();
4995
5026
  dbSpinner.start("Checking Postgres");
4996
5027
  let connected = await pingOnce2(databaseUrl);
@@ -4998,12 +5029,12 @@ Namespace: ${pc19.bold(namespace)}`,
4998
5029
  if (!connected && databaseUrl === DEFAULT_DB_URL) {
4999
5030
  dbSpinner.stop("Postgres unreachable on the default local URL.");
5000
5031
  }
5001
- const shouldUseDocker = !connected && databaseUrl === DEFAULT_DB_URL && (options.useDocker ?? unwrap2(
5032
+ const shouldUseDocker = !connected && databaseUrl === DEFAULT_DB_URL && (options.useDocker ?? (interactive && unwrap2(
5002
5033
  await confirm2({
5003
5034
  message: "Postgres is unavailable. Start the bundled Docker database now?",
5004
5035
  initialValue: true
5005
5036
  })
5006
- ));
5037
+ )));
5007
5038
  if (shouldUseDocker) {
5008
5039
  if (!await isDockerDaemonRunning()) {
5009
5040
  const dockerSpinner = spinner3();
@@ -5038,7 +5069,7 @@ Namespace: ${pc19.bold(namespace)}`,
5038
5069
  } else if (!connected) {
5039
5070
  dbSpinner.stop(connected ? "Postgres reachable." : "Postgres unreachable at this URL.");
5040
5071
  log2.warn(
5041
- "Local Postgres is required. Start it, set DATABASE_URL, pass --postgres-url, or allow the bundled Docker database."
5072
+ "Local Postgres is required. Start it, set DATABASE_URL, pass --postgres-url, or allow the bundled Docker database (--docker)."
5042
5073
  );
5043
5074
  bail2();
5044
5075
  }
@@ -5244,9 +5275,7 @@ function registerZvecCommands(program, runSafely2) {
5244
5275
  ...opts.rebuild ? { rebuild: true } : {}
5245
5276
  });
5246
5277
  if (code !== 0) throw new Error(`zg index exited with code ${code}`);
5247
- console.log(
5248
- pc21.dim("tip: add .zvec-grep/ to .gitignore; enable with `codebuddy zvec enable`")
5249
- );
5278
+ console.log(pc21.dim("next: codebuddy zvec enable (.zvec-grep/ ignores itself in git)"));
5250
5279
  });
5251
5280
  });
5252
5281
  zvec.command("enable").description("Use zvec-grep candidates in context_pack / context_before_edit.").action(async () => {
@@ -5281,7 +5310,7 @@ function createCli() {
5281
5310
  ).argument("[namespace]", "Override the namespace (defaults to current folder name).").option("--postgres-url <url>", "Postgres URL to use for this project.").option("--skip-claude", "Do not register with Claude Desktop.").option("--skip-codex", "Do not register with Codex.").option("--graphify", "Enable Graphify setup without prompting.").option("--no-graphify", "Skip Graphify setup without prompting.").option(
5282
5311
  "--docker",
5283
5312
  "Start CodeBuddy's bundled Postgres container when the default DB is unavailable."
5284
- ).option("--no-docker", "Do not offer to start CodeBuddy's bundled Postgres container.").action(
5313
+ ).option("--no-docker", "Do not offer to start CodeBuddy's bundled Postgres container.").option("-y, --yes", "Never prompt: use flags and defaults (automatic without a TTY).").option("--non-interactive", "Same as --yes.").action(
5285
5314
  async (namespace, opts) => {
5286
5315
  await runSafely(async () => {
5287
5316
  await runUseCommand({
@@ -5290,7 +5319,8 @@ function createCli() {
5290
5319
  ...opts.skipClaude !== void 0 ? { skipClaude: opts.skipClaude } : {},
5291
5320
  ...opts.skipCodex !== void 0 ? { skipCodex: opts.skipCodex } : {},
5292
5321
  ...opts.graphify !== void 0 ? { graphify: opts.graphify } : {},
5293
- ...opts.docker !== void 0 ? { useDocker: opts.docker } : {}
5322
+ ...opts.docker !== void 0 ? { useDocker: opts.docker } : {},
5323
+ ...opts.yes || opts.nonInteractive ? { interactive: false } : {}
5294
5324
  });
5295
5325
  });
5296
5326
  }
@@ -5705,4 +5735,4 @@ function printDbDoctor(report) {
5705
5735
  export {
5706
5736
  createCli
5707
5737
  };
5708
- //# sourceMappingURL=main-MTBQZGJV.js.map
5738
+ //# sourceMappingURL=main-AHNGHC4L.js.map