@orquentra/codebuddy 2.14.0 → 2.15.1

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 (93) hide show
  1. package/ARCHITECTURE.md +98 -0
  2. package/README.md +87 -6
  3. package/dist/{chunk-YNSTIMCF.js → chunk-4DTO2E4Y.js} +352 -157
  4. package/dist/chunk-4DTO2E4Y.js.map +1 -0
  5. package/dist/{chunk-CAEPHLHC.js → chunk-57642NGU.js} +2 -44
  6. package/dist/chunk-57642NGU.js.map +1 -0
  7. package/dist/chunk-6OQKL35B.js +55 -0
  8. package/dist/chunk-6OQKL35B.js.map +1 -0
  9. package/dist/{chunk-4DKYKDHI.js → chunk-7IPSDDO5.js} +5 -3
  10. package/dist/{chunk-4DKYKDHI.js.map → chunk-7IPSDDO5.js.map} +1 -1
  11. package/dist/chunk-7JPUMXFE.js +413 -0
  12. package/dist/chunk-7JPUMXFE.js.map +1 -0
  13. package/dist/{chunk-LRQNPYCK.js → chunk-AFXWCSQ4.js} +13 -7
  14. package/dist/chunk-AFXWCSQ4.js.map +1 -0
  15. package/dist/chunk-BNZDEU5K.js +368 -0
  16. package/dist/chunk-BNZDEU5K.js.map +1 -0
  17. package/dist/chunk-EMWGLKPR.js +45 -0
  18. package/dist/chunk-EMWGLKPR.js.map +1 -0
  19. package/dist/{chunk-YJPCS2GP.js → chunk-GNV4X67F.js} +59 -20
  20. package/dist/chunk-GNV4X67F.js.map +1 -0
  21. package/dist/{chunk-YWDH6DQ6.js → chunk-IM5RFEW4.js} +21 -19
  22. package/dist/{chunk-YWDH6DQ6.js.map → chunk-IM5RFEW4.js.map} +1 -1
  23. package/dist/chunk-K4XNTYV7.js +136 -0
  24. package/dist/chunk-K4XNTYV7.js.map +1 -0
  25. package/dist/{chunk-ESZXCZIN.js → chunk-MNSMW4MZ.js} +33 -5
  26. package/dist/chunk-MNSMW4MZ.js.map +1 -0
  27. package/dist/chunk-NNJSAYHY.js +372 -0
  28. package/dist/chunk-NNJSAYHY.js.map +1 -0
  29. package/dist/{chunk-XSNW6Y3X.js → chunk-OO7Q7XZF.js} +28 -24
  30. package/dist/chunk-OO7Q7XZF.js.map +1 -0
  31. package/dist/{chunk-L6BRYZ6G.js → chunk-PZ4EBFIA.js} +7 -7
  32. package/dist/{chunk-L6BRYZ6G.js.map → chunk-PZ4EBFIA.js.map} +1 -1
  33. package/dist/chunk-RGIVHKBH.js +156 -0
  34. package/dist/chunk-RGIVHKBH.js.map +1 -0
  35. package/dist/{chunk-LW75SKZR.js → chunk-RKF4HBBQ.js} +126 -18
  36. package/dist/chunk-RKF4HBBQ.js.map +1 -0
  37. package/dist/{chunk-7QMHIJBA.js → chunk-VBDM7HC5.js} +969 -968
  38. package/dist/chunk-VBDM7HC5.js.map +1 -0
  39. package/dist/chunk-VF7DKRIU.js +50 -0
  40. package/dist/chunk-VF7DKRIU.js.map +1 -0
  41. package/dist/{chunk-KNR2P2UY.js → chunk-Y4QQML24.js} +21 -6
  42. package/dist/chunk-Y4QQML24.js.map +1 -0
  43. package/dist/cli/index.js +4 -4
  44. package/dist/config-S2BDMT4B.js +15 -0
  45. package/dist/expand-TONFVT5D.js +98 -0
  46. package/dist/expand-TONFVT5D.js.map +1 -0
  47. package/dist/file-vectors-VREL75MA.js +19 -0
  48. package/dist/index-facts-MBJKBYA3.js +38 -0
  49. package/dist/index-pass-RP23G2CR.js +15 -0
  50. package/dist/index.js +24 -22
  51. package/dist/{install-EU35ERK5.js → install-MCJIJMFF.js} +11 -10
  52. package/dist/{lean-43ADYTBW.js → lean-CBHVW6JM.js} +8 -7
  53. package/dist/local-TH5XCLVJ.js +48 -0
  54. package/dist/local-llm-GZREB5DF.js +19 -0
  55. package/dist/local-llm-GZREB5DF.js.map +1 -0
  56. package/dist/{main-SFC5LV4E.js → main-QF6Z552J.js} +346 -140
  57. package/dist/main-QF6Z552J.js.map +1 -0
  58. package/dist/{memory-TGHVTBME.js → memory-LSZKSF53.js} +9 -6
  59. package/dist/{memory-TGHVTBME.js.map → memory-LSZKSF53.js.map} +1 -1
  60. package/dist/{operations-6Z6ZDVBP.js → operations-BPI633VN.js} +8 -7
  61. package/dist/operations-BPI633VN.js.map +1 -0
  62. package/dist/{rerank-IQAEWLQR.js → rerank-EPCTSEVP.js} +7 -4
  63. package/dist/{rerank-IQAEWLQR.js.map → rerank-EPCTSEVP.js.map} +1 -1
  64. package/dist/{runtime-W5AW32SD.js → runtime-W5ACTDFS.js} +9 -8
  65. package/dist/runtime-W5ACTDFS.js.map +1 -0
  66. package/dist/summaries-W2J5FCLG.js +32 -0
  67. package/dist/summaries-W2J5FCLG.js.map +1 -0
  68. package/dist/{zvec-24HLAFGN.js → zvec-LSA2IRAU.js} +13 -5
  69. package/dist/zvec-LSA2IRAU.js.map +1 -0
  70. package/package.json +3 -2
  71. package/dist/chunk-7QMHIJBA.js.map +0 -1
  72. package/dist/chunk-CAEPHLHC.js.map +0 -1
  73. package/dist/chunk-ESC4Y3L7.js +0 -204
  74. package/dist/chunk-ESC4Y3L7.js.map +0 -1
  75. package/dist/chunk-ESZXCZIN.js.map +0 -1
  76. package/dist/chunk-KNR2P2UY.js.map +0 -1
  77. package/dist/chunk-LRQNPYCK.js.map +0 -1
  78. package/dist/chunk-LW75SKZR.js.map +0 -1
  79. package/dist/chunk-SFMIMLXR.js +0 -63
  80. package/dist/chunk-SFMIMLXR.js.map +0 -1
  81. package/dist/chunk-XSNW6Y3X.js.map +0 -1
  82. package/dist/chunk-YJPCS2GP.js.map +0 -1
  83. package/dist/chunk-YNSTIMCF.js.map +0 -1
  84. package/dist/config-GEZFLXK6.js +0 -12
  85. package/dist/main-SFC5LV4E.js.map +0 -1
  86. package/dist/summaries-32PHOB6L.js +0 -20
  87. /package/dist/{config-GEZFLXK6.js.map → config-S2BDMT4B.js.map} +0 -0
  88. /package/dist/{install-EU35ERK5.js.map → file-vectors-VREL75MA.js.map} +0 -0
  89. /package/dist/{lean-43ADYTBW.js.map → index-facts-MBJKBYA3.js.map} +0 -0
  90. /package/dist/{operations-6Z6ZDVBP.js.map → index-pass-RP23G2CR.js.map} +0 -0
  91. /package/dist/{runtime-W5AW32SD.js.map → install-MCJIJMFF.js.map} +0 -0
  92. /package/dist/{summaries-32PHOB6L.js.map → lean-CBHVW6JM.js.map} +0 -0
  93. /package/dist/{zvec-24HLAFGN.js.map → local-TH5XCLVJ.js.map} +0 -0
