@orquentra/codebuddy 2.6.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/CLAUDE_CODEX.md +6 -4
  2. package/WORKFLOW.md +5 -5
  3. package/dist/{chunk-U4HAJLIB.js → chunk-3QGW53GD.js} +16 -3
  4. package/dist/chunk-3QGW53GD.js.map +1 -0
  5. package/dist/{chunk-L5AYZC3F.js → chunk-5EDGAZQN.js} +438 -68
  6. package/dist/chunk-5EDGAZQN.js.map +1 -0
  7. package/dist/chunk-5UH3BEQR.js +32 -0
  8. package/dist/chunk-5UH3BEQR.js.map +1 -0
  9. package/dist/{chunk-JFDRBPYF.js → chunk-6JCKMIQY.js} +2 -2
  10. package/dist/{chunk-UN57FKQX.js → chunk-AK2UAE32.js} +152 -862
  11. package/dist/chunk-AK2UAE32.js.map +1 -0
  12. package/dist/{chunk-GDJH7XJC.js → chunk-AL6VBTNA.js} +77 -3
  13. package/dist/chunk-AL6VBTNA.js.map +1 -0
  14. package/dist/{chunk-WHZSVY2L.js → chunk-AQHPEJR6.js} +94 -23
  15. package/dist/chunk-AQHPEJR6.js.map +1 -0
  16. package/dist/{chunk-PCE4STAY.js → chunk-BQM7FJ7D.js} +6 -6
  17. package/dist/chunk-BQM7FJ7D.js.map +1 -0
  18. package/dist/{chunk-XBJQKCLU.js → chunk-CAEPHLHC.js} +13 -9
  19. package/dist/chunk-CAEPHLHC.js.map +1 -0
  20. package/dist/chunk-CG5B3LYG.js +66 -0
  21. package/dist/chunk-CG5B3LYG.js.map +1 -0
  22. package/dist/chunk-CSRD5OQA.js +79 -0
  23. package/dist/chunk-CSRD5OQA.js.map +1 -0
  24. package/dist/{chunk-IHQM2ZDV.js → chunk-DFV2SK6C.js} +1 -1
  25. package/dist/{chunk-IHQM2ZDV.js.map → chunk-DFV2SK6C.js.map} +1 -1
  26. package/dist/chunk-EEUFU7KV.js +1634 -0
  27. package/dist/chunk-EEUFU7KV.js.map +1 -0
  28. package/dist/{chunk-UFZD3JAA.js → chunk-ESZXCZIN.js} +3 -3
  29. package/dist/{chunk-PYLH4MH5.js → chunk-FJOTP632.js} +1 -1
  30. package/dist/{chunk-SYWJG5NA.js → chunk-GKNYMNFM.js} +17 -7
  31. package/dist/{chunk-SYWJG5NA.js.map → chunk-GKNYMNFM.js.map} +1 -1
  32. package/dist/{chunk-JPHJBCZB.js → chunk-HPOR6YXA.js} +3 -2
  33. package/dist/chunk-HPOR6YXA.js.map +1 -0
  34. package/dist/chunk-JUAFCLRB.js +1014 -0
  35. package/dist/chunk-JUAFCLRB.js.map +1 -0
  36. package/dist/{chunk-776EQXDV.js → chunk-KNR2P2UY.js} +110 -83
  37. package/dist/chunk-KNR2P2UY.js.map +1 -0
  38. package/dist/{chunk-N34XHWBA.js → chunk-LCSBL5TT.js} +24 -11
  39. package/dist/chunk-LCSBL5TT.js.map +1 -0
  40. package/dist/{chunk-QDBDYCM2.js → chunk-MSUAVR3R.js} +76 -11
  41. package/dist/chunk-MSUAVR3R.js.map +1 -0
  42. package/dist/{chunk-6MKEIDX5.js → chunk-SFMIMLXR.js} +2 -2
  43. package/dist/chunk-TGFTKWOR.js +16 -0
  44. package/dist/chunk-TGFTKWOR.js.map +1 -0
  45. package/dist/chunk-TUR6YKEE.js +55 -0
  46. package/dist/chunk-TUR6YKEE.js.map +1 -0
  47. package/dist/chunk-Y7D7PUBE.js +321 -0
  48. package/dist/chunk-Y7D7PUBE.js.map +1 -0
  49. package/dist/{chunk-VAKBJV26.js → chunk-YJIA2MFL.js} +918 -2238
  50. package/dist/chunk-YJIA2MFL.js.map +1 -0
  51. package/dist/cli/index.js +14 -3
  52. package/dist/cli/index.js.map +1 -1
  53. package/dist/{cloud-7WM3YLI4.js → cloud-LXZUR3U2.js} +3 -3
  54. package/dist/{codebuddy-DM-i0Fu5.d.ts → codebuddy-DO7ZhoWn.d.ts} +37 -3
  55. package/dist/config-GEZFLXK6.js +12 -0
  56. package/dist/docker-autostart-KDME7TVC.js +12 -0
  57. package/dist/index.d.ts +48 -3
  58. package/dist/index.js +32 -25
  59. package/dist/langgraph/index.d.ts +1 -1
  60. package/dist/lean-command-CWJAVQ5G.js +18 -0
  61. package/dist/local-ST4WMRCG.js +17 -0
  62. package/dist/{main-HFHLGID4.js → main-AHNGHC4L.js} +327 -329
  63. package/dist/main-AHNGHC4L.js.map +1 -0
  64. package/dist/{memory-4RGVLPKG.js → memory-TGHVTBME.js} +9 -8
  65. package/dist/memory-TGHVTBME.js.map +1 -0
  66. package/dist/operations-7BTAAUHF.js +26 -0
  67. package/dist/postgres-docker-QQZQ2ABK.js +16 -0
  68. package/dist/pretool-command-3SXQSMAO.js +13 -0
  69. package/dist/{rerank-3P3C2UHN.js → rerank-IQAEWLQR.js} +6 -5
  70. package/dist/rerank-IQAEWLQR.js.map +1 -0
  71. package/dist/runtime-E7JQZ2BZ.js +46 -0
  72. package/dist/runtime-E7JQZ2BZ.js.map +1 -0
  73. package/dist/stdio-HOV4P3DQ.js +12 -0
  74. package/dist/stdio-HOV4P3DQ.js.map +1 -0
  75. package/dist/summaries-XV3JGGCC.js +20 -0
  76. package/dist/summaries-XV3JGGCC.js.map +1 -0
  77. package/dist/{zvec-MAPSJQSR.js → zvec-KP4KNB7Y.js} +10 -3
  78. package/dist/zvec-KP4KNB7Y.js.map +1 -0
  79. package/package.json +7 -10
  80. package/src/db/migrations/0006_fact_idempotency_key.sql +2 -0
  81. package/src/db/migrations/meta/0006_snapshot.json +1258 -0
  82. package/src/db/migrations/meta/_journal.json +7 -0
  83. package/dist/chunk-776EQXDV.js.map +0 -1
  84. package/dist/chunk-GDJH7XJC.js.map +0 -1
  85. package/dist/chunk-JPHJBCZB.js.map +0 -1
  86. package/dist/chunk-L5AYZC3F.js.map +0 -1
  87. package/dist/chunk-N34XHWBA.js.map +0 -1
  88. package/dist/chunk-PCE4STAY.js.map +0 -1
  89. package/dist/chunk-PQK3GKZH.js +0 -98
  90. package/dist/chunk-PQK3GKZH.js.map +0 -1
  91. package/dist/chunk-QDBDYCM2.js.map +0 -1
  92. package/dist/chunk-U4HAJLIB.js.map +0 -1
  93. package/dist/chunk-UN57FKQX.js.map +0 -1
  94. package/dist/chunk-VAKBJV26.js.map +0 -1
  95. package/dist/chunk-WHZSVY2L.js.map +0 -1
  96. package/dist/chunk-XBJQKCLU.js.map +0 -1
  97. package/dist/config-KFEHLV6W.js +0 -11
  98. package/dist/lean-command-C5YPFKHP.js +0 -15
  99. package/dist/local-OTOY56B4.js +0 -13
  100. package/dist/main-HFHLGID4.js.map +0 -1
  101. package/dist/memory-4RGVLPKG.js.map +0 -1
  102. package/dist/operations-LOBAQRLO.js +0 -24
  103. package/dist/pretool-command-MQQ2UNQM.js +0 -10
  104. package/dist/rerank-3P3C2UHN.js.map +0 -1
  105. package/dist/summaries-ZBV5IN7Y.js +0 -18
  106. /package/dist/{chunk-JFDRBPYF.js.map → chunk-6JCKMIQY.js.map} +0 -0
  107. /package/dist/{chunk-UFZD3JAA.js.map → chunk-ESZXCZIN.js.map} +0 -0
  108. /package/dist/{chunk-PYLH4MH5.js.map → chunk-FJOTP632.js.map} +0 -0
  109. /package/dist/{chunk-6MKEIDX5.js.map → chunk-SFMIMLXR.js.map} +0 -0
  110. /package/dist/{cloud-7WM3YLI4.js.map → cloud-LXZUR3U2.js.map} +0 -0
  111. /package/dist/{config-KFEHLV6W.js.map → config-GEZFLXK6.js.map} +0 -0
  112. /package/dist/{lean-command-C5YPFKHP.js.map → docker-autostart-KDME7TVC.js.map} +0 -0
  113. /package/dist/{local-OTOY56B4.js.map → lean-command-CWJAVQ5G.js.map} +0 -0
  114. /package/dist/{operations-LOBAQRLO.js.map → local-ST4WMRCG.js.map} +0 -0
  115. /package/dist/{pretool-command-MQQ2UNQM.js.map → operations-7BTAAUHF.js.map} +0 -0
  116. /package/dist/{summaries-ZBV5IN7Y.js.map → postgres-docker-QQZQ2ABK.js.map} +0 -0
  117. /package/dist/{zvec-MAPSJQSR.js.map → pretool-command-3SXQSMAO.js.map} +0 -0
