opencode-codebase-index 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -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 +2422 -730
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2415 -723
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +2351 -711
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2340 -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.cjs
CHANGED
|
@@ -329,7 +329,7 @@ var require_ignore = __commonJS({
|
|
|
329
329
|
// path matching.
|
|
330
330
|
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
331
331
|
// @returns {TestResult} true if a file is ignored
|
|
332
|
-
test(
|
|
332
|
+
test(path12, checkUnignored, mode) {
|
|
333
333
|
let ignored = false;
|
|
334
334
|
let unignored = false;
|
|
335
335
|
let matchedRule;
|
|
@@ -338,7 +338,7 @@ var require_ignore = __commonJS({
|
|
|
338
338
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
339
339
|
return;
|
|
340
340
|
}
|
|
341
|
-
const matched = rule[mode].test(
|
|
341
|
+
const matched = rule[mode].test(path12);
|
|
342
342
|
if (!matched) {
|
|
343
343
|
return;
|
|
344
344
|
}
|
|
@@ -359,17 +359,17 @@ var require_ignore = __commonJS({
|
|
|
359
359
|
var throwError = (message, Ctor) => {
|
|
360
360
|
throw new Ctor(message);
|
|
361
361
|
};
|
|
362
|
-
var checkPath = (
|
|
363
|
-
if (!isString(
|
|
362
|
+
var checkPath = (path12, originalPath, doThrow) => {
|
|
363
|
+
if (!isString(path12)) {
|
|
364
364
|
return doThrow(
|
|
365
365
|
`path must be a string, but got \`${originalPath}\``,
|
|
366
366
|
TypeError
|
|
367
367
|
);
|
|
368
368
|
}
|
|
369
|
-
if (!
|
|
369
|
+
if (!path12) {
|
|
370
370
|
return doThrow(`path must not be empty`, TypeError);
|
|
371
371
|
}
|
|
372
|
-
if (checkPath.isNotRelative(
|
|
372
|
+
if (checkPath.isNotRelative(path12)) {
|
|
373
373
|
const r = "`path.relative()`d";
|
|
374
374
|
return doThrow(
|
|
375
375
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -378,7 +378,7 @@ var require_ignore = __commonJS({
|
|
|
378
378
|
}
|
|
379
379
|
return true;
|
|
380
380
|
};
|
|
381
|
-
var isNotRelative = (
|
|
381
|
+
var isNotRelative = (path12) => REGEX_TEST_INVALID_PATH.test(path12);
|
|
382
382
|
checkPath.isNotRelative = isNotRelative;
|
|
383
383
|
checkPath.convert = (p) => p;
|
|
384
384
|
var Ignore2 = class {
|
|
@@ -408,19 +408,19 @@ var require_ignore = __commonJS({
|
|
|
408
408
|
}
|
|
409
409
|
// @returns {TestResult}
|
|
410
410
|
_test(originalPath, cache, checkUnignored, slices) {
|
|
411
|
-
const
|
|
411
|
+
const path12 = originalPath && checkPath.convert(originalPath);
|
|
412
412
|
checkPath(
|
|
413
|
-
|
|
413
|
+
path12,
|
|
414
414
|
originalPath,
|
|
415
415
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
416
416
|
);
|
|
417
|
-
return this._t(
|
|
417
|
+
return this._t(path12, cache, checkUnignored, slices);
|
|
418
418
|
}
|
|
419
|
-
checkIgnore(
|
|
420
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
421
|
-
return this.test(
|
|
419
|
+
checkIgnore(path12) {
|
|
420
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path12)) {
|
|
421
|
+
return this.test(path12);
|
|
422
422
|
}
|
|
423
|
-
const slices =
|
|
423
|
+
const slices = path12.split(SLASH2).filter(Boolean);
|
|
424
424
|
slices.pop();
|
|
425
425
|
if (slices.length) {
|
|
426
426
|
const parent = this._t(
|
|
@@ -433,18 +433,18 @@ var require_ignore = __commonJS({
|
|
|
433
433
|
return parent;
|
|
434
434
|
}
|
|
435
435
|
}
|
|
436
|
-
return this._rules.test(
|
|
436
|
+
return this._rules.test(path12, false, MODE_CHECK_IGNORE);
|
|
437
437
|
}
|
|
438
|
-
_t(
|
|
439
|
-
if (
|
|
440
|
-
return cache[
|
|
438
|
+
_t(path12, cache, checkUnignored, slices) {
|
|
439
|
+
if (path12 in cache) {
|
|
440
|
+
return cache[path12];
|
|
441
441
|
}
|
|
442
442
|
if (!slices) {
|
|
443
|
-
slices =
|
|
443
|
+
slices = path12.split(SLASH2).filter(Boolean);
|
|
444
444
|
}
|
|
445
445
|
slices.pop();
|
|
446
446
|
if (!slices.length) {
|
|
447
|
-
return cache[
|
|
447
|
+
return cache[path12] = this._rules.test(path12, checkUnignored, MODE_IGNORE);
|
|
448
448
|
}
|
|
449
449
|
const parent = this._t(
|
|
450
450
|
slices.join(SLASH2) + SLASH2,
|
|
@@ -452,29 +452,29 @@ var require_ignore = __commonJS({
|
|
|
452
452
|
checkUnignored,
|
|
453
453
|
slices
|
|
454
454
|
);
|
|
455
|
-
return cache[
|
|
455
|
+
return cache[path12] = parent.ignored ? parent : this._rules.test(path12, checkUnignored, MODE_IGNORE);
|
|
456
456
|
}
|
|
457
|
-
ignores(
|
|
458
|
-
return this._test(
|
|
457
|
+
ignores(path12) {
|
|
458
|
+
return this._test(path12, this._ignoreCache, false).ignored;
|
|
459
459
|
}
|
|
460
460
|
createFilter() {
|
|
461
|
-
return (
|
|
461
|
+
return (path12) => !this.ignores(path12);
|
|
462
462
|
}
|
|
463
463
|
filter(paths) {
|
|
464
464
|
return makeArray(paths).filter(this.createFilter());
|
|
465
465
|
}
|
|
466
466
|
// @returns {TestResult}
|
|
467
|
-
test(
|
|
468
|
-
return this._test(
|
|
467
|
+
test(path12) {
|
|
468
|
+
return this._test(path12, this._testCache, true);
|
|
469
469
|
}
|
|
470
470
|
};
|
|
471
471
|
var factory = (options) => new Ignore2(options);
|
|
472
|
-
var isPathValid = (
|
|
472
|
+
var isPathValid = (path12) => checkPath(path12 && checkPath.convert(path12), path12, RETURN_FALSE);
|
|
473
473
|
var setupWindows = () => {
|
|
474
474
|
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
475
475
|
checkPath.convert = makePosix;
|
|
476
476
|
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
477
|
-
checkPath.isNotRelative = (
|
|
477
|
+
checkPath.isNotRelative = (path12) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path12) || isNotRelative(path12);
|
|
478
478
|
};
|
|
479
479
|
if (
|
|
480
480
|
// Detect `process` so that it can run in browsers.
|
|
@@ -657,7 +657,7 @@ __export(index_exports, {
|
|
|
657
657
|
default: () => index_default
|
|
658
658
|
});
|
|
659
659
|
module.exports = __toCommonJS(index_exports);
|
|
660
|
-
var
|
|
660
|
+
var path11 = __toESM(require("path"), 1);
|
|
661
661
|
var import_url2 = require("url");
|
|
662
662
|
|
|
663
663
|
// src/config/constants.ts
|
|
@@ -667,12 +667,14 @@ var DEFAULT_INCLUDE = [
|
|
|
667
667
|
"**/*.{go,rs,java,kt,scala}",
|
|
668
668
|
"**/*.{c,cpp,cc,h,hpp}",
|
|
669
669
|
"**/*.{rb,php,inc,swift}",
|
|
670
|
+
"**/*.{cls,trigger}",
|
|
670
671
|
"**/*.{vue,svelte,astro}",
|
|
671
672
|
"**/*.{sql,graphql,proto}",
|
|
672
673
|
"**/*.{yaml,yml,toml}",
|
|
673
674
|
"**/*.{md,mdx}",
|
|
674
675
|
"**/*.{sh,bash,zsh}",
|
|
675
|
-
"**/*.{txt,html,htm}"
|
|
676
|
+
"**/*.{txt,html,htm}",
|
|
677
|
+
"**/*.zig"
|
|
676
678
|
];
|
|
677
679
|
var DEFAULT_EXCLUDE = [
|
|
678
680
|
"**/node_modules/**",
|
|
@@ -737,7 +739,7 @@ var EMBEDDING_MODELS = {
|
|
|
737
739
|
provider: "ollama",
|
|
738
740
|
model: "nomic-embed-text",
|
|
739
741
|
dimensions: 768,
|
|
740
|
-
maxTokens:
|
|
742
|
+
maxTokens: 2048,
|
|
741
743
|
costPer1MTokens: 0
|
|
742
744
|
},
|
|
743
745
|
"mxbai-embed-large": {
|
|
@@ -761,9 +763,15 @@ var EMBEDDING_MODELS = {
|
|
|
761
763
|
var DEFAULT_PROVIDER_MODELS = {
|
|
762
764
|
"github-copilot": "text-embedding-3-small",
|
|
763
765
|
"openai": "text-embedding-3-small",
|
|
764
|
-
"google": "
|
|
766
|
+
"google": "gemini-embedding-001",
|
|
765
767
|
"ollama": "nomic-embed-text"
|
|
766
768
|
};
|
|
769
|
+
var AUTO_DETECT_PROVIDER_ORDER = [
|
|
770
|
+
"ollama",
|
|
771
|
+
"github-copilot",
|
|
772
|
+
"openai",
|
|
773
|
+
"google"
|
|
774
|
+
];
|
|
767
775
|
|
|
768
776
|
// src/config/env-substitution.ts
|
|
769
777
|
var ENV_REFERENCE_PATTERN = /^\{env:([A-Z_][A-Z0-9_]*)\}$/;
|
|
@@ -1022,26 +1030,261 @@ function getDefaultModelForProvider(provider) {
|
|
|
1022
1030
|
return models[providerDefault];
|
|
1023
1031
|
}
|
|
1024
1032
|
var availableProviders = Object.keys(EMBEDDING_MODELS);
|
|
1033
|
+
var autoDetectProviders = AUTO_DETECT_PROVIDER_ORDER.filter(
|
|
1034
|
+
(provider) => provider in EMBEDDING_MODELS
|
|
1035
|
+
);
|
|
1025
1036
|
|
|
1026
1037
|
// src/config/merger.ts
|
|
1038
|
+
var import_fs3 = require("fs");
|
|
1039
|
+
var os2 = __toESM(require("os"), 1);
|
|
1040
|
+
var path3 = __toESM(require("path"), 1);
|
|
1041
|
+
|
|
1042
|
+
// src/config/paths.ts
|
|
1043
|
+
var import_fs2 = require("fs");
|
|
1044
|
+
var os = __toESM(require("os"), 1);
|
|
1045
|
+
var path2 = __toESM(require("path"), 1);
|
|
1046
|
+
|
|
1047
|
+
// src/git/index.ts
|
|
1027
1048
|
var import_fs = require("fs");
|
|
1028
1049
|
var path = __toESM(require("path"), 1);
|
|
1029
|
-
|
|
1050
|
+
function readPackedRefs(gitDir) {
|
|
1051
|
+
const packedRefsPath = path.join(gitDir, "packed-refs");
|
|
1052
|
+
if (!(0, import_fs.existsSync)(packedRefsPath)) {
|
|
1053
|
+
return [];
|
|
1054
|
+
}
|
|
1055
|
+
try {
|
|
1056
|
+
return (0, import_fs.readFileSync)(packedRefsPath, "utf-8").split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#") && !line.startsWith("^"));
|
|
1057
|
+
} catch {
|
|
1058
|
+
return [];
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
function resolveCommonGitDir(gitDir) {
|
|
1062
|
+
const commonDirPath = path.join(gitDir, "commondir");
|
|
1063
|
+
if (!(0, import_fs.existsSync)(commonDirPath)) {
|
|
1064
|
+
return gitDir;
|
|
1065
|
+
}
|
|
1066
|
+
try {
|
|
1067
|
+
const raw = (0, import_fs.readFileSync)(commonDirPath, "utf-8").trim();
|
|
1068
|
+
if (!raw) {
|
|
1069
|
+
return gitDir;
|
|
1070
|
+
}
|
|
1071
|
+
const resolved = path.isAbsolute(raw) ? raw : path.resolve(gitDir, raw);
|
|
1072
|
+
if ((0, import_fs.existsSync)(resolved)) {
|
|
1073
|
+
return resolved;
|
|
1074
|
+
}
|
|
1075
|
+
} catch {
|
|
1076
|
+
return gitDir;
|
|
1077
|
+
}
|
|
1078
|
+
return gitDir;
|
|
1079
|
+
}
|
|
1080
|
+
function resolveWorktreeMainRepoRoot(repoRoot) {
|
|
1081
|
+
const gitDir = resolveGitDir(repoRoot);
|
|
1082
|
+
if (!gitDir) {
|
|
1083
|
+
return null;
|
|
1084
|
+
}
|
|
1085
|
+
const commonGitDir = resolveCommonGitDir(gitDir);
|
|
1086
|
+
if (commonGitDir === gitDir || path.basename(commonGitDir) !== ".git") {
|
|
1087
|
+
return null;
|
|
1088
|
+
}
|
|
1089
|
+
const mainRepoRoot = path.dirname(commonGitDir);
|
|
1090
|
+
if (!(0, import_fs.existsSync)(mainRepoRoot)) {
|
|
1091
|
+
return null;
|
|
1092
|
+
}
|
|
1093
|
+
return path.resolve(mainRepoRoot) === path.resolve(repoRoot) ? null : mainRepoRoot;
|
|
1094
|
+
}
|
|
1095
|
+
function resolveGitDir(repoRoot) {
|
|
1096
|
+
const gitPath = path.join(repoRoot, ".git");
|
|
1097
|
+
if (!(0, import_fs.existsSync)(gitPath)) {
|
|
1098
|
+
return null;
|
|
1099
|
+
}
|
|
1100
|
+
try {
|
|
1101
|
+
const stat4 = (0, import_fs.statSync)(gitPath);
|
|
1102
|
+
if (stat4.isDirectory()) {
|
|
1103
|
+
return gitPath;
|
|
1104
|
+
}
|
|
1105
|
+
if (stat4.isFile()) {
|
|
1106
|
+
const content = (0, import_fs.readFileSync)(gitPath, "utf-8").trim();
|
|
1107
|
+
const match = content.match(/^gitdir:\s*(.+)$/);
|
|
1108
|
+
if (match) {
|
|
1109
|
+
const gitdir = match[1];
|
|
1110
|
+
const resolvedPath = path.isAbsolute(gitdir) ? gitdir : path.resolve(repoRoot, gitdir);
|
|
1111
|
+
if ((0, import_fs.existsSync)(resolvedPath)) {
|
|
1112
|
+
return resolvedPath;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
} catch {
|
|
1117
|
+
}
|
|
1118
|
+
return null;
|
|
1119
|
+
}
|
|
1120
|
+
function isGitRepo(dir) {
|
|
1121
|
+
return resolveGitDir(dir) !== null;
|
|
1122
|
+
}
|
|
1123
|
+
function getCurrentBranch(repoRoot) {
|
|
1124
|
+
const gitDir = resolveGitDir(repoRoot);
|
|
1125
|
+
if (!gitDir) {
|
|
1126
|
+
return null;
|
|
1127
|
+
}
|
|
1128
|
+
const headPath = path.join(gitDir, "HEAD");
|
|
1129
|
+
if (!(0, import_fs.existsSync)(headPath)) {
|
|
1130
|
+
return null;
|
|
1131
|
+
}
|
|
1132
|
+
try {
|
|
1133
|
+
const headContent = (0, import_fs.readFileSync)(headPath, "utf-8").trim();
|
|
1134
|
+
const match = headContent.match(/^ref: refs\/heads\/(.+)$/);
|
|
1135
|
+
if (match) {
|
|
1136
|
+
return match[1];
|
|
1137
|
+
}
|
|
1138
|
+
if (/^[0-9a-f]{40}$/i.test(headContent)) {
|
|
1139
|
+
return headContent.slice(0, 7);
|
|
1140
|
+
}
|
|
1141
|
+
return null;
|
|
1142
|
+
} catch {
|
|
1143
|
+
return null;
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
function getBaseBranch(repoRoot) {
|
|
1147
|
+
const gitDir = resolveGitDir(repoRoot);
|
|
1148
|
+
const refStoreDir = gitDir ? resolveCommonGitDir(gitDir) : null;
|
|
1149
|
+
const candidates = ["main", "master", "develop", "trunk"];
|
|
1150
|
+
if (refStoreDir) {
|
|
1151
|
+
for (const candidate of candidates) {
|
|
1152
|
+
const refPath = path.join(refStoreDir, "refs", "heads", candidate);
|
|
1153
|
+
if ((0, import_fs.existsSync)(refPath)) {
|
|
1154
|
+
return candidate;
|
|
1155
|
+
}
|
|
1156
|
+
const packedRefs = readPackedRefs(refStoreDir);
|
|
1157
|
+
if (packedRefs.some((line) => line.endsWith(` refs/heads/${candidate}`))) {
|
|
1158
|
+
return candidate;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
return getCurrentBranch(repoRoot) ?? "main";
|
|
1163
|
+
}
|
|
1164
|
+
function getBranchOrDefault(repoRoot) {
|
|
1165
|
+
if (!isGitRepo(repoRoot)) {
|
|
1166
|
+
return "default";
|
|
1167
|
+
}
|
|
1168
|
+
return getCurrentBranch(repoRoot) ?? "default";
|
|
1169
|
+
}
|
|
1170
|
+
function getHeadPath(repoRoot) {
|
|
1171
|
+
const gitDir = resolveGitDir(repoRoot);
|
|
1172
|
+
if (gitDir) {
|
|
1173
|
+
return path.join(gitDir, "HEAD");
|
|
1174
|
+
}
|
|
1175
|
+
return path.join(repoRoot, ".git", "HEAD");
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
// src/config/paths.ts
|
|
1179
|
+
var PROJECT_CONFIG_RELATIVE_PATH = path2.join(".opencode", "codebase-index.json");
|
|
1180
|
+
var PROJECT_INDEX_RELATIVE_PATH = path2.join(".opencode", "index");
|
|
1181
|
+
function resolveWorktreeFallbackPath(projectRoot, relativePath) {
|
|
1182
|
+
const mainRepoRoot = resolveWorktreeMainRepoRoot(projectRoot);
|
|
1183
|
+
if (!mainRepoRoot) {
|
|
1184
|
+
return null;
|
|
1185
|
+
}
|
|
1186
|
+
const fallbackPath = path2.join(mainRepoRoot, relativePath);
|
|
1187
|
+
return (0, import_fs2.existsSync)(fallbackPath) ? fallbackPath : null;
|
|
1188
|
+
}
|
|
1189
|
+
function hasProjectConfig(projectRoot) {
|
|
1190
|
+
return (0, import_fs2.existsSync)(path2.join(projectRoot, PROJECT_CONFIG_RELATIVE_PATH));
|
|
1191
|
+
}
|
|
1192
|
+
function getGlobalIndexPath() {
|
|
1193
|
+
return path2.join(os.homedir(), ".opencode", "global-index");
|
|
1194
|
+
}
|
|
1195
|
+
function resolveProjectConfigPath(projectRoot) {
|
|
1196
|
+
const localConfigPath = path2.join(projectRoot, PROJECT_CONFIG_RELATIVE_PATH);
|
|
1197
|
+
if ((0, import_fs2.existsSync)(localConfigPath)) {
|
|
1198
|
+
return localConfigPath;
|
|
1199
|
+
}
|
|
1200
|
+
return resolveWorktreeFallbackPath(projectRoot, PROJECT_CONFIG_RELATIVE_PATH) ?? localConfigPath;
|
|
1201
|
+
}
|
|
1202
|
+
function resolveWritableProjectConfigPath(projectRoot) {
|
|
1203
|
+
return path2.join(projectRoot, PROJECT_CONFIG_RELATIVE_PATH);
|
|
1204
|
+
}
|
|
1205
|
+
function resolveProjectIndexPath(projectRoot, scope) {
|
|
1206
|
+
if (scope === "global") {
|
|
1207
|
+
return getGlobalIndexPath();
|
|
1208
|
+
}
|
|
1209
|
+
const localIndexPath = path2.join(projectRoot, PROJECT_INDEX_RELATIVE_PATH);
|
|
1210
|
+
if ((0, import_fs2.existsSync)(localIndexPath)) {
|
|
1211
|
+
return localIndexPath;
|
|
1212
|
+
}
|
|
1213
|
+
if (hasProjectConfig(projectRoot)) {
|
|
1214
|
+
return localIndexPath;
|
|
1215
|
+
}
|
|
1216
|
+
return resolveWorktreeFallbackPath(projectRoot, PROJECT_INDEX_RELATIVE_PATH) ?? localIndexPath;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
// src/config/merger.ts
|
|
1030
1220
|
function loadJsonFile(filePath) {
|
|
1031
1221
|
try {
|
|
1032
|
-
if ((0,
|
|
1033
|
-
const content = (0,
|
|
1222
|
+
if ((0, import_fs3.existsSync)(filePath)) {
|
|
1223
|
+
const content = (0, import_fs3.readFileSync)(filePath, "utf-8");
|
|
1034
1224
|
return JSON.parse(content);
|
|
1035
1225
|
}
|
|
1036
1226
|
} catch {
|
|
1037
1227
|
}
|
|
1038
1228
|
return null;
|
|
1039
1229
|
}
|
|
1230
|
+
function normalizeRelativeConfigPath(candidate) {
|
|
1231
|
+
return candidate.replace(/\\/g, "/");
|
|
1232
|
+
}
|
|
1233
|
+
function isWithinRoot(rootDir, targetPath) {
|
|
1234
|
+
const relativePath = path3.relative(rootDir, targetPath);
|
|
1235
|
+
return relativePath === "" || !relativePath.startsWith("..") && !path3.isAbsolute(relativePath);
|
|
1236
|
+
}
|
|
1237
|
+
function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
|
|
1238
|
+
if (!Array.isArray(values)) {
|
|
1239
|
+
return [];
|
|
1240
|
+
}
|
|
1241
|
+
return values.filter((value) => typeof value === "string").map((value) => {
|
|
1242
|
+
const trimmed = value.trim();
|
|
1243
|
+
if (!trimmed) {
|
|
1244
|
+
return trimmed;
|
|
1245
|
+
}
|
|
1246
|
+
if (path3.isAbsolute(trimmed)) {
|
|
1247
|
+
if (isWithinRoot(sourceRoot, trimmed)) {
|
|
1248
|
+
return normalizeRelativeConfigPath(path3.normalize(path3.relative(sourceRoot, trimmed) || "."));
|
|
1249
|
+
}
|
|
1250
|
+
return path3.normalize(trimmed);
|
|
1251
|
+
}
|
|
1252
|
+
const resolvedFromSource = path3.resolve(sourceRoot, trimmed);
|
|
1253
|
+
if (isWithinRoot(sourceRoot, resolvedFromSource)) {
|
|
1254
|
+
return normalizeRelativeConfigPath(path3.normalize(trimmed));
|
|
1255
|
+
}
|
|
1256
|
+
return normalizeRelativeConfigPath(path3.normalize(path3.relative(targetRoot, resolvedFromSource)));
|
|
1257
|
+
}).filter(Boolean);
|
|
1258
|
+
}
|
|
1259
|
+
function materializeLocalProjectConfig(projectRoot, config) {
|
|
1260
|
+
const localConfigPath = path3.join(projectRoot, ".opencode", "codebase-index.json");
|
|
1261
|
+
(0, import_fs3.mkdirSync)(path3.dirname(localConfigPath), { recursive: true });
|
|
1262
|
+
(0, import_fs3.writeFileSync)(localConfigPath, JSON.stringify(config, null, 2), "utf-8");
|
|
1263
|
+
return localConfigPath;
|
|
1264
|
+
}
|
|
1265
|
+
function loadProjectConfigLayer(projectRoot) {
|
|
1266
|
+
const projectConfigPath = resolveProjectConfigPath(projectRoot);
|
|
1267
|
+
const projectConfig = loadJsonFile(projectConfigPath);
|
|
1268
|
+
if (!projectConfig) {
|
|
1269
|
+
return {};
|
|
1270
|
+
}
|
|
1271
|
+
const normalizedConfig = { ...projectConfig };
|
|
1272
|
+
const projectConfigBaseDir = path3.dirname(path3.dirname(projectConfigPath));
|
|
1273
|
+
if (Array.isArray(normalizedConfig.knowledgeBases)) {
|
|
1274
|
+
normalizedConfig.knowledgeBases = resolveInheritedKnowledgeBaseEntries(
|
|
1275
|
+
normalizedConfig.knowledgeBases,
|
|
1276
|
+
projectConfigBaseDir,
|
|
1277
|
+
projectRoot
|
|
1278
|
+
);
|
|
1279
|
+
}
|
|
1280
|
+
return normalizedConfig;
|
|
1281
|
+
}
|
|
1040
1282
|
function loadMergedConfig(projectRoot) {
|
|
1041
|
-
const globalConfigPath =
|
|
1283
|
+
const globalConfigPath = os2.homedir() + "/.config/opencode/codebase-index.json";
|
|
1042
1284
|
const globalConfig = loadJsonFile(globalConfigPath);
|
|
1043
|
-
const projectConfigPath =
|
|
1285
|
+
const projectConfigPath = resolveProjectConfigPath(projectRoot);
|
|
1044
1286
|
const projectConfig = loadJsonFile(projectConfigPath);
|
|
1287
|
+
const normalizedProjectConfig = loadProjectConfigLayer(projectRoot);
|
|
1045
1288
|
if (!globalConfig && !projectConfig) {
|
|
1046
1289
|
return {};
|
|
1047
1290
|
}
|
|
@@ -1049,56 +1292,56 @@ function loadMergedConfig(projectRoot) {
|
|
|
1049
1292
|
return globalConfig;
|
|
1050
1293
|
}
|
|
1051
1294
|
if (!globalConfig && projectConfig) {
|
|
1052
|
-
return
|
|
1295
|
+
return normalizedProjectConfig;
|
|
1053
1296
|
}
|
|
1054
1297
|
const merged = { ...globalConfig };
|
|
1055
|
-
if (projectConfig && "embeddingProvider" in
|
|
1056
|
-
merged.embeddingProvider =
|
|
1298
|
+
if (projectConfig && "embeddingProvider" in normalizedProjectConfig) {
|
|
1299
|
+
merged.embeddingProvider = normalizedProjectConfig.embeddingProvider;
|
|
1057
1300
|
} else if (globalConfig && globalConfig.embeddingProvider) {
|
|
1058
1301
|
merged.embeddingProvider = globalConfig.embeddingProvider;
|
|
1059
1302
|
}
|
|
1060
|
-
if (projectConfig && "customProvider" in
|
|
1061
|
-
merged.customProvider =
|
|
1303
|
+
if (projectConfig && "customProvider" in normalizedProjectConfig) {
|
|
1304
|
+
merged.customProvider = normalizedProjectConfig.customProvider;
|
|
1062
1305
|
} else if (globalConfig && globalConfig.customProvider) {
|
|
1063
1306
|
merged.customProvider = globalConfig.customProvider;
|
|
1064
1307
|
}
|
|
1065
|
-
if (projectConfig && "embeddingModel" in
|
|
1066
|
-
merged.embeddingModel =
|
|
1308
|
+
if (projectConfig && "embeddingModel" in normalizedProjectConfig) {
|
|
1309
|
+
merged.embeddingModel = normalizedProjectConfig.embeddingModel;
|
|
1067
1310
|
} else if (globalConfig && globalConfig.embeddingModel) {
|
|
1068
1311
|
merged.embeddingModel = globalConfig.embeddingModel;
|
|
1069
1312
|
}
|
|
1070
|
-
if (projectConfig && "reranker" in
|
|
1071
|
-
merged.reranker =
|
|
1313
|
+
if (projectConfig && "reranker" in normalizedProjectConfig) {
|
|
1314
|
+
merged.reranker = normalizedProjectConfig.reranker;
|
|
1072
1315
|
} else if (globalConfig && globalConfig.reranker) {
|
|
1073
1316
|
merged.reranker = globalConfig.reranker;
|
|
1074
1317
|
}
|
|
1075
|
-
if (projectConfig && "include" in
|
|
1076
|
-
merged.include =
|
|
1318
|
+
if (projectConfig && "include" in normalizedProjectConfig) {
|
|
1319
|
+
merged.include = normalizedProjectConfig.include;
|
|
1077
1320
|
} else if (globalConfig && globalConfig.include) {
|
|
1078
1321
|
merged.include = globalConfig.include;
|
|
1079
1322
|
}
|
|
1080
|
-
if (projectConfig && "exclude" in
|
|
1081
|
-
merged.exclude =
|
|
1323
|
+
if (projectConfig && "exclude" in normalizedProjectConfig) {
|
|
1324
|
+
merged.exclude = normalizedProjectConfig.exclude;
|
|
1082
1325
|
} else if (globalConfig && globalConfig.exclude) {
|
|
1083
1326
|
merged.exclude = globalConfig.exclude;
|
|
1084
1327
|
}
|
|
1085
|
-
if (projectConfig && "indexing" in
|
|
1086
|
-
merged.indexing =
|
|
1328
|
+
if (projectConfig && "indexing" in normalizedProjectConfig) {
|
|
1329
|
+
merged.indexing = normalizedProjectConfig.indexing;
|
|
1087
1330
|
} else if (globalConfig && globalConfig.indexing) {
|
|
1088
1331
|
merged.indexing = globalConfig.indexing;
|
|
1089
1332
|
}
|
|
1090
|
-
if (projectConfig && "search" in
|
|
1091
|
-
merged.search =
|
|
1333
|
+
if (projectConfig && "search" in normalizedProjectConfig) {
|
|
1334
|
+
merged.search = normalizedProjectConfig.search;
|
|
1092
1335
|
} else if (globalConfig && globalConfig.search) {
|
|
1093
1336
|
merged.search = globalConfig.search;
|
|
1094
1337
|
}
|
|
1095
|
-
if (projectConfig && "debug" in
|
|
1096
|
-
merged.debug =
|
|
1338
|
+
if (projectConfig && "debug" in normalizedProjectConfig) {
|
|
1339
|
+
merged.debug = normalizedProjectConfig.debug;
|
|
1097
1340
|
} else if (globalConfig && globalConfig.debug) {
|
|
1098
1341
|
merged.debug = globalConfig.debug;
|
|
1099
1342
|
}
|
|
1100
|
-
if (projectConfig && "scope" in
|
|
1101
|
-
merged.scope =
|
|
1343
|
+
if (projectConfig && "scope" in normalizedProjectConfig) {
|
|
1344
|
+
merged.scope = normalizedProjectConfig.scope;
|
|
1102
1345
|
} else if (globalConfig && "scope" in globalConfig) {
|
|
1103
1346
|
merged.scope = globalConfig.scope;
|
|
1104
1347
|
}
|
|
@@ -1107,11 +1350,11 @@ function loadMergedConfig(projectRoot) {
|
|
|
1107
1350
|
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") {
|
|
1108
1351
|
continue;
|
|
1109
1352
|
}
|
|
1110
|
-
merged[key] =
|
|
1353
|
+
merged[key] = normalizedProjectConfig[key];
|
|
1111
1354
|
}
|
|
1112
1355
|
}
|
|
1113
1356
|
const globalKbs = globalConfig && Array.isArray(globalConfig.knowledgeBases) ? globalConfig.knowledgeBases : [];
|
|
1114
|
-
const projectKbs = projectConfig
|
|
1357
|
+
const projectKbs = projectConfig ? Array.isArray(normalizedProjectConfig.knowledgeBases) ? normalizedProjectConfig.knowledgeBases : [] : [];
|
|
1115
1358
|
const allKbs = [...globalKbs, ...projectKbs];
|
|
1116
1359
|
const uniqueKbs = [...new Set(allKbs.map((p) => String(p).trim()))];
|
|
1117
1360
|
merged.knowledgeBases = uniqueKbs;
|
|
@@ -1213,7 +1456,7 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
1213
1456
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
1214
1457
|
const statMethod = opts.lstat ? import_promises.lstat : import_promises.stat;
|
|
1215
1458
|
if (wantBigintFsStats) {
|
|
1216
|
-
this._stat = (
|
|
1459
|
+
this._stat = (path12) => statMethod(path12, { bigint: true });
|
|
1217
1460
|
} else {
|
|
1218
1461
|
this._stat = statMethod;
|
|
1219
1462
|
}
|
|
@@ -1238,8 +1481,8 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
1238
1481
|
const par = this.parent;
|
|
1239
1482
|
const fil = par && par.files;
|
|
1240
1483
|
if (fil && fil.length > 0) {
|
|
1241
|
-
const { path:
|
|
1242
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
1484
|
+
const { path: path12, depth } = par;
|
|
1485
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path12));
|
|
1243
1486
|
const awaited = await Promise.all(slice);
|
|
1244
1487
|
for (const entry of awaited) {
|
|
1245
1488
|
if (!entry)
|
|
@@ -1279,21 +1522,21 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
1279
1522
|
this.reading = false;
|
|
1280
1523
|
}
|
|
1281
1524
|
}
|
|
1282
|
-
async _exploreDir(
|
|
1525
|
+
async _exploreDir(path12, depth) {
|
|
1283
1526
|
let files;
|
|
1284
1527
|
try {
|
|
1285
|
-
files = await (0, import_promises.readdir)(
|
|
1528
|
+
files = await (0, import_promises.readdir)(path12, this._rdOptions);
|
|
1286
1529
|
} catch (error) {
|
|
1287
1530
|
this._onError(error);
|
|
1288
1531
|
}
|
|
1289
|
-
return { files, depth, path:
|
|
1532
|
+
return { files, depth, path: path12 };
|
|
1290
1533
|
}
|
|
1291
|
-
async _formatEntry(dirent,
|
|
1534
|
+
async _formatEntry(dirent, path12) {
|
|
1292
1535
|
let entry;
|
|
1293
|
-
const
|
|
1536
|
+
const basename5 = this._isDirent ? dirent.name : dirent;
|
|
1294
1537
|
try {
|
|
1295
|
-
const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(
|
|
1296
|
-
entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename:
|
|
1538
|
+
const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path12, basename5));
|
|
1539
|
+
entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename5 };
|
|
1297
1540
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
1298
1541
|
} catch (err) {
|
|
1299
1542
|
this._onError(err);
|
|
@@ -1692,16 +1935,16 @@ var delFromSet = (main, prop, item) => {
|
|
|
1692
1935
|
};
|
|
1693
1936
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
1694
1937
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
1695
|
-
function createFsWatchInstance(
|
|
1938
|
+
function createFsWatchInstance(path12, options, listener, errHandler, emitRaw) {
|
|
1696
1939
|
const handleEvent = (rawEvent, evPath) => {
|
|
1697
|
-
listener(
|
|
1698
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
1699
|
-
if (evPath &&
|
|
1700
|
-
fsWatchBroadcast(sp.resolve(
|
|
1940
|
+
listener(path12);
|
|
1941
|
+
emitRaw(rawEvent, evPath, { watchedPath: path12 });
|
|
1942
|
+
if (evPath && path12 !== evPath) {
|
|
1943
|
+
fsWatchBroadcast(sp.resolve(path12, evPath), KEY_LISTENERS, sp.join(path12, evPath));
|
|
1701
1944
|
}
|
|
1702
1945
|
};
|
|
1703
1946
|
try {
|
|
1704
|
-
return (0, import_node_fs.watch)(
|
|
1947
|
+
return (0, import_node_fs.watch)(path12, {
|
|
1705
1948
|
persistent: options.persistent
|
|
1706
1949
|
}, handleEvent);
|
|
1707
1950
|
} catch (error) {
|
|
@@ -1717,12 +1960,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
1717
1960
|
listener(val1, val2, val3);
|
|
1718
1961
|
});
|
|
1719
1962
|
};
|
|
1720
|
-
var setFsWatchListener = (
|
|
1963
|
+
var setFsWatchListener = (path12, fullPath, options, handlers) => {
|
|
1721
1964
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
1722
1965
|
let cont = FsWatchInstances.get(fullPath);
|
|
1723
1966
|
let watcher;
|
|
1724
1967
|
if (!options.persistent) {
|
|
1725
|
-
watcher = createFsWatchInstance(
|
|
1968
|
+
watcher = createFsWatchInstance(path12, options, listener, errHandler, rawEmitter);
|
|
1726
1969
|
if (!watcher)
|
|
1727
1970
|
return;
|
|
1728
1971
|
return watcher.close.bind(watcher);
|
|
@@ -1733,7 +1976,7 @@ var setFsWatchListener = (path11, fullPath, options, handlers) => {
|
|
|
1733
1976
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
1734
1977
|
} else {
|
|
1735
1978
|
watcher = createFsWatchInstance(
|
|
1736
|
-
|
|
1979
|
+
path12,
|
|
1737
1980
|
options,
|
|
1738
1981
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
1739
1982
|
errHandler,
|
|
@@ -1748,7 +1991,7 @@ var setFsWatchListener = (path11, fullPath, options, handlers) => {
|
|
|
1748
1991
|
cont.watcherUnusable = true;
|
|
1749
1992
|
if (isWindows && error.code === "EPERM") {
|
|
1750
1993
|
try {
|
|
1751
|
-
const fd = await (0, import_promises2.open)(
|
|
1994
|
+
const fd = await (0, import_promises2.open)(path12, "r");
|
|
1752
1995
|
await fd.close();
|
|
1753
1996
|
broadcastErr(error);
|
|
1754
1997
|
} catch (err) {
|
|
@@ -1779,7 +2022,7 @@ var setFsWatchListener = (path11, fullPath, options, handlers) => {
|
|
|
1779
2022
|
};
|
|
1780
2023
|
};
|
|
1781
2024
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
1782
|
-
var setFsWatchFileListener = (
|
|
2025
|
+
var setFsWatchFileListener = (path12, fullPath, options, handlers) => {
|
|
1783
2026
|
const { listener, rawEmitter } = handlers;
|
|
1784
2027
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
1785
2028
|
const copts = cont && cont.options;
|
|
@@ -1801,7 +2044,7 @@ var setFsWatchFileListener = (path11, fullPath, options, handlers) => {
|
|
|
1801
2044
|
});
|
|
1802
2045
|
const currmtime = curr.mtimeMs;
|
|
1803
2046
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
1804
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
2047
|
+
foreach(cont.listeners, (listener2) => listener2(path12, curr));
|
|
1805
2048
|
}
|
|
1806
2049
|
})
|
|
1807
2050
|
};
|
|
@@ -1831,13 +2074,13 @@ var NodeFsHandler = class {
|
|
|
1831
2074
|
* @param listener on fs change
|
|
1832
2075
|
* @returns closer for the watcher instance
|
|
1833
2076
|
*/
|
|
1834
|
-
_watchWithNodeFs(
|
|
2077
|
+
_watchWithNodeFs(path12, listener) {
|
|
1835
2078
|
const opts = this.fsw.options;
|
|
1836
|
-
const directory = sp.dirname(
|
|
1837
|
-
const
|
|
2079
|
+
const directory = sp.dirname(path12);
|
|
2080
|
+
const basename5 = sp.basename(path12);
|
|
1838
2081
|
const parent = this.fsw._getWatchedDir(directory);
|
|
1839
|
-
parent.add(
|
|
1840
|
-
const absolutePath = sp.resolve(
|
|
2082
|
+
parent.add(basename5);
|
|
2083
|
+
const absolutePath = sp.resolve(path12);
|
|
1841
2084
|
const options = {
|
|
1842
2085
|
persistent: opts.persistent
|
|
1843
2086
|
};
|
|
@@ -1846,13 +2089,13 @@ var NodeFsHandler = class {
|
|
|
1846
2089
|
let closer;
|
|
1847
2090
|
if (opts.usePolling) {
|
|
1848
2091
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
1849
|
-
options.interval = enableBin && isBinaryPath(
|
|
1850
|
-
closer = setFsWatchFileListener(
|
|
2092
|
+
options.interval = enableBin && isBinaryPath(basename5) ? opts.binaryInterval : opts.interval;
|
|
2093
|
+
closer = setFsWatchFileListener(path12, absolutePath, options, {
|
|
1851
2094
|
listener,
|
|
1852
2095
|
rawEmitter: this.fsw._emitRaw
|
|
1853
2096
|
});
|
|
1854
2097
|
} else {
|
|
1855
|
-
closer = setFsWatchListener(
|
|
2098
|
+
closer = setFsWatchListener(path12, absolutePath, options, {
|
|
1856
2099
|
listener,
|
|
1857
2100
|
errHandler: this._boundHandleError,
|
|
1858
2101
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -1868,13 +2111,13 @@ var NodeFsHandler = class {
|
|
|
1868
2111
|
if (this.fsw.closed) {
|
|
1869
2112
|
return;
|
|
1870
2113
|
}
|
|
1871
|
-
const
|
|
1872
|
-
const
|
|
1873
|
-
const parent = this.fsw._getWatchedDir(
|
|
2114
|
+
const dirname8 = sp.dirname(file);
|
|
2115
|
+
const basename5 = sp.basename(file);
|
|
2116
|
+
const parent = this.fsw._getWatchedDir(dirname8);
|
|
1874
2117
|
let prevStats = stats;
|
|
1875
|
-
if (parent.has(
|
|
2118
|
+
if (parent.has(basename5))
|
|
1876
2119
|
return;
|
|
1877
|
-
const listener = async (
|
|
2120
|
+
const listener = async (path12, newStats) => {
|
|
1878
2121
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
1879
2122
|
return;
|
|
1880
2123
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -1888,18 +2131,18 @@ var NodeFsHandler = class {
|
|
|
1888
2131
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
1889
2132
|
}
|
|
1890
2133
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
1891
|
-
this.fsw._closeFile(
|
|
2134
|
+
this.fsw._closeFile(path12);
|
|
1892
2135
|
prevStats = newStats2;
|
|
1893
2136
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
1894
2137
|
if (closer2)
|
|
1895
|
-
this.fsw._addPathCloser(
|
|
2138
|
+
this.fsw._addPathCloser(path12, closer2);
|
|
1896
2139
|
} else {
|
|
1897
2140
|
prevStats = newStats2;
|
|
1898
2141
|
}
|
|
1899
2142
|
} catch (error) {
|
|
1900
|
-
this.fsw._remove(
|
|
2143
|
+
this.fsw._remove(dirname8, basename5);
|
|
1901
2144
|
}
|
|
1902
|
-
} else if (parent.has(
|
|
2145
|
+
} else if (parent.has(basename5)) {
|
|
1903
2146
|
const at = newStats.atimeMs;
|
|
1904
2147
|
const mt = newStats.mtimeMs;
|
|
1905
2148
|
if (!at || at <= mt || mt !== prevStats.mtimeMs) {
|
|
@@ -1924,7 +2167,7 @@ var NodeFsHandler = class {
|
|
|
1924
2167
|
* @param item basename of this item
|
|
1925
2168
|
* @returns true if no more processing is needed for this entry.
|
|
1926
2169
|
*/
|
|
1927
|
-
async _handleSymlink(entry, directory,
|
|
2170
|
+
async _handleSymlink(entry, directory, path12, item) {
|
|
1928
2171
|
if (this.fsw.closed) {
|
|
1929
2172
|
return;
|
|
1930
2173
|
}
|
|
@@ -1934,7 +2177,7 @@ var NodeFsHandler = class {
|
|
|
1934
2177
|
this.fsw._incrReadyCount();
|
|
1935
2178
|
let linkPath;
|
|
1936
2179
|
try {
|
|
1937
|
-
linkPath = await (0, import_promises2.realpath)(
|
|
2180
|
+
linkPath = await (0, import_promises2.realpath)(path12);
|
|
1938
2181
|
} catch (e) {
|
|
1939
2182
|
this.fsw._emitReady();
|
|
1940
2183
|
return true;
|
|
@@ -1944,12 +2187,12 @@ var NodeFsHandler = class {
|
|
|
1944
2187
|
if (dir.has(item)) {
|
|
1945
2188
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
1946
2189
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
1947
|
-
this.fsw._emit(EV.CHANGE,
|
|
2190
|
+
this.fsw._emit(EV.CHANGE, path12, entry.stats);
|
|
1948
2191
|
}
|
|
1949
2192
|
} else {
|
|
1950
2193
|
dir.add(item);
|
|
1951
2194
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
1952
|
-
this.fsw._emit(EV.ADD,
|
|
2195
|
+
this.fsw._emit(EV.ADD, path12, entry.stats);
|
|
1953
2196
|
}
|
|
1954
2197
|
this.fsw._emitReady();
|
|
1955
2198
|
return true;
|
|
@@ -1979,9 +2222,9 @@ var NodeFsHandler = class {
|
|
|
1979
2222
|
return;
|
|
1980
2223
|
}
|
|
1981
2224
|
const item = entry.path;
|
|
1982
|
-
let
|
|
2225
|
+
let path12 = sp.join(directory, item);
|
|
1983
2226
|
current.add(item);
|
|
1984
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
2227
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path12, item)) {
|
|
1985
2228
|
return;
|
|
1986
2229
|
}
|
|
1987
2230
|
if (this.fsw.closed) {
|
|
@@ -1990,11 +2233,11 @@ var NodeFsHandler = class {
|
|
|
1990
2233
|
}
|
|
1991
2234
|
if (item === target || !target && !previous.has(item)) {
|
|
1992
2235
|
this.fsw._incrReadyCount();
|
|
1993
|
-
|
|
1994
|
-
this._addToNodeFs(
|
|
2236
|
+
path12 = sp.join(dir, sp.relative(dir, path12));
|
|
2237
|
+
this._addToNodeFs(path12, initialAdd, wh, depth + 1);
|
|
1995
2238
|
}
|
|
1996
2239
|
}).on(EV.ERROR, this._boundHandleError);
|
|
1997
|
-
return new Promise((
|
|
2240
|
+
return new Promise((resolve9, reject) => {
|
|
1998
2241
|
if (!stream)
|
|
1999
2242
|
return reject();
|
|
2000
2243
|
stream.once(STR_END, () => {
|
|
@@ -2003,7 +2246,7 @@ var NodeFsHandler = class {
|
|
|
2003
2246
|
return;
|
|
2004
2247
|
}
|
|
2005
2248
|
const wasThrottled = throttler ? throttler.clear() : false;
|
|
2006
|
-
|
|
2249
|
+
resolve9(void 0);
|
|
2007
2250
|
previous.getChildren().filter((item) => {
|
|
2008
2251
|
return item !== directory && !current.has(item);
|
|
2009
2252
|
}).forEach((item) => {
|
|
@@ -2060,13 +2303,13 @@ var NodeFsHandler = class {
|
|
|
2060
2303
|
* @param depth Child path actually targeted for watch
|
|
2061
2304
|
* @param target Child path actually targeted for watch
|
|
2062
2305
|
*/
|
|
2063
|
-
async _addToNodeFs(
|
|
2306
|
+
async _addToNodeFs(path12, initialAdd, priorWh, depth, target) {
|
|
2064
2307
|
const ready = this.fsw._emitReady;
|
|
2065
|
-
if (this.fsw._isIgnored(
|
|
2308
|
+
if (this.fsw._isIgnored(path12) || this.fsw.closed) {
|
|
2066
2309
|
ready();
|
|
2067
2310
|
return false;
|
|
2068
2311
|
}
|
|
2069
|
-
const wh = this.fsw._getWatchHelpers(
|
|
2312
|
+
const wh = this.fsw._getWatchHelpers(path12);
|
|
2070
2313
|
if (priorWh) {
|
|
2071
2314
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
2072
2315
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -2082,8 +2325,8 @@ var NodeFsHandler = class {
|
|
|
2082
2325
|
const follow = this.fsw.options.followSymlinks;
|
|
2083
2326
|
let closer;
|
|
2084
2327
|
if (stats.isDirectory()) {
|
|
2085
|
-
const absPath = sp.resolve(
|
|
2086
|
-
const targetPath = follow ? await (0, import_promises2.realpath)(
|
|
2328
|
+
const absPath = sp.resolve(path12);
|
|
2329
|
+
const targetPath = follow ? await (0, import_promises2.realpath)(path12) : path12;
|
|
2087
2330
|
if (this.fsw.closed)
|
|
2088
2331
|
return;
|
|
2089
2332
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -2093,29 +2336,29 @@ var NodeFsHandler = class {
|
|
|
2093
2336
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
2094
2337
|
}
|
|
2095
2338
|
} else if (stats.isSymbolicLink()) {
|
|
2096
|
-
const targetPath = follow ? await (0, import_promises2.realpath)(
|
|
2339
|
+
const targetPath = follow ? await (0, import_promises2.realpath)(path12) : path12;
|
|
2097
2340
|
if (this.fsw.closed)
|
|
2098
2341
|
return;
|
|
2099
2342
|
const parent = sp.dirname(wh.watchPath);
|
|
2100
2343
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
2101
2344
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
2102
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
2345
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path12, wh, targetPath);
|
|
2103
2346
|
if (this.fsw.closed)
|
|
2104
2347
|
return;
|
|
2105
2348
|
if (targetPath !== void 0) {
|
|
2106
|
-
this.fsw._symlinkPaths.set(sp.resolve(
|
|
2349
|
+
this.fsw._symlinkPaths.set(sp.resolve(path12), targetPath);
|
|
2107
2350
|
}
|
|
2108
2351
|
} else {
|
|
2109
2352
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
2110
2353
|
}
|
|
2111
2354
|
ready();
|
|
2112
2355
|
if (closer)
|
|
2113
|
-
this.fsw._addPathCloser(
|
|
2356
|
+
this.fsw._addPathCloser(path12, closer);
|
|
2114
2357
|
return false;
|
|
2115
2358
|
} catch (error) {
|
|
2116
2359
|
if (this.fsw._handleError(error)) {
|
|
2117
2360
|
ready();
|
|
2118
|
-
return
|
|
2361
|
+
return path12;
|
|
2119
2362
|
}
|
|
2120
2363
|
}
|
|
2121
2364
|
}
|
|
@@ -2147,35 +2390,35 @@ function createPattern(matcher) {
|
|
|
2147
2390
|
if (matcher.path === string)
|
|
2148
2391
|
return true;
|
|
2149
2392
|
if (matcher.recursive) {
|
|
2150
|
-
const
|
|
2151
|
-
if (!
|
|
2393
|
+
const relative8 = sp2.relative(matcher.path, string);
|
|
2394
|
+
if (!relative8) {
|
|
2152
2395
|
return false;
|
|
2153
2396
|
}
|
|
2154
|
-
return !
|
|
2397
|
+
return !relative8.startsWith("..") && !sp2.isAbsolute(relative8);
|
|
2155
2398
|
}
|
|
2156
2399
|
return false;
|
|
2157
2400
|
};
|
|
2158
2401
|
}
|
|
2159
2402
|
return () => false;
|
|
2160
2403
|
}
|
|
2161
|
-
function normalizePath(
|
|
2162
|
-
if (typeof
|
|
2404
|
+
function normalizePath(path12) {
|
|
2405
|
+
if (typeof path12 !== "string")
|
|
2163
2406
|
throw new Error("string expected");
|
|
2164
|
-
|
|
2165
|
-
|
|
2407
|
+
path12 = sp2.normalize(path12);
|
|
2408
|
+
path12 = path12.replace(/\\/g, "/");
|
|
2166
2409
|
let prepend = false;
|
|
2167
|
-
if (
|
|
2410
|
+
if (path12.startsWith("//"))
|
|
2168
2411
|
prepend = true;
|
|
2169
|
-
|
|
2412
|
+
path12 = path12.replace(DOUBLE_SLASH_RE, "/");
|
|
2170
2413
|
if (prepend)
|
|
2171
|
-
|
|
2172
|
-
return
|
|
2414
|
+
path12 = "/" + path12;
|
|
2415
|
+
return path12;
|
|
2173
2416
|
}
|
|
2174
2417
|
function matchPatterns(patterns, testString, stats) {
|
|
2175
|
-
const
|
|
2418
|
+
const path12 = normalizePath(testString);
|
|
2176
2419
|
for (let index = 0; index < patterns.length; index++) {
|
|
2177
2420
|
const pattern = patterns[index];
|
|
2178
|
-
if (pattern(
|
|
2421
|
+
if (pattern(path12, stats)) {
|
|
2179
2422
|
return true;
|
|
2180
2423
|
}
|
|
2181
2424
|
}
|
|
@@ -2213,19 +2456,19 @@ var toUnix = (string) => {
|
|
|
2213
2456
|
}
|
|
2214
2457
|
return str;
|
|
2215
2458
|
};
|
|
2216
|
-
var normalizePathToUnix = (
|
|
2217
|
-
var normalizeIgnored = (cwd = "") => (
|
|
2218
|
-
if (typeof
|
|
2219
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
2459
|
+
var normalizePathToUnix = (path12) => toUnix(sp2.normalize(toUnix(path12)));
|
|
2460
|
+
var normalizeIgnored = (cwd = "") => (path12) => {
|
|
2461
|
+
if (typeof path12 === "string") {
|
|
2462
|
+
return normalizePathToUnix(sp2.isAbsolute(path12) ? path12 : sp2.join(cwd, path12));
|
|
2220
2463
|
} else {
|
|
2221
|
-
return
|
|
2464
|
+
return path12;
|
|
2222
2465
|
}
|
|
2223
2466
|
};
|
|
2224
|
-
var getAbsolutePath = (
|
|
2225
|
-
if (sp2.isAbsolute(
|
|
2226
|
-
return
|
|
2467
|
+
var getAbsolutePath = (path12, cwd) => {
|
|
2468
|
+
if (sp2.isAbsolute(path12)) {
|
|
2469
|
+
return path12;
|
|
2227
2470
|
}
|
|
2228
|
-
return sp2.join(cwd,
|
|
2471
|
+
return sp2.join(cwd, path12);
|
|
2229
2472
|
};
|
|
2230
2473
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
2231
2474
|
var DirEntry = class {
|
|
@@ -2290,10 +2533,10 @@ var WatchHelper = class {
|
|
|
2290
2533
|
dirParts;
|
|
2291
2534
|
followSymlinks;
|
|
2292
2535
|
statMethod;
|
|
2293
|
-
constructor(
|
|
2536
|
+
constructor(path12, follow, fsw) {
|
|
2294
2537
|
this.fsw = fsw;
|
|
2295
|
-
const watchPath =
|
|
2296
|
-
this.path =
|
|
2538
|
+
const watchPath = path12;
|
|
2539
|
+
this.path = path12 = path12.replace(REPLACER_RE, "");
|
|
2297
2540
|
this.watchPath = watchPath;
|
|
2298
2541
|
this.fullWatchPath = sp2.resolve(watchPath);
|
|
2299
2542
|
this.dirParts = [];
|
|
@@ -2433,20 +2676,20 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
2433
2676
|
this._closePromise = void 0;
|
|
2434
2677
|
let paths = unifyPaths(paths_);
|
|
2435
2678
|
if (cwd) {
|
|
2436
|
-
paths = paths.map((
|
|
2437
|
-
const absPath = getAbsolutePath(
|
|
2679
|
+
paths = paths.map((path12) => {
|
|
2680
|
+
const absPath = getAbsolutePath(path12, cwd);
|
|
2438
2681
|
return absPath;
|
|
2439
2682
|
});
|
|
2440
2683
|
}
|
|
2441
|
-
paths.forEach((
|
|
2442
|
-
this._removeIgnoredPath(
|
|
2684
|
+
paths.forEach((path12) => {
|
|
2685
|
+
this._removeIgnoredPath(path12);
|
|
2443
2686
|
});
|
|
2444
2687
|
this._userIgnored = void 0;
|
|
2445
2688
|
if (!this._readyCount)
|
|
2446
2689
|
this._readyCount = 0;
|
|
2447
2690
|
this._readyCount += paths.length;
|
|
2448
|
-
Promise.all(paths.map(async (
|
|
2449
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
2691
|
+
Promise.all(paths.map(async (path12) => {
|
|
2692
|
+
const res = await this._nodeFsHandler._addToNodeFs(path12, !_internal, void 0, 0, _origAdd);
|
|
2450
2693
|
if (res)
|
|
2451
2694
|
this._emitReady();
|
|
2452
2695
|
return res;
|
|
@@ -2468,17 +2711,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
2468
2711
|
return this;
|
|
2469
2712
|
const paths = unifyPaths(paths_);
|
|
2470
2713
|
const { cwd } = this.options;
|
|
2471
|
-
paths.forEach((
|
|
2472
|
-
if (!sp2.isAbsolute(
|
|
2714
|
+
paths.forEach((path12) => {
|
|
2715
|
+
if (!sp2.isAbsolute(path12) && !this._closers.has(path12)) {
|
|
2473
2716
|
if (cwd)
|
|
2474
|
-
|
|
2475
|
-
|
|
2717
|
+
path12 = sp2.join(cwd, path12);
|
|
2718
|
+
path12 = sp2.resolve(path12);
|
|
2476
2719
|
}
|
|
2477
|
-
this._closePath(
|
|
2478
|
-
this._addIgnoredPath(
|
|
2479
|
-
if (this._watched.has(
|
|
2720
|
+
this._closePath(path12);
|
|
2721
|
+
this._addIgnoredPath(path12);
|
|
2722
|
+
if (this._watched.has(path12)) {
|
|
2480
2723
|
this._addIgnoredPath({
|
|
2481
|
-
path:
|
|
2724
|
+
path: path12,
|
|
2482
2725
|
recursive: true
|
|
2483
2726
|
});
|
|
2484
2727
|
}
|
|
@@ -2542,38 +2785,38 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
2542
2785
|
* @param stats arguments to be passed with event
|
|
2543
2786
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
2544
2787
|
*/
|
|
2545
|
-
async _emit(event,
|
|
2788
|
+
async _emit(event, path12, stats) {
|
|
2546
2789
|
if (this.closed)
|
|
2547
2790
|
return;
|
|
2548
2791
|
const opts = this.options;
|
|
2549
2792
|
if (isWindows)
|
|
2550
|
-
|
|
2793
|
+
path12 = sp2.normalize(path12);
|
|
2551
2794
|
if (opts.cwd)
|
|
2552
|
-
|
|
2553
|
-
const args = [
|
|
2795
|
+
path12 = sp2.relative(opts.cwd, path12);
|
|
2796
|
+
const args = [path12];
|
|
2554
2797
|
if (stats != null)
|
|
2555
2798
|
args.push(stats);
|
|
2556
2799
|
const awf = opts.awaitWriteFinish;
|
|
2557
2800
|
let pw;
|
|
2558
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
2801
|
+
if (awf && (pw = this._pendingWrites.get(path12))) {
|
|
2559
2802
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
2560
2803
|
return this;
|
|
2561
2804
|
}
|
|
2562
2805
|
if (opts.atomic) {
|
|
2563
2806
|
if (event === EVENTS.UNLINK) {
|
|
2564
|
-
this._pendingUnlinks.set(
|
|
2807
|
+
this._pendingUnlinks.set(path12, [event, ...args]);
|
|
2565
2808
|
setTimeout(() => {
|
|
2566
|
-
this._pendingUnlinks.forEach((entry,
|
|
2809
|
+
this._pendingUnlinks.forEach((entry, path13) => {
|
|
2567
2810
|
this.emit(...entry);
|
|
2568
2811
|
this.emit(EVENTS.ALL, ...entry);
|
|
2569
|
-
this._pendingUnlinks.delete(
|
|
2812
|
+
this._pendingUnlinks.delete(path13);
|
|
2570
2813
|
});
|
|
2571
2814
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
2572
2815
|
return this;
|
|
2573
2816
|
}
|
|
2574
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
2817
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path12)) {
|
|
2575
2818
|
event = EVENTS.CHANGE;
|
|
2576
|
-
this._pendingUnlinks.delete(
|
|
2819
|
+
this._pendingUnlinks.delete(path12);
|
|
2577
2820
|
}
|
|
2578
2821
|
}
|
|
2579
2822
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -2591,16 +2834,16 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
2591
2834
|
this.emitWithAll(event, args);
|
|
2592
2835
|
}
|
|
2593
2836
|
};
|
|
2594
|
-
this._awaitWriteFinish(
|
|
2837
|
+
this._awaitWriteFinish(path12, awf.stabilityThreshold, event, awfEmit);
|
|
2595
2838
|
return this;
|
|
2596
2839
|
}
|
|
2597
2840
|
if (event === EVENTS.CHANGE) {
|
|
2598
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
2841
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path12, 50);
|
|
2599
2842
|
if (isThrottled)
|
|
2600
2843
|
return this;
|
|
2601
2844
|
}
|
|
2602
2845
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
2603
|
-
const fullPath = opts.cwd ? sp2.join(opts.cwd,
|
|
2846
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path12) : path12;
|
|
2604
2847
|
let stats2;
|
|
2605
2848
|
try {
|
|
2606
2849
|
stats2 = await (0, import_promises3.stat)(fullPath);
|
|
@@ -2631,23 +2874,23 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
2631
2874
|
* @param timeout duration of time to suppress duplicate actions
|
|
2632
2875
|
* @returns tracking object or false if action should be suppressed
|
|
2633
2876
|
*/
|
|
2634
|
-
_throttle(actionType,
|
|
2877
|
+
_throttle(actionType, path12, timeout) {
|
|
2635
2878
|
if (!this._throttled.has(actionType)) {
|
|
2636
2879
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
2637
2880
|
}
|
|
2638
2881
|
const action = this._throttled.get(actionType);
|
|
2639
2882
|
if (!action)
|
|
2640
2883
|
throw new Error("invalid throttle");
|
|
2641
|
-
const actionPath = action.get(
|
|
2884
|
+
const actionPath = action.get(path12);
|
|
2642
2885
|
if (actionPath) {
|
|
2643
2886
|
actionPath.count++;
|
|
2644
2887
|
return false;
|
|
2645
2888
|
}
|
|
2646
2889
|
let timeoutObject;
|
|
2647
2890
|
const clear = () => {
|
|
2648
|
-
const item = action.get(
|
|
2891
|
+
const item = action.get(path12);
|
|
2649
2892
|
const count = item ? item.count : 0;
|
|
2650
|
-
action.delete(
|
|
2893
|
+
action.delete(path12);
|
|
2651
2894
|
clearTimeout(timeoutObject);
|
|
2652
2895
|
if (item)
|
|
2653
2896
|
clearTimeout(item.timeoutObject);
|
|
@@ -2655,7 +2898,7 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
2655
2898
|
};
|
|
2656
2899
|
timeoutObject = setTimeout(clear, timeout);
|
|
2657
2900
|
const thr = { timeoutObject, clear, count: 0 };
|
|
2658
|
-
action.set(
|
|
2901
|
+
action.set(path12, thr);
|
|
2659
2902
|
return thr;
|
|
2660
2903
|
}
|
|
2661
2904
|
_incrReadyCount() {
|
|
@@ -2669,44 +2912,44 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
2669
2912
|
* @param event
|
|
2670
2913
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
2671
2914
|
*/
|
|
2672
|
-
_awaitWriteFinish(
|
|
2915
|
+
_awaitWriteFinish(path12, threshold, event, awfEmit) {
|
|
2673
2916
|
const awf = this.options.awaitWriteFinish;
|
|
2674
2917
|
if (typeof awf !== "object")
|
|
2675
2918
|
return;
|
|
2676
2919
|
const pollInterval = awf.pollInterval;
|
|
2677
2920
|
let timeoutHandler;
|
|
2678
|
-
let fullPath =
|
|
2679
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
2680
|
-
fullPath = sp2.join(this.options.cwd,
|
|
2921
|
+
let fullPath = path12;
|
|
2922
|
+
if (this.options.cwd && !sp2.isAbsolute(path12)) {
|
|
2923
|
+
fullPath = sp2.join(this.options.cwd, path12);
|
|
2681
2924
|
}
|
|
2682
2925
|
const now = /* @__PURE__ */ new Date();
|
|
2683
2926
|
const writes = this._pendingWrites;
|
|
2684
2927
|
function awaitWriteFinishFn(prevStat) {
|
|
2685
2928
|
(0, import_node_fs2.stat)(fullPath, (err, curStat) => {
|
|
2686
|
-
if (err || !writes.has(
|
|
2929
|
+
if (err || !writes.has(path12)) {
|
|
2687
2930
|
if (err && err.code !== "ENOENT")
|
|
2688
2931
|
awfEmit(err);
|
|
2689
2932
|
return;
|
|
2690
2933
|
}
|
|
2691
2934
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
2692
2935
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
2693
|
-
writes.get(
|
|
2936
|
+
writes.get(path12).lastChange = now2;
|
|
2694
2937
|
}
|
|
2695
|
-
const pw = writes.get(
|
|
2938
|
+
const pw = writes.get(path12);
|
|
2696
2939
|
const df = now2 - pw.lastChange;
|
|
2697
2940
|
if (df >= threshold) {
|
|
2698
|
-
writes.delete(
|
|
2941
|
+
writes.delete(path12);
|
|
2699
2942
|
awfEmit(void 0, curStat);
|
|
2700
2943
|
} else {
|
|
2701
2944
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
2702
2945
|
}
|
|
2703
2946
|
});
|
|
2704
2947
|
}
|
|
2705
|
-
if (!writes.has(
|
|
2706
|
-
writes.set(
|
|
2948
|
+
if (!writes.has(path12)) {
|
|
2949
|
+
writes.set(path12, {
|
|
2707
2950
|
lastChange: now,
|
|
2708
2951
|
cancelWait: () => {
|
|
2709
|
-
writes.delete(
|
|
2952
|
+
writes.delete(path12);
|
|
2710
2953
|
clearTimeout(timeoutHandler);
|
|
2711
2954
|
return event;
|
|
2712
2955
|
}
|
|
@@ -2717,8 +2960,8 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
2717
2960
|
/**
|
|
2718
2961
|
* Determines whether user has asked to ignore this path.
|
|
2719
2962
|
*/
|
|
2720
|
-
_isIgnored(
|
|
2721
|
-
if (this.options.atomic && DOT_RE.test(
|
|
2963
|
+
_isIgnored(path12, stats) {
|
|
2964
|
+
if (this.options.atomic && DOT_RE.test(path12))
|
|
2722
2965
|
return true;
|
|
2723
2966
|
if (!this._userIgnored) {
|
|
2724
2967
|
const { cwd } = this.options;
|
|
@@ -2728,17 +2971,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
2728
2971
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
2729
2972
|
this._userIgnored = anymatch(list, void 0);
|
|
2730
2973
|
}
|
|
2731
|
-
return this._userIgnored(
|
|
2974
|
+
return this._userIgnored(path12, stats);
|
|
2732
2975
|
}
|
|
2733
|
-
_isntIgnored(
|
|
2734
|
-
return !this._isIgnored(
|
|
2976
|
+
_isntIgnored(path12, stat4) {
|
|
2977
|
+
return !this._isIgnored(path12, stat4);
|
|
2735
2978
|
}
|
|
2736
2979
|
/**
|
|
2737
2980
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
2738
2981
|
* @param path file or directory pattern being watched
|
|
2739
2982
|
*/
|
|
2740
|
-
_getWatchHelpers(
|
|
2741
|
-
return new WatchHelper(
|
|
2983
|
+
_getWatchHelpers(path12) {
|
|
2984
|
+
return new WatchHelper(path12, this.options.followSymlinks, this);
|
|
2742
2985
|
}
|
|
2743
2986
|
// Directory helpers
|
|
2744
2987
|
// -----------------
|
|
@@ -2770,63 +3013,63 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
2770
3013
|
* @param item base path of item/directory
|
|
2771
3014
|
*/
|
|
2772
3015
|
_remove(directory, item, isDirectory) {
|
|
2773
|
-
const
|
|
2774
|
-
const fullPath = sp2.resolve(
|
|
2775
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
2776
|
-
if (!this._throttle("remove",
|
|
3016
|
+
const path12 = sp2.join(directory, item);
|
|
3017
|
+
const fullPath = sp2.resolve(path12);
|
|
3018
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path12) || this._watched.has(fullPath);
|
|
3019
|
+
if (!this._throttle("remove", path12, 100))
|
|
2777
3020
|
return;
|
|
2778
3021
|
if (!isDirectory && this._watched.size === 1) {
|
|
2779
3022
|
this.add(directory, item, true);
|
|
2780
3023
|
}
|
|
2781
|
-
const wp = this._getWatchedDir(
|
|
3024
|
+
const wp = this._getWatchedDir(path12);
|
|
2782
3025
|
const nestedDirectoryChildren = wp.getChildren();
|
|
2783
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
3026
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path12, nested));
|
|
2784
3027
|
const parent = this._getWatchedDir(directory);
|
|
2785
3028
|
const wasTracked = parent.has(item);
|
|
2786
3029
|
parent.remove(item);
|
|
2787
3030
|
if (this._symlinkPaths.has(fullPath)) {
|
|
2788
3031
|
this._symlinkPaths.delete(fullPath);
|
|
2789
3032
|
}
|
|
2790
|
-
let relPath =
|
|
3033
|
+
let relPath = path12;
|
|
2791
3034
|
if (this.options.cwd)
|
|
2792
|
-
relPath = sp2.relative(this.options.cwd,
|
|
3035
|
+
relPath = sp2.relative(this.options.cwd, path12);
|
|
2793
3036
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
2794
3037
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
2795
3038
|
if (event === EVENTS.ADD)
|
|
2796
3039
|
return;
|
|
2797
3040
|
}
|
|
2798
|
-
this._watched.delete(
|
|
3041
|
+
this._watched.delete(path12);
|
|
2799
3042
|
this._watched.delete(fullPath);
|
|
2800
3043
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
2801
|
-
if (wasTracked && !this._isIgnored(
|
|
2802
|
-
this._emit(eventName,
|
|
2803
|
-
this._closePath(
|
|
3044
|
+
if (wasTracked && !this._isIgnored(path12))
|
|
3045
|
+
this._emit(eventName, path12);
|
|
3046
|
+
this._closePath(path12);
|
|
2804
3047
|
}
|
|
2805
3048
|
/**
|
|
2806
3049
|
* Closes all watchers for a path
|
|
2807
3050
|
*/
|
|
2808
|
-
_closePath(
|
|
2809
|
-
this._closeFile(
|
|
2810
|
-
const dir = sp2.dirname(
|
|
2811
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
3051
|
+
_closePath(path12) {
|
|
3052
|
+
this._closeFile(path12);
|
|
3053
|
+
const dir = sp2.dirname(path12);
|
|
3054
|
+
this._getWatchedDir(dir).remove(sp2.basename(path12));
|
|
2812
3055
|
}
|
|
2813
3056
|
/**
|
|
2814
3057
|
* Closes only file-specific watchers
|
|
2815
3058
|
*/
|
|
2816
|
-
_closeFile(
|
|
2817
|
-
const closers = this._closers.get(
|
|
3059
|
+
_closeFile(path12) {
|
|
3060
|
+
const closers = this._closers.get(path12);
|
|
2818
3061
|
if (!closers)
|
|
2819
3062
|
return;
|
|
2820
3063
|
closers.forEach((closer) => closer());
|
|
2821
|
-
this._closers.delete(
|
|
3064
|
+
this._closers.delete(path12);
|
|
2822
3065
|
}
|
|
2823
|
-
_addPathCloser(
|
|
3066
|
+
_addPathCloser(path12, closer) {
|
|
2824
3067
|
if (!closer)
|
|
2825
3068
|
return;
|
|
2826
|
-
let list = this._closers.get(
|
|
3069
|
+
let list = this._closers.get(path12);
|
|
2827
3070
|
if (!list) {
|
|
2828
3071
|
list = [];
|
|
2829
|
-
this._closers.set(
|
|
3072
|
+
this._closers.set(path12, list);
|
|
2830
3073
|
}
|
|
2831
3074
|
list.push(closer);
|
|
2832
3075
|
}
|
|
@@ -2856,12 +3099,12 @@ function watch(paths, options = {}) {
|
|
|
2856
3099
|
var chokidar_default = { watch, FSWatcher };
|
|
2857
3100
|
|
|
2858
3101
|
// src/watcher/index.ts
|
|
2859
|
-
var
|
|
3102
|
+
var path5 = __toESM(require("path"), 1);
|
|
2860
3103
|
|
|
2861
3104
|
// src/utils/files.ts
|
|
2862
3105
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
2863
|
-
var
|
|
2864
|
-
var
|
|
3106
|
+
var import_fs4 = require("fs");
|
|
3107
|
+
var path4 = __toESM(require("path"), 1);
|
|
2865
3108
|
var PROJECT_MARKERS = [
|
|
2866
3109
|
".git",
|
|
2867
3110
|
"package.json",
|
|
@@ -2880,7 +3123,7 @@ var PROJECT_MARKERS = [
|
|
|
2880
3123
|
];
|
|
2881
3124
|
function hasProjectMarker(projectRoot) {
|
|
2882
3125
|
for (const marker of PROJECT_MARKERS) {
|
|
2883
|
-
if ((0,
|
|
3126
|
+
if ((0, import_fs4.existsSync)(path4.join(projectRoot, marker))) {
|
|
2884
3127
|
return true;
|
|
2885
3128
|
}
|
|
2886
3129
|
}
|
|
@@ -2906,16 +3149,16 @@ function createIgnoreFilter(projectRoot) {
|
|
|
2906
3149
|
"**/*build*/**"
|
|
2907
3150
|
];
|
|
2908
3151
|
ig.add(defaultIgnores);
|
|
2909
|
-
const gitignorePath =
|
|
2910
|
-
if ((0,
|
|
2911
|
-
const gitignoreContent = (0,
|
|
3152
|
+
const gitignorePath = path4.join(projectRoot, ".gitignore");
|
|
3153
|
+
if ((0, import_fs4.existsSync)(gitignorePath)) {
|
|
3154
|
+
const gitignoreContent = (0, import_fs4.readFileSync)(gitignorePath, "utf-8");
|
|
2912
3155
|
ig.add(gitignoreContent);
|
|
2913
3156
|
}
|
|
2914
3157
|
return ig;
|
|
2915
3158
|
}
|
|
2916
3159
|
function shouldIncludeFile(filePath, projectRoot, includePatterns, excludePatterns, ignoreFilter) {
|
|
2917
|
-
const relativePath =
|
|
2918
|
-
const pathParts = relativePath.split(
|
|
3160
|
+
const relativePath = path4.relative(projectRoot, filePath);
|
|
3161
|
+
const pathParts = relativePath.split(path4.sep);
|
|
2919
3162
|
for (const part of pathParts) {
|
|
2920
3163
|
if (part.startsWith(".") && part !== "." && part !== "..") {
|
|
2921
3164
|
return false;
|
|
@@ -2955,12 +3198,12 @@ function matchGlob(filePath, pattern) {
|
|
|
2955
3198
|
return regex.test(filePath);
|
|
2956
3199
|
}
|
|
2957
3200
|
async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns, ignoreFilter, maxFileSize, skipped, options, currentDepth = 0) {
|
|
2958
|
-
const entries = await
|
|
3201
|
+
const entries = await import_fs4.promises.readdir(dir, { withFileTypes: true });
|
|
2959
3202
|
const filesInDir = [];
|
|
2960
3203
|
const subdirs = [];
|
|
2961
3204
|
for (const entry of entries) {
|
|
2962
|
-
const fullPath =
|
|
2963
|
-
const relativePath =
|
|
3205
|
+
const fullPath = path4.join(dir, entry.name);
|
|
3206
|
+
const relativePath = path4.relative(projectRoot, fullPath);
|
|
2964
3207
|
if (entry.name.startsWith(".") && entry.name !== "." && entry.name !== "..") {
|
|
2965
3208
|
if (entry.isDirectory()) {
|
|
2966
3209
|
skipped.push({ path: relativePath, reason: "excluded" });
|
|
@@ -2980,7 +3223,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
|
|
|
2980
3223
|
if (entry.isDirectory()) {
|
|
2981
3224
|
subdirs.push({ fullPath, relativePath });
|
|
2982
3225
|
} else if (entry.isFile()) {
|
|
2983
|
-
const stat4 = await
|
|
3226
|
+
const stat4 = await import_fs4.promises.stat(fullPath);
|
|
2984
3227
|
if (stat4.size > maxFileSize) {
|
|
2985
3228
|
skipped.push({ path: relativePath, reason: "too_large" });
|
|
2986
3229
|
continue;
|
|
@@ -3009,7 +3252,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
|
|
|
3009
3252
|
yield f;
|
|
3010
3253
|
}
|
|
3011
3254
|
for (let i = options.maxFilesPerDirectory; i < filesInDir.length; i++) {
|
|
3012
|
-
skipped.push({ path:
|
|
3255
|
+
skipped.push({ path: path4.relative(projectRoot, filesInDir[i].path), reason: "excluded" });
|
|
3013
3256
|
}
|
|
3014
3257
|
const canRecurse = options.maxDepth === -1 || currentDepth < options.maxDepth;
|
|
3015
3258
|
if (canRecurse) {
|
|
@@ -3049,14 +3292,14 @@ async function collectFiles(projectRoot, includePatterns, excludePatterns, maxFi
|
|
|
3049
3292
|
if (additionalRoots && additionalRoots.length > 0) {
|
|
3050
3293
|
const normalizedRoots = /* @__PURE__ */ new Set();
|
|
3051
3294
|
for (const kbRoot of additionalRoots) {
|
|
3052
|
-
const resolved =
|
|
3053
|
-
|
|
3295
|
+
const resolved = path4.normalize(
|
|
3296
|
+
path4.isAbsolute(kbRoot) ? kbRoot : path4.resolve(projectRoot, kbRoot)
|
|
3054
3297
|
);
|
|
3055
3298
|
normalizedRoots.add(resolved);
|
|
3056
3299
|
}
|
|
3057
3300
|
for (const resolvedKbRoot of normalizedRoots) {
|
|
3058
3301
|
try {
|
|
3059
|
-
const stat4 = await
|
|
3302
|
+
const stat4 = await import_fs4.promises.stat(resolvedKbRoot);
|
|
3060
3303
|
if (!stat4.isDirectory()) {
|
|
3061
3304
|
skipped.push({ path: resolvedKbRoot, reason: "excluded" });
|
|
3062
3305
|
continue;
|
|
@@ -3083,122 +3326,6 @@ async function collectFiles(projectRoot, includePatterns, excludePatterns, maxFi
|
|
|
3083
3326
|
return { files, skipped };
|
|
3084
3327
|
}
|
|
3085
3328
|
|
|
3086
|
-
// src/git/index.ts
|
|
3087
|
-
var import_fs3 = require("fs");
|
|
3088
|
-
var path3 = __toESM(require("path"), 1);
|
|
3089
|
-
function readPackedRefs(gitDir) {
|
|
3090
|
-
const packedRefsPath = path3.join(gitDir, "packed-refs");
|
|
3091
|
-
if (!(0, import_fs3.existsSync)(packedRefsPath)) {
|
|
3092
|
-
return [];
|
|
3093
|
-
}
|
|
3094
|
-
try {
|
|
3095
|
-
return (0, import_fs3.readFileSync)(packedRefsPath, "utf-8").split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#") && !line.startsWith("^"));
|
|
3096
|
-
} catch {
|
|
3097
|
-
return [];
|
|
3098
|
-
}
|
|
3099
|
-
}
|
|
3100
|
-
function resolveCommonGitDir(gitDir) {
|
|
3101
|
-
const commonDirPath = path3.join(gitDir, "commondir");
|
|
3102
|
-
if (!(0, import_fs3.existsSync)(commonDirPath)) {
|
|
3103
|
-
return gitDir;
|
|
3104
|
-
}
|
|
3105
|
-
try {
|
|
3106
|
-
const raw = (0, import_fs3.readFileSync)(commonDirPath, "utf-8").trim();
|
|
3107
|
-
if (!raw) {
|
|
3108
|
-
return gitDir;
|
|
3109
|
-
}
|
|
3110
|
-
const resolved = path3.isAbsolute(raw) ? raw : path3.resolve(gitDir, raw);
|
|
3111
|
-
if ((0, import_fs3.existsSync)(resolved)) {
|
|
3112
|
-
return resolved;
|
|
3113
|
-
}
|
|
3114
|
-
} catch {
|
|
3115
|
-
return gitDir;
|
|
3116
|
-
}
|
|
3117
|
-
return gitDir;
|
|
3118
|
-
}
|
|
3119
|
-
function resolveGitDir(repoRoot) {
|
|
3120
|
-
const gitPath = path3.join(repoRoot, ".git");
|
|
3121
|
-
if (!(0, import_fs3.existsSync)(gitPath)) {
|
|
3122
|
-
return null;
|
|
3123
|
-
}
|
|
3124
|
-
try {
|
|
3125
|
-
const stat4 = (0, import_fs3.statSync)(gitPath);
|
|
3126
|
-
if (stat4.isDirectory()) {
|
|
3127
|
-
return gitPath;
|
|
3128
|
-
}
|
|
3129
|
-
if (stat4.isFile()) {
|
|
3130
|
-
const content = (0, import_fs3.readFileSync)(gitPath, "utf-8").trim();
|
|
3131
|
-
const match = content.match(/^gitdir:\s*(.+)$/);
|
|
3132
|
-
if (match) {
|
|
3133
|
-
const gitdir = match[1];
|
|
3134
|
-
const resolvedPath = path3.isAbsolute(gitdir) ? gitdir : path3.resolve(repoRoot, gitdir);
|
|
3135
|
-
if ((0, import_fs3.existsSync)(resolvedPath)) {
|
|
3136
|
-
return resolvedPath;
|
|
3137
|
-
}
|
|
3138
|
-
}
|
|
3139
|
-
}
|
|
3140
|
-
} catch {
|
|
3141
|
-
}
|
|
3142
|
-
return null;
|
|
3143
|
-
}
|
|
3144
|
-
function isGitRepo(dir) {
|
|
3145
|
-
return resolveGitDir(dir) !== null;
|
|
3146
|
-
}
|
|
3147
|
-
function getCurrentBranch(repoRoot) {
|
|
3148
|
-
const gitDir = resolveGitDir(repoRoot);
|
|
3149
|
-
if (!gitDir) {
|
|
3150
|
-
return null;
|
|
3151
|
-
}
|
|
3152
|
-
const headPath = path3.join(gitDir, "HEAD");
|
|
3153
|
-
if (!(0, import_fs3.existsSync)(headPath)) {
|
|
3154
|
-
return null;
|
|
3155
|
-
}
|
|
3156
|
-
try {
|
|
3157
|
-
const headContent = (0, import_fs3.readFileSync)(headPath, "utf-8").trim();
|
|
3158
|
-
const match = headContent.match(/^ref: refs\/heads\/(.+)$/);
|
|
3159
|
-
if (match) {
|
|
3160
|
-
return match[1];
|
|
3161
|
-
}
|
|
3162
|
-
if (/^[0-9a-f]{40}$/i.test(headContent)) {
|
|
3163
|
-
return headContent.slice(0, 7);
|
|
3164
|
-
}
|
|
3165
|
-
return null;
|
|
3166
|
-
} catch {
|
|
3167
|
-
return null;
|
|
3168
|
-
}
|
|
3169
|
-
}
|
|
3170
|
-
function getBaseBranch(repoRoot) {
|
|
3171
|
-
const gitDir = resolveGitDir(repoRoot);
|
|
3172
|
-
const refStoreDir = gitDir ? resolveCommonGitDir(gitDir) : null;
|
|
3173
|
-
const candidates = ["main", "master", "develop", "trunk"];
|
|
3174
|
-
if (refStoreDir) {
|
|
3175
|
-
for (const candidate of candidates) {
|
|
3176
|
-
const refPath = path3.join(refStoreDir, "refs", "heads", candidate);
|
|
3177
|
-
if ((0, import_fs3.existsSync)(refPath)) {
|
|
3178
|
-
return candidate;
|
|
3179
|
-
}
|
|
3180
|
-
const packedRefs = readPackedRefs(refStoreDir);
|
|
3181
|
-
if (packedRefs.some((line) => line.endsWith(` refs/heads/${candidate}`))) {
|
|
3182
|
-
return candidate;
|
|
3183
|
-
}
|
|
3184
|
-
}
|
|
3185
|
-
}
|
|
3186
|
-
return getCurrentBranch(repoRoot) ?? "main";
|
|
3187
|
-
}
|
|
3188
|
-
function getBranchOrDefault(repoRoot) {
|
|
3189
|
-
if (!isGitRepo(repoRoot)) {
|
|
3190
|
-
return "default";
|
|
3191
|
-
}
|
|
3192
|
-
return getCurrentBranch(repoRoot) ?? "default";
|
|
3193
|
-
}
|
|
3194
|
-
function getHeadPath(repoRoot) {
|
|
3195
|
-
const gitDir = resolveGitDir(repoRoot);
|
|
3196
|
-
if (gitDir) {
|
|
3197
|
-
return path3.join(gitDir, "HEAD");
|
|
3198
|
-
}
|
|
3199
|
-
return path3.join(repoRoot, ".git", "HEAD");
|
|
3200
|
-
}
|
|
3201
|
-
|
|
3202
3329
|
// src/watcher/index.ts
|
|
3203
3330
|
var FileWatcher = class {
|
|
3204
3331
|
watcher = null;
|
|
@@ -3220,9 +3347,9 @@ var FileWatcher = class {
|
|
|
3220
3347
|
const ignoreFilter = createIgnoreFilter(this.projectRoot);
|
|
3221
3348
|
this.watcher = chokidar_default.watch(this.projectRoot, {
|
|
3222
3349
|
ignored: (filePath) => {
|
|
3223
|
-
const relativePath =
|
|
3350
|
+
const relativePath = path5.relative(this.projectRoot, filePath);
|
|
3224
3351
|
if (!relativePath) return false;
|
|
3225
|
-
const pathParts = relativePath.split(
|
|
3352
|
+
const pathParts = relativePath.split(path5.sep);
|
|
3226
3353
|
for (const part of pathParts) {
|
|
3227
3354
|
if (part.startsWith(".") && part !== "." && part !== "..") {
|
|
3228
3355
|
return true;
|
|
@@ -3274,7 +3401,7 @@ var FileWatcher = class {
|
|
|
3274
3401
|
return;
|
|
3275
3402
|
}
|
|
3276
3403
|
const changes = Array.from(this.pendingChanges.entries()).map(
|
|
3277
|
-
([
|
|
3404
|
+
([path12, type]) => ({ path: path12, type })
|
|
3278
3405
|
);
|
|
3279
3406
|
this.pendingChanges.clear();
|
|
3280
3407
|
try {
|
|
@@ -3320,7 +3447,7 @@ var GitHeadWatcher = class {
|
|
|
3320
3447
|
this.onBranchChange = handler;
|
|
3321
3448
|
this.currentBranch = getCurrentBranch(this.projectRoot);
|
|
3322
3449
|
const headPath = getHeadPath(this.projectRoot);
|
|
3323
|
-
const refsPath =
|
|
3450
|
+
const refsPath = path5.join(this.projectRoot, ".git", "refs", "heads");
|
|
3324
3451
|
this.watcher = chokidar_default.watch([headPath, refsPath], {
|
|
3325
3452
|
persistent: true,
|
|
3326
3453
|
ignoreInitial: true,
|
|
@@ -3405,8 +3532,8 @@ function createWatcherWithIndexer(getIndexer2, projectRoot, config) {
|
|
|
3405
3532
|
var import_plugin = require("@opencode-ai/plugin");
|
|
3406
3533
|
|
|
3407
3534
|
// src/indexer/index.ts
|
|
3408
|
-
var
|
|
3409
|
-
var
|
|
3535
|
+
var import_fs6 = require("fs");
|
|
3536
|
+
var path8 = __toESM(require("path"), 1);
|
|
3410
3537
|
var import_perf_hooks = require("perf_hooks");
|
|
3411
3538
|
|
|
3412
3539
|
// node_modules/eventemitter3/index.mjs
|
|
@@ -3431,7 +3558,7 @@ function pTimeout(promise, options) {
|
|
|
3431
3558
|
} = options;
|
|
3432
3559
|
let timer;
|
|
3433
3560
|
let abortHandler;
|
|
3434
|
-
const wrappedPromise = new Promise((
|
|
3561
|
+
const wrappedPromise = new Promise((resolve9, reject) => {
|
|
3435
3562
|
if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
|
|
3436
3563
|
throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
|
|
3437
3564
|
}
|
|
@@ -3445,7 +3572,7 @@ function pTimeout(promise, options) {
|
|
|
3445
3572
|
};
|
|
3446
3573
|
signal.addEventListener("abort", abortHandler, { once: true });
|
|
3447
3574
|
}
|
|
3448
|
-
promise.then(
|
|
3575
|
+
promise.then(resolve9, reject);
|
|
3449
3576
|
if (milliseconds === Number.POSITIVE_INFINITY) {
|
|
3450
3577
|
return;
|
|
3451
3578
|
}
|
|
@@ -3453,7 +3580,7 @@ function pTimeout(promise, options) {
|
|
|
3453
3580
|
timer = customTimers.setTimeout.call(void 0, () => {
|
|
3454
3581
|
if (fallback) {
|
|
3455
3582
|
try {
|
|
3456
|
-
|
|
3583
|
+
resolve9(fallback());
|
|
3457
3584
|
} catch (error) {
|
|
3458
3585
|
reject(error);
|
|
3459
3586
|
}
|
|
@@ -3463,7 +3590,7 @@ function pTimeout(promise, options) {
|
|
|
3463
3590
|
promise.cancel();
|
|
3464
3591
|
}
|
|
3465
3592
|
if (message === false) {
|
|
3466
|
-
|
|
3593
|
+
resolve9();
|
|
3467
3594
|
} else if (message instanceof Error) {
|
|
3468
3595
|
reject(message);
|
|
3469
3596
|
} else {
|
|
@@ -3865,7 +3992,7 @@ var PQueue = class extends import_index.default {
|
|
|
3865
3992
|
// Assign unique ID if not provided
|
|
3866
3993
|
id: options.id ?? (this.#idAssigner++).toString()
|
|
3867
3994
|
};
|
|
3868
|
-
return new Promise((
|
|
3995
|
+
return new Promise((resolve9, reject) => {
|
|
3869
3996
|
const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
|
|
3870
3997
|
let cleanupQueueAbortHandler = () => void 0;
|
|
3871
3998
|
const run = async () => {
|
|
@@ -3905,7 +4032,7 @@ var PQueue = class extends import_index.default {
|
|
|
3905
4032
|
})]);
|
|
3906
4033
|
}
|
|
3907
4034
|
const result = await operation;
|
|
3908
|
-
|
|
4035
|
+
resolve9(result);
|
|
3909
4036
|
this.emit("completed", result);
|
|
3910
4037
|
} catch (error) {
|
|
3911
4038
|
reject(error);
|
|
@@ -4093,13 +4220,13 @@ var PQueue = class extends import_index.default {
|
|
|
4093
4220
|
});
|
|
4094
4221
|
}
|
|
4095
4222
|
async #onEvent(event, filter) {
|
|
4096
|
-
return new Promise((
|
|
4223
|
+
return new Promise((resolve9) => {
|
|
4097
4224
|
const listener = () => {
|
|
4098
4225
|
if (filter && !filter()) {
|
|
4099
4226
|
return;
|
|
4100
4227
|
}
|
|
4101
4228
|
this.off(event, listener);
|
|
4102
|
-
|
|
4229
|
+
resolve9();
|
|
4103
4230
|
};
|
|
4104
4231
|
this.on(event, listener);
|
|
4105
4232
|
});
|
|
@@ -4385,7 +4512,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
|
|
|
4385
4512
|
const finalDelay = Math.min(delayTime, remainingTime);
|
|
4386
4513
|
options.signal?.throwIfAborted();
|
|
4387
4514
|
if (finalDelay > 0) {
|
|
4388
|
-
await new Promise((
|
|
4515
|
+
await new Promise((resolve9, reject) => {
|
|
4389
4516
|
const onAbort = () => {
|
|
4390
4517
|
clearTimeout(timeoutToken);
|
|
4391
4518
|
options.signal?.removeEventListener("abort", onAbort);
|
|
@@ -4393,7 +4520,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
|
|
|
4393
4520
|
};
|
|
4394
4521
|
const timeoutToken = setTimeout(() => {
|
|
4395
4522
|
options.signal?.removeEventListener("abort", onAbort);
|
|
4396
|
-
|
|
4523
|
+
resolve9();
|
|
4397
4524
|
}, finalDelay);
|
|
4398
4525
|
if (options.unref) {
|
|
4399
4526
|
timeoutToken.unref?.();
|
|
@@ -4454,17 +4581,17 @@ async function pRetry(input, options = {}) {
|
|
|
4454
4581
|
}
|
|
4455
4582
|
|
|
4456
4583
|
// src/embeddings/detector.ts
|
|
4457
|
-
var
|
|
4458
|
-
var
|
|
4459
|
-
var
|
|
4584
|
+
var import_fs5 = require("fs");
|
|
4585
|
+
var path6 = __toESM(require("path"), 1);
|
|
4586
|
+
var os3 = __toESM(require("os"), 1);
|
|
4460
4587
|
function getOpenCodeAuthPath() {
|
|
4461
|
-
return
|
|
4588
|
+
return path6.join(os3.homedir(), ".local", "share", "opencode", "auth.json");
|
|
4462
4589
|
}
|
|
4463
4590
|
function loadOpenCodeAuth() {
|
|
4464
4591
|
const authPath = getOpenCodeAuthPath();
|
|
4465
4592
|
try {
|
|
4466
|
-
if ((0,
|
|
4467
|
-
return JSON.parse((0,
|
|
4593
|
+
if ((0, import_fs5.existsSync)(authPath)) {
|
|
4594
|
+
return JSON.parse((0, import_fs5.readFileSync)(authPath, "utf-8"));
|
|
4468
4595
|
}
|
|
4469
4596
|
} catch {
|
|
4470
4597
|
}
|
|
@@ -4497,7 +4624,7 @@ async function detectEmbeddingProvider(preferredProvider, model) {
|
|
|
4497
4624
|
);
|
|
4498
4625
|
}
|
|
4499
4626
|
async function tryDetectProvider() {
|
|
4500
|
-
for (const provider of
|
|
4627
|
+
for (const provider of autoDetectProviders) {
|
|
4501
4628
|
const credentials = await getProviderCredentials(provider);
|
|
4502
4629
|
if (credentials) {
|
|
4503
4630
|
return {
|
|
@@ -4508,7 +4635,7 @@ async function tryDetectProvider() {
|
|
|
4508
4635
|
}
|
|
4509
4636
|
}
|
|
4510
4637
|
throw new Error(
|
|
4511
|
-
`No embedding-capable provider found. Please authenticate with OpenCode using one of: ${
|
|
4638
|
+
`No embedding-capable provider found. Please authenticate with OpenCode using one of: ${autoDetectProviders.join(", ")}.`
|
|
4512
4639
|
);
|
|
4513
4640
|
}
|
|
4514
4641
|
async function getProviderCredentials(provider) {
|
|
@@ -4828,11 +4955,12 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider {
|
|
|
4828
4955
|
return this.modelInfo;
|
|
4829
4956
|
}
|
|
4830
4957
|
};
|
|
4831
|
-
var OllamaEmbeddingProvider = class {
|
|
4958
|
+
var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider {
|
|
4832
4959
|
constructor(credentials, modelInfo) {
|
|
4833
4960
|
this.credentials = credentials;
|
|
4834
4961
|
this.modelInfo = modelInfo;
|
|
4835
4962
|
}
|
|
4963
|
+
static MIN_TRUNCATION_CHARS = 512;
|
|
4836
4964
|
async embedQuery(query) {
|
|
4837
4965
|
const result = await this.embedBatch([query]);
|
|
4838
4966
|
return {
|
|
@@ -4847,30 +4975,103 @@ var OllamaEmbeddingProvider = class {
|
|
|
4847
4975
|
tokensUsed: result.totalTokensUsed
|
|
4848
4976
|
};
|
|
4849
4977
|
}
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4978
|
+
estimateTokens(text) {
|
|
4979
|
+
return Math.ceil(text.length / 4);
|
|
4980
|
+
}
|
|
4981
|
+
truncateToCharLimit(text, maxChars) {
|
|
4982
|
+
if (text.length <= maxChars) {
|
|
4983
|
+
return text;
|
|
4984
|
+
}
|
|
4985
|
+
return `${text.slice(0, Math.max(0, maxChars - 17))}
|
|
4986
|
+
... [truncated]`;
|
|
4987
|
+
}
|
|
4988
|
+
isContextLengthError(error) {
|
|
4989
|
+
const message = (error instanceof Error ? error.message : String(error)).toLowerCase();
|
|
4990
|
+
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");
|
|
4991
|
+
}
|
|
4992
|
+
buildTruncationCandidates(text) {
|
|
4993
|
+
const baseMaxChars = Math.max(1, this.modelInfo.maxTokens * 4);
|
|
4994
|
+
const candidateLimits = /* @__PURE__ */ new Set();
|
|
4995
|
+
const baselineLimit = text.length > baseMaxChars ? baseMaxChars : Math.max(
|
|
4996
|
+
_OllamaEmbeddingProvider.MIN_TRUNCATION_CHARS,
|
|
4997
|
+
Math.floor(text.length * 0.9)
|
|
4998
|
+
);
|
|
4999
|
+
if (baselineLimit < text.length) {
|
|
5000
|
+
candidateLimits.add(baselineLimit);
|
|
5001
|
+
}
|
|
5002
|
+
for (const factor of [0.75, 0.6, 0.45, 0.35, 0.25]) {
|
|
5003
|
+
const scaledLimit = Math.max(
|
|
5004
|
+
_OllamaEmbeddingProvider.MIN_TRUNCATION_CHARS,
|
|
5005
|
+
Math.floor(baselineLimit * factor)
|
|
5006
|
+
);
|
|
5007
|
+
if (scaledLimit < text.length) {
|
|
5008
|
+
candidateLimits.add(scaledLimit);
|
|
5009
|
+
}
|
|
5010
|
+
}
|
|
5011
|
+
candidateLimits.add(Math.min(text.length - 1, _OllamaEmbeddingProvider.MIN_TRUNCATION_CHARS));
|
|
5012
|
+
const candidates = [];
|
|
5013
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5014
|
+
for (const limit of [...candidateLimits].sort((a, b) => b - a)) {
|
|
5015
|
+
if (limit <= 0 || limit >= text.length) {
|
|
5016
|
+
continue;
|
|
5017
|
+
}
|
|
5018
|
+
const truncated = this.truncateToCharLimit(text, limit);
|
|
5019
|
+
if (truncated === text || seen.has(truncated)) {
|
|
5020
|
+
continue;
|
|
5021
|
+
}
|
|
5022
|
+
seen.add(truncated);
|
|
5023
|
+
candidates.push(truncated);
|
|
5024
|
+
}
|
|
5025
|
+
return candidates;
|
|
5026
|
+
}
|
|
5027
|
+
async embedSingleWithFallback(text) {
|
|
5028
|
+
try {
|
|
5029
|
+
return await this.embedSingle(text);
|
|
5030
|
+
} catch (error) {
|
|
5031
|
+
if (!this.isContextLengthError(error)) {
|
|
5032
|
+
throw error;
|
|
5033
|
+
}
|
|
5034
|
+
let lastError = error;
|
|
5035
|
+
for (const truncated of this.buildTruncationCandidates(text)) {
|
|
5036
|
+
try {
|
|
5037
|
+
return await this.embedSingle(truncated);
|
|
5038
|
+
} catch (retryError) {
|
|
5039
|
+
if (!this.isContextLengthError(retryError)) {
|
|
5040
|
+
throw retryError;
|
|
5041
|
+
}
|
|
5042
|
+
lastError = retryError;
|
|
4866
5043
|
}
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
5044
|
+
}
|
|
5045
|
+
throw lastError;
|
|
5046
|
+
}
|
|
5047
|
+
}
|
|
5048
|
+
async embedSingle(text) {
|
|
5049
|
+
const response = await fetch(`${this.credentials.baseUrl}/api/embeddings`, {
|
|
5050
|
+
method: "POST",
|
|
5051
|
+
headers: {
|
|
5052
|
+
"Content-Type": "application/json"
|
|
5053
|
+
},
|
|
5054
|
+
body: JSON.stringify({
|
|
5055
|
+
model: this.modelInfo.model,
|
|
5056
|
+
prompt: text,
|
|
5057
|
+
truncate: false
|
|
4872
5058
|
})
|
|
4873
|
-
);
|
|
5059
|
+
});
|
|
5060
|
+
if (!response.ok) {
|
|
5061
|
+
const error = await response.text();
|
|
5062
|
+
throw new Error(`Ollama embedding API error: ${response.status} - ${error}`);
|
|
5063
|
+
}
|
|
5064
|
+
const data = await response.json();
|
|
5065
|
+
return {
|
|
5066
|
+
embedding: data.embedding,
|
|
5067
|
+
tokensUsed: this.estimateTokens(text)
|
|
5068
|
+
};
|
|
5069
|
+
}
|
|
5070
|
+
async embedBatch(texts) {
|
|
5071
|
+
const results = [];
|
|
5072
|
+
for (const text of texts) {
|
|
5073
|
+
results.push(await this.embedSingleWithFallback(text));
|
|
5074
|
+
}
|
|
4874
5075
|
return {
|
|
4875
5076
|
embeddings: results.map((r) => r.embedding),
|
|
4876
5077
|
totalTokensUsed: results.reduce((sum, r) => sum + r.tokensUsed, 0)
|
|
@@ -5426,14 +5627,14 @@ function initializeLogger(config) {
|
|
|
5426
5627
|
}
|
|
5427
5628
|
|
|
5428
5629
|
// src/native/index.ts
|
|
5429
|
-
var
|
|
5430
|
-
var
|
|
5630
|
+
var path7 = __toESM(require("path"), 1);
|
|
5631
|
+
var os4 = __toESM(require("os"), 1);
|
|
5431
5632
|
var module2 = __toESM(require("module"), 1);
|
|
5432
5633
|
var import_url = require("url");
|
|
5433
5634
|
var import_meta = {};
|
|
5434
5635
|
function getNativeBinding() {
|
|
5435
|
-
const platform2 =
|
|
5436
|
-
const arch2 =
|
|
5636
|
+
const platform2 = os4.platform();
|
|
5637
|
+
const arch2 = os4.arch();
|
|
5437
5638
|
let bindingName;
|
|
5438
5639
|
if (platform2 === "darwin" && arch2 === "arm64") {
|
|
5439
5640
|
bindingName = "codebase-index-native.darwin-arm64.node";
|
|
@@ -5451,18 +5652,19 @@ function getNativeBinding() {
|
|
|
5451
5652
|
let currentDir;
|
|
5452
5653
|
let requireTarget;
|
|
5453
5654
|
if (typeof import_meta !== "undefined" && import_meta.url) {
|
|
5454
|
-
currentDir =
|
|
5655
|
+
currentDir = path7.dirname((0, import_url.fileURLToPath)(import_meta.url));
|
|
5455
5656
|
requireTarget = import_meta.url;
|
|
5456
5657
|
} else if (typeof __dirname !== "undefined") {
|
|
5457
5658
|
currentDir = __dirname;
|
|
5458
5659
|
requireTarget = __filename;
|
|
5459
5660
|
} else {
|
|
5460
5661
|
currentDir = process.cwd();
|
|
5461
|
-
requireTarget =
|
|
5662
|
+
requireTarget = path7.join(currentDir, "index.js");
|
|
5462
5663
|
}
|
|
5463
|
-
const
|
|
5464
|
-
const
|
|
5465
|
-
const
|
|
5664
|
+
const normalizedDir = currentDir.replace(/\\/g, "/");
|
|
5665
|
+
const isDevMode = normalizedDir.includes("/src/native") || currentDir.includes(path7.join("src", "native"));
|
|
5666
|
+
const packageRoot = isDevMode ? path7.resolve(currentDir, "../..") : path7.resolve(currentDir, "..");
|
|
5667
|
+
const nativePath = path7.join(packageRoot, "native", bindingName);
|
|
5466
5668
|
const require2 = module2.createRequire(requireTarget);
|
|
5467
5669
|
return require2(nativePath);
|
|
5468
5670
|
}
|
|
@@ -5493,11 +5695,20 @@ function createMockNativeBinding() {
|
|
|
5493
5695
|
constructor() {
|
|
5494
5696
|
throw error;
|
|
5495
5697
|
}
|
|
5698
|
+
serialize() {
|
|
5699
|
+
throw error;
|
|
5700
|
+
}
|
|
5701
|
+
deserialize() {
|
|
5702
|
+
throw error;
|
|
5703
|
+
}
|
|
5496
5704
|
},
|
|
5497
5705
|
Database: class {
|
|
5498
5706
|
constructor() {
|
|
5499
5707
|
throw error;
|
|
5500
5708
|
}
|
|
5709
|
+
close() {
|
|
5710
|
+
throw error;
|
|
5711
|
+
}
|
|
5501
5712
|
}
|
|
5502
5713
|
};
|
|
5503
5714
|
}
|
|
@@ -5630,7 +5841,7 @@ var MAX_SINGLE_CHUNK_TOKENS = 2e3;
|
|
|
5630
5841
|
function estimateTokens(text) {
|
|
5631
5842
|
return Math.ceil(text.length / CHARS_PER_TOKEN);
|
|
5632
5843
|
}
|
|
5633
|
-
function
|
|
5844
|
+
function getEmbeddingHeaderParts(chunk, filePath) {
|
|
5634
5845
|
const parts = [];
|
|
5635
5846
|
const fileName = filePath.split("/").pop() || filePath;
|
|
5636
5847
|
const dirPath = filePath.split("/").slice(-3, -1).join("/");
|
|
@@ -5677,23 +5888,52 @@ function createEmbeddingText(chunk, filePath) {
|
|
|
5677
5888
|
if (semanticHints.length > 0) {
|
|
5678
5889
|
parts.push(`Purpose: ${semanticHints.join(", ")}`);
|
|
5679
5890
|
}
|
|
5680
|
-
parts
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
const
|
|
5684
|
-
if (
|
|
5685
|
-
|
|
5891
|
+
return parts;
|
|
5892
|
+
}
|
|
5893
|
+
function buildEmbeddingText(headerParts, content, partIndex, partCount) {
|
|
5894
|
+
const parts = [...headerParts];
|
|
5895
|
+
if (partCount && partCount > 1 && partIndex) {
|
|
5896
|
+
parts.push(`Part ${partIndex}/${partCount}`);
|
|
5686
5897
|
}
|
|
5898
|
+
parts.push("");
|
|
5687
5899
|
parts.push(content);
|
|
5688
5900
|
return parts.join("\n");
|
|
5689
5901
|
}
|
|
5690
|
-
function
|
|
5902
|
+
function splitOversizedContent(content, maxContentChars) {
|
|
5903
|
+
if (content.length <= maxContentChars) {
|
|
5904
|
+
return [content];
|
|
5905
|
+
}
|
|
5906
|
+
const overlapChars = Math.max(CHARS_PER_TOKEN * 32, Math.min(Math.floor(maxContentChars * 0.15), CHARS_PER_TOKEN * 128));
|
|
5907
|
+
const stepChars = Math.max(1, maxContentChars - overlapChars);
|
|
5908
|
+
const segments = [];
|
|
5909
|
+
for (let start = 0; start < content.length; start += stepChars) {
|
|
5910
|
+
const end = Math.min(content.length, start + maxContentChars);
|
|
5911
|
+
segments.push(content.slice(start, end));
|
|
5912
|
+
if (end >= content.length) {
|
|
5913
|
+
break;
|
|
5914
|
+
}
|
|
5915
|
+
}
|
|
5916
|
+
return segments;
|
|
5917
|
+
}
|
|
5918
|
+
function createEmbeddingTexts(chunk, filePath, maxChunkTokens = MAX_SINGLE_CHUNK_TOKENS) {
|
|
5919
|
+
const headerParts = getEmbeddingHeaderParts(chunk, filePath);
|
|
5920
|
+
const headerLength = buildEmbeddingText(headerParts, "", 1, 9).length;
|
|
5921
|
+
const maxContentChars = Math.max(1, maxChunkTokens * CHARS_PER_TOKEN - headerLength);
|
|
5922
|
+
const segments = splitOversizedContent(chunk.content, maxContentChars);
|
|
5923
|
+
if (segments.length === 1) {
|
|
5924
|
+
return [buildEmbeddingText(headerParts, segments[0])];
|
|
5925
|
+
}
|
|
5926
|
+
return segments.map((segment, index) => buildEmbeddingText(headerParts, segment, index + 1, segments.length));
|
|
5927
|
+
}
|
|
5928
|
+
function createDynamicBatches(chunks, options = {}) {
|
|
5691
5929
|
const batches = [];
|
|
5692
5930
|
let currentBatch = [];
|
|
5693
5931
|
let currentTokens = 0;
|
|
5932
|
+
const maxBatchTokens = Math.max(1, options.maxBatchTokens ?? MAX_BATCH_TOKENS);
|
|
5933
|
+
const maxBatchItems = Math.max(1, options.maxBatchItems ?? Number.MAX_SAFE_INTEGER);
|
|
5694
5934
|
for (const chunk of chunks) {
|
|
5695
|
-
const chunkTokens = estimateTokens(chunk.text);
|
|
5696
|
-
if (currentBatch.length > 0 && currentTokens + chunkTokens >
|
|
5935
|
+
const chunkTokens = chunk.tokenCount ?? estimateTokens(chunk.text);
|
|
5936
|
+
if (currentBatch.length > 0 && (currentTokens + chunkTokens > maxBatchTokens || currentBatch.length >= maxBatchItems)) {
|
|
5697
5937
|
batches.push(currentBatch);
|
|
5698
5938
|
currentBatch = [];
|
|
5699
5939
|
currentTokens = 0;
|
|
@@ -5812,6 +6052,12 @@ var InvertedIndex = class {
|
|
|
5812
6052
|
save() {
|
|
5813
6053
|
this.inner.save();
|
|
5814
6054
|
}
|
|
6055
|
+
serialize() {
|
|
6056
|
+
return this.inner.serialize();
|
|
6057
|
+
}
|
|
6058
|
+
deserialize(json) {
|
|
6059
|
+
this.inner.deserialize(json);
|
|
6060
|
+
}
|
|
5815
6061
|
addChunk(chunkId, content) {
|
|
5816
6062
|
this.inner.addChunk(chunkId, content);
|
|
5817
6063
|
}
|
|
@@ -5838,158 +6084,263 @@ var InvertedIndex = class {
|
|
|
5838
6084
|
};
|
|
5839
6085
|
var Database = class {
|
|
5840
6086
|
inner;
|
|
6087
|
+
closed = false;
|
|
5841
6088
|
constructor(dbPath) {
|
|
5842
6089
|
this.inner = new native.Database(dbPath);
|
|
5843
6090
|
}
|
|
6091
|
+
throwIfClosed() {
|
|
6092
|
+
if (this.closed) {
|
|
6093
|
+
throw new Error("Database is closed");
|
|
6094
|
+
}
|
|
6095
|
+
}
|
|
6096
|
+
close() {
|
|
6097
|
+
if (this.closed) {
|
|
6098
|
+
return;
|
|
6099
|
+
}
|
|
6100
|
+
if (typeof this.inner.close === "function") {
|
|
6101
|
+
this.inner.close();
|
|
6102
|
+
}
|
|
6103
|
+
this.closed = true;
|
|
6104
|
+
}
|
|
5844
6105
|
embeddingExists(contentHash) {
|
|
6106
|
+
this.throwIfClosed();
|
|
5845
6107
|
return this.inner.embeddingExists(contentHash);
|
|
5846
6108
|
}
|
|
5847
6109
|
getEmbedding(contentHash) {
|
|
6110
|
+
this.throwIfClosed();
|
|
5848
6111
|
return this.inner.getEmbedding(contentHash) ?? null;
|
|
5849
6112
|
}
|
|
5850
6113
|
upsertEmbedding(contentHash, embedding, chunkText, model) {
|
|
6114
|
+
this.throwIfClosed();
|
|
5851
6115
|
this.inner.upsertEmbedding(contentHash, embedding, chunkText, model);
|
|
5852
6116
|
}
|
|
5853
6117
|
upsertEmbeddingsBatch(items) {
|
|
6118
|
+
this.throwIfClosed();
|
|
5854
6119
|
if (items.length === 0) return;
|
|
5855
6120
|
this.inner.upsertEmbeddingsBatch(items);
|
|
5856
6121
|
}
|
|
5857
6122
|
getMissingEmbeddings(contentHashes) {
|
|
6123
|
+
this.throwIfClosed();
|
|
5858
6124
|
return this.inner.getMissingEmbeddings(contentHashes);
|
|
5859
6125
|
}
|
|
5860
6126
|
upsertChunk(chunk) {
|
|
6127
|
+
this.throwIfClosed();
|
|
5861
6128
|
this.inner.upsertChunk(chunk);
|
|
5862
6129
|
}
|
|
5863
6130
|
upsertChunksBatch(chunks) {
|
|
6131
|
+
this.throwIfClosed();
|
|
5864
6132
|
if (chunks.length === 0) return;
|
|
5865
6133
|
this.inner.upsertChunksBatch(chunks);
|
|
5866
6134
|
}
|
|
5867
6135
|
getChunk(chunkId) {
|
|
6136
|
+
this.throwIfClosed();
|
|
5868
6137
|
return this.inner.getChunk(chunkId) ?? null;
|
|
5869
6138
|
}
|
|
5870
6139
|
getChunksByFile(filePath) {
|
|
6140
|
+
this.throwIfClosed();
|
|
5871
6141
|
return this.inner.getChunksByFile(filePath);
|
|
5872
6142
|
}
|
|
5873
6143
|
getChunksByName(name) {
|
|
6144
|
+
this.throwIfClosed();
|
|
5874
6145
|
return this.inner.getChunksByName(name);
|
|
5875
6146
|
}
|
|
5876
6147
|
getChunksByNameCi(name) {
|
|
6148
|
+
this.throwIfClosed();
|
|
5877
6149
|
return this.inner.getChunksByNameCi(name);
|
|
5878
6150
|
}
|
|
5879
6151
|
deleteChunksByFile(filePath) {
|
|
6152
|
+
this.throwIfClosed();
|
|
5880
6153
|
return this.inner.deleteChunksByFile(filePath);
|
|
5881
6154
|
}
|
|
6155
|
+
deleteChunksByIds(chunkIds) {
|
|
6156
|
+
this.throwIfClosed();
|
|
6157
|
+
if (chunkIds.length === 0) return 0;
|
|
6158
|
+
return this.inner.deleteChunksByIds(chunkIds);
|
|
6159
|
+
}
|
|
5882
6160
|
addChunksToBranch(branch, chunkIds) {
|
|
6161
|
+
this.throwIfClosed();
|
|
5883
6162
|
this.inner.addChunksToBranch(branch, chunkIds);
|
|
5884
6163
|
}
|
|
5885
6164
|
addChunksToBranchBatch(branch, chunkIds) {
|
|
6165
|
+
this.throwIfClosed();
|
|
5886
6166
|
if (chunkIds.length === 0) return;
|
|
5887
6167
|
this.inner.addChunksToBranchBatch(branch, chunkIds);
|
|
5888
6168
|
}
|
|
5889
6169
|
clearBranch(branch) {
|
|
6170
|
+
this.throwIfClosed();
|
|
5890
6171
|
return this.inner.clearBranch(branch);
|
|
5891
6172
|
}
|
|
6173
|
+
deleteBranchChunksByChunkIds(chunkIds) {
|
|
6174
|
+
this.throwIfClosed();
|
|
6175
|
+
if (chunkIds.length === 0) return 0;
|
|
6176
|
+
return this.inner.deleteBranchChunksByChunkIds(chunkIds);
|
|
6177
|
+
}
|
|
6178
|
+
deleteBranchChunksForBranch(branch, chunkIds) {
|
|
6179
|
+
this.throwIfClosed();
|
|
6180
|
+
if (chunkIds.length === 0) return 0;
|
|
6181
|
+
return this.inner.deleteBranchChunksForBranch(branch, chunkIds);
|
|
6182
|
+
}
|
|
5892
6183
|
getBranchChunkIds(branch) {
|
|
6184
|
+
this.throwIfClosed();
|
|
5893
6185
|
return this.inner.getBranchChunkIds(branch);
|
|
5894
6186
|
}
|
|
5895
6187
|
getBranchDelta(branch, baseBranch) {
|
|
6188
|
+
this.throwIfClosed();
|
|
5896
6189
|
return this.inner.getBranchDelta(branch, baseBranch);
|
|
5897
6190
|
}
|
|
6191
|
+
getReferencedChunkIds(chunkIds) {
|
|
6192
|
+
this.throwIfClosed();
|
|
6193
|
+
if (chunkIds.length === 0) return [];
|
|
6194
|
+
return this.inner.getReferencedChunkIds(chunkIds);
|
|
6195
|
+
}
|
|
5898
6196
|
chunkExistsOnBranch(branch, chunkId) {
|
|
6197
|
+
this.throwIfClosed();
|
|
5899
6198
|
return this.inner.chunkExistsOnBranch(branch, chunkId);
|
|
5900
6199
|
}
|
|
5901
6200
|
getAllBranches() {
|
|
6201
|
+
this.throwIfClosed();
|
|
5902
6202
|
return this.inner.getAllBranches();
|
|
5903
6203
|
}
|
|
5904
6204
|
getMetadata(key) {
|
|
6205
|
+
this.throwIfClosed();
|
|
5905
6206
|
return this.inner.getMetadata(key) ?? null;
|
|
5906
6207
|
}
|
|
5907
6208
|
setMetadata(key, value) {
|
|
6209
|
+
this.throwIfClosed();
|
|
5908
6210
|
this.inner.setMetadata(key, value);
|
|
5909
6211
|
}
|
|
5910
6212
|
deleteMetadata(key) {
|
|
6213
|
+
this.throwIfClosed();
|
|
5911
6214
|
return this.inner.deleteMetadata(key);
|
|
5912
6215
|
}
|
|
6216
|
+
clearAllIndexedData() {
|
|
6217
|
+
this.throwIfClosed();
|
|
6218
|
+
this.inner.clearAllIndexedData();
|
|
6219
|
+
}
|
|
6220
|
+
clearCallEdgeTargetsForSymbols(symbolIds) {
|
|
6221
|
+
this.throwIfClosed();
|
|
6222
|
+
if (symbolIds.length === 0) return 0;
|
|
6223
|
+
return this.inner.clearCallEdgeTargetsForSymbols(symbolIds);
|
|
6224
|
+
}
|
|
5913
6225
|
gcOrphanEmbeddings() {
|
|
6226
|
+
this.throwIfClosed();
|
|
5914
6227
|
return this.inner.gcOrphanEmbeddings();
|
|
5915
6228
|
}
|
|
5916
6229
|
gcOrphanChunks() {
|
|
6230
|
+
this.throwIfClosed();
|
|
5917
6231
|
return this.inner.gcOrphanChunks();
|
|
5918
6232
|
}
|
|
5919
6233
|
getStats() {
|
|
6234
|
+
this.throwIfClosed();
|
|
5920
6235
|
return this.inner.getStats();
|
|
5921
6236
|
}
|
|
5922
6237
|
// ── Symbol methods ──────────────────────────────────────────────
|
|
5923
6238
|
upsertSymbol(symbol) {
|
|
6239
|
+
this.throwIfClosed();
|
|
5924
6240
|
this.inner.upsertSymbol(symbol);
|
|
5925
6241
|
}
|
|
5926
6242
|
upsertSymbolsBatch(symbols) {
|
|
6243
|
+
this.throwIfClosed();
|
|
5927
6244
|
if (symbols.length === 0) return;
|
|
5928
6245
|
this.inner.upsertSymbolsBatch(symbols);
|
|
5929
6246
|
}
|
|
5930
6247
|
getSymbolsByFile(filePath) {
|
|
6248
|
+
this.throwIfClosed();
|
|
5931
6249
|
return this.inner.getSymbolsByFile(filePath);
|
|
5932
6250
|
}
|
|
5933
6251
|
getSymbolByName(name, filePath) {
|
|
6252
|
+
this.throwIfClosed();
|
|
5934
6253
|
return this.inner.getSymbolByName(name, filePath) ?? null;
|
|
5935
6254
|
}
|
|
5936
6255
|
getSymbolsByName(name) {
|
|
6256
|
+
this.throwIfClosed();
|
|
5937
6257
|
return this.inner.getSymbolsByName(name);
|
|
5938
6258
|
}
|
|
5939
6259
|
getSymbolsByNameCi(name) {
|
|
6260
|
+
this.throwIfClosed();
|
|
5940
6261
|
return this.inner.getSymbolsByNameCi(name);
|
|
5941
6262
|
}
|
|
5942
6263
|
deleteSymbolsByFile(filePath) {
|
|
6264
|
+
this.throwIfClosed();
|
|
5943
6265
|
return this.inner.deleteSymbolsByFile(filePath);
|
|
5944
6266
|
}
|
|
5945
6267
|
// ── Call Edge methods ────────────────────────────────────────────
|
|
5946
6268
|
upsertCallEdge(edge) {
|
|
6269
|
+
this.throwIfClosed();
|
|
5947
6270
|
this.inner.upsertCallEdge(edge);
|
|
5948
6271
|
}
|
|
5949
6272
|
upsertCallEdgesBatch(edges) {
|
|
6273
|
+
this.throwIfClosed();
|
|
5950
6274
|
if (edges.length === 0) return;
|
|
5951
6275
|
this.inner.upsertCallEdgesBatch(edges);
|
|
5952
6276
|
}
|
|
5953
6277
|
getCallers(targetName, branch) {
|
|
6278
|
+
this.throwIfClosed();
|
|
5954
6279
|
return this.inner.getCallers(targetName, branch);
|
|
5955
6280
|
}
|
|
5956
6281
|
getCallersWithContext(targetName, branch) {
|
|
6282
|
+
this.throwIfClosed();
|
|
5957
6283
|
return this.inner.getCallersWithContext(targetName, branch);
|
|
5958
6284
|
}
|
|
5959
6285
|
getCallees(symbolId, branch) {
|
|
6286
|
+
this.throwIfClosed();
|
|
5960
6287
|
return this.inner.getCallees(symbolId, branch);
|
|
5961
6288
|
}
|
|
5962
6289
|
deleteCallEdgesByFile(filePath) {
|
|
6290
|
+
this.throwIfClosed();
|
|
5963
6291
|
return this.inner.deleteCallEdgesByFile(filePath);
|
|
5964
6292
|
}
|
|
5965
6293
|
resolveCallEdge(edgeId, toSymbolId) {
|
|
6294
|
+
this.throwIfClosed();
|
|
5966
6295
|
this.inner.resolveCallEdge(edgeId, toSymbolId);
|
|
5967
6296
|
}
|
|
5968
6297
|
// ── Branch Symbol methods ────────────────────────────────────────
|
|
5969
6298
|
addSymbolsToBranch(branch, symbolIds) {
|
|
6299
|
+
this.throwIfClosed();
|
|
5970
6300
|
this.inner.addSymbolsToBranch(branch, symbolIds);
|
|
5971
6301
|
}
|
|
5972
6302
|
addSymbolsToBranchBatch(branch, symbolIds) {
|
|
6303
|
+
this.throwIfClosed();
|
|
5973
6304
|
if (symbolIds.length === 0) return;
|
|
5974
6305
|
this.inner.addSymbolsToBranchBatch(branch, symbolIds);
|
|
5975
6306
|
}
|
|
5976
6307
|
getBranchSymbolIds(branch) {
|
|
6308
|
+
this.throwIfClosed();
|
|
5977
6309
|
return this.inner.getBranchSymbolIds(branch);
|
|
5978
6310
|
}
|
|
5979
6311
|
clearBranchSymbols(branch) {
|
|
6312
|
+
this.throwIfClosed();
|
|
5980
6313
|
return this.inner.clearBranchSymbols(branch);
|
|
5981
6314
|
}
|
|
6315
|
+
getReferencedSymbolIds(symbolIds) {
|
|
6316
|
+
this.throwIfClosed();
|
|
6317
|
+
if (symbolIds.length === 0) return [];
|
|
6318
|
+
return this.inner.getReferencedSymbolIds(symbolIds);
|
|
6319
|
+
}
|
|
6320
|
+
deleteBranchSymbolsBySymbolIds(symbolIds) {
|
|
6321
|
+
this.throwIfClosed();
|
|
6322
|
+
if (symbolIds.length === 0) return 0;
|
|
6323
|
+
return this.inner.deleteBranchSymbolsBySymbolIds(symbolIds);
|
|
6324
|
+
}
|
|
6325
|
+
deleteBranchSymbolsForBranch(branch, symbolIds) {
|
|
6326
|
+
this.throwIfClosed();
|
|
6327
|
+
if (symbolIds.length === 0) return 0;
|
|
6328
|
+
return this.inner.deleteBranchSymbolsForBranch(branch, symbolIds);
|
|
6329
|
+
}
|
|
5982
6330
|
// ── GC methods for symbols/edges ─────────────────────────────────
|
|
5983
6331
|
gcOrphanSymbols() {
|
|
6332
|
+
this.throwIfClosed();
|
|
5984
6333
|
return this.inner.gcOrphanSymbols();
|
|
5985
6334
|
}
|
|
5986
6335
|
gcOrphanCallEdges() {
|
|
6336
|
+
this.throwIfClosed();
|
|
5987
6337
|
return this.inner.gcOrphanCallEdges();
|
|
5988
6338
|
}
|
|
5989
6339
|
};
|
|
5990
6340
|
|
|
5991
6341
|
// src/indexer/index.ts
|
|
5992
|
-
var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php"]);
|
|
6342
|
+
var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig"]);
|
|
6343
|
+
var CASE_INSENSITIVE_LANGUAGES = /* @__PURE__ */ new Set(["apex"]);
|
|
5993
6344
|
var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
5994
6345
|
"function_declaration",
|
|
5995
6346
|
"function",
|
|
@@ -6011,7 +6362,11 @@ var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
|
6011
6362
|
"enum_item",
|
|
6012
6363
|
"trait_item",
|
|
6013
6364
|
"mod_item",
|
|
6014
|
-
"trait_declaration"
|
|
6365
|
+
"trait_declaration",
|
|
6366
|
+
"trigger_declaration",
|
|
6367
|
+
"test_declaration",
|
|
6368
|
+
"struct_declaration",
|
|
6369
|
+
"union_declaration"
|
|
6015
6370
|
]);
|
|
6016
6371
|
function float32ArrayToBuffer(arr) {
|
|
6017
6372
|
const float32 = new Float32Array(arr);
|
|
@@ -6036,12 +6391,192 @@ function isRateLimitError(error) {
|
|
|
6036
6391
|
const message = getErrorMessage(error);
|
|
6037
6392
|
return message.includes("429") || message.toLowerCase().includes("rate limit") || message.toLowerCase().includes("too many requests");
|
|
6038
6393
|
}
|
|
6394
|
+
function getSafeEmbeddingChunkTokenLimit(provider) {
|
|
6395
|
+
const providerMaxTokens = provider.modelInfo.maxTokens;
|
|
6396
|
+
const maxChunkTokens = Math.max(256, Math.floor(providerMaxTokens * 0.75));
|
|
6397
|
+
return Math.min(2e3, maxChunkTokens);
|
|
6398
|
+
}
|
|
6399
|
+
function getDynamicBatchOptions(provider) {
|
|
6400
|
+
if (provider.provider === "ollama") {
|
|
6401
|
+
return {
|
|
6402
|
+
maxBatchTokens: provider.modelInfo.maxTokens,
|
|
6403
|
+
maxBatchItems: 1
|
|
6404
|
+
};
|
|
6405
|
+
}
|
|
6406
|
+
return {};
|
|
6407
|
+
}
|
|
6408
|
+
function isSqliteCorruptionError(error) {
|
|
6409
|
+
const message = getErrorMessage(error).toLowerCase();
|
|
6410
|
+
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");
|
|
6411
|
+
}
|
|
6412
|
+
var STARTUP_WARNING_METADATA_KEY = "index.startupWarning";
|
|
6039
6413
|
var INDEX_METADATA_VERSION = "1";
|
|
6414
|
+
var EMBEDDING_STRATEGY_VERSION = "2";
|
|
6040
6415
|
var RANKING_TOKEN_CACHE_LIMIT = 4096;
|
|
6416
|
+
var RANK_HYBRID_CACHE_LIMIT = 256;
|
|
6417
|
+
function createPendingChunkStorageText(texts) {
|
|
6418
|
+
const primaryText = texts[0]?.text ?? "";
|
|
6419
|
+
if (texts.length <= 1) {
|
|
6420
|
+
return primaryText;
|
|
6421
|
+
}
|
|
6422
|
+
return `${primaryText}
|
|
6423
|
+
|
|
6424
|
+
... [split into ${texts.length} parts for embedding]`;
|
|
6425
|
+
}
|
|
6426
|
+
function normalizePendingChunk(rawChunk, maxChunkTokens) {
|
|
6427
|
+
if (!rawChunk || typeof rawChunk !== "object") {
|
|
6428
|
+
return null;
|
|
6429
|
+
}
|
|
6430
|
+
const chunk = rawChunk;
|
|
6431
|
+
if (typeof chunk.id !== "string" || typeof chunk.contentHash !== "string" || !chunk.metadata || typeof chunk.metadata !== "object") {
|
|
6432
|
+
return null;
|
|
6433
|
+
}
|
|
6434
|
+
const texts = Array.isArray(chunk.texts) ? chunk.texts.map((entry) => {
|
|
6435
|
+
if (!entry || typeof entry.text !== "string") {
|
|
6436
|
+
return null;
|
|
6437
|
+
}
|
|
6438
|
+
return {
|
|
6439
|
+
text: entry.text,
|
|
6440
|
+
tokenCount: typeof entry.tokenCount === "number" && Number.isFinite(entry.tokenCount) ? entry.tokenCount : estimateTokens(entry.text)
|
|
6441
|
+
};
|
|
6442
|
+
}).filter((entry) => entry !== null) : [];
|
|
6443
|
+
if (texts.length === 0 && typeof chunk.text === "string") {
|
|
6444
|
+
if (typeof chunk.content === "string" && chunk.content.length > 0 && chunk.metadata && typeof chunk.metadata === "object") {
|
|
6445
|
+
const metadata = chunk.metadata;
|
|
6446
|
+
const rebuiltChunk = {
|
|
6447
|
+
content: chunk.content,
|
|
6448
|
+
startLine: typeof metadata.startLine === "number" ? metadata.startLine : 1,
|
|
6449
|
+
endLine: typeof metadata.endLine === "number" ? metadata.endLine : 1,
|
|
6450
|
+
chunkType: typeof metadata.chunkType === "string" ? metadata.chunkType : "other",
|
|
6451
|
+
name: typeof metadata.name === "string" ? metadata.name : void 0,
|
|
6452
|
+
language: typeof metadata.language === "string" ? metadata.language : "text"
|
|
6453
|
+
};
|
|
6454
|
+
const filePath = typeof metadata.filePath === "string" ? metadata.filePath : "unknown";
|
|
6455
|
+
texts.push(
|
|
6456
|
+
...createEmbeddingTexts(rebuiltChunk, filePath, maxChunkTokens).map((text) => ({
|
|
6457
|
+
text,
|
|
6458
|
+
tokenCount: estimateTokens(text)
|
|
6459
|
+
}))
|
|
6460
|
+
);
|
|
6461
|
+
} else {
|
|
6462
|
+
texts.push({
|
|
6463
|
+
text: chunk.text,
|
|
6464
|
+
tokenCount: estimateTokens(chunk.text)
|
|
6465
|
+
});
|
|
6466
|
+
}
|
|
6467
|
+
}
|
|
6468
|
+
if (texts.length === 0) {
|
|
6469
|
+
return null;
|
|
6470
|
+
}
|
|
6471
|
+
return {
|
|
6472
|
+
id: chunk.id,
|
|
6473
|
+
texts,
|
|
6474
|
+
storageText: typeof chunk.storageText === "string" ? chunk.storageText : createPendingChunkStorageText(texts),
|
|
6475
|
+
content: typeof chunk.content === "string" ? chunk.content : "",
|
|
6476
|
+
contentHash: chunk.contentHash,
|
|
6477
|
+
metadata: chunk.metadata
|
|
6478
|
+
};
|
|
6479
|
+
}
|
|
6480
|
+
function getPendingChunkFilePath(rawChunk) {
|
|
6481
|
+
if (!rawChunk || typeof rawChunk !== "object") {
|
|
6482
|
+
return null;
|
|
6483
|
+
}
|
|
6484
|
+
const chunk = rawChunk;
|
|
6485
|
+
if (!chunk.metadata || typeof chunk.metadata !== "object") {
|
|
6486
|
+
return null;
|
|
6487
|
+
}
|
|
6488
|
+
const metadata = chunk.metadata;
|
|
6489
|
+
return typeof metadata.filePath === "string" ? metadata.filePath : null;
|
|
6490
|
+
}
|
|
6491
|
+
function normalizeFailedBatch(batch, maxChunkTokens) {
|
|
6492
|
+
const chunks = batch.chunks.map((chunk) => normalizePendingChunk(chunk, maxChunkTokens)).filter((chunk) => chunk !== null);
|
|
6493
|
+
if (chunks.length === 0) {
|
|
6494
|
+
return null;
|
|
6495
|
+
}
|
|
6496
|
+
return {
|
|
6497
|
+
chunks,
|
|
6498
|
+
error: batch.error,
|
|
6499
|
+
attemptCount: batch.attemptCount,
|
|
6500
|
+
lastAttempt: batch.lastAttempt
|
|
6501
|
+
};
|
|
6502
|
+
}
|
|
6503
|
+
function createPendingEmbeddingRequests(chunks) {
|
|
6504
|
+
return chunks.flatMap(
|
|
6505
|
+
(chunk) => chunk.texts.map((textPart, partIndex) => ({
|
|
6506
|
+
chunk,
|
|
6507
|
+
partIndex,
|
|
6508
|
+
text: textPart.text,
|
|
6509
|
+
tokenCount: textPart.tokenCount
|
|
6510
|
+
}))
|
|
6511
|
+
);
|
|
6512
|
+
}
|
|
6513
|
+
function createPendingEmbeddingRequestBatches(chunks, options = {}) {
|
|
6514
|
+
return createDynamicBatches(createPendingEmbeddingRequests(chunks), options);
|
|
6515
|
+
}
|
|
6516
|
+
function getUniquePendingChunksFromRequests(requests) {
|
|
6517
|
+
const uniqueChunks = /* @__PURE__ */ new Map();
|
|
6518
|
+
for (const request of requests) {
|
|
6519
|
+
uniqueChunks.set(request.chunk.id, request.chunk);
|
|
6520
|
+
}
|
|
6521
|
+
return Array.from(uniqueChunks.values());
|
|
6522
|
+
}
|
|
6523
|
+
function coalesceFailedBatches(batches) {
|
|
6524
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
6525
|
+
for (const batch of batches) {
|
|
6526
|
+
const key = `${batch.attemptCount}:${batch.lastAttempt}:${batch.error}`;
|
|
6527
|
+
const existing = grouped.get(key);
|
|
6528
|
+
if (!existing) {
|
|
6529
|
+
grouped.set(key, {
|
|
6530
|
+
...batch,
|
|
6531
|
+
chunks: [...batch.chunks]
|
|
6532
|
+
});
|
|
6533
|
+
continue;
|
|
6534
|
+
}
|
|
6535
|
+
existing.chunks.push(...batch.chunks);
|
|
6536
|
+
}
|
|
6537
|
+
return Array.from(grouped.values());
|
|
6538
|
+
}
|
|
6539
|
+
function poolEmbeddingVectors(vectors, weights) {
|
|
6540
|
+
const firstVector = vectors[0];
|
|
6541
|
+
if (!firstVector) {
|
|
6542
|
+
return [];
|
|
6543
|
+
}
|
|
6544
|
+
const pooled = new Array(firstVector.length).fill(0);
|
|
6545
|
+
let totalWeight = 0;
|
|
6546
|
+
for (let index = 0; index < vectors.length; index++) {
|
|
6547
|
+
const vector = vectors[index];
|
|
6548
|
+
const weight = Math.max(1, weights[index] ?? 1);
|
|
6549
|
+
totalWeight += weight;
|
|
6550
|
+
for (let dimension = 0; dimension < vector.length; dimension++) {
|
|
6551
|
+
pooled[dimension] += vector[dimension] * weight;
|
|
6552
|
+
}
|
|
6553
|
+
}
|
|
6554
|
+
if (totalWeight === 0) {
|
|
6555
|
+
return firstVector;
|
|
6556
|
+
}
|
|
6557
|
+
return pooled.map((value) => value / totalWeight);
|
|
6558
|
+
}
|
|
6559
|
+
function hasAllEmbeddingParts(parts, expectedPartCount) {
|
|
6560
|
+
if (parts.length !== expectedPartCount) {
|
|
6561
|
+
return false;
|
|
6562
|
+
}
|
|
6563
|
+
for (let index = 0; index < expectedPartCount; index++) {
|
|
6564
|
+
if (parts[index] === void 0) {
|
|
6565
|
+
return false;
|
|
6566
|
+
}
|
|
6567
|
+
}
|
|
6568
|
+
return true;
|
|
6569
|
+
}
|
|
6570
|
+
function isPathWithinRoot(filePath, rootPath) {
|
|
6571
|
+
const normalizedFilePath = path8.resolve(filePath);
|
|
6572
|
+
const normalizedRoot = path8.resolve(rootPath);
|
|
6573
|
+
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path8.sep}`);
|
|
6574
|
+
}
|
|
6041
6575
|
var rankingQueryTokenCache = /* @__PURE__ */ new Map();
|
|
6042
6576
|
var rankingNameTokenCache = /* @__PURE__ */ new Map();
|
|
6043
6577
|
var rankingPathTokenCache = /* @__PURE__ */ new Map();
|
|
6044
6578
|
var rankingTextTokenCache = /* @__PURE__ */ new Map();
|
|
6579
|
+
var rankHybridResultsCache = /* @__PURE__ */ new WeakMap();
|
|
6045
6580
|
var STOPWORDS = /* @__PURE__ */ new Set([
|
|
6046
6581
|
"the",
|
|
6047
6582
|
"and",
|
|
@@ -6257,7 +6792,16 @@ function classifyExternalRerankBand(candidate, preferSourcePaths, docIntent) {
|
|
|
6257
6792
|
function classifyQueryIntent(tokens) {
|
|
6258
6793
|
const sourceIntentHits = tokens.filter((t) => SOURCE_INTENT_HINTS.has(t)).length;
|
|
6259
6794
|
const docTestIntentHits = tokens.filter((t) => DOC_TEST_INTENT_HINTS.has(t)).length;
|
|
6260
|
-
|
|
6795
|
+
if (sourceIntentHits === 0 && docTestIntentHits === 0) {
|
|
6796
|
+
return "neutral";
|
|
6797
|
+
}
|
|
6798
|
+
if (sourceIntentHits > docTestIntentHits) {
|
|
6799
|
+
return "source";
|
|
6800
|
+
}
|
|
6801
|
+
if (docTestIntentHits > sourceIntentHits) {
|
|
6802
|
+
return "doc_test";
|
|
6803
|
+
}
|
|
6804
|
+
return "neutral";
|
|
6261
6805
|
}
|
|
6262
6806
|
function classifyQueryIntentRaw(query) {
|
|
6263
6807
|
const lowerQuery = query.toLowerCase();
|
|
@@ -6279,7 +6823,7 @@ function classifyQueryIntentRaw(query) {
|
|
|
6279
6823
|
if (docTestRawHits > sourceRawHits) {
|
|
6280
6824
|
return "doc_test";
|
|
6281
6825
|
}
|
|
6282
|
-
if (sourceRawHits > docTestRawHits) {
|
|
6826
|
+
if (sourceRawHits > docTestRawHits && sourceRawHits > 0) {
|
|
6283
6827
|
return "source";
|
|
6284
6828
|
}
|
|
6285
6829
|
const hasWhereIsPattern = /\bwhere\s+is\b/.test(lowerQuery);
|
|
@@ -6546,9 +7090,8 @@ function rerankResults(query, candidates, rerankTopN, options) {
|
|
|
6546
7090
|
return candidates;
|
|
6547
7091
|
}
|
|
6548
7092
|
const queryTokenList = Array.from(queryTokens);
|
|
6549
|
-
const intent = classifyQueryIntentRaw(query);
|
|
6550
7093
|
const docIntent = classifyDocIntent(queryTokenList);
|
|
6551
|
-
const preferSourcePaths = options?.prioritizeSourcePaths ??
|
|
7094
|
+
const preferSourcePaths = options?.prioritizeSourcePaths ?? classifyQueryIntentRaw(query) === "source";
|
|
6552
7095
|
const identifierHints = extractIdentifierHints(query);
|
|
6553
7096
|
const head = candidates.slice(0, topN).map((candidate, idx) => {
|
|
6554
7097
|
const pathTokens = splitPathTokens(candidate.metadata.filePath);
|
|
@@ -6698,13 +7241,38 @@ function buildDiversityKey(metadata) {
|
|
|
6698
7241
|
return normalizedPath;
|
|
6699
7242
|
}
|
|
6700
7243
|
function rankHybridResults(query, semanticResults, keywordResults, options) {
|
|
7244
|
+
const prioritizeSourcePaths = options.prioritizeSourcePaths ?? classifyQueryIntentRaw(query) === "source";
|
|
7245
|
+
const cacheKey = `${query}${options.fusionStrategy}|${options.rrfK}|${options.hybridWeight}|${options.rerankTopN}|${options.limit}|${prioritizeSourcePaths ? 1 : 0}`;
|
|
7246
|
+
let byKeyword = rankHybridResultsCache.get(semanticResults);
|
|
7247
|
+
if (!byKeyword) {
|
|
7248
|
+
byKeyword = /* @__PURE__ */ new WeakMap();
|
|
7249
|
+
rankHybridResultsCache.set(semanticResults, byKeyword);
|
|
7250
|
+
}
|
|
7251
|
+
let bucket = byKeyword.get(keywordResults);
|
|
7252
|
+
if (!bucket) {
|
|
7253
|
+
bucket = /* @__PURE__ */ new Map();
|
|
7254
|
+
byKeyword.set(keywordResults, bucket);
|
|
7255
|
+
} else {
|
|
7256
|
+
const cached = bucket.get(cacheKey);
|
|
7257
|
+
if (cached) {
|
|
7258
|
+
return cached;
|
|
7259
|
+
}
|
|
7260
|
+
}
|
|
6701
7261
|
const overfetchLimit = Math.max(options.limit * 4, options.limit);
|
|
6702
7262
|
const fused = options.fusionStrategy === "rrf" ? fuseResultsRrf(semanticResults, keywordResults, options.rrfK, overfetchLimit) : fuseResultsWeighted(semanticResults, keywordResults, options.hybridWeight, overfetchLimit);
|
|
6703
7263
|
const rerankPoolLimit = Math.max(overfetchLimit, options.rerankTopN * 3, options.limit * 6);
|
|
6704
7264
|
const rerankPool = fused.slice(0, rerankPoolLimit);
|
|
6705
|
-
|
|
6706
|
-
prioritizeSourcePaths
|
|
7265
|
+
const ranked = rerankResults(query, rerankPool, options.rerankTopN, {
|
|
7266
|
+
prioritizeSourcePaths
|
|
6707
7267
|
});
|
|
7268
|
+
if (bucket.size >= RANK_HYBRID_CACHE_LIMIT) {
|
|
7269
|
+
const oldest = bucket.keys().next().value;
|
|
7270
|
+
if (oldest !== void 0) {
|
|
7271
|
+
bucket.delete(oldest);
|
|
7272
|
+
}
|
|
7273
|
+
}
|
|
7274
|
+
bucket.set(cacheKey, ranked);
|
|
7275
|
+
return ranked;
|
|
6708
7276
|
}
|
|
6709
7277
|
function rankSemanticOnlyResults(query, semanticResults, options) {
|
|
6710
7278
|
const overfetchLimit = Math.max(options.limit * 4, options.limit);
|
|
@@ -7001,6 +7569,21 @@ function mergeTieredResults(symbolLane, hybridLane, limit) {
|
|
|
7001
7569
|
}
|
|
7002
7570
|
return out;
|
|
7003
7571
|
}
|
|
7572
|
+
function matchesSearchFilters(candidate, options, minScore) {
|
|
7573
|
+
if (candidate.score < minScore) return false;
|
|
7574
|
+
if (options?.fileType) {
|
|
7575
|
+
const ext = candidate.metadata.filePath.split(".").pop()?.toLowerCase();
|
|
7576
|
+
if (ext !== options.fileType.toLowerCase().replace(/^\./, "")) return false;
|
|
7577
|
+
}
|
|
7578
|
+
if (options?.directory) {
|
|
7579
|
+
const normalizedDir = options.directory.replace(/^\/|\/$/g, "");
|
|
7580
|
+
if (!candidate.metadata.filePath.includes(`/${normalizedDir}/`) && !candidate.metadata.filePath.includes(`${normalizedDir}/`)) return false;
|
|
7581
|
+
}
|
|
7582
|
+
if (options?.chunkType && candidate.metadata.chunkType !== options.chunkType) {
|
|
7583
|
+
return false;
|
|
7584
|
+
}
|
|
7585
|
+
return true;
|
|
7586
|
+
}
|
|
7004
7587
|
function unionCandidates(semanticCandidates, keywordCandidates) {
|
|
7005
7588
|
const byId = /* @__PURE__ */ new Map();
|
|
7006
7589
|
for (const candidate of semanticCandidates) {
|
|
@@ -7040,22 +7623,18 @@ var Indexer = class {
|
|
|
7040
7623
|
this.projectRoot = projectRoot;
|
|
7041
7624
|
this.config = config;
|
|
7042
7625
|
this.indexPath = this.getIndexPath();
|
|
7043
|
-
this.fileHashCachePath =
|
|
7044
|
-
this.failedBatchesPath =
|
|
7045
|
-
this.indexingLockPath =
|
|
7626
|
+
this.fileHashCachePath = path8.join(this.indexPath, "file-hashes.json");
|
|
7627
|
+
this.failedBatchesPath = path8.join(this.indexPath, "failed-batches.json");
|
|
7628
|
+
this.indexingLockPath = path8.join(this.indexPath, "indexing.lock");
|
|
7046
7629
|
this.logger = initializeLogger(config.debug);
|
|
7047
7630
|
}
|
|
7048
7631
|
getIndexPath() {
|
|
7049
|
-
|
|
7050
|
-
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
7051
|
-
return path7.join(homeDir, ".opencode", "global-index");
|
|
7052
|
-
}
|
|
7053
|
-
return path7.join(this.projectRoot, ".opencode", "index");
|
|
7632
|
+
return resolveProjectIndexPath(this.projectRoot, this.config.scope);
|
|
7054
7633
|
}
|
|
7055
7634
|
loadFileHashCache() {
|
|
7056
7635
|
try {
|
|
7057
|
-
if ((0,
|
|
7058
|
-
const data = (0,
|
|
7636
|
+
if ((0, import_fs6.existsSync)(this.fileHashCachePath)) {
|
|
7637
|
+
const data = (0, import_fs6.readFileSync)(this.fileHashCachePath, "utf-8");
|
|
7059
7638
|
const parsed = JSON.parse(data);
|
|
7060
7639
|
this.fileHashCache = new Map(Object.entries(parsed));
|
|
7061
7640
|
}
|
|
@@ -7072,91 +7651,450 @@ var Indexer = class {
|
|
|
7072
7651
|
}
|
|
7073
7652
|
atomicWriteSync(targetPath, data) {
|
|
7074
7653
|
const tempPath = `${targetPath}.tmp`;
|
|
7075
|
-
(0,
|
|
7076
|
-
(0,
|
|
7654
|
+
(0, import_fs6.writeFileSync)(tempPath, data);
|
|
7655
|
+
(0, import_fs6.renameSync)(tempPath, targetPath);
|
|
7077
7656
|
}
|
|
7078
|
-
|
|
7079
|
-
|
|
7657
|
+
getScopedRoots() {
|
|
7658
|
+
const roots = /* @__PURE__ */ new Set([path8.resolve(this.projectRoot)]);
|
|
7659
|
+
for (const kbRoot of this.config.knowledgeBases) {
|
|
7660
|
+
roots.add(path8.resolve(this.projectRoot, kbRoot));
|
|
7661
|
+
}
|
|
7662
|
+
return Array.from(roots);
|
|
7080
7663
|
}
|
|
7081
|
-
|
|
7082
|
-
const
|
|
7083
|
-
|
|
7084
|
-
|
|
7085
|
-
}
|
|
7086
|
-
(
|
|
7664
|
+
getBranchCatalogKey() {
|
|
7665
|
+
const branchName = this.currentBranch || "default";
|
|
7666
|
+
if (this.config.scope !== "global") {
|
|
7667
|
+
return branchName;
|
|
7668
|
+
}
|
|
7669
|
+
const projectHash = hashContent(path8.resolve(this.projectRoot)).slice(0, 16);
|
|
7670
|
+
return `${projectHash}:${branchName}`;
|
|
7087
7671
|
}
|
|
7088
|
-
|
|
7089
|
-
|
|
7090
|
-
|
|
7672
|
+
getLegacyBranchCatalogKey() {
|
|
7673
|
+
return this.currentBranch || "default";
|
|
7674
|
+
}
|
|
7675
|
+
getLegacyMigrationMetadataKey() {
|
|
7676
|
+
const projectHash = hashContent(path8.resolve(this.projectRoot)).slice(0, 16);
|
|
7677
|
+
return `index.globalBranchMigration.${projectHash}`;
|
|
7678
|
+
}
|
|
7679
|
+
getProjectEmbeddingStrategyMetadataKey() {
|
|
7680
|
+
const projectHash = hashContent(path8.resolve(this.projectRoot)).slice(0, 16);
|
|
7681
|
+
return `index.embeddingStrategyVersion.${projectHash}`;
|
|
7682
|
+
}
|
|
7683
|
+
getProjectForceReembedMetadataKey() {
|
|
7684
|
+
const projectHash = hashContent(path8.resolve(this.projectRoot)).slice(0, 16);
|
|
7685
|
+
return `index.forceReembed.${projectHash}`;
|
|
7686
|
+
}
|
|
7687
|
+
hasProjectForceReembedPending() {
|
|
7688
|
+
return this.config.scope === "global" && this.database?.getMetadata(this.getProjectForceReembedMetadataKey()) === "true";
|
|
7689
|
+
}
|
|
7690
|
+
hasScopedIndexedData() {
|
|
7691
|
+
if (!this.store || this.config.scope !== "global") {
|
|
7692
|
+
return false;
|
|
7693
|
+
}
|
|
7694
|
+
if (this.hasProjectForceReembedPending()) {
|
|
7695
|
+
return false;
|
|
7696
|
+
}
|
|
7697
|
+
const roots = this.getScopedRoots();
|
|
7698
|
+
if (Array.from(this.fileHashCache.keys()).some((filePath) => this.isFileInCurrentScope(filePath, roots))) {
|
|
7699
|
+
return true;
|
|
7700
|
+
}
|
|
7701
|
+
if (this.loadSerializedFailedBatches().some(
|
|
7702
|
+
(batch) => batch.chunks.some((chunk) => {
|
|
7703
|
+
const filePath = getPendingChunkFilePath(chunk);
|
|
7704
|
+
return filePath !== null && this.isFileInCurrentScope(filePath, roots);
|
|
7705
|
+
})
|
|
7706
|
+
)) {
|
|
7707
|
+
return true;
|
|
7708
|
+
}
|
|
7709
|
+
if (!this.database) {
|
|
7710
|
+
return false;
|
|
7091
7711
|
}
|
|
7712
|
+
if (this.getBranchCatalogKeys().some((branchKey) => {
|
|
7713
|
+
const branchChunkIds = this.database.getBranchChunkIds(branchKey);
|
|
7714
|
+
if (branchChunkIds.length > 0) {
|
|
7715
|
+
return true;
|
|
7716
|
+
}
|
|
7717
|
+
return this.database.getBranchSymbolIds(branchKey).length > 0;
|
|
7718
|
+
})) {
|
|
7719
|
+
return true;
|
|
7720
|
+
}
|
|
7721
|
+
const hasAnyBranchRows = this.database.getAllBranches().some((branchKey) => {
|
|
7722
|
+
const branchChunkIds = this.database.getBranchChunkIds(branchKey);
|
|
7723
|
+
if (branchChunkIds.length > 0) {
|
|
7724
|
+
return true;
|
|
7725
|
+
}
|
|
7726
|
+
return this.database.getBranchSymbolIds(branchKey).length > 0;
|
|
7727
|
+
});
|
|
7728
|
+
if (hasAnyBranchRows) {
|
|
7729
|
+
return false;
|
|
7730
|
+
}
|
|
7731
|
+
return this.store.getAllMetadata().some(({ metadata }) => this.isFileInCurrentScope(metadata.filePath, roots));
|
|
7092
7732
|
}
|
|
7093
|
-
|
|
7094
|
-
this.
|
|
7095
|
-
|
|
7096
|
-
(0, import_fs5.unlinkSync)(this.fileHashCachePath);
|
|
7733
|
+
loadStoredEmbeddingStrategyVersion() {
|
|
7734
|
+
if (!this.database) {
|
|
7735
|
+
return null;
|
|
7097
7736
|
}
|
|
7098
|
-
|
|
7099
|
-
|
|
7100
|
-
|
|
7737
|
+
if (this.hasProjectForceReembedPending()) {
|
|
7738
|
+
return null;
|
|
7739
|
+
}
|
|
7740
|
+
if (this.config.scope !== "global") {
|
|
7741
|
+
return this.database.getMetadata("index.embeddingStrategyVersion") ?? "1";
|
|
7742
|
+
}
|
|
7743
|
+
const projectVersion = this.database.getMetadata(this.getProjectEmbeddingStrategyMetadataKey());
|
|
7744
|
+
if (projectVersion) {
|
|
7745
|
+
return projectVersion;
|
|
7746
|
+
}
|
|
7747
|
+
const legacySharedVersion = this.database.getMetadata("index.embeddingStrategyVersion");
|
|
7748
|
+
if (legacySharedVersion && this.hasScopedIndexedData()) {
|
|
7749
|
+
return legacySharedVersion;
|
|
7750
|
+
}
|
|
7751
|
+
return null;
|
|
7101
7752
|
}
|
|
7102
|
-
|
|
7103
|
-
|
|
7104
|
-
|
|
7105
|
-
|
|
7106
|
-
|
|
7753
|
+
getBranchCatalogKeys() {
|
|
7754
|
+
const primary = this.getBranchCatalogKey();
|
|
7755
|
+
if (this.config.scope !== "global") {
|
|
7756
|
+
return [primary];
|
|
7757
|
+
}
|
|
7758
|
+
if (this.database?.getMetadata(this.getLegacyMigrationMetadataKey()) === "done") {
|
|
7759
|
+
return [primary];
|
|
7760
|
+
}
|
|
7761
|
+
const legacy = this.getLegacyBranchCatalogKey();
|
|
7762
|
+
return primary === legacy ? [primary] : [primary, legacy];
|
|
7763
|
+
}
|
|
7764
|
+
getBranchCatalogCleanupKeys() {
|
|
7765
|
+
const primary = this.getBranchCatalogKey();
|
|
7766
|
+
if (this.config.scope !== "global") {
|
|
7767
|
+
return [primary];
|
|
7768
|
+
}
|
|
7769
|
+
const legacy = this.getLegacyBranchCatalogKey();
|
|
7770
|
+
return primary === legacy ? [primary] : [primary, legacy];
|
|
7771
|
+
}
|
|
7772
|
+
getProjectLocalScopedOwnershipIds(roots) {
|
|
7773
|
+
const chunkIds = /* @__PURE__ */ new Set();
|
|
7774
|
+
const symbolIds = /* @__PURE__ */ new Set();
|
|
7775
|
+
if (!this.database) {
|
|
7776
|
+
return { chunkIds, symbolIds };
|
|
7777
|
+
}
|
|
7778
|
+
const projectRootPath = path8.resolve(this.projectRoot);
|
|
7779
|
+
const projectLocalFilePaths = /* @__PURE__ */ new Set([
|
|
7780
|
+
...Array.from(this.fileHashCache.keys()).filter(
|
|
7781
|
+
(filePath) => this.isFileInCurrentScope(filePath, roots) && isPathWithinRoot(filePath, projectRootPath)
|
|
7782
|
+
),
|
|
7783
|
+
...(this.store?.getAllMetadata() ?? []).map(({ metadata }) => metadata.filePath).filter(
|
|
7784
|
+
(filePath) => this.isFileInCurrentScope(filePath, roots) && isPathWithinRoot(filePath, projectRootPath)
|
|
7785
|
+
)
|
|
7786
|
+
]);
|
|
7787
|
+
for (const filePath of projectLocalFilePaths) {
|
|
7788
|
+
for (const chunk of this.database.getChunksByFile(filePath)) {
|
|
7789
|
+
chunkIds.add(chunk.chunkId);
|
|
7790
|
+
}
|
|
7791
|
+
for (const symbol of this.database.getSymbolsByFile(filePath)) {
|
|
7792
|
+
symbolIds.add(symbol.id);
|
|
7107
7793
|
}
|
|
7108
|
-
} catch {
|
|
7109
|
-
return [];
|
|
7110
7794
|
}
|
|
7111
|
-
return
|
|
7795
|
+
return { chunkIds, symbolIds };
|
|
7112
7796
|
}
|
|
7113
|
-
|
|
7114
|
-
if (
|
|
7115
|
-
|
|
7116
|
-
|
|
7117
|
-
|
|
7797
|
+
getProjectScopedBranchCatalogCleanupKeys(projectChunkIds, projectSymbolIds) {
|
|
7798
|
+
if (this.config.scope !== "global") {
|
|
7799
|
+
return this.getBranchCatalogCleanupKeys();
|
|
7800
|
+
}
|
|
7801
|
+
const projectHash = hashContent(path8.resolve(this.projectRoot)).slice(0, 16);
|
|
7802
|
+
const keys = /* @__PURE__ */ new Set();
|
|
7803
|
+
const projectChunkIdSet = new Set(projectChunkIds);
|
|
7804
|
+
const projectSymbolIdSet = new Set(projectSymbolIds);
|
|
7805
|
+
for (const branchKey of this.database?.getAllBranches() ?? []) {
|
|
7806
|
+
if (branchKey.startsWith(`${projectHash}:`)) {
|
|
7807
|
+
keys.add(branchKey);
|
|
7808
|
+
continue;
|
|
7809
|
+
}
|
|
7810
|
+
if (branchKey.includes(":")) {
|
|
7811
|
+
continue;
|
|
7812
|
+
}
|
|
7813
|
+
const referencesProjectChunks = this.database?.getBranchChunkIds(branchKey).some((chunkId) => projectChunkIdSet.has(chunkId)) ?? false;
|
|
7814
|
+
const referencesProjectSymbols = this.database?.getBranchSymbolIds(branchKey).some((symbolId) => projectSymbolIdSet.has(symbolId)) ?? false;
|
|
7815
|
+
if (referencesProjectChunks || referencesProjectSymbols) {
|
|
7816
|
+
keys.add(branchKey);
|
|
7118
7817
|
}
|
|
7119
|
-
return;
|
|
7120
7818
|
}
|
|
7121
|
-
(
|
|
7819
|
+
for (const branchKey of this.getBranchCatalogCleanupKeys()) {
|
|
7820
|
+
keys.add(branchKey);
|
|
7821
|
+
}
|
|
7822
|
+
return Array.from(keys);
|
|
7122
7823
|
}
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
existing.push({
|
|
7126
|
-
chunks: batch,
|
|
7127
|
-
error,
|
|
7128
|
-
attemptCount: 1,
|
|
7129
|
-
lastAttempt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7130
|
-
});
|
|
7131
|
-
this.saveFailedBatches(existing);
|
|
7824
|
+
isFileInCurrentScope(filePath, roots) {
|
|
7825
|
+
return roots.some((root) => isPathWithinRoot(filePath, root));
|
|
7132
7826
|
}
|
|
7133
|
-
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
case "openai":
|
|
7138
|
-
return { concurrency: 3, intervalMs: 500, minRetryMs: 1e3, maxRetryMs: 3e4 };
|
|
7139
|
-
case "google":
|
|
7140
|
-
return { concurrency: 5, intervalMs: 200, minRetryMs: 1e3, maxRetryMs: 3e4 };
|
|
7141
|
-
case "ollama":
|
|
7142
|
-
return { concurrency: 5, intervalMs: 0, minRetryMs: 500, maxRetryMs: 5e3 };
|
|
7143
|
-
case "custom": {
|
|
7144
|
-
const customConfig = this.config.customProvider;
|
|
7145
|
-
return {
|
|
7146
|
-
concurrency: customConfig?.concurrency ?? 3,
|
|
7147
|
-
intervalMs: customConfig?.requestIntervalMs ?? 1e3,
|
|
7148
|
-
minRetryMs: 1e3,
|
|
7149
|
-
maxRetryMs: 3e4
|
|
7150
|
-
};
|
|
7827
|
+
clearScopedFileHashCache(roots) {
|
|
7828
|
+
for (const filePath of Array.from(this.fileHashCache.keys())) {
|
|
7829
|
+
if (this.isFileInCurrentScope(filePath, roots)) {
|
|
7830
|
+
this.fileHashCache.delete(filePath);
|
|
7151
7831
|
}
|
|
7152
|
-
default:
|
|
7153
|
-
return { concurrency: 3, intervalMs: 1e3, minRetryMs: 1e3, maxRetryMs: 3e4 };
|
|
7154
7832
|
}
|
|
7833
|
+
this.saveFileHashCache();
|
|
7155
7834
|
}
|
|
7156
|
-
|
|
7157
|
-
const
|
|
7158
|
-
|
|
7159
|
-
|
|
7835
|
+
replaceScopedFileHashCache(currentFileHashes, roots) {
|
|
7836
|
+
for (const filePath of Array.from(this.fileHashCache.keys())) {
|
|
7837
|
+
if (this.isFileInCurrentScope(filePath, roots)) {
|
|
7838
|
+
this.fileHashCache.delete(filePath);
|
|
7839
|
+
}
|
|
7840
|
+
}
|
|
7841
|
+
for (const [filePath, hash] of currentFileHashes) {
|
|
7842
|
+
this.fileHashCache.set(filePath, hash);
|
|
7843
|
+
}
|
|
7844
|
+
this.saveFileHashCache();
|
|
7845
|
+
}
|
|
7846
|
+
partitionFailedBatches(roots, maxChunkTokens) {
|
|
7847
|
+
const scoped = [];
|
|
7848
|
+
const retained = [];
|
|
7849
|
+
for (const batch of this.loadSerializedFailedBatches()) {
|
|
7850
|
+
const scopedChunks = batch.chunks.filter((chunk) => {
|
|
7851
|
+
const filePath = getPendingChunkFilePath(chunk);
|
|
7852
|
+
return filePath !== null && this.isFileInCurrentScope(filePath, roots);
|
|
7853
|
+
});
|
|
7854
|
+
const retainedChunks = batch.chunks.filter((chunk) => {
|
|
7855
|
+
const filePath = getPendingChunkFilePath(chunk);
|
|
7856
|
+
return filePath === null || !this.isFileInCurrentScope(filePath, roots);
|
|
7857
|
+
});
|
|
7858
|
+
if (scopedChunks.length > 0) {
|
|
7859
|
+
const normalizedBatch = normalizeFailedBatch({ ...batch, chunks: scopedChunks }, maxChunkTokens);
|
|
7860
|
+
if (normalizedBatch) {
|
|
7861
|
+
scoped.push(normalizedBatch);
|
|
7862
|
+
}
|
|
7863
|
+
}
|
|
7864
|
+
if (retainedChunks.length > 0) {
|
|
7865
|
+
retained.push({ ...batch, chunks: retainedChunks });
|
|
7866
|
+
}
|
|
7867
|
+
}
|
|
7868
|
+
return { scoped, retained };
|
|
7869
|
+
}
|
|
7870
|
+
clearScopedFailedBatches(roots) {
|
|
7871
|
+
const { retained: retainedBatches } = this.partitionFailedBatches(roots);
|
|
7872
|
+
this.saveFailedBatches(retainedBatches);
|
|
7873
|
+
}
|
|
7874
|
+
hasForeignScopedFileHashData(roots) {
|
|
7875
|
+
return Array.from(this.fileHashCache.keys()).some((filePath) => !this.isFileInCurrentScope(filePath, roots));
|
|
7876
|
+
}
|
|
7877
|
+
hasForeignScopedFailedBatches(roots) {
|
|
7878
|
+
const { retained } = this.partitionFailedBatches(roots);
|
|
7879
|
+
return retained.length > 0;
|
|
7880
|
+
}
|
|
7881
|
+
hasForeignScopedBranchData() {
|
|
7882
|
+
if (!this.database || this.config.scope !== "global") {
|
|
7883
|
+
return false;
|
|
7884
|
+
}
|
|
7885
|
+
const projectHash = hashContent(path8.resolve(this.projectRoot)).slice(0, 16);
|
|
7886
|
+
const roots = this.getScopedRoots();
|
|
7887
|
+
const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots);
|
|
7888
|
+
return this.database.getAllBranches().some(
|
|
7889
|
+
(branchKey) => {
|
|
7890
|
+
const branchChunkIds = this.database.getBranchChunkIds(branchKey);
|
|
7891
|
+
const branchSymbolIds = this.database.getBranchSymbolIds(branchKey);
|
|
7892
|
+
const hasBranchData = branchChunkIds.length > 0 || branchSymbolIds.length > 0;
|
|
7893
|
+
if (!hasBranchData) {
|
|
7894
|
+
return false;
|
|
7895
|
+
}
|
|
7896
|
+
if (branchKey.startsWith(`${projectHash}:`)) {
|
|
7897
|
+
return false;
|
|
7898
|
+
}
|
|
7899
|
+
if (!branchKey.includes(":")) {
|
|
7900
|
+
const referencesCurrentProjectChunks = branchChunkIds.some((chunkId) => projectLocalChunkIds.has(chunkId));
|
|
7901
|
+
const referencesCurrentProjectSymbols = branchSymbolIds.some((symbolId) => projectLocalSymbolIds.has(symbolId));
|
|
7902
|
+
return !(referencesCurrentProjectChunks || referencesCurrentProjectSymbols);
|
|
7903
|
+
}
|
|
7904
|
+
return true;
|
|
7905
|
+
}
|
|
7906
|
+
);
|
|
7907
|
+
}
|
|
7908
|
+
saveScopedFailedBatches(batches, roots) {
|
|
7909
|
+
const { retained } = this.partitionFailedBatches(roots);
|
|
7910
|
+
this.saveFailedBatches([...retained, ...batches]);
|
|
7911
|
+
}
|
|
7912
|
+
clearSharedIndexProjectData(store, invertedIndex, database, roots) {
|
|
7913
|
+
const allMetadata = store.getAllMetadata();
|
|
7914
|
+
const scopedEntries = allMetadata.filter(({ metadata }) => this.isFileInCurrentScope(metadata.filePath, roots));
|
|
7915
|
+
const filePaths = /* @__PURE__ */ new Set([
|
|
7916
|
+
...Array.from(this.fileHashCache.keys()).filter((filePath) => this.isFileInCurrentScope(filePath, roots)),
|
|
7917
|
+
...scopedEntries.map(({ metadata }) => metadata.filePath)
|
|
7918
|
+
]);
|
|
7919
|
+
const projectRootPath = path8.resolve(this.projectRoot);
|
|
7920
|
+
const projectLocalFilePaths = new Set(
|
|
7921
|
+
Array.from(filePaths).filter((filePath) => isPathWithinRoot(filePath, projectRootPath))
|
|
7922
|
+
);
|
|
7923
|
+
const removedChunkIds = new Set(scopedEntries.map(({ key }) => key));
|
|
7924
|
+
for (const filePath of filePaths) {
|
|
7925
|
+
for (const chunk of database.getChunksByFile(filePath)) {
|
|
7926
|
+
removedChunkIds.add(chunk.chunkId);
|
|
7927
|
+
}
|
|
7928
|
+
}
|
|
7929
|
+
const removedChunkIdList = Array.from(removedChunkIds);
|
|
7930
|
+
const projectLocalChunkIds = new Set(
|
|
7931
|
+
scopedEntries.filter(({ metadata }) => isPathWithinRoot(metadata.filePath, projectRootPath)).map(({ key }) => key)
|
|
7932
|
+
);
|
|
7933
|
+
for (const filePath of projectLocalFilePaths) {
|
|
7934
|
+
for (const chunk of database.getChunksByFile(filePath)) {
|
|
7935
|
+
projectLocalChunkIds.add(chunk.chunkId);
|
|
7936
|
+
}
|
|
7937
|
+
}
|
|
7938
|
+
const symbolIds = [];
|
|
7939
|
+
const projectLocalSymbolIds = /* @__PURE__ */ new Set();
|
|
7940
|
+
for (const filePath of filePaths) {
|
|
7941
|
+
for (const symbol of database.getSymbolsByFile(filePath)) {
|
|
7942
|
+
symbolIds.push(symbol.id);
|
|
7943
|
+
if (projectLocalFilePaths.has(filePath)) {
|
|
7944
|
+
projectLocalSymbolIds.add(symbol.id);
|
|
7945
|
+
}
|
|
7946
|
+
}
|
|
7947
|
+
}
|
|
7948
|
+
for (const branchKey of this.getProjectScopedBranchCatalogCleanupKeys(Array.from(projectLocalChunkIds), Array.from(projectLocalSymbolIds))) {
|
|
7949
|
+
database.deleteBranchChunksForBranch(branchKey, removedChunkIdList);
|
|
7950
|
+
}
|
|
7951
|
+
const sharedChunkIds = new Set(database.getReferencedChunkIds(removedChunkIdList));
|
|
7952
|
+
const removableChunkIds = removedChunkIdList.filter((chunkId) => !sharedChunkIds.has(chunkId));
|
|
7953
|
+
if (removableChunkIds.length > 0) {
|
|
7954
|
+
this.rebuildVectorStoreExcludingChunkIds(store, database, removableChunkIds);
|
|
7955
|
+
for (const chunkId of removableChunkIds) {
|
|
7956
|
+
invertedIndex.removeChunk(chunkId);
|
|
7957
|
+
}
|
|
7958
|
+
}
|
|
7959
|
+
for (const branchKey of this.getProjectScopedBranchCatalogCleanupKeys(Array.from(projectLocalChunkIds), Array.from(projectLocalSymbolIds))) {
|
|
7960
|
+
database.deleteBranchSymbolsForBranch(branchKey, symbolIds);
|
|
7961
|
+
}
|
|
7962
|
+
const sharedSymbolIds = new Set(database.getReferencedSymbolIds(symbolIds));
|
|
7963
|
+
const removableSymbolIds = symbolIds.filter((symbolId) => !sharedSymbolIds.has(symbolId));
|
|
7964
|
+
database.clearCallEdgeTargetsForSymbols(removableSymbolIds);
|
|
7965
|
+
for (const filePath of filePaths) {
|
|
7966
|
+
const fileChunkIds = database.getChunksByFile(filePath).map((chunk) => chunk.chunkId);
|
|
7967
|
+
const fileSymbols = database.getSymbolsByFile(filePath);
|
|
7968
|
+
if (fileChunkIds.every((chunkId) => !sharedChunkIds.has(chunkId))) {
|
|
7969
|
+
database.deleteChunksByFile(filePath);
|
|
7970
|
+
}
|
|
7971
|
+
if (fileSymbols.every((symbol) => !sharedSymbolIds.has(symbol.id))) {
|
|
7972
|
+
database.deleteCallEdgesByFile(filePath);
|
|
7973
|
+
database.deleteSymbolsByFile(filePath);
|
|
7974
|
+
}
|
|
7975
|
+
}
|
|
7976
|
+
database.gcOrphanCallEdges();
|
|
7977
|
+
database.gcOrphanSymbols();
|
|
7978
|
+
database.gcOrphanEmbeddings();
|
|
7979
|
+
database.gcOrphanChunks();
|
|
7980
|
+
store.save();
|
|
7981
|
+
invertedIndex.save();
|
|
7982
|
+
return {
|
|
7983
|
+
removedChunkIds: removedChunkIdList,
|
|
7984
|
+
hasForeignData: allMetadata.some(({ metadata }) => !this.isFileInCurrentScope(metadata.filePath, roots))
|
|
7985
|
+
};
|
|
7986
|
+
}
|
|
7987
|
+
checkForInterruptedIndexing() {
|
|
7988
|
+
return (0, import_fs6.existsSync)(this.indexingLockPath);
|
|
7989
|
+
}
|
|
7990
|
+
acquireIndexingLock() {
|
|
7991
|
+
const lockData = {
|
|
7992
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7993
|
+
pid: process.pid
|
|
7994
|
+
};
|
|
7995
|
+
(0, import_fs6.writeFileSync)(this.indexingLockPath, JSON.stringify(lockData));
|
|
7996
|
+
}
|
|
7997
|
+
releaseIndexingLock() {
|
|
7998
|
+
if ((0, import_fs6.existsSync)(this.indexingLockPath)) {
|
|
7999
|
+
(0, import_fs6.unlinkSync)(this.indexingLockPath);
|
|
8000
|
+
}
|
|
8001
|
+
}
|
|
8002
|
+
async recoverFromInterruptedIndexing() {
|
|
8003
|
+
this.logger.warn("Detected interrupted indexing session, recovering...");
|
|
8004
|
+
if ((0, import_fs6.existsSync)(this.fileHashCachePath)) {
|
|
8005
|
+
(0, import_fs6.unlinkSync)(this.fileHashCachePath);
|
|
8006
|
+
}
|
|
8007
|
+
await this.healthCheck();
|
|
8008
|
+
this.releaseIndexingLock();
|
|
8009
|
+
this.logger.info("Recovery complete, next index will re-process all files");
|
|
8010
|
+
}
|
|
8011
|
+
loadFailedBatches(maxChunkTokens) {
|
|
8012
|
+
try {
|
|
8013
|
+
return this.loadSerializedFailedBatches().map((batch) => normalizeFailedBatch(batch, maxChunkTokens)).filter((batch) => batch !== null);
|
|
8014
|
+
} catch {
|
|
8015
|
+
return [];
|
|
8016
|
+
}
|
|
8017
|
+
}
|
|
8018
|
+
loadSerializedFailedBatches() {
|
|
8019
|
+
if (!(0, import_fs6.existsSync)(this.failedBatchesPath)) {
|
|
8020
|
+
return [];
|
|
8021
|
+
}
|
|
8022
|
+
const data = (0, import_fs6.readFileSync)(this.failedBatchesPath, "utf-8");
|
|
8023
|
+
const parsed = JSON.parse(data);
|
|
8024
|
+
return parsed.map((batch) => {
|
|
8025
|
+
const chunks = Array.isArray(batch.chunks) ? batch.chunks : [];
|
|
8026
|
+
if (chunks.length === 0) {
|
|
8027
|
+
return null;
|
|
8028
|
+
}
|
|
8029
|
+
return {
|
|
8030
|
+
chunks,
|
|
8031
|
+
error: typeof batch.error === "string" ? batch.error : "Unknown embedding error",
|
|
8032
|
+
attemptCount: typeof batch.attemptCount === "number" ? batch.attemptCount : 1,
|
|
8033
|
+
lastAttempt: typeof batch.lastAttempt === "string" ? batch.lastAttempt : (/* @__PURE__ */ new Date()).toISOString()
|
|
8034
|
+
};
|
|
8035
|
+
}).filter((batch) => batch !== null);
|
|
8036
|
+
}
|
|
8037
|
+
saveFailedBatches(batches) {
|
|
8038
|
+
if (batches.length === 0) {
|
|
8039
|
+
if ((0, import_fs6.existsSync)(this.failedBatchesPath)) {
|
|
8040
|
+
try {
|
|
8041
|
+
(0, import_fs6.unlinkSync)(this.failedBatchesPath);
|
|
8042
|
+
} catch {
|
|
8043
|
+
}
|
|
8044
|
+
}
|
|
8045
|
+
return;
|
|
8046
|
+
}
|
|
8047
|
+
(0, import_fs6.writeFileSync)(this.failedBatchesPath, JSON.stringify(batches, null, 2));
|
|
8048
|
+
}
|
|
8049
|
+
collectRetryableFailedChunks(currentFileHashes, unchangedFilePaths, maxChunkTokens) {
|
|
8050
|
+
const retryableById = /* @__PURE__ */ new Map();
|
|
8051
|
+
for (const batch of this.loadFailedBatches(maxChunkTokens)) {
|
|
8052
|
+
for (const chunk of batch.chunks) {
|
|
8053
|
+
const filePath = chunk.metadata.filePath;
|
|
8054
|
+
if (!currentFileHashes.has(filePath)) {
|
|
8055
|
+
continue;
|
|
8056
|
+
}
|
|
8057
|
+
if (!unchangedFilePaths.has(filePath)) {
|
|
8058
|
+
continue;
|
|
8059
|
+
}
|
|
8060
|
+
const existing = retryableById.get(chunk.id);
|
|
8061
|
+
if (!existing || batch.attemptCount > existing.attemptCount) {
|
|
8062
|
+
retryableById.set(chunk.id, {
|
|
8063
|
+
chunk,
|
|
8064
|
+
attemptCount: batch.attemptCount
|
|
8065
|
+
});
|
|
8066
|
+
}
|
|
8067
|
+
}
|
|
8068
|
+
}
|
|
8069
|
+
return Array.from(retryableById.values());
|
|
8070
|
+
}
|
|
8071
|
+
getProviderRateLimits(provider) {
|
|
8072
|
+
switch (provider) {
|
|
8073
|
+
case "github-copilot":
|
|
8074
|
+
return { concurrency: 1, intervalMs: 4e3, minRetryMs: 5e3, maxRetryMs: 6e4 };
|
|
8075
|
+
case "openai":
|
|
8076
|
+
return { concurrency: 3, intervalMs: 500, minRetryMs: 1e3, maxRetryMs: 3e4 };
|
|
8077
|
+
case "google":
|
|
8078
|
+
return { concurrency: 5, intervalMs: 200, minRetryMs: 1e3, maxRetryMs: 3e4 };
|
|
8079
|
+
case "ollama":
|
|
8080
|
+
return { concurrency: 5, intervalMs: 0, minRetryMs: 500, maxRetryMs: 5e3 };
|
|
8081
|
+
case "custom": {
|
|
8082
|
+
const customConfig = this.config.customProvider;
|
|
8083
|
+
return {
|
|
8084
|
+
concurrency: customConfig?.concurrency ?? 3,
|
|
8085
|
+
intervalMs: customConfig?.requestIntervalMs ?? 1e3,
|
|
8086
|
+
minRetryMs: 1e3,
|
|
8087
|
+
maxRetryMs: 3e4
|
|
8088
|
+
};
|
|
8089
|
+
}
|
|
8090
|
+
default:
|
|
8091
|
+
return { concurrency: 3, intervalMs: 1e3, minRetryMs: 1e3, maxRetryMs: 3e4 };
|
|
8092
|
+
}
|
|
8093
|
+
}
|
|
8094
|
+
async rerankCandidatesWithApi(query, candidates, options) {
|
|
8095
|
+
const reranker = this.config.reranker;
|
|
8096
|
+
if (!reranker || !reranker.enabled || candidates.length <= 1) {
|
|
8097
|
+
return candidates;
|
|
7160
8098
|
}
|
|
7161
8099
|
const queryTokens = Array.from(tokenizeTextForRanking(query));
|
|
7162
8100
|
const preferSourcePaths = classifyQueryIntentRaw(query) === "source";
|
|
@@ -7286,7 +8224,7 @@ var Indexer = class {
|
|
|
7286
8224
|
const intent = isLikelyImplementationPath(candidate.metadata.filePath) ? "implementation" : "doc_or_test";
|
|
7287
8225
|
parts.push(`intent_hint: ${intent}`);
|
|
7288
8226
|
try {
|
|
7289
|
-
const fileContent = await
|
|
8227
|
+
const fileContent = await import_fs6.promises.readFile(candidate.metadata.filePath, "utf-8");
|
|
7290
8228
|
const lines = fileContent.split("\n");
|
|
7291
8229
|
const snippetStartLine = Math.max(1, candidate.metadata.startLine - 2);
|
|
7292
8230
|
const snippetEndLine = Math.min(lines.length, candidate.metadata.endLine + 2);
|
|
@@ -7331,33 +8269,56 @@ var Indexer = class {
|
|
|
7331
8269
|
});
|
|
7332
8270
|
}
|
|
7333
8271
|
}
|
|
7334
|
-
await
|
|
8272
|
+
await import_fs6.promises.mkdir(this.indexPath, { recursive: true });
|
|
7335
8273
|
const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
|
|
7336
|
-
const storePath =
|
|
8274
|
+
const storePath = path8.join(this.indexPath, "vectors");
|
|
7337
8275
|
this.store = new VectorStore(storePath, dimensions);
|
|
7338
|
-
const indexFilePath =
|
|
7339
|
-
if ((0,
|
|
8276
|
+
const indexFilePath = path8.join(this.indexPath, "vectors.usearch");
|
|
8277
|
+
if ((0, import_fs6.existsSync)(indexFilePath)) {
|
|
7340
8278
|
this.store.load();
|
|
7341
8279
|
}
|
|
7342
|
-
const invertedIndexPath =
|
|
8280
|
+
const invertedIndexPath = path8.join(this.indexPath, "inverted-index.json");
|
|
7343
8281
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
7344
8282
|
try {
|
|
7345
8283
|
this.invertedIndex.load();
|
|
7346
8284
|
} catch {
|
|
7347
|
-
if ((0,
|
|
7348
|
-
await
|
|
8285
|
+
if ((0, import_fs6.existsSync)(invertedIndexPath)) {
|
|
8286
|
+
await import_fs6.promises.unlink(invertedIndexPath);
|
|
8287
|
+
}
|
|
8288
|
+
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
8289
|
+
}
|
|
8290
|
+
const dbPath = path8.join(this.indexPath, "codebase.db");
|
|
8291
|
+
let dbIsNew = !(0, import_fs6.existsSync)(dbPath);
|
|
8292
|
+
try {
|
|
8293
|
+
this.database = new Database(dbPath);
|
|
8294
|
+
} catch (error) {
|
|
8295
|
+
if (!await this.tryResetCorruptedIndex("initializing index database", error)) {
|
|
8296
|
+
throw error;
|
|
7349
8297
|
}
|
|
8298
|
+
this.store = new VectorStore(storePath, dimensions);
|
|
7350
8299
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
8300
|
+
this.database = new Database(dbPath);
|
|
8301
|
+
dbIsNew = true;
|
|
8302
|
+
}
|
|
8303
|
+
if (isGitRepo(this.projectRoot)) {
|
|
8304
|
+
this.currentBranch = getBranchOrDefault(this.projectRoot);
|
|
8305
|
+
this.baseBranch = getBaseBranch(this.projectRoot);
|
|
8306
|
+
this.logger.branch("info", "Detected git repository", {
|
|
8307
|
+
currentBranch: this.currentBranch,
|
|
8308
|
+
baseBranch: this.baseBranch
|
|
8309
|
+
});
|
|
8310
|
+
} else {
|
|
8311
|
+
this.currentBranch = "default";
|
|
8312
|
+
this.baseBranch = "default";
|
|
8313
|
+
this.logger.branch("debug", "Not a git repository, using default branch");
|
|
7351
8314
|
}
|
|
7352
|
-
const dbPath = path7.join(this.indexPath, "codebase.db");
|
|
7353
|
-
const dbIsNew = !(0, import_fs5.existsSync)(dbPath);
|
|
7354
|
-
this.database = new Database(dbPath);
|
|
7355
8315
|
if (this.checkForInterruptedIndexing()) {
|
|
7356
8316
|
await this.recoverFromInterruptedIndexing();
|
|
7357
8317
|
}
|
|
7358
8318
|
if (dbIsNew && this.store.count() > 0) {
|
|
7359
8319
|
this.migrateFromLegacyIndex();
|
|
7360
8320
|
}
|
|
8321
|
+
this.loadFileHashCache();
|
|
7361
8322
|
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
7362
8323
|
if (!this.indexCompatibility.compatible) {
|
|
7363
8324
|
this.logger.warn("Index compatibility issue detected", {
|
|
@@ -7366,18 +8327,6 @@ var Indexer = class {
|
|
|
7366
8327
|
configuredProviderInfo: this.configuredProviderInfo
|
|
7367
8328
|
});
|
|
7368
8329
|
}
|
|
7369
|
-
if (isGitRepo(this.projectRoot)) {
|
|
7370
|
-
this.currentBranch = getBranchOrDefault(this.projectRoot);
|
|
7371
|
-
this.baseBranch = getBaseBranch(this.projectRoot);
|
|
7372
|
-
this.logger.branch("info", "Detected git repository", {
|
|
7373
|
-
currentBranch: this.currentBranch,
|
|
7374
|
-
baseBranch: this.baseBranch
|
|
7375
|
-
});
|
|
7376
|
-
} else {
|
|
7377
|
-
this.currentBranch = "default";
|
|
7378
|
-
this.baseBranch = "default";
|
|
7379
|
-
this.logger.branch("debug", "Not a git repository, using default branch");
|
|
7380
|
-
}
|
|
7381
8330
|
if (this.config.indexing.autoGc) {
|
|
7382
8331
|
await this.maybeRunAutoGc();
|
|
7383
8332
|
}
|
|
@@ -7397,20 +8346,169 @@ var Indexer = class {
|
|
|
7397
8346
|
}
|
|
7398
8347
|
}
|
|
7399
8348
|
if (shouldRunGc) {
|
|
7400
|
-
await this.healthCheck();
|
|
8349
|
+
const result = await this.healthCheck();
|
|
8350
|
+
if (result.warning) {
|
|
8351
|
+
this.database.setMetadata(STARTUP_WARNING_METADATA_KEY, result.warning);
|
|
8352
|
+
} else {
|
|
8353
|
+
this.database.deleteMetadata(STARTUP_WARNING_METADATA_KEY);
|
|
8354
|
+
}
|
|
7401
8355
|
this.database.setMetadata("lastGcTimestamp", now.toString());
|
|
7402
8356
|
}
|
|
7403
8357
|
}
|
|
7404
8358
|
async maybeRunOrphanGc() {
|
|
7405
|
-
if (!this.database) return;
|
|
8359
|
+
if (!this.database) return null;
|
|
7406
8360
|
const stats = this.database.getStats();
|
|
7407
|
-
if (!stats) return;
|
|
8361
|
+
if (!stats) return null;
|
|
7408
8362
|
const orphanCount = stats.embeddingCount - stats.chunkCount;
|
|
7409
8363
|
if (orphanCount > this.config.indexing.gcOrphanThreshold) {
|
|
7410
|
-
|
|
7411
|
-
|
|
8364
|
+
try {
|
|
8365
|
+
this.database.gcOrphanEmbeddings();
|
|
8366
|
+
this.database.gcOrphanChunks();
|
|
8367
|
+
} catch (error) {
|
|
8368
|
+
if (await this.tryResetCorruptedIndex("running automatic orphan garbage collection", error)) {
|
|
8369
|
+
return {
|
|
8370
|
+
resetCorruptedIndex: true,
|
|
8371
|
+
warning: this.getCorruptedIndexWarning(path8.join(this.indexPath, "codebase.db"))
|
|
8372
|
+
};
|
|
8373
|
+
}
|
|
8374
|
+
throw error;
|
|
8375
|
+
}
|
|
7412
8376
|
this.database.setMetadata("lastGcTimestamp", Date.now().toString());
|
|
7413
8377
|
}
|
|
8378
|
+
return null;
|
|
8379
|
+
}
|
|
8380
|
+
rebuildVectorStoreExcludingChunkIds(store, database, excludedChunkIds) {
|
|
8381
|
+
const excludedSet = new Set(excludedChunkIds);
|
|
8382
|
+
if (excludedSet.size === 0) {
|
|
8383
|
+
return;
|
|
8384
|
+
}
|
|
8385
|
+
const retainedEntries = store.getAllMetadata().filter(({ key }) => !excludedSet.has(key));
|
|
8386
|
+
const storeBasePath = path8.join(this.indexPath, "vectors");
|
|
8387
|
+
const storeIndexPath = `${storeBasePath}.usearch`;
|
|
8388
|
+
const storeMetadataPath = `${storeBasePath}.meta.json`;
|
|
8389
|
+
const backupIndexPath = `${storeIndexPath}.bak`;
|
|
8390
|
+
const backupMetadataPath = `${storeMetadataPath}.bak`;
|
|
8391
|
+
let backedUpIndex = false;
|
|
8392
|
+
let backedUpMetadata = false;
|
|
8393
|
+
let rebuiltCount = 0;
|
|
8394
|
+
let skippedCount = 0;
|
|
8395
|
+
if ((0, import_fs6.existsSync)(backupIndexPath)) {
|
|
8396
|
+
(0, import_fs6.unlinkSync)(backupIndexPath);
|
|
8397
|
+
}
|
|
8398
|
+
if ((0, import_fs6.existsSync)(backupMetadataPath)) {
|
|
8399
|
+
(0, import_fs6.unlinkSync)(backupMetadataPath);
|
|
8400
|
+
}
|
|
8401
|
+
try {
|
|
8402
|
+
if ((0, import_fs6.existsSync)(storeIndexPath)) {
|
|
8403
|
+
(0, import_fs6.renameSync)(storeIndexPath, backupIndexPath);
|
|
8404
|
+
backedUpIndex = true;
|
|
8405
|
+
}
|
|
8406
|
+
if ((0, import_fs6.existsSync)(storeMetadataPath)) {
|
|
8407
|
+
(0, import_fs6.renameSync)(storeMetadataPath, backupMetadataPath);
|
|
8408
|
+
backedUpMetadata = true;
|
|
8409
|
+
}
|
|
8410
|
+
store.clear();
|
|
8411
|
+
for (const { key, metadata } of retainedEntries) {
|
|
8412
|
+
const chunk = database.getChunk(key);
|
|
8413
|
+
if (!chunk) {
|
|
8414
|
+
skippedCount += 1;
|
|
8415
|
+
continue;
|
|
8416
|
+
}
|
|
8417
|
+
const embeddingBuffer = database.getEmbedding(chunk.contentHash);
|
|
8418
|
+
if (!embeddingBuffer) {
|
|
8419
|
+
skippedCount += 1;
|
|
8420
|
+
continue;
|
|
8421
|
+
}
|
|
8422
|
+
const vector = bufferToFloat32Array(embeddingBuffer);
|
|
8423
|
+
store.add(key, Array.from(vector), metadata);
|
|
8424
|
+
rebuiltCount += 1;
|
|
8425
|
+
}
|
|
8426
|
+
store.save();
|
|
8427
|
+
if (backedUpIndex && (0, import_fs6.existsSync)(backupIndexPath)) {
|
|
8428
|
+
(0, import_fs6.unlinkSync)(backupIndexPath);
|
|
8429
|
+
}
|
|
8430
|
+
if (backedUpMetadata && (0, import_fs6.existsSync)(backupMetadataPath)) {
|
|
8431
|
+
(0, import_fs6.unlinkSync)(backupMetadataPath);
|
|
8432
|
+
}
|
|
8433
|
+
this.logger.gc("info", "Rebuilt vector store to avoid native remove", {
|
|
8434
|
+
excludedChunks: excludedSet.size,
|
|
8435
|
+
rebuiltChunks: rebuiltCount,
|
|
8436
|
+
skippedChunks: skippedCount
|
|
8437
|
+
});
|
|
8438
|
+
} catch (error) {
|
|
8439
|
+
try {
|
|
8440
|
+
store.clear();
|
|
8441
|
+
} catch {
|
|
8442
|
+
}
|
|
8443
|
+
if ((0, import_fs6.existsSync)(storeIndexPath)) {
|
|
8444
|
+
(0, import_fs6.unlinkSync)(storeIndexPath);
|
|
8445
|
+
}
|
|
8446
|
+
if ((0, import_fs6.existsSync)(storeMetadataPath)) {
|
|
8447
|
+
(0, import_fs6.unlinkSync)(storeMetadataPath);
|
|
8448
|
+
}
|
|
8449
|
+
if (backedUpIndex && (0, import_fs6.existsSync)(backupIndexPath)) {
|
|
8450
|
+
(0, import_fs6.renameSync)(backupIndexPath, storeIndexPath);
|
|
8451
|
+
}
|
|
8452
|
+
if (backedUpMetadata && (0, import_fs6.existsSync)(backupMetadataPath)) {
|
|
8453
|
+
(0, import_fs6.renameSync)(backupMetadataPath, storeMetadataPath);
|
|
8454
|
+
}
|
|
8455
|
+
if (backedUpIndex || backedUpMetadata) {
|
|
8456
|
+
store.load();
|
|
8457
|
+
}
|
|
8458
|
+
throw error;
|
|
8459
|
+
}
|
|
8460
|
+
}
|
|
8461
|
+
getCorruptedIndexWarning(dbPath) {
|
|
8462
|
+
if (this.config.scope === "global") {
|
|
8463
|
+
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.`;
|
|
8464
|
+
}
|
|
8465
|
+
return `Detected a corrupted local SQLite index at ${dbPath} and reset the local index. Run index_codebase to rebuild search data.`;
|
|
8466
|
+
}
|
|
8467
|
+
async tryResetCorruptedIndex(stage, error) {
|
|
8468
|
+
if (!isSqliteCorruptionError(error)) {
|
|
8469
|
+
return false;
|
|
8470
|
+
}
|
|
8471
|
+
const dbPath = path8.join(this.indexPath, "codebase.db");
|
|
8472
|
+
const warning = this.getCorruptedIndexWarning(dbPath);
|
|
8473
|
+
const errorMessage = getErrorMessage(error);
|
|
8474
|
+
if (this.config.scope === "global") {
|
|
8475
|
+
this.logger.error("Detected corrupted shared global index database", {
|
|
8476
|
+
stage,
|
|
8477
|
+
dbPath,
|
|
8478
|
+
error: errorMessage
|
|
8479
|
+
});
|
|
8480
|
+
throw new Error(`${warning} Original SQLite error: ${errorMessage}`);
|
|
8481
|
+
}
|
|
8482
|
+
this.logger.warn("Detected corrupted local index database, resetting local index", {
|
|
8483
|
+
stage,
|
|
8484
|
+
dbPath,
|
|
8485
|
+
error: errorMessage
|
|
8486
|
+
});
|
|
8487
|
+
this.store = null;
|
|
8488
|
+
this.invertedIndex = null;
|
|
8489
|
+
this.database?.close();
|
|
8490
|
+
this.database = null;
|
|
8491
|
+
this.indexCompatibility = null;
|
|
8492
|
+
this.fileHashCache.clear();
|
|
8493
|
+
const resetPaths = [
|
|
8494
|
+
path8.join(this.indexPath, "codebase.db"),
|
|
8495
|
+
path8.join(this.indexPath, "codebase.db-shm"),
|
|
8496
|
+
path8.join(this.indexPath, "codebase.db-wal"),
|
|
8497
|
+
path8.join(this.indexPath, "vectors.usearch"),
|
|
8498
|
+
path8.join(this.indexPath, "inverted-index.json"),
|
|
8499
|
+
path8.join(this.indexPath, "file-hashes.json"),
|
|
8500
|
+
path8.join(this.indexPath, "failed-batches.json"),
|
|
8501
|
+
path8.join(this.indexPath, "indexing.lock"),
|
|
8502
|
+
path8.join(this.indexPath, "vectors")
|
|
8503
|
+
];
|
|
8504
|
+
await Promise.all(resetPaths.map(async (targetPath) => {
|
|
8505
|
+
try {
|
|
8506
|
+
await import_fs6.promises.rm(targetPath, { recursive: true, force: true });
|
|
8507
|
+
} catch {
|
|
8508
|
+
}
|
|
8509
|
+
}));
|
|
8510
|
+
await import_fs6.promises.mkdir(this.indexPath, { recursive: true });
|
|
8511
|
+
return true;
|
|
7414
8512
|
}
|
|
7415
8513
|
migrateFromLegacyIndex() {
|
|
7416
8514
|
if (!this.store || !this.database) return;
|
|
@@ -7434,7 +8532,7 @@ var Indexer = class {
|
|
|
7434
8532
|
if (chunkDataBatch.length > 0) {
|
|
7435
8533
|
this.database.upsertChunksBatch(chunkDataBatch);
|
|
7436
8534
|
}
|
|
7437
|
-
this.database.addChunksToBranchBatch(this.
|
|
8535
|
+
this.database.addChunksToBranchBatch(this.getBranchCatalogKey(), chunkIds);
|
|
7438
8536
|
}
|
|
7439
8537
|
loadIndexMetadata() {
|
|
7440
8538
|
if (!this.database) return null;
|
|
@@ -7445,6 +8543,7 @@ var Indexer = class {
|
|
|
7445
8543
|
embeddingProvider: this.database.getMetadata("index.embeddingProvider") ?? "",
|
|
7446
8544
|
embeddingModel: this.database.getMetadata("index.embeddingModel") ?? "",
|
|
7447
8545
|
embeddingDimensions: parseInt(this.database.getMetadata("index.embeddingDimensions") ?? "0", 10),
|
|
8546
|
+
embeddingStrategyVersion: this.loadStoredEmbeddingStrategyVersion() ?? EMBEDDING_STRATEGY_VERSION,
|
|
7448
8547
|
createdAt: this.database.getMetadata("index.createdAt") ?? "",
|
|
7449
8548
|
updatedAt: this.database.getMetadata("index.updatedAt") ?? ""
|
|
7450
8549
|
};
|
|
@@ -7453,10 +8552,22 @@ var Indexer = class {
|
|
|
7453
8552
|
if (!this.database) return;
|
|
7454
8553
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7455
8554
|
const existingCreatedAt = this.database.getMetadata("index.createdAt");
|
|
8555
|
+
const completeProjectEmbeddingStrategyReset = !this.hasProjectForceReembedPending();
|
|
7456
8556
|
this.database.setMetadata("index.version", INDEX_METADATA_VERSION);
|
|
7457
8557
|
this.database.setMetadata("index.embeddingProvider", provider.provider);
|
|
7458
8558
|
this.database.setMetadata("index.embeddingModel", provider.modelInfo.model);
|
|
7459
8559
|
this.database.setMetadata("index.embeddingDimensions", provider.modelInfo.dimensions.toString());
|
|
8560
|
+
if (this.config.scope === "global") {
|
|
8561
|
+
if (completeProjectEmbeddingStrategyReset) {
|
|
8562
|
+
this.database.setMetadata(this.getProjectEmbeddingStrategyMetadataKey(), EMBEDDING_STRATEGY_VERSION);
|
|
8563
|
+
}
|
|
8564
|
+
this.database.setMetadata(this.getLegacyMigrationMetadataKey(), "done");
|
|
8565
|
+
if (completeProjectEmbeddingStrategyReset) {
|
|
8566
|
+
this.database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
8567
|
+
}
|
|
8568
|
+
} else {
|
|
8569
|
+
this.database.setMetadata("index.embeddingStrategyVersion", EMBEDDING_STRATEGY_VERSION);
|
|
8570
|
+
}
|
|
7460
8571
|
this.database.setMetadata("index.updatedAt", now);
|
|
7461
8572
|
if (!existingCreatedAt) {
|
|
7462
8573
|
this.database.setMetadata("index.createdAt", now);
|
|
@@ -7486,6 +8597,14 @@ var Indexer = class {
|
|
|
7486
8597
|
storedMetadata
|
|
7487
8598
|
};
|
|
7488
8599
|
}
|
|
8600
|
+
if (storedMetadata.embeddingStrategyVersion !== EMBEDDING_STRATEGY_VERSION) {
|
|
8601
|
+
return {
|
|
8602
|
+
compatible: false,
|
|
8603
|
+
code: "EMBEDDING_STRATEGY_MISMATCH" /* EMBEDDING_STRATEGY_MISMATCH */,
|
|
8604
|
+
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.`,
|
|
8605
|
+
storedMetadata
|
|
8606
|
+
};
|
|
8607
|
+
}
|
|
7489
8608
|
if (storedMetadata.embeddingProvider !== currentProvider) {
|
|
7490
8609
|
this.logger.warn("Provider changed", {
|
|
7491
8610
|
storedProvider: storedMetadata.embeddingProvider,
|
|
@@ -7533,6 +8652,10 @@ var Indexer = class {
|
|
|
7533
8652
|
}
|
|
7534
8653
|
async index(onProgress) {
|
|
7535
8654
|
const { store, provider, invertedIndex, database, configuredProviderInfo } = await this.ensureInitialized();
|
|
8655
|
+
const scopedRoots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
8656
|
+
const branchCatalogKey = this.getBranchCatalogKey();
|
|
8657
|
+
const forceScopedReembed = scopedRoots !== null && database.getMetadata(this.getProjectForceReembedMetadataKey()) === "true";
|
|
8658
|
+
const failedForcedChunkIds = /* @__PURE__ */ new Set();
|
|
7536
8659
|
if (!this.indexCompatibility?.compatible) {
|
|
7537
8660
|
throw new Error(
|
|
7538
8661
|
`${this.indexCompatibility?.reason} Run index_codebase with force=true to rebuild the index.`
|
|
@@ -7554,6 +8677,7 @@ var Indexer = class {
|
|
|
7554
8677
|
skippedFiles: [],
|
|
7555
8678
|
parseFailures: []
|
|
7556
8679
|
};
|
|
8680
|
+
const failedBatchesForCurrentRun = [];
|
|
7557
8681
|
onProgress?.({
|
|
7558
8682
|
phase: "scanning",
|
|
7559
8683
|
filesProcessed: 0,
|
|
@@ -7588,7 +8712,7 @@ var Indexer = class {
|
|
|
7588
8712
|
unchangedFilePaths.add(f.path);
|
|
7589
8713
|
this.logger.recordCacheHit();
|
|
7590
8714
|
} else {
|
|
7591
|
-
const content = await
|
|
8715
|
+
const content = await import_fs6.promises.readFile(f.path, "utf-8");
|
|
7592
8716
|
changedFiles.push({ path: f.path, content, hash: currentHash });
|
|
7593
8717
|
this.logger.recordCacheMiss();
|
|
7594
8718
|
}
|
|
@@ -7613,6 +8737,12 @@ var Indexer = class {
|
|
|
7613
8737
|
const existingChunks = /* @__PURE__ */ new Map();
|
|
7614
8738
|
const existingChunksByFile = /* @__PURE__ */ new Map();
|
|
7615
8739
|
for (const { key, metadata } of store.getAllMetadata()) {
|
|
8740
|
+
if (scopedRoots && !this.isFileInCurrentScope(metadata.filePath, scopedRoots)) {
|
|
8741
|
+
continue;
|
|
8742
|
+
}
|
|
8743
|
+
if (forceScopedReembed && scopedRoots && this.isFileInCurrentScope(metadata.filePath, scopedRoots)) {
|
|
8744
|
+
continue;
|
|
8745
|
+
}
|
|
7616
8746
|
existingChunks.set(key, metadata.hash);
|
|
7617
8747
|
const fileChunks = existingChunksByFile.get(metadata.filePath) || /* @__PURE__ */ new Set();
|
|
7618
8748
|
fileChunks.add(key);
|
|
@@ -7634,7 +8764,7 @@ var Indexer = class {
|
|
|
7634
8764
|
for (const parsed of parsedFiles) {
|
|
7635
8765
|
currentFilePaths.add(parsed.path);
|
|
7636
8766
|
if (parsed.chunks.length === 0) {
|
|
7637
|
-
const relativePath =
|
|
8767
|
+
const relativePath = path8.relative(this.projectRoot, parsed.path);
|
|
7638
8768
|
stats.parseFailures.push(relativePath);
|
|
7639
8769
|
}
|
|
7640
8770
|
let fileChunkCount = 0;
|
|
@@ -7670,7 +8800,10 @@ var Indexer = class {
|
|
|
7670
8800
|
fileChunkCount++;
|
|
7671
8801
|
continue;
|
|
7672
8802
|
}
|
|
7673
|
-
const
|
|
8803
|
+
const texts = createEmbeddingTexts(chunk, parsed.path, getSafeEmbeddingChunkTokenLimit(configuredProviderInfo)).map((text) => ({
|
|
8804
|
+
text,
|
|
8805
|
+
tokenCount: estimateTokens(text)
|
|
8806
|
+
}));
|
|
7674
8807
|
const metadata = {
|
|
7675
8808
|
filePath: parsed.path,
|
|
7676
8809
|
startLine: chunk.startLine,
|
|
@@ -7680,10 +8813,38 @@ var Indexer = class {
|
|
|
7680
8813
|
language: chunk.language,
|
|
7681
8814
|
hash: contentHash
|
|
7682
8815
|
};
|
|
7683
|
-
pendingChunks.push({
|
|
8816
|
+
pendingChunks.push({
|
|
8817
|
+
id,
|
|
8818
|
+
texts,
|
|
8819
|
+
storageText: createPendingChunkStorageText(texts),
|
|
8820
|
+
content: chunk.content,
|
|
8821
|
+
contentHash,
|
|
8822
|
+
metadata
|
|
8823
|
+
});
|
|
7684
8824
|
fileChunkCount++;
|
|
7685
8825
|
}
|
|
7686
8826
|
}
|
|
8827
|
+
const retryableFailedChunks = this.collectRetryableFailedChunks(
|
|
8828
|
+
currentFileHashes,
|
|
8829
|
+
unchangedFilePaths,
|
|
8830
|
+
getSafeEmbeddingChunkTokenLimit(configuredProviderInfo)
|
|
8831
|
+
);
|
|
8832
|
+
const retryableFailedAttemptCounts = /* @__PURE__ */ new Map();
|
|
8833
|
+
const retryableChunksWithExistingData = /* @__PURE__ */ new Set();
|
|
8834
|
+
if (retryableFailedChunks.length > 0) {
|
|
8835
|
+
const pendingChunkIds = new Set(pendingChunks.map((chunk) => chunk.id));
|
|
8836
|
+
for (const { chunk, attemptCount } of retryableFailedChunks) {
|
|
8837
|
+
retryableFailedAttemptCounts.set(chunk.id, attemptCount);
|
|
8838
|
+
if (existingChunks.has(chunk.id)) {
|
|
8839
|
+
retryableChunksWithExistingData.add(chunk.id);
|
|
8840
|
+
}
|
|
8841
|
+
if (!pendingChunkIds.has(chunk.id)) {
|
|
8842
|
+
pendingChunks.push(chunk);
|
|
8843
|
+
pendingChunkIds.add(chunk.id);
|
|
8844
|
+
currentChunkIds.add(chunk.id);
|
|
8845
|
+
}
|
|
8846
|
+
}
|
|
8847
|
+
}
|
|
7687
8848
|
if (chunkDataBatch.length > 0) {
|
|
7688
8849
|
database.upsertChunksBatch(chunkDataBatch);
|
|
7689
8850
|
}
|
|
@@ -7712,17 +8873,20 @@ var Indexer = class {
|
|
|
7712
8873
|
fileSymbols.push(symbol);
|
|
7713
8874
|
allSymbolIds.add(symbolId);
|
|
7714
8875
|
}
|
|
8876
|
+
const fileLanguage = parsed.chunks[0]?.language;
|
|
8877
|
+
const isCaseInsensitiveLanguage = !!fileLanguage && CASE_INSENSITIVE_LANGUAGES.has(fileLanguage);
|
|
8878
|
+
const normalizeSymbolKey = (name) => isCaseInsensitiveLanguage ? name.toLowerCase() : name;
|
|
7715
8879
|
const symbolsByName = /* @__PURE__ */ new Map();
|
|
7716
8880
|
for (const symbol of fileSymbols) {
|
|
7717
|
-
const
|
|
8881
|
+
const key = normalizeSymbolKey(symbol.name);
|
|
8882
|
+
const existing = symbolsByName.get(key) ?? [];
|
|
7718
8883
|
existing.push(symbol);
|
|
7719
|
-
symbolsByName.set(
|
|
8884
|
+
symbolsByName.set(key, existing);
|
|
7720
8885
|
}
|
|
7721
8886
|
if (fileSymbols.length > 0) {
|
|
7722
8887
|
database.upsertSymbolsBatch(fileSymbols);
|
|
7723
8888
|
symbolsByFile.set(parsed.path, fileSymbols);
|
|
7724
8889
|
}
|
|
7725
|
-
const fileLanguage = parsed.chunks[0]?.language;
|
|
7726
8890
|
if (!fileLanguage || !CALL_GRAPH_LANGUAGES.has(fileLanguage)) continue;
|
|
7727
8891
|
const callSites = extractCalls(changedFile.content, fileLanguage);
|
|
7728
8892
|
if (callSites.length === 0) continue;
|
|
@@ -7747,7 +8911,7 @@ var Indexer = class {
|
|
|
7747
8911
|
if (edges.length > 0) {
|
|
7748
8912
|
database.upsertCallEdgesBatch(edges);
|
|
7749
8913
|
for (const edge of edges) {
|
|
7750
|
-
const candidates = symbolsByName.get(edge.targetName);
|
|
8914
|
+
const candidates = symbolsByName.get(normalizeSymbolKey(edge.targetName));
|
|
7751
8915
|
if (candidates && candidates.length === 1) {
|
|
7752
8916
|
database.resolveCallEdge(edge.id, candidates[0].id);
|
|
7753
8917
|
}
|
|
@@ -7760,14 +8924,20 @@ var Indexer = class {
|
|
|
7760
8924
|
allSymbolIds.add(sym.id);
|
|
7761
8925
|
}
|
|
7762
8926
|
}
|
|
7763
|
-
|
|
8927
|
+
const removedChunkIds = [];
|
|
7764
8928
|
for (const [chunkId] of existingChunks) {
|
|
7765
8929
|
if (!currentChunkIds.has(chunkId)) {
|
|
7766
|
-
|
|
8930
|
+
removedChunkIds.push(chunkId);
|
|
8931
|
+
}
|
|
8932
|
+
}
|
|
8933
|
+
if (removedChunkIds.length > 0) {
|
|
8934
|
+
this.rebuildVectorStoreExcludingChunkIds(store, database, removedChunkIds);
|
|
8935
|
+
for (const chunkId of removedChunkIds) {
|
|
7767
8936
|
invertedIndex.removeChunk(chunkId);
|
|
7768
|
-
removedCount++;
|
|
7769
8937
|
}
|
|
8938
|
+
database.deleteChunksByIds(removedChunkIds);
|
|
7770
8939
|
}
|
|
8940
|
+
const removedCount = removedChunkIds.length;
|
|
7771
8941
|
stats.totalChunks = pendingChunks.length;
|
|
7772
8942
|
stats.existingChunks = currentChunkIds.size - pendingChunks.length;
|
|
7773
8943
|
stats.removedChunks = removedCount;
|
|
@@ -7779,12 +8949,20 @@ var Indexer = class {
|
|
|
7779
8949
|
removed: removedCount
|
|
7780
8950
|
});
|
|
7781
8951
|
if (pendingChunks.length === 0 && removedCount === 0) {
|
|
7782
|
-
database.clearBranch(
|
|
7783
|
-
database.addChunksToBranchBatch(
|
|
7784
|
-
database.clearBranchSymbols(
|
|
7785
|
-
database.addSymbolsToBranchBatch(
|
|
7786
|
-
|
|
7787
|
-
|
|
8952
|
+
database.clearBranch(branchCatalogKey);
|
|
8953
|
+
database.addChunksToBranchBatch(branchCatalogKey, Array.from(currentChunkIds));
|
|
8954
|
+
database.clearBranchSymbols(branchCatalogKey);
|
|
8955
|
+
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
8956
|
+
if (scopedRoots) {
|
|
8957
|
+
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
8958
|
+
this.clearScopedFailedBatches(scopedRoots);
|
|
8959
|
+
} else {
|
|
8960
|
+
this.fileHashCache = currentFileHashes;
|
|
8961
|
+
this.saveFileHashCache();
|
|
8962
|
+
this.saveFailedBatches([]);
|
|
8963
|
+
}
|
|
8964
|
+
this.saveIndexMetadata(configuredProviderInfo);
|
|
8965
|
+
this.indexCompatibility = { compatible: true };
|
|
7788
8966
|
stats.durationMs = Date.now() - startTime;
|
|
7789
8967
|
onProgress?.({
|
|
7790
8968
|
phase: "complete",
|
|
@@ -7797,14 +8975,22 @@ var Indexer = class {
|
|
|
7797
8975
|
return stats;
|
|
7798
8976
|
}
|
|
7799
8977
|
if (pendingChunks.length === 0) {
|
|
7800
|
-
database.clearBranch(
|
|
7801
|
-
database.addChunksToBranchBatch(
|
|
7802
|
-
database.clearBranchSymbols(
|
|
7803
|
-
database.addSymbolsToBranchBatch(
|
|
8978
|
+
database.clearBranch(branchCatalogKey);
|
|
8979
|
+
database.addChunksToBranchBatch(branchCatalogKey, Array.from(currentChunkIds));
|
|
8980
|
+
database.clearBranchSymbols(branchCatalogKey);
|
|
8981
|
+
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
7804
8982
|
store.save();
|
|
7805
8983
|
invertedIndex.save();
|
|
7806
|
-
|
|
7807
|
-
|
|
8984
|
+
if (scopedRoots) {
|
|
8985
|
+
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
8986
|
+
this.clearScopedFailedBatches(scopedRoots);
|
|
8987
|
+
} else {
|
|
8988
|
+
this.fileHashCache = currentFileHashes;
|
|
8989
|
+
this.saveFileHashCache();
|
|
8990
|
+
this.saveFailedBatches([]);
|
|
8991
|
+
}
|
|
8992
|
+
this.saveIndexMetadata(configuredProviderInfo);
|
|
8993
|
+
this.indexCompatibility = { compatible: true };
|
|
7808
8994
|
stats.durationMs = Date.now() - startTime;
|
|
7809
8995
|
onProgress?.({
|
|
7810
8996
|
phase: "complete",
|
|
@@ -7825,8 +9011,9 @@ var Indexer = class {
|
|
|
7825
9011
|
});
|
|
7826
9012
|
const allContentHashes = pendingChunks.map((c) => c.contentHash);
|
|
7827
9013
|
const missingHashes = new Set(database.getMissingEmbeddings(allContentHashes));
|
|
7828
|
-
const
|
|
7829
|
-
const
|
|
9014
|
+
const forcedReembedChunkIds = forceScopedReembed ? new Set(pendingChunks.map((chunk) => chunk.id)) : /* @__PURE__ */ new Set();
|
|
9015
|
+
const chunksNeedingEmbedding = pendingChunks.filter((c) => forcedReembedChunkIds.has(c.id) || missingHashes.has(c.contentHash));
|
|
9016
|
+
const chunksWithExistingEmbedding = pendingChunks.filter((c) => !forcedReembedChunkIds.has(c.id) && !missingHashes.has(c.contentHash));
|
|
7830
9017
|
this.logger.cache("info", "Embedding cache lookup", {
|
|
7831
9018
|
needsEmbedding: chunksNeedingEmbedding.length,
|
|
7832
9019
|
fromCache: chunksWithExistingEmbedding.length
|
|
@@ -7848,17 +9035,24 @@ var Indexer = class {
|
|
|
7848
9035
|
interval: providerRateLimits.intervalMs,
|
|
7849
9036
|
intervalCap: providerRateLimits.concurrency
|
|
7850
9037
|
});
|
|
7851
|
-
const
|
|
9038
|
+
const pendingChunksById = new Map(chunksNeedingEmbedding.map((chunk) => [chunk.id, chunk]));
|
|
9039
|
+
const embeddingPartsByChunk = /* @__PURE__ */ new Map();
|
|
9040
|
+
const completedChunkIds = /* @__PURE__ */ new Set();
|
|
9041
|
+
const failedChunkIds = /* @__PURE__ */ new Set();
|
|
9042
|
+
const requestBatches = createPendingEmbeddingRequestBatches(
|
|
9043
|
+
chunksNeedingEmbedding,
|
|
9044
|
+
getDynamicBatchOptions(configuredProviderInfo)
|
|
9045
|
+
);
|
|
7852
9046
|
let rateLimitBackoffMs = 0;
|
|
7853
|
-
for (const
|
|
9047
|
+
for (const requestBatch of requestBatches) {
|
|
7854
9048
|
queue.add(async () => {
|
|
7855
9049
|
if (rateLimitBackoffMs > 0) {
|
|
7856
|
-
await new Promise((
|
|
9050
|
+
await new Promise((resolve9) => setTimeout(resolve9, rateLimitBackoffMs));
|
|
7857
9051
|
}
|
|
7858
9052
|
try {
|
|
7859
9053
|
const result = await pRetry(
|
|
7860
9054
|
async () => {
|
|
7861
|
-
const texts =
|
|
9055
|
+
const texts = requestBatch.map((request) => request.text);
|
|
7862
9056
|
return provider.embedBatch(texts);
|
|
7863
9057
|
},
|
|
7864
9058
|
{
|
|
@@ -7888,29 +9082,82 @@ var Indexer = class {
|
|
|
7888
9082
|
if (rateLimitBackoffMs > 0) {
|
|
7889
9083
|
rateLimitBackoffMs = Math.max(0, rateLimitBackoffMs - 2e3);
|
|
7890
9084
|
}
|
|
7891
|
-
const
|
|
7892
|
-
|
|
7893
|
-
|
|
7894
|
-
|
|
7895
|
-
|
|
7896
|
-
|
|
7897
|
-
|
|
7898
|
-
|
|
7899
|
-
|
|
7900
|
-
|
|
7901
|
-
|
|
7902
|
-
|
|
7903
|
-
|
|
7904
|
-
|
|
7905
|
-
|
|
7906
|
-
|
|
9085
|
+
const touchedChunkIds = /* @__PURE__ */ new Set();
|
|
9086
|
+
requestBatch.forEach((request, idx) => {
|
|
9087
|
+
if (failedChunkIds.has(request.chunk.id) || completedChunkIds.has(request.chunk.id)) {
|
|
9088
|
+
return;
|
|
9089
|
+
}
|
|
9090
|
+
const vector = result.embeddings[idx];
|
|
9091
|
+
if (!vector) {
|
|
9092
|
+
throw new Error(`Embedding API returned too few vectors for chunk ${request.chunk.id}`);
|
|
9093
|
+
}
|
|
9094
|
+
const parts = embeddingPartsByChunk.get(request.chunk.id) ?? [];
|
|
9095
|
+
parts[request.partIndex] = {
|
|
9096
|
+
vector,
|
|
9097
|
+
tokenCount: request.tokenCount
|
|
9098
|
+
};
|
|
9099
|
+
embeddingPartsByChunk.set(request.chunk.id, parts);
|
|
9100
|
+
touchedChunkIds.add(request.chunk.id);
|
|
9101
|
+
});
|
|
9102
|
+
const pooledResults = [];
|
|
9103
|
+
for (const chunkId of touchedChunkIds) {
|
|
9104
|
+
if (failedChunkIds.has(chunkId) || completedChunkIds.has(chunkId)) {
|
|
9105
|
+
continue;
|
|
9106
|
+
}
|
|
9107
|
+
const chunk = pendingChunksById.get(chunkId);
|
|
9108
|
+
if (!chunk) {
|
|
9109
|
+
continue;
|
|
9110
|
+
}
|
|
9111
|
+
const parts = embeddingPartsByChunk.get(chunk.id) ?? [];
|
|
9112
|
+
if (!hasAllEmbeddingParts(parts, chunk.texts.length)) {
|
|
9113
|
+
continue;
|
|
9114
|
+
}
|
|
9115
|
+
const orderedParts = parts;
|
|
9116
|
+
pooledResults.push({
|
|
9117
|
+
chunk,
|
|
9118
|
+
vector: poolEmbeddingVectors(
|
|
9119
|
+
orderedParts.map((part) => part.vector),
|
|
9120
|
+
orderedParts.map((part) => part.tokenCount)
|
|
9121
|
+
)
|
|
9122
|
+
});
|
|
9123
|
+
}
|
|
9124
|
+
if (pooledResults.length > 0) {
|
|
9125
|
+
const items = pooledResults.map(({ chunk, vector }) => ({
|
|
9126
|
+
id: chunk.id,
|
|
9127
|
+
vector,
|
|
9128
|
+
metadata: chunk.metadata
|
|
9129
|
+
}));
|
|
9130
|
+
store.addBatch(items);
|
|
9131
|
+
const embeddingBatchItems = pooledResults.map(({ chunk, vector }) => ({
|
|
9132
|
+
contentHash: chunk.contentHash,
|
|
9133
|
+
embedding: float32ArrayToBuffer(vector),
|
|
9134
|
+
chunkText: chunk.storageText,
|
|
9135
|
+
model: configuredProviderInfo.modelInfo.model
|
|
9136
|
+
}));
|
|
9137
|
+
try {
|
|
9138
|
+
database.upsertEmbeddingsBatch(embeddingBatchItems);
|
|
9139
|
+
} catch (dbError) {
|
|
9140
|
+
this.rebuildVectorStoreExcludingChunkIds(
|
|
9141
|
+
store,
|
|
9142
|
+
database,
|
|
9143
|
+
pooledResults.map(({ chunk }) => chunk.id)
|
|
9144
|
+
);
|
|
9145
|
+
throw dbError;
|
|
9146
|
+
}
|
|
9147
|
+
for (const { chunk } of pooledResults) {
|
|
9148
|
+
invertedIndex.removeChunk(chunk.id);
|
|
9149
|
+
invertedIndex.addChunk(chunk.id, chunk.content);
|
|
9150
|
+
completedChunkIds.add(chunk.id);
|
|
9151
|
+
embeddingPartsByChunk.delete(chunk.id);
|
|
9152
|
+
}
|
|
9153
|
+
stats.indexedChunks += pooledResults.length;
|
|
9154
|
+
this.logger.recordChunksEmbedded(pooledResults.length);
|
|
7907
9155
|
}
|
|
7908
|
-
stats.indexedChunks += batch.length;
|
|
7909
9156
|
stats.tokensUsed += result.totalTokensUsed;
|
|
7910
|
-
this.logger.recordChunksEmbedded(batch.length);
|
|
7911
9157
|
this.logger.recordEmbeddingApiCall(result.totalTokensUsed);
|
|
7912
9158
|
this.logger.embedding("debug", `Embedded batch`, {
|
|
7913
|
-
batchSize:
|
|
9159
|
+
batchSize: pooledResults.length,
|
|
9160
|
+
requestCount: requestBatch.length,
|
|
7914
9161
|
tokens: result.totalTokensUsed
|
|
7915
9162
|
});
|
|
7916
9163
|
onProgress?.({
|
|
@@ -7921,17 +9168,49 @@ var Indexer = class {
|
|
|
7921
9168
|
totalChunks: pendingChunks.length
|
|
7922
9169
|
});
|
|
7923
9170
|
} catch (error) {
|
|
7924
|
-
|
|
7925
|
-
|
|
9171
|
+
const failedChunks = getUniquePendingChunksFromRequests(requestBatch).filter((chunk) => !completedChunkIds.has(chunk.id));
|
|
9172
|
+
const failureMessage = getErrorMessage(error);
|
|
9173
|
+
const failureTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
9174
|
+
for (const chunk of failedChunks) {
|
|
9175
|
+
if (!failedChunkIds.has(chunk.id)) {
|
|
9176
|
+
failedChunkIds.add(chunk.id);
|
|
9177
|
+
stats.failedChunks += 1;
|
|
9178
|
+
}
|
|
9179
|
+
if (forceScopedReembed) {
|
|
9180
|
+
failedForcedChunkIds.add(chunk.id);
|
|
9181
|
+
}
|
|
9182
|
+
embeddingPartsByChunk.delete(chunk.id);
|
|
9183
|
+
const existingFailedBatchIndex = failedBatchesForCurrentRun.findIndex(
|
|
9184
|
+
(failedBatch2) => failedBatch2.chunks[0]?.id === chunk.id
|
|
9185
|
+
);
|
|
9186
|
+
const existingFailedBatch = existingFailedBatchIndex === -1 ? void 0 : failedBatchesForCurrentRun[existingFailedBatchIndex];
|
|
9187
|
+
const failedBatch = {
|
|
9188
|
+
chunks: [chunk],
|
|
9189
|
+
error: failureMessage,
|
|
9190
|
+
attemptCount: (existingFailedBatch?.attemptCount ?? retryableFailedAttemptCounts.get(chunk.id) ?? 0) + 1,
|
|
9191
|
+
lastAttempt: failureTimestamp
|
|
9192
|
+
};
|
|
9193
|
+
if (existingFailedBatchIndex === -1) {
|
|
9194
|
+
failedBatchesForCurrentRun.push(failedBatch);
|
|
9195
|
+
} else {
|
|
9196
|
+
failedBatchesForCurrentRun[existingFailedBatchIndex] = failedBatch;
|
|
9197
|
+
}
|
|
9198
|
+
}
|
|
7926
9199
|
this.logger.recordEmbeddingError();
|
|
7927
9200
|
this.logger.embedding("error", `Failed to embed batch after retries`, {
|
|
7928
|
-
batchSize:
|
|
7929
|
-
|
|
9201
|
+
batchSize: failedChunks.length,
|
|
9202
|
+
requestCount: requestBatch.length,
|
|
9203
|
+
error: failureMessage
|
|
7930
9204
|
});
|
|
7931
9205
|
}
|
|
7932
9206
|
});
|
|
7933
9207
|
}
|
|
7934
9208
|
await queue.onIdle();
|
|
9209
|
+
if (scopedRoots) {
|
|
9210
|
+
this.saveScopedFailedBatches(coalesceFailedBatches(failedBatchesForCurrentRun), scopedRoots);
|
|
9211
|
+
} else {
|
|
9212
|
+
this.saveFailedBatches(coalesceFailedBatches(failedBatchesForCurrentRun));
|
|
9213
|
+
}
|
|
7935
9214
|
onProgress?.({
|
|
7936
9215
|
phase: "storing",
|
|
7937
9216
|
filesProcessed: files.length,
|
|
@@ -7939,18 +9218,48 @@ var Indexer = class {
|
|
|
7939
9218
|
chunksProcessed: stats.indexedChunks,
|
|
7940
9219
|
totalChunks: pendingChunks.length
|
|
7941
9220
|
});
|
|
7942
|
-
|
|
7943
|
-
|
|
7944
|
-
|
|
7945
|
-
|
|
9221
|
+
const branchChunkIds = Array.from(currentChunkIds).filter(
|
|
9222
|
+
(chunkId) => {
|
|
9223
|
+
const isNewlyFailed = failedChunkIds.has(chunkId) && !retryableChunksWithExistingData.has(chunkId);
|
|
9224
|
+
const isForcedFailed = forceScopedReembed && failedForcedChunkIds.has(chunkId);
|
|
9225
|
+
return !isNewlyFailed && !isForcedFailed;
|
|
9226
|
+
}
|
|
9227
|
+
);
|
|
9228
|
+
database.clearBranch(branchCatalogKey);
|
|
9229
|
+
database.addChunksToBranchBatch(branchCatalogKey, branchChunkIds);
|
|
9230
|
+
database.clearBranchSymbols(branchCatalogKey);
|
|
9231
|
+
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
7946
9232
|
store.save();
|
|
7947
9233
|
invertedIndex.save();
|
|
7948
|
-
|
|
7949
|
-
|
|
9234
|
+
if (scopedRoots) {
|
|
9235
|
+
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
9236
|
+
} else {
|
|
9237
|
+
this.fileHashCache = currentFileHashes;
|
|
9238
|
+
this.saveFileHashCache();
|
|
9239
|
+
}
|
|
7950
9240
|
if (this.config.indexing.autoGc && stats.removedChunks > 0) {
|
|
7951
|
-
await this.maybeRunOrphanGc();
|
|
9241
|
+
const gcReset = await this.maybeRunOrphanGc();
|
|
9242
|
+
if (gcReset) {
|
|
9243
|
+
stats.durationMs = Date.now() - startTime;
|
|
9244
|
+
stats.warning = gcReset.warning;
|
|
9245
|
+
stats.resetCorruptedIndex = true;
|
|
9246
|
+
this.logger.recordIndexingEnd();
|
|
9247
|
+
this.logger.warn("Indexing ended after resetting corrupted local index during automatic GC", {
|
|
9248
|
+
files: stats.totalFiles,
|
|
9249
|
+
indexed: stats.indexedChunks,
|
|
9250
|
+
existing: stats.existingChunks,
|
|
9251
|
+
removed: stats.removedChunks,
|
|
9252
|
+
failed: stats.failedChunks,
|
|
9253
|
+
tokens: stats.tokensUsed,
|
|
9254
|
+
durationMs: stats.durationMs
|
|
9255
|
+
});
|
|
9256
|
+
return stats;
|
|
9257
|
+
}
|
|
7952
9258
|
}
|
|
7953
9259
|
stats.durationMs = Date.now() - startTime;
|
|
9260
|
+
if (forceScopedReembed && failedForcedChunkIds.size === 0) {
|
|
9261
|
+
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
9262
|
+
}
|
|
7954
9263
|
this.saveIndexMetadata(configuredProviderInfo);
|
|
7955
9264
|
this.indexCompatibility = { compatible: true };
|
|
7956
9265
|
this.logger.recordIndexingEnd();
|
|
@@ -8079,27 +9388,30 @@ var Indexer = class {
|
|
|
8079
9388
|
const keywordResults = await this.keywordSearch(query, maxResults * 4);
|
|
8080
9389
|
const keywordMs = import_perf_hooks.performance.now() - keywordStartTime;
|
|
8081
9390
|
let branchChunkIds = null;
|
|
8082
|
-
if (filterByBranch && this.currentBranch !== "default") {
|
|
8083
|
-
branchChunkIds = new Set(
|
|
9391
|
+
if (filterByBranch && (this.config.scope === "global" || this.currentBranch !== "default")) {
|
|
9392
|
+
branchChunkIds = new Set(
|
|
9393
|
+
this.getBranchCatalogKeys().flatMap((branchKey) => database.getBranchChunkIds(branchKey))
|
|
9394
|
+
);
|
|
8084
9395
|
}
|
|
8085
9396
|
const prefilterStartTime = import_perf_hooks.performance.now();
|
|
8086
|
-
const shouldPrefilterByBranch = branchChunkIds !== null && branchChunkIds.size > 0;
|
|
9397
|
+
const shouldPrefilterByBranch = branchChunkIds !== null && (this.config.scope === "global" || branchChunkIds.size > 0);
|
|
9398
|
+
const allowBranchPrefilterFallback = this.config.scope !== "global";
|
|
8087
9399
|
const prefilteredSemantic = shouldPrefilterByBranch && branchChunkIds ? semanticResults.filter((r) => branchChunkIds.has(r.id)) : semanticResults;
|
|
8088
9400
|
const prefilteredKeyword = shouldPrefilterByBranch && branchChunkIds ? keywordResults.filter((r) => branchChunkIds.has(r.id)) : keywordResults;
|
|
8089
|
-
const semanticCandidates = shouldPrefilterByBranch && semanticResults.length > 0 && prefilteredSemantic.length === 0 ? semanticResults : prefilteredSemantic;
|
|
8090
|
-
const keywordCandidates = shouldPrefilterByBranch && keywordResults.length > 0 && prefilteredKeyword.length === 0 ? keywordResults : prefilteredKeyword;
|
|
9401
|
+
const semanticCandidates = allowBranchPrefilterFallback && shouldPrefilterByBranch && semanticResults.length > 0 && prefilteredSemantic.length === 0 ? semanticResults : prefilteredSemantic;
|
|
9402
|
+
const keywordCandidates = allowBranchPrefilterFallback && shouldPrefilterByBranch && keywordResults.length > 0 && prefilteredKeyword.length === 0 ? keywordResults : prefilteredKeyword;
|
|
8091
9403
|
const prefilterMs = import_perf_hooks.performance.now() - prefilterStartTime;
|
|
8092
|
-
if (branchChunkIds && branchChunkIds.size === 0) {
|
|
9404
|
+
if (this.config.scope !== "global" && branchChunkIds && branchChunkIds.size === 0) {
|
|
8093
9405
|
this.logger.search("warn", "Branch prefilter skipped because branch catalog is empty", {
|
|
8094
9406
|
branch: this.currentBranch
|
|
8095
9407
|
});
|
|
8096
9408
|
}
|
|
8097
|
-
if (shouldPrefilterByBranch && semanticResults.length > 0 && prefilteredSemantic.length === 0) {
|
|
9409
|
+
if (allowBranchPrefilterFallback && shouldPrefilterByBranch && semanticResults.length > 0 && prefilteredSemantic.length === 0) {
|
|
8098
9410
|
this.logger.search("warn", "Branch prefilter produced no semantic overlap, using unfiltered semantic candidates", {
|
|
8099
9411
|
branch: this.currentBranch
|
|
8100
9412
|
});
|
|
8101
9413
|
}
|
|
8102
|
-
if (shouldPrefilterByBranch && keywordResults.length > 0 && prefilteredKeyword.length === 0) {
|
|
9414
|
+
if (allowBranchPrefilterFallback && shouldPrefilterByBranch && keywordResults.length > 0 && prefilteredKeyword.length === 0) {
|
|
8103
9415
|
this.logger.search("warn", "Branch prefilter produced no keyword overlap, using unfiltered keyword candidates", {
|
|
8104
9416
|
branch: this.currentBranch
|
|
8105
9417
|
});
|
|
@@ -8152,26 +9464,13 @@ var Indexer = class {
|
|
|
8152
9464
|
const primaryLane = mergeTieredResults(prePrimaryLane, symbolLane, maxResults * 4);
|
|
8153
9465
|
const tiered = mergeTieredResults(primaryLane, rescued, maxResults * 4);
|
|
8154
9466
|
const hasCodeHints = extractCodeTermHints(query).length > 0 || identifierHints.length > 0;
|
|
8155
|
-
const baseFiltered = tiered.filter((r) =>
|
|
8156
|
-
if (r.score < this.config.search.minScore) return false;
|
|
8157
|
-
if (options?.fileType) {
|
|
8158
|
-
const ext = r.metadata.filePath.split(".").pop()?.toLowerCase();
|
|
8159
|
-
if (ext !== options.fileType.toLowerCase().replace(/^\./, "")) return false;
|
|
8160
|
-
}
|
|
8161
|
-
if (options?.directory) {
|
|
8162
|
-
const normalizedDir = options.directory.replace(/^\/|\/$/g, "");
|
|
8163
|
-
if (!r.metadata.filePath.includes(`/${normalizedDir}/`) && !r.metadata.filePath.includes(`${normalizedDir}/`)) return false;
|
|
8164
|
-
}
|
|
8165
|
-
if (options?.chunkType) {
|
|
8166
|
-
if (r.metadata.chunkType !== options.chunkType) return false;
|
|
8167
|
-
}
|
|
8168
|
-
return true;
|
|
8169
|
-
});
|
|
9467
|
+
const baseFiltered = tiered.filter((r) => matchesSearchFilters(r, options, this.config.search.minScore));
|
|
8170
9468
|
const implementationOnly = baseFiltered.filter(
|
|
8171
9469
|
(r) => isLikelyImplementationPath(r.metadata.filePath) && isImplementationChunkType(r.metadata.chunkType)
|
|
8172
9470
|
);
|
|
8173
9471
|
const filtered = (sourceIntent && hasCodeHints && implementationOnly.length > 0 ? implementationOnly : baseFiltered).slice(0, maxResults);
|
|
8174
|
-
const
|
|
9472
|
+
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) : [];
|
|
9473
|
+
const finalResults = filtered.length > 0 ? filtered : identifierFallback;
|
|
8175
9474
|
const totalSearchMs = import_perf_hooks.performance.now() - searchStartTime;
|
|
8176
9475
|
this.logger.recordSearch(totalSearchMs, {
|
|
8177
9476
|
embeddingMs,
|
|
@@ -8197,7 +9496,7 @@ var Indexer = class {
|
|
|
8197
9496
|
let contextEndLine = r.metadata.endLine;
|
|
8198
9497
|
if (!metadataOnly && this.config.search.includeContext) {
|
|
8199
9498
|
try {
|
|
8200
|
-
const fileContent = await
|
|
9499
|
+
const fileContent = await import_fs6.promises.readFile(
|
|
8201
9500
|
r.metadata.filePath,
|
|
8202
9501
|
"utf-8"
|
|
8203
9502
|
);
|
|
@@ -8241,7 +9540,8 @@ var Indexer = class {
|
|
|
8241
9540
|
return results.slice(0, limit);
|
|
8242
9541
|
}
|
|
8243
9542
|
async getStatus() {
|
|
8244
|
-
const { store, configuredProviderInfo } = await this.ensureInitialized();
|
|
9543
|
+
const { store, configuredProviderInfo, database } = await this.ensureInitialized();
|
|
9544
|
+
const failedBatchesCount = this.getFailedBatchesCount();
|
|
8245
9545
|
return {
|
|
8246
9546
|
indexed: store.count() > 0,
|
|
8247
9547
|
vectorCount: store.count(),
|
|
@@ -8250,23 +9550,86 @@ var Indexer = class {
|
|
|
8250
9550
|
indexPath: this.indexPath,
|
|
8251
9551
|
currentBranch: this.currentBranch,
|
|
8252
9552
|
baseBranch: this.baseBranch,
|
|
8253
|
-
compatibility: this.indexCompatibility
|
|
9553
|
+
compatibility: this.indexCompatibility,
|
|
9554
|
+
failedBatchesCount,
|
|
9555
|
+
failedBatchesPath: failedBatchesCount > 0 ? this.failedBatchesPath : void 0,
|
|
9556
|
+
warning: database.getMetadata(STARTUP_WARNING_METADATA_KEY) ?? void 0
|
|
8254
9557
|
};
|
|
8255
9558
|
}
|
|
8256
9559
|
async clearIndex() {
|
|
8257
9560
|
const { store, invertedIndex, database } = await this.ensureInitialized();
|
|
9561
|
+
if (this.config.scope === "global") {
|
|
9562
|
+
store.load();
|
|
9563
|
+
invertedIndex.load();
|
|
9564
|
+
this.loadFileHashCache();
|
|
9565
|
+
const roots = this.getScopedRoots();
|
|
9566
|
+
const compatibility = this.checkCompatibility();
|
|
9567
|
+
const allMetadata = store.getAllMetadata();
|
|
9568
|
+
const hasForeignData = allMetadata.some(({ metadata }) => !this.isFileInCurrentScope(metadata.filePath, roots)) || this.hasForeignScopedBranchData() || this.hasForeignScopedFileHashData(roots) || this.hasForeignScopedFailedBatches(roots);
|
|
9569
|
+
if (!compatibility.compatible && hasForeignData) {
|
|
9570
|
+
if (compatibility.code === "EMBEDDING_STRATEGY_MISMATCH" /* EMBEDDING_STRATEGY_MISMATCH */) {
|
|
9571
|
+
this.clearSharedIndexProjectData(store, invertedIndex, database, roots);
|
|
9572
|
+
this.clearScopedFileHashCache(roots);
|
|
9573
|
+
this.clearScopedFailedBatches(roots);
|
|
9574
|
+
database.setMetadata(this.getProjectForceReembedMetadataKey(), "true");
|
|
9575
|
+
database.deleteMetadata(this.getProjectEmbeddingStrategyMetadataKey());
|
|
9576
|
+
this.indexCompatibility = { compatible: true };
|
|
9577
|
+
return;
|
|
9578
|
+
}
|
|
9579
|
+
throw new Error(
|
|
9580
|
+
`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.`
|
|
9581
|
+
);
|
|
9582
|
+
}
|
|
9583
|
+
if (!hasForeignData) {
|
|
9584
|
+
store.clear();
|
|
9585
|
+
store.save();
|
|
9586
|
+
invertedIndex.clear();
|
|
9587
|
+
invertedIndex.save();
|
|
9588
|
+
this.fileHashCache.clear();
|
|
9589
|
+
this.saveFileHashCache();
|
|
9590
|
+
database.clearAllIndexedData();
|
|
9591
|
+
this.saveFailedBatches([]);
|
|
9592
|
+
database.deleteMetadata("index.version");
|
|
9593
|
+
database.deleteMetadata("index.embeddingProvider");
|
|
9594
|
+
database.deleteMetadata("index.embeddingModel");
|
|
9595
|
+
database.deleteMetadata("index.embeddingDimensions");
|
|
9596
|
+
database.deleteMetadata("index.embeddingStrategyVersion");
|
|
9597
|
+
database.deleteMetadata(this.getProjectEmbeddingStrategyMetadataKey());
|
|
9598
|
+
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
9599
|
+
database.deleteMetadata(this.getLegacyMigrationMetadataKey());
|
|
9600
|
+
database.deleteMetadata("index.createdAt");
|
|
9601
|
+
database.deleteMetadata("index.updatedAt");
|
|
9602
|
+
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
9603
|
+
return;
|
|
9604
|
+
}
|
|
9605
|
+
this.clearSharedIndexProjectData(store, invertedIndex, database, roots);
|
|
9606
|
+
this.clearScopedFileHashCache(roots);
|
|
9607
|
+
this.clearScopedFailedBatches(roots);
|
|
9608
|
+
this.indexCompatibility = compatibility;
|
|
9609
|
+
return;
|
|
9610
|
+
}
|
|
9611
|
+
const localProjectIndexPath = path8.join(this.projectRoot, ".opencode", "index");
|
|
9612
|
+
if (path8.resolve(this.indexPath) !== path8.resolve(localProjectIndexPath)) {
|
|
9613
|
+
throw new Error(
|
|
9614
|
+
"Project-scoped force rebuild is unsafe while using an inherited worktree index. Create a local project config boundary before clearing the index."
|
|
9615
|
+
);
|
|
9616
|
+
}
|
|
8258
9617
|
store.clear();
|
|
8259
9618
|
store.save();
|
|
8260
9619
|
invertedIndex.clear();
|
|
8261
9620
|
invertedIndex.save();
|
|
8262
9621
|
this.fileHashCache.clear();
|
|
8263
9622
|
this.saveFileHashCache();
|
|
8264
|
-
database.
|
|
8265
|
-
|
|
9623
|
+
database.clearAllIndexedData();
|
|
9624
|
+
this.saveFailedBatches([]);
|
|
8266
9625
|
database.deleteMetadata("index.version");
|
|
8267
9626
|
database.deleteMetadata("index.embeddingProvider");
|
|
8268
9627
|
database.deleteMetadata("index.embeddingModel");
|
|
8269
9628
|
database.deleteMetadata("index.embeddingDimensions");
|
|
9629
|
+
database.deleteMetadata("index.embeddingStrategyVersion");
|
|
9630
|
+
database.deleteMetadata(this.getProjectEmbeddingStrategyMetadataKey());
|
|
9631
|
+
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
9632
|
+
database.deleteMetadata(this.getLegacyMigrationMetadataKey());
|
|
8270
9633
|
database.deleteMetadata("index.createdAt");
|
|
8271
9634
|
database.deleteMetadata("index.updatedAt");
|
|
8272
9635
|
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
@@ -8282,28 +9645,61 @@ var Indexer = class {
|
|
|
8282
9645
|
filePathsToChunkKeys.set(metadata.filePath, existing);
|
|
8283
9646
|
}
|
|
8284
9647
|
const removedFilePaths = [];
|
|
8285
|
-
|
|
9648
|
+
const removedChunkKeys = [];
|
|
9649
|
+
const chunkKeysByRemovedFile = /* @__PURE__ */ new Map();
|
|
8286
9650
|
for (const [filePath, chunkKeys] of filePathsToChunkKeys) {
|
|
8287
|
-
if (!(0,
|
|
9651
|
+
if (!(0, import_fs6.existsSync)(filePath)) {
|
|
9652
|
+
chunkKeysByRemovedFile.set(filePath, chunkKeys);
|
|
8288
9653
|
for (const key of chunkKeys) {
|
|
8289
|
-
|
|
8290
|
-
invertedIndex.removeChunk(key);
|
|
8291
|
-
removedCount++;
|
|
9654
|
+
removedChunkKeys.push(key);
|
|
8292
9655
|
}
|
|
8293
|
-
database.deleteChunksByFile(filePath);
|
|
8294
|
-
database.deleteCallEdgesByFile(filePath);
|
|
8295
|
-
database.deleteSymbolsByFile(filePath);
|
|
8296
9656
|
removedFilePaths.push(filePath);
|
|
8297
9657
|
}
|
|
8298
9658
|
}
|
|
9659
|
+
if (removedChunkKeys.length > 0) {
|
|
9660
|
+
this.rebuildVectorStoreExcludingChunkIds(store, database, removedChunkKeys);
|
|
9661
|
+
for (const key of removedChunkKeys) {
|
|
9662
|
+
invertedIndex.removeChunk(key);
|
|
9663
|
+
}
|
|
9664
|
+
}
|
|
9665
|
+
for (const filePath of removedFilePaths) {
|
|
9666
|
+
const fileChunkKeys = chunkKeysByRemovedFile.get(filePath) ?? [];
|
|
9667
|
+
if (fileChunkKeys.length > 0) {
|
|
9668
|
+
database.deleteChunksByIds(fileChunkKeys);
|
|
9669
|
+
}
|
|
9670
|
+
database.deleteCallEdgesByFile(filePath);
|
|
9671
|
+
database.deleteSymbolsByFile(filePath);
|
|
9672
|
+
}
|
|
9673
|
+
const removedCount = removedChunkKeys.length;
|
|
8299
9674
|
if (removedCount > 0) {
|
|
8300
9675
|
store.save();
|
|
8301
9676
|
invertedIndex.save();
|
|
8302
9677
|
}
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
9678
|
+
let gcOrphanEmbeddings;
|
|
9679
|
+
let gcOrphanChunks;
|
|
9680
|
+
let gcOrphanSymbols;
|
|
9681
|
+
let gcOrphanCallEdges;
|
|
9682
|
+
try {
|
|
9683
|
+
gcOrphanEmbeddings = database.gcOrphanEmbeddings();
|
|
9684
|
+
gcOrphanChunks = database.gcOrphanChunks();
|
|
9685
|
+
gcOrphanSymbols = database.gcOrphanSymbols();
|
|
9686
|
+
gcOrphanCallEdges = database.gcOrphanCallEdges();
|
|
9687
|
+
} catch (error) {
|
|
9688
|
+
if (!await this.tryResetCorruptedIndex("running index health check", error)) {
|
|
9689
|
+
throw error;
|
|
9690
|
+
}
|
|
9691
|
+
await this.ensureInitialized();
|
|
9692
|
+
return {
|
|
9693
|
+
removed: 0,
|
|
9694
|
+
filePaths: [],
|
|
9695
|
+
gcOrphanEmbeddings: 0,
|
|
9696
|
+
gcOrphanChunks: 0,
|
|
9697
|
+
gcOrphanSymbols: 0,
|
|
9698
|
+
gcOrphanCallEdges: 0,
|
|
9699
|
+
resetCorruptedIndex: true,
|
|
9700
|
+
warning: this.getCorruptedIndexWarning(path8.join(this.indexPath, "codebase.db"))
|
|
9701
|
+
};
|
|
9702
|
+
}
|
|
8307
9703
|
this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
|
|
8308
9704
|
this.logger.gc("info", "Health check complete", {
|
|
8309
9705
|
removedStale: removedCount,
|
|
@@ -8314,8 +9710,12 @@ var Indexer = class {
|
|
|
8314
9710
|
return { removed: removedCount, filePaths: removedFilePaths, gcOrphanEmbeddings, gcOrphanChunks, gcOrphanSymbols, gcOrphanCallEdges };
|
|
8315
9711
|
}
|
|
8316
9712
|
async retryFailedBatches() {
|
|
8317
|
-
const { store, provider, invertedIndex } = await this.ensureInitialized();
|
|
8318
|
-
const
|
|
9713
|
+
const { store, provider, invertedIndex, database, configuredProviderInfo } = await this.ensureInitialized();
|
|
9714
|
+
const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
|
|
9715
|
+
const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
|
|
9716
|
+
const roots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
9717
|
+
const { scoped: scopedFailedBatches, retained: retainedFailedBatches } = roots ? this.partitionFailedBatches(roots, maxChunkTokens) : { scoped: this.loadFailedBatches(maxChunkTokens), retained: [] };
|
|
9718
|
+
const failedBatches = scopedFailedBatches;
|
|
8319
9719
|
if (failedBatches.length === 0) {
|
|
8320
9720
|
return { succeeded: 0, failed: 0, remaining: 0 };
|
|
8321
9721
|
}
|
|
@@ -8323,49 +9723,170 @@ var Indexer = class {
|
|
|
8323
9723
|
let failed = 0;
|
|
8324
9724
|
const stillFailing = [];
|
|
8325
9725
|
for (const batch of failedBatches) {
|
|
9726
|
+
const batchChunksById = new Map(batch.chunks.map((chunk) => [chunk.id, chunk]));
|
|
9727
|
+
const embeddingPartsByChunk = /* @__PURE__ */ new Map();
|
|
9728
|
+
const completedChunkIds = /* @__PURE__ */ new Set();
|
|
9729
|
+
const failedChunkIds = /* @__PURE__ */ new Set();
|
|
9730
|
+
const failedChunksForBatch = /* @__PURE__ */ new Map();
|
|
9731
|
+
const pooledResults = [];
|
|
8326
9732
|
try {
|
|
8327
|
-
const
|
|
8328
|
-
|
|
8329
|
-
|
|
8330
|
-
return provider.embedBatch(texts);
|
|
8331
|
-
},
|
|
8332
|
-
{
|
|
8333
|
-
retries: this.config.indexing.retries,
|
|
8334
|
-
minTimeout: this.config.indexing.retryDelayMs
|
|
8335
|
-
}
|
|
9733
|
+
const requestBatches = createPendingEmbeddingRequestBatches(
|
|
9734
|
+
batch.chunks,
|
|
9735
|
+
getDynamicBatchOptions(configuredProviderInfo)
|
|
8336
9736
|
);
|
|
8337
|
-
const
|
|
9737
|
+
for (const requestBatch of requestBatches) {
|
|
9738
|
+
try {
|
|
9739
|
+
const result = await pRetry(
|
|
9740
|
+
async () => {
|
|
9741
|
+
const texts = requestBatch.map((request) => request.text);
|
|
9742
|
+
return provider.embedBatch(texts);
|
|
9743
|
+
},
|
|
9744
|
+
{
|
|
9745
|
+
retries: this.config.indexing.retries,
|
|
9746
|
+
minTimeout: Math.max(this.config.indexing.retryDelayMs, providerRateLimits.minRetryMs),
|
|
9747
|
+
maxTimeout: providerRateLimits.maxRetryMs,
|
|
9748
|
+
factor: 2,
|
|
9749
|
+
shouldRetry: (error) => !(error.error instanceof CustomProviderNonRetryableError)
|
|
9750
|
+
}
|
|
9751
|
+
);
|
|
9752
|
+
const touchedChunkIds = /* @__PURE__ */ new Set();
|
|
9753
|
+
requestBatch.forEach((request, idx) => {
|
|
9754
|
+
if (failedChunkIds.has(request.chunk.id) || completedChunkIds.has(request.chunk.id)) {
|
|
9755
|
+
return;
|
|
9756
|
+
}
|
|
9757
|
+
const vector = result.embeddings[idx];
|
|
9758
|
+
if (!vector) {
|
|
9759
|
+
throw new Error(`Embedding API returned too few vectors for chunk ${request.chunk.id}`);
|
|
9760
|
+
}
|
|
9761
|
+
const parts = embeddingPartsByChunk.get(request.chunk.id) ?? [];
|
|
9762
|
+
parts[request.partIndex] = {
|
|
9763
|
+
vector,
|
|
9764
|
+
tokenCount: request.tokenCount
|
|
9765
|
+
};
|
|
9766
|
+
embeddingPartsByChunk.set(request.chunk.id, parts);
|
|
9767
|
+
touchedChunkIds.add(request.chunk.id);
|
|
9768
|
+
});
|
|
9769
|
+
for (const chunkId of touchedChunkIds) {
|
|
9770
|
+
if (failedChunkIds.has(chunkId) || completedChunkIds.has(chunkId)) {
|
|
9771
|
+
continue;
|
|
9772
|
+
}
|
|
9773
|
+
const chunk = batchChunksById.get(chunkId);
|
|
9774
|
+
if (!chunk) {
|
|
9775
|
+
continue;
|
|
9776
|
+
}
|
|
9777
|
+
const parts = embeddingPartsByChunk.get(chunk.id) ?? [];
|
|
9778
|
+
if (!hasAllEmbeddingParts(parts, chunk.texts.length)) {
|
|
9779
|
+
continue;
|
|
9780
|
+
}
|
|
9781
|
+
const orderedParts = parts;
|
|
9782
|
+
pooledResults.push({
|
|
9783
|
+
chunk,
|
|
9784
|
+
vector: poolEmbeddingVectors(
|
|
9785
|
+
orderedParts.map((part) => part.vector),
|
|
9786
|
+
orderedParts.map((part) => part.tokenCount)
|
|
9787
|
+
)
|
|
9788
|
+
});
|
|
9789
|
+
}
|
|
9790
|
+
this.logger.recordEmbeddingApiCall(result.totalTokensUsed);
|
|
9791
|
+
} catch (error) {
|
|
9792
|
+
const failureMessage = String(error);
|
|
9793
|
+
const failureTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
9794
|
+
const failedChunks = getUniquePendingChunksFromRequests(requestBatch).filter((chunk) => !completedChunkIds.has(chunk.id) && !failedChunkIds.has(chunk.id));
|
|
9795
|
+
for (const chunk of failedChunks) {
|
|
9796
|
+
failedChunkIds.add(chunk.id);
|
|
9797
|
+
embeddingPartsByChunk.delete(chunk.id);
|
|
9798
|
+
failedChunksForBatch.set(chunk.id, {
|
|
9799
|
+
chunks: [chunk],
|
|
9800
|
+
attemptCount: batch.attemptCount + 1,
|
|
9801
|
+
lastAttempt: failureTimestamp,
|
|
9802
|
+
error: failureMessage
|
|
9803
|
+
});
|
|
9804
|
+
}
|
|
9805
|
+
failed += failedChunks.length;
|
|
9806
|
+
this.logger.recordEmbeddingError();
|
|
9807
|
+
}
|
|
9808
|
+
}
|
|
9809
|
+
const successfulResults = pooledResults.filter(({ chunk }) => !failedChunkIds.has(chunk.id));
|
|
9810
|
+
const items = successfulResults.map(({ chunk, vector }) => ({
|
|
8338
9811
|
id: chunk.id,
|
|
8339
|
-
vector
|
|
9812
|
+
vector,
|
|
8340
9813
|
metadata: chunk.metadata
|
|
8341
9814
|
}));
|
|
8342
|
-
|
|
8343
|
-
|
|
9815
|
+
if (items.length > 0) {
|
|
9816
|
+
store.addBatch(items);
|
|
9817
|
+
}
|
|
9818
|
+
if (successfulResults.length > 0) {
|
|
9819
|
+
try {
|
|
9820
|
+
database.upsertEmbeddingsBatch(
|
|
9821
|
+
successfulResults.map(({ chunk, vector }) => ({
|
|
9822
|
+
contentHash: chunk.contentHash,
|
|
9823
|
+
embedding: float32ArrayToBuffer(vector),
|
|
9824
|
+
chunkText: chunk.storageText,
|
|
9825
|
+
model: configuredProviderInfo.modelInfo.model
|
|
9826
|
+
}))
|
|
9827
|
+
);
|
|
9828
|
+
} catch (dbError) {
|
|
9829
|
+
this.rebuildVectorStoreExcludingChunkIds(
|
|
9830
|
+
store,
|
|
9831
|
+
database,
|
|
9832
|
+
successfulResults.map(({ chunk }) => chunk.id)
|
|
9833
|
+
);
|
|
9834
|
+
throw dbError;
|
|
9835
|
+
}
|
|
9836
|
+
}
|
|
9837
|
+
for (const { chunk } of successfulResults) {
|
|
8344
9838
|
invertedIndex.removeChunk(chunk.id);
|
|
8345
9839
|
invertedIndex.addChunk(chunk.id, chunk.content);
|
|
9840
|
+
completedChunkIds.add(chunk.id);
|
|
9841
|
+
embeddingPartsByChunk.delete(chunk.id);
|
|
8346
9842
|
}
|
|
8347
|
-
|
|
8348
|
-
|
|
8349
|
-
|
|
9843
|
+
database.addChunksToBranchBatch(
|
|
9844
|
+
this.getBranchCatalogKey(),
|
|
9845
|
+
successfulResults.map(({ chunk }) => chunk.id)
|
|
9846
|
+
);
|
|
9847
|
+
this.logger.recordChunksEmbedded(successfulResults.length);
|
|
9848
|
+
succeeded += successfulResults.length;
|
|
9849
|
+
stillFailing.push(...failedChunksForBatch.values());
|
|
8350
9850
|
} catch (error) {
|
|
8351
|
-
|
|
9851
|
+
const failureMessage = getErrorMessage(error);
|
|
9852
|
+
const failureTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
9853
|
+
const unaccountedChunks = batch.chunks.filter(
|
|
9854
|
+
(chunk) => !failedChunksForBatch.has(chunk.id) && !completedChunkIds.has(chunk.id)
|
|
9855
|
+
);
|
|
9856
|
+
for (const chunk of unaccountedChunks) {
|
|
9857
|
+
failedChunksForBatch.set(chunk.id, {
|
|
9858
|
+
chunks: [chunk],
|
|
9859
|
+
attemptCount: batch.attemptCount + 1,
|
|
9860
|
+
lastAttempt: failureTimestamp,
|
|
9861
|
+
error: failureMessage
|
|
9862
|
+
});
|
|
9863
|
+
}
|
|
9864
|
+
failed += unaccountedChunks.length;
|
|
8352
9865
|
this.logger.recordEmbeddingError();
|
|
8353
|
-
stillFailing.push(
|
|
8354
|
-
...batch,
|
|
8355
|
-
attemptCount: batch.attemptCount + 1,
|
|
8356
|
-
lastAttempt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8357
|
-
error: String(error)
|
|
8358
|
-
});
|
|
9866
|
+
stillFailing.push(...coalesceFailedBatches(Array.from(failedChunksForBatch.values())));
|
|
8359
9867
|
}
|
|
8360
9868
|
}
|
|
8361
|
-
|
|
9869
|
+
const persistedStillFailing = coalesceFailedBatches(stillFailing);
|
|
9870
|
+
if (roots) {
|
|
9871
|
+
this.saveFailedBatches([...retainedFailedBatches, ...persistedStillFailing]);
|
|
9872
|
+
} else {
|
|
9873
|
+
this.saveFailedBatches(persistedStillFailing);
|
|
9874
|
+
}
|
|
8362
9875
|
if (succeeded > 0) {
|
|
8363
9876
|
store.save();
|
|
8364
9877
|
invertedIndex.save();
|
|
8365
9878
|
}
|
|
8366
|
-
|
|
9879
|
+
if (roots && succeeded > 0 && persistedStillFailing.length === 0 && this.hasProjectForceReembedPending()) {
|
|
9880
|
+
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
9881
|
+
this.saveIndexMetadata(configuredProviderInfo);
|
|
9882
|
+
this.indexCompatibility = { compatible: true };
|
|
9883
|
+
}
|
|
9884
|
+
return { succeeded, failed, remaining: persistedStillFailing.length };
|
|
8367
9885
|
}
|
|
8368
9886
|
getFailedBatchesCount() {
|
|
9887
|
+
if (this.config.scope === "global") {
|
|
9888
|
+
return this.partitionFailedBatches(this.getScopedRoots()).scoped.length;
|
|
9889
|
+
}
|
|
8369
9890
|
return this.loadFailedBatches().length;
|
|
8370
9891
|
}
|
|
8371
9892
|
getCurrentBranch() {
|
|
@@ -8414,20 +9935,23 @@ var Indexer = class {
|
|
|
8414
9935
|
const semanticResults = store.search(embedding, limit * 2);
|
|
8415
9936
|
const vectorMs = import_perf_hooks.performance.now() - vectorStartTime;
|
|
8416
9937
|
let branchChunkIds = null;
|
|
8417
|
-
if (filterByBranch && this.currentBranch !== "default") {
|
|
8418
|
-
branchChunkIds = new Set(
|
|
9938
|
+
if (filterByBranch && (this.config.scope === "global" || this.currentBranch !== "default")) {
|
|
9939
|
+
branchChunkIds = new Set(
|
|
9940
|
+
this.getBranchCatalogKeys().flatMap((branchKey) => database.getBranchChunkIds(branchKey))
|
|
9941
|
+
);
|
|
8419
9942
|
}
|
|
8420
9943
|
const prefilterStartTime = import_perf_hooks.performance.now();
|
|
8421
|
-
const shouldPrefilterByBranch = branchChunkIds !== null && branchChunkIds.size > 0;
|
|
9944
|
+
const shouldPrefilterByBranch = branchChunkIds !== null && (this.config.scope === "global" || branchChunkIds.size > 0);
|
|
9945
|
+
const allowBranchPrefilterFallback = this.config.scope !== "global";
|
|
8422
9946
|
const prefilteredSemantic = shouldPrefilterByBranch && branchChunkIds ? semanticResults.filter((r) => branchChunkIds.has(r.id)) : semanticResults;
|
|
8423
|
-
const semanticCandidates = shouldPrefilterByBranch && semanticResults.length > 0 && prefilteredSemantic.length === 0 ? semanticResults : prefilteredSemantic;
|
|
9947
|
+
const semanticCandidates = allowBranchPrefilterFallback && shouldPrefilterByBranch && semanticResults.length > 0 && prefilteredSemantic.length === 0 ? semanticResults : prefilteredSemantic;
|
|
8424
9948
|
const prefilterMs = import_perf_hooks.performance.now() - prefilterStartTime;
|
|
8425
|
-
if (branchChunkIds && branchChunkIds.size === 0) {
|
|
9949
|
+
if (this.config.scope !== "global" && branchChunkIds && branchChunkIds.size === 0) {
|
|
8426
9950
|
this.logger.search("warn", "Branch prefilter skipped because branch catalog is empty", {
|
|
8427
9951
|
branch: this.currentBranch
|
|
8428
9952
|
});
|
|
8429
9953
|
}
|
|
8430
|
-
if (shouldPrefilterByBranch && semanticResults.length > 0 && prefilteredSemantic.length === 0) {
|
|
9954
|
+
if (allowBranchPrefilterFallback && shouldPrefilterByBranch && semanticResults.length > 0 && prefilteredSemantic.length === 0) {
|
|
8431
9955
|
this.logger.search("warn", "Branch prefilter produced no semantic overlap, using unfiltered semantic candidates", {
|
|
8432
9956
|
branch: this.currentBranch
|
|
8433
9957
|
});
|
|
@@ -8476,7 +10000,7 @@ var Indexer = class {
|
|
|
8476
10000
|
let content = "";
|
|
8477
10001
|
if (this.config.search.includeContext) {
|
|
8478
10002
|
try {
|
|
8479
|
-
const fileContent = await
|
|
10003
|
+
const fileContent = await import_fs6.promises.readFile(
|
|
8480
10004
|
r.metadata.filePath,
|
|
8481
10005
|
"utf-8"
|
|
8482
10006
|
);
|
|
@@ -8500,11 +10024,39 @@ var Indexer = class {
|
|
|
8500
10024
|
}
|
|
8501
10025
|
async getCallers(targetName) {
|
|
8502
10026
|
const { database } = await this.ensureInitialized();
|
|
8503
|
-
|
|
10027
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10028
|
+
const results = [];
|
|
10029
|
+
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
10030
|
+
for (const edge of database.getCallersWithContext(targetName, branchKey)) {
|
|
10031
|
+
if (!seen.has(edge.id)) {
|
|
10032
|
+
seen.add(edge.id);
|
|
10033
|
+
results.push(edge);
|
|
10034
|
+
}
|
|
10035
|
+
}
|
|
10036
|
+
}
|
|
10037
|
+
return results;
|
|
8504
10038
|
}
|
|
8505
10039
|
async getCallees(symbolId) {
|
|
8506
10040
|
const { database } = await this.ensureInitialized();
|
|
8507
|
-
|
|
10041
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10042
|
+
const results = [];
|
|
10043
|
+
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
10044
|
+
for (const edge of database.getCallees(symbolId, branchKey)) {
|
|
10045
|
+
if (!seen.has(edge.id)) {
|
|
10046
|
+
seen.add(edge.id);
|
|
10047
|
+
results.push(edge);
|
|
10048
|
+
}
|
|
10049
|
+
}
|
|
10050
|
+
}
|
|
10051
|
+
return results;
|
|
10052
|
+
}
|
|
10053
|
+
async close() {
|
|
10054
|
+
await this.database?.close();
|
|
10055
|
+
this.database = null;
|
|
10056
|
+
this.store = null;
|
|
10057
|
+
this.invertedIndex = null;
|
|
10058
|
+
this.provider = null;
|
|
10059
|
+
this.reranker = null;
|
|
8508
10060
|
}
|
|
8509
10061
|
};
|
|
8510
10062
|
|
|
@@ -8517,7 +10069,17 @@ function truncateContent(content) {
|
|
|
8517
10069
|
// ... (${lines.length - MAX_CONTENT_LINES} more lines)`;
|
|
8518
10070
|
}
|
|
8519
10071
|
function formatIndexStats(stats, verbose = false) {
|
|
10072
|
+
if (stats.resetCorruptedIndex) {
|
|
10073
|
+
return stats.warning ?? "Detected a corrupted local index and reset it during indexing. Run index_codebase again to rebuild search data.";
|
|
10074
|
+
}
|
|
8520
10075
|
const lines = [];
|
|
10076
|
+
if (stats.failedChunks > 0) {
|
|
10077
|
+
lines.push(`INDEXING WARNING: ${stats.failedChunks} chunks failed to embed.`);
|
|
10078
|
+
if (stats.failedBatchesPath) {
|
|
10079
|
+
lines.push(`Inspect failed batches at: ${stats.failedBatchesPath}`);
|
|
10080
|
+
}
|
|
10081
|
+
lines.push("");
|
|
10082
|
+
}
|
|
8521
10083
|
if (stats.indexedChunks === 0 && stats.removedChunks === 0) {
|
|
8522
10084
|
lines.push(`${stats.totalFiles} files processed, ${stats.existingChunks} code chunks already up to date.`);
|
|
8523
10085
|
} else if (stats.indexedChunks === 0) {
|
|
@@ -8562,6 +10124,19 @@ function formatIndexStats(stats, verbose = false) {
|
|
|
8562
10124
|
}
|
|
8563
10125
|
function formatStatus(status) {
|
|
8564
10126
|
if (!status.indexed) {
|
|
10127
|
+
if (status.warning) {
|
|
10128
|
+
return status.warning;
|
|
10129
|
+
}
|
|
10130
|
+
if (status.failedBatchesCount > 0) {
|
|
10131
|
+
const lines2 = [
|
|
10132
|
+
"Codebase is not indexed. The last indexing run left failed embedding batches.",
|
|
10133
|
+
"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."
|
|
10134
|
+
];
|
|
10135
|
+
if (status.failedBatchesPath) {
|
|
10136
|
+
lines2.push(`Failed batches: ${status.failedBatchesPath}`);
|
|
10137
|
+
}
|
|
10138
|
+
return lines2.join("\n");
|
|
10139
|
+
}
|
|
8565
10140
|
return "Codebase is not indexed. Run index_codebase to create an index.";
|
|
8566
10141
|
}
|
|
8567
10142
|
const lines = [
|
|
@@ -8574,6 +10149,13 @@ function formatStatus(status) {
|
|
|
8574
10149
|
lines.push(`Current branch: ${status.currentBranch}`);
|
|
8575
10150
|
lines.push(`Base branch: ${status.baseBranch}`);
|
|
8576
10151
|
}
|
|
10152
|
+
if (status.failedBatchesCount > 0) {
|
|
10153
|
+
lines.push("");
|
|
10154
|
+
lines.push(`INDEXING WARNING: ${status.failedBatchesCount} failed embedding batch${status.failedBatchesCount === 1 ? " remains" : "es remain"}.`);
|
|
10155
|
+
if (status.failedBatchesPath) {
|
|
10156
|
+
lines.push(`Failed batches: ${status.failedBatchesPath}`);
|
|
10157
|
+
}
|
|
10158
|
+
}
|
|
8577
10159
|
if (status.compatibility && !status.compatibility.compatible) {
|
|
8578
10160
|
lines.push("");
|
|
8579
10161
|
lines.push(`COMPATIBILITY WARNING: ${status.compatibility.reason}`);
|
|
@@ -8631,6 +10213,9 @@ function formatCodebasePeek(results) {
|
|
|
8631
10213
|
return formatted.join("\n");
|
|
8632
10214
|
}
|
|
8633
10215
|
function formatHealthCheck(result) {
|
|
10216
|
+
if (result.resetCorruptedIndex) {
|
|
10217
|
+
return result.warning ?? "Detected a corrupted local index and reset it. Run index_codebase to rebuild search data.";
|
|
10218
|
+
}
|
|
8634
10219
|
if (result.removed === 0 && result.gcOrphanEmbeddings === 0 && result.gcOrphanChunks === 0 && result.gcOrphanSymbols === 0 && result.gcOrphanCallEdges === 0) {
|
|
8635
10220
|
return "Index is healthy. No stale entries found.";
|
|
8636
10221
|
}
|
|
@@ -8690,8 +10275,8 @@ ${truncateContent(r.content)}
|
|
|
8690
10275
|
}
|
|
8691
10276
|
|
|
8692
10277
|
// src/tools/index.ts
|
|
8693
|
-
var
|
|
8694
|
-
var
|
|
10278
|
+
var import_fs7 = require("fs");
|
|
10279
|
+
var path9 = __toESM(require("path"), 1);
|
|
8695
10280
|
var z = import_plugin.tool.schema;
|
|
8696
10281
|
var sharedIndexer = null;
|
|
8697
10282
|
var sharedProjectRoot = "";
|
|
@@ -8706,7 +10291,20 @@ function refreshIndexerFromConfig() {
|
|
|
8706
10291
|
if (!sharedProjectRoot) {
|
|
8707
10292
|
throw new Error("Codebase index tools not initialized. Plugin may not be loaded correctly.");
|
|
8708
10293
|
}
|
|
8709
|
-
sharedIndexer = new Indexer(sharedProjectRoot, parseConfig(
|
|
10294
|
+
sharedIndexer = new Indexer(sharedProjectRoot, parseConfig(loadRuntimeConfig()));
|
|
10295
|
+
}
|
|
10296
|
+
function shouldForceLocalizeProjectIndex() {
|
|
10297
|
+
const currentConfig = parseConfig(loadRuntimeConfig());
|
|
10298
|
+
if (currentConfig.scope !== "project") {
|
|
10299
|
+
return false;
|
|
10300
|
+
}
|
|
10301
|
+
const localIndexPath = path9.join(sharedProjectRoot, ".opencode", "index");
|
|
10302
|
+
const mainRepoRoot = resolveWorktreeMainRepoRoot(sharedProjectRoot);
|
|
10303
|
+
if (!mainRepoRoot) {
|
|
10304
|
+
return false;
|
|
10305
|
+
}
|
|
10306
|
+
const inheritedIndexPath = path9.join(mainRepoRoot, ".opencode", "index");
|
|
10307
|
+
return !(0, import_fs7.existsSync)(localIndexPath) && (0, import_fs7.existsSync)(inheritedIndexPath);
|
|
8710
10308
|
}
|
|
8711
10309
|
function getIndexer() {
|
|
8712
10310
|
if (!sharedIndexer) {
|
|
@@ -8715,9 +10313,41 @@ function getIndexer() {
|
|
|
8715
10313
|
return sharedIndexer;
|
|
8716
10314
|
}
|
|
8717
10315
|
function getConfigPath() {
|
|
8718
|
-
return
|
|
10316
|
+
return resolveWritableProjectConfigPath(sharedProjectRoot);
|
|
8719
10317
|
}
|
|
8720
|
-
function
|
|
10318
|
+
function normalizeConfigPathValue(value, baseDir) {
|
|
10319
|
+
const trimmed = value.trim();
|
|
10320
|
+
if (!trimmed) {
|
|
10321
|
+
return trimmed;
|
|
10322
|
+
}
|
|
10323
|
+
const absolutePath = path9.isAbsolute(trimmed) ? trimmed : path9.resolve(baseDir, trimmed);
|
|
10324
|
+
return path9.normalize(absolutePath);
|
|
10325
|
+
}
|
|
10326
|
+
function serializeConfigPathValue(value, baseDir) {
|
|
10327
|
+
const trimmed = value.trim();
|
|
10328
|
+
if (!trimmed) {
|
|
10329
|
+
return trimmed;
|
|
10330
|
+
}
|
|
10331
|
+
const normalizeRelativePath = (candidate) => candidate.replace(/\\/g, "/");
|
|
10332
|
+
if (!path9.isAbsolute(trimmed)) {
|
|
10333
|
+
return normalizeRelativePath(path9.normalize(trimmed));
|
|
10334
|
+
}
|
|
10335
|
+
const relativePath = path9.relative(baseDir, trimmed);
|
|
10336
|
+
if (!relativePath || !relativePath.startsWith("..") && !path9.isAbsolute(relativePath)) {
|
|
10337
|
+
return normalizeRelativePath(path9.normalize(relativePath || "."));
|
|
10338
|
+
}
|
|
10339
|
+
return path9.normalize(trimmed);
|
|
10340
|
+
}
|
|
10341
|
+
function normalizeKnowledgeBasePaths(config) {
|
|
10342
|
+
const normalized = { ...config };
|
|
10343
|
+
if (Array.isArray(normalized.knowledgeBases)) {
|
|
10344
|
+
normalized.knowledgeBases = normalized.knowledgeBases.map((kb) => {
|
|
10345
|
+
return normalizeConfigPathValue(kb, sharedProjectRoot);
|
|
10346
|
+
});
|
|
10347
|
+
}
|
|
10348
|
+
return normalized;
|
|
10349
|
+
}
|
|
10350
|
+
function loadRuntimeConfig() {
|
|
8721
10351
|
const rawConfig = loadMergedConfig(sharedProjectRoot);
|
|
8722
10352
|
const config = {};
|
|
8723
10353
|
if (rawConfig && typeof rawConfig === "object") {
|
|
@@ -8725,27 +10355,32 @@ function loadConfig() {
|
|
|
8725
10355
|
config[key] = rawConfig[key];
|
|
8726
10356
|
}
|
|
8727
10357
|
}
|
|
8728
|
-
|
|
8729
|
-
|
|
8730
|
-
|
|
8731
|
-
|
|
8732
|
-
|
|
8733
|
-
|
|
8734
|
-
|
|
8735
|
-
|
|
8736
|
-
|
|
8737
|
-
return path8.normalize(resolved);
|
|
8738
|
-
});
|
|
10358
|
+
return normalizeKnowledgeBasePaths(config);
|
|
10359
|
+
}
|
|
10360
|
+
function loadEditableConfig() {
|
|
10361
|
+
const rawConfig = loadProjectConfigLayer(sharedProjectRoot);
|
|
10362
|
+
const config = {};
|
|
10363
|
+
if (rawConfig && typeof rawConfig === "object") {
|
|
10364
|
+
for (const key of Object.keys(rawConfig)) {
|
|
10365
|
+
config[key] = rawConfig[key];
|
|
10366
|
+
}
|
|
8739
10367
|
}
|
|
8740
|
-
return config;
|
|
10368
|
+
return normalizeKnowledgeBasePaths(config);
|
|
8741
10369
|
}
|
|
8742
10370
|
function saveConfig(config) {
|
|
8743
|
-
const configDir = path8.join(sharedProjectRoot, ".opencode");
|
|
8744
|
-
if (!(0, import_fs6.existsSync)(configDir)) {
|
|
8745
|
-
(0, import_fs6.mkdirSync)(configDir, { recursive: true });
|
|
8746
|
-
}
|
|
8747
10371
|
const configPath = getConfigPath();
|
|
8748
|
-
|
|
10372
|
+
const configDir = path9.dirname(configPath);
|
|
10373
|
+
const configBaseDir = path9.dirname(configDir);
|
|
10374
|
+
if (!(0, import_fs7.existsSync)(configDir)) {
|
|
10375
|
+
(0, import_fs7.mkdirSync)(configDir, { recursive: true });
|
|
10376
|
+
}
|
|
10377
|
+
const serializableConfig = { ...config };
|
|
10378
|
+
if (Array.isArray(serializableConfig.knowledgeBases)) {
|
|
10379
|
+
serializableConfig.knowledgeBases = serializableConfig.knowledgeBases.map(
|
|
10380
|
+
(kb) => serializeConfigPathValue(kb, configBaseDir)
|
|
10381
|
+
);
|
|
10382
|
+
}
|
|
10383
|
+
(0, import_fs7.writeFileSync)(configPath, JSON.stringify(serializableConfig, null, 2) + "\n", "utf-8");
|
|
8749
10384
|
}
|
|
8750
10385
|
var codebase_peek = (0, import_plugin.tool)({
|
|
8751
10386
|
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.",
|
|
@@ -8775,12 +10410,17 @@ var index_codebase = (0, import_plugin.tool)({
|
|
|
8775
10410
|
verbose: z.boolean().optional().default(false).describe("Show detailed info about skipped files and parsing failures")
|
|
8776
10411
|
},
|
|
8777
10412
|
async execute(args, context) {
|
|
8778
|
-
|
|
10413
|
+
let indexer = getIndexer();
|
|
8779
10414
|
if (args.estimateOnly) {
|
|
8780
10415
|
const estimate = await indexer.estimateCost();
|
|
8781
10416
|
return formatCostEstimate(estimate);
|
|
8782
10417
|
}
|
|
8783
10418
|
if (args.force) {
|
|
10419
|
+
if (shouldForceLocalizeProjectIndex()) {
|
|
10420
|
+
materializeLocalProjectConfig(sharedProjectRoot, loadProjectConfigLayer(sharedProjectRoot));
|
|
10421
|
+
refreshIndexerFromConfig();
|
|
10422
|
+
indexer = getIndexer();
|
|
10423
|
+
}
|
|
8784
10424
|
await indexer.clearIndex();
|
|
8785
10425
|
}
|
|
8786
10426
|
const stats = await indexer.index((progress) => {
|
|
@@ -8961,23 +10601,23 @@ var add_knowledge_base = (0, import_plugin.tool)({
|
|
|
8961
10601
|
},
|
|
8962
10602
|
async execute(args) {
|
|
8963
10603
|
const inputPath = args.path.trim();
|
|
8964
|
-
const resolvedPath =
|
|
8965
|
-
if (!(0,
|
|
10604
|
+
const resolvedPath = path9.isAbsolute(inputPath) ? inputPath : path9.resolve(sharedProjectRoot, inputPath);
|
|
10605
|
+
if (!(0, import_fs7.existsSync)(resolvedPath)) {
|
|
8966
10606
|
return `Error: Directory does not exist: ${resolvedPath}`;
|
|
8967
10607
|
}
|
|
8968
10608
|
try {
|
|
8969
|
-
const stat4 = (0,
|
|
10609
|
+
const stat4 = (0, import_fs7.statSync)(resolvedPath);
|
|
8970
10610
|
if (!stat4.isDirectory()) {
|
|
8971
10611
|
return `Error: Path is not a directory: ${resolvedPath}`;
|
|
8972
10612
|
}
|
|
8973
10613
|
} catch (error) {
|
|
8974
10614
|
return `Error: Cannot access directory: ${resolvedPath} - ${error instanceof Error ? error.message : String(error)}`;
|
|
8975
10615
|
}
|
|
8976
|
-
const config =
|
|
10616
|
+
const config = loadEditableConfig();
|
|
8977
10617
|
const knowledgeBases = Array.isArray(config.knowledgeBases) ? config.knowledgeBases : [];
|
|
8978
|
-
const normalizedPath =
|
|
10618
|
+
const normalizedPath = path9.normalize(resolvedPath);
|
|
8979
10619
|
const alreadyExists = knowledgeBases.some(
|
|
8980
|
-
(kb) =>
|
|
10620
|
+
(kb) => path9.normalize(path9.isAbsolute(kb) ? kb : path9.resolve(sharedProjectRoot, kb)) === normalizedPath
|
|
8981
10621
|
);
|
|
8982
10622
|
if (alreadyExists) {
|
|
8983
10623
|
return `Knowledge base already configured: ${resolvedPath}`;
|
|
@@ -9001,7 +10641,7 @@ var list_knowledge_bases = (0, import_plugin.tool)({
|
|
|
9001
10641
|
description: "List all configured knowledge base folders that are indexed alongside the main project.",
|
|
9002
10642
|
args: {},
|
|
9003
10643
|
async execute() {
|
|
9004
|
-
const config =
|
|
10644
|
+
const config = loadRuntimeConfig();
|
|
9005
10645
|
const knowledgeBases = Array.isArray(config.knowledgeBases) ? config.knowledgeBases : [];
|
|
9006
10646
|
if (knowledgeBases.length === 0) {
|
|
9007
10647
|
return "No knowledge bases configured. Use add_knowledge_base to add folders.";
|
|
@@ -9011,8 +10651,8 @@ var list_knowledge_bases = (0, import_plugin.tool)({
|
|
|
9011
10651
|
`;
|
|
9012
10652
|
for (let i = 0; i < knowledgeBases.length; i++) {
|
|
9013
10653
|
const kb = knowledgeBases[i];
|
|
9014
|
-
const resolvedPath =
|
|
9015
|
-
const exists = (0,
|
|
10654
|
+
const resolvedPath = path9.isAbsolute(kb) ? kb : path9.resolve(sharedProjectRoot, kb);
|
|
10655
|
+
const exists = (0, import_fs7.existsSync)(resolvedPath);
|
|
9016
10656
|
result += `[${i + 1}] ${kb}
|
|
9017
10657
|
`;
|
|
9018
10658
|
result += ` Resolved: ${resolvedPath}
|
|
@@ -9021,7 +10661,7 @@ var list_knowledge_bases = (0, import_plugin.tool)({
|
|
|
9021
10661
|
`;
|
|
9022
10662
|
if (exists) {
|
|
9023
10663
|
try {
|
|
9024
|
-
const stat4 = (0,
|
|
10664
|
+
const stat4 = (0, import_fs7.statSync)(resolvedPath);
|
|
9025
10665
|
result += ` Type: ${stat4.isDirectory() ? "Directory" : "File"}
|
|
9026
10666
|
`;
|
|
9027
10667
|
} catch {
|
|
@@ -9040,14 +10680,14 @@ var remove_knowledge_base = (0, import_plugin.tool)({
|
|
|
9040
10680
|
},
|
|
9041
10681
|
async execute(args) {
|
|
9042
10682
|
const inputPath = args.path.trim();
|
|
9043
|
-
const config =
|
|
10683
|
+
const config = loadEditableConfig();
|
|
9044
10684
|
const knowledgeBases = Array.isArray(config.knowledgeBases) ? config.knowledgeBases : [];
|
|
9045
10685
|
if (knowledgeBases.length === 0) {
|
|
9046
10686
|
return "No knowledge bases configured.";
|
|
9047
10687
|
}
|
|
9048
|
-
const normalizedInput =
|
|
10688
|
+
const normalizedInput = path9.normalize(inputPath);
|
|
9049
10689
|
const index = knowledgeBases.findIndex(
|
|
9050
|
-
(kb) =>
|
|
10690
|
+
(kb) => path9.normalize(kb) === normalizedInput || path9.normalize(path9.isAbsolute(kb) ? kb : path9.resolve(sharedProjectRoot, kb)) === normalizedInput
|
|
9051
10691
|
);
|
|
9052
10692
|
if (index === -1) {
|
|
9053
10693
|
let result2 = `Knowledge base not found: ${inputPath}
|
|
@@ -9079,8 +10719,8 @@ Run /index to rebuild the index without the removed knowledge base.`;
|
|
|
9079
10719
|
});
|
|
9080
10720
|
|
|
9081
10721
|
// src/commands/loader.ts
|
|
9082
|
-
var
|
|
9083
|
-
var
|
|
10722
|
+
var import_fs8 = require("fs");
|
|
10723
|
+
var path10 = __toESM(require("path"), 1);
|
|
9084
10724
|
function parseFrontmatter(content) {
|
|
9085
10725
|
const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
|
|
9086
10726
|
const match = content.match(frontmatterRegex);
|
|
@@ -9101,15 +10741,15 @@ function parseFrontmatter(content) {
|
|
|
9101
10741
|
}
|
|
9102
10742
|
function loadCommandsFromDirectory(commandsDir) {
|
|
9103
10743
|
const commands = /* @__PURE__ */ new Map();
|
|
9104
|
-
if (!(0,
|
|
10744
|
+
if (!(0, import_fs8.existsSync)(commandsDir)) {
|
|
9105
10745
|
return commands;
|
|
9106
10746
|
}
|
|
9107
|
-
const files = (0,
|
|
10747
|
+
const files = (0, import_fs8.readdirSync)(commandsDir).filter((f) => f.endsWith(".md"));
|
|
9108
10748
|
for (const file of files) {
|
|
9109
|
-
const filePath =
|
|
9110
|
-
const content = (0,
|
|
10749
|
+
const filePath = path10.join(commandsDir, file);
|
|
10750
|
+
const content = (0, import_fs8.readFileSync)(filePath, "utf-8");
|
|
9111
10751
|
const { frontmatter, body } = parseFrontmatter(content);
|
|
9112
|
-
const name =
|
|
10752
|
+
const name = path10.basename(file, ".md");
|
|
9113
10753
|
const description = frontmatter.description || `Run the ${name} command`;
|
|
9114
10754
|
commands.set(name, {
|
|
9115
10755
|
description,
|
|
@@ -9397,9 +11037,9 @@ function replaceActiveWatcher(nextWatcher) {
|
|
|
9397
11037
|
function getCommandsDir() {
|
|
9398
11038
|
let currentDir = process.cwd();
|
|
9399
11039
|
if (typeof import_meta2 !== "undefined" && import_meta2.url) {
|
|
9400
|
-
currentDir =
|
|
11040
|
+
currentDir = path11.dirname((0, import_url2.fileURLToPath)(import_meta2.url));
|
|
9401
11041
|
}
|
|
9402
|
-
return
|
|
11042
|
+
return path11.join(currentDir, "..", "commands");
|
|
9403
11043
|
}
|
|
9404
11044
|
var plugin = async ({ directory }) => {
|
|
9405
11045
|
try {
|