grepmax 0.23.0 → 0.24.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.
@@ -0,0 +1,600 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.SURPRISE_COLUMNS = exports.MAX_SURPRISE_ROWS = exports.DEFAULT_SURPRISE_OPTIONS = void 0;
46
+ exports.normalizeSurpriseOptions = normalizeSurpriseOptions;
47
+ exports.isTestPath = isTestPath;
48
+ exports.isEvalPath = isEvalPath;
49
+ exports.relPath = relPath;
50
+ exports.lineLabel = lineLabel;
51
+ exports.directoryBucket = directoryBucket;
52
+ exports.findingBucketLabel = findingBucketLabel;
53
+ exports.findingExamples = findingExamples;
54
+ exports.formatPenaltySummary = formatPenaltySummary;
55
+ exports.skeletonHint = skeletonHint;
56
+ exports.scorePair = scorePair;
57
+ exports.quantile = quantile;
58
+ exports.stats = stats;
59
+ exports.buildFindings = buildFindings;
60
+ exports.analyzeSurprisingConnections = analyzeSurprisingConnections;
61
+ const path = __importStar(require("node:path"));
62
+ const callsites_1 = require("../graph/callsites");
63
+ const arrow_1 = require("../utils/arrow");
64
+ const scope_filter_1 = require("../utils/scope-filter");
65
+ exports.DEFAULT_SURPRISE_OPTIONS = {
66
+ sample: 160,
67
+ neighbors: 20,
68
+ dirDepth: 3,
69
+ minSimilarity: 0,
70
+ maxRows: 50000,
71
+ includeTests: false,
72
+ includeEval: false,
73
+ };
74
+ exports.MAX_SURPRISE_ROWS = 100000;
75
+ exports.SURPRISE_COLUMNS = [
76
+ "id",
77
+ "path",
78
+ "start_line",
79
+ "end_line",
80
+ "defined_symbols",
81
+ "referenced_symbols",
82
+ "type_referenced_symbols",
83
+ "role",
84
+ "content",
85
+ "vector",
86
+ ];
87
+ const CODE_EXTENSIONS = new Set([
88
+ ".ts",
89
+ ".tsx",
90
+ ".js",
91
+ ".jsx",
92
+ ".mjs",
93
+ ".cjs",
94
+ ".py",
95
+ ".go",
96
+ ".rs",
97
+ ".java",
98
+ ".cs",
99
+ ".rb",
100
+ ".kt",
101
+ ".swift",
102
+ ".scala",
103
+ ".php",
104
+ ".c",
105
+ ".cc",
106
+ ".cpp",
107
+ ".h",
108
+ ".hpp",
109
+ ]);
110
+ const TEST_DIR_RE = /(^|\/)(__tests__|tests?|specs?|benchmark)(\/|$)/i;
111
+ const TEST_FILE_RE = /\.(test|spec)\.[cm]?[jt]sx?$/i;
112
+ const NATIVE_TEST_DIR_RE = /(^|\/)\w+Tests?(\/|$)/;
113
+ const NATIVE_TEST_FILE_RE = /Tests?\.(swift|kt|java)$/;
114
+ function normalizeSurpriseOptions(opts = {}) {
115
+ var _a, _b, _c, _d, _e, _f, _g;
116
+ return Object.assign(Object.assign(Object.assign({}, exports.DEFAULT_SURPRISE_OPTIONS), opts), { sample: Math.max(1, Math.floor((_a = opts.sample) !== null && _a !== void 0 ? _a : exports.DEFAULT_SURPRISE_OPTIONS.sample)), neighbors: Math.max(1, Math.floor((_b = opts.neighbors) !== null && _b !== void 0 ? _b : exports.DEFAULT_SURPRISE_OPTIONS.neighbors)), dirDepth: Math.max(1, Math.floor((_c = opts.dirDepth) !== null && _c !== void 0 ? _c : exports.DEFAULT_SURPRISE_OPTIONS.dirDepth)), minSimilarity: Math.max(0, (_d = opts.minSimilarity) !== null && _d !== void 0 ? _d : exports.DEFAULT_SURPRISE_OPTIONS.minSimilarity), maxRows: Math.max(1, Math.min(exports.MAX_SURPRISE_ROWS, Math.floor((_e = opts.maxRows) !== null && _e !== void 0 ? _e : exports.DEFAULT_SURPRISE_OPTIONS.maxRows))), includeTests: (_f = opts.includeTests) !== null && _f !== void 0 ? _f : exports.DEFAULT_SURPRISE_OPTIONS.includeTests, includeEval: (_g = opts.includeEval) !== null && _g !== void 0 ? _g : exports.DEFAULT_SURPRISE_OPTIONS.includeEval });
117
+ }
118
+ function isTestPath(filePath) {
119
+ return (TEST_DIR_RE.test(filePath) ||
120
+ TEST_FILE_RE.test(filePath) ||
121
+ NATIVE_TEST_DIR_RE.test(filePath) ||
122
+ NATIVE_TEST_FILE_RE.test(filePath));
123
+ }
124
+ function isEvalPath(filePath) {
125
+ return (/(^|\/)src\/eval[^/]*\.ts$/i.test(filePath) ||
126
+ /(^|\/)(benchmarks?|experiments|scripts)(\/|$)/i.test(filePath));
127
+ }
128
+ function relPath(absPath, prefix) {
129
+ return absPath.startsWith(prefix) ? absPath.slice(prefix.length) : absPath;
130
+ }
131
+ function lineLabel(row) {
132
+ const sym = row.definedSymbols[0] ? ` ${row.definedSymbols[0]}` : "";
133
+ return `${row.relPath}:${row.startLine + 1}${sym}`;
134
+ }
135
+ function directoryBucket(rel, depth) {
136
+ const dir = path.dirname(rel);
137
+ if (dir === ".")
138
+ return ".";
139
+ return dir.split(path.sep).slice(0, depth).join(path.sep) || ".";
140
+ }
141
+ function findingBucketLabel(finding, dirDepth) {
142
+ return `${directoryBucket(finding.fileA, dirDepth)}<->${directoryBucket(finding.fileB, dirDepth)}`;
143
+ }
144
+ function findingExamples(finding, limit = 2) {
145
+ var _a;
146
+ const examples = ((_a = finding.examples) === null || _a === void 0 ? void 0 : _a.length)
147
+ ? finding.examples
148
+ : [finding.representative];
149
+ return examples.slice(0, limit);
150
+ }
151
+ function formatPenaltySummary(parts) {
152
+ const entries = [
153
+ ["tiny", parts.tinyHelperPenalty],
154
+ ["type", parts.typeConstantPenalty],
155
+ ["wrapper", parts.wrapperPenalty],
156
+ ["generic", parts.genericSymbolPenalty],
157
+ ].filter(([, value]) => Number(value) > 0);
158
+ return entries.length === 0
159
+ ? "none"
160
+ : entries.map(([name, value]) => `${name}:${value}`).join(",");
161
+ }
162
+ function skeletonHint(finding) {
163
+ return `gmax skeleton ${JSON.stringify(finding.fileA)} | gmax skeleton ${JSON.stringify(finding.fileB)}`;
164
+ }
165
+ function isCodePath(filePath) {
166
+ return CODE_EXTENSIONS.has(path.extname(filePath).toLowerCase());
167
+ }
168
+ function vectorLength(vector) {
169
+ if (!vector)
170
+ return 0;
171
+ if (Array.isArray(vector))
172
+ return vector.length;
173
+ if (ArrayBuffer.isView(vector)) {
174
+ const view = vector;
175
+ return typeof view.length === "number" ? view.length : view.byteLength;
176
+ }
177
+ if (typeof vector.length === "number") {
178
+ return Number(vector.length) || 0;
179
+ }
180
+ return 0;
181
+ }
182
+ function stableHash(s) {
183
+ let h = 2166136261;
184
+ for (let i = 0; i < s.length; i++) {
185
+ h ^= s.charCodeAt(i);
186
+ h = Math.imul(h, 16777619);
187
+ }
188
+ return h >>> 0;
189
+ }
190
+ function rowKey(row) {
191
+ return row.id || `${row.path}:${row.startLine}`;
192
+ }
193
+ function pairKey(a, b) {
194
+ return [rowKey(a), rowKey(b)].sort().join("\0");
195
+ }
196
+ function filePairKey(a, b) {
197
+ return [a.path, b.path].sort().join("\0");
198
+ }
199
+ function primarySymbol(row) {
200
+ var _a;
201
+ return (_a = row.definedSymbols.find((symbol) => !(0, callsites_1.isBuiltinCallee)(symbol))) !== null && _a !== void 0 ? _a : "";
202
+ }
203
+ function symbolTokens(symbol) {
204
+ const spaced = symbol
205
+ .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
206
+ .replace(/[_-]+/g, " ")
207
+ .toLowerCase();
208
+ return new Set(spaced.split(/[^a-z0-9]+/).filter((token) => token.length > 1));
209
+ }
210
+ function exactSharedSymbols(a, b) {
211
+ const left = new Set(a.definedSymbols.filter((s) => !(0, callsites_1.isBuiltinCallee)(s)));
212
+ return b.definedSymbols.filter((s) => left.has(s) && !(0, callsites_1.isBuiltinCallee)(s));
213
+ }
214
+ function symbolShapeSimilarity(a, b) {
215
+ const left = symbolTokens(primarySymbol(a));
216
+ const right = symbolTokens(primarySymbol(b));
217
+ if (left.size === 0 || right.size === 0)
218
+ return 0;
219
+ let overlap = 0;
220
+ for (const token of left)
221
+ if (right.has(token))
222
+ overlap++;
223
+ const union = new Set([...left, ...right]).size;
224
+ return union === 0 ? 0 : overlap / union;
225
+ }
226
+ function lineCount(row) {
227
+ return Math.max(1, row.endLine - row.startLine + 1);
228
+ }
229
+ function isTinyHelper(row) {
230
+ return row.content.trim().length < 220 || lineCount(row) <= 8;
231
+ }
232
+ function isTypeLike(row) {
233
+ return /^(export\s+)?(type|interface)\s+/m.test(row.content.trim());
234
+ }
235
+ function isConstantLike(row) {
236
+ if (row.definedSymbols.length === 0)
237
+ return false;
238
+ return row.definedSymbols.every((symbol) => /^[A-Z][A-Z0-9_]{2,}$/.test(symbol) || /_RE$/.test(symbol));
239
+ }
240
+ const GENERIC_SYMBOLS = new Set([
241
+ "constructor",
242
+ "context",
243
+ "data",
244
+ "default",
245
+ "fmt",
246
+ "get",
247
+ "height",
248
+ "kind",
249
+ "main",
250
+ "new",
251
+ "options",
252
+ "rel",
253
+ "relpath",
254
+ "resolve",
255
+ "root",
256
+ "run",
257
+ "set",
258
+ "style",
259
+ "styles",
260
+ "toarray",
261
+ "tostringarray",
262
+ "type",
263
+ "types",
264
+ "value",
265
+ "values",
266
+ "width",
267
+ ]);
268
+ function isGenericSymbol(symbol) {
269
+ const normalized = symbol.replace(/[^a-z0-9]/gi, "").toLowerCase();
270
+ return normalized.length <= 1 || GENERIC_SYMBOLS.has(normalized);
271
+ }
272
+ function isImplementationLike(row) {
273
+ if (isTypeLike(row) || isConstantLike(row))
274
+ return false;
275
+ return row.content.trim().length >= 300 && lineCount(row) >= 8;
276
+ }
277
+ function isCommandLibraryPair(a, b) {
278
+ return ((a.relPath.startsWith("src/commands/") &&
279
+ b.relPath.startsWith("src/lib/")) ||
280
+ (b.relPath.startsWith("src/commands/") && a.relPath.startsWith("src/lib/")));
281
+ }
282
+ function addReason(reasons, condition, reason) {
283
+ if (condition)
284
+ reasons.add(reason);
285
+ }
286
+ function scorePair(pair, pairCount) {
287
+ const sharedSymbols = exactSharedSymbols(pair.source, pair.target);
288
+ const sourcePrimary = primarySymbol(pair.source);
289
+ const targetPrimary = primarySymbol(pair.target);
290
+ const primaryMatch = sourcePrimary !== "" && sourcePrimary === targetPrimary;
291
+ const genericExactSymbol = (primaryMatch && isGenericSymbol(sourcePrimary)) ||
292
+ sharedSymbols.some((symbol) => isGenericSymbol(symbol));
293
+ const symbolShape = symbolShapeSimilarity(pair.source, pair.target);
294
+ const bothImplementation = isImplementationLike(pair.source) && isImplementationLike(pair.target);
295
+ const anyImplementation = isImplementationLike(pair.source) || isImplementationLike(pair.target);
296
+ const strongSymbol = !genericExactSymbol &&
297
+ (primaryMatch || sharedSymbols.length > 0 || symbolShape >= 0.5);
298
+ const sourceTiny = isTinyHelper(pair.source);
299
+ const targetTiny = isTinyHelper(pair.target);
300
+ const sourceTypeConstant = isTypeLike(pair.source) || isConstantLike(pair.source);
301
+ const targetTypeConstant = isTypeLike(pair.target) || isConstantLike(pair.target);
302
+ const commandWrapper = isCommandLibraryPair(pair.source, pair.target);
303
+ const reasons = new Set();
304
+ addReason(reasons, primaryMatch || sharedSymbols.length > 0, "same-symbol");
305
+ addReason(reasons, !primaryMatch && symbolShape >= 0.5, "similar-symbol");
306
+ addReason(reasons, bothImplementation, "implementation");
307
+ addReason(reasons, pairCount > 1, "multi-pair");
308
+ addReason(reasons, sourceTiny || targetTiny, "tiny-helper");
309
+ addReason(reasons, sourceTypeConstant || targetTypeConstant, "type-constant");
310
+ addReason(reasons, commandWrapper, "command-wrapper");
311
+ addReason(reasons, genericExactSymbol, "generic-symbol");
312
+ const sameSymbolBoost = genericExactSymbol
313
+ ? 0
314
+ : primaryMatch
315
+ ? 0.08
316
+ : sharedSymbols.length > 0
317
+ ? 0.05
318
+ : 0;
319
+ const symbolShapeBoost = !primaryMatch && sharedSymbols.length === 0 && symbolShape >= 0.5
320
+ ? Math.min(0.05, symbolShape * 0.08)
321
+ : 0;
322
+ const implementationBoost = bothImplementation
323
+ ? 0.04
324
+ : anyImplementation
325
+ ? 0.015
326
+ : 0;
327
+ const supportBoost = Math.min(0.06, Math.log2(pairCount + 1) * 0.018);
328
+ const tinyHelperBase = (sourceTiny ? 0.06 : 0) + (targetTiny ? 0.06 : 0);
329
+ const tinyHelperPenalty = strongSymbol
330
+ ? Math.min(0.04, tinyHelperBase / 2)
331
+ : Math.min(0.12, tinyHelperBase);
332
+ const typeConstantPenalty = Math.min(0.18, (sourceTypeConstant ? 0.09 : 0) + (targetTypeConstant ? 0.09 : 0));
333
+ const wrapperPenalty = commandWrapper ? 0.12 : 0;
334
+ const genericSymbolPenalty = genericExactSymbol ? 0.08 : 0;
335
+ const score = pair.similarity +
336
+ sameSymbolBoost +
337
+ symbolShapeBoost +
338
+ implementationBoost +
339
+ supportBoost -
340
+ tinyHelperPenalty -
341
+ typeConstantPenalty -
342
+ wrapperPenalty -
343
+ genericSymbolPenalty;
344
+ return {
345
+ base: Number(pair.similarity.toFixed(3)),
346
+ sameSymbolBoost: Number(sameSymbolBoost.toFixed(3)),
347
+ symbolShapeBoost: Number(symbolShapeBoost.toFixed(3)),
348
+ implementationBoost: Number(implementationBoost.toFixed(3)),
349
+ supportBoost: Number(supportBoost.toFixed(3)),
350
+ tinyHelperPenalty: Number(tinyHelperPenalty.toFixed(3)),
351
+ typeConstantPenalty: Number(typeConstantPenalty.toFixed(3)),
352
+ wrapperPenalty: Number(wrapperPenalty.toFixed(3)),
353
+ genericSymbolPenalty: Number(genericSymbolPenalty.toFixed(3)),
354
+ score: Number(score.toFixed(3)),
355
+ reasons: [...reasons].sort(),
356
+ };
357
+ }
358
+ function toChunkRow(raw, prefix) {
359
+ const absPath = String(raw.path || "");
360
+ return {
361
+ id: String(raw.id || ""),
362
+ path: absPath,
363
+ relPath: relPath(absPath, prefix),
364
+ startLine: Number(raw.start_line || 0),
365
+ endLine: Number(raw.end_line || 0),
366
+ role: String(raw.role || ""),
367
+ content: String(raw.content || ""),
368
+ vector: raw.vector,
369
+ definedSymbols: (0, arrow_1.toArr)(raw.defined_symbols),
370
+ referencedSymbols: (0, arrow_1.toArr)(raw.referenced_symbols),
371
+ typeReferencedSymbols: (0, arrow_1.toArr)(raw.type_referenced_symbols),
372
+ };
373
+ }
374
+ function similarityFromDistance(distance) {
375
+ return 1 / (1 + Math.max(0, distance));
376
+ }
377
+ function quantile(sortedAsc, q) {
378
+ if (sortedAsc.length === 0)
379
+ return 0;
380
+ const pos = (sortedAsc.length - 1) * q;
381
+ const base = Math.floor(pos);
382
+ const rest = pos - base;
383
+ const next = sortedAsc[base + 1];
384
+ return next !== undefined
385
+ ? sortedAsc[base] + rest * (next - sortedAsc[base])
386
+ : sortedAsc[base];
387
+ }
388
+ function stats(values) {
389
+ var _a, _b;
390
+ const sorted = [...values].sort((a, b) => a - b);
391
+ const mean = sorted.length === 0 ? 0 : sorted.reduce((a, b) => a + b, 0) / sorted.length;
392
+ return {
393
+ min: Number(((_a = sorted[0]) !== null && _a !== void 0 ? _a : 0).toFixed(3)),
394
+ p50: Number(quantile(sorted, 0.5).toFixed(3)),
395
+ p90: Number(quantile(sorted, 0.9).toFixed(3)),
396
+ max: Number(((_b = sorted[sorted.length - 1]) !== null && _b !== void 0 ? _b : 0).toFixed(3)),
397
+ mean: Number(mean.toFixed(3)),
398
+ };
399
+ }
400
+ function buildFileEdges(rows) {
401
+ const defFiles = new Map();
402
+ for (const row of rows) {
403
+ for (const symbol of row.definedSymbols) {
404
+ if (!defFiles.has(symbol))
405
+ defFiles.set(symbol, new Set());
406
+ defFiles.get(symbol).add(row.path);
407
+ }
408
+ }
409
+ const edges = new Set();
410
+ for (const row of rows) {
411
+ const refs = new Set([
412
+ ...row.referencedSymbols,
413
+ ...row.typeReferencedSymbols,
414
+ ]);
415
+ for (const symbol of refs) {
416
+ if ((0, callsites_1.isBuiltinCallee)(symbol))
417
+ continue;
418
+ const files = defFiles.get(symbol);
419
+ if (!files || files.size !== 1)
420
+ continue;
421
+ const [targetFile] = files;
422
+ if (!targetFile || targetFile === row.path)
423
+ continue;
424
+ edges.add(`${row.path}\0${targetFile}`);
425
+ }
426
+ }
427
+ return edges;
428
+ }
429
+ function hasDirectFileEdge(edges, a, b) {
430
+ return edges.has(`${a.path}\0${b.path}`) || edges.has(`${b.path}\0${a.path}`);
431
+ }
432
+ function filterableCodeRow(row, opts) {
433
+ if (!row.path || !isCodePath(row.path))
434
+ return false;
435
+ if (!opts.includeTests && isTestPath(row.relPath))
436
+ return false;
437
+ if (!opts.includeEval && isEvalPath(row.relPath))
438
+ return false;
439
+ if (vectorLength(row.vector) === 0)
440
+ return false;
441
+ if (row.content.trim().length < 80)
442
+ return false;
443
+ return row.definedSymbols.length > 0;
444
+ }
445
+ function buildFindings(pairs) {
446
+ var _a;
447
+ const groups = new Map();
448
+ for (const pair of pairs) {
449
+ const key = filePairKey(pair.source, pair.target);
450
+ if (!groups.has(key))
451
+ groups.set(key, []);
452
+ groups.get(key).push(pair);
453
+ }
454
+ const findings = [];
455
+ for (const group of groups.values()) {
456
+ const scoredPairs = group.map((pair) => (Object.assign(Object.assign({}, pair), { scoreParts: scorePair(pair, group.length) })));
457
+ scoredPairs.sort((a, b) => b.scoreParts.score - a.scoreParts.score ||
458
+ b.similarity - a.similarity ||
459
+ a.distance - b.distance);
460
+ const representative = scoredPairs[0];
461
+ const files = [representative.source, representative.target].sort((a, b) => a.path.localeCompare(b.path));
462
+ const similaritiesAsc = group
463
+ .map((pair) => pair.similarity)
464
+ .sort((a, b) => a - b);
465
+ findings.push({
466
+ fileA: files[0].relPath,
467
+ fileB: files[1].relPath,
468
+ pairCount: group.length,
469
+ maxSimilarity: Number(((_a = similaritiesAsc[similaritiesAsc.length - 1]) !== null && _a !== void 0 ? _a : 0).toFixed(3)),
470
+ medianSimilarity: Number(quantile(similaritiesAsc, 0.5).toFixed(3)),
471
+ representative,
472
+ examples: scoredPairs.slice(0, 3),
473
+ score: representative.scoreParts.score,
474
+ reasons: representative.scoreParts.reasons,
475
+ topSimilarities: [...similaritiesAsc]
476
+ .reverse()
477
+ .slice(0, 5)
478
+ .map((value) => Number(value.toFixed(3))),
479
+ });
480
+ }
481
+ return findings.sort((a, b) => b.score - a.score ||
482
+ b.maxSimilarity - a.maxSimilarity ||
483
+ b.pairCount - a.pairCount ||
484
+ `${a.fileA}\0${a.fileB}`.localeCompare(`${b.fileA}\0${b.fileB}`));
485
+ }
486
+ function analyzeSurprisingConnections(table_1, projectRoot_1) {
487
+ return __awaiter(this, arguments, void 0, function* (table, projectRoot, partialOptions = {}) {
488
+ var _a;
489
+ const opts = normalizeSurpriseOptions(partialOptions);
490
+ const prefix = projectRoot.endsWith("/") ? projectRoot : `${projectRoot}/`;
491
+ const scope = (0, scope_filter_1.resolveScope)({
492
+ projectRoot,
493
+ in: opts.in,
494
+ exclude: opts.exclude,
495
+ });
496
+ const where = (0, scope_filter_1.buildScopeWhere)(scope);
497
+ const rawRows = (yield table
498
+ .query()
499
+ .select(exports.SURPRISE_COLUMNS)
500
+ .where(where)
501
+ .limit(opts.maxRows)
502
+ .toArray());
503
+ const rows = rawRows.map((row) => toChunkRow(row, prefix));
504
+ const codeRows = rows.filter((row) => filterableCodeRow(row, opts));
505
+ const fileEdges = buildFileEdges(rows);
506
+ const anchors = [...codeRows]
507
+ .sort((a, b) => stableHash(rowKey(a)) - stableHash(rowKey(b)))
508
+ .slice(0, opts.sample);
509
+ const filters = {
510
+ rawNeighbors: 0,
511
+ sameChunk: 0,
512
+ sameFile: 0,
513
+ nonCode: 0,
514
+ weakCode: 0,
515
+ tests: 0,
516
+ evalHarness: 0,
517
+ sameDirBucket: 0,
518
+ graphEdge: 0,
519
+ belowThreshold: 0,
520
+ };
521
+ const pairs = new Map();
522
+ for (const source of anchors) {
523
+ const neighbors = (yield table
524
+ .vectorSearch(source.vector)
525
+ .select([...exports.SURPRISE_COLUMNS, "_distance"])
526
+ .where(where)
527
+ .limit(opts.neighbors + 8)
528
+ .toArray());
529
+ for (const rawTarget of neighbors) {
530
+ filters.rawNeighbors++;
531
+ const target = toChunkRow(rawTarget, prefix);
532
+ if (rowKey(source) === rowKey(target)) {
533
+ filters.sameChunk++;
534
+ continue;
535
+ }
536
+ if (source.path === target.path) {
537
+ filters.sameFile++;
538
+ continue;
539
+ }
540
+ if (!isCodePath(target.path) || vectorLength(target.vector) === 0) {
541
+ filters.nonCode++;
542
+ continue;
543
+ }
544
+ if (target.content.trim().length < 80 ||
545
+ target.definedSymbols.length === 0) {
546
+ filters.weakCode++;
547
+ continue;
548
+ }
549
+ if (!opts.includeTests && isTestPath(target.relPath)) {
550
+ filters.tests++;
551
+ continue;
552
+ }
553
+ if (!opts.includeEval && isEvalPath(target.relPath)) {
554
+ filters.evalHarness++;
555
+ continue;
556
+ }
557
+ if (directoryBucket(source.relPath, opts.dirDepth) ===
558
+ directoryBucket(target.relPath, opts.dirDepth)) {
559
+ filters.sameDirBucket++;
560
+ continue;
561
+ }
562
+ if (hasDirectFileEdge(fileEdges, source, target)) {
563
+ filters.graphEdge++;
564
+ continue;
565
+ }
566
+ const distance = Number((_a = rawTarget._distance) !== null && _a !== void 0 ? _a : 0);
567
+ const similarity = similarityFromDistance(distance);
568
+ if (similarity < opts.minSimilarity) {
569
+ filters.belowThreshold++;
570
+ continue;
571
+ }
572
+ const key = pairKey(source, target);
573
+ const existing = pairs.get(key);
574
+ if (!existing || similarity > existing.similarity) {
575
+ pairs.set(key, { similarity, distance, source, target });
576
+ }
577
+ }
578
+ }
579
+ const acceptedPairs = [...pairs.values()].sort((a, b) => a.distance - b.distance || b.similarity - a.similarity);
580
+ const findings = buildFindings(acceptedPairs);
581
+ return {
582
+ summary: {
583
+ projectRoot,
584
+ rows: rows.length,
585
+ codeRows: codeRows.length,
586
+ sampledAnchors: anchors.length,
587
+ graphFileEdges: fileEdges.size,
588
+ options: opts,
589
+ filters,
590
+ acceptedPairs: acceptedPairs.length,
591
+ acceptedFilePairs: findings.length,
592
+ similarity: stats(acceptedPairs.map((pair) => pair.similarity)),
593
+ distance: stats(acceptedPairs.map((pair) => pair.distance)),
594
+ actionabilityScore: stats(findings.map((finding) => finding.score)),
595
+ },
596
+ pairs: acceptedPairs,
597
+ findings,
598
+ };
599
+ });
600
+ }