@orquentra/codebuddy 2.8.0 → 2.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/CLAUDE_CODEX.md +33 -0
  2. package/README.md +27 -3
  3. package/dist/{adapters-S4HHTWJW.js → adapters-OJRSVFV3.js} +2 -2
  4. package/dist/{chunk-GKNYMNFM.js → chunk-4DKYKDHI.js} +6 -1
  5. package/dist/chunk-4DKYKDHI.js.map +1 -0
  6. package/dist/{chunk-6KGYAFG7.js → chunk-7QMHIJBA.js} +30 -3
  7. package/dist/chunk-7QMHIJBA.js.map +1 -0
  8. package/dist/{chunk-6NZEZJYD.js → chunk-ESC4Y3L7.js} +3 -5
  9. package/dist/{chunk-6NZEZJYD.js.map → chunk-ESC4Y3L7.js.map} +1 -1
  10. package/dist/chunk-GQKWUHMA.js +20 -0
  11. package/dist/chunk-GQKWUHMA.js.map +1 -0
  12. package/dist/{chunk-J4T6HHI4.js → chunk-L6BRYZ6G.js} +537 -316
  13. package/dist/chunk-L6BRYZ6G.js.map +1 -0
  14. package/dist/{chunk-UK6XTS2Y.js → chunk-LRQNPYCK.js} +13 -19
  15. package/dist/chunk-LRQNPYCK.js.map +1 -0
  16. package/dist/{chunk-GXOML4UZ.js → chunk-LW75SKZR.js} +2 -2
  17. package/dist/{chunk-EJQVGFH2.js → chunk-UOTKHSQ5.js} +26 -9
  18. package/dist/chunk-UOTKHSQ5.js.map +1 -0
  19. package/dist/{chunk-W7GAXL6C.js → chunk-V7PT6ER5.js} +6 -5
  20. package/dist/chunk-V7PT6ER5.js.map +1 -0
  21. package/dist/{chunk-ZNLSCV3J.js → chunk-XSNW6Y3X.js} +1253 -315
  22. package/dist/chunk-XSNW6Y3X.js.map +1 -0
  23. package/dist/{chunk-6DONJW3W.js → chunk-YJPCS2GP.js} +274 -69
  24. package/dist/chunk-YJPCS2GP.js.map +1 -0
  25. package/dist/{chunk-XL2HLACE.js → chunk-YN7TEIUH.js} +1 -1
  26. package/dist/chunk-YN7TEIUH.js.map +1 -0
  27. package/dist/{chunk-TLWGZVPC.js → chunk-YNSTIMCF.js} +746 -66
  28. package/dist/chunk-YNSTIMCF.js.map +1 -0
  29. package/dist/{chunk-NY2YXP3R.js → chunk-YWDH6DQ6.js} +70 -4
  30. package/dist/chunk-YWDH6DQ6.js.map +1 -0
  31. package/dist/cli/index.js +23 -9
  32. package/dist/cli/index.js.map +1 -1
  33. package/dist/{codebuddy-DO7ZhoWn.d.ts → codebuddy-CHqMptZB.d.ts} +35 -0
  34. package/dist/index.d.ts +6 -4
  35. package/dist/index.js +9 -9
  36. package/dist/install-EU35ERK5.js +30 -0
  37. package/dist/langgraph/index.d.ts +1 -1
  38. package/dist/lean-43ADYTBW.js +28 -0
  39. package/dist/{main-QUASSFP7.js → main-SFC5LV4E.js} +1333 -437
  40. package/dist/main-SFC5LV4E.js.map +1 -0
  41. package/dist/{operations-WJR7AZZF.js → operations-6Z6ZDVBP.js} +3 -3
  42. package/dist/{pretool-command-JTKX6G3M.js → pretool-command-B5GY5T6E.js} +3 -3
  43. package/dist/prompt-gate-TDTFFQW2.js +8 -0
  44. package/dist/{runtime-OQQ22VJF.js → runtime-W5AW32SD.js} +15 -7
  45. package/dist/{summaries-ML53R7EL.js → summaries-32PHOB6L.js} +3 -4
  46. package/dist/{zvec-XDZNZ2OL.js → zvec-24HLAFGN.js} +3 -4
  47. package/dist/zvec-24HLAFGN.js.map +1 -0
  48. package/package.json +11 -2
  49. package/dist/chunk-6DONJW3W.js.map +0 -1
  50. package/dist/chunk-6KGYAFG7.js.map +0 -1
  51. package/dist/chunk-EJQVGFH2.js.map +0 -1
  52. package/dist/chunk-GKNYMNFM.js.map +0 -1
  53. package/dist/chunk-ITQO632W.js +0 -673
  54. package/dist/chunk-ITQO632W.js.map +0 -1
  55. package/dist/chunk-J4T6HHI4.js.map +0 -1
  56. package/dist/chunk-NY2YXP3R.js.map +0 -1
  57. package/dist/chunk-TLWGZVPC.js.map +0 -1
  58. package/dist/chunk-UK6XTS2Y.js.map +0 -1
  59. package/dist/chunk-W7GAXL6C.js.map +0 -1
  60. package/dist/chunk-XL2HLACE.js.map +0 -1
  61. package/dist/chunk-ZNLSCV3J.js.map +0 -1
  62. package/dist/install-BXM56QCD.js +0 -19
  63. package/dist/lean-command-7DFW32MA.js +0 -19
  64. package/dist/main-QUASSFP7.js.map +0 -1
  65. /package/dist/{adapters-S4HHTWJW.js.map → adapters-OJRSVFV3.js.map} +0 -0
  66. /package/dist/{chunk-GXOML4UZ.js.map → chunk-LW75SKZR.js.map} +0 -0
  67. /package/dist/{install-BXM56QCD.js.map → install-EU35ERK5.js.map} +0 -0
  68. /package/dist/{lean-command-7DFW32MA.js.map → lean-43ADYTBW.js.map} +0 -0
  69. /package/dist/{operations-WJR7AZZF.js.map → operations-6Z6ZDVBP.js.map} +0 -0
  70. /package/dist/{pretool-command-JTKX6G3M.js.map → pretool-command-B5GY5T6E.js.map} +0 -0
  71. /package/dist/{runtime-OQQ22VJF.js.map → prompt-gate-TDTFFQW2.js.map} +0 -0
  72. /package/dist/{summaries-ML53R7EL.js.map → runtime-W5AW32SD.js.map} +0 -0
  73. /package/dist/{zvec-XDZNZ2OL.js.map → summaries-32PHOB6L.js.map} +0 -0
@@ -1,7 +1,8 @@
1
1
  import {
2
- loadArchitectureMap,
3
- loadFreshIndex
4
- } from "./chunk-ITQO632W.js";
2
+ extractPythonImports,
3
+ extractSymbolTable,
4
+ supportsSymbols
5
+ } from "./chunk-Y7D7PUBE.js";
5
6
  import {
6
7
  isCredentialPath
7
8
  } from "./chunk-AL6VBTNA.js";
@@ -12,26 +13,681 @@ import {
12
13
  readConfigFile
13
14
  } from "./chunk-KNR2P2UY.js";
14
15
  import {
16
+ assertNoSymlinkBetween,
15
17
  writeAtomic
16
18
  } from "./chunk-CG5B3LYG.js";
17
19
 
