opencode-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.js
CHANGED
|
@@ -491,7 +491,7 @@ var require_ignore = __commonJS({
|
|
|
491
491
|
// path matching.
|
|
492
492
|
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
493
493
|
// @returns {TestResult} true if a file is ignored
|
|
494
|
-
test(
|
|
494
|
+
test(path33, checkUnignored, mode) {
|
|
495
495
|
let ignored = false;
|
|
496
496
|
let unignored = false;
|
|
497
497
|
let matchedRule;
|
|
@@ -500,7 +500,7 @@ var require_ignore = __commonJS({
|
|
|
500
500
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
501
501
|
return;
|
|
502
502
|
}
|
|
503
|
-
const matched = rule[mode].test(
|
|
503
|
+
const matched = rule[mode].test(path33);
|
|
504
504
|
if (!matched) {
|
|
505
505
|
return;
|
|
506
506
|
}
|
|
@@ -521,17 +521,17 @@ var require_ignore = __commonJS({
|
|
|
521
521
|
var throwError = (message, Ctor) => {
|
|
522
522
|
throw new Ctor(message);
|
|
523
523
|
};
|
|
524
|
-
var checkPath = (
|
|
525
|
-
if (!isString(
|
|
524
|
+
var checkPath = (path33, originalPath, doThrow) => {
|
|
525
|
+
if (!isString(path33)) {
|
|
526
526
|
return doThrow(
|
|
527
527
|
`path must be a string, but got \`${originalPath}\``,
|
|
528
528
|
TypeError
|
|
529
529
|
);
|
|
530
530
|
}
|
|
531
|
-
if (!
|
|
531
|
+
if (!path33) {
|
|
532
532
|
return doThrow(`path must not be empty`, TypeError);
|
|
533
533
|
}
|
|
534
|
-
if (checkPath.isNotRelative(
|
|
534
|
+
if (checkPath.isNotRelative(path33)) {
|
|
535
535
|
const r = "`path.relative()`d";
|
|
536
536
|
return doThrow(
|
|
537
537
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -540,7 +540,7 @@ var require_ignore = __commonJS({
|
|
|
540
540
|
}
|
|
541
541
|
return true;
|
|
542
542
|
};
|
|
543
|
-
var isNotRelative = (
|
|
543
|
+
var isNotRelative = (path33) => REGEX_TEST_INVALID_PATH.test(path33);
|
|
544
544
|
checkPath.isNotRelative = isNotRelative;
|
|
545
545
|
checkPath.convert = (p) => p;
|
|
546
546
|
var Ignore2 = class {
|
|
@@ -570,19 +570,19 @@ var require_ignore = __commonJS({
|
|
|
570
570
|
}
|
|
571
571
|
// @returns {TestResult}
|
|
572
572
|
_test(originalPath, cache, checkUnignored, slices) {
|
|
573
|
-
const
|
|
573
|
+
const path33 = originalPath && checkPath.convert(originalPath);
|
|
574
574
|
checkPath(
|
|
575
|
-
|
|
575
|
+
path33,
|
|
576
576
|
originalPath,
|
|
577
577
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
578
578
|
);
|
|
579
|
-
return this._t(
|
|
579
|
+
return this._t(path33, cache, checkUnignored, slices);
|
|
580
580
|
}
|
|
581
|
-
checkIgnore(
|
|
582
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
583
|
-
return this.test(
|
|
581
|
+
checkIgnore(path33) {
|
|
582
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path33)) {
|
|
583
|
+
return this.test(path33);
|
|
584
584
|
}
|
|
585
|
-
const slices =
|
|
585
|
+
const slices = path33.split(SLASH2).filter(Boolean);
|
|
586
586
|
slices.pop();
|
|
587
587
|
if (slices.length) {
|
|
588
588
|
const parent = this._t(
|
|
@@ -595,18 +595,18 @@ var require_ignore = __commonJS({
|
|
|
595
595
|
return parent;
|
|
596
596
|
}
|
|
597
597
|
}
|
|
598
|
-
return this._rules.test(
|
|
598
|
+
return this._rules.test(path33, false, MODE_CHECK_IGNORE);
|
|
599
599
|
}
|
|
600
|
-
_t(
|
|
601
|
-
if (
|
|
602
|
-
return cache[
|
|
600
|
+
_t(path33, cache, checkUnignored, slices) {
|
|
601
|
+
if (path33 in cache) {
|
|
602
|
+
return cache[path33];
|
|
603
603
|
}
|
|
604
604
|
if (!slices) {
|
|
605
|
-
slices =
|
|
605
|
+
slices = path33.split(SLASH2).filter(Boolean);
|
|
606
606
|
}
|
|
607
607
|
slices.pop();
|
|
608
608
|
if (!slices.length) {
|
|
609
|
-
return cache[
|
|
609
|
+
return cache[path33] = this._rules.test(path33, checkUnignored, MODE_IGNORE);
|
|
610
610
|
}
|
|
611
611
|
const parent = this._t(
|
|
612
612
|
slices.join(SLASH2) + SLASH2,
|
|
@@ -614,29 +614,29 @@ var require_ignore = __commonJS({
|
|
|
614
614
|
checkUnignored,
|
|
615
615
|
slices
|
|
616
616
|
);
|
|
617
|
-
return cache[
|
|
617
|
+
return cache[path33] = parent.ignored ? parent : this._rules.test(path33, checkUnignored, MODE_IGNORE);
|
|
618
618
|
}
|
|
619
|
-
ignores(
|
|
620
|
-
return this._test(
|
|
619
|
+
ignores(path33) {
|
|
620
|
+
return this._test(path33, this._ignoreCache, false).ignored;
|
|
621
621
|
}
|
|
622
622
|
createFilter() {
|
|
623
|
-
return (
|
|
623
|
+
return (path33) => !this.ignores(path33);
|
|
624
624
|
}
|
|
625
625
|
filter(paths) {
|
|
626
626
|
return makeArray(paths).filter(this.createFilter());
|
|
627
627
|
}
|
|
628
628
|
// @returns {TestResult}
|
|
629
|
-
test(
|
|
630
|
-
return this._test(
|
|
629
|
+
test(path33) {
|
|
630
|
+
return this._test(path33, this._testCache, true);
|
|
631
631
|
}
|
|
632
632
|
};
|
|
633
633
|
var factory = (options) => new Ignore2(options);
|
|
634
|
-
var isPathValid = (
|
|
634
|
+
var isPathValid = (path33) => checkPath(path33 && checkPath.convert(path33), path33, RETURN_FALSE);
|
|
635
635
|
var setupWindows = () => {
|
|
636
636
|
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
637
637
|
checkPath.convert = makePosix;
|
|
638
638
|
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
639
|
-
checkPath.isNotRelative = (
|
|
639
|
+
checkPath.isNotRelative = (path33) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path33) || isNotRelative(path33);
|
|
640
640
|
};
|
|
641
641
|
if (
|
|
642
642
|
// Detect `process` so that it can run in browsers.
|
|
@@ -655,7 +655,7 @@ var require_ignore = __commonJS({
|
|
|
655
655
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
656
656
|
import { realpathSync as realpathSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
657
657
|
import * as os8 from "os";
|
|
658
|
-
import * as
|
|
658
|
+
import * as path32 from "path";
|
|
659
659
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
660
660
|
|
|
661
661
|
// src/config/constants.ts
|
|
@@ -716,6 +716,17 @@ var EMBEDDING_MODELS = {
|
|
|
716
716
|
maxTokens: 2048,
|
|
717
717
|
costPer1MTokens: 0.15,
|
|
718
718
|
taskAble: true
|
|
719
|
+
},
|
|
720
|
+
"gemini-embedding-2": {
|
|
721
|
+
provider: "google",
|
|
722
|
+
model: "gemini-embedding-2",
|
|
723
|
+
// Keep a conservative, testable default embedding dimension. Gemini Embedding 2 supports
|
|
724
|
+
// flexible dimensions via outputDimensionality.
|
|
725
|
+
dimensions: 1536,
|
|
726
|
+
maxTokens: 8192,
|
|
727
|
+
costPer1MTokens: 0.15,
|
|
728
|
+
taskAble: false,
|
|
729
|
+
promptStyle: "embedding-2"
|
|
719
730
|
}
|
|
720
731
|
},
|
|
721
732
|
"openai": {
|
|
@@ -749,26 +760,15 @@ var EMBEDDING_MODELS = {
|
|
|
749
760
|
maxTokens: 512,
|
|
750
761
|
costPer1MTokens: 0
|
|
751
762
|
}
|
|
752
|
-
},
|
|
753
|
-
"github-copilot": {
|
|
754
|
-
"text-embedding-3-small": {
|
|
755
|
-
provider: "github-copilot",
|
|
756
|
-
model: "text-embedding-3-small",
|
|
757
|
-
dimensions: 1536,
|
|
758
|
-
maxTokens: 8191,
|
|
759
|
-
costPer1MTokens: 0
|
|
760
|
-
}
|
|
761
763
|
}
|
|
762
764
|
};
|
|
763
765
|
var DEFAULT_PROVIDER_MODELS = {
|
|
764
|
-
"github-copilot": "text-embedding-3-small",
|
|
765
766
|
"openai": "text-embedding-3-small",
|
|
766
767
|
"google": "gemini-embedding-001",
|
|
767
768
|
"ollama": "nomic-embed-text"
|
|
768
769
|
};
|
|
769
770
|
var AUTO_DETECT_PROVIDER_ORDER = [
|
|
770
771
|
"ollama",
|
|
771
|
-
"github-copilot",
|
|
772
772
|
"openai",
|
|
773
773
|
"google"
|
|
774
774
|
];
|
|
@@ -794,6 +794,9 @@ function getDefaultIndexingConfig() {
|
|
|
794
794
|
maxDepth: 5,
|
|
795
795
|
maxFilesPerDirectory: 100,
|
|
796
796
|
fallbackToTextOnMaxChunks: true,
|
|
797
|
+
// Must stay in sync with DEFAULT_LINES_PER_CHUNK in native/src/lib.rs (the napi
|
|
798
|
+
// fallback used when a native caller omits the argument).
|
|
799
|
+
linesPerChunk: 30,
|
|
797
800
|
gitBlame: { enabled: false }
|
|
798
801
|
};
|
|
799
802
|
}
|
|
@@ -927,6 +930,7 @@ function parseConfig(raw) {
|
|
|
927
930
|
maxDepth: typeof rawIndexing.maxDepth === "number" ? rawIndexing.maxDepth < -1 ? -1 : rawIndexing.maxDepth : defaultIndexing.maxDepth,
|
|
928
931
|
maxFilesPerDirectory: typeof rawIndexing.maxFilesPerDirectory === "number" ? Math.max(1, rawIndexing.maxFilesPerDirectory) : defaultIndexing.maxFilesPerDirectory,
|
|
929
932
|
fallbackToTextOnMaxChunks: typeof rawIndexing.fallbackToTextOnMaxChunks === "boolean" ? rawIndexing.fallbackToTextOnMaxChunks : defaultIndexing.fallbackToTextOnMaxChunks,
|
|
933
|
+
linesPerChunk: typeof rawIndexing.linesPerChunk === "number" && Number.isFinite(rawIndexing.linesPerChunk) ? Math.min(Math.max(1, Math.floor(rawIndexing.linesPerChunk)), 4294967295) : defaultIndexing.linesPerChunk,
|
|
930
934
|
gitBlame: {
|
|
931
935
|
enabled: rawIndexing.gitBlame && typeof rawIndexing.gitBlame === "object" && typeof rawIndexing.gitBlame.enabled === "boolean" ? rawIndexing.gitBlame.enabled : defaultIndexing.gitBlame.enabled
|
|
932
936
|
}
|
|
@@ -969,6 +973,7 @@ function parseConfig(raw) {
|
|
|
969
973
|
let embeddingModel;
|
|
970
974
|
let customProvider;
|
|
971
975
|
let reranker;
|
|
976
|
+
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.';
|
|
972
977
|
if (embeddingProviderValue === "custom") {
|
|
973
978
|
embeddingProvider = "custom";
|
|
974
979
|
const rawCustom = input.customProvider && typeof input.customProvider === "object" ? input.customProvider : null;
|
|
@@ -1008,6 +1013,8 @@ function parseConfig(raw) {
|
|
|
1008
1013
|
} else if (rawEmbeddingModel) {
|
|
1009
1014
|
embeddingModel = DEFAULT_PROVIDER_MODELS[embeddingProvider];
|
|
1010
1015
|
}
|
|
1016
|
+
} else if (embeddingProviderValue === "github-copilot") {
|
|
1017
|
+
throw new Error(githubCopilotDeprecationMessage);
|
|
1011
1018
|
} else {
|
|
1012
1019
|
embeddingProvider = "auto";
|
|
1013
1020
|
}
|
|
@@ -1038,10 +1045,21 @@ function parseConfig(raw) {
|
|
|
1038
1045
|
timeoutMs: typeof rawReranker.timeoutMs === "number" ? Math.max(1e3, Math.floor(rawReranker.timeoutMs)) : 1e4
|
|
1039
1046
|
};
|
|
1040
1047
|
}
|
|
1048
|
+
const rawEmbedding = input.embedding && typeof input.embedding === "object" ? input.embedding : {};
|
|
1049
|
+
const rawEmbeddingBatch = rawEmbedding.batch && typeof rawEmbedding.batch === "object" ? rawEmbedding.batch : null;
|
|
1050
|
+
const embeddingMaxBatchItems = typeof rawEmbeddingBatch?.maxBatchItems === "number" && Number.isFinite(rawEmbeddingBatch.maxBatchItems) ? Math.max(1, Math.floor(rawEmbeddingBatch.maxBatchItems)) : void 0;
|
|
1051
|
+
const embeddingMaxBatchTokens = typeof rawEmbeddingBatch?.maxBatchTokens === "number" && Number.isFinite(rawEmbeddingBatch.maxBatchTokens) ? Math.max(1, Math.floor(rawEmbeddingBatch.maxBatchTokens)) : void 0;
|
|
1052
|
+
const embedding = embeddingMaxBatchItems !== void 0 || embeddingMaxBatchTokens !== void 0 ? {
|
|
1053
|
+
batch: {
|
|
1054
|
+
...embeddingMaxBatchItems !== void 0 ? { maxBatchItems: embeddingMaxBatchItems } : {},
|
|
1055
|
+
...embeddingMaxBatchTokens !== void 0 ? { maxBatchTokens: embeddingMaxBatchTokens } : {}
|
|
1056
|
+
}
|
|
1057
|
+
} : {};
|
|
1041
1058
|
return {
|
|
1042
1059
|
embeddingProvider,
|
|
1043
1060
|
embeddingModel,
|
|
1044
1061
|
customProvider,
|
|
1062
|
+
embedding,
|
|
1045
1063
|
scope: isValidScope(scopeValue) ? scopeValue : "project",
|
|
1046
1064
|
include: includeValue ?? DEFAULT_INCLUDE,
|
|
1047
1065
|
exclude: excludeValue ?? DEFAULT_EXCLUDE,
|
|
@@ -1165,9 +1183,9 @@ import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
|
1165
1183
|
import * as path from "path";
|
|
1166
1184
|
|
|
1167
1185
|
// src/eval/report-formatters.ts
|
|
1168
|
-
function assertFiniteNumber(value,
|
|
1186
|
+
function assertFiniteNumber(value, path33) {
|
|
1169
1187
|
if (typeof value !== "number" || Number.isNaN(value) || !Number.isFinite(value)) {
|
|
1170
|
-
throw new Error(`${
|
|
1188
|
+
throw new Error(`${path33} must be a finite number`);
|
|
1171
1189
|
}
|
|
1172
1190
|
return value;
|
|
1173
1191
|
}
|
|
@@ -1439,7 +1457,7 @@ function pTimeout(promise, options) {
|
|
|
1439
1457
|
} = options;
|
|
1440
1458
|
let timer;
|
|
1441
1459
|
let abortHandler;
|
|
1442
|
-
const wrappedPromise = new Promise((
|
|
1460
|
+
const wrappedPromise = new Promise((resolve20, reject) => {
|
|
1443
1461
|
if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
|
|
1444
1462
|
throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
|
|
1445
1463
|
}
|
|
@@ -1453,7 +1471,7 @@ function pTimeout(promise, options) {
|
|
|
1453
1471
|
};
|
|
1454
1472
|
signal.addEventListener("abort", abortHandler, { once: true });
|
|
1455
1473
|
}
|
|
1456
|
-
promise.then(
|
|
1474
|
+
promise.then(resolve20, reject);
|
|
1457
1475
|
if (milliseconds === Number.POSITIVE_INFINITY) {
|
|
1458
1476
|
return;
|
|
1459
1477
|
}
|
|
@@ -1461,7 +1479,7 @@ function pTimeout(promise, options) {
|
|
|
1461
1479
|
timer = customTimers.setTimeout.call(void 0, () => {
|
|
1462
1480
|
if (fallback) {
|
|
1463
1481
|
try {
|
|
1464
|
-
|
|
1482
|
+
resolve20(fallback());
|
|
1465
1483
|
} catch (error) {
|
|
1466
1484
|
reject(error);
|
|
1467
1485
|
}
|
|
@@ -1471,7 +1489,7 @@ function pTimeout(promise, options) {
|
|
|
1471
1489
|
promise.cancel();
|
|
1472
1490
|
}
|
|
1473
1491
|
if (message === false) {
|
|
1474
|
-
|
|
1492
|
+
resolve20();
|
|
1475
1493
|
} else if (message instanceof Error) {
|
|
1476
1494
|
reject(message);
|
|
1477
1495
|
} else {
|
|
@@ -1873,7 +1891,7 @@ var PQueue = class extends import_index.default {
|
|
|
1873
1891
|
// Assign unique ID if not provided
|
|
1874
1892
|
id: options.id ?? (this.#idAssigner++).toString()
|
|
1875
1893
|
};
|
|
1876
|
-
return new Promise((
|
|
1894
|
+
return new Promise((resolve20, reject) => {
|
|
1877
1895
|
const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
|
|
1878
1896
|
let cleanupQueueAbortHandler = () => void 0;
|
|
1879
1897
|
const run = async () => {
|
|
@@ -1913,7 +1931,7 @@ var PQueue = class extends import_index.default {
|
|
|
1913
1931
|
})]);
|
|
1914
1932
|
}
|
|
1915
1933
|
const result = await operation;
|
|
1916
|
-
|
|
1934
|
+
resolve20(result);
|
|
1917
1935
|
this.emit("completed", result);
|
|
1918
1936
|
} catch (error) {
|
|
1919
1937
|
reject(error);
|
|
@@ -2101,13 +2119,13 @@ var PQueue = class extends import_index.default {
|
|
|
2101
2119
|
});
|
|
2102
2120
|
}
|
|
2103
2121
|
async #onEvent(event, filter) {
|
|
2104
|
-
return new Promise((
|
|
2122
|
+
return new Promise((resolve20) => {
|
|
2105
2123
|
const listener = () => {
|
|
2106
2124
|
if (filter && !filter()) {
|
|
2107
2125
|
return;
|
|
2108
2126
|
}
|
|
2109
2127
|
this.off(event, listener);
|
|
2110
|
-
|
|
2128
|
+
resolve20();
|
|
2111
2129
|
};
|
|
2112
2130
|
this.on(event, listener);
|
|
2113
2131
|
});
|
|
@@ -2393,7 +2411,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
|
|
|
2393
2411
|
const finalDelay = Math.min(delayTime, remainingTime);
|
|
2394
2412
|
options.signal?.throwIfAborted();
|
|
2395
2413
|
if (finalDelay > 0) {
|
|
2396
|
-
await new Promise((
|
|
2414
|
+
await new Promise((resolve20, reject) => {
|
|
2397
2415
|
const onAbort = () => {
|
|
2398
2416
|
clearTimeout(timeoutToken);
|
|
2399
2417
|
options.signal?.removeEventListener("abort", onAbort);
|
|
@@ -2401,7 +2419,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
|
|
|
2401
2419
|
};
|
|
2402
2420
|
const timeoutToken = setTimeout(() => {
|
|
2403
2421
|
options.signal?.removeEventListener("abort", onAbort);
|
|
2404
|
-
|
|
2422
|
+
resolve20();
|
|
2405
2423
|
}, finalDelay);
|
|
2406
2424
|
if (options.unref) {
|
|
2407
2425
|
timeoutToken.unref?.();
|
|
@@ -2537,8 +2555,6 @@ async function tryDetectProvider() {
|
|
|
2537
2555
|
}
|
|
2538
2556
|
async function getProviderCredentials(provider) {
|
|
2539
2557
|
switch (provider) {
|
|
2540
|
-
case "github-copilot":
|
|
2541
|
-
return getGitHubCopilotCredentials();
|
|
2542
2558
|
case "openai":
|
|
2543
2559
|
return getOpenAICredentials();
|
|
2544
2560
|
case "google":
|
|
@@ -2549,22 +2565,6 @@ async function getProviderCredentials(provider) {
|
|
|
2549
2565
|
return null;
|
|
2550
2566
|
}
|
|
2551
2567
|
}
|
|
2552
|
-
function getGitHubCopilotCredentials() {
|
|
2553
|
-
const authData = loadOpenCodeAuth();
|
|
2554
|
-
const copilotAuth = authData["github-copilot"] || authData["github-copilot-enterprise"];
|
|
2555
|
-
if (!copilotAuth || copilotAuth.type !== "oauth") {
|
|
2556
|
-
return null;
|
|
2557
|
-
}
|
|
2558
|
-
const auth = copilotAuth;
|
|
2559
|
-
const baseUrl = auth.enterpriseUrl ? `https://copilot-api.${auth.enterpriseUrl.replace(/^https?:\/\//, "").replace(/\/$/, "")}` : "https://models.github.ai";
|
|
2560
|
-
return {
|
|
2561
|
-
provider: "github-copilot",
|
|
2562
|
-
baseUrl,
|
|
2563
|
-
refreshToken: copilotAuth.refresh,
|
|
2564
|
-
accessToken: copilotAuth.access,
|
|
2565
|
-
tokenExpires: copilotAuth.expires
|
|
2566
|
-
};
|
|
2567
|
-
}
|
|
2568
2568
|
function getOpenAICredentials() {
|
|
2569
2569
|
const authData = loadOpenCodeAuth();
|
|
2570
2570
|
const openaiAuth = authData["openai"];
|
|
@@ -2690,8 +2690,6 @@ async function tryDetectOllamaProvider() {
|
|
|
2690
2690
|
}
|
|
2691
2691
|
function getProviderDisplayName(provider) {
|
|
2692
2692
|
switch (provider) {
|
|
2693
|
-
case "github-copilot":
|
|
2694
|
-
return "GitHub Copilot";
|
|
2695
2693
|
case "openai":
|
|
2696
2694
|
return "OpenAI";
|
|
2697
2695
|
case "google":
|
|
@@ -2916,44 +2914,6 @@ var CustomEmbeddingProvider = class extends BaseEmbeddingProvider {
|
|
|
2916
2914
|
}
|
|
2917
2915
|
};
|
|
2918
2916
|
|
|
2919
|
-
// src/embeddings/providers/github-copilot.ts
|
|
2920
|
-
var GitHubCopilotEmbeddingProvider = class extends BaseEmbeddingProvider {
|
|
2921
|
-
constructor(credentials, modelInfo) {
|
|
2922
|
-
super(credentials, modelInfo);
|
|
2923
|
-
}
|
|
2924
|
-
getToken() {
|
|
2925
|
-
if (!this.credentials.refreshToken) {
|
|
2926
|
-
throw new Error("No OAuth token available for GitHub");
|
|
2927
|
-
}
|
|
2928
|
-
return this.credentials.refreshToken;
|
|
2929
|
-
}
|
|
2930
|
-
async embedBatch(texts) {
|
|
2931
|
-
const token = this.getToken();
|
|
2932
|
-
const response = await fetch(`${this.credentials.baseUrl}/inference/embeddings`, {
|
|
2933
|
-
method: "POST",
|
|
2934
|
-
headers: {
|
|
2935
|
-
Authorization: `Bearer ${token}`,
|
|
2936
|
-
"Content-Type": "application/json",
|
|
2937
|
-
Accept: "application/vnd.github+json",
|
|
2938
|
-
"X-GitHub-Api-Version": "2022-11-28"
|
|
2939
|
-
},
|
|
2940
|
-
body: JSON.stringify({
|
|
2941
|
-
model: `openai/${this.modelInfo.model}`,
|
|
2942
|
-
input: texts
|
|
2943
|
-
})
|
|
2944
|
-
});
|
|
2945
|
-
if (!response.ok) {
|
|
2946
|
-
const error = (await response.text()).slice(0, 500);
|
|
2947
|
-
throw new Error(`GitHub Copilot embedding API error: ${response.status} - ${error}`);
|
|
2948
|
-
}
|
|
2949
|
-
const data = await response.json();
|
|
2950
|
-
return {
|
|
2951
|
-
embeddings: data.data.map((d) => d.embedding),
|
|
2952
|
-
totalTokensUsed: data.usage.total_tokens
|
|
2953
|
-
};
|
|
2954
|
-
}
|
|
2955
|
-
};
|
|
2956
|
-
|
|
2957
2917
|
// src/embeddings/providers/google.ts
|
|
2958
2918
|
var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider extends BaseEmbeddingProvider {
|
|
2959
2919
|
static BATCH_SIZE = 20;
|
|
@@ -2961,24 +2921,30 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider extends BaseEmbeddi
|
|
|
2961
2921
|
super(credentials, modelInfo);
|
|
2962
2922
|
}
|
|
2963
2923
|
async embedQuery(query) {
|
|
2964
|
-
const taskType = this.modelInfo.taskAble ? "CODE_RETRIEVAL_QUERY" : void 0;
|
|
2965
|
-
const
|
|
2924
|
+
const taskType = this.modelInfo.model === "gemini-embedding-001" && this.modelInfo.taskAble ? "CODE_RETRIEVAL_QUERY" : void 0;
|
|
2925
|
+
const texts = [
|
|
2926
|
+
this.modelInfo.model === "gemini-embedding-2" ? `task: code retrieval | query: ${query}` : query
|
|
2927
|
+
];
|
|
2928
|
+
const result = await this.embedWithTaskType(texts, taskType);
|
|
2966
2929
|
return {
|
|
2967
2930
|
embedding: result.embeddings[0],
|
|
2968
2931
|
tokensUsed: result.totalTokensUsed
|
|
2969
2932
|
};
|
|
2970
2933
|
}
|
|
2971
2934
|
async embedDocument(document) {
|
|
2972
|
-
const taskType = this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
2973
|
-
const result = await this.embedWithTaskType([
|
|
2935
|
+
const taskType = this.modelInfo.model === "gemini-embedding-001" && this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
2936
|
+
const result = await this.embedWithTaskType([
|
|
2937
|
+
this.modelInfo.model === "gemini-embedding-2" ? `title: none | text: ${document}` : document
|
|
2938
|
+
], taskType);
|
|
2974
2939
|
return {
|
|
2975
2940
|
embedding: result.embeddings[0],
|
|
2976
2941
|
tokensUsed: result.totalTokensUsed
|
|
2977
2942
|
};
|
|
2978
2943
|
}
|
|
2979
2944
|
async embedBatch(texts) {
|
|
2980
|
-
const taskType = this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
2981
|
-
|
|
2945
|
+
const taskType = this.modelInfo.model === "gemini-embedding-001" && this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
2946
|
+
const formattedTexts = this.modelInfo.model === "gemini-embedding-2" ? texts.map((text) => `title: none | text: ${text}`) : texts;
|
|
2947
|
+
return this.embedWithTaskType(formattedTexts, taskType);
|
|
2982
2948
|
}
|
|
2983
2949
|
async embedWithTaskType(texts, taskType) {
|
|
2984
2950
|
const batches = [];
|
|
@@ -3028,6 +2994,10 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider extends BaseEmbeddi
|
|
|
3028
2994
|
var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddingProvider {
|
|
3029
2995
|
static MIN_TRUNCATION_CHARS = 512;
|
|
3030
2996
|
static REQUEST_TIMEOUT_MS = 12e4;
|
|
2997
|
+
// Set when /api/embed returns 404 so subsequent multi-text batches skip the
|
|
2998
|
+
// batched endpoint and go straight to the legacy per-text path (one probe per
|
|
2999
|
+
// old ollama install, not one probe per batch).
|
|
3000
|
+
batchEndpointUnavailable = false;
|
|
3031
3001
|
constructor(credentials, modelInfo) {
|
|
3032
3002
|
super(credentials, modelInfo);
|
|
3033
3003
|
}
|
|
@@ -3045,6 +3015,21 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddi
|
|
|
3045
3015
|
const message = (error instanceof Error ? error.message : String(error)).toLowerCase();
|
|
3046
3016
|
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");
|
|
3047
3017
|
}
|
|
3018
|
+
// True for a 404 from the newer /api/embed endpoint, i.e. an ollama version that
|
|
3019
|
+
// does not provide it. embedBatch uses this to fall back to the legacy per-text
|
|
3020
|
+
// /api/embeddings path so old ollama installs do not regress.
|
|
3021
|
+
isBatchEndpointUnavailableError(error) {
|
|
3022
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3023
|
+
return message.includes("Ollama /api/embed not available");
|
|
3024
|
+
}
|
|
3025
|
+
// True for a malformed /api/embed response (wrong vector count or a bad vector).
|
|
3026
|
+
// embedBatch falls back to the per-text path on this so a bad batch response
|
|
3027
|
+
// re-embeds each text cleanly. A text that then fails per-text is not isolated
|
|
3028
|
+
// here; it is isolated on the recovery run, which re-embeds one text per request.
|
|
3029
|
+
isBatchValidationError(error) {
|
|
3030
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3031
|
+
return message.includes("invalid embedding batch");
|
|
3032
|
+
}
|
|
3048
3033
|
buildTruncationCandidates(text) {
|
|
3049
3034
|
const baseMaxChars = Math.max(1, this.modelInfo.maxTokens * 4);
|
|
3050
3035
|
const candidateLimits = /* @__PURE__ */ new Set();
|
|
@@ -3146,7 +3131,74 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddi
|
|
|
3146
3131
|
tokensUsed: this.estimateTokens(text)
|
|
3147
3132
|
};
|
|
3148
3133
|
}
|
|
3149
|
-
|
|
3134
|
+
// Embeds many texts in one POST /api/embed request (input: string[]). Ollama
|
|
3135
|
+
// encodes each input independently, so the model context length applies per input
|
|
3136
|
+
// (the upstream splitter already bounds each input), not over the batch. This
|
|
3137
|
+
// amortizes N HTTP round-trips into one.
|
|
3138
|
+
async embedMany(texts) {
|
|
3139
|
+
const controller = new AbortController();
|
|
3140
|
+
const timeout = setTimeout(
|
|
3141
|
+
() => controller.abort(),
|
|
3142
|
+
_OllamaEmbeddingProvider.REQUEST_TIMEOUT_MS
|
|
3143
|
+
);
|
|
3144
|
+
let response;
|
|
3145
|
+
try {
|
|
3146
|
+
response = await fetch(`${this.credentials.baseUrl}/api/embed`, {
|
|
3147
|
+
method: "POST",
|
|
3148
|
+
headers: {
|
|
3149
|
+
"Content-Type": "application/json"
|
|
3150
|
+
},
|
|
3151
|
+
body: JSON.stringify({
|
|
3152
|
+
model: this.modelInfo.model,
|
|
3153
|
+
input: texts,
|
|
3154
|
+
truncate: false
|
|
3155
|
+
}),
|
|
3156
|
+
signal: controller.signal
|
|
3157
|
+
});
|
|
3158
|
+
} catch (error) {
|
|
3159
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
3160
|
+
throw new Error(
|
|
3161
|
+
`Ollama embedding request timed out after ${_OllamaEmbeddingProvider.REQUEST_TIMEOUT_MS}ms`
|
|
3162
|
+
);
|
|
3163
|
+
}
|
|
3164
|
+
throw error;
|
|
3165
|
+
} finally {
|
|
3166
|
+
clearTimeout(timeout);
|
|
3167
|
+
}
|
|
3168
|
+
if (!response.ok) {
|
|
3169
|
+
const error = (await response.text()).slice(0, 500);
|
|
3170
|
+
if (response.status === 404) {
|
|
3171
|
+
throw new Error(`Ollama /api/embed not available: ${response.status} - ${error}`);
|
|
3172
|
+
}
|
|
3173
|
+
throw new Error(`Ollama embedding API error: ${response.status} - ${error}`);
|
|
3174
|
+
}
|
|
3175
|
+
let parsed;
|
|
3176
|
+
try {
|
|
3177
|
+
parsed = await response.json();
|
|
3178
|
+
} catch {
|
|
3179
|
+
throw new Error(
|
|
3180
|
+
`Ollama returned an invalid embedding batch; expected ${texts.length} vectors of ${this.modelInfo.dimensions} finite dimensions`
|
|
3181
|
+
);
|
|
3182
|
+
}
|
|
3183
|
+
const data = parsed && typeof parsed === "object" ? parsed : {};
|
|
3184
|
+
if (!Array.isArray(data.embeddings) || data.embeddings.length !== texts.length || data.embeddings.some(
|
|
3185
|
+
(value) => !Array.isArray(value) || value.length !== this.modelInfo.dimensions || value.some((v) => typeof v !== "number" || !Number.isFinite(v))
|
|
3186
|
+
)) {
|
|
3187
|
+
throw new Error(
|
|
3188
|
+
`Ollama returned an invalid embedding batch; expected ${texts.length} vectors of ${this.modelInfo.dimensions} finite dimensions`
|
|
3189
|
+
);
|
|
3190
|
+
}
|
|
3191
|
+
return {
|
|
3192
|
+
embeddings: data.embeddings,
|
|
3193
|
+
totalTokensUsed: texts.reduce((sum, text) => sum + this.estimateTokens(text), 0)
|
|
3194
|
+
};
|
|
3195
|
+
}
|
|
3196
|
+
// Per-text /api/embeddings path shared by the single-text fast path and the
|
|
3197
|
+
// batch fallback. Uses the legacy endpoint one text at a time, so each text gets
|
|
3198
|
+
// its own truncation safety net and a vector validated on its own. A text that
|
|
3199
|
+
// hard-fails per-text throws here and fails the whole request batch; the recovery
|
|
3200
|
+
// run re-embeds one text per request to isolate it.
|
|
3201
|
+
async embedOneByOne(texts) {
|
|
3150
3202
|
const results = [];
|
|
3151
3203
|
for (const text of texts) {
|
|
3152
3204
|
results.push(await this.embedSingleWithFallback(text));
|
|
@@ -3156,6 +3208,26 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddi
|
|
|
3156
3208
|
totalTokensUsed: results.reduce((sum, r) => sum + r.tokensUsed, 0)
|
|
3157
3209
|
};
|
|
3158
3210
|
}
|
|
3211
|
+
async embedBatch(texts) {
|
|
3212
|
+
if (texts.length === 0) {
|
|
3213
|
+
return { embeddings: [], totalTokensUsed: 0 };
|
|
3214
|
+
}
|
|
3215
|
+
if (texts.length === 1 || this.batchEndpointUnavailable) {
|
|
3216
|
+
return this.embedOneByOne(texts);
|
|
3217
|
+
}
|
|
3218
|
+
try {
|
|
3219
|
+
return await this.embedMany(texts);
|
|
3220
|
+
} catch (error) {
|
|
3221
|
+
if (this.isBatchEndpointUnavailableError(error)) {
|
|
3222
|
+
this.batchEndpointUnavailable = true;
|
|
3223
|
+
return this.embedOneByOne(texts);
|
|
3224
|
+
}
|
|
3225
|
+
if (!this.isContextLengthError(error) && !this.isBatchValidationError(error)) {
|
|
3226
|
+
throw error;
|
|
3227
|
+
}
|
|
3228
|
+
return this.embedOneByOne(texts);
|
|
3229
|
+
}
|
|
3230
|
+
}
|
|
3159
3231
|
};
|
|
3160
3232
|
|
|
3161
3233
|
// src/embeddings/providers/openai.ts
|
|
@@ -3190,8 +3262,6 @@ var OpenAIEmbeddingProvider = class extends BaseEmbeddingProvider {
|
|
|
3190
3262
|
// src/embeddings/provider.ts
|
|
3191
3263
|
function createEmbeddingProvider(configuredProviderInfo) {
|
|
3192
3264
|
switch (configuredProviderInfo.provider) {
|
|
3193
|
-
case "github-copilot":
|
|
3194
|
-
return new GitHubCopilotEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
3195
3265
|
case "openai":
|
|
3196
3266
|
return new OpenAIEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
3197
3267
|
case "google":
|
|
@@ -3207,85 +3277,6 @@ function createEmbeddingProvider(configuredProviderInfo) {
|
|
|
3207
3277
|
}
|
|
3208
3278
|
}
|
|
3209
3279
|
|
|
3210
|
-
// src/rerank/index.ts
|
|
3211
|
-
function createReranker(config) {
|
|
3212
|
-
if (!config.enabled) {
|
|
3213
|
-
return new NoOpReranker();
|
|
3214
|
-
}
|
|
3215
|
-
return new SiliconFlowReranker(config);
|
|
3216
|
-
}
|
|
3217
|
-
var NoOpReranker = class {
|
|
3218
|
-
isAvailable() {
|
|
3219
|
-
return false;
|
|
3220
|
-
}
|
|
3221
|
-
async rerank(_query, documents, _topN) {
|
|
3222
|
-
return {
|
|
3223
|
-
results: documents.map((_, index) => ({ index, relevanceScore: 0 }))
|
|
3224
|
-
};
|
|
3225
|
-
}
|
|
3226
|
-
};
|
|
3227
|
-
var SiliconFlowReranker = class {
|
|
3228
|
-
config;
|
|
3229
|
-
constructor(config) {
|
|
3230
|
-
this.config = config;
|
|
3231
|
-
}
|
|
3232
|
-
isAvailable() {
|
|
3233
|
-
return this.config.enabled && !!this.config.baseUrl && !!this.config.model;
|
|
3234
|
-
}
|
|
3235
|
-
async rerank(query, documents, topN) {
|
|
3236
|
-
if (documents.length === 0) {
|
|
3237
|
-
return { results: [] };
|
|
3238
|
-
}
|
|
3239
|
-
const headers = {
|
|
3240
|
-
"Content-Type": "application/json"
|
|
3241
|
-
};
|
|
3242
|
-
if (this.config.apiKey) {
|
|
3243
|
-
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
3244
|
-
}
|
|
3245
|
-
const baseUrl = this.config.baseUrl;
|
|
3246
|
-
if (!baseUrl) {
|
|
3247
|
-
throw new Error("Reranker baseUrl is required. Configure reranker.baseUrl in your codebase-index.json.");
|
|
3248
|
-
}
|
|
3249
|
-
const timeoutMs = this.config.timeoutMs ?? 3e4;
|
|
3250
|
-
const controller = new AbortController();
|
|
3251
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
3252
|
-
try {
|
|
3253
|
-
const response = await fetch(`${baseUrl}/rerank`, {
|
|
3254
|
-
method: "POST",
|
|
3255
|
-
headers,
|
|
3256
|
-
body: JSON.stringify({
|
|
3257
|
-
model: this.config.model,
|
|
3258
|
-
query,
|
|
3259
|
-
documents,
|
|
3260
|
-
top_n: topN ?? this.config.topN ?? 20,
|
|
3261
|
-
return_documents: false
|
|
3262
|
-
}),
|
|
3263
|
-
signal: controller.signal
|
|
3264
|
-
});
|
|
3265
|
-
clearTimeout(timeout);
|
|
3266
|
-
if (!response.ok) {
|
|
3267
|
-
const errorText = await response.text();
|
|
3268
|
-
throw new Error(`Rerank API error: ${response.status} - ${errorText}`);
|
|
3269
|
-
}
|
|
3270
|
-
const data = await response.json();
|
|
3271
|
-
return {
|
|
3272
|
-
results: data.results.map((r) => ({
|
|
3273
|
-
index: r.index,
|
|
3274
|
-
relevanceScore: r.relevance_score,
|
|
3275
|
-
document: r.document?.text
|
|
3276
|
-
})),
|
|
3277
|
-
tokensUsed: data.meta?.tokens?.input_tokens
|
|
3278
|
-
};
|
|
3279
|
-
} catch (error) {
|
|
3280
|
-
clearTimeout(timeout);
|
|
3281
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
3282
|
-
throw new Error(`Rerank API request timed out after ${timeoutMs}ms`);
|
|
3283
|
-
}
|
|
3284
|
-
throw error;
|
|
3285
|
-
}
|
|
3286
|
-
}
|
|
3287
|
-
};
|
|
3288
|
-
|
|
3289
3280
|
// src/utils/files.ts
|
|
3290
3281
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
3291
3282
|
import { existsSync as existsSync2, readFileSync as readFileSync3, promises as fsPromises } from "fs";
|
|
@@ -3447,8 +3438,8 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
|
|
|
3447
3438
|
if (entry.isDirectory()) {
|
|
3448
3439
|
subdirs.push({ fullPath, relativePath });
|
|
3449
3440
|
} else if (entry.isFile()) {
|
|
3450
|
-
const
|
|
3451
|
-
if (
|
|
3441
|
+
const stat5 = await fsPromises.stat(fullPath);
|
|
3442
|
+
if (stat5.size > maxFileSize) {
|
|
3452
3443
|
skipped.push({ path: relativePath, reason: "too_large" });
|
|
3453
3444
|
continue;
|
|
3454
3445
|
}
|
|
@@ -3466,7 +3457,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
|
|
|
3466
3457
|
}
|
|
3467
3458
|
}
|
|
3468
3459
|
if (matched) {
|
|
3469
|
-
filesInDir.push({ path: fullPath, size:
|
|
3460
|
+
filesInDir.push({ path: fullPath, size: stat5.size });
|
|
3470
3461
|
}
|
|
3471
3462
|
}
|
|
3472
3463
|
}
|
|
@@ -3523,8 +3514,8 @@ async function collectFiles(projectRoot, includePatterns, excludePatterns, maxFi
|
|
|
3523
3514
|
}
|
|
3524
3515
|
for (const resolvedKbRoot of normalizedRoots) {
|
|
3525
3516
|
try {
|
|
3526
|
-
const
|
|
3527
|
-
if (!
|
|
3517
|
+
const stat5 = await fsPromises.stat(resolvedKbRoot);
|
|
3518
|
+
if (!stat5.isDirectory()) {
|
|
3528
3519
|
skipped.push({ path: resolvedKbRoot, reason: "excluded" });
|
|
3529
3520
|
continue;
|
|
3530
3521
|
}
|
|
@@ -4288,12 +4279,12 @@ try {
|
|
|
4288
4279
|
}
|
|
4289
4280
|
|
|
4290
4281
|
// src/native/parsing.ts
|
|
4291
|
-
function parseFileAsText(filePath, content) {
|
|
4292
|
-
const result = native.parseFileAsText(filePath, content);
|
|
4282
|
+
function parseFileAsText(filePath, content, linesPerChunk) {
|
|
4283
|
+
const result = native.parseFileAsText(filePath, content, linesPerChunk);
|
|
4293
4284
|
return result.map(mapChunk);
|
|
4294
4285
|
}
|
|
4295
|
-
function parseFiles(files) {
|
|
4296
|
-
const result = native.parseFiles(files);
|
|
4286
|
+
function parseFiles(files, linesPerChunk) {
|
|
4287
|
+
const result = native.parseFiles(files, linesPerChunk);
|
|
4297
4288
|
return result.map((f) => ({
|
|
4298
4289
|
path: f.path,
|
|
4299
4290
|
chunks: f.chunks.map(mapChunk),
|
|
@@ -4370,13 +4361,13 @@ var VectorStore = class {
|
|
|
4370
4361
|
const metadata = items.map((i) => JSON.stringify(i.metadata));
|
|
4371
4362
|
this.inner.addBatch(ids, vectors, metadata);
|
|
4372
4363
|
}
|
|
4373
|
-
search(queryVector, limit = 10) {
|
|
4364
|
+
search(queryVector, limit = 10, allowedIds) {
|
|
4374
4365
|
if (queryVector.length !== this.dimensions) {
|
|
4375
4366
|
throw new Error(
|
|
4376
4367
|
`Query vector dimension mismatch: expected ${this.dimensions}, got ${queryVector.length}`
|
|
4377
4368
|
);
|
|
4378
4369
|
}
|
|
4379
|
-
const results = this.inner.search(queryVector, limit);
|
|
4370
|
+
const results = allowedIds === void 0 ? this.inner.search(queryVector, limit) : this.inner.searchFiltered(queryVector, limit, allowedIds);
|
|
4380
4371
|
return results.map((r) => ({
|
|
4381
4372
|
id: r.id,
|
|
4382
4373
|
score: r.score,
|
|
@@ -4604,6 +4595,10 @@ var Database = class _Database {
|
|
|
4604
4595
|
this.throwIfClosed();
|
|
4605
4596
|
return this.inner.getBranchChunkIds(branch);
|
|
4606
4597
|
}
|
|
4598
|
+
getChunkIdsByBlameDate(since, until) {
|
|
4599
|
+
this.throwIfClosed();
|
|
4600
|
+
return this.inner.getChunkIdsByBlameDate(since, until);
|
|
4601
|
+
}
|
|
4607
4602
|
getBranchDelta(branch, baseBranch) {
|
|
4608
4603
|
this.throwIfClosed();
|
|
4609
4604
|
return this.inner.getBranchDelta(branch, baseBranch);
|
|
@@ -4850,11 +4845,11 @@ function resolveGitDir(repoRoot) {
|
|
|
4850
4845
|
return null;
|
|
4851
4846
|
}
|
|
4852
4847
|
try {
|
|
4853
|
-
const
|
|
4854
|
-
if (
|
|
4848
|
+
const stat5 = statSync2(gitPath);
|
|
4849
|
+
if (stat5.isDirectory()) {
|
|
4855
4850
|
return gitPath;
|
|
4856
4851
|
}
|
|
4857
|
-
if (
|
|
4852
|
+
if (stat5.isFile()) {
|
|
4858
4853
|
const content = readFileSync5(gitPath, "utf-8").trim();
|
|
4859
4854
|
const match = content.match(/^gitdir:\s*(.+)$/);
|
|
4860
4855
|
if (match) {
|
|
@@ -5359,8 +5354,8 @@ async function isWorktreeRegistered(projectRoot, worktreePath) {
|
|
|
5359
5354
|
return false;
|
|
5360
5355
|
}
|
|
5361
5356
|
function isPathWithinRoot(filePath, rootPath) {
|
|
5362
|
-
const
|
|
5363
|
-
return
|
|
5357
|
+
const relative14 = path9.relative(path9.resolve(rootPath), path9.resolve(filePath));
|
|
5358
|
+
return relative14 === "" || !relative14.startsWith(`..${path9.sep}`) && relative14 !== ".." && !path9.isAbsolute(relative14);
|
|
5364
5359
|
}
|
|
5365
5360
|
async function pruneExactMissingWorktreeRegistration(projectRoot, worktreePath) {
|
|
5366
5361
|
if (await pathExists(worktreePath)) return false;
|
|
@@ -5565,6 +5560,9 @@ function resolveWorktreeFallbackPath(projectRoot, relativePath) {
|
|
|
5565
5560
|
const fallbackPath = path10.join(mainRepoRoot, relativePath);
|
|
5566
5561
|
return existsSync5(fallbackPath) ? fallbackPath : null;
|
|
5567
5562
|
}
|
|
5563
|
+
function getHostProjectConfigRelativePath(host) {
|
|
5564
|
+
return getProjectConfigRelativePath(host);
|
|
5565
|
+
}
|
|
5568
5566
|
function getProjectConfigCandidatePaths(projectRoot, host) {
|
|
5569
5567
|
const candidates = [path10.join(projectRoot, getProjectConfigRelativePath(host))];
|
|
5570
5568
|
if (host !== "opencode") {
|
|
@@ -5656,6 +5654,9 @@ function resolveProjectConfigPath(projectRoot, host) {
|
|
|
5656
5654
|
const candidates = getProjectConfigCandidatePaths(projectRoot, host);
|
|
5657
5655
|
return candidates.find((candidate) => existsSync5(candidate)) ?? path10.join(projectRoot, getProjectConfigRelativePath(host));
|
|
5658
5656
|
}
|
|
5657
|
+
function resolveWritableProjectConfigPath(projectRoot, host) {
|
|
5658
|
+
return path10.join(projectRoot, getProjectConfigRelativePath(host));
|
|
5659
|
+
}
|
|
5659
5660
|
function resolveProjectIndexPath(projectRoot, scope, host) {
|
|
5660
5661
|
if (scope === "global") {
|
|
5661
5662
|
return resolveGlobalIndexPath(host);
|
|
@@ -5918,11 +5919,11 @@ function normalizeFiles(rawFiles, projectRoot) {
|
|
|
5918
5919
|
for (const raw of rawFiles) {
|
|
5919
5920
|
if (raw.length === 0) continue;
|
|
5920
5921
|
const absolute = path11.resolve(root, raw);
|
|
5921
|
-
const
|
|
5922
|
-
if (path11.isAbsolute(raw) ||
|
|
5922
|
+
const relative14 = path11.relative(root, absolute);
|
|
5923
|
+
if (path11.isAbsolute(raw) || relative14 === ".." || relative14.startsWith(`..${path11.sep}`) || path11.isAbsolute(relative14)) {
|
|
5923
5924
|
throw new Error(`Changed file escapes the project root: ${JSON.stringify(raw)}`);
|
|
5924
5925
|
}
|
|
5925
|
-
const cleaned =
|
|
5926
|
+
const cleaned = relative14.startsWith(`.${path11.sep}`) ? relative14.slice(2) : relative14;
|
|
5926
5927
|
if (!seen.has(cleaned)) {
|
|
5927
5928
|
seen.add(cleaned);
|
|
5928
5929
|
result.push(cleaned);
|
|
@@ -6259,7 +6260,7 @@ function analyzeQueryIntent(query) {
|
|
|
6259
6260
|
}
|
|
6260
6261
|
function isTestPath(filePath) {
|
|
6261
6262
|
const normalized = normalizePath(filePath);
|
|
6262
|
-
return /(?:^|\/)(?:test|tests|__tests__|spec|specs)(?:\/|$)/u.test(normalized) ||
|
|
6263
|
+
return /(?:^|\/)(?:test|tests|__tests__|spec|specs)(?:\/|$)/u.test(normalized) || /(?:\.(?:test|spec)|_(?:test|spec))\.[^/]+$/u.test(normalized) || /(?:^|\/)(?:test|spec)_[^/]+\.[^/]+$/u.test(normalized);
|
|
6263
6264
|
}
|
|
6264
6265
|
function isFixturePath(filePath) {
|
|
6265
6266
|
const normalized = normalizePath(filePath);
|
|
@@ -6361,6 +6362,10 @@ function scoreCandidate(query, intent, candidate, originalIndex) {
|
|
|
6361
6362
|
let boost = 0;
|
|
6362
6363
|
if (intent.primary === "conceptual") {
|
|
6363
6364
|
boost += Math.min(0.14, overlap * 0.14);
|
|
6365
|
+
if (intent.preferSourcePaths) {
|
|
6366
|
+
boost += implementationPath ? 0.32 : 0;
|
|
6367
|
+
if (testPath || fixturePath || docsPath) boost -= 0.35;
|
|
6368
|
+
}
|
|
6364
6369
|
if (generatedOrVendor) boost -= 0.18;
|
|
6365
6370
|
if (importChunk || weakContainer) boost -= 0.04;
|
|
6366
6371
|
} else if (intent.primary === "test") {
|
|
@@ -6666,7 +6671,8 @@ function rankHybridResults(query, semanticResults, keywordResults, options) {
|
|
|
6666
6671
|
return cached;
|
|
6667
6672
|
}
|
|
6668
6673
|
}
|
|
6669
|
-
const
|
|
6674
|
+
const overfetchFactor = prioritizeSourcePaths ? 12 : 4;
|
|
6675
|
+
const overfetchLimit = Math.max(options.limit * overfetchFactor, options.limit);
|
|
6670
6676
|
const fused = options.fusionStrategy === "rrf" ? fuseResultsRrf(semanticResults, keywordResults, options.rrfK, overfetchLimit) : fuseResultsWeighted(semanticResults, keywordResults, options.hybridWeight, overfetchLimit);
|
|
6671
6677
|
const rerankPoolLimit = Math.max(overfetchLimit, options.rerankTopN * 3, options.limit * 6);
|
|
6672
6678
|
const rerankPool = fused.slice(0, rerankPoolLimit);
|
|
@@ -7284,6 +7290,19 @@ function parseOwner(value) {
|
|
|
7284
7290
|
if (typeof candidate.startedAt !== "string" || Number.isNaN(Date.parse(candidate.startedAt))) return null;
|
|
7285
7291
|
if (typeof candidate.operation !== "string" || !VALID_OPERATIONS.has(candidate.operation)) return null;
|
|
7286
7292
|
if (typeof candidate.token !== "string" || !UUID_PATTERN.test(candidate.token)) return null;
|
|
7293
|
+
if (candidate.recoveryProtocolVersion !== void 0 && candidate.recoveryProtocolVersion !== 1) return null;
|
|
7294
|
+
if (candidate.projectRoot !== void 0 && typeof candidate.projectRoot !== "string") return null;
|
|
7295
|
+
if (candidate.scopedRoots !== void 0) {
|
|
7296
|
+
if (!Array.isArray(candidate.scopedRoots) || candidate.scopedRoots.some((root) => typeof root !== "string")) {
|
|
7297
|
+
return null;
|
|
7298
|
+
}
|
|
7299
|
+
}
|
|
7300
|
+
if (candidate.clearRecovery !== void 0) {
|
|
7301
|
+
const recovery = candidate.clearRecovery;
|
|
7302
|
+
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") {
|
|
7303
|
+
return null;
|
|
7304
|
+
}
|
|
7305
|
+
}
|
|
7287
7306
|
return candidate;
|
|
7288
7307
|
}
|
|
7289
7308
|
function parseReclaimOwner(value) {
|
|
@@ -7524,13 +7543,18 @@ function isTransientIndexLockContention(error) {
|
|
|
7524
7543
|
if (!isIndexLockContentionError(error) || !("reason" in error)) return false;
|
|
7525
7544
|
return error.reason === "active" || error.reason === "reclaiming";
|
|
7526
7545
|
}
|
|
7527
|
-
function acquireIndexLock(indexPath, operation) {
|
|
7546
|
+
function acquireIndexLock(indexPath, operation, recoveryScope) {
|
|
7528
7547
|
mkdirSync2(indexPath, { recursive: true });
|
|
7529
7548
|
const canonicalIndexPath = realpathSync3.native(indexPath);
|
|
7530
7549
|
const lockPath = path13.join(canonicalIndexPath, "indexing.lock");
|
|
7531
7550
|
cleanupDeadPublicationCandidates(canonicalIndexPath);
|
|
7532
7551
|
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
7533
|
-
const owner = createOwner(operation)
|
|
7552
|
+
const owner = recoveryScope === void 0 ? createOwner(operation) : {
|
|
7553
|
+
...createOwner(operation),
|
|
7554
|
+
recoveryProtocolVersion: 1,
|
|
7555
|
+
projectRoot: recoveryScope.projectRoot,
|
|
7556
|
+
scopedRoots: recoveryScope.scopedRoots
|
|
7557
|
+
};
|
|
7534
7558
|
if (publishJsonDirectory(lockPath, owner)) {
|
|
7535
7559
|
const lease = {
|
|
7536
7560
|
canonicalIndexPath,
|
|
@@ -7595,6 +7619,33 @@ function releaseIndexLock(lease) {
|
|
|
7595
7619
|
}
|
|
7596
7620
|
return true;
|
|
7597
7621
|
}
|
|
7622
|
+
function setIndexLockClearRecoveryState(lease, clearRecovery) {
|
|
7623
|
+
const currentOwner = readDirectoryOwner(lease.lockPath);
|
|
7624
|
+
if (!currentOwner || !sameOwner(currentOwner, lease.owner)) {
|
|
7625
|
+
throw new Error(`Lost ownership of index mutation lease ${lease.owner.token}`);
|
|
7626
|
+
}
|
|
7627
|
+
const nextOwner = { ...currentOwner };
|
|
7628
|
+
if (clearRecovery === null) {
|
|
7629
|
+
delete nextOwner.clearRecovery;
|
|
7630
|
+
} else {
|
|
7631
|
+
nextOwner.clearRecovery = clearRecovery;
|
|
7632
|
+
}
|
|
7633
|
+
const ownerPath = path13.join(lease.lockPath, OWNER_FILE_NAME);
|
|
7634
|
+
const temporaryPath = path13.join(
|
|
7635
|
+
lease.lockPath,
|
|
7636
|
+
`${OWNER_FILE_NAME}.tmp.${lease.owner.pid}.${lease.owner.token}.${randomUUID()}`
|
|
7637
|
+
);
|
|
7638
|
+
try {
|
|
7639
|
+
writeFileSync2(temporaryPath, JSON.stringify(nextOwner), {
|
|
7640
|
+
encoding: "utf-8",
|
|
7641
|
+
flag: "wx",
|
|
7642
|
+
mode: 384
|
|
7643
|
+
});
|
|
7644
|
+
retryTransientFilesystemOperation(() => renameSync(temporaryPath, ownerPath));
|
|
7645
|
+
} finally {
|
|
7646
|
+
if (existsSync6(temporaryPath)) rmSync(temporaryPath, { force: true });
|
|
7647
|
+
}
|
|
7648
|
+
}
|
|
7598
7649
|
function createLeaseTemporaryPath(targetPath, owner, kind = "tmp") {
|
|
7599
7650
|
if (kind === "bak") return `${targetPath}.bak.${owner.pid}.${owner.token}`;
|
|
7600
7651
|
temporaryCounter += 1;
|
|
@@ -7832,6 +7883,18 @@ function createFailedBatchWriter(targetPath) {
|
|
|
7832
7883
|
temporaryPath
|
|
7833
7884
|
};
|
|
7834
7885
|
}
|
|
7886
|
+
function writeFailedBatchRecords(targetPath, records) {
|
|
7887
|
+
const writer = createFailedBatchWriter(targetPath);
|
|
7888
|
+
try {
|
|
7889
|
+
for (const record of records) {
|
|
7890
|
+
writer.write(record);
|
|
7891
|
+
}
|
|
7892
|
+
writer.commit();
|
|
7893
|
+
} catch (error) {
|
|
7894
|
+
writer.cleanup();
|
|
7895
|
+
throw error;
|
|
7896
|
+
}
|
|
7897
|
+
}
|
|
7835
7898
|
function* readLegacyFailedBatchRecords(filePath, options) {
|
|
7836
7899
|
const rawData = fs2.readFileSync(filePath, "utf-8");
|
|
7837
7900
|
const trimmed = stripLeadingBomAndWhitespace(rawData).trim();
|
|
@@ -8114,14 +8177,18 @@ function getSafeEmbeddingChunkTokenLimit(provider) {
|
|
|
8114
8177
|
const maxChunkTokens = Math.max(256, Math.floor(providerMaxTokens * 0.75));
|
|
8115
8178
|
return Math.min(2e3, maxChunkTokens);
|
|
8116
8179
|
}
|
|
8117
|
-
|
|
8118
|
-
|
|
8119
|
-
|
|
8120
|
-
|
|
8121
|
-
|
|
8122
|
-
};
|
|
8180
|
+
var DEFAULT_OLLAMA_MAX_BATCH_ITEMS = 16;
|
|
8181
|
+
var DEFAULT_OLLAMA_MAX_BATCH_TOKENS = 65536;
|
|
8182
|
+
function getDynamicBatchOptions(provider, embeddingBatch) {
|
|
8183
|
+
if (provider.provider !== "ollama") {
|
|
8184
|
+
return {};
|
|
8123
8185
|
}
|
|
8124
|
-
|
|
8186
|
+
const base = { maxBatchTokens: DEFAULT_OLLAMA_MAX_BATCH_TOKENS, maxBatchItems: DEFAULT_OLLAMA_MAX_BATCH_ITEMS };
|
|
8187
|
+
return {
|
|
8188
|
+
...base,
|
|
8189
|
+
...typeof embeddingBatch?.maxBatchTokens === "number" && Number.isFinite(embeddingBatch.maxBatchTokens) ? { maxBatchTokens: embeddingBatch.maxBatchTokens } : {},
|
|
8190
|
+
...typeof embeddingBatch?.maxBatchItems === "number" && Number.isFinite(embeddingBatch.maxBatchItems) ? { maxBatchItems: embeddingBatch.maxBatchItems } : {}
|
|
8191
|
+
};
|
|
8125
8192
|
}
|
|
8126
8193
|
function isSqliteCorruptionError(error) {
|
|
8127
8194
|
const message = getErrorMessage3(error).toLowerCase();
|
|
@@ -8139,6 +8206,14 @@ function getPendingChunkId(rawChunk) {
|
|
|
8139
8206
|
const id = rawChunk.id;
|
|
8140
8207
|
return typeof id === "string" ? id : null;
|
|
8141
8208
|
}
|
|
8209
|
+
function parseBlameTimestamp(value, endOfDay) {
|
|
8210
|
+
let timestampMs = Date.parse(value);
|
|
8211
|
+
if (Number.isNaN(timestampMs)) return null;
|
|
8212
|
+
if (endOfDay && /^\d{4}-\d{2}-\d{2}$/.test(value.trim())) {
|
|
8213
|
+
timestampMs += 24 * 60 * 60 * 1e3 - 1;
|
|
8214
|
+
}
|
|
8215
|
+
return Math.floor(timestampMs / 1e3);
|
|
8216
|
+
}
|
|
8142
8217
|
function metadataFromBlame(blame) {
|
|
8143
8218
|
if (!blame) {
|
|
8144
8219
|
return {};
|
|
@@ -8285,7 +8360,7 @@ function promoteIdentifierMatches(query, combined, semanticCandidates, keywordCa
|
|
|
8285
8360
|
const remainder = combined.filter((candidate) => !promotedIds.has(candidate.id));
|
|
8286
8361
|
return [...promoted, ...remainder];
|
|
8287
8362
|
}
|
|
8288
|
-
function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbolIds, limit, fallbackCandidates, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source") {
|
|
8363
|
+
function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbolIds, limit, fallbackCandidates, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source", allowNonSourcePaths = false) {
|
|
8289
8364
|
if (!prioritizeSourcePaths) {
|
|
8290
8365
|
return [];
|
|
8291
8366
|
}
|
|
@@ -8305,7 +8380,7 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbol
|
|
|
8305
8380
|
if (!isImplementationChunkType(chunkType)) {
|
|
8306
8381
|
return false;
|
|
8307
8382
|
}
|
|
8308
|
-
if (!isLikelyImplementationPath2(chunk.filePath)) {
|
|
8383
|
+
if (!allowNonSourcePaths && !isLikelyImplementationPath2(chunk.filePath)) {
|
|
8309
8384
|
return false;
|
|
8310
8385
|
}
|
|
8311
8386
|
const nameLower = (chunk.name ?? "").toLowerCase();
|
|
@@ -8369,7 +8444,7 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbol
|
|
|
8369
8444
|
}
|
|
8370
8445
|
foundCoveringChunk = upsertChunkCandidate(chunk, identifier, normalizedIdentifier) || foundCoveringChunk;
|
|
8371
8446
|
}
|
|
8372
|
-
if (foundCoveringChunk || !isLikelyImplementationPath2(symbol.filePath)) {
|
|
8447
|
+
if (foundCoveringChunk || !allowNonSourcePaths && !isLikelyImplementationPath2(symbol.filePath)) {
|
|
8373
8448
|
continue;
|
|
8374
8449
|
}
|
|
8375
8450
|
const symbolName = symbol.name.toLowerCase();
|
|
@@ -8423,7 +8498,7 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbol
|
|
|
8423
8498
|
const ranked = Array.from(symbolCandidates.values()).sort((a, b) => b.score - a.score || a.id.localeCompare(b.id));
|
|
8424
8499
|
if (ranked.length === 0) {
|
|
8425
8500
|
const implementationFallback = fallbackCandidates.filter(
|
|
8426
|
-
(candidate) => isImplementationChunkType(candidate.metadata.chunkType) && isLikelyImplementationPath2(candidate.metadata.filePath)
|
|
8501
|
+
(candidate) => isImplementationChunkType(candidate.metadata.chunkType) && (allowNonSourcePaths || isLikelyImplementationPath2(candidate.metadata.filePath))
|
|
8427
8502
|
);
|
|
8428
8503
|
for (const candidate of implementationFallback) {
|
|
8429
8504
|
const nameLower = (candidate.metadata.name ?? "").toLowerCase();
|
|
@@ -8539,10 +8614,16 @@ function matchesHardSearchFilters(candidate, options, projectRoot) {
|
|
|
8539
8614
|
return false;
|
|
8540
8615
|
}
|
|
8541
8616
|
if (options?.blameSince) {
|
|
8542
|
-
const
|
|
8543
|
-
if (
|
|
8617
|
+
const since = parseBlameTimestamp(options.blameSince, false);
|
|
8618
|
+
if (since === null) return false;
|
|
8544
8619
|
const committedAt = candidate.metadata.blameCommittedAt;
|
|
8545
|
-
if (committedAt === void 0 || committedAt <
|
|
8620
|
+
if (committedAt === void 0 || committedAt < since) return false;
|
|
8621
|
+
}
|
|
8622
|
+
if (options?.blameUntil) {
|
|
8623
|
+
const until = parseBlameTimestamp(options.blameUntil, true);
|
|
8624
|
+
if (until === null) return false;
|
|
8625
|
+
const committedAt = candidate.metadata.blameCommittedAt;
|
|
8626
|
+
if (committedAt === void 0 || committedAt > until) return false;
|
|
8546
8627
|
}
|
|
8547
8628
|
return true;
|
|
8548
8629
|
}
|
|
@@ -8578,7 +8659,6 @@ var Indexer = class _Indexer {
|
|
|
8578
8659
|
database = null;
|
|
8579
8660
|
provider = null;
|
|
8580
8661
|
configuredProviderInfo = null;
|
|
8581
|
-
reranker = null;
|
|
8582
8662
|
fileHashCache = /* @__PURE__ */ new Map();
|
|
8583
8663
|
fileHashCachePath = "";
|
|
8584
8664
|
failedBatchesPath = "";
|
|
@@ -8599,9 +8679,10 @@ var Indexer = class _Indexer {
|
|
|
8599
8679
|
writerArtifactFingerprint = null;
|
|
8600
8680
|
readerArtifactRetryAfter = /* @__PURE__ */ new Map();
|
|
8601
8681
|
fileBatchLimits;
|
|
8682
|
+
checkpointIntervalChunks;
|
|
8602
8683
|
constructor(projectRoot, config, host, runtimeOptions = {}) {
|
|
8603
8684
|
this.projectRoot = projectRoot;
|
|
8604
|
-
this.projectIdentityHash =
|
|
8685
|
+
this.projectIdentityHash = this.getProjectIdentityHash(projectRoot);
|
|
8605
8686
|
this.materializedProjectRoot = runtimeOptions.materializedProjectRoot ?? projectRoot;
|
|
8606
8687
|
this.branchNameOverride = runtimeOptions.branchName;
|
|
8607
8688
|
this.catalogIdentityOverride = runtimeOptions.catalogIdentity;
|
|
@@ -8611,6 +8692,7 @@ var Indexer = class _Indexer {
|
|
|
8611
8692
|
this.expectedCommitOverride = runtimeOptions.expectedCommit?.toLowerCase();
|
|
8612
8693
|
this.indexPathOverride = runtimeOptions.indexPath;
|
|
8613
8694
|
this.fileBatchLimits = runtimeOptions.fileBatchLimits;
|
|
8695
|
+
this.checkpointIntervalChunks = runtimeOptions.checkpointIntervalChunks;
|
|
8614
8696
|
this.config = config;
|
|
8615
8697
|
this.host = host;
|
|
8616
8698
|
if (isGitRepo(this.materializedProjectRoot)) {
|
|
@@ -8722,6 +8804,9 @@ var Indexer = class _Indexer {
|
|
|
8722
8804
|
return path15.resolve(targetPath);
|
|
8723
8805
|
}
|
|
8724
8806
|
}
|
|
8807
|
+
getProjectIdentityHash(projectRoot) {
|
|
8808
|
+
return hashContent(this.getCanonicalPath(projectRoot)).slice(0, 16);
|
|
8809
|
+
}
|
|
8725
8810
|
isProjectOwnedIndexPath() {
|
|
8726
8811
|
return isProjectIndexPathOwnedByProject(this.projectRoot, this.indexPath, this.host);
|
|
8727
8812
|
}
|
|
@@ -8738,7 +8823,6 @@ var Indexer = class _Indexer {
|
|
|
8738
8823
|
this.database = null;
|
|
8739
8824
|
this.provider = null;
|
|
8740
8825
|
this.configuredProviderInfo = null;
|
|
8741
|
-
this.reranker = null;
|
|
8742
8826
|
this.indexCompatibility = null;
|
|
8743
8827
|
this.initializationMode = "none";
|
|
8744
8828
|
this.readIssues = [];
|
|
@@ -8759,7 +8843,10 @@ var Indexer = class _Indexer {
|
|
|
8759
8843
|
}
|
|
8760
8844
|
async withIndexMutationLease(operation, callback) {
|
|
8761
8845
|
this.refreshBranchInfo();
|
|
8762
|
-
const lease = acquireIndexLock(this.indexPath, operation
|
|
8846
|
+
const lease = acquireIndexLock(this.indexPath, operation, {
|
|
8847
|
+
projectRoot: this.projectRoot,
|
|
8848
|
+
scopedRoots: this.getScopedRoots()
|
|
8849
|
+
});
|
|
8763
8850
|
this.indexPath = lease.canonicalIndexPath;
|
|
8764
8851
|
this.refreshRuntimeArtifactPaths();
|
|
8765
8852
|
this.activeIndexLease = lease;
|
|
@@ -8814,6 +8901,7 @@ var Indexer = class _Indexer {
|
|
|
8814
8901
|
}
|
|
8815
8902
|
loadFileHashCache() {
|
|
8816
8903
|
if (!existsSync8(this.fileHashCachePath)) {
|
|
8904
|
+
this.fileHashCache = /* @__PURE__ */ new Map();
|
|
8817
8905
|
return;
|
|
8818
8906
|
}
|
|
8819
8907
|
try {
|
|
@@ -8853,10 +8941,10 @@ var Indexer = class _Indexer {
|
|
|
8853
8941
|
invertedIndex.serialize()
|
|
8854
8942
|
);
|
|
8855
8943
|
}
|
|
8856
|
-
getScopedRoots() {
|
|
8857
|
-
const roots = /* @__PURE__ */ new Set([this.getCanonicalPath(
|
|
8944
|
+
getScopedRoots(projectRoot = this.projectRoot) {
|
|
8945
|
+
const roots = /* @__PURE__ */ new Set([this.getCanonicalPath(projectRoot)]);
|
|
8858
8946
|
for (const kbRoot of this.config.knowledgeBases) {
|
|
8859
|
-
roots.add(this.getCanonicalPath(path15.resolve(
|
|
8947
|
+
roots.add(this.getCanonicalPath(path15.resolve(projectRoot, kbRoot)));
|
|
8860
8948
|
}
|
|
8861
8949
|
return Array.from(roots);
|
|
8862
8950
|
}
|
|
@@ -8927,14 +9015,17 @@ var Indexer = class _Indexer {
|
|
|
8927
9015
|
getLegacyBranchCatalogKey() {
|
|
8928
9016
|
return this.currentBranch || "default";
|
|
8929
9017
|
}
|
|
8930
|
-
getLegacyMigrationMetadataKey() {
|
|
8931
|
-
return `index.globalBranchMigration.${
|
|
9018
|
+
getLegacyMigrationMetadataKey(projectIdentityHash = this.projectIdentityHash) {
|
|
9019
|
+
return `index.globalBranchMigration.${projectIdentityHash}`;
|
|
8932
9020
|
}
|
|
8933
|
-
getProjectEmbeddingStrategyMetadataKey() {
|
|
8934
|
-
return `index.embeddingStrategyVersion.${
|
|
9021
|
+
getProjectEmbeddingStrategyMetadataKey(projectIdentityHash = this.projectIdentityHash) {
|
|
9022
|
+
return `index.embeddingStrategyVersion.${projectIdentityHash}`;
|
|
8935
9023
|
}
|
|
8936
|
-
getProjectForceReembedMetadataKey() {
|
|
8937
|
-
return `index.forceReembed.${
|
|
9024
|
+
getProjectForceReembedMetadataKey(projectIdentityHash = this.projectIdentityHash) {
|
|
9025
|
+
return `index.forceReembed.${projectIdentityHash}`;
|
|
9026
|
+
}
|
|
9027
|
+
getProjectMigrationFinalizedMetadataKey(projectIdentityHash = this.projectIdentityHash) {
|
|
9028
|
+
return `index.migrationFinalized.${projectIdentityHash}`;
|
|
8938
9029
|
}
|
|
8939
9030
|
getBranchMigrationMetadataKey(prefix, catalogIdentity = this.getBranchCatalogIdentity()) {
|
|
8940
9031
|
const branchKey = this.getBranchCatalogKeyFor(catalogIdentity);
|
|
@@ -9040,7 +9131,7 @@ var Indexer = class _Indexer {
|
|
|
9040
9131
|
const legacy = this.getLegacyBranchCatalogKey();
|
|
9041
9132
|
return primary === legacy ? [primary] : [primary, legacy];
|
|
9042
9133
|
}
|
|
9043
|
-
getProjectLocalScopedOwnershipIds(roots) {
|
|
9134
|
+
getProjectLocalScopedOwnershipIds(roots, projectRoot = this.projectRoot) {
|
|
9044
9135
|
const chunkIds = /* @__PURE__ */ new Set();
|
|
9045
9136
|
const symbolIds = /* @__PURE__ */ new Set();
|
|
9046
9137
|
if (!this.database) {
|
|
@@ -9048,10 +9139,10 @@ var Indexer = class _Indexer {
|
|
|
9048
9139
|
}
|
|
9049
9140
|
const projectLocalFilePaths = /* @__PURE__ */ new Set([
|
|
9050
9141
|
...Array.from(this.fileHashCache.keys()).filter(
|
|
9051
|
-
(filePath) => this.isFileInCurrentScope(filePath, roots) && this.isFileInProjectRoot(filePath)
|
|
9142
|
+
(filePath) => this.isFileInCurrentScope(filePath, roots) && this.isFileInProjectRoot(filePath, projectRoot)
|
|
9052
9143
|
),
|
|
9053
9144
|
...(this.store?.getAllMetadata() ?? []).map(({ metadata }) => metadata.filePath).filter(
|
|
9054
|
-
(filePath) => this.isFileInCurrentScope(filePath, roots) && this.isFileInProjectRoot(filePath)
|
|
9145
|
+
(filePath) => this.isFileInCurrentScope(filePath, roots) && this.isFileInProjectRoot(filePath, projectRoot)
|
|
9055
9146
|
)
|
|
9056
9147
|
]);
|
|
9057
9148
|
for (const filePath of projectLocalFilePaths) {
|
|
@@ -9064,15 +9155,16 @@ var Indexer = class _Indexer {
|
|
|
9064
9155
|
}
|
|
9065
9156
|
return { chunkIds, symbolIds };
|
|
9066
9157
|
}
|
|
9067
|
-
getProjectScopedBranchCatalogCleanupKeys(projectChunkIds, projectSymbolIds) {
|
|
9158
|
+
getProjectScopedBranchCatalogCleanupKeys(projectChunkIds, projectSymbolIds, projectRoot = this.projectRoot) {
|
|
9068
9159
|
if (this.config.scope !== "global") {
|
|
9069
9160
|
return this.getBranchCatalogCleanupKeys();
|
|
9070
9161
|
}
|
|
9071
9162
|
const keys = /* @__PURE__ */ new Set();
|
|
9072
9163
|
const projectChunkIdSet = new Set(projectChunkIds);
|
|
9073
9164
|
const projectSymbolIdSet = new Set(projectSymbolIds);
|
|
9165
|
+
const projectIdentityHash = this.getProjectIdentityHash(projectRoot);
|
|
9074
9166
|
for (const branchKey of this.database?.getAllBranches() ?? []) {
|
|
9075
|
-
if (branchKey.startsWith(`${
|
|
9167
|
+
if (branchKey.startsWith(`${projectIdentityHash}:`)) {
|
|
9076
9168
|
keys.add(branchKey);
|
|
9077
9169
|
continue;
|
|
9078
9170
|
}
|
|
@@ -9082,8 +9174,10 @@ var Indexer = class _Indexer {
|
|
|
9082
9174
|
keys.add(branchKey);
|
|
9083
9175
|
}
|
|
9084
9176
|
}
|
|
9085
|
-
|
|
9086
|
-
|
|
9177
|
+
if (projectRoot === this.projectRoot) {
|
|
9178
|
+
for (const branchKey of this.getBranchCatalogCleanupKeys()) {
|
|
9179
|
+
keys.add(branchKey);
|
|
9180
|
+
}
|
|
9087
9181
|
}
|
|
9088
9182
|
return Array.from(keys);
|
|
9089
9183
|
}
|
|
@@ -9091,10 +9185,10 @@ var Indexer = class _Indexer {
|
|
|
9091
9185
|
const canonicalFilePath = this.getCanonicalStoredFilePath(filePath);
|
|
9092
9186
|
return roots.some((root) => isPathWithinRoot2(canonicalFilePath, root));
|
|
9093
9187
|
}
|
|
9094
|
-
isFileInProjectRoot(filePath) {
|
|
9188
|
+
isFileInProjectRoot(filePath, projectRoot = this.projectRoot) {
|
|
9095
9189
|
return isPathWithinRoot2(
|
|
9096
9190
|
this.getCanonicalStoredFilePath(filePath),
|
|
9097
|
-
this.getCanonicalPath(
|
|
9191
|
+
this.getCanonicalPath(projectRoot)
|
|
9098
9192
|
);
|
|
9099
9193
|
}
|
|
9100
9194
|
clearScopedFileHashCache(roots) {
|
|
@@ -9136,12 +9230,12 @@ var Indexer = class _Indexer {
|
|
|
9136
9230
|
}
|
|
9137
9231
|
return false;
|
|
9138
9232
|
}
|
|
9139
|
-
hasForeignScopedBranchData() {
|
|
9233
|
+
hasForeignScopedBranchData(projectRoot = this.projectRoot, roots = this.getScopedRoots(projectRoot)) {
|
|
9140
9234
|
if (!this.database || this.config.scope !== "global") {
|
|
9141
9235
|
return false;
|
|
9142
9236
|
}
|
|
9143
|
-
const
|
|
9144
|
-
const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots);
|
|
9237
|
+
const projectIdentityHash = this.getProjectIdentityHash(projectRoot);
|
|
9238
|
+
const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots, projectRoot);
|
|
9145
9239
|
return this.database.getAllBranches().some(
|
|
9146
9240
|
(branchKey) => {
|
|
9147
9241
|
const branchChunkIds = this.database.getBranchChunkIds(branchKey);
|
|
@@ -9150,7 +9244,7 @@ var Indexer = class _Indexer {
|
|
|
9150
9244
|
if (!hasBranchData) {
|
|
9151
9245
|
return false;
|
|
9152
9246
|
}
|
|
9153
|
-
if (branchKey.startsWith(`${
|
|
9247
|
+
if (branchKey.startsWith(`${projectIdentityHash}:`)) {
|
|
9154
9248
|
return false;
|
|
9155
9249
|
}
|
|
9156
9250
|
const referencesCurrentProjectChunks = branchChunkIds.some((chunkId) => projectLocalChunkIds.has(chunkId));
|
|
@@ -9159,7 +9253,7 @@ var Indexer = class _Indexer {
|
|
|
9159
9253
|
}
|
|
9160
9254
|
);
|
|
9161
9255
|
}
|
|
9162
|
-
clearSharedIndexProjectData(store, invertedIndex, database, roots) {
|
|
9256
|
+
clearSharedIndexProjectData(store, invertedIndex, database, roots, projectRoot = this.projectRoot) {
|
|
9163
9257
|
const allMetadata = store.getAllMetadata();
|
|
9164
9258
|
const scopedEntries = allMetadata.filter(({ metadata }) => this.isFileInCurrentScope(metadata.filePath, roots));
|
|
9165
9259
|
const filePaths = /* @__PURE__ */ new Set([
|
|
@@ -9167,7 +9261,7 @@ var Indexer = class _Indexer {
|
|
|
9167
9261
|
...scopedEntries.map(({ metadata }) => metadata.filePath)
|
|
9168
9262
|
]);
|
|
9169
9263
|
const projectLocalFilePaths = new Set(
|
|
9170
|
-
Array.from(filePaths).filter((filePath) => this.isFileInProjectRoot(filePath))
|
|
9264
|
+
Array.from(filePaths).filter((filePath) => this.isFileInProjectRoot(filePath, projectRoot))
|
|
9171
9265
|
);
|
|
9172
9266
|
const removedChunkIds = new Set(scopedEntries.map(({ key }) => key));
|
|
9173
9267
|
for (const filePath of filePaths) {
|
|
@@ -9177,7 +9271,7 @@ var Indexer = class _Indexer {
|
|
|
9177
9271
|
}
|
|
9178
9272
|
const removedChunkIdList = Array.from(removedChunkIds);
|
|
9179
9273
|
const projectLocalChunkIds = new Set(
|
|
9180
|
-
scopedEntries.filter(({ metadata }) => this.isFileInProjectRoot(metadata.filePath)).map(({ key }) => key)
|
|
9274
|
+
scopedEntries.filter(({ metadata }) => this.isFileInProjectRoot(metadata.filePath, projectRoot)).map(({ key }) => key)
|
|
9181
9275
|
);
|
|
9182
9276
|
for (const filePath of projectLocalFilePaths) {
|
|
9183
9277
|
for (const chunk of database.getChunksByFile(filePath)) {
|
|
@@ -9196,7 +9290,8 @@ var Indexer = class _Indexer {
|
|
|
9196
9290
|
}
|
|
9197
9291
|
const branchCleanupKeys = this.getProjectScopedBranchCatalogCleanupKeys(
|
|
9198
9292
|
Array.from(projectLocalChunkIds),
|
|
9199
|
-
Array.from(projectLocalSymbolIds)
|
|
9293
|
+
Array.from(projectLocalSymbolIds),
|
|
9294
|
+
projectRoot
|
|
9200
9295
|
);
|
|
9201
9296
|
for (const branchKey of branchCleanupKeys) {
|
|
9202
9297
|
database.deleteBranchChunksForBranch(branchKey, removedChunkIdList);
|
|
@@ -9231,29 +9326,96 @@ var Indexer = class _Indexer {
|
|
|
9231
9326
|
database.gcOrphanSymbols();
|
|
9232
9327
|
database.gcOrphanEmbeddings();
|
|
9233
9328
|
database.gcOrphanChunks();
|
|
9234
|
-
store.save();
|
|
9235
9329
|
this.saveInvertedIndex(invertedIndex);
|
|
9330
|
+
store.save();
|
|
9236
9331
|
return {
|
|
9237
9332
|
removedChunkIds: removedChunkIdList,
|
|
9238
9333
|
hasForeignData: allMetadata.some(({ metadata }) => !this.isFileInCurrentScope(metadata.filePath, roots))
|
|
9239
9334
|
};
|
|
9240
9335
|
}
|
|
9336
|
+
getCurrentClearRecoveryState() {
|
|
9337
|
+
if (!this.configuredProviderInfo) {
|
|
9338
|
+
throw new Error("Cannot persist clear recovery state before the embedding provider is initialized");
|
|
9339
|
+
}
|
|
9340
|
+
const compatibility = this.checkCompatibility();
|
|
9341
|
+
const compatibilityDecision = compatibility.compatible ? "compatible" : compatibility.code === "EMBEDDING_STRATEGY_MISMATCH" /* EMBEDDING_STRATEGY_MISMATCH */ ? "embedding-strategy-mismatch" : "incompatible";
|
|
9342
|
+
return {
|
|
9343
|
+
phase: "clearing",
|
|
9344
|
+
embeddingProvider: this.configuredProviderInfo.provider,
|
|
9345
|
+
embeddingModel: this.configuredProviderInfo.modelInfo.model,
|
|
9346
|
+
embeddingDimensions: this.configuredProviderInfo.modelInfo.dimensions,
|
|
9347
|
+
embeddingStrategyVersion: EMBEDDING_STRATEGY_VERSION,
|
|
9348
|
+
compatibilityDecision
|
|
9349
|
+
};
|
|
9350
|
+
}
|
|
9351
|
+
beginClearRecoveryState() {
|
|
9352
|
+
const recovery = this.getCurrentClearRecoveryState();
|
|
9353
|
+
setIndexLockClearRecoveryState(this.requireActiveLease(), recovery);
|
|
9354
|
+
return recovery;
|
|
9355
|
+
}
|
|
9356
|
+
finishClearRecoveryState() {
|
|
9357
|
+
setIndexLockClearRecoveryState(this.requireActiveLease(), null);
|
|
9358
|
+
}
|
|
9359
|
+
matchesCurrentClearRecoveryConfiguration(recovery) {
|
|
9360
|
+
const configuredProviderInfo = this.configuredProviderInfo;
|
|
9361
|
+
return configuredProviderInfo !== null && recovery.embeddingProvider === configuredProviderInfo.provider && recovery.embeddingModel === configuredProviderInfo.modelInfo.model && recovery.embeddingDimensions === configuredProviderInfo.modelInfo.dimensions && recovery.embeddingStrategyVersion === EMBEDDING_STRATEGY_VERSION;
|
|
9362
|
+
}
|
|
9363
|
+
hasUnknownLegacyForceIndexClear(owner) {
|
|
9364
|
+
return owner.operation === "force-index" && owner.clearRecovery === void 0 && owner.recoveryProtocolVersion !== 1 && existsSync8(path15.join(this.indexPath, "force-index-phase"));
|
|
9365
|
+
}
|
|
9241
9366
|
async recoverFromInterruptedIndexingUnlocked(owners) {
|
|
9242
9367
|
for (const owner of owners) {
|
|
9243
9368
|
this.logger.warn("Detected interrupted indexing session, recovering...", {
|
|
9244
9369
|
pid: owner.pid,
|
|
9245
9370
|
hostname: owner.hostname,
|
|
9246
9371
|
operation: owner.operation,
|
|
9247
|
-
startedAt: owner.startedAt
|
|
9372
|
+
startedAt: owner.startedAt,
|
|
9373
|
+
projectRoot: owner.projectRoot
|
|
9248
9374
|
});
|
|
9249
9375
|
}
|
|
9250
9376
|
if (this.config.scope === "global") {
|
|
9251
|
-
|
|
9252
|
-
|
|
9377
|
+
const clearScopes = [];
|
|
9378
|
+
for (const owner of owners) {
|
|
9379
|
+
if (this.hasUnknownLegacyForceIndexClear(owner)) {
|
|
9380
|
+
throw new Error(
|
|
9381
|
+
`Cannot automatically recover interrupted force-index ${owner.token}: the legacy clearing phase ownership is unknown. The recovery marker was retained for manual inspection.`
|
|
9382
|
+
);
|
|
9383
|
+
}
|
|
9384
|
+
if (owner.operation === "clear" && owner.clearRecovery === void 0 && owner.recoveryProtocolVersion !== 1) {
|
|
9385
|
+
throw new Error(
|
|
9386
|
+
`Cannot automatically recover interrupted global clear ${owner.token}: the originating recovery state is unknown. The recovery marker was retained for manual inspection.`
|
|
9387
|
+
);
|
|
9388
|
+
}
|
|
9389
|
+
if (owner.clearRecovery === void 0) continue;
|
|
9390
|
+
if (!owner.projectRoot || !owner.scopedRoots || owner.scopedRoots.length === 0) {
|
|
9391
|
+
throw new Error(
|
|
9392
|
+
`Cannot automatically recover interrupted global clear ${owner.token}: the originating project scope is unknown. The recovery marker was retained for manual inspection.`
|
|
9393
|
+
);
|
|
9394
|
+
}
|
|
9395
|
+
if (!this.matchesCurrentClearRecoveryConfiguration(owner.clearRecovery)) {
|
|
9396
|
+
throw new Error(
|
|
9397
|
+
`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.`
|
|
9398
|
+
);
|
|
9399
|
+
}
|
|
9400
|
+
clearScopes.push({
|
|
9401
|
+
projectRoot: owner.projectRoot,
|
|
9402
|
+
scopedRoots: owner.scopedRoots,
|
|
9403
|
+
compatibilityDecision: owner.clearRecovery.compatibilityDecision
|
|
9404
|
+
});
|
|
9405
|
+
}
|
|
9406
|
+
if (clearScopes.length > 0) {
|
|
9407
|
+
this.loadFileHashCache();
|
|
9408
|
+
}
|
|
9409
|
+
for (const { projectRoot, scopedRoots, compatibilityDecision } of clearScopes) {
|
|
9410
|
+
this.clearGlobalIndexUnlocked(projectRoot, scopedRoots, compatibilityDecision);
|
|
9253
9411
|
}
|
|
9254
9412
|
await this.healthCheckUnlocked();
|
|
9413
|
+
this.logger.info(
|
|
9414
|
+
clearScopes.length > 0 ? "Recovery complete, next index will rebuild all files" : "Recovery complete, next index will resume from the last checkpoint"
|
|
9415
|
+
);
|
|
9416
|
+
return;
|
|
9255
9417
|
}
|
|
9256
|
-
this.logger.info("Recovery complete, next index will
|
|
9418
|
+
this.logger.info("Recovery complete, next index will resume from the last checkpoint");
|
|
9257
9419
|
}
|
|
9258
9420
|
*loadSerializedFailedBatches() {
|
|
9259
9421
|
let warned = false;
|
|
@@ -9291,33 +9453,118 @@ var Indexer = class _Indexer {
|
|
|
9291
9453
|
state.writer.write(record);
|
|
9292
9454
|
state.recordsWritten += record.chunks.length;
|
|
9293
9455
|
}
|
|
9294
|
-
finalizeFailedBatchWriteState(state) {
|
|
9456
|
+
finalizeFailedBatchWriteState(state, resolvedChunkIds = /* @__PURE__ */ new Set()) {
|
|
9295
9457
|
if (state.recordsWritten > 0) {
|
|
9296
|
-
|
|
9458
|
+
const seenChunkIds = /* @__PURE__ */ new Set();
|
|
9459
|
+
const retained = [];
|
|
9460
|
+
const records = Array.from(readFailedBatchRecords(state.writer.temporaryPath));
|
|
9461
|
+
for (let i = records.length - 1; i >= 0; i--) {
|
|
9462
|
+
const chunks = records[i].chunks.filter((rawChunk) => {
|
|
9463
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
9464
|
+
if (chunkId !== null) {
|
|
9465
|
+
if (resolvedChunkIds.has(chunkId)) return false;
|
|
9466
|
+
if (seenChunkIds.has(chunkId)) return false;
|
|
9467
|
+
seenChunkIds.add(chunkId);
|
|
9468
|
+
}
|
|
9469
|
+
return true;
|
|
9470
|
+
});
|
|
9471
|
+
if (chunks.length > 0) {
|
|
9472
|
+
retained.unshift({ ...records[i], chunks });
|
|
9473
|
+
}
|
|
9474
|
+
}
|
|
9475
|
+
state.writer.cleanup();
|
|
9476
|
+
if (retained.length > 0) {
|
|
9477
|
+
writeFailedBatchRecords(this.failedBatchesPath, retained);
|
|
9478
|
+
} else {
|
|
9479
|
+
writeFailedBatchRecords(this.failedBatchesPath, []);
|
|
9480
|
+
this.clearFailedBatchState();
|
|
9481
|
+
}
|
|
9297
9482
|
return;
|
|
9298
9483
|
}
|
|
9299
|
-
state.writer.
|
|
9484
|
+
state.writer.commit();
|
|
9300
9485
|
this.clearFailedBatchState();
|
|
9301
9486
|
}
|
|
9302
|
-
|
|
9303
|
-
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
}
|
|
9308
|
-
}
|
|
9487
|
+
getCheckpointIntervalChunks(totalChunks) {
|
|
9488
|
+
return Math.max(
|
|
9489
|
+
this.checkpointIntervalChunks ?? 2e3,
|
|
9490
|
+
Math.floor(totalChunks / 10)
|
|
9491
|
+
);
|
|
9309
9492
|
}
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
|
|
9314
|
-
|
|
9315
|
-
|
|
9316
|
-
|
|
9317
|
-
|
|
9318
|
-
|
|
9319
|
-
|
|
9320
|
-
|
|
9493
|
+
checkpointIndexRun(database, store, invertedIndex, failedProcessing, resolvedRetryChunkIds, currentFileHashes, committedFilePaths, scopedRoots, configuredProviderInfo) {
|
|
9494
|
+
if (!this.hasProjectForceReembedPending()) {
|
|
9495
|
+
this.saveIndexMetadata(configuredProviderInfo);
|
|
9496
|
+
this.indexCompatibility = { compatible: true };
|
|
9497
|
+
}
|
|
9498
|
+
database.commitWriteTransaction();
|
|
9499
|
+
database.beginWriteTransaction();
|
|
9500
|
+
this.saveInvertedIndex(invertedIndex);
|
|
9501
|
+
store.save();
|
|
9502
|
+
if (failedProcessing.state.recordsWritten > 0 || failedProcessing.latestById.size > 0 || failedProcessing.discardedExistingRecords) {
|
|
9503
|
+
for (const metadata of failedProcessing.latestById.values()) {
|
|
9504
|
+
const alreadyMaterialized = metadata.chunks.some((rawChunk) => {
|
|
9505
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
9506
|
+
return chunkId !== null && failedProcessing.materializedRetryIds.has(chunkId);
|
|
9507
|
+
});
|
|
9508
|
+
if (alreadyMaterialized) continue;
|
|
9509
|
+
this.writeFailedBatchRecord(failedProcessing.state, {
|
|
9510
|
+
chunks: metadata.chunks,
|
|
9511
|
+
attemptCount: metadata.attemptCount,
|
|
9512
|
+
error: metadata.error,
|
|
9513
|
+
lastAttempt: metadata.lastAttempt
|
|
9514
|
+
});
|
|
9515
|
+
for (const rawChunk of metadata.chunks) {
|
|
9516
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
9517
|
+
if (chunkId !== null) {
|
|
9518
|
+
failedProcessing.materializedRetryIds.add(chunkId);
|
|
9519
|
+
}
|
|
9520
|
+
}
|
|
9521
|
+
}
|
|
9522
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state, resolvedRetryChunkIds);
|
|
9523
|
+
failedProcessing.state = this.createFailedBatchWriteState();
|
|
9524
|
+
failedProcessing.discardedExistingRecords = false;
|
|
9525
|
+
for (const record of this.loadSerializedFailedBatches()) {
|
|
9526
|
+
for (const rawChunk of record.chunks) {
|
|
9527
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
9528
|
+
this.writeFailedBatchRecord(failedProcessing.state, { ...record, chunks: [rawChunk] });
|
|
9529
|
+
if (chunkId !== null) {
|
|
9530
|
+
failedProcessing.materializedRetryIds.add(chunkId);
|
|
9531
|
+
}
|
|
9532
|
+
}
|
|
9533
|
+
}
|
|
9534
|
+
}
|
|
9535
|
+
const partialHashes = /* @__PURE__ */ new Map();
|
|
9536
|
+
for (const filePath of committedFilePaths) {
|
|
9537
|
+
const hash = currentFileHashes.get(filePath);
|
|
9538
|
+
if (hash !== void 0) {
|
|
9539
|
+
partialHashes.set(filePath, hash);
|
|
9540
|
+
}
|
|
9541
|
+
}
|
|
9542
|
+
if (scopedRoots) {
|
|
9543
|
+
this.replaceScopedFileHashCache(partialHashes, scopedRoots);
|
|
9544
|
+
} else {
|
|
9545
|
+
this.fileHashCache = partialHashes;
|
|
9546
|
+
this.saveFileHashCache();
|
|
9547
|
+
}
|
|
9548
|
+
}
|
|
9549
|
+
clearFailedBatchState() {
|
|
9550
|
+
if (existsSync8(this.failedBatchesPath)) {
|
|
9551
|
+
try {
|
|
9552
|
+
unlinkSync2(this.failedBatchesPath);
|
|
9553
|
+
} catch {
|
|
9554
|
+
}
|
|
9555
|
+
}
|
|
9556
|
+
}
|
|
9557
|
+
rewriteFailedBatchState(shouldRetain) {
|
|
9558
|
+
const state = this.createFailedBatchWriteState();
|
|
9559
|
+
try {
|
|
9560
|
+
for (const batch of this.loadSerializedFailedBatches()) {
|
|
9561
|
+
const retainedChunks = batch.chunks.filter(shouldRetain);
|
|
9562
|
+
if (retainedChunks.length > 0) {
|
|
9563
|
+
this.writeFailedBatchRecord(state, { ...batch, chunks: retainedChunks });
|
|
9564
|
+
}
|
|
9565
|
+
}
|
|
9566
|
+
this.finalizeFailedBatchWriteState(state);
|
|
9567
|
+
} catch (error) {
|
|
9321
9568
|
state.writer.cleanup();
|
|
9322
9569
|
throw error;
|
|
9323
9570
|
}
|
|
@@ -9325,6 +9572,7 @@ var Indexer = class _Indexer {
|
|
|
9325
9572
|
prepareFailedBatchProcessing(roots, shouldProcess) {
|
|
9326
9573
|
const state = this.createFailedBatchWriteState();
|
|
9327
9574
|
const latestById = /* @__PURE__ */ new Map();
|
|
9575
|
+
let discardedExistingRecords = false;
|
|
9328
9576
|
try {
|
|
9329
9577
|
for (const batch of this.loadSerializedFailedBatches()) {
|
|
9330
9578
|
for (const rawChunk of batch.chunks) {
|
|
@@ -9335,10 +9583,12 @@ var Indexer = class _Indexer {
|
|
|
9335
9583
|
continue;
|
|
9336
9584
|
}
|
|
9337
9585
|
if (!shouldProcess(filePath)) {
|
|
9586
|
+
discardedExistingRecords = true;
|
|
9338
9587
|
continue;
|
|
9339
9588
|
}
|
|
9340
9589
|
const chunkId = getPendingChunkId(rawChunk);
|
|
9341
9590
|
if (!chunkId) {
|
|
9591
|
+
discardedExistingRecords = true;
|
|
9342
9592
|
continue;
|
|
9343
9593
|
}
|
|
9344
9594
|
const existing = latestById.get(chunkId);
|
|
@@ -9346,12 +9596,18 @@ var Indexer = class _Indexer {
|
|
|
9346
9596
|
latestById.set(chunkId, {
|
|
9347
9597
|
attemptCount: batch.attemptCount,
|
|
9348
9598
|
error: batch.error,
|
|
9349
|
-
lastAttempt: batch.lastAttempt
|
|
9599
|
+
lastAttempt: batch.lastAttempt,
|
|
9600
|
+
chunks: [rawChunk]
|
|
9350
9601
|
});
|
|
9351
9602
|
}
|
|
9352
9603
|
}
|
|
9353
9604
|
}
|
|
9354
|
-
return {
|
|
9605
|
+
return {
|
|
9606
|
+
state,
|
|
9607
|
+
latestById,
|
|
9608
|
+
materializedRetryIds: /* @__PURE__ */ new Set(),
|
|
9609
|
+
discardedExistingRecords
|
|
9610
|
+
};
|
|
9355
9611
|
} catch (error) {
|
|
9356
9612
|
state.writer.cleanup();
|
|
9357
9613
|
throw error;
|
|
@@ -9387,10 +9643,34 @@ var Indexer = class _Indexer {
|
|
|
9387
9643
|
}
|
|
9388
9644
|
}
|
|
9389
9645
|
}
|
|
9646
|
+
restoreMissingChunkRows(database, chunks) {
|
|
9647
|
+
const missing = [];
|
|
9648
|
+
for (const chunk of chunks) {
|
|
9649
|
+
if (database.getChunk(chunk.id)) {
|
|
9650
|
+
continue;
|
|
9651
|
+
}
|
|
9652
|
+
missing.push({
|
|
9653
|
+
chunkId: chunk.id,
|
|
9654
|
+
contentHash: chunk.contentHash,
|
|
9655
|
+
filePath: chunk.metadata.filePath,
|
|
9656
|
+
startLine: chunk.metadata.startLine,
|
|
9657
|
+
endLine: chunk.metadata.endLine,
|
|
9658
|
+
nodeType: chunk.metadata.chunkType,
|
|
9659
|
+
name: chunk.metadata.name,
|
|
9660
|
+
language: chunk.metadata.language,
|
|
9661
|
+
blameSha: chunk.metadata.blameSha,
|
|
9662
|
+
blameAuthor: chunk.metadata.blameAuthor,
|
|
9663
|
+
blameAuthorEmail: chunk.metadata.blameAuthorEmail,
|
|
9664
|
+
blameCommittedAt: chunk.metadata.blameCommittedAt,
|
|
9665
|
+
blameSummary: chunk.metadata.blameSummary
|
|
9666
|
+
});
|
|
9667
|
+
}
|
|
9668
|
+
if (missing.length > 0) {
|
|
9669
|
+
database.upsertChunksBatch(missing);
|
|
9670
|
+
}
|
|
9671
|
+
}
|
|
9390
9672
|
getProviderRateLimits(provider) {
|
|
9391
9673
|
switch (provider) {
|
|
9392
|
-
case "github-copilot":
|
|
9393
|
-
return { concurrency: 1, intervalMs: 4e3, minRetryMs: 5e3, maxRetryMs: 6e4 };
|
|
9394
9674
|
case "openai":
|
|
9395
9675
|
return { concurrency: 3, intervalMs: 500, minRetryMs: 1e3, maxRetryMs: 3e4 };
|
|
9396
9676
|
case "google":
|
|
@@ -9459,16 +9739,17 @@ var Indexer = class _Indexer {
|
|
|
9459
9739
|
const embeddingPartsByChunk = /* @__PURE__ */ new Map();
|
|
9460
9740
|
const completedVectorsByChunkId = /* @__PURE__ */ new Map();
|
|
9461
9741
|
const completedChunkIds = /* @__PURE__ */ new Set();
|
|
9462
|
-
const
|
|
9463
|
-
|
|
9464
|
-
|
|
9465
|
-
|
|
9742
|
+
const batchOptions = getDynamicBatchOptions(options.configuredProviderInfo, this.config.embedding?.batch);
|
|
9743
|
+
if (options.forceSingleItemBatches && options.configuredProviderInfo.provider === "ollama") {
|
|
9744
|
+
batchOptions.maxBatchItems = 1;
|
|
9745
|
+
}
|
|
9746
|
+
const requestBatches = createPendingEmbeddingRequestBatches(chunksNeedingEmbedding, batchOptions);
|
|
9466
9747
|
let fatalError;
|
|
9467
9748
|
for (const requestBatch of requestBatches) {
|
|
9468
9749
|
await options.queue.onSizeLessThan(Math.max(1, options.providerRateLimits.concurrency));
|
|
9469
9750
|
const task = options.queue.add(async () => {
|
|
9470
9751
|
if (options.rateLimitState.backoffMs > 0) {
|
|
9471
|
-
await new Promise((
|
|
9752
|
+
await new Promise((resolve20) => setTimeout(resolve20, options.rateLimitState.backoffMs));
|
|
9472
9753
|
}
|
|
9473
9754
|
try {
|
|
9474
9755
|
const embeddingResult = await pRetry(
|
|
@@ -10025,7 +10306,7 @@ var Indexer = class _Indexer {
|
|
|
10025
10306
|
}
|
|
10026
10307
|
if (!this.configuredProviderInfo) {
|
|
10027
10308
|
throw new Error(
|
|
10028
|
-
"No embedding provider available. Configure
|
|
10309
|
+
"No embedding provider available. Configure OpenAI, Google, Ollama, or a custom OpenAI-compatible endpoint."
|
|
10029
10310
|
);
|
|
10030
10311
|
}
|
|
10031
10312
|
this.logger.info("Initializing indexer", {
|
|
@@ -10035,15 +10316,6 @@ var Indexer = class _Indexer {
|
|
|
10035
10316
|
rerankerEnabled: this.config.reranker?.enabled ?? false
|
|
10036
10317
|
});
|
|
10037
10318
|
this.provider = createEmbeddingProvider(this.configuredProviderInfo);
|
|
10038
|
-
if (this.config.reranker?.enabled) {
|
|
10039
|
-
this.reranker = createReranker(this.config.reranker);
|
|
10040
|
-
if (this.reranker.isAvailable()) {
|
|
10041
|
-
this.logger.info("Reranker initialized", {
|
|
10042
|
-
model: this.config.reranker.model,
|
|
10043
|
-
baseUrl: this.config.reranker.baseUrl
|
|
10044
|
-
});
|
|
10045
|
-
}
|
|
10046
|
-
}
|
|
10047
10319
|
const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
|
|
10048
10320
|
const storePath = path15.join(this.indexPath, "vectors");
|
|
10049
10321
|
const vectorMetadataPath = `${storePath}.meta.json`;
|
|
@@ -10065,7 +10337,20 @@ var Indexer = class _Indexer {
|
|
|
10065
10337
|
]);
|
|
10066
10338
|
}
|
|
10067
10339
|
if (recoveredOwners.length > 0 && this.config.scope === "project") {
|
|
10068
|
-
|
|
10340
|
+
const unknownLegacyForceIndex = recoveredOwners.find(
|
|
10341
|
+
(owner) => this.hasUnknownLegacyForceIndexClear(owner)
|
|
10342
|
+
);
|
|
10343
|
+
if (unknownLegacyForceIndex) {
|
|
10344
|
+
throw new Error(
|
|
10345
|
+
`Cannot automatically recover interrupted force-index ${unknownLegacyForceIndex.token}: the legacy clearing phase ownership is unknown. The recovery marker was retained for manual inspection.`
|
|
10346
|
+
);
|
|
10347
|
+
}
|
|
10348
|
+
const shouldReset = recoveredOwners.some(
|
|
10349
|
+
(owner) => owner.clearRecovery !== void 0 || owner.operation === "clear" && owner.recoveryProtocolVersion !== 1
|
|
10350
|
+
);
|
|
10351
|
+
if (shouldReset) {
|
|
10352
|
+
await this.resetLocalIndexArtifacts();
|
|
10353
|
+
}
|
|
10069
10354
|
}
|
|
10070
10355
|
this.store = new VectorStore(storePath, dimensions);
|
|
10071
10356
|
if (existsSync8(storePath) || existsSync8(vectorMetadataPath)) {
|
|
@@ -10701,7 +10986,17 @@ var Indexer = class _Indexer {
|
|
|
10701
10986
|
const needsCallGraphResolutionMigration = database.getMetadata(this.getCallGraphResolutionMetadataKey()) !== CALL_GRAPH_RESOLUTION_VERSION;
|
|
10702
10987
|
for (const file of files) {
|
|
10703
10988
|
const storedPath = this.toStoredFilePath(file.path);
|
|
10704
|
-
|
|
10989
|
+
let currentHash;
|
|
10990
|
+
try {
|
|
10991
|
+
currentHash = hashFile(file.path);
|
|
10992
|
+
} catch (error) {
|
|
10993
|
+
stats.skippedFiles.push({ path: this.toCanonicalFilePath(file.path), reason: "unreadable" });
|
|
10994
|
+
this.logger.warn("Skipped unreadable file during indexing", {
|
|
10995
|
+
path: file.path,
|
|
10996
|
+
error: getErrorMessage3(error)
|
|
10997
|
+
});
|
|
10998
|
+
continue;
|
|
10999
|
+
}
|
|
10705
11000
|
currentFileHashes.set(storedPath, currentHash);
|
|
10706
11001
|
const cachedHashMatches = this.fileHashCache.get(storedPath) === currentHash;
|
|
10707
11002
|
const needsCallGraphRefresh = cachedHashMatches && needsCallGraphResolutionMigration && database.getChunksByFile(storedPath).some(
|
|
@@ -10709,7 +11004,8 @@ var Indexer = class _Indexer {
|
|
|
10709
11004
|
);
|
|
10710
11005
|
const requiresSwiftParserUpgrade = reparseCachedSwiftFiles && path15.extname(storedPath).toLowerCase() === ".swift";
|
|
10711
11006
|
const requiresMetalParserUpgrade = reparseCachedMetalFiles && path15.extname(storedPath).toLowerCase() === ".metal";
|
|
10712
|
-
|
|
11007
|
+
const inMigrationScope = forceScopedReembed && scopedRoots !== null && this.isFileInCurrentScope(storedPath, scopedRoots);
|
|
11008
|
+
if (cachedHashMatches && !inMigrationScope && !needsCallGraphRefresh && !requiresSwiftParserUpgrade && !requiresMetalParserUpgrade && !refreshCachedSymbols) {
|
|
10713
11009
|
unchangedFilePaths.add(storedPath);
|
|
10714
11010
|
this.logger.recordCacheHit();
|
|
10715
11011
|
} else {
|
|
@@ -10835,6 +11131,9 @@ var Indexer = class _Indexer {
|
|
|
10835
11131
|
}
|
|
10836
11132
|
}
|
|
10837
11133
|
let processedChangedFiles = 0;
|
|
11134
|
+
let lastCheckpointChunks = 0;
|
|
11135
|
+
const committedFilePaths = new Set(unchangedFilePaths);
|
|
11136
|
+
const resolvedRetryChunkIds = /* @__PURE__ */ new Set();
|
|
10838
11137
|
for (const descriptorBatch of iterateOrderedFileBatches(
|
|
10839
11138
|
changedFileDescriptors,
|
|
10840
11139
|
(descriptor) => descriptor.sourceBytes,
|
|
@@ -10848,7 +11147,7 @@ var Indexer = class _Indexer {
|
|
|
10848
11147
|
const loadedByPath = new Map(loadedFiles.map((file) => [file.path, file]));
|
|
10849
11148
|
const descriptorByPath = new Map(descriptorBatch.map((descriptor) => [descriptor.storedPath, descriptor]));
|
|
10850
11149
|
const parseStartTime = performance2.now();
|
|
10851
|
-
const parsedFiles = parseFiles(loadedFiles);
|
|
11150
|
+
const parsedFiles = parseFiles(loadedFiles, this.config.indexing.linesPerChunk);
|
|
10852
11151
|
const parseMs = performance2.now() - parseStartTime;
|
|
10853
11152
|
this.logger.recordFilesParsed(parsedFiles.length);
|
|
10854
11153
|
this.logger.recordParseDuration(parseMs);
|
|
@@ -10871,7 +11170,7 @@ var Indexer = class _Indexer {
|
|
|
10871
11170
|
}
|
|
10872
11171
|
let chunksToProcess = parsed.chunks;
|
|
10873
11172
|
if (this.config.indexing.fallbackToTextOnMaxChunks && chunksToProcess.length > this.config.indexing.maxChunksPerFile) {
|
|
10874
|
-
chunksToProcess = parseFileAsText(parsed.path, loadedFile.content);
|
|
11173
|
+
chunksToProcess = parseFileAsText(parsed.path, loadedFile.content, this.config.indexing.linesPerChunk);
|
|
10875
11174
|
}
|
|
10876
11175
|
chunksToProcess = selectIndexableChunks(
|
|
10877
11176
|
chunksToProcess,
|
|
@@ -11005,6 +11304,10 @@ var Indexer = class _Indexer {
|
|
|
11005
11304
|
}
|
|
11006
11305
|
if (symbolBatch.length > 0) {
|
|
11007
11306
|
database.upsertSymbolsBatch(symbolBatch);
|
|
11307
|
+
database.addSymbolsToBranchBatch(
|
|
11308
|
+
this.getBranchCatalogKey(),
|
|
11309
|
+
symbolBatch.map((symbol) => symbol.id)
|
|
11310
|
+
);
|
|
11008
11311
|
}
|
|
11009
11312
|
if (edgeBatch.length > 0) {
|
|
11010
11313
|
database.upsertCallEdgesBatch(edgeBatch);
|
|
@@ -11040,6 +11343,12 @@ var Indexer = class _Indexer {
|
|
|
11040
11343
|
forceReembed: forceScopedReembed,
|
|
11041
11344
|
reuseCachedEmbeddings: true,
|
|
11042
11345
|
incrementRepeatedFailures: true,
|
|
11346
|
+
onSucceeded: (succeededChunks) => {
|
|
11347
|
+
database.addChunksToBranchBatch(
|
|
11348
|
+
this.getBranchCatalogKey(),
|
|
11349
|
+
succeededChunks.map((chunk) => chunk.id)
|
|
11350
|
+
);
|
|
11351
|
+
},
|
|
11043
11352
|
onProgress: (batchProgress) => onProgress?.({
|
|
11044
11353
|
phase: "embedding",
|
|
11045
11354
|
filesProcessed: unchangedFilePaths.size + processedChangedFiles,
|
|
@@ -11058,6 +11367,27 @@ var Indexer = class _Indexer {
|
|
|
11058
11367
|
}
|
|
11059
11368
|
}
|
|
11060
11369
|
}
|
|
11370
|
+
for (const descriptor of descriptorBatch) {
|
|
11371
|
+
const existingFileChunks = existingChunksByFile.get(descriptor.storedPath);
|
|
11372
|
+
if (!existingFileChunks || existingFileChunks.size === 0) {
|
|
11373
|
+
committedFilePaths.add(descriptor.storedPath);
|
|
11374
|
+
}
|
|
11375
|
+
}
|
|
11376
|
+
const checkpointInterval = this.getCheckpointIntervalChunks(stats.totalChunks);
|
|
11377
|
+
if (stats.totalChunks - lastCheckpointChunks >= checkpointInterval) {
|
|
11378
|
+
lastCheckpointChunks = stats.totalChunks;
|
|
11379
|
+
this.checkpointIndexRun(
|
|
11380
|
+
database,
|
|
11381
|
+
store,
|
|
11382
|
+
invertedIndex,
|
|
11383
|
+
failedProcessing,
|
|
11384
|
+
resolvedRetryChunkIds,
|
|
11385
|
+
currentFileHashes,
|
|
11386
|
+
committedFilePaths,
|
|
11387
|
+
scopedRoots,
|
|
11388
|
+
configuredProviderInfo
|
|
11389
|
+
);
|
|
11390
|
+
}
|
|
11061
11391
|
}
|
|
11062
11392
|
const retryableFailedChunks = this.iterateLatestFailedChunks(
|
|
11063
11393
|
failedProcessing.latestById,
|
|
@@ -11078,6 +11408,7 @@ var Indexer = class _Indexer {
|
|
|
11078
11408
|
retryableChunksWithExistingData.add(chunk.id);
|
|
11079
11409
|
}
|
|
11080
11410
|
}
|
|
11411
|
+
this.restoreMissingChunkRows(database, pendingChunks);
|
|
11081
11412
|
stats.totalChunks += pendingChunks.length;
|
|
11082
11413
|
onProgress?.({
|
|
11083
11414
|
phase: "embedding",
|
|
@@ -11100,6 +11431,17 @@ var Indexer = class _Indexer {
|
|
|
11100
11431
|
forceReembed: forceScopedReembed,
|
|
11101
11432
|
reuseCachedEmbeddings: true,
|
|
11102
11433
|
incrementRepeatedFailures: true,
|
|
11434
|
+
forceSingleItemBatches: true,
|
|
11435
|
+
onSucceeded: (succeededChunks) => {
|
|
11436
|
+
database.addChunksToBranchBatch(
|
|
11437
|
+
this.getBranchCatalogKey(),
|
|
11438
|
+
succeededChunks.map((chunk) => chunk.id)
|
|
11439
|
+
);
|
|
11440
|
+
for (const chunk of succeededChunks) {
|
|
11441
|
+
failedProcessing.latestById.delete(chunk.id);
|
|
11442
|
+
resolvedRetryChunkIds.add(chunk.id);
|
|
11443
|
+
}
|
|
11444
|
+
},
|
|
11103
11445
|
onProgress: (batchProgress) => onProgress?.({
|
|
11104
11446
|
phase: "embedding",
|
|
11105
11447
|
filesProcessed: files.length,
|
|
@@ -11117,6 +11459,20 @@ var Indexer = class _Indexer {
|
|
|
11117
11459
|
failedForcedChunkIds.add(chunkId);
|
|
11118
11460
|
}
|
|
11119
11461
|
}
|
|
11462
|
+
if (stats.totalChunks - lastCheckpointChunks >= this.getCheckpointIntervalChunks(stats.totalChunks)) {
|
|
11463
|
+
lastCheckpointChunks = stats.totalChunks;
|
|
11464
|
+
this.checkpointIndexRun(
|
|
11465
|
+
database,
|
|
11466
|
+
store,
|
|
11467
|
+
invertedIndex,
|
|
11468
|
+
failedProcessing,
|
|
11469
|
+
resolvedRetryChunkIds,
|
|
11470
|
+
currentFileHashes,
|
|
11471
|
+
committedFilePaths,
|
|
11472
|
+
scopedRoots,
|
|
11473
|
+
configuredProviderInfo
|
|
11474
|
+
);
|
|
11475
|
+
}
|
|
11120
11476
|
}
|
|
11121
11477
|
const removedChunkIds = [];
|
|
11122
11478
|
for (const [chunkId] of existingChunks) {
|
|
@@ -11153,13 +11509,6 @@ var Indexer = class _Indexer {
|
|
|
11153
11509
|
if (removedStoredChunks) {
|
|
11154
11510
|
this.saveInvertedIndex(invertedIndex);
|
|
11155
11511
|
}
|
|
11156
|
-
if (scopedRoots) {
|
|
11157
|
-
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
11158
|
-
} else {
|
|
11159
|
-
this.fileHashCache = currentFileHashes;
|
|
11160
|
-
this.saveFileHashCache();
|
|
11161
|
-
}
|
|
11162
|
-
this.finalizeFailedBatchWriteState(failedProcessing.state);
|
|
11163
11512
|
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
11164
11513
|
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
11165
11514
|
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
@@ -11168,6 +11517,13 @@ var Indexer = class _Indexer {
|
|
|
11168
11517
|
this.indexCompatibility = { compatible: true };
|
|
11169
11518
|
database.commitWriteTransaction();
|
|
11170
11519
|
writeTransactionActive = false;
|
|
11520
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state, resolvedRetryChunkIds);
|
|
11521
|
+
if (scopedRoots) {
|
|
11522
|
+
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
11523
|
+
} else {
|
|
11524
|
+
this.fileHashCache = currentFileHashes;
|
|
11525
|
+
this.saveFileHashCache();
|
|
11526
|
+
}
|
|
11171
11527
|
stats.durationMs = Date.now() - startTime;
|
|
11172
11528
|
onProgress?.({
|
|
11173
11529
|
phase: "complete",
|
|
@@ -11191,13 +11547,6 @@ var Indexer = class _Indexer {
|
|
|
11191
11547
|
);
|
|
11192
11548
|
store.save();
|
|
11193
11549
|
this.saveInvertedIndex(invertedIndex);
|
|
11194
|
-
if (scopedRoots) {
|
|
11195
|
-
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
11196
|
-
} else {
|
|
11197
|
-
this.fileHashCache = currentFileHashes;
|
|
11198
|
-
this.saveFileHashCache();
|
|
11199
|
-
}
|
|
11200
|
-
this.finalizeFailedBatchWriteState(failedProcessing.state);
|
|
11201
11550
|
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
11202
11551
|
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
11203
11552
|
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
@@ -11206,6 +11555,13 @@ var Indexer = class _Indexer {
|
|
|
11206
11555
|
this.indexCompatibility = { compatible: true };
|
|
11207
11556
|
database.commitWriteTransaction();
|
|
11208
11557
|
writeTransactionActive = false;
|
|
11558
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state, resolvedRetryChunkIds);
|
|
11559
|
+
if (scopedRoots) {
|
|
11560
|
+
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
11561
|
+
} else {
|
|
11562
|
+
this.fileHashCache = currentFileHashes;
|
|
11563
|
+
this.saveFileHashCache();
|
|
11564
|
+
}
|
|
11209
11565
|
stats.durationMs = Date.now() - startTime;
|
|
11210
11566
|
onProgress?.({
|
|
11211
11567
|
phase: "complete",
|
|
@@ -11240,15 +11596,15 @@ var Indexer = class _Indexer {
|
|
|
11240
11596
|
);
|
|
11241
11597
|
store.save();
|
|
11242
11598
|
this.saveInvertedIndex(invertedIndex);
|
|
11599
|
+
database.commitWriteTransaction();
|
|
11600
|
+
writeTransactionActive = false;
|
|
11601
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state, resolvedRetryChunkIds);
|
|
11243
11602
|
if (scopedRoots) {
|
|
11244
11603
|
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
11245
11604
|
} else {
|
|
11246
11605
|
this.fileHashCache = currentFileHashes;
|
|
11247
11606
|
this.saveFileHashCache();
|
|
11248
11607
|
}
|
|
11249
|
-
this.finalizeFailedBatchWriteState(failedProcessing.state);
|
|
11250
|
-
database.commitWriteTransaction();
|
|
11251
|
-
writeTransactionActive = false;
|
|
11252
11608
|
if (this.config.indexing.autoGc && stats.removedChunks > 0) {
|
|
11253
11609
|
const gcReset = await this.maybeRunOrphanGc();
|
|
11254
11610
|
if (gcReset) {
|
|
@@ -11272,6 +11628,9 @@ var Indexer = class _Indexer {
|
|
|
11272
11628
|
if (forceScopedReembed && failedForcedChunkIds.size === 0) {
|
|
11273
11629
|
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
11274
11630
|
}
|
|
11631
|
+
if (forceScopedReembed) {
|
|
11632
|
+
database.setMetadata(this.getProjectMigrationFinalizedMetadataKey(), "true");
|
|
11633
|
+
}
|
|
11275
11634
|
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
11276
11635
|
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
11277
11636
|
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
@@ -11382,26 +11741,41 @@ var Indexer = class _Indexer {
|
|
|
11382
11741
|
shouldPrefilterByBranch: branchChunkIds !== null && (this.config.scope === "global" || hasInitializedBranchCatalog)
|
|
11383
11742
|
};
|
|
11384
11743
|
}
|
|
11385
|
-
|
|
11744
|
+
searchCandidatesWithAllowedIds(initialLimit, totalCount, allowedChunkIds, shouldPrefilter, search, getChunkId) {
|
|
11386
11745
|
const normalizedLimit = Math.max(0, Math.floor(initialLimit));
|
|
11387
11746
|
if (normalizedLimit === 0) return [];
|
|
11388
|
-
if (!
|
|
11747
|
+
if (!shouldPrefilter || !allowedChunkIds) {
|
|
11389
11748
|
return search(normalizedLimit);
|
|
11390
11749
|
}
|
|
11391
|
-
const targetCount = Math.min(normalizedLimit,
|
|
11750
|
+
const targetCount = Math.min(normalizedLimit, allowedChunkIds.size);
|
|
11392
11751
|
if (targetCount === 0 || totalCount === 0) return [];
|
|
11393
11752
|
let requestedLimit = Math.min(normalizedLimit, totalCount);
|
|
11394
11753
|
while (true) {
|
|
11395
11754
|
const results = search(requestedLimit);
|
|
11396
|
-
const
|
|
11397
|
-
if (
|
|
11398
|
-
return
|
|
11755
|
+
const allowedResults = results.filter((candidate) => allowedChunkIds.has(getChunkId(candidate)));
|
|
11756
|
+
if (allowedResults.length >= targetCount || results.length < requestedLimit || requestedLimit >= totalCount) {
|
|
11757
|
+
return allowedResults;
|
|
11399
11758
|
}
|
|
11400
11759
|
const nextLimit = Math.min(totalCount, Math.max(requestedLimit + 1, requestedLimit * 2));
|
|
11401
|
-
if (nextLimit === requestedLimit) return
|
|
11760
|
+
if (nextLimit === requestedLimit) return allowedResults;
|
|
11402
11761
|
requestedLimit = nextLimit;
|
|
11403
11762
|
}
|
|
11404
11763
|
}
|
|
11764
|
+
getTemporalChunkIds(database, options) {
|
|
11765
|
+
if (!options?.blameSince && !options?.blameUntil) return null;
|
|
11766
|
+
const since = options.blameSince ? parseBlameTimestamp(options.blameSince, false) : void 0;
|
|
11767
|
+
const until = options.blameUntil ? parseBlameTimestamp(options.blameUntil, true) : void 0;
|
|
11768
|
+
if (since === null || until === null) {
|
|
11769
|
+
return /* @__PURE__ */ new Set();
|
|
11770
|
+
}
|
|
11771
|
+
return new Set(database.getChunkIdsByBlameDate(since, until));
|
|
11772
|
+
}
|
|
11773
|
+
intersectChunkIdSets(first, second) {
|
|
11774
|
+
if (first === null) return second;
|
|
11775
|
+
if (second === null) return first;
|
|
11776
|
+
const [smaller, larger] = first.size <= second.size ? [first, second] : [second, first];
|
|
11777
|
+
return new Set(Array.from(smaller).filter((chunkId) => larger.has(chunkId)));
|
|
11778
|
+
}
|
|
11405
11779
|
buildCandidateSnapshot(candidate) {
|
|
11406
11780
|
return {
|
|
11407
11781
|
id: candidate.id,
|
|
@@ -11416,13 +11790,16 @@ var Indexer = class _Indexer {
|
|
|
11416
11790
|
buildCandidateSnapshotList(candidates) {
|
|
11417
11791
|
return candidates.map((candidate) => this.buildCandidateSnapshot(candidate));
|
|
11418
11792
|
}
|
|
11419
|
-
searchSemanticCandidates(store, embedding, initialLimit, branchChunkIds, shouldPrefilterByBranch) {
|
|
11420
|
-
|
|
11421
|
-
|
|
11422
|
-
|
|
11793
|
+
searchSemanticCandidates(store, embedding, initialLimit, branchChunkIds, shouldPrefilterByBranch, temporalChunkIds) {
|
|
11794
|
+
const availableCount = temporalChunkIds?.size ?? store.count();
|
|
11795
|
+
if (availableCount === 0) return [];
|
|
11796
|
+
const allowedIds = temporalChunkIds === null ? void 0 : Array.from(temporalChunkIds);
|
|
11797
|
+
return this.searchCandidatesWithAllowedIds(
|
|
11798
|
+
Math.min(initialLimit, availableCount),
|
|
11799
|
+
availableCount,
|
|
11423
11800
|
branchChunkIds,
|
|
11424
11801
|
shouldPrefilterByBranch,
|
|
11425
|
-
(requestedLimit) => store.search(embedding, requestedLimit),
|
|
11802
|
+
(requestedLimit) => store.search(embedding, requestedLimit, allowedIds),
|
|
11426
11803
|
(candidate) => candidate.id
|
|
11427
11804
|
);
|
|
11428
11805
|
}
|
|
@@ -11447,7 +11824,9 @@ var Indexer = class _Indexer {
|
|
|
11447
11824
|
const rerankTopN = this.config.search.rerankTopN;
|
|
11448
11825
|
const filterByBranch = options?.filterByBranch ?? true;
|
|
11449
11826
|
const sourceIntent = options?.definitionIntent === true || classifyQueryIntentRaw(query) === "source";
|
|
11827
|
+
const prioritizeSourcePaths = sourceIntent || options?.prioritizeSourcePaths === true;
|
|
11450
11828
|
const identifierHints = extractIdentifierHints(query);
|
|
11829
|
+
const candidateLimit = maxResults * (prioritizeSourcePaths ? 12 : 4);
|
|
11451
11830
|
this.logger.search("debug", "Starting search", {
|
|
11452
11831
|
query,
|
|
11453
11832
|
maxResults,
|
|
@@ -11478,25 +11857,28 @@ var Indexer = class _Indexer {
|
|
|
11478
11857
|
branchChunkIds = new Set(branchCatalogKeys.flatMap((branchKey) => database.getBranchChunkIds(branchKey)));
|
|
11479
11858
|
branchSymbolIds = new Set(branchCatalogKeys.flatMap((branchKey) => database.getBranchSymbolIds(branchKey)));
|
|
11480
11859
|
}
|
|
11860
|
+
const temporalChunkIds = this.getTemporalChunkIds(database, options);
|
|
11481
11861
|
const { hasInitializedBranchCatalog, shouldPrefilterByBranch } = this.getBranchPrefilterState(database, branchChunkIds);
|
|
11482
11862
|
const prefilterMs = performance2.now() - prefilterStartTime;
|
|
11483
11863
|
const vectorStartTime = performance2.now();
|
|
11484
11864
|
const semanticCandidates = embedding ? this.searchSemanticCandidates(
|
|
11485
11865
|
store,
|
|
11486
11866
|
embedding,
|
|
11487
|
-
|
|
11867
|
+
candidateLimit,
|
|
11488
11868
|
branchChunkIds,
|
|
11489
|
-
shouldPrefilterByBranch
|
|
11869
|
+
shouldPrefilterByBranch,
|
|
11870
|
+
temporalChunkIds
|
|
11490
11871
|
) : [];
|
|
11491
11872
|
const vectorMs = performance2.now() - vectorStartTime;
|
|
11492
11873
|
const keywordStartTime = performance2.now();
|
|
11493
11874
|
const keywordCandidates = await this.keywordSearch(
|
|
11494
11875
|
query,
|
|
11495
|
-
|
|
11876
|
+
candidateLimit,
|
|
11496
11877
|
store,
|
|
11497
11878
|
invertedIndex,
|
|
11498
11879
|
branchChunkIds,
|
|
11499
|
-
shouldPrefilterByBranch
|
|
11880
|
+
shouldPrefilterByBranch,
|
|
11881
|
+
temporalChunkIds
|
|
11500
11882
|
);
|
|
11501
11883
|
const keywordMs = performance2.now() - keywordStartTime;
|
|
11502
11884
|
const scopedSemanticCandidates = semanticCandidates.filter(
|
|
@@ -11518,7 +11900,7 @@ var Indexer = class _Indexer {
|
|
|
11518
11900
|
rerankTopN,
|
|
11519
11901
|
limit: maxResults,
|
|
11520
11902
|
hybridWeight: rankingHybridWeight,
|
|
11521
|
-
prioritizeSourcePaths
|
|
11903
|
+
prioritizeSourcePaths
|
|
11522
11904
|
});
|
|
11523
11905
|
const rerankedCombined = await this.rerankCandidatesWithApi(query, combined, {
|
|
11524
11906
|
definitionIntent: options?.definitionIntent === true,
|
|
@@ -11554,10 +11936,11 @@ var Indexer = class _Indexer {
|
|
|
11554
11936
|
branchSymbolIds,
|
|
11555
11937
|
maxResults,
|
|
11556
11938
|
union,
|
|
11557
|
-
sourceIntent
|
|
11939
|
+
sourceIntent,
|
|
11940
|
+
options?.definitionIntent === true && ((options.directory?.trim().length ?? 0) > 0 || (options.fileType?.trim().length ?? 0) > 0)
|
|
11558
11941
|
);
|
|
11559
11942
|
const prePrimaryLane = mergeTieredResults(deterministicIdentifierLane, identifierLane, maxResults * 4);
|
|
11560
|
-
const primaryLane = mergeTieredResults(prePrimaryLane, symbolLane, maxResults * 4);
|
|
11943
|
+
const primaryLane = options?.definitionIntent === true ? mergeTieredResults(symbolLane, prePrimaryLane, maxResults * 4) : mergeTieredResults(prePrimaryLane, symbolLane, maxResults * 4);
|
|
11561
11944
|
const tiered = mergeTieredResults(primaryLane, rescued, maxResults * 4);
|
|
11562
11945
|
const hasCodeHints = extractCodeTermHints(query).length > 0 || identifierHints.length > 0;
|
|
11563
11946
|
const baseFiltered = tiered.filter(
|
|
@@ -11652,14 +12035,18 @@ var Indexer = class _Indexer {
|
|
|
11652
12035
|
})
|
|
11653
12036
|
);
|
|
11654
12037
|
}
|
|
11655
|
-
async keywordSearch(query, limit, store, invertedIndex, branchChunkIds = null, shouldPrefilterByBranch = false) {
|
|
12038
|
+
async keywordSearch(query, limit, store, invertedIndex, branchChunkIds = null, shouldPrefilterByBranch = false, temporalChunkIds = null) {
|
|
11656
12039
|
const normalizedLimit = Math.max(0, Math.floor(limit));
|
|
11657
12040
|
if (normalizedLimit === 0) return [];
|
|
11658
|
-
const
|
|
12041
|
+
const allowedChunkIds = this.intersectChunkIdSets(
|
|
12042
|
+
shouldPrefilterByBranch ? branchChunkIds : null,
|
|
12043
|
+
temporalChunkIds
|
|
12044
|
+
);
|
|
12045
|
+
const scoreEntries = this.searchCandidatesWithAllowedIds(
|
|
11659
12046
|
normalizedLimit,
|
|
11660
12047
|
invertedIndex.getDocumentCount(),
|
|
11661
|
-
|
|
11662
|
-
|
|
12048
|
+
allowedChunkIds,
|
|
12049
|
+
allowedChunkIds !== null,
|
|
11663
12050
|
(requestedLimit) => Array.from(invertedIndex.search(query, requestedLimit)),
|
|
11664
12051
|
([chunkId]) => chunkId
|
|
11665
12052
|
);
|
|
@@ -11744,7 +12131,17 @@ var Indexer = class _Indexer {
|
|
|
11744
12131
|
);
|
|
11745
12132
|
const currentFileHashes = /* @__PURE__ */ new Map();
|
|
11746
12133
|
for (const file of files) {
|
|
11747
|
-
|
|
12134
|
+
let hash;
|
|
12135
|
+
try {
|
|
12136
|
+
hash = hashFile(file.path);
|
|
12137
|
+
} catch (error) {
|
|
12138
|
+
this.logger.warn("Skipped unreadable file during freshness check", {
|
|
12139
|
+
path: file.path,
|
|
12140
|
+
error: getErrorMessage3(error)
|
|
12141
|
+
});
|
|
12142
|
+
return { readable: false, current: false, reason: "unreadable" };
|
|
12143
|
+
}
|
|
12144
|
+
currentFileHashes.set(this.toStoredFilePath(file.path), hash);
|
|
11748
12145
|
}
|
|
11749
12146
|
const scopedRoots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
11750
12147
|
const cachedFileHashes = scopedRoots ? new Map(Array.from(this.fileHashCache).filter(([filePath]) => this.isFileInCurrentScope(filePath, scopedRoots))) : this.fileHashCache;
|
|
@@ -11770,69 +12167,87 @@ var Indexer = class _Indexer {
|
|
|
11770
12167
|
async forceIndex(onProgress) {
|
|
11771
12168
|
return this.withIndexMutationLease("force-index", async (recoveredOwners) => {
|
|
11772
12169
|
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
11773
|
-
|
|
12170
|
+
const recovery = this.beginClearRecoveryState();
|
|
12171
|
+
await this.clearIndexUnlocked(recovery.compatibilityDecision);
|
|
12172
|
+
this.finishClearRecoveryState();
|
|
11774
12173
|
return this.indexUnlocked(onProgress, [], true);
|
|
11775
12174
|
});
|
|
11776
12175
|
}
|
|
11777
12176
|
async clearIndex() {
|
|
11778
12177
|
await this.withIndexMutationLease("clear", async (recoveredOwners) => {
|
|
11779
12178
|
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
11780
|
-
|
|
12179
|
+
const recovery = this.beginClearRecoveryState();
|
|
12180
|
+
await this.clearIndexUnlocked(recovery.compatibilityDecision);
|
|
11781
12181
|
});
|
|
11782
12182
|
}
|
|
11783
|
-
|
|
12183
|
+
clearGlobalIndexDataUnlocked(projectRoot = this.projectRoot) {
|
|
11784
12184
|
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
11785
|
-
|
|
11786
|
-
|
|
11787
|
-
|
|
11788
|
-
|
|
11789
|
-
|
|
11790
|
-
|
|
11791
|
-
|
|
11792
|
-
|
|
11793
|
-
|
|
11794
|
-
|
|
11795
|
-
|
|
11796
|
-
|
|
11797
|
-
|
|
11798
|
-
|
|
11799
|
-
|
|
12185
|
+
const clearedBranchKeys = database.getAllBranches();
|
|
12186
|
+
store.clear();
|
|
12187
|
+
store.save();
|
|
12188
|
+
invertedIndex.clear();
|
|
12189
|
+
this.saveInvertedIndex(invertedIndex);
|
|
12190
|
+
this.fileHashCache.clear();
|
|
12191
|
+
this.saveFileHashCache();
|
|
12192
|
+
database.clearAllIndexedData();
|
|
12193
|
+
this.deleteBranchCommitMetadata(database, clearedBranchKeys);
|
|
12194
|
+
this.clearFailedBatchState();
|
|
12195
|
+
database.deleteMetadata("index.version");
|
|
12196
|
+
database.deleteMetadata("index.pathStorageVersion");
|
|
12197
|
+
database.deleteMetadata("index.embeddingProvider");
|
|
12198
|
+
database.deleteMetadata("index.embeddingModel");
|
|
12199
|
+
database.deleteMetadata("index.embeddingDimensions");
|
|
12200
|
+
database.deleteMetadata("index.embeddingStrategyVersion");
|
|
12201
|
+
const projectIdentityHash = this.getProjectIdentityHash(projectRoot);
|
|
12202
|
+
database.deleteMetadata(this.getProjectEmbeddingStrategyMetadataKey(projectIdentityHash));
|
|
12203
|
+
database.deleteMetadata(this.getProjectForceReembedMetadataKey(projectIdentityHash));
|
|
12204
|
+
database.deleteMetadata(this.getLegacyMigrationMetadataKey(projectIdentityHash));
|
|
12205
|
+
database.deleteMetadata("index.createdAt");
|
|
12206
|
+
database.deleteMetadata("index.updatedAt");
|
|
12207
|
+
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
12208
|
+
}
|
|
12209
|
+
clearGlobalIndexUnlocked(projectRoot = this.projectRoot, roots = this.getScopedRoots(), recoveryDecision) {
|
|
12210
|
+
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
12211
|
+
store.load();
|
|
12212
|
+
invertedIndex.load();
|
|
12213
|
+
this.loadFileHashCache();
|
|
12214
|
+
const compatibility = this.checkCompatibility();
|
|
12215
|
+
const compatibilityDecision = recoveryDecision ?? (compatibility.compatible ? "compatible" : compatibility.code === "EMBEDDING_STRATEGY_MISMATCH" /* EMBEDDING_STRATEGY_MISMATCH */ ? "embedding-strategy-mismatch" : "incompatible");
|
|
12216
|
+
const allMetadata = store.getAllMetadata();
|
|
12217
|
+
const hasForeignData = allMetadata.some(({ metadata }) => !this.isFileInCurrentScope(metadata.filePath, roots)) || this.hasForeignScopedBranchData(projectRoot, roots) || this.hasForeignScopedFileHashData(roots) || this.hasForeignScopedFailedBatches(roots);
|
|
12218
|
+
if (compatibilityDecision !== "compatible" && hasForeignData) {
|
|
12219
|
+
if (compatibilityDecision === "embedding-strategy-mismatch") {
|
|
12220
|
+
this.clearSharedIndexProjectData(store, invertedIndex, database, roots, projectRoot);
|
|
12221
|
+
this.clearScopedFileHashCache(roots);
|
|
12222
|
+
this.clearScopedFailedBatches(roots);
|
|
12223
|
+
const projectIdentityHash = this.getProjectIdentityHash(projectRoot);
|
|
12224
|
+
database.setMetadata(this.getProjectForceReembedMetadataKey(projectIdentityHash), "true");
|
|
12225
|
+
database.deleteMetadata(this.getProjectEmbeddingStrategyMetadataKey(projectIdentityHash));
|
|
12226
|
+
database.deleteMetadata(this.getProjectMigrationFinalizedMetadataKey(projectIdentityHash));
|
|
12227
|
+
if (projectRoot === this.projectRoot) {
|
|
11800
12228
|
this.indexCompatibility = { compatible: true };
|
|
11801
|
-
return;
|
|
11802
12229
|
}
|
|
11803
|
-
throw new Error(
|
|
11804
|
-
`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.`
|
|
11805
|
-
);
|
|
11806
|
-
}
|
|
11807
|
-
if (!hasForeignData) {
|
|
11808
|
-
const clearedBranchKeys2 = database.getAllBranches();
|
|
11809
|
-
store.clear();
|
|
11810
|
-
store.save();
|
|
11811
|
-
invertedIndex.clear();
|
|
11812
|
-
this.saveInvertedIndex(invertedIndex);
|
|
11813
|
-
this.fileHashCache.clear();
|
|
11814
|
-
this.saveFileHashCache();
|
|
11815
|
-
database.clearAllIndexedData();
|
|
11816
|
-
this.deleteBranchCommitMetadata(database, clearedBranchKeys2);
|
|
11817
|
-
this.clearFailedBatchState();
|
|
11818
|
-
database.deleteMetadata("index.version");
|
|
11819
|
-
database.deleteMetadata("index.pathStorageVersion");
|
|
11820
|
-
database.deleteMetadata("index.embeddingProvider");
|
|
11821
|
-
database.deleteMetadata("index.embeddingModel");
|
|
11822
|
-
database.deleteMetadata("index.embeddingDimensions");
|
|
11823
|
-
database.deleteMetadata("index.embeddingStrategyVersion");
|
|
11824
|
-
database.deleteMetadata(this.getProjectEmbeddingStrategyMetadataKey());
|
|
11825
|
-
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
11826
|
-
database.deleteMetadata(this.getLegacyMigrationMetadataKey());
|
|
11827
|
-
database.deleteMetadata("index.createdAt");
|
|
11828
|
-
database.deleteMetadata("index.updatedAt");
|
|
11829
|
-
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
11830
12230
|
return;
|
|
11831
12231
|
}
|
|
11832
|
-
|
|
11833
|
-
|
|
11834
|
-
|
|
12232
|
+
throw new Error(
|
|
12233
|
+
`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.`
|
|
12234
|
+
);
|
|
12235
|
+
}
|
|
12236
|
+
if (!hasForeignData) {
|
|
12237
|
+
this.clearGlobalIndexDataUnlocked(projectRoot);
|
|
12238
|
+
return;
|
|
12239
|
+
}
|
|
12240
|
+
this.clearSharedIndexProjectData(store, invertedIndex, database, roots, projectRoot);
|
|
12241
|
+
this.clearScopedFileHashCache(roots);
|
|
12242
|
+
this.clearScopedFailedBatches(roots);
|
|
12243
|
+
if (projectRoot === this.projectRoot) {
|
|
11835
12244
|
this.indexCompatibility = compatibility;
|
|
12245
|
+
}
|
|
12246
|
+
}
|
|
12247
|
+
async clearIndexUnlocked(recoveryDecision) {
|
|
12248
|
+
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
12249
|
+
if (this.config.scope === "global") {
|
|
12250
|
+
this.clearGlobalIndexUnlocked(this.projectRoot, this.getScopedRoots(), recoveryDecision);
|
|
11836
12251
|
return;
|
|
11837
12252
|
}
|
|
11838
12253
|
if (!this.isProjectOwnedIndexPath()) {
|
|
@@ -11998,6 +12413,7 @@ var Indexer = class _Indexer {
|
|
|
11998
12413
|
)) {
|
|
11999
12414
|
const chunks = retryBatch.map(({ chunk }) => chunk);
|
|
12000
12415
|
const attemptCounts = new Map(retryBatch.map(({ chunk, attemptCount }) => [chunk.id, attemptCount]));
|
|
12416
|
+
this.restoreMissingChunkRows(database, chunks);
|
|
12001
12417
|
const batchResult = await this.processPendingChunkBatch(chunks, {
|
|
12002
12418
|
store,
|
|
12003
12419
|
provider,
|
|
@@ -12012,6 +12428,7 @@ var Indexer = class _Indexer {
|
|
|
12012
12428
|
forceReembed: false,
|
|
12013
12429
|
reuseCachedEmbeddings: false,
|
|
12014
12430
|
incrementRepeatedFailures: false,
|
|
12431
|
+
forceSingleItemBatches: true,
|
|
12015
12432
|
onSucceeded: (succeededChunks) => {
|
|
12016
12433
|
database.addChunksToBranchBatch(
|
|
12017
12434
|
this.getBranchCatalogKey(),
|
|
@@ -12033,9 +12450,12 @@ var Indexer = class _Indexer {
|
|
|
12033
12450
|
this.saveInvertedIndex(invertedIndex);
|
|
12034
12451
|
}
|
|
12035
12452
|
if (roots && succeeded > 0 && remaining === 0 && this.hasProjectForceReembedPending()) {
|
|
12036
|
-
database.
|
|
12037
|
-
|
|
12038
|
-
|
|
12453
|
+
const migrationFinalized = database.getMetadata(this.getProjectMigrationFinalizedMetadataKey()) === "true";
|
|
12454
|
+
if (migrationFinalized) {
|
|
12455
|
+
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
12456
|
+
this.saveIndexMetadata(configuredProviderInfo);
|
|
12457
|
+
this.indexCompatibility = { compatible: true };
|
|
12458
|
+
}
|
|
12039
12459
|
}
|
|
12040
12460
|
return { succeeded, failed, remaining };
|
|
12041
12461
|
}
|
|
@@ -12057,7 +12477,8 @@ var Indexer = class _Indexer {
|
|
|
12057
12477
|
latestById.set(chunkId, {
|
|
12058
12478
|
attemptCount: batch.attemptCount,
|
|
12059
12479
|
error: batch.error,
|
|
12060
|
-
lastAttempt: batch.lastAttempt
|
|
12480
|
+
lastAttempt: batch.lastAttempt,
|
|
12481
|
+
chunks: [rawChunk]
|
|
12061
12482
|
});
|
|
12062
12483
|
}
|
|
12063
12484
|
}
|
|
@@ -12124,6 +12545,7 @@ var Indexer = class _Indexer {
|
|
|
12124
12545
|
this.getBranchCatalogKeys().flatMap((branchKey) => database.getBranchChunkIds(branchKey))
|
|
12125
12546
|
);
|
|
12126
12547
|
}
|
|
12548
|
+
const temporalChunkIds = this.getTemporalChunkIds(database, options);
|
|
12127
12549
|
const { hasInitializedBranchCatalog, shouldPrefilterByBranch } = this.getBranchPrefilterState(database, branchChunkIds);
|
|
12128
12550
|
const prefilterMs = performance2.now() - prefilterStartTime;
|
|
12129
12551
|
const vectorStartTime = performance2.now();
|
|
@@ -12132,7 +12554,8 @@ var Indexer = class _Indexer {
|
|
|
12132
12554
|
embedding,
|
|
12133
12555
|
limit * 2,
|
|
12134
12556
|
branchChunkIds,
|
|
12135
|
-
shouldPrefilterByBranch
|
|
12557
|
+
shouldPrefilterByBranch,
|
|
12558
|
+
temporalChunkIds
|
|
12136
12559
|
);
|
|
12137
12560
|
const vectorMs = performance2.now() - vectorStartTime;
|
|
12138
12561
|
if (this.config.scope !== "global" && branchChunkIds && !hasInitializedBranchCatalog) {
|
|
@@ -12250,9 +12673,9 @@ var Indexer = class _Indexer {
|
|
|
12250
12673
|
this.requireReadableComponents(readIssues, "database");
|
|
12251
12674
|
let shortest = [];
|
|
12252
12675
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
12253
|
-
const
|
|
12254
|
-
if (
|
|
12255
|
-
shortest =
|
|
12676
|
+
const path33 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
|
|
12677
|
+
if (path33.length > 0 && (shortest.length === 0 || path33.length < shortest.length)) {
|
|
12678
|
+
shortest = path33;
|
|
12256
12679
|
}
|
|
12257
12680
|
}
|
|
12258
12681
|
return shortest.map((hop) => this.resolveFilePathRecord(hop));
|
|
@@ -12300,13 +12723,13 @@ var Indexer = class _Indexer {
|
|
|
12300
12723
|
}
|
|
12301
12724
|
}
|
|
12302
12725
|
if (!found) continue;
|
|
12303
|
-
const
|
|
12726
|
+
const path33 = [];
|
|
12304
12727
|
let currentSymbolId = toSymbolId;
|
|
12305
12728
|
while (true) {
|
|
12306
12729
|
const symbol = symbolsById.get(currentSymbolId);
|
|
12307
12730
|
if (!symbol) break;
|
|
12308
12731
|
const parent = parentBySymbolId.get(currentSymbolId);
|
|
12309
|
-
|
|
12732
|
+
path33.push({
|
|
12310
12733
|
symbolId: symbol.id,
|
|
12311
12734
|
symbolName: symbol.name,
|
|
12312
12735
|
filePath: symbol.filePath,
|
|
@@ -12316,9 +12739,9 @@ var Indexer = class _Indexer {
|
|
|
12316
12739
|
if (!parent) break;
|
|
12317
12740
|
currentSymbolId = parent.parentId;
|
|
12318
12741
|
}
|
|
12319
|
-
|
|
12320
|
-
if (
|
|
12321
|
-
shortest =
|
|
12742
|
+
path33.reverse();
|
|
12743
|
+
if (path33.length > 0 && (shortest.length === 0 || path33.length < shortest.length)) {
|
|
12744
|
+
shortest = path33;
|
|
12322
12745
|
}
|
|
12323
12746
|
}
|
|
12324
12747
|
return shortest.map((hop) => this.resolveFilePathRecord(hop));
|
|
@@ -12654,7 +13077,6 @@ var Indexer = class _Indexer {
|
|
|
12654
13077
|
this.store = null;
|
|
12655
13078
|
this.invertedIndex = null;
|
|
12656
13079
|
this.provider = null;
|
|
12657
|
-
this.reranker = null;
|
|
12658
13080
|
this.configuredProviderInfo = null;
|
|
12659
13081
|
this.indexCompatibility = null;
|
|
12660
13082
|
this.initializationMode = "none";
|
|
@@ -12715,6 +13137,36 @@ function resolveConfigPathValue(value, baseDir) {
|
|
|
12715
13137
|
const absolutePath = path16.isAbsolute(trimmed) ? trimmed : path16.resolve(baseDir, trimmed);
|
|
12716
13138
|
return path16.normalize(absolutePath);
|
|
12717
13139
|
}
|
|
13140
|
+
function serializeConfigPathValue(value, baseDir) {
|
|
13141
|
+
const trimmed = value.trim();
|
|
13142
|
+
if (!trimmed) {
|
|
13143
|
+
return trimmed;
|
|
13144
|
+
}
|
|
13145
|
+
if (!path16.isAbsolute(trimmed)) {
|
|
13146
|
+
return normalizePathSeparators(path16.normalize(trimmed));
|
|
13147
|
+
}
|
|
13148
|
+
const relativePath = path16.relative(baseDir, trimmed);
|
|
13149
|
+
if (!relativePath || !relativePath.startsWith("..") && !path16.isAbsolute(relativePath)) {
|
|
13150
|
+
return normalizePathSeparators(path16.normalize(relativePath || "."));
|
|
13151
|
+
}
|
|
13152
|
+
return path16.normalize(trimmed);
|
|
13153
|
+
}
|
|
13154
|
+
function resolveKnowledgeBasePath(value, projectRoot) {
|
|
13155
|
+
return path16.isAbsolute(value) ? value : path16.resolve(projectRoot, value);
|
|
13156
|
+
}
|
|
13157
|
+
function normalizeKnowledgeBasePath(value, projectRoot) {
|
|
13158
|
+
return path16.normalize(resolveKnowledgeBasePath(value, projectRoot));
|
|
13159
|
+
}
|
|
13160
|
+
function hasMatchingKnowledgeBasePath(knowledgeBases, inputPath, projectRoot) {
|
|
13161
|
+
const normalizedInput = path16.normalize(inputPath);
|
|
13162
|
+
return knowledgeBases.some((kb) => normalizeKnowledgeBasePath(kb, projectRoot) === normalizedInput);
|
|
13163
|
+
}
|
|
13164
|
+
function findKnowledgeBasePathIndex(knowledgeBases, inputPath, projectRoot) {
|
|
13165
|
+
const normalizedInput = path16.normalize(inputPath);
|
|
13166
|
+
return knowledgeBases.findIndex(
|
|
13167
|
+
(kb) => path16.normalize(kb) === normalizedInput || normalizeKnowledgeBasePath(kb, projectRoot) === normalizedInput
|
|
13168
|
+
);
|
|
13169
|
+
}
|
|
12718
13170
|
|
|
12719
13171
|
// src/tools/format-communities.ts
|
|
12720
13172
|
function compareText(left, right) {
|
|
@@ -12968,8 +13420,8 @@ function formatExactSearchHandoff(results) {
|
|
|
12968
13420
|
}
|
|
12969
13421
|
function formatContextEvidence(result, index) {
|
|
12970
13422
|
const symbol = result.name ? ` ${JSON.stringify(compactEvidenceValue(result.name, 80))}` : "";
|
|
12971
|
-
const
|
|
12972
|
-
return `[${index}] ${result.chunkType}${symbol} in ${
|
|
13423
|
+
const path33 = compactEvidenceValue(result.filePath, 120);
|
|
13424
|
+
return `[${index}] ${result.chunkType}${symbol} in ${path33}:${result.startLine}-${result.endLine} (score ${result.score.toFixed(2)})`;
|
|
12973
13425
|
}
|
|
12974
13426
|
function formatContextPack(heading, selected, candidateCount, duplicateCount, limitOmittedCount, budgetOmittedCount, includeExactSearchHandoff) {
|
|
12975
13427
|
const lines = selected.map((result, index) => formatContextEvidence(result, index + 1));
|
|
@@ -13623,7 +14075,7 @@ function getErrorMessage4(error) {
|
|
|
13623
14075
|
return error instanceof Error ? error.message : String(error);
|
|
13624
14076
|
}
|
|
13625
14077
|
function runCommand(file, args, options) {
|
|
13626
|
-
return new Promise((
|
|
14078
|
+
return new Promise((resolve20, reject) => {
|
|
13627
14079
|
childProcess.execFile(
|
|
13628
14080
|
file,
|
|
13629
14081
|
args,
|
|
@@ -13633,7 +14085,7 @@ function runCommand(file, args, options) {
|
|
|
13633
14085
|
reject(error);
|
|
13634
14086
|
return;
|
|
13635
14087
|
}
|
|
13636
|
-
|
|
14088
|
+
resolve20(stdout);
|
|
13637
14089
|
}
|
|
13638
14090
|
);
|
|
13639
14091
|
});
|
|
@@ -13778,10 +14230,10 @@ function safeFailureMessage(error) {
|
|
|
13778
14230
|
}
|
|
13779
14231
|
function cancellableDelay(delayMs, signal) {
|
|
13780
14232
|
if (signal.aborted) return Promise.reject(new AutoIndexCancelledError());
|
|
13781
|
-
return new Promise((
|
|
14233
|
+
return new Promise((resolve20, reject) => {
|
|
13782
14234
|
const timer = setTimeout(() => {
|
|
13783
14235
|
signal.removeEventListener("abort", onAbort);
|
|
13784
|
-
|
|
14236
|
+
resolve20();
|
|
13785
14237
|
}, delayMs);
|
|
13786
14238
|
timer.unref?.();
|
|
13787
14239
|
const onAbort = () => {
|
|
@@ -13793,15 +14245,15 @@ function cancellableDelay(delayMs, signal) {
|
|
|
13793
14245
|
}
|
|
13794
14246
|
function withTimeout(promise, timeoutMs) {
|
|
13795
14247
|
if (timeoutMs <= 0) return Promise.resolve(void 0);
|
|
13796
|
-
return new Promise((
|
|
13797
|
-
const timer = setTimeout(() =>
|
|
14248
|
+
return new Promise((resolve20) => {
|
|
14249
|
+
const timer = setTimeout(() => resolve20(void 0), timeoutMs);
|
|
13798
14250
|
timer.unref?.();
|
|
13799
14251
|
void promise.then((value) => {
|
|
13800
14252
|
clearTimeout(timer);
|
|
13801
|
-
|
|
14253
|
+
resolve20(value);
|
|
13802
14254
|
}, () => {
|
|
13803
14255
|
clearTimeout(timer);
|
|
13804
|
-
|
|
14256
|
+
resolve20(void 0);
|
|
13805
14257
|
});
|
|
13806
14258
|
});
|
|
13807
14259
|
}
|
|
@@ -14183,17 +14635,17 @@ var AutoIndexCoordinator = class {
|
|
|
14183
14635
|
}
|
|
14184
14636
|
}
|
|
14185
14637
|
waitForBatteryRetry(delayMs) {
|
|
14186
|
-
return new Promise((
|
|
14638
|
+
return new Promise((resolve20) => {
|
|
14187
14639
|
const timer = setTimeout(() => {
|
|
14188
14640
|
if (this.batteryRetryTimer === timer) {
|
|
14189
14641
|
this.batteryRetryTimer = null;
|
|
14190
14642
|
this.resolveBatteryRetry = null;
|
|
14191
14643
|
}
|
|
14192
|
-
|
|
14644
|
+
resolve20();
|
|
14193
14645
|
}, delayMs);
|
|
14194
14646
|
timer.unref?.();
|
|
14195
14647
|
this.batteryRetryTimer = timer;
|
|
14196
|
-
this.resolveBatteryRetry =
|
|
14648
|
+
this.resolveBatteryRetry = resolve20;
|
|
14197
14649
|
});
|
|
14198
14650
|
}
|
|
14199
14651
|
cancelBatteryRetry() {
|
|
@@ -14201,9 +14653,9 @@ var AutoIndexCoordinator = class {
|
|
|
14201
14653
|
clearTimeout(this.batteryRetryTimer);
|
|
14202
14654
|
this.batteryRetryTimer = null;
|
|
14203
14655
|
}
|
|
14204
|
-
const
|
|
14656
|
+
const resolve20 = this.resolveBatteryRetry;
|
|
14205
14657
|
this.resolveBatteryRetry = null;
|
|
14206
|
-
|
|
14658
|
+
resolve20?.();
|
|
14207
14659
|
}
|
|
14208
14660
|
finishBatteryCheck(batteryCheck) {
|
|
14209
14661
|
if (this.batteryCheck !== batteryCheck) return;
|
|
@@ -14421,7 +14873,7 @@ function applyProjectOverride(merged, normalizedProjectConfig, globalConfig, key
|
|
|
14421
14873
|
function mergeUniqueStringArray(values) {
|
|
14422
14874
|
return [...new Set(values.map((value) => String(value).trim()))];
|
|
14423
14875
|
}
|
|
14424
|
-
function
|
|
14876
|
+
function normalizeKnowledgeBasePath2(value) {
|
|
14425
14877
|
let normalized = path19.normalize(String(value).trim());
|
|
14426
14878
|
const root = path19.parse(normalized).root;
|
|
14427
14879
|
while (normalized.length > root.length && /[\\/]$/.test(normalized)) {
|
|
@@ -14430,7 +14882,7 @@ function normalizeKnowledgeBasePath(value) {
|
|
|
14430
14882
|
return normalized;
|
|
14431
14883
|
}
|
|
14432
14884
|
function mergeKnowledgeBasePaths(values) {
|
|
14433
|
-
return [...new Set(values.map((value) =>
|
|
14885
|
+
return [...new Set(values.map((value) => normalizeKnowledgeBasePath2(value)).filter((value) => value.length > 0))];
|
|
14434
14886
|
}
|
|
14435
14887
|
function validateConfigLayerShape(rawConfig, filePath) {
|
|
14436
14888
|
if (!isRecord(rawConfig)) {
|
|
@@ -14560,9 +15012,30 @@ function toConfigRecord(rawConfig) {
|
|
|
14560
15012
|
}
|
|
14561
15013
|
return { ...rawConfig };
|
|
14562
15014
|
}
|
|
15015
|
+
function getConfigPath(projectRoot, host) {
|
|
15016
|
+
return resolveWritableProjectConfigPath(projectRoot, host);
|
|
15017
|
+
}
|
|
14563
15018
|
function loadRuntimeConfig(projectRoot, host) {
|
|
14564
15019
|
return normalizeKnowledgeBasePaths(toConfigRecord(loadMergedConfig(projectRoot, host)), projectRoot);
|
|
14565
15020
|
}
|
|
15021
|
+
function loadEditableConfig(projectRoot, host) {
|
|
15022
|
+
return normalizeKnowledgeBasePaths(toConfigRecord(loadProjectConfigLayer(projectRoot, host)), projectRoot);
|
|
15023
|
+
}
|
|
15024
|
+
function saveConfig(projectRoot, config, host) {
|
|
15025
|
+
const configPath = getConfigPath(projectRoot, host);
|
|
15026
|
+
const configDir = path20.dirname(configPath);
|
|
15027
|
+
const configBaseDir = path20.dirname(configDir);
|
|
15028
|
+
if (!existsSync11(configDir)) {
|
|
15029
|
+
mkdirSync5(configDir, { recursive: true });
|
|
15030
|
+
}
|
|
15031
|
+
const serializableConfig = { ...config };
|
|
15032
|
+
if (Array.isArray(serializableConfig.knowledgeBases)) {
|
|
15033
|
+
serializableConfig.knowledgeBases = serializableConfig.knowledgeBases.map(
|
|
15034
|
+
(kb) => serializeConfigPathValue(kb, configBaseDir)
|
|
15035
|
+
);
|
|
15036
|
+
}
|
|
15037
|
+
writeFileSync4(configPath, JSON.stringify(serializableConfig, null, 2) + "\n", "utf-8");
|
|
15038
|
+
}
|
|
14566
15039
|
|
|
14567
15040
|
// src/tools/operation-runtime.ts
|
|
14568
15041
|
var indexerCache = /* @__PURE__ */ new Map();
|
|
@@ -14781,9 +15254,11 @@ async function searchCodebase(projectRoot, host, query, options = {}) {
|
|
|
14781
15254
|
contextLines: options.contextLines,
|
|
14782
15255
|
metadataOnly: options.metadataOnly,
|
|
14783
15256
|
definitionIntent: options.definitionIntent,
|
|
15257
|
+
prioritizeSourcePaths: options.prioritizeSourcePaths,
|
|
14784
15258
|
blameAuthor: options.blameAuthor,
|
|
14785
15259
|
blameSha: options.blameSha,
|
|
14786
15260
|
blameSince: options.blameSince,
|
|
15261
|
+
blameUntil: options.blameUntil,
|
|
14787
15262
|
trace: options.trace
|
|
14788
15263
|
});
|
|
14789
15264
|
}
|
|
@@ -14829,7 +15304,9 @@ async function findSimilarCode(projectRoot, host, code, options = {}) {
|
|
|
14829
15304
|
fileType: options.fileType,
|
|
14830
15305
|
directory: options.directory,
|
|
14831
15306
|
chunkType: options.chunkType,
|
|
14832
|
-
excludeFile: options.excludeFile
|
|
15307
|
+
excludeFile: options.excludeFile,
|
|
15308
|
+
blameSince: options.blameSince,
|
|
15309
|
+
blameUntil: options.blameUntil
|
|
14833
15310
|
});
|
|
14834
15311
|
}
|
|
14835
15312
|
async function implementationLookup(projectRoot, host, query, options = {}) {
|
|
@@ -14878,12 +15355,12 @@ async function getCallGraphPath(projectRoot, host, from, to, maxDepth, fromFileP
|
|
|
14878
15355
|
if (fromResolution.status !== "resolved" || toResolution.status !== "resolved") {
|
|
14879
15356
|
return { from: fromResolution, to: toResolution, path: [] };
|
|
14880
15357
|
}
|
|
14881
|
-
const
|
|
15358
|
+
const path33 = await indexer.findCallPathBySymbolIds(
|
|
14882
15359
|
fromResolution.symbolId,
|
|
14883
15360
|
toResolution.symbolId,
|
|
14884
15361
|
maxDepth
|
|
14885
15362
|
);
|
|
14886
|
-
return { from: fromResolution, to: toResolution, path:
|
|
15363
|
+
return { from: fromResolution, to: toResolution, path: path33 };
|
|
14887
15364
|
}
|
|
14888
15365
|
async function runIndexCodebase(projectRoot, host, args, onProgress) {
|
|
14889
15366
|
const root = getProjectRoot(projectRoot, host);
|
|
@@ -15075,6 +15552,141 @@ async function getIndexLogs(projectRoot, host, args) {
|
|
|
15075
15552
|
}).join("\n");
|
|
15076
15553
|
return { kind: "entries", text };
|
|
15077
15554
|
}
|
|
15555
|
+
function addKnowledgeBase(projectRoot, host, knowledgeBasePath) {
|
|
15556
|
+
const root = getProjectRoot(projectRoot, host);
|
|
15557
|
+
const inputPath = knowledgeBasePath.trim();
|
|
15558
|
+
const normalizedPath3 = path21.resolve(
|
|
15559
|
+
path21.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, root)
|
|
15560
|
+
);
|
|
15561
|
+
if (!existsSync12(normalizedPath3)) {
|
|
15562
|
+
return `Error: Directory does not exist: ${normalizedPath3}`;
|
|
15563
|
+
}
|
|
15564
|
+
let realPath;
|
|
15565
|
+
try {
|
|
15566
|
+
realPath = realpathSync5(normalizedPath3);
|
|
15567
|
+
} catch {
|
|
15568
|
+
return `Error: Cannot resolve path: ${normalizedPath3}`;
|
|
15569
|
+
}
|
|
15570
|
+
const blockedPrefixes = [
|
|
15571
|
+
"/etc",
|
|
15572
|
+
"/proc",
|
|
15573
|
+
"/sys",
|
|
15574
|
+
"/dev",
|
|
15575
|
+
"/boot",
|
|
15576
|
+
"/root",
|
|
15577
|
+
"/var/run",
|
|
15578
|
+
"/var/log"
|
|
15579
|
+
];
|
|
15580
|
+
const homeDir = process.platform === "win32" ? process.env.USERPROFILE ?? "" : process.env.HOME ?? "";
|
|
15581
|
+
const sensitiveDotDirs = [
|
|
15582
|
+
".ssh",
|
|
15583
|
+
".gnupg",
|
|
15584
|
+
".aws",
|
|
15585
|
+
".config/gcloud",
|
|
15586
|
+
".docker",
|
|
15587
|
+
".kube"
|
|
15588
|
+
];
|
|
15589
|
+
for (const prefix of blockedPrefixes) {
|
|
15590
|
+
if (realPath === prefix || realPath.startsWith(`${prefix}/`)) {
|
|
15591
|
+
return `Error: Adding sensitive directory as knowledge base is not allowed: ${normalizedPath3}`;
|
|
15592
|
+
}
|
|
15593
|
+
}
|
|
15594
|
+
for (const dotDir of sensitiveDotDirs) {
|
|
15595
|
+
const sensitiveDir = path21.join(homeDir, dotDir);
|
|
15596
|
+
if (sensitiveDir && (realPath === sensitiveDir || realPath.startsWith(`${sensitiveDir}/`))) {
|
|
15597
|
+
return `Error: Adding sensitive directory as knowledge base is not allowed: ${normalizedPath3}`;
|
|
15598
|
+
}
|
|
15599
|
+
}
|
|
15600
|
+
try {
|
|
15601
|
+
const stat5 = statSync5(normalizedPath3);
|
|
15602
|
+
if (!stat5.isDirectory()) {
|
|
15603
|
+
return `Error: Path is not a directory: ${normalizedPath3}`;
|
|
15604
|
+
}
|
|
15605
|
+
} catch (error) {
|
|
15606
|
+
return `Error: Cannot access directory: ${normalizedPath3} - ${error instanceof Error ? error.message : String(error)}`;
|
|
15607
|
+
}
|
|
15608
|
+
const config = loadEditableConfig(root, host);
|
|
15609
|
+
const knowledgeBases = Array.isArray(config.knowledgeBases) ? config.knowledgeBases : [];
|
|
15610
|
+
const alreadyExists = hasMatchingKnowledgeBasePath(knowledgeBases, normalizedPath3, root);
|
|
15611
|
+
if (alreadyExists) {
|
|
15612
|
+
return `Knowledge base already configured: ${normalizedPath3}`;
|
|
15613
|
+
}
|
|
15614
|
+
knowledgeBases.push(normalizedPath3);
|
|
15615
|
+
config.knowledgeBases = knowledgeBases;
|
|
15616
|
+
saveConfig(root, config, host);
|
|
15617
|
+
refreshIndexerForDirectory(root, host);
|
|
15618
|
+
let result = `${normalizedPath3}
|
|
15619
|
+
`;
|
|
15620
|
+
result += `Total knowledge bases: ${knowledgeBases.length}
|
|
15621
|
+
`;
|
|
15622
|
+
result += `Config path: ${getConfigPath(root, host)}
|
|
15623
|
+
`;
|
|
15624
|
+
result += `
|
|
15625
|
+
Run /index to rebuild the index with the new knowledge base.`;
|
|
15626
|
+
return result;
|
|
15627
|
+
}
|
|
15628
|
+
function listKnowledgeBases(projectRoot, host) {
|
|
15629
|
+
const root = getProjectRoot(projectRoot, host);
|
|
15630
|
+
const config = loadRuntimeConfig(root, host);
|
|
15631
|
+
const knowledgeBases = Array.isArray(config.knowledgeBases) ? config.knowledgeBases : [];
|
|
15632
|
+
if (knowledgeBases.length === 0) {
|
|
15633
|
+
return "No knowledge bases configured. Use add_knowledge_base to add folders.";
|
|
15634
|
+
}
|
|
15635
|
+
let result = `Knowledge Bases (${knowledgeBases.length}):
|
|
15636
|
+
|
|
15637
|
+
`;
|
|
15638
|
+
for (let i = 0; i < knowledgeBases.length; i++) {
|
|
15639
|
+
const kb = knowledgeBases[i];
|
|
15640
|
+
const resolvedPath = resolveKnowledgeBasePath(kb, root);
|
|
15641
|
+
const exists = existsSync12(resolvedPath);
|
|
15642
|
+
result += `[${i + 1}] ${kb}
|
|
15643
|
+
`;
|
|
15644
|
+
result += ` Resolved: ${resolvedPath}
|
|
15645
|
+
`;
|
|
15646
|
+
result += ` Status: ${exists ? "Exists" : "NOT FOUND"}
|
|
15647
|
+
`;
|
|
15648
|
+
if (exists) {
|
|
15649
|
+
try {
|
|
15650
|
+
const stat5 = statSync5(resolvedPath);
|
|
15651
|
+
result += ` Type: ${stat5.isDirectory() ? "Directory" : "File"}
|
|
15652
|
+
`;
|
|
15653
|
+
} catch {
|
|
15654
|
+
}
|
|
15655
|
+
}
|
|
15656
|
+
result += "\n";
|
|
15657
|
+
}
|
|
15658
|
+
const hasHostConfig = existsSync12(path21.join(root, getHostProjectConfigRelativePath(host)));
|
|
15659
|
+
if (hasHostConfig) {
|
|
15660
|
+
result += `
|
|
15661
|
+
Config sources: 1 file(s).`;
|
|
15662
|
+
}
|
|
15663
|
+
result += `
|
|
15664
|
+
Config file: ${getConfigPath(root, host)}`;
|
|
15665
|
+
return result;
|
|
15666
|
+
}
|
|
15667
|
+
function removeKnowledgeBase(projectRoot, host, knowledgeBasePath) {
|
|
15668
|
+
const root = getProjectRoot(projectRoot, host);
|
|
15669
|
+
const config = loadEditableConfig(root, host);
|
|
15670
|
+
const knowledgeBases = Array.isArray(config.knowledgeBases) ? config.knowledgeBases : [];
|
|
15671
|
+
const index = findKnowledgeBasePathIndex(knowledgeBases, knowledgeBasePath, root);
|
|
15672
|
+
if (index === -1) {
|
|
15673
|
+
return `Knowledge base not found: ${knowledgeBasePath}`;
|
|
15674
|
+
}
|
|
15675
|
+
const removed = knowledgeBases.splice(index, 1)[0];
|
|
15676
|
+
config.knowledgeBases = knowledgeBases;
|
|
15677
|
+
saveConfig(root, config, host);
|
|
15678
|
+
refreshIndexerForDirectory(root, host);
|
|
15679
|
+
let result = `Removed: ${removed}
|
|
15680
|
+
|
|
15681
|
+
`;
|
|
15682
|
+
result += `Remaining knowledge bases: ${knowledgeBases.length}
|
|
15683
|
+
`;
|
|
15684
|
+
result += `Config saved to: ${getConfigPath(root, host)}
|
|
15685
|
+
`;
|
|
15686
|
+
result += `
|
|
15687
|
+
Run /index to rebuild the index without the removed knowledge base.`;
|
|
15688
|
+
return result;
|
|
15689
|
+
}
|
|
15078
15690
|
|
|
15079
15691
|
// src/tools/context-search.ts
|
|
15080
15692
|
var MIN_CONTEXT_RESULT_LIMIT = 1;
|
|
@@ -15303,13 +15915,19 @@ async function resolveSearchContext(input, operations) {
|
|
|
15303
15915
|
(trace) => operations.lookup(symbol, MAX_CONTEXT_RESULT_LIMIT, scope, input.diagnostic ? trace : void 0)
|
|
15304
15916
|
);
|
|
15305
15917
|
};
|
|
15306
|
-
const tryConceptualSearch = async (searchQuery, scope, relaxedFieldsForAttempt) => {
|
|
15918
|
+
const tryConceptualSearch = async (searchQuery, scope, relaxedFieldsForAttempt, prioritizeSourcePaths) => {
|
|
15307
15919
|
return recordAttempt(
|
|
15308
15920
|
"conceptual",
|
|
15309
15921
|
searchQuery,
|
|
15310
15922
|
scope,
|
|
15311
15923
|
relaxedFieldsForAttempt,
|
|
15312
|
-
(trace) => operations.search(
|
|
15924
|
+
(trace) => operations.search(
|
|
15925
|
+
searchQuery,
|
|
15926
|
+
MAX_CONTEXT_RESULT_LIMIT,
|
|
15927
|
+
scope,
|
|
15928
|
+
input.diagnostic ? trace : void 0,
|
|
15929
|
+
{ prioritizeSourcePaths }
|
|
15930
|
+
)
|
|
15313
15931
|
);
|
|
15314
15932
|
};
|
|
15315
15933
|
const findSuccessfulAttemptState = (route) => {
|
|
@@ -15437,10 +16055,12 @@ Explicit symbol lookup only; conceptual search was not attempted.`
|
|
|
15437
16055
|
}
|
|
15438
16056
|
}
|
|
15439
16057
|
for (const attempt of conceptualAttemptPlan) {
|
|
16058
|
+
const attemptIntent = analyzeQueryIntent(attempt.queryText);
|
|
16059
|
+
const prioritizeSourcePaths = attemptIntent.primary !== "docs" && attemptIntent.primary !== "test";
|
|
15440
16060
|
if (inferredSymbol && attempt.queryText === inferredSymbol && attempt.queryText !== query) {
|
|
15441
16061
|
decisions.fallbackFromOriginalConceptualToInferred = true;
|
|
15442
16062
|
}
|
|
15443
|
-
const results = await tryConceptualSearch(attempt.queryText, attempt.scope, attempt.relaxed);
|
|
16063
|
+
const results = await tryConceptualSearch(attempt.queryText, attempt.scope, attempt.relaxed, prioritizeSourcePaths);
|
|
15444
16064
|
if (results.length > 0) {
|
|
15445
16065
|
const heading = buildPackHeading("conceptual", decisions);
|
|
15446
16066
|
const intent = analyzeQueryIntent(attempt.queryText);
|
|
@@ -15508,7 +16128,7 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
|
|
|
15508
16128
|
const directory = input.directory ?? void 0;
|
|
15509
16129
|
const tokenBudget = input.tokenBudget ?? void 0;
|
|
15510
16130
|
if (from && to) {
|
|
15511
|
-
const
|
|
16131
|
+
const path33 = await getCallGraphPath(
|
|
15512
16132
|
projectRoot,
|
|
15513
16133
|
host,
|
|
15514
16134
|
from,
|
|
@@ -15517,25 +16137,25 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
|
|
|
15517
16137
|
fromFilePath,
|
|
15518
16138
|
toFilePath
|
|
15519
16139
|
);
|
|
15520
|
-
const pathText = formatCallGraphPathResult(
|
|
15521
|
-
if (
|
|
16140
|
+
const pathText = formatCallGraphPathResult(path33);
|
|
16141
|
+
if (path33.path.length > 0) {
|
|
15522
16142
|
const fitted2 = fitTextToContextBudget(
|
|
15523
16143
|
pathText,
|
|
15524
16144
|
tokenBudget
|
|
15525
16145
|
);
|
|
15526
16146
|
return {
|
|
15527
16147
|
text: fitted2.text,
|
|
15528
|
-
details: fittedDetails("path", fitted2,
|
|
16148
|
+
details: fittedDetails("path", fitted2, path33.path.length)
|
|
15529
16149
|
};
|
|
15530
16150
|
}
|
|
15531
|
-
if (
|
|
16151
|
+
if (path33.from.status !== "resolved" || path33.to.status !== "resolved") {
|
|
15532
16152
|
const fitted2 = fitTextToContextBudget(pathText, tokenBudget);
|
|
15533
16153
|
return {
|
|
15534
16154
|
text: fitted2.text,
|
|
15535
16155
|
details: fittedDetails("path", fitted2, 0)
|
|
15536
16156
|
};
|
|
15537
16157
|
}
|
|
15538
|
-
const resolvedFrom =
|
|
16158
|
+
const resolvedFrom = path33.from;
|
|
15539
16159
|
const { callers } = await getCallGraphData(projectRoot, host, {
|
|
15540
16160
|
name: to,
|
|
15541
16161
|
direction: "callers",
|
|
@@ -15577,12 +16197,13 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
|
|
|
15577
16197
|
directory: scope.directory,
|
|
15578
16198
|
trace
|
|
15579
16199
|
}),
|
|
15580
|
-
search: (queryText, retrievalLimit, scope, trace) => searchCodebase(projectRoot, host, queryText, {
|
|
16200
|
+
search: (queryText, retrievalLimit, scope, trace, searchOptions) => searchCodebase(projectRoot, host, queryText, {
|
|
15581
16201
|
limit: retrievalLimit,
|
|
15582
16202
|
fileType: scope.fileType,
|
|
15583
16203
|
directory: scope.directory,
|
|
15584
16204
|
metadataOnly: true,
|
|
15585
|
-
trace
|
|
16205
|
+
trace,
|
|
16206
|
+
prioritizeSourcePaths: searchOptions?.prioritizeSourcePaths
|
|
15586
16207
|
})
|
|
15587
16208
|
});
|
|
15588
16209
|
}
|
|
@@ -16012,9 +16633,9 @@ function getRelevantEvidence(query) {
|
|
|
16012
16633
|
});
|
|
16013
16634
|
}
|
|
16014
16635
|
if (query.expected.acceptableFiles) {
|
|
16015
|
-
for (const
|
|
16636
|
+
for (const path33 of query.expected.acceptableFiles) {
|
|
16016
16637
|
legacyEvidence.push({
|
|
16017
|
-
path:
|
|
16638
|
+
path: path33,
|
|
16018
16639
|
...query.expected.symbol !== void 0 ? { symbol: query.expected.symbol } : {},
|
|
16019
16640
|
relevance: 1
|
|
16020
16641
|
});
|
|
@@ -16511,68 +17132,68 @@ function isStringArray4(value) {
|
|
|
16511
17132
|
function isNonEmptyString(value) {
|
|
16512
17133
|
return typeof value === "string" && value.trim().length > 0;
|
|
16513
17134
|
}
|
|
16514
|
-
function asPositiveNumber(value,
|
|
17135
|
+
function asPositiveNumber(value, path33) {
|
|
16515
17136
|
if (typeof value !== "number" || Number.isNaN(value) || value < 0) {
|
|
16516
|
-
throw new Error(`${
|
|
17137
|
+
throw new Error(`${path33} must be a non-negative number`);
|
|
16517
17138
|
}
|
|
16518
17139
|
return value;
|
|
16519
17140
|
}
|
|
16520
|
-
function parseQueryType(value,
|
|
17141
|
+
function parseQueryType(value, path33) {
|
|
16521
17142
|
if (value === "definition" || value === "implementation-intent" || value === "similarity" || value === "keyword-heavy" || value === "conceptual") {
|
|
16522
17143
|
return value;
|
|
16523
17144
|
}
|
|
16524
17145
|
throw new Error(
|
|
16525
|
-
`${
|
|
17146
|
+
`${path33} must be one of: definition, implementation-intent, similarity, keyword-heavy, conceptual`
|
|
16526
17147
|
);
|
|
16527
17148
|
}
|
|
16528
|
-
function parseExpectedRoute(value,
|
|
17149
|
+
function parseExpectedRoute(value, path33) {
|
|
16529
17150
|
if (value === void 0) return void 0;
|
|
16530
17151
|
if (value === "search" || value === "definition") return value;
|
|
16531
|
-
throw new Error(`${
|
|
17152
|
+
throw new Error(`${path33} must be one of: search, definition`);
|
|
16532
17153
|
}
|
|
16533
|
-
function parseExpectedOutcome(value,
|
|
17154
|
+
function parseExpectedOutcome(value, path33) {
|
|
16534
17155
|
if (value === void 0) return void 0;
|
|
16535
17156
|
if (value === "results" || value === "no-results") {
|
|
16536
17157
|
return value;
|
|
16537
17158
|
}
|
|
16538
|
-
throw new Error(`${
|
|
17159
|
+
throw new Error(`${path33} must be one of: results, no-results`);
|
|
16539
17160
|
}
|
|
16540
|
-
function parseRecoveryExpectation(value,
|
|
17161
|
+
function parseRecoveryExpectation(value, path33) {
|
|
16541
17162
|
if (value === void 0) return void 0;
|
|
16542
17163
|
if (value === "none" || value === "filter-relaxed") {
|
|
16543
17164
|
return value;
|
|
16544
17165
|
}
|
|
16545
|
-
throw new Error(`${
|
|
17166
|
+
throw new Error(`${path33} must be one of: none, filter-relaxed`);
|
|
16546
17167
|
}
|
|
16547
|
-
function parseQueryDifficulty(value,
|
|
17168
|
+
function parseQueryDifficulty(value, path33) {
|
|
16548
17169
|
if (value === void 0) return void 0;
|
|
16549
17170
|
if (value === "easy" || value === "medium" || value === "hard") {
|
|
16550
17171
|
return value;
|
|
16551
17172
|
}
|
|
16552
|
-
throw new Error(`${
|
|
17173
|
+
throw new Error(`${path33} must be one of: easy, medium, hard`);
|
|
16553
17174
|
}
|
|
16554
|
-
function parseQueryTags(value,
|
|
17175
|
+
function parseQueryTags(value, path33) {
|
|
16555
17176
|
if (value === void 0) return void 0;
|
|
16556
17177
|
if (!isStringArray4(value) || value.some((tag) => tag.trim().length === 0)) {
|
|
16557
|
-
throw new Error(`${
|
|
17178
|
+
throw new Error(`${path33} must be an array of non-empty strings`);
|
|
16558
17179
|
}
|
|
16559
17180
|
if (value.length > 16) {
|
|
16560
|
-
throw new Error(`${
|
|
17181
|
+
throw new Error(`${path33} must contain at most 16 tags`);
|
|
16561
17182
|
}
|
|
16562
17183
|
return value;
|
|
16563
17184
|
}
|
|
16564
|
-
function parseQueryArgs(value,
|
|
17185
|
+
function parseQueryArgs(value, path33) {
|
|
16565
17186
|
if (value === void 0) return void 0;
|
|
16566
17187
|
if (!isRecord3(value)) {
|
|
16567
|
-
throw new Error(`${
|
|
16568
|
-
}
|
|
16569
|
-
const symbol = parseStringOrUndefined(value.symbol, `${
|
|
16570
|
-
const filePath = parseStringOrUndefined(value.filePath, `${
|
|
16571
|
-
const fileType = parseStringOrUndefined(value.fileType, `${
|
|
16572
|
-
const directory = parseStringOrUndefined(value.directory, `${
|
|
16573
|
-
const callerLimit = parsePositiveIntegerOrUndefined(value.callerLimit, `${
|
|
16574
|
-
const calleeLimit = parsePositiveIntegerOrUndefined(value.calleeLimit, `${
|
|
16575
|
-
const tokenBudget = parsePositiveIntegerOrUndefined(value.tokenBudget, `${
|
|
17188
|
+
throw new Error(`${path33} must be an object`);
|
|
17189
|
+
}
|
|
17190
|
+
const symbol = parseStringOrUndefined(value.symbol, `${path33}.symbol`);
|
|
17191
|
+
const filePath = parseStringOrUndefined(value.filePath, `${path33}.filePath`);
|
|
17192
|
+
const fileType = parseStringOrUndefined(value.fileType, `${path33}.fileType`);
|
|
17193
|
+
const directory = parseStringOrUndefined(value.directory, `${path33}.directory`);
|
|
17194
|
+
const callerLimit = parsePositiveIntegerOrUndefined(value.callerLimit, `${path33}.callerLimit`);
|
|
17195
|
+
const calleeLimit = parsePositiveIntegerOrUndefined(value.calleeLimit, `${path33}.calleeLimit`);
|
|
17196
|
+
const tokenBudget = parsePositiveIntegerOrUndefined(value.tokenBudget, `${path33}.tokenBudget`);
|
|
16576
17197
|
return {
|
|
16577
17198
|
...symbol !== void 0 ? { symbol } : {},
|
|
16578
17199
|
...filePath !== void 0 ? { filePath } : {},
|
|
@@ -16583,50 +17204,50 @@ function parseQueryArgs(value, path31) {
|
|
|
16583
17204
|
...tokenBudget !== void 0 ? { tokenBudget } : {}
|
|
16584
17205
|
};
|
|
16585
17206
|
}
|
|
16586
|
-
function parsePositiveIntegerOrUndefined(value,
|
|
17207
|
+
function parsePositiveIntegerOrUndefined(value, path33) {
|
|
16587
17208
|
if (value === void 0 || value === null) return void 0;
|
|
16588
17209
|
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
16589
|
-
throw new Error(`${
|
|
17210
|
+
throw new Error(`${path33} must be a positive integer`);
|
|
16590
17211
|
}
|
|
16591
17212
|
return value;
|
|
16592
17213
|
}
|
|
16593
17214
|
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-]+)*)?$/;
|
|
16594
|
-
function parseSemanticVersion(value,
|
|
17215
|
+
function parseSemanticVersion(value, path33) {
|
|
16595
17216
|
if (!isNonEmptyString(value)) {
|
|
16596
|
-
throw new Error(`${
|
|
17217
|
+
throw new Error(`${path33} must be a non-empty string`);
|
|
16597
17218
|
}
|
|
16598
17219
|
if (!SEMVER_VERSION_PATTERN.test(value)) {
|
|
16599
|
-
throw new Error(`${
|
|
17220
|
+
throw new Error(`${path33} must be a valid semantic version (MAJOR.MINOR.PATCH)`);
|
|
16600
17221
|
}
|
|
16601
17222
|
return value;
|
|
16602
17223
|
}
|
|
16603
|
-
function parseRetrievalMode(value,
|
|
17224
|
+
function parseRetrievalMode(value, path33) {
|
|
16604
17225
|
if (value === void 0 || value === "search") return "search";
|
|
16605
17226
|
if (value === "context" || value === "edit-context") return value;
|
|
16606
|
-
throw new Error(`${
|
|
17227
|
+
throw new Error(`${path33} must be one of: search, context, edit-context`);
|
|
16607
17228
|
}
|
|
16608
|
-
function parseStringOrUndefined(value,
|
|
17229
|
+
function parseStringOrUndefined(value, path33) {
|
|
16609
17230
|
if (value === void 0 || value === null) return void 0;
|
|
16610
17231
|
if (!isNonEmptyString(value)) {
|
|
16611
|
-
throw new Error(`${
|
|
17232
|
+
throw new Error(`${path33} must be a non-empty string`);
|
|
16612
17233
|
}
|
|
16613
17234
|
return value;
|
|
16614
17235
|
}
|
|
16615
|
-
function parseGradedEvidence(value,
|
|
17236
|
+
function parseGradedEvidence(value, path33) {
|
|
16616
17237
|
if (value === void 0) return [];
|
|
16617
17238
|
if (!Array.isArray(value)) {
|
|
16618
|
-
throw new Error(`${
|
|
17239
|
+
throw new Error(`${path33} must be an array`);
|
|
16619
17240
|
}
|
|
16620
17241
|
return value.map((entry, index) => {
|
|
16621
17242
|
if (!isRecord3(entry)) {
|
|
16622
|
-
throw new Error(`${
|
|
17243
|
+
throw new Error(`${path33}[${index}] must be an object`);
|
|
16623
17244
|
}
|
|
16624
|
-
const evidencePath = parseStringOrUndefined(entry.path, `${
|
|
17245
|
+
const evidencePath = parseStringOrUndefined(entry.path, `${path33}[${index}].path`);
|
|
16625
17246
|
if (evidencePath === void 0) {
|
|
16626
|
-
throw new Error(`${
|
|
17247
|
+
throw new Error(`${path33}[${index}].path is required`);
|
|
16627
17248
|
}
|
|
16628
|
-
const symbol = parseStringOrUndefined(entry.symbol, `${
|
|
16629
|
-
const relevance = parseEvidenceRelevance(entry.relevance, `${
|
|
17249
|
+
const symbol = parseStringOrUndefined(entry.symbol, `${path33}[${index}].symbol`);
|
|
17250
|
+
const relevance = parseEvidenceRelevance(entry.relevance, `${path33}[${index}].relevance`);
|
|
16630
17251
|
return {
|
|
16631
17252
|
path: evidencePath,
|
|
16632
17253
|
...symbol !== void 0 ? { symbol } : {},
|
|
@@ -16634,27 +17255,27 @@ function parseGradedEvidence(value, path31) {
|
|
|
16634
17255
|
};
|
|
16635
17256
|
});
|
|
16636
17257
|
}
|
|
16637
|
-
function parseEvidenceRelevance(value,
|
|
17258
|
+
function parseEvidenceRelevance(value, path33) {
|
|
16638
17259
|
if (value === void 0) {
|
|
16639
|
-
throw new Error(`${
|
|
17260
|
+
throw new Error(`${path33} is required`);
|
|
16640
17261
|
}
|
|
16641
17262
|
if (value !== 1 && value !== 2 && value !== 3) {
|
|
16642
|
-
throw new Error(`${
|
|
17263
|
+
throw new Error(`${path33} must be 1, 2, or 3`);
|
|
16643
17264
|
}
|
|
16644
17265
|
return value;
|
|
16645
17266
|
}
|
|
16646
|
-
function parseExpectedGraphNeighbor(value,
|
|
17267
|
+
function parseExpectedGraphNeighbor(value, path33) {
|
|
16647
17268
|
if (value === void 0) return void 0;
|
|
16648
17269
|
if (!isRecord3(value)) {
|
|
16649
|
-
throw new Error(`${
|
|
17270
|
+
throw new Error(`${path33} must be an object`);
|
|
16650
17271
|
}
|
|
16651
17272
|
if (value.direction !== "caller" && value.direction !== "callee") {
|
|
16652
|
-
throw new Error(`${
|
|
17273
|
+
throw new Error(`${path33}.direction must be one of: caller, callee`);
|
|
16653
17274
|
}
|
|
16654
|
-
const filePath = parseStringOrUndefined(value.filePath, `${
|
|
16655
|
-
const symbol = parseStringOrUndefined(value.symbol, `${
|
|
17275
|
+
const filePath = parseStringOrUndefined(value.filePath, `${path33}.filePath`);
|
|
17276
|
+
const symbol = parseStringOrUndefined(value.symbol, `${path33}.symbol`);
|
|
16656
17277
|
if (filePath === void 0 && symbol === void 0) {
|
|
16657
|
-
throw new Error(`${
|
|
17278
|
+
throw new Error(`${path33} must include filePath or symbol`);
|
|
16658
17279
|
}
|
|
16659
17280
|
return {
|
|
16660
17281
|
direction: value.direction,
|
|
@@ -16662,9 +17283,9 @@ function parseExpectedGraphNeighbor(value, path31) {
|
|
|
16662
17283
|
...symbol !== void 0 ? { symbol } : {}
|
|
16663
17284
|
};
|
|
16664
17285
|
}
|
|
16665
|
-
function parseExpected(input,
|
|
17286
|
+
function parseExpected(input, path33) {
|
|
16666
17287
|
if (!isRecord3(input)) {
|
|
16667
|
-
throw new Error(`${
|
|
17288
|
+
throw new Error(`${path33} must be an object`);
|
|
16668
17289
|
}
|
|
16669
17290
|
const filePathRaw = input.filePath;
|
|
16670
17291
|
const acceptableFilesRaw = input.acceptableFiles;
|
|
@@ -16675,29 +17296,29 @@ function parseExpected(input, path31) {
|
|
|
16675
17296
|
const recoveryExpectationRaw = input.recoveryExpectation;
|
|
16676
17297
|
const gradedEvidenceRaw = input.gradedEvidence;
|
|
16677
17298
|
const graphNeighborRaw = input.graphNeighbor;
|
|
16678
|
-
const filePath = parseStringOrUndefined(filePathRaw, `${
|
|
17299
|
+
const filePath = parseStringOrUndefined(filePathRaw, `${path33}.filePath`);
|
|
16679
17300
|
const acceptableFiles = isStringArray4(acceptableFilesRaw) ? acceptableFilesRaw : void 0;
|
|
16680
|
-
const gradedEvidence = parseGradedEvidence(gradedEvidenceRaw, `${
|
|
16681
|
-
const graphNeighbor = parseExpectedGraphNeighbor(graphNeighborRaw, `${
|
|
16682
|
-
const expectedOutcome = parseExpectedOutcome(expectedOutcomeRaw, `${
|
|
17301
|
+
const gradedEvidence = parseGradedEvidence(gradedEvidenceRaw, `${path33}.gradedEvidence`);
|
|
17302
|
+
const graphNeighbor = parseExpectedGraphNeighbor(graphNeighborRaw, `${path33}.graphNeighbor`);
|
|
17303
|
+
const expectedOutcome = parseExpectedOutcome(expectedOutcomeRaw, `${path33}.expectedOutcome`);
|
|
16683
17304
|
if (expectedOutcome !== "no-results" && !filePath && (!acceptableFiles || acceptableFiles.length === 0) && gradedEvidence.length === 0) {
|
|
16684
17305
|
throw new Error(
|
|
16685
|
-
`${
|
|
17306
|
+
`${path33} must include expected.filePath, expected.acceptableFiles, or expected.gradedEvidence`
|
|
16686
17307
|
);
|
|
16687
17308
|
}
|
|
16688
17309
|
if (acceptableFilesRaw !== void 0 && !isStringArray4(acceptableFilesRaw)) {
|
|
16689
|
-
throw new Error(`${
|
|
17310
|
+
throw new Error(`${path33}.acceptableFiles must be an array of strings`);
|
|
16690
17311
|
}
|
|
16691
17312
|
if (symbolRaw !== void 0 && typeof symbolRaw !== "string") {
|
|
16692
|
-
throw new Error(`${
|
|
17313
|
+
throw new Error(`${path33}.symbol must be a string when provided`);
|
|
16693
17314
|
}
|
|
16694
17315
|
if (branchRaw !== void 0 && typeof branchRaw !== "string") {
|
|
16695
|
-
throw new Error(`${
|
|
17316
|
+
throw new Error(`${path33}.branch must be a string when provided`);
|
|
16696
17317
|
}
|
|
16697
|
-
const expectedRoute = parseExpectedRoute(expectedRouteRaw, `${
|
|
17318
|
+
const expectedRoute = parseExpectedRoute(expectedRouteRaw, `${path33}.expectedRoute`);
|
|
16698
17319
|
const recoveryExpectation = parseRecoveryExpectation(
|
|
16699
17320
|
recoveryExpectationRaw,
|
|
16700
|
-
`${
|
|
17321
|
+
`${path33}.recoveryExpectation`
|
|
16701
17322
|
);
|
|
16702
17323
|
return {
|
|
16703
17324
|
filePath,
|
|
@@ -16711,13 +17332,13 @@ function parseExpected(input, path31) {
|
|
|
16711
17332
|
...graphNeighbor !== void 0 ? { graphNeighbor } : {}
|
|
16712
17333
|
};
|
|
16713
17334
|
}
|
|
16714
|
-
function parseQueryLanguage(value,
|
|
16715
|
-
return parseStringOrUndefined(value,
|
|
17335
|
+
function parseQueryLanguage(value, path33) {
|
|
17336
|
+
return parseStringOrUndefined(value, path33);
|
|
16716
17337
|
}
|
|
16717
17338
|
function parseQuery(input, index) {
|
|
16718
|
-
const
|
|
17339
|
+
const path33 = `queries[${index}]`;
|
|
16719
17340
|
if (!isRecord3(input)) {
|
|
16720
|
-
throw new Error(`${
|
|
17341
|
+
throw new Error(`${path33} must be an object`);
|
|
16721
17342
|
}
|
|
16722
17343
|
const id = input.id;
|
|
16723
17344
|
const query = input.query;
|
|
@@ -16729,21 +17350,21 @@ function parseQuery(input, index) {
|
|
|
16729
17350
|
const tags = input.tags;
|
|
16730
17351
|
const args = input.args;
|
|
16731
17352
|
if (typeof id !== "string" || id.trim().length === 0) {
|
|
16732
|
-
throw new Error(`${
|
|
17353
|
+
throw new Error(`${path33}.id must be a non-empty string`);
|
|
16733
17354
|
}
|
|
16734
17355
|
if (typeof query !== "string" || query.trim().length === 0) {
|
|
16735
|
-
throw new Error(`${
|
|
17356
|
+
throw new Error(`${path33}.query must be a non-empty string`);
|
|
16736
17357
|
}
|
|
16737
17358
|
return {
|
|
16738
17359
|
id,
|
|
16739
17360
|
query,
|
|
16740
|
-
queryType: parseQueryType(queryType, `${
|
|
16741
|
-
retrievalMode: parseRetrievalMode(retrievalMode, `${
|
|
16742
|
-
language: parseQueryLanguage(language, `${
|
|
16743
|
-
difficulty: parseQueryDifficulty(difficulty, `${
|
|
16744
|
-
args: parseQueryArgs(args, `${
|
|
16745
|
-
tags: parseQueryTags(tags, `${
|
|
16746
|
-
expected: parseExpected(expected, `${
|
|
17361
|
+
queryType: parseQueryType(queryType, `${path33}.queryType`),
|
|
17362
|
+
retrievalMode: parseRetrievalMode(retrievalMode, `${path33}.retrievalMode`),
|
|
17363
|
+
language: parseQueryLanguage(language, `${path33}.language`),
|
|
17364
|
+
difficulty: parseQueryDifficulty(difficulty, `${path33}.difficulty`),
|
|
17365
|
+
args: parseQueryArgs(args, `${path33}.args`),
|
|
17366
|
+
tags: parseQueryTags(tags, `${path33}.tags`),
|
|
17367
|
+
expected: parseExpected(expected, `${path33}.expected`)
|
|
16747
17368
|
};
|
|
16748
17369
|
}
|
|
16749
17370
|
function parseGoldenDataset(raw, sourceLabel) {
|
|
@@ -17083,12 +17704,13 @@ async function runEvaluation(options) {
|
|
|
17083
17704
|
fileType: scope.fileType,
|
|
17084
17705
|
directory: scope.directory
|
|
17085
17706
|
}),
|
|
17086
|
-
search: (searchQuery, limit, scope) => indexer.search(searchQuery, limit, {
|
|
17707
|
+
search: (searchQuery, limit, scope, _trace, searchOptions) => indexer.search(searchQuery, limit, {
|
|
17087
17708
|
metadataOnly: true,
|
|
17088
17709
|
filterByBranch: !!query.expected.branch,
|
|
17089
17710
|
definitionIntent: false,
|
|
17090
17711
|
fileType: scope.fileType,
|
|
17091
|
-
directory: scope.directory
|
|
17712
|
+
directory: scope.directory,
|
|
17713
|
+
prioritizeSourcePaths: searchOptions?.prioritizeSourcePaths
|
|
17092
17714
|
})
|
|
17093
17715
|
}) : void 0;
|
|
17094
17716
|
const result = editContextResult?.results ?? contextResult?.details?.results ?? await indexer.search(query.query, 10, {
|
|
@@ -17709,7 +18331,7 @@ async function executeCallGraph(projectRoot, host, args) {
|
|
|
17709
18331
|
return { text: formatCallGraphResult(await getCallGraphData(projectRoot, host, args)) };
|
|
17710
18332
|
}
|
|
17711
18333
|
async function executeCallGraphPath(projectRoot, host, args) {
|
|
17712
|
-
const
|
|
18334
|
+
const path33 = await getCallGraphPath(
|
|
17713
18335
|
projectRoot,
|
|
17714
18336
|
host,
|
|
17715
18337
|
args.from,
|
|
@@ -17718,7 +18340,7 @@ async function executeCallGraphPath(projectRoot, host, args) {
|
|
|
17718
18340
|
args.fromFilePath,
|
|
17719
18341
|
args.toFilePath
|
|
17720
18342
|
);
|
|
17721
|
-
return { text: formatCallGraphPathResult(
|
|
18343
|
+
return { text: formatCallGraphPathResult(path33) };
|
|
17722
18344
|
}
|
|
17723
18345
|
async function executeCodeCommunities(projectRoot, host, args) {
|
|
17724
18346
|
const result = await getCodeCommunities(projectRoot, host, args);
|
|
@@ -17864,11 +18486,21 @@ var PI_TOOL_NAMES = [
|
|
|
17864
18486
|
TOOL_NAME.PI_KNOWLEDGE_BASE_ADD,
|
|
17865
18487
|
TOOL_NAME.PI_KNOWLEDGE_BASE_REMOVE
|
|
17866
18488
|
];
|
|
18489
|
+
var MCP_TOOL_NAMES = [
|
|
18490
|
+
...PORTABLE_TOOL_NAMES,
|
|
18491
|
+
TOOL_NAME.ADD_KNOWLEDGE_BASE,
|
|
18492
|
+
TOOL_NAME.LIST_KNOWLEDGE_BASES,
|
|
18493
|
+
TOOL_NAME.REMOVE_KNOWLEDGE_BASE
|
|
18494
|
+
];
|
|
17867
18495
|
|
|
17868
18496
|
// src/adapters/mcp/register-tools.ts
|
|
17869
18497
|
function allowNullAsUndefined(schema) {
|
|
17870
18498
|
return z2.preprocess((value) => value === null ? void 0 : value, schema);
|
|
17871
18499
|
}
|
|
18500
|
+
function knowledgeBaseResult(text) {
|
|
18501
|
+
const content = [{ type: "text", text }];
|
|
18502
|
+
return text.startsWith("Error: ") ? { content, isError: true } : { content };
|
|
18503
|
+
}
|
|
17872
18504
|
function registerMcpTools(server, runtime) {
|
|
17873
18505
|
server.tool(
|
|
17874
18506
|
TOOL_NAME.CODEBASE_CONTEXT,
|
|
@@ -17929,7 +18561,8 @@ function registerMcpTools(server, runtime) {
|
|
|
17929
18561
|
contextLines: allowNullAsUndefined(z2.number().optional()).describe("Number of extra lines to include before/after each match (default: 0)"),
|
|
17930
18562
|
blameAuthor: allowNullAsUndefined(z2.string().optional()).describe("Filter by git blame author name or email"),
|
|
17931
18563
|
blameSha: allowNullAsUndefined(z2.string().optional()).describe("Filter by git blame commit SHA or prefix"),
|
|
17932
|
-
blameSince: allowNullAsUndefined(z2.string().optional()).describe("Filter to chunks last changed on or after this date")
|
|
18564
|
+
blameSince: allowNullAsUndefined(z2.string().optional()).describe("Filter to chunks last changed on or after this date"),
|
|
18565
|
+
blameUntil: allowNullAsUndefined(z2.string().optional()).describe("Filter to chunks last changed on or before this date")
|
|
17933
18566
|
},
|
|
17934
18567
|
async (args) => {
|
|
17935
18568
|
return searchCodebaseWithEffectiveness(runtime.projectRoot, runtime.host, "search", args.query, {
|
|
@@ -17940,7 +18573,8 @@ function registerMcpTools(server, runtime) {
|
|
|
17940
18573
|
contextLines: args.contextLines,
|
|
17941
18574
|
blameAuthor: args.blameAuthor,
|
|
17942
18575
|
blameSha: args.blameSha,
|
|
17943
|
-
blameSince: args.blameSince
|
|
18576
|
+
blameSince: args.blameSince,
|
|
18577
|
+
blameUntil: args.blameUntil
|
|
17944
18578
|
}, (results) => {
|
|
17945
18579
|
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}":
|
|
17946
18580
|
|
|
@@ -17960,7 +18594,8 @@ ${formatSearchResults(results, "score")}`;
|
|
|
17960
18594
|
chunkType: allowNullAsUndefined(z2.enum(CHUNK_TYPES).optional()).describe("Filter by code chunk type"),
|
|
17961
18595
|
blameAuthor: allowNullAsUndefined(z2.string().optional()).describe("Filter by git blame author name or email"),
|
|
17962
18596
|
blameSha: allowNullAsUndefined(z2.string().optional()).describe("Filter by git blame commit SHA or prefix"),
|
|
17963
|
-
blameSince: allowNullAsUndefined(z2.string().optional()).describe("Filter to chunks last changed on or after this date")
|
|
18597
|
+
blameSince: allowNullAsUndefined(z2.string().optional()).describe("Filter to chunks last changed on or after this date"),
|
|
18598
|
+
blameUntil: allowNullAsUndefined(z2.string().optional()).describe("Filter to chunks last changed on or before this date")
|
|
17964
18599
|
},
|
|
17965
18600
|
async (args) => {
|
|
17966
18601
|
return searchCodebaseWithEffectiveness(runtime.projectRoot, runtime.host, "peek", args.query, {
|
|
@@ -17971,7 +18606,8 @@ ${formatSearchResults(results, "score")}`;
|
|
|
17971
18606
|
metadataOnly: true,
|
|
17972
18607
|
blameAuthor: args.blameAuthor,
|
|
17973
18608
|
blameSha: args.blameSha,
|
|
17974
|
-
blameSince: args.blameSince
|
|
18609
|
+
blameSince: args.blameSince,
|
|
18610
|
+
blameUntil: args.blameUntil
|
|
17975
18611
|
}, (results) => {
|
|
17976
18612
|
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}":
|
|
17977
18613
|
|
|
@@ -18048,7 +18684,9 @@ ${formatCodebasePeek(results)}`;
|
|
|
18048
18684
|
fileType: allowNullAsUndefined(z2.string().optional()).describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
|
|
18049
18685
|
directory: allowNullAsUndefined(z2.string().optional()).describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
|
|
18050
18686
|
chunkType: allowNullAsUndefined(z2.enum(CHUNK_TYPES).optional()).describe("Filter by code chunk type"),
|
|
18051
|
-
excludeFile: allowNullAsUndefined(z2.string().optional()).describe("Exclude results from this file path")
|
|
18687
|
+
excludeFile: allowNullAsUndefined(z2.string().optional()).describe("Exclude results from this file path"),
|
|
18688
|
+
blameSince: allowNullAsUndefined(z2.string().optional()).describe("Filter to chunks last changed on or after this date"),
|
|
18689
|
+
blameUntil: allowNullAsUndefined(z2.string().optional()).describe("Filter to chunks last changed on or before this date")
|
|
18052
18690
|
},
|
|
18053
18691
|
async (args) => {
|
|
18054
18692
|
const results = await findSimilarCode(runtime.projectRoot, runtime.host, args.code, {
|
|
@@ -18056,7 +18694,9 @@ ${formatCodebasePeek(results)}`;
|
|
|
18056
18694
|
fileType: args.fileType,
|
|
18057
18695
|
directory: args.directory,
|
|
18058
18696
|
chunkType: args.chunkType,
|
|
18059
|
-
excludeFile: args.excludeFile
|
|
18697
|
+
excludeFile: args.excludeFile,
|
|
18698
|
+
blameSince: args.blameSince,
|
|
18699
|
+
blameUntil: args.blameUntil
|
|
18060
18700
|
});
|
|
18061
18701
|
if (results.length === 0) {
|
|
18062
18702
|
return { content: [{ type: "text", text: "No similar code found. Try a different snippet or run index_codebase first." }] };
|
|
@@ -18160,6 +18800,37 @@ ${formatSearchResults(results)}` }] };
|
|
|
18160
18800
|
return { content: [{ type: "text", text: result.text }] };
|
|
18161
18801
|
}
|
|
18162
18802
|
);
|
|
18803
|
+
server.tool(
|
|
18804
|
+
TOOL_NAME.ADD_KNOWLEDGE_BASE,
|
|
18805
|
+
"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.",
|
|
18806
|
+
{
|
|
18807
|
+
path: z2.string().describe("Path to the folder to add as a knowledge base (absolute or relative to the project root)")
|
|
18808
|
+
},
|
|
18809
|
+
async (args) => {
|
|
18810
|
+
const result = addKnowledgeBase(runtime.projectRoot, runtime.host, args.path);
|
|
18811
|
+
return knowledgeBaseResult(result);
|
|
18812
|
+
}
|
|
18813
|
+
);
|
|
18814
|
+
server.tool(
|
|
18815
|
+
TOOL_NAME.LIST_KNOWLEDGE_BASES,
|
|
18816
|
+
"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.",
|
|
18817
|
+
{},
|
|
18818
|
+
async () => {
|
|
18819
|
+
const result = listKnowledgeBases(runtime.projectRoot, runtime.host);
|
|
18820
|
+
return knowledgeBaseResult(result);
|
|
18821
|
+
}
|
|
18822
|
+
);
|
|
18823
|
+
server.tool(
|
|
18824
|
+
TOOL_NAME.REMOVE_KNOWLEDGE_BASE,
|
|
18825
|
+
"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.",
|
|
18826
|
+
{
|
|
18827
|
+
path: z2.string().describe("Path of the knowledge base to remove (must match a project-local configured path exactly)")
|
|
18828
|
+
},
|
|
18829
|
+
async (args) => {
|
|
18830
|
+
const result = removeKnowledgeBase(runtime.projectRoot, runtime.host, args.path.trim());
|
|
18831
|
+
return knowledgeBaseResult(result);
|
|
18832
|
+
}
|
|
18833
|
+
);
|
|
18163
18834
|
}
|
|
18164
18835
|
|
|
18165
18836
|
// src/adapters/mcp/server.ts
|
|
@@ -18205,7 +18876,7 @@ function createMcpServer(projectRoot, config, host) {
|
|
|
18205
18876
|
}
|
|
18206
18877
|
|
|
18207
18878
|
// src/watcher/file-watcher.ts
|
|
18208
|
-
import { existsSync as existsSync15 } from "fs";
|
|
18879
|
+
import { existsSync as existsSync15, statSync as statSync6 } from "fs";
|
|
18209
18880
|
|
|
18210
18881
|
// node_modules/chokidar/index.js
|
|
18211
18882
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
@@ -18297,7 +18968,7 @@ var ReaddirpStream = class extends Readable {
|
|
|
18297
18968
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
18298
18969
|
const statMethod = opts.lstat ? lstat : stat;
|
|
18299
18970
|
if (wantBigintFsStats) {
|
|
18300
|
-
this._stat = (
|
|
18971
|
+
this._stat = (path33) => statMethod(path33, { bigint: true });
|
|
18301
18972
|
} else {
|
|
18302
18973
|
this._stat = statMethod;
|
|
18303
18974
|
}
|
|
@@ -18322,8 +18993,8 @@ var ReaddirpStream = class extends Readable {
|
|
|
18322
18993
|
const par = this.parent;
|
|
18323
18994
|
const fil = par && par.files;
|
|
18324
18995
|
if (fil && fil.length > 0) {
|
|
18325
|
-
const { path:
|
|
18326
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
18996
|
+
const { path: path33, depth } = par;
|
|
18997
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path33));
|
|
18327
18998
|
const awaited = await Promise.all(slice);
|
|
18328
18999
|
for (const entry of awaited) {
|
|
18329
19000
|
if (!entry)
|
|
@@ -18363,20 +19034,20 @@ var ReaddirpStream = class extends Readable {
|
|
|
18363
19034
|
this.reading = false;
|
|
18364
19035
|
}
|
|
18365
19036
|
}
|
|
18366
|
-
async _exploreDir(
|
|
19037
|
+
async _exploreDir(path33, depth) {
|
|
18367
19038
|
let files;
|
|
18368
19039
|
try {
|
|
18369
|
-
files = await readdir(
|
|
19040
|
+
files = await readdir(path33, this._rdOptions);
|
|
18370
19041
|
} catch (error) {
|
|
18371
19042
|
this._onError(error);
|
|
18372
19043
|
}
|
|
18373
|
-
return { files, depth, path:
|
|
19044
|
+
return { files, depth, path: path33 };
|
|
18374
19045
|
}
|
|
18375
|
-
async _formatEntry(dirent,
|
|
19046
|
+
async _formatEntry(dirent, path33) {
|
|
18376
19047
|
let entry;
|
|
18377
19048
|
const basename8 = this._isDirent ? dirent.name : dirent;
|
|
18378
19049
|
try {
|
|
18379
|
-
const fullPath = presolve(pjoin(
|
|
19050
|
+
const fullPath = presolve(pjoin(path33, basename8));
|
|
18380
19051
|
entry = { path: prelative(this._root, fullPath), fullPath, basename: basename8 };
|
|
18381
19052
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
18382
19053
|
} catch (err) {
|
|
@@ -18776,16 +19447,16 @@ var delFromSet = (main, prop, item) => {
|
|
|
18776
19447
|
};
|
|
18777
19448
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
18778
19449
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
18779
|
-
function createFsWatchInstance(
|
|
19450
|
+
function createFsWatchInstance(path33, options, listener, errHandler, emitRaw) {
|
|
18780
19451
|
const handleEvent = (rawEvent, evPath) => {
|
|
18781
|
-
listener(
|
|
18782
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
18783
|
-
if (evPath &&
|
|
18784
|
-
fsWatchBroadcast(sp.resolve(
|
|
19452
|
+
listener(path33);
|
|
19453
|
+
emitRaw(rawEvent, evPath, { watchedPath: path33 });
|
|
19454
|
+
if (evPath && path33 !== evPath) {
|
|
19455
|
+
fsWatchBroadcast(sp.resolve(path33, evPath), KEY_LISTENERS, sp.join(path33, evPath));
|
|
18785
19456
|
}
|
|
18786
19457
|
};
|
|
18787
19458
|
try {
|
|
18788
|
-
return fs_watch(
|
|
19459
|
+
return fs_watch(path33, {
|
|
18789
19460
|
persistent: options.persistent
|
|
18790
19461
|
}, handleEvent);
|
|
18791
19462
|
} catch (error) {
|
|
@@ -18801,12 +19472,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
18801
19472
|
listener(val1, val2, val3);
|
|
18802
19473
|
});
|
|
18803
19474
|
};
|
|
18804
|
-
var setFsWatchListener = (
|
|
19475
|
+
var setFsWatchListener = (path33, fullPath, options, handlers) => {
|
|
18805
19476
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
18806
19477
|
let cont = FsWatchInstances.get(fullPath);
|
|
18807
19478
|
let watcher;
|
|
18808
19479
|
if (!options.persistent) {
|
|
18809
|
-
watcher = createFsWatchInstance(
|
|
19480
|
+
watcher = createFsWatchInstance(path33, options, listener, errHandler, rawEmitter);
|
|
18810
19481
|
if (!watcher)
|
|
18811
19482
|
return;
|
|
18812
19483
|
return watcher.close.bind(watcher);
|
|
@@ -18817,7 +19488,7 @@ var setFsWatchListener = (path31, fullPath, options, handlers) => {
|
|
|
18817
19488
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
18818
19489
|
} else {
|
|
18819
19490
|
watcher = createFsWatchInstance(
|
|
18820
|
-
|
|
19491
|
+
path33,
|
|
18821
19492
|
options,
|
|
18822
19493
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
18823
19494
|
errHandler,
|
|
@@ -18832,7 +19503,7 @@ var setFsWatchListener = (path31, fullPath, options, handlers) => {
|
|
|
18832
19503
|
cont.watcherUnusable = true;
|
|
18833
19504
|
if (isWindows && error.code === "EPERM") {
|
|
18834
19505
|
try {
|
|
18835
|
-
const fd = await open(
|
|
19506
|
+
const fd = await open(path33, "r");
|
|
18836
19507
|
await fd.close();
|
|
18837
19508
|
broadcastErr(error);
|
|
18838
19509
|
} catch (err) {
|
|
@@ -18863,7 +19534,7 @@ var setFsWatchListener = (path31, fullPath, options, handlers) => {
|
|
|
18863
19534
|
};
|
|
18864
19535
|
};
|
|
18865
19536
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
18866
|
-
var setFsWatchFileListener = (
|
|
19537
|
+
var setFsWatchFileListener = (path33, fullPath, options, handlers) => {
|
|
18867
19538
|
const { listener, rawEmitter } = handlers;
|
|
18868
19539
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
18869
19540
|
const copts = cont && cont.options;
|
|
@@ -18885,7 +19556,7 @@ var setFsWatchFileListener = (path31, fullPath, options, handlers) => {
|
|
|
18885
19556
|
});
|
|
18886
19557
|
const currmtime = curr.mtimeMs;
|
|
18887
19558
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
18888
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
19559
|
+
foreach(cont.listeners, (listener2) => listener2(path33, curr));
|
|
18889
19560
|
}
|
|
18890
19561
|
})
|
|
18891
19562
|
};
|
|
@@ -18915,13 +19586,13 @@ var NodeFsHandler = class {
|
|
|
18915
19586
|
* @param listener on fs change
|
|
18916
19587
|
* @returns closer for the watcher instance
|
|
18917
19588
|
*/
|
|
18918
|
-
_watchWithNodeFs(
|
|
19589
|
+
_watchWithNodeFs(path33, listener) {
|
|
18919
19590
|
const opts = this.fsw.options;
|
|
18920
|
-
const directory = sp.dirname(
|
|
18921
|
-
const basename8 = sp.basename(
|
|
19591
|
+
const directory = sp.dirname(path33);
|
|
19592
|
+
const basename8 = sp.basename(path33);
|
|
18922
19593
|
const parent = this.fsw._getWatchedDir(directory);
|
|
18923
19594
|
parent.add(basename8);
|
|
18924
|
-
const absolutePath = sp.resolve(
|
|
19595
|
+
const absolutePath = sp.resolve(path33);
|
|
18925
19596
|
const options = {
|
|
18926
19597
|
persistent: opts.persistent
|
|
18927
19598
|
};
|
|
@@ -18931,12 +19602,12 @@ var NodeFsHandler = class {
|
|
|
18931
19602
|
if (opts.usePolling) {
|
|
18932
19603
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
18933
19604
|
options.interval = enableBin && isBinaryPath(basename8) ? opts.binaryInterval : opts.interval;
|
|
18934
|
-
closer = setFsWatchFileListener(
|
|
19605
|
+
closer = setFsWatchFileListener(path33, absolutePath, options, {
|
|
18935
19606
|
listener,
|
|
18936
19607
|
rawEmitter: this.fsw._emitRaw
|
|
18937
19608
|
});
|
|
18938
19609
|
} else {
|
|
18939
|
-
closer = setFsWatchListener(
|
|
19610
|
+
closer = setFsWatchListener(path33, absolutePath, options, {
|
|
18940
19611
|
listener,
|
|
18941
19612
|
errHandler: this._boundHandleError,
|
|
18942
19613
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -18958,7 +19629,7 @@ var NodeFsHandler = class {
|
|
|
18958
19629
|
let prevStats = stats;
|
|
18959
19630
|
if (parent.has(basename8))
|
|
18960
19631
|
return;
|
|
18961
|
-
const listener = async (
|
|
19632
|
+
const listener = async (path33, newStats) => {
|
|
18962
19633
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
18963
19634
|
return;
|
|
18964
19635
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -18972,11 +19643,11 @@ var NodeFsHandler = class {
|
|
|
18972
19643
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
18973
19644
|
}
|
|
18974
19645
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
18975
|
-
this.fsw._closeFile(
|
|
19646
|
+
this.fsw._closeFile(path33);
|
|
18976
19647
|
prevStats = newStats2;
|
|
18977
19648
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
18978
19649
|
if (closer2)
|
|
18979
|
-
this.fsw._addPathCloser(
|
|
19650
|
+
this.fsw._addPathCloser(path33, closer2);
|
|
18980
19651
|
} else {
|
|
18981
19652
|
prevStats = newStats2;
|
|
18982
19653
|
}
|
|
@@ -19008,7 +19679,7 @@ var NodeFsHandler = class {
|
|
|
19008
19679
|
* @param item basename of this item
|
|
19009
19680
|
* @returns true if no more processing is needed for this entry.
|
|
19010
19681
|
*/
|
|
19011
|
-
async _handleSymlink(entry, directory,
|
|
19682
|
+
async _handleSymlink(entry, directory, path33, item) {
|
|
19012
19683
|
if (this.fsw.closed) {
|
|
19013
19684
|
return;
|
|
19014
19685
|
}
|
|
@@ -19018,7 +19689,7 @@ var NodeFsHandler = class {
|
|
|
19018
19689
|
this.fsw._incrReadyCount();
|
|
19019
19690
|
let linkPath;
|
|
19020
19691
|
try {
|
|
19021
|
-
linkPath = await fsrealpath(
|
|
19692
|
+
linkPath = await fsrealpath(path33);
|
|
19022
19693
|
} catch (e) {
|
|
19023
19694
|
this.fsw._emitReady();
|
|
19024
19695
|
return true;
|
|
@@ -19028,12 +19699,12 @@ var NodeFsHandler = class {
|
|
|
19028
19699
|
if (dir.has(item)) {
|
|
19029
19700
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
19030
19701
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
19031
|
-
this.fsw._emit(EV.CHANGE,
|
|
19702
|
+
this.fsw._emit(EV.CHANGE, path33, entry.stats);
|
|
19032
19703
|
}
|
|
19033
19704
|
} else {
|
|
19034
19705
|
dir.add(item);
|
|
19035
19706
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
19036
|
-
this.fsw._emit(EV.ADD,
|
|
19707
|
+
this.fsw._emit(EV.ADD, path33, entry.stats);
|
|
19037
19708
|
}
|
|
19038
19709
|
this.fsw._emitReady();
|
|
19039
19710
|
return true;
|
|
@@ -19063,9 +19734,9 @@ var NodeFsHandler = class {
|
|
|
19063
19734
|
return;
|
|
19064
19735
|
}
|
|
19065
19736
|
const item = entry.path;
|
|
19066
|
-
let
|
|
19737
|
+
let path33 = sp.join(directory, item);
|
|
19067
19738
|
current.add(item);
|
|
19068
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
19739
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path33, item)) {
|
|
19069
19740
|
return;
|
|
19070
19741
|
}
|
|
19071
19742
|
if (this.fsw.closed) {
|
|
@@ -19074,11 +19745,11 @@ var NodeFsHandler = class {
|
|
|
19074
19745
|
}
|
|
19075
19746
|
if (item === target || !target && !previous.has(item)) {
|
|
19076
19747
|
this.fsw._incrReadyCount();
|
|
19077
|
-
|
|
19078
|
-
this._addToNodeFs(
|
|
19748
|
+
path33 = sp.join(dir, sp.relative(dir, path33));
|
|
19749
|
+
this._addToNodeFs(path33, initialAdd, wh, depth + 1);
|
|
19079
19750
|
}
|
|
19080
19751
|
}).on(EV.ERROR, this._boundHandleError);
|
|
19081
|
-
return new Promise((
|
|
19752
|
+
return new Promise((resolve20, reject) => {
|
|
19082
19753
|
if (!stream)
|
|
19083
19754
|
return reject();
|
|
19084
19755
|
stream.once(STR_END, () => {
|
|
@@ -19087,7 +19758,7 @@ var NodeFsHandler = class {
|
|
|
19087
19758
|
return;
|
|
19088
19759
|
}
|
|
19089
19760
|
const wasThrottled = throttler ? throttler.clear() : false;
|
|
19090
|
-
|
|
19761
|
+
resolve20(void 0);
|
|
19091
19762
|
previous.getChildren().filter((item) => {
|
|
19092
19763
|
return item !== directory && !current.has(item);
|
|
19093
19764
|
}).forEach((item) => {
|
|
@@ -19144,13 +19815,13 @@ var NodeFsHandler = class {
|
|
|
19144
19815
|
* @param depth Child path actually targeted for watch
|
|
19145
19816
|
* @param target Child path actually targeted for watch
|
|
19146
19817
|
*/
|
|
19147
|
-
async _addToNodeFs(
|
|
19818
|
+
async _addToNodeFs(path33, initialAdd, priorWh, depth, target) {
|
|
19148
19819
|
const ready = this.fsw._emitReady;
|
|
19149
|
-
if (this.fsw._isIgnored(
|
|
19820
|
+
if (this.fsw._isIgnored(path33) || this.fsw.closed) {
|
|
19150
19821
|
ready();
|
|
19151
19822
|
return false;
|
|
19152
19823
|
}
|
|
19153
|
-
const wh = this.fsw._getWatchHelpers(
|
|
19824
|
+
const wh = this.fsw._getWatchHelpers(path33);
|
|
19154
19825
|
if (priorWh) {
|
|
19155
19826
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
19156
19827
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -19166,8 +19837,8 @@ var NodeFsHandler = class {
|
|
|
19166
19837
|
const follow = this.fsw.options.followSymlinks;
|
|
19167
19838
|
let closer;
|
|
19168
19839
|
if (stats.isDirectory()) {
|
|
19169
|
-
const absPath = sp.resolve(
|
|
19170
|
-
const targetPath = follow ? await fsrealpath(
|
|
19840
|
+
const absPath = sp.resolve(path33);
|
|
19841
|
+
const targetPath = follow ? await fsrealpath(path33) : path33;
|
|
19171
19842
|
if (this.fsw.closed)
|
|
19172
19843
|
return;
|
|
19173
19844
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -19177,29 +19848,29 @@ var NodeFsHandler = class {
|
|
|
19177
19848
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
19178
19849
|
}
|
|
19179
19850
|
} else if (stats.isSymbolicLink()) {
|
|
19180
|
-
const targetPath = follow ? await fsrealpath(
|
|
19851
|
+
const targetPath = follow ? await fsrealpath(path33) : path33;
|
|
19181
19852
|
if (this.fsw.closed)
|
|
19182
19853
|
return;
|
|
19183
19854
|
const parent = sp.dirname(wh.watchPath);
|
|
19184
19855
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
19185
19856
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
19186
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
19857
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path33, wh, targetPath);
|
|
19187
19858
|
if (this.fsw.closed)
|
|
19188
19859
|
return;
|
|
19189
19860
|
if (targetPath !== void 0) {
|
|
19190
|
-
this.fsw._symlinkPaths.set(sp.resolve(
|
|
19861
|
+
this.fsw._symlinkPaths.set(sp.resolve(path33), targetPath);
|
|
19191
19862
|
}
|
|
19192
19863
|
} else {
|
|
19193
19864
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
19194
19865
|
}
|
|
19195
19866
|
ready();
|
|
19196
19867
|
if (closer)
|
|
19197
|
-
this.fsw._addPathCloser(
|
|
19868
|
+
this.fsw._addPathCloser(path33, closer);
|
|
19198
19869
|
return false;
|
|
19199
19870
|
} catch (error) {
|
|
19200
19871
|
if (this.fsw._handleError(error)) {
|
|
19201
19872
|
ready();
|
|
19202
|
-
return
|
|
19873
|
+
return path33;
|
|
19203
19874
|
}
|
|
19204
19875
|
}
|
|
19205
19876
|
}
|
|
@@ -19231,35 +19902,35 @@ function createPattern(matcher) {
|
|
|
19231
19902
|
if (matcher.path === string)
|
|
19232
19903
|
return true;
|
|
19233
19904
|
if (matcher.recursive) {
|
|
19234
|
-
const
|
|
19235
|
-
if (!
|
|
19905
|
+
const relative14 = sp2.relative(matcher.path, string);
|
|
19906
|
+
if (!relative14) {
|
|
19236
19907
|
return false;
|
|
19237
19908
|
}
|
|
19238
|
-
return !
|
|
19909
|
+
return !relative14.startsWith("..") && !sp2.isAbsolute(relative14);
|
|
19239
19910
|
}
|
|
19240
19911
|
return false;
|
|
19241
19912
|
};
|
|
19242
19913
|
}
|
|
19243
19914
|
return () => false;
|
|
19244
19915
|
}
|
|
19245
|
-
function normalizePath3(
|
|
19246
|
-
if (typeof
|
|
19916
|
+
function normalizePath3(path33) {
|
|
19917
|
+
if (typeof path33 !== "string")
|
|
19247
19918
|
throw new Error("string expected");
|
|
19248
|
-
|
|
19249
|
-
|
|
19919
|
+
path33 = sp2.normalize(path33);
|
|
19920
|
+
path33 = path33.replace(/\\/g, "/");
|
|
19250
19921
|
let prepend = false;
|
|
19251
|
-
if (
|
|
19922
|
+
if (path33.startsWith("//"))
|
|
19252
19923
|
prepend = true;
|
|
19253
|
-
|
|
19924
|
+
path33 = path33.replace(DOUBLE_SLASH_RE, "/");
|
|
19254
19925
|
if (prepend)
|
|
19255
|
-
|
|
19256
|
-
return
|
|
19926
|
+
path33 = "/" + path33;
|
|
19927
|
+
return path33;
|
|
19257
19928
|
}
|
|
19258
19929
|
function matchPatterns(patterns, testString, stats) {
|
|
19259
|
-
const
|
|
19930
|
+
const path33 = normalizePath3(testString);
|
|
19260
19931
|
for (let index = 0; index < patterns.length; index++) {
|
|
19261
19932
|
const pattern = patterns[index];
|
|
19262
|
-
if (pattern(
|
|
19933
|
+
if (pattern(path33, stats)) {
|
|
19263
19934
|
return true;
|
|
19264
19935
|
}
|
|
19265
19936
|
}
|
|
@@ -19297,19 +19968,19 @@ var toUnix = (string) => {
|
|
|
19297
19968
|
}
|
|
19298
19969
|
return str;
|
|
19299
19970
|
};
|
|
19300
|
-
var normalizePathToUnix = (
|
|
19301
|
-
var normalizeIgnored = (cwd = "") => (
|
|
19302
|
-
if (typeof
|
|
19303
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
19971
|
+
var normalizePathToUnix = (path33) => toUnix(sp2.normalize(toUnix(path33)));
|
|
19972
|
+
var normalizeIgnored = (cwd = "") => (path33) => {
|
|
19973
|
+
if (typeof path33 === "string") {
|
|
19974
|
+
return normalizePathToUnix(sp2.isAbsolute(path33) ? path33 : sp2.join(cwd, path33));
|
|
19304
19975
|
} else {
|
|
19305
|
-
return
|
|
19976
|
+
return path33;
|
|
19306
19977
|
}
|
|
19307
19978
|
};
|
|
19308
|
-
var getAbsolutePath = (
|
|
19309
|
-
if (sp2.isAbsolute(
|
|
19310
|
-
return
|
|
19979
|
+
var getAbsolutePath = (path33, cwd) => {
|
|
19980
|
+
if (sp2.isAbsolute(path33)) {
|
|
19981
|
+
return path33;
|
|
19311
19982
|
}
|
|
19312
|
-
return sp2.join(cwd,
|
|
19983
|
+
return sp2.join(cwd, path33);
|
|
19313
19984
|
};
|
|
19314
19985
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
19315
19986
|
var DirEntry = class {
|
|
@@ -19374,10 +20045,10 @@ var WatchHelper = class {
|
|
|
19374
20045
|
dirParts;
|
|
19375
20046
|
followSymlinks;
|
|
19376
20047
|
statMethod;
|
|
19377
|
-
constructor(
|
|
20048
|
+
constructor(path33, follow, fsw) {
|
|
19378
20049
|
this.fsw = fsw;
|
|
19379
|
-
const watchPath =
|
|
19380
|
-
this.path =
|
|
20050
|
+
const watchPath = path33;
|
|
20051
|
+
this.path = path33 = path33.replace(REPLACER_RE, "");
|
|
19381
20052
|
this.watchPath = watchPath;
|
|
19382
20053
|
this.fullWatchPath = sp2.resolve(watchPath);
|
|
19383
20054
|
this.dirParts = [];
|
|
@@ -19517,20 +20188,20 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
19517
20188
|
this._closePromise = void 0;
|
|
19518
20189
|
let paths = unifyPaths(paths_);
|
|
19519
20190
|
if (cwd) {
|
|
19520
|
-
paths = paths.map((
|
|
19521
|
-
const absPath = getAbsolutePath(
|
|
20191
|
+
paths = paths.map((path33) => {
|
|
20192
|
+
const absPath = getAbsolutePath(path33, cwd);
|
|
19522
20193
|
return absPath;
|
|
19523
20194
|
});
|
|
19524
20195
|
}
|
|
19525
|
-
paths.forEach((
|
|
19526
|
-
this._removeIgnoredPath(
|
|
20196
|
+
paths.forEach((path33) => {
|
|
20197
|
+
this._removeIgnoredPath(path33);
|
|
19527
20198
|
});
|
|
19528
20199
|
this._userIgnored = void 0;
|
|
19529
20200
|
if (!this._readyCount)
|
|
19530
20201
|
this._readyCount = 0;
|
|
19531
20202
|
this._readyCount += paths.length;
|
|
19532
|
-
Promise.all(paths.map(async (
|
|
19533
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
20203
|
+
Promise.all(paths.map(async (path33) => {
|
|
20204
|
+
const res = await this._nodeFsHandler._addToNodeFs(path33, !_internal, void 0, 0, _origAdd);
|
|
19534
20205
|
if (res)
|
|
19535
20206
|
this._emitReady();
|
|
19536
20207
|
return res;
|
|
@@ -19552,17 +20223,17 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
19552
20223
|
return this;
|
|
19553
20224
|
const paths = unifyPaths(paths_);
|
|
19554
20225
|
const { cwd } = this.options;
|
|
19555
|
-
paths.forEach((
|
|
19556
|
-
if (!sp2.isAbsolute(
|
|
20226
|
+
paths.forEach((path33) => {
|
|
20227
|
+
if (!sp2.isAbsolute(path33) && !this._closers.has(path33)) {
|
|
19557
20228
|
if (cwd)
|
|
19558
|
-
|
|
19559
|
-
|
|
20229
|
+
path33 = sp2.join(cwd, path33);
|
|
20230
|
+
path33 = sp2.resolve(path33);
|
|
19560
20231
|
}
|
|
19561
|
-
this._closePath(
|
|
19562
|
-
this._addIgnoredPath(
|
|
19563
|
-
if (this._watched.has(
|
|
20232
|
+
this._closePath(path33);
|
|
20233
|
+
this._addIgnoredPath(path33);
|
|
20234
|
+
if (this._watched.has(path33)) {
|
|
19564
20235
|
this._addIgnoredPath({
|
|
19565
|
-
path:
|
|
20236
|
+
path: path33,
|
|
19566
20237
|
recursive: true
|
|
19567
20238
|
});
|
|
19568
20239
|
}
|
|
@@ -19626,38 +20297,38 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
19626
20297
|
* @param stats arguments to be passed with event
|
|
19627
20298
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
19628
20299
|
*/
|
|
19629
|
-
async _emit(event,
|
|
20300
|
+
async _emit(event, path33, stats) {
|
|
19630
20301
|
if (this.closed)
|
|
19631
20302
|
return;
|
|
19632
20303
|
const opts = this.options;
|
|
19633
20304
|
if (isWindows)
|
|
19634
|
-
|
|
20305
|
+
path33 = sp2.normalize(path33);
|
|
19635
20306
|
if (opts.cwd)
|
|
19636
|
-
|
|
19637
|
-
const args = [
|
|
20307
|
+
path33 = sp2.relative(opts.cwd, path33);
|
|
20308
|
+
const args = [path33];
|
|
19638
20309
|
if (stats != null)
|
|
19639
20310
|
args.push(stats);
|
|
19640
20311
|
const awf = opts.awaitWriteFinish;
|
|
19641
20312
|
let pw;
|
|
19642
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
20313
|
+
if (awf && (pw = this._pendingWrites.get(path33))) {
|
|
19643
20314
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
19644
20315
|
return this;
|
|
19645
20316
|
}
|
|
19646
20317
|
if (opts.atomic) {
|
|
19647
20318
|
if (event === EVENTS.UNLINK) {
|
|
19648
|
-
this._pendingUnlinks.set(
|
|
20319
|
+
this._pendingUnlinks.set(path33, [event, ...args]);
|
|
19649
20320
|
setTimeout(() => {
|
|
19650
|
-
this._pendingUnlinks.forEach((entry,
|
|
20321
|
+
this._pendingUnlinks.forEach((entry, path34) => {
|
|
19651
20322
|
this.emit(...entry);
|
|
19652
20323
|
this.emit(EVENTS.ALL, ...entry);
|
|
19653
|
-
this._pendingUnlinks.delete(
|
|
20324
|
+
this._pendingUnlinks.delete(path34);
|
|
19654
20325
|
});
|
|
19655
20326
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
19656
20327
|
return this;
|
|
19657
20328
|
}
|
|
19658
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
20329
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path33)) {
|
|
19659
20330
|
event = EVENTS.CHANGE;
|
|
19660
|
-
this._pendingUnlinks.delete(
|
|
20331
|
+
this._pendingUnlinks.delete(path33);
|
|
19661
20332
|
}
|
|
19662
20333
|
}
|
|
19663
20334
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -19675,16 +20346,16 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
19675
20346
|
this.emitWithAll(event, args);
|
|
19676
20347
|
}
|
|
19677
20348
|
};
|
|
19678
|
-
this._awaitWriteFinish(
|
|
20349
|
+
this._awaitWriteFinish(path33, awf.stabilityThreshold, event, awfEmit);
|
|
19679
20350
|
return this;
|
|
19680
20351
|
}
|
|
19681
20352
|
if (event === EVENTS.CHANGE) {
|
|
19682
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
20353
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path33, 50);
|
|
19683
20354
|
if (isThrottled)
|
|
19684
20355
|
return this;
|
|
19685
20356
|
}
|
|
19686
20357
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
19687
|
-
const fullPath = opts.cwd ? sp2.join(opts.cwd,
|
|
20358
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path33) : path33;
|
|
19688
20359
|
let stats2;
|
|
19689
20360
|
try {
|
|
19690
20361
|
stats2 = await stat3(fullPath);
|
|
@@ -19715,23 +20386,23 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
19715
20386
|
* @param timeout duration of time to suppress duplicate actions
|
|
19716
20387
|
* @returns tracking object or false if action should be suppressed
|
|
19717
20388
|
*/
|
|
19718
|
-
_throttle(actionType,
|
|
20389
|
+
_throttle(actionType, path33, timeout) {
|
|
19719
20390
|
if (!this._throttled.has(actionType)) {
|
|
19720
20391
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
19721
20392
|
}
|
|
19722
20393
|
const action = this._throttled.get(actionType);
|
|
19723
20394
|
if (!action)
|
|
19724
20395
|
throw new Error("invalid throttle");
|
|
19725
|
-
const actionPath = action.get(
|
|
20396
|
+
const actionPath = action.get(path33);
|
|
19726
20397
|
if (actionPath) {
|
|
19727
20398
|
actionPath.count++;
|
|
19728
20399
|
return false;
|
|
19729
20400
|
}
|
|
19730
20401
|
let timeoutObject;
|
|
19731
20402
|
const clear = () => {
|
|
19732
|
-
const item = action.get(
|
|
20403
|
+
const item = action.get(path33);
|
|
19733
20404
|
const count = item ? item.count : 0;
|
|
19734
|
-
action.delete(
|
|
20405
|
+
action.delete(path33);
|
|
19735
20406
|
clearTimeout(timeoutObject);
|
|
19736
20407
|
if (item)
|
|
19737
20408
|
clearTimeout(item.timeoutObject);
|
|
@@ -19739,7 +20410,7 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
19739
20410
|
};
|
|
19740
20411
|
timeoutObject = setTimeout(clear, timeout);
|
|
19741
20412
|
const thr = { timeoutObject, clear, count: 0 };
|
|
19742
|
-
action.set(
|
|
20413
|
+
action.set(path33, thr);
|
|
19743
20414
|
return thr;
|
|
19744
20415
|
}
|
|
19745
20416
|
_incrReadyCount() {
|
|
@@ -19753,44 +20424,44 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
19753
20424
|
* @param event
|
|
19754
20425
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
19755
20426
|
*/
|
|
19756
|
-
_awaitWriteFinish(
|
|
20427
|
+
_awaitWriteFinish(path33, threshold, event, awfEmit) {
|
|
19757
20428
|
const awf = this.options.awaitWriteFinish;
|
|
19758
20429
|
if (typeof awf !== "object")
|
|
19759
20430
|
return;
|
|
19760
20431
|
const pollInterval = awf.pollInterval;
|
|
19761
20432
|
let timeoutHandler;
|
|
19762
|
-
let fullPath =
|
|
19763
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
19764
|
-
fullPath = sp2.join(this.options.cwd,
|
|
20433
|
+
let fullPath = path33;
|
|
20434
|
+
if (this.options.cwd && !sp2.isAbsolute(path33)) {
|
|
20435
|
+
fullPath = sp2.join(this.options.cwd, path33);
|
|
19765
20436
|
}
|
|
19766
20437
|
const now2 = /* @__PURE__ */ new Date();
|
|
19767
20438
|
const writes = this._pendingWrites;
|
|
19768
20439
|
function awaitWriteFinishFn(prevStat) {
|
|
19769
20440
|
statcb(fullPath, (err, curStat) => {
|
|
19770
|
-
if (err || !writes.has(
|
|
20441
|
+
if (err || !writes.has(path33)) {
|
|
19771
20442
|
if (err && err.code !== "ENOENT")
|
|
19772
20443
|
awfEmit(err);
|
|
19773
20444
|
return;
|
|
19774
20445
|
}
|
|
19775
20446
|
const now3 = Number(/* @__PURE__ */ new Date());
|
|
19776
20447
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
19777
|
-
writes.get(
|
|
20448
|
+
writes.get(path33).lastChange = now3;
|
|
19778
20449
|
}
|
|
19779
|
-
const pw = writes.get(
|
|
20450
|
+
const pw = writes.get(path33);
|
|
19780
20451
|
const df = now3 - pw.lastChange;
|
|
19781
20452
|
if (df >= threshold) {
|
|
19782
|
-
writes.delete(
|
|
20453
|
+
writes.delete(path33);
|
|
19783
20454
|
awfEmit(void 0, curStat);
|
|
19784
20455
|
} else {
|
|
19785
20456
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
19786
20457
|
}
|
|
19787
20458
|
});
|
|
19788
20459
|
}
|
|
19789
|
-
if (!writes.has(
|
|
19790
|
-
writes.set(
|
|
20460
|
+
if (!writes.has(path33)) {
|
|
20461
|
+
writes.set(path33, {
|
|
19791
20462
|
lastChange: now2,
|
|
19792
20463
|
cancelWait: () => {
|
|
19793
|
-
writes.delete(
|
|
20464
|
+
writes.delete(path33);
|
|
19794
20465
|
clearTimeout(timeoutHandler);
|
|
19795
20466
|
return event;
|
|
19796
20467
|
}
|
|
@@ -19801,8 +20472,8 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
19801
20472
|
/**
|
|
19802
20473
|
* Determines whether user has asked to ignore this path.
|
|
19803
20474
|
*/
|
|
19804
|
-
_isIgnored(
|
|
19805
|
-
if (this.options.atomic && DOT_RE.test(
|
|
20475
|
+
_isIgnored(path33, stats) {
|
|
20476
|
+
if (this.options.atomic && DOT_RE.test(path33))
|
|
19806
20477
|
return true;
|
|
19807
20478
|
if (!this._userIgnored) {
|
|
19808
20479
|
const { cwd } = this.options;
|
|
@@ -19812,17 +20483,17 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
19812
20483
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
19813
20484
|
this._userIgnored = anymatch(list, void 0);
|
|
19814
20485
|
}
|
|
19815
|
-
return this._userIgnored(
|
|
20486
|
+
return this._userIgnored(path33, stats);
|
|
19816
20487
|
}
|
|
19817
|
-
_isntIgnored(
|
|
19818
|
-
return !this._isIgnored(
|
|
20488
|
+
_isntIgnored(path33, stat5) {
|
|
20489
|
+
return !this._isIgnored(path33, stat5);
|
|
19819
20490
|
}
|
|
19820
20491
|
/**
|
|
19821
20492
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
19822
20493
|
* @param path file or directory pattern being watched
|
|
19823
20494
|
*/
|
|
19824
|
-
_getWatchHelpers(
|
|
19825
|
-
return new WatchHelper(
|
|
20495
|
+
_getWatchHelpers(path33) {
|
|
20496
|
+
return new WatchHelper(path33, this.options.followSymlinks, this);
|
|
19826
20497
|
}
|
|
19827
20498
|
// Directory helpers
|
|
19828
20499
|
// -----------------
|
|
@@ -19854,63 +20525,63 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
19854
20525
|
* @param item base path of item/directory
|
|
19855
20526
|
*/
|
|
19856
20527
|
_remove(directory, item, isDirectory) {
|
|
19857
|
-
const
|
|
19858
|
-
const fullPath = sp2.resolve(
|
|
19859
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
19860
|
-
if (!this._throttle("remove",
|
|
20528
|
+
const path33 = sp2.join(directory, item);
|
|
20529
|
+
const fullPath = sp2.resolve(path33);
|
|
20530
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path33) || this._watched.has(fullPath);
|
|
20531
|
+
if (!this._throttle("remove", path33, 100))
|
|
19861
20532
|
return;
|
|
19862
20533
|
if (!isDirectory && this._watched.size === 1) {
|
|
19863
20534
|
this.add(directory, item, true);
|
|
19864
20535
|
}
|
|
19865
|
-
const wp = this._getWatchedDir(
|
|
20536
|
+
const wp = this._getWatchedDir(path33);
|
|
19866
20537
|
const nestedDirectoryChildren = wp.getChildren();
|
|
19867
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
20538
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path33, nested));
|
|
19868
20539
|
const parent = this._getWatchedDir(directory);
|
|
19869
20540
|
const wasTracked = parent.has(item);
|
|
19870
20541
|
parent.remove(item);
|
|
19871
20542
|
if (this._symlinkPaths.has(fullPath)) {
|
|
19872
20543
|
this._symlinkPaths.delete(fullPath);
|
|
19873
20544
|
}
|
|
19874
|
-
let relPath =
|
|
20545
|
+
let relPath = path33;
|
|
19875
20546
|
if (this.options.cwd)
|
|
19876
|
-
relPath = sp2.relative(this.options.cwd,
|
|
20547
|
+
relPath = sp2.relative(this.options.cwd, path33);
|
|
19877
20548
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
19878
20549
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
19879
20550
|
if (event === EVENTS.ADD)
|
|
19880
20551
|
return;
|
|
19881
20552
|
}
|
|
19882
|
-
this._watched.delete(
|
|
20553
|
+
this._watched.delete(path33);
|
|
19883
20554
|
this._watched.delete(fullPath);
|
|
19884
20555
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
19885
|
-
if (wasTracked && !this._isIgnored(
|
|
19886
|
-
this._emit(eventName,
|
|
19887
|
-
this._closePath(
|
|
20556
|
+
if (wasTracked && !this._isIgnored(path33))
|
|
20557
|
+
this._emit(eventName, path33);
|
|
20558
|
+
this._closePath(path33);
|
|
19888
20559
|
}
|
|
19889
20560
|
/**
|
|
19890
20561
|
* Closes all watchers for a path
|
|
19891
20562
|
*/
|
|
19892
|
-
_closePath(
|
|
19893
|
-
this._closeFile(
|
|
19894
|
-
const dir = sp2.dirname(
|
|
19895
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
20563
|
+
_closePath(path33) {
|
|
20564
|
+
this._closeFile(path33);
|
|
20565
|
+
const dir = sp2.dirname(path33);
|
|
20566
|
+
this._getWatchedDir(dir).remove(sp2.basename(path33));
|
|
19896
20567
|
}
|
|
19897
20568
|
/**
|
|
19898
20569
|
* Closes only file-specific watchers
|
|
19899
20570
|
*/
|
|
19900
|
-
_closeFile(
|
|
19901
|
-
const closers = this._closers.get(
|
|
20571
|
+
_closeFile(path33) {
|
|
20572
|
+
const closers = this._closers.get(path33);
|
|
19902
20573
|
if (!closers)
|
|
19903
20574
|
return;
|
|
19904
20575
|
closers.forEach((closer) => closer());
|
|
19905
|
-
this._closers.delete(
|
|
20576
|
+
this._closers.delete(path33);
|
|
19906
20577
|
}
|
|
19907
|
-
_addPathCloser(
|
|
20578
|
+
_addPathCloser(path33, closer) {
|
|
19908
20579
|
if (!closer)
|
|
19909
20580
|
return;
|
|
19910
|
-
let list = this._closers.get(
|
|
20581
|
+
let list = this._closers.get(path33);
|
|
19911
20582
|
if (!list) {
|
|
19912
20583
|
list = [];
|
|
19913
|
-
this._closers.set(
|
|
20584
|
+
this._closers.set(path33, list);
|
|
19914
20585
|
}
|
|
19915
20586
|
list.push(closer);
|
|
19916
20587
|
}
|
|
@@ -19940,12 +20611,291 @@ function watch(paths, options = {}) {
|
|
|
19940
20611
|
var chokidar_default = { watch, FSWatcher };
|
|
19941
20612
|
|
|
19942
20613
|
// src/watcher/file-watcher.ts
|
|
20614
|
+
import * as path28 from "path";
|
|
20615
|
+
|
|
20616
|
+
// src/watcher/native-recursive-watcher.ts
|
|
20617
|
+
import { watch as watch2 } from "fs";
|
|
19943
20618
|
import * as path26 from "path";
|
|
20619
|
+
var NativeRecursiveWatcher = class {
|
|
20620
|
+
constructor(root, onChange, options = {}) {
|
|
20621
|
+
this.root = root;
|
|
20622
|
+
this.onChange = onChange;
|
|
20623
|
+
this.watchFactory = options.watchFactory ?? this.defaultWatchFactory;
|
|
20624
|
+
this.onError = options.onError;
|
|
20625
|
+
}
|
|
20626
|
+
root;
|
|
20627
|
+
onChange;
|
|
20628
|
+
watcher = null;
|
|
20629
|
+
listenerToken = 0;
|
|
20630
|
+
watchFactory;
|
|
20631
|
+
onError;
|
|
20632
|
+
start() {
|
|
20633
|
+
if (this.watcher) return;
|
|
20634
|
+
const token = ++this.listenerToken;
|
|
20635
|
+
const listener = (_eventType, filename) => {
|
|
20636
|
+
if (this.watcher === null || this.listenerToken !== token) return;
|
|
20637
|
+
const absolutePath = this.toAbsolutePath(filename);
|
|
20638
|
+
const nextResult = this.onChange(absolutePath);
|
|
20639
|
+
if (nextResult instanceof Promise) {
|
|
20640
|
+
void nextResult.catch((error) => {
|
|
20641
|
+
console.error("[codebase-index] Error handling native watcher event:", error);
|
|
20642
|
+
});
|
|
20643
|
+
}
|
|
20644
|
+
};
|
|
20645
|
+
const watcher = this.watchFactory(this.root, listener, {
|
|
20646
|
+
persistent: true,
|
|
20647
|
+
recursive: true
|
|
20648
|
+
});
|
|
20649
|
+
watcher.on?.("error", (error) => {
|
|
20650
|
+
if (this.watcher === watcher && this.listenerToken === token) {
|
|
20651
|
+
this.onError?.(error);
|
|
20652
|
+
}
|
|
20653
|
+
});
|
|
20654
|
+
this.watcher = watcher;
|
|
20655
|
+
}
|
|
20656
|
+
async stop() {
|
|
20657
|
+
const watcher = this.watcher;
|
|
20658
|
+
this.watcher = null;
|
|
20659
|
+
this.listenerToken += 1;
|
|
20660
|
+
if (!watcher) return;
|
|
20661
|
+
await watcher.close();
|
|
20662
|
+
}
|
|
20663
|
+
toAbsolutePath(filename) {
|
|
20664
|
+
if (filename == null) return null;
|
|
20665
|
+
const normalizedFilename = typeof filename === "string" ? filename : filename.toString();
|
|
20666
|
+
const absolutePath = path26.resolve(this.root, normalizedFilename);
|
|
20667
|
+
const relativePath = path26.relative(this.root, absolutePath);
|
|
20668
|
+
const outsideRoot = relativePath === ".." || relativePath.startsWith(`..${path26.sep}`) || path26.isAbsolute(relativePath);
|
|
20669
|
+
return outsideRoot ? null : absolutePath;
|
|
20670
|
+
}
|
|
20671
|
+
defaultWatchFactory = (root, listener, options) => watch2(root, options, listener);
|
|
20672
|
+
};
|
|
20673
|
+
|
|
20674
|
+
// src/watcher/snapshot.ts
|
|
20675
|
+
import * as fsPromises4 from "fs/promises";
|
|
20676
|
+
import * as path27 from "path";
|
|
20677
|
+
async function buildFileSnapshotScan(projectRoot, config, configPaths = []) {
|
|
20678
|
+
const normalizedProjectRoot = path27.resolve(projectRoot);
|
|
20679
|
+
const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
|
|
20680
|
+
const includePatterns = [...config.include, ...config.additionalInclude ?? []];
|
|
20681
|
+
const maxDepth = config.indexing?.maxDepth ?? -1;
|
|
20682
|
+
const snapshot = /* @__PURE__ */ new Map();
|
|
20683
|
+
const unreadablePrefixes = /* @__PURE__ */ new Set();
|
|
20684
|
+
const includeFile = async (filePath) => {
|
|
20685
|
+
const normalizedPath3 = path27.resolve(filePath);
|
|
20686
|
+
if (!shouldIncludeFile(normalizedPath3, normalizedProjectRoot, includePatterns, config.exclude, ignoreFilter)) return;
|
|
20687
|
+
const stat5 = await readStatIfFile(normalizedPath3, unreadablePrefixes);
|
|
20688
|
+
if (stat5) snapshot.set(normalizedPath3, { size: stat5.size, mtimeMs: stat5.mtimeMs });
|
|
20689
|
+
};
|
|
20690
|
+
const walk = async (directoryPath, depth) => {
|
|
20691
|
+
let entries;
|
|
20692
|
+
try {
|
|
20693
|
+
entries = await fsPromises4.readdir(directoryPath, { withFileTypes: true });
|
|
20694
|
+
} catch (error) {
|
|
20695
|
+
if (isMissingFsError(error)) return;
|
|
20696
|
+
if (isPermissionFsError(error)) {
|
|
20697
|
+
unreadablePrefixes.add(path27.resolve(directoryPath));
|
|
20698
|
+
return;
|
|
20699
|
+
}
|
|
20700
|
+
throw error;
|
|
20701
|
+
}
|
|
20702
|
+
for (const entry of entries) {
|
|
20703
|
+
const fullPath = path27.join(directoryPath, entry.name);
|
|
20704
|
+
const relativePath = path27.relative(normalizedProjectRoot, fullPath);
|
|
20705
|
+
if (entry.isDirectory()) {
|
|
20706
|
+
if (hasFilteredPathSegment(relativePath, path27.sep) || isRestrictedDirectory(relativePath, path27.sep)) continue;
|
|
20707
|
+
if (ignoreFilter.ignores(relativePath)) continue;
|
|
20708
|
+
if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
|
|
20709
|
+
} else if (entry.isFile()) {
|
|
20710
|
+
await includeFile(fullPath);
|
|
20711
|
+
}
|
|
20712
|
+
}
|
|
20713
|
+
};
|
|
20714
|
+
await walk(normalizedProjectRoot, 0);
|
|
20715
|
+
await includeExplicitConfigPaths(snapshot, unreadablePrefixes, configPaths);
|
|
20716
|
+
return { entries: snapshot, unreadablePrefixes };
|
|
20717
|
+
}
|
|
20718
|
+
async function buildFileSnapshotForPathScan(projectRoot, config, configPaths, targetPath) {
|
|
20719
|
+
const normalizedProjectRoot = path27.resolve(projectRoot);
|
|
20720
|
+
const normalizedTargetPath = path27.resolve(targetPath);
|
|
20721
|
+
if (!isWithinPath(normalizedProjectRoot, normalizedTargetPath)) {
|
|
20722
|
+
return { entries: /* @__PURE__ */ new Map(), unreadablePrefixes: /* @__PURE__ */ new Set() };
|
|
20723
|
+
}
|
|
20724
|
+
const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
|
|
20725
|
+
const includePatterns = [...config.include, ...config.additionalInclude ?? []];
|
|
20726
|
+
const maxDepth = config.indexing?.maxDepth ?? -1;
|
|
20727
|
+
const explicitConfigPaths = new Set(configPaths.map((configPath) => path27.resolve(configPath)));
|
|
20728
|
+
const snapshot = /* @__PURE__ */ new Map();
|
|
20729
|
+
const unreadablePrefixes = /* @__PURE__ */ new Set();
|
|
20730
|
+
const includeFile = async (filePath) => {
|
|
20731
|
+
const normalizedPath3 = path27.resolve(filePath);
|
|
20732
|
+
if (!explicitConfigPaths.has(normalizedPath3) && !shouldIncludeFile(
|
|
20733
|
+
normalizedPath3,
|
|
20734
|
+
normalizedProjectRoot,
|
|
20735
|
+
includePatterns,
|
|
20736
|
+
config.exclude,
|
|
20737
|
+
ignoreFilter
|
|
20738
|
+
)) return;
|
|
20739
|
+
const stat5 = await readStatIfFile(normalizedPath3, unreadablePrefixes);
|
|
20740
|
+
if (stat5) snapshot.set(normalizedPath3, { size: stat5.size, mtimeMs: stat5.mtimeMs });
|
|
20741
|
+
};
|
|
20742
|
+
const walk = async (directoryPath, depth) => {
|
|
20743
|
+
let entries;
|
|
20744
|
+
try {
|
|
20745
|
+
entries = await fsPromises4.readdir(directoryPath, { withFileTypes: true });
|
|
20746
|
+
} catch (error) {
|
|
20747
|
+
if (isMissingFsError(error)) return;
|
|
20748
|
+
if (isPermissionFsError(error)) {
|
|
20749
|
+
unreadablePrefixes.add(path27.resolve(directoryPath));
|
|
20750
|
+
return;
|
|
20751
|
+
}
|
|
20752
|
+
throw error;
|
|
20753
|
+
}
|
|
20754
|
+
for (const entry of entries) {
|
|
20755
|
+
const fullPath = path27.join(directoryPath, entry.name);
|
|
20756
|
+
const relativePath = path27.relative(normalizedProjectRoot, fullPath);
|
|
20757
|
+
if (entry.isDirectory()) {
|
|
20758
|
+
if (hasFilteredPathSegment(relativePath, path27.sep) || isRestrictedDirectory(relativePath, path27.sep)) continue;
|
|
20759
|
+
if (ignoreFilter.ignores(relativePath)) continue;
|
|
20760
|
+
if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
|
|
20761
|
+
} else if (entry.isFile()) {
|
|
20762
|
+
await includeFile(fullPath);
|
|
20763
|
+
}
|
|
20764
|
+
}
|
|
20765
|
+
};
|
|
20766
|
+
const targetStat = await readStatIfFile(normalizedTargetPath, unreadablePrefixes);
|
|
20767
|
+
if (targetStat) await includeFile(normalizedTargetPath);
|
|
20768
|
+
else await walk(normalizedTargetPath, 0);
|
|
20769
|
+
await includeExplicitConfigPathsInPath(snapshot, unreadablePrefixes, configPaths, normalizedTargetPath);
|
|
20770
|
+
return { entries: snapshot, unreadablePrefixes };
|
|
20771
|
+
}
|
|
20772
|
+
function completeFileSnapshot(previous, scan) {
|
|
20773
|
+
const completed = new Map(scan.entries);
|
|
20774
|
+
for (const unreadablePrefix of scan.unreadablePrefixes) {
|
|
20775
|
+
for (const [entryPath, entry] of previous) {
|
|
20776
|
+
if (isWithinPath(unreadablePrefix, entryPath) && !completed.has(entryPath)) completed.set(entryPath, entry);
|
|
20777
|
+
}
|
|
20778
|
+
}
|
|
20779
|
+
return completed;
|
|
20780
|
+
}
|
|
20781
|
+
async function includeExplicitConfigPaths(snapshot, unreadablePrefixes, configPaths) {
|
|
20782
|
+
for (const configPath of [...new Set(configPaths.map((value) => path27.resolve(value)))]) {
|
|
20783
|
+
if (snapshot.has(configPath)) continue;
|
|
20784
|
+
const stat5 = await readStatIfFile(configPath, unreadablePrefixes);
|
|
20785
|
+
if (stat5) snapshot.set(configPath, { size: stat5.size, mtimeMs: stat5.mtimeMs });
|
|
20786
|
+
}
|
|
20787
|
+
}
|
|
20788
|
+
async function includeExplicitConfigPathsInPath(snapshot, unreadablePrefixes, configPaths, targetPath) {
|
|
20789
|
+
await includeExplicitConfigPaths(
|
|
20790
|
+
snapshot,
|
|
20791
|
+
unreadablePrefixes,
|
|
20792
|
+
configPaths.filter((configPath) => isWithinPath(targetPath, path27.resolve(configPath)))
|
|
20793
|
+
);
|
|
20794
|
+
}
|
|
20795
|
+
function isWithinPath(parentPath, childPath) {
|
|
20796
|
+
const relativePath = path27.relative(parentPath, childPath);
|
|
20797
|
+
return relativePath === "" || !relativePath.startsWith(`..${path27.sep}`) && relativePath !== ".." && !path27.isAbsolute(relativePath);
|
|
20798
|
+
}
|
|
20799
|
+
async function readStatIfFile(filePath, unreadablePrefixes) {
|
|
20800
|
+
try {
|
|
20801
|
+
const stat5 = await fsPromises4.stat(filePath);
|
|
20802
|
+
return stat5.isFile() ? stat5 : null;
|
|
20803
|
+
} catch (error) {
|
|
20804
|
+
if (isMissingFsError(error)) return null;
|
|
20805
|
+
if (isPermissionFsError(error)) {
|
|
20806
|
+
unreadablePrefixes.add(path27.resolve(filePath));
|
|
20807
|
+
return null;
|
|
20808
|
+
}
|
|
20809
|
+
throw error;
|
|
20810
|
+
}
|
|
20811
|
+
}
|
|
20812
|
+
function isMissingFsError(error) {
|
|
20813
|
+
return error instanceof Error && ["ENOENT", "ENOTDIR"].includes(error.code ?? "");
|
|
20814
|
+
}
|
|
20815
|
+
function isPermissionFsError(error) {
|
|
20816
|
+
return error instanceof Error && ["EACCES", "EPERM"].includes(error.code ?? "");
|
|
20817
|
+
}
|
|
20818
|
+
var diffTypeOrder = { add: 0, change: 1, unlink: 2 };
|
|
20819
|
+
function diffFileSnapshots(previous, current, forcedChanges = /* @__PURE__ */ new Set()) {
|
|
20820
|
+
const changes = [];
|
|
20821
|
+
for (const [filePath, previousEntry] of previous) {
|
|
20822
|
+
const currentEntry = current.get(filePath);
|
|
20823
|
+
if (!currentEntry) changes.push({ type: "unlink", path: filePath });
|
|
20824
|
+
else if (forcedChanges.has(filePath) || currentEntry.size !== previousEntry.size || currentEntry.mtimeMs !== previousEntry.mtimeMs) {
|
|
20825
|
+
changes.push({ type: "change", path: filePath });
|
|
20826
|
+
}
|
|
20827
|
+
}
|
|
20828
|
+
for (const [filePath] of current) {
|
|
20829
|
+
if (!previous.has(filePath)) changes.push({ type: "add", path: filePath });
|
|
20830
|
+
}
|
|
20831
|
+
return changes.sort((left, right) => left.path.localeCompare(right.path) || diffTypeOrder[left.type] - diffTypeOrder[right.type]);
|
|
20832
|
+
}
|
|
20833
|
+
|
|
20834
|
+
// src/watcher/snapshot-reconciler.ts
|
|
20835
|
+
var FileSnapshotReconciler = class {
|
|
20836
|
+
constructor(projectRoot, config, configPaths) {
|
|
20837
|
+
this.projectRoot = projectRoot;
|
|
20838
|
+
this.config = config;
|
|
20839
|
+
this.configPaths = configPaths;
|
|
20840
|
+
}
|
|
20841
|
+
projectRoot;
|
|
20842
|
+
config;
|
|
20843
|
+
configPaths;
|
|
20844
|
+
snapshot = null;
|
|
20845
|
+
reconciliationTail = Promise.resolve();
|
|
20846
|
+
async initialize() {
|
|
20847
|
+
this.snapshot = (await buildFileSnapshotScan(this.projectRoot, this.config, this.configPaths)).entries;
|
|
20848
|
+
}
|
|
20849
|
+
async reconcile(invalidations = []) {
|
|
20850
|
+
if (this.snapshot === null) {
|
|
20851
|
+
throw new Error("FileSnapshotReconciler is not initialized. Call initialize() before reconcile().");
|
|
20852
|
+
}
|
|
20853
|
+
const reconciliation = this.reconciliationTail.then(async () => {
|
|
20854
|
+
const previousSnapshot = this.snapshot;
|
|
20855
|
+
if (previousSnapshot === null) {
|
|
20856
|
+
throw new Error("FileSnapshotReconciler is not initialized. Call initialize() before reconcile().");
|
|
20857
|
+
}
|
|
20858
|
+
const normalizedInvalidations = invalidations.map((invalidation) => typeof invalidation === "string" || invalidation === null ? { path: invalidation, forceChange: false } : { path: invalidation.path, forceChange: invalidation.forceChange === true });
|
|
20859
|
+
const scopedPaths = normalizedInvalidations.map((invalidation) => invalidation.path).filter((filePath) => filePath !== null);
|
|
20860
|
+
const scan = scopedPaths.length === 0 || scopedPaths.length !== normalizedInvalidations.length ? await buildFileSnapshotScan(this.projectRoot, this.config, this.configPaths) : await this.reconcilePaths(previousSnapshot, scopedPaths);
|
|
20861
|
+
const nextSnapshot = completeFileSnapshot(previousSnapshot, scan);
|
|
20862
|
+
const forcedChanges = new Set(normalizedInvalidations.filter((invalidation) => invalidation.path !== null && invalidation.forceChange).map((invalidation) => invalidation.path));
|
|
20863
|
+
const changes = diffFileSnapshots(previousSnapshot, nextSnapshot, forcedChanges);
|
|
20864
|
+
this.snapshot = nextSnapshot;
|
|
20865
|
+
return changes;
|
|
20866
|
+
});
|
|
20867
|
+
this.reconciliationTail = reconciliation.then(() => void 0, () => void 0);
|
|
20868
|
+
return reconciliation;
|
|
20869
|
+
}
|
|
20870
|
+
async reconcilePaths(previousSnapshot, invalidatedPaths) {
|
|
20871
|
+
const scopes = this.getScopes(invalidatedPaths);
|
|
20872
|
+
const entries = new Map(previousSnapshot);
|
|
20873
|
+
const unreadablePrefixes = /* @__PURE__ */ new Set();
|
|
20874
|
+
for (const scope of scopes) {
|
|
20875
|
+
for (const previousPath of entries.keys()) {
|
|
20876
|
+
if (isWithinPath(scope, previousPath)) entries.delete(previousPath);
|
|
20877
|
+
}
|
|
20878
|
+
const scopedScan = await buildFileSnapshotForPathScan(this.projectRoot, this.config, this.configPaths, scope);
|
|
20879
|
+
for (const [filePath, entry] of scopedScan.entries) entries.set(filePath, entry);
|
|
20880
|
+
for (const unreadablePrefix of scopedScan.unreadablePrefixes) unreadablePrefixes.add(unreadablePrefix);
|
|
20881
|
+
}
|
|
20882
|
+
return { entries, unreadablePrefixes };
|
|
20883
|
+
}
|
|
20884
|
+
getScopes(invalidatedPaths) {
|
|
20885
|
+
const uniquePaths = [...new Set(invalidatedPaths)].sort((left, right) => left.length - right.length);
|
|
20886
|
+
return uniquePaths.filter((candidate, index) => !uniquePaths.slice(0, index).some(
|
|
20887
|
+
(ancestor) => isWithinPath(ancestor, candidate)
|
|
20888
|
+
));
|
|
20889
|
+
}
|
|
20890
|
+
};
|
|
20891
|
+
|
|
20892
|
+
// src/watcher/file-watcher.ts
|
|
19944
20893
|
var FileWatcher = class {
|
|
19945
20894
|
watcher = null;
|
|
19946
20895
|
projectRoot;
|
|
19947
20896
|
config;
|
|
19948
20897
|
configPath;
|
|
20898
|
+
backend;
|
|
19949
20899
|
projectConfigPaths;
|
|
19950
20900
|
pendingChanges = /* @__PURE__ */ new Map();
|
|
19951
20901
|
debounceTimer = null;
|
|
@@ -19955,44 +20905,74 @@ var FileWatcher = class {
|
|
|
19955
20905
|
resolveReady = null;
|
|
19956
20906
|
pollingFallbackAttempted = false;
|
|
19957
20907
|
pendingClose = null;
|
|
20908
|
+
startupReadySignals = 1;
|
|
20909
|
+
nativeWatcher = null;
|
|
20910
|
+
nativeReconciler = null;
|
|
20911
|
+
nativeSetupGeneration = 0;
|
|
20912
|
+
nativeStarting = false;
|
|
20913
|
+
nativeInitializing = false;
|
|
20914
|
+
nativeReconcileTimer = null;
|
|
20915
|
+
nativeInvalidatedPaths = /* @__PURE__ */ new Map();
|
|
20916
|
+
configPathStates = /* @__PURE__ */ new Map();
|
|
19958
20917
|
constructor(projectRoot, config, host, options = {}) {
|
|
19959
20918
|
this.projectRoot = projectRoot;
|
|
19960
20919
|
this.config = config;
|
|
20920
|
+
this.backend = options.backend ?? "auto";
|
|
19961
20921
|
this.configPath = options.configPath;
|
|
19962
20922
|
this.projectConfigPaths = options.configPath ? [options.configPath] : getProjectConfigCandidatePaths(projectRoot, host);
|
|
19963
20923
|
}
|
|
19964
20924
|
start(handler) {
|
|
19965
|
-
if (this.watcher) {
|
|
20925
|
+
if (this.watcher || this.nativeWatcher || this.nativeStarting) {
|
|
19966
20926
|
return;
|
|
19967
20927
|
}
|
|
19968
20928
|
this.onChanges = handler;
|
|
19969
20929
|
this.pollingFallbackAttempted = false;
|
|
19970
20930
|
this.resetReady();
|
|
20931
|
+
if (this.shouldUseNativeWatcher()) {
|
|
20932
|
+
if (this.hasExternalConfigWatchTarget()) {
|
|
20933
|
+
this.setStartupReadySignals(2);
|
|
20934
|
+
this.startExternalConfigWatcher();
|
|
20935
|
+
}
|
|
20936
|
+
this.nativeStarting = true;
|
|
20937
|
+
void this.createNativeWatcher();
|
|
20938
|
+
return;
|
|
20939
|
+
}
|
|
19971
20940
|
this.createWatcher();
|
|
19972
20941
|
}
|
|
19973
20942
|
resetReady() {
|
|
19974
|
-
this.readyPromise = new Promise((
|
|
19975
|
-
this.resolveReady =
|
|
20943
|
+
this.readyPromise = new Promise((resolve20) => {
|
|
20944
|
+
this.resolveReady = resolve20;
|
|
19976
20945
|
});
|
|
20946
|
+
this.startupReadySignals = 1;
|
|
19977
20947
|
}
|
|
19978
|
-
|
|
19979
|
-
|
|
19980
|
-
|
|
19981
|
-
if (this.configPath) {
|
|
19982
|
-
watchTargets = [this.projectRoot, this.configPath];
|
|
19983
|
-
} else {
|
|
19984
|
-
const externalConfigTargets = this.projectConfigPaths.filter((projectConfigPath) => {
|
|
19985
|
-
const relativeConfigPath = path26.relative(this.projectRoot, projectConfigPath);
|
|
19986
|
-
return this.isOutsideProjectPath(relativeConfigPath);
|
|
19987
|
-
}).map((projectConfigPath) => existsSync15(projectConfigPath) ? projectConfigPath : this.getNearestExistingDirectory(path26.dirname(projectConfigPath)));
|
|
19988
|
-
const uniqueExternalConfigTargets = [...new Set(externalConfigTargets)];
|
|
19989
|
-
if (uniqueExternalConfigTargets.length > 0) {
|
|
19990
|
-
watchTargets = [this.projectRoot, ...uniqueExternalConfigTargets];
|
|
19991
|
-
}
|
|
20948
|
+
setStartupReadySignals(expectedSignals) {
|
|
20949
|
+
if (!this.readyPromise) {
|
|
20950
|
+
return;
|
|
19992
20951
|
}
|
|
20952
|
+
this.startupReadySignals = Math.max(0, expectedSignals);
|
|
20953
|
+
}
|
|
20954
|
+
reportStartupReadySignal() {
|
|
20955
|
+
if (!this.readyPromise || !this.resolveReady) {
|
|
20956
|
+
return;
|
|
20957
|
+
}
|
|
20958
|
+
if (this.startupReadySignals <= 0) {
|
|
20959
|
+
return;
|
|
20960
|
+
}
|
|
20961
|
+
this.startupReadySignals -= 1;
|
|
20962
|
+
if (this.startupReadySignals !== 0) {
|
|
20963
|
+
return;
|
|
20964
|
+
}
|
|
20965
|
+
this.resolveReady();
|
|
20966
|
+
this.resolveReady = null;
|
|
20967
|
+
}
|
|
20968
|
+
createWatcher(watchTargets, usePolling = false, reportsStartupReady = true) {
|
|
20969
|
+
let reportedStartupReady = false;
|
|
20970
|
+
this.configPathStates = this.getConfigPathStates();
|
|
20971
|
+
const ignoreFilter = createIgnoreFilter(this.projectRoot);
|
|
20972
|
+
const resolvedWatchTargets = watchTargets ?? this.getFullChokidarWatchTargets();
|
|
19993
20973
|
const watcherOptions = {
|
|
19994
20974
|
ignored: (filePath) => {
|
|
19995
|
-
const relativePath =
|
|
20975
|
+
const relativePath = path28.relative(this.projectRoot, filePath);
|
|
19996
20976
|
if (!relativePath) return false;
|
|
19997
20977
|
if (this.isProjectConfigPathOrAncestor(relativePath)) {
|
|
19998
20978
|
return false;
|
|
@@ -20000,10 +20980,10 @@ var FileWatcher = class {
|
|
|
20000
20980
|
if (this.isOutsideProjectPath(relativePath)) {
|
|
20001
20981
|
return true;
|
|
20002
20982
|
}
|
|
20003
|
-
if (hasFilteredPathSegment(relativePath,
|
|
20983
|
+
if (hasFilteredPathSegment(relativePath, path28.sep)) {
|
|
20004
20984
|
return true;
|
|
20005
20985
|
}
|
|
20006
|
-
if (isRestrictedDirectory(relativePath,
|
|
20986
|
+
if (isRestrictedDirectory(relativePath, path28.sep)) {
|
|
20007
20987
|
return true;
|
|
20008
20988
|
}
|
|
20009
20989
|
if (ignoreFilter.ignores(relativePath)) {
|
|
@@ -20036,10 +21016,13 @@ var FileWatcher = class {
|
|
|
20036
21016
|
watcher = new FSWatcher(watcherOptions);
|
|
20037
21017
|
}
|
|
20038
21018
|
this.watcher = watcher;
|
|
20039
|
-
watcher.
|
|
21019
|
+
watcher.on("ready", () => {
|
|
20040
21020
|
if (this.watcher !== watcher) return;
|
|
20041
|
-
this.
|
|
20042
|
-
|
|
21021
|
+
this.reconcileConfigPathStates();
|
|
21022
|
+
if (reportsStartupReady) {
|
|
21023
|
+
this.reportStartupReadySignal();
|
|
21024
|
+
reportedStartupReady = true;
|
|
21025
|
+
}
|
|
20043
21026
|
});
|
|
20044
21027
|
watcher.on("error", (error) => {
|
|
20045
21028
|
const err = error instanceof Error ? error : null;
|
|
@@ -20053,10 +21036,13 @@ var FileWatcher = class {
|
|
|
20053
21036
|
console.error("[codebase-index] Failed to close exhausted file watcher:", closeError);
|
|
20054
21037
|
});
|
|
20055
21038
|
if (this.onChanges) {
|
|
21039
|
+
const replacementReportsStartupReady = reportsStartupReady || reportedStartupReady;
|
|
20056
21040
|
if (!this.resolveReady) {
|
|
20057
21041
|
this.resetReady();
|
|
21042
|
+
} else if (reportedStartupReady) {
|
|
21043
|
+
this.startupReadySignals += 1;
|
|
20058
21044
|
}
|
|
20059
|
-
this.createWatcher(true);
|
|
21045
|
+
this.createWatcher(resolvedWatchTargets, true, replacementReportsStartupReady);
|
|
20060
21046
|
} else {
|
|
20061
21047
|
this.watcher = null;
|
|
20062
21048
|
}
|
|
@@ -20067,13 +21053,166 @@ var FileWatcher = class {
|
|
|
20067
21053
|
watcher.on("add", (filePath) => this.handleChange(watcher, "add", filePath));
|
|
20068
21054
|
watcher.on("change", (filePath) => this.handleChange(watcher, "change", filePath));
|
|
20069
21055
|
watcher.on("unlink", (filePath) => this.handleChange(watcher, "unlink", filePath));
|
|
20070
|
-
watcher.add(
|
|
21056
|
+
watcher.add(resolvedWatchTargets);
|
|
21057
|
+
}
|
|
21058
|
+
shouldUseNativeWatcher() {
|
|
21059
|
+
if (this.backend === "chokidar") {
|
|
21060
|
+
return false;
|
|
21061
|
+
}
|
|
21062
|
+
return true;
|
|
21063
|
+
}
|
|
21064
|
+
getFullChokidarWatchTargets() {
|
|
21065
|
+
if (this.configPath) {
|
|
21066
|
+
return [this.projectRoot, this.configPath];
|
|
21067
|
+
}
|
|
21068
|
+
const externalConfigTargets = this.getExternalConfigWatchTargets();
|
|
21069
|
+
if (externalConfigTargets.length === 0) {
|
|
21070
|
+
return this.projectRoot;
|
|
21071
|
+
}
|
|
21072
|
+
return [this.projectRoot, ...externalConfigTargets];
|
|
21073
|
+
}
|
|
21074
|
+
getExternalConfigWatchTargets() {
|
|
21075
|
+
return [...new Set(
|
|
21076
|
+
this.projectConfigPaths.filter((projectConfigPath) => {
|
|
21077
|
+
const relativeConfigPath = path28.relative(this.projectRoot, projectConfigPath);
|
|
21078
|
+
return this.isOutsideProjectPath(relativeConfigPath);
|
|
21079
|
+
}).map((projectConfigPath) => {
|
|
21080
|
+
if (existsSync15(projectConfigPath)) {
|
|
21081
|
+
return projectConfigPath;
|
|
21082
|
+
}
|
|
21083
|
+
return this.getNearestExistingDirectory(path28.dirname(projectConfigPath));
|
|
21084
|
+
})
|
|
21085
|
+
)];
|
|
21086
|
+
}
|
|
21087
|
+
hasExternalConfigWatchTarget() {
|
|
21088
|
+
return this.getExternalConfigWatchTargets().length > 0;
|
|
21089
|
+
}
|
|
21090
|
+
startExternalConfigWatcher(usePolling = false) {
|
|
21091
|
+
const externalTargets = this.getExternalConfigWatchTargets();
|
|
21092
|
+
if (externalTargets.length === 0) {
|
|
21093
|
+
return;
|
|
21094
|
+
}
|
|
21095
|
+
this.createWatcher(externalTargets, usePolling);
|
|
21096
|
+
}
|
|
21097
|
+
async createNativeWatcher() {
|
|
21098
|
+
const generation = ++this.nativeSetupGeneration;
|
|
21099
|
+
const reconciler = new FileSnapshotReconciler(this.projectRoot, this.config, this.projectConfigPaths);
|
|
21100
|
+
const watcher = new NativeRecursiveWatcher(
|
|
21101
|
+
this.projectRoot,
|
|
21102
|
+
(filePath) => this.scheduleNativeReconciliation(generation, filePath),
|
|
21103
|
+
{ onError: (error) => void this.fallbackFromNativeWatcher(generation, error) }
|
|
21104
|
+
);
|
|
21105
|
+
this.nativeReconciler = reconciler;
|
|
21106
|
+
this.nativeWatcher = watcher;
|
|
21107
|
+
this.nativeInitializing = true;
|
|
21108
|
+
try {
|
|
21109
|
+
watcher.start();
|
|
21110
|
+
if (!this.isCurrentNativeSetup(generation)) {
|
|
21111
|
+
await watcher.stop();
|
|
21112
|
+
return;
|
|
21113
|
+
}
|
|
21114
|
+
await reconciler.initialize();
|
|
21115
|
+
if (!this.isCurrentNativeSetup(generation) || this.nativeWatcher !== watcher) {
|
|
21116
|
+
await watcher.stop();
|
|
21117
|
+
return;
|
|
21118
|
+
}
|
|
21119
|
+
this.nativeStarting = false;
|
|
21120
|
+
this.nativeInitializing = false;
|
|
21121
|
+
await this.reconcileNativeWatcherWithPendingInvalidations(generation);
|
|
21122
|
+
this.reportStartupReadySignal();
|
|
21123
|
+
} catch (error) {
|
|
21124
|
+
if (!this.isCurrentNativeSetup(generation)) return;
|
|
21125
|
+
this.nativeInitializing = false;
|
|
21126
|
+
if (this.nativeWatcher) {
|
|
21127
|
+
await this.fallbackFromNativeWatcher(generation, error);
|
|
21128
|
+
return;
|
|
21129
|
+
}
|
|
21130
|
+
this.nativeStarting = false;
|
|
21131
|
+
const externalWatcher = this.watcher;
|
|
21132
|
+
this.watcher = null;
|
|
21133
|
+
this.nativeReconciler = null;
|
|
21134
|
+
await externalWatcher?.close();
|
|
21135
|
+
console.warn("[codebase-index] Native recursive watcher unavailable; using Chokidar fallback.", error);
|
|
21136
|
+
this.setStartupReadySignals(1);
|
|
21137
|
+
this.createWatcher();
|
|
21138
|
+
}
|
|
21139
|
+
}
|
|
21140
|
+
isCurrentNativeSetup(generation) {
|
|
21141
|
+
return this.nativeSetupGeneration === generation && this.onChanges !== null;
|
|
21142
|
+
}
|
|
21143
|
+
scheduleNativeReconciliation(generation, filePath) {
|
|
21144
|
+
if (!this.isCurrentNativeSetup(generation)) return;
|
|
21145
|
+
const requiresFullReconciliation = filePath === path28.join(this.projectRoot, ".gitignore");
|
|
21146
|
+
const invalidatedPath = requiresFullReconciliation ? null : filePath;
|
|
21147
|
+
this.nativeInvalidatedPaths.set(invalidatedPath, invalidatedPath !== null);
|
|
21148
|
+
if (this.nativeReconcileTimer) {
|
|
21149
|
+
clearTimeout(this.nativeReconcileTimer);
|
|
21150
|
+
}
|
|
21151
|
+
this.nativeReconcileTimer = setTimeout(() => {
|
|
21152
|
+
this.nativeReconcileTimer = null;
|
|
21153
|
+
void this.reconcileNativeWatcherFromQueue(generation);
|
|
21154
|
+
}, 100);
|
|
21155
|
+
}
|
|
21156
|
+
reconcileNativeWatcherFromQueue(generation) {
|
|
21157
|
+
if (!this.isCurrentNativeSetup(generation) || this.nativeInitializing) return;
|
|
21158
|
+
const invalidatedPaths = this.popNativeInvalidations();
|
|
21159
|
+
if (invalidatedPaths.length === 0) return;
|
|
21160
|
+
void this.reconcileNativeWatcher(generation, invalidatedPaths);
|
|
21161
|
+
}
|
|
21162
|
+
async reconcileNativeWatcher(generation, invalidatedPaths) {
|
|
21163
|
+
if (!this.isCurrentNativeSetup(generation) || !this.nativeReconciler) return;
|
|
21164
|
+
try {
|
|
21165
|
+
const reconciler = this.nativeReconciler;
|
|
21166
|
+
const changes = await reconciler.reconcile(invalidatedPaths);
|
|
21167
|
+
if (!this.isCurrentNativeSetup(generation) || this.nativeReconciler !== reconciler) return;
|
|
21168
|
+
this.recordChanges(changes);
|
|
21169
|
+
} catch (error) {
|
|
21170
|
+
await this.fallbackFromNativeWatcher(generation, error);
|
|
21171
|
+
}
|
|
21172
|
+
}
|
|
21173
|
+
async reconcileNativeWatcherWithPendingInvalidations(generation) {
|
|
21174
|
+
const invalidatedPaths = this.popNativeInvalidations();
|
|
21175
|
+
if (invalidatedPaths.length === 0) return;
|
|
21176
|
+
await this.reconcileNativeWatcher(generation, invalidatedPaths);
|
|
21177
|
+
}
|
|
21178
|
+
popNativeInvalidations() {
|
|
21179
|
+
if (this.nativeInvalidatedPaths.size === 0) return [];
|
|
21180
|
+
const invalidations = [...this.nativeInvalidatedPaths].map(([invalidatedPath, forceChange]) => ({
|
|
21181
|
+
path: invalidatedPath,
|
|
21182
|
+
forceChange
|
|
21183
|
+
}));
|
|
21184
|
+
this.nativeInvalidatedPaths.clear();
|
|
21185
|
+
return invalidations;
|
|
21186
|
+
}
|
|
21187
|
+
async fallbackFromNativeWatcher(generation, error) {
|
|
21188
|
+
if (!this.isCurrentNativeSetup(generation)) return;
|
|
21189
|
+
const watcher = this.nativeWatcher;
|
|
21190
|
+
const externalWatcher = this.watcher;
|
|
21191
|
+
this.nativeWatcher = null;
|
|
21192
|
+
this.watcher = null;
|
|
21193
|
+
this.nativeReconciler = null;
|
|
21194
|
+
this.nativeStarting = false;
|
|
21195
|
+
this.nativeInitializing = false;
|
|
21196
|
+
this.nativeSetupGeneration += 1;
|
|
21197
|
+
if (this.nativeReconcileTimer) {
|
|
21198
|
+
clearTimeout(this.nativeReconcileTimer);
|
|
21199
|
+
this.nativeReconcileTimer = null;
|
|
21200
|
+
}
|
|
21201
|
+
this.nativeInvalidatedPaths.clear();
|
|
21202
|
+
this.setStartupReadySignals(1);
|
|
21203
|
+
console.warn("[codebase-index] Native recursive watcher failed; using Chokidar fallback.", error);
|
|
21204
|
+
await watcher?.stop();
|
|
21205
|
+
await externalWatcher?.close();
|
|
21206
|
+
if (this.onChanges) {
|
|
21207
|
+
this.createWatcher();
|
|
21208
|
+
}
|
|
20071
21209
|
}
|
|
20072
21210
|
handleChange(watcher, type, filePath) {
|
|
20073
21211
|
if (this.watcher !== watcher) {
|
|
20074
21212
|
return;
|
|
20075
21213
|
}
|
|
20076
21214
|
if (this.isProjectConfigPath(filePath)) {
|
|
21215
|
+
this.updateConfigPathState(filePath);
|
|
20077
21216
|
this.pendingChanges.set(filePath, type);
|
|
20078
21217
|
this.scheduleFlush();
|
|
20079
21218
|
return;
|
|
@@ -20088,27 +21227,33 @@ var FileWatcher = class {
|
|
|
20088
21227
|
)) {
|
|
20089
21228
|
return;
|
|
20090
21229
|
}
|
|
20091
|
-
this.
|
|
21230
|
+
this.recordChanges([{ path: filePath, type }]);
|
|
21231
|
+
}
|
|
21232
|
+
recordChanges(changes) {
|
|
21233
|
+
if (changes.length === 0) return;
|
|
21234
|
+
for (const change of changes) {
|
|
21235
|
+
this.pendingChanges.set(change.path, change.type);
|
|
21236
|
+
}
|
|
20092
21237
|
this.scheduleFlush();
|
|
20093
21238
|
}
|
|
20094
21239
|
isProjectConfigPath(filePath) {
|
|
20095
|
-
const relativePath =
|
|
20096
|
-
const normalizedRelativePath =
|
|
21240
|
+
const relativePath = path28.relative(this.projectRoot, filePath);
|
|
21241
|
+
const normalizedRelativePath = path28.normalize(relativePath);
|
|
20097
21242
|
return this.getProjectConfigRelativePaths().some((configPath) => configPath === normalizedRelativePath);
|
|
20098
21243
|
}
|
|
20099
21244
|
isProjectConfigPathOrAncestor(relativePath) {
|
|
20100
|
-
const normalizedRelativePath =
|
|
21245
|
+
const normalizedRelativePath = path28.normalize(relativePath);
|
|
20101
21246
|
return this.getProjectConfigRelativePaths().some(
|
|
20102
|
-
(configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${
|
|
21247
|
+
(configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path28.sep}`)
|
|
20103
21248
|
);
|
|
20104
21249
|
}
|
|
20105
21250
|
isOutsideProjectPath(relativePath) {
|
|
20106
|
-
return relativePath === ".." || relativePath.startsWith(`..${
|
|
21251
|
+
return relativePath === ".." || relativePath.startsWith(`..${path28.sep}`) || path28.isAbsolute(relativePath);
|
|
20107
21252
|
}
|
|
20108
21253
|
getNearestExistingDirectory(directoryPath) {
|
|
20109
21254
|
let candidate = directoryPath;
|
|
20110
21255
|
while (!existsSync15(candidate)) {
|
|
20111
|
-
const parent =
|
|
21256
|
+
const parent = path28.dirname(candidate);
|
|
20112
21257
|
if (parent === candidate) break;
|
|
20113
21258
|
candidate = parent;
|
|
20114
21259
|
}
|
|
@@ -20116,9 +21261,51 @@ var FileWatcher = class {
|
|
|
20116
21261
|
}
|
|
20117
21262
|
getProjectConfigRelativePaths() {
|
|
20118
21263
|
return this.projectConfigPaths.map(
|
|
20119
|
-
(configPath) =>
|
|
21264
|
+
(configPath) => path28.normalize(path28.relative(this.projectRoot, configPath))
|
|
20120
21265
|
);
|
|
20121
21266
|
}
|
|
21267
|
+
getConfigPathStates() {
|
|
21268
|
+
const states = /* @__PURE__ */ new Map();
|
|
21269
|
+
for (const configPath of this.projectConfigPaths) {
|
|
21270
|
+
const state = this.getConfigPathState(configPath);
|
|
21271
|
+
if (state) states.set(configPath, state);
|
|
21272
|
+
}
|
|
21273
|
+
return states;
|
|
21274
|
+
}
|
|
21275
|
+
getConfigPathState(configPath) {
|
|
21276
|
+
try {
|
|
21277
|
+
const stats = statSync6(configPath);
|
|
21278
|
+
return stats.isFile() ? { mtimeMs: stats.mtimeMs, size: stats.size } : void 0;
|
|
21279
|
+
} catch (error) {
|
|
21280
|
+
void error;
|
|
21281
|
+
return void 0;
|
|
21282
|
+
}
|
|
21283
|
+
}
|
|
21284
|
+
updateConfigPathState(configPath) {
|
|
21285
|
+
const state = this.getConfigPathState(configPath);
|
|
21286
|
+
if (state) {
|
|
21287
|
+
this.configPathStates.set(configPath, state);
|
|
21288
|
+
} else {
|
|
21289
|
+
this.configPathStates.delete(configPath);
|
|
21290
|
+
}
|
|
21291
|
+
}
|
|
21292
|
+
reconcileConfigPathStates() {
|
|
21293
|
+
const nextStates = this.getConfigPathStates();
|
|
21294
|
+
const changes = [];
|
|
21295
|
+
for (const configPath of this.projectConfigPaths) {
|
|
21296
|
+
const previous = this.configPathStates.get(configPath);
|
|
21297
|
+
const next = nextStates.get(configPath);
|
|
21298
|
+
if (!previous && next) {
|
|
21299
|
+
changes.push({ path: configPath, type: "add" });
|
|
21300
|
+
} else if (previous && !next) {
|
|
21301
|
+
changes.push({ path: configPath, type: "unlink" });
|
|
21302
|
+
} else if (previous && next && (previous.size !== next.size || previous.mtimeMs !== next.mtimeMs)) {
|
|
21303
|
+
changes.push({ path: configPath, type: "change" });
|
|
21304
|
+
}
|
|
21305
|
+
}
|
|
21306
|
+
this.configPathStates = nextStates;
|
|
21307
|
+
this.recordChanges(changes);
|
|
21308
|
+
}
|
|
20122
21309
|
scheduleFlush() {
|
|
20123
21310
|
if (this.debounceTimer) {
|
|
20124
21311
|
clearTimeout(this.debounceTimer);
|
|
@@ -20132,7 +21319,7 @@ var FileWatcher = class {
|
|
|
20132
21319
|
return;
|
|
20133
21320
|
}
|
|
20134
21321
|
const changes = Array.from(this.pendingChanges.entries()).map(
|
|
20135
|
-
([
|
|
21322
|
+
([path33, type]) => ({ path: path33, type })
|
|
20136
21323
|
);
|
|
20137
21324
|
this.pendingChanges.clear();
|
|
20138
21325
|
try {
|
|
@@ -20146,20 +21333,31 @@ var FileWatcher = class {
|
|
|
20146
21333
|
clearTimeout(this.debounceTimer);
|
|
20147
21334
|
this.debounceTimer = null;
|
|
20148
21335
|
}
|
|
21336
|
+
if (this.nativeReconcileTimer) {
|
|
21337
|
+
clearTimeout(this.nativeReconcileTimer);
|
|
21338
|
+
this.nativeReconcileTimer = null;
|
|
21339
|
+
}
|
|
21340
|
+
this.nativeInvalidatedPaths.clear();
|
|
20149
21341
|
const watcher = this.watcher;
|
|
21342
|
+
const nativeWatcher = this.nativeWatcher;
|
|
20150
21343
|
const pendingClose = this.pendingClose;
|
|
20151
21344
|
const resolveReady = this.resolveReady;
|
|
20152
21345
|
this.watcher = null;
|
|
21346
|
+
this.nativeWatcher = null;
|
|
21347
|
+
this.nativeReconciler = null;
|
|
21348
|
+
this.nativeStarting = false;
|
|
21349
|
+
this.nativeInitializing = false;
|
|
21350
|
+
this.nativeSetupGeneration += 1;
|
|
20153
21351
|
this.pendingClose = null;
|
|
20154
21352
|
this.resolveReady = null;
|
|
20155
21353
|
this.readyPromise = null;
|
|
20156
21354
|
this.pendingChanges.clear();
|
|
20157
21355
|
this.onChanges = null;
|
|
20158
|
-
await Promise.all([watcher?.close(), pendingClose]);
|
|
21356
|
+
await Promise.all([watcher?.close(), nativeWatcher?.stop(), pendingClose]);
|
|
20159
21357
|
resolveReady?.();
|
|
20160
21358
|
}
|
|
20161
21359
|
isRunning() {
|
|
20162
|
-
return this.watcher !== null;
|
|
21360
|
+
return this.watcher !== null || this.nativeWatcher !== null || this.nativeStarting;
|
|
20163
21361
|
}
|
|
20164
21362
|
async waitUntilReady() {
|
|
20165
21363
|
await (this.readyPromise ?? Promise.resolve());
|
|
@@ -20167,7 +21365,7 @@ var FileWatcher = class {
|
|
|
20167
21365
|
};
|
|
20168
21366
|
|
|
20169
21367
|
// src/watcher/git-head-watcher.ts
|
|
20170
|
-
import * as
|
|
21368
|
+
import * as path29 from "path";
|
|
20171
21369
|
var GitHeadWatcher = class {
|
|
20172
21370
|
watcher = null;
|
|
20173
21371
|
projectRoot;
|
|
@@ -20189,13 +21387,13 @@ var GitHeadWatcher = class {
|
|
|
20189
21387
|
this.readyPromise = Promise.resolve();
|
|
20190
21388
|
return;
|
|
20191
21389
|
}
|
|
20192
|
-
this.readyPromise = new Promise((
|
|
20193
|
-
this.resolveReady =
|
|
21390
|
+
this.readyPromise = new Promise((resolve20) => {
|
|
21391
|
+
this.resolveReady = resolve20;
|
|
20194
21392
|
});
|
|
20195
21393
|
this.onBranchChange = handler;
|
|
20196
21394
|
this.currentBranch = getCurrentBranch(this.projectRoot);
|
|
20197
21395
|
const headPath = getHeadPath(this.projectRoot);
|
|
20198
|
-
const refsPath =
|
|
21396
|
+
const refsPath = path29.join(this.projectRoot, ".git", "refs", "heads");
|
|
20199
21397
|
this.watcher = chokidar_default.watch([headPath, refsPath], {
|
|
20200
21398
|
persistent: true,
|
|
20201
21399
|
ignoreInitial: true,
|
|
@@ -20331,7 +21529,7 @@ function getConfigPaths(projectRoot, host, options) {
|
|
|
20331
21529
|
|
|
20332
21530
|
// src/tools/visualize/activity.ts
|
|
20333
21531
|
import { execFileSync } from "child_process";
|
|
20334
|
-
import * as
|
|
21532
|
+
import * as path30 from "path";
|
|
20335
21533
|
function attachRecentActivity(data, projectRoot) {
|
|
20336
21534
|
const activity = readGitActivity(projectRoot);
|
|
20337
21535
|
const changes = activity.size > 0 ? buildGitChanges(data, activity, projectRoot) : buildGraphChanges(data);
|
|
@@ -20493,7 +21691,7 @@ function normalizePath4(filePath) {
|
|
|
20493
21691
|
return filePath.replace(/\\/g, "/");
|
|
20494
21692
|
}
|
|
20495
21693
|
function toGitRelativePath(projectRoot, filePath) {
|
|
20496
|
-
const relativePath =
|
|
21694
|
+
const relativePath = path30.isAbsolute(filePath) ? path30.relative(projectRoot, filePath) : filePath;
|
|
20497
21695
|
return normalizePath4(relativePath);
|
|
20498
21696
|
}
|
|
20499
21697
|
|
|
@@ -20751,7 +21949,7 @@ render();
|
|
|
20751
21949
|
}
|
|
20752
21950
|
|
|
20753
21951
|
// src/tools/visualize/transform.ts
|
|
20754
|
-
import * as
|
|
21952
|
+
import * as path31 from "path";
|
|
20755
21953
|
|
|
20756
21954
|
// src/tools/visualize/modules.ts
|
|
20757
21955
|
var MAX_MODULES = 18;
|
|
@@ -20884,8 +22082,8 @@ function compactModules(prefixToNodes) {
|
|
|
20884
22082
|
function deriveModules(nodes) {
|
|
20885
22083
|
const initial = /* @__PURE__ */ new Map();
|
|
20886
22084
|
for (const node of nodes) {
|
|
20887
|
-
const
|
|
20888
|
-
const prefix = modulePrefixFromRelativePath(
|
|
22085
|
+
const relative14 = stripToProjectRelative(node.filePath);
|
|
22086
|
+
const prefix = modulePrefixFromRelativePath(relative14);
|
|
20889
22087
|
if (!initial.has(prefix)) initial.set(prefix, []);
|
|
20890
22088
|
initial.get(prefix)?.push(node);
|
|
20891
22089
|
}
|
|
@@ -21011,7 +22209,7 @@ function transformForVisualization(symbols, edges, options = {}) {
|
|
|
21011
22209
|
filePath: s.filePath,
|
|
21012
22210
|
kind: s.kind,
|
|
21013
22211
|
line: s.startLine,
|
|
21014
|
-
directory:
|
|
22212
|
+
directory: path31.dirname(s.filePath),
|
|
21015
22213
|
moduleId: "",
|
|
21016
22214
|
moduleLabel: ""
|
|
21017
22215
|
}));
|
|
@@ -21039,9 +22237,9 @@ function parseArgs(argv) {
|
|
|
21039
22237
|
let host = "opencode";
|
|
21040
22238
|
for (let i = 2; i < argv.length; i++) {
|
|
21041
22239
|
if (argv[i] === "--project" && argv[i + 1]) {
|
|
21042
|
-
project =
|
|
22240
|
+
project = path32.resolve(argv[++i]);
|
|
21043
22241
|
} else if (argv[i] === "--config" && argv[i + 1]) {
|
|
21044
|
-
config =
|
|
22242
|
+
config = path32.resolve(argv[++i]);
|
|
21045
22243
|
} else if (argv[i] === "--host" && argv[i + 1]) {
|
|
21046
22244
|
host = parseHostMode(argv[++i]);
|
|
21047
22245
|
} else if (argv[i] === "--host") {
|
|
@@ -21068,7 +22266,7 @@ function parseIndexArgs(argv, cwd) {
|
|
|
21068
22266
|
if (!arg.startsWith("--project=")) {
|
|
21069
22267
|
i += 1;
|
|
21070
22268
|
}
|
|
21071
|
-
project =
|
|
22269
|
+
project = path32.resolve(cwd, value);
|
|
21072
22270
|
continue;
|
|
21073
22271
|
}
|
|
21074
22272
|
if (arg === "--config" || arg.startsWith("--config=")) {
|
|
@@ -21079,7 +22277,7 @@ function parseIndexArgs(argv, cwd) {
|
|
|
21079
22277
|
if (!arg.startsWith("--config=")) {
|
|
21080
22278
|
i += 1;
|
|
21081
22279
|
}
|
|
21082
|
-
config =
|
|
22280
|
+
config = path32.resolve(cwd, value);
|
|
21083
22281
|
continue;
|
|
21084
22282
|
}
|
|
21085
22283
|
if (arg === "--host" || arg.startsWith("--host=")) {
|
|
@@ -21145,7 +22343,7 @@ function parseVisualizeArgs(argv, cwd) {
|
|
|
21145
22343
|
for (let i = 0; i < argv.length; i++) {
|
|
21146
22344
|
const arg = argv[i];
|
|
21147
22345
|
if (arg === "--project" && argv[i + 1]) {
|
|
21148
|
-
project =
|
|
22346
|
+
project = path32.resolve(argv[++i]);
|
|
21149
22347
|
} else if (arg === "--max" && argv[i + 1]) {
|
|
21150
22348
|
maxNodes = Number(argv[++i]);
|
|
21151
22349
|
} else if (arg.startsWith("--max=") || arg.startsWith("max=")) {
|
|
@@ -21182,7 +22380,7 @@ async function handleVisualizeCommand(argv, cwd) {
|
|
|
21182
22380
|
console.error("No connected symbols found. Retry with: npm run visualize -- orphans");
|
|
21183
22381
|
return 1;
|
|
21184
22382
|
}
|
|
21185
|
-
const outputPath =
|
|
22383
|
+
const outputPath = path32.join(os8.tmpdir(), `call-graph-${Date.now()}.html`);
|
|
21186
22384
|
writeFileSync6(outputPath, generateVisualizationHtml(vizData), "utf-8");
|
|
21187
22385
|
console.log(`Temporal call graph visualization generated: ${outputPath}`);
|
|
21188
22386
|
console.log(`Nodes: ${vizData.nodes.length} | Edges: ${vizData.edges.length}`);
|
|
@@ -21214,8 +22412,60 @@ async function runMcpCli(argv) {
|
|
|
21214
22412
|
const config = parseConfig(rawConfig);
|
|
21215
22413
|
const server = createMcpServer(args.project, config, args.host);
|
|
21216
22414
|
const transport = new StdioServerTransport();
|
|
21217
|
-
await server.connect(transport);
|
|
21218
22415
|
let watcher = null;
|
|
22416
|
+
let shutdownPromise;
|
|
22417
|
+
const onServerClose = server.server.onclose;
|
|
22418
|
+
const shutdown = () => {
|
|
22419
|
+
if (shutdownPromise) return shutdownPromise;
|
|
22420
|
+
process.stdin.removeListener("end", requestShutdown);
|
|
22421
|
+
process.stdin.removeListener("close", requestShutdown);
|
|
22422
|
+
process.removeListener("SIGHUP", requestShutdown);
|
|
22423
|
+
process.removeListener("SIGINT", requestShutdown);
|
|
22424
|
+
process.removeListener("SIGTERM", requestShutdown);
|
|
22425
|
+
server.server.onclose = onServerClose;
|
|
22426
|
+
shutdownPromise = (async () => {
|
|
22427
|
+
let exitCode = 0;
|
|
22428
|
+
try {
|
|
22429
|
+
await watcher?.stop();
|
|
22430
|
+
} catch (error) {
|
|
22431
|
+
exitCode = 1;
|
|
22432
|
+
console.error("Failed to stop MCP file watcher cleanly:", error);
|
|
22433
|
+
}
|
|
22434
|
+
try {
|
|
22435
|
+
await stopAutoIndex(args.project, args.host);
|
|
22436
|
+
} catch (error) {
|
|
22437
|
+
exitCode = 1;
|
|
22438
|
+
console.error("Failed to stop automatic indexing cleanly:", error);
|
|
22439
|
+
}
|
|
22440
|
+
try {
|
|
22441
|
+
await server.close();
|
|
22442
|
+
} catch (error) {
|
|
22443
|
+
exitCode = 1;
|
|
22444
|
+
console.error("Failed to close MCP server cleanly:", error);
|
|
22445
|
+
}
|
|
22446
|
+
process.exit(exitCode);
|
|
22447
|
+
})();
|
|
22448
|
+
return shutdownPromise;
|
|
22449
|
+
};
|
|
22450
|
+
const requestShutdown = () => {
|
|
22451
|
+
void shutdown();
|
|
22452
|
+
};
|
|
22453
|
+
server.server.onclose = () => {
|
|
22454
|
+
try {
|
|
22455
|
+
onServerClose?.();
|
|
22456
|
+
} finally {
|
|
22457
|
+
requestShutdown();
|
|
22458
|
+
}
|
|
22459
|
+
};
|
|
22460
|
+
process.stdin.once("end", requestShutdown);
|
|
22461
|
+
process.stdin.once("close", requestShutdown);
|
|
22462
|
+
process.once("SIGINT", requestShutdown);
|
|
22463
|
+
if (process.platform !== "win32") {
|
|
22464
|
+
process.once("SIGHUP", requestShutdown);
|
|
22465
|
+
process.once("SIGTERM", requestShutdown);
|
|
22466
|
+
}
|
|
22467
|
+
await server.connect(transport);
|
|
22468
|
+
if (shutdownPromise) return;
|
|
21219
22469
|
const isHomeDir = isHomeDirectory(args.project);
|
|
21220
22470
|
const isValidProject = !isHomeDir && (!config.indexing.requireProjectMarker || hasProjectMarker(args.project));
|
|
21221
22471
|
if (config.indexing.watchFiles && isValidProject) {
|
|
@@ -21227,26 +22477,6 @@ async function runMcpCli(argv) {
|
|
|
21227
22477
|
args.config ? { configPath: args.config } : {}
|
|
21228
22478
|
);
|
|
21229
22479
|
}
|
|
21230
|
-
let shuttingDown = false;
|
|
21231
|
-
const shutdown = async () => {
|
|
21232
|
-
if (shuttingDown) return;
|
|
21233
|
-
shuttingDown = true;
|
|
21234
|
-
try {
|
|
21235
|
-
await watcher?.stop();
|
|
21236
|
-
await stopAutoIndex(args.project, args.host);
|
|
21237
|
-
await server.close();
|
|
21238
|
-
process.exit(0);
|
|
21239
|
-
} catch (error) {
|
|
21240
|
-
console.error("Failed to stop MCP server cleanly:", error);
|
|
21241
|
-
process.exit(1);
|
|
21242
|
-
}
|
|
21243
|
-
};
|
|
21244
|
-
process.on("SIGINT", () => {
|
|
21245
|
-
void shutdown();
|
|
21246
|
-
});
|
|
21247
|
-
process.on("SIGTERM", () => {
|
|
21248
|
-
void shutdown();
|
|
21249
|
-
});
|
|
21250
22480
|
}
|
|
21251
22481
|
function printIndexProgress(onProgress, title, metadata) {
|
|
21252
22482
|
const details = Object.entries(metadata).filter(([, value]) => value !== void 0 && value !== null).map(([key, value]) => `${key}=${isSensitiveKey(key) ? "[REDACTED]" : String(value)}`).join(" ");
|