@orquentra/codebuddy 2.7.0 → 2.8.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 (52) hide show
  1. package/README.md +10 -0
  2. package/dist/adapters-S4HHTWJW.js +24 -0
  3. package/dist/{chunk-EEUFU7KV.js → chunk-6DONJW3W.js} +163 -22
  4. package/dist/chunk-6DONJW3W.js.map +1 -0
  5. package/dist/{chunk-JUAFCLRB.js → chunk-6KGYAFG7.js} +2 -2
  6. package/dist/{chunk-JUAFCLRB.js.map → chunk-6KGYAFG7.js.map} +1 -1
  7. package/dist/{chunk-BQM7FJ7D.js → chunk-6NZEZJYD.js} +5 -3
  8. package/dist/{chunk-BQM7FJ7D.js.map → chunk-6NZEZJYD.js.map} +1 -1
  9. package/dist/{chunk-AQHPEJR6.js → chunk-EJQVGFH2.js} +2 -2
  10. package/dist/{chunk-MSUAVR3R.js → chunk-GXOML4UZ.js} +2 -2
  11. package/dist/chunk-ITQO632W.js +673 -0
  12. package/dist/chunk-ITQO632W.js.map +1 -0
  13. package/dist/chunk-J4T6HHI4.js +1444 -0
  14. package/dist/chunk-J4T6HHI4.js.map +1 -0
  15. package/dist/{chunk-AK2UAE32.js → chunk-NY2YXP3R.js} +2 -2
  16. package/dist/{chunk-5EDGAZQN.js → chunk-TLWGZVPC.js} +64 -725
  17. package/dist/chunk-TLWGZVPC.js.map +1 -0
  18. package/dist/{chunk-LCSBL5TT.js → chunk-UK6XTS2Y.js} +5 -3
  19. package/dist/{chunk-LCSBL5TT.js.map → chunk-UK6XTS2Y.js.map} +1 -1
  20. package/dist/chunk-W7GAXL6C.js +104 -0
  21. package/dist/chunk-W7GAXL6C.js.map +1 -0
  22. package/dist/{chunk-TUR6YKEE.js → chunk-XL2HLACE.js} +1 -1
  23. package/dist/chunk-XL2HLACE.js.map +1 -0
  24. package/dist/{chunk-YJIA2MFL.js → chunk-ZNLSCV3J.js} +15 -13
  25. package/dist/{chunk-YJIA2MFL.js.map → chunk-ZNLSCV3J.js.map} +1 -1
  26. package/dist/cli/index.js +4 -4
  27. package/dist/index.js +9 -8
  28. package/dist/install-BXM56QCD.js +19 -0
  29. package/dist/{lean-command-CWJAVQ5G.js → lean-command-7DFW32MA.js} +4 -3
  30. package/dist/{main-AHNGHC4L.js → main-QUASSFP7.js} +902 -1686
  31. package/dist/main-QUASSFP7.js.map +1 -0
  32. package/dist/{operations-7BTAAUHF.js → operations-WJR7AZZF.js} +3 -3
  33. package/dist/{pretool-command-3SXQSMAO.js → pretool-command-JTKX6G3M.js} +3 -3
  34. package/dist/{runtime-E7JQZ2BZ.js → runtime-OQQ22VJF.js} +7 -6
  35. package/dist/{summaries-XV3JGGCC.js → summaries-ML53R7EL.js} +4 -3
  36. package/dist/summaries-ML53R7EL.js.map +1 -0
  37. package/dist/{zvec-KP4KNB7Y.js → zvec-XDZNZ2OL.js} +4 -3
  38. package/dist/zvec-XDZNZ2OL.js.map +1 -0
  39. package/package.json +1 -1
  40. package/dist/chunk-5EDGAZQN.js.map +0 -1
  41. package/dist/chunk-EEUFU7KV.js.map +0 -1
  42. package/dist/chunk-TUR6YKEE.js.map +0 -1
  43. package/dist/main-AHNGHC4L.js.map +0 -1
  44. /package/dist/{lean-command-CWJAVQ5G.js.map → adapters-S4HHTWJW.js.map} +0 -0
  45. /package/dist/{chunk-AQHPEJR6.js.map → chunk-EJQVGFH2.js.map} +0 -0
  46. /package/dist/{chunk-MSUAVR3R.js.map → chunk-GXOML4UZ.js.map} +0 -0
  47. /package/dist/{chunk-AK2UAE32.js.map → chunk-NY2YXP3R.js.map} +0 -0
  48. /package/dist/{operations-7BTAAUHF.js.map → install-BXM56QCD.js.map} +0 -0
  49. /package/dist/{pretool-command-3SXQSMAO.js.map → lean-command-7DFW32MA.js.map} +0 -0
  50. /package/dist/{runtime-E7JQZ2BZ.js.map → operations-WJR7AZZF.js.map} +0 -0
  51. /package/dist/{summaries-XV3JGGCC.js.map → pretool-command-JTKX6G3M.js.map} +0 -0
  52. /package/dist/{zvec-KP4KNB7Y.js.map → runtime-OQQ22VJF.js.map} +0 -0
