opencode-codebase-index 0.7.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -7
- package/commands/index.md +4 -0
- package/commands/peek.md +24 -0
- package/commands/reindex.md +25 -0
- package/commands/status.md +5 -1
- package/dist/cli.cjs +2423 -730
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2416 -723
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +2352 -711
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2341 -700
- package/dist/index.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 +3 -3
package/dist/index.js
CHANGED
|
@@ -324,7 +324,7 @@ var require_ignore = __commonJS({
|
|
|
324
324
|
// path matching.
|
|
325
325
|
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
326
326
|
// @returns {TestResult} true if a file is ignored
|
|
327
|
-
test(
|
|
327
|
+
test(path12, checkUnignored, mode) {
|
|
328
328
|
let ignored = false;
|
|
329
329
|
let unignored = false;
|
|
330
330
|
let matchedRule;
|
|
@@ -333,7 +333,7 @@ var require_ignore = __commonJS({
|
|
|
333
333
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
334
334
|
return;
|
|
335
335
|
}
|
|
336
|
-
const matched = rule[mode].test(
|
|
336
|
+
const matched = rule[mode].test(path12);
|
|
337
337
|
if (!matched) {
|
|
338
338
|
return;
|
|
339
339
|
}
|
|
@@ -354,17 +354,17 @@ var require_ignore = __commonJS({
|
|
|
354
354
|
var throwError = (message, Ctor) => {
|
|
355
355
|
throw new Ctor(message);
|
|
356
356
|
};
|
|
357
|
-
var checkPath = (
|
|
358
|
-
if (!isString(
|
|
357
|
+
var checkPath = (path12, originalPath, doThrow) => {
|
|
358
|
+
if (!isString(path12)) {
|
|
359
359
|
return doThrow(
|
|
360
360
|
`path must be a string, but got \`${originalPath}\``,
|
|
361
361
|
TypeError
|
|
362
362
|
);
|
|
363
363
|
}
|
|
364
|
-
if (!
|
|
364
|
+
if (!path12) {
|
|
365
365
|
return doThrow(`path must not be empty`, TypeError);
|
|
366
366
|
}
|
|
367
|
-
if (checkPath.isNotRelative(
|
|
367
|
+
if (checkPath.isNotRelative(path12)) {
|
|
368
368
|
const r = "`path.relative()`d";
|
|
369
369
|
return doThrow(
|
|
370
370
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -373,7 +373,7 @@ var require_ignore = __commonJS({
|
|
|
373
373
|
}
|
|
374
374
|
return true;
|
|
375
375
|
};
|
|
376
|
-
var isNotRelative = (
|
|
376
|
+
var isNotRelative = (path12) => REGEX_TEST_INVALID_PATH.test(path12);
|
|
377
377
|
checkPath.isNotRelative = isNotRelative;
|
|
378
378
|
checkPath.convert = (p) => p;
|
|
379
379
|
var Ignore2 = class {
|
|
@@ -403,19 +403,19 @@ var require_ignore = __commonJS({
|
|
|
403
403
|
}
|
|
404
404
|
// @returns {TestResult}
|
|
405
405
|
_test(originalPath, cache, checkUnignored, slices) {
|
|
406
|
-
const
|
|
406
|
+
const path12 = originalPath && checkPath.convert(originalPath);
|
|
407
407
|
checkPath(
|
|
408
|
-
|
|
408
|
+
path12,
|
|
409
409
|
originalPath,
|
|
410
410
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
411
411
|
);
|
|
412
|
-
return this._t(
|
|
412
|
+
return this._t(path12, cache, checkUnignored, slices);
|
|
413
413
|
}
|
|
414
|
-
checkIgnore(
|
|
415
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
416
|
-
return this.test(
|
|
414
|
+
checkIgnore(path12) {
|
|
415
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path12)) {
|
|
416
|
+
return this.test(path12);
|
|
417
417
|
}
|
|
418
|
-
const slices =
|
|
418
|
+
const slices = path12.split(SLASH2).filter(Boolean);
|
|
419
419
|
slices.pop();
|
|
420
420
|
if (slices.length) {
|
|
421
421
|
const parent = this._t(
|
|
@@ -428,18 +428,18 @@ var require_ignore = __commonJS({
|
|
|
428
428
|
return parent;
|
|
429
429
|
}
|
|
430
430
|
}
|
|
431
|
-
return this._rules.test(
|
|
431
|
+
return this._rules.test(path12, false, MODE_CHECK_IGNORE);
|
|
432
432
|
}
|
|
433
|
-
_t(
|
|
434
|
-
if (
|
|
435
|
-
return cache[
|
|
433
|
+
_t(path12, cache, checkUnignored, slices) {
|
|
434
|
+
if (path12 in cache) {
|
|
435
|
+
return cache[path12];
|
|
436
436
|
}
|
|
437
437
|
if (!slices) {
|
|
438
|
-
slices =
|
|
438
|
+
slices = path12.split(SLASH2).filter(Boolean);
|
|
439
439
|
}
|
|
440
440
|
slices.pop();
|
|
441
441
|
if (!slices.length) {
|
|
442
|
-
return cache[
|
|
442
|
+
return cache[path12] = this._rules.test(path12, checkUnignored, MODE_IGNORE);
|
|
443
443
|
}
|
|
444
444
|
const parent = this._t(
|
|
445
445
|
slices.join(SLASH2) + SLASH2,
|
|
@@ -447,29 +447,29 @@ var require_ignore = __commonJS({
|
|
|
447
447
|
checkUnignored,
|
|
448
448
|
slices
|
|
449
449
|
);
|
|
450
|
-
return cache[
|
|
450
|
+
return cache[path12] = parent.ignored ? parent : this._rules.test(path12, checkUnignored, MODE_IGNORE);
|
|
451
451
|
}
|
|
452
|
-
ignores(
|
|
453
|
-
return this._test(
|
|
452
|
+
ignores(path12) {
|
|
453
|
+
return this._test(path12, this._ignoreCache, false).ignored;
|
|
454
454
|
}
|
|
455
455
|
createFilter() {
|
|
456
|
-
return (
|
|
456
|
+
return (path12) => !this.ignores(path12);
|
|
457
457
|
}
|
|
458
458
|
filter(paths) {
|
|
459
459
|
return makeArray(paths).filter(this.createFilter());
|
|
460
460
|
}
|
|
461
461
|
// @returns {TestResult}
|
|
462
|
-
test(
|
|
463
|
-
return this._test(
|
|
462
|
+
test(path12) {
|
|
463
|
+
return this._test(path12, this._testCache, true);
|
|
464
464
|
}
|
|
465
465
|
};
|
|
466
466
|
var factory = (options) => new Ignore2(options);
|
|
467
|
-
var isPathValid = (
|
|
467
|
+
var isPathValid = (path12) => checkPath(path12 && checkPath.convert(path12), path12, RETURN_FALSE);
|
|
468
468
|
var setupWindows = () => {
|
|
469
469
|
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
470
470
|
checkPath.convert = makePosix;
|
|
471
471
|
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
472
|
-
checkPath.isNotRelative = (
|
|
472
|
+
checkPath.isNotRelative = (path12) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path12) || isNotRelative(path12);
|
|
473
473
|
};
|
|
474
474
|
if (
|
|
475
475
|
// Detect `process` so that it can run in browsers.
|
|
@@ -647,7 +647,7 @@ var require_eventemitter3 = __commonJS({
|
|
|
647
647
|
});
|
|
648
648
|
|
|
649
649
|
// src/index.ts
|
|
650
|
-
import * as
|
|
650
|
+
import * as path11 from "path";
|
|
651
651
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
652
652
|
|
|
653
653
|
// src/config/constants.ts
|
|
@@ -657,12 +657,14 @@ var DEFAULT_INCLUDE = [
|
|
|
657
657
|
"**/*.{go,rs,java,kt,scala}",
|
|
658
658
|
"**/*.{c,cpp,cc,h,hpp}",
|
|
659
659
|
"**/*.{rb,php,inc,swift}",
|
|
660
|
+
"**/*.{cls,trigger}",
|
|
660
661
|
"**/*.{vue,svelte,astro}",
|
|
661
662
|
"**/*.{sql,graphql,proto}",
|
|
662
663
|
"**/*.{yaml,yml,toml}",
|
|
663
664
|
"**/*.{md,mdx}",
|
|
664
665
|
"**/*.{sh,bash,zsh}",
|
|
665
|
-
"**/*.{txt,html,htm}"
|
|
666
|
+
"**/*.{txt,html,htm}",
|
|
667
|
+
"**/*.zig"
|
|
666
668
|
];
|
|
667
669
|
var DEFAULT_EXCLUDE = [
|
|
668
670
|
"**/node_modules/**",
|
|
@@ -727,7 +729,7 @@ var EMBEDDING_MODELS = {
|
|
|
727
729
|
provider: "ollama",
|
|
728
730
|
model: "nomic-embed-text",
|
|
729
731
|
dimensions: 768,
|
|
730
|
-
maxTokens:
|
|
732
|
+
maxTokens: 2048,
|
|
731
733
|
costPer1MTokens: 0
|
|
732
734
|
},
|
|
733
735
|
"mxbai-embed-large": {
|
|
@@ -751,9 +753,15 @@ var EMBEDDING_MODELS = {
|
|
|
751
753
|
var DEFAULT_PROVIDER_MODELS = {
|
|
752
754
|
"github-copilot": "text-embedding-3-small",
|
|
753
755
|
"openai": "text-embedding-3-small",
|
|
754
|
-
"google": "
|
|
756
|
+
"google": "gemini-embedding-001",
|
|
755
757
|
"ollama": "nomic-embed-text"
|
|
756
758
|
};
|
|
759
|
+
var AUTO_DETECT_PROVIDER_ORDER = [
|
|
760
|
+
"ollama",
|
|
761
|
+
"github-copilot",
|
|
762
|
+
"openai",
|
|
763
|
+
"google"
|
|
764
|
+
];
|
|
757
765
|
|
|
758
766
|
// src/config/env-substitution.ts
|
|
759
767
|
var ENV_REFERENCE_PATTERN = /^\{env:([A-Z_][A-Z0-9_]*)\}$/;
|
|
@@ -1012,26 +1020,261 @@ function getDefaultModelForProvider(provider) {
|
|
|
1012
1020
|
return models[providerDefault];
|
|
1013
1021
|
}
|
|
1014
1022
|
var availableProviders = Object.keys(EMBEDDING_MODELS);
|
|
1023
|
+
var autoDetectProviders = AUTO_DETECT_PROVIDER_ORDER.filter(
|
|
1024
|
+
(provider) => provider in EMBEDDING_MODELS
|
|
1025
|
+
);
|
|
1015
1026
|
|
|
1016
1027
|
// src/config/merger.ts
|
|
1017
|
-
import { existsSync, readFileSync } from "fs";
|
|
1018
|
-
import * as
|
|
1028
|
+
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
1029
|
+
import * as os2 from "os";
|
|
1030
|
+
import * as path3 from "path";
|
|
1031
|
+
|
|
1032
|
+
// src/config/paths.ts
|
|
1033
|
+
import { existsSync as existsSync2 } from "fs";
|
|
1019
1034
|
import * as os from "os";
|
|
1035
|
+
import * as path2 from "path";
|
|
1036
|
+
|
|
1037
|
+
// src/git/index.ts
|
|
1038
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "fs";
|
|
1039
|
+
import * as path from "path";
|
|
1040
|
+
function readPackedRefs(gitDir) {
|
|
1041
|
+
const packedRefsPath = path.join(gitDir, "packed-refs");
|
|
1042
|
+
if (!existsSync(packedRefsPath)) {
|
|
1043
|
+
return [];
|
|
1044
|
+
}
|
|
1045
|
+
try {
|
|
1046
|
+
return readFileSync(packedRefsPath, "utf-8").split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#") && !line.startsWith("^"));
|
|
1047
|
+
} catch {
|
|
1048
|
+
return [];
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
function resolveCommonGitDir(gitDir) {
|
|
1052
|
+
const commonDirPath = path.join(gitDir, "commondir");
|
|
1053
|
+
if (!existsSync(commonDirPath)) {
|
|
1054
|
+
return gitDir;
|
|
1055
|
+
}
|
|
1056
|
+
try {
|
|
1057
|
+
const raw = readFileSync(commonDirPath, "utf-8").trim();
|
|
1058
|
+
if (!raw) {
|
|
1059
|
+
return gitDir;
|
|
1060
|
+
}
|
|
1061
|
+
const resolved = path.isAbsolute(raw) ? raw : path.resolve(gitDir, raw);
|
|
1062
|
+
if (existsSync(resolved)) {
|
|
1063
|
+
return resolved;
|
|
1064
|
+
}
|
|
1065
|
+
} catch {
|
|
1066
|
+
return gitDir;
|
|
1067
|
+
}
|
|
1068
|
+
return gitDir;
|
|
1069
|
+
}
|
|
1070
|
+
function resolveWorktreeMainRepoRoot(repoRoot) {
|
|
1071
|
+
const gitDir = resolveGitDir(repoRoot);
|
|
1072
|
+
if (!gitDir) {
|
|
1073
|
+
return null;
|
|
1074
|
+
}
|
|
1075
|
+
const commonGitDir = resolveCommonGitDir(gitDir);
|
|
1076
|
+
if (commonGitDir === gitDir || path.basename(commonGitDir) !== ".git") {
|
|
1077
|
+
return null;
|
|
1078
|
+
}
|
|
1079
|
+
const mainRepoRoot = path.dirname(commonGitDir);
|
|
1080
|
+
if (!existsSync(mainRepoRoot)) {
|
|
1081
|
+
return null;
|
|
1082
|
+
}
|
|
1083
|
+
return path.resolve(mainRepoRoot) === path.resolve(repoRoot) ? null : mainRepoRoot;
|
|
1084
|
+
}
|
|
1085
|
+
function resolveGitDir(repoRoot) {
|
|
1086
|
+
const gitPath = path.join(repoRoot, ".git");
|
|
1087
|
+
if (!existsSync(gitPath)) {
|
|
1088
|
+
return null;
|
|
1089
|
+
}
|
|
1090
|
+
try {
|
|
1091
|
+
const stat4 = statSync(gitPath);
|
|
1092
|
+
if (stat4.isDirectory()) {
|
|
1093
|
+
return gitPath;
|
|
1094
|
+
}
|
|
1095
|
+
if (stat4.isFile()) {
|
|
1096
|
+
const content = readFileSync(gitPath, "utf-8").trim();
|
|
1097
|
+
const match = content.match(/^gitdir:\s*(.+)$/);
|
|
1098
|
+
if (match) {
|
|
1099
|
+
const gitdir = match[1];
|
|
1100
|
+
const resolvedPath = path.isAbsolute(gitdir) ? gitdir : path.resolve(repoRoot, gitdir);
|
|
1101
|
+
if (existsSync(resolvedPath)) {
|
|
1102
|
+
return resolvedPath;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
} catch {
|
|
1107
|
+
}
|
|
1108
|
+
return null;
|
|
1109
|
+
}
|
|
1110
|
+
function isGitRepo(dir) {
|
|
1111
|
+
return resolveGitDir(dir) !== null;
|
|
1112
|
+
}
|
|
1113
|
+
function getCurrentBranch(repoRoot) {
|
|
1114
|
+
const gitDir = resolveGitDir(repoRoot);
|
|
1115
|
+
if (!gitDir) {
|
|
1116
|
+
return null;
|
|
1117
|
+
}
|
|
1118
|
+
const headPath = path.join(gitDir, "HEAD");
|
|
1119
|
+
if (!existsSync(headPath)) {
|
|
1120
|
+
return null;
|
|
1121
|
+
}
|
|
1122
|
+
try {
|
|
1123
|
+
const headContent = readFileSync(headPath, "utf-8").trim();
|
|
1124
|
+
const match = headContent.match(/^ref: refs\/heads\/(.+)$/);
|
|
1125
|
+
if (match) {
|
|
1126
|
+
return match[1];
|
|
1127
|
+
}
|
|
1128
|
+
if (/^[0-9a-f]{40}$/i.test(headContent)) {
|
|
1129
|
+
return headContent.slice(0, 7);
|
|
1130
|
+
}
|
|
1131
|
+
return null;
|
|
1132
|
+
} catch {
|
|
1133
|
+
return null;
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
function getBaseBranch(repoRoot) {
|
|
1137
|
+
const gitDir = resolveGitDir(repoRoot);
|
|
1138
|
+
const refStoreDir = gitDir ? resolveCommonGitDir(gitDir) : null;
|
|
1139
|
+
const candidates = ["main", "master", "develop", "trunk"];
|
|
1140
|
+
if (refStoreDir) {
|
|
1141
|
+
for (const candidate of candidates) {
|
|
1142
|
+
const refPath = path.join(refStoreDir, "refs", "heads", candidate);
|
|
1143
|
+
if (existsSync(refPath)) {
|
|
1144
|
+
return candidate;
|
|
1145
|
+
}
|
|
1146
|
+
const packedRefs = readPackedRefs(refStoreDir);
|
|
1147
|
+
if (packedRefs.some((line) => line.endsWith(` refs/heads/${candidate}`))) {
|
|
1148
|
+
return candidate;
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
return getCurrentBranch(repoRoot) ?? "main";
|
|
1153
|
+
}
|
|
1154
|
+
function getBranchOrDefault(repoRoot) {
|
|
1155
|
+
if (!isGitRepo(repoRoot)) {
|
|
1156
|
+
return "default";
|
|
1157
|
+
}
|
|
1158
|
+
return getCurrentBranch(repoRoot) ?? "default";
|
|
1159
|
+
}
|
|
1160
|
+
function getHeadPath(repoRoot) {
|
|
1161
|
+
const gitDir = resolveGitDir(repoRoot);
|
|
1162
|
+
if (gitDir) {
|
|
1163
|
+
return path.join(gitDir, "HEAD");
|
|
1164
|
+
}
|
|
1165
|
+
return path.join(repoRoot, ".git", "HEAD");
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
// src/config/paths.ts
|
|
1169
|
+
var PROJECT_CONFIG_RELATIVE_PATH = path2.join(".opencode", "codebase-index.json");
|
|
1170
|
+
var PROJECT_INDEX_RELATIVE_PATH = path2.join(".opencode", "index");
|
|
1171
|
+
function resolveWorktreeFallbackPath(projectRoot, relativePath) {
|
|
1172
|
+
const mainRepoRoot = resolveWorktreeMainRepoRoot(projectRoot);
|
|
1173
|
+
if (!mainRepoRoot) {
|
|
1174
|
+
return null;
|
|
1175
|
+
}
|
|
1176
|
+
const fallbackPath = path2.join(mainRepoRoot, relativePath);
|
|
1177
|
+
return existsSync2(fallbackPath) ? fallbackPath : null;
|
|
1178
|
+
}
|
|
1179
|
+
function hasProjectConfig(projectRoot) {
|
|
1180
|
+
return existsSync2(path2.join(projectRoot, PROJECT_CONFIG_RELATIVE_PATH));
|
|
1181
|
+
}
|
|
1182
|
+
function getGlobalIndexPath() {
|
|
1183
|
+
return path2.join(os.homedir(), ".opencode", "global-index");
|
|
1184
|
+
}
|
|
1185
|
+
function resolveProjectConfigPath(projectRoot) {
|
|
1186
|
+
const localConfigPath = path2.join(projectRoot, PROJECT_CONFIG_RELATIVE_PATH);
|
|
1187
|
+
if (existsSync2(localConfigPath)) {
|
|
1188
|
+
return localConfigPath;
|
|
1189
|
+
}
|
|
1190
|
+
return resolveWorktreeFallbackPath(projectRoot, PROJECT_CONFIG_RELATIVE_PATH) ?? localConfigPath;
|
|
1191
|
+
}
|
|
1192
|
+
function resolveWritableProjectConfigPath(projectRoot) {
|
|
1193
|
+
return path2.join(projectRoot, PROJECT_CONFIG_RELATIVE_PATH);
|
|
1194
|
+
}
|
|
1195
|
+
function resolveProjectIndexPath(projectRoot, scope) {
|
|
1196
|
+
if (scope === "global") {
|
|
1197
|
+
return getGlobalIndexPath();
|
|
1198
|
+
}
|
|
1199
|
+
const localIndexPath = path2.join(projectRoot, PROJECT_INDEX_RELATIVE_PATH);
|
|
1200
|
+
if (existsSync2(localIndexPath)) {
|
|
1201
|
+
return localIndexPath;
|
|
1202
|
+
}
|
|
1203
|
+
if (hasProjectConfig(projectRoot)) {
|
|
1204
|
+
return localIndexPath;
|
|
1205
|
+
}
|
|
1206
|
+
return resolveWorktreeFallbackPath(projectRoot, PROJECT_INDEX_RELATIVE_PATH) ?? localIndexPath;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
// src/config/merger.ts
|
|
1020
1210
|
function loadJsonFile(filePath) {
|
|
1021
1211
|
try {
|
|
1022
|
-
if (
|
|
1023
|
-
const content =
|
|
1212
|
+
if (existsSync3(filePath)) {
|
|
1213
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
1024
1214
|
return JSON.parse(content);
|
|
1025
1215
|
}
|
|
1026
1216
|
} catch {
|
|
1027
1217
|
}
|
|
1028
1218
|
return null;
|
|
1029
1219
|
}
|
|
1220
|
+
function normalizeRelativeConfigPath(candidate) {
|
|
1221
|
+
return candidate.replace(/\\/g, "/");
|
|
1222
|
+
}
|
|
1223
|
+
function isWithinRoot(rootDir, targetPath) {
|
|
1224
|
+
const relativePath = path3.relative(rootDir, targetPath);
|
|
1225
|
+
return relativePath === "" || !relativePath.startsWith("..") && !path3.isAbsolute(relativePath);
|
|
1226
|
+
}
|
|
1227
|
+
function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
|
|
1228
|
+
if (!Array.isArray(values)) {
|
|
1229
|
+
return [];
|
|
1230
|
+
}
|
|
1231
|
+
return values.filter((value) => typeof value === "string").map((value) => {
|
|
1232
|
+
const trimmed = value.trim();
|
|
1233
|
+
if (!trimmed) {
|
|
1234
|
+
return trimmed;
|
|
1235
|
+
}
|
|
1236
|
+
if (path3.isAbsolute(trimmed)) {
|
|
1237
|
+
if (isWithinRoot(sourceRoot, trimmed)) {
|
|
1238
|
+
return normalizeRelativeConfigPath(path3.normalize(path3.relative(sourceRoot, trimmed) || "."));
|
|
1239
|
+
}
|
|
1240
|
+
return path3.normalize(trimmed);
|
|
1241
|
+
}
|
|
1242
|
+
const resolvedFromSource = path3.resolve(sourceRoot, trimmed);
|
|
1243
|
+
if (isWithinRoot(sourceRoot, resolvedFromSource)) {
|
|
1244
|
+
return normalizeRelativeConfigPath(path3.normalize(trimmed));
|
|
1245
|
+
}
|
|
1246
|
+
return normalizeRelativeConfigPath(path3.normalize(path3.relative(targetRoot, resolvedFromSource)));
|
|
1247
|
+
}).filter(Boolean);
|
|
1248
|
+
}
|
|
1249
|
+
function materializeLocalProjectConfig(projectRoot, config) {
|
|
1250
|
+
const localConfigPath = path3.join(projectRoot, ".opencode", "codebase-index.json");
|
|
1251
|
+
mkdirSync(path3.dirname(localConfigPath), { recursive: true });
|
|
1252
|
+
writeFileSync(localConfigPath, JSON.stringify(config, null, 2), "utf-8");
|
|
1253
|
+
return localConfigPath;
|
|
1254
|
+
}
|
|
1255
|
+
function loadProjectConfigLayer(projectRoot) {
|
|
1256
|
+
const projectConfigPath = resolveProjectConfigPath(projectRoot);
|
|
1257
|
+
const projectConfig = loadJsonFile(projectConfigPath);
|
|
1258
|
+
if (!projectConfig) {
|
|
1259
|
+
return {};
|
|
1260
|
+
}
|
|
1261
|
+
const normalizedConfig = { ...projectConfig };
|
|
1262
|
+
const projectConfigBaseDir = path3.dirname(path3.dirname(projectConfigPath));
|
|
1263
|
+
if (Array.isArray(normalizedConfig.knowledgeBases)) {
|
|
1264
|
+
normalizedConfig.knowledgeBases = resolveInheritedKnowledgeBaseEntries(
|
|
1265
|
+
normalizedConfig.knowledgeBases,
|
|
1266
|
+
projectConfigBaseDir,
|
|
1267
|
+
projectRoot
|
|
1268
|
+
);
|
|
1269
|
+
}
|
|
1270
|
+
return normalizedConfig;
|
|
1271
|
+
}
|
|
1030
1272
|
function loadMergedConfig(projectRoot) {
|
|
1031
|
-
const globalConfigPath =
|
|
1273
|
+
const globalConfigPath = os2.homedir() + "/.config/opencode/codebase-index.json";
|
|
1032
1274
|
const globalConfig = loadJsonFile(globalConfigPath);
|
|
1033
|
-
const projectConfigPath =
|
|
1275
|
+
const projectConfigPath = resolveProjectConfigPath(projectRoot);
|
|
1034
1276
|
const projectConfig = loadJsonFile(projectConfigPath);
|
|
1277
|
+
const normalizedProjectConfig = loadProjectConfigLayer(projectRoot);
|
|
1035
1278
|
if (!globalConfig && !projectConfig) {
|
|
1036
1279
|
return {};
|
|
1037
1280
|
}
|
|
@@ -1039,56 +1282,56 @@ function loadMergedConfig(projectRoot) {
|
|
|
1039
1282
|
return globalConfig;
|
|
1040
1283
|
}
|
|
1041
1284
|
if (!globalConfig && projectConfig) {
|
|
1042
|
-
return
|
|
1285
|
+
return normalizedProjectConfig;
|
|
1043
1286
|
}
|
|
1044
1287
|
const merged = { ...globalConfig };
|
|
1045
|
-
if (projectConfig && "embeddingProvider" in
|
|
1046
|
-
merged.embeddingProvider =
|
|
1288
|
+
if (projectConfig && "embeddingProvider" in normalizedProjectConfig) {
|
|
1289
|
+
merged.embeddingProvider = normalizedProjectConfig.embeddingProvider;
|
|
1047
1290
|
} else if (globalConfig && globalConfig.embeddingProvider) {
|
|
1048
1291
|
merged.embeddingProvider = globalConfig.embeddingProvider;
|
|
1049
1292
|
}
|
|
1050
|
-
if (projectConfig && "customProvider" in
|
|
1051
|
-
merged.customProvider =
|
|
1293
|
+
if (projectConfig && "customProvider" in normalizedProjectConfig) {
|
|
1294
|
+
merged.customProvider = normalizedProjectConfig.customProvider;
|
|
1052
1295
|
} else if (globalConfig && globalConfig.customProvider) {
|
|
1053
1296
|
merged.customProvider = globalConfig.customProvider;
|
|
1054
1297
|
}
|
|
1055
|
-
if (projectConfig && "embeddingModel" in
|
|
1056
|
-
merged.embeddingModel =
|
|
1298
|
+
if (projectConfig && "embeddingModel" in normalizedProjectConfig) {
|
|
1299
|
+
merged.embeddingModel = normalizedProjectConfig.embeddingModel;
|
|
1057
1300
|
} else if (globalConfig && globalConfig.embeddingModel) {
|
|
1058
1301
|
merged.embeddingModel = globalConfig.embeddingModel;
|
|
1059
1302
|
}
|
|
1060
|
-
if (projectConfig && "reranker" in
|
|
1061
|
-
merged.reranker =
|
|
1303
|
+
if (projectConfig && "reranker" in normalizedProjectConfig) {
|
|
1304
|
+
merged.reranker = normalizedProjectConfig.reranker;
|
|
1062
1305
|
} else if (globalConfig && globalConfig.reranker) {
|
|
1063
1306
|
merged.reranker = globalConfig.reranker;
|
|
1064
1307
|
}
|
|
1065
|
-
if (projectConfig && "include" in
|
|
1066
|
-
merged.include =
|
|
1308
|
+
if (projectConfig && "include" in normalizedProjectConfig) {
|
|
1309
|
+
merged.include = normalizedProjectConfig.include;
|
|
1067
1310
|
} else if (globalConfig && globalConfig.include) {
|
|
1068
1311
|
merged.include = globalConfig.include;
|
|
1069
1312
|
}
|
|
1070
|
-
if (projectConfig && "exclude" in
|
|
1071
|
-
merged.exclude =
|
|
1313
|
+
if (projectConfig && "exclude" in normalizedProjectConfig) {
|
|
1314
|
+
merged.exclude = normalizedProjectConfig.exclude;
|
|
1072
1315
|
} else if (globalConfig && globalConfig.exclude) {
|
|
1073
1316
|
merged.exclude = globalConfig.exclude;
|
|
1074
1317
|
}
|
|
1075
|
-
if (projectConfig && "indexing" in
|
|
1076
|
-
merged.indexing =
|
|
1318
|
+
if (projectConfig && "indexing" in normalizedProjectConfig) {
|
|
1319
|
+
merged.indexing = normalizedProjectConfig.indexing;
|
|
1077
1320
|
} else if (globalConfig && globalConfig.indexing) {
|
|
1078
1321
|
merged.indexing = globalConfig.indexing;
|
|
1079
1322
|
}
|
|
1080
|
-
if (projectConfig && "search" in
|
|
1081
|
-
merged.search =
|
|
1323
|
+
if (projectConfig && "search" in normalizedProjectConfig) {
|
|
1324
|
+
merged.search = normalizedProjectConfig.search;
|
|
1082
1325
|
} else if (globalConfig && globalConfig.search) {
|
|
1083
1326
|
merged.search = globalConfig.search;
|
|
1084
1327
|
}
|
|
1085
|
-
if (projectConfig && "debug" in
|
|
1086
|
-
merged.debug =
|
|
1328
|
+
if (projectConfig && "debug" in normalizedProjectConfig) {
|
|
1329
|
+
merged.debug = normalizedProjectConfig.debug;
|
|
1087
1330
|
} else if (globalConfig && globalConfig.debug) {
|
|
1088
1331
|
merged.debug = globalConfig.debug;
|
|
1089
1332
|
}
|
|
1090
|
-
if (projectConfig && "scope" in
|
|
1091
|
-
merged.scope =
|
|
1333
|
+
if (projectConfig && "scope" in normalizedProjectConfig) {
|
|
1334
|
+
merged.scope = normalizedProjectConfig.scope;
|
|
1092
1335
|
} else if (globalConfig && "scope" in globalConfig) {
|
|
1093
1336
|
merged.scope = globalConfig.scope;
|
|
1094
1337
|
}
|
|
@@ -1097,11 +1340,11 @@ function loadMergedConfig(projectRoot) {
|
|
|
1097
1340
|
if (key === "embeddingProvider" || key === "customProvider" || key === "embeddingModel" || key === "reranker" || key === "include" || key === "exclude" || key === "indexing" || key === "search" || key === "debug" || key === "scope" || key === "knowledgeBases" || key === "additionalInclude") {
|
|
1098
1341
|
continue;
|
|
1099
1342
|
}
|
|
1100
|
-
merged[key] =
|
|
1343
|
+
merged[key] = normalizedProjectConfig[key];
|
|
1101
1344
|
}
|
|
1102
1345
|
}
|
|
1103
1346
|
const globalKbs = globalConfig && Array.isArray(globalConfig.knowledgeBases) ? globalConfig.knowledgeBases : [];
|
|
1104
|
-
const projectKbs = projectConfig
|
|
1347
|
+
const projectKbs = projectConfig ? Array.isArray(normalizedProjectConfig.knowledgeBases) ? normalizedProjectConfig.knowledgeBases : [] : [];
|
|
1105
1348
|
const allKbs = [...globalKbs, ...projectKbs];
|
|
1106
1349
|
const uniqueKbs = [...new Set(allKbs.map((p) => String(p).trim()))];
|
|
1107
1350
|
merged.knowledgeBases = uniqueKbs;
|
|
@@ -1203,7 +1446,7 @@ var ReaddirpStream = class extends Readable {
|
|
|
1203
1446
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
1204
1447
|
const statMethod = opts.lstat ? lstat : stat;
|
|
1205
1448
|
if (wantBigintFsStats) {
|
|
1206
|
-
this._stat = (
|
|
1449
|
+
this._stat = (path12) => statMethod(path12, { bigint: true });
|
|
1207
1450
|
} else {
|
|
1208
1451
|
this._stat = statMethod;
|
|
1209
1452
|
}
|
|
@@ -1228,8 +1471,8 @@ var ReaddirpStream = class extends Readable {
|
|
|
1228
1471
|
const par = this.parent;
|
|
1229
1472
|
const fil = par && par.files;
|
|
1230
1473
|
if (fil && fil.length > 0) {
|
|
1231
|
-
const { path:
|
|
1232
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
1474
|
+
const { path: path12, depth } = par;
|
|
1475
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path12));
|
|
1233
1476
|
const awaited = await Promise.all(slice);
|
|
1234
1477
|
for (const entry of awaited) {
|
|
1235
1478
|
if (!entry)
|
|
@@ -1269,21 +1512,21 @@ var ReaddirpStream = class extends Readable {
|
|
|
1269
1512
|
this.reading = false;
|
|
1270
1513
|
}
|
|
1271
1514
|
}
|
|
1272
|
-
async _exploreDir(
|
|
1515
|
+
async _exploreDir(path12, depth) {
|
|
1273
1516
|
let files;
|
|
1274
1517
|
try {
|
|
1275
|
-
files = await readdir(
|
|
1518
|
+
files = await readdir(path12, this._rdOptions);
|
|
1276
1519
|
} catch (error) {
|
|
1277
1520
|
this._onError(error);
|
|
1278
1521
|
}
|
|
1279
|
-
return { files, depth, path:
|
|
1522
|
+
return { files, depth, path: path12 };
|
|
1280
1523
|
}
|
|
1281
|
-
async _formatEntry(dirent,
|
|
1524
|
+
async _formatEntry(dirent, path12) {
|
|
1282
1525
|
let entry;
|
|
1283
|
-
const
|
|
1526
|
+
const basename5 = this._isDirent ? dirent.name : dirent;
|
|
1284
1527
|
try {
|
|
1285
|
-
const fullPath = presolve(pjoin(
|
|
1286
|
-
entry = { path: prelative(this._root, fullPath), fullPath, basename:
|
|
1528
|
+
const fullPath = presolve(pjoin(path12, basename5));
|
|
1529
|
+
entry = { path: prelative(this._root, fullPath), fullPath, basename: basename5 };
|
|
1287
1530
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
1288
1531
|
} catch (err) {
|
|
1289
1532
|
this._onError(err);
|
|
@@ -1682,16 +1925,16 @@ var delFromSet = (main, prop, item) => {
|
|
|
1682
1925
|
};
|
|
1683
1926
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
1684
1927
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
1685
|
-
function createFsWatchInstance(
|
|
1928
|
+
function createFsWatchInstance(path12, options, listener, errHandler, emitRaw) {
|
|
1686
1929
|
const handleEvent = (rawEvent, evPath) => {
|
|
1687
|
-
listener(
|
|
1688
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
1689
|
-
if (evPath &&
|
|
1690
|
-
fsWatchBroadcast(sp.resolve(
|
|
1930
|
+
listener(path12);
|
|
1931
|
+
emitRaw(rawEvent, evPath, { watchedPath: path12 });
|
|
1932
|
+
if (evPath && path12 !== evPath) {
|
|
1933
|
+
fsWatchBroadcast(sp.resolve(path12, evPath), KEY_LISTENERS, sp.join(path12, evPath));
|
|
1691
1934
|
}
|
|
1692
1935
|
};
|
|
1693
1936
|
try {
|
|
1694
|
-
return fs_watch(
|
|
1937
|
+
return fs_watch(path12, {
|
|
1695
1938
|
persistent: options.persistent
|
|
1696
1939
|
}, handleEvent);
|
|
1697
1940
|
} catch (error) {
|
|
@@ -1707,12 +1950,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
1707
1950
|
listener(val1, val2, val3);
|
|
1708
1951
|
});
|
|
1709
1952
|
};
|
|
1710
|
-
var setFsWatchListener = (
|
|
1953
|
+
var setFsWatchListener = (path12, fullPath, options, handlers) => {
|
|
1711
1954
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
1712
1955
|
let cont = FsWatchInstances.get(fullPath);
|
|
1713
1956
|
let watcher;
|
|
1714
1957
|
if (!options.persistent) {
|
|
1715
|
-
watcher = createFsWatchInstance(
|
|
1958
|
+
watcher = createFsWatchInstance(path12, options, listener, errHandler, rawEmitter);
|
|
1716
1959
|
if (!watcher)
|
|
1717
1960
|
return;
|
|
1718
1961
|
return watcher.close.bind(watcher);
|
|
@@ -1723,7 +1966,7 @@ var setFsWatchListener = (path11, fullPath, options, handlers) => {
|
|
|
1723
1966
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
1724
1967
|
} else {
|
|
1725
1968
|
watcher = createFsWatchInstance(
|
|
1726
|
-
|
|
1969
|
+
path12,
|
|
1727
1970
|
options,
|
|
1728
1971
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
1729
1972
|
errHandler,
|
|
@@ -1738,7 +1981,7 @@ var setFsWatchListener = (path11, fullPath, options, handlers) => {
|
|
|
1738
1981
|
cont.watcherUnusable = true;
|
|
1739
1982
|
if (isWindows && error.code === "EPERM") {
|
|
1740
1983
|
try {
|
|
1741
|
-
const fd = await open(
|
|
1984
|
+
const fd = await open(path12, "r");
|
|
1742
1985
|
await fd.close();
|
|
1743
1986
|
broadcastErr(error);
|
|
1744
1987
|
} catch (err) {
|
|
@@ -1769,7 +2012,7 @@ var setFsWatchListener = (path11, fullPath, options, handlers) => {
|
|
|
1769
2012
|
};
|
|
1770
2013
|
};
|
|
1771
2014
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
1772
|
-
var setFsWatchFileListener = (
|
|
2015
|
+
var setFsWatchFileListener = (path12, fullPath, options, handlers) => {
|
|
1773
2016
|
const { listener, rawEmitter } = handlers;
|
|
1774
2017
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
1775
2018
|
const copts = cont && cont.options;
|
|
@@ -1791,7 +2034,7 @@ var setFsWatchFileListener = (path11, fullPath, options, handlers) => {
|
|
|
1791
2034
|
});
|
|
1792
2035
|
const currmtime = curr.mtimeMs;
|
|
1793
2036
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
1794
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
2037
|
+
foreach(cont.listeners, (listener2) => listener2(path12, curr));
|
|
1795
2038
|
}
|
|
1796
2039
|
})
|
|
1797
2040
|
};
|
|
@@ -1821,13 +2064,13 @@ var NodeFsHandler = class {
|
|
|
1821
2064
|
* @param listener on fs change
|
|
1822
2065
|
* @returns closer for the watcher instance
|
|
1823
2066
|
*/
|
|
1824
|
-
_watchWithNodeFs(
|
|
2067
|
+
_watchWithNodeFs(path12, listener) {
|
|
1825
2068
|
const opts = this.fsw.options;
|
|
1826
|
-
const directory = sp.dirname(
|
|
1827
|
-
const
|
|
2069
|
+
const directory = sp.dirname(path12);
|
|
2070
|
+
const basename5 = sp.basename(path12);
|
|
1828
2071
|
const parent = this.fsw._getWatchedDir(directory);
|
|
1829
|
-
parent.add(
|
|
1830
|
-
const absolutePath = sp.resolve(
|
|
2072
|
+
parent.add(basename5);
|
|
2073
|
+
const absolutePath = sp.resolve(path12);
|
|
1831
2074
|
const options = {
|
|
1832
2075
|
persistent: opts.persistent
|
|
1833
2076
|
};
|
|
@@ -1836,13 +2079,13 @@ var NodeFsHandler = class {
|
|
|
1836
2079
|
let closer;
|
|
1837
2080
|
if (opts.usePolling) {
|
|
1838
2081
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
1839
|
-
options.interval = enableBin && isBinaryPath(
|
|
1840
|
-
closer = setFsWatchFileListener(
|
|
2082
|
+
options.interval = enableBin && isBinaryPath(basename5) ? opts.binaryInterval : opts.interval;
|
|
2083
|
+
closer = setFsWatchFileListener(path12, absolutePath, options, {
|
|
1841
2084
|
listener,
|
|
1842
2085
|
rawEmitter: this.fsw._emitRaw
|
|
1843
2086
|
});
|
|
1844
2087
|
} else {
|
|
1845
|
-
closer = setFsWatchListener(
|
|
2088
|
+
closer = setFsWatchListener(path12, absolutePath, options, {
|
|
1846
2089
|
listener,
|
|
1847
2090
|
errHandler: this._boundHandleError,
|
|
1848
2091
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -1858,13 +2101,13 @@ var NodeFsHandler = class {
|
|
|
1858
2101
|
if (this.fsw.closed) {
|
|
1859
2102
|
return;
|
|
1860
2103
|
}
|
|
1861
|
-
const
|
|
1862
|
-
const
|
|
1863
|
-
const parent = this.fsw._getWatchedDir(
|
|
2104
|
+
const dirname9 = sp.dirname(file);
|
|
2105
|
+
const basename5 = sp.basename(file);
|
|
2106
|
+
const parent = this.fsw._getWatchedDir(dirname9);
|
|
1864
2107
|
let prevStats = stats;
|
|
1865
|
-
if (parent.has(
|
|
2108
|
+
if (parent.has(basename5))
|
|
1866
2109
|
return;
|
|
1867
|
-
const listener = async (
|
|
2110
|
+
const listener = async (path12, newStats) => {
|
|
1868
2111
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
1869
2112
|
return;
|
|
1870
2113
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -1878,18 +2121,18 @@ var NodeFsHandler = class {
|
|
|
1878
2121
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
1879
2122
|
}
|
|
1880
2123
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
1881
|
-
this.fsw._closeFile(
|
|
2124
|
+
this.fsw._closeFile(path12);
|
|
1882
2125
|
prevStats = newStats2;
|
|
1883
2126
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
1884
2127
|
if (closer2)
|
|
1885
|
-
this.fsw._addPathCloser(
|
|
2128
|
+
this.fsw._addPathCloser(path12, closer2);
|
|
1886
2129
|
} else {
|
|
1887
2130
|
prevStats = newStats2;
|
|
1888
2131
|
}
|
|
1889
2132
|
} catch (error) {
|
|
1890
|
-
this.fsw._remove(
|
|
2133
|
+
this.fsw._remove(dirname9, basename5);
|
|
1891
2134
|
}
|
|
1892
|
-
} else if (parent.has(
|
|
2135
|
+
} else if (parent.has(basename5)) {
|
|
1893
2136
|
const at = newStats.atimeMs;
|
|
1894
2137
|
const mt = newStats.mtimeMs;
|
|
1895
2138
|
if (!at || at <= mt || mt !== prevStats.mtimeMs) {
|
|
@@ -1914,7 +2157,7 @@ var NodeFsHandler = class {
|
|
|
1914
2157
|
* @param item basename of this item
|
|
1915
2158
|
* @returns true if no more processing is needed for this entry.
|
|
1916
2159
|
*/
|
|
1917
|
-
async _handleSymlink(entry, directory,
|
|
2160
|
+
async _handleSymlink(entry, directory, path12, item) {
|
|
1918
2161
|
if (this.fsw.closed) {
|
|
1919
2162
|
return;
|
|
1920
2163
|
}
|
|
@@ -1924,7 +2167,7 @@ var NodeFsHandler = class {
|
|
|
1924
2167
|
this.fsw._incrReadyCount();
|
|
1925
2168
|
let linkPath;
|
|
1926
2169
|
try {
|
|
1927
|
-
linkPath = await fsrealpath(
|
|
2170
|
+
linkPath = await fsrealpath(path12);
|
|
1928
2171
|
} catch (e) {
|
|
1929
2172
|
this.fsw._emitReady();
|
|
1930
2173
|
return true;
|
|
@@ -1934,12 +2177,12 @@ var NodeFsHandler = class {
|
|
|
1934
2177
|
if (dir.has(item)) {
|
|
1935
2178
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
1936
2179
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
1937
|
-
this.fsw._emit(EV.CHANGE,
|
|
2180
|
+
this.fsw._emit(EV.CHANGE, path12, entry.stats);
|
|
1938
2181
|
}
|
|
1939
2182
|
} else {
|
|
1940
2183
|
dir.add(item);
|
|
1941
2184
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
1942
|
-
this.fsw._emit(EV.ADD,
|
|
2185
|
+
this.fsw._emit(EV.ADD, path12, entry.stats);
|
|
1943
2186
|
}
|
|
1944
2187
|
this.fsw._emitReady();
|
|
1945
2188
|
return true;
|
|
@@ -1969,9 +2212,9 @@ var NodeFsHandler = class {
|
|
|
1969
2212
|
return;
|
|
1970
2213
|
}
|
|
1971
2214
|
const item = entry.path;
|
|
1972
|
-
let
|
|
2215
|
+
let path12 = sp.join(directory, item);
|
|
1973
2216
|
current.add(item);
|
|
1974
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
2217
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path12, item)) {
|
|
1975
2218
|
return;
|
|
1976
2219
|
}
|
|
1977
2220
|
if (this.fsw.closed) {
|
|
@@ -1980,11 +2223,11 @@ var NodeFsHandler = class {
|
|
|
1980
2223
|
}
|
|
1981
2224
|
if (item === target || !target && !previous.has(item)) {
|
|
1982
2225
|
this.fsw._incrReadyCount();
|
|
1983
|
-
|
|
1984
|
-
this._addToNodeFs(
|
|
2226
|
+
path12 = sp.join(dir, sp.relative(dir, path12));
|
|
2227
|
+
this._addToNodeFs(path12, initialAdd, wh, depth + 1);
|
|
1985
2228
|
}
|
|
1986
2229
|
}).on(EV.ERROR, this._boundHandleError);
|
|
1987
|
-
return new Promise((
|
|
2230
|
+
return new Promise((resolve9, reject) => {
|
|
1988
2231
|
if (!stream)
|
|
1989
2232
|
return reject();
|
|
1990
2233
|
stream.once(STR_END, () => {
|
|
@@ -1993,7 +2236,7 @@ var NodeFsHandler = class {
|
|
|
1993
2236
|
return;
|
|
1994
2237
|
}
|
|
1995
2238
|
const wasThrottled = throttler ? throttler.clear() : false;
|
|
1996
|
-
|
|
2239
|
+
resolve9(void 0);
|
|
1997
2240
|
previous.getChildren().filter((item) => {
|
|
1998
2241
|
return item !== directory && !current.has(item);
|
|
1999
2242
|
}).forEach((item) => {
|
|
@@ -2050,13 +2293,13 @@ var NodeFsHandler = class {
|
|
|
2050
2293
|
* @param depth Child path actually targeted for watch
|
|
2051
2294
|
* @param target Child path actually targeted for watch
|
|
2052
2295
|
*/
|
|
2053
|
-
async _addToNodeFs(
|
|
2296
|
+
async _addToNodeFs(path12, initialAdd, priorWh, depth, target) {
|
|
2054
2297
|
const ready = this.fsw._emitReady;
|
|
2055
|
-
if (this.fsw._isIgnored(
|
|
2298
|
+
if (this.fsw._isIgnored(path12) || this.fsw.closed) {
|
|
2056
2299
|
ready();
|
|
2057
2300
|
return false;
|
|
2058
2301
|
}
|
|
2059
|
-
const wh = this.fsw._getWatchHelpers(
|
|
2302
|
+
const wh = this.fsw._getWatchHelpers(path12);
|
|
2060
2303
|
if (priorWh) {
|
|
2061
2304
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
2062
2305
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -2072,8 +2315,8 @@ var NodeFsHandler = class {
|
|
|
2072
2315
|
const follow = this.fsw.options.followSymlinks;
|
|
2073
2316
|
let closer;
|
|
2074
2317
|
if (stats.isDirectory()) {
|
|
2075
|
-
const absPath = sp.resolve(
|
|
2076
|
-
const targetPath = follow ? await fsrealpath(
|
|
2318
|
+
const absPath = sp.resolve(path12);
|
|
2319
|
+
const targetPath = follow ? await fsrealpath(path12) : path12;
|
|
2077
2320
|
if (this.fsw.closed)
|
|
2078
2321
|
return;
|
|
2079
2322
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -2083,29 +2326,29 @@ var NodeFsHandler = class {
|
|
|
2083
2326
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
2084
2327
|
}
|
|
2085
2328
|
} else if (stats.isSymbolicLink()) {
|
|
2086
|
-
const targetPath = follow ? await fsrealpath(
|
|
2329
|
+
const targetPath = follow ? await fsrealpath(path12) : path12;
|
|
2087
2330
|
if (this.fsw.closed)
|
|
2088
2331
|
return;
|
|
2089
2332
|
const parent = sp.dirname(wh.watchPath);
|
|
2090
2333
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
2091
2334
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
2092
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
2335
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path12, wh, targetPath);
|
|
2093
2336
|
if (this.fsw.closed)
|
|
2094
2337
|
return;
|
|
2095
2338
|
if (targetPath !== void 0) {
|
|
2096
|
-
this.fsw._symlinkPaths.set(sp.resolve(
|
|
2339
|
+
this.fsw._symlinkPaths.set(sp.resolve(path12), targetPath);
|
|
2097
2340
|
}
|
|
2098
2341
|
} else {
|
|
2099
2342
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
2100
2343
|
}
|
|
2101
2344
|
ready();
|
|
2102
2345
|
if (closer)
|
|
2103
|
-
this.fsw._addPathCloser(
|
|
2346
|
+
this.fsw._addPathCloser(path12, closer);
|
|
2104
2347
|
return false;
|
|
2105
2348
|
} catch (error) {
|
|
2106
2349
|
if (this.fsw._handleError(error)) {
|
|
2107
2350
|
ready();
|
|
2108
|
-
return
|
|
2351
|
+
return path12;
|
|
2109
2352
|
}
|
|
2110
2353
|
}
|
|
2111
2354
|
}
|
|
@@ -2137,35 +2380,35 @@ function createPattern(matcher) {
|
|
|
2137
2380
|
if (matcher.path === string)
|
|
2138
2381
|
return true;
|
|
2139
2382
|
if (matcher.recursive) {
|
|
2140
|
-
const
|
|
2141
|
-
if (!
|
|
2383
|
+
const relative8 = sp2.relative(matcher.path, string);
|
|
2384
|
+
if (!relative8) {
|
|
2142
2385
|
return false;
|
|
2143
2386
|
}
|
|
2144
|
-
return !
|
|
2387
|
+
return !relative8.startsWith("..") && !sp2.isAbsolute(relative8);
|
|
2145
2388
|
}
|
|
2146
2389
|
return false;
|
|
2147
2390
|
};
|
|
2148
2391
|
}
|
|
2149
2392
|
return () => false;
|
|
2150
2393
|
}
|
|
2151
|
-
function normalizePath(
|
|
2152
|
-
if (typeof
|
|
2394
|
+
function normalizePath(path12) {
|
|
2395
|
+
if (typeof path12 !== "string")
|
|
2153
2396
|
throw new Error("string expected");
|
|
2154
|
-
|
|
2155
|
-
|
|
2397
|
+
path12 = sp2.normalize(path12);
|
|
2398
|
+
path12 = path12.replace(/\\/g, "/");
|
|
2156
2399
|
let prepend = false;
|
|
2157
|
-
if (
|
|
2400
|
+
if (path12.startsWith("//"))
|
|
2158
2401
|
prepend = true;
|
|
2159
|
-
|
|
2402
|
+
path12 = path12.replace(DOUBLE_SLASH_RE, "/");
|
|
2160
2403
|
if (prepend)
|
|
2161
|
-
|
|
2162
|
-
return
|
|
2404
|
+
path12 = "/" + path12;
|
|
2405
|
+
return path12;
|
|
2163
2406
|
}
|
|
2164
2407
|
function matchPatterns(patterns, testString, stats) {
|
|
2165
|
-
const
|
|
2408
|
+
const path12 = normalizePath(testString);
|
|
2166
2409
|
for (let index = 0; index < patterns.length; index++) {
|
|
2167
2410
|
const pattern = patterns[index];
|
|
2168
|
-
if (pattern(
|
|
2411
|
+
if (pattern(path12, stats)) {
|
|
2169
2412
|
return true;
|
|
2170
2413
|
}
|
|
2171
2414
|
}
|
|
@@ -2203,19 +2446,19 @@ var toUnix = (string) => {
|
|
|
2203
2446
|
}
|
|
2204
2447
|
return str;
|
|
2205
2448
|
};
|
|
2206
|
-
var normalizePathToUnix = (
|
|
2207
|
-
var normalizeIgnored = (cwd = "") => (
|
|
2208
|
-
if (typeof
|
|
2209
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
2449
|
+
var normalizePathToUnix = (path12) => toUnix(sp2.normalize(toUnix(path12)));
|
|
2450
|
+
var normalizeIgnored = (cwd = "") => (path12) => {
|
|
2451
|
+
if (typeof path12 === "string") {
|
|
2452
|
+
return normalizePathToUnix(sp2.isAbsolute(path12) ? path12 : sp2.join(cwd, path12));
|
|
2210
2453
|
} else {
|
|
2211
|
-
return
|
|
2454
|
+
return path12;
|
|
2212
2455
|
}
|
|
2213
2456
|
};
|
|
2214
|
-
var getAbsolutePath = (
|
|
2215
|
-
if (sp2.isAbsolute(
|
|
2216
|
-
return
|
|
2457
|
+
var getAbsolutePath = (path12, cwd) => {
|
|
2458
|
+
if (sp2.isAbsolute(path12)) {
|
|
2459
|
+
return path12;
|
|
2217
2460
|
}
|
|
2218
|
-
return sp2.join(cwd,
|
|
2461
|
+
return sp2.join(cwd, path12);
|
|
2219
2462
|
};
|
|
2220
2463
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
2221
2464
|
var DirEntry = class {
|
|
@@ -2280,10 +2523,10 @@ var WatchHelper = class {
|
|
|
2280
2523
|
dirParts;
|
|
2281
2524
|
followSymlinks;
|
|
2282
2525
|
statMethod;
|
|
2283
|
-
constructor(
|
|
2526
|
+
constructor(path12, follow, fsw) {
|
|
2284
2527
|
this.fsw = fsw;
|
|
2285
|
-
const watchPath =
|
|
2286
|
-
this.path =
|
|
2528
|
+
const watchPath = path12;
|
|
2529
|
+
this.path = path12 = path12.replace(REPLACER_RE, "");
|
|
2287
2530
|
this.watchPath = watchPath;
|
|
2288
2531
|
this.fullWatchPath = sp2.resolve(watchPath);
|
|
2289
2532
|
this.dirParts = [];
|
|
@@ -2423,20 +2666,20 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2423
2666
|
this._closePromise = void 0;
|
|
2424
2667
|
let paths = unifyPaths(paths_);
|
|
2425
2668
|
if (cwd) {
|
|
2426
|
-
paths = paths.map((
|
|
2427
|
-
const absPath = getAbsolutePath(
|
|
2669
|
+
paths = paths.map((path12) => {
|
|
2670
|
+
const absPath = getAbsolutePath(path12, cwd);
|
|
2428
2671
|
return absPath;
|
|
2429
2672
|
});
|
|
2430
2673
|
}
|
|
2431
|
-
paths.forEach((
|
|
2432
|
-
this._removeIgnoredPath(
|
|
2674
|
+
paths.forEach((path12) => {
|
|
2675
|
+
this._removeIgnoredPath(path12);
|
|
2433
2676
|
});
|
|
2434
2677
|
this._userIgnored = void 0;
|
|
2435
2678
|
if (!this._readyCount)
|
|
2436
2679
|
this._readyCount = 0;
|
|
2437
2680
|
this._readyCount += paths.length;
|
|
2438
|
-
Promise.all(paths.map(async (
|
|
2439
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
2681
|
+
Promise.all(paths.map(async (path12) => {
|
|
2682
|
+
const res = await this._nodeFsHandler._addToNodeFs(path12, !_internal, void 0, 0, _origAdd);
|
|
2440
2683
|
if (res)
|
|
2441
2684
|
this._emitReady();
|
|
2442
2685
|
return res;
|
|
@@ -2458,17 +2701,17 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2458
2701
|
return this;
|
|
2459
2702
|
const paths = unifyPaths(paths_);
|
|
2460
2703
|
const { cwd } = this.options;
|
|
2461
|
-
paths.forEach((
|
|
2462
|
-
if (!sp2.isAbsolute(
|
|
2704
|
+
paths.forEach((path12) => {
|
|
2705
|
+
if (!sp2.isAbsolute(path12) && !this._closers.has(path12)) {
|
|
2463
2706
|
if (cwd)
|
|
2464
|
-
|
|
2465
|
-
|
|
2707
|
+
path12 = sp2.join(cwd, path12);
|
|
2708
|
+
path12 = sp2.resolve(path12);
|
|
2466
2709
|
}
|
|
2467
|
-
this._closePath(
|
|
2468
|
-
this._addIgnoredPath(
|
|
2469
|
-
if (this._watched.has(
|
|
2710
|
+
this._closePath(path12);
|
|
2711
|
+
this._addIgnoredPath(path12);
|
|
2712
|
+
if (this._watched.has(path12)) {
|
|
2470
2713
|
this._addIgnoredPath({
|
|
2471
|
-
path:
|
|
2714
|
+
path: path12,
|
|
2472
2715
|
recursive: true
|
|
2473
2716
|
});
|
|
2474
2717
|
}
|
|
@@ -2532,38 +2775,38 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2532
2775
|
* @param stats arguments to be passed with event
|
|
2533
2776
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
2534
2777
|
*/
|
|
2535
|
-
async _emit(event,
|
|
2778
|
+
async _emit(event, path12, stats) {
|
|
2536
2779
|
if (this.closed)
|
|
2537
2780
|
return;
|
|
2538
2781
|
const opts = this.options;
|
|
2539
2782
|
if (isWindows)
|
|
2540
|
-
|
|
2783
|
+
path12 = sp2.normalize(path12);
|
|
2541
2784
|
if (opts.cwd)
|
|
2542
|
-
|
|
2543
|
-
const args = [
|
|
2785
|
+
path12 = sp2.relative(opts.cwd, path12);
|
|
2786
|
+
const args = [path12];
|
|
2544
2787
|
if (stats != null)
|
|
2545
2788
|
args.push(stats);
|
|
2546
2789
|
const awf = opts.awaitWriteFinish;
|
|
2547
2790
|
let pw;
|
|
2548
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
2791
|
+
if (awf && (pw = this._pendingWrites.get(path12))) {
|
|
2549
2792
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
2550
2793
|
return this;
|
|
2551
2794
|
}
|
|
2552
2795
|
if (opts.atomic) {
|
|
2553
2796
|
if (event === EVENTS.UNLINK) {
|
|
2554
|
-
this._pendingUnlinks.set(
|
|
2797
|
+
this._pendingUnlinks.set(path12, [event, ...args]);
|
|
2555
2798
|
setTimeout(() => {
|
|
2556
|
-
this._pendingUnlinks.forEach((entry,
|
|
2799
|
+
this._pendingUnlinks.forEach((entry, path13) => {
|
|
2557
2800
|
this.emit(...entry);
|
|
2558
2801
|
this.emit(EVENTS.ALL, ...entry);
|
|
2559
|
-
this._pendingUnlinks.delete(
|
|
2802
|
+
this._pendingUnlinks.delete(path13);
|
|
2560
2803
|
});
|
|
2561
2804
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
2562
2805
|
return this;
|
|
2563
2806
|
}
|
|
2564
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
2807
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path12)) {
|
|
2565
2808
|
event = EVENTS.CHANGE;
|
|
2566
|
-
this._pendingUnlinks.delete(
|
|
2809
|
+
this._pendingUnlinks.delete(path12);
|
|
2567
2810
|
}
|
|
2568
2811
|
}
|
|
2569
2812
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -2581,16 +2824,16 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2581
2824
|
this.emitWithAll(event, args);
|
|
2582
2825
|
}
|
|
2583
2826
|
};
|
|
2584
|
-
this._awaitWriteFinish(
|
|
2827
|
+
this._awaitWriteFinish(path12, awf.stabilityThreshold, event, awfEmit);
|
|
2585
2828
|
return this;
|
|
2586
2829
|
}
|
|
2587
2830
|
if (event === EVENTS.CHANGE) {
|
|
2588
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
2831
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path12, 50);
|
|
2589
2832
|
if (isThrottled)
|
|
2590
2833
|
return this;
|
|
2591
2834
|
}
|
|
2592
2835
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
2593
|
-
const fullPath = opts.cwd ? sp2.join(opts.cwd,
|
|
2836
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path12) : path12;
|
|
2594
2837
|
let stats2;
|
|
2595
2838
|
try {
|
|
2596
2839
|
stats2 = await stat3(fullPath);
|
|
@@ -2621,23 +2864,23 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2621
2864
|
* @param timeout duration of time to suppress duplicate actions
|
|
2622
2865
|
* @returns tracking object or false if action should be suppressed
|
|
2623
2866
|
*/
|
|
2624
|
-
_throttle(actionType,
|
|
2867
|
+
_throttle(actionType, path12, timeout) {
|
|
2625
2868
|
if (!this._throttled.has(actionType)) {
|
|
2626
2869
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
2627
2870
|
}
|
|
2628
2871
|
const action = this._throttled.get(actionType);
|
|
2629
2872
|
if (!action)
|
|
2630
2873
|
throw new Error("invalid throttle");
|
|
2631
|
-
const actionPath = action.get(
|
|
2874
|
+
const actionPath = action.get(path12);
|
|
2632
2875
|
if (actionPath) {
|
|
2633
2876
|
actionPath.count++;
|
|
2634
2877
|
return false;
|
|
2635
2878
|
}
|
|
2636
2879
|
let timeoutObject;
|
|
2637
2880
|
const clear = () => {
|
|
2638
|
-
const item = action.get(
|
|
2881
|
+
const item = action.get(path12);
|
|
2639
2882
|
const count = item ? item.count : 0;
|
|
2640
|
-
action.delete(
|
|
2883
|
+
action.delete(path12);
|
|
2641
2884
|
clearTimeout(timeoutObject);
|
|
2642
2885
|
if (item)
|
|
2643
2886
|
clearTimeout(item.timeoutObject);
|
|
@@ -2645,7 +2888,7 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2645
2888
|
};
|
|
2646
2889
|
timeoutObject = setTimeout(clear, timeout);
|
|
2647
2890
|
const thr = { timeoutObject, clear, count: 0 };
|
|
2648
|
-
action.set(
|
|
2891
|
+
action.set(path12, thr);
|
|
2649
2892
|
return thr;
|
|
2650
2893
|
}
|
|
2651
2894
|
_incrReadyCount() {
|
|
@@ -2659,44 +2902,44 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2659
2902
|
* @param event
|
|
2660
2903
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
2661
2904
|
*/
|
|
2662
|
-
_awaitWriteFinish(
|
|
2905
|
+
_awaitWriteFinish(path12, threshold, event, awfEmit) {
|
|
2663
2906
|
const awf = this.options.awaitWriteFinish;
|
|
2664
2907
|
if (typeof awf !== "object")
|
|
2665
2908
|
return;
|
|
2666
2909
|
const pollInterval = awf.pollInterval;
|
|
2667
2910
|
let timeoutHandler;
|
|
2668
|
-
let fullPath =
|
|
2669
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
2670
|
-
fullPath = sp2.join(this.options.cwd,
|
|
2911
|
+
let fullPath = path12;
|
|
2912
|
+
if (this.options.cwd && !sp2.isAbsolute(path12)) {
|
|
2913
|
+
fullPath = sp2.join(this.options.cwd, path12);
|
|
2671
2914
|
}
|
|
2672
2915
|
const now = /* @__PURE__ */ new Date();
|
|
2673
2916
|
const writes = this._pendingWrites;
|
|
2674
2917
|
function awaitWriteFinishFn(prevStat) {
|
|
2675
2918
|
statcb(fullPath, (err, curStat) => {
|
|
2676
|
-
if (err || !writes.has(
|
|
2919
|
+
if (err || !writes.has(path12)) {
|
|
2677
2920
|
if (err && err.code !== "ENOENT")
|
|
2678
2921
|
awfEmit(err);
|
|
2679
2922
|
return;
|
|
2680
2923
|
}
|
|
2681
2924
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
2682
2925
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
2683
|
-
writes.get(
|
|
2926
|
+
writes.get(path12).lastChange = now2;
|
|
2684
2927
|
}
|
|
2685
|
-
const pw = writes.get(
|
|
2928
|
+
const pw = writes.get(path12);
|
|
2686
2929
|
const df = now2 - pw.lastChange;
|
|
2687
2930
|
if (df >= threshold) {
|
|
2688
|
-
writes.delete(
|
|
2931
|
+
writes.delete(path12);
|
|
2689
2932
|
awfEmit(void 0, curStat);
|
|
2690
2933
|
} else {
|
|
2691
2934
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
2692
2935
|
}
|
|
2693
2936
|
});
|
|
2694
2937
|
}
|
|
2695
|
-
if (!writes.has(
|
|
2696
|
-
writes.set(
|
|
2938
|
+
if (!writes.has(path12)) {
|
|
2939
|
+
writes.set(path12, {
|
|
2697
2940
|
lastChange: now,
|
|
2698
2941
|
cancelWait: () => {
|
|
2699
|
-
writes.delete(
|
|
2942
|
+
writes.delete(path12);
|
|
2700
2943
|
clearTimeout(timeoutHandler);
|
|
2701
2944
|
return event;
|
|
2702
2945
|
}
|
|
@@ -2707,8 +2950,8 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2707
2950
|
/**
|
|
2708
2951
|
* Determines whether user has asked to ignore this path.
|
|
2709
2952
|
*/
|
|
2710
|
-
_isIgnored(
|
|
2711
|
-
if (this.options.atomic && DOT_RE.test(
|
|
2953
|
+
_isIgnored(path12, stats) {
|
|
2954
|
+
if (this.options.atomic && DOT_RE.test(path12))
|
|
2712
2955
|
return true;
|
|
2713
2956
|
if (!this._userIgnored) {
|
|
2714
2957
|
const { cwd } = this.options;
|
|
@@ -2718,17 +2961,17 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2718
2961
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
2719
2962
|
this._userIgnored = anymatch(list, void 0);
|
|
2720
2963
|
}
|
|
2721
|
-
return this._userIgnored(
|
|
2964
|
+
return this._userIgnored(path12, stats);
|
|
2722
2965
|
}
|
|
2723
|
-
_isntIgnored(
|
|
2724
|
-
return !this._isIgnored(
|
|
2966
|
+
_isntIgnored(path12, stat4) {
|
|
2967
|
+
return !this._isIgnored(path12, stat4);
|
|
2725
2968
|
}
|
|
2726
2969
|
/**
|
|
2727
2970
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
2728
2971
|
* @param path file or directory pattern being watched
|
|
2729
2972
|
*/
|
|
2730
|
-
_getWatchHelpers(
|
|
2731
|
-
return new WatchHelper(
|
|
2973
|
+
_getWatchHelpers(path12) {
|
|
2974
|
+
return new WatchHelper(path12, this.options.followSymlinks, this);
|
|
2732
2975
|
}
|
|
2733
2976
|
// Directory helpers
|
|
2734
2977
|
// -----------------
|
|
@@ -2760,63 +3003,63 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2760
3003
|
* @param item base path of item/directory
|
|
2761
3004
|
*/
|
|
2762
3005
|
_remove(directory, item, isDirectory) {
|
|
2763
|
-
const
|
|
2764
|
-
const fullPath = sp2.resolve(
|
|
2765
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
2766
|
-
if (!this._throttle("remove",
|
|
3006
|
+
const path12 = sp2.join(directory, item);
|
|
3007
|
+
const fullPath = sp2.resolve(path12);
|
|
3008
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path12) || this._watched.has(fullPath);
|
|
3009
|
+
if (!this._throttle("remove", path12, 100))
|
|
2767
3010
|
return;
|
|
2768
3011
|
if (!isDirectory && this._watched.size === 1) {
|
|
2769
3012
|
this.add(directory, item, true);
|
|
2770
3013
|
}
|
|
2771
|
-
const wp = this._getWatchedDir(
|
|
3014
|
+
const wp = this._getWatchedDir(path12);
|
|
2772
3015
|
const nestedDirectoryChildren = wp.getChildren();
|
|
2773
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
3016
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path12, nested));
|
|
2774
3017
|
const parent = this._getWatchedDir(directory);
|
|
2775
3018
|
const wasTracked = parent.has(item);
|
|
2776
3019
|
parent.remove(item);
|
|
2777
3020
|
if (this._symlinkPaths.has(fullPath)) {
|
|
2778
3021
|
this._symlinkPaths.delete(fullPath);
|
|
2779
3022
|
}
|
|
2780
|
-
let relPath =
|
|
3023
|
+
let relPath = path12;
|
|
2781
3024
|
if (this.options.cwd)
|
|
2782
|
-
relPath = sp2.relative(this.options.cwd,
|
|
3025
|
+
relPath = sp2.relative(this.options.cwd, path12);
|
|
2783
3026
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
2784
3027
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
2785
3028
|
if (event === EVENTS.ADD)
|
|
2786
3029
|
return;
|
|
2787
3030
|
}
|
|
2788
|
-
this._watched.delete(
|
|
3031
|
+
this._watched.delete(path12);
|
|
2789
3032
|
this._watched.delete(fullPath);
|
|
2790
3033
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
2791
|
-
if (wasTracked && !this._isIgnored(
|
|
2792
|
-
this._emit(eventName,
|
|
2793
|
-
this._closePath(
|
|
3034
|
+
if (wasTracked && !this._isIgnored(path12))
|
|
3035
|
+
this._emit(eventName, path12);
|
|
3036
|
+
this._closePath(path12);
|
|
2794
3037
|
}
|
|
2795
3038
|
/**
|
|
2796
3039
|
* Closes all watchers for a path
|
|
2797
3040
|
*/
|
|
2798
|
-
_closePath(
|
|
2799
|
-
this._closeFile(
|
|
2800
|
-
const dir = sp2.dirname(
|
|
2801
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
3041
|
+
_closePath(path12) {
|
|
3042
|
+
this._closeFile(path12);
|
|
3043
|
+
const dir = sp2.dirname(path12);
|
|
3044
|
+
this._getWatchedDir(dir).remove(sp2.basename(path12));
|
|
2802
3045
|
}
|
|
2803
3046
|
/**
|
|
2804
3047
|
* Closes only file-specific watchers
|
|
2805
3048
|
*/
|
|
2806
|
-
_closeFile(
|
|
2807
|
-
const closers = this._closers.get(
|
|
3049
|
+
_closeFile(path12) {
|
|
3050
|
+
const closers = this._closers.get(path12);
|
|
2808
3051
|
if (!closers)
|
|
2809
3052
|
return;
|
|
2810
3053
|
closers.forEach((closer) => closer());
|
|
2811
|
-
this._closers.delete(
|
|
3054
|
+
this._closers.delete(path12);
|
|
2812
3055
|
}
|
|
2813
|
-
_addPathCloser(
|
|
3056
|
+
_addPathCloser(path12, closer) {
|
|
2814
3057
|
if (!closer)
|
|
2815
3058
|
return;
|
|
2816
|
-
let list = this._closers.get(
|
|
3059
|
+
let list = this._closers.get(path12);
|
|
2817
3060
|
if (!list) {
|
|
2818
3061
|
list = [];
|
|
2819
|
-
this._closers.set(
|
|
3062
|
+
this._closers.set(path12, list);
|
|
2820
3063
|
}
|
|
2821
3064
|
list.push(closer);
|
|
2822
3065
|
}
|
|
@@ -2846,12 +3089,12 @@ function watch(paths, options = {}) {
|
|
|
2846
3089
|
var chokidar_default = { watch, FSWatcher };
|
|
2847
3090
|
|
|
2848
3091
|
// src/watcher/index.ts
|
|
2849
|
-
import * as
|
|
3092
|
+
import * as path5 from "path";
|
|
2850
3093
|
|
|
2851
3094
|
// src/utils/files.ts
|
|
2852
3095
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
2853
|
-
import { existsSync as
|
|
2854
|
-
import * as
|
|
3096
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, promises as fsPromises } from "fs";
|
|
3097
|
+
import * as path4 from "path";
|
|
2855
3098
|
var PROJECT_MARKERS = [
|
|
2856
3099
|
".git",
|
|
2857
3100
|
"package.json",
|
|
@@ -2870,7 +3113,7 @@ var PROJECT_MARKERS = [
|
|
|
2870
3113
|
];
|
|
2871
3114
|
function hasProjectMarker(projectRoot) {
|
|
2872
3115
|
for (const marker of PROJECT_MARKERS) {
|
|
2873
|
-
if (
|
|
3116
|
+
if (existsSync4(path4.join(projectRoot, marker))) {
|
|
2874
3117
|
return true;
|
|
2875
3118
|
}
|
|
2876
3119
|
}
|
|
@@ -2896,16 +3139,16 @@ function createIgnoreFilter(projectRoot) {
|
|
|
2896
3139
|
"**/*build*/**"
|
|
2897
3140
|
];
|
|
2898
3141
|
ig.add(defaultIgnores);
|
|
2899
|
-
const gitignorePath =
|
|
2900
|
-
if (
|
|
2901
|
-
const gitignoreContent =
|
|
3142
|
+
const gitignorePath = path4.join(projectRoot, ".gitignore");
|
|
3143
|
+
if (existsSync4(gitignorePath)) {
|
|
3144
|
+
const gitignoreContent = readFileSync3(gitignorePath, "utf-8");
|
|
2902
3145
|
ig.add(gitignoreContent);
|
|
2903
3146
|
}
|
|
2904
3147
|
return ig;
|
|
2905
3148
|
}
|
|
2906
3149
|
function shouldIncludeFile(filePath, projectRoot, includePatterns, excludePatterns, ignoreFilter) {
|
|
2907
|
-
const relativePath =
|
|
2908
|
-
const pathParts = relativePath.split(
|
|
3150
|
+
const relativePath = path4.relative(projectRoot, filePath);
|
|
3151
|
+
const pathParts = relativePath.split(path4.sep);
|
|
2909
3152
|
for (const part of pathParts) {
|
|
2910
3153
|
if (part.startsWith(".") && part !== "." && part !== "..") {
|
|
2911
3154
|
return false;
|
|
@@ -2949,8 +3192,8 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
|
|
|
2949
3192
|
const filesInDir = [];
|
|
2950
3193
|
const subdirs = [];
|
|
2951
3194
|
for (const entry of entries) {
|
|
2952
|
-
const fullPath =
|
|
2953
|
-
const relativePath =
|
|
3195
|
+
const fullPath = path4.join(dir, entry.name);
|
|
3196
|
+
const relativePath = path4.relative(projectRoot, fullPath);
|
|
2954
3197
|
if (entry.name.startsWith(".") && entry.name !== "." && entry.name !== "..") {
|
|
2955
3198
|
if (entry.isDirectory()) {
|
|
2956
3199
|
skipped.push({ path: relativePath, reason: "excluded" });
|
|
@@ -2999,7 +3242,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
|
|
|
2999
3242
|
yield f;
|
|
3000
3243
|
}
|
|
3001
3244
|
for (let i = options.maxFilesPerDirectory; i < filesInDir.length; i++) {
|
|
3002
|
-
skipped.push({ path:
|
|
3245
|
+
skipped.push({ path: path4.relative(projectRoot, filesInDir[i].path), reason: "excluded" });
|
|
3003
3246
|
}
|
|
3004
3247
|
const canRecurse = options.maxDepth === -1 || currentDepth < options.maxDepth;
|
|
3005
3248
|
if (canRecurse) {
|
|
@@ -3039,8 +3282,8 @@ async function collectFiles(projectRoot, includePatterns, excludePatterns, maxFi
|
|
|
3039
3282
|
if (additionalRoots && additionalRoots.length > 0) {
|
|
3040
3283
|
const normalizedRoots = /* @__PURE__ */ new Set();
|
|
3041
3284
|
for (const kbRoot of additionalRoots) {
|
|
3042
|
-
const resolved =
|
|
3043
|
-
|
|
3285
|
+
const resolved = path4.normalize(
|
|
3286
|
+
path4.isAbsolute(kbRoot) ? kbRoot : path4.resolve(projectRoot, kbRoot)
|
|
3044
3287
|
);
|
|
3045
3288
|
normalizedRoots.add(resolved);
|
|
3046
3289
|
}
|
|
@@ -3073,122 +3316,6 @@ async function collectFiles(projectRoot, includePatterns, excludePatterns, maxFi
|
|
|
3073
3316
|
return { files, skipped };
|
|
3074
3317
|
}
|
|
3075
3318
|
|
|
3076
|
-
// src/git/index.ts
|
|
3077
|
-
import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync, statSync } from "fs";
|
|
3078
|
-
import * as path3 from "path";
|
|
3079
|
-
function readPackedRefs(gitDir) {
|
|
3080
|
-
const packedRefsPath = path3.join(gitDir, "packed-refs");
|
|
3081
|
-
if (!existsSync3(packedRefsPath)) {
|
|
3082
|
-
return [];
|
|
3083
|
-
}
|
|
3084
|
-
try {
|
|
3085
|
-
return readFileSync3(packedRefsPath, "utf-8").split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#") && !line.startsWith("^"));
|
|
3086
|
-
} catch {
|
|
3087
|
-
return [];
|
|
3088
|
-
}
|
|
3089
|
-
}
|
|
3090
|
-
function resolveCommonGitDir(gitDir) {
|
|
3091
|
-
const commonDirPath = path3.join(gitDir, "commondir");
|
|
3092
|
-
if (!existsSync3(commonDirPath)) {
|
|
3093
|
-
return gitDir;
|
|
3094
|
-
}
|
|
3095
|
-
try {
|
|
3096
|
-
const raw = readFileSync3(commonDirPath, "utf-8").trim();
|
|
3097
|
-
if (!raw) {
|
|
3098
|
-
return gitDir;
|
|
3099
|
-
}
|
|
3100
|
-
const resolved = path3.isAbsolute(raw) ? raw : path3.resolve(gitDir, raw);
|
|
3101
|
-
if (existsSync3(resolved)) {
|
|
3102
|
-
return resolved;
|
|
3103
|
-
}
|
|
3104
|
-
} catch {
|
|
3105
|
-
return gitDir;
|
|
3106
|
-
}
|
|
3107
|
-
return gitDir;
|
|
3108
|
-
}
|
|
3109
|
-
function resolveGitDir(repoRoot) {
|
|
3110
|
-
const gitPath = path3.join(repoRoot, ".git");
|
|
3111
|
-
if (!existsSync3(gitPath)) {
|
|
3112
|
-
return null;
|
|
3113
|
-
}
|
|
3114
|
-
try {
|
|
3115
|
-
const stat4 = statSync(gitPath);
|
|
3116
|
-
if (stat4.isDirectory()) {
|
|
3117
|
-
return gitPath;
|
|
3118
|
-
}
|
|
3119
|
-
if (stat4.isFile()) {
|
|
3120
|
-
const content = readFileSync3(gitPath, "utf-8").trim();
|
|
3121
|
-
const match = content.match(/^gitdir:\s*(.+)$/);
|
|
3122
|
-
if (match) {
|
|
3123
|
-
const gitdir = match[1];
|
|
3124
|
-
const resolvedPath = path3.isAbsolute(gitdir) ? gitdir : path3.resolve(repoRoot, gitdir);
|
|
3125
|
-
if (existsSync3(resolvedPath)) {
|
|
3126
|
-
return resolvedPath;
|
|
3127
|
-
}
|
|
3128
|
-
}
|
|
3129
|
-
}
|
|
3130
|
-
} catch {
|
|
3131
|
-
}
|
|
3132
|
-
return null;
|
|
3133
|
-
}
|
|
3134
|
-
function isGitRepo(dir) {
|
|
3135
|
-
return resolveGitDir(dir) !== null;
|
|
3136
|
-
}
|
|
3137
|
-
function getCurrentBranch(repoRoot) {
|
|
3138
|
-
const gitDir = resolveGitDir(repoRoot);
|
|
3139
|
-
if (!gitDir) {
|
|
3140
|
-
return null;
|
|
3141
|
-
}
|
|
3142
|
-
const headPath = path3.join(gitDir, "HEAD");
|
|
3143
|
-
if (!existsSync3(headPath)) {
|
|
3144
|
-
return null;
|
|
3145
|
-
}
|
|
3146
|
-
try {
|
|
3147
|
-
const headContent = readFileSync3(headPath, "utf-8").trim();
|
|
3148
|
-
const match = headContent.match(/^ref: refs\/heads\/(.+)$/);
|
|
3149
|
-
if (match) {
|
|
3150
|
-
return match[1];
|
|
3151
|
-
}
|
|
3152
|
-
if (/^[0-9a-f]{40}$/i.test(headContent)) {
|
|
3153
|
-
return headContent.slice(0, 7);
|
|
3154
|
-
}
|
|
3155
|
-
return null;
|
|
3156
|
-
} catch {
|
|
3157
|
-
return null;
|
|
3158
|
-
}
|
|
3159
|
-
}
|
|
3160
|
-
function getBaseBranch(repoRoot) {
|
|
3161
|
-
const gitDir = resolveGitDir(repoRoot);
|
|
3162
|
-
const refStoreDir = gitDir ? resolveCommonGitDir(gitDir) : null;
|
|
3163
|
-
const candidates = ["main", "master", "develop", "trunk"];
|
|
3164
|
-
if (refStoreDir) {
|
|
3165
|
-
for (const candidate of candidates) {
|
|
3166
|
-
const refPath = path3.join(refStoreDir, "refs", "heads", candidate);
|
|
3167
|
-
if (existsSync3(refPath)) {
|
|
3168
|
-
return candidate;
|
|
3169
|
-
}
|
|
3170
|
-
const packedRefs = readPackedRefs(refStoreDir);
|
|
3171
|
-
if (packedRefs.some((line) => line.endsWith(` refs/heads/${candidate}`))) {
|
|
3172
|
-
return candidate;
|
|
3173
|
-
}
|
|
3174
|
-
}
|
|
3175
|
-
}
|
|
3176
|
-
return getCurrentBranch(repoRoot) ?? "main";
|
|
3177
|
-
}
|
|
3178
|
-
function getBranchOrDefault(repoRoot) {
|
|
3179
|
-
if (!isGitRepo(repoRoot)) {
|
|
3180
|
-
return "default";
|
|
3181
|
-
}
|
|
3182
|
-
return getCurrentBranch(repoRoot) ?? "default";
|
|
3183
|
-
}
|
|
3184
|
-
function getHeadPath(repoRoot) {
|
|
3185
|
-
const gitDir = resolveGitDir(repoRoot);
|
|
3186
|
-
if (gitDir) {
|
|
3187
|
-
return path3.join(gitDir, "HEAD");
|
|
3188
|
-
}
|
|
3189
|
-
return path3.join(repoRoot, ".git", "HEAD");
|
|
3190
|
-
}
|
|
3191
|
-
|
|
3192
3319
|
// src/watcher/index.ts
|
|
3193
3320
|
var FileWatcher = class {
|
|
3194
3321
|
watcher = null;
|
|
@@ -3210,9 +3337,9 @@ var FileWatcher = class {
|
|
|
3210
3337
|
const ignoreFilter = createIgnoreFilter(this.projectRoot);
|
|
3211
3338
|
this.watcher = chokidar_default.watch(this.projectRoot, {
|
|
3212
3339
|
ignored: (filePath) => {
|
|
3213
|
-
const relativePath =
|
|
3340
|
+
const relativePath = path5.relative(this.projectRoot, filePath);
|
|
3214
3341
|
if (!relativePath) return false;
|
|
3215
|
-
const pathParts = relativePath.split(
|
|
3342
|
+
const pathParts = relativePath.split(path5.sep);
|
|
3216
3343
|
for (const part of pathParts) {
|
|
3217
3344
|
if (part.startsWith(".") && part !== "." && part !== "..") {
|
|
3218
3345
|
return true;
|
|
@@ -3264,7 +3391,7 @@ var FileWatcher = class {
|
|
|
3264
3391
|
return;
|
|
3265
3392
|
}
|
|
3266
3393
|
const changes = Array.from(this.pendingChanges.entries()).map(
|
|
3267
|
-
([
|
|
3394
|
+
([path12, type]) => ({ path: path12, type })
|
|
3268
3395
|
);
|
|
3269
3396
|
this.pendingChanges.clear();
|
|
3270
3397
|
try {
|
|
@@ -3310,7 +3437,7 @@ var GitHeadWatcher = class {
|
|
|
3310
3437
|
this.onBranchChange = handler;
|
|
3311
3438
|
this.currentBranch = getCurrentBranch(this.projectRoot);
|
|
3312
3439
|
const headPath = getHeadPath(this.projectRoot);
|
|
3313
|
-
const refsPath =
|
|
3440
|
+
const refsPath = path5.join(this.projectRoot, ".git", "refs", "heads");
|
|
3314
3441
|
this.watcher = chokidar_default.watch([headPath, refsPath], {
|
|
3315
3442
|
persistent: true,
|
|
3316
3443
|
ignoreInitial: true,
|
|
@@ -3395,8 +3522,8 @@ function createWatcherWithIndexer(getIndexer2, projectRoot, config) {
|
|
|
3395
3522
|
import { tool } from "@opencode-ai/plugin";
|
|
3396
3523
|
|
|
3397
3524
|
// src/indexer/index.ts
|
|
3398
|
-
import { existsSync as
|
|
3399
|
-
import * as
|
|
3525
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync2, renameSync, unlinkSync, mkdirSync as mkdirSync2, promises as fsPromises2 } from "fs";
|
|
3526
|
+
import * as path8 from "path";
|
|
3400
3527
|
import { performance as performance2 } from "perf_hooks";
|
|
3401
3528
|
|
|
3402
3529
|
// node_modules/eventemitter3/index.mjs
|
|
@@ -3421,7 +3548,7 @@ function pTimeout(promise, options) {
|
|
|
3421
3548
|
} = options;
|
|
3422
3549
|
let timer;
|
|
3423
3550
|
let abortHandler;
|
|
3424
|
-
const wrappedPromise = new Promise((
|
|
3551
|
+
const wrappedPromise = new Promise((resolve9, reject) => {
|
|
3425
3552
|
if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
|
|
3426
3553
|
throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
|
|
3427
3554
|
}
|
|
@@ -3435,7 +3562,7 @@ function pTimeout(promise, options) {
|
|
|
3435
3562
|
};
|
|
3436
3563
|
signal.addEventListener("abort", abortHandler, { once: true });
|
|
3437
3564
|
}
|
|
3438
|
-
promise.then(
|
|
3565
|
+
promise.then(resolve9, reject);
|
|
3439
3566
|
if (milliseconds === Number.POSITIVE_INFINITY) {
|
|
3440
3567
|
return;
|
|
3441
3568
|
}
|
|
@@ -3443,7 +3570,7 @@ function pTimeout(promise, options) {
|
|
|
3443
3570
|
timer = customTimers.setTimeout.call(void 0, () => {
|
|
3444
3571
|
if (fallback) {
|
|
3445
3572
|
try {
|
|
3446
|
-
|
|
3573
|
+
resolve9(fallback());
|
|
3447
3574
|
} catch (error) {
|
|
3448
3575
|
reject(error);
|
|
3449
3576
|
}
|
|
@@ -3453,7 +3580,7 @@ function pTimeout(promise, options) {
|
|
|
3453
3580
|
promise.cancel();
|
|
3454
3581
|
}
|
|
3455
3582
|
if (message === false) {
|
|
3456
|
-
|
|
3583
|
+
resolve9();
|
|
3457
3584
|
} else if (message instanceof Error) {
|
|
3458
3585
|
reject(message);
|
|
3459
3586
|
} else {
|
|
@@ -3855,7 +3982,7 @@ var PQueue = class extends import_index.default {
|
|
|
3855
3982
|
// Assign unique ID if not provided
|
|
3856
3983
|
id: options.id ?? (this.#idAssigner++).toString()
|
|
3857
3984
|
};
|
|
3858
|
-
return new Promise((
|
|
3985
|
+
return new Promise((resolve9, reject) => {
|
|
3859
3986
|
const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
|
|
3860
3987
|
let cleanupQueueAbortHandler = () => void 0;
|
|
3861
3988
|
const run = async () => {
|
|
@@ -3895,7 +4022,7 @@ var PQueue = class extends import_index.default {
|
|
|
3895
4022
|
})]);
|
|
3896
4023
|
}
|
|
3897
4024
|
const result = await operation;
|
|
3898
|
-
|
|
4025
|
+
resolve9(result);
|
|
3899
4026
|
this.emit("completed", result);
|
|
3900
4027
|
} catch (error) {
|
|
3901
4028
|
reject(error);
|
|
@@ -4083,13 +4210,13 @@ var PQueue = class extends import_index.default {
|
|
|
4083
4210
|
});
|
|
4084
4211
|
}
|
|
4085
4212
|
async #onEvent(event, filter) {
|
|
4086
|
-
return new Promise((
|
|
4213
|
+
return new Promise((resolve9) => {
|
|
4087
4214
|
const listener = () => {
|
|
4088
4215
|
if (filter && !filter()) {
|
|
4089
4216
|
return;
|
|
4090
4217
|
}
|
|
4091
4218
|
this.off(event, listener);
|
|
4092
|
-
|
|
4219
|
+
resolve9();
|
|
4093
4220
|
};
|
|
4094
4221
|
this.on(event, listener);
|
|
4095
4222
|
});
|
|
@@ -4375,7 +4502,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
|
|
|
4375
4502
|
const finalDelay = Math.min(delayTime, remainingTime);
|
|
4376
4503
|
options.signal?.throwIfAborted();
|
|
4377
4504
|
if (finalDelay > 0) {
|
|
4378
|
-
await new Promise((
|
|
4505
|
+
await new Promise((resolve9, reject) => {
|
|
4379
4506
|
const onAbort = () => {
|
|
4380
4507
|
clearTimeout(timeoutToken);
|
|
4381
4508
|
options.signal?.removeEventListener("abort", onAbort);
|
|
@@ -4383,7 +4510,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
|
|
|
4383
4510
|
};
|
|
4384
4511
|
const timeoutToken = setTimeout(() => {
|
|
4385
4512
|
options.signal?.removeEventListener("abort", onAbort);
|
|
4386
|
-
|
|
4513
|
+
resolve9();
|
|
4387
4514
|
}, finalDelay);
|
|
4388
4515
|
if (options.unref) {
|
|
4389
4516
|
timeoutToken.unref?.();
|
|
@@ -4444,16 +4571,16 @@ async function pRetry(input, options = {}) {
|
|
|
4444
4571
|
}
|
|
4445
4572
|
|
|
4446
4573
|
// src/embeddings/detector.ts
|
|
4447
|
-
import { existsSync as
|
|
4448
|
-
import * as
|
|
4449
|
-
import * as
|
|
4574
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
4575
|
+
import * as path6 from "path";
|
|
4576
|
+
import * as os3 from "os";
|
|
4450
4577
|
function getOpenCodeAuthPath() {
|
|
4451
|
-
return
|
|
4578
|
+
return path6.join(os3.homedir(), ".local", "share", "opencode", "auth.json");
|
|
4452
4579
|
}
|
|
4453
4580
|
function loadOpenCodeAuth() {
|
|
4454
4581
|
const authPath = getOpenCodeAuthPath();
|
|
4455
4582
|
try {
|
|
4456
|
-
if (
|
|
4583
|
+
if (existsSync5(authPath)) {
|
|
4457
4584
|
return JSON.parse(readFileSync4(authPath, "utf-8"));
|
|
4458
4585
|
}
|
|
4459
4586
|
} catch {
|
|
@@ -4487,7 +4614,7 @@ async function detectEmbeddingProvider(preferredProvider, model) {
|
|
|
4487
4614
|
);
|
|
4488
4615
|
}
|
|
4489
4616
|
async function tryDetectProvider() {
|
|
4490
|
-
for (const provider of
|
|
4617
|
+
for (const provider of autoDetectProviders) {
|
|
4491
4618
|
const credentials = await getProviderCredentials(provider);
|
|
4492
4619
|
if (credentials) {
|
|
4493
4620
|
return {
|
|
@@ -4498,7 +4625,7 @@ async function tryDetectProvider() {
|
|
|
4498
4625
|
}
|
|
4499
4626
|
}
|
|
4500
4627
|
throw new Error(
|
|
4501
|
-
`No embedding-capable provider found. Please authenticate with OpenCode using one of: ${
|
|
4628
|
+
`No embedding-capable provider found. Please authenticate with OpenCode using one of: ${autoDetectProviders.join(", ")}.`
|
|
4502
4629
|
);
|
|
4503
4630
|
}
|
|
4504
4631
|
async function getProviderCredentials(provider) {
|
|
@@ -4818,11 +4945,12 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider {
|
|
|
4818
4945
|
return this.modelInfo;
|
|
4819
4946
|
}
|
|
4820
4947
|
};
|
|
4821
|
-
var OllamaEmbeddingProvider = class {
|
|
4948
|
+
var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider {
|
|
4822
4949
|
constructor(credentials, modelInfo) {
|
|
4823
4950
|
this.credentials = credentials;
|
|
4824
4951
|
this.modelInfo = modelInfo;
|
|
4825
4952
|
}
|
|
4953
|
+
static MIN_TRUNCATION_CHARS = 512;
|
|
4826
4954
|
async embedQuery(query) {
|
|
4827
4955
|
const result = await this.embedBatch([query]);
|
|
4828
4956
|
return {
|
|
@@ -4837,30 +4965,103 @@ var OllamaEmbeddingProvider = class {
|
|
|
4837
4965
|
tokensUsed: result.totalTokensUsed
|
|
4838
4966
|
};
|
|
4839
4967
|
}
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
tokensUsed: Math.ceil(text.length / 4)
|
|
4861
|
-
};
|
|
4862
|
-
})
|
|
4968
|
+
estimateTokens(text) {
|
|
4969
|
+
return Math.ceil(text.length / 4);
|
|
4970
|
+
}
|
|
4971
|
+
truncateToCharLimit(text, maxChars) {
|
|
4972
|
+
if (text.length <= maxChars) {
|
|
4973
|
+
return text;
|
|
4974
|
+
}
|
|
4975
|
+
return `${text.slice(0, Math.max(0, maxChars - 17))}
|
|
4976
|
+
... [truncated]`;
|
|
4977
|
+
}
|
|
4978
|
+
isContextLengthError(error) {
|
|
4979
|
+
const message = (error instanceof Error ? error.message : String(error)).toLowerCase();
|
|
4980
|
+
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");
|
|
4981
|
+
}
|
|
4982
|
+
buildTruncationCandidates(text) {
|
|
4983
|
+
const baseMaxChars = Math.max(1, this.modelInfo.maxTokens * 4);
|
|
4984
|
+
const candidateLimits = /* @__PURE__ */ new Set();
|
|
4985
|
+
const baselineLimit = text.length > baseMaxChars ? baseMaxChars : Math.max(
|
|
4986
|
+
_OllamaEmbeddingProvider.MIN_TRUNCATION_CHARS,
|
|
4987
|
+
Math.floor(text.length * 0.9)
|
|
4863
4988
|
);
|
|
4989
|
+
if (baselineLimit < text.length) {
|
|
4990
|
+
candidateLimits.add(baselineLimit);
|
|
4991
|
+
}
|
|
4992
|
+
for (const factor of [0.75, 0.6, 0.45, 0.35, 0.25]) {
|
|
4993
|
+
const scaledLimit = Math.max(
|
|
4994
|
+
_OllamaEmbeddingProvider.MIN_TRUNCATION_CHARS,
|
|
4995
|
+
Math.floor(baselineLimit * factor)
|
|
4996
|
+
);
|
|
4997
|
+
if (scaledLimit < text.length) {
|
|
4998
|
+
candidateLimits.add(scaledLimit);
|
|
4999
|
+
}
|
|
5000
|
+
}
|
|
5001
|
+
candidateLimits.add(Math.min(text.length - 1, _OllamaEmbeddingProvider.MIN_TRUNCATION_CHARS));
|
|
5002
|
+
const candidates = [];
|
|
5003
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5004
|
+
for (const limit of [...candidateLimits].sort((a, b) => b - a)) {
|
|
5005
|
+
if (limit <= 0 || limit >= text.length) {
|
|
5006
|
+
continue;
|
|
5007
|
+
}
|
|
5008
|
+
const truncated = this.truncateToCharLimit(text, limit);
|
|
5009
|
+
if (truncated === text || seen.has(truncated)) {
|
|
5010
|
+
continue;
|
|
5011
|
+
}
|
|
5012
|
+
seen.add(truncated);
|
|
5013
|
+
candidates.push(truncated);
|
|
5014
|
+
}
|
|
5015
|
+
return candidates;
|
|
5016
|
+
}
|
|
5017
|
+
async embedSingleWithFallback(text) {
|
|
5018
|
+
try {
|
|
5019
|
+
return await this.embedSingle(text);
|
|
5020
|
+
} catch (error) {
|
|
5021
|
+
if (!this.isContextLengthError(error)) {
|
|
5022
|
+
throw error;
|
|
5023
|
+
}
|
|
5024
|
+
let lastError = error;
|
|
5025
|
+
for (const truncated of this.buildTruncationCandidates(text)) {
|
|
5026
|
+
try {
|
|
5027
|
+
return await this.embedSingle(truncated);
|
|
5028
|
+
} catch (retryError) {
|
|
5029
|
+
if (!this.isContextLengthError(retryError)) {
|
|
5030
|
+
throw retryError;
|
|
5031
|
+
}
|
|
5032
|
+
lastError = retryError;
|
|
5033
|
+
}
|
|
5034
|
+
}
|
|
5035
|
+
throw lastError;
|
|
5036
|
+
}
|
|
5037
|
+
}
|
|
5038
|
+
async embedSingle(text) {
|
|
5039
|
+
const response = await fetch(`${this.credentials.baseUrl}/api/embeddings`, {
|
|
5040
|
+
method: "POST",
|
|
5041
|
+
headers: {
|
|
5042
|
+
"Content-Type": "application/json"
|
|
5043
|
+
},
|
|
5044
|
+
body: JSON.stringify({
|
|
5045
|
+
model: this.modelInfo.model,
|
|
5046
|
+
prompt: text,
|
|
5047
|
+
truncate: false
|
|
5048
|
+
})
|
|
5049
|
+
});
|
|
5050
|
+
if (!response.ok) {
|
|
5051
|
+
const error = await response.text();
|
|
5052
|
+
throw new Error(`Ollama embedding API error: ${response.status} - ${error}`);
|
|
5053
|
+
}
|
|
5054
|
+
const data = await response.json();
|
|
5055
|
+
return {
|
|
5056
|
+
embedding: data.embedding,
|
|
5057
|
+
tokensUsed: this.estimateTokens(text)
|
|
5058
|
+
};
|
|
5059
|
+
}
|
|
5060
|
+
async embedBatch(texts) {
|
|
5061
|
+
const results = [];
|
|
5062
|
+
for (const text of texts) {
|
|
5063
|
+
results.push(await this.embedSingleWithFallback(text));
|
|
5064
|
+
}
|
|
4864
5065
|
return {
|
|
4865
5066
|
embeddings: results.map((r) => r.embedding),
|
|
4866
5067
|
totalTokensUsed: results.reduce((sum, r) => sum + r.tokensUsed, 0)
|
|
@@ -5416,13 +5617,13 @@ function initializeLogger(config) {
|
|
|
5416
5617
|
}
|
|
5417
5618
|
|
|
5418
5619
|
// src/native/index.ts
|
|
5419
|
-
import * as
|
|
5420
|
-
import * as
|
|
5620
|
+
import * as path7 from "path";
|
|
5621
|
+
import * as os4 from "os";
|
|
5421
5622
|
import * as module from "module";
|
|
5422
5623
|
import { fileURLToPath } from "url";
|
|
5423
5624
|
function getNativeBinding() {
|
|
5424
|
-
const platform2 =
|
|
5425
|
-
const arch2 =
|
|
5625
|
+
const platform2 = os4.platform();
|
|
5626
|
+
const arch2 = os4.arch();
|
|
5426
5627
|
let bindingName;
|
|
5427
5628
|
if (platform2 === "darwin" && arch2 === "arm64") {
|
|
5428
5629
|
bindingName = "codebase-index-native.darwin-arm64.node";
|
|
@@ -5440,18 +5641,19 @@ function getNativeBinding() {
|
|
|
5440
5641
|
let currentDir;
|
|
5441
5642
|
let requireTarget;
|
|
5442
5643
|
if (typeof import.meta !== "undefined" && import.meta.url) {
|
|
5443
|
-
currentDir =
|
|
5644
|
+
currentDir = path7.dirname(fileURLToPath(import.meta.url));
|
|
5444
5645
|
requireTarget = import.meta.url;
|
|
5445
5646
|
} else if (typeof __dirname !== "undefined") {
|
|
5446
5647
|
currentDir = __dirname;
|
|
5447
5648
|
requireTarget = __filename;
|
|
5448
5649
|
} else {
|
|
5449
5650
|
currentDir = process.cwd();
|
|
5450
|
-
requireTarget =
|
|
5651
|
+
requireTarget = path7.join(currentDir, "index.js");
|
|
5451
5652
|
}
|
|
5452
|
-
const
|
|
5453
|
-
const
|
|
5454
|
-
const
|
|
5653
|
+
const normalizedDir = currentDir.replace(/\\/g, "/");
|
|
5654
|
+
const isDevMode = normalizedDir.includes("/src/native") || currentDir.includes(path7.join("src", "native"));
|
|
5655
|
+
const packageRoot = isDevMode ? path7.resolve(currentDir, "../..") : path7.resolve(currentDir, "..");
|
|
5656
|
+
const nativePath = path7.join(packageRoot, "native", bindingName);
|
|
5455
5657
|
const require2 = module.createRequire(requireTarget);
|
|
5456
5658
|
return require2(nativePath);
|
|
5457
5659
|
}
|
|
@@ -5482,11 +5684,20 @@ function createMockNativeBinding() {
|
|
|
5482
5684
|
constructor() {
|
|
5483
5685
|
throw error;
|
|
5484
5686
|
}
|
|
5687
|
+
serialize() {
|
|
5688
|
+
throw error;
|
|
5689
|
+
}
|
|
5690
|
+
deserialize() {
|
|
5691
|
+
throw error;
|
|
5692
|
+
}
|
|
5485
5693
|
},
|
|
5486
5694
|
Database: class {
|
|
5487
5695
|
constructor() {
|
|
5488
5696
|
throw error;
|
|
5489
5697
|
}
|
|
5698
|
+
close() {
|
|
5699
|
+
throw error;
|
|
5700
|
+
}
|
|
5490
5701
|
}
|
|
5491
5702
|
};
|
|
5492
5703
|
}
|
|
@@ -5619,7 +5830,7 @@ var MAX_SINGLE_CHUNK_TOKENS = 2e3;
|
|
|
5619
5830
|
function estimateTokens(text) {
|
|
5620
5831
|
return Math.ceil(text.length / CHARS_PER_TOKEN);
|
|
5621
5832
|
}
|
|
5622
|
-
function
|
|
5833
|
+
function getEmbeddingHeaderParts(chunk, filePath) {
|
|
5623
5834
|
const parts = [];
|
|
5624
5835
|
const fileName = filePath.split("/").pop() || filePath;
|
|
5625
5836
|
const dirPath = filePath.split("/").slice(-3, -1).join("/");
|
|
@@ -5666,23 +5877,52 @@ function createEmbeddingText(chunk, filePath) {
|
|
|
5666
5877
|
if (semanticHints.length > 0) {
|
|
5667
5878
|
parts.push(`Purpose: ${semanticHints.join(", ")}`);
|
|
5668
5879
|
}
|
|
5669
|
-
parts
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
const
|
|
5673
|
-
if (
|
|
5674
|
-
|
|
5880
|
+
return parts;
|
|
5881
|
+
}
|
|
5882
|
+
function buildEmbeddingText(headerParts, content, partIndex, partCount) {
|
|
5883
|
+
const parts = [...headerParts];
|
|
5884
|
+
if (partCount && partCount > 1 && partIndex) {
|
|
5885
|
+
parts.push(`Part ${partIndex}/${partCount}`);
|
|
5675
5886
|
}
|
|
5887
|
+
parts.push("");
|
|
5676
5888
|
parts.push(content);
|
|
5677
5889
|
return parts.join("\n");
|
|
5678
5890
|
}
|
|
5679
|
-
function
|
|
5891
|
+
function splitOversizedContent(content, maxContentChars) {
|
|
5892
|
+
if (content.length <= maxContentChars) {
|
|
5893
|
+
return [content];
|
|
5894
|
+
}
|
|
5895
|
+
const overlapChars = Math.max(CHARS_PER_TOKEN * 32, Math.min(Math.floor(maxContentChars * 0.15), CHARS_PER_TOKEN * 128));
|
|
5896
|
+
const stepChars = Math.max(1, maxContentChars - overlapChars);
|
|
5897
|
+
const segments = [];
|
|
5898
|
+
for (let start = 0; start < content.length; start += stepChars) {
|
|
5899
|
+
const end = Math.min(content.length, start + maxContentChars);
|
|
5900
|
+
segments.push(content.slice(start, end));
|
|
5901
|
+
if (end >= content.length) {
|
|
5902
|
+
break;
|
|
5903
|
+
}
|
|
5904
|
+
}
|
|
5905
|
+
return segments;
|
|
5906
|
+
}
|
|
5907
|
+
function createEmbeddingTexts(chunk, filePath, maxChunkTokens = MAX_SINGLE_CHUNK_TOKENS) {
|
|
5908
|
+
const headerParts = getEmbeddingHeaderParts(chunk, filePath);
|
|
5909
|
+
const headerLength = buildEmbeddingText(headerParts, "", 1, 9).length;
|
|
5910
|
+
const maxContentChars = Math.max(1, maxChunkTokens * CHARS_PER_TOKEN - headerLength);
|
|
5911
|
+
const segments = splitOversizedContent(chunk.content, maxContentChars);
|
|
5912
|
+
if (segments.length === 1) {
|
|
5913
|
+
return [buildEmbeddingText(headerParts, segments[0])];
|
|
5914
|
+
}
|
|
5915
|
+
return segments.map((segment, index) => buildEmbeddingText(headerParts, segment, index + 1, segments.length));
|
|
5916
|
+
}
|
|
5917
|
+
function createDynamicBatches(chunks, options = {}) {
|
|
5680
5918
|
const batches = [];
|
|
5681
5919
|
let currentBatch = [];
|
|
5682
5920
|
let currentTokens = 0;
|
|
5921
|
+
const maxBatchTokens = Math.max(1, options.maxBatchTokens ?? MAX_BATCH_TOKENS);
|
|
5922
|
+
const maxBatchItems = Math.max(1, options.maxBatchItems ?? Number.MAX_SAFE_INTEGER);
|
|
5683
5923
|
for (const chunk of chunks) {
|
|
5684
|
-
const chunkTokens = estimateTokens(chunk.text);
|
|
5685
|
-
if (currentBatch.length > 0 && currentTokens + chunkTokens >
|
|
5924
|
+
const chunkTokens = chunk.tokenCount ?? estimateTokens(chunk.text);
|
|
5925
|
+
if (currentBatch.length > 0 && (currentTokens + chunkTokens > maxBatchTokens || currentBatch.length >= maxBatchItems)) {
|
|
5686
5926
|
batches.push(currentBatch);
|
|
5687
5927
|
currentBatch = [];
|
|
5688
5928
|
currentTokens = 0;
|
|
@@ -5801,6 +6041,12 @@ var InvertedIndex = class {
|
|
|
5801
6041
|
save() {
|
|
5802
6042
|
this.inner.save();
|
|
5803
6043
|
}
|
|
6044
|
+
serialize() {
|
|
6045
|
+
return this.inner.serialize();
|
|
6046
|
+
}
|
|
6047
|
+
deserialize(json) {
|
|
6048
|
+
this.inner.deserialize(json);
|
|
6049
|
+
}
|
|
5804
6050
|
addChunk(chunkId, content) {
|
|
5805
6051
|
this.inner.addChunk(chunkId, content);
|
|
5806
6052
|
}
|
|
@@ -5827,158 +6073,263 @@ var InvertedIndex = class {
|
|
|
5827
6073
|
};
|
|
5828
6074
|
var Database = class {
|
|
5829
6075
|
inner;
|
|
6076
|
+
closed = false;
|
|
5830
6077
|
constructor(dbPath) {
|
|
5831
6078
|
this.inner = new native.Database(dbPath);
|
|
5832
6079
|
}
|
|
6080
|
+
throwIfClosed() {
|
|
6081
|
+
if (this.closed) {
|
|
6082
|
+
throw new Error("Database is closed");
|
|
6083
|
+
}
|
|
6084
|
+
}
|
|
6085
|
+
close() {
|
|
6086
|
+
if (this.closed) {
|
|
6087
|
+
return;
|
|
6088
|
+
}
|
|
6089
|
+
if (typeof this.inner.close === "function") {
|
|
6090
|
+
this.inner.close();
|
|
6091
|
+
}
|
|
6092
|
+
this.closed = true;
|
|
6093
|
+
}
|
|
5833
6094
|
embeddingExists(contentHash) {
|
|
6095
|
+
this.throwIfClosed();
|
|
5834
6096
|
return this.inner.embeddingExists(contentHash);
|
|
5835
6097
|
}
|
|
5836
6098
|
getEmbedding(contentHash) {
|
|
6099
|
+
this.throwIfClosed();
|
|
5837
6100
|
return this.inner.getEmbedding(contentHash) ?? null;
|
|
5838
6101
|
}
|
|
5839
6102
|
upsertEmbedding(contentHash, embedding, chunkText, model) {
|
|
6103
|
+
this.throwIfClosed();
|
|
5840
6104
|
this.inner.upsertEmbedding(contentHash, embedding, chunkText, model);
|
|
5841
6105
|
}
|
|
5842
6106
|
upsertEmbeddingsBatch(items) {
|
|
6107
|
+
this.throwIfClosed();
|
|
5843
6108
|
if (items.length === 0) return;
|
|
5844
6109
|
this.inner.upsertEmbeddingsBatch(items);
|
|
5845
6110
|
}
|
|
5846
6111
|
getMissingEmbeddings(contentHashes) {
|
|
6112
|
+
this.throwIfClosed();
|
|
5847
6113
|
return this.inner.getMissingEmbeddings(contentHashes);
|
|
5848
6114
|
}
|
|
5849
6115
|
upsertChunk(chunk) {
|
|
6116
|
+
this.throwIfClosed();
|
|
5850
6117
|
this.inner.upsertChunk(chunk);
|
|
5851
6118
|
}
|
|
5852
6119
|
upsertChunksBatch(chunks) {
|
|
6120
|
+
this.throwIfClosed();
|
|
5853
6121
|
if (chunks.length === 0) return;
|
|
5854
6122
|
this.inner.upsertChunksBatch(chunks);
|
|
5855
6123
|
}
|
|
5856
6124
|
getChunk(chunkId) {
|
|
6125
|
+
this.throwIfClosed();
|
|
5857
6126
|
return this.inner.getChunk(chunkId) ?? null;
|
|
5858
6127
|
}
|
|
5859
6128
|
getChunksByFile(filePath) {
|
|
6129
|
+
this.throwIfClosed();
|
|
5860
6130
|
return this.inner.getChunksByFile(filePath);
|
|
5861
6131
|
}
|
|
5862
6132
|
getChunksByName(name) {
|
|
6133
|
+
this.throwIfClosed();
|
|
5863
6134
|
return this.inner.getChunksByName(name);
|
|
5864
6135
|
}
|
|
5865
6136
|
getChunksByNameCi(name) {
|
|
6137
|
+
this.throwIfClosed();
|
|
5866
6138
|
return this.inner.getChunksByNameCi(name);
|
|
5867
6139
|
}
|
|
5868
6140
|
deleteChunksByFile(filePath) {
|
|
6141
|
+
this.throwIfClosed();
|
|
5869
6142
|
return this.inner.deleteChunksByFile(filePath);
|
|
5870
6143
|
}
|
|
6144
|
+
deleteChunksByIds(chunkIds) {
|
|
6145
|
+
this.throwIfClosed();
|
|
6146
|
+
if (chunkIds.length === 0) return 0;
|
|
6147
|
+
return this.inner.deleteChunksByIds(chunkIds);
|
|
6148
|
+
}
|
|
5871
6149
|
addChunksToBranch(branch, chunkIds) {
|
|
6150
|
+
this.throwIfClosed();
|
|
5872
6151
|
this.inner.addChunksToBranch(branch, chunkIds);
|
|
5873
6152
|
}
|
|
5874
6153
|
addChunksToBranchBatch(branch, chunkIds) {
|
|
6154
|
+
this.throwIfClosed();
|
|
5875
6155
|
if (chunkIds.length === 0) return;
|
|
5876
6156
|
this.inner.addChunksToBranchBatch(branch, chunkIds);
|
|
5877
6157
|
}
|
|
5878
6158
|
clearBranch(branch) {
|
|
6159
|
+
this.throwIfClosed();
|
|
5879
6160
|
return this.inner.clearBranch(branch);
|
|
5880
6161
|
}
|
|
6162
|
+
deleteBranchChunksByChunkIds(chunkIds) {
|
|
6163
|
+
this.throwIfClosed();
|
|
6164
|
+
if (chunkIds.length === 0) return 0;
|
|
6165
|
+
return this.inner.deleteBranchChunksByChunkIds(chunkIds);
|
|
6166
|
+
}
|
|
6167
|
+
deleteBranchChunksForBranch(branch, chunkIds) {
|
|
6168
|
+
this.throwIfClosed();
|
|
6169
|
+
if (chunkIds.length === 0) return 0;
|
|
6170
|
+
return this.inner.deleteBranchChunksForBranch(branch, chunkIds);
|
|
6171
|
+
}
|
|
5881
6172
|
getBranchChunkIds(branch) {
|
|
6173
|
+
this.throwIfClosed();
|
|
5882
6174
|
return this.inner.getBranchChunkIds(branch);
|
|
5883
6175
|
}
|
|
5884
6176
|
getBranchDelta(branch, baseBranch) {
|
|
6177
|
+
this.throwIfClosed();
|
|
5885
6178
|
return this.inner.getBranchDelta(branch, baseBranch);
|
|
5886
6179
|
}
|
|
6180
|
+
getReferencedChunkIds(chunkIds) {
|
|
6181
|
+
this.throwIfClosed();
|
|
6182
|
+
if (chunkIds.length === 0) return [];
|
|
6183
|
+
return this.inner.getReferencedChunkIds(chunkIds);
|
|
6184
|
+
}
|
|
5887
6185
|
chunkExistsOnBranch(branch, chunkId) {
|
|
6186
|
+
this.throwIfClosed();
|
|
5888
6187
|
return this.inner.chunkExistsOnBranch(branch, chunkId);
|
|
5889
6188
|
}
|
|
5890
6189
|
getAllBranches() {
|
|
6190
|
+
this.throwIfClosed();
|
|
5891
6191
|
return this.inner.getAllBranches();
|
|
5892
6192
|
}
|
|
5893
6193
|
getMetadata(key) {
|
|
6194
|
+
this.throwIfClosed();
|
|
5894
6195
|
return this.inner.getMetadata(key) ?? null;
|
|
5895
6196
|
}
|
|
5896
6197
|
setMetadata(key, value) {
|
|
6198
|
+
this.throwIfClosed();
|
|
5897
6199
|
this.inner.setMetadata(key, value);
|
|
5898
6200
|
}
|
|
5899
6201
|
deleteMetadata(key) {
|
|
6202
|
+
this.throwIfClosed();
|
|
5900
6203
|
return this.inner.deleteMetadata(key);
|
|
5901
6204
|
}
|
|
6205
|
+
clearAllIndexedData() {
|
|
6206
|
+
this.throwIfClosed();
|
|
6207
|
+
this.inner.clearAllIndexedData();
|
|
6208
|
+
}
|
|
6209
|
+
clearCallEdgeTargetsForSymbols(symbolIds) {
|
|
6210
|
+
this.throwIfClosed();
|
|
6211
|
+
if (symbolIds.length === 0) return 0;
|
|
6212
|
+
return this.inner.clearCallEdgeTargetsForSymbols(symbolIds);
|
|
6213
|
+
}
|
|
5902
6214
|
gcOrphanEmbeddings() {
|
|
6215
|
+
this.throwIfClosed();
|
|
5903
6216
|
return this.inner.gcOrphanEmbeddings();
|
|
5904
6217
|
}
|
|
5905
6218
|
gcOrphanChunks() {
|
|
6219
|
+
this.throwIfClosed();
|
|
5906
6220
|
return this.inner.gcOrphanChunks();
|
|
5907
6221
|
}
|
|
5908
6222
|
getStats() {
|
|
6223
|
+
this.throwIfClosed();
|
|
5909
6224
|
return this.inner.getStats();
|
|
5910
6225
|
}
|
|
5911
6226
|
// ── Symbol methods ──────────────────────────────────────────────
|
|
5912
6227
|
upsertSymbol(symbol) {
|
|
6228
|
+
this.throwIfClosed();
|
|
5913
6229
|
this.inner.upsertSymbol(symbol);
|
|
5914
6230
|
}
|
|
5915
6231
|
upsertSymbolsBatch(symbols) {
|
|
6232
|
+
this.throwIfClosed();
|
|
5916
6233
|
if (symbols.length === 0) return;
|
|
5917
6234
|
this.inner.upsertSymbolsBatch(symbols);
|
|
5918
6235
|
}
|
|
5919
6236
|
getSymbolsByFile(filePath) {
|
|
6237
|
+
this.throwIfClosed();
|
|
5920
6238
|
return this.inner.getSymbolsByFile(filePath);
|
|
5921
6239
|
}
|
|
5922
6240
|
getSymbolByName(name, filePath) {
|
|
6241
|
+
this.throwIfClosed();
|
|
5923
6242
|
return this.inner.getSymbolByName(name, filePath) ?? null;
|
|
5924
6243
|
}
|
|
5925
6244
|
getSymbolsByName(name) {
|
|
6245
|
+
this.throwIfClosed();
|
|
5926
6246
|
return this.inner.getSymbolsByName(name);
|
|
5927
6247
|
}
|
|
5928
6248
|
getSymbolsByNameCi(name) {
|
|
6249
|
+
this.throwIfClosed();
|
|
5929
6250
|
return this.inner.getSymbolsByNameCi(name);
|
|
5930
6251
|
}
|
|
5931
6252
|
deleteSymbolsByFile(filePath) {
|
|
6253
|
+
this.throwIfClosed();
|
|
5932
6254
|
return this.inner.deleteSymbolsByFile(filePath);
|
|
5933
6255
|
}
|
|
5934
6256
|
// ── Call Edge methods ────────────────────────────────────────────
|
|
5935
6257
|
upsertCallEdge(edge) {
|
|
6258
|
+
this.throwIfClosed();
|
|
5936
6259
|
this.inner.upsertCallEdge(edge);
|
|
5937
6260
|
}
|
|
5938
6261
|
upsertCallEdgesBatch(edges) {
|
|
6262
|
+
this.throwIfClosed();
|
|
5939
6263
|
if (edges.length === 0) return;
|
|
5940
6264
|
this.inner.upsertCallEdgesBatch(edges);
|
|
5941
6265
|
}
|
|
5942
6266
|
getCallers(targetName, branch) {
|
|
6267
|
+
this.throwIfClosed();
|
|
5943
6268
|
return this.inner.getCallers(targetName, branch);
|
|
5944
6269
|
}
|
|
5945
6270
|
getCallersWithContext(targetName, branch) {
|
|
6271
|
+
this.throwIfClosed();
|
|
5946
6272
|
return this.inner.getCallersWithContext(targetName, branch);
|
|
5947
6273
|
}
|
|
5948
6274
|
getCallees(symbolId, branch) {
|
|
6275
|
+
this.throwIfClosed();
|
|
5949
6276
|
return this.inner.getCallees(symbolId, branch);
|
|
5950
6277
|
}
|
|
5951
6278
|
deleteCallEdgesByFile(filePath) {
|
|
6279
|
+
this.throwIfClosed();
|
|
5952
6280
|
return this.inner.deleteCallEdgesByFile(filePath);
|
|
5953
6281
|
}
|
|
5954
6282
|
resolveCallEdge(edgeId, toSymbolId) {
|
|
6283
|
+
this.throwIfClosed();
|
|
5955
6284
|
this.inner.resolveCallEdge(edgeId, toSymbolId);
|
|
5956
6285
|
}
|
|
5957
6286
|
// ── Branch Symbol methods ────────────────────────────────────────
|
|
5958
6287
|
addSymbolsToBranch(branch, symbolIds) {
|
|
6288
|
+
this.throwIfClosed();
|
|
5959
6289
|
this.inner.addSymbolsToBranch(branch, symbolIds);
|
|
5960
6290
|
}
|
|
5961
6291
|
addSymbolsToBranchBatch(branch, symbolIds) {
|
|
6292
|
+
this.throwIfClosed();
|
|
5962
6293
|
if (symbolIds.length === 0) return;
|
|
5963
6294
|
this.inner.addSymbolsToBranchBatch(branch, symbolIds);
|
|
5964
6295
|
}
|
|
5965
6296
|
getBranchSymbolIds(branch) {
|
|
6297
|
+
this.throwIfClosed();
|
|
5966
6298
|
return this.inner.getBranchSymbolIds(branch);
|
|
5967
6299
|
}
|
|
5968
6300
|
clearBranchSymbols(branch) {
|
|
6301
|
+
this.throwIfClosed();
|
|
5969
6302
|
return this.inner.clearBranchSymbols(branch);
|
|
5970
6303
|
}
|
|
6304
|
+
getReferencedSymbolIds(symbolIds) {
|
|
6305
|
+
this.throwIfClosed();
|
|
6306
|
+
if (symbolIds.length === 0) return [];
|
|
6307
|
+
return this.inner.getReferencedSymbolIds(symbolIds);
|
|
6308
|
+
}
|
|
6309
|
+
deleteBranchSymbolsBySymbolIds(symbolIds) {
|
|
6310
|
+
this.throwIfClosed();
|
|
6311
|
+
if (symbolIds.length === 0) return 0;
|
|
6312
|
+
return this.inner.deleteBranchSymbolsBySymbolIds(symbolIds);
|
|
6313
|
+
}
|
|
6314
|
+
deleteBranchSymbolsForBranch(branch, symbolIds) {
|
|
6315
|
+
this.throwIfClosed();
|
|
6316
|
+
if (symbolIds.length === 0) return 0;
|
|
6317
|
+
return this.inner.deleteBranchSymbolsForBranch(branch, symbolIds);
|
|
6318
|
+
}
|
|
5971
6319
|
// ── GC methods for symbols/edges ─────────────────────────────────
|
|
5972
6320
|
gcOrphanSymbols() {
|
|
6321
|
+
this.throwIfClosed();
|
|
5973
6322
|
return this.inner.gcOrphanSymbols();
|
|
5974
6323
|
}
|
|
5975
6324
|
gcOrphanCallEdges() {
|
|
6325
|
+
this.throwIfClosed();
|
|
5976
6326
|
return this.inner.gcOrphanCallEdges();
|
|
5977
6327
|
}
|
|
5978
6328
|
};
|
|
5979
6329
|
|
|
5980
6330
|
// src/indexer/index.ts
|
|
5981
|
-
var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php"]);
|
|
6331
|
+
var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig"]);
|
|
6332
|
+
var CASE_INSENSITIVE_LANGUAGES = /* @__PURE__ */ new Set(["apex"]);
|
|
5982
6333
|
var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
5983
6334
|
"function_declaration",
|
|
5984
6335
|
"function",
|
|
@@ -6000,7 +6351,11 @@ var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
|
6000
6351
|
"enum_item",
|
|
6001
6352
|
"trait_item",
|
|
6002
6353
|
"mod_item",
|
|
6003
|
-
"trait_declaration"
|
|
6354
|
+
"trait_declaration",
|
|
6355
|
+
"trigger_declaration",
|
|
6356
|
+
"test_declaration",
|
|
6357
|
+
"struct_declaration",
|
|
6358
|
+
"union_declaration"
|
|
6004
6359
|
]);
|
|
6005
6360
|
function float32ArrayToBuffer(arr) {
|
|
6006
6361
|
const float32 = new Float32Array(arr);
|
|
@@ -6025,12 +6380,192 @@ function isRateLimitError(error) {
|
|
|
6025
6380
|
const message = getErrorMessage(error);
|
|
6026
6381
|
return message.includes("429") || message.toLowerCase().includes("rate limit") || message.toLowerCase().includes("too many requests");
|
|
6027
6382
|
}
|
|
6383
|
+
function getSafeEmbeddingChunkTokenLimit(provider) {
|
|
6384
|
+
const providerMaxTokens = provider.modelInfo.maxTokens;
|
|
6385
|
+
const maxChunkTokens = Math.max(256, Math.floor(providerMaxTokens * 0.75));
|
|
6386
|
+
return Math.min(2e3, maxChunkTokens);
|
|
6387
|
+
}
|
|
6388
|
+
function getDynamicBatchOptions(provider) {
|
|
6389
|
+
if (provider.provider === "ollama") {
|
|
6390
|
+
return {
|
|
6391
|
+
maxBatchTokens: provider.modelInfo.maxTokens,
|
|
6392
|
+
maxBatchItems: 1
|
|
6393
|
+
};
|
|
6394
|
+
}
|
|
6395
|
+
return {};
|
|
6396
|
+
}
|
|
6397
|
+
function isSqliteCorruptionError(error) {
|
|
6398
|
+
const message = getErrorMessage(error).toLowerCase();
|
|
6399
|
+
return message.includes("database disk image is malformed") || message.includes("file is not a database") || message.includes("database schema is corrupt") || message.includes("sqlite_corrupt");
|
|
6400
|
+
}
|
|
6401
|
+
var STARTUP_WARNING_METADATA_KEY = "index.startupWarning";
|
|
6028
6402
|
var INDEX_METADATA_VERSION = "1";
|
|
6403
|
+
var EMBEDDING_STRATEGY_VERSION = "2";
|
|
6029
6404
|
var RANKING_TOKEN_CACHE_LIMIT = 4096;
|
|
6405
|
+
var RANK_HYBRID_CACHE_LIMIT = 256;
|
|
6406
|
+
function createPendingChunkStorageText(texts) {
|
|
6407
|
+
const primaryText = texts[0]?.text ?? "";
|
|
6408
|
+
if (texts.length <= 1) {
|
|
6409
|
+
return primaryText;
|
|
6410
|
+
}
|
|
6411
|
+
return `${primaryText}
|
|
6412
|
+
|
|
6413
|
+
... [split into ${texts.length} parts for embedding]`;
|
|
6414
|
+
}
|
|
6415
|
+
function normalizePendingChunk(rawChunk, maxChunkTokens) {
|
|
6416
|
+
if (!rawChunk || typeof rawChunk !== "object") {
|
|
6417
|
+
return null;
|
|
6418
|
+
}
|
|
6419
|
+
const chunk = rawChunk;
|
|
6420
|
+
if (typeof chunk.id !== "string" || typeof chunk.contentHash !== "string" || !chunk.metadata || typeof chunk.metadata !== "object") {
|
|
6421
|
+
return null;
|
|
6422
|
+
}
|
|
6423
|
+
const texts = Array.isArray(chunk.texts) ? chunk.texts.map((entry) => {
|
|
6424
|
+
if (!entry || typeof entry.text !== "string") {
|
|
6425
|
+
return null;
|
|
6426
|
+
}
|
|
6427
|
+
return {
|
|
6428
|
+
text: entry.text,
|
|
6429
|
+
tokenCount: typeof entry.tokenCount === "number" && Number.isFinite(entry.tokenCount) ? entry.tokenCount : estimateTokens(entry.text)
|
|
6430
|
+
};
|
|
6431
|
+
}).filter((entry) => entry !== null) : [];
|
|
6432
|
+
if (texts.length === 0 && typeof chunk.text === "string") {
|
|
6433
|
+
if (typeof chunk.content === "string" && chunk.content.length > 0 && chunk.metadata && typeof chunk.metadata === "object") {
|
|
6434
|
+
const metadata = chunk.metadata;
|
|
6435
|
+
const rebuiltChunk = {
|
|
6436
|
+
content: chunk.content,
|
|
6437
|
+
startLine: typeof metadata.startLine === "number" ? metadata.startLine : 1,
|
|
6438
|
+
endLine: typeof metadata.endLine === "number" ? metadata.endLine : 1,
|
|
6439
|
+
chunkType: typeof metadata.chunkType === "string" ? metadata.chunkType : "other",
|
|
6440
|
+
name: typeof metadata.name === "string" ? metadata.name : void 0,
|
|
6441
|
+
language: typeof metadata.language === "string" ? metadata.language : "text"
|
|
6442
|
+
};
|
|
6443
|
+
const filePath = typeof metadata.filePath === "string" ? metadata.filePath : "unknown";
|
|
6444
|
+
texts.push(
|
|
6445
|
+
...createEmbeddingTexts(rebuiltChunk, filePath, maxChunkTokens).map((text) => ({
|
|
6446
|
+
text,
|
|
6447
|
+
tokenCount: estimateTokens(text)
|
|
6448
|
+
}))
|
|
6449
|
+
);
|
|
6450
|
+
} else {
|
|
6451
|
+
texts.push({
|
|
6452
|
+
text: chunk.text,
|
|
6453
|
+
tokenCount: estimateTokens(chunk.text)
|
|
6454
|
+
});
|
|
6455
|
+
}
|
|
6456
|
+
}
|
|
6457
|
+
if (texts.length === 0) {
|
|
6458
|
+
return null;
|
|
6459
|
+
}
|
|
6460
|
+
return {
|
|
6461
|
+
id: chunk.id,
|
|
6462
|
+
texts,
|
|
6463
|
+
storageText: typeof chunk.storageText === "string" ? chunk.storageText : createPendingChunkStorageText(texts),
|
|
6464
|
+
content: typeof chunk.content === "string" ? chunk.content : "",
|
|
6465
|
+
contentHash: chunk.contentHash,
|
|
6466
|
+
metadata: chunk.metadata
|
|
6467
|
+
};
|
|
6468
|
+
}
|
|
6469
|
+
function getPendingChunkFilePath(rawChunk) {
|
|
6470
|
+
if (!rawChunk || typeof rawChunk !== "object") {
|
|
6471
|
+
return null;
|
|
6472
|
+
}
|
|
6473
|
+
const chunk = rawChunk;
|
|
6474
|
+
if (!chunk.metadata || typeof chunk.metadata !== "object") {
|
|
6475
|
+
return null;
|
|
6476
|
+
}
|
|
6477
|
+
const metadata = chunk.metadata;
|
|
6478
|
+
return typeof metadata.filePath === "string" ? metadata.filePath : null;
|
|
6479
|
+
}
|
|
6480
|
+
function normalizeFailedBatch(batch, maxChunkTokens) {
|
|
6481
|
+
const chunks = batch.chunks.map((chunk) => normalizePendingChunk(chunk, maxChunkTokens)).filter((chunk) => chunk !== null);
|
|
6482
|
+
if (chunks.length === 0) {
|
|
6483
|
+
return null;
|
|
6484
|
+
}
|
|
6485
|
+
return {
|
|
6486
|
+
chunks,
|
|
6487
|
+
error: batch.error,
|
|
6488
|
+
attemptCount: batch.attemptCount,
|
|
6489
|
+
lastAttempt: batch.lastAttempt
|
|
6490
|
+
};
|
|
6491
|
+
}
|
|
6492
|
+
function createPendingEmbeddingRequests(chunks) {
|
|
6493
|
+
return chunks.flatMap(
|
|
6494
|
+
(chunk) => chunk.texts.map((textPart, partIndex) => ({
|
|
6495
|
+
chunk,
|
|
6496
|
+
partIndex,
|
|
6497
|
+
text: textPart.text,
|
|
6498
|
+
tokenCount: textPart.tokenCount
|
|
6499
|
+
}))
|
|
6500
|
+
);
|
|
6501
|
+
}
|
|
6502
|
+
function createPendingEmbeddingRequestBatches(chunks, options = {}) {
|
|
6503
|
+
return createDynamicBatches(createPendingEmbeddingRequests(chunks), options);
|
|
6504
|
+
}
|
|
6505
|
+
function getUniquePendingChunksFromRequests(requests) {
|
|
6506
|
+
const uniqueChunks = /* @__PURE__ */ new Map();
|
|
6507
|
+
for (const request of requests) {
|
|
6508
|
+
uniqueChunks.set(request.chunk.id, request.chunk);
|
|
6509
|
+
}
|
|
6510
|
+
return Array.from(uniqueChunks.values());
|
|
6511
|
+
}
|
|
6512
|
+
function coalesceFailedBatches(batches) {
|
|
6513
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
6514
|
+
for (const batch of batches) {
|
|
6515
|
+
const key = `${batch.attemptCount}:${batch.lastAttempt}:${batch.error}`;
|
|
6516
|
+
const existing = grouped.get(key);
|
|
6517
|
+
if (!existing) {
|
|
6518
|
+
grouped.set(key, {
|
|
6519
|
+
...batch,
|
|
6520
|
+
chunks: [...batch.chunks]
|
|
6521
|
+
});
|
|
6522
|
+
continue;
|
|
6523
|
+
}
|
|
6524
|
+
existing.chunks.push(...batch.chunks);
|
|
6525
|
+
}
|
|
6526
|
+
return Array.from(grouped.values());
|
|
6527
|
+
}
|
|
6528
|
+
function poolEmbeddingVectors(vectors, weights) {
|
|
6529
|
+
const firstVector = vectors[0];
|
|
6530
|
+
if (!firstVector) {
|
|
6531
|
+
return [];
|
|
6532
|
+
}
|
|
6533
|
+
const pooled = new Array(firstVector.length).fill(0);
|
|
6534
|
+
let totalWeight = 0;
|
|
6535
|
+
for (let index = 0; index < vectors.length; index++) {
|
|
6536
|
+
const vector = vectors[index];
|
|
6537
|
+
const weight = Math.max(1, weights[index] ?? 1);
|
|
6538
|
+
totalWeight += weight;
|
|
6539
|
+
for (let dimension = 0; dimension < vector.length; dimension++) {
|
|
6540
|
+
pooled[dimension] += vector[dimension] * weight;
|
|
6541
|
+
}
|
|
6542
|
+
}
|
|
6543
|
+
if (totalWeight === 0) {
|
|
6544
|
+
return firstVector;
|
|
6545
|
+
}
|
|
6546
|
+
return pooled.map((value) => value / totalWeight);
|
|
6547
|
+
}
|
|
6548
|
+
function hasAllEmbeddingParts(parts, expectedPartCount) {
|
|
6549
|
+
if (parts.length !== expectedPartCount) {
|
|
6550
|
+
return false;
|
|
6551
|
+
}
|
|
6552
|
+
for (let index = 0; index < expectedPartCount; index++) {
|
|
6553
|
+
if (parts[index] === void 0) {
|
|
6554
|
+
return false;
|
|
6555
|
+
}
|
|
6556
|
+
}
|
|
6557
|
+
return true;
|
|
6558
|
+
}
|
|
6559
|
+
function isPathWithinRoot(filePath, rootPath) {
|
|
6560
|
+
const normalizedFilePath = path8.resolve(filePath);
|
|
6561
|
+
const normalizedRoot = path8.resolve(rootPath);
|
|
6562
|
+
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path8.sep}`);
|
|
6563
|
+
}
|
|
6030
6564
|
var rankingQueryTokenCache = /* @__PURE__ */ new Map();
|
|
6031
6565
|
var rankingNameTokenCache = /* @__PURE__ */ new Map();
|
|
6032
6566
|
var rankingPathTokenCache = /* @__PURE__ */ new Map();
|
|
6033
6567
|
var rankingTextTokenCache = /* @__PURE__ */ new Map();
|
|
6568
|
+
var rankHybridResultsCache = /* @__PURE__ */ new WeakMap();
|
|
6034
6569
|
var STOPWORDS = /* @__PURE__ */ new Set([
|
|
6035
6570
|
"the",
|
|
6036
6571
|
"and",
|
|
@@ -6246,7 +6781,16 @@ function classifyExternalRerankBand(candidate, preferSourcePaths, docIntent) {
|
|
|
6246
6781
|
function classifyQueryIntent(tokens) {
|
|
6247
6782
|
const sourceIntentHits = tokens.filter((t) => SOURCE_INTENT_HINTS.has(t)).length;
|
|
6248
6783
|
const docTestIntentHits = tokens.filter((t) => DOC_TEST_INTENT_HINTS.has(t)).length;
|
|
6249
|
-
|
|
6784
|
+
if (sourceIntentHits === 0 && docTestIntentHits === 0) {
|
|
6785
|
+
return "neutral";
|
|
6786
|
+
}
|
|
6787
|
+
if (sourceIntentHits > docTestIntentHits) {
|
|
6788
|
+
return "source";
|
|
6789
|
+
}
|
|
6790
|
+
if (docTestIntentHits > sourceIntentHits) {
|
|
6791
|
+
return "doc_test";
|
|
6792
|
+
}
|
|
6793
|
+
return "neutral";
|
|
6250
6794
|
}
|
|
6251
6795
|
function classifyQueryIntentRaw(query) {
|
|
6252
6796
|
const lowerQuery = query.toLowerCase();
|
|
@@ -6268,7 +6812,7 @@ function classifyQueryIntentRaw(query) {
|
|
|
6268
6812
|
if (docTestRawHits > sourceRawHits) {
|
|
6269
6813
|
return "doc_test";
|
|
6270
6814
|
}
|
|
6271
|
-
if (sourceRawHits > docTestRawHits) {
|
|
6815
|
+
if (sourceRawHits > docTestRawHits && sourceRawHits > 0) {
|
|
6272
6816
|
return "source";
|
|
6273
6817
|
}
|
|
6274
6818
|
const hasWhereIsPattern = /\bwhere\s+is\b/.test(lowerQuery);
|
|
@@ -6535,9 +7079,8 @@ function rerankResults(query, candidates, rerankTopN, options) {
|
|
|
6535
7079
|
return candidates;
|
|
6536
7080
|
}
|
|
6537
7081
|
const queryTokenList = Array.from(queryTokens);
|
|
6538
|
-
const intent = classifyQueryIntentRaw(query);
|
|
6539
7082
|
const docIntent = classifyDocIntent(queryTokenList);
|
|
6540
|
-
const preferSourcePaths = options?.prioritizeSourcePaths ??
|
|
7083
|
+
const preferSourcePaths = options?.prioritizeSourcePaths ?? classifyQueryIntentRaw(query) === "source";
|
|
6541
7084
|
const identifierHints = extractIdentifierHints(query);
|
|
6542
7085
|
const head = candidates.slice(0, topN).map((candidate, idx) => {
|
|
6543
7086
|
const pathTokens = splitPathTokens(candidate.metadata.filePath);
|
|
@@ -6687,13 +7230,38 @@ function buildDiversityKey(metadata) {
|
|
|
6687
7230
|
return normalizedPath;
|
|
6688
7231
|
}
|
|
6689
7232
|
function rankHybridResults(query, semanticResults, keywordResults, options) {
|
|
7233
|
+
const prioritizeSourcePaths = options.prioritizeSourcePaths ?? classifyQueryIntentRaw(query) === "source";
|
|
7234
|
+
const cacheKey = `${query}${options.fusionStrategy}|${options.rrfK}|${options.hybridWeight}|${options.rerankTopN}|${options.limit}|${prioritizeSourcePaths ? 1 : 0}`;
|
|
7235
|
+
let byKeyword = rankHybridResultsCache.get(semanticResults);
|
|
7236
|
+
if (!byKeyword) {
|
|
7237
|
+
byKeyword = /* @__PURE__ */ new WeakMap();
|
|
7238
|
+
rankHybridResultsCache.set(semanticResults, byKeyword);
|
|
7239
|
+
}
|
|
7240
|
+
let bucket = byKeyword.get(keywordResults);
|
|
7241
|
+
if (!bucket) {
|
|
7242
|
+
bucket = /* @__PURE__ */ new Map();
|
|
7243
|
+
byKeyword.set(keywordResults, bucket);
|
|
7244
|
+
} else {
|
|
7245
|
+
const cached = bucket.get(cacheKey);
|
|
7246
|
+
if (cached) {
|
|
7247
|
+
return cached;
|
|
7248
|
+
}
|
|
7249
|
+
}
|
|
6690
7250
|
const overfetchLimit = Math.max(options.limit * 4, options.limit);
|
|
6691
7251
|
const fused = options.fusionStrategy === "rrf" ? fuseResultsRrf(semanticResults, keywordResults, options.rrfK, overfetchLimit) : fuseResultsWeighted(semanticResults, keywordResults, options.hybridWeight, overfetchLimit);
|
|
6692
7252
|
const rerankPoolLimit = Math.max(overfetchLimit, options.rerankTopN * 3, options.limit * 6);
|
|
6693
7253
|
const rerankPool = fused.slice(0, rerankPoolLimit);
|
|
6694
|
-
|
|
6695
|
-
prioritizeSourcePaths
|
|
7254
|
+
const ranked = rerankResults(query, rerankPool, options.rerankTopN, {
|
|
7255
|
+
prioritizeSourcePaths
|
|
6696
7256
|
});
|
|
7257
|
+
if (bucket.size >= RANK_HYBRID_CACHE_LIMIT) {
|
|
7258
|
+
const oldest = bucket.keys().next().value;
|
|
7259
|
+
if (oldest !== void 0) {
|
|
7260
|
+
bucket.delete(oldest);
|
|
7261
|
+
}
|
|
7262
|
+
}
|
|
7263
|
+
bucket.set(cacheKey, ranked);
|
|
7264
|
+
return ranked;
|
|
6697
7265
|
}
|
|
6698
7266
|
function rankSemanticOnlyResults(query, semanticResults, options) {
|
|
6699
7267
|
const overfetchLimit = Math.max(options.limit * 4, options.limit);
|
|
@@ -6990,6 +7558,21 @@ function mergeTieredResults(symbolLane, hybridLane, limit) {
|
|
|
6990
7558
|
}
|
|
6991
7559
|
return out;
|
|
6992
7560
|
}
|
|
7561
|
+
function matchesSearchFilters(candidate, options, minScore) {
|
|
7562
|
+
if (candidate.score < minScore) return false;
|
|
7563
|
+
if (options?.fileType) {
|
|
7564
|
+
const ext = candidate.metadata.filePath.split(".").pop()?.toLowerCase();
|
|
7565
|
+
if (ext !== options.fileType.toLowerCase().replace(/^\./, "")) return false;
|
|
7566
|
+
}
|
|
7567
|
+
if (options?.directory) {
|
|
7568
|
+
const normalizedDir = options.directory.replace(/^\/|\/$/g, "");
|
|
7569
|
+
if (!candidate.metadata.filePath.includes(`/${normalizedDir}/`) && !candidate.metadata.filePath.includes(`${normalizedDir}/`)) return false;
|
|
7570
|
+
}
|
|
7571
|
+
if (options?.chunkType && candidate.metadata.chunkType !== options.chunkType) {
|
|
7572
|
+
return false;
|
|
7573
|
+
}
|
|
7574
|
+
return true;
|
|
7575
|
+
}
|
|
6993
7576
|
function unionCandidates(semanticCandidates, keywordCandidates) {
|
|
6994
7577
|
const byId = /* @__PURE__ */ new Map();
|
|
6995
7578
|
for (const candidate of semanticCandidates) {
|
|
@@ -7029,21 +7612,17 @@ var Indexer = class {
|
|
|
7029
7612
|
this.projectRoot = projectRoot;
|
|
7030
7613
|
this.config = config;
|
|
7031
7614
|
this.indexPath = this.getIndexPath();
|
|
7032
|
-
this.fileHashCachePath =
|
|
7033
|
-
this.failedBatchesPath =
|
|
7034
|
-
this.indexingLockPath =
|
|
7615
|
+
this.fileHashCachePath = path8.join(this.indexPath, "file-hashes.json");
|
|
7616
|
+
this.failedBatchesPath = path8.join(this.indexPath, "failed-batches.json");
|
|
7617
|
+
this.indexingLockPath = path8.join(this.indexPath, "indexing.lock");
|
|
7035
7618
|
this.logger = initializeLogger(config.debug);
|
|
7036
7619
|
}
|
|
7037
7620
|
getIndexPath() {
|
|
7038
|
-
|
|
7039
|
-
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
7040
|
-
return path7.join(homeDir, ".opencode", "global-index");
|
|
7041
|
-
}
|
|
7042
|
-
return path7.join(this.projectRoot, ".opencode", "index");
|
|
7621
|
+
return resolveProjectIndexPath(this.projectRoot, this.config.scope);
|
|
7043
7622
|
}
|
|
7044
7623
|
loadFileHashCache() {
|
|
7045
7624
|
try {
|
|
7046
|
-
if (
|
|
7625
|
+
if (existsSync6(this.fileHashCachePath)) {
|
|
7047
7626
|
const data = readFileSync5(this.fileHashCachePath, "utf-8");
|
|
7048
7627
|
const parsed = JSON.parse(data);
|
|
7049
7628
|
this.fileHashCache = new Map(Object.entries(parsed));
|
|
@@ -7061,95 +7640,455 @@ var Indexer = class {
|
|
|
7061
7640
|
}
|
|
7062
7641
|
atomicWriteSync(targetPath, data) {
|
|
7063
7642
|
const tempPath = `${targetPath}.tmp`;
|
|
7064
|
-
|
|
7643
|
+
mkdirSync2(path8.dirname(targetPath), { recursive: true });
|
|
7644
|
+
writeFileSync2(tempPath, data);
|
|
7065
7645
|
renameSync(tempPath, targetPath);
|
|
7066
7646
|
}
|
|
7067
|
-
|
|
7068
|
-
|
|
7647
|
+
getScopedRoots() {
|
|
7648
|
+
const roots = /* @__PURE__ */ new Set([path8.resolve(this.projectRoot)]);
|
|
7649
|
+
for (const kbRoot of this.config.knowledgeBases) {
|
|
7650
|
+
roots.add(path8.resolve(this.projectRoot, kbRoot));
|
|
7651
|
+
}
|
|
7652
|
+
return Array.from(roots);
|
|
7069
7653
|
}
|
|
7070
|
-
|
|
7071
|
-
const
|
|
7072
|
-
|
|
7073
|
-
|
|
7074
|
-
}
|
|
7075
|
-
|
|
7654
|
+
getBranchCatalogKey() {
|
|
7655
|
+
const branchName = this.currentBranch || "default";
|
|
7656
|
+
if (this.config.scope !== "global") {
|
|
7657
|
+
return branchName;
|
|
7658
|
+
}
|
|
7659
|
+
const projectHash = hashContent(path8.resolve(this.projectRoot)).slice(0, 16);
|
|
7660
|
+
return `${projectHash}:${branchName}`;
|
|
7076
7661
|
}
|
|
7077
|
-
|
|
7078
|
-
|
|
7079
|
-
|
|
7662
|
+
getLegacyBranchCatalogKey() {
|
|
7663
|
+
return this.currentBranch || "default";
|
|
7664
|
+
}
|
|
7665
|
+
getLegacyMigrationMetadataKey() {
|
|
7666
|
+
const projectHash = hashContent(path8.resolve(this.projectRoot)).slice(0, 16);
|
|
7667
|
+
return `index.globalBranchMigration.${projectHash}`;
|
|
7668
|
+
}
|
|
7669
|
+
getProjectEmbeddingStrategyMetadataKey() {
|
|
7670
|
+
const projectHash = hashContent(path8.resolve(this.projectRoot)).slice(0, 16);
|
|
7671
|
+
return `index.embeddingStrategyVersion.${projectHash}`;
|
|
7672
|
+
}
|
|
7673
|
+
getProjectForceReembedMetadataKey() {
|
|
7674
|
+
const projectHash = hashContent(path8.resolve(this.projectRoot)).slice(0, 16);
|
|
7675
|
+
return `index.forceReembed.${projectHash}`;
|
|
7676
|
+
}
|
|
7677
|
+
hasProjectForceReembedPending() {
|
|
7678
|
+
return this.config.scope === "global" && this.database?.getMetadata(this.getProjectForceReembedMetadataKey()) === "true";
|
|
7679
|
+
}
|
|
7680
|
+
hasScopedIndexedData() {
|
|
7681
|
+
if (!this.store || this.config.scope !== "global") {
|
|
7682
|
+
return false;
|
|
7683
|
+
}
|
|
7684
|
+
if (this.hasProjectForceReembedPending()) {
|
|
7685
|
+
return false;
|
|
7686
|
+
}
|
|
7687
|
+
const roots = this.getScopedRoots();
|
|
7688
|
+
if (Array.from(this.fileHashCache.keys()).some((filePath) => this.isFileInCurrentScope(filePath, roots))) {
|
|
7689
|
+
return true;
|
|
7690
|
+
}
|
|
7691
|
+
if (this.loadSerializedFailedBatches().some(
|
|
7692
|
+
(batch) => batch.chunks.some((chunk) => {
|
|
7693
|
+
const filePath = getPendingChunkFilePath(chunk);
|
|
7694
|
+
return filePath !== null && this.isFileInCurrentScope(filePath, roots);
|
|
7695
|
+
})
|
|
7696
|
+
)) {
|
|
7697
|
+
return true;
|
|
7698
|
+
}
|
|
7699
|
+
if (!this.database) {
|
|
7700
|
+
return false;
|
|
7701
|
+
}
|
|
7702
|
+
if (this.getBranchCatalogKeys().some((branchKey) => {
|
|
7703
|
+
const branchChunkIds = this.database.getBranchChunkIds(branchKey);
|
|
7704
|
+
if (branchChunkIds.length > 0) {
|
|
7705
|
+
return true;
|
|
7706
|
+
}
|
|
7707
|
+
return this.database.getBranchSymbolIds(branchKey).length > 0;
|
|
7708
|
+
})) {
|
|
7709
|
+
return true;
|
|
7080
7710
|
}
|
|
7711
|
+
const hasAnyBranchRows = this.database.getAllBranches().some((branchKey) => {
|
|
7712
|
+
const branchChunkIds = this.database.getBranchChunkIds(branchKey);
|
|
7713
|
+
if (branchChunkIds.length > 0) {
|
|
7714
|
+
return true;
|
|
7715
|
+
}
|
|
7716
|
+
return this.database.getBranchSymbolIds(branchKey).length > 0;
|
|
7717
|
+
});
|
|
7718
|
+
if (hasAnyBranchRows) {
|
|
7719
|
+
return false;
|
|
7720
|
+
}
|
|
7721
|
+
return this.store.getAllMetadata().some(({ metadata }) => this.isFileInCurrentScope(metadata.filePath, roots));
|
|
7081
7722
|
}
|
|
7082
|
-
|
|
7083
|
-
this.
|
|
7084
|
-
|
|
7085
|
-
unlinkSync(this.fileHashCachePath);
|
|
7723
|
+
loadStoredEmbeddingStrategyVersion() {
|
|
7724
|
+
if (!this.database) {
|
|
7725
|
+
return null;
|
|
7086
7726
|
}
|
|
7087
|
-
|
|
7088
|
-
|
|
7089
|
-
|
|
7727
|
+
if (this.hasProjectForceReembedPending()) {
|
|
7728
|
+
return null;
|
|
7729
|
+
}
|
|
7730
|
+
if (this.config.scope !== "global") {
|
|
7731
|
+
return this.database.getMetadata("index.embeddingStrategyVersion") ?? "1";
|
|
7732
|
+
}
|
|
7733
|
+
const projectVersion = this.database.getMetadata(this.getProjectEmbeddingStrategyMetadataKey());
|
|
7734
|
+
if (projectVersion) {
|
|
7735
|
+
return projectVersion;
|
|
7736
|
+
}
|
|
7737
|
+
const legacySharedVersion = this.database.getMetadata("index.embeddingStrategyVersion");
|
|
7738
|
+
if (legacySharedVersion && this.hasScopedIndexedData()) {
|
|
7739
|
+
return legacySharedVersion;
|
|
7740
|
+
}
|
|
7741
|
+
return null;
|
|
7090
7742
|
}
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
|
|
7094
|
-
|
|
7095
|
-
|
|
7743
|
+
getBranchCatalogKeys() {
|
|
7744
|
+
const primary = this.getBranchCatalogKey();
|
|
7745
|
+
if (this.config.scope !== "global") {
|
|
7746
|
+
return [primary];
|
|
7747
|
+
}
|
|
7748
|
+
if (this.database?.getMetadata(this.getLegacyMigrationMetadataKey()) === "done") {
|
|
7749
|
+
return [primary];
|
|
7750
|
+
}
|
|
7751
|
+
const legacy = this.getLegacyBranchCatalogKey();
|
|
7752
|
+
return primary === legacy ? [primary] : [primary, legacy];
|
|
7753
|
+
}
|
|
7754
|
+
getBranchCatalogCleanupKeys() {
|
|
7755
|
+
const primary = this.getBranchCatalogKey();
|
|
7756
|
+
if (this.config.scope !== "global") {
|
|
7757
|
+
return [primary];
|
|
7758
|
+
}
|
|
7759
|
+
const legacy = this.getLegacyBranchCatalogKey();
|
|
7760
|
+
return primary === legacy ? [primary] : [primary, legacy];
|
|
7761
|
+
}
|
|
7762
|
+
getProjectLocalScopedOwnershipIds(roots) {
|
|
7763
|
+
const chunkIds = /* @__PURE__ */ new Set();
|
|
7764
|
+
const symbolIds = /* @__PURE__ */ new Set();
|
|
7765
|
+
if (!this.database) {
|
|
7766
|
+
return { chunkIds, symbolIds };
|
|
7767
|
+
}
|
|
7768
|
+
const projectRootPath = path8.resolve(this.projectRoot);
|
|
7769
|
+
const projectLocalFilePaths = /* @__PURE__ */ new Set([
|
|
7770
|
+
...Array.from(this.fileHashCache.keys()).filter(
|
|
7771
|
+
(filePath) => this.isFileInCurrentScope(filePath, roots) && isPathWithinRoot(filePath, projectRootPath)
|
|
7772
|
+
),
|
|
7773
|
+
...(this.store?.getAllMetadata() ?? []).map(({ metadata }) => metadata.filePath).filter(
|
|
7774
|
+
(filePath) => this.isFileInCurrentScope(filePath, roots) && isPathWithinRoot(filePath, projectRootPath)
|
|
7775
|
+
)
|
|
7776
|
+
]);
|
|
7777
|
+
for (const filePath of projectLocalFilePaths) {
|
|
7778
|
+
for (const chunk of this.database.getChunksByFile(filePath)) {
|
|
7779
|
+
chunkIds.add(chunk.chunkId);
|
|
7780
|
+
}
|
|
7781
|
+
for (const symbol of this.database.getSymbolsByFile(filePath)) {
|
|
7782
|
+
symbolIds.add(symbol.id);
|
|
7096
7783
|
}
|
|
7097
|
-
} catch {
|
|
7098
|
-
return [];
|
|
7099
7784
|
}
|
|
7100
|
-
return
|
|
7785
|
+
return { chunkIds, symbolIds };
|
|
7101
7786
|
}
|
|
7102
|
-
|
|
7103
|
-
if (
|
|
7104
|
-
|
|
7105
|
-
|
|
7106
|
-
|
|
7787
|
+
getProjectScopedBranchCatalogCleanupKeys(projectChunkIds, projectSymbolIds) {
|
|
7788
|
+
if (this.config.scope !== "global") {
|
|
7789
|
+
return this.getBranchCatalogCleanupKeys();
|
|
7790
|
+
}
|
|
7791
|
+
const projectHash = hashContent(path8.resolve(this.projectRoot)).slice(0, 16);
|
|
7792
|
+
const keys = /* @__PURE__ */ new Set();
|
|
7793
|
+
const projectChunkIdSet = new Set(projectChunkIds);
|
|
7794
|
+
const projectSymbolIdSet = new Set(projectSymbolIds);
|
|
7795
|
+
for (const branchKey of this.database?.getAllBranches() ?? []) {
|
|
7796
|
+
if (branchKey.startsWith(`${projectHash}:`)) {
|
|
7797
|
+
keys.add(branchKey);
|
|
7798
|
+
continue;
|
|
7107
7799
|
}
|
|
7108
|
-
|
|
7800
|
+
if (branchKey.includes(":")) {
|
|
7801
|
+
continue;
|
|
7802
|
+
}
|
|
7803
|
+
const referencesProjectChunks = this.database?.getBranchChunkIds(branchKey).some((chunkId) => projectChunkIdSet.has(chunkId)) ?? false;
|
|
7804
|
+
const referencesProjectSymbols = this.database?.getBranchSymbolIds(branchKey).some((symbolId) => projectSymbolIdSet.has(symbolId)) ?? false;
|
|
7805
|
+
if (referencesProjectChunks || referencesProjectSymbols) {
|
|
7806
|
+
keys.add(branchKey);
|
|
7807
|
+
}
|
|
7808
|
+
}
|
|
7809
|
+
for (const branchKey of this.getBranchCatalogCleanupKeys()) {
|
|
7810
|
+
keys.add(branchKey);
|
|
7109
7811
|
}
|
|
7110
|
-
|
|
7812
|
+
return Array.from(keys);
|
|
7111
7813
|
}
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
existing.push({
|
|
7115
|
-
chunks: batch,
|
|
7116
|
-
error,
|
|
7117
|
-
attemptCount: 1,
|
|
7118
|
-
lastAttempt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7119
|
-
});
|
|
7120
|
-
this.saveFailedBatches(existing);
|
|
7814
|
+
isFileInCurrentScope(filePath, roots) {
|
|
7815
|
+
return roots.some((root) => isPathWithinRoot(filePath, root));
|
|
7121
7816
|
}
|
|
7122
|
-
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
|
|
7126
|
-
case "openai":
|
|
7127
|
-
return { concurrency: 3, intervalMs: 500, minRetryMs: 1e3, maxRetryMs: 3e4 };
|
|
7128
|
-
case "google":
|
|
7129
|
-
return { concurrency: 5, intervalMs: 200, minRetryMs: 1e3, maxRetryMs: 3e4 };
|
|
7130
|
-
case "ollama":
|
|
7131
|
-
return { concurrency: 5, intervalMs: 0, minRetryMs: 500, maxRetryMs: 5e3 };
|
|
7132
|
-
case "custom": {
|
|
7133
|
-
const customConfig = this.config.customProvider;
|
|
7134
|
-
return {
|
|
7135
|
-
concurrency: customConfig?.concurrency ?? 3,
|
|
7136
|
-
intervalMs: customConfig?.requestIntervalMs ?? 1e3,
|
|
7137
|
-
minRetryMs: 1e3,
|
|
7138
|
-
maxRetryMs: 3e4
|
|
7139
|
-
};
|
|
7817
|
+
clearScopedFileHashCache(roots) {
|
|
7818
|
+
for (const filePath of Array.from(this.fileHashCache.keys())) {
|
|
7819
|
+
if (this.isFileInCurrentScope(filePath, roots)) {
|
|
7820
|
+
this.fileHashCache.delete(filePath);
|
|
7140
7821
|
}
|
|
7141
|
-
default:
|
|
7142
|
-
return { concurrency: 3, intervalMs: 1e3, minRetryMs: 1e3, maxRetryMs: 3e4 };
|
|
7143
7822
|
}
|
|
7823
|
+
this.saveFileHashCache();
|
|
7144
7824
|
}
|
|
7145
|
-
|
|
7146
|
-
const
|
|
7147
|
-
|
|
7148
|
-
|
|
7825
|
+
replaceScopedFileHashCache(currentFileHashes, roots) {
|
|
7826
|
+
for (const filePath of Array.from(this.fileHashCache.keys())) {
|
|
7827
|
+
if (this.isFileInCurrentScope(filePath, roots)) {
|
|
7828
|
+
this.fileHashCache.delete(filePath);
|
|
7829
|
+
}
|
|
7149
7830
|
}
|
|
7150
|
-
const
|
|
7151
|
-
|
|
7152
|
-
|
|
7831
|
+
for (const [filePath, hash] of currentFileHashes) {
|
|
7832
|
+
this.fileHashCache.set(filePath, hash);
|
|
7833
|
+
}
|
|
7834
|
+
this.saveFileHashCache();
|
|
7835
|
+
}
|
|
7836
|
+
partitionFailedBatches(roots, maxChunkTokens) {
|
|
7837
|
+
const scoped = [];
|
|
7838
|
+
const retained = [];
|
|
7839
|
+
for (const batch of this.loadSerializedFailedBatches()) {
|
|
7840
|
+
const scopedChunks = batch.chunks.filter((chunk) => {
|
|
7841
|
+
const filePath = getPendingChunkFilePath(chunk);
|
|
7842
|
+
return filePath !== null && this.isFileInCurrentScope(filePath, roots);
|
|
7843
|
+
});
|
|
7844
|
+
const retainedChunks = batch.chunks.filter((chunk) => {
|
|
7845
|
+
const filePath = getPendingChunkFilePath(chunk);
|
|
7846
|
+
return filePath === null || !this.isFileInCurrentScope(filePath, roots);
|
|
7847
|
+
});
|
|
7848
|
+
if (scopedChunks.length > 0) {
|
|
7849
|
+
const normalizedBatch = normalizeFailedBatch({ ...batch, chunks: scopedChunks }, maxChunkTokens);
|
|
7850
|
+
if (normalizedBatch) {
|
|
7851
|
+
scoped.push(normalizedBatch);
|
|
7852
|
+
}
|
|
7853
|
+
}
|
|
7854
|
+
if (retainedChunks.length > 0) {
|
|
7855
|
+
retained.push({ ...batch, chunks: retainedChunks });
|
|
7856
|
+
}
|
|
7857
|
+
}
|
|
7858
|
+
return { scoped, retained };
|
|
7859
|
+
}
|
|
7860
|
+
clearScopedFailedBatches(roots) {
|
|
7861
|
+
const { retained: retainedBatches } = this.partitionFailedBatches(roots);
|
|
7862
|
+
this.saveFailedBatches(retainedBatches);
|
|
7863
|
+
}
|
|
7864
|
+
hasForeignScopedFileHashData(roots) {
|
|
7865
|
+
return Array.from(this.fileHashCache.keys()).some((filePath) => !this.isFileInCurrentScope(filePath, roots));
|
|
7866
|
+
}
|
|
7867
|
+
hasForeignScopedFailedBatches(roots) {
|
|
7868
|
+
const { retained } = this.partitionFailedBatches(roots);
|
|
7869
|
+
return retained.length > 0;
|
|
7870
|
+
}
|
|
7871
|
+
hasForeignScopedBranchData() {
|
|
7872
|
+
if (!this.database || this.config.scope !== "global") {
|
|
7873
|
+
return false;
|
|
7874
|
+
}
|
|
7875
|
+
const projectHash = hashContent(path8.resolve(this.projectRoot)).slice(0, 16);
|
|
7876
|
+
const roots = this.getScopedRoots();
|
|
7877
|
+
const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots);
|
|
7878
|
+
return this.database.getAllBranches().some(
|
|
7879
|
+
(branchKey) => {
|
|
7880
|
+
const branchChunkIds = this.database.getBranchChunkIds(branchKey);
|
|
7881
|
+
const branchSymbolIds = this.database.getBranchSymbolIds(branchKey);
|
|
7882
|
+
const hasBranchData = branchChunkIds.length > 0 || branchSymbolIds.length > 0;
|
|
7883
|
+
if (!hasBranchData) {
|
|
7884
|
+
return false;
|
|
7885
|
+
}
|
|
7886
|
+
if (branchKey.startsWith(`${projectHash}:`)) {
|
|
7887
|
+
return false;
|
|
7888
|
+
}
|
|
7889
|
+
if (!branchKey.includes(":")) {
|
|
7890
|
+
const referencesCurrentProjectChunks = branchChunkIds.some((chunkId) => projectLocalChunkIds.has(chunkId));
|
|
7891
|
+
const referencesCurrentProjectSymbols = branchSymbolIds.some((symbolId) => projectLocalSymbolIds.has(symbolId));
|
|
7892
|
+
return !(referencesCurrentProjectChunks || referencesCurrentProjectSymbols);
|
|
7893
|
+
}
|
|
7894
|
+
return true;
|
|
7895
|
+
}
|
|
7896
|
+
);
|
|
7897
|
+
}
|
|
7898
|
+
saveScopedFailedBatches(batches, roots) {
|
|
7899
|
+
const { retained } = this.partitionFailedBatches(roots);
|
|
7900
|
+
this.saveFailedBatches([...retained, ...batches]);
|
|
7901
|
+
}
|
|
7902
|
+
clearSharedIndexProjectData(store, invertedIndex, database, roots) {
|
|
7903
|
+
const allMetadata = store.getAllMetadata();
|
|
7904
|
+
const scopedEntries = allMetadata.filter(({ metadata }) => this.isFileInCurrentScope(metadata.filePath, roots));
|
|
7905
|
+
const filePaths = /* @__PURE__ */ new Set([
|
|
7906
|
+
...Array.from(this.fileHashCache.keys()).filter((filePath) => this.isFileInCurrentScope(filePath, roots)),
|
|
7907
|
+
...scopedEntries.map(({ metadata }) => metadata.filePath)
|
|
7908
|
+
]);
|
|
7909
|
+
const projectRootPath = path8.resolve(this.projectRoot);
|
|
7910
|
+
const projectLocalFilePaths = new Set(
|
|
7911
|
+
Array.from(filePaths).filter((filePath) => isPathWithinRoot(filePath, projectRootPath))
|
|
7912
|
+
);
|
|
7913
|
+
const removedChunkIds = new Set(scopedEntries.map(({ key }) => key));
|
|
7914
|
+
for (const filePath of filePaths) {
|
|
7915
|
+
for (const chunk of database.getChunksByFile(filePath)) {
|
|
7916
|
+
removedChunkIds.add(chunk.chunkId);
|
|
7917
|
+
}
|
|
7918
|
+
}
|
|
7919
|
+
const removedChunkIdList = Array.from(removedChunkIds);
|
|
7920
|
+
const projectLocalChunkIds = new Set(
|
|
7921
|
+
scopedEntries.filter(({ metadata }) => isPathWithinRoot(metadata.filePath, projectRootPath)).map(({ key }) => key)
|
|
7922
|
+
);
|
|
7923
|
+
for (const filePath of projectLocalFilePaths) {
|
|
7924
|
+
for (const chunk of database.getChunksByFile(filePath)) {
|
|
7925
|
+
projectLocalChunkIds.add(chunk.chunkId);
|
|
7926
|
+
}
|
|
7927
|
+
}
|
|
7928
|
+
const symbolIds = [];
|
|
7929
|
+
const projectLocalSymbolIds = /* @__PURE__ */ new Set();
|
|
7930
|
+
for (const filePath of filePaths) {
|
|
7931
|
+
for (const symbol of database.getSymbolsByFile(filePath)) {
|
|
7932
|
+
symbolIds.push(symbol.id);
|
|
7933
|
+
if (projectLocalFilePaths.has(filePath)) {
|
|
7934
|
+
projectLocalSymbolIds.add(symbol.id);
|
|
7935
|
+
}
|
|
7936
|
+
}
|
|
7937
|
+
}
|
|
7938
|
+
for (const branchKey of this.getProjectScopedBranchCatalogCleanupKeys(Array.from(projectLocalChunkIds), Array.from(projectLocalSymbolIds))) {
|
|
7939
|
+
database.deleteBranchChunksForBranch(branchKey, removedChunkIdList);
|
|
7940
|
+
}
|
|
7941
|
+
const sharedChunkIds = new Set(database.getReferencedChunkIds(removedChunkIdList));
|
|
7942
|
+
const removableChunkIds = removedChunkIdList.filter((chunkId) => !sharedChunkIds.has(chunkId));
|
|
7943
|
+
if (removableChunkIds.length > 0) {
|
|
7944
|
+
this.rebuildVectorStoreExcludingChunkIds(store, database, removableChunkIds);
|
|
7945
|
+
for (const chunkId of removableChunkIds) {
|
|
7946
|
+
invertedIndex.removeChunk(chunkId);
|
|
7947
|
+
}
|
|
7948
|
+
}
|
|
7949
|
+
for (const branchKey of this.getProjectScopedBranchCatalogCleanupKeys(Array.from(projectLocalChunkIds), Array.from(projectLocalSymbolIds))) {
|
|
7950
|
+
database.deleteBranchSymbolsForBranch(branchKey, symbolIds);
|
|
7951
|
+
}
|
|
7952
|
+
const sharedSymbolIds = new Set(database.getReferencedSymbolIds(symbolIds));
|
|
7953
|
+
const removableSymbolIds = symbolIds.filter((symbolId) => !sharedSymbolIds.has(symbolId));
|
|
7954
|
+
database.clearCallEdgeTargetsForSymbols(removableSymbolIds);
|
|
7955
|
+
for (const filePath of filePaths) {
|
|
7956
|
+
const fileChunkIds = database.getChunksByFile(filePath).map((chunk) => chunk.chunkId);
|
|
7957
|
+
const fileSymbols = database.getSymbolsByFile(filePath);
|
|
7958
|
+
if (fileChunkIds.every((chunkId) => !sharedChunkIds.has(chunkId))) {
|
|
7959
|
+
database.deleteChunksByFile(filePath);
|
|
7960
|
+
}
|
|
7961
|
+
if (fileSymbols.every((symbol) => !sharedSymbolIds.has(symbol.id))) {
|
|
7962
|
+
database.deleteCallEdgesByFile(filePath);
|
|
7963
|
+
database.deleteSymbolsByFile(filePath);
|
|
7964
|
+
}
|
|
7965
|
+
}
|
|
7966
|
+
database.gcOrphanCallEdges();
|
|
7967
|
+
database.gcOrphanSymbols();
|
|
7968
|
+
database.gcOrphanEmbeddings();
|
|
7969
|
+
database.gcOrphanChunks();
|
|
7970
|
+
store.save();
|
|
7971
|
+
invertedIndex.save();
|
|
7972
|
+
return {
|
|
7973
|
+
removedChunkIds: removedChunkIdList,
|
|
7974
|
+
hasForeignData: allMetadata.some(({ metadata }) => !this.isFileInCurrentScope(metadata.filePath, roots))
|
|
7975
|
+
};
|
|
7976
|
+
}
|
|
7977
|
+
checkForInterruptedIndexing() {
|
|
7978
|
+
return existsSync6(this.indexingLockPath);
|
|
7979
|
+
}
|
|
7980
|
+
acquireIndexingLock() {
|
|
7981
|
+
const lockData = {
|
|
7982
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7983
|
+
pid: process.pid
|
|
7984
|
+
};
|
|
7985
|
+
writeFileSync2(this.indexingLockPath, JSON.stringify(lockData));
|
|
7986
|
+
}
|
|
7987
|
+
releaseIndexingLock() {
|
|
7988
|
+
if (existsSync6(this.indexingLockPath)) {
|
|
7989
|
+
unlinkSync(this.indexingLockPath);
|
|
7990
|
+
}
|
|
7991
|
+
}
|
|
7992
|
+
async recoverFromInterruptedIndexing() {
|
|
7993
|
+
this.logger.warn("Detected interrupted indexing session, recovering...");
|
|
7994
|
+
if (existsSync6(this.fileHashCachePath)) {
|
|
7995
|
+
unlinkSync(this.fileHashCachePath);
|
|
7996
|
+
}
|
|
7997
|
+
await this.healthCheck();
|
|
7998
|
+
this.releaseIndexingLock();
|
|
7999
|
+
this.logger.info("Recovery complete, next index will re-process all files");
|
|
8000
|
+
}
|
|
8001
|
+
loadFailedBatches(maxChunkTokens) {
|
|
8002
|
+
try {
|
|
8003
|
+
return this.loadSerializedFailedBatches().map((batch) => normalizeFailedBatch(batch, maxChunkTokens)).filter((batch) => batch !== null);
|
|
8004
|
+
} catch {
|
|
8005
|
+
return [];
|
|
8006
|
+
}
|
|
8007
|
+
}
|
|
8008
|
+
loadSerializedFailedBatches() {
|
|
8009
|
+
if (!existsSync6(this.failedBatchesPath)) {
|
|
8010
|
+
return [];
|
|
8011
|
+
}
|
|
8012
|
+
const data = readFileSync5(this.failedBatchesPath, "utf-8");
|
|
8013
|
+
const parsed = JSON.parse(data);
|
|
8014
|
+
return parsed.map((batch) => {
|
|
8015
|
+
const chunks = Array.isArray(batch.chunks) ? batch.chunks : [];
|
|
8016
|
+
if (chunks.length === 0) {
|
|
8017
|
+
return null;
|
|
8018
|
+
}
|
|
8019
|
+
return {
|
|
8020
|
+
chunks,
|
|
8021
|
+
error: typeof batch.error === "string" ? batch.error : "Unknown embedding error",
|
|
8022
|
+
attemptCount: typeof batch.attemptCount === "number" ? batch.attemptCount : 1,
|
|
8023
|
+
lastAttempt: typeof batch.lastAttempt === "string" ? batch.lastAttempt : (/* @__PURE__ */ new Date()).toISOString()
|
|
8024
|
+
};
|
|
8025
|
+
}).filter((batch) => batch !== null);
|
|
8026
|
+
}
|
|
8027
|
+
saveFailedBatches(batches) {
|
|
8028
|
+
if (batches.length === 0) {
|
|
8029
|
+
if (existsSync6(this.failedBatchesPath)) {
|
|
8030
|
+
try {
|
|
8031
|
+
unlinkSync(this.failedBatchesPath);
|
|
8032
|
+
} catch {
|
|
8033
|
+
}
|
|
8034
|
+
}
|
|
8035
|
+
return;
|
|
8036
|
+
}
|
|
8037
|
+
writeFileSync2(this.failedBatchesPath, JSON.stringify(batches, null, 2));
|
|
8038
|
+
}
|
|
8039
|
+
collectRetryableFailedChunks(currentFileHashes, unchangedFilePaths, maxChunkTokens) {
|
|
8040
|
+
const retryableById = /* @__PURE__ */ new Map();
|
|
8041
|
+
for (const batch of this.loadFailedBatches(maxChunkTokens)) {
|
|
8042
|
+
for (const chunk of batch.chunks) {
|
|
8043
|
+
const filePath = chunk.metadata.filePath;
|
|
8044
|
+
if (!currentFileHashes.has(filePath)) {
|
|
8045
|
+
continue;
|
|
8046
|
+
}
|
|
8047
|
+
if (!unchangedFilePaths.has(filePath)) {
|
|
8048
|
+
continue;
|
|
8049
|
+
}
|
|
8050
|
+
const existing = retryableById.get(chunk.id);
|
|
8051
|
+
if (!existing || batch.attemptCount > existing.attemptCount) {
|
|
8052
|
+
retryableById.set(chunk.id, {
|
|
8053
|
+
chunk,
|
|
8054
|
+
attemptCount: batch.attemptCount
|
|
8055
|
+
});
|
|
8056
|
+
}
|
|
8057
|
+
}
|
|
8058
|
+
}
|
|
8059
|
+
return Array.from(retryableById.values());
|
|
8060
|
+
}
|
|
8061
|
+
getProviderRateLimits(provider) {
|
|
8062
|
+
switch (provider) {
|
|
8063
|
+
case "github-copilot":
|
|
8064
|
+
return { concurrency: 1, intervalMs: 4e3, minRetryMs: 5e3, maxRetryMs: 6e4 };
|
|
8065
|
+
case "openai":
|
|
8066
|
+
return { concurrency: 3, intervalMs: 500, minRetryMs: 1e3, maxRetryMs: 3e4 };
|
|
8067
|
+
case "google":
|
|
8068
|
+
return { concurrency: 5, intervalMs: 200, minRetryMs: 1e3, maxRetryMs: 3e4 };
|
|
8069
|
+
case "ollama":
|
|
8070
|
+
return { concurrency: 5, intervalMs: 0, minRetryMs: 500, maxRetryMs: 5e3 };
|
|
8071
|
+
case "custom": {
|
|
8072
|
+
const customConfig = this.config.customProvider;
|
|
8073
|
+
return {
|
|
8074
|
+
concurrency: customConfig?.concurrency ?? 3,
|
|
8075
|
+
intervalMs: customConfig?.requestIntervalMs ?? 1e3,
|
|
8076
|
+
minRetryMs: 1e3,
|
|
8077
|
+
maxRetryMs: 3e4
|
|
8078
|
+
};
|
|
8079
|
+
}
|
|
8080
|
+
default:
|
|
8081
|
+
return { concurrency: 3, intervalMs: 1e3, minRetryMs: 1e3, maxRetryMs: 3e4 };
|
|
8082
|
+
}
|
|
8083
|
+
}
|
|
8084
|
+
async rerankCandidatesWithApi(query, candidates, options) {
|
|
8085
|
+
const reranker = this.config.reranker;
|
|
8086
|
+
if (!reranker || !reranker.enabled || candidates.length <= 1) {
|
|
8087
|
+
return candidates;
|
|
8088
|
+
}
|
|
8089
|
+
const queryTokens = Array.from(tokenizeTextForRanking(query));
|
|
8090
|
+
const preferSourcePaths = classifyQueryIntentRaw(query) === "source";
|
|
8091
|
+
const docIntent = classifyDocIntent(queryTokens) === "docs";
|
|
7153
8092
|
if (options?.definitionIntent === true) {
|
|
7154
8093
|
return candidates;
|
|
7155
8094
|
}
|
|
@@ -7322,31 +8261,54 @@ var Indexer = class {
|
|
|
7322
8261
|
}
|
|
7323
8262
|
await fsPromises2.mkdir(this.indexPath, { recursive: true });
|
|
7324
8263
|
const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
|
|
7325
|
-
const storePath =
|
|
8264
|
+
const storePath = path8.join(this.indexPath, "vectors");
|
|
7326
8265
|
this.store = new VectorStore(storePath, dimensions);
|
|
7327
|
-
const indexFilePath =
|
|
7328
|
-
if (
|
|
8266
|
+
const indexFilePath = path8.join(this.indexPath, "vectors.usearch");
|
|
8267
|
+
if (existsSync6(indexFilePath)) {
|
|
7329
8268
|
this.store.load();
|
|
7330
8269
|
}
|
|
7331
|
-
const invertedIndexPath =
|
|
8270
|
+
const invertedIndexPath = path8.join(this.indexPath, "inverted-index.json");
|
|
7332
8271
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
7333
8272
|
try {
|
|
7334
8273
|
this.invertedIndex.load();
|
|
7335
8274
|
} catch {
|
|
7336
|
-
if (
|
|
8275
|
+
if (existsSync6(invertedIndexPath)) {
|
|
7337
8276
|
await fsPromises2.unlink(invertedIndexPath);
|
|
7338
8277
|
}
|
|
7339
8278
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
7340
8279
|
}
|
|
7341
|
-
const dbPath =
|
|
7342
|
-
|
|
7343
|
-
|
|
8280
|
+
const dbPath = path8.join(this.indexPath, "codebase.db");
|
|
8281
|
+
let dbIsNew = !existsSync6(dbPath);
|
|
8282
|
+
try {
|
|
8283
|
+
this.database = new Database(dbPath);
|
|
8284
|
+
} catch (error) {
|
|
8285
|
+
if (!await this.tryResetCorruptedIndex("initializing index database", error)) {
|
|
8286
|
+
throw error;
|
|
8287
|
+
}
|
|
8288
|
+
this.store = new VectorStore(storePath, dimensions);
|
|
8289
|
+
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
8290
|
+
this.database = new Database(dbPath);
|
|
8291
|
+
dbIsNew = true;
|
|
8292
|
+
}
|
|
8293
|
+
if (isGitRepo(this.projectRoot)) {
|
|
8294
|
+
this.currentBranch = getBranchOrDefault(this.projectRoot);
|
|
8295
|
+
this.baseBranch = getBaseBranch(this.projectRoot);
|
|
8296
|
+
this.logger.branch("info", "Detected git repository", {
|
|
8297
|
+
currentBranch: this.currentBranch,
|
|
8298
|
+
baseBranch: this.baseBranch
|
|
8299
|
+
});
|
|
8300
|
+
} else {
|
|
8301
|
+
this.currentBranch = "default";
|
|
8302
|
+
this.baseBranch = "default";
|
|
8303
|
+
this.logger.branch("debug", "Not a git repository, using default branch");
|
|
8304
|
+
}
|
|
7344
8305
|
if (this.checkForInterruptedIndexing()) {
|
|
7345
8306
|
await this.recoverFromInterruptedIndexing();
|
|
7346
8307
|
}
|
|
7347
8308
|
if (dbIsNew && this.store.count() > 0) {
|
|
7348
8309
|
this.migrateFromLegacyIndex();
|
|
7349
8310
|
}
|
|
8311
|
+
this.loadFileHashCache();
|
|
7350
8312
|
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
7351
8313
|
if (!this.indexCompatibility.compatible) {
|
|
7352
8314
|
this.logger.warn("Index compatibility issue detected", {
|
|
@@ -7355,18 +8317,6 @@ var Indexer = class {
|
|
|
7355
8317
|
configuredProviderInfo: this.configuredProviderInfo
|
|
7356
8318
|
});
|
|
7357
8319
|
}
|
|
7358
|
-
if (isGitRepo(this.projectRoot)) {
|
|
7359
|
-
this.currentBranch = getBranchOrDefault(this.projectRoot);
|
|
7360
|
-
this.baseBranch = getBaseBranch(this.projectRoot);
|
|
7361
|
-
this.logger.branch("info", "Detected git repository", {
|
|
7362
|
-
currentBranch: this.currentBranch,
|
|
7363
|
-
baseBranch: this.baseBranch
|
|
7364
|
-
});
|
|
7365
|
-
} else {
|
|
7366
|
-
this.currentBranch = "default";
|
|
7367
|
-
this.baseBranch = "default";
|
|
7368
|
-
this.logger.branch("debug", "Not a git repository, using default branch");
|
|
7369
|
-
}
|
|
7370
8320
|
if (this.config.indexing.autoGc) {
|
|
7371
8321
|
await this.maybeRunAutoGc();
|
|
7372
8322
|
}
|
|
@@ -7386,20 +8336,169 @@ var Indexer = class {
|
|
|
7386
8336
|
}
|
|
7387
8337
|
}
|
|
7388
8338
|
if (shouldRunGc) {
|
|
7389
|
-
await this.healthCheck();
|
|
8339
|
+
const result = await this.healthCheck();
|
|
8340
|
+
if (result.warning) {
|
|
8341
|
+
this.database.setMetadata(STARTUP_WARNING_METADATA_KEY, result.warning);
|
|
8342
|
+
} else {
|
|
8343
|
+
this.database.deleteMetadata(STARTUP_WARNING_METADATA_KEY);
|
|
8344
|
+
}
|
|
7390
8345
|
this.database.setMetadata("lastGcTimestamp", now.toString());
|
|
7391
8346
|
}
|
|
7392
8347
|
}
|
|
7393
8348
|
async maybeRunOrphanGc() {
|
|
7394
|
-
if (!this.database) return;
|
|
8349
|
+
if (!this.database) return null;
|
|
7395
8350
|
const stats = this.database.getStats();
|
|
7396
|
-
if (!stats) return;
|
|
8351
|
+
if (!stats) return null;
|
|
7397
8352
|
const orphanCount = stats.embeddingCount - stats.chunkCount;
|
|
7398
8353
|
if (orphanCount > this.config.indexing.gcOrphanThreshold) {
|
|
7399
|
-
|
|
7400
|
-
|
|
8354
|
+
try {
|
|
8355
|
+
this.database.gcOrphanEmbeddings();
|
|
8356
|
+
this.database.gcOrphanChunks();
|
|
8357
|
+
} catch (error) {
|
|
8358
|
+
if (await this.tryResetCorruptedIndex("running automatic orphan garbage collection", error)) {
|
|
8359
|
+
return {
|
|
8360
|
+
resetCorruptedIndex: true,
|
|
8361
|
+
warning: this.getCorruptedIndexWarning(path8.join(this.indexPath, "codebase.db"))
|
|
8362
|
+
};
|
|
8363
|
+
}
|
|
8364
|
+
throw error;
|
|
8365
|
+
}
|
|
7401
8366
|
this.database.setMetadata("lastGcTimestamp", Date.now().toString());
|
|
7402
8367
|
}
|
|
8368
|
+
return null;
|
|
8369
|
+
}
|
|
8370
|
+
rebuildVectorStoreExcludingChunkIds(store, database, excludedChunkIds) {
|
|
8371
|
+
const excludedSet = new Set(excludedChunkIds);
|
|
8372
|
+
if (excludedSet.size === 0) {
|
|
8373
|
+
return;
|
|
8374
|
+
}
|
|
8375
|
+
const retainedEntries = store.getAllMetadata().filter(({ key }) => !excludedSet.has(key));
|
|
8376
|
+
const storeBasePath = path8.join(this.indexPath, "vectors");
|
|
8377
|
+
const storeIndexPath = `${storeBasePath}.usearch`;
|
|
8378
|
+
const storeMetadataPath = `${storeBasePath}.meta.json`;
|
|
8379
|
+
const backupIndexPath = `${storeIndexPath}.bak`;
|
|
8380
|
+
const backupMetadataPath = `${storeMetadataPath}.bak`;
|
|
8381
|
+
let backedUpIndex = false;
|
|
8382
|
+
let backedUpMetadata = false;
|
|
8383
|
+
let rebuiltCount = 0;
|
|
8384
|
+
let skippedCount = 0;
|
|
8385
|
+
if (existsSync6(backupIndexPath)) {
|
|
8386
|
+
unlinkSync(backupIndexPath);
|
|
8387
|
+
}
|
|
8388
|
+
if (existsSync6(backupMetadataPath)) {
|
|
8389
|
+
unlinkSync(backupMetadataPath);
|
|
8390
|
+
}
|
|
8391
|
+
try {
|
|
8392
|
+
if (existsSync6(storeIndexPath)) {
|
|
8393
|
+
renameSync(storeIndexPath, backupIndexPath);
|
|
8394
|
+
backedUpIndex = true;
|
|
8395
|
+
}
|
|
8396
|
+
if (existsSync6(storeMetadataPath)) {
|
|
8397
|
+
renameSync(storeMetadataPath, backupMetadataPath);
|
|
8398
|
+
backedUpMetadata = true;
|
|
8399
|
+
}
|
|
8400
|
+
store.clear();
|
|
8401
|
+
for (const { key, metadata } of retainedEntries) {
|
|
8402
|
+
const chunk = database.getChunk(key);
|
|
8403
|
+
if (!chunk) {
|
|
8404
|
+
skippedCount += 1;
|
|
8405
|
+
continue;
|
|
8406
|
+
}
|
|
8407
|
+
const embeddingBuffer = database.getEmbedding(chunk.contentHash);
|
|
8408
|
+
if (!embeddingBuffer) {
|
|
8409
|
+
skippedCount += 1;
|
|
8410
|
+
continue;
|
|
8411
|
+
}
|
|
8412
|
+
const vector = bufferToFloat32Array(embeddingBuffer);
|
|
8413
|
+
store.add(key, Array.from(vector), metadata);
|
|
8414
|
+
rebuiltCount += 1;
|
|
8415
|
+
}
|
|
8416
|
+
store.save();
|
|
8417
|
+
if (backedUpIndex && existsSync6(backupIndexPath)) {
|
|
8418
|
+
unlinkSync(backupIndexPath);
|
|
8419
|
+
}
|
|
8420
|
+
if (backedUpMetadata && existsSync6(backupMetadataPath)) {
|
|
8421
|
+
unlinkSync(backupMetadataPath);
|
|
8422
|
+
}
|
|
8423
|
+
this.logger.gc("info", "Rebuilt vector store to avoid native remove", {
|
|
8424
|
+
excludedChunks: excludedSet.size,
|
|
8425
|
+
rebuiltChunks: rebuiltCount,
|
|
8426
|
+
skippedChunks: skippedCount
|
|
8427
|
+
});
|
|
8428
|
+
} catch (error) {
|
|
8429
|
+
try {
|
|
8430
|
+
store.clear();
|
|
8431
|
+
} catch {
|
|
8432
|
+
}
|
|
8433
|
+
if (existsSync6(storeIndexPath)) {
|
|
8434
|
+
unlinkSync(storeIndexPath);
|
|
8435
|
+
}
|
|
8436
|
+
if (existsSync6(storeMetadataPath)) {
|
|
8437
|
+
unlinkSync(storeMetadataPath);
|
|
8438
|
+
}
|
|
8439
|
+
if (backedUpIndex && existsSync6(backupIndexPath)) {
|
|
8440
|
+
renameSync(backupIndexPath, storeIndexPath);
|
|
8441
|
+
}
|
|
8442
|
+
if (backedUpMetadata && existsSync6(backupMetadataPath)) {
|
|
8443
|
+
renameSync(backupMetadataPath, storeMetadataPath);
|
|
8444
|
+
}
|
|
8445
|
+
if (backedUpIndex || backedUpMetadata) {
|
|
8446
|
+
store.load();
|
|
8447
|
+
}
|
|
8448
|
+
throw error;
|
|
8449
|
+
}
|
|
8450
|
+
}
|
|
8451
|
+
getCorruptedIndexWarning(dbPath) {
|
|
8452
|
+
if (this.config.scope === "global") {
|
|
8453
|
+
return `Detected a corrupted shared global SQLite index at ${dbPath}. Automatic repair is disabled for global scope because it may delete other projects' index data. Remove or repair the shared index manually, then rerun index_codebase with force=true.`;
|
|
8454
|
+
}
|
|
8455
|
+
return `Detected a corrupted local SQLite index at ${dbPath} and reset the local index. Run index_codebase to rebuild search data.`;
|
|
8456
|
+
}
|
|
8457
|
+
async tryResetCorruptedIndex(stage, error) {
|
|
8458
|
+
if (!isSqliteCorruptionError(error)) {
|
|
8459
|
+
return false;
|
|
8460
|
+
}
|
|
8461
|
+
const dbPath = path8.join(this.indexPath, "codebase.db");
|
|
8462
|
+
const warning = this.getCorruptedIndexWarning(dbPath);
|
|
8463
|
+
const errorMessage = getErrorMessage(error);
|
|
8464
|
+
if (this.config.scope === "global") {
|
|
8465
|
+
this.logger.error("Detected corrupted shared global index database", {
|
|
8466
|
+
stage,
|
|
8467
|
+
dbPath,
|
|
8468
|
+
error: errorMessage
|
|
8469
|
+
});
|
|
8470
|
+
throw new Error(`${warning} Original SQLite error: ${errorMessage}`);
|
|
8471
|
+
}
|
|
8472
|
+
this.logger.warn("Detected corrupted local index database, resetting local index", {
|
|
8473
|
+
stage,
|
|
8474
|
+
dbPath,
|
|
8475
|
+
error: errorMessage
|
|
8476
|
+
});
|
|
8477
|
+
this.store = null;
|
|
8478
|
+
this.invertedIndex = null;
|
|
8479
|
+
this.database?.close();
|
|
8480
|
+
this.database = null;
|
|
8481
|
+
this.indexCompatibility = null;
|
|
8482
|
+
this.fileHashCache.clear();
|
|
8483
|
+
const resetPaths = [
|
|
8484
|
+
path8.join(this.indexPath, "codebase.db"),
|
|
8485
|
+
path8.join(this.indexPath, "codebase.db-shm"),
|
|
8486
|
+
path8.join(this.indexPath, "codebase.db-wal"),
|
|
8487
|
+
path8.join(this.indexPath, "vectors.usearch"),
|
|
8488
|
+
path8.join(this.indexPath, "inverted-index.json"),
|
|
8489
|
+
path8.join(this.indexPath, "file-hashes.json"),
|
|
8490
|
+
path8.join(this.indexPath, "failed-batches.json"),
|
|
8491
|
+
path8.join(this.indexPath, "indexing.lock"),
|
|
8492
|
+
path8.join(this.indexPath, "vectors")
|
|
8493
|
+
];
|
|
8494
|
+
await Promise.all(resetPaths.map(async (targetPath) => {
|
|
8495
|
+
try {
|
|
8496
|
+
await fsPromises2.rm(targetPath, { recursive: true, force: true });
|
|
8497
|
+
} catch {
|
|
8498
|
+
}
|
|
8499
|
+
}));
|
|
8500
|
+
await fsPromises2.mkdir(this.indexPath, { recursive: true });
|
|
8501
|
+
return true;
|
|
7403
8502
|
}
|
|
7404
8503
|
migrateFromLegacyIndex() {
|
|
7405
8504
|
if (!this.store || !this.database) return;
|
|
@@ -7423,7 +8522,7 @@ var Indexer = class {
|
|
|
7423
8522
|
if (chunkDataBatch.length > 0) {
|
|
7424
8523
|
this.database.upsertChunksBatch(chunkDataBatch);
|
|
7425
8524
|
}
|
|
7426
|
-
this.database.addChunksToBranchBatch(this.
|
|
8525
|
+
this.database.addChunksToBranchBatch(this.getBranchCatalogKey(), chunkIds);
|
|
7427
8526
|
}
|
|
7428
8527
|
loadIndexMetadata() {
|
|
7429
8528
|
if (!this.database) return null;
|
|
@@ -7434,6 +8533,7 @@ var Indexer = class {
|
|
|
7434
8533
|
embeddingProvider: this.database.getMetadata("index.embeddingProvider") ?? "",
|
|
7435
8534
|
embeddingModel: this.database.getMetadata("index.embeddingModel") ?? "",
|
|
7436
8535
|
embeddingDimensions: parseInt(this.database.getMetadata("index.embeddingDimensions") ?? "0", 10),
|
|
8536
|
+
embeddingStrategyVersion: this.loadStoredEmbeddingStrategyVersion() ?? EMBEDDING_STRATEGY_VERSION,
|
|
7437
8537
|
createdAt: this.database.getMetadata("index.createdAt") ?? "",
|
|
7438
8538
|
updatedAt: this.database.getMetadata("index.updatedAt") ?? ""
|
|
7439
8539
|
};
|
|
@@ -7442,10 +8542,22 @@ var Indexer = class {
|
|
|
7442
8542
|
if (!this.database) return;
|
|
7443
8543
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7444
8544
|
const existingCreatedAt = this.database.getMetadata("index.createdAt");
|
|
8545
|
+
const completeProjectEmbeddingStrategyReset = !this.hasProjectForceReembedPending();
|
|
7445
8546
|
this.database.setMetadata("index.version", INDEX_METADATA_VERSION);
|
|
7446
8547
|
this.database.setMetadata("index.embeddingProvider", provider.provider);
|
|
7447
8548
|
this.database.setMetadata("index.embeddingModel", provider.modelInfo.model);
|
|
7448
8549
|
this.database.setMetadata("index.embeddingDimensions", provider.modelInfo.dimensions.toString());
|
|
8550
|
+
if (this.config.scope === "global") {
|
|
8551
|
+
if (completeProjectEmbeddingStrategyReset) {
|
|
8552
|
+
this.database.setMetadata(this.getProjectEmbeddingStrategyMetadataKey(), EMBEDDING_STRATEGY_VERSION);
|
|
8553
|
+
}
|
|
8554
|
+
this.database.setMetadata(this.getLegacyMigrationMetadataKey(), "done");
|
|
8555
|
+
if (completeProjectEmbeddingStrategyReset) {
|
|
8556
|
+
this.database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
8557
|
+
}
|
|
8558
|
+
} else {
|
|
8559
|
+
this.database.setMetadata("index.embeddingStrategyVersion", EMBEDDING_STRATEGY_VERSION);
|
|
8560
|
+
}
|
|
7449
8561
|
this.database.setMetadata("index.updatedAt", now);
|
|
7450
8562
|
if (!existingCreatedAt) {
|
|
7451
8563
|
this.database.setMetadata("index.createdAt", now);
|
|
@@ -7475,6 +8587,14 @@ var Indexer = class {
|
|
|
7475
8587
|
storedMetadata
|
|
7476
8588
|
};
|
|
7477
8589
|
}
|
|
8590
|
+
if (storedMetadata.embeddingStrategyVersion !== EMBEDDING_STRATEGY_VERSION) {
|
|
8591
|
+
return {
|
|
8592
|
+
compatible: false,
|
|
8593
|
+
code: "EMBEDDING_STRATEGY_MISMATCH" /* EMBEDDING_STRATEGY_MISMATCH */,
|
|
8594
|
+
reason: `Embedding strategy mismatch: index was built with embedding strategy v${storedMetadata.embeddingStrategyVersion}, but the current code requires v${EMBEDDING_STRATEGY_VERSION}. Run index_codebase with force=true to rebuild cached embeddings.`,
|
|
8595
|
+
storedMetadata
|
|
8596
|
+
};
|
|
8597
|
+
}
|
|
7478
8598
|
if (storedMetadata.embeddingProvider !== currentProvider) {
|
|
7479
8599
|
this.logger.warn("Provider changed", {
|
|
7480
8600
|
storedProvider: storedMetadata.embeddingProvider,
|
|
@@ -7522,6 +8642,10 @@ var Indexer = class {
|
|
|
7522
8642
|
}
|
|
7523
8643
|
async index(onProgress) {
|
|
7524
8644
|
const { store, provider, invertedIndex, database, configuredProviderInfo } = await this.ensureInitialized();
|
|
8645
|
+
const scopedRoots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
8646
|
+
const branchCatalogKey = this.getBranchCatalogKey();
|
|
8647
|
+
const forceScopedReembed = scopedRoots !== null && database.getMetadata(this.getProjectForceReembedMetadataKey()) === "true";
|
|
8648
|
+
const failedForcedChunkIds = /* @__PURE__ */ new Set();
|
|
7525
8649
|
if (!this.indexCompatibility?.compatible) {
|
|
7526
8650
|
throw new Error(
|
|
7527
8651
|
`${this.indexCompatibility?.reason} Run index_codebase with force=true to rebuild the index.`
|
|
@@ -7543,6 +8667,7 @@ var Indexer = class {
|
|
|
7543
8667
|
skippedFiles: [],
|
|
7544
8668
|
parseFailures: []
|
|
7545
8669
|
};
|
|
8670
|
+
const failedBatchesForCurrentRun = [];
|
|
7546
8671
|
onProgress?.({
|
|
7547
8672
|
phase: "scanning",
|
|
7548
8673
|
filesProcessed: 0,
|
|
@@ -7602,6 +8727,12 @@ var Indexer = class {
|
|
|
7602
8727
|
const existingChunks = /* @__PURE__ */ new Map();
|
|
7603
8728
|
const existingChunksByFile = /* @__PURE__ */ new Map();
|
|
7604
8729
|
for (const { key, metadata } of store.getAllMetadata()) {
|
|
8730
|
+
if (scopedRoots && !this.isFileInCurrentScope(metadata.filePath, scopedRoots)) {
|
|
8731
|
+
continue;
|
|
8732
|
+
}
|
|
8733
|
+
if (forceScopedReembed && scopedRoots && this.isFileInCurrentScope(metadata.filePath, scopedRoots)) {
|
|
8734
|
+
continue;
|
|
8735
|
+
}
|
|
7605
8736
|
existingChunks.set(key, metadata.hash);
|
|
7606
8737
|
const fileChunks = existingChunksByFile.get(metadata.filePath) || /* @__PURE__ */ new Set();
|
|
7607
8738
|
fileChunks.add(key);
|
|
@@ -7623,7 +8754,7 @@ var Indexer = class {
|
|
|
7623
8754
|
for (const parsed of parsedFiles) {
|
|
7624
8755
|
currentFilePaths.add(parsed.path);
|
|
7625
8756
|
if (parsed.chunks.length === 0) {
|
|
7626
|
-
const relativePath =
|
|
8757
|
+
const relativePath = path8.relative(this.projectRoot, parsed.path);
|
|
7627
8758
|
stats.parseFailures.push(relativePath);
|
|
7628
8759
|
}
|
|
7629
8760
|
let fileChunkCount = 0;
|
|
@@ -7659,7 +8790,10 @@ var Indexer = class {
|
|
|
7659
8790
|
fileChunkCount++;
|
|
7660
8791
|
continue;
|
|
7661
8792
|
}
|
|
7662
|
-
const
|
|
8793
|
+
const texts = createEmbeddingTexts(chunk, parsed.path, getSafeEmbeddingChunkTokenLimit(configuredProviderInfo)).map((text) => ({
|
|
8794
|
+
text,
|
|
8795
|
+
tokenCount: estimateTokens(text)
|
|
8796
|
+
}));
|
|
7663
8797
|
const metadata = {
|
|
7664
8798
|
filePath: parsed.path,
|
|
7665
8799
|
startLine: chunk.startLine,
|
|
@@ -7669,10 +8803,38 @@ var Indexer = class {
|
|
|
7669
8803
|
language: chunk.language,
|
|
7670
8804
|
hash: contentHash
|
|
7671
8805
|
};
|
|
7672
|
-
pendingChunks.push({
|
|
8806
|
+
pendingChunks.push({
|
|
8807
|
+
id,
|
|
8808
|
+
texts,
|
|
8809
|
+
storageText: createPendingChunkStorageText(texts),
|
|
8810
|
+
content: chunk.content,
|
|
8811
|
+
contentHash,
|
|
8812
|
+
metadata
|
|
8813
|
+
});
|
|
7673
8814
|
fileChunkCount++;
|
|
7674
8815
|
}
|
|
7675
8816
|
}
|
|
8817
|
+
const retryableFailedChunks = this.collectRetryableFailedChunks(
|
|
8818
|
+
currentFileHashes,
|
|
8819
|
+
unchangedFilePaths,
|
|
8820
|
+
getSafeEmbeddingChunkTokenLimit(configuredProviderInfo)
|
|
8821
|
+
);
|
|
8822
|
+
const retryableFailedAttemptCounts = /* @__PURE__ */ new Map();
|
|
8823
|
+
const retryableChunksWithExistingData = /* @__PURE__ */ new Set();
|
|
8824
|
+
if (retryableFailedChunks.length > 0) {
|
|
8825
|
+
const pendingChunkIds = new Set(pendingChunks.map((chunk) => chunk.id));
|
|
8826
|
+
for (const { chunk, attemptCount } of retryableFailedChunks) {
|
|
8827
|
+
retryableFailedAttemptCounts.set(chunk.id, attemptCount);
|
|
8828
|
+
if (existingChunks.has(chunk.id)) {
|
|
8829
|
+
retryableChunksWithExistingData.add(chunk.id);
|
|
8830
|
+
}
|
|
8831
|
+
if (!pendingChunkIds.has(chunk.id)) {
|
|
8832
|
+
pendingChunks.push(chunk);
|
|
8833
|
+
pendingChunkIds.add(chunk.id);
|
|
8834
|
+
currentChunkIds.add(chunk.id);
|
|
8835
|
+
}
|
|
8836
|
+
}
|
|
8837
|
+
}
|
|
7676
8838
|
if (chunkDataBatch.length > 0) {
|
|
7677
8839
|
database.upsertChunksBatch(chunkDataBatch);
|
|
7678
8840
|
}
|
|
@@ -7701,17 +8863,20 @@ var Indexer = class {
|
|
|
7701
8863
|
fileSymbols.push(symbol);
|
|
7702
8864
|
allSymbolIds.add(symbolId);
|
|
7703
8865
|
}
|
|
8866
|
+
const fileLanguage = parsed.chunks[0]?.language;
|
|
8867
|
+
const isCaseInsensitiveLanguage = !!fileLanguage && CASE_INSENSITIVE_LANGUAGES.has(fileLanguage);
|
|
8868
|
+
const normalizeSymbolKey = (name) => isCaseInsensitiveLanguage ? name.toLowerCase() : name;
|
|
7704
8869
|
const symbolsByName = /* @__PURE__ */ new Map();
|
|
7705
8870
|
for (const symbol of fileSymbols) {
|
|
7706
|
-
const
|
|
8871
|
+
const key = normalizeSymbolKey(symbol.name);
|
|
8872
|
+
const existing = symbolsByName.get(key) ?? [];
|
|
7707
8873
|
existing.push(symbol);
|
|
7708
|
-
symbolsByName.set(
|
|
8874
|
+
symbolsByName.set(key, existing);
|
|
7709
8875
|
}
|
|
7710
8876
|
if (fileSymbols.length > 0) {
|
|
7711
8877
|
database.upsertSymbolsBatch(fileSymbols);
|
|
7712
8878
|
symbolsByFile.set(parsed.path, fileSymbols);
|
|
7713
8879
|
}
|
|
7714
|
-
const fileLanguage = parsed.chunks[0]?.language;
|
|
7715
8880
|
if (!fileLanguage || !CALL_GRAPH_LANGUAGES.has(fileLanguage)) continue;
|
|
7716
8881
|
const callSites = extractCalls(changedFile.content, fileLanguage);
|
|
7717
8882
|
if (callSites.length === 0) continue;
|
|
@@ -7736,7 +8901,7 @@ var Indexer = class {
|
|
|
7736
8901
|
if (edges.length > 0) {
|
|
7737
8902
|
database.upsertCallEdgesBatch(edges);
|
|
7738
8903
|
for (const edge of edges) {
|
|
7739
|
-
const candidates = symbolsByName.get(edge.targetName);
|
|
8904
|
+
const candidates = symbolsByName.get(normalizeSymbolKey(edge.targetName));
|
|
7740
8905
|
if (candidates && candidates.length === 1) {
|
|
7741
8906
|
database.resolveCallEdge(edge.id, candidates[0].id);
|
|
7742
8907
|
}
|
|
@@ -7749,14 +8914,20 @@ var Indexer = class {
|
|
|
7749
8914
|
allSymbolIds.add(sym.id);
|
|
7750
8915
|
}
|
|
7751
8916
|
}
|
|
7752
|
-
|
|
8917
|
+
const removedChunkIds = [];
|
|
7753
8918
|
for (const [chunkId] of existingChunks) {
|
|
7754
8919
|
if (!currentChunkIds.has(chunkId)) {
|
|
7755
|
-
|
|
8920
|
+
removedChunkIds.push(chunkId);
|
|
8921
|
+
}
|
|
8922
|
+
}
|
|
8923
|
+
if (removedChunkIds.length > 0) {
|
|
8924
|
+
this.rebuildVectorStoreExcludingChunkIds(store, database, removedChunkIds);
|
|
8925
|
+
for (const chunkId of removedChunkIds) {
|
|
7756
8926
|
invertedIndex.removeChunk(chunkId);
|
|
7757
|
-
removedCount++;
|
|
7758
8927
|
}
|
|
8928
|
+
database.deleteChunksByIds(removedChunkIds);
|
|
7759
8929
|
}
|
|
8930
|
+
const removedCount = removedChunkIds.length;
|
|
7760
8931
|
stats.totalChunks = pendingChunks.length;
|
|
7761
8932
|
stats.existingChunks = currentChunkIds.size - pendingChunks.length;
|
|
7762
8933
|
stats.removedChunks = removedCount;
|
|
@@ -7768,12 +8939,20 @@ var Indexer = class {
|
|
|
7768
8939
|
removed: removedCount
|
|
7769
8940
|
});
|
|
7770
8941
|
if (pendingChunks.length === 0 && removedCount === 0) {
|
|
7771
|
-
database.clearBranch(
|
|
7772
|
-
database.addChunksToBranchBatch(
|
|
7773
|
-
database.clearBranchSymbols(
|
|
7774
|
-
database.addSymbolsToBranchBatch(
|
|
7775
|
-
|
|
7776
|
-
|
|
8942
|
+
database.clearBranch(branchCatalogKey);
|
|
8943
|
+
database.addChunksToBranchBatch(branchCatalogKey, Array.from(currentChunkIds));
|
|
8944
|
+
database.clearBranchSymbols(branchCatalogKey);
|
|
8945
|
+
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
8946
|
+
if (scopedRoots) {
|
|
8947
|
+
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
8948
|
+
this.clearScopedFailedBatches(scopedRoots);
|
|
8949
|
+
} else {
|
|
8950
|
+
this.fileHashCache = currentFileHashes;
|
|
8951
|
+
this.saveFileHashCache();
|
|
8952
|
+
this.saveFailedBatches([]);
|
|
8953
|
+
}
|
|
8954
|
+
this.saveIndexMetadata(configuredProviderInfo);
|
|
8955
|
+
this.indexCompatibility = { compatible: true };
|
|
7777
8956
|
stats.durationMs = Date.now() - startTime;
|
|
7778
8957
|
onProgress?.({
|
|
7779
8958
|
phase: "complete",
|
|
@@ -7786,14 +8965,22 @@ var Indexer = class {
|
|
|
7786
8965
|
return stats;
|
|
7787
8966
|
}
|
|
7788
8967
|
if (pendingChunks.length === 0) {
|
|
7789
|
-
database.clearBranch(
|
|
7790
|
-
database.addChunksToBranchBatch(
|
|
7791
|
-
database.clearBranchSymbols(
|
|
7792
|
-
database.addSymbolsToBranchBatch(
|
|
8968
|
+
database.clearBranch(branchCatalogKey);
|
|
8969
|
+
database.addChunksToBranchBatch(branchCatalogKey, Array.from(currentChunkIds));
|
|
8970
|
+
database.clearBranchSymbols(branchCatalogKey);
|
|
8971
|
+
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
7793
8972
|
store.save();
|
|
7794
8973
|
invertedIndex.save();
|
|
7795
|
-
|
|
7796
|
-
|
|
8974
|
+
if (scopedRoots) {
|
|
8975
|
+
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
8976
|
+
this.clearScopedFailedBatches(scopedRoots);
|
|
8977
|
+
} else {
|
|
8978
|
+
this.fileHashCache = currentFileHashes;
|
|
8979
|
+
this.saveFileHashCache();
|
|
8980
|
+
this.saveFailedBatches([]);
|
|
8981
|
+
}
|
|
8982
|
+
this.saveIndexMetadata(configuredProviderInfo);
|
|
8983
|
+
this.indexCompatibility = { compatible: true };
|
|
7797
8984
|
stats.durationMs = Date.now() - startTime;
|
|
7798
8985
|
onProgress?.({
|
|
7799
8986
|
phase: "complete",
|
|
@@ -7814,8 +9001,9 @@ var Indexer = class {
|
|
|
7814
9001
|
});
|
|
7815
9002
|
const allContentHashes = pendingChunks.map((c) => c.contentHash);
|
|
7816
9003
|
const missingHashes = new Set(database.getMissingEmbeddings(allContentHashes));
|
|
7817
|
-
const
|
|
7818
|
-
const
|
|
9004
|
+
const forcedReembedChunkIds = forceScopedReembed ? new Set(pendingChunks.map((chunk) => chunk.id)) : /* @__PURE__ */ new Set();
|
|
9005
|
+
const chunksNeedingEmbedding = pendingChunks.filter((c) => forcedReembedChunkIds.has(c.id) || missingHashes.has(c.contentHash));
|
|
9006
|
+
const chunksWithExistingEmbedding = pendingChunks.filter((c) => !forcedReembedChunkIds.has(c.id) && !missingHashes.has(c.contentHash));
|
|
7819
9007
|
this.logger.cache("info", "Embedding cache lookup", {
|
|
7820
9008
|
needsEmbedding: chunksNeedingEmbedding.length,
|
|
7821
9009
|
fromCache: chunksWithExistingEmbedding.length
|
|
@@ -7837,17 +9025,24 @@ var Indexer = class {
|
|
|
7837
9025
|
interval: providerRateLimits.intervalMs,
|
|
7838
9026
|
intervalCap: providerRateLimits.concurrency
|
|
7839
9027
|
});
|
|
7840
|
-
const
|
|
9028
|
+
const pendingChunksById = new Map(chunksNeedingEmbedding.map((chunk) => [chunk.id, chunk]));
|
|
9029
|
+
const embeddingPartsByChunk = /* @__PURE__ */ new Map();
|
|
9030
|
+
const completedChunkIds = /* @__PURE__ */ new Set();
|
|
9031
|
+
const failedChunkIds = /* @__PURE__ */ new Set();
|
|
9032
|
+
const requestBatches = createPendingEmbeddingRequestBatches(
|
|
9033
|
+
chunksNeedingEmbedding,
|
|
9034
|
+
getDynamicBatchOptions(configuredProviderInfo)
|
|
9035
|
+
);
|
|
7841
9036
|
let rateLimitBackoffMs = 0;
|
|
7842
|
-
for (const
|
|
9037
|
+
for (const requestBatch of requestBatches) {
|
|
7843
9038
|
queue.add(async () => {
|
|
7844
9039
|
if (rateLimitBackoffMs > 0) {
|
|
7845
|
-
await new Promise((
|
|
9040
|
+
await new Promise((resolve9) => setTimeout(resolve9, rateLimitBackoffMs));
|
|
7846
9041
|
}
|
|
7847
9042
|
try {
|
|
7848
9043
|
const result = await pRetry(
|
|
7849
9044
|
async () => {
|
|
7850
|
-
const texts =
|
|
9045
|
+
const texts = requestBatch.map((request) => request.text);
|
|
7851
9046
|
return provider.embedBatch(texts);
|
|
7852
9047
|
},
|
|
7853
9048
|
{
|
|
@@ -7877,29 +9072,82 @@ var Indexer = class {
|
|
|
7877
9072
|
if (rateLimitBackoffMs > 0) {
|
|
7878
9073
|
rateLimitBackoffMs = Math.max(0, rateLimitBackoffMs - 2e3);
|
|
7879
9074
|
}
|
|
7880
|
-
const
|
|
7881
|
-
|
|
7882
|
-
|
|
7883
|
-
|
|
7884
|
-
|
|
7885
|
-
|
|
7886
|
-
|
|
7887
|
-
|
|
7888
|
-
|
|
7889
|
-
|
|
7890
|
-
|
|
7891
|
-
|
|
7892
|
-
|
|
7893
|
-
|
|
7894
|
-
|
|
7895
|
-
|
|
9075
|
+
const touchedChunkIds = /* @__PURE__ */ new Set();
|
|
9076
|
+
requestBatch.forEach((request, idx) => {
|
|
9077
|
+
if (failedChunkIds.has(request.chunk.id) || completedChunkIds.has(request.chunk.id)) {
|
|
9078
|
+
return;
|
|
9079
|
+
}
|
|
9080
|
+
const vector = result.embeddings[idx];
|
|
9081
|
+
if (!vector) {
|
|
9082
|
+
throw new Error(`Embedding API returned too few vectors for chunk ${request.chunk.id}`);
|
|
9083
|
+
}
|
|
9084
|
+
const parts = embeddingPartsByChunk.get(request.chunk.id) ?? [];
|
|
9085
|
+
parts[request.partIndex] = {
|
|
9086
|
+
vector,
|
|
9087
|
+
tokenCount: request.tokenCount
|
|
9088
|
+
};
|
|
9089
|
+
embeddingPartsByChunk.set(request.chunk.id, parts);
|
|
9090
|
+
touchedChunkIds.add(request.chunk.id);
|
|
9091
|
+
});
|
|
9092
|
+
const pooledResults = [];
|
|
9093
|
+
for (const chunkId of touchedChunkIds) {
|
|
9094
|
+
if (failedChunkIds.has(chunkId) || completedChunkIds.has(chunkId)) {
|
|
9095
|
+
continue;
|
|
9096
|
+
}
|
|
9097
|
+
const chunk = pendingChunksById.get(chunkId);
|
|
9098
|
+
if (!chunk) {
|
|
9099
|
+
continue;
|
|
9100
|
+
}
|
|
9101
|
+
const parts = embeddingPartsByChunk.get(chunk.id) ?? [];
|
|
9102
|
+
if (!hasAllEmbeddingParts(parts, chunk.texts.length)) {
|
|
9103
|
+
continue;
|
|
9104
|
+
}
|
|
9105
|
+
const orderedParts = parts;
|
|
9106
|
+
pooledResults.push({
|
|
9107
|
+
chunk,
|
|
9108
|
+
vector: poolEmbeddingVectors(
|
|
9109
|
+
orderedParts.map((part) => part.vector),
|
|
9110
|
+
orderedParts.map((part) => part.tokenCount)
|
|
9111
|
+
)
|
|
9112
|
+
});
|
|
9113
|
+
}
|
|
9114
|
+
if (pooledResults.length > 0) {
|
|
9115
|
+
const items = pooledResults.map(({ chunk, vector }) => ({
|
|
9116
|
+
id: chunk.id,
|
|
9117
|
+
vector,
|
|
9118
|
+
metadata: chunk.metadata
|
|
9119
|
+
}));
|
|
9120
|
+
store.addBatch(items);
|
|
9121
|
+
const embeddingBatchItems = pooledResults.map(({ chunk, vector }) => ({
|
|
9122
|
+
contentHash: chunk.contentHash,
|
|
9123
|
+
embedding: float32ArrayToBuffer(vector),
|
|
9124
|
+
chunkText: chunk.storageText,
|
|
9125
|
+
model: configuredProviderInfo.modelInfo.model
|
|
9126
|
+
}));
|
|
9127
|
+
try {
|
|
9128
|
+
database.upsertEmbeddingsBatch(embeddingBatchItems);
|
|
9129
|
+
} catch (dbError) {
|
|
9130
|
+
this.rebuildVectorStoreExcludingChunkIds(
|
|
9131
|
+
store,
|
|
9132
|
+
database,
|
|
9133
|
+
pooledResults.map(({ chunk }) => chunk.id)
|
|
9134
|
+
);
|
|
9135
|
+
throw dbError;
|
|
9136
|
+
}
|
|
9137
|
+
for (const { chunk } of pooledResults) {
|
|
9138
|
+
invertedIndex.removeChunk(chunk.id);
|
|
9139
|
+
invertedIndex.addChunk(chunk.id, chunk.content);
|
|
9140
|
+
completedChunkIds.add(chunk.id);
|
|
9141
|
+
embeddingPartsByChunk.delete(chunk.id);
|
|
9142
|
+
}
|
|
9143
|
+
stats.indexedChunks += pooledResults.length;
|
|
9144
|
+
this.logger.recordChunksEmbedded(pooledResults.length);
|
|
7896
9145
|
}
|
|
7897
|
-
stats.indexedChunks += batch.length;
|
|
7898
9146
|
stats.tokensUsed += result.totalTokensUsed;
|
|
7899
|
-
this.logger.recordChunksEmbedded(batch.length);
|
|
7900
9147
|
this.logger.recordEmbeddingApiCall(result.totalTokensUsed);
|
|
7901
9148
|
this.logger.embedding("debug", `Embedded batch`, {
|
|
7902
|
-
batchSize:
|
|
9149
|
+
batchSize: pooledResults.length,
|
|
9150
|
+
requestCount: requestBatch.length,
|
|
7903
9151
|
tokens: result.totalTokensUsed
|
|
7904
9152
|
});
|
|
7905
9153
|
onProgress?.({
|
|
@@ -7910,17 +9158,49 @@ var Indexer = class {
|
|
|
7910
9158
|
totalChunks: pendingChunks.length
|
|
7911
9159
|
});
|
|
7912
9160
|
} catch (error) {
|
|
7913
|
-
|
|
7914
|
-
|
|
9161
|
+
const failedChunks = getUniquePendingChunksFromRequests(requestBatch).filter((chunk) => !completedChunkIds.has(chunk.id));
|
|
9162
|
+
const failureMessage = getErrorMessage(error);
|
|
9163
|
+
const failureTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
9164
|
+
for (const chunk of failedChunks) {
|
|
9165
|
+
if (!failedChunkIds.has(chunk.id)) {
|
|
9166
|
+
failedChunkIds.add(chunk.id);
|
|
9167
|
+
stats.failedChunks += 1;
|
|
9168
|
+
}
|
|
9169
|
+
if (forceScopedReembed) {
|
|
9170
|
+
failedForcedChunkIds.add(chunk.id);
|
|
9171
|
+
}
|
|
9172
|
+
embeddingPartsByChunk.delete(chunk.id);
|
|
9173
|
+
const existingFailedBatchIndex = failedBatchesForCurrentRun.findIndex(
|
|
9174
|
+
(failedBatch2) => failedBatch2.chunks[0]?.id === chunk.id
|
|
9175
|
+
);
|
|
9176
|
+
const existingFailedBatch = existingFailedBatchIndex === -1 ? void 0 : failedBatchesForCurrentRun[existingFailedBatchIndex];
|
|
9177
|
+
const failedBatch = {
|
|
9178
|
+
chunks: [chunk],
|
|
9179
|
+
error: failureMessage,
|
|
9180
|
+
attemptCount: (existingFailedBatch?.attemptCount ?? retryableFailedAttemptCounts.get(chunk.id) ?? 0) + 1,
|
|
9181
|
+
lastAttempt: failureTimestamp
|
|
9182
|
+
};
|
|
9183
|
+
if (existingFailedBatchIndex === -1) {
|
|
9184
|
+
failedBatchesForCurrentRun.push(failedBatch);
|
|
9185
|
+
} else {
|
|
9186
|
+
failedBatchesForCurrentRun[existingFailedBatchIndex] = failedBatch;
|
|
9187
|
+
}
|
|
9188
|
+
}
|
|
7915
9189
|
this.logger.recordEmbeddingError();
|
|
7916
9190
|
this.logger.embedding("error", `Failed to embed batch after retries`, {
|
|
7917
|
-
batchSize:
|
|
7918
|
-
|
|
9191
|
+
batchSize: failedChunks.length,
|
|
9192
|
+
requestCount: requestBatch.length,
|
|
9193
|
+
error: failureMessage
|
|
7919
9194
|
});
|
|
7920
9195
|
}
|
|
7921
9196
|
});
|
|
7922
9197
|
}
|
|
7923
9198
|
await queue.onIdle();
|
|
9199
|
+
if (scopedRoots) {
|
|
9200
|
+
this.saveScopedFailedBatches(coalesceFailedBatches(failedBatchesForCurrentRun), scopedRoots);
|
|
9201
|
+
} else {
|
|
9202
|
+
this.saveFailedBatches(coalesceFailedBatches(failedBatchesForCurrentRun));
|
|
9203
|
+
}
|
|
7924
9204
|
onProgress?.({
|
|
7925
9205
|
phase: "storing",
|
|
7926
9206
|
filesProcessed: files.length,
|
|
@@ -7928,18 +9208,48 @@ var Indexer = class {
|
|
|
7928
9208
|
chunksProcessed: stats.indexedChunks,
|
|
7929
9209
|
totalChunks: pendingChunks.length
|
|
7930
9210
|
});
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
9211
|
+
const branchChunkIds = Array.from(currentChunkIds).filter(
|
|
9212
|
+
(chunkId) => {
|
|
9213
|
+
const isNewlyFailed = failedChunkIds.has(chunkId) && !retryableChunksWithExistingData.has(chunkId);
|
|
9214
|
+
const isForcedFailed = forceScopedReembed && failedForcedChunkIds.has(chunkId);
|
|
9215
|
+
return !isNewlyFailed && !isForcedFailed;
|
|
9216
|
+
}
|
|
9217
|
+
);
|
|
9218
|
+
database.clearBranch(branchCatalogKey);
|
|
9219
|
+
database.addChunksToBranchBatch(branchCatalogKey, branchChunkIds);
|
|
9220
|
+
database.clearBranchSymbols(branchCatalogKey);
|
|
9221
|
+
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
7935
9222
|
store.save();
|
|
7936
9223
|
invertedIndex.save();
|
|
7937
|
-
|
|
7938
|
-
|
|
9224
|
+
if (scopedRoots) {
|
|
9225
|
+
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
9226
|
+
} else {
|
|
9227
|
+
this.fileHashCache = currentFileHashes;
|
|
9228
|
+
this.saveFileHashCache();
|
|
9229
|
+
}
|
|
7939
9230
|
if (this.config.indexing.autoGc && stats.removedChunks > 0) {
|
|
7940
|
-
await this.maybeRunOrphanGc();
|
|
9231
|
+
const gcReset = await this.maybeRunOrphanGc();
|
|
9232
|
+
if (gcReset) {
|
|
9233
|
+
stats.durationMs = Date.now() - startTime;
|
|
9234
|
+
stats.warning = gcReset.warning;
|
|
9235
|
+
stats.resetCorruptedIndex = true;
|
|
9236
|
+
this.logger.recordIndexingEnd();
|
|
9237
|
+
this.logger.warn("Indexing ended after resetting corrupted local index during automatic GC", {
|
|
9238
|
+
files: stats.totalFiles,
|
|
9239
|
+
indexed: stats.indexedChunks,
|
|
9240
|
+
existing: stats.existingChunks,
|
|
9241
|
+
removed: stats.removedChunks,
|
|
9242
|
+
failed: stats.failedChunks,
|
|
9243
|
+
tokens: stats.tokensUsed,
|
|
9244
|
+
durationMs: stats.durationMs
|
|
9245
|
+
});
|
|
9246
|
+
return stats;
|
|
9247
|
+
}
|
|
7941
9248
|
}
|
|
7942
9249
|
stats.durationMs = Date.now() - startTime;
|
|
9250
|
+
if (forceScopedReembed && failedForcedChunkIds.size === 0) {
|
|
9251
|
+
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
9252
|
+
}
|
|
7943
9253
|
this.saveIndexMetadata(configuredProviderInfo);
|
|
7944
9254
|
this.indexCompatibility = { compatible: true };
|
|
7945
9255
|
this.logger.recordIndexingEnd();
|
|
@@ -8068,27 +9378,30 @@ var Indexer = class {
|
|
|
8068
9378
|
const keywordResults = await this.keywordSearch(query, maxResults * 4);
|
|
8069
9379
|
const keywordMs = performance2.now() - keywordStartTime;
|
|
8070
9380
|
let branchChunkIds = null;
|
|
8071
|
-
if (filterByBranch && this.currentBranch !== "default") {
|
|
8072
|
-
branchChunkIds = new Set(
|
|
9381
|
+
if (filterByBranch && (this.config.scope === "global" || this.currentBranch !== "default")) {
|
|
9382
|
+
branchChunkIds = new Set(
|
|
9383
|
+
this.getBranchCatalogKeys().flatMap((branchKey) => database.getBranchChunkIds(branchKey))
|
|
9384
|
+
);
|
|
8073
9385
|
}
|
|
8074
9386
|
const prefilterStartTime = performance2.now();
|
|
8075
|
-
const shouldPrefilterByBranch = branchChunkIds !== null && branchChunkIds.size > 0;
|
|
9387
|
+
const shouldPrefilterByBranch = branchChunkIds !== null && (this.config.scope === "global" || branchChunkIds.size > 0);
|
|
9388
|
+
const allowBranchPrefilterFallback = this.config.scope !== "global";
|
|
8076
9389
|
const prefilteredSemantic = shouldPrefilterByBranch && branchChunkIds ? semanticResults.filter((r) => branchChunkIds.has(r.id)) : semanticResults;
|
|
8077
9390
|
const prefilteredKeyword = shouldPrefilterByBranch && branchChunkIds ? keywordResults.filter((r) => branchChunkIds.has(r.id)) : keywordResults;
|
|
8078
|
-
const semanticCandidates = shouldPrefilterByBranch && semanticResults.length > 0 && prefilteredSemantic.length === 0 ? semanticResults : prefilteredSemantic;
|
|
8079
|
-
const keywordCandidates = shouldPrefilterByBranch && keywordResults.length > 0 && prefilteredKeyword.length === 0 ? keywordResults : prefilteredKeyword;
|
|
9391
|
+
const semanticCandidates = allowBranchPrefilterFallback && shouldPrefilterByBranch && semanticResults.length > 0 && prefilteredSemantic.length === 0 ? semanticResults : prefilteredSemantic;
|
|
9392
|
+
const keywordCandidates = allowBranchPrefilterFallback && shouldPrefilterByBranch && keywordResults.length > 0 && prefilteredKeyword.length === 0 ? keywordResults : prefilteredKeyword;
|
|
8080
9393
|
const prefilterMs = performance2.now() - prefilterStartTime;
|
|
8081
|
-
if (branchChunkIds && branchChunkIds.size === 0) {
|
|
9394
|
+
if (this.config.scope !== "global" && branchChunkIds && branchChunkIds.size === 0) {
|
|
8082
9395
|
this.logger.search("warn", "Branch prefilter skipped because branch catalog is empty", {
|
|
8083
9396
|
branch: this.currentBranch
|
|
8084
9397
|
});
|
|
8085
9398
|
}
|
|
8086
|
-
if (shouldPrefilterByBranch && semanticResults.length > 0 && prefilteredSemantic.length === 0) {
|
|
9399
|
+
if (allowBranchPrefilterFallback && shouldPrefilterByBranch && semanticResults.length > 0 && prefilteredSemantic.length === 0) {
|
|
8087
9400
|
this.logger.search("warn", "Branch prefilter produced no semantic overlap, using unfiltered semantic candidates", {
|
|
8088
9401
|
branch: this.currentBranch
|
|
8089
9402
|
});
|
|
8090
9403
|
}
|
|
8091
|
-
if (shouldPrefilterByBranch && keywordResults.length > 0 && prefilteredKeyword.length === 0) {
|
|
9404
|
+
if (allowBranchPrefilterFallback && shouldPrefilterByBranch && keywordResults.length > 0 && prefilteredKeyword.length === 0) {
|
|
8092
9405
|
this.logger.search("warn", "Branch prefilter produced no keyword overlap, using unfiltered keyword candidates", {
|
|
8093
9406
|
branch: this.currentBranch
|
|
8094
9407
|
});
|
|
@@ -8141,26 +9454,13 @@ var Indexer = class {
|
|
|
8141
9454
|
const primaryLane = mergeTieredResults(prePrimaryLane, symbolLane, maxResults * 4);
|
|
8142
9455
|
const tiered = mergeTieredResults(primaryLane, rescued, maxResults * 4);
|
|
8143
9456
|
const hasCodeHints = extractCodeTermHints(query).length > 0 || identifierHints.length > 0;
|
|
8144
|
-
const baseFiltered = tiered.filter((r) =>
|
|
8145
|
-
if (r.score < this.config.search.minScore) return false;
|
|
8146
|
-
if (options?.fileType) {
|
|
8147
|
-
const ext = r.metadata.filePath.split(".").pop()?.toLowerCase();
|
|
8148
|
-
if (ext !== options.fileType.toLowerCase().replace(/^\./, "")) return false;
|
|
8149
|
-
}
|
|
8150
|
-
if (options?.directory) {
|
|
8151
|
-
const normalizedDir = options.directory.replace(/^\/|\/$/g, "");
|
|
8152
|
-
if (!r.metadata.filePath.includes(`/${normalizedDir}/`) && !r.metadata.filePath.includes(`${normalizedDir}/`)) return false;
|
|
8153
|
-
}
|
|
8154
|
-
if (options?.chunkType) {
|
|
8155
|
-
if (r.metadata.chunkType !== options.chunkType) return false;
|
|
8156
|
-
}
|
|
8157
|
-
return true;
|
|
8158
|
-
});
|
|
9457
|
+
const baseFiltered = tiered.filter((r) => matchesSearchFilters(r, options, this.config.search.minScore));
|
|
8159
9458
|
const implementationOnly = baseFiltered.filter(
|
|
8160
9459
|
(r) => isLikelyImplementationPath(r.metadata.filePath) && isImplementationChunkType(r.metadata.chunkType)
|
|
8161
9460
|
);
|
|
8162
9461
|
const filtered = (sourceIntent && hasCodeHints && implementationOnly.length > 0 ? implementationOnly : baseFiltered).slice(0, maxResults);
|
|
8163
|
-
const
|
|
9462
|
+
const identifierFallback = !options?.definitionIntent && filtered.length === 0 && identifierHints.length > 0 ? buildSymbolDefinitionLane(query, database, branchChunkIds, maxResults, union, true).filter((r) => matchesSearchFilters(r, options, this.config.search.minScore)).slice(0, maxResults) : [];
|
|
9463
|
+
const finalResults = filtered.length > 0 ? filtered : identifierFallback;
|
|
8164
9464
|
const totalSearchMs = performance2.now() - searchStartTime;
|
|
8165
9465
|
this.logger.recordSearch(totalSearchMs, {
|
|
8166
9466
|
embeddingMs,
|
|
@@ -8230,7 +9530,8 @@ var Indexer = class {
|
|
|
8230
9530
|
return results.slice(0, limit);
|
|
8231
9531
|
}
|
|
8232
9532
|
async getStatus() {
|
|
8233
|
-
const { store, configuredProviderInfo } = await this.ensureInitialized();
|
|
9533
|
+
const { store, configuredProviderInfo, database } = await this.ensureInitialized();
|
|
9534
|
+
const failedBatchesCount = this.getFailedBatchesCount();
|
|
8234
9535
|
return {
|
|
8235
9536
|
indexed: store.count() > 0,
|
|
8236
9537
|
vectorCount: store.count(),
|
|
@@ -8239,23 +9540,86 @@ var Indexer = class {
|
|
|
8239
9540
|
indexPath: this.indexPath,
|
|
8240
9541
|
currentBranch: this.currentBranch,
|
|
8241
9542
|
baseBranch: this.baseBranch,
|
|
8242
|
-
compatibility: this.indexCompatibility
|
|
9543
|
+
compatibility: this.indexCompatibility,
|
|
9544
|
+
failedBatchesCount,
|
|
9545
|
+
failedBatchesPath: failedBatchesCount > 0 ? this.failedBatchesPath : void 0,
|
|
9546
|
+
warning: database.getMetadata(STARTUP_WARNING_METADATA_KEY) ?? void 0
|
|
8243
9547
|
};
|
|
8244
9548
|
}
|
|
8245
9549
|
async clearIndex() {
|
|
8246
9550
|
const { store, invertedIndex, database } = await this.ensureInitialized();
|
|
9551
|
+
if (this.config.scope === "global") {
|
|
9552
|
+
store.load();
|
|
9553
|
+
invertedIndex.load();
|
|
9554
|
+
this.loadFileHashCache();
|
|
9555
|
+
const roots = this.getScopedRoots();
|
|
9556
|
+
const compatibility = this.checkCompatibility();
|
|
9557
|
+
const allMetadata = store.getAllMetadata();
|
|
9558
|
+
const hasForeignData = allMetadata.some(({ metadata }) => !this.isFileInCurrentScope(metadata.filePath, roots)) || this.hasForeignScopedBranchData() || this.hasForeignScopedFileHashData(roots) || this.hasForeignScopedFailedBatches(roots);
|
|
9559
|
+
if (!compatibility.compatible && hasForeignData) {
|
|
9560
|
+
if (compatibility.code === "EMBEDDING_STRATEGY_MISMATCH" /* EMBEDDING_STRATEGY_MISMATCH */) {
|
|
9561
|
+
this.clearSharedIndexProjectData(store, invertedIndex, database, roots);
|
|
9562
|
+
this.clearScopedFileHashCache(roots);
|
|
9563
|
+
this.clearScopedFailedBatches(roots);
|
|
9564
|
+
database.setMetadata(this.getProjectForceReembedMetadataKey(), "true");
|
|
9565
|
+
database.deleteMetadata(this.getProjectEmbeddingStrategyMetadataKey());
|
|
9566
|
+
this.indexCompatibility = { compatible: true };
|
|
9567
|
+
return;
|
|
9568
|
+
}
|
|
9569
|
+
throw new Error(
|
|
9570
|
+
`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.`
|
|
9571
|
+
);
|
|
9572
|
+
}
|
|
9573
|
+
if (!hasForeignData) {
|
|
9574
|
+
store.clear();
|
|
9575
|
+
store.save();
|
|
9576
|
+
invertedIndex.clear();
|
|
9577
|
+
invertedIndex.save();
|
|
9578
|
+
this.fileHashCache.clear();
|
|
9579
|
+
this.saveFileHashCache();
|
|
9580
|
+
database.clearAllIndexedData();
|
|
9581
|
+
this.saveFailedBatches([]);
|
|
9582
|
+
database.deleteMetadata("index.version");
|
|
9583
|
+
database.deleteMetadata("index.embeddingProvider");
|
|
9584
|
+
database.deleteMetadata("index.embeddingModel");
|
|
9585
|
+
database.deleteMetadata("index.embeddingDimensions");
|
|
9586
|
+
database.deleteMetadata("index.embeddingStrategyVersion");
|
|
9587
|
+
database.deleteMetadata(this.getProjectEmbeddingStrategyMetadataKey());
|
|
9588
|
+
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
9589
|
+
database.deleteMetadata(this.getLegacyMigrationMetadataKey());
|
|
9590
|
+
database.deleteMetadata("index.createdAt");
|
|
9591
|
+
database.deleteMetadata("index.updatedAt");
|
|
9592
|
+
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
9593
|
+
return;
|
|
9594
|
+
}
|
|
9595
|
+
this.clearSharedIndexProjectData(store, invertedIndex, database, roots);
|
|
9596
|
+
this.clearScopedFileHashCache(roots);
|
|
9597
|
+
this.clearScopedFailedBatches(roots);
|
|
9598
|
+
this.indexCompatibility = compatibility;
|
|
9599
|
+
return;
|
|
9600
|
+
}
|
|
9601
|
+
const localProjectIndexPath = path8.join(this.projectRoot, ".opencode", "index");
|
|
9602
|
+
if (path8.resolve(this.indexPath) !== path8.resolve(localProjectIndexPath)) {
|
|
9603
|
+
throw new Error(
|
|
9604
|
+
"Project-scoped force rebuild is unsafe while using an inherited worktree index. Create a local project config boundary before clearing the index."
|
|
9605
|
+
);
|
|
9606
|
+
}
|
|
8247
9607
|
store.clear();
|
|
8248
9608
|
store.save();
|
|
8249
9609
|
invertedIndex.clear();
|
|
8250
9610
|
invertedIndex.save();
|
|
8251
9611
|
this.fileHashCache.clear();
|
|
8252
9612
|
this.saveFileHashCache();
|
|
8253
|
-
database.
|
|
8254
|
-
|
|
9613
|
+
database.clearAllIndexedData();
|
|
9614
|
+
this.saveFailedBatches([]);
|
|
8255
9615
|
database.deleteMetadata("index.version");
|
|
8256
9616
|
database.deleteMetadata("index.embeddingProvider");
|
|
8257
9617
|
database.deleteMetadata("index.embeddingModel");
|
|
8258
9618
|
database.deleteMetadata("index.embeddingDimensions");
|
|
9619
|
+
database.deleteMetadata("index.embeddingStrategyVersion");
|
|
9620
|
+
database.deleteMetadata(this.getProjectEmbeddingStrategyMetadataKey());
|
|
9621
|
+
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
9622
|
+
database.deleteMetadata(this.getLegacyMigrationMetadataKey());
|
|
8259
9623
|
database.deleteMetadata("index.createdAt");
|
|
8260
9624
|
database.deleteMetadata("index.updatedAt");
|
|
8261
9625
|
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
@@ -8271,28 +9635,61 @@ var Indexer = class {
|
|
|
8271
9635
|
filePathsToChunkKeys.set(metadata.filePath, existing);
|
|
8272
9636
|
}
|
|
8273
9637
|
const removedFilePaths = [];
|
|
8274
|
-
|
|
9638
|
+
const removedChunkKeys = [];
|
|
9639
|
+
const chunkKeysByRemovedFile = /* @__PURE__ */ new Map();
|
|
8275
9640
|
for (const [filePath, chunkKeys] of filePathsToChunkKeys) {
|
|
8276
|
-
if (!
|
|
9641
|
+
if (!existsSync6(filePath)) {
|
|
9642
|
+
chunkKeysByRemovedFile.set(filePath, chunkKeys);
|
|
8277
9643
|
for (const key of chunkKeys) {
|
|
8278
|
-
|
|
8279
|
-
invertedIndex.removeChunk(key);
|
|
8280
|
-
removedCount++;
|
|
9644
|
+
removedChunkKeys.push(key);
|
|
8281
9645
|
}
|
|
8282
|
-
database.deleteChunksByFile(filePath);
|
|
8283
|
-
database.deleteCallEdgesByFile(filePath);
|
|
8284
|
-
database.deleteSymbolsByFile(filePath);
|
|
8285
9646
|
removedFilePaths.push(filePath);
|
|
8286
9647
|
}
|
|
8287
9648
|
}
|
|
9649
|
+
if (removedChunkKeys.length > 0) {
|
|
9650
|
+
this.rebuildVectorStoreExcludingChunkIds(store, database, removedChunkKeys);
|
|
9651
|
+
for (const key of removedChunkKeys) {
|
|
9652
|
+
invertedIndex.removeChunk(key);
|
|
9653
|
+
}
|
|
9654
|
+
}
|
|
9655
|
+
for (const filePath of removedFilePaths) {
|
|
9656
|
+
const fileChunkKeys = chunkKeysByRemovedFile.get(filePath) ?? [];
|
|
9657
|
+
if (fileChunkKeys.length > 0) {
|
|
9658
|
+
database.deleteChunksByIds(fileChunkKeys);
|
|
9659
|
+
}
|
|
9660
|
+
database.deleteCallEdgesByFile(filePath);
|
|
9661
|
+
database.deleteSymbolsByFile(filePath);
|
|
9662
|
+
}
|
|
9663
|
+
const removedCount = removedChunkKeys.length;
|
|
8288
9664
|
if (removedCount > 0) {
|
|
8289
9665
|
store.save();
|
|
8290
9666
|
invertedIndex.save();
|
|
8291
9667
|
}
|
|
8292
|
-
|
|
8293
|
-
|
|
8294
|
-
|
|
8295
|
-
|
|
9668
|
+
let gcOrphanEmbeddings;
|
|
9669
|
+
let gcOrphanChunks;
|
|
9670
|
+
let gcOrphanSymbols;
|
|
9671
|
+
let gcOrphanCallEdges;
|
|
9672
|
+
try {
|
|
9673
|
+
gcOrphanEmbeddings = database.gcOrphanEmbeddings();
|
|
9674
|
+
gcOrphanChunks = database.gcOrphanChunks();
|
|
9675
|
+
gcOrphanSymbols = database.gcOrphanSymbols();
|
|
9676
|
+
gcOrphanCallEdges = database.gcOrphanCallEdges();
|
|
9677
|
+
} catch (error) {
|
|
9678
|
+
if (!await this.tryResetCorruptedIndex("running index health check", error)) {
|
|
9679
|
+
throw error;
|
|
9680
|
+
}
|
|
9681
|
+
await this.ensureInitialized();
|
|
9682
|
+
return {
|
|
9683
|
+
removed: 0,
|
|
9684
|
+
filePaths: [],
|
|
9685
|
+
gcOrphanEmbeddings: 0,
|
|
9686
|
+
gcOrphanChunks: 0,
|
|
9687
|
+
gcOrphanSymbols: 0,
|
|
9688
|
+
gcOrphanCallEdges: 0,
|
|
9689
|
+
resetCorruptedIndex: true,
|
|
9690
|
+
warning: this.getCorruptedIndexWarning(path8.join(this.indexPath, "codebase.db"))
|
|
9691
|
+
};
|
|
9692
|
+
}
|
|
8296
9693
|
this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
|
|
8297
9694
|
this.logger.gc("info", "Health check complete", {
|
|
8298
9695
|
removedStale: removedCount,
|
|
@@ -8303,8 +9700,12 @@ var Indexer = class {
|
|
|
8303
9700
|
return { removed: removedCount, filePaths: removedFilePaths, gcOrphanEmbeddings, gcOrphanChunks, gcOrphanSymbols, gcOrphanCallEdges };
|
|
8304
9701
|
}
|
|
8305
9702
|
async retryFailedBatches() {
|
|
8306
|
-
const { store, provider, invertedIndex } = await this.ensureInitialized();
|
|
8307
|
-
const
|
|
9703
|
+
const { store, provider, invertedIndex, database, configuredProviderInfo } = await this.ensureInitialized();
|
|
9704
|
+
const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
|
|
9705
|
+
const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
|
|
9706
|
+
const roots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
9707
|
+
const { scoped: scopedFailedBatches, retained: retainedFailedBatches } = roots ? this.partitionFailedBatches(roots, maxChunkTokens) : { scoped: this.loadFailedBatches(maxChunkTokens), retained: [] };
|
|
9708
|
+
const failedBatches = scopedFailedBatches;
|
|
8308
9709
|
if (failedBatches.length === 0) {
|
|
8309
9710
|
return { succeeded: 0, failed: 0, remaining: 0 };
|
|
8310
9711
|
}
|
|
@@ -8312,49 +9713,170 @@ var Indexer = class {
|
|
|
8312
9713
|
let failed = 0;
|
|
8313
9714
|
const stillFailing = [];
|
|
8314
9715
|
for (const batch of failedBatches) {
|
|
9716
|
+
const batchChunksById = new Map(batch.chunks.map((chunk) => [chunk.id, chunk]));
|
|
9717
|
+
const embeddingPartsByChunk = /* @__PURE__ */ new Map();
|
|
9718
|
+
const completedChunkIds = /* @__PURE__ */ new Set();
|
|
9719
|
+
const failedChunkIds = /* @__PURE__ */ new Set();
|
|
9720
|
+
const failedChunksForBatch = /* @__PURE__ */ new Map();
|
|
9721
|
+
const pooledResults = [];
|
|
8315
9722
|
try {
|
|
8316
|
-
const
|
|
8317
|
-
|
|
8318
|
-
|
|
8319
|
-
return provider.embedBatch(texts);
|
|
8320
|
-
},
|
|
8321
|
-
{
|
|
8322
|
-
retries: this.config.indexing.retries,
|
|
8323
|
-
minTimeout: this.config.indexing.retryDelayMs
|
|
8324
|
-
}
|
|
9723
|
+
const requestBatches = createPendingEmbeddingRequestBatches(
|
|
9724
|
+
batch.chunks,
|
|
9725
|
+
getDynamicBatchOptions(configuredProviderInfo)
|
|
8325
9726
|
);
|
|
8326
|
-
const
|
|
9727
|
+
for (const requestBatch of requestBatches) {
|
|
9728
|
+
try {
|
|
9729
|
+
const result = await pRetry(
|
|
9730
|
+
async () => {
|
|
9731
|
+
const texts = requestBatch.map((request) => request.text);
|
|
9732
|
+
return provider.embedBatch(texts);
|
|
9733
|
+
},
|
|
9734
|
+
{
|
|
9735
|
+
retries: this.config.indexing.retries,
|
|
9736
|
+
minTimeout: Math.max(this.config.indexing.retryDelayMs, providerRateLimits.minRetryMs),
|
|
9737
|
+
maxTimeout: providerRateLimits.maxRetryMs,
|
|
9738
|
+
factor: 2,
|
|
9739
|
+
shouldRetry: (error) => !(error.error instanceof CustomProviderNonRetryableError)
|
|
9740
|
+
}
|
|
9741
|
+
);
|
|
9742
|
+
const touchedChunkIds = /* @__PURE__ */ new Set();
|
|
9743
|
+
requestBatch.forEach((request, idx) => {
|
|
9744
|
+
if (failedChunkIds.has(request.chunk.id) || completedChunkIds.has(request.chunk.id)) {
|
|
9745
|
+
return;
|
|
9746
|
+
}
|
|
9747
|
+
const vector = result.embeddings[idx];
|
|
9748
|
+
if (!vector) {
|
|
9749
|
+
throw new Error(`Embedding API returned too few vectors for chunk ${request.chunk.id}`);
|
|
9750
|
+
}
|
|
9751
|
+
const parts = embeddingPartsByChunk.get(request.chunk.id) ?? [];
|
|
9752
|
+
parts[request.partIndex] = {
|
|
9753
|
+
vector,
|
|
9754
|
+
tokenCount: request.tokenCount
|
|
9755
|
+
};
|
|
9756
|
+
embeddingPartsByChunk.set(request.chunk.id, parts);
|
|
9757
|
+
touchedChunkIds.add(request.chunk.id);
|
|
9758
|
+
});
|
|
9759
|
+
for (const chunkId of touchedChunkIds) {
|
|
9760
|
+
if (failedChunkIds.has(chunkId) || completedChunkIds.has(chunkId)) {
|
|
9761
|
+
continue;
|
|
9762
|
+
}
|
|
9763
|
+
const chunk = batchChunksById.get(chunkId);
|
|
9764
|
+
if (!chunk) {
|
|
9765
|
+
continue;
|
|
9766
|
+
}
|
|
9767
|
+
const parts = embeddingPartsByChunk.get(chunk.id) ?? [];
|
|
9768
|
+
if (!hasAllEmbeddingParts(parts, chunk.texts.length)) {
|
|
9769
|
+
continue;
|
|
9770
|
+
}
|
|
9771
|
+
const orderedParts = parts;
|
|
9772
|
+
pooledResults.push({
|
|
9773
|
+
chunk,
|
|
9774
|
+
vector: poolEmbeddingVectors(
|
|
9775
|
+
orderedParts.map((part) => part.vector),
|
|
9776
|
+
orderedParts.map((part) => part.tokenCount)
|
|
9777
|
+
)
|
|
9778
|
+
});
|
|
9779
|
+
}
|
|
9780
|
+
this.logger.recordEmbeddingApiCall(result.totalTokensUsed);
|
|
9781
|
+
} catch (error) {
|
|
9782
|
+
const failureMessage = String(error);
|
|
9783
|
+
const failureTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
9784
|
+
const failedChunks = getUniquePendingChunksFromRequests(requestBatch).filter((chunk) => !completedChunkIds.has(chunk.id) && !failedChunkIds.has(chunk.id));
|
|
9785
|
+
for (const chunk of failedChunks) {
|
|
9786
|
+
failedChunkIds.add(chunk.id);
|
|
9787
|
+
embeddingPartsByChunk.delete(chunk.id);
|
|
9788
|
+
failedChunksForBatch.set(chunk.id, {
|
|
9789
|
+
chunks: [chunk],
|
|
9790
|
+
attemptCount: batch.attemptCount + 1,
|
|
9791
|
+
lastAttempt: failureTimestamp,
|
|
9792
|
+
error: failureMessage
|
|
9793
|
+
});
|
|
9794
|
+
}
|
|
9795
|
+
failed += failedChunks.length;
|
|
9796
|
+
this.logger.recordEmbeddingError();
|
|
9797
|
+
}
|
|
9798
|
+
}
|
|
9799
|
+
const successfulResults = pooledResults.filter(({ chunk }) => !failedChunkIds.has(chunk.id));
|
|
9800
|
+
const items = successfulResults.map(({ chunk, vector }) => ({
|
|
8327
9801
|
id: chunk.id,
|
|
8328
|
-
vector
|
|
9802
|
+
vector,
|
|
8329
9803
|
metadata: chunk.metadata
|
|
8330
9804
|
}));
|
|
8331
|
-
|
|
8332
|
-
|
|
9805
|
+
if (items.length > 0) {
|
|
9806
|
+
store.addBatch(items);
|
|
9807
|
+
}
|
|
9808
|
+
if (successfulResults.length > 0) {
|
|
9809
|
+
try {
|
|
9810
|
+
database.upsertEmbeddingsBatch(
|
|
9811
|
+
successfulResults.map(({ chunk, vector }) => ({
|
|
9812
|
+
contentHash: chunk.contentHash,
|
|
9813
|
+
embedding: float32ArrayToBuffer(vector),
|
|
9814
|
+
chunkText: chunk.storageText,
|
|
9815
|
+
model: configuredProviderInfo.modelInfo.model
|
|
9816
|
+
}))
|
|
9817
|
+
);
|
|
9818
|
+
} catch (dbError) {
|
|
9819
|
+
this.rebuildVectorStoreExcludingChunkIds(
|
|
9820
|
+
store,
|
|
9821
|
+
database,
|
|
9822
|
+
successfulResults.map(({ chunk }) => chunk.id)
|
|
9823
|
+
);
|
|
9824
|
+
throw dbError;
|
|
9825
|
+
}
|
|
9826
|
+
}
|
|
9827
|
+
for (const { chunk } of successfulResults) {
|
|
8333
9828
|
invertedIndex.removeChunk(chunk.id);
|
|
8334
9829
|
invertedIndex.addChunk(chunk.id, chunk.content);
|
|
9830
|
+
completedChunkIds.add(chunk.id);
|
|
9831
|
+
embeddingPartsByChunk.delete(chunk.id);
|
|
8335
9832
|
}
|
|
8336
|
-
|
|
8337
|
-
|
|
8338
|
-
|
|
9833
|
+
database.addChunksToBranchBatch(
|
|
9834
|
+
this.getBranchCatalogKey(),
|
|
9835
|
+
successfulResults.map(({ chunk }) => chunk.id)
|
|
9836
|
+
);
|
|
9837
|
+
this.logger.recordChunksEmbedded(successfulResults.length);
|
|
9838
|
+
succeeded += successfulResults.length;
|
|
9839
|
+
stillFailing.push(...failedChunksForBatch.values());
|
|
8339
9840
|
} catch (error) {
|
|
8340
|
-
|
|
9841
|
+
const failureMessage = getErrorMessage(error);
|
|
9842
|
+
const failureTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
9843
|
+
const unaccountedChunks = batch.chunks.filter(
|
|
9844
|
+
(chunk) => !failedChunksForBatch.has(chunk.id) && !completedChunkIds.has(chunk.id)
|
|
9845
|
+
);
|
|
9846
|
+
for (const chunk of unaccountedChunks) {
|
|
9847
|
+
failedChunksForBatch.set(chunk.id, {
|
|
9848
|
+
chunks: [chunk],
|
|
9849
|
+
attemptCount: batch.attemptCount + 1,
|
|
9850
|
+
lastAttempt: failureTimestamp,
|
|
9851
|
+
error: failureMessage
|
|
9852
|
+
});
|
|
9853
|
+
}
|
|
9854
|
+
failed += unaccountedChunks.length;
|
|
8341
9855
|
this.logger.recordEmbeddingError();
|
|
8342
|
-
stillFailing.push(
|
|
8343
|
-
...batch,
|
|
8344
|
-
attemptCount: batch.attemptCount + 1,
|
|
8345
|
-
lastAttempt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8346
|
-
error: String(error)
|
|
8347
|
-
});
|
|
9856
|
+
stillFailing.push(...coalesceFailedBatches(Array.from(failedChunksForBatch.values())));
|
|
8348
9857
|
}
|
|
8349
9858
|
}
|
|
8350
|
-
|
|
9859
|
+
const persistedStillFailing = coalesceFailedBatches(stillFailing);
|
|
9860
|
+
if (roots) {
|
|
9861
|
+
this.saveFailedBatches([...retainedFailedBatches, ...persistedStillFailing]);
|
|
9862
|
+
} else {
|
|
9863
|
+
this.saveFailedBatches(persistedStillFailing);
|
|
9864
|
+
}
|
|
8351
9865
|
if (succeeded > 0) {
|
|
8352
9866
|
store.save();
|
|
8353
9867
|
invertedIndex.save();
|
|
8354
9868
|
}
|
|
8355
|
-
|
|
9869
|
+
if (roots && succeeded > 0 && persistedStillFailing.length === 0 && this.hasProjectForceReembedPending()) {
|
|
9870
|
+
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
9871
|
+
this.saveIndexMetadata(configuredProviderInfo);
|
|
9872
|
+
this.indexCompatibility = { compatible: true };
|
|
9873
|
+
}
|
|
9874
|
+
return { succeeded, failed, remaining: persistedStillFailing.length };
|
|
8356
9875
|
}
|
|
8357
9876
|
getFailedBatchesCount() {
|
|
9877
|
+
if (this.config.scope === "global") {
|
|
9878
|
+
return this.partitionFailedBatches(this.getScopedRoots()).scoped.length;
|
|
9879
|
+
}
|
|
8358
9880
|
return this.loadFailedBatches().length;
|
|
8359
9881
|
}
|
|
8360
9882
|
getCurrentBranch() {
|
|
@@ -8403,20 +9925,23 @@ var Indexer = class {
|
|
|
8403
9925
|
const semanticResults = store.search(embedding, limit * 2);
|
|
8404
9926
|
const vectorMs = performance2.now() - vectorStartTime;
|
|
8405
9927
|
let branchChunkIds = null;
|
|
8406
|
-
if (filterByBranch && this.currentBranch !== "default") {
|
|
8407
|
-
branchChunkIds = new Set(
|
|
9928
|
+
if (filterByBranch && (this.config.scope === "global" || this.currentBranch !== "default")) {
|
|
9929
|
+
branchChunkIds = new Set(
|
|
9930
|
+
this.getBranchCatalogKeys().flatMap((branchKey) => database.getBranchChunkIds(branchKey))
|
|
9931
|
+
);
|
|
8408
9932
|
}
|
|
8409
9933
|
const prefilterStartTime = performance2.now();
|
|
8410
|
-
const shouldPrefilterByBranch = branchChunkIds !== null && branchChunkIds.size > 0;
|
|
9934
|
+
const shouldPrefilterByBranch = branchChunkIds !== null && (this.config.scope === "global" || branchChunkIds.size > 0);
|
|
9935
|
+
const allowBranchPrefilterFallback = this.config.scope !== "global";
|
|
8411
9936
|
const prefilteredSemantic = shouldPrefilterByBranch && branchChunkIds ? semanticResults.filter((r) => branchChunkIds.has(r.id)) : semanticResults;
|
|
8412
|
-
const semanticCandidates = shouldPrefilterByBranch && semanticResults.length > 0 && prefilteredSemantic.length === 0 ? semanticResults : prefilteredSemantic;
|
|
9937
|
+
const semanticCandidates = allowBranchPrefilterFallback && shouldPrefilterByBranch && semanticResults.length > 0 && prefilteredSemantic.length === 0 ? semanticResults : prefilteredSemantic;
|
|
8413
9938
|
const prefilterMs = performance2.now() - prefilterStartTime;
|
|
8414
|
-
if (branchChunkIds && branchChunkIds.size === 0) {
|
|
9939
|
+
if (this.config.scope !== "global" && branchChunkIds && branchChunkIds.size === 0) {
|
|
8415
9940
|
this.logger.search("warn", "Branch prefilter skipped because branch catalog is empty", {
|
|
8416
9941
|
branch: this.currentBranch
|
|
8417
9942
|
});
|
|
8418
9943
|
}
|
|
8419
|
-
if (shouldPrefilterByBranch && semanticResults.length > 0 && prefilteredSemantic.length === 0) {
|
|
9944
|
+
if (allowBranchPrefilterFallback && shouldPrefilterByBranch && semanticResults.length > 0 && prefilteredSemantic.length === 0) {
|
|
8420
9945
|
this.logger.search("warn", "Branch prefilter produced no semantic overlap, using unfiltered semantic candidates", {
|
|
8421
9946
|
branch: this.currentBranch
|
|
8422
9947
|
});
|
|
@@ -8489,11 +10014,39 @@ var Indexer = class {
|
|
|
8489
10014
|
}
|
|
8490
10015
|
async getCallers(targetName) {
|
|
8491
10016
|
const { database } = await this.ensureInitialized();
|
|
8492
|
-
|
|
10017
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10018
|
+
const results = [];
|
|
10019
|
+
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
10020
|
+
for (const edge of database.getCallersWithContext(targetName, branchKey)) {
|
|
10021
|
+
if (!seen.has(edge.id)) {
|
|
10022
|
+
seen.add(edge.id);
|
|
10023
|
+
results.push(edge);
|
|
10024
|
+
}
|
|
10025
|
+
}
|
|
10026
|
+
}
|
|
10027
|
+
return results;
|
|
8493
10028
|
}
|
|
8494
10029
|
async getCallees(symbolId) {
|
|
8495
10030
|
const { database } = await this.ensureInitialized();
|
|
8496
|
-
|
|
10031
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10032
|
+
const results = [];
|
|
10033
|
+
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
10034
|
+
for (const edge of database.getCallees(symbolId, branchKey)) {
|
|
10035
|
+
if (!seen.has(edge.id)) {
|
|
10036
|
+
seen.add(edge.id);
|
|
10037
|
+
results.push(edge);
|
|
10038
|
+
}
|
|
10039
|
+
}
|
|
10040
|
+
}
|
|
10041
|
+
return results;
|
|
10042
|
+
}
|
|
10043
|
+
async close() {
|
|
10044
|
+
await this.database?.close();
|
|
10045
|
+
this.database = null;
|
|
10046
|
+
this.store = null;
|
|
10047
|
+
this.invertedIndex = null;
|
|
10048
|
+
this.provider = null;
|
|
10049
|
+
this.reranker = null;
|
|
8497
10050
|
}
|
|
8498
10051
|
};
|
|
8499
10052
|
|
|
@@ -8506,7 +10059,17 @@ function truncateContent(content) {
|
|
|
8506
10059
|
// ... (${lines.length - MAX_CONTENT_LINES} more lines)`;
|
|
8507
10060
|
}
|
|
8508
10061
|
function formatIndexStats(stats, verbose = false) {
|
|
10062
|
+
if (stats.resetCorruptedIndex) {
|
|
10063
|
+
return stats.warning ?? "Detected a corrupted local index and reset it during indexing. Run index_codebase again to rebuild search data.";
|
|
10064
|
+
}
|
|
8509
10065
|
const lines = [];
|
|
10066
|
+
if (stats.failedChunks > 0) {
|
|
10067
|
+
lines.push(`INDEXING WARNING: ${stats.failedChunks} chunks failed to embed.`);
|
|
10068
|
+
if (stats.failedBatchesPath) {
|
|
10069
|
+
lines.push(`Inspect failed batches at: ${stats.failedBatchesPath}`);
|
|
10070
|
+
}
|
|
10071
|
+
lines.push("");
|
|
10072
|
+
}
|
|
8510
10073
|
if (stats.indexedChunks === 0 && stats.removedChunks === 0) {
|
|
8511
10074
|
lines.push(`${stats.totalFiles} files processed, ${stats.existingChunks} code chunks already up to date.`);
|
|
8512
10075
|
} else if (stats.indexedChunks === 0) {
|
|
@@ -8551,6 +10114,19 @@ function formatIndexStats(stats, verbose = false) {
|
|
|
8551
10114
|
}
|
|
8552
10115
|
function formatStatus(status) {
|
|
8553
10116
|
if (!status.indexed) {
|
|
10117
|
+
if (status.warning) {
|
|
10118
|
+
return status.warning;
|
|
10119
|
+
}
|
|
10120
|
+
if (status.failedBatchesCount > 0) {
|
|
10121
|
+
const lines2 = [
|
|
10122
|
+
"Codebase is not indexed. The last indexing run left failed embedding batches.",
|
|
10123
|
+
"Fix the provider/model configuration, then rerun index_codebase normally to retry the saved failed batches. Use force=true only for a full rebuild or compatibility reset."
|
|
10124
|
+
];
|
|
10125
|
+
if (status.failedBatchesPath) {
|
|
10126
|
+
lines2.push(`Failed batches: ${status.failedBatchesPath}`);
|
|
10127
|
+
}
|
|
10128
|
+
return lines2.join("\n");
|
|
10129
|
+
}
|
|
8554
10130
|
return "Codebase is not indexed. Run index_codebase to create an index.";
|
|
8555
10131
|
}
|
|
8556
10132
|
const lines = [
|
|
@@ -8563,6 +10139,13 @@ function formatStatus(status) {
|
|
|
8563
10139
|
lines.push(`Current branch: ${status.currentBranch}`);
|
|
8564
10140
|
lines.push(`Base branch: ${status.baseBranch}`);
|
|
8565
10141
|
}
|
|
10142
|
+
if (status.failedBatchesCount > 0) {
|
|
10143
|
+
lines.push("");
|
|
10144
|
+
lines.push(`INDEXING WARNING: ${status.failedBatchesCount} failed embedding batch${status.failedBatchesCount === 1 ? " remains" : "es remain"}.`);
|
|
10145
|
+
if (status.failedBatchesPath) {
|
|
10146
|
+
lines.push(`Failed batches: ${status.failedBatchesPath}`);
|
|
10147
|
+
}
|
|
10148
|
+
}
|
|
8566
10149
|
if (status.compatibility && !status.compatibility.compatible) {
|
|
8567
10150
|
lines.push("");
|
|
8568
10151
|
lines.push(`COMPATIBILITY WARNING: ${status.compatibility.reason}`);
|
|
@@ -8620,6 +10203,9 @@ function formatCodebasePeek(results) {
|
|
|
8620
10203
|
return formatted.join("\n");
|
|
8621
10204
|
}
|
|
8622
10205
|
function formatHealthCheck(result) {
|
|
10206
|
+
if (result.resetCorruptedIndex) {
|
|
10207
|
+
return result.warning ?? "Detected a corrupted local index and reset it. Run index_codebase to rebuild search data.";
|
|
10208
|
+
}
|
|
8623
10209
|
if (result.removed === 0 && result.gcOrphanEmbeddings === 0 && result.gcOrphanChunks === 0 && result.gcOrphanSymbols === 0 && result.gcOrphanCallEdges === 0) {
|
|
8624
10210
|
return "Index is healthy. No stale entries found.";
|
|
8625
10211
|
}
|
|
@@ -8679,8 +10265,8 @@ ${truncateContent(r.content)}
|
|
|
8679
10265
|
}
|
|
8680
10266
|
|
|
8681
10267
|
// src/tools/index.ts
|
|
8682
|
-
import { existsSync as
|
|
8683
|
-
import * as
|
|
10268
|
+
import { existsSync as existsSync7, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, statSync as statSync2 } from "fs";
|
|
10269
|
+
import * as path9 from "path";
|
|
8684
10270
|
var z = tool.schema;
|
|
8685
10271
|
var sharedIndexer = null;
|
|
8686
10272
|
var sharedProjectRoot = "";
|
|
@@ -8695,7 +10281,20 @@ function refreshIndexerFromConfig() {
|
|
|
8695
10281
|
if (!sharedProjectRoot) {
|
|
8696
10282
|
throw new Error("Codebase index tools not initialized. Plugin may not be loaded correctly.");
|
|
8697
10283
|
}
|
|
8698
|
-
sharedIndexer = new Indexer(sharedProjectRoot, parseConfig(
|
|
10284
|
+
sharedIndexer = new Indexer(sharedProjectRoot, parseConfig(loadRuntimeConfig()));
|
|
10285
|
+
}
|
|
10286
|
+
function shouldForceLocalizeProjectIndex() {
|
|
10287
|
+
const currentConfig = parseConfig(loadRuntimeConfig());
|
|
10288
|
+
if (currentConfig.scope !== "project") {
|
|
10289
|
+
return false;
|
|
10290
|
+
}
|
|
10291
|
+
const localIndexPath = path9.join(sharedProjectRoot, ".opencode", "index");
|
|
10292
|
+
const mainRepoRoot = resolveWorktreeMainRepoRoot(sharedProjectRoot);
|
|
10293
|
+
if (!mainRepoRoot) {
|
|
10294
|
+
return false;
|
|
10295
|
+
}
|
|
10296
|
+
const inheritedIndexPath = path9.join(mainRepoRoot, ".opencode", "index");
|
|
10297
|
+
return !existsSync7(localIndexPath) && existsSync7(inheritedIndexPath);
|
|
8699
10298
|
}
|
|
8700
10299
|
function getIndexer() {
|
|
8701
10300
|
if (!sharedIndexer) {
|
|
@@ -8704,9 +10303,41 @@ function getIndexer() {
|
|
|
8704
10303
|
return sharedIndexer;
|
|
8705
10304
|
}
|
|
8706
10305
|
function getConfigPath() {
|
|
8707
|
-
return
|
|
10306
|
+
return resolveWritableProjectConfigPath(sharedProjectRoot);
|
|
8708
10307
|
}
|
|
8709
|
-
function
|
|
10308
|
+
function normalizeConfigPathValue(value, baseDir) {
|
|
10309
|
+
const trimmed = value.trim();
|
|
10310
|
+
if (!trimmed) {
|
|
10311
|
+
return trimmed;
|
|
10312
|
+
}
|
|
10313
|
+
const absolutePath = path9.isAbsolute(trimmed) ? trimmed : path9.resolve(baseDir, trimmed);
|
|
10314
|
+
return path9.normalize(absolutePath);
|
|
10315
|
+
}
|
|
10316
|
+
function serializeConfigPathValue(value, baseDir) {
|
|
10317
|
+
const trimmed = value.trim();
|
|
10318
|
+
if (!trimmed) {
|
|
10319
|
+
return trimmed;
|
|
10320
|
+
}
|
|
10321
|
+
const normalizeRelativePath = (candidate) => candidate.replace(/\\/g, "/");
|
|
10322
|
+
if (!path9.isAbsolute(trimmed)) {
|
|
10323
|
+
return normalizeRelativePath(path9.normalize(trimmed));
|
|
10324
|
+
}
|
|
10325
|
+
const relativePath = path9.relative(baseDir, trimmed);
|
|
10326
|
+
if (!relativePath || !relativePath.startsWith("..") && !path9.isAbsolute(relativePath)) {
|
|
10327
|
+
return normalizeRelativePath(path9.normalize(relativePath || "."));
|
|
10328
|
+
}
|
|
10329
|
+
return path9.normalize(trimmed);
|
|
10330
|
+
}
|
|
10331
|
+
function normalizeKnowledgeBasePaths(config) {
|
|
10332
|
+
const normalized = { ...config };
|
|
10333
|
+
if (Array.isArray(normalized.knowledgeBases)) {
|
|
10334
|
+
normalized.knowledgeBases = normalized.knowledgeBases.map((kb) => {
|
|
10335
|
+
return normalizeConfigPathValue(kb, sharedProjectRoot);
|
|
10336
|
+
});
|
|
10337
|
+
}
|
|
10338
|
+
return normalized;
|
|
10339
|
+
}
|
|
10340
|
+
function loadRuntimeConfig() {
|
|
8710
10341
|
const rawConfig = loadMergedConfig(sharedProjectRoot);
|
|
8711
10342
|
const config = {};
|
|
8712
10343
|
if (rawConfig && typeof rawConfig === "object") {
|
|
@@ -8714,27 +10345,32 @@ function loadConfig() {
|
|
|
8714
10345
|
config[key] = rawConfig[key];
|
|
8715
10346
|
}
|
|
8716
10347
|
}
|
|
8717
|
-
|
|
8718
|
-
|
|
8719
|
-
|
|
8720
|
-
|
|
8721
|
-
|
|
8722
|
-
|
|
8723
|
-
|
|
8724
|
-
|
|
8725
|
-
|
|
8726
|
-
return path8.normalize(resolved);
|
|
8727
|
-
});
|
|
10348
|
+
return normalizeKnowledgeBasePaths(config);
|
|
10349
|
+
}
|
|
10350
|
+
function loadEditableConfig() {
|
|
10351
|
+
const rawConfig = loadProjectConfigLayer(sharedProjectRoot);
|
|
10352
|
+
const config = {};
|
|
10353
|
+
if (rawConfig && typeof rawConfig === "object") {
|
|
10354
|
+
for (const key of Object.keys(rawConfig)) {
|
|
10355
|
+
config[key] = rawConfig[key];
|
|
10356
|
+
}
|
|
8728
10357
|
}
|
|
8729
|
-
return config;
|
|
10358
|
+
return normalizeKnowledgeBasePaths(config);
|
|
8730
10359
|
}
|
|
8731
10360
|
function saveConfig(config) {
|
|
8732
|
-
const configDir = path8.join(sharedProjectRoot, ".opencode");
|
|
8733
|
-
if (!existsSync6(configDir)) {
|
|
8734
|
-
mkdirSync(configDir, { recursive: true });
|
|
8735
|
-
}
|
|
8736
10361
|
const configPath = getConfigPath();
|
|
8737
|
-
|
|
10362
|
+
const configDir = path9.dirname(configPath);
|
|
10363
|
+
const configBaseDir = path9.dirname(configDir);
|
|
10364
|
+
if (!existsSync7(configDir)) {
|
|
10365
|
+
mkdirSync3(configDir, { recursive: true });
|
|
10366
|
+
}
|
|
10367
|
+
const serializableConfig = { ...config };
|
|
10368
|
+
if (Array.isArray(serializableConfig.knowledgeBases)) {
|
|
10369
|
+
serializableConfig.knowledgeBases = serializableConfig.knowledgeBases.map(
|
|
10370
|
+
(kb) => serializeConfigPathValue(kb, configBaseDir)
|
|
10371
|
+
);
|
|
10372
|
+
}
|
|
10373
|
+
writeFileSync3(configPath, JSON.stringify(serializableConfig, null, 2) + "\n", "utf-8");
|
|
8738
10374
|
}
|
|
8739
10375
|
var codebase_peek = tool({
|
|
8740
10376
|
description: "Quick lookup of code locations by meaning. Returns only metadata (file, line, name, type) WITHOUT code content. Use this first to find WHERE code is, then use Read tool to examine specific files. Saves tokens by not returning full code blocks. Best for: discovery, navigation, finding multiple related locations.",
|
|
@@ -8764,12 +10400,17 @@ var index_codebase = tool({
|
|
|
8764
10400
|
verbose: z.boolean().optional().default(false).describe("Show detailed info about skipped files and parsing failures")
|
|
8765
10401
|
},
|
|
8766
10402
|
async execute(args, context) {
|
|
8767
|
-
|
|
10403
|
+
let indexer = getIndexer();
|
|
8768
10404
|
if (args.estimateOnly) {
|
|
8769
10405
|
const estimate = await indexer.estimateCost();
|
|
8770
10406
|
return formatCostEstimate(estimate);
|
|
8771
10407
|
}
|
|
8772
10408
|
if (args.force) {
|
|
10409
|
+
if (shouldForceLocalizeProjectIndex()) {
|
|
10410
|
+
materializeLocalProjectConfig(sharedProjectRoot, loadProjectConfigLayer(sharedProjectRoot));
|
|
10411
|
+
refreshIndexerFromConfig();
|
|
10412
|
+
indexer = getIndexer();
|
|
10413
|
+
}
|
|
8773
10414
|
await indexer.clearIndex();
|
|
8774
10415
|
}
|
|
8775
10416
|
const stats = await indexer.index((progress) => {
|
|
@@ -8950,8 +10591,8 @@ var add_knowledge_base = tool({
|
|
|
8950
10591
|
},
|
|
8951
10592
|
async execute(args) {
|
|
8952
10593
|
const inputPath = args.path.trim();
|
|
8953
|
-
const resolvedPath =
|
|
8954
|
-
if (!
|
|
10594
|
+
const resolvedPath = path9.isAbsolute(inputPath) ? inputPath : path9.resolve(sharedProjectRoot, inputPath);
|
|
10595
|
+
if (!existsSync7(resolvedPath)) {
|
|
8955
10596
|
return `Error: Directory does not exist: ${resolvedPath}`;
|
|
8956
10597
|
}
|
|
8957
10598
|
try {
|
|
@@ -8962,11 +10603,11 @@ var add_knowledge_base = tool({
|
|
|
8962
10603
|
} catch (error) {
|
|
8963
10604
|
return `Error: Cannot access directory: ${resolvedPath} - ${error instanceof Error ? error.message : String(error)}`;
|
|
8964
10605
|
}
|
|
8965
|
-
const config =
|
|
10606
|
+
const config = loadEditableConfig();
|
|
8966
10607
|
const knowledgeBases = Array.isArray(config.knowledgeBases) ? config.knowledgeBases : [];
|
|
8967
|
-
const normalizedPath =
|
|
10608
|
+
const normalizedPath = path9.normalize(resolvedPath);
|
|
8968
10609
|
const alreadyExists = knowledgeBases.some(
|
|
8969
|
-
(kb) =>
|
|
10610
|
+
(kb) => path9.normalize(path9.isAbsolute(kb) ? kb : path9.resolve(sharedProjectRoot, kb)) === normalizedPath
|
|
8970
10611
|
);
|
|
8971
10612
|
if (alreadyExists) {
|
|
8972
10613
|
return `Knowledge base already configured: ${resolvedPath}`;
|
|
@@ -8990,7 +10631,7 @@ var list_knowledge_bases = tool({
|
|
|
8990
10631
|
description: "List all configured knowledge base folders that are indexed alongside the main project.",
|
|
8991
10632
|
args: {},
|
|
8992
10633
|
async execute() {
|
|
8993
|
-
const config =
|
|
10634
|
+
const config = loadRuntimeConfig();
|
|
8994
10635
|
const knowledgeBases = Array.isArray(config.knowledgeBases) ? config.knowledgeBases : [];
|
|
8995
10636
|
if (knowledgeBases.length === 0) {
|
|
8996
10637
|
return "No knowledge bases configured. Use add_knowledge_base to add folders.";
|
|
@@ -9000,8 +10641,8 @@ var list_knowledge_bases = tool({
|
|
|
9000
10641
|
`;
|
|
9001
10642
|
for (let i = 0; i < knowledgeBases.length; i++) {
|
|
9002
10643
|
const kb = knowledgeBases[i];
|
|
9003
|
-
const resolvedPath =
|
|
9004
|
-
const exists =
|
|
10644
|
+
const resolvedPath = path9.isAbsolute(kb) ? kb : path9.resolve(sharedProjectRoot, kb);
|
|
10645
|
+
const exists = existsSync7(resolvedPath);
|
|
9005
10646
|
result += `[${i + 1}] ${kb}
|
|
9006
10647
|
`;
|
|
9007
10648
|
result += ` Resolved: ${resolvedPath}
|
|
@@ -9029,14 +10670,14 @@ var remove_knowledge_base = tool({
|
|
|
9029
10670
|
},
|
|
9030
10671
|
async execute(args) {
|
|
9031
10672
|
const inputPath = args.path.trim();
|
|
9032
|
-
const config =
|
|
10673
|
+
const config = loadEditableConfig();
|
|
9033
10674
|
const knowledgeBases = Array.isArray(config.knowledgeBases) ? config.knowledgeBases : [];
|
|
9034
10675
|
if (knowledgeBases.length === 0) {
|
|
9035
10676
|
return "No knowledge bases configured.";
|
|
9036
10677
|
}
|
|
9037
|
-
const normalizedInput =
|
|
10678
|
+
const normalizedInput = path9.normalize(inputPath);
|
|
9038
10679
|
const index = knowledgeBases.findIndex(
|
|
9039
|
-
(kb) =>
|
|
10680
|
+
(kb) => path9.normalize(kb) === normalizedInput || path9.normalize(path9.isAbsolute(kb) ? kb : path9.resolve(sharedProjectRoot, kb)) === normalizedInput
|
|
9040
10681
|
);
|
|
9041
10682
|
if (index === -1) {
|
|
9042
10683
|
let result2 = `Knowledge base not found: ${inputPath}
|
|
@@ -9068,8 +10709,8 @@ Run /index to rebuild the index without the removed knowledge base.`;
|
|
|
9068
10709
|
});
|
|
9069
10710
|
|
|
9070
10711
|
// src/commands/loader.ts
|
|
9071
|
-
import { existsSync as
|
|
9072
|
-
import * as
|
|
10712
|
+
import { existsSync as existsSync8, readdirSync as readdirSync2, readFileSync as readFileSync6 } from "fs";
|
|
10713
|
+
import * as path10 from "path";
|
|
9073
10714
|
function parseFrontmatter(content) {
|
|
9074
10715
|
const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
|
|
9075
10716
|
const match = content.match(frontmatterRegex);
|
|
@@ -9090,15 +10731,15 @@ function parseFrontmatter(content) {
|
|
|
9090
10731
|
}
|
|
9091
10732
|
function loadCommandsFromDirectory(commandsDir) {
|
|
9092
10733
|
const commands = /* @__PURE__ */ new Map();
|
|
9093
|
-
if (!
|
|
10734
|
+
if (!existsSync8(commandsDir)) {
|
|
9094
10735
|
return commands;
|
|
9095
10736
|
}
|
|
9096
10737
|
const files = readdirSync2(commandsDir).filter((f) => f.endsWith(".md"));
|
|
9097
10738
|
for (const file of files) {
|
|
9098
|
-
const filePath =
|
|
10739
|
+
const filePath = path10.join(commandsDir, file);
|
|
9099
10740
|
const content = readFileSync6(filePath, "utf-8");
|
|
9100
10741
|
const { frontmatter, body } = parseFrontmatter(content);
|
|
9101
|
-
const name =
|
|
10742
|
+
const name = path10.basename(file, ".md");
|
|
9102
10743
|
const description = frontmatter.description || `Run the ${name} command`;
|
|
9103
10744
|
commands.set(name, {
|
|
9104
10745
|
description,
|
|
@@ -9385,9 +11026,9 @@ function replaceActiveWatcher(nextWatcher) {
|
|
|
9385
11026
|
function getCommandsDir() {
|
|
9386
11027
|
let currentDir = process.cwd();
|
|
9387
11028
|
if (typeof import.meta !== "undefined" && import.meta.url) {
|
|
9388
|
-
currentDir =
|
|
11029
|
+
currentDir = path11.dirname(fileURLToPath2(import.meta.url));
|
|
9389
11030
|
}
|
|
9390
|
-
return
|
|
11031
|
+
return path11.join(currentDir, "..", "commands");
|
|
9391
11032
|
}
|
|
9392
11033
|
var plugin = async ({ directory }) => {
|
|
9393
11034
|
try {
|