open-codebase-index 0.22.5 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +6 -5
- package/commands/peek.md +2 -1
- package/commands/search.md +2 -1
- package/dist/cli.cjs +1986 -756
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1987 -757
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1663 -672
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1664 -673
- package/dist/index.js.map +1 -1
- package/dist/pi-extension.cjs +5464 -2270
- package/dist/pi-extension.cjs.map +1 -1
- package/dist/pi-extension.js +5453 -2259
- package/dist/pi-extension.js.map +1 -1
- package/native/codebase-index-native.darwin-arm64.node +0 -0
- package/native/codebase-index-native.darwin-x64.node +0 -0
- package/native/codebase-index-native.linux-arm64-gnu.node +0 -0
- package/native/codebase-index-native.linux-x64-gnu.node +0 -0
- package/native/codebase-index-native.win32-x64-msvc.node +0 -0
- package/package.json +5 -1
- package/skill/SKILL.md +12 -16
package/dist/cli.cjs
CHANGED
|
@@ -496,7 +496,7 @@ var require_ignore = __commonJS({
|
|
|
496
496
|
// path matching.
|
|
497
497
|
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
498
498
|
// @returns {TestResult} true if a file is ignored
|
|
499
|
-
test(
|
|
499
|
+
test(path33, checkUnignored, mode) {
|
|
500
500
|
let ignored = false;
|
|
501
501
|
let unignored = false;
|
|
502
502
|
let matchedRule;
|
|
@@ -505,7 +505,7 @@ var require_ignore = __commonJS({
|
|
|
505
505
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
506
506
|
return;
|
|
507
507
|
}
|
|
508
|
-
const matched = rule[mode].test(
|
|
508
|
+
const matched = rule[mode].test(path33);
|
|
509
509
|
if (!matched) {
|
|
510
510
|
return;
|
|
511
511
|
}
|
|
@@ -526,17 +526,17 @@ var require_ignore = __commonJS({
|
|
|
526
526
|
var throwError = (message, Ctor) => {
|
|
527
527
|
throw new Ctor(message);
|
|
528
528
|
};
|
|
529
|
-
var checkPath = (
|
|
530
|
-
if (!isString(
|
|
529
|
+
var checkPath = (path33, originalPath, doThrow) => {
|
|
530
|
+
if (!isString(path33)) {
|
|
531
531
|
return doThrow(
|
|
532
532
|
`path must be a string, but got \`${originalPath}\``,
|
|
533
533
|
TypeError
|
|
534
534
|
);
|
|
535
535
|
}
|
|
536
|
-
if (!
|
|
536
|
+
if (!path33) {
|
|
537
537
|
return doThrow(`path must not be empty`, TypeError);
|
|
538
538
|
}
|
|
539
|
-
if (checkPath.isNotRelative(
|
|
539
|
+
if (checkPath.isNotRelative(path33)) {
|
|
540
540
|
const r = "`path.relative()`d";
|
|
541
541
|
return doThrow(
|
|
542
542
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -545,7 +545,7 @@ var require_ignore = __commonJS({
|
|
|
545
545
|
}
|
|
546
546
|
return true;
|
|
547
547
|
};
|
|
548
|
-
var isNotRelative = (
|
|
548
|
+
var isNotRelative = (path33) => REGEX_TEST_INVALID_PATH.test(path33);
|
|
549
549
|
checkPath.isNotRelative = isNotRelative;
|
|
550
550
|
checkPath.convert = (p) => p;
|
|
551
551
|
var Ignore2 = class {
|
|
@@ -575,19 +575,19 @@ var require_ignore = __commonJS({
|
|
|
575
575
|
}
|
|
576
576
|
// @returns {TestResult}
|
|
577
577
|
_test(originalPath, cache, checkUnignored, slices) {
|
|
578
|
-
const
|
|
578
|
+
const path33 = originalPath && checkPath.convert(originalPath);
|
|
579
579
|
checkPath(
|
|
580
|
-
|
|
580
|
+
path33,
|
|
581
581
|
originalPath,
|
|
582
582
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
583
583
|
);
|
|
584
|
-
return this._t(
|
|
584
|
+
return this._t(path33, cache, checkUnignored, slices);
|
|
585
585
|
}
|
|
586
|
-
checkIgnore(
|
|
587
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
588
|
-
return this.test(
|
|
586
|
+
checkIgnore(path33) {
|
|
587
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path33)) {
|
|
588
|
+
return this.test(path33);
|
|
589
589
|
}
|
|
590
|
-
const slices =
|
|
590
|
+
const slices = path33.split(SLASH2).filter(Boolean);
|
|
591
591
|
slices.pop();
|
|
592
592
|
if (slices.length) {
|
|
593
593
|
const parent = this._t(
|
|
@@ -600,18 +600,18 @@ var require_ignore = __commonJS({
|
|
|
600
600
|
return parent;
|
|
601
601
|
}
|
|
602
602
|
}
|
|
603
|
-
return this._rules.test(
|
|
603
|
+
return this._rules.test(path33, false, MODE_CHECK_IGNORE);
|
|
604
604
|
}
|
|
605
|
-
_t(
|
|
606
|
-
if (
|
|
607
|
-
return cache[
|
|
605
|
+
_t(path33, cache, checkUnignored, slices) {
|
|
606
|
+
if (path33 in cache) {
|
|
607
|
+
return cache[path33];
|
|
608
608
|
}
|
|
609
609
|
if (!slices) {
|
|
610
|
-
slices =
|
|
610
|
+
slices = path33.split(SLASH2).filter(Boolean);
|
|
611
611
|
}
|
|
612
612
|
slices.pop();
|
|
613
613
|
if (!slices.length) {
|
|
614
|
-
return cache[
|
|
614
|
+
return cache[path33] = this._rules.test(path33, checkUnignored, MODE_IGNORE);
|
|
615
615
|
}
|
|
616
616
|
const parent = this._t(
|
|
617
617
|
slices.join(SLASH2) + SLASH2,
|
|
@@ -619,29 +619,29 @@ var require_ignore = __commonJS({
|
|
|
619
619
|
checkUnignored,
|
|
620
620
|
slices
|
|
621
621
|
);
|
|
622
|
-
return cache[
|
|
622
|
+
return cache[path33] = parent.ignored ? parent : this._rules.test(path33, checkUnignored, MODE_IGNORE);
|
|
623
623
|
}
|
|
624
|
-
ignores(
|
|
625
|
-
return this._test(
|
|
624
|
+
ignores(path33) {
|
|
625
|
+
return this._test(path33, this._ignoreCache, false).ignored;
|
|
626
626
|
}
|
|
627
627
|
createFilter() {
|
|
628
|
-
return (
|
|
628
|
+
return (path33) => !this.ignores(path33);
|
|
629
629
|
}
|
|
630
630
|
filter(paths) {
|
|
631
631
|
return makeArray(paths).filter(this.createFilter());
|
|
632
632
|
}
|
|
633
633
|
// @returns {TestResult}
|
|
634
|
-
test(
|
|
635
|
-
return this._test(
|
|
634
|
+
test(path33) {
|
|
635
|
+
return this._test(path33, this._testCache, true);
|
|
636
636
|
}
|
|
637
637
|
};
|
|
638
638
|
var factory = (options) => new Ignore2(options);
|
|
639
|
-
var isPathValid = (
|
|
639
|
+
var isPathValid = (path33) => checkPath(path33 && checkPath.convert(path33), path33, RETURN_FALSE);
|
|
640
640
|
var setupWindows = () => {
|
|
641
641
|
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
642
642
|
checkPath.convert = makePosix;
|
|
643
643
|
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
644
|
-
checkPath.isNotRelative = (
|
|
644
|
+
checkPath.isNotRelative = (path33) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path33) || isNotRelative(path33);
|
|
645
645
|
};
|
|
646
646
|
if (
|
|
647
647
|
// Detect `process` so that it can run in browsers.
|
|
@@ -669,7 +669,7 @@ module.exports = __toCommonJS(cli_exports);
|
|
|
669
669
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
670
670
|
var import_fs20 = require("fs");
|
|
671
671
|
var os8 = __toESM(require("os"), 1);
|
|
672
|
-
var
|
|
672
|
+
var path32 = __toESM(require("path"), 1);
|
|
673
673
|
var import_url = require("url");
|
|
674
674
|
|
|
675
675
|
// src/config/constants.ts
|
|
@@ -730,6 +730,17 @@ var EMBEDDING_MODELS = {
|
|
|
730
730
|
maxTokens: 2048,
|
|
731
731
|
costPer1MTokens: 0.15,
|
|
732
732
|
taskAble: true
|
|
733
|
+
},
|
|
734
|
+
"gemini-embedding-2": {
|
|
735
|
+
provider: "google",
|
|
736
|
+
model: "gemini-embedding-2",
|
|
737
|
+
// Keep a conservative, testable default embedding dimension. Gemini Embedding 2 supports
|
|
738
|
+
// flexible dimensions via outputDimensionality.
|
|
739
|
+
dimensions: 1536,
|
|
740
|
+
maxTokens: 8192,
|
|
741
|
+
costPer1MTokens: 0.15,
|
|
742
|
+
taskAble: false,
|
|
743
|
+
promptStyle: "embedding-2"
|
|
733
744
|
}
|
|
734
745
|
},
|
|
735
746
|
"openai": {
|
|
@@ -763,26 +774,15 @@ var EMBEDDING_MODELS = {
|
|
|
763
774
|
maxTokens: 512,
|
|
764
775
|
costPer1MTokens: 0
|
|
765
776
|
}
|
|
766
|
-
},
|
|
767
|
-
"github-copilot": {
|
|
768
|
-
"text-embedding-3-small": {
|
|
769
|
-
provider: "github-copilot",
|
|
770
|
-
model: "text-embedding-3-small",
|
|
771
|
-
dimensions: 1536,
|
|
772
|
-
maxTokens: 8191,
|
|
773
|
-
costPer1MTokens: 0
|
|
774
|
-
}
|
|
775
777
|
}
|
|
776
778
|
};
|
|
777
779
|
var DEFAULT_PROVIDER_MODELS = {
|
|
778
|
-
"github-copilot": "text-embedding-3-small",
|
|
779
780
|
"openai": "text-embedding-3-small",
|
|
780
781
|
"google": "gemini-embedding-001",
|
|
781
782
|
"ollama": "nomic-embed-text"
|
|
782
783
|
};
|
|
783
784
|
var AUTO_DETECT_PROVIDER_ORDER = [
|
|
784
785
|
"ollama",
|
|
785
|
-
"github-copilot",
|
|
786
786
|
"openai",
|
|
787
787
|
"google"
|
|
788
788
|
];
|
|
@@ -808,6 +808,9 @@ function getDefaultIndexingConfig() {
|
|
|
808
808
|
maxDepth: 5,
|
|
809
809
|
maxFilesPerDirectory: 100,
|
|
810
810
|
fallbackToTextOnMaxChunks: true,
|
|
811
|
+
// Must stay in sync with DEFAULT_LINES_PER_CHUNK in native/src/lib.rs (the napi
|
|
812
|
+
// fallback used when a native caller omits the argument).
|
|
813
|
+
linesPerChunk: 30,
|
|
811
814
|
gitBlame: { enabled: false }
|
|
812
815
|
};
|
|
813
816
|
}
|
|
@@ -941,6 +944,7 @@ function parseConfig(raw) {
|
|
|
941
944
|
maxDepth: typeof rawIndexing.maxDepth === "number" ? rawIndexing.maxDepth < -1 ? -1 : rawIndexing.maxDepth : defaultIndexing.maxDepth,
|
|
942
945
|
maxFilesPerDirectory: typeof rawIndexing.maxFilesPerDirectory === "number" ? Math.max(1, rawIndexing.maxFilesPerDirectory) : defaultIndexing.maxFilesPerDirectory,
|
|
943
946
|
fallbackToTextOnMaxChunks: typeof rawIndexing.fallbackToTextOnMaxChunks === "boolean" ? rawIndexing.fallbackToTextOnMaxChunks : defaultIndexing.fallbackToTextOnMaxChunks,
|
|
947
|
+
linesPerChunk: typeof rawIndexing.linesPerChunk === "number" && Number.isFinite(rawIndexing.linesPerChunk) ? Math.min(Math.max(1, Math.floor(rawIndexing.linesPerChunk)), 4294967295) : defaultIndexing.linesPerChunk,
|
|
944
948
|
gitBlame: {
|
|
945
949
|
enabled: rawIndexing.gitBlame && typeof rawIndexing.gitBlame === "object" && typeof rawIndexing.gitBlame.enabled === "boolean" ? rawIndexing.gitBlame.enabled : defaultIndexing.gitBlame.enabled
|
|
946
950
|
}
|
|
@@ -983,6 +987,7 @@ function parseConfig(raw) {
|
|
|
983
987
|
let embeddingModel;
|
|
984
988
|
let customProvider;
|
|
985
989
|
let reranker;
|
|
990
|
+
const githubCopilotDeprecationMessage = '`embeddingProvider: "github-copilot"` is deprecated and no longer available. Migrate existing configs to `embeddingProvider: "google"` and select an explicit Google model. For existing indexes, run `index_codebase` with `force: true` after changing to `gemini-embedding-001` or `gemini-embedding-2` to rebuild embeddings. See docs/configuration.md for details.';
|
|
986
991
|
if (embeddingProviderValue === "custom") {
|
|
987
992
|
embeddingProvider = "custom";
|
|
988
993
|
const rawCustom = input.customProvider && typeof input.customProvider === "object" ? input.customProvider : null;
|
|
@@ -1022,6 +1027,8 @@ function parseConfig(raw) {
|
|
|
1022
1027
|
} else if (rawEmbeddingModel) {
|
|
1023
1028
|
embeddingModel = DEFAULT_PROVIDER_MODELS[embeddingProvider];
|
|
1024
1029
|
}
|
|
1030
|
+
} else if (embeddingProviderValue === "github-copilot") {
|
|
1031
|
+
throw new Error(githubCopilotDeprecationMessage);
|
|
1025
1032
|
} else {
|
|
1026
1033
|
embeddingProvider = "auto";
|
|
1027
1034
|
}
|
|
@@ -1052,10 +1059,21 @@ function parseConfig(raw) {
|
|
|
1052
1059
|
timeoutMs: typeof rawReranker.timeoutMs === "number" ? Math.max(1e3, Math.floor(rawReranker.timeoutMs)) : 1e4
|
|
1053
1060
|
};
|
|
1054
1061
|
}
|
|
1062
|
+
const rawEmbedding = input.embedding && typeof input.embedding === "object" ? input.embedding : {};
|
|
1063
|
+
const rawEmbeddingBatch = rawEmbedding.batch && typeof rawEmbedding.batch === "object" ? rawEmbedding.batch : null;
|
|
1064
|
+
const embeddingMaxBatchItems = typeof rawEmbeddingBatch?.maxBatchItems === "number" && Number.isFinite(rawEmbeddingBatch.maxBatchItems) ? Math.max(1, Math.floor(rawEmbeddingBatch.maxBatchItems)) : void 0;
|
|
1065
|
+
const embeddingMaxBatchTokens = typeof rawEmbeddingBatch?.maxBatchTokens === "number" && Number.isFinite(rawEmbeddingBatch.maxBatchTokens) ? Math.max(1, Math.floor(rawEmbeddingBatch.maxBatchTokens)) : void 0;
|
|
1066
|
+
const embedding = embeddingMaxBatchItems !== void 0 || embeddingMaxBatchTokens !== void 0 ? {
|
|
1067
|
+
batch: {
|
|
1068
|
+
...embeddingMaxBatchItems !== void 0 ? { maxBatchItems: embeddingMaxBatchItems } : {},
|
|
1069
|
+
...embeddingMaxBatchTokens !== void 0 ? { maxBatchTokens: embeddingMaxBatchTokens } : {}
|
|
1070
|
+
}
|
|
1071
|
+
} : {};
|
|
1055
1072
|
return {
|
|
1056
1073
|
embeddingProvider,
|
|
1057
1074
|
embeddingModel,
|
|
1058
1075
|
customProvider,
|
|
1076
|
+
embedding,
|
|
1059
1077
|
scope: isValidScope(scopeValue) ? scopeValue : "project",
|
|
1060
1078
|
include: includeValue ?? DEFAULT_INCLUDE,
|
|
1061
1079
|
exclude: excludeValue ?? DEFAULT_EXCLUDE,
|
|
@@ -1179,9 +1197,9 @@ var import_fs = require("fs");
|
|
|
1179
1197
|
var path = __toESM(require("path"), 1);
|
|
1180
1198
|
|
|
1181
1199
|
// src/eval/report-formatters.ts
|
|
1182
|
-
function assertFiniteNumber(value,
|
|
1200
|
+
function assertFiniteNumber(value, path33) {
|
|
1183
1201
|
if (typeof value !== "number" || Number.isNaN(value) || !Number.isFinite(value)) {
|
|
1184
|
-
throw new Error(`${
|
|
1202
|
+
throw new Error(`${path33} must be a finite number`);
|
|
1185
1203
|
}
|
|
1186
1204
|
return value;
|
|
1187
1205
|
}
|
|
@@ -1453,7 +1471,7 @@ function pTimeout(promise, options) {
|
|
|
1453
1471
|
} = options;
|
|
1454
1472
|
let timer;
|
|
1455
1473
|
let abortHandler;
|
|
1456
|
-
const wrappedPromise = new Promise((
|
|
1474
|
+
const wrappedPromise = new Promise((resolve20, reject) => {
|
|
1457
1475
|
if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
|
|
1458
1476
|
throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
|
|
1459
1477
|
}
|
|
@@ -1467,7 +1485,7 @@ function pTimeout(promise, options) {
|
|
|
1467
1485
|
};
|
|
1468
1486
|
signal.addEventListener("abort", abortHandler, { once: true });
|
|
1469
1487
|
}
|
|
1470
|
-
promise.then(
|
|
1488
|
+
promise.then(resolve20, reject);
|
|
1471
1489
|
if (milliseconds === Number.POSITIVE_INFINITY) {
|
|
1472
1490
|
return;
|
|
1473
1491
|
}
|
|
@@ -1475,7 +1493,7 @@ function pTimeout(promise, options) {
|
|
|
1475
1493
|
timer = customTimers.setTimeout.call(void 0, () => {
|
|
1476
1494
|
if (fallback) {
|
|
1477
1495
|
try {
|
|
1478
|
-
|
|
1496
|
+
resolve20(fallback());
|
|
1479
1497
|
} catch (error) {
|
|
1480
1498
|
reject(error);
|
|
1481
1499
|
}
|
|
@@ -1485,7 +1503,7 @@ function pTimeout(promise, options) {
|
|
|
1485
1503
|
promise.cancel();
|
|
1486
1504
|
}
|
|
1487
1505
|
if (message === false) {
|
|
1488
|
-
|
|
1506
|
+
resolve20();
|
|
1489
1507
|
} else if (message instanceof Error) {
|
|
1490
1508
|
reject(message);
|
|
1491
1509
|
} else {
|
|
@@ -1887,7 +1905,7 @@ var PQueue = class extends import_index.default {
|
|
|
1887
1905
|
// Assign unique ID if not provided
|
|
1888
1906
|
id: options.id ?? (this.#idAssigner++).toString()
|
|
1889
1907
|
};
|
|
1890
|
-
return new Promise((
|
|
1908
|
+
return new Promise((resolve20, reject) => {
|
|
1891
1909
|
const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
|
|
1892
1910
|
let cleanupQueueAbortHandler = () => void 0;
|
|
1893
1911
|
const run = async () => {
|
|
@@ -1927,7 +1945,7 @@ var PQueue = class extends import_index.default {
|
|
|
1927
1945
|
})]);
|
|
1928
1946
|
}
|
|
1929
1947
|
const result = await operation;
|
|
1930
|
-
|
|
1948
|
+
resolve20(result);
|
|
1931
1949
|
this.emit("completed", result);
|
|
1932
1950
|
} catch (error) {
|
|
1933
1951
|
reject(error);
|
|
@@ -2115,13 +2133,13 @@ var PQueue = class extends import_index.default {
|
|
|
2115
2133
|
});
|
|
2116
2134
|
}
|
|
2117
2135
|
async #onEvent(event, filter) {
|
|
2118
|
-
return new Promise((
|
|
2136
|
+
return new Promise((resolve20) => {
|
|
2119
2137
|
const listener = () => {
|
|
2120
2138
|
if (filter && !filter()) {
|
|
2121
2139
|
return;
|
|
2122
2140
|
}
|
|
2123
2141
|
this.off(event, listener);
|
|
2124
|
-
|
|
2142
|
+
resolve20();
|
|
2125
2143
|
};
|
|
2126
2144
|
this.on(event, listener);
|
|
2127
2145
|
});
|
|
@@ -2407,7 +2425,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
|
|
|
2407
2425
|
const finalDelay = Math.min(delayTime, remainingTime);
|
|
2408
2426
|
options.signal?.throwIfAborted();
|
|
2409
2427
|
if (finalDelay > 0) {
|
|
2410
|
-
await new Promise((
|
|
2428
|
+
await new Promise((resolve20, reject) => {
|
|
2411
2429
|
const onAbort = () => {
|
|
2412
2430
|
clearTimeout(timeoutToken);
|
|
2413
2431
|
options.signal?.removeEventListener("abort", onAbort);
|
|
@@ -2415,7 +2433,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
|
|
|
2415
2433
|
};
|
|
2416
2434
|
const timeoutToken = setTimeout(() => {
|
|
2417
2435
|
options.signal?.removeEventListener("abort", onAbort);
|
|
2418
|
-
|
|
2436
|
+
resolve20();
|
|
2419
2437
|
}, finalDelay);
|
|
2420
2438
|
if (options.unref) {
|
|
2421
2439
|
timeoutToken.unref?.();
|
|
@@ -2551,8 +2569,6 @@ async function tryDetectProvider() {
|
|
|
2551
2569
|
}
|
|
2552
2570
|
async function getProviderCredentials(provider) {
|
|
2553
2571
|
switch (provider) {
|
|
2554
|
-
case "github-copilot":
|
|
2555
|
-
return getGitHubCopilotCredentials();
|
|
2556
2572
|
case "openai":
|
|
2557
2573
|
return getOpenAICredentials();
|
|
2558
2574
|
case "google":
|
|
@@ -2563,22 +2579,6 @@ async function getProviderCredentials(provider) {
|
|
|
2563
2579
|
return null;
|
|
2564
2580
|
}
|
|
2565
2581
|
}
|
|
2566
|
-
function getGitHubCopilotCredentials() {
|
|
2567
|
-
const authData = loadOpenCodeAuth();
|
|
2568
|
-
const copilotAuth = authData["github-copilot"] || authData["github-copilot-enterprise"];
|
|
2569
|
-
if (!copilotAuth || copilotAuth.type !== "oauth") {
|
|
2570
|
-
return null;
|
|
2571
|
-
}
|
|
2572
|
-
const auth = copilotAuth;
|
|
2573
|
-
const baseUrl = auth.enterpriseUrl ? `https://copilot-api.${auth.enterpriseUrl.replace(/^https?:\/\//, "").replace(/\/$/, "")}` : "https://models.github.ai";
|
|
2574
|
-
return {
|
|
2575
|
-
provider: "github-copilot",
|
|
2576
|
-
baseUrl,
|
|
2577
|
-
refreshToken: copilotAuth.refresh,
|
|
2578
|
-
accessToken: copilotAuth.access,
|
|
2579
|
-
tokenExpires: copilotAuth.expires
|
|
2580
|
-
};
|
|
2581
|
-
}
|
|
2582
2582
|
function getOpenAICredentials() {
|
|
2583
2583
|
const authData = loadOpenCodeAuth();
|
|
2584
2584
|
const openaiAuth = authData["openai"];
|
|
@@ -2704,8 +2704,6 @@ async function tryDetectOllamaProvider() {
|
|
|
2704
2704
|
}
|
|
2705
2705
|
function getProviderDisplayName(provider) {
|
|
2706
2706
|
switch (provider) {
|
|
2707
|
-
case "github-copilot":
|
|
2708
|
-
return "GitHub Copilot";
|
|
2709
2707
|
case "openai":
|
|
2710
2708
|
return "OpenAI";
|
|
2711
2709
|
case "google":
|
|
@@ -2930,44 +2928,6 @@ var CustomEmbeddingProvider = class extends BaseEmbeddingProvider {
|
|
|
2930
2928
|
}
|
|
2931
2929
|
};
|
|
2932
2930
|
|
|
2933
|
-
// src/embeddings/providers/github-copilot.ts
|
|
2934
|
-
var GitHubCopilotEmbeddingProvider = class extends BaseEmbeddingProvider {
|
|
2935
|
-
constructor(credentials, modelInfo) {
|
|
2936
|
-
super(credentials, modelInfo);
|
|
2937
|
-
}
|
|
2938
|
-
getToken() {
|
|
2939
|
-
if (!this.credentials.refreshToken) {
|
|
2940
|
-
throw new Error("No OAuth token available for GitHub");
|
|
2941
|
-
}
|
|
2942
|
-
return this.credentials.refreshToken;
|
|
2943
|
-
}
|
|
2944
|
-
async embedBatch(texts) {
|
|
2945
|
-
const token = this.getToken();
|
|
2946
|
-
const response = await fetch(`${this.credentials.baseUrl}/inference/embeddings`, {
|
|
2947
|
-
method: "POST",
|
|
2948
|
-
headers: {
|
|
2949
|
-
Authorization: `Bearer ${token}`,
|
|
2950
|
-
"Content-Type": "application/json",
|
|
2951
|
-
Accept: "application/vnd.github+json",
|
|
2952
|
-
"X-GitHub-Api-Version": "2022-11-28"
|
|
2953
|
-
},
|
|
2954
|
-
body: JSON.stringify({
|
|
2955
|
-
model: `openai/${this.modelInfo.model}`,
|
|
2956
|
-
input: texts
|
|
2957
|
-
})
|
|
2958
|
-
});
|
|
2959
|
-
if (!response.ok) {
|
|
2960
|
-
const error = (await response.text()).slice(0, 500);
|
|
2961
|
-
throw new Error(`GitHub Copilot embedding API error: ${response.status} - ${error}`);
|
|
2962
|
-
}
|
|
2963
|
-
const data = await response.json();
|
|
2964
|
-
return {
|
|
2965
|
-
embeddings: data.data.map((d) => d.embedding),
|
|
2966
|
-
totalTokensUsed: data.usage.total_tokens
|
|
2967
|
-
};
|
|
2968
|
-
}
|
|
2969
|
-
};
|
|
2970
|
-
|
|
2971
2931
|
// src/embeddings/providers/google.ts
|
|
2972
2932
|
var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider extends BaseEmbeddingProvider {
|
|
2973
2933
|
static BATCH_SIZE = 20;
|
|
@@ -2975,24 +2935,30 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider extends BaseEmbeddi
|
|
|
2975
2935
|
super(credentials, modelInfo);
|
|
2976
2936
|
}
|
|
2977
2937
|
async embedQuery(query) {
|
|
2978
|
-
const taskType = this.modelInfo.taskAble ? "CODE_RETRIEVAL_QUERY" : void 0;
|
|
2979
|
-
const
|
|
2938
|
+
const taskType = this.modelInfo.model === "gemini-embedding-001" && this.modelInfo.taskAble ? "CODE_RETRIEVAL_QUERY" : void 0;
|
|
2939
|
+
const texts = [
|
|
2940
|
+
this.modelInfo.model === "gemini-embedding-2" ? `task: code retrieval | query: ${query}` : query
|
|
2941
|
+
];
|
|
2942
|
+
const result = await this.embedWithTaskType(texts, taskType);
|
|
2980
2943
|
return {
|
|
2981
2944
|
embedding: result.embeddings[0],
|
|
2982
2945
|
tokensUsed: result.totalTokensUsed
|
|
2983
2946
|
};
|
|
2984
2947
|
}
|
|
2985
2948
|
async embedDocument(document) {
|
|
2986
|
-
const taskType = this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
2987
|
-
const result = await this.embedWithTaskType([
|
|
2949
|
+
const taskType = this.modelInfo.model === "gemini-embedding-001" && this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
2950
|
+
const result = await this.embedWithTaskType([
|
|
2951
|
+
this.modelInfo.model === "gemini-embedding-2" ? `title: none | text: ${document}` : document
|
|
2952
|
+
], taskType);
|
|
2988
2953
|
return {
|
|
2989
2954
|
embedding: result.embeddings[0],
|
|
2990
2955
|
tokensUsed: result.totalTokensUsed
|
|
2991
2956
|
};
|
|
2992
2957
|
}
|
|
2993
2958
|
async embedBatch(texts) {
|
|
2994
|
-
const taskType = this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
2995
|
-
|
|
2959
|
+
const taskType = this.modelInfo.model === "gemini-embedding-001" && this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
2960
|
+
const formattedTexts = this.modelInfo.model === "gemini-embedding-2" ? texts.map((text) => `title: none | text: ${text}`) : texts;
|
|
2961
|
+
return this.embedWithTaskType(formattedTexts, taskType);
|
|
2996
2962
|
}
|
|
2997
2963
|
async embedWithTaskType(texts, taskType) {
|
|
2998
2964
|
const batches = [];
|
|
@@ -3042,6 +3008,10 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider extends BaseEmbeddi
|
|
|
3042
3008
|
var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddingProvider {
|
|
3043
3009
|
static MIN_TRUNCATION_CHARS = 512;
|
|
3044
3010
|
static REQUEST_TIMEOUT_MS = 12e4;
|
|
3011
|
+
// Set when /api/embed returns 404 so subsequent multi-text batches skip the
|
|
3012
|
+
// batched endpoint and go straight to the legacy per-text path (one probe per
|
|
3013
|
+
// old ollama install, not one probe per batch).
|
|
3014
|
+
batchEndpointUnavailable = false;
|
|
3045
3015
|
constructor(credentials, modelInfo) {
|
|
3046
3016
|
super(credentials, modelInfo);
|
|
3047
3017
|
}
|
|
@@ -3059,6 +3029,21 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddi
|
|
|
3059
3029
|
const message = (error instanceof Error ? error.message : String(error)).toLowerCase();
|
|
3060
3030
|
return message.includes("context length") && (message.includes("exceed") || message.includes("exceeded") || message.includes("too long")) || message.includes("input length exceeds the context length") || message.includes("context length exceeded");
|
|
3061
3031
|
}
|
|
3032
|
+
// True for a 404 from the newer /api/embed endpoint, i.e. an ollama version that
|
|
3033
|
+
// does not provide it. embedBatch uses this to fall back to the legacy per-text
|
|
3034
|
+
// /api/embeddings path so old ollama installs do not regress.
|
|
3035
|
+
isBatchEndpointUnavailableError(error) {
|
|
3036
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3037
|
+
return message.includes("Ollama /api/embed not available");
|
|
3038
|
+
}
|
|
3039
|
+
// True for a malformed /api/embed response (wrong vector count or a bad vector).
|
|
3040
|
+
// embedBatch falls back to the per-text path on this so a bad batch response
|
|
3041
|
+
// re-embeds each text cleanly. A text that then fails per-text is not isolated
|
|
3042
|
+
// here; it is isolated on the recovery run, which re-embeds one text per request.
|
|
3043
|
+
isBatchValidationError(error) {
|
|
3044
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3045
|
+
return message.includes("invalid embedding batch");
|
|
3046
|
+
}
|
|
3062
3047
|
buildTruncationCandidates(text) {
|
|
3063
3048
|
const baseMaxChars = Math.max(1, this.modelInfo.maxTokens * 4);
|
|
3064
3049
|
const candidateLimits = /* @__PURE__ */ new Set();
|
|
@@ -3160,7 +3145,74 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddi
|
|
|
3160
3145
|
tokensUsed: this.estimateTokens(text)
|
|
3161
3146
|
};
|
|
3162
3147
|
}
|
|
3163
|
-
|
|
3148
|
+
// Embeds many texts in one POST /api/embed request (input: string[]). Ollama
|
|
3149
|
+
// encodes each input independently, so the model context length applies per input
|
|
3150
|
+
// (the upstream splitter already bounds each input), not over the batch. This
|
|
3151
|
+
// amortizes N HTTP round-trips into one.
|
|
3152
|
+
async embedMany(texts) {
|
|
3153
|
+
const controller = new AbortController();
|
|
3154
|
+
const timeout = setTimeout(
|
|
3155
|
+
() => controller.abort(),
|
|
3156
|
+
_OllamaEmbeddingProvider.REQUEST_TIMEOUT_MS
|
|
3157
|
+
);
|
|
3158
|
+
let response;
|
|
3159
|
+
try {
|
|
3160
|
+
response = await fetch(`${this.credentials.baseUrl}/api/embed`, {
|
|
3161
|
+
method: "POST",
|
|
3162
|
+
headers: {
|
|
3163
|
+
"Content-Type": "application/json"
|
|
3164
|
+
},
|
|
3165
|
+
body: JSON.stringify({
|
|
3166
|
+
model: this.modelInfo.model,
|
|
3167
|
+
input: texts,
|
|
3168
|
+
truncate: false
|
|
3169
|
+
}),
|
|
3170
|
+
signal: controller.signal
|
|
3171
|
+
});
|
|
3172
|
+
} catch (error) {
|
|
3173
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
3174
|
+
throw new Error(
|
|
3175
|
+
`Ollama embedding request timed out after ${_OllamaEmbeddingProvider.REQUEST_TIMEOUT_MS}ms`
|
|
3176
|
+
);
|
|
3177
|
+
}
|
|
3178
|
+
throw error;
|
|
3179
|
+
} finally {
|
|
3180
|
+
clearTimeout(timeout);
|
|
3181
|
+
}
|
|
3182
|
+
if (!response.ok) {
|
|
3183
|
+
const error = (await response.text()).slice(0, 500);
|
|
3184
|
+
if (response.status === 404) {
|
|
3185
|
+
throw new Error(`Ollama /api/embed not available: ${response.status} - ${error}`);
|
|
3186
|
+
}
|
|
3187
|
+
throw new Error(`Ollama embedding API error: ${response.status} - ${error}`);
|
|
3188
|
+
}
|
|
3189
|
+
let parsed;
|
|
3190
|
+
try {
|
|
3191
|
+
parsed = await response.json();
|
|
3192
|
+
} catch {
|
|
3193
|
+
throw new Error(
|
|
3194
|
+
`Ollama returned an invalid embedding batch; expected ${texts.length} vectors of ${this.modelInfo.dimensions} finite dimensions`
|
|
3195
|
+
);
|
|
3196
|
+
}
|
|
3197
|
+
const data = parsed && typeof parsed === "object" ? parsed : {};
|
|
3198
|
+
if (!Array.isArray(data.embeddings) || data.embeddings.length !== texts.length || data.embeddings.some(
|
|
3199
|
+
(value) => !Array.isArray(value) || value.length !== this.modelInfo.dimensions || value.some((v) => typeof v !== "number" || !Number.isFinite(v))
|
|
3200
|
+
)) {
|
|
3201
|
+
throw new Error(
|
|
3202
|
+
`Ollama returned an invalid embedding batch; expected ${texts.length} vectors of ${this.modelInfo.dimensions} finite dimensions`
|
|
3203
|
+
);
|
|
3204
|
+
}
|
|
3205
|
+
return {
|
|
3206
|
+
embeddings: data.embeddings,
|
|
3207
|
+
totalTokensUsed: texts.reduce((sum, text) => sum + this.estimateTokens(text), 0)
|
|
3208
|
+
};
|
|
3209
|
+
}
|
|
3210
|
+
// Per-text /api/embeddings path shared by the single-text fast path and the
|
|
3211
|
+
// batch fallback. Uses the legacy endpoint one text at a time, so each text gets
|
|
3212
|
+
// its own truncation safety net and a vector validated on its own. A text that
|
|
3213
|
+
// hard-fails per-text throws here and fails the whole request batch; the recovery
|
|
3214
|
+
// run re-embeds one text per request to isolate it.
|
|
3215
|
+
async embedOneByOne(texts) {
|
|
3164
3216
|
const results = [];
|
|
3165
3217
|
for (const text of texts) {
|
|
3166
3218
|
results.push(await this.embedSingleWithFallback(text));
|
|
@@ -3170,6 +3222,26 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddi
|
|
|
3170
3222
|
totalTokensUsed: results.reduce((sum, r) => sum + r.tokensUsed, 0)
|
|
3171
3223
|
};
|
|
3172
3224
|
}
|
|
3225
|
+
async embedBatch(texts) {
|
|
3226
|
+
if (texts.length === 0) {
|
|
3227
|
+
return { embeddings: [], totalTokensUsed: 0 };
|
|
3228
|
+
}
|
|
3229
|
+
if (texts.length === 1 || this.batchEndpointUnavailable) {
|
|
3230
|
+
return this.embedOneByOne(texts);
|
|
3231
|
+
}
|
|
3232
|
+
try {
|
|
3233
|
+
return await this.embedMany(texts);
|
|
3234
|
+
} catch (error) {
|
|
3235
|
+
if (this.isBatchEndpointUnavailableError(error)) {
|
|
3236
|
+
this.batchEndpointUnavailable = true;
|
|
3237
|
+
return this.embedOneByOne(texts);
|
|
3238
|
+
}
|
|
3239
|
+
if (!this.isContextLengthError(error) && !this.isBatchValidationError(error)) {
|
|
3240
|
+
throw error;
|
|
3241
|
+
}
|
|
3242
|
+
return this.embedOneByOne(texts);
|
|
3243
|
+
}
|
|
3244
|
+
}
|
|
3173
3245
|
};
|
|
3174
3246
|
|
|
3175
3247
|
// src/embeddings/providers/openai.ts
|
|
@@ -3204,8 +3276,6 @@ var OpenAIEmbeddingProvider = class extends BaseEmbeddingProvider {
|
|
|
3204
3276
|
// src/embeddings/provider.ts
|
|
3205
3277
|
function createEmbeddingProvider(configuredProviderInfo) {
|
|
3206
3278
|
switch (configuredProviderInfo.provider) {
|
|
3207
|
-
case "github-copilot":
|
|
3208
|
-
return new GitHubCopilotEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
3209
3279
|
case "openai":
|
|
3210
3280
|
return new OpenAIEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
3211
3281
|
case "google":
|
|
@@ -3221,85 +3291,6 @@ function createEmbeddingProvider(configuredProviderInfo) {
|
|
|
3221
3291
|
}
|
|
3222
3292
|
}
|
|
3223
3293
|
|
|
3224
|
-
// src/rerank/index.ts
|
|
3225
|
-
function createReranker(config) {
|
|
3226
|
-
if (!config.enabled) {
|
|
3227
|
-
return new NoOpReranker();
|
|
3228
|
-
}
|
|
3229
|
-
return new SiliconFlowReranker(config);
|
|
3230
|
-
}
|
|
3231
|
-
var NoOpReranker = class {
|
|
3232
|
-
isAvailable() {
|
|
3233
|
-
return false;
|
|
3234
|
-
}
|
|
3235
|
-
async rerank(_query, documents, _topN) {
|
|
3236
|
-
return {
|
|
3237
|
-
results: documents.map((_, index) => ({ index, relevanceScore: 0 }))
|
|
3238
|
-
};
|
|
3239
|
-
}
|
|
3240
|
-
};
|
|
3241
|
-
var SiliconFlowReranker = class {
|
|
3242
|
-
config;
|
|
3243
|
-
constructor(config) {
|
|
3244
|
-
this.config = config;
|
|
3245
|
-
}
|
|
3246
|
-
isAvailable() {
|
|
3247
|
-
return this.config.enabled && !!this.config.baseUrl && !!this.config.model;
|
|
3248
|
-
}
|
|
3249
|
-
async rerank(query, documents, topN) {
|
|
3250
|
-
if (documents.length === 0) {
|
|
3251
|
-
return { results: [] };
|
|
3252
|
-
}
|
|
3253
|
-
const headers = {
|
|
3254
|
-
"Content-Type": "application/json"
|
|
3255
|
-
};
|
|
3256
|
-
if (this.config.apiKey) {
|
|
3257
|
-
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
3258
|
-
}
|
|
3259
|
-
const baseUrl = this.config.baseUrl;
|
|
3260
|
-
if (!baseUrl) {
|
|
3261
|
-
throw new Error("Reranker baseUrl is required. Configure reranker.baseUrl in your codebase-index.json.");
|
|
3262
|
-
}
|
|
3263
|
-
const timeoutMs = this.config.timeoutMs ?? 3e4;
|
|
3264
|
-
const controller = new AbortController();
|
|
3265
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
3266
|
-
try {
|
|
3267
|
-
const response = await fetch(`${baseUrl}/rerank`, {
|
|
3268
|
-
method: "POST",
|
|
3269
|
-
headers,
|
|
3270
|
-
body: JSON.stringify({
|
|
3271
|
-
model: this.config.model,
|
|
3272
|
-
query,
|
|
3273
|
-
documents,
|
|
3274
|
-
top_n: topN ?? this.config.topN ?? 20,
|
|
3275
|
-
return_documents: false
|
|
3276
|
-
}),
|
|
3277
|
-
signal: controller.signal
|
|
3278
|
-
});
|
|
3279
|
-
clearTimeout(timeout);
|
|
3280
|
-
if (!response.ok) {
|
|
3281
|
-
const errorText = await response.text();
|
|
3282
|
-
throw new Error(`Rerank API error: ${response.status} - ${errorText}`);
|
|
3283
|
-
}
|
|
3284
|
-
const data = await response.json();
|
|
3285
|
-
return {
|
|
3286
|
-
results: data.results.map((r) => ({
|
|
3287
|
-
index: r.index,
|
|
3288
|
-
relevanceScore: r.relevance_score,
|
|
3289
|
-
document: r.document?.text
|
|
3290
|
-
})),
|
|
3291
|
-
tokensUsed: data.meta?.tokens?.input_tokens
|
|
3292
|
-
};
|
|
3293
|
-
} catch (error) {
|
|
3294
|
-
clearTimeout(timeout);
|
|
3295
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
3296
|
-
throw new Error(`Rerank API request timed out after ${timeoutMs}ms`);
|
|
3297
|
-
}
|
|
3298
|
-
throw error;
|
|
3299
|
-
}
|
|
3300
|
-
}
|
|
3301
|
-
};
|
|
3302
|
-
|
|
3303
3294
|
// src/utils/files.ts
|
|
3304
3295
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
3305
3296
|
var import_fs3 = require("fs");
|
|
@@ -3461,8 +3452,8 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
|
|
|
3461
3452
|
if (entry.isDirectory()) {
|
|
3462
3453
|
subdirs.push({ fullPath, relativePath });
|
|
3463
3454
|
} else if (entry.isFile()) {
|
|
3464
|
-
const
|
|
3465
|
-
if (
|
|
3455
|
+
const stat5 = await import_fs3.promises.stat(fullPath);
|
|
3456
|
+
if (stat5.size > maxFileSize) {
|
|
3466
3457
|
skipped.push({ path: relativePath, reason: "too_large" });
|
|
3467
3458
|
continue;
|
|
3468
3459
|
}
|
|
@@ -3480,7 +3471,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
|
|
|
3480
3471
|
}
|
|
3481
3472
|
}
|
|
3482
3473
|
if (matched) {
|
|
3483
|
-
filesInDir.push({ path: fullPath, size:
|
|
3474
|
+
filesInDir.push({ path: fullPath, size: stat5.size });
|
|
3484
3475
|
}
|
|
3485
3476
|
}
|
|
3486
3477
|
}
|
|
@@ -3537,8 +3528,8 @@ async function collectFiles(projectRoot, includePatterns, excludePatterns, maxFi
|
|
|
3537
3528
|
}
|
|
3538
3529
|
for (const resolvedKbRoot of normalizedRoots) {
|
|
3539
3530
|
try {
|
|
3540
|
-
const
|
|
3541
|
-
if (!
|
|
3531
|
+
const stat5 = await import_fs3.promises.stat(resolvedKbRoot);
|
|
3532
|
+
if (!stat5.isDirectory()) {
|
|
3542
3533
|
skipped.push({ path: resolvedKbRoot, reason: "excluded" });
|
|
3543
3534
|
continue;
|
|
3544
3535
|
}
|
|
@@ -4303,12 +4294,12 @@ try {
|
|
|
4303
4294
|
}
|
|
4304
4295
|
|
|
4305
4296
|
// src/native/parsing.ts
|
|
4306
|
-
function parseFileAsText(filePath, content) {
|
|
4307
|
-
const result = native.parseFileAsText(filePath, content);
|
|
4297
|
+
function parseFileAsText(filePath, content, linesPerChunk) {
|
|
4298
|
+
const result = native.parseFileAsText(filePath, content, linesPerChunk);
|
|
4308
4299
|
return result.map(mapChunk);
|
|
4309
4300
|
}
|
|
4310
|
-
function parseFiles(files) {
|
|
4311
|
-
const result = native.parseFiles(files);
|
|
4301
|
+
function parseFiles(files, linesPerChunk) {
|
|
4302
|
+
const result = native.parseFiles(files, linesPerChunk);
|
|
4312
4303
|
return result.map((f) => ({
|
|
4313
4304
|
path: f.path,
|
|
4314
4305
|
chunks: f.chunks.map(mapChunk),
|
|
@@ -4385,13 +4376,13 @@ var VectorStore = class {
|
|
|
4385
4376
|
const metadata = items.map((i) => JSON.stringify(i.metadata));
|
|
4386
4377
|
this.inner.addBatch(ids, vectors, metadata);
|
|
4387
4378
|
}
|
|
4388
|
-
search(queryVector, limit = 10) {
|
|
4379
|
+
search(queryVector, limit = 10, allowedIds) {
|
|
4389
4380
|
if (queryVector.length !== this.dimensions) {
|
|
4390
4381
|
throw new Error(
|
|
4391
4382
|
`Query vector dimension mismatch: expected ${this.dimensions}, got ${queryVector.length}`
|
|
4392
4383
|
);
|
|
4393
4384
|
}
|
|
4394
|
-
const results = this.inner.search(queryVector, limit);
|
|
4385
|
+
const results = allowedIds === void 0 ? this.inner.search(queryVector, limit) : this.inner.searchFiltered(queryVector, limit, allowedIds);
|
|
4395
4386
|
return results.map((r) => ({
|
|
4396
4387
|
id: r.id,
|
|
4397
4388
|
score: r.score,
|
|
@@ -4619,6 +4610,10 @@ var Database = class _Database {
|
|
|
4619
4610
|
this.throwIfClosed();
|
|
4620
4611
|
return this.inner.getBranchChunkIds(branch);
|
|
4621
4612
|
}
|
|
4613
|
+
getChunkIdsByBlameDate(since, until) {
|
|
4614
|
+
this.throwIfClosed();
|
|
4615
|
+
return this.inner.getChunkIdsByBlameDate(since, until);
|
|
4616
|
+
}
|
|
4622
4617
|
getBranchDelta(branch, baseBranch) {
|
|
4623
4618
|
this.throwIfClosed();
|
|
4624
4619
|
return this.inner.getBranchDelta(branch, baseBranch);
|
|
@@ -4865,11 +4860,11 @@ function resolveGitDir(repoRoot) {
|
|
|
4865
4860
|
return null;
|
|
4866
4861
|
}
|
|
4867
4862
|
try {
|
|
4868
|
-
const
|
|
4869
|
-
if (
|
|
4863
|
+
const stat5 = (0, import_fs5.statSync)(gitPath);
|
|
4864
|
+
if (stat5.isDirectory()) {
|
|
4870
4865
|
return gitPath;
|
|
4871
4866
|
}
|
|
4872
|
-
if (
|
|
4867
|
+
if (stat5.isFile()) {
|
|
4873
4868
|
const content = (0, import_fs5.readFileSync)(gitPath, "utf-8").trim();
|
|
4874
4869
|
const match = content.match(/^gitdir:\s*(.+)$/);
|
|
4875
4870
|
if (match) {
|
|
@@ -5374,8 +5369,8 @@ async function isWorktreeRegistered(projectRoot, worktreePath) {
|
|
|
5374
5369
|
return false;
|
|
5375
5370
|
}
|
|
5376
5371
|
function isPathWithinRoot(filePath, rootPath) {
|
|
5377
|
-
const
|
|
5378
|
-
return
|
|
5372
|
+
const relative14 = path9.relative(path9.resolve(rootPath), path9.resolve(filePath));
|
|
5373
|
+
return relative14 === "" || !relative14.startsWith(`..${path9.sep}`) && relative14 !== ".." && !path9.isAbsolute(relative14);
|
|
5379
5374
|
}
|
|
5380
5375
|
async function pruneExactMissingWorktreeRegistration(projectRoot, worktreePath) {
|
|
5381
5376
|
if (await pathExists(worktreePath)) return false;
|
|
@@ -5580,6 +5575,9 @@ function resolveWorktreeFallbackPath(projectRoot, relativePath) {
|
|
|
5580
5575
|
const fallbackPath = path10.join(mainRepoRoot, relativePath);
|
|
5581
5576
|
return (0, import_fs7.existsSync)(fallbackPath) ? fallbackPath : null;
|
|
5582
5577
|
}
|
|
5578
|
+
function getHostProjectConfigRelativePath(host) {
|
|
5579
|
+
return getProjectConfigRelativePath(host);
|
|
5580
|
+
}
|
|
5583
5581
|
function getProjectConfigCandidatePaths(projectRoot, host) {
|
|
5584
5582
|
const candidates = [path10.join(projectRoot, getProjectConfigRelativePath(host))];
|
|
5585
5583
|
if (host !== "opencode") {
|
|
@@ -5671,6 +5669,9 @@ function resolveProjectConfigPath(projectRoot, host) {
|
|
|
5671
5669
|
const candidates = getProjectConfigCandidatePaths(projectRoot, host);
|
|
5672
5670
|
return candidates.find((candidate) => (0, import_fs7.existsSync)(candidate)) ?? path10.join(projectRoot, getProjectConfigRelativePath(host));
|
|
5673
5671
|
}
|
|
5672
|
+
function resolveWritableProjectConfigPath(projectRoot, host) {
|
|
5673
|
+
return path10.join(projectRoot, getProjectConfigRelativePath(host));
|
|
5674
|
+
}
|
|
5674
5675
|
function resolveProjectIndexPath(projectRoot, scope, host) {
|
|
5675
5676
|
if (scope === "global") {
|
|
5676
5677
|
return resolveGlobalIndexPath(host);
|
|
@@ -5933,11 +5934,11 @@ function normalizeFiles(rawFiles, projectRoot) {
|
|
|
5933
5934
|
for (const raw of rawFiles) {
|
|
5934
5935
|
if (raw.length === 0) continue;
|
|
5935
5936
|
const absolute = path11.resolve(root, raw);
|
|
5936
|
-
const
|
|
5937
|
-
if (path11.isAbsolute(raw) ||
|
|
5937
|
+
const relative14 = path11.relative(root, absolute);
|
|
5938
|
+
if (path11.isAbsolute(raw) || relative14 === ".." || relative14.startsWith(`..${path11.sep}`) || path11.isAbsolute(relative14)) {
|
|
5938
5939
|
throw new Error(`Changed file escapes the project root: ${JSON.stringify(raw)}`);
|
|
5939
5940
|
}
|
|
5940
|
-
const cleaned =
|
|
5941
|
+
const cleaned = relative14.startsWith(`.${path11.sep}`) ? relative14.slice(2) : relative14;
|
|
5941
5942
|
if (!seen.has(cleaned)) {
|
|
5942
5943
|
seen.add(cleaned);
|
|
5943
5944
|
result.push(cleaned);
|
|
@@ -6274,7 +6275,7 @@ function analyzeQueryIntent(query) {
|
|
|
6274
6275
|
}
|
|
6275
6276
|
function isTestPath(filePath) {
|
|
6276
6277
|
const normalized = normalizePath(filePath);
|
|
6277
|
-
return /(?:^|\/)(?:test|tests|__tests__|spec|specs)(?:\/|$)/u.test(normalized) ||
|
|
6278
|
+
return /(?:^|\/)(?:test|tests|__tests__|spec|specs)(?:\/|$)/u.test(normalized) || /(?:\.(?:test|spec)|_(?:test|spec))\.[^/]+$/u.test(normalized) || /(?:^|\/)(?:test|spec)_[^/]+\.[^/]+$/u.test(normalized);
|
|
6278
6279
|
}
|
|
6279
6280
|
function isFixturePath(filePath) {
|
|
6280
6281
|
const normalized = normalizePath(filePath);
|
|
@@ -6376,6 +6377,10 @@ function scoreCandidate(query, intent, candidate, originalIndex) {
|
|
|
6376
6377
|
let boost = 0;
|
|
6377
6378
|
if (intent.primary === "conceptual") {
|
|
6378
6379
|
boost += Math.min(0.14, overlap * 0.14);
|
|
6380
|
+
if (intent.preferSourcePaths) {
|
|
6381
|
+
boost += implementationPath ? 0.32 : 0;
|
|
6382
|
+
if (testPath || fixturePath || docsPath) boost -= 0.35;
|
|
6383
|
+
}
|
|
6379
6384
|
if (generatedOrVendor) boost -= 0.18;
|
|
6380
6385
|
if (importChunk || weakContainer) boost -= 0.04;
|
|
6381
6386
|
} else if (intent.primary === "test") {
|
|
@@ -6681,7 +6686,8 @@ function rankHybridResults(query, semanticResults, keywordResults, options) {
|
|
|
6681
6686
|
return cached;
|
|
6682
6687
|
}
|
|
6683
6688
|
}
|
|
6684
|
-
const
|
|
6689
|
+
const overfetchFactor = prioritizeSourcePaths ? 12 : 4;
|
|
6690
|
+
const overfetchLimit = Math.max(options.limit * overfetchFactor, options.limit);
|
|
6685
6691
|
const fused = options.fusionStrategy === "rrf" ? fuseResultsRrf(semanticResults, keywordResults, options.rrfK, overfetchLimit) : fuseResultsWeighted(semanticResults, keywordResults, options.hybridWeight, overfetchLimit);
|
|
6686
6692
|
const rerankPoolLimit = Math.max(overfetchLimit, options.rerankTopN * 3, options.limit * 6);
|
|
6687
6693
|
const rerankPool = fused.slice(0, rerankPoolLimit);
|
|
@@ -7289,6 +7295,19 @@ function parseOwner(value) {
|
|
|
7289
7295
|
if (typeof candidate.startedAt !== "string" || Number.isNaN(Date.parse(candidate.startedAt))) return null;
|
|
7290
7296
|
if (typeof candidate.operation !== "string" || !VALID_OPERATIONS.has(candidate.operation)) return null;
|
|
7291
7297
|
if (typeof candidate.token !== "string" || !UUID_PATTERN.test(candidate.token)) return null;
|
|
7298
|
+
if (candidate.recoveryProtocolVersion !== void 0 && candidate.recoveryProtocolVersion !== 1) return null;
|
|
7299
|
+
if (candidate.projectRoot !== void 0 && typeof candidate.projectRoot !== "string") return null;
|
|
7300
|
+
if (candidate.scopedRoots !== void 0) {
|
|
7301
|
+
if (!Array.isArray(candidate.scopedRoots) || candidate.scopedRoots.some((root) => typeof root !== "string")) {
|
|
7302
|
+
return null;
|
|
7303
|
+
}
|
|
7304
|
+
}
|
|
7305
|
+
if (candidate.clearRecovery !== void 0) {
|
|
7306
|
+
const recovery = candidate.clearRecovery;
|
|
7307
|
+
if (typeof recovery !== "object" || recovery === null || recovery.phase !== "clearing" || typeof recovery.embeddingProvider !== "string" || recovery.embeddingProvider.length === 0 || typeof recovery.embeddingModel !== "string" || recovery.embeddingModel.length === 0 || !Number.isInteger(recovery.embeddingDimensions) || (recovery.embeddingDimensions ?? 0) <= 0 || typeof recovery.embeddingStrategyVersion !== "string" || recovery.embeddingStrategyVersion.length === 0 || recovery.compatibilityDecision !== "compatible" && recovery.compatibilityDecision !== "embedding-strategy-mismatch" && recovery.compatibilityDecision !== "incompatible" || candidate.operation !== "clear" && candidate.operation !== "force-index") {
|
|
7308
|
+
return null;
|
|
7309
|
+
}
|
|
7310
|
+
}
|
|
7292
7311
|
return candidate;
|
|
7293
7312
|
}
|
|
7294
7313
|
function parseReclaimOwner(value) {
|
|
@@ -7529,13 +7548,18 @@ function isTransientIndexLockContention(error) {
|
|
|
7529
7548
|
if (!isIndexLockContentionError(error) || !("reason" in error)) return false;
|
|
7530
7549
|
return error.reason === "active" || error.reason === "reclaiming";
|
|
7531
7550
|
}
|
|
7532
|
-
function acquireIndexLock(indexPath, operation) {
|
|
7551
|
+
function acquireIndexLock(indexPath, operation, recoveryScope) {
|
|
7533
7552
|
(0, import_fs9.mkdirSync)(indexPath, { recursive: true });
|
|
7534
7553
|
const canonicalIndexPath = import_fs9.realpathSync.native(indexPath);
|
|
7535
7554
|
const lockPath = path13.join(canonicalIndexPath, "indexing.lock");
|
|
7536
7555
|
cleanupDeadPublicationCandidates(canonicalIndexPath);
|
|
7537
7556
|
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
7538
|
-
const owner = createOwner(operation)
|
|
7557
|
+
const owner = recoveryScope === void 0 ? createOwner(operation) : {
|
|
7558
|
+
...createOwner(operation),
|
|
7559
|
+
recoveryProtocolVersion: 1,
|
|
7560
|
+
projectRoot: recoveryScope.projectRoot,
|
|
7561
|
+
scopedRoots: recoveryScope.scopedRoots
|
|
7562
|
+
};
|
|
7539
7563
|
if (publishJsonDirectory(lockPath, owner)) {
|
|
7540
7564
|
const lease = {
|
|
7541
7565
|
canonicalIndexPath,
|
|
@@ -7600,6 +7624,33 @@ function releaseIndexLock(lease) {
|
|
|
7600
7624
|
}
|
|
7601
7625
|
return true;
|
|
7602
7626
|
}
|
|
7627
|
+
function setIndexLockClearRecoveryState(lease, clearRecovery) {
|
|
7628
|
+
const currentOwner = readDirectoryOwner(lease.lockPath);
|
|
7629
|
+
if (!currentOwner || !sameOwner(currentOwner, lease.owner)) {
|
|
7630
|
+
throw new Error(`Lost ownership of index mutation lease ${lease.owner.token}`);
|
|
7631
|
+
}
|
|
7632
|
+
const nextOwner = { ...currentOwner };
|
|
7633
|
+
if (clearRecovery === null) {
|
|
7634
|
+
delete nextOwner.clearRecovery;
|
|
7635
|
+
} else {
|
|
7636
|
+
nextOwner.clearRecovery = clearRecovery;
|
|
7637
|
+
}
|
|
7638
|
+
const ownerPath = path13.join(lease.lockPath, OWNER_FILE_NAME);
|
|
7639
|
+
const temporaryPath = path13.join(
|
|
7640
|
+
lease.lockPath,
|
|
7641
|
+
`${OWNER_FILE_NAME}.tmp.${lease.owner.pid}.${lease.owner.token}.${(0, import_crypto2.randomUUID)()}`
|
|
7642
|
+
);
|
|
7643
|
+
try {
|
|
7644
|
+
(0, import_fs9.writeFileSync)(temporaryPath, JSON.stringify(nextOwner), {
|
|
7645
|
+
encoding: "utf-8",
|
|
7646
|
+
flag: "wx",
|
|
7647
|
+
mode: 384
|
|
7648
|
+
});
|
|
7649
|
+
retryTransientFilesystemOperation(() => (0, import_fs9.renameSync)(temporaryPath, ownerPath));
|
|
7650
|
+
} finally {
|
|
7651
|
+
if ((0, import_fs9.existsSync)(temporaryPath)) (0, import_fs9.rmSync)(temporaryPath, { force: true });
|
|
7652
|
+
}
|
|
7653
|
+
}
|
|
7603
7654
|
function createLeaseTemporaryPath(targetPath, owner, kind = "tmp") {
|
|
7604
7655
|
if (kind === "bak") return `${targetPath}.bak.${owner.pid}.${owner.token}`;
|
|
7605
7656
|
temporaryCounter += 1;
|
|
@@ -7837,6 +7888,18 @@ function createFailedBatchWriter(targetPath) {
|
|
|
7837
7888
|
temporaryPath
|
|
7838
7889
|
};
|
|
7839
7890
|
}
|
|
7891
|
+
function writeFailedBatchRecords(targetPath, records) {
|
|
7892
|
+
const writer = createFailedBatchWriter(targetPath);
|
|
7893
|
+
try {
|
|
7894
|
+
for (const record of records) {
|
|
7895
|
+
writer.write(record);
|
|
7896
|
+
}
|
|
7897
|
+
writer.commit();
|
|
7898
|
+
} catch (error) {
|
|
7899
|
+
writer.cleanup();
|
|
7900
|
+
throw error;
|
|
7901
|
+
}
|
|
7902
|
+
}
|
|
7840
7903
|
function* readLegacyFailedBatchRecords(filePath, options) {
|
|
7841
7904
|
const rawData = fs2.readFileSync(filePath, "utf-8");
|
|
7842
7905
|
const trimmed = stripLeadingBomAndWhitespace(rawData).trim();
|
|
@@ -8119,14 +8182,18 @@ function getSafeEmbeddingChunkTokenLimit(provider) {
|
|
|
8119
8182
|
const maxChunkTokens = Math.max(256, Math.floor(providerMaxTokens * 0.75));
|
|
8120
8183
|
return Math.min(2e3, maxChunkTokens);
|
|
8121
8184
|
}
|
|
8122
|
-
|
|
8123
|
-
|
|
8124
|
-
|
|
8125
|
-
|
|
8126
|
-
|
|
8127
|
-
};
|
|
8185
|
+
var DEFAULT_OLLAMA_MAX_BATCH_ITEMS = 16;
|
|
8186
|
+
var DEFAULT_OLLAMA_MAX_BATCH_TOKENS = 65536;
|
|
8187
|
+
function getDynamicBatchOptions(provider, embeddingBatch) {
|
|
8188
|
+
if (provider.provider !== "ollama") {
|
|
8189
|
+
return {};
|
|
8128
8190
|
}
|
|
8129
|
-
|
|
8191
|
+
const base = { maxBatchTokens: DEFAULT_OLLAMA_MAX_BATCH_TOKENS, maxBatchItems: DEFAULT_OLLAMA_MAX_BATCH_ITEMS };
|
|
8192
|
+
return {
|
|
8193
|
+
...base,
|
|
8194
|
+
...typeof embeddingBatch?.maxBatchTokens === "number" && Number.isFinite(embeddingBatch.maxBatchTokens) ? { maxBatchTokens: embeddingBatch.maxBatchTokens } : {},
|
|
8195
|
+
...typeof embeddingBatch?.maxBatchItems === "number" && Number.isFinite(embeddingBatch.maxBatchItems) ? { maxBatchItems: embeddingBatch.maxBatchItems } : {}
|
|
8196
|
+
};
|
|
8130
8197
|
}
|
|
8131
8198
|
function isSqliteCorruptionError(error) {
|
|
8132
8199
|
const message = getErrorMessage3(error).toLowerCase();
|
|
@@ -8144,6 +8211,14 @@ function getPendingChunkId(rawChunk) {
|
|
|
8144
8211
|
const id = rawChunk.id;
|
|
8145
8212
|
return typeof id === "string" ? id : null;
|
|
8146
8213
|
}
|
|
8214
|
+
function parseBlameTimestamp(value, endOfDay) {
|
|
8215
|
+
let timestampMs = Date.parse(value);
|
|
8216
|
+
if (Number.isNaN(timestampMs)) return null;
|
|
8217
|
+
if (endOfDay && /^\d{4}-\d{2}-\d{2}$/.test(value.trim())) {
|
|
8218
|
+
timestampMs += 24 * 60 * 60 * 1e3 - 1;
|
|
8219
|
+
}
|
|
8220
|
+
return Math.floor(timestampMs / 1e3);
|
|
8221
|
+
}
|
|
8147
8222
|
function metadataFromBlame(blame) {
|
|
8148
8223
|
if (!blame) {
|
|
8149
8224
|
return {};
|
|
@@ -8290,7 +8365,7 @@ function promoteIdentifierMatches(query, combined, semanticCandidates, keywordCa
|
|
|
8290
8365
|
const remainder = combined.filter((candidate) => !promotedIds.has(candidate.id));
|
|
8291
8366
|
return [...promoted, ...remainder];
|
|
8292
8367
|
}
|
|
8293
|
-
function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbolIds, limit, fallbackCandidates, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source") {
|
|
8368
|
+
function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbolIds, limit, fallbackCandidates, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source", allowNonSourcePaths = false) {
|
|
8294
8369
|
if (!prioritizeSourcePaths) {
|
|
8295
8370
|
return [];
|
|
8296
8371
|
}
|
|
@@ -8310,7 +8385,7 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbol
|
|
|
8310
8385
|
if (!isImplementationChunkType(chunkType)) {
|
|
8311
8386
|
return false;
|
|
8312
8387
|
}
|
|
8313
|
-
if (!isLikelyImplementationPath2(chunk.filePath)) {
|
|
8388
|
+
if (!allowNonSourcePaths && !isLikelyImplementationPath2(chunk.filePath)) {
|
|
8314
8389
|
return false;
|
|
8315
8390
|
}
|
|
8316
8391
|
const nameLower = (chunk.name ?? "").toLowerCase();
|
|
@@ -8374,7 +8449,7 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbol
|
|
|
8374
8449
|
}
|
|
8375
8450
|
foundCoveringChunk = upsertChunkCandidate(chunk, identifier, normalizedIdentifier) || foundCoveringChunk;
|
|
8376
8451
|
}
|
|
8377
|
-
if (foundCoveringChunk || !isLikelyImplementationPath2(symbol.filePath)) {
|
|
8452
|
+
if (foundCoveringChunk || !allowNonSourcePaths && !isLikelyImplementationPath2(symbol.filePath)) {
|
|
8378
8453
|
continue;
|
|
8379
8454
|
}
|
|
8380
8455
|
const symbolName = symbol.name.toLowerCase();
|
|
@@ -8428,7 +8503,7 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbol
|
|
|
8428
8503
|
const ranked = Array.from(symbolCandidates.values()).sort((a, b) => b.score - a.score || a.id.localeCompare(b.id));
|
|
8429
8504
|
if (ranked.length === 0) {
|
|
8430
8505
|
const implementationFallback = fallbackCandidates.filter(
|
|
8431
|
-
(candidate) => isImplementationChunkType(candidate.metadata.chunkType) && isLikelyImplementationPath2(candidate.metadata.filePath)
|
|
8506
|
+
(candidate) => isImplementationChunkType(candidate.metadata.chunkType) && (allowNonSourcePaths || isLikelyImplementationPath2(candidate.metadata.filePath))
|
|
8432
8507
|
);
|
|
8433
8508
|
for (const candidate of implementationFallback) {
|
|
8434
8509
|
const nameLower = (candidate.metadata.name ?? "").toLowerCase();
|
|
@@ -8544,10 +8619,16 @@ function matchesHardSearchFilters(candidate, options, projectRoot) {
|
|
|
8544
8619
|
return false;
|
|
8545
8620
|
}
|
|
8546
8621
|
if (options?.blameSince) {
|
|
8547
|
-
const
|
|
8548
|
-
if (
|
|
8622
|
+
const since = parseBlameTimestamp(options.blameSince, false);
|
|
8623
|
+
if (since === null) return false;
|
|
8549
8624
|
const committedAt = candidate.metadata.blameCommittedAt;
|
|
8550
|
-
if (committedAt === void 0 || committedAt <
|
|
8625
|
+
if (committedAt === void 0 || committedAt < since) return false;
|
|
8626
|
+
}
|
|
8627
|
+
if (options?.blameUntil) {
|
|
8628
|
+
const until = parseBlameTimestamp(options.blameUntil, true);
|
|
8629
|
+
if (until === null) return false;
|
|
8630
|
+
const committedAt = candidate.metadata.blameCommittedAt;
|
|
8631
|
+
if (committedAt === void 0 || committedAt > until) return false;
|
|
8551
8632
|
}
|
|
8552
8633
|
return true;
|
|
8553
8634
|
}
|
|
@@ -8583,7 +8664,6 @@ var Indexer = class _Indexer {
|
|
|
8583
8664
|
database = null;
|
|
8584
8665
|
provider = null;
|
|
8585
8666
|
configuredProviderInfo = null;
|
|
8586
|
-
reranker = null;
|
|
8587
8667
|
fileHashCache = /* @__PURE__ */ new Map();
|
|
8588
8668
|
fileHashCachePath = "";
|
|
8589
8669
|
failedBatchesPath = "";
|
|
@@ -8604,9 +8684,10 @@ var Indexer = class _Indexer {
|
|
|
8604
8684
|
writerArtifactFingerprint = null;
|
|
8605
8685
|
readerArtifactRetryAfter = /* @__PURE__ */ new Map();
|
|
8606
8686
|
fileBatchLimits;
|
|
8687
|
+
checkpointIntervalChunks;
|
|
8607
8688
|
constructor(projectRoot, config, host, runtimeOptions = {}) {
|
|
8608
8689
|
this.projectRoot = projectRoot;
|
|
8609
|
-
this.projectIdentityHash =
|
|
8690
|
+
this.projectIdentityHash = this.getProjectIdentityHash(projectRoot);
|
|
8610
8691
|
this.materializedProjectRoot = runtimeOptions.materializedProjectRoot ?? projectRoot;
|
|
8611
8692
|
this.branchNameOverride = runtimeOptions.branchName;
|
|
8612
8693
|
this.catalogIdentityOverride = runtimeOptions.catalogIdentity;
|
|
@@ -8616,6 +8697,7 @@ var Indexer = class _Indexer {
|
|
|
8616
8697
|
this.expectedCommitOverride = runtimeOptions.expectedCommit?.toLowerCase();
|
|
8617
8698
|
this.indexPathOverride = runtimeOptions.indexPath;
|
|
8618
8699
|
this.fileBatchLimits = runtimeOptions.fileBatchLimits;
|
|
8700
|
+
this.checkpointIntervalChunks = runtimeOptions.checkpointIntervalChunks;
|
|
8619
8701
|
this.config = config;
|
|
8620
8702
|
this.host = host;
|
|
8621
8703
|
if (isGitRepo(this.materializedProjectRoot)) {
|
|
@@ -8727,6 +8809,9 @@ var Indexer = class _Indexer {
|
|
|
8727
8809
|
return path15.resolve(targetPath);
|
|
8728
8810
|
}
|
|
8729
8811
|
}
|
|
8812
|
+
getProjectIdentityHash(projectRoot) {
|
|
8813
|
+
return hashContent(this.getCanonicalPath(projectRoot)).slice(0, 16);
|
|
8814
|
+
}
|
|
8730
8815
|
isProjectOwnedIndexPath() {
|
|
8731
8816
|
return isProjectIndexPathOwnedByProject(this.projectRoot, this.indexPath, this.host);
|
|
8732
8817
|
}
|
|
@@ -8743,7 +8828,6 @@ var Indexer = class _Indexer {
|
|
|
8743
8828
|
this.database = null;
|
|
8744
8829
|
this.provider = null;
|
|
8745
8830
|
this.configuredProviderInfo = null;
|
|
8746
|
-
this.reranker = null;
|
|
8747
8831
|
this.indexCompatibility = null;
|
|
8748
8832
|
this.initializationMode = "none";
|
|
8749
8833
|
this.readIssues = [];
|
|
@@ -8764,7 +8848,10 @@ var Indexer = class _Indexer {
|
|
|
8764
8848
|
}
|
|
8765
8849
|
async withIndexMutationLease(operation, callback) {
|
|
8766
8850
|
this.refreshBranchInfo();
|
|
8767
|
-
const lease = acquireIndexLock(this.indexPath, operation
|
|
8851
|
+
const lease = acquireIndexLock(this.indexPath, operation, {
|
|
8852
|
+
projectRoot: this.projectRoot,
|
|
8853
|
+
scopedRoots: this.getScopedRoots()
|
|
8854
|
+
});
|
|
8768
8855
|
this.indexPath = lease.canonicalIndexPath;
|
|
8769
8856
|
this.refreshRuntimeArtifactPaths();
|
|
8770
8857
|
this.activeIndexLease = lease;
|
|
@@ -8819,6 +8906,7 @@ var Indexer = class _Indexer {
|
|
|
8819
8906
|
}
|
|
8820
8907
|
loadFileHashCache() {
|
|
8821
8908
|
if (!(0, import_fs10.existsSync)(this.fileHashCachePath)) {
|
|
8909
|
+
this.fileHashCache = /* @__PURE__ */ new Map();
|
|
8822
8910
|
return;
|
|
8823
8911
|
}
|
|
8824
8912
|
try {
|
|
@@ -8858,10 +8946,10 @@ var Indexer = class _Indexer {
|
|
|
8858
8946
|
invertedIndex.serialize()
|
|
8859
8947
|
);
|
|
8860
8948
|
}
|
|
8861
|
-
getScopedRoots() {
|
|
8862
|
-
const roots = /* @__PURE__ */ new Set([this.getCanonicalPath(
|
|
8949
|
+
getScopedRoots(projectRoot = this.projectRoot) {
|
|
8950
|
+
const roots = /* @__PURE__ */ new Set([this.getCanonicalPath(projectRoot)]);
|
|
8863
8951
|
for (const kbRoot of this.config.knowledgeBases) {
|
|
8864
|
-
roots.add(this.getCanonicalPath(path15.resolve(
|
|
8952
|
+
roots.add(this.getCanonicalPath(path15.resolve(projectRoot, kbRoot)));
|
|
8865
8953
|
}
|
|
8866
8954
|
return Array.from(roots);
|
|
8867
8955
|
}
|
|
@@ -8932,14 +9020,17 @@ var Indexer = class _Indexer {
|
|
|
8932
9020
|
getLegacyBranchCatalogKey() {
|
|
8933
9021
|
return this.currentBranch || "default";
|
|
8934
9022
|
}
|
|
8935
|
-
getLegacyMigrationMetadataKey() {
|
|
8936
|
-
return `index.globalBranchMigration.${
|
|
9023
|
+
getLegacyMigrationMetadataKey(projectIdentityHash = this.projectIdentityHash) {
|
|
9024
|
+
return `index.globalBranchMigration.${projectIdentityHash}`;
|
|
8937
9025
|
}
|
|
8938
|
-
getProjectEmbeddingStrategyMetadataKey() {
|
|
8939
|
-
return `index.embeddingStrategyVersion.${
|
|
9026
|
+
getProjectEmbeddingStrategyMetadataKey(projectIdentityHash = this.projectIdentityHash) {
|
|
9027
|
+
return `index.embeddingStrategyVersion.${projectIdentityHash}`;
|
|
8940
9028
|
}
|
|
8941
|
-
getProjectForceReembedMetadataKey() {
|
|
8942
|
-
return `index.forceReembed.${
|
|
9029
|
+
getProjectForceReembedMetadataKey(projectIdentityHash = this.projectIdentityHash) {
|
|
9030
|
+
return `index.forceReembed.${projectIdentityHash}`;
|
|
9031
|
+
}
|
|
9032
|
+
getProjectMigrationFinalizedMetadataKey(projectIdentityHash = this.projectIdentityHash) {
|
|
9033
|
+
return `index.migrationFinalized.${projectIdentityHash}`;
|
|
8943
9034
|
}
|
|
8944
9035
|
getBranchMigrationMetadataKey(prefix, catalogIdentity = this.getBranchCatalogIdentity()) {
|
|
8945
9036
|
const branchKey = this.getBranchCatalogKeyFor(catalogIdentity);
|
|
@@ -9045,7 +9136,7 @@ var Indexer = class _Indexer {
|
|
|
9045
9136
|
const legacy = this.getLegacyBranchCatalogKey();
|
|
9046
9137
|
return primary === legacy ? [primary] : [primary, legacy];
|
|
9047
9138
|
}
|
|
9048
|
-
getProjectLocalScopedOwnershipIds(roots) {
|
|
9139
|
+
getProjectLocalScopedOwnershipIds(roots, projectRoot = this.projectRoot) {
|
|
9049
9140
|
const chunkIds = /* @__PURE__ */ new Set();
|
|
9050
9141
|
const symbolIds = /* @__PURE__ */ new Set();
|
|
9051
9142
|
if (!this.database) {
|
|
@@ -9053,10 +9144,10 @@ var Indexer = class _Indexer {
|
|
|
9053
9144
|
}
|
|
9054
9145
|
const projectLocalFilePaths = /* @__PURE__ */ new Set([
|
|
9055
9146
|
...Array.from(this.fileHashCache.keys()).filter(
|
|
9056
|
-
(filePath) => this.isFileInCurrentScope(filePath, roots) && this.isFileInProjectRoot(filePath)
|
|
9147
|
+
(filePath) => this.isFileInCurrentScope(filePath, roots) && this.isFileInProjectRoot(filePath, projectRoot)
|
|
9057
9148
|
),
|
|
9058
9149
|
...(this.store?.getAllMetadata() ?? []).map(({ metadata }) => metadata.filePath).filter(
|
|
9059
|
-
(filePath) => this.isFileInCurrentScope(filePath, roots) && this.isFileInProjectRoot(filePath)
|
|
9150
|
+
(filePath) => this.isFileInCurrentScope(filePath, roots) && this.isFileInProjectRoot(filePath, projectRoot)
|
|
9060
9151
|
)
|
|
9061
9152
|
]);
|
|
9062
9153
|
for (const filePath of projectLocalFilePaths) {
|
|
@@ -9069,15 +9160,16 @@ var Indexer = class _Indexer {
|
|
|
9069
9160
|
}
|
|
9070
9161
|
return { chunkIds, symbolIds };
|
|
9071
9162
|
}
|
|
9072
|
-
getProjectScopedBranchCatalogCleanupKeys(projectChunkIds, projectSymbolIds) {
|
|
9163
|
+
getProjectScopedBranchCatalogCleanupKeys(projectChunkIds, projectSymbolIds, projectRoot = this.projectRoot) {
|
|
9073
9164
|
if (this.config.scope !== "global") {
|
|
9074
9165
|
return this.getBranchCatalogCleanupKeys();
|
|
9075
9166
|
}
|
|
9076
9167
|
const keys = /* @__PURE__ */ new Set();
|
|
9077
9168
|
const projectChunkIdSet = new Set(projectChunkIds);
|
|
9078
9169
|
const projectSymbolIdSet = new Set(projectSymbolIds);
|
|
9170
|
+
const projectIdentityHash = this.getProjectIdentityHash(projectRoot);
|
|
9079
9171
|
for (const branchKey of this.database?.getAllBranches() ?? []) {
|
|
9080
|
-
if (branchKey.startsWith(`${
|
|
9172
|
+
if (branchKey.startsWith(`${projectIdentityHash}:`)) {
|
|
9081
9173
|
keys.add(branchKey);
|
|
9082
9174
|
continue;
|
|
9083
9175
|
}
|
|
@@ -9087,8 +9179,10 @@ var Indexer = class _Indexer {
|
|
|
9087
9179
|
keys.add(branchKey);
|
|
9088
9180
|
}
|
|
9089
9181
|
}
|
|
9090
|
-
|
|
9091
|
-
|
|
9182
|
+
if (projectRoot === this.projectRoot) {
|
|
9183
|
+
for (const branchKey of this.getBranchCatalogCleanupKeys()) {
|
|
9184
|
+
keys.add(branchKey);
|
|
9185
|
+
}
|
|
9092
9186
|
}
|
|
9093
9187
|
return Array.from(keys);
|
|
9094
9188
|
}
|
|
@@ -9096,10 +9190,10 @@ var Indexer = class _Indexer {
|
|
|
9096
9190
|
const canonicalFilePath = this.getCanonicalStoredFilePath(filePath);
|
|
9097
9191
|
return roots.some((root) => isPathWithinRoot2(canonicalFilePath, root));
|
|
9098
9192
|
}
|
|
9099
|
-
isFileInProjectRoot(filePath) {
|
|
9193
|
+
isFileInProjectRoot(filePath, projectRoot = this.projectRoot) {
|
|
9100
9194
|
return isPathWithinRoot2(
|
|
9101
9195
|
this.getCanonicalStoredFilePath(filePath),
|
|
9102
|
-
this.getCanonicalPath(
|
|
9196
|
+
this.getCanonicalPath(projectRoot)
|
|
9103
9197
|
);
|
|
9104
9198
|
}
|
|
9105
9199
|
clearScopedFileHashCache(roots) {
|
|
@@ -9141,12 +9235,12 @@ var Indexer = class _Indexer {
|
|
|
9141
9235
|
}
|
|
9142
9236
|
return false;
|
|
9143
9237
|
}
|
|
9144
|
-
hasForeignScopedBranchData() {
|
|
9238
|
+
hasForeignScopedBranchData(projectRoot = this.projectRoot, roots = this.getScopedRoots(projectRoot)) {
|
|
9145
9239
|
if (!this.database || this.config.scope !== "global") {
|
|
9146
9240
|
return false;
|
|
9147
9241
|
}
|
|
9148
|
-
const
|
|
9149
|
-
const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots);
|
|
9242
|
+
const projectIdentityHash = this.getProjectIdentityHash(projectRoot);
|
|
9243
|
+
const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots, projectRoot);
|
|
9150
9244
|
return this.database.getAllBranches().some(
|
|
9151
9245
|
(branchKey) => {
|
|
9152
9246
|
const branchChunkIds = this.database.getBranchChunkIds(branchKey);
|
|
@@ -9155,7 +9249,7 @@ var Indexer = class _Indexer {
|
|
|
9155
9249
|
if (!hasBranchData) {
|
|
9156
9250
|
return false;
|
|
9157
9251
|
}
|
|
9158
|
-
if (branchKey.startsWith(`${
|
|
9252
|
+
if (branchKey.startsWith(`${projectIdentityHash}:`)) {
|
|
9159
9253
|
return false;
|
|
9160
9254
|
}
|
|
9161
9255
|
const referencesCurrentProjectChunks = branchChunkIds.some((chunkId) => projectLocalChunkIds.has(chunkId));
|
|
@@ -9164,7 +9258,7 @@ var Indexer = class _Indexer {
|
|
|
9164
9258
|
}
|
|
9165
9259
|
);
|
|
9166
9260
|
}
|
|
9167
|
-
clearSharedIndexProjectData(store, invertedIndex, database, roots) {
|
|
9261
|
+
clearSharedIndexProjectData(store, invertedIndex, database, roots, projectRoot = this.projectRoot) {
|
|
9168
9262
|
const allMetadata = store.getAllMetadata();
|
|
9169
9263
|
const scopedEntries = allMetadata.filter(({ metadata }) => this.isFileInCurrentScope(metadata.filePath, roots));
|
|
9170
9264
|
const filePaths = /* @__PURE__ */ new Set([
|
|
@@ -9172,7 +9266,7 @@ var Indexer = class _Indexer {
|
|
|
9172
9266
|
...scopedEntries.map(({ metadata }) => metadata.filePath)
|
|
9173
9267
|
]);
|
|
9174
9268
|
const projectLocalFilePaths = new Set(
|
|
9175
|
-
Array.from(filePaths).filter((filePath) => this.isFileInProjectRoot(filePath))
|
|
9269
|
+
Array.from(filePaths).filter((filePath) => this.isFileInProjectRoot(filePath, projectRoot))
|
|
9176
9270
|
);
|
|
9177
9271
|
const removedChunkIds = new Set(scopedEntries.map(({ key }) => key));
|
|
9178
9272
|
for (const filePath of filePaths) {
|
|
@@ -9182,7 +9276,7 @@ var Indexer = class _Indexer {
|
|
|
9182
9276
|
}
|
|
9183
9277
|
const removedChunkIdList = Array.from(removedChunkIds);
|
|
9184
9278
|
const projectLocalChunkIds = new Set(
|
|
9185
|
-
scopedEntries.filter(({ metadata }) => this.isFileInProjectRoot(metadata.filePath)).map(({ key }) => key)
|
|
9279
|
+
scopedEntries.filter(({ metadata }) => this.isFileInProjectRoot(metadata.filePath, projectRoot)).map(({ key }) => key)
|
|
9186
9280
|
);
|
|
9187
9281
|
for (const filePath of projectLocalFilePaths) {
|
|
9188
9282
|
for (const chunk of database.getChunksByFile(filePath)) {
|
|
@@ -9201,7 +9295,8 @@ var Indexer = class _Indexer {
|
|
|
9201
9295
|
}
|
|
9202
9296
|
const branchCleanupKeys = this.getProjectScopedBranchCatalogCleanupKeys(
|
|
9203
9297
|
Array.from(projectLocalChunkIds),
|
|
9204
|
-
Array.from(projectLocalSymbolIds)
|
|
9298
|
+
Array.from(projectLocalSymbolIds),
|
|
9299
|
+
projectRoot
|
|
9205
9300
|
);
|
|
9206
9301
|
for (const branchKey of branchCleanupKeys) {
|
|
9207
9302
|
database.deleteBranchChunksForBranch(branchKey, removedChunkIdList);
|
|
@@ -9236,29 +9331,96 @@ var Indexer = class _Indexer {
|
|
|
9236
9331
|
database.gcOrphanSymbols();
|
|
9237
9332
|
database.gcOrphanEmbeddings();
|
|
9238
9333
|
database.gcOrphanChunks();
|
|
9239
|
-
store.save();
|
|
9240
9334
|
this.saveInvertedIndex(invertedIndex);
|
|
9335
|
+
store.save();
|
|
9241
9336
|
return {
|
|
9242
9337
|
removedChunkIds: removedChunkIdList,
|
|
9243
9338
|
hasForeignData: allMetadata.some(({ metadata }) => !this.isFileInCurrentScope(metadata.filePath, roots))
|
|
9244
9339
|
};
|
|
9245
9340
|
}
|
|
9341
|
+
getCurrentClearRecoveryState() {
|
|
9342
|
+
if (!this.configuredProviderInfo) {
|
|
9343
|
+
throw new Error("Cannot persist clear recovery state before the embedding provider is initialized");
|
|
9344
|
+
}
|
|
9345
|
+
const compatibility = this.checkCompatibility();
|
|
9346
|
+
const compatibilityDecision = compatibility.compatible ? "compatible" : compatibility.code === "EMBEDDING_STRATEGY_MISMATCH" /* EMBEDDING_STRATEGY_MISMATCH */ ? "embedding-strategy-mismatch" : "incompatible";
|
|
9347
|
+
return {
|
|
9348
|
+
phase: "clearing",
|
|
9349
|
+
embeddingProvider: this.configuredProviderInfo.provider,
|
|
9350
|
+
embeddingModel: this.configuredProviderInfo.modelInfo.model,
|
|
9351
|
+
embeddingDimensions: this.configuredProviderInfo.modelInfo.dimensions,
|
|
9352
|
+
embeddingStrategyVersion: EMBEDDING_STRATEGY_VERSION,
|
|
9353
|
+
compatibilityDecision
|
|
9354
|
+
};
|
|
9355
|
+
}
|
|
9356
|
+
beginClearRecoveryState() {
|
|
9357
|
+
const recovery = this.getCurrentClearRecoveryState();
|
|
9358
|
+
setIndexLockClearRecoveryState(this.requireActiveLease(), recovery);
|
|
9359
|
+
return recovery;
|
|
9360
|
+
}
|
|
9361
|
+
finishClearRecoveryState() {
|
|
9362
|
+
setIndexLockClearRecoveryState(this.requireActiveLease(), null);
|
|
9363
|
+
}
|
|
9364
|
+
matchesCurrentClearRecoveryConfiguration(recovery) {
|
|
9365
|
+
const configuredProviderInfo = this.configuredProviderInfo;
|
|
9366
|
+
return configuredProviderInfo !== null && recovery.embeddingProvider === configuredProviderInfo.provider && recovery.embeddingModel === configuredProviderInfo.modelInfo.model && recovery.embeddingDimensions === configuredProviderInfo.modelInfo.dimensions && recovery.embeddingStrategyVersion === EMBEDDING_STRATEGY_VERSION;
|
|
9367
|
+
}
|
|
9368
|
+
hasUnknownLegacyForceIndexClear(owner) {
|
|
9369
|
+
return owner.operation === "force-index" && owner.clearRecovery === void 0 && owner.recoveryProtocolVersion !== 1 && (0, import_fs10.existsSync)(path15.join(this.indexPath, "force-index-phase"));
|
|
9370
|
+
}
|
|
9246
9371
|
async recoverFromInterruptedIndexingUnlocked(owners) {
|
|
9247
9372
|
for (const owner of owners) {
|
|
9248
9373
|
this.logger.warn("Detected interrupted indexing session, recovering...", {
|
|
9249
9374
|
pid: owner.pid,
|
|
9250
9375
|
hostname: owner.hostname,
|
|
9251
9376
|
operation: owner.operation,
|
|
9252
|
-
startedAt: owner.startedAt
|
|
9377
|
+
startedAt: owner.startedAt,
|
|
9378
|
+
projectRoot: owner.projectRoot
|
|
9253
9379
|
});
|
|
9254
9380
|
}
|
|
9255
9381
|
if (this.config.scope === "global") {
|
|
9256
|
-
|
|
9257
|
-
|
|
9382
|
+
const clearScopes = [];
|
|
9383
|
+
for (const owner of owners) {
|
|
9384
|
+
if (this.hasUnknownLegacyForceIndexClear(owner)) {
|
|
9385
|
+
throw new Error(
|
|
9386
|
+
`Cannot automatically recover interrupted force-index ${owner.token}: the legacy clearing phase ownership is unknown. The recovery marker was retained for manual inspection.`
|
|
9387
|
+
);
|
|
9388
|
+
}
|
|
9389
|
+
if (owner.operation === "clear" && owner.clearRecovery === void 0 && owner.recoveryProtocolVersion !== 1) {
|
|
9390
|
+
throw new Error(
|
|
9391
|
+
`Cannot automatically recover interrupted global clear ${owner.token}: the originating recovery state is unknown. The recovery marker was retained for manual inspection.`
|
|
9392
|
+
);
|
|
9393
|
+
}
|
|
9394
|
+
if (owner.clearRecovery === void 0) continue;
|
|
9395
|
+
if (!owner.projectRoot || !owner.scopedRoots || owner.scopedRoots.length === 0) {
|
|
9396
|
+
throw new Error(
|
|
9397
|
+
`Cannot automatically recover interrupted global clear ${owner.token}: the originating project scope is unknown. The recovery marker was retained for manual inspection.`
|
|
9398
|
+
);
|
|
9399
|
+
}
|
|
9400
|
+
if (!this.matchesCurrentClearRecoveryConfiguration(owner.clearRecovery)) {
|
|
9401
|
+
throw new Error(
|
|
9402
|
+
`Cannot automatically recover interrupted global clear ${owner.token}: the current embedding configuration does not match the originating lease. The recovery marker was retained; retry from the originating project with matching settings.`
|
|
9403
|
+
);
|
|
9404
|
+
}
|
|
9405
|
+
clearScopes.push({
|
|
9406
|
+
projectRoot: owner.projectRoot,
|
|
9407
|
+
scopedRoots: owner.scopedRoots,
|
|
9408
|
+
compatibilityDecision: owner.clearRecovery.compatibilityDecision
|
|
9409
|
+
});
|
|
9410
|
+
}
|
|
9411
|
+
if (clearScopes.length > 0) {
|
|
9412
|
+
this.loadFileHashCache();
|
|
9413
|
+
}
|
|
9414
|
+
for (const { projectRoot, scopedRoots, compatibilityDecision } of clearScopes) {
|
|
9415
|
+
this.clearGlobalIndexUnlocked(projectRoot, scopedRoots, compatibilityDecision);
|
|
9258
9416
|
}
|
|
9259
9417
|
await this.healthCheckUnlocked();
|
|
9418
|
+
this.logger.info(
|
|
9419
|
+
clearScopes.length > 0 ? "Recovery complete, next index will rebuild all files" : "Recovery complete, next index will resume from the last checkpoint"
|
|
9420
|
+
);
|
|
9421
|
+
return;
|
|
9260
9422
|
}
|
|
9261
|
-
this.logger.info("Recovery complete, next index will
|
|
9423
|
+
this.logger.info("Recovery complete, next index will resume from the last checkpoint");
|
|
9262
9424
|
}
|
|
9263
9425
|
*loadSerializedFailedBatches() {
|
|
9264
9426
|
let warned = false;
|
|
@@ -9296,33 +9458,118 @@ var Indexer = class _Indexer {
|
|
|
9296
9458
|
state.writer.write(record);
|
|
9297
9459
|
state.recordsWritten += record.chunks.length;
|
|
9298
9460
|
}
|
|
9299
|
-
finalizeFailedBatchWriteState(state) {
|
|
9461
|
+
finalizeFailedBatchWriteState(state, resolvedChunkIds = /* @__PURE__ */ new Set()) {
|
|
9300
9462
|
if (state.recordsWritten > 0) {
|
|
9301
|
-
|
|
9463
|
+
const seenChunkIds = /* @__PURE__ */ new Set();
|
|
9464
|
+
const retained = [];
|
|
9465
|
+
const records = Array.from(readFailedBatchRecords(state.writer.temporaryPath));
|
|
9466
|
+
for (let i = records.length - 1; i >= 0; i--) {
|
|
9467
|
+
const chunks = records[i].chunks.filter((rawChunk) => {
|
|
9468
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
9469
|
+
if (chunkId !== null) {
|
|
9470
|
+
if (resolvedChunkIds.has(chunkId)) return false;
|
|
9471
|
+
if (seenChunkIds.has(chunkId)) return false;
|
|
9472
|
+
seenChunkIds.add(chunkId);
|
|
9473
|
+
}
|
|
9474
|
+
return true;
|
|
9475
|
+
});
|
|
9476
|
+
if (chunks.length > 0) {
|
|
9477
|
+
retained.unshift({ ...records[i], chunks });
|
|
9478
|
+
}
|
|
9479
|
+
}
|
|
9480
|
+
state.writer.cleanup();
|
|
9481
|
+
if (retained.length > 0) {
|
|
9482
|
+
writeFailedBatchRecords(this.failedBatchesPath, retained);
|
|
9483
|
+
} else {
|
|
9484
|
+
writeFailedBatchRecords(this.failedBatchesPath, []);
|
|
9485
|
+
this.clearFailedBatchState();
|
|
9486
|
+
}
|
|
9302
9487
|
return;
|
|
9303
9488
|
}
|
|
9304
|
-
state.writer.
|
|
9489
|
+
state.writer.commit();
|
|
9305
9490
|
this.clearFailedBatchState();
|
|
9306
9491
|
}
|
|
9307
|
-
|
|
9308
|
-
|
|
9309
|
-
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
}
|
|
9313
|
-
}
|
|
9492
|
+
getCheckpointIntervalChunks(totalChunks) {
|
|
9493
|
+
return Math.max(
|
|
9494
|
+
this.checkpointIntervalChunks ?? 2e3,
|
|
9495
|
+
Math.floor(totalChunks / 10)
|
|
9496
|
+
);
|
|
9314
9497
|
}
|
|
9315
|
-
|
|
9316
|
-
|
|
9317
|
-
|
|
9318
|
-
|
|
9319
|
-
|
|
9320
|
-
|
|
9321
|
-
|
|
9322
|
-
|
|
9323
|
-
|
|
9324
|
-
|
|
9325
|
-
|
|
9498
|
+
checkpointIndexRun(database, store, invertedIndex, failedProcessing, resolvedRetryChunkIds, currentFileHashes, committedFilePaths, scopedRoots, configuredProviderInfo) {
|
|
9499
|
+
if (!this.hasProjectForceReembedPending()) {
|
|
9500
|
+
this.saveIndexMetadata(configuredProviderInfo);
|
|
9501
|
+
this.indexCompatibility = { compatible: true };
|
|
9502
|
+
}
|
|
9503
|
+
database.commitWriteTransaction();
|
|
9504
|
+
database.beginWriteTransaction();
|
|
9505
|
+
this.saveInvertedIndex(invertedIndex);
|
|
9506
|
+
store.save();
|
|
9507
|
+
if (failedProcessing.state.recordsWritten > 0 || failedProcessing.latestById.size > 0 || failedProcessing.discardedExistingRecords) {
|
|
9508
|
+
for (const metadata of failedProcessing.latestById.values()) {
|
|
9509
|
+
const alreadyMaterialized = metadata.chunks.some((rawChunk) => {
|
|
9510
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
9511
|
+
return chunkId !== null && failedProcessing.materializedRetryIds.has(chunkId);
|
|
9512
|
+
});
|
|
9513
|
+
if (alreadyMaterialized) continue;
|
|
9514
|
+
this.writeFailedBatchRecord(failedProcessing.state, {
|
|
9515
|
+
chunks: metadata.chunks,
|
|
9516
|
+
attemptCount: metadata.attemptCount,
|
|
9517
|
+
error: metadata.error,
|
|
9518
|
+
lastAttempt: metadata.lastAttempt
|
|
9519
|
+
});
|
|
9520
|
+
for (const rawChunk of metadata.chunks) {
|
|
9521
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
9522
|
+
if (chunkId !== null) {
|
|
9523
|
+
failedProcessing.materializedRetryIds.add(chunkId);
|
|
9524
|
+
}
|
|
9525
|
+
}
|
|
9526
|
+
}
|
|
9527
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state, resolvedRetryChunkIds);
|
|
9528
|
+
failedProcessing.state = this.createFailedBatchWriteState();
|
|
9529
|
+
failedProcessing.discardedExistingRecords = false;
|
|
9530
|
+
for (const record of this.loadSerializedFailedBatches()) {
|
|
9531
|
+
for (const rawChunk of record.chunks) {
|
|
9532
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
9533
|
+
this.writeFailedBatchRecord(failedProcessing.state, { ...record, chunks: [rawChunk] });
|
|
9534
|
+
if (chunkId !== null) {
|
|
9535
|
+
failedProcessing.materializedRetryIds.add(chunkId);
|
|
9536
|
+
}
|
|
9537
|
+
}
|
|
9538
|
+
}
|
|
9539
|
+
}
|
|
9540
|
+
const partialHashes = /* @__PURE__ */ new Map();
|
|
9541
|
+
for (const filePath of committedFilePaths) {
|
|
9542
|
+
const hash = currentFileHashes.get(filePath);
|
|
9543
|
+
if (hash !== void 0) {
|
|
9544
|
+
partialHashes.set(filePath, hash);
|
|
9545
|
+
}
|
|
9546
|
+
}
|
|
9547
|
+
if (scopedRoots) {
|
|
9548
|
+
this.replaceScopedFileHashCache(partialHashes, scopedRoots);
|
|
9549
|
+
} else {
|
|
9550
|
+
this.fileHashCache = partialHashes;
|
|
9551
|
+
this.saveFileHashCache();
|
|
9552
|
+
}
|
|
9553
|
+
}
|
|
9554
|
+
clearFailedBatchState() {
|
|
9555
|
+
if ((0, import_fs10.existsSync)(this.failedBatchesPath)) {
|
|
9556
|
+
try {
|
|
9557
|
+
(0, import_fs10.unlinkSync)(this.failedBatchesPath);
|
|
9558
|
+
} catch {
|
|
9559
|
+
}
|
|
9560
|
+
}
|
|
9561
|
+
}
|
|
9562
|
+
rewriteFailedBatchState(shouldRetain) {
|
|
9563
|
+
const state = this.createFailedBatchWriteState();
|
|
9564
|
+
try {
|
|
9565
|
+
for (const batch of this.loadSerializedFailedBatches()) {
|
|
9566
|
+
const retainedChunks = batch.chunks.filter(shouldRetain);
|
|
9567
|
+
if (retainedChunks.length > 0) {
|
|
9568
|
+
this.writeFailedBatchRecord(state, { ...batch, chunks: retainedChunks });
|
|
9569
|
+
}
|
|
9570
|
+
}
|
|
9571
|
+
this.finalizeFailedBatchWriteState(state);
|
|
9572
|
+
} catch (error) {
|
|
9326
9573
|
state.writer.cleanup();
|
|
9327
9574
|
throw error;
|
|
9328
9575
|
}
|
|
@@ -9330,6 +9577,7 @@ var Indexer = class _Indexer {
|
|
|
9330
9577
|
prepareFailedBatchProcessing(roots, shouldProcess) {
|
|
9331
9578
|
const state = this.createFailedBatchWriteState();
|
|
9332
9579
|
const latestById = /* @__PURE__ */ new Map();
|
|
9580
|
+
let discardedExistingRecords = false;
|
|
9333
9581
|
try {
|
|
9334
9582
|
for (const batch of this.loadSerializedFailedBatches()) {
|
|
9335
9583
|
for (const rawChunk of batch.chunks) {
|
|
@@ -9340,10 +9588,12 @@ var Indexer = class _Indexer {
|
|
|
9340
9588
|
continue;
|
|
9341
9589
|
}
|
|
9342
9590
|
if (!shouldProcess(filePath)) {
|
|
9591
|
+
discardedExistingRecords = true;
|
|
9343
9592
|
continue;
|
|
9344
9593
|
}
|
|
9345
9594
|
const chunkId = getPendingChunkId(rawChunk);
|
|
9346
9595
|
if (!chunkId) {
|
|
9596
|
+
discardedExistingRecords = true;
|
|
9347
9597
|
continue;
|
|
9348
9598
|
}
|
|
9349
9599
|
const existing = latestById.get(chunkId);
|
|
@@ -9351,12 +9601,18 @@ var Indexer = class _Indexer {
|
|
|
9351
9601
|
latestById.set(chunkId, {
|
|
9352
9602
|
attemptCount: batch.attemptCount,
|
|
9353
9603
|
error: batch.error,
|
|
9354
|
-
lastAttempt: batch.lastAttempt
|
|
9604
|
+
lastAttempt: batch.lastAttempt,
|
|
9605
|
+
chunks: [rawChunk]
|
|
9355
9606
|
});
|
|
9356
9607
|
}
|
|
9357
9608
|
}
|
|
9358
9609
|
}
|
|
9359
|
-
return {
|
|
9610
|
+
return {
|
|
9611
|
+
state,
|
|
9612
|
+
latestById,
|
|
9613
|
+
materializedRetryIds: /* @__PURE__ */ new Set(),
|
|
9614
|
+
discardedExistingRecords
|
|
9615
|
+
};
|
|
9360
9616
|
} catch (error) {
|
|
9361
9617
|
state.writer.cleanup();
|
|
9362
9618
|
throw error;
|
|
@@ -9392,10 +9648,34 @@ var Indexer = class _Indexer {
|
|
|
9392
9648
|
}
|
|
9393
9649
|
}
|
|
9394
9650
|
}
|
|
9651
|
+
restoreMissingChunkRows(database, chunks) {
|
|
9652
|
+
const missing = [];
|
|
9653
|
+
for (const chunk of chunks) {
|
|
9654
|
+
if (database.getChunk(chunk.id)) {
|
|
9655
|
+
continue;
|
|
9656
|
+
}
|
|
9657
|
+
missing.push({
|
|
9658
|
+
chunkId: chunk.id,
|
|
9659
|
+
contentHash: chunk.contentHash,
|
|
9660
|
+
filePath: chunk.metadata.filePath,
|
|
9661
|
+
startLine: chunk.metadata.startLine,
|
|
9662
|
+
endLine: chunk.metadata.endLine,
|
|
9663
|
+
nodeType: chunk.metadata.chunkType,
|
|
9664
|
+
name: chunk.metadata.name,
|
|
9665
|
+
language: chunk.metadata.language,
|
|
9666
|
+
blameSha: chunk.metadata.blameSha,
|
|
9667
|
+
blameAuthor: chunk.metadata.blameAuthor,
|
|
9668
|
+
blameAuthorEmail: chunk.metadata.blameAuthorEmail,
|
|
9669
|
+
blameCommittedAt: chunk.metadata.blameCommittedAt,
|
|
9670
|
+
blameSummary: chunk.metadata.blameSummary
|
|
9671
|
+
});
|
|
9672
|
+
}
|
|
9673
|
+
if (missing.length > 0) {
|
|
9674
|
+
database.upsertChunksBatch(missing);
|
|
9675
|
+
}
|
|
9676
|
+
}
|
|
9395
9677
|
getProviderRateLimits(provider) {
|
|
9396
9678
|
switch (provider) {
|
|
9397
|
-
case "github-copilot":
|
|
9398
|
-
return { concurrency: 1, intervalMs: 4e3, minRetryMs: 5e3, maxRetryMs: 6e4 };
|
|
9399
9679
|
case "openai":
|
|
9400
9680
|
return { concurrency: 3, intervalMs: 500, minRetryMs: 1e3, maxRetryMs: 3e4 };
|
|
9401
9681
|
case "google":
|
|
@@ -9464,16 +9744,17 @@ var Indexer = class _Indexer {
|
|
|
9464
9744
|
const embeddingPartsByChunk = /* @__PURE__ */ new Map();
|
|
9465
9745
|
const completedVectorsByChunkId = /* @__PURE__ */ new Map();
|
|
9466
9746
|
const completedChunkIds = /* @__PURE__ */ new Set();
|
|
9467
|
-
const
|
|
9468
|
-
|
|
9469
|
-
|
|
9470
|
-
|
|
9747
|
+
const batchOptions = getDynamicBatchOptions(options.configuredProviderInfo, this.config.embedding?.batch);
|
|
9748
|
+
if (options.forceSingleItemBatches && options.configuredProviderInfo.provider === "ollama") {
|
|
9749
|
+
batchOptions.maxBatchItems = 1;
|
|
9750
|
+
}
|
|
9751
|
+
const requestBatches = createPendingEmbeddingRequestBatches(chunksNeedingEmbedding, batchOptions);
|
|
9471
9752
|
let fatalError;
|
|
9472
9753
|
for (const requestBatch of requestBatches) {
|
|
9473
9754
|
await options.queue.onSizeLessThan(Math.max(1, options.providerRateLimits.concurrency));
|
|
9474
9755
|
const task = options.queue.add(async () => {
|
|
9475
9756
|
if (options.rateLimitState.backoffMs > 0) {
|
|
9476
|
-
await new Promise((
|
|
9757
|
+
await new Promise((resolve20) => setTimeout(resolve20, options.rateLimitState.backoffMs));
|
|
9477
9758
|
}
|
|
9478
9759
|
try {
|
|
9479
9760
|
const embeddingResult = await pRetry(
|
|
@@ -10030,7 +10311,7 @@ var Indexer = class _Indexer {
|
|
|
10030
10311
|
}
|
|
10031
10312
|
if (!this.configuredProviderInfo) {
|
|
10032
10313
|
throw new Error(
|
|
10033
|
-
"No embedding provider available. Configure
|
|
10314
|
+
"No embedding provider available. Configure OpenAI, Google, Ollama, or a custom OpenAI-compatible endpoint."
|
|
10034
10315
|
);
|
|
10035
10316
|
}
|
|
10036
10317
|
this.logger.info("Initializing indexer", {
|
|
@@ -10040,15 +10321,6 @@ var Indexer = class _Indexer {
|
|
|
10040
10321
|
rerankerEnabled: this.config.reranker?.enabled ?? false
|
|
10041
10322
|
});
|
|
10042
10323
|
this.provider = createEmbeddingProvider(this.configuredProviderInfo);
|
|
10043
|
-
if (this.config.reranker?.enabled) {
|
|
10044
|
-
this.reranker = createReranker(this.config.reranker);
|
|
10045
|
-
if (this.reranker.isAvailable()) {
|
|
10046
|
-
this.logger.info("Reranker initialized", {
|
|
10047
|
-
model: this.config.reranker.model,
|
|
10048
|
-
baseUrl: this.config.reranker.baseUrl
|
|
10049
|
-
});
|
|
10050
|
-
}
|
|
10051
|
-
}
|
|
10052
10324
|
const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
|
|
10053
10325
|
const storePath = path15.join(this.indexPath, "vectors");
|
|
10054
10326
|
const vectorMetadataPath = `${storePath}.meta.json`;
|
|
@@ -10070,7 +10342,20 @@ var Indexer = class _Indexer {
|
|
|
10070
10342
|
]);
|
|
10071
10343
|
}
|
|
10072
10344
|
if (recoveredOwners.length > 0 && this.config.scope === "project") {
|
|
10073
|
-
|
|
10345
|
+
const unknownLegacyForceIndex = recoveredOwners.find(
|
|
10346
|
+
(owner) => this.hasUnknownLegacyForceIndexClear(owner)
|
|
10347
|
+
);
|
|
10348
|
+
if (unknownLegacyForceIndex) {
|
|
10349
|
+
throw new Error(
|
|
10350
|
+
`Cannot automatically recover interrupted force-index ${unknownLegacyForceIndex.token}: the legacy clearing phase ownership is unknown. The recovery marker was retained for manual inspection.`
|
|
10351
|
+
);
|
|
10352
|
+
}
|
|
10353
|
+
const shouldReset = recoveredOwners.some(
|
|
10354
|
+
(owner) => owner.clearRecovery !== void 0 || owner.operation === "clear" && owner.recoveryProtocolVersion !== 1
|
|
10355
|
+
);
|
|
10356
|
+
if (shouldReset) {
|
|
10357
|
+
await this.resetLocalIndexArtifacts();
|
|
10358
|
+
}
|
|
10074
10359
|
}
|
|
10075
10360
|
this.store = new VectorStore(storePath, dimensions);
|
|
10076
10361
|
if ((0, import_fs10.existsSync)(storePath) || (0, import_fs10.existsSync)(vectorMetadataPath)) {
|
|
@@ -10706,7 +10991,17 @@ var Indexer = class _Indexer {
|
|
|
10706
10991
|
const needsCallGraphResolutionMigration = database.getMetadata(this.getCallGraphResolutionMetadataKey()) !== CALL_GRAPH_RESOLUTION_VERSION;
|
|
10707
10992
|
for (const file of files) {
|
|
10708
10993
|
const storedPath = this.toStoredFilePath(file.path);
|
|
10709
|
-
|
|
10994
|
+
let currentHash;
|
|
10995
|
+
try {
|
|
10996
|
+
currentHash = hashFile(file.path);
|
|
10997
|
+
} catch (error) {
|
|
10998
|
+
stats.skippedFiles.push({ path: this.toCanonicalFilePath(file.path), reason: "unreadable" });
|
|
10999
|
+
this.logger.warn("Skipped unreadable file during indexing", {
|
|
11000
|
+
path: file.path,
|
|
11001
|
+
error: getErrorMessage3(error)
|
|
11002
|
+
});
|
|
11003
|
+
continue;
|
|
11004
|
+
}
|
|
10710
11005
|
currentFileHashes.set(storedPath, currentHash);
|
|
10711
11006
|
const cachedHashMatches = this.fileHashCache.get(storedPath) === currentHash;
|
|
10712
11007
|
const needsCallGraphRefresh = cachedHashMatches && needsCallGraphResolutionMigration && database.getChunksByFile(storedPath).some(
|
|
@@ -10714,7 +11009,8 @@ var Indexer = class _Indexer {
|
|
|
10714
11009
|
);
|
|
10715
11010
|
const requiresSwiftParserUpgrade = reparseCachedSwiftFiles && path15.extname(storedPath).toLowerCase() === ".swift";
|
|
10716
11011
|
const requiresMetalParserUpgrade = reparseCachedMetalFiles && path15.extname(storedPath).toLowerCase() === ".metal";
|
|
10717
|
-
|
|
11012
|
+
const inMigrationScope = forceScopedReembed && scopedRoots !== null && this.isFileInCurrentScope(storedPath, scopedRoots);
|
|
11013
|
+
if (cachedHashMatches && !inMigrationScope && !needsCallGraphRefresh && !requiresSwiftParserUpgrade && !requiresMetalParserUpgrade && !refreshCachedSymbols) {
|
|
10718
11014
|
unchangedFilePaths.add(storedPath);
|
|
10719
11015
|
this.logger.recordCacheHit();
|
|
10720
11016
|
} else {
|
|
@@ -10840,6 +11136,9 @@ var Indexer = class _Indexer {
|
|
|
10840
11136
|
}
|
|
10841
11137
|
}
|
|
10842
11138
|
let processedChangedFiles = 0;
|
|
11139
|
+
let lastCheckpointChunks = 0;
|
|
11140
|
+
const committedFilePaths = new Set(unchangedFilePaths);
|
|
11141
|
+
const resolvedRetryChunkIds = /* @__PURE__ */ new Set();
|
|
10843
11142
|
for (const descriptorBatch of iterateOrderedFileBatches(
|
|
10844
11143
|
changedFileDescriptors,
|
|
10845
11144
|
(descriptor) => descriptor.sourceBytes,
|
|
@@ -10853,7 +11152,7 @@ var Indexer = class _Indexer {
|
|
|
10853
11152
|
const loadedByPath = new Map(loadedFiles.map((file) => [file.path, file]));
|
|
10854
11153
|
const descriptorByPath = new Map(descriptorBatch.map((descriptor) => [descriptor.storedPath, descriptor]));
|
|
10855
11154
|
const parseStartTime = import_perf_hooks.performance.now();
|
|
10856
|
-
const parsedFiles = parseFiles(loadedFiles);
|
|
11155
|
+
const parsedFiles = parseFiles(loadedFiles, this.config.indexing.linesPerChunk);
|
|
10857
11156
|
const parseMs = import_perf_hooks.performance.now() - parseStartTime;
|
|
10858
11157
|
this.logger.recordFilesParsed(parsedFiles.length);
|
|
10859
11158
|
this.logger.recordParseDuration(parseMs);
|
|
@@ -10876,7 +11175,7 @@ var Indexer = class _Indexer {
|
|
|
10876
11175
|
}
|
|
10877
11176
|
let chunksToProcess = parsed.chunks;
|
|
10878
11177
|
if (this.config.indexing.fallbackToTextOnMaxChunks && chunksToProcess.length > this.config.indexing.maxChunksPerFile) {
|
|
10879
|
-
chunksToProcess = parseFileAsText(parsed.path, loadedFile.content);
|
|
11178
|
+
chunksToProcess = parseFileAsText(parsed.path, loadedFile.content, this.config.indexing.linesPerChunk);
|
|
10880
11179
|
}
|
|
10881
11180
|
chunksToProcess = selectIndexableChunks(
|
|
10882
11181
|
chunksToProcess,
|
|
@@ -11010,6 +11309,10 @@ var Indexer = class _Indexer {
|
|
|
11010
11309
|
}
|
|
11011
11310
|
if (symbolBatch.length > 0) {
|
|
11012
11311
|
database.upsertSymbolsBatch(symbolBatch);
|
|
11312
|
+
database.addSymbolsToBranchBatch(
|
|
11313
|
+
this.getBranchCatalogKey(),
|
|
11314
|
+
symbolBatch.map((symbol) => symbol.id)
|
|
11315
|
+
);
|
|
11013
11316
|
}
|
|
11014
11317
|
if (edgeBatch.length > 0) {
|
|
11015
11318
|
database.upsertCallEdgesBatch(edgeBatch);
|
|
@@ -11045,6 +11348,12 @@ var Indexer = class _Indexer {
|
|
|
11045
11348
|
forceReembed: forceScopedReembed,
|
|
11046
11349
|
reuseCachedEmbeddings: true,
|
|
11047
11350
|
incrementRepeatedFailures: true,
|
|
11351
|
+
onSucceeded: (succeededChunks) => {
|
|
11352
|
+
database.addChunksToBranchBatch(
|
|
11353
|
+
this.getBranchCatalogKey(),
|
|
11354
|
+
succeededChunks.map((chunk) => chunk.id)
|
|
11355
|
+
);
|
|
11356
|
+
},
|
|
11048
11357
|
onProgress: (batchProgress) => onProgress?.({
|
|
11049
11358
|
phase: "embedding",
|
|
11050
11359
|
filesProcessed: unchangedFilePaths.size + processedChangedFiles,
|
|
@@ -11063,6 +11372,27 @@ var Indexer = class _Indexer {
|
|
|
11063
11372
|
}
|
|
11064
11373
|
}
|
|
11065
11374
|
}
|
|
11375
|
+
for (const descriptor of descriptorBatch) {
|
|
11376
|
+
const existingFileChunks = existingChunksByFile.get(descriptor.storedPath);
|
|
11377
|
+
if (!existingFileChunks || existingFileChunks.size === 0) {
|
|
11378
|
+
committedFilePaths.add(descriptor.storedPath);
|
|
11379
|
+
}
|
|
11380
|
+
}
|
|
11381
|
+
const checkpointInterval = this.getCheckpointIntervalChunks(stats.totalChunks);
|
|
11382
|
+
if (stats.totalChunks - lastCheckpointChunks >= checkpointInterval) {
|
|
11383
|
+
lastCheckpointChunks = stats.totalChunks;
|
|
11384
|
+
this.checkpointIndexRun(
|
|
11385
|
+
database,
|
|
11386
|
+
store,
|
|
11387
|
+
invertedIndex,
|
|
11388
|
+
failedProcessing,
|
|
11389
|
+
resolvedRetryChunkIds,
|
|
11390
|
+
currentFileHashes,
|
|
11391
|
+
committedFilePaths,
|
|
11392
|
+
scopedRoots,
|
|
11393
|
+
configuredProviderInfo
|
|
11394
|
+
);
|
|
11395
|
+
}
|
|
11066
11396
|
}
|
|
11067
11397
|
const retryableFailedChunks = this.iterateLatestFailedChunks(
|
|
11068
11398
|
failedProcessing.latestById,
|
|
@@ -11083,6 +11413,7 @@ var Indexer = class _Indexer {
|
|
|
11083
11413
|
retryableChunksWithExistingData.add(chunk.id);
|
|
11084
11414
|
}
|
|
11085
11415
|
}
|
|
11416
|
+
this.restoreMissingChunkRows(database, pendingChunks);
|
|
11086
11417
|
stats.totalChunks += pendingChunks.length;
|
|
11087
11418
|
onProgress?.({
|
|
11088
11419
|
phase: "embedding",
|
|
@@ -11105,6 +11436,17 @@ var Indexer = class _Indexer {
|
|
|
11105
11436
|
forceReembed: forceScopedReembed,
|
|
11106
11437
|
reuseCachedEmbeddings: true,
|
|
11107
11438
|
incrementRepeatedFailures: true,
|
|
11439
|
+
forceSingleItemBatches: true,
|
|
11440
|
+
onSucceeded: (succeededChunks) => {
|
|
11441
|
+
database.addChunksToBranchBatch(
|
|
11442
|
+
this.getBranchCatalogKey(),
|
|
11443
|
+
succeededChunks.map((chunk) => chunk.id)
|
|
11444
|
+
);
|
|
11445
|
+
for (const chunk of succeededChunks) {
|
|
11446
|
+
failedProcessing.latestById.delete(chunk.id);
|
|
11447
|
+
resolvedRetryChunkIds.add(chunk.id);
|
|
11448
|
+
}
|
|
11449
|
+
},
|
|
11108
11450
|
onProgress: (batchProgress) => onProgress?.({
|
|
11109
11451
|
phase: "embedding",
|
|
11110
11452
|
filesProcessed: files.length,
|
|
@@ -11122,6 +11464,20 @@ var Indexer = class _Indexer {
|
|
|
11122
11464
|
failedForcedChunkIds.add(chunkId);
|
|
11123
11465
|
}
|
|
11124
11466
|
}
|
|
11467
|
+
if (stats.totalChunks - lastCheckpointChunks >= this.getCheckpointIntervalChunks(stats.totalChunks)) {
|
|
11468
|
+
lastCheckpointChunks = stats.totalChunks;
|
|
11469
|
+
this.checkpointIndexRun(
|
|
11470
|
+
database,
|
|
11471
|
+
store,
|
|
11472
|
+
invertedIndex,
|
|
11473
|
+
failedProcessing,
|
|
11474
|
+
resolvedRetryChunkIds,
|
|
11475
|
+
currentFileHashes,
|
|
11476
|
+
committedFilePaths,
|
|
11477
|
+
scopedRoots,
|
|
11478
|
+
configuredProviderInfo
|
|
11479
|
+
);
|
|
11480
|
+
}
|
|
11125
11481
|
}
|
|
11126
11482
|
const removedChunkIds = [];
|
|
11127
11483
|
for (const [chunkId] of existingChunks) {
|
|
@@ -11158,13 +11514,6 @@ var Indexer = class _Indexer {
|
|
|
11158
11514
|
if (removedStoredChunks) {
|
|
11159
11515
|
this.saveInvertedIndex(invertedIndex);
|
|
11160
11516
|
}
|
|
11161
|
-
if (scopedRoots) {
|
|
11162
|
-
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
11163
|
-
} else {
|
|
11164
|
-
this.fileHashCache = currentFileHashes;
|
|
11165
|
-
this.saveFileHashCache();
|
|
11166
|
-
}
|
|
11167
|
-
this.finalizeFailedBatchWriteState(failedProcessing.state);
|
|
11168
11517
|
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
11169
11518
|
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
11170
11519
|
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
@@ -11173,6 +11522,13 @@ var Indexer = class _Indexer {
|
|
|
11173
11522
|
this.indexCompatibility = { compatible: true };
|
|
11174
11523
|
database.commitWriteTransaction();
|
|
11175
11524
|
writeTransactionActive = false;
|
|
11525
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state, resolvedRetryChunkIds);
|
|
11526
|
+
if (scopedRoots) {
|
|
11527
|
+
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
11528
|
+
} else {
|
|
11529
|
+
this.fileHashCache = currentFileHashes;
|
|
11530
|
+
this.saveFileHashCache();
|
|
11531
|
+
}
|
|
11176
11532
|
stats.durationMs = Date.now() - startTime;
|
|
11177
11533
|
onProgress?.({
|
|
11178
11534
|
phase: "complete",
|
|
@@ -11196,13 +11552,6 @@ var Indexer = class _Indexer {
|
|
|
11196
11552
|
);
|
|
11197
11553
|
store.save();
|
|
11198
11554
|
this.saveInvertedIndex(invertedIndex);
|
|
11199
|
-
if (scopedRoots) {
|
|
11200
|
-
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
11201
|
-
} else {
|
|
11202
|
-
this.fileHashCache = currentFileHashes;
|
|
11203
|
-
this.saveFileHashCache();
|
|
11204
|
-
}
|
|
11205
|
-
this.finalizeFailedBatchWriteState(failedProcessing.state);
|
|
11206
11555
|
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
11207
11556
|
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
11208
11557
|
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
@@ -11211,6 +11560,13 @@ var Indexer = class _Indexer {
|
|
|
11211
11560
|
this.indexCompatibility = { compatible: true };
|
|
11212
11561
|
database.commitWriteTransaction();
|
|
11213
11562
|
writeTransactionActive = false;
|
|
11563
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state, resolvedRetryChunkIds);
|
|
11564
|
+
if (scopedRoots) {
|
|
11565
|
+
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
11566
|
+
} else {
|
|
11567
|
+
this.fileHashCache = currentFileHashes;
|
|
11568
|
+
this.saveFileHashCache();
|
|
11569
|
+
}
|
|
11214
11570
|
stats.durationMs = Date.now() - startTime;
|
|
11215
11571
|
onProgress?.({
|
|
11216
11572
|
phase: "complete",
|
|
@@ -11245,15 +11601,15 @@ var Indexer = class _Indexer {
|
|
|
11245
11601
|
);
|
|
11246
11602
|
store.save();
|
|
11247
11603
|
this.saveInvertedIndex(invertedIndex);
|
|
11604
|
+
database.commitWriteTransaction();
|
|
11605
|
+
writeTransactionActive = false;
|
|
11606
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state, resolvedRetryChunkIds);
|
|
11248
11607
|
if (scopedRoots) {
|
|
11249
11608
|
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
11250
11609
|
} else {
|
|
11251
11610
|
this.fileHashCache = currentFileHashes;
|
|
11252
11611
|
this.saveFileHashCache();
|
|
11253
11612
|
}
|
|
11254
|
-
this.finalizeFailedBatchWriteState(failedProcessing.state);
|
|
11255
|
-
database.commitWriteTransaction();
|
|
11256
|
-
writeTransactionActive = false;
|
|
11257
11613
|
if (this.config.indexing.autoGc && stats.removedChunks > 0) {
|
|
11258
11614
|
const gcReset = await this.maybeRunOrphanGc();
|
|
11259
11615
|
if (gcReset) {
|
|
@@ -11277,6 +11633,9 @@ var Indexer = class _Indexer {
|
|
|
11277
11633
|
if (forceScopedReembed && failedForcedChunkIds.size === 0) {
|
|
11278
11634
|
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
11279
11635
|
}
|
|
11636
|
+
if (forceScopedReembed) {
|
|
11637
|
+
database.setMetadata(this.getProjectMigrationFinalizedMetadataKey(), "true");
|
|
11638
|
+
}
|
|
11280
11639
|
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
11281
11640
|
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
11282
11641
|
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
@@ -11387,26 +11746,41 @@ var Indexer = class _Indexer {
|
|
|
11387
11746
|
shouldPrefilterByBranch: branchChunkIds !== null && (this.config.scope === "global" || hasInitializedBranchCatalog)
|
|
11388
11747
|
};
|
|
11389
11748
|
}
|
|
11390
|
-
|
|
11749
|
+
searchCandidatesWithAllowedIds(initialLimit, totalCount, allowedChunkIds, shouldPrefilter, search, getChunkId) {
|
|
11391
11750
|
const normalizedLimit = Math.max(0, Math.floor(initialLimit));
|
|
11392
11751
|
if (normalizedLimit === 0) return [];
|
|
11393
|
-
if (!
|
|
11752
|
+
if (!shouldPrefilter || !allowedChunkIds) {
|
|
11394
11753
|
return search(normalizedLimit);
|
|
11395
11754
|
}
|
|
11396
|
-
const targetCount = Math.min(normalizedLimit,
|
|
11755
|
+
const targetCount = Math.min(normalizedLimit, allowedChunkIds.size);
|
|
11397
11756
|
if (targetCount === 0 || totalCount === 0) return [];
|
|
11398
11757
|
let requestedLimit = Math.min(normalizedLimit, totalCount);
|
|
11399
11758
|
while (true) {
|
|
11400
11759
|
const results = search(requestedLimit);
|
|
11401
|
-
const
|
|
11402
|
-
if (
|
|
11403
|
-
return
|
|
11760
|
+
const allowedResults = results.filter((candidate) => allowedChunkIds.has(getChunkId(candidate)));
|
|
11761
|
+
if (allowedResults.length >= targetCount || results.length < requestedLimit || requestedLimit >= totalCount) {
|
|
11762
|
+
return allowedResults;
|
|
11404
11763
|
}
|
|
11405
11764
|
const nextLimit = Math.min(totalCount, Math.max(requestedLimit + 1, requestedLimit * 2));
|
|
11406
|
-
if (nextLimit === requestedLimit) return
|
|
11765
|
+
if (nextLimit === requestedLimit) return allowedResults;
|
|
11407
11766
|
requestedLimit = nextLimit;
|
|
11408
11767
|
}
|
|
11409
11768
|
}
|
|
11769
|
+
getTemporalChunkIds(database, options) {
|
|
11770
|
+
if (!options?.blameSince && !options?.blameUntil) return null;
|
|
11771
|
+
const since = options.blameSince ? parseBlameTimestamp(options.blameSince, false) : void 0;
|
|
11772
|
+
const until = options.blameUntil ? parseBlameTimestamp(options.blameUntil, true) : void 0;
|
|
11773
|
+
if (since === null || until === null) {
|
|
11774
|
+
return /* @__PURE__ */ new Set();
|
|
11775
|
+
}
|
|
11776
|
+
return new Set(database.getChunkIdsByBlameDate(since, until));
|
|
11777
|
+
}
|
|
11778
|
+
intersectChunkIdSets(first, second) {
|
|
11779
|
+
if (first === null) return second;
|
|
11780
|
+
if (second === null) return first;
|
|
11781
|
+
const [smaller, larger] = first.size <= second.size ? [first, second] : [second, first];
|
|
11782
|
+
return new Set(Array.from(smaller).filter((chunkId) => larger.has(chunkId)));
|
|
11783
|
+
}
|
|
11410
11784
|
buildCandidateSnapshot(candidate) {
|
|
11411
11785
|
return {
|
|
11412
11786
|
id: candidate.id,
|
|
@@ -11421,13 +11795,16 @@ var Indexer = class _Indexer {
|
|
|
11421
11795
|
buildCandidateSnapshotList(candidates) {
|
|
11422
11796
|
return candidates.map((candidate) => this.buildCandidateSnapshot(candidate));
|
|
11423
11797
|
}
|
|
11424
|
-
searchSemanticCandidates(store, embedding, initialLimit, branchChunkIds, shouldPrefilterByBranch) {
|
|
11425
|
-
|
|
11426
|
-
|
|
11427
|
-
|
|
11798
|
+
searchSemanticCandidates(store, embedding, initialLimit, branchChunkIds, shouldPrefilterByBranch, temporalChunkIds) {
|
|
11799
|
+
const availableCount = temporalChunkIds?.size ?? store.count();
|
|
11800
|
+
if (availableCount === 0) return [];
|
|
11801
|
+
const allowedIds = temporalChunkIds === null ? void 0 : Array.from(temporalChunkIds);
|
|
11802
|
+
return this.searchCandidatesWithAllowedIds(
|
|
11803
|
+
Math.min(initialLimit, availableCount),
|
|
11804
|
+
availableCount,
|
|
11428
11805
|
branchChunkIds,
|
|
11429
11806
|
shouldPrefilterByBranch,
|
|
11430
|
-
(requestedLimit) => store.search(embedding, requestedLimit),
|
|
11807
|
+
(requestedLimit) => store.search(embedding, requestedLimit, allowedIds),
|
|
11431
11808
|
(candidate) => candidate.id
|
|
11432
11809
|
);
|
|
11433
11810
|
}
|
|
@@ -11452,7 +11829,9 @@ var Indexer = class _Indexer {
|
|
|
11452
11829
|
const rerankTopN = this.config.search.rerankTopN;
|
|
11453
11830
|
const filterByBranch = options?.filterByBranch ?? true;
|
|
11454
11831
|
const sourceIntent = options?.definitionIntent === true || classifyQueryIntentRaw(query) === "source";
|
|
11832
|
+
const prioritizeSourcePaths = sourceIntent || options?.prioritizeSourcePaths === true;
|
|
11455
11833
|
const identifierHints = extractIdentifierHints(query);
|
|
11834
|
+
const candidateLimit = maxResults * (prioritizeSourcePaths ? 12 : 4);
|
|
11456
11835
|
this.logger.search("debug", "Starting search", {
|
|
11457
11836
|
query,
|
|
11458
11837
|
maxResults,
|
|
@@ -11483,25 +11862,28 @@ var Indexer = class _Indexer {
|
|
|
11483
11862
|
branchChunkIds = new Set(branchCatalogKeys.flatMap((branchKey) => database.getBranchChunkIds(branchKey)));
|
|
11484
11863
|
branchSymbolIds = new Set(branchCatalogKeys.flatMap((branchKey) => database.getBranchSymbolIds(branchKey)));
|
|
11485
11864
|
}
|
|
11865
|
+
const temporalChunkIds = this.getTemporalChunkIds(database, options);
|
|
11486
11866
|
const { hasInitializedBranchCatalog, shouldPrefilterByBranch } = this.getBranchPrefilterState(database, branchChunkIds);
|
|
11487
11867
|
const prefilterMs = import_perf_hooks.performance.now() - prefilterStartTime;
|
|
11488
11868
|
const vectorStartTime = import_perf_hooks.performance.now();
|
|
11489
11869
|
const semanticCandidates = embedding ? this.searchSemanticCandidates(
|
|
11490
11870
|
store,
|
|
11491
11871
|
embedding,
|
|
11492
|
-
|
|
11872
|
+
candidateLimit,
|
|
11493
11873
|
branchChunkIds,
|
|
11494
|
-
shouldPrefilterByBranch
|
|
11874
|
+
shouldPrefilterByBranch,
|
|
11875
|
+
temporalChunkIds
|
|
11495
11876
|
) : [];
|
|
11496
11877
|
const vectorMs = import_perf_hooks.performance.now() - vectorStartTime;
|
|
11497
11878
|
const keywordStartTime = import_perf_hooks.performance.now();
|
|
11498
11879
|
const keywordCandidates = await this.keywordSearch(
|
|
11499
11880
|
query,
|
|
11500
|
-
|
|
11881
|
+
candidateLimit,
|
|
11501
11882
|
store,
|
|
11502
11883
|
invertedIndex,
|
|
11503
11884
|
branchChunkIds,
|
|
11504
|
-
shouldPrefilterByBranch
|
|
11885
|
+
shouldPrefilterByBranch,
|
|
11886
|
+
temporalChunkIds
|
|
11505
11887
|
);
|
|
11506
11888
|
const keywordMs = import_perf_hooks.performance.now() - keywordStartTime;
|
|
11507
11889
|
const scopedSemanticCandidates = semanticCandidates.filter(
|
|
@@ -11523,7 +11905,7 @@ var Indexer = class _Indexer {
|
|
|
11523
11905
|
rerankTopN,
|
|
11524
11906
|
limit: maxResults,
|
|
11525
11907
|
hybridWeight: rankingHybridWeight,
|
|
11526
|
-
prioritizeSourcePaths
|
|
11908
|
+
prioritizeSourcePaths
|
|
11527
11909
|
});
|
|
11528
11910
|
const rerankedCombined = await this.rerankCandidatesWithApi(query, combined, {
|
|
11529
11911
|
definitionIntent: options?.definitionIntent === true,
|
|
@@ -11559,10 +11941,11 @@ var Indexer = class _Indexer {
|
|
|
11559
11941
|
branchSymbolIds,
|
|
11560
11942
|
maxResults,
|
|
11561
11943
|
union,
|
|
11562
|
-
sourceIntent
|
|
11944
|
+
sourceIntent,
|
|
11945
|
+
options?.definitionIntent === true && ((options.directory?.trim().length ?? 0) > 0 || (options.fileType?.trim().length ?? 0) > 0)
|
|
11563
11946
|
);
|
|
11564
11947
|
const prePrimaryLane = mergeTieredResults(deterministicIdentifierLane, identifierLane, maxResults * 4);
|
|
11565
|
-
const primaryLane = mergeTieredResults(prePrimaryLane, symbolLane, maxResults * 4);
|
|
11948
|
+
const primaryLane = options?.definitionIntent === true ? mergeTieredResults(symbolLane, prePrimaryLane, maxResults * 4) : mergeTieredResults(prePrimaryLane, symbolLane, maxResults * 4);
|
|
11566
11949
|
const tiered = mergeTieredResults(primaryLane, rescued, maxResults * 4);
|
|
11567
11950
|
const hasCodeHints = extractCodeTermHints(query).length > 0 || identifierHints.length > 0;
|
|
11568
11951
|
const baseFiltered = tiered.filter(
|
|
@@ -11657,14 +12040,18 @@ var Indexer = class _Indexer {
|
|
|
11657
12040
|
})
|
|
11658
12041
|
);
|
|
11659
12042
|
}
|
|
11660
|
-
async keywordSearch(query, limit, store, invertedIndex, branchChunkIds = null, shouldPrefilterByBranch = false) {
|
|
12043
|
+
async keywordSearch(query, limit, store, invertedIndex, branchChunkIds = null, shouldPrefilterByBranch = false, temporalChunkIds = null) {
|
|
11661
12044
|
const normalizedLimit = Math.max(0, Math.floor(limit));
|
|
11662
12045
|
if (normalizedLimit === 0) return [];
|
|
11663
|
-
const
|
|
12046
|
+
const allowedChunkIds = this.intersectChunkIdSets(
|
|
12047
|
+
shouldPrefilterByBranch ? branchChunkIds : null,
|
|
12048
|
+
temporalChunkIds
|
|
12049
|
+
);
|
|
12050
|
+
const scoreEntries = this.searchCandidatesWithAllowedIds(
|
|
11664
12051
|
normalizedLimit,
|
|
11665
12052
|
invertedIndex.getDocumentCount(),
|
|
11666
|
-
|
|
11667
|
-
|
|
12053
|
+
allowedChunkIds,
|
|
12054
|
+
allowedChunkIds !== null,
|
|
11668
12055
|
(requestedLimit) => Array.from(invertedIndex.search(query, requestedLimit)),
|
|
11669
12056
|
([chunkId]) => chunkId
|
|
11670
12057
|
);
|
|
@@ -11749,7 +12136,17 @@ var Indexer = class _Indexer {
|
|
|
11749
12136
|
);
|
|
11750
12137
|
const currentFileHashes = /* @__PURE__ */ new Map();
|
|
11751
12138
|
for (const file of files) {
|
|
11752
|
-
|
|
12139
|
+
let hash;
|
|
12140
|
+
try {
|
|
12141
|
+
hash = hashFile(file.path);
|
|
12142
|
+
} catch (error) {
|
|
12143
|
+
this.logger.warn("Skipped unreadable file during freshness check", {
|
|
12144
|
+
path: file.path,
|
|
12145
|
+
error: getErrorMessage3(error)
|
|
12146
|
+
});
|
|
12147
|
+
return { readable: false, current: false, reason: "unreadable" };
|
|
12148
|
+
}
|
|
12149
|
+
currentFileHashes.set(this.toStoredFilePath(file.path), hash);
|
|
11753
12150
|
}
|
|
11754
12151
|
const scopedRoots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
11755
12152
|
const cachedFileHashes = scopedRoots ? new Map(Array.from(this.fileHashCache).filter(([filePath]) => this.isFileInCurrentScope(filePath, scopedRoots))) : this.fileHashCache;
|
|
@@ -11775,69 +12172,87 @@ var Indexer = class _Indexer {
|
|
|
11775
12172
|
async forceIndex(onProgress) {
|
|
11776
12173
|
return this.withIndexMutationLease("force-index", async (recoveredOwners) => {
|
|
11777
12174
|
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
11778
|
-
|
|
12175
|
+
const recovery = this.beginClearRecoveryState();
|
|
12176
|
+
await this.clearIndexUnlocked(recovery.compatibilityDecision);
|
|
12177
|
+
this.finishClearRecoveryState();
|
|
11779
12178
|
return this.indexUnlocked(onProgress, [], true);
|
|
11780
12179
|
});
|
|
11781
12180
|
}
|
|
11782
12181
|
async clearIndex() {
|
|
11783
12182
|
await this.withIndexMutationLease("clear", async (recoveredOwners) => {
|
|
11784
12183
|
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
11785
|
-
|
|
12184
|
+
const recovery = this.beginClearRecoveryState();
|
|
12185
|
+
await this.clearIndexUnlocked(recovery.compatibilityDecision);
|
|
11786
12186
|
});
|
|
11787
12187
|
}
|
|
11788
|
-
|
|
12188
|
+
clearGlobalIndexDataUnlocked(projectRoot = this.projectRoot) {
|
|
11789
12189
|
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
11790
|
-
|
|
11791
|
-
|
|
11792
|
-
|
|
11793
|
-
|
|
11794
|
-
|
|
11795
|
-
|
|
11796
|
-
|
|
11797
|
-
|
|
11798
|
-
|
|
11799
|
-
|
|
11800
|
-
|
|
11801
|
-
|
|
11802
|
-
|
|
11803
|
-
|
|
11804
|
-
|
|
12190
|
+
const clearedBranchKeys = database.getAllBranches();
|
|
12191
|
+
store.clear();
|
|
12192
|
+
store.save();
|
|
12193
|
+
invertedIndex.clear();
|
|
12194
|
+
this.saveInvertedIndex(invertedIndex);
|
|
12195
|
+
this.fileHashCache.clear();
|
|
12196
|
+
this.saveFileHashCache();
|
|
12197
|
+
database.clearAllIndexedData();
|
|
12198
|
+
this.deleteBranchCommitMetadata(database, clearedBranchKeys);
|
|
12199
|
+
this.clearFailedBatchState();
|
|
12200
|
+
database.deleteMetadata("index.version");
|
|
12201
|
+
database.deleteMetadata("index.pathStorageVersion");
|
|
12202
|
+
database.deleteMetadata("index.embeddingProvider");
|
|
12203
|
+
database.deleteMetadata("index.embeddingModel");
|
|
12204
|
+
database.deleteMetadata("index.embeddingDimensions");
|
|
12205
|
+
database.deleteMetadata("index.embeddingStrategyVersion");
|
|
12206
|
+
const projectIdentityHash = this.getProjectIdentityHash(projectRoot);
|
|
12207
|
+
database.deleteMetadata(this.getProjectEmbeddingStrategyMetadataKey(projectIdentityHash));
|
|
12208
|
+
database.deleteMetadata(this.getProjectForceReembedMetadataKey(projectIdentityHash));
|
|
12209
|
+
database.deleteMetadata(this.getLegacyMigrationMetadataKey(projectIdentityHash));
|
|
12210
|
+
database.deleteMetadata("index.createdAt");
|
|
12211
|
+
database.deleteMetadata("index.updatedAt");
|
|
12212
|
+
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
12213
|
+
}
|
|
12214
|
+
clearGlobalIndexUnlocked(projectRoot = this.projectRoot, roots = this.getScopedRoots(), recoveryDecision) {
|
|
12215
|
+
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
12216
|
+
store.load();
|
|
12217
|
+
invertedIndex.load();
|
|
12218
|
+
this.loadFileHashCache();
|
|
12219
|
+
const compatibility = this.checkCompatibility();
|
|
12220
|
+
const compatibilityDecision = recoveryDecision ?? (compatibility.compatible ? "compatible" : compatibility.code === "EMBEDDING_STRATEGY_MISMATCH" /* EMBEDDING_STRATEGY_MISMATCH */ ? "embedding-strategy-mismatch" : "incompatible");
|
|
12221
|
+
const allMetadata = store.getAllMetadata();
|
|
12222
|
+
const hasForeignData = allMetadata.some(({ metadata }) => !this.isFileInCurrentScope(metadata.filePath, roots)) || this.hasForeignScopedBranchData(projectRoot, roots) || this.hasForeignScopedFileHashData(roots) || this.hasForeignScopedFailedBatches(roots);
|
|
12223
|
+
if (compatibilityDecision !== "compatible" && hasForeignData) {
|
|
12224
|
+
if (compatibilityDecision === "embedding-strategy-mismatch") {
|
|
12225
|
+
this.clearSharedIndexProjectData(store, invertedIndex, database, roots, projectRoot);
|
|
12226
|
+
this.clearScopedFileHashCache(roots);
|
|
12227
|
+
this.clearScopedFailedBatches(roots);
|
|
12228
|
+
const projectIdentityHash = this.getProjectIdentityHash(projectRoot);
|
|
12229
|
+
database.setMetadata(this.getProjectForceReembedMetadataKey(projectIdentityHash), "true");
|
|
12230
|
+
database.deleteMetadata(this.getProjectEmbeddingStrategyMetadataKey(projectIdentityHash));
|
|
12231
|
+
database.deleteMetadata(this.getProjectMigrationFinalizedMetadataKey(projectIdentityHash));
|
|
12232
|
+
if (projectRoot === this.projectRoot) {
|
|
11805
12233
|
this.indexCompatibility = { compatible: true };
|
|
11806
|
-
return;
|
|
11807
12234
|
}
|
|
11808
|
-
throw new Error(
|
|
11809
|
-
`Global index compatibility reset is unsafe because the shared index contains files from other projects. The current global index cannot be force-rebuilt for ${this.projectRoot} without deleting other repositories' indexed data. Use scope="project" for isolated rebuilds, or manually delete the shared global index if you intend to rebuild all projects.`
|
|
11810
|
-
);
|
|
11811
|
-
}
|
|
11812
|
-
if (!hasForeignData) {
|
|
11813
|
-
const clearedBranchKeys2 = database.getAllBranches();
|
|
11814
|
-
store.clear();
|
|
11815
|
-
store.save();
|
|
11816
|
-
invertedIndex.clear();
|
|
11817
|
-
this.saveInvertedIndex(invertedIndex);
|
|
11818
|
-
this.fileHashCache.clear();
|
|
11819
|
-
this.saveFileHashCache();
|
|
11820
|
-
database.clearAllIndexedData();
|
|
11821
|
-
this.deleteBranchCommitMetadata(database, clearedBranchKeys2);
|
|
11822
|
-
this.clearFailedBatchState();
|
|
11823
|
-
database.deleteMetadata("index.version");
|
|
11824
|
-
database.deleteMetadata("index.pathStorageVersion");
|
|
11825
|
-
database.deleteMetadata("index.embeddingProvider");
|
|
11826
|
-
database.deleteMetadata("index.embeddingModel");
|
|
11827
|
-
database.deleteMetadata("index.embeddingDimensions");
|
|
11828
|
-
database.deleteMetadata("index.embeddingStrategyVersion");
|
|
11829
|
-
database.deleteMetadata(this.getProjectEmbeddingStrategyMetadataKey());
|
|
11830
|
-
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
11831
|
-
database.deleteMetadata(this.getLegacyMigrationMetadataKey());
|
|
11832
|
-
database.deleteMetadata("index.createdAt");
|
|
11833
|
-
database.deleteMetadata("index.updatedAt");
|
|
11834
|
-
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
11835
12235
|
return;
|
|
11836
12236
|
}
|
|
11837
|
-
|
|
11838
|
-
|
|
11839
|
-
|
|
12237
|
+
throw new Error(
|
|
12238
|
+
`Global index compatibility reset is unsafe because the shared index contains files from other projects. The current global index cannot be force-rebuilt for ${projectRoot} without deleting other repositories' indexed data. Use scope="project" for isolated rebuilds, or manually delete the shared global index if you intend to rebuild all projects.`
|
|
12239
|
+
);
|
|
12240
|
+
}
|
|
12241
|
+
if (!hasForeignData) {
|
|
12242
|
+
this.clearGlobalIndexDataUnlocked(projectRoot);
|
|
12243
|
+
return;
|
|
12244
|
+
}
|
|
12245
|
+
this.clearSharedIndexProjectData(store, invertedIndex, database, roots, projectRoot);
|
|
12246
|
+
this.clearScopedFileHashCache(roots);
|
|
12247
|
+
this.clearScopedFailedBatches(roots);
|
|
12248
|
+
if (projectRoot === this.projectRoot) {
|
|
11840
12249
|
this.indexCompatibility = compatibility;
|
|
12250
|
+
}
|
|
12251
|
+
}
|
|
12252
|
+
async clearIndexUnlocked(recoveryDecision) {
|
|
12253
|
+
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
12254
|
+
if (this.config.scope === "global") {
|
|
12255
|
+
this.clearGlobalIndexUnlocked(this.projectRoot, this.getScopedRoots(), recoveryDecision);
|
|
11841
12256
|
return;
|
|
11842
12257
|
}
|
|
11843
12258
|
if (!this.isProjectOwnedIndexPath()) {
|
|
@@ -12003,6 +12418,7 @@ var Indexer = class _Indexer {
|
|
|
12003
12418
|
)) {
|
|
12004
12419
|
const chunks = retryBatch.map(({ chunk }) => chunk);
|
|
12005
12420
|
const attemptCounts = new Map(retryBatch.map(({ chunk, attemptCount }) => [chunk.id, attemptCount]));
|
|
12421
|
+
this.restoreMissingChunkRows(database, chunks);
|
|
12006
12422
|
const batchResult = await this.processPendingChunkBatch(chunks, {
|
|
12007
12423
|
store,
|
|
12008
12424
|
provider,
|
|
@@ -12017,6 +12433,7 @@ var Indexer = class _Indexer {
|
|
|
12017
12433
|
forceReembed: false,
|
|
12018
12434
|
reuseCachedEmbeddings: false,
|
|
12019
12435
|
incrementRepeatedFailures: false,
|
|
12436
|
+
forceSingleItemBatches: true,
|
|
12020
12437
|
onSucceeded: (succeededChunks) => {
|
|
12021
12438
|
database.addChunksToBranchBatch(
|
|
12022
12439
|
this.getBranchCatalogKey(),
|
|
@@ -12038,9 +12455,12 @@ var Indexer = class _Indexer {
|
|
|
12038
12455
|
this.saveInvertedIndex(invertedIndex);
|
|
12039
12456
|
}
|
|
12040
12457
|
if (roots && succeeded > 0 && remaining === 0 && this.hasProjectForceReembedPending()) {
|
|
12041
|
-
database.
|
|
12042
|
-
|
|
12043
|
-
|
|
12458
|
+
const migrationFinalized = database.getMetadata(this.getProjectMigrationFinalizedMetadataKey()) === "true";
|
|
12459
|
+
if (migrationFinalized) {
|
|
12460
|
+
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
12461
|
+
this.saveIndexMetadata(configuredProviderInfo);
|
|
12462
|
+
this.indexCompatibility = { compatible: true };
|
|
12463
|
+
}
|
|
12044
12464
|
}
|
|
12045
12465
|
return { succeeded, failed, remaining };
|
|
12046
12466
|
}
|
|
@@ -12062,7 +12482,8 @@ var Indexer = class _Indexer {
|
|
|
12062
12482
|
latestById.set(chunkId, {
|
|
12063
12483
|
attemptCount: batch.attemptCount,
|
|
12064
12484
|
error: batch.error,
|
|
12065
|
-
lastAttempt: batch.lastAttempt
|
|
12485
|
+
lastAttempt: batch.lastAttempt,
|
|
12486
|
+
chunks: [rawChunk]
|
|
12066
12487
|
});
|
|
12067
12488
|
}
|
|
12068
12489
|
}
|
|
@@ -12129,6 +12550,7 @@ var Indexer = class _Indexer {
|
|
|
12129
12550
|
this.getBranchCatalogKeys().flatMap((branchKey) => database.getBranchChunkIds(branchKey))
|
|
12130
12551
|
);
|
|
12131
12552
|
}
|
|
12553
|
+
const temporalChunkIds = this.getTemporalChunkIds(database, options);
|
|
12132
12554
|
const { hasInitializedBranchCatalog, shouldPrefilterByBranch } = this.getBranchPrefilterState(database, branchChunkIds);
|
|
12133
12555
|
const prefilterMs = import_perf_hooks.performance.now() - prefilterStartTime;
|
|
12134
12556
|
const vectorStartTime = import_perf_hooks.performance.now();
|
|
@@ -12137,7 +12559,8 @@ var Indexer = class _Indexer {
|
|
|
12137
12559
|
embedding,
|
|
12138
12560
|
limit * 2,
|
|
12139
12561
|
branchChunkIds,
|
|
12140
|
-
shouldPrefilterByBranch
|
|
12562
|
+
shouldPrefilterByBranch,
|
|
12563
|
+
temporalChunkIds
|
|
12141
12564
|
);
|
|
12142
12565
|
const vectorMs = import_perf_hooks.performance.now() - vectorStartTime;
|
|
12143
12566
|
if (this.config.scope !== "global" && branchChunkIds && !hasInitializedBranchCatalog) {
|
|
@@ -12255,9 +12678,9 @@ var Indexer = class _Indexer {
|
|
|
12255
12678
|
this.requireReadableComponents(readIssues, "database");
|
|
12256
12679
|
let shortest = [];
|
|
12257
12680
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
12258
|
-
const
|
|
12259
|
-
if (
|
|
12260
|
-
shortest =
|
|
12681
|
+
const path33 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
|
|
12682
|
+
if (path33.length > 0 && (shortest.length === 0 || path33.length < shortest.length)) {
|
|
12683
|
+
shortest = path33;
|
|
12261
12684
|
}
|
|
12262
12685
|
}
|
|
12263
12686
|
return shortest.map((hop) => this.resolveFilePathRecord(hop));
|
|
@@ -12305,13 +12728,13 @@ var Indexer = class _Indexer {
|
|
|
12305
12728
|
}
|
|
12306
12729
|
}
|
|
12307
12730
|
if (!found) continue;
|
|
12308
|
-
const
|
|
12731
|
+
const path33 = [];
|
|
12309
12732
|
let currentSymbolId = toSymbolId;
|
|
12310
12733
|
while (true) {
|
|
12311
12734
|
const symbol = symbolsById.get(currentSymbolId);
|
|
12312
12735
|
if (!symbol) break;
|
|
12313
12736
|
const parent = parentBySymbolId.get(currentSymbolId);
|
|
12314
|
-
|
|
12737
|
+
path33.push({
|
|
12315
12738
|
symbolId: symbol.id,
|
|
12316
12739
|
symbolName: symbol.name,
|
|
12317
12740
|
filePath: symbol.filePath,
|
|
@@ -12321,9 +12744,9 @@ var Indexer = class _Indexer {
|
|
|
12321
12744
|
if (!parent) break;
|
|
12322
12745
|
currentSymbolId = parent.parentId;
|
|
12323
12746
|
}
|
|
12324
|
-
|
|
12325
|
-
if (
|
|
12326
|
-
shortest =
|
|
12747
|
+
path33.reverse();
|
|
12748
|
+
if (path33.length > 0 && (shortest.length === 0 || path33.length < shortest.length)) {
|
|
12749
|
+
shortest = path33;
|
|
12327
12750
|
}
|
|
12328
12751
|
}
|
|
12329
12752
|
return shortest.map((hop) => this.resolveFilePathRecord(hop));
|
|
@@ -12659,7 +13082,6 @@ var Indexer = class _Indexer {
|
|
|
12659
13082
|
this.store = null;
|
|
12660
13083
|
this.invertedIndex = null;
|
|
12661
13084
|
this.provider = null;
|
|
12662
|
-
this.reranker = null;
|
|
12663
13085
|
this.configuredProviderInfo = null;
|
|
12664
13086
|
this.indexCompatibility = null;
|
|
12665
13087
|
this.initializationMode = "none";
|
|
@@ -12720,6 +13142,36 @@ function resolveConfigPathValue(value, baseDir) {
|
|
|
12720
13142
|
const absolutePath = path16.isAbsolute(trimmed) ? trimmed : path16.resolve(baseDir, trimmed);
|
|
12721
13143
|
return path16.normalize(absolutePath);
|
|
12722
13144
|
}
|
|
13145
|
+
function serializeConfigPathValue(value, baseDir) {
|
|
13146
|
+
const trimmed = value.trim();
|
|
13147
|
+
if (!trimmed) {
|
|
13148
|
+
return trimmed;
|
|
13149
|
+
}
|
|
13150
|
+
if (!path16.isAbsolute(trimmed)) {
|
|
13151
|
+
return normalizePathSeparators(path16.normalize(trimmed));
|
|
13152
|
+
}
|
|
13153
|
+
const relativePath = path16.relative(baseDir, trimmed);
|
|
13154
|
+
if (!relativePath || !relativePath.startsWith("..") && !path16.isAbsolute(relativePath)) {
|
|
13155
|
+
return normalizePathSeparators(path16.normalize(relativePath || "."));
|
|
13156
|
+
}
|
|
13157
|
+
return path16.normalize(trimmed);
|
|
13158
|
+
}
|
|
13159
|
+
function resolveKnowledgeBasePath(value, projectRoot) {
|
|
13160
|
+
return path16.isAbsolute(value) ? value : path16.resolve(projectRoot, value);
|
|
13161
|
+
}
|
|
13162
|
+
function normalizeKnowledgeBasePath(value, projectRoot) {
|
|
13163
|
+
return path16.normalize(resolveKnowledgeBasePath(value, projectRoot));
|
|
13164
|
+
}
|
|
13165
|
+
function hasMatchingKnowledgeBasePath(knowledgeBases, inputPath, projectRoot) {
|
|
13166
|
+
const normalizedInput = path16.normalize(inputPath);
|
|
13167
|
+
return knowledgeBases.some((kb) => normalizeKnowledgeBasePath(kb, projectRoot) === normalizedInput);
|
|
13168
|
+
}
|
|
13169
|
+
function findKnowledgeBasePathIndex(knowledgeBases, inputPath, projectRoot) {
|
|
13170
|
+
const normalizedInput = path16.normalize(inputPath);
|
|
13171
|
+
return knowledgeBases.findIndex(
|
|
13172
|
+
(kb) => path16.normalize(kb) === normalizedInput || normalizeKnowledgeBasePath(kb, projectRoot) === normalizedInput
|
|
13173
|
+
);
|
|
13174
|
+
}
|
|
12723
13175
|
|
|
12724
13176
|
// src/tools/format-communities.ts
|
|
12725
13177
|
function compareText(left, right) {
|
|
@@ -12973,8 +13425,8 @@ function formatExactSearchHandoff(results) {
|
|
|
12973
13425
|
}
|
|
12974
13426
|
function formatContextEvidence(result, index) {
|
|
12975
13427
|
const symbol = result.name ? ` ${JSON.stringify(compactEvidenceValue(result.name, 80))}` : "";
|
|
12976
|
-
const
|
|
12977
|
-
return `[${index}] ${result.chunkType}${symbol} in ${
|
|
13428
|
+
const path33 = compactEvidenceValue(result.filePath, 120);
|
|
13429
|
+
return `[${index}] ${result.chunkType}${symbol} in ${path33}:${result.startLine}-${result.endLine} (score ${result.score.toFixed(2)})`;
|
|
12978
13430
|
}
|
|
12979
13431
|
function formatContextPack(heading, selected, candidateCount, duplicateCount, limitOmittedCount, budgetOmittedCount, includeExactSearchHandoff) {
|
|
12980
13432
|
const lines = selected.map((result, index) => formatContextEvidence(result, index + 1));
|
|
@@ -13628,7 +14080,7 @@ function getErrorMessage4(error) {
|
|
|
13628
14080
|
return error instanceof Error ? error.message : String(error);
|
|
13629
14081
|
}
|
|
13630
14082
|
function runCommand(file, args, options) {
|
|
13631
|
-
return new Promise((
|
|
14083
|
+
return new Promise((resolve20, reject) => {
|
|
13632
14084
|
childProcess.execFile(
|
|
13633
14085
|
file,
|
|
13634
14086
|
args,
|
|
@@ -13638,7 +14090,7 @@ function runCommand(file, args, options) {
|
|
|
13638
14090
|
reject(error);
|
|
13639
14091
|
return;
|
|
13640
14092
|
}
|
|
13641
|
-
|
|
14093
|
+
resolve20(stdout);
|
|
13642
14094
|
}
|
|
13643
14095
|
);
|
|
13644
14096
|
});
|
|
@@ -13783,10 +14235,10 @@ function safeFailureMessage(error) {
|
|
|
13783
14235
|
}
|
|
13784
14236
|
function cancellableDelay(delayMs, signal) {
|
|
13785
14237
|
if (signal.aborted) return Promise.reject(new AutoIndexCancelledError());
|
|
13786
|
-
return new Promise((
|
|
14238
|
+
return new Promise((resolve20, reject) => {
|
|
13787
14239
|
const timer = setTimeout(() => {
|
|
13788
14240
|
signal.removeEventListener("abort", onAbort);
|
|
13789
|
-
|
|
14241
|
+
resolve20();
|
|
13790
14242
|
}, delayMs);
|
|
13791
14243
|
timer.unref?.();
|
|
13792
14244
|
const onAbort = () => {
|
|
@@ -13798,15 +14250,15 @@ function cancellableDelay(delayMs, signal) {
|
|
|
13798
14250
|
}
|
|
13799
14251
|
function withTimeout(promise, timeoutMs) {
|
|
13800
14252
|
if (timeoutMs <= 0) return Promise.resolve(void 0);
|
|
13801
|
-
return new Promise((
|
|
13802
|
-
const timer = setTimeout(() =>
|
|
14253
|
+
return new Promise((resolve20) => {
|
|
14254
|
+
const timer = setTimeout(() => resolve20(void 0), timeoutMs);
|
|
13803
14255
|
timer.unref?.();
|
|
13804
14256
|
void promise.then((value) => {
|
|
13805
14257
|
clearTimeout(timer);
|
|
13806
|
-
|
|
14258
|
+
resolve20(value);
|
|
13807
14259
|
}, () => {
|
|
13808
14260
|
clearTimeout(timer);
|
|
13809
|
-
|
|
14261
|
+
resolve20(void 0);
|
|
13810
14262
|
});
|
|
13811
14263
|
});
|
|
13812
14264
|
}
|
|
@@ -14188,17 +14640,17 @@ var AutoIndexCoordinator = class {
|
|
|
14188
14640
|
}
|
|
14189
14641
|
}
|
|
14190
14642
|
waitForBatteryRetry(delayMs) {
|
|
14191
|
-
return new Promise((
|
|
14643
|
+
return new Promise((resolve20) => {
|
|
14192
14644
|
const timer = setTimeout(() => {
|
|
14193
14645
|
if (this.batteryRetryTimer === timer) {
|
|
14194
14646
|
this.batteryRetryTimer = null;
|
|
14195
14647
|
this.resolveBatteryRetry = null;
|
|
14196
14648
|
}
|
|
14197
|
-
|
|
14649
|
+
resolve20();
|
|
14198
14650
|
}, delayMs);
|
|
14199
14651
|
timer.unref?.();
|
|
14200
14652
|
this.batteryRetryTimer = timer;
|
|
14201
|
-
this.resolveBatteryRetry =
|
|
14653
|
+
this.resolveBatteryRetry = resolve20;
|
|
14202
14654
|
});
|
|
14203
14655
|
}
|
|
14204
14656
|
cancelBatteryRetry() {
|
|
@@ -14206,9 +14658,9 @@ var AutoIndexCoordinator = class {
|
|
|
14206
14658
|
clearTimeout(this.batteryRetryTimer);
|
|
14207
14659
|
this.batteryRetryTimer = null;
|
|
14208
14660
|
}
|
|
14209
|
-
const
|
|
14661
|
+
const resolve20 = this.resolveBatteryRetry;
|
|
14210
14662
|
this.resolveBatteryRetry = null;
|
|
14211
|
-
|
|
14663
|
+
resolve20?.();
|
|
14212
14664
|
}
|
|
14213
14665
|
finishBatteryCheck(batteryCheck) {
|
|
14214
14666
|
if (this.batteryCheck !== batteryCheck) return;
|
|
@@ -14426,7 +14878,7 @@ function applyProjectOverride(merged, normalizedProjectConfig, globalConfig, key
|
|
|
14426
14878
|
function mergeUniqueStringArray(values) {
|
|
14427
14879
|
return [...new Set(values.map((value) => String(value).trim()))];
|
|
14428
14880
|
}
|
|
14429
|
-
function
|
|
14881
|
+
function normalizeKnowledgeBasePath2(value) {
|
|
14430
14882
|
let normalized = path19.normalize(String(value).trim());
|
|
14431
14883
|
const root = path19.parse(normalized).root;
|
|
14432
14884
|
while (normalized.length > root.length && /[\\/]$/.test(normalized)) {
|
|
@@ -14435,7 +14887,7 @@ function normalizeKnowledgeBasePath(value) {
|
|
|
14435
14887
|
return normalized;
|
|
14436
14888
|
}
|
|
14437
14889
|
function mergeKnowledgeBasePaths(values) {
|
|
14438
|
-
return [...new Set(values.map((value) =>
|
|
14890
|
+
return [...new Set(values.map((value) => normalizeKnowledgeBasePath2(value)).filter((value) => value.length > 0))];
|
|
14439
14891
|
}
|
|
14440
14892
|
function validateConfigLayerShape(rawConfig, filePath) {
|
|
14441
14893
|
if (!isRecord(rawConfig)) {
|
|
@@ -14565,9 +15017,30 @@ function toConfigRecord(rawConfig) {
|
|
|
14565
15017
|
}
|
|
14566
15018
|
return { ...rawConfig };
|
|
14567
15019
|
}
|
|
15020
|
+
function getConfigPath(projectRoot, host) {
|
|
15021
|
+
return resolveWritableProjectConfigPath(projectRoot, host);
|
|
15022
|
+
}
|
|
14568
15023
|
function loadRuntimeConfig(projectRoot, host) {
|
|
14569
15024
|
return normalizeKnowledgeBasePaths(toConfigRecord(loadMergedConfig(projectRoot, host)), projectRoot);
|
|
14570
15025
|
}
|
|
15026
|
+
function loadEditableConfig(projectRoot, host) {
|
|
15027
|
+
return normalizeKnowledgeBasePaths(toConfigRecord(loadProjectConfigLayer(projectRoot, host)), projectRoot);
|
|
15028
|
+
}
|
|
15029
|
+
function saveConfig(projectRoot, config, host) {
|
|
15030
|
+
const configPath = getConfigPath(projectRoot, host);
|
|
15031
|
+
const configDir = path20.dirname(configPath);
|
|
15032
|
+
const configBaseDir = path20.dirname(configDir);
|
|
15033
|
+
if (!(0, import_fs13.existsSync)(configDir)) {
|
|
15034
|
+
(0, import_fs13.mkdirSync)(configDir, { recursive: true });
|
|
15035
|
+
}
|
|
15036
|
+
const serializableConfig = { ...config };
|
|
15037
|
+
if (Array.isArray(serializableConfig.knowledgeBases)) {
|
|
15038
|
+
serializableConfig.knowledgeBases = serializableConfig.knowledgeBases.map(
|
|
15039
|
+
(kb) => serializeConfigPathValue(kb, configBaseDir)
|
|
15040
|
+
);
|
|
15041
|
+
}
|
|
15042
|
+
(0, import_fs13.writeFileSync)(configPath, JSON.stringify(serializableConfig, null, 2) + "\n", "utf-8");
|
|
15043
|
+
}
|
|
14571
15044
|
|
|
14572
15045
|
// src/tools/operation-runtime.ts
|
|
14573
15046
|
var indexerCache = /* @__PURE__ */ new Map();
|
|
@@ -14786,9 +15259,11 @@ async function searchCodebase(projectRoot, host, query, options = {}) {
|
|
|
14786
15259
|
contextLines: options.contextLines,
|
|
14787
15260
|
metadataOnly: options.metadataOnly,
|
|
14788
15261
|
definitionIntent: options.definitionIntent,
|
|
15262
|
+
prioritizeSourcePaths: options.prioritizeSourcePaths,
|
|
14789
15263
|
blameAuthor: options.blameAuthor,
|
|
14790
15264
|
blameSha: options.blameSha,
|
|
14791
15265
|
blameSince: options.blameSince,
|
|
15266
|
+
blameUntil: options.blameUntil,
|
|
14792
15267
|
trace: options.trace
|
|
14793
15268
|
});
|
|
14794
15269
|
}
|
|
@@ -14834,7 +15309,9 @@ async function findSimilarCode(projectRoot, host, code, options = {}) {
|
|
|
14834
15309
|
fileType: options.fileType,
|
|
14835
15310
|
directory: options.directory,
|
|
14836
15311
|
chunkType: options.chunkType,
|
|
14837
|
-
excludeFile: options.excludeFile
|
|
15312
|
+
excludeFile: options.excludeFile,
|
|
15313
|
+
blameSince: options.blameSince,
|
|
15314
|
+
blameUntil: options.blameUntil
|
|
14838
15315
|
});
|
|
14839
15316
|
}
|
|
14840
15317
|
async function implementationLookup(projectRoot, host, query, options = {}) {
|
|
@@ -14883,12 +15360,12 @@ async function getCallGraphPath(projectRoot, host, from, to, maxDepth, fromFileP
|
|
|
14883
15360
|
if (fromResolution.status !== "resolved" || toResolution.status !== "resolved") {
|
|
14884
15361
|
return { from: fromResolution, to: toResolution, path: [] };
|
|
14885
15362
|
}
|
|
14886
|
-
const
|
|
15363
|
+
const path33 = await indexer.findCallPathBySymbolIds(
|
|
14887
15364
|
fromResolution.symbolId,
|
|
14888
15365
|
toResolution.symbolId,
|
|
14889
15366
|
maxDepth
|
|
14890
15367
|
);
|
|
14891
|
-
return { from: fromResolution, to: toResolution, path:
|
|
15368
|
+
return { from: fromResolution, to: toResolution, path: path33 };
|
|
14892
15369
|
}
|
|
14893
15370
|
async function runIndexCodebase(projectRoot, host, args, onProgress) {
|
|
14894
15371
|
const root = getProjectRoot(projectRoot, host);
|
|
@@ -15080,6 +15557,141 @@ async function getIndexLogs(projectRoot, host, args) {
|
|
|
15080
15557
|
}).join("\n");
|
|
15081
15558
|
return { kind: "entries", text };
|
|
15082
15559
|
}
|
|
15560
|
+
function addKnowledgeBase(projectRoot, host, knowledgeBasePath) {
|
|
15561
|
+
const root = getProjectRoot(projectRoot, host);
|
|
15562
|
+
const inputPath = knowledgeBasePath.trim();
|
|
15563
|
+
const normalizedPath3 = path21.resolve(
|
|
15564
|
+
path21.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, root)
|
|
15565
|
+
);
|
|
15566
|
+
if (!(0, import_fs14.existsSync)(normalizedPath3)) {
|
|
15567
|
+
return `Error: Directory does not exist: ${normalizedPath3}`;
|
|
15568
|
+
}
|
|
15569
|
+
let realPath;
|
|
15570
|
+
try {
|
|
15571
|
+
realPath = (0, import_fs14.realpathSync)(normalizedPath3);
|
|
15572
|
+
} catch {
|
|
15573
|
+
return `Error: Cannot resolve path: ${normalizedPath3}`;
|
|
15574
|
+
}
|
|
15575
|
+
const blockedPrefixes = [
|
|
15576
|
+
"/etc",
|
|
15577
|
+
"/proc",
|
|
15578
|
+
"/sys",
|
|
15579
|
+
"/dev",
|
|
15580
|
+
"/boot",
|
|
15581
|
+
"/root",
|
|
15582
|
+
"/var/run",
|
|
15583
|
+
"/var/log"
|
|
15584
|
+
];
|
|
15585
|
+
const homeDir = process.platform === "win32" ? process.env.USERPROFILE ?? "" : process.env.HOME ?? "";
|
|
15586
|
+
const sensitiveDotDirs = [
|
|
15587
|
+
".ssh",
|
|
15588
|
+
".gnupg",
|
|
15589
|
+
".aws",
|
|
15590
|
+
".config/gcloud",
|
|
15591
|
+
".docker",
|
|
15592
|
+
".kube"
|
|
15593
|
+
];
|
|
15594
|
+
for (const prefix of blockedPrefixes) {
|
|
15595
|
+
if (realPath === prefix || realPath.startsWith(`${prefix}/`)) {
|
|
15596
|
+
return `Error: Adding sensitive directory as knowledge base is not allowed: ${normalizedPath3}`;
|
|
15597
|
+
}
|
|
15598
|
+
}
|
|
15599
|
+
for (const dotDir of sensitiveDotDirs) {
|
|
15600
|
+
const sensitiveDir = path21.join(homeDir, dotDir);
|
|
15601
|
+
if (sensitiveDir && (realPath === sensitiveDir || realPath.startsWith(`${sensitiveDir}/`))) {
|
|
15602
|
+
return `Error: Adding sensitive directory as knowledge base is not allowed: ${normalizedPath3}`;
|
|
15603
|
+
}
|
|
15604
|
+
}
|
|
15605
|
+
try {
|
|
15606
|
+
const stat5 = (0, import_fs14.statSync)(normalizedPath3);
|
|
15607
|
+
if (!stat5.isDirectory()) {
|
|
15608
|
+
return `Error: Path is not a directory: ${normalizedPath3}`;
|
|
15609
|
+
}
|
|
15610
|
+
} catch (error) {
|
|
15611
|
+
return `Error: Cannot access directory: ${normalizedPath3} - ${error instanceof Error ? error.message : String(error)}`;
|
|
15612
|
+
}
|
|
15613
|
+
const config = loadEditableConfig(root, host);
|
|
15614
|
+
const knowledgeBases = Array.isArray(config.knowledgeBases) ? config.knowledgeBases : [];
|
|
15615
|
+
const alreadyExists = hasMatchingKnowledgeBasePath(knowledgeBases, normalizedPath3, root);
|
|
15616
|
+
if (alreadyExists) {
|
|
15617
|
+
return `Knowledge base already configured: ${normalizedPath3}`;
|
|
15618
|
+
}
|
|
15619
|
+
knowledgeBases.push(normalizedPath3);
|
|
15620
|
+
config.knowledgeBases = knowledgeBases;
|
|
15621
|
+
saveConfig(root, config, host);
|
|
15622
|
+
refreshIndexerForDirectory(root, host);
|
|
15623
|
+
let result = `${normalizedPath3}
|
|
15624
|
+
`;
|
|
15625
|
+
result += `Total knowledge bases: ${knowledgeBases.length}
|
|
15626
|
+
`;
|
|
15627
|
+
result += `Config path: ${getConfigPath(root, host)}
|
|
15628
|
+
`;
|
|
15629
|
+
result += `
|
|
15630
|
+
Run /index to rebuild the index with the new knowledge base.`;
|
|
15631
|
+
return result;
|
|
15632
|
+
}
|
|
15633
|
+
function listKnowledgeBases(projectRoot, host) {
|
|
15634
|
+
const root = getProjectRoot(projectRoot, host);
|
|
15635
|
+
const config = loadRuntimeConfig(root, host);
|
|
15636
|
+
const knowledgeBases = Array.isArray(config.knowledgeBases) ? config.knowledgeBases : [];
|
|
15637
|
+
if (knowledgeBases.length === 0) {
|
|
15638
|
+
return "No knowledge bases configured. Use add_knowledge_base to add folders.";
|
|
15639
|
+
}
|
|
15640
|
+
let result = `Knowledge Bases (${knowledgeBases.length}):
|
|
15641
|
+
|
|
15642
|
+
`;
|
|
15643
|
+
for (let i = 0; i < knowledgeBases.length; i++) {
|
|
15644
|
+
const kb = knowledgeBases[i];
|
|
15645
|
+
const resolvedPath = resolveKnowledgeBasePath(kb, root);
|
|
15646
|
+
const exists = (0, import_fs14.existsSync)(resolvedPath);
|
|
15647
|
+
result += `[${i + 1}] ${kb}
|
|
15648
|
+
`;
|
|
15649
|
+
result += ` Resolved: ${resolvedPath}
|
|
15650
|
+
`;
|
|
15651
|
+
result += ` Status: ${exists ? "Exists" : "NOT FOUND"}
|
|
15652
|
+
`;
|
|
15653
|
+
if (exists) {
|
|
15654
|
+
try {
|
|
15655
|
+
const stat5 = (0, import_fs14.statSync)(resolvedPath);
|
|
15656
|
+
result += ` Type: ${stat5.isDirectory() ? "Directory" : "File"}
|
|
15657
|
+
`;
|
|
15658
|
+
} catch {
|
|
15659
|
+
}
|
|
15660
|
+
}
|
|
15661
|
+
result += "\n";
|
|
15662
|
+
}
|
|
15663
|
+
const hasHostConfig = (0, import_fs14.existsSync)(path21.join(root, getHostProjectConfigRelativePath(host)));
|
|
15664
|
+
if (hasHostConfig) {
|
|
15665
|
+
result += `
|
|
15666
|
+
Config sources: 1 file(s).`;
|
|
15667
|
+
}
|
|
15668
|
+
result += `
|
|
15669
|
+
Config file: ${getConfigPath(root, host)}`;
|
|
15670
|
+
return result;
|
|
15671
|
+
}
|
|
15672
|
+
function removeKnowledgeBase(projectRoot, host, knowledgeBasePath) {
|
|
15673
|
+
const root = getProjectRoot(projectRoot, host);
|
|
15674
|
+
const config = loadEditableConfig(root, host);
|
|
15675
|
+
const knowledgeBases = Array.isArray(config.knowledgeBases) ? config.knowledgeBases : [];
|
|
15676
|
+
const index = findKnowledgeBasePathIndex(knowledgeBases, knowledgeBasePath, root);
|
|
15677
|
+
if (index === -1) {
|
|
15678
|
+
return `Knowledge base not found: ${knowledgeBasePath}`;
|
|
15679
|
+
}
|
|
15680
|
+
const removed = knowledgeBases.splice(index, 1)[0];
|
|
15681
|
+
config.knowledgeBases = knowledgeBases;
|
|
15682
|
+
saveConfig(root, config, host);
|
|
15683
|
+
refreshIndexerForDirectory(root, host);
|
|
15684
|
+
let result = `Removed: ${removed}
|
|
15685
|
+
|
|
15686
|
+
`;
|
|
15687
|
+
result += `Remaining knowledge bases: ${knowledgeBases.length}
|
|
15688
|
+
`;
|
|
15689
|
+
result += `Config saved to: ${getConfigPath(root, host)}
|
|
15690
|
+
`;
|
|
15691
|
+
result += `
|
|
15692
|
+
Run /index to rebuild the index without the removed knowledge base.`;
|
|
15693
|
+
return result;
|
|
15694
|
+
}
|
|
15083
15695
|
|
|
15084
15696
|
// src/tools/context-search.ts
|
|
15085
15697
|
var MIN_CONTEXT_RESULT_LIMIT = 1;
|
|
@@ -15308,13 +15920,19 @@ async function resolveSearchContext(input, operations) {
|
|
|
15308
15920
|
(trace) => operations.lookup(symbol, MAX_CONTEXT_RESULT_LIMIT, scope, input.diagnostic ? trace : void 0)
|
|
15309
15921
|
);
|
|
15310
15922
|
};
|
|
15311
|
-
const tryConceptualSearch = async (searchQuery, scope, relaxedFieldsForAttempt) => {
|
|
15923
|
+
const tryConceptualSearch = async (searchQuery, scope, relaxedFieldsForAttempt, prioritizeSourcePaths) => {
|
|
15312
15924
|
return recordAttempt(
|
|
15313
15925
|
"conceptual",
|
|
15314
15926
|
searchQuery,
|
|
15315
15927
|
scope,
|
|
15316
15928
|
relaxedFieldsForAttempt,
|
|
15317
|
-
(trace) => operations.search(
|
|
15929
|
+
(trace) => operations.search(
|
|
15930
|
+
searchQuery,
|
|
15931
|
+
MAX_CONTEXT_RESULT_LIMIT,
|
|
15932
|
+
scope,
|
|
15933
|
+
input.diagnostic ? trace : void 0,
|
|
15934
|
+
{ prioritizeSourcePaths }
|
|
15935
|
+
)
|
|
15318
15936
|
);
|
|
15319
15937
|
};
|
|
15320
15938
|
const findSuccessfulAttemptState = (route) => {
|
|
@@ -15442,10 +16060,12 @@ Explicit symbol lookup only; conceptual search was not attempted.`
|
|
|
15442
16060
|
}
|
|
15443
16061
|
}
|
|
15444
16062
|
for (const attempt of conceptualAttemptPlan) {
|
|
16063
|
+
const attemptIntent = analyzeQueryIntent(attempt.queryText);
|
|
16064
|
+
const prioritizeSourcePaths = attemptIntent.primary !== "docs" && attemptIntent.primary !== "test";
|
|
15445
16065
|
if (inferredSymbol && attempt.queryText === inferredSymbol && attempt.queryText !== query) {
|
|
15446
16066
|
decisions.fallbackFromOriginalConceptualToInferred = true;
|
|
15447
16067
|
}
|
|
15448
|
-
const results = await tryConceptualSearch(attempt.queryText, attempt.scope, attempt.relaxed);
|
|
16068
|
+
const results = await tryConceptualSearch(attempt.queryText, attempt.scope, attempt.relaxed, prioritizeSourcePaths);
|
|
15449
16069
|
if (results.length > 0) {
|
|
15450
16070
|
const heading = buildPackHeading("conceptual", decisions);
|
|
15451
16071
|
const intent = analyzeQueryIntent(attempt.queryText);
|
|
@@ -15513,7 +16133,7 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
|
|
|
15513
16133
|
const directory = input.directory ?? void 0;
|
|
15514
16134
|
const tokenBudget = input.tokenBudget ?? void 0;
|
|
15515
16135
|
if (from && to) {
|
|
15516
|
-
const
|
|
16136
|
+
const path33 = await getCallGraphPath(
|
|
15517
16137
|
projectRoot,
|
|
15518
16138
|
host,
|
|
15519
16139
|
from,
|
|
@@ -15522,25 +16142,25 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
|
|
|
15522
16142
|
fromFilePath,
|
|
15523
16143
|
toFilePath
|
|
15524
16144
|
);
|
|
15525
|
-
const pathText = formatCallGraphPathResult(
|
|
15526
|
-
if (
|
|
16145
|
+
const pathText = formatCallGraphPathResult(path33);
|
|
16146
|
+
if (path33.path.length > 0) {
|
|
15527
16147
|
const fitted2 = fitTextToContextBudget(
|
|
15528
16148
|
pathText,
|
|
15529
16149
|
tokenBudget
|
|
15530
16150
|
);
|
|
15531
16151
|
return {
|
|
15532
16152
|
text: fitted2.text,
|
|
15533
|
-
details: fittedDetails("path", fitted2,
|
|
16153
|
+
details: fittedDetails("path", fitted2, path33.path.length)
|
|
15534
16154
|
};
|
|
15535
16155
|
}
|
|
15536
|
-
if (
|
|
16156
|
+
if (path33.from.status !== "resolved" || path33.to.status !== "resolved") {
|
|
15537
16157
|
const fitted2 = fitTextToContextBudget(pathText, tokenBudget);
|
|
15538
16158
|
return {
|
|
15539
16159
|
text: fitted2.text,
|
|
15540
16160
|
details: fittedDetails("path", fitted2, 0)
|
|
15541
16161
|
};
|
|
15542
16162
|
}
|
|
15543
|
-
const resolvedFrom =
|
|
16163
|
+
const resolvedFrom = path33.from;
|
|
15544
16164
|
const { callers } = await getCallGraphData(projectRoot, host, {
|
|
15545
16165
|
name: to,
|
|
15546
16166
|
direction: "callers",
|
|
@@ -15582,12 +16202,13 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
|
|
|
15582
16202
|
directory: scope.directory,
|
|
15583
16203
|
trace
|
|
15584
16204
|
}),
|
|
15585
|
-
search: (queryText, retrievalLimit, scope, trace) => searchCodebase(projectRoot, host, queryText, {
|
|
16205
|
+
search: (queryText, retrievalLimit, scope, trace, searchOptions) => searchCodebase(projectRoot, host, queryText, {
|
|
15586
16206
|
limit: retrievalLimit,
|
|
15587
16207
|
fileType: scope.fileType,
|
|
15588
16208
|
directory: scope.directory,
|
|
15589
16209
|
metadataOnly: true,
|
|
15590
|
-
trace
|
|
16210
|
+
trace,
|
|
16211
|
+
prioritizeSourcePaths: searchOptions?.prioritizeSourcePaths
|
|
15591
16212
|
})
|
|
15592
16213
|
});
|
|
15593
16214
|
}
|
|
@@ -16017,9 +16638,9 @@ function getRelevantEvidence(query) {
|
|
|
16017
16638
|
});
|
|
16018
16639
|
}
|
|
16019
16640
|
if (query.expected.acceptableFiles) {
|
|
16020
|
-
for (const
|
|
16641
|
+
for (const path33 of query.expected.acceptableFiles) {
|
|
16021
16642
|
legacyEvidence.push({
|
|
16022
|
-
path:
|
|
16643
|
+
path: path33,
|
|
16023
16644
|
...query.expected.symbol !== void 0 ? { symbol: query.expected.symbol } : {},
|
|
16024
16645
|
relevance: 1
|
|
16025
16646
|
});
|
|
@@ -16516,68 +17137,68 @@ function isStringArray4(value) {
|
|
|
16516
17137
|
function isNonEmptyString(value) {
|
|
16517
17138
|
return typeof value === "string" && value.trim().length > 0;
|
|
16518
17139
|
}
|
|
16519
|
-
function asPositiveNumber(value,
|
|
17140
|
+
function asPositiveNumber(value, path33) {
|
|
16520
17141
|
if (typeof value !== "number" || Number.isNaN(value) || value < 0) {
|
|
16521
|
-
throw new Error(`${
|
|
17142
|
+
throw new Error(`${path33} must be a non-negative number`);
|
|
16522
17143
|
}
|
|
16523
17144
|
return value;
|
|
16524
17145
|
}
|
|
16525
|
-
function parseQueryType(value,
|
|
17146
|
+
function parseQueryType(value, path33) {
|
|
16526
17147
|
if (value === "definition" || value === "implementation-intent" || value === "similarity" || value === "keyword-heavy" || value === "conceptual") {
|
|
16527
17148
|
return value;
|
|
16528
17149
|
}
|
|
16529
17150
|
throw new Error(
|
|
16530
|
-
`${
|
|
17151
|
+
`${path33} must be one of: definition, implementation-intent, similarity, keyword-heavy, conceptual`
|
|
16531
17152
|
);
|
|
16532
17153
|
}
|
|
16533
|
-
function parseExpectedRoute(value,
|
|
17154
|
+
function parseExpectedRoute(value, path33) {
|
|
16534
17155
|
if (value === void 0) return void 0;
|
|
16535
17156
|
if (value === "search" || value === "definition") return value;
|
|
16536
|
-
throw new Error(`${
|
|
17157
|
+
throw new Error(`${path33} must be one of: search, definition`);
|
|
16537
17158
|
}
|
|
16538
|
-
function parseExpectedOutcome(value,
|
|
17159
|
+
function parseExpectedOutcome(value, path33) {
|
|
16539
17160
|
if (value === void 0) return void 0;
|
|
16540
17161
|
if (value === "results" || value === "no-results") {
|
|
16541
17162
|
return value;
|
|
16542
17163
|
}
|
|
16543
|
-
throw new Error(`${
|
|
17164
|
+
throw new Error(`${path33} must be one of: results, no-results`);
|
|
16544
17165
|
}
|
|
16545
|
-
function parseRecoveryExpectation(value,
|
|
17166
|
+
function parseRecoveryExpectation(value, path33) {
|
|
16546
17167
|
if (value === void 0) return void 0;
|
|
16547
17168
|
if (value === "none" || value === "filter-relaxed") {
|
|
16548
17169
|
return value;
|
|
16549
17170
|
}
|
|
16550
|
-
throw new Error(`${
|
|
17171
|
+
throw new Error(`${path33} must be one of: none, filter-relaxed`);
|
|
16551
17172
|
}
|
|
16552
|
-
function parseQueryDifficulty(value,
|
|
17173
|
+
function parseQueryDifficulty(value, path33) {
|
|
16553
17174
|
if (value === void 0) return void 0;
|
|
16554
17175
|
if (value === "easy" || value === "medium" || value === "hard") {
|
|
16555
17176
|
return value;
|
|
16556
17177
|
}
|
|
16557
|
-
throw new Error(`${
|
|
17178
|
+
throw new Error(`${path33} must be one of: easy, medium, hard`);
|
|
16558
17179
|
}
|
|
16559
|
-
function parseQueryTags(value,
|
|
17180
|
+
function parseQueryTags(value, path33) {
|
|
16560
17181
|
if (value === void 0) return void 0;
|
|
16561
17182
|
if (!isStringArray4(value) || value.some((tag) => tag.trim().length === 0)) {
|
|
16562
|
-
throw new Error(`${
|
|
17183
|
+
throw new Error(`${path33} must be an array of non-empty strings`);
|
|
16563
17184
|
}
|
|
16564
17185
|
if (value.length > 16) {
|
|
16565
|
-
throw new Error(`${
|
|
17186
|
+
throw new Error(`${path33} must contain at most 16 tags`);
|
|
16566
17187
|
}
|
|
16567
17188
|
return value;
|
|
16568
17189
|
}
|
|
16569
|
-
function parseQueryArgs(value,
|
|
17190
|
+
function parseQueryArgs(value, path33) {
|
|
16570
17191
|
if (value === void 0) return void 0;
|
|
16571
17192
|
if (!isRecord3(value)) {
|
|
16572
|
-
throw new Error(`${
|
|
16573
|
-
}
|
|
16574
|
-
const symbol = parseStringOrUndefined(value.symbol, `${
|
|
16575
|
-
const filePath = parseStringOrUndefined(value.filePath, `${
|
|
16576
|
-
const fileType = parseStringOrUndefined(value.fileType, `${
|
|
16577
|
-
const directory = parseStringOrUndefined(value.directory, `${
|
|
16578
|
-
const callerLimit = parsePositiveIntegerOrUndefined(value.callerLimit, `${
|
|
16579
|
-
const calleeLimit = parsePositiveIntegerOrUndefined(value.calleeLimit, `${
|
|
16580
|
-
const tokenBudget = parsePositiveIntegerOrUndefined(value.tokenBudget, `${
|
|
17193
|
+
throw new Error(`${path33} must be an object`);
|
|
17194
|
+
}
|
|
17195
|
+
const symbol = parseStringOrUndefined(value.symbol, `${path33}.symbol`);
|
|
17196
|
+
const filePath = parseStringOrUndefined(value.filePath, `${path33}.filePath`);
|
|
17197
|
+
const fileType = parseStringOrUndefined(value.fileType, `${path33}.fileType`);
|
|
17198
|
+
const directory = parseStringOrUndefined(value.directory, `${path33}.directory`);
|
|
17199
|
+
const callerLimit = parsePositiveIntegerOrUndefined(value.callerLimit, `${path33}.callerLimit`);
|
|
17200
|
+
const calleeLimit = parsePositiveIntegerOrUndefined(value.calleeLimit, `${path33}.calleeLimit`);
|
|
17201
|
+
const tokenBudget = parsePositiveIntegerOrUndefined(value.tokenBudget, `${path33}.tokenBudget`);
|
|
16581
17202
|
return {
|
|
16582
17203
|
...symbol !== void 0 ? { symbol } : {},
|
|
16583
17204
|
...filePath !== void 0 ? { filePath } : {},
|
|
@@ -16588,50 +17209,50 @@ function parseQueryArgs(value, path31) {
|
|
|
16588
17209
|
...tokenBudget !== void 0 ? { tokenBudget } : {}
|
|
16589
17210
|
};
|
|
16590
17211
|
}
|
|
16591
|
-
function parsePositiveIntegerOrUndefined(value,
|
|
17212
|
+
function parsePositiveIntegerOrUndefined(value, path33) {
|
|
16592
17213
|
if (value === void 0 || value === null) return void 0;
|
|
16593
17214
|
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
16594
|
-
throw new Error(`${
|
|
17215
|
+
throw new Error(`${path33} must be a positive integer`);
|
|
16595
17216
|
}
|
|
16596
17217
|
return value;
|
|
16597
17218
|
}
|
|
16598
17219
|
var SEMVER_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
16599
|
-
function parseSemanticVersion(value,
|
|
17220
|
+
function parseSemanticVersion(value, path33) {
|
|
16600
17221
|
if (!isNonEmptyString(value)) {
|
|
16601
|
-
throw new Error(`${
|
|
17222
|
+
throw new Error(`${path33} must be a non-empty string`);
|
|
16602
17223
|
}
|
|
16603
17224
|
if (!SEMVER_VERSION_PATTERN.test(value)) {
|
|
16604
|
-
throw new Error(`${
|
|
17225
|
+
throw new Error(`${path33} must be a valid semantic version (MAJOR.MINOR.PATCH)`);
|
|
16605
17226
|
}
|
|
16606
17227
|
return value;
|
|
16607
17228
|
}
|
|
16608
|
-
function parseRetrievalMode(value,
|
|
17229
|
+
function parseRetrievalMode(value, path33) {
|
|
16609
17230
|
if (value === void 0 || value === "search") return "search";
|
|
16610
17231
|
if (value === "context" || value === "edit-context") return value;
|
|
16611
|
-
throw new Error(`${
|
|
17232
|
+
throw new Error(`${path33} must be one of: search, context, edit-context`);
|
|
16612
17233
|
}
|
|
16613
|
-
function parseStringOrUndefined(value,
|
|
17234
|
+
function parseStringOrUndefined(value, path33) {
|
|
16614
17235
|
if (value === void 0 || value === null) return void 0;
|
|
16615
17236
|
if (!isNonEmptyString(value)) {
|
|
16616
|
-
throw new Error(`${
|
|
17237
|
+
throw new Error(`${path33} must be a non-empty string`);
|
|
16617
17238
|
}
|
|
16618
17239
|
return value;
|
|
16619
17240
|
}
|
|
16620
|
-
function parseGradedEvidence(value,
|
|
17241
|
+
function parseGradedEvidence(value, path33) {
|
|
16621
17242
|
if (value === void 0) return [];
|
|
16622
17243
|
if (!Array.isArray(value)) {
|
|
16623
|
-
throw new Error(`${
|
|
17244
|
+
throw new Error(`${path33} must be an array`);
|
|
16624
17245
|
}
|
|
16625
17246
|
return value.map((entry, index) => {
|
|
16626
17247
|
if (!isRecord3(entry)) {
|
|
16627
|
-
throw new Error(`${
|
|
17248
|
+
throw new Error(`${path33}[${index}] must be an object`);
|
|
16628
17249
|
}
|
|
16629
|
-
const evidencePath = parseStringOrUndefined(entry.path, `${
|
|
17250
|
+
const evidencePath = parseStringOrUndefined(entry.path, `${path33}[${index}].path`);
|
|
16630
17251
|
if (evidencePath === void 0) {
|
|
16631
|
-
throw new Error(`${
|
|
17252
|
+
throw new Error(`${path33}[${index}].path is required`);
|
|
16632
17253
|
}
|
|
16633
|
-
const symbol = parseStringOrUndefined(entry.symbol, `${
|
|
16634
|
-
const relevance = parseEvidenceRelevance(entry.relevance, `${
|
|
17254
|
+
const symbol = parseStringOrUndefined(entry.symbol, `${path33}[${index}].symbol`);
|
|
17255
|
+
const relevance = parseEvidenceRelevance(entry.relevance, `${path33}[${index}].relevance`);
|
|
16635
17256
|
return {
|
|
16636
17257
|
path: evidencePath,
|
|
16637
17258
|
...symbol !== void 0 ? { symbol } : {},
|
|
@@ -16639,27 +17260,27 @@ function parseGradedEvidence(value, path31) {
|
|
|
16639
17260
|
};
|
|
16640
17261
|
});
|
|
16641
17262
|
}
|
|
16642
|
-
function parseEvidenceRelevance(value,
|
|
17263
|
+
function parseEvidenceRelevance(value, path33) {
|
|
16643
17264
|
if (value === void 0) {
|
|
16644
|
-
throw new Error(`${
|
|
17265
|
+
throw new Error(`${path33} is required`);
|
|
16645
17266
|
}
|
|
16646
17267
|
if (value !== 1 && value !== 2 && value !== 3) {
|
|
16647
|
-
throw new Error(`${
|
|
17268
|
+
throw new Error(`${path33} must be 1, 2, or 3`);
|
|
16648
17269
|
}
|
|
16649
17270
|
return value;
|
|
16650
17271
|
}
|
|
16651
|
-
function parseExpectedGraphNeighbor(value,
|
|
17272
|
+
function parseExpectedGraphNeighbor(value, path33) {
|
|
16652
17273
|
if (value === void 0) return void 0;
|
|
16653
17274
|
if (!isRecord3(value)) {
|
|
16654
|
-
throw new Error(`${
|
|
17275
|
+
throw new Error(`${path33} must be an object`);
|
|
16655
17276
|
}
|
|
16656
17277
|
if (value.direction !== "caller" && value.direction !== "callee") {
|
|
16657
|
-
throw new Error(`${
|
|
17278
|
+
throw new Error(`${path33}.direction must be one of: caller, callee`);
|
|
16658
17279
|
}
|
|
16659
|
-
const filePath = parseStringOrUndefined(value.filePath, `${
|
|
16660
|
-
const symbol = parseStringOrUndefined(value.symbol, `${
|
|
17280
|
+
const filePath = parseStringOrUndefined(value.filePath, `${path33}.filePath`);
|
|
17281
|
+
const symbol = parseStringOrUndefined(value.symbol, `${path33}.symbol`);
|
|
16661
17282
|
if (filePath === void 0 && symbol === void 0) {
|
|
16662
|
-
throw new Error(`${
|
|
17283
|
+
throw new Error(`${path33} must include filePath or symbol`);
|
|
16663
17284
|
}
|
|
16664
17285
|
return {
|
|
16665
17286
|
direction: value.direction,
|
|
@@ -16667,9 +17288,9 @@ function parseExpectedGraphNeighbor(value, path31) {
|
|
|
16667
17288
|
...symbol !== void 0 ? { symbol } : {}
|
|
16668
17289
|
};
|
|
16669
17290
|
}
|
|
16670
|
-
function parseExpected(input,
|
|
17291
|
+
function parseExpected(input, path33) {
|
|
16671
17292
|
if (!isRecord3(input)) {
|
|
16672
|
-
throw new Error(`${
|
|
17293
|
+
throw new Error(`${path33} must be an object`);
|
|
16673
17294
|
}
|
|
16674
17295
|
const filePathRaw = input.filePath;
|
|
16675
17296
|
const acceptableFilesRaw = input.acceptableFiles;
|
|
@@ -16680,29 +17301,29 @@ function parseExpected(input, path31) {
|
|
|
16680
17301
|
const recoveryExpectationRaw = input.recoveryExpectation;
|
|
16681
17302
|
const gradedEvidenceRaw = input.gradedEvidence;
|
|
16682
17303
|
const graphNeighborRaw = input.graphNeighbor;
|
|
16683
|
-
const filePath = parseStringOrUndefined(filePathRaw, `${
|
|
17304
|
+
const filePath = parseStringOrUndefined(filePathRaw, `${path33}.filePath`);
|
|
16684
17305
|
const acceptableFiles = isStringArray4(acceptableFilesRaw) ? acceptableFilesRaw : void 0;
|
|
16685
|
-
const gradedEvidence = parseGradedEvidence(gradedEvidenceRaw, `${
|
|
16686
|
-
const graphNeighbor = parseExpectedGraphNeighbor(graphNeighborRaw, `${
|
|
16687
|
-
const expectedOutcome = parseExpectedOutcome(expectedOutcomeRaw, `${
|
|
17306
|
+
const gradedEvidence = parseGradedEvidence(gradedEvidenceRaw, `${path33}.gradedEvidence`);
|
|
17307
|
+
const graphNeighbor = parseExpectedGraphNeighbor(graphNeighborRaw, `${path33}.graphNeighbor`);
|
|
17308
|
+
const expectedOutcome = parseExpectedOutcome(expectedOutcomeRaw, `${path33}.expectedOutcome`);
|
|
16688
17309
|
if (expectedOutcome !== "no-results" && !filePath && (!acceptableFiles || acceptableFiles.length === 0) && gradedEvidence.length === 0) {
|
|
16689
17310
|
throw new Error(
|
|
16690
|
-
`${
|
|
17311
|
+
`${path33} must include expected.filePath, expected.acceptableFiles, or expected.gradedEvidence`
|
|
16691
17312
|
);
|
|
16692
17313
|
}
|
|
16693
17314
|
if (acceptableFilesRaw !== void 0 && !isStringArray4(acceptableFilesRaw)) {
|
|
16694
|
-
throw new Error(`${
|
|
17315
|
+
throw new Error(`${path33}.acceptableFiles must be an array of strings`);
|
|
16695
17316
|
}
|
|
16696
17317
|
if (symbolRaw !== void 0 && typeof symbolRaw !== "string") {
|
|
16697
|
-
throw new Error(`${
|
|
17318
|
+
throw new Error(`${path33}.symbol must be a string when provided`);
|
|
16698
17319
|
}
|
|
16699
17320
|
if (branchRaw !== void 0 && typeof branchRaw !== "string") {
|
|
16700
|
-
throw new Error(`${
|
|
17321
|
+
throw new Error(`${path33}.branch must be a string when provided`);
|
|
16701
17322
|
}
|
|
16702
|
-
const expectedRoute = parseExpectedRoute(expectedRouteRaw, `${
|
|
17323
|
+
const expectedRoute = parseExpectedRoute(expectedRouteRaw, `${path33}.expectedRoute`);
|
|
16703
17324
|
const recoveryExpectation = parseRecoveryExpectation(
|
|
16704
17325
|
recoveryExpectationRaw,
|
|
16705
|
-
`${
|
|
17326
|
+
`${path33}.recoveryExpectation`
|
|
16706
17327
|
);
|
|
16707
17328
|
return {
|
|
16708
17329
|
filePath,
|
|
@@ -16716,13 +17337,13 @@ function parseExpected(input, path31) {
|
|
|
16716
17337
|
...graphNeighbor !== void 0 ? { graphNeighbor } : {}
|
|
16717
17338
|
};
|
|
16718
17339
|
}
|
|
16719
|
-
function parseQueryLanguage(value,
|
|
16720
|
-
return parseStringOrUndefined(value,
|
|
17340
|
+
function parseQueryLanguage(value, path33) {
|
|
17341
|
+
return parseStringOrUndefined(value, path33);
|
|
16721
17342
|
}
|
|
16722
17343
|
function parseQuery(input, index) {
|
|
16723
|
-
const
|
|
17344
|
+
const path33 = `queries[${index}]`;
|
|
16724
17345
|
if (!isRecord3(input)) {
|
|
16725
|
-
throw new Error(`${
|
|
17346
|
+
throw new Error(`${path33} must be an object`);
|
|
16726
17347
|
}
|
|
16727
17348
|
const id = input.id;
|
|
16728
17349
|
const query = input.query;
|
|
@@ -16734,21 +17355,21 @@ function parseQuery(input, index) {
|
|
|
16734
17355
|
const tags = input.tags;
|
|
16735
17356
|
const args = input.args;
|
|
16736
17357
|
if (typeof id !== "string" || id.trim().length === 0) {
|
|
16737
|
-
throw new Error(`${
|
|
17358
|
+
throw new Error(`${path33}.id must be a non-empty string`);
|
|
16738
17359
|
}
|
|
16739
17360
|
if (typeof query !== "string" || query.trim().length === 0) {
|
|
16740
|
-
throw new Error(`${
|
|
17361
|
+
throw new Error(`${path33}.query must be a non-empty string`);
|
|
16741
17362
|
}
|
|
16742
17363
|
return {
|
|
16743
17364
|
id,
|
|
16744
17365
|
query,
|
|
16745
|
-
queryType: parseQueryType(queryType, `${
|
|
16746
|
-
retrievalMode: parseRetrievalMode(retrievalMode, `${
|
|
16747
|
-
language: parseQueryLanguage(language, `${
|
|
16748
|
-
difficulty: parseQueryDifficulty(difficulty, `${
|
|
16749
|
-
args: parseQueryArgs(args, `${
|
|
16750
|
-
tags: parseQueryTags(tags, `${
|
|
16751
|
-
expected: parseExpected(expected, `${
|
|
17366
|
+
queryType: parseQueryType(queryType, `${path33}.queryType`),
|
|
17367
|
+
retrievalMode: parseRetrievalMode(retrievalMode, `${path33}.retrievalMode`),
|
|
17368
|
+
language: parseQueryLanguage(language, `${path33}.language`),
|
|
17369
|
+
difficulty: parseQueryDifficulty(difficulty, `${path33}.difficulty`),
|
|
17370
|
+
args: parseQueryArgs(args, `${path33}.args`),
|
|
17371
|
+
tags: parseQueryTags(tags, `${path33}.tags`),
|
|
17372
|
+
expected: parseExpected(expected, `${path33}.expected`)
|
|
16752
17373
|
};
|
|
16753
17374
|
}
|
|
16754
17375
|
function parseGoldenDataset(raw, sourceLabel) {
|
|
@@ -17088,12 +17709,13 @@ async function runEvaluation(options) {
|
|
|
17088
17709
|
fileType: scope.fileType,
|
|
17089
17710
|
directory: scope.directory
|
|
17090
17711
|
}),
|
|
17091
|
-
search: (searchQuery, limit, scope) => indexer.search(searchQuery, limit, {
|
|
17712
|
+
search: (searchQuery, limit, scope, _trace, searchOptions) => indexer.search(searchQuery, limit, {
|
|
17092
17713
|
metadataOnly: true,
|
|
17093
17714
|
filterByBranch: !!query.expected.branch,
|
|
17094
17715
|
definitionIntent: false,
|
|
17095
17716
|
fileType: scope.fileType,
|
|
17096
|
-
directory: scope.directory
|
|
17717
|
+
directory: scope.directory,
|
|
17718
|
+
prioritizeSourcePaths: searchOptions?.prioritizeSourcePaths
|
|
17097
17719
|
})
|
|
17098
17720
|
}) : void 0;
|
|
17099
17721
|
const result = editContextResult?.results ?? contextResult?.details?.results ?? await indexer.search(query.query, 10, {
|
|
@@ -17715,7 +18337,7 @@ async function executeCallGraph(projectRoot, host, args) {
|
|
|
17715
18337
|
return { text: formatCallGraphResult(await getCallGraphData(projectRoot, host, args)) };
|
|
17716
18338
|
}
|
|
17717
18339
|
async function executeCallGraphPath(projectRoot, host, args) {
|
|
17718
|
-
const
|
|
18340
|
+
const path33 = await getCallGraphPath(
|
|
17719
18341
|
projectRoot,
|
|
17720
18342
|
host,
|
|
17721
18343
|
args.from,
|
|
@@ -17724,7 +18346,7 @@ async function executeCallGraphPath(projectRoot, host, args) {
|
|
|
17724
18346
|
args.fromFilePath,
|
|
17725
18347
|
args.toFilePath
|
|
17726
18348
|
);
|
|
17727
|
-
return { text: formatCallGraphPathResult(
|
|
18349
|
+
return { text: formatCallGraphPathResult(path33) };
|
|
17728
18350
|
}
|
|
17729
18351
|
async function executeCodeCommunities(projectRoot, host, args) {
|
|
17730
18352
|
const result = await getCodeCommunities(projectRoot, host, args);
|
|
@@ -17870,11 +18492,21 @@ var PI_TOOL_NAMES = [
|
|
|
17870
18492
|
TOOL_NAME.PI_KNOWLEDGE_BASE_ADD,
|
|
17871
18493
|
TOOL_NAME.PI_KNOWLEDGE_BASE_REMOVE
|
|
17872
18494
|
];
|
|
18495
|
+
var MCP_TOOL_NAMES = [
|
|
18496
|
+
...PORTABLE_TOOL_NAMES,
|
|
18497
|
+
TOOL_NAME.ADD_KNOWLEDGE_BASE,
|
|
18498
|
+
TOOL_NAME.LIST_KNOWLEDGE_BASES,
|
|
18499
|
+
TOOL_NAME.REMOVE_KNOWLEDGE_BASE
|
|
18500
|
+
];
|
|
17873
18501
|
|
|
17874
18502
|
// src/adapters/mcp/register-tools.ts
|
|
17875
18503
|
function allowNullAsUndefined(schema) {
|
|
17876
18504
|
return import_zod2.z.preprocess((value) => value === null ? void 0 : value, schema);
|
|
17877
18505
|
}
|
|
18506
|
+
function knowledgeBaseResult(text) {
|
|
18507
|
+
const content = [{ type: "text", text }];
|
|
18508
|
+
return text.startsWith("Error: ") ? { content, isError: true } : { content };
|
|
18509
|
+
}
|
|
17878
18510
|
function registerMcpTools(server, runtime) {
|
|
17879
18511
|
server.tool(
|
|
17880
18512
|
TOOL_NAME.CODEBASE_CONTEXT,
|
|
@@ -17935,7 +18567,8 @@ function registerMcpTools(server, runtime) {
|
|
|
17935
18567
|
contextLines: allowNullAsUndefined(import_zod2.z.number().optional()).describe("Number of extra lines to include before/after each match (default: 0)"),
|
|
17936
18568
|
blameAuthor: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter by git blame author name or email"),
|
|
17937
18569
|
blameSha: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter by git blame commit SHA or prefix"),
|
|
17938
|
-
blameSince: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter to chunks last changed on or after this date")
|
|
18570
|
+
blameSince: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter to chunks last changed on or after this date"),
|
|
18571
|
+
blameUntil: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter to chunks last changed on or before this date")
|
|
17939
18572
|
},
|
|
17940
18573
|
async (args) => {
|
|
17941
18574
|
return searchCodebaseWithEffectiveness(runtime.projectRoot, runtime.host, "search", args.query, {
|
|
@@ -17946,7 +18579,8 @@ function registerMcpTools(server, runtime) {
|
|
|
17946
18579
|
contextLines: args.contextLines,
|
|
17947
18580
|
blameAuthor: args.blameAuthor,
|
|
17948
18581
|
blameSha: args.blameSha,
|
|
17949
|
-
blameSince: args.blameSince
|
|
18582
|
+
blameSince: args.blameSince,
|
|
18583
|
+
blameUntil: args.blameUntil
|
|
17950
18584
|
}, (results) => {
|
|
17951
18585
|
const text = results.length === 0 ? "No matching code found. Try a different query or run index_codebase first." : `Found ${results.length} results for "${args.query}":
|
|
17952
18586
|
|
|
@@ -17966,7 +18600,8 @@ ${formatSearchResults(results, "score")}`;
|
|
|
17966
18600
|
chunkType: allowNullAsUndefined(import_zod2.z.enum(CHUNK_TYPES).optional()).describe("Filter by code chunk type"),
|
|
17967
18601
|
blameAuthor: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter by git blame author name or email"),
|
|
17968
18602
|
blameSha: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter by git blame commit SHA or prefix"),
|
|
17969
|
-
blameSince: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter to chunks last changed on or after this date")
|
|
18603
|
+
blameSince: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter to chunks last changed on or after this date"),
|
|
18604
|
+
blameUntil: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter to chunks last changed on or before this date")
|
|
17970
18605
|
},
|
|
17971
18606
|
async (args) => {
|
|
17972
18607
|
return searchCodebaseWithEffectiveness(runtime.projectRoot, runtime.host, "peek", args.query, {
|
|
@@ -17977,7 +18612,8 @@ ${formatSearchResults(results, "score")}`;
|
|
|
17977
18612
|
metadataOnly: true,
|
|
17978
18613
|
blameAuthor: args.blameAuthor,
|
|
17979
18614
|
blameSha: args.blameSha,
|
|
17980
|
-
blameSince: args.blameSince
|
|
18615
|
+
blameSince: args.blameSince,
|
|
18616
|
+
blameUntil: args.blameUntil
|
|
17981
18617
|
}, (results) => {
|
|
17982
18618
|
const text = results.length === 0 ? "No matching code found. Try a different query or run index_codebase first." : `Found ${results.length} locations for "${args.query}":
|
|
17983
18619
|
|
|
@@ -18054,7 +18690,9 @@ ${formatCodebasePeek(results)}`;
|
|
|
18054
18690
|
fileType: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
|
|
18055
18691
|
directory: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
|
|
18056
18692
|
chunkType: allowNullAsUndefined(import_zod2.z.enum(CHUNK_TYPES).optional()).describe("Filter by code chunk type"),
|
|
18057
|
-
excludeFile: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Exclude results from this file path")
|
|
18693
|
+
excludeFile: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Exclude results from this file path"),
|
|
18694
|
+
blameSince: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter to chunks last changed on or after this date"),
|
|
18695
|
+
blameUntil: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter to chunks last changed on or before this date")
|
|
18058
18696
|
},
|
|
18059
18697
|
async (args) => {
|
|
18060
18698
|
const results = await findSimilarCode(runtime.projectRoot, runtime.host, args.code, {
|
|
@@ -18062,7 +18700,9 @@ ${formatCodebasePeek(results)}`;
|
|
|
18062
18700
|
fileType: args.fileType,
|
|
18063
18701
|
directory: args.directory,
|
|
18064
18702
|
chunkType: args.chunkType,
|
|
18065
|
-
excludeFile: args.excludeFile
|
|
18703
|
+
excludeFile: args.excludeFile,
|
|
18704
|
+
blameSince: args.blameSince,
|
|
18705
|
+
blameUntil: args.blameUntil
|
|
18066
18706
|
});
|
|
18067
18707
|
if (results.length === 0) {
|
|
18068
18708
|
return { content: [{ type: "text", text: "No similar code found. Try a different snippet or run index_codebase first." }] };
|
|
@@ -18166,6 +18806,37 @@ ${formatSearchResults(results)}` }] };
|
|
|
18166
18806
|
return { content: [{ type: "text", text: result.text }] };
|
|
18167
18807
|
}
|
|
18168
18808
|
);
|
|
18809
|
+
server.tool(
|
|
18810
|
+
TOOL_NAME.ADD_KNOWLEDGE_BASE,
|
|
18811
|
+
"Add a folder as a knowledge base to the semantic search index. The folder is indexed alongside the project code on the next index run. Provide an absolute path or a path relative to the project root. The path is written to the project-local host config of this MCP server (under the server project root), not to a user-global config, and the index is refreshed. Git blame metadata is collected only for files in the project git repo; knowledge-base files outside the repo remain searchable by content but have no blame. A knowledge base that appears in list_knowledge_bases but was inherited from a global config cannot be removed by this tool.",
|
|
18812
|
+
{
|
|
18813
|
+
path: import_zod2.z.string().describe("Path to the folder to add as a knowledge base (absolute or relative to the project root)")
|
|
18814
|
+
},
|
|
18815
|
+
async (args) => {
|
|
18816
|
+
const result = addKnowledgeBase(runtime.projectRoot, runtime.host, args.path);
|
|
18817
|
+
return knowledgeBaseResult(result);
|
|
18818
|
+
}
|
|
18819
|
+
);
|
|
18820
|
+
server.tool(
|
|
18821
|
+
TOOL_NAME.LIST_KNOWLEDGE_BASES,
|
|
18822
|
+
"List the configured knowledge base folders that the index includes alongside the project code. The list is the union of project-local and user-global knowledge bases; each entry shows the resolved path and whether it exists.",
|
|
18823
|
+
{},
|
|
18824
|
+
async () => {
|
|
18825
|
+
const result = listKnowledgeBases(runtime.projectRoot, runtime.host);
|
|
18826
|
+
return knowledgeBaseResult(result);
|
|
18827
|
+
}
|
|
18828
|
+
);
|
|
18829
|
+
server.tool(
|
|
18830
|
+
TOOL_NAME.REMOVE_KNOWLEDGE_BASE,
|
|
18831
|
+
"Remove a knowledge base folder from the semantic search index and refresh the index. The path must match a project-local configured path exactly. Knowledge bases inherited from a user-global config are not removable by this tool.",
|
|
18832
|
+
{
|
|
18833
|
+
path: import_zod2.z.string().describe("Path of the knowledge base to remove (must match a project-local configured path exactly)")
|
|
18834
|
+
},
|
|
18835
|
+
async (args) => {
|
|
18836
|
+
const result = removeKnowledgeBase(runtime.projectRoot, runtime.host, args.path.trim());
|
|
18837
|
+
return knowledgeBaseResult(result);
|
|
18838
|
+
}
|
|
18839
|
+
);
|
|
18169
18840
|
}
|
|
18170
18841
|
|
|
18171
18842
|
// src/adapters/mcp/server.ts
|
|
@@ -18303,7 +18974,7 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
18303
18974
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
18304
18975
|
const statMethod = opts.lstat ? import_promises.lstat : import_promises.stat;
|
|
18305
18976
|
if (wantBigintFsStats) {
|
|
18306
|
-
this._stat = (
|
|
18977
|
+
this._stat = (path33) => statMethod(path33, { bigint: true });
|
|
18307
18978
|
} else {
|
|
18308
18979
|
this._stat = statMethod;
|
|
18309
18980
|
}
|
|
@@ -18328,8 +18999,8 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
18328
18999
|
const par = this.parent;
|
|
18329
19000
|
const fil = par && par.files;
|
|
18330
19001
|
if (fil && fil.length > 0) {
|
|
18331
|
-
const { path:
|
|
18332
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
19002
|
+
const { path: path33, depth } = par;
|
|
19003
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path33));
|
|
18333
19004
|
const awaited = await Promise.all(slice);
|
|
18334
19005
|
for (const entry of awaited) {
|
|
18335
19006
|
if (!entry)
|
|
@@ -18369,20 +19040,20 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
18369
19040
|
this.reading = false;
|
|
18370
19041
|
}
|
|
18371
19042
|
}
|
|
18372
|
-
async _exploreDir(
|
|
19043
|
+
async _exploreDir(path33, depth) {
|
|
18373
19044
|
let files;
|
|
18374
19045
|
try {
|
|
18375
|
-
files = await (0, import_promises.readdir)(
|
|
19046
|
+
files = await (0, import_promises.readdir)(path33, this._rdOptions);
|
|
18376
19047
|
} catch (error) {
|
|
18377
19048
|
this._onError(error);
|
|
18378
19049
|
}
|
|
18379
|
-
return { files, depth, path:
|
|
19050
|
+
return { files, depth, path: path33 };
|
|
18380
19051
|
}
|
|
18381
|
-
async _formatEntry(dirent,
|
|
19052
|
+
async _formatEntry(dirent, path33) {
|
|
18382
19053
|
let entry;
|
|
18383
19054
|
const basename8 = this._isDirent ? dirent.name : dirent;
|
|
18384
19055
|
try {
|
|
18385
|
-
const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(
|
|
19056
|
+
const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path33, basename8));
|
|
18386
19057
|
entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename8 };
|
|
18387
19058
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
18388
19059
|
} catch (err) {
|
|
@@ -18782,16 +19453,16 @@ var delFromSet = (main, prop, item) => {
|
|
|
18782
19453
|
};
|
|
18783
19454
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
18784
19455
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
18785
|
-
function createFsWatchInstance(
|
|
19456
|
+
function createFsWatchInstance(path33, options, listener, errHandler, emitRaw) {
|
|
18786
19457
|
const handleEvent = (rawEvent, evPath) => {
|
|
18787
|
-
listener(
|
|
18788
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
18789
|
-
if (evPath &&
|
|
18790
|
-
fsWatchBroadcast(sp.resolve(
|
|
19458
|
+
listener(path33);
|
|
19459
|
+
emitRaw(rawEvent, evPath, { watchedPath: path33 });
|
|
19460
|
+
if (evPath && path33 !== evPath) {
|
|
19461
|
+
fsWatchBroadcast(sp.resolve(path33, evPath), KEY_LISTENERS, sp.join(path33, evPath));
|
|
18791
19462
|
}
|
|
18792
19463
|
};
|
|
18793
19464
|
try {
|
|
18794
|
-
return (0, import_node_fs.watch)(
|
|
19465
|
+
return (0, import_node_fs.watch)(path33, {
|
|
18795
19466
|
persistent: options.persistent
|
|
18796
19467
|
}, handleEvent);
|
|
18797
19468
|
} catch (error) {
|
|
@@ -18807,12 +19478,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
18807
19478
|
listener(val1, val2, val3);
|
|
18808
19479
|
});
|
|
18809
19480
|
};
|
|
18810
|
-
var setFsWatchListener = (
|
|
19481
|
+
var setFsWatchListener = (path33, fullPath, options, handlers) => {
|
|
18811
19482
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
18812
19483
|
let cont = FsWatchInstances.get(fullPath);
|
|
18813
19484
|
let watcher;
|
|
18814
19485
|
if (!options.persistent) {
|
|
18815
|
-
watcher = createFsWatchInstance(
|
|
19486
|
+
watcher = createFsWatchInstance(path33, options, listener, errHandler, rawEmitter);
|
|
18816
19487
|
if (!watcher)
|
|
18817
19488
|
return;
|
|
18818
19489
|
return watcher.close.bind(watcher);
|
|
@@ -18823,7 +19494,7 @@ var setFsWatchListener = (path31, fullPath, options, handlers) => {
|
|
|
18823
19494
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
18824
19495
|
} else {
|
|
18825
19496
|
watcher = createFsWatchInstance(
|
|
18826
|
-
|
|
19497
|
+
path33,
|
|
18827
19498
|
options,
|
|
18828
19499
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
18829
19500
|
errHandler,
|
|
@@ -18838,7 +19509,7 @@ var setFsWatchListener = (path31, fullPath, options, handlers) => {
|
|
|
18838
19509
|
cont.watcherUnusable = true;
|
|
18839
19510
|
if (isWindows && error.code === "EPERM") {
|
|
18840
19511
|
try {
|
|
18841
|
-
const fd = await (0, import_promises2.open)(
|
|
19512
|
+
const fd = await (0, import_promises2.open)(path33, "r");
|
|
18842
19513
|
await fd.close();
|
|
18843
19514
|
broadcastErr(error);
|
|
18844
19515
|
} catch (err) {
|
|
@@ -18869,7 +19540,7 @@ var setFsWatchListener = (path31, fullPath, options, handlers) => {
|
|
|
18869
19540
|
};
|
|
18870
19541
|
};
|
|
18871
19542
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
18872
|
-
var setFsWatchFileListener = (
|
|
19543
|
+
var setFsWatchFileListener = (path33, fullPath, options, handlers) => {
|
|
18873
19544
|
const { listener, rawEmitter } = handlers;
|
|
18874
19545
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
18875
19546
|
const copts = cont && cont.options;
|
|
@@ -18891,7 +19562,7 @@ var setFsWatchFileListener = (path31, fullPath, options, handlers) => {
|
|
|
18891
19562
|
});
|
|
18892
19563
|
const currmtime = curr.mtimeMs;
|
|
18893
19564
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
18894
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
19565
|
+
foreach(cont.listeners, (listener2) => listener2(path33, curr));
|
|
18895
19566
|
}
|
|
18896
19567
|
})
|
|
18897
19568
|
};
|
|
@@ -18921,13 +19592,13 @@ var NodeFsHandler = class {
|
|
|
18921
19592
|
* @param listener on fs change
|
|
18922
19593
|
* @returns closer for the watcher instance
|
|
18923
19594
|
*/
|
|
18924
|
-
_watchWithNodeFs(
|
|
19595
|
+
_watchWithNodeFs(path33, listener) {
|
|
18925
19596
|
const opts = this.fsw.options;
|
|
18926
|
-
const directory = sp.dirname(
|
|
18927
|
-
const basename8 = sp.basename(
|
|
19597
|
+
const directory = sp.dirname(path33);
|
|
19598
|
+
const basename8 = sp.basename(path33);
|
|
18928
19599
|
const parent = this.fsw._getWatchedDir(directory);
|
|
18929
19600
|
parent.add(basename8);
|
|
18930
|
-
const absolutePath = sp.resolve(
|
|
19601
|
+
const absolutePath = sp.resolve(path33);
|
|
18931
19602
|
const options = {
|
|
18932
19603
|
persistent: opts.persistent
|
|
18933
19604
|
};
|
|
@@ -18937,12 +19608,12 @@ var NodeFsHandler = class {
|
|
|
18937
19608
|
if (opts.usePolling) {
|
|
18938
19609
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
18939
19610
|
options.interval = enableBin && isBinaryPath(basename8) ? opts.binaryInterval : opts.interval;
|
|
18940
|
-
closer = setFsWatchFileListener(
|
|
19611
|
+
closer = setFsWatchFileListener(path33, absolutePath, options, {
|
|
18941
19612
|
listener,
|
|
18942
19613
|
rawEmitter: this.fsw._emitRaw
|
|
18943
19614
|
});
|
|
18944
19615
|
} else {
|
|
18945
|
-
closer = setFsWatchListener(
|
|
19616
|
+
closer = setFsWatchListener(path33, absolutePath, options, {
|
|
18946
19617
|
listener,
|
|
18947
19618
|
errHandler: this._boundHandleError,
|
|
18948
19619
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -18964,7 +19635,7 @@ var NodeFsHandler = class {
|
|
|
18964
19635
|
let prevStats = stats;
|
|
18965
19636
|
if (parent.has(basename8))
|
|
18966
19637
|
return;
|
|
18967
|
-
const listener = async (
|
|
19638
|
+
const listener = async (path33, newStats) => {
|
|
18968
19639
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
18969
19640
|
return;
|
|
18970
19641
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -18978,11 +19649,11 @@ var NodeFsHandler = class {
|
|
|
18978
19649
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
18979
19650
|
}
|
|
18980
19651
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
18981
|
-
this.fsw._closeFile(
|
|
19652
|
+
this.fsw._closeFile(path33);
|
|
18982
19653
|
prevStats = newStats2;
|
|
18983
19654
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
18984
19655
|
if (closer2)
|
|
18985
|
-
this.fsw._addPathCloser(
|
|
19656
|
+
this.fsw._addPathCloser(path33, closer2);
|
|
18986
19657
|
} else {
|
|
18987
19658
|
prevStats = newStats2;
|
|
18988
19659
|
}
|
|
@@ -19014,7 +19685,7 @@ var NodeFsHandler = class {
|
|
|
19014
19685
|
* @param item basename of this item
|
|
19015
19686
|
* @returns true if no more processing is needed for this entry.
|
|
19016
19687
|
*/
|
|
19017
|
-
async _handleSymlink(entry, directory,
|
|
19688
|
+
async _handleSymlink(entry, directory, path33, item) {
|
|
19018
19689
|
if (this.fsw.closed) {
|
|
19019
19690
|
return;
|
|
19020
19691
|
}
|
|
@@ -19024,7 +19695,7 @@ var NodeFsHandler = class {
|
|
|
19024
19695
|
this.fsw._incrReadyCount();
|
|
19025
19696
|
let linkPath;
|
|
19026
19697
|
try {
|
|
19027
|
-
linkPath = await (0, import_promises2.realpath)(
|
|
19698
|
+
linkPath = await (0, import_promises2.realpath)(path33);
|
|
19028
19699
|
} catch (e) {
|
|
19029
19700
|
this.fsw._emitReady();
|
|
19030
19701
|
return true;
|
|
@@ -19034,12 +19705,12 @@ var NodeFsHandler = class {
|
|
|
19034
19705
|
if (dir.has(item)) {
|
|
19035
19706
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
19036
19707
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
19037
|
-
this.fsw._emit(EV.CHANGE,
|
|
19708
|
+
this.fsw._emit(EV.CHANGE, path33, entry.stats);
|
|
19038
19709
|
}
|
|
19039
19710
|
} else {
|
|
19040
19711
|
dir.add(item);
|
|
19041
19712
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
19042
|
-
this.fsw._emit(EV.ADD,
|
|
19713
|
+
this.fsw._emit(EV.ADD, path33, entry.stats);
|
|
19043
19714
|
}
|
|
19044
19715
|
this.fsw._emitReady();
|
|
19045
19716
|
return true;
|
|
@@ -19069,9 +19740,9 @@ var NodeFsHandler = class {
|
|
|
19069
19740
|
return;
|
|
19070
19741
|
}
|
|
19071
19742
|
const item = entry.path;
|
|
19072
|
-
let
|
|
19743
|
+
let path33 = sp.join(directory, item);
|
|
19073
19744
|
current.add(item);
|
|
19074
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
19745
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path33, item)) {
|
|
19075
19746
|
return;
|
|
19076
19747
|
}
|
|
19077
19748
|
if (this.fsw.closed) {
|
|
@@ -19080,11 +19751,11 @@ var NodeFsHandler = class {
|
|
|
19080
19751
|
}
|
|
19081
19752
|
if (item === target || !target && !previous.has(item)) {
|
|
19082
19753
|
this.fsw._incrReadyCount();
|
|
19083
|
-
|
|
19084
|
-
this._addToNodeFs(
|
|
19754
|
+
path33 = sp.join(dir, sp.relative(dir, path33));
|
|
19755
|
+
this._addToNodeFs(path33, initialAdd, wh, depth + 1);
|
|
19085
19756
|
}
|
|
19086
19757
|
}).on(EV.ERROR, this._boundHandleError);
|
|
19087
|
-
return new Promise((
|
|
19758
|
+
return new Promise((resolve20, reject) => {
|
|
19088
19759
|
if (!stream)
|
|
19089
19760
|
return reject();
|
|
19090
19761
|
stream.once(STR_END, () => {
|
|
@@ -19093,7 +19764,7 @@ var NodeFsHandler = class {
|
|
|
19093
19764
|
return;
|
|
19094
19765
|
}
|
|
19095
19766
|
const wasThrottled = throttler ? throttler.clear() : false;
|
|
19096
|
-
|
|
19767
|
+
resolve20(void 0);
|
|
19097
19768
|
previous.getChildren().filter((item) => {
|
|
19098
19769
|
return item !== directory && !current.has(item);
|
|
19099
19770
|
}).forEach((item) => {
|
|
@@ -19150,13 +19821,13 @@ var NodeFsHandler = class {
|
|
|
19150
19821
|
* @param depth Child path actually targeted for watch
|
|
19151
19822
|
* @param target Child path actually targeted for watch
|
|
19152
19823
|
*/
|
|
19153
|
-
async _addToNodeFs(
|
|
19824
|
+
async _addToNodeFs(path33, initialAdd, priorWh, depth, target) {
|
|
19154
19825
|
const ready = this.fsw._emitReady;
|
|
19155
|
-
if (this.fsw._isIgnored(
|
|
19826
|
+
if (this.fsw._isIgnored(path33) || this.fsw.closed) {
|
|
19156
19827
|
ready();
|
|
19157
19828
|
return false;
|
|
19158
19829
|
}
|
|
19159
|
-
const wh = this.fsw._getWatchHelpers(
|
|
19830
|
+
const wh = this.fsw._getWatchHelpers(path33);
|
|
19160
19831
|
if (priorWh) {
|
|
19161
19832
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
19162
19833
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -19172,8 +19843,8 @@ var NodeFsHandler = class {
|
|
|
19172
19843
|
const follow = this.fsw.options.followSymlinks;
|
|
19173
19844
|
let closer;
|
|
19174
19845
|
if (stats.isDirectory()) {
|
|
19175
|
-
const absPath = sp.resolve(
|
|
19176
|
-
const targetPath = follow ? await (0, import_promises2.realpath)(
|
|
19846
|
+
const absPath = sp.resolve(path33);
|
|
19847
|
+
const targetPath = follow ? await (0, import_promises2.realpath)(path33) : path33;
|
|
19177
19848
|
if (this.fsw.closed)
|
|
19178
19849
|
return;
|
|
19179
19850
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -19183,29 +19854,29 @@ var NodeFsHandler = class {
|
|
|
19183
19854
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
19184
19855
|
}
|
|
19185
19856
|
} else if (stats.isSymbolicLink()) {
|
|
19186
|
-
const targetPath = follow ? await (0, import_promises2.realpath)(
|
|
19857
|
+
const targetPath = follow ? await (0, import_promises2.realpath)(path33) : path33;
|
|
19187
19858
|
if (this.fsw.closed)
|
|
19188
19859
|
return;
|
|
19189
19860
|
const parent = sp.dirname(wh.watchPath);
|
|
19190
19861
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
19191
19862
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
19192
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
19863
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path33, wh, targetPath);
|
|
19193
19864
|
if (this.fsw.closed)
|
|
19194
19865
|
return;
|
|
19195
19866
|
if (targetPath !== void 0) {
|
|
19196
|
-
this.fsw._symlinkPaths.set(sp.resolve(
|
|
19867
|
+
this.fsw._symlinkPaths.set(sp.resolve(path33), targetPath);
|
|
19197
19868
|
}
|
|
19198
19869
|
} else {
|
|
19199
19870
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
19200
19871
|
}
|
|
19201
19872
|
ready();
|
|
19202
19873
|
if (closer)
|
|
19203
|
-
this.fsw._addPathCloser(
|
|
19874
|
+
this.fsw._addPathCloser(path33, closer);
|
|
19204
19875
|
return false;
|
|
19205
19876
|
} catch (error) {
|
|
19206
19877
|
if (this.fsw._handleError(error)) {
|
|
19207
19878
|
ready();
|
|
19208
|
-
return
|
|
19879
|
+
return path33;
|
|
19209
19880
|
}
|
|
19210
19881
|
}
|
|
19211
19882
|
}
|
|
@@ -19237,35 +19908,35 @@ function createPattern(matcher) {
|
|
|
19237
19908
|
if (matcher.path === string)
|
|
19238
19909
|
return true;
|
|
19239
19910
|
if (matcher.recursive) {
|
|
19240
|
-
const
|
|
19241
|
-
if (!
|
|
19911
|
+
const relative14 = sp2.relative(matcher.path, string);
|
|
19912
|
+
if (!relative14) {
|
|
19242
19913
|
return false;
|
|
19243
19914
|
}
|
|
19244
|
-
return !
|
|
19915
|
+
return !relative14.startsWith("..") && !sp2.isAbsolute(relative14);
|
|
19245
19916
|
}
|
|
19246
19917
|
return false;
|
|
19247
19918
|
};
|
|
19248
19919
|
}
|
|
19249
19920
|
return () => false;
|
|
19250
19921
|
}
|
|
19251
|
-
function normalizePath3(
|
|
19252
|
-
if (typeof
|
|
19922
|
+
function normalizePath3(path33) {
|
|
19923
|
+
if (typeof path33 !== "string")
|
|
19253
19924
|
throw new Error("string expected");
|
|
19254
|
-
|
|
19255
|
-
|
|
19925
|
+
path33 = sp2.normalize(path33);
|
|
19926
|
+
path33 = path33.replace(/\\/g, "/");
|
|
19256
19927
|
let prepend = false;
|
|
19257
|
-
if (
|
|
19928
|
+
if (path33.startsWith("//"))
|
|
19258
19929
|
prepend = true;
|
|
19259
|
-
|
|
19930
|
+
path33 = path33.replace(DOUBLE_SLASH_RE, "/");
|
|
19260
19931
|
if (prepend)
|
|
19261
|
-
|
|
19262
|
-
return
|
|
19932
|
+
path33 = "/" + path33;
|
|
19933
|
+
return path33;
|
|
19263
19934
|
}
|
|
19264
19935
|
function matchPatterns(patterns, testString, stats) {
|
|
19265
|
-
const
|
|
19936
|
+
const path33 = normalizePath3(testString);
|
|
19266
19937
|
for (let index = 0; index < patterns.length; index++) {
|
|
19267
19938
|
const pattern = patterns[index];
|
|
19268
|
-
if (pattern(
|
|
19939
|
+
if (pattern(path33, stats)) {
|
|
19269
19940
|
return true;
|
|
19270
19941
|
}
|
|
19271
19942
|
}
|
|
@@ -19303,19 +19974,19 @@ var toUnix = (string) => {
|
|
|
19303
19974
|
}
|
|
19304
19975
|
return str;
|
|
19305
19976
|
};
|
|
19306
|
-
var normalizePathToUnix = (
|
|
19307
|
-
var normalizeIgnored = (cwd = "") => (
|
|
19308
|
-
if (typeof
|
|
19309
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
19977
|
+
var normalizePathToUnix = (path33) => toUnix(sp2.normalize(toUnix(path33)));
|
|
19978
|
+
var normalizeIgnored = (cwd = "") => (path33) => {
|
|
19979
|
+
if (typeof path33 === "string") {
|
|
19980
|
+
return normalizePathToUnix(sp2.isAbsolute(path33) ? path33 : sp2.join(cwd, path33));
|
|
19310
19981
|
} else {
|
|
19311
|
-
return
|
|
19982
|
+
return path33;
|
|
19312
19983
|
}
|
|
19313
19984
|
};
|
|
19314
|
-
var getAbsolutePath = (
|
|
19315
|
-
if (sp2.isAbsolute(
|
|
19316
|
-
return
|
|
19985
|
+
var getAbsolutePath = (path33, cwd) => {
|
|
19986
|
+
if (sp2.isAbsolute(path33)) {
|
|
19987
|
+
return path33;
|
|
19317
19988
|
}
|
|
19318
|
-
return sp2.join(cwd,
|
|
19989
|
+
return sp2.join(cwd, path33);
|
|
19319
19990
|
};
|
|
19320
19991
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
19321
19992
|
var DirEntry = class {
|
|
@@ -19380,10 +20051,10 @@ var WatchHelper = class {
|
|
|
19380
20051
|
dirParts;
|
|
19381
20052
|
followSymlinks;
|
|
19382
20053
|
statMethod;
|
|
19383
|
-
constructor(
|
|
20054
|
+
constructor(path33, follow, fsw) {
|
|
19384
20055
|
this.fsw = fsw;
|
|
19385
|
-
const watchPath =
|
|
19386
|
-
this.path =
|
|
20056
|
+
const watchPath = path33;
|
|
20057
|
+
this.path = path33 = path33.replace(REPLACER_RE, "");
|
|
19387
20058
|
this.watchPath = watchPath;
|
|
19388
20059
|
this.fullWatchPath = sp2.resolve(watchPath);
|
|
19389
20060
|
this.dirParts = [];
|
|
@@ -19523,20 +20194,20 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
19523
20194
|
this._closePromise = void 0;
|
|
19524
20195
|
let paths = unifyPaths(paths_);
|
|
19525
20196
|
if (cwd) {
|
|
19526
|
-
paths = paths.map((
|
|
19527
|
-
const absPath = getAbsolutePath(
|
|
20197
|
+
paths = paths.map((path33) => {
|
|
20198
|
+
const absPath = getAbsolutePath(path33, cwd);
|
|
19528
20199
|
return absPath;
|
|
19529
20200
|
});
|
|
19530
20201
|
}
|
|
19531
|
-
paths.forEach((
|
|
19532
|
-
this._removeIgnoredPath(
|
|
20202
|
+
paths.forEach((path33) => {
|
|
20203
|
+
this._removeIgnoredPath(path33);
|
|
19533
20204
|
});
|
|
19534
20205
|
this._userIgnored = void 0;
|
|
19535
20206
|
if (!this._readyCount)
|
|
19536
20207
|
this._readyCount = 0;
|
|
19537
20208
|
this._readyCount += paths.length;
|
|
19538
|
-
Promise.all(paths.map(async (
|
|
19539
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
20209
|
+
Promise.all(paths.map(async (path33) => {
|
|
20210
|
+
const res = await this._nodeFsHandler._addToNodeFs(path33, !_internal, void 0, 0, _origAdd);
|
|
19540
20211
|
if (res)
|
|
19541
20212
|
this._emitReady();
|
|
19542
20213
|
return res;
|
|
@@ -19558,17 +20229,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
19558
20229
|
return this;
|
|
19559
20230
|
const paths = unifyPaths(paths_);
|
|
19560
20231
|
const { cwd } = this.options;
|
|
19561
|
-
paths.forEach((
|
|
19562
|
-
if (!sp2.isAbsolute(
|
|
20232
|
+
paths.forEach((path33) => {
|
|
20233
|
+
if (!sp2.isAbsolute(path33) && !this._closers.has(path33)) {
|
|
19563
20234
|
if (cwd)
|
|
19564
|
-
|
|
19565
|
-
|
|
20235
|
+
path33 = sp2.join(cwd, path33);
|
|
20236
|
+
path33 = sp2.resolve(path33);
|
|
19566
20237
|
}
|
|
19567
|
-
this._closePath(
|
|
19568
|
-
this._addIgnoredPath(
|
|
19569
|
-
if (this._watched.has(
|
|
20238
|
+
this._closePath(path33);
|
|
20239
|
+
this._addIgnoredPath(path33);
|
|
20240
|
+
if (this._watched.has(path33)) {
|
|
19570
20241
|
this._addIgnoredPath({
|
|
19571
|
-
path:
|
|
20242
|
+
path: path33,
|
|
19572
20243
|
recursive: true
|
|
19573
20244
|
});
|
|
19574
20245
|
}
|
|
@@ -19632,38 +20303,38 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
19632
20303
|
* @param stats arguments to be passed with event
|
|
19633
20304
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
19634
20305
|
*/
|
|
19635
|
-
async _emit(event,
|
|
20306
|
+
async _emit(event, path33, stats) {
|
|
19636
20307
|
if (this.closed)
|
|
19637
20308
|
return;
|
|
19638
20309
|
const opts = this.options;
|
|
19639
20310
|
if (isWindows)
|
|
19640
|
-
|
|
20311
|
+
path33 = sp2.normalize(path33);
|
|
19641
20312
|
if (opts.cwd)
|
|
19642
|
-
|
|
19643
|
-
const args = [
|
|
20313
|
+
path33 = sp2.relative(opts.cwd, path33);
|
|
20314
|
+
const args = [path33];
|
|
19644
20315
|
if (stats != null)
|
|
19645
20316
|
args.push(stats);
|
|
19646
20317
|
const awf = opts.awaitWriteFinish;
|
|
19647
20318
|
let pw;
|
|
19648
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
20319
|
+
if (awf && (pw = this._pendingWrites.get(path33))) {
|
|
19649
20320
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
19650
20321
|
return this;
|
|
19651
20322
|
}
|
|
19652
20323
|
if (opts.atomic) {
|
|
19653
20324
|
if (event === EVENTS.UNLINK) {
|
|
19654
|
-
this._pendingUnlinks.set(
|
|
20325
|
+
this._pendingUnlinks.set(path33, [event, ...args]);
|
|
19655
20326
|
setTimeout(() => {
|
|
19656
|
-
this._pendingUnlinks.forEach((entry,
|
|
20327
|
+
this._pendingUnlinks.forEach((entry, path34) => {
|
|
19657
20328
|
this.emit(...entry);
|
|
19658
20329
|
this.emit(EVENTS.ALL, ...entry);
|
|
19659
|
-
this._pendingUnlinks.delete(
|
|
20330
|
+
this._pendingUnlinks.delete(path34);
|
|
19660
20331
|
});
|
|
19661
20332
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
19662
20333
|
return this;
|
|
19663
20334
|
}
|
|
19664
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
20335
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path33)) {
|
|
19665
20336
|
event = EVENTS.CHANGE;
|
|
19666
|
-
this._pendingUnlinks.delete(
|
|
20337
|
+
this._pendingUnlinks.delete(path33);
|
|
19667
20338
|
}
|
|
19668
20339
|
}
|
|
19669
20340
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -19681,16 +20352,16 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
19681
20352
|
this.emitWithAll(event, args);
|
|
19682
20353
|
}
|
|
19683
20354
|
};
|
|
19684
|
-
this._awaitWriteFinish(
|
|
20355
|
+
this._awaitWriteFinish(path33, awf.stabilityThreshold, event, awfEmit);
|
|
19685
20356
|
return this;
|
|
19686
20357
|
}
|
|
19687
20358
|
if (event === EVENTS.CHANGE) {
|
|
19688
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
20359
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path33, 50);
|
|
19689
20360
|
if (isThrottled)
|
|
19690
20361
|
return this;
|
|
19691
20362
|
}
|
|
19692
20363
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
19693
|
-
const fullPath = opts.cwd ? sp2.join(opts.cwd,
|
|
20364
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path33) : path33;
|
|
19694
20365
|
let stats2;
|
|
19695
20366
|
try {
|
|
19696
20367
|
stats2 = await (0, import_promises3.stat)(fullPath);
|
|
@@ -19721,23 +20392,23 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
19721
20392
|
* @param timeout duration of time to suppress duplicate actions
|
|
19722
20393
|
* @returns tracking object or false if action should be suppressed
|
|
19723
20394
|
*/
|
|
19724
|
-
_throttle(actionType,
|
|
20395
|
+
_throttle(actionType, path33, timeout) {
|
|
19725
20396
|
if (!this._throttled.has(actionType)) {
|
|
19726
20397
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
19727
20398
|
}
|
|
19728
20399
|
const action = this._throttled.get(actionType);
|
|
19729
20400
|
if (!action)
|
|
19730
20401
|
throw new Error("invalid throttle");
|
|
19731
|
-
const actionPath = action.get(
|
|
20402
|
+
const actionPath = action.get(path33);
|
|
19732
20403
|
if (actionPath) {
|
|
19733
20404
|
actionPath.count++;
|
|
19734
20405
|
return false;
|
|
19735
20406
|
}
|
|
19736
20407
|
let timeoutObject;
|
|
19737
20408
|
const clear = () => {
|
|
19738
|
-
const item = action.get(
|
|
20409
|
+
const item = action.get(path33);
|
|
19739
20410
|
const count = item ? item.count : 0;
|
|
19740
|
-
action.delete(
|
|
20411
|
+
action.delete(path33);
|
|
19741
20412
|
clearTimeout(timeoutObject);
|
|
19742
20413
|
if (item)
|
|
19743
20414
|
clearTimeout(item.timeoutObject);
|
|
@@ -19745,7 +20416,7 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
19745
20416
|
};
|
|
19746
20417
|
timeoutObject = setTimeout(clear, timeout);
|
|
19747
20418
|
const thr = { timeoutObject, clear, count: 0 };
|
|
19748
|
-
action.set(
|
|
20419
|
+
action.set(path33, thr);
|
|
19749
20420
|
return thr;
|
|
19750
20421
|
}
|
|
19751
20422
|
_incrReadyCount() {
|
|
@@ -19759,44 +20430,44 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
19759
20430
|
* @param event
|
|
19760
20431
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
19761
20432
|
*/
|
|
19762
|
-
_awaitWriteFinish(
|
|
20433
|
+
_awaitWriteFinish(path33, threshold, event, awfEmit) {
|
|
19763
20434
|
const awf = this.options.awaitWriteFinish;
|
|
19764
20435
|
if (typeof awf !== "object")
|
|
19765
20436
|
return;
|
|
19766
20437
|
const pollInterval = awf.pollInterval;
|
|
19767
20438
|
let timeoutHandler;
|
|
19768
|
-
let fullPath =
|
|
19769
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
19770
|
-
fullPath = sp2.join(this.options.cwd,
|
|
20439
|
+
let fullPath = path33;
|
|
20440
|
+
if (this.options.cwd && !sp2.isAbsolute(path33)) {
|
|
20441
|
+
fullPath = sp2.join(this.options.cwd, path33);
|
|
19771
20442
|
}
|
|
19772
20443
|
const now2 = /* @__PURE__ */ new Date();
|
|
19773
20444
|
const writes = this._pendingWrites;
|
|
19774
20445
|
function awaitWriteFinishFn(prevStat) {
|
|
19775
20446
|
(0, import_node_fs2.stat)(fullPath, (err, curStat) => {
|
|
19776
|
-
if (err || !writes.has(
|
|
20447
|
+
if (err || !writes.has(path33)) {
|
|
19777
20448
|
if (err && err.code !== "ENOENT")
|
|
19778
20449
|
awfEmit(err);
|
|
19779
20450
|
return;
|
|
19780
20451
|
}
|
|
19781
20452
|
const now3 = Number(/* @__PURE__ */ new Date());
|
|
19782
20453
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
19783
|
-
writes.get(
|
|
20454
|
+
writes.get(path33).lastChange = now3;
|
|
19784
20455
|
}
|
|
19785
|
-
const pw = writes.get(
|
|
20456
|
+
const pw = writes.get(path33);
|
|
19786
20457
|
const df = now3 - pw.lastChange;
|
|
19787
20458
|
if (df >= threshold) {
|
|
19788
|
-
writes.delete(
|
|
20459
|
+
writes.delete(path33);
|
|
19789
20460
|
awfEmit(void 0, curStat);
|
|
19790
20461
|
} else {
|
|
19791
20462
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
19792
20463
|
}
|
|
19793
20464
|
});
|
|
19794
20465
|
}
|
|
19795
|
-
if (!writes.has(
|
|
19796
|
-
writes.set(
|
|
20466
|
+
if (!writes.has(path33)) {
|
|
20467
|
+
writes.set(path33, {
|
|
19797
20468
|
lastChange: now2,
|
|
19798
20469
|
cancelWait: () => {
|
|
19799
|
-
writes.delete(
|
|
20470
|
+
writes.delete(path33);
|
|
19800
20471
|
clearTimeout(timeoutHandler);
|
|
19801
20472
|
return event;
|
|
19802
20473
|
}
|
|
@@ -19807,8 +20478,8 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
19807
20478
|
/**
|
|
19808
20479
|
* Determines whether user has asked to ignore this path.
|
|
19809
20480
|
*/
|
|
19810
|
-
_isIgnored(
|
|
19811
|
-
if (this.options.atomic && DOT_RE.test(
|
|
20481
|
+
_isIgnored(path33, stats) {
|
|
20482
|
+
if (this.options.atomic && DOT_RE.test(path33))
|
|
19812
20483
|
return true;
|
|
19813
20484
|
if (!this._userIgnored) {
|
|
19814
20485
|
const { cwd } = this.options;
|
|
@@ -19818,17 +20489,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
19818
20489
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
19819
20490
|
this._userIgnored = anymatch(list, void 0);
|
|
19820
20491
|
}
|
|
19821
|
-
return this._userIgnored(
|
|
20492
|
+
return this._userIgnored(path33, stats);
|
|
19822
20493
|
}
|
|
19823
|
-
_isntIgnored(
|
|
19824
|
-
return !this._isIgnored(
|
|
20494
|
+
_isntIgnored(path33, stat5) {
|
|
20495
|
+
return !this._isIgnored(path33, stat5);
|
|
19825
20496
|
}
|
|
19826
20497
|
/**
|
|
19827
20498
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
19828
20499
|
* @param path file or directory pattern being watched
|
|
19829
20500
|
*/
|
|
19830
|
-
_getWatchHelpers(
|
|
19831
|
-
return new WatchHelper(
|
|
20501
|
+
_getWatchHelpers(path33) {
|
|
20502
|
+
return new WatchHelper(path33, this.options.followSymlinks, this);
|
|
19832
20503
|
}
|
|
19833
20504
|
// Directory helpers
|
|
19834
20505
|
// -----------------
|
|
@@ -19860,63 +20531,63 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
19860
20531
|
* @param item base path of item/directory
|
|
19861
20532
|
*/
|
|
19862
20533
|
_remove(directory, item, isDirectory) {
|
|
19863
|
-
const
|
|
19864
|
-
const fullPath = sp2.resolve(
|
|
19865
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
19866
|
-
if (!this._throttle("remove",
|
|
20534
|
+
const path33 = sp2.join(directory, item);
|
|
20535
|
+
const fullPath = sp2.resolve(path33);
|
|
20536
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path33) || this._watched.has(fullPath);
|
|
20537
|
+
if (!this._throttle("remove", path33, 100))
|
|
19867
20538
|
return;
|
|
19868
20539
|
if (!isDirectory && this._watched.size === 1) {
|
|
19869
20540
|
this.add(directory, item, true);
|
|
19870
20541
|
}
|
|
19871
|
-
const wp = this._getWatchedDir(
|
|
20542
|
+
const wp = this._getWatchedDir(path33);
|
|
19872
20543
|
const nestedDirectoryChildren = wp.getChildren();
|
|
19873
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
20544
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path33, nested));
|
|
19874
20545
|
const parent = this._getWatchedDir(directory);
|
|
19875
20546
|
const wasTracked = parent.has(item);
|
|
19876
20547
|
parent.remove(item);
|
|
19877
20548
|
if (this._symlinkPaths.has(fullPath)) {
|
|
19878
20549
|
this._symlinkPaths.delete(fullPath);
|
|
19879
20550
|
}
|
|
19880
|
-
let relPath =
|
|
20551
|
+
let relPath = path33;
|
|
19881
20552
|
if (this.options.cwd)
|
|
19882
|
-
relPath = sp2.relative(this.options.cwd,
|
|
20553
|
+
relPath = sp2.relative(this.options.cwd, path33);
|
|
19883
20554
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
19884
20555
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
19885
20556
|
if (event === EVENTS.ADD)
|
|
19886
20557
|
return;
|
|
19887
20558
|
}
|
|
19888
|
-
this._watched.delete(
|
|
20559
|
+
this._watched.delete(path33);
|
|
19889
20560
|
this._watched.delete(fullPath);
|
|
19890
20561
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
19891
|
-
if (wasTracked && !this._isIgnored(
|
|
19892
|
-
this._emit(eventName,
|
|
19893
|
-
this._closePath(
|
|
20562
|
+
if (wasTracked && !this._isIgnored(path33))
|
|
20563
|
+
this._emit(eventName, path33);
|
|
20564
|
+
this._closePath(path33);
|
|
19894
20565
|
}
|
|
19895
20566
|
/**
|
|
19896
20567
|
* Closes all watchers for a path
|
|
19897
20568
|
*/
|
|
19898
|
-
_closePath(
|
|
19899
|
-
this._closeFile(
|
|
19900
|
-
const dir = sp2.dirname(
|
|
19901
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
20569
|
+
_closePath(path33) {
|
|
20570
|
+
this._closeFile(path33);
|
|
20571
|
+
const dir = sp2.dirname(path33);
|
|
20572
|
+
this._getWatchedDir(dir).remove(sp2.basename(path33));
|
|
19902
20573
|
}
|
|
19903
20574
|
/**
|
|
19904
20575
|
* Closes only file-specific watchers
|
|
19905
20576
|
*/
|
|
19906
|
-
_closeFile(
|
|
19907
|
-
const closers = this._closers.get(
|
|
20577
|
+
_closeFile(path33) {
|
|
20578
|
+
const closers = this._closers.get(path33);
|
|
19908
20579
|
if (!closers)
|
|
19909
20580
|
return;
|
|
19910
20581
|
closers.forEach((closer) => closer());
|
|
19911
|
-
this._closers.delete(
|
|
20582
|
+
this._closers.delete(path33);
|
|
19912
20583
|
}
|
|
19913
|
-
_addPathCloser(
|
|
20584
|
+
_addPathCloser(path33, closer) {
|
|
19914
20585
|
if (!closer)
|
|
19915
20586
|
return;
|
|
19916
|
-
let list = this._closers.get(
|
|
20587
|
+
let list = this._closers.get(path33);
|
|
19917
20588
|
if (!list) {
|
|
19918
20589
|
list = [];
|
|
19919
|
-
this._closers.set(
|
|
20590
|
+
this._closers.set(path33, list);
|
|
19920
20591
|
}
|
|
19921
20592
|
list.push(closer);
|
|
19922
20593
|
}
|
|
@@ -19946,12 +20617,291 @@ function watch(paths, options = {}) {
|
|
|
19946
20617
|
var chokidar_default = { watch, FSWatcher };
|
|
19947
20618
|
|
|
19948
20619
|
// src/watcher/file-watcher.ts
|
|
20620
|
+
var path28 = __toESM(require("path"), 1);
|
|
20621
|
+
|
|
20622
|
+
// src/watcher/native-recursive-watcher.ts
|
|
20623
|
+
var import_node_fs3 = require("fs");
|
|
19949
20624
|
var path26 = __toESM(require("path"), 1);
|
|
20625
|
+
var NativeRecursiveWatcher = class {
|
|
20626
|
+
constructor(root, onChange, options = {}) {
|
|
20627
|
+
this.root = root;
|
|
20628
|
+
this.onChange = onChange;
|
|
20629
|
+
this.watchFactory = options.watchFactory ?? this.defaultWatchFactory;
|
|
20630
|
+
this.onError = options.onError;
|
|
20631
|
+
}
|
|
20632
|
+
root;
|
|
20633
|
+
onChange;
|
|
20634
|
+
watcher = null;
|
|
20635
|
+
listenerToken = 0;
|
|
20636
|
+
watchFactory;
|
|
20637
|
+
onError;
|
|
20638
|
+
start() {
|
|
20639
|
+
if (this.watcher) return;
|
|
20640
|
+
const token = ++this.listenerToken;
|
|
20641
|
+
const listener = (_eventType, filename) => {
|
|
20642
|
+
if (this.watcher === null || this.listenerToken !== token) return;
|
|
20643
|
+
const absolutePath = this.toAbsolutePath(filename);
|
|
20644
|
+
const nextResult = this.onChange(absolutePath);
|
|
20645
|
+
if (nextResult instanceof Promise) {
|
|
20646
|
+
void nextResult.catch((error) => {
|
|
20647
|
+
console.error("[codebase-index] Error handling native watcher event:", error);
|
|
20648
|
+
});
|
|
20649
|
+
}
|
|
20650
|
+
};
|
|
20651
|
+
const watcher = this.watchFactory(this.root, listener, {
|
|
20652
|
+
persistent: true,
|
|
20653
|
+
recursive: true
|
|
20654
|
+
});
|
|
20655
|
+
watcher.on?.("error", (error) => {
|
|
20656
|
+
if (this.watcher === watcher && this.listenerToken === token) {
|
|
20657
|
+
this.onError?.(error);
|
|
20658
|
+
}
|
|
20659
|
+
});
|
|
20660
|
+
this.watcher = watcher;
|
|
20661
|
+
}
|
|
20662
|
+
async stop() {
|
|
20663
|
+
const watcher = this.watcher;
|
|
20664
|
+
this.watcher = null;
|
|
20665
|
+
this.listenerToken += 1;
|
|
20666
|
+
if (!watcher) return;
|
|
20667
|
+
await watcher.close();
|
|
20668
|
+
}
|
|
20669
|
+
toAbsolutePath(filename) {
|
|
20670
|
+
if (filename == null) return null;
|
|
20671
|
+
const normalizedFilename = typeof filename === "string" ? filename : filename.toString();
|
|
20672
|
+
const absolutePath = path26.resolve(this.root, normalizedFilename);
|
|
20673
|
+
const relativePath = path26.relative(this.root, absolutePath);
|
|
20674
|
+
const outsideRoot = relativePath === ".." || relativePath.startsWith(`..${path26.sep}`) || path26.isAbsolute(relativePath);
|
|
20675
|
+
return outsideRoot ? null : absolutePath;
|
|
20676
|
+
}
|
|
20677
|
+
defaultWatchFactory = (root, listener, options) => (0, import_node_fs3.watch)(root, options, listener);
|
|
20678
|
+
};
|
|
20679
|
+
|
|
20680
|
+
// src/watcher/snapshot.ts
|
|
20681
|
+
var fsPromises4 = __toESM(require("fs/promises"), 1);
|
|
20682
|
+
var path27 = __toESM(require("path"), 1);
|
|
20683
|
+
async function buildFileSnapshotScan(projectRoot, config, configPaths = []) {
|
|
20684
|
+
const normalizedProjectRoot = path27.resolve(projectRoot);
|
|
20685
|
+
const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
|
|
20686
|
+
const includePatterns = [...config.include, ...config.additionalInclude ?? []];
|
|
20687
|
+
const maxDepth = config.indexing?.maxDepth ?? -1;
|
|
20688
|
+
const snapshot = /* @__PURE__ */ new Map();
|
|
20689
|
+
const unreadablePrefixes = /* @__PURE__ */ new Set();
|
|
20690
|
+
const includeFile = async (filePath) => {
|
|
20691
|
+
const normalizedPath3 = path27.resolve(filePath);
|
|
20692
|
+
if (!shouldIncludeFile(normalizedPath3, normalizedProjectRoot, includePatterns, config.exclude, ignoreFilter)) return;
|
|
20693
|
+
const stat5 = await readStatIfFile(normalizedPath3, unreadablePrefixes);
|
|
20694
|
+
if (stat5) snapshot.set(normalizedPath3, { size: stat5.size, mtimeMs: stat5.mtimeMs });
|
|
20695
|
+
};
|
|
20696
|
+
const walk = async (directoryPath, depth) => {
|
|
20697
|
+
let entries;
|
|
20698
|
+
try {
|
|
20699
|
+
entries = await fsPromises4.readdir(directoryPath, { withFileTypes: true });
|
|
20700
|
+
} catch (error) {
|
|
20701
|
+
if (isMissingFsError(error)) return;
|
|
20702
|
+
if (isPermissionFsError(error)) {
|
|
20703
|
+
unreadablePrefixes.add(path27.resolve(directoryPath));
|
|
20704
|
+
return;
|
|
20705
|
+
}
|
|
20706
|
+
throw error;
|
|
20707
|
+
}
|
|
20708
|
+
for (const entry of entries) {
|
|
20709
|
+
const fullPath = path27.join(directoryPath, entry.name);
|
|
20710
|
+
const relativePath = path27.relative(normalizedProjectRoot, fullPath);
|
|
20711
|
+
if (entry.isDirectory()) {
|
|
20712
|
+
if (hasFilteredPathSegment(relativePath, path27.sep) || isRestrictedDirectory(relativePath, path27.sep)) continue;
|
|
20713
|
+
if (ignoreFilter.ignores(relativePath)) continue;
|
|
20714
|
+
if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
|
|
20715
|
+
} else if (entry.isFile()) {
|
|
20716
|
+
await includeFile(fullPath);
|
|
20717
|
+
}
|
|
20718
|
+
}
|
|
20719
|
+
};
|
|
20720
|
+
await walk(normalizedProjectRoot, 0);
|
|
20721
|
+
await includeExplicitConfigPaths(snapshot, unreadablePrefixes, configPaths);
|
|
20722
|
+
return { entries: snapshot, unreadablePrefixes };
|
|
20723
|
+
}
|
|
20724
|
+
async function buildFileSnapshotForPathScan(projectRoot, config, configPaths, targetPath) {
|
|
20725
|
+
const normalizedProjectRoot = path27.resolve(projectRoot);
|
|
20726
|
+
const normalizedTargetPath = path27.resolve(targetPath);
|
|
20727
|
+
if (!isWithinPath(normalizedProjectRoot, normalizedTargetPath)) {
|
|
20728
|
+
return { entries: /* @__PURE__ */ new Map(), unreadablePrefixes: /* @__PURE__ */ new Set() };
|
|
20729
|
+
}
|
|
20730
|
+
const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
|
|
20731
|
+
const includePatterns = [...config.include, ...config.additionalInclude ?? []];
|
|
20732
|
+
const maxDepth = config.indexing?.maxDepth ?? -1;
|
|
20733
|
+
const explicitConfigPaths = new Set(configPaths.map((configPath) => path27.resolve(configPath)));
|
|
20734
|
+
const snapshot = /* @__PURE__ */ new Map();
|
|
20735
|
+
const unreadablePrefixes = /* @__PURE__ */ new Set();
|
|
20736
|
+
const includeFile = async (filePath) => {
|
|
20737
|
+
const normalizedPath3 = path27.resolve(filePath);
|
|
20738
|
+
if (!explicitConfigPaths.has(normalizedPath3) && !shouldIncludeFile(
|
|
20739
|
+
normalizedPath3,
|
|
20740
|
+
normalizedProjectRoot,
|
|
20741
|
+
includePatterns,
|
|
20742
|
+
config.exclude,
|
|
20743
|
+
ignoreFilter
|
|
20744
|
+
)) return;
|
|
20745
|
+
const stat5 = await readStatIfFile(normalizedPath3, unreadablePrefixes);
|
|
20746
|
+
if (stat5) snapshot.set(normalizedPath3, { size: stat5.size, mtimeMs: stat5.mtimeMs });
|
|
20747
|
+
};
|
|
20748
|
+
const walk = async (directoryPath, depth) => {
|
|
20749
|
+
let entries;
|
|
20750
|
+
try {
|
|
20751
|
+
entries = await fsPromises4.readdir(directoryPath, { withFileTypes: true });
|
|
20752
|
+
} catch (error) {
|
|
20753
|
+
if (isMissingFsError(error)) return;
|
|
20754
|
+
if (isPermissionFsError(error)) {
|
|
20755
|
+
unreadablePrefixes.add(path27.resolve(directoryPath));
|
|
20756
|
+
return;
|
|
20757
|
+
}
|
|
20758
|
+
throw error;
|
|
20759
|
+
}
|
|
20760
|
+
for (const entry of entries) {
|
|
20761
|
+
const fullPath = path27.join(directoryPath, entry.name);
|
|
20762
|
+
const relativePath = path27.relative(normalizedProjectRoot, fullPath);
|
|
20763
|
+
if (entry.isDirectory()) {
|
|
20764
|
+
if (hasFilteredPathSegment(relativePath, path27.sep) || isRestrictedDirectory(relativePath, path27.sep)) continue;
|
|
20765
|
+
if (ignoreFilter.ignores(relativePath)) continue;
|
|
20766
|
+
if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
|
|
20767
|
+
} else if (entry.isFile()) {
|
|
20768
|
+
await includeFile(fullPath);
|
|
20769
|
+
}
|
|
20770
|
+
}
|
|
20771
|
+
};
|
|
20772
|
+
const targetStat = await readStatIfFile(normalizedTargetPath, unreadablePrefixes);
|
|
20773
|
+
if (targetStat) await includeFile(normalizedTargetPath);
|
|
20774
|
+
else await walk(normalizedTargetPath, 0);
|
|
20775
|
+
await includeExplicitConfigPathsInPath(snapshot, unreadablePrefixes, configPaths, normalizedTargetPath);
|
|
20776
|
+
return { entries: snapshot, unreadablePrefixes };
|
|
20777
|
+
}
|
|
20778
|
+
function completeFileSnapshot(previous, scan) {
|
|
20779
|
+
const completed = new Map(scan.entries);
|
|
20780
|
+
for (const unreadablePrefix of scan.unreadablePrefixes) {
|
|
20781
|
+
for (const [entryPath, entry] of previous) {
|
|
20782
|
+
if (isWithinPath(unreadablePrefix, entryPath) && !completed.has(entryPath)) completed.set(entryPath, entry);
|
|
20783
|
+
}
|
|
20784
|
+
}
|
|
20785
|
+
return completed;
|
|
20786
|
+
}
|
|
20787
|
+
async function includeExplicitConfigPaths(snapshot, unreadablePrefixes, configPaths) {
|
|
20788
|
+
for (const configPath of [...new Set(configPaths.map((value) => path27.resolve(value)))]) {
|
|
20789
|
+
if (snapshot.has(configPath)) continue;
|
|
20790
|
+
const stat5 = await readStatIfFile(configPath, unreadablePrefixes);
|
|
20791
|
+
if (stat5) snapshot.set(configPath, { size: stat5.size, mtimeMs: stat5.mtimeMs });
|
|
20792
|
+
}
|
|
20793
|
+
}
|
|
20794
|
+
async function includeExplicitConfigPathsInPath(snapshot, unreadablePrefixes, configPaths, targetPath) {
|
|
20795
|
+
await includeExplicitConfigPaths(
|
|
20796
|
+
snapshot,
|
|
20797
|
+
unreadablePrefixes,
|
|
20798
|
+
configPaths.filter((configPath) => isWithinPath(targetPath, path27.resolve(configPath)))
|
|
20799
|
+
);
|
|
20800
|
+
}
|
|
20801
|
+
function isWithinPath(parentPath, childPath) {
|
|
20802
|
+
const relativePath = path27.relative(parentPath, childPath);
|
|
20803
|
+
return relativePath === "" || !relativePath.startsWith(`..${path27.sep}`) && relativePath !== ".." && !path27.isAbsolute(relativePath);
|
|
20804
|
+
}
|
|
20805
|
+
async function readStatIfFile(filePath, unreadablePrefixes) {
|
|
20806
|
+
try {
|
|
20807
|
+
const stat5 = await fsPromises4.stat(filePath);
|
|
20808
|
+
return stat5.isFile() ? stat5 : null;
|
|
20809
|
+
} catch (error) {
|
|
20810
|
+
if (isMissingFsError(error)) return null;
|
|
20811
|
+
if (isPermissionFsError(error)) {
|
|
20812
|
+
unreadablePrefixes.add(path27.resolve(filePath));
|
|
20813
|
+
return null;
|
|
20814
|
+
}
|
|
20815
|
+
throw error;
|
|
20816
|
+
}
|
|
20817
|
+
}
|
|
20818
|
+
function isMissingFsError(error) {
|
|
20819
|
+
return error instanceof Error && ["ENOENT", "ENOTDIR"].includes(error.code ?? "");
|
|
20820
|
+
}
|
|
20821
|
+
function isPermissionFsError(error) {
|
|
20822
|
+
return error instanceof Error && ["EACCES", "EPERM"].includes(error.code ?? "");
|
|
20823
|
+
}
|
|
20824
|
+
var diffTypeOrder = { add: 0, change: 1, unlink: 2 };
|
|
20825
|
+
function diffFileSnapshots(previous, current, forcedChanges = /* @__PURE__ */ new Set()) {
|
|
20826
|
+
const changes = [];
|
|
20827
|
+
for (const [filePath, previousEntry] of previous) {
|
|
20828
|
+
const currentEntry = current.get(filePath);
|
|
20829
|
+
if (!currentEntry) changes.push({ type: "unlink", path: filePath });
|
|
20830
|
+
else if (forcedChanges.has(filePath) || currentEntry.size !== previousEntry.size || currentEntry.mtimeMs !== previousEntry.mtimeMs) {
|
|
20831
|
+
changes.push({ type: "change", path: filePath });
|
|
20832
|
+
}
|
|
20833
|
+
}
|
|
20834
|
+
for (const [filePath] of current) {
|
|
20835
|
+
if (!previous.has(filePath)) changes.push({ type: "add", path: filePath });
|
|
20836
|
+
}
|
|
20837
|
+
return changes.sort((left, right) => left.path.localeCompare(right.path) || diffTypeOrder[left.type] - diffTypeOrder[right.type]);
|
|
20838
|
+
}
|
|
20839
|
+
|
|
20840
|
+
// src/watcher/snapshot-reconciler.ts
|
|
20841
|
+
var FileSnapshotReconciler = class {
|
|
20842
|
+
constructor(projectRoot, config, configPaths) {
|
|
20843
|
+
this.projectRoot = projectRoot;
|
|
20844
|
+
this.config = config;
|
|
20845
|
+
this.configPaths = configPaths;
|
|
20846
|
+
}
|
|
20847
|
+
projectRoot;
|
|
20848
|
+
config;
|
|
20849
|
+
configPaths;
|
|
20850
|
+
snapshot = null;
|
|
20851
|
+
reconciliationTail = Promise.resolve();
|
|
20852
|
+
async initialize() {
|
|
20853
|
+
this.snapshot = (await buildFileSnapshotScan(this.projectRoot, this.config, this.configPaths)).entries;
|
|
20854
|
+
}
|
|
20855
|
+
async reconcile(invalidations = []) {
|
|
20856
|
+
if (this.snapshot === null) {
|
|
20857
|
+
throw new Error("FileSnapshotReconciler is not initialized. Call initialize() before reconcile().");
|
|
20858
|
+
}
|
|
20859
|
+
const reconciliation = this.reconciliationTail.then(async () => {
|
|
20860
|
+
const previousSnapshot = this.snapshot;
|
|
20861
|
+
if (previousSnapshot === null) {
|
|
20862
|
+
throw new Error("FileSnapshotReconciler is not initialized. Call initialize() before reconcile().");
|
|
20863
|
+
}
|
|
20864
|
+
const normalizedInvalidations = invalidations.map((invalidation) => typeof invalidation === "string" || invalidation === null ? { path: invalidation, forceChange: false } : { path: invalidation.path, forceChange: invalidation.forceChange === true });
|
|
20865
|
+
const scopedPaths = normalizedInvalidations.map((invalidation) => invalidation.path).filter((filePath) => filePath !== null);
|
|
20866
|
+
const scan = scopedPaths.length === 0 || scopedPaths.length !== normalizedInvalidations.length ? await buildFileSnapshotScan(this.projectRoot, this.config, this.configPaths) : await this.reconcilePaths(previousSnapshot, scopedPaths);
|
|
20867
|
+
const nextSnapshot = completeFileSnapshot(previousSnapshot, scan);
|
|
20868
|
+
const forcedChanges = new Set(normalizedInvalidations.filter((invalidation) => invalidation.path !== null && invalidation.forceChange).map((invalidation) => invalidation.path));
|
|
20869
|
+
const changes = diffFileSnapshots(previousSnapshot, nextSnapshot, forcedChanges);
|
|
20870
|
+
this.snapshot = nextSnapshot;
|
|
20871
|
+
return changes;
|
|
20872
|
+
});
|
|
20873
|
+
this.reconciliationTail = reconciliation.then(() => void 0, () => void 0);
|
|
20874
|
+
return reconciliation;
|
|
20875
|
+
}
|
|
20876
|
+
async reconcilePaths(previousSnapshot, invalidatedPaths) {
|
|
20877
|
+
const scopes = this.getScopes(invalidatedPaths);
|
|
20878
|
+
const entries = new Map(previousSnapshot);
|
|
20879
|
+
const unreadablePrefixes = /* @__PURE__ */ new Set();
|
|
20880
|
+
for (const scope of scopes) {
|
|
20881
|
+
for (const previousPath of entries.keys()) {
|
|
20882
|
+
if (isWithinPath(scope, previousPath)) entries.delete(previousPath);
|
|
20883
|
+
}
|
|
20884
|
+
const scopedScan = await buildFileSnapshotForPathScan(this.projectRoot, this.config, this.configPaths, scope);
|
|
20885
|
+
for (const [filePath, entry] of scopedScan.entries) entries.set(filePath, entry);
|
|
20886
|
+
for (const unreadablePrefix of scopedScan.unreadablePrefixes) unreadablePrefixes.add(unreadablePrefix);
|
|
20887
|
+
}
|
|
20888
|
+
return { entries, unreadablePrefixes };
|
|
20889
|
+
}
|
|
20890
|
+
getScopes(invalidatedPaths) {
|
|
20891
|
+
const uniquePaths = [...new Set(invalidatedPaths)].sort((left, right) => left.length - right.length);
|
|
20892
|
+
return uniquePaths.filter((candidate, index) => !uniquePaths.slice(0, index).some(
|
|
20893
|
+
(ancestor) => isWithinPath(ancestor, candidate)
|
|
20894
|
+
));
|
|
20895
|
+
}
|
|
20896
|
+
};
|
|
20897
|
+
|
|
20898
|
+
// src/watcher/file-watcher.ts
|
|
19950
20899
|
var FileWatcher = class {
|
|
19951
20900
|
watcher = null;
|
|
19952
20901
|
projectRoot;
|
|
19953
20902
|
config;
|
|
19954
20903
|
configPath;
|
|
20904
|
+
backend;
|
|
19955
20905
|
projectConfigPaths;
|
|
19956
20906
|
pendingChanges = /* @__PURE__ */ new Map();
|
|
19957
20907
|
debounceTimer = null;
|
|
@@ -19961,44 +20911,74 @@ var FileWatcher = class {
|
|
|
19961
20911
|
resolveReady = null;
|
|
19962
20912
|
pollingFallbackAttempted = false;
|
|
19963
20913
|
pendingClose = null;
|
|
20914
|
+
startupReadySignals = 1;
|
|
20915
|
+
nativeWatcher = null;
|
|
20916
|
+
nativeReconciler = null;
|
|
20917
|
+
nativeSetupGeneration = 0;
|
|
20918
|
+
nativeStarting = false;
|
|
20919
|
+
nativeInitializing = false;
|
|
20920
|
+
nativeReconcileTimer = null;
|
|
20921
|
+
nativeInvalidatedPaths = /* @__PURE__ */ new Map();
|
|
20922
|
+
configPathStates = /* @__PURE__ */ new Map();
|
|
19964
20923
|
constructor(projectRoot, config, host, options = {}) {
|
|
19965
20924
|
this.projectRoot = projectRoot;
|
|
19966
20925
|
this.config = config;
|
|
20926
|
+
this.backend = options.backend ?? "auto";
|
|
19967
20927
|
this.configPath = options.configPath;
|
|
19968
20928
|
this.projectConfigPaths = options.configPath ? [options.configPath] : getProjectConfigCandidatePaths(projectRoot, host);
|
|
19969
20929
|
}
|
|
19970
20930
|
start(handler) {
|
|
19971
|
-
if (this.watcher) {
|
|
20931
|
+
if (this.watcher || this.nativeWatcher || this.nativeStarting) {
|
|
19972
20932
|
return;
|
|
19973
20933
|
}
|
|
19974
20934
|
this.onChanges = handler;
|
|
19975
20935
|
this.pollingFallbackAttempted = false;
|
|
19976
20936
|
this.resetReady();
|
|
20937
|
+
if (this.shouldUseNativeWatcher()) {
|
|
20938
|
+
if (this.hasExternalConfigWatchTarget()) {
|
|
20939
|
+
this.setStartupReadySignals(2);
|
|
20940
|
+
this.startExternalConfigWatcher();
|
|
20941
|
+
}
|
|
20942
|
+
this.nativeStarting = true;
|
|
20943
|
+
void this.createNativeWatcher();
|
|
20944
|
+
return;
|
|
20945
|
+
}
|
|
19977
20946
|
this.createWatcher();
|
|
19978
20947
|
}
|
|
19979
20948
|
resetReady() {
|
|
19980
|
-
this.readyPromise = new Promise((
|
|
19981
|
-
this.resolveReady =
|
|
20949
|
+
this.readyPromise = new Promise((resolve20) => {
|
|
20950
|
+
this.resolveReady = resolve20;
|
|
19982
20951
|
});
|
|
20952
|
+
this.startupReadySignals = 1;
|
|
19983
20953
|
}
|
|
19984
|
-
|
|
19985
|
-
|
|
19986
|
-
|
|
19987
|
-
if (this.configPath) {
|
|
19988
|
-
watchTargets = [this.projectRoot, this.configPath];
|
|
19989
|
-
} else {
|
|
19990
|
-
const externalConfigTargets = this.projectConfigPaths.filter((projectConfigPath) => {
|
|
19991
|
-
const relativeConfigPath = path26.relative(this.projectRoot, projectConfigPath);
|
|
19992
|
-
return this.isOutsideProjectPath(relativeConfigPath);
|
|
19993
|
-
}).map((projectConfigPath) => (0, import_fs19.existsSync)(projectConfigPath) ? projectConfigPath : this.getNearestExistingDirectory(path26.dirname(projectConfigPath)));
|
|
19994
|
-
const uniqueExternalConfigTargets = [...new Set(externalConfigTargets)];
|
|
19995
|
-
if (uniqueExternalConfigTargets.length > 0) {
|
|
19996
|
-
watchTargets = [this.projectRoot, ...uniqueExternalConfigTargets];
|
|
19997
|
-
}
|
|
20954
|
+
setStartupReadySignals(expectedSignals) {
|
|
20955
|
+
if (!this.readyPromise) {
|
|
20956
|
+
return;
|
|
19998
20957
|
}
|
|
20958
|
+
this.startupReadySignals = Math.max(0, expectedSignals);
|
|
20959
|
+
}
|
|
20960
|
+
reportStartupReadySignal() {
|
|
20961
|
+
if (!this.readyPromise || !this.resolveReady) {
|
|
20962
|
+
return;
|
|
20963
|
+
}
|
|
20964
|
+
if (this.startupReadySignals <= 0) {
|
|
20965
|
+
return;
|
|
20966
|
+
}
|
|
20967
|
+
this.startupReadySignals -= 1;
|
|
20968
|
+
if (this.startupReadySignals !== 0) {
|
|
20969
|
+
return;
|
|
20970
|
+
}
|
|
20971
|
+
this.resolveReady();
|
|
20972
|
+
this.resolveReady = null;
|
|
20973
|
+
}
|
|
20974
|
+
createWatcher(watchTargets, usePolling = false, reportsStartupReady = true) {
|
|
20975
|
+
let reportedStartupReady = false;
|
|
20976
|
+
this.configPathStates = this.getConfigPathStates();
|
|
20977
|
+
const ignoreFilter = createIgnoreFilter(this.projectRoot);
|
|
20978
|
+
const resolvedWatchTargets = watchTargets ?? this.getFullChokidarWatchTargets();
|
|
19999
20979
|
const watcherOptions = {
|
|
20000
20980
|
ignored: (filePath) => {
|
|
20001
|
-
const relativePath =
|
|
20981
|
+
const relativePath = path28.relative(this.projectRoot, filePath);
|
|
20002
20982
|
if (!relativePath) return false;
|
|
20003
20983
|
if (this.isProjectConfigPathOrAncestor(relativePath)) {
|
|
20004
20984
|
return false;
|
|
@@ -20006,10 +20986,10 @@ var FileWatcher = class {
|
|
|
20006
20986
|
if (this.isOutsideProjectPath(relativePath)) {
|
|
20007
20987
|
return true;
|
|
20008
20988
|
}
|
|
20009
|
-
if (hasFilteredPathSegment(relativePath,
|
|
20989
|
+
if (hasFilteredPathSegment(relativePath, path28.sep)) {
|
|
20010
20990
|
return true;
|
|
20011
20991
|
}
|
|
20012
|
-
if (isRestrictedDirectory(relativePath,
|
|
20992
|
+
if (isRestrictedDirectory(relativePath, path28.sep)) {
|
|
20013
20993
|
return true;
|
|
20014
20994
|
}
|
|
20015
20995
|
if (ignoreFilter.ignores(relativePath)) {
|
|
@@ -20042,10 +21022,13 @@ var FileWatcher = class {
|
|
|
20042
21022
|
watcher = new FSWatcher(watcherOptions);
|
|
20043
21023
|
}
|
|
20044
21024
|
this.watcher = watcher;
|
|
20045
|
-
watcher.
|
|
21025
|
+
watcher.on("ready", () => {
|
|
20046
21026
|
if (this.watcher !== watcher) return;
|
|
20047
|
-
this.
|
|
20048
|
-
|
|
21027
|
+
this.reconcileConfigPathStates();
|
|
21028
|
+
if (reportsStartupReady) {
|
|
21029
|
+
this.reportStartupReadySignal();
|
|
21030
|
+
reportedStartupReady = true;
|
|
21031
|
+
}
|
|
20049
21032
|
});
|
|
20050
21033
|
watcher.on("error", (error) => {
|
|
20051
21034
|
const err = error instanceof Error ? error : null;
|
|
@@ -20059,10 +21042,13 @@ var FileWatcher = class {
|
|
|
20059
21042
|
console.error("[codebase-index] Failed to close exhausted file watcher:", closeError);
|
|
20060
21043
|
});
|
|
20061
21044
|
if (this.onChanges) {
|
|
21045
|
+
const replacementReportsStartupReady = reportsStartupReady || reportedStartupReady;
|
|
20062
21046
|
if (!this.resolveReady) {
|
|
20063
21047
|
this.resetReady();
|
|
21048
|
+
} else if (reportedStartupReady) {
|
|
21049
|
+
this.startupReadySignals += 1;
|
|
20064
21050
|
}
|
|
20065
|
-
this.createWatcher(true);
|
|
21051
|
+
this.createWatcher(resolvedWatchTargets, true, replacementReportsStartupReady);
|
|
20066
21052
|
} else {
|
|
20067
21053
|
this.watcher = null;
|
|
20068
21054
|
}
|
|
@@ -20073,13 +21059,166 @@ var FileWatcher = class {
|
|
|
20073
21059
|
watcher.on("add", (filePath) => this.handleChange(watcher, "add", filePath));
|
|
20074
21060
|
watcher.on("change", (filePath) => this.handleChange(watcher, "change", filePath));
|
|
20075
21061
|
watcher.on("unlink", (filePath) => this.handleChange(watcher, "unlink", filePath));
|
|
20076
|
-
watcher.add(
|
|
21062
|
+
watcher.add(resolvedWatchTargets);
|
|
21063
|
+
}
|
|
21064
|
+
shouldUseNativeWatcher() {
|
|
21065
|
+
if (this.backend === "chokidar") {
|
|
21066
|
+
return false;
|
|
21067
|
+
}
|
|
21068
|
+
return true;
|
|
21069
|
+
}
|
|
21070
|
+
getFullChokidarWatchTargets() {
|
|
21071
|
+
if (this.configPath) {
|
|
21072
|
+
return [this.projectRoot, this.configPath];
|
|
21073
|
+
}
|
|
21074
|
+
const externalConfigTargets = this.getExternalConfigWatchTargets();
|
|
21075
|
+
if (externalConfigTargets.length === 0) {
|
|
21076
|
+
return this.projectRoot;
|
|
21077
|
+
}
|
|
21078
|
+
return [this.projectRoot, ...externalConfigTargets];
|
|
21079
|
+
}
|
|
21080
|
+
getExternalConfigWatchTargets() {
|
|
21081
|
+
return [...new Set(
|
|
21082
|
+
this.projectConfigPaths.filter((projectConfigPath) => {
|
|
21083
|
+
const relativeConfigPath = path28.relative(this.projectRoot, projectConfigPath);
|
|
21084
|
+
return this.isOutsideProjectPath(relativeConfigPath);
|
|
21085
|
+
}).map((projectConfigPath) => {
|
|
21086
|
+
if ((0, import_fs19.existsSync)(projectConfigPath)) {
|
|
21087
|
+
return projectConfigPath;
|
|
21088
|
+
}
|
|
21089
|
+
return this.getNearestExistingDirectory(path28.dirname(projectConfigPath));
|
|
21090
|
+
})
|
|
21091
|
+
)];
|
|
21092
|
+
}
|
|
21093
|
+
hasExternalConfigWatchTarget() {
|
|
21094
|
+
return this.getExternalConfigWatchTargets().length > 0;
|
|
21095
|
+
}
|
|
21096
|
+
startExternalConfigWatcher(usePolling = false) {
|
|
21097
|
+
const externalTargets = this.getExternalConfigWatchTargets();
|
|
21098
|
+
if (externalTargets.length === 0) {
|
|
21099
|
+
return;
|
|
21100
|
+
}
|
|
21101
|
+
this.createWatcher(externalTargets, usePolling);
|
|
21102
|
+
}
|
|
21103
|
+
async createNativeWatcher() {
|
|
21104
|
+
const generation = ++this.nativeSetupGeneration;
|
|
21105
|
+
const reconciler = new FileSnapshotReconciler(this.projectRoot, this.config, this.projectConfigPaths);
|
|
21106
|
+
const watcher = new NativeRecursiveWatcher(
|
|
21107
|
+
this.projectRoot,
|
|
21108
|
+
(filePath) => this.scheduleNativeReconciliation(generation, filePath),
|
|
21109
|
+
{ onError: (error) => void this.fallbackFromNativeWatcher(generation, error) }
|
|
21110
|
+
);
|
|
21111
|
+
this.nativeReconciler = reconciler;
|
|
21112
|
+
this.nativeWatcher = watcher;
|
|
21113
|
+
this.nativeInitializing = true;
|
|
21114
|
+
try {
|
|
21115
|
+
watcher.start();
|
|
21116
|
+
if (!this.isCurrentNativeSetup(generation)) {
|
|
21117
|
+
await watcher.stop();
|
|
21118
|
+
return;
|
|
21119
|
+
}
|
|
21120
|
+
await reconciler.initialize();
|
|
21121
|
+
if (!this.isCurrentNativeSetup(generation) || this.nativeWatcher !== watcher) {
|
|
21122
|
+
await watcher.stop();
|
|
21123
|
+
return;
|
|
21124
|
+
}
|
|
21125
|
+
this.nativeStarting = false;
|
|
21126
|
+
this.nativeInitializing = false;
|
|
21127
|
+
await this.reconcileNativeWatcherWithPendingInvalidations(generation);
|
|
21128
|
+
this.reportStartupReadySignal();
|
|
21129
|
+
} catch (error) {
|
|
21130
|
+
if (!this.isCurrentNativeSetup(generation)) return;
|
|
21131
|
+
this.nativeInitializing = false;
|
|
21132
|
+
if (this.nativeWatcher) {
|
|
21133
|
+
await this.fallbackFromNativeWatcher(generation, error);
|
|
21134
|
+
return;
|
|
21135
|
+
}
|
|
21136
|
+
this.nativeStarting = false;
|
|
21137
|
+
const externalWatcher = this.watcher;
|
|
21138
|
+
this.watcher = null;
|
|
21139
|
+
this.nativeReconciler = null;
|
|
21140
|
+
await externalWatcher?.close();
|
|
21141
|
+
console.warn("[codebase-index] Native recursive watcher unavailable; using Chokidar fallback.", error);
|
|
21142
|
+
this.setStartupReadySignals(1);
|
|
21143
|
+
this.createWatcher();
|
|
21144
|
+
}
|
|
21145
|
+
}
|
|
21146
|
+
isCurrentNativeSetup(generation) {
|
|
21147
|
+
return this.nativeSetupGeneration === generation && this.onChanges !== null;
|
|
21148
|
+
}
|
|
21149
|
+
scheduleNativeReconciliation(generation, filePath) {
|
|
21150
|
+
if (!this.isCurrentNativeSetup(generation)) return;
|
|
21151
|
+
const requiresFullReconciliation = filePath === path28.join(this.projectRoot, ".gitignore");
|
|
21152
|
+
const invalidatedPath = requiresFullReconciliation ? null : filePath;
|
|
21153
|
+
this.nativeInvalidatedPaths.set(invalidatedPath, invalidatedPath !== null);
|
|
21154
|
+
if (this.nativeReconcileTimer) {
|
|
21155
|
+
clearTimeout(this.nativeReconcileTimer);
|
|
21156
|
+
}
|
|
21157
|
+
this.nativeReconcileTimer = setTimeout(() => {
|
|
21158
|
+
this.nativeReconcileTimer = null;
|
|
21159
|
+
void this.reconcileNativeWatcherFromQueue(generation);
|
|
21160
|
+
}, 100);
|
|
21161
|
+
}
|
|
21162
|
+
reconcileNativeWatcherFromQueue(generation) {
|
|
21163
|
+
if (!this.isCurrentNativeSetup(generation) || this.nativeInitializing) return;
|
|
21164
|
+
const invalidatedPaths = this.popNativeInvalidations();
|
|
21165
|
+
if (invalidatedPaths.length === 0) return;
|
|
21166
|
+
void this.reconcileNativeWatcher(generation, invalidatedPaths);
|
|
21167
|
+
}
|
|
21168
|
+
async reconcileNativeWatcher(generation, invalidatedPaths) {
|
|
21169
|
+
if (!this.isCurrentNativeSetup(generation) || !this.nativeReconciler) return;
|
|
21170
|
+
try {
|
|
21171
|
+
const reconciler = this.nativeReconciler;
|
|
21172
|
+
const changes = await reconciler.reconcile(invalidatedPaths);
|
|
21173
|
+
if (!this.isCurrentNativeSetup(generation) || this.nativeReconciler !== reconciler) return;
|
|
21174
|
+
this.recordChanges(changes);
|
|
21175
|
+
} catch (error) {
|
|
21176
|
+
await this.fallbackFromNativeWatcher(generation, error);
|
|
21177
|
+
}
|
|
21178
|
+
}
|
|
21179
|
+
async reconcileNativeWatcherWithPendingInvalidations(generation) {
|
|
21180
|
+
const invalidatedPaths = this.popNativeInvalidations();
|
|
21181
|
+
if (invalidatedPaths.length === 0) return;
|
|
21182
|
+
await this.reconcileNativeWatcher(generation, invalidatedPaths);
|
|
21183
|
+
}
|
|
21184
|
+
popNativeInvalidations() {
|
|
21185
|
+
if (this.nativeInvalidatedPaths.size === 0) return [];
|
|
21186
|
+
const invalidations = [...this.nativeInvalidatedPaths].map(([invalidatedPath, forceChange]) => ({
|
|
21187
|
+
path: invalidatedPath,
|
|
21188
|
+
forceChange
|
|
21189
|
+
}));
|
|
21190
|
+
this.nativeInvalidatedPaths.clear();
|
|
21191
|
+
return invalidations;
|
|
21192
|
+
}
|
|
21193
|
+
async fallbackFromNativeWatcher(generation, error) {
|
|
21194
|
+
if (!this.isCurrentNativeSetup(generation)) return;
|
|
21195
|
+
const watcher = this.nativeWatcher;
|
|
21196
|
+
const externalWatcher = this.watcher;
|
|
21197
|
+
this.nativeWatcher = null;
|
|
21198
|
+
this.watcher = null;
|
|
21199
|
+
this.nativeReconciler = null;
|
|
21200
|
+
this.nativeStarting = false;
|
|
21201
|
+
this.nativeInitializing = false;
|
|
21202
|
+
this.nativeSetupGeneration += 1;
|
|
21203
|
+
if (this.nativeReconcileTimer) {
|
|
21204
|
+
clearTimeout(this.nativeReconcileTimer);
|
|
21205
|
+
this.nativeReconcileTimer = null;
|
|
21206
|
+
}
|
|
21207
|
+
this.nativeInvalidatedPaths.clear();
|
|
21208
|
+
this.setStartupReadySignals(1);
|
|
21209
|
+
console.warn("[codebase-index] Native recursive watcher failed; using Chokidar fallback.", error);
|
|
21210
|
+
await watcher?.stop();
|
|
21211
|
+
await externalWatcher?.close();
|
|
21212
|
+
if (this.onChanges) {
|
|
21213
|
+
this.createWatcher();
|
|
21214
|
+
}
|
|
20077
21215
|
}
|
|
20078
21216
|
handleChange(watcher, type, filePath) {
|
|
20079
21217
|
if (this.watcher !== watcher) {
|
|
20080
21218
|
return;
|
|
20081
21219
|
}
|
|
20082
21220
|
if (this.isProjectConfigPath(filePath)) {
|
|
21221
|
+
this.updateConfigPathState(filePath);
|
|
20083
21222
|
this.pendingChanges.set(filePath, type);
|
|
20084
21223
|
this.scheduleFlush();
|
|
20085
21224
|
return;
|
|
@@ -20094,27 +21233,33 @@ var FileWatcher = class {
|
|
|
20094
21233
|
)) {
|
|
20095
21234
|
return;
|
|
20096
21235
|
}
|
|
20097
|
-
this.
|
|
21236
|
+
this.recordChanges([{ path: filePath, type }]);
|
|
21237
|
+
}
|
|
21238
|
+
recordChanges(changes) {
|
|
21239
|
+
if (changes.length === 0) return;
|
|
21240
|
+
for (const change of changes) {
|
|
21241
|
+
this.pendingChanges.set(change.path, change.type);
|
|
21242
|
+
}
|
|
20098
21243
|
this.scheduleFlush();
|
|
20099
21244
|
}
|
|
20100
21245
|
isProjectConfigPath(filePath) {
|
|
20101
|
-
const relativePath =
|
|
20102
|
-
const normalizedRelativePath =
|
|
21246
|
+
const relativePath = path28.relative(this.projectRoot, filePath);
|
|
21247
|
+
const normalizedRelativePath = path28.normalize(relativePath);
|
|
20103
21248
|
return this.getProjectConfigRelativePaths().some((configPath) => configPath === normalizedRelativePath);
|
|
20104
21249
|
}
|
|
20105
21250
|
isProjectConfigPathOrAncestor(relativePath) {
|
|
20106
|
-
const normalizedRelativePath =
|
|
21251
|
+
const normalizedRelativePath = path28.normalize(relativePath);
|
|
20107
21252
|
return this.getProjectConfigRelativePaths().some(
|
|
20108
|
-
(configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${
|
|
21253
|
+
(configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path28.sep}`)
|
|
20109
21254
|
);
|
|
20110
21255
|
}
|
|
20111
21256
|
isOutsideProjectPath(relativePath) {
|
|
20112
|
-
return relativePath === ".." || relativePath.startsWith(`..${
|
|
21257
|
+
return relativePath === ".." || relativePath.startsWith(`..${path28.sep}`) || path28.isAbsolute(relativePath);
|
|
20113
21258
|
}
|
|
20114
21259
|
getNearestExistingDirectory(directoryPath) {
|
|
20115
21260
|
let candidate = directoryPath;
|
|
20116
21261
|
while (!(0, import_fs19.existsSync)(candidate)) {
|
|
20117
|
-
const parent =
|
|
21262
|
+
const parent = path28.dirname(candidate);
|
|
20118
21263
|
if (parent === candidate) break;
|
|
20119
21264
|
candidate = parent;
|
|
20120
21265
|
}
|
|
@@ -20122,9 +21267,51 @@ var FileWatcher = class {
|
|
|
20122
21267
|
}
|
|
20123
21268
|
getProjectConfigRelativePaths() {
|
|
20124
21269
|
return this.projectConfigPaths.map(
|
|
20125
|
-
(configPath) =>
|
|
21270
|
+
(configPath) => path28.normalize(path28.relative(this.projectRoot, configPath))
|
|
20126
21271
|
);
|
|
20127
21272
|
}
|
|
21273
|
+
getConfigPathStates() {
|
|
21274
|
+
const states = /* @__PURE__ */ new Map();
|
|
21275
|
+
for (const configPath of this.projectConfigPaths) {
|
|
21276
|
+
const state = this.getConfigPathState(configPath);
|
|
21277
|
+
if (state) states.set(configPath, state);
|
|
21278
|
+
}
|
|
21279
|
+
return states;
|
|
21280
|
+
}
|
|
21281
|
+
getConfigPathState(configPath) {
|
|
21282
|
+
try {
|
|
21283
|
+
const stats = (0, import_fs19.statSync)(configPath);
|
|
21284
|
+
return stats.isFile() ? { mtimeMs: stats.mtimeMs, size: stats.size } : void 0;
|
|
21285
|
+
} catch (error) {
|
|
21286
|
+
void error;
|
|
21287
|
+
return void 0;
|
|
21288
|
+
}
|
|
21289
|
+
}
|
|
21290
|
+
updateConfigPathState(configPath) {
|
|
21291
|
+
const state = this.getConfigPathState(configPath);
|
|
21292
|
+
if (state) {
|
|
21293
|
+
this.configPathStates.set(configPath, state);
|
|
21294
|
+
} else {
|
|
21295
|
+
this.configPathStates.delete(configPath);
|
|
21296
|
+
}
|
|
21297
|
+
}
|
|
21298
|
+
reconcileConfigPathStates() {
|
|
21299
|
+
const nextStates = this.getConfigPathStates();
|
|
21300
|
+
const changes = [];
|
|
21301
|
+
for (const configPath of this.projectConfigPaths) {
|
|
21302
|
+
const previous = this.configPathStates.get(configPath);
|
|
21303
|
+
const next = nextStates.get(configPath);
|
|
21304
|
+
if (!previous && next) {
|
|
21305
|
+
changes.push({ path: configPath, type: "add" });
|
|
21306
|
+
} else if (previous && !next) {
|
|
21307
|
+
changes.push({ path: configPath, type: "unlink" });
|
|
21308
|
+
} else if (previous && next && (previous.size !== next.size || previous.mtimeMs !== next.mtimeMs)) {
|
|
21309
|
+
changes.push({ path: configPath, type: "change" });
|
|
21310
|
+
}
|
|
21311
|
+
}
|
|
21312
|
+
this.configPathStates = nextStates;
|
|
21313
|
+
this.recordChanges(changes);
|
|
21314
|
+
}
|
|
20128
21315
|
scheduleFlush() {
|
|
20129
21316
|
if (this.debounceTimer) {
|
|
20130
21317
|
clearTimeout(this.debounceTimer);
|
|
@@ -20138,7 +21325,7 @@ var FileWatcher = class {
|
|
|
20138
21325
|
return;
|
|
20139
21326
|
}
|
|
20140
21327
|
const changes = Array.from(this.pendingChanges.entries()).map(
|
|
20141
|
-
([
|
|
21328
|
+
([path33, type]) => ({ path: path33, type })
|
|
20142
21329
|
);
|
|
20143
21330
|
this.pendingChanges.clear();
|
|
20144
21331
|
try {
|
|
@@ -20152,20 +21339,31 @@ var FileWatcher = class {
|
|
|
20152
21339
|
clearTimeout(this.debounceTimer);
|
|
20153
21340
|
this.debounceTimer = null;
|
|
20154
21341
|
}
|
|
21342
|
+
if (this.nativeReconcileTimer) {
|
|
21343
|
+
clearTimeout(this.nativeReconcileTimer);
|
|
21344
|
+
this.nativeReconcileTimer = null;
|
|
21345
|
+
}
|
|
21346
|
+
this.nativeInvalidatedPaths.clear();
|
|
20155
21347
|
const watcher = this.watcher;
|
|
21348
|
+
const nativeWatcher = this.nativeWatcher;
|
|
20156
21349
|
const pendingClose = this.pendingClose;
|
|
20157
21350
|
const resolveReady = this.resolveReady;
|
|
20158
21351
|
this.watcher = null;
|
|
21352
|
+
this.nativeWatcher = null;
|
|
21353
|
+
this.nativeReconciler = null;
|
|
21354
|
+
this.nativeStarting = false;
|
|
21355
|
+
this.nativeInitializing = false;
|
|
21356
|
+
this.nativeSetupGeneration += 1;
|
|
20159
21357
|
this.pendingClose = null;
|
|
20160
21358
|
this.resolveReady = null;
|
|
20161
21359
|
this.readyPromise = null;
|
|
20162
21360
|
this.pendingChanges.clear();
|
|
20163
21361
|
this.onChanges = null;
|
|
20164
|
-
await Promise.all([watcher?.close(), pendingClose]);
|
|
21362
|
+
await Promise.all([watcher?.close(), nativeWatcher?.stop(), pendingClose]);
|
|
20165
21363
|
resolveReady?.();
|
|
20166
21364
|
}
|
|
20167
21365
|
isRunning() {
|
|
20168
|
-
return this.watcher !== null;
|
|
21366
|
+
return this.watcher !== null || this.nativeWatcher !== null || this.nativeStarting;
|
|
20169
21367
|
}
|
|
20170
21368
|
async waitUntilReady() {
|
|
20171
21369
|
await (this.readyPromise ?? Promise.resolve());
|
|
@@ -20173,7 +21371,7 @@ var FileWatcher = class {
|
|
|
20173
21371
|
};
|
|
20174
21372
|
|
|
20175
21373
|
// src/watcher/git-head-watcher.ts
|
|
20176
|
-
var
|
|
21374
|
+
var path29 = __toESM(require("path"), 1);
|
|
20177
21375
|
var GitHeadWatcher = class {
|
|
20178
21376
|
watcher = null;
|
|
20179
21377
|
projectRoot;
|
|
@@ -20195,13 +21393,13 @@ var GitHeadWatcher = class {
|
|
|
20195
21393
|
this.readyPromise = Promise.resolve();
|
|
20196
21394
|
return;
|
|
20197
21395
|
}
|
|
20198
|
-
this.readyPromise = new Promise((
|
|
20199
|
-
this.resolveReady =
|
|
21396
|
+
this.readyPromise = new Promise((resolve20) => {
|
|
21397
|
+
this.resolveReady = resolve20;
|
|
20200
21398
|
});
|
|
20201
21399
|
this.onBranchChange = handler;
|
|
20202
21400
|
this.currentBranch = getCurrentBranch(this.projectRoot);
|
|
20203
21401
|
const headPath = getHeadPath(this.projectRoot);
|
|
20204
|
-
const refsPath =
|
|
21402
|
+
const refsPath = path29.join(this.projectRoot, ".git", "refs", "heads");
|
|
20205
21403
|
this.watcher = chokidar_default.watch([headPath, refsPath], {
|
|
20206
21404
|
persistent: true,
|
|
20207
21405
|
ignoreInitial: true,
|
|
@@ -20337,7 +21535,7 @@ function getConfigPaths(projectRoot, host, options) {
|
|
|
20337
21535
|
|
|
20338
21536
|
// src/tools/visualize/activity.ts
|
|
20339
21537
|
var import_child_process5 = require("child_process");
|
|
20340
|
-
var
|
|
21538
|
+
var path30 = __toESM(require("path"), 1);
|
|
20341
21539
|
function attachRecentActivity(data, projectRoot) {
|
|
20342
21540
|
const activity = readGitActivity(projectRoot);
|
|
20343
21541
|
const changes = activity.size > 0 ? buildGitChanges(data, activity, projectRoot) : buildGraphChanges(data);
|
|
@@ -20499,7 +21697,7 @@ function normalizePath4(filePath) {
|
|
|
20499
21697
|
return filePath.replace(/\\/g, "/");
|
|
20500
21698
|
}
|
|
20501
21699
|
function toGitRelativePath(projectRoot, filePath) {
|
|
20502
|
-
const relativePath =
|
|
21700
|
+
const relativePath = path30.isAbsolute(filePath) ? path30.relative(projectRoot, filePath) : filePath;
|
|
20503
21701
|
return normalizePath4(relativePath);
|
|
20504
21702
|
}
|
|
20505
21703
|
|
|
@@ -20757,7 +21955,7 @@ render();
|
|
|
20757
21955
|
}
|
|
20758
21956
|
|
|
20759
21957
|
// src/tools/visualize/transform.ts
|
|
20760
|
-
var
|
|
21958
|
+
var path31 = __toESM(require("path"), 1);
|
|
20761
21959
|
|
|
20762
21960
|
// src/tools/visualize/modules.ts
|
|
20763
21961
|
var MAX_MODULES = 18;
|
|
@@ -20890,8 +22088,8 @@ function compactModules(prefixToNodes) {
|
|
|
20890
22088
|
function deriveModules(nodes) {
|
|
20891
22089
|
const initial = /* @__PURE__ */ new Map();
|
|
20892
22090
|
for (const node of nodes) {
|
|
20893
|
-
const
|
|
20894
|
-
const prefix = modulePrefixFromRelativePath(
|
|
22091
|
+
const relative14 = stripToProjectRelative(node.filePath);
|
|
22092
|
+
const prefix = modulePrefixFromRelativePath(relative14);
|
|
20895
22093
|
if (!initial.has(prefix)) initial.set(prefix, []);
|
|
20896
22094
|
initial.get(prefix)?.push(node);
|
|
20897
22095
|
}
|
|
@@ -21017,7 +22215,7 @@ function transformForVisualization(symbols, edges, options = {}) {
|
|
|
21017
22215
|
filePath: s.filePath,
|
|
21018
22216
|
kind: s.kind,
|
|
21019
22217
|
line: s.startLine,
|
|
21020
|
-
directory:
|
|
22218
|
+
directory: path31.dirname(s.filePath),
|
|
21021
22219
|
moduleId: "",
|
|
21022
22220
|
moduleLabel: ""
|
|
21023
22221
|
}));
|
|
@@ -21045,9 +22243,9 @@ function parseArgs(argv) {
|
|
|
21045
22243
|
let host = "opencode";
|
|
21046
22244
|
for (let i = 2; i < argv.length; i++) {
|
|
21047
22245
|
if (argv[i] === "--project" && argv[i + 1]) {
|
|
21048
|
-
project =
|
|
22246
|
+
project = path32.resolve(argv[++i]);
|
|
21049
22247
|
} else if (argv[i] === "--config" && argv[i + 1]) {
|
|
21050
|
-
config =
|
|
22248
|
+
config = path32.resolve(argv[++i]);
|
|
21051
22249
|
} else if (argv[i] === "--host" && argv[i + 1]) {
|
|
21052
22250
|
host = parseHostMode(argv[++i]);
|
|
21053
22251
|
} else if (argv[i] === "--host") {
|
|
@@ -21074,7 +22272,7 @@ function parseIndexArgs(argv, cwd) {
|
|
|
21074
22272
|
if (!arg.startsWith("--project=")) {
|
|
21075
22273
|
i += 1;
|
|
21076
22274
|
}
|
|
21077
|
-
project =
|
|
22275
|
+
project = path32.resolve(cwd, value);
|
|
21078
22276
|
continue;
|
|
21079
22277
|
}
|
|
21080
22278
|
if (arg === "--config" || arg.startsWith("--config=")) {
|
|
@@ -21085,7 +22283,7 @@ function parseIndexArgs(argv, cwd) {
|
|
|
21085
22283
|
if (!arg.startsWith("--config=")) {
|
|
21086
22284
|
i += 1;
|
|
21087
22285
|
}
|
|
21088
|
-
config =
|
|
22286
|
+
config = path32.resolve(cwd, value);
|
|
21089
22287
|
continue;
|
|
21090
22288
|
}
|
|
21091
22289
|
if (arg === "--host" || arg.startsWith("--host=")) {
|
|
@@ -21151,7 +22349,7 @@ function parseVisualizeArgs(argv, cwd) {
|
|
|
21151
22349
|
for (let i = 0; i < argv.length; i++) {
|
|
21152
22350
|
const arg = argv[i];
|
|
21153
22351
|
if (arg === "--project" && argv[i + 1]) {
|
|
21154
|
-
project =
|
|
22352
|
+
project = path32.resolve(argv[++i]);
|
|
21155
22353
|
} else if (arg === "--max" && argv[i + 1]) {
|
|
21156
22354
|
maxNodes = Number(argv[++i]);
|
|
21157
22355
|
} else if (arg.startsWith("--max=") || arg.startsWith("max=")) {
|
|
@@ -21188,7 +22386,7 @@ async function handleVisualizeCommand(argv, cwd) {
|
|
|
21188
22386
|
console.error("No connected symbols found. Retry with: npm run visualize -- orphans");
|
|
21189
22387
|
return 1;
|
|
21190
22388
|
}
|
|
21191
|
-
const outputPath =
|
|
22389
|
+
const outputPath = path32.join(os8.tmpdir(), `call-graph-${Date.now()}.html`);
|
|
21192
22390
|
(0, import_fs20.writeFileSync)(outputPath, generateVisualizationHtml(vizData), "utf-8");
|
|
21193
22391
|
console.log(`Temporal call graph visualization generated: ${outputPath}`);
|
|
21194
22392
|
console.log(`Nodes: ${vizData.nodes.length} | Edges: ${vizData.edges.length}`);
|
|
@@ -21220,8 +22418,60 @@ async function runMcpCli(argv) {
|
|
|
21220
22418
|
const config = parseConfig(rawConfig);
|
|
21221
22419
|
const server = createMcpServer(args.project, config, args.host);
|
|
21222
22420
|
const transport = new import_stdio.StdioServerTransport();
|
|
21223
|
-
await server.connect(transport);
|
|
21224
22421
|
let watcher = null;
|
|
22422
|
+
let shutdownPromise;
|
|
22423
|
+
const onServerClose = server.server.onclose;
|
|
22424
|
+
const shutdown = () => {
|
|
22425
|
+
if (shutdownPromise) return shutdownPromise;
|
|
22426
|
+
process.stdin.removeListener("end", requestShutdown);
|
|
22427
|
+
process.stdin.removeListener("close", requestShutdown);
|
|
22428
|
+
process.removeListener("SIGHUP", requestShutdown);
|
|
22429
|
+
process.removeListener("SIGINT", requestShutdown);
|
|
22430
|
+
process.removeListener("SIGTERM", requestShutdown);
|
|
22431
|
+
server.server.onclose = onServerClose;
|
|
22432
|
+
shutdownPromise = (async () => {
|
|
22433
|
+
let exitCode = 0;
|
|
22434
|
+
try {
|
|
22435
|
+
await watcher?.stop();
|
|
22436
|
+
} catch (error) {
|
|
22437
|
+
exitCode = 1;
|
|
22438
|
+
console.error("Failed to stop MCP file watcher cleanly:", error);
|
|
22439
|
+
}
|
|
22440
|
+
try {
|
|
22441
|
+
await stopAutoIndex(args.project, args.host);
|
|
22442
|
+
} catch (error) {
|
|
22443
|
+
exitCode = 1;
|
|
22444
|
+
console.error("Failed to stop automatic indexing cleanly:", error);
|
|
22445
|
+
}
|
|
22446
|
+
try {
|
|
22447
|
+
await server.close();
|
|
22448
|
+
} catch (error) {
|
|
22449
|
+
exitCode = 1;
|
|
22450
|
+
console.error("Failed to close MCP server cleanly:", error);
|
|
22451
|
+
}
|
|
22452
|
+
process.exit(exitCode);
|
|
22453
|
+
})();
|
|
22454
|
+
return shutdownPromise;
|
|
22455
|
+
};
|
|
22456
|
+
const requestShutdown = () => {
|
|
22457
|
+
void shutdown();
|
|
22458
|
+
};
|
|
22459
|
+
server.server.onclose = () => {
|
|
22460
|
+
try {
|
|
22461
|
+
onServerClose?.();
|
|
22462
|
+
} finally {
|
|
22463
|
+
requestShutdown();
|
|
22464
|
+
}
|
|
22465
|
+
};
|
|
22466
|
+
process.stdin.once("end", requestShutdown);
|
|
22467
|
+
process.stdin.once("close", requestShutdown);
|
|
22468
|
+
process.once("SIGINT", requestShutdown);
|
|
22469
|
+
if (process.platform !== "win32") {
|
|
22470
|
+
process.once("SIGHUP", requestShutdown);
|
|
22471
|
+
process.once("SIGTERM", requestShutdown);
|
|
22472
|
+
}
|
|
22473
|
+
await server.connect(transport);
|
|
22474
|
+
if (shutdownPromise) return;
|
|
21225
22475
|
const isHomeDir = isHomeDirectory(args.project);
|
|
21226
22476
|
const isValidProject = !isHomeDir && (!config.indexing.requireProjectMarker || hasProjectMarker(args.project));
|
|
21227
22477
|
if (config.indexing.watchFiles && isValidProject) {
|
|
@@ -21233,26 +22483,6 @@ async function runMcpCli(argv) {
|
|
|
21233
22483
|
args.config ? { configPath: args.config } : {}
|
|
21234
22484
|
);
|
|
21235
22485
|
}
|
|
21236
|
-
let shuttingDown = false;
|
|
21237
|
-
const shutdown = async () => {
|
|
21238
|
-
if (shuttingDown) return;
|
|
21239
|
-
shuttingDown = true;
|
|
21240
|
-
try {
|
|
21241
|
-
await watcher?.stop();
|
|
21242
|
-
await stopAutoIndex(args.project, args.host);
|
|
21243
|
-
await server.close();
|
|
21244
|
-
process.exit(0);
|
|
21245
|
-
} catch (error) {
|
|
21246
|
-
console.error("Failed to stop MCP server cleanly:", error);
|
|
21247
|
-
process.exit(1);
|
|
21248
|
-
}
|
|
21249
|
-
};
|
|
21250
|
-
process.on("SIGINT", () => {
|
|
21251
|
-
void shutdown();
|
|
21252
|
-
});
|
|
21253
|
-
process.on("SIGTERM", () => {
|
|
21254
|
-
void shutdown();
|
|
21255
|
-
});
|
|
21256
22486
|
}
|
|
21257
22487
|
function printIndexProgress(onProgress, title, metadata) {
|
|
21258
22488
|
const details = Object.entries(metadata).filter(([, value]) => value !== void 0 && value !== null).map(([key, value]) => `${key}=${isSensitiveKey(key) ? "[REDACTED]" : String(value)}`).join(" ");
|