@@ -1,72 +1,70 @@
1
1
  import {
2
- loadLlmSummaries,
3
- summarizeRepository
4
- } from "./chunk-PCE4STAY.js";
2
+ runPreToolHookCommand
3
+ } from "./chunk-AQHPEJR6.js";
5
4
  import {
6
- chatJson,
7
- coolingDown,
8
- usageToday
9
- } from "./chunk-UFZD3JAA.js";
5
+ isDockerDaemonRunning,
6
+ tryStartDockerDesktop,
7
+ waitForDockerDaemon
8
+ } from "./chunk-5UH3BEQR.js";
9
+ import {
10
+ postgresDown,
11
+ postgresStatus,
12
+ postgresUp
13
+ } from "./chunk-CSRD5OQA.js";
10
14
  import {
11
15
  renderLeanBlock,
12
16
  runLeanHookCommand
13
- } from "./chunk-N34XHWBA.js";
14
- import {
15
- runPreToolHookCommand
16
- } from "./chunk-WHZSVY2L.js";
17
+ } from "./chunk-LCSBL5TT.js";
17
18
  import {
18
19
  failSoft,
19
20
  readHookInput,
20
21
  writeStdout
21
- } from "./chunk-PYLH4MH5.js";
22
+ } from "./chunk-FJOTP632.js";
23
+ import {
24
+ generateSuggestions,
25
+ migrateMemoryToFiles,
26
+ registerCodeBuddyTools,
27
+ startMcpServer,
28
+ watchRepository
29
+ } from "./chunk-EEUFU7KV.js";
30
+ import {
31
+ DEFAULT_ZVEC_MODEL,
32
+ buildZvecIndex,
33
+ zvecStatus
34
+ } from "./chunk-MSUAVR3R.js";
22
35
  import {
23
36
  PlanFileStore,
24
37
  PlanLifecycle,
25
38
  ReviewStore,
26
- VERSION,
27
39
  WorklogStore,
28
40
  approveReviewItem,
29
41
  assessRisk,
30
42
  buildBeforeEditContext,
31
43
  buildBootstrapContext,
32
- generateSuggestions,
33
- migrateMemoryToFiles,
34
44
  openHookSemantics,
35
- parseTraceLines,
36
- readTraces,
37
- registerCodeBuddyTools,
38
45
  renderContextBlock,
39
46
  resolveRiskPaths,
40
47
  runCaptureHook,
41
48
  runContextHook,
42
49
  runResetHook,
43
50
  runStageHook,
44
- runSyncHook,
45
- startMcpServer,
46
- tracePath,
47
- watchRepository
48
- } from "./chunk-VAKBJV26.js";
49
- import "./chunk-IHQM2ZDV.js";
51
+ runSyncHook
52
+ } from "./chunk-YJIA2MFL.js";
53
+ import {
54
+ parseTraceLines,
55
+ readTraces,
56
+ tracePath
57
+ } from "./chunk-TUR6YKEE.js";
58
+ import "./chunk-DFV2SK6C.js";
50
59
  import {
51
60
  SessionCapsuleLedger,
61
+ detectTestCommand,
52
62
  estimateTokens
53
- } from "./chunk-SYWJG5NA.js";
63
+ } from "./chunk-GKNYMNFM.js";
54
64
  import {
55
- buildArchitectureMap,
56
- generateGraphifyGraph,
57
- indexRepository,
58
- loadFreshIndex,
59
- loadGraphifyMap,
60
- neighbours,
61
- queryMap,
62
- setupGraphify
63
- } from "./chunk-L5AYZC3F.js";
64
- import {
65
- extractSymbolTable,
66
- supportsSymbols
67
- } from "./chunk-PQK3GKZH.js";
65
+ hookRoot
66
+ } from "./chunk-TGFTKWOR.js";
68
67
  import {
69
- MemoryFileStore,
70
68
  bootstrapDatabase,
71
69
  buildProviderFromConfig,
72
70
  createDatabaseClient,
@@ -77,32 +75,56 @@ import {
77
75
  pingDatabase,
78
76
  runDoctor,
79
77
  runMigrations
80
- } from "./chunk-UN57FKQX.js";
78
+ } from "./chunk-AK2UAE32.js";
81
79
  import {
82
- redactPersisted
83
- } from "./chunk-GDJH7XJC.js";
80
+ MemoryFileStore,
81
+ VERSION
82
+ } from "./chunk-JUAFCLRB.js";
84
83
  import {
85
84
  computeFactHash,
86
85
  generateEntityId
87
- } from "./chunk-XBJQKCLU.js";
88
- import "./chunk-JFDRBPYF.js";
86
+ } from "./chunk-CAEPHLHC.js";
87
+ import "./chunk-6JCKMIQY.js";
89
88
  import {
89
+ localRuntimeInstalled,
90
90
  modelCacheDir
91
- } from "./chunk-U4HAJLIB.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";
92
119
  import {
93
120
  DEFAULT_CLOUD_EMBEDDING_MODELS,
94
121
  DEFAULT_RERANK_MODELS,
95
122
  LOCAL_EMBEDDING_MODEL,
96
123
  getModelDefinition
97
- } from "./chunk-JPHJBCZB.js";
98
- import {
99
- DEFAULT_ZVEC_MODEL,
100
- buildZvecIndex,
101
- zvecStatus
102
- } from "./chunk-QDBDYCM2.js";
124
+ } from "./chunk-HPOR6YXA.js";
103
125
  import {
104
126
  resolveLlmConfig
105
- } from "./chunk-6MKEIDX5.js";
127
+ } from "./chunk-SFMIMLXR.js";
106
128
  import {
107
129
  PLAN_POLICIES,
108
130
  configPath,
@@ -111,6 +133,7 @@ import {
111
133
  initProjectScaffold,
112
134
  loadPlanPolicy,
113
135
  loadRuntimeConfig,
136
+ migrateLegacyNamespace,
114
137
  readConfigFile,
115
138
  redactSecrets,
116
139
  resolveNamespaceFrom,
@@ -118,9 +141,11 @@ import {
118
141
  setLlmEnabled,
119
142
  setPlanPolicy,
120
143
  setZvecEnabled,
121
- writeAtomic,
122
144
  writeConfigFile
123
- } from "./chunk-776EQXDV.js";
145
+ } from "./chunk-KNR2P2UY.js";
146
+ import {
147
+ writeAtomic
148
+ } from "./chunk-CG5B3LYG.js";
124
149
  import "./chunk-PZ5AY32C.js";
125
150
 
126
151
  // src/cli/main.ts
@@ -226,9 +251,7 @@ import pc from "picocolors";
226
251
 
227
252
  // src/change/engine.ts
228
253
  import { spawn } from "child_process";
229
- import { constants as constants2 } from "fs";
230
- import { access as access2, readFile } from "fs/promises";
231
- import { extname, join as join2 } from "path";
254
+ import { extname } from "path";
232
255
  var CODE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
233
256
  var DEFAULT_TEST_TIMEOUT_MS = 12e4;
234
257
  var MAX_TEST_OUTPUT_CHARS = 12e3;
@@ -277,7 +300,7 @@ async function buildChangeReport(input) {
277
300
  }
278
301
  async function verifyChange(input) {
279
302
  const report = await buildChangeReport(input);
280
- 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());
281
304
  if (!detected) {
282
305
  return {
283
306
  report,
@@ -288,7 +311,7 @@ async function verifyChange(input) {
288
311
  exitCode: null,
289
312
  signal: null,
290
313
  durationMs: 0,
291
- 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."
292
315
  }
293
316
  };
294
317
  }
@@ -302,44 +325,19 @@ async function verifyChange(input) {
302
325
  )
303
326
  };
304
327
  }
305
- async function detectTestCommand(repositoryRoot) {
306
- const packagePath = join2(repositoryRoot, "package.json");
307
- try {
308
- const packageJson = JSON.parse(await readFile(packagePath, "utf8"));
309
- if (typeof packageJson.scripts?.test === "string") {
310
- return {
311
- command: `${packageManagerName(packageJson.packageManager)} test`,
312
- source: "package_script"
313
- };
314
- }
315
- } catch (error) {
316
- if (error.code !== "ENOENT") throw error;
317
- }
318
- for (const candidate of [
319
- ["pnpm-lock.yaml", "pnpm test"],
320
- ["yarn.lock", "yarn test"],
321
- ["bun.lockb", "bun test"],
322
- ["bun.lock", "bun test"],
323
- ["package-lock.json", "npm test"]
324
- ]) {
325
- try {
326
- await access2(join2(repositoryRoot, candidate[0]), constants2.F_OK);
327
- return { command: candidate[1], source: "package_manager" };
328
- } catch {
329
- }
330
- }
331
- return null;
332
- }
333
- function packageManagerName(value) {
334
- if (typeof value !== "string") return "npm";
335
- const name = value.split(/[\s@]/, 1)[0] ?? "npm";
336
- 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
+ };
337
335
  }
338
336
  function runTestCommand(command, source, repositoryRoot, timeoutMs) {
339
337
  if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
340
338
  throw new Error("--timeout must be a positive number of milliseconds.");
341
339
  }
