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/index.js
CHANGED
|
@@ -328,7 +328,7 @@ var require_ignore = __commonJS({
|
|
|
328
328
|
// path matching.
|
|
329
329
|
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
330
330
|
// @returns {TestResult} true if a file is ignored
|
|
331
|
-
test(
|
|
331
|
+
test(path30, checkUnignored, mode) {
|
|
332
332
|
let ignored = false;
|
|
333
333
|
let unignored = false;
|
|
334
334
|
let matchedRule;
|
|
@@ -337,7 +337,7 @@ var require_ignore = __commonJS({
|
|
|
337
337
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
338
338
|
return;
|
|
339
339
|
}
|
|
340
|
-
const matched = rule[mode].test(
|
|
340
|
+
const matched = rule[mode].test(path30);
|
|
341
341
|
if (!matched) {
|
|
342
342
|
return;
|
|
343
343
|
}
|
|
@@ -358,17 +358,17 @@ var require_ignore = __commonJS({
|
|
|
358
358
|
var throwError = (message, Ctor) => {
|
|
359
359
|
throw new Ctor(message);
|
|
360
360
|
};
|
|
361
|
-
var checkPath = (
|
|
362
|
-
if (!isString(
|
|
361
|
+
var checkPath = (path30, originalPath, doThrow) => {
|
|
362
|
+
if (!isString(path30)) {
|
|
363
363
|
return doThrow(
|
|
364
364
|
`path must be a string, but got \`${originalPath}\``,
|
|
365
365
|
TypeError
|
|
366
366
|
);
|
|
367
367
|
}
|
|
368
|
-
if (!
|
|
368
|
+
if (!path30) {
|
|
369
369
|
return doThrow(`path must not be empty`, TypeError);
|
|
370
370
|
}
|
|
371
|
-
if (checkPath.isNotRelative(
|
|
371
|
+
if (checkPath.isNotRelative(path30)) {
|
|
372
372
|
const r = "`path.relative()`d";
|
|
373
373
|
return doThrow(
|
|
374
374
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -377,7 +377,7 @@ var require_ignore = __commonJS({
|
|
|
377
377
|
}
|
|
378
378
|
return true;
|
|
379
379
|
};
|
|
380
|
-
var isNotRelative = (
|
|
380
|
+
var isNotRelative = (path30) => REGEX_TEST_INVALID_PATH.test(path30);
|
|
381
381
|
checkPath.isNotRelative = isNotRelative;
|
|
382
382
|
checkPath.convert = (p) => p;
|
|
383
383
|
var Ignore2 = class {
|
|
@@ -407,19 +407,19 @@ var require_ignore = __commonJS({
|
|
|
407
407
|
}
|
|
408
408
|
// @returns {TestResult}
|
|
409
409
|
_test(originalPath, cache, checkUnignored, slices) {
|
|
410
|
-
const
|
|
410
|
+
const path30 = originalPath && checkPath.convert(originalPath);
|
|
411
411
|
checkPath(
|
|
412
|
-
|
|
412
|
+
path30,
|
|
413
413
|
originalPath,
|
|
414
414
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
415
415
|
);
|
|
416
|
-
return this._t(
|
|
416
|
+
return this._t(path30, cache, checkUnignored, slices);
|
|
417
417
|
}
|
|
418
|
-
checkIgnore(
|
|
419
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
420
|
-
return this.test(
|
|
418
|
+
checkIgnore(path30) {
|
|
419
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path30)) {
|
|
420
|
+
return this.test(path30);
|
|
421
421
|
}
|
|
422
|
-
const slices =
|
|
422
|
+
const slices = path30.split(SLASH2).filter(Boolean);
|
|
423
423
|
slices.pop();
|
|
424
424
|
if (slices.length) {
|
|
425
425
|
const parent = this._t(
|
|
@@ -432,18 +432,18 @@ var require_ignore = __commonJS({
|
|
|
432
432
|
return parent;
|
|
433
433
|
}
|
|
434
434
|
}
|
|
435
|
-
return this._rules.test(
|
|
435
|
+
return this._rules.test(path30, false, MODE_CHECK_IGNORE);
|
|
436
436
|
}
|
|
437
|
-
_t(
|
|
438
|
-
if (
|
|
439
|
-
return cache[
|
|
437
|
+
_t(path30, cache, checkUnignored, slices) {
|
|
438
|
+
if (path30 in cache) {
|
|
439
|
+
return cache[path30];
|
|
440
440
|
}
|
|
441
441
|
if (!slices) {
|
|
442
|
-
slices =
|
|
442
|
+
slices = path30.split(SLASH2).filter(Boolean);
|
|
443
443
|
}
|
|
444
444
|
slices.pop();
|
|
445
445
|
if (!slices.length) {
|
|
446
|
-
return cache[
|
|
446
|
+
return cache[path30] = this._rules.test(path30, checkUnignored, MODE_IGNORE);
|
|
447
447
|
}
|
|
448
448
|
const parent = this._t(
|
|
449
449
|
slices.join(SLASH2) + SLASH2,
|
|
@@ -451,29 +451,29 @@ var require_ignore = __commonJS({
|
|
|
451
451
|
checkUnignored,
|
|
452
452
|
slices
|
|
453
453
|
);
|
|
454
|
-
return cache[
|
|
454
|
+
return cache[path30] = parent.ignored ? parent : this._rules.test(path30, checkUnignored, MODE_IGNORE);
|
|
455
455
|
}
|
|
456
|
-
ignores(
|
|
457
|
-
return this._test(
|
|
456
|
+
ignores(path30) {
|
|
457
|
+
return this._test(path30, this._ignoreCache, false).ignored;
|
|
458
458
|
}
|
|
459
459
|
createFilter() {
|
|
460
|
-
return (
|
|
460
|
+
return (path30) => !this.ignores(path30);
|
|
461
461
|
}
|
|
462
462
|
filter(paths) {
|
|
463
463
|
return makeArray(paths).filter(this.createFilter());
|
|
464
464
|
}
|
|
465
465
|
// @returns {TestResult}
|
|
466
|
-
test(
|
|
467
|
-
return this._test(
|
|
466
|
+
test(path30) {
|
|
467
|
+
return this._test(path30, this._testCache, true);
|
|
468
468
|
}
|
|
469
469
|
};
|
|
470
470
|
var factory = (options) => new Ignore2(options);
|
|
471
|
-
var isPathValid = (
|
|
471
|
+
var isPathValid = (path30) => checkPath(path30 && checkPath.convert(path30), path30, RETURN_FALSE);
|
|
472
472
|
var setupWindows = () => {
|
|
473
473
|
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
474
474
|
checkPath.convert = makePosix;
|
|
475
475
|
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
476
|
-
checkPath.isNotRelative = (
|
|
476
|
+
checkPath.isNotRelative = (path30) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path30) || isNotRelative(path30);
|
|
477
477
|
};
|
|
478
478
|
if (
|
|
479
479
|
// Detect `process` so that it can run in browsers.
|
|
@@ -651,7 +651,7 @@ var require_eventemitter3 = __commonJS({
|
|
|
651
651
|
});
|
|
652
652
|
|
|
653
653
|
// src/adapters/opencode.ts
|
|
654
|
-
import * as
|
|
654
|
+
import * as path29 from "path";
|
|
655
655
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
656
656
|
|
|
657
657
|
// src/config/constants.ts
|
|
@@ -712,6 +712,17 @@ var EMBEDDING_MODELS = {
|
|
|
712
712
|
maxTokens: 2048,
|
|
713
713
|
costPer1MTokens: 0.15,
|
|
714
714
|
taskAble: true
|
|
715
|
+
},
|
|
716
|
+
"gemini-embedding-2": {
|
|
717
|
+
provider: "google",
|
|
718
|
+
model: "gemini-embedding-2",
|
|
719
|
+
// Keep a conservative, testable default embedding dimension. Gemini Embedding 2 supports
|
|
720
|
+
// flexible dimensions via outputDimensionality.
|
|
721
|
+
dimensions: 1536,
|
|
722
|
+
maxTokens: 8192,
|
|
723
|
+
costPer1MTokens: 0.15,
|
|
724
|
+
taskAble: false,
|
|
725
|
+
promptStyle: "embedding-2"
|
|
715
726
|
}
|
|
716
727
|
},
|
|
717
728
|
"openai": {
|
|
@@ -745,26 +756,15 @@ var EMBEDDING_MODELS = {
|
|
|
745
756
|
maxTokens: 512,
|
|
746
757
|
costPer1MTokens: 0
|
|
747
758
|
}
|
|
748
|
-
},
|
|
749
|
-
"github-copilot": {
|
|
750
|
-
"text-embedding-3-small": {
|
|
751
|
-
provider: "github-copilot",
|
|
752
|
-
model: "text-embedding-3-small",
|
|
753
|
-
dimensions: 1536,
|
|
754
|
-
maxTokens: 8191,
|
|
755
|
-
costPer1MTokens: 0
|
|
756
|
-
}
|
|
757
759
|
}
|
|
758
760
|
};
|
|
759
761
|
var DEFAULT_PROVIDER_MODELS = {
|
|
760
|
-
"github-copilot": "text-embedding-3-small",
|
|
761
762
|
"openai": "text-embedding-3-small",
|
|
762
763
|
"google": "gemini-embedding-001",
|
|
763
764
|
"ollama": "nomic-embed-text"
|
|
764
765
|
};
|
|
765
766
|
var AUTO_DETECT_PROVIDER_ORDER = [
|
|
766
767
|
"ollama",
|
|
767
|
-
"github-copilot",
|
|
768
768
|
"openai",
|
|
769
769
|
"google"
|
|
770
770
|
];
|
|
@@ -790,6 +790,9 @@ function getDefaultIndexingConfig() {
|
|
|
790
790
|
maxDepth: 5,
|
|
791
791
|
maxFilesPerDirectory: 100,
|
|
792
792
|
fallbackToTextOnMaxChunks: true,
|
|
793
|
+
// Must stay in sync with DEFAULT_LINES_PER_CHUNK in native/src/lib.rs (the napi
|
|
794
|
+
// fallback used when a native caller omits the argument).
|
|
795
|
+
linesPerChunk: 30,
|
|
793
796
|
gitBlame: { enabled: false }
|
|
794
797
|
};
|
|
795
798
|
}
|
|
@@ -923,6 +926,7 @@ function parseConfig(raw) {
|
|
|
923
926
|
maxDepth: typeof rawIndexing.maxDepth === "number" ? rawIndexing.maxDepth < -1 ? -1 : rawIndexing.maxDepth : defaultIndexing.maxDepth,
|
|
924
927
|
maxFilesPerDirectory: typeof rawIndexing.maxFilesPerDirectory === "number" ? Math.max(1, rawIndexing.maxFilesPerDirectory) : defaultIndexing.maxFilesPerDirectory,
|
|
925
928
|
fallbackToTextOnMaxChunks: typeof rawIndexing.fallbackToTextOnMaxChunks === "boolean" ? rawIndexing.fallbackToTextOnMaxChunks : defaultIndexing.fallbackToTextOnMaxChunks,
|
|
929
|
+
linesPerChunk: typeof rawIndexing.linesPerChunk === "number" && Number.isFinite(rawIndexing.linesPerChunk) ? Math.min(Math.max(1, Math.floor(rawIndexing.linesPerChunk)), 4294967295) : defaultIndexing.linesPerChunk,
|
|
926
930
|
gitBlame: {
|
|
927
931
|
enabled: rawIndexing.gitBlame && typeof rawIndexing.gitBlame === "object" && typeof rawIndexing.gitBlame.enabled === "boolean" ? rawIndexing.gitBlame.enabled : defaultIndexing.gitBlame.enabled
|
|
928
932
|
}
|
|
@@ -965,6 +969,7 @@ function parseConfig(raw) {
|
|
|
965
969
|
let embeddingModel;
|
|
966
970
|
let customProvider;
|
|
967
971
|
let reranker;
|
|
972
|
+
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.';
|
|
968
973
|
if (embeddingProviderValue === "custom") {
|
|
969
974
|
embeddingProvider = "custom";
|
|
970
975
|
const rawCustom = input.customProvider && typeof input.customProvider === "object" ? input.customProvider : null;
|
|
@@ -1004,6 +1009,8 @@ function parseConfig(raw) {
|
|
|
1004
1009
|
} else if (rawEmbeddingModel) {
|
|
1005
1010
|
embeddingModel = DEFAULT_PROVIDER_MODELS[embeddingProvider];
|
|
1006
1011
|
}
|
|
1012
|
+
} else if (embeddingProviderValue === "github-copilot") {
|
|
1013
|
+
throw new Error(githubCopilotDeprecationMessage);
|
|
1007
1014
|
} else {
|
|
1008
1015
|
embeddingProvider = "auto";
|
|
1009
1016
|
}
|
|
@@ -1034,10 +1041,21 @@ function parseConfig(raw) {
|
|
|
1034
1041
|
timeoutMs: typeof rawReranker.timeoutMs === "number" ? Math.max(1e3, Math.floor(rawReranker.timeoutMs)) : 1e4
|
|
1035
1042
|
};
|
|
1036
1043
|
}
|
|
1044
|
+
const rawEmbedding = input.embedding && typeof input.embedding === "object" ? input.embedding : {};
|
|
1045
|
+
const rawEmbeddingBatch = rawEmbedding.batch && typeof rawEmbedding.batch === "object" ? rawEmbedding.batch : null;
|
|
1046
|
+
const embeddingMaxBatchItems = typeof rawEmbeddingBatch?.maxBatchItems === "number" && Number.isFinite(rawEmbeddingBatch.maxBatchItems) ? Math.max(1, Math.floor(rawEmbeddingBatch.maxBatchItems)) : void 0;
|
|
1047
|
+
const embeddingMaxBatchTokens = typeof rawEmbeddingBatch?.maxBatchTokens === "number" && Number.isFinite(rawEmbeddingBatch.maxBatchTokens) ? Math.max(1, Math.floor(rawEmbeddingBatch.maxBatchTokens)) : void 0;
|
|
1048
|
+
const embedding = embeddingMaxBatchItems !== void 0 || embeddingMaxBatchTokens !== void 0 ? {
|
|
1049
|
+
batch: {
|
|
1050
|
+
...embeddingMaxBatchItems !== void 0 ? { maxBatchItems: embeddingMaxBatchItems } : {},
|
|
1051
|
+
...embeddingMaxBatchTokens !== void 0 ? { maxBatchTokens: embeddingMaxBatchTokens } : {}
|
|
1052
|
+
}
|
|
1053
|
+
} : {};
|
|
1037
1054
|
return {
|
|
1038
1055
|
embeddingProvider,
|
|
1039
1056
|
embeddingModel,
|
|
1040
1057
|
customProvider,
|
|
1058
|
+
embedding,
|
|
1041
1059
|
scope: isValidScope(scopeValue) ? scopeValue : "project",
|
|
1042
1060
|
include: includeValue ?? DEFAULT_INCLUDE,
|
|
1043
1061
|
exclude: excludeValue ?? DEFAULT_EXCLUDE,
|
|
@@ -1129,11 +1147,11 @@ function resolveGitDir(repoRoot) {
|
|
|
1129
1147
|
return null;
|
|
1130
1148
|
}
|
|
1131
1149
|
try {
|
|
1132
|
-
const
|
|
1133
|
-
if (
|
|
1150
|
+
const stat5 = statSync2(gitPath);
|
|
1151
|
+
if (stat5.isDirectory()) {
|
|
1134
1152
|
return gitPath;
|
|
1135
1153
|
}
|
|
1136
|
-
if (
|
|
1154
|
+
if (stat5.isFile()) {
|
|
1137
1155
|
const content = readFileSync2(gitPath, "utf-8").trim();
|
|
1138
1156
|
const match = content.match(/^gitdir:\s*(.+)$/);
|
|
1139
1157
|
if (match) {
|
|
@@ -2202,7 +2220,7 @@ function analyzeQueryIntent(query) {
|
|
|
2202
2220
|
}
|
|
2203
2221
|
function isTestPath(filePath) {
|
|
2204
2222
|
const normalized = normalizePath(filePath);
|
|
2205
|
-
return /(?:^|\/)(?:test|tests|__tests__|spec|specs)(?:\/|$)/u.test(normalized) ||
|
|
2223
|
+
return /(?:^|\/)(?:test|tests|__tests__|spec|specs)(?:\/|$)/u.test(normalized) || /(?:\.(?:test|spec)|_(?:test|spec))\.[^/]+$/u.test(normalized) || /(?:^|\/)(?:test|spec)_[^/]+\.[^/]+$/u.test(normalized);
|
|
2206
2224
|
}
|
|
2207
2225
|
function isFixturePath(filePath) {
|
|
2208
2226
|
const normalized = normalizePath(filePath);
|
|
@@ -2304,6 +2322,10 @@ function scoreCandidate(query, intent, candidate, originalIndex) {
|
|
|
2304
2322
|
let boost = 0;
|
|
2305
2323
|
if (intent.primary === "conceptual") {
|
|
2306
2324
|
boost += Math.min(0.14, overlap * 0.14);
|
|
2325
|
+
if (intent.preferSourcePaths) {
|
|
2326
|
+
boost += implementationPath ? 0.32 : 0;
|
|
2327
|
+
if (testPath || fixturePath || docsPath) boost -= 0.35;
|
|
2328
|
+
}
|
|
2307
2329
|
if (generatedOrVendor) boost -= 0.18;
|
|
2308
2330
|
if (importChunk || weakContainer) boost -= 0.04;
|
|
2309
2331
|
} else if (intent.primary === "test") {
|
|
@@ -2577,8 +2599,8 @@ function formatExactSearchHandoff(results) {
|
|
|
2577
2599
|
}
|
|
2578
2600
|
function formatContextEvidence(result, index) {
|
|
2579
2601
|
const symbol = result.name ? ` ${JSON.stringify(compactEvidenceValue(result.name, 80))}` : "";
|
|
2580
|
-
const
|
|
2581
|
-
return `[${index}] ${result.chunkType}${symbol} in ${
|
|
2602
|
+
const path30 = compactEvidenceValue(result.filePath, 120);
|
|
2603
|
+
return `[${index}] ${result.chunkType}${symbol} in ${path30}:${result.startLine}-${result.endLine} (score ${result.score.toFixed(2)})`;
|
|
2582
2604
|
}
|
|
2583
2605
|
function formatContextPack(heading, selected, candidateCount, duplicateCount, limitOmittedCount, budgetOmittedCount, includeExactSearchHandoff) {
|
|
2584
2606
|
const lines = selected.map((result, index) => formatContextEvidence(result, index + 1));
|
|
@@ -3281,6 +3303,19 @@ function parseOwner(value) {
|
|
|
3281
3303
|
if (typeof candidate.startedAt !== "string" || Number.isNaN(Date.parse(candidate.startedAt))) return null;
|
|
3282
3304
|
if (typeof candidate.operation !== "string" || !VALID_OPERATIONS.has(candidate.operation)) return null;
|
|
3283
3305
|
if (typeof candidate.token !== "string" || !UUID_PATTERN.test(candidate.token)) return null;
|
|
3306
|
+
if (candidate.recoveryProtocolVersion !== void 0 && candidate.recoveryProtocolVersion !== 1) return null;
|
|
3307
|
+
if (candidate.projectRoot !== void 0 && typeof candidate.projectRoot !== "string") return null;
|
|
3308
|
+
if (candidate.scopedRoots !== void 0) {
|
|
3309
|
+
if (!Array.isArray(candidate.scopedRoots) || candidate.scopedRoots.some((root) => typeof root !== "string")) {
|
|
3310
|
+
return null;
|
|
3311
|
+
}
|
|
3312
|
+
}
|
|
3313
|
+
if (candidate.clearRecovery !== void 0) {
|
|
3314
|
+
const recovery = candidate.clearRecovery;
|
|
3315
|
+
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") {
|
|
3316
|
+
return null;
|
|
3317
|
+
}
|
|
3318
|
+
}
|
|
3284
3319
|
return candidate;
|
|
3285
3320
|
}
|
|
3286
3321
|
function parseReclaimOwner(value) {
|
|
@@ -3521,13 +3556,18 @@ function isTransientIndexLockContention(error) {
|
|
|
3521
3556
|
if (!isIndexLockContentionError(error) || !("reason" in error)) return false;
|
|
3522
3557
|
return error.reason === "active" || error.reason === "reclaiming";
|
|
3523
3558
|
}
|
|
3524
|
-
function acquireIndexLock(indexPath, operation) {
|
|
3559
|
+
function acquireIndexLock(indexPath, operation, recoveryScope) {
|
|
3525
3560
|
mkdirSync(indexPath, { recursive: true });
|
|
3526
3561
|
const canonicalIndexPath = realpathSync2.native(indexPath);
|
|
3527
3562
|
const lockPath = path9.join(canonicalIndexPath, "indexing.lock");
|
|
3528
3563
|
cleanupDeadPublicationCandidates(canonicalIndexPath);
|
|
3529
3564
|
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
3530
|
-
const owner = createOwner(operation)
|
|
3565
|
+
const owner = recoveryScope === void 0 ? createOwner(operation) : {
|
|
3566
|
+
...createOwner(operation),
|
|
3567
|
+
recoveryProtocolVersion: 1,
|
|
3568
|
+
projectRoot: recoveryScope.projectRoot,
|
|
3569
|
+
scopedRoots: recoveryScope.scopedRoots
|
|
3570
|
+
};
|
|
3531
3571
|
if (publishJsonDirectory(lockPath, owner)) {
|
|
3532
3572
|
const lease = {
|
|
3533
3573
|
canonicalIndexPath,
|
|
@@ -3592,6 +3632,33 @@ function releaseIndexLock(lease) {
|
|
|
3592
3632
|
}
|
|
3593
3633
|
return true;
|
|
3594
3634
|
}
|
|
3635
|
+
function setIndexLockClearRecoveryState(lease, clearRecovery) {
|
|
3636
|
+
const currentOwner = readDirectoryOwner(lease.lockPath);
|
|
3637
|
+
if (!currentOwner || !sameOwner(currentOwner, lease.owner)) {
|
|
3638
|
+
throw new Error(`Lost ownership of index mutation lease ${lease.owner.token}`);
|
|
3639
|
+
}
|
|
3640
|
+
const nextOwner = { ...currentOwner };
|
|
3641
|
+
if (clearRecovery === null) {
|
|
3642
|
+
delete nextOwner.clearRecovery;
|
|
3643
|
+
} else {
|
|
3644
|
+
nextOwner.clearRecovery = clearRecovery;
|
|
3645
|
+
}
|
|
3646
|
+
const ownerPath = path9.join(lease.lockPath, OWNER_FILE_NAME);
|
|
3647
|
+
const temporaryPath = path9.join(
|
|
3648
|
+
lease.lockPath,
|
|
3649
|
+
`${OWNER_FILE_NAME}.tmp.${lease.owner.pid}.${lease.owner.token}.${randomUUID()}`
|
|
3650
|
+
);
|
|
3651
|
+
try {
|
|
3652
|
+
writeFileSync(temporaryPath, JSON.stringify(nextOwner), {
|
|
3653
|
+
encoding: "utf-8",
|
|
3654
|
+
flag: "wx",
|
|
3655
|
+
mode: 384
|
|
3656
|
+
});
|
|
3657
|
+
retryTransientFilesystemOperation(() => renameSync(temporaryPath, ownerPath));
|
|
3658
|
+
} finally {
|
|
3659
|
+
if (existsSync5(temporaryPath)) rmSync(temporaryPath, { force: true });
|
|
3660
|
+
}
|
|
3661
|
+
}
|
|
3595
3662
|
function createLeaseTemporaryPath(targetPath, owner, kind = "tmp") {
|
|
3596
3663
|
if (kind === "bak") return `${targetPath}.bak.${owner.pid}.${owner.token}`;
|
|
3597
3664
|
temporaryCounter += 1;
|
|
@@ -3741,8 +3808,8 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
|
|
|
3741
3808
|
if (entry.isDirectory()) {
|
|
3742
3809
|
subdirs.push({ fullPath, relativePath });
|
|
3743
3810
|
} else if (entry.isFile()) {
|
|
3744
|
-
const
|
|
3745
|
-
if (
|
|
3811
|
+
const stat5 = await fsPromises.stat(fullPath);
|
|
3812
|
+
if (stat5.size > maxFileSize) {
|
|
3746
3813
|
skipped.push({ path: relativePath, reason: "too_large" });
|
|
3747
3814
|
continue;
|
|
3748
3815
|
}
|
|
@@ -3760,7 +3827,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
|
|
|
3760
3827
|
}
|
|
3761
3828
|
}
|
|
3762
3829
|
if (matched) {
|
|
3763
|
-
filesInDir.push({ path: fullPath, size:
|
|
3830
|
+
filesInDir.push({ path: fullPath, size: stat5.size });
|
|
3764
3831
|
}
|
|
3765
3832
|
}
|
|
3766
3833
|
}
|
|
@@ -3817,8 +3884,8 @@ async function collectFiles(projectRoot, includePatterns, excludePatterns, maxFi
|
|
|
3817
3884
|
}
|
|
3818
3885
|
for (const resolvedKbRoot of normalizedRoots) {
|
|
3819
3886
|
try {
|
|
3820
|
-
const
|
|
3821
|
-
if (!
|
|
3887
|
+
const stat5 = await fsPromises.stat(resolvedKbRoot);
|
|
3888
|
+
if (!stat5.isDirectory()) {
|
|
3822
3889
|
skipped.push({ path: resolvedKbRoot, reason: "excluded" });
|
|
3823
3890
|
continue;
|
|
3824
3891
|
}
|
|
@@ -3852,7 +3919,7 @@ function getErrorMessage(error) {
|
|
|
3852
3919
|
return error instanceof Error ? error.message : String(error);
|
|
3853
3920
|
}
|
|
3854
3921
|
function runCommand(file, args, options) {
|
|
3855
|
-
return new Promise((
|
|
3922
|
+
return new Promise((resolve17, reject) => {
|
|
3856
3923
|
childProcess.execFile(
|
|
3857
3924
|
file,
|
|
3858
3925
|
args,
|
|
@@ -3862,7 +3929,7 @@ function runCommand(file, args, options) {
|
|
|
3862
3929
|
reject(error);
|
|
3863
3930
|
return;
|
|
3864
3931
|
}
|
|
3865
|
-
|
|
3932
|
+
resolve17(stdout);
|
|
3866
3933
|
}
|
|
3867
3934
|
);
|
|
3868
3935
|
});
|
|
@@ -4007,10 +4074,10 @@ function safeFailureMessage(error) {
|
|
|
4007
4074
|
}
|
|
4008
4075
|
function cancellableDelay(delayMs, signal) {
|
|
4009
4076
|
if (signal.aborted) return Promise.reject(new AutoIndexCancelledError());
|
|
4010
|
-
return new Promise((
|
|
4077
|
+
return new Promise((resolve17, reject) => {
|
|
4011
4078
|
const timer = setTimeout(() => {
|
|
4012
4079
|
signal.removeEventListener("abort", onAbort);
|
|
4013
|
-
|
|
4080
|
+
resolve17();
|
|
4014
4081
|
}, delayMs);
|
|
4015
4082
|
timer.unref?.();
|
|
4016
4083
|
const onAbort = () => {
|
|
@@ -4022,15 +4089,15 @@ function cancellableDelay(delayMs, signal) {
|
|
|
4022
4089
|
}
|
|
4023
4090
|
function withTimeout(promise, timeoutMs) {
|
|
4024
4091
|
if (timeoutMs <= 0) return Promise.resolve(void 0);
|
|
4025
|
-
return new Promise((
|
|
4026
|
-
const timer = setTimeout(() =>
|
|
4092
|
+
return new Promise((resolve17) => {
|
|
4093
|
+
const timer = setTimeout(() => resolve17(void 0), timeoutMs);
|
|
4027
4094
|
timer.unref?.();
|
|
4028
4095
|
void promise.then((value) => {
|
|
4029
4096
|
clearTimeout(timer);
|
|
4030
|
-
|
|
4097
|
+
resolve17(value);
|
|
4031
4098
|
}, () => {
|
|
4032
4099
|
clearTimeout(timer);
|
|
4033
|
-
|
|
4100
|
+
resolve17(void 0);
|
|
4034
4101
|
});
|
|
4035
4102
|
});
|
|
4036
4103
|
}
|
|
@@ -4412,17 +4479,17 @@ var AutoIndexCoordinator = class {
|
|
|
4412
4479
|
}
|
|
4413
4480
|
}
|
|
4414
4481
|
waitForBatteryRetry(delayMs) {
|
|
4415
|
-
return new Promise((
|
|
4482
|
+
return new Promise((resolve17) => {
|
|
4416
4483
|
const timer = setTimeout(() => {
|
|
4417
4484
|
if (this.batteryRetryTimer === timer) {
|
|
4418
4485
|
this.batteryRetryTimer = null;
|
|
4419
4486
|
this.resolveBatteryRetry = null;
|
|
4420
4487
|
}
|
|
4421
|
-
|
|
4488
|
+
resolve17();
|
|
4422
4489
|
}, delayMs);
|
|
4423
4490
|
timer.unref?.();
|
|
4424
4491
|
this.batteryRetryTimer = timer;
|
|
4425
|
-
this.resolveBatteryRetry =
|
|
4492
|
+
this.resolveBatteryRetry = resolve17;
|
|
4426
4493
|
});
|
|
4427
4494
|
}
|
|
4428
4495
|
cancelBatteryRetry() {
|
|
@@ -4430,9 +4497,9 @@ var AutoIndexCoordinator = class {
|
|
|
4430
4497
|
clearTimeout(this.batteryRetryTimer);
|
|
4431
4498
|
this.batteryRetryTimer = null;
|
|
4432
4499
|
}
|
|
4433
|
-
const
|
|
4500
|
+
const resolve17 = this.resolveBatteryRetry;
|
|
4434
4501
|
this.resolveBatteryRetry = null;
|
|
4435
|
-
|
|
4502
|
+
resolve17?.();
|
|
4436
4503
|
}
|
|
4437
4504
|
finishBatteryCheck(batteryCheck) {
|
|
4438
4505
|
if (this.batteryCheck !== batteryCheck) return;
|
|
@@ -4637,7 +4704,7 @@ function pTimeout(promise, options) {
|
|
|
4637
4704
|
} = options;
|
|
4638
4705
|
let timer;
|
|
4639
4706
|
let abortHandler;
|
|
4640
|
-
const wrappedPromise = new Promise((
|
|
4707
|
+
const wrappedPromise = new Promise((resolve17, reject) => {
|
|
4641
4708
|
if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
|
|
4642
4709
|
throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
|
|
4643
4710
|
}
|
|
@@ -4651,7 +4718,7 @@ function pTimeout(promise, options) {
|
|
|
4651
4718
|
};
|
|
4652
4719
|
signal.addEventListener("abort", abortHandler, { once: true });
|
|
4653
4720
|
}
|
|
4654
|
-
promise.then(
|
|
4721
|
+
promise.then(resolve17, reject);
|
|
4655
4722
|
if (milliseconds === Number.POSITIVE_INFINITY) {
|
|
4656
4723
|
return;
|
|
4657
4724
|
}
|
|
@@ -4659,7 +4726,7 @@ function pTimeout(promise, options) {
|
|
|
4659
4726
|
timer = customTimers.setTimeout.call(void 0, () => {
|
|
4660
4727
|
if (fallback) {
|
|
4661
4728
|
try {
|
|
4662
|
-
|
|
4729
|
+
resolve17(fallback());
|
|
4663
4730
|
} catch (error) {
|
|
4664
4731
|
reject(error);
|
|
4665
4732
|
}
|
|
@@ -4669,7 +4736,7 @@ function pTimeout(promise, options) {
|
|
|
4669
4736
|
promise.cancel();
|
|
4670
4737
|
}
|
|
4671
4738
|
if (message === false) {
|
|
4672
|
-
|
|
4739
|
+
resolve17();
|
|
4673
4740
|
} else if (message instanceof Error) {
|
|
4674
4741
|
reject(message);
|
|
4675
4742
|
} else {
|
|
@@ -5071,7 +5138,7 @@ var PQueue = class extends import_index.default {
|
|
|
5071
5138
|
// Assign unique ID if not provided
|
|
5072
5139
|
id: options.id ?? (this.#idAssigner++).toString()
|
|
5073
5140
|
};
|
|
5074
|
-
return new Promise((
|
|
5141
|
+
return new Promise((resolve17, reject) => {
|
|
5075
5142
|
const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
|
|
5076
5143
|
let cleanupQueueAbortHandler = () => void 0;
|
|
5077
5144
|
const run = async () => {
|
|
@@ -5111,7 +5178,7 @@ var PQueue = class extends import_index.default {
|
|
|
5111
5178
|
})]);
|
|
5112
5179
|
}
|
|
5113
5180
|
const result = await operation;
|
|
5114
|
-
|
|
5181
|
+
resolve17(result);
|
|
5115
5182
|
this.emit("completed", result);
|
|
5116
5183
|
} catch (error) {
|
|
5117
5184
|
reject(error);
|
|
@@ -5299,13 +5366,13 @@ var PQueue = class extends import_index.default {
|
|
|
5299
5366
|
});
|
|
5300
5367
|
}
|
|
5301
5368
|
async #onEvent(event, filter) {
|
|
5302
|
-
return new Promise((
|
|
5369
|
+
return new Promise((resolve17) => {
|
|
5303
5370
|
const listener = () => {
|
|
5304
5371
|
if (filter && !filter()) {
|
|
5305
5372
|
return;
|
|
5306
5373
|
}
|
|
5307
5374
|
this.off(event, listener);
|
|
5308
|
-
|
|
5375
|
+
resolve17();
|
|
5309
5376
|
};
|
|
5310
5377
|
this.on(event, listener);
|
|
5311
5378
|
});
|
|
@@ -5591,7 +5658,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
|
|
|
5591
5658
|
const finalDelay = Math.min(delayTime, remainingTime);
|
|
5592
5659
|
options.signal?.throwIfAborted();
|
|
5593
5660
|
if (finalDelay > 0) {
|
|
5594
|
-
await new Promise((
|
|
5661
|
+
await new Promise((resolve17, reject) => {
|
|
5595
5662
|
const onAbort = () => {
|
|
5596
5663
|
clearTimeout(timeoutToken);
|
|
5597
5664
|
options.signal?.removeEventListener("abort", onAbort);
|
|
@@ -5599,7 +5666,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
|
|
|
5599
5666
|
};
|
|
5600
5667
|
const timeoutToken = setTimeout(() => {
|
|
5601
5668
|
options.signal?.removeEventListener("abort", onAbort);
|
|
5602
|
-
|
|
5669
|
+
resolve17();
|
|
5603
5670
|
}, finalDelay);
|
|
5604
5671
|
if (options.unref) {
|
|
5605
5672
|
timeoutToken.unref?.();
|
|
@@ -5735,8 +5802,6 @@ async function tryDetectProvider() {
|
|
|
5735
5802
|
}
|
|
5736
5803
|
async function getProviderCredentials(provider) {
|
|
5737
5804
|
switch (provider) {
|
|
5738
|
-
case "github-copilot":
|
|
5739
|
-
return getGitHubCopilotCredentials();
|
|
5740
5805
|
case "openai":
|
|
5741
5806
|
return getOpenAICredentials();
|
|
5742
5807
|
case "google":
|
|
@@ -5747,22 +5812,6 @@ async function getProviderCredentials(provider) {
|
|
|
5747
5812
|
return null;
|
|
5748
5813
|
}
|
|
5749
5814
|
}
|
|
5750
|
-
function getGitHubCopilotCredentials() {
|
|
5751
|
-
const authData = loadOpenCodeAuth();
|
|
5752
|
-
const copilotAuth = authData["github-copilot"] || authData["github-copilot-enterprise"];
|
|
5753
|
-
if (!copilotAuth || copilotAuth.type !== "oauth") {
|
|
5754
|
-
return null;
|
|
5755
|
-
}
|
|
5756
|
-
const auth = copilotAuth;
|
|
5757
|
-
const baseUrl = auth.enterpriseUrl ? `https://copilot-api.${auth.enterpriseUrl.replace(/^https?:\/\//, "").replace(/\/$/, "")}` : "https://models.github.ai";
|
|
5758
|
-
return {
|
|
5759
|
-
provider: "github-copilot",
|
|
5760
|
-
baseUrl,
|
|
5761
|
-
refreshToken: copilotAuth.refresh,
|
|
5762
|
-
accessToken: copilotAuth.access,
|
|
5763
|
-
tokenExpires: copilotAuth.expires
|
|
5764
|
-
};
|
|
5765
|
-
}
|
|
5766
5815
|
function getOpenAICredentials() {
|
|
5767
5816
|
const authData = loadOpenCodeAuth();
|
|
5768
5817
|
const openaiAuth = authData["openai"];
|
|
@@ -5888,8 +5937,6 @@ async function tryDetectOllamaProvider() {
|
|
|
5888
5937
|
}
|
|
5889
5938
|
function getProviderDisplayName(provider) {
|
|
5890
5939
|
switch (provider) {
|
|
5891
|
-
case "github-copilot":
|
|
5892
|
-
return "GitHub Copilot";
|
|
5893
5940
|
case "openai":
|
|
5894
5941
|
return "OpenAI";
|
|
5895
5942
|
case "google":
|
|
@@ -6114,44 +6161,6 @@ var CustomEmbeddingProvider = class extends BaseEmbeddingProvider {
|
|
|
6114
6161
|
}
|
|
6115
6162
|
};
|
|
6116
6163
|
|
|
6117
|
-
// src/embeddings/providers/github-copilot.ts
|
|
6118
|
-
var GitHubCopilotEmbeddingProvider = class extends BaseEmbeddingProvider {
|
|
6119
|
-
constructor(credentials, modelInfo) {
|
|
6120
|
-
super(credentials, modelInfo);
|
|
6121
|
-
}
|
|
6122
|
-
getToken() {
|
|
6123
|
-
if (!this.credentials.refreshToken) {
|
|
6124
|
-
throw new Error("No OAuth token available for GitHub");
|
|
6125
|
-
}
|
|
6126
|
-
return this.credentials.refreshToken;
|
|
6127
|
-
}
|
|
6128
|
-
async embedBatch(texts) {
|
|
6129
|
-
const token = this.getToken();
|
|
6130
|
-
const response = await fetch(`${this.credentials.baseUrl}/inference/embeddings`, {
|
|
6131
|
-
method: "POST",
|
|
6132
|
-
headers: {
|
|
6133
|
-
Authorization: `Bearer ${token}`,
|
|
6134
|
-
"Content-Type": "application/json",
|
|
6135
|
-
Accept: "application/vnd.github+json",
|
|
6136
|
-
"X-GitHub-Api-Version": "2022-11-28"
|
|
6137
|
-
},
|
|
6138
|
-
body: JSON.stringify({
|
|
6139
|
-
model: `openai/${this.modelInfo.model}`,
|
|
6140
|
-
input: texts
|
|
6141
|
-
})
|
|
6142
|
-
});
|
|
6143
|
-
if (!response.ok) {
|
|
6144
|
-
const error = (await response.text()).slice(0, 500);
|
|
6145
|
-
throw new Error(`GitHub Copilot embedding API error: ${response.status} - ${error}`);
|
|
6146
|
-
}
|
|
6147
|
-
const data = await response.json();
|
|
6148
|
-
return {
|
|
6149
|
-
embeddings: data.data.map((d) => d.embedding),
|
|
6150
|
-
totalTokensUsed: data.usage.total_tokens
|
|
6151
|
-
};
|
|
6152
|
-
}
|
|
6153
|
-
};
|
|
6154
|
-
|
|
6155
6164
|
// src/embeddings/providers/google.ts
|
|
6156
6165
|
var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider extends BaseEmbeddingProvider {
|
|
6157
6166
|
static BATCH_SIZE = 20;
|
|
@@ -6159,24 +6168,30 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider extends BaseEmbeddi
|
|
|
6159
6168
|
super(credentials, modelInfo);
|
|
6160
6169
|
}
|
|
6161
6170
|
async embedQuery(query) {
|
|
6162
|
-
const taskType = this.modelInfo.taskAble ? "CODE_RETRIEVAL_QUERY" : void 0;
|
|
6163
|
-
const
|
|
6171
|
+
const taskType = this.modelInfo.model === "gemini-embedding-001" && this.modelInfo.taskAble ? "CODE_RETRIEVAL_QUERY" : void 0;
|
|
6172
|
+
const texts = [
|
|
6173
|
+
this.modelInfo.model === "gemini-embedding-2" ? `task: code retrieval | query: ${query}` : query
|
|
6174
|
+
];
|
|
6175
|
+
const result = await this.embedWithTaskType(texts, taskType);
|
|
6164
6176
|
return {
|
|
6165
6177
|
embedding: result.embeddings[0],
|
|
6166
6178
|
tokensUsed: result.totalTokensUsed
|
|
6167
6179
|
};
|
|
6168
6180
|
}
|
|
6169
6181
|
async embedDocument(document) {
|
|
6170
|
-
const taskType = this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
6171
|
-
const result = await this.embedWithTaskType([
|
|
6182
|
+
const taskType = this.modelInfo.model === "gemini-embedding-001" && this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
6183
|
+
const result = await this.embedWithTaskType([
|
|
6184
|
+
this.modelInfo.model === "gemini-embedding-2" ? `title: none | text: ${document}` : document
|
|
6185
|
+
], taskType);
|
|
6172
6186
|
return {
|
|
6173
6187
|
embedding: result.embeddings[0],
|
|
6174
6188
|
tokensUsed: result.totalTokensUsed
|
|
6175
6189
|
};
|
|
6176
6190
|
}
|
|
6177
6191
|
async embedBatch(texts) {
|
|
6178
|
-
const taskType = this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
6179
|
-
|
|
6192
|
+
const taskType = this.modelInfo.model === "gemini-embedding-001" && this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
6193
|
+
const formattedTexts = this.modelInfo.model === "gemini-embedding-2" ? texts.map((text) => `title: none | text: ${text}`) : texts;
|
|
6194
|
+
return this.embedWithTaskType(formattedTexts, taskType);
|
|
6180
6195
|
}
|
|
6181
6196
|
async embedWithTaskType(texts, taskType) {
|
|
6182
6197
|
const batches = [];
|
|
@@ -6226,6 +6241,10 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider extends BaseEmbeddi
|
|
|
6226
6241
|
var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddingProvider {
|
|
6227
6242
|
static MIN_TRUNCATION_CHARS = 512;
|
|
6228
6243
|
static REQUEST_TIMEOUT_MS = 12e4;
|
|
6244
|
+
// Set when /api/embed returns 404 so subsequent multi-text batches skip the
|
|
6245
|
+
// batched endpoint and go straight to the legacy per-text path (one probe per
|
|
6246
|
+
// old ollama install, not one probe per batch).
|
|
6247
|
+
batchEndpointUnavailable = false;
|
|
6229
6248
|
constructor(credentials, modelInfo) {
|
|
6230
6249
|
super(credentials, modelInfo);
|
|
6231
6250
|
}
|
|
@@ -6243,6 +6262,21 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddi
|
|
|
6243
6262
|
const message = (error instanceof Error ? error.message : String(error)).toLowerCase();
|
|
6244
6263
|
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");
|
|
6245
6264
|
}
|
|
6265
|
+
// True for a 404 from the newer /api/embed endpoint, i.e. an ollama version that
|
|
6266
|
+
// does not provide it. embedBatch uses this to fall back to the legacy per-text
|
|
6267
|
+
// /api/embeddings path so old ollama installs do not regress.
|
|
6268
|
+
isBatchEndpointUnavailableError(error) {
|
|
6269
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6270
|
+
return message.includes("Ollama /api/embed not available");
|
|
6271
|
+
}
|
|
6272
|
+
// True for a malformed /api/embed response (wrong vector count or a bad vector).
|
|
6273
|
+
// embedBatch falls back to the per-text path on this so a bad batch response
|
|
6274
|
+
// re-embeds each text cleanly. A text that then fails per-text is not isolated
|
|
6275
|
+
// here; it is isolated on the recovery run, which re-embeds one text per request.
|
|
6276
|
+
isBatchValidationError(error) {
|
|
6277
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6278
|
+
return message.includes("invalid embedding batch");
|
|
6279
|
+
}
|
|
6246
6280
|
buildTruncationCandidates(text) {
|
|
6247
6281
|
const baseMaxChars = Math.max(1, this.modelInfo.maxTokens * 4);
|
|
6248
6282
|
const candidateLimits = /* @__PURE__ */ new Set();
|
|
@@ -6344,7 +6378,74 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddi
|
|
|
6344
6378
|
tokensUsed: this.estimateTokens(text)
|
|
6345
6379
|
};
|
|
6346
6380
|
}
|
|
6347
|
-
|
|
6381
|
+
// Embeds many texts in one POST /api/embed request (input: string[]). Ollama
|
|
6382
|
+
// encodes each input independently, so the model context length applies per input
|
|
6383
|
+
// (the upstream splitter already bounds each input), not over the batch. This
|
|
6384
|
+
// amortizes N HTTP round-trips into one.
|
|
6385
|
+
async embedMany(texts) {
|
|
6386
|
+
const controller = new AbortController();
|
|
6387
|
+
const timeout = setTimeout(
|
|
6388
|
+
() => controller.abort(),
|
|
6389
|
+
_OllamaEmbeddingProvider.REQUEST_TIMEOUT_MS
|
|
6390
|
+
);
|
|
6391
|
+
let response;
|
|
6392
|
+
try {
|
|
6393
|
+
response = await fetch(`${this.credentials.baseUrl}/api/embed`, {
|
|
6394
|
+
method: "POST",
|
|
6395
|
+
headers: {
|
|
6396
|
+
"Content-Type": "application/json"
|
|
6397
|
+
},
|
|
6398
|
+
body: JSON.stringify({
|
|
6399
|
+
model: this.modelInfo.model,
|
|
6400
|
+
input: texts,
|
|
6401
|
+
truncate: false
|
|
6402
|
+
}),
|
|
6403
|
+
signal: controller.signal
|
|
6404
|
+
});
|
|
6405
|
+
} catch (error) {
|
|
6406
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
6407
|
+
throw new Error(
|
|
6408
|
+
`Ollama embedding request timed out after ${_OllamaEmbeddingProvider.REQUEST_TIMEOUT_MS}ms`
|
|
6409
|
+
);
|
|
6410
|
+
}
|
|
6411
|
+
throw error;
|
|
6412
|
+
} finally {
|
|
6413
|
+
clearTimeout(timeout);
|
|
6414
|
+
}
|
|
6415
|
+
if (!response.ok) {
|
|
6416
|
+
const error = (await response.text()).slice(0, 500);
|
|
6417
|
+
if (response.status === 404) {
|
|
6418
|
+
throw new Error(`Ollama /api/embed not available: ${response.status} - ${error}`);
|
|
6419
|
+
}
|
|
6420
|
+
throw new Error(`Ollama embedding API error: ${response.status} - ${error}`);
|
|
6421
|
+
}
|
|
6422
|
+
let parsed;
|
|
6423
|
+
try {
|
|
6424
|
+
parsed = await response.json();
|
|
6425
|
+
} catch {
|
|
6426
|
+
throw new Error(
|
|
6427
|
+
`Ollama returned an invalid embedding batch; expected ${texts.length} vectors of ${this.modelInfo.dimensions} finite dimensions`
|
|
6428
|
+
);
|
|
6429
|
+
}
|
|
6430
|
+
const data = parsed && typeof parsed === "object" ? parsed : {};
|
|
6431
|
+
if (!Array.isArray(data.embeddings) || data.embeddings.length !== texts.length || data.embeddings.some(
|
|
6432
|
+
(value) => !Array.isArray(value) || value.length !== this.modelInfo.dimensions || value.some((v) => typeof v !== "number" || !Number.isFinite(v))
|
|
6433
|
+
)) {
|
|
6434
|
+
throw new Error(
|
|
6435
|
+
`Ollama returned an invalid embedding batch; expected ${texts.length} vectors of ${this.modelInfo.dimensions} finite dimensions`
|
|
6436
|
+
);
|
|
6437
|
+
}
|
|
6438
|
+
return {
|
|
6439
|
+
embeddings: data.embeddings,
|
|
6440
|
+
totalTokensUsed: texts.reduce((sum, text) => sum + this.estimateTokens(text), 0)
|
|
6441
|
+
};
|
|
6442
|
+
}
|
|
6443
|
+
// Per-text /api/embeddings path shared by the single-text fast path and the
|
|
6444
|
+
// batch fallback. Uses the legacy endpoint one text at a time, so each text gets
|
|
6445
|
+
// its own truncation safety net and a vector validated on its own. A text that
|
|
6446
|
+
// hard-fails per-text throws here and fails the whole request batch; the recovery
|
|
6447
|
+
// run re-embeds one text per request to isolate it.
|
|
6448
|
+
async embedOneByOne(texts) {
|
|
6348
6449
|
const results = [];
|
|
6349
6450
|
for (const text of texts) {
|
|
6350
6451
|
results.push(await this.embedSingleWithFallback(text));
|
|
@@ -6354,6 +6455,26 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddi
|
|
|
6354
6455
|
totalTokensUsed: results.reduce((sum, r) => sum + r.tokensUsed, 0)
|
|
6355
6456
|
};
|
|
6356
6457
|
}
|
|
6458
|
+
async embedBatch(texts) {
|
|
6459
|
+
if (texts.length === 0) {
|
|
6460
|
+
return { embeddings: [], totalTokensUsed: 0 };
|
|
6461
|
+
}
|
|
6462
|
+
if (texts.length === 1 || this.batchEndpointUnavailable) {
|
|
6463
|
+
return this.embedOneByOne(texts);
|
|
6464
|
+
}
|
|
6465
|
+
try {
|
|
6466
|
+
return await this.embedMany(texts);
|
|
6467
|
+
} catch (error) {
|
|
6468
|
+
if (this.isBatchEndpointUnavailableError(error)) {
|
|
6469
|
+
this.batchEndpointUnavailable = true;
|
|
6470
|
+
return this.embedOneByOne(texts);
|
|
6471
|
+
}
|
|
6472
|
+
if (!this.isContextLengthError(error) && !this.isBatchValidationError(error)) {
|
|
6473
|
+
throw error;
|
|
6474
|
+
}
|
|
6475
|
+
return this.embedOneByOne(texts);
|
|
6476
|
+
}
|
|
6477
|
+
}
|
|
6357
6478
|
};
|
|
6358
6479
|
|
|
6359
6480
|
// src/embeddings/providers/openai.ts
|
|
@@ -6388,8 +6509,6 @@ var OpenAIEmbeddingProvider = class extends BaseEmbeddingProvider {
|
|
|
6388
6509
|
// src/embeddings/provider.ts
|
|
6389
6510
|
function createEmbeddingProvider(configuredProviderInfo) {
|
|
6390
6511
|
switch (configuredProviderInfo.provider) {
|
|
6391
|
-
case "github-copilot":
|
|
6392
|
-
return new GitHubCopilotEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
6393
6512
|
case "openai":
|
|
6394
6513
|
return new OpenAIEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
6395
6514
|
case "google":
|
|
@@ -6405,85 +6524,6 @@ function createEmbeddingProvider(configuredProviderInfo) {
|
|
|
6405
6524
|
}
|
|
6406
6525
|
}
|
|
6407
6526
|
|
|
6408
|
-
// src/rerank/index.ts
|
|
6409
|
-
function createReranker(config) {
|
|
6410
|
-
if (!config.enabled) {
|
|
6411
|
-
return new NoOpReranker();
|
|
6412
|
-
}
|
|
6413
|
-
return new SiliconFlowReranker(config);
|
|
6414
|
-
}
|
|
6415
|
-
var NoOpReranker = class {
|
|
6416
|
-
isAvailable() {
|
|
6417
|
-
return false;
|
|
6418
|
-
}
|
|
6419
|
-
async rerank(_query, documents, _topN) {
|
|
6420
|
-
return {
|
|
6421
|
-
results: documents.map((_, index) => ({ index, relevanceScore: 0 }))
|
|
6422
|
-
};
|
|
6423
|
-
}
|
|
6424
|
-
};
|
|
6425
|
-
var SiliconFlowReranker = class {
|
|
6426
|
-
config;
|
|
6427
|
-
constructor(config) {
|
|
6428
|
-
this.config = config;
|
|
6429
|
-
}
|
|
6430
|
-
isAvailable() {
|
|
6431
|
-
return this.config.enabled && !!this.config.baseUrl && !!this.config.model;
|
|
6432
|
-
}
|
|
6433
|
-
async rerank(query, documents, topN) {
|
|
6434
|
-
if (documents.length === 0) {
|
|
6435
|
-
return { results: [] };
|
|
6436
|
-
}
|
|
6437
|
-
const headers = {
|
|
6438
|
-
"Content-Type": "application/json"
|
|
6439
|
-
};
|
|
6440
|
-
if (this.config.apiKey) {
|
|
6441
|
-
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
6442
|
-
}
|
|
6443
|
-
const baseUrl = this.config.baseUrl;
|
|
6444
|
-
if (!baseUrl) {
|
|
6445
|
-
throw new Error("Reranker baseUrl is required. Configure reranker.baseUrl in your codebase-index.json.");
|
|
6446
|
-
}
|
|
6447
|
-
const timeoutMs = this.config.timeoutMs ?? 3e4;
|
|
6448
|
-
const controller = new AbortController();
|
|
6449
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
6450
|
-
try {
|
|
6451
|
-
const response = await fetch(`${baseUrl}/rerank`, {
|
|
6452
|
-
method: "POST",
|
|
6453
|
-
headers,
|
|
6454
|
-
body: JSON.stringify({
|
|
6455
|
-
model: this.config.model,
|
|
6456
|
-
query,
|
|
6457
|
-
documents,
|
|
6458
|
-
top_n: topN ?? this.config.topN ?? 20,
|
|
6459
|
-
return_documents: false
|
|
6460
|
-
}),
|
|
6461
|
-
signal: controller.signal
|
|
6462
|
-
});
|
|
6463
|
-
clearTimeout(timeout);
|
|
6464
|
-
if (!response.ok) {
|
|
6465
|
-
const errorText = await response.text();
|
|
6466
|
-
throw new Error(`Rerank API error: ${response.status} - ${errorText}`);
|
|
6467
|
-
}
|
|
6468
|
-
const data = await response.json();
|
|
6469
|
-
return {
|
|
6470
|
-
results: data.results.map((r) => ({
|
|
6471
|
-
index: r.index,
|
|
6472
|
-
relevanceScore: r.relevance_score,
|
|
6473
|
-
document: r.document?.text
|
|
6474
|
-
})),
|
|
6475
|
-
tokensUsed: data.meta?.tokens?.input_tokens
|
|
6476
|
-
};
|
|
6477
|
-
} catch (error) {
|
|
6478
|
-
clearTimeout(timeout);
|
|
6479
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
6480
|
-
throw new Error(`Rerank API request timed out after ${timeoutMs}ms`);
|
|
6481
|
-
}
|
|
6482
|
-
throw error;
|
|
6483
|
-
}
|
|
6484
|
-
}
|
|
6485
|
-
};
|
|
6486
|
-
|
|
6487
6527
|
// src/utils/cost.ts
|
|
6488
6528
|
function estimateChunksFromFiles(files) {
|
|
6489
6529
|
let totalChunks = 0;
|
|
@@ -7219,12 +7259,12 @@ try {
|
|
|
7219
7259
|
}
|
|
7220
7260
|
|
|
7221
7261
|
// src/native/parsing.ts
|
|
7222
|
-
function parseFileAsText(filePath, content) {
|
|
7223
|
-
const result = native.parseFileAsText(filePath, content);
|
|
7262
|
+
function parseFileAsText(filePath, content, linesPerChunk) {
|
|
7263
|
+
const result = native.parseFileAsText(filePath, content, linesPerChunk);
|
|
7224
7264
|
return result.map(mapChunk);
|
|
7225
7265
|
}
|
|
7226
|
-
function parseFiles(files) {
|
|
7227
|
-
const result = native.parseFiles(files);
|
|
7266
|
+
function parseFiles(files, linesPerChunk) {
|
|
7267
|
+
const result = native.parseFiles(files, linesPerChunk);
|
|
7228
7268
|
return result.map((f) => ({
|
|
7229
7269
|
path: f.path,
|
|
7230
7270
|
chunks: f.chunks.map(mapChunk),
|
|
@@ -7301,13 +7341,13 @@ var VectorStore = class {
|
|
|
7301
7341
|
const metadata = items.map((i) => JSON.stringify(i.metadata));
|
|
7302
7342
|
this.inner.addBatch(ids, vectors, metadata);
|
|
7303
7343
|
}
|
|
7304
|
-
search(queryVector, limit = 10) {
|
|
7344
|
+
search(queryVector, limit = 10, allowedIds) {
|
|
7305
7345
|
if (queryVector.length !== this.dimensions) {
|
|
7306
7346
|
throw new Error(
|
|
7307
7347
|
`Query vector dimension mismatch: expected ${this.dimensions}, got ${queryVector.length}`
|
|
7308
7348
|
);
|
|
7309
7349
|
}
|
|
7310
|
-
const results = this.inner.search(queryVector, limit);
|
|
7350
|
+
const results = allowedIds === void 0 ? this.inner.search(queryVector, limit) : this.inner.searchFiltered(queryVector, limit, allowedIds);
|
|
7311
7351
|
return results.map((r) => ({
|
|
7312
7352
|
id: r.id,
|
|
7313
7353
|
score: r.score,
|
|
@@ -7535,6 +7575,10 @@ var Database = class _Database {
|
|
|
7535
7575
|
this.throwIfClosed();
|
|
7536
7576
|
return this.inner.getBranchChunkIds(branch);
|
|
7537
7577
|
}
|
|
7578
|
+
getChunkIdsByBlameDate(since, until) {
|
|
7579
|
+
this.throwIfClosed();
|
|
7580
|
+
return this.inner.getChunkIdsByBlameDate(since, until);
|
|
7581
|
+
}
|
|
7538
7582
|
getBranchDelta(branch, baseBranch) {
|
|
7539
7583
|
this.throwIfClosed();
|
|
7540
7584
|
return this.inner.getBranchDelta(branch, baseBranch);
|
|
@@ -8043,8 +8087,8 @@ async function isWorktreeRegistered(projectRoot, worktreePath) {
|
|
|
8043
8087
|
return false;
|
|
8044
8088
|
}
|
|
8045
8089
|
function isPathWithinRoot(filePath, rootPath) {
|
|
8046
|
-
const
|
|
8047
|
-
return
|
|
8090
|
+
const relative14 = path15.relative(path15.resolve(rootPath), path15.resolve(filePath));
|
|
8091
|
+
return relative14 === "" || !relative14.startsWith(`..${path15.sep}`) && relative14 !== ".." && !path15.isAbsolute(relative14);
|
|
8048
8092
|
}
|
|
8049
8093
|
async function pruneExactMissingWorktreeRegistration(projectRoot, worktreePath) {
|
|
8050
8094
|
if (await pathExists(worktreePath)) return false;
|
|
@@ -8421,11 +8465,11 @@ function normalizeFiles(rawFiles, projectRoot) {
|
|
|
8421
8465
|
for (const raw of rawFiles) {
|
|
8422
8466
|
if (raw.length === 0) continue;
|
|
8423
8467
|
const absolute = path16.resolve(root, raw);
|
|
8424
|
-
const
|
|
8425
|
-
if (path16.isAbsolute(raw) ||
|
|
8468
|
+
const relative14 = path16.relative(root, absolute);
|
|
8469
|
+
if (path16.isAbsolute(raw) || relative14 === ".." || relative14.startsWith(`..${path16.sep}`) || path16.isAbsolute(relative14)) {
|
|
8426
8470
|
throw new Error(`Changed file escapes the project root: ${JSON.stringify(raw)}`);
|
|
8427
8471
|
}
|
|
8428
|
-
const cleaned =
|
|
8472
|
+
const cleaned = relative14.startsWith(`.${path16.sep}`) ? relative14.slice(2) : relative14;
|
|
8429
8473
|
if (!seen.has(cleaned)) {
|
|
8430
8474
|
seen.add(cleaned);
|
|
8431
8475
|
result.push(cleaned);
|
|
@@ -8657,7 +8701,8 @@ function rankHybridResults(query, semanticResults, keywordResults, options) {
|
|
|
8657
8701
|
return cached;
|
|
8658
8702
|
}
|
|
8659
8703
|
}
|
|
8660
|
-
const
|
|
8704
|
+
const overfetchFactor = prioritizeSourcePaths ? 12 : 4;
|
|
8705
|
+
const overfetchLimit = Math.max(options.limit * overfetchFactor, options.limit);
|
|
8661
8706
|
const fused = options.fusionStrategy === "rrf" ? fuseResultsRrf(semanticResults, keywordResults, options.rrfK, overfetchLimit) : fuseResultsWeighted(semanticResults, keywordResults, options.hybridWeight, overfetchLimit);
|
|
8662
8707
|
const rerankPoolLimit = Math.max(overfetchLimit, options.rerankTopN * 3, options.limit * 6);
|
|
8663
8708
|
const rerankPool = fused.slice(0, rerankPoolLimit);
|
|
@@ -9422,6 +9467,18 @@ function createFailedBatchWriter(targetPath) {
|
|
|
9422
9467
|
temporaryPath
|
|
9423
9468
|
};
|
|
9424
9469
|
}
|
|
9470
|
+
function writeFailedBatchRecords(targetPath, records) {
|
|
9471
|
+
const writer = createFailedBatchWriter(targetPath);
|
|
9472
|
+
try {
|
|
9473
|
+
for (const record of records) {
|
|
9474
|
+
writer.write(record);
|
|
9475
|
+
}
|
|
9476
|
+
writer.commit();
|
|
9477
|
+
} catch (error) {
|
|
9478
|
+
writer.cleanup();
|
|
9479
|
+
throw error;
|
|
9480
|
+
}
|
|
9481
|
+
}
|
|
9425
9482
|
function* readLegacyFailedBatchRecords(filePath, options) {
|
|
9426
9483
|
const rawData = fs2.readFileSync(filePath, "utf-8");
|
|
9427
9484
|
const trimmed = stripLeadingBomAndWhitespace(rawData).trim();
|
|
@@ -9704,14 +9761,18 @@ function getSafeEmbeddingChunkTokenLimit(provider) {
|
|
|
9704
9761
|
const maxChunkTokens = Math.max(256, Math.floor(providerMaxTokens * 0.75));
|
|
9705
9762
|
return Math.min(2e3, maxChunkTokens);
|
|
9706
9763
|
}
|
|
9707
|
-
|
|
9708
|
-
|
|
9709
|
-
|
|
9710
|
-
|
|
9711
|
-
|
|
9712
|
-
};
|
|
9764
|
+
var DEFAULT_OLLAMA_MAX_BATCH_ITEMS = 16;
|
|
9765
|
+
var DEFAULT_OLLAMA_MAX_BATCH_TOKENS = 65536;
|
|
9766
|
+
function getDynamicBatchOptions(provider, embeddingBatch) {
|
|
9767
|
+
if (provider.provider !== "ollama") {
|
|
9768
|
+
return {};
|
|
9713
9769
|
}
|
|
9714
|
-
|
|
9770
|
+
const base = { maxBatchTokens: DEFAULT_OLLAMA_MAX_BATCH_TOKENS, maxBatchItems: DEFAULT_OLLAMA_MAX_BATCH_ITEMS };
|
|
9771
|
+
return {
|
|
9772
|
+
...base,
|
|
9773
|
+
...typeof embeddingBatch?.maxBatchTokens === "number" && Number.isFinite(embeddingBatch.maxBatchTokens) ? { maxBatchTokens: embeddingBatch.maxBatchTokens } : {},
|
|
9774
|
+
...typeof embeddingBatch?.maxBatchItems === "number" && Number.isFinite(embeddingBatch.maxBatchItems) ? { maxBatchItems: embeddingBatch.maxBatchItems } : {}
|
|
9775
|
+
};
|
|
9715
9776
|
}
|
|
9716
9777
|
function isSqliteCorruptionError(error) {
|
|
9717
9778
|
const message = getErrorMessage4(error).toLowerCase();
|
|
@@ -9729,6 +9790,14 @@ function getPendingChunkId(rawChunk) {
|
|
|
9729
9790
|
const id = rawChunk.id;
|
|
9730
9791
|
return typeof id === "string" ? id : null;
|
|
9731
9792
|
}
|
|
9793
|
+
function parseBlameTimestamp(value, endOfDay) {
|
|
9794
|
+
let timestampMs = Date.parse(value);
|
|
9795
|
+
if (Number.isNaN(timestampMs)) return null;
|
|
9796
|
+
if (endOfDay && /^\d{4}-\d{2}-\d{2}$/.test(value.trim())) {
|
|
9797
|
+
timestampMs += 24 * 60 * 60 * 1e3 - 1;
|
|
9798
|
+
}
|
|
9799
|
+
return Math.floor(timestampMs / 1e3);
|
|
9800
|
+
}
|
|
9732
9801
|
function metadataFromBlame(blame) {
|
|
9733
9802
|
if (!blame) {
|
|
9734
9803
|
return {};
|
|
@@ -9875,7 +9944,7 @@ function promoteIdentifierMatches(query, combined, semanticCandidates, keywordCa
|
|
|
9875
9944
|
const remainder = combined.filter((candidate) => !promotedIds.has(candidate.id));
|
|
9876
9945
|
return [...promoted, ...remainder];
|
|
9877
9946
|
}
|
|
9878
|
-
function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbolIds, limit, fallbackCandidates, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source") {
|
|
9947
|
+
function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbolIds, limit, fallbackCandidates, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source", allowNonSourcePaths = false) {
|
|
9879
9948
|
if (!prioritizeSourcePaths) {
|
|
9880
9949
|
return [];
|
|
9881
9950
|
}
|
|
@@ -9895,7 +9964,7 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbol
|
|
|
9895
9964
|
if (!isImplementationChunkType(chunkType)) {
|
|
9896
9965
|
return false;
|
|
9897
9966
|
}
|
|
9898
|
-
if (!isLikelyImplementationPath2(chunk.filePath)) {
|
|
9967
|
+
if (!allowNonSourcePaths && !isLikelyImplementationPath2(chunk.filePath)) {
|
|
9899
9968
|
return false;
|
|
9900
9969
|
}
|
|
9901
9970
|
const nameLower = (chunk.name ?? "").toLowerCase();
|
|
@@ -9959,7 +10028,7 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbol
|
|
|
9959
10028
|
}
|
|
9960
10029
|
foundCoveringChunk = upsertChunkCandidate(chunk, identifier, normalizedIdentifier) || foundCoveringChunk;
|
|
9961
10030
|
}
|
|
9962
|
-
if (foundCoveringChunk || !isLikelyImplementationPath2(symbol.filePath)) {
|
|
10031
|
+
if (foundCoveringChunk || !allowNonSourcePaths && !isLikelyImplementationPath2(symbol.filePath)) {
|
|
9963
10032
|
continue;
|
|
9964
10033
|
}
|
|
9965
10034
|
const symbolName = symbol.name.toLowerCase();
|
|
@@ -10013,7 +10082,7 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbol
|
|
|
10013
10082
|
const ranked = Array.from(symbolCandidates.values()).sort((a, b) => b.score - a.score || a.id.localeCompare(b.id));
|
|
10014
10083
|
if (ranked.length === 0) {
|
|
10015
10084
|
const implementationFallback = fallbackCandidates.filter(
|
|
10016
|
-
(candidate) => isImplementationChunkType(candidate.metadata.chunkType) && isLikelyImplementationPath2(candidate.metadata.filePath)
|
|
10085
|
+
(candidate) => isImplementationChunkType(candidate.metadata.chunkType) && (allowNonSourcePaths || isLikelyImplementationPath2(candidate.metadata.filePath))
|
|
10017
10086
|
);
|
|
10018
10087
|
for (const candidate of implementationFallback) {
|
|
10019
10088
|
const nameLower = (candidate.metadata.name ?? "").toLowerCase();
|
|
@@ -10129,10 +10198,16 @@ function matchesHardSearchFilters(candidate, options, projectRoot) {
|
|
|
10129
10198
|
return false;
|
|
10130
10199
|
}
|
|
10131
10200
|
if (options?.blameSince) {
|
|
10132
|
-
const
|
|
10133
|
-
if (
|
|
10201
|
+
const since = parseBlameTimestamp(options.blameSince, false);
|
|
10202
|
+
if (since === null) return false;
|
|
10203
|
+
const committedAt = candidate.metadata.blameCommittedAt;
|
|
10204
|
+
if (committedAt === void 0 || committedAt < since) return false;
|
|
10205
|
+
}
|
|
10206
|
+
if (options?.blameUntil) {
|
|
10207
|
+
const until = parseBlameTimestamp(options.blameUntil, true);
|
|
10208
|
+
if (until === null) return false;
|
|
10134
10209
|
const committedAt = candidate.metadata.blameCommittedAt;
|
|
10135
|
-
if (committedAt === void 0 || committedAt
|
|
10210
|
+
if (committedAt === void 0 || committedAt > until) return false;
|
|
10136
10211
|
}
|
|
10137
10212
|
return true;
|
|
10138
10213
|
}
|
|
@@ -10168,7 +10243,6 @@ var Indexer = class _Indexer {
|
|
|
10168
10243
|
database = null;
|
|
10169
10244
|
provider = null;
|
|
10170
10245
|
configuredProviderInfo = null;
|
|
10171
|
-
reranker = null;
|
|
10172
10246
|
fileHashCache = /* @__PURE__ */ new Map();
|
|
10173
10247
|
fileHashCachePath = "";
|
|
10174
10248
|
failedBatchesPath = "";
|
|
@@ -10189,9 +10263,10 @@ var Indexer = class _Indexer {
|
|
|
10189
10263
|
writerArtifactFingerprint = null;
|
|
10190
10264
|
readerArtifactRetryAfter = /* @__PURE__ */ new Map();
|
|
10191
10265
|
fileBatchLimits;
|
|
10266
|
+
checkpointIntervalChunks;
|
|
10192
10267
|
constructor(projectRoot, config, host, runtimeOptions = {}) {
|
|
10193
10268
|
this.projectRoot = projectRoot;
|
|
10194
|
-
this.projectIdentityHash =
|
|
10269
|
+
this.projectIdentityHash = this.getProjectIdentityHash(projectRoot);
|
|
10195
10270
|
this.materializedProjectRoot = runtimeOptions.materializedProjectRoot ?? projectRoot;
|
|
10196
10271
|
this.branchNameOverride = runtimeOptions.branchName;
|
|
10197
10272
|
this.catalogIdentityOverride = runtimeOptions.catalogIdentity;
|
|
@@ -10201,6 +10276,7 @@ var Indexer = class _Indexer {
|
|
|
10201
10276
|
this.expectedCommitOverride = runtimeOptions.expectedCommit?.toLowerCase();
|
|
10202
10277
|
this.indexPathOverride = runtimeOptions.indexPath;
|
|
10203
10278
|
this.fileBatchLimits = runtimeOptions.fileBatchLimits;
|
|
10279
|
+
this.checkpointIntervalChunks = runtimeOptions.checkpointIntervalChunks;
|
|
10204
10280
|
this.config = config;
|
|
10205
10281
|
this.host = host;
|
|
10206
10282
|
if (isGitRepo(this.materializedProjectRoot)) {
|
|
@@ -10312,6 +10388,9 @@ var Indexer = class _Indexer {
|
|
|
10312
10388
|
return path19.resolve(targetPath);
|
|
10313
10389
|
}
|
|
10314
10390
|
}
|
|
10391
|
+
getProjectIdentityHash(projectRoot) {
|
|
10392
|
+
return hashContent(this.getCanonicalPath(projectRoot)).slice(0, 16);
|
|
10393
|
+
}
|
|
10315
10394
|
isProjectOwnedIndexPath() {
|
|
10316
10395
|
return isProjectIndexPathOwnedByProject(this.projectRoot, this.indexPath, this.host);
|
|
10317
10396
|
}
|
|
@@ -10328,7 +10407,6 @@ var Indexer = class _Indexer {
|
|
|
10328
10407
|
this.database = null;
|
|
10329
10408
|
this.provider = null;
|
|
10330
10409
|
this.configuredProviderInfo = null;
|
|
10331
|
-
this.reranker = null;
|
|
10332
10410
|
this.indexCompatibility = null;
|
|
10333
10411
|
this.initializationMode = "none";
|
|
10334
10412
|
this.readIssues = [];
|
|
@@ -10349,7 +10427,10 @@ var Indexer = class _Indexer {
|
|
|
10349
10427
|
}
|
|
10350
10428
|
async withIndexMutationLease(operation, callback) {
|
|
10351
10429
|
this.refreshBranchInfo();
|
|
10352
|
-
const lease = acquireIndexLock(this.indexPath, operation
|
|
10430
|
+
const lease = acquireIndexLock(this.indexPath, operation, {
|
|
10431
|
+
projectRoot: this.projectRoot,
|
|
10432
|
+
scopedRoots: this.getScopedRoots()
|
|
10433
|
+
});
|
|
10353
10434
|
this.indexPath = lease.canonicalIndexPath;
|
|
10354
10435
|
this.refreshRuntimeArtifactPaths();
|
|
10355
10436
|
this.activeIndexLease = lease;
|
|
@@ -10404,6 +10485,7 @@ var Indexer = class _Indexer {
|
|
|
10404
10485
|
}
|
|
10405
10486
|
loadFileHashCache() {
|
|
10406
10487
|
if (!existsSync11(this.fileHashCachePath)) {
|
|
10488
|
+
this.fileHashCache = /* @__PURE__ */ new Map();
|
|
10407
10489
|
return;
|
|
10408
10490
|
}
|
|
10409
10491
|
try {
|
|
@@ -10443,10 +10525,10 @@ var Indexer = class _Indexer {
|
|
|
10443
10525
|
invertedIndex.serialize()
|
|
10444
10526
|
);
|
|
10445
10527
|
}
|
|
10446
|
-
getScopedRoots() {
|
|
10447
|
-
const roots = /* @__PURE__ */ new Set([this.getCanonicalPath(
|
|
10528
|
+
getScopedRoots(projectRoot = this.projectRoot) {
|
|
10529
|
+
const roots = /* @__PURE__ */ new Set([this.getCanonicalPath(projectRoot)]);
|
|
10448
10530
|
for (const kbRoot of this.config.knowledgeBases) {
|
|
10449
|
-
roots.add(this.getCanonicalPath(path19.resolve(
|
|
10531
|
+
roots.add(this.getCanonicalPath(path19.resolve(projectRoot, kbRoot)));
|
|
10450
10532
|
}
|
|
10451
10533
|
return Array.from(roots);
|
|
10452
10534
|
}
|
|
@@ -10517,14 +10599,17 @@ var Indexer = class _Indexer {
|
|
|
10517
10599
|
getLegacyBranchCatalogKey() {
|
|
10518
10600
|
return this.currentBranch || "default";
|
|
10519
10601
|
}
|
|
10520
|
-
getLegacyMigrationMetadataKey() {
|
|
10521
|
-
return `index.globalBranchMigration.${
|
|
10602
|
+
getLegacyMigrationMetadataKey(projectIdentityHash = this.projectIdentityHash) {
|
|
10603
|
+
return `index.globalBranchMigration.${projectIdentityHash}`;
|
|
10522
10604
|
}
|
|
10523
|
-
getProjectEmbeddingStrategyMetadataKey() {
|
|
10524
|
-
return `index.embeddingStrategyVersion.${
|
|
10605
|
+
getProjectEmbeddingStrategyMetadataKey(projectIdentityHash = this.projectIdentityHash) {
|
|
10606
|
+
return `index.embeddingStrategyVersion.${projectIdentityHash}`;
|
|
10525
10607
|
}
|
|
10526
|
-
getProjectForceReembedMetadataKey() {
|
|
10527
|
-
return `index.forceReembed.${
|
|
10608
|
+
getProjectForceReembedMetadataKey(projectIdentityHash = this.projectIdentityHash) {
|
|
10609
|
+
return `index.forceReembed.${projectIdentityHash}`;
|
|
10610
|
+
}
|
|
10611
|
+
getProjectMigrationFinalizedMetadataKey(projectIdentityHash = this.projectIdentityHash) {
|
|
10612
|
+
return `index.migrationFinalized.${projectIdentityHash}`;
|
|
10528
10613
|
}
|
|
10529
10614
|
getBranchMigrationMetadataKey(prefix, catalogIdentity = this.getBranchCatalogIdentity()) {
|
|
10530
10615
|
const branchKey = this.getBranchCatalogKeyFor(catalogIdentity);
|
|
@@ -10630,7 +10715,7 @@ var Indexer = class _Indexer {
|
|
|
10630
10715
|
const legacy = this.getLegacyBranchCatalogKey();
|
|
10631
10716
|
return primary === legacy ? [primary] : [primary, legacy];
|
|
10632
10717
|
}
|
|
10633
|
-
getProjectLocalScopedOwnershipIds(roots) {
|
|
10718
|
+
getProjectLocalScopedOwnershipIds(roots, projectRoot = this.projectRoot) {
|
|
10634
10719
|
const chunkIds = /* @__PURE__ */ new Set();
|
|
10635
10720
|
const symbolIds = /* @__PURE__ */ new Set();
|
|
10636
10721
|
if (!this.database) {
|
|
@@ -10638,10 +10723,10 @@ var Indexer = class _Indexer {
|
|
|
10638
10723
|
}
|
|
10639
10724
|
const projectLocalFilePaths = /* @__PURE__ */ new Set([
|
|
10640
10725
|
...Array.from(this.fileHashCache.keys()).filter(
|
|
10641
|
-
(filePath) => this.isFileInCurrentScope(filePath, roots) && this.isFileInProjectRoot(filePath)
|
|
10726
|
+
(filePath) => this.isFileInCurrentScope(filePath, roots) && this.isFileInProjectRoot(filePath, projectRoot)
|
|
10642
10727
|
),
|
|
10643
10728
|
...(this.store?.getAllMetadata() ?? []).map(({ metadata }) => metadata.filePath).filter(
|
|
10644
|
-
(filePath) => this.isFileInCurrentScope(filePath, roots) && this.isFileInProjectRoot(filePath)
|
|
10729
|
+
(filePath) => this.isFileInCurrentScope(filePath, roots) && this.isFileInProjectRoot(filePath, projectRoot)
|
|
10645
10730
|
)
|
|
10646
10731
|
]);
|
|
10647
10732
|
for (const filePath of projectLocalFilePaths) {
|
|
@@ -10654,15 +10739,16 @@ var Indexer = class _Indexer {
|
|
|
10654
10739
|
}
|
|
10655
10740
|
return { chunkIds, symbolIds };
|
|
10656
10741
|
}
|
|
10657
|
-
getProjectScopedBranchCatalogCleanupKeys(projectChunkIds, projectSymbolIds) {
|
|
10742
|
+
getProjectScopedBranchCatalogCleanupKeys(projectChunkIds, projectSymbolIds, projectRoot = this.projectRoot) {
|
|
10658
10743
|
if (this.config.scope !== "global") {
|
|
10659
10744
|
return this.getBranchCatalogCleanupKeys();
|
|
10660
10745
|
}
|
|
10661
10746
|
const keys = /* @__PURE__ */ new Set();
|
|
10662
10747
|
const projectChunkIdSet = new Set(projectChunkIds);
|
|
10663
10748
|
const projectSymbolIdSet = new Set(projectSymbolIds);
|
|
10749
|
+
const projectIdentityHash = this.getProjectIdentityHash(projectRoot);
|
|
10664
10750
|
for (const branchKey of this.database?.getAllBranches() ?? []) {
|
|
10665
|
-
if (branchKey.startsWith(`${
|
|
10751
|
+
if (branchKey.startsWith(`${projectIdentityHash}:`)) {
|
|
10666
10752
|
keys.add(branchKey);
|
|
10667
10753
|
continue;
|
|
10668
10754
|
}
|
|
@@ -10672,8 +10758,10 @@ var Indexer = class _Indexer {
|
|
|
10672
10758
|
keys.add(branchKey);
|
|
10673
10759
|
}
|
|
10674
10760
|
}
|
|
10675
|
-
|
|
10676
|
-
|
|
10761
|
+
if (projectRoot === this.projectRoot) {
|
|
10762
|
+
for (const branchKey of this.getBranchCatalogCleanupKeys()) {
|
|
10763
|
+
keys.add(branchKey);
|
|
10764
|
+
}
|
|
10677
10765
|
}
|
|
10678
10766
|
return Array.from(keys);
|
|
10679
10767
|
}
|
|
@@ -10681,10 +10769,10 @@ var Indexer = class _Indexer {
|
|
|
10681
10769
|
const canonicalFilePath = this.getCanonicalStoredFilePath(filePath);
|
|
10682
10770
|
return roots.some((root) => isPathWithinRoot2(canonicalFilePath, root));
|
|
10683
10771
|
}
|
|
10684
|
-
isFileInProjectRoot(filePath) {
|
|
10772
|
+
isFileInProjectRoot(filePath, projectRoot = this.projectRoot) {
|
|
10685
10773
|
return isPathWithinRoot2(
|
|
10686
10774
|
this.getCanonicalStoredFilePath(filePath),
|
|
10687
|
-
this.getCanonicalPath(
|
|
10775
|
+
this.getCanonicalPath(projectRoot)
|
|
10688
10776
|
);
|
|
10689
10777
|
}
|
|
10690
10778
|
clearScopedFileHashCache(roots) {
|
|
@@ -10726,12 +10814,12 @@ var Indexer = class _Indexer {
|
|
|
10726
10814
|
}
|
|
10727
10815
|
return false;
|
|
10728
10816
|
}
|
|
10729
|
-
hasForeignScopedBranchData() {
|
|
10817
|
+
hasForeignScopedBranchData(projectRoot = this.projectRoot, roots = this.getScopedRoots(projectRoot)) {
|
|
10730
10818
|
if (!this.database || this.config.scope !== "global") {
|
|
10731
10819
|
return false;
|
|
10732
10820
|
}
|
|
10733
|
-
const
|
|
10734
|
-
const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots);
|
|
10821
|
+
const projectIdentityHash = this.getProjectIdentityHash(projectRoot);
|
|
10822
|
+
const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots, projectRoot);
|
|
10735
10823
|
return this.database.getAllBranches().some(
|
|
10736
10824
|
(branchKey) => {
|
|
10737
10825
|
const branchChunkIds = this.database.getBranchChunkIds(branchKey);
|
|
@@ -10740,7 +10828,7 @@ var Indexer = class _Indexer {
|
|
|
10740
10828
|
if (!hasBranchData) {
|
|
10741
10829
|
return false;
|
|
10742
10830
|
}
|
|
10743
|
-
if (branchKey.startsWith(`${
|
|
10831
|
+
if (branchKey.startsWith(`${projectIdentityHash}:`)) {
|
|
10744
10832
|
return false;
|
|
10745
10833
|
}
|
|
10746
10834
|
const referencesCurrentProjectChunks = branchChunkIds.some((chunkId) => projectLocalChunkIds.has(chunkId));
|
|
@@ -10749,7 +10837,7 @@ var Indexer = class _Indexer {
|
|
|
10749
10837
|
}
|
|
10750
10838
|
);
|
|
10751
10839
|
}
|
|
10752
|
-
clearSharedIndexProjectData(store, invertedIndex, database, roots) {
|
|
10840
|
+
clearSharedIndexProjectData(store, invertedIndex, database, roots, projectRoot = this.projectRoot) {
|
|
10753
10841
|
const allMetadata = store.getAllMetadata();
|
|
10754
10842
|
const scopedEntries = allMetadata.filter(({ metadata }) => this.isFileInCurrentScope(metadata.filePath, roots));
|
|
10755
10843
|
const filePaths = /* @__PURE__ */ new Set([
|
|
@@ -10757,7 +10845,7 @@ var Indexer = class _Indexer {
|
|
|
10757
10845
|
...scopedEntries.map(({ metadata }) => metadata.filePath)
|
|
10758
10846
|
]);
|
|
10759
10847
|
const projectLocalFilePaths = new Set(
|
|
10760
|
-
Array.from(filePaths).filter((filePath) => this.isFileInProjectRoot(filePath))
|
|
10848
|
+
Array.from(filePaths).filter((filePath) => this.isFileInProjectRoot(filePath, projectRoot))
|
|
10761
10849
|
);
|
|
10762
10850
|
const removedChunkIds = new Set(scopedEntries.map(({ key }) => key));
|
|
10763
10851
|
for (const filePath of filePaths) {
|
|
@@ -10767,7 +10855,7 @@ var Indexer = class _Indexer {
|
|
|
10767
10855
|
}
|
|
10768
10856
|
const removedChunkIdList = Array.from(removedChunkIds);
|
|
10769
10857
|
const projectLocalChunkIds = new Set(
|
|
10770
|
-
scopedEntries.filter(({ metadata }) => this.isFileInProjectRoot(metadata.filePath)).map(({ key }) => key)
|
|
10858
|
+
scopedEntries.filter(({ metadata }) => this.isFileInProjectRoot(metadata.filePath, projectRoot)).map(({ key }) => key)
|
|
10771
10859
|
);
|
|
10772
10860
|
for (const filePath of projectLocalFilePaths) {
|
|
10773
10861
|
for (const chunk of database.getChunksByFile(filePath)) {
|
|
@@ -10786,7 +10874,8 @@ var Indexer = class _Indexer {
|
|
|
10786
10874
|
}
|
|
10787
10875
|
const branchCleanupKeys = this.getProjectScopedBranchCatalogCleanupKeys(
|
|
10788
10876
|
Array.from(projectLocalChunkIds),
|
|
10789
|
-
Array.from(projectLocalSymbolIds)
|
|
10877
|
+
Array.from(projectLocalSymbolIds),
|
|
10878
|
+
projectRoot
|
|
10790
10879
|
);
|
|
10791
10880
|
for (const branchKey of branchCleanupKeys) {
|
|
10792
10881
|
database.deleteBranchChunksForBranch(branchKey, removedChunkIdList);
|
|
@@ -10821,29 +10910,96 @@ var Indexer = class _Indexer {
|
|
|
10821
10910
|
database.gcOrphanSymbols();
|
|
10822
10911
|
database.gcOrphanEmbeddings();
|
|
10823
10912
|
database.gcOrphanChunks();
|
|
10824
|
-
store.save();
|
|
10825
10913
|
this.saveInvertedIndex(invertedIndex);
|
|
10914
|
+
store.save();
|
|
10826
10915
|
return {
|
|
10827
10916
|
removedChunkIds: removedChunkIdList,
|
|
10828
10917
|
hasForeignData: allMetadata.some(({ metadata }) => !this.isFileInCurrentScope(metadata.filePath, roots))
|
|
10829
10918
|
};
|
|
10830
10919
|
}
|
|
10920
|
+
getCurrentClearRecoveryState() {
|
|
10921
|
+
if (!this.configuredProviderInfo) {
|
|
10922
|
+
throw new Error("Cannot persist clear recovery state before the embedding provider is initialized");
|
|
10923
|
+
}
|
|
10924
|
+
const compatibility = this.checkCompatibility();
|
|
10925
|
+
const compatibilityDecision = compatibility.compatible ? "compatible" : compatibility.code === "EMBEDDING_STRATEGY_MISMATCH" /* EMBEDDING_STRATEGY_MISMATCH */ ? "embedding-strategy-mismatch" : "incompatible";
|
|
10926
|
+
return {
|
|
10927
|
+
phase: "clearing",
|
|
10928
|
+
embeddingProvider: this.configuredProviderInfo.provider,
|
|
10929
|
+
embeddingModel: this.configuredProviderInfo.modelInfo.model,
|
|
10930
|
+
embeddingDimensions: this.configuredProviderInfo.modelInfo.dimensions,
|
|
10931
|
+
embeddingStrategyVersion: EMBEDDING_STRATEGY_VERSION,
|
|
10932
|
+
compatibilityDecision
|
|
10933
|
+
};
|
|
10934
|
+
}
|
|
10935
|
+
beginClearRecoveryState() {
|
|
10936
|
+
const recovery = this.getCurrentClearRecoveryState();
|
|
10937
|
+
setIndexLockClearRecoveryState(this.requireActiveLease(), recovery);
|
|
10938
|
+
return recovery;
|
|
10939
|
+
}
|
|
10940
|
+
finishClearRecoveryState() {
|
|
10941
|
+
setIndexLockClearRecoveryState(this.requireActiveLease(), null);
|
|
10942
|
+
}
|
|
10943
|
+
matchesCurrentClearRecoveryConfiguration(recovery) {
|
|
10944
|
+
const configuredProviderInfo = this.configuredProviderInfo;
|
|
10945
|
+
return configuredProviderInfo !== null && recovery.embeddingProvider === configuredProviderInfo.provider && recovery.embeddingModel === configuredProviderInfo.modelInfo.model && recovery.embeddingDimensions === configuredProviderInfo.modelInfo.dimensions && recovery.embeddingStrategyVersion === EMBEDDING_STRATEGY_VERSION;
|
|
10946
|
+
}
|
|
10947
|
+
hasUnknownLegacyForceIndexClear(owner) {
|
|
10948
|
+
return owner.operation === "force-index" && owner.clearRecovery === void 0 && owner.recoveryProtocolVersion !== 1 && existsSync11(path19.join(this.indexPath, "force-index-phase"));
|
|
10949
|
+
}
|
|
10831
10950
|
async recoverFromInterruptedIndexingUnlocked(owners) {
|
|
10832
10951
|
for (const owner of owners) {
|
|
10833
10952
|
this.logger.warn("Detected interrupted indexing session, recovering...", {
|
|
10834
10953
|
pid: owner.pid,
|
|
10835
10954
|
hostname: owner.hostname,
|
|
10836
10955
|
operation: owner.operation,
|
|
10837
|
-
startedAt: owner.startedAt
|
|
10956
|
+
startedAt: owner.startedAt,
|
|
10957
|
+
projectRoot: owner.projectRoot
|
|
10838
10958
|
});
|
|
10839
10959
|
}
|
|
10840
10960
|
if (this.config.scope === "global") {
|
|
10841
|
-
|
|
10842
|
-
|
|
10961
|
+
const clearScopes = [];
|
|
10962
|
+
for (const owner of owners) {
|
|
10963
|
+
if (this.hasUnknownLegacyForceIndexClear(owner)) {
|
|
10964
|
+
throw new Error(
|
|
10965
|
+
`Cannot automatically recover interrupted force-index ${owner.token}: the legacy clearing phase ownership is unknown. The recovery marker was retained for manual inspection.`
|
|
10966
|
+
);
|
|
10967
|
+
}
|
|
10968
|
+
if (owner.operation === "clear" && owner.clearRecovery === void 0 && owner.recoveryProtocolVersion !== 1) {
|
|
10969
|
+
throw new Error(
|
|
10970
|
+
`Cannot automatically recover interrupted global clear ${owner.token}: the originating recovery state is unknown. The recovery marker was retained for manual inspection.`
|
|
10971
|
+
);
|
|
10972
|
+
}
|
|
10973
|
+
if (owner.clearRecovery === void 0) continue;
|
|
10974
|
+
if (!owner.projectRoot || !owner.scopedRoots || owner.scopedRoots.length === 0) {
|
|
10975
|
+
throw new Error(
|
|
10976
|
+
`Cannot automatically recover interrupted global clear ${owner.token}: the originating project scope is unknown. The recovery marker was retained for manual inspection.`
|
|
10977
|
+
);
|
|
10978
|
+
}
|
|
10979
|
+
if (!this.matchesCurrentClearRecoveryConfiguration(owner.clearRecovery)) {
|
|
10980
|
+
throw new Error(
|
|
10981
|
+
`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.`
|
|
10982
|
+
);
|
|
10983
|
+
}
|
|
10984
|
+
clearScopes.push({
|
|
10985
|
+
projectRoot: owner.projectRoot,
|
|
10986
|
+
scopedRoots: owner.scopedRoots,
|
|
10987
|
+
compatibilityDecision: owner.clearRecovery.compatibilityDecision
|
|
10988
|
+
});
|
|
10989
|
+
}
|
|
10990
|
+
if (clearScopes.length > 0) {
|
|
10991
|
+
this.loadFileHashCache();
|
|
10992
|
+
}
|
|
10993
|
+
for (const { projectRoot, scopedRoots, compatibilityDecision } of clearScopes) {
|
|
10994
|
+
this.clearGlobalIndexUnlocked(projectRoot, scopedRoots, compatibilityDecision);
|
|
10843
10995
|
}
|
|
10844
10996
|
await this.healthCheckUnlocked();
|
|
10997
|
+
this.logger.info(
|
|
10998
|
+
clearScopes.length > 0 ? "Recovery complete, next index will rebuild all files" : "Recovery complete, next index will resume from the last checkpoint"
|
|
10999
|
+
);
|
|
11000
|
+
return;
|
|
10845
11001
|
}
|
|
10846
|
-
this.logger.info("Recovery complete, next index will
|
|
11002
|
+
this.logger.info("Recovery complete, next index will resume from the last checkpoint");
|
|
10847
11003
|
}
|
|
10848
11004
|
*loadSerializedFailedBatches() {
|
|
10849
11005
|
let warned = false;
|
|
@@ -10881,40 +11037,126 @@ var Indexer = class _Indexer {
|
|
|
10881
11037
|
state.writer.write(record);
|
|
10882
11038
|
state.recordsWritten += record.chunks.length;
|
|
10883
11039
|
}
|
|
10884
|
-
finalizeFailedBatchWriteState(state) {
|
|
11040
|
+
finalizeFailedBatchWriteState(state, resolvedChunkIds = /* @__PURE__ */ new Set()) {
|
|
10885
11041
|
if (state.recordsWritten > 0) {
|
|
10886
|
-
|
|
10887
|
-
|
|
10888
|
-
|
|
10889
|
-
|
|
10890
|
-
|
|
10891
|
-
|
|
10892
|
-
|
|
10893
|
-
|
|
10894
|
-
|
|
10895
|
-
|
|
10896
|
-
|
|
10897
|
-
|
|
10898
|
-
|
|
10899
|
-
|
|
10900
|
-
|
|
10901
|
-
const state = this.createFailedBatchWriteState();
|
|
10902
|
-
try {
|
|
10903
|
-
for (const batch of this.loadSerializedFailedBatches()) {
|
|
10904
|
-
const retainedChunks = batch.chunks.filter(shouldRetain);
|
|
10905
|
-
if (retainedChunks.length > 0) {
|
|
10906
|
-
this.writeFailedBatchRecord(state, { ...batch, chunks: retainedChunks });
|
|
11042
|
+
const seenChunkIds = /* @__PURE__ */ new Set();
|
|
11043
|
+
const retained = [];
|
|
11044
|
+
const records = Array.from(readFailedBatchRecords(state.writer.temporaryPath));
|
|
11045
|
+
for (let i = records.length - 1; i >= 0; i--) {
|
|
11046
|
+
const chunks = records[i].chunks.filter((rawChunk) => {
|
|
11047
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
11048
|
+
if (chunkId !== null) {
|
|
11049
|
+
if (resolvedChunkIds.has(chunkId)) return false;
|
|
11050
|
+
if (seenChunkIds.has(chunkId)) return false;
|
|
11051
|
+
seenChunkIds.add(chunkId);
|
|
11052
|
+
}
|
|
11053
|
+
return true;
|
|
11054
|
+
});
|
|
11055
|
+
if (chunks.length > 0) {
|
|
11056
|
+
retained.unshift({ ...records[i], chunks });
|
|
10907
11057
|
}
|
|
10908
11058
|
}
|
|
10909
|
-
this.finalizeFailedBatchWriteState(state);
|
|
10910
|
-
} catch (error) {
|
|
10911
11059
|
state.writer.cleanup();
|
|
10912
|
-
|
|
11060
|
+
if (retained.length > 0) {
|
|
11061
|
+
writeFailedBatchRecords(this.failedBatchesPath, retained);
|
|
11062
|
+
} else {
|
|
11063
|
+
writeFailedBatchRecords(this.failedBatchesPath, []);
|
|
11064
|
+
this.clearFailedBatchState();
|
|
11065
|
+
}
|
|
11066
|
+
return;
|
|
11067
|
+
}
|
|
11068
|
+
state.writer.commit();
|
|
11069
|
+
this.clearFailedBatchState();
|
|
11070
|
+
}
|
|
11071
|
+
getCheckpointIntervalChunks(totalChunks) {
|
|
11072
|
+
return Math.max(
|
|
11073
|
+
this.checkpointIntervalChunks ?? 2e3,
|
|
11074
|
+
Math.floor(totalChunks / 10)
|
|
11075
|
+
);
|
|
11076
|
+
}
|
|
11077
|
+
checkpointIndexRun(database, store, invertedIndex, failedProcessing, resolvedRetryChunkIds, currentFileHashes, committedFilePaths, scopedRoots, configuredProviderInfo) {
|
|
11078
|
+
if (!this.hasProjectForceReembedPending()) {
|
|
11079
|
+
this.saveIndexMetadata(configuredProviderInfo);
|
|
11080
|
+
this.indexCompatibility = { compatible: true };
|
|
11081
|
+
}
|
|
11082
|
+
database.commitWriteTransaction();
|
|
11083
|
+
database.beginWriteTransaction();
|
|
11084
|
+
this.saveInvertedIndex(invertedIndex);
|
|
11085
|
+
store.save();
|
|
11086
|
+
if (failedProcessing.state.recordsWritten > 0 || failedProcessing.latestById.size > 0 || failedProcessing.discardedExistingRecords) {
|
|
11087
|
+
for (const metadata of failedProcessing.latestById.values()) {
|
|
11088
|
+
const alreadyMaterialized = metadata.chunks.some((rawChunk) => {
|
|
11089
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
11090
|
+
return chunkId !== null && failedProcessing.materializedRetryIds.has(chunkId);
|
|
11091
|
+
});
|
|
11092
|
+
if (alreadyMaterialized) continue;
|
|
11093
|
+
this.writeFailedBatchRecord(failedProcessing.state, {
|
|
11094
|
+
chunks: metadata.chunks,
|
|
11095
|
+
attemptCount: metadata.attemptCount,
|
|
11096
|
+
error: metadata.error,
|
|
11097
|
+
lastAttempt: metadata.lastAttempt
|
|
11098
|
+
});
|
|
11099
|
+
for (const rawChunk of metadata.chunks) {
|
|
11100
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
11101
|
+
if (chunkId !== null) {
|
|
11102
|
+
failedProcessing.materializedRetryIds.add(chunkId);
|
|
11103
|
+
}
|
|
11104
|
+
}
|
|
11105
|
+
}
|
|
11106
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state, resolvedRetryChunkIds);
|
|
11107
|
+
failedProcessing.state = this.createFailedBatchWriteState();
|
|
11108
|
+
failedProcessing.discardedExistingRecords = false;
|
|
11109
|
+
for (const record of this.loadSerializedFailedBatches()) {
|
|
11110
|
+
for (const rawChunk of record.chunks) {
|
|
11111
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
11112
|
+
this.writeFailedBatchRecord(failedProcessing.state, { ...record, chunks: [rawChunk] });
|
|
11113
|
+
if (chunkId !== null) {
|
|
11114
|
+
failedProcessing.materializedRetryIds.add(chunkId);
|
|
11115
|
+
}
|
|
11116
|
+
}
|
|
11117
|
+
}
|
|
11118
|
+
}
|
|
11119
|
+
const partialHashes = /* @__PURE__ */ new Map();
|
|
11120
|
+
for (const filePath of committedFilePaths) {
|
|
11121
|
+
const hash = currentFileHashes.get(filePath);
|
|
11122
|
+
if (hash !== void 0) {
|
|
11123
|
+
partialHashes.set(filePath, hash);
|
|
11124
|
+
}
|
|
11125
|
+
}
|
|
11126
|
+
if (scopedRoots) {
|
|
11127
|
+
this.replaceScopedFileHashCache(partialHashes, scopedRoots);
|
|
11128
|
+
} else {
|
|
11129
|
+
this.fileHashCache = partialHashes;
|
|
11130
|
+
this.saveFileHashCache();
|
|
11131
|
+
}
|
|
11132
|
+
}
|
|
11133
|
+
clearFailedBatchState() {
|
|
11134
|
+
if (existsSync11(this.failedBatchesPath)) {
|
|
11135
|
+
try {
|
|
11136
|
+
unlinkSync2(this.failedBatchesPath);
|
|
11137
|
+
} catch {
|
|
11138
|
+
}
|
|
11139
|
+
}
|
|
11140
|
+
}
|
|
11141
|
+
rewriteFailedBatchState(shouldRetain) {
|
|
11142
|
+
const state = this.createFailedBatchWriteState();
|
|
11143
|
+
try {
|
|
11144
|
+
for (const batch of this.loadSerializedFailedBatches()) {
|
|
11145
|
+
const retainedChunks = batch.chunks.filter(shouldRetain);
|
|
11146
|
+
if (retainedChunks.length > 0) {
|
|
11147
|
+
this.writeFailedBatchRecord(state, { ...batch, chunks: retainedChunks });
|
|
11148
|
+
}
|
|
11149
|
+
}
|
|
11150
|
+
this.finalizeFailedBatchWriteState(state);
|
|
11151
|
+
} catch (error) {
|
|
11152
|
+
state.writer.cleanup();
|
|
11153
|
+
throw error;
|
|
10913
11154
|
}
|
|
10914
11155
|
}
|
|
10915
11156
|
prepareFailedBatchProcessing(roots, shouldProcess) {
|
|
10916
11157
|
const state = this.createFailedBatchWriteState();
|
|
10917
11158
|
const latestById = /* @__PURE__ */ new Map();
|
|
11159
|
+
let discardedExistingRecords = false;
|
|
10918
11160
|
try {
|
|
10919
11161
|
for (const batch of this.loadSerializedFailedBatches()) {
|
|
10920
11162
|
for (const rawChunk of batch.chunks) {
|
|
@@ -10925,10 +11167,12 @@ var Indexer = class _Indexer {
|
|
|
10925
11167
|
continue;
|
|
10926
11168
|
}
|
|
10927
11169
|
if (!shouldProcess(filePath)) {
|
|
11170
|
+
discardedExistingRecords = true;
|
|
10928
11171
|
continue;
|
|
10929
11172
|
}
|
|
10930
11173
|
const chunkId = getPendingChunkId(rawChunk);
|
|
10931
11174
|
if (!chunkId) {
|
|
11175
|
+
discardedExistingRecords = true;
|
|
10932
11176
|
continue;
|
|
10933
11177
|
}
|
|
10934
11178
|
const existing = latestById.get(chunkId);
|
|
@@ -10936,12 +11180,18 @@ var Indexer = class _Indexer {
|
|
|
10936
11180
|
latestById.set(chunkId, {
|
|
10937
11181
|
attemptCount: batch.attemptCount,
|
|
10938
11182
|
error: batch.error,
|
|
10939
|
-
lastAttempt: batch.lastAttempt
|
|
11183
|
+
lastAttempt: batch.lastAttempt,
|
|
11184
|
+
chunks: [rawChunk]
|
|
10940
11185
|
});
|
|
10941
11186
|
}
|
|
10942
11187
|
}
|
|
10943
11188
|
}
|
|
10944
|
-
return {
|
|
11189
|
+
return {
|
|
11190
|
+
state,
|
|
11191
|
+
latestById,
|
|
11192
|
+
materializedRetryIds: /* @__PURE__ */ new Set(),
|
|
11193
|
+
discardedExistingRecords
|
|
11194
|
+
};
|
|
10945
11195
|
} catch (error) {
|
|
10946
11196
|
state.writer.cleanup();
|
|
10947
11197
|
throw error;
|
|
@@ -10977,10 +11227,34 @@ var Indexer = class _Indexer {
|
|
|
10977
11227
|
}
|
|
10978
11228
|
}
|
|
10979
11229
|
}
|
|
11230
|
+
restoreMissingChunkRows(database, chunks) {
|
|
11231
|
+
const missing = [];
|
|
11232
|
+
for (const chunk of chunks) {
|
|
11233
|
+
if (database.getChunk(chunk.id)) {
|
|
11234
|
+
continue;
|
|
11235
|
+
}
|
|
11236
|
+
missing.push({
|
|
11237
|
+
chunkId: chunk.id,
|
|
11238
|
+
contentHash: chunk.contentHash,
|
|
11239
|
+
filePath: chunk.metadata.filePath,
|
|
11240
|
+
startLine: chunk.metadata.startLine,
|
|
11241
|
+
endLine: chunk.metadata.endLine,
|
|
11242
|
+
nodeType: chunk.metadata.chunkType,
|
|
11243
|
+
name: chunk.metadata.name,
|
|
11244
|
+
language: chunk.metadata.language,
|
|
11245
|
+
blameSha: chunk.metadata.blameSha,
|
|
11246
|
+
blameAuthor: chunk.metadata.blameAuthor,
|
|
11247
|
+
blameAuthorEmail: chunk.metadata.blameAuthorEmail,
|
|
11248
|
+
blameCommittedAt: chunk.metadata.blameCommittedAt,
|
|
11249
|
+
blameSummary: chunk.metadata.blameSummary
|
|
11250
|
+
});
|
|
11251
|
+
}
|
|
11252
|
+
if (missing.length > 0) {
|
|
11253
|
+
database.upsertChunksBatch(missing);
|
|
11254
|
+
}
|
|
11255
|
+
}
|
|
10980
11256
|
getProviderRateLimits(provider) {
|
|
10981
11257
|
switch (provider) {
|
|
10982
|
-
case "github-copilot":
|
|
10983
|
-
return { concurrency: 1, intervalMs: 4e3, minRetryMs: 5e3, maxRetryMs: 6e4 };
|
|
10984
11258
|
case "openai":
|
|
10985
11259
|
return { concurrency: 3, intervalMs: 500, minRetryMs: 1e3, maxRetryMs: 3e4 };
|
|
10986
11260
|
case "google":
|
|
@@ -11049,16 +11323,17 @@ var Indexer = class _Indexer {
|
|
|
11049
11323
|
const embeddingPartsByChunk = /* @__PURE__ */ new Map();
|
|
11050
11324
|
const completedVectorsByChunkId = /* @__PURE__ */ new Map();
|
|
11051
11325
|
const completedChunkIds = /* @__PURE__ */ new Set();
|
|
11052
|
-
const
|
|
11053
|
-
|
|
11054
|
-
|
|
11055
|
-
|
|
11326
|
+
const batchOptions = getDynamicBatchOptions(options.configuredProviderInfo, this.config.embedding?.batch);
|
|
11327
|
+
if (options.forceSingleItemBatches && options.configuredProviderInfo.provider === "ollama") {
|
|
11328
|
+
batchOptions.maxBatchItems = 1;
|
|
11329
|
+
}
|
|
11330
|
+
const requestBatches = createPendingEmbeddingRequestBatches(chunksNeedingEmbedding, batchOptions);
|
|
11056
11331
|
let fatalError;
|
|
11057
11332
|
for (const requestBatch of requestBatches) {
|
|
11058
11333
|
await options.queue.onSizeLessThan(Math.max(1, options.providerRateLimits.concurrency));
|
|
11059
11334
|
const task = options.queue.add(async () => {
|
|
11060
11335
|
if (options.rateLimitState.backoffMs > 0) {
|
|
11061
|
-
await new Promise((
|
|
11336
|
+
await new Promise((resolve17) => setTimeout(resolve17, options.rateLimitState.backoffMs));
|
|
11062
11337
|
}
|
|
11063
11338
|
try {
|
|
11064
11339
|
const embeddingResult = await pRetry(
|
|
@@ -11615,7 +11890,7 @@ var Indexer = class _Indexer {
|
|
|
11615
11890
|
}
|
|
11616
11891
|
if (!this.configuredProviderInfo) {
|
|
11617
11892
|
throw new Error(
|
|
11618
|
-
"No embedding provider available. Configure
|
|
11893
|
+
"No embedding provider available. Configure OpenAI, Google, Ollama, or a custom OpenAI-compatible endpoint."
|
|
11619
11894
|
);
|
|
11620
11895
|
}
|
|
11621
11896
|
this.logger.info("Initializing indexer", {
|
|
@@ -11625,15 +11900,6 @@ var Indexer = class _Indexer {
|
|
|
11625
11900
|
rerankerEnabled: this.config.reranker?.enabled ?? false
|
|
11626
11901
|
});
|
|
11627
11902
|
this.provider = createEmbeddingProvider(this.configuredProviderInfo);
|
|
11628
|
-
if (this.config.reranker?.enabled) {
|
|
11629
|
-
this.reranker = createReranker(this.config.reranker);
|
|
11630
|
-
if (this.reranker.isAvailable()) {
|
|
11631
|
-
this.logger.info("Reranker initialized", {
|
|
11632
|
-
model: this.config.reranker.model,
|
|
11633
|
-
baseUrl: this.config.reranker.baseUrl
|
|
11634
|
-
});
|
|
11635
|
-
}
|
|
11636
|
-
}
|
|
11637
11903
|
const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
|
|
11638
11904
|
const storePath = path19.join(this.indexPath, "vectors");
|
|
11639
11905
|
const vectorMetadataPath = `${storePath}.meta.json`;
|
|
@@ -11655,7 +11921,20 @@ var Indexer = class _Indexer {
|
|
|
11655
11921
|
]);
|
|
11656
11922
|
}
|
|
11657
11923
|
if (recoveredOwners.length > 0 && this.config.scope === "project") {
|
|
11658
|
-
|
|
11924
|
+
const unknownLegacyForceIndex = recoveredOwners.find(
|
|
11925
|
+
(owner) => this.hasUnknownLegacyForceIndexClear(owner)
|
|
11926
|
+
);
|
|
11927
|
+
if (unknownLegacyForceIndex) {
|
|
11928
|
+
throw new Error(
|
|
11929
|
+
`Cannot automatically recover interrupted force-index ${unknownLegacyForceIndex.token}: the legacy clearing phase ownership is unknown. The recovery marker was retained for manual inspection.`
|
|
11930
|
+
);
|
|
11931
|
+
}
|
|
11932
|
+
const shouldReset = recoveredOwners.some(
|
|
11933
|
+
(owner) => owner.clearRecovery !== void 0 || owner.operation === "clear" && owner.recoveryProtocolVersion !== 1
|
|
11934
|
+
);
|
|
11935
|
+
if (shouldReset) {
|
|
11936
|
+
await this.resetLocalIndexArtifacts();
|
|
11937
|
+
}
|
|
11659
11938
|
}
|
|
11660
11939
|
this.store = new VectorStore(storePath, dimensions);
|
|
11661
11940
|
if (existsSync11(storePath) || existsSync11(vectorMetadataPath)) {
|
|
@@ -12291,7 +12570,17 @@ var Indexer = class _Indexer {
|
|
|
12291
12570
|
const needsCallGraphResolutionMigration = database.getMetadata(this.getCallGraphResolutionMetadataKey()) !== CALL_GRAPH_RESOLUTION_VERSION;
|
|
12292
12571
|
for (const file of files) {
|
|
12293
12572
|
const storedPath = this.toStoredFilePath(file.path);
|
|
12294
|
-
|
|
12573
|
+
let currentHash;
|
|
12574
|
+
try {
|
|
12575
|
+
currentHash = hashFile(file.path);
|
|
12576
|
+
} catch (error) {
|
|
12577
|
+
stats.skippedFiles.push({ path: this.toCanonicalFilePath(file.path), reason: "unreadable" });
|
|
12578
|
+
this.logger.warn("Skipped unreadable file during indexing", {
|
|
12579
|
+
path: file.path,
|
|
12580
|
+
error: getErrorMessage4(error)
|
|
12581
|
+
});
|
|
12582
|
+
continue;
|
|
12583
|
+
}
|
|
12295
12584
|
currentFileHashes.set(storedPath, currentHash);
|
|
12296
12585
|
const cachedHashMatches = this.fileHashCache.get(storedPath) === currentHash;
|
|
12297
12586
|
const needsCallGraphRefresh = cachedHashMatches && needsCallGraphResolutionMigration && database.getChunksByFile(storedPath).some(
|
|
@@ -12299,7 +12588,8 @@ var Indexer = class _Indexer {
|
|
|
12299
12588
|
);
|
|
12300
12589
|
const requiresSwiftParserUpgrade = reparseCachedSwiftFiles && path19.extname(storedPath).toLowerCase() === ".swift";
|
|
12301
12590
|
const requiresMetalParserUpgrade = reparseCachedMetalFiles && path19.extname(storedPath).toLowerCase() === ".metal";
|
|
12302
|
-
|
|
12591
|
+
const inMigrationScope = forceScopedReembed && scopedRoots !== null && this.isFileInCurrentScope(storedPath, scopedRoots);
|
|
12592
|
+
if (cachedHashMatches && !inMigrationScope && !needsCallGraphRefresh && !requiresSwiftParserUpgrade && !requiresMetalParserUpgrade && !refreshCachedSymbols) {
|
|
12303
12593
|
unchangedFilePaths.add(storedPath);
|
|
12304
12594
|
this.logger.recordCacheHit();
|
|
12305
12595
|
} else {
|
|
@@ -12425,6 +12715,9 @@ var Indexer = class _Indexer {
|
|
|
12425
12715
|
}
|
|
12426
12716
|
}
|
|
12427
12717
|
let processedChangedFiles = 0;
|
|
12718
|
+
let lastCheckpointChunks = 0;
|
|
12719
|
+
const committedFilePaths = new Set(unchangedFilePaths);
|
|
12720
|
+
const resolvedRetryChunkIds = /* @__PURE__ */ new Set();
|
|
12428
12721
|
for (const descriptorBatch of iterateOrderedFileBatches(
|
|
12429
12722
|
changedFileDescriptors,
|
|
12430
12723
|
(descriptor) => descriptor.sourceBytes,
|
|
@@ -12438,7 +12731,7 @@ var Indexer = class _Indexer {
|
|
|
12438
12731
|
const loadedByPath = new Map(loadedFiles.map((file) => [file.path, file]));
|
|
12439
12732
|
const descriptorByPath = new Map(descriptorBatch.map((descriptor) => [descriptor.storedPath, descriptor]));
|
|
12440
12733
|
const parseStartTime = performance2.now();
|
|
12441
|
-
const parsedFiles = parseFiles(loadedFiles);
|
|
12734
|
+
const parsedFiles = parseFiles(loadedFiles, this.config.indexing.linesPerChunk);
|
|
12442
12735
|
const parseMs = performance2.now() - parseStartTime;
|
|
12443
12736
|
this.logger.recordFilesParsed(parsedFiles.length);
|
|
12444
12737
|
this.logger.recordParseDuration(parseMs);
|
|
@@ -12461,7 +12754,7 @@ var Indexer = class _Indexer {
|
|
|
12461
12754
|
}
|
|
12462
12755
|
let chunksToProcess = parsed.chunks;
|
|
12463
12756
|
if (this.config.indexing.fallbackToTextOnMaxChunks && chunksToProcess.length > this.config.indexing.maxChunksPerFile) {
|
|
12464
|
-
chunksToProcess = parseFileAsText(parsed.path, loadedFile.content);
|
|
12757
|
+
chunksToProcess = parseFileAsText(parsed.path, loadedFile.content, this.config.indexing.linesPerChunk);
|
|
12465
12758
|
}
|
|
12466
12759
|
chunksToProcess = selectIndexableChunks(
|
|
12467
12760
|
chunksToProcess,
|
|
@@ -12595,6 +12888,10 @@ var Indexer = class _Indexer {
|
|
|
12595
12888
|
}
|
|
12596
12889
|
if (symbolBatch.length > 0) {
|
|
12597
12890
|
database.upsertSymbolsBatch(symbolBatch);
|
|
12891
|
+
database.addSymbolsToBranchBatch(
|
|
12892
|
+
this.getBranchCatalogKey(),
|
|
12893
|
+
symbolBatch.map((symbol) => symbol.id)
|
|
12894
|
+
);
|
|
12598
12895
|
}
|
|
12599
12896
|
if (edgeBatch.length > 0) {
|
|
12600
12897
|
database.upsertCallEdgesBatch(edgeBatch);
|
|
@@ -12630,6 +12927,12 @@ var Indexer = class _Indexer {
|
|
|
12630
12927
|
forceReembed: forceScopedReembed,
|
|
12631
12928
|
reuseCachedEmbeddings: true,
|
|
12632
12929
|
incrementRepeatedFailures: true,
|
|
12930
|
+
onSucceeded: (succeededChunks) => {
|
|
12931
|
+
database.addChunksToBranchBatch(
|
|
12932
|
+
this.getBranchCatalogKey(),
|
|
12933
|
+
succeededChunks.map((chunk) => chunk.id)
|
|
12934
|
+
);
|
|
12935
|
+
},
|
|
12633
12936
|
onProgress: (batchProgress) => onProgress?.({
|
|
12634
12937
|
phase: "embedding",
|
|
12635
12938
|
filesProcessed: unchangedFilePaths.size + processedChangedFiles,
|
|
@@ -12648,6 +12951,27 @@ var Indexer = class _Indexer {
|
|
|
12648
12951
|
}
|
|
12649
12952
|
}
|
|
12650
12953
|
}
|
|
12954
|
+
for (const descriptor of descriptorBatch) {
|
|
12955
|
+
const existingFileChunks = existingChunksByFile.get(descriptor.storedPath);
|
|
12956
|
+
if (!existingFileChunks || existingFileChunks.size === 0) {
|
|
12957
|
+
committedFilePaths.add(descriptor.storedPath);
|
|
12958
|
+
}
|
|
12959
|
+
}
|
|
12960
|
+
const checkpointInterval = this.getCheckpointIntervalChunks(stats.totalChunks);
|
|
12961
|
+
if (stats.totalChunks - lastCheckpointChunks >= checkpointInterval) {
|
|
12962
|
+
lastCheckpointChunks = stats.totalChunks;
|
|
12963
|
+
this.checkpointIndexRun(
|
|
12964
|
+
database,
|
|
12965
|
+
store,
|
|
12966
|
+
invertedIndex,
|
|
12967
|
+
failedProcessing,
|
|
12968
|
+
resolvedRetryChunkIds,
|
|
12969
|
+
currentFileHashes,
|
|
12970
|
+
committedFilePaths,
|
|
12971
|
+
scopedRoots,
|
|
12972
|
+
configuredProviderInfo
|
|
12973
|
+
);
|
|
12974
|
+
}
|
|
12651
12975
|
}
|
|
12652
12976
|
const retryableFailedChunks = this.iterateLatestFailedChunks(
|
|
12653
12977
|
failedProcessing.latestById,
|
|
@@ -12668,6 +12992,7 @@ var Indexer = class _Indexer {
|
|
|
12668
12992
|
retryableChunksWithExistingData.add(chunk.id);
|
|
12669
12993
|
}
|
|
12670
12994
|
}
|
|
12995
|
+
this.restoreMissingChunkRows(database, pendingChunks);
|
|
12671
12996
|
stats.totalChunks += pendingChunks.length;
|
|
12672
12997
|
onProgress?.({
|
|
12673
12998
|
phase: "embedding",
|
|
@@ -12690,6 +13015,17 @@ var Indexer = class _Indexer {
|
|
|
12690
13015
|
forceReembed: forceScopedReembed,
|
|
12691
13016
|
reuseCachedEmbeddings: true,
|
|
12692
13017
|
incrementRepeatedFailures: true,
|
|
13018
|
+
forceSingleItemBatches: true,
|
|
13019
|
+
onSucceeded: (succeededChunks) => {
|
|
13020
|
+
database.addChunksToBranchBatch(
|
|
13021
|
+
this.getBranchCatalogKey(),
|
|
13022
|
+
succeededChunks.map((chunk) => chunk.id)
|
|
13023
|
+
);
|
|
13024
|
+
for (const chunk of succeededChunks) {
|
|
13025
|
+
failedProcessing.latestById.delete(chunk.id);
|
|
13026
|
+
resolvedRetryChunkIds.add(chunk.id);
|
|
13027
|
+
}
|
|
13028
|
+
},
|
|
12693
13029
|
onProgress: (batchProgress) => onProgress?.({
|
|
12694
13030
|
phase: "embedding",
|
|
12695
13031
|
filesProcessed: files.length,
|
|
@@ -12707,6 +13043,20 @@ var Indexer = class _Indexer {
|
|
|
12707
13043
|
failedForcedChunkIds.add(chunkId);
|
|
12708
13044
|
}
|
|
12709
13045
|
}
|
|
13046
|
+
if (stats.totalChunks - lastCheckpointChunks >= this.getCheckpointIntervalChunks(stats.totalChunks)) {
|
|
13047
|
+
lastCheckpointChunks = stats.totalChunks;
|
|
13048
|
+
this.checkpointIndexRun(
|
|
13049
|
+
database,
|
|
13050
|
+
store,
|
|
13051
|
+
invertedIndex,
|
|
13052
|
+
failedProcessing,
|
|
13053
|
+
resolvedRetryChunkIds,
|
|
13054
|
+
currentFileHashes,
|
|
13055
|
+
committedFilePaths,
|
|
13056
|
+
scopedRoots,
|
|
13057
|
+
configuredProviderInfo
|
|
13058
|
+
);
|
|
13059
|
+
}
|
|
12710
13060
|
}
|
|
12711
13061
|
const removedChunkIds = [];
|
|
12712
13062
|
for (const [chunkId] of existingChunks) {
|
|
@@ -12743,13 +13093,6 @@ var Indexer = class _Indexer {
|
|
|
12743
13093
|
if (removedStoredChunks) {
|
|
12744
13094
|
this.saveInvertedIndex(invertedIndex);
|
|
12745
13095
|
}
|
|
12746
|
-
if (scopedRoots) {
|
|
12747
|
-
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
12748
|
-
} else {
|
|
12749
|
-
this.fileHashCache = currentFileHashes;
|
|
12750
|
-
this.saveFileHashCache();
|
|
12751
|
-
}
|
|
12752
|
-
this.finalizeFailedBatchWriteState(failedProcessing.state);
|
|
12753
13096
|
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
12754
13097
|
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
12755
13098
|
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
@@ -12758,6 +13101,13 @@ var Indexer = class _Indexer {
|
|
|
12758
13101
|
this.indexCompatibility = { compatible: true };
|
|
12759
13102
|
database.commitWriteTransaction();
|
|
12760
13103
|
writeTransactionActive = false;
|
|
13104
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state, resolvedRetryChunkIds);
|
|
13105
|
+
if (scopedRoots) {
|
|
13106
|
+
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
13107
|
+
} else {
|
|
13108
|
+
this.fileHashCache = currentFileHashes;
|
|
13109
|
+
this.saveFileHashCache();
|
|
13110
|
+
}
|
|
12761
13111
|
stats.durationMs = Date.now() - startTime;
|
|
12762
13112
|
onProgress?.({
|
|
12763
13113
|
phase: "complete",
|
|
@@ -12781,13 +13131,6 @@ var Indexer = class _Indexer {
|
|
|
12781
13131
|
);
|
|
12782
13132
|
store.save();
|
|
12783
13133
|
this.saveInvertedIndex(invertedIndex);
|
|
12784
|
-
if (scopedRoots) {
|
|
12785
|
-
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
12786
|
-
} else {
|
|
12787
|
-
this.fileHashCache = currentFileHashes;
|
|
12788
|
-
this.saveFileHashCache();
|
|
12789
|
-
}
|
|
12790
|
-
this.finalizeFailedBatchWriteState(failedProcessing.state);
|
|
12791
13134
|
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
12792
13135
|
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
12793
13136
|
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
@@ -12796,6 +13139,13 @@ var Indexer = class _Indexer {
|
|
|
12796
13139
|
this.indexCompatibility = { compatible: true };
|
|
12797
13140
|
database.commitWriteTransaction();
|
|
12798
13141
|
writeTransactionActive = false;
|
|
13142
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state, resolvedRetryChunkIds);
|
|
13143
|
+
if (scopedRoots) {
|
|
13144
|
+
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
13145
|
+
} else {
|
|
13146
|
+
this.fileHashCache = currentFileHashes;
|
|
13147
|
+
this.saveFileHashCache();
|
|
13148
|
+
}
|
|
12799
13149
|
stats.durationMs = Date.now() - startTime;
|
|
12800
13150
|
onProgress?.({
|
|
12801
13151
|
phase: "complete",
|
|
@@ -12830,15 +13180,15 @@ var Indexer = class _Indexer {
|
|
|
12830
13180
|
);
|
|
12831
13181
|
store.save();
|
|
12832
13182
|
this.saveInvertedIndex(invertedIndex);
|
|
13183
|
+
database.commitWriteTransaction();
|
|
13184
|
+
writeTransactionActive = false;
|
|
13185
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state, resolvedRetryChunkIds);
|
|
12833
13186
|
if (scopedRoots) {
|
|
12834
13187
|
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
12835
13188
|
} else {
|
|
12836
13189
|
this.fileHashCache = currentFileHashes;
|
|
12837
13190
|
this.saveFileHashCache();
|
|
12838
13191
|
}
|
|
12839
|
-
this.finalizeFailedBatchWriteState(failedProcessing.state);
|
|
12840
|
-
database.commitWriteTransaction();
|
|
12841
|
-
writeTransactionActive = false;
|
|
12842
13192
|
if (this.config.indexing.autoGc && stats.removedChunks > 0) {
|
|
12843
13193
|
const gcReset = await this.maybeRunOrphanGc();
|
|
12844
13194
|
if (gcReset) {
|
|
@@ -12862,6 +13212,9 @@ var Indexer = class _Indexer {
|
|
|
12862
13212
|
if (forceScopedReembed && failedForcedChunkIds.size === 0) {
|
|
12863
13213
|
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
12864
13214
|
}
|
|
13215
|
+
if (forceScopedReembed) {
|
|
13216
|
+
database.setMetadata(this.getProjectMigrationFinalizedMetadataKey(), "true");
|
|
13217
|
+
}
|
|
12865
13218
|
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
12866
13219
|
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
12867
13220
|
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
@@ -12972,26 +13325,41 @@ var Indexer = class _Indexer {
|
|
|
12972
13325
|
shouldPrefilterByBranch: branchChunkIds !== null && (this.config.scope === "global" || hasInitializedBranchCatalog)
|
|
12973
13326
|
};
|
|
12974
13327
|
}
|
|
12975
|
-
|
|
13328
|
+
searchCandidatesWithAllowedIds(initialLimit, totalCount, allowedChunkIds, shouldPrefilter, search, getChunkId) {
|
|
12976
13329
|
const normalizedLimit = Math.max(0, Math.floor(initialLimit));
|
|
12977
13330
|
if (normalizedLimit === 0) return [];
|
|
12978
|
-
if (!
|
|
13331
|
+
if (!shouldPrefilter || !allowedChunkIds) {
|
|
12979
13332
|
return search(normalizedLimit);
|
|
12980
13333
|
}
|
|
12981
|
-
const targetCount = Math.min(normalizedLimit,
|
|
13334
|
+
const targetCount = Math.min(normalizedLimit, allowedChunkIds.size);
|
|
12982
13335
|
if (targetCount === 0 || totalCount === 0) return [];
|
|
12983
13336
|
let requestedLimit = Math.min(normalizedLimit, totalCount);
|
|
12984
13337
|
while (true) {
|
|
12985
13338
|
const results = search(requestedLimit);
|
|
12986
|
-
const
|
|
12987
|
-
if (
|
|
12988
|
-
return
|
|
13339
|
+
const allowedResults = results.filter((candidate) => allowedChunkIds.has(getChunkId(candidate)));
|
|
13340
|
+
if (allowedResults.length >= targetCount || results.length < requestedLimit || requestedLimit >= totalCount) {
|
|
13341
|
+
return allowedResults;
|
|
12989
13342
|
}
|
|
12990
13343
|
const nextLimit = Math.min(totalCount, Math.max(requestedLimit + 1, requestedLimit * 2));
|
|
12991
|
-
if (nextLimit === requestedLimit) return
|
|
13344
|
+
if (nextLimit === requestedLimit) return allowedResults;
|
|
12992
13345
|
requestedLimit = nextLimit;
|
|
12993
13346
|
}
|
|
12994
13347
|
}
|
|
13348
|
+
getTemporalChunkIds(database, options) {
|
|
13349
|
+
if (!options?.blameSince && !options?.blameUntil) return null;
|
|
13350
|
+
const since = options.blameSince ? parseBlameTimestamp(options.blameSince, false) : void 0;
|
|
13351
|
+
const until = options.blameUntil ? parseBlameTimestamp(options.blameUntil, true) : void 0;
|
|
13352
|
+
if (since === null || until === null) {
|
|
13353
|
+
return /* @__PURE__ */ new Set();
|
|
13354
|
+
}
|
|
13355
|
+
return new Set(database.getChunkIdsByBlameDate(since, until));
|
|
13356
|
+
}
|
|
13357
|
+
intersectChunkIdSets(first, second) {
|
|
13358
|
+
if (first === null) return second;
|
|
13359
|
+
if (second === null) return first;
|
|
13360
|
+
const [smaller, larger] = first.size <= second.size ? [first, second] : [second, first];
|
|
13361
|
+
return new Set(Array.from(smaller).filter((chunkId) => larger.has(chunkId)));
|
|
13362
|
+
}
|
|
12995
13363
|
buildCandidateSnapshot(candidate) {
|
|
12996
13364
|
return {
|
|
12997
13365
|
id: candidate.id,
|
|
@@ -13006,13 +13374,16 @@ var Indexer = class _Indexer {
|
|
|
13006
13374
|
buildCandidateSnapshotList(candidates) {
|
|
13007
13375
|
return candidates.map((candidate) => this.buildCandidateSnapshot(candidate));
|
|
13008
13376
|
}
|
|
13009
|
-
searchSemanticCandidates(store, embedding, initialLimit, branchChunkIds, shouldPrefilterByBranch) {
|
|
13010
|
-
|
|
13011
|
-
|
|
13012
|
-
|
|
13377
|
+
searchSemanticCandidates(store, embedding, initialLimit, branchChunkIds, shouldPrefilterByBranch, temporalChunkIds) {
|
|
13378
|
+
const availableCount = temporalChunkIds?.size ?? store.count();
|
|
13379
|
+
if (availableCount === 0) return [];
|
|
13380
|
+
const allowedIds = temporalChunkIds === null ? void 0 : Array.from(temporalChunkIds);
|
|
13381
|
+
return this.searchCandidatesWithAllowedIds(
|
|
13382
|
+
Math.min(initialLimit, availableCount),
|
|
13383
|
+
availableCount,
|
|
13013
13384
|
branchChunkIds,
|
|
13014
13385
|
shouldPrefilterByBranch,
|
|
13015
|
-
(requestedLimit) => store.search(embedding, requestedLimit),
|
|
13386
|
+
(requestedLimit) => store.search(embedding, requestedLimit, allowedIds),
|
|
13016
13387
|
(candidate) => candidate.id
|
|
13017
13388
|
);
|
|
13018
13389
|
}
|
|
@@ -13037,7 +13408,9 @@ var Indexer = class _Indexer {
|
|
|
13037
13408
|
const rerankTopN = this.config.search.rerankTopN;
|
|
13038
13409
|
const filterByBranch = options?.filterByBranch ?? true;
|
|
13039
13410
|
const sourceIntent = options?.definitionIntent === true || classifyQueryIntentRaw(query) === "source";
|
|
13411
|
+
const prioritizeSourcePaths = sourceIntent || options?.prioritizeSourcePaths === true;
|
|
13040
13412
|
const identifierHints = extractIdentifierHints(query);
|
|
13413
|
+
const candidateLimit = maxResults * (prioritizeSourcePaths ? 12 : 4);
|
|
13041
13414
|
this.logger.search("debug", "Starting search", {
|
|
13042
13415
|
query,
|
|
13043
13416
|
maxResults,
|
|
@@ -13068,25 +13441,28 @@ var Indexer = class _Indexer {
|
|
|
13068
13441
|
branchChunkIds = new Set(branchCatalogKeys.flatMap((branchKey) => database.getBranchChunkIds(branchKey)));
|
|
13069
13442
|
branchSymbolIds = new Set(branchCatalogKeys.flatMap((branchKey) => database.getBranchSymbolIds(branchKey)));
|
|
13070
13443
|
}
|
|
13444
|
+
const temporalChunkIds = this.getTemporalChunkIds(database, options);
|
|
13071
13445
|
const { hasInitializedBranchCatalog, shouldPrefilterByBranch } = this.getBranchPrefilterState(database, branchChunkIds);
|
|
13072
13446
|
const prefilterMs = performance2.now() - prefilterStartTime;
|
|
13073
13447
|
const vectorStartTime = performance2.now();
|
|
13074
13448
|
const semanticCandidates = embedding ? this.searchSemanticCandidates(
|
|
13075
13449
|
store,
|
|
13076
13450
|
embedding,
|
|
13077
|
-
|
|
13451
|
+
candidateLimit,
|
|
13078
13452
|
branchChunkIds,
|
|
13079
|
-
shouldPrefilterByBranch
|
|
13453
|
+
shouldPrefilterByBranch,
|
|
13454
|
+
temporalChunkIds
|
|
13080
13455
|
) : [];
|
|
13081
13456
|
const vectorMs = performance2.now() - vectorStartTime;
|
|
13082
13457
|
const keywordStartTime = performance2.now();
|
|
13083
13458
|
const keywordCandidates = await this.keywordSearch(
|
|
13084
13459
|
query,
|
|
13085
|
-
|
|
13460
|
+
candidateLimit,
|
|
13086
13461
|
store,
|
|
13087
13462
|
invertedIndex,
|
|
13088
13463
|
branchChunkIds,
|
|
13089
|
-
shouldPrefilterByBranch
|
|
13464
|
+
shouldPrefilterByBranch,
|
|
13465
|
+
temporalChunkIds
|
|
13090
13466
|
);
|
|
13091
13467
|
const keywordMs = performance2.now() - keywordStartTime;
|
|
13092
13468
|
const scopedSemanticCandidates = semanticCandidates.filter(
|
|
@@ -13108,7 +13484,7 @@ var Indexer = class _Indexer {
|
|
|
13108
13484
|
rerankTopN,
|
|
13109
13485
|
limit: maxResults,
|
|
13110
13486
|
hybridWeight: rankingHybridWeight,
|
|
13111
|
-
prioritizeSourcePaths
|
|
13487
|
+
prioritizeSourcePaths
|
|
13112
13488
|
});
|
|
13113
13489
|
const rerankedCombined = await this.rerankCandidatesWithApi(query, combined, {
|
|
13114
13490
|
definitionIntent: options?.definitionIntent === true,
|
|
@@ -13144,10 +13520,11 @@ var Indexer = class _Indexer {
|
|
|
13144
13520
|
branchSymbolIds,
|
|
13145
13521
|
maxResults,
|
|
13146
13522
|
union,
|
|
13147
|
-
sourceIntent
|
|
13523
|
+
sourceIntent,
|
|
13524
|
+
options?.definitionIntent === true && ((options.directory?.trim().length ?? 0) > 0 || (options.fileType?.trim().length ?? 0) > 0)
|
|
13148
13525
|
);
|
|
13149
13526
|
const prePrimaryLane = mergeTieredResults(deterministicIdentifierLane, identifierLane, maxResults * 4);
|
|
13150
|
-
const primaryLane = mergeTieredResults(prePrimaryLane, symbolLane, maxResults * 4);
|
|
13527
|
+
const primaryLane = options?.definitionIntent === true ? mergeTieredResults(symbolLane, prePrimaryLane, maxResults * 4) : mergeTieredResults(prePrimaryLane, symbolLane, maxResults * 4);
|
|
13151
13528
|
const tiered = mergeTieredResults(primaryLane, rescued, maxResults * 4);
|
|
13152
13529
|
const hasCodeHints = extractCodeTermHints(query).length > 0 || identifierHints.length > 0;
|
|
13153
13530
|
const baseFiltered = tiered.filter(
|
|
@@ -13242,14 +13619,18 @@ var Indexer = class _Indexer {
|
|
|
13242
13619
|
})
|
|
13243
13620
|
);
|
|
13244
13621
|
}
|
|
13245
|
-
async keywordSearch(query, limit, store, invertedIndex, branchChunkIds = null, shouldPrefilterByBranch = false) {
|
|
13622
|
+
async keywordSearch(query, limit, store, invertedIndex, branchChunkIds = null, shouldPrefilterByBranch = false, temporalChunkIds = null) {
|
|
13246
13623
|
const normalizedLimit = Math.max(0, Math.floor(limit));
|
|
13247
13624
|
if (normalizedLimit === 0) return [];
|
|
13248
|
-
const
|
|
13625
|
+
const allowedChunkIds = this.intersectChunkIdSets(
|
|
13626
|
+
shouldPrefilterByBranch ? branchChunkIds : null,
|
|
13627
|
+
temporalChunkIds
|
|
13628
|
+
);
|
|
13629
|
+
const scoreEntries = this.searchCandidatesWithAllowedIds(
|
|
13249
13630
|
normalizedLimit,
|
|
13250
13631
|
invertedIndex.getDocumentCount(),
|
|
13251
|
-
|
|
13252
|
-
|
|
13632
|
+
allowedChunkIds,
|
|
13633
|
+
allowedChunkIds !== null,
|
|
13253
13634
|
(requestedLimit) => Array.from(invertedIndex.search(query, requestedLimit)),
|
|
13254
13635
|
([chunkId]) => chunkId
|
|
13255
13636
|
);
|
|
@@ -13334,7 +13715,17 @@ var Indexer = class _Indexer {
|
|
|
13334
13715
|
);
|
|
13335
13716
|
const currentFileHashes = /* @__PURE__ */ new Map();
|
|
13336
13717
|
for (const file of files) {
|
|
13337
|
-
|
|
13718
|
+
let hash;
|
|
13719
|
+
try {
|
|
13720
|
+
hash = hashFile(file.path);
|
|
13721
|
+
} catch (error) {
|
|
13722
|
+
this.logger.warn("Skipped unreadable file during freshness check", {
|
|
13723
|
+
path: file.path,
|
|
13724
|
+
error: getErrorMessage4(error)
|
|
13725
|
+
});
|
|
13726
|
+
return { readable: false, current: false, reason: "unreadable" };
|
|
13727
|
+
}
|
|
13728
|
+
currentFileHashes.set(this.toStoredFilePath(file.path), hash);
|
|
13338
13729
|
}
|
|
13339
13730
|
const scopedRoots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
13340
13731
|
const cachedFileHashes = scopedRoots ? new Map(Array.from(this.fileHashCache).filter(([filePath]) => this.isFileInCurrentScope(filePath, scopedRoots))) : this.fileHashCache;
|
|
@@ -13360,69 +13751,87 @@ var Indexer = class _Indexer {
|
|
|
13360
13751
|
async forceIndex(onProgress) {
|
|
13361
13752
|
return this.withIndexMutationLease("force-index", async (recoveredOwners) => {
|
|
13362
13753
|
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
13363
|
-
|
|
13754
|
+
const recovery = this.beginClearRecoveryState();
|
|
13755
|
+
await this.clearIndexUnlocked(recovery.compatibilityDecision);
|
|
13756
|
+
this.finishClearRecoveryState();
|
|
13364
13757
|
return this.indexUnlocked(onProgress, [], true);
|
|
13365
13758
|
});
|
|
13366
13759
|
}
|
|
13367
13760
|
async clearIndex() {
|
|
13368
13761
|
await this.withIndexMutationLease("clear", async (recoveredOwners) => {
|
|
13369
13762
|
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
13370
|
-
|
|
13763
|
+
const recovery = this.beginClearRecoveryState();
|
|
13764
|
+
await this.clearIndexUnlocked(recovery.compatibilityDecision);
|
|
13371
13765
|
});
|
|
13372
13766
|
}
|
|
13373
|
-
|
|
13767
|
+
clearGlobalIndexDataUnlocked(projectRoot = this.projectRoot) {
|
|
13374
13768
|
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
13375
|
-
|
|
13376
|
-
|
|
13377
|
-
|
|
13378
|
-
|
|
13379
|
-
|
|
13380
|
-
|
|
13381
|
-
|
|
13382
|
-
|
|
13383
|
-
|
|
13384
|
-
|
|
13385
|
-
|
|
13386
|
-
|
|
13387
|
-
|
|
13388
|
-
|
|
13389
|
-
|
|
13769
|
+
const clearedBranchKeys = database.getAllBranches();
|
|
13770
|
+
store.clear();
|
|
13771
|
+
store.save();
|
|
13772
|
+
invertedIndex.clear();
|
|
13773
|
+
this.saveInvertedIndex(invertedIndex);
|
|
13774
|
+
this.fileHashCache.clear();
|
|
13775
|
+
this.saveFileHashCache();
|
|
13776
|
+
database.clearAllIndexedData();
|
|
13777
|
+
this.deleteBranchCommitMetadata(database, clearedBranchKeys);
|
|
13778
|
+
this.clearFailedBatchState();
|
|
13779
|
+
database.deleteMetadata("index.version");
|
|
13780
|
+
database.deleteMetadata("index.pathStorageVersion");
|
|
13781
|
+
database.deleteMetadata("index.embeddingProvider");
|
|
13782
|
+
database.deleteMetadata("index.embeddingModel");
|
|
13783
|
+
database.deleteMetadata("index.embeddingDimensions");
|
|
13784
|
+
database.deleteMetadata("index.embeddingStrategyVersion");
|
|
13785
|
+
const projectIdentityHash = this.getProjectIdentityHash(projectRoot);
|
|
13786
|
+
database.deleteMetadata(this.getProjectEmbeddingStrategyMetadataKey(projectIdentityHash));
|
|
13787
|
+
database.deleteMetadata(this.getProjectForceReembedMetadataKey(projectIdentityHash));
|
|
13788
|
+
database.deleteMetadata(this.getLegacyMigrationMetadataKey(projectIdentityHash));
|
|
13789
|
+
database.deleteMetadata("index.createdAt");
|
|
13790
|
+
database.deleteMetadata("index.updatedAt");
|
|
13791
|
+
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
13792
|
+
}
|
|
13793
|
+
clearGlobalIndexUnlocked(projectRoot = this.projectRoot, roots = this.getScopedRoots(), recoveryDecision) {
|
|
13794
|
+
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
13795
|
+
store.load();
|
|
13796
|
+
invertedIndex.load();
|
|
13797
|
+
this.loadFileHashCache();
|
|
13798
|
+
const compatibility = this.checkCompatibility();
|
|
13799
|
+
const compatibilityDecision = recoveryDecision ?? (compatibility.compatible ? "compatible" : compatibility.code === "EMBEDDING_STRATEGY_MISMATCH" /* EMBEDDING_STRATEGY_MISMATCH */ ? "embedding-strategy-mismatch" : "incompatible");
|
|
13800
|
+
const allMetadata = store.getAllMetadata();
|
|
13801
|
+
const hasForeignData = allMetadata.some(({ metadata }) => !this.isFileInCurrentScope(metadata.filePath, roots)) || this.hasForeignScopedBranchData(projectRoot, roots) || this.hasForeignScopedFileHashData(roots) || this.hasForeignScopedFailedBatches(roots);
|
|
13802
|
+
if (compatibilityDecision !== "compatible" && hasForeignData) {
|
|
13803
|
+
if (compatibilityDecision === "embedding-strategy-mismatch") {
|
|
13804
|
+
this.clearSharedIndexProjectData(store, invertedIndex, database, roots, projectRoot);
|
|
13805
|
+
this.clearScopedFileHashCache(roots);
|
|
13806
|
+
this.clearScopedFailedBatches(roots);
|
|
13807
|
+
const projectIdentityHash = this.getProjectIdentityHash(projectRoot);
|
|
13808
|
+
database.setMetadata(this.getProjectForceReembedMetadataKey(projectIdentityHash), "true");
|
|
13809
|
+
database.deleteMetadata(this.getProjectEmbeddingStrategyMetadataKey(projectIdentityHash));
|
|
13810
|
+
database.deleteMetadata(this.getProjectMigrationFinalizedMetadataKey(projectIdentityHash));
|
|
13811
|
+
if (projectRoot === this.projectRoot) {
|
|
13390
13812
|
this.indexCompatibility = { compatible: true };
|
|
13391
|
-
return;
|
|
13392
13813
|
}
|
|
13393
|
-
throw new Error(
|
|
13394
|
-
`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.`
|
|
13395
|
-
);
|
|
13396
|
-
}
|
|
13397
|
-
if (!hasForeignData) {
|
|
13398
|
-
const clearedBranchKeys2 = database.getAllBranches();
|
|
13399
|
-
store.clear();
|
|
13400
|
-
store.save();
|
|
13401
|
-
invertedIndex.clear();
|
|
13402
|
-
this.saveInvertedIndex(invertedIndex);
|
|
13403
|
-
this.fileHashCache.clear();
|
|
13404
|
-
this.saveFileHashCache();
|
|
13405
|
-
database.clearAllIndexedData();
|
|
13406
|
-
this.deleteBranchCommitMetadata(database, clearedBranchKeys2);
|
|
13407
|
-
this.clearFailedBatchState();
|
|
13408
|
-
database.deleteMetadata("index.version");
|
|
13409
|
-
database.deleteMetadata("index.pathStorageVersion");
|
|
13410
|
-
database.deleteMetadata("index.embeddingProvider");
|
|
13411
|
-
database.deleteMetadata("index.embeddingModel");
|
|
13412
|
-
database.deleteMetadata("index.embeddingDimensions");
|
|
13413
|
-
database.deleteMetadata("index.embeddingStrategyVersion");
|
|
13414
|
-
database.deleteMetadata(this.getProjectEmbeddingStrategyMetadataKey());
|
|
13415
|
-
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
13416
|
-
database.deleteMetadata(this.getLegacyMigrationMetadataKey());
|
|
13417
|
-
database.deleteMetadata("index.createdAt");
|
|
13418
|
-
database.deleteMetadata("index.updatedAt");
|
|
13419
|
-
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
13420
13814
|
return;
|
|
13421
13815
|
}
|
|
13422
|
-
|
|
13423
|
-
|
|
13424
|
-
|
|
13816
|
+
throw new Error(
|
|
13817
|
+
`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.`
|
|
13818
|
+
);
|
|
13819
|
+
}
|
|
13820
|
+
if (!hasForeignData) {
|
|
13821
|
+
this.clearGlobalIndexDataUnlocked(projectRoot);
|
|
13822
|
+
return;
|
|
13823
|
+
}
|
|
13824
|
+
this.clearSharedIndexProjectData(store, invertedIndex, database, roots, projectRoot);
|
|
13825
|
+
this.clearScopedFileHashCache(roots);
|
|
13826
|
+
this.clearScopedFailedBatches(roots);
|
|
13827
|
+
if (projectRoot === this.projectRoot) {
|
|
13425
13828
|
this.indexCompatibility = compatibility;
|
|
13829
|
+
}
|
|
13830
|
+
}
|
|
13831
|
+
async clearIndexUnlocked(recoveryDecision) {
|
|
13832
|
+
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
13833
|
+
if (this.config.scope === "global") {
|
|
13834
|
+
this.clearGlobalIndexUnlocked(this.projectRoot, this.getScopedRoots(), recoveryDecision);
|
|
13426
13835
|
return;
|
|
13427
13836
|
}
|
|
13428
13837
|
if (!this.isProjectOwnedIndexPath()) {
|
|
@@ -13588,6 +13997,7 @@ var Indexer = class _Indexer {
|
|
|
13588
13997
|
)) {
|
|
13589
13998
|
const chunks = retryBatch.map(({ chunk }) => chunk);
|
|
13590
13999
|
const attemptCounts = new Map(retryBatch.map(({ chunk, attemptCount }) => [chunk.id, attemptCount]));
|
|
14000
|
+
this.restoreMissingChunkRows(database, chunks);
|
|
13591
14001
|
const batchResult = await this.processPendingChunkBatch(chunks, {
|
|
13592
14002
|
store,
|
|
13593
14003
|
provider,
|
|
@@ -13602,6 +14012,7 @@ var Indexer = class _Indexer {
|
|
|
13602
14012
|
forceReembed: false,
|
|
13603
14013
|
reuseCachedEmbeddings: false,
|
|
13604
14014
|
incrementRepeatedFailures: false,
|
|
14015
|
+
forceSingleItemBatches: true,
|
|
13605
14016
|
onSucceeded: (succeededChunks) => {
|
|
13606
14017
|
database.addChunksToBranchBatch(
|
|
13607
14018
|
this.getBranchCatalogKey(),
|
|
@@ -13623,9 +14034,12 @@ var Indexer = class _Indexer {
|
|
|
13623
14034
|
this.saveInvertedIndex(invertedIndex);
|
|
13624
14035
|
}
|
|
13625
14036
|
if (roots && succeeded > 0 && remaining === 0 && this.hasProjectForceReembedPending()) {
|
|
13626
|
-
database.
|
|
13627
|
-
|
|
13628
|
-
|
|
14037
|
+
const migrationFinalized = database.getMetadata(this.getProjectMigrationFinalizedMetadataKey()) === "true";
|
|
14038
|
+
if (migrationFinalized) {
|
|
14039
|
+
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
14040
|
+
this.saveIndexMetadata(configuredProviderInfo);
|
|
14041
|
+
this.indexCompatibility = { compatible: true };
|
|
14042
|
+
}
|
|
13629
14043
|
}
|
|
13630
14044
|
return { succeeded, failed, remaining };
|
|
13631
14045
|
}
|
|
@@ -13647,7 +14061,8 @@ var Indexer = class _Indexer {
|
|
|
13647
14061
|
latestById.set(chunkId, {
|
|
13648
14062
|
attemptCount: batch.attemptCount,
|
|
13649
14063
|
error: batch.error,
|
|
13650
|
-
lastAttempt: batch.lastAttempt
|
|
14064
|
+
lastAttempt: batch.lastAttempt,
|
|
14065
|
+
chunks: [rawChunk]
|
|
13651
14066
|
});
|
|
13652
14067
|
}
|
|
13653
14068
|
}
|
|
@@ -13714,6 +14129,7 @@ var Indexer = class _Indexer {
|
|
|
13714
14129
|
this.getBranchCatalogKeys().flatMap((branchKey) => database.getBranchChunkIds(branchKey))
|
|
13715
14130
|
);
|
|
13716
14131
|
}
|
|
14132
|
+
const temporalChunkIds = this.getTemporalChunkIds(database, options);
|
|
13717
14133
|
const { hasInitializedBranchCatalog, shouldPrefilterByBranch } = this.getBranchPrefilterState(database, branchChunkIds);
|
|
13718
14134
|
const prefilterMs = performance2.now() - prefilterStartTime;
|
|
13719
14135
|
const vectorStartTime = performance2.now();
|
|
@@ -13722,7 +14138,8 @@ var Indexer = class _Indexer {
|
|
|
13722
14138
|
embedding,
|
|
13723
14139
|
limit * 2,
|
|
13724
14140
|
branchChunkIds,
|
|
13725
|
-
shouldPrefilterByBranch
|
|
14141
|
+
shouldPrefilterByBranch,
|
|
14142
|
+
temporalChunkIds
|
|
13726
14143
|
);
|
|
13727
14144
|
const vectorMs = performance2.now() - vectorStartTime;
|
|
13728
14145
|
if (this.config.scope !== "global" && branchChunkIds && !hasInitializedBranchCatalog) {
|
|
@@ -13840,9 +14257,9 @@ var Indexer = class _Indexer {
|
|
|
13840
14257
|
this.requireReadableComponents(readIssues, "database");
|
|
13841
14258
|
let shortest = [];
|
|
13842
14259
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
13843
|
-
const
|
|
13844
|
-
if (
|
|
13845
|
-
shortest =
|
|
14260
|
+
const path30 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
|
|
14261
|
+
if (path30.length > 0 && (shortest.length === 0 || path30.length < shortest.length)) {
|
|
14262
|
+
shortest = path30;
|
|
13846
14263
|
}
|
|
13847
14264
|
}
|
|
13848
14265
|
return shortest.map((hop) => this.resolveFilePathRecord(hop));
|
|
@@ -13890,13 +14307,13 @@ var Indexer = class _Indexer {
|
|
|
13890
14307
|
}
|
|
13891
14308
|
}
|
|
13892
14309
|
if (!found) continue;
|
|
13893
|
-
const
|
|
14310
|
+
const path30 = [];
|
|
13894
14311
|
let currentSymbolId = toSymbolId;
|
|
13895
14312
|
while (true) {
|
|
13896
14313
|
const symbol = symbolsById.get(currentSymbolId);
|
|
13897
14314
|
if (!symbol) break;
|
|
13898
14315
|
const parent = parentBySymbolId.get(currentSymbolId);
|
|
13899
|
-
|
|
14316
|
+
path30.push({
|
|
13900
14317
|
symbolId: symbol.id,
|
|
13901
14318
|
symbolName: symbol.name,
|
|
13902
14319
|
filePath: symbol.filePath,
|
|
@@ -13906,9 +14323,9 @@ var Indexer = class _Indexer {
|
|
|
13906
14323
|
if (!parent) break;
|
|
13907
14324
|
currentSymbolId = parent.parentId;
|
|
13908
14325
|
}
|
|
13909
|
-
|
|
13910
|
-
if (
|
|
13911
|
-
shortest =
|
|
14326
|
+
path30.reverse();
|
|
14327
|
+
if (path30.length > 0 && (shortest.length === 0 || path30.length < shortest.length)) {
|
|
14328
|
+
shortest = path30;
|
|
13912
14329
|
}
|
|
13913
14330
|
}
|
|
13914
14331
|
return shortest.map((hop) => this.resolveFilePathRecord(hop));
|
|
@@ -14244,7 +14661,6 @@ var Indexer = class _Indexer {
|
|
|
14244
14661
|
this.store = null;
|
|
14245
14662
|
this.invertedIndex = null;
|
|
14246
14663
|
this.provider = null;
|
|
14247
|
-
this.reranker = null;
|
|
14248
14664
|
this.configuredProviderInfo = null;
|
|
14249
14665
|
this.indexCompatibility = null;
|
|
14250
14666
|
this.initializationMode = "none";
|
|
@@ -14472,9 +14888,11 @@ async function searchCodebase(projectRoot, host, query, options = {}) {
|
|
|
14472
14888
|
contextLines: options.contextLines,
|
|
14473
14889
|
metadataOnly: options.metadataOnly,
|
|
14474
14890
|
definitionIntent: options.definitionIntent,
|
|
14891
|
+
prioritizeSourcePaths: options.prioritizeSourcePaths,
|
|
14475
14892
|
blameAuthor: options.blameAuthor,
|
|
14476
14893
|
blameSha: options.blameSha,
|
|
14477
14894
|
blameSince: options.blameSince,
|
|
14895
|
+
blameUntil: options.blameUntil,
|
|
14478
14896
|
trace: options.trace
|
|
14479
14897
|
});
|
|
14480
14898
|
}
|
|
@@ -14520,7 +14938,9 @@ async function findSimilarCode(projectRoot, host, code, options = {}) {
|
|
|
14520
14938
|
fileType: options.fileType,
|
|
14521
14939
|
directory: options.directory,
|
|
14522
14940
|
chunkType: options.chunkType,
|
|
14523
|
-
excludeFile: options.excludeFile
|
|
14941
|
+
excludeFile: options.excludeFile,
|
|
14942
|
+
blameSince: options.blameSince,
|
|
14943
|
+
blameUntil: options.blameUntil
|
|
14524
14944
|
});
|
|
14525
14945
|
}
|
|
14526
14946
|
async function implementationLookup(projectRoot, host, query, options = {}) {
|
|
@@ -14569,12 +14989,12 @@ async function getCallGraphPath(projectRoot, host, from, to, maxDepth, fromFileP
|
|
|
14569
14989
|
if (fromResolution.status !== "resolved" || toResolution.status !== "resolved") {
|
|
14570
14990
|
return { from: fromResolution, to: toResolution, path: [] };
|
|
14571
14991
|
}
|
|
14572
|
-
const
|
|
14992
|
+
const path30 = await indexer.findCallPathBySymbolIds(
|
|
14573
14993
|
fromResolution.symbolId,
|
|
14574
14994
|
toResolution.symbolId,
|
|
14575
14995
|
maxDepth
|
|
14576
14996
|
);
|
|
14577
|
-
return { from: fromResolution, to: toResolution, path:
|
|
14997
|
+
return { from: fromResolution, to: toResolution, path: path30 };
|
|
14578
14998
|
}
|
|
14579
14999
|
async function runIndexCodebase(projectRoot, host, args, onProgress) {
|
|
14580
15000
|
const root = getProjectRoot(projectRoot, host);
|
|
@@ -14800,8 +15220,8 @@ function addKnowledgeBase(projectRoot, host, knowledgeBasePath) {
|
|
|
14800
15220
|
}
|
|
14801
15221
|
}
|
|
14802
15222
|
try {
|
|
14803
|
-
const
|
|
14804
|
-
if (!
|
|
15223
|
+
const stat5 = statSync5(normalizedPath2);
|
|
15224
|
+
if (!stat5.isDirectory()) {
|
|
14805
15225
|
return `Error: Path is not a directory: ${normalizedPath2}`;
|
|
14806
15226
|
}
|
|
14807
15227
|
} catch (error) {
|
|
@@ -14849,8 +15269,8 @@ function listKnowledgeBases(projectRoot, host) {
|
|
|
14849
15269
|
`;
|
|
14850
15270
|
if (exists) {
|
|
14851
15271
|
try {
|
|
14852
|
-
const
|
|
14853
|
-
result += ` Type: ${
|
|
15272
|
+
const stat5 = statSync5(resolvedPath);
|
|
15273
|
+
result += ` Type: ${stat5.isDirectory() ? "Directory" : "File"}
|
|
14854
15274
|
`;
|
|
14855
15275
|
} catch {
|
|
14856
15276
|
}
|
|
@@ -14891,7 +15311,7 @@ Run /index to rebuild the index without the removed knowledge base.`;
|
|
|
14891
15311
|
}
|
|
14892
15312
|
|
|
14893
15313
|
// src/watcher/file-watcher.ts
|
|
14894
|
-
import { existsSync as existsSync13 } from "fs";
|
|
15314
|
+
import { existsSync as existsSync13, statSync as statSync6 } from "fs";
|
|
14895
15315
|
|
|
14896
15316
|
// node_modules/chokidar/index.js
|
|
14897
15317
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
@@ -14983,7 +15403,7 @@ var ReaddirpStream = class extends Readable {
|
|
|
14983
15403
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
14984
15404
|
const statMethod = opts.lstat ? lstat : stat;
|
|
14985
15405
|
if (wantBigintFsStats) {
|
|
14986
|
-
this._stat = (
|
|
15406
|
+
this._stat = (path30) => statMethod(path30, { bigint: true });
|
|
14987
15407
|
} else {
|
|
14988
15408
|
this._stat = statMethod;
|
|
14989
15409
|
}
|
|
@@ -15008,8 +15428,8 @@ var ReaddirpStream = class extends Readable {
|
|
|
15008
15428
|
const par = this.parent;
|
|
15009
15429
|
const fil = par && par.files;
|
|
15010
15430
|
if (fil && fil.length > 0) {
|
|
15011
|
-
const { path:
|
|
15012
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
15431
|
+
const { path: path30, depth } = par;
|
|
15432
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path30));
|
|
15013
15433
|
const awaited = await Promise.all(slice);
|
|
15014
15434
|
for (const entry of awaited) {
|
|
15015
15435
|
if (!entry)
|
|
@@ -15049,20 +15469,20 @@ var ReaddirpStream = class extends Readable {
|
|
|
15049
15469
|
this.reading = false;
|
|
15050
15470
|
}
|
|
15051
15471
|
}
|
|
15052
|
-
async _exploreDir(
|
|
15472
|
+
async _exploreDir(path30, depth) {
|
|
15053
15473
|
let files;
|
|
15054
15474
|
try {
|
|
15055
|
-
files = await readdir(
|
|
15475
|
+
files = await readdir(path30, this._rdOptions);
|
|
15056
15476
|
} catch (error) {
|
|
15057
15477
|
this._onError(error);
|
|
15058
15478
|
}
|
|
15059
|
-
return { files, depth, path:
|
|
15479
|
+
return { files, depth, path: path30 };
|
|
15060
15480
|
}
|
|
15061
|
-
async _formatEntry(dirent,
|
|
15481
|
+
async _formatEntry(dirent, path30) {
|
|
15062
15482
|
let entry;
|
|
15063
15483
|
const basename9 = this._isDirent ? dirent.name : dirent;
|
|
15064
15484
|
try {
|
|
15065
|
-
const fullPath = presolve(pjoin(
|
|
15485
|
+
const fullPath = presolve(pjoin(path30, basename9));
|
|
15066
15486
|
entry = { path: prelative(this._root, fullPath), fullPath, basename: basename9 };
|
|
15067
15487
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
15068
15488
|
} catch (err) {
|
|
@@ -15462,16 +15882,16 @@ var delFromSet = (main, prop, item) => {
|
|
|
15462
15882
|
};
|
|
15463
15883
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
15464
15884
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
15465
|
-
function createFsWatchInstance(
|
|
15885
|
+
function createFsWatchInstance(path30, options, listener, errHandler, emitRaw) {
|
|
15466
15886
|
const handleEvent = (rawEvent, evPath) => {
|
|
15467
|
-
listener(
|
|
15468
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
15469
|
-
if (evPath &&
|
|
15470
|
-
fsWatchBroadcast(sp.resolve(
|
|
15887
|
+
listener(path30);
|
|
15888
|
+
emitRaw(rawEvent, evPath, { watchedPath: path30 });
|
|
15889
|
+
if (evPath && path30 !== evPath) {
|
|
15890
|
+
fsWatchBroadcast(sp.resolve(path30, evPath), KEY_LISTENERS, sp.join(path30, evPath));
|
|
15471
15891
|
}
|
|
15472
15892
|
};
|
|
15473
15893
|
try {
|
|
15474
|
-
return fs_watch(
|
|
15894
|
+
return fs_watch(path30, {
|
|
15475
15895
|
persistent: options.persistent
|
|
15476
15896
|
}, handleEvent);
|
|
15477
15897
|
} catch (error) {
|
|
@@ -15487,12 +15907,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
15487
15907
|
listener(val1, val2, val3);
|
|
15488
15908
|
});
|
|
15489
15909
|
};
|
|
15490
|
-
var setFsWatchListener = (
|
|
15910
|
+
var setFsWatchListener = (path30, fullPath, options, handlers) => {
|
|
15491
15911
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
15492
15912
|
let cont = FsWatchInstances.get(fullPath);
|
|
15493
15913
|
let watcher;
|
|
15494
15914
|
if (!options.persistent) {
|
|
15495
|
-
watcher = createFsWatchInstance(
|
|
15915
|
+
watcher = createFsWatchInstance(path30, options, listener, errHandler, rawEmitter);
|
|
15496
15916
|
if (!watcher)
|
|
15497
15917
|
return;
|
|
15498
15918
|
return watcher.close.bind(watcher);
|
|
@@ -15503,7 +15923,7 @@ var setFsWatchListener = (path28, fullPath, options, handlers) => {
|
|
|
15503
15923
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
15504
15924
|
} else {
|
|
15505
15925
|
watcher = createFsWatchInstance(
|
|
15506
|
-
|
|
15926
|
+
path30,
|
|
15507
15927
|
options,
|
|
15508
15928
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
15509
15929
|
errHandler,
|
|
@@ -15518,7 +15938,7 @@ var setFsWatchListener = (path28, fullPath, options, handlers) => {
|
|
|
15518
15938
|
cont.watcherUnusable = true;
|
|
15519
15939
|
if (isWindows && error.code === "EPERM") {
|
|
15520
15940
|
try {
|
|
15521
|
-
const fd = await open(
|
|
15941
|
+
const fd = await open(path30, "r");
|
|
15522
15942
|
await fd.close();
|
|
15523
15943
|
broadcastErr(error);
|
|
15524
15944
|
} catch (err) {
|
|
@@ -15549,7 +15969,7 @@ var setFsWatchListener = (path28, fullPath, options, handlers) => {
|
|
|
15549
15969
|
};
|
|
15550
15970
|
};
|
|
15551
15971
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
15552
|
-
var setFsWatchFileListener = (
|
|
15972
|
+
var setFsWatchFileListener = (path30, fullPath, options, handlers) => {
|
|
15553
15973
|
const { listener, rawEmitter } = handlers;
|
|
15554
15974
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
15555
15975
|
const copts = cont && cont.options;
|
|
@@ -15571,7 +15991,7 @@ var setFsWatchFileListener = (path28, fullPath, options, handlers) => {
|
|
|
15571
15991
|
});
|
|
15572
15992
|
const currmtime = curr.mtimeMs;
|
|
15573
15993
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
15574
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
15994
|
+
foreach(cont.listeners, (listener2) => listener2(path30, curr));
|
|
15575
15995
|
}
|
|
15576
15996
|
})
|
|
15577
15997
|
};
|
|
@@ -15601,13 +16021,13 @@ var NodeFsHandler = class {
|
|
|
15601
16021
|
* @param listener on fs change
|
|
15602
16022
|
* @returns closer for the watcher instance
|
|
15603
16023
|
*/
|
|
15604
|
-
_watchWithNodeFs(
|
|
16024
|
+
_watchWithNodeFs(path30, listener) {
|
|
15605
16025
|
const opts = this.fsw.options;
|
|
15606
|
-
const directory = sp.dirname(
|
|
15607
|
-
const basename9 = sp.basename(
|
|
16026
|
+
const directory = sp.dirname(path30);
|
|
16027
|
+
const basename9 = sp.basename(path30);
|
|
15608
16028
|
const parent = this.fsw._getWatchedDir(directory);
|
|
15609
16029
|
parent.add(basename9);
|
|
15610
|
-
const absolutePath = sp.resolve(
|
|
16030
|
+
const absolutePath = sp.resolve(path30);
|
|
15611
16031
|
const options = {
|
|
15612
16032
|
persistent: opts.persistent
|
|
15613
16033
|
};
|
|
@@ -15617,12 +16037,12 @@ var NodeFsHandler = class {
|
|
|
15617
16037
|
if (opts.usePolling) {
|
|
15618
16038
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
15619
16039
|
options.interval = enableBin && isBinaryPath(basename9) ? opts.binaryInterval : opts.interval;
|
|
15620
|
-
closer = setFsWatchFileListener(
|
|
16040
|
+
closer = setFsWatchFileListener(path30, absolutePath, options, {
|
|
15621
16041
|
listener,
|
|
15622
16042
|
rawEmitter: this.fsw._emitRaw
|
|
15623
16043
|
});
|
|
15624
16044
|
} else {
|
|
15625
|
-
closer = setFsWatchListener(
|
|
16045
|
+
closer = setFsWatchListener(path30, absolutePath, options, {
|
|
15626
16046
|
listener,
|
|
15627
16047
|
errHandler: this._boundHandleError,
|
|
15628
16048
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -15644,7 +16064,7 @@ var NodeFsHandler = class {
|
|
|
15644
16064
|
let prevStats = stats;
|
|
15645
16065
|
if (parent.has(basename9))
|
|
15646
16066
|
return;
|
|
15647
|
-
const listener = async (
|
|
16067
|
+
const listener = async (path30, newStats) => {
|
|
15648
16068
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
15649
16069
|
return;
|
|
15650
16070
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -15658,11 +16078,11 @@ var NodeFsHandler = class {
|
|
|
15658
16078
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
15659
16079
|
}
|
|
15660
16080
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
15661
|
-
this.fsw._closeFile(
|
|
16081
|
+
this.fsw._closeFile(path30);
|
|
15662
16082
|
prevStats = newStats2;
|
|
15663
16083
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
15664
16084
|
if (closer2)
|
|
15665
|
-
this.fsw._addPathCloser(
|
|
16085
|
+
this.fsw._addPathCloser(path30, closer2);
|
|
15666
16086
|
} else {
|
|
15667
16087
|
prevStats = newStats2;
|
|
15668
16088
|
}
|
|
@@ -15694,7 +16114,7 @@ var NodeFsHandler = class {
|
|
|
15694
16114
|
* @param item basename of this item
|
|
15695
16115
|
* @returns true if no more processing is needed for this entry.
|
|
15696
16116
|
*/
|
|
15697
|
-
async _handleSymlink(entry, directory,
|
|
16117
|
+
async _handleSymlink(entry, directory, path30, item) {
|
|
15698
16118
|
if (this.fsw.closed) {
|
|
15699
16119
|
return;
|
|
15700
16120
|
}
|
|
@@ -15704,7 +16124,7 @@ var NodeFsHandler = class {
|
|
|
15704
16124
|
this.fsw._incrReadyCount();
|
|
15705
16125
|
let linkPath;
|
|
15706
16126
|
try {
|
|
15707
|
-
linkPath = await fsrealpath(
|
|
16127
|
+
linkPath = await fsrealpath(path30);
|
|
15708
16128
|
} catch (e) {
|
|
15709
16129
|
this.fsw._emitReady();
|
|
15710
16130
|
return true;
|
|
@@ -15714,12 +16134,12 @@ var NodeFsHandler = class {
|
|
|
15714
16134
|
if (dir.has(item)) {
|
|
15715
16135
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
15716
16136
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
15717
|
-
this.fsw._emit(EV.CHANGE,
|
|
16137
|
+
this.fsw._emit(EV.CHANGE, path30, entry.stats);
|
|
15718
16138
|
}
|
|
15719
16139
|
} else {
|
|
15720
16140
|
dir.add(item);
|
|
15721
16141
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
15722
|
-
this.fsw._emit(EV.ADD,
|
|
16142
|
+
this.fsw._emit(EV.ADD, path30, entry.stats);
|
|
15723
16143
|
}
|
|
15724
16144
|
this.fsw._emitReady();
|
|
15725
16145
|
return true;
|
|
@@ -15749,9 +16169,9 @@ var NodeFsHandler = class {
|
|
|
15749
16169
|
return;
|
|
15750
16170
|
}
|
|
15751
16171
|
const item = entry.path;
|
|
15752
|
-
let
|
|
16172
|
+
let path30 = sp.join(directory, item);
|
|
15753
16173
|
current.add(item);
|
|
15754
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
16174
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path30, item)) {
|
|
15755
16175
|
return;
|
|
15756
16176
|
}
|
|
15757
16177
|
if (this.fsw.closed) {
|
|
@@ -15760,11 +16180,11 @@ var NodeFsHandler = class {
|
|
|
15760
16180
|
}
|
|
15761
16181
|
if (item === target || !target && !previous.has(item)) {
|
|
15762
16182
|
this.fsw._incrReadyCount();
|
|
15763
|
-
|
|
15764
|
-
this._addToNodeFs(
|
|
16183
|
+
path30 = sp.join(dir, sp.relative(dir, path30));
|
|
16184
|
+
this._addToNodeFs(path30, initialAdd, wh, depth + 1);
|
|
15765
16185
|
}
|
|
15766
16186
|
}).on(EV.ERROR, this._boundHandleError);
|
|
15767
|
-
return new Promise((
|
|
16187
|
+
return new Promise((resolve17, reject) => {
|
|
15768
16188
|
if (!stream)
|
|
15769
16189
|
return reject();
|
|
15770
16190
|
stream.once(STR_END, () => {
|
|
@@ -15773,7 +16193,7 @@ var NodeFsHandler = class {
|
|
|
15773
16193
|
return;
|
|
15774
16194
|
}
|
|
15775
16195
|
const wasThrottled = throttler ? throttler.clear() : false;
|
|
15776
|
-
|
|
16196
|
+
resolve17(void 0);
|
|
15777
16197
|
previous.getChildren().filter((item) => {
|
|
15778
16198
|
return item !== directory && !current.has(item);
|
|
15779
16199
|
}).forEach((item) => {
|
|
@@ -15830,13 +16250,13 @@ var NodeFsHandler = class {
|
|
|
15830
16250
|
* @param depth Child path actually targeted for watch
|
|
15831
16251
|
* @param target Child path actually targeted for watch
|
|
15832
16252
|
*/
|
|
15833
|
-
async _addToNodeFs(
|
|
16253
|
+
async _addToNodeFs(path30, initialAdd, priorWh, depth, target) {
|
|
15834
16254
|
const ready = this.fsw._emitReady;
|
|
15835
|
-
if (this.fsw._isIgnored(
|
|
16255
|
+
if (this.fsw._isIgnored(path30) || this.fsw.closed) {
|
|
15836
16256
|
ready();
|
|
15837
16257
|
return false;
|
|
15838
16258
|
}
|
|
15839
|
-
const wh = this.fsw._getWatchHelpers(
|
|
16259
|
+
const wh = this.fsw._getWatchHelpers(path30);
|
|
15840
16260
|
if (priorWh) {
|
|
15841
16261
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
15842
16262
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -15852,8 +16272,8 @@ var NodeFsHandler = class {
|
|
|
15852
16272
|
const follow = this.fsw.options.followSymlinks;
|
|
15853
16273
|
let closer;
|
|
15854
16274
|
if (stats.isDirectory()) {
|
|
15855
|
-
const absPath = sp.resolve(
|
|
15856
|
-
const targetPath = follow ? await fsrealpath(
|
|
16275
|
+
const absPath = sp.resolve(path30);
|
|
16276
|
+
const targetPath = follow ? await fsrealpath(path30) : path30;
|
|
15857
16277
|
if (this.fsw.closed)
|
|
15858
16278
|
return;
|
|
15859
16279
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -15863,29 +16283,29 @@ var NodeFsHandler = class {
|
|
|
15863
16283
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
15864
16284
|
}
|
|
15865
16285
|
} else if (stats.isSymbolicLink()) {
|
|
15866
|
-
const targetPath = follow ? await fsrealpath(
|
|
16286
|
+
const targetPath = follow ? await fsrealpath(path30) : path30;
|
|
15867
16287
|
if (this.fsw.closed)
|
|
15868
16288
|
return;
|
|
15869
16289
|
const parent = sp.dirname(wh.watchPath);
|
|
15870
16290
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
15871
16291
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
15872
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
16292
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path30, wh, targetPath);
|
|
15873
16293
|
if (this.fsw.closed)
|
|
15874
16294
|
return;
|
|
15875
16295
|
if (targetPath !== void 0) {
|
|
15876
|
-
this.fsw._symlinkPaths.set(sp.resolve(
|
|
16296
|
+
this.fsw._symlinkPaths.set(sp.resolve(path30), targetPath);
|
|
15877
16297
|
}
|
|
15878
16298
|
} else {
|
|
15879
16299
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
15880
16300
|
}
|
|
15881
16301
|
ready();
|
|
15882
16302
|
if (closer)
|
|
15883
|
-
this.fsw._addPathCloser(
|
|
16303
|
+
this.fsw._addPathCloser(path30, closer);
|
|
15884
16304
|
return false;
|
|
15885
16305
|
} catch (error) {
|
|
15886
16306
|
if (this.fsw._handleError(error)) {
|
|
15887
16307
|
ready();
|
|
15888
|
-
return
|
|
16308
|
+
return path30;
|
|
15889
16309
|
}
|
|
15890
16310
|
}
|
|
15891
16311
|
}
|
|
@@ -15917,35 +16337,35 @@ function createPattern(matcher) {
|
|
|
15917
16337
|
if (matcher.path === string)
|
|
15918
16338
|
return true;
|
|
15919
16339
|
if (matcher.recursive) {
|
|
15920
|
-
const
|
|
15921
|
-
if (!
|
|
16340
|
+
const relative14 = sp2.relative(matcher.path, string);
|
|
16341
|
+
if (!relative14) {
|
|
15922
16342
|
return false;
|
|
15923
16343
|
}
|
|
15924
|
-
return !
|
|
16344
|
+
return !relative14.startsWith("..") && !sp2.isAbsolute(relative14);
|
|
15925
16345
|
}
|
|
15926
16346
|
return false;
|
|
15927
16347
|
};
|
|
15928
16348
|
}
|
|
15929
16349
|
return () => false;
|
|
15930
16350
|
}
|
|
15931
|
-
function normalizePath2(
|
|
15932
|
-
if (typeof
|
|
16351
|
+
function normalizePath2(path30) {
|
|
16352
|
+
if (typeof path30 !== "string")
|
|
15933
16353
|
throw new Error("string expected");
|
|
15934
|
-
|
|
15935
|
-
|
|
16354
|
+
path30 = sp2.normalize(path30);
|
|
16355
|
+
path30 = path30.replace(/\\/g, "/");
|
|
15936
16356
|
let prepend = false;
|
|
15937
|
-
if (
|
|
16357
|
+
if (path30.startsWith("//"))
|
|
15938
16358
|
prepend = true;
|
|
15939
|
-
|
|
16359
|
+
path30 = path30.replace(DOUBLE_SLASH_RE, "/");
|
|
15940
16360
|
if (prepend)
|
|
15941
|
-
|
|
15942
|
-
return
|
|
16361
|
+
path30 = "/" + path30;
|
|
16362
|
+
return path30;
|
|
15943
16363
|
}
|
|
15944
16364
|
function matchPatterns(patterns, testString, stats) {
|
|
15945
|
-
const
|
|
16365
|
+
const path30 = normalizePath2(testString);
|
|
15946
16366
|
for (let index = 0; index < patterns.length; index++) {
|
|
15947
16367
|
const pattern = patterns[index];
|
|
15948
|
-
if (pattern(
|
|
16368
|
+
if (pattern(path30, stats)) {
|
|
15949
16369
|
return true;
|
|
15950
16370
|
}
|
|
15951
16371
|
}
|
|
@@ -15983,19 +16403,19 @@ var toUnix = (string) => {
|
|
|
15983
16403
|
}
|
|
15984
16404
|
return str;
|
|
15985
16405
|
};
|
|
15986
|
-
var normalizePathToUnix = (
|
|
15987
|
-
var normalizeIgnored = (cwd = "") => (
|
|
15988
|
-
if (typeof
|
|
15989
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
16406
|
+
var normalizePathToUnix = (path30) => toUnix(sp2.normalize(toUnix(path30)));
|
|
16407
|
+
var normalizeIgnored = (cwd = "") => (path30) => {
|
|
16408
|
+
if (typeof path30 === "string") {
|
|
16409
|
+
return normalizePathToUnix(sp2.isAbsolute(path30) ? path30 : sp2.join(cwd, path30));
|
|
15990
16410
|
} else {
|
|
15991
|
-
return
|
|
16411
|
+
return path30;
|
|
15992
16412
|
}
|
|
15993
16413
|
};
|
|
15994
|
-
var getAbsolutePath = (
|
|
15995
|
-
if (sp2.isAbsolute(
|
|
15996
|
-
return
|
|
16414
|
+
var getAbsolutePath = (path30, cwd) => {
|
|
16415
|
+
if (sp2.isAbsolute(path30)) {
|
|
16416
|
+
return path30;
|
|
15997
16417
|
}
|
|
15998
|
-
return sp2.join(cwd,
|
|
16418
|
+
return sp2.join(cwd, path30);
|
|
15999
16419
|
};
|
|
16000
16420
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
16001
16421
|
var DirEntry = class {
|
|
@@ -16060,10 +16480,10 @@ var WatchHelper = class {
|
|
|
16060
16480
|
dirParts;
|
|
16061
16481
|
followSymlinks;
|
|
16062
16482
|
statMethod;
|
|
16063
|
-
constructor(
|
|
16483
|
+
constructor(path30, follow, fsw) {
|
|
16064
16484
|
this.fsw = fsw;
|
|
16065
|
-
const watchPath =
|
|
16066
|
-
this.path =
|
|
16485
|
+
const watchPath = path30;
|
|
16486
|
+
this.path = path30 = path30.replace(REPLACER_RE, "");
|
|
16067
16487
|
this.watchPath = watchPath;
|
|
16068
16488
|
this.fullWatchPath = sp2.resolve(watchPath);
|
|
16069
16489
|
this.dirParts = [];
|
|
@@ -16203,20 +16623,20 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
16203
16623
|
this._closePromise = void 0;
|
|
16204
16624
|
let paths = unifyPaths(paths_);
|
|
16205
16625
|
if (cwd) {
|
|
16206
|
-
paths = paths.map((
|
|
16207
|
-
const absPath = getAbsolutePath(
|
|
16626
|
+
paths = paths.map((path30) => {
|
|
16627
|
+
const absPath = getAbsolutePath(path30, cwd);
|
|
16208
16628
|
return absPath;
|
|
16209
16629
|
});
|
|
16210
16630
|
}
|
|
16211
|
-
paths.forEach((
|
|
16212
|
-
this._removeIgnoredPath(
|
|
16631
|
+
paths.forEach((path30) => {
|
|
16632
|
+
this._removeIgnoredPath(path30);
|
|
16213
16633
|
});
|
|
16214
16634
|
this._userIgnored = void 0;
|
|
16215
16635
|
if (!this._readyCount)
|
|
16216
16636
|
this._readyCount = 0;
|
|
16217
16637
|
this._readyCount += paths.length;
|
|
16218
|
-
Promise.all(paths.map(async (
|
|
16219
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
16638
|
+
Promise.all(paths.map(async (path30) => {
|
|
16639
|
+
const res = await this._nodeFsHandler._addToNodeFs(path30, !_internal, void 0, 0, _origAdd);
|
|
16220
16640
|
if (res)
|
|
16221
16641
|
this._emitReady();
|
|
16222
16642
|
return res;
|
|
@@ -16238,17 +16658,17 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
16238
16658
|
return this;
|
|
16239
16659
|
const paths = unifyPaths(paths_);
|
|
16240
16660
|
const { cwd } = this.options;
|
|
16241
|
-
paths.forEach((
|
|
16242
|
-
if (!sp2.isAbsolute(
|
|
16661
|
+
paths.forEach((path30) => {
|
|
16662
|
+
if (!sp2.isAbsolute(path30) && !this._closers.has(path30)) {
|
|
16243
16663
|
if (cwd)
|
|
16244
|
-
|
|
16245
|
-
|
|
16664
|
+
path30 = sp2.join(cwd, path30);
|
|
16665
|
+
path30 = sp2.resolve(path30);
|
|
16246
16666
|
}
|
|
16247
|
-
this._closePath(
|
|
16248
|
-
this._addIgnoredPath(
|
|
16249
|
-
if (this._watched.has(
|
|
16667
|
+
this._closePath(path30);
|
|
16668
|
+
this._addIgnoredPath(path30);
|
|
16669
|
+
if (this._watched.has(path30)) {
|
|
16250
16670
|
this._addIgnoredPath({
|
|
16251
|
-
path:
|
|
16671
|
+
path: path30,
|
|
16252
16672
|
recursive: true
|
|
16253
16673
|
});
|
|
16254
16674
|
}
|
|
@@ -16312,38 +16732,38 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
16312
16732
|
* @param stats arguments to be passed with event
|
|
16313
16733
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
16314
16734
|
*/
|
|
16315
|
-
async _emit(event,
|
|
16735
|
+
async _emit(event, path30, stats) {
|
|
16316
16736
|
if (this.closed)
|
|
16317
16737
|
return;
|
|
16318
16738
|
const opts = this.options;
|
|
16319
16739
|
if (isWindows)
|
|
16320
|
-
|
|
16740
|
+
path30 = sp2.normalize(path30);
|
|
16321
16741
|
if (opts.cwd)
|
|
16322
|
-
|
|
16323
|
-
const args = [
|
|
16742
|
+
path30 = sp2.relative(opts.cwd, path30);
|
|
16743
|
+
const args = [path30];
|
|
16324
16744
|
if (stats != null)
|
|
16325
16745
|
args.push(stats);
|
|
16326
16746
|
const awf = opts.awaitWriteFinish;
|
|
16327
16747
|
let pw;
|
|
16328
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
16748
|
+
if (awf && (pw = this._pendingWrites.get(path30))) {
|
|
16329
16749
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
16330
16750
|
return this;
|
|
16331
16751
|
}
|
|
16332
16752
|
if (opts.atomic) {
|
|
16333
16753
|
if (event === EVENTS.UNLINK) {
|
|
16334
|
-
this._pendingUnlinks.set(
|
|
16754
|
+
this._pendingUnlinks.set(path30, [event, ...args]);
|
|
16335
16755
|
setTimeout(() => {
|
|
16336
|
-
this._pendingUnlinks.forEach((entry,
|
|
16756
|
+
this._pendingUnlinks.forEach((entry, path31) => {
|
|
16337
16757
|
this.emit(...entry);
|
|
16338
16758
|
this.emit(EVENTS.ALL, ...entry);
|
|
16339
|
-
this._pendingUnlinks.delete(
|
|
16759
|
+
this._pendingUnlinks.delete(path31);
|
|
16340
16760
|
});
|
|
16341
16761
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
16342
16762
|
return this;
|
|
16343
16763
|
}
|
|
16344
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
16764
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path30)) {
|
|
16345
16765
|
event = EVENTS.CHANGE;
|
|
16346
|
-
this._pendingUnlinks.delete(
|
|
16766
|
+
this._pendingUnlinks.delete(path30);
|
|
16347
16767
|
}
|
|
16348
16768
|
}
|
|
16349
16769
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -16361,16 +16781,16 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
16361
16781
|
this.emitWithAll(event, args);
|
|
16362
16782
|
}
|
|
16363
16783
|
};
|
|
16364
|
-
this._awaitWriteFinish(
|
|
16784
|
+
this._awaitWriteFinish(path30, awf.stabilityThreshold, event, awfEmit);
|
|
16365
16785
|
return this;
|
|
16366
16786
|
}
|
|
16367
16787
|
if (event === EVENTS.CHANGE) {
|
|
16368
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
16788
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path30, 50);
|
|
16369
16789
|
if (isThrottled)
|
|
16370
16790
|
return this;
|
|
16371
16791
|
}
|
|
16372
16792
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
16373
|
-
const fullPath = opts.cwd ? sp2.join(opts.cwd,
|
|
16793
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path30) : path30;
|
|
16374
16794
|
let stats2;
|
|
16375
16795
|
try {
|
|
16376
16796
|
stats2 = await stat3(fullPath);
|
|
@@ -16401,23 +16821,23 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
16401
16821
|
* @param timeout duration of time to suppress duplicate actions
|
|
16402
16822
|
* @returns tracking object or false if action should be suppressed
|
|
16403
16823
|
*/
|
|
16404
|
-
_throttle(actionType,
|
|
16824
|
+
_throttle(actionType, path30, timeout) {
|
|
16405
16825
|
if (!this._throttled.has(actionType)) {
|
|
16406
16826
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
16407
16827
|
}
|
|
16408
16828
|
const action = this._throttled.get(actionType);
|
|
16409
16829
|
if (!action)
|
|
16410
16830
|
throw new Error("invalid throttle");
|
|
16411
|
-
const actionPath = action.get(
|
|
16831
|
+
const actionPath = action.get(path30);
|
|
16412
16832
|
if (actionPath) {
|
|
16413
16833
|
actionPath.count++;
|
|
16414
16834
|
return false;
|
|
16415
16835
|
}
|
|
16416
16836
|
let timeoutObject;
|
|
16417
16837
|
const clear = () => {
|
|
16418
|
-
const item = action.get(
|
|
16838
|
+
const item = action.get(path30);
|
|
16419
16839
|
const count = item ? item.count : 0;
|
|
16420
|
-
action.delete(
|
|
16840
|
+
action.delete(path30);
|
|
16421
16841
|
clearTimeout(timeoutObject);
|
|
16422
16842
|
if (item)
|
|
16423
16843
|
clearTimeout(item.timeoutObject);
|
|
@@ -16425,7 +16845,7 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
16425
16845
|
};
|
|
16426
16846
|
timeoutObject = setTimeout(clear, timeout);
|
|
16427
16847
|
const thr = { timeoutObject, clear, count: 0 };
|
|
16428
|
-
action.set(
|
|
16848
|
+
action.set(path30, thr);
|
|
16429
16849
|
return thr;
|
|
16430
16850
|
}
|
|
16431
16851
|
_incrReadyCount() {
|
|
@@ -16439,44 +16859,44 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
16439
16859
|
* @param event
|
|
16440
16860
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
16441
16861
|
*/
|
|
16442
|
-
_awaitWriteFinish(
|
|
16862
|
+
_awaitWriteFinish(path30, threshold, event, awfEmit) {
|
|
16443
16863
|
const awf = this.options.awaitWriteFinish;
|
|
16444
16864
|
if (typeof awf !== "object")
|
|
16445
16865
|
return;
|
|
16446
16866
|
const pollInterval = awf.pollInterval;
|
|
16447
16867
|
let timeoutHandler;
|
|
16448
|
-
let fullPath =
|
|
16449
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
16450
|
-
fullPath = sp2.join(this.options.cwd,
|
|
16868
|
+
let fullPath = path30;
|
|
16869
|
+
if (this.options.cwd && !sp2.isAbsolute(path30)) {
|
|
16870
|
+
fullPath = sp2.join(this.options.cwd, path30);
|
|
16451
16871
|
}
|
|
16452
16872
|
const now2 = /* @__PURE__ */ new Date();
|
|
16453
16873
|
const writes = this._pendingWrites;
|
|
16454
16874
|
function awaitWriteFinishFn(prevStat) {
|
|
16455
16875
|
statcb(fullPath, (err, curStat) => {
|
|
16456
|
-
if (err || !writes.has(
|
|
16876
|
+
if (err || !writes.has(path30)) {
|
|
16457
16877
|
if (err && err.code !== "ENOENT")
|
|
16458
16878
|
awfEmit(err);
|
|
16459
16879
|
return;
|
|
16460
16880
|
}
|
|
16461
16881
|
const now3 = Number(/* @__PURE__ */ new Date());
|
|
16462
16882
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
16463
|
-
writes.get(
|
|
16883
|
+
writes.get(path30).lastChange = now3;
|
|
16464
16884
|
}
|
|
16465
|
-
const pw = writes.get(
|
|
16885
|
+
const pw = writes.get(path30);
|
|
16466
16886
|
const df = now3 - pw.lastChange;
|
|
16467
16887
|
if (df >= threshold) {
|
|
16468
|
-
writes.delete(
|
|
16888
|
+
writes.delete(path30);
|
|
16469
16889
|
awfEmit(void 0, curStat);
|
|
16470
16890
|
} else {
|
|
16471
16891
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
16472
16892
|
}
|
|
16473
16893
|
});
|
|
16474
16894
|
}
|
|
16475
|
-
if (!writes.has(
|
|
16476
|
-
writes.set(
|
|
16895
|
+
if (!writes.has(path30)) {
|
|
16896
|
+
writes.set(path30, {
|
|
16477
16897
|
lastChange: now2,
|
|
16478
16898
|
cancelWait: () => {
|
|
16479
|
-
writes.delete(
|
|
16899
|
+
writes.delete(path30);
|
|
16480
16900
|
clearTimeout(timeoutHandler);
|
|
16481
16901
|
return event;
|
|
16482
16902
|
}
|
|
@@ -16487,8 +16907,8 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
16487
16907
|
/**
|
|
16488
16908
|
* Determines whether user has asked to ignore this path.
|
|
16489
16909
|
*/
|
|
16490
|
-
_isIgnored(
|
|
16491
|
-
if (this.options.atomic && DOT_RE.test(
|
|
16910
|
+
_isIgnored(path30, stats) {
|
|
16911
|
+
if (this.options.atomic && DOT_RE.test(path30))
|
|
16492
16912
|
return true;
|
|
16493
16913
|
if (!this._userIgnored) {
|
|
16494
16914
|
const { cwd } = this.options;
|
|
@@ -16498,17 +16918,17 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
16498
16918
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
16499
16919
|
this._userIgnored = anymatch(list, void 0);
|
|
16500
16920
|
}
|
|
16501
|
-
return this._userIgnored(
|
|
16921
|
+
return this._userIgnored(path30, stats);
|
|
16502
16922
|
}
|
|
16503
|
-
_isntIgnored(
|
|
16504
|
-
return !this._isIgnored(
|
|
16923
|
+
_isntIgnored(path30, stat5) {
|
|
16924
|
+
return !this._isIgnored(path30, stat5);
|
|
16505
16925
|
}
|
|
16506
16926
|
/**
|
|
16507
16927
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
16508
16928
|
* @param path file or directory pattern being watched
|
|
16509
16929
|
*/
|
|
16510
|
-
_getWatchHelpers(
|
|
16511
|
-
return new WatchHelper(
|
|
16930
|
+
_getWatchHelpers(path30) {
|
|
16931
|
+
return new WatchHelper(path30, this.options.followSymlinks, this);
|
|
16512
16932
|
}
|
|
16513
16933
|
// Directory helpers
|
|
16514
16934
|
// -----------------
|
|
@@ -16540,63 +16960,63 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
16540
16960
|
* @param item base path of item/directory
|
|
16541
16961
|
*/
|
|
16542
16962
|
_remove(directory, item, isDirectory) {
|
|
16543
|
-
const
|
|
16544
|
-
const fullPath = sp2.resolve(
|
|
16545
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
16546
|
-
if (!this._throttle("remove",
|
|
16963
|
+
const path30 = sp2.join(directory, item);
|
|
16964
|
+
const fullPath = sp2.resolve(path30);
|
|
16965
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path30) || this._watched.has(fullPath);
|
|
16966
|
+
if (!this._throttle("remove", path30, 100))
|
|
16547
16967
|
return;
|
|
16548
16968
|
if (!isDirectory && this._watched.size === 1) {
|
|
16549
16969
|
this.add(directory, item, true);
|
|
16550
16970
|
}
|
|
16551
|
-
const wp = this._getWatchedDir(
|
|
16971
|
+
const wp = this._getWatchedDir(path30);
|
|
16552
16972
|
const nestedDirectoryChildren = wp.getChildren();
|
|
16553
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
16973
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path30, nested));
|
|
16554
16974
|
const parent = this._getWatchedDir(directory);
|
|
16555
16975
|
const wasTracked = parent.has(item);
|
|
16556
16976
|
parent.remove(item);
|
|
16557
16977
|
if (this._symlinkPaths.has(fullPath)) {
|
|
16558
16978
|
this._symlinkPaths.delete(fullPath);
|
|
16559
16979
|
}
|
|
16560
|
-
let relPath =
|
|
16980
|
+
let relPath = path30;
|
|
16561
16981
|
if (this.options.cwd)
|
|
16562
|
-
relPath = sp2.relative(this.options.cwd,
|
|
16982
|
+
relPath = sp2.relative(this.options.cwd, path30);
|
|
16563
16983
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
16564
16984
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
16565
16985
|
if (event === EVENTS.ADD)
|
|
16566
16986
|
return;
|
|
16567
16987
|
}
|
|
16568
|
-
this._watched.delete(
|
|
16988
|
+
this._watched.delete(path30);
|
|
16569
16989
|
this._watched.delete(fullPath);
|
|
16570
16990
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
16571
|
-
if (wasTracked && !this._isIgnored(
|
|
16572
|
-
this._emit(eventName,
|
|
16573
|
-
this._closePath(
|
|
16991
|
+
if (wasTracked && !this._isIgnored(path30))
|
|
16992
|
+
this._emit(eventName, path30);
|
|
16993
|
+
this._closePath(path30);
|
|
16574
16994
|
}
|
|
16575
16995
|
/**
|
|
16576
16996
|
* Closes all watchers for a path
|
|
16577
16997
|
*/
|
|
16578
|
-
_closePath(
|
|
16579
|
-
this._closeFile(
|
|
16580
|
-
const dir = sp2.dirname(
|
|
16581
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
16998
|
+
_closePath(path30) {
|
|
16999
|
+
this._closeFile(path30);
|
|
17000
|
+
const dir = sp2.dirname(path30);
|
|
17001
|
+
this._getWatchedDir(dir).remove(sp2.basename(path30));
|
|
16582
17002
|
}
|
|
16583
17003
|
/**
|
|
16584
17004
|
* Closes only file-specific watchers
|
|
16585
17005
|
*/
|
|
16586
|
-
_closeFile(
|
|
16587
|
-
const closers = this._closers.get(
|
|
17006
|
+
_closeFile(path30) {
|
|
17007
|
+
const closers = this._closers.get(path30);
|
|
16588
17008
|
if (!closers)
|
|
16589
17009
|
return;
|
|
16590
17010
|
closers.forEach((closer) => closer());
|
|
16591
|
-
this._closers.delete(
|
|
17011
|
+
this._closers.delete(path30);
|
|
16592
17012
|
}
|
|
16593
|
-
_addPathCloser(
|
|
17013
|
+
_addPathCloser(path30, closer) {
|
|
16594
17014
|
if (!closer)
|
|
16595
17015
|
return;
|
|
16596
|
-
let list = this._closers.get(
|
|
17016
|
+
let list = this._closers.get(path30);
|
|
16597
17017
|
if (!list) {
|
|
16598
17018
|
list = [];
|
|
16599
|
-
this._closers.set(
|
|
17019
|
+
this._closers.set(path30, list);
|
|
16600
17020
|
}
|
|
16601
17021
|
list.push(closer);
|
|
16602
17022
|
}
|
|
@@ -16626,12 +17046,291 @@ function watch(paths, options = {}) {
|
|
|
16626
17046
|
var chokidar_default = { watch, FSWatcher };
|
|
16627
17047
|
|
|
16628
17048
|
// src/watcher/file-watcher.ts
|
|
17049
|
+
import * as path23 from "path";
|
|
17050
|
+
|
|
17051
|
+
// src/watcher/native-recursive-watcher.ts
|
|
17052
|
+
import { watch as watch2 } from "fs";
|
|
16629
17053
|
import * as path21 from "path";
|
|
17054
|
+
var NativeRecursiveWatcher = class {
|
|
17055
|
+
constructor(root, onChange, options = {}) {
|
|
17056
|
+
this.root = root;
|
|
17057
|
+
this.onChange = onChange;
|
|
17058
|
+
this.watchFactory = options.watchFactory ?? this.defaultWatchFactory;
|
|
17059
|
+
this.onError = options.onError;
|
|
17060
|
+
}
|
|
17061
|
+
root;
|
|
17062
|
+
onChange;
|
|
17063
|
+
watcher = null;
|
|
17064
|
+
listenerToken = 0;
|
|
17065
|
+
watchFactory;
|
|
17066
|
+
onError;
|
|
17067
|
+
start() {
|
|
17068
|
+
if (this.watcher) return;
|
|
17069
|
+
const token = ++this.listenerToken;
|
|
17070
|
+
const listener = (_eventType, filename) => {
|
|
17071
|
+
if (this.watcher === null || this.listenerToken !== token) return;
|
|
17072
|
+
const absolutePath = this.toAbsolutePath(filename);
|
|
17073
|
+
const nextResult = this.onChange(absolutePath);
|
|
17074
|
+
if (nextResult instanceof Promise) {
|
|
17075
|
+
void nextResult.catch((error) => {
|
|
17076
|
+
console.error("[codebase-index] Error handling native watcher event:", error);
|
|
17077
|
+
});
|
|
17078
|
+
}
|
|
17079
|
+
};
|
|
17080
|
+
const watcher = this.watchFactory(this.root, listener, {
|
|
17081
|
+
persistent: true,
|
|
17082
|
+
recursive: true
|
|
17083
|
+
});
|
|
17084
|
+
watcher.on?.("error", (error) => {
|
|
17085
|
+
if (this.watcher === watcher && this.listenerToken === token) {
|
|
17086
|
+
this.onError?.(error);
|
|
17087
|
+
}
|
|
17088
|
+
});
|
|
17089
|
+
this.watcher = watcher;
|
|
17090
|
+
}
|
|
17091
|
+
async stop() {
|
|
17092
|
+
const watcher = this.watcher;
|
|
17093
|
+
this.watcher = null;
|
|
17094
|
+
this.listenerToken += 1;
|
|
17095
|
+
if (!watcher) return;
|
|
17096
|
+
await watcher.close();
|
|
17097
|
+
}
|
|
17098
|
+
toAbsolutePath(filename) {
|
|
17099
|
+
if (filename == null) return null;
|
|
17100
|
+
const normalizedFilename = typeof filename === "string" ? filename : filename.toString();
|
|
17101
|
+
const absolutePath = path21.resolve(this.root, normalizedFilename);
|
|
17102
|
+
const relativePath = path21.relative(this.root, absolutePath);
|
|
17103
|
+
const outsideRoot = relativePath === ".." || relativePath.startsWith(`..${path21.sep}`) || path21.isAbsolute(relativePath);
|
|
17104
|
+
return outsideRoot ? null : absolutePath;
|
|
17105
|
+
}
|
|
17106
|
+
defaultWatchFactory = (root, listener, options) => watch2(root, options, listener);
|
|
17107
|
+
};
|
|
17108
|
+
|
|
17109
|
+
// src/watcher/snapshot.ts
|
|
17110
|
+
import * as fsPromises4 from "fs/promises";
|
|
17111
|
+
import * as path22 from "path";
|
|
17112
|
+
async function buildFileSnapshotScan(projectRoot, config, configPaths = []) {
|
|
17113
|
+
const normalizedProjectRoot = path22.resolve(projectRoot);
|
|
17114
|
+
const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
|
|
17115
|
+
const includePatterns = [...config.include, ...config.additionalInclude ?? []];
|
|
17116
|
+
const maxDepth = config.indexing?.maxDepth ?? -1;
|
|
17117
|
+
const snapshot = /* @__PURE__ */ new Map();
|
|
17118
|
+
const unreadablePrefixes = /* @__PURE__ */ new Set();
|
|
17119
|
+
const includeFile = async (filePath) => {
|
|
17120
|
+
const normalizedPath2 = path22.resolve(filePath);
|
|
17121
|
+
if (!shouldIncludeFile(normalizedPath2, normalizedProjectRoot, includePatterns, config.exclude, ignoreFilter)) return;
|
|
17122
|
+
const stat5 = await readStatIfFile(normalizedPath2, unreadablePrefixes);
|
|
17123
|
+
if (stat5) snapshot.set(normalizedPath2, { size: stat5.size, mtimeMs: stat5.mtimeMs });
|
|
17124
|
+
};
|
|
17125
|
+
const walk = async (directoryPath, depth) => {
|
|
17126
|
+
let entries;
|
|
17127
|
+
try {
|
|
17128
|
+
entries = await fsPromises4.readdir(directoryPath, { withFileTypes: true });
|
|
17129
|
+
} catch (error) {
|
|
17130
|
+
if (isMissingFsError(error)) return;
|
|
17131
|
+
if (isPermissionFsError(error)) {
|
|
17132
|
+
unreadablePrefixes.add(path22.resolve(directoryPath));
|
|
17133
|
+
return;
|
|
17134
|
+
}
|
|
17135
|
+
throw error;
|
|
17136
|
+
}
|
|
17137
|
+
for (const entry of entries) {
|
|
17138
|
+
const fullPath = path22.join(directoryPath, entry.name);
|
|
17139
|
+
const relativePath = path22.relative(normalizedProjectRoot, fullPath);
|
|
17140
|
+
if (entry.isDirectory()) {
|
|
17141
|
+
if (hasFilteredPathSegment(relativePath, path22.sep) || isRestrictedDirectory(relativePath, path22.sep)) continue;
|
|
17142
|
+
if (ignoreFilter.ignores(relativePath)) continue;
|
|
17143
|
+
if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
|
|
17144
|
+
} else if (entry.isFile()) {
|
|
17145
|
+
await includeFile(fullPath);
|
|
17146
|
+
}
|
|
17147
|
+
}
|
|
17148
|
+
};
|
|
17149
|
+
await walk(normalizedProjectRoot, 0);
|
|
17150
|
+
await includeExplicitConfigPaths(snapshot, unreadablePrefixes, configPaths);
|
|
17151
|
+
return { entries: snapshot, unreadablePrefixes };
|
|
17152
|
+
}
|
|
17153
|
+
async function buildFileSnapshotForPathScan(projectRoot, config, configPaths, targetPath) {
|
|
17154
|
+
const normalizedProjectRoot = path22.resolve(projectRoot);
|
|
17155
|
+
const normalizedTargetPath = path22.resolve(targetPath);
|
|
17156
|
+
if (!isWithinPath(normalizedProjectRoot, normalizedTargetPath)) {
|
|
17157
|
+
return { entries: /* @__PURE__ */ new Map(), unreadablePrefixes: /* @__PURE__ */ new Set() };
|
|
17158
|
+
}
|
|
17159
|
+
const ignoreFilter = createIgnoreFilter(normalizedProjectRoot);
|
|
17160
|
+
const includePatterns = [...config.include, ...config.additionalInclude ?? []];
|
|
17161
|
+
const maxDepth = config.indexing?.maxDepth ?? -1;
|
|
17162
|
+
const explicitConfigPaths = new Set(configPaths.map((configPath) => path22.resolve(configPath)));
|
|
17163
|
+
const snapshot = /* @__PURE__ */ new Map();
|
|
17164
|
+
const unreadablePrefixes = /* @__PURE__ */ new Set();
|
|
17165
|
+
const includeFile = async (filePath) => {
|
|
17166
|
+
const normalizedPath2 = path22.resolve(filePath);
|
|
17167
|
+
if (!explicitConfigPaths.has(normalizedPath2) && !shouldIncludeFile(
|
|
17168
|
+
normalizedPath2,
|
|
17169
|
+
normalizedProjectRoot,
|
|
17170
|
+
includePatterns,
|
|
17171
|
+
config.exclude,
|
|
17172
|
+
ignoreFilter
|
|
17173
|
+
)) return;
|
|
17174
|
+
const stat5 = await readStatIfFile(normalizedPath2, unreadablePrefixes);
|
|
17175
|
+
if (stat5) snapshot.set(normalizedPath2, { size: stat5.size, mtimeMs: stat5.mtimeMs });
|
|
17176
|
+
};
|
|
17177
|
+
const walk = async (directoryPath, depth) => {
|
|
17178
|
+
let entries;
|
|
17179
|
+
try {
|
|
17180
|
+
entries = await fsPromises4.readdir(directoryPath, { withFileTypes: true });
|
|
17181
|
+
} catch (error) {
|
|
17182
|
+
if (isMissingFsError(error)) return;
|
|
17183
|
+
if (isPermissionFsError(error)) {
|
|
17184
|
+
unreadablePrefixes.add(path22.resolve(directoryPath));
|
|
17185
|
+
return;
|
|
17186
|
+
}
|
|
17187
|
+
throw error;
|
|
17188
|
+
}
|
|
17189
|
+
for (const entry of entries) {
|
|
17190
|
+
const fullPath = path22.join(directoryPath, entry.name);
|
|
17191
|
+
const relativePath = path22.relative(normalizedProjectRoot, fullPath);
|
|
17192
|
+
if (entry.isDirectory()) {
|
|
17193
|
+
if (hasFilteredPathSegment(relativePath, path22.sep) || isRestrictedDirectory(relativePath, path22.sep)) continue;
|
|
17194
|
+
if (ignoreFilter.ignores(relativePath)) continue;
|
|
17195
|
+
if (maxDepth === -1 || depth < maxDepth) await walk(fullPath, depth + 1);
|
|
17196
|
+
} else if (entry.isFile()) {
|
|
17197
|
+
await includeFile(fullPath);
|
|
17198
|
+
}
|
|
17199
|
+
}
|
|
17200
|
+
};
|
|
17201
|
+
const targetStat = await readStatIfFile(normalizedTargetPath, unreadablePrefixes);
|
|
17202
|
+
if (targetStat) await includeFile(normalizedTargetPath);
|
|
17203
|
+
else await walk(normalizedTargetPath, 0);
|
|
17204
|
+
await includeExplicitConfigPathsInPath(snapshot, unreadablePrefixes, configPaths, normalizedTargetPath);
|
|
17205
|
+
return { entries: snapshot, unreadablePrefixes };
|
|
17206
|
+
}
|
|
17207
|
+
function completeFileSnapshot(previous, scan) {
|
|
17208
|
+
const completed = new Map(scan.entries);
|
|
17209
|
+
for (const unreadablePrefix of scan.unreadablePrefixes) {
|
|
17210
|
+
for (const [entryPath, entry] of previous) {
|
|
17211
|
+
if (isWithinPath(unreadablePrefix, entryPath) && !completed.has(entryPath)) completed.set(entryPath, entry);
|
|
17212
|
+
}
|
|
17213
|
+
}
|
|
17214
|
+
return completed;
|
|
17215
|
+
}
|
|
17216
|
+
async function includeExplicitConfigPaths(snapshot, unreadablePrefixes, configPaths) {
|
|
17217
|
+
for (const configPath of [...new Set(configPaths.map((value) => path22.resolve(value)))]) {
|
|
17218
|
+
if (snapshot.has(configPath)) continue;
|
|
17219
|
+
const stat5 = await readStatIfFile(configPath, unreadablePrefixes);
|
|
17220
|
+
if (stat5) snapshot.set(configPath, { size: stat5.size, mtimeMs: stat5.mtimeMs });
|
|
17221
|
+
}
|
|
17222
|
+
}
|
|
17223
|
+
async function includeExplicitConfigPathsInPath(snapshot, unreadablePrefixes, configPaths, targetPath) {
|
|
17224
|
+
await includeExplicitConfigPaths(
|
|
17225
|
+
snapshot,
|
|
17226
|
+
unreadablePrefixes,
|
|
17227
|
+
configPaths.filter((configPath) => isWithinPath(targetPath, path22.resolve(configPath)))
|
|
17228
|
+
);
|
|
17229
|
+
}
|
|
17230
|
+
function isWithinPath(parentPath, childPath) {
|
|
17231
|
+
const relativePath = path22.relative(parentPath, childPath);
|
|
17232
|
+
return relativePath === "" || !relativePath.startsWith(`..${path22.sep}`) && relativePath !== ".." && !path22.isAbsolute(relativePath);
|
|
17233
|
+
}
|
|
17234
|
+
async function readStatIfFile(filePath, unreadablePrefixes) {
|
|
17235
|
+
try {
|
|
17236
|
+
const stat5 = await fsPromises4.stat(filePath);
|
|
17237
|
+
return stat5.isFile() ? stat5 : null;
|
|
17238
|
+
} catch (error) {
|
|
17239
|
+
if (isMissingFsError(error)) return null;
|
|
17240
|
+
if (isPermissionFsError(error)) {
|
|
17241
|
+
unreadablePrefixes.add(path22.resolve(filePath));
|
|
17242
|
+
return null;
|
|
17243
|
+
}
|
|
17244
|
+
throw error;
|
|
17245
|
+
}
|
|
17246
|
+
}
|
|
17247
|
+
function isMissingFsError(error) {
|
|
17248
|
+
return error instanceof Error && ["ENOENT", "ENOTDIR"].includes(error.code ?? "");
|
|
17249
|
+
}
|
|
17250
|
+
function isPermissionFsError(error) {
|
|
17251
|
+
return error instanceof Error && ["EACCES", "EPERM"].includes(error.code ?? "");
|
|
17252
|
+
}
|
|
17253
|
+
var diffTypeOrder = { add: 0, change: 1, unlink: 2 };
|
|
17254
|
+
function diffFileSnapshots(previous, current, forcedChanges = /* @__PURE__ */ new Set()) {
|
|
17255
|
+
const changes = [];
|
|
17256
|
+
for (const [filePath, previousEntry] of previous) {
|
|
17257
|
+
const currentEntry = current.get(filePath);
|
|
17258
|
+
if (!currentEntry) changes.push({ type: "unlink", path: filePath });
|
|
17259
|
+
else if (forcedChanges.has(filePath) || currentEntry.size !== previousEntry.size || currentEntry.mtimeMs !== previousEntry.mtimeMs) {
|
|
17260
|
+
changes.push({ type: "change", path: filePath });
|
|
17261
|
+
}
|
|
17262
|
+
}
|
|
17263
|
+
for (const [filePath] of current) {
|
|
17264
|
+
if (!previous.has(filePath)) changes.push({ type: "add", path: filePath });
|
|
17265
|
+
}
|
|
17266
|
+
return changes.sort((left, right) => left.path.localeCompare(right.path) || diffTypeOrder[left.type] - diffTypeOrder[right.type]);
|
|
17267
|
+
}
|
|
17268
|
+
|
|
17269
|
+
// src/watcher/snapshot-reconciler.ts
|
|
17270
|
+
var FileSnapshotReconciler = class {
|
|
17271
|
+
constructor(projectRoot, config, configPaths) {
|
|
17272
|
+
this.projectRoot = projectRoot;
|
|
17273
|
+
this.config = config;
|
|
17274
|
+
this.configPaths = configPaths;
|
|
17275
|
+
}
|
|
17276
|
+
projectRoot;
|
|
17277
|
+
config;
|
|
17278
|
+
configPaths;
|
|
17279
|
+
snapshot = null;
|
|
17280
|
+
reconciliationTail = Promise.resolve();
|
|
17281
|
+
async initialize() {
|
|
17282
|
+
this.snapshot = (await buildFileSnapshotScan(this.projectRoot, this.config, this.configPaths)).entries;
|
|
17283
|
+
}
|
|
17284
|
+
async reconcile(invalidations = []) {
|
|
17285
|
+
if (this.snapshot === null) {
|
|
17286
|
+
throw new Error("FileSnapshotReconciler is not initialized. Call initialize() before reconcile().");
|
|
17287
|
+
}
|
|
17288
|
+
const reconciliation = this.reconciliationTail.then(async () => {
|
|
17289
|
+
const previousSnapshot = this.snapshot;
|
|
17290
|
+
if (previousSnapshot === null) {
|
|
17291
|
+
throw new Error("FileSnapshotReconciler is not initialized. Call initialize() before reconcile().");
|
|
17292
|
+
}
|
|
17293
|
+
const normalizedInvalidations = invalidations.map((invalidation) => typeof invalidation === "string" || invalidation === null ? { path: invalidation, forceChange: false } : { path: invalidation.path, forceChange: invalidation.forceChange === true });
|
|
17294
|
+
const scopedPaths = normalizedInvalidations.map((invalidation) => invalidation.path).filter((filePath) => filePath !== null);
|
|
17295
|
+
const scan = scopedPaths.length === 0 || scopedPaths.length !== normalizedInvalidations.length ? await buildFileSnapshotScan(this.projectRoot, this.config, this.configPaths) : await this.reconcilePaths(previousSnapshot, scopedPaths);
|
|
17296
|
+
const nextSnapshot = completeFileSnapshot(previousSnapshot, scan);
|
|
17297
|
+
const forcedChanges = new Set(normalizedInvalidations.filter((invalidation) => invalidation.path !== null && invalidation.forceChange).map((invalidation) => invalidation.path));
|
|
17298
|
+
const changes = diffFileSnapshots(previousSnapshot, nextSnapshot, forcedChanges);
|
|
17299
|
+
this.snapshot = nextSnapshot;
|
|
17300
|
+
return changes;
|
|
17301
|
+
});
|
|
17302
|
+
this.reconciliationTail = reconciliation.then(() => void 0, () => void 0);
|
|
17303
|
+
return reconciliation;
|
|
17304
|
+
}
|
|
17305
|
+
async reconcilePaths(previousSnapshot, invalidatedPaths) {
|
|
17306
|
+
const scopes = this.getScopes(invalidatedPaths);
|
|
17307
|
+
const entries = new Map(previousSnapshot);
|
|
17308
|
+
const unreadablePrefixes = /* @__PURE__ */ new Set();
|
|
17309
|
+
for (const scope of scopes) {
|
|
17310
|
+
for (const previousPath of entries.keys()) {
|
|
17311
|
+
if (isWithinPath(scope, previousPath)) entries.delete(previousPath);
|
|
17312
|
+
}
|
|
17313
|
+
const scopedScan = await buildFileSnapshotForPathScan(this.projectRoot, this.config, this.configPaths, scope);
|
|
17314
|
+
for (const [filePath, entry] of scopedScan.entries) entries.set(filePath, entry);
|
|
17315
|
+
for (const unreadablePrefix of scopedScan.unreadablePrefixes) unreadablePrefixes.add(unreadablePrefix);
|
|
17316
|
+
}
|
|
17317
|
+
return { entries, unreadablePrefixes };
|
|
17318
|
+
}
|
|
17319
|
+
getScopes(invalidatedPaths) {
|
|
17320
|
+
const uniquePaths = [...new Set(invalidatedPaths)].sort((left, right) => left.length - right.length);
|
|
17321
|
+
return uniquePaths.filter((candidate, index) => !uniquePaths.slice(0, index).some(
|
|
17322
|
+
(ancestor) => isWithinPath(ancestor, candidate)
|
|
17323
|
+
));
|
|
17324
|
+
}
|
|
17325
|
+
};
|
|
17326
|
+
|
|
17327
|
+
// src/watcher/file-watcher.ts
|
|
16630
17328
|
var FileWatcher = class {
|
|
16631
17329
|
watcher = null;
|
|
16632
17330
|
projectRoot;
|
|
16633
17331
|
config;
|
|
16634
17332
|
configPath;
|
|
17333
|
+
backend;
|
|
16635
17334
|
projectConfigPaths;
|
|
16636
17335
|
pendingChanges = /* @__PURE__ */ new Map();
|
|
16637
17336
|
debounceTimer = null;
|
|
@@ -16641,44 +17340,74 @@ var FileWatcher = class {
|
|
|
16641
17340
|
resolveReady = null;
|
|
16642
17341
|
pollingFallbackAttempted = false;
|
|
16643
17342
|
pendingClose = null;
|
|
17343
|
+
startupReadySignals = 1;
|
|
17344
|
+
nativeWatcher = null;
|
|
17345
|
+
nativeReconciler = null;
|
|
17346
|
+
nativeSetupGeneration = 0;
|
|
17347
|
+
nativeStarting = false;
|
|
17348
|
+
nativeInitializing = false;
|
|
17349
|
+
nativeReconcileTimer = null;
|
|
17350
|
+
nativeInvalidatedPaths = /* @__PURE__ */ new Map();
|
|
17351
|
+
configPathStates = /* @__PURE__ */ new Map();
|
|
16644
17352
|
constructor(projectRoot, config, host, options = {}) {
|
|
16645
17353
|
this.projectRoot = projectRoot;
|
|
16646
17354
|
this.config = config;
|
|
17355
|
+
this.backend = options.backend ?? "auto";
|
|
16647
17356
|
this.configPath = options.configPath;
|
|
16648
17357
|
this.projectConfigPaths = options.configPath ? [options.configPath] : getProjectConfigCandidatePaths(projectRoot, host);
|
|
16649
17358
|
}
|
|
16650
17359
|
start(handler) {
|
|
16651
|
-
if (this.watcher) {
|
|
17360
|
+
if (this.watcher || this.nativeWatcher || this.nativeStarting) {
|
|
16652
17361
|
return;
|
|
16653
17362
|
}
|
|
16654
17363
|
this.onChanges = handler;
|
|
16655
17364
|
this.pollingFallbackAttempted = false;
|
|
16656
17365
|
this.resetReady();
|
|
17366
|
+
if (this.shouldUseNativeWatcher()) {
|
|
17367
|
+
if (this.hasExternalConfigWatchTarget()) {
|
|
17368
|
+
this.setStartupReadySignals(2);
|
|
17369
|
+
this.startExternalConfigWatcher();
|
|
17370
|
+
}
|
|
17371
|
+
this.nativeStarting = true;
|
|
17372
|
+
void this.createNativeWatcher();
|
|
17373
|
+
return;
|
|
17374
|
+
}
|
|
16657
17375
|
this.createWatcher();
|
|
16658
17376
|
}
|
|
16659
17377
|
resetReady() {
|
|
16660
|
-
this.readyPromise = new Promise((
|
|
16661
|
-
this.resolveReady =
|
|
17378
|
+
this.readyPromise = new Promise((resolve17) => {
|
|
17379
|
+
this.resolveReady = resolve17;
|
|
16662
17380
|
});
|
|
17381
|
+
this.startupReadySignals = 1;
|
|
16663
17382
|
}
|
|
16664
|
-
|
|
16665
|
-
|
|
16666
|
-
|
|
16667
|
-
|
|
16668
|
-
|
|
16669
|
-
|
|
16670
|
-
|
|
16671
|
-
|
|
16672
|
-
|
|
16673
|
-
|
|
16674
|
-
|
|
16675
|
-
|
|
16676
|
-
watchTargets = [this.projectRoot, ...uniqueExternalConfigTargets];
|
|
16677
|
-
}
|
|
17383
|
+
setStartupReadySignals(expectedSignals) {
|
|
17384
|
+
if (!this.readyPromise) {
|
|
17385
|
+
return;
|
|
17386
|
+
}
|
|
17387
|
+
this.startupReadySignals = Math.max(0, expectedSignals);
|
|
17388
|
+
}
|
|
17389
|
+
reportStartupReadySignal() {
|
|
17390
|
+
if (!this.readyPromise || !this.resolveReady) {
|
|
17391
|
+
return;
|
|
17392
|
+
}
|
|
17393
|
+
if (this.startupReadySignals <= 0) {
|
|
17394
|
+
return;
|
|
16678
17395
|
}
|
|
17396
|
+
this.startupReadySignals -= 1;
|
|
17397
|
+
if (this.startupReadySignals !== 0) {
|
|
17398
|
+
return;
|
|
17399
|
+
}
|
|
17400
|
+
this.resolveReady();
|
|
17401
|
+
this.resolveReady = null;
|
|
17402
|
+
}
|
|
17403
|
+
createWatcher(watchTargets, usePolling = false, reportsStartupReady = true) {
|
|
17404
|
+
let reportedStartupReady = false;
|
|
17405
|
+
this.configPathStates = this.getConfigPathStates();
|
|
17406
|
+
const ignoreFilter = createIgnoreFilter(this.projectRoot);
|
|
17407
|
+
const resolvedWatchTargets = watchTargets ?? this.getFullChokidarWatchTargets();
|
|
16679
17408
|
const watcherOptions = {
|
|
16680
17409
|
ignored: (filePath) => {
|
|
16681
|
-
const relativePath =
|
|
17410
|
+
const relativePath = path23.relative(this.projectRoot, filePath);
|
|
16682
17411
|
if (!relativePath) return false;
|
|
16683
17412
|
if (this.isProjectConfigPathOrAncestor(relativePath)) {
|
|
16684
17413
|
return false;
|
|
@@ -16686,10 +17415,10 @@ var FileWatcher = class {
|
|
|
16686
17415
|
if (this.isOutsideProjectPath(relativePath)) {
|
|
16687
17416
|
return true;
|
|
16688
17417
|
}
|
|
16689
|
-
if (hasFilteredPathSegment(relativePath,
|
|
17418
|
+
if (hasFilteredPathSegment(relativePath, path23.sep)) {
|
|
16690
17419
|
return true;
|
|
16691
17420
|
}
|
|
16692
|
-
if (isRestrictedDirectory(relativePath,
|
|
17421
|
+
if (isRestrictedDirectory(relativePath, path23.sep)) {
|
|
16693
17422
|
return true;
|
|
16694
17423
|
}
|
|
16695
17424
|
if (ignoreFilter.ignores(relativePath)) {
|
|
@@ -16722,10 +17451,13 @@ var FileWatcher = class {
|
|
|
16722
17451
|
watcher = new FSWatcher(watcherOptions);
|
|
16723
17452
|
}
|
|
16724
17453
|
this.watcher = watcher;
|
|
16725
|
-
watcher.
|
|
17454
|
+
watcher.on("ready", () => {
|
|
16726
17455
|
if (this.watcher !== watcher) return;
|
|
16727
|
-
this.
|
|
16728
|
-
|
|
17456
|
+
this.reconcileConfigPathStates();
|
|
17457
|
+
if (reportsStartupReady) {
|
|
17458
|
+
this.reportStartupReadySignal();
|
|
17459
|
+
reportedStartupReady = true;
|
|
17460
|
+
}
|
|
16729
17461
|
});
|
|
16730
17462
|
watcher.on("error", (error) => {
|
|
16731
17463
|
const err = error instanceof Error ? error : null;
|
|
@@ -16739,10 +17471,13 @@ var FileWatcher = class {
|
|
|
16739
17471
|
console.error("[codebase-index] Failed to close exhausted file watcher:", closeError);
|
|
16740
17472
|
});
|
|
16741
17473
|
if (this.onChanges) {
|
|
17474
|
+
const replacementReportsStartupReady = reportsStartupReady || reportedStartupReady;
|
|
16742
17475
|
if (!this.resolveReady) {
|
|
16743
17476
|
this.resetReady();
|
|
17477
|
+
} else if (reportedStartupReady) {
|
|
17478
|
+
this.startupReadySignals += 1;
|
|
16744
17479
|
}
|
|
16745
|
-
this.createWatcher(true);
|
|
17480
|
+
this.createWatcher(resolvedWatchTargets, true, replacementReportsStartupReady);
|
|
16746
17481
|
} else {
|
|
16747
17482
|
this.watcher = null;
|
|
16748
17483
|
}
|
|
@@ -16753,13 +17488,166 @@ var FileWatcher = class {
|
|
|
16753
17488
|
watcher.on("add", (filePath) => this.handleChange(watcher, "add", filePath));
|
|
16754
17489
|
watcher.on("change", (filePath) => this.handleChange(watcher, "change", filePath));
|
|
16755
17490
|
watcher.on("unlink", (filePath) => this.handleChange(watcher, "unlink", filePath));
|
|
16756
|
-
watcher.add(
|
|
17491
|
+
watcher.add(resolvedWatchTargets);
|
|
17492
|
+
}
|
|
17493
|
+
shouldUseNativeWatcher() {
|
|
17494
|
+
if (this.backend === "chokidar") {
|
|
17495
|
+
return false;
|
|
17496
|
+
}
|
|
17497
|
+
return true;
|
|
17498
|
+
}
|
|
17499
|
+
getFullChokidarWatchTargets() {
|
|
17500
|
+
if (this.configPath) {
|
|
17501
|
+
return [this.projectRoot, this.configPath];
|
|
17502
|
+
}
|
|
17503
|
+
const externalConfigTargets = this.getExternalConfigWatchTargets();
|
|
17504
|
+
if (externalConfigTargets.length === 0) {
|
|
17505
|
+
return this.projectRoot;
|
|
17506
|
+
}
|
|
17507
|
+
return [this.projectRoot, ...externalConfigTargets];
|
|
17508
|
+
}
|
|
17509
|
+
getExternalConfigWatchTargets() {
|
|
17510
|
+
return [...new Set(
|
|
17511
|
+
this.projectConfigPaths.filter((projectConfigPath) => {
|
|
17512
|
+
const relativeConfigPath = path23.relative(this.projectRoot, projectConfigPath);
|
|
17513
|
+
return this.isOutsideProjectPath(relativeConfigPath);
|
|
17514
|
+
}).map((projectConfigPath) => {
|
|
17515
|
+
if (existsSync13(projectConfigPath)) {
|
|
17516
|
+
return projectConfigPath;
|
|
17517
|
+
}
|
|
17518
|
+
return this.getNearestExistingDirectory(path23.dirname(projectConfigPath));
|
|
17519
|
+
})
|
|
17520
|
+
)];
|
|
17521
|
+
}
|
|
17522
|
+
hasExternalConfigWatchTarget() {
|
|
17523
|
+
return this.getExternalConfigWatchTargets().length > 0;
|
|
17524
|
+
}
|
|
17525
|
+
startExternalConfigWatcher(usePolling = false) {
|
|
17526
|
+
const externalTargets = this.getExternalConfigWatchTargets();
|
|
17527
|
+
if (externalTargets.length === 0) {
|
|
17528
|
+
return;
|
|
17529
|
+
}
|
|
17530
|
+
this.createWatcher(externalTargets, usePolling);
|
|
17531
|
+
}
|
|
17532
|
+
async createNativeWatcher() {
|
|
17533
|
+
const generation = ++this.nativeSetupGeneration;
|
|
17534
|
+
const reconciler = new FileSnapshotReconciler(this.projectRoot, this.config, this.projectConfigPaths);
|
|
17535
|
+
const watcher = new NativeRecursiveWatcher(
|
|
17536
|
+
this.projectRoot,
|
|
17537
|
+
(filePath) => this.scheduleNativeReconciliation(generation, filePath),
|
|
17538
|
+
{ onError: (error) => void this.fallbackFromNativeWatcher(generation, error) }
|
|
17539
|
+
);
|
|
17540
|
+
this.nativeReconciler = reconciler;
|
|
17541
|
+
this.nativeWatcher = watcher;
|
|
17542
|
+
this.nativeInitializing = true;
|
|
17543
|
+
try {
|
|
17544
|
+
watcher.start();
|
|
17545
|
+
if (!this.isCurrentNativeSetup(generation)) {
|
|
17546
|
+
await watcher.stop();
|
|
17547
|
+
return;
|
|
17548
|
+
}
|
|
17549
|
+
await reconciler.initialize();
|
|
17550
|
+
if (!this.isCurrentNativeSetup(generation) || this.nativeWatcher !== watcher) {
|
|
17551
|
+
await watcher.stop();
|
|
17552
|
+
return;
|
|
17553
|
+
}
|
|
17554
|
+
this.nativeStarting = false;
|
|
17555
|
+
this.nativeInitializing = false;
|
|
17556
|
+
await this.reconcileNativeWatcherWithPendingInvalidations(generation);
|
|
17557
|
+
this.reportStartupReadySignal();
|
|
17558
|
+
} catch (error) {
|
|
17559
|
+
if (!this.isCurrentNativeSetup(generation)) return;
|
|
17560
|
+
this.nativeInitializing = false;
|
|
17561
|
+
if (this.nativeWatcher) {
|
|
17562
|
+
await this.fallbackFromNativeWatcher(generation, error);
|
|
17563
|
+
return;
|
|
17564
|
+
}
|
|
17565
|
+
this.nativeStarting = false;
|
|
17566
|
+
const externalWatcher = this.watcher;
|
|
17567
|
+
this.watcher = null;
|
|
17568
|
+
this.nativeReconciler = null;
|
|
17569
|
+
await externalWatcher?.close();
|
|
17570
|
+
console.warn("[codebase-index] Native recursive watcher unavailable; using Chokidar fallback.", error);
|
|
17571
|
+
this.setStartupReadySignals(1);
|
|
17572
|
+
this.createWatcher();
|
|
17573
|
+
}
|
|
17574
|
+
}
|
|
17575
|
+
isCurrentNativeSetup(generation) {
|
|
17576
|
+
return this.nativeSetupGeneration === generation && this.onChanges !== null;
|
|
17577
|
+
}
|
|
17578
|
+
scheduleNativeReconciliation(generation, filePath) {
|
|
17579
|
+
if (!this.isCurrentNativeSetup(generation)) return;
|
|
17580
|
+
const requiresFullReconciliation = filePath === path23.join(this.projectRoot, ".gitignore");
|
|
17581
|
+
const invalidatedPath = requiresFullReconciliation ? null : filePath;
|
|
17582
|
+
this.nativeInvalidatedPaths.set(invalidatedPath, invalidatedPath !== null);
|
|
17583
|
+
if (this.nativeReconcileTimer) {
|
|
17584
|
+
clearTimeout(this.nativeReconcileTimer);
|
|
17585
|
+
}
|
|
17586
|
+
this.nativeReconcileTimer = setTimeout(() => {
|
|
17587
|
+
this.nativeReconcileTimer = null;
|
|
17588
|
+
void this.reconcileNativeWatcherFromQueue(generation);
|
|
17589
|
+
}, 100);
|
|
17590
|
+
}
|
|
17591
|
+
reconcileNativeWatcherFromQueue(generation) {
|
|
17592
|
+
if (!this.isCurrentNativeSetup(generation) || this.nativeInitializing) return;
|
|
17593
|
+
const invalidatedPaths = this.popNativeInvalidations();
|
|
17594
|
+
if (invalidatedPaths.length === 0) return;
|
|
17595
|
+
void this.reconcileNativeWatcher(generation, invalidatedPaths);
|
|
17596
|
+
}
|
|
17597
|
+
async reconcileNativeWatcher(generation, invalidatedPaths) {
|
|
17598
|
+
if (!this.isCurrentNativeSetup(generation) || !this.nativeReconciler) return;
|
|
17599
|
+
try {
|
|
17600
|
+
const reconciler = this.nativeReconciler;
|
|
17601
|
+
const changes = await reconciler.reconcile(invalidatedPaths);
|
|
17602
|
+
if (!this.isCurrentNativeSetup(generation) || this.nativeReconciler !== reconciler) return;
|
|
17603
|
+
this.recordChanges(changes);
|
|
17604
|
+
} catch (error) {
|
|
17605
|
+
await this.fallbackFromNativeWatcher(generation, error);
|
|
17606
|
+
}
|
|
17607
|
+
}
|
|
17608
|
+
async reconcileNativeWatcherWithPendingInvalidations(generation) {
|
|
17609
|
+
const invalidatedPaths = this.popNativeInvalidations();
|
|
17610
|
+
if (invalidatedPaths.length === 0) return;
|
|
17611
|
+
await this.reconcileNativeWatcher(generation, invalidatedPaths);
|
|
17612
|
+
}
|
|
17613
|
+
popNativeInvalidations() {
|
|
17614
|
+
if (this.nativeInvalidatedPaths.size === 0) return [];
|
|
17615
|
+
const invalidations = [...this.nativeInvalidatedPaths].map(([invalidatedPath, forceChange]) => ({
|
|
17616
|
+
path: invalidatedPath,
|
|
17617
|
+
forceChange
|
|
17618
|
+
}));
|
|
17619
|
+
this.nativeInvalidatedPaths.clear();
|
|
17620
|
+
return invalidations;
|
|
17621
|
+
}
|
|
17622
|
+
async fallbackFromNativeWatcher(generation, error) {
|
|
17623
|
+
if (!this.isCurrentNativeSetup(generation)) return;
|
|
17624
|
+
const watcher = this.nativeWatcher;
|
|
17625
|
+
const externalWatcher = this.watcher;
|
|
17626
|
+
this.nativeWatcher = null;
|
|
17627
|
+
this.watcher = null;
|
|
17628
|
+
this.nativeReconciler = null;
|
|
17629
|
+
this.nativeStarting = false;
|
|
17630
|
+
this.nativeInitializing = false;
|
|
17631
|
+
this.nativeSetupGeneration += 1;
|
|
17632
|
+
if (this.nativeReconcileTimer) {
|
|
17633
|
+
clearTimeout(this.nativeReconcileTimer);
|
|
17634
|
+
this.nativeReconcileTimer = null;
|
|
17635
|
+
}
|
|
17636
|
+
this.nativeInvalidatedPaths.clear();
|
|
17637
|
+
this.setStartupReadySignals(1);
|
|
17638
|
+
console.warn("[codebase-index] Native recursive watcher failed; using Chokidar fallback.", error);
|
|
17639
|
+
await watcher?.stop();
|
|
17640
|
+
await externalWatcher?.close();
|
|
17641
|
+
if (this.onChanges) {
|
|
17642
|
+
this.createWatcher();
|
|
17643
|
+
}
|
|
16757
17644
|
}
|
|
16758
17645
|
handleChange(watcher, type, filePath) {
|
|
16759
17646
|
if (this.watcher !== watcher) {
|
|
16760
17647
|
return;
|
|
16761
17648
|
}
|
|
16762
17649
|
if (this.isProjectConfigPath(filePath)) {
|
|
17650
|
+
this.updateConfigPathState(filePath);
|
|
16763
17651
|
this.pendingChanges.set(filePath, type);
|
|
16764
17652
|
this.scheduleFlush();
|
|
16765
17653
|
return;
|
|
@@ -16774,27 +17662,33 @@ var FileWatcher = class {
|
|
|
16774
17662
|
)) {
|
|
16775
17663
|
return;
|
|
16776
17664
|
}
|
|
16777
|
-
this.
|
|
17665
|
+
this.recordChanges([{ path: filePath, type }]);
|
|
17666
|
+
}
|
|
17667
|
+
recordChanges(changes) {
|
|
17668
|
+
if (changes.length === 0) return;
|
|
17669
|
+
for (const change of changes) {
|
|
17670
|
+
this.pendingChanges.set(change.path, change.type);
|
|
17671
|
+
}
|
|
16778
17672
|
this.scheduleFlush();
|
|
16779
17673
|
}
|
|
16780
17674
|
isProjectConfigPath(filePath) {
|
|
16781
|
-
const relativePath =
|
|
16782
|
-
const normalizedRelativePath =
|
|
17675
|
+
const relativePath = path23.relative(this.projectRoot, filePath);
|
|
17676
|
+
const normalizedRelativePath = path23.normalize(relativePath);
|
|
16783
17677
|
return this.getProjectConfigRelativePaths().some((configPath) => configPath === normalizedRelativePath);
|
|
16784
17678
|
}
|
|
16785
17679
|
isProjectConfigPathOrAncestor(relativePath) {
|
|
16786
|
-
const normalizedRelativePath =
|
|
17680
|
+
const normalizedRelativePath = path23.normalize(relativePath);
|
|
16787
17681
|
return this.getProjectConfigRelativePaths().some(
|
|
16788
|
-
(configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${
|
|
17682
|
+
(configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path23.sep}`)
|
|
16789
17683
|
);
|
|
16790
17684
|
}
|
|
16791
17685
|
isOutsideProjectPath(relativePath) {
|
|
16792
|
-
return relativePath === ".." || relativePath.startsWith(`..${
|
|
17686
|
+
return relativePath === ".." || relativePath.startsWith(`..${path23.sep}`) || path23.isAbsolute(relativePath);
|
|
16793
17687
|
}
|
|
16794
17688
|
getNearestExistingDirectory(directoryPath) {
|
|
16795
17689
|
let candidate = directoryPath;
|
|
16796
17690
|
while (!existsSync13(candidate)) {
|
|
16797
|
-
const parent =
|
|
17691
|
+
const parent = path23.dirname(candidate);
|
|
16798
17692
|
if (parent === candidate) break;
|
|
16799
17693
|
candidate = parent;
|
|
16800
17694
|
}
|
|
@@ -16802,9 +17696,51 @@ var FileWatcher = class {
|
|
|
16802
17696
|
}
|
|
16803
17697
|
getProjectConfigRelativePaths() {
|
|
16804
17698
|
return this.projectConfigPaths.map(
|
|
16805
|
-
(configPath) =>
|
|
17699
|
+
(configPath) => path23.normalize(path23.relative(this.projectRoot, configPath))
|
|
16806
17700
|
);
|
|
16807
17701
|
}
|
|
17702
|
+
getConfigPathStates() {
|
|
17703
|
+
const states = /* @__PURE__ */ new Map();
|
|
17704
|
+
for (const configPath of this.projectConfigPaths) {
|
|
17705
|
+
const state = this.getConfigPathState(configPath);
|
|
17706
|
+
if (state) states.set(configPath, state);
|
|
17707
|
+
}
|
|
17708
|
+
return states;
|
|
17709
|
+
}
|
|
17710
|
+
getConfigPathState(configPath) {
|
|
17711
|
+
try {
|
|
17712
|
+
const stats = statSync6(configPath);
|
|
17713
|
+
return stats.isFile() ? { mtimeMs: stats.mtimeMs, size: stats.size } : void 0;
|
|
17714
|
+
} catch (error) {
|
|
17715
|
+
void error;
|
|
17716
|
+
return void 0;
|
|
17717
|
+
}
|
|
17718
|
+
}
|
|
17719
|
+
updateConfigPathState(configPath) {
|
|
17720
|
+
const state = this.getConfigPathState(configPath);
|
|
17721
|
+
if (state) {
|
|
17722
|
+
this.configPathStates.set(configPath, state);
|
|
17723
|
+
} else {
|
|
17724
|
+
this.configPathStates.delete(configPath);
|
|
17725
|
+
}
|
|
17726
|
+
}
|
|
17727
|
+
reconcileConfigPathStates() {
|
|
17728
|
+
const nextStates = this.getConfigPathStates();
|
|
17729
|
+
const changes = [];
|
|
17730
|
+
for (const configPath of this.projectConfigPaths) {
|
|
17731
|
+
const previous = this.configPathStates.get(configPath);
|
|
17732
|
+
const next = nextStates.get(configPath);
|
|
17733
|
+
if (!previous && next) {
|
|
17734
|
+
changes.push({ path: configPath, type: "add" });
|
|
17735
|
+
} else if (previous && !next) {
|
|
17736
|
+
changes.push({ path: configPath, type: "unlink" });
|
|
17737
|
+
} else if (previous && next && (previous.size !== next.size || previous.mtimeMs !== next.mtimeMs)) {
|
|
17738
|
+
changes.push({ path: configPath, type: "change" });
|
|
17739
|
+
}
|
|
17740
|
+
}
|
|
17741
|
+
this.configPathStates = nextStates;
|
|
17742
|
+
this.recordChanges(changes);
|
|
17743
|
+
}
|
|
16808
17744
|
scheduleFlush() {
|
|
16809
17745
|
if (this.debounceTimer) {
|
|
16810
17746
|
clearTimeout(this.debounceTimer);
|
|
@@ -16818,7 +17754,7 @@ var FileWatcher = class {
|
|
|
16818
17754
|
return;
|
|
16819
17755
|
}
|
|
16820
17756
|
const changes = Array.from(this.pendingChanges.entries()).map(
|
|
16821
|
-
([
|
|
17757
|
+
([path30, type]) => ({ path: path30, type })
|
|
16822
17758
|
);
|
|
16823
17759
|
this.pendingChanges.clear();
|
|
16824
17760
|
try {
|
|
@@ -16832,20 +17768,31 @@ var FileWatcher = class {
|
|
|
16832
17768
|
clearTimeout(this.debounceTimer);
|
|
16833
17769
|
this.debounceTimer = null;
|
|
16834
17770
|
}
|
|
17771
|
+
if (this.nativeReconcileTimer) {
|
|
17772
|
+
clearTimeout(this.nativeReconcileTimer);
|
|
17773
|
+
this.nativeReconcileTimer = null;
|
|
17774
|
+
}
|
|
17775
|
+
this.nativeInvalidatedPaths.clear();
|
|
16835
17776
|
const watcher = this.watcher;
|
|
17777
|
+
const nativeWatcher = this.nativeWatcher;
|
|
16836
17778
|
const pendingClose = this.pendingClose;
|
|
16837
17779
|
const resolveReady = this.resolveReady;
|
|
16838
17780
|
this.watcher = null;
|
|
17781
|
+
this.nativeWatcher = null;
|
|
17782
|
+
this.nativeReconciler = null;
|
|
17783
|
+
this.nativeStarting = false;
|
|
17784
|
+
this.nativeInitializing = false;
|
|
17785
|
+
this.nativeSetupGeneration += 1;
|
|
16839
17786
|
this.pendingClose = null;
|
|
16840
17787
|
this.resolveReady = null;
|
|
16841
17788
|
this.readyPromise = null;
|
|
16842
17789
|
this.pendingChanges.clear();
|
|
16843
17790
|
this.onChanges = null;
|
|
16844
|
-
await Promise.all([watcher?.close(), pendingClose]);
|
|
17791
|
+
await Promise.all([watcher?.close(), nativeWatcher?.stop(), pendingClose]);
|
|
16845
17792
|
resolveReady?.();
|
|
16846
17793
|
}
|
|
16847
17794
|
isRunning() {
|
|
16848
|
-
return this.watcher !== null;
|
|
17795
|
+
return this.watcher !== null || this.nativeWatcher !== null || this.nativeStarting;
|
|
16849
17796
|
}
|
|
16850
17797
|
async waitUntilReady() {
|
|
16851
17798
|
await (this.readyPromise ?? Promise.resolve());
|
|
@@ -16853,7 +17800,7 @@ var FileWatcher = class {
|
|
|
16853
17800
|
};
|
|
16854
17801
|
|
|
16855
17802
|
// src/watcher/git-head-watcher.ts
|
|
16856
|
-
import * as
|
|
17803
|
+
import * as path24 from "path";
|
|
16857
17804
|
var GitHeadWatcher = class {
|
|
16858
17805
|
watcher = null;
|
|
16859
17806
|
projectRoot;
|
|
@@ -16875,13 +17822,13 @@ var GitHeadWatcher = class {
|
|
|
16875
17822
|
this.readyPromise = Promise.resolve();
|
|
16876
17823
|
return;
|
|
16877
17824
|
}
|
|
16878
|
-
this.readyPromise = new Promise((
|
|
16879
|
-
this.resolveReady =
|
|
17825
|
+
this.readyPromise = new Promise((resolve17) => {
|
|
17826
|
+
this.resolveReady = resolve17;
|
|
16880
17827
|
});
|
|
16881
17828
|
this.onBranchChange = handler;
|
|
16882
17829
|
this.currentBranch = getCurrentBranch(this.projectRoot);
|
|
16883
17830
|
const headPath = getHeadPath(this.projectRoot);
|
|
16884
|
-
const refsPath =
|
|
17831
|
+
const refsPath = path24.join(this.projectRoot, ".git", "refs", "heads");
|
|
16885
17832
|
this.watcher = chokidar_default.watch([headPath, refsPath], {
|
|
16886
17833
|
persistent: true,
|
|
16887
17834
|
ignoreInitial: true,
|
|
@@ -17532,13 +18479,19 @@ async function resolveSearchContext(input, operations) {
|
|
|
17532
18479
|
(trace) => operations.lookup(symbol, MAX_CONTEXT_RESULT_LIMIT, scope, input.diagnostic ? trace : void 0)
|
|
17533
18480
|
);
|
|
17534
18481
|
};
|
|
17535
|
-
const tryConceptualSearch = async (searchQuery, scope, relaxedFieldsForAttempt) => {
|
|
18482
|
+
const tryConceptualSearch = async (searchQuery, scope, relaxedFieldsForAttempt, prioritizeSourcePaths) => {
|
|
17536
18483
|
return recordAttempt(
|
|
17537
18484
|
"conceptual",
|
|
17538
18485
|
searchQuery,
|
|
17539
18486
|
scope,
|
|
17540
18487
|
relaxedFieldsForAttempt,
|
|
17541
|
-
(trace) => operations.search(
|
|
18488
|
+
(trace) => operations.search(
|
|
18489
|
+
searchQuery,
|
|
18490
|
+
MAX_CONTEXT_RESULT_LIMIT,
|
|
18491
|
+
scope,
|
|
18492
|
+
input.diagnostic ? trace : void 0,
|
|
18493
|
+
{ prioritizeSourcePaths }
|
|
18494
|
+
)
|
|
17542
18495
|
);
|
|
17543
18496
|
};
|
|
17544
18497
|
const findSuccessfulAttemptState = (route) => {
|
|
@@ -17666,10 +18619,12 @@ Explicit symbol lookup only; conceptual search was not attempted.`
|
|
|
17666
18619
|
}
|
|
17667
18620
|
}
|
|
17668
18621
|
for (const attempt of conceptualAttemptPlan) {
|
|
18622
|
+
const attemptIntent = analyzeQueryIntent(attempt.queryText);
|
|
18623
|
+
const prioritizeSourcePaths = attemptIntent.primary !== "docs" && attemptIntent.primary !== "test";
|
|
17669
18624
|
if (inferredSymbol && attempt.queryText === inferredSymbol && attempt.queryText !== query) {
|
|
17670
18625
|
decisions.fallbackFromOriginalConceptualToInferred = true;
|
|
17671
18626
|
}
|
|
17672
|
-
const results = await tryConceptualSearch(attempt.queryText, attempt.scope, attempt.relaxed);
|
|
18627
|
+
const results = await tryConceptualSearch(attempt.queryText, attempt.scope, attempt.relaxed, prioritizeSourcePaths);
|
|
17673
18628
|
if (results.length > 0) {
|
|
17674
18629
|
const heading = buildPackHeading("conceptual", decisions);
|
|
17675
18630
|
const intent = analyzeQueryIntent(attempt.queryText);
|
|
@@ -17737,7 +18692,7 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
|
|
|
17737
18692
|
const directory = input.directory ?? void 0;
|
|
17738
18693
|
const tokenBudget = input.tokenBudget ?? void 0;
|
|
17739
18694
|
if (from && to) {
|
|
17740
|
-
const
|
|
18695
|
+
const path30 = await getCallGraphPath(
|
|
17741
18696
|
projectRoot,
|
|
17742
18697
|
host,
|
|
17743
18698
|
from,
|
|
@@ -17746,25 +18701,25 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
|
|
|
17746
18701
|
fromFilePath,
|
|
17747
18702
|
toFilePath
|
|
17748
18703
|
);
|
|
17749
|
-
const pathText = formatCallGraphPathResult(
|
|
17750
|
-
if (
|
|
18704
|
+
const pathText = formatCallGraphPathResult(path30);
|
|
18705
|
+
if (path30.path.length > 0) {
|
|
17751
18706
|
const fitted2 = fitTextToContextBudget(
|
|
17752
18707
|
pathText,
|
|
17753
18708
|
tokenBudget
|
|
17754
18709
|
);
|
|
17755
18710
|
return {
|
|
17756
18711
|
text: fitted2.text,
|
|
17757
|
-
details: fittedDetails("path", fitted2,
|
|
18712
|
+
details: fittedDetails("path", fitted2, path30.path.length)
|
|
17758
18713
|
};
|
|
17759
18714
|
}
|
|
17760
|
-
if (
|
|
18715
|
+
if (path30.from.status !== "resolved" || path30.to.status !== "resolved") {
|
|
17761
18716
|
const fitted2 = fitTextToContextBudget(pathText, tokenBudget);
|
|
17762
18717
|
return {
|
|
17763
18718
|
text: fitted2.text,
|
|
17764
18719
|
details: fittedDetails("path", fitted2, 0)
|
|
17765
18720
|
};
|
|
17766
18721
|
}
|
|
17767
|
-
const resolvedFrom =
|
|
18722
|
+
const resolvedFrom = path30.from;
|
|
17768
18723
|
const { callers } = await getCallGraphData(projectRoot, host, {
|
|
17769
18724
|
name: to,
|
|
17770
18725
|
direction: "callers",
|
|
@@ -17806,12 +18761,13 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
|
|
|
17806
18761
|
directory: scope.directory,
|
|
17807
18762
|
trace
|
|
17808
18763
|
}),
|
|
17809
|
-
search: (queryText, retrievalLimit, scope, trace) => searchCodebase(projectRoot, host, queryText, {
|
|
18764
|
+
search: (queryText, retrievalLimit, scope, trace, searchOptions) => searchCodebase(projectRoot, host, queryText, {
|
|
17810
18765
|
limit: retrievalLimit,
|
|
17811
18766
|
fileType: scope.fileType,
|
|
17812
18767
|
directory: scope.directory,
|
|
17813
18768
|
metadataOnly: true,
|
|
17814
|
-
trace
|
|
18769
|
+
trace,
|
|
18770
|
+
prioritizeSourcePaths: searchOptions?.prioritizeSourcePaths
|
|
17815
18771
|
})
|
|
17816
18772
|
});
|
|
17817
18773
|
}
|
|
@@ -17923,7 +18879,7 @@ async function executeCallGraph(projectRoot, host, args) {
|
|
|
17923
18879
|
return { text: formatCallGraphResult(await getCallGraphData(projectRoot, host, args)) };
|
|
17924
18880
|
}
|
|
17925
18881
|
async function executeCallGraphPath(projectRoot, host, args) {
|
|
17926
|
-
const
|
|
18882
|
+
const path30 = await getCallGraphPath(
|
|
17927
18883
|
projectRoot,
|
|
17928
18884
|
host,
|
|
17929
18885
|
args.from,
|
|
@@ -17932,7 +18888,7 @@ async function executeCallGraphPath(projectRoot, host, args) {
|
|
|
17932
18888
|
args.fromFilePath,
|
|
17933
18889
|
args.toFilePath
|
|
17934
18890
|
);
|
|
17935
|
-
return { text: formatCallGraphPathResult(
|
|
18891
|
+
return { text: formatCallGraphPathResult(path30) };
|
|
17936
18892
|
}
|
|
17937
18893
|
async function executeCodeCommunities(projectRoot, host, args) {
|
|
17938
18894
|
const result = await getCodeCommunities(projectRoot, host, args);
|
|
@@ -17942,11 +18898,11 @@ async function executeCodeCommunities(projectRoot, host, args) {
|
|
|
17942
18898
|
// src/adapters/opencode/tools.ts
|
|
17943
18899
|
import { writeFileSync as writeFileSync4 } from "fs";
|
|
17944
18900
|
import * as os7 from "os";
|
|
17945
|
-
import * as
|
|
18901
|
+
import * as path27 from "path";
|
|
17946
18902
|
|
|
17947
18903
|
// src/tools/visualize/activity.ts
|
|
17948
18904
|
import { execFileSync } from "child_process";
|
|
17949
|
-
import * as
|
|
18905
|
+
import * as path25 from "path";
|
|
17950
18906
|
function attachRecentActivity(data, projectRoot) {
|
|
17951
18907
|
const activity = readGitActivity(projectRoot);
|
|
17952
18908
|
const changes = activity.size > 0 ? buildGitChanges(data, activity, projectRoot) : buildGraphChanges(data);
|
|
@@ -18108,7 +19064,7 @@ function normalizePath3(filePath) {
|
|
|
18108
19064
|
return filePath.replace(/\\/g, "/");
|
|
18109
19065
|
}
|
|
18110
19066
|
function toGitRelativePath(projectRoot, filePath) {
|
|
18111
|
-
const relativePath =
|
|
19067
|
+
const relativePath = path25.isAbsolute(filePath) ? path25.relative(projectRoot, filePath) : filePath;
|
|
18112
19068
|
return normalizePath3(relativePath);
|
|
18113
19069
|
}
|
|
18114
19070
|
|
|
@@ -18366,7 +19322,7 @@ render();
|
|
|
18366
19322
|
}
|
|
18367
19323
|
|
|
18368
19324
|
// src/tools/visualize/transform.ts
|
|
18369
|
-
import * as
|
|
19325
|
+
import * as path26 from "path";
|
|
18370
19326
|
|
|
18371
19327
|
// src/tools/visualize/modules.ts
|
|
18372
19328
|
var MAX_MODULES = 18;
|
|
@@ -18499,8 +19455,8 @@ function compactModules(prefixToNodes) {
|
|
|
18499
19455
|
function deriveModules(nodes) {
|
|
18500
19456
|
const initial = /* @__PURE__ */ new Map();
|
|
18501
19457
|
for (const node of nodes) {
|
|
18502
|
-
const
|
|
18503
|
-
const prefix = modulePrefixFromRelativePath(
|
|
19458
|
+
const relative14 = stripToProjectRelative(node.filePath);
|
|
19459
|
+
const prefix = modulePrefixFromRelativePath(relative14);
|
|
18504
19460
|
if (!initial.has(prefix)) initial.set(prefix, []);
|
|
18505
19461
|
initial.get(prefix)?.push(node);
|
|
18506
19462
|
}
|
|
@@ -18626,7 +19582,7 @@ function transformForVisualization(symbols, edges, options = {}) {
|
|
|
18626
19582
|
filePath: s.filePath,
|
|
18627
19583
|
kind: s.kind,
|
|
18628
19584
|
line: s.startLine,
|
|
18629
|
-
directory:
|
|
19585
|
+
directory: path26.dirname(s.filePath),
|
|
18630
19586
|
moduleId: "",
|
|
18631
19587
|
moduleLabel: ""
|
|
18632
19588
|
}));
|
|
@@ -18726,7 +19682,8 @@ var codebase_peek = tool({
|
|
|
18726
19682
|
chunkType: z3.enum(CHUNK_TYPE_VALUES).optional().describe("Filter by code chunk type"),
|
|
18727
19683
|
blameAuthor: z3.string().optional().describe("Filter by git blame author name or email"),
|
|
18728
19684
|
blameSha: z3.string().optional().describe("Filter by git blame commit SHA or prefix"),
|
|
18729
|
-
blameSince: z3.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)")
|
|
19685
|
+
blameSince: z3.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)"),
|
|
19686
|
+
blameUntil: z3.string().optional().describe("Filter to chunks last changed on or before this date (e.g., 2025-01-31)")
|
|
18730
19687
|
},
|
|
18731
19688
|
async execute(args, context) {
|
|
18732
19689
|
return searchCodebaseWithEffectiveness(context?.worktree, DEFAULT_HOST, "peek", args.query, {
|
|
@@ -18737,7 +19694,8 @@ var codebase_peek = tool({
|
|
|
18737
19694
|
metadataOnly: true,
|
|
18738
19695
|
blameAuthor: args.blameAuthor,
|
|
18739
19696
|
blameSha: args.blameSha,
|
|
18740
|
-
blameSince: args.blameSince
|
|
19697
|
+
blameSince: args.blameSince,
|
|
19698
|
+
blameUntil: args.blameUntil
|
|
18741
19699
|
}, (results) => {
|
|
18742
19700
|
const text = formatCodebasePeek(results);
|
|
18743
19701
|
return { output: text, text };
|
|
@@ -18799,7 +19757,9 @@ var find_similar = tool({
|
|
|
18799
19757
|
fileType: z3.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
|
|
18800
19758
|
directory: z3.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
|
|
18801
19759
|
chunkType: z3.enum(CHUNK_TYPE_VALUES).optional().describe("Filter by code chunk type"),
|
|
18802
|
-
excludeFile: z3.string().optional().describe("Exclude results from this file path (useful when searching for duplicates of code from a specific file)")
|
|
19760
|
+
excludeFile: z3.string().optional().describe("Exclude results from this file path (useful when searching for duplicates of code from a specific file)"),
|
|
19761
|
+
blameSince: z3.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)"),
|
|
19762
|
+
blameUntil: z3.string().optional().describe("Filter to chunks last changed on or before this date (e.g., 2025-01-31)")
|
|
18803
19763
|
},
|
|
18804
19764
|
async execute(args, context) {
|
|
18805
19765
|
const results = await findSimilarCode(context?.worktree, DEFAULT_HOST, args.code, {
|
|
@@ -18807,7 +19767,9 @@ var find_similar = tool({
|
|
|
18807
19767
|
fileType: args.fileType,
|
|
18808
19768
|
directory: args.directory,
|
|
18809
19769
|
chunkType: args.chunkType,
|
|
18810
|
-
excludeFile: args.excludeFile
|
|
19770
|
+
excludeFile: args.excludeFile,
|
|
19771
|
+
blameSince: args.blameSince,
|
|
19772
|
+
blameUntil: args.blameUntil
|
|
18811
19773
|
});
|
|
18812
19774
|
if (results.length === 0) {
|
|
18813
19775
|
return "No similar code found. Try a different snippet or run index_codebase first.";
|
|
@@ -18826,7 +19788,8 @@ var codebase_search = tool({
|
|
|
18826
19788
|
contextLines: z3.number().optional().describe("Number of extra lines to include before/after each match (default: 0)"),
|
|
18827
19789
|
blameAuthor: z3.string().optional().describe("Filter by git blame author name or email"),
|
|
18828
19790
|
blameSha: z3.string().optional().describe("Filter by git blame commit SHA or prefix"),
|
|
18829
|
-
blameSince: z3.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)")
|
|
19791
|
+
blameSince: z3.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)"),
|
|
19792
|
+
blameUntil: z3.string().optional().describe("Filter to chunks last changed on or before this date (e.g., 2025-01-31)")
|
|
18830
19793
|
},
|
|
18831
19794
|
async execute(args, context) {
|
|
18832
19795
|
return searchCodebaseWithEffectiveness(context?.worktree, DEFAULT_HOST, "search", args.query, {
|
|
@@ -18837,7 +19800,8 @@ var codebase_search = tool({
|
|
|
18837
19800
|
contextLines: args.contextLines,
|
|
18838
19801
|
blameAuthor: args.blameAuthor,
|
|
18839
19802
|
blameSha: args.blameSha,
|
|
18840
|
-
blameSince: args.blameSince
|
|
19803
|
+
blameSince: args.blameSince,
|
|
19804
|
+
blameUntil: args.blameUntil
|
|
18841
19805
|
}, (results) => {
|
|
18842
19806
|
const text = results.length === 0 ? "No matching code found. Try a different query or run index_codebase first." : formatSearchResults(results, "score");
|
|
18843
19807
|
return { output: text, text };
|
|
@@ -18946,7 +19910,7 @@ var index_visualize = tool({
|
|
|
18946
19910
|
return "No connected symbols found for visualization. Try including orphans with includeOrphans=true, or check that the call graph has resolved edges.";
|
|
18947
19911
|
}
|
|
18948
19912
|
const html = generateVisualizationHtml(vizData);
|
|
18949
|
-
const outputPath =
|
|
19913
|
+
const outputPath = path27.join(os7.tmpdir(), `call-graph-${Date.now()}.html`);
|
|
18950
19914
|
writeFileSync4(outputPath, html, "utf-8");
|
|
18951
19915
|
let result = `Temporal call graph visualization generated: ${outputPath}
|
|
18952
19916
|
|
|
@@ -19050,10 +20014,16 @@ var PI_TOOL_NAMES = [
|
|
|
19050
20014
|
TOOL_NAME.PI_KNOWLEDGE_BASE_ADD,
|
|
19051
20015
|
TOOL_NAME.PI_KNOWLEDGE_BASE_REMOVE
|
|
19052
20016
|
];
|
|
20017
|
+
var MCP_TOOL_NAMES = [
|
|
20018
|
+
...PORTABLE_TOOL_NAMES,
|
|
20019
|
+
TOOL_NAME.ADD_KNOWLEDGE_BASE,
|
|
20020
|
+
TOOL_NAME.LIST_KNOWLEDGE_BASES,
|
|
20021
|
+
TOOL_NAME.REMOVE_KNOWLEDGE_BASE
|
|
20022
|
+
];
|
|
19053
20023
|
|
|
19054
20024
|
// src/commands/loader.ts
|
|
19055
20025
|
import { existsSync as existsSync14, readdirSync as readdirSync3, readFileSync as readFileSync9 } from "fs";
|
|
19056
|
-
import * as
|
|
20026
|
+
import * as path28 from "path";
|
|
19057
20027
|
function parseFrontmatter(content) {
|
|
19058
20028
|
const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
|
|
19059
20029
|
const match = content.match(frontmatterRegex);
|
|
@@ -19079,7 +20049,7 @@ function loadCommandsFromDirectory(commandsDir) {
|
|
|
19079
20049
|
}
|
|
19080
20050
|
const files = readdirSync3(commandsDir).filter((f) => f.endsWith(".md"));
|
|
19081
20051
|
for (const file of files) {
|
|
19082
|
-
const filePath =
|
|
20052
|
+
const filePath = path28.join(commandsDir, file);
|
|
19083
20053
|
let content;
|
|
19084
20054
|
try {
|
|
19085
20055
|
content = readFileSync9(filePath, "utf-8");
|
|
@@ -19088,7 +20058,7 @@ function loadCommandsFromDirectory(commandsDir) {
|
|
|
19088
20058
|
throw new Error(`Failed to load command file ${filePath}: ${message}`);
|
|
19089
20059
|
}
|
|
19090
20060
|
const { frontmatter, body } = parseFrontmatter(content);
|
|
19091
|
-
const name =
|
|
20061
|
+
const name = path28.basename(file, ".md");
|
|
19092
20062
|
const description = frontmatter.description || `Run the ${name} command`;
|
|
19093
20063
|
commands.set(name, {
|
|
19094
20064
|
description,
|
|
@@ -19422,23 +20392,41 @@ var RoutingHintController = class {
|
|
|
19422
20392
|
|
|
19423
20393
|
// src/adapters/opencode.ts
|
|
19424
20394
|
var activeWatchers = /* @__PURE__ */ new Map();
|
|
19425
|
-
|
|
19426
|
-
|
|
19427
|
-
|
|
19428
|
-
existing.
|
|
19429
|
-
|
|
19430
|
-
|
|
19431
|
-
|
|
19432
|
-
|
|
20395
|
+
var watcherReplacementChains = /* @__PURE__ */ new Map();
|
|
20396
|
+
async function replaceActiveWatcher(projectRoot, createNextWatcher) {
|
|
20397
|
+
const chain = (watcherReplacementChains.get(projectRoot) ?? Promise.resolve()).catch(() => void 0).then(async () => {
|
|
20398
|
+
const existing = activeWatchers.get(projectRoot);
|
|
20399
|
+
if (existing) {
|
|
20400
|
+
try {
|
|
20401
|
+
await existing.stop();
|
|
20402
|
+
} catch (error) {
|
|
20403
|
+
console.error("[codebase-index] Failed to stop replaced watcher:", error);
|
|
20404
|
+
throw error;
|
|
20405
|
+
}
|
|
20406
|
+
if (activeWatchers.get(projectRoot) === existing) {
|
|
20407
|
+
activeWatchers.delete(projectRoot);
|
|
20408
|
+
}
|
|
20409
|
+
}
|
|
20410
|
+
if (createNextWatcher) {
|
|
20411
|
+
activeWatchers.set(projectRoot, createNextWatcher());
|
|
20412
|
+
}
|
|
20413
|
+
});
|
|
20414
|
+
watcherReplacementChains.set(projectRoot, chain);
|
|
20415
|
+
try {
|
|
20416
|
+
await chain;
|
|
20417
|
+
} finally {
|
|
20418
|
+
if (watcherReplacementChains.get(projectRoot) === chain) {
|
|
20419
|
+
watcherReplacementChains.delete(projectRoot);
|
|
20420
|
+
}
|
|
19433
20421
|
}
|
|
19434
20422
|
}
|
|
19435
20423
|
function getCommandsDir() {
|
|
19436
20424
|
let currentDir = process.cwd();
|
|
19437
20425
|
if (typeof import.meta !== "undefined" && import.meta.url) {
|
|
19438
|
-
currentDir =
|
|
20426
|
+
currentDir = path29.dirname(fileURLToPath2(import.meta.url));
|
|
19439
20427
|
}
|
|
19440
|
-
const packageRoot =
|
|
19441
|
-
return
|
|
20428
|
+
const packageRoot = path29.basename(currentDir) === "adapters" ? path29.join(currentDir, "..", "..") : path29.join(currentDir, "..");
|
|
20429
|
+
return path29.join(packageRoot, "commands");
|
|
19442
20430
|
}
|
|
19443
20431
|
function appendRoutingHints(output, hints, preferredRole) {
|
|
19444
20432
|
const preferredBucket = preferredRole === "developer" ? output.developer : output.system;
|
|
@@ -19479,9 +20467,12 @@ var plugin = async ({ directory, worktree }) => {
|
|
|
19479
20467
|
startAutoIndex(projectRoot, "opencode", "startup");
|
|
19480
20468
|
}
|
|
19481
20469
|
if (config.indexing.watchFiles && isValidProject) {
|
|
19482
|
-
replaceActiveWatcher(
|
|
20470
|
+
await replaceActiveWatcher(
|
|
20471
|
+
projectRoot,
|
|
20472
|
+
() => createWatcherWithIndexer(getProjectIndexer, projectRoot, config, "opencode")
|
|
20473
|
+
);
|
|
19483
20474
|
} else {
|
|
19484
|
-
replaceActiveWatcher(projectRoot, null);
|
|
20475
|
+
await replaceActiveWatcher(projectRoot, null);
|
|
19485
20476
|
}
|
|
19486
20477
|
return {
|
|
19487
20478
|
tool: {
|