opencode-codebase-index 0.14.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +2 -2
- package/README.md +73 -12
- package/dist/cli.cjs +1782 -607
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1781 -596
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1610 -533
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1606 -519
- package/dist/index.js.map +1 -1
- package/dist/pi-extension.cjs +1396 -311
- package/dist/pi-extension.cjs.map +1 -1
- package/dist/pi-extension.js +1395 -300
- package/dist/pi-extension.js.map +1 -1
- package/hooks/hooks.json +2 -2
- 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/skills/codebase-search/SKILL.md +9 -7
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}",
|
|
@@ -1254,6 +1254,9 @@ function getHostProjectIndexRelativePath(host) {
|
|
|
1254
1254
|
function hasHostProjectConfig(projectRoot, host) {
|
|
1255
1255
|
return (0, import_fs3.existsSync)(path3.join(projectRoot, getProjectConfigRelativePath(host)));
|
|
1256
1256
|
}
|
|
1257
|
+
function hasHostGlobalConfig(host) {
|
|
1258
|
+
return (0, import_fs3.existsSync)(getGlobalConfigPath(host));
|
|
1259
|
+
}
|
|
1257
1260
|
function getGlobalIndexPath(host = "opencode") {
|
|
1258
1261
|
switch (host) {
|
|
1259
1262
|
case "opencode":
|
|
@@ -1293,6 +1296,9 @@ function resolveGlobalIndexPath(host = "opencode") {
|
|
|
1293
1296
|
return hostIndexPath;
|
|
1294
1297
|
}
|
|
1295
1298
|
if (host !== "opencode") {
|
|
1299
|
+
if (hasHostGlobalConfig(host)) {
|
|
1300
|
+
return hostIndexPath;
|
|
1301
|
+
}
|
|
1296
1302
|
const legacyIndexPath = getGlobalIndexPath("opencode");
|
|
1297
1303
|
if ((0, import_fs3.existsSync)(legacyIndexPath)) {
|
|
1298
1304
|
return legacyIndexPath;
|
|
@@ -1591,13 +1597,429 @@ function loadMergedConfig(projectRoot, host = "opencode") {
|
|
|
1591
1597
|
return merged;
|
|
1592
1598
|
}
|
|
1593
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
|
+
|
|
1594
2016
|
// src/tools/operations.ts
|
|
1595
|
-
var
|
|
1596
|
-
var
|
|
2017
|
+
var import_fs10 = require("fs");
|
|
2018
|
+
var path16 = __toESM(require("path"), 1);
|
|
1597
2019
|
|
|
1598
2020
|
// src/indexer/index.ts
|
|
1599
|
-
var
|
|
1600
|
-
var
|
|
2021
|
+
var import_fs8 = require("fs");
|
|
2022
|
+
var path13 = __toESM(require("path"), 1);
|
|
1601
2023
|
var import_perf_hooks = require("perf_hooks");
|
|
1602
2024
|
var import_child_process3 = require("child_process");
|
|
1603
2025
|
var import_util3 = require("util");
|
|
@@ -2647,17 +3069,17 @@ async function pRetry(input, options = {}) {
|
|
|
2647
3069
|
}
|
|
2648
3070
|
|
|
2649
3071
|
// src/embeddings/detector.ts
|
|
2650
|
-
var
|
|
2651
|
-
var
|
|
2652
|
-
var
|
|
3072
|
+
var import_fs6 = require("fs");
|
|
3073
|
+
var path8 = __toESM(require("path"), 1);
|
|
3074
|
+
var os3 = __toESM(require("os"), 1);
|
|
2653
3075
|
function getOpenCodeAuthPath() {
|
|
2654
|
-
return
|
|
3076
|
+
return path8.join(os3.homedir(), ".local", "share", "opencode", "auth.json");
|
|
2655
3077
|
}
|
|
2656
3078
|
function loadOpenCodeAuth() {
|
|
2657
3079
|
const authPath = getOpenCodeAuthPath();
|
|
2658
3080
|
try {
|
|
2659
|
-
if ((0,
|
|
2660
|
-
return JSON.parse((0,
|
|
3081
|
+
if ((0, import_fs6.existsSync)(authPath)) {
|
|
3082
|
+
return JSON.parse((0, import_fs6.readFileSync)(authPath, "utf-8"));
|
|
2661
3083
|
}
|
|
2662
3084
|
} catch {
|
|
2663
3085
|
}
|
|
@@ -2879,17 +3301,17 @@ function validateExternalUrl(urlString) {
|
|
|
2879
3301
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
2880
3302
|
return { valid: false, reason: `Blocked protocol: ${parsed.protocol}` };
|
|
2881
3303
|
}
|
|
2882
|
-
const
|
|
2883
|
-
if (BLOCKED_HOSTNAMES.has(
|
|
2884
|
-
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})` };
|
|
2885
3307
|
}
|
|
2886
3308
|
for (const pattern of BLOCKED_METADATA_IPS) {
|
|
2887
|
-
if (pattern.test(
|
|
2888
|
-
return { valid: false, reason: `Blocked: cloud metadata IP (${
|
|
3309
|
+
if (pattern.test(hostname2)) {
|
|
3310
|
+
return { valid: false, reason: `Blocked: cloud metadata IP (${hostname2})` };
|
|
2889
3311
|
}
|
|
2890
3312
|
}
|
|
2891
|
-
if (/^169\.254\./.test(
|
|
2892
|
-
return { valid: false, reason: `Blocked: link-local address (${
|
|
3313
|
+
if (/^169\.254\./.test(hostname2)) {
|
|
3314
|
+
return { valid: false, reason: `Blocked: link-local address (${hostname2})` };
|
|
2893
3315
|
}
|
|
2894
3316
|
return { valid: true };
|
|
2895
3317
|
}
|
|
@@ -3360,8 +3782,8 @@ var SiliconFlowReranker = class {
|
|
|
3360
3782
|
|
|
3361
3783
|
// src/utils/files.ts
|
|
3362
3784
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
3363
|
-
var
|
|
3364
|
-
var
|
|
3785
|
+
var import_fs7 = require("fs");
|
|
3786
|
+
var path9 = __toESM(require("path"), 1);
|
|
3365
3787
|
var PROJECT_MARKERS = [
|
|
3366
3788
|
".git",
|
|
3367
3789
|
"package.json",
|
|
@@ -3381,7 +3803,7 @@ var PROJECT_MARKERS = [
|
|
|
3381
3803
|
];
|
|
3382
3804
|
function hasProjectMarker(projectRoot) {
|
|
3383
3805
|
for (const marker of PROJECT_MARKERS) {
|
|
3384
|
-
if ((0,
|
|
3806
|
+
if ((0, import_fs7.existsSync)(path9.join(projectRoot, marker))) {
|
|
3385
3807
|
return true;
|
|
3386
3808
|
}
|
|
3387
3809
|
}
|
|
@@ -3408,16 +3830,16 @@ function createIgnoreFilter(projectRoot) {
|
|
|
3408
3830
|
"**/*build*/**"
|
|
3409
3831
|
];
|
|
3410
3832
|
ig.add(defaultIgnores);
|
|
3411
|
-
const gitignorePath =
|
|
3412
|
-
if ((0,
|
|
3413
|
-
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");
|
|
3414
3836
|
ig.add(gitignoreContent);
|
|
3415
3837
|
}
|
|
3416
3838
|
return ig;
|
|
3417
3839
|
}
|
|
3418
3840
|
function shouldIncludeFile(filePath, projectRoot, includePatterns, excludePatterns, ignoreFilter) {
|
|
3419
|
-
const relativePath =
|
|
3420
|
-
if (hasFilteredPathSegment(relativePath,
|
|
3841
|
+
const relativePath = path9.relative(projectRoot, filePath);
|
|
3842
|
+
if (hasFilteredPathSegment(relativePath, path9.sep)) {
|
|
3421
3843
|
return false;
|
|
3422
3844
|
}
|
|
3423
3845
|
if (ignoreFilter.ignores(relativePath)) {
|
|
@@ -3451,12 +3873,12 @@ function matchGlob(filePath, pattern) {
|
|
|
3451
3873
|
return regex.test(filePath);
|
|
3452
3874
|
}
|
|
3453
3875
|
async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns, ignoreFilter, maxFileSize, skipped, options, currentDepth = 0) {
|
|
3454
|
-
const entries = await
|
|
3876
|
+
const entries = await import_fs7.promises.readdir(dir, { withFileTypes: true });
|
|
3455
3877
|
const filesInDir = [];
|
|
3456
3878
|
const subdirs = [];
|
|
3457
3879
|
for (const entry of entries) {
|
|
3458
|
-
const fullPath =
|
|
3459
|
-
const relativePath =
|
|
3880
|
+
const fullPath = path9.join(dir, entry.name);
|
|
3881
|
+
const relativePath = path9.relative(projectRoot, fullPath);
|
|
3460
3882
|
if (isHiddenPathSegment(entry.name)) {
|
|
3461
3883
|
if (entry.isDirectory()) {
|
|
3462
3884
|
skipped.push({ path: relativePath, reason: "excluded" });
|
|
@@ -3476,7 +3898,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
|
|
|
3476
3898
|
if (entry.isDirectory()) {
|
|
3477
3899
|
subdirs.push({ fullPath, relativePath });
|
|
3478
3900
|
} else if (entry.isFile()) {
|
|
3479
|
-
const stat4 = await
|
|
3901
|
+
const stat4 = await import_fs7.promises.stat(fullPath);
|
|
3480
3902
|
if (stat4.size > maxFileSize) {
|
|
3481
3903
|
skipped.push({ path: relativePath, reason: "too_large" });
|
|
3482
3904
|
continue;
|
|
@@ -3505,7 +3927,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
|
|
|
3505
3927
|
yield f;
|
|
3506
3928
|
}
|
|
3507
3929
|
for (let i = options.maxFilesPerDirectory; i < filesInDir.length; i++) {
|
|
3508
|
-
skipped.push({ path:
|
|
3930
|
+
skipped.push({ path: path9.relative(projectRoot, filesInDir[i].path), reason: "excluded" });
|
|
3509
3931
|
}
|
|
3510
3932
|
const canRecurse = options.maxDepth === -1 || currentDepth < options.maxDepth;
|
|
3511
3933
|
if (canRecurse) {
|
|
@@ -3545,14 +3967,14 @@ async function collectFiles(projectRoot, includePatterns, excludePatterns, maxFi
|
|
|
3545
3967
|
if (additionalRoots && additionalRoots.length > 0) {
|
|
3546
3968
|
const normalizedRoots = /* @__PURE__ */ new Set();
|
|
3547
3969
|
for (const kbRoot of additionalRoots) {
|
|
3548
|
-
const resolved =
|
|
3549
|
-
|
|
3970
|
+
const resolved = path9.normalize(
|
|
3971
|
+
path9.isAbsolute(kbRoot) ? kbRoot : path9.resolve(projectRoot, kbRoot)
|
|
3550
3972
|
);
|
|
3551
3973
|
normalizedRoots.add(resolved);
|
|
3552
3974
|
}
|
|
3553
3975
|
for (const resolvedKbRoot of normalizedRoots) {
|
|
3554
3976
|
try {
|
|
3555
|
-
const stat4 = await
|
|
3977
|
+
const stat4 = await import_fs7.promises.stat(resolvedKbRoot);
|
|
3556
3978
|
if (!stat4.isDirectory()) {
|
|
3557
3979
|
skipped.push({ path: resolvedKbRoot, reason: "excluded" });
|
|
3558
3980
|
continue;
|
|
@@ -3953,14 +4375,14 @@ function initializeLogger(config) {
|
|
|
3953
4375
|
}
|
|
3954
4376
|
|
|
3955
4377
|
// src/native/index.ts
|
|
3956
|
-
var
|
|
3957
|
-
var
|
|
4378
|
+
var path10 = __toESM(require("path"), 1);
|
|
4379
|
+
var os4 = __toESM(require("os"), 1);
|
|
3958
4380
|
var module2 = __toESM(require("module"), 1);
|
|
3959
4381
|
var import_url = require("url");
|
|
3960
4382
|
var import_meta = {};
|
|
3961
4383
|
function getNativeBinding() {
|
|
3962
|
-
const platform2 =
|
|
3963
|
-
const arch2 =
|
|
4384
|
+
const platform2 = os4.platform();
|
|
4385
|
+
const arch2 = os4.arch();
|
|
3964
4386
|
let bindingName;
|
|
3965
4387
|
if (platform2 === "darwin" && arch2 === "arm64") {
|
|
3966
4388
|
bindingName = "codebase-index-native.darwin-arm64.node";
|
|
@@ -3978,19 +4400,19 @@ function getNativeBinding() {
|
|
|
3978
4400
|
let currentDir;
|
|
3979
4401
|
let requireTarget;
|
|
3980
4402
|
if (typeof import_meta !== "undefined" && import_meta.url) {
|
|
3981
|
-
currentDir =
|
|
4403
|
+
currentDir = path10.dirname((0, import_url.fileURLToPath)(import_meta.url));
|
|
3982
4404
|
requireTarget = import_meta.url;
|
|
3983
4405
|
} else if (typeof __dirname !== "undefined") {
|
|
3984
4406
|
currentDir = __dirname;
|
|
3985
4407
|
requireTarget = __filename;
|
|
3986
4408
|
} else {
|
|
3987
4409
|
currentDir = process.cwd();
|
|
3988
|
-
requireTarget =
|
|
4410
|
+
requireTarget = path10.join(currentDir, "index.js");
|
|
3989
4411
|
}
|
|
3990
4412
|
const normalizedDir = currentDir.replace(/\\/g, "/");
|
|
3991
|
-
const isDevMode = normalizedDir.includes("/src/native") || currentDir.includes(
|
|
3992
|
-
const packageRoot = isDevMode ?
|
|
3993
|
-
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);
|
|
3994
4416
|
const require2 = module2.createRequire(requireTarget);
|
|
3995
4417
|
return require2(nativePath);
|
|
3996
4418
|
}
|
|
@@ -4032,6 +4454,12 @@ function createMockNativeBinding() {
|
|
|
4032
4454
|
constructor() {
|
|
4033
4455
|
throw error;
|
|
4034
4456
|
}
|
|
4457
|
+
static openReadOnly() {
|
|
4458
|
+
throw error;
|
|
4459
|
+
}
|
|
4460
|
+
static createEmptyReadOnly() {
|
|
4461
|
+
throw error;
|
|
4462
|
+
}
|
|
4035
4463
|
close() {
|
|
4036
4464
|
throw error;
|
|
4037
4465
|
}
|
|
@@ -4135,6 +4563,12 @@ var VectorStore = class {
|
|
|
4135
4563
|
load() {
|
|
4136
4564
|
this.inner.load();
|
|
4137
4565
|
}
|
|
4566
|
+
loadStrict() {
|
|
4567
|
+
this.inner.loadStrict();
|
|
4568
|
+
}
|
|
4569
|
+
hasFingerprint() {
|
|
4570
|
+
return this.inner.hasFingerprint();
|
|
4571
|
+
}
|
|
4138
4572
|
count() {
|
|
4139
4573
|
return this.inner.count();
|
|
4140
4574
|
}
|
|
@@ -4417,12 +4851,24 @@ var InvertedIndex = class {
|
|
|
4417
4851
|
return this.inner.documentCount();
|
|
4418
4852
|
}
|
|
4419
4853
|
};
|
|
4420
|
-
var Database = class {
|
|
4854
|
+
var Database = class _Database {
|
|
4421
4855
|
inner;
|
|
4422
4856
|
closed = false;
|
|
4423
4857
|
constructor(dbPath) {
|
|
4424
4858
|
this.inner = new native.Database(dbPath);
|
|
4425
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
|
+
}
|
|
4426
4872
|
throwIfClosed() {
|
|
4427
4873
|
if (this.closed) {
|
|
4428
4874
|
throw new Error("Database is closed");
|
|
@@ -4699,7 +5145,7 @@ var Database = class {
|
|
|
4699
5145
|
};
|
|
4700
5146
|
|
|
4701
5147
|
// src/tools/changed-files.ts
|
|
4702
|
-
var
|
|
5148
|
+
var path11 = __toESM(require("path"), 1);
|
|
4703
5149
|
var import_child_process = require("child_process");
|
|
4704
5150
|
var import_util = require("util");
|
|
4705
5151
|
var execFileAsync = (0, import_util.promisify)(import_child_process.execFile);
|
|
@@ -4787,8 +5233,8 @@ function normalizeFiles(rawFiles, projectRoot) {
|
|
|
4787
5233
|
for (const raw of rawFiles) {
|
|
4788
5234
|
const trimmed = raw.trim();
|
|
4789
5235
|
if (!trimmed) continue;
|
|
4790
|
-
const absolute =
|
|
4791
|
-
const relative11 =
|
|
5236
|
+
const absolute = path11.resolve(projectRoot, trimmed);
|
|
5237
|
+
const relative11 = path11.relative(projectRoot, absolute);
|
|
4792
5238
|
const cleaned = relative11.startsWith("./") ? relative11.slice(2) : relative11;
|
|
4793
5239
|
if (!seen.has(cleaned)) {
|
|
4794
5240
|
seen.add(cleaned);
|
|
@@ -4800,7 +5246,7 @@ function normalizeFiles(rawFiles, projectRoot) {
|
|
|
4800
5246
|
|
|
4801
5247
|
// src/indexer/git-blame.ts
|
|
4802
5248
|
var import_child_process2 = require("child_process");
|
|
4803
|
-
var
|
|
5249
|
+
var path12 = __toESM(require("path"), 1);
|
|
4804
5250
|
var import_util2 = require("util");
|
|
4805
5251
|
var execFileAsync2 = (0, import_util2.promisify)(import_child_process2.execFile);
|
|
4806
5252
|
function parseGitBlamePorcelain(output) {
|
|
@@ -4838,7 +5284,7 @@ function parseGitBlamePorcelain(output) {
|
|
|
4838
5284
|
return Array.from(commits.values()).filter((commit) => commit.lines > 0).sort((a, b) => b.lines - a.lines || b.committedAt - a.committedAt)[0];
|
|
4839
5285
|
}
|
|
4840
5286
|
async function getChunkGitBlame(projectRoot, filePath, startLine, endLine) {
|
|
4841
|
-
const relativePath =
|
|
5287
|
+
const relativePath = path12.relative(projectRoot, filePath);
|
|
4842
5288
|
try {
|
|
4843
5289
|
const { stdout } = await execFileAsync2(
|
|
4844
5290
|
"git",
|
|
@@ -4932,6 +5378,7 @@ function isSqliteCorruptionError(error) {
|
|
|
4932
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");
|
|
4933
5379
|
}
|
|
4934
5380
|
var STARTUP_WARNING_METADATA_KEY = "index.startupWarning";
|
|
5381
|
+
var READER_ARTIFACT_RETRY_INTERVAL_MS = 1e3;
|
|
4935
5382
|
function metadataFromBlame(blame) {
|
|
4936
5383
|
if (!blame) {
|
|
4937
5384
|
return {};
|
|
@@ -5129,9 +5576,9 @@ function hasAllEmbeddingParts(parts, expectedPartCount) {
|
|
|
5129
5576
|
return true;
|
|
5130
5577
|
}
|
|
5131
5578
|
function isPathWithinRoot(filePath, rootPath) {
|
|
5132
|
-
const normalizedFilePath =
|
|
5133
|
-
const normalizedRoot =
|
|
5134
|
-
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}`);
|
|
5135
5582
|
}
|
|
5136
5583
|
var rankingQueryTokenCache = /* @__PURE__ */ new Map();
|
|
5137
5584
|
var rankingNameTokenCache = /* @__PURE__ */ new Map();
|
|
@@ -6197,26 +6644,133 @@ var Indexer = class {
|
|
|
6197
6644
|
queryCacheTtlMs = 5 * 60 * 1e3;
|
|
6198
6645
|
querySimilarityThreshold = 0.85;
|
|
6199
6646
|
indexCompatibility = null;
|
|
6200
|
-
|
|
6647
|
+
activeIndexLease = null;
|
|
6648
|
+
initializationPromise = null;
|
|
6649
|
+
initializationMode = "none";
|
|
6650
|
+
readIssues = [];
|
|
6651
|
+
retiredDatabases = [];
|
|
6652
|
+
readerArtifactFingerprint = null;
|
|
6653
|
+
writerArtifactFingerprint = null;
|
|
6654
|
+
readerArtifactRetryAfter = /* @__PURE__ */ new Map();
|
|
6201
6655
|
constructor(projectRoot, config, host = "opencode") {
|
|
6202
6656
|
this.projectRoot = projectRoot;
|
|
6203
6657
|
this.config = config;
|
|
6204
6658
|
this.host = host;
|
|
6205
6659
|
this.indexPath = this.getIndexPath();
|
|
6206
|
-
this.fileHashCachePath =
|
|
6207
|
-
this.failedBatchesPath =
|
|
6208
|
-
this.indexingLockPath = path12.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");
|
|
6209
6662
|
this.logger = initializeLogger(config.debug);
|
|
6210
6663
|
}
|
|
6211
6664
|
getIndexPath() {
|
|
6212
6665
|
return resolveProjectIndexPath(this.projectRoot, this.config.scope, this.host);
|
|
6213
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
|
+
}
|
|
6214
6768
|
loadFileHashCache() {
|
|
6215
|
-
if (!(0,
|
|
6769
|
+
if (!(0, import_fs8.existsSync)(this.fileHashCachePath)) {
|
|
6216
6770
|
return;
|
|
6217
6771
|
}
|
|
6218
6772
|
try {
|
|
6219
|
-
const data = (0,
|
|
6773
|
+
const data = (0, import_fs8.readFileSync)(this.fileHashCachePath, "utf-8");
|
|
6220
6774
|
const parsed = JSON.parse(data);
|
|
6221
6775
|
this.fileHashCache = new Map(Object.entries(parsed));
|
|
6222
6776
|
} catch (error) {
|
|
@@ -6236,15 +6790,26 @@ var Indexer = class {
|
|
|
6236
6790
|
this.atomicWriteSync(this.fileHashCachePath, JSON.stringify(obj));
|
|
6237
6791
|
}
|
|
6238
6792
|
atomicWriteSync(targetPath, data) {
|
|
6239
|
-
const
|
|
6240
|
-
(
|
|
6241
|
-
(0,
|
|
6242
|
-
|
|
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
|
+
);
|
|
6243
6808
|
}
|
|
6244
6809
|
getScopedRoots() {
|
|
6245
|
-
const roots = /* @__PURE__ */ new Set([
|
|
6810
|
+
const roots = /* @__PURE__ */ new Set([path13.resolve(this.projectRoot)]);
|
|
6246
6811
|
for (const kbRoot of this.config.knowledgeBases) {
|
|
6247
|
-
roots.add(
|
|
6812
|
+
roots.add(path13.resolve(this.projectRoot, kbRoot));
|
|
6248
6813
|
}
|
|
6249
6814
|
return Array.from(roots);
|
|
6250
6815
|
}
|
|
@@ -6253,29 +6818,29 @@ var Indexer = class {
|
|
|
6253
6818
|
if (this.config.scope !== "global") {
|
|
6254
6819
|
return branchName;
|
|
6255
6820
|
}
|
|
6256
|
-
const projectHash = hashContent(
|
|
6821
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6257
6822
|
return `${projectHash}:${branchName}`;
|
|
6258
6823
|
}
|
|
6259
6824
|
getBranchCatalogKeyFor(branchName) {
|
|
6260
6825
|
if (this.config.scope !== "global") {
|
|
6261
6826
|
return branchName;
|
|
6262
6827
|
}
|
|
6263
|
-
const projectHash = hashContent(
|
|
6828
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6264
6829
|
return `${projectHash}:${branchName}`;
|
|
6265
6830
|
}
|
|
6266
6831
|
getLegacyBranchCatalogKey() {
|
|
6267
6832
|
return this.currentBranch || "default";
|
|
6268
6833
|
}
|
|
6269
6834
|
getLegacyMigrationMetadataKey() {
|
|
6270
|
-
const projectHash = hashContent(
|
|
6835
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6271
6836
|
return `index.globalBranchMigration.${projectHash}`;
|
|
6272
6837
|
}
|
|
6273
6838
|
getProjectEmbeddingStrategyMetadataKey() {
|
|
6274
|
-
const projectHash = hashContent(
|
|
6839
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6275
6840
|
return `index.embeddingStrategyVersion.${projectHash}`;
|
|
6276
6841
|
}
|
|
6277
6842
|
getProjectForceReembedMetadataKey() {
|
|
6278
|
-
const projectHash = hashContent(
|
|
6843
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6279
6844
|
return `index.forceReembed.${projectHash}`;
|
|
6280
6845
|
}
|
|
6281
6846
|
hasProjectForceReembedPending() {
|
|
@@ -6369,7 +6934,7 @@ var Indexer = class {
|
|
|
6369
6934
|
if (!this.database) {
|
|
6370
6935
|
return { chunkIds, symbolIds };
|
|
6371
6936
|
}
|
|
6372
|
-
const projectRootPath =
|
|
6937
|
+
const projectRootPath = path13.resolve(this.projectRoot);
|
|
6373
6938
|
const projectLocalFilePaths = /* @__PURE__ */ new Set([
|
|
6374
6939
|
...Array.from(this.fileHashCache.keys()).filter(
|
|
6375
6940
|
(filePath) => this.isFileInCurrentScope(filePath, roots) && isPathWithinRoot(filePath, projectRootPath)
|
|
@@ -6392,7 +6957,7 @@ var Indexer = class {
|
|
|
6392
6957
|
if (this.config.scope !== "global") {
|
|
6393
6958
|
return this.getBranchCatalogCleanupKeys();
|
|
6394
6959
|
}
|
|
6395
|
-
const projectHash = hashContent(
|
|
6960
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6396
6961
|
const keys = /* @__PURE__ */ new Set();
|
|
6397
6962
|
const projectChunkIdSet = new Set(projectChunkIds);
|
|
6398
6963
|
const projectSymbolIdSet = new Set(projectSymbolIds);
|
|
@@ -6476,7 +7041,7 @@ var Indexer = class {
|
|
|
6476
7041
|
if (!this.database || this.config.scope !== "global") {
|
|
6477
7042
|
return false;
|
|
6478
7043
|
}
|
|
6479
|
-
const projectHash = hashContent(
|
|
7044
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6480
7045
|
const roots = this.getScopedRoots();
|
|
6481
7046
|
const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots);
|
|
6482
7047
|
return this.database.getAllBranches().some(
|
|
@@ -6510,7 +7075,7 @@ var Indexer = class {
|
|
|
6510
7075
|
...Array.from(this.fileHashCache.keys()).filter((filePath) => this.isFileInCurrentScope(filePath, roots)),
|
|
6511
7076
|
...scopedEntries.map(({ metadata }) => metadata.filePath)
|
|
6512
7077
|
]);
|
|
6513
|
-
const projectRootPath =
|
|
7078
|
+
const projectRootPath = path13.resolve(this.projectRoot);
|
|
6514
7079
|
const projectLocalFilePaths = new Set(
|
|
6515
7080
|
Array.from(filePaths).filter((filePath) => isPathWithinRoot(filePath, projectRootPath))
|
|
6516
7081
|
);
|
|
@@ -6572,34 +7137,27 @@ var Indexer = class {
|
|
|
6572
7137
|
database.gcOrphanEmbeddings();
|
|
6573
7138
|
database.gcOrphanChunks();
|
|
6574
7139
|
store.save();
|
|
6575
|
-
|
|
7140
|
+
this.saveInvertedIndex(invertedIndex);
|
|
6576
7141
|
return {
|
|
6577
7142
|
removedChunkIds: removedChunkIdList,
|
|
6578
7143
|
hasForeignData: allMetadata.some(({ metadata }) => !this.isFileInCurrentScope(metadata.filePath, roots))
|
|
6579
7144
|
};
|
|
6580
7145
|
}
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
(0, import_fs7.writeFileSync)(this.indexingLockPath, JSON.stringify(lockData));
|
|
6590
|
-
}
|
|
6591
|
-
releaseIndexingLock() {
|
|
6592
|
-
if ((0, import_fs7.existsSync)(this.indexingLockPath)) {
|
|
6593
|
-
(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
|
+
});
|
|
6594
7154
|
}
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
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();
|
|
6600
7160
|
}
|
|
6601
|
-
await this.healthCheck();
|
|
6602
|
-
this.releaseIndexingLock();
|
|
6603
7161
|
this.logger.info("Recovery complete, next index will re-process all files");
|
|
6604
7162
|
}
|
|
6605
7163
|
loadFailedBatches(maxChunkTokens) {
|
|
@@ -6615,10 +7173,10 @@ var Indexer = class {
|
|
|
6615
7173
|
}
|
|
6616
7174
|
}
|
|
6617
7175
|
loadSerializedFailedBatches() {
|
|
6618
|
-
if (!(0,
|
|
7176
|
+
if (!(0, import_fs8.existsSync)(this.failedBatchesPath)) {
|
|
6619
7177
|
return [];
|
|
6620
7178
|
}
|
|
6621
|
-
const data = (0,
|
|
7179
|
+
const data = (0, import_fs8.readFileSync)(this.failedBatchesPath, "utf-8");
|
|
6622
7180
|
const parsed = JSON.parse(data);
|
|
6623
7181
|
return parsed.map((batch) => {
|
|
6624
7182
|
const chunks = Array.isArray(batch.chunks) ? batch.chunks : [];
|
|
@@ -6635,15 +7193,15 @@ var Indexer = class {
|
|
|
6635
7193
|
}
|
|
6636
7194
|
saveFailedBatches(batches) {
|
|
6637
7195
|
if (batches.length === 0) {
|
|
6638
|
-
if ((0,
|
|
7196
|
+
if ((0, import_fs8.existsSync)(this.failedBatchesPath)) {
|
|
6639
7197
|
try {
|
|
6640
|
-
(0,
|
|
7198
|
+
(0, import_fs8.unlinkSync)(this.failedBatchesPath);
|
|
6641
7199
|
} catch {
|
|
6642
7200
|
}
|
|
6643
7201
|
}
|
|
6644
7202
|
return;
|
|
6645
7203
|
}
|
|
6646
|
-
|
|
7204
|
+
this.atomicWriteSync(this.failedBatchesPath, JSON.stringify(batches, null, 2));
|
|
6647
7205
|
}
|
|
6648
7206
|
collectRetryableFailedChunks(currentFileHashes, unchangedFilePaths, maxChunkTokens) {
|
|
6649
7207
|
const retryableById = /* @__PURE__ */ new Map();
|
|
@@ -6823,7 +7381,7 @@ var Indexer = class {
|
|
|
6823
7381
|
const intent = isLikelyImplementationPath(candidate.metadata.filePath) ? "implementation" : "doc_or_test";
|
|
6824
7382
|
parts.push(`intent_hint: ${intent}`);
|
|
6825
7383
|
try {
|
|
6826
|
-
const fileContent = await
|
|
7384
|
+
const fileContent = await import_fs8.promises.readFile(candidate.metadata.filePath, "utf-8");
|
|
6827
7385
|
const lines = fileContent.split("\n");
|
|
6828
7386
|
const snippetStartLine = Math.max(1, candidate.metadata.startLine - 2);
|
|
6829
7387
|
const snippetEndLine = Math.min(lines.length, candidate.metadata.endLine + 2);
|
|
@@ -6837,6 +7395,222 @@ var Indexer = class {
|
|
|
6837
7395
|
return parts.join("\n");
|
|
6838
7396
|
}
|
|
6839
7397
|
async initialize() {
|
|
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();
|
|
6840
7614
|
if (this.config.embeddingProvider === "custom") {
|
|
6841
7615
|
if (!this.config.customProvider) {
|
|
6842
7616
|
throw new Error("embeddingProvider is 'custom' but customProvider config is missing.");
|
|
@@ -6868,36 +7642,103 @@ var Indexer = class {
|
|
|
6868
7642
|
});
|
|
6869
7643
|
}
|
|
6870
7644
|
}
|
|
6871
|
-
await import_fs7.promises.mkdir(this.indexPath, { recursive: true });
|
|
6872
7645
|
const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
|
|
6873
|
-
const storePath =
|
|
6874
|
-
|
|
6875
|
-
const
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
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();
|
|
6886
7671
|
}
|
|
6887
7672
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
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);
|
|
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;
|
|
6896
7691
|
}
|
|
7692
|
+
} else {
|
|
6897
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
|
+
}
|
|
6898
7707
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
6899
|
-
|
|
6900
|
-
|
|
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
|
+
}
|
|
6901
7742
|
}
|
|
6902
7743
|
if (isGitRepo(this.projectRoot)) {
|
|
6903
7744
|
this.currentBranch = getBranchOrDefault(this.projectRoot);
|
|
@@ -6911,10 +7752,10 @@ var Indexer = class {
|
|
|
6911
7752
|
this.baseBranch = "default";
|
|
6912
7753
|
this.logger.branch("debug", "Not a git repository, using default branch");
|
|
6913
7754
|
}
|
|
6914
|
-
if (
|
|
6915
|
-
await this.
|
|
7755
|
+
if (mode === "writer" && recoveredOwners.length > 0) {
|
|
7756
|
+
await this.recoverFromInterruptedIndexingUnlocked(recoveredOwners);
|
|
6916
7757
|
}
|
|
6917
|
-
if (dbIsNew && this.store.count() > 0) {
|
|
7758
|
+
if (mode === "writer" && dbIsNew && this.store.count() > 0) {
|
|
6918
7759
|
this.migrateFromLegacyIndex();
|
|
6919
7760
|
}
|
|
6920
7761
|
this.loadFileHashCache();
|
|
@@ -6926,9 +7767,11 @@ var Indexer = class {
|
|
|
6926
7767
|
configuredProviderInfo: this.configuredProviderInfo
|
|
6927
7768
|
});
|
|
6928
7769
|
}
|
|
6929
|
-
if (this.config.indexing.autoGc) {
|
|
7770
|
+
if (mode === "writer" && this.config.indexing.autoGc && !options.skipAutoGc) {
|
|
6930
7771
|
await this.maybeRunAutoGc();
|
|
6931
7772
|
}
|
|
7773
|
+
this.initializationMode = mode;
|
|
7774
|
+
this.readerArtifactFingerprint = readerArtifactFingerprint;
|
|
6932
7775
|
}
|
|
6933
7776
|
async maybeRunAutoGc() {
|
|
6934
7777
|
if (!this.database) return;
|
|
@@ -6945,7 +7788,7 @@ var Indexer = class {
|
|
|
6945
7788
|
}
|
|
6946
7789
|
}
|
|
6947
7790
|
if (shouldRunGc) {
|
|
6948
|
-
const result = await this.
|
|
7791
|
+
const result = await this.healthCheckUnlocked();
|
|
6949
7792
|
if (result.warning) {
|
|
6950
7793
|
this.database.setMetadata(STARTUP_WARNING_METADATA_KEY, result.warning);
|
|
6951
7794
|
} else {
|
|
@@ -6967,7 +7810,7 @@ var Indexer = class {
|
|
|
6967
7810
|
if (await this.tryResetCorruptedIndex("running automatic orphan garbage collection", error)) {
|
|
6968
7811
|
return {
|
|
6969
7812
|
resetCorruptedIndex: true,
|
|
6970
|
-
warning: this.getCorruptedIndexWarning(
|
|
7813
|
+
warning: this.getCorruptedIndexWarning(path13.join(this.indexPath, "codebase.db"))
|
|
6971
7814
|
};
|
|
6972
7815
|
}
|
|
6973
7816
|
throw error;
|
|
@@ -6982,28 +7825,29 @@ var Indexer = class {
|
|
|
6982
7825
|
return;
|
|
6983
7826
|
}
|
|
6984
7827
|
const retainedEntries = store.getAllMetadata().filter(({ key }) => !excludedSet.has(key));
|
|
6985
|
-
const storeBasePath =
|
|
6986
|
-
const storeIndexPath =
|
|
7828
|
+
const storeBasePath = path13.join(this.indexPath, "vectors");
|
|
7829
|
+
const storeIndexPath = storeBasePath;
|
|
6987
7830
|
const storeMetadataPath = `${storeBasePath}.meta.json`;
|
|
6988
|
-
const
|
|
6989
|
-
const
|
|
7831
|
+
const lease = this.requireActiveLease();
|
|
7832
|
+
const backupIndexPath = createLeaseTemporaryPath(storeIndexPath, lease.owner, "bak");
|
|
7833
|
+
const backupMetadataPath = createLeaseTemporaryPath(storeMetadataPath, lease.owner, "bak");
|
|
6990
7834
|
let backedUpIndex = false;
|
|
6991
7835
|
let backedUpMetadata = false;
|
|
6992
7836
|
let rebuiltCount = 0;
|
|
6993
7837
|
let skippedCount = 0;
|
|
6994
|
-
if ((0,
|
|
6995
|
-
(0,
|
|
7838
|
+
if ((0, import_fs8.existsSync)(backupIndexPath)) {
|
|
7839
|
+
(0, import_fs8.unlinkSync)(backupIndexPath);
|
|
6996
7840
|
}
|
|
6997
|
-
if ((0,
|
|
6998
|
-
(0,
|
|
7841
|
+
if ((0, import_fs8.existsSync)(backupMetadataPath)) {
|
|
7842
|
+
(0, import_fs8.unlinkSync)(backupMetadataPath);
|
|
6999
7843
|
}
|
|
7000
7844
|
try {
|
|
7001
|
-
if ((0,
|
|
7002
|
-
(0,
|
|
7845
|
+
if ((0, import_fs8.existsSync)(storeIndexPath)) {
|
|
7846
|
+
(0, import_fs8.renameSync)(storeIndexPath, backupIndexPath);
|
|
7003
7847
|
backedUpIndex = true;
|
|
7004
7848
|
}
|
|
7005
|
-
if ((0,
|
|
7006
|
-
(0,
|
|
7849
|
+
if ((0, import_fs8.existsSync)(storeMetadataPath)) {
|
|
7850
|
+
(0, import_fs8.renameSync)(storeMetadataPath, backupMetadataPath);
|
|
7007
7851
|
backedUpMetadata = true;
|
|
7008
7852
|
}
|
|
7009
7853
|
store.clear();
|
|
@@ -7023,11 +7867,11 @@ var Indexer = class {
|
|
|
7023
7867
|
rebuiltCount += 1;
|
|
7024
7868
|
}
|
|
7025
7869
|
store.save();
|
|
7026
|
-
if (backedUpIndex && (0,
|
|
7027
|
-
(0,
|
|
7870
|
+
if (backedUpIndex && (0, import_fs8.existsSync)(backupIndexPath)) {
|
|
7871
|
+
(0, import_fs8.unlinkSync)(backupIndexPath);
|
|
7028
7872
|
}
|
|
7029
|
-
if (backedUpMetadata && (0,
|
|
7030
|
-
(0,
|
|
7873
|
+
if (backedUpMetadata && (0, import_fs8.existsSync)(backupMetadataPath)) {
|
|
7874
|
+
(0, import_fs8.unlinkSync)(backupMetadataPath);
|
|
7031
7875
|
}
|
|
7032
7876
|
this.logger.gc("info", "Rebuilt vector store to avoid native remove", {
|
|
7033
7877
|
excludedChunks: excludedSet.size,
|
|
@@ -7039,17 +7883,17 @@ var Indexer = class {
|
|
|
7039
7883
|
store.clear();
|
|
7040
7884
|
} catch {
|
|
7041
7885
|
}
|
|
7042
|
-
if ((0,
|
|
7043
|
-
(0,
|
|
7886
|
+
if ((0, import_fs8.existsSync)(storeIndexPath)) {
|
|
7887
|
+
(0, import_fs8.unlinkSync)(storeIndexPath);
|
|
7044
7888
|
}
|
|
7045
|
-
if ((0,
|
|
7046
|
-
(0,
|
|
7889
|
+
if ((0, import_fs8.existsSync)(storeMetadataPath)) {
|
|
7890
|
+
(0, import_fs8.unlinkSync)(storeMetadataPath);
|
|
7047
7891
|
}
|
|
7048
|
-
if (backedUpIndex && (0,
|
|
7049
|
-
(0,
|
|
7892
|
+
if (backedUpIndex && (0, import_fs8.existsSync)(backupIndexPath)) {
|
|
7893
|
+
(0, import_fs8.renameSync)(backupIndexPath, storeIndexPath);
|
|
7050
7894
|
}
|
|
7051
|
-
if (backedUpMetadata && (0,
|
|
7052
|
-
(0,
|
|
7895
|
+
if (backedUpMetadata && (0, import_fs8.existsSync)(backupMetadataPath)) {
|
|
7896
|
+
(0, import_fs8.renameSync)(backupMetadataPath, storeMetadataPath);
|
|
7053
7897
|
}
|
|
7054
7898
|
if (backedUpIndex || backedUpMetadata) {
|
|
7055
7899
|
store.load();
|
|
@@ -7063,11 +7907,37 @@ var Indexer = class {
|
|
|
7063
7907
|
}
|
|
7064
7908
|
return `Detected a corrupted local SQLite index at ${dbPath} and reset the local index. Run index_codebase to rebuild search data.`;
|
|
7065
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
|
+
}
|
|
7066
7936
|
async tryResetCorruptedIndex(stage, error) {
|
|
7067
7937
|
if (!isSqliteCorruptionError(error)) {
|
|
7068
7938
|
return false;
|
|
7069
7939
|
}
|
|
7070
|
-
const dbPath =
|
|
7940
|
+
const dbPath = path13.join(this.indexPath, "codebase.db");
|
|
7071
7941
|
const warning = this.getCorruptedIndexWarning(dbPath);
|
|
7072
7942
|
const errorMessage = getErrorMessage2(error);
|
|
7073
7943
|
if (this.config.scope === "global") {
|
|
@@ -7083,30 +7953,7 @@ var Indexer = class {
|
|
|
7083
7953
|
dbPath,
|
|
7084
7954
|
error: errorMessage
|
|
7085
7955
|
});
|
|
7086
|
-
this.
|
|
7087
|
-
this.invertedIndex = null;
|
|
7088
|
-
this.database?.close();
|
|
7089
|
-
this.database = null;
|
|
7090
|
-
this.indexCompatibility = null;
|
|
7091
|
-
this.fileHashCache.clear();
|
|
7092
|
-
const resetPaths = [
|
|
7093
|
-
path12.join(this.indexPath, "codebase.db"),
|
|
7094
|
-
path12.join(this.indexPath, "codebase.db-shm"),
|
|
7095
|
-
path12.join(this.indexPath, "codebase.db-wal"),
|
|
7096
|
-
path12.join(this.indexPath, "vectors.usearch"),
|
|
7097
|
-
path12.join(this.indexPath, "inverted-index.json"),
|
|
7098
|
-
path12.join(this.indexPath, "file-hashes.json"),
|
|
7099
|
-
path12.join(this.indexPath, "failed-batches.json"),
|
|
7100
|
-
path12.join(this.indexPath, "indexing.lock"),
|
|
7101
|
-
path12.join(this.indexPath, "vectors")
|
|
7102
|
-
];
|
|
7103
|
-
await Promise.all(resetPaths.map(async (targetPath) => {
|
|
7104
|
-
try {
|
|
7105
|
-
await import_fs7.promises.rm(targetPath, { recursive: true, force: true });
|
|
7106
|
-
} catch {
|
|
7107
|
-
}
|
|
7108
|
-
}));
|
|
7109
|
-
await import_fs7.promises.mkdir(this.indexPath, { recursive: true });
|
|
7956
|
+
await this.resetLocalIndexArtifacts();
|
|
7110
7957
|
return true;
|
|
7111
7958
|
}
|
|
7112
7959
|
migrateFromLegacyIndex() {
|
|
@@ -7225,8 +8072,62 @@ var Indexer = class {
|
|
|
7225
8072
|
return this.indexCompatibility;
|
|
7226
8073
|
}
|
|
7227
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() {
|
|
7228
8129
|
if (!this.store || !this.provider || !this.invertedIndex || !this.configuredProviderInfo || !this.database) {
|
|
7229
|
-
|
|
8130
|
+
throw new Error("Index state is not initialized");
|
|
7230
8131
|
}
|
|
7231
8132
|
return {
|
|
7232
8133
|
store: this.store,
|
|
@@ -7250,7 +8151,12 @@ var Indexer = class {
|
|
|
7250
8151
|
return createCostEstimate(files, configuredProviderInfo);
|
|
7251
8152
|
}
|
|
7252
8153
|
async index(onProgress) {
|
|
7253
|
-
|
|
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);
|
|
7254
8160
|
const scopedRoots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
7255
8161
|
const branchCatalogKey = this.getBranchCatalogKey();
|
|
7256
8162
|
const forceScopedReembed = scopedRoots !== null && database.getMetadata(this.getProjectForceReembedMetadataKey()) === "true";
|
|
@@ -7260,7 +8166,6 @@ var Indexer = class {
|
|
|
7260
8166
|
`${this.indexCompatibility?.reason} Run index_codebase with force=true to rebuild the index.`
|
|
7261
8167
|
);
|
|
7262
8168
|
}
|
|
7263
|
-
this.acquireIndexingLock();
|
|
7264
8169
|
this.logger.recordIndexingStart();
|
|
7265
8170
|
this.logger.info("Starting indexing", { projectRoot: this.projectRoot });
|
|
7266
8171
|
const startTime = Date.now();
|
|
@@ -7311,7 +8216,7 @@ var Indexer = class {
|
|
|
7311
8216
|
unchangedFilePaths.add(f.path);
|
|
7312
8217
|
this.logger.recordCacheHit();
|
|
7313
8218
|
} else {
|
|
7314
|
-
const content = await
|
|
8219
|
+
const content = await import_fs8.promises.readFile(f.path, "utf-8");
|
|
7315
8220
|
changedFiles.push({ path: f.path, content, hash: currentHash });
|
|
7316
8221
|
this.logger.recordCacheMiss();
|
|
7317
8222
|
}
|
|
@@ -7409,7 +8314,7 @@ var Indexer = class {
|
|
|
7409
8314
|
for (const parsed of parsedFiles) {
|
|
7410
8315
|
currentFilePaths.add(parsed.path);
|
|
7411
8316
|
if (parsed.chunks.length === 0) {
|
|
7412
|
-
const relativePath =
|
|
8317
|
+
const relativePath = path13.relative(this.projectRoot, parsed.path);
|
|
7413
8318
|
stats.parseFailures.push(relativePath);
|
|
7414
8319
|
}
|
|
7415
8320
|
let fileChunkCount = 0;
|
|
@@ -7609,7 +8514,9 @@ var Indexer = class {
|
|
|
7609
8514
|
database.addChunksToBranchBatch(branchCatalogKey, Array.from(currentChunkIds));
|
|
7610
8515
|
database.clearBranchSymbols(branchCatalogKey);
|
|
7611
8516
|
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
7612
|
-
|
|
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) {
|
|
7613
8520
|
store.save();
|
|
7614
8521
|
}
|
|
7615
8522
|
if (scopedRoots) {
|
|
@@ -7630,7 +8537,6 @@ var Indexer = class {
|
|
|
7630
8537
|
chunksProcessed: 0,
|
|
7631
8538
|
totalChunks: 0
|
|
7632
8539
|
});
|
|
7633
|
-
this.releaseIndexingLock();
|
|
7634
8540
|
return stats;
|
|
7635
8541
|
}
|
|
7636
8542
|
if (pendingChunks.length === 0) {
|
|
@@ -7639,7 +8545,7 @@ var Indexer = class {
|
|
|
7639
8545
|
database.clearBranchSymbols(branchCatalogKey);
|
|
7640
8546
|
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
7641
8547
|
store.save();
|
|
7642
|
-
|
|
8548
|
+
this.saveInvertedIndex(invertedIndex);
|
|
7643
8549
|
if (scopedRoots) {
|
|
7644
8550
|
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
7645
8551
|
this.clearScopedFailedBatches(scopedRoots);
|
|
@@ -7658,7 +8564,6 @@ var Indexer = class {
|
|
|
7658
8564
|
chunksProcessed: 0,
|
|
7659
8565
|
totalChunks: 0
|
|
7660
8566
|
});
|
|
7661
|
-
this.releaseIndexingLock();
|
|
7662
8567
|
return stats;
|
|
7663
8568
|
}
|
|
7664
8569
|
onProgress?.({
|
|
@@ -7889,7 +8794,7 @@ var Indexer = class {
|
|
|
7889
8794
|
database.clearBranchSymbols(branchCatalogKey);
|
|
7890
8795
|
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
7891
8796
|
store.save();
|
|
7892
|
-
|
|
8797
|
+
this.saveInvertedIndex(invertedIndex);
|
|
7893
8798
|
if (scopedRoots) {
|
|
7894
8799
|
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
7895
8800
|
} else {
|
|
@@ -7941,7 +8846,6 @@ var Indexer = class {
|
|
|
7941
8846
|
chunksProcessed: stats.indexedChunks,
|
|
7942
8847
|
totalChunks: pendingChunks.length
|
|
7943
8848
|
});
|
|
7944
|
-
this.releaseIndexingLock();
|
|
7945
8849
|
return stats;
|
|
7946
8850
|
}
|
|
7947
8851
|
async getQueryEmbedding(query, provider) {
|
|
@@ -8007,8 +8911,8 @@ var Indexer = class {
|
|
|
8007
8911
|
return intersection / union;
|
|
8008
8912
|
}
|
|
8009
8913
|
async search(query, limit, options) {
|
|
8010
|
-
const { store, provider, database } = await this.ensureInitialized();
|
|
8011
|
-
|
|
8914
|
+
const { store, provider, invertedIndex, database, readIssues, compatibility } = await this.ensureInitialized();
|
|
8915
|
+
this.requireReadableComponents(readIssues, "vectors", "database");
|
|
8012
8916
|
if (!compatibility.compatible) {
|
|
8013
8917
|
throw new Error(
|
|
8014
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.`
|
|
@@ -8022,6 +8926,7 @@ var Indexer = class {
|
|
|
8022
8926
|
const maxResults = limit ?? this.config.search.maxResults;
|
|
8023
8927
|
const hybridWeight = options?.hybridWeight ?? this.config.search.hybridWeight;
|
|
8024
8928
|
const fusionStrategy = this.config.search.fusionStrategy;
|
|
8929
|
+
const effectiveHybridWeight = fusionStrategy === "weighted" && readIssues.some((issue) => issue.component === "keyword") ? 0 : hybridWeight;
|
|
8025
8930
|
const rrfK = this.config.search.rrfK;
|
|
8026
8931
|
const rerankTopN = this.config.search.rerankTopN;
|
|
8027
8932
|
const filterByBranch = options?.filterByBranch ?? true;
|
|
@@ -8030,7 +8935,7 @@ var Indexer = class {
|
|
|
8030
8935
|
this.logger.search("debug", "Starting search", {
|
|
8031
8936
|
query,
|
|
8032
8937
|
maxResults,
|
|
8033
|
-
hybridWeight,
|
|
8938
|
+
hybridWeight: effectiveHybridWeight,
|
|
8034
8939
|
fusionStrategy,
|
|
8035
8940
|
rrfK,
|
|
8036
8941
|
rerankTopN,
|
|
@@ -8038,13 +8943,22 @@ var Indexer = class {
|
|
|
8038
8943
|
});
|
|
8039
8944
|
const embeddingStartTime = import_perf_hooks.performance.now();
|
|
8040
8945
|
const embeddingQuery = stripFilePathHint(query);
|
|
8041
|
-
|
|
8946
|
+
let embedding;
|
|
8947
|
+
try {
|
|
8948
|
+
embedding = await this.getQueryEmbedding(embeddingQuery, provider);
|
|
8949
|
+
} catch (error) {
|
|
8950
|
+
this.logger.warn("Query embedding failed; falling back to keyword-only search", {
|
|
8951
|
+
query,
|
|
8952
|
+
error: getErrorMessage2(error),
|
|
8953
|
+
action: "Check the embedding provider configuration and retry search after restoring provider health."
|
|
8954
|
+
});
|
|
8955
|
+
}
|
|
8042
8956
|
const embeddingMs = import_perf_hooks.performance.now() - embeddingStartTime;
|
|
8043
8957
|
const vectorStartTime = import_perf_hooks.performance.now();
|
|
8044
|
-
const semanticResults = store.search(embedding, maxResults * 4);
|
|
8958
|
+
const semanticResults = embedding ? store.search(embedding, maxResults * 4) : [];
|
|
8045
8959
|
const vectorMs = import_perf_hooks.performance.now() - vectorStartTime;
|
|
8046
8960
|
const keywordStartTime = import_perf_hooks.performance.now();
|
|
8047
|
-
const keywordResults = await this.keywordSearch(query, maxResults * 4);
|
|
8961
|
+
const keywordResults = await this.keywordSearch(query, maxResults * 4, store, invertedIndex);
|
|
8048
8962
|
const keywordMs = import_perf_hooks.performance.now() - keywordStartTime;
|
|
8049
8963
|
let branchChunkIds = null;
|
|
8050
8964
|
if (filterByBranch && (this.config.scope === "global" || this.currentBranch !== "default")) {
|
|
@@ -8076,12 +8990,13 @@ var Indexer = class {
|
|
|
8076
8990
|
});
|
|
8077
8991
|
}
|
|
8078
8992
|
const fusionStartTime = import_perf_hooks.performance.now();
|
|
8993
|
+
const rankingHybridWeight = embedding === void 0 && fusionStrategy === "weighted" ? 1 : effectiveHybridWeight;
|
|
8079
8994
|
const combined = rankHybridResults(query, semanticCandidates, keywordCandidates, {
|
|
8080
8995
|
fusionStrategy,
|
|
8081
8996
|
rrfK,
|
|
8082
8997
|
rerankTopN,
|
|
8083
8998
|
limit: maxResults,
|
|
8084
|
-
hybridWeight,
|
|
8999
|
+
hybridWeight: rankingHybridWeight,
|
|
8085
9000
|
prioritizeSourcePaths: sourceIntent
|
|
8086
9001
|
});
|
|
8087
9002
|
const rerankedCombined = await this.rerankCandidatesWithApi(query, combined, {
|
|
@@ -8155,7 +9070,7 @@ var Indexer = class {
|
|
|
8155
9070
|
let contextEndLine = r.metadata.endLine;
|
|
8156
9071
|
if (!metadataOnly && this.config.search.includeContext) {
|
|
8157
9072
|
try {
|
|
8158
|
-
const fileContent = await
|
|
9073
|
+
const fileContent = await import_fs8.promises.readFile(
|
|
8159
9074
|
r.metadata.filePath,
|
|
8160
9075
|
"utf-8"
|
|
8161
9076
|
);
|
|
@@ -8181,8 +9096,7 @@ var Indexer = class {
|
|
|
8181
9096
|
})
|
|
8182
9097
|
);
|
|
8183
9098
|
}
|
|
8184
|
-
async keywordSearch(query, limit) {
|
|
8185
|
-
const { store, invertedIndex } = await this.ensureInitialized();
|
|
9099
|
+
async keywordSearch(query, limit, store, invertedIndex) {
|
|
8186
9100
|
const scores = invertedIndex.search(query);
|
|
8187
9101
|
if (scores.size === 0) {
|
|
8188
9102
|
return [];
|
|
@@ -8200,24 +9114,54 @@ var Indexer = class {
|
|
|
8200
9114
|
return results.slice(0, limit);
|
|
8201
9115
|
}
|
|
8202
9116
|
async getStatus() {
|
|
8203
|
-
const { store, configuredProviderInfo, database } = await this.ensureInitialized();
|
|
9117
|
+
const { store, configuredProviderInfo, database, readIssues, compatibility } = await this.ensureInitialized();
|
|
8204
9118
|
const failedBatchesCount = this.getFailedBatchesCount();
|
|
9119
|
+
const vectorCount = store.count();
|
|
9120
|
+
const statusReadIssues = [...readIssues];
|
|
9121
|
+
let startupWarning = "";
|
|
9122
|
+
if (!statusReadIssues.some((issue) => issue.component === "database")) {
|
|
9123
|
+
try {
|
|
9124
|
+
startupWarning = database.getMetadata(STARTUP_WARNING_METADATA_KEY) ?? "";
|
|
9125
|
+
} catch (error) {
|
|
9126
|
+
const message = this.getDatabaseReadIssueMessage();
|
|
9127
|
+
statusReadIssues.push(this.createReadIssue("database", message));
|
|
9128
|
+
if (!this.readIssues.some((issue) => issue.component === "database")) {
|
|
9129
|
+
this.recordReadIssue("database", message, error);
|
|
9130
|
+
}
|
|
9131
|
+
}
|
|
9132
|
+
}
|
|
9133
|
+
const readWarning = statusReadIssues.map((issue) => issue.message).join(" ");
|
|
9134
|
+
const warning = [readWarning, startupWarning].filter((message) => message.length > 0).join(" ");
|
|
9135
|
+
const hasBlockingReadIssue = statusReadIssues.some((issue) => issue.blocking);
|
|
8205
9136
|
return {
|
|
8206
|
-
indexed:
|
|
8207
|
-
vectorCount
|
|
9137
|
+
indexed: vectorCount > 0 && !hasBlockingReadIssue,
|
|
9138
|
+
vectorCount,
|
|
8208
9139
|
provider: configuredProviderInfo.provider,
|
|
8209
9140
|
model: configuredProviderInfo.modelInfo.model,
|
|
8210
9141
|
indexPath: this.indexPath,
|
|
8211
9142
|
currentBranch: this.currentBranch,
|
|
8212
9143
|
baseBranch: this.baseBranch,
|
|
8213
|
-
compatibility
|
|
9144
|
+
compatibility,
|
|
8214
9145
|
failedBatchesCount,
|
|
8215
9146
|
failedBatchesPath: failedBatchesCount > 0 ? this.failedBatchesPath : void 0,
|
|
8216
|
-
warning:
|
|
9147
|
+
warning: warning || void 0
|
|
8217
9148
|
};
|
|
8218
9149
|
}
|
|
9150
|
+
async forceIndex(onProgress) {
|
|
9151
|
+
return this.withIndexMutationLease("force-index", async (recoveredOwners) => {
|
|
9152
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9153
|
+
await this.clearIndexUnlocked();
|
|
9154
|
+
return this.indexUnlocked(onProgress, [], true);
|
|
9155
|
+
});
|
|
9156
|
+
}
|
|
8219
9157
|
async clearIndex() {
|
|
8220
|
-
|
|
9158
|
+
await this.withIndexMutationLease("clear", async (recoveredOwners) => {
|
|
9159
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9160
|
+
await this.clearIndexUnlocked();
|
|
9161
|
+
});
|
|
9162
|
+
}
|
|
9163
|
+
async clearIndexUnlocked() {
|
|
9164
|
+
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
8221
9165
|
if (this.config.scope === "global") {
|
|
8222
9166
|
store.load();
|
|
8223
9167
|
invertedIndex.load();
|
|
@@ -8244,7 +9188,7 @@ var Indexer = class {
|
|
|
8244
9188
|
store.clear();
|
|
8245
9189
|
store.save();
|
|
8246
9190
|
invertedIndex.clear();
|
|
8247
|
-
|
|
9191
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8248
9192
|
this.fileHashCache.clear();
|
|
8249
9193
|
this.saveFileHashCache();
|
|
8250
9194
|
database.clearAllIndexedData();
|
|
@@ -8268,14 +9212,7 @@ var Indexer = class {
|
|
|
8268
9212
|
this.indexCompatibility = compatibility;
|
|
8269
9213
|
return;
|
|
8270
9214
|
}
|
|
8271
|
-
|
|
8272
|
-
if (this.host !== "opencode") {
|
|
8273
|
-
localProjectIndexPaths.push(path12.join(this.projectRoot, getHostProjectIndexRelativePath("opencode")));
|
|
8274
|
-
}
|
|
8275
|
-
const isLocalProjectIndex = localProjectIndexPaths.some(
|
|
8276
|
-
(localPath) => path12.resolve(this.indexPath) === path12.resolve(localPath)
|
|
8277
|
-
);
|
|
8278
|
-
if (!isLocalProjectIndex) {
|
|
9215
|
+
if (!this.isLocalProjectIndexPath()) {
|
|
8279
9216
|
throw new Error(
|
|
8280
9217
|
"Project-scoped force rebuild is unsafe while using an inherited worktree index. Create a local project config boundary before clearing the index."
|
|
8281
9218
|
);
|
|
@@ -8283,7 +9220,7 @@ var Indexer = class {
|
|
|
8283
9220
|
store.clear();
|
|
8284
9221
|
store.save();
|
|
8285
9222
|
invertedIndex.clear();
|
|
8286
|
-
|
|
9223
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8287
9224
|
this.fileHashCache.clear();
|
|
8288
9225
|
this.saveFileHashCache();
|
|
8289
9226
|
database.clearAllIndexedData();
|
|
@@ -8301,7 +9238,13 @@ var Indexer = class {
|
|
|
8301
9238
|
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
8302
9239
|
}
|
|
8303
9240
|
async healthCheck() {
|
|
8304
|
-
|
|
9241
|
+
return this.withIndexMutationLease("health-check", async (recoveredOwners) => {
|
|
9242
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9243
|
+
return this.healthCheckUnlocked();
|
|
9244
|
+
});
|
|
9245
|
+
}
|
|
9246
|
+
async healthCheckUnlocked() {
|
|
9247
|
+
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
8305
9248
|
this.logger.gc("info", "Starting health check");
|
|
8306
9249
|
const allMetadata = store.getAllMetadata();
|
|
8307
9250
|
const filePathsToChunkKeys = /* @__PURE__ */ new Map();
|
|
@@ -8314,7 +9257,7 @@ var Indexer = class {
|
|
|
8314
9257
|
const removedChunkKeys = [];
|
|
8315
9258
|
const chunkKeysByRemovedFile = /* @__PURE__ */ new Map();
|
|
8316
9259
|
for (const [filePath, chunkKeys] of filePathsToChunkKeys) {
|
|
8317
|
-
if (!(0,
|
|
9260
|
+
if (!(0, import_fs8.existsSync)(filePath)) {
|
|
8318
9261
|
chunkKeysByRemovedFile.set(filePath, chunkKeys);
|
|
8319
9262
|
for (const key of chunkKeys) {
|
|
8320
9263
|
removedChunkKeys.push(key);
|
|
@@ -8339,7 +9282,7 @@ var Indexer = class {
|
|
|
8339
9282
|
const removedCount = removedChunkKeys.length;
|
|
8340
9283
|
if (removedCount > 0) {
|
|
8341
9284
|
store.save();
|
|
8342
|
-
|
|
9285
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8343
9286
|
}
|
|
8344
9287
|
let gcOrphanEmbeddings;
|
|
8345
9288
|
let gcOrphanChunks;
|
|
@@ -8354,7 +9297,7 @@ var Indexer = class {
|
|
|
8354
9297
|
if (!await this.tryResetCorruptedIndex("running index health check", error)) {
|
|
8355
9298
|
throw error;
|
|
8356
9299
|
}
|
|
8357
|
-
await this.
|
|
9300
|
+
await this.initializeUnlocked("writer", [], { skipAutoGc: true });
|
|
8358
9301
|
return {
|
|
8359
9302
|
removed: 0,
|
|
8360
9303
|
filePaths: [],
|
|
@@ -8363,7 +9306,7 @@ var Indexer = class {
|
|
|
8363
9306
|
gcOrphanSymbols: 0,
|
|
8364
9307
|
gcOrphanCallEdges: 0,
|
|
8365
9308
|
resetCorruptedIndex: true,
|
|
8366
|
-
warning: this.getCorruptedIndexWarning(
|
|
9309
|
+
warning: this.getCorruptedIndexWarning(path13.join(this.indexPath, "codebase.db"))
|
|
8367
9310
|
};
|
|
8368
9311
|
}
|
|
8369
9312
|
this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
|
|
@@ -8376,7 +9319,13 @@ var Indexer = class {
|
|
|
8376
9319
|
return { removed: removedCount, filePaths: removedFilePaths, gcOrphanEmbeddings, gcOrphanChunks, gcOrphanSymbols, gcOrphanCallEdges };
|
|
8377
9320
|
}
|
|
8378
9321
|
async retryFailedBatches() {
|
|
8379
|
-
|
|
9322
|
+
return this.withIndexMutationLease("retry-failed-batches", async (recoveredOwners) => {
|
|
9323
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9324
|
+
return this.retryFailedBatchesUnlocked();
|
|
9325
|
+
});
|
|
9326
|
+
}
|
|
9327
|
+
async retryFailedBatchesUnlocked() {
|
|
9328
|
+
const { store, provider, invertedIndex, database, configuredProviderInfo } = this.requireLoadedIndexState();
|
|
8380
9329
|
const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
|
|
8381
9330
|
const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
|
|
8382
9331
|
const roots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
@@ -8540,7 +9489,7 @@ var Indexer = class {
|
|
|
8540
9489
|
}
|
|
8541
9490
|
if (succeeded > 0) {
|
|
8542
9491
|
store.save();
|
|
8543
|
-
|
|
9492
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8544
9493
|
}
|
|
8545
9494
|
if (roots && succeeded > 0 && persistedStillFailing.length === 0 && this.hasProjectForceReembedPending()) {
|
|
8546
9495
|
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
@@ -8568,15 +9517,16 @@ var Indexer = class {
|
|
|
8568
9517
|
}
|
|
8569
9518
|
}
|
|
8570
9519
|
async getDatabaseStats() {
|
|
8571
|
-
const { database } = await this.ensureInitialized();
|
|
9520
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9521
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8572
9522
|
return database.getStats();
|
|
8573
9523
|
}
|
|
8574
9524
|
getLogger() {
|
|
8575
9525
|
return this.logger;
|
|
8576
9526
|
}
|
|
8577
9527
|
async findSimilar(code, limit = this.config.search.maxResults, options) {
|
|
8578
|
-
const { store, provider, database } = await this.ensureInitialized();
|
|
8579
|
-
|
|
9528
|
+
const { store, provider, database, readIssues, compatibility } = await this.ensureInitialized();
|
|
9529
|
+
this.requireReadableComponents(readIssues, "vectors", "database");
|
|
8580
9530
|
if (!compatibility.compatible) {
|
|
8581
9531
|
throw new Error(
|
|
8582
9532
|
`${compatibility.reason ?? "Index is incompatible with current embedding provider."} Run index_codebase with force=true to rebuild the index.`
|
|
@@ -8666,7 +9616,7 @@ var Indexer = class {
|
|
|
8666
9616
|
let content = "";
|
|
8667
9617
|
if (this.config.search.includeContext) {
|
|
8668
9618
|
try {
|
|
8669
|
-
const fileContent = await
|
|
9619
|
+
const fileContent = await import_fs8.promises.readFile(
|
|
8670
9620
|
r.metadata.filePath,
|
|
8671
9621
|
"utf-8"
|
|
8672
9622
|
);
|
|
@@ -8690,7 +9640,8 @@ var Indexer = class {
|
|
|
8690
9640
|
);
|
|
8691
9641
|
}
|
|
8692
9642
|
async getCallers(targetName, callTypeFilter) {
|
|
8693
|
-
const { database } = await this.ensureInitialized();
|
|
9643
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9644
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8694
9645
|
const seen = /* @__PURE__ */ new Set();
|
|
8695
9646
|
const results = [];
|
|
8696
9647
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
@@ -8704,7 +9655,8 @@ var Indexer = class {
|
|
|
8704
9655
|
return results;
|
|
8705
9656
|
}
|
|
8706
9657
|
async getCallees(symbolId, callTypeFilter) {
|
|
8707
|
-
const { database } = await this.ensureInitialized();
|
|
9658
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9659
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8708
9660
|
const seen = /* @__PURE__ */ new Set();
|
|
8709
9661
|
const results = [];
|
|
8710
9662
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
@@ -8718,43 +9670,50 @@ var Indexer = class {
|
|
|
8718
9670
|
return results;
|
|
8719
9671
|
}
|
|
8720
9672
|
async findCallPath(fromName, toName, maxDepth) {
|
|
8721
|
-
const { database } = await this.ensureInitialized();
|
|
9673
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9674
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8722
9675
|
let shortest = [];
|
|
8723
9676
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
8724
|
-
const
|
|
8725
|
-
if (
|
|
8726
|
-
shortest =
|
|
9677
|
+
const path24 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
|
|
9678
|
+
if (path24.length > 0 && (shortest.length === 0 || path24.length < shortest.length)) {
|
|
9679
|
+
shortest = path24;
|
|
8727
9680
|
}
|
|
8728
9681
|
}
|
|
8729
9682
|
return shortest;
|
|
8730
9683
|
}
|
|
8731
9684
|
async getSymbolsForBranch(branch) {
|
|
8732
|
-
const { database } = await this.ensureInitialized();
|
|
9685
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9686
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8733
9687
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8734
9688
|
return database.getSymbolsForBranch(resolvedBranch);
|
|
8735
9689
|
}
|
|
8736
9690
|
async getSymbolsForFiles(filePaths, branch) {
|
|
8737
|
-
const { database } = await this.ensureInitialized();
|
|
9691
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9692
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8738
9693
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8739
9694
|
return database.getSymbolsForFiles(filePaths, resolvedBranch);
|
|
8740
9695
|
}
|
|
8741
9696
|
async getTransitiveReachability(rootSymbolIds, direction, maxDepth) {
|
|
8742
|
-
const { database } = await this.ensureInitialized();
|
|
9697
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9698
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8743
9699
|
const branch = this.getBranchCatalogKey();
|
|
8744
9700
|
return database.getTransitiveReachability(rootSymbolIds, branch, direction, maxDepth);
|
|
8745
9701
|
}
|
|
8746
9702
|
async detectCommunities(branch, symbolIds) {
|
|
8747
|
-
const { database } = await this.ensureInitialized();
|
|
9703
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9704
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8748
9705
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8749
9706
|
return database.detectCommunities(resolvedBranch, symbolIds);
|
|
8750
9707
|
}
|
|
8751
9708
|
async computeCentrality(branch) {
|
|
8752
|
-
const { database } = await this.ensureInitialized();
|
|
9709
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9710
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8753
9711
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8754
9712
|
return database.computeCentrality(resolvedBranch);
|
|
8755
9713
|
}
|
|
8756
9714
|
async getPrImpact(opts) {
|
|
8757
|
-
const { database } = await this.ensureInitialized();
|
|
9715
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9716
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8758
9717
|
const execFileAsync3 = (0, import_util3.promisify)(import_child_process3.execFile);
|
|
8759
9718
|
const changedFilesResult = await getChangedFiles({
|
|
8760
9719
|
pr: opts.pr,
|
|
@@ -8777,7 +9736,7 @@ var Indexer = class {
|
|
|
8777
9736
|
"Run index_codebase first to build the call graph and symbol index for this branch."
|
|
8778
9737
|
);
|
|
8779
9738
|
}
|
|
8780
|
-
const absoluteChangedFiles = changedFiles.map((f) =>
|
|
9739
|
+
const absoluteChangedFiles = changedFiles.map((f) => path13.resolve(this.projectRoot, f));
|
|
8781
9740
|
const directSymbols = database.getSymbolsForFiles(absoluteChangedFiles, branchKey);
|
|
8782
9741
|
const directIds = directSymbols.map((s) => s.id);
|
|
8783
9742
|
const direction = opts.direction ?? "both";
|
|
@@ -8860,7 +9819,7 @@ var Indexer = class {
|
|
|
8860
9819
|
projectRoot: this.projectRoot,
|
|
8861
9820
|
baseBranch: this.baseBranch
|
|
8862
9821
|
});
|
|
8863
|
-
const otherAbsolute = otherChanged.files.map((f) =>
|
|
9822
|
+
const otherAbsolute = otherChanged.files.map((f) => path13.resolve(this.projectRoot, f));
|
|
8864
9823
|
const otherBranchKey = this.getBranchCatalogKeyFor(openPr.headRefName);
|
|
8865
9824
|
const otherSymbols = database.getSymbolsForFiles(otherAbsolute, otherBranchKey);
|
|
8866
9825
|
const otherLabels = /* @__PURE__ */ new Set();
|
|
@@ -8910,7 +9869,8 @@ var Indexer = class {
|
|
|
8910
9869
|
};
|
|
8911
9870
|
}
|
|
8912
9871
|
async getVisualizationData(options) {
|
|
8913
|
-
const { database, store } = await this.ensureInitialized();
|
|
9872
|
+
const { database, store, readIssues } = await this.ensureInitialized();
|
|
9873
|
+
this.requireReadableComponents(readIssues, "vectors", "database");
|
|
8914
9874
|
const seenSymbols = /* @__PURE__ */ new Map();
|
|
8915
9875
|
const seenEdges = /* @__PURE__ */ new Map();
|
|
8916
9876
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
@@ -8923,12 +9883,12 @@ var Indexer = class {
|
|
|
8923
9883
|
if (meta.filePath) filePaths.add(meta.filePath);
|
|
8924
9884
|
}
|
|
8925
9885
|
const directory = options?.directory?.replace(/\/$/, "");
|
|
8926
|
-
const absoluteDirectoryFilter = directory ?
|
|
9886
|
+
const absoluteDirectoryFilter = directory ? path13.resolve(this.projectRoot, directory) : void 0;
|
|
8927
9887
|
for (const filePath of filePaths) {
|
|
8928
9888
|
if (directory) {
|
|
8929
|
-
const absoluteFilePath =
|
|
9889
|
+
const absoluteFilePath = path13.resolve(filePath);
|
|
8930
9890
|
const matchesRelative = filePath === directory || filePath.startsWith(directory + "/");
|
|
8931
|
-
const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter +
|
|
9891
|
+
const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter + path13.sep));
|
|
8932
9892
|
if (!matchesRelative && !matchesProjectRelative) {
|
|
8933
9893
|
continue;
|
|
8934
9894
|
}
|
|
@@ -8950,53 +9910,64 @@ var Indexer = class {
|
|
|
8950
9910
|
return { symbols: [...seenSymbols.values()], edges: [...seenEdges.values()] };
|
|
8951
9911
|
}
|
|
8952
9912
|
async close() {
|
|
8953
|
-
|
|
9913
|
+
this.database?.close();
|
|
9914
|
+
for (const database of this.retiredDatabases) {
|
|
9915
|
+
database.close();
|
|
9916
|
+
}
|
|
9917
|
+
this.retiredDatabases = [];
|
|
8954
9918
|
this.database = null;
|
|
8955
9919
|
this.store = null;
|
|
8956
9920
|
this.invertedIndex = null;
|
|
8957
9921
|
this.provider = null;
|
|
8958
9922
|
this.reranker = null;
|
|
9923
|
+
this.configuredProviderInfo = null;
|
|
9924
|
+
this.indexCompatibility = null;
|
|
9925
|
+
this.initializationMode = "none";
|
|
9926
|
+
this.readIssues = [];
|
|
9927
|
+
this.readerArtifactFingerprint = null;
|
|
9928
|
+
this.writerArtifactFingerprint = null;
|
|
9929
|
+
this.readerArtifactRetryAfter.clear();
|
|
8959
9930
|
}
|
|
8960
9931
|
};
|
|
8961
9932
|
|
|
8962
9933
|
// src/tools/knowledge-base-paths.ts
|
|
8963
|
-
var
|
|
9934
|
+
var path14 = __toESM(require("path"), 1);
|
|
8964
9935
|
function resolveConfigPathValue(value, baseDir) {
|
|
8965
9936
|
const trimmed = value.trim();
|
|
8966
9937
|
if (!trimmed) {
|
|
8967
9938
|
return trimmed;
|
|
8968
9939
|
}
|
|
8969
|
-
const absolutePath =
|
|
8970
|
-
return
|
|
9940
|
+
const absolutePath = path14.isAbsolute(trimmed) ? trimmed : path14.resolve(baseDir, trimmed);
|
|
9941
|
+
return path14.normalize(absolutePath);
|
|
8971
9942
|
}
|
|
8972
9943
|
function serializeConfigPathValue(value, baseDir) {
|
|
8973
9944
|
const trimmed = value.trim();
|
|
8974
9945
|
if (!trimmed) {
|
|
8975
9946
|
return trimmed;
|
|
8976
9947
|
}
|
|
8977
|
-
if (!
|
|
8978
|
-
return normalizePathSeparators(
|
|
9948
|
+
if (!path14.isAbsolute(trimmed)) {
|
|
9949
|
+
return normalizePathSeparators(path14.normalize(trimmed));
|
|
8979
9950
|
}
|
|
8980
|
-
const relativePath =
|
|
8981
|
-
if (!relativePath || !relativePath.startsWith("..") && !
|
|
8982
|
-
return normalizePathSeparators(
|
|
9951
|
+
const relativePath = path14.relative(baseDir, trimmed);
|
|
9952
|
+
if (!relativePath || !relativePath.startsWith("..") && !path14.isAbsolute(relativePath)) {
|
|
9953
|
+
return normalizePathSeparators(path14.normalize(relativePath || "."));
|
|
8983
9954
|
}
|
|
8984
|
-
return
|
|
9955
|
+
return path14.normalize(trimmed);
|
|
8985
9956
|
}
|
|
8986
9957
|
function resolveKnowledgeBasePath(value, projectRoot) {
|
|
8987
|
-
return
|
|
9958
|
+
return path14.isAbsolute(value) ? value : path14.resolve(projectRoot, value);
|
|
8988
9959
|
}
|
|
8989
9960
|
function normalizeKnowledgeBasePath2(value, projectRoot) {
|
|
8990
|
-
return
|
|
9961
|
+
return path14.normalize(resolveKnowledgeBasePath(value, projectRoot));
|
|
8991
9962
|
}
|
|
8992
9963
|
function hasMatchingKnowledgeBasePath(knowledgeBases, inputPath, projectRoot) {
|
|
8993
|
-
const normalizedInput =
|
|
9964
|
+
const normalizedInput = path14.normalize(inputPath);
|
|
8994
9965
|
return knowledgeBases.some((kb) => normalizeKnowledgeBasePath2(kb, projectRoot) === normalizedInput);
|
|
8995
9966
|
}
|
|
8996
9967
|
function findKnowledgeBasePathIndex(knowledgeBases, inputPath, projectRoot) {
|
|
8997
|
-
const normalizedInput =
|
|
9968
|
+
const normalizedInput = path14.normalize(inputPath);
|
|
8998
9969
|
return knowledgeBases.findIndex(
|
|
8999
|
-
(kb) =>
|
|
9970
|
+
(kb) => path14.normalize(kb) === normalizedInput || normalizeKnowledgeBasePath2(kb, projectRoot) === normalizedInput
|
|
9000
9971
|
);
|
|
9001
9972
|
}
|
|
9002
9973
|
|
|
@@ -9096,6 +10067,10 @@ function formatStatus(status) {
|
|
|
9096
10067
|
lines.push(`Failed batches: ${status.failedBatchesPath}`);
|
|
9097
10068
|
}
|
|
9098
10069
|
}
|
|
10070
|
+
if (status.warning) {
|
|
10071
|
+
lines.push("");
|
|
10072
|
+
lines.push(`INDEX WARNING: ${status.warning}`);
|
|
10073
|
+
}
|
|
9099
10074
|
if (status.compatibility && !status.compatibility.compatible) {
|
|
9100
10075
|
lines.push("");
|
|
9101
10076
|
lines.push(`COMPATIBILITY WARNING: ${status.compatibility.reason}`);
|
|
@@ -9201,16 +10176,16 @@ function formatCallGraphCallees(symbolId, callees, relationshipType) {
|
|
|
9201
10176
|
return `[${index + 1}] \u2192 ${edge.targetName} (${edge.callType})${confidence} at line ${edge.line}${edge.isResolved ? ` [resolved: ${edge.toSymbolId}]` : " [unresolved]"}`;
|
|
9202
10177
|
}).join("\n");
|
|
9203
10178
|
}
|
|
9204
|
-
function formatCallGraphPath(from, to,
|
|
9205
|
-
if (
|
|
10179
|
+
function formatCallGraphPath(from, to, path24) {
|
|
10180
|
+
if (path24.length === 0) {
|
|
9206
10181
|
return `No path found between "${from}" and "${to}". They may be in disconnected components, or the call graph index needs updating.`;
|
|
9207
10182
|
}
|
|
9208
|
-
const formatted =
|
|
10183
|
+
const formatted = path24.map((hop, index) => {
|
|
9209
10184
|
const prefix = index === 0 ? "[start]" : `--${hop.callType}-->`;
|
|
9210
10185
|
const location = hop.filePath ? ` (${hop.filePath}:${hop.line})` : "";
|
|
9211
10186
|
return `${prefix} ${hop.symbolName}${location}`;
|
|
9212
10187
|
});
|
|
9213
|
-
return `Path (${
|
|
10188
|
+
return `Path (${path24.length} hops):
|
|
9214
10189
|
${formatted.join("\n")}`;
|
|
9215
10190
|
}
|
|
9216
10191
|
function formatResultHeader(result, index) {
|
|
@@ -9250,8 +10225,8 @@ ${truncateContent(r.content)}
|
|
|
9250
10225
|
}
|
|
9251
10226
|
|
|
9252
10227
|
// src/tools/config-state.ts
|
|
9253
|
-
var
|
|
9254
|
-
var
|
|
10228
|
+
var import_fs9 = require("fs");
|
|
10229
|
+
var path15 = __toESM(require("path"), 1);
|
|
9255
10230
|
function normalizeKnowledgeBasePaths(config, projectRoot) {
|
|
9256
10231
|
const normalized = { ...config };
|
|
9257
10232
|
if (Array.isArray(normalized.knowledgeBases)) {
|
|
@@ -9278,10 +10253,10 @@ function loadEditableConfig(projectRoot, host = "opencode") {
|
|
|
9278
10253
|
}
|
|
9279
10254
|
function saveConfig(projectRoot, config, host = "opencode") {
|
|
9280
10255
|
const configPath = getConfigPath(projectRoot, host);
|
|
9281
|
-
const configDir =
|
|
9282
|
-
const configBaseDir =
|
|
9283
|
-
if (!(0,
|
|
9284
|
-
(0,
|
|
10256
|
+
const configDir = path15.dirname(configPath);
|
|
10257
|
+
const configBaseDir = path15.dirname(configDir);
|
|
10258
|
+
if (!(0, import_fs9.existsSync)(configDir)) {
|
|
10259
|
+
(0, import_fs9.mkdirSync)(configDir, { recursive: true });
|
|
9285
10260
|
}
|
|
9286
10261
|
const serializableConfig = { ...config };
|
|
9287
10262
|
if (Array.isArray(serializableConfig.knowledgeBases)) {
|
|
@@ -9289,13 +10264,31 @@ function saveConfig(projectRoot, config, host = "opencode") {
|
|
|
9289
10264
|
(kb) => serializeConfigPathValue(kb, configBaseDir)
|
|
9290
10265
|
);
|
|
9291
10266
|
}
|
|
9292
|
-
(0,
|
|
10267
|
+
(0, import_fs9.writeFileSync)(configPath, JSON.stringify(serializableConfig, null, 2) + "\n", "utf-8");
|
|
9293
10268
|
}
|
|
9294
10269
|
|
|
9295
10270
|
// src/tools/operations.ts
|
|
9296
10271
|
var indexerCache = /* @__PURE__ */ new Map();
|
|
9297
10272
|
var configCache = /* @__PURE__ */ new Map();
|
|
9298
10273
|
var defaultProjectRoots = /* @__PURE__ */ new Map();
|
|
10274
|
+
function getIndexBusyResult(error) {
|
|
10275
|
+
if (!isIndexLockContentionError(error)) return null;
|
|
10276
|
+
const owner = error.owner;
|
|
10277
|
+
const ownerText = owner ? `PID ${owner.pid}, operation ${owner.operation}, since ${owner.startedAt}` : "unreadable owner";
|
|
10278
|
+
if (error.reason === "legacy-lock") {
|
|
10279
|
+
return {
|
|
10280
|
+
kind: "busy",
|
|
10281
|
+
text: `INDEX_BUSY: legacy lock format detected (${ownerText}). Verify the PID and remove this lock manually only if it is stale.`
|
|
10282
|
+
};
|
|
10283
|
+
}
|
|
10284
|
+
if (error.reason === "unknown-owner") {
|
|
10285
|
+
return {
|
|
10286
|
+
kind: "busy",
|
|
10287
|
+
text: `INDEX_BUSY: unreadable or remote lock owner (${ownerText}). Automatic recovery was refused; manual verification is required.`
|
|
10288
|
+
};
|
|
10289
|
+
}
|
|
10290
|
+
return { kind: "busy", text: `INDEX_BUSY: another index operation is already in progress (${ownerText}).` };
|
|
10291
|
+
}
|
|
9299
10292
|
function getProjectRoot(projectRoot, host) {
|
|
9300
10293
|
if (projectRoot) {
|
|
9301
10294
|
return projectRoot;
|
|
@@ -9340,8 +10333,8 @@ function refreshIndexerForDirectory(projectRoot, host = "opencode", config = par
|
|
|
9340
10333
|
}
|
|
9341
10334
|
function shouldForceLocalizeProjectIndex(projectRoot, host = "opencode") {
|
|
9342
10335
|
const root = getProjectRoot(projectRoot, host);
|
|
9343
|
-
const localIndexPath =
|
|
9344
|
-
if ((0,
|
|
10336
|
+
const localIndexPath = path16.join(root, getHostProjectIndexRelativePath(host));
|
|
10337
|
+
if ((0, import_fs10.existsSync)(localIndexPath)) {
|
|
9345
10338
|
return false;
|
|
9346
10339
|
}
|
|
9347
10340
|
const inheritedIndexPath = resolveWorktreeFallbackProjectIndexPath(root, host);
|
|
@@ -9397,35 +10390,46 @@ async function getCallGraphPath(projectRoot, host, from, to, maxDepth) {
|
|
|
9397
10390
|
async function runIndexCodebase(projectRoot, host, args, onProgress) {
|
|
9398
10391
|
const root = getProjectRoot(projectRoot, host);
|
|
9399
10392
|
const key = getIndexerCacheKey(root, host);
|
|
9400
|
-
const cachedConfig = configCache.get(key);
|
|
9401
10393
|
let indexer = getIndexerForProject(root, host);
|
|
9402
|
-
|
|
9403
|
-
|
|
9404
|
-
|
|
9405
|
-
|
|
9406
|
-
|
|
9407
|
-
|
|
9408
|
-
|
|
9409
|
-
|
|
9410
|
-
|
|
9411
|
-
|
|
9412
|
-
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
|
-
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
|
|
9419
|
-
|
|
9420
|
-
|
|
9421
|
-
chunksProcessed: progress.chunksProcessed,
|
|
9422
|
-
totalChunks: progress.totalChunks,
|
|
9423
|
-
percentage: calculatePercentage(progress)
|
|
10394
|
+
const runtimeConfig = configCache.get(key);
|
|
10395
|
+
try {
|
|
10396
|
+
if (args.estimateOnly) {
|
|
10397
|
+
return { kind: "estimate", estimate: await indexer.estimateCost() };
|
|
10398
|
+
}
|
|
10399
|
+
const runIndex = async (target) => {
|
|
10400
|
+
const operation = args.force ? target.forceIndex.bind(target) : target.index.bind(target);
|
|
10401
|
+
return operation((progress) => {
|
|
10402
|
+
if (onProgress) {
|
|
10403
|
+
return onProgress(formatProgressTitle(progress), {
|
|
10404
|
+
phase: progress.phase,
|
|
10405
|
+
filesProcessed: progress.filesProcessed,
|
|
10406
|
+
totalFiles: progress.totalFiles,
|
|
10407
|
+
chunksProcessed: progress.chunksProcessed,
|
|
10408
|
+
totalChunks: progress.totalChunks,
|
|
10409
|
+
percentage: calculatePercentage(progress)
|
|
10410
|
+
});
|
|
10411
|
+
}
|
|
10412
|
+
return Promise.resolve();
|
|
9424
10413
|
});
|
|
10414
|
+
};
|
|
10415
|
+
let stats;
|
|
10416
|
+
if (args.force && shouldForceLocalizeProjectIndex(root, host)) {
|
|
10417
|
+
const inheritedIndexPath = resolveProjectIndexPath(root, runtimeConfig.scope, host);
|
|
10418
|
+
stats = await withIndexLock(inheritedIndexPath, "force-index", async () => {
|
|
10419
|
+
materializeLocalProjectConfig(root, loadProjectConfigLayer(root, host), host);
|
|
10420
|
+
refreshIndexerForDirectory(root, host, runtimeConfig);
|
|
10421
|
+
indexer = getIndexerForProject(root, host);
|
|
10422
|
+
return runIndex(indexer);
|
|
10423
|
+
}, { completeRecoveries: false });
|
|
10424
|
+
} else {
|
|
10425
|
+
stats = await runIndex(indexer);
|
|
9425
10426
|
}
|
|
9426
|
-
return
|
|
9427
|
-
})
|
|
9428
|
-
|
|
10427
|
+
return { kind: "stats", stats };
|
|
10428
|
+
} catch (error) {
|
|
10429
|
+
const busyResult = getIndexBusyResult(error);
|
|
10430
|
+
if (!busyResult) throw error;
|
|
10431
|
+
return busyResult;
|
|
10432
|
+
}
|
|
9429
10433
|
}
|
|
9430
10434
|
async function getIndexStatus(projectRoot, host) {
|
|
9431
10435
|
const indexer = getIndexerForProject(projectRoot, host);
|
|
@@ -9435,6 +10439,15 @@ async function getIndexHealthCheck(projectRoot, host) {
|
|
|
9435
10439
|
const indexer = getIndexerForProject(projectRoot, host);
|
|
9436
10440
|
return indexer.healthCheck();
|
|
9437
10441
|
}
|
|
10442
|
+
async function runIndexHealthCheck(projectRoot, host) {
|
|
10443
|
+
try {
|
|
10444
|
+
return { kind: "health", health: await getIndexHealthCheck(projectRoot, host) };
|
|
10445
|
+
} catch (error) {
|
|
10446
|
+
const busyResult = getIndexBusyResult(error);
|
|
10447
|
+
if (!busyResult) throw error;
|
|
10448
|
+
return busyResult;
|
|
10449
|
+
}
|
|
10450
|
+
}
|
|
9438
10451
|
async function getIndexMetrics(projectRoot, host) {
|
|
9439
10452
|
const indexer = getIndexerForProject(projectRoot, host);
|
|
9440
10453
|
const logger = indexer.getLogger();
|
|
@@ -9490,15 +10503,15 @@ async function getIndexLogs(projectRoot, host, args) {
|
|
|
9490
10503
|
function addKnowledgeBase(projectRoot, host, knowledgeBasePath) {
|
|
9491
10504
|
const root = getProjectRoot(projectRoot, host);
|
|
9492
10505
|
const inputPath = knowledgeBasePath.trim();
|
|
9493
|
-
const normalizedPath =
|
|
9494
|
-
|
|
10506
|
+
const normalizedPath = path16.resolve(
|
|
10507
|
+
path16.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, root)
|
|
9495
10508
|
);
|
|
9496
|
-
if (!(0,
|
|
10509
|
+
if (!(0, import_fs10.existsSync)(normalizedPath)) {
|
|
9497
10510
|
return `Error: Directory does not exist: ${normalizedPath}`;
|
|
9498
10511
|
}
|
|
9499
10512
|
let realPath;
|
|
9500
10513
|
try {
|
|
9501
|
-
realPath = (0,
|
|
10514
|
+
realPath = (0, import_fs10.realpathSync)(normalizedPath);
|
|
9502
10515
|
} catch {
|
|
9503
10516
|
return `Error: Cannot resolve path: ${normalizedPath}`;
|
|
9504
10517
|
}
|
|
@@ -9527,13 +10540,13 @@ function addKnowledgeBase(projectRoot, host, knowledgeBasePath) {
|
|
|
9527
10540
|
}
|
|
9528
10541
|
}
|
|
9529
10542
|
for (const dotDir of sensitiveDotDirs) {
|
|
9530
|
-
const sensitiveDir =
|
|
10543
|
+
const sensitiveDir = path16.join(homeDir, dotDir);
|
|
9531
10544
|
if (sensitiveDir && (realPath === sensitiveDir || realPath.startsWith(`${sensitiveDir}/`))) {
|
|
9532
10545
|
return `Error: Adding sensitive directory as knowledge base is not allowed: ${normalizedPath}`;
|
|
9533
10546
|
}
|
|
9534
10547
|
}
|
|
9535
10548
|
try {
|
|
9536
|
-
const stat4 = (0,
|
|
10549
|
+
const stat4 = (0, import_fs10.statSync)(normalizedPath);
|
|
9537
10550
|
if (!stat4.isDirectory()) {
|
|
9538
10551
|
return `Error: Path is not a directory: ${normalizedPath}`;
|
|
9539
10552
|
}
|
|
@@ -9573,7 +10586,7 @@ function listKnowledgeBases(projectRoot, host) {
|
|
|
9573
10586
|
for (let i = 0; i < knowledgeBases.length; i++) {
|
|
9574
10587
|
const kb = knowledgeBases[i];
|
|
9575
10588
|
const resolvedPath = resolveKnowledgeBasePath(kb, root);
|
|
9576
|
-
const exists = (0,
|
|
10589
|
+
const exists = (0, import_fs10.existsSync)(resolvedPath);
|
|
9577
10590
|
result += `[${i + 1}] ${kb}
|
|
9578
10591
|
`;
|
|
9579
10592
|
result += ` Resolved: ${resolvedPath}
|
|
@@ -9582,7 +10595,7 @@ function listKnowledgeBases(projectRoot, host) {
|
|
|
9582
10595
|
`;
|
|
9583
10596
|
if (exists) {
|
|
9584
10597
|
try {
|
|
9585
|
-
const stat4 = (0,
|
|
10598
|
+
const stat4 = (0, import_fs10.statSync)(resolvedPath);
|
|
9586
10599
|
result += ` Type: ${stat4.isDirectory() ? "Directory" : "File"}
|
|
9587
10600
|
`;
|
|
9588
10601
|
} catch {
|
|
@@ -9590,7 +10603,7 @@ function listKnowledgeBases(projectRoot, host) {
|
|
|
9590
10603
|
}
|
|
9591
10604
|
result += "\n";
|
|
9592
10605
|
}
|
|
9593
|
-
const hasHostConfig = (0,
|
|
10606
|
+
const hasHostConfig = (0, import_fs10.existsSync)(path16.join(root, getHostProjectConfigRelativePath(host)));
|
|
9594
10607
|
if (hasHostConfig) {
|
|
9595
10608
|
result += `
|
|
9596
10609
|
Config sources: 1 file(s).`;
|
|
@@ -9713,7 +10726,7 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
9713
10726
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
9714
10727
|
const statMethod = opts.lstat ? import_promises.lstat : import_promises.stat;
|
|
9715
10728
|
if (wantBigintFsStats) {
|
|
9716
|
-
this._stat = (
|
|
10729
|
+
this._stat = (path24) => statMethod(path24, { bigint: true });
|
|
9717
10730
|
} else {
|
|
9718
10731
|
this._stat = statMethod;
|
|
9719
10732
|
}
|
|
@@ -9738,8 +10751,8 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
9738
10751
|
const par = this.parent;
|
|
9739
10752
|
const fil = par && par.files;
|
|
9740
10753
|
if (fil && fil.length > 0) {
|
|
9741
|
-
const { path:
|
|
9742
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
10754
|
+
const { path: path24, depth } = par;
|
|
10755
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path24));
|
|
9743
10756
|
const awaited = await Promise.all(slice);
|
|
9744
10757
|
for (const entry of awaited) {
|
|
9745
10758
|
if (!entry)
|
|
@@ -9779,20 +10792,20 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
9779
10792
|
this.reading = false;
|
|
9780
10793
|
}
|
|
9781
10794
|
}
|
|
9782
|
-
async _exploreDir(
|
|
10795
|
+
async _exploreDir(path24, depth) {
|
|
9783
10796
|
let files;
|
|
9784
10797
|
try {
|
|
9785
|
-
files = await (0, import_promises.readdir)(
|
|
10798
|
+
files = await (0, import_promises.readdir)(path24, this._rdOptions);
|
|
9786
10799
|
} catch (error) {
|
|
9787
10800
|
this._onError(error);
|
|
9788
10801
|
}
|
|
9789
|
-
return { files, depth, path:
|
|
10802
|
+
return { files, depth, path: path24 };
|
|
9790
10803
|
}
|
|
9791
|
-
async _formatEntry(dirent,
|
|
10804
|
+
async _formatEntry(dirent, path24) {
|
|
9792
10805
|
let entry;
|
|
9793
10806
|
const basename5 = this._isDirent ? dirent.name : dirent;
|
|
9794
10807
|
try {
|
|
9795
|
-
const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(
|
|
10808
|
+
const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path24, basename5));
|
|
9796
10809
|
entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename5 };
|
|
9797
10810
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
9798
10811
|
} catch (err) {
|
|
@@ -10192,16 +11205,16 @@ var delFromSet = (main, prop, item) => {
|
|
|
10192
11205
|
};
|
|
10193
11206
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
10194
11207
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
10195
|
-
function createFsWatchInstance(
|
|
11208
|
+
function createFsWatchInstance(path24, options, listener, errHandler, emitRaw) {
|
|
10196
11209
|
const handleEvent = (rawEvent, evPath) => {
|
|
10197
|
-
listener(
|
|
10198
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
10199
|
-
if (evPath &&
|
|
10200
|
-
fsWatchBroadcast(sp.resolve(
|
|
11210
|
+
listener(path24);
|
|
11211
|
+
emitRaw(rawEvent, evPath, { watchedPath: path24 });
|
|
11212
|
+
if (evPath && path24 !== evPath) {
|
|
11213
|
+
fsWatchBroadcast(sp.resolve(path24, evPath), KEY_LISTENERS, sp.join(path24, evPath));
|
|
10201
11214
|
}
|
|
10202
11215
|
};
|
|
10203
11216
|
try {
|
|
10204
|
-
return (0, import_node_fs.watch)(
|
|
11217
|
+
return (0, import_node_fs.watch)(path24, {
|
|
10205
11218
|
persistent: options.persistent
|
|
10206
11219
|
}, handleEvent);
|
|
10207
11220
|
} catch (error) {
|
|
@@ -10217,12 +11230,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
10217
11230
|
listener(val1, val2, val3);
|
|
10218
11231
|
});
|
|
10219
11232
|
};
|
|
10220
|
-
var setFsWatchListener = (
|
|
11233
|
+
var setFsWatchListener = (path24, fullPath, options, handlers) => {
|
|
10221
11234
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
10222
11235
|
let cont = FsWatchInstances.get(fullPath);
|
|
10223
11236
|
let watcher;
|
|
10224
11237
|
if (!options.persistent) {
|
|
10225
|
-
watcher = createFsWatchInstance(
|
|
11238
|
+
watcher = createFsWatchInstance(path24, options, listener, errHandler, rawEmitter);
|
|
10226
11239
|
if (!watcher)
|
|
10227
11240
|
return;
|
|
10228
11241
|
return watcher.close.bind(watcher);
|
|
@@ -10233,7 +11246,7 @@ var setFsWatchListener = (path23, fullPath, options, handlers) => {
|
|
|
10233
11246
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
10234
11247
|
} else {
|
|
10235
11248
|
watcher = createFsWatchInstance(
|
|
10236
|
-
|
|
11249
|
+
path24,
|
|
10237
11250
|
options,
|
|
10238
11251
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
10239
11252
|
errHandler,
|
|
@@ -10248,7 +11261,7 @@ var setFsWatchListener = (path23, fullPath, options, handlers) => {
|
|
|
10248
11261
|
cont.watcherUnusable = true;
|
|
10249
11262
|
if (isWindows && error.code === "EPERM") {
|
|
10250
11263
|
try {
|
|
10251
|
-
const fd = await (0, import_promises2.open)(
|
|
11264
|
+
const fd = await (0, import_promises2.open)(path24, "r");
|
|
10252
11265
|
await fd.close();
|
|
10253
11266
|
broadcastErr(error);
|
|
10254
11267
|
} catch (err) {
|
|
@@ -10279,7 +11292,7 @@ var setFsWatchListener = (path23, fullPath, options, handlers) => {
|
|
|
10279
11292
|
};
|
|
10280
11293
|
};
|
|
10281
11294
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
10282
|
-
var setFsWatchFileListener = (
|
|
11295
|
+
var setFsWatchFileListener = (path24, fullPath, options, handlers) => {
|
|
10283
11296
|
const { listener, rawEmitter } = handlers;
|
|
10284
11297
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
10285
11298
|
const copts = cont && cont.options;
|
|
@@ -10301,7 +11314,7 @@ var setFsWatchFileListener = (path23, fullPath, options, handlers) => {
|
|
|
10301
11314
|
});
|
|
10302
11315
|
const currmtime = curr.mtimeMs;
|
|
10303
11316
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
10304
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
11317
|
+
foreach(cont.listeners, (listener2) => listener2(path24, curr));
|
|
10305
11318
|
}
|
|
10306
11319
|
})
|
|
10307
11320
|
};
|
|
@@ -10331,13 +11344,13 @@ var NodeFsHandler = class {
|
|
|
10331
11344
|
* @param listener on fs change
|
|
10332
11345
|
* @returns closer for the watcher instance
|
|
10333
11346
|
*/
|
|
10334
|
-
_watchWithNodeFs(
|
|
11347
|
+
_watchWithNodeFs(path24, listener) {
|
|
10335
11348
|
const opts = this.fsw.options;
|
|
10336
|
-
const directory = sp.dirname(
|
|
10337
|
-
const basename5 = sp.basename(
|
|
11349
|
+
const directory = sp.dirname(path24);
|
|
11350
|
+
const basename5 = sp.basename(path24);
|
|
10338
11351
|
const parent = this.fsw._getWatchedDir(directory);
|
|
10339
11352
|
parent.add(basename5);
|
|
10340
|
-
const absolutePath = sp.resolve(
|
|
11353
|
+
const absolutePath = sp.resolve(path24);
|
|
10341
11354
|
const options = {
|
|
10342
11355
|
persistent: opts.persistent
|
|
10343
11356
|
};
|
|
@@ -10347,12 +11360,12 @@ var NodeFsHandler = class {
|
|
|
10347
11360
|
if (opts.usePolling) {
|
|
10348
11361
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
10349
11362
|
options.interval = enableBin && isBinaryPath(basename5) ? opts.binaryInterval : opts.interval;
|
|
10350
|
-
closer = setFsWatchFileListener(
|
|
11363
|
+
closer = setFsWatchFileListener(path24, absolutePath, options, {
|
|
10351
11364
|
listener,
|
|
10352
11365
|
rawEmitter: this.fsw._emitRaw
|
|
10353
11366
|
});
|
|
10354
11367
|
} else {
|
|
10355
|
-
closer = setFsWatchListener(
|
|
11368
|
+
closer = setFsWatchListener(path24, absolutePath, options, {
|
|
10356
11369
|
listener,
|
|
10357
11370
|
errHandler: this._boundHandleError,
|
|
10358
11371
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -10374,7 +11387,7 @@ var NodeFsHandler = class {
|
|
|
10374
11387
|
let prevStats = stats;
|
|
10375
11388
|
if (parent.has(basename5))
|
|
10376
11389
|
return;
|
|
10377
|
-
const listener = async (
|
|
11390
|
+
const listener = async (path24, newStats) => {
|
|
10378
11391
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
10379
11392
|
return;
|
|
10380
11393
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -10388,11 +11401,11 @@ var NodeFsHandler = class {
|
|
|
10388
11401
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
10389
11402
|
}
|
|
10390
11403
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
10391
|
-
this.fsw._closeFile(
|
|
11404
|
+
this.fsw._closeFile(path24);
|
|
10392
11405
|
prevStats = newStats2;
|
|
10393
11406
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
10394
11407
|
if (closer2)
|
|
10395
|
-
this.fsw._addPathCloser(
|
|
11408
|
+
this.fsw._addPathCloser(path24, closer2);
|
|
10396
11409
|
} else {
|
|
10397
11410
|
prevStats = newStats2;
|
|
10398
11411
|
}
|
|
@@ -10424,7 +11437,7 @@ var NodeFsHandler = class {
|
|
|
10424
11437
|
* @param item basename of this item
|
|
10425
11438
|
* @returns true if no more processing is needed for this entry.
|
|
10426
11439
|
*/
|
|
10427
|
-
async _handleSymlink(entry, directory,
|
|
11440
|
+
async _handleSymlink(entry, directory, path24, item) {
|
|
10428
11441
|
if (this.fsw.closed) {
|
|
10429
11442
|
return;
|
|
10430
11443
|
}
|
|
@@ -10434,7 +11447,7 @@ var NodeFsHandler = class {
|
|
|
10434
11447
|
this.fsw._incrReadyCount();
|
|
10435
11448
|
let linkPath;
|
|
10436
11449
|
try {
|
|
10437
|
-
linkPath = await (0, import_promises2.realpath)(
|
|
11450
|
+
linkPath = await (0, import_promises2.realpath)(path24);
|
|
10438
11451
|
} catch (e) {
|
|
10439
11452
|
this.fsw._emitReady();
|
|
10440
11453
|
return true;
|
|
@@ -10444,12 +11457,12 @@ var NodeFsHandler = class {
|
|
|
10444
11457
|
if (dir.has(item)) {
|
|
10445
11458
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
10446
11459
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
10447
|
-
this.fsw._emit(EV.CHANGE,
|
|
11460
|
+
this.fsw._emit(EV.CHANGE, path24, entry.stats);
|
|
10448
11461
|
}
|
|
10449
11462
|
} else {
|
|
10450
11463
|
dir.add(item);
|
|
10451
11464
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
10452
|
-
this.fsw._emit(EV.ADD,
|
|
11465
|
+
this.fsw._emit(EV.ADD, path24, entry.stats);
|
|
10453
11466
|
}
|
|
10454
11467
|
this.fsw._emitReady();
|
|
10455
11468
|
return true;
|
|
@@ -10479,9 +11492,9 @@ var NodeFsHandler = class {
|
|
|
10479
11492
|
return;
|
|
10480
11493
|
}
|
|
10481
11494
|
const item = entry.path;
|
|
10482
|
-
let
|
|
11495
|
+
let path24 = sp.join(directory, item);
|
|
10483
11496
|
current.add(item);
|
|
10484
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
11497
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path24, item)) {
|
|
10485
11498
|
return;
|
|
10486
11499
|
}
|
|
10487
11500
|
if (this.fsw.closed) {
|
|
@@ -10490,8 +11503,8 @@ var NodeFsHandler = class {
|
|
|
10490
11503
|
}
|
|
10491
11504
|
if (item === target || !target && !previous.has(item)) {
|
|
10492
11505
|
this.fsw._incrReadyCount();
|
|
10493
|
-
|
|
10494
|
-
this._addToNodeFs(
|
|
11506
|
+
path24 = sp.join(dir, sp.relative(dir, path24));
|
|
11507
|
+
this._addToNodeFs(path24, initialAdd, wh, depth + 1);
|
|
10495
11508
|
}
|
|
10496
11509
|
}).on(EV.ERROR, this._boundHandleError);
|
|
10497
11510
|
return new Promise((resolve13, reject) => {
|
|
@@ -10560,13 +11573,13 @@ var NodeFsHandler = class {
|
|
|
10560
11573
|
* @param depth Child path actually targeted for watch
|
|
10561
11574
|
* @param target Child path actually targeted for watch
|
|
10562
11575
|
*/
|
|
10563
|
-
async _addToNodeFs(
|
|
11576
|
+
async _addToNodeFs(path24, initialAdd, priorWh, depth, target) {
|
|
10564
11577
|
const ready = this.fsw._emitReady;
|
|
10565
|
-
if (this.fsw._isIgnored(
|
|
11578
|
+
if (this.fsw._isIgnored(path24) || this.fsw.closed) {
|
|
10566
11579
|
ready();
|
|
10567
11580
|
return false;
|
|
10568
11581
|
}
|
|
10569
|
-
const wh = this.fsw._getWatchHelpers(
|
|
11582
|
+
const wh = this.fsw._getWatchHelpers(path24);
|
|
10570
11583
|
if (priorWh) {
|
|
10571
11584
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
10572
11585
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -10582,8 +11595,8 @@ var NodeFsHandler = class {
|
|
|
10582
11595
|
const follow = this.fsw.options.followSymlinks;
|
|
10583
11596
|
let closer;
|
|
10584
11597
|
if (stats.isDirectory()) {
|
|
10585
|
-
const absPath = sp.resolve(
|
|
10586
|
-
const targetPath = follow ? await (0, import_promises2.realpath)(
|
|
11598
|
+
const absPath = sp.resolve(path24);
|
|
11599
|
+
const targetPath = follow ? await (0, import_promises2.realpath)(path24) : path24;
|
|
10587
11600
|
if (this.fsw.closed)
|
|
10588
11601
|
return;
|
|
10589
11602
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -10593,29 +11606,29 @@ var NodeFsHandler = class {
|
|
|
10593
11606
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
10594
11607
|
}
|
|
10595
11608
|
} else if (stats.isSymbolicLink()) {
|
|
10596
|
-
const targetPath = follow ? await (0, import_promises2.realpath)(
|
|
11609
|
+
const targetPath = follow ? await (0, import_promises2.realpath)(path24) : path24;
|
|
10597
11610
|
if (this.fsw.closed)
|
|
10598
11611
|
return;
|
|
10599
11612
|
const parent = sp.dirname(wh.watchPath);
|
|
10600
11613
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
10601
11614
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
10602
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
11615
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path24, wh, targetPath);
|
|
10603
11616
|
if (this.fsw.closed)
|
|
10604
11617
|
return;
|
|
10605
11618
|
if (targetPath !== void 0) {
|
|
10606
|
-
this.fsw._symlinkPaths.set(sp.resolve(
|
|
11619
|
+
this.fsw._symlinkPaths.set(sp.resolve(path24), targetPath);
|
|
10607
11620
|
}
|
|
10608
11621
|
} else {
|
|
10609
11622
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
10610
11623
|
}
|
|
10611
11624
|
ready();
|
|
10612
11625
|
if (closer)
|
|
10613
|
-
this.fsw._addPathCloser(
|
|
11626
|
+
this.fsw._addPathCloser(path24, closer);
|
|
10614
11627
|
return false;
|
|
10615
11628
|
} catch (error) {
|
|
10616
11629
|
if (this.fsw._handleError(error)) {
|
|
10617
11630
|
ready();
|
|
10618
|
-
return
|
|
11631
|
+
return path24;
|
|
10619
11632
|
}
|
|
10620
11633
|
}
|
|
10621
11634
|
}
|
|
@@ -10658,24 +11671,24 @@ function createPattern(matcher) {
|
|
|
10658
11671
|
}
|
|
10659
11672
|
return () => false;
|
|
10660
11673
|
}
|
|
10661
|
-
function normalizePath(
|
|
10662
|
-
if (typeof
|
|
11674
|
+
function normalizePath(path24) {
|
|
11675
|
+
if (typeof path24 !== "string")
|
|
10663
11676
|
throw new Error("string expected");
|
|
10664
|
-
|
|
10665
|
-
|
|
11677
|
+
path24 = sp2.normalize(path24);
|
|
11678
|
+
path24 = path24.replace(/\\/g, "/");
|
|
10666
11679
|
let prepend = false;
|
|
10667
|
-
if (
|
|
11680
|
+
if (path24.startsWith("//"))
|
|
10668
11681
|
prepend = true;
|
|
10669
|
-
|
|
11682
|
+
path24 = path24.replace(DOUBLE_SLASH_RE, "/");
|
|
10670
11683
|
if (prepend)
|
|
10671
|
-
|
|
10672
|
-
return
|
|
11684
|
+
path24 = "/" + path24;
|
|
11685
|
+
return path24;
|
|
10673
11686
|
}
|
|
10674
11687
|
function matchPatterns(patterns, testString, stats) {
|
|
10675
|
-
const
|
|
11688
|
+
const path24 = normalizePath(testString);
|
|
10676
11689
|
for (let index = 0; index < patterns.length; index++) {
|
|
10677
11690
|
const pattern = patterns[index];
|
|
10678
|
-
if (pattern(
|
|
11691
|
+
if (pattern(path24, stats)) {
|
|
10679
11692
|
return true;
|
|
10680
11693
|
}
|
|
10681
11694
|
}
|
|
@@ -10713,19 +11726,19 @@ var toUnix = (string) => {
|
|
|
10713
11726
|
}
|
|
10714
11727
|
return str;
|
|
10715
11728
|
};
|
|
10716
|
-
var normalizePathToUnix = (
|
|
10717
|
-
var normalizeIgnored = (cwd = "") => (
|
|
10718
|
-
if (typeof
|
|
10719
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
11729
|
+
var normalizePathToUnix = (path24) => toUnix(sp2.normalize(toUnix(path24)));
|
|
11730
|
+
var normalizeIgnored = (cwd = "") => (path24) => {
|
|
11731
|
+
if (typeof path24 === "string") {
|
|
11732
|
+
return normalizePathToUnix(sp2.isAbsolute(path24) ? path24 : sp2.join(cwd, path24));
|
|
10720
11733
|
} else {
|
|
10721
|
-
return
|
|
11734
|
+
return path24;
|
|
10722
11735
|
}
|
|
10723
11736
|
};
|
|
10724
|
-
var getAbsolutePath = (
|
|
10725
|
-
if (sp2.isAbsolute(
|
|
10726
|
-
return
|
|
11737
|
+
var getAbsolutePath = (path24, cwd) => {
|
|
11738
|
+
if (sp2.isAbsolute(path24)) {
|
|
11739
|
+
return path24;
|
|
10727
11740
|
}
|
|
10728
|
-
return sp2.join(cwd,
|
|
11741
|
+
return sp2.join(cwd, path24);
|
|
10729
11742
|
};
|
|
10730
11743
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
10731
11744
|
var DirEntry = class {
|
|
@@ -10790,10 +11803,10 @@ var WatchHelper = class {
|
|
|
10790
11803
|
dirParts;
|
|
10791
11804
|
followSymlinks;
|
|
10792
11805
|
statMethod;
|
|
10793
|
-
constructor(
|
|
11806
|
+
constructor(path24, follow, fsw) {
|
|
10794
11807
|
this.fsw = fsw;
|
|
10795
|
-
const watchPath =
|
|
10796
|
-
this.path =
|
|
11808
|
+
const watchPath = path24;
|
|
11809
|
+
this.path = path24 = path24.replace(REPLACER_RE, "");
|
|
10797
11810
|
this.watchPath = watchPath;
|
|
10798
11811
|
this.fullWatchPath = sp2.resolve(watchPath);
|
|
10799
11812
|
this.dirParts = [];
|
|
@@ -10933,20 +11946,20 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
10933
11946
|
this._closePromise = void 0;
|
|
10934
11947
|
let paths = unifyPaths(paths_);
|
|
10935
11948
|
if (cwd) {
|
|
10936
|
-
paths = paths.map((
|
|
10937
|
-
const absPath = getAbsolutePath(
|
|
11949
|
+
paths = paths.map((path24) => {
|
|
11950
|
+
const absPath = getAbsolutePath(path24, cwd);
|
|
10938
11951
|
return absPath;
|
|
10939
11952
|
});
|
|
10940
11953
|
}
|
|
10941
|
-
paths.forEach((
|
|
10942
|
-
this._removeIgnoredPath(
|
|
11954
|
+
paths.forEach((path24) => {
|
|
11955
|
+
this._removeIgnoredPath(path24);
|
|
10943
11956
|
});
|
|
10944
11957
|
this._userIgnored = void 0;
|
|
10945
11958
|
if (!this._readyCount)
|
|
10946
11959
|
this._readyCount = 0;
|
|
10947
11960
|
this._readyCount += paths.length;
|
|
10948
|
-
Promise.all(paths.map(async (
|
|
10949
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
11961
|
+
Promise.all(paths.map(async (path24) => {
|
|
11962
|
+
const res = await this._nodeFsHandler._addToNodeFs(path24, !_internal, void 0, 0, _origAdd);
|
|
10950
11963
|
if (res)
|
|
10951
11964
|
this._emitReady();
|
|
10952
11965
|
return res;
|
|
@@ -10968,17 +11981,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
10968
11981
|
return this;
|
|
10969
11982
|
const paths = unifyPaths(paths_);
|
|
10970
11983
|
const { cwd } = this.options;
|
|
10971
|
-
paths.forEach((
|
|
10972
|
-
if (!sp2.isAbsolute(
|
|
11984
|
+
paths.forEach((path24) => {
|
|
11985
|
+
if (!sp2.isAbsolute(path24) && !this._closers.has(path24)) {
|
|
10973
11986
|
if (cwd)
|
|
10974
|
-
|
|
10975
|
-
|
|
11987
|
+
path24 = sp2.join(cwd, path24);
|
|
11988
|
+
path24 = sp2.resolve(path24);
|
|
10976
11989
|
}
|
|
10977
|
-
this._closePath(
|
|
10978
|
-
this._addIgnoredPath(
|
|
10979
|
-
if (this._watched.has(
|
|
11990
|
+
this._closePath(path24);
|
|
11991
|
+
this._addIgnoredPath(path24);
|
|
11992
|
+
if (this._watched.has(path24)) {
|
|
10980
11993
|
this._addIgnoredPath({
|
|
10981
|
-
path:
|
|
11994
|
+
path: path24,
|
|
10982
11995
|
recursive: true
|
|
10983
11996
|
});
|
|
10984
11997
|
}
|
|
@@ -11042,38 +12055,38 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
11042
12055
|
* @param stats arguments to be passed with event
|
|
11043
12056
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
11044
12057
|
*/
|
|
11045
|
-
async _emit(event,
|
|
12058
|
+
async _emit(event, path24, stats) {
|
|
11046
12059
|
if (this.closed)
|
|
11047
12060
|
return;
|
|
11048
12061
|
const opts = this.options;
|
|
11049
12062
|
if (isWindows)
|
|
11050
|
-
|
|
12063
|
+
path24 = sp2.normalize(path24);
|
|
11051
12064
|
if (opts.cwd)
|
|
11052
|
-
|
|
11053
|
-
const args = [
|
|
12065
|
+
path24 = sp2.relative(opts.cwd, path24);
|
|
12066
|
+
const args = [path24];
|
|
11054
12067
|
if (stats != null)
|
|
11055
12068
|
args.push(stats);
|
|
11056
12069
|
const awf = opts.awaitWriteFinish;
|
|
11057
12070
|
let pw;
|
|
11058
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
12071
|
+
if (awf && (pw = this._pendingWrites.get(path24))) {
|
|
11059
12072
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
11060
12073
|
return this;
|
|
11061
12074
|
}
|
|
11062
12075
|
if (opts.atomic) {
|
|
11063
12076
|
if (event === EVENTS.UNLINK) {
|
|
11064
|
-
this._pendingUnlinks.set(
|
|
12077
|
+
this._pendingUnlinks.set(path24, [event, ...args]);
|
|
11065
12078
|
setTimeout(() => {
|
|
11066
|
-
this._pendingUnlinks.forEach((entry,
|
|
12079
|
+
this._pendingUnlinks.forEach((entry, path25) => {
|
|
11067
12080
|
this.emit(...entry);
|
|
11068
12081
|
this.emit(EVENTS.ALL, ...entry);
|
|
11069
|
-
this._pendingUnlinks.delete(
|
|
12082
|
+
this._pendingUnlinks.delete(path25);
|
|
11070
12083
|
});
|
|
11071
12084
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
11072
12085
|
return this;
|
|
11073
12086
|
}
|
|
11074
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
12087
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path24)) {
|
|
11075
12088
|
event = EVENTS.CHANGE;
|
|
11076
|
-
this._pendingUnlinks.delete(
|
|
12089
|
+
this._pendingUnlinks.delete(path24);
|
|
11077
12090
|
}
|
|
11078
12091
|
}
|
|
11079
12092
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -11091,16 +12104,16 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
11091
12104
|
this.emitWithAll(event, args);
|
|
11092
12105
|
}
|
|
11093
12106
|
};
|
|
11094
|
-
this._awaitWriteFinish(
|
|
12107
|
+
this._awaitWriteFinish(path24, awf.stabilityThreshold, event, awfEmit);
|
|
11095
12108
|
return this;
|
|
11096
12109
|
}
|
|
11097
12110
|
if (event === EVENTS.CHANGE) {
|
|
11098
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
12111
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path24, 50);
|
|
11099
12112
|
if (isThrottled)
|
|
11100
12113
|
return this;
|
|
11101
12114
|
}
|
|
11102
12115
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
11103
|
-
const fullPath = opts.cwd ? sp2.join(opts.cwd,
|
|
12116
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path24) : path24;
|
|
11104
12117
|
let stats2;
|
|
11105
12118
|
try {
|
|
11106
12119
|
stats2 = await (0, import_promises3.stat)(fullPath);
|
|
@@ -11131,23 +12144,23 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
11131
12144
|
* @param timeout duration of time to suppress duplicate actions
|
|
11132
12145
|
* @returns tracking object or false if action should be suppressed
|
|
11133
12146
|
*/
|
|
11134
|
-
_throttle(actionType,
|
|
12147
|
+
_throttle(actionType, path24, timeout) {
|
|
11135
12148
|
if (!this._throttled.has(actionType)) {
|
|
11136
12149
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
11137
12150
|
}
|
|
11138
12151
|
const action = this._throttled.get(actionType);
|
|
11139
12152
|
if (!action)
|
|
11140
12153
|
throw new Error("invalid throttle");
|
|
11141
|
-
const actionPath = action.get(
|
|
12154
|
+
const actionPath = action.get(path24);
|
|
11142
12155
|
if (actionPath) {
|
|
11143
12156
|
actionPath.count++;
|
|
11144
12157
|
return false;
|
|
11145
12158
|
}
|
|
11146
12159
|
let timeoutObject;
|
|
11147
12160
|
const clear = () => {
|
|
11148
|
-
const item = action.get(
|
|
12161
|
+
const item = action.get(path24);
|
|
11149
12162
|
const count = item ? item.count : 0;
|
|
11150
|
-
action.delete(
|
|
12163
|
+
action.delete(path24);
|
|
11151
12164
|
clearTimeout(timeoutObject);
|
|
11152
12165
|
if (item)
|
|
11153
12166
|
clearTimeout(item.timeoutObject);
|
|
@@ -11155,7 +12168,7 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
11155
12168
|
};
|
|
11156
12169
|
timeoutObject = setTimeout(clear, timeout);
|
|
11157
12170
|
const thr = { timeoutObject, clear, count: 0 };
|
|
11158
|
-
action.set(
|
|
12171
|
+
action.set(path24, thr);
|
|
11159
12172
|
return thr;
|
|
11160
12173
|
}
|
|
11161
12174
|
_incrReadyCount() {
|
|
@@ -11169,44 +12182,44 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
11169
12182
|
* @param event
|
|
11170
12183
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
11171
12184
|
*/
|
|
11172
|
-
_awaitWriteFinish(
|
|
12185
|
+
_awaitWriteFinish(path24, threshold, event, awfEmit) {
|
|
11173
12186
|
const awf = this.options.awaitWriteFinish;
|
|
11174
12187
|
if (typeof awf !== "object")
|
|
11175
12188
|
return;
|
|
11176
12189
|
const pollInterval = awf.pollInterval;
|
|
11177
12190
|
let timeoutHandler;
|
|
11178
|
-
let fullPath =
|
|
11179
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
11180
|
-
fullPath = sp2.join(this.options.cwd,
|
|
12191
|
+
let fullPath = path24;
|
|
12192
|
+
if (this.options.cwd && !sp2.isAbsolute(path24)) {
|
|
12193
|
+
fullPath = sp2.join(this.options.cwd, path24);
|
|
11181
12194
|
}
|
|
11182
12195
|
const now = /* @__PURE__ */ new Date();
|
|
11183
12196
|
const writes = this._pendingWrites;
|
|
11184
12197
|
function awaitWriteFinishFn(prevStat) {
|
|
11185
12198
|
(0, import_node_fs2.stat)(fullPath, (err, curStat) => {
|
|
11186
|
-
if (err || !writes.has(
|
|
12199
|
+
if (err || !writes.has(path24)) {
|
|
11187
12200
|
if (err && err.code !== "ENOENT")
|
|
11188
12201
|
awfEmit(err);
|
|
11189
12202
|
return;
|
|
11190
12203
|
}
|
|
11191
12204
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
11192
12205
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
11193
|
-
writes.get(
|
|
12206
|
+
writes.get(path24).lastChange = now2;
|
|
11194
12207
|
}
|
|
11195
|
-
const pw = writes.get(
|
|
12208
|
+
const pw = writes.get(path24);
|
|
11196
12209
|
const df = now2 - pw.lastChange;
|
|
11197
12210
|
if (df >= threshold) {
|
|
11198
|
-
writes.delete(
|
|
12211
|
+
writes.delete(path24);
|
|
11199
12212
|
awfEmit(void 0, curStat);
|
|
11200
12213
|
} else {
|
|
11201
12214
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
11202
12215
|
}
|
|
11203
12216
|
});
|
|
11204
12217
|
}
|
|
11205
|
-
if (!writes.has(
|
|
11206
|
-
writes.set(
|
|
12218
|
+
if (!writes.has(path24)) {
|
|
12219
|
+
writes.set(path24, {
|
|
11207
12220
|
lastChange: now,
|
|
11208
12221
|
cancelWait: () => {
|
|
11209
|
-
writes.delete(
|
|
12222
|
+
writes.delete(path24);
|
|
11210
12223
|
clearTimeout(timeoutHandler);
|
|
11211
12224
|
return event;
|
|
11212
12225
|
}
|
|
@@ -11217,8 +12230,8 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
11217
12230
|
/**
|
|
11218
12231
|
* Determines whether user has asked to ignore this path.
|
|
11219
12232
|
*/
|
|
11220
|
-
_isIgnored(
|
|
11221
|
-
if (this.options.atomic && DOT_RE.test(
|
|
12233
|
+
_isIgnored(path24, stats) {
|
|
12234
|
+
if (this.options.atomic && DOT_RE.test(path24))
|
|
11222
12235
|
return true;
|
|
11223
12236
|
if (!this._userIgnored) {
|
|
11224
12237
|
const { cwd } = this.options;
|
|
@@ -11228,17 +12241,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
11228
12241
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
11229
12242
|
this._userIgnored = anymatch(list, void 0);
|
|
11230
12243
|
}
|
|
11231
|
-
return this._userIgnored(
|
|
12244
|
+
return this._userIgnored(path24, stats);
|
|
11232
12245
|
}
|
|
11233
|
-
_isntIgnored(
|
|
11234
|
-
return !this._isIgnored(
|
|
12246
|
+
_isntIgnored(path24, stat4) {
|
|
12247
|
+
return !this._isIgnored(path24, stat4);
|
|
11235
12248
|
}
|
|
11236
12249
|
/**
|
|
11237
12250
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
11238
12251
|
* @param path file or directory pattern being watched
|
|
11239
12252
|
*/
|
|
11240
|
-
_getWatchHelpers(
|
|
11241
|
-
return new WatchHelper(
|
|
12253
|
+
_getWatchHelpers(path24) {
|
|
12254
|
+
return new WatchHelper(path24, this.options.followSymlinks, this);
|
|
11242
12255
|
}
|
|
11243
12256
|
// Directory helpers
|
|
11244
12257
|
// -----------------
|
|
@@ -11270,63 +12283,63 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
11270
12283
|
* @param item base path of item/directory
|
|
11271
12284
|
*/
|
|
11272
12285
|
_remove(directory, item, isDirectory) {
|
|
11273
|
-
const
|
|
11274
|
-
const fullPath = sp2.resolve(
|
|
11275
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
11276
|
-
if (!this._throttle("remove",
|
|
12286
|
+
const path24 = sp2.join(directory, item);
|
|
12287
|
+
const fullPath = sp2.resolve(path24);
|
|
12288
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path24) || this._watched.has(fullPath);
|
|
12289
|
+
if (!this._throttle("remove", path24, 100))
|
|
11277
12290
|
return;
|
|
11278
12291
|
if (!isDirectory && this._watched.size === 1) {
|
|
11279
12292
|
this.add(directory, item, true);
|
|
11280
12293
|
}
|
|
11281
|
-
const wp = this._getWatchedDir(
|
|
12294
|
+
const wp = this._getWatchedDir(path24);
|
|
11282
12295
|
const nestedDirectoryChildren = wp.getChildren();
|
|
11283
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
12296
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path24, nested));
|
|
11284
12297
|
const parent = this._getWatchedDir(directory);
|
|
11285
12298
|
const wasTracked = parent.has(item);
|
|
11286
12299
|
parent.remove(item);
|
|
11287
12300
|
if (this._symlinkPaths.has(fullPath)) {
|
|
11288
12301
|
this._symlinkPaths.delete(fullPath);
|
|
11289
12302
|
}
|
|
11290
|
-
let relPath =
|
|
12303
|
+
let relPath = path24;
|
|
11291
12304
|
if (this.options.cwd)
|
|
11292
|
-
relPath = sp2.relative(this.options.cwd,
|
|
12305
|
+
relPath = sp2.relative(this.options.cwd, path24);
|
|
11293
12306
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
11294
12307
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
11295
12308
|
if (event === EVENTS.ADD)
|
|
11296
12309
|
return;
|
|
11297
12310
|
}
|
|
11298
|
-
this._watched.delete(
|
|
12311
|
+
this._watched.delete(path24);
|
|
11299
12312
|
this._watched.delete(fullPath);
|
|
11300
12313
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
11301
|
-
if (wasTracked && !this._isIgnored(
|
|
11302
|
-
this._emit(eventName,
|
|
11303
|
-
this._closePath(
|
|
12314
|
+
if (wasTracked && !this._isIgnored(path24))
|
|
12315
|
+
this._emit(eventName, path24);
|
|
12316
|
+
this._closePath(path24);
|
|
11304
12317
|
}
|
|
11305
12318
|
/**
|
|
11306
12319
|
* Closes all watchers for a path
|
|
11307
12320
|
*/
|
|
11308
|
-
_closePath(
|
|
11309
|
-
this._closeFile(
|
|
11310
|
-
const dir = sp2.dirname(
|
|
11311
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
12321
|
+
_closePath(path24) {
|
|
12322
|
+
this._closeFile(path24);
|
|
12323
|
+
const dir = sp2.dirname(path24);
|
|
12324
|
+
this._getWatchedDir(dir).remove(sp2.basename(path24));
|
|
11312
12325
|
}
|
|
11313
12326
|
/**
|
|
11314
12327
|
* Closes only file-specific watchers
|
|
11315
12328
|
*/
|
|
11316
|
-
_closeFile(
|
|
11317
|
-
const closers = this._closers.get(
|
|
12329
|
+
_closeFile(path24) {
|
|
12330
|
+
const closers = this._closers.get(path24);
|
|
11318
12331
|
if (!closers)
|
|
11319
12332
|
return;
|
|
11320
12333
|
closers.forEach((closer) => closer());
|
|
11321
|
-
this._closers.delete(
|
|
12334
|
+
this._closers.delete(path24);
|
|
11322
12335
|
}
|
|
11323
|
-
_addPathCloser(
|
|
12336
|
+
_addPathCloser(path24, closer) {
|
|
11324
12337
|
if (!closer)
|
|
11325
12338
|
return;
|
|
11326
|
-
let list = this._closers.get(
|
|
12339
|
+
let list = this._closers.get(path24);
|
|
11327
12340
|
if (!list) {
|
|
11328
12341
|
list = [];
|
|
11329
|
-
this._closers.set(
|
|
12342
|
+
this._closers.set(path24, list);
|
|
11330
12343
|
}
|
|
11331
12344
|
list.push(closer);
|
|
11332
12345
|
}
|
|
@@ -11356,7 +12369,7 @@ function watch(paths, options = {}) {
|
|
|
11356
12369
|
var chokidar_default = { watch, FSWatcher };
|
|
11357
12370
|
|
|
11358
12371
|
// src/watcher/file-watcher.ts
|
|
11359
|
-
var
|
|
12372
|
+
var path17 = __toESM(require("path"), 1);
|
|
11360
12373
|
var FileWatcher = class {
|
|
11361
12374
|
watcher = null;
|
|
11362
12375
|
projectRoot;
|
|
@@ -11367,6 +12380,8 @@ var FileWatcher = class {
|
|
|
11367
12380
|
debounceTimer = null;
|
|
11368
12381
|
debounceMs = 1e3;
|
|
11369
12382
|
onChanges = null;
|
|
12383
|
+
readyPromise = null;
|
|
12384
|
+
resolveReady = null;
|
|
11370
12385
|
constructor(projectRoot, config, host = "opencode", options = {}) {
|
|
11371
12386
|
this.projectRoot = projectRoot;
|
|
11372
12387
|
this.config = config;
|
|
@@ -11382,15 +12397,15 @@ var FileWatcher = class {
|
|
|
11382
12397
|
const watchTargets = this.configPath ? [this.projectRoot, this.configPath] : this.projectRoot;
|
|
11383
12398
|
this.watcher = chokidar_default.watch(watchTargets, {
|
|
11384
12399
|
ignored: (filePath) => {
|
|
11385
|
-
const relativePath =
|
|
12400
|
+
const relativePath = path17.relative(this.projectRoot, filePath);
|
|
11386
12401
|
if (!relativePath) return false;
|
|
11387
12402
|
if (this.isProjectConfigPathOrAncestor(relativePath)) {
|
|
11388
12403
|
return false;
|
|
11389
12404
|
}
|
|
11390
|
-
if (hasFilteredPathSegment(relativePath,
|
|
12405
|
+
if (hasFilteredPathSegment(relativePath, path17.sep)) {
|
|
11391
12406
|
return true;
|
|
11392
12407
|
}
|
|
11393
|
-
if (isRestrictedDirectory(relativePath,
|
|
12408
|
+
if (isRestrictedDirectory(relativePath, path17.sep)) {
|
|
11394
12409
|
return true;
|
|
11395
12410
|
}
|
|
11396
12411
|
if (ignoreFilter.ignores(relativePath)) {
|
|
@@ -11405,6 +12420,13 @@ var FileWatcher = class {
|
|
|
11405
12420
|
pollInterval: 100
|
|
11406
12421
|
}
|
|
11407
12422
|
});
|
|
12423
|
+
this.readyPromise = new Promise((resolve13) => {
|
|
12424
|
+
this.resolveReady = resolve13;
|
|
12425
|
+
});
|
|
12426
|
+
this.watcher.once("ready", () => {
|
|
12427
|
+
this.resolveReady?.();
|
|
12428
|
+
this.resolveReady = null;
|
|
12429
|
+
});
|
|
11408
12430
|
this.watcher.on("error", (error) => {
|
|
11409
12431
|
const err = error instanceof Error ? error : null;
|
|
11410
12432
|
if (err?.code === "EPERM" || err?.code === "EACCES") {
|
|
@@ -11436,24 +12458,24 @@ var FileWatcher = class {
|
|
|
11436
12458
|
this.scheduleFlush();
|
|
11437
12459
|
}
|
|
11438
12460
|
isProjectConfigPath(filePath) {
|
|
11439
|
-
const relativePath =
|
|
11440
|
-
const normalizedRelativePath =
|
|
12461
|
+
const relativePath = path17.relative(this.projectRoot, filePath);
|
|
12462
|
+
const normalizedRelativePath = path17.normalize(relativePath);
|
|
11441
12463
|
return this.getProjectConfigRelativePaths().some((configPath) => configPath === normalizedRelativePath);
|
|
11442
12464
|
}
|
|
11443
12465
|
isProjectConfigPathOrAncestor(relativePath) {
|
|
11444
|
-
const normalizedRelativePath =
|
|
12466
|
+
const normalizedRelativePath = path17.normalize(relativePath);
|
|
11445
12467
|
return this.getProjectConfigRelativePaths().some(
|
|
11446
|
-
(configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${
|
|
12468
|
+
(configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path17.sep}`)
|
|
11447
12469
|
);
|
|
11448
12470
|
}
|
|
11449
12471
|
getProjectConfigRelativePaths() {
|
|
11450
12472
|
if (this.configPath) {
|
|
11451
|
-
return [
|
|
12473
|
+
return [path17.normalize(path17.relative(this.projectRoot, this.configPath))];
|
|
11452
12474
|
}
|
|
11453
12475
|
return [
|
|
11454
12476
|
resolveProjectConfigPath(this.projectRoot, this.host),
|
|
11455
12477
|
resolveWritableProjectConfigPath(this.projectRoot, this.host)
|
|
11456
|
-
].map((configPath) =>
|
|
12478
|
+
].map((configPath) => path17.normalize(path17.relative(this.projectRoot, configPath)));
|
|
11457
12479
|
}
|
|
11458
12480
|
scheduleFlush() {
|
|
11459
12481
|
if (this.debounceTimer) {
|
|
@@ -11468,7 +12490,7 @@ var FileWatcher = class {
|
|
|
11468
12490
|
return;
|
|
11469
12491
|
}
|
|
11470
12492
|
const changes = Array.from(this.pendingChanges.entries()).map(
|
|
11471
|
-
([
|
|
12493
|
+
([path24, type]) => ({ path: path24, type })
|
|
11472
12494
|
);
|
|
11473
12495
|
this.pendingChanges.clear();
|
|
11474
12496
|
try {
|
|
@@ -11477,25 +12499,32 @@ var FileWatcher = class {
|
|
|
11477
12499
|
console.error("Error handling file changes:", error);
|
|
11478
12500
|
}
|
|
11479
12501
|
}
|
|
11480
|
-
stop() {
|
|
12502
|
+
async stop() {
|
|
11481
12503
|
if (this.debounceTimer) {
|
|
11482
12504
|
clearTimeout(this.debounceTimer);
|
|
11483
12505
|
this.debounceTimer = null;
|
|
11484
12506
|
}
|
|
11485
12507
|
if (this.watcher) {
|
|
11486
|
-
this.watcher
|
|
12508
|
+
const watcher = this.watcher;
|
|
11487
12509
|
this.watcher = null;
|
|
12510
|
+
await watcher.close();
|
|
11488
12511
|
}
|
|
12512
|
+
this.resolveReady?.();
|
|
12513
|
+
this.resolveReady = null;
|
|
12514
|
+
this.readyPromise = null;
|
|
11489
12515
|
this.pendingChanges.clear();
|
|
11490
12516
|
this.onChanges = null;
|
|
11491
12517
|
}
|
|
11492
12518
|
isRunning() {
|
|
11493
12519
|
return this.watcher !== null;
|
|
11494
12520
|
}
|
|
12521
|
+
async waitUntilReady() {
|
|
12522
|
+
await (this.readyPromise ?? Promise.resolve());
|
|
12523
|
+
}
|
|
11495
12524
|
};
|
|
11496
12525
|
|
|
11497
12526
|
// src/watcher/git-head-watcher.ts
|
|
11498
|
-
var
|
|
12527
|
+
var path18 = __toESM(require("path"), 1);
|
|
11499
12528
|
var GitHeadWatcher = class {
|
|
11500
12529
|
watcher = null;
|
|
11501
12530
|
projectRoot;
|
|
@@ -11517,7 +12546,7 @@ var GitHeadWatcher = class {
|
|
|
11517
12546
|
this.onBranchChange = handler;
|
|
11518
12547
|
this.currentBranch = getCurrentBranch(this.projectRoot);
|
|
11519
12548
|
const headPath = getHeadPath(this.projectRoot);
|
|
11520
|
-
const refsPath =
|
|
12549
|
+
const refsPath = path18.join(this.projectRoot, ".git", "refs", "heads");
|
|
11521
12550
|
this.watcher = chokidar_default.watch([headPath, refsPath], {
|
|
11522
12551
|
persistent: true,
|
|
11523
12552
|
ignoreInitial: true,
|
|
@@ -11554,14 +12583,15 @@ var GitHeadWatcher = class {
|
|
|
11554
12583
|
getCurrentBranch() {
|
|
11555
12584
|
return this.currentBranch;
|
|
11556
12585
|
}
|
|
11557
|
-
stop() {
|
|
12586
|
+
async stop() {
|
|
11558
12587
|
if (this.debounceTimer) {
|
|
11559
12588
|
clearTimeout(this.debounceTimer);
|
|
11560
12589
|
this.debounceTimer = null;
|
|
11561
12590
|
}
|
|
11562
12591
|
if (this.watcher) {
|
|
11563
|
-
this.watcher
|
|
12592
|
+
const watcher = this.watcher;
|
|
11564
12593
|
this.watcher = null;
|
|
12594
|
+
await watcher.close();
|
|
11565
12595
|
}
|
|
11566
12596
|
this.onBranchChange = null;
|
|
11567
12597
|
}
|
|
@@ -11579,6 +12609,8 @@ var BackgroundReindexer = class {
|
|
|
11579
12609
|
running = false;
|
|
11580
12610
|
pending = false;
|
|
11581
12611
|
stopped = false;
|
|
12612
|
+
retryTimer = null;
|
|
12613
|
+
retryDelayMs = 50;
|
|
11582
12614
|
request() {
|
|
11583
12615
|
if (this.stopped) {
|
|
11584
12616
|
return;
|
|
@@ -11589,9 +12621,13 @@ var BackgroundReindexer = class {
|
|
|
11589
12621
|
stop() {
|
|
11590
12622
|
this.stopped = true;
|
|
11591
12623
|
this.pending = false;
|
|
12624
|
+
if (this.retryTimer) {
|
|
12625
|
+
clearTimeout(this.retryTimer);
|
|
12626
|
+
this.retryTimer = null;
|
|
12627
|
+
}
|
|
11592
12628
|
}
|
|
11593
12629
|
drain() {
|
|
11594
|
-
if (this.stopped || this.running || !this.pending) {
|
|
12630
|
+
if (this.stopped || this.running || this.retryTimer || !this.pending) {
|
|
11595
12631
|
return;
|
|
11596
12632
|
}
|
|
11597
12633
|
this.pending = false;
|
|
@@ -11599,13 +12635,29 @@ var BackgroundReindexer = class {
|
|
|
11599
12635
|
void this.run();
|
|
11600
12636
|
}
|
|
11601
12637
|
async run() {
|
|
12638
|
+
let retryAfterContention = false;
|
|
11602
12639
|
try {
|
|
11603
12640
|
await this.runIndex();
|
|
12641
|
+
this.retryDelayMs = 50;
|
|
11604
12642
|
} catch (error) {
|
|
11605
|
-
|
|
12643
|
+
if (isTransientIndexLockContention(error)) {
|
|
12644
|
+
this.pending = true;
|
|
12645
|
+
retryAfterContention = true;
|
|
12646
|
+
} else {
|
|
12647
|
+
console.error("[codebase-index] Background reindex failed:", error);
|
|
12648
|
+
}
|
|
11606
12649
|
} finally {
|
|
11607
12650
|
this.running = false;
|
|
11608
|
-
this.
|
|
12651
|
+
if (retryAfterContention && !this.stopped) {
|
|
12652
|
+
const delay = this.retryDelayMs;
|
|
12653
|
+
this.retryDelayMs = Math.min(this.retryDelayMs * 2, 500);
|
|
12654
|
+
this.retryTimer = setTimeout(() => {
|
|
12655
|
+
this.retryTimer = null;
|
|
12656
|
+
this.drain();
|
|
12657
|
+
}, delay);
|
|
12658
|
+
} else {
|
|
12659
|
+
this.drain();
|
|
12660
|
+
}
|
|
11609
12661
|
}
|
|
11610
12662
|
}
|
|
11611
12663
|
};
|
|
@@ -11639,10 +12691,12 @@ function createWatcherWithIndexer(getIndexer, projectRoot, config, host = "openc
|
|
|
11639
12691
|
return {
|
|
11640
12692
|
fileWatcher,
|
|
11641
12693
|
gitWatcher,
|
|
11642
|
-
|
|
12694
|
+
whenReady() {
|
|
12695
|
+
return fileWatcher.waitUntilReady();
|
|
12696
|
+
},
|
|
12697
|
+
async stop() {
|
|
11643
12698
|
backgroundReindexer.stop();
|
|
11644
|
-
fileWatcher.stop();
|
|
11645
|
-
gitWatcher?.stop();
|
|
12699
|
+
await Promise.all([fileWatcher.stop(), gitWatcher?.stop()]);
|
|
11646
12700
|
}
|
|
11647
12701
|
};
|
|
11648
12702
|
}
|
|
@@ -11748,13 +12802,13 @@ var pr_impact = (0, import_plugin.tool)({
|
|
|
11748
12802
|
});
|
|
11749
12803
|
|
|
11750
12804
|
// src/tools/index.ts
|
|
11751
|
-
var
|
|
11752
|
-
var
|
|
11753
|
-
var
|
|
12805
|
+
var import_fs11 = require("fs");
|
|
12806
|
+
var os5 = __toESM(require("os"), 1);
|
|
12807
|
+
var path21 = __toESM(require("path"), 1);
|
|
11754
12808
|
|
|
11755
12809
|
// src/tools/visualize/activity.ts
|
|
11756
12810
|
var import_child_process4 = require("child_process");
|
|
11757
|
-
var
|
|
12811
|
+
var path19 = __toESM(require("path"), 1);
|
|
11758
12812
|
function attachRecentActivity(data, projectRoot) {
|
|
11759
12813
|
const activity = readGitActivity(projectRoot);
|
|
11760
12814
|
const changes = activity.size > 0 ? buildGitChanges(data, activity, projectRoot) : buildGraphChanges(data);
|
|
@@ -11916,7 +12970,7 @@ function normalizePath2(filePath) {
|
|
|
11916
12970
|
return filePath.replace(/\\/g, "/");
|
|
11917
12971
|
}
|
|
11918
12972
|
function toGitRelativePath(projectRoot, filePath) {
|
|
11919
|
-
const relativePath =
|
|
12973
|
+
const relativePath = path19.isAbsolute(filePath) ? path19.relative(projectRoot, filePath) : filePath;
|
|
11920
12974
|
return normalizePath2(relativePath);
|
|
11921
12975
|
}
|
|
11922
12976
|
|
|
@@ -12174,7 +13228,7 @@ render();
|
|
|
12174
13228
|
}
|
|
12175
13229
|
|
|
12176
13230
|
// src/tools/visualize/transform.ts
|
|
12177
|
-
var
|
|
13231
|
+
var path20 = __toESM(require("path"), 1);
|
|
12178
13232
|
|
|
12179
13233
|
// src/tools/visualize/modules.ts
|
|
12180
13234
|
var MAX_MODULES = 18;
|
|
@@ -12434,7 +13488,7 @@ function transformForVisualization(symbols, edges, options = {}) {
|
|
|
12434
13488
|
filePath: s.filePath,
|
|
12435
13489
|
kind: s.kind,
|
|
12436
13490
|
line: s.startLine,
|
|
12437
|
-
directory:
|
|
13491
|
+
directory: path20.dirname(s.filePath),
|
|
12438
13492
|
moduleId: "",
|
|
12439
13493
|
moduleLabel: ""
|
|
12440
13494
|
}));
|
|
@@ -12515,7 +13569,9 @@ var index_codebase = (0, import_plugin2.tool)({
|
|
|
12515
13569
|
const result = await runIndexCodebase(context?.worktree, DEFAULT_HOST, args, (title, metadata) => {
|
|
12516
13570
|
context.metadata({ title, metadata });
|
|
12517
13571
|
});
|
|
12518
|
-
|
|
13572
|
+
if (result.kind === "estimate") return formatCostEstimate(result.estimate);
|
|
13573
|
+
if (result.kind === "busy") return result.text;
|
|
13574
|
+
return formatIndexStats(result.stats, args.verbose ?? false);
|
|
12519
13575
|
}
|
|
12520
13576
|
});
|
|
12521
13577
|
var index_status = (0, import_plugin2.tool)({
|
|
@@ -12529,7 +13585,9 @@ var index_health_check = (0, import_plugin2.tool)({
|
|
|
12529
13585
|
description: "Check index health and remove stale entries from deleted files. Run this to clean up the index after files have been deleted.",
|
|
12530
13586
|
args: {},
|
|
12531
13587
|
async execute(_args, context) {
|
|
12532
|
-
|
|
13588
|
+
const result = await runIndexHealthCheck(context?.worktree, DEFAULT_HOST);
|
|
13589
|
+
if (result.kind === "busy") return result.text;
|
|
13590
|
+
return formatHealthCheck(result.health);
|
|
12533
13591
|
}
|
|
12534
13592
|
});
|
|
12535
13593
|
var index_metrics = (0, import_plugin2.tool)({
|
|
@@ -12649,8 +13707,8 @@ var call_graph_path = (0, import_plugin2.tool)({
|
|
|
12649
13707
|
maxDepth: z2.number().optional().default(10).describe("Maximum traversal depth (default: 10)")
|
|
12650
13708
|
},
|
|
12651
13709
|
async execute(args, context) {
|
|
12652
|
-
const
|
|
12653
|
-
return formatCallGraphPath(args.from, args.to,
|
|
13710
|
+
const path24 = await getCallGraphPath(context?.worktree, DEFAULT_HOST, args.from, args.to, args.maxDepth);
|
|
13711
|
+
return formatCallGraphPath(args.from, args.to, path24);
|
|
12654
13712
|
}
|
|
12655
13713
|
});
|
|
12656
13714
|
var add_knowledge_base = (0, import_plugin2.tool)({
|
|
@@ -12703,8 +13761,8 @@ var index_visualize = (0, import_plugin2.tool)({
|
|
|
12703
13761
|
return "No connected symbols found for visualization. Try including orphans with includeOrphans=true, or check that the call graph has resolved edges.";
|
|
12704
13762
|
}
|
|
12705
13763
|
const html = generateVisualizationHtml(vizData);
|
|
12706
|
-
const outputPath =
|
|
12707
|
-
(0,
|
|
13764
|
+
const outputPath = path21.join(os5.tmpdir(), `call-graph-${Date.now()}.html`);
|
|
13765
|
+
(0, import_fs11.writeFileSync)(outputPath, html, "utf-8");
|
|
12708
13766
|
let result = `Temporal call graph visualization generated: ${outputPath}
|
|
12709
13767
|
|
|
12710
13768
|
`;
|
|
@@ -12725,8 +13783,8 @@ var index_visualize = (0, import_plugin2.tool)({
|
|
|
12725
13783
|
});
|
|
12726
13784
|
|
|
12727
13785
|
// src/commands/loader.ts
|
|
12728
|
-
var
|
|
12729
|
-
var
|
|
13786
|
+
var import_fs12 = require("fs");
|
|
13787
|
+
var path22 = __toESM(require("path"), 1);
|
|
12730
13788
|
function parseFrontmatter(content) {
|
|
12731
13789
|
const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
|
|
12732
13790
|
const match = content.match(frontmatterRegex);
|
|
@@ -12747,21 +13805,21 @@ function parseFrontmatter(content) {
|
|
|
12747
13805
|
}
|
|
12748
13806
|
function loadCommandsFromDirectory(commandsDir) {
|
|
12749
13807
|
const commands = /* @__PURE__ */ new Map();
|
|
12750
|
-
if (!(0,
|
|
13808
|
+
if (!(0, import_fs12.existsSync)(commandsDir)) {
|
|
12751
13809
|
return commands;
|
|
12752
13810
|
}
|
|
12753
|
-
const files = (0,
|
|
13811
|
+
const files = (0, import_fs12.readdirSync)(commandsDir).filter((f) => f.endsWith(".md"));
|
|
12754
13812
|
for (const file of files) {
|
|
12755
|
-
const filePath =
|
|
13813
|
+
const filePath = path22.join(commandsDir, file);
|
|
12756
13814
|
let content;
|
|
12757
13815
|
try {
|
|
12758
|
-
content = (0,
|
|
13816
|
+
content = (0, import_fs12.readFileSync)(filePath, "utf-8");
|
|
12759
13817
|
} catch (error) {
|
|
12760
13818
|
const message = error instanceof Error ? error.message : String(error);
|
|
12761
13819
|
throw new Error(`Failed to load command file ${filePath}: ${message}`);
|
|
12762
13820
|
}
|
|
12763
13821
|
const { frontmatter, body } = parseFrontmatter(content);
|
|
12764
|
-
const name =
|
|
13822
|
+
const name = path22.basename(file, ".md");
|
|
12765
13823
|
const description = frontmatter.description || `Run the ${name} command`;
|
|
12766
13824
|
commands.set(name, {
|
|
12767
13825
|
description,
|
|
@@ -13065,18 +14123,37 @@ var RoutingHintController = class {
|
|
|
13065
14123
|
};
|
|
13066
14124
|
|
|
13067
14125
|
// src/utils/auto-index.ts
|
|
14126
|
+
var INITIAL_RETRY_DELAY_MS = 50;
|
|
14127
|
+
var MAX_RETRY_DELAY_MS = 500;
|
|
14128
|
+
var autoIndexStates = /* @__PURE__ */ new WeakMap();
|
|
13068
14129
|
function getErrorMessage3(error) {
|
|
13069
14130
|
return error instanceof Error ? error.message : String(error);
|
|
13070
14131
|
}
|
|
13071
|
-
function
|
|
13072
|
-
indexer.
|
|
13073
|
-
|
|
13074
|
-
console.error(`[codebase-index] Auto-index failed for "${projectRoot}": ${getErrorMessage3(error)}`);
|
|
13075
|
-
});
|
|
14132
|
+
function runAutoIndex(indexer, projectRoot, state) {
|
|
14133
|
+
void indexer.index().then(() => {
|
|
14134
|
+
autoIndexStates.delete(indexer);
|
|
13076
14135
|
}).catch((error) => {
|
|
13077
|
-
|
|
14136
|
+
if (!isTransientIndexLockContention(error)) {
|
|
14137
|
+
autoIndexStates.delete(indexer);
|
|
14138
|
+
console.error(`[codebase-index] Auto-index failed for "${projectRoot}": ${getErrorMessage3(error)}`);
|
|
14139
|
+
return;
|
|
14140
|
+
}
|
|
14141
|
+
const retryDelayMs = state.retryDelayMs;
|
|
14142
|
+
state.retryDelayMs = Math.min(retryDelayMs * 2, MAX_RETRY_DELAY_MS);
|
|
14143
|
+
const retryTimer = setTimeout(() => {
|
|
14144
|
+
runAutoIndex(indexer, projectRoot, state);
|
|
14145
|
+
}, retryDelayMs);
|
|
14146
|
+
retryTimer.unref?.();
|
|
13078
14147
|
});
|
|
13079
14148
|
}
|
|
14149
|
+
function startAutoIndex(indexer, projectRoot) {
|
|
14150
|
+
if (autoIndexStates.has(indexer)) return;
|
|
14151
|
+
const state = {
|
|
14152
|
+
retryDelayMs: INITIAL_RETRY_DELAY_MS
|
|
14153
|
+
};
|
|
14154
|
+
autoIndexStates.set(indexer, state);
|
|
14155
|
+
runAutoIndex(indexer, projectRoot, state);
|
|
14156
|
+
}
|
|
13080
14157
|
|
|
13081
14158
|
// src/index.ts
|
|
13082
14159
|
var import_meta2 = {};
|
|
@@ -13094,9 +14171,9 @@ function replaceActiveWatcher(projectRoot, nextWatcher) {
|
|
|
13094
14171
|
function getCommandsDir() {
|
|
13095
14172
|
let currentDir = process.cwd();
|
|
13096
14173
|
if (typeof import_meta2 !== "undefined" && import_meta2.url) {
|
|
13097
|
-
currentDir =
|
|
14174
|
+
currentDir = path23.dirname((0, import_url2.fileURLToPath)(import_meta2.url));
|
|
13098
14175
|
}
|
|
13099
|
-
return
|
|
14176
|
+
return path23.join(currentDir, "..", "commands");
|
|
13100
14177
|
}
|
|
13101
14178
|
function appendRoutingHints(output, hints, preferredRole) {
|
|
13102
14179
|
const preferredBucket = preferredRole === "developer" ? output.developer : output.system;
|
|
@@ -13116,7 +14193,7 @@ var plugin = async ({ directory, worktree }) => {
|
|
|
13116
14193
|
initializeTools2(projectRoot, config);
|
|
13117
14194
|
const getProjectIndexer = () => getIndexerForProject2(projectRoot);
|
|
13118
14195
|
const routingHints = config.search.routingHints ? new RoutingHintController(() => getProjectIndexer().getStatus(), 200, config.search.routingGraphHandoffHints) : null;
|
|
13119
|
-
const isHomeDir =
|
|
14196
|
+
const isHomeDir = path23.resolve(projectRoot) === path23.resolve(os6.homedir());
|
|
13120
14197
|
const isValidProject = !isHomeDir && (!config.indexing.requireProjectMarker || hasProjectMarker(projectRoot));
|
|
13121
14198
|
if (isHomeDir) {
|
|
13122
14199
|
console.warn(
|