opencode-codebase-index 0.8.0 → 0.9.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 +29 -11
- package/dist/cli.cjs +1520 -1287
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1519 -1286
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1110 -923
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1081 -894
- 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 +8 -2
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(path18, 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(path18);
|
|
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 = (path18, originalPath, doThrow) => {
|
|
363
|
+
if (!isString(path18)) {
|
|
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 (!path18) {
|
|
370
370
|
return doThrow(`path must not be empty`, TypeError);
|
|
371
371
|
}
|
|
372
|
-
if (checkPath.isNotRelative(
|
|
372
|
+
if (checkPath.isNotRelative(path18)) {
|
|
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 = (path18) => REGEX_TEST_INVALID_PATH.test(path18);
|
|
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 path18 = originalPath && checkPath.convert(originalPath);
|
|
412
412
|
checkPath(
|
|
413
|
-
|
|
413
|
+
path18,
|
|
414
414
|
originalPath,
|
|
415
415
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
416
416
|
);
|
|
417
|
-
return this._t(
|
|
417
|
+
return this._t(path18, cache, checkUnignored, slices);
|
|
418
418
|
}
|
|
419
|
-
checkIgnore(
|
|
420
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
421
|
-
return this.test(
|
|
419
|
+
checkIgnore(path18) {
|
|
420
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path18)) {
|
|
421
|
+
return this.test(path18);
|
|
422
422
|
}
|
|
423
|
-
const slices =
|
|
423
|
+
const slices = path18.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(path18, false, MODE_CHECK_IGNORE);
|
|
437
437
|
}
|
|
438
|
-
_t(
|
|
439
|
-
if (
|
|
440
|
-
return cache[
|
|
438
|
+
_t(path18, cache, checkUnignored, slices) {
|
|
439
|
+
if (path18 in cache) {
|
|
440
|
+
return cache[path18];
|
|
441
441
|
}
|
|
442
442
|
if (!slices) {
|
|
443
|
-
slices =
|
|
443
|
+
slices = path18.split(SLASH2).filter(Boolean);
|
|
444
444
|
}
|
|
445
445
|
slices.pop();
|
|
446
446
|
if (!slices.length) {
|
|
447
|
-
return cache[
|
|
447
|
+
return cache[path18] = this._rules.test(path18, 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[path18] = parent.ignored ? parent : this._rules.test(path18, checkUnignored, MODE_IGNORE);
|
|
456
456
|
}
|
|
457
|
-
ignores(
|
|
458
|
-
return this._test(
|
|
457
|
+
ignores(path18) {
|
|
458
|
+
return this._test(path18, this._ignoreCache, false).ignored;
|
|
459
459
|
}
|
|
460
460
|
createFilter() {
|
|
461
|
-
return (
|
|
461
|
+
return (path18) => !this.ignores(path18);
|
|
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(path18) {
|
|
468
|
+
return this._test(path18, this._testCache, true);
|
|
469
469
|
}
|
|
470
470
|
};
|
|
471
471
|
var factory = (options) => new Ignore2(options);
|
|
472
|
-
var isPathValid = (
|
|
472
|
+
var isPathValid = (path18) => checkPath(path18 && checkPath.convert(path18), path18, 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 = (path18) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path18) || isNotRelative(path18);
|
|
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 path17 = __toESM(require("path"), 1);
|
|
661
661
|
var import_url2 = require("url");
|
|
662
662
|
|
|
663
663
|
// src/config/constants.ts
|
|
@@ -674,7 +674,8 @@ var DEFAULT_INCLUDE = [
|
|
|
674
674
|
"**/*.{md,mdx}",
|
|
675
675
|
"**/*.{sh,bash,zsh}",
|
|
676
676
|
"**/*.{txt,html,htm}",
|
|
677
|
-
"**/*.zig"
|
|
677
|
+
"**/*.zig",
|
|
678
|
+
"**/*.gd"
|
|
678
679
|
];
|
|
679
680
|
var DEFAULT_EXCLUDE = [
|
|
680
681
|
"**/node_modules/**",
|
|
@@ -773,28 +774,7 @@ var AUTO_DETECT_PROVIDER_ORDER = [
|
|
|
773
774
|
"google"
|
|
774
775
|
];
|
|
775
776
|
|
|
776
|
-
// src/config/
|
|
777
|
-
var ENV_REFERENCE_PATTERN = /^\{env:([A-Z_][A-Z0-9_]*)\}$/;
|
|
778
|
-
var ENV_REFERENCE_LIKE_PATTERN = /\{env:[^}]+\}/;
|
|
779
|
-
function substituteEnvString(value, keyPath) {
|
|
780
|
-
const match = value.match(ENV_REFERENCE_PATTERN);
|
|
781
|
-
if (!match) {
|
|
782
|
-
if (ENV_REFERENCE_LIKE_PATTERN.test(value)) {
|
|
783
|
-
throw new Error(
|
|
784
|
-
`Invalid environment variable reference at '${keyPath}'. Expected the entire string to match '{env:VAR_NAME}' with VAR_NAME matching [A-Z_][A-Z0-9_]*.`
|
|
785
|
-
);
|
|
786
|
-
}
|
|
787
|
-
return value;
|
|
788
|
-
}
|
|
789
|
-
const variableName = match[1];
|
|
790
|
-
const envValue = process.env[variableName];
|
|
791
|
-
if (envValue === void 0) {
|
|
792
|
-
throw new Error(`Missing environment variable '${variableName}' referenced by config at '${keyPath}'.`);
|
|
793
|
-
}
|
|
794
|
-
return envValue;
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
// src/config/schema.ts
|
|
777
|
+
// src/config/defaults.ts
|
|
798
778
|
function getDefaultIndexingConfig() {
|
|
799
779
|
return {
|
|
800
780
|
autoIndex: false,
|
|
@@ -826,12 +806,6 @@ function getDefaultSearchConfig() {
|
|
|
826
806
|
routingHints: true
|
|
827
807
|
};
|
|
828
808
|
}
|
|
829
|
-
function isValidFusionStrategy(value) {
|
|
830
|
-
return value === "weighted" || value === "rrf";
|
|
831
|
-
}
|
|
832
|
-
function isValidRerankerProvider(value) {
|
|
833
|
-
return value === "cohere" || value === "jina" || value === "custom";
|
|
834
|
-
}
|
|
835
809
|
function getDefaultRerankerBaseUrl(provider) {
|
|
836
810
|
switch (provider) {
|
|
837
811
|
case "cohere":
|
|
@@ -854,8 +828,37 @@ function getDefaultDebugConfig() {
|
|
|
854
828
|
metrics: true
|
|
855
829
|
};
|
|
856
830
|
}
|
|
831
|
+
|
|
832
|
+
// src/config/env-substitution.ts
|
|
833
|
+
var ENV_REFERENCE_PATTERN = /^\{env:([A-Z_][A-Z0-9_]*)\}$/;
|
|
834
|
+
var ENV_REFERENCE_LIKE_PATTERN = /\{env:[^}]+\}/;
|
|
835
|
+
function substituteEnvString(value, keyPath) {
|
|
836
|
+
const match = value.match(ENV_REFERENCE_PATTERN);
|
|
837
|
+
if (!match) {
|
|
838
|
+
if (ENV_REFERENCE_LIKE_PATTERN.test(value)) {
|
|
839
|
+
throw new Error(
|
|
840
|
+
`Invalid environment variable reference at '${keyPath}'. Expected the entire string to match '{env:VAR_NAME}' with VAR_NAME matching [A-Z_][A-Z0-9_]*.`
|
|
841
|
+
);
|
|
842
|
+
}
|
|
843
|
+
return value;
|
|
844
|
+
}
|
|
845
|
+
const variableName = match[1];
|
|
846
|
+
const envValue = process.env[variableName];
|
|
847
|
+
if (envValue === void 0) {
|
|
848
|
+
throw new Error(`Missing environment variable '${variableName}' referenced by config at '${keyPath}'.`);
|
|
849
|
+
}
|
|
850
|
+
return envValue;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
// src/config/validators.ts
|
|
857
854
|
var VALID_SCOPES = ["project", "global"];
|
|
858
855
|
var VALID_LOG_LEVELS = ["error", "warn", "info", "debug"];
|
|
856
|
+
function isValidFusionStrategy(value) {
|
|
857
|
+
return value === "weighted" || value === "rrf";
|
|
858
|
+
}
|
|
859
|
+
function isValidRerankerProvider(value) {
|
|
860
|
+
return value === "cohere" || value === "jina" || value === "custom";
|
|
861
|
+
}
|
|
859
862
|
function isValidProvider(value) {
|
|
860
863
|
return typeof value === "string" && Object.keys(EMBEDDING_MODELS).includes(value);
|
|
861
864
|
}
|
|
@@ -883,6 +886,8 @@ function getResolvedStringArray(value, keyPath) {
|
|
|
883
886
|
function isValidLogLevel(value) {
|
|
884
887
|
return typeof value === "string" && VALID_LOG_LEVELS.includes(value);
|
|
885
888
|
}
|
|
889
|
+
|
|
890
|
+
// src/config/schema.ts
|
|
886
891
|
function parseConfig(raw) {
|
|
887
892
|
const input = raw && typeof raw === "object" ? raw : {};
|
|
888
893
|
const embeddingProviderValue = getResolvedString(input.embeddingProvider, "$root.embeddingProvider");
|
|
@@ -937,9 +942,9 @@ function parseConfig(raw) {
|
|
|
937
942
|
const rawAdditionalInclude = input.additionalInclude;
|
|
938
943
|
const additionalInclude = isStringArray(rawAdditionalInclude) ? rawAdditionalInclude.filter((p) => typeof p === "string" && p.trim().length > 0).map((p) => p.trim()) : [];
|
|
939
944
|
let embeddingProvider;
|
|
940
|
-
let embeddingModel
|
|
941
|
-
let customProvider
|
|
942
|
-
let reranker
|
|
945
|
+
let embeddingModel;
|
|
946
|
+
let customProvider;
|
|
947
|
+
let reranker;
|
|
943
948
|
if (embeddingProviderValue === "custom") {
|
|
944
949
|
embeddingProvider = "custom";
|
|
945
950
|
const rawCustom = input.customProvider && typeof input.customProvider === "object" ? input.customProvider : null;
|
|
@@ -972,7 +977,7 @@ function parseConfig(raw) {
|
|
|
972
977
|
embeddingProvider = embeddingProviderValue;
|
|
973
978
|
const rawEmbeddingModel = input.embeddingModel;
|
|
974
979
|
if (typeof rawEmbeddingModel === "string") {
|
|
975
|
-
const embeddingModelValue =
|
|
980
|
+
const embeddingModelValue = getResolvedString(rawEmbeddingModel, "$root.embeddingModel");
|
|
976
981
|
if (embeddingModelValue) {
|
|
977
982
|
embeddingModel = isValidModel(embeddingModelValue, embeddingProvider) ? embeddingModelValue : DEFAULT_PROVIDER_MODELS[embeddingProvider];
|
|
978
983
|
}
|
|
@@ -1035,16 +1040,20 @@ var autoDetectProviders = AUTO_DETECT_PROVIDER_ORDER.filter(
|
|
|
1035
1040
|
);
|
|
1036
1041
|
|
|
1037
1042
|
// src/config/merger.ts
|
|
1038
|
-
var
|
|
1043
|
+
var import_fs4 = require("fs");
|
|
1039
1044
|
var os2 = __toESM(require("os"), 1);
|
|
1040
|
-
var
|
|
1045
|
+
var path6 = __toESM(require("path"), 1);
|
|
1041
1046
|
|
|
1042
1047
|
// src/config/paths.ts
|
|
1043
|
-
var
|
|
1048
|
+
var import_fs3 = require("fs");
|
|
1044
1049
|
var os = __toESM(require("os"), 1);
|
|
1045
|
-
var
|
|
1050
|
+
var path3 = __toESM(require("path"), 1);
|
|
1046
1051
|
|
|
1047
1052
|
// src/git/index.ts
|
|
1053
|
+
var import_fs2 = require("fs");
|
|
1054
|
+
var path2 = __toESM(require("path"), 1);
|
|
1055
|
+
|
|
1056
|
+
// src/git/refs.ts
|
|
1048
1057
|
var import_fs = require("fs");
|
|
1049
1058
|
var path = __toESM(require("path"), 1);
|
|
1050
1059
|
function readPackedRefs(gitDir) {
|
|
@@ -1077,38 +1086,40 @@ function resolveCommonGitDir(gitDir) {
|
|
|
1077
1086
|
}
|
|
1078
1087
|
return gitDir;
|
|
1079
1088
|
}
|
|
1089
|
+
|
|
1090
|
+
// src/git/index.ts
|
|
1080
1091
|
function resolveWorktreeMainRepoRoot(repoRoot) {
|
|
1081
1092
|
const gitDir = resolveGitDir(repoRoot);
|
|
1082
1093
|
if (!gitDir) {
|
|
1083
1094
|
return null;
|
|
1084
1095
|
}
|
|
1085
1096
|
const commonGitDir = resolveCommonGitDir(gitDir);
|
|
1086
|
-
if (commonGitDir === gitDir ||
|
|
1097
|
+
if (commonGitDir === gitDir || path2.basename(commonGitDir) !== ".git") {
|
|
1087
1098
|
return null;
|
|
1088
1099
|
}
|
|
1089
|
-
const mainRepoRoot =
|
|
1090
|
-
if (!(0,
|
|
1100
|
+
const mainRepoRoot = path2.dirname(commonGitDir);
|
|
1101
|
+
if (!(0, import_fs2.existsSync)(mainRepoRoot)) {
|
|
1091
1102
|
return null;
|
|
1092
1103
|
}
|
|
1093
|
-
return
|
|
1104
|
+
return path2.resolve(mainRepoRoot) === path2.resolve(repoRoot) ? null : mainRepoRoot;
|
|
1094
1105
|
}
|
|
1095
1106
|
function resolveGitDir(repoRoot) {
|
|
1096
|
-
const gitPath =
|
|
1097
|
-
if (!(0,
|
|
1107
|
+
const gitPath = path2.join(repoRoot, ".git");
|
|
1108
|
+
if (!(0, import_fs2.existsSync)(gitPath)) {
|
|
1098
1109
|
return null;
|
|
1099
1110
|
}
|
|
1100
1111
|
try {
|
|
1101
|
-
const stat4 = (0,
|
|
1112
|
+
const stat4 = (0, import_fs2.statSync)(gitPath);
|
|
1102
1113
|
if (stat4.isDirectory()) {
|
|
1103
1114
|
return gitPath;
|
|
1104
1115
|
}
|
|
1105
1116
|
if (stat4.isFile()) {
|
|
1106
|
-
const content = (0,
|
|
1117
|
+
const content = (0, import_fs2.readFileSync)(gitPath, "utf-8").trim();
|
|
1107
1118
|
const match = content.match(/^gitdir:\s*(.+)$/);
|
|
1108
1119
|
if (match) {
|
|
1109
1120
|
const gitdir = match[1];
|
|
1110
|
-
const resolvedPath =
|
|
1111
|
-
if ((0,
|
|
1121
|
+
const resolvedPath = path2.isAbsolute(gitdir) ? gitdir : path2.resolve(repoRoot, gitdir);
|
|
1122
|
+
if ((0, import_fs2.existsSync)(resolvedPath)) {
|
|
1112
1123
|
return resolvedPath;
|
|
1113
1124
|
}
|
|
1114
1125
|
}
|
|
@@ -1125,12 +1136,12 @@ function getCurrentBranch(repoRoot) {
|
|
|
1125
1136
|
if (!gitDir) {
|
|
1126
1137
|
return null;
|
|
1127
1138
|
}
|
|
1128
|
-
const headPath =
|
|
1129
|
-
if (!(0,
|
|
1139
|
+
const headPath = path2.join(gitDir, "HEAD");
|
|
1140
|
+
if (!(0, import_fs2.existsSync)(headPath)) {
|
|
1130
1141
|
return null;
|
|
1131
1142
|
}
|
|
1132
1143
|
try {
|
|
1133
|
-
const headContent = (0,
|
|
1144
|
+
const headContent = (0, import_fs2.readFileSync)(headPath, "utf-8").trim();
|
|
1134
1145
|
const match = headContent.match(/^ref: refs\/heads\/(.+)$/);
|
|
1135
1146
|
if (match) {
|
|
1136
1147
|
return match[1];
|
|
@@ -1149,8 +1160,8 @@ function getBaseBranch(repoRoot) {
|
|
|
1149
1160
|
const candidates = ["main", "master", "develop", "trunk"];
|
|
1150
1161
|
if (refStoreDir) {
|
|
1151
1162
|
for (const candidate of candidates) {
|
|
1152
|
-
const refPath =
|
|
1153
|
-
if ((0,
|
|
1163
|
+
const refPath = path2.join(refStoreDir, "refs", "heads", candidate);
|
|
1164
|
+
if ((0, import_fs2.existsSync)(refPath)) {
|
|
1154
1165
|
return candidate;
|
|
1155
1166
|
}
|
|
1156
1167
|
const packedRefs = readPackedRefs(refStoreDir);
|
|
@@ -1170,44 +1181,44 @@ function getBranchOrDefault(repoRoot) {
|
|
|
1170
1181
|
function getHeadPath(repoRoot) {
|
|
1171
1182
|
const gitDir = resolveGitDir(repoRoot);
|
|
1172
1183
|
if (gitDir) {
|
|
1173
|
-
return
|
|
1184
|
+
return path2.join(gitDir, "HEAD");
|
|
1174
1185
|
}
|
|
1175
|
-
return
|
|
1186
|
+
return path2.join(repoRoot, ".git", "HEAD");
|
|
1176
1187
|
}
|
|
1177
1188
|
|
|
1178
1189
|
// src/config/paths.ts
|
|
1179
|
-
var PROJECT_CONFIG_RELATIVE_PATH =
|
|
1180
|
-
var PROJECT_INDEX_RELATIVE_PATH =
|
|
1190
|
+
var PROJECT_CONFIG_RELATIVE_PATH = path3.join(".opencode", "codebase-index.json");
|
|
1191
|
+
var PROJECT_INDEX_RELATIVE_PATH = path3.join(".opencode", "index");
|
|
1181
1192
|
function resolveWorktreeFallbackPath(projectRoot, relativePath) {
|
|
1182
1193
|
const mainRepoRoot = resolveWorktreeMainRepoRoot(projectRoot);
|
|
1183
1194
|
if (!mainRepoRoot) {
|
|
1184
1195
|
return null;
|
|
1185
1196
|
}
|
|
1186
|
-
const fallbackPath =
|
|
1187
|
-
return (0,
|
|
1197
|
+
const fallbackPath = path3.join(mainRepoRoot, relativePath);
|
|
1198
|
+
return (0, import_fs3.existsSync)(fallbackPath) ? fallbackPath : null;
|
|
1188
1199
|
}
|
|
1189
1200
|
function hasProjectConfig(projectRoot) {
|
|
1190
|
-
return (0,
|
|
1201
|
+
return (0, import_fs3.existsSync)(path3.join(projectRoot, PROJECT_CONFIG_RELATIVE_PATH));
|
|
1191
1202
|
}
|
|
1192
1203
|
function getGlobalIndexPath() {
|
|
1193
|
-
return
|
|
1204
|
+
return path3.join(os.homedir(), ".opencode", "global-index");
|
|
1194
1205
|
}
|
|
1195
1206
|
function resolveProjectConfigPath(projectRoot) {
|
|
1196
|
-
const localConfigPath =
|
|
1197
|
-
if ((0,
|
|
1207
|
+
const localConfigPath = path3.join(projectRoot, PROJECT_CONFIG_RELATIVE_PATH);
|
|
1208
|
+
if ((0, import_fs3.existsSync)(localConfigPath)) {
|
|
1198
1209
|
return localConfigPath;
|
|
1199
1210
|
}
|
|
1200
1211
|
return resolveWorktreeFallbackPath(projectRoot, PROJECT_CONFIG_RELATIVE_PATH) ?? localConfigPath;
|
|
1201
1212
|
}
|
|
1202
1213
|
function resolveWritableProjectConfigPath(projectRoot) {
|
|
1203
|
-
return
|
|
1214
|
+
return path3.join(projectRoot, PROJECT_CONFIG_RELATIVE_PATH);
|
|
1204
1215
|
}
|
|
1205
1216
|
function resolveProjectIndexPath(projectRoot, scope) {
|
|
1206
1217
|
if (scope === "global") {
|
|
1207
1218
|
return getGlobalIndexPath();
|
|
1208
1219
|
}
|
|
1209
|
-
const localIndexPath =
|
|
1210
|
-
if ((0,
|
|
1220
|
+
const localIndexPath = path3.join(projectRoot, PROJECT_INDEX_RELATIVE_PATH);
|
|
1221
|
+
if ((0, import_fs3.existsSync)(localIndexPath)) {
|
|
1211
1222
|
return localIndexPath;
|
|
1212
1223
|
}
|
|
1213
1224
|
if (hasProjectConfig(projectRoot)) {
|
|
@@ -1216,23 +1227,30 @@ function resolveProjectIndexPath(projectRoot, scope) {
|
|
|
1216
1227
|
return resolveWorktreeFallbackPath(projectRoot, PROJECT_INDEX_RELATIVE_PATH) ?? localIndexPath;
|
|
1217
1228
|
}
|
|
1218
1229
|
|
|
1219
|
-
// src/config/
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
return
|
|
1230
|
+
// src/config/rebase.ts
|
|
1231
|
+
var path5 = __toESM(require("path"), 1);
|
|
1232
|
+
|
|
1233
|
+
// src/utils/paths.ts
|
|
1234
|
+
var path4 = __toESM(require("path"), 1);
|
|
1235
|
+
function normalizePathSeparators(value) {
|
|
1236
|
+
return value.replace(/\\/g, "/");
|
|
1237
|
+
}
|
|
1238
|
+
function isHiddenPathSegment(part) {
|
|
1239
|
+
return part.startsWith(".") && part !== "." && part !== "..";
|
|
1229
1240
|
}
|
|
1230
|
-
function
|
|
1231
|
-
return
|
|
1241
|
+
function isBuildPathSegment(part) {
|
|
1242
|
+
return part.toLowerCase().includes("build");
|
|
1243
|
+
}
|
|
1244
|
+
function hasFilteredPathSegment(relativePath, separator = path4.sep) {
|
|
1245
|
+
return relativePath.split(separator).some(
|
|
1246
|
+
(part) => isHiddenPathSegment(part) || isBuildPathSegment(part)
|
|
1247
|
+
);
|
|
1232
1248
|
}
|
|
1249
|
+
|
|
1250
|
+
// src/config/rebase.ts
|
|
1233
1251
|
function isWithinRoot(rootDir, targetPath) {
|
|
1234
|
-
const relativePath =
|
|
1235
|
-
return relativePath === "" || !relativePath.startsWith("..") && !
|
|
1252
|
+
const relativePath = path5.relative(rootDir, targetPath);
|
|
1253
|
+
return relativePath === "" || !relativePath.startsWith("..") && !path5.isAbsolute(relativePath);
|
|
1236
1254
|
}
|
|
1237
1255
|
function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
|
|
1238
1256
|
if (!Array.isArray(values)) {
|
|
@@ -1243,23 +1261,107 @@ function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
|
|
|
1243
1261
|
if (!trimmed) {
|
|
1244
1262
|
return trimmed;
|
|
1245
1263
|
}
|
|
1246
|
-
if (
|
|
1264
|
+
if (path5.isAbsolute(trimmed)) {
|
|
1247
1265
|
if (isWithinRoot(sourceRoot, trimmed)) {
|
|
1248
|
-
return
|
|
1266
|
+
return normalizePathSeparators(path5.normalize(path5.relative(sourceRoot, trimmed) || "."));
|
|
1249
1267
|
}
|
|
1250
|
-
return
|
|
1268
|
+
return path5.normalize(trimmed);
|
|
1251
1269
|
}
|
|
1252
|
-
const resolvedFromSource =
|
|
1270
|
+
const resolvedFromSource = path5.resolve(sourceRoot, trimmed);
|
|
1253
1271
|
if (isWithinRoot(sourceRoot, resolvedFromSource)) {
|
|
1254
|
-
return
|
|
1272
|
+
return normalizePathSeparators(path5.normalize(trimmed));
|
|
1255
1273
|
}
|
|
1256
|
-
return
|
|
1274
|
+
return normalizePathSeparators(path5.normalize(path5.relative(targetRoot, resolvedFromSource)));
|
|
1257
1275
|
}).filter(Boolean);
|
|
1258
1276
|
}
|
|
1277
|
+
|
|
1278
|
+
// src/config/merger.ts
|
|
1279
|
+
var PROJECT_OVERRIDE_KEYS = [
|
|
1280
|
+
"embeddingProvider",
|
|
1281
|
+
"customProvider",
|
|
1282
|
+
"embeddingModel",
|
|
1283
|
+
"reranker",
|
|
1284
|
+
"include",
|
|
1285
|
+
"exclude",
|
|
1286
|
+
"indexing",
|
|
1287
|
+
"search",
|
|
1288
|
+
"debug",
|
|
1289
|
+
"scope"
|
|
1290
|
+
];
|
|
1291
|
+
var MERGE_ARRAY_KEYS = ["knowledgeBases", "additionalInclude"];
|
|
1292
|
+
function isRecord(value) {
|
|
1293
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1294
|
+
}
|
|
1295
|
+
function isStringArray2(value) {
|
|
1296
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
1297
|
+
}
|
|
1298
|
+
function applyProjectOverride(merged, normalizedProjectConfig, globalConfig, key) {
|
|
1299
|
+
if (key in normalizedProjectConfig) {
|
|
1300
|
+
merged[key] = normalizedProjectConfig[key];
|
|
1301
|
+
return;
|
|
1302
|
+
}
|
|
1303
|
+
if (key in globalConfig) {
|
|
1304
|
+
merged[key] = globalConfig[key];
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
function mergeUniqueStringArray(values) {
|
|
1308
|
+
return [...new Set(values.map((value) => String(value).trim()))];
|
|
1309
|
+
}
|
|
1310
|
+
function normalizeKnowledgeBasePath(value) {
|
|
1311
|
+
let normalized = path6.normalize(String(value).trim());
|
|
1312
|
+
const root = path6.parse(normalized).root;
|
|
1313
|
+
while (normalized.length > root.length && /[\\/]$/.test(normalized)) {
|
|
1314
|
+
normalized = normalized.slice(0, -1);
|
|
1315
|
+
}
|
|
1316
|
+
return normalized;
|
|
1317
|
+
}
|
|
1318
|
+
function mergeKnowledgeBasePaths(values) {
|
|
1319
|
+
return [...new Set(values.map((value) => normalizeKnowledgeBasePath(value)).filter((value) => value.length > 0))];
|
|
1320
|
+
}
|
|
1321
|
+
function validateConfigLayerShape(rawConfig, filePath) {
|
|
1322
|
+
if (!isRecord(rawConfig)) {
|
|
1323
|
+
throw new Error(`Config file ${filePath} must contain a JSON object at the root.`);
|
|
1324
|
+
}
|
|
1325
|
+
if (rawConfig.knowledgeBases !== void 0 && !isStringArray2(rawConfig.knowledgeBases)) {
|
|
1326
|
+
throw new Error(`Config file ${filePath} field 'knowledgeBases' must be an array of strings.`);
|
|
1327
|
+
}
|
|
1328
|
+
if (rawConfig.additionalInclude !== void 0 && !isStringArray2(rawConfig.additionalInclude)) {
|
|
1329
|
+
throw new Error(`Config file ${filePath} field 'additionalInclude' must be an array of strings.`);
|
|
1330
|
+
}
|
|
1331
|
+
if (rawConfig.include !== void 0 && !isStringArray2(rawConfig.include)) {
|
|
1332
|
+
throw new Error(`Config file ${filePath} field 'include' must be an array of strings.`);
|
|
1333
|
+
}
|
|
1334
|
+
if (rawConfig.exclude !== void 0 && !isStringArray2(rawConfig.exclude)) {
|
|
1335
|
+
throw new Error(`Config file ${filePath} field 'exclude' must be an array of strings.`);
|
|
1336
|
+
}
|
|
1337
|
+
for (const section of ["customProvider", "indexing", "search", "debug", "reranker"]) {
|
|
1338
|
+
const value = rawConfig[section];
|
|
1339
|
+
if (value !== void 0 && !isRecord(value)) {
|
|
1340
|
+
throw new Error(`Config file ${filePath} field '${section}' must be an object.`);
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
return rawConfig;
|
|
1344
|
+
}
|
|
1345
|
+
function loadJsonFile(filePath) {
|
|
1346
|
+
if (!(0, import_fs4.existsSync)(filePath)) {
|
|
1347
|
+
return null;
|
|
1348
|
+
}
|
|
1349
|
+
try {
|
|
1350
|
+
const content = (0, import_fs4.readFileSync)(filePath, "utf-8");
|
|
1351
|
+
return validateConfigLayerShape(JSON.parse(content), filePath);
|
|
1352
|
+
} catch (error) {
|
|
1353
|
+
if (error instanceof Error && error.message.startsWith("Config file ")) {
|
|
1354
|
+
throw error;
|
|
1355
|
+
}
|
|
1356
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1357
|
+
throw new Error(`Failed to load config file ${filePath}: ${message}`);
|
|
1358
|
+
}
|
|
1359
|
+
return null;
|
|
1360
|
+
}
|
|
1259
1361
|
function materializeLocalProjectConfig(projectRoot, config) {
|
|
1260
|
-
const localConfigPath =
|
|
1261
|
-
(0,
|
|
1262
|
-
(0,
|
|
1362
|
+
const localConfigPath = path6.join(projectRoot, ".opencode", "codebase-index.json");
|
|
1363
|
+
(0, import_fs4.mkdirSync)(path6.dirname(localConfigPath), { recursive: true });
|
|
1364
|
+
(0, import_fs4.writeFileSync)(localConfigPath, JSON.stringify(config, null, 2), "utf-8");
|
|
1263
1365
|
return localConfigPath;
|
|
1264
1366
|
}
|
|
1265
1367
|
function loadProjectConfigLayer(projectRoot) {
|
|
@@ -1269,7 +1371,7 @@ function loadProjectConfigLayer(projectRoot) {
|
|
|
1269
1371
|
return {};
|
|
1270
1372
|
}
|
|
1271
1373
|
const normalizedConfig = { ...projectConfig };
|
|
1272
|
-
const projectConfigBaseDir =
|
|
1374
|
+
const projectConfigBaseDir = path6.dirname(path6.dirname(projectConfigPath));
|
|
1273
1375
|
if (Array.isArray(normalizedConfig.knowledgeBases)) {
|
|
1274
1376
|
normalizedConfig.knowledgeBases = resolveInheritedKnowledgeBaseEntries(
|
|
1275
1377
|
normalizedConfig.knowledgeBases,
|
|
@@ -1281,10 +1383,22 @@ function loadProjectConfigLayer(projectRoot) {
|
|
|
1281
1383
|
}
|
|
1282
1384
|
function loadMergedConfig(projectRoot) {
|
|
1283
1385
|
const globalConfigPath = os2.homedir() + "/.config/opencode/codebase-index.json";
|
|
1284
|
-
const globalConfig = loadJsonFile(globalConfigPath);
|
|
1285
1386
|
const projectConfigPath = resolveProjectConfigPath(projectRoot);
|
|
1387
|
+
let globalConfig = null;
|
|
1388
|
+
let globalConfigError = null;
|
|
1389
|
+
try {
|
|
1390
|
+
globalConfig = loadJsonFile(globalConfigPath);
|
|
1391
|
+
} catch (error) {
|
|
1392
|
+
globalConfigError = error instanceof Error ? error : new Error(String(error));
|
|
1393
|
+
}
|
|
1286
1394
|
const projectConfig = loadJsonFile(projectConfigPath);
|
|
1287
1395
|
const normalizedProjectConfig = loadProjectConfigLayer(projectRoot);
|
|
1396
|
+
if (globalConfigError) {
|
|
1397
|
+
if (!projectConfig) {
|
|
1398
|
+
throw globalConfigError;
|
|
1399
|
+
}
|
|
1400
|
+
globalConfig = null;
|
|
1401
|
+
}
|
|
1288
1402
|
if (!globalConfig && !projectConfig) {
|
|
1289
1403
|
return {};
|
|
1290
1404
|
}
|
|
@@ -1294,60 +1408,16 @@ function loadMergedConfig(projectRoot) {
|
|
|
1294
1408
|
if (!globalConfig && projectConfig) {
|
|
1295
1409
|
return normalizedProjectConfig;
|
|
1296
1410
|
}
|
|
1411
|
+
if (!globalConfig || !projectConfig) {
|
|
1412
|
+
return globalConfig ?? normalizedProjectConfig;
|
|
1413
|
+
}
|
|
1297
1414
|
const merged = { ...globalConfig };
|
|
1298
|
-
|
|
1299
|
-
merged
|
|
1300
|
-
} else if (globalConfig && globalConfig.embeddingProvider) {
|
|
1301
|
-
merged.embeddingProvider = globalConfig.embeddingProvider;
|
|
1302
|
-
}
|
|
1303
|
-
if (projectConfig && "customProvider" in normalizedProjectConfig) {
|
|
1304
|
-
merged.customProvider = normalizedProjectConfig.customProvider;
|
|
1305
|
-
} else if (globalConfig && globalConfig.customProvider) {
|
|
1306
|
-
merged.customProvider = globalConfig.customProvider;
|
|
1307
|
-
}
|
|
1308
|
-
if (projectConfig && "embeddingModel" in normalizedProjectConfig) {
|
|
1309
|
-
merged.embeddingModel = normalizedProjectConfig.embeddingModel;
|
|
1310
|
-
} else if (globalConfig && globalConfig.embeddingModel) {
|
|
1311
|
-
merged.embeddingModel = globalConfig.embeddingModel;
|
|
1312
|
-
}
|
|
1313
|
-
if (projectConfig && "reranker" in normalizedProjectConfig) {
|
|
1314
|
-
merged.reranker = normalizedProjectConfig.reranker;
|
|
1315
|
-
} else if (globalConfig && globalConfig.reranker) {
|
|
1316
|
-
merged.reranker = globalConfig.reranker;
|
|
1317
|
-
}
|
|
1318
|
-
if (projectConfig && "include" in normalizedProjectConfig) {
|
|
1319
|
-
merged.include = normalizedProjectConfig.include;
|
|
1320
|
-
} else if (globalConfig && globalConfig.include) {
|
|
1321
|
-
merged.include = globalConfig.include;
|
|
1322
|
-
}
|
|
1323
|
-
if (projectConfig && "exclude" in normalizedProjectConfig) {
|
|
1324
|
-
merged.exclude = normalizedProjectConfig.exclude;
|
|
1325
|
-
} else if (globalConfig && globalConfig.exclude) {
|
|
1326
|
-
merged.exclude = globalConfig.exclude;
|
|
1327
|
-
}
|
|
1328
|
-
if (projectConfig && "indexing" in normalizedProjectConfig) {
|
|
1329
|
-
merged.indexing = normalizedProjectConfig.indexing;
|
|
1330
|
-
} else if (globalConfig && globalConfig.indexing) {
|
|
1331
|
-
merged.indexing = globalConfig.indexing;
|
|
1332
|
-
}
|
|
1333
|
-
if (projectConfig && "search" in normalizedProjectConfig) {
|
|
1334
|
-
merged.search = normalizedProjectConfig.search;
|
|
1335
|
-
} else if (globalConfig && globalConfig.search) {
|
|
1336
|
-
merged.search = globalConfig.search;
|
|
1337
|
-
}
|
|
1338
|
-
if (projectConfig && "debug" in normalizedProjectConfig) {
|
|
1339
|
-
merged.debug = normalizedProjectConfig.debug;
|
|
1340
|
-
} else if (globalConfig && globalConfig.debug) {
|
|
1341
|
-
merged.debug = globalConfig.debug;
|
|
1342
|
-
}
|
|
1343
|
-
if (projectConfig && "scope" in normalizedProjectConfig) {
|
|
1344
|
-
merged.scope = normalizedProjectConfig.scope;
|
|
1345
|
-
} else if (globalConfig && "scope" in globalConfig) {
|
|
1346
|
-
merged.scope = globalConfig.scope;
|
|
1415
|
+
for (const key of PROJECT_OVERRIDE_KEYS) {
|
|
1416
|
+
applyProjectOverride(merged, normalizedProjectConfig, globalConfig, key);
|
|
1347
1417
|
}
|
|
1348
1418
|
if (projectConfig) {
|
|
1349
1419
|
for (const key of Object.keys(projectConfig)) {
|
|
1350
|
-
if (key
|
|
1420
|
+
if (PROJECT_OVERRIDE_KEYS.includes(key) || MERGE_ARRAY_KEYS.includes(key)) {
|
|
1351
1421
|
continue;
|
|
1352
1422
|
}
|
|
1353
1423
|
merged[key] = normalizedProjectConfig[key];
|
|
@@ -1356,13 +1426,11 @@ function loadMergedConfig(projectRoot) {
|
|
|
1356
1426
|
const globalKbs = globalConfig && Array.isArray(globalConfig.knowledgeBases) ? globalConfig.knowledgeBases : [];
|
|
1357
1427
|
const projectKbs = projectConfig ? Array.isArray(normalizedProjectConfig.knowledgeBases) ? normalizedProjectConfig.knowledgeBases : [] : [];
|
|
1358
1428
|
const allKbs = [...globalKbs, ...projectKbs];
|
|
1359
|
-
|
|
1360
|
-
merged.knowledgeBases = uniqueKbs;
|
|
1429
|
+
merged.knowledgeBases = mergeKnowledgeBasePaths(allKbs);
|
|
1361
1430
|
const globalAdditional = globalConfig && Array.isArray(globalConfig.additionalInclude) ? globalConfig.additionalInclude : [];
|
|
1362
1431
|
const projectAdditional = projectConfig && Array.isArray(projectConfig.additionalInclude) ? projectConfig.additionalInclude : [];
|
|
1363
1432
|
const allAdditional = [...globalAdditional, ...projectAdditional];
|
|
1364
|
-
|
|
1365
|
-
merged.additionalInclude = uniqueAdditional;
|
|
1433
|
+
merged.additionalInclude = mergeUniqueStringArray(allAdditional);
|
|
1366
1434
|
return merged;
|
|
1367
1435
|
}
|
|
1368
1436
|
|
|
@@ -1456,7 +1524,7 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
1456
1524
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
1457
1525
|
const statMethod = opts.lstat ? import_promises.lstat : import_promises.stat;
|
|
1458
1526
|
if (wantBigintFsStats) {
|
|
1459
|
-
this._stat = (
|
|
1527
|
+
this._stat = (path18) => statMethod(path18, { bigint: true });
|
|
1460
1528
|
} else {
|
|
1461
1529
|
this._stat = statMethod;
|
|
1462
1530
|
}
|
|
@@ -1481,8 +1549,8 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
1481
1549
|
const par = this.parent;
|
|
1482
1550
|
const fil = par && par.files;
|
|
1483
1551
|
if (fil && fil.length > 0) {
|
|
1484
|
-
const { path:
|
|
1485
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
1552
|
+
const { path: path18, depth } = par;
|
|
1553
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path18));
|
|
1486
1554
|
const awaited = await Promise.all(slice);
|
|
1487
1555
|
for (const entry of awaited) {
|
|
1488
1556
|
if (!entry)
|
|
@@ -1522,20 +1590,20 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
1522
1590
|
this.reading = false;
|
|
1523
1591
|
}
|
|
1524
1592
|
}
|
|
1525
|
-
async _exploreDir(
|
|
1593
|
+
async _exploreDir(path18, depth) {
|
|
1526
1594
|
let files;
|
|
1527
1595
|
try {
|
|
1528
|
-
files = await (0, import_promises.readdir)(
|
|
1596
|
+
files = await (0, import_promises.readdir)(path18, this._rdOptions);
|
|
1529
1597
|
} catch (error) {
|
|
1530
1598
|
this._onError(error);
|
|
1531
1599
|
}
|
|
1532
|
-
return { files, depth, path:
|
|
1600
|
+
return { files, depth, path: path18 };
|
|
1533
1601
|
}
|
|
1534
|
-
async _formatEntry(dirent,
|
|
1602
|
+
async _formatEntry(dirent, path18) {
|
|
1535
1603
|
let entry;
|
|
1536
1604
|
const basename5 = this._isDirent ? dirent.name : dirent;
|
|
1537
1605
|
try {
|
|
1538
|
-
const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(
|
|
1606
|
+
const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path18, basename5));
|
|
1539
1607
|
entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename5 };
|
|
1540
1608
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
1541
1609
|
} catch (err) {
|
|
@@ -1935,16 +2003,16 @@ var delFromSet = (main, prop, item) => {
|
|
|
1935
2003
|
};
|
|
1936
2004
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
1937
2005
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
1938
|
-
function createFsWatchInstance(
|
|
2006
|
+
function createFsWatchInstance(path18, options, listener, errHandler, emitRaw) {
|
|
1939
2007
|
const handleEvent = (rawEvent, evPath) => {
|
|
1940
|
-
listener(
|
|
1941
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
1942
|
-
if (evPath &&
|
|
1943
|
-
fsWatchBroadcast(sp.resolve(
|
|
2008
|
+
listener(path18);
|
|
2009
|
+
emitRaw(rawEvent, evPath, { watchedPath: path18 });
|
|
2010
|
+
if (evPath && path18 !== evPath) {
|
|
2011
|
+
fsWatchBroadcast(sp.resolve(path18, evPath), KEY_LISTENERS, sp.join(path18, evPath));
|
|
1944
2012
|
}
|
|
1945
2013
|
};
|
|
1946
2014
|
try {
|
|
1947
|
-
return (0, import_node_fs.watch)(
|
|
2015
|
+
return (0, import_node_fs.watch)(path18, {
|
|
1948
2016
|
persistent: options.persistent
|
|
1949
2017
|
}, handleEvent);
|
|
1950
2018
|
} catch (error) {
|
|
@@ -1960,12 +2028,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
1960
2028
|
listener(val1, val2, val3);
|
|
1961
2029
|
});
|
|
1962
2030
|
};
|
|
1963
|
-
var setFsWatchListener = (
|
|
2031
|
+
var setFsWatchListener = (path18, fullPath, options, handlers) => {
|
|
1964
2032
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
1965
2033
|
let cont = FsWatchInstances.get(fullPath);
|
|
1966
2034
|
let watcher;
|
|
1967
2035
|
if (!options.persistent) {
|
|
1968
|
-
watcher = createFsWatchInstance(
|
|
2036
|
+
watcher = createFsWatchInstance(path18, options, listener, errHandler, rawEmitter);
|
|
1969
2037
|
if (!watcher)
|
|
1970
2038
|
return;
|
|
1971
2039
|
return watcher.close.bind(watcher);
|
|
@@ -1976,7 +2044,7 @@ var setFsWatchListener = (path12, fullPath, options, handlers) => {
|
|
|
1976
2044
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
1977
2045
|
} else {
|
|
1978
2046
|
watcher = createFsWatchInstance(
|
|
1979
|
-
|
|
2047
|
+
path18,
|
|
1980
2048
|
options,
|
|
1981
2049
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
1982
2050
|
errHandler,
|
|
@@ -1991,7 +2059,7 @@ var setFsWatchListener = (path12, fullPath, options, handlers) => {
|
|
|
1991
2059
|
cont.watcherUnusable = true;
|
|
1992
2060
|
if (isWindows && error.code === "EPERM") {
|
|
1993
2061
|
try {
|
|
1994
|
-
const fd = await (0, import_promises2.open)(
|
|
2062
|
+
const fd = await (0, import_promises2.open)(path18, "r");
|
|
1995
2063
|
await fd.close();
|
|
1996
2064
|
broadcastErr(error);
|
|
1997
2065
|
} catch (err) {
|
|
@@ -2022,7 +2090,7 @@ var setFsWatchListener = (path12, fullPath, options, handlers) => {
|
|
|
2022
2090
|
};
|
|
2023
2091
|
};
|
|
2024
2092
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
2025
|
-
var setFsWatchFileListener = (
|
|
2093
|
+
var setFsWatchFileListener = (path18, fullPath, options, handlers) => {
|
|
2026
2094
|
const { listener, rawEmitter } = handlers;
|
|
2027
2095
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
2028
2096
|
const copts = cont && cont.options;
|
|
@@ -2044,7 +2112,7 @@ var setFsWatchFileListener = (path12, fullPath, options, handlers) => {
|
|
|
2044
2112
|
});
|
|
2045
2113
|
const currmtime = curr.mtimeMs;
|
|
2046
2114
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
2047
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
2115
|
+
foreach(cont.listeners, (listener2) => listener2(path18, curr));
|
|
2048
2116
|
}
|
|
2049
2117
|
})
|
|
2050
2118
|
};
|
|
@@ -2074,13 +2142,13 @@ var NodeFsHandler = class {
|
|
|
2074
2142
|
* @param listener on fs change
|
|
2075
2143
|
* @returns closer for the watcher instance
|
|
2076
2144
|
*/
|
|
2077
|
-
_watchWithNodeFs(
|
|
2145
|
+
_watchWithNodeFs(path18, listener) {
|
|
2078
2146
|
const opts = this.fsw.options;
|
|
2079
|
-
const directory = sp.dirname(
|
|
2080
|
-
const basename5 = sp.basename(
|
|
2147
|
+
const directory = sp.dirname(path18);
|
|
2148
|
+
const basename5 = sp.basename(path18);
|
|
2081
2149
|
const parent = this.fsw._getWatchedDir(directory);
|
|
2082
2150
|
parent.add(basename5);
|
|
2083
|
-
const absolutePath = sp.resolve(
|
|
2151
|
+
const absolutePath = sp.resolve(path18);
|
|
2084
2152
|
const options = {
|
|
2085
2153
|
persistent: opts.persistent
|
|
2086
2154
|
};
|
|
@@ -2090,12 +2158,12 @@ var NodeFsHandler = class {
|
|
|
2090
2158
|
if (opts.usePolling) {
|
|
2091
2159
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
2092
2160
|
options.interval = enableBin && isBinaryPath(basename5) ? opts.binaryInterval : opts.interval;
|
|
2093
|
-
closer = setFsWatchFileListener(
|
|
2161
|
+
closer = setFsWatchFileListener(path18, absolutePath, options, {
|
|
2094
2162
|
listener,
|
|
2095
2163
|
rawEmitter: this.fsw._emitRaw
|
|
2096
2164
|
});
|
|
2097
2165
|
} else {
|
|
2098
|
-
closer = setFsWatchListener(
|
|
2166
|
+
closer = setFsWatchListener(path18, absolutePath, options, {
|
|
2099
2167
|
listener,
|
|
2100
2168
|
errHandler: this._boundHandleError,
|
|
2101
2169
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -2111,13 +2179,13 @@ var NodeFsHandler = class {
|
|
|
2111
2179
|
if (this.fsw.closed) {
|
|
2112
2180
|
return;
|
|
2113
2181
|
}
|
|
2114
|
-
const
|
|
2182
|
+
const dirname9 = sp.dirname(file);
|
|
2115
2183
|
const basename5 = sp.basename(file);
|
|
2116
|
-
const parent = this.fsw._getWatchedDir(
|
|
2184
|
+
const parent = this.fsw._getWatchedDir(dirname9);
|
|
2117
2185
|
let prevStats = stats;
|
|
2118
2186
|
if (parent.has(basename5))
|
|
2119
2187
|
return;
|
|
2120
|
-
const listener = async (
|
|
2188
|
+
const listener = async (path18, newStats) => {
|
|
2121
2189
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
2122
2190
|
return;
|
|
2123
2191
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -2131,16 +2199,16 @@ var NodeFsHandler = class {
|
|
|
2131
2199
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
2132
2200
|
}
|
|
2133
2201
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
2134
|
-
this.fsw._closeFile(
|
|
2202
|
+
this.fsw._closeFile(path18);
|
|
2135
2203
|
prevStats = newStats2;
|
|
2136
2204
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
2137
2205
|
if (closer2)
|
|
2138
|
-
this.fsw._addPathCloser(
|
|
2206
|
+
this.fsw._addPathCloser(path18, closer2);
|
|
2139
2207
|
} else {
|
|
2140
2208
|
prevStats = newStats2;
|
|
2141
2209
|
}
|
|
2142
2210
|
} catch (error) {
|
|
2143
|
-
this.fsw._remove(
|
|
2211
|
+
this.fsw._remove(dirname9, basename5);
|
|
2144
2212
|
}
|
|
2145
2213
|
} else if (parent.has(basename5)) {
|
|
2146
2214
|
const at = newStats.atimeMs;
|
|
@@ -2167,7 +2235,7 @@ var NodeFsHandler = class {
|
|
|
2167
2235
|
* @param item basename of this item
|
|
2168
2236
|
* @returns true if no more processing is needed for this entry.
|
|
2169
2237
|
*/
|
|
2170
|
-
async _handleSymlink(entry, directory,
|
|
2238
|
+
async _handleSymlink(entry, directory, path18, item) {
|
|
2171
2239
|
if (this.fsw.closed) {
|
|
2172
2240
|
return;
|
|
2173
2241
|
}
|
|
@@ -2177,7 +2245,7 @@ var NodeFsHandler = class {
|
|
|
2177
2245
|
this.fsw._incrReadyCount();
|
|
2178
2246
|
let linkPath;
|
|
2179
2247
|
try {
|
|
2180
|
-
linkPath = await (0, import_promises2.realpath)(
|
|
2248
|
+
linkPath = await (0, import_promises2.realpath)(path18);
|
|
2181
2249
|
} catch (e) {
|
|
2182
2250
|
this.fsw._emitReady();
|
|
2183
2251
|
return true;
|
|
@@ -2187,12 +2255,12 @@ var NodeFsHandler = class {
|
|
|
2187
2255
|
if (dir.has(item)) {
|
|
2188
2256
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
2189
2257
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
2190
|
-
this.fsw._emit(EV.CHANGE,
|
|
2258
|
+
this.fsw._emit(EV.CHANGE, path18, entry.stats);
|
|
2191
2259
|
}
|
|
2192
2260
|
} else {
|
|
2193
2261
|
dir.add(item);
|
|
2194
2262
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
2195
|
-
this.fsw._emit(EV.ADD,
|
|
2263
|
+
this.fsw._emit(EV.ADD, path18, entry.stats);
|
|
2196
2264
|
}
|
|
2197
2265
|
this.fsw._emitReady();
|
|
2198
2266
|
return true;
|
|
@@ -2222,9 +2290,9 @@ var NodeFsHandler = class {
|
|
|
2222
2290
|
return;
|
|
2223
2291
|
}
|
|
2224
2292
|
const item = entry.path;
|
|
2225
|
-
let
|
|
2293
|
+
let path18 = sp.join(directory, item);
|
|
2226
2294
|
current.add(item);
|
|
2227
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
2295
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path18, item)) {
|
|
2228
2296
|
return;
|
|
2229
2297
|
}
|
|
2230
2298
|
if (this.fsw.closed) {
|
|
@@ -2233,11 +2301,11 @@ var NodeFsHandler = class {
|
|
|
2233
2301
|
}
|
|
2234
2302
|
if (item === target || !target && !previous.has(item)) {
|
|
2235
2303
|
this.fsw._incrReadyCount();
|
|
2236
|
-
|
|
2237
|
-
this._addToNodeFs(
|
|
2304
|
+
path18 = sp.join(dir, sp.relative(dir, path18));
|
|
2305
|
+
this._addToNodeFs(path18, initialAdd, wh, depth + 1);
|
|
2238
2306
|
}
|
|
2239
2307
|
}).on(EV.ERROR, this._boundHandleError);
|
|
2240
|
-
return new Promise((
|
|
2308
|
+
return new Promise((resolve11, reject) => {
|
|
2241
2309
|
if (!stream)
|
|
2242
2310
|
return reject();
|
|
2243
2311
|
stream.once(STR_END, () => {
|
|
@@ -2246,7 +2314,7 @@ var NodeFsHandler = class {
|
|
|
2246
2314
|
return;
|
|
2247
2315
|
}
|
|
2248
2316
|
const wasThrottled = throttler ? throttler.clear() : false;
|
|
2249
|
-
|
|
2317
|
+
resolve11(void 0);
|
|
2250
2318
|
previous.getChildren().filter((item) => {
|
|
2251
2319
|
return item !== directory && !current.has(item);
|
|
2252
2320
|
}).forEach((item) => {
|
|
@@ -2303,13 +2371,13 @@ var NodeFsHandler = class {
|
|
|
2303
2371
|
* @param depth Child path actually targeted for watch
|
|
2304
2372
|
* @param target Child path actually targeted for watch
|
|
2305
2373
|
*/
|
|
2306
|
-
async _addToNodeFs(
|
|
2374
|
+
async _addToNodeFs(path18, initialAdd, priorWh, depth, target) {
|
|
2307
2375
|
const ready = this.fsw._emitReady;
|
|
2308
|
-
if (this.fsw._isIgnored(
|
|
2376
|
+
if (this.fsw._isIgnored(path18) || this.fsw.closed) {
|
|
2309
2377
|
ready();
|
|
2310
2378
|
return false;
|
|
2311
2379
|
}
|
|
2312
|
-
const wh = this.fsw._getWatchHelpers(
|
|
2380
|
+
const wh = this.fsw._getWatchHelpers(path18);
|
|
2313
2381
|
if (priorWh) {
|
|
2314
2382
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
2315
2383
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -2325,8 +2393,8 @@ var NodeFsHandler = class {
|
|
|
2325
2393
|
const follow = this.fsw.options.followSymlinks;
|
|
2326
2394
|
let closer;
|
|
2327
2395
|
if (stats.isDirectory()) {
|
|
2328
|
-
const absPath = sp.resolve(
|
|
2329
|
-
const targetPath = follow ? await (0, import_promises2.realpath)(
|
|
2396
|
+
const absPath = sp.resolve(path18);
|
|
2397
|
+
const targetPath = follow ? await (0, import_promises2.realpath)(path18) : path18;
|
|
2330
2398
|
if (this.fsw.closed)
|
|
2331
2399
|
return;
|
|
2332
2400
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -2336,29 +2404,29 @@ var NodeFsHandler = class {
|
|
|
2336
2404
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
2337
2405
|
}
|
|
2338
2406
|
} else if (stats.isSymbolicLink()) {
|
|
2339
|
-
const targetPath = follow ? await (0, import_promises2.realpath)(
|
|
2407
|
+
const targetPath = follow ? await (0, import_promises2.realpath)(path18) : path18;
|
|
2340
2408
|
if (this.fsw.closed)
|
|
2341
2409
|
return;
|
|
2342
2410
|
const parent = sp.dirname(wh.watchPath);
|
|
2343
2411
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
2344
2412
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
2345
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
2413
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path18, wh, targetPath);
|
|
2346
2414
|
if (this.fsw.closed)
|
|
2347
2415
|
return;
|
|
2348
2416
|
if (targetPath !== void 0) {
|
|
2349
|
-
this.fsw._symlinkPaths.set(sp.resolve(
|
|
2417
|
+
this.fsw._symlinkPaths.set(sp.resolve(path18), targetPath);
|
|
2350
2418
|
}
|
|
2351
2419
|
} else {
|
|
2352
2420
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
2353
2421
|
}
|
|
2354
2422
|
ready();
|
|
2355
2423
|
if (closer)
|
|
2356
|
-
this.fsw._addPathCloser(
|
|
2424
|
+
this.fsw._addPathCloser(path18, closer);
|
|
2357
2425
|
return false;
|
|
2358
2426
|
} catch (error) {
|
|
2359
2427
|
if (this.fsw._handleError(error)) {
|
|
2360
2428
|
ready();
|
|
2361
|
-
return
|
|
2429
|
+
return path18;
|
|
2362
2430
|
}
|
|
2363
2431
|
}
|
|
2364
2432
|
}
|
|
@@ -2401,24 +2469,24 @@ function createPattern(matcher) {
|
|
|
2401
2469
|
}
|
|
2402
2470
|
return () => false;
|
|
2403
2471
|
}
|
|
2404
|
-
function normalizePath(
|
|
2405
|
-
if (typeof
|
|
2472
|
+
function normalizePath(path18) {
|
|
2473
|
+
if (typeof path18 !== "string")
|
|
2406
2474
|
throw new Error("string expected");
|
|
2407
|
-
|
|
2408
|
-
|
|
2475
|
+
path18 = sp2.normalize(path18);
|
|
2476
|
+
path18 = path18.replace(/\\/g, "/");
|
|
2409
2477
|
let prepend = false;
|
|
2410
|
-
if (
|
|
2478
|
+
if (path18.startsWith("//"))
|
|
2411
2479
|
prepend = true;
|
|
2412
|
-
|
|
2480
|
+
path18 = path18.replace(DOUBLE_SLASH_RE, "/");
|
|
2413
2481
|
if (prepend)
|
|
2414
|
-
|
|
2415
|
-
return
|
|
2482
|
+
path18 = "/" + path18;
|
|
2483
|
+
return path18;
|
|
2416
2484
|
}
|
|
2417
2485
|
function matchPatterns(patterns, testString, stats) {
|
|
2418
|
-
const
|
|
2486
|
+
const path18 = normalizePath(testString);
|
|
2419
2487
|
for (let index = 0; index < patterns.length; index++) {
|
|
2420
2488
|
const pattern = patterns[index];
|
|
2421
|
-
if (pattern(
|
|
2489
|
+
if (pattern(path18, stats)) {
|
|
2422
2490
|
return true;
|
|
2423
2491
|
}
|
|
2424
2492
|
}
|
|
@@ -2456,19 +2524,19 @@ var toUnix = (string) => {
|
|
|
2456
2524
|
}
|
|
2457
2525
|
return str;
|
|
2458
2526
|
};
|
|
2459
|
-
var normalizePathToUnix = (
|
|
2460
|
-
var normalizeIgnored = (cwd = "") => (
|
|
2461
|
-
if (typeof
|
|
2462
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
2527
|
+
var normalizePathToUnix = (path18) => toUnix(sp2.normalize(toUnix(path18)));
|
|
2528
|
+
var normalizeIgnored = (cwd = "") => (path18) => {
|
|
2529
|
+
if (typeof path18 === "string") {
|
|
2530
|
+
return normalizePathToUnix(sp2.isAbsolute(path18) ? path18 : sp2.join(cwd, path18));
|
|
2463
2531
|
} else {
|
|
2464
|
-
return
|
|
2532
|
+
return path18;
|
|
2465
2533
|
}
|
|
2466
2534
|
};
|
|
2467
|
-
var getAbsolutePath = (
|
|
2468
|
-
if (sp2.isAbsolute(
|
|
2469
|
-
return
|
|
2535
|
+
var getAbsolutePath = (path18, cwd) => {
|
|
2536
|
+
if (sp2.isAbsolute(path18)) {
|
|
2537
|
+
return path18;
|
|
2470
2538
|
}
|
|
2471
|
-
return sp2.join(cwd,
|
|
2539
|
+
return sp2.join(cwd, path18);
|
|
2472
2540
|
};
|
|
2473
2541
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
2474
2542
|
var DirEntry = class {
|
|
@@ -2533,10 +2601,10 @@ var WatchHelper = class {
|
|
|
2533
2601
|
dirParts;
|
|
2534
2602
|
followSymlinks;
|
|
2535
2603
|
statMethod;
|
|
2536
|
-
constructor(
|
|
2604
|
+
constructor(path18, follow, fsw) {
|
|
2537
2605
|
this.fsw = fsw;
|
|
2538
|
-
const watchPath =
|
|
2539
|
-
this.path =
|
|
2606
|
+
const watchPath = path18;
|
|
2607
|
+
this.path = path18 = path18.replace(REPLACER_RE, "");
|
|
2540
2608
|
this.watchPath = watchPath;
|
|
2541
2609
|
this.fullWatchPath = sp2.resolve(watchPath);
|
|
2542
2610
|
this.dirParts = [];
|
|
@@ -2676,20 +2744,20 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
2676
2744
|
this._closePromise = void 0;
|
|
2677
2745
|
let paths = unifyPaths(paths_);
|
|
2678
2746
|
if (cwd) {
|
|
2679
|
-
paths = paths.map((
|
|
2680
|
-
const absPath = getAbsolutePath(
|
|
2747
|
+
paths = paths.map((path18) => {
|
|
2748
|
+
const absPath = getAbsolutePath(path18, cwd);
|
|
2681
2749
|
return absPath;
|
|
2682
2750
|
});
|
|
2683
2751
|
}
|
|
2684
|
-
paths.forEach((
|
|
2685
|
-
this._removeIgnoredPath(
|
|
2752
|
+
paths.forEach((path18) => {
|
|
2753
|
+
this._removeIgnoredPath(path18);
|
|
2686
2754
|
});
|
|
2687
2755
|
this._userIgnored = void 0;
|
|
2688
2756
|
if (!this._readyCount)
|
|
2689
2757
|
this._readyCount = 0;
|
|
2690
2758
|
this._readyCount += paths.length;
|
|
2691
|
-
Promise.all(paths.map(async (
|
|
2692
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
2759
|
+
Promise.all(paths.map(async (path18) => {
|
|
2760
|
+
const res = await this._nodeFsHandler._addToNodeFs(path18, !_internal, void 0, 0, _origAdd);
|
|
2693
2761
|
if (res)
|
|
2694
2762
|
this._emitReady();
|
|
2695
2763
|
return res;
|
|
@@ -2711,17 +2779,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
2711
2779
|
return this;
|
|
2712
2780
|
const paths = unifyPaths(paths_);
|
|
2713
2781
|
const { cwd } = this.options;
|
|
2714
|
-
paths.forEach((
|
|
2715
|
-
if (!sp2.isAbsolute(
|
|
2782
|
+
paths.forEach((path18) => {
|
|
2783
|
+
if (!sp2.isAbsolute(path18) && !this._closers.has(path18)) {
|
|
2716
2784
|
if (cwd)
|
|
2717
|
-
|
|
2718
|
-
|
|
2785
|
+
path18 = sp2.join(cwd, path18);
|
|
2786
|
+
path18 = sp2.resolve(path18);
|
|
2719
2787
|
}
|
|
2720
|
-
this._closePath(
|
|
2721
|
-
this._addIgnoredPath(
|
|
2722
|
-
if (this._watched.has(
|
|
2788
|
+
this._closePath(path18);
|
|
2789
|
+
this._addIgnoredPath(path18);
|
|
2790
|
+
if (this._watched.has(path18)) {
|
|
2723
2791
|
this._addIgnoredPath({
|
|
2724
|
-
path:
|
|
2792
|
+
path: path18,
|
|
2725
2793
|
recursive: true
|
|
2726
2794
|
});
|
|
2727
2795
|
}
|
|
@@ -2785,38 +2853,38 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
2785
2853
|
* @param stats arguments to be passed with event
|
|
2786
2854
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
2787
2855
|
*/
|
|
2788
|
-
async _emit(event,
|
|
2856
|
+
async _emit(event, path18, stats) {
|
|
2789
2857
|
if (this.closed)
|
|
2790
2858
|
return;
|
|
2791
2859
|
const opts = this.options;
|
|
2792
2860
|
if (isWindows)
|
|
2793
|
-
|
|
2861
|
+
path18 = sp2.normalize(path18);
|
|
2794
2862
|
if (opts.cwd)
|
|
2795
|
-
|
|
2796
|
-
const args = [
|
|
2863
|
+
path18 = sp2.relative(opts.cwd, path18);
|
|
2864
|
+
const args = [path18];
|
|
2797
2865
|
if (stats != null)
|
|
2798
2866
|
args.push(stats);
|
|
2799
2867
|
const awf = opts.awaitWriteFinish;
|
|
2800
2868
|
let pw;
|
|
2801
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
2869
|
+
if (awf && (pw = this._pendingWrites.get(path18))) {
|
|
2802
2870
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
2803
2871
|
return this;
|
|
2804
2872
|
}
|
|
2805
2873
|
if (opts.atomic) {
|
|
2806
2874
|
if (event === EVENTS.UNLINK) {
|
|
2807
|
-
this._pendingUnlinks.set(
|
|
2875
|
+
this._pendingUnlinks.set(path18, [event, ...args]);
|
|
2808
2876
|
setTimeout(() => {
|
|
2809
|
-
this._pendingUnlinks.forEach((entry,
|
|
2877
|
+
this._pendingUnlinks.forEach((entry, path19) => {
|
|
2810
2878
|
this.emit(...entry);
|
|
2811
2879
|
this.emit(EVENTS.ALL, ...entry);
|
|
2812
|
-
this._pendingUnlinks.delete(
|
|
2880
|
+
this._pendingUnlinks.delete(path19);
|
|
2813
2881
|
});
|
|
2814
2882
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
2815
2883
|
return this;
|
|
2816
2884
|
}
|
|
2817
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
2885
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path18)) {
|
|
2818
2886
|
event = EVENTS.CHANGE;
|
|
2819
|
-
this._pendingUnlinks.delete(
|
|
2887
|
+
this._pendingUnlinks.delete(path18);
|
|
2820
2888
|
}
|
|
2821
2889
|
}
|
|
2822
2890
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -2834,16 +2902,16 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
2834
2902
|
this.emitWithAll(event, args);
|
|
2835
2903
|
}
|
|
2836
2904
|
};
|
|
2837
|
-
this._awaitWriteFinish(
|
|
2905
|
+
this._awaitWriteFinish(path18, awf.stabilityThreshold, event, awfEmit);
|
|
2838
2906
|
return this;
|
|
2839
2907
|
}
|
|
2840
2908
|
if (event === EVENTS.CHANGE) {
|
|
2841
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
2909
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path18, 50);
|
|
2842
2910
|
if (isThrottled)
|
|
2843
2911
|
return this;
|
|
2844
2912
|
}
|
|
2845
2913
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
2846
|
-
const fullPath = opts.cwd ? sp2.join(opts.cwd,
|
|
2914
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path18) : path18;
|
|
2847
2915
|
let stats2;
|
|
2848
2916
|
try {
|
|
2849
2917
|
stats2 = await (0, import_promises3.stat)(fullPath);
|
|
@@ -2874,23 +2942,23 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
2874
2942
|
* @param timeout duration of time to suppress duplicate actions
|
|
2875
2943
|
* @returns tracking object or false if action should be suppressed
|
|
2876
2944
|
*/
|
|
2877
|
-
_throttle(actionType,
|
|
2945
|
+
_throttle(actionType, path18, timeout) {
|
|
2878
2946
|
if (!this._throttled.has(actionType)) {
|
|
2879
2947
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
2880
2948
|
}
|
|
2881
2949
|
const action = this._throttled.get(actionType);
|
|
2882
2950
|
if (!action)
|
|
2883
2951
|
throw new Error("invalid throttle");
|
|
2884
|
-
const actionPath = action.get(
|
|
2952
|
+
const actionPath = action.get(path18);
|
|
2885
2953
|
if (actionPath) {
|
|
2886
2954
|
actionPath.count++;
|
|
2887
2955
|
return false;
|
|
2888
2956
|
}
|
|
2889
2957
|
let timeoutObject;
|
|
2890
2958
|
const clear = () => {
|
|
2891
|
-
const item = action.get(
|
|
2959
|
+
const item = action.get(path18);
|
|
2892
2960
|
const count = item ? item.count : 0;
|
|
2893
|
-
action.delete(
|
|
2961
|
+
action.delete(path18);
|
|
2894
2962
|
clearTimeout(timeoutObject);
|
|
2895
2963
|
if (item)
|
|
2896
2964
|
clearTimeout(item.timeoutObject);
|
|
@@ -2898,7 +2966,7 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
2898
2966
|
};
|
|
2899
2967
|
timeoutObject = setTimeout(clear, timeout);
|
|
2900
2968
|
const thr = { timeoutObject, clear, count: 0 };
|
|
2901
|
-
action.set(
|
|
2969
|
+
action.set(path18, thr);
|
|
2902
2970
|
return thr;
|
|
2903
2971
|
}
|
|
2904
2972
|
_incrReadyCount() {
|
|
@@ -2912,44 +2980,44 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
2912
2980
|
* @param event
|
|
2913
2981
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
2914
2982
|
*/
|
|
2915
|
-
_awaitWriteFinish(
|
|
2983
|
+
_awaitWriteFinish(path18, threshold, event, awfEmit) {
|
|
2916
2984
|
const awf = this.options.awaitWriteFinish;
|
|
2917
2985
|
if (typeof awf !== "object")
|
|
2918
2986
|
return;
|
|
2919
2987
|
const pollInterval = awf.pollInterval;
|
|
2920
2988
|
let timeoutHandler;
|
|
2921
|
-
let fullPath =
|
|
2922
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
2923
|
-
fullPath = sp2.join(this.options.cwd,
|
|
2989
|
+
let fullPath = path18;
|
|
2990
|
+
if (this.options.cwd && !sp2.isAbsolute(path18)) {
|
|
2991
|
+
fullPath = sp2.join(this.options.cwd, path18);
|
|
2924
2992
|
}
|
|
2925
2993
|
const now = /* @__PURE__ */ new Date();
|
|
2926
2994
|
const writes = this._pendingWrites;
|
|
2927
2995
|
function awaitWriteFinishFn(prevStat) {
|
|
2928
2996
|
(0, import_node_fs2.stat)(fullPath, (err, curStat) => {
|
|
2929
|
-
if (err || !writes.has(
|
|
2997
|
+
if (err || !writes.has(path18)) {
|
|
2930
2998
|
if (err && err.code !== "ENOENT")
|
|
2931
2999
|
awfEmit(err);
|
|
2932
3000
|
return;
|
|
2933
3001
|
}
|
|
2934
3002
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
2935
3003
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
2936
|
-
writes.get(
|
|
3004
|
+
writes.get(path18).lastChange = now2;
|
|
2937
3005
|
}
|
|
2938
|
-
const pw = writes.get(
|
|
3006
|
+
const pw = writes.get(path18);
|
|
2939
3007
|
const df = now2 - pw.lastChange;
|
|
2940
3008
|
if (df >= threshold) {
|
|
2941
|
-
writes.delete(
|
|
3009
|
+
writes.delete(path18);
|
|
2942
3010
|
awfEmit(void 0, curStat);
|
|
2943
3011
|
} else {
|
|
2944
3012
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
2945
3013
|
}
|
|
2946
3014
|
});
|
|
2947
3015
|
}
|
|
2948
|
-
if (!writes.has(
|
|
2949
|
-
writes.set(
|
|
3016
|
+
if (!writes.has(path18)) {
|
|
3017
|
+
writes.set(path18, {
|
|
2950
3018
|
lastChange: now,
|
|
2951
3019
|
cancelWait: () => {
|
|
2952
|
-
writes.delete(
|
|
3020
|
+
writes.delete(path18);
|
|
2953
3021
|
clearTimeout(timeoutHandler);
|
|
2954
3022
|
return event;
|
|
2955
3023
|
}
|
|
@@ -2960,8 +3028,8 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
2960
3028
|
/**
|
|
2961
3029
|
* Determines whether user has asked to ignore this path.
|
|
2962
3030
|
*/
|
|
2963
|
-
_isIgnored(
|
|
2964
|
-
if (this.options.atomic && DOT_RE.test(
|
|
3031
|
+
_isIgnored(path18, stats) {
|
|
3032
|
+
if (this.options.atomic && DOT_RE.test(path18))
|
|
2965
3033
|
return true;
|
|
2966
3034
|
if (!this._userIgnored) {
|
|
2967
3035
|
const { cwd } = this.options;
|
|
@@ -2971,17 +3039,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
2971
3039
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
2972
3040
|
this._userIgnored = anymatch(list, void 0);
|
|
2973
3041
|
}
|
|
2974
|
-
return this._userIgnored(
|
|
3042
|
+
return this._userIgnored(path18, stats);
|
|
2975
3043
|
}
|
|
2976
|
-
_isntIgnored(
|
|
2977
|
-
return !this._isIgnored(
|
|
3044
|
+
_isntIgnored(path18, stat4) {
|
|
3045
|
+
return !this._isIgnored(path18, stat4);
|
|
2978
3046
|
}
|
|
2979
3047
|
/**
|
|
2980
3048
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
2981
3049
|
* @param path file or directory pattern being watched
|
|
2982
3050
|
*/
|
|
2983
|
-
_getWatchHelpers(
|
|
2984
|
-
return new WatchHelper(
|
|
3051
|
+
_getWatchHelpers(path18) {
|
|
3052
|
+
return new WatchHelper(path18, this.options.followSymlinks, this);
|
|
2985
3053
|
}
|
|
2986
3054
|
// Directory helpers
|
|
2987
3055
|
// -----------------
|
|
@@ -3013,63 +3081,63 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
3013
3081
|
* @param item base path of item/directory
|
|
3014
3082
|
*/
|
|
3015
3083
|
_remove(directory, item, isDirectory) {
|
|
3016
|
-
const
|
|
3017
|
-
const fullPath = sp2.resolve(
|
|
3018
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
3019
|
-
if (!this._throttle("remove",
|
|
3084
|
+
const path18 = sp2.join(directory, item);
|
|
3085
|
+
const fullPath = sp2.resolve(path18);
|
|
3086
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path18) || this._watched.has(fullPath);
|
|
3087
|
+
if (!this._throttle("remove", path18, 100))
|
|
3020
3088
|
return;
|
|
3021
3089
|
if (!isDirectory && this._watched.size === 1) {
|
|
3022
3090
|
this.add(directory, item, true);
|
|
3023
3091
|
}
|
|
3024
|
-
const wp = this._getWatchedDir(
|
|
3092
|
+
const wp = this._getWatchedDir(path18);
|
|
3025
3093
|
const nestedDirectoryChildren = wp.getChildren();
|
|
3026
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
3094
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path18, nested));
|
|
3027
3095
|
const parent = this._getWatchedDir(directory);
|
|
3028
3096
|
const wasTracked = parent.has(item);
|
|
3029
3097
|
parent.remove(item);
|
|
3030
3098
|
if (this._symlinkPaths.has(fullPath)) {
|
|
3031
3099
|
this._symlinkPaths.delete(fullPath);
|
|
3032
3100
|
}
|
|
3033
|
-
let relPath =
|
|
3101
|
+
let relPath = path18;
|
|
3034
3102
|
if (this.options.cwd)
|
|
3035
|
-
relPath = sp2.relative(this.options.cwd,
|
|
3103
|
+
relPath = sp2.relative(this.options.cwd, path18);
|
|
3036
3104
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
3037
3105
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
3038
3106
|
if (event === EVENTS.ADD)
|
|
3039
3107
|
return;
|
|
3040
3108
|
}
|
|
3041
|
-
this._watched.delete(
|
|
3109
|
+
this._watched.delete(path18);
|
|
3042
3110
|
this._watched.delete(fullPath);
|
|
3043
3111
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
3044
|
-
if (wasTracked && !this._isIgnored(
|
|
3045
|
-
this._emit(eventName,
|
|
3046
|
-
this._closePath(
|
|
3112
|
+
if (wasTracked && !this._isIgnored(path18))
|
|
3113
|
+
this._emit(eventName, path18);
|
|
3114
|
+
this._closePath(path18);
|
|
3047
3115
|
}
|
|
3048
3116
|
/**
|
|
3049
3117
|
* Closes all watchers for a path
|
|
3050
3118
|
*/
|
|
3051
|
-
_closePath(
|
|
3052
|
-
this._closeFile(
|
|
3053
|
-
const dir = sp2.dirname(
|
|
3054
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
3119
|
+
_closePath(path18) {
|
|
3120
|
+
this._closeFile(path18);
|
|
3121
|
+
const dir = sp2.dirname(path18);
|
|
3122
|
+
this._getWatchedDir(dir).remove(sp2.basename(path18));
|
|
3055
3123
|
}
|
|
3056
3124
|
/**
|
|
3057
3125
|
* Closes only file-specific watchers
|
|
3058
3126
|
*/
|
|
3059
|
-
_closeFile(
|
|
3060
|
-
const closers = this._closers.get(
|
|
3127
|
+
_closeFile(path18) {
|
|
3128
|
+
const closers = this._closers.get(path18);
|
|
3061
3129
|
if (!closers)
|
|
3062
3130
|
return;
|
|
3063
3131
|
closers.forEach((closer) => closer());
|
|
3064
|
-
this._closers.delete(
|
|
3132
|
+
this._closers.delete(path18);
|
|
3065
3133
|
}
|
|
3066
|
-
_addPathCloser(
|
|
3134
|
+
_addPathCloser(path18, closer) {
|
|
3067
3135
|
if (!closer)
|
|
3068
3136
|
return;
|
|
3069
|
-
let list = this._closers.get(
|
|
3137
|
+
let list = this._closers.get(path18);
|
|
3070
3138
|
if (!list) {
|
|
3071
3139
|
list = [];
|
|
3072
|
-
this._closers.set(
|
|
3140
|
+
this._closers.set(path18, list);
|
|
3073
3141
|
}
|
|
3074
3142
|
list.push(closer);
|
|
3075
3143
|
}
|
|
@@ -3098,13 +3166,13 @@ function watch(paths, options = {}) {
|
|
|
3098
3166
|
}
|
|
3099
3167
|
var chokidar_default = { watch, FSWatcher };
|
|
3100
3168
|
|
|
3101
|
-
// src/watcher/
|
|
3102
|
-
var
|
|
3169
|
+
// src/watcher/file-watcher.ts
|
|
3170
|
+
var path8 = __toESM(require("path"), 1);
|
|
3103
3171
|
|
|
3104
3172
|
// src/utils/files.ts
|
|
3105
3173
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
3106
|
-
var
|
|
3107
|
-
var
|
|
3174
|
+
var import_fs5 = require("fs");
|
|
3175
|
+
var path7 = __toESM(require("path"), 1);
|
|
3108
3176
|
var PROJECT_MARKERS = [
|
|
3109
3177
|
".git",
|
|
3110
3178
|
"package.json",
|
|
@@ -3123,7 +3191,7 @@ var PROJECT_MARKERS = [
|
|
|
3123
3191
|
];
|
|
3124
3192
|
function hasProjectMarker(projectRoot) {
|
|
3125
3193
|
for (const marker of PROJECT_MARKERS) {
|
|
3126
|
-
if ((0,
|
|
3194
|
+
if ((0, import_fs5.existsSync)(path7.join(projectRoot, marker))) {
|
|
3127
3195
|
return true;
|
|
3128
3196
|
}
|
|
3129
3197
|
}
|
|
@@ -3149,23 +3217,17 @@ function createIgnoreFilter(projectRoot) {
|
|
|
3149
3217
|
"**/*build*/**"
|
|
3150
3218
|
];
|
|
3151
3219
|
ig.add(defaultIgnores);
|
|
3152
|
-
const gitignorePath =
|
|
3153
|
-
if ((0,
|
|
3154
|
-
const gitignoreContent = (0,
|
|
3220
|
+
const gitignorePath = path7.join(projectRoot, ".gitignore");
|
|
3221
|
+
if ((0, import_fs5.existsSync)(gitignorePath)) {
|
|
3222
|
+
const gitignoreContent = (0, import_fs5.readFileSync)(gitignorePath, "utf-8");
|
|
3155
3223
|
ig.add(gitignoreContent);
|
|
3156
3224
|
}
|
|
3157
3225
|
return ig;
|
|
3158
3226
|
}
|
|
3159
3227
|
function shouldIncludeFile(filePath, projectRoot, includePatterns, excludePatterns, ignoreFilter) {
|
|
3160
|
-
const relativePath =
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
if (part.startsWith(".") && part !== "." && part !== "..") {
|
|
3164
|
-
return false;
|
|
3165
|
-
}
|
|
3166
|
-
if (part.toLowerCase().includes("build")) {
|
|
3167
|
-
return false;
|
|
3168
|
-
}
|
|
3228
|
+
const relativePath = path7.relative(projectRoot, filePath);
|
|
3229
|
+
if (hasFilteredPathSegment(relativePath, path7.sep)) {
|
|
3230
|
+
return false;
|
|
3169
3231
|
}
|
|
3170
3232
|
if (ignoreFilter.ignores(relativePath)) {
|
|
3171
3233
|
return false;
|
|
@@ -3198,19 +3260,19 @@ function matchGlob(filePath, pattern) {
|
|
|
3198
3260
|
return regex.test(filePath);
|
|
3199
3261
|
}
|
|
3200
3262
|
async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns, ignoreFilter, maxFileSize, skipped, options, currentDepth = 0) {
|
|
3201
|
-
const entries = await
|
|
3263
|
+
const entries = await import_fs5.promises.readdir(dir, { withFileTypes: true });
|
|
3202
3264
|
const filesInDir = [];
|
|
3203
3265
|
const subdirs = [];
|
|
3204
3266
|
for (const entry of entries) {
|
|
3205
|
-
const fullPath =
|
|
3206
|
-
const relativePath =
|
|
3207
|
-
if (
|
|
3267
|
+
const fullPath = path7.join(dir, entry.name);
|
|
3268
|
+
const relativePath = path7.relative(projectRoot, fullPath);
|
|
3269
|
+
if (isHiddenPathSegment(entry.name)) {
|
|
3208
3270
|
if (entry.isDirectory()) {
|
|
3209
3271
|
skipped.push({ path: relativePath, reason: "excluded" });
|
|
3210
3272
|
}
|
|
3211
3273
|
continue;
|
|
3212
3274
|
}
|
|
3213
|
-
if (entry.isDirectory() && entry.name
|
|
3275
|
+
if (entry.isDirectory() && isBuildPathSegment(entry.name)) {
|
|
3214
3276
|
skipped.push({ path: relativePath, reason: "excluded" });
|
|
3215
3277
|
continue;
|
|
3216
3278
|
}
|
|
@@ -3223,7 +3285,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
|
|
|
3223
3285
|
if (entry.isDirectory()) {
|
|
3224
3286
|
subdirs.push({ fullPath, relativePath });
|
|
3225
3287
|
} else if (entry.isFile()) {
|
|
3226
|
-
const stat4 = await
|
|
3288
|
+
const stat4 = await import_fs5.promises.stat(fullPath);
|
|
3227
3289
|
if (stat4.size > maxFileSize) {
|
|
3228
3290
|
skipped.push({ path: relativePath, reason: "too_large" });
|
|
3229
3291
|
continue;
|
|
@@ -3252,7 +3314,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
|
|
|
3252
3314
|
yield f;
|
|
3253
3315
|
}
|
|
3254
3316
|
for (let i = options.maxFilesPerDirectory; i < filesInDir.length; i++) {
|
|
3255
|
-
skipped.push({ path:
|
|
3317
|
+
skipped.push({ path: path7.relative(projectRoot, filesInDir[i].path), reason: "excluded" });
|
|
3256
3318
|
}
|
|
3257
3319
|
const canRecurse = options.maxDepth === -1 || currentDepth < options.maxDepth;
|
|
3258
3320
|
if (canRecurse) {
|
|
@@ -3292,14 +3354,14 @@ async function collectFiles(projectRoot, includePatterns, excludePatterns, maxFi
|
|
|
3292
3354
|
if (additionalRoots && additionalRoots.length > 0) {
|
|
3293
3355
|
const normalizedRoots = /* @__PURE__ */ new Set();
|
|
3294
3356
|
for (const kbRoot of additionalRoots) {
|
|
3295
|
-
const resolved =
|
|
3296
|
-
|
|
3357
|
+
const resolved = path7.normalize(
|
|
3358
|
+
path7.isAbsolute(kbRoot) ? kbRoot : path7.resolve(projectRoot, kbRoot)
|
|
3297
3359
|
);
|
|
3298
3360
|
normalizedRoots.add(resolved);
|
|
3299
3361
|
}
|
|
3300
3362
|
for (const resolvedKbRoot of normalizedRoots) {
|
|
3301
3363
|
try {
|
|
3302
|
-
const stat4 = await
|
|
3364
|
+
const stat4 = await import_fs5.promises.stat(resolvedKbRoot);
|
|
3303
3365
|
if (!stat4.isDirectory()) {
|
|
3304
3366
|
skipped.push({ path: resolvedKbRoot, reason: "excluded" });
|
|
3305
3367
|
continue;
|
|
@@ -3326,7 +3388,7 @@ async function collectFiles(projectRoot, includePatterns, excludePatterns, maxFi
|
|
|
3326
3388
|
return { files, skipped };
|
|
3327
3389
|
}
|
|
3328
3390
|
|
|
3329
|
-
// src/watcher/
|
|
3391
|
+
// src/watcher/file-watcher.ts
|
|
3330
3392
|
var FileWatcher = class {
|
|
3331
3393
|
watcher = null;
|
|
3332
3394
|
projectRoot;
|
|
@@ -3347,16 +3409,10 @@ var FileWatcher = class {
|
|
|
3347
3409
|
const ignoreFilter = createIgnoreFilter(this.projectRoot);
|
|
3348
3410
|
this.watcher = chokidar_default.watch(this.projectRoot, {
|
|
3349
3411
|
ignored: (filePath) => {
|
|
3350
|
-
const relativePath =
|
|
3412
|
+
const relativePath = path8.relative(this.projectRoot, filePath);
|
|
3351
3413
|
if (!relativePath) return false;
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
if (part.startsWith(".") && part !== "." && part !== "..") {
|
|
3355
|
-
return true;
|
|
3356
|
-
}
|
|
3357
|
-
if (part.toLowerCase().includes("build")) {
|
|
3358
|
-
return true;
|
|
3359
|
-
}
|
|
3414
|
+
if (hasFilteredPathSegment(relativePath, path8.sep)) {
|
|
3415
|
+
return true;
|
|
3360
3416
|
}
|
|
3361
3417
|
if (ignoreFilter.ignores(relativePath)) {
|
|
3362
3418
|
return true;
|
|
@@ -3401,7 +3457,7 @@ var FileWatcher = class {
|
|
|
3401
3457
|
return;
|
|
3402
3458
|
}
|
|
3403
3459
|
const changes = Array.from(this.pendingChanges.entries()).map(
|
|
3404
|
-
([
|
|
3460
|
+
([path18, type]) => ({ path: path18, type })
|
|
3405
3461
|
);
|
|
3406
3462
|
this.pendingChanges.clear();
|
|
3407
3463
|
try {
|
|
@@ -3426,6 +3482,9 @@ var FileWatcher = class {
|
|
|
3426
3482
|
return this.watcher !== null;
|
|
3427
3483
|
}
|
|
3428
3484
|
};
|
|
3485
|
+
|
|
3486
|
+
// src/watcher/git-head-watcher.ts
|
|
3487
|
+
var path9 = __toESM(require("path"), 1);
|
|
3429
3488
|
var GitHeadWatcher = class {
|
|
3430
3489
|
watcher = null;
|
|
3431
3490
|
projectRoot;
|
|
@@ -3447,7 +3506,7 @@ var GitHeadWatcher = class {
|
|
|
3447
3506
|
this.onBranchChange = handler;
|
|
3448
3507
|
this.currentBranch = getCurrentBranch(this.projectRoot);
|
|
3449
3508
|
const headPath = getHeadPath(this.projectRoot);
|
|
3450
|
-
const refsPath =
|
|
3509
|
+
const refsPath = path9.join(this.projectRoot, ".git", "refs", "heads");
|
|
3451
3510
|
this.watcher = chokidar_default.watch([headPath, refsPath], {
|
|
3452
3511
|
persistent: true,
|
|
3453
3512
|
ignoreInitial: true,
|
|
@@ -3499,6 +3558,8 @@ var GitHeadWatcher = class {
|
|
|
3499
3558
|
return this.watcher !== null;
|
|
3500
3559
|
}
|
|
3501
3560
|
};
|
|
3561
|
+
|
|
3562
|
+
// src/watcher/index.ts
|
|
3502
3563
|
function createWatcherWithIndexer(getIndexer2, projectRoot, config) {
|
|
3503
3564
|
const fileWatcher = new FileWatcher(projectRoot, config);
|
|
3504
3565
|
fileWatcher.start(async (changes) => {
|
|
@@ -3532,8 +3593,8 @@ function createWatcherWithIndexer(getIndexer2, projectRoot, config) {
|
|
|
3532
3593
|
var import_plugin = require("@opencode-ai/plugin");
|
|
3533
3594
|
|
|
3534
3595
|
// src/indexer/index.ts
|
|
3535
|
-
var
|
|
3536
|
-
var
|
|
3596
|
+
var import_fs7 = require("fs");
|
|
3597
|
+
var path12 = __toESM(require("path"), 1);
|
|
3537
3598
|
var import_perf_hooks = require("perf_hooks");
|
|
3538
3599
|
|
|
3539
3600
|
// node_modules/eventemitter3/index.mjs
|
|
@@ -3558,7 +3619,7 @@ function pTimeout(promise, options) {
|
|
|
3558
3619
|
} = options;
|
|
3559
3620
|
let timer;
|
|
3560
3621
|
let abortHandler;
|
|
3561
|
-
const wrappedPromise = new Promise((
|
|
3622
|
+
const wrappedPromise = new Promise((resolve11, reject) => {
|
|
3562
3623
|
if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
|
|
3563
3624
|
throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
|
|
3564
3625
|
}
|
|
@@ -3572,7 +3633,7 @@ function pTimeout(promise, options) {
|
|
|
3572
3633
|
};
|
|
3573
3634
|
signal.addEventListener("abort", abortHandler, { once: true });
|
|
3574
3635
|
}
|
|
3575
|
-
promise.then(
|
|
3636
|
+
promise.then(resolve11, reject);
|
|
3576
3637
|
if (milliseconds === Number.POSITIVE_INFINITY) {
|
|
3577
3638
|
return;
|
|
3578
3639
|
}
|
|
@@ -3580,7 +3641,7 @@ function pTimeout(promise, options) {
|
|
|
3580
3641
|
timer = customTimers.setTimeout.call(void 0, () => {
|
|
3581
3642
|
if (fallback) {
|
|
3582
3643
|
try {
|
|
3583
|
-
|
|
3644
|
+
resolve11(fallback());
|
|
3584
3645
|
} catch (error) {
|
|
3585
3646
|
reject(error);
|
|
3586
3647
|
}
|
|
@@ -3590,7 +3651,7 @@ function pTimeout(promise, options) {
|
|
|
3590
3651
|
promise.cancel();
|
|
3591
3652
|
}
|
|
3592
3653
|
if (message === false) {
|
|
3593
|
-
|
|
3654
|
+
resolve11();
|
|
3594
3655
|
} else if (message instanceof Error) {
|
|
3595
3656
|
reject(message);
|
|
3596
3657
|
} else {
|
|
@@ -3992,7 +4053,7 @@ var PQueue = class extends import_index.default {
|
|
|
3992
4053
|
// Assign unique ID if not provided
|
|
3993
4054
|
id: options.id ?? (this.#idAssigner++).toString()
|
|
3994
4055
|
};
|
|
3995
|
-
return new Promise((
|
|
4056
|
+
return new Promise((resolve11, reject) => {
|
|
3996
4057
|
const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
|
|
3997
4058
|
let cleanupQueueAbortHandler = () => void 0;
|
|
3998
4059
|
const run = async () => {
|
|
@@ -4032,7 +4093,7 @@ var PQueue = class extends import_index.default {
|
|
|
4032
4093
|
})]);
|
|
4033
4094
|
}
|
|
4034
4095
|
const result = await operation;
|
|
4035
|
-
|
|
4096
|
+
resolve11(result);
|
|
4036
4097
|
this.emit("completed", result);
|
|
4037
4098
|
} catch (error) {
|
|
4038
4099
|
reject(error);
|
|
@@ -4220,13 +4281,13 @@ var PQueue = class extends import_index.default {
|
|
|
4220
4281
|
});
|
|
4221
4282
|
}
|
|
4222
4283
|
async #onEvent(event, filter) {
|
|
4223
|
-
return new Promise((
|
|
4284
|
+
return new Promise((resolve11) => {
|
|
4224
4285
|
const listener = () => {
|
|
4225
4286
|
if (filter && !filter()) {
|
|
4226
4287
|
return;
|
|
4227
4288
|
}
|
|
4228
4289
|
this.off(event, listener);
|
|
4229
|
-
|
|
4290
|
+
resolve11();
|
|
4230
4291
|
};
|
|
4231
4292
|
this.on(event, listener);
|
|
4232
4293
|
});
|
|
@@ -4512,7 +4573,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
|
|
|
4512
4573
|
const finalDelay = Math.min(delayTime, remainingTime);
|
|
4513
4574
|
options.signal?.throwIfAborted();
|
|
4514
4575
|
if (finalDelay > 0) {
|
|
4515
|
-
await new Promise((
|
|
4576
|
+
await new Promise((resolve11, reject) => {
|
|
4516
4577
|
const onAbort = () => {
|
|
4517
4578
|
clearTimeout(timeoutToken);
|
|
4518
4579
|
options.signal?.removeEventListener("abort", onAbort);
|
|
@@ -4520,7 +4581,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
|
|
|
4520
4581
|
};
|
|
4521
4582
|
const timeoutToken = setTimeout(() => {
|
|
4522
4583
|
options.signal?.removeEventListener("abort", onAbort);
|
|
4523
|
-
|
|
4584
|
+
resolve11();
|
|
4524
4585
|
}, finalDelay);
|
|
4525
4586
|
if (options.unref) {
|
|
4526
4587
|
timeoutToken.unref?.();
|
|
@@ -4581,17 +4642,17 @@ async function pRetry(input, options = {}) {
|
|
|
4581
4642
|
}
|
|
4582
4643
|
|
|
4583
4644
|
// src/embeddings/detector.ts
|
|
4584
|
-
var
|
|
4585
|
-
var
|
|
4645
|
+
var import_fs6 = require("fs");
|
|
4646
|
+
var path10 = __toESM(require("path"), 1);
|
|
4586
4647
|
var os3 = __toESM(require("os"), 1);
|
|
4587
4648
|
function getOpenCodeAuthPath() {
|
|
4588
|
-
return
|
|
4649
|
+
return path10.join(os3.homedir(), ".local", "share", "opencode", "auth.json");
|
|
4589
4650
|
}
|
|
4590
4651
|
function loadOpenCodeAuth() {
|
|
4591
4652
|
const authPath = getOpenCodeAuthPath();
|
|
4592
4653
|
try {
|
|
4593
|
-
if ((0,
|
|
4594
|
-
return JSON.parse((0,
|
|
4654
|
+
if ((0, import_fs6.existsSync)(authPath)) {
|
|
4655
|
+
return JSON.parse((0, import_fs6.readFileSync)(authPath, "utf-8"));
|
|
4595
4656
|
}
|
|
4596
4657
|
} catch {
|
|
4597
4658
|
}
|
|
@@ -4658,7 +4719,8 @@ function getGitHubCopilotCredentials() {
|
|
|
4658
4719
|
if (!copilotAuth || copilotAuth.type !== "oauth") {
|
|
4659
4720
|
return null;
|
|
4660
4721
|
}
|
|
4661
|
-
const
|
|
4722
|
+
const auth = copilotAuth;
|
|
4723
|
+
const baseUrl = auth.enterpriseUrl ? `https://copilot-api.${auth.enterpriseUrl.replace(/^https?:\/\//, "").replace(/\/$/, "")}` : "https://models.github.ai";
|
|
4662
4724
|
return {
|
|
4663
4725
|
provider: "github-copilot",
|
|
4664
4726
|
baseUrl,
|
|
@@ -4754,42 +4816,12 @@ function createCustomProviderInfo(config) {
|
|
|
4754
4816
|
};
|
|
4755
4817
|
}
|
|
4756
4818
|
|
|
4757
|
-
// src/embeddings/provider.ts
|
|
4758
|
-
var
|
|
4759
|
-
constructor(message) {
|
|
4760
|
-
super(message);
|
|
4761
|
-
this.name = "CustomProviderNonRetryableError";
|
|
4762
|
-
}
|
|
4763
|
-
};
|
|
4764
|
-
function createEmbeddingProvider(configuredProviderInfo) {
|
|
4765
|
-
switch (configuredProviderInfo.provider) {
|
|
4766
|
-
case "github-copilot":
|
|
4767
|
-
return new GitHubCopilotEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
4768
|
-
case "openai":
|
|
4769
|
-
return new OpenAIEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
4770
|
-
case "google":
|
|
4771
|
-
return new GoogleEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
4772
|
-
case "ollama":
|
|
4773
|
-
return new OllamaEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
4774
|
-
case "custom":
|
|
4775
|
-
return new CustomEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
4776
|
-
default: {
|
|
4777
|
-
const _exhaustive = configuredProviderInfo;
|
|
4778
|
-
throw new Error(`Unsupported embedding provider: ${_exhaustive.provider}`);
|
|
4779
|
-
}
|
|
4780
|
-
}
|
|
4781
|
-
}
|
|
4782
|
-
var GitHubCopilotEmbeddingProvider = class {
|
|
4819
|
+
// src/embeddings/provider-types.ts
|
|
4820
|
+
var BaseEmbeddingProvider = class {
|
|
4783
4821
|
constructor(credentials, modelInfo) {
|
|
4784
4822
|
this.credentials = credentials;
|
|
4785
4823
|
this.modelInfo = modelInfo;
|
|
4786
4824
|
}
|
|
4787
|
-
getToken() {
|
|
4788
|
-
if (!this.credentials.refreshToken) {
|
|
4789
|
-
throw new Error("No OAuth token available for GitHub");
|
|
4790
|
-
}
|
|
4791
|
-
return this.credentials.refreshToken;
|
|
4792
|
-
}
|
|
4793
4825
|
async embedQuery(query) {
|
|
4794
4826
|
const result = await this.embedBatch([query]);
|
|
4795
4827
|
return {
|
|
@@ -4804,69 +4836,204 @@ var GitHubCopilotEmbeddingProvider = class {
|
|
|
4804
4836
|
tokensUsed: result.totalTokensUsed
|
|
4805
4837
|
};
|
|
4806
4838
|
}
|
|
4807
|
-
async embedBatch(texts) {
|
|
4808
|
-
const token = this.getToken();
|
|
4809
|
-
const response = await fetch(`${this.credentials.baseUrl}/inference/embeddings`, {
|
|
4810
|
-
method: "POST",
|
|
4811
|
-
headers: {
|
|
4812
|
-
Authorization: `Bearer ${token}`,
|
|
4813
|
-
"Content-Type": "application/json",
|
|
4814
|
-
Accept: "application/vnd.github+json",
|
|
4815
|
-
"X-GitHub-Api-Version": "2022-11-28"
|
|
4816
|
-
},
|
|
4817
|
-
body: JSON.stringify({
|
|
4818
|
-
model: `openai/${this.modelInfo.model}`,
|
|
4819
|
-
input: texts
|
|
4820
|
-
})
|
|
4821
|
-
});
|
|
4822
|
-
if (!response.ok) {
|
|
4823
|
-
const error = await response.text();
|
|
4824
|
-
throw new Error(`GitHub Copilot embedding API error: ${response.status} - ${error}`);
|
|
4825
|
-
}
|
|
4826
|
-
const data = await response.json();
|
|
4827
|
-
return {
|
|
4828
|
-
embeddings: data.data.map((d) => d.embedding),
|
|
4829
|
-
totalTokensUsed: data.usage.total_tokens
|
|
4830
|
-
};
|
|
4831
|
-
}
|
|
4832
4839
|
getModelInfo() {
|
|
4833
4840
|
return this.modelInfo;
|
|
4834
4841
|
}
|
|
4835
4842
|
};
|
|
4836
|
-
var
|
|
4843
|
+
var CustomProviderNonRetryableError = class extends Error {
|
|
4844
|
+
constructor(message) {
|
|
4845
|
+
super(message);
|
|
4846
|
+
this.name = "CustomProviderNonRetryableError";
|
|
4847
|
+
}
|
|
4848
|
+
};
|
|
4849
|
+
|
|
4850
|
+
// src/utils/url-validation.ts
|
|
4851
|
+
var BLOCKED_METADATA_IPS = [
|
|
4852
|
+
/^169\.254\.169\.254$/,
|
|
4853
|
+
// AWS/Azure/GCP metadata
|
|
4854
|
+
/^169\.254\.170\.2$/,
|
|
4855
|
+
// AWS ECS task metadata
|
|
4856
|
+
/^fd00:ec2::254$/
|
|
4857
|
+
// AWS IMDSv2 IPv6
|
|
4858
|
+
];
|
|
4859
|
+
var BLOCKED_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
4860
|
+
"metadata.google.internal",
|
|
4861
|
+
"metadata.google",
|
|
4862
|
+
"metadata.goog",
|
|
4863
|
+
"kubernetes.default.svc"
|
|
4864
|
+
]);
|
|
4865
|
+
function validateExternalUrl(urlString) {
|
|
4866
|
+
let parsed;
|
|
4867
|
+
try {
|
|
4868
|
+
parsed = new URL(urlString);
|
|
4869
|
+
} catch {
|
|
4870
|
+
return { valid: false, reason: `Invalid URL: ${sanitizeUrlForError(urlString)}` };
|
|
4871
|
+
}
|
|
4872
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
4873
|
+
return { valid: false, reason: `Blocked protocol: ${parsed.protocol}` };
|
|
4874
|
+
}
|
|
4875
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
4876
|
+
if (BLOCKED_HOSTNAMES.has(hostname)) {
|
|
4877
|
+
return { valid: false, reason: `Blocked: cloud metadata service (${hostname})` };
|
|
4878
|
+
}
|
|
4879
|
+
for (const pattern of BLOCKED_METADATA_IPS) {
|
|
4880
|
+
if (pattern.test(hostname)) {
|
|
4881
|
+
return { valid: false, reason: `Blocked: cloud metadata IP (${hostname})` };
|
|
4882
|
+
}
|
|
4883
|
+
}
|
|
4884
|
+
if (/^169\.254\./.test(hostname)) {
|
|
4885
|
+
return { valid: false, reason: `Blocked: link-local address (${hostname})` };
|
|
4886
|
+
}
|
|
4887
|
+
return { valid: true };
|
|
4888
|
+
}
|
|
4889
|
+
function sanitizeUrlForError(url) {
|
|
4890
|
+
try {
|
|
4891
|
+
const parsed = new URL(url);
|
|
4892
|
+
parsed.username = "";
|
|
4893
|
+
parsed.password = "";
|
|
4894
|
+
return parsed.toString();
|
|
4895
|
+
} catch {
|
|
4896
|
+
const maxLen = 80;
|
|
4897
|
+
if (url.length > maxLen) {
|
|
4898
|
+
return url.slice(0, maxLen) + "...";
|
|
4899
|
+
}
|
|
4900
|
+
return url;
|
|
4901
|
+
}
|
|
4902
|
+
}
|
|
4903
|
+
|
|
4904
|
+
// src/embeddings/providers/custom.ts
|
|
4905
|
+
var CustomEmbeddingProvider = class extends BaseEmbeddingProvider {
|
|
4837
4906
|
constructor(credentials, modelInfo) {
|
|
4838
|
-
|
|
4839
|
-
this.modelInfo = modelInfo;
|
|
4907
|
+
super(credentials, modelInfo);
|
|
4840
4908
|
}
|
|
4841
|
-
|
|
4842
|
-
const
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4909
|
+
splitIntoRequestBatches(texts) {
|
|
4910
|
+
const maxBatchSize = this.modelInfo.maxBatchSize;
|
|
4911
|
+
if (!maxBatchSize || texts.length <= maxBatchSize) {
|
|
4912
|
+
return [texts];
|
|
4913
|
+
}
|
|
4914
|
+
const batches = [];
|
|
4915
|
+
for (let i = 0; i < texts.length; i += maxBatchSize) {
|
|
4916
|
+
batches.push(texts.slice(i, i + maxBatchSize));
|
|
4917
|
+
}
|
|
4918
|
+
return batches;
|
|
4919
|
+
}
|
|
4920
|
+
async embedRequest(texts) {
|
|
4921
|
+
if (texts.length === 0) {
|
|
4922
|
+
return {
|
|
4923
|
+
embeddings: [],
|
|
4924
|
+
totalTokensUsed: 0
|
|
4925
|
+
};
|
|
4926
|
+
}
|
|
4927
|
+
const headers = {
|
|
4928
|
+
"Content-Type": "application/json"
|
|
4846
4929
|
};
|
|
4930
|
+
if (this.credentials.apiKey) {
|
|
4931
|
+
headers.Authorization = `Bearer ${this.credentials.apiKey}`;
|
|
4932
|
+
}
|
|
4933
|
+
const baseUrl = this.credentials.baseUrl ?? "";
|
|
4934
|
+
const fullUrl = `${baseUrl}/embeddings`;
|
|
4935
|
+
const urlCheck = validateExternalUrl(fullUrl);
|
|
4936
|
+
if (!urlCheck.valid) {
|
|
4937
|
+
throw new CustomProviderNonRetryableError(
|
|
4938
|
+
`Custom embedding provider URL blocked (SSRF protection): ${urlCheck.reason}`
|
|
4939
|
+
);
|
|
4940
|
+
}
|
|
4941
|
+
const timeoutMs = this.modelInfo.timeoutMs;
|
|
4942
|
+
const controller = new AbortController();
|
|
4943
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
4944
|
+
let response;
|
|
4945
|
+
try {
|
|
4946
|
+
response = await fetch(fullUrl, {
|
|
4947
|
+
method: "POST",
|
|
4948
|
+
headers,
|
|
4949
|
+
body: JSON.stringify({
|
|
4950
|
+
model: this.modelInfo.model,
|
|
4951
|
+
input: texts
|
|
4952
|
+
}),
|
|
4953
|
+
signal: controller.signal
|
|
4954
|
+
});
|
|
4955
|
+
} catch (error) {
|
|
4956
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
4957
|
+
throw new Error(`Custom embedding API request timed out after ${timeoutMs}ms for ${sanitizeUrlForError(fullUrl)}`);
|
|
4958
|
+
}
|
|
4959
|
+
throw error;
|
|
4960
|
+
} finally {
|
|
4961
|
+
clearTimeout(timeout);
|
|
4962
|
+
}
|
|
4963
|
+
if (!response.ok) {
|
|
4964
|
+
const errorText = (await response.text()).slice(0, 500);
|
|
4965
|
+
if (response.status >= 400 && response.status < 500 && response.status !== 429) {
|
|
4966
|
+
throw new CustomProviderNonRetryableError(`Custom embedding API error (non-retryable): ${response.status} - ${errorText}`);
|
|
4967
|
+
}
|
|
4968
|
+
throw new Error(`Custom embedding API error: ${response.status} - ${errorText}`);
|
|
4969
|
+
}
|
|
4970
|
+
const data = await response.json();
|
|
4971
|
+
if (data.data && Array.isArray(data.data)) {
|
|
4972
|
+
if (data.data.length > 0) {
|
|
4973
|
+
const actualDims = data.data[0].embedding.length;
|
|
4974
|
+
if (actualDims !== this.modelInfo.dimensions) {
|
|
4975
|
+
throw new Error(
|
|
4976
|
+
`Dimension mismatch: customProvider.dimensions is ${this.modelInfo.dimensions}, but the API returned vectors with ${actualDims} dimensions. Update your config to match the model's actual output dimensions.`
|
|
4977
|
+
);
|
|
4978
|
+
}
|
|
4979
|
+
}
|
|
4980
|
+
if (data.data.length !== texts.length) {
|
|
4981
|
+
throw new Error(
|
|
4982
|
+
`Embedding count mismatch: sent ${texts.length} texts but received ${data.data.length} embeddings. The custom embedding server may not support batch input.`
|
|
4983
|
+
);
|
|
4984
|
+
}
|
|
4985
|
+
return {
|
|
4986
|
+
embeddings: data.data.map((d) => d.embedding),
|
|
4987
|
+
totalTokensUsed: data.usage?.total_tokens ?? texts.reduce((sum, t) => sum + Math.ceil(t.length / 4), 0)
|
|
4988
|
+
};
|
|
4989
|
+
}
|
|
4990
|
+
throw new Error("Custom embedding API returned unexpected response format. Expected OpenAI-compatible format with data[].embedding.");
|
|
4847
4991
|
}
|
|
4848
|
-
async
|
|
4849
|
-
const
|
|
4992
|
+
async embedBatch(texts) {
|
|
4993
|
+
const requestBatches = this.splitIntoRequestBatches(texts);
|
|
4994
|
+
const embeddings = [];
|
|
4995
|
+
let totalTokensUsed = 0;
|
|
4996
|
+
for (const batch of requestBatches) {
|
|
4997
|
+
const result = await this.embedRequest(batch);
|
|
4998
|
+
embeddings.push(...result.embeddings);
|
|
4999
|
+
totalTokensUsed += result.totalTokensUsed;
|
|
5000
|
+
}
|
|
4850
5001
|
return {
|
|
4851
|
-
|
|
4852
|
-
|
|
5002
|
+
embeddings,
|
|
5003
|
+
totalTokensUsed
|
|
4853
5004
|
};
|
|
4854
5005
|
}
|
|
5006
|
+
};
|
|
5007
|
+
|
|
5008
|
+
// src/embeddings/providers/github-copilot.ts
|
|
5009
|
+
var GitHubCopilotEmbeddingProvider = class extends BaseEmbeddingProvider {
|
|
5010
|
+
constructor(credentials, modelInfo) {
|
|
5011
|
+
super(credentials, modelInfo);
|
|
5012
|
+
}
|
|
5013
|
+
getToken() {
|
|
5014
|
+
if (!this.credentials.refreshToken) {
|
|
5015
|
+
throw new Error("No OAuth token available for GitHub");
|
|
5016
|
+
}
|
|
5017
|
+
return this.credentials.refreshToken;
|
|
5018
|
+
}
|
|
4855
5019
|
async embedBatch(texts) {
|
|
4856
|
-
const
|
|
5020
|
+
const token = this.getToken();
|
|
5021
|
+
const response = await fetch(`${this.credentials.baseUrl}/inference/embeddings`, {
|
|
4857
5022
|
method: "POST",
|
|
4858
5023
|
headers: {
|
|
4859
|
-
Authorization: `Bearer ${
|
|
4860
|
-
"Content-Type": "application/json"
|
|
5024
|
+
Authorization: `Bearer ${token}`,
|
|
5025
|
+
"Content-Type": "application/json",
|
|
5026
|
+
Accept: "application/vnd.github+json",
|
|
5027
|
+
"X-GitHub-Api-Version": "2022-11-28"
|
|
4861
5028
|
},
|
|
4862
5029
|
body: JSON.stringify({
|
|
4863
|
-
model: this.modelInfo.model
|
|
5030
|
+
model: `openai/${this.modelInfo.model}`,
|
|
4864
5031
|
input: texts
|
|
4865
5032
|
})
|
|
4866
5033
|
});
|
|
4867
5034
|
if (!response.ok) {
|
|
4868
|
-
const error = await response.text();
|
|
4869
|
-
throw new Error(`
|
|
5035
|
+
const error = (await response.text()).slice(0, 500);
|
|
5036
|
+
throw new Error(`GitHub Copilot embedding API error: ${response.status} - ${error}`);
|
|
4870
5037
|
}
|
|
4871
5038
|
const data = await response.json();
|
|
4872
5039
|
return {
|
|
@@ -4874,16 +5041,14 @@ var OpenAIEmbeddingProvider = class {
|
|
|
4874
5041
|
totalTokensUsed: data.usage.total_tokens
|
|
4875
5042
|
};
|
|
4876
5043
|
}
|
|
4877
|
-
getModelInfo() {
|
|
4878
|
-
return this.modelInfo;
|
|
4879
|
-
}
|
|
4880
5044
|
};
|
|
4881
|
-
|
|
5045
|
+
|
|
5046
|
+
// src/embeddings/providers/google.ts
|
|
5047
|
+
var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider extends BaseEmbeddingProvider {
|
|
5048
|
+
static BATCH_SIZE = 20;
|
|
4882
5049
|
constructor(credentials, modelInfo) {
|
|
4883
|
-
|
|
4884
|
-
this.modelInfo = modelInfo;
|
|
5050
|
+
super(credentials, modelInfo);
|
|
4885
5051
|
}
|
|
4886
|
-
static BATCH_SIZE = 20;
|
|
4887
5052
|
async embedQuery(query) {
|
|
4888
5053
|
const taskType = this.modelInfo.taskAble ? "CODE_RETRIEVAL_QUERY" : void 0;
|
|
4889
5054
|
const result = await this.embedWithTaskType([query], taskType);
|
|
@@ -4904,12 +5069,6 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider {
|
|
|
4904
5069
|
const taskType = this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
4905
5070
|
return this.embedWithTaskType(texts, taskType);
|
|
4906
5071
|
}
|
|
4907
|
-
/**
|
|
4908
|
-
* Embeds texts using the Google embedContent API.
|
|
4909
|
-
* Sends multiple texts as parts in batched requests (up to BATCH_SIZE per call).
|
|
4910
|
-
* When taskType is provided (gemini-embedding-001), includes it in the request
|
|
4911
|
-
* for task-specific embedding optimization.
|
|
4912
|
-
*/
|
|
4913
5072
|
async embedWithTaskType(texts, taskType) {
|
|
4914
5073
|
const batches = [];
|
|
4915
5074
|
for (let i = 0; i < texts.length; i += _GoogleEmbeddingProvider.BATCH_SIZE) {
|
|
@@ -4926,17 +5085,18 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider {
|
|
|
4926
5085
|
outputDimensionality: this.modelInfo.dimensions
|
|
4927
5086
|
}));
|
|
4928
5087
|
const response = await fetch(
|
|
4929
|
-
`${this.credentials.baseUrl}/models/${this.modelInfo.model}:batchEmbedContents
|
|
5088
|
+
`${this.credentials.baseUrl}/models/${this.modelInfo.model}:batchEmbedContents`,
|
|
4930
5089
|
{
|
|
4931
5090
|
method: "POST",
|
|
4932
5091
|
headers: {
|
|
4933
|
-
"Content-Type": "application/json"
|
|
5092
|
+
"Content-Type": "application/json",
|
|
5093
|
+
...this.credentials.apiKey && { "x-goog-api-key": this.credentials.apiKey }
|
|
4934
5094
|
},
|
|
4935
5095
|
body: JSON.stringify({ requests })
|
|
4936
5096
|
}
|
|
4937
5097
|
);
|
|
4938
5098
|
if (!response.ok) {
|
|
4939
|
-
const error = await response.text();
|
|
5099
|
+
const error = (await response.text()).slice(0, 500);
|
|
4940
5100
|
throw new Error(`Google embedding API error: ${response.status} - ${error}`);
|
|
4941
5101
|
}
|
|
4942
5102
|
const data = await response.json();
|
|
@@ -4951,29 +5111,13 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider {
|
|
|
4951
5111
|
totalTokensUsed: batchResults.reduce((sum, r) => sum + r.tokensUsed, 0)
|
|
4952
5112
|
};
|
|
4953
5113
|
}
|
|
4954
|
-
getModelInfo() {
|
|
4955
|
-
return this.modelInfo;
|
|
4956
|
-
}
|
|
4957
5114
|
};
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
this.modelInfo = modelInfo;
|
|
4962
|
-
}
|
|
5115
|
+
|
|
5116
|
+
// src/embeddings/providers/ollama.ts
|
|
5117
|
+
var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddingProvider {
|
|
4963
5118
|
static MIN_TRUNCATION_CHARS = 512;
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
return {
|
|
4967
|
-
embedding: result.embeddings[0],
|
|
4968
|
-
tokensUsed: result.totalTokensUsed
|
|
4969
|
-
};
|
|
4970
|
-
}
|
|
4971
|
-
async embedDocument(document) {
|
|
4972
|
-
const result = await this.embedBatch([document]);
|
|
4973
|
-
return {
|
|
4974
|
-
embedding: result.embeddings[0],
|
|
4975
|
-
tokensUsed: result.totalTokensUsed
|
|
4976
|
-
};
|
|
5119
|
+
constructor(credentials, modelInfo) {
|
|
5120
|
+
super(credentials, modelInfo);
|
|
4977
5121
|
}
|
|
4978
5122
|
estimateTokens(text) {
|
|
4979
5123
|
return Math.ceil(text.length / 4);
|
|
@@ -5037,166 +5181,97 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider {
|
|
|
5037
5181
|
return await this.embedSingle(truncated);
|
|
5038
5182
|
} catch (retryError) {
|
|
5039
5183
|
if (!this.isContextLengthError(retryError)) {
|
|
5040
|
-
throw retryError;
|
|
5041
|
-
}
|
|
5042
|
-
lastError = retryError;
|
|
5043
|
-
}
|
|
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
|
|
5058
|
-
})
|
|
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
|
-
}
|
|
5075
|
-
return {
|
|
5076
|
-
embeddings: results.map((r) => r.embedding),
|
|
5077
|
-
totalTokensUsed: results.reduce((sum, r) => sum + r.tokensUsed, 0)
|
|
5078
|
-
};
|
|
5079
|
-
}
|
|
5080
|
-
getModelInfo() {
|
|
5081
|
-
return this.modelInfo;
|
|
5082
|
-
}
|
|
5083
|
-
};
|
|
5084
|
-
var CustomEmbeddingProvider = class {
|
|
5085
|
-
constructor(credentials, modelInfo) {
|
|
5086
|
-
this.credentials = credentials;
|
|
5087
|
-
this.modelInfo = modelInfo;
|
|
5088
|
-
}
|
|
5089
|
-
splitIntoRequestBatches(texts) {
|
|
5090
|
-
const maxBatchSize = this.modelInfo.maxBatchSize;
|
|
5091
|
-
if (!maxBatchSize || texts.length <= maxBatchSize) {
|
|
5092
|
-
return [texts];
|
|
5093
|
-
}
|
|
5094
|
-
const batches = [];
|
|
5095
|
-
for (let i = 0; i < texts.length; i += maxBatchSize) {
|
|
5096
|
-
batches.push(texts.slice(i, i + maxBatchSize));
|
|
5097
|
-
}
|
|
5098
|
-
return batches;
|
|
5099
|
-
}
|
|
5100
|
-
async embedRequest(texts) {
|
|
5101
|
-
if (texts.length === 0) {
|
|
5102
|
-
return {
|
|
5103
|
-
embeddings: [],
|
|
5104
|
-
totalTokensUsed: 0
|
|
5105
|
-
};
|
|
5106
|
-
}
|
|
5107
|
-
const headers = {
|
|
5108
|
-
"Content-Type": "application/json"
|
|
5109
|
-
};
|
|
5110
|
-
if (this.credentials.apiKey) {
|
|
5111
|
-
headers["Authorization"] = `Bearer ${this.credentials.apiKey}`;
|
|
5112
|
-
}
|
|
5113
|
-
const baseUrl = this.credentials.baseUrl ?? "";
|
|
5114
|
-
const timeoutMs = this.modelInfo.timeoutMs;
|
|
5115
|
-
const controller = new AbortController();
|
|
5116
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
5117
|
-
let response;
|
|
5118
|
-
try {
|
|
5119
|
-
response = await fetch(`${baseUrl}/embeddings`, {
|
|
5120
|
-
method: "POST",
|
|
5121
|
-
headers,
|
|
5122
|
-
body: JSON.stringify({
|
|
5123
|
-
model: this.modelInfo.model,
|
|
5124
|
-
input: texts
|
|
5125
|
-
}),
|
|
5126
|
-
signal: controller.signal
|
|
5127
|
-
});
|
|
5128
|
-
} catch (error) {
|
|
5129
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
5130
|
-
throw new Error(`Custom embedding API request timed out after ${timeoutMs}ms for ${baseUrl}/embeddings`);
|
|
5131
|
-
}
|
|
5132
|
-
throw error;
|
|
5133
|
-
} finally {
|
|
5134
|
-
clearTimeout(timeout);
|
|
5135
|
-
}
|
|
5136
|
-
if (!response.ok) {
|
|
5137
|
-
const errorText = await response.text();
|
|
5138
|
-
if (response.status >= 400 && response.status < 500 && response.status !== 429) {
|
|
5139
|
-
throw new CustomProviderNonRetryableError(`Custom embedding API error (non-retryable): ${response.status} - ${errorText}`);
|
|
5140
|
-
}
|
|
5141
|
-
throw new Error(`Custom embedding API error: ${response.status} - ${errorText}`);
|
|
5142
|
-
}
|
|
5143
|
-
const data = await response.json();
|
|
5144
|
-
if (data.data && Array.isArray(data.data)) {
|
|
5145
|
-
if (data.data.length > 0) {
|
|
5146
|
-
const actualDims = data.data[0].embedding.length;
|
|
5147
|
-
if (actualDims !== this.modelInfo.dimensions) {
|
|
5148
|
-
throw new Error(
|
|
5149
|
-
`Dimension mismatch: customProvider.dimensions is ${this.modelInfo.dimensions}, but the API returned vectors with ${actualDims} dimensions. Update your config to match the model's actual output dimensions.`
|
|
5150
|
-
);
|
|
5184
|
+
throw retryError;
|
|
5185
|
+
}
|
|
5186
|
+
lastError = retryError;
|
|
5151
5187
|
}
|
|
5152
5188
|
}
|
|
5153
|
-
|
|
5154
|
-
throw new Error(
|
|
5155
|
-
`Embedding count mismatch: sent ${texts.length} texts but received ${data.data.length} embeddings. The custom embedding server may not support batch input.`
|
|
5156
|
-
);
|
|
5157
|
-
}
|
|
5158
|
-
return {
|
|
5159
|
-
embeddings: data.data.map((d) => d.embedding),
|
|
5160
|
-
// Rough estimate: ~4 chars per token. Used as fallback when the server
|
|
5161
|
-
// doesn't return usage.total_tokens (e.g. llama.cpp, some vLLM configs).
|
|
5162
|
-
totalTokensUsed: data.usage?.total_tokens ?? texts.reduce((sum, t) => sum + Math.ceil(t.length / 4), 0)
|
|
5163
|
-
};
|
|
5189
|
+
throw lastError;
|
|
5164
5190
|
}
|
|
5165
|
-
throw new Error("Custom embedding API returned unexpected response format. Expected OpenAI-compatible format with data[].embedding.");
|
|
5166
5191
|
}
|
|
5167
|
-
async
|
|
5168
|
-
const
|
|
5192
|
+
async embedSingle(text) {
|
|
5193
|
+
const response = await fetch(`${this.credentials.baseUrl}/api/embeddings`, {
|
|
5194
|
+
method: "POST",
|
|
5195
|
+
headers: {
|
|
5196
|
+
"Content-Type": "application/json"
|
|
5197
|
+
},
|
|
5198
|
+
body: JSON.stringify({
|
|
5199
|
+
model: this.modelInfo.model,
|
|
5200
|
+
prompt: text,
|
|
5201
|
+
truncate: false
|
|
5202
|
+
})
|
|
5203
|
+
});
|
|
5204
|
+
if (!response.ok) {
|
|
5205
|
+
const error = (await response.text()).slice(0, 500);
|
|
5206
|
+
throw new Error(`Ollama embedding API error: ${response.status} - ${error}`);
|
|
5207
|
+
}
|
|
5208
|
+
const data = await response.json();
|
|
5169
5209
|
return {
|
|
5170
|
-
embedding:
|
|
5171
|
-
tokensUsed:
|
|
5210
|
+
embedding: data.embedding,
|
|
5211
|
+
tokensUsed: this.estimateTokens(text)
|
|
5172
5212
|
};
|
|
5173
5213
|
}
|
|
5174
|
-
async
|
|
5175
|
-
const
|
|
5214
|
+
async embedBatch(texts) {
|
|
5215
|
+
const results = [];
|
|
5216
|
+
for (const text of texts) {
|
|
5217
|
+
results.push(await this.embedSingleWithFallback(text));
|
|
5218
|
+
}
|
|
5176
5219
|
return {
|
|
5177
|
-
|
|
5178
|
-
|
|
5220
|
+
embeddings: results.map((r) => r.embedding),
|
|
5221
|
+
totalTokensUsed: results.reduce((sum, r) => sum + r.tokensUsed, 0)
|
|
5179
5222
|
};
|
|
5180
5223
|
}
|
|
5224
|
+
};
|
|
5225
|
+
|
|
5226
|
+
// src/embeddings/providers/openai.ts
|
|
5227
|
+
var OpenAIEmbeddingProvider = class extends BaseEmbeddingProvider {
|
|
5228
|
+
constructor(credentials, modelInfo) {
|
|
5229
|
+
super(credentials, modelInfo);
|
|
5230
|
+
}
|
|
5181
5231
|
async embedBatch(texts) {
|
|
5182
|
-
const
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5232
|
+
const response = await fetch(`${this.credentials.baseUrl}/embeddings`, {
|
|
5233
|
+
method: "POST",
|
|
5234
|
+
headers: {
|
|
5235
|
+
Authorization: `Bearer ${this.credentials.apiKey}`,
|
|
5236
|
+
"Content-Type": "application/json"
|
|
5237
|
+
},
|
|
5238
|
+
body: JSON.stringify({
|
|
5239
|
+
model: this.modelInfo.model,
|
|
5240
|
+
input: texts
|
|
5241
|
+
})
|
|
5242
|
+
});
|
|
5243
|
+
if (!response.ok) {
|
|
5244
|
+
const error = (await response.text()).slice(0, 500);
|
|
5245
|
+
throw new Error(`OpenAI embedding API error: ${response.status} - ${error}`);
|
|
5189
5246
|
}
|
|
5247
|
+
const data = await response.json();
|
|
5190
5248
|
return {
|
|
5191
|
-
embeddings,
|
|
5192
|
-
totalTokensUsed
|
|
5249
|
+
embeddings: data.data.map((d) => d.embedding),
|
|
5250
|
+
totalTokensUsed: data.usage.total_tokens
|
|
5193
5251
|
};
|
|
5194
5252
|
}
|
|
5195
|
-
getModelInfo() {
|
|
5196
|
-
return this.modelInfo;
|
|
5197
|
-
}
|
|
5198
5253
|
};
|
|
5199
5254
|
|
|
5255
|
+
// src/embeddings/provider.ts
|
|
5256
|
+
function createEmbeddingProvider(configuredProviderInfo) {
|
|
5257
|
+
switch (configuredProviderInfo.provider) {
|
|
5258
|
+
case "github-copilot":
|
|
5259
|
+
return new GitHubCopilotEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
5260
|
+
case "openai":
|
|
5261
|
+
return new OpenAIEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
5262
|
+
case "google":
|
|
5263
|
+
return new GoogleEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
5264
|
+
case "ollama":
|
|
5265
|
+
return new OllamaEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
5266
|
+
case "custom":
|
|
5267
|
+
return new CustomEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
5268
|
+
default: {
|
|
5269
|
+
const _exhaustive = configuredProviderInfo;
|
|
5270
|
+
throw new Error(`Unsupported embedding provider: ${_exhaustive.provider}`);
|
|
5271
|
+
}
|
|
5272
|
+
}
|
|
5273
|
+
}
|
|
5274
|
+
|
|
5200
5275
|
// src/rerank/index.ts
|
|
5201
5276
|
function createReranker(config) {
|
|
5202
5277
|
if (!config.enabled) {
|
|
@@ -5232,7 +5307,10 @@ var SiliconFlowReranker = class {
|
|
|
5232
5307
|
if (this.config.apiKey) {
|
|
5233
5308
|
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
5234
5309
|
}
|
|
5235
|
-
const baseUrl = this.config.baseUrl
|
|
5310
|
+
const baseUrl = this.config.baseUrl;
|
|
5311
|
+
if (!baseUrl) {
|
|
5312
|
+
throw new Error("Reranker baseUrl is required. Configure reranker.baseUrl in your codebase-index.json.");
|
|
5313
|
+
}
|
|
5236
5314
|
const timeoutMs = this.config.timeoutMs ?? 3e4;
|
|
5237
5315
|
const controller = new AbortController();
|
|
5238
5316
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -5306,20 +5384,21 @@ function createCostEstimate(files, provider) {
|
|
|
5306
5384
|
}
|
|
5307
5385
|
function formatCostEstimate(estimate) {
|
|
5308
5386
|
const sizeFormatted = formatBytes(estimate.totalSizeBytes);
|
|
5387
|
+
const filesFormatted = `${estimate.filesCount.toLocaleString()} files`;
|
|
5309
5388
|
const costFormatted = estimate.isFree ? "Free" : `~$${estimate.estimatedCost.toFixed(4)}`;
|
|
5310
5389
|
return `
|
|
5311
5390
|
\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
|
|
5312
5391
|
\u2502 \u{1F4CA} Indexing Estimate \u2502
|
|
5313
5392
|
\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524
|
|
5314
5393
|
\u2502 \u2502
|
|
5315
|
-
\u2502 Files to index: ${
|
|
5316
|
-
\u2502 Total size: ${
|
|
5317
|
-
\u2502 Estimated chunks: ${
|
|
5318
|
-
\u2502 Estimated tokens: ${
|
|
5394
|
+
\u2502 Files to index: ${filesFormatted.padEnd(40)}\u2502
|
|
5395
|
+
\u2502 Total size: ${sizeFormatted.padEnd(40)}\u2502
|
|
5396
|
+
\u2502 Estimated chunks: ${("~" + estimate.estimatedChunks.toLocaleString() + " chunks").padEnd(40)}\u2502
|
|
5397
|
+
\u2502 Estimated tokens: ${("~" + estimate.estimatedTokens.toLocaleString() + " tokens").padEnd(40)}\u2502
|
|
5319
5398
|
\u2502 \u2502
|
|
5320
|
-
\u2502 Provider: ${
|
|
5321
|
-
\u2502 Model: ${
|
|
5322
|
-
\u2502 Cost: ${
|
|
5399
|
+
\u2502 Provider: ${estimate.provider.padEnd(52)}\u2502
|
|
5400
|
+
\u2502 Model: ${estimate.model.padEnd(52)}\u2502
|
|
5401
|
+
\u2502 Cost: ${costFormatted.padEnd(52)}\u2502
|
|
5323
5402
|
\u2502 \u2502
|
|
5324
5403
|
\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
|
|
5325
5404
|
`;
|
|
@@ -5331,9 +5410,6 @@ function formatBytes(bytes) {
|
|
|
5331
5410
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
5332
5411
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i];
|
|
5333
5412
|
}
|
|
5334
|
-
function padRight(str, length) {
|
|
5335
|
-
return str.padEnd(length);
|
|
5336
|
-
}
|
|
5337
5413
|
|
|
5338
5414
|
// src/utils/logger.ts
|
|
5339
5415
|
var LOG_LEVEL_PRIORITY = {
|
|
@@ -5400,6 +5476,10 @@ var Logger = class {
|
|
|
5400
5476
|
this.logs.shift();
|
|
5401
5477
|
}
|
|
5402
5478
|
}
|
|
5479
|
+
withMetrics(fn) {
|
|
5480
|
+
if (!this.config.metrics) return;
|
|
5481
|
+
fn();
|
|
5482
|
+
}
|
|
5403
5483
|
search(level, message, data) {
|
|
5404
5484
|
if (this.config.logSearch) {
|
|
5405
5485
|
this.log(level, "search", message, data);
|
|
@@ -5438,89 +5518,107 @@ var Logger = class {
|
|
|
5438
5518
|
this.log("debug", "general", message, data);
|
|
5439
5519
|
}
|
|
5440
5520
|
recordIndexingStart() {
|
|
5441
|
-
|
|
5442
|
-
|
|
5521
|
+
this.withMetrics(() => {
|
|
5522
|
+
this.metrics.indexingStartTime = Date.now();
|
|
5523
|
+
});
|
|
5443
5524
|
}
|
|
5444
5525
|
recordIndexingEnd() {
|
|
5445
|
-
|
|
5446
|
-
|
|
5526
|
+
this.withMetrics(() => {
|
|
5527
|
+
this.metrics.indexingEndTime = Date.now();
|
|
5528
|
+
});
|
|
5447
5529
|
}
|
|
5448
5530
|
recordFilesScanned(count) {
|
|
5449
|
-
|
|
5450
|
-
|
|
5531
|
+
this.withMetrics(() => {
|
|
5532
|
+
this.metrics.filesScanned = count;
|
|
5533
|
+
});
|
|
5451
5534
|
}
|
|
5452
5535
|
recordFilesParsed(count) {
|
|
5453
|
-
|
|
5454
|
-
|
|
5536
|
+
this.withMetrics(() => {
|
|
5537
|
+
this.metrics.filesParsed = count;
|
|
5538
|
+
});
|
|
5455
5539
|
}
|
|
5456
5540
|
recordParseDuration(durationMs) {
|
|
5457
|
-
|
|
5458
|
-
|
|
5541
|
+
this.withMetrics(() => {
|
|
5542
|
+
this.metrics.parseMs = durationMs;
|
|
5543
|
+
});
|
|
5459
5544
|
}
|
|
5460
5545
|
recordChunksProcessed(count) {
|
|
5461
|
-
|
|
5462
|
-
|
|
5546
|
+
this.withMetrics(() => {
|
|
5547
|
+
this.metrics.chunksProcessed += count;
|
|
5548
|
+
});
|
|
5463
5549
|
}
|
|
5464
5550
|
recordChunksEmbedded(count) {
|
|
5465
|
-
|
|
5466
|
-
|
|
5551
|
+
this.withMetrics(() => {
|
|
5552
|
+
this.metrics.chunksEmbedded += count;
|
|
5553
|
+
});
|
|
5467
5554
|
}
|
|
5468
5555
|
recordChunksFromCache(count) {
|
|
5469
|
-
|
|
5470
|
-
|
|
5556
|
+
this.withMetrics(() => {
|
|
5557
|
+
this.metrics.chunksFromCache += count;
|
|
5558
|
+
});
|
|
5471
5559
|
}
|
|
5472
5560
|
recordChunksRemoved(count) {
|
|
5473
|
-
|
|
5474
|
-
|
|
5561
|
+
this.withMetrics(() => {
|
|
5562
|
+
this.metrics.chunksRemoved += count;
|
|
5563
|
+
});
|
|
5475
5564
|
}
|
|
5476
5565
|
recordEmbeddingApiCall(tokens) {
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5566
|
+
this.withMetrics(() => {
|
|
5567
|
+
this.metrics.embeddingApiCalls++;
|
|
5568
|
+
this.metrics.embeddingTokensUsed += tokens;
|
|
5569
|
+
});
|
|
5480
5570
|
}
|
|
5481
5571
|
recordEmbeddingError() {
|
|
5482
|
-
|
|
5483
|
-
|
|
5572
|
+
this.withMetrics(() => {
|
|
5573
|
+
this.metrics.embeddingErrors++;
|
|
5574
|
+
});
|
|
5484
5575
|
}
|
|
5485
5576
|
recordSearch(durationMs, breakdown) {
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5577
|
+
this.withMetrics(() => {
|
|
5578
|
+
this.metrics.searchCount++;
|
|
5579
|
+
this.metrics.searchTotalMs += durationMs;
|
|
5580
|
+
this.metrics.searchLastMs = durationMs;
|
|
5581
|
+
this.metrics.searchAvgMs = this.metrics.searchTotalMs / this.metrics.searchCount;
|
|
5582
|
+
if (breakdown) {
|
|
5583
|
+
this.metrics.embeddingCallMs = breakdown.embeddingMs;
|
|
5584
|
+
this.metrics.vectorSearchMs = breakdown.vectorMs;
|
|
5585
|
+
this.metrics.keywordSearchMs = breakdown.keywordMs;
|
|
5586
|
+
this.metrics.fusionMs = breakdown.fusionMs;
|
|
5587
|
+
}
|
|
5588
|
+
});
|
|
5497
5589
|
}
|
|
5498
5590
|
recordCacheHit() {
|
|
5499
|
-
|
|
5500
|
-
|
|
5591
|
+
this.withMetrics(() => {
|
|
5592
|
+
this.metrics.cacheHits++;
|
|
5593
|
+
});
|
|
5501
5594
|
}
|
|
5502
5595
|
recordCacheMiss() {
|
|
5503
|
-
|
|
5504
|
-
|
|
5596
|
+
this.withMetrics(() => {
|
|
5597
|
+
this.metrics.cacheMisses++;
|
|
5598
|
+
});
|
|
5505
5599
|
}
|
|
5506
5600
|
recordQueryCacheHit() {
|
|
5507
|
-
|
|
5508
|
-
|
|
5601
|
+
this.withMetrics(() => {
|
|
5602
|
+
this.metrics.queryCacheHits++;
|
|
5603
|
+
});
|
|
5509
5604
|
}
|
|
5510
5605
|
recordQueryCacheSimilarHit() {
|
|
5511
|
-
|
|
5512
|
-
|
|
5606
|
+
this.withMetrics(() => {
|
|
5607
|
+
this.metrics.queryCacheSimilarHits++;
|
|
5608
|
+
});
|
|
5513
5609
|
}
|
|
5514
5610
|
recordQueryCacheMiss() {
|
|
5515
|
-
|
|
5516
|
-
|
|
5611
|
+
this.withMetrics(() => {
|
|
5612
|
+
this.metrics.queryCacheMisses++;
|
|
5613
|
+
});
|
|
5517
5614
|
}
|
|
5518
5615
|
recordGc(orphans, chunks, embeddings) {
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5616
|
+
this.withMetrics(() => {
|
|
5617
|
+
this.metrics.gcRuns++;
|
|
5618
|
+
this.metrics.gcOrphansRemoved += orphans;
|
|
5619
|
+
this.metrics.gcChunksRemoved += chunks;
|
|
5620
|
+
this.metrics.gcEmbeddingsRemoved += embeddings;
|
|
5621
|
+
});
|
|
5524
5622
|
}
|
|
5525
5623
|
getMetrics() {
|
|
5526
5624
|
return { ...this.metrics };
|
|
@@ -5627,7 +5725,7 @@ function initializeLogger(config) {
|
|
|
5627
5725
|
}
|
|
5628
5726
|
|
|
5629
5727
|
// src/native/index.ts
|
|
5630
|
-
var
|
|
5728
|
+
var path11 = __toESM(require("path"), 1);
|
|
5631
5729
|
var os4 = __toESM(require("os"), 1);
|
|
5632
5730
|
var module2 = __toESM(require("module"), 1);
|
|
5633
5731
|
var import_url = require("url");
|
|
@@ -5652,19 +5750,19 @@ function getNativeBinding() {
|
|
|
5652
5750
|
let currentDir;
|
|
5653
5751
|
let requireTarget;
|
|
5654
5752
|
if (typeof import_meta !== "undefined" && import_meta.url) {
|
|
5655
|
-
currentDir =
|
|
5753
|
+
currentDir = path11.dirname((0, import_url.fileURLToPath)(import_meta.url));
|
|
5656
5754
|
requireTarget = import_meta.url;
|
|
5657
5755
|
} else if (typeof __dirname !== "undefined") {
|
|
5658
5756
|
currentDir = __dirname;
|
|
5659
5757
|
requireTarget = __filename;
|
|
5660
5758
|
} else {
|
|
5661
5759
|
currentDir = process.cwd();
|
|
5662
|
-
requireTarget =
|
|
5760
|
+
requireTarget = path11.join(currentDir, "index.js");
|
|
5663
5761
|
}
|
|
5664
5762
|
const normalizedDir = currentDir.replace(/\\/g, "/");
|
|
5665
|
-
const isDevMode = normalizedDir.includes("/src/native") || currentDir.includes(
|
|
5666
|
-
const packageRoot = isDevMode ?
|
|
5667
|
-
const nativePath =
|
|
5763
|
+
const isDevMode = normalizedDir.includes("/src/native") || currentDir.includes(path11.join("src", "native"));
|
|
5764
|
+
const packageRoot = isDevMode ? path11.resolve(currentDir, "../..") : path11.resolve(currentDir, "..");
|
|
5765
|
+
const nativePath = path11.join(packageRoot, "native", bindingName);
|
|
5668
5766
|
const require2 = module2.createRequire(requireTarget);
|
|
5669
5767
|
return require2(nativePath);
|
|
5670
5768
|
}
|
|
@@ -6234,7 +6332,6 @@ var Database = class {
|
|
|
6234
6332
|
this.throwIfClosed();
|
|
6235
6333
|
return this.inner.getStats();
|
|
6236
6334
|
}
|
|
6237
|
-
// ── Symbol methods ──────────────────────────────────────────────
|
|
6238
6335
|
upsertSymbol(symbol) {
|
|
6239
6336
|
this.throwIfClosed();
|
|
6240
6337
|
this.inner.upsertSymbol(symbol);
|
|
@@ -6264,7 +6361,6 @@ var Database = class {
|
|
|
6264
6361
|
this.throwIfClosed();
|
|
6265
6362
|
return this.inner.deleteSymbolsByFile(filePath);
|
|
6266
6363
|
}
|
|
6267
|
-
// ── Call Edge methods ────────────────────────────────────────────
|
|
6268
6364
|
upsertCallEdge(edge) {
|
|
6269
6365
|
this.throwIfClosed();
|
|
6270
6366
|
this.inner.upsertCallEdge(edge);
|
|
@@ -6294,7 +6390,6 @@ var Database = class {
|
|
|
6294
6390
|
this.throwIfClosed();
|
|
6295
6391
|
this.inner.resolveCallEdge(edgeId, toSymbolId);
|
|
6296
6392
|
}
|
|
6297
|
-
// ── Branch Symbol methods ────────────────────────────────────────
|
|
6298
6393
|
addSymbolsToBranch(branch, symbolIds) {
|
|
6299
6394
|
this.throwIfClosed();
|
|
6300
6395
|
this.inner.addSymbolsToBranch(branch, symbolIds);
|
|
@@ -6327,7 +6422,6 @@ var Database = class {
|
|
|
6327
6422
|
if (symbolIds.length === 0) return 0;
|
|
6328
6423
|
return this.inner.deleteBranchSymbolsForBranch(branch, symbolIds);
|
|
6329
6424
|
}
|
|
6330
|
-
// ── GC methods for symbols/edges ─────────────────────────────────
|
|
6331
6425
|
gcOrphanSymbols() {
|
|
6332
6426
|
this.throwIfClosed();
|
|
6333
6427
|
return this.inner.gcOrphanSymbols();
|
|
@@ -6339,7 +6433,7 @@ var Database = class {
|
|
|
6339
6433
|
};
|
|
6340
6434
|
|
|
6341
6435
|
// src/indexer/index.ts
|
|
6342
|
-
var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig"]);
|
|
6436
|
+
var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig", "gdscript", "matlab"]);
|
|
6343
6437
|
var CASE_INSENSITIVE_LANGUAGES = /* @__PURE__ */ new Set(["apex"]);
|
|
6344
6438
|
var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
6345
6439
|
"function_declaration",
|
|
@@ -6366,7 +6460,15 @@ var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
|
6366
6460
|
"trigger_declaration",
|
|
6367
6461
|
"test_declaration",
|
|
6368
6462
|
"struct_declaration",
|
|
6369
|
-
"union_declaration"
|
|
6463
|
+
"union_declaration",
|
|
6464
|
+
// GDScript declarations whose names participate in the call graph.
|
|
6465
|
+
// `function_definition` and `class_definition` are already in the set
|
|
6466
|
+
// above (shared with Python/C/Bash and Python, respectively).
|
|
6467
|
+
"constructor_definition",
|
|
6468
|
+
"enum_definition",
|
|
6469
|
+
"signal_statement",
|
|
6470
|
+
"const_statement",
|
|
6471
|
+
"class_name_statement"
|
|
6370
6472
|
]);
|
|
6371
6473
|
function float32ArrayToBuffer(arr) {
|
|
6372
6474
|
const float32 = new Float32Array(arr);
|
|
@@ -6568,9 +6670,9 @@ function hasAllEmbeddingParts(parts, expectedPartCount) {
|
|
|
6568
6670
|
return true;
|
|
6569
6671
|
}
|
|
6570
6672
|
function isPathWithinRoot(filePath, rootPath) {
|
|
6571
|
-
const normalizedFilePath =
|
|
6572
|
-
const normalizedRoot =
|
|
6573
|
-
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${
|
|
6673
|
+
const normalizedFilePath = path12.resolve(filePath);
|
|
6674
|
+
const normalizedRoot = path12.resolve(rootPath);
|
|
6675
|
+
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path12.sep}`);
|
|
6574
6676
|
}
|
|
6575
6677
|
var rankingQueryTokenCache = /* @__PURE__ */ new Map();
|
|
6576
6678
|
var rankingNameTokenCache = /* @__PURE__ */ new Map();
|
|
@@ -7623,22 +7725,28 @@ var Indexer = class {
|
|
|
7623
7725
|
this.projectRoot = projectRoot;
|
|
7624
7726
|
this.config = config;
|
|
7625
7727
|
this.indexPath = this.getIndexPath();
|
|
7626
|
-
this.fileHashCachePath =
|
|
7627
|
-
this.failedBatchesPath =
|
|
7628
|
-
this.indexingLockPath =
|
|
7728
|
+
this.fileHashCachePath = path12.join(this.indexPath, "file-hashes.json");
|
|
7729
|
+
this.failedBatchesPath = path12.join(this.indexPath, "failed-batches.json");
|
|
7730
|
+
this.indexingLockPath = path12.join(this.indexPath, "indexing.lock");
|
|
7629
7731
|
this.logger = initializeLogger(config.debug);
|
|
7630
7732
|
}
|
|
7631
7733
|
getIndexPath() {
|
|
7632
7734
|
return resolveProjectIndexPath(this.projectRoot, this.config.scope);
|
|
7633
7735
|
}
|
|
7634
7736
|
loadFileHashCache() {
|
|
7737
|
+
if (!(0, import_fs7.existsSync)(this.fileHashCachePath)) {
|
|
7738
|
+
return;
|
|
7739
|
+
}
|
|
7635
7740
|
try {
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7641
|
-
|
|
7741
|
+
const data = (0, import_fs7.readFileSync)(this.fileHashCachePath, "utf-8");
|
|
7742
|
+
const parsed = JSON.parse(data);
|
|
7743
|
+
this.fileHashCache = new Map(Object.entries(parsed));
|
|
7744
|
+
} catch (error) {
|
|
7745
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7746
|
+
this.logger.warn("Failed to load file hash cache, resetting cache state", {
|
|
7747
|
+
fileHashCachePath: this.fileHashCachePath,
|
|
7748
|
+
error: message
|
|
7749
|
+
});
|
|
7642
7750
|
this.fileHashCache = /* @__PURE__ */ new Map();
|
|
7643
7751
|
}
|
|
7644
7752
|
}
|
|
@@ -7651,13 +7759,14 @@ var Indexer = class {
|
|
|
7651
7759
|
}
|
|
7652
7760
|
atomicWriteSync(targetPath, data) {
|
|
7653
7761
|
const tempPath = `${targetPath}.tmp`;
|
|
7654
|
-
(0,
|
|
7655
|
-
(0,
|
|
7762
|
+
(0, import_fs7.mkdirSync)(path12.dirname(targetPath), { recursive: true });
|
|
7763
|
+
(0, import_fs7.writeFileSync)(tempPath, data);
|
|
7764
|
+
(0, import_fs7.renameSync)(tempPath, targetPath);
|
|
7656
7765
|
}
|
|
7657
7766
|
getScopedRoots() {
|
|
7658
|
-
const roots = /* @__PURE__ */ new Set([
|
|
7767
|
+
const roots = /* @__PURE__ */ new Set([path12.resolve(this.projectRoot)]);
|
|
7659
7768
|
for (const kbRoot of this.config.knowledgeBases) {
|
|
7660
|
-
roots.add(
|
|
7769
|
+
roots.add(path12.resolve(this.projectRoot, kbRoot));
|
|
7661
7770
|
}
|
|
7662
7771
|
return Array.from(roots);
|
|
7663
7772
|
}
|
|
@@ -7666,22 +7775,22 @@ var Indexer = class {
|
|
|
7666
7775
|
if (this.config.scope !== "global") {
|
|
7667
7776
|
return branchName;
|
|
7668
7777
|
}
|
|
7669
|
-
const projectHash = hashContent(
|
|
7778
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7670
7779
|
return `${projectHash}:${branchName}`;
|
|
7671
7780
|
}
|
|
7672
7781
|
getLegacyBranchCatalogKey() {
|
|
7673
7782
|
return this.currentBranch || "default";
|
|
7674
7783
|
}
|
|
7675
7784
|
getLegacyMigrationMetadataKey() {
|
|
7676
|
-
const projectHash = hashContent(
|
|
7785
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7677
7786
|
return `index.globalBranchMigration.${projectHash}`;
|
|
7678
7787
|
}
|
|
7679
7788
|
getProjectEmbeddingStrategyMetadataKey() {
|
|
7680
|
-
const projectHash = hashContent(
|
|
7789
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7681
7790
|
return `index.embeddingStrategyVersion.${projectHash}`;
|
|
7682
7791
|
}
|
|
7683
7792
|
getProjectForceReembedMetadataKey() {
|
|
7684
|
-
const projectHash = hashContent(
|
|
7793
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7685
7794
|
return `index.forceReembed.${projectHash}`;
|
|
7686
7795
|
}
|
|
7687
7796
|
hasProjectForceReembedPending() {
|
|
@@ -7775,7 +7884,7 @@ var Indexer = class {
|
|
|
7775
7884
|
if (!this.database) {
|
|
7776
7885
|
return { chunkIds, symbolIds };
|
|
7777
7886
|
}
|
|
7778
|
-
const projectRootPath =
|
|
7887
|
+
const projectRootPath = path12.resolve(this.projectRoot);
|
|
7779
7888
|
const projectLocalFilePaths = /* @__PURE__ */ new Set([
|
|
7780
7889
|
...Array.from(this.fileHashCache.keys()).filter(
|
|
7781
7890
|
(filePath) => this.isFileInCurrentScope(filePath, roots) && isPathWithinRoot(filePath, projectRootPath)
|
|
@@ -7798,7 +7907,7 @@ var Indexer = class {
|
|
|
7798
7907
|
if (this.config.scope !== "global") {
|
|
7799
7908
|
return this.getBranchCatalogCleanupKeys();
|
|
7800
7909
|
}
|
|
7801
|
-
const projectHash = hashContent(
|
|
7910
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7802
7911
|
const keys = /* @__PURE__ */ new Set();
|
|
7803
7912
|
const projectChunkIdSet = new Set(projectChunkIds);
|
|
7804
7913
|
const projectSymbolIdSet = new Set(projectSymbolIds);
|
|
@@ -7882,7 +7991,7 @@ var Indexer = class {
|
|
|
7882
7991
|
if (!this.database || this.config.scope !== "global") {
|
|
7883
7992
|
return false;
|
|
7884
7993
|
}
|
|
7885
|
-
const projectHash = hashContent(
|
|
7994
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7886
7995
|
const roots = this.getScopedRoots();
|
|
7887
7996
|
const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots);
|
|
7888
7997
|
return this.database.getAllBranches().some(
|
|
@@ -7916,7 +8025,7 @@ var Indexer = class {
|
|
|
7916
8025
|
...Array.from(this.fileHashCache.keys()).filter((filePath) => this.isFileInCurrentScope(filePath, roots)),
|
|
7917
8026
|
...scopedEntries.map(({ metadata }) => metadata.filePath)
|
|
7918
8027
|
]);
|
|
7919
|
-
const projectRootPath =
|
|
8028
|
+
const projectRootPath = path12.resolve(this.projectRoot);
|
|
7920
8029
|
const projectLocalFilePaths = new Set(
|
|
7921
8030
|
Array.from(filePaths).filter((filePath) => isPathWithinRoot(filePath, projectRootPath))
|
|
7922
8031
|
);
|
|
@@ -7985,24 +8094,24 @@ var Indexer = class {
|
|
|
7985
8094
|
};
|
|
7986
8095
|
}
|
|
7987
8096
|
checkForInterruptedIndexing() {
|
|
7988
|
-
return (0,
|
|
8097
|
+
return (0, import_fs7.existsSync)(this.indexingLockPath);
|
|
7989
8098
|
}
|
|
7990
8099
|
acquireIndexingLock() {
|
|
7991
8100
|
const lockData = {
|
|
7992
8101
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7993
8102
|
pid: process.pid
|
|
7994
8103
|
};
|
|
7995
|
-
(0,
|
|
8104
|
+
(0, import_fs7.writeFileSync)(this.indexingLockPath, JSON.stringify(lockData));
|
|
7996
8105
|
}
|
|
7997
8106
|
releaseIndexingLock() {
|
|
7998
|
-
if ((0,
|
|
7999
|
-
(0,
|
|
8107
|
+
if ((0, import_fs7.existsSync)(this.indexingLockPath)) {
|
|
8108
|
+
(0, import_fs7.unlinkSync)(this.indexingLockPath);
|
|
8000
8109
|
}
|
|
8001
8110
|
}
|
|
8002
8111
|
async recoverFromInterruptedIndexing() {
|
|
8003
8112
|
this.logger.warn("Detected interrupted indexing session, recovering...");
|
|
8004
|
-
if ((0,
|
|
8005
|
-
(0,
|
|
8113
|
+
if ((0, import_fs7.existsSync)(this.fileHashCachePath)) {
|
|
8114
|
+
(0, import_fs7.unlinkSync)(this.fileHashCachePath);
|
|
8006
8115
|
}
|
|
8007
8116
|
await this.healthCheck();
|
|
8008
8117
|
this.releaseIndexingLock();
|
|
@@ -8011,15 +8120,20 @@ var Indexer = class {
|
|
|
8011
8120
|
loadFailedBatches(maxChunkTokens) {
|
|
8012
8121
|
try {
|
|
8013
8122
|
return this.loadSerializedFailedBatches().map((batch) => normalizeFailedBatch(batch, maxChunkTokens)).filter((batch) => batch !== null);
|
|
8014
|
-
} catch {
|
|
8123
|
+
} catch (error) {
|
|
8124
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
8125
|
+
this.logger.warn("Failed to load failed batch state, skipping persisted retries", {
|
|
8126
|
+
failedBatchesPath: this.failedBatchesPath,
|
|
8127
|
+
error: message
|
|
8128
|
+
});
|
|
8015
8129
|
return [];
|
|
8016
8130
|
}
|
|
8017
8131
|
}
|
|
8018
8132
|
loadSerializedFailedBatches() {
|
|
8019
|
-
if (!(0,
|
|
8133
|
+
if (!(0, import_fs7.existsSync)(this.failedBatchesPath)) {
|
|
8020
8134
|
return [];
|
|
8021
8135
|
}
|
|
8022
|
-
const data = (0,
|
|
8136
|
+
const data = (0, import_fs7.readFileSync)(this.failedBatchesPath, "utf-8");
|
|
8023
8137
|
const parsed = JSON.parse(data);
|
|
8024
8138
|
return parsed.map((batch) => {
|
|
8025
8139
|
const chunks = Array.isArray(batch.chunks) ? batch.chunks : [];
|
|
@@ -8036,15 +8150,15 @@ var Indexer = class {
|
|
|
8036
8150
|
}
|
|
8037
8151
|
saveFailedBatches(batches) {
|
|
8038
8152
|
if (batches.length === 0) {
|
|
8039
|
-
if ((0,
|
|
8153
|
+
if ((0, import_fs7.existsSync)(this.failedBatchesPath)) {
|
|
8040
8154
|
try {
|
|
8041
|
-
(0,
|
|
8155
|
+
(0, import_fs7.unlinkSync)(this.failedBatchesPath);
|
|
8042
8156
|
} catch {
|
|
8043
8157
|
}
|
|
8044
8158
|
}
|
|
8045
8159
|
return;
|
|
8046
8160
|
}
|
|
8047
|
-
(0,
|
|
8161
|
+
(0, import_fs7.writeFileSync)(this.failedBatchesPath, JSON.stringify(batches, null, 2));
|
|
8048
8162
|
}
|
|
8049
8163
|
collectRetryableFailedChunks(currentFileHashes, unchangedFilePaths, maxChunkTokens) {
|
|
8050
8164
|
const retryableById = /* @__PURE__ */ new Map();
|
|
@@ -8224,7 +8338,7 @@ var Indexer = class {
|
|
|
8224
8338
|
const intent = isLikelyImplementationPath(candidate.metadata.filePath) ? "implementation" : "doc_or_test";
|
|
8225
8339
|
parts.push(`intent_hint: ${intent}`);
|
|
8226
8340
|
try {
|
|
8227
|
-
const fileContent = await
|
|
8341
|
+
const fileContent = await import_fs7.promises.readFile(candidate.metadata.filePath, "utf-8");
|
|
8228
8342
|
const lines = fileContent.split("\n");
|
|
8229
8343
|
const snippetStartLine = Math.max(1, candidate.metadata.startLine - 2);
|
|
8230
8344
|
const snippetEndLine = Math.min(lines.length, candidate.metadata.endLine + 2);
|
|
@@ -8269,26 +8383,26 @@ var Indexer = class {
|
|
|
8269
8383
|
});
|
|
8270
8384
|
}
|
|
8271
8385
|
}
|
|
8272
|
-
await
|
|
8386
|
+
await import_fs7.promises.mkdir(this.indexPath, { recursive: true });
|
|
8273
8387
|
const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
|
|
8274
|
-
const storePath =
|
|
8388
|
+
const storePath = path12.join(this.indexPath, "vectors");
|
|
8275
8389
|
this.store = new VectorStore(storePath, dimensions);
|
|
8276
|
-
const indexFilePath =
|
|
8277
|
-
if ((0,
|
|
8390
|
+
const indexFilePath = path12.join(this.indexPath, "vectors.usearch");
|
|
8391
|
+
if ((0, import_fs7.existsSync)(indexFilePath)) {
|
|
8278
8392
|
this.store.load();
|
|
8279
8393
|
}
|
|
8280
|
-
const invertedIndexPath =
|
|
8394
|
+
const invertedIndexPath = path12.join(this.indexPath, "inverted-index.json");
|
|
8281
8395
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
8282
8396
|
try {
|
|
8283
8397
|
this.invertedIndex.load();
|
|
8284
8398
|
} catch {
|
|
8285
|
-
if ((0,
|
|
8286
|
-
await
|
|
8399
|
+
if ((0, import_fs7.existsSync)(invertedIndexPath)) {
|
|
8400
|
+
await import_fs7.promises.unlink(invertedIndexPath);
|
|
8287
8401
|
}
|
|
8288
8402
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
8289
8403
|
}
|
|
8290
|
-
const dbPath =
|
|
8291
|
-
let dbIsNew = !(0,
|
|
8404
|
+
const dbPath = path12.join(this.indexPath, "codebase.db");
|
|
8405
|
+
let dbIsNew = !(0, import_fs7.existsSync)(dbPath);
|
|
8292
8406
|
try {
|
|
8293
8407
|
this.database = new Database(dbPath);
|
|
8294
8408
|
} catch (error) {
|
|
@@ -8368,7 +8482,7 @@ var Indexer = class {
|
|
|
8368
8482
|
if (await this.tryResetCorruptedIndex("running automatic orphan garbage collection", error)) {
|
|
8369
8483
|
return {
|
|
8370
8484
|
resetCorruptedIndex: true,
|
|
8371
|
-
warning: this.getCorruptedIndexWarning(
|
|
8485
|
+
warning: this.getCorruptedIndexWarning(path12.join(this.indexPath, "codebase.db"))
|
|
8372
8486
|
};
|
|
8373
8487
|
}
|
|
8374
8488
|
throw error;
|
|
@@ -8383,7 +8497,7 @@ var Indexer = class {
|
|
|
8383
8497
|
return;
|
|
8384
8498
|
}
|
|
8385
8499
|
const retainedEntries = store.getAllMetadata().filter(({ key }) => !excludedSet.has(key));
|
|
8386
|
-
const storeBasePath =
|
|
8500
|
+
const storeBasePath = path12.join(this.indexPath, "vectors");
|
|
8387
8501
|
const storeIndexPath = `${storeBasePath}.usearch`;
|
|
8388
8502
|
const storeMetadataPath = `${storeBasePath}.meta.json`;
|
|
8389
8503
|
const backupIndexPath = `${storeIndexPath}.bak`;
|
|
@@ -8392,19 +8506,19 @@ var Indexer = class {
|
|
|
8392
8506
|
let backedUpMetadata = false;
|
|
8393
8507
|
let rebuiltCount = 0;
|
|
8394
8508
|
let skippedCount = 0;
|
|
8395
|
-
if ((0,
|
|
8396
|
-
(0,
|
|
8509
|
+
if ((0, import_fs7.existsSync)(backupIndexPath)) {
|
|
8510
|
+
(0, import_fs7.unlinkSync)(backupIndexPath);
|
|
8397
8511
|
}
|
|
8398
|
-
if ((0,
|
|
8399
|
-
(0,
|
|
8512
|
+
if ((0, import_fs7.existsSync)(backupMetadataPath)) {
|
|
8513
|
+
(0, import_fs7.unlinkSync)(backupMetadataPath);
|
|
8400
8514
|
}
|
|
8401
8515
|
try {
|
|
8402
|
-
if ((0,
|
|
8403
|
-
(0,
|
|
8516
|
+
if ((0, import_fs7.existsSync)(storeIndexPath)) {
|
|
8517
|
+
(0, import_fs7.renameSync)(storeIndexPath, backupIndexPath);
|
|
8404
8518
|
backedUpIndex = true;
|
|
8405
8519
|
}
|
|
8406
|
-
if ((0,
|
|
8407
|
-
(0,
|
|
8520
|
+
if ((0, import_fs7.existsSync)(storeMetadataPath)) {
|
|
8521
|
+
(0, import_fs7.renameSync)(storeMetadataPath, backupMetadataPath);
|
|
8408
8522
|
backedUpMetadata = true;
|
|
8409
8523
|
}
|
|
8410
8524
|
store.clear();
|
|
@@ -8424,11 +8538,11 @@ var Indexer = class {
|
|
|
8424
8538
|
rebuiltCount += 1;
|
|
8425
8539
|
}
|
|
8426
8540
|
store.save();
|
|
8427
|
-
if (backedUpIndex && (0,
|
|
8428
|
-
(0,
|
|
8541
|
+
if (backedUpIndex && (0, import_fs7.existsSync)(backupIndexPath)) {
|
|
8542
|
+
(0, import_fs7.unlinkSync)(backupIndexPath);
|
|
8429
8543
|
}
|
|
8430
|
-
if (backedUpMetadata && (0,
|
|
8431
|
-
(0,
|
|
8544
|
+
if (backedUpMetadata && (0, import_fs7.existsSync)(backupMetadataPath)) {
|
|
8545
|
+
(0, import_fs7.unlinkSync)(backupMetadataPath);
|
|
8432
8546
|
}
|
|
8433
8547
|
this.logger.gc("info", "Rebuilt vector store to avoid native remove", {
|
|
8434
8548
|
excludedChunks: excludedSet.size,
|
|
@@ -8440,17 +8554,17 @@ var Indexer = class {
|
|
|
8440
8554
|
store.clear();
|
|
8441
8555
|
} catch {
|
|
8442
8556
|
}
|
|
8443
|
-
if ((0,
|
|
8444
|
-
(0,
|
|
8557
|
+
if ((0, import_fs7.existsSync)(storeIndexPath)) {
|
|
8558
|
+
(0, import_fs7.unlinkSync)(storeIndexPath);
|
|
8445
8559
|
}
|
|
8446
|
-
if ((0,
|
|
8447
|
-
(0,
|
|
8560
|
+
if ((0, import_fs7.existsSync)(storeMetadataPath)) {
|
|
8561
|
+
(0, import_fs7.unlinkSync)(storeMetadataPath);
|
|
8448
8562
|
}
|
|
8449
|
-
if (backedUpIndex && (0,
|
|
8450
|
-
(0,
|
|
8563
|
+
if (backedUpIndex && (0, import_fs7.existsSync)(backupIndexPath)) {
|
|
8564
|
+
(0, import_fs7.renameSync)(backupIndexPath, storeIndexPath);
|
|
8451
8565
|
}
|
|
8452
|
-
if (backedUpMetadata && (0,
|
|
8453
|
-
(0,
|
|
8566
|
+
if (backedUpMetadata && (0, import_fs7.existsSync)(backupMetadataPath)) {
|
|
8567
|
+
(0, import_fs7.renameSync)(backupMetadataPath, storeMetadataPath);
|
|
8454
8568
|
}
|
|
8455
8569
|
if (backedUpIndex || backedUpMetadata) {
|
|
8456
8570
|
store.load();
|
|
@@ -8468,7 +8582,7 @@ var Indexer = class {
|
|
|
8468
8582
|
if (!isSqliteCorruptionError(error)) {
|
|
8469
8583
|
return false;
|
|
8470
8584
|
}
|
|
8471
|
-
const dbPath =
|
|
8585
|
+
const dbPath = path12.join(this.indexPath, "codebase.db");
|
|
8472
8586
|
const warning = this.getCorruptedIndexWarning(dbPath);
|
|
8473
8587
|
const errorMessage = getErrorMessage(error);
|
|
8474
8588
|
if (this.config.scope === "global") {
|
|
@@ -8491,23 +8605,23 @@ var Indexer = class {
|
|
|
8491
8605
|
this.indexCompatibility = null;
|
|
8492
8606
|
this.fileHashCache.clear();
|
|
8493
8607
|
const resetPaths = [
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
|
|
8498
|
-
|
|
8499
|
-
|
|
8500
|
-
|
|
8501
|
-
|
|
8502
|
-
|
|
8608
|
+
path12.join(this.indexPath, "codebase.db"),
|
|
8609
|
+
path12.join(this.indexPath, "codebase.db-shm"),
|
|
8610
|
+
path12.join(this.indexPath, "codebase.db-wal"),
|
|
8611
|
+
path12.join(this.indexPath, "vectors.usearch"),
|
|
8612
|
+
path12.join(this.indexPath, "inverted-index.json"),
|
|
8613
|
+
path12.join(this.indexPath, "file-hashes.json"),
|
|
8614
|
+
path12.join(this.indexPath, "failed-batches.json"),
|
|
8615
|
+
path12.join(this.indexPath, "indexing.lock"),
|
|
8616
|
+
path12.join(this.indexPath, "vectors")
|
|
8503
8617
|
];
|
|
8504
8618
|
await Promise.all(resetPaths.map(async (targetPath) => {
|
|
8505
8619
|
try {
|
|
8506
|
-
await
|
|
8620
|
+
await import_fs7.promises.rm(targetPath, { recursive: true, force: true });
|
|
8507
8621
|
} catch {
|
|
8508
8622
|
}
|
|
8509
8623
|
}));
|
|
8510
|
-
await
|
|
8624
|
+
await import_fs7.promises.mkdir(this.indexPath, { recursive: true });
|
|
8511
8625
|
return true;
|
|
8512
8626
|
}
|
|
8513
8627
|
migrateFromLegacyIndex() {
|
|
@@ -8712,7 +8826,7 @@ var Indexer = class {
|
|
|
8712
8826
|
unchangedFilePaths.add(f.path);
|
|
8713
8827
|
this.logger.recordCacheHit();
|
|
8714
8828
|
} else {
|
|
8715
|
-
const content = await
|
|
8829
|
+
const content = await import_fs7.promises.readFile(f.path, "utf-8");
|
|
8716
8830
|
changedFiles.push({ path: f.path, content, hash: currentHash });
|
|
8717
8831
|
this.logger.recordCacheMiss();
|
|
8718
8832
|
}
|
|
@@ -8764,7 +8878,7 @@ var Indexer = class {
|
|
|
8764
8878
|
for (const parsed of parsedFiles) {
|
|
8765
8879
|
currentFilePaths.add(parsed.path);
|
|
8766
8880
|
if (parsed.chunks.length === 0) {
|
|
8767
|
-
const relativePath =
|
|
8881
|
+
const relativePath = path12.relative(this.projectRoot, parsed.path);
|
|
8768
8882
|
stats.parseFailures.push(relativePath);
|
|
8769
8883
|
}
|
|
8770
8884
|
let fileChunkCount = 0;
|
|
@@ -9047,7 +9161,7 @@ var Indexer = class {
|
|
|
9047
9161
|
for (const requestBatch of requestBatches) {
|
|
9048
9162
|
queue.add(async () => {
|
|
9049
9163
|
if (rateLimitBackoffMs > 0) {
|
|
9050
|
-
await new Promise((
|
|
9164
|
+
await new Promise((resolve11) => setTimeout(resolve11, rateLimitBackoffMs));
|
|
9051
9165
|
}
|
|
9052
9166
|
try {
|
|
9053
9167
|
const result = await pRetry(
|
|
@@ -9496,7 +9610,7 @@ var Indexer = class {
|
|
|
9496
9610
|
let contextEndLine = r.metadata.endLine;
|
|
9497
9611
|
if (!metadataOnly && this.config.search.includeContext) {
|
|
9498
9612
|
try {
|
|
9499
|
-
const fileContent = await
|
|
9613
|
+
const fileContent = await import_fs7.promises.readFile(
|
|
9500
9614
|
r.metadata.filePath,
|
|
9501
9615
|
"utf-8"
|
|
9502
9616
|
);
|
|
@@ -9608,8 +9722,8 @@ var Indexer = class {
|
|
|
9608
9722
|
this.indexCompatibility = compatibility;
|
|
9609
9723
|
return;
|
|
9610
9724
|
}
|
|
9611
|
-
const localProjectIndexPath =
|
|
9612
|
-
if (
|
|
9725
|
+
const localProjectIndexPath = path12.join(this.projectRoot, ".opencode", "index");
|
|
9726
|
+
if (path12.resolve(this.indexPath) !== path12.resolve(localProjectIndexPath)) {
|
|
9613
9727
|
throw new Error(
|
|
9614
9728
|
"Project-scoped force rebuild is unsafe while using an inherited worktree index. Create a local project config boundary before clearing the index."
|
|
9615
9729
|
);
|
|
@@ -9648,7 +9762,7 @@ var Indexer = class {
|
|
|
9648
9762
|
const removedChunkKeys = [];
|
|
9649
9763
|
const chunkKeysByRemovedFile = /* @__PURE__ */ new Map();
|
|
9650
9764
|
for (const [filePath, chunkKeys] of filePathsToChunkKeys) {
|
|
9651
|
-
if (!(0,
|
|
9765
|
+
if (!(0, import_fs7.existsSync)(filePath)) {
|
|
9652
9766
|
chunkKeysByRemovedFile.set(filePath, chunkKeys);
|
|
9653
9767
|
for (const key of chunkKeys) {
|
|
9654
9768
|
removedChunkKeys.push(key);
|
|
@@ -9697,7 +9811,7 @@ var Indexer = class {
|
|
|
9697
9811
|
gcOrphanSymbols: 0,
|
|
9698
9812
|
gcOrphanCallEdges: 0,
|
|
9699
9813
|
resetCorruptedIndex: true,
|
|
9700
|
-
warning: this.getCorruptedIndexWarning(
|
|
9814
|
+
warning: this.getCorruptedIndexWarning(path12.join(this.indexPath, "codebase.db"))
|
|
9701
9815
|
};
|
|
9702
9816
|
}
|
|
9703
9817
|
this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
|
|
@@ -10000,7 +10114,7 @@ var Indexer = class {
|
|
|
10000
10114
|
let content = "";
|
|
10001
10115
|
if (this.config.search.includeContext) {
|
|
10002
10116
|
try {
|
|
10003
|
-
const fileContent = await
|
|
10117
|
+
const fileContent = await import_fs7.promises.readFile(
|
|
10004
10118
|
r.metadata.filePath,
|
|
10005
10119
|
"utf-8"
|
|
10006
10120
|
);
|
|
@@ -10249,12 +10363,15 @@ function formatLogs(logs) {
|
|
|
10249
10363
|
return `[${l.timestamp}] [${l.level.toUpperCase()}] [${l.category}] ${l.message}${dataStr}`;
|
|
10250
10364
|
}).join("\n");
|
|
10251
10365
|
}
|
|
10366
|
+
function formatResultHeader(result, index) {
|
|
10367
|
+
return result.name ? `[${index + 1}] ${result.chunkType} "${result.name}" in ${result.filePath}:${result.startLine}-${result.endLine}` : `[${index + 1}] ${result.chunkType} in ${result.filePath}:${result.startLine}-${result.endLine}`;
|
|
10368
|
+
}
|
|
10252
10369
|
function formatDefinitionLookup(results, query) {
|
|
10253
10370
|
if (results.length === 0) {
|
|
10254
10371
|
return `No definition found for "${query}". Try codebase_search for broader discovery, or verify the symbol name.`;
|
|
10255
10372
|
}
|
|
10256
10373
|
const formatted = results.map((r, idx) => {
|
|
10257
|
-
const header = r
|
|
10374
|
+
const header = formatResultHeader(r, idx);
|
|
10258
10375
|
return `${header} (score: ${r.score.toFixed(2)})
|
|
10259
10376
|
\`\`\`
|
|
10260
10377
|
${truncateContent(r.content)}
|
|
@@ -10264,7 +10381,7 @@ ${truncateContent(r.content)}
|
|
|
10264
10381
|
}
|
|
10265
10382
|
function formatSearchResults(results, scoreFormat = "similarity") {
|
|
10266
10383
|
const formatted = results.map((r, idx) => {
|
|
10267
|
-
const header = r
|
|
10384
|
+
const header = formatResultHeader(r, idx);
|
|
10268
10385
|
const scoreLabel = scoreFormat === "similarity" ? `(similarity: ${(r.score * 100).toFixed(1)}%)` : `(score: ${r.score.toFixed(2)})`;
|
|
10269
10386
|
return `${header} ${scoreLabel}
|
|
10270
10387
|
\`\`\`
|
|
@@ -10274,9 +10391,99 @@ ${truncateContent(r.content)}
|
|
|
10274
10391
|
return formatted.join("\n\n");
|
|
10275
10392
|
}
|
|
10276
10393
|
|
|
10394
|
+
// src/tools/knowledge-base-paths.ts
|
|
10395
|
+
var path13 = __toESM(require("path"), 1);
|
|
10396
|
+
function resolveConfigPathValue(value, baseDir) {
|
|
10397
|
+
const trimmed = value.trim();
|
|
10398
|
+
if (!trimmed) {
|
|
10399
|
+
return trimmed;
|
|
10400
|
+
}
|
|
10401
|
+
const absolutePath = path13.isAbsolute(trimmed) ? trimmed : path13.resolve(baseDir, trimmed);
|
|
10402
|
+
return path13.normalize(absolutePath);
|
|
10403
|
+
}
|
|
10404
|
+
function serializeConfigPathValue(value, baseDir) {
|
|
10405
|
+
const trimmed = value.trim();
|
|
10406
|
+
if (!trimmed) {
|
|
10407
|
+
return trimmed;
|
|
10408
|
+
}
|
|
10409
|
+
if (!path13.isAbsolute(trimmed)) {
|
|
10410
|
+
return normalizePathSeparators(path13.normalize(trimmed));
|
|
10411
|
+
}
|
|
10412
|
+
const relativePath = path13.relative(baseDir, trimmed);
|
|
10413
|
+
if (!relativePath || !relativePath.startsWith("..") && !path13.isAbsolute(relativePath)) {
|
|
10414
|
+
return normalizePathSeparators(path13.normalize(relativePath || "."));
|
|
10415
|
+
}
|
|
10416
|
+
return path13.normalize(trimmed);
|
|
10417
|
+
}
|
|
10418
|
+
function resolveKnowledgeBasePath(value, projectRoot) {
|
|
10419
|
+
return path13.isAbsolute(value) ? value : path13.resolve(projectRoot, value);
|
|
10420
|
+
}
|
|
10421
|
+
function normalizeKnowledgeBasePath2(value, projectRoot) {
|
|
10422
|
+
return path13.normalize(resolveKnowledgeBasePath(value, projectRoot));
|
|
10423
|
+
}
|
|
10424
|
+
function hasMatchingKnowledgeBasePath(knowledgeBases, inputPath, projectRoot) {
|
|
10425
|
+
const normalizedInput = path13.normalize(inputPath);
|
|
10426
|
+
return knowledgeBases.some((kb) => normalizeKnowledgeBasePath2(kb, projectRoot) === normalizedInput);
|
|
10427
|
+
}
|
|
10428
|
+
function findKnowledgeBasePathIndex(knowledgeBases, inputPath, projectRoot) {
|
|
10429
|
+
const normalizedInput = path13.normalize(inputPath);
|
|
10430
|
+
return knowledgeBases.findIndex(
|
|
10431
|
+
(kb) => path13.normalize(kb) === normalizedInput || normalizeKnowledgeBasePath2(kb, projectRoot) === normalizedInput
|
|
10432
|
+
);
|
|
10433
|
+
}
|
|
10434
|
+
|
|
10277
10435
|
// src/tools/index.ts
|
|
10278
|
-
var
|
|
10279
|
-
var
|
|
10436
|
+
var import_fs9 = require("fs");
|
|
10437
|
+
var path15 = __toESM(require("path"), 1);
|
|
10438
|
+
|
|
10439
|
+
// src/tools/config-state.ts
|
|
10440
|
+
var import_fs8 = require("fs");
|
|
10441
|
+
var path14 = __toESM(require("path"), 1);
|
|
10442
|
+
function normalizeKnowledgeBasePaths(config, projectRoot) {
|
|
10443
|
+
const normalized = { ...config };
|
|
10444
|
+
if (Array.isArray(normalized.knowledgeBases)) {
|
|
10445
|
+
normalized.knowledgeBases = normalized.knowledgeBases.map(
|
|
10446
|
+
(kb) => resolveConfigPathValue(kb, projectRoot)
|
|
10447
|
+
);
|
|
10448
|
+
}
|
|
10449
|
+
return normalized;
|
|
10450
|
+
}
|
|
10451
|
+
function toConfigRecord(rawConfig) {
|
|
10452
|
+
if (!rawConfig || typeof rawConfig !== "object") {
|
|
10453
|
+
return {};
|
|
10454
|
+
}
|
|
10455
|
+
return { ...rawConfig };
|
|
10456
|
+
}
|
|
10457
|
+
function getConfigPath(projectRoot) {
|
|
10458
|
+
return resolveWritableProjectConfigPath(projectRoot);
|
|
10459
|
+
}
|
|
10460
|
+
function loadRuntimeConfig(projectRoot) {
|
|
10461
|
+
return normalizeKnowledgeBasePaths(toConfigRecord(loadMergedConfig(projectRoot)), projectRoot);
|
|
10462
|
+
}
|
|
10463
|
+
function loadEditableConfig(projectRoot) {
|
|
10464
|
+
return normalizeKnowledgeBasePaths(toConfigRecord(loadProjectConfigLayer(projectRoot)), projectRoot);
|
|
10465
|
+
}
|
|
10466
|
+
function saveConfig(projectRoot, config) {
|
|
10467
|
+
const configPath = getConfigPath(projectRoot);
|
|
10468
|
+
const configDir = path14.dirname(configPath);
|
|
10469
|
+
const configBaseDir = path14.dirname(configDir);
|
|
10470
|
+
if (!(0, import_fs8.existsSync)(configDir)) {
|
|
10471
|
+
(0, import_fs8.mkdirSync)(configDir, { recursive: true });
|
|
10472
|
+
}
|
|
10473
|
+
const serializableConfig = { ...config };
|
|
10474
|
+
if (Array.isArray(serializableConfig.knowledgeBases)) {
|
|
10475
|
+
serializableConfig.knowledgeBases = serializableConfig.knowledgeBases.map(
|
|
10476
|
+
(kb) => serializeConfigPathValue(kb, configBaseDir)
|
|
10477
|
+
);
|
|
10478
|
+
}
|
|
10479
|
+
(0, import_fs8.writeFileSync)(configPath, JSON.stringify(serializableConfig, null, 2) + "\n", "utf-8");
|
|
10480
|
+
}
|
|
10481
|
+
|
|
10482
|
+
// src/tools/index.ts
|
|
10483
|
+
var os5 = __toESM(require("os"), 1);
|
|
10484
|
+
function ensureStringArray(value) {
|
|
10485
|
+
return Array.isArray(value) ? value : [];
|
|
10486
|
+
}
|
|
10280
10487
|
var z = import_plugin.tool.schema;
|
|
10281
10488
|
var sharedIndexer = null;
|
|
10282
10489
|
var sharedProjectRoot = "";
|
|
@@ -10291,20 +10498,20 @@ function refreshIndexerFromConfig() {
|
|
|
10291
10498
|
if (!sharedProjectRoot) {
|
|
10292
10499
|
throw new Error("Codebase index tools not initialized. Plugin may not be loaded correctly.");
|
|
10293
10500
|
}
|
|
10294
|
-
sharedIndexer = new Indexer(sharedProjectRoot, parseConfig(loadRuntimeConfig()));
|
|
10501
|
+
sharedIndexer = new Indexer(sharedProjectRoot, parseConfig(loadRuntimeConfig(sharedProjectRoot)));
|
|
10295
10502
|
}
|
|
10296
10503
|
function shouldForceLocalizeProjectIndex() {
|
|
10297
|
-
const currentConfig = parseConfig(loadRuntimeConfig());
|
|
10504
|
+
const currentConfig = parseConfig(loadRuntimeConfig(sharedProjectRoot));
|
|
10298
10505
|
if (currentConfig.scope !== "project") {
|
|
10299
10506
|
return false;
|
|
10300
10507
|
}
|
|
10301
|
-
const localIndexPath =
|
|
10508
|
+
const localIndexPath = path15.join(sharedProjectRoot, ".opencode", "index");
|
|
10302
10509
|
const mainRepoRoot = resolveWorktreeMainRepoRoot(sharedProjectRoot);
|
|
10303
10510
|
if (!mainRepoRoot) {
|
|
10304
10511
|
return false;
|
|
10305
10512
|
}
|
|
10306
|
-
const inheritedIndexPath =
|
|
10307
|
-
return !(0,
|
|
10513
|
+
const inheritedIndexPath = path15.join(mainRepoRoot, ".opencode", "index");
|
|
10514
|
+
return !(0, import_fs9.existsSync)(localIndexPath) && (0, import_fs9.existsSync)(inheritedIndexPath);
|
|
10308
10515
|
}
|
|
10309
10516
|
function getIndexer() {
|
|
10310
10517
|
if (!sharedIndexer) {
|
|
@@ -10312,76 +10519,6 @@ function getIndexer() {
|
|
|
10312
10519
|
}
|
|
10313
10520
|
return sharedIndexer;
|
|
10314
10521
|
}
|
|
10315
|
-
function getConfigPath() {
|
|
10316
|
-
return resolveWritableProjectConfigPath(sharedProjectRoot);
|
|
10317
|
-
}
|
|
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() {
|
|
10351
|
-
const rawConfig = loadMergedConfig(sharedProjectRoot);
|
|
10352
|
-
const config = {};
|
|
10353
|
-
if (rawConfig && typeof rawConfig === "object") {
|
|
10354
|
-
for (const key of Object.keys(rawConfig)) {
|
|
10355
|
-
config[key] = rawConfig[key];
|
|
10356
|
-
}
|
|
10357
|
-
}
|
|
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
|
-
}
|
|
10367
|
-
}
|
|
10368
|
-
return normalizeKnowledgeBasePaths(config);
|
|
10369
|
-
}
|
|
10370
|
-
function saveConfig(config) {
|
|
10371
|
-
const configPath = getConfigPath();
|
|
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");
|
|
10384
|
-
}
|
|
10385
10522
|
var codebase_peek = (0, import_plugin.tool)({
|
|
10386
10523
|
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.",
|
|
10387
10524
|
args: {
|
|
@@ -10601,36 +10738,64 @@ var add_knowledge_base = (0, import_plugin.tool)({
|
|
|
10601
10738
|
},
|
|
10602
10739
|
async execute(args) {
|
|
10603
10740
|
const inputPath = args.path.trim();
|
|
10604
|
-
const
|
|
10605
|
-
|
|
10606
|
-
|
|
10741
|
+
const normalizedPath = path15.resolve(
|
|
10742
|
+
path15.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, sharedProjectRoot)
|
|
10743
|
+
);
|
|
10744
|
+
if (!(0, import_fs9.existsSync)(normalizedPath)) {
|
|
10745
|
+
return `Error: Directory does not exist: ${normalizedPath}`;
|
|
10746
|
+
}
|
|
10747
|
+
let realPath;
|
|
10748
|
+
try {
|
|
10749
|
+
realPath = (0, import_fs9.realpathSync)(normalizedPath);
|
|
10750
|
+
} catch {
|
|
10751
|
+
return `Error: Cannot resolve path: ${normalizedPath}`;
|
|
10752
|
+
}
|
|
10753
|
+
const blockedPrefixes = [
|
|
10754
|
+
"/etc",
|
|
10755
|
+
"/proc",
|
|
10756
|
+
"/sys",
|
|
10757
|
+
"/dev",
|
|
10758
|
+
"/boot",
|
|
10759
|
+
"/root",
|
|
10760
|
+
"/var/run",
|
|
10761
|
+
"/var/log"
|
|
10762
|
+
];
|
|
10763
|
+
const homeDir = os5.homedir();
|
|
10764
|
+
const sensitiveDotDirs = [".ssh", ".gnupg", ".aws", ".config/gcloud", ".docker", ".kube"];
|
|
10765
|
+
for (const prefix of blockedPrefixes) {
|
|
10766
|
+
if (realPath === prefix || realPath.startsWith(prefix + "/")) {
|
|
10767
|
+
return `Error: Adding system directory as knowledge base is not allowed: ${normalizedPath}`;
|
|
10768
|
+
}
|
|
10769
|
+
}
|
|
10770
|
+
for (const dotDir of sensitiveDotDirs) {
|
|
10771
|
+
const sensitiveDir = path15.join(homeDir, dotDir);
|
|
10772
|
+
if (realPath === sensitiveDir || realPath.startsWith(sensitiveDir + "/")) {
|
|
10773
|
+
return `Error: Adding sensitive directory as knowledge base is not allowed: ${normalizedPath}`;
|
|
10774
|
+
}
|
|
10607
10775
|
}
|
|
10608
10776
|
try {
|
|
10609
|
-
const stat4 = (0,
|
|
10777
|
+
const stat4 = (0, import_fs9.statSync)(normalizedPath);
|
|
10610
10778
|
if (!stat4.isDirectory()) {
|
|
10611
|
-
return `Error: Path is not a directory: ${
|
|
10779
|
+
return `Error: Path is not a directory: ${normalizedPath}`;
|
|
10612
10780
|
}
|
|
10613
10781
|
} catch (error) {
|
|
10614
|
-
return `Error: Cannot access directory: ${
|
|
10782
|
+
return `Error: Cannot access directory: ${normalizedPath} - ${error instanceof Error ? error.message : String(error)}`;
|
|
10615
10783
|
}
|
|
10616
|
-
const config = loadEditableConfig();
|
|
10617
|
-
const knowledgeBases =
|
|
10618
|
-
const
|
|
10619
|
-
const alreadyExists = knowledgeBases.some(
|
|
10620
|
-
(kb) => path9.normalize(path9.isAbsolute(kb) ? kb : path9.resolve(sharedProjectRoot, kb)) === normalizedPath
|
|
10621
|
-
);
|
|
10784
|
+
const config = loadEditableConfig(sharedProjectRoot);
|
|
10785
|
+
const knowledgeBases = ensureStringArray(config.knowledgeBases);
|
|
10786
|
+
const alreadyExists = hasMatchingKnowledgeBasePath(knowledgeBases, normalizedPath, sharedProjectRoot);
|
|
10622
10787
|
if (alreadyExists) {
|
|
10623
|
-
return `Knowledge base already configured: ${
|
|
10788
|
+
return `Knowledge base already configured: ${normalizedPath}`;
|
|
10624
10789
|
}
|
|
10625
|
-
knowledgeBases.push(
|
|
10790
|
+
knowledgeBases.push(normalizedPath);
|
|
10626
10791
|
config.knowledgeBases = knowledgeBases;
|
|
10627
|
-
saveConfig(config);
|
|
10792
|
+
saveConfig(sharedProjectRoot, config);
|
|
10628
10793
|
refreshIndexerFromConfig();
|
|
10629
|
-
let result = `${
|
|
10794
|
+
let result = `${normalizedPath}
|
|
10630
10795
|
`;
|
|
10631
10796
|
result += `Total knowledge bases: ${knowledgeBases.length}
|
|
10632
10797
|
`;
|
|
10633
|
-
result += `Config saved to: ${getConfigPath()}
|
|
10798
|
+
result += `Config saved to: ${getConfigPath(sharedProjectRoot)}
|
|
10634
10799
|
`;
|
|
10635
10800
|
result += `
|
|
10636
10801
|
Run /index to rebuild the index with the new knowledge base.`;
|
|
@@ -10641,8 +10806,8 @@ var list_knowledge_bases = (0, import_plugin.tool)({
|
|
|
10641
10806
|
description: "List all configured knowledge base folders that are indexed alongside the main project.",
|
|
10642
10807
|
args: {},
|
|
10643
10808
|
async execute() {
|
|
10644
|
-
const config = loadRuntimeConfig();
|
|
10645
|
-
const knowledgeBases =
|
|
10809
|
+
const config = loadRuntimeConfig(sharedProjectRoot);
|
|
10810
|
+
const knowledgeBases = ensureStringArray(config.knowledgeBases);
|
|
10646
10811
|
if (knowledgeBases.length === 0) {
|
|
10647
10812
|
return "No knowledge bases configured. Use add_knowledge_base to add folders.";
|
|
10648
10813
|
}
|
|
@@ -10651,8 +10816,8 @@ var list_knowledge_bases = (0, import_plugin.tool)({
|
|
|
10651
10816
|
`;
|
|
10652
10817
|
for (let i = 0; i < knowledgeBases.length; i++) {
|
|
10653
10818
|
const kb = knowledgeBases[i];
|
|
10654
|
-
const resolvedPath =
|
|
10655
|
-
const exists = (0,
|
|
10819
|
+
const resolvedPath = resolveKnowledgeBasePath(kb, sharedProjectRoot);
|
|
10820
|
+
const exists = (0, import_fs9.existsSync)(resolvedPath);
|
|
10656
10821
|
result += `[${i + 1}] ${kb}
|
|
10657
10822
|
`;
|
|
10658
10823
|
result += ` Resolved: ${resolvedPath}
|
|
@@ -10661,7 +10826,7 @@ var list_knowledge_bases = (0, import_plugin.tool)({
|
|
|
10661
10826
|
`;
|
|
10662
10827
|
if (exists) {
|
|
10663
10828
|
try {
|
|
10664
|
-
const stat4 = (0,
|
|
10829
|
+
const stat4 = (0, import_fs9.statSync)(resolvedPath);
|
|
10665
10830
|
result += ` Type: ${stat4.isDirectory() ? "Directory" : "File"}
|
|
10666
10831
|
`;
|
|
10667
10832
|
} catch {
|
|
@@ -10669,7 +10834,7 @@ var list_knowledge_bases = (0, import_plugin.tool)({
|
|
|
10669
10834
|
}
|
|
10670
10835
|
result += "\n";
|
|
10671
10836
|
}
|
|
10672
|
-
result += `Config file: ${getConfigPath()}`;
|
|
10837
|
+
result += `Config file: ${getConfigPath(sharedProjectRoot)}`;
|
|
10673
10838
|
return result;
|
|
10674
10839
|
}
|
|
10675
10840
|
});
|
|
@@ -10680,15 +10845,12 @@ var remove_knowledge_base = (0, import_plugin.tool)({
|
|
|
10680
10845
|
},
|
|
10681
10846
|
async execute(args) {
|
|
10682
10847
|
const inputPath = args.path.trim();
|
|
10683
|
-
const config = loadEditableConfig();
|
|
10684
|
-
const knowledgeBases =
|
|
10848
|
+
const config = loadEditableConfig(sharedProjectRoot);
|
|
10849
|
+
const knowledgeBases = ensureStringArray(config.knowledgeBases);
|
|
10685
10850
|
if (knowledgeBases.length === 0) {
|
|
10686
10851
|
return "No knowledge bases configured.";
|
|
10687
10852
|
}
|
|
10688
|
-
const
|
|
10689
|
-
const index = knowledgeBases.findIndex(
|
|
10690
|
-
(kb) => path9.normalize(kb) === normalizedInput || path9.normalize(path9.isAbsolute(kb) ? kb : path9.resolve(sharedProjectRoot, kb)) === normalizedInput
|
|
10691
|
-
);
|
|
10853
|
+
const index = findKnowledgeBasePathIndex(knowledgeBases, inputPath, sharedProjectRoot);
|
|
10692
10854
|
if (index === -1) {
|
|
10693
10855
|
let result2 = `Knowledge base not found: ${inputPath}
|
|
10694
10856
|
|
|
@@ -10703,14 +10865,14 @@ var remove_knowledge_base = (0, import_plugin.tool)({
|
|
|
10703
10865
|
}
|
|
10704
10866
|
const removed = knowledgeBases.splice(index, 1)[0];
|
|
10705
10867
|
config.knowledgeBases = knowledgeBases;
|
|
10706
|
-
saveConfig(config);
|
|
10868
|
+
saveConfig(sharedProjectRoot, config);
|
|
10707
10869
|
refreshIndexerFromConfig();
|
|
10708
10870
|
let result = `Removed: ${removed}
|
|
10709
10871
|
|
|
10710
10872
|
`;
|
|
10711
10873
|
result += `Remaining knowledge bases: ${knowledgeBases.length}
|
|
10712
10874
|
`;
|
|
10713
|
-
result += `Config saved to: ${getConfigPath()}
|
|
10875
|
+
result += `Config saved to: ${getConfigPath(sharedProjectRoot)}
|
|
10714
10876
|
`;
|
|
10715
10877
|
result += `
|
|
10716
10878
|
Run /index to rebuild the index without the removed knowledge base.`;
|
|
@@ -10719,8 +10881,8 @@ Run /index to rebuild the index without the removed knowledge base.`;
|
|
|
10719
10881
|
});
|
|
10720
10882
|
|
|
10721
10883
|
// src/commands/loader.ts
|
|
10722
|
-
var
|
|
10723
|
-
var
|
|
10884
|
+
var import_fs10 = require("fs");
|
|
10885
|
+
var path16 = __toESM(require("path"), 1);
|
|
10724
10886
|
function parseFrontmatter(content) {
|
|
10725
10887
|
const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
|
|
10726
10888
|
const match = content.match(frontmatterRegex);
|
|
@@ -10741,15 +10903,21 @@ function parseFrontmatter(content) {
|
|
|
10741
10903
|
}
|
|
10742
10904
|
function loadCommandsFromDirectory(commandsDir) {
|
|
10743
10905
|
const commands = /* @__PURE__ */ new Map();
|
|
10744
|
-
if (!(0,
|
|
10906
|
+
if (!(0, import_fs10.existsSync)(commandsDir)) {
|
|
10745
10907
|
return commands;
|
|
10746
10908
|
}
|
|
10747
|
-
const files = (0,
|
|
10909
|
+
const files = (0, import_fs10.readdirSync)(commandsDir).filter((f) => f.endsWith(".md"));
|
|
10748
10910
|
for (const file of files) {
|
|
10749
|
-
const filePath =
|
|
10750
|
-
|
|
10911
|
+
const filePath = path16.join(commandsDir, file);
|
|
10912
|
+
let content;
|
|
10913
|
+
try {
|
|
10914
|
+
content = (0, import_fs10.readFileSync)(filePath, "utf-8");
|
|
10915
|
+
} catch (error) {
|
|
10916
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
10917
|
+
throw new Error(`Failed to load command file ${filePath}: ${message}`);
|
|
10918
|
+
}
|
|
10751
10919
|
const { frontmatter, body } = parseFrontmatter(content);
|
|
10752
|
-
const name =
|
|
10920
|
+
const name = path16.basename(file, ".md");
|
|
10753
10921
|
const description = frontmatter.description || `Run the ${name} command`;
|
|
10754
10922
|
commands.set(name, {
|
|
10755
10923
|
description,
|
|
@@ -10759,7 +10927,7 @@ function loadCommandsFromDirectory(commandsDir) {
|
|
|
10759
10927
|
return commands;
|
|
10760
10928
|
}
|
|
10761
10929
|
|
|
10762
|
-
// src/routing-hints.ts
|
|
10930
|
+
// src/routing-hints-patterns.ts
|
|
10763
10931
|
var EXTERNAL_HINTS = [
|
|
10764
10932
|
"docs",
|
|
10765
10933
|
"documentation",
|
|
@@ -10850,6 +11018,8 @@ var SNAKE_PATTERN = /\b[a-z0-9]+_[a-z0-9_]+\b/g;
|
|
|
10850
11018
|
var KEBAB_PATTERN = /\b[a-z0-9]+-[a-z0-9-]+\b/g;
|
|
10851
11019
|
var BACKTICK_IDENTIFIER_PATTERN = /`([^`]+)`/g;
|
|
10852
11020
|
var BACKTICK_IDENTIFIER_PRESENCE_PATTERN = /`([^`]+)`/;
|
|
11021
|
+
var DOUBLE_QUOTED_PATTERN = /"[^"]+"/;
|
|
11022
|
+
var SINGLE_QUOTED_PATTERN = /'[^']+'/;
|
|
10853
11023
|
function normalizeText(text) {
|
|
10854
11024
|
return text.trim().replace(/\s+/g, " ");
|
|
10855
11025
|
}
|
|
@@ -10862,6 +11032,21 @@ function countWords(text) {
|
|
|
10862
11032
|
}
|
|
10863
11033
|
return text.split(/\s+/).filter(Boolean).length;
|
|
10864
11034
|
}
|
|
11035
|
+
function isExternalLookup(text) {
|
|
11036
|
+
return URL_PATTERN.test(text) || includesHint(text, EXTERNAL_HINTS);
|
|
11037
|
+
}
|
|
11038
|
+
function hasConceptualDiscoveryHint(text) {
|
|
11039
|
+
return includesHint(text, CONCEPTUAL_DISCOVERY_HINTS);
|
|
11040
|
+
}
|
|
11041
|
+
function hasDefinitionHint(text) {
|
|
11042
|
+
return includesHint(text, DEFINITION_HINTS);
|
|
11043
|
+
}
|
|
11044
|
+
function hasExactMatchHint(text) {
|
|
11045
|
+
return includesHint(text, EXACT_MATCH_HINTS);
|
|
11046
|
+
}
|
|
11047
|
+
function hasNonDiscoveryHint(text) {
|
|
11048
|
+
return includesHint(text, NON_DISCOVERY_HINTS);
|
|
11049
|
+
}
|
|
10865
11050
|
function hasIdentifierShape(text) {
|
|
10866
11051
|
const matches = [
|
|
10867
11052
|
...text.match(CAMEL_OR_PASCAL_PATTERN) ?? [],
|
|
@@ -10877,11 +11062,13 @@ function hasIdentifierShape(text) {
|
|
|
10877
11062
|
});
|
|
10878
11063
|
}
|
|
10879
11064
|
function containsQuotedIdentifier(text) {
|
|
10880
|
-
return BACKTICK_IDENTIFIER_PRESENCE_PATTERN.test(text) ||
|
|
11065
|
+
return BACKTICK_IDENTIFIER_PRESENCE_PATTERN.test(text) || DOUBLE_QUOTED_PATTERN.test(text) || SINGLE_QUOTED_PATTERN.test(text);
|
|
10881
11066
|
}
|
|
10882
11067
|
function looksLikeDirectPath(text) {
|
|
10883
11068
|
return FILE_PATH_PATTERN.test(text) || /\b[a-z0-9_-]+\.(ts|tsx|js|jsx|rs|py|go|java|json|md|yaml|yml)\b/i.test(text);
|
|
10884
11069
|
}
|
|
11070
|
+
|
|
11071
|
+
// src/routing-hints.ts
|
|
10885
11072
|
function extractUserText(parts) {
|
|
10886
11073
|
return normalizeText(
|
|
10887
11074
|
parts.filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text ?? "").join(" ")
|
|
@@ -10897,21 +11084,21 @@ function assessRoutingIntent(text) {
|
|
|
10897
11084
|
reason: "empty_text"
|
|
10898
11085
|
};
|
|
10899
11086
|
}
|
|
10900
|
-
if (
|
|
11087
|
+
if (isExternalLookup(lowered)) {
|
|
10901
11088
|
return {
|
|
10902
11089
|
intent: "external",
|
|
10903
11090
|
text: normalizedText,
|
|
10904
11091
|
reason: "external_lookup"
|
|
10905
11092
|
};
|
|
10906
11093
|
}
|
|
10907
|
-
const
|
|
10908
|
-
const
|
|
10909
|
-
const
|
|
10910
|
-
const
|
|
11094
|
+
const matchedConceptualHint = hasConceptualDiscoveryHint(lowered);
|
|
11095
|
+
const matchedDefinitionHint = hasDefinitionHint(lowered);
|
|
11096
|
+
const matchedExactMatchHint = hasExactMatchHint(lowered);
|
|
11097
|
+
const matchedNonDiscoveryHint = hasNonDiscoveryHint(lowered);
|
|
10911
11098
|
const hasIdentifier = hasIdentifierShape(normalizedText);
|
|
10912
11099
|
const hasQuotedIdentifier = containsQuotedIdentifier(normalizedText);
|
|
10913
11100
|
const shortQuery = countWords(lowered) <= 10;
|
|
10914
|
-
if (
|
|
11101
|
+
if (matchedNonDiscoveryHint && !matchedConceptualHint) {
|
|
10915
11102
|
return {
|
|
10916
11103
|
intent: "other",
|
|
10917
11104
|
text: normalizedText,
|
|
@@ -10925,21 +11112,21 @@ function assessRoutingIntent(text) {
|
|
|
10925
11112
|
reason: "direct_path_reference"
|
|
10926
11113
|
};
|
|
10927
11114
|
}
|
|
10928
|
-
if ((
|
|
11115
|
+
if ((matchedDefinitionHint || lowered.includes("where is") || lowered.includes("where are")) && (lowered.includes("defined") || lowered.includes("definition"))) {
|
|
10929
11116
|
return {
|
|
10930
11117
|
intent: "definition_lookup",
|
|
10931
11118
|
text: normalizedText,
|
|
10932
11119
|
reason: "definition_lookup_request"
|
|
10933
11120
|
};
|
|
10934
11121
|
}
|
|
10935
|
-
if ((
|
|
11122
|
+
if ((matchedExactMatchHint || hasQuotedIdentifier || hasIdentifier) && !matchedConceptualHint && shortQuery) {
|
|
10936
11123
|
return {
|
|
10937
11124
|
intent: "exact_identifier",
|
|
10938
11125
|
text: normalizedText,
|
|
10939
|
-
reason:
|
|
11126
|
+
reason: matchedExactMatchHint || hasQuotedIdentifier ? "exact_match_request" : "identifier_shaped_query"
|
|
10940
11127
|
};
|
|
10941
11128
|
}
|
|
10942
|
-
if (
|
|
11129
|
+
if (matchedConceptualHint) {
|
|
10943
11130
|
return {
|
|
10944
11131
|
intent: "local_conceptual",
|
|
10945
11132
|
text: normalizedText,
|
|
@@ -11037,9 +11224,9 @@ function replaceActiveWatcher(nextWatcher) {
|
|
|
11037
11224
|
function getCommandsDir() {
|
|
11038
11225
|
let currentDir = process.cwd();
|
|
11039
11226
|
if (typeof import_meta2 !== "undefined" && import_meta2.url) {
|
|
11040
|
-
currentDir =
|
|
11227
|
+
currentDir = path17.dirname((0, import_url2.fileURLToPath)(import_meta2.url));
|
|
11041
11228
|
}
|
|
11042
|
-
return
|
|
11229
|
+
return path17.join(currentDir, "..", "commands");
|
|
11043
11230
|
}
|
|
11044
11231
|
var plugin = async ({ directory }) => {
|
|
11045
11232
|
try {
|