@orquentra/codebuddy 2.15.0 → 2.15.3
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 +12 -7
- package/README.md +2 -2
- package/dist/{chunk-YMXFSHXT.js → chunk-3L5IR6OW.js} +2 -2
- package/dist/{chunk-YMXFSHXT.js.map → chunk-3L5IR6OW.js.map} +1 -1
- package/dist/{chunk-JJQDVGF7.js → chunk-3TZNQJTH.js} +3 -3
- package/dist/{chunk-4BSO3HST.js → chunk-4LMBZWIF.js} +48 -13
- package/dist/chunk-4LMBZWIF.js.map +1 -0
- package/dist/{chunk-WHKXUXHE.js → chunk-4TKKLQLH.js} +5 -5
- package/dist/chunk-4TKKLQLH.js.map +1 -0
- package/dist/{chunk-KQCP3ZYH.js → chunk-5ZLNHYJH.js} +31 -15
- package/dist/chunk-5ZLNHYJH.js.map +1 -0
- package/dist/{chunk-U7KDM62P.js → chunk-6KSSJJZO.js} +17 -9
- package/dist/chunk-6KSSJJZO.js.map +1 -0
- package/dist/{chunk-6YI2D5F5.js → chunk-7IXPARDM.js} +3 -3
- package/dist/{chunk-UTPC66MV.js → chunk-FEFOZ2ZW.js} +23 -4
- package/dist/chunk-FEFOZ2ZW.js.map +1 -0
- package/dist/{chunk-ITXE27SZ.js → chunk-FGJ2KPPN.js} +104 -22
- package/dist/chunk-FGJ2KPPN.js.map +1 -0
- package/dist/{chunk-D5E7SGI5.js → chunk-KGDRGA2L.js} +14 -14
- package/dist/{chunk-D5E7SGI5.js.map → chunk-KGDRGA2L.js.map} +1 -1
- package/dist/{chunk-UR3DF74I.js → chunk-LZTYPJ5I.js} +36 -18
- package/dist/chunk-LZTYPJ5I.js.map +1 -0
- package/dist/{chunk-UATJCD2T.js → chunk-R5E2YGLE.js} +14 -8
- package/dist/chunk-R5E2YGLE.js.map +1 -0
- package/dist/chunk-R5JRKMEP.js +55 -0
- package/dist/chunk-R5JRKMEP.js.map +1 -0
- package/dist/{chunk-RZ266NHE.js → chunk-SPQU2422.js} +5 -5
- package/dist/{chunk-RZ266NHE.js.map → chunk-SPQU2422.js.map} +1 -1
- package/dist/{chunk-BTTQWSDS.js → chunk-SXVH74NE.js} +36 -6
- package/dist/chunk-SXVH74NE.js.map +1 -0
- package/dist/{chunk-KYRM3HPP.js → chunk-TIMS3PB7.js} +3 -3
- 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-32X53LWY.js} +4 -4
- package/dist/{expand-KWYGNCKZ.js → expand-VFYOWNZS.js} +5 -5
- package/dist/{file-vectors-MKXTSGGH.js → file-vectors-UBOKGYXX.js} +4 -3
- package/dist/{index-facts-4MTEHKMN.js → index-facts-ZTUANWBF.js} +7 -7
- package/dist/index-pass-S6NC25P5.js +15 -0
- package/dist/index.js +9 -8
- package/dist/{install-E3JWINTN.js → install-ZTE3H7QM.js} +8 -7
- package/dist/{lean-MLTAFS5L.js → lean-5N6BOONS.js} +5 -4
- package/dist/{local-75DJECVR.js → local-5EYLEBEP.js} +4 -2
- package/dist/{local-llm-MECVJTVU.js → local-llm-ESMVP5LE.js} +4 -4
- package/dist/{main-RLMKQZW4.js → main-B3HZCGVE.js} +61 -50
- package/dist/main-B3HZCGVE.js.map +1 -0
- package/dist/{memory-PFFVMTCH.js → memory-CI7HIGHN.js} +5 -5
- package/dist/{operations-STR7S5UO.js → operations-MW52HIQ3.js} +4 -4
- package/dist/{rerank-XXKWDI5Q.js → rerank-O4S5BC45.js} +5 -5
- package/dist/{runtime-SWFWGTQ4.js → runtime-SZAWIXEB.js} +6 -5
- package/dist/{summaries-QXWFFDUZ.js → summaries-GCHZGIIA.js} +11 -8
- package/dist/{zvec-F73IBWO5.js → zvec-V34P24WF.js} +11 -6
- package/package.json +1 -1
- package/dist/chunk-4BSO3HST.js.map +0 -1
- package/dist/chunk-BTTQWSDS.js.map +0 -1
- package/dist/chunk-ITXE27SZ.js.map +0 -1
- package/dist/chunk-KQCP3ZYH.js.map +0 -1
- package/dist/chunk-U7KDM62P.js.map +0 -1
- package/dist/chunk-UATJCD2T.js.map +0 -1
- package/dist/chunk-UR3DF74I.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-JJQDVGF7.js.map → chunk-3TZNQJTH.js.map} +0 -0
- /package/dist/{chunk-6YI2D5F5.js.map → chunk-7IXPARDM.js.map} +0 -0
- /package/dist/{chunk-KYRM3HPP.js.map → chunk-TIMS3PB7.js.map} +0 -0
- /package/dist/{config-TXQJCCVI.js.map → config-32X53LWY.js.map} +0 -0
- /package/dist/{expand-KWYGNCKZ.js.map → expand-VFYOWNZS.js.map} +0 -0
- /package/dist/{file-vectors-MKXTSGGH.js.map → file-vectors-UBOKGYXX.js.map} +0 -0
- /package/dist/{index-facts-4MTEHKMN.js.map → index-facts-ZTUANWBF.js.map} +0 -0
- /package/dist/{index-pass-TBDUEZCB.js.map → index-pass-S6NC25P5.js.map} +0 -0
- /package/dist/{install-E3JWINTN.js.map → install-ZTE3H7QM.js.map} +0 -0
- /package/dist/{lean-MLTAFS5L.js.map → lean-5N6BOONS.js.map} +0 -0
- /package/dist/{local-75DJECVR.js.map → local-5EYLEBEP.js.map} +0 -0
- /package/dist/{local-llm-MECVJTVU.js.map → local-llm-ESMVP5LE.js.map} +0 -0
- /package/dist/{memory-PFFVMTCH.js.map → memory-CI7HIGHN.js.map} +0 -0
- /package/dist/{operations-STR7S5UO.js.map → operations-MW52HIQ3.js.map} +0 -0
- /package/dist/{rerank-XXKWDI5Q.js.map → rerank-O4S5BC45.js.map} +0 -0
- /package/dist/{runtime-SWFWGTQ4.js.map → runtime-SZAWIXEB.js.map} +0 -0
- /package/dist/{summaries-QXWFFDUZ.js.map → summaries-GCHZGIIA.js.map} +0 -0
- /package/dist/{zvec-F73IBWO5.js.map → zvec-V34P24WF.js.map} +0 -0
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
withCardLock
|
|
3
|
+
} from "./chunk-R5JRKMEP.js";
|
|
1
4
|
import {
|
|
2
5
|
resolveLlmConfig
|
|
3
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-4TKKLQLH.js";
|
|
4
7
|
import {
|
|
5
8
|
loadRuntimeConfig
|
|
6
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-Y4QQML24.js";
|
|
7
10
|
|
|
8
11
|
// src/llm/index-pass.ts
|
|
9
12
|
var VECTOR_BUDGET_MS = 5 * 6e4;
|
|
@@ -11,28 +14,33 @@ async function runIndexModelPass(root, options = {}) {
|
|
|
11
14
|
const config = await resolveLlmConfig(root);
|
|
12
15
|
if (!config.enabled) return { cards: null, facts: null, cardVectors: 0 };
|
|
13
16
|
const signal = options.signal ? { signal: options.signal } : {};
|
|
14
|
-
const { summarizeRepository } = await import("./summaries-
|
|
17
|
+
const { summarizeRepository } = await import("./summaries-GCHZGIIA.js");
|
|
15
18
|
const cards = config.features.summaries ? await summarizeRepository(root, {
|
|
16
19
|
config,
|
|
17
20
|
pace: config.provider !== "local",
|
|
18
21
|
...signal,
|
|
19
22
|
...options.onProgress ? { onProgress: options.onProgress } : {}
|
|
20
23
|
}) : null;
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
if (cards?.stoppedBy === "locked")
|
|
25
|
+
return { cards: { ...cards, lockedBy: cards.lockedBy ?? null }, facts: null, cardVectors: 0 };
|
|
26
|
+
const { extractIndexFacts } = await import("./index-facts-ZTUANWBF.js");
|
|
27
|
+
const locked = await withCardLock(root, () => extractIndexFacts(root, { config, ...signal }));
|
|
28
|
+
const facts = locked.ok ? locked.value : null;
|
|
23
29
|
let cardVectors = 0;
|
|
24
30
|
const local = (await loadRuntimeConfig(root, { requireDatabase: false }).catch(() => null))?.provider.type === "local";
|
|
25
31
|
if (local && cards && cards.summarized > 0 && !options.signal?.aborted) {
|
|
26
|
-
const { syncFileVectors } = await import("./file-vectors-
|
|
32
|
+
const { syncFileVectors } = await import("./file-vectors-UBOKGYXX.js");
|
|
27
33
|
cardVectors = (await syncFileVectors(root, VECTOR_BUDGET_MS))?.cards ?? 0;
|
|
28
34
|
}
|
|
29
35
|
return {
|
|
30
36
|
cards: cards && {
|
|
31
37
|
summarized: cards.summarized,
|
|
38
|
+
timedOut: cards.timedOut,
|
|
32
39
|
remaining: cards.remaining,
|
|
33
|
-
stoppedBy: cards.stoppedBy
|
|
40
|
+
stoppedBy: cards.stoppedBy,
|
|
41
|
+
...cards.reason ? { reason: cards.reason } : {}
|
|
34
42
|
},
|
|
35
|
-
facts: facts.stoppedBy === "disabled" ? null : { written: facts.written, removed: facts.removed },
|
|
43
|
+
facts: !facts || facts.stoppedBy === "disabled" ? null : { written: facts.written, removed: facts.removed },
|
|
36
44
|
cardVectors
|
|
37
45
|
};
|
|
38
46
|
}
|
|
@@ -40,4 +48,4 @@ async function runIndexModelPass(root, options = {}) {
|
|
|
40
48
|
export {
|
|
41
49
|
runIndexModelPass
|
|
42
50
|
};
|
|
43
|
-
//# sourceMappingURL=chunk-
|
|
51
|
+
//# sourceMappingURL=chunk-6KSSJJZO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/llm/index-pass.ts"],"sourcesContent":["import { loadRuntimeConfig } from \"../core/config-file.js\";\nimport { withCardLock } from \"./card-lock.js\";\nimport { resolveLlmConfig } from \"./config.js\";\n\nexport type IndexModelPass = {\n cards: {\n summarized: number;\n timedOut: number;\n remaining: number;\n stoppedBy: string;\n lockedBy?: number | null;\n reason?: string;\n } | null;\n facts: { written: number; removed: number } | null;\n cardVectors: number;\n};\n\n/** One file vector budget per pass: card vectors are one short text per file. */\nconst VECTOR_BUDGET_MS = 5 * 60_000;\n\n/**\n * The model-backed part of indexing, always off the request path: intent\n * cards for new or changed source files, index facts for changed sources,\n * then the card vectors. Resumable (everything is cached by content hash) and\n * a no-op returning nulls when no model layer is enabled. One writer per\n * repository: while another process holds the card lock this pass does nothing.\n */\nexport async function runIndexModelPass(\n root: string,\n options: { signal?: AbortSignal; onProgress?: (done: number, total: number) => void } = {},\n): Promise<IndexModelPass> {\n const config = await resolveLlmConfig(root);\n if (!config.enabled) return { cards: null, facts: null, cardVectors: 0 };\n const signal = options.signal ? { signal: options.signal } : {};\n const { summarizeRepository } = await import(\"./summaries.js\");\n const cards = config.features.summaries\n ? await summarizeRepository(root, {\n config,\n pace: config.provider !== \"local\",\n ...signal,\n ...(options.onProgress ? { onProgress: options.onProgress } : {}),\n })\n : null;\n if (cards?.stoppedBy === \"locked\")\n return { cards: { ...cards, lockedBy: cards.lockedBy ?? null }, facts: null, cardVectors: 0 };\n const { extractIndexFacts } = await import(\"./index-facts.js\");\n const locked = await withCardLock(root, () => extractIndexFacts(root, { config, ...signal }));\n const facts = locked.ok ? locked.value : null;\n let cardVectors = 0;\n // Only the local embedder: a hosted one would bill every card.\n const local =\n (await loadRuntimeConfig(root, { requireDatabase: false }).catch(() => null))?.provider.type ===\n \"local\";\n if (local && cards && cards.summarized > 0 && !options.signal?.aborted) {\n const { syncFileVectors } = await import(\"../context/file-vectors.js\");\n cardVectors = (await syncFileVectors(root, VECTOR_BUDGET_MS))?.cards ?? 0;\n }\n return {\n cards: cards && {\n summarized: cards.summarized,\n timedOut: cards.timedOut,\n remaining: cards.remaining,\n stoppedBy: cards.stoppedBy,\n ...(cards.reason ? { reason: cards.reason } : {}),\n },\n facts:\n !facts || facts.stoppedBy === \"disabled\"\n ? null\n : { written: facts.written, removed: facts.removed },\n cardVectors,\n };\n}\n"],"mappings":";;;;;;;;;;;AAkBA,IAAM,mBAAmB,IAAI;AAS7B,eAAsB,kBACpB,MACA,UAAwF,CAAC,GAChE;AACzB,QAAM,SAAS,MAAM,iBAAiB,IAAI;AAC1C,MAAI,CAAC,OAAO,QAAS,QAAO,EAAE,OAAO,MAAM,OAAO,MAAM,aAAa,EAAE;AACvE,QAAM,SAAS,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;AAC9D,QAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,yBAAgB;AAC7D,QAAM,QAAQ,OAAO,SAAS,YAC1B,MAAM,oBAAoB,MAAM;AAAA,IAC9B;AAAA,IACA,MAAM,OAAO,aAAa;AAAA,IAC1B,GAAG;AAAA,IACH,GAAI,QAAQ,aAAa,EAAE,YAAY,QAAQ,WAAW,IAAI,CAAC;AAAA,EACjE,CAAC,IACD;AACJ,MAAI,OAAO,cAAc;AACvB,WAAO,EAAE,OAAO,EAAE,GAAG,OAAO,UAAU,MAAM,YAAY,KAAK,GAAG,OAAO,MAAM,aAAa,EAAE;AAC9F,QAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,2BAAkB;AAC7D,QAAM,SAAS,MAAM,aAAa,MAAM,MAAM,kBAAkB,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAC;AAC5F,QAAM,QAAQ,OAAO,KAAK,OAAO,QAAQ;AACzC,MAAI,cAAc;AAElB,QAAM,SACH,MAAM,kBAAkB,MAAM,EAAE,iBAAiB,MAAM,CAAC,EAAE,MAAM,MAAM,IAAI,IAAI,SAAS,SACxF;AACF,MAAI,SAAS,SAAS,MAAM,aAAa,KAAK,CAAC,QAAQ,QAAQ,SAAS;AACtE,UAAM,EAAE,gBAAgB,IAAI,MAAM,OAAO,4BAA4B;AACrE,mBAAe,MAAM,gBAAgB,MAAM,gBAAgB,IAAI,SAAS;AAAA,EAC1E;AACA,SAAO;AAAA,IACL,OAAO,SAAS;AAAA,MACd,YAAY,MAAM;AAAA,MAClB,UAAU,MAAM;AAAA,MAChB,WAAW,MAAM;AAAA,MACjB,WAAW,MAAM;AAAA,MACjB,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IACjD;AAAA,IACA,OACE,CAAC,SAAS,MAAM,cAAc,aAC1B,OACA,EAAE,SAAS,MAAM,SAAS,SAAS,MAAM,QAAQ;AAAA,IACvD;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
redactLlmKeys
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-4TKKLQLH.js";
|
|
4
4
|
import {
|
|
5
5
|
writeAtomic
|
|
6
6
|
} from "./chunk-CG5B3LYG.js";
|
|
7
7
|
import {
|
|
8
8
|
localChatJson
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-SXVH74NE.js";
|
|
10
10
|
|
|
11
11
|
// src/llm/budget.ts
|
|
12
12
|
import { mkdir, readFile } from "fs/promises";
|
|
@@ -224,4 +224,4 @@ export {
|
|
|
224
224
|
coolingDown,
|
|
225
225
|
chatJson
|
|
226
226
|
};
|
|
227
|
-
//# sourceMappingURL=chunk-
|
|
227
|
+
//# sourceMappingURL=chunk-7IXPARDM.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
readConfigFile,
|
|
3
3
|
setConfigBlock
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-Y4QQML24.js";
|
|
5
5
|
import {
|
|
6
6
|
LLAMA_RUNTIME,
|
|
7
7
|
LOCAL_MODELS,
|
|
@@ -13,10 +13,12 @@ import {
|
|
|
13
13
|
localModelPath,
|
|
14
14
|
localRuntimeAvailable,
|
|
15
15
|
removeLocalModels
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-SXVH74NE.js";
|
|
17
17
|
|
|
18
18
|
// src/cli/commands/local-llm.ts
|
|
19
19
|
import { spawn } from "child_process";
|
|
20
|
+
import { stat } from "fs/promises";
|
|
21
|
+
import { join } from "path";
|
|
20
22
|
import { confirm, isCancel, spinner } from "@clack/prompts";
|
|
21
23
|
import pc from "picocolors";
|
|
22
24
|
var ok = (text) => `${pc.green("\u2713")} ${text}`;
|
|
@@ -116,7 +118,24 @@ async function printLocalLlmStatus(cards) {
|
|
|
116
118
|
);
|
|
117
119
|
if (llm?.enabled === false)
|
|
118
120
|
console.log(row("status", warn("disabled in .codebuddy/config.json")));
|
|
119
|
-
if (cards)
|
|
121
|
+
if (!cards) return;
|
|
122
|
+
const pass = cards.lastPass;
|
|
123
|
+
const detail = cards.writer ? `being written by pid ${cards.writer}` : pass && `last pass: ${pass.summarized} written${pass.timedOut ? `, ${pass.timedOut} timed out` : ""}, ${pass.remaining} left (${pass.reason ? `${pass.stoppedBy}: ${pass.reason}` : pass.stoppedBy})`;
|
|
124
|
+
console.log(
|
|
125
|
+
row(
|
|
126
|
+
"cards",
|
|
127
|
+
`${cards.carded} / ${cards.eligible} source files${detail ? pc.dim(` (${detail})`) : ""}`
|
|
128
|
+
)
|
|
129
|
+
);
|
|
130
|
+
if (cards.carded === 0) return;
|
|
131
|
+
const { cardsDir } = await import("./zvec-V34P24WF.js");
|
|
132
|
+
const built = await stat(join(cardsDir(process.cwd()), ".zvec-grep")).catch(() => null);
|
|
133
|
+
console.log(
|
|
134
|
+
row(
|
|
135
|
+
"card index",
|
|
136
|
+
built ? ok("built (zvec-grep)") : pc.dim("not built (needs zvec-grep; next card pass)")
|
|
137
|
+
)
|
|
138
|
+
);
|
|
120
139
|
}
|
|
121
140
|
|
|
122
141
|
export {
|
|
@@ -125,4 +144,4 @@ export {
|
|
|
125
144
|
removeLocalLlm,
|
|
126
145
|
printLocalLlmStatus
|
|
127
146
|
};
|
|
128
|
-
//# sourceMappingURL=chunk-
|
|
147
|
+
//# sourceMappingURL=chunk-FEFOZ2ZW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/commands/local-llm.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { confirm, isCancel, spinner } from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { readConfigFile, setConfigBlock } from \"../../core/config-file.js\";\nimport {\n checkLocalModelResources,\n downloadLocalModel,\n formatBytes,\n freeMemory,\n installedLocalModel,\n LLAMA_RUNTIME,\n LOCAL_MODELS,\n type LocalModelSize,\n localModelPath,\n localRuntimeAvailable,\n removeLocalModels,\n} from \"../../llm/local.js\";\nimport type { cardProgress } from \"../../llm/summaries.js\";\n\nconst ok = (text: string) => `${pc.green(\"✓\")} ${text}`;\nconst warn = (text: string) => `${pc.yellow(\"!\")} ${text}`;\nconst row = (label: string, value: string) => ` ${label.padEnd(10)} ${value}`;\n\nexport const LOCAL_LLM_SIZES = Object.keys(LOCAL_MODELS) as LocalModelSize[];\n\n/**\n * `codebuddy models add local-llm`: the explicit opt-in for the local small\n * model. Shows the size, checks disk and RAM, installs the node-llama-cpp\n * runtime when missing (after confirmation), downloads the pinned GGUF with a\n * checksum check, and records the size in `.codebuddy/config.json`.\n */\nexport async function addLocalLlm(\n options: { size?: LocalModelSize; yes?: boolean; interactive?: boolean } = {},\n): Promise<boolean> {\n const spec = LOCAL_MODELS[options.size ?? \"default\"];\n const interactive = options.interactive ?? process.stdin.isTTY === true;\n console.log(\n `${pc.bold(\"Local LLM\")} ${spec.id} ${pc.dim(`(${formatBytes(spec.bytes)} download, ~${formatBytes(spec.ramBytes)} RAM while carding)`)}`,\n );\n const resources = await checkLocalModelResources(spec);\n if (!resources.ok) {\n console.log(warn(resources.reason));\n return false;\n }\n if (freeMemory() < spec.ramBytes) {\n console.log(warn(`only ${formatBytes(freeMemory())} RAM free right now; carding may be slow`));\n }\n if (!(await localRuntimeAvailable())) {\n const install = options.yes\n ? true\n : interactive\n ? await confirm({\n message: `Install the local runtime (${LLAMA_RUNTIME}, ~20–35 MB prebuilt) with npm -g?`,\n initialValue: true,\n })\n : false;\n if (isCancel(install) || !install) {\n console.log(warn(`Runtime missing. Install it with: npm install -g ${LLAMA_RUNTIME}`));\n return false;\n }\n const code = await new Promise<number>((resolve) => {\n const child = spawn(\"npm\", [\"install\", \"-g\", LLAMA_RUNTIME], { stdio: \"inherit\" });\n child.on(\"error\", () => resolve(1));\n child.on(\"close\", (exit) => resolve(exit ?? 1));\n });\n if (code !== 0 || !(await localRuntimeAvailable())) {\n console.log(warn(`Runtime install failed. Run: npm install -g ${LLAMA_RUNTIME}`));\n return false;\n }\n }\n if ((await installedLocalModel(spec.size))?.id !== spec.id) {\n const progress = spinner();\n progress.start(`Downloading ${spec.file}`);\n let shown = 0;\n try {\n await downloadLocalModel(spec, (received, total) => {\n const percent = Math.floor((received / total) * 100);\n if (percent !== shown) {\n shown = percent;\n progress.message(`Downloading ${spec.file} ${percent}%`);\n }\n });\n progress.stop(ok(`${spec.id} verified ${pc.dim(localModelPath(spec))}`));\n } catch (error) {\n progress.stop(warn((error as Error).message));\n return false;\n }\n } else {\n console.log(ok(`${spec.id} already installed`));\n }\n const llm = (await readConfigFile()).llm;\n // A BYOK block stays as it is (the local model is then unused until it is switched).\n if (llm === undefined || llm.provider === \"local\" || llm.enabled === false) {\n await setConfigBlock(\"llm\", { ...llm, enabled: true, provider: \"local\", size: spec.size });\n } else {\n console.log(\n pc.dim(\n ` .codebuddy/config.json uses ${llm.provider}; set llm.provider to \"local\" to use it.`,\n ),\n );\n }\n console.log(\n pc.dim(\n \" Cards are written in the background by `codebuddy serve` or `codebuddy index --cards`.\",\n ),\n );\n return true;\n}\n\nexport async function removeLocalLlm(): Promise<void> {\n const removed = await removeLocalModels();\n const llm = (await readConfigFile()).llm;\n if (llm?.provider === \"local\") await setConfigBlock(\"llm\", { ...llm, enabled: false });\n console.log(\n removed.length > 0\n ? ok(`removed ${removed.join(\", \")}; CodeBuddy falls back to 2.14 behaviour`)\n : pc.dim(\"no local LLM installed\"),\n );\n}\n\nexport async function printLocalLlmStatus(\n cards: Awaited<ReturnType<typeof cardProgress>> | null,\n): Promise<void> {\n const llm = (await readConfigFile()).llm;\n const installed = await installedLocalModel(llm?.size);\n console.log(pc.bold(\"Local LLM\"));\n if (!installed) {\n console.log(row(\"model\", pc.dim(\"not installed (optional: codebuddy models add local-llm)\")));\n return;\n }\n const runtime = await localRuntimeAvailable();\n console.log(row(\"model\", ok(`${installed.id} ${pc.dim(formatBytes(installed.bytes))}`)));\n console.log(\n row(\"runtime\", runtime ? ok(LLAMA_RUNTIME) : warn(`missing: npm install -g ${LLAMA_RUNTIME}`)),\n );\n if (llm?.enabled === false)\n console.log(row(\"status\", warn(\"disabled in .codebuddy/config.json\")));\n if (!cards) return;\n const pass = cards.lastPass;\n const detail = cards.writer\n ? `being written by pid ${cards.writer}`\n : pass &&\n `last pass: ${pass.summarized} written${pass.timedOut ? `, ${pass.timedOut} timed out` : \"\"}, ${pass.remaining} left (${pass.reason ? `${pass.stoppedBy}: ${pass.reason}` : pass.stoppedBy})`;\n console.log(\n row(\n \"cards\",\n `${cards.carded} / ${cards.eligible} source files${detail ? pc.dim(` (${detail})`) : \"\"}`,\n ),\n );\n if (cards.carded === 0) return;\n const { cardsDir } = await import(\"../../retrieval/zvec.js\");\n const built = await stat(join(cardsDir(process.cwd()), \".zvec-grep\")).catch(() => null);\n console.log(\n row(\n \"card index\",\n built ? ok(\"built (zvec-grep)\") : pc.dim(\"not built (needs zvec-grep; next card pass)\"),\n ),\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
chatJson
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-7IXPARDM.js";
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
LEGACY_CARDS_DIR,
|
|
6
|
+
cardsDir,
|
|
6
7
|
refreshZvecCardIndex
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-4LMBZWIF.js";
|
|
8
9
|
import {
|
|
9
10
|
CHANGELOG_PATH,
|
|
10
11
|
NON_SOURCE_PATH,
|
|
@@ -12,23 +13,28 @@ import {
|
|
|
12
13
|
loadFreshIndex,
|
|
13
14
|
loadProjectArchitectureMap,
|
|
14
15
|
readBoundedSource
|
|
15
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-LZTYPJ5I.js";
|
|
17
|
+
import {
|
|
18
|
+
cardLockHolder,
|
|
19
|
+
withCardLock
|
|
20
|
+
} from "./chunk-R5JRKMEP.js";
|
|
16
21
|
import {
|
|
17
22
|
redactSensitive,
|
|
18
23
|
scanSensitive
|
|
19
24
|
} from "./chunk-AL6VBTNA.js";
|
|
20
25
|
import {
|
|
21
26
|
resolveLlmConfig
|
|
22
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-4TKKLQLH.js";
|
|
23
28
|
import {
|
|
24
29
|
writeAtomic
|
|
25
30
|
} from "./chunk-CG5B3LYG.js";
|
|
26
31
|
import {
|
|
32
|
+
loadLocalModel,
|
|
27
33
|
waitForForeground
|
|
28
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-SXVH74NE.js";
|
|
29
35
|
|
|
30
36
|
// src/llm/summaries.ts
|
|
31
|
-
import { mkdir, readdir, readFile, rm } from "fs/promises";
|
|
37
|
+
import { mkdir, readdir, readFile, rm, stat } from "fs/promises";
|
|
32
38
|
import { dirname, join } from "path";
|
|
33
39
|
import { z } from "zod";
|
|
34
40
|
var SUMMARY_PROMPT_VERSION = 2;
|
|
@@ -53,7 +59,9 @@ var LIMITS = {
|
|
|
53
59
|
tokensPerMinute: 6e3,
|
|
54
60
|
timeoutMs: 2e4,
|
|
55
61
|
/** One file per call for the local model: a 0.6B model loses track of batches. */
|
|
56
|
-
localTimeoutMs: 3e4
|
|
62
|
+
localTimeoutMs: 3e4,
|
|
63
|
+
/** This many local timeouts in a row means the machine is overloaded: stop the pass. */
|
|
64
|
+
localTimeoutRun: 5
|
|
57
65
|
};
|
|
58
66
|
function cardEligible(path) {
|
|
59
67
|
return !TEST_PATH.test(path) && !NON_SOURCE_PATH.test(path) && !CHANGELOG_PATH.test(path) && !GENERATED_PATH.test(path);
|
|
@@ -142,12 +150,33 @@ var SYSTEM = [
|
|
|
142
150
|
"Symptoms describe what goes wrong for a user of this specific file (a wrong value, an error, nothing happening), never generic phrases.",
|
|
143
151
|
"Never invent paths or symbol names."
|
|
144
152
|
].join(" ");
|
|
153
|
+
var passPath = (root) => join(root, ".codebuddy", "cache", "cards-pass.json");
|
|
145
154
|
async function summarizeRepository(root, options = {}) {
|
|
146
155
|
const resolved = options.config ?? await resolveLlmConfig(root);
|
|
147
156
|
if (!resolved.enabled || !resolved.features.summaries) {
|
|
148
|
-
return { summarized: 0, skipped: 0, remaining: 0, stoppedBy: "disabled" };
|
|
157
|
+
return { summarized: 0, skipped: 0, timedOut: 0, remaining: 0, stoppedBy: "disabled" };
|
|
149
158
|
}
|
|
150
159
|
const config = resolved;
|
|
160
|
+
const locked = await withCardLock(root, async () => {
|
|
161
|
+
const result = await summarizeLocked(root, config, options);
|
|
162
|
+
const record = { ...result, at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
163
|
+
await writeAtomic(passPath(root), `${JSON.stringify(record)}
|
|
164
|
+
`).catch(() => {
|
|
165
|
+
});
|
|
166
|
+
return result;
|
|
167
|
+
});
|
|
168
|
+
if (locked.ok) return locked.value;
|
|
169
|
+
console.error(`[cards] already being written by pid ${locked.holder ?? "unknown"}`);
|
|
170
|
+
return {
|
|
171
|
+
summarized: 0,
|
|
172
|
+
skipped: 0,
|
|
173
|
+
timedOut: 0,
|
|
174
|
+
remaining: 0,
|
|
175
|
+
stoppedBy: "locked",
|
|
176
|
+
lockedBy: locked.holder
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
async function summarizeLocked(root, config, options) {
|
|
151
180
|
const { manifest } = await loadFreshIndex(root);
|
|
152
181
|
const store = await readStore(root);
|
|
153
182
|
for (const path of Object.keys(store)) if (!manifest.entries[path]) delete store[path];
|
|
@@ -163,10 +192,22 @@ async function summarizeRepository(root, options = {}) {
|
|
|
163
192
|
const local = config.provider === "local";
|
|
164
193
|
const batchFiles = local ? 1 : options.batchFiles ?? LIMITS.batchFiles;
|
|
165
194
|
const limit = options.limit ?? Number.POSITIVE_INFINITY;
|
|
195
|
+
const localTimeoutMs = options.localTimeoutMs ?? LIMITS.localTimeoutMs;
|
|
166
196
|
let summarized = 0;
|
|
167
197
|
let skipped = 0;
|
|
198
|
+
let timedOut = 0;
|
|
199
|
+
let timeoutRun = 0;
|
|
168
200
|
let index = 0;
|
|
169
201
|
let stoppedBy = "done";
|
|
202
|
+
if (pending.length > 0) options.onProgress?.(0, pending.length);
|
|
203
|
+
let reason;
|
|
204
|
+
const loadError = local && pending.length > 0 && config.local ? await loadLocalModel(config.local) : null;
|
|
205
|
+
if (loadError) {
|
|
206
|
+
console.error(`[cards] ${loadError}`);
|
|
207
|
+
reason = loadError;
|
|
208
|
+
index = pending.length;
|
|
209
|
+
stoppedBy = "error";
|
|
210
|
+
}
|
|
170
211
|
while (index < pending.length) {
|
|
171
212
|
if (options.signal?.aborted) {
|
|
172
213
|
stoppedBy = "aborted";
|
|
@@ -202,14 +243,28 @@ async function summarizeRepository(root, options = {}) {
|
|
|
202
243
|
schema: outputSchema,
|
|
203
244
|
jsonSchema: CARD_GRAMMAR,
|
|
204
245
|
maxTokens: local ? LIMITS.localOutputTokens : LIMITS.outputTokensPerFile * batch.length,
|
|
205
|
-
deadlineMs: local ?
|
|
246
|
+
deadlineMs: local ? localTimeoutMs : LIMITS.timeoutMs,
|
|
206
247
|
purpose: "background",
|
|
207
248
|
repositoryRoot: root
|
|
208
249
|
});
|
|
209
250
|
let result = await ask();
|
|
210
251
|
if (!result.ok && result.reason === "invalid") result = await ask();
|
|
252
|
+
if (!result.ok && local && result.reason === "timeout") {
|
|
253
|
+
timedOut += batch.length;
|
|
254
|
+
timeoutRun++;
|
|
255
|
+
for (const { entry } of batch)
|
|
256
|
+
console.error(
|
|
257
|
+
`[cards] ${entry.path} timed out after ${Math.round(localTimeoutMs / 1e3)} s`
|
|
258
|
+
);
|
|
259
|
+
options.onProgress?.(index, pending.length);
|
|
260
|
+
if (timeoutRun >= LIMITS.localTimeoutRun) {
|
|
261
|
+
stoppedBy = "timeout";
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
211
266
|
if (!result.ok) {
|
|
212
|
-
if (result.reason === "invalid" || result.reason === "http" && batch.length === 1
|
|
267
|
+
if (result.reason === "invalid" || result.reason === "http" && batch.length === 1) {
|
|
213
268
|
skipped += batch.length;
|
|
214
269
|
continue;
|
|
215
270
|
}
|
|
@@ -217,9 +272,10 @@ async function summarizeRepository(root, options = {}) {
|
|
|
217
272
|
stoppedBy = result.reason === "budget" ? "budget" : result.reason === "cooldown" || result.reason === "rate-limited" ? "cooldown" : "error";
|
|
218
273
|
break;
|
|
219
274
|
}
|
|
275
|
+
timeoutRun = 0;
|
|
220
276
|
for (const { entry } of batch) {
|
|
221
277
|
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
|
-
if (!answer?.summary.trim()) {
|
|
278
|
+
if (!answer?.summary.trim() || brokenCardText(answer.summary.trim().slice(0, 160))) {
|
|
223
279
|
skipped++;
|
|
224
280
|
continue;
|
|
225
281
|
}
|
|
@@ -239,15 +295,34 @@ async function summarizeRepository(root, options = {}) {
|
|
|
239
295
|
}
|
|
240
296
|
}
|
|
241
297
|
await writeStore(root, store);
|
|
242
|
-
await pruneCardFiles(root, store, manifest);
|
|
243
|
-
if (summarized > 0)
|
|
244
|
-
await refreshZvecCardIndex(
|
|
245
|
-
|
|
298
|
+
const written = await pruneCardFiles(root, store, manifest);
|
|
299
|
+
if (summarized + written > 0 || !await stat(join(cardsDir(root), ".zvec-grep")).catch(() => null)) {
|
|
300
|
+
const cardIndex = await refreshZvecCardIndex(
|
|
301
|
+
root,
|
|
302
|
+
options.signal ? { signal: options.signal } : {}
|
|
303
|
+
);
|
|
304
|
+
if (cardIndex.status !== "ok" && cardIndex.status !== "disabled")
|
|
305
|
+
console.error(`[cards] ${cardIndex.reason}`);
|
|
306
|
+
}
|
|
307
|
+
return {
|
|
308
|
+
summarized,
|
|
309
|
+
skipped,
|
|
310
|
+
timedOut,
|
|
311
|
+
// A load failure leaves every pending file for the next pass.
|
|
312
|
+
remaining: loadError ? pending.length : pending.length - index,
|
|
313
|
+
stoppedBy,
|
|
314
|
+
...reason ? { reason } : {}
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
function brokenCardText(text) {
|
|
318
|
+
return text.length > 160 || /[{}[\]]{2,}|"\s*:/.test(text) || /[^\P{L}\p{Script=Latin}]/u.test(text);
|
|
246
319
|
}
|
|
247
320
|
function toCard(entry, model, answer) {
|
|
248
321
|
const known = new Set((entry.symbolTable ?? []).map((symbol) => symbol.name));
|
|
249
322
|
const clean = (items, count, chars) => [
|
|
250
|
-
...new Set(
|
|
323
|
+
...new Set(
|
|
324
|
+
items.map((item) => item.trim()).filter((item) => item && !brokenCardText(item)).map((item) => redactSensitive(item).slice(0, chars))
|
|
325
|
+
)
|
|
251
326
|
].slice(0, count);
|
|
252
327
|
return {
|
|
253
328
|
hash: entry.hash,
|
|
@@ -257,7 +332,7 @@ function toCard(entry, model, answer) {
|
|
|
257
332
|
symptoms: clean(answer.symptoms, LIMITS.symptoms, LIMITS.symptomChars),
|
|
258
333
|
terms: clean(answer.terms, LIMITS.terms, LIMITS.termChars),
|
|
259
334
|
symbols: Object.fromEntries(
|
|
260
|
-
answer.symbols.filter((symbol) => known.has(symbol.name)).slice(0, LIMITS.symbols).map((symbol) => [symbol.name, symbol.summary.trim().slice(0, LIMITS.symbolChars)])
|
|
335
|
+
answer.symbols.filter((symbol) => known.has(symbol.name) && !brokenCardText(symbol.summary.trim())).slice(0, LIMITS.symbols).map((symbol) => [symbol.name, symbol.summary.trim().slice(0, LIMITS.symbolChars)])
|
|
261
336
|
)
|
|
262
337
|
};
|
|
263
338
|
}
|
|
@@ -265,7 +340,7 @@ async function writeCardFiles(root, store, paths) {
|
|
|
265
340
|
for (const path of paths) {
|
|
266
341
|
const card = store[path];
|
|
267
342
|
if (!card) continue;
|
|
268
|
-
const target = join(root,
|
|
343
|
+
const target = join(cardsDir(root), `${path}.md`);
|
|
269
344
|
await mkdir(dirname(target), { recursive: true });
|
|
270
345
|
await writeAtomic(
|
|
271
346
|
target,
|
|
@@ -288,7 +363,8 @@ async function writeCardFiles(root, store, paths) {
|
|
|
288
363
|
}
|
|
289
364
|
}
|
|
290
365
|
async function pruneCardFiles(root, store, manifest) {
|
|
291
|
-
const directory =
|
|
366
|
+
const directory = cardsDir(root);
|
|
367
|
+
await rm(join(root, LEGACY_CARDS_DIR), { recursive: true, force: true });
|
|
292
368
|
const current = new Set(
|
|
293
369
|
Object.entries(store).filter(([path, card]) => manifest.entries[path]?.hash === card.hash && card.symptoms).map(([path]) => `${path}.md`)
|
|
294
370
|
);
|
|
@@ -299,17 +375,22 @@ async function pruneCardFiles(root, store, manifest) {
|
|
|
299
375
|
await rm(join(directory, name), { force: true });
|
|
300
376
|
}
|
|
301
377
|
}
|
|
378
|
+
const missing = [...current].filter((name) => !present.has(name));
|
|
302
379
|
await writeCardFiles(
|
|
303
380
|
root,
|
|
304
381
|
store,
|
|
305
|
-
|
|
382
|
+
missing.map((name) => name.slice(0, -3))
|
|
306
383
|
);
|
|
384
|
+
return missing.length;
|
|
307
385
|
}
|
|
308
386
|
async function cardProgress(root) {
|
|
309
387
|
const { manifest } = await loadFreshIndex(root);
|
|
310
388
|
const store = await readStore(root);
|
|
311
389
|
const eligible = Object.keys(manifest.entries).filter(cardEligible);
|
|
390
|
+
const lastPass = await readFile(passPath(root), "utf8").then((text) => JSON.parse(text)).catch(() => null);
|
|
312
391
|
return {
|
|
392
|
+
writer: await cardLockHolder(root),
|
|
393
|
+
lastPass,
|
|
313
394
|
carded: eligible.filter(
|
|
314
395
|
(path) => store[path]?.hash === manifest.entries[path]?.hash && store[path]?.version === SUMMARY_PROMPT_VERSION
|
|
315
396
|
).length,
|
|
@@ -353,6 +434,7 @@ export {
|
|
|
353
434
|
cardWords,
|
|
354
435
|
loadLlmSummaries,
|
|
355
436
|
summarizeRepository,
|
|
437
|
+
brokenCardText,
|
|
356
438
|
cardProgress
|
|
357
439
|
};
|
|
358
|
-
//# sourceMappingURL=chunk-
|
|
440
|
+
//# sourceMappingURL=chunk-FGJ2KPPN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/llm/summaries.ts"],"sourcesContent":["import { mkdir, readdir, readFile, rm, stat } 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 { cardsDir, LEGACY_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 { loadLocalModel, waitForForeground } 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 (`cardsDir`), 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 /** Why the pass stopped early with `stoppedBy: \"error\"`, when known. */\n reason?: string;\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 let reason: string | undefined;\n const loadError =\n local && pending.length > 0 && config.local ? await loadLocalModel(config.local) : null;\n if (loadError) {\n console.error(`[cards] ${loadError}`);\n reason = loadError;\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 // A broken summary is no card (the file is retried by the next pass).\n if (!answer?.summary.trim() || brokenCardText(answer.summary.trim().slice(0, 160))) {\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 const written = await pruneCardFiles(root, store, manifest);\n // Also after a migration or a missing card index: the cards exist, the index may not.\n if (\n summarized + written > 0 ||\n !(await stat(join(cardsDir(root), \".zvec-grep\")).catch(() => null))\n ) {\n const cardIndex = await refreshZvecCardIndex(\n root,\n options.signal ? { signal: options.signal } : {},\n );\n if (cardIndex.status !== \"ok\" && cardIndex.status !== \"disabled\")\n console.error(`[cards] ${cardIndex.reason}`);\n }\n return {\n summarized,\n skipped,\n timedOut,\n // A load failure leaves every pending file for the next pass.\n remaining: loadError ? pending.length : pending.length - index,\n stoppedBy,\n ...(reason ? { reason } : {}),\n };\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. */\n/**\n * Text a small model broke rather than wrote: JSON punctuation runs (`]}]}`),\n * letters outside the Latin script (reasoning leaking in another language),\n * or a run-on string far past any field's bound. Such strings are dropped.\n */\nexport function brokenCardText(text: string): boolean {\n return (\n text.length > 160 || /[{}[\\]]{2,}|\"\\s*:/.test(text) || /[^\\P{L}\\p{Script=Latin}]/u.test(text)\n );\n}\n\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(\n items\n .map((item) => item.trim())\n .filter((item) => item && !brokenCardText(item))\n .map((item) => redactSensitive(item).slice(0, chars)),\n ),\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) && !brokenCardText(symbol.summary.trim()))\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(cardsDir(root), `${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. */\n/** Returns how many missing card files it wrote. */\nasync function pruneCardFiles(\n root: string,\n store: Store,\n manifest: IndexManifest,\n): Promise<number> {\n const directory = cardsDir(root);\n // Pre-2.15.2 in-repo cards: their zg index never worked (see `cardsDir`).\n await rm(join(root, LEGACY_CARDS_DIR), { recursive: true, force: true });\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 const missing = [...current].filter((name) => !present.has(name));\n await writeCardFiles(\n root,\n store,\n missing.map((name) => name.slice(0, -3)),\n );\n return missing.length;\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":[]}
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
rankSemanticReuse,
|
|
12
12
|
readPolicyFile,
|
|
13
13
|
resolveRiskPaths
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-5ZLNHYJH.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-4LMBZWIF.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-LZTYPJ5I.js";
|
|
41
41
|
import {
|
|
42
42
|
codeBuddyConfigSchema,
|
|
43
43
|
createRuntime,
|
|
44
44
|
listFactsPage
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-TIMS3PB7.js";
|
|
46
46
|
import {
|
|
47
47
|
MemoryFileStore,
|
|
48
48
|
VERSION
|
|
49
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-3L5IR6OW.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-SXVH74NE.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-ZTE3H7QM.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-S6NC25P5.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-5EYLEBEP.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-KGDRGA2L.js.map
|