@mutmutco/cli 3.139.0 → 3.139.2

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.
@@ -0,0 +1,684 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/repo-index-v4/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ REPO_INDEX_V4_SCHEMA: () => REPO_INDEX_V4_SCHEMA,
34
+ V4_GRAPH_MAX_EDGES: () => V4_GRAPH_MAX_EDGES,
35
+ V4_GRAPH_MAX_EDGES_PER_FILE: () => V4_GRAPH_MAX_EDGES_PER_FILE,
36
+ V4_MAX_ARTIFACT_BYTES: () => V4_MAX_ARTIFACT_BYTES,
37
+ V4_MAX_CHUNKS: () => V4_MAX_CHUNKS,
38
+ V4_STAGE_MAX_BODY_BYTES: () => V4_STAGE_MAX_BODY_BYTES,
39
+ V4_STAGE_MAX_RECORDS: () => V4_STAGE_MAX_RECORDS,
40
+ buildGraphEdges: () => buildGraphEdges,
41
+ buildRepoIndexV4: () => buildRepoIndexV4,
42
+ buildStructuralChunks: () => buildStructuralChunks,
43
+ encodedStageBytes: () => encodedStageBytes,
44
+ graphEdgesForSource: () => graphEdgesForSource,
45
+ languageForPath: () => languageForPath,
46
+ repoIndexV4StorePath: () => repoIndexV4StorePath,
47
+ shardRepoIndexV4: () => shardRepoIndexV4,
48
+ structuralChunksForSource: () => structuralChunksForSource
49
+ });
50
+ module.exports = __toCommonJS(index_exports);
51
+
52
+ // src/repo-index-v4/chunks.ts
53
+ var import_node_crypto2 = require("node:crypto");
54
+ var import_node_fs2 = require("node:fs");
55
+ var import_node_path3 = require("node:path");
56
+
57
+ // src/doc-refs-core.ts
58
+ var import_node_child_process = require("node:child_process");
59
+ var CHECK_IGNORE_MAX_BUFFER = 32 * 1024 * 1024;
60
+ function defaultIsIgnored(root, relPaths, exec = import_node_child_process.execFileSync) {
61
+ const inRepo = relPaths.filter((p) => !p.startsWith(".."));
62
+ if (inRepo.length === 0) return /* @__PURE__ */ new Set();
63
+ try {
64
+ const out = exec("git", ["check-ignore", "--stdin"], {
65
+ cwd: root,
66
+ input: inRepo.join("\n"),
67
+ encoding: "utf8",
68
+ maxBuffer: CHECK_IGNORE_MAX_BUFFER
69
+ });
70
+ return new Set(out.split(/\r?\n/).filter(Boolean));
71
+ } catch (error) {
72
+ if (error?.status === 1) return /* @__PURE__ */ new Set();
73
+ if (error?.status === 128) return /* @__PURE__ */ new Set();
74
+ if (error?.code === "ENOENT") return /* @__PURE__ */ new Set();
75
+ if (error?.code === "ENOBUFS") {
76
+ throw new Error(
77
+ `git check-ignore produced more than ${CHECK_IGNORE_MAX_BUFFER} bytes for ${inRepo.length} path(s) \u2014 the ignore set cannot be read, and treating it as empty would index gitignored paths (#4062)`
78
+ );
79
+ }
80
+ throw error;
81
+ }
82
+ }
83
+
84
+ // src/repo-index.ts
85
+ var import_node_child_process2 = require("node:child_process");
86
+ var import_node_path2 = require("node:path");
87
+
88
+ // ../infra/repo-index-path-policy.mjs
89
+ var HARD_DENY = Object.freeze([
90
+ /(^|\/)\.env(\.|$)/i,
91
+ /(^|\/)\.env\./i,
92
+ /credentials/i,
93
+ /secrets?\.json$/i,
94
+ /\.pem$/i,
95
+ /\.p12$/i,
96
+ /\.key$/i,
97
+ /(^|\/)id_rsa/i,
98
+ /(^|\/)id_ed25519/i,
99
+ /\.keystore$/i
100
+ ]);
101
+ function isHardDeniedRepoIndexPath(value) {
102
+ return typeof value !== "string" || HARD_DENY.some((pattern) => pattern.test(value));
103
+ }
104
+ function isSafeRepoIndexPath(value) {
105
+ if (typeof value !== "string" || !value || value.length > 1024) return false;
106
+ if (value.startsWith("/") || value.includes("\\") || /[\u0000-\u001f\u007f]/u.test(value)) return false;
107
+ const segments = value.split("/");
108
+ if (segments.some((segment) => !segment || segment === "." || segment === "..")) return false;
109
+ return !isHardDeniedRepoIndexPath(value);
110
+ }
111
+
112
+ // src/repo-runtime-state.ts
113
+ var import_node_crypto = require("node:crypto");
114
+ var import_node_fs = require("node:fs");
115
+ var import_node_os = require("node:os");
116
+ var import_node_path = require("node:path");
117
+ var RUNTIME_DIR = "mmi-runtime";
118
+ function hashPath(path) {
119
+ return (0, import_node_crypto.createHash)("sha256").update((0, import_node_path.resolve)(path)).digest("hex").slice(0, 16);
120
+ }
121
+ function resolveGitDir(cwd) {
122
+ const dotGit = (0, import_node_path.join)(cwd, ".git");
123
+ try {
124
+ const st = (0, import_node_fs.statSync)(dotGit);
125
+ if (st.isDirectory()) return dotGit;
126
+ if (!st.isFile()) return void 0;
127
+ } catch {
128
+ return void 0;
129
+ }
130
+ try {
131
+ const raw = (0, import_node_fs.readFileSync)(dotGit, "utf8").trim();
132
+ const match = /^gitdir:\s*(.+)$/i.exec(raw);
133
+ if (!match) return void 0;
134
+ const gitdir = match[1].trim();
135
+ return (0, import_node_path.isAbsolute)(gitdir) ? gitdir : (0, import_node_path.resolve)(cwd, gitdir);
136
+ } catch {
137
+ return void 0;
138
+ }
139
+ }
140
+ function repoRuntimeStatePath(cwd, ...parts) {
141
+ const gitDir = resolveGitDir(cwd);
142
+ if (gitDir && (0, import_node_fs.existsSync)(gitDir)) return (0, import_node_path.join)(gitDir, RUNTIME_DIR, ...parts);
143
+ return (0, import_node_path.join)((0, import_node_os.tmpdir)(), "mmi-cli", hashPath(cwd), ...parts);
144
+ }
145
+
146
+ // src/repo-index.ts
147
+ var INDEXABLE_EXT = /* @__PURE__ */ new Set([
148
+ ".ts",
149
+ ".tsx",
150
+ ".js",
151
+ ".jsx",
152
+ ".mjs",
153
+ ".cjs",
154
+ ".py",
155
+ ".go",
156
+ ".rs",
157
+ ".java",
158
+ ".kt",
159
+ ".md",
160
+ ".yml",
161
+ ".yaml",
162
+ ".json",
163
+ ".toml",
164
+ ".sh",
165
+ ".ps1",
166
+ ".css",
167
+ ".html"
168
+ ]);
169
+ function isHardDeniedPath(relPosix) {
170
+ return isHardDeniedRepoIndexPath(relPosix);
171
+ }
172
+ function isIndexablePath(relPosix) {
173
+ if (!isSafeRepoIndexPath(relPosix)) return false;
174
+ if (relPosix.startsWith(".git/")) return false;
175
+ if (relPosix.includes("node_modules/")) return false;
176
+ if (relPosix.includes("dist/")) return false;
177
+ const dot = relPosix.lastIndexOf(".");
178
+ if (dot < 0) return false;
179
+ return INDEXABLE_EXT.has(relPosix.slice(dot).toLowerCase());
180
+ }
181
+ function toPosix(p) {
182
+ return p.split(import_node_path2.sep).join("/");
183
+ }
184
+ function listCandidatePaths(cwd, exec = import_node_child_process2.execFileSync) {
185
+ try {
186
+ const out = exec("git", ["ls-files", "-z", "-c", "-o", "--exclude-standard"], {
187
+ cwd,
188
+ encoding: "utf8",
189
+ maxBuffer: 64 * 1024 * 1024
190
+ });
191
+ return out.split("\0").filter(Boolean).map(toPosix);
192
+ } catch {
193
+ return [];
194
+ }
195
+ }
196
+
197
+ // src/repo-index-v4/language.ts
198
+ var LANGUAGE_BY_EXTENSION = {
199
+ ".ts": { name: "typescript", parserName: "typescript" },
200
+ ".tsx": { name: "tsx", parserName: "tsx" },
201
+ ".js": { name: "javascript", parserName: "javascript" },
202
+ ".jsx": { name: "jsx", parserName: "javascript" },
203
+ ".py": { name: "python", parserName: "python" },
204
+ ".go": { name: "go", parserName: "go" },
205
+ ".rs": { name: "rust", parserName: "rust" },
206
+ ".java": { name: "java", parserName: "java" },
207
+ ".kt": { name: "kotlin", parserName: "kotlin" }
208
+ };
209
+ function languageForPath(path) {
210
+ const dot = path.lastIndexOf(".");
211
+ return dot < 0 ? void 0 : LANGUAGE_BY_EXTENSION[path.slice(dot).toLowerCase()];
212
+ }
213
+
214
+ // src/repo-index-v4/chunks.ts
215
+ var BLURB_CAP = 200;
216
+ var SIGNATURE_CAP = 300;
217
+ var STRUCTURAL_KINDS = /* @__PURE__ */ new Set([
218
+ "function_declaration",
219
+ "function_definition",
220
+ "function_item",
221
+ "function",
222
+ "method_declaration",
223
+ "method_definition",
224
+ "method",
225
+ "class_declaration",
226
+ "class_definition",
227
+ "class",
228
+ "interface_declaration",
229
+ "interface_definition",
230
+ "enum_declaration",
231
+ "enum_definition",
232
+ "enum_item",
233
+ "struct_item",
234
+ "struct_specifier",
235
+ "trait_item",
236
+ "impl_item",
237
+ "type_declaration",
238
+ "type_alias_declaration",
239
+ "object_declaration"
240
+ ]);
241
+ var parserRuntime;
242
+ var grammarCache = /* @__PURE__ */ new Map();
243
+ async function parserFor(language) {
244
+ const api = await (parserRuntime ??= import("web-tree-sitter"));
245
+ await api.Parser.init();
246
+ let grammar = grammarCache.get(language);
247
+ if (!grammar) {
248
+ grammar = import("tree-sitter-wasm").then(({ getWasmPath }) => api.Language.load(getWasmPath(language)));
249
+ grammarCache.set(language, grammar);
250
+ }
251
+ const parser = new api.Parser();
252
+ parser.setLanguage(await grammar);
253
+ return parser;
254
+ }
255
+ function sha256(value) {
256
+ return (0, import_node_crypto2.createHash)("sha256").update(value).digest("hex");
257
+ }
258
+ function pointerId(path, kind, contentHash, start, end, symbol) {
259
+ return sha256(["repo-index-v4", path, kind, contentHash, String(start), String(end), symbol ?? ""].join("\0"));
260
+ }
261
+ function lineAt(bytes, offset) {
262
+ let line = 1;
263
+ for (let i = 0; i < Math.min(offset, bytes.length); i++) if (bytes[i] === 10) line++;
264
+ return line;
265
+ }
266
+ function lineRange(bytes, start, end) {
267
+ const startLine = lineAt(bytes, start);
268
+ const endLine = lineAt(bytes, Math.max(start, end - 1));
269
+ return { startLine, endLine: Math.max(startLine, endLine) };
270
+ }
271
+ function boundedLine(value, cap) {
272
+ const line = value?.replace(/\r?\n/g, " ").replace(/\s+/g, " ").trim();
273
+ return line ? line.slice(0, cap) : void 0;
274
+ }
275
+ function citation(repo, commit, path, bytes, start, end, symbol) {
276
+ return { repo, commit, path, ...lineRange(bytes, start, end), ...symbol ? { symbol } : {} };
277
+ }
278
+ function doclineBefore(bytes, start) {
279
+ const prior2 = Buffer.from(bytes.subarray(0, start)).toString("utf8").split(/\r?\n/);
280
+ for (let i = prior2.length - 1; i >= 0; i--) {
281
+ const line = prior2[i].trim();
282
+ if (!line) continue;
283
+ const text = line.replace(/^(?:\/\/\/|\/\/|#|\/\*\*?|\*)\s?/, "").replace(/\*\/$/, "").trim();
284
+ return text === line ? void 0 : boundedLine(text, BLURB_CAP);
285
+ }
286
+ return void 0;
287
+ }
288
+ function fileChunk(repo, commit, path, source, language) {
289
+ const bytes = Buffer.from(source, "utf8");
290
+ const contentHash = sha256(bytes);
291
+ return {
292
+ id: pointerId(path, "file", contentHash, 0, bytes.length),
293
+ path,
294
+ kind: "file",
295
+ contentHash,
296
+ ...language ? { language } : {},
297
+ citations: [citation(repo, commit, path, bytes, 0, bytes.length)]
298
+ };
299
+ }
300
+ function structuralNode(node) {
301
+ if (STRUCTURAL_KINDS.has(node.type)) return node;
302
+ return node.namedChildren.find((child) => STRUCTURAL_KINDS.has(child.type));
303
+ }
304
+ function structureChunk(repo, commit, path, sourceBytes, language, owner) {
305
+ const item = structuralNode(owner);
306
+ if (!item) return void 0;
307
+ const start = owner.startIndex;
308
+ const end = owner.endIndex;
309
+ if (start < 0 || end < start || end > sourceBytes.length) return void 0;
310
+ const contentHash = sha256(sourceBytes.subarray(start, end));
311
+ const name = boundedLine(item.childForFieldName("name")?.text, SIGNATURE_CAP);
312
+ const bodyStart = item.childForFieldName("body")?.startIndex ?? end;
313
+ const symbol = boundedLine(Buffer.from(sourceBytes.subarray(start, bodyStart)).toString("utf8"), SIGNATURE_CAP) ?? name;
314
+ return {
315
+ id: pointerId(path, "symbol", contentHash, start, end, symbol),
316
+ path,
317
+ kind: "symbol",
318
+ contentHash,
319
+ language,
320
+ ...symbol ? { symbol } : {},
321
+ ...doclineBefore(sourceBytes, start) ? { blurb: doclineBefore(sourceBytes, start) } : {},
322
+ citations: [citation(repo, commit, path, sourceBytes, start, end, name)]
323
+ };
324
+ }
325
+ async function structuralChunksForSource(repo, commit, path, source) {
326
+ const language = languageForPath(path);
327
+ if (!language) return [fileChunk(repo, commit, path, source)];
328
+ let parser;
329
+ let tree;
330
+ try {
331
+ parser = await parserFor(language.parserName);
332
+ tree = parser.parse(source) ?? void 0;
333
+ if (!tree || tree.rootNode.hasError) return [fileChunk(repo, commit, path, source, language.name)];
334
+ const bytes = Buffer.from(source, "utf8");
335
+ const chunks = tree.rootNode.namedChildren.map((node) => structureChunk(repo, commit, path, bytes, language.name, node)).filter((chunk) => chunk !== void 0).sort((a, b) => a.citations[0].startLine - b.citations[0].startLine || a.id.localeCompare(b.id));
336
+ return chunks.length ? chunks : [fileChunk(repo, commit, path, source, language.name)];
337
+ } catch {
338
+ return [fileChunk(repo, commit, path, source, language.name)];
339
+ } finally {
340
+ tree?.delete();
341
+ parser?.delete();
342
+ }
343
+ }
344
+ async function buildStructuralChunks(cwd, repo, commit) {
345
+ const candidates = listCandidatePaths(cwd).filter(isIndexablePath).sort((a, b) => a.localeCompare(b));
346
+ const ignored = defaultIsIgnored(cwd, candidates);
347
+ const chunks = [];
348
+ for (const path of candidates) {
349
+ if (ignored.has(path) || isHardDeniedPath(path)) continue;
350
+ const absolute = (0, import_node_path3.join)(cwd, ...path.split("/"));
351
+ if (!(0, import_node_fs2.existsSync)(absolute)) continue;
352
+ let source;
353
+ try {
354
+ source = (0, import_node_fs2.readFileSync)(absolute, "utf8");
355
+ } catch {
356
+ continue;
357
+ }
358
+ chunks.push(...await structuralChunksForSource(repo, commit, path, source));
359
+ }
360
+ return chunks.sort((a, b) => a.path.localeCompare(b.path) || a.citations[0].startLine - b.citations[0].startLine || a.id.localeCompare(b.id));
361
+ }
362
+
363
+ // src/repo-index-v4/types.ts
364
+ var REPO_INDEX_V4_SCHEMA = 4;
365
+
366
+ // src/repo-index-v4/builder.ts
367
+ var import_node_crypto3 = require("node:crypto");
368
+ var import_node_child_process3 = require("node:child_process");
369
+ var import_node_fs3 = require("node:fs");
370
+ var import_node_path4 = require("node:path");
371
+ var V4_MAX_CHUNKS = 1e4;
372
+ var V4_MAX_ARTIFACT_BYTES = 64 * 1024 * 1024;
373
+ var V4_EMBED_BATCH = 32;
374
+ var COMMIT = /^[a-f0-9]{40}$/;
375
+ function canonicalJson(value) {
376
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
377
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
378
+ const record = value;
379
+ return `{${Object.keys(record).sort().map((k) => `${JSON.stringify(k)}:${canonicalJson(record[k])}`).join(",")}}`;
380
+ }
381
+ function sha2562(value) {
382
+ return (0, import_node_crypto3.createHash)("sha256").update(canonicalJson(value)).digest("hex");
383
+ }
384
+ function statePath(cwd) {
385
+ return repoRuntimeStatePath(cwd, "repo-index", "v4.json");
386
+ }
387
+ function git(cwd, args) {
388
+ return String((0, import_node_child_process3.execFileSync)("git", args, { cwd, encoding: "utf8", windowsHide: true, stdio: ["ignore", "pipe", "ignore"] })).trim();
389
+ }
390
+ function gitInfo(cwd) {
391
+ const commit = git(cwd, ["rev-parse", "HEAD"]).toLowerCase();
392
+ if (!COMMIT.test(commit)) throw new Error("repo-index v4 requires an exact git HEAD commit");
393
+ let defaultBranch = "main";
394
+ try {
395
+ defaultBranch = git(cwd, ["symbolic-ref", "--short", "refs/remotes/origin/HEAD"]).replace(/^origin\//, "") || defaultBranch;
396
+ } catch {
397
+ }
398
+ let createdAt;
399
+ try {
400
+ createdAt = new Date(git(cwd, ["show", "-s", "--format=%cI", "HEAD"])).toISOString();
401
+ } catch {
402
+ createdAt = "1970-01-01T00:00:00.000Z";
403
+ }
404
+ return { commit, defaultBranch, createdAt };
405
+ }
406
+ function prior(cwd) {
407
+ try {
408
+ const p = JSON.parse((0, import_node_fs3.readFileSync)(statePath(cwd), "utf8"));
409
+ return p?.schemaVersion === 4 && p?.manifest?.immutable === true ? p : null;
410
+ } catch {
411
+ return null;
412
+ }
413
+ }
414
+ function tombstone(repo, commit, path, createdAt) {
415
+ const base = { repo, commit, path, reason: "deleted", createdAt };
416
+ return { ...base, id: sha2562(base) };
417
+ }
418
+ function embeddingInput(cwd, chunk) {
419
+ const c = chunk.citations[0];
420
+ if (!c) return `${chunk.path}
421
+ ${chunk.symbol ?? ""}
422
+ ${chunk.blurb ?? ""}`;
423
+ try {
424
+ const lines = (0, import_node_fs3.readFileSync)((0, import_node_path4.join)(cwd, ...chunk.path.split("/")), "utf8").split(/\r?\n/);
425
+ const body = lines.slice(Math.max(0, (c.startLine ?? 1) - 1), Math.min(lines.length, c.endLine ?? lines.length)).join("\n");
426
+ return body.slice(0, 1e5);
427
+ } catch {
428
+ return `${chunk.path}
429
+ ${chunk.symbol ?? ""}
430
+ ${chunk.blurb ?? ""}`;
431
+ }
432
+ }
433
+ function runEmbedder(cwd, chunks, modelDirectory, createdAt) {
434
+ if (!chunks.length) return { embeddings: [] };
435
+ const runner = (0, import_node_path4.join)(cwd, "repo-indexer", "src", "batch.mjs");
436
+ const fallback = (0, import_node_path4.join)(process.cwd(), "repo-indexer", "src", "batch.mjs");
437
+ const file = (0, import_node_fs3.existsSync)(runner) ? runner : fallback;
438
+ if (!(0, import_node_fs3.existsSync)(file)) return { embeddings: [], reason: "embeddings-unavailable" };
439
+ const request = { texts: chunks.map((chunk) => ({ id: chunk.id, text: embeddingInput(cwd, chunk) })), maxBatch: V4_EMBED_BATCH };
440
+ const env = { ...process.env, ...modelDirectory ? { MMI_REPO_INDEXER_MODEL_DIR: modelDirectory } : {} };
441
+ const result = (0, import_node_child_process3.spawnSync)(process.execPath, [file], { input: JSON.stringify(request), encoding: "utf8", windowsHide: true, timeout: 3e5, maxBuffer: V4_MAX_ARTIFACT_BYTES, env });
442
+ if (result.error || result.status !== 0) return { embeddings: [], reason: "embeddings-unavailable" };
443
+ try {
444
+ const response = JSON.parse(result.stdout);
445
+ if (!response.ok || !response.provenance || !Array.isArray(response.embeddings)) return { embeddings: [], reason: "embeddings-unavailable" };
446
+ const provenance = {
447
+ provider: response.provenance.provider,
448
+ model: response.provenance.model,
449
+ modelDigest: response.provenance.modelDigest,
450
+ dimensions: response.provenance.dimensions,
451
+ input: response.provenance.input,
452
+ createdAt
453
+ };
454
+ const byId = new Map(response.embeddings.map((e) => [e.id, e.vector]));
455
+ const embeddings = chunks.map((chunk) => ({ chunkId: chunk.id, vector: byId.get(chunk.id), provenance })).filter((e) => Array.isArray(e.vector) && e.vector.length === provenance.dimensions && e.vector.every(Number.isFinite));
456
+ return embeddings.length === chunks.length ? { embeddings } : { embeddings: [], reason: "partial-coverage" };
457
+ } catch {
458
+ return { embeddings: [], reason: "embeddings-unavailable" };
459
+ }
460
+ }
461
+ async function buildRepoIndexV4(cwd, repo, opts = {}) {
462
+ const { commit, defaultBranch, createdAt } = gitInfo(cwd);
463
+ const chunks = await buildStructuralChunks(cwd, repo, commit);
464
+ if (chunks.length > V4_MAX_CHUNKS) throw new Error(`repo-index v4 exceeds ${V4_MAX_CHUNKS} chunk ceiling`);
465
+ const old = prior(cwd);
466
+ const currentPaths = new Set(chunks.map((c) => c.path));
467
+ const tombstonePaths = [
468
+ ...(old?.manifest.tombstones ?? []).map((t) => t.path),
469
+ ...(old?.manifest.chunks ?? []).map((c) => c.path).filter((path2) => !currentPaths.has(path2))
470
+ ];
471
+ const tombstones = [...new Set(tombstonePaths)].sort().map((path2) => tombstone(repo, commit, path2, createdAt));
472
+ const oldChunkHashById = new Map((old?.manifest.chunks ?? []).map((c) => [c.id, c.contentHash]));
473
+ const reusableEmbedding = (embedding) => {
474
+ const norm = Math.hypot(...embedding.vector);
475
+ return embedding.vector.length === 384 && embedding.vector.every(Number.isFinite) && norm >= 0.98 && norm <= 1.02 && embedding.provenance.provider === "local" && embedding.provenance.modelDigest === "828e1496d7fabb79cfa4dcd84fa38625c0d3d21da474a00f08db0f559940cf35" && embedding.provenance.dimensions === 384 && embedding.provenance.input === "pointer-safe-structural-chunk";
476
+ };
477
+ const oldEmbeddingByHash = new Map((old?.manifest.embeddings ?? []).filter(reusableEmbedding).map((e) => [oldChunkHashById.get(e.chunkId), e]));
478
+ const reusable = chunks.flatMap((chunk) => {
479
+ const priorEmbedding = oldEmbeddingByHash.get(chunk.contentHash);
480
+ return priorEmbedding ? [{ ...priorEmbedding, chunkId: chunk.id }] : [];
481
+ });
482
+ const reusableIds = new Set(reusable.map((e) => e.chunkId));
483
+ const missing = chunks.filter((chunk) => !reusableIds.has(chunk.id));
484
+ const generated = missing.length === 0 ? { embeddings: [] } : opts.embed === false ? { embeddings: [], reason: "embeddings-unavailable" } : runEmbedder(cwd, missing, opts.modelDirectory, createdAt);
485
+ const embeddings = [...reusable, ...generated.embeddings].sort((a, b) => a.chunkId.localeCompare(b.chunkId));
486
+ const chunksDigest = sha2562(chunks);
487
+ const embeddingsDigest = sha2562(embeddings);
488
+ const artifact = (kind, digest) => {
489
+ const identity2 = { repo, commit, immutable: true, kind, uri: `s3://repo-index/v4/${repo}/${commit}/${digest}.${kind}.json`, sha256: digest, createdAt };
490
+ return { ...identity2, id: sha2562(identity2) };
491
+ };
492
+ const artifacts = [artifact("chunks", chunksDigest), artifact("embeddings", embeddingsDigest)];
493
+ const grammarProvenance = {
494
+ runtime: { name: "web-tree-sitter", version: "0.26.12", license: "MIT" },
495
+ grammarPack: { name: "tree-sitter-wasm", version: "1.1.4", license: "MIT", digest: "f3089ddf2c9615a423783b645c4dfb23ccda30807bbd059746f583952357c489" },
496
+ languages: ["go", "java", "javascript", "jsx", "kotlin", "python", "rust", "tsx", "typescript"]
497
+ };
498
+ const rrf = { algorithm: "reciprocal-rank-fusion", k: 60, lexicalWeight: 1, semanticWeight: 1 };
499
+ const identity = { repo, commit, defaultBranch, immutable: true, createdAt, grammarProvenance, chunks, embeddings, artifacts, tombstones, rrf };
500
+ const manifest = { ...identity, id: sha2562(identity) };
501
+ const complete = embeddings.length === chunks.length;
502
+ const envelope = {
503
+ schemaVersion: 4,
504
+ manifest,
505
+ status: { repo, commit, state: complete ? "ready" : "degraded", updatedAt: createdAt, ...!complete ? { degradedReasons: [generated.reason === "partial-coverage" ? "partial-coverage" : "embeddings-unavailable"] } : {} }
506
+ };
507
+ const encoded = canonicalJson(envelope);
508
+ if (Buffer.byteLength(encoded) > V4_MAX_ARTIFACT_BYTES) throw new Error(`repo-index v4 artifact exceeds ${V4_MAX_ARTIFACT_BYTES} byte ceiling`);
509
+ const path = statePath(cwd);
510
+ (0, import_node_fs3.mkdirSync)((0, import_node_path4.dirname)(path), { recursive: true });
511
+ (0, import_node_fs3.writeFileSync)(path, `${JSON.stringify(envelope, null, 2)}
512
+ `, "utf8");
513
+ return envelope;
514
+ }
515
+ function repoIndexV4StorePath(cwd) {
516
+ return statePath(cwd);
517
+ }
518
+
519
+ // src/repo-index-v4/shards.ts
520
+ var V4_STAGE_MAX_BODY_BYTES = 750 * 1024;
521
+ var V4_STAGE_MAX_RECORDS = 256;
522
+ var V4_STAGE_MAX_SHARDS = 128;
523
+ function encodedBytes(value) {
524
+ return Buffer.byteLength(JSON.stringify(value));
525
+ }
526
+ function split(kind, records, manifest) {
527
+ const groups = [];
528
+ let group = [];
529
+ for (const record of records) {
530
+ const next = [...group, record];
531
+ const probe = { repo: manifest.repo, commit: manifest.commit, manifestId: manifest.id, kind, index: 127, total: 128, digest: "0".repeat(64), records: next };
532
+ if (group.length && (group.length >= V4_STAGE_MAX_RECORDS || encodedBytes(probe) > V4_STAGE_MAX_BODY_BYTES)) {
533
+ groups.push(group);
534
+ group = [record];
535
+ } else {
536
+ group = next;
537
+ }
538
+ }
539
+ if (group.length || records.length === 0) groups.push(group);
540
+ if (groups.length > V4_STAGE_MAX_SHARDS) throw new Error(`repo-index v4 ${kind} requires ${groups.length} shards (max ${V4_STAGE_MAX_SHARDS})`);
541
+ return groups.map((items, index) => {
542
+ const request = {
543
+ repo: manifest.repo,
544
+ commit: manifest.commit,
545
+ manifestId: manifest.id,
546
+ kind,
547
+ index,
548
+ total: groups.length,
549
+ digest: sha2562(items),
550
+ records: items
551
+ };
552
+ if (encodedBytes(request) > V4_STAGE_MAX_BODY_BYTES) throw new Error(`repo-index v4 ${kind} shard ${index} exceeds ${V4_STAGE_MAX_BODY_BYTES} bytes`);
553
+ return request;
554
+ });
555
+ }
556
+ function shardRepoIndexV4(envelope) {
557
+ const { chunks, embeddings, ...header } = envelope.manifest;
558
+ const stages = [...split("chunks", chunks, envelope.manifest), ...split("embeddings", embeddings ?? [], envelope.manifest)];
559
+ const shards = stages.map(({ kind, index, total, digest, records }) => ({ kind, index, total, digest, count: records.length }));
560
+ return { stages, finalize: { repo: header.repo, commit: header.commit, manifestId: header.id, header, status: envelope.status, shards } };
561
+ }
562
+ function encodedStageBytes(stage) {
563
+ return encodedBytes(stage);
564
+ }
565
+
566
+ // src/repo-index-v4/edges.ts
567
+ var import_node_fs4 = require("node:fs");
568
+ var import_node_path5 = require("node:path");
569
+ var V4_GRAPH_MAX_EDGES = 5e3;
570
+ var V4_GRAPH_MAX_EDGES_PER_FILE = 64;
571
+ var SUPPORTED = /* @__PURE__ */ new Map([
572
+ [".js", "javascript"],
573
+ [".jsx", "javascript"],
574
+ [".mjs", "javascript"],
575
+ [".cjs", "javascript"],
576
+ [".ts", "typescript"],
577
+ [".tsx", "typescript"],
578
+ [".py", "python"],
579
+ [".go", "go"],
580
+ [".rs", "rust"],
581
+ [".cs", "csharp"],
582
+ [".md", "resource"],
583
+ [".json", "resource"],
584
+ [".yaml", "resource"],
585
+ [".yml", "resource"]
586
+ ]);
587
+ function extension(path) {
588
+ const index = path.lastIndexOf(".");
589
+ return index < 0 ? "" : path.slice(index).toLowerCase();
590
+ }
591
+ function lineAt2(source, offset) {
592
+ return source.slice(0, offset).split(/\r?\n/).length;
593
+ }
594
+ function normalizedRepo(value) {
595
+ return value.toLowerCase().replace(/^https?:\/\/github\.com\//, "").replace(/\.git(?:[#/?].*)?$/, "").replace(/[?#].*$/, "");
596
+ }
597
+ function targetRepo(specifier, roster) {
598
+ const direct = normalizedRepo(specifier).match(/(?:^|\/)mutmutco\/([a-z0-9._-]+)/)?.[1];
599
+ if (direct && roster.has(`mutmutco/${direct}`)) return `mutmutco/${direct}`;
600
+ const scoped = specifier.toLowerCase().match(/^@mutmutco\/([a-z0-9._-]+)/)?.[1];
601
+ if (scoped && roster.has(`mutmutco/${scoped}`)) return `mutmutco/${scoped}`;
602
+ const crate = specifier.toLowerCase().replace(/_/g, "-");
603
+ return roster.has(`mutmutco/${crate}`) ? `mutmutco/${crate}` : void 0;
604
+ }
605
+ function candidate(base) {
606
+ return { ...base, id: sha2562(base) };
607
+ }
608
+ function graphEdgesForSource(repo, commit, path, source, rosterRepos) {
609
+ const language = SUPPORTED.get(extension(path));
610
+ if (!language) return [];
611
+ const roster = new Set(rosterRepos.map((value) => value.toLowerCase()));
612
+ roster.delete(repo.toLowerCase());
613
+ const edges = [];
614
+ const seen = /* @__PURE__ */ new Set();
615
+ const push = (kind, offset, target, confidence, rule, symbol) => {
616
+ const targetName = targetRepo(target, roster);
617
+ if (!targetName) return;
618
+ const base = { kind, confidence, source: { repo, commit, path, line: lineAt2(source, offset), ...symbol ? { symbol } : {} }, target: { repo: targetName, ...symbol ? { symbol } : {} }, resolver: { name: "repo-index-v4-static", version: 1, language, rule } };
619
+ const edge = candidate(base);
620
+ if (!seen.has(edge.id) && edges.length < V4_GRAPH_MAX_EDGES_PER_FILE) {
621
+ seen.add(edge.id);
622
+ edges.push(edge);
623
+ }
624
+ };
625
+ const imports = [];
626
+ const patterns = [
627
+ /\bimport\s+(?:\{?\s*([A-Za-z_$][\w$]*)[^'";]*?\s+from\s+)?['"]([^'"]+)['"]/g,
628
+ /\brequire\(\s*['"]([^'"]+)['"]\s*\)/g,
629
+ /\bfrom\s+([A-Za-z0-9_.\/-]+)\s+import\s+([A-Za-z_]\w*)/g,
630
+ /\b(?:use|extern\s+crate)\s+([A-Za-z0-9_:\/-]+)/g,
631
+ /\bimport\s+"([^"]+)"/g,
632
+ /<ProjectReference\b[^>]*Include=["']([^"']+)["']/gi
633
+ ];
634
+ for (const pattern of patterns) for (const match of source.matchAll(pattern)) {
635
+ const values = match.slice(1).filter((value) => !!value);
636
+ const target = values.find((value) => targetRepo(value, roster));
637
+ if (!target || match.index === void 0) continue;
638
+ const binding = values.find((value) => value !== target && /^[A-Za-z_$][\w$]*$/.test(value));
639
+ imports.push({ binding, target });
640
+ push("import", match.index, target, 0.95, `import-${pattern.source.slice(0, 16)}`, binding);
641
+ }
642
+ for (const imported of imports) if (imported.binding) {
643
+ const call = new RegExp(`\\b${imported.binding.replace(/[$]/g, "\\$&")}\\s*\\(`, "g");
644
+ for (const match of source.matchAll(call)) if (match.index !== void 0) push("call", match.index, imported.target, 0.85, "import-bound-call", imported.binding);
645
+ }
646
+ const resource = /https?:\/\/github\.com\/mutmutco\/[A-Za-z0-9._-]+(?:\.git)?/g;
647
+ for (const match of source.matchAll(resource)) if (match.index !== void 0) push("resource", match.index, match[0], 1, "github-url");
648
+ return edges.sort((a, b) => a.source.path.localeCompare(b.source.path) || a.source.line - b.source.line || a.kind.localeCompare(b.kind) || a.id.localeCompare(b.id));
649
+ }
650
+ function buildGraphEdges(cwd, repo, commit, rosterRepos) {
651
+ const paths = listCandidatePaths(cwd).filter(isIndexablePath).sort((a, b) => a.localeCompare(b));
652
+ const ignored = defaultIsIgnored(cwd, paths);
653
+ const edges = [];
654
+ for (const path of paths) {
655
+ if (ignored.has(path) || isHardDeniedPath(path) || !SUPPORTED.has(extension(path))) continue;
656
+ const absolute = (0, import_node_path5.join)(cwd, ...path.split("/"));
657
+ if (!(0, import_node_fs4.existsSync)(absolute)) continue;
658
+ try {
659
+ edges.push(...graphEdgesForSource(repo, commit, path, (0, import_node_fs4.readFileSync)(absolute, "utf8"), rosterRepos));
660
+ } catch {
661
+ }
662
+ if (edges.length >= V4_GRAPH_MAX_EDGES) break;
663
+ }
664
+ return edges.slice(0, V4_GRAPH_MAX_EDGES).sort((a, b) => a.source.path.localeCompare(b.source.path) || a.source.line - b.source.line || a.kind.localeCompare(b.kind) || a.id.localeCompare(b.id));
665
+ }
666
+ // Annotate the CommonJS export names for ESM import in node:
667
+ 0 && (module.exports = {
668
+ REPO_INDEX_V4_SCHEMA,
669
+ V4_GRAPH_MAX_EDGES,
670
+ V4_GRAPH_MAX_EDGES_PER_FILE,
671
+ V4_MAX_ARTIFACT_BYTES,
672
+ V4_MAX_CHUNKS,
673
+ V4_STAGE_MAX_BODY_BYTES,
674
+ V4_STAGE_MAX_RECORDS,
675
+ buildGraphEdges,
676
+ buildRepoIndexV4,
677
+ buildStructuralChunks,
678
+ encodedStageBytes,
679
+ graphEdgesForSource,
680
+ languageForPath,
681
+ repoIndexV4StorePath,
682
+ shardRepoIndexV4,
683
+ structuralChunksForSource
684
+ });