@@ -1,21 +1,12 @@
1
- import {
2
- isDockerDaemonRunning,
3
- tryStartDockerDesktop,
4
- waitForDockerDaemon
5
- } from "./chunk-5UH3BEQR.js";
6
- import {
7
- postgresDown,
8
- postgresStatus,
9
- postgresUp
10
- } from "./chunk-CSRD5OQA.js";
11
1
  import {
12
2
  runPreToolHookCommand
13
3
  } from "./chunk-UOTKHSQ5.js";
14
4
  import {
15
- failSoft,
16
- readHookInput,
17
- writeStdout
18
- } from "./chunk-FJOTP632.js";
5
+ LOCAL_LLM_SIZES,
6
+ addLocalLlm,
7
+ printLocalLlmStatus,
8
+ removeLocalLlm
9
+ } from "./chunk-K4XNTYV7.js";
19
10
  import {
20
11
  WORKFLOW_CLIENTS,
21
12
  clientDisplayName,
@@ -41,7 +32,43 @@ import {
41
32
  setHooks,
42
33
  uninstallClient,
43
34
  writeProjectMcpConfig
44
- } from "./chunk-L6BRYZ6G.js";
35
+ } from "./chunk-PZ4EBFIA.js";
36
+ import {
37
+ renderLeanBlock,
38
+ runLeanHook
39
+ } from "./chunk-AFXWCSQ4.js";
40
+ import {
41
+ isDockerDaemonRunning,
42
+ tryStartDockerDesktop,
43
+ waitForDockerDaemon
44
+ } from "./chunk-5UH3BEQR.js";
45
+ import {
46
+ postgresDown,
47
+ postgresStatus,
48
+ postgresUp
49
+ } from "./chunk-CSRD5OQA.js";
50
+ import {
51
+ failSoft,
52
+ readHookInput,
53
+ writeStdout
54
+ } from "./chunk-FJOTP632.js";
55
+ import {
56
+ loadLlmSummaries,
57
+ summarizeRepository
58
+ } from "./chunk-7JPUMXFE.js";
59
+ import {
60
+ listIndexFacts,
61
+ pruneIndexFacts
62
+ } from "./chunk-BNZDEU5K.js";
63
+ import {
64
+ chatJson,
65
+ coolingDown,
66
+ usageToday
67
+ } from "./chunk-MNSMW4MZ.js";
68
+ import {
69
+ runIndexModelPass
70
+ } from "./chunk-VF7DKRIU.js";
71
+ import "./chunk-6OQKL35B.js";
45
72
  import {
46
73
  AUTOMATION_MODES,
47
74
  CLIENT_ADAPTERS,
@@ -52,10 +79,6 @@ import {
52
79
  parseMode,
53
80
  supportedModes
54
81
  } from "./chunk-V7PT6ER5.js";
55
- import {
56
- renderLeanBlock,
57
- runLeanHook
58
- } from "./chunk-LRQNPYCK.js";
59
82
  import {
60
83
  TELEMETRY_NOTICE,
61
84
  findReuse,
@@ -72,12 +95,7 @@ import {
72
95
  traceCliCommand,
73
96
  watchRepository,
74
97
  writeTelemetryState
75
- } from "./chunk-YJPCS2GP.js";
76
- import {
77
- DEFAULT_ZVEC_MODEL,
78
- buildZvecIndex,
79
- zvecStatus
80
- } from "./chunk-LW75SKZR.js";
98
+ } from "./chunk-GNV4X67F.js";
81
99
  import {
82
100
  HookStagingStore,
83
101
  PlanFileStore,
@@ -107,7 +125,7 @@ import {
107
125
  runStageHook,
108
126
  runSyncHook,
109
127
  syncReuseVectors
110
- } from "./chunk-XSNW6Y3X.js";
128
+ } from "./chunk-OO7Q7XZF.js";
111
129
  import {
112
130
  parseTraceLines,
113
131
  readTraces,
@@ -117,12 +135,33 @@ import {
117
135
  SessionCapsuleLedger,
118
136
  detectTestCommand,
119
137
  estimateTokens
120
- } from "./chunk-4DKYKDHI.js";
138
+ } from "./chunk-7IPSDDO5.js";
121
139
  import {
122
140
  hookRoot
123
141
  } from "./chunk-TGFTKWOR.js";
124
142
  import "./chunk-GQKWUHMA.js";
125
143
  import "./chunk-DFV2SK6C.js";
144
+ import {
145
+ DEFAULT_ZVEC_MODEL,
146
+ buildZvecIndex,
147
+ zvecStatus
148
+ } from "./chunk-RKF4HBBQ.js";
149
+ import {
150
+ buildArchitectureMap,
151
+ generateGraphifyGraph,
152
+ indexRepository,
153
+ loadFreshIndex,
154
+ loadGraphifyMap,
155
+ loadProjectArchitectureMap,
156
+ neighbours,
157
+ queryMap,
158
+ setupGraphify,
159
+ syncFileVectors
160
+ } from "./chunk-4DTO2E4Y.js";
161
+ import {
162
+ extractSymbolTable,
163
+ supportsSymbols
164
+ } from "./chunk-Y7D7PUBE.js";
126
165
  import {
127
166
  bootstrapDatabase,
128
167
  buildProviderFromConfig,
@@ -134,57 +173,25 @@ import {
134
173
  pingDatabase,
135
174
  runDoctor,
136
175
  runMigrations
137
- } from "./chunk-YWDH6DQ6.js";
176
+ } from "./chunk-IM5RFEW4.js";
177
+ import "./chunk-57642NGU.js";
138
178
  import {
139
179
  MemoryFileStore,
140
180
  VERSION
141
- } from "./chunk-7QMHIJBA.js";
181
+ } from "./chunk-VBDM7HC5.js";
142
182
  import {
143
183
  computeFactHash,
144
184
  generateEntityId
145
- } from "./chunk-CAEPHLHC.js";
146
- import "./chunk-6JCKMIQY.js";
147
- import {
148
- localRuntimeInstalled,
149
- modelCacheDir
150
- } from "./chunk-3QGW53GD.js";
151
- import {
152
- loadLlmSummaries,
153
- summarizeRepository
154
- } from "./chunk-ESC4Y3L7.js";
155
- import {
156
- chatJson,
157
- coolingDown,
158
- usageToday
159
- } from "./chunk-ESZXCZIN.js";
160
- import {
161
- buildArchitectureMap,
162
- generateGraphifyGraph,
163
- indexRepository,
164
- loadFreshIndex,
165
- loadGraphifyMap,
166
- loadProjectArchitectureMap,
167
- neighbours,
168
- queryMap,
169
- setupGraphify,
170
- syncFileVectors
171
- } from "./chunk-YNSTIMCF.js";
172
- import {
173
- extractSymbolTable,
174
- supportsSymbols
175
- } from "./chunk-Y7D7PUBE.js";
185
+ } from "./chunk-EMWGLKPR.js";
176
186
  import {
177
187
  redactPersisted
178
188
  } from "./chunk-AL6VBTNA.js";
