opencode-codebase-index 0.13.2 → 0.15.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/README.md +45 -6
- package/commands/peek.md +4 -0
- package/commands/search.md +4 -0
- package/dist/cli.cjs +1921 -640
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1919 -628
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1856 -580
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1855 -569
- package/dist/index.js.map +1 -1
- package/dist/pi-extension.cjs +1855 -610
- package/dist/pi-extension.cjs.map +1 -1
- package/dist/pi-extension.js +1861 -606
- 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 +11 -8
- package/skill/SKILL.md +5 -0
package/dist/pi-extension.cjs
CHANGED
|
@@ -495,7 +495,7 @@ var require_ignore = __commonJS({
|
|
|
495
495
|
// path matching.
|
|
496
496
|
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
497
497
|
// @returns {TestResult} true if a file is ignored
|
|
498
|
-
test(
|
|
498
|
+
test(path17, checkUnignored, mode) {
|
|
499
499
|
let ignored = false;
|
|
500
500
|
let unignored = false;
|
|
501
501
|
let matchedRule;
|
|
@@ -504,7 +504,7 @@ var require_ignore = __commonJS({
|
|
|
504
504
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
505
505
|
return;
|
|
506
506
|
}
|
|
507
|
-
const matched = rule[mode].test(
|
|
507
|
+
const matched = rule[mode].test(path17);
|
|
508
508
|
if (!matched) {
|
|
509
509
|
return;
|
|
510
510
|
}
|
|
@@ -525,17 +525,17 @@ var require_ignore = __commonJS({
|
|
|
525
525
|
var throwError = (message, Ctor) => {
|
|
526
526
|
throw new Ctor(message);
|
|
527
527
|
};
|
|
528
|
-
var checkPath = (
|
|
529
|
-
if (!isString(
|
|
528
|
+
var checkPath = (path17, originalPath, doThrow) => {
|
|
529
|
+
if (!isString(path17)) {
|
|
530
530
|
return doThrow(
|
|
531
531
|
`path must be a string, but got \`${originalPath}\``,
|
|
532
532
|
TypeError
|
|
533
533
|
);
|
|
534
534
|
}
|
|
535
|
-
if (!
|
|
535
|
+
if (!path17) {
|
|
536
536
|
return doThrow(`path must not be empty`, TypeError);
|
|
537
537
|
}
|
|
538
|
-
if (checkPath.isNotRelative(
|
|
538
|
+
if (checkPath.isNotRelative(path17)) {
|
|
539
539
|
const r = "`path.relative()`d";
|
|
540
540
|
return doThrow(
|
|
541
541
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -544,7 +544,7 @@ var require_ignore = __commonJS({
|
|
|
544
544
|
}
|
|
545
545
|
return true;
|
|
546
546
|
};
|
|
547
|
-
var isNotRelative = (
|
|
547
|
+
var isNotRelative = (path17) => REGEX_TEST_INVALID_PATH.test(path17);
|
|
548
548
|
checkPath.isNotRelative = isNotRelative;
|
|
549
549
|
checkPath.convert = (p) => p;
|
|
550
550
|
var Ignore2 = class {
|
|
@@ -574,19 +574,19 @@ var require_ignore = __commonJS({
|
|
|
574
574
|
}
|
|
575
575
|
// @returns {TestResult}
|
|
576
576
|
_test(originalPath, cache, checkUnignored, slices) {
|
|
577
|
-
const
|
|
577
|
+
const path17 = originalPath && checkPath.convert(originalPath);
|
|
578
578
|
checkPath(
|
|
579
|
-
|
|
579
|
+
path17,
|
|
580
580
|
originalPath,
|
|
581
581
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
582
582
|
);
|
|
583
|
-
return this._t(
|
|
583
|
+
return this._t(path17, cache, checkUnignored, slices);
|
|
584
584
|
}
|
|
585
|
-
checkIgnore(
|
|
586
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
587
|
-
return this.test(
|
|
585
|
+
checkIgnore(path17) {
|
|
586
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path17)) {
|
|
587
|
+
return this.test(path17);
|
|
588
588
|
}
|
|
589
|
-
const slices =
|
|
589
|
+
const slices = path17.split(SLASH).filter(Boolean);
|
|
590
590
|
slices.pop();
|
|
591
591
|
if (slices.length) {
|
|
592
592
|
const parent = this._t(
|
|
@@ -599,18 +599,18 @@ var require_ignore = __commonJS({
|
|
|
599
599
|
return parent;
|
|
600
600
|
}
|
|
601
601
|
}
|
|
602
|
-
return this._rules.test(
|
|
602
|
+
return this._rules.test(path17, false, MODE_CHECK_IGNORE);
|
|
603
603
|
}
|
|
604
|
-
_t(
|
|
605
|
-
if (
|
|
606
|
-
return cache[
|
|
604
|
+
_t(path17, cache, checkUnignored, slices) {
|
|
605
|
+
if (path17 in cache) {
|
|
606
|
+
return cache[path17];
|
|
607
607
|
}
|
|
608
608
|
if (!slices) {
|
|
609
|
-
slices =
|
|
609
|
+
slices = path17.split(SLASH).filter(Boolean);
|
|
610
610
|
}
|
|
611
611
|
slices.pop();
|
|
612
612
|
if (!slices.length) {
|
|
613
|
-
return cache[
|
|
613
|
+
return cache[path17] = this._rules.test(path17, checkUnignored, MODE_IGNORE);
|
|
614
614
|
}
|
|
615
615
|
const parent = this._t(
|
|
616
616
|
slices.join(SLASH) + SLASH,
|
|
@@ -618,29 +618,29 @@ var require_ignore = __commonJS({
|
|
|
618
618
|
checkUnignored,
|
|
619
619
|
slices
|
|
620
620
|
);
|
|
621
|
-
return cache[
|
|
621
|
+
return cache[path17] = parent.ignored ? parent : this._rules.test(path17, checkUnignored, MODE_IGNORE);
|
|
622
622
|
}
|
|
623
|
-
ignores(
|
|
624
|
-
return this._test(
|
|
623
|
+
ignores(path17) {
|
|
624
|
+
return this._test(path17, this._ignoreCache, false).ignored;
|
|
625
625
|
}
|
|
626
626
|
createFilter() {
|
|
627
|
-
return (
|
|
627
|
+
return (path17) => !this.ignores(path17);
|
|
628
628
|
}
|
|
629
629
|
filter(paths) {
|
|
630
630
|
return makeArray(paths).filter(this.createFilter());
|
|
631
631
|
}
|
|
632
632
|
// @returns {TestResult}
|
|
633
|
-
test(
|
|
634
|
-
return this._test(
|
|
633
|
+
test(path17) {
|
|
634
|
+
return this._test(path17, this._testCache, true);
|
|
635
635
|
}
|
|
636
636
|
};
|
|
637
637
|
var factory = (options) => new Ignore2(options);
|
|
638
|
-
var isPathValid = (
|
|
638
|
+
var isPathValid = (path17) => checkPath(path17 && checkPath.convert(path17), path17, RETURN_FALSE);
|
|
639
639
|
var setupWindows = () => {
|
|
640
640
|
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
641
641
|
checkPath.convert = makePosix;
|
|
642
642
|
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
643
|
-
checkPath.isNotRelative = (
|
|
643
|
+
checkPath.isNotRelative = (path17) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path17) || isNotRelative(path17);
|
|
644
644
|
};
|
|
645
645
|
if (
|
|
646
646
|
// Detect `process` so that it can run in browsers.
|
|
@@ -661,14 +661,14 @@ __export(pi_extension_exports, {
|
|
|
661
661
|
default: () => codebaseIndexPiExtension
|
|
662
662
|
});
|
|
663
663
|
module.exports = __toCommonJS(pi_extension_exports);
|
|
664
|
-
var
|
|
664
|
+
var import_typebox2 = require("typebox");
|
|
665
665
|
|
|
666
666
|
// src/config/constants.ts
|
|
667
667
|
var DEFAULT_INCLUDE = [
|
|
668
|
-
"**/*.{ts,tsx,js,jsx,mjs,cjs}",
|
|
668
|
+
"**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}",
|
|
669
669
|
"**/*.{py,pyi}",
|
|
670
|
-
"**/*.{go,rs,java,kt,scala}",
|
|
671
|
-
"**/*.{c,cpp,cc,h,hpp}",
|
|
670
|
+
"**/*.{go,rs,java,cs,kt,scala}",
|
|
671
|
+
"**/*.{c,cpp,cc,cxx,h,hpp,hxx}",
|
|
672
672
|
"**/*.{rb,php,inc,swift}",
|
|
673
673
|
"**/*.{cls,trigger}",
|
|
674
674
|
"**/*.{vue,svelte,astro}",
|
|
@@ -793,7 +793,8 @@ function getDefaultIndexingConfig() {
|
|
|
793
793
|
requireProjectMarker: true,
|
|
794
794
|
maxDepth: 5,
|
|
795
795
|
maxFilesPerDirectory: 100,
|
|
796
|
-
fallbackToTextOnMaxChunks: true
|
|
796
|
+
fallbackToTextOnMaxChunks: true,
|
|
797
|
+
gitBlame: { enabled: false }
|
|
797
798
|
};
|
|
798
799
|
}
|
|
799
800
|
function getDefaultSearchConfig() {
|
|
@@ -917,7 +918,10 @@ function parseConfig(raw) {
|
|
|
917
918
|
requireProjectMarker: typeof rawIndexing.requireProjectMarker === "boolean" ? rawIndexing.requireProjectMarker : defaultIndexing.requireProjectMarker,
|
|
918
919
|
maxDepth: typeof rawIndexing.maxDepth === "number" ? rawIndexing.maxDepth < -1 ? -1 : rawIndexing.maxDepth : defaultIndexing.maxDepth,
|
|
919
920
|
maxFilesPerDirectory: typeof rawIndexing.maxFilesPerDirectory === "number" ? Math.max(1, rawIndexing.maxFilesPerDirectory) : defaultIndexing.maxFilesPerDirectory,
|
|
920
|
-
fallbackToTextOnMaxChunks: typeof rawIndexing.fallbackToTextOnMaxChunks === "boolean" ? rawIndexing.fallbackToTextOnMaxChunks : defaultIndexing.fallbackToTextOnMaxChunks
|
|
921
|
+
fallbackToTextOnMaxChunks: typeof rawIndexing.fallbackToTextOnMaxChunks === "boolean" ? rawIndexing.fallbackToTextOnMaxChunks : defaultIndexing.fallbackToTextOnMaxChunks,
|
|
922
|
+
gitBlame: {
|
|
923
|
+
enabled: rawIndexing.gitBlame && typeof rawIndexing.gitBlame === "object" && typeof rawIndexing.gitBlame.enabled === "boolean" ? rawIndexing.gitBlame.enabled : defaultIndexing.gitBlame.enabled
|
|
924
|
+
}
|
|
921
925
|
};
|
|
922
926
|
const rawSearch = input.search && typeof input.search === "object" ? input.search : {};
|
|
923
927
|
const search = {
|
|
@@ -1333,8 +1337,8 @@ function formatPrImpact(result) {
|
|
|
1333
1337
|
}
|
|
1334
1338
|
|
|
1335
1339
|
// src/tools/operations.ts
|
|
1336
|
-
var
|
|
1337
|
-
var
|
|
1340
|
+
var import_fs10 = require("fs");
|
|
1341
|
+
var path16 = __toESM(require("path"), 1);
|
|
1338
1342
|
|
|
1339
1343
|
// src/config/merger.ts
|
|
1340
1344
|
var import_fs5 = require("fs");
|
|
@@ -1504,21 +1508,21 @@ function getProjectIndexRelativePath(host) {
|
|
|
1504
1508
|
return CODEBASE_PROJECT_INDEX_RELATIVE_PATH;
|
|
1505
1509
|
}
|
|
1506
1510
|
}
|
|
1507
|
-
function resolveWorktreeFallbackPath(
|
|
1508
|
-
const mainRepoRoot = resolveWorktreeMainRepoRoot(
|
|
1511
|
+
function resolveWorktreeFallbackPath(projectRoot3, relativePath) {
|
|
1512
|
+
const mainRepoRoot = resolveWorktreeMainRepoRoot(projectRoot3);
|
|
1509
1513
|
if (!mainRepoRoot) {
|
|
1510
1514
|
return null;
|
|
1511
1515
|
}
|
|
1512
1516
|
const fallbackPath = path4.join(mainRepoRoot, relativePath);
|
|
1513
1517
|
return (0, import_fs4.existsSync)(fallbackPath) ? fallbackPath : null;
|
|
1514
1518
|
}
|
|
1515
|
-
function resolveWorktreeFallbackProjectIndexPath(
|
|
1516
|
-
const inheritedHostPath = resolveWorktreeFallbackPath(
|
|
1519
|
+
function resolveWorktreeFallbackProjectIndexPath(projectRoot3, host) {
|
|
1520
|
+
const inheritedHostPath = resolveWorktreeFallbackPath(projectRoot3, getProjectIndexRelativePath(host));
|
|
1517
1521
|
if (inheritedHostPath) {
|
|
1518
1522
|
return inheritedHostPath;
|
|
1519
1523
|
}
|
|
1520
1524
|
if (host !== "opencode") {
|
|
1521
|
-
return resolveWorktreeFallbackPath(
|
|
1525
|
+
return resolveWorktreeFallbackPath(projectRoot3, OPENCODE_PROJECT_INDEX_RELATIVE_PATH);
|
|
1522
1526
|
}
|
|
1523
1527
|
return null;
|
|
1524
1528
|
}
|
|
@@ -1528,8 +1532,11 @@ function getHostProjectConfigRelativePath(host) {
|
|
|
1528
1532
|
function getHostProjectIndexRelativePath(host) {
|
|
1529
1533
|
return getProjectIndexRelativePath(host);
|
|
1530
1534
|
}
|
|
1531
|
-
function hasHostProjectConfig(
|
|
1532
|
-
return (0, import_fs4.existsSync)(path4.join(
|
|
1535
|
+
function hasHostProjectConfig(projectRoot3, host) {
|
|
1536
|
+
return (0, import_fs4.existsSync)(path4.join(projectRoot3, getProjectConfigRelativePath(host)));
|
|
1537
|
+
}
|
|
1538
|
+
function hasHostGlobalConfig(host) {
|
|
1539
|
+
return (0, import_fs4.existsSync)(getGlobalConfigPath(host));
|
|
1533
1540
|
}
|
|
1534
1541
|
function getGlobalIndexPath(host = "opencode") {
|
|
1535
1542
|
switch (host) {
|
|
@@ -1570,6 +1577,9 @@ function resolveGlobalIndexPath(host = "opencode") {
|
|
|
1570
1577
|
return hostIndexPath;
|
|
1571
1578
|
}
|
|
1572
1579
|
if (host !== "opencode") {
|
|
1580
|
+
if (hasHostGlobalConfig(host)) {
|
|
1581
|
+
return hostIndexPath;
|
|
1582
|
+
}
|
|
1573
1583
|
const legacyIndexPath = getGlobalIndexPath("opencode");
|
|
1574
1584
|
if ((0, import_fs4.existsSync)(legacyIndexPath)) {
|
|
1575
1585
|
return legacyIndexPath;
|
|
@@ -1577,55 +1587,55 @@ function resolveGlobalIndexPath(host = "opencode") {
|
|
|
1577
1587
|
}
|
|
1578
1588
|
return hostIndexPath;
|
|
1579
1589
|
}
|
|
1580
|
-
function resolveProjectConfigPath(
|
|
1581
|
-
const hostConfigPath = path4.join(
|
|
1590
|
+
function resolveProjectConfigPath(projectRoot3, host = "opencode") {
|
|
1591
|
+
const hostConfigPath = path4.join(projectRoot3, getProjectConfigRelativePath(host));
|
|
1582
1592
|
if ((0, import_fs4.existsSync)(hostConfigPath)) {
|
|
1583
1593
|
return hostConfigPath;
|
|
1584
1594
|
}
|
|
1585
1595
|
if (host !== "opencode") {
|
|
1586
|
-
const legacyConfigPath = path4.join(
|
|
1596
|
+
const legacyConfigPath = path4.join(projectRoot3, OPENCODE_PROJECT_CONFIG_RELATIVE_PATH);
|
|
1587
1597
|
if ((0, import_fs4.existsSync)(legacyConfigPath)) {
|
|
1588
1598
|
return legacyConfigPath;
|
|
1589
1599
|
}
|
|
1590
1600
|
}
|
|
1591
|
-
const hostFallback = resolveWorktreeFallbackPath(
|
|
1601
|
+
const hostFallback = resolveWorktreeFallbackPath(projectRoot3, getProjectConfigRelativePath(host));
|
|
1592
1602
|
if (hostFallback) {
|
|
1593
1603
|
return hostFallback;
|
|
1594
1604
|
}
|
|
1595
1605
|
if (host !== "opencode") {
|
|
1596
|
-
const legacyFallback = resolveWorktreeFallbackPath(
|
|
1606
|
+
const legacyFallback = resolveWorktreeFallbackPath(projectRoot3, OPENCODE_PROJECT_CONFIG_RELATIVE_PATH);
|
|
1597
1607
|
if (legacyFallback) {
|
|
1598
1608
|
return legacyFallback;
|
|
1599
1609
|
}
|
|
1600
1610
|
}
|
|
1601
1611
|
return hostConfigPath;
|
|
1602
1612
|
}
|
|
1603
|
-
function resolveWritableProjectConfigPath(
|
|
1604
|
-
return path4.join(
|
|
1613
|
+
function resolveWritableProjectConfigPath(projectRoot3, host = "opencode") {
|
|
1614
|
+
return path4.join(projectRoot3, getProjectConfigRelativePath(host));
|
|
1605
1615
|
}
|
|
1606
|
-
function resolveProjectIndexPath(
|
|
1616
|
+
function resolveProjectIndexPath(projectRoot3, scope, host = "opencode") {
|
|
1607
1617
|
if (scope === "global") {
|
|
1608
1618
|
return resolveGlobalIndexPath(host);
|
|
1609
1619
|
}
|
|
1610
|
-
const localIndexPath = path4.join(
|
|
1620
|
+
const localIndexPath = path4.join(projectRoot3, getProjectIndexRelativePath(host));
|
|
1611
1621
|
if ((0, import_fs4.existsSync)(localIndexPath)) {
|
|
1612
1622
|
return localIndexPath;
|
|
1613
1623
|
}
|
|
1614
1624
|
if (host !== "opencode") {
|
|
1615
|
-
const legacyIndexPath = path4.join(
|
|
1616
|
-
if ((0, import_fs4.existsSync)(legacyIndexPath) && !hasHostProjectConfig(
|
|
1625
|
+
const legacyIndexPath = path4.join(projectRoot3, OPENCODE_PROJECT_INDEX_RELATIVE_PATH);
|
|
1626
|
+
if ((0, import_fs4.existsSync)(legacyIndexPath) && !hasHostProjectConfig(projectRoot3, host)) {
|
|
1617
1627
|
return legacyIndexPath;
|
|
1618
1628
|
}
|
|
1619
1629
|
}
|
|
1620
|
-
if (hasHostProjectConfig(
|
|
1630
|
+
if (hasHostProjectConfig(projectRoot3, host)) {
|
|
1621
1631
|
return localIndexPath;
|
|
1622
1632
|
}
|
|
1623
|
-
const hostFallback = resolveWorktreeFallbackPath(
|
|
1633
|
+
const hostFallback = resolveWorktreeFallbackPath(projectRoot3, getProjectIndexRelativePath(host));
|
|
1624
1634
|
if (hostFallback) {
|
|
1625
1635
|
return hostFallback;
|
|
1626
1636
|
}
|
|
1627
1637
|
if (host !== "opencode") {
|
|
1628
|
-
const legacyFallback = resolveWorktreeFallbackPath(
|
|
1638
|
+
const legacyFallback = resolveWorktreeFallbackPath(projectRoot3, OPENCODE_PROJECT_INDEX_RELATIVE_PATH);
|
|
1629
1639
|
if (legacyFallback) {
|
|
1630
1640
|
return legacyFallback;
|
|
1631
1641
|
}
|
|
@@ -1758,14 +1768,14 @@ function loadJsonFile(filePath) {
|
|
|
1758
1768
|
throw new Error(`Failed to load config file ${filePath}: ${message}`);
|
|
1759
1769
|
}
|
|
1760
1770
|
}
|
|
1761
|
-
function materializeLocalProjectConfig(
|
|
1762
|
-
const localConfigPath = resolveWritableProjectConfigPath(
|
|
1771
|
+
function materializeLocalProjectConfig(projectRoot3, config, host = "opencode") {
|
|
1772
|
+
const localConfigPath = resolveWritableProjectConfigPath(projectRoot3, host);
|
|
1763
1773
|
(0, import_fs5.mkdirSync)(path7.dirname(localConfigPath), { recursive: true });
|
|
1764
1774
|
(0, import_fs5.writeFileSync)(localConfigPath, JSON.stringify(config, null, 2), "utf-8");
|
|
1765
1775
|
return localConfigPath;
|
|
1766
1776
|
}
|
|
1767
|
-
function loadProjectConfigLayer(
|
|
1768
|
-
const projectConfigPath = resolveProjectConfigPath(
|
|
1777
|
+
function loadProjectConfigLayer(projectRoot3, host = "opencode") {
|
|
1778
|
+
const projectConfigPath = resolveProjectConfigPath(projectRoot3, host);
|
|
1769
1779
|
const projectConfig = loadJsonFile(projectConfigPath);
|
|
1770
1780
|
if (!projectConfig) {
|
|
1771
1781
|
return {};
|
|
@@ -1776,14 +1786,14 @@ function loadProjectConfigLayer(projectRoot2, host = "opencode") {
|
|
|
1776
1786
|
normalizedConfig.knowledgeBases = resolveInheritedKnowledgeBaseEntries(
|
|
1777
1787
|
normalizedConfig.knowledgeBases,
|
|
1778
1788
|
projectConfigBaseDir,
|
|
1779
|
-
|
|
1789
|
+
projectRoot3
|
|
1780
1790
|
);
|
|
1781
1791
|
}
|
|
1782
1792
|
return normalizedConfig;
|
|
1783
1793
|
}
|
|
1784
|
-
function loadMergedConfig(
|
|
1794
|
+
function loadMergedConfig(projectRoot3, host = "opencode") {
|
|
1785
1795
|
const globalConfigPath = resolveGlobalConfigPath(host);
|
|
1786
|
-
const projectConfigPath = resolveProjectConfigPath(
|
|
1796
|
+
const projectConfigPath = resolveProjectConfigPath(projectRoot3, host);
|
|
1787
1797
|
let globalConfig = null;
|
|
1788
1798
|
let globalConfigError = null;
|
|
1789
1799
|
try {
|
|
@@ -1792,7 +1802,7 @@ function loadMergedConfig(projectRoot2, host = "opencode") {
|
|
|
1792
1802
|
globalConfigError = error instanceof Error ? error : new Error(String(error));
|
|
1793
1803
|
}
|
|
1794
1804
|
const projectConfig = loadJsonFile(projectConfigPath);
|
|
1795
|
-
const normalizedProjectConfig = loadProjectConfigLayer(
|
|
1805
|
+
const normalizedProjectConfig = loadProjectConfigLayer(projectRoot3, host);
|
|
1796
1806
|
if (globalConfigError) {
|
|
1797
1807
|
if (!projectConfig) {
|
|
1798
1808
|
throw globalConfigError;
|
|
@@ -1835,11 +1845,11 @@ function loadMergedConfig(projectRoot2, host = "opencode") {
|
|
|
1835
1845
|
}
|
|
1836
1846
|
|
|
1837
1847
|
// src/indexer/index.ts
|
|
1838
|
-
var
|
|
1839
|
-
var
|
|
1848
|
+
var import_fs8 = require("fs");
|
|
1849
|
+
var path13 = __toESM(require("path"), 1);
|
|
1840
1850
|
var import_perf_hooks = require("perf_hooks");
|
|
1841
|
-
var
|
|
1842
|
-
var
|
|
1851
|
+
var import_child_process3 = require("child_process");
|
|
1852
|
+
var import_util3 = require("util");
|
|
1843
1853
|
|
|
1844
1854
|
// node_modules/eventemitter3/index.mjs
|
|
1845
1855
|
var import_index = __toESM(require_eventemitter3(), 1);
|
|
@@ -2943,17 +2953,17 @@ function validateExternalUrl(urlString) {
|
|
|
2943
2953
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
2944
2954
|
return { valid: false, reason: `Blocked protocol: ${parsed.protocol}` };
|
|
2945
2955
|
}
|
|
2946
|
-
const
|
|
2947
|
-
if (BLOCKED_HOSTNAMES.has(
|
|
2948
|
-
return { valid: false, reason: `Blocked: cloud metadata service (${
|
|
2956
|
+
const hostname2 = parsed.hostname.toLowerCase();
|
|
2957
|
+
if (BLOCKED_HOSTNAMES.has(hostname2)) {
|
|
2958
|
+
return { valid: false, reason: `Blocked: cloud metadata service (${hostname2})` };
|
|
2949
2959
|
}
|
|
2950
2960
|
for (const pattern of BLOCKED_METADATA_IPS) {
|
|
2951
|
-
if (pattern.test(
|
|
2952
|
-
return { valid: false, reason: `Blocked: cloud metadata IP (${
|
|
2961
|
+
if (pattern.test(hostname2)) {
|
|
2962
|
+
return { valid: false, reason: `Blocked: cloud metadata IP (${hostname2})` };
|
|
2953
2963
|
}
|
|
2954
2964
|
}
|
|
2955
|
-
if (/^169\.254\./.test(
|
|
2956
|
-
return { valid: false, reason: `Blocked: link-local address (${
|
|
2965
|
+
if (/^169\.254\./.test(hostname2)) {
|
|
2966
|
+
return { valid: false, reason: `Blocked: link-local address (${hostname2})` };
|
|
2957
2967
|
}
|
|
2958
2968
|
return { valid: true };
|
|
2959
2969
|
}
|
|
@@ -3147,10 +3157,10 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider extends BaseEmbeddi
|
|
|
3147
3157
|
}
|
|
3148
3158
|
const batchResults = await Promise.all(
|
|
3149
3159
|
batches.map(async (batch) => {
|
|
3150
|
-
const requests = batch.map((
|
|
3160
|
+
const requests = batch.map((text3) => ({
|
|
3151
3161
|
model: `models/${this.modelInfo.model}`,
|
|
3152
3162
|
content: {
|
|
3153
|
-
parts: [{ text:
|
|
3163
|
+
parts: [{ text: text3 }]
|
|
3154
3164
|
},
|
|
3155
3165
|
taskType,
|
|
3156
3166
|
outputDimensionality: this.modelInfo.dimensions
|
|
@@ -3173,7 +3183,7 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider extends BaseEmbeddi
|
|
|
3173
3183
|
const data = await response.json();
|
|
3174
3184
|
return {
|
|
3175
3185
|
embeddings: data.embeddings.map((e) => e.values),
|
|
3176
|
-
tokensUsed: batch.reduce((sum,
|
|
3186
|
+
tokensUsed: batch.reduce((sum, text3) => sum + Math.ceil(text3.length / 4), 0)
|
|
3177
3187
|
};
|
|
3178
3188
|
})
|
|
3179
3189
|
);
|
|
@@ -3190,28 +3200,28 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddi
|
|
|
3190
3200
|
constructor(credentials, modelInfo) {
|
|
3191
3201
|
super(credentials, modelInfo);
|
|
3192
3202
|
}
|
|
3193
|
-
estimateTokens(
|
|
3194
|
-
return Math.ceil(
|
|
3203
|
+
estimateTokens(text3) {
|
|
3204
|
+
return Math.ceil(text3.length / 4);
|
|
3195
3205
|
}
|
|
3196
|
-
truncateToCharLimit(
|
|
3197
|
-
if (
|
|
3198
|
-
return
|
|
3206
|
+
truncateToCharLimit(text3, maxChars) {
|
|
3207
|
+
if (text3.length <= maxChars) {
|
|
3208
|
+
return text3;
|
|
3199
3209
|
}
|
|
3200
|
-
return `${
|
|
3210
|
+
return `${text3.slice(0, Math.max(0, maxChars - 17))}
|
|
3201
3211
|
... [truncated]`;
|
|
3202
3212
|
}
|
|
3203
3213
|
isContextLengthError(error) {
|
|
3204
3214
|
const message = (error instanceof Error ? error.message : String(error)).toLowerCase();
|
|
3205
3215
|
return message.includes("context length") && (message.includes("exceed") || message.includes("exceeded") || message.includes("too long")) || message.includes("input length exceeds the context length") || message.includes("context length exceeded");
|
|
3206
3216
|
}
|
|
3207
|
-
buildTruncationCandidates(
|
|
3217
|
+
buildTruncationCandidates(text3) {
|
|
3208
3218
|
const baseMaxChars = Math.max(1, this.modelInfo.maxTokens * 4);
|
|
3209
3219
|
const candidateLimits = /* @__PURE__ */ new Set();
|
|
3210
|
-
const baselineLimit =
|
|
3220
|
+
const baselineLimit = text3.length > baseMaxChars ? baseMaxChars : Math.max(
|
|
3211
3221
|
_OllamaEmbeddingProvider.MIN_TRUNCATION_CHARS,
|
|
3212
|
-
Math.floor(
|
|
3222
|
+
Math.floor(text3.length * 0.9)
|
|
3213
3223
|
);
|
|
3214
|
-
if (baselineLimit <
|
|
3224
|
+
if (baselineLimit < text3.length) {
|
|
3215
3225
|
candidateLimits.add(baselineLimit);
|
|
3216
3226
|
}
|
|
3217
3227
|
for (const factor of [0.75, 0.6, 0.45, 0.35, 0.25]) {
|
|
@@ -3219,19 +3229,19 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddi
|
|
|
3219
3229
|
_OllamaEmbeddingProvider.MIN_TRUNCATION_CHARS,
|
|
3220
3230
|
Math.floor(baselineLimit * factor)
|
|
3221
3231
|
);
|
|
3222
|
-
if (scaledLimit <
|
|
3232
|
+
if (scaledLimit < text3.length) {
|
|
3223
3233
|
candidateLimits.add(scaledLimit);
|
|
3224
3234
|
}
|
|
3225
3235
|
}
|
|
3226
|
-
candidateLimits.add(Math.min(
|
|
3236
|
+
candidateLimits.add(Math.min(text3.length - 1, _OllamaEmbeddingProvider.MIN_TRUNCATION_CHARS));
|
|
3227
3237
|
const candidates = [];
|
|
3228
3238
|
const seen = /* @__PURE__ */ new Set();
|
|
3229
3239
|
for (const limit of [...candidateLimits].sort((a, b) => b - a)) {
|
|
3230
|
-
if (limit <= 0 || limit >=
|
|
3240
|
+
if (limit <= 0 || limit >= text3.length) {
|
|
3231
3241
|
continue;
|
|
3232
3242
|
}
|
|
3233
|
-
const truncated = this.truncateToCharLimit(
|
|
3234
|
-
if (truncated ===
|
|
3243
|
+
const truncated = this.truncateToCharLimit(text3, limit);
|
|
3244
|
+
if (truncated === text3 || seen.has(truncated)) {
|
|
3235
3245
|
continue;
|
|
3236
3246
|
}
|
|
3237
3247
|
seen.add(truncated);
|
|
@@ -3239,15 +3249,15 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddi
|
|
|
3239
3249
|
}
|
|
3240
3250
|
return candidates;
|
|
3241
3251
|
}
|
|
3242
|
-
async embedSingleWithFallback(
|
|
3252
|
+
async embedSingleWithFallback(text3) {
|
|
3243
3253
|
try {
|
|
3244
|
-
return await this.embedSingle(
|
|
3254
|
+
return await this.embedSingle(text3);
|
|
3245
3255
|
} catch (error) {
|
|
3246
3256
|
if (!this.isContextLengthError(error)) {
|
|
3247
3257
|
throw error;
|
|
3248
3258
|
}
|
|
3249
3259
|
let lastError = error;
|
|
3250
|
-
for (const truncated of this.buildTruncationCandidates(
|
|
3260
|
+
for (const truncated of this.buildTruncationCandidates(text3)) {
|
|
3251
3261
|
try {
|
|
3252
3262
|
return await this.embedSingle(truncated);
|
|
3253
3263
|
} catch (retryError) {
|
|
@@ -3260,7 +3270,7 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddi
|
|
|
3260
3270
|
throw lastError;
|
|
3261
3271
|
}
|
|
3262
3272
|
}
|
|
3263
|
-
async embedSingle(
|
|
3273
|
+
async embedSingle(text3) {
|
|
3264
3274
|
const response = await fetch(`${this.credentials.baseUrl}/api/embeddings`, {
|
|
3265
3275
|
method: "POST",
|
|
3266
3276
|
headers: {
|
|
@@ -3268,7 +3278,7 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddi
|
|
|
3268
3278
|
},
|
|
3269
3279
|
body: JSON.stringify({
|
|
3270
3280
|
model: this.modelInfo.model,
|
|
3271
|
-
prompt:
|
|
3281
|
+
prompt: text3,
|
|
3272
3282
|
truncate: false
|
|
3273
3283
|
})
|
|
3274
3284
|
});
|
|
@@ -3279,13 +3289,13 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddi
|
|
|
3279
3289
|
const data = await response.json();
|
|
3280
3290
|
return {
|
|
3281
3291
|
embedding: data.embedding,
|
|
3282
|
-
tokensUsed: this.estimateTokens(
|
|
3292
|
+
tokensUsed: this.estimateTokens(text3)
|
|
3283
3293
|
};
|
|
3284
3294
|
}
|
|
3285
3295
|
async embedBatch(texts) {
|
|
3286
3296
|
const results = [];
|
|
3287
|
-
for (const
|
|
3288
|
-
results.push(await this.embedSingleWithFallback(
|
|
3297
|
+
for (const text3 of texts) {
|
|
3298
|
+
results.push(await this.embedSingleWithFallback(text3));
|
|
3289
3299
|
}
|
|
3290
3300
|
return {
|
|
3291
3301
|
embeddings: results.map((r) => r.embedding),
|
|
@@ -3426,7 +3436,7 @@ var SiliconFlowReranker = class {
|
|
|
3426
3436
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
3427
3437
|
var import_fs6 = require("fs");
|
|
3428
3438
|
var path8 = __toESM(require("path"), 1);
|
|
3429
|
-
function createIgnoreFilter(
|
|
3439
|
+
function createIgnoreFilter(projectRoot3) {
|
|
3430
3440
|
const ig = (0, import_ignore.default)();
|
|
3431
3441
|
const defaultIgnores = [
|
|
3432
3442
|
"node_modules",
|
|
@@ -3447,7 +3457,7 @@ function createIgnoreFilter(projectRoot2) {
|
|
|
3447
3457
|
"**/*build*/**"
|
|
3448
3458
|
];
|
|
3449
3459
|
ig.add(defaultIgnores);
|
|
3450
|
-
const gitignorePath = path8.join(
|
|
3460
|
+
const gitignorePath = path8.join(projectRoot3, ".gitignore");
|
|
3451
3461
|
if ((0, import_fs6.existsSync)(gitignorePath)) {
|
|
3452
3462
|
const gitignoreContent = (0, import_fs6.readFileSync)(gitignorePath, "utf-8");
|
|
3453
3463
|
ig.add(gitignoreContent);
|
|
@@ -3469,13 +3479,13 @@ function matchGlob(filePath, pattern) {
|
|
|
3469
3479
|
const regex = new RegExp(`^${regexPattern}$`);
|
|
3470
3480
|
return regex.test(filePath);
|
|
3471
3481
|
}
|
|
3472
|
-
async function* walkDirectory(dir,
|
|
3482
|
+
async function* walkDirectory(dir, projectRoot3, includePatterns, excludePatterns, ignoreFilter, maxFileSize, skipped, options, currentDepth = 0) {
|
|
3473
3483
|
const entries = await import_fs6.promises.readdir(dir, { withFileTypes: true });
|
|
3474
3484
|
const filesInDir = [];
|
|
3475
3485
|
const subdirs = [];
|
|
3476
3486
|
for (const entry of entries) {
|
|
3477
3487
|
const fullPath = path8.join(dir, entry.name);
|
|
3478
|
-
const relativePath = path8.relative(
|
|
3488
|
+
const relativePath = path8.relative(projectRoot3, fullPath);
|
|
3479
3489
|
if (isHiddenPathSegment(entry.name)) {
|
|
3480
3490
|
if (entry.isDirectory()) {
|
|
3481
3491
|
skipped.push({ path: relativePath, reason: "excluded" });
|
|
@@ -3524,14 +3534,14 @@ async function* walkDirectory(dir, projectRoot2, includePatterns, excludePattern
|
|
|
3524
3534
|
yield f;
|
|
3525
3535
|
}
|
|
3526
3536
|
for (let i = options.maxFilesPerDirectory; i < filesInDir.length; i++) {
|
|
3527
|
-
skipped.push({ path: path8.relative(
|
|
3537
|
+
skipped.push({ path: path8.relative(projectRoot3, filesInDir[i].path), reason: "excluded" });
|
|
3528
3538
|
}
|
|
3529
3539
|
const canRecurse = options.maxDepth === -1 || currentDepth < options.maxDepth;
|
|
3530
3540
|
if (canRecurse) {
|
|
3531
3541
|
for (const sub of subdirs) {
|
|
3532
3542
|
yield* walkDirectory(
|
|
3533
3543
|
sub.fullPath,
|
|
3534
|
-
|
|
3544
|
+
projectRoot3,
|
|
3535
3545
|
includePatterns,
|
|
3536
3546
|
excludePatterns,
|
|
3537
3547
|
ignoreFilter,
|
|
@@ -3543,14 +3553,14 @@ async function* walkDirectory(dir, projectRoot2, includePatterns, excludePattern
|
|
|
3543
3553
|
}
|
|
3544
3554
|
}
|
|
3545
3555
|
}
|
|
3546
|
-
async function collectFiles(
|
|
3556
|
+
async function collectFiles(projectRoot3, includePatterns, excludePatterns, maxFileSize, additionalRoots, walkOptions) {
|
|
3547
3557
|
const opts = walkOptions ?? { maxDepth: 5, maxFilesPerDirectory: 100 };
|
|
3548
|
-
const ignoreFilter = createIgnoreFilter(
|
|
3558
|
+
const ignoreFilter = createIgnoreFilter(projectRoot3);
|
|
3549
3559
|
const files = [];
|
|
3550
3560
|
const skipped = [];
|
|
3551
3561
|
for await (const file of walkDirectory(
|
|
3552
|
-
|
|
3553
|
-
|
|
3562
|
+
projectRoot3,
|
|
3563
|
+
projectRoot3,
|
|
3554
3564
|
includePatterns,
|
|
3555
3565
|
excludePatterns,
|
|
3556
3566
|
ignoreFilter,
|
|
@@ -3565,7 +3575,7 @@ async function collectFiles(projectRoot2, includePatterns, excludePatterns, maxF
|
|
|
3565
3575
|
const normalizedRoots = /* @__PURE__ */ new Set();
|
|
3566
3576
|
for (const kbRoot of additionalRoots) {
|
|
3567
3577
|
const resolved = path8.normalize(
|
|
3568
|
-
path8.isAbsolute(kbRoot) ? kbRoot : path8.resolve(
|
|
3578
|
+
path8.isAbsolute(kbRoot) ? kbRoot : path8.resolve(projectRoot3, kbRoot)
|
|
3569
3579
|
);
|
|
3570
3580
|
normalizedRoots.add(resolved);
|
|
3571
3581
|
}
|
|
@@ -3991,6 +4001,12 @@ function createMockNativeBinding() {
|
|
|
3991
4001
|
constructor() {
|
|
3992
4002
|
throw error;
|
|
3993
4003
|
}
|
|
4004
|
+
static openReadOnly() {
|
|
4005
|
+
throw error;
|
|
4006
|
+
}
|
|
4007
|
+
static createEmptyReadOnly() {
|
|
4008
|
+
throw error;
|
|
4009
|
+
}
|
|
3994
4010
|
close() {
|
|
3995
4011
|
throw error;
|
|
3996
4012
|
}
|
|
@@ -4094,6 +4110,12 @@ var VectorStore = class {
|
|
|
4094
4110
|
load() {
|
|
4095
4111
|
this.inner.load();
|
|
4096
4112
|
}
|
|
4113
|
+
loadStrict() {
|
|
4114
|
+
this.inner.loadStrict();
|
|
4115
|
+
}
|
|
4116
|
+
hasFingerprint() {
|
|
4117
|
+
return this.inner.hasFingerprint();
|
|
4118
|
+
}
|
|
4097
4119
|
count() {
|
|
4098
4120
|
return this.inner.count();
|
|
4099
4121
|
}
|
|
@@ -4132,8 +4154,8 @@ var VectorStore = class {
|
|
|
4132
4154
|
var CHARS_PER_TOKEN = 4;
|
|
4133
4155
|
var MAX_BATCH_TOKENS = 7500;
|
|
4134
4156
|
var MAX_SINGLE_CHUNK_TOKENS = 2e3;
|
|
4135
|
-
function estimateTokens(
|
|
4136
|
-
return Math.ceil(
|
|
4157
|
+
function estimateTokens(text3) {
|
|
4158
|
+
return Math.ceil(text3.length / CHARS_PER_TOKEN);
|
|
4137
4159
|
}
|
|
4138
4160
|
function getEmbeddingHeaderParts(chunk, filePath) {
|
|
4139
4161
|
const parts = [];
|
|
@@ -4376,12 +4398,24 @@ var InvertedIndex = class {
|
|
|
4376
4398
|
return this.inner.documentCount();
|
|
4377
4399
|
}
|
|
4378
4400
|
};
|
|
4379
|
-
var Database = class {
|
|
4401
|
+
var Database = class _Database {
|
|
4380
4402
|
inner;
|
|
4381
4403
|
closed = false;
|
|
4382
4404
|
constructor(dbPath) {
|
|
4383
4405
|
this.inner = new native.Database(dbPath);
|
|
4384
4406
|
}
|
|
4407
|
+
static fromNative(inner) {
|
|
4408
|
+
const database = Object.create(_Database.prototype);
|
|
4409
|
+
database.inner = inner;
|
|
4410
|
+
database.closed = false;
|
|
4411
|
+
return database;
|
|
4412
|
+
}
|
|
4413
|
+
static openReadOnly(dbPath) {
|
|
4414
|
+
return _Database.fromNative(native.Database.openReadOnly(dbPath));
|
|
4415
|
+
}
|
|
4416
|
+
static createEmptyReadOnly() {
|
|
4417
|
+
return _Database.fromNative(native.Database.createEmptyReadOnly());
|
|
4418
|
+
}
|
|
4385
4419
|
throwIfClosed() {
|
|
4386
4420
|
if (this.closed) {
|
|
4387
4421
|
throw new Error("Database is closed");
|
|
@@ -4667,20 +4701,20 @@ function getErrorMessage(error) {
|
|
|
4667
4701
|
return String(error);
|
|
4668
4702
|
}
|
|
4669
4703
|
async function getChangedFiles(opts) {
|
|
4670
|
-
const { pr, branch, projectRoot:
|
|
4704
|
+
const { pr, branch, projectRoot: projectRoot3, baseBranch = "main" } = opts;
|
|
4671
4705
|
if (pr !== void 0) {
|
|
4672
|
-
return getChangedFilesForPr(pr,
|
|
4706
|
+
return getChangedFilesForPr(pr, projectRoot3, baseBranch);
|
|
4673
4707
|
}
|
|
4674
|
-
return getChangedFilesForBranch(branch,
|
|
4708
|
+
return getChangedFilesForBranch(branch, projectRoot3, baseBranch);
|
|
4675
4709
|
}
|
|
4676
|
-
async function getChangedFilesForPr(pr,
|
|
4710
|
+
async function getChangedFilesForPr(pr, projectRoot3, baseBranch) {
|
|
4677
4711
|
let headRefName;
|
|
4678
4712
|
let actualBaseBranch = baseBranch;
|
|
4679
4713
|
try {
|
|
4680
4714
|
const { stdout } = await execFileAsync(
|
|
4681
4715
|
"gh",
|
|
4682
4716
|
["pr", "view", String(pr), "--json", "headRefName,baseRefName,files"],
|
|
4683
|
-
{ cwd:
|
|
4717
|
+
{ cwd: projectRoot3, timeout: 3e4 }
|
|
4684
4718
|
);
|
|
4685
4719
|
const data = JSON.parse(stdout);
|
|
4686
4720
|
headRefName = data.headRefName;
|
|
@@ -4689,7 +4723,7 @@ async function getChangedFilesForPr(pr, projectRoot2, baseBranch) {
|
|
|
4689
4723
|
return {
|
|
4690
4724
|
files: normalizeFiles(
|
|
4691
4725
|
data.files.map((f) => f.path),
|
|
4692
|
-
|
|
4726
|
+
projectRoot3
|
|
4693
4727
|
),
|
|
4694
4728
|
baseBranch: actualBaseBranch,
|
|
4695
4729
|
source: "gh",
|
|
@@ -4706,49 +4740,49 @@ async function getChangedFilesForPr(pr, projectRoot2, baseBranch) {
|
|
|
4706
4740
|
`PR #${pr} returned no usable branch or file information.`
|
|
4707
4741
|
);
|
|
4708
4742
|
}
|
|
4709
|
-
const result = await getChangedFilesForBranch(headRefName,
|
|
4743
|
+
const result = await getChangedFilesForBranch(headRefName, projectRoot3, actualBaseBranch);
|
|
4710
4744
|
return { ...result, headRefName };
|
|
4711
4745
|
}
|
|
4712
|
-
async function getChangedFilesForBranch(branch,
|
|
4713
|
-
const targetBranch = branch || await getCurrentBranch2(
|
|
4714
|
-
const mergeBase = await getMergeBase(
|
|
4746
|
+
async function getChangedFilesForBranch(branch, projectRoot3, baseBranch) {
|
|
4747
|
+
const targetBranch = branch || await getCurrentBranch2(projectRoot3);
|
|
4748
|
+
const mergeBase = await getMergeBase(projectRoot3, baseBranch, targetBranch);
|
|
4715
4749
|
const { stdout } = await execFileAsync(
|
|
4716
4750
|
"git",
|
|
4717
4751
|
["diff", "--name-only", `${mergeBase}...${targetBranch}`],
|
|
4718
|
-
{ cwd:
|
|
4752
|
+
{ cwd: projectRoot3, timeout: 3e4 }
|
|
4719
4753
|
);
|
|
4720
4754
|
return {
|
|
4721
|
-
files: normalizeFiles(stdout.split("\n"),
|
|
4755
|
+
files: normalizeFiles(stdout.split("\n"), projectRoot3),
|
|
4722
4756
|
baseBranch,
|
|
4723
4757
|
source: "git",
|
|
4724
4758
|
headRefName: targetBranch
|
|
4725
4759
|
};
|
|
4726
4760
|
}
|
|
4727
|
-
async function getCurrentBranch2(
|
|
4761
|
+
async function getCurrentBranch2(projectRoot3) {
|
|
4728
4762
|
const { stdout } = await execFileAsync(
|
|
4729
4763
|
"git",
|
|
4730
4764
|
["branch", "--show-current"],
|
|
4731
|
-
{ cwd:
|
|
4765
|
+
{ cwd: projectRoot3, timeout: 3e4 }
|
|
4732
4766
|
);
|
|
4733
4767
|
return stdout.trim() || "HEAD";
|
|
4734
4768
|
}
|
|
4735
|
-
async function getMergeBase(
|
|
4769
|
+
async function getMergeBase(projectRoot3, baseBranch, branch) {
|
|
4736
4770
|
const { stdout } = await execFileAsync(
|
|
4737
4771
|
"git",
|
|
4738
4772
|
["merge-base", baseBranch, branch],
|
|
4739
|
-
{ cwd:
|
|
4773
|
+
{ cwd: projectRoot3, timeout: 3e4 }
|
|
4740
4774
|
);
|
|
4741
4775
|
return stdout.trim();
|
|
4742
4776
|
}
|
|
4743
|
-
function normalizeFiles(rawFiles,
|
|
4777
|
+
function normalizeFiles(rawFiles, projectRoot3) {
|
|
4744
4778
|
const seen = /* @__PURE__ */ new Set();
|
|
4745
4779
|
const result = [];
|
|
4746
4780
|
for (const raw of rawFiles) {
|
|
4747
4781
|
const trimmed = raw.trim();
|
|
4748
4782
|
if (!trimmed) continue;
|
|
4749
|
-
const absolute = path10.resolve(
|
|
4750
|
-
const
|
|
4751
|
-
const cleaned =
|
|
4783
|
+
const absolute = path10.resolve(projectRoot3, trimmed);
|
|
4784
|
+
const relative7 = path10.relative(projectRoot3, absolute);
|
|
4785
|
+
const cleaned = relative7.startsWith("./") ? relative7.slice(2) : relative7;
|
|
4752
4786
|
if (!seen.has(cleaned)) {
|
|
4753
4787
|
seen.add(cleaned);
|
|
4754
4788
|
result.push(cleaned);
|
|
@@ -4757,8 +4791,473 @@ function normalizeFiles(rawFiles, projectRoot2) {
|
|
|
4757
4791
|
return result;
|
|
4758
4792
|
}
|
|
4759
4793
|
|
|
4794
|
+
// src/indexer/git-blame.ts
|
|
4795
|
+
var import_child_process2 = require("child_process");
|
|
4796
|
+
var path11 = __toESM(require("path"), 1);
|
|
4797
|
+
var import_util2 = require("util");
|
|
4798
|
+
var execFileAsync2 = (0, import_util2.promisify)(import_child_process2.execFile);
|
|
4799
|
+
function parseGitBlamePorcelain(output) {
|
|
4800
|
+
const commits = /* @__PURE__ */ new Map();
|
|
4801
|
+
let current;
|
|
4802
|
+
for (const line of output.split("\n")) {
|
|
4803
|
+
if (/^[0-9a-f]{40} /.test(line)) {
|
|
4804
|
+
const sha = line.slice(0, 40);
|
|
4805
|
+
current = commits.get(sha) ?? {
|
|
4806
|
+
sha,
|
|
4807
|
+
author: "",
|
|
4808
|
+
authorEmail: "",
|
|
4809
|
+
committedAt: 0,
|
|
4810
|
+
summary: "",
|
|
4811
|
+
lines: 0
|
|
4812
|
+
};
|
|
4813
|
+
commits.set(sha, current);
|
|
4814
|
+
continue;
|
|
4815
|
+
}
|
|
4816
|
+
if (!current) {
|
|
4817
|
+
continue;
|
|
4818
|
+
}
|
|
4819
|
+
if (line.startsWith("author ")) {
|
|
4820
|
+
current.author = line.slice("author ".length);
|
|
4821
|
+
} else if (line.startsWith("author-mail ")) {
|
|
4822
|
+
current.authorEmail = line.slice("author-mail ".length).replace(/^<|>$/g, "");
|
|
4823
|
+
} else if (line.startsWith("author-time ")) {
|
|
4824
|
+
current.committedAt = Number.parseInt(line.slice("author-time ".length), 10);
|
|
4825
|
+
} else if (line.startsWith("summary ")) {
|
|
4826
|
+
current.summary = line.slice("summary ".length);
|
|
4827
|
+
} else if (line.startsWith(" ")) {
|
|
4828
|
+
current.lines += 1;
|
|
4829
|
+
}
|
|
4830
|
+
}
|
|
4831
|
+
return Array.from(commits.values()).filter((commit) => commit.lines > 0).sort((a, b) => b.lines - a.lines || b.committedAt - a.committedAt)[0];
|
|
4832
|
+
}
|
|
4833
|
+
async function getChunkGitBlame(projectRoot3, filePath, startLine, endLine) {
|
|
4834
|
+
const relativePath = path11.relative(projectRoot3, filePath);
|
|
4835
|
+
try {
|
|
4836
|
+
const { stdout } = await execFileAsync2(
|
|
4837
|
+
"git",
|
|
4838
|
+
["blame", "--line-porcelain", "-L", `${startLine},${endLine}`, "--", relativePath],
|
|
4839
|
+
{ cwd: projectRoot3, timeout: 3e4 }
|
|
4840
|
+
);
|
|
4841
|
+
return parseGitBlamePorcelain(stdout);
|
|
4842
|
+
} catch {
|
|
4843
|
+
return void 0;
|
|
4844
|
+
}
|
|
4845
|
+
}
|
|
4846
|
+
|
|
4847
|
+
// src/indexer/index-lock.ts
|
|
4848
|
+
var import_crypto = require("crypto");
|
|
4849
|
+
var import_fs7 = require("fs");
|
|
4850
|
+
var os4 = __toESM(require("os"), 1);
|
|
4851
|
+
var path12 = __toESM(require("path"), 1);
|
|
4852
|
+
var OWNER_FILE_NAME = "owner.json";
|
|
4853
|
+
var RECLAIM_DIRECTORY_NAME = "reclaiming";
|
|
4854
|
+
var RECOVERY_MARKER_PREFIX = "indexing.lock.recovery.";
|
|
4855
|
+
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
4856
|
+
var VALID_OPERATIONS = /* @__PURE__ */ new Set([
|
|
4857
|
+
"initialize",
|
|
4858
|
+
"index",
|
|
4859
|
+
"force-index",
|
|
4860
|
+
"clear",
|
|
4861
|
+
"health-check",
|
|
4862
|
+
"retry-failed-batches",
|
|
4863
|
+
"recovery"
|
|
4864
|
+
]);
|
|
4865
|
+
var temporaryCounter = 0;
|
|
4866
|
+
function getErrorCode(error) {
|
|
4867
|
+
return typeof error === "object" && error !== null && "code" in error ? String(error.code) : void 0;
|
|
4868
|
+
}
|
|
4869
|
+
function retryTransientFilesystemOperation(operation) {
|
|
4870
|
+
let lastError;
|
|
4871
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
4872
|
+
try {
|
|
4873
|
+
operation();
|
|
4874
|
+
return;
|
|
4875
|
+
} catch (error) {
|
|
4876
|
+
lastError = error;
|
|
4877
|
+
const code = getErrorCode(error);
|
|
4878
|
+
if (code !== "EBUSY" && code !== "EPERM") throw error;
|
|
4879
|
+
if (attempt < 2) {
|
|
4880
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, (attempt + 1) * 10);
|
|
4881
|
+
}
|
|
4882
|
+
}
|
|
4883
|
+
}
|
|
4884
|
+
throw lastError;
|
|
4885
|
+
}
|
|
4886
|
+
function parseOwner(value) {
|
|
4887
|
+
if (typeof value !== "object" || value === null) return null;
|
|
4888
|
+
const candidate = value;
|
|
4889
|
+
if (!Number.isInteger(candidate.pid) || (candidate.pid ?? 0) <= 0) return null;
|
|
4890
|
+
if (typeof candidate.hostname !== "string" || candidate.hostname.length === 0) return null;
|
|
4891
|
+
if (typeof candidate.startedAt !== "string" || Number.isNaN(Date.parse(candidate.startedAt))) return null;
|
|
4892
|
+
if (typeof candidate.operation !== "string" || !VALID_OPERATIONS.has(candidate.operation)) return null;
|
|
4893
|
+
if (typeof candidate.token !== "string" || !UUID_PATTERN.test(candidate.token)) return null;
|
|
4894
|
+
return candidate;
|
|
4895
|
+
}
|
|
4896
|
+
function parseReclaimOwner(value) {
|
|
4897
|
+
if (typeof value !== "object" || value === null) return null;
|
|
4898
|
+
const candidate = value;
|
|
4899
|
+
if (!Number.isInteger(candidate.pid) || (candidate.pid ?? 0) <= 0) return null;
|
|
4900
|
+
if (typeof candidate.hostname !== "string" || candidate.hostname.length === 0) return null;
|
|
4901
|
+
if (typeof candidate.startedAt !== "string" || Number.isNaN(Date.parse(candidate.startedAt))) return null;
|
|
4902
|
+
if (typeof candidate.token !== "string" || !UUID_PATTERN.test(candidate.token)) return null;
|
|
4903
|
+
if (typeof candidate.expectedOwnerToken !== "string" || !UUID_PATTERN.test(candidate.expectedOwnerToken)) return null;
|
|
4904
|
+
return candidate;
|
|
4905
|
+
}
|
|
4906
|
+
function readJsonDirectory(directoryPath, parser) {
|
|
4907
|
+
try {
|
|
4908
|
+
return parser(JSON.parse((0, import_fs7.readFileSync)(path12.join(directoryPath, OWNER_FILE_NAME), "utf-8")));
|
|
4909
|
+
} catch {
|
|
4910
|
+
return null;
|
|
4911
|
+
}
|
|
4912
|
+
}
|
|
4913
|
+
function readDirectoryOwner(lockPath) {
|
|
4914
|
+
return readJsonDirectory(lockPath, parseOwner);
|
|
4915
|
+
}
|
|
4916
|
+
function readReclaimOwner(markerPath) {
|
|
4917
|
+
return readJsonDirectory(markerPath, parseReclaimOwner);
|
|
4918
|
+
}
|
|
4919
|
+
function readRecoveryOwner(markerPath) {
|
|
4920
|
+
return readDirectoryOwner(markerPath);
|
|
4921
|
+
}
|
|
4922
|
+
function readLegacyOwner(lockPath) {
|
|
4923
|
+
try {
|
|
4924
|
+
const parsed = JSON.parse((0, import_fs7.readFileSync)(lockPath, "utf-8"));
|
|
4925
|
+
if (!Number.isInteger(parsed.pid) || Number(parsed.pid) <= 0) return null;
|
|
4926
|
+
if (typeof parsed.startedAt !== "string" || Number.isNaN(Date.parse(parsed.startedAt))) return null;
|
|
4927
|
+
return {
|
|
4928
|
+
pid: Number(parsed.pid),
|
|
4929
|
+
hostname: typeof parsed.hostname === "string" ? parsed.hostname : os4.hostname(),
|
|
4930
|
+
startedAt: parsed.startedAt,
|
|
4931
|
+
operation: typeof parsed.operation === "string" && VALID_OPERATIONS.has(parsed.operation) ? parsed.operation : "index",
|
|
4932
|
+
token: typeof parsed.token === "string" ? parsed.token : "legacy-v0.14.0"
|
|
4933
|
+
};
|
|
4934
|
+
} catch {
|
|
4935
|
+
return null;
|
|
4936
|
+
}
|
|
4937
|
+
}
|
|
4938
|
+
function getOwnerLiveness(owner) {
|
|
4939
|
+
if (owner.hostname !== os4.hostname()) return "unknown";
|
|
4940
|
+
try {
|
|
4941
|
+
process.kill(owner.pid, 0);
|
|
4942
|
+
return "alive";
|
|
4943
|
+
} catch (error) {
|
|
4944
|
+
const code = getErrorCode(error);
|
|
4945
|
+
if (code === "ESRCH") return "dead";
|
|
4946
|
+
if (code === "EPERM") return "alive";
|
|
4947
|
+
return "unknown";
|
|
4948
|
+
}
|
|
4949
|
+
}
|
|
4950
|
+
function sameOwner(left, right) {
|
|
4951
|
+
return left.pid === right.pid && left.hostname === right.hostname && left.token === right.token;
|
|
4952
|
+
}
|
|
4953
|
+
function sameReclaimOwner(left, right) {
|
|
4954
|
+
return left.pid === right.pid && left.hostname === right.hostname && left.token === right.token && left.expectedOwnerToken === right.expectedOwnerToken;
|
|
4955
|
+
}
|
|
4956
|
+
function publishJsonDirectory(finalPath, value) {
|
|
4957
|
+
const candidatePath = `${finalPath}.candidate.${process.pid}.${(0, import_crypto.randomUUID)()}`;
|
|
4958
|
+
(0, import_fs7.mkdirSync)(candidatePath, { mode: 448 });
|
|
4959
|
+
try {
|
|
4960
|
+
(0, import_fs7.writeFileSync)(path12.join(candidatePath, OWNER_FILE_NAME), JSON.stringify(value), {
|
|
4961
|
+
encoding: "utf-8",
|
|
4962
|
+
flag: "wx",
|
|
4963
|
+
mode: 384
|
|
4964
|
+
});
|
|
4965
|
+
if ((0, import_fs7.existsSync)(finalPath)) return false;
|
|
4966
|
+
try {
|
|
4967
|
+
(0, import_fs7.renameSync)(candidatePath, finalPath);
|
|
4968
|
+
return true;
|
|
4969
|
+
} catch (error) {
|
|
4970
|
+
if ((0, import_fs7.existsSync)(finalPath)) return false;
|
|
4971
|
+
throw error;
|
|
4972
|
+
}
|
|
4973
|
+
} finally {
|
|
4974
|
+
if ((0, import_fs7.existsSync)(candidatePath)) (0, import_fs7.rmSync)(candidatePath, { recursive: true, force: true });
|
|
4975
|
+
}
|
|
4976
|
+
}
|
|
4977
|
+
function createOwner(operation) {
|
|
4978
|
+
return {
|
|
4979
|
+
pid: process.pid,
|
|
4980
|
+
hostname: os4.hostname(),
|
|
4981
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4982
|
+
operation,
|
|
4983
|
+
token: (0, import_crypto.randomUUID)()
|
|
4984
|
+
};
|
|
4985
|
+
}
|
|
4986
|
+
function recoveryMarkerPath(indexPath, owner) {
|
|
4987
|
+
return path12.join(indexPath, `${RECOVERY_MARKER_PREFIX}${owner.token}`);
|
|
4988
|
+
}
|
|
4989
|
+
function publishRecoveryMarker(indexPath, owner) {
|
|
4990
|
+
const markerPath = recoveryMarkerPath(indexPath, owner);
|
|
4991
|
+
if (!publishJsonDirectory(markerPath, owner)) {
|
|
4992
|
+
const markerOwner = readRecoveryOwner(markerPath);
|
|
4993
|
+
if (!markerOwner || !sameOwner(markerOwner, owner)) {
|
|
4994
|
+
throw new IndexLockContentionError(markerPath, markerOwner, "unknown-owner");
|
|
4995
|
+
}
|
|
4996
|
+
}
|
|
4997
|
+
return markerPath;
|
|
4998
|
+
}
|
|
4999
|
+
function getPendingRecoveries(indexPath) {
|
|
5000
|
+
const recoveries = [];
|
|
5001
|
+
const markerNames = (0, import_fs7.readdirSync)(indexPath).filter((name) => {
|
|
5002
|
+
if (!name.startsWith(RECOVERY_MARKER_PREFIX)) return false;
|
|
5003
|
+
return UUID_PATTERN.test(name.slice(RECOVERY_MARKER_PREFIX.length));
|
|
5004
|
+
}).sort();
|
|
5005
|
+
for (const markerName of markerNames) {
|
|
5006
|
+
const markerPath = path12.join(indexPath, markerName);
|
|
5007
|
+
const markerToken = markerName.slice(RECOVERY_MARKER_PREFIX.length);
|
|
5008
|
+
let markerStats;
|
|
5009
|
+
try {
|
|
5010
|
+
markerStats = (0, import_fs7.lstatSync)(markerPath);
|
|
5011
|
+
} catch (error) {
|
|
5012
|
+
if (getErrorCode(error) === "ENOENT") continue;
|
|
5013
|
+
throw error;
|
|
5014
|
+
}
|
|
5015
|
+
if (!markerStats.isDirectory()) {
|
|
5016
|
+
throw new IndexLockContentionError(markerPath, null, "unknown-owner");
|
|
5017
|
+
}
|
|
5018
|
+
const owner = readRecoveryOwner(markerPath);
|
|
5019
|
+
if (!owner || owner.token !== markerToken || getOwnerLiveness(owner) !== "dead") {
|
|
5020
|
+
throw new IndexLockContentionError(markerPath, owner, "unknown-owner");
|
|
5021
|
+
}
|
|
5022
|
+
recoveries.push({ owner, markerPath });
|
|
5023
|
+
}
|
|
5024
|
+
recoveries.sort((left, right) => {
|
|
5025
|
+
const byTimestamp = left.owner.startedAt.localeCompare(right.owner.startedAt);
|
|
5026
|
+
return byTimestamp !== 0 ? byTimestamp : left.markerPath.localeCompare(right.markerPath);
|
|
5027
|
+
});
|
|
5028
|
+
return recoveries;
|
|
5029
|
+
}
|
|
5030
|
+
function cleanupDeadPublicationCandidates(indexPath) {
|
|
5031
|
+
const candidatePattern = /^indexing\.lock(?:\.recovery\.[0-9a-f-]{36})?\.candidate\.(\d+)\.[0-9a-f-]{36}$/i;
|
|
5032
|
+
for (const entry of (0, import_fs7.readdirSync)(indexPath)) {
|
|
5033
|
+
const match = candidatePattern.exec(entry);
|
|
5034
|
+
if (!match) continue;
|
|
5035
|
+
const pid = Number(match[1]);
|
|
5036
|
+
if (!Number.isInteger(pid) || pid <= 0) continue;
|
|
5037
|
+
if (getOwnerLiveness({ pid, hostname: os4.hostname() }) !== "dead") continue;
|
|
5038
|
+
(0, import_fs7.rmSync)(path12.join(indexPath, entry), { recursive: true, force: true });
|
|
5039
|
+
}
|
|
5040
|
+
}
|
|
5041
|
+
function removeDeadReclaimMarker(lockPath, expectedOwner) {
|
|
5042
|
+
const markerPath = path12.join(lockPath, RECLAIM_DIRECTORY_NAME);
|
|
5043
|
+
const marker = readReclaimOwner(markerPath);
|
|
5044
|
+
if (!marker || marker.expectedOwnerToken !== expectedOwner.token || getOwnerLiveness(marker) !== "dead") {
|
|
5045
|
+
return false;
|
|
5046
|
+
}
|
|
5047
|
+
const currentOwner = readDirectoryOwner(lockPath);
|
|
5048
|
+
if (!currentOwner || !sameOwner(currentOwner, expectedOwner) || getOwnerLiveness(currentOwner) !== "dead") {
|
|
5049
|
+
return false;
|
|
5050
|
+
}
|
|
5051
|
+
const claimedMarkerPath = `${markerPath}.stale.${marker.pid}.${marker.token}.${(0, import_crypto.randomUUID)()}`;
|
|
5052
|
+
try {
|
|
5053
|
+
(0, import_fs7.renameSync)(markerPath, claimedMarkerPath);
|
|
5054
|
+
} catch (error) {
|
|
5055
|
+
if (getErrorCode(error) === "ENOENT") return false;
|
|
5056
|
+
throw error;
|
|
5057
|
+
}
|
|
5058
|
+
const claimedMarker = readReclaimOwner(claimedMarkerPath);
|
|
5059
|
+
const ownerAfterClaim = readDirectoryOwner(lockPath);
|
|
5060
|
+
if (!claimedMarker || !sameReclaimOwner(claimedMarker, marker) || !ownerAfterClaim || !sameOwner(ownerAfterClaim, expectedOwner) || getOwnerLiveness(ownerAfterClaim) !== "dead") {
|
|
5061
|
+
if (!(0, import_fs7.existsSync)(markerPath) && (0, import_fs7.existsSync)(claimedMarkerPath)) {
|
|
5062
|
+
(0, import_fs7.renameSync)(claimedMarkerPath, markerPath);
|
|
5063
|
+
}
|
|
5064
|
+
return false;
|
|
5065
|
+
}
|
|
5066
|
+
(0, import_fs7.rmSync)(claimedMarkerPath, { recursive: true, force: true });
|
|
5067
|
+
return true;
|
|
5068
|
+
}
|
|
5069
|
+
function reclaimDeadOwner(indexPath, lockPath, expectedOwner) {
|
|
5070
|
+
const reclaimPath = path12.join(lockPath, RECLAIM_DIRECTORY_NAME);
|
|
5071
|
+
const reclaimOwner = {
|
|
5072
|
+
pid: process.pid,
|
|
5073
|
+
hostname: os4.hostname(),
|
|
5074
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5075
|
+
token: (0, import_crypto.randomUUID)(),
|
|
5076
|
+
expectedOwnerToken: expectedOwner.token
|
|
5077
|
+
};
|
|
5078
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
5079
|
+
if (publishJsonDirectory(reclaimPath, reclaimOwner)) break;
|
|
5080
|
+
if (attempt === 0 && removeDeadReclaimMarker(lockPath, expectedOwner)) continue;
|
|
5081
|
+
return false;
|
|
5082
|
+
}
|
|
5083
|
+
try {
|
|
5084
|
+
const currentReclaimer = readReclaimOwner(reclaimPath);
|
|
5085
|
+
const currentOwner = readDirectoryOwner(lockPath);
|
|
5086
|
+
if (!currentReclaimer || !sameReclaimOwner(currentReclaimer, reclaimOwner) || !currentOwner || !sameOwner(currentOwner, expectedOwner) || getOwnerLiveness(currentOwner) !== "dead") {
|
|
5087
|
+
return false;
|
|
5088
|
+
}
|
|
5089
|
+
publishRecoveryMarker(indexPath, expectedOwner);
|
|
5090
|
+
const ownerBeforeQuarantine = readDirectoryOwner(lockPath);
|
|
5091
|
+
const reclaimerBeforeQuarantine = readReclaimOwner(reclaimPath);
|
|
5092
|
+
if (!ownerBeforeQuarantine || !sameOwner(ownerBeforeQuarantine, expectedOwner) || getOwnerLiveness(ownerBeforeQuarantine) !== "dead" || !reclaimerBeforeQuarantine || !sameReclaimOwner(reclaimerBeforeQuarantine, reclaimOwner)) {
|
|
5093
|
+
return false;
|
|
5094
|
+
}
|
|
5095
|
+
const quarantinePath = `${lockPath}.stale.${expectedOwner.token}.${reclaimOwner.token}`;
|
|
5096
|
+
(0, import_fs7.renameSync)(lockPath, quarantinePath);
|
|
5097
|
+
(0, import_fs7.rmSync)(quarantinePath, { recursive: true, force: true });
|
|
5098
|
+
return true;
|
|
5099
|
+
} catch (error) {
|
|
5100
|
+
if (getErrorCode(error) === "ENOENT") return false;
|
|
5101
|
+
throw error;
|
|
5102
|
+
}
|
|
5103
|
+
}
|
|
5104
|
+
var IndexLockContentionError = class extends Error {
|
|
5105
|
+
constructor(lockPath, owner, reason) {
|
|
5106
|
+
const ownerDescription = owner ? `PID ${owner.pid} on ${owner.hostname}, operation ${owner.operation}, since ${owner.startedAt}` : "an unreadable owner";
|
|
5107
|
+
super(`Index mutation already in progress: ${ownerDescription}`);
|
|
5108
|
+
this.lockPath = lockPath;
|
|
5109
|
+
this.owner = owner;
|
|
5110
|
+
this.reason = reason;
|
|
5111
|
+
this.name = "IndexLockContentionError";
|
|
5112
|
+
}
|
|
5113
|
+
lockPath;
|
|
5114
|
+
owner;
|
|
5115
|
+
reason;
|
|
5116
|
+
code = "INDEX_BUSY";
|
|
5117
|
+
};
|
|
5118
|
+
function isIndexLockContentionError(error) {
|
|
5119
|
+
return error instanceof IndexLockContentionError || typeof error === "object" && error !== null && "code" in error && error.code === "INDEX_BUSY";
|
|
5120
|
+
}
|
|
5121
|
+
function acquireIndexLock(indexPath, operation) {
|
|
5122
|
+
(0, import_fs7.mkdirSync)(indexPath, { recursive: true });
|
|
5123
|
+
const canonicalIndexPath = import_fs7.realpathSync.native(indexPath);
|
|
5124
|
+
const lockPath = path12.join(canonicalIndexPath, "indexing.lock");
|
|
5125
|
+
cleanupDeadPublicationCandidates(canonicalIndexPath);
|
|
5126
|
+
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
5127
|
+
const owner = createOwner(operation);
|
|
5128
|
+
if (publishJsonDirectory(lockPath, owner)) {
|
|
5129
|
+
const lease = {
|
|
5130
|
+
canonicalIndexPath,
|
|
5131
|
+
lockPath,
|
|
5132
|
+
owner,
|
|
5133
|
+
recoveries: []
|
|
5134
|
+
};
|
|
5135
|
+
try {
|
|
5136
|
+
lease.recoveries = getPendingRecoveries(canonicalIndexPath);
|
|
5137
|
+
return lease;
|
|
5138
|
+
} catch (error) {
|
|
5139
|
+
releaseIndexLock(lease);
|
|
5140
|
+
throw error;
|
|
5141
|
+
}
|
|
5142
|
+
}
|
|
5143
|
+
let stats;
|
|
5144
|
+
try {
|
|
5145
|
+
stats = (0, import_fs7.lstatSync)(lockPath);
|
|
5146
|
+
} catch (error) {
|
|
5147
|
+
if (getErrorCode(error) === "ENOENT") continue;
|
|
5148
|
+
throw error;
|
|
5149
|
+
}
|
|
5150
|
+
if (!stats.isDirectory()) {
|
|
5151
|
+
const legacyOwner = readLegacyOwner(lockPath);
|
|
5152
|
+
throw new IndexLockContentionError(lockPath, legacyOwner, "legacy-lock");
|
|
5153
|
+
}
|
|
5154
|
+
const existingOwner = readDirectoryOwner(lockPath);
|
|
5155
|
+
if (!existingOwner) {
|
|
5156
|
+
throw new IndexLockContentionError(lockPath, null, "unknown-owner");
|
|
5157
|
+
}
|
|
5158
|
+
const liveness = getOwnerLiveness(existingOwner);
|
|
5159
|
+
if (liveness !== "dead") {
|
|
5160
|
+
throw new IndexLockContentionError(lockPath, existingOwner, liveness === "alive" ? "active" : "unknown-owner");
|
|
5161
|
+
}
|
|
5162
|
+
if (!reclaimDeadOwner(canonicalIndexPath, lockPath, existingOwner)) {
|
|
5163
|
+
throw new IndexLockContentionError(lockPath, existingOwner, "reclaiming");
|
|
5164
|
+
}
|
|
5165
|
+
}
|
|
5166
|
+
throw new IndexLockContentionError(lockPath, null, "reclaiming");
|
|
5167
|
+
}
|
|
5168
|
+
function releaseIndexLock(lease) {
|
|
5169
|
+
const currentOwner = readDirectoryOwner(lease.lockPath);
|
|
5170
|
+
if (!currentOwner || !sameOwner(currentOwner, lease.owner)) return false;
|
|
5171
|
+
const releasePath = `${lease.lockPath}.release.${lease.owner.pid}.${lease.owner.token}`;
|
|
5172
|
+
try {
|
|
5173
|
+
retryTransientFilesystemOperation(() => (0, import_fs7.renameSync)(lease.lockPath, releasePath));
|
|
5174
|
+
} catch (error) {
|
|
5175
|
+
if (getErrorCode(error) === "ENOENT") return false;
|
|
5176
|
+
throw error;
|
|
5177
|
+
}
|
|
5178
|
+
const claimedOwner = readDirectoryOwner(releasePath);
|
|
5179
|
+
if (!claimedOwner || !sameOwner(claimedOwner, lease.owner)) {
|
|
5180
|
+
if (!(0, import_fs7.existsSync)(lease.lockPath) && (0, import_fs7.existsSync)(releasePath)) {
|
|
5181
|
+
(0, import_fs7.renameSync)(releasePath, lease.lockPath);
|
|
5182
|
+
}
|
|
5183
|
+
return false;
|
|
5184
|
+
}
|
|
5185
|
+
try {
|
|
5186
|
+
retryTransientFilesystemOperation(() => (0, import_fs7.rmSync)(releasePath, { recursive: true, force: true }));
|
|
5187
|
+
} catch (error) {
|
|
5188
|
+
console.error(`[codebase-index] Lease released but tombstone cleanup failed: ${releasePath}`, error);
|
|
5189
|
+
}
|
|
5190
|
+
return true;
|
|
5191
|
+
}
|
|
5192
|
+
async function withIndexLock(indexPath, operation, callback, options = {}) {
|
|
5193
|
+
const lease = acquireIndexLock(indexPath, operation);
|
|
5194
|
+
let result;
|
|
5195
|
+
let callbackError;
|
|
5196
|
+
let callbackFailed = false;
|
|
5197
|
+
try {
|
|
5198
|
+
result = await callback(lease);
|
|
5199
|
+
} catch (error) {
|
|
5200
|
+
callbackFailed = true;
|
|
5201
|
+
callbackError = error;
|
|
5202
|
+
}
|
|
5203
|
+
if (!callbackFailed && options.completeRecoveries !== false) {
|
|
5204
|
+
try {
|
|
5205
|
+
completeLeaseRecovery(lease);
|
|
5206
|
+
} catch (error) {
|
|
5207
|
+
callbackFailed = true;
|
|
5208
|
+
callbackError = error;
|
|
5209
|
+
}
|
|
5210
|
+
}
|
|
5211
|
+
let releaseError;
|
|
5212
|
+
try {
|
|
5213
|
+
if (!releaseIndexLock(lease)) {
|
|
5214
|
+
releaseError = new Error(`Lost ownership of index mutation lease ${lease.owner.token}`);
|
|
5215
|
+
}
|
|
5216
|
+
} catch (error) {
|
|
5217
|
+
releaseError = error;
|
|
5218
|
+
}
|
|
5219
|
+
if (releaseError !== void 0) {
|
|
5220
|
+
if (callbackFailed) throw new AggregateError([callbackError, releaseError], "Index mutation and lease release both failed");
|
|
5221
|
+
throw releaseError;
|
|
5222
|
+
}
|
|
5223
|
+
if (callbackFailed) throw callbackError;
|
|
5224
|
+
return result;
|
|
5225
|
+
}
|
|
5226
|
+
function createLeaseTemporaryPath(targetPath, owner, kind = "tmp") {
|
|
5227
|
+
if (kind === "bak") return `${targetPath}.bak.${owner.pid}.${owner.token}`;
|
|
5228
|
+
temporaryCounter += 1;
|
|
5229
|
+
return `${targetPath}.tmp.${owner.pid}.${owner.token}.${temporaryCounter}`;
|
|
5230
|
+
}
|
|
5231
|
+
function removeLeaseTemporaryPath(temporaryPath) {
|
|
5232
|
+
if ((0, import_fs7.existsSync)(temporaryPath)) (0, import_fs7.rmSync)(temporaryPath, { recursive: true, force: true });
|
|
5233
|
+
}
|
|
5234
|
+
function recoverLeaseArtifacts(indexPath, owner, backupTargets) {
|
|
5235
|
+
for (const targetPath of backupTargets) {
|
|
5236
|
+
const backupPath = createLeaseTemporaryPath(targetPath, owner, "bak");
|
|
5237
|
+
if (!(0, import_fs7.existsSync)(backupPath)) continue;
|
|
5238
|
+
if ((0, import_fs7.existsSync)(targetPath)) (0, import_fs7.rmSync)(targetPath, { recursive: true, force: true });
|
|
5239
|
+
(0, import_fs7.renameSync)(backupPath, targetPath);
|
|
5240
|
+
}
|
|
5241
|
+
const temporaryOwnerMarker = `.tmp.${owner.pid}.${owner.token}.`;
|
|
5242
|
+
for (const entry of (0, import_fs7.readdirSync)(indexPath)) {
|
|
5243
|
+
if (!entry.includes(temporaryOwnerMarker)) continue;
|
|
5244
|
+
(0, import_fs7.rmSync)(path12.join(indexPath, entry), { recursive: true, force: true });
|
|
5245
|
+
}
|
|
5246
|
+
}
|
|
5247
|
+
function completeLeaseRecovery(lease) {
|
|
5248
|
+
for (const recovery of lease.recoveries) {
|
|
5249
|
+
const markerOwner = readRecoveryOwner(recovery.markerPath);
|
|
5250
|
+
if (!markerOwner || !sameOwner(markerOwner, recovery.owner)) {
|
|
5251
|
+
throw new Error(`Recovery marker ownership changed: ${recovery.markerPath}`);
|
|
5252
|
+
}
|
|
5253
|
+
}
|
|
5254
|
+
for (const recovery of lease.recoveries) {
|
|
5255
|
+
(0, import_fs7.rmSync)(recovery.markerPath, { recursive: true, force: true });
|
|
5256
|
+
}
|
|
5257
|
+
}
|
|
5258
|
+
|
|
4760
5259
|
// src/indexer/index.ts
|
|
4761
|
-
var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig", "gdscript", "matlab"]);
|
|
5260
|
+
var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig", "gdscript", "matlab", "bash"]);
|
|
4762
5261
|
var CASE_INSENSITIVE_LANGUAGES = /* @__PURE__ */ new Set(["apex"]);
|
|
4763
5262
|
var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
4764
5263
|
"function_declaration",
|
|
@@ -4838,6 +5337,46 @@ function isSqliteCorruptionError(error) {
|
|
|
4838
5337
|
return message.includes("database disk image is malformed") || message.includes("file is not a database") || message.includes("database schema is corrupt") || message.includes("sqlite_corrupt");
|
|
4839
5338
|
}
|
|
4840
5339
|
var STARTUP_WARNING_METADATA_KEY = "index.startupWarning";
|
|
5340
|
+
var READER_ARTIFACT_RETRY_INTERVAL_MS = 1e3;
|
|
5341
|
+
function metadataFromBlame(blame) {
|
|
5342
|
+
if (!blame) {
|
|
5343
|
+
return {};
|
|
5344
|
+
}
|
|
5345
|
+
return {
|
|
5346
|
+
blameSha: blame.sha,
|
|
5347
|
+
blameAuthor: blame.author,
|
|
5348
|
+
blameAuthorEmail: blame.authorEmail,
|
|
5349
|
+
blameCommittedAt: blame.committedAt,
|
|
5350
|
+
blameSummary: blame.summary
|
|
5351
|
+
};
|
|
5352
|
+
}
|
|
5353
|
+
function blameFromChunkData(chunk) {
|
|
5354
|
+
if (!chunk?.blameSha || !chunk.blameAuthor || !chunk.blameAuthorEmail || chunk.blameCommittedAt === void 0 || !chunk.blameSummary) {
|
|
5355
|
+
return void 0;
|
|
5356
|
+
}
|
|
5357
|
+
return {
|
|
5358
|
+
sha: chunk.blameSha,
|
|
5359
|
+
author: chunk.blameAuthor,
|
|
5360
|
+
authorEmail: chunk.blameAuthorEmail,
|
|
5361
|
+
committedAt: chunk.blameCommittedAt,
|
|
5362
|
+
summary: chunk.blameSummary
|
|
5363
|
+
};
|
|
5364
|
+
}
|
|
5365
|
+
function blameFromMetadata(metadata) {
|
|
5366
|
+
if (!metadata.blameSha || !metadata.blameAuthor || !metadata.blameAuthorEmail || metadata.blameCommittedAt === void 0 || !metadata.blameSummary) {
|
|
5367
|
+
return void 0;
|
|
5368
|
+
}
|
|
5369
|
+
return {
|
|
5370
|
+
sha: metadata.blameSha,
|
|
5371
|
+
author: metadata.blameAuthor,
|
|
5372
|
+
authorEmail: metadata.blameAuthorEmail,
|
|
5373
|
+
committedAt: metadata.blameCommittedAt,
|
|
5374
|
+
summary: metadata.blameSummary
|
|
5375
|
+
};
|
|
5376
|
+
}
|
|
5377
|
+
function hasBlameMetadata(metadata) {
|
|
5378
|
+
return blameFromMetadata(metadata) !== void 0;
|
|
5379
|
+
}
|
|
4841
5380
|
var INDEX_METADATA_VERSION = "1";
|
|
4842
5381
|
var EMBEDDING_STRATEGY_VERSION = "2";
|
|
4843
5382
|
var RANKING_TOKEN_CACHE_LIMIT = 4096;
|
|
@@ -4881,9 +5420,9 @@ function normalizePendingChunk(rawChunk, maxChunkTokens) {
|
|
|
4881
5420
|
};
|
|
4882
5421
|
const filePath = typeof metadata.filePath === "string" ? metadata.filePath : "unknown";
|
|
4883
5422
|
texts.push(
|
|
4884
|
-
...createEmbeddingTexts(rebuiltChunk, filePath, maxChunkTokens).map((
|
|
4885
|
-
text:
|
|
4886
|
-
tokenCount: estimateTokens(
|
|
5423
|
+
...createEmbeddingTexts(rebuiltChunk, filePath, maxChunkTokens).map((text3) => ({
|
|
5424
|
+
text: text3,
|
|
5425
|
+
tokenCount: estimateTokens(text3)
|
|
4887
5426
|
}))
|
|
4888
5427
|
);
|
|
4889
5428
|
} else {
|
|
@@ -4996,9 +5535,9 @@ function hasAllEmbeddingParts(parts, expectedPartCount) {
|
|
|
4996
5535
|
return true;
|
|
4997
5536
|
}
|
|
4998
5537
|
function isPathWithinRoot(filePath, rootPath) {
|
|
4999
|
-
const normalizedFilePath =
|
|
5000
|
-
const normalizedRoot =
|
|
5001
|
-
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${
|
|
5538
|
+
const normalizedFilePath = path13.resolve(filePath);
|
|
5539
|
+
const normalizedRoot = path13.resolve(rootPath);
|
|
5540
|
+
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path13.sep}`);
|
|
5002
5541
|
}
|
|
5003
5542
|
var rankingQueryTokenCache = /* @__PURE__ */ new Map();
|
|
5004
5543
|
var rankingNameTokenCache = /* @__PURE__ */ new Map();
|
|
@@ -5114,11 +5653,11 @@ function setBoundedCache(cache, key, value) {
|
|
|
5114
5653
|
}
|
|
5115
5654
|
cache.set(key, value);
|
|
5116
5655
|
}
|
|
5117
|
-
function tokenizeTextForRanking(
|
|
5118
|
-
if (!
|
|
5656
|
+
function tokenizeTextForRanking(text3) {
|
|
5657
|
+
if (!text3) {
|
|
5119
5658
|
return /* @__PURE__ */ new Set();
|
|
5120
5659
|
}
|
|
5121
|
-
const lowered =
|
|
5660
|
+
const lowered = text3.toLowerCase();
|
|
5122
5661
|
const cache = rankingQueryTokenCache.get(lowered) ?? rankingTextTokenCache.get(lowered);
|
|
5123
5662
|
if (cache) {
|
|
5124
5663
|
return cache;
|
|
@@ -5757,7 +6296,8 @@ function promoteIdentifierMatches(query, combined, semanticCandidates, keywordCa
|
|
|
5757
6296
|
chunkType,
|
|
5758
6297
|
name: chunk.name ?? void 0,
|
|
5759
6298
|
language: chunk.language,
|
|
5760
|
-
hash: chunk.contentHash
|
|
6299
|
+
hash: chunk.contentHash,
|
|
6300
|
+
...metadataFromBlame(blameFromChunkData(chunk))
|
|
5761
6301
|
};
|
|
5762
6302
|
const baselineScore = existing?.score ?? 0.5;
|
|
5763
6303
|
candidateUnion.set(chunk.chunkId, {
|
|
@@ -5841,7 +6381,8 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallb
|
|
|
5841
6381
|
chunkType,
|
|
5842
6382
|
name: chunk.name ?? void 0,
|
|
5843
6383
|
language: chunk.language,
|
|
5844
|
-
hash: chunk.contentHash
|
|
6384
|
+
hash: chunk.contentHash,
|
|
6385
|
+
...metadataFromBlame(blameFromChunkData(chunk))
|
|
5845
6386
|
}
|
|
5846
6387
|
});
|
|
5847
6388
|
}
|
|
@@ -6010,6 +6551,21 @@ function matchesSearchFilters(candidate, options, minScore) {
|
|
|
6010
6551
|
if (options?.chunkType && candidate.metadata.chunkType !== options.chunkType) {
|
|
6011
6552
|
return false;
|
|
6012
6553
|
}
|
|
6554
|
+
if (options?.blameAuthor) {
|
|
6555
|
+
const author = options.blameAuthor.toLowerCase();
|
|
6556
|
+
const candidateAuthor = candidate.metadata.blameAuthor?.toLowerCase();
|
|
6557
|
+
const candidateEmail = candidate.metadata.blameAuthorEmail?.toLowerCase();
|
|
6558
|
+
if (candidateAuthor !== author && candidateEmail !== author) return false;
|
|
6559
|
+
}
|
|
6560
|
+
if (options?.blameSha && !candidate.metadata.blameSha?.toLowerCase().startsWith(options.blameSha.toLowerCase())) {
|
|
6561
|
+
return false;
|
|
6562
|
+
}
|
|
6563
|
+
if (options?.blameSince) {
|
|
6564
|
+
const sinceMs = Date.parse(options.blameSince);
|
|
6565
|
+
if (Number.isNaN(sinceMs)) return false;
|
|
6566
|
+
const committedAt = candidate.metadata.blameCommittedAt;
|
|
6567
|
+
if (committedAt === void 0 || committedAt < Math.floor(sinceMs / 1e3)) return false;
|
|
6568
|
+
}
|
|
6013
6569
|
return true;
|
|
6014
6570
|
}
|
|
6015
6571
|
function unionCandidates(semanticCandidates, keywordCandidates) {
|
|
@@ -6047,26 +6603,133 @@ var Indexer = class {
|
|
|
6047
6603
|
queryCacheTtlMs = 5 * 60 * 1e3;
|
|
6048
6604
|
querySimilarityThreshold = 0.85;
|
|
6049
6605
|
indexCompatibility = null;
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6606
|
+
activeIndexLease = null;
|
|
6607
|
+
initializationPromise = null;
|
|
6608
|
+
initializationMode = "none";
|
|
6609
|
+
readIssues = [];
|
|
6610
|
+
retiredDatabases = [];
|
|
6611
|
+
readerArtifactFingerprint = null;
|
|
6612
|
+
writerArtifactFingerprint = null;
|
|
6613
|
+
readerArtifactRetryAfter = /* @__PURE__ */ new Map();
|
|
6614
|
+
constructor(projectRoot3, config, host = "opencode") {
|
|
6615
|
+
this.projectRoot = projectRoot3;
|
|
6053
6616
|
this.config = config;
|
|
6054
6617
|
this.host = host;
|
|
6055
6618
|
this.indexPath = this.getIndexPath();
|
|
6056
|
-
this.fileHashCachePath =
|
|
6057
|
-
this.failedBatchesPath =
|
|
6058
|
-
this.indexingLockPath = path11.join(this.indexPath, "indexing.lock");
|
|
6619
|
+
this.fileHashCachePath = path13.join(this.indexPath, "file-hashes.json");
|
|
6620
|
+
this.failedBatchesPath = path13.join(this.indexPath, "failed-batches.json");
|
|
6059
6621
|
this.logger = initializeLogger(config.debug);
|
|
6060
6622
|
}
|
|
6061
6623
|
getIndexPath() {
|
|
6062
6624
|
return resolveProjectIndexPath(this.projectRoot, this.config.scope, this.host);
|
|
6063
6625
|
}
|
|
6626
|
+
isLocalProjectIndexPath() {
|
|
6627
|
+
const localProjectIndexPaths = [path13.join(this.projectRoot, getHostProjectIndexRelativePath(this.host))];
|
|
6628
|
+
if (this.host !== "opencode") {
|
|
6629
|
+
localProjectIndexPaths.push(path13.join(this.projectRoot, getHostProjectIndexRelativePath("opencode")));
|
|
6630
|
+
}
|
|
6631
|
+
return localProjectIndexPaths.some((localPath) => {
|
|
6632
|
+
if (!(0, import_fs8.existsSync)(localPath) || !(0, import_fs8.existsSync)(this.indexPath)) {
|
|
6633
|
+
return path13.resolve(this.indexPath) === path13.resolve(localPath);
|
|
6634
|
+
}
|
|
6635
|
+
const indexStats = (0, import_fs8.statSync)(this.indexPath);
|
|
6636
|
+
const localStats = (0, import_fs8.statSync)(localPath);
|
|
6637
|
+
return indexStats.dev === localStats.dev && indexStats.ino === localStats.ino;
|
|
6638
|
+
});
|
|
6639
|
+
}
|
|
6640
|
+
resetLoadedIndexState(retireDatabase = false) {
|
|
6641
|
+
if (this.database) {
|
|
6642
|
+
if (retireDatabase) {
|
|
6643
|
+
this.retiredDatabases.push(this.database);
|
|
6644
|
+
} else {
|
|
6645
|
+
this.database.close();
|
|
6646
|
+
}
|
|
6647
|
+
}
|
|
6648
|
+
this.store = null;
|
|
6649
|
+
this.invertedIndex = null;
|
|
6650
|
+
this.database = null;
|
|
6651
|
+
this.provider = null;
|
|
6652
|
+
this.configuredProviderInfo = null;
|
|
6653
|
+
this.reranker = null;
|
|
6654
|
+
this.indexCompatibility = null;
|
|
6655
|
+
this.initializationMode = "none";
|
|
6656
|
+
this.readIssues = [];
|
|
6657
|
+
this.readerArtifactFingerprint = null;
|
|
6658
|
+
this.writerArtifactFingerprint = null;
|
|
6659
|
+
this.readerArtifactRetryAfter.clear();
|
|
6660
|
+
this.fileHashCache.clear();
|
|
6661
|
+
}
|
|
6662
|
+
refreshLoadedIndexState() {
|
|
6663
|
+
if (!this.store || !this.invertedIndex || !this.configuredProviderInfo) return;
|
|
6664
|
+
this.store.load();
|
|
6665
|
+
this.invertedIndex.load();
|
|
6666
|
+
this.fileHashCache.clear();
|
|
6667
|
+
this.loadFileHashCache();
|
|
6668
|
+
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
6669
|
+
this.readIssues = [];
|
|
6670
|
+
this.readerArtifactRetryAfter.clear();
|
|
6671
|
+
}
|
|
6672
|
+
async withIndexMutationLease(operation, callback) {
|
|
6673
|
+
const lease = acquireIndexLock(this.indexPath, operation);
|
|
6674
|
+
this.indexPath = lease.canonicalIndexPath;
|
|
6675
|
+
this.fileHashCachePath = path13.join(this.indexPath, "file-hashes.json");
|
|
6676
|
+
this.failedBatchesPath = path13.join(this.indexPath, "failed-batches.json");
|
|
6677
|
+
this.activeIndexLease = lease;
|
|
6678
|
+
let result;
|
|
6679
|
+
let callbackError;
|
|
6680
|
+
let callbackFailed = false;
|
|
6681
|
+
try {
|
|
6682
|
+
result = await callback(lease.recoveries.map(({ owner }) => owner));
|
|
6683
|
+
} catch (error) {
|
|
6684
|
+
callbackFailed = true;
|
|
6685
|
+
callbackError = error;
|
|
6686
|
+
}
|
|
6687
|
+
if (!callbackFailed) {
|
|
6688
|
+
try {
|
|
6689
|
+
completeLeaseRecovery(lease);
|
|
6690
|
+
this.writerArtifactFingerprint = this.captureReaderArtifactFingerprint();
|
|
6691
|
+
} catch (error) {
|
|
6692
|
+
callbackFailed = true;
|
|
6693
|
+
callbackError = error;
|
|
6694
|
+
}
|
|
6695
|
+
}
|
|
6696
|
+
let releaseError;
|
|
6697
|
+
try {
|
|
6698
|
+
if (!releaseIndexLock(lease)) {
|
|
6699
|
+
releaseError = new Error(`Lost ownership of index mutation lease ${lease.owner.token}`);
|
|
6700
|
+
this.writerArtifactFingerprint = null;
|
|
6701
|
+
if (this.activeIndexLease?.owner.token === lease.owner.token) {
|
|
6702
|
+
this.activeIndexLease = null;
|
|
6703
|
+
}
|
|
6704
|
+
} else if (this.activeIndexLease?.owner.token === lease.owner.token) {
|
|
6705
|
+
this.activeIndexLease = null;
|
|
6706
|
+
}
|
|
6707
|
+
} catch (error) {
|
|
6708
|
+
releaseError = error;
|
|
6709
|
+
this.writerArtifactFingerprint = null;
|
|
6710
|
+
if (!(0, import_fs8.existsSync)(lease.lockPath) && this.activeIndexLease?.owner.token === lease.owner.token) {
|
|
6711
|
+
this.activeIndexLease = null;
|
|
6712
|
+
}
|
|
6713
|
+
}
|
|
6714
|
+
if (releaseError !== void 0) {
|
|
6715
|
+
if (callbackFailed) throw new AggregateError([callbackError, releaseError], "Index mutation and lease release both failed");
|
|
6716
|
+
throw releaseError;
|
|
6717
|
+
}
|
|
6718
|
+
if (callbackFailed) throw callbackError;
|
|
6719
|
+
return result;
|
|
6720
|
+
}
|
|
6721
|
+
requireActiveLease() {
|
|
6722
|
+
if (!this.activeIndexLease) {
|
|
6723
|
+
throw new Error("Index mutation attempted without an active interprocess lease");
|
|
6724
|
+
}
|
|
6725
|
+
return this.activeIndexLease;
|
|
6726
|
+
}
|
|
6064
6727
|
loadFileHashCache() {
|
|
6065
|
-
if (!(0,
|
|
6728
|
+
if (!(0, import_fs8.existsSync)(this.fileHashCachePath)) {
|
|
6066
6729
|
return;
|
|
6067
6730
|
}
|
|
6068
6731
|
try {
|
|
6069
|
-
const data = (0,
|
|
6732
|
+
const data = (0, import_fs8.readFileSync)(this.fileHashCachePath, "utf-8");
|
|
6070
6733
|
const parsed = JSON.parse(data);
|
|
6071
6734
|
this.fileHashCache = new Map(Object.entries(parsed));
|
|
6072
6735
|
} catch (error) {
|
|
@@ -6086,15 +6749,26 @@ var Indexer = class {
|
|
|
6086
6749
|
this.atomicWriteSync(this.fileHashCachePath, JSON.stringify(obj));
|
|
6087
6750
|
}
|
|
6088
6751
|
atomicWriteSync(targetPath, data) {
|
|
6089
|
-
const
|
|
6090
|
-
(
|
|
6091
|
-
(0,
|
|
6092
|
-
|
|
6752
|
+
const lease = this.requireActiveLease();
|
|
6753
|
+
const tempPath = createLeaseTemporaryPath(targetPath, lease.owner, "tmp");
|
|
6754
|
+
(0, import_fs8.mkdirSync)(path13.dirname(targetPath), { recursive: true });
|
|
6755
|
+
try {
|
|
6756
|
+
(0, import_fs8.writeFileSync)(tempPath, data);
|
|
6757
|
+
(0, import_fs8.renameSync)(tempPath, targetPath);
|
|
6758
|
+
} finally {
|
|
6759
|
+
removeLeaseTemporaryPath(tempPath);
|
|
6760
|
+
}
|
|
6761
|
+
}
|
|
6762
|
+
saveInvertedIndex(invertedIndex) {
|
|
6763
|
+
this.atomicWriteSync(
|
|
6764
|
+
path13.join(this.indexPath, "inverted-index.json"),
|
|
6765
|
+
invertedIndex.serialize()
|
|
6766
|
+
);
|
|
6093
6767
|
}
|
|
6094
6768
|
getScopedRoots() {
|
|
6095
|
-
const roots = /* @__PURE__ */ new Set([
|
|
6769
|
+
const roots = /* @__PURE__ */ new Set([path13.resolve(this.projectRoot)]);
|
|
6096
6770
|
for (const kbRoot of this.config.knowledgeBases) {
|
|
6097
|
-
roots.add(
|
|
6771
|
+
roots.add(path13.resolve(this.projectRoot, kbRoot));
|
|
6098
6772
|
}
|
|
6099
6773
|
return Array.from(roots);
|
|
6100
6774
|
}
|
|
@@ -6103,29 +6777,29 @@ var Indexer = class {
|
|
|
6103
6777
|
if (this.config.scope !== "global") {
|
|
6104
6778
|
return branchName;
|
|
6105
6779
|
}
|
|
6106
|
-
const projectHash = hashContent(
|
|
6780
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6107
6781
|
return `${projectHash}:${branchName}`;
|
|
6108
6782
|
}
|
|
6109
6783
|
getBranchCatalogKeyFor(branchName) {
|
|
6110
6784
|
if (this.config.scope !== "global") {
|
|
6111
6785
|
return branchName;
|
|
6112
6786
|
}
|
|
6113
|
-
const projectHash = hashContent(
|
|
6787
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6114
6788
|
return `${projectHash}:${branchName}`;
|
|
6115
6789
|
}
|
|
6116
6790
|
getLegacyBranchCatalogKey() {
|
|
6117
6791
|
return this.currentBranch || "default";
|
|
6118
6792
|
}
|
|
6119
6793
|
getLegacyMigrationMetadataKey() {
|
|
6120
|
-
const projectHash = hashContent(
|
|
6794
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6121
6795
|
return `index.globalBranchMigration.${projectHash}`;
|
|
6122
6796
|
}
|
|
6123
6797
|
getProjectEmbeddingStrategyMetadataKey() {
|
|
6124
|
-
const projectHash = hashContent(
|
|
6798
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6125
6799
|
return `index.embeddingStrategyVersion.${projectHash}`;
|
|
6126
6800
|
}
|
|
6127
6801
|
getProjectForceReembedMetadataKey() {
|
|
6128
|
-
const projectHash = hashContent(
|
|
6802
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6129
6803
|
return `index.forceReembed.${projectHash}`;
|
|
6130
6804
|
}
|
|
6131
6805
|
hasProjectForceReembedPending() {
|
|
@@ -6219,7 +6893,7 @@ var Indexer = class {
|
|
|
6219
6893
|
if (!this.database) {
|
|
6220
6894
|
return { chunkIds, symbolIds };
|
|
6221
6895
|
}
|
|
6222
|
-
const projectRootPath =
|
|
6896
|
+
const projectRootPath = path13.resolve(this.projectRoot);
|
|
6223
6897
|
const projectLocalFilePaths = /* @__PURE__ */ new Set([
|
|
6224
6898
|
...Array.from(this.fileHashCache.keys()).filter(
|
|
6225
6899
|
(filePath) => this.isFileInCurrentScope(filePath, roots) && isPathWithinRoot(filePath, projectRootPath)
|
|
@@ -6242,7 +6916,7 @@ var Indexer = class {
|
|
|
6242
6916
|
if (this.config.scope !== "global") {
|
|
6243
6917
|
return this.getBranchCatalogCleanupKeys();
|
|
6244
6918
|
}
|
|
6245
|
-
const projectHash = hashContent(
|
|
6919
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6246
6920
|
const keys = /* @__PURE__ */ new Set();
|
|
6247
6921
|
const projectChunkIdSet = new Set(projectChunkIds);
|
|
6248
6922
|
const projectSymbolIdSet = new Set(projectSymbolIds);
|
|
@@ -6326,7 +7000,7 @@ var Indexer = class {
|
|
|
6326
7000
|
if (!this.database || this.config.scope !== "global") {
|
|
6327
7001
|
return false;
|
|
6328
7002
|
}
|
|
6329
|
-
const projectHash = hashContent(
|
|
7003
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6330
7004
|
const roots = this.getScopedRoots();
|
|
6331
7005
|
const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots);
|
|
6332
7006
|
return this.database.getAllBranches().some(
|
|
@@ -6360,7 +7034,7 @@ var Indexer = class {
|
|
|
6360
7034
|
...Array.from(this.fileHashCache.keys()).filter((filePath) => this.isFileInCurrentScope(filePath, roots)),
|
|
6361
7035
|
...scopedEntries.map(({ metadata }) => metadata.filePath)
|
|
6362
7036
|
]);
|
|
6363
|
-
const projectRootPath =
|
|
7037
|
+
const projectRootPath = path13.resolve(this.projectRoot);
|
|
6364
7038
|
const projectLocalFilePaths = new Set(
|
|
6365
7039
|
Array.from(filePaths).filter((filePath) => isPathWithinRoot(filePath, projectRootPath))
|
|
6366
7040
|
);
|
|
@@ -6422,34 +7096,27 @@ var Indexer = class {
|
|
|
6422
7096
|
database.gcOrphanEmbeddings();
|
|
6423
7097
|
database.gcOrphanChunks();
|
|
6424
7098
|
store.save();
|
|
6425
|
-
|
|
7099
|
+
this.saveInvertedIndex(invertedIndex);
|
|
6426
7100
|
return {
|
|
6427
7101
|
removedChunkIds: removedChunkIdList,
|
|
6428
7102
|
hasForeignData: allMetadata.some(({ metadata }) => !this.isFileInCurrentScope(metadata.filePath, roots))
|
|
6429
7103
|
};
|
|
6430
7104
|
}
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
(0, import_fs7.writeFileSync)(this.indexingLockPath, JSON.stringify(lockData));
|
|
6440
|
-
}
|
|
6441
|
-
releaseIndexingLock() {
|
|
6442
|
-
if ((0, import_fs7.existsSync)(this.indexingLockPath)) {
|
|
6443
|
-
(0, import_fs7.unlinkSync)(this.indexingLockPath);
|
|
7105
|
+
async recoverFromInterruptedIndexingUnlocked(owners) {
|
|
7106
|
+
for (const owner of owners) {
|
|
7107
|
+
this.logger.warn("Detected interrupted indexing session, recovering...", {
|
|
7108
|
+
pid: owner.pid,
|
|
7109
|
+
hostname: owner.hostname,
|
|
7110
|
+
operation: owner.operation,
|
|
7111
|
+
startedAt: owner.startedAt
|
|
7112
|
+
});
|
|
6444
7113
|
}
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
|
|
7114
|
+
if (this.config.scope === "global") {
|
|
7115
|
+
if ((0, import_fs8.existsSync)(this.fileHashCachePath)) {
|
|
7116
|
+
(0, import_fs8.unlinkSync)(this.fileHashCachePath);
|
|
7117
|
+
}
|
|
7118
|
+
await this.healthCheckUnlocked();
|
|
6450
7119
|
}
|
|
6451
|
-
await this.healthCheck();
|
|
6452
|
-
this.releaseIndexingLock();
|
|
6453
7120
|
this.logger.info("Recovery complete, next index will re-process all files");
|
|
6454
7121
|
}
|
|
6455
7122
|
loadFailedBatches(maxChunkTokens) {
|
|
@@ -6465,10 +7132,10 @@ var Indexer = class {
|
|
|
6465
7132
|
}
|
|
6466
7133
|
}
|
|
6467
7134
|
loadSerializedFailedBatches() {
|
|
6468
|
-
if (!(0,
|
|
7135
|
+
if (!(0, import_fs8.existsSync)(this.failedBatchesPath)) {
|
|
6469
7136
|
return [];
|
|
6470
7137
|
}
|
|
6471
|
-
const data = (0,
|
|
7138
|
+
const data = (0, import_fs8.readFileSync)(this.failedBatchesPath, "utf-8");
|
|
6472
7139
|
const parsed = JSON.parse(data);
|
|
6473
7140
|
return parsed.map((batch) => {
|
|
6474
7141
|
const chunks = Array.isArray(batch.chunks) ? batch.chunks : [];
|
|
@@ -6485,15 +7152,15 @@ var Indexer = class {
|
|
|
6485
7152
|
}
|
|
6486
7153
|
saveFailedBatches(batches) {
|
|
6487
7154
|
if (batches.length === 0) {
|
|
6488
|
-
if ((0,
|
|
7155
|
+
if ((0, import_fs8.existsSync)(this.failedBatchesPath)) {
|
|
6489
7156
|
try {
|
|
6490
|
-
(0,
|
|
7157
|
+
(0, import_fs8.unlinkSync)(this.failedBatchesPath);
|
|
6491
7158
|
} catch {
|
|
6492
7159
|
}
|
|
6493
7160
|
}
|
|
6494
7161
|
return;
|
|
6495
7162
|
}
|
|
6496
|
-
|
|
7163
|
+
this.atomicWriteSync(this.failedBatchesPath, JSON.stringify(batches, null, 2));
|
|
6497
7164
|
}
|
|
6498
7165
|
collectRetryableFailedChunks(currentFileHashes, unchangedFilePaths, maxChunkTokens) {
|
|
6499
7166
|
const retryableById = /* @__PURE__ */ new Map();
|
|
@@ -6673,7 +7340,7 @@ var Indexer = class {
|
|
|
6673
7340
|
const intent = isLikelyImplementationPath(candidate.metadata.filePath) ? "implementation" : "doc_or_test";
|
|
6674
7341
|
parts.push(`intent_hint: ${intent}`);
|
|
6675
7342
|
try {
|
|
6676
|
-
const fileContent = await
|
|
7343
|
+
const fileContent = await import_fs8.promises.readFile(candidate.metadata.filePath, "utf-8");
|
|
6677
7344
|
const lines = fileContent.split("\n");
|
|
6678
7345
|
const snippetStartLine = Math.max(1, candidate.metadata.startLine - 2);
|
|
6679
7346
|
const snippetEndLine = Math.min(lines.length, candidate.metadata.endLine + 2);
|
|
@@ -6687,29 +7354,245 @@ var Indexer = class {
|
|
|
6687
7354
|
return parts.join("\n");
|
|
6688
7355
|
}
|
|
6689
7356
|
async initialize() {
|
|
6690
|
-
if (this.
|
|
6691
|
-
|
|
6692
|
-
|
|
7357
|
+
if (this.initializationPromise) {
|
|
7358
|
+
await this.initializationPromise;
|
|
7359
|
+
}
|
|
7360
|
+
if (this.isInitializedFor("reader")) {
|
|
7361
|
+
return;
|
|
7362
|
+
}
|
|
7363
|
+
await this.initializeOnce("reader", [], { skipAutoGc: true });
|
|
7364
|
+
}
|
|
7365
|
+
async initializeOnce(mode, recoveredOwners, options) {
|
|
7366
|
+
if (this.initializationPromise) {
|
|
7367
|
+
await this.initializationPromise;
|
|
7368
|
+
if (this.isInitializedFor(mode)) {
|
|
7369
|
+
return;
|
|
6693
7370
|
}
|
|
6694
|
-
this.
|
|
6695
|
-
} else if (this.config.embeddingProvider === "auto") {
|
|
6696
|
-
this.configuredProviderInfo = await tryDetectProvider();
|
|
6697
|
-
} else {
|
|
6698
|
-
this.configuredProviderInfo = await detectEmbeddingProvider(this.config.embeddingProvider, this.config.embeddingModel);
|
|
7371
|
+
return this.initializeOnce(mode, recoveredOwners, options);
|
|
6699
7372
|
}
|
|
6700
|
-
if (
|
|
6701
|
-
|
|
6702
|
-
"No embedding provider available. Configure GitHub Copilot, OpenAI, Google, Ollama, or a custom OpenAI-compatible endpoint."
|
|
6703
|
-
);
|
|
7373
|
+
if (this.isInitializedFor(mode)) {
|
|
7374
|
+
return;
|
|
6704
7375
|
}
|
|
6705
|
-
this.
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
7376
|
+
const initialization = this.initializeUnlocked(mode, recoveredOwners, options).catch((error) => {
|
|
7377
|
+
this.resetLoadedIndexState();
|
|
7378
|
+
throw error;
|
|
7379
|
+
}).finally(() => {
|
|
7380
|
+
if (this.initializationPromise === initialization) {
|
|
7381
|
+
this.initializationPromise = null;
|
|
7382
|
+
}
|
|
6710
7383
|
});
|
|
6711
|
-
this.
|
|
6712
|
-
|
|
7384
|
+
this.initializationPromise = initialization;
|
|
7385
|
+
await initialization;
|
|
7386
|
+
}
|
|
7387
|
+
isInitializedFor(mode) {
|
|
7388
|
+
const hasState = Boolean(
|
|
7389
|
+
this.store && this.provider && this.invertedIndex && this.configuredProviderInfo && this.database
|
|
7390
|
+
);
|
|
7391
|
+
if (!hasState) {
|
|
7392
|
+
return false;
|
|
7393
|
+
}
|
|
7394
|
+
return mode === "reader" ? this.initializationMode !== "none" : this.initializationMode === "writer";
|
|
7395
|
+
}
|
|
7396
|
+
recordReadIssue(component, message, error) {
|
|
7397
|
+
this.readIssues.push(this.createReadIssue(component, message));
|
|
7398
|
+
this.readerArtifactRetryAfter.set(component, Date.now() + READER_ARTIFACT_RETRY_INTERVAL_MS);
|
|
7399
|
+
this.logger.warn(message, error === void 0 ? void 0 : { error: getErrorMessage2(error) });
|
|
7400
|
+
}
|
|
7401
|
+
createReadIssue(component, message) {
|
|
7402
|
+
return {
|
|
7403
|
+
component,
|
|
7404
|
+
message,
|
|
7405
|
+
blocking: component !== "keyword"
|
|
7406
|
+
};
|
|
7407
|
+
}
|
|
7408
|
+
getVectorReadIssueMessage() {
|
|
7409
|
+
if (this.config.scope === "global") {
|
|
7410
|
+
return "Shared vector index could not be read. Restore or repair the complete fingerprinted shared vector artifacts; automatic reset is disabled for global scope.";
|
|
7411
|
+
}
|
|
7412
|
+
if (!this.isLocalProjectIndexPath()) {
|
|
7413
|
+
return "Vector index could not be read from an inherited project index. Restore or fingerprint it from the checkout that owns the index; do not remove or rebuild it from this worktree.";
|
|
7414
|
+
}
|
|
7415
|
+
return "Vector index could not be read. Run index_codebase after the active writer finishes to fingerprint a structurally valid legacy pair, or remove this checkout's local index directory and run index_codebase to rebuild it.";
|
|
7416
|
+
}
|
|
7417
|
+
getKeywordReadIssueMessage() {
|
|
7418
|
+
if (this.config.scope === "global") {
|
|
7419
|
+
return "Shared keyword index could not be read; semantic search remains available. Restore or repair the shared keyword artifact; automatic reset is disabled for global scope.";
|
|
7420
|
+
}
|
|
7421
|
+
if (!this.isLocalProjectIndexPath()) {
|
|
7422
|
+
return "Keyword index could not be read from an inherited project index; semantic search remains available. Restore or repair it from the checkout that owns the index; do not rebuild it from this worktree.";
|
|
7423
|
+
}
|
|
7424
|
+
return "Keyword index could not be read; semantic search remains available. Restore a readable published keyword index, or run index_codebase with force=true after the active writer finishes.";
|
|
7425
|
+
}
|
|
7426
|
+
getDatabaseReadIssueMessage() {
|
|
7427
|
+
if (this.config.scope === "global") {
|
|
7428
|
+
return "Shared index database could not be read. Restore or repair the shared SQLite database; automatic reset is disabled for global scope.";
|
|
7429
|
+
}
|
|
7430
|
+
if (!this.isLocalProjectIndexPath()) {
|
|
7431
|
+
return "Index database could not be read from an inherited project index. Restore or repair it from the checkout that owns the index; do not migrate or rebuild it from this worktree.";
|
|
7432
|
+
}
|
|
7433
|
+
return "Index database could not be read. Run index_codebase after the active writer finishes to repair or migrate it under the writer lease.";
|
|
7434
|
+
}
|
|
7435
|
+
getReaderFileFingerprint(filePath, identityOnly = false) {
|
|
7436
|
+
try {
|
|
7437
|
+
const stats = (0, import_fs8.statSync)(filePath);
|
|
7438
|
+
if (identityOnly) {
|
|
7439
|
+
return `${stats.dev}:${stats.ino}`;
|
|
7440
|
+
}
|
|
7441
|
+
return `${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeMs}:${stats.ctimeMs}`;
|
|
7442
|
+
} catch (error) {
|
|
7443
|
+
return `unavailable:${getErrorMessage2(error)}`;
|
|
7444
|
+
}
|
|
7445
|
+
}
|
|
7446
|
+
captureReaderArtifactFingerprint() {
|
|
7447
|
+
const storePath = path13.join(this.indexPath, "vectors");
|
|
7448
|
+
return {
|
|
7449
|
+
vectors: `${this.getReaderFileFingerprint(storePath)}|${this.getReaderFileFingerprint(`${storePath}.meta.json`)}`,
|
|
7450
|
+
keyword: this.getReaderFileFingerprint(path13.join(this.indexPath, "inverted-index.json")),
|
|
7451
|
+
database: this.getReaderFileFingerprint(path13.join(this.indexPath, "codebase.db")),
|
|
7452
|
+
databaseIdentity: this.getReaderFileFingerprint(path13.join(this.indexPath, "codebase.db"), true)
|
|
7453
|
+
};
|
|
7454
|
+
}
|
|
7455
|
+
refreshReaderArtifacts() {
|
|
7456
|
+
if (this.initializationMode !== "reader" || !this.configuredProviderInfo) {
|
|
7457
|
+
return;
|
|
7458
|
+
}
|
|
7459
|
+
const previousFingerprint = this.readerArtifactFingerprint;
|
|
7460
|
+
const currentFingerprint = this.captureReaderArtifactFingerprint();
|
|
7461
|
+
const issues = new Map(this.readIssues.map((issue) => [issue.component, issue]));
|
|
7462
|
+
const retryDue = (component) => issues.has(component) && Date.now() >= (this.readerArtifactRetryAfter.get(component) ?? 0);
|
|
7463
|
+
const vectorsChanged = !previousFingerprint || currentFingerprint.vectors !== previousFingerprint.vectors;
|
|
7464
|
+
const keywordChanged = !previousFingerprint || currentFingerprint.keyword !== previousFingerprint.keyword;
|
|
7465
|
+
const databaseChanged = !previousFingerprint || currentFingerprint.database !== previousFingerprint.database;
|
|
7466
|
+
const databaseReplaced = !previousFingerprint || currentFingerprint.databaseIdentity !== previousFingerprint.databaseIdentity;
|
|
7467
|
+
if (previousFingerprint && !vectorsChanged && !keywordChanged && !databaseChanged && !Array.from(issues.keys()).some(retryDue)) {
|
|
7468
|
+
return;
|
|
7469
|
+
}
|
|
7470
|
+
const setIssue = (component, message, error) => {
|
|
7471
|
+
if (!issues.has(component)) {
|
|
7472
|
+
this.logger.warn(message, error === void 0 ? void 0 : { error: getErrorMessage2(error) });
|
|
7473
|
+
}
|
|
7474
|
+
issues.set(component, this.createReadIssue(component, message));
|
|
7475
|
+
this.readerArtifactRetryAfter.set(component, Date.now() + READER_ARTIFACT_RETRY_INTERVAL_MS);
|
|
7476
|
+
};
|
|
7477
|
+
const storePath = path13.join(this.indexPath, "vectors");
|
|
7478
|
+
const vectorMetadataPath = `${storePath}.meta.json`;
|
|
7479
|
+
const invertedIndexPath = path13.join(this.indexPath, "inverted-index.json");
|
|
7480
|
+
const dbPath = path13.join(this.indexPath, "codebase.db");
|
|
7481
|
+
if (vectorsChanged || retryDue("vectors")) {
|
|
7482
|
+
const vectorStoreExists = (0, import_fs8.existsSync)(storePath);
|
|
7483
|
+
const vectorMetadataExists = (0, import_fs8.existsSync)(vectorMetadataPath);
|
|
7484
|
+
if (vectorStoreExists && vectorMetadataExists) {
|
|
7485
|
+
try {
|
|
7486
|
+
const store = new VectorStore(storePath, this.configuredProviderInfo.modelInfo.dimensions);
|
|
7487
|
+
store.loadStrict();
|
|
7488
|
+
this.store = store;
|
|
7489
|
+
issues.delete("vectors");
|
|
7490
|
+
this.readerArtifactRetryAfter.delete("vectors");
|
|
7491
|
+
} catch (error) {
|
|
7492
|
+
setIssue("vectors", this.getVectorReadIssueMessage(), error);
|
|
7493
|
+
}
|
|
7494
|
+
} else if (vectorStoreExists !== vectorMetadataExists || issues.has("vectors")) {
|
|
7495
|
+
setIssue("vectors", this.getVectorReadIssueMessage());
|
|
7496
|
+
}
|
|
7497
|
+
}
|
|
7498
|
+
if (keywordChanged || retryDue("keyword") || !(0, import_fs8.existsSync)(invertedIndexPath) && (this.store?.count() ?? 0) > 0) {
|
|
7499
|
+
if ((0, import_fs8.existsSync)(invertedIndexPath)) {
|
|
7500
|
+
try {
|
|
7501
|
+
const invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
7502
|
+
invertedIndex.load();
|
|
7503
|
+
this.invertedIndex = invertedIndex;
|
|
7504
|
+
issues.delete("keyword");
|
|
7505
|
+
this.readerArtifactRetryAfter.delete("keyword");
|
|
7506
|
+
} catch (error) {
|
|
7507
|
+
setIssue("keyword", this.getKeywordReadIssueMessage(), error);
|
|
7508
|
+
}
|
|
7509
|
+
} else if ((this.store?.count() ?? 0) > 0 || issues.has("keyword")) {
|
|
7510
|
+
setIssue("keyword", this.getKeywordReadIssueMessage());
|
|
7511
|
+
}
|
|
7512
|
+
}
|
|
7513
|
+
if (databaseReplaced || databaseChanged && issues.has("database") || retryDue("database")) {
|
|
7514
|
+
if ((0, import_fs8.existsSync)(dbPath)) {
|
|
7515
|
+
try {
|
|
7516
|
+
const database = Database.openReadOnly(dbPath);
|
|
7517
|
+
if (this.database) {
|
|
7518
|
+
this.retiredDatabases.push(this.database);
|
|
7519
|
+
}
|
|
7520
|
+
this.database = database;
|
|
7521
|
+
issues.delete("database");
|
|
7522
|
+
this.readerArtifactRetryAfter.delete("database");
|
|
7523
|
+
} catch (error) {
|
|
7524
|
+
setIssue("database", this.getDatabaseReadIssueMessage(), error);
|
|
7525
|
+
}
|
|
7526
|
+
} else if ((this.store?.count() ?? 0) > 0 || issues.has("database")) {
|
|
7527
|
+
setIssue("database", this.getDatabaseReadIssueMessage());
|
|
7528
|
+
}
|
|
7529
|
+
}
|
|
7530
|
+
if (!issues.has("database")) {
|
|
7531
|
+
try {
|
|
7532
|
+
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
7533
|
+
} catch (error) {
|
|
7534
|
+
setIssue("database", this.getDatabaseReadIssueMessage(), error);
|
|
7535
|
+
}
|
|
7536
|
+
}
|
|
7537
|
+
this.readIssues = Array.from(issues.values());
|
|
7538
|
+
this.readerArtifactFingerprint = currentFingerprint;
|
|
7539
|
+
}
|
|
7540
|
+
refreshInactiveWriterArtifacts() {
|
|
7541
|
+
if (this.initializationMode !== "writer" || this.activeIndexLease) {
|
|
7542
|
+
return true;
|
|
7543
|
+
}
|
|
7544
|
+
const previousFingerprint = this.writerArtifactFingerprint;
|
|
7545
|
+
const currentFingerprint = this.captureReaderArtifactFingerprint();
|
|
7546
|
+
const retryDue = this.readIssues.some(
|
|
7547
|
+
(issue) => Date.now() >= (this.readerArtifactRetryAfter.get(issue.component) ?? 0)
|
|
7548
|
+
);
|
|
7549
|
+
const artifactsChanged = !previousFingerprint || currentFingerprint.vectors !== previousFingerprint.vectors || currentFingerprint.keyword !== previousFingerprint.keyword || currentFingerprint.database !== previousFingerprint.database || currentFingerprint.databaseIdentity !== previousFingerprint.databaseIdentity;
|
|
7550
|
+
if (!artifactsChanged && !retryDue) {
|
|
7551
|
+
return true;
|
|
7552
|
+
}
|
|
7553
|
+
if (!previousFingerprint || currentFingerprint.databaseIdentity !== previousFingerprint.databaseIdentity) {
|
|
7554
|
+
return false;
|
|
7555
|
+
}
|
|
7556
|
+
this.initializationMode = "reader";
|
|
7557
|
+
this.readerArtifactFingerprint = previousFingerprint;
|
|
7558
|
+
try {
|
|
7559
|
+
this.refreshReaderArtifacts();
|
|
7560
|
+
this.writerArtifactFingerprint = this.readerArtifactFingerprint ?? currentFingerprint;
|
|
7561
|
+
} finally {
|
|
7562
|
+
this.readerArtifactFingerprint = null;
|
|
7563
|
+
this.initializationMode = "writer";
|
|
7564
|
+
}
|
|
7565
|
+
return true;
|
|
7566
|
+
}
|
|
7567
|
+
async initializeUnlocked(mode, recoveredOwners = [], options = {}) {
|
|
7568
|
+
if (mode === "writer") {
|
|
7569
|
+
this.requireActiveLease();
|
|
7570
|
+
}
|
|
7571
|
+
this.readIssues = [];
|
|
7572
|
+
this.readerArtifactRetryAfter.clear();
|
|
7573
|
+
if (this.config.embeddingProvider === "custom") {
|
|
7574
|
+
if (!this.config.customProvider) {
|
|
7575
|
+
throw new Error("embeddingProvider is 'custom' but customProvider config is missing.");
|
|
7576
|
+
}
|
|
7577
|
+
this.configuredProviderInfo = createCustomProviderInfo(this.config.customProvider);
|
|
7578
|
+
} else if (this.config.embeddingProvider === "auto") {
|
|
7579
|
+
this.configuredProviderInfo = await tryDetectProvider();
|
|
7580
|
+
} else {
|
|
7581
|
+
this.configuredProviderInfo = await detectEmbeddingProvider(this.config.embeddingProvider, this.config.embeddingModel);
|
|
7582
|
+
}
|
|
7583
|
+
if (!this.configuredProviderInfo) {
|
|
7584
|
+
throw new Error(
|
|
7585
|
+
"No embedding provider available. Configure GitHub Copilot, OpenAI, Google, Ollama, or a custom OpenAI-compatible endpoint."
|
|
7586
|
+
);
|
|
7587
|
+
}
|
|
7588
|
+
this.logger.info("Initializing indexer", {
|
|
7589
|
+
provider: this.configuredProviderInfo.provider,
|
|
7590
|
+
model: this.configuredProviderInfo.modelInfo.model,
|
|
7591
|
+
scope: this.config.scope,
|
|
7592
|
+
rerankerEnabled: this.config.reranker?.enabled ?? false
|
|
7593
|
+
});
|
|
7594
|
+
this.provider = createEmbeddingProvider(this.configuredProviderInfo);
|
|
7595
|
+
if (this.config.reranker?.enabled) {
|
|
6713
7596
|
this.reranker = createReranker(this.config.reranker);
|
|
6714
7597
|
if (this.reranker.isAvailable()) {
|
|
6715
7598
|
this.logger.info("Reranker initialized", {
|
|
@@ -6718,36 +7601,103 @@ var Indexer = class {
|
|
|
6718
7601
|
});
|
|
6719
7602
|
}
|
|
6720
7603
|
}
|
|
6721
|
-
await import_fs7.promises.mkdir(this.indexPath, { recursive: true });
|
|
6722
7604
|
const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
|
|
6723
|
-
const storePath =
|
|
6724
|
-
|
|
6725
|
-
const
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
7605
|
+
const storePath = path13.join(this.indexPath, "vectors");
|
|
7606
|
+
const vectorMetadataPath = `${storePath}.meta.json`;
|
|
7607
|
+
const invertedIndexPath = path13.join(this.indexPath, "inverted-index.json");
|
|
7608
|
+
const dbPath = path13.join(this.indexPath, "codebase.db");
|
|
7609
|
+
let dbIsNew = !(0, import_fs8.existsSync)(dbPath);
|
|
7610
|
+
const readerArtifactFingerprint = mode === "reader" ? this.captureReaderArtifactFingerprint() : null;
|
|
7611
|
+
if (mode === "writer") {
|
|
7612
|
+
await import_fs8.promises.mkdir(this.indexPath, { recursive: true });
|
|
7613
|
+
if (recoveredOwners.length > 0 && this.config.scope === "project" && !this.isLocalProjectIndexPath()) {
|
|
7614
|
+
throw new Error(
|
|
7615
|
+
"Interrupted indexing recovery is unsafe while using an inherited worktree index. Run index_codebase with force=true to create a local project index boundary."
|
|
7616
|
+
);
|
|
7617
|
+
}
|
|
7618
|
+
for (const recoveredOwner of recoveredOwners) {
|
|
7619
|
+
recoverLeaseArtifacts(this.indexPath, recoveredOwner, [
|
|
7620
|
+
storePath,
|
|
7621
|
+
`${storePath}.meta.json`
|
|
7622
|
+
]);
|
|
7623
|
+
}
|
|
7624
|
+
if (recoveredOwners.length > 0 && this.config.scope === "project") {
|
|
7625
|
+
await this.resetLocalIndexArtifacts();
|
|
7626
|
+
}
|
|
7627
|
+
this.store = new VectorStore(storePath, dimensions);
|
|
7628
|
+
if ((0, import_fs8.existsSync)(storePath) || (0, import_fs8.existsSync)(vectorMetadataPath)) {
|
|
7629
|
+
this.store.load();
|
|
6736
7630
|
}
|
|
6737
7631
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
6738
|
-
|
|
6739
|
-
|
|
6740
|
-
|
|
6741
|
-
|
|
6742
|
-
|
|
6743
|
-
|
|
6744
|
-
|
|
6745
|
-
|
|
7632
|
+
try {
|
|
7633
|
+
this.invertedIndex.load();
|
|
7634
|
+
} catch {
|
|
7635
|
+
if ((0, import_fs8.existsSync)(invertedIndexPath)) {
|
|
7636
|
+
await import_fs8.promises.unlink(invertedIndexPath);
|
|
7637
|
+
}
|
|
7638
|
+
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
7639
|
+
}
|
|
7640
|
+
try {
|
|
7641
|
+
this.database = new Database(dbPath);
|
|
7642
|
+
} catch (error) {
|
|
7643
|
+
if (!await this.tryResetCorruptedIndex("initializing index database", error)) {
|
|
7644
|
+
throw error;
|
|
7645
|
+
}
|
|
7646
|
+
this.store = new VectorStore(storePath, dimensions);
|
|
7647
|
+
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
7648
|
+
this.database = new Database(dbPath);
|
|
7649
|
+
dbIsNew = true;
|
|
6746
7650
|
}
|
|
7651
|
+
} else {
|
|
6747
7652
|
this.store = new VectorStore(storePath, dimensions);
|
|
7653
|
+
const vectorStoreExists = (0, import_fs8.existsSync)(storePath);
|
|
7654
|
+
const vectorMetadataExists = (0, import_fs8.existsSync)(vectorMetadataPath);
|
|
7655
|
+
const vectorReadFailureMessage = this.getVectorReadIssueMessage();
|
|
7656
|
+
if (vectorStoreExists !== vectorMetadataExists) {
|
|
7657
|
+
this.recordReadIssue("vectors", vectorReadFailureMessage);
|
|
7658
|
+
} else if (vectorStoreExists) {
|
|
7659
|
+
try {
|
|
7660
|
+
this.store.loadStrict();
|
|
7661
|
+
} catch (error) {
|
|
7662
|
+
this.recordReadIssue("vectors", vectorReadFailureMessage, error);
|
|
7663
|
+
this.store = new VectorStore(storePath, dimensions);
|
|
7664
|
+
}
|
|
7665
|
+
}
|
|
6748
7666
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
6749
|
-
|
|
6750
|
-
|
|
7667
|
+
if ((0, import_fs8.existsSync)(invertedIndexPath)) {
|
|
7668
|
+
try {
|
|
7669
|
+
this.invertedIndex.load();
|
|
7670
|
+
} catch (error) {
|
|
7671
|
+
this.recordReadIssue(
|
|
7672
|
+
"keyword",
|
|
7673
|
+
this.getKeywordReadIssueMessage(),
|
|
7674
|
+
error
|
|
7675
|
+
);
|
|
7676
|
+
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
7677
|
+
}
|
|
7678
|
+
} else if (this.store.count() > 0) {
|
|
7679
|
+
this.recordReadIssue("keyword", this.getKeywordReadIssueMessage());
|
|
7680
|
+
}
|
|
7681
|
+
if ((0, import_fs8.existsSync)(dbPath)) {
|
|
7682
|
+
try {
|
|
7683
|
+
this.database = Database.openReadOnly(dbPath);
|
|
7684
|
+
} catch (error) {
|
|
7685
|
+
this.recordReadIssue(
|
|
7686
|
+
"database",
|
|
7687
|
+
this.getDatabaseReadIssueMessage(),
|
|
7688
|
+
error
|
|
7689
|
+
);
|
|
7690
|
+
this.database = Database.createEmptyReadOnly();
|
|
7691
|
+
}
|
|
7692
|
+
} else {
|
|
7693
|
+
this.database = Database.createEmptyReadOnly();
|
|
7694
|
+
if (this.store.count() > 0) {
|
|
7695
|
+
this.recordReadIssue(
|
|
7696
|
+
"database",
|
|
7697
|
+
`Index database is missing for the published vectors. ${this.getDatabaseReadIssueMessage()}`
|
|
7698
|
+
);
|
|
7699
|
+
}
|
|
7700
|
+
}
|
|
6751
7701
|
}
|
|
6752
7702
|
if (isGitRepo(this.projectRoot)) {
|
|
6753
7703
|
this.currentBranch = getBranchOrDefault(this.projectRoot);
|
|
@@ -6761,10 +7711,10 @@ var Indexer = class {
|
|
|
6761
7711
|
this.baseBranch = "default";
|
|
6762
7712
|
this.logger.branch("debug", "Not a git repository, using default branch");
|
|
6763
7713
|
}
|
|
6764
|
-
if (
|
|
6765
|
-
await this.
|
|
7714
|
+
if (mode === "writer" && recoveredOwners.length > 0) {
|
|
7715
|
+
await this.recoverFromInterruptedIndexingUnlocked(recoveredOwners);
|
|
6766
7716
|
}
|
|
6767
|
-
if (dbIsNew && this.store.count() > 0) {
|
|
7717
|
+
if (mode === "writer" && dbIsNew && this.store.count() > 0) {
|
|
6768
7718
|
this.migrateFromLegacyIndex();
|
|
6769
7719
|
}
|
|
6770
7720
|
this.loadFileHashCache();
|
|
@@ -6776,9 +7726,11 @@ var Indexer = class {
|
|
|
6776
7726
|
configuredProviderInfo: this.configuredProviderInfo
|
|
6777
7727
|
});
|
|
6778
7728
|
}
|
|
6779
|
-
if (this.config.indexing.autoGc) {
|
|
7729
|
+
if (mode === "writer" && this.config.indexing.autoGc && !options.skipAutoGc) {
|
|
6780
7730
|
await this.maybeRunAutoGc();
|
|
6781
7731
|
}
|
|
7732
|
+
this.initializationMode = mode;
|
|
7733
|
+
this.readerArtifactFingerprint = readerArtifactFingerprint;
|
|
6782
7734
|
}
|
|
6783
7735
|
async maybeRunAutoGc() {
|
|
6784
7736
|
if (!this.database) return;
|
|
@@ -6795,7 +7747,7 @@ var Indexer = class {
|
|
|
6795
7747
|
}
|
|
6796
7748
|
}
|
|
6797
7749
|
if (shouldRunGc) {
|
|
6798
|
-
const result = await this.
|
|
7750
|
+
const result = await this.healthCheckUnlocked();
|
|
6799
7751
|
if (result.warning) {
|
|
6800
7752
|
this.database.setMetadata(STARTUP_WARNING_METADATA_KEY, result.warning);
|
|
6801
7753
|
} else {
|
|
@@ -6817,7 +7769,7 @@ var Indexer = class {
|
|
|
6817
7769
|
if (await this.tryResetCorruptedIndex("running automatic orphan garbage collection", error)) {
|
|
6818
7770
|
return {
|
|
6819
7771
|
resetCorruptedIndex: true,
|
|
6820
|
-
warning: this.getCorruptedIndexWarning(
|
|
7772
|
+
warning: this.getCorruptedIndexWarning(path13.join(this.indexPath, "codebase.db"))
|
|
6821
7773
|
};
|
|
6822
7774
|
}
|
|
6823
7775
|
throw error;
|
|
@@ -6832,28 +7784,29 @@ var Indexer = class {
|
|
|
6832
7784
|
return;
|
|
6833
7785
|
}
|
|
6834
7786
|
const retainedEntries = store.getAllMetadata().filter(({ key }) => !excludedSet.has(key));
|
|
6835
|
-
const storeBasePath =
|
|
6836
|
-
const storeIndexPath =
|
|
7787
|
+
const storeBasePath = path13.join(this.indexPath, "vectors");
|
|
7788
|
+
const storeIndexPath = storeBasePath;
|
|
6837
7789
|
const storeMetadataPath = `${storeBasePath}.meta.json`;
|
|
6838
|
-
const
|
|
6839
|
-
const
|
|
7790
|
+
const lease = this.requireActiveLease();
|
|
7791
|
+
const backupIndexPath = createLeaseTemporaryPath(storeIndexPath, lease.owner, "bak");
|
|
7792
|
+
const backupMetadataPath = createLeaseTemporaryPath(storeMetadataPath, lease.owner, "bak");
|
|
6840
7793
|
let backedUpIndex = false;
|
|
6841
7794
|
let backedUpMetadata = false;
|
|
6842
7795
|
let rebuiltCount = 0;
|
|
6843
7796
|
let skippedCount = 0;
|
|
6844
|
-
if ((0,
|
|
6845
|
-
(0,
|
|
7797
|
+
if ((0, import_fs8.existsSync)(backupIndexPath)) {
|
|
7798
|
+
(0, import_fs8.unlinkSync)(backupIndexPath);
|
|
6846
7799
|
}
|
|
6847
|
-
if ((0,
|
|
6848
|
-
(0,
|
|
7800
|
+
if ((0, import_fs8.existsSync)(backupMetadataPath)) {
|
|
7801
|
+
(0, import_fs8.unlinkSync)(backupMetadataPath);
|
|
6849
7802
|
}
|
|
6850
7803
|
try {
|
|
6851
|
-
if ((0,
|
|
6852
|
-
(0,
|
|
7804
|
+
if ((0, import_fs8.existsSync)(storeIndexPath)) {
|
|
7805
|
+
(0, import_fs8.renameSync)(storeIndexPath, backupIndexPath);
|
|
6853
7806
|
backedUpIndex = true;
|
|
6854
7807
|
}
|
|
6855
|
-
if ((0,
|
|
6856
|
-
(0,
|
|
7808
|
+
if ((0, import_fs8.existsSync)(storeMetadataPath)) {
|
|
7809
|
+
(0, import_fs8.renameSync)(storeMetadataPath, backupMetadataPath);
|
|
6857
7810
|
backedUpMetadata = true;
|
|
6858
7811
|
}
|
|
6859
7812
|
store.clear();
|
|
@@ -6873,11 +7826,11 @@ var Indexer = class {
|
|
|
6873
7826
|
rebuiltCount += 1;
|
|
6874
7827
|
}
|
|
6875
7828
|
store.save();
|
|
6876
|
-
if (backedUpIndex && (0,
|
|
6877
|
-
(0,
|
|
7829
|
+
if (backedUpIndex && (0, import_fs8.existsSync)(backupIndexPath)) {
|
|
7830
|
+
(0, import_fs8.unlinkSync)(backupIndexPath);
|
|
6878
7831
|
}
|
|
6879
|
-
if (backedUpMetadata && (0,
|
|
6880
|
-
(0,
|
|
7832
|
+
if (backedUpMetadata && (0, import_fs8.existsSync)(backupMetadataPath)) {
|
|
7833
|
+
(0, import_fs8.unlinkSync)(backupMetadataPath);
|
|
6881
7834
|
}
|
|
6882
7835
|
this.logger.gc("info", "Rebuilt vector store to avoid native remove", {
|
|
6883
7836
|
excludedChunks: excludedSet.size,
|
|
@@ -6889,17 +7842,17 @@ var Indexer = class {
|
|
|
6889
7842
|
store.clear();
|
|
6890
7843
|
} catch {
|
|
6891
7844
|
}
|
|
6892
|
-
if ((0,
|
|
6893
|
-
(0,
|
|
7845
|
+
if ((0, import_fs8.existsSync)(storeIndexPath)) {
|
|
7846
|
+
(0, import_fs8.unlinkSync)(storeIndexPath);
|
|
6894
7847
|
}
|
|
6895
|
-
if ((0,
|
|
6896
|
-
(0,
|
|
7848
|
+
if ((0, import_fs8.existsSync)(storeMetadataPath)) {
|
|
7849
|
+
(0, import_fs8.unlinkSync)(storeMetadataPath);
|
|
6897
7850
|
}
|
|
6898
|
-
if (backedUpIndex && (0,
|
|
6899
|
-
(0,
|
|
7851
|
+
if (backedUpIndex && (0, import_fs8.existsSync)(backupIndexPath)) {
|
|
7852
|
+
(0, import_fs8.renameSync)(backupIndexPath, storeIndexPath);
|
|
6900
7853
|
}
|
|
6901
|
-
if (backedUpMetadata && (0,
|
|
6902
|
-
(0,
|
|
7854
|
+
if (backedUpMetadata && (0, import_fs8.existsSync)(backupMetadataPath)) {
|
|
7855
|
+
(0, import_fs8.renameSync)(backupMetadataPath, storeMetadataPath);
|
|
6903
7856
|
}
|
|
6904
7857
|
if (backedUpIndex || backedUpMetadata) {
|
|
6905
7858
|
store.load();
|
|
@@ -6913,11 +7866,37 @@ var Indexer = class {
|
|
|
6913
7866
|
}
|
|
6914
7867
|
return `Detected a corrupted local SQLite index at ${dbPath} and reset the local index. Run index_codebase to rebuild search data.`;
|
|
6915
7868
|
}
|
|
7869
|
+
async resetLocalIndexArtifacts() {
|
|
7870
|
+
this.store = null;
|
|
7871
|
+
this.invertedIndex = null;
|
|
7872
|
+
this.database?.close();
|
|
7873
|
+
this.database = null;
|
|
7874
|
+
this.indexCompatibility = null;
|
|
7875
|
+
this.initializationMode = "none";
|
|
7876
|
+
this.readIssues = [];
|
|
7877
|
+
this.readerArtifactFingerprint = null;
|
|
7878
|
+
this.writerArtifactFingerprint = null;
|
|
7879
|
+
this.readerArtifactRetryAfter.clear();
|
|
7880
|
+
this.fileHashCache.clear();
|
|
7881
|
+
const resetPaths = [
|
|
7882
|
+
path13.join(this.indexPath, "codebase.db"),
|
|
7883
|
+
path13.join(this.indexPath, "codebase.db-shm"),
|
|
7884
|
+
path13.join(this.indexPath, "codebase.db-wal"),
|
|
7885
|
+
path13.join(this.indexPath, "vectors"),
|
|
7886
|
+
path13.join(this.indexPath, "vectors.usearch"),
|
|
7887
|
+
path13.join(this.indexPath, "vectors.meta.json"),
|
|
7888
|
+
path13.join(this.indexPath, "inverted-index.json"),
|
|
7889
|
+
path13.join(this.indexPath, "file-hashes.json"),
|
|
7890
|
+
path13.join(this.indexPath, "failed-batches.json")
|
|
7891
|
+
];
|
|
7892
|
+
await Promise.all(resetPaths.map((targetPath) => import_fs8.promises.rm(targetPath, { recursive: true, force: true })));
|
|
7893
|
+
await import_fs8.promises.mkdir(this.indexPath, { recursive: true });
|
|
7894
|
+
}
|
|
6916
7895
|
async tryResetCorruptedIndex(stage, error) {
|
|
6917
7896
|
if (!isSqliteCorruptionError(error)) {
|
|
6918
7897
|
return false;
|
|
6919
7898
|
}
|
|
6920
|
-
const dbPath =
|
|
7899
|
+
const dbPath = path13.join(this.indexPath, "codebase.db");
|
|
6921
7900
|
const warning = this.getCorruptedIndexWarning(dbPath);
|
|
6922
7901
|
const errorMessage = getErrorMessage2(error);
|
|
6923
7902
|
if (this.config.scope === "global") {
|
|
@@ -6933,30 +7912,7 @@ var Indexer = class {
|
|
|
6933
7912
|
dbPath,
|
|
6934
7913
|
error: errorMessage
|
|
6935
7914
|
});
|
|
6936
|
-
this.
|
|
6937
|
-
this.invertedIndex = null;
|
|
6938
|
-
this.database?.close();
|
|
6939
|
-
this.database = null;
|
|
6940
|
-
this.indexCompatibility = null;
|
|
6941
|
-
this.fileHashCache.clear();
|
|
6942
|
-
const resetPaths = [
|
|
6943
|
-
path11.join(this.indexPath, "codebase.db"),
|
|
6944
|
-
path11.join(this.indexPath, "codebase.db-shm"),
|
|
6945
|
-
path11.join(this.indexPath, "codebase.db-wal"),
|
|
6946
|
-
path11.join(this.indexPath, "vectors.usearch"),
|
|
6947
|
-
path11.join(this.indexPath, "inverted-index.json"),
|
|
6948
|
-
path11.join(this.indexPath, "file-hashes.json"),
|
|
6949
|
-
path11.join(this.indexPath, "failed-batches.json"),
|
|
6950
|
-
path11.join(this.indexPath, "indexing.lock"),
|
|
6951
|
-
path11.join(this.indexPath, "vectors")
|
|
6952
|
-
];
|
|
6953
|
-
await Promise.all(resetPaths.map(async (targetPath) => {
|
|
6954
|
-
try {
|
|
6955
|
-
await import_fs7.promises.rm(targetPath, { recursive: true, force: true });
|
|
6956
|
-
} catch {
|
|
6957
|
-
}
|
|
6958
|
-
}));
|
|
6959
|
-
await import_fs7.promises.mkdir(this.indexPath, { recursive: true });
|
|
7915
|
+
await this.resetLocalIndexArtifacts();
|
|
6960
7916
|
return true;
|
|
6961
7917
|
}
|
|
6962
7918
|
migrateFromLegacyIndex() {
|
|
@@ -7075,8 +8031,62 @@ var Indexer = class {
|
|
|
7075
8031
|
return this.indexCompatibility;
|
|
7076
8032
|
}
|
|
7077
8033
|
async ensureInitialized() {
|
|
8034
|
+
let initializedReader = false;
|
|
8035
|
+
while (true) {
|
|
8036
|
+
if (this.initializationPromise) {
|
|
8037
|
+
await this.initializationPromise;
|
|
8038
|
+
}
|
|
8039
|
+
if (!this.isInitializedFor("reader")) {
|
|
8040
|
+
await this.initialize();
|
|
8041
|
+
initializedReader = true;
|
|
8042
|
+
continue;
|
|
8043
|
+
}
|
|
8044
|
+
if (this.initializationMode === "writer" && !this.activeIndexLease && !initializedReader) {
|
|
8045
|
+
if (!this.refreshInactiveWriterArtifacts()) {
|
|
8046
|
+
this.resetLoadedIndexState(true);
|
|
8047
|
+
await this.initialize();
|
|
8048
|
+
initializedReader = true;
|
|
8049
|
+
continue;
|
|
8050
|
+
}
|
|
8051
|
+
}
|
|
8052
|
+
if (this.initializationMode === "reader" && !initializedReader) {
|
|
8053
|
+
this.refreshReaderArtifacts();
|
|
8054
|
+
}
|
|
8055
|
+
const state = this.requireLoadedIndexState();
|
|
8056
|
+
return {
|
|
8057
|
+
...state,
|
|
8058
|
+
readIssues: [...this.readIssues],
|
|
8059
|
+
compatibility: this.indexCompatibility ?? this.validateIndexCompatibility(state.configuredProviderInfo)
|
|
8060
|
+
};
|
|
8061
|
+
}
|
|
8062
|
+
}
|
|
8063
|
+
async ensureInitializedUnlocked(recoveredOwners = []) {
|
|
8064
|
+
this.requireActiveLease();
|
|
8065
|
+
if (this.initializationPromise) {
|
|
8066
|
+
await this.initializationPromise;
|
|
8067
|
+
}
|
|
8068
|
+
if (recoveredOwners.length > 0 || !this.isInitializedFor("writer")) {
|
|
8069
|
+
const retireReaderDatabase = this.initializationMode === "reader";
|
|
8070
|
+
this.resetLoadedIndexState(retireReaderDatabase);
|
|
8071
|
+
await this.initializeOnce("writer", recoveredOwners, { skipAutoGc: true });
|
|
8072
|
+
} else {
|
|
8073
|
+
this.refreshLoadedIndexState();
|
|
8074
|
+
}
|
|
8075
|
+
if (this.config.indexing.autoGc) {
|
|
8076
|
+
await this.maybeRunAutoGc();
|
|
8077
|
+
}
|
|
8078
|
+
return this.requireLoadedIndexState();
|
|
8079
|
+
}
|
|
8080
|
+
requireReadableComponents(readIssues, ...components) {
|
|
8081
|
+
const componentSet = new Set(components);
|
|
8082
|
+
const issues = readIssues.filter((issue) => issue.blocking && componentSet.has(issue.component));
|
|
8083
|
+
if (issues.length > 0) {
|
|
8084
|
+
throw new Error(issues.map((issue) => issue.message).join(" "));
|
|
8085
|
+
}
|
|
8086
|
+
}
|
|
8087
|
+
requireLoadedIndexState() {
|
|
7078
8088
|
if (!this.store || !this.provider || !this.invertedIndex || !this.configuredProviderInfo || !this.database) {
|
|
7079
|
-
|
|
8089
|
+
throw new Error("Index state is not initialized");
|
|
7080
8090
|
}
|
|
7081
8091
|
return {
|
|
7082
8092
|
store: this.store,
|
|
@@ -7100,7 +8110,12 @@ var Indexer = class {
|
|
|
7100
8110
|
return createCostEstimate(files, configuredProviderInfo);
|
|
7101
8111
|
}
|
|
7102
8112
|
async index(onProgress) {
|
|
7103
|
-
|
|
8113
|
+
return this.withIndexMutationLease("index", async (recoveredOwners) => {
|
|
8114
|
+
return this.indexUnlocked(onProgress, recoveredOwners);
|
|
8115
|
+
});
|
|
8116
|
+
}
|
|
8117
|
+
async indexUnlocked(onProgress, recoveredOwners = [], stateReady = false) {
|
|
8118
|
+
const { store, provider, invertedIndex, database, configuredProviderInfo } = stateReady ? this.requireLoadedIndexState() : await this.ensureInitializedUnlocked(recoveredOwners);
|
|
7104
8119
|
const scopedRoots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
7105
8120
|
const branchCatalogKey = this.getBranchCatalogKey();
|
|
7106
8121
|
const forceScopedReembed = scopedRoots !== null && database.getMetadata(this.getProjectForceReembedMetadataKey()) === "true";
|
|
@@ -7110,7 +8125,6 @@ var Indexer = class {
|
|
|
7110
8125
|
`${this.indexCompatibility?.reason} Run index_codebase with force=true to rebuild the index.`
|
|
7111
8126
|
);
|
|
7112
8127
|
}
|
|
7113
|
-
this.acquireIndexingLock();
|
|
7114
8128
|
this.logger.recordIndexingStart();
|
|
7115
8129
|
this.logger.info("Starting indexing", { projectRoot: this.projectRoot });
|
|
7116
8130
|
const startTime = Date.now();
|
|
@@ -7161,7 +8175,7 @@ var Indexer = class {
|
|
|
7161
8175
|
unchangedFilePaths.add(f.path);
|
|
7162
8176
|
this.logger.recordCacheHit();
|
|
7163
8177
|
} else {
|
|
7164
|
-
const content = await
|
|
8178
|
+
const content = await import_fs8.promises.readFile(f.path, "utf-8");
|
|
7165
8179
|
changedFiles.push({ path: f.path, content, hash: currentHash });
|
|
7166
8180
|
this.logger.recordCacheMiss();
|
|
7167
8181
|
}
|
|
@@ -7185,6 +8199,7 @@ var Indexer = class {
|
|
|
7185
8199
|
this.logger.debug("Parsed changed files", { parsedCount: parsedFiles.length, parseMs: parseMs.toFixed(2) });
|
|
7186
8200
|
const existingChunks = /* @__PURE__ */ new Map();
|
|
7187
8201
|
const existingChunksByFile = /* @__PURE__ */ new Map();
|
|
8202
|
+
const existingMetadataById = /* @__PURE__ */ new Map();
|
|
7188
8203
|
for (const { key, metadata } of store.getAllMetadata()) {
|
|
7189
8204
|
if (scopedRoots && !this.isFileInCurrentScope(metadata.filePath, scopedRoots)) {
|
|
7190
8205
|
continue;
|
|
@@ -7193,6 +8208,7 @@ var Indexer = class {
|
|
|
7193
8208
|
continue;
|
|
7194
8209
|
}
|
|
7195
8210
|
existingChunks.set(key, metadata.hash);
|
|
8211
|
+
existingMetadataById.set(key, metadata);
|
|
7196
8212
|
const fileChunks = existingChunksByFile.get(metadata.filePath) || /* @__PURE__ */ new Set();
|
|
7197
8213
|
fileChunks.add(key);
|
|
7198
8214
|
existingChunksByFile.set(metadata.filePath, fileChunks);
|
|
@@ -7200,6 +8216,8 @@ var Indexer = class {
|
|
|
7200
8216
|
const currentChunkIds = /* @__PURE__ */ new Set();
|
|
7201
8217
|
const currentFilePaths = /* @__PURE__ */ new Set();
|
|
7202
8218
|
const pendingChunks = [];
|
|
8219
|
+
const gitBlameEnabled = this.config.indexing.gitBlame.enabled && isGitRepo(this.projectRoot);
|
|
8220
|
+
let backfilledBlameMetadata = false;
|
|
7203
8221
|
for (const filePath of unchangedFilePaths) {
|
|
7204
8222
|
currentFilePaths.add(filePath);
|
|
7205
8223
|
const fileChunks = existingChunksByFile.get(filePath);
|
|
@@ -7210,10 +8228,52 @@ var Indexer = class {
|
|
|
7210
8228
|
}
|
|
7211
8229
|
}
|
|
7212
8230
|
const chunkDataBatch = [];
|
|
8231
|
+
if (gitBlameEnabled) {
|
|
8232
|
+
const backfillItems = [];
|
|
8233
|
+
for (const chunkId of currentChunkIds) {
|
|
8234
|
+
const metadata = existingMetadataById.get(chunkId);
|
|
8235
|
+
if (!metadata || hasBlameMetadata(metadata)) {
|
|
8236
|
+
continue;
|
|
8237
|
+
}
|
|
8238
|
+
const chunk = database.getChunk(chunkId);
|
|
8239
|
+
if (!chunk) {
|
|
8240
|
+
continue;
|
|
8241
|
+
}
|
|
8242
|
+
const blame = await getChunkGitBlame(this.projectRoot, chunk.filePath, chunk.startLine, chunk.endLine);
|
|
8243
|
+
const blameMetadata = metadataFromBlame(blame);
|
|
8244
|
+
if (!blameMetadata.blameSha) {
|
|
8245
|
+
continue;
|
|
8246
|
+
}
|
|
8247
|
+
chunkDataBatch.push({
|
|
8248
|
+
...chunk,
|
|
8249
|
+
blameSha: blameMetadata.blameSha,
|
|
8250
|
+
blameAuthor: blameMetadata.blameAuthor,
|
|
8251
|
+
blameAuthorEmail: blameMetadata.blameAuthorEmail,
|
|
8252
|
+
blameCommittedAt: blameMetadata.blameCommittedAt,
|
|
8253
|
+
blameSummary: blameMetadata.blameSummary
|
|
8254
|
+
});
|
|
8255
|
+
const embeddingBuffer = database.getEmbedding(chunk.contentHash);
|
|
8256
|
+
if (!embeddingBuffer) {
|
|
8257
|
+
continue;
|
|
8258
|
+
}
|
|
8259
|
+
backfillItems.push({
|
|
8260
|
+
id: chunkId,
|
|
8261
|
+
vector: Array.from(bufferToFloat32Array(embeddingBuffer)),
|
|
8262
|
+
metadata: {
|
|
8263
|
+
...metadata,
|
|
8264
|
+
...blameMetadata
|
|
8265
|
+
}
|
|
8266
|
+
});
|
|
8267
|
+
}
|
|
8268
|
+
if (backfillItems.length > 0) {
|
|
8269
|
+
store.addBatch(backfillItems);
|
|
8270
|
+
backfilledBlameMetadata = true;
|
|
8271
|
+
}
|
|
8272
|
+
}
|
|
7213
8273
|
for (const parsed of parsedFiles) {
|
|
7214
8274
|
currentFilePaths.add(parsed.path);
|
|
7215
8275
|
if (parsed.chunks.length === 0) {
|
|
7216
|
-
const relativePath =
|
|
8276
|
+
const relativePath = path13.relative(this.projectRoot, parsed.path);
|
|
7217
8277
|
stats.parseFailures.push(relativePath);
|
|
7218
8278
|
}
|
|
7219
8279
|
let fileChunkCount = 0;
|
|
@@ -7234,6 +8294,10 @@ var Indexer = class {
|
|
|
7234
8294
|
}
|
|
7235
8295
|
const id = generateChunkId(parsed.path, chunk);
|
|
7236
8296
|
const contentHash = generateChunkHash(chunk);
|
|
8297
|
+
const existingContentHash = existingChunks.get(id);
|
|
8298
|
+
const existingChunk = gitBlameEnabled ? database.getChunk(id) : null;
|
|
8299
|
+
const blame = gitBlameEnabled && existingContentHash !== contentHash ? await getChunkGitBlame(this.projectRoot, parsed.path, chunk.startLine, chunk.endLine) : blameFromChunkData(existingChunk);
|
|
8300
|
+
const blameMetadata = metadataFromBlame(blame);
|
|
7237
8301
|
currentChunkIds.add(id);
|
|
7238
8302
|
chunkDataBatch.push({
|
|
7239
8303
|
chunkId: id,
|
|
@@ -7243,15 +8307,20 @@ var Indexer = class {
|
|
|
7243
8307
|
endLine: chunk.endLine,
|
|
7244
8308
|
nodeType: chunk.chunkType,
|
|
7245
8309
|
name: chunk.name,
|
|
7246
|
-
language: chunk.language
|
|
8310
|
+
language: chunk.language,
|
|
8311
|
+
blameSha: blameMetadata.blameSha,
|
|
8312
|
+
blameAuthor: blameMetadata.blameAuthor,
|
|
8313
|
+
blameAuthorEmail: blameMetadata.blameAuthorEmail,
|
|
8314
|
+
blameCommittedAt: blameMetadata.blameCommittedAt,
|
|
8315
|
+
blameSummary: blameMetadata.blameSummary
|
|
7247
8316
|
});
|
|
7248
|
-
if (
|
|
8317
|
+
if (existingContentHash === contentHash) {
|
|
7249
8318
|
fileChunkCount++;
|
|
7250
8319
|
continue;
|
|
7251
8320
|
}
|
|
7252
|
-
const texts = createEmbeddingTexts(chunk, parsed.path, getSafeEmbeddingChunkTokenLimit(configuredProviderInfo)).map((
|
|
7253
|
-
text:
|
|
7254
|
-
tokenCount: estimateTokens(
|
|
8321
|
+
const texts = createEmbeddingTexts(chunk, parsed.path, getSafeEmbeddingChunkTokenLimit(configuredProviderInfo)).map((text3) => ({
|
|
8322
|
+
text: text3,
|
|
8323
|
+
tokenCount: estimateTokens(text3)
|
|
7255
8324
|
}));
|
|
7256
8325
|
const metadata = {
|
|
7257
8326
|
filePath: parsed.path,
|
|
@@ -7260,7 +8329,8 @@ var Indexer = class {
|
|
|
7260
8329
|
chunkType: chunk.chunkType,
|
|
7261
8330
|
name: chunk.name,
|
|
7262
8331
|
language: chunk.language,
|
|
7263
|
-
hash: contentHash
|
|
8332
|
+
hash: contentHash,
|
|
8333
|
+
...blameMetadata
|
|
7264
8334
|
};
|
|
7265
8335
|
pendingChunks.push({
|
|
7266
8336
|
id,
|
|
@@ -7403,6 +8473,11 @@ var Indexer = class {
|
|
|
7403
8473
|
database.addChunksToBranchBatch(branchCatalogKey, Array.from(currentChunkIds));
|
|
7404
8474
|
database.clearBranchSymbols(branchCatalogKey);
|
|
7405
8475
|
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
8476
|
+
const vectorPath = path13.join(this.indexPath, "vectors");
|
|
8477
|
+
const shouldFingerprintLegacyPair = !store.hasFingerprint() && (0, import_fs8.existsSync)(vectorPath) && (0, import_fs8.existsSync)(`${vectorPath}.meta.json`);
|
|
8478
|
+
if (backfilledBlameMetadata || shouldFingerprintLegacyPair) {
|
|
8479
|
+
store.save();
|
|
8480
|
+
}
|
|
7406
8481
|
if (scopedRoots) {
|
|
7407
8482
|
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
7408
8483
|
this.clearScopedFailedBatches(scopedRoots);
|
|
@@ -7421,7 +8496,6 @@ var Indexer = class {
|
|
|
7421
8496
|
chunksProcessed: 0,
|
|
7422
8497
|
totalChunks: 0
|
|
7423
8498
|
});
|
|
7424
|
-
this.releaseIndexingLock();
|
|
7425
8499
|
return stats;
|
|
7426
8500
|
}
|
|
7427
8501
|
if (pendingChunks.length === 0) {
|
|
@@ -7430,7 +8504,7 @@ var Indexer = class {
|
|
|
7430
8504
|
database.clearBranchSymbols(branchCatalogKey);
|
|
7431
8505
|
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
7432
8506
|
store.save();
|
|
7433
|
-
|
|
8507
|
+
this.saveInvertedIndex(invertedIndex);
|
|
7434
8508
|
if (scopedRoots) {
|
|
7435
8509
|
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
7436
8510
|
this.clearScopedFailedBatches(scopedRoots);
|
|
@@ -7449,7 +8523,6 @@ var Indexer = class {
|
|
|
7449
8523
|
chunksProcessed: 0,
|
|
7450
8524
|
totalChunks: 0
|
|
7451
8525
|
});
|
|
7452
|
-
this.releaseIndexingLock();
|
|
7453
8526
|
return stats;
|
|
7454
8527
|
}
|
|
7455
8528
|
onProgress?.({
|
|
@@ -7680,7 +8753,7 @@ var Indexer = class {
|
|
|
7680
8753
|
database.clearBranchSymbols(branchCatalogKey);
|
|
7681
8754
|
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
7682
8755
|
store.save();
|
|
7683
|
-
|
|
8756
|
+
this.saveInvertedIndex(invertedIndex);
|
|
7684
8757
|
if (scopedRoots) {
|
|
7685
8758
|
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
7686
8759
|
} else {
|
|
@@ -7732,7 +8805,6 @@ var Indexer = class {
|
|
|
7732
8805
|
chunksProcessed: stats.indexedChunks,
|
|
7733
8806
|
totalChunks: pendingChunks.length
|
|
7734
8807
|
});
|
|
7735
|
-
this.releaseIndexingLock();
|
|
7736
8808
|
return stats;
|
|
7737
8809
|
}
|
|
7738
8810
|
async getQueryEmbedding(query, provider) {
|
|
@@ -7782,9 +8854,9 @@ var Indexer = class {
|
|
|
7782
8854
|
}
|
|
7783
8855
|
return bestMatch;
|
|
7784
8856
|
}
|
|
7785
|
-
tokenize(
|
|
8857
|
+
tokenize(text3) {
|
|
7786
8858
|
return new Set(
|
|
7787
|
-
|
|
8859
|
+
text3.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter((t) => t.length > 1)
|
|
7788
8860
|
);
|
|
7789
8861
|
}
|
|
7790
8862
|
jaccardSimilarity(a, b) {
|
|
@@ -7798,8 +8870,8 @@ var Indexer = class {
|
|
|
7798
8870
|
return intersection / union;
|
|
7799
8871
|
}
|
|
7800
8872
|
async search(query, limit, options) {
|
|
7801
|
-
const { store, provider, database } = await this.ensureInitialized();
|
|
7802
|
-
|
|
8873
|
+
const { store, provider, invertedIndex, database, readIssues, compatibility } = await this.ensureInitialized();
|
|
8874
|
+
this.requireReadableComponents(readIssues, "vectors", "database");
|
|
7803
8875
|
if (!compatibility.compatible) {
|
|
7804
8876
|
throw new Error(
|
|
7805
8877
|
`${compatibility.reason ?? "Index is incompatible with current embedding provider."} A possible solution is to run index_codebase with force=true to rebuild the index.`
|
|
@@ -7813,6 +8885,7 @@ var Indexer = class {
|
|
|
7813
8885
|
const maxResults = limit ?? this.config.search.maxResults;
|
|
7814
8886
|
const hybridWeight = options?.hybridWeight ?? this.config.search.hybridWeight;
|
|
7815
8887
|
const fusionStrategy = this.config.search.fusionStrategy;
|
|
8888
|
+
const effectiveHybridWeight = fusionStrategy === "weighted" && readIssues.some((issue) => issue.component === "keyword") ? 0 : hybridWeight;
|
|
7816
8889
|
const rrfK = this.config.search.rrfK;
|
|
7817
8890
|
const rerankTopN = this.config.search.rerankTopN;
|
|
7818
8891
|
const filterByBranch = options?.filterByBranch ?? true;
|
|
@@ -7821,7 +8894,7 @@ var Indexer = class {
|
|
|
7821
8894
|
this.logger.search("debug", "Starting search", {
|
|
7822
8895
|
query,
|
|
7823
8896
|
maxResults,
|
|
7824
|
-
hybridWeight,
|
|
8897
|
+
hybridWeight: effectiveHybridWeight,
|
|
7825
8898
|
fusionStrategy,
|
|
7826
8899
|
rrfK,
|
|
7827
8900
|
rerankTopN,
|
|
@@ -7835,7 +8908,7 @@ var Indexer = class {
|
|
|
7835
8908
|
const semanticResults = store.search(embedding, maxResults * 4);
|
|
7836
8909
|
const vectorMs = import_perf_hooks.performance.now() - vectorStartTime;
|
|
7837
8910
|
const keywordStartTime = import_perf_hooks.performance.now();
|
|
7838
|
-
const keywordResults = await this.keywordSearch(query, maxResults * 4);
|
|
8911
|
+
const keywordResults = await this.keywordSearch(query, maxResults * 4, store, invertedIndex);
|
|
7839
8912
|
const keywordMs = import_perf_hooks.performance.now() - keywordStartTime;
|
|
7840
8913
|
let branchChunkIds = null;
|
|
7841
8914
|
if (filterByBranch && (this.config.scope === "global" || this.currentBranch !== "default")) {
|
|
@@ -7872,7 +8945,7 @@ var Indexer = class {
|
|
|
7872
8945
|
rrfK,
|
|
7873
8946
|
rerankTopN,
|
|
7874
8947
|
limit: maxResults,
|
|
7875
|
-
hybridWeight,
|
|
8948
|
+
hybridWeight: effectiveHybridWeight,
|
|
7876
8949
|
prioritizeSourcePaths: sourceIntent
|
|
7877
8950
|
});
|
|
7878
8951
|
const rerankedCombined = await this.rerankCandidatesWithApi(query, combined, {
|
|
@@ -7946,7 +9019,7 @@ var Indexer = class {
|
|
|
7946
9019
|
let contextEndLine = r.metadata.endLine;
|
|
7947
9020
|
if (!metadataOnly && this.config.search.includeContext) {
|
|
7948
9021
|
try {
|
|
7949
|
-
const fileContent = await
|
|
9022
|
+
const fileContent = await import_fs8.promises.readFile(
|
|
7950
9023
|
r.metadata.filePath,
|
|
7951
9024
|
"utf-8"
|
|
7952
9025
|
);
|
|
@@ -7966,13 +9039,13 @@ var Indexer = class {
|
|
|
7966
9039
|
content,
|
|
7967
9040
|
score: r.score,
|
|
7968
9041
|
chunkType: r.metadata.chunkType,
|
|
7969
|
-
name: r.metadata.name
|
|
9042
|
+
name: r.metadata.name,
|
|
9043
|
+
blame: blameFromMetadata(r.metadata)
|
|
7970
9044
|
};
|
|
7971
9045
|
})
|
|
7972
9046
|
);
|
|
7973
9047
|
}
|
|
7974
|
-
async keywordSearch(query, limit) {
|
|
7975
|
-
const { store, invertedIndex } = await this.ensureInitialized();
|
|
9048
|
+
async keywordSearch(query, limit, store, invertedIndex) {
|
|
7976
9049
|
const scores = invertedIndex.search(query);
|
|
7977
9050
|
if (scores.size === 0) {
|
|
7978
9051
|
return [];
|
|
@@ -7990,24 +9063,54 @@ var Indexer = class {
|
|
|
7990
9063
|
return results.slice(0, limit);
|
|
7991
9064
|
}
|
|
7992
9065
|
async getStatus() {
|
|
7993
|
-
const { store, configuredProviderInfo, database } = await this.ensureInitialized();
|
|
9066
|
+
const { store, configuredProviderInfo, database, readIssues, compatibility } = await this.ensureInitialized();
|
|
7994
9067
|
const failedBatchesCount = this.getFailedBatchesCount();
|
|
9068
|
+
const vectorCount = store.count();
|
|
9069
|
+
const statusReadIssues = [...readIssues];
|
|
9070
|
+
let startupWarning = "";
|
|
9071
|
+
if (!statusReadIssues.some((issue) => issue.component === "database")) {
|
|
9072
|
+
try {
|
|
9073
|
+
startupWarning = database.getMetadata(STARTUP_WARNING_METADATA_KEY) ?? "";
|
|
9074
|
+
} catch (error) {
|
|
9075
|
+
const message = this.getDatabaseReadIssueMessage();
|
|
9076
|
+
statusReadIssues.push(this.createReadIssue("database", message));
|
|
9077
|
+
if (!this.readIssues.some((issue) => issue.component === "database")) {
|
|
9078
|
+
this.recordReadIssue("database", message, error);
|
|
9079
|
+
}
|
|
9080
|
+
}
|
|
9081
|
+
}
|
|
9082
|
+
const readWarning = statusReadIssues.map((issue) => issue.message).join(" ");
|
|
9083
|
+
const warning = [readWarning, startupWarning].filter((message) => message.length > 0).join(" ");
|
|
9084
|
+
const hasBlockingReadIssue = statusReadIssues.some((issue) => issue.blocking);
|
|
7995
9085
|
return {
|
|
7996
|
-
indexed:
|
|
7997
|
-
vectorCount
|
|
9086
|
+
indexed: vectorCount > 0 && !hasBlockingReadIssue,
|
|
9087
|
+
vectorCount,
|
|
7998
9088
|
provider: configuredProviderInfo.provider,
|
|
7999
9089
|
model: configuredProviderInfo.modelInfo.model,
|
|
8000
9090
|
indexPath: this.indexPath,
|
|
8001
9091
|
currentBranch: this.currentBranch,
|
|
8002
9092
|
baseBranch: this.baseBranch,
|
|
8003
|
-
compatibility
|
|
9093
|
+
compatibility,
|
|
8004
9094
|
failedBatchesCount,
|
|
8005
9095
|
failedBatchesPath: failedBatchesCount > 0 ? this.failedBatchesPath : void 0,
|
|
8006
|
-
warning:
|
|
9096
|
+
warning: warning || void 0
|
|
8007
9097
|
};
|
|
8008
9098
|
}
|
|
9099
|
+
async forceIndex(onProgress) {
|
|
9100
|
+
return this.withIndexMutationLease("force-index", async (recoveredOwners) => {
|
|
9101
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9102
|
+
await this.clearIndexUnlocked();
|
|
9103
|
+
return this.indexUnlocked(onProgress, [], true);
|
|
9104
|
+
});
|
|
9105
|
+
}
|
|
8009
9106
|
async clearIndex() {
|
|
8010
|
-
|
|
9107
|
+
await this.withIndexMutationLease("clear", async (recoveredOwners) => {
|
|
9108
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9109
|
+
await this.clearIndexUnlocked();
|
|
9110
|
+
});
|
|
9111
|
+
}
|
|
9112
|
+
async clearIndexUnlocked() {
|
|
9113
|
+
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
8011
9114
|
if (this.config.scope === "global") {
|
|
8012
9115
|
store.load();
|
|
8013
9116
|
invertedIndex.load();
|
|
@@ -8034,7 +9137,7 @@ var Indexer = class {
|
|
|
8034
9137
|
store.clear();
|
|
8035
9138
|
store.save();
|
|
8036
9139
|
invertedIndex.clear();
|
|
8037
|
-
|
|
9140
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8038
9141
|
this.fileHashCache.clear();
|
|
8039
9142
|
this.saveFileHashCache();
|
|
8040
9143
|
database.clearAllIndexedData();
|
|
@@ -8058,14 +9161,7 @@ var Indexer = class {
|
|
|
8058
9161
|
this.indexCompatibility = compatibility;
|
|
8059
9162
|
return;
|
|
8060
9163
|
}
|
|
8061
|
-
|
|
8062
|
-
if (this.host !== "opencode") {
|
|
8063
|
-
localProjectIndexPaths.push(path11.join(this.projectRoot, getHostProjectIndexRelativePath("opencode")));
|
|
8064
|
-
}
|
|
8065
|
-
const isLocalProjectIndex = localProjectIndexPaths.some(
|
|
8066
|
-
(localPath) => path11.resolve(this.indexPath) === path11.resolve(localPath)
|
|
8067
|
-
);
|
|
8068
|
-
if (!isLocalProjectIndex) {
|
|
9164
|
+
if (!this.isLocalProjectIndexPath()) {
|
|
8069
9165
|
throw new Error(
|
|
8070
9166
|
"Project-scoped force rebuild is unsafe while using an inherited worktree index. Create a local project config boundary before clearing the index."
|
|
8071
9167
|
);
|
|
@@ -8073,7 +9169,7 @@ var Indexer = class {
|
|
|
8073
9169
|
store.clear();
|
|
8074
9170
|
store.save();
|
|
8075
9171
|
invertedIndex.clear();
|
|
8076
|
-
|
|
9172
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8077
9173
|
this.fileHashCache.clear();
|
|
8078
9174
|
this.saveFileHashCache();
|
|
8079
9175
|
database.clearAllIndexedData();
|
|
@@ -8091,7 +9187,13 @@ var Indexer = class {
|
|
|
8091
9187
|
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
8092
9188
|
}
|
|
8093
9189
|
async healthCheck() {
|
|
8094
|
-
|
|
9190
|
+
return this.withIndexMutationLease("health-check", async (recoveredOwners) => {
|
|
9191
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9192
|
+
return this.healthCheckUnlocked();
|
|
9193
|
+
});
|
|
9194
|
+
}
|
|
9195
|
+
async healthCheckUnlocked() {
|
|
9196
|
+
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
8095
9197
|
this.logger.gc("info", "Starting health check");
|
|
8096
9198
|
const allMetadata = store.getAllMetadata();
|
|
8097
9199
|
const filePathsToChunkKeys = /* @__PURE__ */ new Map();
|
|
@@ -8104,7 +9206,7 @@ var Indexer = class {
|
|
|
8104
9206
|
const removedChunkKeys = [];
|
|
8105
9207
|
const chunkKeysByRemovedFile = /* @__PURE__ */ new Map();
|
|
8106
9208
|
for (const [filePath, chunkKeys] of filePathsToChunkKeys) {
|
|
8107
|
-
if (!(0,
|
|
9209
|
+
if (!(0, import_fs8.existsSync)(filePath)) {
|
|
8108
9210
|
chunkKeysByRemovedFile.set(filePath, chunkKeys);
|
|
8109
9211
|
for (const key of chunkKeys) {
|
|
8110
9212
|
removedChunkKeys.push(key);
|
|
@@ -8129,7 +9231,7 @@ var Indexer = class {
|
|
|
8129
9231
|
const removedCount = removedChunkKeys.length;
|
|
8130
9232
|
if (removedCount > 0) {
|
|
8131
9233
|
store.save();
|
|
8132
|
-
|
|
9234
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8133
9235
|
}
|
|
8134
9236
|
let gcOrphanEmbeddings;
|
|
8135
9237
|
let gcOrphanChunks;
|
|
@@ -8144,7 +9246,7 @@ var Indexer = class {
|
|
|
8144
9246
|
if (!await this.tryResetCorruptedIndex("running index health check", error)) {
|
|
8145
9247
|
throw error;
|
|
8146
9248
|
}
|
|
8147
|
-
await this.
|
|
9249
|
+
await this.initializeUnlocked("writer", [], { skipAutoGc: true });
|
|
8148
9250
|
return {
|
|
8149
9251
|
removed: 0,
|
|
8150
9252
|
filePaths: [],
|
|
@@ -8153,7 +9255,7 @@ var Indexer = class {
|
|
|
8153
9255
|
gcOrphanSymbols: 0,
|
|
8154
9256
|
gcOrphanCallEdges: 0,
|
|
8155
9257
|
resetCorruptedIndex: true,
|
|
8156
|
-
warning: this.getCorruptedIndexWarning(
|
|
9258
|
+
warning: this.getCorruptedIndexWarning(path13.join(this.indexPath, "codebase.db"))
|
|
8157
9259
|
};
|
|
8158
9260
|
}
|
|
8159
9261
|
this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
|
|
@@ -8166,7 +9268,13 @@ var Indexer = class {
|
|
|
8166
9268
|
return { removed: removedCount, filePaths: removedFilePaths, gcOrphanEmbeddings, gcOrphanChunks, gcOrphanSymbols, gcOrphanCallEdges };
|
|
8167
9269
|
}
|
|
8168
9270
|
async retryFailedBatches() {
|
|
8169
|
-
|
|
9271
|
+
return this.withIndexMutationLease("retry-failed-batches", async (recoveredOwners) => {
|
|
9272
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9273
|
+
return this.retryFailedBatchesUnlocked();
|
|
9274
|
+
});
|
|
9275
|
+
}
|
|
9276
|
+
async retryFailedBatchesUnlocked() {
|
|
9277
|
+
const { store, provider, invertedIndex, database, configuredProviderInfo } = this.requireLoadedIndexState();
|
|
8170
9278
|
const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
|
|
8171
9279
|
const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
|
|
8172
9280
|
const roots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
@@ -8330,7 +9438,7 @@ var Indexer = class {
|
|
|
8330
9438
|
}
|
|
8331
9439
|
if (succeeded > 0) {
|
|
8332
9440
|
store.save();
|
|
8333
|
-
|
|
9441
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8334
9442
|
}
|
|
8335
9443
|
if (roots && succeeded > 0 && persistedStillFailing.length === 0 && this.hasProjectForceReembedPending()) {
|
|
8336
9444
|
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
@@ -8358,15 +9466,16 @@ var Indexer = class {
|
|
|
8358
9466
|
}
|
|
8359
9467
|
}
|
|
8360
9468
|
async getDatabaseStats() {
|
|
8361
|
-
const { database } = await this.ensureInitialized();
|
|
9469
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9470
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8362
9471
|
return database.getStats();
|
|
8363
9472
|
}
|
|
8364
9473
|
getLogger() {
|
|
8365
9474
|
return this.logger;
|
|
8366
9475
|
}
|
|
8367
9476
|
async findSimilar(code, limit = this.config.search.maxResults, options) {
|
|
8368
|
-
const { store, provider, database } = await this.ensureInitialized();
|
|
8369
|
-
|
|
9477
|
+
const { store, provider, database, readIssues, compatibility } = await this.ensureInitialized();
|
|
9478
|
+
this.requireReadableComponents(readIssues, "vectors", "database");
|
|
8370
9479
|
if (!compatibility.compatible) {
|
|
8371
9480
|
throw new Error(
|
|
8372
9481
|
`${compatibility.reason ?? "Index is incompatible with current embedding provider."} Run index_codebase with force=true to rebuild the index.`
|
|
@@ -8456,7 +9565,7 @@ var Indexer = class {
|
|
|
8456
9565
|
let content = "";
|
|
8457
9566
|
if (this.config.search.includeContext) {
|
|
8458
9567
|
try {
|
|
8459
|
-
const fileContent = await
|
|
9568
|
+
const fileContent = await import_fs8.promises.readFile(
|
|
8460
9569
|
r.metadata.filePath,
|
|
8461
9570
|
"utf-8"
|
|
8462
9571
|
);
|
|
@@ -8473,13 +9582,15 @@ var Indexer = class {
|
|
|
8473
9582
|
content,
|
|
8474
9583
|
score: r.score,
|
|
8475
9584
|
chunkType: r.metadata.chunkType,
|
|
8476
|
-
name: r.metadata.name
|
|
9585
|
+
name: r.metadata.name,
|
|
9586
|
+
blame: blameFromMetadata(r.metadata)
|
|
8477
9587
|
};
|
|
8478
9588
|
})
|
|
8479
9589
|
);
|
|
8480
9590
|
}
|
|
8481
9591
|
async getCallers(targetName, callTypeFilter) {
|
|
8482
|
-
const { database } = await this.ensureInitialized();
|
|
9592
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9593
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8483
9594
|
const seen = /* @__PURE__ */ new Set();
|
|
8484
9595
|
const results = [];
|
|
8485
9596
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
@@ -8493,7 +9604,8 @@ var Indexer = class {
|
|
|
8493
9604
|
return results;
|
|
8494
9605
|
}
|
|
8495
9606
|
async getCallees(symbolId, callTypeFilter) {
|
|
8496
|
-
const { database } = await this.ensureInitialized();
|
|
9607
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9608
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8497
9609
|
const seen = /* @__PURE__ */ new Set();
|
|
8498
9610
|
const results = [];
|
|
8499
9611
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
@@ -8507,44 +9619,51 @@ var Indexer = class {
|
|
|
8507
9619
|
return results;
|
|
8508
9620
|
}
|
|
8509
9621
|
async findCallPath(fromName, toName, maxDepth) {
|
|
8510
|
-
const { database } = await this.ensureInitialized();
|
|
9622
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9623
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8511
9624
|
let shortest = [];
|
|
8512
9625
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
8513
|
-
const
|
|
8514
|
-
if (
|
|
8515
|
-
shortest =
|
|
9626
|
+
const path17 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
|
|
9627
|
+
if (path17.length > 0 && (shortest.length === 0 || path17.length < shortest.length)) {
|
|
9628
|
+
shortest = path17;
|
|
8516
9629
|
}
|
|
8517
9630
|
}
|
|
8518
9631
|
return shortest;
|
|
8519
9632
|
}
|
|
8520
9633
|
async getSymbolsForBranch(branch) {
|
|
8521
|
-
const { database } = await this.ensureInitialized();
|
|
9634
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9635
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8522
9636
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8523
9637
|
return database.getSymbolsForBranch(resolvedBranch);
|
|
8524
9638
|
}
|
|
8525
9639
|
async getSymbolsForFiles(filePaths, branch) {
|
|
8526
|
-
const { database } = await this.ensureInitialized();
|
|
9640
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9641
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8527
9642
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8528
9643
|
return database.getSymbolsForFiles(filePaths, resolvedBranch);
|
|
8529
9644
|
}
|
|
8530
9645
|
async getTransitiveReachability(rootSymbolIds, direction, maxDepth) {
|
|
8531
|
-
const { database } = await this.ensureInitialized();
|
|
9646
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9647
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8532
9648
|
const branch = this.getBranchCatalogKey();
|
|
8533
9649
|
return database.getTransitiveReachability(rootSymbolIds, branch, direction, maxDepth);
|
|
8534
9650
|
}
|
|
8535
9651
|
async detectCommunities(branch, symbolIds) {
|
|
8536
|
-
const { database } = await this.ensureInitialized();
|
|
9652
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9653
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8537
9654
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8538
9655
|
return database.detectCommunities(resolvedBranch, symbolIds);
|
|
8539
9656
|
}
|
|
8540
9657
|
async computeCentrality(branch) {
|
|
8541
|
-
const { database } = await this.ensureInitialized();
|
|
9658
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9659
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8542
9660
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8543
9661
|
return database.computeCentrality(resolvedBranch);
|
|
8544
9662
|
}
|
|
8545
9663
|
async getPrImpact(opts) {
|
|
8546
|
-
const { database } = await this.ensureInitialized();
|
|
8547
|
-
|
|
9664
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9665
|
+
this.requireReadableComponents(readIssues, "database");
|
|
9666
|
+
const execFileAsync3 = (0, import_util3.promisify)(import_child_process3.execFile);
|
|
8548
9667
|
const changedFilesResult = await getChangedFiles({
|
|
8549
9668
|
pr: opts.pr,
|
|
8550
9669
|
branch: opts.branch,
|
|
@@ -8566,7 +9685,7 @@ var Indexer = class {
|
|
|
8566
9685
|
"Run index_codebase first to build the call graph and symbol index for this branch."
|
|
8567
9686
|
);
|
|
8568
9687
|
}
|
|
8569
|
-
const absoluteChangedFiles = changedFiles.map((f) =>
|
|
9688
|
+
const absoluteChangedFiles = changedFiles.map((f) => path13.resolve(this.projectRoot, f));
|
|
8570
9689
|
const directSymbols = database.getSymbolsForFiles(absoluteChangedFiles, branchKey);
|
|
8571
9690
|
const directIds = directSymbols.map((s) => s.id);
|
|
8572
9691
|
const direction = opts.direction ?? "both";
|
|
@@ -8628,7 +9747,7 @@ var Indexer = class {
|
|
|
8628
9747
|
if (opts.checkConflicts) {
|
|
8629
9748
|
conflictingPRs = [];
|
|
8630
9749
|
try {
|
|
8631
|
-
const { stdout } = await
|
|
9750
|
+
const { stdout } = await execFileAsync3(
|
|
8632
9751
|
"gh",
|
|
8633
9752
|
["pr", "list", "--state", "open", "--json", "number,headRefName", "--limit", "10000"],
|
|
8634
9753
|
{ cwd: this.projectRoot, timeout: 3e4 }
|
|
@@ -8649,7 +9768,7 @@ var Indexer = class {
|
|
|
8649
9768
|
projectRoot: this.projectRoot,
|
|
8650
9769
|
baseBranch: this.baseBranch
|
|
8651
9770
|
});
|
|
8652
|
-
const otherAbsolute = otherChanged.files.map((f) =>
|
|
9771
|
+
const otherAbsolute = otherChanged.files.map((f) => path13.resolve(this.projectRoot, f));
|
|
8653
9772
|
const otherBranchKey = this.getBranchCatalogKeyFor(openPr.headRefName);
|
|
8654
9773
|
const otherSymbols = database.getSymbolsForFiles(otherAbsolute, otherBranchKey);
|
|
8655
9774
|
const otherLabels = /* @__PURE__ */ new Set();
|
|
@@ -8699,7 +9818,8 @@ var Indexer = class {
|
|
|
8699
9818
|
};
|
|
8700
9819
|
}
|
|
8701
9820
|
async getVisualizationData(options) {
|
|
8702
|
-
const { database, store } = await this.ensureInitialized();
|
|
9821
|
+
const { database, store, readIssues } = await this.ensureInitialized();
|
|
9822
|
+
this.requireReadableComponents(readIssues, "vectors", "database");
|
|
8703
9823
|
const seenSymbols = /* @__PURE__ */ new Map();
|
|
8704
9824
|
const seenEdges = /* @__PURE__ */ new Map();
|
|
8705
9825
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
@@ -8712,12 +9832,12 @@ var Indexer = class {
|
|
|
8712
9832
|
if (meta.filePath) filePaths.add(meta.filePath);
|
|
8713
9833
|
}
|
|
8714
9834
|
const directory = options?.directory?.replace(/\/$/, "");
|
|
8715
|
-
const absoluteDirectoryFilter = directory ?
|
|
9835
|
+
const absoluteDirectoryFilter = directory ? path13.resolve(this.projectRoot, directory) : void 0;
|
|
8716
9836
|
for (const filePath of filePaths) {
|
|
8717
9837
|
if (directory) {
|
|
8718
|
-
const absoluteFilePath =
|
|
9838
|
+
const absoluteFilePath = path13.resolve(filePath);
|
|
8719
9839
|
const matchesRelative = filePath === directory || filePath.startsWith(directory + "/");
|
|
8720
|
-
const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter +
|
|
9840
|
+
const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter + path13.sep));
|
|
8721
9841
|
if (!matchesRelative && !matchesProjectRelative) {
|
|
8722
9842
|
continue;
|
|
8723
9843
|
}
|
|
@@ -8739,53 +9859,64 @@ var Indexer = class {
|
|
|
8739
9859
|
return { symbols: [...seenSymbols.values()], edges: [...seenEdges.values()] };
|
|
8740
9860
|
}
|
|
8741
9861
|
async close() {
|
|
8742
|
-
|
|
9862
|
+
this.database?.close();
|
|
9863
|
+
for (const database of this.retiredDatabases) {
|
|
9864
|
+
database.close();
|
|
9865
|
+
}
|
|
9866
|
+
this.retiredDatabases = [];
|
|
8743
9867
|
this.database = null;
|
|
8744
9868
|
this.store = null;
|
|
8745
9869
|
this.invertedIndex = null;
|
|
8746
9870
|
this.provider = null;
|
|
8747
9871
|
this.reranker = null;
|
|
9872
|
+
this.configuredProviderInfo = null;
|
|
9873
|
+
this.indexCompatibility = null;
|
|
9874
|
+
this.initializationMode = "none";
|
|
9875
|
+
this.readIssues = [];
|
|
9876
|
+
this.readerArtifactFingerprint = null;
|
|
9877
|
+
this.writerArtifactFingerprint = null;
|
|
9878
|
+
this.readerArtifactRetryAfter.clear();
|
|
8748
9879
|
}
|
|
8749
9880
|
};
|
|
8750
9881
|
|
|
8751
9882
|
// src/tools/knowledge-base-paths.ts
|
|
8752
|
-
var
|
|
9883
|
+
var path14 = __toESM(require("path"), 1);
|
|
8753
9884
|
function resolveConfigPathValue(value, baseDir) {
|
|
8754
9885
|
const trimmed = value.trim();
|
|
8755
9886
|
if (!trimmed) {
|
|
8756
9887
|
return trimmed;
|
|
8757
9888
|
}
|
|
8758
|
-
const absolutePath =
|
|
8759
|
-
return
|
|
9889
|
+
const absolutePath = path14.isAbsolute(trimmed) ? trimmed : path14.resolve(baseDir, trimmed);
|
|
9890
|
+
return path14.normalize(absolutePath);
|
|
8760
9891
|
}
|
|
8761
9892
|
function serializeConfigPathValue(value, baseDir) {
|
|
8762
9893
|
const trimmed = value.trim();
|
|
8763
9894
|
if (!trimmed) {
|
|
8764
9895
|
return trimmed;
|
|
8765
9896
|
}
|
|
8766
|
-
if (!
|
|
8767
|
-
return normalizePathSeparators(
|
|
9897
|
+
if (!path14.isAbsolute(trimmed)) {
|
|
9898
|
+
return normalizePathSeparators(path14.normalize(trimmed));
|
|
8768
9899
|
}
|
|
8769
|
-
const relativePath =
|
|
8770
|
-
if (!relativePath || !relativePath.startsWith("..") && !
|
|
8771
|
-
return normalizePathSeparators(
|
|
9900
|
+
const relativePath = path14.relative(baseDir, trimmed);
|
|
9901
|
+
if (!relativePath || !relativePath.startsWith("..") && !path14.isAbsolute(relativePath)) {
|
|
9902
|
+
return normalizePathSeparators(path14.normalize(relativePath || "."));
|
|
8772
9903
|
}
|
|
8773
|
-
return
|
|
9904
|
+
return path14.normalize(trimmed);
|
|
8774
9905
|
}
|
|
8775
|
-
function resolveKnowledgeBasePath(value,
|
|
8776
|
-
return
|
|
9906
|
+
function resolveKnowledgeBasePath(value, projectRoot3) {
|
|
9907
|
+
return path14.isAbsolute(value) ? value : path14.resolve(projectRoot3, value);
|
|
8777
9908
|
}
|
|
8778
|
-
function normalizeKnowledgeBasePath2(value,
|
|
8779
|
-
return
|
|
9909
|
+
function normalizeKnowledgeBasePath2(value, projectRoot3) {
|
|
9910
|
+
return path14.normalize(resolveKnowledgeBasePath(value, projectRoot3));
|
|
8780
9911
|
}
|
|
8781
|
-
function hasMatchingKnowledgeBasePath(knowledgeBases, inputPath,
|
|
8782
|
-
const normalizedInput =
|
|
8783
|
-
return knowledgeBases.some((kb) => normalizeKnowledgeBasePath2(kb,
|
|
9912
|
+
function hasMatchingKnowledgeBasePath(knowledgeBases, inputPath, projectRoot3) {
|
|
9913
|
+
const normalizedInput = path14.normalize(inputPath);
|
|
9914
|
+
return knowledgeBases.some((kb) => normalizeKnowledgeBasePath2(kb, projectRoot3) === normalizedInput);
|
|
8784
9915
|
}
|
|
8785
|
-
function findKnowledgeBasePathIndex(knowledgeBases, inputPath,
|
|
8786
|
-
const normalizedInput =
|
|
9916
|
+
function findKnowledgeBasePathIndex(knowledgeBases, inputPath, projectRoot3) {
|
|
9917
|
+
const normalizedInput = path14.normalize(inputPath);
|
|
8787
9918
|
return knowledgeBases.findIndex(
|
|
8788
|
-
(kb) =>
|
|
9919
|
+
(kb) => path14.normalize(kb) === normalizedInput || normalizeKnowledgeBasePath2(kb, projectRoot3) === normalizedInput
|
|
8789
9920
|
);
|
|
8790
9921
|
}
|
|
8791
9922
|
|
|
@@ -8885,6 +10016,10 @@ function formatStatus(status) {
|
|
|
8885
10016
|
lines.push(`Failed batches: ${status.failedBatchesPath}`);
|
|
8886
10017
|
}
|
|
8887
10018
|
}
|
|
10019
|
+
if (status.warning) {
|
|
10020
|
+
lines.push("");
|
|
10021
|
+
lines.push(`INDEX WARNING: ${status.warning}`);
|
|
10022
|
+
}
|
|
8888
10023
|
if (status.compatibility && !status.compatibility.compatible) {
|
|
8889
10024
|
lines.push("");
|
|
8890
10025
|
lines.push(`COMPATIBILITY WARNING: ${status.compatibility.reason}`);
|
|
@@ -8958,16 +10093,57 @@ function formatHealthCheck(result) {
|
|
|
8958
10093
|
}
|
|
8959
10094
|
return lines.join("\n");
|
|
8960
10095
|
}
|
|
10096
|
+
function formatCallGraphCallers(name, callers, relationshipType) {
|
|
10097
|
+
if (callers.length === 0) {
|
|
10098
|
+
return `No callers found for "${name}"${relationshipType ? ` with type ${relationshipType}` : ""}. It may not be called by any tracked function, or the index needs updating.`;
|
|
10099
|
+
}
|
|
10100
|
+
const formatted = callers.map((edge, index) => {
|
|
10101
|
+
const confidence = edge.confidence !== "Direct" ? ` [${edge.confidence.toLowerCase()}]` : "";
|
|
10102
|
+
return `[${index + 1}] \u2190 from ${edge.fromSymbolName ?? "<unknown>"} in ${edge.fromSymbolFilePath ?? "<unknown file>"} [${edge.fromSymbolId}] (${edge.callType})${confidence} at line ${edge.line}${edge.isResolved ? " [resolved]" : " [unresolved]"}`;
|
|
10103
|
+
});
|
|
10104
|
+
return `"${name}" is called by ${callers.length} function(s):
|
|
10105
|
+
|
|
10106
|
+
${formatted.join("\n")}`;
|
|
10107
|
+
}
|
|
10108
|
+
function formatCallGraphCallees(symbolId, callees, relationshipType) {
|
|
10109
|
+
if (callees.length === 0) {
|
|
10110
|
+
return `No callees found for symbol ${symbolId}${relationshipType ? ` with type ${relationshipType}` : ""}. The function may not call any other tracked functions.`;
|
|
10111
|
+
}
|
|
10112
|
+
return callees.map((edge, index) => {
|
|
10113
|
+
const confidence = edge.confidence !== "Direct" ? ` [${edge.confidence.toLowerCase()}]` : "";
|
|
10114
|
+
return `[${index + 1}] \u2192 ${edge.targetName} (${edge.callType})${confidence} at line ${edge.line}${edge.isResolved ? ` [resolved: ${edge.toSymbolId}]` : " [unresolved]"}`;
|
|
10115
|
+
}).join("\n");
|
|
10116
|
+
}
|
|
10117
|
+
function formatCallGraphPath(from, to, path17) {
|
|
10118
|
+
if (path17.length === 0) {
|
|
10119
|
+
return `No path found between "${from}" and "${to}". They may be in disconnected components, or the call graph index needs updating.`;
|
|
10120
|
+
}
|
|
10121
|
+
const formatted = path17.map((hop, index) => {
|
|
10122
|
+
const prefix = index === 0 ? "[start]" : `--${hop.callType}-->`;
|
|
10123
|
+
const location = hop.filePath ? ` (${hop.filePath}:${hop.line})` : "";
|
|
10124
|
+
return `${prefix} ${hop.symbolName}${location}`;
|
|
10125
|
+
});
|
|
10126
|
+
return `Path (${path17.length} hops):
|
|
10127
|
+
${formatted.join("\n")}`;
|
|
10128
|
+
}
|
|
8961
10129
|
function formatResultHeader(result, index) {
|
|
8962
10130
|
return result.name ? `[${index + 1}] ${result.chunkType} "${result.name}" in ${result.filePath}:${result.startLine}-${result.endLine}` : `[${index + 1}] ${result.chunkType} in ${result.filePath}:${result.startLine}-${result.endLine}`;
|
|
8963
10131
|
}
|
|
10132
|
+
function formatBlame(result) {
|
|
10133
|
+
if (!result.blame) {
|
|
10134
|
+
return "";
|
|
10135
|
+
}
|
|
10136
|
+
const date = new Date(result.blame.committedAt * 1e3).toISOString().slice(0, 10);
|
|
10137
|
+
return `
|
|
10138
|
+
${result.blame.sha.slice(0, 7)} | ${result.blame.author} | ${date} | ${result.blame.summary}`;
|
|
10139
|
+
}
|
|
8964
10140
|
function formatDefinitionLookup(results, query) {
|
|
8965
10141
|
if (results.length === 0) {
|
|
8966
10142
|
return `No definition found for "${query}". Try codebase_search for broader discovery, or verify the symbol name.`;
|
|
8967
10143
|
}
|
|
8968
10144
|
const formatted = results.map((r, idx) => {
|
|
8969
10145
|
const header = formatResultHeader(r, idx);
|
|
8970
|
-
return `${header} (score: ${r.score.toFixed(2)})
|
|
10146
|
+
return `${header} (score: ${r.score.toFixed(2)})${formatBlame(r)}
|
|
8971
10147
|
\`\`\`
|
|
8972
10148
|
${truncateContent(r.content)}
|
|
8973
10149
|
\`\`\``;
|
|
@@ -8978,7 +10154,7 @@ function formatSearchResults(results, scoreFormat = "similarity") {
|
|
|
8978
10154
|
const formatted = results.map((r, idx) => {
|
|
8979
10155
|
const header = formatResultHeader(r, idx);
|
|
8980
10156
|
const scoreLabel = scoreFormat === "similarity" ? `(similarity: ${(r.score * 100).toFixed(1)}%)` : `(score: ${r.score.toFixed(2)})`;
|
|
8981
|
-
return `${header} ${scoreLabel}
|
|
10157
|
+
return `${header} ${scoreLabel}${formatBlame(r)}
|
|
8982
10158
|
\`\`\`
|
|
8983
10159
|
${truncateContent(r.content)}
|
|
8984
10160
|
\`\`\``;
|
|
@@ -8987,13 +10163,13 @@ ${truncateContent(r.content)}
|
|
|
8987
10163
|
}
|
|
8988
10164
|
|
|
8989
10165
|
// src/tools/config-state.ts
|
|
8990
|
-
var
|
|
8991
|
-
var
|
|
8992
|
-
function normalizeKnowledgeBasePaths(config,
|
|
10166
|
+
var import_fs9 = require("fs");
|
|
10167
|
+
var path15 = __toESM(require("path"), 1);
|
|
10168
|
+
function normalizeKnowledgeBasePaths(config, projectRoot3) {
|
|
8993
10169
|
const normalized = { ...config };
|
|
8994
10170
|
if (Array.isArray(normalized.knowledgeBases)) {
|
|
8995
10171
|
normalized.knowledgeBases = normalized.knowledgeBases.map(
|
|
8996
|
-
(kb) => resolveConfigPathValue(kb,
|
|
10172
|
+
(kb) => resolveConfigPathValue(kb, projectRoot3)
|
|
8997
10173
|
);
|
|
8998
10174
|
}
|
|
8999
10175
|
return normalized;
|
|
@@ -9004,21 +10180,21 @@ function toConfigRecord(rawConfig) {
|
|
|
9004
10180
|
}
|
|
9005
10181
|
return { ...rawConfig };
|
|
9006
10182
|
}
|
|
9007
|
-
function getConfigPath(
|
|
9008
|
-
return resolveWritableProjectConfigPath(
|
|
10183
|
+
function getConfigPath(projectRoot3, host = "opencode") {
|
|
10184
|
+
return resolveWritableProjectConfigPath(projectRoot3, host);
|
|
9009
10185
|
}
|
|
9010
|
-
function loadRuntimeConfig(
|
|
9011
|
-
return normalizeKnowledgeBasePaths(toConfigRecord(loadMergedConfig(
|
|
10186
|
+
function loadRuntimeConfig(projectRoot3, host = "opencode") {
|
|
10187
|
+
return normalizeKnowledgeBasePaths(toConfigRecord(loadMergedConfig(projectRoot3, host)), projectRoot3);
|
|
9012
10188
|
}
|
|
9013
|
-
function loadEditableConfig(
|
|
9014
|
-
return normalizeKnowledgeBasePaths(toConfigRecord(loadProjectConfigLayer(
|
|
10189
|
+
function loadEditableConfig(projectRoot3, host = "opencode") {
|
|
10190
|
+
return normalizeKnowledgeBasePaths(toConfigRecord(loadProjectConfigLayer(projectRoot3, host)), projectRoot3);
|
|
9015
10191
|
}
|
|
9016
|
-
function saveConfig(
|
|
9017
|
-
const configPath = getConfigPath(
|
|
9018
|
-
const configDir =
|
|
9019
|
-
const configBaseDir =
|
|
9020
|
-
if (!(0,
|
|
9021
|
-
(0,
|
|
10192
|
+
function saveConfig(projectRoot3, config, host = "opencode") {
|
|
10193
|
+
const configPath = getConfigPath(projectRoot3, host);
|
|
10194
|
+
const configDir = path15.dirname(configPath);
|
|
10195
|
+
const configBaseDir = path15.dirname(configDir);
|
|
10196
|
+
if (!(0, import_fs9.existsSync)(configDir)) {
|
|
10197
|
+
(0, import_fs9.mkdirSync)(configDir, { recursive: true });
|
|
9022
10198
|
}
|
|
9023
10199
|
const serializableConfig = { ...config };
|
|
9024
10200
|
if (Array.isArray(serializableConfig.knowledgeBases)) {
|
|
@@ -9026,16 +10202,34 @@ function saveConfig(projectRoot2, config, host = "opencode") {
|
|
|
9026
10202
|
(kb) => serializeConfigPathValue(kb, configBaseDir)
|
|
9027
10203
|
);
|
|
9028
10204
|
}
|
|
9029
|
-
(0,
|
|
10205
|
+
(0, import_fs9.writeFileSync)(configPath, JSON.stringify(serializableConfig, null, 2) + "\n", "utf-8");
|
|
9030
10206
|
}
|
|
9031
10207
|
|
|
9032
10208
|
// src/tools/operations.ts
|
|
9033
10209
|
var indexerCache = /* @__PURE__ */ new Map();
|
|
9034
10210
|
var configCache = /* @__PURE__ */ new Map();
|
|
9035
10211
|
var defaultProjectRoots = /* @__PURE__ */ new Map();
|
|
9036
|
-
function
|
|
9037
|
-
if (
|
|
9038
|
-
|
|
10212
|
+
function getIndexBusyResult(error) {
|
|
10213
|
+
if (!isIndexLockContentionError(error)) return null;
|
|
10214
|
+
const owner = error.owner;
|
|
10215
|
+
const ownerText = owner ? `PID ${owner.pid}, operation ${owner.operation}, since ${owner.startedAt}` : "unreadable owner";
|
|
10216
|
+
if (error.reason === "legacy-lock") {
|
|
10217
|
+
return {
|
|
10218
|
+
kind: "busy",
|
|
10219
|
+
text: `INDEX_BUSY: legacy lock format detected (${ownerText}). Verify the PID and remove this lock manually only if it is stale.`
|
|
10220
|
+
};
|
|
10221
|
+
}
|
|
10222
|
+
if (error.reason === "unknown-owner") {
|
|
10223
|
+
return {
|
|
10224
|
+
kind: "busy",
|
|
10225
|
+
text: `INDEX_BUSY: unreadable or remote lock owner (${ownerText}). Automatic recovery was refused; manual verification is required.`
|
|
10226
|
+
};
|
|
10227
|
+
}
|
|
10228
|
+
return { kind: "busy", text: `INDEX_BUSY: another index operation is already in progress (${ownerText}).` };
|
|
10229
|
+
}
|
|
10230
|
+
function getProjectRoot(projectRoot3, host) {
|
|
10231
|
+
if (projectRoot3) {
|
|
10232
|
+
return projectRoot3;
|
|
9039
10233
|
}
|
|
9040
10234
|
const root = defaultProjectRoots.get(host);
|
|
9041
10235
|
if (!root) {
|
|
@@ -9043,53 +10237,56 @@ function getProjectRoot(projectRoot2, host) {
|
|
|
9043
10237
|
}
|
|
9044
10238
|
return root;
|
|
9045
10239
|
}
|
|
9046
|
-
function getIndexerCacheKey(
|
|
9047
|
-
return `${host}::${
|
|
10240
|
+
function getIndexerCacheKey(projectRoot3, host) {
|
|
10241
|
+
return `${host}::${projectRoot3}`;
|
|
9048
10242
|
}
|
|
9049
|
-
function getOrCreateIndexer(
|
|
9050
|
-
const key = getIndexerCacheKey(
|
|
10243
|
+
function getOrCreateIndexer(projectRoot3, host) {
|
|
10244
|
+
const key = getIndexerCacheKey(projectRoot3, host);
|
|
9051
10245
|
const cached = indexerCache.get(key);
|
|
9052
10246
|
if (cached) {
|
|
9053
10247
|
return cached;
|
|
9054
10248
|
}
|
|
9055
|
-
const config = parseConfig(loadRuntimeConfig(
|
|
9056
|
-
const indexer = new Indexer(
|
|
10249
|
+
const config = parseConfig(loadRuntimeConfig(projectRoot3, host));
|
|
10250
|
+
const indexer = new Indexer(projectRoot3, config, host);
|
|
9057
10251
|
indexerCache.set(key, indexer);
|
|
9058
10252
|
configCache.set(key, config);
|
|
9059
10253
|
return indexer;
|
|
9060
10254
|
}
|
|
9061
|
-
function getIndexerForProject(
|
|
9062
|
-
const root = getProjectRoot(
|
|
10255
|
+
function getIndexerForProject(projectRoot3, host = "opencode") {
|
|
10256
|
+
const root = getProjectRoot(projectRoot3, host);
|
|
9063
10257
|
return getOrCreateIndexer(root, host);
|
|
9064
10258
|
}
|
|
9065
|
-
function refreshIndexerForDirectory(
|
|
9066
|
-
const key = getIndexerCacheKey(
|
|
9067
|
-
const indexer = new Indexer(
|
|
10259
|
+
function refreshIndexerForDirectory(projectRoot3, host = "opencode", config = parseConfig(loadRuntimeConfig(projectRoot3, host))) {
|
|
10260
|
+
const key = getIndexerCacheKey(projectRoot3, host);
|
|
10261
|
+
const indexer = new Indexer(projectRoot3, config, host);
|
|
9068
10262
|
indexerCache.set(key, indexer);
|
|
9069
10263
|
configCache.set(key, config);
|
|
9070
10264
|
}
|
|
9071
|
-
function shouldForceLocalizeProjectIndex(
|
|
9072
|
-
const root = getProjectRoot(
|
|
9073
|
-
const localIndexPath =
|
|
9074
|
-
if ((0,
|
|
10265
|
+
function shouldForceLocalizeProjectIndex(projectRoot3, host = "opencode") {
|
|
10266
|
+
const root = getProjectRoot(projectRoot3, host);
|
|
10267
|
+
const localIndexPath = path16.join(root, getHostProjectIndexRelativePath(host));
|
|
10268
|
+
if ((0, import_fs10.existsSync)(localIndexPath)) {
|
|
9075
10269
|
return false;
|
|
9076
10270
|
}
|
|
9077
10271
|
const inheritedIndexPath = resolveWorktreeFallbackProjectIndexPath(root, host);
|
|
9078
10272
|
return inheritedIndexPath !== null;
|
|
9079
10273
|
}
|
|
9080
|
-
async function searchCodebase(
|
|
9081
|
-
const indexer = getIndexerForProject(
|
|
10274
|
+
async function searchCodebase(projectRoot3, host, query, options = {}) {
|
|
10275
|
+
const indexer = getIndexerForProject(projectRoot3, host);
|
|
9082
10276
|
return indexer.search(query, options.limit, {
|
|
9083
10277
|
fileType: options.fileType,
|
|
9084
10278
|
directory: options.directory,
|
|
9085
10279
|
chunkType: options.chunkType,
|
|
9086
10280
|
contextLines: options.contextLines,
|
|
9087
10281
|
metadataOnly: options.metadataOnly,
|
|
9088
|
-
definitionIntent: options.definitionIntent
|
|
10282
|
+
definitionIntent: options.definitionIntent,
|
|
10283
|
+
blameAuthor: options.blameAuthor,
|
|
10284
|
+
blameSha: options.blameSha,
|
|
10285
|
+
blameSince: options.blameSince
|
|
9089
10286
|
});
|
|
9090
10287
|
}
|
|
9091
|
-
async function findSimilarCode(
|
|
9092
|
-
const indexer = getIndexerForProject(
|
|
10288
|
+
async function findSimilarCode(projectRoot3, host, code, options = {}) {
|
|
10289
|
+
const indexer = getIndexerForProject(projectRoot3, host);
|
|
9093
10290
|
return indexer.findSimilar(code, options.limit, {
|
|
9094
10291
|
fileType: options.fileType,
|
|
9095
10292
|
directory: options.directory,
|
|
@@ -9097,16 +10294,16 @@ async function findSimilarCode(projectRoot2, host, code, options = {}) {
|
|
|
9097
10294
|
excludeFile: options.excludeFile
|
|
9098
10295
|
});
|
|
9099
10296
|
}
|
|
9100
|
-
async function implementationLookup(
|
|
9101
|
-
const indexer = getIndexerForProject(
|
|
10297
|
+
async function implementationLookup(projectRoot3, host, query, options = {}) {
|
|
10298
|
+
const indexer = getIndexerForProject(projectRoot3, host);
|
|
9102
10299
|
return indexer.search(query, options.limit, {
|
|
9103
10300
|
fileType: options.fileType,
|
|
9104
10301
|
directory: options.directory,
|
|
9105
10302
|
definitionIntent: true
|
|
9106
10303
|
});
|
|
9107
10304
|
}
|
|
9108
|
-
async function getCallGraphData(
|
|
9109
|
-
const indexer = getIndexerForProject(
|
|
10305
|
+
async function getCallGraphData(projectRoot3, host, params) {
|
|
10306
|
+
const indexer = getIndexerForProject(projectRoot3, host);
|
|
9110
10307
|
if (params.direction === "callees") {
|
|
9111
10308
|
if (!params.symbolId) {
|
|
9112
10309
|
return { direction: "callees", callees: [], callers: [] };
|
|
@@ -9117,53 +10314,73 @@ async function getCallGraphData(projectRoot2, host, params) {
|
|
|
9117
10314
|
const callers = await indexer.getCallers(params.name, params.relationshipType);
|
|
9118
10315
|
return { direction: "callers", callers, callees: [] };
|
|
9119
10316
|
}
|
|
9120
|
-
async function getCallGraphPath(
|
|
9121
|
-
const indexer = getIndexerForProject(
|
|
10317
|
+
async function getCallGraphPath(projectRoot3, host, from, to, maxDepth) {
|
|
10318
|
+
const indexer = getIndexerForProject(projectRoot3, host);
|
|
9122
10319
|
return indexer.findCallPath(from, to, maxDepth);
|
|
9123
10320
|
}
|
|
9124
|
-
async function runIndexCodebase(
|
|
9125
|
-
const root = getProjectRoot(
|
|
10321
|
+
async function runIndexCodebase(projectRoot3, host, args, onProgress) {
|
|
10322
|
+
const root = getProjectRoot(projectRoot3, host);
|
|
9126
10323
|
const key = getIndexerCacheKey(root, host);
|
|
9127
|
-
const cachedConfig = configCache.get(key);
|
|
9128
10324
|
let indexer = getIndexerForProject(root, host);
|
|
9129
|
-
|
|
9130
|
-
|
|
9131
|
-
|
|
9132
|
-
|
|
9133
|
-
|
|
9134
|
-
|
|
9135
|
-
|
|
9136
|
-
|
|
9137
|
-
|
|
9138
|
-
|
|
9139
|
-
|
|
9140
|
-
|
|
9141
|
-
|
|
9142
|
-
|
|
9143
|
-
|
|
9144
|
-
|
|
9145
|
-
|
|
9146
|
-
|
|
9147
|
-
|
|
9148
|
-
chunksProcessed: progress.chunksProcessed,
|
|
9149
|
-
totalChunks: progress.totalChunks,
|
|
9150
|
-
percentage: calculatePercentage(progress)
|
|
10325
|
+
const runtimeConfig = configCache.get(key);
|
|
10326
|
+
try {
|
|
10327
|
+
if (args.estimateOnly) {
|
|
10328
|
+
return { kind: "estimate", estimate: await indexer.estimateCost() };
|
|
10329
|
+
}
|
|
10330
|
+
const runIndex = async (target) => {
|
|
10331
|
+
const operation = args.force ? target.forceIndex.bind(target) : target.index.bind(target);
|
|
10332
|
+
return operation((progress) => {
|
|
10333
|
+
if (onProgress) {
|
|
10334
|
+
return onProgress(formatProgressTitle(progress), {
|
|
10335
|
+
phase: progress.phase,
|
|
10336
|
+
filesProcessed: progress.filesProcessed,
|
|
10337
|
+
totalFiles: progress.totalFiles,
|
|
10338
|
+
chunksProcessed: progress.chunksProcessed,
|
|
10339
|
+
totalChunks: progress.totalChunks,
|
|
10340
|
+
percentage: calculatePercentage(progress)
|
|
10341
|
+
});
|
|
10342
|
+
}
|
|
10343
|
+
return Promise.resolve();
|
|
9151
10344
|
});
|
|
10345
|
+
};
|
|
10346
|
+
let stats;
|
|
10347
|
+
if (args.force && shouldForceLocalizeProjectIndex(root, host)) {
|
|
10348
|
+
const inheritedIndexPath = resolveProjectIndexPath(root, runtimeConfig.scope, host);
|
|
10349
|
+
stats = await withIndexLock(inheritedIndexPath, "force-index", async () => {
|
|
10350
|
+
materializeLocalProjectConfig(root, loadProjectConfigLayer(root, host), host);
|
|
10351
|
+
refreshIndexerForDirectory(root, host, runtimeConfig);
|
|
10352
|
+
indexer = getIndexerForProject(root, host);
|
|
10353
|
+
return runIndex(indexer);
|
|
10354
|
+
}, { completeRecoveries: false });
|
|
10355
|
+
} else {
|
|
10356
|
+
stats = await runIndex(indexer);
|
|
9152
10357
|
}
|
|
9153
|
-
return
|
|
9154
|
-
})
|
|
9155
|
-
|
|
10358
|
+
return { kind: "stats", stats };
|
|
10359
|
+
} catch (error) {
|
|
10360
|
+
const busyResult = getIndexBusyResult(error);
|
|
10361
|
+
if (!busyResult) throw error;
|
|
10362
|
+
return busyResult;
|
|
10363
|
+
}
|
|
9156
10364
|
}
|
|
9157
|
-
async function getIndexStatus(
|
|
9158
|
-
const indexer = getIndexerForProject(
|
|
10365
|
+
async function getIndexStatus(projectRoot3, host) {
|
|
10366
|
+
const indexer = getIndexerForProject(projectRoot3, host);
|
|
9159
10367
|
return indexer.getStatus();
|
|
9160
10368
|
}
|
|
9161
|
-
async function getIndexHealthCheck(
|
|
9162
|
-
const indexer = getIndexerForProject(
|
|
10369
|
+
async function getIndexHealthCheck(projectRoot3, host) {
|
|
10370
|
+
const indexer = getIndexerForProject(projectRoot3, host);
|
|
9163
10371
|
return indexer.healthCheck();
|
|
9164
10372
|
}
|
|
9165
|
-
async function
|
|
9166
|
-
|
|
10373
|
+
async function runIndexHealthCheck(projectRoot3, host) {
|
|
10374
|
+
try {
|
|
10375
|
+
return { kind: "health", health: await getIndexHealthCheck(projectRoot3, host) };
|
|
10376
|
+
} catch (error) {
|
|
10377
|
+
const busyResult = getIndexBusyResult(error);
|
|
10378
|
+
if (!busyResult) throw error;
|
|
10379
|
+
return busyResult;
|
|
10380
|
+
}
|
|
10381
|
+
}
|
|
10382
|
+
async function getPrImpact(projectRoot3, host, params) {
|
|
10383
|
+
const indexer = getIndexerForProject(projectRoot3, host);
|
|
9167
10384
|
return indexer.getPrImpact({
|
|
9168
10385
|
pr: params.pr,
|
|
9169
10386
|
branch: params.branch,
|
|
@@ -9173,8 +10390,8 @@ async function getPrImpact(projectRoot2, host, params) {
|
|
|
9173
10390
|
direction: params.direction
|
|
9174
10391
|
});
|
|
9175
10392
|
}
|
|
9176
|
-
async function getIndexMetrics(
|
|
9177
|
-
const indexer = getIndexerForProject(
|
|
10393
|
+
async function getIndexMetrics(projectRoot3, host) {
|
|
10394
|
+
const indexer = getIndexerForProject(projectRoot3, host);
|
|
9178
10395
|
const logger = indexer.getLogger();
|
|
9179
10396
|
if (!logger.isEnabled()) {
|
|
9180
10397
|
return {
|
|
@@ -9196,8 +10413,8 @@ async function getIndexMetrics(projectRoot2, host) {
|
|
|
9196
10413
|
text: logger.formatMetrics()
|
|
9197
10414
|
};
|
|
9198
10415
|
}
|
|
9199
|
-
async function getIndexLogs(
|
|
9200
|
-
const indexer = getIndexerForProject(
|
|
10416
|
+
async function getIndexLogs(projectRoot3, host, args) {
|
|
10417
|
+
const indexer = getIndexerForProject(projectRoot3, host);
|
|
9201
10418
|
const logger = indexer.getLogger();
|
|
9202
10419
|
if (!logger.isEnabled()) {
|
|
9203
10420
|
return {
|
|
@@ -9219,24 +10436,24 @@ async function getIndexLogs(projectRoot2, host, args) {
|
|
|
9219
10436
|
text: "No logs recorded yet. Logs are captured during indexing and search operations."
|
|
9220
10437
|
};
|
|
9221
10438
|
}
|
|
9222
|
-
const
|
|
10439
|
+
const text3 = logs.map((entry) => {
|
|
9223
10440
|
const dataStr = entry.data ? ` ${JSON.stringify(entry.data)}` : "";
|
|
9224
10441
|
return `[${entry.timestamp}] [${entry.level.toUpperCase()}] [${entry.category}] ${entry.message}${dataStr}`;
|
|
9225
10442
|
}).join("\n");
|
|
9226
|
-
return { kind: "entries", text:
|
|
10443
|
+
return { kind: "entries", text: text3 };
|
|
9227
10444
|
}
|
|
9228
|
-
function addKnowledgeBase(
|
|
9229
|
-
const root = getProjectRoot(
|
|
10445
|
+
function addKnowledgeBase(projectRoot3, host, knowledgeBasePath) {
|
|
10446
|
+
const root = getProjectRoot(projectRoot3, host);
|
|
9230
10447
|
const inputPath = knowledgeBasePath.trim();
|
|
9231
|
-
const normalizedPath =
|
|
9232
|
-
|
|
10448
|
+
const normalizedPath = path16.resolve(
|
|
10449
|
+
path16.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, root)
|
|
9233
10450
|
);
|
|
9234
|
-
if (!(0,
|
|
10451
|
+
if (!(0, import_fs10.existsSync)(normalizedPath)) {
|
|
9235
10452
|
return `Error: Directory does not exist: ${normalizedPath}`;
|
|
9236
10453
|
}
|
|
9237
10454
|
let realPath;
|
|
9238
10455
|
try {
|
|
9239
|
-
realPath = (0,
|
|
10456
|
+
realPath = (0, import_fs10.realpathSync)(normalizedPath);
|
|
9240
10457
|
} catch {
|
|
9241
10458
|
return `Error: Cannot resolve path: ${normalizedPath}`;
|
|
9242
10459
|
}
|
|
@@ -9265,13 +10482,13 @@ function addKnowledgeBase(projectRoot2, host, knowledgeBasePath) {
|
|
|
9265
10482
|
}
|
|
9266
10483
|
}
|
|
9267
10484
|
for (const dotDir of sensitiveDotDirs) {
|
|
9268
|
-
const sensitiveDir =
|
|
10485
|
+
const sensitiveDir = path16.join(homeDir, dotDir);
|
|
9269
10486
|
if (sensitiveDir && (realPath === sensitiveDir || realPath.startsWith(`${sensitiveDir}/`))) {
|
|
9270
10487
|
return `Error: Adding sensitive directory as knowledge base is not allowed: ${normalizedPath}`;
|
|
9271
10488
|
}
|
|
9272
10489
|
}
|
|
9273
10490
|
try {
|
|
9274
|
-
const stat = (0,
|
|
10491
|
+
const stat = (0, import_fs10.statSync)(normalizedPath);
|
|
9275
10492
|
if (!stat.isDirectory()) {
|
|
9276
10493
|
return `Error: Path is not a directory: ${normalizedPath}`;
|
|
9277
10494
|
}
|
|
@@ -9298,8 +10515,8 @@ function addKnowledgeBase(projectRoot2, host, knowledgeBasePath) {
|
|
|
9298
10515
|
Run /index to rebuild the index with the new knowledge base.`;
|
|
9299
10516
|
return result;
|
|
9300
10517
|
}
|
|
9301
|
-
function listKnowledgeBases(
|
|
9302
|
-
const root = getProjectRoot(
|
|
10518
|
+
function listKnowledgeBases(projectRoot3, host) {
|
|
10519
|
+
const root = getProjectRoot(projectRoot3, host);
|
|
9303
10520
|
const config = loadRuntimeConfig(root, host);
|
|
9304
10521
|
const knowledgeBases = Array.isArray(config.knowledgeBases) ? config.knowledgeBases : [];
|
|
9305
10522
|
if (knowledgeBases.length === 0) {
|
|
@@ -9311,7 +10528,7 @@ function listKnowledgeBases(projectRoot2, host) {
|
|
|
9311
10528
|
for (let i = 0; i < knowledgeBases.length; i++) {
|
|
9312
10529
|
const kb = knowledgeBases[i];
|
|
9313
10530
|
const resolvedPath = resolveKnowledgeBasePath(kb, root);
|
|
9314
|
-
const exists = (0,
|
|
10531
|
+
const exists = (0, import_fs10.existsSync)(resolvedPath);
|
|
9315
10532
|
result += `[${i + 1}] ${kb}
|
|
9316
10533
|
`;
|
|
9317
10534
|
result += ` Resolved: ${resolvedPath}
|
|
@@ -9320,7 +10537,7 @@ function listKnowledgeBases(projectRoot2, host) {
|
|
|
9320
10537
|
`;
|
|
9321
10538
|
if (exists) {
|
|
9322
10539
|
try {
|
|
9323
|
-
const stat = (0,
|
|
10540
|
+
const stat = (0, import_fs10.statSync)(resolvedPath);
|
|
9324
10541
|
result += ` Type: ${stat.isDirectory() ? "Directory" : "File"}
|
|
9325
10542
|
`;
|
|
9326
10543
|
} catch {
|
|
@@ -9328,7 +10545,7 @@ function listKnowledgeBases(projectRoot2, host) {
|
|
|
9328
10545
|
}
|
|
9329
10546
|
result += "\n";
|
|
9330
10547
|
}
|
|
9331
|
-
const hasHostConfig = (0,
|
|
10548
|
+
const hasHostConfig = (0, import_fs10.existsSync)(path16.join(root, getHostProjectConfigRelativePath(host)));
|
|
9332
10549
|
if (hasHostConfig) {
|
|
9333
10550
|
result += `
|
|
9334
10551
|
Config sources: 1 file(s).`;
|
|
@@ -9337,8 +10554,8 @@ Config sources: 1 file(s).`;
|
|
|
9337
10554
|
Config file: ${getConfigPath(root, host)}`;
|
|
9338
10555
|
return result;
|
|
9339
10556
|
}
|
|
9340
|
-
function removeKnowledgeBase(
|
|
9341
|
-
const root = getProjectRoot(
|
|
10557
|
+
function removeKnowledgeBase(projectRoot3, host, knowledgeBasePath) {
|
|
10558
|
+
const root = getProjectRoot(projectRoot3, host);
|
|
9342
10559
|
const config = loadEditableConfig(root, host);
|
|
9343
10560
|
const knowledgeBases = Array.isArray(config.knowledgeBases) ? config.knowledgeBases : [];
|
|
9344
10561
|
const index = findKnowledgeBasePathIndex(knowledgeBases, knowledgeBasePath, root);
|
|
@@ -9361,17 +10578,9 @@ Run /index to rebuild the index without the removed knowledge base.`;
|
|
|
9361
10578
|
return result;
|
|
9362
10579
|
}
|
|
9363
10580
|
|
|
9364
|
-
// src/pi-
|
|
10581
|
+
// src/pi-call-graph.ts
|
|
10582
|
+
var import_typebox = require("typebox");
|
|
9365
10583
|
var HOST = "pi";
|
|
9366
|
-
var ChunkType = import_typebox.Type.Union([
|
|
9367
|
-
import_typebox.Type.Literal("function"),
|
|
9368
|
-
import_typebox.Type.Literal("class"),
|
|
9369
|
-
import_typebox.Type.Literal("method"),
|
|
9370
|
-
import_typebox.Type.Literal("interface"),
|
|
9371
|
-
import_typebox.Type.Literal("type"),
|
|
9372
|
-
import_typebox.Type.Literal("module"),
|
|
9373
|
-
import_typebox.Type.Literal("block")
|
|
9374
|
-
]);
|
|
9375
10584
|
var RelationshipType = import_typebox.Type.Union([
|
|
9376
10585
|
import_typebox.Type.Literal("Call"),
|
|
9377
10586
|
import_typebox.Type.Literal("MethodCall"),
|
|
@@ -9380,214 +10589,250 @@ var RelationshipType = import_typebox.Type.Union([
|
|
|
9380
10589
|
import_typebox.Type.Literal("Inherits"),
|
|
9381
10590
|
import_typebox.Type.Literal("Implements")
|
|
9382
10591
|
]);
|
|
9383
|
-
function text(
|
|
9384
|
-
return { content: [{ type: "text", text:
|
|
10592
|
+
function text(text3, details) {
|
|
10593
|
+
return { content: [{ type: "text", text: text3 }], details };
|
|
9385
10594
|
}
|
|
9386
10595
|
function projectRoot(ctx) {
|
|
9387
10596
|
return ctx?.cwd ?? process.cwd();
|
|
9388
10597
|
}
|
|
10598
|
+
function registerPiCallGraphTools(pi) {
|
|
10599
|
+
pi.registerTool({
|
|
10600
|
+
name: "call_graph",
|
|
10601
|
+
label: "Call Graph",
|
|
10602
|
+
description: "Find callers or callees of a function/method in the indexed call graph.",
|
|
10603
|
+
parameters: import_typebox.Type.Object({
|
|
10604
|
+
name: import_typebox.Type.String(),
|
|
10605
|
+
direction: import_typebox.Type.Optional(import_typebox.Type.Union([import_typebox.Type.Literal("callers"), import_typebox.Type.Literal("callees")])),
|
|
10606
|
+
symbolId: import_typebox.Type.Optional(import_typebox.Type.String()),
|
|
10607
|
+
relationshipType: import_typebox.Type.Optional(RelationshipType)
|
|
10608
|
+
}),
|
|
10609
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
10610
|
+
const result = await getCallGraphData(projectRoot(ctx), HOST, params);
|
|
10611
|
+
if (result.direction === "callees") {
|
|
10612
|
+
return text(
|
|
10613
|
+
params.symbolId ? formatCallGraphCallees(params.symbolId, result.callees, params.relationshipType) : "Error: 'symbolId' is required when direction is 'callees'.",
|
|
10614
|
+
result
|
|
10615
|
+
);
|
|
10616
|
+
}
|
|
10617
|
+
return text(formatCallGraphCallers(params.name, result.callers, params.relationshipType), result);
|
|
10618
|
+
}
|
|
10619
|
+
});
|
|
10620
|
+
pi.registerTool({
|
|
10621
|
+
name: "call_graph_path",
|
|
10622
|
+
label: "Call Graph Path",
|
|
10623
|
+
description: "Find a call path between two functions/methods.",
|
|
10624
|
+
parameters: import_typebox.Type.Object({
|
|
10625
|
+
from: import_typebox.Type.String(),
|
|
10626
|
+
to: import_typebox.Type.String(),
|
|
10627
|
+
maxDepth: import_typebox.Type.Optional(import_typebox.Type.Number({ default: 10 }))
|
|
10628
|
+
}),
|
|
10629
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
10630
|
+
const result = await getCallGraphPath(projectRoot(ctx), HOST, params.from, params.to, params.maxDepth);
|
|
10631
|
+
return text(formatCallGraphPath(params.from, params.to, result), result);
|
|
10632
|
+
}
|
|
10633
|
+
});
|
|
10634
|
+
}
|
|
10635
|
+
|
|
10636
|
+
// src/pi-extension.ts
|
|
10637
|
+
var HOST2 = "pi";
|
|
10638
|
+
var ChunkType = import_typebox2.Type.Union([
|
|
10639
|
+
import_typebox2.Type.Literal("function"),
|
|
10640
|
+
import_typebox2.Type.Literal("class"),
|
|
10641
|
+
import_typebox2.Type.Literal("method"),
|
|
10642
|
+
import_typebox2.Type.Literal("interface"),
|
|
10643
|
+
import_typebox2.Type.Literal("type"),
|
|
10644
|
+
import_typebox2.Type.Literal("module"),
|
|
10645
|
+
import_typebox2.Type.Literal("block")
|
|
10646
|
+
]);
|
|
10647
|
+
function text2(text3, details) {
|
|
10648
|
+
return { content: [{ type: "text", text: text3 }], details };
|
|
10649
|
+
}
|
|
10650
|
+
function projectRoot2(ctx) {
|
|
10651
|
+
return ctx?.cwd ?? process.cwd();
|
|
10652
|
+
}
|
|
9389
10653
|
function codebaseIndexPiExtension(pi) {
|
|
9390
10654
|
pi.registerTool({
|
|
9391
10655
|
name: "codebase_search",
|
|
9392
10656
|
label: "Codebase Search",
|
|
9393
10657
|
description: "Semantic search over the indexed codebase. Describe behavior, not syntax.",
|
|
9394
|
-
parameters:
|
|
9395
|
-
query:
|
|
9396
|
-
limit:
|
|
9397
|
-
fileType:
|
|
9398
|
-
directory:
|
|
9399
|
-
chunkType:
|
|
9400
|
-
contextLines:
|
|
10658
|
+
parameters: import_typebox2.Type.Object({
|
|
10659
|
+
query: import_typebox2.Type.String({ description: "Natural language description of what code you're looking for" }),
|
|
10660
|
+
limit: import_typebox2.Type.Optional(import_typebox2.Type.Number({ description: "Maximum results (default: 10)" })),
|
|
10661
|
+
fileType: import_typebox2.Type.Optional(import_typebox2.Type.String({ description: "Filter by extension, e.g. ts, py, rs" })),
|
|
10662
|
+
directory: import_typebox2.Type.Optional(import_typebox2.Type.String({ description: "Filter by directory path" })),
|
|
10663
|
+
chunkType: import_typebox2.Type.Optional(ChunkType),
|
|
10664
|
+
contextLines: import_typebox2.Type.Optional(import_typebox2.Type.Number({ description: "Extra lines around each match" })),
|
|
10665
|
+
blameAuthor: import_typebox2.Type.Optional(import_typebox2.Type.String({ description: "Filter by git blame author name or email" })),
|
|
10666
|
+
blameSha: import_typebox2.Type.Optional(import_typebox2.Type.String({ description: "Filter by git blame commit SHA or prefix" })),
|
|
10667
|
+
blameSince: import_typebox2.Type.Optional(import_typebox2.Type.String({ description: "Filter to chunks last changed on or after this date" }))
|
|
9401
10668
|
}),
|
|
9402
10669
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
9403
|
-
const results = await searchCodebase(
|
|
9404
|
-
return
|
|
10670
|
+
const results = await searchCodebase(projectRoot2(ctx), HOST2, params.query, params);
|
|
10671
|
+
return text2(formatSearchResults(results), results);
|
|
9405
10672
|
}
|
|
9406
10673
|
});
|
|
9407
10674
|
pi.registerTool({
|
|
9408
10675
|
name: "codebase_peek",
|
|
9409
10676
|
label: "Codebase Peek",
|
|
9410
10677
|
description: "Semantic search returning only metadata (file, lines, symbol) to save tokens.",
|
|
9411
|
-
parameters:
|
|
9412
|
-
query:
|
|
9413
|
-
limit:
|
|
9414
|
-
fileType:
|
|
9415
|
-
directory:
|
|
9416
|
-
chunkType:
|
|
10678
|
+
parameters: import_typebox2.Type.Object({
|
|
10679
|
+
query: import_typebox2.Type.String(),
|
|
10680
|
+
limit: import_typebox2.Type.Optional(import_typebox2.Type.Number()),
|
|
10681
|
+
fileType: import_typebox2.Type.Optional(import_typebox2.Type.String()),
|
|
10682
|
+
directory: import_typebox2.Type.Optional(import_typebox2.Type.String()),
|
|
10683
|
+
chunkType: import_typebox2.Type.Optional(ChunkType),
|
|
10684
|
+
blameAuthor: import_typebox2.Type.Optional(import_typebox2.Type.String()),
|
|
10685
|
+
blameSha: import_typebox2.Type.Optional(import_typebox2.Type.String()),
|
|
10686
|
+
blameSince: import_typebox2.Type.Optional(import_typebox2.Type.String())
|
|
9417
10687
|
}),
|
|
9418
10688
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
9419
|
-
const results = await searchCodebase(
|
|
9420
|
-
return
|
|
10689
|
+
const results = await searchCodebase(projectRoot2(ctx), HOST2, params.query, { ...params, metadataOnly: true });
|
|
10690
|
+
return text2(formatSearchResults(results), results);
|
|
9421
10691
|
}
|
|
9422
10692
|
});
|
|
9423
10693
|
pi.registerTool({
|
|
9424
10694
|
name: "find_similar",
|
|
9425
10695
|
label: "Find Similar Code",
|
|
9426
10696
|
description: "Find code similar to a snippet for duplicate detection and pattern discovery.",
|
|
9427
|
-
parameters:
|
|
9428
|
-
code:
|
|
9429
|
-
limit:
|
|
9430
|
-
fileType:
|
|
9431
|
-
directory:
|
|
9432
|
-
chunkType:
|
|
9433
|
-
excludeFile:
|
|
10697
|
+
parameters: import_typebox2.Type.Object({
|
|
10698
|
+
code: import_typebox2.Type.String({ description: "Code snippet to compare" }),
|
|
10699
|
+
limit: import_typebox2.Type.Optional(import_typebox2.Type.Number()),
|
|
10700
|
+
fileType: import_typebox2.Type.Optional(import_typebox2.Type.String()),
|
|
10701
|
+
directory: import_typebox2.Type.Optional(import_typebox2.Type.String()),
|
|
10702
|
+
chunkType: import_typebox2.Type.Optional(ChunkType),
|
|
10703
|
+
excludeFile: import_typebox2.Type.Optional(import_typebox2.Type.String())
|
|
9434
10704
|
}),
|
|
9435
10705
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
9436
|
-
const results = await findSimilarCode(
|
|
9437
|
-
return
|
|
10706
|
+
const results = await findSimilarCode(projectRoot2(ctx), HOST2, params.code, params);
|
|
10707
|
+
return text2(formatSearchResults(results, "similarity"), results);
|
|
9438
10708
|
}
|
|
9439
10709
|
});
|
|
9440
10710
|
pi.registerTool({
|
|
9441
10711
|
name: "implementation_lookup",
|
|
9442
10712
|
label: "Implementation Lookup",
|
|
9443
10713
|
description: "Find likely symbol definitions or implementations by name or natural language.",
|
|
9444
|
-
parameters:
|
|
9445
|
-
query:
|
|
9446
|
-
limit:
|
|
9447
|
-
fileType:
|
|
9448
|
-
directory:
|
|
10714
|
+
parameters: import_typebox2.Type.Object({
|
|
10715
|
+
query: import_typebox2.Type.String(),
|
|
10716
|
+
limit: import_typebox2.Type.Optional(import_typebox2.Type.Number()),
|
|
10717
|
+
fileType: import_typebox2.Type.Optional(import_typebox2.Type.String()),
|
|
10718
|
+
directory: import_typebox2.Type.Optional(import_typebox2.Type.String())
|
|
9449
10719
|
}),
|
|
9450
10720
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
9451
|
-
const results = await implementationLookup(
|
|
9452
|
-
return
|
|
10721
|
+
const results = await implementationLookup(projectRoot2(ctx), HOST2, params.query, params);
|
|
10722
|
+
return text2(formatDefinitionLookup(results, params.query), results);
|
|
9453
10723
|
}
|
|
9454
10724
|
});
|
|
9455
10725
|
pi.registerTool({
|
|
9456
10726
|
name: "index_codebase",
|
|
9457
10727
|
label: "Index Codebase",
|
|
9458
10728
|
description: "Build or refresh the semantic codebase index.",
|
|
9459
|
-
parameters:
|
|
9460
|
-
force:
|
|
9461
|
-
estimateOnly:
|
|
9462
|
-
verbose:
|
|
10729
|
+
parameters: import_typebox2.Type.Object({
|
|
10730
|
+
force: import_typebox2.Type.Optional(import_typebox2.Type.Boolean({ default: false })),
|
|
10731
|
+
estimateOnly: import_typebox2.Type.Optional(import_typebox2.Type.Boolean({ default: false })),
|
|
10732
|
+
verbose: import_typebox2.Type.Optional(import_typebox2.Type.Boolean({ default: false }))
|
|
9463
10733
|
}),
|
|
9464
10734
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
9465
|
-
const result = await runIndexCodebase(
|
|
9466
|
-
|
|
10735
|
+
const result = await runIndexCodebase(projectRoot2(ctx), HOST2, params);
|
|
10736
|
+
if (result.kind === "estimate") return text2(formatCostEstimate(result.estimate), result.estimate);
|
|
10737
|
+
if (result.kind === "busy") return text2(result.text, { code: "INDEX_BUSY" });
|
|
10738
|
+
return text2(formatIndexStats(result.stats, params.verbose ?? false), result.stats);
|
|
9467
10739
|
}
|
|
9468
10740
|
});
|
|
9469
10741
|
pi.registerTool({
|
|
9470
10742
|
name: "index_status",
|
|
9471
10743
|
label: "Index Status",
|
|
9472
10744
|
description: "Check index health and current status.",
|
|
9473
|
-
parameters:
|
|
10745
|
+
parameters: import_typebox2.Type.Object({}),
|
|
9474
10746
|
async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
|
|
9475
|
-
const status = await getIndexStatus(
|
|
9476
|
-
return
|
|
10747
|
+
const status = await getIndexStatus(projectRoot2(ctx), HOST2);
|
|
10748
|
+
return text2(formatStatus(status), status);
|
|
9477
10749
|
}
|
|
9478
10750
|
});
|
|
9479
10751
|
pi.registerTool({
|
|
9480
10752
|
name: "index_health_check",
|
|
9481
10753
|
label: "Index Health Check",
|
|
9482
10754
|
description: "Garbage collect orphaned embeddings/chunks and report health.",
|
|
9483
|
-
parameters:
|
|
10755
|
+
parameters: import_typebox2.Type.Object({}),
|
|
9484
10756
|
async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
|
|
9485
|
-
const result = await
|
|
9486
|
-
return
|
|
10757
|
+
const result = await runIndexHealthCheck(projectRoot2(ctx), HOST2);
|
|
10758
|
+
if (result.kind === "busy") return text2(result.text, { code: "INDEX_BUSY" });
|
|
10759
|
+
return text2(formatHealthCheck(result.health), result.health);
|
|
9487
10760
|
}
|
|
9488
10761
|
});
|
|
9489
10762
|
pi.registerTool({
|
|
9490
10763
|
name: "index_metrics",
|
|
9491
10764
|
label: "Index Metrics",
|
|
9492
10765
|
description: "Return collected performance metrics when debug metrics are enabled.",
|
|
9493
|
-
parameters:
|
|
10766
|
+
parameters: import_typebox2.Type.Object({}),
|
|
9494
10767
|
async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
|
|
9495
|
-
const result = await getIndexMetrics(
|
|
9496
|
-
return
|
|
10768
|
+
const result = await getIndexMetrics(projectRoot2(ctx), HOST2);
|
|
10769
|
+
return text2(result.text, result);
|
|
9497
10770
|
}
|
|
9498
10771
|
});
|
|
9499
10772
|
pi.registerTool({
|
|
9500
10773
|
name: "index_logs",
|
|
9501
10774
|
label: "Index Logs",
|
|
9502
10775
|
description: "Return recent debug logs when debug logging is enabled.",
|
|
9503
|
-
parameters:
|
|
9504
|
-
limit:
|
|
9505
|
-
category:
|
|
9506
|
-
|
|
9507
|
-
|
|
9508
|
-
|
|
9509
|
-
|
|
9510
|
-
|
|
9511
|
-
|
|
10776
|
+
parameters: import_typebox2.Type.Object({
|
|
10777
|
+
limit: import_typebox2.Type.Optional(import_typebox2.Type.Number()),
|
|
10778
|
+
category: import_typebox2.Type.Optional(import_typebox2.Type.Union([
|
|
10779
|
+
import_typebox2.Type.Literal("search"),
|
|
10780
|
+
import_typebox2.Type.Literal("embedding"),
|
|
10781
|
+
import_typebox2.Type.Literal("cache"),
|
|
10782
|
+
import_typebox2.Type.Literal("gc"),
|
|
10783
|
+
import_typebox2.Type.Literal("branch"),
|
|
10784
|
+
import_typebox2.Type.Literal("general")
|
|
9512
10785
|
])),
|
|
9513
|
-
level:
|
|
9514
|
-
}),
|
|
9515
|
-
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
9516
|
-
const result = await getIndexLogs(projectRoot(ctx), HOST, params);
|
|
9517
|
-
return text(result.text, result);
|
|
9518
|
-
}
|
|
9519
|
-
});
|
|
9520
|
-
pi.registerTool({
|
|
9521
|
-
name: "call_graph",
|
|
9522
|
-
label: "Call Graph",
|
|
9523
|
-
description: "Find callers or callees of a function/method in the indexed call graph.",
|
|
9524
|
-
parameters: import_typebox.Type.Object({
|
|
9525
|
-
name: import_typebox.Type.String(),
|
|
9526
|
-
direction: import_typebox.Type.Optional(import_typebox.Type.Union([import_typebox.Type.Literal("callers"), import_typebox.Type.Literal("callees")])),
|
|
9527
|
-
symbolId: import_typebox.Type.Optional(import_typebox.Type.String()),
|
|
9528
|
-
relationshipType: import_typebox.Type.Optional(RelationshipType)
|
|
9529
|
-
}),
|
|
9530
|
-
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
9531
|
-
const result = await getCallGraphData(projectRoot(ctx), HOST, params);
|
|
9532
|
-
return text(JSON.stringify(result, null, 2), result);
|
|
9533
|
-
}
|
|
9534
|
-
});
|
|
9535
|
-
pi.registerTool({
|
|
9536
|
-
name: "call_graph_path",
|
|
9537
|
-
label: "Call Graph Path",
|
|
9538
|
-
description: "Find a call path between two functions/methods.",
|
|
9539
|
-
parameters: import_typebox.Type.Object({
|
|
9540
|
-
from: import_typebox.Type.String(),
|
|
9541
|
-
to: import_typebox.Type.String(),
|
|
9542
|
-
maxDepth: import_typebox.Type.Optional(import_typebox.Type.Number({ default: 10 }))
|
|
10786
|
+
level: import_typebox2.Type.Optional(import_typebox2.Type.Union([import_typebox2.Type.Literal("error"), import_typebox2.Type.Literal("warn"), import_typebox2.Type.Literal("info"), import_typebox2.Type.Literal("debug")]))
|
|
9543
10787
|
}),
|
|
9544
10788
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
9545
|
-
const result = await
|
|
9546
|
-
return
|
|
10789
|
+
const result = await getIndexLogs(projectRoot2(ctx), HOST2, params);
|
|
10790
|
+
return text2(result.text, result);
|
|
9547
10791
|
}
|
|
9548
10792
|
});
|
|
10793
|
+
registerPiCallGraphTools(pi);
|
|
9549
10794
|
pi.registerTool({
|
|
9550
10795
|
name: "pr_impact",
|
|
9551
10796
|
label: "PR Impact",
|
|
9552
10797
|
description: "Analyze PR or branch impact through changed symbols and call graph neighborhoods.",
|
|
9553
|
-
parameters:
|
|
9554
|
-
pr:
|
|
9555
|
-
branch:
|
|
9556
|
-
maxDepth:
|
|
9557
|
-
hubThreshold:
|
|
9558
|
-
checkConflicts:
|
|
9559
|
-
direction:
|
|
10798
|
+
parameters: import_typebox2.Type.Object({
|
|
10799
|
+
pr: import_typebox2.Type.Optional(import_typebox2.Type.Number()),
|
|
10800
|
+
branch: import_typebox2.Type.Optional(import_typebox2.Type.String()),
|
|
10801
|
+
maxDepth: import_typebox2.Type.Optional(import_typebox2.Type.Number({ default: 5 })),
|
|
10802
|
+
hubThreshold: import_typebox2.Type.Optional(import_typebox2.Type.Number({ default: 10 })),
|
|
10803
|
+
checkConflicts: import_typebox2.Type.Optional(import_typebox2.Type.Boolean({ default: false })),
|
|
10804
|
+
direction: import_typebox2.Type.Optional(import_typebox2.Type.Union([import_typebox2.Type.Literal("callers"), import_typebox2.Type.Literal("callees"), import_typebox2.Type.Literal("both")]))
|
|
9560
10805
|
}),
|
|
9561
10806
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
9562
|
-
const result = await getPrImpact(
|
|
9563
|
-
return
|
|
10807
|
+
const result = await getPrImpact(projectRoot2(ctx), HOST2, params);
|
|
10808
|
+
return text2(formatPrImpact(result), result);
|
|
9564
10809
|
}
|
|
9565
10810
|
});
|
|
9566
10811
|
pi.registerTool({
|
|
9567
10812
|
name: "knowledge_base_list",
|
|
9568
10813
|
label: "List Knowledge Bases",
|
|
9569
10814
|
description: "List configured knowledge-base paths included in the index.",
|
|
9570
|
-
parameters:
|
|
10815
|
+
parameters: import_typebox2.Type.Object({}),
|
|
9571
10816
|
async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
|
|
9572
|
-
return
|
|
10817
|
+
return text2(listKnowledgeBases(projectRoot2(ctx), HOST2));
|
|
9573
10818
|
}
|
|
9574
10819
|
});
|
|
9575
10820
|
pi.registerTool({
|
|
9576
10821
|
name: "knowledge_base_add",
|
|
9577
10822
|
label: "Add Knowledge Base",
|
|
9578
10823
|
description: "Add a knowledge-base path to the codebase index config.",
|
|
9579
|
-
parameters:
|
|
10824
|
+
parameters: import_typebox2.Type.Object({ path: import_typebox2.Type.String({ description: "File or directory path to add" }) }),
|
|
9580
10825
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
9581
|
-
return
|
|
10826
|
+
return text2(addKnowledgeBase(projectRoot2(ctx), HOST2, params.path));
|
|
9582
10827
|
}
|
|
9583
10828
|
});
|
|
9584
10829
|
pi.registerTool({
|
|
9585
10830
|
name: "knowledge_base_remove",
|
|
9586
10831
|
label: "Remove Knowledge Base",
|
|
9587
10832
|
description: "Remove a knowledge-base path from the codebase index config.",
|
|
9588
|
-
parameters:
|
|
10833
|
+
parameters: import_typebox2.Type.Object({ path: import_typebox2.Type.String({ description: "File or directory path to remove" }) }),
|
|
9589
10834
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
9590
|
-
return
|
|
10835
|
+
return text2(removeKnowledgeBase(projectRoot2(ctx), HOST2, params.path));
|
|
9591
10836
|
}
|
|
9592
10837
|
});
|
|
9593
10838
|
}
|