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/index.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(path24, 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(path24);
|
|
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 = (path24, originalPath, doThrow) => {
|
|
529
|
+
if (!isString(path24)) {
|
|
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 (!path24) {
|
|
536
536
|
return doThrow(`path must not be empty`, TypeError);
|
|
537
537
|
}
|
|
538
|
-
if (checkPath.isNotRelative(
|
|
538
|
+
if (checkPath.isNotRelative(path24)) {
|
|
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 = (path24) => REGEX_TEST_INVALID_PATH.test(path24);
|
|
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 path24 = originalPath && checkPath.convert(originalPath);
|
|
578
578
|
checkPath(
|
|
579
|
-
|
|
579
|
+
path24,
|
|
580
580
|
originalPath,
|
|
581
581
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
582
582
|
);
|
|
583
|
-
return this._t(
|
|
583
|
+
return this._t(path24, cache, checkUnignored, slices);
|
|
584
584
|
}
|
|
585
|
-
checkIgnore(
|
|
586
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
587
|
-
return this.test(
|
|
585
|
+
checkIgnore(path24) {
|
|
586
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path24)) {
|
|
587
|
+
return this.test(path24);
|
|
588
588
|
}
|
|
589
|
-
const slices =
|
|
589
|
+
const slices = path24.split(SLASH2).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(path24, false, MODE_CHECK_IGNORE);
|
|
603
603
|
}
|
|
604
|
-
_t(
|
|
605
|
-
if (
|
|
606
|
-
return cache[
|
|
604
|
+
_t(path24, cache, checkUnignored, slices) {
|
|
605
|
+
if (path24 in cache) {
|
|
606
|
+
return cache[path24];
|
|
607
607
|
}
|
|
608
608
|
if (!slices) {
|
|
609
|
-
slices =
|
|
609
|
+
slices = path24.split(SLASH2).filter(Boolean);
|
|
610
610
|
}
|
|
611
611
|
slices.pop();
|
|
612
612
|
if (!slices.length) {
|
|
613
|
-
return cache[
|
|
613
|
+
return cache[path24] = this._rules.test(path24, checkUnignored, MODE_IGNORE);
|
|
614
614
|
}
|
|
615
615
|
const parent = this._t(
|
|
616
616
|
slices.join(SLASH2) + SLASH2,
|
|
@@ -618,29 +618,29 @@ var require_ignore = __commonJS({
|
|
|
618
618
|
checkUnignored,
|
|
619
619
|
slices
|
|
620
620
|
);
|
|
621
|
-
return cache[
|
|
621
|
+
return cache[path24] = parent.ignored ? parent : this._rules.test(path24, checkUnignored, MODE_IGNORE);
|
|
622
622
|
}
|
|
623
|
-
ignores(
|
|
624
|
-
return this._test(
|
|
623
|
+
ignores(path24) {
|
|
624
|
+
return this._test(path24, this._ignoreCache, false).ignored;
|
|
625
625
|
}
|
|
626
626
|
createFilter() {
|
|
627
|
-
return (
|
|
627
|
+
return (path24) => !this.ignores(path24);
|
|
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(path24) {
|
|
634
|
+
return this._test(path24, this._testCache, true);
|
|
635
635
|
}
|
|
636
636
|
};
|
|
637
637
|
var factory = (options) => new Ignore2(options);
|
|
638
|
-
var isPathValid = (
|
|
638
|
+
var isPathValid = (path24) => checkPath(path24 && checkPath.convert(path24), path24, 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 = (path24) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path24) || isNotRelative(path24);
|
|
644
644
|
};
|
|
645
645
|
if (
|
|
646
646
|
// Detect `process` so that it can run in browsers.
|
|
@@ -661,16 +661,16 @@ __export(index_exports, {
|
|
|
661
661
|
default: () => index_default
|
|
662
662
|
});
|
|
663
663
|
module.exports = __toCommonJS(index_exports);
|
|
664
|
-
var
|
|
665
|
-
var
|
|
664
|
+
var os6 = __toESM(require("os"), 1);
|
|
665
|
+
var path23 = __toESM(require("path"), 1);
|
|
666
666
|
var import_url2 = require("url");
|
|
667
667
|
|
|
668
668
|
// src/config/constants.ts
|
|
669
669
|
var DEFAULT_INCLUDE = [
|
|
670
|
-
"**/*.{ts,tsx,js,jsx,mjs,cjs}",
|
|
670
|
+
"**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}",
|
|
671
671
|
"**/*.{py,pyi}",
|
|
672
|
-
"**/*.{go,rs,java,kt,scala}",
|
|
673
|
-
"**/*.{c,cpp,cc,h,hpp}",
|
|
672
|
+
"**/*.{go,rs,java,cs,kt,scala}",
|
|
673
|
+
"**/*.{c,cpp,cc,cxx,h,hpp,hxx}",
|
|
674
674
|
"**/*.{rb,php,inc,swift}",
|
|
675
675
|
"**/*.{cls,trigger}",
|
|
676
676
|
"**/*.{vue,svelte,astro}",
|
|
@@ -795,7 +795,8 @@ function getDefaultIndexingConfig() {
|
|
|
795
795
|
requireProjectMarker: true,
|
|
796
796
|
maxDepth: 5,
|
|
797
797
|
maxFilesPerDirectory: 100,
|
|
798
|
-
fallbackToTextOnMaxChunks: true
|
|
798
|
+
fallbackToTextOnMaxChunks: true,
|
|
799
|
+
gitBlame: { enabled: false }
|
|
799
800
|
};
|
|
800
801
|
}
|
|
801
802
|
function getDefaultSearchConfig() {
|
|
@@ -919,7 +920,10 @@ function parseConfig(raw) {
|
|
|
919
920
|
requireProjectMarker: typeof rawIndexing.requireProjectMarker === "boolean" ? rawIndexing.requireProjectMarker : defaultIndexing.requireProjectMarker,
|
|
920
921
|
maxDepth: typeof rawIndexing.maxDepth === "number" ? rawIndexing.maxDepth < -1 ? -1 : rawIndexing.maxDepth : defaultIndexing.maxDepth,
|
|
921
922
|
maxFilesPerDirectory: typeof rawIndexing.maxFilesPerDirectory === "number" ? Math.max(1, rawIndexing.maxFilesPerDirectory) : defaultIndexing.maxFilesPerDirectory,
|
|
922
|
-
fallbackToTextOnMaxChunks: typeof rawIndexing.fallbackToTextOnMaxChunks === "boolean" ? rawIndexing.fallbackToTextOnMaxChunks : defaultIndexing.fallbackToTextOnMaxChunks
|
|
923
|
+
fallbackToTextOnMaxChunks: typeof rawIndexing.fallbackToTextOnMaxChunks === "boolean" ? rawIndexing.fallbackToTextOnMaxChunks : defaultIndexing.fallbackToTextOnMaxChunks,
|
|
924
|
+
gitBlame: {
|
|
925
|
+
enabled: rawIndexing.gitBlame && typeof rawIndexing.gitBlame === "object" && typeof rawIndexing.gitBlame.enabled === "boolean" ? rawIndexing.gitBlame.enabled : defaultIndexing.gitBlame.enabled
|
|
926
|
+
}
|
|
923
927
|
};
|
|
924
928
|
const rawSearch = input.search && typeof input.search === "object" ? input.search : {};
|
|
925
929
|
const search = {
|
|
@@ -1250,6 +1254,9 @@ function getHostProjectIndexRelativePath(host) {
|
|
|
1250
1254
|
function hasHostProjectConfig(projectRoot, host) {
|
|
1251
1255
|
return (0, import_fs3.existsSync)(path3.join(projectRoot, getProjectConfigRelativePath(host)));
|
|
1252
1256
|
}
|
|
1257
|
+
function hasHostGlobalConfig(host) {
|
|
1258
|
+
return (0, import_fs3.existsSync)(getGlobalConfigPath(host));
|
|
1259
|
+
}
|
|
1253
1260
|
function getGlobalIndexPath(host = "opencode") {
|
|
1254
1261
|
switch (host) {
|
|
1255
1262
|
case "opencode":
|
|
@@ -1289,6 +1296,9 @@ function resolveGlobalIndexPath(host = "opencode") {
|
|
|
1289
1296
|
return hostIndexPath;
|
|
1290
1297
|
}
|
|
1291
1298
|
if (host !== "opencode") {
|
|
1299
|
+
if (hasHostGlobalConfig(host)) {
|
|
1300
|
+
return hostIndexPath;
|
|
1301
|
+
}
|
|
1292
1302
|
const legacyIndexPath = getGlobalIndexPath("opencode");
|
|
1293
1303
|
if ((0, import_fs3.existsSync)(legacyIndexPath)) {
|
|
1294
1304
|
return legacyIndexPath;
|
|
@@ -1587,16 +1597,432 @@ function loadMergedConfig(projectRoot, host = "opencode") {
|
|
|
1587
1597
|
return merged;
|
|
1588
1598
|
}
|
|
1589
1599
|
|
|
1600
|
+
// src/indexer/index-lock.ts
|
|
1601
|
+
var import_crypto = require("crypto");
|
|
1602
|
+
var import_fs5 = require("fs");
|
|
1603
|
+
var os2 = __toESM(require("os"), 1);
|
|
1604
|
+
var path7 = __toESM(require("path"), 1);
|
|
1605
|
+
var OWNER_FILE_NAME = "owner.json";
|
|
1606
|
+
var RECLAIM_DIRECTORY_NAME = "reclaiming";
|
|
1607
|
+
var RECOVERY_MARKER_PREFIX = "indexing.lock.recovery.";
|
|
1608
|
+
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;
|
|
1609
|
+
var VALID_OPERATIONS = /* @__PURE__ */ new Set([
|
|
1610
|
+
"initialize",
|
|
1611
|
+
"index",
|
|
1612
|
+
"force-index",
|
|
1613
|
+
"clear",
|
|
1614
|
+
"health-check",
|
|
1615
|
+
"retry-failed-batches",
|
|
1616
|
+
"recovery"
|
|
1617
|
+
]);
|
|
1618
|
+
var temporaryCounter = 0;
|
|
1619
|
+
function getErrorCode(error) {
|
|
1620
|
+
return typeof error === "object" && error !== null && "code" in error ? String(error.code) : void 0;
|
|
1621
|
+
}
|
|
1622
|
+
function retryTransientFilesystemOperation(operation) {
|
|
1623
|
+
let lastError;
|
|
1624
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
1625
|
+
try {
|
|
1626
|
+
operation();
|
|
1627
|
+
return;
|
|
1628
|
+
} catch (error) {
|
|
1629
|
+
lastError = error;
|
|
1630
|
+
const code = getErrorCode(error);
|
|
1631
|
+
if (code !== "EBUSY" && code !== "EPERM") throw error;
|
|
1632
|
+
if (attempt < 2) {
|
|
1633
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, (attempt + 1) * 10);
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
throw lastError;
|
|
1638
|
+
}
|
|
1639
|
+
function parseOwner(value) {
|
|
1640
|
+
if (typeof value !== "object" || value === null) return null;
|
|
1641
|
+
const candidate = value;
|
|
1642
|
+
if (!Number.isInteger(candidate.pid) || (candidate.pid ?? 0) <= 0) return null;
|
|
1643
|
+
if (typeof candidate.hostname !== "string" || candidate.hostname.length === 0) return null;
|
|
1644
|
+
if (typeof candidate.startedAt !== "string" || Number.isNaN(Date.parse(candidate.startedAt))) return null;
|
|
1645
|
+
if (typeof candidate.operation !== "string" || !VALID_OPERATIONS.has(candidate.operation)) return null;
|
|
1646
|
+
if (typeof candidate.token !== "string" || !UUID_PATTERN.test(candidate.token)) return null;
|
|
1647
|
+
return candidate;
|
|
1648
|
+
}
|
|
1649
|
+
function parseReclaimOwner(value) {
|
|
1650
|
+
if (typeof value !== "object" || value === null) return null;
|
|
1651
|
+
const candidate = value;
|
|
1652
|
+
if (!Number.isInteger(candidate.pid) || (candidate.pid ?? 0) <= 0) return null;
|
|
1653
|
+
if (typeof candidate.hostname !== "string" || candidate.hostname.length === 0) return null;
|
|
1654
|
+
if (typeof candidate.startedAt !== "string" || Number.isNaN(Date.parse(candidate.startedAt))) return null;
|
|
1655
|
+
if (typeof candidate.token !== "string" || !UUID_PATTERN.test(candidate.token)) return null;
|
|
1656
|
+
if (typeof candidate.expectedOwnerToken !== "string" || !UUID_PATTERN.test(candidate.expectedOwnerToken)) return null;
|
|
1657
|
+
return candidate;
|
|
1658
|
+
}
|
|
1659
|
+
function readJsonDirectory(directoryPath, parser) {
|
|
1660
|
+
try {
|
|
1661
|
+
return parser(JSON.parse((0, import_fs5.readFileSync)(path7.join(directoryPath, OWNER_FILE_NAME), "utf-8")));
|
|
1662
|
+
} catch {
|
|
1663
|
+
return null;
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
function readDirectoryOwner(lockPath) {
|
|
1667
|
+
return readJsonDirectory(lockPath, parseOwner);
|
|
1668
|
+
}
|
|
1669
|
+
function readReclaimOwner(markerPath) {
|
|
1670
|
+
return readJsonDirectory(markerPath, parseReclaimOwner);
|
|
1671
|
+
}
|
|
1672
|
+
function readRecoveryOwner(markerPath) {
|
|
1673
|
+
return readDirectoryOwner(markerPath);
|
|
1674
|
+
}
|
|
1675
|
+
function readLegacyOwner(lockPath) {
|
|
1676
|
+
try {
|
|
1677
|
+
const parsed = JSON.parse((0, import_fs5.readFileSync)(lockPath, "utf-8"));
|
|
1678
|
+
if (!Number.isInteger(parsed.pid) || Number(parsed.pid) <= 0) return null;
|
|
1679
|
+
if (typeof parsed.startedAt !== "string" || Number.isNaN(Date.parse(parsed.startedAt))) return null;
|
|
1680
|
+
return {
|
|
1681
|
+
pid: Number(parsed.pid),
|
|
1682
|
+
hostname: typeof parsed.hostname === "string" ? parsed.hostname : os2.hostname(),
|
|
1683
|
+
startedAt: parsed.startedAt,
|
|
1684
|
+
operation: typeof parsed.operation === "string" && VALID_OPERATIONS.has(parsed.operation) ? parsed.operation : "index",
|
|
1685
|
+
token: typeof parsed.token === "string" ? parsed.token : "legacy-v0.14.0"
|
|
1686
|
+
};
|
|
1687
|
+
} catch {
|
|
1688
|
+
return null;
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
function getOwnerLiveness(owner) {
|
|
1692
|
+
if (owner.hostname !== os2.hostname()) return "unknown";
|
|
1693
|
+
try {
|
|
1694
|
+
process.kill(owner.pid, 0);
|
|
1695
|
+
return "alive";
|
|
1696
|
+
} catch (error) {
|
|
1697
|
+
const code = getErrorCode(error);
|
|
1698
|
+
if (code === "ESRCH") return "dead";
|
|
1699
|
+
if (code === "EPERM") return "alive";
|
|
1700
|
+
return "unknown";
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
function sameOwner(left, right) {
|
|
1704
|
+
return left.pid === right.pid && left.hostname === right.hostname && left.token === right.token;
|
|
1705
|
+
}
|
|
1706
|
+
function sameReclaimOwner(left, right) {
|
|
1707
|
+
return left.pid === right.pid && left.hostname === right.hostname && left.token === right.token && left.expectedOwnerToken === right.expectedOwnerToken;
|
|
1708
|
+
}
|
|
1709
|
+
function publishJsonDirectory(finalPath, value) {
|
|
1710
|
+
const candidatePath = `${finalPath}.candidate.${process.pid}.${(0, import_crypto.randomUUID)()}`;
|
|
1711
|
+
(0, import_fs5.mkdirSync)(candidatePath, { mode: 448 });
|
|
1712
|
+
try {
|
|
1713
|
+
(0, import_fs5.writeFileSync)(path7.join(candidatePath, OWNER_FILE_NAME), JSON.stringify(value), {
|
|
1714
|
+
encoding: "utf-8",
|
|
1715
|
+
flag: "wx",
|
|
1716
|
+
mode: 384
|
|
1717
|
+
});
|
|
1718
|
+
if ((0, import_fs5.existsSync)(finalPath)) return false;
|
|
1719
|
+
try {
|
|
1720
|
+
(0, import_fs5.renameSync)(candidatePath, finalPath);
|
|
1721
|
+
return true;
|
|
1722
|
+
} catch (error) {
|
|
1723
|
+
if ((0, import_fs5.existsSync)(finalPath)) return false;
|
|
1724
|
+
throw error;
|
|
1725
|
+
}
|
|
1726
|
+
} finally {
|
|
1727
|
+
if ((0, import_fs5.existsSync)(candidatePath)) (0, import_fs5.rmSync)(candidatePath, { recursive: true, force: true });
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
function createOwner(operation) {
|
|
1731
|
+
return {
|
|
1732
|
+
pid: process.pid,
|
|
1733
|
+
hostname: os2.hostname(),
|
|
1734
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1735
|
+
operation,
|
|
1736
|
+
token: (0, import_crypto.randomUUID)()
|
|
1737
|
+
};
|
|
1738
|
+
}
|
|
1739
|
+
function recoveryMarkerPath(indexPath, owner) {
|
|
1740
|
+
return path7.join(indexPath, `${RECOVERY_MARKER_PREFIX}${owner.token}`);
|
|
1741
|
+
}
|
|
1742
|
+
function publishRecoveryMarker(indexPath, owner) {
|
|
1743
|
+
const markerPath = recoveryMarkerPath(indexPath, owner);
|
|
1744
|
+
if (!publishJsonDirectory(markerPath, owner)) {
|
|
1745
|
+
const markerOwner = readRecoveryOwner(markerPath);
|
|
1746
|
+
if (!markerOwner || !sameOwner(markerOwner, owner)) {
|
|
1747
|
+
throw new IndexLockContentionError(markerPath, markerOwner, "unknown-owner");
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
return markerPath;
|
|
1751
|
+
}
|
|
1752
|
+
function getPendingRecoveries(indexPath) {
|
|
1753
|
+
const recoveries = [];
|
|
1754
|
+
const markerNames = (0, import_fs5.readdirSync)(indexPath).filter((name) => {
|
|
1755
|
+
if (!name.startsWith(RECOVERY_MARKER_PREFIX)) return false;
|
|
1756
|
+
return UUID_PATTERN.test(name.slice(RECOVERY_MARKER_PREFIX.length));
|
|
1757
|
+
}).sort();
|
|
1758
|
+
for (const markerName of markerNames) {
|
|
1759
|
+
const markerPath = path7.join(indexPath, markerName);
|
|
1760
|
+
const markerToken = markerName.slice(RECOVERY_MARKER_PREFIX.length);
|
|
1761
|
+
let markerStats;
|
|
1762
|
+
try {
|
|
1763
|
+
markerStats = (0, import_fs5.lstatSync)(markerPath);
|
|
1764
|
+
} catch (error) {
|
|
1765
|
+
if (getErrorCode(error) === "ENOENT") continue;
|
|
1766
|
+
throw error;
|
|
1767
|
+
}
|
|
1768
|
+
if (!markerStats.isDirectory()) {
|
|
1769
|
+
throw new IndexLockContentionError(markerPath, null, "unknown-owner");
|
|
1770
|
+
}
|
|
1771
|
+
const owner = readRecoveryOwner(markerPath);
|
|
1772
|
+
if (!owner || owner.token !== markerToken || getOwnerLiveness(owner) !== "dead") {
|
|
1773
|
+
throw new IndexLockContentionError(markerPath, owner, "unknown-owner");
|
|
1774
|
+
}
|
|
1775
|
+
recoveries.push({ owner, markerPath });
|
|
1776
|
+
}
|
|
1777
|
+
recoveries.sort((left, right) => {
|
|
1778
|
+
const byTimestamp = left.owner.startedAt.localeCompare(right.owner.startedAt);
|
|
1779
|
+
return byTimestamp !== 0 ? byTimestamp : left.markerPath.localeCompare(right.markerPath);
|
|
1780
|
+
});
|
|
1781
|
+
return recoveries;
|
|
1782
|
+
}
|
|
1783
|
+
function cleanupDeadPublicationCandidates(indexPath) {
|
|
1784
|
+
const candidatePattern = /^indexing\.lock(?:\.recovery\.[0-9a-f-]{36})?\.candidate\.(\d+)\.[0-9a-f-]{36}$/i;
|
|
1785
|
+
for (const entry of (0, import_fs5.readdirSync)(indexPath)) {
|
|
1786
|
+
const match = candidatePattern.exec(entry);
|
|
1787
|
+
if (!match) continue;
|
|
1788
|
+
const pid = Number(match[1]);
|
|
1789
|
+
if (!Number.isInteger(pid) || pid <= 0) continue;
|
|
1790
|
+
if (getOwnerLiveness({ pid, hostname: os2.hostname() }) !== "dead") continue;
|
|
1791
|
+
(0, import_fs5.rmSync)(path7.join(indexPath, entry), { recursive: true, force: true });
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
function removeDeadReclaimMarker(lockPath, expectedOwner) {
|
|
1795
|
+
const markerPath = path7.join(lockPath, RECLAIM_DIRECTORY_NAME);
|
|
1796
|
+
const marker = readReclaimOwner(markerPath);
|
|
1797
|
+
if (!marker || marker.expectedOwnerToken !== expectedOwner.token || getOwnerLiveness(marker) !== "dead") {
|
|
1798
|
+
return false;
|
|
1799
|
+
}
|
|
1800
|
+
const currentOwner = readDirectoryOwner(lockPath);
|
|
1801
|
+
if (!currentOwner || !sameOwner(currentOwner, expectedOwner) || getOwnerLiveness(currentOwner) !== "dead") {
|
|
1802
|
+
return false;
|
|
1803
|
+
}
|
|
1804
|
+
const claimedMarkerPath = `${markerPath}.stale.${marker.pid}.${marker.token}.${(0, import_crypto.randomUUID)()}`;
|
|
1805
|
+
try {
|
|
1806
|
+
(0, import_fs5.renameSync)(markerPath, claimedMarkerPath);
|
|
1807
|
+
} catch (error) {
|
|
1808
|
+
if (getErrorCode(error) === "ENOENT") return false;
|
|
1809
|
+
throw error;
|
|
1810
|
+
}
|
|
1811
|
+
const claimedMarker = readReclaimOwner(claimedMarkerPath);
|
|
1812
|
+
const ownerAfterClaim = readDirectoryOwner(lockPath);
|
|
1813
|
+
if (!claimedMarker || !sameReclaimOwner(claimedMarker, marker) || !ownerAfterClaim || !sameOwner(ownerAfterClaim, expectedOwner) || getOwnerLiveness(ownerAfterClaim) !== "dead") {
|
|
1814
|
+
if (!(0, import_fs5.existsSync)(markerPath) && (0, import_fs5.existsSync)(claimedMarkerPath)) {
|
|
1815
|
+
(0, import_fs5.renameSync)(claimedMarkerPath, markerPath);
|
|
1816
|
+
}
|
|
1817
|
+
return false;
|
|
1818
|
+
}
|
|
1819
|
+
(0, import_fs5.rmSync)(claimedMarkerPath, { recursive: true, force: true });
|
|
1820
|
+
return true;
|
|
1821
|
+
}
|
|
1822
|
+
function reclaimDeadOwner(indexPath, lockPath, expectedOwner) {
|
|
1823
|
+
const reclaimPath = path7.join(lockPath, RECLAIM_DIRECTORY_NAME);
|
|
1824
|
+
const reclaimOwner = {
|
|
1825
|
+
pid: process.pid,
|
|
1826
|
+
hostname: os2.hostname(),
|
|
1827
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1828
|
+
token: (0, import_crypto.randomUUID)(),
|
|
1829
|
+
expectedOwnerToken: expectedOwner.token
|
|
1830
|
+
};
|
|
1831
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
1832
|
+
if (publishJsonDirectory(reclaimPath, reclaimOwner)) break;
|
|
1833
|
+
if (attempt === 0 && removeDeadReclaimMarker(lockPath, expectedOwner)) continue;
|
|
1834
|
+
return false;
|
|
1835
|
+
}
|
|
1836
|
+
try {
|
|
1837
|
+
const currentReclaimer = readReclaimOwner(reclaimPath);
|
|
1838
|
+
const currentOwner = readDirectoryOwner(lockPath);
|
|
1839
|
+
if (!currentReclaimer || !sameReclaimOwner(currentReclaimer, reclaimOwner) || !currentOwner || !sameOwner(currentOwner, expectedOwner) || getOwnerLiveness(currentOwner) !== "dead") {
|
|
1840
|
+
return false;
|
|
1841
|
+
}
|
|
1842
|
+
publishRecoveryMarker(indexPath, expectedOwner);
|
|
1843
|
+
const ownerBeforeQuarantine = readDirectoryOwner(lockPath);
|
|
1844
|
+
const reclaimerBeforeQuarantine = readReclaimOwner(reclaimPath);
|
|
1845
|
+
if (!ownerBeforeQuarantine || !sameOwner(ownerBeforeQuarantine, expectedOwner) || getOwnerLiveness(ownerBeforeQuarantine) !== "dead" || !reclaimerBeforeQuarantine || !sameReclaimOwner(reclaimerBeforeQuarantine, reclaimOwner)) {
|
|
1846
|
+
return false;
|
|
1847
|
+
}
|
|
1848
|
+
const quarantinePath = `${lockPath}.stale.${expectedOwner.token}.${reclaimOwner.token}`;
|
|
1849
|
+
(0, import_fs5.renameSync)(lockPath, quarantinePath);
|
|
1850
|
+
(0, import_fs5.rmSync)(quarantinePath, { recursive: true, force: true });
|
|
1851
|
+
return true;
|
|
1852
|
+
} catch (error) {
|
|
1853
|
+
if (getErrorCode(error) === "ENOENT") return false;
|
|
1854
|
+
throw error;
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
var IndexLockContentionError = class extends Error {
|
|
1858
|
+
constructor(lockPath, owner, reason) {
|
|
1859
|
+
const ownerDescription = owner ? `PID ${owner.pid} on ${owner.hostname}, operation ${owner.operation}, since ${owner.startedAt}` : "an unreadable owner";
|
|
1860
|
+
super(`Index mutation already in progress: ${ownerDescription}`);
|
|
1861
|
+
this.lockPath = lockPath;
|
|
1862
|
+
this.owner = owner;
|
|
1863
|
+
this.reason = reason;
|
|
1864
|
+
this.name = "IndexLockContentionError";
|
|
1865
|
+
}
|
|
1866
|
+
lockPath;
|
|
1867
|
+
owner;
|
|
1868
|
+
reason;
|
|
1869
|
+
code = "INDEX_BUSY";
|
|
1870
|
+
};
|
|
1871
|
+
function isIndexLockContentionError(error) {
|
|
1872
|
+
return error instanceof IndexLockContentionError || typeof error === "object" && error !== null && "code" in error && error.code === "INDEX_BUSY";
|
|
1873
|
+
}
|
|
1874
|
+
function isTransientIndexLockContention(error) {
|
|
1875
|
+
if (!isIndexLockContentionError(error) || !("reason" in error)) return false;
|
|
1876
|
+
return error.reason === "active" || error.reason === "reclaiming";
|
|
1877
|
+
}
|
|
1878
|
+
function acquireIndexLock(indexPath, operation) {
|
|
1879
|
+
(0, import_fs5.mkdirSync)(indexPath, { recursive: true });
|
|
1880
|
+
const canonicalIndexPath = import_fs5.realpathSync.native(indexPath);
|
|
1881
|
+
const lockPath = path7.join(canonicalIndexPath, "indexing.lock");
|
|
1882
|
+
cleanupDeadPublicationCandidates(canonicalIndexPath);
|
|
1883
|
+
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
1884
|
+
const owner = createOwner(operation);
|
|
1885
|
+
if (publishJsonDirectory(lockPath, owner)) {
|
|
1886
|
+
const lease = {
|
|
1887
|
+
canonicalIndexPath,
|
|
1888
|
+
lockPath,
|
|
1889
|
+
owner,
|
|
1890
|
+
recoveries: []
|
|
1891
|
+
};
|
|
1892
|
+
try {
|
|
1893
|
+
lease.recoveries = getPendingRecoveries(canonicalIndexPath);
|
|
1894
|
+
return lease;
|
|
1895
|
+
} catch (error) {
|
|
1896
|
+
releaseIndexLock(lease);
|
|
1897
|
+
throw error;
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
let stats;
|
|
1901
|
+
try {
|
|
1902
|
+
stats = (0, import_fs5.lstatSync)(lockPath);
|
|
1903
|
+
} catch (error) {
|
|
1904
|
+
if (getErrorCode(error) === "ENOENT") continue;
|
|
1905
|
+
throw error;
|
|
1906
|
+
}
|
|
1907
|
+
if (!stats.isDirectory()) {
|
|
1908
|
+
const legacyOwner = readLegacyOwner(lockPath);
|
|
1909
|
+
throw new IndexLockContentionError(lockPath, legacyOwner, "legacy-lock");
|
|
1910
|
+
}
|
|
1911
|
+
const existingOwner = readDirectoryOwner(lockPath);
|
|
1912
|
+
if (!existingOwner) {
|
|
1913
|
+
throw new IndexLockContentionError(lockPath, null, "unknown-owner");
|
|
1914
|
+
}
|
|
1915
|
+
const liveness = getOwnerLiveness(existingOwner);
|
|
1916
|
+
if (liveness !== "dead") {
|
|
1917
|
+
throw new IndexLockContentionError(lockPath, existingOwner, liveness === "alive" ? "active" : "unknown-owner");
|
|
1918
|
+
}
|
|
1919
|
+
if (!reclaimDeadOwner(canonicalIndexPath, lockPath, existingOwner)) {
|
|
1920
|
+
throw new IndexLockContentionError(lockPath, existingOwner, "reclaiming");
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
throw new IndexLockContentionError(lockPath, null, "reclaiming");
|
|
1924
|
+
}
|
|
1925
|
+
function releaseIndexLock(lease) {
|
|
1926
|
+
const currentOwner = readDirectoryOwner(lease.lockPath);
|
|
1927
|
+
if (!currentOwner || !sameOwner(currentOwner, lease.owner)) return false;
|
|
1928
|
+
const releasePath = `${lease.lockPath}.release.${lease.owner.pid}.${lease.owner.token}`;
|
|
1929
|
+
try {
|
|
1930
|
+
retryTransientFilesystemOperation(() => (0, import_fs5.renameSync)(lease.lockPath, releasePath));
|
|
1931
|
+
} catch (error) {
|
|
1932
|
+
if (getErrorCode(error) === "ENOENT") return false;
|
|
1933
|
+
throw error;
|
|
1934
|
+
}
|
|
1935
|
+
const claimedOwner = readDirectoryOwner(releasePath);
|
|
1936
|
+
if (!claimedOwner || !sameOwner(claimedOwner, lease.owner)) {
|
|
1937
|
+
if (!(0, import_fs5.existsSync)(lease.lockPath) && (0, import_fs5.existsSync)(releasePath)) {
|
|
1938
|
+
(0, import_fs5.renameSync)(releasePath, lease.lockPath);
|
|
1939
|
+
}
|
|
1940
|
+
return false;
|
|
1941
|
+
}
|
|
1942
|
+
try {
|
|
1943
|
+
retryTransientFilesystemOperation(() => (0, import_fs5.rmSync)(releasePath, { recursive: true, force: true }));
|
|
1944
|
+
} catch (error) {
|
|
1945
|
+
console.error(`[codebase-index] Lease released but tombstone cleanup failed: ${releasePath}`, error);
|
|
1946
|
+
}
|
|
1947
|
+
return true;
|
|
1948
|
+
}
|
|
1949
|
+
async function withIndexLock(indexPath, operation, callback, options = {}) {
|
|
1950
|
+
const lease = acquireIndexLock(indexPath, operation);
|
|
1951
|
+
let result;
|
|
1952
|
+
let callbackError;
|
|
1953
|
+
let callbackFailed = false;
|
|
1954
|
+
try {
|
|
1955
|
+
result = await callback(lease);
|
|
1956
|
+
} catch (error) {
|
|
1957
|
+
callbackFailed = true;
|
|
1958
|
+
callbackError = error;
|
|
1959
|
+
}
|
|
1960
|
+
if (!callbackFailed && options.completeRecoveries !== false) {
|
|
1961
|
+
try {
|
|
1962
|
+
completeLeaseRecovery(lease);
|
|
1963
|
+
} catch (error) {
|
|
1964
|
+
callbackFailed = true;
|
|
1965
|
+
callbackError = error;
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
let releaseError;
|
|
1969
|
+
try {
|
|
1970
|
+
if (!releaseIndexLock(lease)) {
|
|
1971
|
+
releaseError = new Error(`Lost ownership of index mutation lease ${lease.owner.token}`);
|
|
1972
|
+
}
|
|
1973
|
+
} catch (error) {
|
|
1974
|
+
releaseError = error;
|
|
1975
|
+
}
|
|
1976
|
+
if (releaseError !== void 0) {
|
|
1977
|
+
if (callbackFailed) throw new AggregateError([callbackError, releaseError], "Index mutation and lease release both failed");
|
|
1978
|
+
throw releaseError;
|
|
1979
|
+
}
|
|
1980
|
+
if (callbackFailed) throw callbackError;
|
|
1981
|
+
return result;
|
|
1982
|
+
}
|
|
1983
|
+
function createLeaseTemporaryPath(targetPath, owner, kind = "tmp") {
|
|
1984
|
+
if (kind === "bak") return `${targetPath}.bak.${owner.pid}.${owner.token}`;
|
|
1985
|
+
temporaryCounter += 1;
|
|
1986
|
+
return `${targetPath}.tmp.${owner.pid}.${owner.token}.${temporaryCounter}`;
|
|
1987
|
+
}
|
|
1988
|
+
function removeLeaseTemporaryPath(temporaryPath) {
|
|
1989
|
+
if ((0, import_fs5.existsSync)(temporaryPath)) (0, import_fs5.rmSync)(temporaryPath, { recursive: true, force: true });
|
|
1990
|
+
}
|
|
1991
|
+
function recoverLeaseArtifacts(indexPath, owner, backupTargets) {
|
|
1992
|
+
for (const targetPath of backupTargets) {
|
|
1993
|
+
const backupPath = createLeaseTemporaryPath(targetPath, owner, "bak");
|
|
1994
|
+
if (!(0, import_fs5.existsSync)(backupPath)) continue;
|
|
1995
|
+
if ((0, import_fs5.existsSync)(targetPath)) (0, import_fs5.rmSync)(targetPath, { recursive: true, force: true });
|
|
1996
|
+
(0, import_fs5.renameSync)(backupPath, targetPath);
|
|
1997
|
+
}
|
|
1998
|
+
const temporaryOwnerMarker = `.tmp.${owner.pid}.${owner.token}.`;
|
|
1999
|
+
for (const entry of (0, import_fs5.readdirSync)(indexPath)) {
|
|
2000
|
+
if (!entry.includes(temporaryOwnerMarker)) continue;
|
|
2001
|
+
(0, import_fs5.rmSync)(path7.join(indexPath, entry), { recursive: true, force: true });
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
function completeLeaseRecovery(lease) {
|
|
2005
|
+
for (const recovery of lease.recoveries) {
|
|
2006
|
+
const markerOwner = readRecoveryOwner(recovery.markerPath);
|
|
2007
|
+
if (!markerOwner || !sameOwner(markerOwner, recovery.owner)) {
|
|
2008
|
+
throw new Error(`Recovery marker ownership changed: ${recovery.markerPath}`);
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
for (const recovery of lease.recoveries) {
|
|
2012
|
+
(0, import_fs5.rmSync)(recovery.markerPath, { recursive: true, force: true });
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
|
|
1590
2016
|
// src/tools/operations.ts
|
|
1591
|
-
var
|
|
1592
|
-
var
|
|
2017
|
+
var import_fs10 = require("fs");
|
|
2018
|
+
var path16 = __toESM(require("path"), 1);
|
|
1593
2019
|
|
|
1594
2020
|
// src/indexer/index.ts
|
|
1595
|
-
var
|
|
1596
|
-
var
|
|
2021
|
+
var import_fs8 = require("fs");
|
|
2022
|
+
var path13 = __toESM(require("path"), 1);
|
|
1597
2023
|
var import_perf_hooks = require("perf_hooks");
|
|
1598
|
-
var
|
|
1599
|
-
var
|
|
2024
|
+
var import_child_process3 = require("child_process");
|
|
2025
|
+
var import_util3 = require("util");
|
|
1600
2026
|
|
|
1601
2027
|
// node_modules/eventemitter3/index.mjs
|
|
1602
2028
|
var import_index = __toESM(require_eventemitter3(), 1);
|
|
@@ -2643,17 +3069,17 @@ async function pRetry(input, options = {}) {
|
|
|
2643
3069
|
}
|
|
2644
3070
|
|
|
2645
3071
|
// src/embeddings/detector.ts
|
|
2646
|
-
var
|
|
2647
|
-
var
|
|
2648
|
-
var
|
|
3072
|
+
var import_fs6 = require("fs");
|
|
3073
|
+
var path8 = __toESM(require("path"), 1);
|
|
3074
|
+
var os3 = __toESM(require("os"), 1);
|
|
2649
3075
|
function getOpenCodeAuthPath() {
|
|
2650
|
-
return
|
|
3076
|
+
return path8.join(os3.homedir(), ".local", "share", "opencode", "auth.json");
|
|
2651
3077
|
}
|
|
2652
3078
|
function loadOpenCodeAuth() {
|
|
2653
3079
|
const authPath = getOpenCodeAuthPath();
|
|
2654
3080
|
try {
|
|
2655
|
-
if ((0,
|
|
2656
|
-
return JSON.parse((0,
|
|
3081
|
+
if ((0, import_fs6.existsSync)(authPath)) {
|
|
3082
|
+
return JSON.parse((0, import_fs6.readFileSync)(authPath, "utf-8"));
|
|
2657
3083
|
}
|
|
2658
3084
|
} catch {
|
|
2659
3085
|
}
|
|
@@ -2875,17 +3301,17 @@ function validateExternalUrl(urlString) {
|
|
|
2875
3301
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
2876
3302
|
return { valid: false, reason: `Blocked protocol: ${parsed.protocol}` };
|
|
2877
3303
|
}
|
|
2878
|
-
const
|
|
2879
|
-
if (BLOCKED_HOSTNAMES.has(
|
|
2880
|
-
return { valid: false, reason: `Blocked: cloud metadata service (${
|
|
3304
|
+
const hostname2 = parsed.hostname.toLowerCase();
|
|
3305
|
+
if (BLOCKED_HOSTNAMES.has(hostname2)) {
|
|
3306
|
+
return { valid: false, reason: `Blocked: cloud metadata service (${hostname2})` };
|
|
2881
3307
|
}
|
|
2882
3308
|
for (const pattern of BLOCKED_METADATA_IPS) {
|
|
2883
|
-
if (pattern.test(
|
|
2884
|
-
return { valid: false, reason: `Blocked: cloud metadata IP (${
|
|
3309
|
+
if (pattern.test(hostname2)) {
|
|
3310
|
+
return { valid: false, reason: `Blocked: cloud metadata IP (${hostname2})` };
|
|
2885
3311
|
}
|
|
2886
3312
|
}
|
|
2887
|
-
if (/^169\.254\./.test(
|
|
2888
|
-
return { valid: false, reason: `Blocked: link-local address (${
|
|
3313
|
+
if (/^169\.254\./.test(hostname2)) {
|
|
3314
|
+
return { valid: false, reason: `Blocked: link-local address (${hostname2})` };
|
|
2889
3315
|
}
|
|
2890
3316
|
return { valid: true };
|
|
2891
3317
|
}
|
|
@@ -3356,8 +3782,8 @@ var SiliconFlowReranker = class {
|
|
|
3356
3782
|
|
|
3357
3783
|
// src/utils/files.ts
|
|
3358
3784
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
3359
|
-
var
|
|
3360
|
-
var
|
|
3785
|
+
var import_fs7 = require("fs");
|
|
3786
|
+
var path9 = __toESM(require("path"), 1);
|
|
3361
3787
|
var PROJECT_MARKERS = [
|
|
3362
3788
|
".git",
|
|
3363
3789
|
"package.json",
|
|
@@ -3377,7 +3803,7 @@ var PROJECT_MARKERS = [
|
|
|
3377
3803
|
];
|
|
3378
3804
|
function hasProjectMarker(projectRoot) {
|
|
3379
3805
|
for (const marker of PROJECT_MARKERS) {
|
|
3380
|
-
if ((0,
|
|
3806
|
+
if ((0, import_fs7.existsSync)(path9.join(projectRoot, marker))) {
|
|
3381
3807
|
return true;
|
|
3382
3808
|
}
|
|
3383
3809
|
}
|
|
@@ -3404,16 +3830,16 @@ function createIgnoreFilter(projectRoot) {
|
|
|
3404
3830
|
"**/*build*/**"
|
|
3405
3831
|
];
|
|
3406
3832
|
ig.add(defaultIgnores);
|
|
3407
|
-
const gitignorePath =
|
|
3408
|
-
if ((0,
|
|
3409
|
-
const gitignoreContent = (0,
|
|
3833
|
+
const gitignorePath = path9.join(projectRoot, ".gitignore");
|
|
3834
|
+
if ((0, import_fs7.existsSync)(gitignorePath)) {
|
|
3835
|
+
const gitignoreContent = (0, import_fs7.readFileSync)(gitignorePath, "utf-8");
|
|
3410
3836
|
ig.add(gitignoreContent);
|
|
3411
3837
|
}
|
|
3412
3838
|
return ig;
|
|
3413
3839
|
}
|
|
3414
3840
|
function shouldIncludeFile(filePath, projectRoot, includePatterns, excludePatterns, ignoreFilter) {
|
|
3415
|
-
const relativePath =
|
|
3416
|
-
if (hasFilteredPathSegment(relativePath,
|
|
3841
|
+
const relativePath = path9.relative(projectRoot, filePath);
|
|
3842
|
+
if (hasFilteredPathSegment(relativePath, path9.sep)) {
|
|
3417
3843
|
return false;
|
|
3418
3844
|
}
|
|
3419
3845
|
if (ignoreFilter.ignores(relativePath)) {
|
|
@@ -3447,12 +3873,12 @@ function matchGlob(filePath, pattern) {
|
|
|
3447
3873
|
return regex.test(filePath);
|
|
3448
3874
|
}
|
|
3449
3875
|
async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns, ignoreFilter, maxFileSize, skipped, options, currentDepth = 0) {
|
|
3450
|
-
const entries = await
|
|
3876
|
+
const entries = await import_fs7.promises.readdir(dir, { withFileTypes: true });
|
|
3451
3877
|
const filesInDir = [];
|
|
3452
3878
|
const subdirs = [];
|
|
3453
3879
|
for (const entry of entries) {
|
|
3454
|
-
const fullPath =
|
|
3455
|
-
const relativePath =
|
|
3880
|
+
const fullPath = path9.join(dir, entry.name);
|
|
3881
|
+
const relativePath = path9.relative(projectRoot, fullPath);
|
|
3456
3882
|
if (isHiddenPathSegment(entry.name)) {
|
|
3457
3883
|
if (entry.isDirectory()) {
|
|
3458
3884
|
skipped.push({ path: relativePath, reason: "excluded" });
|
|
@@ -3472,7 +3898,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
|
|
|
3472
3898
|
if (entry.isDirectory()) {
|
|
3473
3899
|
subdirs.push({ fullPath, relativePath });
|
|
3474
3900
|
} else if (entry.isFile()) {
|
|
3475
|
-
const stat4 = await
|
|
3901
|
+
const stat4 = await import_fs7.promises.stat(fullPath);
|
|
3476
3902
|
if (stat4.size > maxFileSize) {
|
|
3477
3903
|
skipped.push({ path: relativePath, reason: "too_large" });
|
|
3478
3904
|
continue;
|
|
@@ -3501,7 +3927,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
|
|
|
3501
3927
|
yield f;
|
|
3502
3928
|
}
|
|
3503
3929
|
for (let i = options.maxFilesPerDirectory; i < filesInDir.length; i++) {
|
|
3504
|
-
skipped.push({ path:
|
|
3930
|
+
skipped.push({ path: path9.relative(projectRoot, filesInDir[i].path), reason: "excluded" });
|
|
3505
3931
|
}
|
|
3506
3932
|
const canRecurse = options.maxDepth === -1 || currentDepth < options.maxDepth;
|
|
3507
3933
|
if (canRecurse) {
|
|
@@ -3541,14 +3967,14 @@ async function collectFiles(projectRoot, includePatterns, excludePatterns, maxFi
|
|
|
3541
3967
|
if (additionalRoots && additionalRoots.length > 0) {
|
|
3542
3968
|
const normalizedRoots = /* @__PURE__ */ new Set();
|
|
3543
3969
|
for (const kbRoot of additionalRoots) {
|
|
3544
|
-
const resolved =
|
|
3545
|
-
|
|
3970
|
+
const resolved = path9.normalize(
|
|
3971
|
+
path9.isAbsolute(kbRoot) ? kbRoot : path9.resolve(projectRoot, kbRoot)
|
|
3546
3972
|
);
|
|
3547
3973
|
normalizedRoots.add(resolved);
|
|
3548
3974
|
}
|
|
3549
3975
|
for (const resolvedKbRoot of normalizedRoots) {
|
|
3550
3976
|
try {
|
|
3551
|
-
const stat4 = await
|
|
3977
|
+
const stat4 = await import_fs7.promises.stat(resolvedKbRoot);
|
|
3552
3978
|
if (!stat4.isDirectory()) {
|
|
3553
3979
|
skipped.push({ path: resolvedKbRoot, reason: "excluded" });
|
|
3554
3980
|
continue;
|
|
@@ -3949,14 +4375,14 @@ function initializeLogger(config) {
|
|
|
3949
4375
|
}
|
|
3950
4376
|
|
|
3951
4377
|
// src/native/index.ts
|
|
3952
|
-
var
|
|
3953
|
-
var
|
|
4378
|
+
var path10 = __toESM(require("path"), 1);
|
|
4379
|
+
var os4 = __toESM(require("os"), 1);
|
|
3954
4380
|
var module2 = __toESM(require("module"), 1);
|
|
3955
4381
|
var import_url = require("url");
|
|
3956
4382
|
var import_meta = {};
|
|
3957
4383
|
function getNativeBinding() {
|
|
3958
|
-
const platform2 =
|
|
3959
|
-
const arch2 =
|
|
4384
|
+
const platform2 = os4.platform();
|
|
4385
|
+
const arch2 = os4.arch();
|
|
3960
4386
|
let bindingName;
|
|
3961
4387
|
if (platform2 === "darwin" && arch2 === "arm64") {
|
|
3962
4388
|
bindingName = "codebase-index-native.darwin-arm64.node";
|
|
@@ -3974,19 +4400,19 @@ function getNativeBinding() {
|
|
|
3974
4400
|
let currentDir;
|
|
3975
4401
|
let requireTarget;
|
|
3976
4402
|
if (typeof import_meta !== "undefined" && import_meta.url) {
|
|
3977
|
-
currentDir =
|
|
4403
|
+
currentDir = path10.dirname((0, import_url.fileURLToPath)(import_meta.url));
|
|
3978
4404
|
requireTarget = import_meta.url;
|
|
3979
4405
|
} else if (typeof __dirname !== "undefined") {
|
|
3980
4406
|
currentDir = __dirname;
|
|
3981
4407
|
requireTarget = __filename;
|
|
3982
4408
|
} else {
|
|
3983
4409
|
currentDir = process.cwd();
|
|
3984
|
-
requireTarget =
|
|
4410
|
+
requireTarget = path10.join(currentDir, "index.js");
|
|
3985
4411
|
}
|
|
3986
4412
|
const normalizedDir = currentDir.replace(/\\/g, "/");
|
|
3987
|
-
const isDevMode = normalizedDir.includes("/src/native") || currentDir.includes(
|
|
3988
|
-
const packageRoot = isDevMode ?
|
|
3989
|
-
const nativePath =
|
|
4413
|
+
const isDevMode = normalizedDir.includes("/src/native") || currentDir.includes(path10.join("src", "native"));
|
|
4414
|
+
const packageRoot = isDevMode ? path10.resolve(currentDir, "../..") : path10.resolve(currentDir, "..");
|
|
4415
|
+
const nativePath = path10.join(packageRoot, "native", bindingName);
|
|
3990
4416
|
const require2 = module2.createRequire(requireTarget);
|
|
3991
4417
|
return require2(nativePath);
|
|
3992
4418
|
}
|
|
@@ -4028,6 +4454,12 @@ function createMockNativeBinding() {
|
|
|
4028
4454
|
constructor() {
|
|
4029
4455
|
throw error;
|
|
4030
4456
|
}
|
|
4457
|
+
static openReadOnly() {
|
|
4458
|
+
throw error;
|
|
4459
|
+
}
|
|
4460
|
+
static createEmptyReadOnly() {
|
|
4461
|
+
throw error;
|
|
4462
|
+
}
|
|
4031
4463
|
close() {
|
|
4032
4464
|
throw error;
|
|
4033
4465
|
}
|
|
@@ -4131,6 +4563,12 @@ var VectorStore = class {
|
|
|
4131
4563
|
load() {
|
|
4132
4564
|
this.inner.load();
|
|
4133
4565
|
}
|
|
4566
|
+
loadStrict() {
|
|
4567
|
+
this.inner.loadStrict();
|
|
4568
|
+
}
|
|
4569
|
+
hasFingerprint() {
|
|
4570
|
+
return this.inner.hasFingerprint();
|
|
4571
|
+
}
|
|
4134
4572
|
count() {
|
|
4135
4573
|
return this.inner.count();
|
|
4136
4574
|
}
|
|
@@ -4413,12 +4851,24 @@ var InvertedIndex = class {
|
|
|
4413
4851
|
return this.inner.documentCount();
|
|
4414
4852
|
}
|
|
4415
4853
|
};
|
|
4416
|
-
var Database = class {
|
|
4854
|
+
var Database = class _Database {
|
|
4417
4855
|
inner;
|
|
4418
4856
|
closed = false;
|
|
4419
4857
|
constructor(dbPath) {
|
|
4420
4858
|
this.inner = new native.Database(dbPath);
|
|
4421
4859
|
}
|
|
4860
|
+
static fromNative(inner) {
|
|
4861
|
+
const database = Object.create(_Database.prototype);
|
|
4862
|
+
database.inner = inner;
|
|
4863
|
+
database.closed = false;
|
|
4864
|
+
return database;
|
|
4865
|
+
}
|
|
4866
|
+
static openReadOnly(dbPath) {
|
|
4867
|
+
return _Database.fromNative(native.Database.openReadOnly(dbPath));
|
|
4868
|
+
}
|
|
4869
|
+
static createEmptyReadOnly() {
|
|
4870
|
+
return _Database.fromNative(native.Database.createEmptyReadOnly());
|
|
4871
|
+
}
|
|
4422
4872
|
throwIfClosed() {
|
|
4423
4873
|
if (this.closed) {
|
|
4424
4874
|
throw new Error("Database is closed");
|
|
@@ -4695,7 +5145,7 @@ var Database = class {
|
|
|
4695
5145
|
};
|
|
4696
5146
|
|
|
4697
5147
|
// src/tools/changed-files.ts
|
|
4698
|
-
var
|
|
5148
|
+
var path11 = __toESM(require("path"), 1);
|
|
4699
5149
|
var import_child_process = require("child_process");
|
|
4700
5150
|
var import_util = require("util");
|
|
4701
5151
|
var execFileAsync = (0, import_util.promisify)(import_child_process.execFile);
|
|
@@ -4783,9 +5233,9 @@ function normalizeFiles(rawFiles, projectRoot) {
|
|
|
4783
5233
|
for (const raw of rawFiles) {
|
|
4784
5234
|
const trimmed = raw.trim();
|
|
4785
5235
|
if (!trimmed) continue;
|
|
4786
|
-
const absolute =
|
|
4787
|
-
const
|
|
4788
|
-
const cleaned =
|
|
5236
|
+
const absolute = path11.resolve(projectRoot, trimmed);
|
|
5237
|
+
const relative11 = path11.relative(projectRoot, absolute);
|
|
5238
|
+
const cleaned = relative11.startsWith("./") ? relative11.slice(2) : relative11;
|
|
4789
5239
|
if (!seen.has(cleaned)) {
|
|
4790
5240
|
seen.add(cleaned);
|
|
4791
5241
|
result.push(cleaned);
|
|
@@ -4794,8 +5244,61 @@ function normalizeFiles(rawFiles, projectRoot) {
|
|
|
4794
5244
|
return result;
|
|
4795
5245
|
}
|
|
4796
5246
|
|
|
5247
|
+
// src/indexer/git-blame.ts
|
|
5248
|
+
var import_child_process2 = require("child_process");
|
|
5249
|
+
var path12 = __toESM(require("path"), 1);
|
|
5250
|
+
var import_util2 = require("util");
|
|
5251
|
+
var execFileAsync2 = (0, import_util2.promisify)(import_child_process2.execFile);
|
|
5252
|
+
function parseGitBlamePorcelain(output) {
|
|
5253
|
+
const commits = /* @__PURE__ */ new Map();
|
|
5254
|
+
let current;
|
|
5255
|
+
for (const line of output.split("\n")) {
|
|
5256
|
+
if (/^[0-9a-f]{40} /.test(line)) {
|
|
5257
|
+
const sha = line.slice(0, 40);
|
|
5258
|
+
current = commits.get(sha) ?? {
|
|
5259
|
+
sha,
|
|
5260
|
+
author: "",
|
|
5261
|
+
authorEmail: "",
|
|
5262
|
+
committedAt: 0,
|
|
5263
|
+
summary: "",
|
|
5264
|
+
lines: 0
|
|
5265
|
+
};
|
|
5266
|
+
commits.set(sha, current);
|
|
5267
|
+
continue;
|
|
5268
|
+
}
|
|
5269
|
+
if (!current) {
|
|
5270
|
+
continue;
|
|
5271
|
+
}
|
|
5272
|
+
if (line.startsWith("author ")) {
|
|
5273
|
+
current.author = line.slice("author ".length);
|
|
5274
|
+
} else if (line.startsWith("author-mail ")) {
|
|
5275
|
+
current.authorEmail = line.slice("author-mail ".length).replace(/^<|>$/g, "");
|
|
5276
|
+
} else if (line.startsWith("author-time ")) {
|
|
5277
|
+
current.committedAt = Number.parseInt(line.slice("author-time ".length), 10);
|
|
5278
|
+
} else if (line.startsWith("summary ")) {
|
|
5279
|
+
current.summary = line.slice("summary ".length);
|
|
5280
|
+
} else if (line.startsWith(" ")) {
|
|
5281
|
+
current.lines += 1;
|
|
5282
|
+
}
|
|
5283
|
+
}
|
|
5284
|
+
return Array.from(commits.values()).filter((commit) => commit.lines > 0).sort((a, b) => b.lines - a.lines || b.committedAt - a.committedAt)[0];
|
|
5285
|
+
}
|
|
5286
|
+
async function getChunkGitBlame(projectRoot, filePath, startLine, endLine) {
|
|
5287
|
+
const relativePath = path12.relative(projectRoot, filePath);
|
|
5288
|
+
try {
|
|
5289
|
+
const { stdout } = await execFileAsync2(
|
|
5290
|
+
"git",
|
|
5291
|
+
["blame", "--line-porcelain", "-L", `${startLine},${endLine}`, "--", relativePath],
|
|
5292
|
+
{ cwd: projectRoot, timeout: 3e4 }
|
|
5293
|
+
);
|
|
5294
|
+
return parseGitBlamePorcelain(stdout);
|
|
5295
|
+
} catch {
|
|
5296
|
+
return void 0;
|
|
5297
|
+
}
|
|
5298
|
+
}
|
|
5299
|
+
|
|
4797
5300
|
// src/indexer/index.ts
|
|
4798
|
-
var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig", "gdscript", "matlab"]);
|
|
5301
|
+
var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig", "gdscript", "matlab", "bash"]);
|
|
4799
5302
|
var CASE_INSENSITIVE_LANGUAGES = /* @__PURE__ */ new Set(["apex"]);
|
|
4800
5303
|
var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
4801
5304
|
"function_declaration",
|
|
@@ -4875,6 +5378,46 @@ function isSqliteCorruptionError(error) {
|
|
|
4875
5378
|
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");
|
|
4876
5379
|
}
|
|
4877
5380
|
var STARTUP_WARNING_METADATA_KEY = "index.startupWarning";
|
|
5381
|
+
var READER_ARTIFACT_RETRY_INTERVAL_MS = 1e3;
|
|
5382
|
+
function metadataFromBlame(blame) {
|
|
5383
|
+
if (!blame) {
|
|
5384
|
+
return {};
|
|
5385
|
+
}
|
|
5386
|
+
return {
|
|
5387
|
+
blameSha: blame.sha,
|
|
5388
|
+
blameAuthor: blame.author,
|
|
5389
|
+
blameAuthorEmail: blame.authorEmail,
|
|
5390
|
+
blameCommittedAt: blame.committedAt,
|
|
5391
|
+
blameSummary: blame.summary
|
|
5392
|
+
};
|
|
5393
|
+
}
|
|
5394
|
+
function blameFromChunkData(chunk) {
|
|
5395
|
+
if (!chunk?.blameSha || !chunk.blameAuthor || !chunk.blameAuthorEmail || chunk.blameCommittedAt === void 0 || !chunk.blameSummary) {
|
|
5396
|
+
return void 0;
|
|
5397
|
+
}
|
|
5398
|
+
return {
|
|
5399
|
+
sha: chunk.blameSha,
|
|
5400
|
+
author: chunk.blameAuthor,
|
|
5401
|
+
authorEmail: chunk.blameAuthorEmail,
|
|
5402
|
+
committedAt: chunk.blameCommittedAt,
|
|
5403
|
+
summary: chunk.blameSummary
|
|
5404
|
+
};
|
|
5405
|
+
}
|
|
5406
|
+
function blameFromMetadata(metadata) {
|
|
5407
|
+
if (!metadata.blameSha || !metadata.blameAuthor || !metadata.blameAuthorEmail || metadata.blameCommittedAt === void 0 || !metadata.blameSummary) {
|
|
5408
|
+
return void 0;
|
|
5409
|
+
}
|
|
5410
|
+
return {
|
|
5411
|
+
sha: metadata.blameSha,
|
|
5412
|
+
author: metadata.blameAuthor,
|
|
5413
|
+
authorEmail: metadata.blameAuthorEmail,
|
|
5414
|
+
committedAt: metadata.blameCommittedAt,
|
|
5415
|
+
summary: metadata.blameSummary
|
|
5416
|
+
};
|
|
5417
|
+
}
|
|
5418
|
+
function hasBlameMetadata(metadata) {
|
|
5419
|
+
return blameFromMetadata(metadata) !== void 0;
|
|
5420
|
+
}
|
|
4878
5421
|
var INDEX_METADATA_VERSION = "1";
|
|
4879
5422
|
var EMBEDDING_STRATEGY_VERSION = "2";
|
|
4880
5423
|
var RANKING_TOKEN_CACHE_LIMIT = 4096;
|
|
@@ -5033,9 +5576,9 @@ function hasAllEmbeddingParts(parts, expectedPartCount) {
|
|
|
5033
5576
|
return true;
|
|
5034
5577
|
}
|
|
5035
5578
|
function isPathWithinRoot(filePath, rootPath) {
|
|
5036
|
-
const normalizedFilePath =
|
|
5037
|
-
const normalizedRoot =
|
|
5038
|
-
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${
|
|
5579
|
+
const normalizedFilePath = path13.resolve(filePath);
|
|
5580
|
+
const normalizedRoot = path13.resolve(rootPath);
|
|
5581
|
+
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path13.sep}`);
|
|
5039
5582
|
}
|
|
5040
5583
|
var rankingQueryTokenCache = /* @__PURE__ */ new Map();
|
|
5041
5584
|
var rankingNameTokenCache = /* @__PURE__ */ new Map();
|
|
@@ -5794,7 +6337,8 @@ function promoteIdentifierMatches(query, combined, semanticCandidates, keywordCa
|
|
|
5794
6337
|
chunkType,
|
|
5795
6338
|
name: chunk.name ?? void 0,
|
|
5796
6339
|
language: chunk.language,
|
|
5797
|
-
hash: chunk.contentHash
|
|
6340
|
+
hash: chunk.contentHash,
|
|
6341
|
+
...metadataFromBlame(blameFromChunkData(chunk))
|
|
5798
6342
|
};
|
|
5799
6343
|
const baselineScore = existing?.score ?? 0.5;
|
|
5800
6344
|
candidateUnion.set(chunk.chunkId, {
|
|
@@ -5878,7 +6422,8 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallb
|
|
|
5878
6422
|
chunkType,
|
|
5879
6423
|
name: chunk.name ?? void 0,
|
|
5880
6424
|
language: chunk.language,
|
|
5881
|
-
hash: chunk.contentHash
|
|
6425
|
+
hash: chunk.contentHash,
|
|
6426
|
+
...metadataFromBlame(blameFromChunkData(chunk))
|
|
5882
6427
|
}
|
|
5883
6428
|
});
|
|
5884
6429
|
}
|
|
@@ -6047,6 +6592,21 @@ function matchesSearchFilters(candidate, options, minScore) {
|
|
|
6047
6592
|
if (options?.chunkType && candidate.metadata.chunkType !== options.chunkType) {
|
|
6048
6593
|
return false;
|
|
6049
6594
|
}
|
|
6595
|
+
if (options?.blameAuthor) {
|
|
6596
|
+
const author = options.blameAuthor.toLowerCase();
|
|
6597
|
+
const candidateAuthor = candidate.metadata.blameAuthor?.toLowerCase();
|
|
6598
|
+
const candidateEmail = candidate.metadata.blameAuthorEmail?.toLowerCase();
|
|
6599
|
+
if (candidateAuthor !== author && candidateEmail !== author) return false;
|
|
6600
|
+
}
|
|
6601
|
+
if (options?.blameSha && !candidate.metadata.blameSha?.toLowerCase().startsWith(options.blameSha.toLowerCase())) {
|
|
6602
|
+
return false;
|
|
6603
|
+
}
|
|
6604
|
+
if (options?.blameSince) {
|
|
6605
|
+
const sinceMs = Date.parse(options.blameSince);
|
|
6606
|
+
if (Number.isNaN(sinceMs)) return false;
|
|
6607
|
+
const committedAt = candidate.metadata.blameCommittedAt;
|
|
6608
|
+
if (committedAt === void 0 || committedAt < Math.floor(sinceMs / 1e3)) return false;
|
|
6609
|
+
}
|
|
6050
6610
|
return true;
|
|
6051
6611
|
}
|
|
6052
6612
|
function unionCandidates(semanticCandidates, keywordCandidates) {
|
|
@@ -6084,26 +6644,133 @@ var Indexer = class {
|
|
|
6084
6644
|
queryCacheTtlMs = 5 * 60 * 1e3;
|
|
6085
6645
|
querySimilarityThreshold = 0.85;
|
|
6086
6646
|
indexCompatibility = null;
|
|
6087
|
-
|
|
6647
|
+
activeIndexLease = null;
|
|
6648
|
+
initializationPromise = null;
|
|
6649
|
+
initializationMode = "none";
|
|
6650
|
+
readIssues = [];
|
|
6651
|
+
retiredDatabases = [];
|
|
6652
|
+
readerArtifactFingerprint = null;
|
|
6653
|
+
writerArtifactFingerprint = null;
|
|
6654
|
+
readerArtifactRetryAfter = /* @__PURE__ */ new Map();
|
|
6088
6655
|
constructor(projectRoot, config, host = "opencode") {
|
|
6089
6656
|
this.projectRoot = projectRoot;
|
|
6090
6657
|
this.config = config;
|
|
6091
6658
|
this.host = host;
|
|
6092
6659
|
this.indexPath = this.getIndexPath();
|
|
6093
|
-
this.fileHashCachePath =
|
|
6094
|
-
this.failedBatchesPath =
|
|
6095
|
-
this.indexingLockPath = path11.join(this.indexPath, "indexing.lock");
|
|
6660
|
+
this.fileHashCachePath = path13.join(this.indexPath, "file-hashes.json");
|
|
6661
|
+
this.failedBatchesPath = path13.join(this.indexPath, "failed-batches.json");
|
|
6096
6662
|
this.logger = initializeLogger(config.debug);
|
|
6097
6663
|
}
|
|
6098
6664
|
getIndexPath() {
|
|
6099
6665
|
return resolveProjectIndexPath(this.projectRoot, this.config.scope, this.host);
|
|
6100
6666
|
}
|
|
6667
|
+
isLocalProjectIndexPath() {
|
|
6668
|
+
const localProjectIndexPaths = [path13.join(this.projectRoot, getHostProjectIndexRelativePath(this.host))];
|
|
6669
|
+
if (this.host !== "opencode") {
|
|
6670
|
+
localProjectIndexPaths.push(path13.join(this.projectRoot, getHostProjectIndexRelativePath("opencode")));
|
|
6671
|
+
}
|
|
6672
|
+
return localProjectIndexPaths.some((localPath) => {
|
|
6673
|
+
if (!(0, import_fs8.existsSync)(localPath) || !(0, import_fs8.existsSync)(this.indexPath)) {
|
|
6674
|
+
return path13.resolve(this.indexPath) === path13.resolve(localPath);
|
|
6675
|
+
}
|
|
6676
|
+
const indexStats = (0, import_fs8.statSync)(this.indexPath);
|
|
6677
|
+
const localStats = (0, import_fs8.statSync)(localPath);
|
|
6678
|
+
return indexStats.dev === localStats.dev && indexStats.ino === localStats.ino;
|
|
6679
|
+
});
|
|
6680
|
+
}
|
|
6681
|
+
resetLoadedIndexState(retireDatabase = false) {
|
|
6682
|
+
if (this.database) {
|
|
6683
|
+
if (retireDatabase) {
|
|
6684
|
+
this.retiredDatabases.push(this.database);
|
|
6685
|
+
} else {
|
|
6686
|
+
this.database.close();
|
|
6687
|
+
}
|
|
6688
|
+
}
|
|
6689
|
+
this.store = null;
|
|
6690
|
+
this.invertedIndex = null;
|
|
6691
|
+
this.database = null;
|
|
6692
|
+
this.provider = null;
|
|
6693
|
+
this.configuredProviderInfo = null;
|
|
6694
|
+
this.reranker = null;
|
|
6695
|
+
this.indexCompatibility = null;
|
|
6696
|
+
this.initializationMode = "none";
|
|
6697
|
+
this.readIssues = [];
|
|
6698
|
+
this.readerArtifactFingerprint = null;
|
|
6699
|
+
this.writerArtifactFingerprint = null;
|
|
6700
|
+
this.readerArtifactRetryAfter.clear();
|
|
6701
|
+
this.fileHashCache.clear();
|
|
6702
|
+
}
|
|
6703
|
+
refreshLoadedIndexState() {
|
|
6704
|
+
if (!this.store || !this.invertedIndex || !this.configuredProviderInfo) return;
|
|
6705
|
+
this.store.load();
|
|
6706
|
+
this.invertedIndex.load();
|
|
6707
|
+
this.fileHashCache.clear();
|
|
6708
|
+
this.loadFileHashCache();
|
|
6709
|
+
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
6710
|
+
this.readIssues = [];
|
|
6711
|
+
this.readerArtifactRetryAfter.clear();
|
|
6712
|
+
}
|
|
6713
|
+
async withIndexMutationLease(operation, callback) {
|
|
6714
|
+
const lease = acquireIndexLock(this.indexPath, operation);
|
|
6715
|
+
this.indexPath = lease.canonicalIndexPath;
|
|
6716
|
+
this.fileHashCachePath = path13.join(this.indexPath, "file-hashes.json");
|
|
6717
|
+
this.failedBatchesPath = path13.join(this.indexPath, "failed-batches.json");
|
|
6718
|
+
this.activeIndexLease = lease;
|
|
6719
|
+
let result;
|
|
6720
|
+
let callbackError;
|
|
6721
|
+
let callbackFailed = false;
|
|
6722
|
+
try {
|
|
6723
|
+
result = await callback(lease.recoveries.map(({ owner }) => owner));
|
|
6724
|
+
} catch (error) {
|
|
6725
|
+
callbackFailed = true;
|
|
6726
|
+
callbackError = error;
|
|
6727
|
+
}
|
|
6728
|
+
if (!callbackFailed) {
|
|
6729
|
+
try {
|
|
6730
|
+
completeLeaseRecovery(lease);
|
|
6731
|
+
this.writerArtifactFingerprint = this.captureReaderArtifactFingerprint();
|
|
6732
|
+
} catch (error) {
|
|
6733
|
+
callbackFailed = true;
|
|
6734
|
+
callbackError = error;
|
|
6735
|
+
}
|
|
6736
|
+
}
|
|
6737
|
+
let releaseError;
|
|
6738
|
+
try {
|
|
6739
|
+
if (!releaseIndexLock(lease)) {
|
|
6740
|
+
releaseError = new Error(`Lost ownership of index mutation lease ${lease.owner.token}`);
|
|
6741
|
+
this.writerArtifactFingerprint = null;
|
|
6742
|
+
if (this.activeIndexLease?.owner.token === lease.owner.token) {
|
|
6743
|
+
this.activeIndexLease = null;
|
|
6744
|
+
}
|
|
6745
|
+
} else if (this.activeIndexLease?.owner.token === lease.owner.token) {
|
|
6746
|
+
this.activeIndexLease = null;
|
|
6747
|
+
}
|
|
6748
|
+
} catch (error) {
|
|
6749
|
+
releaseError = error;
|
|
6750
|
+
this.writerArtifactFingerprint = null;
|
|
6751
|
+
if (!(0, import_fs8.existsSync)(lease.lockPath) && this.activeIndexLease?.owner.token === lease.owner.token) {
|
|
6752
|
+
this.activeIndexLease = null;
|
|
6753
|
+
}
|
|
6754
|
+
}
|
|
6755
|
+
if (releaseError !== void 0) {
|
|
6756
|
+
if (callbackFailed) throw new AggregateError([callbackError, releaseError], "Index mutation and lease release both failed");
|
|
6757
|
+
throw releaseError;
|
|
6758
|
+
}
|
|
6759
|
+
if (callbackFailed) throw callbackError;
|
|
6760
|
+
return result;
|
|
6761
|
+
}
|
|
6762
|
+
requireActiveLease() {
|
|
6763
|
+
if (!this.activeIndexLease) {
|
|
6764
|
+
throw new Error("Index mutation attempted without an active interprocess lease");
|
|
6765
|
+
}
|
|
6766
|
+
return this.activeIndexLease;
|
|
6767
|
+
}
|
|
6101
6768
|
loadFileHashCache() {
|
|
6102
|
-
if (!(0,
|
|
6769
|
+
if (!(0, import_fs8.existsSync)(this.fileHashCachePath)) {
|
|
6103
6770
|
return;
|
|
6104
6771
|
}
|
|
6105
6772
|
try {
|
|
6106
|
-
const data = (0,
|
|
6773
|
+
const data = (0, import_fs8.readFileSync)(this.fileHashCachePath, "utf-8");
|
|
6107
6774
|
const parsed = JSON.parse(data);
|
|
6108
6775
|
this.fileHashCache = new Map(Object.entries(parsed));
|
|
6109
6776
|
} catch (error) {
|
|
@@ -6123,15 +6790,26 @@ var Indexer = class {
|
|
|
6123
6790
|
this.atomicWriteSync(this.fileHashCachePath, JSON.stringify(obj));
|
|
6124
6791
|
}
|
|
6125
6792
|
atomicWriteSync(targetPath, data) {
|
|
6126
|
-
const
|
|
6127
|
-
(
|
|
6128
|
-
(0,
|
|
6129
|
-
|
|
6793
|
+
const lease = this.requireActiveLease();
|
|
6794
|
+
const tempPath = createLeaseTemporaryPath(targetPath, lease.owner, "tmp");
|
|
6795
|
+
(0, import_fs8.mkdirSync)(path13.dirname(targetPath), { recursive: true });
|
|
6796
|
+
try {
|
|
6797
|
+
(0, import_fs8.writeFileSync)(tempPath, data);
|
|
6798
|
+
(0, import_fs8.renameSync)(tempPath, targetPath);
|
|
6799
|
+
} finally {
|
|
6800
|
+
removeLeaseTemporaryPath(tempPath);
|
|
6801
|
+
}
|
|
6802
|
+
}
|
|
6803
|
+
saveInvertedIndex(invertedIndex) {
|
|
6804
|
+
this.atomicWriteSync(
|
|
6805
|
+
path13.join(this.indexPath, "inverted-index.json"),
|
|
6806
|
+
invertedIndex.serialize()
|
|
6807
|
+
);
|
|
6130
6808
|
}
|
|
6131
6809
|
getScopedRoots() {
|
|
6132
|
-
const roots = /* @__PURE__ */ new Set([
|
|
6810
|
+
const roots = /* @__PURE__ */ new Set([path13.resolve(this.projectRoot)]);
|
|
6133
6811
|
for (const kbRoot of this.config.knowledgeBases) {
|
|
6134
|
-
roots.add(
|
|
6812
|
+
roots.add(path13.resolve(this.projectRoot, kbRoot));
|
|
6135
6813
|
}
|
|
6136
6814
|
return Array.from(roots);
|
|
6137
6815
|
}
|
|
@@ -6140,29 +6818,29 @@ var Indexer = class {
|
|
|
6140
6818
|
if (this.config.scope !== "global") {
|
|
6141
6819
|
return branchName;
|
|
6142
6820
|
}
|
|
6143
|
-
const projectHash = hashContent(
|
|
6821
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6144
6822
|
return `${projectHash}:${branchName}`;
|
|
6145
6823
|
}
|
|
6146
6824
|
getBranchCatalogKeyFor(branchName) {
|
|
6147
6825
|
if (this.config.scope !== "global") {
|
|
6148
6826
|
return branchName;
|
|
6149
6827
|
}
|
|
6150
|
-
const projectHash = hashContent(
|
|
6828
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6151
6829
|
return `${projectHash}:${branchName}`;
|
|
6152
6830
|
}
|
|
6153
6831
|
getLegacyBranchCatalogKey() {
|
|
6154
6832
|
return this.currentBranch || "default";
|
|
6155
6833
|
}
|
|
6156
6834
|
getLegacyMigrationMetadataKey() {
|
|
6157
|
-
const projectHash = hashContent(
|
|
6835
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6158
6836
|
return `index.globalBranchMigration.${projectHash}`;
|
|
6159
6837
|
}
|
|
6160
6838
|
getProjectEmbeddingStrategyMetadataKey() {
|
|
6161
|
-
const projectHash = hashContent(
|
|
6839
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6162
6840
|
return `index.embeddingStrategyVersion.${projectHash}`;
|
|
6163
6841
|
}
|
|
6164
6842
|
getProjectForceReembedMetadataKey() {
|
|
6165
|
-
const projectHash = hashContent(
|
|
6843
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6166
6844
|
return `index.forceReembed.${projectHash}`;
|
|
6167
6845
|
}
|
|
6168
6846
|
hasProjectForceReembedPending() {
|
|
@@ -6256,7 +6934,7 @@ var Indexer = class {
|
|
|
6256
6934
|
if (!this.database) {
|
|
6257
6935
|
return { chunkIds, symbolIds };
|
|
6258
6936
|
}
|
|
6259
|
-
const projectRootPath =
|
|
6937
|
+
const projectRootPath = path13.resolve(this.projectRoot);
|
|
6260
6938
|
const projectLocalFilePaths = /* @__PURE__ */ new Set([
|
|
6261
6939
|
...Array.from(this.fileHashCache.keys()).filter(
|
|
6262
6940
|
(filePath) => this.isFileInCurrentScope(filePath, roots) && isPathWithinRoot(filePath, projectRootPath)
|
|
@@ -6279,7 +6957,7 @@ var Indexer = class {
|
|
|
6279
6957
|
if (this.config.scope !== "global") {
|
|
6280
6958
|
return this.getBranchCatalogCleanupKeys();
|
|
6281
6959
|
}
|
|
6282
|
-
const projectHash = hashContent(
|
|
6960
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6283
6961
|
const keys = /* @__PURE__ */ new Set();
|
|
6284
6962
|
const projectChunkIdSet = new Set(projectChunkIds);
|
|
6285
6963
|
const projectSymbolIdSet = new Set(projectSymbolIds);
|
|
@@ -6363,7 +7041,7 @@ var Indexer = class {
|
|
|
6363
7041
|
if (!this.database || this.config.scope !== "global") {
|
|
6364
7042
|
return false;
|
|
6365
7043
|
}
|
|
6366
|
-
const projectHash = hashContent(
|
|
7044
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6367
7045
|
const roots = this.getScopedRoots();
|
|
6368
7046
|
const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots);
|
|
6369
7047
|
return this.database.getAllBranches().some(
|
|
@@ -6397,7 +7075,7 @@ var Indexer = class {
|
|
|
6397
7075
|
...Array.from(this.fileHashCache.keys()).filter((filePath) => this.isFileInCurrentScope(filePath, roots)),
|
|
6398
7076
|
...scopedEntries.map(({ metadata }) => metadata.filePath)
|
|
6399
7077
|
]);
|
|
6400
|
-
const projectRootPath =
|
|
7078
|
+
const projectRootPath = path13.resolve(this.projectRoot);
|
|
6401
7079
|
const projectLocalFilePaths = new Set(
|
|
6402
7080
|
Array.from(filePaths).filter((filePath) => isPathWithinRoot(filePath, projectRootPath))
|
|
6403
7081
|
);
|
|
@@ -6459,34 +7137,27 @@ var Indexer = class {
|
|
|
6459
7137
|
database.gcOrphanEmbeddings();
|
|
6460
7138
|
database.gcOrphanChunks();
|
|
6461
7139
|
store.save();
|
|
6462
|
-
|
|
7140
|
+
this.saveInvertedIndex(invertedIndex);
|
|
6463
7141
|
return {
|
|
6464
7142
|
removedChunkIds: removedChunkIdList,
|
|
6465
7143
|
hasForeignData: allMetadata.some(({ metadata }) => !this.isFileInCurrentScope(metadata.filePath, roots))
|
|
6466
7144
|
};
|
|
6467
7145
|
}
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
(0, import_fs7.writeFileSync)(this.indexingLockPath, JSON.stringify(lockData));
|
|
6477
|
-
}
|
|
6478
|
-
releaseIndexingLock() {
|
|
6479
|
-
if ((0, import_fs7.existsSync)(this.indexingLockPath)) {
|
|
6480
|
-
(0, import_fs7.unlinkSync)(this.indexingLockPath);
|
|
7146
|
+
async recoverFromInterruptedIndexingUnlocked(owners) {
|
|
7147
|
+
for (const owner of owners) {
|
|
7148
|
+
this.logger.warn("Detected interrupted indexing session, recovering...", {
|
|
7149
|
+
pid: owner.pid,
|
|
7150
|
+
hostname: owner.hostname,
|
|
7151
|
+
operation: owner.operation,
|
|
7152
|
+
startedAt: owner.startedAt
|
|
7153
|
+
});
|
|
6481
7154
|
}
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
7155
|
+
if (this.config.scope === "global") {
|
|
7156
|
+
if ((0, import_fs8.existsSync)(this.fileHashCachePath)) {
|
|
7157
|
+
(0, import_fs8.unlinkSync)(this.fileHashCachePath);
|
|
7158
|
+
}
|
|
7159
|
+
await this.healthCheckUnlocked();
|
|
6487
7160
|
}
|
|
6488
|
-
await this.healthCheck();
|
|
6489
|
-
this.releaseIndexingLock();
|
|
6490
7161
|
this.logger.info("Recovery complete, next index will re-process all files");
|
|
6491
7162
|
}
|
|
6492
7163
|
loadFailedBatches(maxChunkTokens) {
|
|
@@ -6502,10 +7173,10 @@ var Indexer = class {
|
|
|
6502
7173
|
}
|
|
6503
7174
|
}
|
|
6504
7175
|
loadSerializedFailedBatches() {
|
|
6505
|
-
if (!(0,
|
|
7176
|
+
if (!(0, import_fs8.existsSync)(this.failedBatchesPath)) {
|
|
6506
7177
|
return [];
|
|
6507
7178
|
}
|
|
6508
|
-
const data = (0,
|
|
7179
|
+
const data = (0, import_fs8.readFileSync)(this.failedBatchesPath, "utf-8");
|
|
6509
7180
|
const parsed = JSON.parse(data);
|
|
6510
7181
|
return parsed.map((batch) => {
|
|
6511
7182
|
const chunks = Array.isArray(batch.chunks) ? batch.chunks : [];
|
|
@@ -6522,15 +7193,15 @@ var Indexer = class {
|
|
|
6522
7193
|
}
|
|
6523
7194
|
saveFailedBatches(batches) {
|
|
6524
7195
|
if (batches.length === 0) {
|
|
6525
|
-
if ((0,
|
|
7196
|
+
if ((0, import_fs8.existsSync)(this.failedBatchesPath)) {
|
|
6526
7197
|
try {
|
|
6527
|
-
(0,
|
|
7198
|
+
(0, import_fs8.unlinkSync)(this.failedBatchesPath);
|
|
6528
7199
|
} catch {
|
|
6529
7200
|
}
|
|
6530
7201
|
}
|
|
6531
7202
|
return;
|
|
6532
7203
|
}
|
|
6533
|
-
|
|
7204
|
+
this.atomicWriteSync(this.failedBatchesPath, JSON.stringify(batches, null, 2));
|
|
6534
7205
|
}
|
|
6535
7206
|
collectRetryableFailedChunks(currentFileHashes, unchangedFilePaths, maxChunkTokens) {
|
|
6536
7207
|
const retryableById = /* @__PURE__ */ new Map();
|
|
@@ -6710,7 +7381,7 @@ var Indexer = class {
|
|
|
6710
7381
|
const intent = isLikelyImplementationPath(candidate.metadata.filePath) ? "implementation" : "doc_or_test";
|
|
6711
7382
|
parts.push(`intent_hint: ${intent}`);
|
|
6712
7383
|
try {
|
|
6713
|
-
const fileContent = await
|
|
7384
|
+
const fileContent = await import_fs8.promises.readFile(candidate.metadata.filePath, "utf-8");
|
|
6714
7385
|
const lines = fileContent.split("\n");
|
|
6715
7386
|
const snippetStartLine = Math.max(1, candidate.metadata.startLine - 2);
|
|
6716
7387
|
const snippetEndLine = Math.min(lines.length, candidate.metadata.endLine + 2);
|
|
@@ -6724,9 +7395,225 @@ var Indexer = class {
|
|
|
6724
7395
|
return parts.join("\n");
|
|
6725
7396
|
}
|
|
6726
7397
|
async initialize() {
|
|
6727
|
-
if (this.
|
|
6728
|
-
|
|
6729
|
-
|
|
7398
|
+
if (this.initializationPromise) {
|
|
7399
|
+
await this.initializationPromise;
|
|
7400
|
+
}
|
|
7401
|
+
if (this.isInitializedFor("reader")) {
|
|
7402
|
+
return;
|
|
7403
|
+
}
|
|
7404
|
+
await this.initializeOnce("reader", [], { skipAutoGc: true });
|
|
7405
|
+
}
|
|
7406
|
+
async initializeOnce(mode, recoveredOwners, options) {
|
|
7407
|
+
if (this.initializationPromise) {
|
|
7408
|
+
await this.initializationPromise;
|
|
7409
|
+
if (this.isInitializedFor(mode)) {
|
|
7410
|
+
return;
|
|
7411
|
+
}
|
|
7412
|
+
return this.initializeOnce(mode, recoveredOwners, options);
|
|
7413
|
+
}
|
|
7414
|
+
if (this.isInitializedFor(mode)) {
|
|
7415
|
+
return;
|
|
7416
|
+
}
|
|
7417
|
+
const initialization = this.initializeUnlocked(mode, recoveredOwners, options).catch((error) => {
|
|
7418
|
+
this.resetLoadedIndexState();
|
|
7419
|
+
throw error;
|
|
7420
|
+
}).finally(() => {
|
|
7421
|
+
if (this.initializationPromise === initialization) {
|
|
7422
|
+
this.initializationPromise = null;
|
|
7423
|
+
}
|
|
7424
|
+
});
|
|
7425
|
+
this.initializationPromise = initialization;
|
|
7426
|
+
await initialization;
|
|
7427
|
+
}
|
|
7428
|
+
isInitializedFor(mode) {
|
|
7429
|
+
const hasState = Boolean(
|
|
7430
|
+
this.store && this.provider && this.invertedIndex && this.configuredProviderInfo && this.database
|
|
7431
|
+
);
|
|
7432
|
+
if (!hasState) {
|
|
7433
|
+
return false;
|
|
7434
|
+
}
|
|
7435
|
+
return mode === "reader" ? this.initializationMode !== "none" : this.initializationMode === "writer";
|
|
7436
|
+
}
|
|
7437
|
+
recordReadIssue(component, message, error) {
|
|
7438
|
+
this.readIssues.push(this.createReadIssue(component, message));
|
|
7439
|
+
this.readerArtifactRetryAfter.set(component, Date.now() + READER_ARTIFACT_RETRY_INTERVAL_MS);
|
|
7440
|
+
this.logger.warn(message, error === void 0 ? void 0 : { error: getErrorMessage2(error) });
|
|
7441
|
+
}
|
|
7442
|
+
createReadIssue(component, message) {
|
|
7443
|
+
return {
|
|
7444
|
+
component,
|
|
7445
|
+
message,
|
|
7446
|
+
blocking: component !== "keyword"
|
|
7447
|
+
};
|
|
7448
|
+
}
|
|
7449
|
+
getVectorReadIssueMessage() {
|
|
7450
|
+
if (this.config.scope === "global") {
|
|
7451
|
+
return "Shared vector index could not be read. Restore or repair the complete fingerprinted shared vector artifacts; automatic reset is disabled for global scope.";
|
|
7452
|
+
}
|
|
7453
|
+
if (!this.isLocalProjectIndexPath()) {
|
|
7454
|
+
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.";
|
|
7455
|
+
}
|
|
7456
|
+
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.";
|
|
7457
|
+
}
|
|
7458
|
+
getKeywordReadIssueMessage() {
|
|
7459
|
+
if (this.config.scope === "global") {
|
|
7460
|
+
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.";
|
|
7461
|
+
}
|
|
7462
|
+
if (!this.isLocalProjectIndexPath()) {
|
|
7463
|
+
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.";
|
|
7464
|
+
}
|
|
7465
|
+
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.";
|
|
7466
|
+
}
|
|
7467
|
+
getDatabaseReadIssueMessage() {
|
|
7468
|
+
if (this.config.scope === "global") {
|
|
7469
|
+
return "Shared index database could not be read. Restore or repair the shared SQLite database; automatic reset is disabled for global scope.";
|
|
7470
|
+
}
|
|
7471
|
+
if (!this.isLocalProjectIndexPath()) {
|
|
7472
|
+
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.";
|
|
7473
|
+
}
|
|
7474
|
+
return "Index database could not be read. Run index_codebase after the active writer finishes to repair or migrate it under the writer lease.";
|
|
7475
|
+
}
|
|
7476
|
+
getReaderFileFingerprint(filePath, identityOnly = false) {
|
|
7477
|
+
try {
|
|
7478
|
+
const stats = (0, import_fs8.statSync)(filePath);
|
|
7479
|
+
if (identityOnly) {
|
|
7480
|
+
return `${stats.dev}:${stats.ino}`;
|
|
7481
|
+
}
|
|
7482
|
+
return `${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeMs}:${stats.ctimeMs}`;
|
|
7483
|
+
} catch (error) {
|
|
7484
|
+
return `unavailable:${getErrorMessage2(error)}`;
|
|
7485
|
+
}
|
|
7486
|
+
}
|
|
7487
|
+
captureReaderArtifactFingerprint() {
|
|
7488
|
+
const storePath = path13.join(this.indexPath, "vectors");
|
|
7489
|
+
return {
|
|
7490
|
+
vectors: `${this.getReaderFileFingerprint(storePath)}|${this.getReaderFileFingerprint(`${storePath}.meta.json`)}`,
|
|
7491
|
+
keyword: this.getReaderFileFingerprint(path13.join(this.indexPath, "inverted-index.json")),
|
|
7492
|
+
database: this.getReaderFileFingerprint(path13.join(this.indexPath, "codebase.db")),
|
|
7493
|
+
databaseIdentity: this.getReaderFileFingerprint(path13.join(this.indexPath, "codebase.db"), true)
|
|
7494
|
+
};
|
|
7495
|
+
}
|
|
7496
|
+
refreshReaderArtifacts() {
|
|
7497
|
+
if (this.initializationMode !== "reader" || !this.configuredProviderInfo) {
|
|
7498
|
+
return;
|
|
7499
|
+
}
|
|
7500
|
+
const previousFingerprint = this.readerArtifactFingerprint;
|
|
7501
|
+
const currentFingerprint = this.captureReaderArtifactFingerprint();
|
|
7502
|
+
const issues = new Map(this.readIssues.map((issue) => [issue.component, issue]));
|
|
7503
|
+
const retryDue = (component) => issues.has(component) && Date.now() >= (this.readerArtifactRetryAfter.get(component) ?? 0);
|
|
7504
|
+
const vectorsChanged = !previousFingerprint || currentFingerprint.vectors !== previousFingerprint.vectors;
|
|
7505
|
+
const keywordChanged = !previousFingerprint || currentFingerprint.keyword !== previousFingerprint.keyword;
|
|
7506
|
+
const databaseChanged = !previousFingerprint || currentFingerprint.database !== previousFingerprint.database;
|
|
7507
|
+
const databaseReplaced = !previousFingerprint || currentFingerprint.databaseIdentity !== previousFingerprint.databaseIdentity;
|
|
7508
|
+
if (previousFingerprint && !vectorsChanged && !keywordChanged && !databaseChanged && !Array.from(issues.keys()).some(retryDue)) {
|
|
7509
|
+
return;
|
|
7510
|
+
}
|
|
7511
|
+
const setIssue = (component, message, error) => {
|
|
7512
|
+
if (!issues.has(component)) {
|
|
7513
|
+
this.logger.warn(message, error === void 0 ? void 0 : { error: getErrorMessage2(error) });
|
|
7514
|
+
}
|
|
7515
|
+
issues.set(component, this.createReadIssue(component, message));
|
|
7516
|
+
this.readerArtifactRetryAfter.set(component, Date.now() + READER_ARTIFACT_RETRY_INTERVAL_MS);
|
|
7517
|
+
};
|
|
7518
|
+
const storePath = path13.join(this.indexPath, "vectors");
|
|
7519
|
+
const vectorMetadataPath = `${storePath}.meta.json`;
|
|
7520
|
+
const invertedIndexPath = path13.join(this.indexPath, "inverted-index.json");
|
|
7521
|
+
const dbPath = path13.join(this.indexPath, "codebase.db");
|
|
7522
|
+
if (vectorsChanged || retryDue("vectors")) {
|
|
7523
|
+
const vectorStoreExists = (0, import_fs8.existsSync)(storePath);
|
|
7524
|
+
const vectorMetadataExists = (0, import_fs8.existsSync)(vectorMetadataPath);
|
|
7525
|
+
if (vectorStoreExists && vectorMetadataExists) {
|
|
7526
|
+
try {
|
|
7527
|
+
const store = new VectorStore(storePath, this.configuredProviderInfo.modelInfo.dimensions);
|
|
7528
|
+
store.loadStrict();
|
|
7529
|
+
this.store = store;
|
|
7530
|
+
issues.delete("vectors");
|
|
7531
|
+
this.readerArtifactRetryAfter.delete("vectors");
|
|
7532
|
+
} catch (error) {
|
|
7533
|
+
setIssue("vectors", this.getVectorReadIssueMessage(), error);
|
|
7534
|
+
}
|
|
7535
|
+
} else if (vectorStoreExists !== vectorMetadataExists || issues.has("vectors")) {
|
|
7536
|
+
setIssue("vectors", this.getVectorReadIssueMessage());
|
|
7537
|
+
}
|
|
7538
|
+
}
|
|
7539
|
+
if (keywordChanged || retryDue("keyword") || !(0, import_fs8.existsSync)(invertedIndexPath) && (this.store?.count() ?? 0) > 0) {
|
|
7540
|
+
if ((0, import_fs8.existsSync)(invertedIndexPath)) {
|
|
7541
|
+
try {
|
|
7542
|
+
const invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
7543
|
+
invertedIndex.load();
|
|
7544
|
+
this.invertedIndex = invertedIndex;
|
|
7545
|
+
issues.delete("keyword");
|
|
7546
|
+
this.readerArtifactRetryAfter.delete("keyword");
|
|
7547
|
+
} catch (error) {
|
|
7548
|
+
setIssue("keyword", this.getKeywordReadIssueMessage(), error);
|
|
7549
|
+
}
|
|
7550
|
+
} else if ((this.store?.count() ?? 0) > 0 || issues.has("keyword")) {
|
|
7551
|
+
setIssue("keyword", this.getKeywordReadIssueMessage());
|
|
7552
|
+
}
|
|
7553
|
+
}
|
|
7554
|
+
if (databaseReplaced || databaseChanged && issues.has("database") || retryDue("database")) {
|
|
7555
|
+
if ((0, import_fs8.existsSync)(dbPath)) {
|
|
7556
|
+
try {
|
|
7557
|
+
const database = Database.openReadOnly(dbPath);
|
|
7558
|
+
if (this.database) {
|
|
7559
|
+
this.retiredDatabases.push(this.database);
|
|
7560
|
+
}
|
|
7561
|
+
this.database = database;
|
|
7562
|
+
issues.delete("database");
|
|
7563
|
+
this.readerArtifactRetryAfter.delete("database");
|
|
7564
|
+
} catch (error) {
|
|
7565
|
+
setIssue("database", this.getDatabaseReadIssueMessage(), error);
|
|
7566
|
+
}
|
|
7567
|
+
} else if ((this.store?.count() ?? 0) > 0 || issues.has("database")) {
|
|
7568
|
+
setIssue("database", this.getDatabaseReadIssueMessage());
|
|
7569
|
+
}
|
|
7570
|
+
}
|
|
7571
|
+
if (!issues.has("database")) {
|
|
7572
|
+
try {
|
|
7573
|
+
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
7574
|
+
} catch (error) {
|
|
7575
|
+
setIssue("database", this.getDatabaseReadIssueMessage(), error);
|
|
7576
|
+
}
|
|
7577
|
+
}
|
|
7578
|
+
this.readIssues = Array.from(issues.values());
|
|
7579
|
+
this.readerArtifactFingerprint = currentFingerprint;
|
|
7580
|
+
}
|
|
7581
|
+
refreshInactiveWriterArtifacts() {
|
|
7582
|
+
if (this.initializationMode !== "writer" || this.activeIndexLease) {
|
|
7583
|
+
return true;
|
|
7584
|
+
}
|
|
7585
|
+
const previousFingerprint = this.writerArtifactFingerprint;
|
|
7586
|
+
const currentFingerprint = this.captureReaderArtifactFingerprint();
|
|
7587
|
+
const retryDue = this.readIssues.some(
|
|
7588
|
+
(issue) => Date.now() >= (this.readerArtifactRetryAfter.get(issue.component) ?? 0)
|
|
7589
|
+
);
|
|
7590
|
+
const artifactsChanged = !previousFingerprint || currentFingerprint.vectors !== previousFingerprint.vectors || currentFingerprint.keyword !== previousFingerprint.keyword || currentFingerprint.database !== previousFingerprint.database || currentFingerprint.databaseIdentity !== previousFingerprint.databaseIdentity;
|
|
7591
|
+
if (!artifactsChanged && !retryDue) {
|
|
7592
|
+
return true;
|
|
7593
|
+
}
|
|
7594
|
+
if (!previousFingerprint || currentFingerprint.databaseIdentity !== previousFingerprint.databaseIdentity) {
|
|
7595
|
+
return false;
|
|
7596
|
+
}
|
|
7597
|
+
this.initializationMode = "reader";
|
|
7598
|
+
this.readerArtifactFingerprint = previousFingerprint;
|
|
7599
|
+
try {
|
|
7600
|
+
this.refreshReaderArtifacts();
|
|
7601
|
+
this.writerArtifactFingerprint = this.readerArtifactFingerprint ?? currentFingerprint;
|
|
7602
|
+
} finally {
|
|
7603
|
+
this.readerArtifactFingerprint = null;
|
|
7604
|
+
this.initializationMode = "writer";
|
|
7605
|
+
}
|
|
7606
|
+
return true;
|
|
7607
|
+
}
|
|
7608
|
+
async initializeUnlocked(mode, recoveredOwners = [], options = {}) {
|
|
7609
|
+
if (mode === "writer") {
|
|
7610
|
+
this.requireActiveLease();
|
|
7611
|
+
}
|
|
7612
|
+
this.readIssues = [];
|
|
7613
|
+
this.readerArtifactRetryAfter.clear();
|
|
7614
|
+
if (this.config.embeddingProvider === "custom") {
|
|
7615
|
+
if (!this.config.customProvider) {
|
|
7616
|
+
throw new Error("embeddingProvider is 'custom' but customProvider config is missing.");
|
|
6730
7617
|
}
|
|
6731
7618
|
this.configuredProviderInfo = createCustomProviderInfo(this.config.customProvider);
|
|
6732
7619
|
} else if (this.config.embeddingProvider === "auto") {
|
|
@@ -6755,36 +7642,103 @@ var Indexer = class {
|
|
|
6755
7642
|
});
|
|
6756
7643
|
}
|
|
6757
7644
|
}
|
|
6758
|
-
await import_fs7.promises.mkdir(this.indexPath, { recursive: true });
|
|
6759
7645
|
const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
|
|
6760
|
-
const storePath =
|
|
6761
|
-
|
|
6762
|
-
const
|
|
6763
|
-
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
7646
|
+
const storePath = path13.join(this.indexPath, "vectors");
|
|
7647
|
+
const vectorMetadataPath = `${storePath}.meta.json`;
|
|
7648
|
+
const invertedIndexPath = path13.join(this.indexPath, "inverted-index.json");
|
|
7649
|
+
const dbPath = path13.join(this.indexPath, "codebase.db");
|
|
7650
|
+
let dbIsNew = !(0, import_fs8.existsSync)(dbPath);
|
|
7651
|
+
const readerArtifactFingerprint = mode === "reader" ? this.captureReaderArtifactFingerprint() : null;
|
|
7652
|
+
if (mode === "writer") {
|
|
7653
|
+
await import_fs8.promises.mkdir(this.indexPath, { recursive: true });
|
|
7654
|
+
if (recoveredOwners.length > 0 && this.config.scope === "project" && !this.isLocalProjectIndexPath()) {
|
|
7655
|
+
throw new Error(
|
|
7656
|
+
"Interrupted indexing recovery is unsafe while using an inherited worktree index. Run index_codebase with force=true to create a local project index boundary."
|
|
7657
|
+
);
|
|
7658
|
+
}
|
|
7659
|
+
for (const recoveredOwner of recoveredOwners) {
|
|
7660
|
+
recoverLeaseArtifacts(this.indexPath, recoveredOwner, [
|
|
7661
|
+
storePath,
|
|
7662
|
+
`${storePath}.meta.json`
|
|
7663
|
+
]);
|
|
7664
|
+
}
|
|
7665
|
+
if (recoveredOwners.length > 0 && this.config.scope === "project") {
|
|
7666
|
+
await this.resetLocalIndexArtifacts();
|
|
7667
|
+
}
|
|
7668
|
+
this.store = new VectorStore(storePath, dimensions);
|
|
7669
|
+
if ((0, import_fs8.existsSync)(storePath) || (0, import_fs8.existsSync)(vectorMetadataPath)) {
|
|
7670
|
+
this.store.load();
|
|
6773
7671
|
}
|
|
6774
7672
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
6781
|
-
|
|
6782
|
-
throw error;
|
|
7673
|
+
try {
|
|
7674
|
+
this.invertedIndex.load();
|
|
7675
|
+
} catch {
|
|
7676
|
+
if ((0, import_fs8.existsSync)(invertedIndexPath)) {
|
|
7677
|
+
await import_fs8.promises.unlink(invertedIndexPath);
|
|
7678
|
+
}
|
|
7679
|
+
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
6783
7680
|
}
|
|
7681
|
+
try {
|
|
7682
|
+
this.database = new Database(dbPath);
|
|
7683
|
+
} catch (error) {
|
|
7684
|
+
if (!await this.tryResetCorruptedIndex("initializing index database", error)) {
|
|
7685
|
+
throw error;
|
|
7686
|
+
}
|
|
7687
|
+
this.store = new VectorStore(storePath, dimensions);
|
|
7688
|
+
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
7689
|
+
this.database = new Database(dbPath);
|
|
7690
|
+
dbIsNew = true;
|
|
7691
|
+
}
|
|
7692
|
+
} else {
|
|
6784
7693
|
this.store = new VectorStore(storePath, dimensions);
|
|
7694
|
+
const vectorStoreExists = (0, import_fs8.existsSync)(storePath);
|
|
7695
|
+
const vectorMetadataExists = (0, import_fs8.existsSync)(vectorMetadataPath);
|
|
7696
|
+
const vectorReadFailureMessage = this.getVectorReadIssueMessage();
|
|
7697
|
+
if (vectorStoreExists !== vectorMetadataExists) {
|
|
7698
|
+
this.recordReadIssue("vectors", vectorReadFailureMessage);
|
|
7699
|
+
} else if (vectorStoreExists) {
|
|
7700
|
+
try {
|
|
7701
|
+
this.store.loadStrict();
|
|
7702
|
+
} catch (error) {
|
|
7703
|
+
this.recordReadIssue("vectors", vectorReadFailureMessage, error);
|
|
7704
|
+
this.store = new VectorStore(storePath, dimensions);
|
|
7705
|
+
}
|
|
7706
|
+
}
|
|
6785
7707
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
6786
|
-
|
|
6787
|
-
|
|
7708
|
+
if ((0, import_fs8.existsSync)(invertedIndexPath)) {
|
|
7709
|
+
try {
|
|
7710
|
+
this.invertedIndex.load();
|
|
7711
|
+
} catch (error) {
|
|
7712
|
+
this.recordReadIssue(
|
|
7713
|
+
"keyword",
|
|
7714
|
+
this.getKeywordReadIssueMessage(),
|
|
7715
|
+
error
|
|
7716
|
+
);
|
|
7717
|
+
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
7718
|
+
}
|
|
7719
|
+
} else if (this.store.count() > 0) {
|
|
7720
|
+
this.recordReadIssue("keyword", this.getKeywordReadIssueMessage());
|
|
7721
|
+
}
|
|
7722
|
+
if ((0, import_fs8.existsSync)(dbPath)) {
|
|
7723
|
+
try {
|
|
7724
|
+
this.database = Database.openReadOnly(dbPath);
|
|
7725
|
+
} catch (error) {
|
|
7726
|
+
this.recordReadIssue(
|
|
7727
|
+
"database",
|
|
7728
|
+
this.getDatabaseReadIssueMessage(),
|
|
7729
|
+
error
|
|
7730
|
+
);
|
|
7731
|
+
this.database = Database.createEmptyReadOnly();
|
|
7732
|
+
}
|
|
7733
|
+
} else {
|
|
7734
|
+
this.database = Database.createEmptyReadOnly();
|
|
7735
|
+
if (this.store.count() > 0) {
|
|
7736
|
+
this.recordReadIssue(
|
|
7737
|
+
"database",
|
|
7738
|
+
`Index database is missing for the published vectors. ${this.getDatabaseReadIssueMessage()}`
|
|
7739
|
+
);
|
|
7740
|
+
}
|
|
7741
|
+
}
|
|
6788
7742
|
}
|
|
6789
7743
|
if (isGitRepo(this.projectRoot)) {
|
|
6790
7744
|
this.currentBranch = getBranchOrDefault(this.projectRoot);
|
|
@@ -6798,10 +7752,10 @@ var Indexer = class {
|
|
|
6798
7752
|
this.baseBranch = "default";
|
|
6799
7753
|
this.logger.branch("debug", "Not a git repository, using default branch");
|
|
6800
7754
|
}
|
|
6801
|
-
if (
|
|
6802
|
-
await this.
|
|
7755
|
+
if (mode === "writer" && recoveredOwners.length > 0) {
|
|
7756
|
+
await this.recoverFromInterruptedIndexingUnlocked(recoveredOwners);
|
|
6803
7757
|
}
|
|
6804
|
-
if (dbIsNew && this.store.count() > 0) {
|
|
7758
|
+
if (mode === "writer" && dbIsNew && this.store.count() > 0) {
|
|
6805
7759
|
this.migrateFromLegacyIndex();
|
|
6806
7760
|
}
|
|
6807
7761
|
this.loadFileHashCache();
|
|
@@ -6813,9 +7767,11 @@ var Indexer = class {
|
|
|
6813
7767
|
configuredProviderInfo: this.configuredProviderInfo
|
|
6814
7768
|
});
|
|
6815
7769
|
}
|
|
6816
|
-
if (this.config.indexing.autoGc) {
|
|
7770
|
+
if (mode === "writer" && this.config.indexing.autoGc && !options.skipAutoGc) {
|
|
6817
7771
|
await this.maybeRunAutoGc();
|
|
6818
7772
|
}
|
|
7773
|
+
this.initializationMode = mode;
|
|
7774
|
+
this.readerArtifactFingerprint = readerArtifactFingerprint;
|
|
6819
7775
|
}
|
|
6820
7776
|
async maybeRunAutoGc() {
|
|
6821
7777
|
if (!this.database) return;
|
|
@@ -6832,7 +7788,7 @@ var Indexer = class {
|
|
|
6832
7788
|
}
|
|
6833
7789
|
}
|
|
6834
7790
|
if (shouldRunGc) {
|
|
6835
|
-
const result = await this.
|
|
7791
|
+
const result = await this.healthCheckUnlocked();
|
|
6836
7792
|
if (result.warning) {
|
|
6837
7793
|
this.database.setMetadata(STARTUP_WARNING_METADATA_KEY, result.warning);
|
|
6838
7794
|
} else {
|
|
@@ -6854,7 +7810,7 @@ var Indexer = class {
|
|
|
6854
7810
|
if (await this.tryResetCorruptedIndex("running automatic orphan garbage collection", error)) {
|
|
6855
7811
|
return {
|
|
6856
7812
|
resetCorruptedIndex: true,
|
|
6857
|
-
warning: this.getCorruptedIndexWarning(
|
|
7813
|
+
warning: this.getCorruptedIndexWarning(path13.join(this.indexPath, "codebase.db"))
|
|
6858
7814
|
};
|
|
6859
7815
|
}
|
|
6860
7816
|
throw error;
|
|
@@ -6869,28 +7825,29 @@ var Indexer = class {
|
|
|
6869
7825
|
return;
|
|
6870
7826
|
}
|
|
6871
7827
|
const retainedEntries = store.getAllMetadata().filter(({ key }) => !excludedSet.has(key));
|
|
6872
|
-
const storeBasePath =
|
|
6873
|
-
const storeIndexPath =
|
|
7828
|
+
const storeBasePath = path13.join(this.indexPath, "vectors");
|
|
7829
|
+
const storeIndexPath = storeBasePath;
|
|
6874
7830
|
const storeMetadataPath = `${storeBasePath}.meta.json`;
|
|
6875
|
-
const
|
|
6876
|
-
const
|
|
7831
|
+
const lease = this.requireActiveLease();
|
|
7832
|
+
const backupIndexPath = createLeaseTemporaryPath(storeIndexPath, lease.owner, "bak");
|
|
7833
|
+
const backupMetadataPath = createLeaseTemporaryPath(storeMetadataPath, lease.owner, "bak");
|
|
6877
7834
|
let backedUpIndex = false;
|
|
6878
7835
|
let backedUpMetadata = false;
|
|
6879
7836
|
let rebuiltCount = 0;
|
|
6880
7837
|
let skippedCount = 0;
|
|
6881
|
-
if ((0,
|
|
6882
|
-
(0,
|
|
7838
|
+
if ((0, import_fs8.existsSync)(backupIndexPath)) {
|
|
7839
|
+
(0, import_fs8.unlinkSync)(backupIndexPath);
|
|
6883
7840
|
}
|
|
6884
|
-
if ((0,
|
|
6885
|
-
(0,
|
|
7841
|
+
if ((0, import_fs8.existsSync)(backupMetadataPath)) {
|
|
7842
|
+
(0, import_fs8.unlinkSync)(backupMetadataPath);
|
|
6886
7843
|
}
|
|
6887
7844
|
try {
|
|
6888
|
-
if ((0,
|
|
6889
|
-
(0,
|
|
7845
|
+
if ((0, import_fs8.existsSync)(storeIndexPath)) {
|
|
7846
|
+
(0, import_fs8.renameSync)(storeIndexPath, backupIndexPath);
|
|
6890
7847
|
backedUpIndex = true;
|
|
6891
7848
|
}
|
|
6892
|
-
if ((0,
|
|
6893
|
-
(0,
|
|
7849
|
+
if ((0, import_fs8.existsSync)(storeMetadataPath)) {
|
|
7850
|
+
(0, import_fs8.renameSync)(storeMetadataPath, backupMetadataPath);
|
|
6894
7851
|
backedUpMetadata = true;
|
|
6895
7852
|
}
|
|
6896
7853
|
store.clear();
|
|
@@ -6910,11 +7867,11 @@ var Indexer = class {
|
|
|
6910
7867
|
rebuiltCount += 1;
|
|
6911
7868
|
}
|
|
6912
7869
|
store.save();
|
|
6913
|
-
if (backedUpIndex && (0,
|
|
6914
|
-
(0,
|
|
7870
|
+
if (backedUpIndex && (0, import_fs8.existsSync)(backupIndexPath)) {
|
|
7871
|
+
(0, import_fs8.unlinkSync)(backupIndexPath);
|
|
6915
7872
|
}
|
|
6916
|
-
if (backedUpMetadata && (0,
|
|
6917
|
-
(0,
|
|
7873
|
+
if (backedUpMetadata && (0, import_fs8.existsSync)(backupMetadataPath)) {
|
|
7874
|
+
(0, import_fs8.unlinkSync)(backupMetadataPath);
|
|
6918
7875
|
}
|
|
6919
7876
|
this.logger.gc("info", "Rebuilt vector store to avoid native remove", {
|
|
6920
7877
|
excludedChunks: excludedSet.size,
|
|
@@ -6926,17 +7883,17 @@ var Indexer = class {
|
|
|
6926
7883
|
store.clear();
|
|
6927
7884
|
} catch {
|
|
6928
7885
|
}
|
|
6929
|
-
if ((0,
|
|
6930
|
-
(0,
|
|
7886
|
+
if ((0, import_fs8.existsSync)(storeIndexPath)) {
|
|
7887
|
+
(0, import_fs8.unlinkSync)(storeIndexPath);
|
|
6931
7888
|
}
|
|
6932
|
-
if ((0,
|
|
6933
|
-
(0,
|
|
7889
|
+
if ((0, import_fs8.existsSync)(storeMetadataPath)) {
|
|
7890
|
+
(0, import_fs8.unlinkSync)(storeMetadataPath);
|
|
6934
7891
|
}
|
|
6935
|
-
if (backedUpIndex && (0,
|
|
6936
|
-
(0,
|
|
7892
|
+
if (backedUpIndex && (0, import_fs8.existsSync)(backupIndexPath)) {
|
|
7893
|
+
(0, import_fs8.renameSync)(backupIndexPath, storeIndexPath);
|
|
6937
7894
|
}
|
|
6938
|
-
if (backedUpMetadata && (0,
|
|
6939
|
-
(0,
|
|
7895
|
+
if (backedUpMetadata && (0, import_fs8.existsSync)(backupMetadataPath)) {
|
|
7896
|
+
(0, import_fs8.renameSync)(backupMetadataPath, storeMetadataPath);
|
|
6940
7897
|
}
|
|
6941
7898
|
if (backedUpIndex || backedUpMetadata) {
|
|
6942
7899
|
store.load();
|
|
@@ -6950,11 +7907,37 @@ var Indexer = class {
|
|
|
6950
7907
|
}
|
|
6951
7908
|
return `Detected a corrupted local SQLite index at ${dbPath} and reset the local index. Run index_codebase to rebuild search data.`;
|
|
6952
7909
|
}
|
|
7910
|
+
async resetLocalIndexArtifacts() {
|
|
7911
|
+
this.store = null;
|
|
7912
|
+
this.invertedIndex = null;
|
|
7913
|
+
this.database?.close();
|
|
7914
|
+
this.database = null;
|
|
7915
|
+
this.indexCompatibility = null;
|
|
7916
|
+
this.initializationMode = "none";
|
|
7917
|
+
this.readIssues = [];
|
|
7918
|
+
this.readerArtifactFingerprint = null;
|
|
7919
|
+
this.writerArtifactFingerprint = null;
|
|
7920
|
+
this.readerArtifactRetryAfter.clear();
|
|
7921
|
+
this.fileHashCache.clear();
|
|
7922
|
+
const resetPaths = [
|
|
7923
|
+
path13.join(this.indexPath, "codebase.db"),
|
|
7924
|
+
path13.join(this.indexPath, "codebase.db-shm"),
|
|
7925
|
+
path13.join(this.indexPath, "codebase.db-wal"),
|
|
7926
|
+
path13.join(this.indexPath, "vectors"),
|
|
7927
|
+
path13.join(this.indexPath, "vectors.usearch"),
|
|
7928
|
+
path13.join(this.indexPath, "vectors.meta.json"),
|
|
7929
|
+
path13.join(this.indexPath, "inverted-index.json"),
|
|
7930
|
+
path13.join(this.indexPath, "file-hashes.json"),
|
|
7931
|
+
path13.join(this.indexPath, "failed-batches.json")
|
|
7932
|
+
];
|
|
7933
|
+
await Promise.all(resetPaths.map((targetPath) => import_fs8.promises.rm(targetPath, { recursive: true, force: true })));
|
|
7934
|
+
await import_fs8.promises.mkdir(this.indexPath, { recursive: true });
|
|
7935
|
+
}
|
|
6953
7936
|
async tryResetCorruptedIndex(stage, error) {
|
|
6954
7937
|
if (!isSqliteCorruptionError(error)) {
|
|
6955
7938
|
return false;
|
|
6956
7939
|
}
|
|
6957
|
-
const dbPath =
|
|
7940
|
+
const dbPath = path13.join(this.indexPath, "codebase.db");
|
|
6958
7941
|
const warning = this.getCorruptedIndexWarning(dbPath);
|
|
6959
7942
|
const errorMessage = getErrorMessage2(error);
|
|
6960
7943
|
if (this.config.scope === "global") {
|
|
@@ -6970,30 +7953,7 @@ var Indexer = class {
|
|
|
6970
7953
|
dbPath,
|
|
6971
7954
|
error: errorMessage
|
|
6972
7955
|
});
|
|
6973
|
-
this.
|
|
6974
|
-
this.invertedIndex = null;
|
|
6975
|
-
this.database?.close();
|
|
6976
|
-
this.database = null;
|
|
6977
|
-
this.indexCompatibility = null;
|
|
6978
|
-
this.fileHashCache.clear();
|
|
6979
|
-
const resetPaths = [
|
|
6980
|
-
path11.join(this.indexPath, "codebase.db"),
|
|
6981
|
-
path11.join(this.indexPath, "codebase.db-shm"),
|
|
6982
|
-
path11.join(this.indexPath, "codebase.db-wal"),
|
|
6983
|
-
path11.join(this.indexPath, "vectors.usearch"),
|
|
6984
|
-
path11.join(this.indexPath, "inverted-index.json"),
|
|
6985
|
-
path11.join(this.indexPath, "file-hashes.json"),
|
|
6986
|
-
path11.join(this.indexPath, "failed-batches.json"),
|
|
6987
|
-
path11.join(this.indexPath, "indexing.lock"),
|
|
6988
|
-
path11.join(this.indexPath, "vectors")
|
|
6989
|
-
];
|
|
6990
|
-
await Promise.all(resetPaths.map(async (targetPath) => {
|
|
6991
|
-
try {
|
|
6992
|
-
await import_fs7.promises.rm(targetPath, { recursive: true, force: true });
|
|
6993
|
-
} catch {
|
|
6994
|
-
}
|
|
6995
|
-
}));
|
|
6996
|
-
await import_fs7.promises.mkdir(this.indexPath, { recursive: true });
|
|
7956
|
+
await this.resetLocalIndexArtifacts();
|
|
6997
7957
|
return true;
|
|
6998
7958
|
}
|
|
6999
7959
|
migrateFromLegacyIndex() {
|
|
@@ -7112,8 +8072,62 @@ var Indexer = class {
|
|
|
7112
8072
|
return this.indexCompatibility;
|
|
7113
8073
|
}
|
|
7114
8074
|
async ensureInitialized() {
|
|
8075
|
+
let initializedReader = false;
|
|
8076
|
+
while (true) {
|
|
8077
|
+
if (this.initializationPromise) {
|
|
8078
|
+
await this.initializationPromise;
|
|
8079
|
+
}
|
|
8080
|
+
if (!this.isInitializedFor("reader")) {
|
|
8081
|
+
await this.initialize();
|
|
8082
|
+
initializedReader = true;
|
|
8083
|
+
continue;
|
|
8084
|
+
}
|
|
8085
|
+
if (this.initializationMode === "writer" && !this.activeIndexLease && !initializedReader) {
|
|
8086
|
+
if (!this.refreshInactiveWriterArtifacts()) {
|
|
8087
|
+
this.resetLoadedIndexState(true);
|
|
8088
|
+
await this.initialize();
|
|
8089
|
+
initializedReader = true;
|
|
8090
|
+
continue;
|
|
8091
|
+
}
|
|
8092
|
+
}
|
|
8093
|
+
if (this.initializationMode === "reader" && !initializedReader) {
|
|
8094
|
+
this.refreshReaderArtifacts();
|
|
8095
|
+
}
|
|
8096
|
+
const state = this.requireLoadedIndexState();
|
|
8097
|
+
return {
|
|
8098
|
+
...state,
|
|
8099
|
+
readIssues: [...this.readIssues],
|
|
8100
|
+
compatibility: this.indexCompatibility ?? this.validateIndexCompatibility(state.configuredProviderInfo)
|
|
8101
|
+
};
|
|
8102
|
+
}
|
|
8103
|
+
}
|
|
8104
|
+
async ensureInitializedUnlocked(recoveredOwners = []) {
|
|
8105
|
+
this.requireActiveLease();
|
|
8106
|
+
if (this.initializationPromise) {
|
|
8107
|
+
await this.initializationPromise;
|
|
8108
|
+
}
|
|
8109
|
+
if (recoveredOwners.length > 0 || !this.isInitializedFor("writer")) {
|
|
8110
|
+
const retireReaderDatabase = this.initializationMode === "reader";
|
|
8111
|
+
this.resetLoadedIndexState(retireReaderDatabase);
|
|
8112
|
+
await this.initializeOnce("writer", recoveredOwners, { skipAutoGc: true });
|
|
8113
|
+
} else {
|
|
8114
|
+
this.refreshLoadedIndexState();
|
|
8115
|
+
}
|
|
8116
|
+
if (this.config.indexing.autoGc) {
|
|
8117
|
+
await this.maybeRunAutoGc();
|
|
8118
|
+
}
|
|
8119
|
+
return this.requireLoadedIndexState();
|
|
8120
|
+
}
|
|
8121
|
+
requireReadableComponents(readIssues, ...components) {
|
|
8122
|
+
const componentSet = new Set(components);
|
|
8123
|
+
const issues = readIssues.filter((issue) => issue.blocking && componentSet.has(issue.component));
|
|
8124
|
+
if (issues.length > 0) {
|
|
8125
|
+
throw new Error(issues.map((issue) => issue.message).join(" "));
|
|
8126
|
+
}
|
|
8127
|
+
}
|
|
8128
|
+
requireLoadedIndexState() {
|
|
7115
8129
|
if (!this.store || !this.provider || !this.invertedIndex || !this.configuredProviderInfo || !this.database) {
|
|
7116
|
-
|
|
8130
|
+
throw new Error("Index state is not initialized");
|
|
7117
8131
|
}
|
|
7118
8132
|
return {
|
|
7119
8133
|
store: this.store,
|
|
@@ -7137,7 +8151,12 @@ var Indexer = class {
|
|
|
7137
8151
|
return createCostEstimate(files, configuredProviderInfo);
|
|
7138
8152
|
}
|
|
7139
8153
|
async index(onProgress) {
|
|
7140
|
-
|
|
8154
|
+
return this.withIndexMutationLease("index", async (recoveredOwners) => {
|
|
8155
|
+
return this.indexUnlocked(onProgress, recoveredOwners);
|
|
8156
|
+
});
|
|
8157
|
+
}
|
|
8158
|
+
async indexUnlocked(onProgress, recoveredOwners = [], stateReady = false) {
|
|
8159
|
+
const { store, provider, invertedIndex, database, configuredProviderInfo } = stateReady ? this.requireLoadedIndexState() : await this.ensureInitializedUnlocked(recoveredOwners);
|
|
7141
8160
|
const scopedRoots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
7142
8161
|
const branchCatalogKey = this.getBranchCatalogKey();
|
|
7143
8162
|
const forceScopedReembed = scopedRoots !== null && database.getMetadata(this.getProjectForceReembedMetadataKey()) === "true";
|
|
@@ -7147,7 +8166,6 @@ var Indexer = class {
|
|
|
7147
8166
|
`${this.indexCompatibility?.reason} Run index_codebase with force=true to rebuild the index.`
|
|
7148
8167
|
);
|
|
7149
8168
|
}
|
|
7150
|
-
this.acquireIndexingLock();
|
|
7151
8169
|
this.logger.recordIndexingStart();
|
|
7152
8170
|
this.logger.info("Starting indexing", { projectRoot: this.projectRoot });
|
|
7153
8171
|
const startTime = Date.now();
|
|
@@ -7198,7 +8216,7 @@ var Indexer = class {
|
|
|
7198
8216
|
unchangedFilePaths.add(f.path);
|
|
7199
8217
|
this.logger.recordCacheHit();
|
|
7200
8218
|
} else {
|
|
7201
|
-
const content = await
|
|
8219
|
+
const content = await import_fs8.promises.readFile(f.path, "utf-8");
|
|
7202
8220
|
changedFiles.push({ path: f.path, content, hash: currentHash });
|
|
7203
8221
|
this.logger.recordCacheMiss();
|
|
7204
8222
|
}
|
|
@@ -7222,6 +8240,7 @@ var Indexer = class {
|
|
|
7222
8240
|
this.logger.debug("Parsed changed files", { parsedCount: parsedFiles.length, parseMs: parseMs.toFixed(2) });
|
|
7223
8241
|
const existingChunks = /* @__PURE__ */ new Map();
|
|
7224
8242
|
const existingChunksByFile = /* @__PURE__ */ new Map();
|
|
8243
|
+
const existingMetadataById = /* @__PURE__ */ new Map();
|
|
7225
8244
|
for (const { key, metadata } of store.getAllMetadata()) {
|
|
7226
8245
|
if (scopedRoots && !this.isFileInCurrentScope(metadata.filePath, scopedRoots)) {
|
|
7227
8246
|
continue;
|
|
@@ -7230,6 +8249,7 @@ var Indexer = class {
|
|
|
7230
8249
|
continue;
|
|
7231
8250
|
}
|
|
7232
8251
|
existingChunks.set(key, metadata.hash);
|
|
8252
|
+
existingMetadataById.set(key, metadata);
|
|
7233
8253
|
const fileChunks = existingChunksByFile.get(metadata.filePath) || /* @__PURE__ */ new Set();
|
|
7234
8254
|
fileChunks.add(key);
|
|
7235
8255
|
existingChunksByFile.set(metadata.filePath, fileChunks);
|
|
@@ -7237,6 +8257,8 @@ var Indexer = class {
|
|
|
7237
8257
|
const currentChunkIds = /* @__PURE__ */ new Set();
|
|
7238
8258
|
const currentFilePaths = /* @__PURE__ */ new Set();
|
|
7239
8259
|
const pendingChunks = [];
|
|
8260
|
+
const gitBlameEnabled = this.config.indexing.gitBlame.enabled && isGitRepo(this.projectRoot);
|
|
8261
|
+
let backfilledBlameMetadata = false;
|
|
7240
8262
|
for (const filePath of unchangedFilePaths) {
|
|
7241
8263
|
currentFilePaths.add(filePath);
|
|
7242
8264
|
const fileChunks = existingChunksByFile.get(filePath);
|
|
@@ -7247,10 +8269,52 @@ var Indexer = class {
|
|
|
7247
8269
|
}
|
|
7248
8270
|
}
|
|
7249
8271
|
const chunkDataBatch = [];
|
|
8272
|
+
if (gitBlameEnabled) {
|
|
8273
|
+
const backfillItems = [];
|
|
8274
|
+
for (const chunkId of currentChunkIds) {
|
|
8275
|
+
const metadata = existingMetadataById.get(chunkId);
|
|
8276
|
+
if (!metadata || hasBlameMetadata(metadata)) {
|
|
8277
|
+
continue;
|
|
8278
|
+
}
|
|
8279
|
+
const chunk = database.getChunk(chunkId);
|
|
8280
|
+
if (!chunk) {
|
|
8281
|
+
continue;
|
|
8282
|
+
}
|
|
8283
|
+
const blame = await getChunkGitBlame(this.projectRoot, chunk.filePath, chunk.startLine, chunk.endLine);
|
|
8284
|
+
const blameMetadata = metadataFromBlame(blame);
|
|
8285
|
+
if (!blameMetadata.blameSha) {
|
|
8286
|
+
continue;
|
|
8287
|
+
}
|
|
8288
|
+
chunkDataBatch.push({
|
|
8289
|
+
...chunk,
|
|
8290
|
+
blameSha: blameMetadata.blameSha,
|
|
8291
|
+
blameAuthor: blameMetadata.blameAuthor,
|
|
8292
|
+
blameAuthorEmail: blameMetadata.blameAuthorEmail,
|
|
8293
|
+
blameCommittedAt: blameMetadata.blameCommittedAt,
|
|
8294
|
+
blameSummary: blameMetadata.blameSummary
|
|
8295
|
+
});
|
|
8296
|
+
const embeddingBuffer = database.getEmbedding(chunk.contentHash);
|
|
8297
|
+
if (!embeddingBuffer) {
|
|
8298
|
+
continue;
|
|
8299
|
+
}
|
|
8300
|
+
backfillItems.push({
|
|
8301
|
+
id: chunkId,
|
|
8302
|
+
vector: Array.from(bufferToFloat32Array(embeddingBuffer)),
|
|
8303
|
+
metadata: {
|
|
8304
|
+
...metadata,
|
|
8305
|
+
...blameMetadata
|
|
8306
|
+
}
|
|
8307
|
+
});
|
|
8308
|
+
}
|
|
8309
|
+
if (backfillItems.length > 0) {
|
|
8310
|
+
store.addBatch(backfillItems);
|
|
8311
|
+
backfilledBlameMetadata = true;
|
|
8312
|
+
}
|
|
8313
|
+
}
|
|
7250
8314
|
for (const parsed of parsedFiles) {
|
|
7251
8315
|
currentFilePaths.add(parsed.path);
|
|
7252
8316
|
if (parsed.chunks.length === 0) {
|
|
7253
|
-
const relativePath =
|
|
8317
|
+
const relativePath = path13.relative(this.projectRoot, parsed.path);
|
|
7254
8318
|
stats.parseFailures.push(relativePath);
|
|
7255
8319
|
}
|
|
7256
8320
|
let fileChunkCount = 0;
|
|
@@ -7271,6 +8335,10 @@ var Indexer = class {
|
|
|
7271
8335
|
}
|
|
7272
8336
|
const id = generateChunkId(parsed.path, chunk);
|
|
7273
8337
|
const contentHash = generateChunkHash(chunk);
|
|
8338
|
+
const existingContentHash = existingChunks.get(id);
|
|
8339
|
+
const existingChunk = gitBlameEnabled ? database.getChunk(id) : null;
|
|
8340
|
+
const blame = gitBlameEnabled && existingContentHash !== contentHash ? await getChunkGitBlame(this.projectRoot, parsed.path, chunk.startLine, chunk.endLine) : blameFromChunkData(existingChunk);
|
|
8341
|
+
const blameMetadata = metadataFromBlame(blame);
|
|
7274
8342
|
currentChunkIds.add(id);
|
|
7275
8343
|
chunkDataBatch.push({
|
|
7276
8344
|
chunkId: id,
|
|
@@ -7280,9 +8348,14 @@ var Indexer = class {
|
|
|
7280
8348
|
endLine: chunk.endLine,
|
|
7281
8349
|
nodeType: chunk.chunkType,
|
|
7282
8350
|
name: chunk.name,
|
|
7283
|
-
language: chunk.language
|
|
8351
|
+
language: chunk.language,
|
|
8352
|
+
blameSha: blameMetadata.blameSha,
|
|
8353
|
+
blameAuthor: blameMetadata.blameAuthor,
|
|
8354
|
+
blameAuthorEmail: blameMetadata.blameAuthorEmail,
|
|
8355
|
+
blameCommittedAt: blameMetadata.blameCommittedAt,
|
|
8356
|
+
blameSummary: blameMetadata.blameSummary
|
|
7284
8357
|
});
|
|
7285
|
-
if (
|
|
8358
|
+
if (existingContentHash === contentHash) {
|
|
7286
8359
|
fileChunkCount++;
|
|
7287
8360
|
continue;
|
|
7288
8361
|
}
|
|
@@ -7297,7 +8370,8 @@ var Indexer = class {
|
|
|
7297
8370
|
chunkType: chunk.chunkType,
|
|
7298
8371
|
name: chunk.name,
|
|
7299
8372
|
language: chunk.language,
|
|
7300
|
-
hash: contentHash
|
|
8373
|
+
hash: contentHash,
|
|
8374
|
+
...blameMetadata
|
|
7301
8375
|
};
|
|
7302
8376
|
pendingChunks.push({
|
|
7303
8377
|
id,
|
|
@@ -7440,6 +8514,11 @@ var Indexer = class {
|
|
|
7440
8514
|
database.addChunksToBranchBatch(branchCatalogKey, Array.from(currentChunkIds));
|
|
7441
8515
|
database.clearBranchSymbols(branchCatalogKey);
|
|
7442
8516
|
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
8517
|
+
const vectorPath = path13.join(this.indexPath, "vectors");
|
|
8518
|
+
const shouldFingerprintLegacyPair = !store.hasFingerprint() && (0, import_fs8.existsSync)(vectorPath) && (0, import_fs8.existsSync)(`${vectorPath}.meta.json`);
|
|
8519
|
+
if (backfilledBlameMetadata || shouldFingerprintLegacyPair) {
|
|
8520
|
+
store.save();
|
|
8521
|
+
}
|
|
7443
8522
|
if (scopedRoots) {
|
|
7444
8523
|
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
7445
8524
|
this.clearScopedFailedBatches(scopedRoots);
|
|
@@ -7458,7 +8537,6 @@ var Indexer = class {
|
|
|
7458
8537
|
chunksProcessed: 0,
|
|
7459
8538
|
totalChunks: 0
|
|
7460
8539
|
});
|
|
7461
|
-
this.releaseIndexingLock();
|
|
7462
8540
|
return stats;
|
|
7463
8541
|
}
|
|
7464
8542
|
if (pendingChunks.length === 0) {
|
|
@@ -7467,7 +8545,7 @@ var Indexer = class {
|
|
|
7467
8545
|
database.clearBranchSymbols(branchCatalogKey);
|
|
7468
8546
|
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
7469
8547
|
store.save();
|
|
7470
|
-
|
|
8548
|
+
this.saveInvertedIndex(invertedIndex);
|
|
7471
8549
|
if (scopedRoots) {
|
|
7472
8550
|
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
7473
8551
|
this.clearScopedFailedBatches(scopedRoots);
|
|
@@ -7486,7 +8564,6 @@ var Indexer = class {
|
|
|
7486
8564
|
chunksProcessed: 0,
|
|
7487
8565
|
totalChunks: 0
|
|
7488
8566
|
});
|
|
7489
|
-
this.releaseIndexingLock();
|
|
7490
8567
|
return stats;
|
|
7491
8568
|
}
|
|
7492
8569
|
onProgress?.({
|
|
@@ -7717,7 +8794,7 @@ var Indexer = class {
|
|
|
7717
8794
|
database.clearBranchSymbols(branchCatalogKey);
|
|
7718
8795
|
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
7719
8796
|
store.save();
|
|
7720
|
-
|
|
8797
|
+
this.saveInvertedIndex(invertedIndex);
|
|
7721
8798
|
if (scopedRoots) {
|
|
7722
8799
|
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
7723
8800
|
} else {
|
|
@@ -7769,7 +8846,6 @@ var Indexer = class {
|
|
|
7769
8846
|
chunksProcessed: stats.indexedChunks,
|
|
7770
8847
|
totalChunks: pendingChunks.length
|
|
7771
8848
|
});
|
|
7772
|
-
this.releaseIndexingLock();
|
|
7773
8849
|
return stats;
|
|
7774
8850
|
}
|
|
7775
8851
|
async getQueryEmbedding(query, provider) {
|
|
@@ -7835,8 +8911,8 @@ var Indexer = class {
|
|
|
7835
8911
|
return intersection / union;
|
|
7836
8912
|
}
|
|
7837
8913
|
async search(query, limit, options) {
|
|
7838
|
-
const { store, provider, database } = await this.ensureInitialized();
|
|
7839
|
-
|
|
8914
|
+
const { store, provider, invertedIndex, database, readIssues, compatibility } = await this.ensureInitialized();
|
|
8915
|
+
this.requireReadableComponents(readIssues, "vectors", "database");
|
|
7840
8916
|
if (!compatibility.compatible) {
|
|
7841
8917
|
throw new Error(
|
|
7842
8918
|
`${compatibility.reason ?? "Index is incompatible with current embedding provider."} A possible solution is to run index_codebase with force=true to rebuild the index.`
|
|
@@ -7850,6 +8926,7 @@ var Indexer = class {
|
|
|
7850
8926
|
const maxResults = limit ?? this.config.search.maxResults;
|
|
7851
8927
|
const hybridWeight = options?.hybridWeight ?? this.config.search.hybridWeight;
|
|
7852
8928
|
const fusionStrategy = this.config.search.fusionStrategy;
|
|
8929
|
+
const effectiveHybridWeight = fusionStrategy === "weighted" && readIssues.some((issue) => issue.component === "keyword") ? 0 : hybridWeight;
|
|
7853
8930
|
const rrfK = this.config.search.rrfK;
|
|
7854
8931
|
const rerankTopN = this.config.search.rerankTopN;
|
|
7855
8932
|
const filterByBranch = options?.filterByBranch ?? true;
|
|
@@ -7858,7 +8935,7 @@ var Indexer = class {
|
|
|
7858
8935
|
this.logger.search("debug", "Starting search", {
|
|
7859
8936
|
query,
|
|
7860
8937
|
maxResults,
|
|
7861
|
-
hybridWeight,
|
|
8938
|
+
hybridWeight: effectiveHybridWeight,
|
|
7862
8939
|
fusionStrategy,
|
|
7863
8940
|
rrfK,
|
|
7864
8941
|
rerankTopN,
|
|
@@ -7872,7 +8949,7 @@ var Indexer = class {
|
|
|
7872
8949
|
const semanticResults = store.search(embedding, maxResults * 4);
|
|
7873
8950
|
const vectorMs = import_perf_hooks.performance.now() - vectorStartTime;
|
|
7874
8951
|
const keywordStartTime = import_perf_hooks.performance.now();
|
|
7875
|
-
const keywordResults = await this.keywordSearch(query, maxResults * 4);
|
|
8952
|
+
const keywordResults = await this.keywordSearch(query, maxResults * 4, store, invertedIndex);
|
|
7876
8953
|
const keywordMs = import_perf_hooks.performance.now() - keywordStartTime;
|
|
7877
8954
|
let branchChunkIds = null;
|
|
7878
8955
|
if (filterByBranch && (this.config.scope === "global" || this.currentBranch !== "default")) {
|
|
@@ -7909,7 +8986,7 @@ var Indexer = class {
|
|
|
7909
8986
|
rrfK,
|
|
7910
8987
|
rerankTopN,
|
|
7911
8988
|
limit: maxResults,
|
|
7912
|
-
hybridWeight,
|
|
8989
|
+
hybridWeight: effectiveHybridWeight,
|
|
7913
8990
|
prioritizeSourcePaths: sourceIntent
|
|
7914
8991
|
});
|
|
7915
8992
|
const rerankedCombined = await this.rerankCandidatesWithApi(query, combined, {
|
|
@@ -7983,7 +9060,7 @@ var Indexer = class {
|
|
|
7983
9060
|
let contextEndLine = r.metadata.endLine;
|
|
7984
9061
|
if (!metadataOnly && this.config.search.includeContext) {
|
|
7985
9062
|
try {
|
|
7986
|
-
const fileContent = await
|
|
9063
|
+
const fileContent = await import_fs8.promises.readFile(
|
|
7987
9064
|
r.metadata.filePath,
|
|
7988
9065
|
"utf-8"
|
|
7989
9066
|
);
|
|
@@ -8003,13 +9080,13 @@ var Indexer = class {
|
|
|
8003
9080
|
content,
|
|
8004
9081
|
score: r.score,
|
|
8005
9082
|
chunkType: r.metadata.chunkType,
|
|
8006
|
-
name: r.metadata.name
|
|
9083
|
+
name: r.metadata.name,
|
|
9084
|
+
blame: blameFromMetadata(r.metadata)
|
|
8007
9085
|
};
|
|
8008
9086
|
})
|
|
8009
9087
|
);
|
|
8010
9088
|
}
|
|
8011
|
-
async keywordSearch(query, limit) {
|
|
8012
|
-
const { store, invertedIndex } = await this.ensureInitialized();
|
|
9089
|
+
async keywordSearch(query, limit, store, invertedIndex) {
|
|
8013
9090
|
const scores = invertedIndex.search(query);
|
|
8014
9091
|
if (scores.size === 0) {
|
|
8015
9092
|
return [];
|
|
@@ -8027,24 +9104,54 @@ var Indexer = class {
|
|
|
8027
9104
|
return results.slice(0, limit);
|
|
8028
9105
|
}
|
|
8029
9106
|
async getStatus() {
|
|
8030
|
-
const { store, configuredProviderInfo, database } = await this.ensureInitialized();
|
|
9107
|
+
const { store, configuredProviderInfo, database, readIssues, compatibility } = await this.ensureInitialized();
|
|
8031
9108
|
const failedBatchesCount = this.getFailedBatchesCount();
|
|
9109
|
+
const vectorCount = store.count();
|
|
9110
|
+
const statusReadIssues = [...readIssues];
|
|
9111
|
+
let startupWarning = "";
|
|
9112
|
+
if (!statusReadIssues.some((issue) => issue.component === "database")) {
|
|
9113
|
+
try {
|
|
9114
|
+
startupWarning = database.getMetadata(STARTUP_WARNING_METADATA_KEY) ?? "";
|
|
9115
|
+
} catch (error) {
|
|
9116
|
+
const message = this.getDatabaseReadIssueMessage();
|
|
9117
|
+
statusReadIssues.push(this.createReadIssue("database", message));
|
|
9118
|
+
if (!this.readIssues.some((issue) => issue.component === "database")) {
|
|
9119
|
+
this.recordReadIssue("database", message, error);
|
|
9120
|
+
}
|
|
9121
|
+
}
|
|
9122
|
+
}
|
|
9123
|
+
const readWarning = statusReadIssues.map((issue) => issue.message).join(" ");
|
|
9124
|
+
const warning = [readWarning, startupWarning].filter((message) => message.length > 0).join(" ");
|
|
9125
|
+
const hasBlockingReadIssue = statusReadIssues.some((issue) => issue.blocking);
|
|
8032
9126
|
return {
|
|
8033
|
-
indexed:
|
|
8034
|
-
vectorCount
|
|
9127
|
+
indexed: vectorCount > 0 && !hasBlockingReadIssue,
|
|
9128
|
+
vectorCount,
|
|
8035
9129
|
provider: configuredProviderInfo.provider,
|
|
8036
9130
|
model: configuredProviderInfo.modelInfo.model,
|
|
8037
9131
|
indexPath: this.indexPath,
|
|
8038
9132
|
currentBranch: this.currentBranch,
|
|
8039
9133
|
baseBranch: this.baseBranch,
|
|
8040
|
-
compatibility
|
|
9134
|
+
compatibility,
|
|
8041
9135
|
failedBatchesCount,
|
|
8042
9136
|
failedBatchesPath: failedBatchesCount > 0 ? this.failedBatchesPath : void 0,
|
|
8043
|
-
warning:
|
|
9137
|
+
warning: warning || void 0
|
|
8044
9138
|
};
|
|
8045
9139
|
}
|
|
9140
|
+
async forceIndex(onProgress) {
|
|
9141
|
+
return this.withIndexMutationLease("force-index", async (recoveredOwners) => {
|
|
9142
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9143
|
+
await this.clearIndexUnlocked();
|
|
9144
|
+
return this.indexUnlocked(onProgress, [], true);
|
|
9145
|
+
});
|
|
9146
|
+
}
|
|
8046
9147
|
async clearIndex() {
|
|
8047
|
-
|
|
9148
|
+
await this.withIndexMutationLease("clear", async (recoveredOwners) => {
|
|
9149
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9150
|
+
await this.clearIndexUnlocked();
|
|
9151
|
+
});
|
|
9152
|
+
}
|
|
9153
|
+
async clearIndexUnlocked() {
|
|
9154
|
+
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
8048
9155
|
if (this.config.scope === "global") {
|
|
8049
9156
|
store.load();
|
|
8050
9157
|
invertedIndex.load();
|
|
@@ -8071,7 +9178,7 @@ var Indexer = class {
|
|
|
8071
9178
|
store.clear();
|
|
8072
9179
|
store.save();
|
|
8073
9180
|
invertedIndex.clear();
|
|
8074
|
-
|
|
9181
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8075
9182
|
this.fileHashCache.clear();
|
|
8076
9183
|
this.saveFileHashCache();
|
|
8077
9184
|
database.clearAllIndexedData();
|
|
@@ -8095,14 +9202,7 @@ var Indexer = class {
|
|
|
8095
9202
|
this.indexCompatibility = compatibility;
|
|
8096
9203
|
return;
|
|
8097
9204
|
}
|
|
8098
|
-
|
|
8099
|
-
if (this.host !== "opencode") {
|
|
8100
|
-
localProjectIndexPaths.push(path11.join(this.projectRoot, getHostProjectIndexRelativePath("opencode")));
|
|
8101
|
-
}
|
|
8102
|
-
const isLocalProjectIndex = localProjectIndexPaths.some(
|
|
8103
|
-
(localPath) => path11.resolve(this.indexPath) === path11.resolve(localPath)
|
|
8104
|
-
);
|
|
8105
|
-
if (!isLocalProjectIndex) {
|
|
9205
|
+
if (!this.isLocalProjectIndexPath()) {
|
|
8106
9206
|
throw new Error(
|
|
8107
9207
|
"Project-scoped force rebuild is unsafe while using an inherited worktree index. Create a local project config boundary before clearing the index."
|
|
8108
9208
|
);
|
|
@@ -8110,7 +9210,7 @@ var Indexer = class {
|
|
|
8110
9210
|
store.clear();
|
|
8111
9211
|
store.save();
|
|
8112
9212
|
invertedIndex.clear();
|
|
8113
|
-
|
|
9213
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8114
9214
|
this.fileHashCache.clear();
|
|
8115
9215
|
this.saveFileHashCache();
|
|
8116
9216
|
database.clearAllIndexedData();
|
|
@@ -8128,7 +9228,13 @@ var Indexer = class {
|
|
|
8128
9228
|
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
8129
9229
|
}
|
|
8130
9230
|
async healthCheck() {
|
|
8131
|
-
|
|
9231
|
+
return this.withIndexMutationLease("health-check", async (recoveredOwners) => {
|
|
9232
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9233
|
+
return this.healthCheckUnlocked();
|
|
9234
|
+
});
|
|
9235
|
+
}
|
|
9236
|
+
async healthCheckUnlocked() {
|
|
9237
|
+
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
8132
9238
|
this.logger.gc("info", "Starting health check");
|
|
8133
9239
|
const allMetadata = store.getAllMetadata();
|
|
8134
9240
|
const filePathsToChunkKeys = /* @__PURE__ */ new Map();
|
|
@@ -8141,7 +9247,7 @@ var Indexer = class {
|
|
|
8141
9247
|
const removedChunkKeys = [];
|
|
8142
9248
|
const chunkKeysByRemovedFile = /* @__PURE__ */ new Map();
|
|
8143
9249
|
for (const [filePath, chunkKeys] of filePathsToChunkKeys) {
|
|
8144
|
-
if (!(0,
|
|
9250
|
+
if (!(0, import_fs8.existsSync)(filePath)) {
|
|
8145
9251
|
chunkKeysByRemovedFile.set(filePath, chunkKeys);
|
|
8146
9252
|
for (const key of chunkKeys) {
|
|
8147
9253
|
removedChunkKeys.push(key);
|
|
@@ -8166,7 +9272,7 @@ var Indexer = class {
|
|
|
8166
9272
|
const removedCount = removedChunkKeys.length;
|
|
8167
9273
|
if (removedCount > 0) {
|
|
8168
9274
|
store.save();
|
|
8169
|
-
|
|
9275
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8170
9276
|
}
|
|
8171
9277
|
let gcOrphanEmbeddings;
|
|
8172
9278
|
let gcOrphanChunks;
|
|
@@ -8181,7 +9287,7 @@ var Indexer = class {
|
|
|
8181
9287
|
if (!await this.tryResetCorruptedIndex("running index health check", error)) {
|
|
8182
9288
|
throw error;
|
|
8183
9289
|
}
|
|
8184
|
-
await this.
|
|
9290
|
+
await this.initializeUnlocked("writer", [], { skipAutoGc: true });
|
|
8185
9291
|
return {
|
|
8186
9292
|
removed: 0,
|
|
8187
9293
|
filePaths: [],
|
|
@@ -8190,7 +9296,7 @@ var Indexer = class {
|
|
|
8190
9296
|
gcOrphanSymbols: 0,
|
|
8191
9297
|
gcOrphanCallEdges: 0,
|
|
8192
9298
|
resetCorruptedIndex: true,
|
|
8193
|
-
warning: this.getCorruptedIndexWarning(
|
|
9299
|
+
warning: this.getCorruptedIndexWarning(path13.join(this.indexPath, "codebase.db"))
|
|
8194
9300
|
};
|
|
8195
9301
|
}
|
|
8196
9302
|
this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
|
|
@@ -8203,7 +9309,13 @@ var Indexer = class {
|
|
|
8203
9309
|
return { removed: removedCount, filePaths: removedFilePaths, gcOrphanEmbeddings, gcOrphanChunks, gcOrphanSymbols, gcOrphanCallEdges };
|
|
8204
9310
|
}
|
|
8205
9311
|
async retryFailedBatches() {
|
|
8206
|
-
|
|
9312
|
+
return this.withIndexMutationLease("retry-failed-batches", async (recoveredOwners) => {
|
|
9313
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9314
|
+
return this.retryFailedBatchesUnlocked();
|
|
9315
|
+
});
|
|
9316
|
+
}
|
|
9317
|
+
async retryFailedBatchesUnlocked() {
|
|
9318
|
+
const { store, provider, invertedIndex, database, configuredProviderInfo } = this.requireLoadedIndexState();
|
|
8207
9319
|
const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
|
|
8208
9320
|
const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
|
|
8209
9321
|
const roots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
@@ -8367,7 +9479,7 @@ var Indexer = class {
|
|
|
8367
9479
|
}
|
|
8368
9480
|
if (succeeded > 0) {
|
|
8369
9481
|
store.save();
|
|
8370
|
-
|
|
9482
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8371
9483
|
}
|
|
8372
9484
|
if (roots && succeeded > 0 && persistedStillFailing.length === 0 && this.hasProjectForceReembedPending()) {
|
|
8373
9485
|
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
@@ -8395,15 +9507,16 @@ var Indexer = class {
|
|
|
8395
9507
|
}
|
|
8396
9508
|
}
|
|
8397
9509
|
async getDatabaseStats() {
|
|
8398
|
-
const { database } = await this.ensureInitialized();
|
|
9510
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9511
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8399
9512
|
return database.getStats();
|
|
8400
9513
|
}
|
|
8401
9514
|
getLogger() {
|
|
8402
9515
|
return this.logger;
|
|
8403
9516
|
}
|
|
8404
9517
|
async findSimilar(code, limit = this.config.search.maxResults, options) {
|
|
8405
|
-
const { store, provider, database } = await this.ensureInitialized();
|
|
8406
|
-
|
|
9518
|
+
const { store, provider, database, readIssues, compatibility } = await this.ensureInitialized();
|
|
9519
|
+
this.requireReadableComponents(readIssues, "vectors", "database");
|
|
8407
9520
|
if (!compatibility.compatible) {
|
|
8408
9521
|
throw new Error(
|
|
8409
9522
|
`${compatibility.reason ?? "Index is incompatible with current embedding provider."} Run index_codebase with force=true to rebuild the index.`
|
|
@@ -8493,7 +9606,7 @@ var Indexer = class {
|
|
|
8493
9606
|
let content = "";
|
|
8494
9607
|
if (this.config.search.includeContext) {
|
|
8495
9608
|
try {
|
|
8496
|
-
const fileContent = await
|
|
9609
|
+
const fileContent = await import_fs8.promises.readFile(
|
|
8497
9610
|
r.metadata.filePath,
|
|
8498
9611
|
"utf-8"
|
|
8499
9612
|
);
|
|
@@ -8510,13 +9623,15 @@ var Indexer = class {
|
|
|
8510
9623
|
content,
|
|
8511
9624
|
score: r.score,
|
|
8512
9625
|
chunkType: r.metadata.chunkType,
|
|
8513
|
-
name: r.metadata.name
|
|
9626
|
+
name: r.metadata.name,
|
|
9627
|
+
blame: blameFromMetadata(r.metadata)
|
|
8514
9628
|
};
|
|
8515
9629
|
})
|
|
8516
9630
|
);
|
|
8517
9631
|
}
|
|
8518
9632
|
async getCallers(targetName, callTypeFilter) {
|
|
8519
|
-
const { database } = await this.ensureInitialized();
|
|
9633
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9634
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8520
9635
|
const seen = /* @__PURE__ */ new Set();
|
|
8521
9636
|
const results = [];
|
|
8522
9637
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
@@ -8530,7 +9645,8 @@ var Indexer = class {
|
|
|
8530
9645
|
return results;
|
|
8531
9646
|
}
|
|
8532
9647
|
async getCallees(symbolId, callTypeFilter) {
|
|
8533
|
-
const { database } = await this.ensureInitialized();
|
|
9648
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9649
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8534
9650
|
const seen = /* @__PURE__ */ new Set();
|
|
8535
9651
|
const results = [];
|
|
8536
9652
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
@@ -8544,44 +9660,51 @@ var Indexer = class {
|
|
|
8544
9660
|
return results;
|
|
8545
9661
|
}
|
|
8546
9662
|
async findCallPath(fromName, toName, maxDepth) {
|
|
8547
|
-
const { database } = await this.ensureInitialized();
|
|
9663
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9664
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8548
9665
|
let shortest = [];
|
|
8549
9666
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
8550
|
-
const
|
|
8551
|
-
if (
|
|
8552
|
-
shortest =
|
|
9667
|
+
const path24 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
|
|
9668
|
+
if (path24.length > 0 && (shortest.length === 0 || path24.length < shortest.length)) {
|
|
9669
|
+
shortest = path24;
|
|
8553
9670
|
}
|
|
8554
9671
|
}
|
|
8555
9672
|
return shortest;
|
|
8556
9673
|
}
|
|
8557
9674
|
async getSymbolsForBranch(branch) {
|
|
8558
|
-
const { database } = await this.ensureInitialized();
|
|
9675
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9676
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8559
9677
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8560
9678
|
return database.getSymbolsForBranch(resolvedBranch);
|
|
8561
9679
|
}
|
|
8562
9680
|
async getSymbolsForFiles(filePaths, branch) {
|
|
8563
|
-
const { database } = await this.ensureInitialized();
|
|
9681
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9682
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8564
9683
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8565
9684
|
return database.getSymbolsForFiles(filePaths, resolvedBranch);
|
|
8566
9685
|
}
|
|
8567
9686
|
async getTransitiveReachability(rootSymbolIds, direction, maxDepth) {
|
|
8568
|
-
const { database } = await this.ensureInitialized();
|
|
9687
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9688
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8569
9689
|
const branch = this.getBranchCatalogKey();
|
|
8570
9690
|
return database.getTransitiveReachability(rootSymbolIds, branch, direction, maxDepth);
|
|
8571
9691
|
}
|
|
8572
9692
|
async detectCommunities(branch, symbolIds) {
|
|
8573
|
-
const { database } = await this.ensureInitialized();
|
|
9693
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9694
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8574
9695
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8575
9696
|
return database.detectCommunities(resolvedBranch, symbolIds);
|
|
8576
9697
|
}
|
|
8577
9698
|
async computeCentrality(branch) {
|
|
8578
|
-
const { database } = await this.ensureInitialized();
|
|
9699
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9700
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8579
9701
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8580
9702
|
return database.computeCentrality(resolvedBranch);
|
|
8581
9703
|
}
|
|
8582
9704
|
async getPrImpact(opts) {
|
|
8583
|
-
const { database } = await this.ensureInitialized();
|
|
8584
|
-
|
|
9705
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9706
|
+
this.requireReadableComponents(readIssues, "database");
|
|
9707
|
+
const execFileAsync3 = (0, import_util3.promisify)(import_child_process3.execFile);
|
|
8585
9708
|
const changedFilesResult = await getChangedFiles({
|
|
8586
9709
|
pr: opts.pr,
|
|
8587
9710
|
branch: opts.branch,
|
|
@@ -8603,7 +9726,7 @@ var Indexer = class {
|
|
|
8603
9726
|
"Run index_codebase first to build the call graph and symbol index for this branch."
|
|
8604
9727
|
);
|
|
8605
9728
|
}
|
|
8606
|
-
const absoluteChangedFiles = changedFiles.map((f) =>
|
|
9729
|
+
const absoluteChangedFiles = changedFiles.map((f) => path13.resolve(this.projectRoot, f));
|
|
8607
9730
|
const directSymbols = database.getSymbolsForFiles(absoluteChangedFiles, branchKey);
|
|
8608
9731
|
const directIds = directSymbols.map((s) => s.id);
|
|
8609
9732
|
const direction = opts.direction ?? "both";
|
|
@@ -8665,7 +9788,7 @@ var Indexer = class {
|
|
|
8665
9788
|
if (opts.checkConflicts) {
|
|
8666
9789
|
conflictingPRs = [];
|
|
8667
9790
|
try {
|
|
8668
|
-
const { stdout } = await
|
|
9791
|
+
const { stdout } = await execFileAsync3(
|
|
8669
9792
|
"gh",
|
|
8670
9793
|
["pr", "list", "--state", "open", "--json", "number,headRefName", "--limit", "10000"],
|
|
8671
9794
|
{ cwd: this.projectRoot, timeout: 3e4 }
|
|
@@ -8686,7 +9809,7 @@ var Indexer = class {
|
|
|
8686
9809
|
projectRoot: this.projectRoot,
|
|
8687
9810
|
baseBranch: this.baseBranch
|
|
8688
9811
|
});
|
|
8689
|
-
const otherAbsolute = otherChanged.files.map((f) =>
|
|
9812
|
+
const otherAbsolute = otherChanged.files.map((f) => path13.resolve(this.projectRoot, f));
|
|
8690
9813
|
const otherBranchKey = this.getBranchCatalogKeyFor(openPr.headRefName);
|
|
8691
9814
|
const otherSymbols = database.getSymbolsForFiles(otherAbsolute, otherBranchKey);
|
|
8692
9815
|
const otherLabels = /* @__PURE__ */ new Set();
|
|
@@ -8736,7 +9859,8 @@ var Indexer = class {
|
|
|
8736
9859
|
};
|
|
8737
9860
|
}
|
|
8738
9861
|
async getVisualizationData(options) {
|
|
8739
|
-
const { database, store } = await this.ensureInitialized();
|
|
9862
|
+
const { database, store, readIssues } = await this.ensureInitialized();
|
|
9863
|
+
this.requireReadableComponents(readIssues, "vectors", "database");
|
|
8740
9864
|
const seenSymbols = /* @__PURE__ */ new Map();
|
|
8741
9865
|
const seenEdges = /* @__PURE__ */ new Map();
|
|
8742
9866
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
@@ -8749,12 +9873,12 @@ var Indexer = class {
|
|
|
8749
9873
|
if (meta.filePath) filePaths.add(meta.filePath);
|
|
8750
9874
|
}
|
|
8751
9875
|
const directory = options?.directory?.replace(/\/$/, "");
|
|
8752
|
-
const absoluteDirectoryFilter = directory ?
|
|
9876
|
+
const absoluteDirectoryFilter = directory ? path13.resolve(this.projectRoot, directory) : void 0;
|
|
8753
9877
|
for (const filePath of filePaths) {
|
|
8754
9878
|
if (directory) {
|
|
8755
|
-
const absoluteFilePath =
|
|
9879
|
+
const absoluteFilePath = path13.resolve(filePath);
|
|
8756
9880
|
const matchesRelative = filePath === directory || filePath.startsWith(directory + "/");
|
|
8757
|
-
const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter +
|
|
9881
|
+
const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter + path13.sep));
|
|
8758
9882
|
if (!matchesRelative && !matchesProjectRelative) {
|
|
8759
9883
|
continue;
|
|
8760
9884
|
}
|
|
@@ -8776,53 +9900,64 @@ var Indexer = class {
|
|
|
8776
9900
|
return { symbols: [...seenSymbols.values()], edges: [...seenEdges.values()] };
|
|
8777
9901
|
}
|
|
8778
9902
|
async close() {
|
|
8779
|
-
|
|
9903
|
+
this.database?.close();
|
|
9904
|
+
for (const database of this.retiredDatabases) {
|
|
9905
|
+
database.close();
|
|
9906
|
+
}
|
|
9907
|
+
this.retiredDatabases = [];
|
|
8780
9908
|
this.database = null;
|
|
8781
9909
|
this.store = null;
|
|
8782
9910
|
this.invertedIndex = null;
|
|
8783
9911
|
this.provider = null;
|
|
8784
9912
|
this.reranker = null;
|
|
9913
|
+
this.configuredProviderInfo = null;
|
|
9914
|
+
this.indexCompatibility = null;
|
|
9915
|
+
this.initializationMode = "none";
|
|
9916
|
+
this.readIssues = [];
|
|
9917
|
+
this.readerArtifactFingerprint = null;
|
|
9918
|
+
this.writerArtifactFingerprint = null;
|
|
9919
|
+
this.readerArtifactRetryAfter.clear();
|
|
8785
9920
|
}
|
|
8786
9921
|
};
|
|
8787
9922
|
|
|
8788
9923
|
// src/tools/knowledge-base-paths.ts
|
|
8789
|
-
var
|
|
9924
|
+
var path14 = __toESM(require("path"), 1);
|
|
8790
9925
|
function resolveConfigPathValue(value, baseDir) {
|
|
8791
9926
|
const trimmed = value.trim();
|
|
8792
9927
|
if (!trimmed) {
|
|
8793
9928
|
return trimmed;
|
|
8794
9929
|
}
|
|
8795
|
-
const absolutePath =
|
|
8796
|
-
return
|
|
9930
|
+
const absolutePath = path14.isAbsolute(trimmed) ? trimmed : path14.resolve(baseDir, trimmed);
|
|
9931
|
+
return path14.normalize(absolutePath);
|
|
8797
9932
|
}
|
|
8798
9933
|
function serializeConfigPathValue(value, baseDir) {
|
|
8799
9934
|
const trimmed = value.trim();
|
|
8800
9935
|
if (!trimmed) {
|
|
8801
9936
|
return trimmed;
|
|
8802
9937
|
}
|
|
8803
|
-
if (!
|
|
8804
|
-
return normalizePathSeparators(
|
|
9938
|
+
if (!path14.isAbsolute(trimmed)) {
|
|
9939
|
+
return normalizePathSeparators(path14.normalize(trimmed));
|
|
8805
9940
|
}
|
|
8806
|
-
const relativePath =
|
|
8807
|
-
if (!relativePath || !relativePath.startsWith("..") && !
|
|
8808
|
-
return normalizePathSeparators(
|
|
9941
|
+
const relativePath = path14.relative(baseDir, trimmed);
|
|
9942
|
+
if (!relativePath || !relativePath.startsWith("..") && !path14.isAbsolute(relativePath)) {
|
|
9943
|
+
return normalizePathSeparators(path14.normalize(relativePath || "."));
|
|
8809
9944
|
}
|
|
8810
|
-
return
|
|
9945
|
+
return path14.normalize(trimmed);
|
|
8811
9946
|
}
|
|
8812
9947
|
function resolveKnowledgeBasePath(value, projectRoot) {
|
|
8813
|
-
return
|
|
9948
|
+
return path14.isAbsolute(value) ? value : path14.resolve(projectRoot, value);
|
|
8814
9949
|
}
|
|
8815
9950
|
function normalizeKnowledgeBasePath2(value, projectRoot) {
|
|
8816
|
-
return
|
|
9951
|
+
return path14.normalize(resolveKnowledgeBasePath(value, projectRoot));
|
|
8817
9952
|
}
|
|
8818
9953
|
function hasMatchingKnowledgeBasePath(knowledgeBases, inputPath, projectRoot) {
|
|
8819
|
-
const normalizedInput =
|
|
9954
|
+
const normalizedInput = path14.normalize(inputPath);
|
|
8820
9955
|
return knowledgeBases.some((kb) => normalizeKnowledgeBasePath2(kb, projectRoot) === normalizedInput);
|
|
8821
9956
|
}
|
|
8822
9957
|
function findKnowledgeBasePathIndex(knowledgeBases, inputPath, projectRoot) {
|
|
8823
|
-
const normalizedInput =
|
|
9958
|
+
const normalizedInput = path14.normalize(inputPath);
|
|
8824
9959
|
return knowledgeBases.findIndex(
|
|
8825
|
-
(kb) =>
|
|
9960
|
+
(kb) => path14.normalize(kb) === normalizedInput || normalizeKnowledgeBasePath2(kb, projectRoot) === normalizedInput
|
|
8826
9961
|
);
|
|
8827
9962
|
}
|
|
8828
9963
|
|
|
@@ -8922,6 +10057,10 @@ function formatStatus(status) {
|
|
|
8922
10057
|
lines.push(`Failed batches: ${status.failedBatchesPath}`);
|
|
8923
10058
|
}
|
|
8924
10059
|
}
|
|
10060
|
+
if (status.warning) {
|
|
10061
|
+
lines.push("");
|
|
10062
|
+
lines.push(`INDEX WARNING: ${status.warning}`);
|
|
10063
|
+
}
|
|
8925
10064
|
if (status.compatibility && !status.compatibility.compatible) {
|
|
8926
10065
|
lines.push("");
|
|
8927
10066
|
lines.push(`COMPATIBILITY WARNING: ${status.compatibility.reason}`);
|
|
@@ -8974,7 +10113,7 @@ function formatCodebasePeek(results) {
|
|
|
8974
10113
|
const formatted = results.map((r, idx) => {
|
|
8975
10114
|
const location = `${r.filePath}:${r.startLine}-${r.endLine}`;
|
|
8976
10115
|
const name = r.name ? `"${r.name}"` : "(anonymous)";
|
|
8977
|
-
return `[${idx + 1}] ${r.chunkType} ${name} at ${location} (score: ${r.score.toFixed(2)})`;
|
|
10116
|
+
return `[${idx + 1}] ${r.chunkType} ${name} at ${location} (score: ${r.score.toFixed(2)})${formatBlame(r)}`;
|
|
8978
10117
|
});
|
|
8979
10118
|
return formatted.join("\n");
|
|
8980
10119
|
}
|
|
@@ -9006,16 +10145,57 @@ function formatHealthCheck(result) {
|
|
|
9006
10145
|
}
|
|
9007
10146
|
return lines.join("\n");
|
|
9008
10147
|
}
|
|
10148
|
+
function formatCallGraphCallers(name, callers, relationshipType) {
|
|
10149
|
+
if (callers.length === 0) {
|
|
10150
|
+
return `No callers found for "${name}"${relationshipType ? ` with type ${relationshipType}` : ""}. It may not be called by any tracked function, or the index needs updating.`;
|
|
10151
|
+
}
|
|
10152
|
+
const formatted = callers.map((edge, index) => {
|
|
10153
|
+
const confidence = edge.confidence !== "Direct" ? ` [${edge.confidence.toLowerCase()}]` : "";
|
|
10154
|
+
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]"}`;
|
|
10155
|
+
});
|
|
10156
|
+
return `"${name}" is called by ${callers.length} function(s):
|
|
10157
|
+
|
|
10158
|
+
${formatted.join("\n")}`;
|
|
10159
|
+
}
|
|
10160
|
+
function formatCallGraphCallees(symbolId, callees, relationshipType) {
|
|
10161
|
+
if (callees.length === 0) {
|
|
10162
|
+
return `No callees found for symbol ${symbolId}${relationshipType ? ` with type ${relationshipType}` : ""}. The function may not call any other tracked functions.`;
|
|
10163
|
+
}
|
|
10164
|
+
return callees.map((edge, index) => {
|
|
10165
|
+
const confidence = edge.confidence !== "Direct" ? ` [${edge.confidence.toLowerCase()}]` : "";
|
|
10166
|
+
return `[${index + 1}] \u2192 ${edge.targetName} (${edge.callType})${confidence} at line ${edge.line}${edge.isResolved ? ` [resolved: ${edge.toSymbolId}]` : " [unresolved]"}`;
|
|
10167
|
+
}).join("\n");
|
|
10168
|
+
}
|
|
10169
|
+
function formatCallGraphPath(from, to, path24) {
|
|
10170
|
+
if (path24.length === 0) {
|
|
10171
|
+
return `No path found between "${from}" and "${to}". They may be in disconnected components, or the call graph index needs updating.`;
|
|
10172
|
+
}
|
|
10173
|
+
const formatted = path24.map((hop, index) => {
|
|
10174
|
+
const prefix = index === 0 ? "[start]" : `--${hop.callType}-->`;
|
|
10175
|
+
const location = hop.filePath ? ` (${hop.filePath}:${hop.line})` : "";
|
|
10176
|
+
return `${prefix} ${hop.symbolName}${location}`;
|
|
10177
|
+
});
|
|
10178
|
+
return `Path (${path24.length} hops):
|
|
10179
|
+
${formatted.join("\n")}`;
|
|
10180
|
+
}
|
|
9009
10181
|
function formatResultHeader(result, index) {
|
|
9010
10182
|
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}`;
|
|
9011
10183
|
}
|
|
10184
|
+
function formatBlame(result) {
|
|
10185
|
+
if (!result.blame) {
|
|
10186
|
+
return "";
|
|
10187
|
+
}
|
|
10188
|
+
const date = new Date(result.blame.committedAt * 1e3).toISOString().slice(0, 10);
|
|
10189
|
+
return `
|
|
10190
|
+
${result.blame.sha.slice(0, 7)} | ${result.blame.author} | ${date} | ${result.blame.summary}`;
|
|
10191
|
+
}
|
|
9012
10192
|
function formatDefinitionLookup(results, query) {
|
|
9013
10193
|
if (results.length === 0) {
|
|
9014
10194
|
return `No definition found for "${query}". Try codebase_search for broader discovery, or verify the symbol name.`;
|
|
9015
10195
|
}
|
|
9016
10196
|
const formatted = results.map((r, idx) => {
|
|
9017
10197
|
const header = formatResultHeader(r, idx);
|
|
9018
|
-
return `${header} (score: ${r.score.toFixed(2)})
|
|
10198
|
+
return `${header} (score: ${r.score.toFixed(2)})${formatBlame(r)}
|
|
9019
10199
|
\`\`\`
|
|
9020
10200
|
${truncateContent(r.content)}
|
|
9021
10201
|
\`\`\``;
|
|
@@ -9026,7 +10206,7 @@ function formatSearchResults(results, scoreFormat = "similarity") {
|
|
|
9026
10206
|
const formatted = results.map((r, idx) => {
|
|
9027
10207
|
const header = formatResultHeader(r, idx);
|
|
9028
10208
|
const scoreLabel = scoreFormat === "similarity" ? `(similarity: ${(r.score * 100).toFixed(1)}%)` : `(score: ${r.score.toFixed(2)})`;
|
|
9029
|
-
return `${header} ${scoreLabel}
|
|
10209
|
+
return `${header} ${scoreLabel}${formatBlame(r)}
|
|
9030
10210
|
\`\`\`
|
|
9031
10211
|
${truncateContent(r.content)}
|
|
9032
10212
|
\`\`\``;
|
|
@@ -9035,8 +10215,8 @@ ${truncateContent(r.content)}
|
|
|
9035
10215
|
}
|
|
9036
10216
|
|
|
9037
10217
|
// src/tools/config-state.ts
|
|
9038
|
-
var
|
|
9039
|
-
var
|
|
10218
|
+
var import_fs9 = require("fs");
|
|
10219
|
+
var path15 = __toESM(require("path"), 1);
|
|
9040
10220
|
function normalizeKnowledgeBasePaths(config, projectRoot) {
|
|
9041
10221
|
const normalized = { ...config };
|
|
9042
10222
|
if (Array.isArray(normalized.knowledgeBases)) {
|
|
@@ -9063,10 +10243,10 @@ function loadEditableConfig(projectRoot, host = "opencode") {
|
|
|
9063
10243
|
}
|
|
9064
10244
|
function saveConfig(projectRoot, config, host = "opencode") {
|
|
9065
10245
|
const configPath = getConfigPath(projectRoot, host);
|
|
9066
|
-
const configDir =
|
|
9067
|
-
const configBaseDir =
|
|
9068
|
-
if (!(0,
|
|
9069
|
-
(0,
|
|
10246
|
+
const configDir = path15.dirname(configPath);
|
|
10247
|
+
const configBaseDir = path15.dirname(configDir);
|
|
10248
|
+
if (!(0, import_fs9.existsSync)(configDir)) {
|
|
10249
|
+
(0, import_fs9.mkdirSync)(configDir, { recursive: true });
|
|
9070
10250
|
}
|
|
9071
10251
|
const serializableConfig = { ...config };
|
|
9072
10252
|
if (Array.isArray(serializableConfig.knowledgeBases)) {
|
|
@@ -9074,13 +10254,31 @@ function saveConfig(projectRoot, config, host = "opencode") {
|
|
|
9074
10254
|
(kb) => serializeConfigPathValue(kb, configBaseDir)
|
|
9075
10255
|
);
|
|
9076
10256
|
}
|
|
9077
|
-
(0,
|
|
10257
|
+
(0, import_fs9.writeFileSync)(configPath, JSON.stringify(serializableConfig, null, 2) + "\n", "utf-8");
|
|
9078
10258
|
}
|
|
9079
10259
|
|
|
9080
10260
|
// src/tools/operations.ts
|
|
9081
10261
|
var indexerCache = /* @__PURE__ */ new Map();
|
|
9082
10262
|
var configCache = /* @__PURE__ */ new Map();
|
|
9083
10263
|
var defaultProjectRoots = /* @__PURE__ */ new Map();
|
|
10264
|
+
function getIndexBusyResult(error) {
|
|
10265
|
+
if (!isIndexLockContentionError(error)) return null;
|
|
10266
|
+
const owner = error.owner;
|
|
10267
|
+
const ownerText = owner ? `PID ${owner.pid}, operation ${owner.operation}, since ${owner.startedAt}` : "unreadable owner";
|
|
10268
|
+
if (error.reason === "legacy-lock") {
|
|
10269
|
+
return {
|
|
10270
|
+
kind: "busy",
|
|
10271
|
+
text: `INDEX_BUSY: legacy lock format detected (${ownerText}). Verify the PID and remove this lock manually only if it is stale.`
|
|
10272
|
+
};
|
|
10273
|
+
}
|
|
10274
|
+
if (error.reason === "unknown-owner") {
|
|
10275
|
+
return {
|
|
10276
|
+
kind: "busy",
|
|
10277
|
+
text: `INDEX_BUSY: unreadable or remote lock owner (${ownerText}). Automatic recovery was refused; manual verification is required.`
|
|
10278
|
+
};
|
|
10279
|
+
}
|
|
10280
|
+
return { kind: "busy", text: `INDEX_BUSY: another index operation is already in progress (${ownerText}).` };
|
|
10281
|
+
}
|
|
9084
10282
|
function getProjectRoot(projectRoot, host) {
|
|
9085
10283
|
if (projectRoot) {
|
|
9086
10284
|
return projectRoot;
|
|
@@ -9125,8 +10323,8 @@ function refreshIndexerForDirectory(projectRoot, host = "opencode", config = par
|
|
|
9125
10323
|
}
|
|
9126
10324
|
function shouldForceLocalizeProjectIndex(projectRoot, host = "opencode") {
|
|
9127
10325
|
const root = getProjectRoot(projectRoot, host);
|
|
9128
|
-
const localIndexPath =
|
|
9129
|
-
if ((0,
|
|
10326
|
+
const localIndexPath = path16.join(root, getHostProjectIndexRelativePath(host));
|
|
10327
|
+
if ((0, import_fs10.existsSync)(localIndexPath)) {
|
|
9130
10328
|
return false;
|
|
9131
10329
|
}
|
|
9132
10330
|
const inheritedIndexPath = resolveWorktreeFallbackProjectIndexPath(root, host);
|
|
@@ -9140,7 +10338,10 @@ async function searchCodebase(projectRoot, host, query, options = {}) {
|
|
|
9140
10338
|
chunkType: options.chunkType,
|
|
9141
10339
|
contextLines: options.contextLines,
|
|
9142
10340
|
metadataOnly: options.metadataOnly,
|
|
9143
|
-
definitionIntent: options.definitionIntent
|
|
10341
|
+
definitionIntent: options.definitionIntent,
|
|
10342
|
+
blameAuthor: options.blameAuthor,
|
|
10343
|
+
blameSha: options.blameSha,
|
|
10344
|
+
blameSince: options.blameSince
|
|
9144
10345
|
});
|
|
9145
10346
|
}
|
|
9146
10347
|
async function findSimilarCode(projectRoot, host, code, options = {}) {
|
|
@@ -9179,35 +10380,46 @@ async function getCallGraphPath(projectRoot, host, from, to, maxDepth) {
|
|
|
9179
10380
|
async function runIndexCodebase(projectRoot, host, args, onProgress) {
|
|
9180
10381
|
const root = getProjectRoot(projectRoot, host);
|
|
9181
10382
|
const key = getIndexerCacheKey(root, host);
|
|
9182
|
-
const cachedConfig = configCache.get(key);
|
|
9183
10383
|
let indexer = getIndexerForProject(root, host);
|
|
9184
|
-
|
|
9185
|
-
|
|
9186
|
-
|
|
9187
|
-
|
|
9188
|
-
|
|
9189
|
-
|
|
9190
|
-
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
|
|
9194
|
-
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
chunksProcessed: progress.chunksProcessed,
|
|
9204
|
-
totalChunks: progress.totalChunks,
|
|
9205
|
-
percentage: calculatePercentage(progress)
|
|
10384
|
+
const runtimeConfig = configCache.get(key);
|
|
10385
|
+
try {
|
|
10386
|
+
if (args.estimateOnly) {
|
|
10387
|
+
return { kind: "estimate", estimate: await indexer.estimateCost() };
|
|
10388
|
+
}
|
|
10389
|
+
const runIndex = async (target) => {
|
|
10390
|
+
const operation = args.force ? target.forceIndex.bind(target) : target.index.bind(target);
|
|
10391
|
+
return operation((progress) => {
|
|
10392
|
+
if (onProgress) {
|
|
10393
|
+
return onProgress(formatProgressTitle(progress), {
|
|
10394
|
+
phase: progress.phase,
|
|
10395
|
+
filesProcessed: progress.filesProcessed,
|
|
10396
|
+
totalFiles: progress.totalFiles,
|
|
10397
|
+
chunksProcessed: progress.chunksProcessed,
|
|
10398
|
+
totalChunks: progress.totalChunks,
|
|
10399
|
+
percentage: calculatePercentage(progress)
|
|
10400
|
+
});
|
|
10401
|
+
}
|
|
10402
|
+
return Promise.resolve();
|
|
9206
10403
|
});
|
|
10404
|
+
};
|
|
10405
|
+
let stats;
|
|
10406
|
+
if (args.force && shouldForceLocalizeProjectIndex(root, host)) {
|
|
10407
|
+
const inheritedIndexPath = resolveProjectIndexPath(root, runtimeConfig.scope, host);
|
|
10408
|
+
stats = await withIndexLock(inheritedIndexPath, "force-index", async () => {
|
|
10409
|
+
materializeLocalProjectConfig(root, loadProjectConfigLayer(root, host), host);
|
|
10410
|
+
refreshIndexerForDirectory(root, host, runtimeConfig);
|
|
10411
|
+
indexer = getIndexerForProject(root, host);
|
|
10412
|
+
return runIndex(indexer);
|
|
10413
|
+
}, { completeRecoveries: false });
|
|
10414
|
+
} else {
|
|
10415
|
+
stats = await runIndex(indexer);
|
|
9207
10416
|
}
|
|
9208
|
-
return
|
|
9209
|
-
})
|
|
9210
|
-
|
|
10417
|
+
return { kind: "stats", stats };
|
|
10418
|
+
} catch (error) {
|
|
10419
|
+
const busyResult = getIndexBusyResult(error);
|
|
10420
|
+
if (!busyResult) throw error;
|
|
10421
|
+
return busyResult;
|
|
10422
|
+
}
|
|
9211
10423
|
}
|
|
9212
10424
|
async function getIndexStatus(projectRoot, host) {
|
|
9213
10425
|
const indexer = getIndexerForProject(projectRoot, host);
|
|
@@ -9217,6 +10429,15 @@ async function getIndexHealthCheck(projectRoot, host) {
|
|
|
9217
10429
|
const indexer = getIndexerForProject(projectRoot, host);
|
|
9218
10430
|
return indexer.healthCheck();
|
|
9219
10431
|
}
|
|
10432
|
+
async function runIndexHealthCheck(projectRoot, host) {
|
|
10433
|
+
try {
|
|
10434
|
+
return { kind: "health", health: await getIndexHealthCheck(projectRoot, host) };
|
|
10435
|
+
} catch (error) {
|
|
10436
|
+
const busyResult = getIndexBusyResult(error);
|
|
10437
|
+
if (!busyResult) throw error;
|
|
10438
|
+
return busyResult;
|
|
10439
|
+
}
|
|
10440
|
+
}
|
|
9220
10441
|
async function getIndexMetrics(projectRoot, host) {
|
|
9221
10442
|
const indexer = getIndexerForProject(projectRoot, host);
|
|
9222
10443
|
const logger = indexer.getLogger();
|
|
@@ -9272,15 +10493,15 @@ async function getIndexLogs(projectRoot, host, args) {
|
|
|
9272
10493
|
function addKnowledgeBase(projectRoot, host, knowledgeBasePath) {
|
|
9273
10494
|
const root = getProjectRoot(projectRoot, host);
|
|
9274
10495
|
const inputPath = knowledgeBasePath.trim();
|
|
9275
|
-
const normalizedPath =
|
|
9276
|
-
|
|
10496
|
+
const normalizedPath = path16.resolve(
|
|
10497
|
+
path16.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, root)
|
|
9277
10498
|
);
|
|
9278
|
-
if (!(0,
|
|
10499
|
+
if (!(0, import_fs10.existsSync)(normalizedPath)) {
|
|
9279
10500
|
return `Error: Directory does not exist: ${normalizedPath}`;
|
|
9280
10501
|
}
|
|
9281
10502
|
let realPath;
|
|
9282
10503
|
try {
|
|
9283
|
-
realPath = (0,
|
|
10504
|
+
realPath = (0, import_fs10.realpathSync)(normalizedPath);
|
|
9284
10505
|
} catch {
|
|
9285
10506
|
return `Error: Cannot resolve path: ${normalizedPath}`;
|
|
9286
10507
|
}
|
|
@@ -9309,13 +10530,13 @@ function addKnowledgeBase(projectRoot, host, knowledgeBasePath) {
|
|
|
9309
10530
|
}
|
|
9310
10531
|
}
|
|
9311
10532
|
for (const dotDir of sensitiveDotDirs) {
|
|
9312
|
-
const sensitiveDir =
|
|
10533
|
+
const sensitiveDir = path16.join(homeDir, dotDir);
|
|
9313
10534
|
if (sensitiveDir && (realPath === sensitiveDir || realPath.startsWith(`${sensitiveDir}/`))) {
|
|
9314
10535
|
return `Error: Adding sensitive directory as knowledge base is not allowed: ${normalizedPath}`;
|
|
9315
10536
|
}
|
|
9316
10537
|
}
|
|
9317
10538
|
try {
|
|
9318
|
-
const stat4 = (0,
|
|
10539
|
+
const stat4 = (0, import_fs10.statSync)(normalizedPath);
|
|
9319
10540
|
if (!stat4.isDirectory()) {
|
|
9320
10541
|
return `Error: Path is not a directory: ${normalizedPath}`;
|
|
9321
10542
|
}
|
|
@@ -9355,7 +10576,7 @@ function listKnowledgeBases(projectRoot, host) {
|
|
|
9355
10576
|
for (let i = 0; i < knowledgeBases.length; i++) {
|
|
9356
10577
|
const kb = knowledgeBases[i];
|
|
9357
10578
|
const resolvedPath = resolveKnowledgeBasePath(kb, root);
|
|
9358
|
-
const exists = (0,
|
|
10579
|
+
const exists = (0, import_fs10.existsSync)(resolvedPath);
|
|
9359
10580
|
result += `[${i + 1}] ${kb}
|
|
9360
10581
|
`;
|
|
9361
10582
|
result += ` Resolved: ${resolvedPath}
|
|
@@ -9364,7 +10585,7 @@ function listKnowledgeBases(projectRoot, host) {
|
|
|
9364
10585
|
`;
|
|
9365
10586
|
if (exists) {
|
|
9366
10587
|
try {
|
|
9367
|
-
const stat4 = (0,
|
|
10588
|
+
const stat4 = (0, import_fs10.statSync)(resolvedPath);
|
|
9368
10589
|
result += ` Type: ${stat4.isDirectory() ? "Directory" : "File"}
|
|
9369
10590
|
`;
|
|
9370
10591
|
} catch {
|
|
@@ -9372,7 +10593,7 @@ function listKnowledgeBases(projectRoot, host) {
|
|
|
9372
10593
|
}
|
|
9373
10594
|
result += "\n";
|
|
9374
10595
|
}
|
|
9375
|
-
const hasHostConfig = (0,
|
|
10596
|
+
const hasHostConfig = (0, import_fs10.existsSync)(path16.join(root, getHostProjectConfigRelativePath(host)));
|
|
9376
10597
|
if (hasHostConfig) {
|
|
9377
10598
|
result += `
|
|
9378
10599
|
Config sources: 1 file(s).`;
|
|
@@ -9495,7 +10716,7 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
9495
10716
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
9496
10717
|
const statMethod = opts.lstat ? import_promises.lstat : import_promises.stat;
|
|
9497
10718
|
if (wantBigintFsStats) {
|
|
9498
|
-
this._stat = (
|
|
10719
|
+
this._stat = (path24) => statMethod(path24, { bigint: true });
|
|
9499
10720
|
} else {
|
|
9500
10721
|
this._stat = statMethod;
|
|
9501
10722
|
}
|
|
@@ -9520,8 +10741,8 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
9520
10741
|
const par = this.parent;
|
|
9521
10742
|
const fil = par && par.files;
|
|
9522
10743
|
if (fil && fil.length > 0) {
|
|
9523
|
-
const { path:
|
|
9524
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
10744
|
+
const { path: path24, depth } = par;
|
|
10745
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path24));
|
|
9525
10746
|
const awaited = await Promise.all(slice);
|
|
9526
10747
|
for (const entry of awaited) {
|
|
9527
10748
|
if (!entry)
|
|
@@ -9561,20 +10782,20 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
9561
10782
|
this.reading = false;
|
|
9562
10783
|
}
|
|
9563
10784
|
}
|
|
9564
|
-
async _exploreDir(
|
|
10785
|
+
async _exploreDir(path24, depth) {
|
|
9565
10786
|
let files;
|
|
9566
10787
|
try {
|
|
9567
|
-
files = await (0, import_promises.readdir)(
|
|
10788
|
+
files = await (0, import_promises.readdir)(path24, this._rdOptions);
|
|
9568
10789
|
} catch (error) {
|
|
9569
10790
|
this._onError(error);
|
|
9570
10791
|
}
|
|
9571
|
-
return { files, depth, path:
|
|
10792
|
+
return { files, depth, path: path24 };
|
|
9572
10793
|
}
|
|
9573
|
-
async _formatEntry(dirent,
|
|
10794
|
+
async _formatEntry(dirent, path24) {
|
|
9574
10795
|
let entry;
|
|
9575
10796
|
const basename5 = this._isDirent ? dirent.name : dirent;
|
|
9576
10797
|
try {
|
|
9577
|
-
const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(
|
|
10798
|
+
const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path24, basename5));
|
|
9578
10799
|
entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename5 };
|
|
9579
10800
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
9580
10801
|
} catch (err) {
|
|
@@ -9974,16 +11195,16 @@ var delFromSet = (main, prop, item) => {
|
|
|
9974
11195
|
};
|
|
9975
11196
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
9976
11197
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
9977
|
-
function createFsWatchInstance(
|
|
11198
|
+
function createFsWatchInstance(path24, options, listener, errHandler, emitRaw) {
|
|
9978
11199
|
const handleEvent = (rawEvent, evPath) => {
|
|
9979
|
-
listener(
|
|
9980
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
9981
|
-
if (evPath &&
|
|
9982
|
-
fsWatchBroadcast(sp.resolve(
|
|
11200
|
+
listener(path24);
|
|
11201
|
+
emitRaw(rawEvent, evPath, { watchedPath: path24 });
|
|
11202
|
+
if (evPath && path24 !== evPath) {
|
|
11203
|
+
fsWatchBroadcast(sp.resolve(path24, evPath), KEY_LISTENERS, sp.join(path24, evPath));
|
|
9983
11204
|
}
|
|
9984
11205
|
};
|
|
9985
11206
|
try {
|
|
9986
|
-
return (0, import_node_fs.watch)(
|
|
11207
|
+
return (0, import_node_fs.watch)(path24, {
|
|
9987
11208
|
persistent: options.persistent
|
|
9988
11209
|
}, handleEvent);
|
|
9989
11210
|
} catch (error) {
|
|
@@ -9999,12 +11220,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
9999
11220
|
listener(val1, val2, val3);
|
|
10000
11221
|
});
|
|
10001
11222
|
};
|
|
10002
|
-
var setFsWatchListener = (
|
|
11223
|
+
var setFsWatchListener = (path24, fullPath, options, handlers) => {
|
|
10003
11224
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
10004
11225
|
let cont = FsWatchInstances.get(fullPath);
|
|
10005
11226
|
let watcher;
|
|
10006
11227
|
if (!options.persistent) {
|
|
10007
|
-
watcher = createFsWatchInstance(
|
|
11228
|
+
watcher = createFsWatchInstance(path24, options, listener, errHandler, rawEmitter);
|
|
10008
11229
|
if (!watcher)
|
|
10009
11230
|
return;
|
|
10010
11231
|
return watcher.close.bind(watcher);
|
|
@@ -10015,7 +11236,7 @@ var setFsWatchListener = (path22, fullPath, options, handlers) => {
|
|
|
10015
11236
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
10016
11237
|
} else {
|
|
10017
11238
|
watcher = createFsWatchInstance(
|
|
10018
|
-
|
|
11239
|
+
path24,
|
|
10019
11240
|
options,
|
|
10020
11241
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
10021
11242
|
errHandler,
|
|
@@ -10030,7 +11251,7 @@ var setFsWatchListener = (path22, fullPath, options, handlers) => {
|
|
|
10030
11251
|
cont.watcherUnusable = true;
|
|
10031
11252
|
if (isWindows && error.code === "EPERM") {
|
|
10032
11253
|
try {
|
|
10033
|
-
const fd = await (0, import_promises2.open)(
|
|
11254
|
+
const fd = await (0, import_promises2.open)(path24, "r");
|
|
10034
11255
|
await fd.close();
|
|
10035
11256
|
broadcastErr(error);
|
|
10036
11257
|
} catch (err) {
|
|
@@ -10061,7 +11282,7 @@ var setFsWatchListener = (path22, fullPath, options, handlers) => {
|
|
|
10061
11282
|
};
|
|
10062
11283
|
};
|
|
10063
11284
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
10064
|
-
var setFsWatchFileListener = (
|
|
11285
|
+
var setFsWatchFileListener = (path24, fullPath, options, handlers) => {
|
|
10065
11286
|
const { listener, rawEmitter } = handlers;
|
|
10066
11287
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
10067
11288
|
const copts = cont && cont.options;
|
|
@@ -10083,7 +11304,7 @@ var setFsWatchFileListener = (path22, fullPath, options, handlers) => {
|
|
|
10083
11304
|
});
|
|
10084
11305
|
const currmtime = curr.mtimeMs;
|
|
10085
11306
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
10086
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
11307
|
+
foreach(cont.listeners, (listener2) => listener2(path24, curr));
|
|
10087
11308
|
}
|
|
10088
11309
|
})
|
|
10089
11310
|
};
|
|
@@ -10113,13 +11334,13 @@ var NodeFsHandler = class {
|
|
|
10113
11334
|
* @param listener on fs change
|
|
10114
11335
|
* @returns closer for the watcher instance
|
|
10115
11336
|
*/
|
|
10116
|
-
_watchWithNodeFs(
|
|
11337
|
+
_watchWithNodeFs(path24, listener) {
|
|
10117
11338
|
const opts = this.fsw.options;
|
|
10118
|
-
const directory = sp.dirname(
|
|
10119
|
-
const basename5 = sp.basename(
|
|
11339
|
+
const directory = sp.dirname(path24);
|
|
11340
|
+
const basename5 = sp.basename(path24);
|
|
10120
11341
|
const parent = this.fsw._getWatchedDir(directory);
|
|
10121
11342
|
parent.add(basename5);
|
|
10122
|
-
const absolutePath = sp.resolve(
|
|
11343
|
+
const absolutePath = sp.resolve(path24);
|
|
10123
11344
|
const options = {
|
|
10124
11345
|
persistent: opts.persistent
|
|
10125
11346
|
};
|
|
@@ -10129,12 +11350,12 @@ var NodeFsHandler = class {
|
|
|
10129
11350
|
if (opts.usePolling) {
|
|
10130
11351
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
10131
11352
|
options.interval = enableBin && isBinaryPath(basename5) ? opts.binaryInterval : opts.interval;
|
|
10132
|
-
closer = setFsWatchFileListener(
|
|
11353
|
+
closer = setFsWatchFileListener(path24, absolutePath, options, {
|
|
10133
11354
|
listener,
|
|
10134
11355
|
rawEmitter: this.fsw._emitRaw
|
|
10135
11356
|
});
|
|
10136
11357
|
} else {
|
|
10137
|
-
closer = setFsWatchListener(
|
|
11358
|
+
closer = setFsWatchListener(path24, absolutePath, options, {
|
|
10138
11359
|
listener,
|
|
10139
11360
|
errHandler: this._boundHandleError,
|
|
10140
11361
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -10156,7 +11377,7 @@ var NodeFsHandler = class {
|
|
|
10156
11377
|
let prevStats = stats;
|
|
10157
11378
|
if (parent.has(basename5))
|
|
10158
11379
|
return;
|
|
10159
|
-
const listener = async (
|
|
11380
|
+
const listener = async (path24, newStats) => {
|
|
10160
11381
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
10161
11382
|
return;
|
|
10162
11383
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -10170,11 +11391,11 @@ var NodeFsHandler = class {
|
|
|
10170
11391
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
10171
11392
|
}
|
|
10172
11393
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
10173
|
-
this.fsw._closeFile(
|
|
11394
|
+
this.fsw._closeFile(path24);
|
|
10174
11395
|
prevStats = newStats2;
|
|
10175
11396
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
10176
11397
|
if (closer2)
|
|
10177
|
-
this.fsw._addPathCloser(
|
|
11398
|
+
this.fsw._addPathCloser(path24, closer2);
|
|
10178
11399
|
} else {
|
|
10179
11400
|
prevStats = newStats2;
|
|
10180
11401
|
}
|
|
@@ -10206,7 +11427,7 @@ var NodeFsHandler = class {
|
|
|
10206
11427
|
* @param item basename of this item
|
|
10207
11428
|
* @returns true if no more processing is needed for this entry.
|
|
10208
11429
|
*/
|
|
10209
|
-
async _handleSymlink(entry, directory,
|
|
11430
|
+
async _handleSymlink(entry, directory, path24, item) {
|
|
10210
11431
|
if (this.fsw.closed) {
|
|
10211
11432
|
return;
|
|
10212
11433
|
}
|
|
@@ -10216,7 +11437,7 @@ var NodeFsHandler = class {
|
|
|
10216
11437
|
this.fsw._incrReadyCount();
|
|
10217
11438
|
let linkPath;
|
|
10218
11439
|
try {
|
|
10219
|
-
linkPath = await (0, import_promises2.realpath)(
|
|
11440
|
+
linkPath = await (0, import_promises2.realpath)(path24);
|
|
10220
11441
|
} catch (e) {
|
|
10221
11442
|
this.fsw._emitReady();
|
|
10222
11443
|
return true;
|
|
@@ -10226,12 +11447,12 @@ var NodeFsHandler = class {
|
|
|
10226
11447
|
if (dir.has(item)) {
|
|
10227
11448
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
10228
11449
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
10229
|
-
this.fsw._emit(EV.CHANGE,
|
|
11450
|
+
this.fsw._emit(EV.CHANGE, path24, entry.stats);
|
|
10230
11451
|
}
|
|
10231
11452
|
} else {
|
|
10232
11453
|
dir.add(item);
|
|
10233
11454
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
10234
|
-
this.fsw._emit(EV.ADD,
|
|
11455
|
+
this.fsw._emit(EV.ADD, path24, entry.stats);
|
|
10235
11456
|
}
|
|
10236
11457
|
this.fsw._emitReady();
|
|
10237
11458
|
return true;
|
|
@@ -10261,9 +11482,9 @@ var NodeFsHandler = class {
|
|
|
10261
11482
|
return;
|
|
10262
11483
|
}
|
|
10263
11484
|
const item = entry.path;
|
|
10264
|
-
let
|
|
11485
|
+
let path24 = sp.join(directory, item);
|
|
10265
11486
|
current.add(item);
|
|
10266
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
11487
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path24, item)) {
|
|
10267
11488
|
return;
|
|
10268
11489
|
}
|
|
10269
11490
|
if (this.fsw.closed) {
|
|
@@ -10272,8 +11493,8 @@ var NodeFsHandler = class {
|
|
|
10272
11493
|
}
|
|
10273
11494
|
if (item === target || !target && !previous.has(item)) {
|
|
10274
11495
|
this.fsw._incrReadyCount();
|
|
10275
|
-
|
|
10276
|
-
this._addToNodeFs(
|
|
11496
|
+
path24 = sp.join(dir, sp.relative(dir, path24));
|
|
11497
|
+
this._addToNodeFs(path24, initialAdd, wh, depth + 1);
|
|
10277
11498
|
}
|
|
10278
11499
|
}).on(EV.ERROR, this._boundHandleError);
|
|
10279
11500
|
return new Promise((resolve13, reject) => {
|
|
@@ -10342,13 +11563,13 @@ var NodeFsHandler = class {
|
|
|
10342
11563
|
* @param depth Child path actually targeted for watch
|
|
10343
11564
|
* @param target Child path actually targeted for watch
|
|
10344
11565
|
*/
|
|
10345
|
-
async _addToNodeFs(
|
|
11566
|
+
async _addToNodeFs(path24, initialAdd, priorWh, depth, target) {
|
|
10346
11567
|
const ready = this.fsw._emitReady;
|
|
10347
|
-
if (this.fsw._isIgnored(
|
|
11568
|
+
if (this.fsw._isIgnored(path24) || this.fsw.closed) {
|
|
10348
11569
|
ready();
|
|
10349
11570
|
return false;
|
|
10350
11571
|
}
|
|
10351
|
-
const wh = this.fsw._getWatchHelpers(
|
|
11572
|
+
const wh = this.fsw._getWatchHelpers(path24);
|
|
10352
11573
|
if (priorWh) {
|
|
10353
11574
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
10354
11575
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -10364,8 +11585,8 @@ var NodeFsHandler = class {
|
|
|
10364
11585
|
const follow = this.fsw.options.followSymlinks;
|
|
10365
11586
|
let closer;
|
|
10366
11587
|
if (stats.isDirectory()) {
|
|
10367
|
-
const absPath = sp.resolve(
|
|
10368
|
-
const targetPath = follow ? await (0, import_promises2.realpath)(
|
|
11588
|
+
const absPath = sp.resolve(path24);
|
|
11589
|
+
const targetPath = follow ? await (0, import_promises2.realpath)(path24) : path24;
|
|
10369
11590
|
if (this.fsw.closed)
|
|
10370
11591
|
return;
|
|
10371
11592
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -10375,29 +11596,29 @@ var NodeFsHandler = class {
|
|
|
10375
11596
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
10376
11597
|
}
|
|
10377
11598
|
} else if (stats.isSymbolicLink()) {
|
|
10378
|
-
const targetPath = follow ? await (0, import_promises2.realpath)(
|
|
11599
|
+
const targetPath = follow ? await (0, import_promises2.realpath)(path24) : path24;
|
|
10379
11600
|
if (this.fsw.closed)
|
|
10380
11601
|
return;
|
|
10381
11602
|
const parent = sp.dirname(wh.watchPath);
|
|
10382
11603
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
10383
11604
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
10384
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
11605
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path24, wh, targetPath);
|
|
10385
11606
|
if (this.fsw.closed)
|
|
10386
11607
|
return;
|
|
10387
11608
|
if (targetPath !== void 0) {
|
|
10388
|
-
this.fsw._symlinkPaths.set(sp.resolve(
|
|
11609
|
+
this.fsw._symlinkPaths.set(sp.resolve(path24), targetPath);
|
|
10389
11610
|
}
|
|
10390
11611
|
} else {
|
|
10391
11612
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
10392
11613
|
}
|
|
10393
11614
|
ready();
|
|
10394
11615
|
if (closer)
|
|
10395
|
-
this.fsw._addPathCloser(
|
|
11616
|
+
this.fsw._addPathCloser(path24, closer);
|
|
10396
11617
|
return false;
|
|
10397
11618
|
} catch (error) {
|
|
10398
11619
|
if (this.fsw._handleError(error)) {
|
|
10399
11620
|
ready();
|
|
10400
|
-
return
|
|
11621
|
+
return path24;
|
|
10401
11622
|
}
|
|
10402
11623
|
}
|
|
10403
11624
|
}
|
|
@@ -10429,35 +11650,35 @@ function createPattern(matcher) {
|
|
|
10429
11650
|
if (matcher.path === string)
|
|
10430
11651
|
return true;
|
|
10431
11652
|
if (matcher.recursive) {
|
|
10432
|
-
const
|
|
10433
|
-
if (!
|
|
11653
|
+
const relative11 = sp2.relative(matcher.path, string);
|
|
11654
|
+
if (!relative11) {
|
|
10434
11655
|
return false;
|
|
10435
11656
|
}
|
|
10436
|
-
return !
|
|
11657
|
+
return !relative11.startsWith("..") && !sp2.isAbsolute(relative11);
|
|
10437
11658
|
}
|
|
10438
11659
|
return false;
|
|
10439
11660
|
};
|
|
10440
11661
|
}
|
|
10441
11662
|
return () => false;
|
|
10442
11663
|
}
|
|
10443
|
-
function normalizePath(
|
|
10444
|
-
if (typeof
|
|
11664
|
+
function normalizePath(path24) {
|
|
11665
|
+
if (typeof path24 !== "string")
|
|
10445
11666
|
throw new Error("string expected");
|
|
10446
|
-
|
|
10447
|
-
|
|
11667
|
+
path24 = sp2.normalize(path24);
|
|
11668
|
+
path24 = path24.replace(/\\/g, "/");
|
|
10448
11669
|
let prepend = false;
|
|
10449
|
-
if (
|
|
11670
|
+
if (path24.startsWith("//"))
|
|
10450
11671
|
prepend = true;
|
|
10451
|
-
|
|
11672
|
+
path24 = path24.replace(DOUBLE_SLASH_RE, "/");
|
|
10452
11673
|
if (prepend)
|
|
10453
|
-
|
|
10454
|
-
return
|
|
11674
|
+
path24 = "/" + path24;
|
|
11675
|
+
return path24;
|
|
10455
11676
|
}
|
|
10456
11677
|
function matchPatterns(patterns, testString, stats) {
|
|
10457
|
-
const
|
|
11678
|
+
const path24 = normalizePath(testString);
|
|
10458
11679
|
for (let index = 0; index < patterns.length; index++) {
|
|
10459
11680
|
const pattern = patterns[index];
|
|
10460
|
-
if (pattern(
|
|
11681
|
+
if (pattern(path24, stats)) {
|
|
10461
11682
|
return true;
|
|
10462
11683
|
}
|
|
10463
11684
|
}
|
|
@@ -10495,19 +11716,19 @@ var toUnix = (string) => {
|
|
|
10495
11716
|
}
|
|
10496
11717
|
return str;
|
|
10497
11718
|
};
|
|
10498
|
-
var normalizePathToUnix = (
|
|
10499
|
-
var normalizeIgnored = (cwd = "") => (
|
|
10500
|
-
if (typeof
|
|
10501
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
11719
|
+
var normalizePathToUnix = (path24) => toUnix(sp2.normalize(toUnix(path24)));
|
|
11720
|
+
var normalizeIgnored = (cwd = "") => (path24) => {
|
|
11721
|
+
if (typeof path24 === "string") {
|
|
11722
|
+
return normalizePathToUnix(sp2.isAbsolute(path24) ? path24 : sp2.join(cwd, path24));
|
|
10502
11723
|
} else {
|
|
10503
|
-
return
|
|
11724
|
+
return path24;
|
|
10504
11725
|
}
|
|
10505
11726
|
};
|
|
10506
|
-
var getAbsolutePath = (
|
|
10507
|
-
if (sp2.isAbsolute(
|
|
10508
|
-
return
|
|
11727
|
+
var getAbsolutePath = (path24, cwd) => {
|
|
11728
|
+
if (sp2.isAbsolute(path24)) {
|
|
11729
|
+
return path24;
|
|
10509
11730
|
}
|
|
10510
|
-
return sp2.join(cwd,
|
|
11731
|
+
return sp2.join(cwd, path24);
|
|
10511
11732
|
};
|
|
10512
11733
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
10513
11734
|
var DirEntry = class {
|
|
@@ -10572,10 +11793,10 @@ var WatchHelper = class {
|
|
|
10572
11793
|
dirParts;
|
|
10573
11794
|
followSymlinks;
|
|
10574
11795
|
statMethod;
|
|
10575
|
-
constructor(
|
|
11796
|
+
constructor(path24, follow, fsw) {
|
|
10576
11797
|
this.fsw = fsw;
|
|
10577
|
-
const watchPath =
|
|
10578
|
-
this.path =
|
|
11798
|
+
const watchPath = path24;
|
|
11799
|
+
this.path = path24 = path24.replace(REPLACER_RE, "");
|
|
10579
11800
|
this.watchPath = watchPath;
|
|
10580
11801
|
this.fullWatchPath = sp2.resolve(watchPath);
|
|
10581
11802
|
this.dirParts = [];
|
|
@@ -10715,20 +11936,20 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
10715
11936
|
this._closePromise = void 0;
|
|
10716
11937
|
let paths = unifyPaths(paths_);
|
|
10717
11938
|
if (cwd) {
|
|
10718
|
-
paths = paths.map((
|
|
10719
|
-
const absPath = getAbsolutePath(
|
|
11939
|
+
paths = paths.map((path24) => {
|
|
11940
|
+
const absPath = getAbsolutePath(path24, cwd);
|
|
10720
11941
|
return absPath;
|
|
10721
11942
|
});
|
|
10722
11943
|
}
|
|
10723
|
-
paths.forEach((
|
|
10724
|
-
this._removeIgnoredPath(
|
|
11944
|
+
paths.forEach((path24) => {
|
|
11945
|
+
this._removeIgnoredPath(path24);
|
|
10725
11946
|
});
|
|
10726
11947
|
this._userIgnored = void 0;
|
|
10727
11948
|
if (!this._readyCount)
|
|
10728
11949
|
this._readyCount = 0;
|
|
10729
11950
|
this._readyCount += paths.length;
|
|
10730
|
-
Promise.all(paths.map(async (
|
|
10731
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
11951
|
+
Promise.all(paths.map(async (path24) => {
|
|
11952
|
+
const res = await this._nodeFsHandler._addToNodeFs(path24, !_internal, void 0, 0, _origAdd);
|
|
10732
11953
|
if (res)
|
|
10733
11954
|
this._emitReady();
|
|
10734
11955
|
return res;
|
|
@@ -10750,17 +11971,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
10750
11971
|
return this;
|
|
10751
11972
|
const paths = unifyPaths(paths_);
|
|
10752
11973
|
const { cwd } = this.options;
|
|
10753
|
-
paths.forEach((
|
|
10754
|
-
if (!sp2.isAbsolute(
|
|
11974
|
+
paths.forEach((path24) => {
|
|
11975
|
+
if (!sp2.isAbsolute(path24) && !this._closers.has(path24)) {
|
|
10755
11976
|
if (cwd)
|
|
10756
|
-
|
|
10757
|
-
|
|
11977
|
+
path24 = sp2.join(cwd, path24);
|
|
11978
|
+
path24 = sp2.resolve(path24);
|
|
10758
11979
|
}
|
|
10759
|
-
this._closePath(
|
|
10760
|
-
this._addIgnoredPath(
|
|
10761
|
-
if (this._watched.has(
|
|
11980
|
+
this._closePath(path24);
|
|
11981
|
+
this._addIgnoredPath(path24);
|
|
11982
|
+
if (this._watched.has(path24)) {
|
|
10762
11983
|
this._addIgnoredPath({
|
|
10763
|
-
path:
|
|
11984
|
+
path: path24,
|
|
10764
11985
|
recursive: true
|
|
10765
11986
|
});
|
|
10766
11987
|
}
|
|
@@ -10824,38 +12045,38 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
10824
12045
|
* @param stats arguments to be passed with event
|
|
10825
12046
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
10826
12047
|
*/
|
|
10827
|
-
async _emit(event,
|
|
12048
|
+
async _emit(event, path24, stats) {
|
|
10828
12049
|
if (this.closed)
|
|
10829
12050
|
return;
|
|
10830
12051
|
const opts = this.options;
|
|
10831
12052
|
if (isWindows)
|
|
10832
|
-
|
|
12053
|
+
path24 = sp2.normalize(path24);
|
|
10833
12054
|
if (opts.cwd)
|
|
10834
|
-
|
|
10835
|
-
const args = [
|
|
12055
|
+
path24 = sp2.relative(opts.cwd, path24);
|
|
12056
|
+
const args = [path24];
|
|
10836
12057
|
if (stats != null)
|
|
10837
12058
|
args.push(stats);
|
|
10838
12059
|
const awf = opts.awaitWriteFinish;
|
|
10839
12060
|
let pw;
|
|
10840
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
12061
|
+
if (awf && (pw = this._pendingWrites.get(path24))) {
|
|
10841
12062
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
10842
12063
|
return this;
|
|
10843
12064
|
}
|
|
10844
12065
|
if (opts.atomic) {
|
|
10845
12066
|
if (event === EVENTS.UNLINK) {
|
|
10846
|
-
this._pendingUnlinks.set(
|
|
12067
|
+
this._pendingUnlinks.set(path24, [event, ...args]);
|
|
10847
12068
|
setTimeout(() => {
|
|
10848
|
-
this._pendingUnlinks.forEach((entry,
|
|
12069
|
+
this._pendingUnlinks.forEach((entry, path25) => {
|
|
10849
12070
|
this.emit(...entry);
|
|
10850
12071
|
this.emit(EVENTS.ALL, ...entry);
|
|
10851
|
-
this._pendingUnlinks.delete(
|
|
12072
|
+
this._pendingUnlinks.delete(path25);
|
|
10852
12073
|
});
|
|
10853
12074
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
10854
12075
|
return this;
|
|
10855
12076
|
}
|
|
10856
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
12077
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path24)) {
|
|
10857
12078
|
event = EVENTS.CHANGE;
|
|
10858
|
-
this._pendingUnlinks.delete(
|
|
12079
|
+
this._pendingUnlinks.delete(path24);
|
|
10859
12080
|
}
|
|
10860
12081
|
}
|
|
10861
12082
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -10873,16 +12094,16 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
10873
12094
|
this.emitWithAll(event, args);
|
|
10874
12095
|
}
|
|
10875
12096
|
};
|
|
10876
|
-
this._awaitWriteFinish(
|
|
12097
|
+
this._awaitWriteFinish(path24, awf.stabilityThreshold, event, awfEmit);
|
|
10877
12098
|
return this;
|
|
10878
12099
|
}
|
|
10879
12100
|
if (event === EVENTS.CHANGE) {
|
|
10880
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
12101
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path24, 50);
|
|
10881
12102
|
if (isThrottled)
|
|
10882
12103
|
return this;
|
|
10883
12104
|
}
|
|
10884
12105
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
10885
|
-
const fullPath = opts.cwd ? sp2.join(opts.cwd,
|
|
12106
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path24) : path24;
|
|
10886
12107
|
let stats2;
|
|
10887
12108
|
try {
|
|
10888
12109
|
stats2 = await (0, import_promises3.stat)(fullPath);
|
|
@@ -10913,23 +12134,23 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
10913
12134
|
* @param timeout duration of time to suppress duplicate actions
|
|
10914
12135
|
* @returns tracking object or false if action should be suppressed
|
|
10915
12136
|
*/
|
|
10916
|
-
_throttle(actionType,
|
|
12137
|
+
_throttle(actionType, path24, timeout) {
|
|
10917
12138
|
if (!this._throttled.has(actionType)) {
|
|
10918
12139
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
10919
12140
|
}
|
|
10920
12141
|
const action = this._throttled.get(actionType);
|
|
10921
12142
|
if (!action)
|
|
10922
12143
|
throw new Error("invalid throttle");
|
|
10923
|
-
const actionPath = action.get(
|
|
12144
|
+
const actionPath = action.get(path24);
|
|
10924
12145
|
if (actionPath) {
|
|
10925
12146
|
actionPath.count++;
|
|
10926
12147
|
return false;
|
|
10927
12148
|
}
|
|
10928
12149
|
let timeoutObject;
|
|
10929
12150
|
const clear = () => {
|
|
10930
|
-
const item = action.get(
|
|
12151
|
+
const item = action.get(path24);
|
|
10931
12152
|
const count = item ? item.count : 0;
|
|
10932
|
-
action.delete(
|
|
12153
|
+
action.delete(path24);
|
|
10933
12154
|
clearTimeout(timeoutObject);
|
|
10934
12155
|
if (item)
|
|
10935
12156
|
clearTimeout(item.timeoutObject);
|
|
@@ -10937,7 +12158,7 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
10937
12158
|
};
|
|
10938
12159
|
timeoutObject = setTimeout(clear, timeout);
|
|
10939
12160
|
const thr = { timeoutObject, clear, count: 0 };
|
|
10940
|
-
action.set(
|
|
12161
|
+
action.set(path24, thr);
|
|
10941
12162
|
return thr;
|
|
10942
12163
|
}
|
|
10943
12164
|
_incrReadyCount() {
|
|
@@ -10951,44 +12172,44 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
10951
12172
|
* @param event
|
|
10952
12173
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
10953
12174
|
*/
|
|
10954
|
-
_awaitWriteFinish(
|
|
12175
|
+
_awaitWriteFinish(path24, threshold, event, awfEmit) {
|
|
10955
12176
|
const awf = this.options.awaitWriteFinish;
|
|
10956
12177
|
if (typeof awf !== "object")
|
|
10957
12178
|
return;
|
|
10958
12179
|
const pollInterval = awf.pollInterval;
|
|
10959
12180
|
let timeoutHandler;
|
|
10960
|
-
let fullPath =
|
|
10961
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
10962
|
-
fullPath = sp2.join(this.options.cwd,
|
|
12181
|
+
let fullPath = path24;
|
|
12182
|
+
if (this.options.cwd && !sp2.isAbsolute(path24)) {
|
|
12183
|
+
fullPath = sp2.join(this.options.cwd, path24);
|
|
10963
12184
|
}
|
|
10964
12185
|
const now = /* @__PURE__ */ new Date();
|
|
10965
12186
|
const writes = this._pendingWrites;
|
|
10966
12187
|
function awaitWriteFinishFn(prevStat) {
|
|
10967
12188
|
(0, import_node_fs2.stat)(fullPath, (err, curStat) => {
|
|
10968
|
-
if (err || !writes.has(
|
|
12189
|
+
if (err || !writes.has(path24)) {
|
|
10969
12190
|
if (err && err.code !== "ENOENT")
|
|
10970
12191
|
awfEmit(err);
|
|
10971
12192
|
return;
|
|
10972
12193
|
}
|
|
10973
12194
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
10974
12195
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
10975
|
-
writes.get(
|
|
12196
|
+
writes.get(path24).lastChange = now2;
|
|
10976
12197
|
}
|
|
10977
|
-
const pw = writes.get(
|
|
12198
|
+
const pw = writes.get(path24);
|
|
10978
12199
|
const df = now2 - pw.lastChange;
|
|
10979
12200
|
if (df >= threshold) {
|
|
10980
|
-
writes.delete(
|
|
12201
|
+
writes.delete(path24);
|
|
10981
12202
|
awfEmit(void 0, curStat);
|
|
10982
12203
|
} else {
|
|
10983
12204
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
10984
12205
|
}
|
|
10985
12206
|
});
|
|
10986
12207
|
}
|
|
10987
|
-
if (!writes.has(
|
|
10988
|
-
writes.set(
|
|
12208
|
+
if (!writes.has(path24)) {
|
|
12209
|
+
writes.set(path24, {
|
|
10989
12210
|
lastChange: now,
|
|
10990
12211
|
cancelWait: () => {
|
|
10991
|
-
writes.delete(
|
|
12212
|
+
writes.delete(path24);
|
|
10992
12213
|
clearTimeout(timeoutHandler);
|
|
10993
12214
|
return event;
|
|
10994
12215
|
}
|
|
@@ -10999,8 +12220,8 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
10999
12220
|
/**
|
|
11000
12221
|
* Determines whether user has asked to ignore this path.
|
|
11001
12222
|
*/
|
|
11002
|
-
_isIgnored(
|
|
11003
|
-
if (this.options.atomic && DOT_RE.test(
|
|
12223
|
+
_isIgnored(path24, stats) {
|
|
12224
|
+
if (this.options.atomic && DOT_RE.test(path24))
|
|
11004
12225
|
return true;
|
|
11005
12226
|
if (!this._userIgnored) {
|
|
11006
12227
|
const { cwd } = this.options;
|
|
@@ -11010,17 +12231,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
11010
12231
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
11011
12232
|
this._userIgnored = anymatch(list, void 0);
|
|
11012
12233
|
}
|
|
11013
|
-
return this._userIgnored(
|
|
12234
|
+
return this._userIgnored(path24, stats);
|
|
11014
12235
|
}
|
|
11015
|
-
_isntIgnored(
|
|
11016
|
-
return !this._isIgnored(
|
|
12236
|
+
_isntIgnored(path24, stat4) {
|
|
12237
|
+
return !this._isIgnored(path24, stat4);
|
|
11017
12238
|
}
|
|
11018
12239
|
/**
|
|
11019
12240
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
11020
12241
|
* @param path file or directory pattern being watched
|
|
11021
12242
|
*/
|
|
11022
|
-
_getWatchHelpers(
|
|
11023
|
-
return new WatchHelper(
|
|
12243
|
+
_getWatchHelpers(path24) {
|
|
12244
|
+
return new WatchHelper(path24, this.options.followSymlinks, this);
|
|
11024
12245
|
}
|
|
11025
12246
|
// Directory helpers
|
|
11026
12247
|
// -----------------
|
|
@@ -11052,63 +12273,63 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
11052
12273
|
* @param item base path of item/directory
|
|
11053
12274
|
*/
|
|
11054
12275
|
_remove(directory, item, isDirectory) {
|
|
11055
|
-
const
|
|
11056
|
-
const fullPath = sp2.resolve(
|
|
11057
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
11058
|
-
if (!this._throttle("remove",
|
|
12276
|
+
const path24 = sp2.join(directory, item);
|
|
12277
|
+
const fullPath = sp2.resolve(path24);
|
|
12278
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path24) || this._watched.has(fullPath);
|
|
12279
|
+
if (!this._throttle("remove", path24, 100))
|
|
11059
12280
|
return;
|
|
11060
12281
|
if (!isDirectory && this._watched.size === 1) {
|
|
11061
12282
|
this.add(directory, item, true);
|
|
11062
12283
|
}
|
|
11063
|
-
const wp = this._getWatchedDir(
|
|
12284
|
+
const wp = this._getWatchedDir(path24);
|
|
11064
12285
|
const nestedDirectoryChildren = wp.getChildren();
|
|
11065
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
12286
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path24, nested));
|
|
11066
12287
|
const parent = this._getWatchedDir(directory);
|
|
11067
12288
|
const wasTracked = parent.has(item);
|
|
11068
12289
|
parent.remove(item);
|
|
11069
12290
|
if (this._symlinkPaths.has(fullPath)) {
|
|
11070
12291
|
this._symlinkPaths.delete(fullPath);
|
|
11071
12292
|
}
|
|
11072
|
-
let relPath =
|
|
12293
|
+
let relPath = path24;
|
|
11073
12294
|
if (this.options.cwd)
|
|
11074
|
-
relPath = sp2.relative(this.options.cwd,
|
|
12295
|
+
relPath = sp2.relative(this.options.cwd, path24);
|
|
11075
12296
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
11076
12297
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
11077
12298
|
if (event === EVENTS.ADD)
|
|
11078
12299
|
return;
|
|
11079
12300
|
}
|
|
11080
|
-
this._watched.delete(
|
|
12301
|
+
this._watched.delete(path24);
|
|
11081
12302
|
this._watched.delete(fullPath);
|
|
11082
12303
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
11083
|
-
if (wasTracked && !this._isIgnored(
|
|
11084
|
-
this._emit(eventName,
|
|
11085
|
-
this._closePath(
|
|
12304
|
+
if (wasTracked && !this._isIgnored(path24))
|
|
12305
|
+
this._emit(eventName, path24);
|
|
12306
|
+
this._closePath(path24);
|
|
11086
12307
|
}
|
|
11087
12308
|
/**
|
|
11088
12309
|
* Closes all watchers for a path
|
|
11089
12310
|
*/
|
|
11090
|
-
_closePath(
|
|
11091
|
-
this._closeFile(
|
|
11092
|
-
const dir = sp2.dirname(
|
|
11093
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
12311
|
+
_closePath(path24) {
|
|
12312
|
+
this._closeFile(path24);
|
|
12313
|
+
const dir = sp2.dirname(path24);
|
|
12314
|
+
this._getWatchedDir(dir).remove(sp2.basename(path24));
|
|
11094
12315
|
}
|
|
11095
12316
|
/**
|
|
11096
12317
|
* Closes only file-specific watchers
|
|
11097
12318
|
*/
|
|
11098
|
-
_closeFile(
|
|
11099
|
-
const closers = this._closers.get(
|
|
12319
|
+
_closeFile(path24) {
|
|
12320
|
+
const closers = this._closers.get(path24);
|
|
11100
12321
|
if (!closers)
|
|
11101
12322
|
return;
|
|
11102
12323
|
closers.forEach((closer) => closer());
|
|
11103
|
-
this._closers.delete(
|
|
12324
|
+
this._closers.delete(path24);
|
|
11104
12325
|
}
|
|
11105
|
-
_addPathCloser(
|
|
12326
|
+
_addPathCloser(path24, closer) {
|
|
11106
12327
|
if (!closer)
|
|
11107
12328
|
return;
|
|
11108
|
-
let list = this._closers.get(
|
|
12329
|
+
let list = this._closers.get(path24);
|
|
11109
12330
|
if (!list) {
|
|
11110
12331
|
list = [];
|
|
11111
|
-
this._closers.set(
|
|
12332
|
+
this._closers.set(path24, list);
|
|
11112
12333
|
}
|
|
11113
12334
|
list.push(closer);
|
|
11114
12335
|
}
|
|
@@ -11138,7 +12359,7 @@ function watch(paths, options = {}) {
|
|
|
11138
12359
|
var chokidar_default = { watch, FSWatcher };
|
|
11139
12360
|
|
|
11140
12361
|
// src/watcher/file-watcher.ts
|
|
11141
|
-
var
|
|
12362
|
+
var path17 = __toESM(require("path"), 1);
|
|
11142
12363
|
var FileWatcher = class {
|
|
11143
12364
|
watcher = null;
|
|
11144
12365
|
projectRoot;
|
|
@@ -11149,6 +12370,8 @@ var FileWatcher = class {
|
|
|
11149
12370
|
debounceTimer = null;
|
|
11150
12371
|
debounceMs = 1e3;
|
|
11151
12372
|
onChanges = null;
|
|
12373
|
+
readyPromise = null;
|
|
12374
|
+
resolveReady = null;
|
|
11152
12375
|
constructor(projectRoot, config, host = "opencode", options = {}) {
|
|
11153
12376
|
this.projectRoot = projectRoot;
|
|
11154
12377
|
this.config = config;
|
|
@@ -11164,15 +12387,15 @@ var FileWatcher = class {
|
|
|
11164
12387
|
const watchTargets = this.configPath ? [this.projectRoot, this.configPath] : this.projectRoot;
|
|
11165
12388
|
this.watcher = chokidar_default.watch(watchTargets, {
|
|
11166
12389
|
ignored: (filePath) => {
|
|
11167
|
-
const relativePath =
|
|
12390
|
+
const relativePath = path17.relative(this.projectRoot, filePath);
|
|
11168
12391
|
if (!relativePath) return false;
|
|
11169
12392
|
if (this.isProjectConfigPathOrAncestor(relativePath)) {
|
|
11170
12393
|
return false;
|
|
11171
12394
|
}
|
|
11172
|
-
if (hasFilteredPathSegment(relativePath,
|
|
12395
|
+
if (hasFilteredPathSegment(relativePath, path17.sep)) {
|
|
11173
12396
|
return true;
|
|
11174
12397
|
}
|
|
11175
|
-
if (isRestrictedDirectory(relativePath,
|
|
12398
|
+
if (isRestrictedDirectory(relativePath, path17.sep)) {
|
|
11176
12399
|
return true;
|
|
11177
12400
|
}
|
|
11178
12401
|
if (ignoreFilter.ignores(relativePath)) {
|
|
@@ -11187,6 +12410,13 @@ var FileWatcher = class {
|
|
|
11187
12410
|
pollInterval: 100
|
|
11188
12411
|
}
|
|
11189
12412
|
});
|
|
12413
|
+
this.readyPromise = new Promise((resolve13) => {
|
|
12414
|
+
this.resolveReady = resolve13;
|
|
12415
|
+
});
|
|
12416
|
+
this.watcher.once("ready", () => {
|
|
12417
|
+
this.resolveReady?.();
|
|
12418
|
+
this.resolveReady = null;
|
|
12419
|
+
});
|
|
11190
12420
|
this.watcher.on("error", (error) => {
|
|
11191
12421
|
const err = error instanceof Error ? error : null;
|
|
11192
12422
|
if (err?.code === "EPERM" || err?.code === "EACCES") {
|
|
@@ -11218,24 +12448,24 @@ var FileWatcher = class {
|
|
|
11218
12448
|
this.scheduleFlush();
|
|
11219
12449
|
}
|
|
11220
12450
|
isProjectConfigPath(filePath) {
|
|
11221
|
-
const relativePath =
|
|
11222
|
-
const normalizedRelativePath =
|
|
12451
|
+
const relativePath = path17.relative(this.projectRoot, filePath);
|
|
12452
|
+
const normalizedRelativePath = path17.normalize(relativePath);
|
|
11223
12453
|
return this.getProjectConfigRelativePaths().some((configPath) => configPath === normalizedRelativePath);
|
|
11224
12454
|
}
|
|
11225
12455
|
isProjectConfigPathOrAncestor(relativePath) {
|
|
11226
|
-
const normalizedRelativePath =
|
|
12456
|
+
const normalizedRelativePath = path17.normalize(relativePath);
|
|
11227
12457
|
return this.getProjectConfigRelativePaths().some(
|
|
11228
|
-
(configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${
|
|
12458
|
+
(configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path17.sep}`)
|
|
11229
12459
|
);
|
|
11230
12460
|
}
|
|
11231
12461
|
getProjectConfigRelativePaths() {
|
|
11232
12462
|
if (this.configPath) {
|
|
11233
|
-
return [
|
|
12463
|
+
return [path17.normalize(path17.relative(this.projectRoot, this.configPath))];
|
|
11234
12464
|
}
|
|
11235
12465
|
return [
|
|
11236
12466
|
resolveProjectConfigPath(this.projectRoot, this.host),
|
|
11237
12467
|
resolveWritableProjectConfigPath(this.projectRoot, this.host)
|
|
11238
|
-
].map((configPath) =>
|
|
12468
|
+
].map((configPath) => path17.normalize(path17.relative(this.projectRoot, configPath)));
|
|
11239
12469
|
}
|
|
11240
12470
|
scheduleFlush() {
|
|
11241
12471
|
if (this.debounceTimer) {
|
|
@@ -11250,7 +12480,7 @@ var FileWatcher = class {
|
|
|
11250
12480
|
return;
|
|
11251
12481
|
}
|
|
11252
12482
|
const changes = Array.from(this.pendingChanges.entries()).map(
|
|
11253
|
-
([
|
|
12483
|
+
([path24, type]) => ({ path: path24, type })
|
|
11254
12484
|
);
|
|
11255
12485
|
this.pendingChanges.clear();
|
|
11256
12486
|
try {
|
|
@@ -11259,25 +12489,32 @@ var FileWatcher = class {
|
|
|
11259
12489
|
console.error("Error handling file changes:", error);
|
|
11260
12490
|
}
|
|
11261
12491
|
}
|
|
11262
|
-
stop() {
|
|
12492
|
+
async stop() {
|
|
11263
12493
|
if (this.debounceTimer) {
|
|
11264
12494
|
clearTimeout(this.debounceTimer);
|
|
11265
12495
|
this.debounceTimer = null;
|
|
11266
12496
|
}
|
|
11267
12497
|
if (this.watcher) {
|
|
11268
|
-
this.watcher
|
|
12498
|
+
const watcher = this.watcher;
|
|
11269
12499
|
this.watcher = null;
|
|
12500
|
+
await watcher.close();
|
|
11270
12501
|
}
|
|
12502
|
+
this.resolveReady?.();
|
|
12503
|
+
this.resolveReady = null;
|
|
12504
|
+
this.readyPromise = null;
|
|
11271
12505
|
this.pendingChanges.clear();
|
|
11272
12506
|
this.onChanges = null;
|
|
11273
12507
|
}
|
|
11274
12508
|
isRunning() {
|
|
11275
12509
|
return this.watcher !== null;
|
|
11276
12510
|
}
|
|
12511
|
+
async waitUntilReady() {
|
|
12512
|
+
await (this.readyPromise ?? Promise.resolve());
|
|
12513
|
+
}
|
|
11277
12514
|
};
|
|
11278
12515
|
|
|
11279
12516
|
// src/watcher/git-head-watcher.ts
|
|
11280
|
-
var
|
|
12517
|
+
var path18 = __toESM(require("path"), 1);
|
|
11281
12518
|
var GitHeadWatcher = class {
|
|
11282
12519
|
watcher = null;
|
|
11283
12520
|
projectRoot;
|
|
@@ -11299,7 +12536,7 @@ var GitHeadWatcher = class {
|
|
|
11299
12536
|
this.onBranchChange = handler;
|
|
11300
12537
|
this.currentBranch = getCurrentBranch(this.projectRoot);
|
|
11301
12538
|
const headPath = getHeadPath(this.projectRoot);
|
|
11302
|
-
const refsPath =
|
|
12539
|
+
const refsPath = path18.join(this.projectRoot, ".git", "refs", "heads");
|
|
11303
12540
|
this.watcher = chokidar_default.watch([headPath, refsPath], {
|
|
11304
12541
|
persistent: true,
|
|
11305
12542
|
ignoreInitial: true,
|
|
@@ -11336,14 +12573,15 @@ var GitHeadWatcher = class {
|
|
|
11336
12573
|
getCurrentBranch() {
|
|
11337
12574
|
return this.currentBranch;
|
|
11338
12575
|
}
|
|
11339
|
-
stop() {
|
|
12576
|
+
async stop() {
|
|
11340
12577
|
if (this.debounceTimer) {
|
|
11341
12578
|
clearTimeout(this.debounceTimer);
|
|
11342
12579
|
this.debounceTimer = null;
|
|
11343
12580
|
}
|
|
11344
12581
|
if (this.watcher) {
|
|
11345
|
-
this.watcher
|
|
12582
|
+
const watcher = this.watcher;
|
|
11346
12583
|
this.watcher = null;
|
|
12584
|
+
await watcher.close();
|
|
11347
12585
|
}
|
|
11348
12586
|
this.onBranchChange = null;
|
|
11349
12587
|
}
|
|
@@ -11361,6 +12599,8 @@ var BackgroundReindexer = class {
|
|
|
11361
12599
|
running = false;
|
|
11362
12600
|
pending = false;
|
|
11363
12601
|
stopped = false;
|
|
12602
|
+
retryTimer = null;
|
|
12603
|
+
retryDelayMs = 50;
|
|
11364
12604
|
request() {
|
|
11365
12605
|
if (this.stopped) {
|
|
11366
12606
|
return;
|
|
@@ -11371,9 +12611,13 @@ var BackgroundReindexer = class {
|
|
|
11371
12611
|
stop() {
|
|
11372
12612
|
this.stopped = true;
|
|
11373
12613
|
this.pending = false;
|
|
12614
|
+
if (this.retryTimer) {
|
|
12615
|
+
clearTimeout(this.retryTimer);
|
|
12616
|
+
this.retryTimer = null;
|
|
12617
|
+
}
|
|
11374
12618
|
}
|
|
11375
12619
|
drain() {
|
|
11376
|
-
if (this.stopped || this.running || !this.pending) {
|
|
12620
|
+
if (this.stopped || this.running || this.retryTimer || !this.pending) {
|
|
11377
12621
|
return;
|
|
11378
12622
|
}
|
|
11379
12623
|
this.pending = false;
|
|
@@ -11381,13 +12625,29 @@ var BackgroundReindexer = class {
|
|
|
11381
12625
|
void this.run();
|
|
11382
12626
|
}
|
|
11383
12627
|
async run() {
|
|
12628
|
+
let retryAfterContention = false;
|
|
11384
12629
|
try {
|
|
11385
12630
|
await this.runIndex();
|
|
12631
|
+
this.retryDelayMs = 50;
|
|
11386
12632
|
} catch (error) {
|
|
11387
|
-
|
|
12633
|
+
if (isTransientIndexLockContention(error)) {
|
|
12634
|
+
this.pending = true;
|
|
12635
|
+
retryAfterContention = true;
|
|
12636
|
+
} else {
|
|
12637
|
+
console.error("[codebase-index] Background reindex failed:", error);
|
|
12638
|
+
}
|
|
11388
12639
|
} finally {
|
|
11389
12640
|
this.running = false;
|
|
11390
|
-
this.
|
|
12641
|
+
if (retryAfterContention && !this.stopped) {
|
|
12642
|
+
const delay = this.retryDelayMs;
|
|
12643
|
+
this.retryDelayMs = Math.min(this.retryDelayMs * 2, 500);
|
|
12644
|
+
this.retryTimer = setTimeout(() => {
|
|
12645
|
+
this.retryTimer = null;
|
|
12646
|
+
this.drain();
|
|
12647
|
+
}, delay);
|
|
12648
|
+
} else {
|
|
12649
|
+
this.drain();
|
|
12650
|
+
}
|
|
11391
12651
|
}
|
|
11392
12652
|
}
|
|
11393
12653
|
};
|
|
@@ -11421,10 +12681,12 @@ function createWatcherWithIndexer(getIndexer, projectRoot, config, host = "openc
|
|
|
11421
12681
|
return {
|
|
11422
12682
|
fileWatcher,
|
|
11423
12683
|
gitWatcher,
|
|
11424
|
-
|
|
12684
|
+
whenReady() {
|
|
12685
|
+
return fileWatcher.waitUntilReady();
|
|
12686
|
+
},
|
|
12687
|
+
async stop() {
|
|
11425
12688
|
backgroundReindexer.stop();
|
|
11426
|
-
fileWatcher.stop();
|
|
11427
|
-
gitWatcher?.stop();
|
|
12689
|
+
await Promise.all([fileWatcher.stop(), gitWatcher?.stop()]);
|
|
11428
12690
|
}
|
|
11429
12691
|
};
|
|
11430
12692
|
}
|
|
@@ -11530,13 +12792,13 @@ var pr_impact = (0, import_plugin.tool)({
|
|
|
11530
12792
|
});
|
|
11531
12793
|
|
|
11532
12794
|
// src/tools/index.ts
|
|
11533
|
-
var
|
|
11534
|
-
var
|
|
11535
|
-
var
|
|
12795
|
+
var import_fs11 = require("fs");
|
|
12796
|
+
var os5 = __toESM(require("os"), 1);
|
|
12797
|
+
var path21 = __toESM(require("path"), 1);
|
|
11536
12798
|
|
|
11537
12799
|
// src/tools/visualize/activity.ts
|
|
11538
|
-
var
|
|
11539
|
-
var
|
|
12800
|
+
var import_child_process4 = require("child_process");
|
|
12801
|
+
var path19 = __toESM(require("path"), 1);
|
|
11540
12802
|
function attachRecentActivity(data, projectRoot) {
|
|
11541
12803
|
const activity = readGitActivity(projectRoot);
|
|
11542
12804
|
const changes = activity.size > 0 ? buildGitChanges(data, activity, projectRoot) : buildGraphChanges(data);
|
|
@@ -11547,7 +12809,7 @@ function attachRecentActivity(data, projectRoot) {
|
|
|
11547
12809
|
}
|
|
11548
12810
|
function readGitActivity(projectRoot) {
|
|
11549
12811
|
try {
|
|
11550
|
-
const output = (0,
|
|
12812
|
+
const output = (0, import_child_process4.execFileSync)(
|
|
11551
12813
|
"git",
|
|
11552
12814
|
["-C", projectRoot, "log", "--since=90.days", "--numstat", "--date=short", "--pretty=format:__COMMIT__%x09%h%x09%ad%x09%s"],
|
|
11553
12815
|
{ encoding: "utf8", maxBuffer: 8 * 1024 * 1024, stdio: ["ignore", "pipe", "ignore"] }
|
|
@@ -11698,7 +12960,7 @@ function normalizePath2(filePath) {
|
|
|
11698
12960
|
return filePath.replace(/\\/g, "/");
|
|
11699
12961
|
}
|
|
11700
12962
|
function toGitRelativePath(projectRoot, filePath) {
|
|
11701
|
-
const relativePath =
|
|
12963
|
+
const relativePath = path19.isAbsolute(filePath) ? path19.relative(projectRoot, filePath) : filePath;
|
|
11702
12964
|
return normalizePath2(relativePath);
|
|
11703
12965
|
}
|
|
11704
12966
|
|
|
@@ -11956,7 +13218,7 @@ render();
|
|
|
11956
13218
|
}
|
|
11957
13219
|
|
|
11958
13220
|
// src/tools/visualize/transform.ts
|
|
11959
|
-
var
|
|
13221
|
+
var path20 = __toESM(require("path"), 1);
|
|
11960
13222
|
|
|
11961
13223
|
// src/tools/visualize/modules.ts
|
|
11962
13224
|
var MAX_MODULES = 18;
|
|
@@ -12089,8 +13351,8 @@ function compactModules(prefixToNodes) {
|
|
|
12089
13351
|
function deriveModules(nodes) {
|
|
12090
13352
|
const initial = /* @__PURE__ */ new Map();
|
|
12091
13353
|
for (const node of nodes) {
|
|
12092
|
-
const
|
|
12093
|
-
const prefix = modulePrefixFromRelativePath(
|
|
13354
|
+
const relative11 = stripToProjectRelative(node.filePath);
|
|
13355
|
+
const prefix = modulePrefixFromRelativePath(relative11);
|
|
12094
13356
|
if (!initial.has(prefix)) initial.set(prefix, []);
|
|
12095
13357
|
initial.get(prefix)?.push(node);
|
|
12096
13358
|
}
|
|
@@ -12216,7 +13478,7 @@ function transformForVisualization(symbols, edges, options = {}) {
|
|
|
12216
13478
|
filePath: s.filePath,
|
|
12217
13479
|
kind: s.kind,
|
|
12218
13480
|
line: s.startLine,
|
|
12219
|
-
directory:
|
|
13481
|
+
directory: path20.dirname(s.filePath),
|
|
12220
13482
|
moduleId: "",
|
|
12221
13483
|
moduleLabel: ""
|
|
12222
13484
|
}));
|
|
@@ -12267,7 +13529,10 @@ var codebase_peek = (0, import_plugin2.tool)({
|
|
|
12267
13529
|
limit: z2.number().optional().default(10).describe("Maximum number of results to return"),
|
|
12268
13530
|
fileType: z2.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
|
|
12269
13531
|
directory: z2.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
|
|
12270
|
-
chunkType: z2.enum(CHUNK_TYPE_VALUES).optional().describe("Filter by code chunk type")
|
|
13532
|
+
chunkType: z2.enum(CHUNK_TYPE_VALUES).optional().describe("Filter by code chunk type"),
|
|
13533
|
+
blameAuthor: z2.string().optional().describe("Filter by git blame author name or email"),
|
|
13534
|
+
blameSha: z2.string().optional().describe("Filter by git blame commit SHA or prefix"),
|
|
13535
|
+
blameSince: z2.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)")
|
|
12271
13536
|
},
|
|
12272
13537
|
async execute(args, context) {
|
|
12273
13538
|
const results = await searchCodebase(context?.worktree, DEFAULT_HOST, args.query, {
|
|
@@ -12275,7 +13540,10 @@ var codebase_peek = (0, import_plugin2.tool)({
|
|
|
12275
13540
|
fileType: args.fileType,
|
|
12276
13541
|
directory: args.directory,
|
|
12277
13542
|
chunkType: args.chunkType,
|
|
12278
|
-
metadataOnly: true
|
|
13543
|
+
metadataOnly: true,
|
|
13544
|
+
blameAuthor: args.blameAuthor,
|
|
13545
|
+
blameSha: args.blameSha,
|
|
13546
|
+
blameSince: args.blameSince
|
|
12279
13547
|
});
|
|
12280
13548
|
return formatCodebasePeek(results);
|
|
12281
13549
|
}
|
|
@@ -12291,7 +13559,9 @@ var index_codebase = (0, import_plugin2.tool)({
|
|
|
12291
13559
|
const result = await runIndexCodebase(context?.worktree, DEFAULT_HOST, args, (title, metadata) => {
|
|
12292
13560
|
context.metadata({ title, metadata });
|
|
12293
13561
|
});
|
|
12294
|
-
|
|
13562
|
+
if (result.kind === "estimate") return formatCostEstimate(result.estimate);
|
|
13563
|
+
if (result.kind === "busy") return result.text;
|
|
13564
|
+
return formatIndexStats(result.stats, args.verbose ?? false);
|
|
12295
13565
|
}
|
|
12296
13566
|
});
|
|
12297
13567
|
var index_status = (0, import_plugin2.tool)({
|
|
@@ -12305,7 +13575,9 @@ var index_health_check = (0, import_plugin2.tool)({
|
|
|
12305
13575
|
description: "Check index health and remove stale entries from deleted files. Run this to clean up the index after files have been deleted.",
|
|
12306
13576
|
args: {},
|
|
12307
13577
|
async execute(_args, context) {
|
|
12308
|
-
|
|
13578
|
+
const result = await runIndexHealthCheck(context?.worktree, DEFAULT_HOST);
|
|
13579
|
+
if (result.kind === "busy") return result.text;
|
|
13580
|
+
return formatHealthCheck(result.health);
|
|
12309
13581
|
}
|
|
12310
13582
|
});
|
|
12311
13583
|
var index_metrics = (0, import_plugin2.tool)({
|
|
@@ -12358,7 +13630,10 @@ var codebase_search = (0, import_plugin2.tool)({
|
|
|
12358
13630
|
fileType: z2.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
|
|
12359
13631
|
directory: z2.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
|
|
12360
13632
|
chunkType: z2.enum(CHUNK_TYPE_VALUES).optional().describe("Filter by code chunk type"),
|
|
12361
|
-
contextLines: z2.number().optional().describe("Number of extra lines to include before/after each match (default: 0)")
|
|
13633
|
+
contextLines: z2.number().optional().describe("Number of extra lines to include before/after each match (default: 0)"),
|
|
13634
|
+
blameAuthor: z2.string().optional().describe("Filter by git blame author name or email"),
|
|
13635
|
+
blameSha: z2.string().optional().describe("Filter by git blame commit SHA or prefix"),
|
|
13636
|
+
blameSince: z2.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)")
|
|
12362
13637
|
},
|
|
12363
13638
|
async execute(args, context) {
|
|
12364
13639
|
const results = await searchCodebase(context?.worktree, DEFAULT_HOST, args.query, {
|
|
@@ -12366,7 +13641,10 @@ var codebase_search = (0, import_plugin2.tool)({
|
|
|
12366
13641
|
fileType: args.fileType,
|
|
12367
13642
|
directory: args.directory,
|
|
12368
13643
|
chunkType: args.chunkType,
|
|
12369
|
-
contextLines: args.contextLines
|
|
13644
|
+
contextLines: args.contextLines,
|
|
13645
|
+
blameAuthor: args.blameAuthor,
|
|
13646
|
+
blameSha: args.blameSha,
|
|
13647
|
+
blameSince: args.blameSince
|
|
12370
13648
|
});
|
|
12371
13649
|
if (results.length === 0) {
|
|
12372
13650
|
return "No matching code found. Try a different query or run index_codebase first.";
|
|
@@ -12405,22 +13683,10 @@ var call_graph = (0, import_plugin2.tool)({
|
|
|
12405
13683
|
return "Error: 'symbolId' is required when direction is 'callees'. First use direction='callers' to find the symbol ID.";
|
|
12406
13684
|
}
|
|
12407
13685
|
const { callees } = await getCallGraphData(context?.worktree, DEFAULT_HOST, args);
|
|
12408
|
-
|
|
12409
|
-
return `No callees found for symbol ${args.symbolId}${args.relationshipType ? ` with type ${args.relationshipType}` : ""}. The function may not call any other tracked functions.`;
|
|
12410
|
-
}
|
|
12411
|
-
return callees.map((edge, index) => {
|
|
12412
|
-
const confidence = edge.confidence !== "Direct" ? ` [${edge.confidence.toLowerCase()}]` : "";
|
|
12413
|
-
return `[${index + 1}] \u2192 ${edge.targetName} (${edge.callType})${confidence} at line ${edge.line}${edge.isResolved ? ` [resolved: ${edge.toSymbolId}]` : " [unresolved]"}`;
|
|
12414
|
-
}).join("\n");
|
|
13686
|
+
return formatCallGraphCallees(args.symbolId, callees, args.relationshipType);
|
|
12415
13687
|
}
|
|
12416
13688
|
const { callers } = await getCallGraphData(context?.worktree, DEFAULT_HOST, args);
|
|
12417
|
-
|
|
12418
|
-
return `No callers found for "${args.name}"${args.relationshipType ? ` with type ${args.relationshipType}` : ""}. It may not be called by any tracked function, or the index needs updating.`;
|
|
12419
|
-
}
|
|
12420
|
-
return callers.map((edge, index) => {
|
|
12421
|
-
const confidence = edge.confidence !== "Direct" ? ` [${edge.confidence.toLowerCase()}]` : "";
|
|
12422
|
-
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]"}`;
|
|
12423
|
-
}).join("\n");
|
|
13689
|
+
return formatCallGraphCallers(args.name, callers, args.relationshipType);
|
|
12424
13690
|
}
|
|
12425
13691
|
});
|
|
12426
13692
|
var call_graph_path = (0, import_plugin2.tool)({
|
|
@@ -12431,17 +13697,8 @@ var call_graph_path = (0, import_plugin2.tool)({
|
|
|
12431
13697
|
maxDepth: z2.number().optional().default(10).describe("Maximum traversal depth (default: 10)")
|
|
12432
13698
|
},
|
|
12433
13699
|
async execute(args, context) {
|
|
12434
|
-
const
|
|
12435
|
-
|
|
12436
|
-
return `No path found between "${args.from}" and "${args.to}". They may be in disconnected components, or the call graph index needs updating.`;
|
|
12437
|
-
}
|
|
12438
|
-
const formatted = path22.map((hop, index) => {
|
|
12439
|
-
const prefix = index === 0 ? "[start]" : `--${hop.callType}-->`;
|
|
12440
|
-
const location = hop.filePath ? ` (${hop.filePath}:${hop.line})` : "";
|
|
12441
|
-
return `${prefix} ${hop.symbolName}${location}`;
|
|
12442
|
-
});
|
|
12443
|
-
return `Path (${path22.length} hops):
|
|
12444
|
-
${formatted.join("\n")}`;
|
|
13700
|
+
const path24 = await getCallGraphPath(context?.worktree, DEFAULT_HOST, args.from, args.to, args.maxDepth);
|
|
13701
|
+
return formatCallGraphPath(args.from, args.to, path24);
|
|
12445
13702
|
}
|
|
12446
13703
|
});
|
|
12447
13704
|
var add_knowledge_base = (0, import_plugin2.tool)({
|
|
@@ -12494,8 +13751,8 @@ var index_visualize = (0, import_plugin2.tool)({
|
|
|
12494
13751
|
return "No connected symbols found for visualization. Try including orphans with includeOrphans=true, or check that the call graph has resolved edges.";
|
|
12495
13752
|
}
|
|
12496
13753
|
const html = generateVisualizationHtml(vizData);
|
|
12497
|
-
const outputPath =
|
|
12498
|
-
(0,
|
|
13754
|
+
const outputPath = path21.join(os5.tmpdir(), `call-graph-${Date.now()}.html`);
|
|
13755
|
+
(0, import_fs11.writeFileSync)(outputPath, html, "utf-8");
|
|
12499
13756
|
let result = `Temporal call graph visualization generated: ${outputPath}
|
|
12500
13757
|
|
|
12501
13758
|
`;
|
|
@@ -12516,8 +13773,8 @@ var index_visualize = (0, import_plugin2.tool)({
|
|
|
12516
13773
|
});
|
|
12517
13774
|
|
|
12518
13775
|
// src/commands/loader.ts
|
|
12519
|
-
var
|
|
12520
|
-
var
|
|
13776
|
+
var import_fs12 = require("fs");
|
|
13777
|
+
var path22 = __toESM(require("path"), 1);
|
|
12521
13778
|
function parseFrontmatter(content) {
|
|
12522
13779
|
const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
|
|
12523
13780
|
const match = content.match(frontmatterRegex);
|
|
@@ -12538,21 +13795,21 @@ function parseFrontmatter(content) {
|
|
|
12538
13795
|
}
|
|
12539
13796
|
function loadCommandsFromDirectory(commandsDir) {
|
|
12540
13797
|
const commands = /* @__PURE__ */ new Map();
|
|
12541
|
-
if (!(0,
|
|
13798
|
+
if (!(0, import_fs12.existsSync)(commandsDir)) {
|
|
12542
13799
|
return commands;
|
|
12543
13800
|
}
|
|
12544
|
-
const files = (0,
|
|
13801
|
+
const files = (0, import_fs12.readdirSync)(commandsDir).filter((f) => f.endsWith(".md"));
|
|
12545
13802
|
for (const file of files) {
|
|
12546
|
-
const filePath =
|
|
13803
|
+
const filePath = path22.join(commandsDir, file);
|
|
12547
13804
|
let content;
|
|
12548
13805
|
try {
|
|
12549
|
-
content = (0,
|
|
13806
|
+
content = (0, import_fs12.readFileSync)(filePath, "utf-8");
|
|
12550
13807
|
} catch (error) {
|
|
12551
13808
|
const message = error instanceof Error ? error.message : String(error);
|
|
12552
13809
|
throw new Error(`Failed to load command file ${filePath}: ${message}`);
|
|
12553
13810
|
}
|
|
12554
13811
|
const { frontmatter, body } = parseFrontmatter(content);
|
|
12555
|
-
const name =
|
|
13812
|
+
const name = path22.basename(file, ".md");
|
|
12556
13813
|
const description = frontmatter.description || `Run the ${name} command`;
|
|
12557
13814
|
commands.set(name, {
|
|
12558
13815
|
description,
|
|
@@ -12856,18 +14113,37 @@ var RoutingHintController = class {
|
|
|
12856
14113
|
};
|
|
12857
14114
|
|
|
12858
14115
|
// src/utils/auto-index.ts
|
|
14116
|
+
var INITIAL_RETRY_DELAY_MS = 50;
|
|
14117
|
+
var MAX_RETRY_DELAY_MS = 500;
|
|
14118
|
+
var autoIndexStates = /* @__PURE__ */ new WeakMap();
|
|
12859
14119
|
function getErrorMessage3(error) {
|
|
12860
14120
|
return error instanceof Error ? error.message : String(error);
|
|
12861
14121
|
}
|
|
12862
|
-
function
|
|
12863
|
-
indexer.
|
|
12864
|
-
|
|
12865
|
-
console.error(`[codebase-index] Auto-index failed for "${projectRoot}": ${getErrorMessage3(error)}`);
|
|
12866
|
-
});
|
|
14122
|
+
function runAutoIndex(indexer, projectRoot, state) {
|
|
14123
|
+
void indexer.index().then(() => {
|
|
14124
|
+
autoIndexStates.delete(indexer);
|
|
12867
14125
|
}).catch((error) => {
|
|
12868
|
-
|
|
14126
|
+
if (!isTransientIndexLockContention(error)) {
|
|
14127
|
+
autoIndexStates.delete(indexer);
|
|
14128
|
+
console.error(`[codebase-index] Auto-index failed for "${projectRoot}": ${getErrorMessage3(error)}`);
|
|
14129
|
+
return;
|
|
14130
|
+
}
|
|
14131
|
+
const retryDelayMs = state.retryDelayMs;
|
|
14132
|
+
state.retryDelayMs = Math.min(retryDelayMs * 2, MAX_RETRY_DELAY_MS);
|
|
14133
|
+
const retryTimer = setTimeout(() => {
|
|
14134
|
+
runAutoIndex(indexer, projectRoot, state);
|
|
14135
|
+
}, retryDelayMs);
|
|
14136
|
+
retryTimer.unref?.();
|
|
12869
14137
|
});
|
|
12870
14138
|
}
|
|
14139
|
+
function startAutoIndex(indexer, projectRoot) {
|
|
14140
|
+
if (autoIndexStates.has(indexer)) return;
|
|
14141
|
+
const state = {
|
|
14142
|
+
retryDelayMs: INITIAL_RETRY_DELAY_MS
|
|
14143
|
+
};
|
|
14144
|
+
autoIndexStates.set(indexer, state);
|
|
14145
|
+
runAutoIndex(indexer, projectRoot, state);
|
|
14146
|
+
}
|
|
12871
14147
|
|
|
12872
14148
|
// src/index.ts
|
|
12873
14149
|
var import_meta2 = {};
|
|
@@ -12885,9 +14161,9 @@ function replaceActiveWatcher(projectRoot, nextWatcher) {
|
|
|
12885
14161
|
function getCommandsDir() {
|
|
12886
14162
|
let currentDir = process.cwd();
|
|
12887
14163
|
if (typeof import_meta2 !== "undefined" && import_meta2.url) {
|
|
12888
|
-
currentDir =
|
|
14164
|
+
currentDir = path23.dirname((0, import_url2.fileURLToPath)(import_meta2.url));
|
|
12889
14165
|
}
|
|
12890
|
-
return
|
|
14166
|
+
return path23.join(currentDir, "..", "commands");
|
|
12891
14167
|
}
|
|
12892
14168
|
function appendRoutingHints(output, hints, preferredRole) {
|
|
12893
14169
|
const preferredBucket = preferredRole === "developer" ? output.developer : output.system;
|
|
@@ -12907,7 +14183,7 @@ var plugin = async ({ directory, worktree }) => {
|
|
|
12907
14183
|
initializeTools2(projectRoot, config);
|
|
12908
14184
|
const getProjectIndexer = () => getIndexerForProject2(projectRoot);
|
|
12909
14185
|
const routingHints = config.search.routingHints ? new RoutingHintController(() => getProjectIndexer().getStatus(), 200, config.search.routingGraphHandoffHints) : null;
|
|
12910
|
-
const isHomeDir =
|
|
14186
|
+
const isHomeDir = path23.resolve(projectRoot) === path23.resolve(os6.homedir());
|
|
12911
14187
|
const isValidProject = !isHomeDir && (!config.indexing.requireProjectMarker || hasProjectMarker(projectRoot));
|
|
12912
14188
|
if (isHomeDir) {
|
|
12913
14189
|
console.warn(
|