@orquentra/codebuddy 2.15.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.
- package/ARCHITECTURE.md +1 -1
- package/README.md +2 -2
- package/dist/{chunk-UR3DF74I.js → chunk-4DTO2E4Y.js} +8 -8
- package/dist/chunk-6OQKL35B.js +55 -0
- package/dist/chunk-6OQKL35B.js.map +1 -0
- package/dist/{chunk-ITXE27SZ.js → chunk-7JPUMXFE.js} +67 -12
- package/dist/chunk-7JPUMXFE.js.map +1 -0
- package/dist/{chunk-JJQDVGF7.js → chunk-AFXWCSQ4.js} +3 -3
- package/dist/{chunk-UATJCD2T.js → chunk-BNZDEU5K.js} +6 -6
- package/dist/{chunk-D5E7SGI5.js → chunk-GNV4X67F.js} +14 -14
- package/dist/{chunk-D5E7SGI5.js.map → chunk-GNV4X67F.js.map} +1 -1
- package/dist/{chunk-KYRM3HPP.js → chunk-IM5RFEW4.js} +3 -3
- package/dist/{chunk-UTPC66MV.js → chunk-K4XNTYV7.js} +12 -4
- package/dist/chunk-K4XNTYV7.js.map +1 -0
- package/dist/{chunk-6YI2D5F5.js → chunk-MNSMW4MZ.js} +3 -3
- package/dist/{chunk-BTTQWSDS.js → chunk-NNJSAYHY.js} +1 -1
- package/dist/chunk-NNJSAYHY.js.map +1 -0
- package/dist/{chunk-KQCP3ZYH.js → chunk-OO7Q7XZF.js} +7 -7
- package/dist/{chunk-RZ266NHE.js → chunk-PZ4EBFIA.js} +5 -5
- package/dist/{chunk-RZ266NHE.js.map → chunk-PZ4EBFIA.js.map} +1 -1
- package/dist/{chunk-WHKXUXHE.js → chunk-RGIVHKBH.js} +5 -5
- package/dist/chunk-RGIVHKBH.js.map +1 -0
- package/dist/{chunk-4BSO3HST.js → chunk-RKF4HBBQ.js} +3 -3
- package/dist/{chunk-YMXFSHXT.js → chunk-VBDM7HC5.js} +2 -2
- package/dist/{chunk-YMXFSHXT.js.map → chunk-VBDM7HC5.js.map} +1 -1
- package/dist/{chunk-U7KDM62P.js → chunk-VF7DKRIU.js} +15 -8
- package/dist/chunk-VF7DKRIU.js.map +1 -0
- package/dist/{chunk-M75SN4D4.js → chunk-Y4QQML24.js} +3 -3
- package/dist/{chunk-M75SN4D4.js.map → chunk-Y4QQML24.js.map} +1 -1
- package/dist/cli/index.js +4 -4
- package/dist/{config-TXQJCCVI.js → config-S2BDMT4B.js} +4 -4
- package/dist/{expand-KWYGNCKZ.js → expand-TONFVT5D.js} +5 -5
- package/dist/{file-vectors-MKXTSGGH.js → file-vectors-VREL75MA.js} +3 -3
- package/dist/{index-facts-4MTEHKMN.js → index-facts-MBJKBYA3.js} +7 -7
- package/dist/index-pass-RP23G2CR.js +15 -0
- package/dist/index.js +8 -8
- package/dist/{install-E3JWINTN.js → install-MCJIJMFF.js} +7 -7
- package/dist/{lean-MLTAFS5L.js → lean-CBHVW6JM.js} +4 -4
- package/dist/{local-75DJECVR.js → local-TH5XCLVJ.js} +2 -2
- package/dist/{local-llm-MECVJTVU.js → local-llm-GZREB5DF.js} +4 -4
- package/dist/{main-RLMKQZW4.js → main-QF6Z552J.js} +39 -42
- package/dist/main-QF6Z552J.js.map +1 -0
- package/dist/{memory-PFFVMTCH.js → memory-LSZKSF53.js} +5 -5
- package/dist/{operations-STR7S5UO.js → operations-BPI633VN.js} +4 -4
- package/dist/{rerank-XXKWDI5Q.js → rerank-EPCTSEVP.js} +5 -5
- package/dist/{runtime-SWFWGTQ4.js → runtime-W5ACTDFS.js} +5 -5
- package/dist/{summaries-QXWFFDUZ.js → summaries-W2J5FCLG.js} +9 -8
- package/dist/{zvec-F73IBWO5.js → zvec-LSA2IRAU.js} +4 -4
- package/package.json +1 -1
- package/dist/chunk-BTTQWSDS.js.map +0 -1
- package/dist/chunk-ITXE27SZ.js.map +0 -1
- package/dist/chunk-U7KDM62P.js.map +0 -1
- package/dist/chunk-UTPC66MV.js.map +0 -1
- package/dist/chunk-WHKXUXHE.js.map +0 -1
- package/dist/index-pass-TBDUEZCB.js +0 -14
- package/dist/main-RLMKQZW4.js.map +0 -1
- /package/dist/{chunk-UR3DF74I.js.map → chunk-4DTO2E4Y.js.map} +0 -0
- /package/dist/{chunk-JJQDVGF7.js.map → chunk-AFXWCSQ4.js.map} +0 -0
- /package/dist/{chunk-UATJCD2T.js.map → chunk-BNZDEU5K.js.map} +0 -0
- /package/dist/{chunk-KYRM3HPP.js.map → chunk-IM5RFEW4.js.map} +0 -0
- /package/dist/{chunk-6YI2D5F5.js.map → chunk-MNSMW4MZ.js.map} +0 -0
- /package/dist/{chunk-KQCP3ZYH.js.map → chunk-OO7Q7XZF.js.map} +0 -0
- /package/dist/{chunk-4BSO3HST.js.map → chunk-RKF4HBBQ.js.map} +0 -0
- /package/dist/{config-TXQJCCVI.js.map → config-S2BDMT4B.js.map} +0 -0
- /package/dist/{expand-KWYGNCKZ.js.map → expand-TONFVT5D.js.map} +0 -0
- /package/dist/{file-vectors-MKXTSGGH.js.map → file-vectors-VREL75MA.js.map} +0 -0
- /package/dist/{index-facts-4MTEHKMN.js.map → index-facts-MBJKBYA3.js.map} +0 -0
- /package/dist/{index-pass-TBDUEZCB.js.map → index-pass-RP23G2CR.js.map} +0 -0
- /package/dist/{install-E3JWINTN.js.map → install-MCJIJMFF.js.map} +0 -0
- /package/dist/{lean-MLTAFS5L.js.map → lean-CBHVW6JM.js.map} +0 -0
- /package/dist/{local-75DJECVR.js.map → local-TH5XCLVJ.js.map} +0 -0
- /package/dist/{local-llm-MECVJTVU.js.map → local-llm-GZREB5DF.js.map} +0 -0
- /package/dist/{memory-PFFVMTCH.js.map → memory-LSZKSF53.js.map} +0 -0
- /package/dist/{operations-STR7S5UO.js.map → operations-BPI633VN.js.map} +0 -0
- /package/dist/{rerank-XXKWDI5Q.js.map → rerank-EPCTSEVP.js.map} +0 -0
- /package/dist/{runtime-SWFWGTQ4.js.map → runtime-W5ACTDFS.js.map} +0 -0
- /package/dist/{summaries-QXWFFDUZ.js.map → summaries-W2J5FCLG.js.map} +0 -0
- /package/dist/{zvec-F73IBWO5.js.map → zvec-LSA2IRAU.js.map} +0 -0
package/ARCHITECTURE.md
CHANGED
|
@@ -130,7 +130,7 @@ four criteria in the 2.15 brief:
|
|
|
130
130
|
Cactus has no maintained Node.js binding for a desktop/server process, and its
|
|
131
131
|
Node package hosts only the Needle tool-calling model, which the brief excludes
|
|
132
132
|
(the job is plain English → code vocabulary, not tool selection). So
|
|
133
|
-
node-llama-cpp. It is not a package dependency: `codebuddy
|
|
133
|
+
node-llama-cpp. It is not a package dependency: `codebuddy` (or
|
|
134
134
|
`codebuddy models add local-llm`) installs it with `npm -g` and downloads the pinned GGUF into
|
|
135
135
|
`~/.cache/codebuddy/models/gguf/`, checking size and sha256 before moving it
|
|
136
136
|
into place. Forcing `gpu: false` on macOS makes node-llama-cpp compile llama.cpp
|
package/README.md
CHANGED
|
@@ -79,7 +79,7 @@ installation fails, include the exact npm error and requested version in a
|
|
|
79
79
|
A small local model can write a plain-English **intent card** for every source
|
|
80
80
|
file, so a task worded nothing like the code (for example "the browser forgets
|
|
81
81
|
how long to wait before reconnecting") still finds the right file. `codebuddy`
|
|
82
|
-
/ `codebuddy
|
|
82
|
+
/ `codebuddy` installs it as part of setup (Qwen3-0.6B Q4_K_M, ~400 MB,
|
|
83
83
|
checksum-verified; skip with `--no-local-llm` or `CODEBUDDY_LOCAL_LLM=0`), and it
|
|
84
84
|
runs offline after that. To manage it by hand:
|
|
85
85
|
|
|
@@ -127,7 +127,7 @@ the 2.14 behaviour.
|
|
|
127
127
|
shows them and `codebuddy memory prune --origin index` removes them. Facts a
|
|
128
128
|
person or agent wrote are never replaced.
|
|
129
129
|
|
|
130
|
-
`codebuddy
|
|
130
|
+
`codebuddy` also builds the zvec-grep index and enables it when `zg` 0.2.x
|
|
131
131
|
is installed, and `codebuddy serve` keeps a `zg server` running so the prompt
|
|
132
132
|
hook gets vector candidates within its deadline.
|
|
133
133
|
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from "./chunk-AL6VBTNA.js";
|
|
9
9
|
import {
|
|
10
10
|
readConfigFile
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-Y4QQML24.js";
|
|
12
12
|
import {
|
|
13
13
|
assertNoSymlinkBetween,
|
|
14
14
|
writeAtomic
|
|
@@ -1268,7 +1268,7 @@ async function withLlmSummaries(repositoryRoot, manifest) {
|
|
|
1268
1268
|
const entries = Object.values(manifest.entries);
|
|
1269
1269
|
try {
|
|
1270
1270
|
if ((await readConfigFile(repositoryRoot)).llm?.enabled === false) return entries;
|
|
1271
|
-
const { loadLlmSummaries, cardWords } = await import("./summaries-
|
|
1271
|
+
const { loadLlmSummaries, cardWords } = await import("./summaries-W2J5FCLG.js");
|
|
1272
1272
|
const summaries = await loadLlmSummaries(repositoryRoot, manifest);
|
|
1273
1273
|
if (summaries.size === 0) return entries;
|
|
1274
1274
|
return entries.map((entry) => {
|
|
@@ -1288,7 +1288,7 @@ async function withExpansion(repositoryRoot, task, ranked, manifest, mapSource,
|
|
|
1288
1288
|
if (ranked.paths.length > 0 && ranked.confidence !== "low") return null;
|
|
1289
1289
|
if (identifierTokens(task).some((token) => CODE_IDENTIFIER.test(token))) return null;
|
|
1290
1290
|
const deadlineAt = options.expansionDeadlineAt ?? Date.now() + EXPANSION_MS.pack;
|
|
1291
|
-
const { expandTask } = await import("./expand-
|
|
1291
|
+
const { expandTask } = await import("./expand-TONFVT5D.js");
|
|
1292
1292
|
const expansion = await expandTask(repositoryRoot, task, deadlineAt - Date.now());
|
|
1293
1293
|
if (!expansion) return null;
|
|
1294
1294
|
const expanded = await rankTaskFiles(
|
|
@@ -1388,7 +1388,7 @@ async function rankTaskFiles(repositoryRoot, task, manifest, mapSource, options)
|
|
|
1388
1388
|
reason: "semantic retrieval off",
|
|
1389
1389
|
stale: 0,
|
|
1390
1390
|
changed: 0
|
|
1391
|
-
} : await import("./zvec-
|
|
1391
|
+
} : await import("./zvec-LSA2IRAU.js").then(
|
|
1392
1392
|
({ zvecCandidates }) => zvecCandidates(
|
|
1393
1393
|
repositoryRoot,
|
|
1394
1394
|
task,
|
|
@@ -1592,7 +1592,7 @@ var MIN_RERANK_MS = 200;
|
|
|
1592
1592
|
async function llmRerank(input) {
|
|
1593
1593
|
let config;
|
|
1594
1594
|
try {
|
|
1595
|
-
config = await (await import("./config-
|
|
1595
|
+
config = await (await import("./config-S2BDMT4B.js")).resolveLlmConfig(input.repositoryRoot);
|
|
1596
1596
|
} catch {
|
|
1597
1597
|
return null;
|
|
1598
1598
|
}
|
|
@@ -1617,7 +1617,7 @@ async function llmRerank(input) {
|
|
|
1617
1617
|
input.deadlineAt === void 0 ? config.timeoutMs : input.deadlineAt - Date.now()
|
|
1618
1618
|
);
|
|
1619
1619
|
if (deadlineMs < MIN_RERANK_MS) return skipped("no time left in the hook budget");
|
|
1620
|
-
const { rerankCandidates } = await import("./rerank-
|
|
1620
|
+
const { rerankCandidates } = await import("./rerank-EPCTSEVP.js");
|
|
1621
1621
|
const result = await rerankCandidates(
|
|
1622
1622
|
input.repositoryRoot,
|
|
1623
1623
|
config,
|
|
@@ -2033,7 +2033,7 @@ async function refreshFileVectors(repositoryRoot, manifest, deadlineAt, embed =
|
|
|
2033
2033
|
}
|
|
2034
2034
|
async function currentCardTexts(repositoryRoot, manifest) {
|
|
2035
2035
|
try {
|
|
2036
|
-
const { cardText, loadLlmSummaries } = await import("./summaries-
|
|
2036
|
+
const { cardText, loadLlmSummaries } = await import("./summaries-W2J5FCLG.js");
|
|
2037
2037
|
const texts = /* @__PURE__ */ new Map();
|
|
2038
2038
|
for (const [path, card] of await loadLlmSummaries(repositoryRoot, manifest)) {
|
|
2039
2039
|
if (card.symptoms) texts.set(path, `${path}
|
|
@@ -2159,4 +2159,4 @@ export {
|
|
|
2159
2159
|
namedSegments,
|
|
2160
2160
|
loadProjectArchitectureMap
|
|
2161
2161
|
};
|
|
2162
|
-
//# sourceMappingURL=chunk-
|
|
2162
|
+
//# sourceMappingURL=chunk-4DTO2E4Y.js.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// src/llm/card-lock.ts
|
|
2
|
+
import { link, mkdir, readFile, rm, writeFile } from "fs/promises";
|
|
3
|
+
import { dirname, join } from "path";
|
|
4
|
+
function cardLockPath(root) {
|
|
5
|
+
return join(root, ".codebuddy", "cache", "cards.lock");
|
|
6
|
+
}
|
|
7
|
+
async function withCardLock(root, fn) {
|
|
8
|
+
const path = cardLockPath(root);
|
|
9
|
+
await mkdir(dirname(path), { recursive: true });
|
|
10
|
+
const temporary = `${path}.${process.pid}`;
|
|
11
|
+
await writeFile(temporary, `${process.pid}
|
|
12
|
+
`, { mode: 384 });
|
|
13
|
+
try {
|
|
14
|
+
for (let attempt = 0; ; attempt++) {
|
|
15
|
+
try {
|
|
16
|
+
await link(temporary, path);
|
|
17
|
+
break;
|
|
18
|
+
} catch (error) {
|
|
19
|
+
if (error.code !== "EEXIST") throw error;
|
|
20
|
+
const holder = await cardLockHolder(root);
|
|
21
|
+
if (holder !== null || attempt > 0) return { ok: false, holder };
|
|
22
|
+
await rm(path, { force: true });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
} finally {
|
|
26
|
+
await rm(temporary, { force: true });
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
return { ok: true, value: await fn() };
|
|
30
|
+
} finally {
|
|
31
|
+
if (await readPid(path) === process.pid) await rm(path, { force: true });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function cardLockHolder(root) {
|
|
35
|
+
const pid = await readPid(cardLockPath(root));
|
|
36
|
+
return pid !== null && isAlive(pid) ? pid : null;
|
|
37
|
+
}
|
|
38
|
+
function isAlive(pid) {
|
|
39
|
+
try {
|
|
40
|
+
process.kill(pid, 0);
|
|
41
|
+
return true;
|
|
42
|
+
} catch (error) {
|
|
43
|
+
return error.code === "EPERM";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async function readPid(path) {
|
|
47
|
+
const pid = Number.parseInt(await readFile(path, "utf8").catch(() => ""), 10);
|
|
48
|
+
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export {
|
|
52
|
+
withCardLock,
|
|
53
|
+
cardLockHolder
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=chunk-6OQKL35B.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/llm/card-lock.ts"],"sourcesContent":["import { link, mkdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\n\n/**\n * One model writer per repository. Setup (`codebuddy`) starts a detached\n * `index --cards`, and every `codebuddy serve` schedules its own pass: without\n * this each loads its own model copy, they oversubscribe the CPU until every\n * file times out, and each rewrites `llm-summaries.json` from its own copy.\n * The lock file holds the writer's PID; a dead PID is a stale lock.\n */\nexport function cardLockPath(root: string): string {\n return join(root, \".codebuddy\", \"cache\", \"cards.lock\");\n}\n\nexport type CardLockResult<T> = { ok: true; value: T } | { ok: false; holder: number | null };\n\n/** Run `fn` holding the lock, or return the live holder's PID without running it. */\nexport async function withCardLock<T>(\n root: string,\n fn: () => Promise<T>,\n): Promise<CardLockResult<T>> {\n const path = cardLockPath(root);\n await mkdir(dirname(path), { recursive: true });\n // Written whole, then linked: a reader never sees an empty lock file.\n const temporary = `${path}.${process.pid}`;\n await writeFile(temporary, `${process.pid}\\n`, { mode: 0o600 });\n try {\n for (let attempt = 0; ; attempt++) {\n try {\n await link(temporary, path);\n break;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n const holder = await cardLockHolder(root);\n if (holder !== null || attempt > 0) return { ok: false, holder };\n // ponytail: two processes clearing the same stale lock at once can both\n // win; needs a rename-and-recheck if that window ever matters.\n await rm(path, { force: true });\n }\n }\n } finally {\n await rm(temporary, { force: true });\n }\n try {\n return { ok: true, value: await fn() };\n } finally {\n if ((await readPid(path)) === process.pid) await rm(path, { force: true });\n }\n}\n\n/** The live process writing cards, or null (no lock, or its process is gone). */\nexport async function cardLockHolder(root: string): Promise<number | null> {\n const pid = await readPid(cardLockPath(root));\n return pid !== null && isAlive(pid) ? pid : null;\n}\n\nfunction isAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n\nasync function readPid(path: string): Promise<number | null> {\n const pid = Number.parseInt(await readFile(path, \"utf8\").catch(() => \"\"), 10);\n return Number.isInteger(pid) && pid > 0 ? pid : null;\n}\n"],"mappings":";AAAA,SAAS,MAAM,OAAO,UAAU,IAAI,iBAAiB;AACrD,SAAS,SAAS,YAAY;AASvB,SAAS,aAAa,MAAsB;AACjD,SAAO,KAAK,MAAM,cAAc,SAAS,YAAY;AACvD;AAKA,eAAsB,aACpB,MACA,IAC4B;AAC5B,QAAM,OAAO,aAAa,IAAI;AAC9B,QAAM,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAE9C,QAAM,YAAY,GAAG,IAAI,IAAI,QAAQ,GAAG;AACxC,QAAM,UAAU,WAAW,GAAG,QAAQ,GAAG;AAAA,GAAM,EAAE,MAAM,IAAM,CAAC;AAC9D,MAAI;AACF,aAAS,UAAU,KAAK,WAAW;AACjC,UAAI;AACF,cAAM,KAAK,WAAW,IAAI;AAC1B;AAAA,MACF,SAAS,OAAO;AACd,YAAK,MAAgC,SAAS,SAAU,OAAM;AAC9D,cAAM,SAAS,MAAM,eAAe,IAAI;AACxC,YAAI,WAAW,QAAQ,UAAU,EAAG,QAAO,EAAE,IAAI,OAAO,OAAO;AAG/D,cAAM,GAAG,MAAM,EAAE,OAAO,KAAK,CAAC;AAAA,MAChC;AAAA,IACF;AAAA,EACF,UAAE;AACA,UAAM,GAAG,WAAW,EAAE,OAAO,KAAK,CAAC;AAAA,EACrC;AACA,MAAI;AACF,WAAO,EAAE,IAAI,MAAM,OAAO,MAAM,GAAG,EAAE;AAAA,EACvC,UAAE;AACA,QAAK,MAAM,QAAQ,IAAI,MAAO,QAAQ,IAAK,OAAM,GAAG,MAAM,EAAE,OAAO,KAAK,CAAC;AAAA,EAC3E;AACF;AAGA,eAAsB,eAAe,MAAsC;AACzE,QAAM,MAAM,MAAM,QAAQ,aAAa,IAAI,CAAC;AAC5C,SAAO,QAAQ,QAAQ,QAAQ,GAAG,IAAI,MAAM;AAC9C;AAEA,SAAS,QAAQ,KAAsB;AACrC,MAAI;AACF,YAAQ,KAAK,KAAK,CAAC;AACnB,WAAO;AAAA,EACT,SAAS,OAAO;AACd,WAAQ,MAAgC,SAAS;AAAA,EACnD;AACF;AAEA,eAAe,QAAQ,MAAsC;AAC3D,QAAM,MAAM,OAAO,SAAS,MAAM,SAAS,MAAM,MAAM,EAAE,MAAM,MAAM,EAAE,GAAG,EAAE;AAC5E,SAAO,OAAO,UAAU,GAAG,KAAK,MAAM,IAAI,MAAM;AAClD;","names":[]}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
chatJson
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-MNSMW4MZ.js";
|
|
4
|
+
import {
|
|
5
|
+
cardLockHolder,
|
|
6
|
+
withCardLock
|
|
7
|
+
} from "./chunk-6OQKL35B.js";
|
|
4
8
|
import {
|
|
5
9
|
CARDS_DIR,
|
|
6
10
|
refreshZvecCardIndex
|
|
7
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-RKF4HBBQ.js";
|
|
8
12
|
import {
|
|
9
13
|
CHANGELOG_PATH,
|
|
10
14
|
NON_SOURCE_PATH,
|
|
@@ -12,20 +16,21 @@ import {
|
|
|
12
16
|
loadFreshIndex,
|
|
13
17
|
loadProjectArchitectureMap,
|
|
14
18
|
readBoundedSource
|
|
15
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-4DTO2E4Y.js";
|
|
16
20
|
import {
|
|
17
21
|
redactSensitive,
|
|
18
22
|
scanSensitive
|
|
19
23
|
} from "./chunk-AL6VBTNA.js";
|
|
20
24
|
import {
|
|
21
25
|
resolveLlmConfig
|
|
22
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-RGIVHKBH.js";
|
|
23
27
|
import {
|
|
24
28
|
writeAtomic
|
|
25
29
|
} from "./chunk-CG5B3LYG.js";
|
|
26
30
|
import {
|
|
27
|
-
waitForForeground
|
|
28
|
-
|
|
31
|
+
waitForForeground,
|
|
32
|
+
warmLocalModel
|
|
33
|
+
} from "./chunk-NNJSAYHY.js";
|
|
29
34
|
|
|
30
35
|
// src/llm/summaries.ts
|
|
31
36
|
import { mkdir, readdir, readFile, rm } from "fs/promises";
|
|
@@ -53,7 +58,9 @@ var LIMITS = {
|
|
|
53
58
|
tokensPerMinute: 6e3,
|
|
54
59
|
timeoutMs: 2e4,
|
|
55
60
|
/** One file per call for the local model: a 0.6B model loses track of batches. */
|
|
56
|
-
localTimeoutMs: 3e4
|
|
61
|
+
localTimeoutMs: 3e4,
|
|
62
|
+
/** This many local timeouts in a row means the machine is overloaded: stop the pass. */
|
|
63
|
+
localTimeoutRun: 5
|
|
57
64
|
};
|
|
58
65
|
function cardEligible(path) {
|
|
59
66
|
return !TEST_PATH.test(path) && !NON_SOURCE_PATH.test(path) && !CHANGELOG_PATH.test(path) && !GENERATED_PATH.test(path);
|
|
@@ -142,12 +149,33 @@ var SYSTEM = [
|
|
|
142
149
|
"Symptoms describe what goes wrong for a user of this specific file (a wrong value, an error, nothing happening), never generic phrases.",
|
|
143
150
|
"Never invent paths or symbol names."
|
|
144
151
|
].join(" ");
|
|
152
|
+
var passPath = (root) => join(root, ".codebuddy", "cache", "cards-pass.json");
|
|
145
153
|
async function summarizeRepository(root, options = {}) {
|
|
146
154
|
const resolved = options.config ?? await resolveLlmConfig(root);
|
|
147
155
|
if (!resolved.enabled || !resolved.features.summaries) {
|
|
148
|
-
return { summarized: 0, skipped: 0, remaining: 0, stoppedBy: "disabled" };
|
|
156
|
+
return { summarized: 0, skipped: 0, timedOut: 0, remaining: 0, stoppedBy: "disabled" };
|
|
149
157
|
}
|
|
150
158
|
const config = resolved;
|
|
159
|
+
const locked = await withCardLock(root, async () => {
|
|
160
|
+
const result = await summarizeLocked(root, config, options);
|
|
161
|
+
const record = { ...result, at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
162
|
+
await writeAtomic(passPath(root), `${JSON.stringify(record)}
|
|
163
|
+
`).catch(() => {
|
|
164
|
+
});
|
|
165
|
+
return result;
|
|
166
|
+
});
|
|
167
|
+
if (locked.ok) return locked.value;
|
|
168
|
+
console.error(`[cards] already being written by pid ${locked.holder ?? "unknown"}`);
|
|
169
|
+
return {
|
|
170
|
+
summarized: 0,
|
|
171
|
+
skipped: 0,
|
|
172
|
+
timedOut: 0,
|
|
173
|
+
remaining: 0,
|
|
174
|
+
stoppedBy: "locked",
|
|
175
|
+
lockedBy: locked.holder
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
async function summarizeLocked(root, config, options) {
|
|
151
179
|
const { manifest } = await loadFreshIndex(root);
|
|
152
180
|
const store = await readStore(root);
|
|
153
181
|
for (const path of Object.keys(store)) if (!manifest.entries[path]) delete store[path];
|
|
@@ -163,10 +191,19 @@ async function summarizeRepository(root, options = {}) {
|
|
|
163
191
|
const local = config.provider === "local";
|
|
164
192
|
const batchFiles = local ? 1 : options.batchFiles ?? LIMITS.batchFiles;
|
|
165
193
|
const limit = options.limit ?? Number.POSITIVE_INFINITY;
|
|
194
|
+
const localTimeoutMs = options.localTimeoutMs ?? LIMITS.localTimeoutMs;
|
|
166
195
|
let summarized = 0;
|
|
167
196
|
let skipped = 0;
|
|
197
|
+
let timedOut = 0;
|
|
198
|
+
let timeoutRun = 0;
|
|
168
199
|
let index = 0;
|
|
169
200
|
let stoppedBy = "done";
|
|
201
|
+
if (pending.length > 0) options.onProgress?.(0, pending.length);
|
|
202
|
+
if (local && pending.length > 0 && config.local && !await warmLocalModel(config.local)) {
|
|
203
|
+
console.error("[cards] local model failed to load");
|
|
204
|
+
index = pending.length;
|
|
205
|
+
stoppedBy = "error";
|
|
206
|
+
}
|
|
170
207
|
while (index < pending.length) {
|
|
171
208
|
if (options.signal?.aborted) {
|
|
172
209
|
stoppedBy = "aborted";
|
|
@@ -202,14 +239,28 @@ async function summarizeRepository(root, options = {}) {
|
|
|
202
239
|
schema: outputSchema,
|
|
203
240
|
jsonSchema: CARD_GRAMMAR,
|
|
204
241
|
maxTokens: local ? LIMITS.localOutputTokens : LIMITS.outputTokensPerFile * batch.length,
|
|
205
|
-
deadlineMs: local ?
|
|
242
|
+
deadlineMs: local ? localTimeoutMs : LIMITS.timeoutMs,
|
|
206
243
|
purpose: "background",
|
|
207
244
|
repositoryRoot: root
|
|
208
245
|
});
|
|
209
246
|
let result = await ask();
|
|
210
247
|
if (!result.ok && result.reason === "invalid") result = await ask();
|
|
248
|
+
if (!result.ok && local && result.reason === "timeout") {
|
|
249
|
+
timedOut += batch.length;
|
|
250
|
+
timeoutRun++;
|
|
251
|
+
for (const { entry } of batch)
|
|
252
|
+
console.error(
|
|
253
|
+
`[cards] ${entry.path} timed out after ${Math.round(localTimeoutMs / 1e3)} s`
|
|
254
|
+
);
|
|
255
|
+
options.onProgress?.(index, pending.length);
|
|
256
|
+
if (timeoutRun >= LIMITS.localTimeoutRun) {
|
|
257
|
+
stoppedBy = "timeout";
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
211
262
|
if (!result.ok) {
|
|
212
|
-
if (result.reason === "invalid" || result.reason === "http" && batch.length === 1
|
|
263
|
+
if (result.reason === "invalid" || result.reason === "http" && batch.length === 1) {
|
|
213
264
|
skipped += batch.length;
|
|
214
265
|
continue;
|
|
215
266
|
}
|
|
@@ -217,6 +268,7 @@ async function summarizeRepository(root, options = {}) {
|
|
|
217
268
|
stoppedBy = result.reason === "budget" ? "budget" : result.reason === "cooldown" || result.reason === "rate-limited" ? "cooldown" : "error";
|
|
218
269
|
break;
|
|
219
270
|
}
|
|
271
|
+
timeoutRun = 0;
|
|
220
272
|
for (const { entry } of batch) {
|
|
221
273
|
const answer = result.value.files.find((file) => file.path === entry.path) ?? (batch.length === 1 && result.value.files.length === 1 ? result.value.files[0] : void 0);
|
|
222
274
|
if (!answer?.summary.trim()) {
|
|
@@ -242,7 +294,7 @@ async function summarizeRepository(root, options = {}) {
|
|
|
242
294
|
await pruneCardFiles(root, store, manifest);
|
|
243
295
|
if (summarized > 0)
|
|
244
296
|
await refreshZvecCardIndex(root, options.signal ? { signal: options.signal } : {});
|
|
245
|
-
return { summarized, skipped, remaining: pending.length - index, stoppedBy };
|
|
297
|
+
return { summarized, skipped, timedOut, remaining: pending.length - index, stoppedBy };
|
|
246
298
|
}
|
|
247
299
|
function toCard(entry, model, answer) {
|
|
248
300
|
const known = new Set((entry.symbolTable ?? []).map((symbol) => symbol.name));
|
|
@@ -309,7 +361,10 @@ async function cardProgress(root) {
|
|
|
309
361
|
const { manifest } = await loadFreshIndex(root);
|
|
310
362
|
const store = await readStore(root);
|
|
311
363
|
const eligible = Object.keys(manifest.entries).filter(cardEligible);
|
|
364
|
+
const lastPass = await readFile(passPath(root), "utf8").then((text) => JSON.parse(text)).catch(() => null);
|
|
312
365
|
return {
|
|
366
|
+
writer: await cardLockHolder(root),
|
|
367
|
+
lastPass,
|
|
313
368
|
carded: eligible.filter(
|
|
314
369
|
(path) => store[path]?.hash === manifest.entries[path]?.hash && store[path]?.version === SUMMARY_PROMPT_VERSION
|
|
315
370
|
).length,
|
|
@@ -355,4 +410,4 @@ export {
|
|
|
355
410
|
summarizeRepository,
|
|
356
411
|
cardProgress
|
|
357
412
|
};
|
|
358
|
-
//# sourceMappingURL=chunk-
|
|
413
|
+
//# sourceMappingURL=chunk-7JPUMXFE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/llm/summaries.ts"],"sourcesContent":["import { mkdir, readdir, readFile, rm } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { z } from \"zod\";\nimport {\n CHANGELOG_PATH,\n loadProjectArchitectureMap,\n NON_SOURCE_PATH,\n readBoundedSource,\n TEST_PATH,\n} from \"../context/discovery.js\";\nimport { writeAtomic } from \"../core/markdown-store-fs.js\";\nimport { redactSensitive, scanSensitive } from \"../core/sensitive.js\";\nimport { loadFreshIndex } from \"../indexer/engine.js\";\nimport type { IndexEntry, IndexManifest } from \"../indexer/types.js\";\nimport { CARDS_DIR, refreshZvecCardIndex } from \"../retrieval/zvec.js\";\nimport { cardLockHolder, withCardLock } from \"./card-lock.js\";\nimport { chatJson } from \"./client.js\";\nimport { type EnabledLlmConfig, resolveLlmConfig } from \"./config.js\";\nimport { waitForForeground, warmLocalModel } from \"./local.js\";\n\n/**\n * Intent cards: for each source file, a plain-English summary, the symptoms a\n * bug report would use, the code terms a search should hit, and one-line\n * descriptions of its main symbols. Written off the prompt path by the local\n * model (`provider: \"local\"`) or the BYOK model (`codebuddy index --cards`,\n * `codebuddy serve`, `watch`, `codebuddy llm summarize`) and cached by content\n * hash. Retrieval uses them three ways: a card vector per file\n * (`file-vectors.ts`), a card workspace in zvec-grep (`CARDS_DIR`), and\n * down-weighted card words in the content index. A card alone never admits a\n * file: card evidence counts when another dense signal agrees (see the\n * agreement rule in `context/discovery.ts`).\n */\nexport const SUMMARY_PROMPT_VERSION = 2;\nconst LIMITS = {\n fileChars: 200,\n symbolChars: 100,\n symptomChars: 80,\n termChars: 40,\n symptoms: 6,\n terms: 10,\n symbols: 8,\n signatures: 12,\n neighbours: 5,\n /** ~600 tokens per file: long-line (minified) files are cut, never sent whole. */\n headChars: 1_600,\n signatureChars: 160,\n batchTokens: 5_000,\n batchFiles: 8,\n outputTokensPerFile: 320,\n /** The local grammar bounds every field, so one card always fits. */\n localOutputTokens: 700,\n tokensPerMinute: 6_000,\n timeoutMs: 20_000,\n /** One file per call for the local model: a 0.6B model loses track of batches. */\n localTimeoutMs: 30_000,\n /** This many local timeouts in a row means the machine is overloaded: stop the pass. */\n localTimeoutRun: 5,\n} as const;\n\nexport type LlmSummary = {\n hash: string;\n model: string;\n version: number;\n summary: string;\n symbols: Record<string, string>;\n /** Card fields (prompt version ≥ 2). */\n symptoms?: string[];\n terms?: string[];\n};\n\n/**\n * Source files that get a card: no tests, docs, config, changelogs, vendored,\n * generated, minified or lock files (secret-bearing ones are skipped when read).\n */\nexport function cardEligible(path: string): boolean {\n return (\n !TEST_PATH.test(path) &&\n !NON_SOURCE_PATH.test(path) &&\n !CHANGELOG_PATH.test(path) &&\n !GENERATED_PATH.test(path)\n );\n}\nconst GENERATED_PATH =\n /(^|\\/)(vendor|vendored|third[_-]?party|node_modules|dist|build|generated|__generated__)\\/|\\.(min|bundle|generated|pb)\\.[a-z]+$|_pb2\\.py$|(^|\\/)[^/]*lock[^/]*$/i;\n\n/** The text embedded as a file's card vector (empty for a summary without card fields). */\nexport function cardText(card: LlmSummary): string {\n return [\n card.summary,\n ...(card.symptoms ?? []),\n ...Object.entries(card.symbols).map(([name, text]) => `${name}: ${text}`),\n ]\n .filter(Boolean)\n .join(\"\\n\");\n}\n\n/** Words the content index adds (down-weighted) for a card: terms and symptoms. */\nexport function cardWords(card: LlmSummary): string {\n return [...(card.terms ?? []), ...(card.symptoms ?? [])].join(\" \");\n}\n\ntype Store = Record<string, LlmSummary>;\nconst storePath = (root: string) => join(root, \".codebuddy\", \"cache\", \"llm-summaries.json\");\n\nasync function readStore(root: string): Promise<Store> {\n try {\n return JSON.parse(await readFile(storePath(root), \"utf8\")) as Store;\n } catch {\n return {};\n }\n}\n\nasync function writeStore(root: string, store: Store): Promise<void> {\n await mkdir(join(root, \".codebuddy\", \"cache\"), { recursive: true });\n await writeAtomic(storePath(root), `${JSON.stringify(store)}\\n`);\n}\n\n/** Summaries whose content hash still matches the current index. */\nexport async function loadLlmSummaries(\n root: string,\n manifest: IndexManifest,\n): Promise<Map<string, LlmSummary>> {\n const store = await readStore(root);\n return new Map(\n Object.entries(store).filter(([path, entry]) => manifest.entries[path]?.hash === entry.hash),\n );\n}\n\nconst outputSchema = z.object({\n files: z.array(\n z.object({\n path: z.string(),\n summary: z.string(),\n symptoms: z.array(z.string()).default([]),\n terms: z.array(z.string()).default([]),\n symbols: z.array(z.object({ name: z.string(), summary: z.string() })).default([]),\n }),\n ),\n});\n\n/**\n * Grammar bounds for the local model: without them a 0.6B model writes\n * paragraph summaries and endless arrays until it hits the token cap and the\n * JSON is cut off. Zod still validates (and trims) every answer.\n */\nconst boundedString = (maxLength: number) => ({ type: \"string\", maxLength });\nconst CARD_GRAMMAR = {\n type: \"object\",\n properties: {\n files: {\n type: \"array\",\n minItems: 1,\n maxItems: 1,\n items: {\n type: \"object\",\n properties: {\n path: boundedString(300),\n summary: boundedString(LIMITS.fileChars),\n symptoms: {\n type: \"array\",\n items: boundedString(LIMITS.symptomChars),\n maxItems: LIMITS.symptoms,\n },\n terms: { type: \"array\", items: boundedString(LIMITS.termChars), maxItems: LIMITS.terms },\n symbols: {\n type: \"array\",\n maxItems: LIMITS.symbols,\n items: {\n type: \"object\",\n properties: { name: boundedString(80), summary: boundedString(LIMITS.symbolChars) },\n },\n },\n },\n },\n },\n },\n};\n\nconst SYSTEM = [\n \"You write search cards for source files so that a plain-English bug report or task finds the right file.\",\n 'Answer with JSON only: {\"files\":[{\"path\":\"...\",\"summary\":\"...\",\"symptoms\":[\"...\"],\"terms\":[\"...\"],\"symbols\":[{\"name\":\"...\",\"summary\":\"...\"}]}]}.',\n \"For each input file:\",\n `summary: one plain-English sentence (max ${LIMITS.fileChars} chars) saying what the file does;`,\n `symptoms: 3 to ${LIMITS.symptoms} short phrases a user or bug report would use to describe a problem caused by this file, in everyday words without code identifiers;`,\n `terms: 5 to ${LIMITS.terms} code identifiers and domain words from the file that a search should match;`,\n `symbols: up to ${LIMITS.symbols} main symbols (only names listed in \"signatures\"), each with one short phrase.`,\n \"Symptoms describe what goes wrong for a user of this specific file (a wrong value, an error, nothing happening), never generic phrases.\",\n \"Never invent paths or symbol names.\",\n].join(\" \");\n\nexport type SummarizeResult = {\n summarized: number;\n skipped: number;\n /** Local files that ran past the deadline (counted apart from `skipped`). */\n timedOut: number;\n remaining: number;\n stoppedBy:\n | \"done\"\n | \"limit\"\n | \"budget\"\n | \"cooldown\"\n | \"disabled\"\n | \"aborted\"\n | \"error\"\n | \"timeout\"\n | \"locked\";\n /** Set with `stoppedBy: \"locked\"`: the PID already writing cards (null if unknown). */\n lockedBy?: number | null;\n};\n\n/** The last finished pass, for `codebuddy models status`. */\nexport type CardPassRecord = Omit<SummarizeResult, \"lockedBy\"> & { at: string };\nconst passPath = (root: string) => join(root, \".codebuddy\", \"cache\", \"cards-pass.json\");\n\nexport async function summarizeRepository(\n root: string,\n options: {\n limit?: number;\n onProgress?: (done: number, total: number) => void;\n signal?: AbortSignal;\n /** Injected config (tests, CLI); defaults to `resolveLlmConfig`. */\n config?: EnabledLlmConfig;\n /** Wait between batches to stay under the free tier's tokens/minute. */\n pace?: boolean;\n batchFiles?: number;\n /** Per-file deadline for the local model (tests). */\n localTimeoutMs?: number;\n } = {},\n): Promise<SummarizeResult> {\n const resolved = options.config ?? (await resolveLlmConfig(root));\n if (!resolved.enabled || !resolved.features.summaries) {\n return { summarized: 0, skipped: 0, timedOut: 0, remaining: 0, stoppedBy: \"disabled\" };\n }\n const config = resolved;\n const locked = await withCardLock(root, async () => {\n const result = await summarizeLocked(root, config, options);\n const record: CardPassRecord = { ...result, at: new Date().toISOString() };\n await writeAtomic(passPath(root), `${JSON.stringify(record)}\\n`).catch(() => {});\n return result;\n });\n if (locked.ok) return locked.value;\n console.error(`[cards] already being written by pid ${locked.holder ?? \"unknown\"}`);\n return {\n summarized: 0,\n skipped: 0,\n timedOut: 0,\n remaining: 0,\n stoppedBy: \"locked\",\n lockedBy: locked.holder,\n };\n}\n\nasync function summarizeLocked(\n root: string,\n config: EnabledLlmConfig,\n options: NonNullable<Parameters<typeof summarizeRepository>[1]>,\n): Promise<SummarizeResult> {\n const { manifest } = await loadFreshIndex(root);\n const store = await readStore(root);\n for (const path of Object.keys(store)) if (!manifest.entries[path]) delete store[path];\n\n const map = await loadProjectArchitectureMap(root, manifest).catch(() => null);\n const pending = orderByImportance(\n map,\n Object.values(manifest.entries).filter((entry) => {\n if (!cardEligible(entry.path)) return false;\n const done = store[entry.path];\n return !(\n done?.hash === entry.hash &&\n done.model === config.model &&\n done.version === SUMMARY_PROMPT_VERSION\n );\n }),\n );\n const local = config.provider === \"local\";\n const batchFiles = local ? 1 : (options.batchFiles ?? LIMITS.batchFiles);\n const limit = options.limit ?? Number.POSITIVE_INFINITY;\n const localTimeoutMs = options.localTimeoutMs ?? LIMITS.localTimeoutMs;\n let summarized = 0;\n let skipped = 0;\n let timedOut = 0;\n let timeoutRun = 0;\n let index = 0;\n let stoppedBy: SummarizeResult[\"stoppedBy\"] = \"done\";\n if (pending.length > 0) options.onProgress?.(0, pending.length);\n // Load once, outside the per-file deadline: a cold load under memory\n // pressure must not time out (and skip) the first files.\n if (local && pending.length > 0 && config.local && !(await warmLocalModel(config.local))) {\n console.error(\"[cards] local model failed to load\");\n index = pending.length;\n stoppedBy = \"error\";\n }\n\n while (index < pending.length) {\n if (options.signal?.aborted) {\n stoppedBy = \"aborted\";\n break;\n }\n if (summarized >= limit) {\n stoppedBy = \"limit\";\n break;\n }\n const batch: Array<{ entry: IndexEntry; input: FileInput }> = [];\n let tokens = 0;\n while (index < pending.length && batch.length < batchFiles) {\n if (summarized + batch.length >= limit) break;\n const entry = pending[index] as IndexEntry;\n const input = await fileInput(root, entry, map);\n if (input === null) {\n skipped++;\n index++;\n continue;\n }\n const cost = Math.ceil(JSON.stringify(input).length / 4);\n if (batch.length > 0 && tokens + cost > LIMITS.batchTokens - 600) break;\n batch.push({ entry, input });\n tokens += cost;\n index++;\n }\n if (batch.length === 0) continue;\n\n // A prompt hook or tool call is running: let it have the machine first.\n if (local) await waitForForeground(root, options.signal);\n const started = Date.now();\n const ask = () =>\n chatJson(config, {\n system: SYSTEM,\n user: JSON.stringify({ files: batch.map(({ input }) => input) }),\n schema: outputSchema,\n jsonSchema: CARD_GRAMMAR,\n maxTokens: local ? LIMITS.localOutputTokens : LIMITS.outputTokensPerFile * batch.length,\n deadlineMs: local ? localTimeoutMs : LIMITS.timeoutMs,\n purpose: \"background\",\n repositoryRoot: root,\n });\n let result = await ask();\n // Invalid output gets one retry, then the file is skipped.\n if (!result.ok && result.reason === \"invalid\") result = await ask();\n if (!result.ok && local && result.reason === \"timeout\") {\n // One slow file is not an outage, but a run of them is an overloaded\n // machine: say so and stop instead of burning hours writing nothing.\n timedOut += batch.length;\n timeoutRun++;\n for (const { entry } of batch)\n console.error(\n `[cards] ${entry.path} timed out after ${Math.round(localTimeoutMs / 1000)} s`,\n );\n options.onProgress?.(index, pending.length);\n if (timeoutRun >= LIMITS.localTimeoutRun) {\n stoppedBy = \"timeout\";\n break;\n }\n continue;\n }\n if (!result.ok) {\n // A batch the provider rejects outright (bad output, or e.g. 413 for one\n // file) is skipped, not retried forever; limits and outages stop the run.\n if (result.reason === \"invalid\" || (result.reason === \"http\" && batch.length === 1)) {\n skipped += batch.length;\n continue;\n }\n index -= batch.length;\n stoppedBy =\n result.reason === \"budget\"\n ? \"budget\"\n : result.reason === \"cooldown\" || result.reason === \"rate-limited\"\n ? \"cooldown\"\n : \"error\";\n break;\n }\n timeoutRun = 0;\n for (const { entry } of batch) {\n // A one-file batch (the local model) may echo the path imperfectly.\n const answer =\n result.value.files.find((file) => file.path === entry.path) ??\n (batch.length === 1 && result.value.files.length === 1 ? result.value.files[0] : undefined);\n if (!answer?.summary.trim()) {\n skipped++;\n continue;\n }\n store[entry.path] = toCard(entry, config.model, answer);\n summarized++;\n }\n await writeStore(root, store);\n await writeCardFiles(\n root,\n store,\n batch.map(({ entry }) => entry.path),\n );\n options.onProgress?.(index, pending.length);\n if (options.pace !== false) {\n const wait = (result.tokens / LIMITS.tokensPerMinute) * 60_000 - (Date.now() - started);\n if (wait > 0) await new Promise((resolve) => setTimeout(resolve, wait));\n }\n }\n await writeStore(root, store);\n await pruneCardFiles(root, store, manifest);\n if (summarized > 0)\n await refreshZvecCardIndex(root, options.signal ? { signal: options.signal } : {});\n return { summarized, skipped, timedOut, remaining: pending.length - index, stoppedBy };\n}\n\ntype CardAnswer = z.infer<typeof outputSchema>[\"files\"][number];\n\n/** Bounded, de-duplicated card fields; symbols only from the file's symbol table. */\nfunction toCard(entry: IndexEntry, model: string, answer: CardAnswer): LlmSummary {\n const known = new Set((entry.symbolTable ?? []).map((symbol) => symbol.name));\n const clean = (items: string[], count: number, chars: number) =>\n [\n ...new Set(items.map((item) => redactSensitive(item.trim()).slice(0, chars)).filter(Boolean)),\n ].slice(0, count);\n return {\n hash: entry.hash,\n model,\n version: SUMMARY_PROMPT_VERSION,\n summary: redactSensitive(answer.summary.trim()).slice(0, LIMITS.fileChars),\n symptoms: clean(answer.symptoms, LIMITS.symptoms, LIMITS.symptomChars),\n terms: clean(answer.terms, LIMITS.terms, LIMITS.termChars),\n symbols: Object.fromEntries(\n answer.symbols\n .filter((symbol) => known.has(symbol.name))\n .slice(0, LIMITS.symbols)\n .map((symbol) => [symbol.name, symbol.summary.trim().slice(0, LIMITS.symbolChars)]),\n ),\n };\n}\n\n/** One Markdown card per source file for the zvec-grep card workspace. */\nasync function writeCardFiles(root: string, store: Store, paths: string[]): Promise<void> {\n for (const path of paths) {\n const card = store[path];\n if (!card) continue;\n const target = join(root, CARDS_DIR, `${path}.md`);\n await mkdir(dirname(target), { recursive: true });\n await writeAtomic(\n target,\n [\n \"---\",\n `source: ${JSON.stringify(path)}`,\n `hash: ${card.hash}`,\n \"---\",\n \"\",\n `# ${path}`,\n \"\",\n card.summary,\n \"\",\n ...(card.symptoms?.length ? [\"Symptoms:\", ...card.symptoms.map((s) => `- ${s}`), \"\"] : []),\n ...(card.terms?.length ? [`Terms: ${card.terms.join(\", \")}`, \"\"] : []),\n ...Object.entries(card.symbols).map(([name, text]) => `- ${name}: ${text}`),\n \"\",\n ].join(\"\\n\"),\n );\n }\n}\n\n/** Write missing cards and delete cards whose source is gone or no longer current. */\nasync function pruneCardFiles(root: string, store: Store, manifest: IndexManifest): Promise<void> {\n const directory = join(root, CARDS_DIR);\n const current = new Set(\n Object.entries(store)\n .filter(([path, card]) => manifest.entries[path]?.hash === card.hash && card.symptoms)\n .map(([path]) => `${path}.md`),\n );\n const existing = await readdir(directory, { recursive: true }).catch(() => [] as string[]);\n const present = new Set(existing.map((name) => name.split(\"\\\\\").join(\"/\")));\n for (const name of present) {\n if (name.endsWith(\".md\") && !name.startsWith(\".zvec-grep\") && !current.has(name)) {\n await rm(join(directory, name), { force: true });\n }\n }\n await writeCardFiles(\n root,\n store,\n [...current].filter((name) => !present.has(name)).map((name) => name.slice(0, -3)),\n );\n}\n\n/**\n * Card coverage for `stats`/`storage`/`models`: current cards vs eligible\n * source files, the live writer's PID and the last finished pass.\n */\nexport async function cardProgress(root: string): Promise<{\n carded: number;\n eligible: number;\n writer: number | null;\n lastPass: CardPassRecord | null;\n}> {\n const { manifest } = await loadFreshIndex(root);\n const store = await readStore(root);\n const eligible = Object.keys(manifest.entries).filter(cardEligible);\n const lastPass = await readFile(passPath(root), \"utf8\")\n .then((text) => JSON.parse(text) as CardPassRecord)\n .catch(() => null);\n return {\n writer: await cardLockHolder(root),\n lastPass,\n carded: eligible.filter(\n (path) =>\n store[path]?.hash === manifest.entries[path]?.hash &&\n store[path]?.version === SUMMARY_PROMPT_VERSION,\n ).length,\n eligible: eligible.length,\n };\n}\n\ntype FileInput = {\n path: string;\n language: string;\n signatures: string[];\n /** Files this one imports and files that import it (paths only). */\n neighbours: string[];\n head: string;\n};\n\ntype ImportMap = Awaited<ReturnType<typeof loadProjectArchitectureMap>> | null;\n\n/** Import lines say little about what a file does; the card reads past them. */\nconst IMPORT_LINE =\n /^\\s*(import\\b|from\\s+\\S+\\s+import\\b|export\\s+(\\*|\\{[^}]*\\})\\s+from\\b|(const|let|var)\\s+\\S+\\s*=\\s*require\\()|^\\s*$/;\n\n/** What is sent for one file; null for unreadable or secret-bearing files. */\nasync function fileInput(\n root: string,\n entry: IndexEntry,\n map: ImportMap,\n): Promise<FileInput | null> {\n const source = await readBoundedSource(root, entry.path);\n if (source === null || scanSensitive(source).sensitive) return null;\n const edges = map?.edges ?? [];\n return {\n path: entry.path,\n language: entry.language,\n // Not the deterministic summary: small models copy its \"ts • 80 lines\" form.\n signatures: (entry.symbolTable ?? [])\n .filter((symbol) => symbol.kind !== \"heading\")\n .slice(0, LIMITS.signatures)\n .map((symbol) => redactSensitive(symbol.signature.slice(0, LIMITS.signatureChars))),\n neighbours: [\n ...new Set([\n ...edges.filter((edge) => edge.from === entry.path).map((edge) => edge.to),\n ...edges.filter((edge) => edge.to === entry.path).map((edge) => edge.from),\n ]),\n ].slice(0, LIMITS.neighbours),\n // The module docstring and first definitions, never the whole file.\n head: redactSensitive(\n source\n .split(/\\r?\\n/)\n .filter((line) => !IMPORT_LINE.test(line))\n .join(\"\\n\")\n .slice(0, LIMITS.headChars),\n ),\n };\n}\n\n/** Most-imported source first. */\nfunction orderByImportance(map: ImportMap, entries: IndexEntry[]): IndexEntry[] {\n const fanIn = new Map<string, number>();\n for (const edge of map?.edges ?? []) fanIn.set(edge.to, (fanIn.get(edge.to) ?? 0) + 1);\n return [...entries].sort(\n (left, right) =>\n (fanIn.get(right.path) ?? 0) - (fanIn.get(left.path) ?? 0) ||\n left.path.localeCompare(right.path),\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
loadProjectArchitectureMap,
|
|
19
19
|
readBoundedSource,
|
|
20
20
|
taskTerms
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-4DTO2E4Y.js";
|
|
22
22
|
import {
|
|
23
23
|
loadKnownSecretValues,
|
|
24
24
|
redactSourceText
|
|
@@ -45,7 +45,7 @@ async function runLeanHook(input, emit, budgetMs = LEAN_HOOK_BUDGET_MS) {
|
|
|
45
45
|
if (prompt === "" || prompt.startsWith("/")) return "";
|
|
46
46
|
const sessionId = input.session_id ?? "default";
|
|
47
47
|
try {
|
|
48
|
-
void import("./local-
|
|
48
|
+
void import("./local-TH5XCLVJ.js").then(({ markForeground }) => markForeground(repositoryRoot));
|
|
49
49
|
const { manifest } = await loadFreshIndex(repositoryRoot);
|
|
50
50
|
const renderBy = started + budgetMs - RENDER_RESERVE_MS;
|
|
51
51
|
const discovery = await discoverTaskPaths(
|
|
@@ -187,4 +187,4 @@ export {
|
|
|
187
187
|
renderLeanBlock,
|
|
188
188
|
isReadOnlyPrompt
|
|
189
189
|
};
|
|
190
|
-
//# sourceMappingURL=chunk-
|
|
190
|
+
//# sourceMappingURL=chunk-AFXWCSQ4.js.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
chatJson
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-MNSMW4MZ.js";
|
|
4
4
|
import {
|
|
5
5
|
MemoryFileStore,
|
|
6
6
|
termOverlap
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-VBDM7HC5.js";
|
|
8
8
|
import {
|
|
9
9
|
generateEntityId
|
|
10
10
|
} from "./chunk-EMWGLKPR.js";
|
|
@@ -14,16 +14,16 @@ import {
|
|
|
14
14
|
} from "./chunk-AL6VBTNA.js";
|
|
15
15
|
import {
|
|
16
16
|
resolveLlmConfig
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-RGIVHKBH.js";
|
|
18
18
|
import {
|
|
19
19
|
resolveNamespaceFrom
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-Y4QQML24.js";
|
|
21
21
|
import {
|
|
22
22
|
writeAtomic
|
|
23
23
|
} from "./chunk-CG5B3LYG.js";
|
|
24
24
|
import {
|
|
25
25
|
waitForForeground
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-NNJSAYHY.js";
|
|
27
27
|
|
|
28
28
|
// src/llm/index-facts.ts
|
|
29
29
|
import { createHash } from "crypto";
|
|
@@ -365,4 +365,4 @@ export {
|
|
|
365
365
|
isStatement,
|
|
366
366
|
hasLineEvidence
|
|
367
367
|
};
|
|
368
|
-
//# sourceMappingURL=chunk-
|
|
368
|
+
//# sourceMappingURL=chunk-BNZDEU5K.js.map
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
rankSemanticReuse,
|
|
12
12
|
readPolicyFile,
|
|
13
13
|
resolveRiskPaths
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-OO7Q7XZF.js";
|
|
15
15
|
import {
|
|
16
16
|
readTraces,
|
|
17
17
|
recordTrace
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
import {
|
|
26
26
|
ensureZvecServer,
|
|
27
27
|
refreshZvecIndex
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-RKF4HBBQ.js";
|
|
29
29
|
import {
|
|
30
30
|
NON_SOURCE_PATH,
|
|
31
31
|
TEST_PATH,
|
|
@@ -37,16 +37,16 @@ import {
|
|
|
37
37
|
neighbours,
|
|
38
38
|
queryMap,
|
|
39
39
|
taskTerms
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-4DTO2E4Y.js";
|
|
41
41
|
import {
|
|
42
42
|
codeBuddyConfigSchema,
|
|
43
43
|
createRuntime,
|
|
44
44
|
listFactsPage
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-IM5RFEW4.js";
|
|
46
46
|
import {
|
|
47
47
|
MemoryFileStore,
|
|
48
48
|
VERSION
|
|
49
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-VBDM7HC5.js";
|
|
50
50
|
import {
|
|
51
51
|
redactPersisted
|
|
52
52
|
} from "./chunk-AL6VBTNA.js";
|
|
@@ -55,10 +55,10 @@ import {
|
|
|
55
55
|
loadPlanPolicy,
|
|
56
56
|
loadRuntimeConfig,
|
|
57
57
|
redactSecrets
|
|
58
|
-
} from "./chunk-
|
|
58
|
+
} from "./chunk-Y4QQML24.js";
|
|
59
59
|
import {
|
|
60
60
|
markForeground
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-NNJSAYHY.js";
|
|
62
62
|
|
|
63
63
|
// src/core/migrate-to-files.ts
|
|
64
64
|
import { createHash } from "crypto";
|
|
@@ -311,7 +311,7 @@ async function traceCliCommand(command, started, root = process.cwd()) {
|
|
|
311
311
|
}
|
|
312
312
|
async function installedClients(repositoryRoot) {
|
|
313
313
|
const { CLIENT_IDS } = await import("./adapters-OJRSVFV3.js");
|
|
314
|
-
const { detectClient } = await import("./install-
|
|
314
|
+
const { detectClient } = await import("./install-MCJIJMFF.js");
|
|
315
315
|
const found = [];
|
|
316
316
|
for (const id of CLIENT_IDS) {
|
|
317
317
|
const status = await detectClient(id, repositoryRoot).catch(() => null);
|
|
@@ -1626,7 +1626,7 @@ function startProjectSync(options) {
|
|
|
1626
1626
|
if (result.status !== "disabled") logger.info(`[sync] ${result.reason}`);
|
|
1627
1627
|
});
|
|
1628
1628
|
const modelPass = backgroundRefresh(async () => {
|
|
1629
|
-
const { runIndexModelPass } = await import("./index-pass-
|
|
1629
|
+
const { runIndexModelPass } = await import("./index-pass-RP23G2CR.js");
|
|
1630
1630
|
const result = await runIndexModelPass(repositoryRoot, { signal: controller.signal }).catch(
|
|
1631
1631
|
(error) => {
|
|
1632
1632
|
logger.error(`[sync] index model pass failed: ${error.message}`);
|
|
@@ -1635,7 +1635,7 @@ function startProjectSync(options) {
|
|
|
1635
1635
|
);
|
|
1636
1636
|
if (result?.cards && (result.cards.summarized > 0 || result.cards.remaining > 0)) {
|
|
1637
1637
|
logger.info(
|
|
1638
|
-
`[sync] intent cards: ${result.cards.summarized} written, ${result.cards.remaining} left (${result.cards.stoppedBy})`
|
|
1638
|
+
`[sync] intent cards: ${result.cards.summarized} written, ${result.cards.timedOut} timed out, ${result.cards.remaining} left (${result.cards.stoppedBy})`
|
|
1639
1639
|
);
|
|
1640
1640
|
}
|
|
1641
1641
|
if (result?.facts && result.facts.written + result.facts.removed > 0) {
|
|
@@ -1725,7 +1725,7 @@ function startProjectSync(options) {
|
|
|
1725
1725
|
await Promise.all([graphRefresh.settled(), zvecRefresh.settled(), modelPass.settled()]);
|
|
1726
1726
|
await stopZvecServer().catch(() => {
|
|
1727
1727
|
});
|
|
1728
|
-
await import("./local-
|
|
1728
|
+
await import("./local-TH5XCLVJ.js").then(({ unloadLocalModel }) => unloadLocalModel()).catch(() => {
|
|
1729
1729
|
});
|
|
1730
1730
|
}
|
|
1731
1731
|
};
|
|
@@ -1752,7 +1752,7 @@ async function resolveInitializedProjectRoot(configuredRoot, cwd = process.cwd()
|
|
|
1752
1752
|
);
|
|
1753
1753
|
if (!initialized) {
|
|
1754
1754
|
throw new Error(
|
|
1755
|
-
`CodeBuddy will not scan ${root}: run codebuddy
|
|
1755
|
+
`CodeBuddy will not scan ${root}: run codebuddy (or codebuddy init) in your project folder first. For a desktop client, re-register CodeBuddy from that folder to save its project path.`
|
|
1756
1756
|
);
|
|
1757
1757
|
}
|
|
1758
1758
|
return root;
|
|
@@ -1919,7 +1919,7 @@ async function startMcpServer(options = {}) {
|
|
|
1919
1919
|
}
|
|
1920
1920
|
async function startSetupServer(problem) {
|
|
1921
1921
|
const fix = [
|
|
1922
|
-
"Open your project folder in a terminal and run `codebuddy
|
|
1922
|
+
"Open your project folder in a terminal and run `codebuddy` (or `codebuddy init`) if it has no .codebuddy/config.json.",
|
|
1923
1923
|
"Then register CodeBuddy from that folder so the client starts it there: `codebuddy claude install` (Claude Desktop) or `codebuddy codex install` (Codex), and restart the client.",
|
|
1924
1924
|
"Or set CODEBUDDY_PROJECT_ROOT to the project's absolute path in this server's env."
|
|
1925
1925
|
];
|
|
@@ -2016,4 +2016,4 @@ export {
|
|
|
2016
2016
|
registerCodeBuddyTools,
|
|
2017
2017
|
startMcpServer
|
|
2018
2018
|
};
|
|
2019
|
-
//# sourceMappingURL=chunk-
|
|
2019
|
+
//# sourceMappingURL=chunk-GNV4X67F.js.map
|