@orquentra/codebuddy 2.14.0 → 2.15.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 (91) hide show
  1. package/ARCHITECTURE.md +98 -0
  2. package/README.md +87 -6
  3. package/dist/{chunk-LW75SKZR.js → chunk-4BSO3HST.js} +126 -18
  4. package/dist/chunk-4BSO3HST.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-ESZXCZIN.js → chunk-6YI2D5F5.js} +33 -5
  8. package/dist/chunk-6YI2D5F5.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-BTTQWSDS.js +372 -0
  12. package/dist/chunk-BTTQWSDS.js.map +1 -0
  13. package/dist/{chunk-YJPCS2GP.js → chunk-D5E7SGI5.js} +57 -18
  14. package/dist/chunk-D5E7SGI5.js.map +1 -0
  15. package/dist/chunk-EMWGLKPR.js +45 -0
  16. package/dist/chunk-EMWGLKPR.js.map +1 -0
  17. package/dist/chunk-ITXE27SZ.js +358 -0
  18. package/dist/chunk-ITXE27SZ.js.map +1 -0
  19. package/dist/{chunk-LRQNPYCK.js → chunk-JJQDVGF7.js} +13 -7
  20. package/dist/chunk-JJQDVGF7.js.map +1 -0
  21. package/dist/{chunk-XSNW6Y3X.js → chunk-KQCP3ZYH.js} +28 -24
  22. package/dist/chunk-KQCP3ZYH.js.map +1 -0
  23. package/dist/{chunk-YWDH6DQ6.js → chunk-KYRM3HPP.js} +21 -19
  24. package/dist/{chunk-YWDH6DQ6.js.map → chunk-KYRM3HPP.js.map} +1 -1
  25. package/dist/{chunk-KNR2P2UY.js → chunk-M75SN4D4.js} +19 -4
  26. package/dist/chunk-M75SN4D4.js.map +1 -0
  27. package/dist/{chunk-L6BRYZ6G.js → chunk-RZ266NHE.js} +7 -7
  28. package/dist/chunk-U7KDM62P.js +43 -0
  29. package/dist/chunk-U7KDM62P.js.map +1 -0
  30. package/dist/chunk-UATJCD2T.js +368 -0
  31. package/dist/chunk-UATJCD2T.js.map +1 -0
  32. package/dist/{chunk-YNSTIMCF.js → chunk-UR3DF74I.js} +352 -157
  33. package/dist/chunk-UR3DF74I.js.map +1 -0
  34. package/dist/chunk-UTPC66MV.js +128 -0
  35. package/dist/chunk-UTPC66MV.js.map +1 -0
  36. package/dist/chunk-WHKXUXHE.js +156 -0
  37. package/dist/chunk-WHKXUXHE.js.map +1 -0
  38. package/dist/{chunk-7QMHIJBA.js → chunk-YMXFSHXT.js} +969 -968
  39. package/dist/chunk-YMXFSHXT.js.map +1 -0
  40. package/dist/cli/index.js +4 -4
  41. package/dist/config-TXQJCCVI.js +15 -0
  42. package/dist/expand-KWYGNCKZ.js +98 -0
  43. package/dist/expand-KWYGNCKZ.js.map +1 -0
  44. package/dist/file-vectors-MKXTSGGH.js +19 -0
  45. package/dist/index-facts-4MTEHKMN.js +38 -0
  46. package/dist/index-pass-TBDUEZCB.js +14 -0
  47. package/dist/index.js +24 -22
  48. package/dist/{install-EU35ERK5.js → install-E3JWINTN.js} +11 -10
  49. package/dist/{lean-43ADYTBW.js → lean-MLTAFS5L.js} +8 -7
  50. package/dist/local-75DJECVR.js +48 -0
  51. package/dist/local-llm-MECVJTVU.js +19 -0
  52. package/dist/local-llm-MECVJTVU.js.map +1 -0
  53. package/dist/{main-SFC5LV4E.js → main-RLMKQZW4.js} +332 -123
  54. package/dist/main-RLMKQZW4.js.map +1 -0
  55. package/dist/{memory-TGHVTBME.js → memory-PFFVMTCH.js} +9 -6
  56. package/dist/{memory-TGHVTBME.js.map → memory-PFFVMTCH.js.map} +1 -1
  57. package/dist/{operations-6Z6ZDVBP.js → operations-STR7S5UO.js} +8 -7
  58. package/dist/operations-STR7S5UO.js.map +1 -0
  59. package/dist/{rerank-IQAEWLQR.js → rerank-XXKWDI5Q.js} +7 -4
  60. package/dist/{rerank-IQAEWLQR.js.map → rerank-XXKWDI5Q.js.map} +1 -1
  61. package/dist/{runtime-W5AW32SD.js → runtime-SWFWGTQ4.js} +9 -8
  62. package/dist/runtime-SWFWGTQ4.js.map +1 -0
  63. package/dist/summaries-QXWFFDUZ.js +31 -0
  64. package/dist/summaries-QXWFFDUZ.js.map +1 -0
  65. package/dist/{zvec-24HLAFGN.js → zvec-F73IBWO5.js} +13 -5
  66. package/dist/zvec-F73IBWO5.js.map +1 -0
  67. package/package.json +3 -2
  68. package/dist/chunk-7QMHIJBA.js.map +0 -1
  69. package/dist/chunk-CAEPHLHC.js.map +0 -1
  70. package/dist/chunk-ESC4Y3L7.js +0 -204
  71. package/dist/chunk-ESC4Y3L7.js.map +0 -1
  72. package/dist/chunk-ESZXCZIN.js.map +0 -1
  73. package/dist/chunk-KNR2P2UY.js.map +0 -1
  74. package/dist/chunk-LRQNPYCK.js.map +0 -1
  75. package/dist/chunk-LW75SKZR.js.map +0 -1
  76. package/dist/chunk-SFMIMLXR.js +0 -63
  77. package/dist/chunk-SFMIMLXR.js.map +0 -1
  78. package/dist/chunk-XSNW6Y3X.js.map +0 -1
  79. package/dist/chunk-YJPCS2GP.js.map +0 -1
  80. package/dist/chunk-YNSTIMCF.js.map +0 -1
  81. package/dist/config-GEZFLXK6.js +0 -12
  82. package/dist/main-SFC5LV4E.js.map +0 -1
  83. package/dist/summaries-32PHOB6L.js +0 -20
  84. /package/dist/{chunk-L6BRYZ6G.js.map → chunk-RZ266NHE.js.map} +0 -0
  85. /package/dist/{config-GEZFLXK6.js.map → config-TXQJCCVI.js.map} +0 -0
  86. /package/dist/{install-EU35ERK5.js.map → file-vectors-MKXTSGGH.js.map} +0 -0
  87. /package/dist/{lean-43ADYTBW.js.map → index-facts-4MTEHKMN.js.map} +0 -0
  88. /package/dist/{operations-6Z6ZDVBP.js.map → index-pass-TBDUEZCB.js.map} +0 -0
  89. /package/dist/{runtime-W5AW32SD.js.map → install-E3JWINTN.js.map} +0 -0
  90. /package/dist/{summaries-32PHOB6L.js.map → lean-MLTAFS5L.js.map} +0 -0
  91. /package/dist/{zvec-24HLAFGN.js.map → local-75DJECVR.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-UTPC66MV.js";
19
10
  import {
20
11
  WORKFLOW_CLIENTS,
21
12
  clientDisplayName,
@@ -41,7 +32,42 @@ import {
41
32
  setHooks,
42
33
  uninstallClient,
43
34
  writeProjectMcpConfig
44
- } from "./chunk-L6BRYZ6G.js";
35
+ } from "./chunk-RZ266NHE.js";
36
+ import {
37
+ renderLeanBlock,
38
+ runLeanHook
39
+ } from "./chunk-JJQDVGF7.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-ITXE27SZ.js";
59
+ import {
60
+ listIndexFacts,
61
+ pruneIndexFacts
62
+ } from "./chunk-UATJCD2T.js";
63
+ import {
64
+ chatJson,
65
+ coolingDown,
66
+ usageToday
67
+ } from "./chunk-6YI2D5F5.js";
68
+ import {
69
+ runIndexModelPass
70
+ } from "./chunk-U7KDM62P.js";
45
71
  import {
46
72
  AUTOMATION_MODES,
47
73
  CLIENT_ADAPTERS,
@@ -52,10 +78,6 @@ import {
52
78
  parseMode,
53
79
  supportedModes
54
80
  } from "./chunk-V7PT6ER5.js";
55
- import {
56
- renderLeanBlock,
57
- runLeanHook
58
- } from "./chunk-LRQNPYCK.js";
59
81
  import {
60
82
  TELEMETRY_NOTICE,
61
83
  findReuse,
@@ -72,12 +94,7 @@ import {
72
94
  traceCliCommand,
73
95
  watchRepository,
74
96
  writeTelemetryState
75
- } from "./chunk-YJPCS2GP.js";
76
- import {
77
- DEFAULT_ZVEC_MODEL,
78
- buildZvecIndex,
79
- zvecStatus
80
- } from "./chunk-LW75SKZR.js";
97
+ } from "./chunk-D5E7SGI5.js";
81
98
  import {
82
99
  HookStagingStore,
83
100
  PlanFileStore,
@@ -107,7 +124,7 @@ import {
107
124
  runStageHook,
108
125
  runSyncHook,
109
126
  syncReuseVectors
110
- } from "./chunk-XSNW6Y3X.js";
127
+ } from "./chunk-KQCP3ZYH.js";
111
128
  import {
112
129
  parseTraceLines,
113
130
  readTraces,
@@ -117,12 +134,33 @@ import {
117
134
  SessionCapsuleLedger,
118
135
  detectTestCommand,
119
136
  estimateTokens
120
- } from "./chunk-4DKYKDHI.js";
137
+ } from "./chunk-7IPSDDO5.js";
121
138
  import {
122
139
  hookRoot
123
140
  } from "./chunk-TGFTKWOR.js";
124
141
  import "./chunk-GQKWUHMA.js";
125
142
  import "./chunk-DFV2SK6C.js";
143
+ import {
144
+ DEFAULT_ZVEC_MODEL,
145
+ buildZvecIndex,
146
+ zvecStatus
147
+ } from "./chunk-4BSO3HST.js";
148
+ import {
149
+ buildArchitectureMap,
150
+ generateGraphifyGraph,
151
+ indexRepository,
152
+ loadFreshIndex,
153
+ loadGraphifyMap,
154
+ loadProjectArchitectureMap,
155
+ neighbours,
156
+ queryMap,
157
+ setupGraphify,
158
+ syncFileVectors
159
+ } from "./chunk-UR3DF74I.js";
160
+ import {
161
+ extractSymbolTable,
162
+ supportsSymbols
163
+ } from "./chunk-Y7D7PUBE.js";
126
164
  import {
127
165
  bootstrapDatabase,
128
166
  buildProviderFromConfig,
@@ -134,57 +172,25 @@ import {
134
172
  pingDatabase,
135
173
  runDoctor,
136
174
  runMigrations
137
- } from "./chunk-YWDH6DQ6.js";
175
+ } from "./chunk-KYRM3HPP.js";
176
+ import "./chunk-57642NGU.js";
138
177
  import {
139
178
  MemoryFileStore,
140
179
  VERSION
141
- } from "./chunk-7QMHIJBA.js";
180
+ } from "./chunk-YMXFSHXT.js";
142
181
  import {
143
182
  computeFactHash,
144
183
  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";
184
+ } from "./chunk-EMWGLKPR.js";
176
185
  import {
177
186
  redactPersisted
178
187
  } from "./chunk-AL6VBTNA.js";
188
+ import "./chunk-6JCKMIQY.js";
179
189
  import {
180
- DEFAULT_CLOUD_EMBEDDING_MODELS,
181
- DEFAULT_RERANK_MODELS,
182
- LOCAL_EMBEDDING_MODEL,
183
- getModelDefinition
184
- } from "./chunk-HPOR6YXA.js";
185
- import {
190
+ mergeGlobalConfig,
191
+ readGlobalConfig,
186
192
  resolveLlmConfig
187
- } from "./chunk-SFMIMLXR.js";
193
+ } from "./chunk-WHKXUXHE.js";
188
194
  import {
189
195
  PLAN_POLICIES,
190
196
  configPath,
@@ -202,10 +208,21 @@ import {
202
208
  setPlanPolicy,
203
209
  setZvecEnabled,
204
210
  writeConfigFile
205
- } from "./chunk-KNR2P2UY.js";
211
+ } from "./chunk-M75SN4D4.js";
206
212
  import {
207
213
  writeAtomic
208
214
  } from "./chunk-CG5B3LYG.js";