342
- return new Promise((resolve14) => {
340
+ return new Promise((resolve13) => {
343
341
  const startedAt = Date.now();
344
342
  const child = spawn(command, {
345
343
  cwd: repositoryRoot,
@@ -364,7 +362,7 @@ ${output.slice(-MAX_TEST_OUTPUT_CHARS)}`;
364
362
  }, timeoutMs);
365
363
  child.on("close", (exitCode, signal) => {
366
364
  clearTimeout(timer);
367
- resolve14({
365
+ resolve13({
368
366
  status: timedOut ? "timed_out" : exitCode === 0 ? "passed" : "failed",
369
367
  command,
370
368
  source,
@@ -376,7 +374,7 @@ ${output.slice(-MAX_TEST_OUTPUT_CHARS)}`;
376
374
  });
377
375
  child.on("error", (error) => {
378
376
  clearTimeout(timer);
379
- resolve14({
377
+ resolve13({
380
378
  status: "failed",
381
379
  command,
382
380
  source,
@@ -589,22 +587,22 @@ function testBadge(status) {
589
587
 
590
588
  // src/cli/commands/claude-desktop.ts
591
589
  import { existsSync } from "fs";
592
- import { mkdir as mkdir2, readFile as readFile2 } from "fs/promises";
593
- 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";
594
592
  import { fileURLToPath } from "url";
595
593
  function claudeDesktopConfigPath(home = process.env.HOME ?? "") {
596
594
  if (process.platform === "darwin") {
597
- return join3(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
595
+ return join2(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
598
596
  }
599
597
  if (process.platform === "win32") {
600
- return join3(process.env.APPDATA ?? home, "Claude", "claude_desktop_config.json");
598
+ return join2(process.env.APPDATA ?? home, "Claude", "claude_desktop_config.json");
601
599
  }
602
- return join3(home, ".config", "Claude", "claude_desktop_config.json");
600
+ return join2(home, ".config", "Claude", "claude_desktop_config.json");
603
601
  }
604
602
  async function readClaudeConfig(path) {
605
603
  if (!existsSync(path)) return {};
606
604
  try {
607
- return JSON.parse(await readFile2(path, "utf8"));
605
+ return JSON.parse(await readFile(path, "utf8"));
608
606
  } catch (error) {
609
607
  throw new Error(
610
608
  `Failed to parse Claude Desktop config at ${path}: ${error.message}`
@@ -641,7 +639,10 @@ function buildEntry(options) {
641
639
  if (options.groqApiKey) env.GROQ_API_KEY = options.groqApiKey;
642
640
  if (options.databaseUrl) env.DATABASE_URL = options.databaseUrl;
643
641
  return {
644
- command: "node",
642
+ // Absolute: Claude Desktop is a GUI app and does not inherit the shell's
643
+ // PATH (nvm, Homebrew), so a bare "node" may not resolve or may be another
644
+ // version. Re-run `codebuddy claude install` after switching Node.
645
+ command: process.execPath,
645
646
  args: [resolveCliEntrypoint(), "serve"],
646
647
  env
647
648
  };
@@ -776,25 +777,25 @@ function sharedRulesReaders(client) {
776
777
  // src/clients/cost.ts
777
778
  import { mkdtemp, rm as rm3 } from "fs/promises";
778
779
  import { tmpdir } from "os";
779
- import { join as join8, resolve as resolve7 } from "path";
780
+ import { join as join7, resolve as resolve7 } from "path";
780
781
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
781
782
  import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
782
783
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
783
784
 
784
785
  // src/templates/install.ts
785
- import { readFile as readFile7, rm as rm2 } from "fs/promises";
786
- 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";
787
788
 
788
789
  // src/hooks/hermes-plugin.ts
789
- import { readFile as readFile4 } from "fs/promises";
790
- 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";
791
792
 
792
793
  // src/hooks/managed-plugin.ts
793
- 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";
794
795
  import { dirname as dirname3 } from "path";
795
796
  async function read(path) {
796
797
  try {
797
- return await readFile3(path, "utf8");
798
+ return await readFile2(path, "utf8");
798
799
  } catch (error) {
799
800
  if (error.code === "ENOENT") return null;
800
801
  throw error;
@@ -990,7 +991,7 @@ def register(ctx):
990
991
  }
991
992
  var HERMES_PLUGIN_SOURCE = renderHermesPlugin("full");
992
993
  function hermesPluginDir(repositoryRoot) {
993
- return join4(resolve3(repositoryRoot), ".hermes", "plugins", "codebuddy");
994
+ return join3(resolve3(repositoryRoot), ".hermes", "plugins", "codebuddy");
994
995
  }
995
996
  function plugin(repositoryRoot, variant = "full") {
996
997
  const dir = hermesPluginDir(repositoryRoot);
@@ -998,10 +999,10 @@ function plugin(repositoryRoot, variant = "full") {
998
999
  marker: HERMES_PLUGIN_MARKER,
999
1000
  files: [
1000
1001
  {
1001
- path: join4(dir, "plugin.yaml"),
1002
+ path: join3(dir, "plugin.yaml"),
1002
1003
  content: variant === "lean" ? HERMES_LEAN_MANIFEST : HERMES_PLUGIN_MANIFEST
1003
1004
  },
1004
- { path: join4(dir, "__init__.py"), content: renderHermesPlugin(variant) }
1005
+ { path: join3(dir, "__init__.py"), content: renderHermesPlugin(variant) }
1005
1006
  ],
1006
1007
  dirs: [dir, dirname4(dir), dirname4(dirname4(dir))]
1007
1008
  };
@@ -1014,13 +1015,13 @@ function setHermesPlugin(repositoryRoot, enabled, variant = "full") {
1014
1015
  }
1015
1016
  async function hermesPluginVariant(repositoryRoot) {
1016
1017
  if (!await hermesPluginInstalled(repositoryRoot)) return null;
1017
- const source = await readFile4(join4(hermesPluginDir(repositoryRoot), "__init__.py"), "utf8");
1018
+ const source = await readFile3(join3(hermesPluginDir(repositoryRoot), "__init__.py"), "utf8");
1018
1019
  return source.startsWith(`${HERMES_PLUGIN_MARKER} v1 lean`) ? "lean" : "full";
1019
1020
  }
1020
1021
 
1021
1022
  // src/hooks/opencode-plugin.ts
1022
- import { readFile as readFile5 } from "fs/promises";
1023
- 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";
1024
1025
  var OPENCODE_PLUGIN_MARKER = "// codebuddy-managed: opencode-plugin";
1025
1026
  var OPENCODE_RUNTIME = `const ID_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1026
1027
  let lastMs = 0;
@@ -1035,6 +1036,8 @@ function hook(command, payload) {
1035
1036
  child = spawn("codebuddy", ["hooks", command], {
1036
1037
  cwd: payload.cwd,
1037
1038
  stdio: ["pipe", "pipe", "ignore"],
1039
+ // npm installs a codebuddy.cmd shim on Windows, which spawn only runs through a shell.
1040
+ shell: process.platform === "win32",
1038
1041
  });
1039
1042
  } catch {
1040
1043
  done("");
@@ -1184,7 +1187,7 @@ export const CodeBuddyPlugin = async ({ directory }) => {
1184
1187
  }
1185
1188
  var OPENCODE_PLUGIN_SOURCE = renderOpencodePlugin("full");
1186
1189
  function opencodePluginPath(repositoryRoot) {
1187
- return join5(resolve4(repositoryRoot), ".opencode", "plugins", "codebuddy.js");
1190
+ return join4(resolve4(repositoryRoot), ".opencode", "plugins", "codebuddy.js");
1188
1191
  }
1189
1192
  function plugin2(repositoryRoot, variant = "full") {
1190
1193
  const path = opencodePluginPath(repositoryRoot);
@@ -1202,17 +1205,17 @@ function setOpencodePlugin(repositoryRoot, enabled, variant = "full") {
1202
1205
  }
1203
1206
  async function opencodePluginVariant(repositoryRoot) {
1204
1207
  if (!await opencodePluginInstalled(repositoryRoot)) return null;
1205
- const source = await readFile5(opencodePluginPath(repositoryRoot), "utf8");
1208
+ const source = await readFile4(opencodePluginPath(repositoryRoot), "utf8");
1206
1209
  return source.startsWith(`${OPENCODE_PLUGIN_MARKER} v1 lean`) ? "lean" : "full";
1207
1210
  }
1208
1211
 
1209
1212
  // src/hooks/settings.ts
1210
- import { mkdir as mkdir4, readFile as readFile6 } from "fs/promises";
1211
- 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";
1212
1215
  var HOOK_COMMAND_PREFIX = "codebuddy hooks";
1213
1216
  var TOOL_GUARD_DEF = {
1214
1217
  event: "PreToolUse",
1215
- matcher: "Read|Grep",
1218
+ matcher: "Read|Grep|Bash",
1216
1219
  command: `${HOOK_COMMAND_PREFIX} pretool`
1217
1220
  };
1218
1221
  var HOOK_DEFS = [
@@ -1234,7 +1237,7 @@ var LEAN_HOOK_DEFS = [
1234
1237
  TOOL_GUARD_DEF
1235
1238
  ];
1236
1239
  function claudeSettingsPath(repositoryRoot = process.cwd()) {
1237
- return join6(resolve5(repositoryRoot), ".claude", "settings.json");
1240
+ return join5(resolve5(repositoryRoot), ".claude", "settings.json");
1238
1241
  }
1239
1242
  function upsertHooks(settings, variant = "full") {
1240
1243
  const defs = variant === "lean" ? LEAN_HOOK_DEFS : HOOK_DEFS;
@@ -1313,7 +1316,7 @@ function hooksVariant(settings) {
1313
1316
  }
1314
1317
  async function readSettings(path) {
1315
1318
  try {
1316
- return JSON.parse(await readFile6(path, "utf8"));
1319
+ return JSON.parse(await readFile5(path, "utf8"));
1317
1320
  } catch (error) {
1318
1321
  if (error.code === "ENOENT") return {};
1319
1322
  throw error;
@@ -1417,12 +1420,16 @@ var START_HOOKS = [
1417
1420
  "follow the agent-driven flow: `context_pack` first, `context_after_turn` last.",
1418
1421
  "",
1419
1422
  "1. Start from the injected block; do not call `context_bootstrap` for orientation",
1420
- " it already covers.",
1421
- "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",
1422
1429
  " task needs symbols, neighbours, or file-level evidence the block lacks. Pass the",
1423
1430
  " block's `session` value as `sessionId` and the previous `delivery.deliveryId`",
1424
1431
  " as `ack` so unchanged sections are not resent.",
1425
- "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",
1426
1433
  " those files before relying on it.",
1427
1434
  ""
1428
1435
  ];
@@ -1525,7 +1532,7 @@ ${END_MARKER}
1525
1532
  }
1526
1533
  async function installWorkflowRules(options) {
1527
1534
  const projectRoot = resolve6(options.projectRoot ?? process.cwd());
1528
- const path = options.targetPath ?? join7(projectRoot, defaultRulesFile(options.client));
1535
+ const path = options.targetPath ?? join6(projectRoot, defaultRulesFile(options.client));
1529
1536
  const existing = await readIfPresent(path);
1530
1537
  const mode = options.mode ?? await defaultRulesMode(options.client, projectRoot);
1531
1538
  if (options.onlyIfManaged && (existing === null || !hasManagedBlock(existing))) {
@@ -1580,7 +1587,7 @@ function removeBlock(existing) {
1580
1587
  }
1581
1588
  async function readIfPresent(path) {
1582
1589
  try {
1583
- return await readFile7(path, "utf8");
1590
+ return await readFile6(path, "utf8");
1584
1591
  } catch (error) {
1585
1592
  if (error.code === "ENOENT") return null;
1586
1593
  throw error;
@@ -1617,7 +1624,7 @@ async function hookBlockTokens(root, namespace) {
1617
1624
  namespace,
1618
1625
  useGit: true
1619
1626
  }).catch(() => null);
1620
- const scratch = await mkdtemp(join8(tmpdir(), "codebuddy-cost-"));
1627
+ const scratch = await mkdtemp(join7(tmpdir(), "codebuddy-cost-"));
1621
1628
  try {
1622
1629
  const ledger = new SessionCapsuleLedger(scratch);
1623
1630
  const render = () => renderContextBlock(bootstrap, beforeEdit, ledger, "cost", true);
@@ -1670,14 +1677,14 @@ async function measureClientCosts(projectRoot = process.cwd(), namespace) {
1670
1677
  }
1671
1678
 
1672
1679
  // src/clients/install.ts
1673
- import { access as access4 } from "fs/promises";
1674
- 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";
1675
1682
 
1676
1683
  // src/integrations/project-mcp.ts
1677
- import { access as access3, mkdir as mkdir5, readFile as readFile8, rm as rm4 } from "fs/promises";
1678
- 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";
1679
1686
  async function writeProjectMcpConfig(options) {
1680
- const path = join9(options.repositoryRoot, ".mcp.json");
1687
+ const path = join8(options.repositoryRoot, ".mcp.json");
1681
1688
  const current = await readProjectMcpConfig(path);
1682
1689
  current.mcpServers ??= {};
1683
1690
  current.mcpServers[`codebuddy-${options.namespace}`] = codebuddyMcpServer(
@@ -1714,12 +1721,12 @@ function codebuddyMcpServer(repositoryRoot, namespace) {
1714
1721
  };
1715
1722
  }
1716
1723
  async function projectMcpPath(root, file, format) {
1717
- const path = join9(root, file);
1724
+ const path = join8(root, file);
1718
1725
  if (format !== "opencode" || await exists(path)) return path;
1719
1726
  return await exists(`${path}c`) ? `${path}c` : path;
1720
1727
  }
1721
1728
  async function exists(path) {
1722
- return access3(path).then(
1729
+ return access2(path).then(
1723
1730
  () => true,
1724
1731
  () => false
1725
1732
  );
@@ -1780,7 +1787,7 @@ function parseJsonc(raw) {
1780
1787
  }
1781
1788
  async function readConfig(path) {
1782
1789
  try {
1783
- const { value, comments } = parseJsonc(await readFile8(path, "utf8"));
1790
+ const { value, comments } = parseJsonc(await readFile7(path, "utf8"));
1784
1791
  const config = value && typeof value === "object" ? value : {};
1785
1792
  return { config, comments };
1786
1793
  } catch (error) {
@@ -1794,15 +1801,15 @@ async function readProjectMcpConfig(path) {
1794
1801
 
1795
1802
  // src/cli/commands/codex.ts
1796
1803
  import { existsSync as existsSync2 } from "fs";
1797
- import { mkdir as mkdir6, readFile as readFile9 } from "fs/promises";
1804
+ import { mkdir as mkdir6, readFile as readFile8 } from "fs/promises";
1798
1805
  import { homedir } from "os";
1799
- import { dirname as dirname8, join as join10 } from "path";
1806
+ import { dirname as dirname8, join as join9 } from "path";
1800
1807
  function codexConfigPath(home = homedir()) {
1801
- return join10(home, ".codex", "config.toml");
1808
+ return join9(home, ".codex", "config.toml");
1802
1809
  }
1803
1810
  async function readCodexConfig(path) {
1804
1811
  if (!existsSync2(path)) return "";
1805
- return readFile9(path, "utf8");
1812
+ return readFile8(path, "utf8");
1806
1813
  }
1807
1814
  async function writeCodexConfig(path, content) {
1808
1815
  await mkdir6(dirname8(path), { recursive: true });
@@ -1908,17 +1915,17 @@ function escapeRegExp(value) {
1908
1915
  }
1909
1916
 
1910
1917
  // src/integrations/hermes-config.ts
1911
- import { mkdir as mkdir7, readFile as readFile10 } from "fs/promises";
1918
+ import { mkdir as mkdir7, readFile as readFile9 } from "fs/promises";
1912
1919
  import { homedir as homedir2 } from "os";
1913
- import { dirname as dirname9, join as join11 } from "path";
1920
+ import { dirname as dirname9, join as join10 } from "path";
1914
1921
  import { isDeepStrictEqual } from "util";
1915
1922
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
1916
1923
  function hermesConfigPath(home = homedir2()) {
1917
- return join11(process.env.HERMES_HOME ?? join11(home, ".hermes"), "config.yaml");
1924
+ return join10(process.env.HERMES_HOME ?? join10(home, ".hermes"), "config.yaml");
1918
1925
  }
1919
1926
  async function readRaw(path) {
1920
1927
  try {
1921
- return await readFile10(path, "utf8");
1928
+ return await readFile9(path, "utf8");
1922
1929
  } catch (error) {
1923
1930
  if (error.code === "ENOENT") return "";
1924
1931
  throw error;
@@ -2040,8 +2047,8 @@ async function installClient(options) {
2040
2047
  return apply(options, options.mode, options.mode !== "lean");
2041
2048
  }
2042
2049
  async function ensureIndex(root) {
2043
- const target = join12(root, ".codebuddy", "cache", "index.json");
2044
- const existed = await access4(target).then(
2050
+ const target = join11(root, ".codebuddy", "cache", "index.json");
2051
+ const existed = await access3(target).then(
2045
2052
  () => true,
2046
2053
  () => false
2047
2054
  );
@@ -2061,7 +2068,7 @@ async function apply(options, mode, keepMcp) {
2061
2068
  const shared = sharedRulesReaders(options.client);
2062
2069
  const liveSiblings = mode === "mcp-only" || mode === "lean" ? await installedWithMcp(root, shared, namespace) : [];
2063
2070
  if (liveSiblings.length > 0) {
2064
- const path = join12(root, adapter.rulesFile);
2071
+ const path = join11(root, adapter.rulesFile);
2065
2072
  steps.push({ surface: "rules", target: path, action: "kept" });
2066
2073
  notes.push(
2067
2074
  `Kept the CodeBuddy block in ${adapter.rulesFile}: still used by ${liveSiblings.map((id) => CLIENT_ADAPTERS[id].name).join(", ")}.`
@@ -2141,7 +2148,7 @@ async function setHooks(root, enabled, client = "claude", variant = "full") {
2141
2148
  async function detectClient(client, projectRoot = process.cwd(), namespace) {
2142
2149
  const root = resolve9(projectRoot);
2143
2150
  const adapter = CLIENT_ADAPTERS[client];
2144
- const rulesMode = await readManagedMode(join12(root, adapter.rulesFile));
2151
+ const rulesMode = await readManagedMode(join11(root, adapter.rulesFile));
2145
2152
  const hooks = await clientHooksInstalled(root, client);
2146
2153
  const name = `codebuddy-${namespace ?? await resolveNamespaceFrom(root)}`;
2147
2154
  const mcp = "file" in adapter.mcp ? await hasMcpServer(
@@ -2167,8 +2174,8 @@ async function detectClient(client, projectRoot = process.cwd(), namespace) {
2167
2174
 
2168
2175
  // src/clients/verify.ts
2169
2176
  import { execFile } from "child_process";
2170
- import { readFile as readFile11, realpath } from "fs/promises";
2171
- 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";
2172
2179
  import { pathToFileURL } from "url";
2173
2180
  var runHost = (bin, args, cwd) => new Promise((done) => {
2174
2181
  execFile(
@@ -2203,7 +2210,7 @@ var CONFORMANCE = {
2203
2210
  if (!config) return [fail("config", "opencode debug config returned no JSON")];
2204
2211
  const installed = await clientHooksInstalled(root, "opencode");
2205
2212
  const href = pathToFileURL(
2206
- join13(await realpath(root).catch(() => root), ".opencode", "plugins", "codebuddy.js")
2213
+ join12(await realpath(root).catch(() => root), ".opencode", "plugins", "codebuddy.js")
2207
2214
  ).href;
2208
2215
  const loaded = config.plugin?.includes(href) === true;
2209
2216
  return [
@@ -2230,7 +2237,7 @@ var CONFORMANCE = {
2230
2237
  return checks;
2231
2238
  }
2232
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(
2233
- await readFile11(join13(hermesConfigPath(), "..", ".env"), "utf8").catch(() => "")
2240
+ await readFile10(join12(hermesConfigPath(), "..", ".env"), "utf8").catch(() => "")
2234
2241
  );
2235
2242
  const plugins = (await readHermesConfig(hermesConfigPath()))?.plugins;
2236
2243
  const enabled = Array.isArray(plugins?.enabled) && plugins.enabled.includes("codebuddy");
@@ -2646,13 +2653,16 @@ th.n { text-align: right; }
2646
2653
  .chip { display: inline-block; font: 600 10px var(--mono); text-transform: uppercase; letter-spacing: .06em; padding: 2px 6px; border-radius: 4px; }
2647
2654
  .chip.recall { background: var(--recall-soft); color: var(--recall); }
2648
2655
  .chip.tool { background: var(--tool-soft); color: var(--tool); }
2649
- .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); }
2650
2657
  .chip.err { background: var(--bad-soft); color: var(--bad); }
2651
2658
  .feed { display: grid; gap: 10px; max-height: 520px; overflow: auto; }
2652
2659
  .prompt { border: 1px solid var(--line); border-radius: 8px; padding: 10px 12px; }
2653
2660
  .prompt .q { font-weight: 500; overflow-wrap: anywhere; }
2654
2661
  .prompt .meta { font: 12px var(--mono); color: var(--muted); margin-top: 4px; display: flex; flex-wrap: wrap; gap: 4px 12px; overflow-wrap: anywhere; }
2655
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; }
2656
2666
  .prompt .none { font-size: 13px; color: var(--muted); margin-top: 6px; }
2657
2667
  .mono { font-family: var(--mono); }
2658
2668
  .muted { color: var(--muted); }
@@ -2731,7 +2741,10 @@ th.n { text-align: right; }
2731
2741
  function kpis(list) {
2732
2742
  var recalls = list.filter(function (e) { return e.kind === "recall"; });
2733
2743
  var tools = list.filter(function (e) { return e.kind === "tool"; });
2734
- 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);
2735
2748
  var withFacts = recalls.filter(function (e) { return e.detail && e.detail.facts && e.detail.facts.length; });
2736
2749
  var factCount = withFacts.reduce(function (s, e) { return s + e.detail.facts.length; }, 0);
2737
2750
  var dense = recalls.reduce(function (s, e) { return s + ((e.detail && e.detail.dense) || 0); }, 0);
@@ -2742,7 +2755,8 @@ th.n { text-align: right; }
2742
2755
  var tiles = [
2743
2756
  ["Prompts recalled", num(recalls.length), recalls.length ? Math.round(100 * withFacts.length / recalls.length) + "% got facts" : "no prompts yet"],
2744
2757
  ["Facts injected", num(factCount), factCount ? Math.round(100 * dense / factCount) + "% matched by meaning" : "-"],
2745
- ["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"],
2746
2760
  ["MCP tool calls", num(tools.length), tools.length ? new Set(tools.map(function (e) { return e.name; })).size + " distinct tools" : "-"],
2747
2761
  ["Recall latency", lat.length ? ms(pct(lat, 0.5)) : "-", lat.length ? "p95 " + ms(pct(lat, 0.95)) : "-"],
2748
2762
  ["Memories saved", num(saved), captures.length + " capture" + (captures.length === 1 ? "" : "s")],
@@ -2812,6 +2826,7 @@ th.n { text-align: right; }
2812
2826
  (e.ok ? "" : '<span class="chip err">' + esc(d.dropped || "dropped") + "</span>") + "</div>" +
2813
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>') +
2814
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>" : "") +
2815
2830
  "</div>";
2816
2831
  }).join("");
2817
2832
  }
@@ -2836,6 +2851,7 @@ th.n { text-align: right; }
2836
2851
  var d = e.detail || {};
2837
2852
  if (e.kind === "recall") return (d.facts ? d.facts.length : 0) + " fact(s), " + (d.files ? d.files.length : 0) + " file(s)";
2838
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";
2839
2855
  if (e.kind === "sync") return (d.imported || 0) + " new, " + (d.updated || 0) + " updated" + (d.reembedding ? ", " + d.reembedding + " re-embedding" : "");
2840
2856
  return e.session ? "session " + e.session.slice(0, 12) : "";
2841
2857
  }
@@ -2939,7 +2955,13 @@ data: ${data}
2939
2955
  `);
2940
2956
  };
2941
2957
  watchFile(path, { interval: options.pollMs ?? 1e3 }, () => void onChange());
2958
+ let boundPort = 0;
2942
2959
  const server = createServer(async (request, response) => {
2960
+ const host = request.headers.host ?? "";
2961
+ if (host !== `127.0.0.1:${boundPort}` && host !== `localhost:${boundPort}`) {
2962
+ response.writeHead(403, { "content-type": "text/plain" }).end("Forbidden host");
2963
+ return;
2964
+ }
2943
2965
  const url = new URL(request.url ?? "/", "http://localhost");
2944
2966
  if (request.method !== "GET") {
2945
2967
  response.writeHead(405).end();
@@ -2979,18 +3001,19 @@ data: ${data}
2979
3001
  response.writeHead(404, { "content-type": "text/plain" }).end("Not found");
2980
3002
  });
2981
3003
  const port = await listen(server, options.port ?? 4777);
3004
+ boundPort = port;
2982
3005
  const heartbeat = setInterval(() => {
2983
3006
  for (const client of clients) client.write(": ping\n\n");
2984
3007
  }, 25e3);
2985
3008
  heartbeat.unref();
2986
3009
  return {
2987
3010
  url: `http://127.0.0.1:${port}/`,
2988
- close: () => new Promise((resolve14) => {
3011
+ close: () => new Promise((resolve13) => {
2989
3012
  clearInterval(heartbeat);
2990
3013
  unwatchFile(path);
2991
3014
  for (const client of clients) client.end();
2992
3015
  server.closeAllConnections();
2993
- server.close(() => resolve14());
3016
+ server.close(() => resolve13());
2994
3017
  })
2995
3018
  };
2996
3019
  }
@@ -2998,11 +3021,11 @@ async function listen(server, port) {
2998
3021
  for (let attempt = 0; attempt < 10; attempt += 1) {
2999
3022
  const candidate = port === 0 ? 0 : port + attempt;
3000
3023
  try {
3001
- await new Promise((resolve14, reject) => {
3024
+ await new Promise((resolve13, reject) => {
3002
3025
  server.once("error", reject);
3003
3026
  server.listen(candidate, "127.0.0.1", () => {
3004
3027
  server.off("error", reject);
3005
- resolve14();
3028
+ resolve13();
3006
3029
  });
3007
3030
  });
3008
3031
  return server.address().port;
@@ -3040,8 +3063,8 @@ function registerDashboardCommand(program, runSafely2) {
3040
3063
  );
3041
3064
  console.log(pc4.dim(" Ctrl+C to stop."));
3042
3065
  if (opts.open) openBrowser(dashboard.url);
3043
- await new Promise((resolve14) => {
3044
- const stop = () => void dashboard.close().then(resolve14);
3066
+ await new Promise((resolve13) => {
3067
+ const stop = () => void dashboard.close().then(resolve13);
3045
3068
  process.once("SIGINT", stop);
3046
3069
  process.once("SIGTERM", stop);
3047
3070
  });
@@ -3053,13 +3076,13 @@ function registerDashboardCommand(program, runSafely2) {
3053
3076
  import pc5 from "picocolors";
3054
3077
 
3055
3078
  // src/eval/runner.ts
3056
- import { readFile as readFile12 } from "fs/promises";
3057
- 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";
3058
3081
  async function runEvaluation(input = {}) {
3059
3082
  const datasetPath = resolve11(
3060
- input.datasetPath ?? join14(process.cwd(), "evals", "change-readiness.json")
3083
+ input.datasetPath ?? join13(process.cwd(), "evals", "change-readiness.json")
3061
3084
  );
3062
- const dataset = JSON.parse(await readFile12(datasetPath, "utf8"));
3085
+ const dataset = JSON.parse(await readFile11(datasetPath, "utf8"));
3063
3086
  if (dataset.version !== 1 || !Array.isArray(dataset.cases)) {
3064
3087
  throw new Error(`Unsupported evaluation dataset: ${datasetPath}`);
3065
3088
  }
@@ -3147,7 +3170,7 @@ function registerGitHubCommands(program, runSafely2) {
3147
3170
  }
3148
3171
 
3149
3172
  // src/cli/commands/hooks.ts
3150
- import { spawn as spawn3 } from "child_process";
3173
+ import { execFile as execFile2, spawn as spawn3 } from "child_process";
3151
3174
  import { relative as relative2 } from "path";
3152
3175
  import pc7 from "picocolors";
3153
3176
  function registerHooksCommands(program, runSafely2) {
@@ -3168,8 +3191,9 @@ function registerHooksCommands(program, runSafely2) {
3168
3191
  console.log(
3169
3192
  `${changed ? pc7.green("installed") : pc7.dim("unchanged")} CodeBuddy hooks in ${where}`
3170
3193
  );
3171
- await reconcileRules(client, "hooks");
3172
- syncInBackground(process.cwd());
3194
+ await reconcileRules(client, "hooks", { create: true });
3195
+ warnIfHookCommandStale();
3196
+ syncInBackground(process.cwd(), true);
3173
3197
  console.log(
3174
3198
  pc7.dim(
3175
3199
  ` prompt\u2192recall \xB7 edit\u2192stage \xB7 turn end\u2192capture \xB7 compaction\u2192reset. Restart ${CLIENT_ADAPTERS[client].name} to activate.`
@@ -3227,18 +3251,18 @@ function registerHooksCommands(program, runSafely2) {
3227
3251
  `codebuddy: captured from ${changedFiles.length} changed file(s) \u2014 ${result.saved.length} saved, ${result.queuedForReview.length} queued
3228
3252
  `
3229
3253
  );
3230
- if (result.saved.length > 0) syncInBackground(input.cwd ?? process.cwd());
3254
+ if (result.saved.length > 0) syncInBackground(hookRoot(input.cwd));
3231
3255
  }
3232
3256
  });
3233
3257
  });
3234
3258
  hooks.command("sync").description(
3235
3259
  "[hook] Reindex Markdown memory into PostgreSQL and embed it locally (runs detached after capture)."
3236
- ).action(async () => {
3260
+ ).option("--respace", "Also re-embed vectors made by another embedding model.").action(async (opts) => {
3237
3261
  await failSoft(async () => {
3238
- const result = await runSyncHook(process.cwd());
3262
+ const result = await runSyncHook(process.cwd(), opts.respace ? { respace: true } : {});
3239
3263
  process.stderr.write(
3240
3264
  result ? `codebuddy: synced ${result.reindex.facts.imported} new / ${result.reindex.facts.updated} updated fact(s)${result.reindex.reembedding ? `, ${result.reindex.reembedding} re-embedding` : ""}${result.drained ? "" : " (embeddings still pending)"}
3241
- ` : "codebuddy: no PostgreSQL configured; nothing to sync\n"
3265
+ ` : "codebuddy: nothing to sync (no PostgreSQL configured, or another sync is running)\n"
3242
3266
  );
3243
3267
  });
3244
3268
  });
@@ -3249,7 +3273,7 @@ function registerHooksCommands(program, runSafely2) {
3249
3273
  });
3250
3274
  hooks.command("reset").description("[hook] Forget what context was sent this session (after compaction or /clear).").action(async () => {
3251
3275
  await failSoft(async () => {
3252
- await runResetHook(await readHookInput());
3276
+ await runResetHook(await readHookInput(), writeStdout);
3253
3277
  });
3254
3278
  });
3255
3279
  }
@@ -3263,24 +3287,55 @@ function assertHookClient(value) {
3263
3287
  }
3264
3288
  return client;
3265
3289
  }
3266
- async function reconcileRules(client, mode) {
3267
- 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
+ });
3268
3296
  if (result.action !== "unchanged") {
3269
3297
  const where = relative2(process.cwd(), result.path) || result.path;
3270
3298
  console.log(
3271
- `${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)`
3272
3300
  );
3273
3301
  }
3274
3302
  }
3275
- function syncInBackground(cwd) {
3303
+ function warnIfHookCommandStale() {
3304
+ execFile2(
3305
+ "codebuddy",
3306
+ ["--version"],
3307
+ { timeout: 1e4, shell: process.platform === "win32" },
3308
+ (error, stdout) => {
3309
+ const found = stdout.trim();
3310
+ if (error || !found) {
3311
+ console.log(
3312
+ pc7.yellow(
3313
+ " ! `codebuddy` is not on PATH, so the hooks cannot run. Install it globally: npm i -g @orquentra/codebuddy"
3314
+ )
3315
+ );
3316
+ } else if (found !== VERSION) {
3317
+ console.log(
3318
+ pc7.yellow(
3319
+ ` ! The hooks will run codebuddy ${found} from PATH, not ${VERSION}. Update it: npm i -g @orquentra/codebuddy@${VERSION}`
3320
+ )
3321
+ );
3322
+ }
3323
+ }
3324
+ );
3325
+ }
3326
+ function syncInBackground(cwd, respace = false) {
3276
3327
  const entry = process.argv[1];
3277
3328
  if (!entry) return;
3278
3329
  try {
3279
- spawn3(process.execPath, [...process.execArgv, entry, "hooks", "sync"], {
3280
- cwd,
3281
- detached: true,
3282
- stdio: "ignore"
3283
- }).unref();
3330
+ spawn3(
3331
+ process.execPath,
3332
+ [...process.execArgv, entry, "hooks", "sync", ...respace ? ["--respace"] : []],
3333
+ {
3334
+ cwd,
3335
+ detached: true,
3336
+ stdio: "ignore"
3337
+ }
3338
+ ).unref();
3284
3339
  } catch {
3285
3340
  }
3286
3341
  }
@@ -3401,15 +3456,27 @@ function registerLlmCommands(program, runSafely2) {
3401
3456
  }
3402
3457
 
3403
3458
  // src/cli/commands/indexing.ts
3459
+ var VECTOR_BUDGET_MS = 10 * 6e4;
3404
3460
  function registerIndexCommands(program, runSafely2) {
3405
- 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) => {
3406
3462
  await runSafely2(async () => {
3407
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;
3408
3466
  if (opts.json) {
3409
- console.log(JSON.stringify(result, null, 2));
3467
+ console.log(JSON.stringify({ ...result, ...vectors ? { vectors } : {} }, null, 2));
3410
3468
  return;
3411
3469
  }
3412
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
+ }
3413
3480
  });
3414
3481
  });
3415
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) => {
@@ -3447,81 +3514,6 @@ function printResult(result, compact = false) {
3447
3514
  // src/cli/commands/init-wizard.ts
3448
3515
  import { writeFile as writeFile2 } from "fs/promises";
3449
3516
  import { confirm, isCancel, log, note, password, spinner, text } from "@clack/prompts";
3450
-
3451
- // src/cli/commands/postgres-docker.ts
3452
- import { spawn as spawn4 } from "child_process";
3453
- import { existsSync as existsSync3 } from "fs";
3454
- import { dirname as dirname11, resolve as resolve12 } from "path";
3455
- import { fileURLToPath as fileURLToPath2 } from "url";
3456
- function resolveBundledComposeFile() {
3457
- const here = dirname11(fileURLToPath2(import.meta.url));
3458
- const candidates = [
3459
- // installed package: dist/cli/commands/ -> root
3460
- resolve12(here, "..", "..", "..", "docker-compose.yml"),
3461
- // source layout: src/cli/commands/ -> root
3462
- resolve12(here, "..", "..", "..", "..", "docker-compose.yml"),
3463
- resolve12(here, "..", "..", "docker-compose.yml"),
3464
- resolve12(process.cwd(), "docker-compose.yml")
3465
- ];
3466
- for (const candidate of candidates) {
3467
- if (existsSync3(candidate)) return candidate;
3468
- }
3469
- throw new Error(`Could not locate docker-compose.yml. Looked in:
3470
- ${candidates.join("\n ")}`);
3471
- }
3472
- async function runDockerCompose(args, options = {}) {
3473
- const spawnImpl = options.spawnImpl ?? spawn4;
3474
- const composeFile = resolveBundledComposeFile();
3475
- const attempts = [
3476
- { kind: "v2", bin: "docker", args: ["compose", "-f", composeFile, ...args] },
3477
- { kind: "v1", bin: "docker-compose", args: ["-f", composeFile, ...args] }
3478
- ];
3479
- let lastSpawnError = null;
3480
- for (const attempt of attempts) {
3481
- try {
3482
- return await spawnOnce(spawnImpl, attempt);
3483
- } catch (error) {
3484
- const err = error;
3485
- if (err.code !== "ENOENT") throw err;
3486
- lastSpawnError = err;
3487
- }
3488
- }
3489
- throw new Error(
3490
- `Could not find docker. Install Docker Desktop (or docker-compose v1) and ensure it is on your PATH. (${lastSpawnError?.message ?? "spawn ENOENT"})`
3491
- );
3492
- }
3493
- function spawnOnce(spawnImpl, attempt) {
3494
- return new Promise((resolvePromise, rejectPromise) => {
3495
- const child = spawnImpl(attempt.bin, attempt.args, {
3496
- stdio: ["ignore", "pipe", "pipe"]
3497
- });
3498
- let stdout = "";
3499
- let stderr = "";
3500
- child.stdout?.on("data", (chunk) => {
3501
- stdout += String(chunk);
3502
- });
3503
- child.stderr?.on("data", (chunk) => {
3504
- stderr += String(chunk);
3505
- });
3506
- child.on("error", (error) => {
3507
- rejectPromise(error);
3508
- });
3509
- child.on("close", (code) => {
3510
- resolvePromise({ code: code ?? 0, stdout, stderr });
3511
- });
3512
- });
3513
- }
3514
- async function postgresUp(options) {
3515
- return runDockerCompose(["up", "-d"], options ?? {});
3516
- }
3517
- async function postgresDown(options) {
3518
- return runDockerCompose(["down"], options ?? {});
3519
- }
3520
- async function postgresStatus(options) {
3521
- return runDockerCompose(["ps"], options ?? {});
3522
- }
3523
-
3524
- // src/cli/commands/init-wizard.ts
3525
3517
  function bail() {
3526
3518
  log.warn("Aborted. No config was written.");
3527
3519
  process.exit(1);
@@ -3587,7 +3579,7 @@ async function runInitWizard() {
3587
3579
  if (result.code === 0) {
3588
3580
  dockerSpinner.stop("Postgres container started.");
3589
3581
  for (let attempt = 0; attempt < 10 && !connected; attempt++) {
3590
- await new Promise((resolve14) => setTimeout(resolve14, 500));
3582
+ await new Promise((resolve13) => setTimeout(resolve13, 500));
3591
3583
  connected = await pingOnce(databaseUrl);
3592
3584
  }
3593
3585
  if (!connected) {
@@ -3776,7 +3768,7 @@ function registerMapCommands(program, runSafely2) {
3776
3768
  }
3777
3769
 
3778
3770
  // src/cli/commands/memory-review.ts
3779
- import { spawn as spawn5 } from "child_process";
3771
+ import { spawn as spawn4 } from "child_process";
3780
3772
  import { relative as relative3 } from "path";
3781
3773
  import pc12 from "picocolors";
3782
3774
  function registerMemoryCommands(program, runSafely2) {
@@ -3872,15 +3864,15 @@ function truncate(value, max) {
3872
3864
  function openEditor(path) {
3873
3865
  const editor = process.env.EDITOR ?? process.env.VISUAL ?? "vi";
3874
3866
  return new Promise((resolvePromise, reject) => {
3875
- const child = spawn5(editor, [path], { stdio: "inherit" });
3867
+ const child = spawn4(editor, [path], { stdio: "inherit" });
3876
3868
  child.on("error", reject);
3877
3869
  child.on("close", () => resolvePromise());
3878
3870
  });
3879
3871
  }
3880
3872
 
3881
3873
  // src/cli/commands/models.ts
3882
- import { existsSync as existsSync4 } from "fs";
3883
- import { join as join15 } from "path";
3874
+ import { existsSync as existsSync3 } from "fs";
3875
+ import { join as join14 } from "path";
3884
3876
  import { spinner as spinner2 } from "@clack/prompts";
3885
3877
  import pc13 from "picocolors";
3886
3878
 
@@ -4129,12 +4121,15 @@ async function calibrateEmbedder(embed) {
4129
4121
  (_, i) => at(Math.round((0.05 + i * 0.01) * 100) / 100, "dev")
4130
4122
  );
4131
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);
4132
4126
  return {
4133
4127
  auc,
4134
4128
  recallAt1: answered === 0 ? 0 : hits / answered,
4135
4129
  mrr: answered === 0 ? 0 : reciprocal / answered,
4136
4130
  best,
4137
4131
  test: at(best.floor, "test"),
4132
+ recall: { dev: recallBest, test: at(recallBest.floor, "test") },
4138
4133
  at
4139
4134
  };
4140
4135
  }
@@ -4154,7 +4149,7 @@ async function withSpinner(label, work, done) {
4154
4149
  progress.stop(done(value));
4155
4150
  return value;
4156
4151
  } catch (error) {
4157
- progress.stop(pc13.red(`\u2717 ${label}`), 1);
4152
+ progress.error(pc13.red(`\u2717 ${label}`));
4158
4153
  throw error;
4159
4154
  }
4160
4155
  }
@@ -4162,8 +4157,8 @@ function keyPresent(provider, apiKey) {
4162
4157
  return Boolean(process.env[KEY_ENV2[provider]] || provider === "huggingface" && apiKey);
4163
4158
  }
4164
4159
  function localModelDownloaded(id, revision) {
4165
- const dir = join15(modelCacheDir(), id, ...revision ? [revision] : []);
4166
- return existsSync4(join15(dir, "onnx", "model_quantized.onnx"));
4160
+ const dir = join14(modelCacheDir(), id, ...revision ? [revision] : []);
4161
+ return existsSync3(join14(dir, "onnx", "model_quantized.onnx"));
4167
4162
  }
4168
4163
  async function vectorCounts(postgresUrl, namespace, space) {
4169
4164
  const client = createDatabaseClient({ postgresUrl, maxConnections: 1, connectTimeoutSeconds: 3 });
@@ -4195,7 +4190,12 @@ async function printStatus() {
4195
4190
  const next = [];
4196
4191
  console.log(pc13.bold("Embeddings"));
4197
4192
  console.log(row("provider", `${provider.type} ${pc13.dim(model)}`));
4198
- if (provider.type === "local") {
4193
+ if (provider.type === "local" && !localRuntimeInstalled()) {
4194
+ console.log(
4195
+ row("runtime", warn("@huggingface/transformers is not installed: dense recall is off"))
4196
+ );
4197
+ next.push("npm i -g @huggingface/transformers");
4198
+ } else if (provider.type === "local") {
4199
4199
  const ready = localModelDownloaded(LOCAL_EMBEDDING_MODEL.id, LOCAL_EMBEDDING_MODEL.revision);
4200
4200
  console.log(
4201
4201
  row(
@@ -4331,7 +4331,7 @@ Examples:
4331
4331
  );
4332
4332
  }
4333
4333
  if (!missingKey) {
4334
- syncInBackground(process.cwd());
4334
+ syncInBackground(process.cwd(), true);
4335
4335
  console.log(
4336
4336
  pc13.dim(" Re-embedding existing memory in the background (codebuddy hooks sync).")
4337
4337
  );
@@ -4371,7 +4371,7 @@ before keeping one on. A reranker never admits a fact, it only orders them.`
4371
4371
  });
4372
4372
  models.command("pull").description("Download the local models now instead of on first use.").action(async () => {
4373
4373
  await runSafely2(async () => {
4374
- const { LocalModelProvider, rerankLocally } = await import("./local-OTOY56B4.js");
4374
+ const { LocalModelProvider, rerankLocally } = await import("./local-ST4WMRCG.js");
4375
4375
  const file = await readConfigFile();
4376
4376
  let started = Date.now();
4377
4377
  await withSpinner(
@@ -4416,19 +4416,30 @@ before keeping one on. A reranker never admits a fact, it only orders them.`
4416
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)")}`
4417
4417
  )
4418
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
+ );
4419
4425
  if (opts.dryRun) return;
4420
4426
  const file = await readConfigFile();
4421
4427
  await setConfigBlock("provider", {
4422
4428
  ...file.provider ?? { type: config.provider.type },
4423
- relevanceFloor: result.best.floor
4429
+ relevanceFloor: result.best.floor,
4430
+ recallFloor: result.recall.dev.floor
4424
4431
  });
4425
- 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
+ );
4426
4437
  });
4427
4438
  });
4428
4439
  }
4429
4440
 
4430
4441
  // src/cli/commands/plan.ts
4431
- import { spawn as spawn6 } from "child_process";
4442
+ import { spawn as spawn5 } from "child_process";
4432
4443
  import { relative as relative4 } from "path";
4433
4444
  import pc14 from "picocolors";
4434
4445
  function makeLifecycle(cwd = process.cwd()) {
@@ -4490,15 +4501,15 @@ function renderPlan(plan) {
4490
4501
  }
4491
4502
  function openEditor2(path) {
4492
4503
  const editor = process.env.EDITOR ?? process.env.VISUAL ?? "vi";
4493
- return new Promise((resolve14, reject) => {
4494
- const child = spawn6(editor, [path], { stdio: "inherit" });
4504
+ return new Promise((resolve13, reject) => {
4505
+ const child = spawn5(editor, [path], { stdio: "inherit" });
4495
4506
  child.on("error", reject);
4496
- child.on("close", () => resolve14());
4507
+ child.on("close", () => resolve13());
4497
4508
  });
4498
4509
  }
4499
4510
  function runGit(args) {
4500
- return new Promise((resolve14, reject) => {
4501
- const child = spawn6("git", args, { stdio: ["ignore", "pipe", "pipe"] });
4511
+ return new Promise((resolve13, reject) => {
4512
+ const child = spawn5("git", args, { stdio: ["ignore", "pipe", "pipe"] });
4502
4513
  let stdout = "";
4503
4514
  let stderr = "";
4504
4515
  child.stdout.on("data", (c) => {
@@ -4508,7 +4519,7 @@ function runGit(args) {
4508
4519
  stderr += String(c);
4509
4520
  });
4510
4521
  child.on("error", reject);
4511
- child.on("close", (code) => resolve14({ code: code ?? 0, stdout, stderr }));
4522
+ child.on("close", (code) => resolve13({ code: code ?? 0, stdout, stderr }));
4512
4523
  });
4513
4524
  }
4514
4525
  function registerPlanCommands(program, runSafely2) {
@@ -4822,8 +4833,8 @@ function severityBadge2(severity) {
4822
4833
  }
4823
4834
 
4824
4835
  // src/cli/commands/symbols.ts
4825
- import { readFile as readFile13 } from "fs/promises";
4826
- import { resolve as resolve13 } from "path";
4836
+ import { readFile as readFile12 } from "fs/promises";
4837
+ import { resolve as resolve12 } from "path";
4827
4838
  import pc18 from "picocolors";
4828
4839
  function registerSymbolsCommand(program, runSafely2) {
4829
4840
  program.command("symbols").argument("<path>", "Repo-relative or absolute path to a code file.").option("--json", "Print machine-readable JSON.").description("List a file's top-level symbols (functions, classes, types, exports).").action(async (path, opts) => {
@@ -4832,8 +4843,8 @@ function registerSymbolsCommand(program, runSafely2) {
4832
4843
  console.log(pc18.dim(`no symbol extraction for ${path} (unsupported language)`));
4833
4844
  return;
4834
4845
  }
4835
- const content = await readFile13(resolve13(path), "utf8");
4836
- const table = extractSymbolTable(content);
4846
+ const content = await readFile12(resolve12(path), "utf8");
4847
+ const table = extractSymbolTable(content, path);
4837
4848
  if (opts.json) {
4838
4849
  console.log(JSON.stringify({ path, symbols: table }, null, 2));
4839
4850
  return;
@@ -4860,9 +4871,9 @@ import { confirm as confirm2, isCancel as isCancel2, log as log2, note as note2,
4860
4871
  import pc19 from "picocolors";
4861
4872
 
4862
4873
  // src/core/global-config.ts
4863
- 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";
4864
4875
  import { homedir as homedir3 } from "os";
4865
- import { dirname as dirname12, join as join16 } from "path";
4876
+ import { dirname as dirname11, join as join15 } from "path";
4866
4877
  import { z as z2 } from "zod";
4867
4878
  var GLOBAL_DIR = ".codebuddy";
4868
4879
  var GLOBAL_FILE = "global.json";
@@ -4875,12 +4886,12 @@ var globalConfigSchema = z2.object({
4875
4886
  installPath: z2.string().optional()
4876
4887
  });
4877
4888
  function globalConfigPath(home = homedir3()) {
4878
- return join16(home, GLOBAL_DIR, GLOBAL_FILE);
4889
+ return join15(home, GLOBAL_DIR, GLOBAL_FILE);
4879
4890
  }
4880
4891
  async function readGlobalConfig(home = homedir3()) {
4881
4892
  const path = globalConfigPath(home);
4882
4893
  try {
4883
- const raw = await readFile14(path, "utf8");
4894
+ const raw = await readFile13(path, "utf8");
4884
4895
  return globalConfigSchema.parse(JSON.parse(raw));
4885
4896
  } catch (error) {
4886
4897
  if (error.code === "ENOENT") return {};
@@ -4889,7 +4900,7 @@ async function readGlobalConfig(home = homedir3()) {
4889
4900
  }
4890
4901
  async function writeGlobalConfig(next, home = homedir3()) {
4891
4902
  const path = globalConfigPath(home);
4892
- await mkdir8(dirname12(path), { recursive: true, mode: 448 });
4903
+ await mkdir8(dirname11(path), { recursive: true, mode: 448 });
4893
4904
  await writeFile3(path, `${JSON.stringify(next, null, 2)}
4894
4905
  `, { mode: 384 });
4895
4906
  await chmod(path, 384);
@@ -4902,32 +4913,6 @@ async function mergeGlobalConfig(patch, home = homedir3()) {
4902
4913
  return { path, merged };
4903
4914
  }
4904
4915
 
4905
- // src/cli/commands/docker-autostart.ts
4906
- import { spawn as spawn7 } from "child_process";
4907
- async function isDockerDaemonRunning() {
4908
- return new Promise((resolve14) => {
4909
- const child = spawn7("docker", ["info"], { stdio: "ignore" });
4910
- child.on("error", () => resolve14(false));
4911
- child.on("close", (code) => resolve14(code === 0));
4912
- });
4913
- }
4914
- async function tryStartDockerDesktop() {
4915
- if (process.platform !== "darwin") return { started: false };
4916
- return new Promise((resolve14) => {
4917
- const child = spawn7("open", ["-ga", "Docker"], { stdio: "ignore" });
4918
- child.on("error", () => resolve14({ started: false }));
4919
- child.on("close", (code) => resolve14({ started: code === 0 }));
4920
- });
4921
- }
4922
- async function waitForDockerDaemon(timeoutMs = 6e4) {
4923
- const deadline = Date.now() + timeoutMs;
4924
- while (Date.now() < deadline) {
4925
- if (await isDockerDaemonRunning()) return true;
4926
- await new Promise((resolve14) => setTimeout(resolve14, 1e3));
4927
- }
4928
- return false;
4929
- }
4930
-
4931
4916
  // src/cli/commands/use-command.ts
4932
4917
  function bail2() {
4933
4918
  log2.warn("Aborted.");
@@ -4955,15 +4940,17 @@ async function runUseCommand(options = {}) {
4955
4940
  const sigintHandler = () => bail2();
4956
4941
  process.on("SIGINT", sigintHandler);
4957
4942
  try {
4943
+ const interactive = options.interactive ?? process.stdin.isTTY === true;
4958
4944
  const folderName = basename2(process.cwd());
4959
4945
  const namespace = sanitizeNamespace(options.namespace ?? folderName);
4960
4946
  await initProjectScaffold();
4961
- const graphifyEnabled = options.graphify ?? unwrap2(
4947
+ if (options.namespace === void 0) await migrateLegacyNamespace();
4948
+ const graphifyEnabled = options.graphify ?? (interactive && unwrap2(
4962
4949
  await confirm2({
4963
4950
  message: "Set up Graphify for richer architecture context?",
4964
4951
  initialValue: true
4965
4952
  })
4966
- );
4953
+ ));
4967
4954
  note2(
4968
4955
  `Project folder: ${pc19.bold(process.cwd())}
4969
4956
  Namespace: ${pc19.bold(namespace)}`,
@@ -4974,6 +4961,8 @@ Namespace: ${pc19.bold(namespace)}`,
4974
4961
  let embeddings = projectConfig.provider?.type;
4975
4962
  if (embeddings) {
4976
4963
  log2.info(`Embeddings: ${embeddings} (change with ${pc19.cyan("codebuddy models use")}).`);
4964
+ } else if (!interactive) {
4965
+ embeddings = "local";
4977
4966
  } else {
4978
4967
  embeddings = unwrap2(
4979
4968
  await select({
@@ -5013,6 +5002,10 @@ Namespace: ${pc19.bold(namespace)}`,
5013
5002
  } else if (stored.hfToken) {
5014
5003
  hfToken = stored.hfToken;
5015
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
+ );
5016
5009
  } else {
5017
5010
  hfToken = unwrap2(
5018
5011
  await password2({
@@ -5022,13 +5015,13 @@ Namespace: ${pc19.bold(namespace)}`,
5022
5015
  if (!hfToken) log2.warn("No token: hosted embeddings will fail until HF_TOKEN is set.");
5023
5016
  }
5024
5017
  const initialUrl = options.postgresUrl ?? process.env.DATABASE_URL ?? stored.databaseUrl ?? DEFAULT_DB_URL;
5025
- const databaseUrl = unwrap2(
5018
+ const databaseUrl = options.postgresUrl ?? (interactive ? unwrap2(
5026
5019
  await text2({
5027
5020
  message: "Postgres URL (press Enter to accept)",
5028
5021
  initialValue: initialUrl,
5029
5022
  validate: (value) => value && value.length > 0 ? void 0 : "Postgres URL is required."
5030
5023
  })
5031
- );
5024
+ ) : initialUrl);
5032
5025
  const dbSpinner = spinner3();
5033
5026
  dbSpinner.start("Checking Postgres");
5034
5027
  let connected = await pingOnce2(databaseUrl);
@@ -5036,12 +5029,12 @@ Namespace: ${pc19.bold(namespace)}`,
5036
5029
  if (!connected && databaseUrl === DEFAULT_DB_URL) {
5037
5030
  dbSpinner.stop("Postgres unreachable on the default local URL.");
5038
5031
  }
5039
- const shouldUseDocker = !connected && databaseUrl === DEFAULT_DB_URL && (options.useDocker ?? unwrap2(
5032
+ const shouldUseDocker = !connected && databaseUrl === DEFAULT_DB_URL && (options.useDocker ?? (interactive && unwrap2(
5040
5033
  await confirm2({
5041
5034
  message: "Postgres is unavailable. Start the bundled Docker database now?",
5042
5035
  initialValue: true
5043
5036
  })
5044
- ));
5037
+ )));
5045
5038
  if (shouldUseDocker) {
5046
5039
  if (!await isDockerDaemonRunning()) {
5047
5040
  const dockerSpinner = spinner3();
@@ -5061,7 +5054,7 @@ Namespace: ${pc19.bold(namespace)}`,
5061
5054
  if (result.code === 0) {
5062
5055
  composeSpinner.stop("Postgres container started.");
5063
5056
  for (let attempt = 0; attempt < 12 && !connected; attempt++) {
5064
- await new Promise((resolve14) => setTimeout(resolve14, 500));
5057
+ await new Promise((resolve13) => setTimeout(resolve13, 500));
5065
5058
  connected = await pingOnce2(databaseUrl);
5066
5059
  }
5067
5060
  if (!connected) {
@@ -5076,7 +5069,7 @@ Namespace: ${pc19.bold(namespace)}`,
5076
5069
  } else if (!connected) {
5077
5070
  dbSpinner.stop(connected ? "Postgres reachable." : "Postgres unreachable at this URL.");
5078
5071
  log2.warn(
5079
- "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)."
5080
5073
  );
5081
5074
  bail2();
5082
5075
  }
@@ -5282,9 +5275,7 @@ function registerZvecCommands(program, runSafely2) {
5282
5275
  ...opts.rebuild ? { rebuild: true } : {}
5283
5276
  });
5284
5277
  if (code !== 0) throw new Error(`zg index exited with code ${code}`);
5285
- console.log(
5286
- pc21.dim("tip: add .zvec-grep/ to .gitignore; enable with `codebuddy zvec enable`")
5287
- );
5278
+ console.log(pc21.dim("next: codebuddy zvec enable (.zvec-grep/ ignores itself in git)"));
5288
5279
  });
5289
5280
  });
5290
5281
  zvec.command("enable").description("Use zvec-grep candidates in context_pack / context_before_edit.").action(async () => {
@@ -5319,7 +5310,7 @@ function createCli() {
5319
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(
5320
5311
  "--docker",
5321
5312
  "Start CodeBuddy's bundled Postgres container when the default DB is unavailable."
5322
- ).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(
5323
5314
  async (namespace, opts) => {
5324
5315
  await runSafely(async () => {
5325
5316
  await runUseCommand({
@@ -5328,7 +5319,8 @@ function createCli() {
5328
5319
  ...opts.skipClaude !== void 0 ? { skipClaude: opts.skipClaude } : {},
5329
5320
  ...opts.skipCodex !== void 0 ? { skipCodex: opts.skipCodex } : {},
5330
5321
  ...opts.graphify !== void 0 ? { graphify: opts.graphify } : {},
5331
- ...opts.docker !== void 0 ? { useDocker: opts.docker } : {}
5322
+ ...opts.docker !== void 0 ? { useDocker: opts.docker } : {},
5323
+ ...opts.yes || opts.nonInteractive ? { interactive: false } : {}
5332
5324
  });
5333
5325
  });
5334
5326
  }
@@ -5472,7 +5464,7 @@ function createCli() {
5472
5464
  console.log(`Claude Desktop config: ${result.path}`);
5473
5465
  for (const entry of result.entries) {
5474
5466
  console.log(
5475
- ` ${entry.key} namespace=${entry.namespace ?? "?"} db=${entry.databaseUrl ?? "?"}`
5467
+ ` ${entry.key} namespace=${entry.namespace ?? "?"} db=${entry.databaseUrl ? String(redactSecrets(entry.databaseUrl)) : "?"}`
5476
5468
  );
5477
5469
  }
5478
5470
  });
@@ -5517,7 +5509,7 @@ function createCli() {
5517
5509
  console.log(`Codex config: ${result.path}`);
5518
5510
  for (const entry of result.entries) {
5519
5511
  console.log(
5520
- ` ${entry.key} namespace=${entry.namespace ?? "?"} db=${entry.databaseUrl ?? "?"}`
5512
+ ` ${entry.key} namespace=${entry.namespace ?? "?"} db=${entry.databaseUrl ? String(redactSecrets(entry.databaseUrl)) : "?"}`
5521
5513
  );
5522
5514
  }
5523
5515
  });
@@ -5703,6 +5695,9 @@ function printDoctor(report) {
5703
5695
  console.log(
5704
5696
  ` runtime=${report.namespace.runtime ?? "unset"} default=${report.namespace.expectedDefault}`
5705
5697
  );
5698
+ if (report.namespace.migrated) {
5699
+ console.log(` migrated legacy namespace "default" to "${report.namespace.migrated}"`);
5700
+ }
5706
5701
  if (report.namespace.warning) console.log(` ${report.namespace.warning}`);
5707
5702
  console.log(
5708
5703
  `vectors: ${report.vectors.count} index=${report.vectors.index ?? "missing"} tuning=${report.vectors.needsTuning ? "recommended" : "not-needed"}`
@@ -5732,9 +5727,12 @@ function printDbDoctor(report) {
5732
5727
  if (report.pgvector.error) console.log(` ${report.pgvector.error}`);
5733
5728
  console.log(`namespace: ${report.namespace.ok ? ok2 : warn2}`);
5734
5729
  console.log(` runtime=${report.namespace.runtime ?? "unset"}`);
5730
+ if (report.namespace.migrated) {
5731
+ console.log(` migrated legacy namespace "default" to "${report.namespace.migrated}"`);
5732
+ }
5735
5733
  if (report.namespace.warning) console.log(` ${report.namespace.warning}`);
5736
5734
  }
5737
5735
  export {
5738
5736
  createCli
5739
5737
  };
5740
- //# sourceMappingURL=main-HFHLGID4.js.map
5738
+ //# sourceMappingURL=main-AHNGHC4L.js.map