20
+ // src/map/indexer.ts
21
+ import { readdir, readFile, stat } from "fs/promises";
22
+ import { dirname, extname, join, relative, resolve } from "path";
23
+ var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py"]);
24
+ var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
25
+ ".git",
26
+ "node_modules",
27
+ "dist",
28
+ "build",
29
+ "coverage",
30
+ ".codebuddy",
31
+ // Python virtualenvs and caches would otherwise map site-packages.
32
+ ".venv",
33
+ "venv",
34
+ ".tox",
35
+ "__pycache__"
36
+ ]);
37
+ async function buildArchitectureMap(repositoryRoot = process.cwd()) {
38
+ const root = resolve(repositoryRoot);
39
+ const files = await listSourceFiles(root);
40
+ const fileSet = new Set(files.map((file) => normalize(relative(root, file))));
41
+ const modules = [];
42
+ const edges = [];
43
+ for (const absolutePath of files) {
44
+ const source = await readFile(absolutePath, "utf8");
45
+ const from = normalize(relative(root, absolutePath));
46
+ const resolvedImports = moduleImports(root, from, source, fileSet);
47
+ edges.push(...resolvedImports.map(({ to, kind }) => ({ from, to, kind })));
48
+ modules.push({
49
+ path: from,
50
+ language: moduleLanguage(from),
51
+ imports: [...new Set(resolvedImports.map(({ to }) => to))].sort()
52
+ });
53
+ }
54
+ return {
55
+ modules: modules.sort((left, right) => left.path.localeCompare(right.path)),
56
+ edges: edges.sort(
57
+ (left, right) => `${left.from}:${left.to}`.localeCompare(`${right.from}:${right.to}`)
58
+ )
59
+ };
60
+ }
61
+ async function updateArchitectureMap(repositoryRoot, sourcePaths, previous, changedPaths) {
62
+ const root = resolve(repositoryRoot);
63
+ const normalizedPaths = sourcePaths.map(normalize).sort();
64
+ const fileSet = new Set(normalizedPaths);
65
+ const changed = new Set(changedPaths.map(normalize));
66
+ const retainedModules = (previous?.modules ?? []).filter(
67
+ (module) => fileSet.has(module.path) && !changed.has(module.path)
68
+ );
69
+ const retainedEdges = (previous?.edges ?? []).filter(
70
+ (edge) => fileSet.has(edge.from) && fileSet.has(edge.to) && !changed.has(edge.from)
71
+ );
72
+ const modules = [...retainedModules];
73
+ const edges = [...retainedEdges];
74
+ for (const path of normalizedPaths) {
75
+ if (!changed.has(path)) continue;
76
+ const source = await readFile(join(root, path), "utf8");
77
+ const resolvedImports = moduleImports(root, path, source, fileSet);
78
+ edges.push(...resolvedImports.map(({ to, kind }) => ({ from: path, to, kind })));
79
+ modules.push({
80
+ path,
81
+ language: moduleLanguage(path),
82
+ imports: [...new Set(resolvedImports.map(({ to }) => to))].sort()
83
+ });
84
+ }
85
+ return {
86
+ map: {
87
+ modules: modules.sort((left, right) => left.path.localeCompare(right.path)),
88
+ edges: edges.sort(
89
+ (left, right) => `${left.from}:${left.to}`.localeCompare(`${right.from}:${right.to}`)
90
+ )
91
+ },
92
+ stats: {
93
+ readFiles: changed.size,
94
+ reusedFiles: retainedModules.length,
95
+ rebuilt: previous === null || changed.size === normalizedPaths.length
96
+ }
97
+ };
98
+ }
99
+ function queryMap(map, query) {
100
+ const limit = query.limit ?? 50;
101
+ return map.edges.filter((edge) => query.from ? edge.from === normalize(query.from) : true).filter((edge) => query.to ? edge.to === normalize(query.to) : true).slice(0, limit);
102
+ }
103
+ function neighbours(map, modulePath, direction = "both") {
104
+ const target = normalize(modulePath);
105
+ const edges = map.edges.filter((edge) => {
106
+ if (direction === "in") return edge.to === target;
107
+ if (direction === "out") return edge.from === target;
108
+ return edge.from === target || edge.to === target;
109
+ });
110
+ const paths = /* @__PURE__ */ new Set([target]);
111
+ for (const edge of edges) {
112
+ paths.add(edge.from);
113
+ paths.add(edge.to);
114
+ }
115
+ return {
116
+ modules: map.modules.filter((module) => paths.has(module.path)),
117
+ edges
118
+ };
119
+ }
120
+ async function listSourceFiles(root) {
121
+ const files = [];
122
+ await walk(root, files);
123
+ return files.sort();
124
+ }
125
+ async function walk(directory, files) {
126
+ const entries = await readdir(directory, { withFileTypes: true });
127
+ for (const entry of entries) {
128
+ const absolute = join(directory, entry.name);
129
+ if (entry.isDirectory()) {
130
+ if (IGNORED_DIRECTORIES.has(entry.name)) continue;
131
+ await walk(absolute, files);
132
+ continue;
133
+ }
134
+ if (!entry.isFile()) continue;
135
+ if (!SUPPORTED_EXTENSIONS.has(extname(entry.name))) continue;
136
+ const info = await stat(absolute);
137
+ if (info.size > 5e5) continue;
138
+ files.push(absolute);
139
+ }
140
+ }
141
+ function moduleLanguage(path) {
142
+ const extension = extname(path);
143
+ if (extension === ".py") return "py";
144
+ return extension.includes("ts") ? "ts" : "js";
145
+ }
146
+ function moduleImports(root, from, source, fileSet) {
147
+ if (extname(from) === ".py") {
148
+ return pythonImports(from, source, fileSet).map((to) => ({ to, kind: "import" }));
149
+ }
150
+ return extractImports(source).flatMap((specifier) => {
151
+ const to = resolveImport(root, from, specifier.value, fileSet);
152
+ return to ? [{ to, kind: specifier.kind }] : [];
153
+ });
154
+ }
155
+ var PYTHON_ROOTS = ["", "src/"];
156
+ function pythonImports(from, source, fileSet) {
157
+ const pick = (...candidates) => candidates.find((candidate) => fileSet.has(candidate)) ?? null;
158
+ const moduleFile = (base) => pick(`${base}.py`, `${base}/__init__.py`);
159
+ const packageDir = from.split("/").slice(0, -1);
160
+ const targets = [];
161
+ for (const statement of extractPythonImports(source)) {
162
+ const parts = statement.module ? statement.module.split(".") : [];
163
+ let bases;
164
+ if (statement.level > 0) {
165
+ const up = statement.level - 1;
166
+ if (up > packageDir.length) continue;
167
+ bases = [[...packageDir.slice(0, packageDir.length - up), ...parts].join("/")];
168
+ } else {
169
+ bases = PYTHON_ROOTS.map((prefix) => `${prefix}${parts.join("/")}`);
170
+ }
171
+ for (const base of bases) {
172
+ const prefix = base ? `${base}/` : "";
173
+ const module = parts.length > 0 ? moduleFile(base) : pick(`${prefix}__init__.py`);
174
+ const submodules = statement.names.flatMap((name) => moduleFile(`${prefix}${name}`) ?? []);
175
+ if (!module && submodules.length === 0) continue;
176
+ targets.push(...submodules);
177
+ if (module && submodules.length < Math.max(1, statement.names.length)) targets.push(module);
178
+ break;
179
+ }
180
+ }
181
+ return [...new Set(targets)].filter((target) => target !== from);
182
+ }
183
+ function extractImports(source) {
184
+ const imports = [];
185
+ const stripped = source.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/(^|[^:])\/\/[^\n]*/g, "$1 ");
186
+ const staticImport = /\bimport\s+(?:type\s+)?(?:[^'"()+]+?\s+from\s+)?["']([^"']+)["']/g;
187
+ const exportFrom = /\bexport\s+[^'"()+]+?\s+from\s+["']([^"']+)["']/g;
188
+ const dynamicImport = /\bimport\s*\(\s*["']([^"']+)["']\s*\)/g;
189
+ for (const match of stripped.matchAll(staticImport)) {
190
+ if (match[1]) imports.push({ value: match[1], kind: "import" });
191
+ }
192
+ for (const match of stripped.matchAll(exportFrom)) {
193
+ if (match[1]) imports.push({ value: match[1], kind: "import" });
194
+ }
195
+ for (const match of stripped.matchAll(dynamicImport)) {
196
+ if (match[1]) imports.push({ value: match[1], kind: "dynamic_import" });
197
+ }
198
+ return imports;
199
+ }
200
+ function resolveImport(root, from, specifier, fileSet) {
201
+ if (!specifier.startsWith(".")) return null;
202
+ const base = normalize(relative(root, resolve(root, dirname(from), specifier)));
203
+ const stem = base.replace(/\.(js|jsx|mjs|cjs)$/, "");
204
+ const bases = stem === base ? [base] : [base, stem];
205
+ const candidates = bases.flatMap((b) => [
206
+ b,
207
+ `${b}.ts`,
208
+ `${b}.tsx`,
209
+ `${b}.js`,
210
+ `${b}.jsx`,
211
+ `${b}.mjs`,
212
+ `${b}.cjs`,
213
+ `${b}/index.ts`,
214
+ `${b}/index.tsx`,
215
+ `${b}/index.js`,
216
+ `${b}/index.jsx`
217
+ ]);
218
+ return candidates.find((candidate) => fileSet.has(candidate)) ?? null;
219
+ }
220
+ function normalize(path) {
221
+ return path.replace(/\\/g, "/").replace(/^\.\//, "");
222
+ }
223
+
224
+ // src/indexer/engine.ts
225
+ import { createHash } from "crypto";
226
+ import { readFile as readFile4, stat as stat2 } from "fs/promises";
227
+ import { join as join5, resolve as resolve4 } from "path";
228
+
229
+ // src/map/cache.ts
230
+ import { mkdir as mkdir2, readFile as readFile3 } from "fs/promises";
231
+ import { dirname as dirname3, extname as extname2, join as join3, resolve as resolve3 } from "path";
232
+
233
+ // src/indexer/store.ts
234
+ import { mkdir, readFile as readFile2 } from "fs/promises";
235
+ import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
236
+ import { z } from "zod";
237
+
238
+ // src/indexer/types.ts
239
+ var INDEX_MANIFEST_VERSION = 3;
240
+
241
+ // src/indexer/store.ts
242
+ var symbolSpanSchema = z.object({
243
+ name: z.string(),
244
+ kind: z.enum(["function", "method", "class", "interface", "type", "enum", "const", "heading"]),
245
+ startLine: z.number().int().nonnegative(),
246
+ endLine: z.number().int().nonnegative(),
247
+ signature: z.string(),
248
+ exported: z.boolean()
249
+ });
250
+ var entrySchema = z.object({
251
+ path: z.string(),
252
+ hash: z.string(),
253
+ size: z.number().int().nonnegative(),
254
+ mtimeMs: z.number().optional(),
255
+ ctimeMs: z.number().optional(),
256
+ language: z.string(),
257
+ lines: z.number().int().nonnegative(),
258
+ symbols: z.array(z.string()),
259
+ symbolTable: z.array(symbolSpanSchema).optional(),
260
+ summary: z.string(),
261
+ indexedAt: z.string()
262
+ });
263
+ var manifestSchema = z.object({
264
+ version: z.literal(INDEX_MANIFEST_VERSION),
265
+ generatedAt: z.string(),
266
+ entries: z.record(z.string(), entrySchema)
267
+ });
268
+ var IndexStore = class {
269
+ repositoryRoot;
270
+ cacheDirectory;
271
+ manifestPath;
272
+ constructor(repositoryRoot = process.cwd()) {
273
+ this.repositoryRoot = resolve2(repositoryRoot);
274
+ this.cacheDirectory = join2(this.repositoryRoot, ".codebuddy", "cache");
275
+ this.manifestPath = join2(this.cacheDirectory, "index.json");
276
+ }
277
+ /** Read the manifest, or an empty one if absent/corrupt/version-mismatched. */
278
+ async read() {
279
+ try {
280
+ const raw = await readFile2(this.manifestPath, "utf8");
281
+ const parsed = manifestSchema.safeParse(JSON.parse(raw));
282
+ if (!parsed.success) return emptyManifest();
283
+ return parsed.data;
284
+ } catch (error) {
285
+ if (error.code === "ENOENT") return emptyManifest();
286
+ return emptyManifest();
287
+ }
288
+ }
289
+ async write(manifest) {
290
+ await assertNoSymlinkBetween(this.repositoryRoot, dirname2(this.manifestPath));
291
+ await mkdir(this.cacheDirectory, { recursive: true, mode: 448 });
292
+ await writeAtomic(this.manifestPath, `${JSON.stringify(manifest, null, 2)}
293
+ `);
294
+ }
295
+ };
296
+ function emptyManifest() {
297
+ return { version: INDEX_MANIFEST_VERSION, generatedAt: (/* @__PURE__ */ new Date(0)).toISOString(), entries: {} };
298
+ }
299
+
300
+ // src/map/cache.ts
301
+ var CACHE_VERSION = 1;
302
+ var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py"]);
303
+ async function loadArchitectureMap(repositoryRoot = process.cwd(), suppliedManifest) {
304
+ const root = resolve3(repositoryRoot);
305
+ const manifest = suppliedManifest ?? await new IndexStore(root).read();
306
+ if (Object.keys(manifest.entries).length === 0) {
307
+ const map = await buildArchitectureMap(root);
308
+ return {
309
+ map,
310
+ stats: { readFiles: map.modules.length, reusedFiles: 0, rebuilt: true },
311
+ source: "fallback"
312
+ };
313
+ }
314
+ return refreshArchitectureCache(root, manifest);
315
+ }
316
+ async function refreshArchitectureCache(repositoryRoot, manifest) {
317
+ const root = resolve3(repositoryRoot);
318
+ const path = join3(root, ".codebuddy", "cache", "architecture.json");
319
+ const currentHashes = sourceHashes(manifest);
320
+ const currentPaths = Object.keys(currentHashes).sort();
321
+ const cached = await readCache(path);
322
+ const cachedPaths = cached ? Object.keys(cached.sourceHashes).sort() : [];
323
+ const fileSetChanged = cachedPaths.join("\n") !== currentPaths.join("\n");
324
+ const changedPaths = !cached || fileSetChanged ? currentPaths : currentPaths.filter(
325
+ (sourcePath) => cached.sourceHashes[sourcePath] !== currentHashes[sourcePath]
326
+ );
327
+ if (cached && changedPaths.length === 0) {
328
+ return {
329
+ map: cached.map,
330
+ stats: { readFiles: 0, reusedFiles: cached.map.modules.length, rebuilt: false },
331
+ source: "cache"
332
+ };
333
+ }
334
+ const updated = await updateArchitectureMap(
335
+ root,
336
+ currentPaths,
337
+ fileSetChanged ? null : cached?.map ?? null,
338
+ changedPaths
339
+ );
340
+ const next = {
341
+ version: CACHE_VERSION,
342
+ generatedAt: manifest.generatedAt,
343
+ sourceHashes: currentHashes,
344
+ map: updated.map
345
+ };
346
+ await assertNoSymlinkBetween(root, dirname3(path));
347
+ await mkdir2(dirname3(path), { recursive: true, mode: 448 });
348
+ await writeAtomic(path, `${JSON.stringify(next, null, 2)}
349
+ `);
350
+ return { ...updated, source: fileSetChanged || !cached ? "rebuild" : "incremental" };
351
+ }
352
+ async function readCache(path) {
353
+ try {
354
+ const parsed = JSON.parse(await readFile3(path, "utf8"));
355
+ if (parsed.version !== CACHE_VERSION || !parsed.map || !parsed.sourceHashes) return null;
356
+ if (!Array.isArray(parsed.map.modules) || !Array.isArray(parsed.map.edges)) return null;
357
+ if (!parsed.map.modules.every(
358
+ (module) => module && typeof module.path === "string" && typeof module.language === "string" && Array.isArray(module.imports)
359
+ ) || !parsed.map.edges.every(
360
+ (edge) => edge && typeof edge.from === "string" && typeof edge.to === "string"
361
+ )) {
362
+ return null;
363
+ }
364
+ return parsed;
365
+ } catch {
366
+ return null;
367
+ }
368
+ }
369
+ function sourceHashes(manifest) {
370
+ return Object.fromEntries(
371
+ Object.values(manifest.entries).filter((entry) => SOURCE_EXTENSIONS.has(extname2(entry.path).toLowerCase())).map((entry) => [entry.path, entry.hash])
372
+ );
373
+ }
374
+
375
+ // src/indexer/scan.ts
376
+ import { spawn } from "child_process";
377
+ import { readdir as readdir2 } from "fs/promises";
378
+ import { extname as extname4, join as join4, relative as relative2, sep } from "path";
379
+
380
+ // src/indexer/summary.ts
381
+ import { extname as extname3 } from "path";
382
+ var LANGUAGE_BY_EXTENSION = {
383
+ ".ts": "ts",
384
+ ".tsx": "tsx",
385
+ ".js": "js",
386
+ ".jsx": "jsx",
387
+ ".mjs": "js",
388
+ ".cjs": "js",
389
+ ".json": "json",
390
+ ".md": "md",
391
+ ".mdx": "md",
392
+ ".rst": "rst",
393
+ ".yaml": "yaml",
394
+ ".yml": "yaml",
395
+ ".toml": "toml",
396
+ ".css": "css",
397
+ ".scss": "scss",
398
+ ".html": "html",
399
+ ".py": "py",
400
+ ".go": "go",
401
+ ".rs": "rust",
402
+ ".java": "java",
403
+ ".rb": "ruby",
404
+ ".php": "php",
405
+ ".sh": "shell",
406
+ ".sql": "sql",
407
+ ".txt": "text"
408
+ };
409
+ var SUPPORTED_EXTENSIONS2 = new Set(Object.keys(LANGUAGE_BY_EXTENSION));
410
+ var CODE_LANGUAGES = /* @__PURE__ */ new Set(["ts", "tsx", "js", "jsx"]);
411
+ var SYMBOL_LIMIT = 8;
412
+ var SUMMARY_COMMENT_MAX = 80;
413
+ function languageFor(path) {
414
+ return LANGUAGE_BY_EXTENSION[extname3(path).toLowerCase()] ?? "text";
415
+ }
416
+ function summarizeSource(path, content) {
417
+ const language = languageFor(path);
418
+ const lines = countLines(content);
419
+ const symbols = CODE_LANGUAGES.has(language) ? extractSymbols(content) : language === "py" ? pythonPublicNames(content) : [];
420
+ const summary = composeSummary(language, lines, symbols, content);
421
+ return { language, lines, symbols, summary };
422
+ }
423
+ function countLines(content) {
424
+ if (content.length === 0) return 0;
425
+ return content.replace(/\r/g, "").replace(/\n$/, "").split("\n").length;
426
+ }
427
+ function extractSymbols(content) {
428
+ const names = [];
429
+ const push = (name) => {
430
+ const trimmed = name?.trim();
431
+ if (trimmed && /^[A-Za-z_$][\w$]*$/.test(trimmed) && !names.includes(trimmed)) {
432
+ names.push(trimmed);
433
+ }
434
+ };
435
+ const declaration = /^export\s+(?:default\s+)?(?:async\s+)?(?:abstract\s+)?(?:const|let|var|function|class|interface|type|enum)\s+([A-Za-z_$][\w$]*)/gm;
436
+ for (const match of content.matchAll(declaration)) push(match[1]);
437
+ const namedExports = /^export\s*\{([^}]*)\}/gm;
438
+ for (const match of content.matchAll(namedExports)) {
439
+ for (const part of (match[1] ?? "").split(",")) {
440
+ const alias = part.includes(" as ") ? part.split(" as ").pop() : part;
441
+ push(alias);
442
+ }
443
+ }
444
+ return names.slice(0, SYMBOL_LIMIT);
445
+ }
446
+ function pythonPublicNames(content) {
447
+ return extractSymbolTable(content, "x.py").filter((symbol) => symbol.exported && symbol.kind !== "method").map((symbol) => symbol.name).slice(0, SYMBOL_LIMIT);
448
+ }
449
+ function composeSummary(language, lines, symbols, content) {
450
+ const head = `${language} \u2022 ${lines} line${lines === 1 ? "" : "s"}`;
451
+ if (symbols.length > 0) return `${head} \u2022 exports ${symbols.join(", ")}`;
452
+ const comment = leadingComment(content);
453
+ if (comment) return `${head} \u2022 ${comment}`;
454
+ return head;
455
+ }
456
+ function leadingComment(content) {
457
+ for (const raw of content.split(/\r?\n/)) {
458
+ const line = raw.trim();
459
+ if (line === "") continue;
460
+ const stripped = line.replace(/^\/\/+/, "").replace(/^\/\*+/, "").replace(/\*+\/$/, "").replace(/^[#*]+/, "").trim();
461
+ if (line.startsWith("//") || line.startsWith("/*") || line.startsWith("#") || line.startsWith("*")) {
462
+ if (stripped) return truncate(stripped, SUMMARY_COMMENT_MAX);
463
+ continue;
464
+ }
465
+ return null;
466
+ }
467
+ return null;
468
+ }
469
+ function truncate(value, max) {
470
+ return value.length <= max ? value : `${value.slice(0, max - 1)}\u2026`;
471
+ }
472
+
473
+ // src/indexer/scan.ts
474
+ var FALLBACK_IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
475
+ ".git",
476
+ "node_modules",
477
+ "dist",
478
+ "build",
479
+ "coverage",
480
+ ".codebuddy",
481
+ "graphify-out",
482
+ ".next",
483
+ ".turbo",
484
+ "out",
485
+ ".venv",
486
+ "venv",
487
+ "__pycache__"
488
+ ]);
489
+ async function listIndexableFiles(repositoryRoot) {
490
+ const tracked = await gitListFiles(repositoryRoot);
491
+ const paths = tracked ?? await walkFiles(repositoryRoot);
492
+ return paths.map(normalize2).filter((path) => !path.startsWith(".codebuddy/") && !path.startsWith("graphify-out/")).filter((path) => SUPPORTED_EXTENSIONS2.has(extname4(path).toLowerCase())).sort();
493
+ }
494
+ async function gitListFiles(repositoryRoot) {
495
+ const result = await runGit(repositoryRoot, [
496
+ "ls-files",
497
+ "-z",
498
+ "--cached",
499
+ "--others",
500
+ "--exclude-standard"
501
+ ]);
502
+ if (result.code !== 0) return null;
503
+ return result.stdout.split("\0").filter(Boolean);
504
+ }
505
+ async function walkFiles(root) {
506
+ const files = [];
507
+ const walk2 = async (directory) => {
508
+ const entries = await readdir2(directory, { withFileTypes: true });
509
+ for (const entry of entries) {
510
+ const absolute = join4(directory, entry.name);
511
+ if (entry.isDirectory()) {
512
+ if (FALLBACK_IGNORED_DIRECTORIES.has(entry.name)) continue;
513
+ await walk2(absolute);
514
+ continue;
515
+ }
516
+ if (entry.isFile()) files.push(relative2(root, absolute));
517
+ }
518
+ };
519
+ await walk2(root);
520
+ return files;
521
+ }
522
+ function runGit(cwd, args) {
523
+ return new Promise((resolveResult) => {
524
+ const child = spawn("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
525
+ let stdout = "";
526
+ let stderr = "";
527
+ child.stdout.setEncoding("utf8");
528
+ child.stdout.on("data", (chunk2) => {
529
+ stdout += String(chunk2);
530
+ });
531
+ child.stderr.on("data", (chunk2) => {
532
+ stderr += String(chunk2);
533
+ });
534
+ child.on("error", () => resolveResult({ code: -1, stdout: "", stderr: "git unavailable" }));
535
+ child.on("close", (code) => resolveResult({ code: code ?? 0, stdout, stderr }));
536
+ });
537
+ }
538
+ function normalize2(path) {
539
+ return (sep === "\\" ? path.replace(/\\/g, "/") : path).replace(/^\.\//, "");
540
+ }
541
+
542
+ // src/indexer/engine.ts
543
+ var MAX_FILE_BYTES = 512e3;
544
+ var RACY_WINDOW_MS = 3e3;
545
+ async function indexRepository(options = {}) {
546
+ return (await refreshIndex(options)).result;
547
+ }
548
+ async function loadFreshIndex(repositoryRoot) {
549
+ try {
550
+ const { result, manifest } = await refreshIndex({ repositoryRoot });
551
+ const delta = result.added + result.changed + result.removed;
552
+ const failures = result.errors.length > 0 ? `; ${result.errors.length} file(s) could not be validated and were omitted` : "";
553
+ return {
554
+ manifest,
555
+ explain: delta === 0 ? `index: ${result.totalIndexed} indexed file(s) match current source${failures}` : `index: refreshed against current source (${result.changed} changed, ${result.added} added, ${result.removed} removed; ${result.totalIndexed} indexed)${failures}`
556
+ };
557
+ } catch (error) {
558
+ return {
559
+ manifest: emptyManifest(),
560
+ explain: `index: freshness check failed (${error.message}); indexed evidence omitted`
561
+ };
562
+ }
563
+ }
564
+ var passes = /* @__PURE__ */ new Map();
565
+ function refreshIndex(options) {
566
+ const key = resolve4(options.repositoryRoot ?? process.cwd());
567
+ const next = (passes.get(key) ?? Promise.resolve()).catch(() => void 0).then(() => runIndexPass(options));
568
+ passes.set(key, next);
569
+ void next.finally(() => {
570
+ if (passes.get(key) === next) passes.delete(key);
571
+ }).catch(() => void 0);
572
+ return next;
573
+ }
574
+ async function runIndexPass(options) {
575
+ const start = Date.now();
576
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
577
+ const repositoryRoot = resolve4(options.repositoryRoot ?? process.cwd());
578
+ const store = new IndexStore(repositoryRoot);
579
+ const [files, previous] = await Promise.all([listIndexableFiles(repositoryRoot), store.read()]);
580
+ const entries = {};
581
+ const errors = [];
582
+ let added = 0;
583
+ let changed = 0;
584
+ let unchanged = 0;
585
+ let skipped = 0;
586
+ let dirty = previous.generatedAt === emptyManifest().generatedAt;
587
+ for (const path of files) {
588
+ try {
589
+ const info = await stat2(join5(repositoryRoot, path));
590
+ if (!info.isFile()) continue;
591
+ if (info.size > MAX_FILE_BYTES) {
592
+ skipped++;
593
+ continue;
594
+ }
595
+ const existing = previous.entries[path];
596
+ if (!options.full && existing && statUnchanged(existing, info)) {
597
+ entries[path] = existing;
598
+ unchanged++;
599
+ continue;
600
+ }
601
+ const content = await readFile4(join5(repositoryRoot, path), "utf8");
602
+ const hash = hashContent(content);
603
+ const metadata = trustedMetadata(info);
604
+ dirty = true;
605
+ if (!options.full && existing && existing.hash === hash) {
606
+ const { mtimeMs: _mtime, ctimeMs: _ctime, ...rest } = existing;
607
+ entries[path] = { ...rest, size: info.size, ...metadata };
608
+ unchanged++;
609
+ continue;
610
+ }
611
+ const { language, lines, symbols, summary } = summarizeSource(path, content);
612
+ const symbolTable = supportsSymbols(path) ? extractSymbolTable(content, path) : [];
613
+ entries[path] = {
614
+ path,
615
+ hash,
616
+ size: info.size,
617
+ ...metadata,
618
+ language,
619
+ lines,
620
+ symbols,
621
+ ...symbolTable.length > 0 ? { symbolTable } : {},
622
+ summary,
623
+ indexedAt: now().toISOString()
624
+ };
625
+ if (existing) changed++;
626
+ else added++;
627
+ } catch (error) {
628
+ errors.push({ path, error: error.message });
629
+ skipped++;
630
+ }
631
+ }
632
+ const removed = Object.keys(previous.entries).filter((path) => !(path in entries)).length;
633
+ const manifest = {
634
+ version: INDEX_MANIFEST_VERSION,
635
+ generatedAt: now().toISOString(),
636
+ entries: sortEntries(entries)
637
+ };
638
+ if (dirty || removed > 0) await store.write(manifest);
639
+ const architecture = await refreshArchitectureCache(repositoryRoot, manifest);
640
+ const result = {
641
+ scanned: files.length,
642
+ added,
643
+ changed,
644
+ unchanged,
645
+ removed,
646
+ skipped,
647
+ errors,
648
+ durationMs: Date.now() - start,
649
+ totalIndexed: Object.keys(entries).length,
650
+ architecture: {
651
+ source: architecture.source,
652
+ readFiles: architecture.stats.readFiles,
653
+ reusedFiles: architecture.stats.reusedFiles
654
+ }
655
+ };
656
+ return { result, manifest };
657
+ }
658
+ function statUnchanged(entry, info) {
659
+ return entry.mtimeMs !== void 0 && entry.size === info.size && entry.mtimeMs === info.mtimeMs && entry.ctimeMs === info.ctimeMs;
660
+ }
661
+ function trustedMetadata(info) {
662
+ if (Date.now() - Math.max(info.mtimeMs, info.ctimeMs) < RACY_WINDOW_MS) return {};
663
+ return { mtimeMs: info.mtimeMs, ctimeMs: info.ctimeMs };
664
+ }
665
+ function hashContent(content) {
666
+ return createHash("sha256").update(content, "utf8").digest("hex");
667
+ }
668
+ function sortEntries(entries) {
669
+ return Object.fromEntries(
670
+ Object.keys(entries).sort().map((key) => [key, entries[key]])
671
+ );
672
+ }
673
+
18
674
  // src/integrations/graphify.ts
19
675
  import { execFile } from "child_process";
20
- import { mkdir, mkdtemp, readFile as readFile2, rm, stat, utimes } from "fs/promises";
676
+ import { mkdir as mkdir3, mkdtemp, readFile as readFile6, rm, stat as stat3, utimes } from "fs/promises";
21
677
  import { tmpdir } from "os";
22
- import { dirname, join, relative, resolve, sep } from "path";
678
+ import { dirname as dirname4, join as join6, relative as relative3, resolve as resolve5, sep as sep2 } from "path";
23
679
  import { promisify } from "util";
24
680
 
25
681
  // src/map/graph-freshness.ts
26
- import { createHash } from "crypto";
27
- import { readFile } from "fs/promises";
28
- var digest = (value) => createHash("sha256").update(value).digest("hex");
682
+ import { createHash as createHash2 } from "crypto";
683
+ import { readFile as readFile5 } from "fs/promises";
684
+ var digest = (value) => createHash2("sha256").update(value).digest("hex");
29
685
  async function writeGraphSourceSnapshot(path, manifest) {
30
686
  await writeAtomic(
31
687
  `${path}.sources.json`,
32
688
  JSON.stringify({
33
689
  version: 1,
34
- graphHash: digest(await readFile(path, "utf8")),
690
+ graphHash: digest(await readFile5(path, "utf8")),
35
691
  sources: Object.fromEntries(
36
692
  Object.values(manifest.entries).map((entry) => [entry.path, entry.hash])
37
693
  )
@@ -41,13 +697,13 @@ async function writeGraphSourceSnapshot(path, manifest) {
41
697
  async function readGraphSourceSnapshot(path) {
42
698
  let raw;
43
699
  try {
44
- raw = await readFile(`${path}.sources.json`, "utf8");
700
+ raw = await readFile5(`${path}.sources.json`, "utf8");
45
701
  } catch (error) {
46
702
  return error.code === "ENOENT" ? null : {};
47
703
  }
48
704
  try {
49
705
  const snapshot = JSON.parse(raw);
50
- if (snapshot.version !== 1 || snapshot.graphHash !== digest(await readFile(path, "utf8")) || !snapshot.sources || typeof snapshot.sources !== "object" || Array.isArray(snapshot.sources))
706
+ if (snapshot.version !== 1 || snapshot.graphHash !== digest(await readFile5(path, "utf8")) || !snapshot.sources || typeof snapshot.sources !== "object" || Array.isArray(snapshot.sources))
51
707
  return {};
52
708
  return Object.fromEntries(
53
709
  Object.entries(snapshot.sources).filter(
@@ -134,17 +790,17 @@ async function graphifyGraphExists(repositoryRoot, graphPath = DEFAULT_GRAPH_PAT
134
790
  }
135
791
  async function generateGraphifyGraph(options) {
136
792
  const run = options.run ?? runCommand;
137
- const repositoryRoot = resolve(options.repositoryRoot);
793
+ const repositoryRoot = resolve5(options.repositoryRoot);
138
794
  const graphPath = options.graphPath ?? DEFAULT_GRAPH_PATH;
139
795
  const destination = resolveGraphPath(repositoryRoot, graphPath);
140
- await mkdir(dirname(destination), { recursive: true });
141
- await ignoreGeneratedGraphDirectory(dirname(destination));
796
+ await mkdir3(dirname4(destination), { recursive: true });
797
+ await ignoreGeneratedGraphDirectory(dirname4(destination));
142
798
  const { manifest: sourceSnapshot } = await loadFreshIndex(repositoryRoot);
143
799
  const current = await readGraphFileIfPresent(destination);
144
800
  const isDefaultPath = graphPath === DEFAULT_GRAPH_PATH;
145
801
  if (current && isDefaultPath) {
146
- const backup = await readFile2(destination, "utf8");
147
- const backupStat = await stat(destination);
802
+ const backup = await readFile6(destination, "utf8");
803
+ const backupStat = await stat3(destination);
148
804
  try {
149
805
  await run("graphify", ["update", repositoryRoot, "--force", "--no-cluster"], repositoryRoot);
150
806
  const updated = await readGraphFile(destination);
@@ -158,16 +814,16 @@ async function generateGraphifyGraph(options) {
158
814
  );
159
815
  }
160
816
  }
161
- const stagingRoot = await mkdtemp(join(tmpdir(), "codebuddy-graphify-"));
817
+ const stagingRoot = await mkdtemp(join6(tmpdir(), "codebuddy-graphify-"));
162
818
  try {
163
819
  await run(
164
820
  "graphify",
165
821
  ["extract", repositoryRoot, "--out", stagingRoot, "--code-only", "--no-cluster"],
166
822
  repositoryRoot
167
823
  );
168
- const stagedPath = join(stagingRoot, DEFAULT_GRAPH_PATH);
824
+ const stagedPath = join6(stagingRoot, DEFAULT_GRAPH_PATH);
169
825
  const staged = await readGraphFile(stagedPath);
170
- await writeAtomic(destination, await readFile2(stagedPath, "utf8"));
826
+ await writeAtomic(destination, await readFile6(stagedPath, "utf8"));
171
827
  await writeGraphSourceSnapshot(destination, sourceSnapshot);
172
828
  return { graphPath, generated: true, ...staged };
173
829
  } finally {
@@ -175,17 +831,17 @@ async function generateGraphifyGraph(options) {
175
831
  }
176
832
  }
177
833
  async function ignoreGeneratedGraphDirectory(directory) {
178
- const path = join(directory, ".gitignore");
834
+ const path = join6(directory, ".gitignore");
179
835
  try {
180
- await stat(path);
836
+ await stat3(path);
181
837
  } catch {
182
838
  await writeAtomic(path, "# CodeBuddy-generated Graphify output (derived, rebuildable)\n*\n");
183
839
  }
184
840
  }
185
841
  function resolveGraphPath(repositoryRoot, graphPath) {
186
- const absolute = resolve(repositoryRoot, graphPath);
187
- const relativePath = relative(repositoryRoot, absolute);
188
- if (relativePath === "" || relativePath === ".." || relativePath.startsWith(`..${sep}`)) {
842
+ const absolute = resolve5(repositoryRoot, graphPath);
843
+ const relativePath = relative3(repositoryRoot, absolute);
844
+ if (relativePath === "" || relativePath === ".." || relativePath.startsWith(`..${sep2}`)) {
189
845
  throw new Error(`Graph path must be inside the project root: ${graphPath}`);
190
846
  }
191
847
  return absolute;
@@ -198,10 +854,10 @@ async function readGraphFileIfPresent(path) {
198
854
  }
199
855
  }
200
856
  async function readGraphFile(path) {
201
- const info = await stat(path);
857
+ const info = await stat3(path);
202
858
  if (!info.isFile() || info.size === 0)
203
859
  throw new Error(`Graphify graph is missing or empty: ${path}`);
204
- const raw = JSON.parse(await readFile2(path, "utf8"));
860
+ const raw = JSON.parse(await readFile6(path, "utf8"));
205
861
  const nodes = arrayLength(raw.nodes ?? raw.entities);
206
862
  const edges = arrayLength(raw.edges ?? raw.links ?? raw.relationships);
207
863
  if (nodes === 0) throw new Error(`Graphify graph contains no nodes: ${path}`);
@@ -212,16 +868,16 @@ function arrayLength(value) {
212
868
  }
213
869
 
214
870
  // src/context/file-vectors.ts
215
- import { mkdir as mkdir3, readFile as readFile6 } from "fs/promises";
216
- import { dirname as dirname3, join as join4 } from "path";
871
+ import { mkdir as mkdir5, readFile as readFile10 } from "fs/promises";
872
+ import { dirname as dirname6, join as join9 } from "path";
217
873
 
218
874
  // src/context/discovery.ts
219
- import { readFile as readFile5, realpath, stat as stat2 } from "fs/promises";
220
- import { isAbsolute as isAbsolute3, join as join3, relative as relative4 } from "path";
875
+ import { readFile as readFile9, realpath, stat as stat4 } from "fs/promises";
876
+ import { isAbsolute as isAbsolute3, join as join8, relative as relative6 } from "path";
221
877
 
222
878
  // src/map/graphify.ts
223
- import { readFile as readFile3 } from "fs/promises";
224
- import { isAbsolute, relative as relative2, resolve as resolve2 } from "path";
879
+ import { readFile as readFile7 } from "fs/promises";
880
+ import { isAbsolute, relative as relative4, resolve as resolve6 } from "path";
225
881
  var NON_DEPENDENCY_RELATIONS = /* @__PURE__ */ new Set([
226
882
  "contains",
227
883
  "part_of",
@@ -255,7 +911,7 @@ var DIRECT_DEPENDENCY_RELATIONS = /* @__PURE__ */ new Set([
255
911
  ]);
256
912
  var MAX_EDGE_SYMBOLS = 8;
257
913
  async function loadGraphifyMap(graphPath, repositoryRoot = process.cwd()) {
258
- const raw = JSON.parse(await readFile3(graphPath, "utf8"));
914
+ const raw = JSON.parse(await readFile7(graphPath, "utf8"));
259
915
  const nodes = arrayRecord(raw.nodes ?? raw.entities);
260
916
  const edges = arrayRecord(raw.edges ?? raw.links ?? raw.relationships);
261
917
  const nodePaths = /* @__PURE__ */ new Map();
@@ -268,7 +924,7 @@ async function loadGraphifyMap(graphPath, repositoryRoot = process.cwd()) {
268
924
  nodePaths.set(id, path);
269
925
  const label = stringValue(node.label ?? node.name);
270
926
  if (label && label !== path && !path.endsWith(`/${label}`)) nodeLabels.set(id, label);
271
- modules.push({ path, language: languageFor(path), imports: [] });
927
+ modules.push({ path, language: languageFor2(path), imports: [] });
272
928
  }
273
929
  const modulePaths = new Set(modules.map((module) => module.path));
274
930
  const importedEdges = [];
@@ -348,21 +1004,21 @@ function graphifyPath(node, repositoryRoot) {
348
1004
  return candidate ? normalizeGraphPath(candidate, repositoryRoot) : null;
349
1005
  }
350
1006
  function normalizeGraphPath(path, repositoryRoot) {
351
- const absolute = isAbsolute(path) ? resolve2(path) : resolve2(repositoryRoot, path);
352
- const relativePath = relative2(resolve2(repositoryRoot), absolute).replace(/\\/g, "/");
1007
+ const absolute = isAbsolute(path) ? resolve6(path) : resolve6(repositoryRoot, path);
1008
+ const relativePath = relative4(resolve6(repositoryRoot), absolute).replace(/\\/g, "/");
353
1009
  if (relativePath === "" || relativePath === ".." || relativePath.startsWith("../")) return null;
354
1010
  return relativePath.replace(/^\.\//, "");
355
1011
  }
356
1012
  function stringValue(value) {
357
1013
  return typeof value === "string" && value.length > 0 ? value : null;
358
1014
  }
359
- function languageFor(path) {
1015
+ function languageFor2(path) {
360
1016
  if (/\.py$/.test(path)) return "py";
361
1017
  return /\.tsx?$/.test(path) ? "ts" : "js";
362
1018
  }
363
1019
 
364
1020
  // src/context/anchors.ts
365
- import { isAbsolute as isAbsolute2, relative as relative3, resolve as resolve3, sep as sep2 } from "path";
1021
+ import { isAbsolute as isAbsolute2, relative as relative5, resolve as resolve7, sep as sep3 } from "path";
366
1022
  var MAX_ANCHORS = 3;
367
1023
  var MAX_ERROR_SEARCH_FILES = 2e3;
368
1024
  var JS_FRAME = /\bat\s+(?:[^\s()]+\s+\()?([^\s()]+?):(\d+):\d+\)?/g;
@@ -370,7 +1026,7 @@ var PY_FRAME = /\bFile\s+"([^"]+)",\s+line\s+(\d+)/g;
370
1026
  var PATH_TOKEN = /(?:^|[\s(`'"[])((?:\.{0,2}\/)?[\w@.-]+(?:\/[\w@.-]+)*\.[A-Za-z0-9]+)(?::(\d+))?/g;
371
1027
  var QUOTED = /"([^"\n]{8,200})"|'([^'\n]{8,200})'|`([^`\n]{8,200})`/g;
372
1028
  async function extractTaskAnchors(repositoryRoot, task, paths) {
373
- const root = resolve3(repositoryRoot);
1029
+ const root = resolve7(repositoryRoot);
374
1030
  const found = [];
375
1031
  const add = (raw, line, kind, at) => {
376
1032
  const path = toIndexedPath(root, raw, paths);
@@ -398,9 +1054,9 @@ async function extractTaskAnchors(repositoryRoot, task, paths) {
398
1054
  return anchors.slice(0, MAX_ANCHORS);
399
1055
  }
400
1056
  function toIndexedPath(root, raw, paths) {
401
- const absolute = isAbsolute2(raw) ? raw : resolve3(root, raw);
402
- const rel = relative3(root, absolute);
403
- if (rel === "" || rel.startsWith(`..${sep2}`) || rel === ".." || isAbsolute2(rel)) return null;
1057
+ const absolute = isAbsolute2(raw) ? raw : resolve7(root, raw);
1058
+ const rel = relative5(root, absolute);
1059
+ if (rel === "" || rel.startsWith(`..${sep3}`) || rel === ".." || isAbsolute2(rel)) return null;
404
1060
  const normalized = rel.replace(/\\/g, "/");
405
1061
  return paths.has(normalized) ? normalized : null;
406
1062
  }
@@ -426,8 +1082,8 @@ async function errorAnchors(root, task, paths) {
426
1082
  }
427
1083
 
428
1084
  // src/context/content-index.ts
429
- import { mkdir as mkdir2, readFile as readFile4 } from "fs/promises";
430
- import { dirname as dirname2, join as join2 } from "path";
1085
+ import { mkdir as mkdir4, readFile as readFile8 } from "fs/promises";
1086
+ import { dirname as dirname5, join as join7 } from "path";
431
1087
  var PASSAGE_LINES = 40;
432
1088
  var K1 = 1.2;
433
1089
  var B = 0.75;
@@ -520,11 +1176,11 @@ function idfCoverage(text, terms, idf) {
520
1176
  return terms.reduce((sum, term) => sum + (words.has(term) ? idf.get(term) ?? 0 : 0), 0) / total;
521
1177
  }
522
1178
  function cachePath(repositoryRoot) {
523
- return join2(repositoryRoot, ".codebuddy", "cache", "content-terms.json");
1179
+ return join7(repositoryRoot, ".codebuddy", "cache", "content-terms.json");
524
1180
  }
525
1181
  async function loadCache(repositoryRoot) {
526
1182
  try {
527
- const parsed = JSON.parse(await readFile4(cachePath(repositoryRoot), "utf8"));
1183
+ const parsed = JSON.parse(await readFile8(cachePath(repositoryRoot), "utf8"));
528
1184
  if (parsed.version === 2 && parsed.files && typeof parsed.files === "object") return parsed;
529
1185
  } catch {
530
1186
  }
@@ -533,7 +1189,7 @@ async function loadCache(repositoryRoot) {
533
1189
  async function saveCache(repositoryRoot, cache) {
534
1190
  try {
535
1191
  const path = cachePath(repositoryRoot);
536
- await mkdir2(dirname2(path), { recursive: true });
1192
+ await mkdir4(dirname5(path), { recursive: true });
537
1193
  await writeAtomic(path, JSON.stringify(cache));
538
1194
  } catch {
539
1195
  }
@@ -547,15 +1203,16 @@ var MAX_EVIDENCE_BYTES = 512e3;
547
1203
  async function readBoundedSource(repositoryRoot, path) {
548
1204
  if (isCredentialPath(path)) return null;
549
1205
  try {
550
- const absolute = join3(repositoryRoot, path);
551
- if ((await stat2(absolute)).size > MAX_EVIDENCE_BYTES) return null;
552
- const source = await readFile5(absolute, "utf8");
1206
+ const absolute = join8(repositoryRoot, path);
1207
+ if ((await stat4(absolute)).size > MAX_EVIDENCE_BYTES) return null;
1208
+ const source = await readFile9(absolute, "utf8");
553
1209
  return source.includes("\0") ? null : source;
554
1210
  } catch {
555
1211
  return null;
556
1212
  }
557
1213
  }
558
1214
  async function discoverTaskPaths(repositoryRoot, task, manifest, mapSource, options = {}) {
1215
+ task = stripIssueTemplate(task);
559
1216
  const anchors = await extractTaskAnchors(
560
1217
  repositoryRoot,
561
1218
  task,
@@ -597,7 +1254,7 @@ async function withLlmSummaries(repositoryRoot, manifest) {
597
1254
  const { resolveLlmConfig } = await import("./config-GEZFLXK6.js");
598
1255
  const config = await resolveLlmConfig(repositoryRoot);
599
1256
  if (!config.enabled || !config.features.summaries) return entries;
600
- const { loadLlmSummaries } = await import("./summaries-ML53R7EL.js");
1257
+ const { loadLlmSummaries } = await import("./summaries-32PHOB6L.js");
601
1258
  const summaries = await loadLlmSummaries(repositoryRoot, manifest);
602
1259
  if (summaries.size === 0) return entries;
603
1260
  return entries.map((entry) => {
@@ -614,9 +1271,13 @@ async function rankTaskFiles(repositoryRoot, task, manifest, mapSource, options)
614
1271
  (entry) => wantsChangelog || !CHANGELOG_PATH.test(entry.path)
615
1272
  );
616
1273
  const wantsTests = /\b(tests?|spec)\b/i.test(task);
617
- const identifiers = identifierTokens(task).filter((token) => CODE_IDENTIFIER.test(token));
1274
+ const identifiers = identifierTokens(task).filter(
1275
+ (token) => CODE_IDENTIFIER.test(token) && !ABBREVIATION.test(token)
1276
+ );
618
1277
  if (identifiers.length > 0) {
619
- const exact = entries.filter((entry) => wantsTests || !TEST_PATH.test(entry.path)).map((entry) => ({
1278
+ const exact = entries.filter(
1279
+ (entry) => (wantsTests || !TEST_PATH.test(entry.path)) && countsAsSource(entry.path, task) && !EXCEPTION_MODULE.test(entry.path)
1280
+ ).map((entry) => ({
620
1281
  path: entry.path,
621
1282
  named: new Set(
622
1283
  (entry.symbolTable ?? []).flatMap(
@@ -664,7 +1325,7 @@ async function rankTaskFiles(repositoryRoot, task, manifest, mapSource, options)
664
1325
  reason: "semantic retrieval off",
665
1326
  stale: 0,
666
1327
  changed: 0
667
- } : await import("./zvec-XDZNZ2OL.js").then(
1328
+ } : await import("./zvec-24HLAFGN.js").then(
668
1329
  ({ zvecCandidates }) => zvecCandidates(
669
1330
  repositoryRoot,
670
1331
  task,
@@ -888,7 +1549,7 @@ async function commandFiles(repositoryRoot, task, entries) {
888
1549
  var COMMAND_DIR = /(^|\/)(cli|cmd|commands?)\//;
889
1550
  async function packageBins(repositoryRoot) {
890
1551
  try {
891
- const manifest = JSON.parse(await readFile5(join3(repositoryRoot, "package.json"), "utf8"));
1552
+ const manifest = JSON.parse(await readFile9(join8(repositoryRoot, "package.json"), "utf8"));
892
1553
  if (typeof manifest.bin === "string" && typeof manifest.name === "string") {
893
1554
  return [manifest.name.slice(manifest.name.lastIndexOf("/") + 1)];
894
1555
  }
@@ -1058,6 +1719,17 @@ function foldPlural(word) {
1058
1719
  if (/(ss|sh|ch|x|z)es$/.test(word)) return word.slice(0, -2);
1059
1720
  return word.slice(0, -1);
1060
1721
  }
1722
+ var ISSUE_TEMPLATE = [
1723
+ /_No response_/gi,
1724
+ /\(Please specify[^)]*\)/gi,
1725
+ /-\s*\[[ xX]\]\s*[\w.+-]+/g,
1726
+ /\d+\.\s*(?:Go to|Click on|Scroll down to)\s*'\.+'|\bSee error\b/gi,
1727
+ /#{1,6}\s*[\w-]+ Version\b(?:\s*v?\d+(?:\.\d+)+[\w.-]*,?)*/gi,
1728
+ /#{1,6}\s*(?:Description|Summary|URL to code causing the issue|MCVE|Minimal reproducible example|Steps to reproduce|Expected behaviou?r|Actual behaviou?r|Screenshots|Logs|Environment|Platform|Additional context)\b/gi
1729
+ ];
1730
+ function stripIssueTemplate(task) {
1731
+ return ISSUE_TEMPLATE.reduce((text, pattern) => text.replace(pattern, " "), task);
1732
+ }
1061
1733
  function taskTerms(task) {
1062
1734
  return [
1063
1735
  ...new Set(
@@ -1070,6 +1742,8 @@ function countTerms(text, terms) {
1070
1742
  const words = new Set(splitWords(text));
1071
1743
  return terms.filter((term) => words.has(term)).length;
1072
1744
  }
1745
+ var ABBREVIATION = /^[a-z](?:\.[a-z])+$/i;
1746
+ var EXCEPTION_MODULE = /(^|\/)(exceptions?|errors?)(\/|\.[a-z]+$)/i;
1073
1747
  var CODE_IDENTIFIER = /[a-z0-9][A-Z]|[_$.]/;
1074
1748
  function identifierTokens(task) {
1075
1749
  const tokens = task.match(/[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*/g) ?? [];
@@ -1083,24 +1757,24 @@ async function loadProjectArchitectureMap(repositoryRoot, manifestSource) {
1083
1757
  const builtIn = (await loadArchitectureMap(repositoryRoot, manifest)).map;
1084
1758
  if (config.graphify?.enabled && await graphifyGraphExists(repositoryRoot, config.graphify.graphPath)) {
1085
1759
  try {
1086
- const graphPath = join3(
1760
+ const graphPath = join8(
1087
1761
  repositoryRoot,
1088
1762
  config.graphify.graphPath ?? "graphify-out/graph.json"
1089
1763
  );
1090
- const [graph, graphFile, sourceHashes] = await Promise.all([
1764
+ const [graph, graphFile, sourceHashes2] = await Promise.all([
1091
1765
  loadGraphifyMap(graphPath, repositoryRoot),
1092
- stat2(graphPath),
1766
+ stat4(graphPath),
1093
1767
  readGraphSourceSnapshot(graphPath)
1094
1768
  ]);
1095
1769
  const livePaths = new Set(
1096
1770
  (await Promise.all(
1097
1771
  graph.modules.map(async (module) => {
1098
1772
  try {
1099
- const absolute = await realpath(join3(repositoryRoot, module.path));
1100
- const inside = relative4(await realpath(repositoryRoot), absolute);
1773
+ const absolute = await realpath(join8(repositoryRoot, module.path));
1774
+ const inside = relative6(await realpath(repositoryRoot), absolute);
1101
1775
  if (inside.startsWith("..") || isAbsolute3(inside)) return null;
1102
- const source = await stat2(absolute);
1103
- const fresh = sourceHashes === null ? source.mtimeMs <= graphFile.mtimeMs : sourceHashes[module.path] !== void 0 && sourceHashes[module.path] === manifest.entries[module.path]?.hash;
1776
+ const source = await stat4(absolute);
1777
+ const fresh = sourceHashes2 === null ? source.mtimeMs <= graphFile.mtimeMs : sourceHashes2[module.path] !== void 0 && sourceHashes2[module.path] === manifest.entries[module.path]?.hash;
1104
1778
  return source.isFile() && fresh ? module.path : null;
1105
1779
  } catch {
1106
1780
  return null;
@@ -1251,11 +1925,11 @@ function dot(left, right) {
1251
1925
  return sum;
1252
1926
  }
1253
1927
  function storePath(repositoryRoot) {
1254
- return join4(repositoryRoot, ".codebuddy", "cache", "file-vectors.json");
1928
+ return join9(repositoryRoot, ".codebuddy", "cache", "file-vectors.json");
1255
1929
  }
1256
1930
  async function loadStore(repositoryRoot) {
1257
1931
  try {
1258
- const parsed = JSON.parse(await readFile6(storePath(repositoryRoot), "utf8"));
1932
+ const parsed = JSON.parse(await readFile10(storePath(repositoryRoot), "utf8"));
1259
1933
  if (parsed.version === 1 && parsed.model === MODEL && parsed.files) return parsed;
1260
1934
  } catch {
1261
1935
  }
@@ -1263,11 +1937,17 @@ async function loadStore(repositoryRoot) {
1263
1937
  }
1264
1938
  async function saveStore(repositoryRoot, store) {
1265
1939
  const path = storePath(repositoryRoot);
1266
- await mkdir3(dirname3(path), { recursive: true });
1940
+ await mkdir5(dirname6(path), { recursive: true });
1267
1941
  await writeAtomic(path, JSON.stringify(store));
1268
1942
  }
1269
1943
 
1270
1944
  export {
1945
+ buildArchitectureMap,
1946
+ queryMap,
1947
+ neighbours,
1948
+ indexRepository,
1949
+ loadFreshIndex,
1950
+ hashContent,
1271
1951
  setupGraphify,
1272
1952
  generateGraphifyGraph,
1273
1953
  loadGraphifyMap,
@@ -1284,4 +1964,4 @@ export {
1284
1964
  namedSegments,
1285
1965
  loadProjectArchitectureMap
1286
1966
  };
1287
- //# sourceMappingURL=chunk-TLWGZVPC.js.map
1967
+ //# sourceMappingURL=chunk-YNSTIMCF.js.map