215
+ import "./chunk-BTTQWSDS.js";
216
+ import {
217
+ localRuntimeInstalled,
218
+ modelCacheDir
219
+ } from "./chunk-3QGW53GD.js";
220
+ import {
221
+ DEFAULT_CLOUD_EMBEDDING_MODELS,
222
+ DEFAULT_RERANK_MODELS,
223
+ LOCAL_EMBEDDING_MODEL,
224
+ getModelDefinition
225
+ } from "./chunk-HPOR6YXA.js";
209
226
  import "./chunk-PZ5AY32C.js";
210
227
 
211
228
  // src/cli/main.ts
@@ -1988,8 +2005,9 @@ var KEY_ENV = { groq: "GROQ_API_KEY", openrouter: "OPENROUTER_API_KEY" };
1988
2005
  var WATCH_BATCH = 8;
1989
2006
  async function llmStatusReport(root, env = process.env) {
1990
2007
  const block = (await readConfigFile(root)).llm;
1991
- const provider = block?.provider ?? "groq";
1992
2008
  const config = await resolveLlmConfig(root, env);
2009
+ const provider = config.enabled ? config.provider : block?.provider ?? "groq";
2010
+ const keyName = provider === "local" ? "" : KEY_ENV[provider];
1993
2011
  const model = config.enabled ? config.model : block?.model ?? null;
1994
2012
  const key = `${provider}:${model}`;
1995
2013
  const { manifest } = await loadFreshIndex(root);
@@ -1998,8 +2016,9 @@ async function llmStatusReport(root, env = process.env) {
1998
2016
  reason: config.enabled ? "on" : config.reason,
1999
2017
  provider,
2000
2018
  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]]),
