opencode-codebase-index 0.21.0 → 0.22.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/dist/cli.cjs +2145 -1391
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2230 -1476
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1975 -1365
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2017 -1407
- package/dist/index.js.map +1 -1
- package/dist/pi-extension.cjs +1699 -1087
- package/dist/pi-extension.cjs.map +1 -1
- package/dist/pi-extension.js +1736 -1124
- package/dist/pi-extension.js.map +1 -1
- package/native/codebase-index-native.darwin-arm64.node +0 -0
- package/native/codebase-index-native.darwin-x64.node +0 -0
- package/native/codebase-index-native.linux-arm64-gnu.node +0 -0
- package/native/codebase-index-native.linux-x64-gnu.node +0 -0
- package/native/codebase-index-native.win32-x64-msvc.node +0 -0
- package/package.json +5 -3
package/dist/pi-extension.cjs
CHANGED
|
@@ -333,7 +333,7 @@ var require_ignore = __commonJS({
|
|
|
333
333
|
// path matching.
|
|
334
334
|
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
335
335
|
// @returns {TestResult} true if a file is ignored
|
|
336
|
-
test(
|
|
336
|
+
test(path21, checkUnignored, mode) {
|
|
337
337
|
let ignored = false;
|
|
338
338
|
let unignored = false;
|
|
339
339
|
let matchedRule;
|
|
@@ -342,7 +342,7 @@ var require_ignore = __commonJS({
|
|
|
342
342
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
343
343
|
return;
|
|
344
344
|
}
|
|
345
|
-
const matched = rule[mode].test(
|
|
345
|
+
const matched = rule[mode].test(path21);
|
|
346
346
|
if (!matched) {
|
|
347
347
|
return;
|
|
348
348
|
}
|
|
@@ -363,17 +363,17 @@ var require_ignore = __commonJS({
|
|
|
363
363
|
var throwError = (message, Ctor) => {
|
|
364
364
|
throw new Ctor(message);
|
|
365
365
|
};
|
|
366
|
-
var checkPath = (
|
|
367
|
-
if (!isString(
|
|
366
|
+
var checkPath = (path21, originalPath, doThrow) => {
|
|
367
|
+
if (!isString(path21)) {
|
|
368
368
|
return doThrow(
|
|
369
369
|
`path must be a string, but got \`${originalPath}\``,
|
|
370
370
|
TypeError
|
|
371
371
|
);
|
|
372
372
|
}
|
|
373
|
-
if (!
|
|
373
|
+
if (!path21) {
|
|
374
374
|
return doThrow(`path must not be empty`, TypeError);
|
|
375
375
|
}
|
|
376
|
-
if (checkPath.isNotRelative(
|
|
376
|
+
if (checkPath.isNotRelative(path21)) {
|
|
377
377
|
const r = "`path.relative()`d";
|
|
378
378
|
return doThrow(
|
|
379
379
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -382,7 +382,7 @@ var require_ignore = __commonJS({
|
|
|
382
382
|
}
|
|
383
383
|
return true;
|
|
384
384
|
};
|
|
385
|
-
var isNotRelative = (
|
|
385
|
+
var isNotRelative = (path21) => REGEX_TEST_INVALID_PATH.test(path21);
|
|
386
386
|
checkPath.isNotRelative = isNotRelative;
|
|
387
387
|
checkPath.convert = (p) => p;
|
|
388
388
|
var Ignore2 = class {
|
|
@@ -412,19 +412,19 @@ var require_ignore = __commonJS({
|
|
|
412
412
|
}
|
|
413
413
|
// @returns {TestResult}
|
|
414
414
|
_test(originalPath, cache, checkUnignored, slices) {
|
|
415
|
-
const
|
|
415
|
+
const path21 = originalPath && checkPath.convert(originalPath);
|
|
416
416
|
checkPath(
|
|
417
|
-
|
|
417
|
+
path21,
|
|
418
418
|
originalPath,
|
|
419
419
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
420
420
|
);
|
|
421
|
-
return this._t(
|
|
421
|
+
return this._t(path21, cache, checkUnignored, slices);
|
|
422
422
|
}
|
|
423
|
-
checkIgnore(
|
|
424
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
425
|
-
return this.test(
|
|
423
|
+
checkIgnore(path21) {
|
|
424
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path21)) {
|
|
425
|
+
return this.test(path21);
|
|
426
426
|
}
|
|
427
|
-
const slices =
|
|
427
|
+
const slices = path21.split(SLASH).filter(Boolean);
|
|
428
428
|
slices.pop();
|
|
429
429
|
if (slices.length) {
|
|
430
430
|
const parent = this._t(
|
|
@@ -437,18 +437,18 @@ var require_ignore = __commonJS({
|
|
|
437
437
|
return parent;
|
|
438
438
|
}
|
|
439
439
|
}
|
|
440
|
-
return this._rules.test(
|
|
440
|
+
return this._rules.test(path21, false, MODE_CHECK_IGNORE);
|
|
441
441
|
}
|
|
442
|
-
_t(
|
|
443
|
-
if (
|
|
444
|
-
return cache[
|
|
442
|
+
_t(path21, cache, checkUnignored, slices) {
|
|
443
|
+
if (path21 in cache) {
|
|
444
|
+
return cache[path21];
|
|
445
445
|
}
|
|
446
446
|
if (!slices) {
|
|
447
|
-
slices =
|
|
447
|
+
slices = path21.split(SLASH).filter(Boolean);
|
|
448
448
|
}
|
|
449
449
|
slices.pop();
|
|
450
450
|
if (!slices.length) {
|
|
451
|
-
return cache[
|
|
451
|
+
return cache[path21] = this._rules.test(path21, checkUnignored, MODE_IGNORE);
|
|
452
452
|
}
|
|
453
453
|
const parent = this._t(
|
|
454
454
|
slices.join(SLASH) + SLASH,
|
|
@@ -456,29 +456,29 @@ var require_ignore = __commonJS({
|
|
|
456
456
|
checkUnignored,
|
|
457
457
|
slices
|
|
458
458
|
);
|
|
459
|
-
return cache[
|
|
459
|
+
return cache[path21] = parent.ignored ? parent : this._rules.test(path21, checkUnignored, MODE_IGNORE);
|
|
460
460
|
}
|
|
461
|
-
ignores(
|
|
462
|
-
return this._test(
|
|
461
|
+
ignores(path21) {
|
|
462
|
+
return this._test(path21, this._ignoreCache, false).ignored;
|
|
463
463
|
}
|
|
464
464
|
createFilter() {
|
|
465
|
-
return (
|
|
465
|
+
return (path21) => !this.ignores(path21);
|
|
466
466
|
}
|
|
467
467
|
filter(paths) {
|
|
468
468
|
return makeArray(paths).filter(this.createFilter());
|
|
469
469
|
}
|
|
470
470
|
// @returns {TestResult}
|
|
471
|
-
test(
|
|
472
|
-
return this._test(
|
|
471
|
+
test(path21) {
|
|
472
|
+
return this._test(path21, this._testCache, true);
|
|
473
473
|
}
|
|
474
474
|
};
|
|
475
475
|
var factory = (options) => new Ignore2(options);
|
|
476
|
-
var isPathValid = (
|
|
476
|
+
var isPathValid = (path21) => checkPath(path21 && checkPath.convert(path21), path21, RETURN_FALSE);
|
|
477
477
|
var setupWindows = () => {
|
|
478
478
|
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
479
479
|
checkPath.convert = makePosix;
|
|
480
480
|
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
481
|
-
checkPath.isNotRelative = (
|
|
481
|
+
checkPath.isNotRelative = (path21) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path21) || isNotRelative(path21);
|
|
482
482
|
};
|
|
483
483
|
if (
|
|
484
484
|
// Detect `process` so that it can run in browsers.
|
|
@@ -816,7 +816,8 @@ function getDefaultSearchConfig() {
|
|
|
816
816
|
contextLines: 0,
|
|
817
817
|
routingHints: true,
|
|
818
818
|
routingGraphHandoffHints: false,
|
|
819
|
-
routingHintRole: "system"
|
|
819
|
+
routingHintRole: "system",
|
|
820
|
+
communityBoost: 0
|
|
820
821
|
};
|
|
821
822
|
}
|
|
822
823
|
function getDefaultRerankerBaseUrl(provider) {
|
|
@@ -949,7 +950,8 @@ function parseConfig(raw) {
|
|
|
949
950
|
contextLines: typeof rawSearch.contextLines === "number" ? Math.min(50, Math.max(0, rawSearch.contextLines)) : defaultSearch.contextLines,
|
|
950
951
|
routingHints: typeof rawSearch.routingHints === "boolean" ? rawSearch.routingHints : defaultSearch.routingHints,
|
|
951
952
|
routingGraphHandoffHints: typeof rawSearch.routingGraphHandoffHints === "boolean" ? rawSearch.routingGraphHandoffHints : defaultSearch.routingGraphHandoffHints,
|
|
952
|
-
routingHintRole: rawSearch.routingHintRole === "developer" || rawSearch.routingHintRole === "system" ? rawSearch.routingHintRole : defaultSearch.routingHintRole
|
|
953
|
+
routingHintRole: rawSearch.routingHintRole === "developer" || rawSearch.routingHintRole === "system" ? rawSearch.routingHintRole : defaultSearch.routingHintRole,
|
|
954
|
+
communityBoost: typeof rawSearch.communityBoost === "number" && Number.isFinite(rawSearch.communityBoost) ? Math.min(1, Math.max(0, rawSearch.communityBoost)) : defaultSearch.communityBoost
|
|
953
955
|
};
|
|
954
956
|
const rawDebug = input.debug && typeof input.debug === "object" ? input.debug : {};
|
|
955
957
|
const debug = {
|
|
@@ -1449,9 +1451,122 @@ function formatPrImpact(result) {
|
|
|
1449
1451
|
return lines.join("\n");
|
|
1450
1452
|
}
|
|
1451
1453
|
|
|
1454
|
+
// src/tools/format-communities.ts
|
|
1455
|
+
function compareText(left, right) {
|
|
1456
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
1457
|
+
}
|
|
1458
|
+
function buildCodeCommunitiesResult(communities, centrality, couplings = [], options = {}) {
|
|
1459
|
+
const minSize = options.minSize ?? 1;
|
|
1460
|
+
const limit = options.limit ?? 20;
|
|
1461
|
+
const hubThreshold = options.hubThreshold ?? 5;
|
|
1462
|
+
const minCoupling = options.minCoupling ?? 1;
|
|
1463
|
+
const couplingLimit = options.couplingLimit ?? 20;
|
|
1464
|
+
const communityMap = /* @__PURE__ */ new Map();
|
|
1465
|
+
for (const c of communities) {
|
|
1466
|
+
let entry = communityMap.get(c.communityId);
|
|
1467
|
+
if (!entry) {
|
|
1468
|
+
entry = { label: c.communityLabel, members: [] };
|
|
1469
|
+
communityMap.set(c.communityId, entry);
|
|
1470
|
+
}
|
|
1471
|
+
entry.members.push(c);
|
|
1472
|
+
}
|
|
1473
|
+
const sortedCommunities = Array.from(communityMap.entries()).map(([id, entry]) => ({
|
|
1474
|
+
id,
|
|
1475
|
+
label: entry.label,
|
|
1476
|
+
symbolCount: entry.members.length,
|
|
1477
|
+
members: entry.members.map((m) => ({
|
|
1478
|
+
symbolId: m.symbolId,
|
|
1479
|
+
symbolName: m.symbolName,
|
|
1480
|
+
filePath: m.filePath
|
|
1481
|
+
})).sort((a, b) => compareText(a.symbolName, b.symbolName) || compareText(a.symbolId, b.symbolId))
|
|
1482
|
+
})).filter((c) => c.symbolCount >= minSize).sort((a, b) => b.symbolCount - a.symbolCount || compareText(a.label, b.label) || a.id - b.id).slice(0, limit);
|
|
1483
|
+
const communityBySymbol = new Map(communities.map((community) => [community.symbolId, community]));
|
|
1484
|
+
const communityLabelById = new Map(communities.map((community) => [community.communityId, community.communityLabel]));
|
|
1485
|
+
const hubNodes = centrality.map((c) => ({
|
|
1486
|
+
symbolId: c.symbolId,
|
|
1487
|
+
symbolName: c.symbolName,
|
|
1488
|
+
filePath: c.filePath,
|
|
1489
|
+
callerCount: c.callerCount,
|
|
1490
|
+
calleeCount: c.calleeCount,
|
|
1491
|
+
totalConnections: c.totalConnections,
|
|
1492
|
+
crossCommunityConnections: communityBySymbol.get(c.symbolId)?.crossCommunityConnections ?? 0
|
|
1493
|
+
})).filter((h) => h.crossCommunityConnections >= hubThreshold).sort(
|
|
1494
|
+
(a, b) => b.crossCommunityConnections - a.crossCommunityConnections || b.totalConnections - a.totalConnections || compareText(a.symbolId, b.symbolId)
|
|
1495
|
+
).slice(0, limit);
|
|
1496
|
+
const canonicalCoupling = (value) => Math.trunc(value);
|
|
1497
|
+
const couplingItems = couplings.map((entry) => {
|
|
1498
|
+
const relationships = entry.relationships ?? entry.representativeRelationships ?? [];
|
|
1499
|
+
const normalizedRelationships = relationships.map((relationship) => ({
|
|
1500
|
+
fromSymbolId: relationship.fromSymbolId,
|
|
1501
|
+
fromSymbolName: relationship.fromSymbolName,
|
|
1502
|
+
fromFilePath: relationship.fromFilePath,
|
|
1503
|
+
toSymbolId: relationship.toSymbolId,
|
|
1504
|
+
toSymbolName: relationship.toSymbolName,
|
|
1505
|
+
toFilePath: relationship.toFilePath
|
|
1506
|
+
})).sort(
|
|
1507
|
+
(left, right) => compareText(left.fromSymbolName, right.fromSymbolName) || compareText(left.fromSymbolId, right.fromSymbolId) || compareText(left.toSymbolName, right.toSymbolName) || compareText(left.toSymbolId, right.toSymbolId) || compareText(left.fromFilePath, right.fromFilePath) || compareText(left.toFilePath, right.toFilePath)
|
|
1508
|
+
).slice(0, 5);
|
|
1509
|
+
const communityA = canonicalCoupling(Math.min(entry.communityA, entry.communityB));
|
|
1510
|
+
const communityB = canonicalCoupling(Math.max(entry.communityA, entry.communityB));
|
|
1511
|
+
return {
|
|
1512
|
+
communityA,
|
|
1513
|
+
communityB,
|
|
1514
|
+
communityAName: communityLabelById.get(communityA) ?? `Community ${communityA}`,
|
|
1515
|
+
communityBName: communityLabelById.get(communityB) ?? `Community ${communityB}`,
|
|
1516
|
+
distinctConnections: canonicalCoupling(entry.count),
|
|
1517
|
+
representativeRelationships: normalizedRelationships
|
|
1518
|
+
};
|
|
1519
|
+
}).filter((entry) => entry.distinctConnections >= minCoupling).sort(
|
|
1520
|
+
(left, right) => right.distinctConnections - left.distinctConnections || compareText(left.communityAName, right.communityAName) || compareText(left.communityBName, right.communityBName) || compareText(left.communityAName + left.communityBName, right.communityAName + right.communityBName) || left.communityA - right.communityA || left.communityB - right.communityB
|
|
1521
|
+
).slice(0, Math.max(1, Math.floor(couplingLimit)));
|
|
1522
|
+
return {
|
|
1523
|
+
communities: sortedCommunities,
|
|
1524
|
+
hubNodes,
|
|
1525
|
+
totalSymbols: communities.length,
|
|
1526
|
+
totalCommunities: communityMap.size,
|
|
1527
|
+
couplings: couplingItems
|
|
1528
|
+
};
|
|
1529
|
+
}
|
|
1530
|
+
function formatCodeCommunities(result) {
|
|
1531
|
+
const lines = [];
|
|
1532
|
+
lines.push(`\u2192 Communities: ${result.totalCommunities} (${result.communities.length} shown, ${result.totalSymbols} symbols total)`);
|
|
1533
|
+
for (const community of result.communities) {
|
|
1534
|
+
lines.push(` Community ${community.id} (${community.label}): ${community.symbolCount} symbols`);
|
|
1535
|
+
const shownMembers = community.members.slice(0, 8);
|
|
1536
|
+
for (const m of shownMembers) {
|
|
1537
|
+
lines.push(` - ${m.symbolName} (${m.filePath})`);
|
|
1538
|
+
}
|
|
1539
|
+
if (community.members.length > 8) {
|
|
1540
|
+
lines.push(` ... and ${community.members.length - 8} more`);
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
if (result.hubNodes.length > 0) {
|
|
1544
|
+
lines.push(`\u2192 Hub nodes (${result.hubNodes.length} shown, cross-community connections):`);
|
|
1545
|
+
for (const hub of result.hubNodes) {
|
|
1546
|
+
lines.push(
|
|
1547
|
+
` - ${hub.symbolName} (${hub.crossCommunityConnections} cross-community, ${hub.callerCount} callers, ${hub.calleeCount} callees) at ${hub.filePath}`
|
|
1548
|
+
);
|
|
1549
|
+
}
|
|
1550
|
+
} else {
|
|
1551
|
+
lines.push("\u2192 Hub nodes: none with significant cross-community connections");
|
|
1552
|
+
}
|
|
1553
|
+
if (result.couplings.length > 0) {
|
|
1554
|
+
lines.push(`\u2192 Community couplings: ${result.couplings.length} shown`);
|
|
1555
|
+
for (const coupling of result.couplings) {
|
|
1556
|
+
lines.push(` - ${coupling.communityAName} \u2194 ${coupling.communityBName}: ${coupling.distinctConnections} distinct connections`);
|
|
1557
|
+
for (const relationship of coupling.representativeRelationships) {
|
|
1558
|
+
lines.push(` - ${relationship.fromSymbolName} (${relationship.fromFilePath}) -> ${relationship.toSymbolName} (${relationship.toFilePath})`);
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
} else {
|
|
1562
|
+
lines.push("\u2192 Community couplings: none above minCoupling threshold");
|
|
1563
|
+
}
|
|
1564
|
+
return lines.join("\n");
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1452
1567
|
// src/tools/operations.ts
|
|
1453
1568
|
var import_fs13 = require("fs");
|
|
1454
|
-
var
|
|
1569
|
+
var path20 = __toESM(require("path"), 1);
|
|
1455
1570
|
|
|
1456
1571
|
// src/config/paths.ts
|
|
1457
1572
|
var import_fs4 = require("fs");
|
|
@@ -1937,6 +2052,15 @@ function findKnowledgeBasePathIndex(knowledgeBases, inputPath, projectRoot3) {
|
|
|
1937
2052
|
);
|
|
1938
2053
|
}
|
|
1939
2054
|
|
|
2055
|
+
// src/tools/contracts.ts
|
|
2056
|
+
var CODE_COMMUNITIES_MIN_SIZE = 1;
|
|
2057
|
+
var CODE_COMMUNITIES_DEFAULT_LIMIT = 20;
|
|
2058
|
+
var CODE_COMMUNITIES_MAX_LIMIT = 100;
|
|
2059
|
+
var CODE_COMMUNITIES_DEFAULT_HUB_THRESHOLD = 5;
|
|
2060
|
+
var CODE_COMMUNITIES_MIN_COUPLING = 1;
|
|
2061
|
+
var CODE_COMMUNITIES_DEFAULT_COUPLING_LIMIT = 20;
|
|
2062
|
+
var CODE_COMMUNITIES_MAX_COUPLING_LIMIT = 100;
|
|
2063
|
+
|
|
1940
2064
|
// src/tools/context-pack.ts
|
|
1941
2065
|
var import_tiktoken = require("tiktoken");
|
|
1942
2066
|
|
|
@@ -2580,8 +2704,8 @@ function formatExactSearchHandoff(results) {
|
|
|
2580
2704
|
}
|
|
2581
2705
|
function formatContextEvidence(result, index) {
|
|
2582
2706
|
const symbol = result.name ? ` ${JSON.stringify(compactEvidenceValue(result.name, 80))}` : "";
|
|
2583
|
-
const
|
|
2584
|
-
return `[${index}] ${result.chunkType}${symbol} in ${
|
|
2707
|
+
const path21 = compactEvidenceValue(result.filePath, 120);
|
|
2708
|
+
return `[${index}] ${result.chunkType}${symbol} in ${path21}:${result.startLine}-${result.endLine} (score ${result.score.toFixed(2)})`;
|
|
2585
2709
|
}
|
|
2586
2710
|
function formatContextPack(heading, selected, candidateCount, duplicateCount, limitOmittedCount, budgetOmittedCount, includeExactSearchHandoff) {
|
|
2587
2711
|
const lines = selected.map((result, index) => formatContextEvidence(result, index + 1));
|
|
@@ -4755,7 +4879,7 @@ function saveConfig(projectRoot3, config, host) {
|
|
|
4755
4879
|
|
|
4756
4880
|
// src/indexer/index.ts
|
|
4757
4881
|
var import_fs12 = require("fs");
|
|
4758
|
-
var
|
|
4882
|
+
var path19 = __toESM(require("path"), 1);
|
|
4759
4883
|
var import_perf_hooks = require("perf_hooks");
|
|
4760
4884
|
var import_child_process4 = require("child_process");
|
|
4761
4885
|
var import_util4 = require("util");
|
|
@@ -7023,6 +7147,9 @@ function createMockNativeBinding() {
|
|
|
7023
7147
|
detectCommunities() {
|
|
7024
7148
|
throw error;
|
|
7025
7149
|
}
|
|
7150
|
+
detectCommunityCouplings() {
|
|
7151
|
+
throw error;
|
|
7152
|
+
}
|
|
7026
7153
|
computeCentrality() {
|
|
7027
7154
|
throw error;
|
|
7028
7155
|
}
|
|
@@ -7260,6 +7387,18 @@ var Database = class _Database {
|
|
|
7260
7387
|
}
|
|
7261
7388
|
this.closed = true;
|
|
7262
7389
|
}
|
|
7390
|
+
beginWriteTransaction() {
|
|
7391
|
+
this.throwIfClosed();
|
|
7392
|
+
this.inner.beginWriteTransaction();
|
|
7393
|
+
}
|
|
7394
|
+
commitWriteTransaction() {
|
|
7395
|
+
this.throwIfClosed();
|
|
7396
|
+
this.inner.commitWriteTransaction();
|
|
7397
|
+
}
|
|
7398
|
+
rollbackWriteTransaction() {
|
|
7399
|
+
this.throwIfClosed();
|
|
7400
|
+
this.inner.rollbackWriteTransaction();
|
|
7401
|
+
}
|
|
7263
7402
|
embeddingExists(contentHash) {
|
|
7264
7403
|
this.throwIfClosed();
|
|
7265
7404
|
return this.inner.embeddingExists(contentHash);
|
|
@@ -7519,6 +7658,13 @@ var Database = class _Database {
|
|
|
7519
7658
|
this.throwIfClosed();
|
|
7520
7659
|
return this.inner.computeCentrality(branch);
|
|
7521
7660
|
}
|
|
7661
|
+
detectCommunityCouplings(branch) {
|
|
7662
|
+
this.throwIfClosed();
|
|
7663
|
+
return this.inner.detectCommunityCouplings(branch).map((entry) => ({
|
|
7664
|
+
...entry,
|
|
7665
|
+
relationships: entry.representativeRelationships ?? []
|
|
7666
|
+
}));
|
|
7667
|
+
}
|
|
7522
7668
|
};
|
|
7523
7669
|
|
|
7524
7670
|
// src/git/branch-materialization.ts
|
|
@@ -8288,6 +8434,21 @@ async function getChunkGitBlame(projectRoot3, filePath, startLine, endLine) {
|
|
|
8288
8434
|
}
|
|
8289
8435
|
|
|
8290
8436
|
// src/indexer/search-ranking.ts
|
|
8437
|
+
function applyCommunityBoost(candidates, sameCommunityCandidateIds, boost) {
|
|
8438
|
+
if (boost <= 0 || sameCommunityCandidateIds.size === 0 || candidates.length <= 1) {
|
|
8439
|
+
return candidates;
|
|
8440
|
+
}
|
|
8441
|
+
const result = candidates.map((candidate) => sameCommunityCandidateIds.has(candidate.id) ? { ...candidate, score: candidate.score * (1 + boost) } : candidate);
|
|
8442
|
+
for (let index = 1; index < result.length; index += 1) {
|
|
8443
|
+
const candidate = result[index];
|
|
8444
|
+
const previous = result[index - 1];
|
|
8445
|
+
if (candidate && previous && sameCommunityCandidateIds.has(candidate.id) && !sameCommunityCandidateIds.has(previous.id) && candidate.score > previous.score) {
|
|
8446
|
+
result[index - 1] = candidate;
|
|
8447
|
+
result[index] = previous;
|
|
8448
|
+
}
|
|
8449
|
+
}
|
|
8450
|
+
return result;
|
|
8451
|
+
}
|
|
8291
8452
|
var RANK_HYBRID_CACHE_LIMIT = 256;
|
|
8292
8453
|
var rankHybridResultsCache = /* @__PURE__ */ new WeakMap();
|
|
8293
8454
|
function classifyQueryIntentRaw(query) {
|
|
@@ -8466,6 +8627,117 @@ function rankSemanticOnlyResults(query, semanticResults, options) {
|
|
|
8466
8627
|
});
|
|
8467
8628
|
}
|
|
8468
8629
|
|
|
8630
|
+
// src/tools/symbol-inference.ts
|
|
8631
|
+
var IDENTIFIER_RE = /[A-Za-z_$][A-Za-z0-9_$]*/g;
|
|
8632
|
+
var QUOTED_BACKTICK_RE = /`([^`]+)`/g;
|
|
8633
|
+
var QUOTED_SINGLE_RE = /'([^'\\]+)'/g;
|
|
8634
|
+
var QUOTED_DOUBLE_RE = /"([^"]+)"/g;
|
|
8635
|
+
var SYMBOL_LIKE_RE = /^(?:[A-Za-z_$][A-Za-z0-9_$]*)$/;
|
|
8636
|
+
var CAMEL_CASE_RE = /^[a-z_][A-Za-z0-9_$]*[A-Z][A-Za-z0-9_$]*$/;
|
|
8637
|
+
var PASCAL_CASE_RE = /^[A-Z][A-Za-z0-9_$]*$/;
|
|
8638
|
+
var SNAKE_CASE_RE = /^[a-z][a-z0-9_]*_[a-z0-9_]+$/;
|
|
8639
|
+
var DEFINITION_INTENT_RE = /\b(where|defined|definition|define|declaration|symbol|function|method|class|interface|type)\b/i;
|
|
8640
|
+
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
8641
|
+
"a",
|
|
8642
|
+
"an",
|
|
8643
|
+
"and",
|
|
8644
|
+
"are",
|
|
8645
|
+
"at",
|
|
8646
|
+
"for",
|
|
8647
|
+
"find",
|
|
8648
|
+
"how",
|
|
8649
|
+
"i",
|
|
8650
|
+
"in",
|
|
8651
|
+
"is",
|
|
8652
|
+
"it",
|
|
8653
|
+
"of",
|
|
8654
|
+
"on",
|
|
8655
|
+
"that",
|
|
8656
|
+
"the",
|
|
8657
|
+
"definition",
|
|
8658
|
+
"show",
|
|
8659
|
+
"to",
|
|
8660
|
+
"where",
|
|
8661
|
+
"which",
|
|
8662
|
+
"what",
|
|
8663
|
+
"you",
|
|
8664
|
+
"your",
|
|
8665
|
+
"with"
|
|
8666
|
+
]);
|
|
8667
|
+
function stripCallSuffix(token) {
|
|
8668
|
+
return token.replace(/\(\s*\)$/, "");
|
|
8669
|
+
}
|
|
8670
|
+
function isLikelySymbolName(token) {
|
|
8671
|
+
if (!SYMBOL_LIKE_RE.test(token)) {
|
|
8672
|
+
return false;
|
|
8673
|
+
}
|
|
8674
|
+
if (STOP_WORDS.has(token.toLowerCase())) {
|
|
8675
|
+
return false;
|
|
8676
|
+
}
|
|
8677
|
+
return CAMEL_CASE_RE.test(token) || PASCAL_CASE_RE.test(token) || SNAKE_CASE_RE.test(token);
|
|
8678
|
+
}
|
|
8679
|
+
function extractQuotedIdentifiers(query) {
|
|
8680
|
+
const identifiers = /* @__PURE__ */ new Set();
|
|
8681
|
+
for (const match of query.matchAll(QUOTED_BACKTICK_RE)) {
|
|
8682
|
+
const candidate = stripCallSuffix(match[1].trim());
|
|
8683
|
+
if (candidate && isLikelySymbolName(candidate)) {
|
|
8684
|
+
identifiers.add(candidate);
|
|
8685
|
+
}
|
|
8686
|
+
}
|
|
8687
|
+
for (const match of query.matchAll(QUOTED_SINGLE_RE)) {
|
|
8688
|
+
const candidate = stripCallSuffix(match[1].trim());
|
|
8689
|
+
if (candidate && isLikelySymbolName(candidate)) {
|
|
8690
|
+
identifiers.add(candidate);
|
|
8691
|
+
}
|
|
8692
|
+
}
|
|
8693
|
+
for (const match of query.matchAll(QUOTED_DOUBLE_RE)) {
|
|
8694
|
+
const candidate = stripCallSuffix(match[1].trim());
|
|
8695
|
+
if (candidate && isLikelySymbolName(candidate)) {
|
|
8696
|
+
identifiers.add(candidate);
|
|
8697
|
+
}
|
|
8698
|
+
}
|
|
8699
|
+
return [...identifiers];
|
|
8700
|
+
}
|
|
8701
|
+
function extractBareIdentifiers(query) {
|
|
8702
|
+
const unquoted = query.replace(QUOTED_BACKTICK_RE, " ").replace(QUOTED_SINGLE_RE, " ").replace(QUOTED_DOUBLE_RE, " ");
|
|
8703
|
+
const identifiers = /* @__PURE__ */ new Set();
|
|
8704
|
+
for (const match of unquoted.matchAll(IDENTIFIER_RE)) {
|
|
8705
|
+
const candidate = stripCallSuffix(match[0]);
|
|
8706
|
+
if (isLikelySymbolName(candidate)) {
|
|
8707
|
+
identifiers.add(candidate);
|
|
8708
|
+
}
|
|
8709
|
+
}
|
|
8710
|
+
return [...identifiers];
|
|
8711
|
+
}
|
|
8712
|
+
function isSingleMeaningfulToken(query, symbol) {
|
|
8713
|
+
const tokens = query.replace(/[`'"()]/g, " ").split(/[^A-Za-z0-9_$]+/).map((token) => token.trim().toLowerCase()).filter((token) => token.length > 0).filter((token) => !STOP_WORDS.has(token));
|
|
8714
|
+
return tokens.length === 1 && tokens[0] === symbol.toLowerCase();
|
|
8715
|
+
}
|
|
8716
|
+
function inferExactSymbolFromQuery(query) {
|
|
8717
|
+
if (analyzeQueryIntent(query).explicitArtifactIntent) {
|
|
8718
|
+
return void 0;
|
|
8719
|
+
}
|
|
8720
|
+
const quoted = extractQuotedIdentifiers(query);
|
|
8721
|
+
if (quoted.length === 1) {
|
|
8722
|
+
return quoted[0];
|
|
8723
|
+
}
|
|
8724
|
+
if (quoted.length > 1) {
|
|
8725
|
+
return void 0;
|
|
8726
|
+
}
|
|
8727
|
+
const candidates = extractBareIdentifiers(query);
|
|
8728
|
+
if (candidates.length !== 1) {
|
|
8729
|
+
return void 0;
|
|
8730
|
+
}
|
|
8731
|
+
const candidate = candidates[0];
|
|
8732
|
+
if (DEFINITION_INTENT_RE.test(query)) {
|
|
8733
|
+
return candidate;
|
|
8734
|
+
}
|
|
8735
|
+
if (isSingleMeaningfulToken(query, candidate)) {
|
|
8736
|
+
return candidate;
|
|
8737
|
+
}
|
|
8738
|
+
return void 0;
|
|
8739
|
+
}
|
|
8740
|
+
|
|
8469
8741
|
// src/indexer/call-graph-constants.ts
|
|
8470
8742
|
var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
8471
8743
|
"function_declaration",
|
|
@@ -8968,22 +9240,6 @@ function getUniquePendingChunksFromRequests(requests) {
|
|
|
8968
9240
|
}
|
|
8969
9241
|
return Array.from(uniqueChunks.values());
|
|
8970
9242
|
}
|
|
8971
|
-
function coalesceFailedBatches(batches) {
|
|
8972
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
8973
|
-
for (const batch of batches) {
|
|
8974
|
-
const key = `${batch.attemptCount}:${batch.lastAttempt}:${batch.error}`;
|
|
8975
|
-
const existing = grouped.get(key);
|
|
8976
|
-
if (!existing) {
|
|
8977
|
-
grouped.set(key, {
|
|
8978
|
-
...batch,
|
|
8979
|
-
chunks: [...batch.chunks]
|
|
8980
|
-
});
|
|
8981
|
-
continue;
|
|
8982
|
-
}
|
|
8983
|
-
existing.chunks.push(...batch.chunks);
|
|
8984
|
-
}
|
|
8985
|
-
return Array.from(grouped.values());
|
|
8986
|
-
}
|
|
8987
9243
|
function poolEmbeddingVectors(vectors, weights) {
|
|
8988
9244
|
const firstVector = vectors[0];
|
|
8989
9245
|
if (!firstVector) {
|
|
@@ -9016,9 +9272,268 @@ function hasAllEmbeddingParts(parts, expectedPartCount) {
|
|
|
9016
9272
|
return true;
|
|
9017
9273
|
}
|
|
9018
9274
|
|
|
9275
|
+
// src/indexer/failed-state-persistence.ts
|
|
9276
|
+
var fs2 = __toESM(require("fs"), 1);
|
|
9277
|
+
var import_node_crypto = require("crypto");
|
|
9278
|
+
var path18 = __toESM(require("path"), 1);
|
|
9279
|
+
var import_node_string_decoder = require("string_decoder");
|
|
9280
|
+
var CURRENT_FAILED_BATCH_VERSION = 1;
|
|
9281
|
+
var DEFAULT_MALFORMED_LINE_ACTION = "skip";
|
|
9282
|
+
function* readFailedBatchRecords(filePath, options = {}) {
|
|
9283
|
+
if (!fs2.existsSync(filePath)) {
|
|
9284
|
+
return;
|
|
9285
|
+
}
|
|
9286
|
+
const fileFormat = detectFailedBatchFileFormat(filePath);
|
|
9287
|
+
if (fileFormat === "legacy") {
|
|
9288
|
+
yield* readLegacyFailedBatchRecords(filePath, options);
|
|
9289
|
+
return;
|
|
9290
|
+
}
|
|
9291
|
+
yield* readJsonlFailedBatchRecords(filePath, options);
|
|
9292
|
+
}
|
|
9293
|
+
function createFailedBatchWriter(targetPath) {
|
|
9294
|
+
const temporaryPath = createTemporaryPath(targetPath);
|
|
9295
|
+
let finalized = false;
|
|
9296
|
+
fs2.mkdirSync(path18.dirname(targetPath), { recursive: true });
|
|
9297
|
+
fs2.closeSync(fs2.openSync(temporaryPath, "w"));
|
|
9298
|
+
const write = (record) => {
|
|
9299
|
+
if (finalized) {
|
|
9300
|
+
throw new Error("Failed batch writer has been finalized");
|
|
9301
|
+
}
|
|
9302
|
+
const lines = record.chunks.map((chunk) => {
|
|
9303
|
+
const lineRecord = {
|
|
9304
|
+
version: CURRENT_FAILED_BATCH_VERSION,
|
|
9305
|
+
chunks: [chunk],
|
|
9306
|
+
error: record.error,
|
|
9307
|
+
attemptCount: record.attemptCount,
|
|
9308
|
+
lastAttempt: record.lastAttempt
|
|
9309
|
+
};
|
|
9310
|
+
return JSON.stringify(lineRecord);
|
|
9311
|
+
});
|
|
9312
|
+
if (lines.length === 0) {
|
|
9313
|
+
return;
|
|
9314
|
+
}
|
|
9315
|
+
fs2.mkdirSync(path18.dirname(targetPath), { recursive: true });
|
|
9316
|
+
fs2.appendFileSync(temporaryPath, `${lines.join("\n")}
|
|
9317
|
+
`, "utf-8");
|
|
9318
|
+
};
|
|
9319
|
+
const commit = () => {
|
|
9320
|
+
if (finalized) {
|
|
9321
|
+
return;
|
|
9322
|
+
}
|
|
9323
|
+
fs2.mkdirSync(path18.dirname(targetPath), { recursive: true });
|
|
9324
|
+
fs2.renameSync(temporaryPath, targetPath);
|
|
9325
|
+
finalized = true;
|
|
9326
|
+
};
|
|
9327
|
+
const cleanup = () => {
|
|
9328
|
+
if (finalized) {
|
|
9329
|
+
return;
|
|
9330
|
+
}
|
|
9331
|
+
fs2.rmSync(temporaryPath, { force: true });
|
|
9332
|
+
};
|
|
9333
|
+
return {
|
|
9334
|
+
write,
|
|
9335
|
+
commit,
|
|
9336
|
+
cleanup,
|
|
9337
|
+
temporaryPath
|
|
9338
|
+
};
|
|
9339
|
+
}
|
|
9340
|
+
function* readLegacyFailedBatchRecords(filePath, options) {
|
|
9341
|
+
const rawData = fs2.readFileSync(filePath, "utf-8");
|
|
9342
|
+
const trimmed = stripLeadingBomAndWhitespace(rawData).trim();
|
|
9343
|
+
if (trimmed.length === 0) {
|
|
9344
|
+
return;
|
|
9345
|
+
}
|
|
9346
|
+
let parsed;
|
|
9347
|
+
try {
|
|
9348
|
+
parsed = JSON.parse(trimmed);
|
|
9349
|
+
} catch (error) {
|
|
9350
|
+
handleMalformedLine(filePath, 1, trimmed, error, options);
|
|
9351
|
+
return;
|
|
9352
|
+
}
|
|
9353
|
+
if (!Array.isArray(parsed)) {
|
|
9354
|
+
handleMalformedLine(filePath, 1, trimmed, new Error("Expected legacy failed-batch file to contain a JSON array"), options);
|
|
9355
|
+
return;
|
|
9356
|
+
}
|
|
9357
|
+
for (const entry of parsed) {
|
|
9358
|
+
const normalized = normalizeFailedBatchRecord(entry);
|
|
9359
|
+
if (normalized) {
|
|
9360
|
+
yield normalized;
|
|
9361
|
+
}
|
|
9362
|
+
}
|
|
9363
|
+
}
|
|
9364
|
+
function* readJsonlFailedBatchRecords(filePath, options) {
|
|
9365
|
+
const handle = fs2.openSync(filePath, "r");
|
|
9366
|
+
const decoder = new import_node_string_decoder.StringDecoder("utf8");
|
|
9367
|
+
const readBuffer = Buffer.allocUnsafe(64 * 1024);
|
|
9368
|
+
let buffer = "";
|
|
9369
|
+
let lineNumber = 0;
|
|
9370
|
+
try {
|
|
9371
|
+
let bytesRead = 0;
|
|
9372
|
+
do {
|
|
9373
|
+
bytesRead = fs2.readSync(handle, readBuffer, 0, readBuffer.length, null);
|
|
9374
|
+
buffer += decoder.write(readBuffer.subarray(0, bytesRead));
|
|
9375
|
+
let newlineIndex = buffer.indexOf("\n");
|
|
9376
|
+
while (newlineIndex >= 0) {
|
|
9377
|
+
const rawLine = buffer.slice(0, newlineIndex);
|
|
9378
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
9379
|
+
lineNumber += 1;
|
|
9380
|
+
const normalized = parseFailedBatchLine(rawLine, filePath, lineNumber, options);
|
|
9381
|
+
if (normalized) {
|
|
9382
|
+
yield normalized;
|
|
9383
|
+
}
|
|
9384
|
+
newlineIndex = buffer.indexOf("\n");
|
|
9385
|
+
}
|
|
9386
|
+
} while (bytesRead > 0);
|
|
9387
|
+
buffer += decoder.end();
|
|
9388
|
+
const finalLine = buffer.trimEnd();
|
|
9389
|
+
if (finalLine.length > 0) {
|
|
9390
|
+
lineNumber += 1;
|
|
9391
|
+
const normalized = parseFailedBatchLine(finalLine, filePath, lineNumber, options);
|
|
9392
|
+
if (normalized) {
|
|
9393
|
+
yield normalized;
|
|
9394
|
+
}
|
|
9395
|
+
}
|
|
9396
|
+
} finally {
|
|
9397
|
+
fs2.closeSync(handle);
|
|
9398
|
+
}
|
|
9399
|
+
}
|
|
9400
|
+
function parseFailedBatchLine(rawLine, filePath, lineNumber, options) {
|
|
9401
|
+
const line = rawLine.trimEnd();
|
|
9402
|
+
if (line.length === 0) {
|
|
9403
|
+
return null;
|
|
9404
|
+
}
|
|
9405
|
+
try {
|
|
9406
|
+
const parsed = JSON.parse(line);
|
|
9407
|
+
const normalized = normalizeFailedBatchRecord(parsed);
|
|
9408
|
+
if (!normalized) {
|
|
9409
|
+
handleMalformedLine(filePath, lineNumber, line, new Error("Malformed failed-batch record"), options);
|
|
9410
|
+
return null;
|
|
9411
|
+
}
|
|
9412
|
+
return normalized;
|
|
9413
|
+
} catch (error) {
|
|
9414
|
+
handleMalformedLine(filePath, lineNumber, line, error, options);
|
|
9415
|
+
return null;
|
|
9416
|
+
}
|
|
9417
|
+
}
|
|
9418
|
+
function normalizeFailedBatchRecord(rawRecord) {
|
|
9419
|
+
if (!rawRecord || typeof rawRecord !== "object" || Array.isArray(rawRecord)) {
|
|
9420
|
+
return null;
|
|
9421
|
+
}
|
|
9422
|
+
const typed = rawRecord;
|
|
9423
|
+
const chunks = Array.isArray(typed.chunks) ? typed.chunks : null;
|
|
9424
|
+
if (!chunks || chunks.length === 0) {
|
|
9425
|
+
return null;
|
|
9426
|
+
}
|
|
9427
|
+
return {
|
|
9428
|
+
version: typeof typed.version === "number" && Number.isFinite(typed.version) ? typed.version : CURRENT_FAILED_BATCH_VERSION,
|
|
9429
|
+
chunks,
|
|
9430
|
+
error: typeof typed.error === "string" ? typed.error : "Unknown embedding error",
|
|
9431
|
+
attemptCount: typeof typed.attemptCount === "number" && Number.isFinite(typed.attemptCount) ? typed.attemptCount : 1,
|
|
9432
|
+
lastAttempt: typeof typed.lastAttempt === "string" ? typed.lastAttempt : (/* @__PURE__ */ new Date()).toISOString()
|
|
9433
|
+
};
|
|
9434
|
+
}
|
|
9435
|
+
function detectFailedBatchFileFormat(filePath) {
|
|
9436
|
+
const handle = fs2.openSync(filePath, "r");
|
|
9437
|
+
try {
|
|
9438
|
+
const buffer = Buffer.alloc(4096);
|
|
9439
|
+
const bytesRead = fs2.readSync(handle, buffer, 0, buffer.length, 0);
|
|
9440
|
+
if (bytesRead <= 0) {
|
|
9441
|
+
return "jsonl";
|
|
9442
|
+
}
|
|
9443
|
+
const prefix = stripLeadingBomAndWhitespace(buffer.subarray(0, bytesRead).toString("utf-8"));
|
|
9444
|
+
return prefix.startsWith("[") ? "legacy" : "jsonl";
|
|
9445
|
+
} finally {
|
|
9446
|
+
fs2.closeSync(handle);
|
|
9447
|
+
}
|
|
9448
|
+
}
|
|
9449
|
+
function stripLeadingBomAndWhitespace(value) {
|
|
9450
|
+
let result = value.trimStart();
|
|
9451
|
+
if (result.charCodeAt(0) === 65279) {
|
|
9452
|
+
result = result.slice(1);
|
|
9453
|
+
}
|
|
9454
|
+
return result;
|
|
9455
|
+
}
|
|
9456
|
+
function createTemporaryPath(targetPath) {
|
|
9457
|
+
const randomId = (0, import_node_crypto.createHash)("sha1").update(`${Date.now()}:${(0, import_node_crypto.randomBytes)(8).toString("hex")}`).digest("hex");
|
|
9458
|
+
const targetDir = path18.dirname(targetPath);
|
|
9459
|
+
const baseName = path18.basename(targetPath);
|
|
9460
|
+
return path18.join(targetDir, `.${baseName}.${randomId}.tmp`);
|
|
9461
|
+
}
|
|
9462
|
+
function handleMalformedLine(filePath, lineNumber, line, error, options) {
|
|
9463
|
+
const action = options.malformedLineAction ?? DEFAULT_MALFORMED_LINE_ACTION;
|
|
9464
|
+
const normalizedError = error instanceof Error ? error : new Error(String(error));
|
|
9465
|
+
if (options.onMalformedLine) {
|
|
9466
|
+
options.onMalformedLine(normalizedError, line, lineNumber, filePath);
|
|
9467
|
+
}
|
|
9468
|
+
if (action === "fail") {
|
|
9469
|
+
throw normalizedError;
|
|
9470
|
+
}
|
|
9471
|
+
}
|
|
9472
|
+
|
|
9473
|
+
// src/indexer/file-batches.ts
|
|
9474
|
+
var INDEX_FILE_BATCH_LIMITS = Object.freeze({
|
|
9475
|
+
maxFiles: 64,
|
|
9476
|
+
maxBytes: 8 * 1024 * 1024
|
|
9477
|
+
});
|
|
9478
|
+
function* iterateOrderedFileBatches(items, getBytes, limits = INDEX_FILE_BATCH_LIMITS) {
|
|
9479
|
+
const maxFiles = Math.max(1, Math.floor(limits.maxFiles));
|
|
9480
|
+
const maxBytes = Math.max(1, Math.floor(limits.maxBytes));
|
|
9481
|
+
let batch = [];
|
|
9482
|
+
let batchBytes = 0;
|
|
9483
|
+
for (const item of items) {
|
|
9484
|
+
const itemBytes = Math.max(0, Math.floor(getBytes(item)));
|
|
9485
|
+
if (batch.length > 0 && (batch.length >= maxFiles || batchBytes + itemBytes > maxBytes)) {
|
|
9486
|
+
yield batch;
|
|
9487
|
+
batch = [];
|
|
9488
|
+
batchBytes = 0;
|
|
9489
|
+
}
|
|
9490
|
+
batch.push(item);
|
|
9491
|
+
batchBytes += itemBytes;
|
|
9492
|
+
}
|
|
9493
|
+
if (batch.length > 0) {
|
|
9494
|
+
yield batch;
|
|
9495
|
+
}
|
|
9496
|
+
}
|
|
9497
|
+
|
|
9019
9498
|
// src/indexer/index.ts
|
|
9020
9499
|
var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "swift", "php", "apex", "zig", "gdscript", "matlab", "bash", "c", "cpp", "metal"]);
|
|
9021
9500
|
var CASE_INSENSITIVE_LANGUAGES = /* @__PURE__ */ new Set(["apex", "php"]);
|
|
9501
|
+
function candidateOverlapsSymbol(candidate, symbol) {
|
|
9502
|
+
return candidate.metadata.filePath === symbol.filePath && candidate.metadata.startLine <= symbol.endLine && candidate.metadata.endLine >= symbol.startLine;
|
|
9503
|
+
}
|
|
9504
|
+
function resolveSameCommunityCandidateIds(query, candidates, database, branchCatalogKeys) {
|
|
9505
|
+
const anchorName = inferExactSymbolFromQuery(query);
|
|
9506
|
+
if (!anchorName || candidates.length === 0) {
|
|
9507
|
+
return /* @__PURE__ */ new Set();
|
|
9508
|
+
}
|
|
9509
|
+
const catalogs = branchCatalogKeys.map((branchKey) => ({
|
|
9510
|
+
branchKey,
|
|
9511
|
+
symbols: database.getSymbolsForBranch(branchKey)
|
|
9512
|
+
}));
|
|
9513
|
+
const exactAnchors = catalogs.flatMap(({ branchKey, symbols }) => symbols.filter((symbol) => symbol.name === anchorName).map((symbol) => ({ branchKey, symbol })));
|
|
9514
|
+
const anchors = exactAnchors.length > 0 ? exactAnchors : catalogs.flatMap(({ branchKey, symbols }) => symbols.filter((symbol) => symbol.name.toLowerCase() === anchorName.toLowerCase()).map((symbol) => ({ branchKey, symbol })));
|
|
9515
|
+
const uniqueAnchors = new Map(anchors.map((anchor2) => [anchor2.symbol.id, anchor2]));
|
|
9516
|
+
if (uniqueAnchors.size !== 1) {
|
|
9517
|
+
return /* @__PURE__ */ new Set();
|
|
9518
|
+
}
|
|
9519
|
+
const anchor = uniqueAnchors.values().next().value;
|
|
9520
|
+
const branchSymbols = catalogs.find((catalog) => catalog.branchKey === anchor.branchKey)?.symbols ?? [];
|
|
9521
|
+
const candidateSymbols = branchSymbols.filter(
|
|
9522
|
+
(symbol) => candidates.some((candidate) => candidateOverlapsSymbol(candidate, symbol))
|
|
9523
|
+
);
|
|
9524
|
+
const assignments = database.detectCommunities(
|
|
9525
|
+
anchor.branchKey,
|
|
9526
|
+
[anchor.symbol.id, ...candidateSymbols.map((symbol) => symbol.id)]
|
|
9527
|
+
);
|
|
9528
|
+
const anchorCommunity = assignments.find((assignment) => assignment.symbolId === anchor.symbol.id)?.communityId;
|
|
9529
|
+
if (anchorCommunity === void 0) {
|
|
9530
|
+
return /* @__PURE__ */ new Set();
|
|
9531
|
+
}
|
|
9532
|
+
const sameCommunitySymbolIds = new Set(assignments.filter((assignment) => assignment.communityId === anchorCommunity).map((assignment) => assignment.symbolId));
|
|
9533
|
+
return new Set(candidates.filter((candidate) => candidateSymbols.some(
|
|
9534
|
+
(symbol) => sameCommunitySymbolIds.has(symbol.id) && candidateOverlapsSymbol(candidate, symbol)
|
|
9535
|
+
)).map((candidate) => candidate.id));
|
|
9536
|
+
}
|
|
9022
9537
|
var CALL_GRAPH_RESOLUTION_VERSION = "4";
|
|
9023
9538
|
var PHP_FUNCTION_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
9024
9539
|
"function_declaration",
|
|
@@ -9119,6 +9634,16 @@ function isSqliteCorruptionError(error) {
|
|
|
9119
9634
|
}
|
|
9120
9635
|
var STARTUP_WARNING_METADATA_KEY = "index.startupWarning";
|
|
9121
9636
|
var READER_ARTIFACT_RETRY_INTERVAL_MS = 1e3;
|
|
9637
|
+
function getFailedBatchGroupKey(record) {
|
|
9638
|
+
return `${record.attemptCount}:${record.lastAttempt}:${record.error}`;
|
|
9639
|
+
}
|
|
9640
|
+
function getPendingChunkId(rawChunk) {
|
|
9641
|
+
if (!rawChunk || typeof rawChunk !== "object") {
|
|
9642
|
+
return null;
|
|
9643
|
+
}
|
|
9644
|
+
const id = rawChunk.id;
|
|
9645
|
+
return typeof id === "string" ? id : null;
|
|
9646
|
+
}
|
|
9122
9647
|
function metadataFromBlame(blame) {
|
|
9123
9648
|
if (!blame) {
|
|
9124
9649
|
return {};
|
|
@@ -9166,9 +9691,9 @@ var SWIFT_PARSER_VERSION = "1";
|
|
|
9166
9691
|
var METAL_PARSER_VERSION = "1";
|
|
9167
9692
|
var SYMBOL_EXTRACTOR_VERSION = "1";
|
|
9168
9693
|
function isPathWithinRoot2(filePath, rootPath) {
|
|
9169
|
-
const normalizedFilePath =
|
|
9170
|
-
const normalizedRoot =
|
|
9171
|
-
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${
|
|
9694
|
+
const normalizedFilePath = path19.resolve(filePath);
|
|
9695
|
+
const normalizedRoot = path19.resolve(rootPath);
|
|
9696
|
+
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path19.sep}`);
|
|
9172
9697
|
}
|
|
9173
9698
|
function promoteIdentifierMatches(query, combined, semanticCandidates, keywordCandidates, database, branchChunkIds, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source") {
|
|
9174
9699
|
if (combined.length === 0) {
|
|
@@ -9499,10 +10024,10 @@ function matchesHardSearchFilters(candidate, options, projectRoot3) {
|
|
|
9499
10024
|
}
|
|
9500
10025
|
if (options?.directory) {
|
|
9501
10026
|
const candidatePath = canonicalizePathForComparison(
|
|
9502
|
-
|
|
10027
|
+
path19.resolve(projectRoot3, candidate.metadata.filePath.replace(/\\/g, path19.sep))
|
|
9503
10028
|
);
|
|
9504
10029
|
const directoryPath = canonicalizePathForComparison(
|
|
9505
|
-
|
|
10030
|
+
path19.resolve(projectRoot3, options.directory.trim().replace(/\\/g, path19.sep))
|
|
9506
10031
|
);
|
|
9507
10032
|
if (!isPathWithinRoot2(candidatePath, directoryPath)) return false;
|
|
9508
10033
|
}
|
|
@@ -9578,6 +10103,7 @@ var Indexer = class _Indexer {
|
|
|
9578
10103
|
readerArtifactFingerprint = null;
|
|
9579
10104
|
writerArtifactFingerprint = null;
|
|
9580
10105
|
readerArtifactRetryAfter = /* @__PURE__ */ new Map();
|
|
10106
|
+
fileBatchLimits;
|
|
9581
10107
|
constructor(projectRoot3, config, host, runtimeOptions = {}) {
|
|
9582
10108
|
this.projectRoot = projectRoot3;
|
|
9583
10109
|
this.projectIdentityHash = hashContent(this.getCanonicalPath(projectRoot3)).slice(0, 16);
|
|
@@ -9589,6 +10115,7 @@ var Indexer = class _Indexer {
|
|
|
9589
10115
|
}
|
|
9590
10116
|
this.expectedCommitOverride = runtimeOptions.expectedCommit?.toLowerCase();
|
|
9591
10117
|
this.indexPathOverride = runtimeOptions.indexPath;
|
|
10118
|
+
this.fileBatchLimits = runtimeOptions.fileBatchLimits;
|
|
9592
10119
|
this.config = config;
|
|
9593
10120
|
this.host = host;
|
|
9594
10121
|
if (isGitRepo(this.materializedProjectRoot)) {
|
|
@@ -9606,26 +10133,26 @@ var Indexer = class _Indexer {
|
|
|
9606
10133
|
return this.indexPathOverride ?? resolveProjectIndexPath(this.projectRoot, this.config.scope, this.host);
|
|
9607
10134
|
}
|
|
9608
10135
|
toCanonicalFilePath(filePath) {
|
|
9609
|
-
if (!
|
|
10136
|
+
if (!path19.isAbsolute(filePath)) {
|
|
9610
10137
|
return this.resolveStoredFilePath(filePath, this.projectRoot);
|
|
9611
10138
|
}
|
|
9612
|
-
if (
|
|
10139
|
+
if (path19.resolve(this.materializedProjectRoot) === path19.resolve(this.projectRoot) || !isPathWithinRoot2(filePath, this.materializedProjectRoot)) {
|
|
9613
10140
|
return filePath;
|
|
9614
10141
|
}
|
|
9615
|
-
return
|
|
10142
|
+
return path19.resolve(this.projectRoot, path19.relative(this.materializedProjectRoot, filePath));
|
|
9616
10143
|
}
|
|
9617
10144
|
toStoredFilePath(filePath) {
|
|
9618
10145
|
const canonicalFilePath = this.toCanonicalFilePath(filePath);
|
|
9619
10146
|
if (this.config.scope !== "project" || !isPathWithinRoot2(canonicalFilePath, this.projectRoot)) {
|
|
9620
10147
|
return canonicalFilePath;
|
|
9621
10148
|
}
|
|
9622
|
-
return
|
|
10149
|
+
return path19.relative(this.projectRoot, canonicalFilePath).split(path19.sep).join("/");
|
|
9623
10150
|
}
|
|
9624
10151
|
resolveStoredFilePath(filePath, rootPath = this.projectRoot) {
|
|
9625
|
-
if (
|
|
10152
|
+
if (path19.isAbsolute(filePath)) {
|
|
9626
10153
|
return filePath;
|
|
9627
10154
|
}
|
|
9628
|
-
const resolvedPath =
|
|
10155
|
+
const resolvedPath = path19.resolve(rootPath, ...filePath.split("/"));
|
|
9629
10156
|
if (!isPathWithinRoot2(resolvedPath, rootPath)) {
|
|
9630
10157
|
throw new Error(`Stored project path escapes project root: ${JSON.stringify(filePath)}`);
|
|
9631
10158
|
}
|
|
@@ -9649,7 +10176,7 @@ var Indexer = class _Indexer {
|
|
|
9649
10176
|
}
|
|
9650
10177
|
toMaterializedFilePath(filePath) {
|
|
9651
10178
|
const storedFilePath = this.toStoredFilePath(filePath);
|
|
9652
|
-
if (
|
|
10179
|
+
if (path19.isAbsolute(storedFilePath)) {
|
|
9653
10180
|
return storedFilePath;
|
|
9654
10181
|
}
|
|
9655
10182
|
return this.resolveStoredFilePath(storedFilePath, this.materializedProjectRoot);
|
|
@@ -9666,10 +10193,10 @@ var Indexer = class _Indexer {
|
|
|
9666
10193
|
}
|
|
9667
10194
|
getRuntimeArtifactPath(fileName) {
|
|
9668
10195
|
const namespace = this.getRuntimeArtifactNamespace();
|
|
9669
|
-
if (!namespace) return
|
|
9670
|
-
const extension =
|
|
10196
|
+
if (!namespace) return path19.join(this.indexPath, fileName);
|
|
10197
|
+
const extension = path19.extname(fileName);
|
|
9671
10198
|
const baseName = fileName.slice(0, fileName.length - extension.length);
|
|
9672
|
-
return
|
|
10199
|
+
return path19.join(this.indexPath, `${baseName}.${namespace}${extension}`);
|
|
9673
10200
|
}
|
|
9674
10201
|
refreshRuntimeArtifactPaths() {
|
|
9675
10202
|
this.fileHashCachePath = this.getRuntimeArtifactPath("file-hashes.json");
|
|
@@ -9682,14 +10209,14 @@ var Indexer = class _Indexer {
|
|
|
9682
10209
|
getMaterializedKnowledgeBases() {
|
|
9683
10210
|
const canonicalProjectRoot = this.getCanonicalPath(this.projectRoot);
|
|
9684
10211
|
return this.config.knowledgeBases.map((knowledgeBase) => {
|
|
9685
|
-
const configuredPath =
|
|
10212
|
+
const configuredPath = path19.isAbsolute(knowledgeBase) ? knowledgeBase : path19.resolve(this.projectRoot, knowledgeBase);
|
|
9686
10213
|
const canonicalPath = this.getCanonicalPath(configuredPath);
|
|
9687
10214
|
if (!isPathWithinRoot2(canonicalPath, canonicalProjectRoot)) {
|
|
9688
10215
|
return canonicalPath;
|
|
9689
10216
|
}
|
|
9690
|
-
return
|
|
10217
|
+
return path19.resolve(
|
|
9691
10218
|
this.materializedProjectRoot,
|
|
9692
|
-
|
|
10219
|
+
path19.relative(canonicalProjectRoot, canonicalPath)
|
|
9693
10220
|
);
|
|
9694
10221
|
});
|
|
9695
10222
|
}
|
|
@@ -9697,7 +10224,7 @@ var Indexer = class _Indexer {
|
|
|
9697
10224
|
try {
|
|
9698
10225
|
return canonicalizePathForComparison(targetPath);
|
|
9699
10226
|
} catch {
|
|
9700
|
-
return
|
|
10227
|
+
return path19.resolve(targetPath);
|
|
9701
10228
|
}
|
|
9702
10229
|
}
|
|
9703
10230
|
isProjectOwnedIndexPath() {
|
|
@@ -9817,7 +10344,7 @@ var Indexer = class _Indexer {
|
|
|
9817
10344
|
atomicWriteSync(targetPath, data) {
|
|
9818
10345
|
const lease = this.requireActiveLease();
|
|
9819
10346
|
const tempPath = createLeaseTemporaryPath(targetPath, lease.owner, "tmp");
|
|
9820
|
-
(0, import_fs12.mkdirSync)(
|
|
10347
|
+
(0, import_fs12.mkdirSync)(path19.dirname(targetPath), { recursive: true });
|
|
9821
10348
|
try {
|
|
9822
10349
|
(0, import_fs12.writeFileSync)(tempPath, data);
|
|
9823
10350
|
(0, import_fs12.renameSync)(tempPath, targetPath);
|
|
@@ -9827,14 +10354,14 @@ var Indexer = class _Indexer {
|
|
|
9827
10354
|
}
|
|
9828
10355
|
saveInvertedIndex(invertedIndex) {
|
|
9829
10356
|
this.atomicWriteSync(
|
|
9830
|
-
|
|
10357
|
+
path19.join(this.indexPath, "inverted-index.json"),
|
|
9831
10358
|
invertedIndex.serialize()
|
|
9832
10359
|
);
|
|
9833
10360
|
}
|
|
9834
10361
|
getScopedRoots() {
|
|
9835
10362
|
const roots = /* @__PURE__ */ new Set([this.getCanonicalPath(this.projectRoot)]);
|
|
9836
10363
|
for (const kbRoot of this.config.knowledgeBases) {
|
|
9837
|
-
roots.add(this.getCanonicalPath(
|
|
10364
|
+
roots.add(this.getCanonicalPath(path19.resolve(this.projectRoot, kbRoot)));
|
|
9838
10365
|
}
|
|
9839
10366
|
return Array.from(roots);
|
|
9840
10367
|
}
|
|
@@ -9850,6 +10377,9 @@ var Indexer = class _Indexer {
|
|
|
9850
10377
|
}
|
|
9851
10378
|
return `${this.projectIdentityHash}:${branchName}`;
|
|
9852
10379
|
}
|
|
10380
|
+
resolveBranchCatalogKey(branchName) {
|
|
10381
|
+
return branchName === void 0 ? this.getBranchCatalogKey() : this.getBranchCatalogKeyFor(branchName);
|
|
10382
|
+
}
|
|
9853
10383
|
getBranchCommitMetadataKey(catalogIdentity = this.getBranchCatalogIdentity()) {
|
|
9854
10384
|
const branchKey = this.getBranchCatalogKeyFor(catalogIdentity);
|
|
9855
10385
|
return `index.branchCommit.${hashContent(branchKey).slice(0, 24)}`;
|
|
@@ -9944,13 +10474,13 @@ var Indexer = class _Indexer {
|
|
|
9944
10474
|
if (Array.from(this.fileHashCache.keys()).some((filePath) => this.isFileInCurrentScope(filePath, roots))) {
|
|
9945
10475
|
return true;
|
|
9946
10476
|
}
|
|
9947
|
-
|
|
9948
|
-
(batch
|
|
10477
|
+
for (const batch of this.loadSerializedFailedBatches()) {
|
|
10478
|
+
if (batch.chunks.some((chunk) => {
|
|
9949
10479
|
const filePath = getPendingChunkFilePath(chunk);
|
|
9950
10480
|
return filePath !== null && this.isFileInCurrentScope(filePath, roots);
|
|
9951
|
-
})
|
|
9952
|
-
|
|
9953
|
-
|
|
10481
|
+
})) {
|
|
10482
|
+
return true;
|
|
10483
|
+
}
|
|
9954
10484
|
}
|
|
9955
10485
|
if (!this.database) {
|
|
9956
10486
|
return false;
|
|
@@ -10091,40 +10621,25 @@ var Indexer = class _Indexer {
|
|
|
10091
10621
|
}
|
|
10092
10622
|
this.saveFileHashCache();
|
|
10093
10623
|
}
|
|
10094
|
-
partitionFailedBatches(roots, maxChunkTokens) {
|
|
10095
|
-
const scoped = [];
|
|
10096
|
-
const retained = [];
|
|
10097
|
-
for (const batch of this.loadSerializedFailedBatches()) {
|
|
10098
|
-
const scopedChunks = batch.chunks.filter((chunk) => {
|
|
10099
|
-
const filePath = getPendingChunkFilePath(chunk);
|
|
10100
|
-
return filePath !== null && this.isFileInCurrentScope(filePath, roots);
|
|
10101
|
-
});
|
|
10102
|
-
const retainedChunks = batch.chunks.filter((chunk) => {
|
|
10103
|
-
const filePath = getPendingChunkFilePath(chunk);
|
|
10104
|
-
return filePath === null || !this.isFileInCurrentScope(filePath, roots);
|
|
10105
|
-
});
|
|
10106
|
-
if (scopedChunks.length > 0) {
|
|
10107
|
-
const normalizedBatch = normalizeFailedBatch({ ...batch, chunks: scopedChunks }, maxChunkTokens);
|
|
10108
|
-
if (normalizedBatch) {
|
|
10109
|
-
scoped.push(normalizedBatch);
|
|
10110
|
-
}
|
|
10111
|
-
}
|
|
10112
|
-
if (retainedChunks.length > 0) {
|
|
10113
|
-
retained.push({ ...batch, chunks: retainedChunks });
|
|
10114
|
-
}
|
|
10115
|
-
}
|
|
10116
|
-
return { scoped, retained };
|
|
10117
|
-
}
|
|
10118
10624
|
clearScopedFailedBatches(roots) {
|
|
10119
|
-
|
|
10120
|
-
|
|
10625
|
+
this.rewriteFailedBatchState((chunk) => {
|
|
10626
|
+
const filePath = getPendingChunkFilePath(chunk);
|
|
10627
|
+
return filePath === null || !this.isFileInCurrentScope(filePath, roots);
|
|
10628
|
+
});
|
|
10121
10629
|
}
|
|
10122
10630
|
hasForeignScopedFileHashData(roots) {
|
|
10123
10631
|
return Array.from(this.fileHashCache.keys()).some((filePath) => !this.isFileInCurrentScope(filePath, roots));
|
|
10124
10632
|
}
|
|
10125
10633
|
hasForeignScopedFailedBatches(roots) {
|
|
10126
|
-
const
|
|
10127
|
-
|
|
10634
|
+
for (const batch of this.loadSerializedFailedBatches()) {
|
|
10635
|
+
if (batch.chunks.some((chunk) => {
|
|
10636
|
+
const filePath = getPendingChunkFilePath(chunk);
|
|
10637
|
+
return filePath === null || !this.isFileInCurrentScope(filePath, roots);
|
|
10638
|
+
})) {
|
|
10639
|
+
return true;
|
|
10640
|
+
}
|
|
10641
|
+
}
|
|
10642
|
+
return false;
|
|
10128
10643
|
}
|
|
10129
10644
|
hasForeignScopedBranchData() {
|
|
10130
10645
|
if (!this.database || this.config.scope !== "global") {
|
|
@@ -10149,10 +10664,6 @@ var Indexer = class _Indexer {
|
|
|
10149
10664
|
}
|
|
10150
10665
|
);
|
|
10151
10666
|
}
|
|
10152
|
-
saveScopedFailedBatches(batches, roots) {
|
|
10153
|
-
const { retained } = this.partitionFailedBatches(roots);
|
|
10154
|
-
this.saveFailedBatches([...retained, ...batches]);
|
|
10155
|
-
}
|
|
10156
10667
|
clearSharedIndexProjectData(store, invertedIndex, database, roots) {
|
|
10157
10668
|
const allMetadata = store.getAllMetadata();
|
|
10158
10669
|
const scopedEntries = allMetadata.filter(({ metadata }) => this.isFileInCurrentScope(metadata.filePath, roots));
|
|
@@ -10249,70 +10760,137 @@ var Indexer = class _Indexer {
|
|
|
10249
10760
|
}
|
|
10250
10761
|
this.logger.info("Recovery complete, next index will re-process all files");
|
|
10251
10762
|
}
|
|
10252
|
-
|
|
10253
|
-
|
|
10254
|
-
|
|
10255
|
-
|
|
10256
|
-
|
|
10763
|
+
*loadSerializedFailedBatches() {
|
|
10764
|
+
let warned = false;
|
|
10765
|
+
const warn = (error) => {
|
|
10766
|
+
if (warned) return;
|
|
10767
|
+
warned = true;
|
|
10257
10768
|
this.logger.warn("Failed to load failed batch state, skipping persisted retries", {
|
|
10258
10769
|
failedBatchesPath: this.failedBatchesPath,
|
|
10259
|
-
error:
|
|
10770
|
+
error: getErrorMessage4(error)
|
|
10260
10771
|
});
|
|
10261
|
-
|
|
10772
|
+
};
|
|
10773
|
+
try {
|
|
10774
|
+
for (const record of readFailedBatchRecords(this.failedBatchesPath, {
|
|
10775
|
+
malformedLineAction: "skip",
|
|
10776
|
+
onMalformedLine: (error) => warn(error)
|
|
10777
|
+
})) {
|
|
10778
|
+
yield {
|
|
10779
|
+
chunks: record.chunks,
|
|
10780
|
+
error: record.error,
|
|
10781
|
+
attemptCount: record.attemptCount,
|
|
10782
|
+
lastAttempt: record.lastAttempt
|
|
10783
|
+
};
|
|
10784
|
+
}
|
|
10785
|
+
} catch (error) {
|
|
10786
|
+
warn(error);
|
|
10262
10787
|
}
|
|
10263
10788
|
}
|
|
10264
|
-
|
|
10265
|
-
|
|
10266
|
-
|
|
10789
|
+
createFailedBatchWriteState() {
|
|
10790
|
+
return {
|
|
10791
|
+
writer: createFailedBatchWriter(this.failedBatchesPath),
|
|
10792
|
+
recordsWritten: 0
|
|
10793
|
+
};
|
|
10794
|
+
}
|
|
10795
|
+
writeFailedBatchRecord(state, record) {
|
|
10796
|
+
state.writer.write(record);
|
|
10797
|
+
state.recordsWritten += record.chunks.length;
|
|
10798
|
+
}
|
|
10799
|
+
finalizeFailedBatchWriteState(state) {
|
|
10800
|
+
if (state.recordsWritten > 0) {
|
|
10801
|
+
state.writer.commit();
|
|
10802
|
+
return;
|
|
10267
10803
|
}
|
|
10268
|
-
|
|
10269
|
-
|
|
10270
|
-
|
|
10271
|
-
|
|
10272
|
-
|
|
10273
|
-
|
|
10804
|
+
state.writer.cleanup();
|
|
10805
|
+
this.clearFailedBatchState();
|
|
10806
|
+
}
|
|
10807
|
+
clearFailedBatchState() {
|
|
10808
|
+
if ((0, import_fs12.existsSync)(this.failedBatchesPath)) {
|
|
10809
|
+
try {
|
|
10810
|
+
(0, import_fs12.unlinkSync)(this.failedBatchesPath);
|
|
10811
|
+
} catch {
|
|
10274
10812
|
}
|
|
10275
|
-
|
|
10276
|
-
chunks,
|
|
10277
|
-
error: typeof batch.error === "string" ? batch.error : "Unknown embedding error",
|
|
10278
|
-
attemptCount: typeof batch.attemptCount === "number" ? batch.attemptCount : 1,
|
|
10279
|
-
lastAttempt: typeof batch.lastAttempt === "string" ? batch.lastAttempt : (/* @__PURE__ */ new Date()).toISOString()
|
|
10280
|
-
};
|
|
10281
|
-
}).filter((batch) => batch !== null);
|
|
10813
|
+
}
|
|
10282
10814
|
}
|
|
10283
|
-
|
|
10284
|
-
|
|
10285
|
-
|
|
10286
|
-
|
|
10287
|
-
|
|
10288
|
-
|
|
10815
|
+
rewriteFailedBatchState(shouldRetain) {
|
|
10816
|
+
const state = this.createFailedBatchWriteState();
|
|
10817
|
+
try {
|
|
10818
|
+
for (const batch of this.loadSerializedFailedBatches()) {
|
|
10819
|
+
const retainedChunks = batch.chunks.filter(shouldRetain);
|
|
10820
|
+
if (retainedChunks.length > 0) {
|
|
10821
|
+
this.writeFailedBatchRecord(state, { ...batch, chunks: retainedChunks });
|
|
10289
10822
|
}
|
|
10290
10823
|
}
|
|
10291
|
-
|
|
10824
|
+
this.finalizeFailedBatchWriteState(state);
|
|
10825
|
+
} catch (error) {
|
|
10826
|
+
state.writer.cleanup();
|
|
10827
|
+
throw error;
|
|
10828
|
+
}
|
|
10829
|
+
}
|
|
10830
|
+
prepareFailedBatchProcessing(roots, shouldProcess) {
|
|
10831
|
+
const state = this.createFailedBatchWriteState();
|
|
10832
|
+
const latestById = /* @__PURE__ */ new Map();
|
|
10833
|
+
try {
|
|
10834
|
+
for (const batch of this.loadSerializedFailedBatches()) {
|
|
10835
|
+
for (const rawChunk of batch.chunks) {
|
|
10836
|
+
const filePath = getPendingChunkFilePath(rawChunk);
|
|
10837
|
+
const inScope = roots === null || filePath !== null && this.isFileInCurrentScope(filePath, roots);
|
|
10838
|
+
if (!inScope) {
|
|
10839
|
+
this.writeFailedBatchRecord(state, { ...batch, chunks: [rawChunk] });
|
|
10840
|
+
continue;
|
|
10841
|
+
}
|
|
10842
|
+
if (!shouldProcess(filePath)) {
|
|
10843
|
+
continue;
|
|
10844
|
+
}
|
|
10845
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
10846
|
+
if (!chunkId) {
|
|
10847
|
+
continue;
|
|
10848
|
+
}
|
|
10849
|
+
const existing = latestById.get(chunkId);
|
|
10850
|
+
if (!existing || batch.attemptCount >= existing.attemptCount) {
|
|
10851
|
+
latestById.set(chunkId, {
|
|
10852
|
+
attemptCount: batch.attemptCount,
|
|
10853
|
+
error: batch.error,
|
|
10854
|
+
lastAttempt: batch.lastAttempt
|
|
10855
|
+
});
|
|
10856
|
+
}
|
|
10857
|
+
}
|
|
10858
|
+
}
|
|
10859
|
+
return { state, latestById };
|
|
10860
|
+
} catch (error) {
|
|
10861
|
+
state.writer.cleanup();
|
|
10862
|
+
throw error;
|
|
10292
10863
|
}
|
|
10293
|
-
this.atomicWriteSync(this.failedBatchesPath, JSON.stringify(batches, null, 2));
|
|
10294
10864
|
}
|
|
10295
|
-
|
|
10296
|
-
const
|
|
10297
|
-
for (const batch of this.
|
|
10298
|
-
for (const
|
|
10299
|
-
const
|
|
10300
|
-
if (!
|
|
10865
|
+
*iterateLatestFailedChunks(latestById, roots, shouldProcess, maxChunkTokens) {
|
|
10866
|
+
const yielded = /* @__PURE__ */ new Set();
|
|
10867
|
+
for (const batch of this.loadSerializedFailedBatches()) {
|
|
10868
|
+
for (const rawChunk of batch.chunks) {
|
|
10869
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
10870
|
+
if (!chunkId || yielded.has(chunkId)) {
|
|
10301
10871
|
continue;
|
|
10302
10872
|
}
|
|
10303
|
-
|
|
10873
|
+
const latest = latestById.get(chunkId);
|
|
10874
|
+
if (!latest || latest.attemptCount !== batch.attemptCount || latest.error !== batch.error || latest.lastAttempt !== batch.lastAttempt) {
|
|
10304
10875
|
continue;
|
|
10305
10876
|
}
|
|
10306
|
-
const
|
|
10307
|
-
|
|
10308
|
-
|
|
10309
|
-
|
|
10310
|
-
|
|
10311
|
-
|
|
10877
|
+
const filePath = getPendingChunkFilePath(rawChunk);
|
|
10878
|
+
const inScope = roots === null || filePath !== null && this.isFileInCurrentScope(filePath, roots);
|
|
10879
|
+
if (!inScope || !shouldProcess(filePath)) {
|
|
10880
|
+
continue;
|
|
10881
|
+
}
|
|
10882
|
+
const normalized = normalizeFailedBatch({ ...batch, chunks: [rawChunk] }, maxChunkTokens);
|
|
10883
|
+
const chunk = normalized?.chunks[0];
|
|
10884
|
+
if (!chunk) {
|
|
10885
|
+
continue;
|
|
10312
10886
|
}
|
|
10887
|
+
yielded.add(chunkId);
|
|
10888
|
+
yield {
|
|
10889
|
+
chunk,
|
|
10890
|
+
attemptCount: batch.attemptCount
|
|
10891
|
+
};
|
|
10313
10892
|
}
|
|
10314
10893
|
}
|
|
10315
|
-
return Array.from(retryableById.values());
|
|
10316
10894
|
}
|
|
10317
10895
|
getProviderRateLimits(provider) {
|
|
10318
10896
|
switch (provider) {
|
|
@@ -10337,6 +10915,242 @@ var Indexer = class _Indexer {
|
|
|
10337
10915
|
return { concurrency: 3, intervalMs: 1e3, minRetryMs: 1e3, maxRetryMs: 3e4 };
|
|
10338
10916
|
}
|
|
10339
10917
|
}
|
|
10918
|
+
async processPendingChunkBatch(chunks, options) {
|
|
10919
|
+
const result = {
|
|
10920
|
+
indexedChunks: 0,
|
|
10921
|
+
failedChunks: 0,
|
|
10922
|
+
tokensUsed: 0,
|
|
10923
|
+
failedChunkIds: /* @__PURE__ */ new Set()
|
|
10924
|
+
};
|
|
10925
|
+
if (chunks.length === 0) {
|
|
10926
|
+
return result;
|
|
10927
|
+
}
|
|
10928
|
+
const chunksNeedingEmbedding = [];
|
|
10929
|
+
let cachedChunkCount = 0;
|
|
10930
|
+
if (options.reuseCachedEmbeddings && !options.forceReembed) {
|
|
10931
|
+
const missingHashes = new Set(options.database.getMissingEmbeddings(chunks.map((chunk) => chunk.contentHash)));
|
|
10932
|
+
for (const chunk of chunks) {
|
|
10933
|
+
if (missingHashes.has(chunk.contentHash)) {
|
|
10934
|
+
chunksNeedingEmbedding.push(chunk);
|
|
10935
|
+
continue;
|
|
10936
|
+
}
|
|
10937
|
+
const embeddingBuffer = options.database.getEmbedding(chunk.contentHash);
|
|
10938
|
+
if (!embeddingBuffer) {
|
|
10939
|
+
chunksNeedingEmbedding.push(chunk);
|
|
10940
|
+
continue;
|
|
10941
|
+
}
|
|
10942
|
+
options.store.add(chunk.id, Array.from(bufferToFloat32Array(embeddingBuffer)), chunk.metadata);
|
|
10943
|
+
options.invertedIndex.removeChunk(chunk.id);
|
|
10944
|
+
options.invertedIndex.addChunk(chunk.id, chunk.content);
|
|
10945
|
+
options.onSucceeded?.([chunk]);
|
|
10946
|
+
result.indexedChunks += 1;
|
|
10947
|
+
cachedChunkCount += 1;
|
|
10948
|
+
}
|
|
10949
|
+
} else {
|
|
10950
|
+
chunksNeedingEmbedding.push(...chunks);
|
|
10951
|
+
}
|
|
10952
|
+
this.logger.cache("info", "Embedding cache lookup", {
|
|
10953
|
+
needsEmbedding: chunksNeedingEmbedding.length,
|
|
10954
|
+
fromCache: cachedChunkCount
|
|
10955
|
+
});
|
|
10956
|
+
if (cachedChunkCount > 0) {
|
|
10957
|
+
this.logger.recordChunksFromCache(cachedChunkCount);
|
|
10958
|
+
options.onProgress?.(result);
|
|
10959
|
+
}
|
|
10960
|
+
if (chunksNeedingEmbedding.length === 0) {
|
|
10961
|
+
return result;
|
|
10962
|
+
}
|
|
10963
|
+
const pendingChunksById = new Map(chunksNeedingEmbedding.map((chunk) => [chunk.id, chunk]));
|
|
10964
|
+
const embeddingPartsByChunk = /* @__PURE__ */ new Map();
|
|
10965
|
+
const completedVectorsByChunkId = /* @__PURE__ */ new Map();
|
|
10966
|
+
const completedChunkIds = /* @__PURE__ */ new Set();
|
|
10967
|
+
const requestBatches = createPendingEmbeddingRequestBatches(
|
|
10968
|
+
chunksNeedingEmbedding,
|
|
10969
|
+
getDynamicBatchOptions(options.configuredProviderInfo)
|
|
10970
|
+
);
|
|
10971
|
+
let fatalError;
|
|
10972
|
+
for (const requestBatch of requestBatches) {
|
|
10973
|
+
await options.queue.onSizeLessThan(Math.max(1, options.providerRateLimits.concurrency));
|
|
10974
|
+
const task = options.queue.add(async () => {
|
|
10975
|
+
if (options.rateLimitState.backoffMs > 0) {
|
|
10976
|
+
await new Promise((resolve13) => setTimeout(resolve13, options.rateLimitState.backoffMs));
|
|
10977
|
+
}
|
|
10978
|
+
try {
|
|
10979
|
+
const embeddingResult = await pRetry(
|
|
10980
|
+
async () => {
|
|
10981
|
+
const texts = requestBatch.map((request) => request.text);
|
|
10982
|
+
return options.provider.embedBatch(texts);
|
|
10983
|
+
},
|
|
10984
|
+
{
|
|
10985
|
+
retries: this.config.indexing.retries,
|
|
10986
|
+
minTimeout: Math.max(this.config.indexing.retryDelayMs, options.providerRateLimits.minRetryMs),
|
|
10987
|
+
maxTimeout: options.providerRateLimits.maxRetryMs,
|
|
10988
|
+
factor: 2,
|
|
10989
|
+
shouldRetry: (error) => !(error.error instanceof CustomProviderNonRetryableError),
|
|
10990
|
+
onFailedAttempt: (error) => {
|
|
10991
|
+
const message = getErrorMessage4(error);
|
|
10992
|
+
if (isRateLimitError(error)) {
|
|
10993
|
+
options.rateLimitState.backoffMs = Math.min(
|
|
10994
|
+
options.providerRateLimits.maxRetryMs,
|
|
10995
|
+
(options.rateLimitState.backoffMs || options.providerRateLimits.minRetryMs) * 2
|
|
10996
|
+
);
|
|
10997
|
+
this.logger.embedding("warn", "Rate limited, backing off", {
|
|
10998
|
+
attempt: error.attemptNumber,
|
|
10999
|
+
retriesLeft: error.retriesLeft,
|
|
11000
|
+
backoffMs: options.rateLimitState.backoffMs
|
|
11001
|
+
});
|
|
11002
|
+
} else {
|
|
11003
|
+
this.logger.embedding("error", "Embedding batch failed", {
|
|
11004
|
+
attempt: error.attemptNumber,
|
|
11005
|
+
error: message
|
|
11006
|
+
});
|
|
11007
|
+
}
|
|
11008
|
+
}
|
|
11009
|
+
}
|
|
11010
|
+
);
|
|
11011
|
+
if (options.rateLimitState.backoffMs > 0) {
|
|
11012
|
+
options.rateLimitState.backoffMs = Math.max(0, options.rateLimitState.backoffMs - 2e3);
|
|
11013
|
+
}
|
|
11014
|
+
const touchedChunkIds = /* @__PURE__ */ new Set();
|
|
11015
|
+
requestBatch.forEach((request, index) => {
|
|
11016
|
+
if (result.failedChunkIds.has(request.chunk.id) || completedChunkIds.has(request.chunk.id)) {
|
|
11017
|
+
return;
|
|
11018
|
+
}
|
|
11019
|
+
const vector = embeddingResult.embeddings[index];
|
|
11020
|
+
if (!vector) {
|
|
11021
|
+
throw new Error(`Embedding API returned too few vectors for chunk ${request.chunk.id}`);
|
|
11022
|
+
}
|
|
11023
|
+
const parts = embeddingPartsByChunk.get(request.chunk.id) ?? [];
|
|
11024
|
+
parts[request.partIndex] = {
|
|
11025
|
+
vector,
|
|
11026
|
+
tokenCount: request.tokenCount
|
|
11027
|
+
};
|
|
11028
|
+
embeddingPartsByChunk.set(request.chunk.id, parts);
|
|
11029
|
+
touchedChunkIds.add(request.chunk.id);
|
|
11030
|
+
});
|
|
11031
|
+
const pooledResults = [];
|
|
11032
|
+
for (const chunkId of touchedChunkIds) {
|
|
11033
|
+
if (result.failedChunkIds.has(chunkId) || completedChunkIds.has(chunkId)) {
|
|
11034
|
+
continue;
|
|
11035
|
+
}
|
|
11036
|
+
const chunk = pendingChunksById.get(chunkId);
|
|
11037
|
+
if (!chunk) {
|
|
11038
|
+
continue;
|
|
11039
|
+
}
|
|
11040
|
+
const parts = embeddingPartsByChunk.get(chunk.id) ?? [];
|
|
11041
|
+
if (!hasAllEmbeddingParts(parts, chunk.texts.length)) {
|
|
11042
|
+
continue;
|
|
11043
|
+
}
|
|
11044
|
+
const orderedParts = parts;
|
|
11045
|
+
pooledResults.push({
|
|
11046
|
+
chunk,
|
|
11047
|
+
vector: poolEmbeddingVectors(
|
|
11048
|
+
orderedParts.map((part) => part.vector),
|
|
11049
|
+
orderedParts.map((part) => part.tokenCount)
|
|
11050
|
+
)
|
|
11051
|
+
});
|
|
11052
|
+
}
|
|
11053
|
+
if (pooledResults.length > 0) {
|
|
11054
|
+
options.database.upsertEmbeddingsBatch(pooledResults.map(({ chunk, vector }) => ({
|
|
11055
|
+
contentHash: chunk.contentHash,
|
|
11056
|
+
embedding: float32ArrayToBuffer(vector),
|
|
11057
|
+
chunkText: chunk.storageText,
|
|
11058
|
+
model: options.configuredProviderInfo.modelInfo.model
|
|
11059
|
+
})));
|
|
11060
|
+
const succeededChunks = pooledResults.map(({ chunk }) => chunk);
|
|
11061
|
+
for (const { chunk, vector } of pooledResults) {
|
|
11062
|
+
completedVectorsByChunkId.set(chunk.id, vector);
|
|
11063
|
+
}
|
|
11064
|
+
for (const chunk of succeededChunks) {
|
|
11065
|
+
completedChunkIds.add(chunk.id);
|
|
11066
|
+
embeddingPartsByChunk.delete(chunk.id);
|
|
11067
|
+
}
|
|
11068
|
+
}
|
|
11069
|
+
result.tokensUsed += embeddingResult.totalTokensUsed;
|
|
11070
|
+
this.logger.recordEmbeddingApiCall(embeddingResult.totalTokensUsed);
|
|
11071
|
+
this.logger.embedding("debug", "Embedded batch", {
|
|
11072
|
+
batchSize: pooledResults.length,
|
|
11073
|
+
requestCount: requestBatch.length,
|
|
11074
|
+
tokens: embeddingResult.totalTokensUsed
|
|
11075
|
+
});
|
|
11076
|
+
} catch (error) {
|
|
11077
|
+
const failedChunks = getUniquePendingChunksFromRequests(requestBatch).filter((chunk) => !completedChunkIds.has(chunk.id)).filter((chunk) => options.incrementRepeatedFailures || !result.failedChunkIds.has(chunk.id));
|
|
11078
|
+
const failureMessage = getErrorMessage4(error);
|
|
11079
|
+
const failureTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
11080
|
+
for (const chunk of failedChunks) {
|
|
11081
|
+
if (!result.failedChunkIds.has(chunk.id)) {
|
|
11082
|
+
result.failedChunkIds.add(chunk.id);
|
|
11083
|
+
result.failedChunks += 1;
|
|
11084
|
+
}
|
|
11085
|
+
embeddingPartsByChunk.delete(chunk.id);
|
|
11086
|
+
const attemptCount = (options.attemptCounts.get(chunk.id) ?? 0) + 1;
|
|
11087
|
+
options.attemptCounts.set(chunk.id, attemptCount);
|
|
11088
|
+
this.writeFailedBatchRecord(options.failedState, {
|
|
11089
|
+
chunks: [chunk],
|
|
11090
|
+
error: failureMessage,
|
|
11091
|
+
attemptCount,
|
|
11092
|
+
lastAttempt: failureTimestamp
|
|
11093
|
+
});
|
|
11094
|
+
}
|
|
11095
|
+
this.logger.recordEmbeddingError();
|
|
11096
|
+
this.logger.embedding("error", "Failed to embed batch after retries", {
|
|
11097
|
+
batchSize: failedChunks.length,
|
|
11098
|
+
requestCount: requestBatch.length,
|
|
11099
|
+
error: failureMessage
|
|
11100
|
+
});
|
|
11101
|
+
}
|
|
11102
|
+
options.onProgress?.(result);
|
|
11103
|
+
});
|
|
11104
|
+
void task.catch((error) => {
|
|
11105
|
+
fatalError ??= error;
|
|
11106
|
+
});
|
|
11107
|
+
}
|
|
11108
|
+
await options.queue.onIdle();
|
|
11109
|
+
if (fatalError !== void 0) {
|
|
11110
|
+
throw fatalError;
|
|
11111
|
+
}
|
|
11112
|
+
const orderedSucceededChunks = chunksNeedingEmbedding.filter((chunk) => completedVectorsByChunkId.has(chunk.id));
|
|
11113
|
+
if (orderedSucceededChunks.length > 0) {
|
|
11114
|
+
try {
|
|
11115
|
+
options.store.addBatch(orderedSucceededChunks.map((chunk) => ({
|
|
11116
|
+
id: chunk.id,
|
|
11117
|
+
vector: completedVectorsByChunkId.get(chunk.id),
|
|
11118
|
+
metadata: chunk.metadata
|
|
11119
|
+
})));
|
|
11120
|
+
for (const chunk of orderedSucceededChunks) {
|
|
11121
|
+
options.invertedIndex.removeChunk(chunk.id);
|
|
11122
|
+
options.invertedIndex.addChunk(chunk.id, chunk.content);
|
|
11123
|
+
}
|
|
11124
|
+
options.onSucceeded?.(orderedSucceededChunks);
|
|
11125
|
+
result.indexedChunks += orderedSucceededChunks.length;
|
|
11126
|
+
this.logger.recordChunksEmbedded(orderedSucceededChunks.length);
|
|
11127
|
+
} catch (error) {
|
|
11128
|
+
const failureMessage = getErrorMessage4(error);
|
|
11129
|
+
const failureTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
11130
|
+
for (const chunk of orderedSucceededChunks) {
|
|
11131
|
+
options.store.remove(chunk.id);
|
|
11132
|
+
options.invertedIndex.removeChunk(chunk.id);
|
|
11133
|
+
result.failedChunkIds.add(chunk.id);
|
|
11134
|
+
result.failedChunks += 1;
|
|
11135
|
+
const attemptCount = (options.attemptCounts.get(chunk.id) ?? 0) + 1;
|
|
11136
|
+
options.attemptCounts.set(chunk.id, attemptCount);
|
|
11137
|
+
this.writeFailedBatchRecord(options.failedState, {
|
|
11138
|
+
chunks: [chunk],
|
|
11139
|
+
error: failureMessage,
|
|
11140
|
+
attemptCount,
|
|
11141
|
+
lastAttempt: failureTimestamp
|
|
11142
|
+
});
|
|
11143
|
+
}
|
|
11144
|
+
this.logger.recordEmbeddingError();
|
|
11145
|
+
this.logger.embedding("error", "Failed to publish embedded chunks", {
|
|
11146
|
+
batchSize: orderedSucceededChunks.length,
|
|
11147
|
+
error: failureMessage
|
|
11148
|
+
});
|
|
11149
|
+
}
|
|
11150
|
+
options.onProgress?.(result);
|
|
11151
|
+
}
|
|
11152
|
+
return result;
|
|
11153
|
+
}
|
|
10340
11154
|
async rerankCandidatesWithApi(query, candidates, options) {
|
|
10341
11155
|
const reranker = this.config.reranker;
|
|
10342
11156
|
if (!reranker || !reranker.enabled || candidates.length <= 1) {
|
|
@@ -10578,12 +11392,12 @@ var Indexer = class _Indexer {
|
|
|
10578
11392
|
}
|
|
10579
11393
|
}
|
|
10580
11394
|
captureReaderArtifactFingerprint() {
|
|
10581
|
-
const storePath =
|
|
11395
|
+
const storePath = path19.join(this.indexPath, "vectors");
|
|
10582
11396
|
return {
|
|
10583
11397
|
vectors: `${this.getReaderFileFingerprint(storePath)}|${this.getReaderFileFingerprint(`${storePath}.meta.json`)}`,
|
|
10584
|
-
keyword: this.getReaderFileFingerprint(
|
|
10585
|
-
database: this.getReaderFileFingerprint(
|
|
10586
|
-
databaseIdentity: this.getReaderFileFingerprint(
|
|
11398
|
+
keyword: this.getReaderFileFingerprint(path19.join(this.indexPath, "inverted-index.json")),
|
|
11399
|
+
database: this.getReaderFileFingerprint(path19.join(this.indexPath, "codebase.db")),
|
|
11400
|
+
databaseIdentity: this.getReaderFileFingerprint(path19.join(this.indexPath, "codebase.db"), true)
|
|
10587
11401
|
};
|
|
10588
11402
|
}
|
|
10589
11403
|
refreshReaderArtifacts() {
|
|
@@ -10608,10 +11422,10 @@ var Indexer = class _Indexer {
|
|
|
10608
11422
|
issues.set(component, this.createReadIssue(component, message));
|
|
10609
11423
|
this.readerArtifactRetryAfter.set(component, Date.now() + READER_ARTIFACT_RETRY_INTERVAL_MS);
|
|
10610
11424
|
};
|
|
10611
|
-
const storePath =
|
|
11425
|
+
const storePath = path19.join(this.indexPath, "vectors");
|
|
10612
11426
|
const vectorMetadataPath = `${storePath}.meta.json`;
|
|
10613
|
-
const invertedIndexPath =
|
|
10614
|
-
const dbPath =
|
|
11427
|
+
const invertedIndexPath = path19.join(this.indexPath, "inverted-index.json");
|
|
11428
|
+
const dbPath = path19.join(this.indexPath, "codebase.db");
|
|
10615
11429
|
if (vectorsChanged || retryDue("vectors")) {
|
|
10616
11430
|
const vectorStoreExists = (0, import_fs12.existsSync)(storePath);
|
|
10617
11431
|
const vectorMetadataExists = (0, import_fs12.existsSync)(vectorMetadataPath);
|
|
@@ -10736,10 +11550,10 @@ var Indexer = class _Indexer {
|
|
|
10736
11550
|
}
|
|
10737
11551
|
}
|
|
10738
11552
|
const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
|
|
10739
|
-
const storePath =
|
|
11553
|
+
const storePath = path19.join(this.indexPath, "vectors");
|
|
10740
11554
|
const vectorMetadataPath = `${storePath}.meta.json`;
|
|
10741
|
-
const invertedIndexPath =
|
|
10742
|
-
const dbPath =
|
|
11555
|
+
const invertedIndexPath = path19.join(this.indexPath, "inverted-index.json");
|
|
11556
|
+
const dbPath = path19.join(this.indexPath, "codebase.db");
|
|
10743
11557
|
let dbIsNew = !(0, import_fs12.existsSync)(dbPath);
|
|
10744
11558
|
const readerArtifactFingerprint = mode === "reader" ? this.captureReaderArtifactFingerprint() : null;
|
|
10745
11559
|
if (mode === "writer") {
|
|
@@ -10904,7 +11718,7 @@ var Indexer = class _Indexer {
|
|
|
10904
11718
|
if (await this.tryResetCorruptedIndex("running automatic orphan garbage collection", error)) {
|
|
10905
11719
|
return {
|
|
10906
11720
|
resetCorruptedIndex: true,
|
|
10907
|
-
warning: this.getCorruptedIndexWarning(
|
|
11721
|
+
warning: this.getCorruptedIndexWarning(path19.join(this.indexPath, "codebase.db"))
|
|
10908
11722
|
};
|
|
10909
11723
|
}
|
|
10910
11724
|
throw error;
|
|
@@ -10919,7 +11733,7 @@ var Indexer = class _Indexer {
|
|
|
10919
11733
|
return;
|
|
10920
11734
|
}
|
|
10921
11735
|
const retainedEntries = store.getAllMetadata().filter(({ key }) => !excludedSet.has(key));
|
|
10922
|
-
const storeBasePath =
|
|
11736
|
+
const storeBasePath = path19.join(this.indexPath, "vectors");
|
|
10923
11737
|
const storeIndexPath = storeBasePath;
|
|
10924
11738
|
const storeMetadataPath = `${storeBasePath}.meta.json`;
|
|
10925
11739
|
const lease = this.requireActiveLease();
|
|
@@ -11006,7 +11820,7 @@ var Indexer = class _Indexer {
|
|
|
11006
11820
|
const names = await import_fs12.promises.readdir(this.indexPath);
|
|
11007
11821
|
const runtimeStatePattern = /^(?:file-hashes|failed-batches)(?:\.[a-f0-9]{16})?\.json$/;
|
|
11008
11822
|
await Promise.all(
|
|
11009
|
-
names.filter((name) => runtimeStatePattern.test(name)).map((name) => import_fs12.promises.rm(
|
|
11823
|
+
names.filter((name) => runtimeStatePattern.test(name)).map((name) => import_fs12.promises.rm(path19.join(this.indexPath, name), { force: true }))
|
|
11010
11824
|
);
|
|
11011
11825
|
}
|
|
11012
11826
|
async resetLocalIndexArtifacts() {
|
|
@@ -11022,13 +11836,13 @@ var Indexer = class _Indexer {
|
|
|
11022
11836
|
this.readerArtifactRetryAfter.clear();
|
|
11023
11837
|
this.fileHashCache.clear();
|
|
11024
11838
|
const resetPaths = [
|
|
11025
|
-
|
|
11026
|
-
|
|
11027
|
-
|
|
11028
|
-
|
|
11029
|
-
|
|
11030
|
-
|
|
11031
|
-
|
|
11839
|
+
path19.join(this.indexPath, "codebase.db"),
|
|
11840
|
+
path19.join(this.indexPath, "codebase.db-shm"),
|
|
11841
|
+
path19.join(this.indexPath, "codebase.db-wal"),
|
|
11842
|
+
path19.join(this.indexPath, "vectors"),
|
|
11843
|
+
path19.join(this.indexPath, "vectors.usearch"),
|
|
11844
|
+
path19.join(this.indexPath, "vectors.meta.json"),
|
|
11845
|
+
path19.join(this.indexPath, "inverted-index.json")
|
|
11032
11846
|
];
|
|
11033
11847
|
await Promise.all(resetPaths.map((targetPath) => import_fs12.promises.rm(targetPath, { recursive: true, force: true })));
|
|
11034
11848
|
await this.removeProjectRuntimeStateArtifacts();
|
|
@@ -11038,7 +11852,7 @@ var Indexer = class _Indexer {
|
|
|
11038
11852
|
if (!isSqliteCorruptionError(error)) {
|
|
11039
11853
|
return false;
|
|
11040
11854
|
}
|
|
11041
|
-
const dbPath =
|
|
11855
|
+
const dbPath = path19.join(this.indexPath, "codebase.db");
|
|
11042
11856
|
const warning = this.getCorruptedIndexWarning(dbPath);
|
|
11043
11857
|
const errorMessage = getErrorMessage4(error);
|
|
11044
11858
|
if (this.config.scope === "global") {
|
|
@@ -11347,7 +12161,6 @@ var Indexer = class _Indexer {
|
|
|
11347
12161
|
skippedFiles: [],
|
|
11348
12162
|
parseFailures: []
|
|
11349
12163
|
};
|
|
11350
|
-
const failedBatchesForCurrentRun = [];
|
|
11351
12164
|
onProgress?.({
|
|
11352
12165
|
phase: "scanning",
|
|
11353
12166
|
filesProcessed: 0,
|
|
@@ -11362,14 +12175,10 @@ var Indexer = class _Indexer {
|
|
|
11362
12175
|
const reparseCachedMetalFiles = database.getMetadata(metalParserMetadataKey) !== METAL_PARSER_VERSION;
|
|
11363
12176
|
const symbolExtractorMetadataKey = this.getSymbolExtractorVersionMetadataKey();
|
|
11364
12177
|
const refreshCachedSymbols = database.getMetadata(symbolExtractorMetadataKey) !== SYMBOL_EXTRACTOR_VERSION;
|
|
11365
|
-
if (reparseCachedSwiftFiles && Array.from(this.fileHashCache.keys()).some(
|
|
11366
|
-
(filePath) => path18.extname(filePath).toLowerCase() === ".swift"
|
|
11367
|
-
)) {
|
|
12178
|
+
if (reparseCachedSwiftFiles && Array.from(this.fileHashCache.keys()).some((filePath) => path19.extname(filePath).toLowerCase() === ".swift")) {
|
|
11368
12179
|
this.logger.info("Reindexing cached Swift files for parser support");
|
|
11369
12180
|
}
|
|
11370
|
-
if (reparseCachedMetalFiles && Array.from(this.fileHashCache.keys()).some(
|
|
11371
|
-
(filePath) => path18.extname(filePath).toLowerCase() === ".metal"
|
|
11372
|
-
)) {
|
|
12181
|
+
if (reparseCachedMetalFiles && Array.from(this.fileHashCache.keys()).some((filePath) => path19.extname(filePath).toLowerCase() === ".metal")) {
|
|
11373
12182
|
this.logger.info("Reindexing cached Metal files for parser support");
|
|
11374
12183
|
}
|
|
11375
12184
|
const includePatterns = [...this.config.include, ...this.config.additionalInclude];
|
|
@@ -11391,46 +12200,44 @@ var Indexer = class _Indexer {
|
|
|
11391
12200
|
totalFiles: files.length,
|
|
11392
12201
|
skippedFiles: skipped.length
|
|
11393
12202
|
});
|
|
11394
|
-
const
|
|
12203
|
+
const changedFileDescriptors = [];
|
|
11395
12204
|
const unchangedFilePaths = /* @__PURE__ */ new Set();
|
|
11396
12205
|
const currentFileHashes = /* @__PURE__ */ new Map();
|
|
11397
12206
|
const needsCallGraphResolutionMigration = database.getMetadata(this.getCallGraphResolutionMetadataKey()) !== CALL_GRAPH_RESOLUTION_VERSION;
|
|
11398
|
-
for (const
|
|
11399
|
-
const storedPath = this.toStoredFilePath(
|
|
11400
|
-
const currentHash = hashFile(
|
|
12207
|
+
for (const file of files) {
|
|
12208
|
+
const storedPath = this.toStoredFilePath(file.path);
|
|
12209
|
+
const currentHash = hashFile(file.path);
|
|
11401
12210
|
currentFileHashes.set(storedPath, currentHash);
|
|
11402
12211
|
const cachedHashMatches = this.fileHashCache.get(storedPath) === currentHash;
|
|
11403
12212
|
const needsCallGraphRefresh = cachedHashMatches && needsCallGraphResolutionMigration && database.getChunksByFile(storedPath).some(
|
|
11404
12213
|
(chunk) => chunk.language === "php" || chunk.language === "c" || chunk.language === "cpp"
|
|
11405
12214
|
);
|
|
11406
|
-
const requiresSwiftParserUpgrade = reparseCachedSwiftFiles &&
|
|
11407
|
-
const requiresMetalParserUpgrade = reparseCachedMetalFiles &&
|
|
12215
|
+
const requiresSwiftParserUpgrade = reparseCachedSwiftFiles && path19.extname(storedPath).toLowerCase() === ".swift";
|
|
12216
|
+
const requiresMetalParserUpgrade = reparseCachedMetalFiles && path19.extname(storedPath).toLowerCase() === ".metal";
|
|
11408
12217
|
if (cachedHashMatches && !needsCallGraphRefresh && !requiresSwiftParserUpgrade && !requiresMetalParserUpgrade && !refreshCachedSymbols) {
|
|
11409
12218
|
unchangedFilePaths.add(storedPath);
|
|
11410
12219
|
this.logger.recordCacheHit();
|
|
11411
12220
|
} else {
|
|
11412
|
-
|
|
11413
|
-
|
|
12221
|
+
changedFileDescriptors.push({
|
|
12222
|
+
storedPath,
|
|
12223
|
+
materializedPath: file.path,
|
|
12224
|
+
hash: currentHash,
|
|
12225
|
+
sourceBytes: file.size
|
|
12226
|
+
});
|
|
11414
12227
|
this.logger.recordCacheMiss();
|
|
11415
12228
|
}
|
|
11416
12229
|
}
|
|
11417
12230
|
this.logger.cache("info", "File hash cache results", {
|
|
11418
12231
|
unchanged: unchangedFilePaths.size,
|
|
11419
|
-
changed:
|
|
12232
|
+
changed: changedFileDescriptors.length
|
|
11420
12233
|
});
|
|
11421
12234
|
onProgress?.({
|
|
11422
12235
|
phase: "parsing",
|
|
11423
|
-
filesProcessed:
|
|
12236
|
+
filesProcessed: unchangedFilePaths.size,
|
|
11424
12237
|
totalFiles: files.length,
|
|
11425
12238
|
chunksProcessed: 0,
|
|
11426
12239
|
totalChunks: 0
|
|
11427
12240
|
});
|
|
11428
|
-
const parseStartTime = import_perf_hooks.performance.now();
|
|
11429
|
-
const parsedFiles = parseFiles(changedFiles);
|
|
11430
|
-
const parseMs = import_perf_hooks.performance.now() - parseStartTime;
|
|
11431
|
-
this.logger.recordFilesParsed(parsedFiles.length);
|
|
11432
|
-
this.logger.recordParseDuration(parseMs);
|
|
11433
|
-
this.logger.debug("Parsed changed files", { parsedCount: parsedFiles.length, parseMs: parseMs.toFixed(2) });
|
|
11434
12241
|
const existingChunks = /* @__PURE__ */ new Map();
|
|
11435
12242
|
const existingChunksByFile = /* @__PURE__ */ new Map();
|
|
11436
12243
|
const existingMetadataById = /* @__PURE__ */ new Map();
|
|
@@ -11446,17 +12253,17 @@ var Indexer = class _Indexer {
|
|
|
11446
12253
|
}
|
|
11447
12254
|
existingChunks.set(key, metadata.hash);
|
|
11448
12255
|
existingMetadataById.set(key, metadata);
|
|
11449
|
-
const fileChunks = existingChunksByFile.get(metadata.filePath)
|
|
12256
|
+
const fileChunks = existingChunksByFile.get(metadata.filePath) ?? /* @__PURE__ */ new Set();
|
|
11450
12257
|
fileChunks.add(key);
|
|
11451
12258
|
existingChunksByFile.set(metadata.filePath, fileChunks);
|
|
11452
12259
|
}
|
|
11453
12260
|
const currentChunkIds = /* @__PURE__ */ new Set();
|
|
11454
|
-
const
|
|
11455
|
-
const
|
|
12261
|
+
const allSymbolIds = /* @__PURE__ */ new Set();
|
|
12262
|
+
const failedChunkIds = /* @__PURE__ */ new Set();
|
|
12263
|
+
const retryableChunksWithExistingData = /* @__PURE__ */ new Set();
|
|
11456
12264
|
const gitBlameEnabled = this.config.indexing.gitBlame.enabled && isGitRepo(this.materializedProjectRoot);
|
|
11457
12265
|
let backfilledBlameMetadata = false;
|
|
11458
12266
|
for (const filePath of unchangedFilePaths) {
|
|
11459
|
-
currentFilePaths.add(filePath);
|
|
11460
12267
|
const fileChunks = existingChunksByFile.get(filePath);
|
|
11461
12268
|
if (fileChunks) {
|
|
11462
12269
|
for (const chunkId of fileChunks) {
|
|
@@ -11464,635 +12271,552 @@ var Indexer = class _Indexer {
|
|
|
11464
12271
|
}
|
|
11465
12272
|
}
|
|
11466
12273
|
}
|
|
11467
|
-
const
|
|
11468
|
-
|
|
11469
|
-
|
|
11470
|
-
|
|
11471
|
-
|
|
11472
|
-
|
|
11473
|
-
|
|
11474
|
-
|
|
11475
|
-
|
|
11476
|
-
|
|
11477
|
-
|
|
12274
|
+
const shouldRetryFailedPath = (filePath) => filePath !== null && currentFileHashes.has(filePath) && unchangedFilePaths.has(filePath);
|
|
12275
|
+
const failedProcessing = this.prepareFailedBatchProcessing(scopedRoots, shouldRetryFailedPath);
|
|
12276
|
+
const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
|
|
12277
|
+
const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
|
|
12278
|
+
const queue = new PQueue({
|
|
12279
|
+
concurrency: providerRateLimits.concurrency,
|
|
12280
|
+
interval: providerRateLimits.intervalMs,
|
|
12281
|
+
intervalCap: providerRateLimits.concurrency
|
|
12282
|
+
});
|
|
12283
|
+
const rateLimitState = { backoffMs: 0 };
|
|
12284
|
+
let writeTransactionActive = false;
|
|
12285
|
+
try {
|
|
12286
|
+
database.beginWriteTransaction();
|
|
12287
|
+
writeTransactionActive = true;
|
|
12288
|
+
const blameChunkDataBatch = [];
|
|
12289
|
+
if (gitBlameEnabled) {
|
|
12290
|
+
const backfillItems = [];
|
|
12291
|
+
for (const chunkId of currentChunkIds) {
|
|
12292
|
+
const metadata = existingMetadataById.get(chunkId);
|
|
12293
|
+
if (!metadata || hasBlameMetadata(metadata)) {
|
|
12294
|
+
continue;
|
|
12295
|
+
}
|
|
12296
|
+
const chunk = database.getChunk(chunkId);
|
|
12297
|
+
if (!chunk) {
|
|
12298
|
+
continue;
|
|
12299
|
+
}
|
|
12300
|
+
const blame = await getChunkGitBlame(
|
|
12301
|
+
this.materializedProjectRoot,
|
|
12302
|
+
this.toMaterializedFilePath(chunk.filePath),
|
|
12303
|
+
chunk.startLine,
|
|
12304
|
+
chunk.endLine
|
|
12305
|
+
);
|
|
12306
|
+
const blameMetadata = metadataFromBlame(blame);
|
|
12307
|
+
if (!blameMetadata.blameSha) {
|
|
12308
|
+
continue;
|
|
12309
|
+
}
|
|
12310
|
+
blameChunkDataBatch.push({
|
|
12311
|
+
...chunk,
|
|
12312
|
+
blameSha: blameMetadata.blameSha,
|
|
12313
|
+
blameAuthor: blameMetadata.blameAuthor,
|
|
12314
|
+
blameAuthorEmail: blameMetadata.blameAuthorEmail,
|
|
12315
|
+
blameCommittedAt: blameMetadata.blameCommittedAt,
|
|
12316
|
+
blameSummary: blameMetadata.blameSummary
|
|
12317
|
+
});
|
|
12318
|
+
const embeddingBuffer = database.getEmbedding(chunk.contentHash);
|
|
12319
|
+
if (embeddingBuffer) {
|
|
12320
|
+
backfillItems.push({
|
|
12321
|
+
id: chunkId,
|
|
12322
|
+
vector: Array.from(bufferToFloat32Array(embeddingBuffer)),
|
|
12323
|
+
metadata: { ...metadata, ...blameMetadata }
|
|
12324
|
+
});
|
|
12325
|
+
}
|
|
11478
12326
|
}
|
|
11479
|
-
|
|
11480
|
-
|
|
11481
|
-
this.toMaterializedFilePath(chunk.filePath),
|
|
11482
|
-
chunk.startLine,
|
|
11483
|
-
chunk.endLine
|
|
11484
|
-
);
|
|
11485
|
-
const blameMetadata = metadataFromBlame(blame);
|
|
11486
|
-
if (!blameMetadata.blameSha) {
|
|
11487
|
-
continue;
|
|
12327
|
+
if (blameChunkDataBatch.length > 0) {
|
|
12328
|
+
database.upsertChunksBatch(blameChunkDataBatch);
|
|
11488
12329
|
}
|
|
11489
|
-
|
|
11490
|
-
|
|
11491
|
-
|
|
11492
|
-
blameAuthor: blameMetadata.blameAuthor,
|
|
11493
|
-
blameAuthorEmail: blameMetadata.blameAuthorEmail,
|
|
11494
|
-
blameCommittedAt: blameMetadata.blameCommittedAt,
|
|
11495
|
-
blameSummary: blameMetadata.blameSummary
|
|
11496
|
-
});
|
|
11497
|
-
const embeddingBuffer = database.getEmbedding(chunk.contentHash);
|
|
11498
|
-
if (!embeddingBuffer) {
|
|
11499
|
-
continue;
|
|
12330
|
+
if (backfillItems.length > 0) {
|
|
12331
|
+
store.addBatch(backfillItems);
|
|
12332
|
+
backfilledBlameMetadata = true;
|
|
11500
12333
|
}
|
|
11501
|
-
backfillItems.push({
|
|
11502
|
-
id: chunkId,
|
|
11503
|
-
vector: Array.from(bufferToFloat32Array(embeddingBuffer)),
|
|
11504
|
-
metadata: {
|
|
11505
|
-
...metadata,
|
|
11506
|
-
...blameMetadata
|
|
11507
|
-
}
|
|
11508
|
-
});
|
|
11509
12334
|
}
|
|
11510
|
-
|
|
11511
|
-
|
|
11512
|
-
|
|
11513
|
-
|
|
11514
|
-
|
|
11515
|
-
for (const parsed of parsedFiles) {
|
|
11516
|
-
currentFilePaths.add(parsed.path);
|
|
11517
|
-
if (parsed.chunks.length === 0) {
|
|
11518
|
-
stats.parseFailures.push(path18.isAbsolute(parsed.path) ? path18.relative(this.projectRoot, parsed.path) : parsed.path);
|
|
11519
|
-
}
|
|
11520
|
-
let chunksToProcess = parsed.chunks;
|
|
11521
|
-
if (this.config.indexing.fallbackToTextOnMaxChunks && chunksToProcess.length > this.config.indexing.maxChunksPerFile) {
|
|
11522
|
-
const changedFile = changedFiles.find((f) => f.path === parsed.path);
|
|
11523
|
-
if (changedFile) {
|
|
11524
|
-
const textChunks = parseFileAsText(parsed.path, changedFile.content);
|
|
11525
|
-
chunksToProcess = textChunks;
|
|
12335
|
+
for (const filePath of unchangedFilePaths) {
|
|
12336
|
+
for (const symbol of database.getSymbolsByFile(filePath)) {
|
|
12337
|
+
if (!restrictExistingChunksToBranch || previousBranchSymbolIdSet.has(symbol.id)) {
|
|
12338
|
+
allSymbolIds.add(symbol.id);
|
|
12339
|
+
}
|
|
11526
12340
|
}
|
|
11527
12341
|
}
|
|
11528
|
-
|
|
11529
|
-
|
|
11530
|
-
|
|
11531
|
-
|
|
11532
|
-
|
|
11533
|
-
|
|
11534
|
-
const
|
|
11535
|
-
|
|
11536
|
-
|
|
11537
|
-
|
|
11538
|
-
|
|
11539
|
-
|
|
11540
|
-
|
|
11541
|
-
|
|
11542
|
-
|
|
11543
|
-
|
|
11544
|
-
|
|
11545
|
-
|
|
11546
|
-
|
|
11547
|
-
|
|
11548
|
-
|
|
11549
|
-
filePath: parsed.path,
|
|
11550
|
-
startLine: chunk.startLine,
|
|
11551
|
-
endLine: chunk.endLine,
|
|
11552
|
-
nodeType: chunk.chunkType,
|
|
11553
|
-
name: chunk.name,
|
|
11554
|
-
language: chunk.language,
|
|
11555
|
-
blameSha: blameMetadata.blameSha,
|
|
11556
|
-
blameAuthor: blameMetadata.blameAuthor,
|
|
11557
|
-
blameAuthorEmail: blameMetadata.blameAuthorEmail,
|
|
11558
|
-
blameCommittedAt: blameMetadata.blameCommittedAt,
|
|
11559
|
-
blameSummary: blameMetadata.blameSummary
|
|
12342
|
+
let processedChangedFiles = 0;
|
|
12343
|
+
for (const descriptorBatch of iterateOrderedFileBatches(
|
|
12344
|
+
changedFileDescriptors,
|
|
12345
|
+
(descriptor) => descriptor.sourceBytes,
|
|
12346
|
+
this.fileBatchLimits
|
|
12347
|
+
)) {
|
|
12348
|
+
const loadedFiles = await Promise.all(descriptorBatch.map(async (descriptor) => ({
|
|
12349
|
+
path: descriptor.storedPath,
|
|
12350
|
+
content: await import_fs12.promises.readFile(descriptor.materializedPath, "utf-8"),
|
|
12351
|
+
hash: descriptor.hash
|
|
12352
|
+
})));
|
|
12353
|
+
const loadedByPath = new Map(loadedFiles.map((file) => [file.path, file]));
|
|
12354
|
+
const descriptorByPath = new Map(descriptorBatch.map((descriptor) => [descriptor.storedPath, descriptor]));
|
|
12355
|
+
const parseStartTime = import_perf_hooks.performance.now();
|
|
12356
|
+
const parsedFiles = parseFiles(loadedFiles);
|
|
12357
|
+
const parseMs = import_perf_hooks.performance.now() - parseStartTime;
|
|
12358
|
+
this.logger.recordFilesParsed(parsedFiles.length);
|
|
12359
|
+
this.logger.recordParseDuration(parseMs);
|
|
12360
|
+
this.logger.debug("Parsed changed file batch", {
|
|
12361
|
+
parsedCount: parsedFiles.length,
|
|
12362
|
+
parseMs: parseMs.toFixed(2)
|
|
11560
12363
|
});
|
|
11561
|
-
|
|
11562
|
-
|
|
12364
|
+
const chunkDataBatch = [];
|
|
12365
|
+
const pendingChunks = [];
|
|
12366
|
+
const symbolBatch = [];
|
|
12367
|
+
const edgeBatch = [];
|
|
12368
|
+
for (const parsed of parsedFiles) {
|
|
12369
|
+
const loadedFile = loadedByPath.get(parsed.path);
|
|
12370
|
+
const descriptor = descriptorByPath.get(parsed.path);
|
|
12371
|
+
if (!loadedFile || !descriptor) {
|
|
12372
|
+
throw new Error(`Parsed file was not present in its source batch: ${parsed.path}`);
|
|
12373
|
+
}
|
|
12374
|
+
if (parsed.chunks.length === 0) {
|
|
12375
|
+
stats.parseFailures.push(path19.isAbsolute(parsed.path) ? path19.relative(this.projectRoot, parsed.path) : parsed.path);
|
|
12376
|
+
}
|
|
12377
|
+
let chunksToProcess = parsed.chunks;
|
|
12378
|
+
if (this.config.indexing.fallbackToTextOnMaxChunks && chunksToProcess.length > this.config.indexing.maxChunksPerFile) {
|
|
12379
|
+
chunksToProcess = parseFileAsText(parsed.path, loadedFile.content);
|
|
12380
|
+
}
|
|
12381
|
+
chunksToProcess = selectIndexableChunks(
|
|
12382
|
+
chunksToProcess,
|
|
12383
|
+
this.config.indexing.maxChunksPerFile,
|
|
12384
|
+
this.config.indexing.semanticOnly
|
|
12385
|
+
);
|
|
12386
|
+
for (const chunk of chunksToProcess) {
|
|
12387
|
+
const id = this.getPreparedChunkId(generateChunkId(parsed.path, chunk));
|
|
12388
|
+
const contentHash = generateChunkHash(chunk);
|
|
12389
|
+
const existingContentHash = existingChunks.get(id);
|
|
12390
|
+
const existingChunk = gitBlameEnabled ? database.getChunk(id) : null;
|
|
12391
|
+
const blame = gitBlameEnabled && existingContentHash !== contentHash ? await getChunkGitBlame(
|
|
12392
|
+
this.materializedProjectRoot,
|
|
12393
|
+
descriptor.materializedPath,
|
|
12394
|
+
chunk.startLine,
|
|
12395
|
+
chunk.endLine
|
|
12396
|
+
) : blameFromChunkData(existingChunk);
|
|
12397
|
+
const blameMetadata = metadataFromBlame(blame);
|
|
12398
|
+
currentChunkIds.add(id);
|
|
12399
|
+
chunkDataBatch.push({
|
|
12400
|
+
chunkId: id,
|
|
12401
|
+
contentHash,
|
|
12402
|
+
filePath: parsed.path,
|
|
12403
|
+
startLine: chunk.startLine,
|
|
12404
|
+
endLine: chunk.endLine,
|
|
12405
|
+
nodeType: chunk.chunkType,
|
|
12406
|
+
name: chunk.name,
|
|
12407
|
+
language: chunk.language,
|
|
12408
|
+
blameSha: blameMetadata.blameSha,
|
|
12409
|
+
blameAuthor: blameMetadata.blameAuthor,
|
|
12410
|
+
blameAuthorEmail: blameMetadata.blameAuthorEmail,
|
|
12411
|
+
blameCommittedAt: blameMetadata.blameCommittedAt,
|
|
12412
|
+
blameSummary: blameMetadata.blameSummary
|
|
12413
|
+
});
|
|
12414
|
+
if (existingContentHash === contentHash) {
|
|
12415
|
+
continue;
|
|
12416
|
+
}
|
|
12417
|
+
const texts = createEmbeddingTexts(chunk, parsed.path, maxChunkTokens).map((text3) => ({
|
|
12418
|
+
text: text3,
|
|
12419
|
+
tokenCount: estimateTokens(text3)
|
|
12420
|
+
}));
|
|
12421
|
+
pendingChunks.push({
|
|
12422
|
+
id,
|
|
12423
|
+
texts,
|
|
12424
|
+
storageText: createPendingChunkStorageText(texts),
|
|
12425
|
+
content: chunk.content,
|
|
12426
|
+
contentHash,
|
|
12427
|
+
metadata: {
|
|
12428
|
+
filePath: parsed.path,
|
|
12429
|
+
startLine: chunk.startLine,
|
|
12430
|
+
endLine: chunk.endLine,
|
|
12431
|
+
chunkType: chunk.chunkType,
|
|
12432
|
+
name: chunk.name,
|
|
12433
|
+
language: chunk.language,
|
|
12434
|
+
hash: contentHash,
|
|
12435
|
+
...blameMetadata
|
|
12436
|
+
}
|
|
12437
|
+
});
|
|
12438
|
+
}
|
|
12439
|
+
const fileSymbols = [];
|
|
12440
|
+
for (const parsedSymbol of parsed.symbols) {
|
|
12441
|
+
if (!CALL_GRAPH_SYMBOL_CHUNK_TYPES.has(parsedSymbol.kind)) {
|
|
12442
|
+
continue;
|
|
12443
|
+
}
|
|
12444
|
+
const preparedNamespace = this.getPreparedBranchNamespace();
|
|
12445
|
+
const symbolId = `sym_${hashContent(
|
|
12446
|
+
(preparedNamespace ? `${preparedNamespace}:` : "") + parsed.path + ":" + parsedSymbol.name + ":" + parsedSymbol.kind + ":" + parsedSymbol.startLine + ":" + parsedSymbol.startCol + ":" + descriptor.hash
|
|
12447
|
+
).slice(0, 16)}`;
|
|
12448
|
+
const symbol = {
|
|
12449
|
+
id: symbolId,
|
|
12450
|
+
filePath: parsed.path,
|
|
12451
|
+
name: parsedSymbol.name,
|
|
12452
|
+
kind: parsedSymbol.kind,
|
|
12453
|
+
startLine: parsedSymbol.startLine,
|
|
12454
|
+
startCol: parsedSymbol.startCol,
|
|
12455
|
+
endLine: parsedSymbol.endLine,
|
|
12456
|
+
endCol: parsedSymbol.endCol,
|
|
12457
|
+
language: parsedSymbol.language
|
|
12458
|
+
};
|
|
12459
|
+
fileSymbols.push(symbol);
|
|
12460
|
+
symbolBatch.push(symbol);
|
|
12461
|
+
allSymbolIds.add(symbolId);
|
|
12462
|
+
}
|
|
12463
|
+
const fileLanguage = parsed.symbols[0]?.language ?? parsed.chunks[0]?.language;
|
|
12464
|
+
if (!fileLanguage || !CALL_GRAPH_LANGUAGES.has(fileLanguage)) {
|
|
12465
|
+
continue;
|
|
12466
|
+
}
|
|
12467
|
+
const isCaseInsensitiveLanguage = CASE_INSENSITIVE_LANGUAGES.has(fileLanguage);
|
|
12468
|
+
const normalizeSymbolKey = (name) => isCaseInsensitiveLanguage ? name.toLowerCase() : name;
|
|
12469
|
+
const symbolsByName = /* @__PURE__ */ new Map();
|
|
12470
|
+
for (const symbol of fileSymbols) {
|
|
12471
|
+
const key = normalizeSymbolKey(symbol.name);
|
|
12472
|
+
const symbols = symbolsByName.get(key) ?? [];
|
|
12473
|
+
symbols.push(symbol);
|
|
12474
|
+
symbolsByName.set(key, symbols);
|
|
12475
|
+
}
|
|
12476
|
+
for (const site of extractCalls(loadedFile.content, fileLanguage)) {
|
|
12477
|
+
const enclosingSymbol = findEnclosingSymbol(fileSymbols, site.line, site.column);
|
|
12478
|
+
if (!enclosingSymbol) {
|
|
12479
|
+
continue;
|
|
12480
|
+
}
|
|
12481
|
+
let candidates = symbolsByName.get(normalizeSymbolKey(site.calleeName));
|
|
12482
|
+
if (fileLanguage === "php" && candidates) {
|
|
12483
|
+
if (site.callType === "Constructor") {
|
|
12484
|
+
candidates = candidates.filter((candidate) => PHP_CLASS_SYMBOL_CHUNK_TYPES.has(candidate.kind));
|
|
12485
|
+
} else if (site.callType === "Call") {
|
|
12486
|
+
candidates = candidates.filter((candidate) => PHP_FUNCTION_SYMBOL_CHUNK_TYPES.has(candidate.kind));
|
|
12487
|
+
}
|
|
12488
|
+
}
|
|
12489
|
+
candidates = candidates?.filter(
|
|
12490
|
+
(symbol) => isCompatibleCFamilyCallTarget(fileLanguage, site.callType, symbol.kind)
|
|
12491
|
+
);
|
|
12492
|
+
const resolvedTarget = candidates?.length === 1 ? candidates[0] : void 0;
|
|
12493
|
+
edgeBatch.push({
|
|
12494
|
+
id: `edge_${hashContent(
|
|
12495
|
+
enclosingSymbol.id + ":" + site.calleeName + ":" + site.line + ":" + site.column
|
|
12496
|
+
).slice(0, 16)}`,
|
|
12497
|
+
fromSymbolId: enclosingSymbol.id,
|
|
12498
|
+
targetName: site.calleeName,
|
|
12499
|
+
toSymbolId: resolvedTarget?.id,
|
|
12500
|
+
callType: site.callType,
|
|
12501
|
+
confidence: site.confidence,
|
|
12502
|
+
line: site.line,
|
|
12503
|
+
col: site.column,
|
|
12504
|
+
isResolved: resolvedTarget !== void 0
|
|
12505
|
+
});
|
|
12506
|
+
}
|
|
11563
12507
|
}
|
|
11564
|
-
|
|
11565
|
-
|
|
11566
|
-
tokenCount: estimateTokens(text3)
|
|
11567
|
-
}));
|
|
11568
|
-
const metadata = {
|
|
11569
|
-
filePath: parsed.path,
|
|
11570
|
-
startLine: chunk.startLine,
|
|
11571
|
-
endLine: chunk.endLine,
|
|
11572
|
-
chunkType: chunk.chunkType,
|
|
11573
|
-
name: chunk.name,
|
|
11574
|
-
language: chunk.language,
|
|
11575
|
-
hash: contentHash,
|
|
11576
|
-
...blameMetadata
|
|
11577
|
-
};
|
|
11578
|
-
pendingChunks.push({
|
|
11579
|
-
id,
|
|
11580
|
-
texts,
|
|
11581
|
-
storageText: createPendingChunkStorageText(texts),
|
|
11582
|
-
content: chunk.content,
|
|
11583
|
-
contentHash,
|
|
11584
|
-
metadata
|
|
11585
|
-
});
|
|
11586
|
-
}
|
|
11587
|
-
}
|
|
11588
|
-
const retryableFailedChunks = this.collectRetryableFailedChunks(
|
|
11589
|
-
currentFileHashes,
|
|
11590
|
-
unchangedFilePaths,
|
|
11591
|
-
getSafeEmbeddingChunkTokenLimit(configuredProviderInfo)
|
|
11592
|
-
);
|
|
11593
|
-
const retryableFailedAttemptCounts = /* @__PURE__ */ new Map();
|
|
11594
|
-
const retryableChunksWithExistingData = /* @__PURE__ */ new Set();
|
|
11595
|
-
if (retryableFailedChunks.length > 0) {
|
|
11596
|
-
const pendingChunkIds = new Set(pendingChunks.map((chunk) => chunk.id));
|
|
11597
|
-
for (const { chunk, attemptCount } of retryableFailedChunks) {
|
|
11598
|
-
retryableFailedAttemptCounts.set(chunk.id, attemptCount);
|
|
11599
|
-
if (existingChunks.has(chunk.id)) {
|
|
11600
|
-
retryableChunksWithExistingData.add(chunk.id);
|
|
12508
|
+
if (chunkDataBatch.length > 0) {
|
|
12509
|
+
database.upsertChunksBatch(chunkDataBatch);
|
|
11601
12510
|
}
|
|
11602
|
-
if (
|
|
11603
|
-
|
|
11604
|
-
pendingChunkIds.add(chunk.id);
|
|
11605
|
-
currentChunkIds.add(chunk.id);
|
|
12511
|
+
if (symbolBatch.length > 0) {
|
|
12512
|
+
database.upsertSymbolsBatch(symbolBatch);
|
|
11606
12513
|
}
|
|
11607
|
-
|
|
11608
|
-
|
|
11609
|
-
|
|
11610
|
-
|
|
11611
|
-
|
|
11612
|
-
|
|
11613
|
-
|
|
11614
|
-
|
|
11615
|
-
|
|
11616
|
-
|
|
11617
|
-
|
|
11618
|
-
for (const parsedSymbol of parsed.symbols) {
|
|
11619
|
-
if (!CALL_GRAPH_SYMBOL_CHUNK_TYPES.has(parsedSymbol.kind)) continue;
|
|
11620
|
-
const preparedNamespace = this.getPreparedBranchNamespace();
|
|
11621
|
-
const symbolId = `sym_${hashContent(
|
|
11622
|
-
(preparedNamespace ? `${preparedNamespace}:` : "") + parsed.path + ":" + parsedSymbol.name + ":" + parsedSymbol.kind + ":" + parsedSymbol.startLine + ":" + parsedSymbol.startCol + ":" + changedFile.hash
|
|
11623
|
-
).slice(0, 16)}`;
|
|
11624
|
-
const symbol = {
|
|
11625
|
-
id: symbolId,
|
|
11626
|
-
filePath: parsed.path,
|
|
11627
|
-
name: parsedSymbol.name,
|
|
11628
|
-
kind: parsedSymbol.kind,
|
|
11629
|
-
startLine: parsedSymbol.startLine,
|
|
11630
|
-
startCol: parsedSymbol.startCol,
|
|
11631
|
-
endLine: parsedSymbol.endLine,
|
|
11632
|
-
endCol: parsedSymbol.endCol,
|
|
11633
|
-
language: parsedSymbol.language
|
|
11634
|
-
};
|
|
11635
|
-
fileSymbols.push(symbol);
|
|
11636
|
-
allSymbolIds.add(symbolId);
|
|
11637
|
-
}
|
|
11638
|
-
const fileLanguage = parsed.symbols[0]?.language ?? parsed.chunks[0]?.language;
|
|
11639
|
-
const isCaseInsensitiveLanguage = !!fileLanguage && CASE_INSENSITIVE_LANGUAGES.has(fileLanguage);
|
|
11640
|
-
const normalizeSymbolKey = (name) => isCaseInsensitiveLanguage ? name.toLowerCase() : name;
|
|
11641
|
-
const symbolsByName = /* @__PURE__ */ new Map();
|
|
11642
|
-
for (const symbol of fileSymbols) {
|
|
11643
|
-
const key = normalizeSymbolKey(symbol.name);
|
|
11644
|
-
const existing = symbolsByName.get(key) ?? [];
|
|
11645
|
-
existing.push(symbol);
|
|
11646
|
-
symbolsByName.set(key, existing);
|
|
11647
|
-
}
|
|
11648
|
-
if (fileSymbols.length > 0) {
|
|
11649
|
-
database.upsertSymbolsBatch(fileSymbols);
|
|
11650
|
-
symbolsByFile.set(parsed.path, fileSymbols);
|
|
11651
|
-
}
|
|
11652
|
-
if (!fileLanguage || !CALL_GRAPH_LANGUAGES.has(fileLanguage)) continue;
|
|
11653
|
-
const callSites = extractCalls(changedFile.content, fileLanguage);
|
|
11654
|
-
if (callSites.length === 0) continue;
|
|
11655
|
-
const edges = [];
|
|
11656
|
-
for (const site of callSites) {
|
|
11657
|
-
const enclosingSymbol = findEnclosingSymbol(
|
|
11658
|
-
fileSymbols,
|
|
11659
|
-
site.line,
|
|
11660
|
-
site.column
|
|
11661
|
-
);
|
|
11662
|
-
if (!enclosingSymbol) continue;
|
|
11663
|
-
const edgeId = `edge_${hashContent(enclosingSymbol.id + ":" + site.calleeName + ":" + site.line + ":" + site.column).slice(0, 16)}`;
|
|
11664
|
-
edges.push({
|
|
11665
|
-
id: edgeId,
|
|
11666
|
-
fromSymbolId: enclosingSymbol.id,
|
|
11667
|
-
targetName: site.calleeName,
|
|
11668
|
-
toSymbolId: void 0,
|
|
11669
|
-
callType: site.callType,
|
|
11670
|
-
confidence: site.confidence,
|
|
11671
|
-
line: site.line,
|
|
11672
|
-
col: site.column,
|
|
11673
|
-
isResolved: false
|
|
12514
|
+
if (edgeBatch.length > 0) {
|
|
12515
|
+
database.upsertCallEdgesBatch(edgeBatch);
|
|
12516
|
+
}
|
|
12517
|
+
processedChangedFiles += descriptorBatch.length;
|
|
12518
|
+
stats.totalChunks += pendingChunks.length;
|
|
12519
|
+
onProgress?.({
|
|
12520
|
+
phase: "parsing",
|
|
12521
|
+
filesProcessed: unchangedFilePaths.size + processedChangedFiles,
|
|
12522
|
+
totalFiles: files.length,
|
|
12523
|
+
chunksProcessed: stats.indexedChunks,
|
|
12524
|
+
totalChunks: stats.totalChunks
|
|
11674
12525
|
});
|
|
11675
|
-
|
|
11676
|
-
|
|
11677
|
-
|
|
11678
|
-
|
|
11679
|
-
|
|
11680
|
-
|
|
11681
|
-
|
|
11682
|
-
|
|
11683
|
-
|
|
11684
|
-
|
|
11685
|
-
|
|
11686
|
-
|
|
11687
|
-
|
|
11688
|
-
|
|
12526
|
+
if (pendingChunks.length > 0) {
|
|
12527
|
+
onProgress?.({
|
|
12528
|
+
phase: "embedding",
|
|
12529
|
+
filesProcessed: unchangedFilePaths.size + processedChangedFiles,
|
|
12530
|
+
totalFiles: files.length,
|
|
12531
|
+
chunksProcessed: stats.indexedChunks,
|
|
12532
|
+
totalChunks: stats.totalChunks
|
|
12533
|
+
});
|
|
12534
|
+
const batchResult = await this.processPendingChunkBatch(pendingChunks, {
|
|
12535
|
+
store,
|
|
12536
|
+
provider,
|
|
12537
|
+
invertedIndex,
|
|
12538
|
+
database,
|
|
12539
|
+
configuredProviderInfo,
|
|
12540
|
+
queue,
|
|
12541
|
+
providerRateLimits,
|
|
12542
|
+
rateLimitState,
|
|
12543
|
+
failedState: failedProcessing.state,
|
|
12544
|
+
attemptCounts: /* @__PURE__ */ new Map(),
|
|
12545
|
+
forceReembed: forceScopedReembed,
|
|
12546
|
+
reuseCachedEmbeddings: true,
|
|
12547
|
+
incrementRepeatedFailures: true,
|
|
12548
|
+
onProgress: (batchProgress) => onProgress?.({
|
|
12549
|
+
phase: "embedding",
|
|
12550
|
+
filesProcessed: unchangedFilePaths.size + processedChangedFiles,
|
|
12551
|
+
totalFiles: files.length,
|
|
12552
|
+
chunksProcessed: stats.indexedChunks + batchProgress.indexedChunks,
|
|
12553
|
+
totalChunks: stats.totalChunks
|
|
12554
|
+
})
|
|
12555
|
+
});
|
|
12556
|
+
stats.indexedChunks += batchResult.indexedChunks;
|
|
12557
|
+
stats.failedChunks += batchResult.failedChunks;
|
|
12558
|
+
stats.tokensUsed += batchResult.tokensUsed;
|
|
12559
|
+
for (const chunkId of batchResult.failedChunkIds) {
|
|
12560
|
+
failedChunkIds.add(chunkId);
|
|
12561
|
+
if (forceScopedReembed) {
|
|
12562
|
+
failedForcedChunkIds.add(chunkId);
|
|
11689
12563
|
}
|
|
11690
12564
|
}
|
|
11691
|
-
|
|
11692
|
-
|
|
11693
|
-
|
|
11694
|
-
|
|
11695
|
-
|
|
12565
|
+
}
|
|
12566
|
+
}
|
|
12567
|
+
const retryableFailedChunks = this.iterateLatestFailedChunks(
|
|
12568
|
+
failedProcessing.latestById,
|
|
12569
|
+
scopedRoots,
|
|
12570
|
+
shouldRetryFailedPath,
|
|
12571
|
+
maxChunkTokens
|
|
12572
|
+
);
|
|
12573
|
+
for (const retryBatch of iterateOrderedFileBatches(
|
|
12574
|
+
retryableFailedChunks,
|
|
12575
|
+
({ chunk }) => Buffer.byteLength(chunk.content, "utf-8"),
|
|
12576
|
+
this.fileBatchLimits
|
|
12577
|
+
)) {
|
|
12578
|
+
const pendingChunks = retryBatch.map(({ chunk }) => chunk);
|
|
12579
|
+
const attemptCounts = new Map(retryBatch.map(({ chunk, attemptCount }) => [chunk.id, attemptCount]));
|
|
12580
|
+
for (const chunk of pendingChunks) {
|
|
12581
|
+
currentChunkIds.add(chunk.id);
|
|
12582
|
+
if (existingChunks.has(chunk.id)) {
|
|
12583
|
+
retryableChunksWithExistingData.add(chunk.id);
|
|
12584
|
+
}
|
|
12585
|
+
}
|
|
12586
|
+
stats.totalChunks += pendingChunks.length;
|
|
12587
|
+
onProgress?.({
|
|
12588
|
+
phase: "embedding",
|
|
12589
|
+
filesProcessed: files.length,
|
|
12590
|
+
totalFiles: files.length,
|
|
12591
|
+
chunksProcessed: stats.indexedChunks,
|
|
12592
|
+
totalChunks: stats.totalChunks
|
|
12593
|
+
});
|
|
12594
|
+
const batchResult = await this.processPendingChunkBatch(pendingChunks, {
|
|
12595
|
+
store,
|
|
12596
|
+
provider,
|
|
12597
|
+
invertedIndex,
|
|
12598
|
+
database,
|
|
12599
|
+
configuredProviderInfo,
|
|
12600
|
+
queue,
|
|
12601
|
+
providerRateLimits,
|
|
12602
|
+
rateLimitState,
|
|
12603
|
+
failedState: failedProcessing.state,
|
|
12604
|
+
attemptCounts,
|
|
12605
|
+
forceReembed: forceScopedReembed,
|
|
12606
|
+
reuseCachedEmbeddings: true,
|
|
12607
|
+
incrementRepeatedFailures: true,
|
|
12608
|
+
onProgress: (batchProgress) => onProgress?.({
|
|
12609
|
+
phase: "embedding",
|
|
12610
|
+
filesProcessed: files.length,
|
|
12611
|
+
totalFiles: files.length,
|
|
12612
|
+
chunksProcessed: stats.indexedChunks + batchProgress.indexedChunks,
|
|
12613
|
+
totalChunks: stats.totalChunks
|
|
12614
|
+
})
|
|
12615
|
+
});
|
|
12616
|
+
stats.indexedChunks += batchResult.indexedChunks;
|
|
12617
|
+
stats.failedChunks += batchResult.failedChunks;
|
|
12618
|
+
stats.tokensUsed += batchResult.tokensUsed;
|
|
12619
|
+
for (const chunkId of batchResult.failedChunkIds) {
|
|
12620
|
+
failedChunkIds.add(chunkId);
|
|
12621
|
+
if (forceScopedReembed) {
|
|
12622
|
+
failedForcedChunkIds.add(chunkId);
|
|
11696
12623
|
}
|
|
11697
12624
|
}
|
|
11698
12625
|
}
|
|
11699
|
-
|
|
11700
|
-
|
|
11701
|
-
|
|
11702
|
-
|
|
11703
|
-
if (!restrictExistingChunksToBranch || previousBranchSymbolIdSet.has(sym.id)) {
|
|
11704
|
-
allSymbolIds.add(sym.id);
|
|
12626
|
+
const removedChunkIds = [];
|
|
12627
|
+
for (const [chunkId] of existingChunks) {
|
|
12628
|
+
if (!currentChunkIds.has(chunkId)) {
|
|
12629
|
+
removedChunkIds.push(chunkId);
|
|
11705
12630
|
}
|
|
11706
12631
|
}
|
|
11707
|
-
|
|
11708
|
-
|
|
11709
|
-
|
|
11710
|
-
|
|
11711
|
-
|
|
12632
|
+
const removedCount = removedChunkIds.length;
|
|
12633
|
+
stats.existingChunks = currentChunkIds.size - stats.totalChunks;
|
|
12634
|
+
stats.removedChunks = removedCount;
|
|
12635
|
+
this.logger.recordChunksProcessed(currentChunkIds.size);
|
|
12636
|
+
this.logger.recordChunksRemoved(removedCount);
|
|
12637
|
+
this.logger.info("Chunk analysis complete", {
|
|
12638
|
+
pending: stats.totalChunks,
|
|
12639
|
+
existing: stats.existingChunks,
|
|
12640
|
+
removed: removedCount
|
|
12641
|
+
});
|
|
12642
|
+
if (stats.totalChunks === 0 && removedCount === 0) {
|
|
12643
|
+
const removedStoredChunks = this.replaceBranchCatalog(
|
|
12644
|
+
store,
|
|
12645
|
+
invertedIndex,
|
|
12646
|
+
database,
|
|
12647
|
+
branchCatalogKey,
|
|
12648
|
+
previousBranchChunkIds,
|
|
12649
|
+
Array.from(currentChunkIds),
|
|
12650
|
+
previousBranchSymbolIds,
|
|
12651
|
+
Array.from(allSymbolIds)
|
|
12652
|
+
);
|
|
12653
|
+
const vectorPath = path19.join(this.indexPath, "vectors");
|
|
12654
|
+
const shouldFingerprintLegacyPair = !store.hasFingerprint() && (0, import_fs12.existsSync)(vectorPath) && (0, import_fs12.existsSync)(`${vectorPath}.meta.json`);
|
|
12655
|
+
if (backfilledBlameMetadata || shouldFingerprintLegacyPair || removedStoredChunks) {
|
|
12656
|
+
store.save();
|
|
12657
|
+
}
|
|
12658
|
+
if (removedStoredChunks) {
|
|
12659
|
+
this.saveInvertedIndex(invertedIndex);
|
|
12660
|
+
}
|
|
12661
|
+
if (scopedRoots) {
|
|
12662
|
+
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
12663
|
+
} else {
|
|
12664
|
+
this.fileHashCache = currentFileHashes;
|
|
12665
|
+
this.saveFileHashCache();
|
|
12666
|
+
}
|
|
12667
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state);
|
|
12668
|
+
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
12669
|
+
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
12670
|
+
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
12671
|
+
this.saveBranchCommit(database, indexedCommit);
|
|
12672
|
+
this.saveIndexMetadata(configuredProviderInfo);
|
|
12673
|
+
this.indexCompatibility = { compatible: true };
|
|
12674
|
+
database.commitWriteTransaction();
|
|
12675
|
+
writeTransactionActive = false;
|
|
12676
|
+
stats.durationMs = Date.now() - startTime;
|
|
12677
|
+
onProgress?.({
|
|
12678
|
+
phase: "complete",
|
|
12679
|
+
filesProcessed: files.length,
|
|
12680
|
+
totalFiles: files.length,
|
|
12681
|
+
chunksProcessed: 0,
|
|
12682
|
+
totalChunks: 0
|
|
12683
|
+
});
|
|
12684
|
+
return stats;
|
|
11712
12685
|
}
|
|
11713
|
-
|
|
11714
|
-
|
|
11715
|
-
|
|
11716
|
-
|
|
11717
|
-
|
|
11718
|
-
|
|
11719
|
-
|
|
11720
|
-
|
|
11721
|
-
|
|
11722
|
-
|
|
11723
|
-
|
|
11724
|
-
});
|
|
11725
|
-
if (pendingChunks.length === 0 && removedCount === 0) {
|
|
11726
|
-
const removedStoredChunks = this.replaceBranchCatalog(
|
|
11727
|
-
store,
|
|
11728
|
-
invertedIndex,
|
|
11729
|
-
database,
|
|
11730
|
-
branchCatalogKey,
|
|
11731
|
-
previousBranchChunkIds,
|
|
11732
|
-
Array.from(currentChunkIds),
|
|
11733
|
-
previousBranchSymbolIds,
|
|
11734
|
-
Array.from(allSymbolIds)
|
|
11735
|
-
);
|
|
11736
|
-
const vectorPath = path18.join(this.indexPath, "vectors");
|
|
11737
|
-
const shouldFingerprintLegacyPair = !store.hasFingerprint() && (0, import_fs12.existsSync)(vectorPath) && (0, import_fs12.existsSync)(`${vectorPath}.meta.json`);
|
|
11738
|
-
if (backfilledBlameMetadata || shouldFingerprintLegacyPair || removedStoredChunks) {
|
|
12686
|
+
if (stats.totalChunks === 0) {
|
|
12687
|
+
this.replaceBranchCatalog(
|
|
12688
|
+
store,
|
|
12689
|
+
invertedIndex,
|
|
12690
|
+
database,
|
|
12691
|
+
branchCatalogKey,
|
|
12692
|
+
previousBranchChunkIds,
|
|
12693
|
+
Array.from(currentChunkIds),
|
|
12694
|
+
previousBranchSymbolIds,
|
|
12695
|
+
Array.from(allSymbolIds)
|
|
12696
|
+
);
|
|
11739
12697
|
store.save();
|
|
11740
|
-
}
|
|
11741
|
-
if (removedStoredChunks) {
|
|
11742
12698
|
this.saveInvertedIndex(invertedIndex);
|
|
12699
|
+
if (scopedRoots) {
|
|
12700
|
+
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
12701
|
+
} else {
|
|
12702
|
+
this.fileHashCache = currentFileHashes;
|
|
12703
|
+
this.saveFileHashCache();
|
|
12704
|
+
}
|
|
12705
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state);
|
|
12706
|
+
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
12707
|
+
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
12708
|
+
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
12709
|
+
this.saveBranchCommit(database, indexedCommit);
|
|
12710
|
+
this.saveIndexMetadata(configuredProviderInfo);
|
|
12711
|
+
this.indexCompatibility = { compatible: true };
|
|
12712
|
+
database.commitWriteTransaction();
|
|
12713
|
+
writeTransactionActive = false;
|
|
12714
|
+
stats.durationMs = Date.now() - startTime;
|
|
12715
|
+
onProgress?.({
|
|
12716
|
+
phase: "complete",
|
|
12717
|
+
filesProcessed: files.length,
|
|
12718
|
+
totalFiles: files.length,
|
|
12719
|
+
chunksProcessed: 0,
|
|
12720
|
+
totalChunks: 0
|
|
12721
|
+
});
|
|
12722
|
+
return stats;
|
|
11743
12723
|
}
|
|
11744
|
-
if (scopedRoots) {
|
|
11745
|
-
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
11746
|
-
this.clearScopedFailedBatches(scopedRoots);
|
|
11747
|
-
} else {
|
|
11748
|
-
this.fileHashCache = currentFileHashes;
|
|
11749
|
-
this.saveFileHashCache();
|
|
11750
|
-
this.saveFailedBatches([]);
|
|
11751
|
-
}
|
|
11752
|
-
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
11753
|
-
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
11754
|
-
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
11755
|
-
this.saveBranchCommit(database, indexedCommit);
|
|
11756
|
-
this.saveIndexMetadata(configuredProviderInfo);
|
|
11757
|
-
this.indexCompatibility = { compatible: true };
|
|
11758
|
-
stats.durationMs = Date.now() - startTime;
|
|
11759
12724
|
onProgress?.({
|
|
11760
|
-
phase: "
|
|
12725
|
+
phase: "storing",
|
|
11761
12726
|
filesProcessed: files.length,
|
|
11762
12727
|
totalFiles: files.length,
|
|
11763
|
-
chunksProcessed:
|
|
11764
|
-
totalChunks:
|
|
12728
|
+
chunksProcessed: stats.indexedChunks,
|
|
12729
|
+
totalChunks: stats.totalChunks
|
|
12730
|
+
});
|
|
12731
|
+
const branchChunkIds = Array.from(currentChunkIds).filter((chunkId) => {
|
|
12732
|
+
const isNewlyFailed = failedChunkIds.has(chunkId) && !retryableChunksWithExistingData.has(chunkId);
|
|
12733
|
+
const isForcedFailed = forceScopedReembed && failedForcedChunkIds.has(chunkId);
|
|
12734
|
+
return !isNewlyFailed && !isForcedFailed;
|
|
11765
12735
|
});
|
|
11766
|
-
return stats;
|
|
11767
|
-
}
|
|
11768
|
-
if (pendingChunks.length === 0) {
|
|
11769
12736
|
this.replaceBranchCatalog(
|
|
11770
12737
|
store,
|
|
11771
12738
|
invertedIndex,
|
|
11772
12739
|
database,
|
|
11773
12740
|
branchCatalogKey,
|
|
11774
12741
|
previousBranchChunkIds,
|
|
11775
|
-
|
|
12742
|
+
branchChunkIds,
|
|
11776
12743
|
previousBranchSymbolIds,
|
|
11777
12744
|
Array.from(allSymbolIds)
|
|
11778
12745
|
);
|
|
11779
12746
|
store.save();
|
|
11780
12747
|
this.saveInvertedIndex(invertedIndex);
|
|
11781
|
-
if (scopedRoots) {
|
|
11782
|
-
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
11783
|
-
|
|
11784
|
-
|
|
11785
|
-
this.
|
|
11786
|
-
|
|
11787
|
-
|
|
11788
|
-
|
|
11789
|
-
|
|
11790
|
-
|
|
11791
|
-
|
|
11792
|
-
|
|
11793
|
-
|
|
11794
|
-
|
|
11795
|
-
|
|
11796
|
-
|
|
11797
|
-
|
|
11798
|
-
|
|
11799
|
-
|
|
11800
|
-
|
|
11801
|
-
|
|
11802
|
-
|
|
11803
|
-
|
|
11804
|
-
|
|
11805
|
-
onProgress?.({
|
|
11806
|
-
phase: "embedding",
|
|
11807
|
-
filesProcessed: files.length,
|
|
11808
|
-
totalFiles: files.length,
|
|
11809
|
-
chunksProcessed: 0,
|
|
11810
|
-
totalChunks: pendingChunks.length
|
|
11811
|
-
});
|
|
11812
|
-
const allContentHashes = pendingChunks.map((c) => c.contentHash);
|
|
11813
|
-
const missingHashes = new Set(database.getMissingEmbeddings(allContentHashes));
|
|
11814
|
-
const forcedReembedChunkIds = forceScopedReembed ? new Set(pendingChunks.map((chunk) => chunk.id)) : /* @__PURE__ */ new Set();
|
|
11815
|
-
const chunksNeedingEmbedding = pendingChunks.filter((c) => forcedReembedChunkIds.has(c.id) || missingHashes.has(c.contentHash));
|
|
11816
|
-
const chunksWithExistingEmbedding = pendingChunks.filter((c) => !forcedReembedChunkIds.has(c.id) && !missingHashes.has(c.contentHash));
|
|
11817
|
-
this.logger.cache("info", "Embedding cache lookup", {
|
|
11818
|
-
needsEmbedding: chunksNeedingEmbedding.length,
|
|
11819
|
-
fromCache: chunksWithExistingEmbedding.length
|
|
11820
|
-
});
|
|
11821
|
-
this.logger.recordChunksFromCache(chunksWithExistingEmbedding.length);
|
|
11822
|
-
for (const chunk of chunksWithExistingEmbedding) {
|
|
11823
|
-
const embeddingBuffer = database.getEmbedding(chunk.contentHash);
|
|
11824
|
-
if (embeddingBuffer) {
|
|
11825
|
-
const vector = bufferToFloat32Array(embeddingBuffer);
|
|
11826
|
-
store.add(chunk.id, Array.from(vector), chunk.metadata);
|
|
11827
|
-
invertedIndex.removeChunk(chunk.id);
|
|
11828
|
-
invertedIndex.addChunk(chunk.id, chunk.content);
|
|
11829
|
-
stats.indexedChunks++;
|
|
11830
|
-
}
|
|
11831
|
-
}
|
|
11832
|
-
const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
|
|
11833
|
-
const queue = new PQueue({
|
|
11834
|
-
concurrency: providerRateLimits.concurrency,
|
|
11835
|
-
interval: providerRateLimits.intervalMs,
|
|
11836
|
-
intervalCap: providerRateLimits.concurrency
|
|
11837
|
-
});
|
|
11838
|
-
const pendingChunksById = new Map(chunksNeedingEmbedding.map((chunk) => [chunk.id, chunk]));
|
|
11839
|
-
const embeddingPartsByChunk = /* @__PURE__ */ new Map();
|
|
11840
|
-
const completedChunkIds = /* @__PURE__ */ new Set();
|
|
11841
|
-
const failedChunkIds = /* @__PURE__ */ new Set();
|
|
11842
|
-
const requestBatches = createPendingEmbeddingRequestBatches(
|
|
11843
|
-
chunksNeedingEmbedding,
|
|
11844
|
-
getDynamicBatchOptions(configuredProviderInfo)
|
|
11845
|
-
);
|
|
11846
|
-
let rateLimitBackoffMs = 0;
|
|
11847
|
-
for (const requestBatch of requestBatches) {
|
|
11848
|
-
queue.add(async () => {
|
|
11849
|
-
if (rateLimitBackoffMs > 0) {
|
|
11850
|
-
await new Promise((resolve13) => setTimeout(resolve13, rateLimitBackoffMs));
|
|
11851
|
-
}
|
|
11852
|
-
try {
|
|
11853
|
-
const result = await pRetry(
|
|
11854
|
-
async () => {
|
|
11855
|
-
const texts = requestBatch.map((request) => request.text);
|
|
11856
|
-
return provider.embedBatch(texts);
|
|
11857
|
-
},
|
|
11858
|
-
{
|
|
11859
|
-
retries: this.config.indexing.retries,
|
|
11860
|
-
minTimeout: Math.max(this.config.indexing.retryDelayMs, providerRateLimits.minRetryMs),
|
|
11861
|
-
maxTimeout: providerRateLimits.maxRetryMs,
|
|
11862
|
-
factor: 2,
|
|
11863
|
-
shouldRetry: (error) => !(error.error instanceof CustomProviderNonRetryableError),
|
|
11864
|
-
onFailedAttempt: (error) => {
|
|
11865
|
-
const message = getErrorMessage4(error);
|
|
11866
|
-
if (isRateLimitError(error)) {
|
|
11867
|
-
rateLimitBackoffMs = Math.min(providerRateLimits.maxRetryMs, (rateLimitBackoffMs || providerRateLimits.minRetryMs) * 2);
|
|
11868
|
-
this.logger.embedding("warn", `Rate limited, backing off`, {
|
|
11869
|
-
attempt: error.attemptNumber,
|
|
11870
|
-
retriesLeft: error.retriesLeft,
|
|
11871
|
-
backoffMs: rateLimitBackoffMs
|
|
11872
|
-
});
|
|
11873
|
-
} else {
|
|
11874
|
-
this.logger.embedding("error", `Embedding batch failed`, {
|
|
11875
|
-
attempt: error.attemptNumber,
|
|
11876
|
-
error: message
|
|
11877
|
-
});
|
|
11878
|
-
}
|
|
11879
|
-
}
|
|
11880
|
-
}
|
|
11881
|
-
);
|
|
11882
|
-
if (rateLimitBackoffMs > 0) {
|
|
11883
|
-
rateLimitBackoffMs = Math.max(0, rateLimitBackoffMs - 2e3);
|
|
11884
|
-
}
|
|
11885
|
-
const touchedChunkIds = /* @__PURE__ */ new Set();
|
|
11886
|
-
requestBatch.forEach((request, idx) => {
|
|
11887
|
-
if (failedChunkIds.has(request.chunk.id) || completedChunkIds.has(request.chunk.id)) {
|
|
11888
|
-
return;
|
|
11889
|
-
}
|
|
11890
|
-
const vector = result.embeddings[idx];
|
|
11891
|
-
if (!vector) {
|
|
11892
|
-
throw new Error(`Embedding API returned too few vectors for chunk ${request.chunk.id}`);
|
|
11893
|
-
}
|
|
11894
|
-
const parts = embeddingPartsByChunk.get(request.chunk.id) ?? [];
|
|
11895
|
-
parts[request.partIndex] = {
|
|
11896
|
-
vector,
|
|
11897
|
-
tokenCount: request.tokenCount
|
|
11898
|
-
};
|
|
11899
|
-
embeddingPartsByChunk.set(request.chunk.id, parts);
|
|
11900
|
-
touchedChunkIds.add(request.chunk.id);
|
|
11901
|
-
});
|
|
11902
|
-
const pooledResults = [];
|
|
11903
|
-
for (const chunkId of touchedChunkIds) {
|
|
11904
|
-
if (failedChunkIds.has(chunkId) || completedChunkIds.has(chunkId)) {
|
|
11905
|
-
continue;
|
|
11906
|
-
}
|
|
11907
|
-
const chunk = pendingChunksById.get(chunkId);
|
|
11908
|
-
if (!chunk) {
|
|
11909
|
-
continue;
|
|
11910
|
-
}
|
|
11911
|
-
const parts = embeddingPartsByChunk.get(chunk.id) ?? [];
|
|
11912
|
-
if (!hasAllEmbeddingParts(parts, chunk.texts.length)) {
|
|
11913
|
-
continue;
|
|
11914
|
-
}
|
|
11915
|
-
const orderedParts = parts;
|
|
11916
|
-
pooledResults.push({
|
|
11917
|
-
chunk,
|
|
11918
|
-
vector: poolEmbeddingVectors(
|
|
11919
|
-
orderedParts.map((part) => part.vector),
|
|
11920
|
-
orderedParts.map((part) => part.tokenCount)
|
|
11921
|
-
)
|
|
11922
|
-
});
|
|
11923
|
-
}
|
|
11924
|
-
if (pooledResults.length > 0) {
|
|
11925
|
-
const items = pooledResults.map(({ chunk, vector }) => ({
|
|
11926
|
-
id: chunk.id,
|
|
11927
|
-
vector,
|
|
11928
|
-
metadata: chunk.metadata
|
|
11929
|
-
}));
|
|
11930
|
-
store.addBatch(items);
|
|
11931
|
-
const embeddingBatchItems = pooledResults.map(({ chunk, vector }) => ({
|
|
11932
|
-
contentHash: chunk.contentHash,
|
|
11933
|
-
embedding: float32ArrayToBuffer(vector),
|
|
11934
|
-
chunkText: chunk.storageText,
|
|
11935
|
-
model: configuredProviderInfo.modelInfo.model
|
|
11936
|
-
}));
|
|
11937
|
-
try {
|
|
11938
|
-
database.upsertEmbeddingsBatch(embeddingBatchItems);
|
|
11939
|
-
} catch (dbError) {
|
|
11940
|
-
this.rebuildVectorStoreExcludingChunkIds(
|
|
11941
|
-
store,
|
|
11942
|
-
database,
|
|
11943
|
-
pooledResults.map(({ chunk }) => chunk.id)
|
|
11944
|
-
);
|
|
11945
|
-
throw dbError;
|
|
11946
|
-
}
|
|
11947
|
-
for (const { chunk } of pooledResults) {
|
|
11948
|
-
invertedIndex.removeChunk(chunk.id);
|
|
11949
|
-
invertedIndex.addChunk(chunk.id, chunk.content);
|
|
11950
|
-
completedChunkIds.add(chunk.id);
|
|
11951
|
-
embeddingPartsByChunk.delete(chunk.id);
|
|
11952
|
-
}
|
|
11953
|
-
stats.indexedChunks += pooledResults.length;
|
|
11954
|
-
this.logger.recordChunksEmbedded(pooledResults.length);
|
|
11955
|
-
}
|
|
11956
|
-
stats.tokensUsed += result.totalTokensUsed;
|
|
11957
|
-
this.logger.recordEmbeddingApiCall(result.totalTokensUsed);
|
|
11958
|
-
this.logger.embedding("debug", `Embedded batch`, {
|
|
11959
|
-
batchSize: pooledResults.length,
|
|
11960
|
-
requestCount: requestBatch.length,
|
|
11961
|
-
tokens: result.totalTokensUsed
|
|
11962
|
-
});
|
|
11963
|
-
onProgress?.({
|
|
11964
|
-
phase: "embedding",
|
|
11965
|
-
filesProcessed: files.length,
|
|
11966
|
-
totalFiles: files.length,
|
|
11967
|
-
chunksProcessed: stats.indexedChunks,
|
|
11968
|
-
totalChunks: pendingChunks.length
|
|
11969
|
-
});
|
|
11970
|
-
} catch (error) {
|
|
11971
|
-
const failedChunks = getUniquePendingChunksFromRequests(requestBatch).filter((chunk) => !completedChunkIds.has(chunk.id));
|
|
11972
|
-
const failureMessage = getErrorMessage4(error);
|
|
11973
|
-
const failureTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
11974
|
-
for (const chunk of failedChunks) {
|
|
11975
|
-
if (!failedChunkIds.has(chunk.id)) {
|
|
11976
|
-
failedChunkIds.add(chunk.id);
|
|
11977
|
-
stats.failedChunks += 1;
|
|
11978
|
-
}
|
|
11979
|
-
if (forceScopedReembed) {
|
|
11980
|
-
failedForcedChunkIds.add(chunk.id);
|
|
11981
|
-
}
|
|
11982
|
-
embeddingPartsByChunk.delete(chunk.id);
|
|
11983
|
-
const existingFailedBatchIndex = failedBatchesForCurrentRun.findIndex(
|
|
11984
|
-
(failedBatch2) => failedBatch2.chunks[0]?.id === chunk.id
|
|
11985
|
-
);
|
|
11986
|
-
const existingFailedBatch = existingFailedBatchIndex === -1 ? void 0 : failedBatchesForCurrentRun[existingFailedBatchIndex];
|
|
11987
|
-
const failedBatch = {
|
|
11988
|
-
chunks: [chunk],
|
|
11989
|
-
error: failureMessage,
|
|
11990
|
-
attemptCount: (existingFailedBatch?.attemptCount ?? retryableFailedAttemptCounts.get(chunk.id) ?? 0) + 1,
|
|
11991
|
-
lastAttempt: failureTimestamp
|
|
11992
|
-
};
|
|
11993
|
-
if (existingFailedBatchIndex === -1) {
|
|
11994
|
-
failedBatchesForCurrentRun.push(failedBatch);
|
|
11995
|
-
} else {
|
|
11996
|
-
failedBatchesForCurrentRun[existingFailedBatchIndex] = failedBatch;
|
|
11997
|
-
}
|
|
11998
|
-
}
|
|
11999
|
-
this.logger.recordEmbeddingError();
|
|
12000
|
-
this.logger.embedding("error", `Failed to embed batch after retries`, {
|
|
12001
|
-
batchSize: failedChunks.length,
|
|
12002
|
-
requestCount: requestBatch.length,
|
|
12003
|
-
error: failureMessage
|
|
12748
|
+
if (scopedRoots) {
|
|
12749
|
+
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
12750
|
+
} else {
|
|
12751
|
+
this.fileHashCache = currentFileHashes;
|
|
12752
|
+
this.saveFileHashCache();
|
|
12753
|
+
}
|
|
12754
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state);
|
|
12755
|
+
database.commitWriteTransaction();
|
|
12756
|
+
writeTransactionActive = false;
|
|
12757
|
+
if (this.config.indexing.autoGc && stats.removedChunks > 0) {
|
|
12758
|
+
const gcReset = await this.maybeRunOrphanGc();
|
|
12759
|
+
if (gcReset) {
|
|
12760
|
+
stats.durationMs = Date.now() - startTime;
|
|
12761
|
+
stats.warning = gcReset.warning;
|
|
12762
|
+
stats.resetCorruptedIndex = true;
|
|
12763
|
+
this.logger.recordIndexingEnd();
|
|
12764
|
+
this.logger.warn("Indexing ended after resetting corrupted local index during automatic GC", {
|
|
12765
|
+
files: stats.totalFiles,
|
|
12766
|
+
indexed: stats.indexedChunks,
|
|
12767
|
+
existing: stats.existingChunks,
|
|
12768
|
+
removed: stats.removedChunks,
|
|
12769
|
+
failed: stats.failedChunks,
|
|
12770
|
+
tokens: stats.tokensUsed,
|
|
12771
|
+
durationMs: stats.durationMs
|
|
12004
12772
|
});
|
|
12773
|
+
return stats;
|
|
12005
12774
|
}
|
|
12775
|
+
}
|
|
12776
|
+
stats.durationMs = Date.now() - startTime;
|
|
12777
|
+
if (forceScopedReembed && failedForcedChunkIds.size === 0) {
|
|
12778
|
+
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
12779
|
+
}
|
|
12780
|
+
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
12781
|
+
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
12782
|
+
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
12783
|
+
this.saveBranchCommit(database, indexedCommit);
|
|
12784
|
+
this.saveIndexMetadata(configuredProviderInfo);
|
|
12785
|
+
this.indexCompatibility = { compatible: true };
|
|
12786
|
+
this.logger.recordIndexingEnd();
|
|
12787
|
+
this.logger.info("Indexing complete", {
|
|
12788
|
+
files: stats.totalFiles,
|
|
12789
|
+
indexed: stats.indexedChunks,
|
|
12790
|
+
existing: stats.existingChunks,
|
|
12791
|
+
removed: stats.removedChunks,
|
|
12792
|
+
failed: stats.failedChunks,
|
|
12793
|
+
tokens: stats.tokensUsed,
|
|
12794
|
+
durationMs: stats.durationMs
|
|
12006
12795
|
});
|
|
12007
|
-
|
|
12008
|
-
|
|
12009
|
-
if (scopedRoots) {
|
|
12010
|
-
this.saveScopedFailedBatches(coalesceFailedBatches(failedBatchesForCurrentRun), scopedRoots);
|
|
12011
|
-
} else {
|
|
12012
|
-
this.saveFailedBatches(coalesceFailedBatches(failedBatchesForCurrentRun));
|
|
12013
|
-
}
|
|
12014
|
-
onProgress?.({
|
|
12015
|
-
phase: "storing",
|
|
12016
|
-
filesProcessed: files.length,
|
|
12017
|
-
totalFiles: files.length,
|
|
12018
|
-
chunksProcessed: stats.indexedChunks,
|
|
12019
|
-
totalChunks: pendingChunks.length
|
|
12020
|
-
});
|
|
12021
|
-
const branchChunkIds = Array.from(currentChunkIds).filter(
|
|
12022
|
-
(chunkId) => {
|
|
12023
|
-
const isNewlyFailed = failedChunkIds.has(chunkId) && !retryableChunksWithExistingData.has(chunkId);
|
|
12024
|
-
const isForcedFailed = forceScopedReembed && failedForcedChunkIds.has(chunkId);
|
|
12025
|
-
return !isNewlyFailed && !isForcedFailed;
|
|
12796
|
+
if (stats.failedChunks > 0) {
|
|
12797
|
+
stats.failedBatchesPath = this.failedBatchesPath;
|
|
12026
12798
|
}
|
|
12027
|
-
|
|
12028
|
-
|
|
12029
|
-
|
|
12030
|
-
|
|
12031
|
-
|
|
12032
|
-
|
|
12033
|
-
|
|
12034
|
-
|
|
12035
|
-
|
|
12036
|
-
|
|
12037
|
-
|
|
12038
|
-
|
|
12039
|
-
|
|
12040
|
-
|
|
12041
|
-
|
|
12042
|
-
|
|
12043
|
-
|
|
12044
|
-
|
|
12045
|
-
}
|
|
12046
|
-
if (this.config.indexing.autoGc && stats.removedChunks > 0) {
|
|
12047
|
-
const gcReset = await this.maybeRunOrphanGc();
|
|
12048
|
-
if (gcReset) {
|
|
12049
|
-
stats.durationMs = Date.now() - startTime;
|
|
12050
|
-
stats.warning = gcReset.warning;
|
|
12051
|
-
stats.resetCorruptedIndex = true;
|
|
12052
|
-
this.logger.recordIndexingEnd();
|
|
12053
|
-
this.logger.warn("Indexing ended after resetting corrupted local index during automatic GC", {
|
|
12054
|
-
files: stats.totalFiles,
|
|
12055
|
-
indexed: stats.indexedChunks,
|
|
12056
|
-
existing: stats.existingChunks,
|
|
12057
|
-
removed: stats.removedChunks,
|
|
12058
|
-
failed: stats.failedChunks,
|
|
12059
|
-
tokens: stats.tokensUsed,
|
|
12060
|
-
durationMs: stats.durationMs
|
|
12061
|
-
});
|
|
12062
|
-
return stats;
|
|
12799
|
+
onProgress?.({
|
|
12800
|
+
phase: "complete",
|
|
12801
|
+
filesProcessed: files.length,
|
|
12802
|
+
totalFiles: files.length,
|
|
12803
|
+
chunksProcessed: stats.indexedChunks,
|
|
12804
|
+
totalChunks: stats.totalChunks
|
|
12805
|
+
});
|
|
12806
|
+
return stats;
|
|
12807
|
+
} catch (error) {
|
|
12808
|
+
failedProcessing.state.writer.cleanup();
|
|
12809
|
+
if (writeTransactionActive) {
|
|
12810
|
+
try {
|
|
12811
|
+
database.rollbackWriteTransaction();
|
|
12812
|
+
} catch (rollbackError) {
|
|
12813
|
+
this.logger.error("Failed to roll back indexing database transaction", {
|
|
12814
|
+
error: getErrorMessage4(rollbackError)
|
|
12815
|
+
});
|
|
12816
|
+
}
|
|
12063
12817
|
}
|
|
12818
|
+
throw error;
|
|
12064
12819
|
}
|
|
12065
|
-
stats.durationMs = Date.now() - startTime;
|
|
12066
|
-
if (forceScopedReembed && failedForcedChunkIds.size === 0) {
|
|
12067
|
-
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
12068
|
-
}
|
|
12069
|
-
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
12070
|
-
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
12071
|
-
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
12072
|
-
this.saveBranchCommit(database, indexedCommit);
|
|
12073
|
-
this.saveIndexMetadata(configuredProviderInfo);
|
|
12074
|
-
this.indexCompatibility = { compatible: true };
|
|
12075
|
-
this.logger.recordIndexingEnd();
|
|
12076
|
-
this.logger.info("Indexing complete", {
|
|
12077
|
-
files: stats.totalFiles,
|
|
12078
|
-
indexed: stats.indexedChunks,
|
|
12079
|
-
existing: stats.existingChunks,
|
|
12080
|
-
removed: stats.removedChunks,
|
|
12081
|
-
failed: stats.failedChunks,
|
|
12082
|
-
tokens: stats.tokensUsed,
|
|
12083
|
-
durationMs: stats.durationMs
|
|
12084
|
-
});
|
|
12085
|
-
if (stats.failedChunks > 0) {
|
|
12086
|
-
stats.failedBatchesPath = this.failedBatchesPath;
|
|
12087
|
-
}
|
|
12088
|
-
onProgress?.({
|
|
12089
|
-
phase: "complete",
|
|
12090
|
-
filesProcessed: files.length,
|
|
12091
|
-
totalFiles: files.length,
|
|
12092
|
-
chunksProcessed: stats.indexedChunks,
|
|
12093
|
-
totalChunks: pendingChunks.length
|
|
12094
|
-
});
|
|
12095
|
-
return stats;
|
|
12096
12820
|
}
|
|
12097
12821
|
async getQueryEmbedding(query, provider) {
|
|
12098
12822
|
const now2 = Date.now();
|
|
@@ -12330,10 +13054,31 @@ var Indexer = class _Indexer {
|
|
|
12330
13054
|
const baseFiltered = tiered.filter(
|
|
12331
13055
|
(r) => matchesSearchFilters(r, options, this.config.search.minScore, this.projectRoot)
|
|
12332
13056
|
);
|
|
12333
|
-
|
|
13057
|
+
let communityRanked = baseFiltered;
|
|
13058
|
+
if (this.config.search.communityBoost > 0) {
|
|
13059
|
+
try {
|
|
13060
|
+
const sameCommunityCandidateIds = resolveSameCommunityCandidateIds(
|
|
13061
|
+
query,
|
|
13062
|
+
baseFiltered,
|
|
13063
|
+
database,
|
|
13064
|
+
this.getBranchCatalogKeys()
|
|
13065
|
+
);
|
|
13066
|
+
communityRanked = applyCommunityBoost(
|
|
13067
|
+
baseFiltered,
|
|
13068
|
+
sameCommunityCandidateIds,
|
|
13069
|
+
this.config.search.communityBoost
|
|
13070
|
+
);
|
|
13071
|
+
} catch (error) {
|
|
13072
|
+
this.logger.search("debug", "Community-aware ranking unavailable; using existing ranking", {
|
|
13073
|
+
query,
|
|
13074
|
+
error: getErrorMessage4(error)
|
|
13075
|
+
});
|
|
13076
|
+
}
|
|
13077
|
+
}
|
|
13078
|
+
const implementationOnly = communityRanked.filter(
|
|
12334
13079
|
(r) => isLikelyImplementationPath2(r.metadata.filePath) && isImplementationChunkType(r.metadata.chunkType)
|
|
12335
13080
|
);
|
|
12336
|
-
const filtered = (sourceIntent && hasCodeHints && implementationOnly.length > 0 ? implementationOnly :
|
|
13081
|
+
const filtered = (sourceIntent && hasCodeHints && implementationOnly.length > 0 ? implementationOnly : communityRanked).slice(0, maxResults);
|
|
12337
13082
|
const identifierFallback = !options?.definitionIntent && filtered.length === 0 && identifierHints.length > 0 ? buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbolIds, maxResults, union, true).filter((r) => matchesSearchFilters(r, options, this.config.search.minScore, this.projectRoot)).slice(0, maxResults) : [];
|
|
12338
13083
|
const finalResults = filtered.length > 0 ? filtered : identifierFallback;
|
|
12339
13084
|
const totalSearchMs = import_perf_hooks.performance.now() - searchStartTime;
|
|
@@ -12550,7 +13295,7 @@ var Indexer = class _Indexer {
|
|
|
12550
13295
|
this.saveFileHashCache();
|
|
12551
13296
|
database.clearAllIndexedData();
|
|
12552
13297
|
this.deleteBranchCommitMetadata(database, clearedBranchKeys2);
|
|
12553
|
-
this.
|
|
13298
|
+
this.clearFailedBatchState();
|
|
12554
13299
|
database.deleteMetadata("index.version");
|
|
12555
13300
|
database.deleteMetadata("index.pathStorageVersion");
|
|
12556
13301
|
database.deleteMetadata("index.embeddingProvider");
|
|
@@ -12681,7 +13426,7 @@ var Indexer = class _Indexer {
|
|
|
12681
13426
|
gcOrphanSymbols: 0,
|
|
12682
13427
|
gcOrphanCallEdges: 0,
|
|
12683
13428
|
resetCorruptedIndex: true,
|
|
12684
|
-
warning: this.getCorruptedIndexWarning(
|
|
13429
|
+
warning: this.getCorruptedIndexWarning(path19.join(this.indexPath, "codebase.db"))
|
|
12685
13430
|
};
|
|
12686
13431
|
}
|
|
12687
13432
|
this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
|
|
@@ -12711,180 +13456,94 @@ var Indexer = class _Indexer {
|
|
|
12711
13456
|
const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
|
|
12712
13457
|
const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
|
|
12713
13458
|
const roots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
12714
|
-
const
|
|
12715
|
-
|
|
12716
|
-
|
|
13459
|
+
const failedProcessing = this.prepareFailedBatchProcessing(roots, () => true);
|
|
13460
|
+
if (failedProcessing.latestById.size === 0) {
|
|
13461
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state);
|
|
12717
13462
|
return { succeeded: 0, failed: 0, remaining: 0 };
|
|
12718
13463
|
}
|
|
13464
|
+
const queue = new PQueue({ concurrency: 1 });
|
|
13465
|
+
const rateLimitState = { backoffMs: 0 };
|
|
12719
13466
|
let succeeded = 0;
|
|
12720
13467
|
let failed = 0;
|
|
12721
|
-
|
|
12722
|
-
|
|
12723
|
-
|
|
12724
|
-
|
|
12725
|
-
|
|
12726
|
-
|
|
12727
|
-
|
|
12728
|
-
const
|
|
12729
|
-
|
|
12730
|
-
|
|
12731
|
-
|
|
12732
|
-
|
|
12733
|
-
);
|
|
12734
|
-
|
|
12735
|
-
|
|
12736
|
-
|
|
12737
|
-
|
|
12738
|
-
|
|
12739
|
-
|
|
12740
|
-
|
|
12741
|
-
|
|
12742
|
-
|
|
12743
|
-
|
|
12744
|
-
|
|
12745
|
-
|
|
12746
|
-
|
|
12747
|
-
|
|
12748
|
-
|
|
12749
|
-
|
|
12750
|
-
|
|
12751
|
-
|
|
12752
|
-
|
|
12753
|
-
}
|
|
12754
|
-
const vector = result.embeddings[idx];
|
|
12755
|
-
if (!vector) {
|
|
12756
|
-
throw new Error(`Embedding API returned too few vectors for chunk ${request.chunk.id}`);
|
|
12757
|
-
}
|
|
12758
|
-
const parts = embeddingPartsByChunk.get(request.chunk.id) ?? [];
|
|
12759
|
-
parts[request.partIndex] = {
|
|
12760
|
-
vector,
|
|
12761
|
-
tokenCount: request.tokenCount
|
|
12762
|
-
};
|
|
12763
|
-
embeddingPartsByChunk.set(request.chunk.id, parts);
|
|
12764
|
-
touchedChunkIds.add(request.chunk.id);
|
|
12765
|
-
});
|
|
12766
|
-
for (const chunkId of touchedChunkIds) {
|
|
12767
|
-
if (failedChunkIds.has(chunkId) || completedChunkIds.has(chunkId)) {
|
|
12768
|
-
continue;
|
|
12769
|
-
}
|
|
12770
|
-
const chunk = batchChunksById.get(chunkId);
|
|
12771
|
-
if (!chunk) {
|
|
12772
|
-
continue;
|
|
12773
|
-
}
|
|
12774
|
-
const parts = embeddingPartsByChunk.get(chunk.id) ?? [];
|
|
12775
|
-
if (!hasAllEmbeddingParts(parts, chunk.texts.length)) {
|
|
12776
|
-
continue;
|
|
12777
|
-
}
|
|
12778
|
-
const orderedParts = parts;
|
|
12779
|
-
pooledResults.push({
|
|
12780
|
-
chunk,
|
|
12781
|
-
vector: poolEmbeddingVectors(
|
|
12782
|
-
orderedParts.map((part) => part.vector),
|
|
12783
|
-
orderedParts.map((part) => part.tokenCount)
|
|
12784
|
-
)
|
|
12785
|
-
});
|
|
12786
|
-
}
|
|
12787
|
-
this.logger.recordEmbeddingApiCall(result.totalTokensUsed);
|
|
12788
|
-
} catch (error) {
|
|
12789
|
-
const failureMessage = String(error);
|
|
12790
|
-
const failureTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
12791
|
-
const failedChunks = getUniquePendingChunksFromRequests(requestBatch).filter((chunk) => !completedChunkIds.has(chunk.id) && !failedChunkIds.has(chunk.id));
|
|
12792
|
-
for (const chunk of failedChunks) {
|
|
12793
|
-
failedChunkIds.add(chunk.id);
|
|
12794
|
-
embeddingPartsByChunk.delete(chunk.id);
|
|
12795
|
-
failedChunksForBatch.set(chunk.id, {
|
|
12796
|
-
chunks: [chunk],
|
|
12797
|
-
attemptCount: batch.attemptCount + 1,
|
|
12798
|
-
lastAttempt: failureTimestamp,
|
|
12799
|
-
error: failureMessage
|
|
12800
|
-
});
|
|
12801
|
-
}
|
|
12802
|
-
failed += failedChunks.length;
|
|
12803
|
-
this.logger.recordEmbeddingError();
|
|
12804
|
-
}
|
|
12805
|
-
}
|
|
12806
|
-
const successfulResults = pooledResults.filter(({ chunk }) => !failedChunkIds.has(chunk.id));
|
|
12807
|
-
const items = successfulResults.map(({ chunk, vector }) => ({
|
|
12808
|
-
id: chunk.id,
|
|
12809
|
-
vector,
|
|
12810
|
-
metadata: chunk.metadata
|
|
12811
|
-
}));
|
|
12812
|
-
if (items.length > 0) {
|
|
12813
|
-
store.addBatch(items);
|
|
12814
|
-
}
|
|
12815
|
-
if (successfulResults.length > 0) {
|
|
12816
|
-
try {
|
|
12817
|
-
database.upsertEmbeddingsBatch(
|
|
12818
|
-
successfulResults.map(({ chunk, vector }) => ({
|
|
12819
|
-
contentHash: chunk.contentHash,
|
|
12820
|
-
embedding: float32ArrayToBuffer(vector),
|
|
12821
|
-
chunkText: chunk.storageText,
|
|
12822
|
-
model: configuredProviderInfo.modelInfo.model
|
|
12823
|
-
}))
|
|
12824
|
-
);
|
|
12825
|
-
} catch (dbError) {
|
|
12826
|
-
this.rebuildVectorStoreExcludingChunkIds(
|
|
12827
|
-
store,
|
|
12828
|
-
database,
|
|
12829
|
-
successfulResults.map(({ chunk }) => chunk.id)
|
|
13468
|
+
try {
|
|
13469
|
+
const retryableChunks = this.iterateLatestFailedChunks(
|
|
13470
|
+
failedProcessing.latestById,
|
|
13471
|
+
roots,
|
|
13472
|
+
() => true,
|
|
13473
|
+
maxChunkTokens
|
|
13474
|
+
);
|
|
13475
|
+
for (const retryBatch of iterateOrderedFileBatches(
|
|
13476
|
+
retryableChunks,
|
|
13477
|
+
({ chunk }) => Buffer.byteLength(chunk.content, "utf-8"),
|
|
13478
|
+
this.fileBatchLimits
|
|
13479
|
+
)) {
|
|
13480
|
+
const chunks = retryBatch.map(({ chunk }) => chunk);
|
|
13481
|
+
const attemptCounts = new Map(retryBatch.map(({ chunk, attemptCount }) => [chunk.id, attemptCount]));
|
|
13482
|
+
const batchResult = await this.processPendingChunkBatch(chunks, {
|
|
13483
|
+
store,
|
|
13484
|
+
provider,
|
|
13485
|
+
invertedIndex,
|
|
13486
|
+
database,
|
|
13487
|
+
configuredProviderInfo,
|
|
13488
|
+
queue,
|
|
13489
|
+
providerRateLimits,
|
|
13490
|
+
rateLimitState,
|
|
13491
|
+
failedState: failedProcessing.state,
|
|
13492
|
+
attemptCounts,
|
|
13493
|
+
forceReembed: false,
|
|
13494
|
+
reuseCachedEmbeddings: false,
|
|
13495
|
+
incrementRepeatedFailures: false,
|
|
13496
|
+
onSucceeded: (succeededChunks) => {
|
|
13497
|
+
database.addChunksToBranchBatch(
|
|
13498
|
+
this.getBranchCatalogKey(),
|
|
13499
|
+
succeededChunks.map((chunk) => chunk.id)
|
|
12830
13500
|
);
|
|
12831
|
-
throw dbError;
|
|
12832
13501
|
}
|
|
12833
|
-
}
|
|
12834
|
-
|
|
12835
|
-
|
|
12836
|
-
invertedIndex.addChunk(chunk.id, chunk.content);
|
|
12837
|
-
completedChunkIds.add(chunk.id);
|
|
12838
|
-
embeddingPartsByChunk.delete(chunk.id);
|
|
12839
|
-
}
|
|
12840
|
-
database.addChunksToBranchBatch(
|
|
12841
|
-
this.getBranchCatalogKey(),
|
|
12842
|
-
successfulResults.map(({ chunk }) => chunk.id)
|
|
12843
|
-
);
|
|
12844
|
-
this.logger.recordChunksEmbedded(successfulResults.length);
|
|
12845
|
-
succeeded += successfulResults.length;
|
|
12846
|
-
stillFailing.push(...failedChunksForBatch.values());
|
|
12847
|
-
} catch (error) {
|
|
12848
|
-
const failureMessage = getErrorMessage4(error);
|
|
12849
|
-
const failureTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
12850
|
-
const unaccountedChunks = batch.chunks.filter(
|
|
12851
|
-
(chunk) => !failedChunksForBatch.has(chunk.id) && !completedChunkIds.has(chunk.id)
|
|
12852
|
-
);
|
|
12853
|
-
for (const chunk of unaccountedChunks) {
|
|
12854
|
-
failedChunksForBatch.set(chunk.id, {
|
|
12855
|
-
chunks: [chunk],
|
|
12856
|
-
attemptCount: batch.attemptCount + 1,
|
|
12857
|
-
lastAttempt: failureTimestamp,
|
|
12858
|
-
error: failureMessage
|
|
12859
|
-
});
|
|
12860
|
-
}
|
|
12861
|
-
failed += unaccountedChunks.length;
|
|
12862
|
-
this.logger.recordEmbeddingError();
|
|
12863
|
-
stillFailing.push(...coalesceFailedBatches(Array.from(failedChunksForBatch.values())));
|
|
13502
|
+
});
|
|
13503
|
+
succeeded += batchResult.indexedChunks;
|
|
13504
|
+
failed += batchResult.failedChunks;
|
|
12864
13505
|
}
|
|
13506
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state);
|
|
13507
|
+
} catch (error) {
|
|
13508
|
+
failedProcessing.state.writer.cleanup();
|
|
13509
|
+
throw error;
|
|
12865
13510
|
}
|
|
12866
|
-
const
|
|
12867
|
-
if (roots) {
|
|
12868
|
-
this.saveFailedBatches([...retainedFailedBatches, ...persistedStillFailing]);
|
|
12869
|
-
} else {
|
|
12870
|
-
this.saveFailedBatches(persistedStillFailing);
|
|
12871
|
-
}
|
|
13511
|
+
const remaining = this.getFailedBatchesCount();
|
|
12872
13512
|
if (succeeded > 0) {
|
|
12873
13513
|
store.save();
|
|
12874
13514
|
this.saveInvertedIndex(invertedIndex);
|
|
12875
13515
|
}
|
|
12876
|
-
if (roots && succeeded > 0 &&
|
|
13516
|
+
if (roots && succeeded > 0 && remaining === 0 && this.hasProjectForceReembedPending()) {
|
|
12877
13517
|
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
12878
13518
|
this.saveIndexMetadata(configuredProviderInfo);
|
|
12879
13519
|
this.indexCompatibility = { compatible: true };
|
|
12880
13520
|
}
|
|
12881
|
-
return { succeeded, failed, remaining
|
|
13521
|
+
return { succeeded, failed, remaining };
|
|
12882
13522
|
}
|
|
12883
13523
|
getFailedBatchesCount() {
|
|
12884
|
-
|
|
12885
|
-
|
|
13524
|
+
const roots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
13525
|
+
const latestById = /* @__PURE__ */ new Map();
|
|
13526
|
+
for (const batch of this.loadSerializedFailedBatches()) {
|
|
13527
|
+
for (const rawChunk of batch.chunks) {
|
|
13528
|
+
const filePath = getPendingChunkFilePath(rawChunk);
|
|
13529
|
+
if (roots && (filePath === null || !this.isFileInCurrentScope(filePath, roots))) {
|
|
13530
|
+
continue;
|
|
13531
|
+
}
|
|
13532
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
13533
|
+
if (!chunkId) {
|
|
13534
|
+
continue;
|
|
13535
|
+
}
|
|
13536
|
+
const existing = latestById.get(chunkId);
|
|
13537
|
+
if (!existing || batch.attemptCount >= existing.attemptCount) {
|
|
13538
|
+
latestById.set(chunkId, {
|
|
13539
|
+
attemptCount: batch.attemptCount,
|
|
13540
|
+
error: batch.error,
|
|
13541
|
+
lastAttempt: batch.lastAttempt
|
|
13542
|
+
});
|
|
13543
|
+
}
|
|
13544
|
+
}
|
|
12886
13545
|
}
|
|
12887
|
-
return
|
|
13546
|
+
return new Set(Array.from(latestById.values(), getFailedBatchGroupKey)).size;
|
|
12888
13547
|
}
|
|
12889
13548
|
getCurrentBranch() {
|
|
12890
13549
|
return this.currentBranch;
|
|
@@ -13072,9 +13731,9 @@ var Indexer = class _Indexer {
|
|
|
13072
13731
|
this.requireReadableComponents(readIssues, "database");
|
|
13073
13732
|
let shortest = [];
|
|
13074
13733
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
13075
|
-
const
|
|
13076
|
-
if (
|
|
13077
|
-
shortest =
|
|
13734
|
+
const path21 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
|
|
13735
|
+
if (path21.length > 0 && (shortest.length === 0 || path21.length < shortest.length)) {
|
|
13736
|
+
shortest = path21;
|
|
13078
13737
|
}
|
|
13079
13738
|
}
|
|
13080
13739
|
return shortest.map((hop) => this.resolveFilePathRecord(hop));
|
|
@@ -13122,13 +13781,13 @@ var Indexer = class _Indexer {
|
|
|
13122
13781
|
}
|
|
13123
13782
|
}
|
|
13124
13783
|
if (!found) continue;
|
|
13125
|
-
const
|
|
13784
|
+
const path21 = [];
|
|
13126
13785
|
let currentSymbolId = toSymbolId;
|
|
13127
13786
|
while (true) {
|
|
13128
13787
|
const symbol = symbolsById.get(currentSymbolId);
|
|
13129
13788
|
if (!symbol) break;
|
|
13130
13789
|
const parent = parentBySymbolId.get(currentSymbolId);
|
|
13131
|
-
|
|
13790
|
+
path21.push({
|
|
13132
13791
|
symbolId: symbol.id,
|
|
13133
13792
|
symbolName: symbol.name,
|
|
13134
13793
|
filePath: symbol.filePath,
|
|
@@ -13138,9 +13797,9 @@ var Indexer = class _Indexer {
|
|
|
13138
13797
|
if (!parent) break;
|
|
13139
13798
|
currentSymbolId = parent.parentId;
|
|
13140
13799
|
}
|
|
13141
|
-
|
|
13142
|
-
if (
|
|
13143
|
-
shortest =
|
|
13800
|
+
path21.reverse();
|
|
13801
|
+
if (path21.length > 0 && (shortest.length === 0 || path21.length < shortest.length)) {
|
|
13802
|
+
shortest = path21;
|
|
13144
13803
|
}
|
|
13145
13804
|
}
|
|
13146
13805
|
return shortest.map((hop) => this.resolveFilePathRecord(hop));
|
|
@@ -13159,13 +13818,13 @@ var Indexer = class _Indexer {
|
|
|
13159
13818
|
async getSymbolsForBranch(branch) {
|
|
13160
13819
|
const { database, readIssues } = await this.ensureInitialized();
|
|
13161
13820
|
this.requireReadableComponents(readIssues, "database");
|
|
13162
|
-
const resolvedBranch =
|
|
13821
|
+
const resolvedBranch = this.resolveBranchCatalogKey(branch);
|
|
13163
13822
|
return database.getSymbolsForBranch(resolvedBranch).map((symbol) => this.resolveFilePathRecord(symbol));
|
|
13164
13823
|
}
|
|
13165
13824
|
async getSymbolsForFiles(filePaths, branch) {
|
|
13166
13825
|
const { database, readIssues } = await this.ensureInitialized();
|
|
13167
13826
|
this.requireReadableComponents(readIssues, "database");
|
|
13168
|
-
const resolvedBranch =
|
|
13827
|
+
const resolvedBranch = this.resolveBranchCatalogKey(branch);
|
|
13169
13828
|
const storedFilePaths = filePaths.map((filePath) => this.toStoredFilePath(filePath));
|
|
13170
13829
|
return database.getSymbolsForFiles(storedFilePaths, resolvedBranch).map((symbol) => this.resolveFilePathRecord(symbol));
|
|
13171
13830
|
}
|
|
@@ -13178,13 +13837,26 @@ var Indexer = class _Indexer {
|
|
|
13178
13837
|
async detectCommunities(branch, symbolIds) {
|
|
13179
13838
|
const { database, readIssues } = await this.ensureInitialized();
|
|
13180
13839
|
this.requireReadableComponents(readIssues, "database");
|
|
13181
|
-
const resolvedBranch =
|
|
13840
|
+
const resolvedBranch = this.resolveBranchCatalogKey(branch);
|
|
13182
13841
|
return database.detectCommunities(resolvedBranch, symbolIds).map((entry) => this.resolveFilePathRecord(entry));
|
|
13183
13842
|
}
|
|
13843
|
+
async detectCommunityCouplings(branch) {
|
|
13844
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
13845
|
+
this.requireReadableComponents(readIssues, "database");
|
|
13846
|
+
const resolvedBranch = this.resolveBranchCatalogKey(branch);
|
|
13847
|
+
return database.detectCommunityCouplings(resolvedBranch).map((entry) => ({
|
|
13848
|
+
...entry,
|
|
13849
|
+
relationships: (entry.relationships ?? entry.representativeRelationships ?? []).map((relationship) => ({
|
|
13850
|
+
...relationship,
|
|
13851
|
+
fromFilePath: this.resolveStoredFilePath(relationship.fromFilePath),
|
|
13852
|
+
toFilePath: this.resolveStoredFilePath(relationship.toFilePath)
|
|
13853
|
+
}))
|
|
13854
|
+
}));
|
|
13855
|
+
}
|
|
13184
13856
|
async computeCentrality(branch) {
|
|
13185
13857
|
const { database, readIssues } = await this.ensureInitialized();
|
|
13186
13858
|
this.requireReadableComponents(readIssues, "database");
|
|
13187
|
-
const resolvedBranch =
|
|
13859
|
+
const resolvedBranch = this.resolveBranchCatalogKey(branch);
|
|
13188
13860
|
return database.computeCentrality(resolvedBranch).map((entry) => this.resolveFilePathRecord(entry));
|
|
13189
13861
|
}
|
|
13190
13862
|
async getPrImpact(opts, onPreparationProgress) {
|
|
@@ -13278,7 +13950,7 @@ var Indexer = class _Indexer {
|
|
|
13278
13950
|
);
|
|
13279
13951
|
}
|
|
13280
13952
|
}
|
|
13281
|
-
const toStoredChangedFiles = (filePaths) => filePaths.map((filePath) => this.toStoredFilePath(
|
|
13953
|
+
const toStoredChangedFiles = (filePaths) => filePaths.map((filePath) => this.toStoredFilePath(path19.resolve(this.projectRoot, filePath)));
|
|
13282
13954
|
const storedChangedFiles = toStoredChangedFiles(changedFiles);
|
|
13283
13955
|
const directSymbols = database.getSymbolsForFiles(storedChangedFiles, branchKey);
|
|
13284
13956
|
const directIds = directSymbols.map((s) => s.id);
|
|
@@ -13427,12 +14099,12 @@ var Indexer = class _Indexer {
|
|
|
13427
14099
|
if (meta.filePath) filePaths.add(meta.filePath);
|
|
13428
14100
|
}
|
|
13429
14101
|
const directory = options?.directory?.replace(/\/$/, "");
|
|
13430
|
-
const absoluteDirectoryFilter = directory ?
|
|
14102
|
+
const absoluteDirectoryFilter = directory ? path19.resolve(this.projectRoot, directory) : void 0;
|
|
13431
14103
|
for (const filePath of filePaths) {
|
|
13432
14104
|
if (directory) {
|
|
13433
14105
|
const absoluteFilePath = this.resolveStoredFilePath(filePath);
|
|
13434
14106
|
const matchesRelative = filePath === directory || filePath.startsWith(directory + "/");
|
|
13435
|
-
const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter +
|
|
14107
|
+
const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter + path19.sep));
|
|
13436
14108
|
if (!matchesRelative && !matchesProjectRelative) {
|
|
13437
14109
|
continue;
|
|
13438
14110
|
}
|
|
@@ -13592,7 +14264,7 @@ function trimOrUndefined(value) {
|
|
|
13592
14264
|
return normalized || void 0;
|
|
13593
14265
|
}
|
|
13594
14266
|
function normalizeCallGraphPath(value) {
|
|
13595
|
-
let normalized =
|
|
14267
|
+
let normalized = path20.posix.normalize(value.trim().replaceAll("\\", "/"));
|
|
13596
14268
|
if (normalized.startsWith("./")) {
|
|
13597
14269
|
normalized = normalized.slice(2);
|
|
13598
14270
|
}
|
|
@@ -13776,12 +14448,12 @@ async function getCallGraphPath(projectRoot3, host, from, to, maxDepth, fromFile
|
|
|
13776
14448
|
if (fromResolution.status !== "resolved" || toResolution.status !== "resolved") {
|
|
13777
14449
|
return { from: fromResolution, to: toResolution, path: [] };
|
|
13778
14450
|
}
|
|
13779
|
-
const
|
|
14451
|
+
const path21 = await indexer.findCallPathBySymbolIds(
|
|
13780
14452
|
fromResolution.symbolId,
|
|
13781
14453
|
toResolution.symbolId,
|
|
13782
14454
|
maxDepth
|
|
13783
14455
|
);
|
|
13784
|
-
return { from: fromResolution, to: toResolution, path:
|
|
14456
|
+
return { from: fromResolution, to: toResolution, path: path21 };
|
|
13785
14457
|
}
|
|
13786
14458
|
async function runIndexCodebase(projectRoot3, host, args, onProgress) {
|
|
13787
14459
|
const root = getProjectRoot(projectRoot3, host);
|
|
@@ -13857,6 +14529,34 @@ async function getPrImpact(projectRoot3, host, params) {
|
|
|
13857
14529
|
direction: params.direction
|
|
13858
14530
|
});
|
|
13859
14531
|
}
|
|
14532
|
+
async function getCodeCommunities(projectRoot3, host, params) {
|
|
14533
|
+
await ensureAutoIndexReadyForRetrieval(projectRoot3, host);
|
|
14534
|
+
const indexer = getIndexerForProject(projectRoot3, host);
|
|
14535
|
+
const [communities, centrality, couplings] = await Promise.all([
|
|
14536
|
+
indexer.detectCommunities(params.branch),
|
|
14537
|
+
indexer.computeCentrality(params.branch),
|
|
14538
|
+
indexer.detectCommunityCouplings(params.branch)
|
|
14539
|
+
]);
|
|
14540
|
+
return buildCodeCommunitiesResult(communities, centrality, couplings, {
|
|
14541
|
+
minSize: Math.max(CODE_COMMUNITIES_MIN_SIZE, Math.floor(params.minSize ?? CODE_COMMUNITIES_MIN_SIZE)),
|
|
14542
|
+
limit: Math.min(
|
|
14543
|
+
CODE_COMMUNITIES_MAX_LIMIT,
|
|
14544
|
+
Math.max(1, Math.floor(params.limit ?? CODE_COMMUNITIES_DEFAULT_LIMIT))
|
|
14545
|
+
),
|
|
14546
|
+
hubThreshold: Math.max(
|
|
14547
|
+
0,
|
|
14548
|
+
Math.floor(params.hubThreshold ?? CODE_COMMUNITIES_DEFAULT_HUB_THRESHOLD)
|
|
14549
|
+
),
|
|
14550
|
+
minCoupling: Math.max(
|
|
14551
|
+
CODE_COMMUNITIES_MIN_COUPLING,
|
|
14552
|
+
Math.floor(params.minCoupling ?? CODE_COMMUNITIES_MIN_COUPLING)
|
|
14553
|
+
),
|
|
14554
|
+
couplingLimit: Math.min(
|
|
14555
|
+
CODE_COMMUNITIES_MAX_COUPLING_LIMIT,
|
|
14556
|
+
Math.max(1, Math.floor(params.couplingLimit ?? CODE_COMMUNITIES_DEFAULT_COUPLING_LIMIT))
|
|
14557
|
+
)
|
|
14558
|
+
});
|
|
14559
|
+
}
|
|
13860
14560
|
async function getIndexMetrics(projectRoot3, host, args = {}) {
|
|
13861
14561
|
const root = getProjectRoot(projectRoot3, host);
|
|
13862
14562
|
const key = getIndexerCacheKey(root, host);
|
|
@@ -13948,8 +14648,8 @@ async function getIndexLogs(projectRoot3, host, args) {
|
|
|
13948
14648
|
function addKnowledgeBase(projectRoot3, host, knowledgeBasePath) {
|
|
13949
14649
|
const root = getProjectRoot(projectRoot3, host);
|
|
13950
14650
|
const inputPath = knowledgeBasePath.trim();
|
|
13951
|
-
const normalizedPath =
|
|
13952
|
-
|
|
14651
|
+
const normalizedPath = path20.resolve(
|
|
14652
|
+
path20.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, root)
|
|
13953
14653
|
);
|
|
13954
14654
|
if (!(0, import_fs13.existsSync)(normalizedPath)) {
|
|
13955
14655
|
return `Error: Directory does not exist: ${normalizedPath}`;
|
|
@@ -13985,7 +14685,7 @@ function addKnowledgeBase(projectRoot3, host, knowledgeBasePath) {
|
|
|
13985
14685
|
}
|
|
13986
14686
|
}
|
|
13987
14687
|
for (const dotDir of sensitiveDotDirs) {
|
|
13988
|
-
const sensitiveDir =
|
|
14688
|
+
const sensitiveDir = path20.join(homeDir, dotDir);
|
|
13989
14689
|
if (sensitiveDir && (realPath === sensitiveDir || realPath.startsWith(`${sensitiveDir}/`))) {
|
|
13990
14690
|
return `Error: Adding sensitive directory as knowledge base is not allowed: ${normalizedPath}`;
|
|
13991
14691
|
}
|
|
@@ -14048,7 +14748,7 @@ function listKnowledgeBases(projectRoot3, host) {
|
|
|
14048
14748
|
}
|
|
14049
14749
|
result += "\n";
|
|
14050
14750
|
}
|
|
14051
|
-
const hasHostConfig = (0, import_fs13.existsSync)(
|
|
14751
|
+
const hasHostConfig = (0, import_fs13.existsSync)(path20.join(root, getHostProjectConfigRelativePath(host)));
|
|
14052
14752
|
if (hasHostConfig) {
|
|
14053
14753
|
result += `
|
|
14054
14754
|
Config sources: 1 file(s).`;
|
|
@@ -14081,117 +14781,6 @@ Run /index to rebuild the index without the removed knowledge base.`;
|
|
|
14081
14781
|
return result;
|
|
14082
14782
|
}
|
|
14083
14783
|
|
|
14084
|
-
// src/tools/symbol-inference.ts
|
|
14085
|
-
var IDENTIFIER_RE = /[A-Za-z_$][A-Za-z0-9_$]*/g;
|
|
14086
|
-
var QUOTED_BACKTICK_RE = /`([^`]+)`/g;
|
|
14087
|
-
var QUOTED_SINGLE_RE = /'([^'\\]+)'/g;
|
|
14088
|
-
var QUOTED_DOUBLE_RE = /"([^"]+)"/g;
|
|
14089
|
-
var SYMBOL_LIKE_RE = /^(?:[A-Za-z_$][A-Za-z0-9_$]*)$/;
|
|
14090
|
-
var CAMEL_CASE_RE = /^[a-z_][A-Za-z0-9_$]*[A-Z][A-Za-z0-9_$]*$/;
|
|
14091
|
-
var PASCAL_CASE_RE = /^[A-Z][A-Za-z0-9_$]*$/;
|
|
14092
|
-
var SNAKE_CASE_RE = /^[a-z][a-z0-9_]*_[a-z0-9_]+$/;
|
|
14093
|
-
var DEFINITION_INTENT_RE = /\b(where|defined|definition|define|declaration|symbol|function|method|class|interface|type)\b/i;
|
|
14094
|
-
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
14095
|
-
"a",
|
|
14096
|
-
"an",
|
|
14097
|
-
"and",
|
|
14098
|
-
"are",
|
|
14099
|
-
"at",
|
|
14100
|
-
"for",
|
|
14101
|
-
"find",
|
|
14102
|
-
"how",
|
|
14103
|
-
"i",
|
|
14104
|
-
"in",
|
|
14105
|
-
"is",
|
|
14106
|
-
"it",
|
|
14107
|
-
"of",
|
|
14108
|
-
"on",
|
|
14109
|
-
"that",
|
|
14110
|
-
"the",
|
|
14111
|
-
"definition",
|
|
14112
|
-
"show",
|
|
14113
|
-
"to",
|
|
14114
|
-
"where",
|
|
14115
|
-
"which",
|
|
14116
|
-
"what",
|
|
14117
|
-
"you",
|
|
14118
|
-
"your",
|
|
14119
|
-
"with"
|
|
14120
|
-
]);
|
|
14121
|
-
function stripCallSuffix(token) {
|
|
14122
|
-
return token.replace(/\(\s*\)$/, "");
|
|
14123
|
-
}
|
|
14124
|
-
function isLikelySymbolName(token) {
|
|
14125
|
-
if (!SYMBOL_LIKE_RE.test(token)) {
|
|
14126
|
-
return false;
|
|
14127
|
-
}
|
|
14128
|
-
if (STOP_WORDS.has(token.toLowerCase())) {
|
|
14129
|
-
return false;
|
|
14130
|
-
}
|
|
14131
|
-
return CAMEL_CASE_RE.test(token) || PASCAL_CASE_RE.test(token) || SNAKE_CASE_RE.test(token);
|
|
14132
|
-
}
|
|
14133
|
-
function extractQuotedIdentifiers(query) {
|
|
14134
|
-
const identifiers = /* @__PURE__ */ new Set();
|
|
14135
|
-
for (const match of query.matchAll(QUOTED_BACKTICK_RE)) {
|
|
14136
|
-
const candidate = stripCallSuffix(match[1].trim());
|
|
14137
|
-
if (candidate && isLikelySymbolName(candidate)) {
|
|
14138
|
-
identifiers.add(candidate);
|
|
14139
|
-
}
|
|
14140
|
-
}
|
|
14141
|
-
for (const match of query.matchAll(QUOTED_SINGLE_RE)) {
|
|
14142
|
-
const candidate = stripCallSuffix(match[1].trim());
|
|
14143
|
-
if (candidate && isLikelySymbolName(candidate)) {
|
|
14144
|
-
identifiers.add(candidate);
|
|
14145
|
-
}
|
|
14146
|
-
}
|
|
14147
|
-
for (const match of query.matchAll(QUOTED_DOUBLE_RE)) {
|
|
14148
|
-
const candidate = stripCallSuffix(match[1].trim());
|
|
14149
|
-
if (candidate && isLikelySymbolName(candidate)) {
|
|
14150
|
-
identifiers.add(candidate);
|
|
14151
|
-
}
|
|
14152
|
-
}
|
|
14153
|
-
return [...identifiers];
|
|
14154
|
-
}
|
|
14155
|
-
function extractBareIdentifiers(query) {
|
|
14156
|
-
const unquoted = query.replace(QUOTED_BACKTICK_RE, " ").replace(QUOTED_SINGLE_RE, " ").replace(QUOTED_DOUBLE_RE, " ");
|
|
14157
|
-
const identifiers = /* @__PURE__ */ new Set();
|
|
14158
|
-
for (const match of unquoted.matchAll(IDENTIFIER_RE)) {
|
|
14159
|
-
const candidate = stripCallSuffix(match[0]);
|
|
14160
|
-
if (isLikelySymbolName(candidate)) {
|
|
14161
|
-
identifiers.add(candidate);
|
|
14162
|
-
}
|
|
14163
|
-
}
|
|
14164
|
-
return [...identifiers];
|
|
14165
|
-
}
|
|
14166
|
-
function isSingleMeaningfulToken(query, symbol) {
|
|
14167
|
-
const tokens = query.replace(/[`'"()]/g, " ").split(/[^A-Za-z0-9_$]+/).map((token) => token.trim().toLowerCase()).filter((token) => token.length > 0).filter((token) => !STOP_WORDS.has(token));
|
|
14168
|
-
return tokens.length === 1 && tokens[0] === symbol.toLowerCase();
|
|
14169
|
-
}
|
|
14170
|
-
function inferExactSymbolFromQuery(query) {
|
|
14171
|
-
if (analyzeQueryIntent(query).explicitArtifactIntent) {
|
|
14172
|
-
return void 0;
|
|
14173
|
-
}
|
|
14174
|
-
const quoted = extractQuotedIdentifiers(query);
|
|
14175
|
-
if (quoted.length === 1) {
|
|
14176
|
-
return quoted[0];
|
|
14177
|
-
}
|
|
14178
|
-
if (quoted.length > 1) {
|
|
14179
|
-
return void 0;
|
|
14180
|
-
}
|
|
14181
|
-
const candidates = extractBareIdentifiers(query);
|
|
14182
|
-
if (candidates.length !== 1) {
|
|
14183
|
-
return void 0;
|
|
14184
|
-
}
|
|
14185
|
-
const candidate = candidates[0];
|
|
14186
|
-
if (DEFINITION_INTENT_RE.test(query)) {
|
|
14187
|
-
return candidate;
|
|
14188
|
-
}
|
|
14189
|
-
if (isSingleMeaningfulToken(query, candidate)) {
|
|
14190
|
-
return candidate;
|
|
14191
|
-
}
|
|
14192
|
-
return void 0;
|
|
14193
|
-
}
|
|
14194
|
-
|
|
14195
14784
|
// src/tools/context-search.ts
|
|
14196
14785
|
var MIN_CONTEXT_RESULT_LIMIT = 1;
|
|
14197
14786
|
var MAX_CONTEXT_RESULT_LIMIT = 100;
|
|
@@ -14542,7 +15131,7 @@ async function resolveCodebaseContextUnmeasured(projectRoot3, host, input) {
|
|
|
14542
15131
|
const directory = input.directory ?? void 0;
|
|
14543
15132
|
const tokenBudget = input.tokenBudget ?? void 0;
|
|
14544
15133
|
if (from && to) {
|
|
14545
|
-
const
|
|
15134
|
+
const path21 = await getCallGraphPath(
|
|
14546
15135
|
projectRoot3,
|
|
14547
15136
|
host,
|
|
14548
15137
|
from,
|
|
@@ -14551,25 +15140,25 @@ async function resolveCodebaseContextUnmeasured(projectRoot3, host, input) {
|
|
|
14551
15140
|
fromFilePath,
|
|
14552
15141
|
toFilePath
|
|
14553
15142
|
);
|
|
14554
|
-
const pathText = formatCallGraphPathResult(
|
|
14555
|
-
if (
|
|
15143
|
+
const pathText = formatCallGraphPathResult(path21);
|
|
15144
|
+
if (path21.path.length > 0) {
|
|
14556
15145
|
const fitted2 = fitTextToContextBudget(
|
|
14557
15146
|
pathText,
|
|
14558
15147
|
tokenBudget
|
|
14559
15148
|
);
|
|
14560
15149
|
return {
|
|
14561
15150
|
text: fitted2.text,
|
|
14562
|
-
details: fittedDetails("path", fitted2,
|
|
15151
|
+
details: fittedDetails("path", fitted2, path21.path.length)
|
|
14563
15152
|
};
|
|
14564
15153
|
}
|
|
14565
|
-
if (
|
|
15154
|
+
if (path21.from.status !== "resolved" || path21.to.status !== "resolved") {
|
|
14566
15155
|
const fitted2 = fitTextToContextBudget(pathText, tokenBudget);
|
|
14567
15156
|
return {
|
|
14568
15157
|
text: fitted2.text,
|
|
14569
15158
|
details: fittedDetails("path", fitted2, 0)
|
|
14570
15159
|
};
|
|
14571
15160
|
}
|
|
14572
|
-
const resolvedFrom =
|
|
15161
|
+
const resolvedFrom = path21.from;
|
|
14573
15162
|
const { callers } = await getCallGraphData(projectRoot3, host, {
|
|
14574
15163
|
name: to,
|
|
14575
15164
|
direction: "callers",
|
|
@@ -14689,6 +15278,7 @@ var TOOL_NAME = {
|
|
|
14689
15278
|
CALL_GRAPH: "call_graph",
|
|
14690
15279
|
CALL_GRAPH_PATH: "call_graph_path",
|
|
14691
15280
|
PR_IMPACT: "pr_impact",
|
|
15281
|
+
CODE_COMMUNITIES: "code_communities",
|
|
14692
15282
|
ADD_KNOWLEDGE_BASE: "add_knowledge_base",
|
|
14693
15283
|
LIST_KNOWLEDGE_BASES: "list_knowledge_bases",
|
|
14694
15284
|
REMOVE_KNOWLEDGE_BASE: "remove_knowledge_base",
|
|
@@ -14710,7 +15300,8 @@ var PORTABLE_TOOL_NAMES = [
|
|
|
14710
15300
|
TOOL_NAME.IMPLEMENTATION_LOOKUP,
|
|
14711
15301
|
TOOL_NAME.CALL_GRAPH,
|
|
14712
15302
|
TOOL_NAME.CALL_GRAPH_PATH,
|
|
14713
|
-
TOOL_NAME.PR_IMPACT
|
|
15303
|
+
TOOL_NAME.PR_IMPACT,
|
|
15304
|
+
TOOL_NAME.CODE_COMMUNITIES
|
|
14714
15305
|
];
|
|
14715
15306
|
var OPENCODE_TOOL_NAMES = [
|
|
14716
15307
|
TOOL_NAME.CODEBASE_CONTEXT,
|
|
@@ -14729,6 +15320,7 @@ var OPENCODE_TOOL_NAMES = [
|
|
|
14729
15320
|
TOOL_NAME.LIST_KNOWLEDGE_BASES,
|
|
14730
15321
|
TOOL_NAME.REMOVE_KNOWLEDGE_BASE,
|
|
14731
15322
|
TOOL_NAME.PR_IMPACT,
|
|
15323
|
+
TOOL_NAME.CODE_COMMUNITIES,
|
|
14732
15324
|
TOOL_NAME.INDEX_VISUALIZE
|
|
14733
15325
|
];
|
|
14734
15326
|
var PI_TOOL_NAMES = [
|
|
@@ -14745,6 +15337,7 @@ var PI_TOOL_NAMES = [
|
|
|
14745
15337
|
TOOL_NAME.CALL_GRAPH,
|
|
14746
15338
|
TOOL_NAME.CALL_GRAPH_PATH,
|
|
14747
15339
|
TOOL_NAME.PR_IMPACT,
|
|
15340
|
+
TOOL_NAME.CODE_COMMUNITIES,
|
|
14748
15341
|
TOOL_NAME.PI_KNOWLEDGE_BASE_LIST,
|
|
14749
15342
|
TOOL_NAME.PI_KNOWLEDGE_BASE_ADD,
|
|
14750
15343
|
TOOL_NAME.PI_KNOWLEDGE_BASE_REMOVE
|
|
@@ -15039,6 +15632,25 @@ Check index_status first when index readiness is unknown. For repository questio
|
|
|
15039
15632
|
return text2(formatPrImpact(result), result);
|
|
15040
15633
|
}
|
|
15041
15634
|
});
|
|
15635
|
+
pi.registerTool({
|
|
15636
|
+
name: TOOL_NAME.CODE_COMMUNITIES,
|
|
15637
|
+
label: "Code Communities",
|
|
15638
|
+
description: "Discover natural module boundaries and hub symbols using graph community detection. Clusters symbols by call-graph connectivity to reveal architecture.",
|
|
15639
|
+
parameters: import_typebox2.Type.Object({
|
|
15640
|
+
branch: import_typebox2.Type.Optional(import_typebox2.Type.String()),
|
|
15641
|
+
minSize: import_typebox2.Type.Optional(import_typebox2.Type.Integer({ minimum: CODE_COMMUNITIES_MIN_SIZE, default: CODE_COMMUNITIES_MIN_SIZE })),
|
|
15642
|
+
limit: import_typebox2.Type.Optional(import_typebox2.Type.Integer({ minimum: 1, maximum: CODE_COMMUNITIES_MAX_LIMIT, default: CODE_COMMUNITIES_DEFAULT_LIMIT })),
|
|
15643
|
+
hubThreshold: import_typebox2.Type.Optional(import_typebox2.Type.Integer({ minimum: 0, default: CODE_COMMUNITIES_DEFAULT_HUB_THRESHOLD })),
|
|
15644
|
+
minCoupling: import_typebox2.Type.Optional(import_typebox2.Type.Integer({ minimum: CODE_COMMUNITIES_MIN_COUPLING, default: CODE_COMMUNITIES_MIN_COUPLING })),
|
|
15645
|
+
couplingLimit: import_typebox2.Type.Optional(
|
|
15646
|
+
import_typebox2.Type.Integer({ minimum: 1, maximum: CODE_COMMUNITIES_MAX_COUPLING_LIMIT, default: CODE_COMMUNITIES_DEFAULT_COUPLING_LIMIT })
|
|
15647
|
+
)
|
|
15648
|
+
}),
|
|
15649
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
15650
|
+
const result = await getCodeCommunities(projectRoot2(ctx), HOST2, params);
|
|
15651
|
+
return text2(formatCodeCommunities(result), result);
|
|
15652
|
+
}
|
|
15653
|
+
});
|
|
15042
15654
|
pi.registerTool({
|
|
15043
15655
|
name: TOOL_NAME.PI_KNOWLEDGE_BASE_LIST,
|
|
15044
15656
|
label: "List Knowledge Bases",
|