package/README.md CHANGED
@@ -52,6 +52,15 @@ This opens a live local page showing:
52
52
 
53
53
  It updates as you work, and the traces never leave your machine.
54
54
 
55
+ ## Telemetry
56
+
57
+ CodeBuddy sends anonymous usage counts (tool names, timings, success, version,
58
+ OS, agent client) so we can see which features are used. Prompts, code, file
59
+ paths and repository names are never sent, and nothing is sent until a notice
60
+ has been shown once in your terminal. Opt out with `codebuddy telemetry disable`,
61
+ `CODEBUDDY_TELEMETRY=0` or `DO_NOT_TRACK=1`; CI never sends. Details:
62
+ [docs/telemetry.md](https://github.com/Orquentra-Inc/CodeBuddy/blob/main/docs/telemetry.md).
63
+
55
64
  ## Try it without a database
56
65
 
57
66
  ```bash
@@ -66,6 +75,7 @@ needs more detail. Inspect missing or truncated evidence before editing.
66
75
  ## Documentation
67
76
 
68
77
  - [Full guide and examples](https://github.com/Orquentra-Inc/CodeBuddy/blob/348bb7c/README.md)
78
+ - [Telemetry and the admin panel](https://github.com/Orquentra-Inc/CodeBuddy/blob/main/docs/telemetry.md)
69
79
  - [Token budgets](https://github.com/Orquentra-Inc/CodeBuddy/blob/main/docs/token-budgets.md)
70
80
  - [Optional LLM setup](https://github.com/Orquentra-Inc/CodeBuddy/blob/main/docs/llm-byok.md)
71
81
  - [Source and issues](https://github.com/Orquentra-Inc/CodeBuddy)
@@ -0,0 +1,24 @@
1
+ import {
2
+ AUTOMATION_MODES,
3
+ CLIENT_ADAPTERS,
4
+ CLIENT_IDS,
5
+ LIFECYCLE_EVENTS,
6
+ MODE_LABELS,
7
+ parseClient,
8
+ parseMode,
9
+ sharedRulesReaders,
10
+ supportedModes
11
+ } from "./chunk-W7GAXL6C.js";
12
+ import "./chunk-PZ5AY32C.js";
13
+ export {
14
+ AUTOMATION_MODES,
15
+ CLIENT_ADAPTERS,
16
+ CLIENT_IDS,
17
+ LIFECYCLE_EVENTS,
18
+ MODE_LABELS,
19
+ parseClient,
20
+ parseMode,
21
+ sharedRulesReaders,
22
+ supportedModes
23
+ };
24
+ //# sourceMappingURL=adapters-S4HHTWJW.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  refreshZvecIndex
3
- } from "./chunk-MSUAVR3R.js";
3
+ } from "./chunk-GXOML4UZ.js";
4
4
  import {
5
5
  PlanFileStore,
6
6
  PlanLifecycle,
@@ -13,33 +13,36 @@ import {
13
13
  llmMemoryOptions,
14
14
  readPolicyFile,
15
15
  resolveRiskPaths
16
- } from "./chunk-YJIA2MFL.js";
17
- import {
18
- recordTrace
19
- } from "./chunk-TUR6YKEE.js";
16
+ } from "./chunk-ZNLSCV3J.js";
20
17
  import {
21
18
  SessionCapsuleLedger,
22
19
  coveringTestCandidates,
23
20
  estimateTokens
24
21
  } from "./chunk-GKNYMNFM.js";
22
+ import {
23
+ readTraces,
24
+ recordTrace
25
+ } from "./chunk-XL2HLACE.js";
25
26
  import {
26
27
  codeBuddyConfigSchema,
27
28
  createRuntime,
28
29
  listFactsPage
29
- } from "./chunk-AK2UAE32.js";
30
+ } from "./chunk-NY2YXP3R.js";
30
31
  import {
31
32
  MemoryFileStore,
32
33
  VERSION
33
- } from "./chunk-JUAFCLRB.js";
34
+ } from "./chunk-6KGYAFG7.js";
34
35
  import {
35
- buildArchitectureMap,
36
36
  generateGraphifyGraph,
37
+ loadProjectArchitectureMap
38
+ } from "./chunk-TLWGZVPC.js";
39
+ import {
40
+ buildArchitectureMap,
37
41
  indexRepository,
38
42
  loadFreshIndex,
39
- loadProjectArchitectureMap,
40
43
  neighbours,
41
44
  queryMap
42
- } from "./chunk-5EDGAZQN.js";
45
+ } from "./chunk-ITQO632W.js";
43
46
  import {
44
47
  redactPersisted
45
48
  } from "./chunk-AL6VBTNA.js";
@@ -167,11 +170,138 @@ function checksum(content) {
167
170
  return createHash("sha256").update(content).digest("hex");
168
171
  }
169
172
 
173
+ // src/telemetry/telemetry.ts
174
+ import { createHash as createHash2, randomUUID } from "crypto";
175
+ import { mkdir, readFile, writeFile } from "fs/promises";
176
+ import { homedir } from "os";
177
+ import { dirname, join } from "path";
178
+ var TELEMETRY_URL = "https://tanmay-hp-linux-server.tail41864f.ts.net/v1/events";
179
+ var FLUSH_INTERVAL_MS = 10 * 6e4;
180
+ var MAX_EVENTS = 500;
181
+ var SEND_TIMEOUT_MS = 3e3;
182
+ var NAME = /^[A-Za-z][A-Za-z0-9_:.-]{0,63}$/;
183
+ function telemetryStatePath(home = homedir()) {
184
+ return join(home, ".codebuddy", "telemetry.json");
185
+ }
186
+ async function readTelemetryState(home = homedir()) {
187
+ try {
188
+ const state = JSON.parse(await readFile(telemetryStatePath(home), "utf8"));
189
+ if (typeof state.installId === "string" && state.installId) return state;
190
+ } catch {
191
+ }
192
+ return writeTelemetryState({ installId: randomUUID() }, home);
193
+ }
194
+ async function writeTelemetryState(state, home = homedir()) {
195
+ const path = telemetryStatePath(home);
196
+ await mkdir(dirname(path), { recursive: true, mode: 448 });
197
+ await writeFile(path, `${JSON.stringify(state, null, 2)}
198
+ `, { mode: 384 });
199
+ return state;
200
+ }
201
+ function telemetryBlocked(state, env = process.env) {
202
+ if (env.CODEBUDDY_TELEMETRY === "0") return "CODEBUDDY_TELEMETRY=0";
203
+ if (env.DO_NOT_TRACK === "1" || env.DO_NOT_TRACK === "true") return "DO_NOT_TRACK";
204
+ if (env.CI) return "CI";
205
+ if (state.disabled) return "disabled with `codebuddy telemetry disable`";
206
+ if (!telemetryUrl(env)) return "no collector configured";
207
+ if (!state.noticeAt) return "first-run notice not shown yet";
208
+ return null;
209
+ }
210
+ function telemetryUrl(env = process.env) {
211
+ return env.CODEBUDDY_TELEMETRY_URL ?? TELEMETRY_URL;
212
+ }
213
+ var TELEMETRY_NOTICE = `CodeBuddy collects anonymous usage data (which tools run, how long they take,
214
+ success/failure, version, OS, agent client) to improve the product.
215
+ Prompts, code, file paths and repository names are never sent.
216
+ Opt out any time: \`codebuddy telemetry disable\` or CODEBUDDY_TELEMETRY=0.
217
+ Details: https://github.com/Orquentra-Inc/CodeBuddy/blob/main/docs/telemetry.md`;
218
+ async function showTelemetryNoticeOnce(write = (text) => process.stderr.write(text), options = {}) {
219
+ const env = options.env ?? process.env;
220
+ if (!(options.tty ?? process.stderr.isTTY)) return;
221
+ try {
222
+ const state = await readTelemetryState(options.home);
223
+ if (state.noticeAt || state.disabled || env.CODEBUDDY_TELEMETRY === "0" || env.CI) return;
224
+ if (!telemetryUrl(env)) return;
225
+ write(`
226
+ ${TELEMETRY_NOTICE}
227
+
228
+ `);
229
+ await writeTelemetryState({ ...state, noticeAt: (/* @__PURE__ */ new Date()).toISOString() }, options.home);
230
+ } catch {
231
+ }
232
+ }
233
+ function anonymize(installId, value) {
234
+ return createHash2("sha256").update(`${installId}\0${value}`).digest("hex").slice(0, 16);
235
+ }
236
+ function toTelemetryEvents(installId, traces) {
237
+ return traces.map((trace) => ({
238
+ ts: trace.ts,
239
+ kind: NAME.test(trace.kind) ? trace.kind : "other",
240
+ name: NAME.test(trace.name) ? trace.name : "other",
241
+ ms: Math.max(0, Math.round(trace.ms) || 0),
242
+ ok: trace.ok === true,
243
+ ...typeof trace.tokensIn === "number" ? { tokensIn: trace.tokensIn } : {},
244
+ ...typeof trace.tokensOut === "number" ? { tokensOut: trace.tokensOut } : {},
245
+ ...trace.session ? { session: anonymize(installId, trace.session) } : {}
246
+ }));
247
+ }
248
+ async function flushTelemetry(repositoryRoot, options = {}) {
249
+ const env = options.env ?? process.env;
250
+ const now = options.now ?? Date.now();
251
+ try {
252
+ const state = await readTelemetryState(options.home);
253
+ if (telemetryBlocked(state, env)) return 0;
254
+ const cursorPath = join(repositoryRoot, ".codebuddy", "cache", "telemetry.json");
255
+ const cursor = await readFile(cursorPath, "utf8").then((raw) => JSON.parse(raw)).catch(() => ({}));
256
+ if (cursor.flushedAt && now - cursor.flushedAt < FLUSH_INTERVAL_MS) return 0;
257
+ const save = (next) => mkdir(dirname(cursorPath), { recursive: true }).then(
258
+ () => writeFile(cursorPath, JSON.stringify(next))
259
+ );
260
+ await save({ ...cursor, flushedAt: now });
261
+ const pending = (await readTraces(repositoryRoot, 1e4)).filter((trace) => !cursor.sentTs || trace.ts > cursor.sentTs).slice(-MAX_EVENTS);
262
+ const last = pending.at(-1);
263
+ if (!last) return 0;
264
+ const batch = {
265
+ installId: state.installId,
266
+ project: anonymize(state.installId, repositoryRoot),
267
+ version: VERSION,
268
+ platform: process.platform,
269
+ arch: process.arch,
270
+ node: process.versions.node.split(".")[0] ?? "",
271
+ clients: await (options.clients ?? (() => installedClients(repositoryRoot)))().catch(
272
+ () => []
273
+ ),
274
+ events: toTelemetryEvents(state.installId, pending)
275
+ };
276
+ const response = await (options.fetchImpl ?? fetch)(telemetryUrl(env), {
277
+ method: "POST",
278
+ headers: { "content-type": "application/json" },
279
+ body: JSON.stringify(batch),
280
+ signal: AbortSignal.timeout(SEND_TIMEOUT_MS)
281
+ });
282
+ if (!response.ok) return 0;
283
+ await save({ sentTs: last.ts, flushedAt: now });
284
+ return batch.events.length;
285
+ } catch {
286
+ return 0;
287
+ }
288
+ }
289
+ async function installedClients(repositoryRoot) {
290
+ const { CLIENT_IDS } = await import("./adapters-S4HHTWJW.js");
291
+ const { detectClient } = await import("./install-BXM56QCD.js");
292
+ const found = [];
293
+ for (const id of CLIENT_IDS) {
294
+ const status = await detectClient(id, repositoryRoot).catch(() => null);
295
+ if (status && (status.hooks || status.mcp)) found.push(id);
296
+ }
297
+ return found;
298
+ }
299
+
170
300
  // src/mcp/tools/index.ts
171
301
  import { z } from "zod";
172
302
 
173
303
  // src/context/delivery.ts
174
- import { createHash as createHash2 } from "crypto";
304
+ import { createHash as createHash3 } from "crypto";
175
305
  var DELIVERY_VERSION = 1;
176
306
  var OMITTABLE_SECTIONS = [
177
307
  "plan",
@@ -190,7 +320,7 @@ async function applySessionDelivery(repositoryRoot, payload, options) {
190
320
  if (!options.sessionId) return output;
191
321
  const sections = OMITTABLE_SECTIONS.filter((key) => output[key] !== void 0).map((key) => ({
192
322
  key: `mcp:${key}`,
193
- hash: createHash2("sha256").update(JSON.stringify(output[key])).digest("hex")
323
+ hash: createHash3("sha256").update(JSON.stringify(output[key])).digest("hex")
194
324
  }));
195
325
  try {
196
326
  await new SessionCapsuleLedger(repositoryRoot).exchange(options.sessionId, sections, {
@@ -217,7 +347,7 @@ async function applySessionDelivery(repositoryRoot, payload, options) {
217
347
  output = enforceContextBudget(output);
218
348
  return OMITTABLE_SECTIONS.filter((key) => output[key] !== void 0).map((key) => ({
219
349
  key: `mcp:${key}`,
220
- hash: createHash2("sha256").update(JSON.stringify(output[key])).digest("hex")
350
+ hash: createHash3("sha256").update(JSON.stringify(output[key])).digest("hex")
221
351
  }));
222
352
  }
223
353
  });
@@ -230,7 +360,7 @@ async function applySessionDelivery(repositoryRoot, payload, options) {
230
360
  // src/suggest/engine.ts
231
361
  import { constants } from "fs";
232
362
  import { access } from "fs/promises";
233
- import { extname, join, resolve } from "path";
363
+ import { extname, join as join2, resolve } from "path";
234
364
 
235
365
  // src/suggest/types.ts
236
366
  var SEVERITY_RANK = {
@@ -267,7 +397,7 @@ async function generateSuggestions(input) {
267
397
  resolveRelevantPlan(planStore, namespace, input.planId),
268
398
  memoryStore.findIncidentFactsForPaths({ namespace, paths: targetPaths }),
269
399
  buildArchitectureMap(repositoryRoot),
270
- readPolicyFile(join(repositoryRoot, ".codebuddy", "policies.yaml"))
400
+ readPolicyFile(join2(repositoryRoot, ".codebuddy", "policies.yaml"))
271
401
  ]);
272
402
  const suggestions = [];
273
403
  for (const item of risks.items) {
@@ -347,7 +477,7 @@ async function generateSuggestions(input) {
347
477
  for (const rule of policyFile.rules) {
348
478
  const prefix = literalPrefix(rule.pattern);
349
479
  if (!prefix) continue;
350
- if (await pathExists(join(repositoryRoot, prefix))) continue;
480
+ if (await pathExists(join2(repositoryRoot, prefix))) continue;
351
481
  suggestions.push({
352
482
  id: `stale_policy:${rule.id}`,
353
483
  category: "stale_policy",
@@ -391,7 +521,7 @@ function isTestableCode(path) {
391
521
  }
392
522
  async function hasSiblingTest(repositoryRoot, path) {
393
523
  for (const candidate of coveringTestCandidates(path)) {
394
- if (await pathExists(join(repositoryRoot, candidate))) return true;
524
+ if (await pathExists(join2(repositoryRoot, candidate))) return true;
395
525
  }
396
526
  return false;
397
527
  }
@@ -1146,7 +1276,7 @@ import pino from "pino";
1146
1276
 
1147
1277
  // src/automation/project-sync.ts
1148
1278
  import { watch as fsWatch2 } from "fs";
1149
- import { join as join2, relative, resolve as resolve3, sep } from "path";
1279
+ import { join as join3, relative, resolve as resolve3, sep } from "path";
1150
1280
 
1151
1281
  // src/indexer/watch.ts
1152
1282
  import { watch as fsWatch } from "fs";
@@ -1327,7 +1457,7 @@ function startProjectSync(options) {
1327
1457
  300
1328
1458
  );
1329
1459
  memoryWatcher = fsWatch2(
1330
- join2(repositoryRoot, ".codebuddy", "memory"),
1460
+ join3(repositoryRoot, ".codebuddy", "memory"),
1331
1461
  { recursive: true },
1332
1462
  (_event, filename) => {
1333
1463
  const name = filename?.replace(/\\/g, "/") ?? "";
@@ -1361,7 +1491,7 @@ function startProjectSync(options) {
1361
1491
 
1362
1492
  // src/core/project-root.ts
1363
1493
  import { realpath, stat } from "fs/promises";
1364
- import { homedir } from "os";
1494
+ import { homedir as homedir2 } from "os";
1365
1495
  import { isAbsolute, parse, resolve as resolve4 } from "path";
1366
1496
  async function resolveInitializedProjectRoot(configuredRoot, cwd = process.cwd()) {
1367
1497
  if (configuredRoot !== void 0 && !isAbsolute(configuredRoot)) {
@@ -1369,7 +1499,7 @@ async function resolveInitializedProjectRoot(configuredRoot, cwd = process.cwd()
1369
1499
  }
1370
1500
  const selected = resolve4(configuredRoot ?? cwd);
1371
1501
  const root = await realpath(selected);
1372
- if (root === parse(root).root || root === await realpath(homedir())) {
1502
+ if (root === parse(root).root || root === await realpath(homedir2())) {
1373
1503
  throw new Error(
1374
1504
  "Select an initialized project folder, not the filesystem root or home folder."
1375
1505
  );
@@ -1519,8 +1649,11 @@ async function startMcpServer(options = {}) {
1519
1649
  });
1520
1650
  const server = new McpServer({ name: "codebuddy", version: VERSION });
1521
1651
  registerCodeBuddyTools(server, { config, database }, toolProfile);
1652
+ const telemetry = setInterval(() => void flushTelemetry(projectRoot), 6e4);
1653
+ telemetry.unref();
1522
1654
  const shutdown = async () => {
1523
1655
  logger.info("codebuddy MCP server shutting down");
1656
+ clearInterval(telemetry);
1524
1657
  await server.close();
1525
1658
  await sync.close();
1526
1659
  await database.close();
@@ -1626,9 +1759,17 @@ function isConnectionRefused(error) {
1626
1759
  export {
1627
1760
  migrateMemoryToFiles,
1628
1761
  watchRepository,
1762
+ telemetryStatePath,
1763
+ readTelemetryState,
1764
+ writeTelemetryState,
1765
+ telemetryBlocked,
1766
+ telemetryUrl,
1767
+ TELEMETRY_NOTICE,
1768
+ showTelemetryNoticeOnce,
1769
+ flushTelemetry,
1629
1770
  generateSuggestions,
1630
1771
  mcpToolInputSchemas,
1631
1772
  registerCodeBuddyTools,
1632
1773
  startMcpServer
1633
1774
  };
1634
- //# sourceMappingURL=chunk-EEUFU7KV.js.map
1775
+ //# sourceMappingURL=chunk-6DONJW3W.js.map