2019
+ keyEnv: provider === "local" ? "none needed" : env.CODEBUDDY_LLM_API_KEY ? "CODEBUDDY_LLM_API_KEY" : keyName,
2020
+ // Enabled implies a key (environment or saved by `codebuddy use`).
2021
+ keyPresent: provider === "local" || config.enabled || Boolean(env.CODEBUDDY_LLM_API_KEY || env[keyName]),
2003
2022
  features: config.enabled ? config.features : null,
2004
2023
  usageToday: (await usageToday(root))[key] ?? 0,
2005
2024
  dailyTokenBudget: config.enabled ? config.dailyTokenBudget : block?.dailyTokenBudget ?? 4e5,
@@ -2040,7 +2059,7 @@ function registerLlmCommands(program, runSafely2) {
2040
2059
  if (!report.keyPresent) console.log(pc8.dim(`next: export ${report.keyEnv}=<your key>`));
2041
2060
  });
2042
2061
  });
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) => {
2062
+ 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
2063
  await runSafely2(async () => {
2045
2064
  if (opts.provider !== "groq" && opts.provider !== "openrouter") {
2046
2065
  throw new Error("--provider must be groq or openrouter");
@@ -2096,20 +2115,31 @@ function registerLlmCommands(program, runSafely2) {
2096
2115
  // src/cli/commands/indexing.ts
2097
2116
  var VECTOR_BUDGET_MS = 10 * 6e4;
2098
2117
  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) => {
2118
+ 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(
2119
+ "--cards",
2120
+ "Also write intent cards and index facts with the local (or BYOK) model, then embed the cards."
2121
+ ).description("Index the repository: content hashes and lightweight file summaries.").action(async (opts) => {
2100
2122
  await runSafely2(async () => {
2101
2123
  const result = await indexRepository({ ...opts.full ? { full: true } : {} });
2102
2124
  const local = opts.vectors && (await loadRuntimeConfig(process.cwd(), { requireDatabase: false }).catch(() => null))?.provider.type === "local";
2103
2125
  const vectorStarted = Date.now();
2104
2126
  const vectors = local ? await syncFileVectors(process.cwd(), VECTOR_BUDGET_MS) : null;
2105
2127
  const reuseVectors = local ? await syncReuseVectors(process.cwd(), VECTOR_BUDGET_MS - (Date.now() - vectorStarted)) : null;
2128
+ const cardsStarted = Date.now();
2129
+ const model = opts.cards ? await runIndexModelPass(process.cwd(), {
2130
+ ...opts.json ? {} : {
2131
+ onProgress: (done, total) => process.stderr.write(`\r cards: ${done}/${total}`)
2132
+ }
2133
+ }) : null;
2134
+ if (model && !opts.json) process.stderr.write("\n");
2106
2135
  if (opts.json) {
2107
2136
  console.log(
2108
2137
  JSON.stringify(
2109
2138
  {
2110
2139
  ...result,
2111
2140
  ...vectors ? { vectors } : {},
2112
- ...reuseVectors ? { reuseVectors } : {}
2141
+ ...reuseVectors ? { reuseVectors } : {},
2142
+ ...model ? { model: { ...model, ms: Date.now() - cardsStarted } } : {}
2113
2143
  },
2114
2144
  null,
2115
2145
  2
@@ -2127,6 +2157,7 @@ function registerIndexCommands(program, runSafely2) {
2127
2157
  } else if (local) {
2128
2158
  console.log(pc9.dim(" file vectors: skipped (local embedding runtime not available)"));
2129
2159
  }
2160
+ if (model) printModelPass(model, Date.now() - cardsStarted);
2130
2161
  });
2131
2162
  });
2132
2163
  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 +2184,24 @@ function registerIndexCommands(program, runSafely2) {
2153
2184
  });
2154
2185
  });
2155
2186
  }
2187
+ function printModelPass(model, ms) {
2188
+ if (!model.cards && !model.facts) {
2189
+ console.log(pc9.dim(" cards: skipped (no model; codebuddy models add local-llm)"));
2190
+ return;
2191
+ }
2192
+ if (model.cards) {
2193
+ console.log(
2194
+ pc9.dim(
2195
+ ` cards: ${model.cards.summarized} written, ${model.cards.remaining} left (${model.cards.stoppedBy}), ${model.cardVectors} embedded, ${Math.round(ms / 1e3)} s`
2196
+ )
2197
+ );
2198
+ }
2199
+ if (model.facts) {
2200
+ console.log(
2201
+ pc9.dim(` index facts: ${model.facts.written} written, ${model.facts.removed} replaced`)
2202
+ );
2203
+ }
2204
+ }
2156
2205
  function printResult(result, compact = false) {
2157
2206
  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
2207
  console.log(compact ? pc9.dim(summary) : summary);
@@ -2474,6 +2523,36 @@ import { relative as relative3 } from "path";
2474
2523
  import pc13 from "picocolors";
2475
2524
  function registerMemoryCommands(program, runSafely2) {
2476
2525
  const memory = program.command("memory").description("Inspect and curate CodeBuddy memory.");
2526
+ memory.command("list").option(
2527
+ "--origin <origin>",
2528
+ "Only facts from this origin: index (extracted from the repository)."
2529
+ ).option("--json", "Print machine-readable JSON.").description("List durable facts (Markdown under .codebuddy/memory/facts/).").action(async (opts) => {
2530
+ await runSafely2(async () => {
2531
+ if (opts.origin !== void 0 && opts.origin !== "index") {
2532
+ throw new Error(`Unknown origin "${opts.origin}"; supported: index.`);
2533
+ }
2534
+ const facts = opts.origin === void 0 ? await new MemoryFileStore().listFacts() : await listIndexFacts(process.cwd());
2535
+ if (opts.json) {
2536
+ console.log(JSON.stringify(facts, null, 2));
2537
+ return;
2538
+ }
2539
+ if (facts.length === 0) console.log(pc13.dim("no facts"));
2540
+ for (const fact of facts) {
2541
+ const source = fact.verification[0] ?? fact.paths[0];
2542
+ console.log(`${pc13.cyan(fact.id)} ${fact.content}${source ? pc13.dim(` [${source}]`) : ""}`);
2543
+ }
2544
+ });
2545
+ });
2546
+ 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) => {
2547
+ await runSafely2(async () => {
2548
+ if (opts.origin !== "index")
2549
+ throw new Error(`Unknown origin "${opts.origin}"; supported: index.`);
2550
+ const removed = await pruneIndexFacts(process.cwd());
2551
+ console.log(
2552
+ `${removed} index fact(s) removed ${pc13.dim("(run `codebuddy hooks sync` to update the database)")}`
2553
+ );
2554
+ });
2555
+ });
2477
2556
  const review = memory.command("review").description("Review memory candidates captured automatically after turns.");
2478
2557
  review.command("list", { isDefault: true }).option("--json", "Print machine-readable JSON.").description("List pending review candidates.").action(async (opts) => {
2479
2558
  await runSafely2(async () => {
@@ -2945,6 +3024,10 @@ async function printStatus() {
2945
3024
  );
2946
3025
  }
2947
3026
  }
3027
+ {
3028
+ const { cardProgress } = await import("./summaries-QXWFFDUZ.js");
3029
+ await printLocalLlmStatus(await cardProgress(process.cwd()).catch(() => null));
3030
+ }
2948
3031
  console.log(pc14.bold("Memory index"));
2949
3032
  if (process.env.CODEBUDDY_SEMANTIC === "0") {
2950
3033
  console.log(row("recall", warn("CODEBUDDY_SEMANTIC=0: lexical only")));
@@ -3070,6 +3153,29 @@ before keeping one on. A reranker never admits a fact, it only orders them.`
3070
3153
  console.log(pc14.dim(" Run codebuddy models pull to download it now."));
3071
3154
  });
3072
3155
  });
3156
+ 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(
3157
+ "Download the optional local LLM (Qwen3-0.6B by default) that writes file cards and index facts."
3158
+ ).action(async (name2, opts) => {
3159
+ await runSafely2(async () => {
3160
+ if (name2 !== "local-llm") throw new Error(`Unknown model "${name2}"; try: local-llm.`);
3161
+ if (!LOCAL_LLM_SIZES.includes(opts.size)) {
3162
+ throw new Error(`--size must be one of ${LOCAL_LLM_SIZES.join(", ")}.`);
3163
+ }
3164
+ const added = await addLocalLlm({
3165
+ size: opts.size,
3166
+ ...opts.yes ? { yes: true } : {}
3167
+ });
3168
+ if (!added) process.exitCode = 1;
3169
+ });
3170
+ });
3171
+ models.command("remove").argument("<name>", "Optional model to remove: local-llm.").description(
3172
+ "Delete the local LLM weights; CodeBuddy falls back to keyword/vector-only behaviour."
3173
+ ).action(async (name2) => {
3174
+ await runSafely2(async () => {
3175
+ if (name2 !== "local-llm") throw new Error(`Unknown model "${name2}"; try: local-llm.`);
3176
+ await removeLocalLlm();
3177
+ });
3178
+ });
3073
3179
  models.command("pull").description("Download the local models now instead of on first use.").action(async () => {
3074
3180
  await runSafely2(async () => {
3075
3181
  const { LocalModelProvider, rerankLocally } = await import("./local-ST4WMRCG.js");
@@ -4887,54 +4993,10 @@ function registerTelemetryCommands(program, runSafely2) {
4887
4993
  }
4888
4994
 
4889
4995
  // src/cli/commands/use-command.ts
4996
+ import { execFile as execFile3, spawn as spawn7 } from "child_process";
4890
4997
  import { basename as basename2 } from "path";
4891
4998
  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
4999
  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
5000
  function bail2() {
4939
5001
  log2.warn("Aborted.");
4940
5002
  process.exit(1);
@@ -4953,6 +5015,131 @@ async function pingOnce2(databaseUrl) {
4953
5015
  await client.close().catch(() => void 0);
4954
5016
  }
4955
5017
  }
5018
+ async function setupZvec(configured) {
5019
+ if (configured === false) {
5020
+ log2.info(`zvec-grep: disabled in config (enable with ${pc22.cyan("codebuddy zvec enable")}).`);
5021
+ return;
5022
+ }
5023
+ let report = await zvecStatus(process.cwd()).catch(() => null);
5024
+ if (report && report.version === null && process.env.CODEBUDDY_ZVEC !== "0") {
5025
+ const install = spinner3();
5026
+ install.start(`Installing zvec-grep (${ZVEC_PACKAGE})`);
5027
+ const code2 = await npmGlobalInstall(ZVEC_PACKAGE);
5028
+ install.stop(code2 === 0 ? "zvec-grep installed." : `zvec-grep install failed (exit ${code2}).`);
5029
+ if (code2 === 0) report = await zvecStatus(process.cwd()).catch(() => null);
5030
+ }
5031
+ if (!report?.supported) {
5032
+ log2.warn(
5033
+ `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")}.`
5034
+ );
5035
+ return;
5036
+ }
5037
+ const zvecSpinner = spinner3();
5038
+ zvecSpinner.start("Building the zvec-grep semantic index");
5039
+ const code = report.indexReady ? 0 : await buildZvecIndex(process.cwd(), { quiet: true });
5040
+ if (code !== 0) {
5041
+ zvecSpinner.stop(`zvec-grep index failed (exit ${code}); keyword-only file discovery.`);
5042
+ return;
5043
+ }
5044
+ await setZvecEnabled(true);
5045
+ zvecSpinner.stop("zvec-grep semantic file discovery enabled.");
5046
+ }
5047
+ var ZVEC_PACKAGE = "@zvec/zvec-grep@0.2";
5048
+ function npmGlobalInstall(spec) {
5049
+ return new Promise((resolve6) => {
5050
+ execFile3(
5051
+ "npm",
5052
+ ["install", "-g", spec],
5053
+ { timeout: 10 * 6e4 },
5054
+ (error) => resolve6(error ? typeof error.code === "number" ? error.code : 1 : 0)
5055
+ );
5056
+ });
5057
+ }
5058
+ async function setupEmbeddings(embeddings) {
5059
+ if (embeddings !== "local" || process.env.CODEBUDDY_SEMANTIC === "0") return;
5060
+ const pull = spinner3();
5061
+ pull.start("Downloading the local embedding model (~90 MB, once)");
5062
+ try {
5063
+ const { LocalModelProvider } = await import("./local-ST4WMRCG.js");
5064
+ await new LocalModelProvider().embed({ input: "warm up" });
5065
+ pull.stop("Local embedding model ready.");
5066
+ } catch (error) {
5067
+ pull.stop(
5068
+ `Embedding model unavailable (${error.message.split("\n")[0]}); recall stays lexical until ${pc22.cyan("codebuddy models pull")} succeeds.`
5069
+ );
5070
+ }
5071
+ }
5072
+ function startBackgroundIndex(withModel) {
5073
+ const entry = process.argv[1];
5074
+ if (!entry) return;
5075
+ const child = spawn7(process.execPath, [entry, "index", "--cards"], {
5076
+ cwd: process.cwd(),
5077
+ detached: true,
5078
+ stdio: "ignore"
5079
+ });
5080
+ child.unref();
5081
+ log2.info(
5082
+ withModel ? "Building file vectors, intent cards and project facts in the background." : "Building file vectors in the background."
5083
+ );
5084
+ }
5085
+ async function setupLocalLlm(choice, interactive) {
5086
+ if (choice === false || process.env.CODEBUDDY_LOCAL_LLM === "0") {
5087
+ log2.info(`Local LLM: skipped (add later with ${pc22.cyan("codebuddy models add local-llm")}).`);
5088
+ return false;
5089
+ }
5090
+ const { installedLocalModel, machineCanCard } = await import("./local-75DJECVR.js");
5091
+ let installed = await installedLocalModel() !== null;
5092
+ const machine = machineCanCard();
5093
+ if (!installed && !machine.ok) {
5094
+ if (!await setupCloudQwen(machine.reason, interactive)) return false;
5095
+ installed = true;
5096
+ }
5097
+ if (!installed) {
5098
+ const { addLocalLlm: addLocalLlm2 } = await import("./local-llm-MECVJTVU.js");
5099
+ installed = await addLocalLlm2({ yes: true, interactive });
5100
+ if (!installed) {
5101
+ log2.warn(
5102
+ `Local LLM not installed; file discovery uses keywords and vectors only. Retry with ${pc22.cyan("codebuddy models add local-llm")}.`
5103
+ );
5104
+ return false;
5105
+ }
5106
+ }
5107
+ return installed;
5108
+ }
5109
+ async function setupCloudQwen(reason, interactive) {
5110
+ const existing = (await readConfigFile()).llm;
5111
+ if (existing && existing.provider !== "local") return existing.enabled;
5112
+ const fromEnv = process.env.GROQ_API_KEY ? "groq" : process.env.OPENROUTER_API_KEY ? "openrouter" : null;
5113
+ const saved = await readGlobalConfig().catch(
5114
+ () => ({})
5115
+ );
5116
+ let chosen = fromEnv ?? (saved.groqApiKey ? "groq" : saved.openrouterApiKey ? "openrouter" : null);
5117
+ if (!chosen && interactive) {
5118
+ log2.info(
5119
+ `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")}.`
5120
+ );
5121
+ const key = unwrap2(
5122
+ await password2({
5123
+ message: "Groq API key (Enter to skip; saved to ~/.codebuddy/global.json)"
5124
+ })
5125
+ ).trim();
5126
+ if (key) {
5127
+ await mergeGlobalConfig({ groqApiKey: key });
5128
+ chosen = "groq";
5129
+ }
5130
+ }
5131
+ if (!chosen) {
5132
+ log2.warn(
5133
+ `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 use")} (or ${pc22.cyan("export GROQ_API_KEY=\u2026")} and ${pc22.cyan("codebuddy llm enable --provider groq")}). Keyword + vector discovery work meanwhile.`
5134
+ );
5135
+ return false;
5136
+ }
5137
+ await setLlmEnabled(true, { provider: chosen });
5138
+ log2.info(
5139
+ `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\`.`
5140
+ );
5141
+ return true;
5142
+ }
4956
5143
  function sanitizeNamespace(input) {
4957
5144
  return input.replace(/[^a-zA-Z0-9_.:-]/g, "-").slice(0, 128) || "default";
4958
5145
  }
@@ -5138,6 +5325,9 @@ Namespace: ${pc22.bold(namespace)}`,
5138
5325
  tokenBudget: existingConfig.tokenBudget ?? 4e3,
5139
5326
  graphify: { enabled: graphifyConfigured, graphPath: "graphify-out/graph.json" }
5140
5327
  });
5328
+ await setupEmbeddings(embeddings);
5329
+ await setupZvec(existingConfig.zvec?.enabled);
5330
+ startBackgroundIndex(await setupLocalLlm(options.localLlm, interactive));
5141
5331
  const graphifyMessage = graphifySetup?.message ?? "Graphify disabled; CodeBuddy MCP remains available.";
5142
5332
  await writeProjectMcpConfig({
5143
5333
  repositoryRoot: process.cwd(),
@@ -5331,7 +5521,7 @@ function createCli() {
5331
5521
  ).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(
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(
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(
5335
5525
  async (namespace, opts) => {
5336
5526
  await runSafely(async () => {
5337
5527
  await runUseCommand({
@@ -5341,6 +5531,7 @@ function createCli() {
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
  }
@@ -5728,6 +5929,14 @@ Quick start:
5728
5929
  see exactly what an agent would be given`
5729
5930
  );
5730
5931
  }
5932
+ async function indexModelStats(root) {
5933
+ const { cardProgress } = await import("./summaries-QXWFFDUZ.js");
5934
+ const { listIndexFacts: listIndexFacts2 } = await import("./index-facts-4MTEHKMN.js");
5935
+ return {
5936
+ cards: await cardProgress(root).catch(() => ({ carded: 0, eligible: 0 })),
5937
+ indexFacts: (await listIndexFacts2(root).catch(() => [])).length
5938
+ };
5939
+ }
5731
5940
  async function withRuntime(fn) {
5732
5941
  await runSafely(async () => {
5733
5942
  const runtime = await createRuntime(await loadRuntimeConfig());
@@ -5847,4 +6056,4 @@ function printStorage(database, capture2) {
5847
6056
  export {
5848
6057
  createCli
5849
6058
  };
5850
- //# sourceMappingURL=main-SFC5LV4E.js.map
6059
+ //# sourceMappingURL=main-RLMKQZW4.js.map