189
+ import "./chunk-6JCKMIQY.js";
179
190
  import {
180
- DEFAULT_CLOUD_EMBEDDING_MODELS,
181
- DEFAULT_RERANK_MODELS,
182
- LOCAL_EMBEDDING_MODEL,
183
- getModelDefinition
184
- } from "./chunk-HPOR6YXA.js";
185
- import {
191
+ mergeGlobalConfig,
192
+ readGlobalConfig,
186
193
  resolveLlmConfig
187
- } from "./chunk-SFMIMLXR.js";
194
+ } from "./chunk-RGIVHKBH.js";
188
195
  import {
189
196
  PLAN_POLICIES,
190
197
  configPath,
@@ -202,10 +209,21 @@ import {
202
209
  setPlanPolicy,
203
210
  setZvecEnabled,
204
211
  writeConfigFile
205
- } from "./chunk-KNR2P2UY.js";
212
+ } from "./chunk-Y4QQML24.js";
206
213
  import {
207
214
  writeAtomic
208
215
  } from "./chunk-CG5B3LYG.js";
216
+ import "./chunk-NNJSAYHY.js";
217
+ import {
218
+ localRuntimeInstalled,
219
+ modelCacheDir
220
+ } from "./chunk-3QGW53GD.js";
221
+ import {
222
+ DEFAULT_CLOUD_EMBEDDING_MODELS,
223
+ DEFAULT_RERANK_MODELS,
224
+ LOCAL_EMBEDDING_MODEL,
225
+ getModelDefinition
226
+ } from "./chunk-HPOR6YXA.js";
209
227
  import "./chunk-PZ5AY32C.js";
210
228
 
211
229
  // src/cli/main.ts
@@ -1988,8 +2006,9 @@ var KEY_ENV = { groq: "GROQ_API_KEY", openrouter: "OPENROUTER_API_KEY" };
1988
2006
  var WATCH_BATCH = 8;
