@orquentra/codebuddy 2.9.0 → 2.14.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.
- package/CLAUDE_CODEX.md +33 -0
- package/README.md +27 -3
- package/dist/{adapters-S4HHTWJW.js → adapters-OJRSVFV3.js} +2 -2
- package/dist/{chunk-GKNYMNFM.js → chunk-4DKYKDHI.js} +6 -1
- package/dist/chunk-4DKYKDHI.js.map +1 -0
- package/dist/{chunk-SRA4MDVG.js → chunk-7QMHIJBA.js} +30 -3
- package/dist/chunk-7QMHIJBA.js.map +1 -0
- package/dist/{chunk-6NZEZJYD.js → chunk-ESC4Y3L7.js} +3 -5
- package/dist/{chunk-6NZEZJYD.js.map → chunk-ESC4Y3L7.js.map} +1 -1
- package/dist/chunk-GQKWUHMA.js +20 -0
- package/dist/chunk-GQKWUHMA.js.map +1 -0
- package/dist/{chunk-J4T6HHI4.js → chunk-L6BRYZ6G.js} +537 -316
- package/dist/chunk-L6BRYZ6G.js.map +1 -0
- package/dist/{chunk-UK6XTS2Y.js → chunk-LRQNPYCK.js} +13 -19
- package/dist/chunk-LRQNPYCK.js.map +1 -0
- package/dist/{chunk-GXOML4UZ.js → chunk-LW75SKZR.js} +2 -2
- package/dist/{chunk-ENKDFLOO.js → chunk-UOTKHSQ5.js} +25 -8
- package/dist/chunk-UOTKHSQ5.js.map +1 -0
- package/dist/{chunk-W7GAXL6C.js → chunk-V7PT6ER5.js} +6 -5
- package/dist/chunk-V7PT6ER5.js.map +1 -0
- package/dist/{chunk-F4BJ3DXW.js → chunk-XSNW6Y3X.js} +1253 -315
- package/dist/chunk-XSNW6Y3X.js.map +1 -0
- package/dist/{chunk-2WH2Q4TA.js → chunk-YJPCS2GP.js} +256 -68
- package/dist/chunk-YJPCS2GP.js.map +1 -0
- package/dist/{chunk-TLWGZVPC.js → chunk-YNSTIMCF.js} +746 -66
- package/dist/chunk-YNSTIMCF.js.map +1 -0
- package/dist/{chunk-QN76UJP7.js → chunk-YWDH6DQ6.js} +70 -4
- package/dist/chunk-YWDH6DQ6.js.map +1 -0
- package/dist/cli/index.js +23 -9
- package/dist/cli/index.js.map +1 -1
- package/dist/{codebuddy-DO7ZhoWn.d.ts → codebuddy-CHqMptZB.d.ts} +35 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.js +8 -8
- package/dist/install-EU35ERK5.js +30 -0
- package/dist/langgraph/index.d.ts +1 -1
- package/dist/lean-43ADYTBW.js +28 -0
- package/dist/{main-YJKSYFPT.js → main-SFC5LV4E.js} +704 -242
- package/dist/main-SFC5LV4E.js.map +1 -0
- package/dist/{operations-OG4ISWIM.js → operations-6Z6ZDVBP.js} +3 -3
- package/dist/{pretool-command-PRFOIHMW.js → pretool-command-B5GY5T6E.js} +2 -2
- package/dist/prompt-gate-TDTFFQW2.js +8 -0
- package/dist/{runtime-GCSEIAVD.js → runtime-W5AW32SD.js} +14 -6
- package/dist/{summaries-ML53R7EL.js → summaries-32PHOB6L.js} +3 -4
- package/dist/{zvec-XDZNZ2OL.js → zvec-24HLAFGN.js} +3 -4
- package/dist/zvec-24HLAFGN.js.map +1 -0
- package/package.json +11 -2
- package/dist/chunk-2WH2Q4TA.js.map +0 -1
- package/dist/chunk-ENKDFLOO.js.map +0 -1
- package/dist/chunk-F4BJ3DXW.js.map +0 -1
- package/dist/chunk-GKNYMNFM.js.map +0 -1
- package/dist/chunk-ITQO632W.js +0 -673
- package/dist/chunk-ITQO632W.js.map +0 -1
- package/dist/chunk-J4T6HHI4.js.map +0 -1
- package/dist/chunk-QN76UJP7.js.map +0 -1
- package/dist/chunk-SRA4MDVG.js.map +0 -1
- package/dist/chunk-TLWGZVPC.js.map +0 -1
- package/dist/chunk-UK6XTS2Y.js.map +0 -1
- package/dist/chunk-W7GAXL6C.js.map +0 -1
- package/dist/install-BXM56QCD.js +0 -19
- package/dist/lean-command-7DFW32MA.js +0 -19
- package/dist/main-YJKSYFPT.js.map +0 -1
- /package/dist/{adapters-S4HHTWJW.js.map → adapters-OJRSVFV3.js.map} +0 -0
- /package/dist/{chunk-GXOML4UZ.js.map → chunk-LW75SKZR.js.map} +0 -0
- /package/dist/{install-BXM56QCD.js.map → install-EU35ERK5.js.map} +0 -0
- /package/dist/{lean-command-7DFW32MA.js.map → lean-43ADYTBW.js.map} +0 -0
- /package/dist/{operations-OG4ISWIM.js.map → operations-6Z6ZDVBP.js.map} +0 -0
- /package/dist/{pretool-command-PRFOIHMW.js.map → pretool-command-B5GY5T6E.js.map} +0 -0
- /package/dist/{runtime-GCSEIAVD.js.map → prompt-gate-TDTFFQW2.js.map} +0 -0
- /package/dist/{summaries-ML53R7EL.js.map → runtime-W5AW32SD.js.map} +0 -0
- /package/dist/{zvec-XDZNZ2OL.js.map → summaries-32PHOB6L.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
refreshZvecIndex
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-LW75SKZR.js";
|
|
4
4
|
import {
|
|
5
5
|
PlanFileStore,
|
|
6
6
|
PlanLifecycle,
|
|
@@ -11,38 +11,41 @@ import {
|
|
|
11
11
|
configuredReranker,
|
|
12
12
|
enforceContextBudget,
|
|
13
13
|
llmMemoryOptions,
|
|
14
|
+
rankSemanticReuse,
|
|
14
15
|
readPolicyFile,
|
|
15
16
|
resolveRiskPaths
|
|
16
|
-
} from "./chunk-
|
|
17
|
-
import {
|
|
18
|
-
SessionCapsuleLedger,
|
|
19
|
-
coveringTestCandidates,
|
|
20
|
-
estimateTokens
|
|
21
|
-
} from "./chunk-GKNYMNFM.js";
|
|
17
|
+
} from "./chunk-XSNW6Y3X.js";
|
|
22
18
|
import {
|
|
23
19
|
readTraces,
|
|
24
20
|
recordTrace
|
|
25
21
|
} from "./chunk-YN7TEIUH.js";
|
|
22
|
+
import {
|
|
23
|
+
SessionCapsuleLedger,
|
|
24
|
+
coveringTestCandidates,
|
|
25
|
+
estimateTokens,
|
|
26
|
+
findCoveringTest
|
|
27
|
+
} from "./chunk-4DKYKDHI.js";
|
|
26
28
|
import {
|
|
27
29
|
codeBuddyConfigSchema,
|
|
28
30
|
createRuntime,
|
|
29
31
|
listFactsPage
|
|
30
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-YWDH6DQ6.js";
|
|
31
33
|
import {
|
|
32
34
|
MemoryFileStore,
|
|
33
35
|
VERSION
|
|
34
|
-
} from "./chunk-
|
|
35
|
-
import {
|
|
36
|
-
generateGraphifyGraph,
|
|
37
|
-
loadProjectArchitectureMap
|
|
38
|
-
} from "./chunk-TLWGZVPC.js";
|
|
36
|
+
} from "./chunk-7QMHIJBA.js";
|
|
39
37
|
import {
|
|
38
|
+
NON_SOURCE_PATH,
|
|
39
|
+
TEST_PATH,
|
|
40
40
|
buildArchitectureMap,
|
|
41
|
+
generateGraphifyGraph,
|
|
41
42
|
indexRepository,
|
|
42
43
|
loadFreshIndex,
|
|
44
|
+
loadProjectArchitectureMap,
|
|
43
45
|
neighbours,
|
|
44
|
-
queryMap
|
|
45
|
-
|
|
46
|
+
queryMap,
|
|
47
|
+
taskTerms
|
|
48
|
+
} from "./chunk-YNSTIMCF.js";
|
|
46
49
|
import {
|
|
47
50
|
redactPersisted
|
|
48
51
|
} from "./chunk-AL6VBTNA.js";
|
|
@@ -303,8 +306,8 @@ async function traceCliCommand(command, started, root = process.cwd()) {
|
|
|
303
306
|
await flushTelemetry(root);
|
|
304
307
|
}
|
|
305
308
|
async function installedClients(repositoryRoot) {
|
|
306
|
-
const { CLIENT_IDS } = await import("./adapters-
|
|
307
|
-
const { detectClient } = await import("./install-
|
|
309
|
+
const { CLIENT_IDS } = await import("./adapters-OJRSVFV3.js");
|
|
310
|
+
const { detectClient } = await import("./install-EU35ERK5.js");
|
|
308
311
|
const found = [];
|
|
309
312
|
for (const id of CLIENT_IDS) {
|
|
310
313
|
const status = await detectClient(id, repositoryRoot).catch(() => null);
|
|
@@ -328,7 +331,8 @@ var OMITTABLE_SECTIONS = [
|
|
|
328
331
|
"handoffs",
|
|
329
332
|
"architecture",
|
|
330
333
|
"symbols",
|
|
331
|
-
"neighbours"
|
|
334
|
+
"neighbours",
|
|
335
|
+
"reuse"
|
|
332
336
|
];
|
|
333
337
|
var SESSION_TTL_MS = 30 * 60 * 1e3;
|
|
334
338
|
async function applySessionDelivery(repositoryRoot, payload, options) {
|
|
@@ -373,10 +377,88 @@ async function applySessionDelivery(repositoryRoot, payload, options) {
|
|
|
373
377
|
}
|
|
374
378
|
}
|
|
375
379
|
|
|
380
|
+
// src/context/reuse.ts
|
|
381
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
382
|
+
import { join as join2 } from "path";
|
|
383
|
+
var REUSE_ADMISSION = { minMatched: 2, minCoverage: 0.67, limit: 3 };
|
|
384
|
+
var REUSABLE_KINDS = /* @__PURE__ */ new Set(["function", "class", "method", "const"]);
|
|
385
|
+
async function findReuse(repositoryRoot, task, manifest, map, admission = REUSE_ADMISSION, semanticRanker = rankSemanticReuse) {
|
|
386
|
+
const terms = new Set(taskTerms(task));
|
|
387
|
+
if (terms.size < admission.minMatched) return [];
|
|
388
|
+
const symbols = Object.values(manifest.entries).filter((entry) => !TEST_PATH.test(entry.path) && !NON_SOURCE_PATH.test(entry.path)).flatMap(
|
|
389
|
+
(entry) => (entry.symbolTable ?? []).filter((symbol) => symbol.exported && REUSABLE_KINDS.has(symbol.kind)).map((symbol) => ({ ...symbol, path: entry.path, words: new Set(taskTerms(symbol.name)) }))
|
|
390
|
+
);
|
|
391
|
+
const frequency = /* @__PURE__ */ new Map();
|
|
392
|
+
for (const symbol of symbols) {
|
|
393
|
+
for (const word of symbol.words) frequency.set(word, (frequency.get(word) ?? 0) + 1);
|
|
394
|
+
}
|
|
395
|
+
const idf = (word) => Math.log((symbols.length + 1) / ((frequency.get(word) ?? 0) + 1));
|
|
396
|
+
const ranked = symbols.map((symbol) => {
|
|
397
|
+
const matched = [...symbol.words].filter((word) => terms.has(word));
|
|
398
|
+
return {
|
|
399
|
+
symbol,
|
|
400
|
+
matched,
|
|
401
|
+
coverage: symbol.words.size > 0 ? matched.length / symbol.words.size : 0,
|
|
402
|
+
score: matched.reduce((sum, word) => sum + idf(word), 0)
|
|
403
|
+
};
|
|
404
|
+
}).filter(
|
|
405
|
+
(item) => item.matched.length >= admission.minMatched && item.coverage >= admission.minCoverage
|
|
406
|
+
).sort((a, b) => b.score - a.score || a.symbol.path.localeCompare(b.symbol.path));
|
|
407
|
+
let semantic = false;
|
|
408
|
+
if (!ranked.length) {
|
|
409
|
+
const candidates = await semanticRanker(repositoryRoot, task, manifest);
|
|
410
|
+
const first = candidates[0];
|
|
411
|
+
if (first && first.score >= 0.84 && first.score - (candidates[1]?.score ?? 0) >= 0.04) {
|
|
412
|
+
const symbol = symbols.find((item) => item.path === first.path && item.name === first.name);
|
|
413
|
+
if (symbol) {
|
|
414
|
+
ranked.push({ symbol, matched: [], coverage: 0, score: first.score });
|
|
415
|
+
semantic = true;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
const indexed = new Set(Object.keys(manifest.entries));
|
|
420
|
+
const seen = /* @__PURE__ */ new Set();
|
|
421
|
+
const picked = [];
|
|
422
|
+
for (const { symbol, matched, score } of ranked) {
|
|
423
|
+
const key = `${symbol.path}:${symbol.name}`;
|
|
424
|
+
if (seen.has(key)) continue;
|
|
425
|
+
seen.add(key);
|
|
426
|
+
const usage = await usageSite(repositoryRoot, symbol.path, symbol.name, map);
|
|
427
|
+
const test = findCoveringTest(symbol.path, indexed);
|
|
428
|
+
picked.push({
|
|
429
|
+
path: symbol.path,
|
|
430
|
+
name: symbol.name,
|
|
431
|
+
signature: symbol.signature,
|
|
432
|
+
startLine: symbol.startLine,
|
|
433
|
+
endLine: symbol.endLine,
|
|
434
|
+
matched,
|
|
435
|
+
score: Math.round(score * 100) / 100,
|
|
436
|
+
...semantic ? { evidence: "semantic" } : {},
|
|
437
|
+
...usage ? { usage } : {},
|
|
438
|
+
...test ? { test } : {}
|
|
439
|
+
});
|
|
440
|
+
if (picked.length === admission.limit) break;
|
|
441
|
+
}
|
|
442
|
+
return picked;
|
|
443
|
+
}
|
|
444
|
+
async function usageSite(root, path, name, map) {
|
|
445
|
+
const short = name.split(".").at(-1) ?? name;
|
|
446
|
+
const pattern = new RegExp(`\\b${short.replace(/[^\w$]/g, "")}\\b`);
|
|
447
|
+
const importers = (map?.edges ?? []).filter((edge) => edge.to === path && !TEST_PATH.test(edge.from)).map((edge) => edge.from).sort();
|
|
448
|
+
for (const importer of importers.slice(0, 20)) {
|
|
449
|
+
const source = await readFile2(join2(root, importer), "utf8").catch(() => "");
|
|
450
|
+
const lines = source.split("\n");
|
|
451
|
+
const index = lines.findIndex((line) => pattern.test(line) && !/^\s*import\b/.test(line));
|
|
452
|
+
if (index !== -1)
|
|
453
|
+
return `${importer}:${index + 1}: ${(lines[index] ?? "").trim().slice(0, 140)}`;
|
|
454
|
+
}
|
|
455
|
+
return void 0;
|
|
456
|
+
}
|
|
457
|
+
|
|
376
458
|
// src/suggest/engine.ts
|
|
377
459
|
import { constants } from "fs";
|
|
378
460
|
import { access } from "fs/promises";
|
|
379
|
-
import { extname, join as
|
|
461
|
+
import { extname, join as join3, resolve } from "path";
|
|
380
462
|
|
|
381
463
|
// src/suggest/types.ts
|
|
382
464
|
var SEVERITY_RANK = {
|
|
@@ -413,7 +495,7 @@ async function generateSuggestions(input) {
|
|
|
413
495
|
resolveRelevantPlan(planStore, namespace, input.planId),
|
|
414
496
|
memoryStore.findIncidentFactsForPaths({ namespace, paths: targetPaths }),
|
|
415
497
|
buildArchitectureMap(repositoryRoot),
|
|
416
|
-
readPolicyFile(
|
|
498
|
+
readPolicyFile(join3(repositoryRoot, ".codebuddy", "policies.yaml"))
|
|
417
499
|
]);
|
|
418
500
|
const suggestions = [];
|
|
419
501
|
for (const item of risks.items) {
|
|
@@ -493,7 +575,7 @@ async function generateSuggestions(input) {
|
|
|
493
575
|
for (const rule of policyFile.rules) {
|
|
494
576
|
const prefix = literalPrefix(rule.pattern);
|
|
495
577
|
if (!prefix) continue;
|
|
496
|
-
if (await pathExists(
|
|
578
|
+
if (await pathExists(join3(repositoryRoot, prefix))) continue;
|
|
497
579
|
suggestions.push({
|
|
498
580
|
id: `stale_policy:${rule.id}`,
|
|
499
581
|
category: "stale_policy",
|
|
@@ -537,7 +619,7 @@ function isTestableCode(path) {
|
|
|
537
619
|
}
|
|
538
620
|
async function hasSiblingTest(repositoryRoot, path) {
|
|
539
621
|
for (const candidate of coveringTestCandidates(path)) {
|
|
540
|
-
if (await pathExists(
|
|
622
|
+
if (await pathExists(join3(repositoryRoot, candidate))) return true;
|
|
541
623
|
}
|
|
542
624
|
return false;
|
|
543
625
|
}
|
|
@@ -586,6 +668,40 @@ function normalize(path) {
|
|
|
586
668
|
return path.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
587
669
|
}
|
|
588
670
|
|
|
671
|
+
// src/mcp/database-retry.ts
|
|
672
|
+
function isTransientDatabaseError(error) {
|
|
673
|
+
let current = error;
|
|
674
|
+
for (let depth = 0; depth < 4 && current && typeof current === "object"; depth++) {
|
|
675
|
+
const item = current;
|
|
676
|
+
if (typeof item.code === "string" && (/* @__PURE__ */ new Set([
|
|
677
|
+
"ECONNRESET",
|
|
678
|
+
"ECONNREFUSED",
|
|
679
|
+
"EPIPE",
|
|
680
|
+
"CONNECTION_CLOSED",
|
|
681
|
+
"CONNECTION_ENDED",
|
|
682
|
+
"08000",
|
|
683
|
+
"08001",
|
|
684
|
+
"08003",
|
|
685
|
+
"08006",
|
|
686
|
+
"57P01",
|
|
687
|
+
"57P02",
|
|
688
|
+
"57P03"
|
|
689
|
+
])).has(item.code))
|
|
690
|
+
return true;
|
|
691
|
+
current = item.cause;
|
|
692
|
+
}
|
|
693
|
+
return false;
|
|
694
|
+
}
|
|
695
|
+
async function withDatabaseRetry(operation, readOnly, delayMs = 250) {
|
|
696
|
+
try {
|
|
697
|
+
return await operation();
|
|
698
|
+
} catch (error) {
|
|
699
|
+
if (!readOnly || !isTransientDatabaseError(error)) throw error;
|
|
700
|
+
await new Promise((resolve5) => setTimeout(resolve5, delayMs));
|
|
701
|
+
return operation();
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
589
705
|
// src/mcp/tool-result.ts
|
|
590
706
|
function serializeToolResult(value, format = process.env.CODEBUDDY_RESPONSE_FORMAT ?? "both") {
|
|
591
707
|
if (format !== "text" && format !== "both") {
|
|
@@ -631,7 +747,7 @@ var recallSchema = {
|
|
|
631
747
|
budget: z.number().int().positive().optional(),
|
|
632
748
|
callerModel: z.string().min(1).max(200).optional(),
|
|
633
749
|
conflictMode: z.enum(["all", "latest", "highest_confidence"]).optional(),
|
|
634
|
-
/**
|
|
750
|
+
/** Default true: a flat list of memories (id, content, date). false = full provenance + stats. */
|
|
635
751
|
compact: z.boolean().optional()
|
|
636
752
|
};
|
|
637
753
|
var shareSchema = {
|
|
@@ -768,6 +884,30 @@ var FILE_TOOLS = Object.keys(mcpToolInputSchemas).filter(
|
|
|
768
884
|
(name) => !DATABASE_TOOLS.includes(name)
|
|
769
885
|
);
|
|
770
886
|
var DEGRADED_FALLBACK = "PostgreSQL is unavailable, so this call did nothing. File-based tools still work; read the repository directly and say recall is unavailable instead of guessing. Run `codebuddy db doctor` to diagnose; the next call after retryInMs reconnects.";
|
|
887
|
+
var LEAN_TOOLS = {
|
|
888
|
+
context_pack: {
|
|
889
|
+
description: "Find the files, symbols and line ranges for a coding task. Call only when the injected CodeBuddy block names no files.",
|
|
890
|
+
defaults: { tokenBudget: 800 },
|
|
891
|
+
inputSchema: {
|
|
892
|
+
task: z.string(),
|
|
893
|
+
paths: z.array(z.string()).optional(),
|
|
894
|
+
sessionId: z.string().optional(),
|
|
895
|
+
ack: z.string().optional()
|
|
896
|
+
}
|
|
897
|
+
},
|
|
898
|
+
recall: {
|
|
899
|
+
description: "Recall stored project memory (conventions, decisions, gotchas) for a question.",
|
|
900
|
+
inputSchema: { query: z.string() },
|
|
901
|
+
defaults: { sessionId: "agent" }
|
|
902
|
+
},
|
|
903
|
+
remember: {
|
|
904
|
+
description: "Store one durable project fact (convention, decision, gotcha).",
|
|
905
|
+
inputSchema: { content: z.string() },
|
|
906
|
+
// A fact is Markdown-backed and survives reindex; the full profile's
|
|
907
|
+
// default (`interaction`) is a database-only conversation record.
|
|
908
|
+
defaults: { type: "fact" }
|
|
909
|
+
}
|
|
910
|
+
};
|
|
771
911
|
var CORE_TOOLS = [
|
|
772
912
|
"context_pack",
|
|
773
913
|
"context_bootstrap",
|
|
@@ -777,9 +917,9 @@ var CORE_TOOLS = [
|
|
|
777
917
|
"health"
|
|
778
918
|
];
|
|
779
919
|
function resolveToolProfile(value = process.env.CODEBUDDY_TOOL_PROFILE) {
|
|
780
|
-
if (value === void 0 || value === "" || value === "
|
|
781
|
-
if (value === "core") return
|
|
782
|
-
throw new Error(`CODEBUDDY_TOOL_PROFILE must be "core" or "full", got "${value}"`);
|
|
920
|
+
if (value === void 0 || value === "" || value === "lean") return "lean";
|
|
921
|
+
if (value === "core" || value === "full") return value;
|
|
922
|
+
throw new Error(`CODEBUDDY_TOOL_PROFILE must be "lean", "core" or "full", got "${value}"`);
|
|
783
923
|
}
|
|
784
924
|
var PLAN_BODY_LIMIT = 8e3;
|
|
785
925
|
function serializePlan(plan) {
|
|
@@ -790,7 +930,7 @@ function serializePlan(plan) {
|
|
|
790
930
|
return { plan: { ...plan, body } };
|
|
791
931
|
}
|
|
792
932
|
function registerCodeBuddyTools(server, options, profile = "full") {
|
|
793
|
-
const exposed = (name) => profile === "full" || CORE_TOOLS.includes(name);
|
|
933
|
+
const exposed = (name) => profile === "full" || (profile === "lean" ? name in LEAN_TOOLS : CORE_TOOLS.includes(name));
|
|
794
934
|
const config = "database" in options ? options.config : options.memory.config;
|
|
795
935
|
const traceRoot = config.projectRoot ?? process.cwd();
|
|
796
936
|
const register = (name, toolConfig, callback) => {
|
|
@@ -815,7 +955,29 @@ function registerCodeBuddyTools(server, options, profile = "full") {
|
|
|
815
955
|
throw error;
|
|
816
956
|
}
|
|
817
957
|
});
|
|
818
|
-
const
|
|
958
|
+
const lean = profile === "lean" ? LEAN_TOOLS[name] : void 0;
|
|
959
|
+
const full = mcpToolInputSchemas[name];
|
|
960
|
+
const tool = lean ? server.registerTool(
|
|
961
|
+
name,
|
|
962
|
+
{
|
|
963
|
+
...toolConfig,
|
|
964
|
+
description: lean.description,
|
|
965
|
+
inputSchema: lean.inputSchema
|
|
966
|
+
},
|
|
967
|
+
// The lean schema only lists fields; the full schema enforces limits.
|
|
968
|
+
(async (input, extra) => {
|
|
969
|
+
const parsed = full.safeParse({ ...lean.defaults, ...input });
|
|
970
|
+
if (!parsed.success) {
|
|
971
|
+
return {
|
|
972
|
+
content: [
|
|
973
|
+
{ type: "text", text: `Invalid input: ${parsed.error.message}` }
|
|
974
|
+
],
|
|
975
|
+
isError: true
|
|
976
|
+
};
|
|
977
|
+
}
|
|
978
|
+
return traced(parsed.data, extra);
|
|
979
|
+
})
|
|
980
|
+
) : server.registerTool(name, toolConfig, traced);
|
|
819
981
|
if (!exposed(name)) tool.remove();
|
|
820
982
|
return tool;
|
|
821
983
|
};
|
|
@@ -831,7 +993,25 @@ function registerCodeBuddyTools(server, options, profile = "full") {
|
|
|
831
993
|
isError: true
|
|
832
994
|
};
|
|
833
995
|
}
|
|
834
|
-
|
|
996
|
+
try {
|
|
997
|
+
return json(
|
|
998
|
+
await withDatabaseRetry(
|
|
999
|
+
() => run(db),
|
|
1000
|
+
["recall", "list_facts", "list_namespaces"].includes(tool)
|
|
1001
|
+
)
|
|
1002
|
+
);
|
|
1003
|
+
} catch (error) {
|
|
1004
|
+
if (!isTransientDatabaseError(error)) throw error;
|
|
1005
|
+
return {
|
|
1006
|
+
...json({
|
|
1007
|
+
degraded: true,
|
|
1008
|
+
tool,
|
|
1009
|
+
database: { ...database.status(), available: false, state: "unavailable" },
|
|
1010
|
+
fallback: "The database connection failed during this operation. A write may have committed; verify state before repeating it. File-based context remains available."
|
|
1011
|
+
}),
|
|
1012
|
+
isError: true
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
835
1015
|
};
|
|
836
1016
|
const namespace = config.namespace;
|
|
837
1017
|
const projectRoot = config.projectRoot ?? process.cwd();
|
|
@@ -880,12 +1060,12 @@ function registerCodeBuddyTools(server, options, profile = "full") {
|
|
|
880
1060
|
"recall",
|
|
881
1061
|
{
|
|
882
1062
|
title: "Recall",
|
|
883
|
-
description: "Recall
|
|
1063
|
+
description: "Recall project memory for a query. Returns a flat list of memories (id, content, date), or an empty list when nothing is relevant. Pass compact=false for full provenance and token stats.",
|
|
884
1064
|
inputSchema: mcpToolInputSchemas.recall.shape
|
|
885
1065
|
},
|
|
886
1066
|
async (input) => viaDatabase(
|
|
887
1067
|
"recall",
|
|
888
|
-
async (db) => withResponseTokens(await db.memory.recall(toRecallInput(input))
|
|
1068
|
+
async (db) => input.compact === false ? withResponseTokens(await db.memory.recall(toRecallInput(input))) : compactRecall(await db.memory.recall(toRecallInput(input)))
|
|
889
1069
|
)
|
|
890
1070
|
);
|
|
891
1071
|
register(
|
|
@@ -1148,24 +1328,35 @@ function registerCodeBuddyTools(server, options, profile = "full") {
|
|
|
1148
1328
|
description: "Build the smallest useful context pack for a coding task. Automatically finds relevant files from the local index when paths are omitted, then includes only bounded symbols, policies, risks, incidents, architecture neighbours, and relevant team facts within the requested token budget. Use this once at the start of a task instead of manually chaining context, risk, map, and memory tools. Request evidence=source with exact paths when short excerpts omit needed behavior; source excerpts remain bounded by the token budget. Optional sessionId enables session-aware delivery: pass the previous response's delivery.deliveryId as ack and unchanged sections are omitted (listed in delivery.unchanged); pass full=true to get everything again.",
|
|
1149
1329
|
inputSchema: mcpToolInputSchemas.context_pack.shape
|
|
1150
1330
|
},
|
|
1151
|
-
async (input) =>
|
|
1152
|
-
await
|
|
1153
|
-
projectRoot,
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
})
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1331
|
+
async (input) => {
|
|
1332
|
+
const context = await buildBeforeEditContext({
|
|
1333
|
+
repositoryRoot: projectRoot,
|
|
1334
|
+
namespace,
|
|
1335
|
+
task: input.task,
|
|
1336
|
+
semanticRanker: rankContextFacts,
|
|
1337
|
+
...await rerankerOption(),
|
|
1338
|
+
...input.evidence ? { evidence: input.evidence } : {},
|
|
1339
|
+
...input.paths !== void 0 ? { paths: input.paths } : {},
|
|
1340
|
+
...input.planId !== void 0 ? { planId: input.planId } : {},
|
|
1341
|
+
...input.useGit !== void 0 ? { useGit: input.useGit } : {},
|
|
1342
|
+
tokenBudget: input.tokenBudget ?? tokenBudget ?? 4e3
|
|
1343
|
+
});
|
|
1344
|
+
const reuse = await loadFreshIndex(projectRoot).then(
|
|
1345
|
+
async ({ manifest }) => findReuse(
|
|
1346
|
+
projectRoot,
|
|
1347
|
+
input.task,
|
|
1348
|
+
manifest,
|
|
1349
|
+
await loadProjectArchitectureMap(projectRoot, manifest)
|
|
1350
|
+
)
|
|
1351
|
+
).catch(() => []);
|
|
1352
|
+
return json(
|
|
1353
|
+
await applySessionDelivery(
|
|
1354
|
+
projectRoot,
|
|
1355
|
+
enforceContextBudget({ ...context, ...reuse.length > 0 ? { reuse } : {} }),
|
|
1356
|
+
deliveryOptions(input)
|
|
1357
|
+
)
|
|
1358
|
+
);
|
|
1359
|
+
}
|
|
1169
1360
|
);
|
|
1170
1361
|
register(
|
|
1171
1362
|
"context_after_turn",
|
|
@@ -1259,21 +1450,17 @@ function toRecallInput(input) {
|
|
|
1259
1450
|
const { compact: _compact, ...rest } = input;
|
|
1260
1451
|
return cleanOptional(rest);
|
|
1261
1452
|
}
|
|
1262
|
-
function
|
|
1263
|
-
const
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
} : {
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
content: message.content
|
|
1274
|
-
})),
|
|
1275
|
-
stats
|
|
1276
|
-
} : { ...result, stats };
|
|
1453
|
+
function compactRecall(result) {
|
|
1454
|
+
const memories = result.messages.map((message) => ({
|
|
1455
|
+
...message.fact ? { id: message.fact.id } : {},
|
|
1456
|
+
content: message.content,
|
|
1457
|
+
...message.fact?.createdAt ? { date: message.fact.createdAt.slice(0, 10) } : {}
|
|
1458
|
+
}));
|
|
1459
|
+
return memories.length > 0 ? { memories } : { memories, note: "nothing relevant" };
|
|
1460
|
+
}
|
|
1461
|
+
function withResponseTokens(result) {
|
|
1462
|
+
const stats = { ...result.stats };
|
|
1463
|
+
const value = { ...result, stats };
|
|
1277
1464
|
for (let pass = 0; pass < 2; pass++) {
|
|
1278
1465
|
const serialized = serializeToolResult(value);
|
|
1279
1466
|
stats.responseTokens = estimateTokens(serialized.content[0]?.text);
|
|
@@ -1292,7 +1479,7 @@ import pino from "pino";
|
|
|
1292
1479
|
|
|
1293
1480
|
// src/automation/project-sync.ts
|
|
1294
1481
|
import { watch as fsWatch2 } from "fs";
|
|
1295
|
-
import { join as
|
|
1482
|
+
import { join as join4, relative, resolve as resolve3, sep } from "path";
|
|
1296
1483
|
|
|
1297
1484
|
// src/indexer/watch.ts
|
|
1298
1485
|
import { watch as fsWatch } from "fs";
|
|
@@ -1473,7 +1660,7 @@ function startProjectSync(options) {
|
|
|
1473
1660
|
300
|
|
1474
1661
|
);
|
|
1475
1662
|
memoryWatcher = fsWatch2(
|
|
1476
|
-
|
|
1663
|
+
join4(repositoryRoot, ".codebuddy", "memory"),
|
|
1477
1664
|
{ recursive: true },
|
|
1478
1665
|
(_event, filename) => {
|
|
1479
1666
|
const name = filename?.replace(/\\/g, "/") ?? "";
|
|
@@ -1676,7 +1863,7 @@ async function startMcpServer(options = {}) {
|
|
|
1676
1863
|
};
|
|
1677
1864
|
let stopping;
|
|
1678
1865
|
const stop = () => {
|
|
1679
|
-
stopping
|
|
1866
|
+
if (!stopping) stopping = shutdown().finally(() => process.exit(0));
|
|
1680
1867
|
};
|
|
1681
1868
|
process.once("SIGINT", stop);
|
|
1682
1869
|
process.once("SIGTERM", stop);
|
|
@@ -1784,9 +1971,10 @@ export {
|
|
|
1784
1971
|
showTelemetryNoticeOnce,
|
|
1785
1972
|
flushTelemetry,
|
|
1786
1973
|
traceCliCommand,
|
|
1974
|
+
findReuse,
|
|
1787
1975
|
generateSuggestions,
|
|
1788
1976
|
mcpToolInputSchemas,
|
|
1789
1977
|
registerCodeBuddyTools,
|
|
1790
1978
|
startMcpServer
|
|
1791
1979
|
};
|
|
1792
|
-
//# sourceMappingURL=chunk-
|
|
1980
|
+
//# sourceMappingURL=chunk-YJPCS2GP.js.map
|