1989
2007
  async function llmStatusReport(root, env = process.env) {
1990
2008
  const block = (await readConfigFile(root)).llm;
1991
- const provider = block?.provider ?? "groq";
1992
2009
  const config = await resolveLlmConfig(root, env);
2010
+ const provider = config.enabled ? config.provider : block?.provider ?? "groq";
2011
+ const keyName = provider === "local" ? "" : KEY_ENV[provider];
1993
2012
  const model = config.enabled ? config.model : block?.model ?? null;
1994
2013
  const key = `${provider}:${model}`;
1995
2014
  const { manifest } = await loadFreshIndex(root);
@@ -1998,8 +2017,9 @@ async function llmStatusReport(root, env = process.env) {
1998
2017
  reason: config.enabled ? "on" : config.reason,
1999
2018
  provider,
2000
2019
  model,
2001
- keyEnv: env.CODEBUDDY_LLM_API_KEY ? "CODEBUDDY_LLM_API_KEY" : KEY_ENV[provider],
2002
- keyPresent: Boolean(env.CODEBUDDY_LLM_API_KEY || env[KEY_ENV[provider]]),
2020
+ keyEnv: provider === "local" ? "none needed" : env.CODEBUDDY_LLM_API_KEY ? "CODEBUDDY_LLM_API_KEY" : keyName,
2021
+ // Enabled implies a key (environment or saved by `codebuddy`).
2022
+ keyPresent: provider === "local" || config.enabled || Boolean(env.CODEBUDDY_LLM_API_KEY || env[keyName]),
2003
2023
  features: config.enabled ? config.features : null,
2004
2024
  usageToday: (await usageToday(root))[key] ?? 0,
2005
2025
  dailyTokenBudget: config.enabled ? config.dailyTokenBudget : block?.dailyTokenBudget ?? 4e5,
@@ -2040,7 +2060,7 @@ function registerLlmCommands(program, runSafely2) {
2040
2060
  if (!report.keyPresent) console.log(pc8.dim(`next: export ${report.keyEnv}=<your key>`));
2041
2061
  });
2042
2062
  });
2043
- llm.command("enable").option("--provider <provider>", "groq or openrouter", "groq").option("--model <model>", "Model id (default: llama-3.1-8b-instant on groq).").description("Turn the layer on in .codebuddy/config.json (the key stays in your environment).").action(async (opts) => {
2063
+ llm.command("enable").option("--provider <provider>", "groq or openrouter", "groq").option("--model <model>", "Model id (default: qwen/qwen3.8-27b).").description("Turn the layer on in .codebuddy/config.json (the key stays in your environment).").action(async (opts) => {
2044
2064
  await runSafely2(async () => {
2045
2065
  if (opts.provider !== "groq" && opts.provider !== "openrouter") {
2046
2066
  throw new Error("--provider must be groq or openrouter");
@@ -2087,7 +2107,7 @@ function registerLlmCommands(program, runSafely2) {
2087
2107
  onProgress: (done, total) => console.log(pc8.dim(`summarized ${done}/${total}`))
2088
2108
  });
2089
2109
  console.log(
2090
- `${result.summarized} summarized, ${result.skipped} skipped, ${result.remaining} remaining (${result.stoppedBy})`
2110
+ `${result.summarized} summarized, ${result.skipped} skipped, ${result.timedOut} timed out, ${result.remaining} remaining (${result.stoppedBy})`
2091
2111
  );
2092
2112
  });
2093
2113
  });
@@ -2096,20 +2116,31 @@ function registerLlmCommands(program, runSafely2) {
2096
2116
  // src/cli/commands/indexing.ts
2097
2117
  var VECTOR_BUDGET_MS = 10 * 6e4;
2098
2118
  function registerIndexCommands(program, runSafely2) {
2099
- 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) => {
2119
+ 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.").option(
2120
+ "--cards",
2121
+ "Also write intent cards and index facts with the local (or BYOK) model, then embed the cards."
2122
+ ).description("Index the repository: content hashes and lightweight file summaries.").action(async (opts) => {
2100
2123
  await runSafely2(async () => {
2101
2124
  const result = await indexRepository({ ...opts.full ? { full: true } : {} });
2102
2125
  const local = opts.vectors && (await loadRuntimeConfig(process.cwd(), { requireDatabase: false }).catch(() => null))?.provider.type === "local";
2103
2126
  const vectorStarted = Date.now();
2104
2127
  const vectors = local ? await syncFileVectors(process.cwd(), VECTOR_BUDGET_MS) : null;
2105
2128
  const reuseVectors = local ? await syncReuseVectors(process.cwd(), VECTOR_BUDGET_MS - (Date.now() - vectorStarted)) : null;
2129
+ const cardsStarted = Date.now();
2130
+ const model = opts.cards ? await runIndexModelPass(process.cwd(), {
2131
+ ...opts.json ? {} : {
2132
+ onProgress: (done, total) => process.stderr.write(`\r cards: ${done}/${total}`)
2133
+ }
2134
+ }) : null;
2135
+ if (model && !opts.json) process.stderr.write("\n");
2106
2136
  if (opts.json) {
2107
2137
  console.log(
2108
2138
  JSON.stringify(
2109
2139
  {
2110
2140
  ...result,
2111
2141
  ...vectors ? { vectors } : {},
2112
- ...reuseVectors ? { reuseVectors } : {}
2142
+ ...reuseVectors ? { reuseVectors } : {},
2143
+ ...model ? { model: { ...model, ms: Date.now() - cardsStarted } } : {}
2113
2144
  },
2114
2145
  null,
2115
2146
  2
@@ -2127,6 +2158,7 @@ function registerIndexCommands(program, runSafely2) {
2127
2158
  } else if (local) {
2128
2159
  console.log(pc9.dim(" file vectors: skipped (local embedding runtime not available)"));
2129
2160
  }
2161
+ if (model) printModelPass(model, Date.now() - cardsStarted);
2130
2162
  });
2131
2163
  });
2132
2164
  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) => {
@@ -2153,6 +2185,30 @@ function registerIndexCommands(program, runSafely2) {
2153
2185
  });
2154
2186
  });
2155
2187
  }
2188
+ function printModelPass(model, ms) {
2189
+ if (!model.cards && !model.facts) {
2190
+ console.log(pc9.dim(" cards: skipped (no model; codebuddy models add local-llm)"));
2191
+ return;
2192
+ }
2193
+ if (model.cards?.stoppedBy === "locked") {
2194
+ console.log(
2195
+ pc9.yellow(` cards: already being written by pid ${model.cards.lockedBy ?? "unknown"}`)
2196
+ );
2197
+ return;
2198
+ }
2199
+ if (model.cards) {
2200
+ console.log(
2201
+ pc9.dim(
2202
+ ` cards: ${model.cards.summarized} written, ${model.cards.timedOut ? `${model.cards.timedOut} timed out, ` : ""}${model.cards.remaining} left (${model.cards.stoppedBy}), ${model.cardVectors} embedded, ${Math.round(ms / 1e3)} s`
2203
+ )
2204
+ );
2205
+ }
2206
+ if (model.facts) {
2207
+ console.log(
2208
+ pc9.dim(` index facts: ${model.facts.written} written, ${model.facts.removed} replaced`)
2209
+ );
2210
+ }
2211
+ }
2156
2212
  function printResult(result, compact = false) {
2157
2213
  const summary = `${result.totalIndexed} indexed (${pc9.green(`+${result.added}`)} ${pc9.yellow(`~${result.changed}`)} ${pc9.dim(`=${result.unchanged}`)} ${pc9.red(`-${result.removed}`)}) in ${result.durationMs}ms`;
2158
2214
  console.log(compact ? pc9.dim(summary) : summary);
@@ -2474,6 +2530,36 @@ import { relative as relative3 } from "path";
2474
2530
  import pc13 from "picocolors";
2475
2531
  function registerMemoryCommands(program, runSafely2) {
2476
2532
  const memory = program.command("memory").description("Inspect and curate CodeBuddy memory.");
2533
+ memory.command("list").option(
2534
+ "--origin <origin>",
2535
+ "Only facts from this origin: index (extracted from the repository)."
2536
+ ).option("--json", "Print machine-readable JSON.").description("List durable facts (Markdown under .codebuddy/memory/facts/).").action(async (opts) => {
2537
+ await runSafely2(async () => {
2538
+ if (opts.origin !== void 0 && opts.origin !== "index") {
2539
+ throw new Error(`Unknown origin "${opts.origin}"; supported: index.`);
2540
+ }
2541
+ const facts = opts.origin === void 0 ? await new MemoryFileStore().listFacts() : await listIndexFacts(process.cwd());
2542
+ if (opts.json) {
2543
+ console.log(JSON.stringify(facts, null, 2));
2544
+ return;
2545
+ }
2546
+ if (facts.length === 0) console.log(pc13.dim("no facts"));
2547
+ for (const fact of facts) {
2548
+ const source = fact.verification[0] ?? fact.paths[0];
2549
+ console.log(`${pc13.cyan(fact.id)} ${fact.content}${source ? pc13.dim(` [${source}]`) : ""}`);
2550
+ }
2551
+ });
2552
+ });
2553
+ memory.command("prune").requiredOption("--origin <origin>", "Which facts to delete: index.").description("Delete facts by origin (index: re-extracted on the next index pass).").action(async (opts) => {
2554
+ await runSafely2(async () => {
2555
+ if (opts.origin !== "index")
2556
+ throw new Error(`Unknown origin "${opts.origin}"; supported: index.`);
2557
+ const removed = await pruneIndexFacts(process.cwd());
2558
+ console.log(
2559
+ `${removed} index fact(s) removed ${pc13.dim("(run `codebuddy hooks sync` to update the database)")}`
2560
+ );
2561
+ });
2562
+ });
2477
2563
  const review = memory.command("review").description("Review memory candidates captured automatically after turns.");
2478
2564
  review.command("list", { isDefault: true }).option("--json", "Print machine-readable JSON.").description("List pending review candidates.").action(async (opts) => {
2479
2565
  await runSafely2(async () => {
@@ -2945,13 +3031,17 @@ async function printStatus() {
2945
3031
  );
2946
3032
  }
2947
3033
  }
3034
+ {
3035
+ const { cardProgress } = await import("./summaries-W2J5FCLG.js");
3036
+ await printLocalLlmStatus(await cardProgress(process.cwd()).catch(() => null));
3037
+ }
2948
3038
  console.log(pc14.bold("Memory index"));
2949
3039
  if (process.env.CODEBUDDY_SEMANTIC === "0") {
2950
3040
  console.log(row("recall", warn("CODEBUDDY_SEMANTIC=0: lexical only")));
2951
3041
  }
2952
3042
  if (!config.postgresUrl) {
2953
3043
  console.log(row("postgres", warn("not configured: recall is lexical only")));
2954
- next.push("codebuddy use (sets up PostgreSQL)");
3044
+ next.push("codebuddy (sets up PostgreSQL)");
2955
3045
  } else {
2956
3046
  const counts = await vectorCounts(
2957
3047
  config.postgresUrl,
@@ -3070,6 +3160,29 @@ before keeping one on. A reranker never admits a fact, it only orders them.`
3070
3160
  console.log(pc14.dim(" Run codebuddy models pull to download it now."));
3071
3161
  });
3072
3162
  });
3163
+ models.command("add").argument("<name>", "Optional model to install: local-llm.").option("--size <size>", `Local LLM size: ${LOCAL_LLM_SIZES.join(" | ")}.`, "default").option("-y, --yes", "Install the runtime without asking.").description(
3164
+ "Download the optional local LLM (Qwen3-0.6B by default) that writes file cards and index facts."
3165
+ ).action(async (name2, opts) => {
3166
+ await runSafely2(async () => {
3167
+ if (name2 !== "local-llm") throw new Error(`Unknown model "${name2}"; try: local-llm.`);
3168
+ if (!LOCAL_LLM_SIZES.includes(opts.size)) {
3169
+ throw new Error(`--size must be one of ${LOCAL_LLM_SIZES.join(", ")}.`);
3170
+ }
3171
+ const added = await addLocalLlm({
3172
+ size: opts.size,
3173
+ ...opts.yes ? { yes: true } : {}
3174
+ });
3175
+ if (!added) process.exitCode = 1;
3176
+ });
3177
+ });
3178
+ models.command("remove").argument("<name>", "Optional model to remove: local-llm.").description(
3179
+ "Delete the local LLM weights; CodeBuddy falls back to keyword/vector-only behaviour."
3180
+ ).action(async (name2) => {
3181
+ await runSafely2(async () => {
3182
+ if (name2 !== "local-llm") throw new Error(`Unknown model "${name2}"; try: local-llm.`);
3183
+ await removeLocalLlm();
3184
+ });
3185
+ });
3073
3186
  models.command("pull").description("Download the local models now instead of on first use.").action(async () => {
3074
3187
  await runSafely2(async () => {
3075
3188
  const { LocalModelProvider, rerankLocally } = await import("./local-ST4WMRCG.js");
@@ -4887,54 +5000,10 @@ function registerTelemetryCommands(program, runSafely2) {
4887
5000
  }
4888
5001
 
4889
5002
  // src/cli/commands/use-command.ts
5003
+ import { execFile as execFile3, spawn as spawn7 } from "child_process";
4890
5004
  import { basename as basename2 } from "path";
4891
5005
  import { confirm as confirm2, isCancel as isCancel2, log as log2, note as note2, password as password2, select, spinner as spinner3, text as text2 } from "@clack/prompts";
4892
5006
  import pc22 from "picocolors";
4893
-
4894
- // src/core/global-config.ts
4895
- import { chmod, mkdir as mkdir4, readFile as readFile6, writeFile as writeFile3 } from "fs/promises";
4896
- import { homedir } from "os";
4897
- import { dirname as dirname3, join as join8 } from "path";
4898
- import { z as z3 } from "zod";
4899
- var GLOBAL_DIR = ".codebuddy";
4900
- var GLOBAL_FILE = "global.json";
4901
- var globalConfigSchema = z3.object({
4902
- hfToken: z3.string().min(1).optional(),
4903
- databaseUrl: z3.string().min(1).optional(),
4904
- // Where the codebuddy install lives on this machine. Cached so future
4905
- // `codebuddy use` calls in unrelated projects can find the CLI entry
4906
- // without re-resolving via process.argv every time.
4907
- installPath: z3.string().optional()
4908
- });
4909
- function globalConfigPath(home = homedir()) {
4910
- return join8(home, GLOBAL_DIR, GLOBAL_FILE);
4911
- }
4912
- async function readGlobalConfig(home = homedir()) {
4913
- const path = globalConfigPath(home);
4914
- try {
4915
- const raw = await readFile6(path, "utf8");
4916
- return globalConfigSchema.parse(JSON.parse(raw));
4917
- } catch (error) {
4918
- if (error.code === "ENOENT") return {};
4919
- throw error;
4920
- }
4921
- }
4922
- async function writeGlobalConfig(next, home = homedir()) {
4923
- const path = globalConfigPath(home);
4924
- await mkdir4(dirname3(path), { recursive: true, mode: 448 });
4925
- await writeFile3(path, `${JSON.stringify(next, null, 2)}
4926
- `, { mode: 384 });
4927
- await chmod(path, 384);
4928
- return { path };
4929
- }
4930
- async function mergeGlobalConfig(patch, home = homedir()) {
4931
- const current = await readGlobalConfig(home);
4932
- const merged = { ...current, ...patch };
4933
- const { path } = await writeGlobalConfig(merged, home);
4934
- return { path, merged };
4935
- }
4936
-
4937
- // src/cli/commands/use-command.ts
4938
5007
  function bail2() {
4939
5008
  log2.warn("Aborted.");
4940
5009
  process.exit(1);
@@ -4953,6 +5022,131 @@ async function pingOnce2(databaseUrl) {
4953
5022
  await client.close().catch(() => void 0);
4954
5023
  }
4955
5024
  }
5025
+ async function setupZvec(configured) {
5026
+ if (configured === false) {
5027
+ log2.info(`zvec-grep: disabled in config (enable with ${pc22.cyan("codebuddy zvec enable")}).`);
5028
+ return;
5029
+ }
5030
+ let report = await zvecStatus(process.cwd()).catch(() => null);
5031
+ if (report && report.version === null && process.env.CODEBUDDY_ZVEC !== "0") {
5032
+ const install = spinner3();
5033
+ install.start(`Installing zvec-grep (${ZVEC_PACKAGE})`);
5034
+ const code2 = await npmGlobalInstall(ZVEC_PACKAGE);
5035
+ install.stop(code2 === 0 ? "zvec-grep installed." : `zvec-grep install failed (exit ${code2}).`);
5036
+ if (code2 === 0) report = await zvecStatus(process.cwd()).catch(() => null);
5037
+ }
5038
+ if (!report?.supported) {
5039
+ log2.warn(
5040
+ `zvec-grep: ${report?.reason ?? "unavailable"}; keyword-only file discovery. Install with ${pc22.cyan("npm install -g @zvec/zvec-grep")}, then ${pc22.cyan("codebuddy zvec index && codebuddy zvec enable")}.`
5041
+ );
5042
+ return;
5043
+ }
5044
+ const zvecSpinner = spinner3();
5045
+ zvecSpinner.start("Building the zvec-grep semantic index");
5046
+ const code = report.indexReady ? 0 : await buildZvecIndex(process.cwd(), { quiet: true });
5047
+ if (code !== 0) {
5048
+ zvecSpinner.stop(`zvec-grep index failed (exit ${code}); keyword-only file discovery.`);
5049
+ return;
5050
+ }
5051
+ await setZvecEnabled(true);
5052
+ zvecSpinner.stop("zvec-grep semantic file discovery enabled.");
5053
+ }
5054
+ var ZVEC_PACKAGE = "@zvec/zvec-grep@0.2";
5055
+ function npmGlobalInstall(spec) {
5056
+ return new Promise((resolve6) => {
5057
+ execFile3(
5058
+ "npm",
5059
+ ["install", "-g", spec],
5060
+ { timeout: 10 * 6e4 },
5061
+ (error) => resolve6(error ? typeof error.code === "number" ? error.code : 1 : 0)
5062
+ );
5063
+ });
5064
+ }
5065
+ async function setupEmbeddings(embeddings) {
5066
+ if (embeddings !== "local" || process.env.CODEBUDDY_SEMANTIC === "0") return;
5067
+ const pull = spinner3();
5068
+ pull.start("Downloading the local embedding model (~90 MB, once)");
5069
+ try {
5070
+ const { LocalModelProvider } = await import("./local-ST4WMRCG.js");
5071
+ await new LocalModelProvider().embed({ input: "warm up" });
5072
+ pull.stop("Local embedding model ready.");
5073
+ } catch (error) {
5074
+ pull.stop(
5075
+ `Embedding model unavailable (${error.message.split("\n")[0]}); recall stays lexical until ${pc22.cyan("codebuddy models pull")} succeeds.`
5076
+ );
5077
+ }
5078
+ }
5079
+ function startBackgroundIndex(withModel) {
5080
+ const entry = process.argv[1];
5081
+ if (!entry) return;
5082
+ const child = spawn7(process.execPath, [entry, "index", "--cards"], {
5083
+ cwd: process.cwd(),
5084
+ detached: true,
5085
+ stdio: "ignore"
5086
+ });
5087
+ child.unref();
5088
+ log2.info(
5089
+ withModel ? "Building file vectors, intent cards and project facts in the background." : "Building file vectors in the background."
5090
+ );
5091
+ }
5092
+ async function setupLocalLlm(choice, interactive) {
5093
+ if (choice === false || process.env.CODEBUDDY_LOCAL_LLM === "0") {
5094
+ log2.info(`Local LLM: skipped (add later with ${pc22.cyan("codebuddy models add local-llm")}).`);
5095
+ return false;
5096
+ }
5097
+ const { installedLocalModel, machineCanCard } = await import("./local-TH5XCLVJ.js");
5098
+ let installed = await installedLocalModel() !== null;
5099
+ const machine = machineCanCard();
5100
+ if (!installed && !machine.ok) {
5101
+ if (!await setupCloudQwen(machine.reason, interactive)) return false;
5102
+ installed = true;
5103
+ }
5104
+ if (!installed) {
5105
+ const { addLocalLlm: addLocalLlm2 } = await import("./local-llm-GZREB5DF.js");
5106
+ installed = await addLocalLlm2({ yes: true, interactive });
5107
+ if (!installed) {
5108
+ log2.warn(
5109
+ `Local LLM not installed; file discovery uses keywords and vectors only. Retry with ${pc22.cyan("codebuddy models add local-llm")}.`
5110
+ );
5111
+ return false;
5112
+ }
5113
+ }
5114
+ return installed;
5115
+ }
5116
+ async function setupCloudQwen(reason, interactive) {
5117
+ const existing = (await readConfigFile()).llm;
5118
+ if (existing && existing.provider !== "local") return existing.enabled;
5119
+ const fromEnv = process.env.GROQ_API_KEY ? "groq" : process.env.OPENROUTER_API_KEY ? "openrouter" : null;
5120
+ const saved = await readGlobalConfig().catch(
5121
+ () => ({})
5122
+ );
5123
+ let chosen = fromEnv ?? (saved.groqApiKey ? "groq" : saved.openrouterApiKey ? "openrouter" : null);
5124
+ if (!chosen && interactive) {
5125
+ log2.info(
5126
+ `This machine is too small for the local model (${reason}), so CodeBuddy can use Qwen in the cloud. Get a free Groq key at ${pc22.cyan("https://console.groq.com/keys")}.`
5127
+ );
5128
+ const key = unwrap2(
5129
+ await password2({
5130
+ message: "Groq API key (Enter to skip; saved to ~/.codebuddy/global.json)"
5131
+ })
5132
+ ).trim();
5133
+ if (key) {
5134
+ await mergeGlobalConfig({ groqApiKey: key });
5135
+ chosen = "groq";
5136
+ }
5137
+ }
5138
+ if (!chosen) {
5139
+ log2.warn(
5140
+ `Local LLM: this machine is too small for it (${reason}). For cloud Qwen, get a free key at ${pc22.cyan("https://console.groq.com/keys")} and rerun ${pc22.cyan("codebuddy")} (or ${pc22.cyan("export GROQ_API_KEY=\u2026")} and ${pc22.cyan("codebuddy llm enable --provider groq")}). Keyword + vector discovery work meanwhile.`
5141
+ );
5142
+ return false;
5143
+ }
5144
+ await setLlmEnabled(true, { provider: chosen });
5145
+ log2.info(
5146
+ `LLM: this machine is too small for the local model (${reason}); using Qwen (qwen/qwen3.8-27b) on ${chosen}. Redacted file heads and signatures are sent to it; turn off with \`codebuddy llm disable\`.`
5147
+ );
5148
+ return true;
5149
+ }
4956
5150
  function sanitizeNamespace(input) {
4957
5151
  return input.replace(/[^a-zA-Z0-9_.:-]/g, "-").slice(0, 128) || "default";
4958
5152
  }
@@ -4975,7 +5169,7 @@ async function runUseCommand(options = {}) {
4975
5169
  note2(
4976
5170
  `Project folder: ${pc22.bold(process.cwd())}
4977
5171
  Namespace: ${pc22.bold(namespace)}`,
4978
- "codebuddy use"
5172
+ "codebuddy"
4979
5173
  );
4980
5174
  const stored = await readGlobalConfig();
4981
5175
  const projectConfig = await readConfigFile();
@@ -5138,6 +5332,9 @@ Namespace: ${pc22.bold(namespace)}`,
5138
5332
  tokenBudget: existingConfig.tokenBudget ?? 4e3,
5139
5333
  graphify: { enabled: graphifyConfigured, graphPath: "graphify-out/graph.json" }
5140
5334
  });
5335
+ await setupEmbeddings(embeddings);
5336
+ await setupZvec(existingConfig.zvec?.enabled);
5337
+ startBackgroundIndex(await setupLocalLlm(options.localLlm, interactive));
5141
5338
  const graphifyMessage = graphifySetup?.message ?? "Graphify disabled; CodeBuddy MCP remains available.";
5142
5339
  await writeProjectMcpConfig({
5143
5340
  repositoryRoot: process.cwd(),
@@ -5321,26 +5518,20 @@ function createCli() {
5321
5518
  program.name("codebuddy").description(
5322
5519
  "Project memory and context for AI coding agents: recalls the right facts, files and risks on every prompt."
5323
5520
  ).version(VERSION);
5324
- program.action(async () => {
5325
- await runSafely(async () => {
5326
- await runUseCommand();
5327
- });
5328
- });
5329
- program.command("use").description(
5330
- "One-command setup for this folder: PostgreSQL (starts Docker if needed), migrations, embeddings, and agent wiring."
5331
- ).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(
5521
+ program.enablePositionalOptions().option("--namespace <name>", "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(
5332
5522
  "--docker",
5333
5523
  "Start CodeBuddy's bundled Postgres container when the default DB is unavailable."
5334
- ).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(
5335
- async (namespace, opts) => {
5524
+ ).option("--no-docker", "Do not offer to start CodeBuddy's bundled Postgres container.").option("--no-local-llm", "Skip installing the local LLM (Qwen3-0.6B, ~400 MB).").option("-y, --yes", "Never prompt: use flags and defaults (automatic without a TTY).").option("--non-interactive", "Same as --yes.").action(
5525
+ async (opts) => {
5336
5526
  await runSafely(async () => {
5337
5527
  await runUseCommand({
5338
- ...namespace !== void 0 ? { namespace } : {},
5528
+ ...opts.namespace !== void 0 ? { namespace: opts.namespace } : {},
5339
5529
  ...opts.postgresUrl !== void 0 ? { postgresUrl: opts.postgresUrl } : {},
5340
5530
  ...opts.skipClaude !== void 0 ? { skipClaude: opts.skipClaude } : {},
5341
5531
  ...opts.skipCodex !== void 0 ? { skipCodex: opts.skipCodex } : {},
5342
5532
  ...opts.graphify !== void 0 ? { graphify: opts.graphify } : {},
5343
5533
  ...opts.docker !== void 0 ? { useDocker: opts.docker } : {},
5534
+ ...opts.localLlm !== void 0 ? { localLlm: opts.localLlm } : {},
5344
5535
  ...opts.yes || opts.nonInteractive ? { interactive: false } : {}
5345
5536
  });
5346
5537
  });
@@ -5580,7 +5771,13 @@ function createCli() {
5580
5771
  });
5581
5772
  program.command("stats").description("Show CodeBuddy storage and usage stats.").action(async () => {
5582
5773
  await withRuntime(async (runtime) => {
5583
- console.log(JSON.stringify(await runtime.repository.getStats(), null, 2));
5774
+ console.log(
5775
+ JSON.stringify(
5776
+ { ...await runtime.repository.getStats(), ...await indexModelStats(process.cwd()) },
5777
+ null,
5778
+ 2
5779
+ )
5780
+ );
5584
5781
  });
5585
5782
  });
5586
5783
  program.command("storage").option("--json", "Print machine-readable JSON.").description(
@@ -5603,11 +5800,15 @@ function createCli() {
5603
5800
  } catch (error) {
5604
5801
  database = { unavailable: error.message };
5605
5802
  }
5803
+ const index = await indexModelStats(root);
5606
5804
  if (opts.json) {
5607
- console.log(JSON.stringify({ database, capture: capture2 }, null, 2));
5805
+ console.log(JSON.stringify({ database, capture: capture2, ...index }, null, 2));
5608
5806
  return;
5609
5807
  }
5610
5808
  printStorage(database, capture2);
5809
+ console.log(
5810
+ `intent cards: ${index.cards.carded} / ${index.cards.eligible} source files; index facts: ${index.indexFacts}`
5811
+ );
5611
5812
  if ("unavailable" in database || database.markdownOnly.length + database.dbOnly.length > 0) {
5612
5813
  process.exitCode = 1;
5613
5814
  }
@@ -5658,14 +5859,11 @@ function createCli() {
5658
5859
  return showTelemetryNoticeOnce();
5659
5860
  });
5660
5861
  program.hook("postAction", (_program, command) => traceCliCommand(command, started));
5661
- program.action(async () => {
5662
- await runSafely(() => runUseCommand());
5663
- });
5664
5862
  groupHelp(program);
5665
5863
  return program;
5666
5864
  }
5667
5865
  var HELP_GROUPS = [
5668
- ["Get started:", ["use", "init", "doctor", "hooks", "clients", "models", "dashboard"]],
5866
+ ["Get started:", ["init", "doctor", "hooks", "clients", "models", "dashboard"]],
5669
5867
  [
5670
5868
  "Daily work:",
5671
5869
  [
@@ -5728,6 +5926,14 @@ Quick start:
5728
5926
  see exactly what an agent would be given`
5729
5927
  );
5730
5928
  }
5929
+ async function indexModelStats(root) {
5930
+ const { cardProgress } = await import("./summaries-W2J5FCLG.js");
5931
+ const { listIndexFacts: listIndexFacts2 } = await import("./index-facts-MBJKBYA3.js");
5932
+ return {
5933
+ cards: await cardProgress(root).catch(() => ({ carded: 0, eligible: 0 })),
5934
+ indexFacts: (await listIndexFacts2(root).catch(() => [])).length
5935
+ };
5936
+ }
5731
5937
  async function withRuntime(fn) {
5732
5938
  await runSafely(async () => {
5733
5939
  const runtime = await createRuntime(await loadRuntimeConfig());
@@ -5847,4 +6053,4 @@ function printStorage(database, capture2) {
5847
6053
  export {
5848
6054
  createCli
5849
6055
  };
5850
- //# sourceMappingURL=main-SFC5LV4E.js.map
6056
+ //# sourceMappingURL=main-QF6Z552J.js.map