opencode-codebase-index 0.8.1 → 0.10.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 +32 -13
- package/dist/cli.cjs +1526 -1293
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1525 -1292
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1235 -968
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1206 -939
- 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.js
CHANGED
|
@@ -324,7 +324,7 @@ var require_ignore = __commonJS({
|
|
|
324
324
|
// path matching.
|
|
325
325
|
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
326
326
|
// @returns {TestResult} true if a file is ignored
|
|
327
|
-
test(
|
|
327
|
+
test(path18, checkUnignored, mode) {
|
|
328
328
|
let ignored = false;
|
|
329
329
|
let unignored = false;
|
|
330
330
|
let matchedRule;
|
|
@@ -333,7 +333,7 @@ var require_ignore = __commonJS({
|
|
|
333
333
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
334
334
|
return;
|
|
335
335
|
}
|
|
336
|
-
const matched = rule[mode].test(
|
|
336
|
+
const matched = rule[mode].test(path18);
|
|
337
337
|
if (!matched) {
|
|
338
338
|
return;
|
|
339
339
|
}
|
|
@@ -354,17 +354,17 @@ var require_ignore = __commonJS({
|
|
|
354
354
|
var throwError = (message, Ctor) => {
|
|
355
355
|
throw new Ctor(message);
|
|
356
356
|
};
|
|
357
|
-
var checkPath = (
|
|
358
|
-
if (!isString(
|
|
357
|
+
var checkPath = (path18, originalPath, doThrow) => {
|
|
358
|
+
if (!isString(path18)) {
|
|
359
359
|
return doThrow(
|
|
360
360
|
`path must be a string, but got \`${originalPath}\``,
|
|
361
361
|
TypeError
|
|
362
362
|
);
|
|
363
363
|
}
|
|
364
|
-
if (!
|
|
364
|
+
if (!path18) {
|
|
365
365
|
return doThrow(`path must not be empty`, TypeError);
|
|
366
366
|
}
|
|
367
|
-
if (checkPath.isNotRelative(
|
|
367
|
+
if (checkPath.isNotRelative(path18)) {
|
|
368
368
|
const r = "`path.relative()`d";
|
|
369
369
|
return doThrow(
|
|
370
370
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -373,7 +373,7 @@ var require_ignore = __commonJS({
|
|
|
373
373
|
}
|
|
374
374
|
return true;
|
|
375
375
|
};
|
|
376
|
-
var isNotRelative = (
|
|
376
|
+
var isNotRelative = (path18) => REGEX_TEST_INVALID_PATH.test(path18);
|
|
377
377
|
checkPath.isNotRelative = isNotRelative;
|
|
378
378
|
checkPath.convert = (p) => p;
|
|
379
379
|
var Ignore2 = class {
|
|
@@ -403,19 +403,19 @@ var require_ignore = __commonJS({
|
|
|
403
403
|
}
|
|
404
404
|
// @returns {TestResult}
|
|
405
405
|
_test(originalPath, cache, checkUnignored, slices) {
|
|
406
|
-
const
|
|
406
|
+
const path18 = originalPath && checkPath.convert(originalPath);
|
|
407
407
|
checkPath(
|
|
408
|
-
|
|
408
|
+
path18,
|
|
409
409
|
originalPath,
|
|
410
410
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
411
411
|
);
|
|
412
|
-
return this._t(
|
|
412
|
+
return this._t(path18, cache, checkUnignored, slices);
|
|
413
413
|
}
|
|
414
|
-
checkIgnore(
|
|
415
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
416
|
-
return this.test(
|
|
414
|
+
checkIgnore(path18) {
|
|
415
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path18)) {
|
|
416
|
+
return this.test(path18);
|
|
417
417
|
}
|
|
418
|
-
const slices =
|
|
418
|
+
const slices = path18.split(SLASH2).filter(Boolean);
|
|
419
419
|
slices.pop();
|
|
420
420
|
if (slices.length) {
|
|
421
421
|
const parent = this._t(
|
|
@@ -428,18 +428,18 @@ var require_ignore = __commonJS({
|
|
|
428
428
|
return parent;
|
|
429
429
|
}
|
|
430
430
|
}
|
|
431
|
-
return this._rules.test(
|
|
431
|
+
return this._rules.test(path18, false, MODE_CHECK_IGNORE);
|
|
432
432
|
}
|
|
433
|
-
_t(
|
|
434
|
-
if (
|
|
435
|
-
return cache[
|
|
433
|
+
_t(path18, cache, checkUnignored, slices) {
|
|
434
|
+
if (path18 in cache) {
|
|
435
|
+
return cache[path18];
|
|
436
436
|
}
|
|
437
437
|
if (!slices) {
|
|
438
|
-
slices =
|
|
438
|
+
slices = path18.split(SLASH2).filter(Boolean);
|
|
439
439
|
}
|
|
440
440
|
slices.pop();
|
|
441
441
|
if (!slices.length) {
|
|
442
|
-
return cache[
|
|
442
|
+
return cache[path18] = this._rules.test(path18, checkUnignored, MODE_IGNORE);
|
|
443
443
|
}
|
|
444
444
|
const parent = this._t(
|
|
445
445
|
slices.join(SLASH2) + SLASH2,
|
|
@@ -447,29 +447,29 @@ var require_ignore = __commonJS({
|
|
|
447
447
|
checkUnignored,
|
|
448
448
|
slices
|
|
449
449
|
);
|
|
450
|
-
return cache[
|
|
450
|
+
return cache[path18] = parent.ignored ? parent : this._rules.test(path18, checkUnignored, MODE_IGNORE);
|
|
451
451
|
}
|
|
452
|
-
ignores(
|
|
453
|
-
return this._test(
|
|
452
|
+
ignores(path18) {
|
|
453
|
+
return this._test(path18, this._ignoreCache, false).ignored;
|
|
454
454
|
}
|
|
455
455
|
createFilter() {
|
|
456
|
-
return (
|
|
456
|
+
return (path18) => !this.ignores(path18);
|
|
457
457
|
}
|
|
458
458
|
filter(paths) {
|
|
459
459
|
return makeArray(paths).filter(this.createFilter());
|
|
460
460
|
}
|
|
461
461
|
// @returns {TestResult}
|
|
462
|
-
test(
|
|
463
|
-
return this._test(
|
|
462
|
+
test(path18) {
|
|
463
|
+
return this._test(path18, this._testCache, true);
|
|
464
464
|
}
|
|
465
465
|
};
|
|
466
466
|
var factory = (options) => new Ignore2(options);
|
|
467
|
-
var isPathValid = (
|
|
467
|
+
var isPathValid = (path18) => checkPath(path18 && checkPath.convert(path18), path18, RETURN_FALSE);
|
|
468
468
|
var setupWindows = () => {
|
|
469
469
|
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
470
470
|
checkPath.convert = makePosix;
|
|
471
471
|
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
472
|
-
checkPath.isNotRelative = (
|
|
472
|
+
checkPath.isNotRelative = (path18) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path18) || isNotRelative(path18);
|
|
473
473
|
};
|
|
474
474
|
if (
|
|
475
475
|
// Detect `process` so that it can run in browsers.
|
|
@@ -647,7 +647,8 @@ var require_eventemitter3 = __commonJS({
|
|
|
647
647
|
});
|
|
648
648
|
|
|
649
649
|
// src/index.ts
|
|
650
|
-
import * as
|
|
650
|
+
import * as os6 from "os";
|
|
651
|
+
import * as path17 from "path";
|
|
651
652
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
652
653
|
|
|
653
654
|
// src/config/constants.ts
|
|
@@ -664,7 +665,8 @@ var DEFAULT_INCLUDE = [
|
|
|
664
665
|
"**/*.{md,mdx}",
|
|
665
666
|
"**/*.{sh,bash,zsh}",
|
|
666
667
|
"**/*.{txt,html,htm}",
|
|
667
|
-
"**/*.zig"
|
|
668
|
+
"**/*.zig",
|
|
669
|
+
"**/*.gd"
|
|
668
670
|
];
|
|
669
671
|
var DEFAULT_EXCLUDE = [
|
|
670
672
|
"**/node_modules/**",
|
|
@@ -763,28 +765,7 @@ var AUTO_DETECT_PROVIDER_ORDER = [
|
|
|
763
765
|
"google"
|
|
764
766
|
];
|
|
765
767
|
|
|
766
|
-
// src/config/
|
|
767
|
-
var ENV_REFERENCE_PATTERN = /^\{env:([A-Z_][A-Z0-9_]*)\}$/;
|
|
768
|
-
var ENV_REFERENCE_LIKE_PATTERN = /\{env:[^}]+\}/;
|
|
769
|
-
function substituteEnvString(value, keyPath) {
|
|
770
|
-
const match = value.match(ENV_REFERENCE_PATTERN);
|
|
771
|
-
if (!match) {
|
|
772
|
-
if (ENV_REFERENCE_LIKE_PATTERN.test(value)) {
|
|
773
|
-
throw new Error(
|
|
774
|
-
`Invalid environment variable reference at '${keyPath}'. Expected the entire string to match '{env:VAR_NAME}' with VAR_NAME matching [A-Z_][A-Z0-9_]*.`
|
|
775
|
-
);
|
|
776
|
-
}
|
|
777
|
-
return value;
|
|
778
|
-
}
|
|
779
|
-
const variableName = match[1];
|
|
780
|
-
const envValue = process.env[variableName];
|
|
781
|
-
if (envValue === void 0) {
|
|
782
|
-
throw new Error(`Missing environment variable '${variableName}' referenced by config at '${keyPath}'.`);
|
|
783
|
-
}
|
|
784
|
-
return envValue;
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
// src/config/schema.ts
|
|
768
|
+
// src/config/defaults.ts
|
|
788
769
|
function getDefaultIndexingConfig() {
|
|
789
770
|
return {
|
|
790
771
|
autoIndex: false,
|
|
@@ -813,15 +794,10 @@ function getDefaultSearchConfig() {
|
|
|
813
794
|
rrfK: 60,
|
|
814
795
|
rerankTopN: 20,
|
|
815
796
|
contextLines: 0,
|
|
816
|
-
routingHints: true
|
|
797
|
+
routingHints: true,
|
|
798
|
+
routingHintRole: "system"
|
|
817
799
|
};
|
|
818
800
|
}
|
|
819
|
-
function isValidFusionStrategy(value) {
|
|
820
|
-
return value === "weighted" || value === "rrf";
|
|
821
|
-
}
|
|
822
|
-
function isValidRerankerProvider(value) {
|
|
823
|
-
return value === "cohere" || value === "jina" || value === "custom";
|
|
824
|
-
}
|
|
825
801
|
function getDefaultRerankerBaseUrl(provider) {
|
|
826
802
|
switch (provider) {
|
|
827
803
|
case "cohere":
|
|
@@ -844,8 +820,37 @@ function getDefaultDebugConfig() {
|
|
|
844
820
|
metrics: true
|
|
845
821
|
};
|
|
846
822
|
}
|
|
823
|
+
|
|
824
|
+
// src/config/env-substitution.ts
|
|
825
|
+
var ENV_REFERENCE_PATTERN = /^\{env:([A-Z_][A-Z0-9_]*)\}$/;
|
|
826
|
+
var ENV_REFERENCE_LIKE_PATTERN = /\{env:[^}]+\}/;
|
|
827
|
+
function substituteEnvString(value, keyPath) {
|
|
828
|
+
const match = value.match(ENV_REFERENCE_PATTERN);
|
|
829
|
+
if (!match) {
|
|
830
|
+
if (ENV_REFERENCE_LIKE_PATTERN.test(value)) {
|
|
831
|
+
throw new Error(
|
|
832
|
+
`Invalid environment variable reference at '${keyPath}'. Expected the entire string to match '{env:VAR_NAME}' with VAR_NAME matching [A-Z_][A-Z0-9_]*.`
|
|
833
|
+
);
|
|
834
|
+
}
|
|
835
|
+
return value;
|
|
836
|
+
}
|
|
837
|
+
const variableName = match[1];
|
|
838
|
+
const envValue = process.env[variableName];
|
|
839
|
+
if (envValue === void 0) {
|
|
840
|
+
throw new Error(`Missing environment variable '${variableName}' referenced by config at '${keyPath}'.`);
|
|
841
|
+
}
|
|
842
|
+
return envValue;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// src/config/validators.ts
|
|
847
846
|
var VALID_SCOPES = ["project", "global"];
|
|
848
847
|
var VALID_LOG_LEVELS = ["error", "warn", "info", "debug"];
|
|
848
|
+
function isValidFusionStrategy(value) {
|
|
849
|
+
return value === "weighted" || value === "rrf";
|
|
850
|
+
}
|
|
851
|
+
function isValidRerankerProvider(value) {
|
|
852
|
+
return value === "cohere" || value === "jina" || value === "custom";
|
|
853
|
+
}
|
|
849
854
|
function isValidProvider(value) {
|
|
850
855
|
return typeof value === "string" && Object.keys(EMBEDDING_MODELS).includes(value);
|
|
851
856
|
}
|
|
@@ -873,6 +878,8 @@ function getResolvedStringArray(value, keyPath) {
|
|
|
873
878
|
function isValidLogLevel(value) {
|
|
874
879
|
return typeof value === "string" && VALID_LOG_LEVELS.includes(value);
|
|
875
880
|
}
|
|
881
|
+
|
|
882
|
+
// src/config/schema.ts
|
|
876
883
|
function parseConfig(raw) {
|
|
877
884
|
const input = raw && typeof raw === "object" ? raw : {};
|
|
878
885
|
const embeddingProviderValue = getResolvedString(input.embeddingProvider, "$root.embeddingProvider");
|
|
@@ -909,7 +916,8 @@ function parseConfig(raw) {
|
|
|
909
916
|
rrfK: typeof rawSearch.rrfK === "number" ? Math.max(1, Math.floor(rawSearch.rrfK)) : defaultSearch.rrfK,
|
|
910
917
|
rerankTopN: typeof rawSearch.rerankTopN === "number" ? Math.min(200, Math.max(0, Math.floor(rawSearch.rerankTopN))) : defaultSearch.rerankTopN,
|
|
911
918
|
contextLines: typeof rawSearch.contextLines === "number" ? Math.min(50, Math.max(0, rawSearch.contextLines)) : defaultSearch.contextLines,
|
|
912
|
-
routingHints: typeof rawSearch.routingHints === "boolean" ? rawSearch.routingHints : defaultSearch.routingHints
|
|
919
|
+
routingHints: typeof rawSearch.routingHints === "boolean" ? rawSearch.routingHints : defaultSearch.routingHints,
|
|
920
|
+
routingHintRole: rawSearch.routingHintRole === "developer" || rawSearch.routingHintRole === "system" ? rawSearch.routingHintRole : defaultSearch.routingHintRole
|
|
913
921
|
};
|
|
914
922
|
const rawDebug = input.debug && typeof input.debug === "object" ? input.debug : {};
|
|
915
923
|
const debug = {
|
|
@@ -927,9 +935,9 @@ function parseConfig(raw) {
|
|
|
927
935
|
const rawAdditionalInclude = input.additionalInclude;
|
|
928
936
|
const additionalInclude = isStringArray(rawAdditionalInclude) ? rawAdditionalInclude.filter((p) => typeof p === "string" && p.trim().length > 0).map((p) => p.trim()) : [];
|
|
929
937
|
let embeddingProvider;
|
|
930
|
-
let embeddingModel
|
|
931
|
-
let customProvider
|
|
932
|
-
let reranker
|
|
938
|
+
let embeddingModel;
|
|
939
|
+
let customProvider;
|
|
940
|
+
let reranker;
|
|
933
941
|
if (embeddingProviderValue === "custom") {
|
|
934
942
|
embeddingProvider = "custom";
|
|
935
943
|
const rawCustom = input.customProvider && typeof input.customProvider === "object" ? input.customProvider : null;
|
|
@@ -962,7 +970,7 @@ function parseConfig(raw) {
|
|
|
962
970
|
embeddingProvider = embeddingProviderValue;
|
|
963
971
|
const rawEmbeddingModel = input.embeddingModel;
|
|
964
972
|
if (typeof rawEmbeddingModel === "string") {
|
|
965
|
-
const embeddingModelValue =
|
|
973
|
+
const embeddingModelValue = getResolvedString(rawEmbeddingModel, "$root.embeddingModel");
|
|
966
974
|
if (embeddingModelValue) {
|
|
967
975
|
embeddingModel = isValidModel(embeddingModelValue, embeddingProvider) ? embeddingModelValue : DEFAULT_PROVIDER_MODELS[embeddingProvider];
|
|
968
976
|
}
|
|
@@ -1025,16 +1033,20 @@ var autoDetectProviders = AUTO_DETECT_PROVIDER_ORDER.filter(
|
|
|
1025
1033
|
);
|
|
1026
1034
|
|
|
1027
1035
|
// src/config/merger.ts
|
|
1028
|
-
import { existsSync as
|
|
1036
|
+
import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
1029
1037
|
import * as os2 from "os";
|
|
1030
|
-
import * as
|
|
1038
|
+
import * as path6 from "path";
|
|
1031
1039
|
|
|
1032
1040
|
// src/config/paths.ts
|
|
1033
|
-
import { existsSync as
|
|
1041
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1034
1042
|
import * as os from "os";
|
|
1035
|
-
import * as
|
|
1043
|
+
import * as path3 from "path";
|
|
1036
1044
|
|
|
1037
1045
|
// src/git/index.ts
|
|
1046
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
|
|
1047
|
+
import * as path2 from "path";
|
|
1048
|
+
|
|
1049
|
+
// src/git/refs.ts
|
|
1038
1050
|
import { existsSync, readFileSync, readdirSync, statSync } from "fs";
|
|
1039
1051
|
import * as path from "path";
|
|
1040
1052
|
function readPackedRefs(gitDir) {
|
|
@@ -1067,38 +1079,40 @@ function resolveCommonGitDir(gitDir) {
|
|
|
1067
1079
|
}
|
|
1068
1080
|
return gitDir;
|
|
1069
1081
|
}
|
|
1082
|
+
|
|
1083
|
+
// src/git/index.ts
|
|
1070
1084
|
function resolveWorktreeMainRepoRoot(repoRoot) {
|
|
1071
1085
|
const gitDir = resolveGitDir(repoRoot);
|
|
1072
1086
|
if (!gitDir) {
|
|
1073
1087
|
return null;
|
|
1074
1088
|
}
|
|
1075
1089
|
const commonGitDir = resolveCommonGitDir(gitDir);
|
|
1076
|
-
if (commonGitDir === gitDir ||
|
|
1090
|
+
if (commonGitDir === gitDir || path2.basename(commonGitDir) !== ".git") {
|
|
1077
1091
|
return null;
|
|
1078
1092
|
}
|
|
1079
|
-
const mainRepoRoot =
|
|
1080
|
-
if (!
|
|
1093
|
+
const mainRepoRoot = path2.dirname(commonGitDir);
|
|
1094
|
+
if (!existsSync2(mainRepoRoot)) {
|
|
1081
1095
|
return null;
|
|
1082
1096
|
}
|
|
1083
|
-
return
|
|
1097
|
+
return path2.resolve(mainRepoRoot) === path2.resolve(repoRoot) ? null : mainRepoRoot;
|
|
1084
1098
|
}
|
|
1085
1099
|
function resolveGitDir(repoRoot) {
|
|
1086
|
-
const gitPath =
|
|
1087
|
-
if (!
|
|
1100
|
+
const gitPath = path2.join(repoRoot, ".git");
|
|
1101
|
+
if (!existsSync2(gitPath)) {
|
|
1088
1102
|
return null;
|
|
1089
1103
|
}
|
|
1090
1104
|
try {
|
|
1091
|
-
const stat4 =
|
|
1105
|
+
const stat4 = statSync2(gitPath);
|
|
1092
1106
|
if (stat4.isDirectory()) {
|
|
1093
1107
|
return gitPath;
|
|
1094
1108
|
}
|
|
1095
1109
|
if (stat4.isFile()) {
|
|
1096
|
-
const content =
|
|
1110
|
+
const content = readFileSync2(gitPath, "utf-8").trim();
|
|
1097
1111
|
const match = content.match(/^gitdir:\s*(.+)$/);
|
|
1098
1112
|
if (match) {
|
|
1099
1113
|
const gitdir = match[1];
|
|
1100
|
-
const resolvedPath =
|
|
1101
|
-
if (
|
|
1114
|
+
const resolvedPath = path2.isAbsolute(gitdir) ? gitdir : path2.resolve(repoRoot, gitdir);
|
|
1115
|
+
if (existsSync2(resolvedPath)) {
|
|
1102
1116
|
return resolvedPath;
|
|
1103
1117
|
}
|
|
1104
1118
|
}
|
|
@@ -1115,12 +1129,12 @@ function getCurrentBranch(repoRoot) {
|
|
|
1115
1129
|
if (!gitDir) {
|
|
1116
1130
|
return null;
|
|
1117
1131
|
}
|
|
1118
|
-
const headPath =
|
|
1119
|
-
if (!
|
|
1132
|
+
const headPath = path2.join(gitDir, "HEAD");
|
|
1133
|
+
if (!existsSync2(headPath)) {
|
|
1120
1134
|
return null;
|
|
1121
1135
|
}
|
|
1122
1136
|
try {
|
|
1123
|
-
const headContent =
|
|
1137
|
+
const headContent = readFileSync2(headPath, "utf-8").trim();
|
|
1124
1138
|
const match = headContent.match(/^ref: refs\/heads\/(.+)$/);
|
|
1125
1139
|
if (match) {
|
|
1126
1140
|
return match[1];
|
|
@@ -1139,8 +1153,8 @@ function getBaseBranch(repoRoot) {
|
|
|
1139
1153
|
const candidates = ["main", "master", "develop", "trunk"];
|
|
1140
1154
|
if (refStoreDir) {
|
|
1141
1155
|
for (const candidate of candidates) {
|
|
1142
|
-
const refPath =
|
|
1143
|
-
if (
|
|
1156
|
+
const refPath = path2.join(refStoreDir, "refs", "heads", candidate);
|
|
1157
|
+
if (existsSync2(refPath)) {
|
|
1144
1158
|
return candidate;
|
|
1145
1159
|
}
|
|
1146
1160
|
const packedRefs = readPackedRefs(refStoreDir);
|
|
@@ -1160,44 +1174,44 @@ function getBranchOrDefault(repoRoot) {
|
|
|
1160
1174
|
function getHeadPath(repoRoot) {
|
|
1161
1175
|
const gitDir = resolveGitDir(repoRoot);
|
|
1162
1176
|
if (gitDir) {
|
|
1163
|
-
return
|
|
1177
|
+
return path2.join(gitDir, "HEAD");
|
|
1164
1178
|
}
|
|
1165
|
-
return
|
|
1179
|
+
return path2.join(repoRoot, ".git", "HEAD");
|
|
1166
1180
|
}
|
|
1167
1181
|
|
|
1168
1182
|
// src/config/paths.ts
|
|
1169
|
-
var PROJECT_CONFIG_RELATIVE_PATH =
|
|
1170
|
-
var PROJECT_INDEX_RELATIVE_PATH =
|
|
1183
|
+
var PROJECT_CONFIG_RELATIVE_PATH = path3.join(".opencode", "codebase-index.json");
|
|
1184
|
+
var PROJECT_INDEX_RELATIVE_PATH = path3.join(".opencode", "index");
|
|
1171
1185
|
function resolveWorktreeFallbackPath(projectRoot, relativePath) {
|
|
1172
1186
|
const mainRepoRoot = resolveWorktreeMainRepoRoot(projectRoot);
|
|
1173
1187
|
if (!mainRepoRoot) {
|
|
1174
1188
|
return null;
|
|
1175
1189
|
}
|
|
1176
|
-
const fallbackPath =
|
|
1177
|
-
return
|
|
1190
|
+
const fallbackPath = path3.join(mainRepoRoot, relativePath);
|
|
1191
|
+
return existsSync3(fallbackPath) ? fallbackPath : null;
|
|
1178
1192
|
}
|
|
1179
1193
|
function hasProjectConfig(projectRoot) {
|
|
1180
|
-
return
|
|
1194
|
+
return existsSync3(path3.join(projectRoot, PROJECT_CONFIG_RELATIVE_PATH));
|
|
1181
1195
|
}
|
|
1182
1196
|
function getGlobalIndexPath() {
|
|
1183
|
-
return
|
|
1197
|
+
return path3.join(os.homedir(), ".opencode", "global-index");
|
|
1184
1198
|
}
|
|
1185
1199
|
function resolveProjectConfigPath(projectRoot) {
|
|
1186
|
-
const localConfigPath =
|
|
1187
|
-
if (
|
|
1200
|
+
const localConfigPath = path3.join(projectRoot, PROJECT_CONFIG_RELATIVE_PATH);
|
|
1201
|
+
if (existsSync3(localConfigPath)) {
|
|
1188
1202
|
return localConfigPath;
|
|
1189
1203
|
}
|
|
1190
1204
|
return resolveWorktreeFallbackPath(projectRoot, PROJECT_CONFIG_RELATIVE_PATH) ?? localConfigPath;
|
|
1191
1205
|
}
|
|
1192
1206
|
function resolveWritableProjectConfigPath(projectRoot) {
|
|
1193
|
-
return
|
|
1207
|
+
return path3.join(projectRoot, PROJECT_CONFIG_RELATIVE_PATH);
|
|
1194
1208
|
}
|
|
1195
1209
|
function resolveProjectIndexPath(projectRoot, scope) {
|
|
1196
1210
|
if (scope === "global") {
|
|
1197
1211
|
return getGlobalIndexPath();
|
|
1198
1212
|
}
|
|
1199
|
-
const localIndexPath =
|
|
1200
|
-
if (
|
|
1213
|
+
const localIndexPath = path3.join(projectRoot, PROJECT_INDEX_RELATIVE_PATH);
|
|
1214
|
+
if (existsSync3(localIndexPath)) {
|
|
1201
1215
|
return localIndexPath;
|
|
1202
1216
|
}
|
|
1203
1217
|
if (hasProjectConfig(projectRoot)) {
|
|
@@ -1206,23 +1220,56 @@ function resolveProjectIndexPath(projectRoot, scope) {
|
|
|
1206
1220
|
return resolveWorktreeFallbackPath(projectRoot, PROJECT_INDEX_RELATIVE_PATH) ?? localIndexPath;
|
|
1207
1221
|
}
|
|
1208
1222
|
|
|
1209
|
-
// src/config/
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
} catch {
|
|
1217
|
-
}
|
|
1218
|
-
return null;
|
|
1223
|
+
// src/config/rebase.ts
|
|
1224
|
+
import * as path5 from "path";
|
|
1225
|
+
|
|
1226
|
+
// src/utils/paths.ts
|
|
1227
|
+
import * as path4 from "path";
|
|
1228
|
+
function normalizePathSeparators(value) {
|
|
1229
|
+
return value.replace(/\\/g, "/");
|
|
1219
1230
|
}
|
|
1220
|
-
function
|
|
1221
|
-
return
|
|
1231
|
+
function isHiddenPathSegment(part) {
|
|
1232
|
+
return part.startsWith(".") && part !== "." && part !== "..";
|
|
1222
1233
|
}
|
|
1234
|
+
function isBuildPathSegment(part) {
|
|
1235
|
+
return part.toLowerCase().includes("build");
|
|
1236
|
+
}
|
|
1237
|
+
function hasFilteredPathSegment(relativePath, separator = path4.sep) {
|
|
1238
|
+
return relativePath.split(separator).some(
|
|
1239
|
+
(part) => isHiddenPathSegment(part) || isBuildPathSegment(part)
|
|
1240
|
+
);
|
|
1241
|
+
}
|
|
1242
|
+
var RESTRICTED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
1243
|
+
// macOS
|
|
1244
|
+
"library",
|
|
1245
|
+
"applications",
|
|
1246
|
+
"system",
|
|
1247
|
+
"volumes",
|
|
1248
|
+
"private",
|
|
1249
|
+
"cores",
|
|
1250
|
+
// Linux
|
|
1251
|
+
"proc",
|
|
1252
|
+
"sys",
|
|
1253
|
+
"dev",
|
|
1254
|
+
"run",
|
|
1255
|
+
"snap",
|
|
1256
|
+
// Windows
|
|
1257
|
+
"windows",
|
|
1258
|
+
"programdata",
|
|
1259
|
+
"program files",
|
|
1260
|
+
"program files (x86)",
|
|
1261
|
+
"$recycle.bin"
|
|
1262
|
+
]);
|
|
1263
|
+
function isRestrictedDirectory(relativePath, separator = path4.sep) {
|
|
1264
|
+
const firstSegment = relativePath.split(separator)[0];
|
|
1265
|
+
if (!firstSegment) return false;
|
|
1266
|
+
return RESTRICTED_DIRECTORIES.has(firstSegment.toLowerCase());
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
// src/config/rebase.ts
|
|
1223
1270
|
function isWithinRoot(rootDir, targetPath) {
|
|
1224
|
-
const relativePath =
|
|
1225
|
-
return relativePath === "" || !relativePath.startsWith("..") && !
|
|
1271
|
+
const relativePath = path5.relative(rootDir, targetPath);
|
|
1272
|
+
return relativePath === "" || !relativePath.startsWith("..") && !path5.isAbsolute(relativePath);
|
|
1226
1273
|
}
|
|
1227
1274
|
function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
|
|
1228
1275
|
if (!Array.isArray(values)) {
|
|
@@ -1233,22 +1280,106 @@ function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
|
|
|
1233
1280
|
if (!trimmed) {
|
|
1234
1281
|
return trimmed;
|
|
1235
1282
|
}
|
|
1236
|
-
if (
|
|
1283
|
+
if (path5.isAbsolute(trimmed)) {
|
|
1237
1284
|
if (isWithinRoot(sourceRoot, trimmed)) {
|
|
1238
|
-
return
|
|
1285
|
+
return normalizePathSeparators(path5.normalize(path5.relative(sourceRoot, trimmed) || "."));
|
|
1239
1286
|
}
|
|
1240
|
-
return
|
|
1287
|
+
return path5.normalize(trimmed);
|
|
1241
1288
|
}
|
|
1242
|
-
const resolvedFromSource =
|
|
1289
|
+
const resolvedFromSource = path5.resolve(sourceRoot, trimmed);
|
|
1243
1290
|
if (isWithinRoot(sourceRoot, resolvedFromSource)) {
|
|
1244
|
-
return
|
|
1291
|
+
return normalizePathSeparators(path5.normalize(trimmed));
|
|
1245
1292
|
}
|
|
1246
|
-
return
|
|
1293
|
+
return normalizePathSeparators(path5.normalize(path5.relative(targetRoot, resolvedFromSource)));
|
|
1247
1294
|
}).filter(Boolean);
|
|
1248
1295
|
}
|
|
1296
|
+
|
|
1297
|
+
// src/config/merger.ts
|
|
1298
|
+
var PROJECT_OVERRIDE_KEYS = [
|
|
1299
|
+
"embeddingProvider",
|
|
1300
|
+
"customProvider",
|
|
1301
|
+
"embeddingModel",
|
|
1302
|
+
"reranker",
|
|
1303
|
+
"include",
|
|
1304
|
+
"exclude",
|
|
1305
|
+
"indexing",
|
|
1306
|
+
"search",
|
|
1307
|
+
"debug",
|
|
1308
|
+
"scope"
|
|
1309
|
+
];
|
|
1310
|
+
var MERGE_ARRAY_KEYS = ["knowledgeBases", "additionalInclude"];
|
|
1311
|
+
function isRecord(value) {
|
|
1312
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1313
|
+
}
|
|
1314
|
+
function isStringArray2(value) {
|
|
1315
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
1316
|
+
}
|
|
1317
|
+
function applyProjectOverride(merged, normalizedProjectConfig, globalConfig, key) {
|
|
1318
|
+
if (key in normalizedProjectConfig) {
|
|
1319
|
+
merged[key] = normalizedProjectConfig[key];
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1322
|
+
if (key in globalConfig) {
|
|
1323
|
+
merged[key] = globalConfig[key];
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
function mergeUniqueStringArray(values) {
|
|
1327
|
+
return [...new Set(values.map((value) => String(value).trim()))];
|
|
1328
|
+
}
|
|
1329
|
+
function normalizeKnowledgeBasePath(value) {
|
|
1330
|
+
let normalized = path6.normalize(String(value).trim());
|
|
1331
|
+
const root = path6.parse(normalized).root;
|
|
1332
|
+
while (normalized.length > root.length && /[\\/]$/.test(normalized)) {
|
|
1333
|
+
normalized = normalized.slice(0, -1);
|
|
1334
|
+
}
|
|
1335
|
+
return normalized;
|
|
1336
|
+
}
|
|
1337
|
+
function mergeKnowledgeBasePaths(values) {
|
|
1338
|
+
return [...new Set(values.map((value) => normalizeKnowledgeBasePath(value)).filter((value) => value.length > 0))];
|
|
1339
|
+
}
|
|
1340
|
+
function validateConfigLayerShape(rawConfig, filePath) {
|
|
1341
|
+
if (!isRecord(rawConfig)) {
|
|
1342
|
+
throw new Error(`Config file ${filePath} must contain a JSON object at the root.`);
|
|
1343
|
+
}
|
|
1344
|
+
if (rawConfig.knowledgeBases !== void 0 && !isStringArray2(rawConfig.knowledgeBases)) {
|
|
1345
|
+
throw new Error(`Config file ${filePath} field 'knowledgeBases' must be an array of strings.`);
|
|
1346
|
+
}
|
|
1347
|
+
if (rawConfig.additionalInclude !== void 0 && !isStringArray2(rawConfig.additionalInclude)) {
|
|
1348
|
+
throw new Error(`Config file ${filePath} field 'additionalInclude' must be an array of strings.`);
|
|
1349
|
+
}
|
|
1350
|
+
if (rawConfig.include !== void 0 && !isStringArray2(rawConfig.include)) {
|
|
1351
|
+
throw new Error(`Config file ${filePath} field 'include' must be an array of strings.`);
|
|
1352
|
+
}
|
|
1353
|
+
if (rawConfig.exclude !== void 0 && !isStringArray2(rawConfig.exclude)) {
|
|
1354
|
+
throw new Error(`Config file ${filePath} field 'exclude' must be an array of strings.`);
|
|
1355
|
+
}
|
|
1356
|
+
for (const section of ["customProvider", "indexing", "search", "debug", "reranker"]) {
|
|
1357
|
+
const value = rawConfig[section];
|
|
1358
|
+
if (value !== void 0 && !isRecord(value)) {
|
|
1359
|
+
throw new Error(`Config file ${filePath} field '${section}' must be an object.`);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
return rawConfig;
|
|
1363
|
+
}
|
|
1364
|
+
function loadJsonFile(filePath) {
|
|
1365
|
+
if (!existsSync4(filePath)) {
|
|
1366
|
+
return null;
|
|
1367
|
+
}
|
|
1368
|
+
try {
|
|
1369
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
1370
|
+
return validateConfigLayerShape(JSON.parse(content), filePath);
|
|
1371
|
+
} catch (error) {
|
|
1372
|
+
if (error instanceof Error && error.message.startsWith("Config file ")) {
|
|
1373
|
+
throw error;
|
|
1374
|
+
}
|
|
1375
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1376
|
+
throw new Error(`Failed to load config file ${filePath}: ${message}`);
|
|
1377
|
+
}
|
|
1378
|
+
return null;
|
|
1379
|
+
}
|
|
1249
1380
|
function materializeLocalProjectConfig(projectRoot, config) {
|
|
1250
|
-
const localConfigPath =
|
|
1251
|
-
mkdirSync(
|
|
1381
|
+
const localConfigPath = path6.join(projectRoot, ".opencode", "codebase-index.json");
|
|
1382
|
+
mkdirSync(path6.dirname(localConfigPath), { recursive: true });
|
|
1252
1383
|
writeFileSync(localConfigPath, JSON.stringify(config, null, 2), "utf-8");
|
|
1253
1384
|
return localConfigPath;
|
|
1254
1385
|
}
|
|
@@ -1259,7 +1390,7 @@ function loadProjectConfigLayer(projectRoot) {
|
|
|
1259
1390
|
return {};
|
|
1260
1391
|
}
|
|
1261
1392
|
const normalizedConfig = { ...projectConfig };
|
|
1262
|
-
const projectConfigBaseDir =
|
|
1393
|
+
const projectConfigBaseDir = path6.dirname(path6.dirname(projectConfigPath));
|
|
1263
1394
|
if (Array.isArray(normalizedConfig.knowledgeBases)) {
|
|
1264
1395
|
normalizedConfig.knowledgeBases = resolveInheritedKnowledgeBaseEntries(
|
|
1265
1396
|
normalizedConfig.knowledgeBases,
|
|
@@ -1271,10 +1402,22 @@ function loadProjectConfigLayer(projectRoot) {
|
|
|
1271
1402
|
}
|
|
1272
1403
|
function loadMergedConfig(projectRoot) {
|
|
1273
1404
|
const globalConfigPath = os2.homedir() + "/.config/opencode/codebase-index.json";
|
|
1274
|
-
const globalConfig = loadJsonFile(globalConfigPath);
|
|
1275
1405
|
const projectConfigPath = resolveProjectConfigPath(projectRoot);
|
|
1406
|
+
let globalConfig = null;
|
|
1407
|
+
let globalConfigError = null;
|
|
1408
|
+
try {
|
|
1409
|
+
globalConfig = loadJsonFile(globalConfigPath);
|
|
1410
|
+
} catch (error) {
|
|
1411
|
+
globalConfigError = error instanceof Error ? error : new Error(String(error));
|
|
1412
|
+
}
|
|
1276
1413
|
const projectConfig = loadJsonFile(projectConfigPath);
|
|
1277
1414
|
const normalizedProjectConfig = loadProjectConfigLayer(projectRoot);
|
|
1415
|
+
if (globalConfigError) {
|
|
1416
|
+
if (!projectConfig) {
|
|
1417
|
+
throw globalConfigError;
|
|
1418
|
+
}
|
|
1419
|
+
globalConfig = null;
|
|
1420
|
+
}
|
|
1278
1421
|
if (!globalConfig && !projectConfig) {
|
|
1279
1422
|
return {};
|
|
1280
1423
|
}
|
|
@@ -1284,60 +1427,16 @@ function loadMergedConfig(projectRoot) {
|
|
|
1284
1427
|
if (!globalConfig && projectConfig) {
|
|
1285
1428
|
return normalizedProjectConfig;
|
|
1286
1429
|
}
|
|
1430
|
+
if (!globalConfig || !projectConfig) {
|
|
1431
|
+
return globalConfig ?? normalizedProjectConfig;
|
|
1432
|
+
}
|
|
1287
1433
|
const merged = { ...globalConfig };
|
|
1288
|
-
|
|
1289
|
-
merged
|
|
1290
|
-
} else if (globalConfig && globalConfig.embeddingProvider) {
|
|
1291
|
-
merged.embeddingProvider = globalConfig.embeddingProvider;
|
|
1292
|
-
}
|
|
1293
|
-
if (projectConfig && "customProvider" in normalizedProjectConfig) {
|
|
1294
|
-
merged.customProvider = normalizedProjectConfig.customProvider;
|
|
1295
|
-
} else if (globalConfig && globalConfig.customProvider) {
|
|
1296
|
-
merged.customProvider = globalConfig.customProvider;
|
|
1297
|
-
}
|
|
1298
|
-
if (projectConfig && "embeddingModel" in normalizedProjectConfig) {
|
|
1299
|
-
merged.embeddingModel = normalizedProjectConfig.embeddingModel;
|
|
1300
|
-
} else if (globalConfig && globalConfig.embeddingModel) {
|
|
1301
|
-
merged.embeddingModel = globalConfig.embeddingModel;
|
|
1302
|
-
}
|
|
1303
|
-
if (projectConfig && "reranker" in normalizedProjectConfig) {
|
|
1304
|
-
merged.reranker = normalizedProjectConfig.reranker;
|
|
1305
|
-
} else if (globalConfig && globalConfig.reranker) {
|
|
1306
|
-
merged.reranker = globalConfig.reranker;
|
|
1307
|
-
}
|
|
1308
|
-
if (projectConfig && "include" in normalizedProjectConfig) {
|
|
1309
|
-
merged.include = normalizedProjectConfig.include;
|
|
1310
|
-
} else if (globalConfig && globalConfig.include) {
|
|
1311
|
-
merged.include = globalConfig.include;
|
|
1312
|
-
}
|
|
1313
|
-
if (projectConfig && "exclude" in normalizedProjectConfig) {
|
|
1314
|
-
merged.exclude = normalizedProjectConfig.exclude;
|
|
1315
|
-
} else if (globalConfig && globalConfig.exclude) {
|
|
1316
|
-
merged.exclude = globalConfig.exclude;
|
|
1317
|
-
}
|
|
1318
|
-
if (projectConfig && "indexing" in normalizedProjectConfig) {
|
|
1319
|
-
merged.indexing = normalizedProjectConfig.indexing;
|
|
1320
|
-
} else if (globalConfig && globalConfig.indexing) {
|
|
1321
|
-
merged.indexing = globalConfig.indexing;
|
|
1322
|
-
}
|
|
1323
|
-
if (projectConfig && "search" in normalizedProjectConfig) {
|
|
1324
|
-
merged.search = normalizedProjectConfig.search;
|
|
1325
|
-
} else if (globalConfig && globalConfig.search) {
|
|
1326
|
-
merged.search = globalConfig.search;
|
|
1327
|
-
}
|
|
1328
|
-
if (projectConfig && "debug" in normalizedProjectConfig) {
|
|
1329
|
-
merged.debug = normalizedProjectConfig.debug;
|
|
1330
|
-
} else if (globalConfig && globalConfig.debug) {
|
|
1331
|
-
merged.debug = globalConfig.debug;
|
|
1332
|
-
}
|
|
1333
|
-
if (projectConfig && "scope" in normalizedProjectConfig) {
|
|
1334
|
-
merged.scope = normalizedProjectConfig.scope;
|
|
1335
|
-
} else if (globalConfig && "scope" in globalConfig) {
|
|
1336
|
-
merged.scope = globalConfig.scope;
|
|
1434
|
+
for (const key of PROJECT_OVERRIDE_KEYS) {
|
|
1435
|
+
applyProjectOverride(merged, normalizedProjectConfig, globalConfig, key);
|
|
1337
1436
|
}
|
|
1338
1437
|
if (projectConfig) {
|
|
1339
1438
|
for (const key of Object.keys(projectConfig)) {
|
|
1340
|
-
if (key
|
|
1439
|
+
if (PROJECT_OVERRIDE_KEYS.includes(key) || MERGE_ARRAY_KEYS.includes(key)) {
|
|
1341
1440
|
continue;
|
|
1342
1441
|
}
|
|
1343
1442
|
merged[key] = normalizedProjectConfig[key];
|
|
@@ -1346,13 +1445,11 @@ function loadMergedConfig(projectRoot) {
|
|
|
1346
1445
|
const globalKbs = globalConfig && Array.isArray(globalConfig.knowledgeBases) ? globalConfig.knowledgeBases : [];
|
|
1347
1446
|
const projectKbs = projectConfig ? Array.isArray(normalizedProjectConfig.knowledgeBases) ? normalizedProjectConfig.knowledgeBases : [] : [];
|
|
1348
1447
|
const allKbs = [...globalKbs, ...projectKbs];
|
|
1349
|
-
|
|
1350
|
-
merged.knowledgeBases = uniqueKbs;
|
|
1448
|
+
merged.knowledgeBases = mergeKnowledgeBasePaths(allKbs);
|
|
1351
1449
|
const globalAdditional = globalConfig && Array.isArray(globalConfig.additionalInclude) ? globalConfig.additionalInclude : [];
|
|
1352
1450
|
const projectAdditional = projectConfig && Array.isArray(projectConfig.additionalInclude) ? projectConfig.additionalInclude : [];
|
|
1353
1451
|
const allAdditional = [...globalAdditional, ...projectAdditional];
|
|
1354
|
-
|
|
1355
|
-
merged.additionalInclude = uniqueAdditional;
|
|
1452
|
+
merged.additionalInclude = mergeUniqueStringArray(allAdditional);
|
|
1356
1453
|
return merged;
|
|
1357
1454
|
}
|
|
1358
1455
|
|
|
@@ -1446,7 +1543,7 @@ var ReaddirpStream = class extends Readable {
|
|
|
1446
1543
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
1447
1544
|
const statMethod = opts.lstat ? lstat : stat;
|
|
1448
1545
|
if (wantBigintFsStats) {
|
|
1449
|
-
this._stat = (
|
|
1546
|
+
this._stat = (path18) => statMethod(path18, { bigint: true });
|
|
1450
1547
|
} else {
|
|
1451
1548
|
this._stat = statMethod;
|
|
1452
1549
|
}
|
|
@@ -1471,8 +1568,8 @@ var ReaddirpStream = class extends Readable {
|
|
|
1471
1568
|
const par = this.parent;
|
|
1472
1569
|
const fil = par && par.files;
|
|
1473
1570
|
if (fil && fil.length > 0) {
|
|
1474
|
-
const { path:
|
|
1475
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
1571
|
+
const { path: path18, depth } = par;
|
|
1572
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path18));
|
|
1476
1573
|
const awaited = await Promise.all(slice);
|
|
1477
1574
|
for (const entry of awaited) {
|
|
1478
1575
|
if (!entry)
|
|
@@ -1512,20 +1609,20 @@ var ReaddirpStream = class extends Readable {
|
|
|
1512
1609
|
this.reading = false;
|
|
1513
1610
|
}
|
|
1514
1611
|
}
|
|
1515
|
-
async _exploreDir(
|
|
1612
|
+
async _exploreDir(path18, depth) {
|
|
1516
1613
|
let files;
|
|
1517
1614
|
try {
|
|
1518
|
-
files = await readdir(
|
|
1615
|
+
files = await readdir(path18, this._rdOptions);
|
|
1519
1616
|
} catch (error) {
|
|
1520
1617
|
this._onError(error);
|
|
1521
1618
|
}
|
|
1522
|
-
return { files, depth, path:
|
|
1619
|
+
return { files, depth, path: path18 };
|
|
1523
1620
|
}
|
|
1524
|
-
async _formatEntry(dirent,
|
|
1621
|
+
async _formatEntry(dirent, path18) {
|
|
1525
1622
|
let entry;
|
|
1526
1623
|
const basename5 = this._isDirent ? dirent.name : dirent;
|
|
1527
1624
|
try {
|
|
1528
|
-
const fullPath = presolve(pjoin(
|
|
1625
|
+
const fullPath = presolve(pjoin(path18, basename5));
|
|
1529
1626
|
entry = { path: prelative(this._root, fullPath), fullPath, basename: basename5 };
|
|
1530
1627
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
1531
1628
|
} catch (err) {
|
|
@@ -1925,16 +2022,16 @@ var delFromSet = (main, prop, item) => {
|
|
|
1925
2022
|
};
|
|
1926
2023
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
1927
2024
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
1928
|
-
function createFsWatchInstance(
|
|
2025
|
+
function createFsWatchInstance(path18, options, listener, errHandler, emitRaw) {
|
|
1929
2026
|
const handleEvent = (rawEvent, evPath) => {
|
|
1930
|
-
listener(
|
|
1931
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
1932
|
-
if (evPath &&
|
|
1933
|
-
fsWatchBroadcast(sp.resolve(
|
|
2027
|
+
listener(path18);
|
|
2028
|
+
emitRaw(rawEvent, evPath, { watchedPath: path18 });
|
|
2029
|
+
if (evPath && path18 !== evPath) {
|
|
2030
|
+
fsWatchBroadcast(sp.resolve(path18, evPath), KEY_LISTENERS, sp.join(path18, evPath));
|
|
1934
2031
|
}
|
|
1935
2032
|
};
|
|
1936
2033
|
try {
|
|
1937
|
-
return fs_watch(
|
|
2034
|
+
return fs_watch(path18, {
|
|
1938
2035
|
persistent: options.persistent
|
|
1939
2036
|
}, handleEvent);
|
|
1940
2037
|
} catch (error) {
|
|
@@ -1950,12 +2047,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
1950
2047
|
listener(val1, val2, val3);
|
|
1951
2048
|
});
|
|
1952
2049
|
};
|
|
1953
|
-
var setFsWatchListener = (
|
|
2050
|
+
var setFsWatchListener = (path18, fullPath, options, handlers) => {
|
|
1954
2051
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
1955
2052
|
let cont = FsWatchInstances.get(fullPath);
|
|
1956
2053
|
let watcher;
|
|
1957
2054
|
if (!options.persistent) {
|
|
1958
|
-
watcher = createFsWatchInstance(
|
|
2055
|
+
watcher = createFsWatchInstance(path18, options, listener, errHandler, rawEmitter);
|
|
1959
2056
|
if (!watcher)
|
|
1960
2057
|
return;
|
|
1961
2058
|
return watcher.close.bind(watcher);
|
|
@@ -1966,7 +2063,7 @@ var setFsWatchListener = (path12, fullPath, options, handlers) => {
|
|
|
1966
2063
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
1967
2064
|
} else {
|
|
1968
2065
|
watcher = createFsWatchInstance(
|
|
1969
|
-
|
|
2066
|
+
path18,
|
|
1970
2067
|
options,
|
|
1971
2068
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
1972
2069
|
errHandler,
|
|
@@ -1981,7 +2078,7 @@ var setFsWatchListener = (path12, fullPath, options, handlers) => {
|
|
|
1981
2078
|
cont.watcherUnusable = true;
|
|
1982
2079
|
if (isWindows && error.code === "EPERM") {
|
|
1983
2080
|
try {
|
|
1984
|
-
const fd = await open(
|
|
2081
|
+
const fd = await open(path18, "r");
|
|
1985
2082
|
await fd.close();
|
|
1986
2083
|
broadcastErr(error);
|
|
1987
2084
|
} catch (err) {
|
|
@@ -2012,7 +2109,7 @@ var setFsWatchListener = (path12, fullPath, options, handlers) => {
|
|
|
2012
2109
|
};
|
|
2013
2110
|
};
|
|
2014
2111
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
2015
|
-
var setFsWatchFileListener = (
|
|
2112
|
+
var setFsWatchFileListener = (path18, fullPath, options, handlers) => {
|
|
2016
2113
|
const { listener, rawEmitter } = handlers;
|
|
2017
2114
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
2018
2115
|
const copts = cont && cont.options;
|
|
@@ -2034,7 +2131,7 @@ var setFsWatchFileListener = (path12, fullPath, options, handlers) => {
|
|
|
2034
2131
|
});
|
|
2035
2132
|
const currmtime = curr.mtimeMs;
|
|
2036
2133
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
2037
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
2134
|
+
foreach(cont.listeners, (listener2) => listener2(path18, curr));
|
|
2038
2135
|
}
|
|
2039
2136
|
})
|
|
2040
2137
|
};
|
|
@@ -2064,13 +2161,13 @@ var NodeFsHandler = class {
|
|
|
2064
2161
|
* @param listener on fs change
|
|
2065
2162
|
* @returns closer for the watcher instance
|
|
2066
2163
|
*/
|
|
2067
|
-
_watchWithNodeFs(
|
|
2164
|
+
_watchWithNodeFs(path18, listener) {
|
|
2068
2165
|
const opts = this.fsw.options;
|
|
2069
|
-
const directory = sp.dirname(
|
|
2070
|
-
const basename5 = sp.basename(
|
|
2166
|
+
const directory = sp.dirname(path18);
|
|
2167
|
+
const basename5 = sp.basename(path18);
|
|
2071
2168
|
const parent = this.fsw._getWatchedDir(directory);
|
|
2072
2169
|
parent.add(basename5);
|
|
2073
|
-
const absolutePath = sp.resolve(
|
|
2170
|
+
const absolutePath = sp.resolve(path18);
|
|
2074
2171
|
const options = {
|
|
2075
2172
|
persistent: opts.persistent
|
|
2076
2173
|
};
|
|
@@ -2080,12 +2177,12 @@ var NodeFsHandler = class {
|
|
|
2080
2177
|
if (opts.usePolling) {
|
|
2081
2178
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
2082
2179
|
options.interval = enableBin && isBinaryPath(basename5) ? opts.binaryInterval : opts.interval;
|
|
2083
|
-
closer = setFsWatchFileListener(
|
|
2180
|
+
closer = setFsWatchFileListener(path18, absolutePath, options, {
|
|
2084
2181
|
listener,
|
|
2085
2182
|
rawEmitter: this.fsw._emitRaw
|
|
2086
2183
|
});
|
|
2087
2184
|
} else {
|
|
2088
|
-
closer = setFsWatchListener(
|
|
2185
|
+
closer = setFsWatchListener(path18, absolutePath, options, {
|
|
2089
2186
|
listener,
|
|
2090
2187
|
errHandler: this._boundHandleError,
|
|
2091
2188
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -2107,7 +2204,7 @@ var NodeFsHandler = class {
|
|
|
2107
2204
|
let prevStats = stats;
|
|
2108
2205
|
if (parent.has(basename5))
|
|
2109
2206
|
return;
|
|
2110
|
-
const listener = async (
|
|
2207
|
+
const listener = async (path18, newStats) => {
|
|
2111
2208
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
2112
2209
|
return;
|
|
2113
2210
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -2121,11 +2218,11 @@ var NodeFsHandler = class {
|
|
|
2121
2218
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
2122
2219
|
}
|
|
2123
2220
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
2124
|
-
this.fsw._closeFile(
|
|
2221
|
+
this.fsw._closeFile(path18);
|
|
2125
2222
|
prevStats = newStats2;
|
|
2126
2223
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
2127
2224
|
if (closer2)
|
|
2128
|
-
this.fsw._addPathCloser(
|
|
2225
|
+
this.fsw._addPathCloser(path18, closer2);
|
|
2129
2226
|
} else {
|
|
2130
2227
|
prevStats = newStats2;
|
|
2131
2228
|
}
|
|
@@ -2157,7 +2254,7 @@ var NodeFsHandler = class {
|
|
|
2157
2254
|
* @param item basename of this item
|
|
2158
2255
|
* @returns true if no more processing is needed for this entry.
|
|
2159
2256
|
*/
|
|
2160
|
-
async _handleSymlink(entry, directory,
|
|
2257
|
+
async _handleSymlink(entry, directory, path18, item) {
|
|
2161
2258
|
if (this.fsw.closed) {
|
|
2162
2259
|
return;
|
|
2163
2260
|
}
|
|
@@ -2167,7 +2264,7 @@ var NodeFsHandler = class {
|
|
|
2167
2264
|
this.fsw._incrReadyCount();
|
|
2168
2265
|
let linkPath;
|
|
2169
2266
|
try {
|
|
2170
|
-
linkPath = await fsrealpath(
|
|
2267
|
+
linkPath = await fsrealpath(path18);
|
|
2171
2268
|
} catch (e) {
|
|
2172
2269
|
this.fsw._emitReady();
|
|
2173
2270
|
return true;
|
|
@@ -2177,12 +2274,12 @@ var NodeFsHandler = class {
|
|
|
2177
2274
|
if (dir.has(item)) {
|
|
2178
2275
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
2179
2276
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
2180
|
-
this.fsw._emit(EV.CHANGE,
|
|
2277
|
+
this.fsw._emit(EV.CHANGE, path18, entry.stats);
|
|
2181
2278
|
}
|
|
2182
2279
|
} else {
|
|
2183
2280
|
dir.add(item);
|
|
2184
2281
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
2185
|
-
this.fsw._emit(EV.ADD,
|
|
2282
|
+
this.fsw._emit(EV.ADD, path18, entry.stats);
|
|
2186
2283
|
}
|
|
2187
2284
|
this.fsw._emitReady();
|
|
2188
2285
|
return true;
|
|
@@ -2212,9 +2309,9 @@ var NodeFsHandler = class {
|
|
|
2212
2309
|
return;
|
|
2213
2310
|
}
|
|
2214
2311
|
const item = entry.path;
|
|
2215
|
-
let
|
|
2312
|
+
let path18 = sp.join(directory, item);
|
|
2216
2313
|
current.add(item);
|
|
2217
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
2314
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path18, item)) {
|
|
2218
2315
|
return;
|
|
2219
2316
|
}
|
|
2220
2317
|
if (this.fsw.closed) {
|
|
@@ -2223,11 +2320,11 @@ var NodeFsHandler = class {
|
|
|
2223
2320
|
}
|
|
2224
2321
|
if (item === target || !target && !previous.has(item)) {
|
|
2225
2322
|
this.fsw._incrReadyCount();
|
|
2226
|
-
|
|
2227
|
-
this._addToNodeFs(
|
|
2323
|
+
path18 = sp.join(dir, sp.relative(dir, path18));
|
|
2324
|
+
this._addToNodeFs(path18, initialAdd, wh, depth + 1);
|
|
2228
2325
|
}
|
|
2229
2326
|
}).on(EV.ERROR, this._boundHandleError);
|
|
2230
|
-
return new Promise((
|
|
2327
|
+
return new Promise((resolve12, reject) => {
|
|
2231
2328
|
if (!stream)
|
|
2232
2329
|
return reject();
|
|
2233
2330
|
stream.once(STR_END, () => {
|
|
@@ -2236,7 +2333,7 @@ var NodeFsHandler = class {
|
|
|
2236
2333
|
return;
|
|
2237
2334
|
}
|
|
2238
2335
|
const wasThrottled = throttler ? throttler.clear() : false;
|
|
2239
|
-
|
|
2336
|
+
resolve12(void 0);
|
|
2240
2337
|
previous.getChildren().filter((item) => {
|
|
2241
2338
|
return item !== directory && !current.has(item);
|
|
2242
2339
|
}).forEach((item) => {
|
|
@@ -2293,13 +2390,13 @@ var NodeFsHandler = class {
|
|
|
2293
2390
|
* @param depth Child path actually targeted for watch
|
|
2294
2391
|
* @param target Child path actually targeted for watch
|
|
2295
2392
|
*/
|
|
2296
|
-
async _addToNodeFs(
|
|
2393
|
+
async _addToNodeFs(path18, initialAdd, priorWh, depth, target) {
|
|
2297
2394
|
const ready = this.fsw._emitReady;
|
|
2298
|
-
if (this.fsw._isIgnored(
|
|
2395
|
+
if (this.fsw._isIgnored(path18) || this.fsw.closed) {
|
|
2299
2396
|
ready();
|
|
2300
2397
|
return false;
|
|
2301
2398
|
}
|
|
2302
|
-
const wh = this.fsw._getWatchHelpers(
|
|
2399
|
+
const wh = this.fsw._getWatchHelpers(path18);
|
|
2303
2400
|
if (priorWh) {
|
|
2304
2401
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
2305
2402
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -2315,8 +2412,8 @@ var NodeFsHandler = class {
|
|
|
2315
2412
|
const follow = this.fsw.options.followSymlinks;
|
|
2316
2413
|
let closer;
|
|
2317
2414
|
if (stats.isDirectory()) {
|
|
2318
|
-
const absPath = sp.resolve(
|
|
2319
|
-
const targetPath = follow ? await fsrealpath(
|
|
2415
|
+
const absPath = sp.resolve(path18);
|
|
2416
|
+
const targetPath = follow ? await fsrealpath(path18) : path18;
|
|
2320
2417
|
if (this.fsw.closed)
|
|
2321
2418
|
return;
|
|
2322
2419
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -2326,29 +2423,29 @@ var NodeFsHandler = class {
|
|
|
2326
2423
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
2327
2424
|
}
|
|
2328
2425
|
} else if (stats.isSymbolicLink()) {
|
|
2329
|
-
const targetPath = follow ? await fsrealpath(
|
|
2426
|
+
const targetPath = follow ? await fsrealpath(path18) : path18;
|
|
2330
2427
|
if (this.fsw.closed)
|
|
2331
2428
|
return;
|
|
2332
2429
|
const parent = sp.dirname(wh.watchPath);
|
|
2333
2430
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
2334
2431
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
2335
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
2432
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path18, wh, targetPath);
|
|
2336
2433
|
if (this.fsw.closed)
|
|
2337
2434
|
return;
|
|
2338
2435
|
if (targetPath !== void 0) {
|
|
2339
|
-
this.fsw._symlinkPaths.set(sp.resolve(
|
|
2436
|
+
this.fsw._symlinkPaths.set(sp.resolve(path18), targetPath);
|
|
2340
2437
|
}
|
|
2341
2438
|
} else {
|
|
2342
2439
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
2343
2440
|
}
|
|
2344
2441
|
ready();
|
|
2345
2442
|
if (closer)
|
|
2346
|
-
this.fsw._addPathCloser(
|
|
2443
|
+
this.fsw._addPathCloser(path18, closer);
|
|
2347
2444
|
return false;
|
|
2348
2445
|
} catch (error) {
|
|
2349
2446
|
if (this.fsw._handleError(error)) {
|
|
2350
2447
|
ready();
|
|
2351
|
-
return
|
|
2448
|
+
return path18;
|
|
2352
2449
|
}
|
|
2353
2450
|
}
|
|
2354
2451
|
}
|
|
@@ -2391,24 +2488,24 @@ function createPattern(matcher) {
|
|
|
2391
2488
|
}
|
|
2392
2489
|
return () => false;
|
|
2393
2490
|
}
|
|
2394
|
-
function normalizePath(
|
|
2395
|
-
if (typeof
|
|
2491
|
+
function normalizePath(path18) {
|
|
2492
|
+
if (typeof path18 !== "string")
|
|
2396
2493
|
throw new Error("string expected");
|
|
2397
|
-
|
|
2398
|
-
|
|
2494
|
+
path18 = sp2.normalize(path18);
|
|
2495
|
+
path18 = path18.replace(/\\/g, "/");
|
|
2399
2496
|
let prepend = false;
|
|
2400
|
-
if (
|
|
2497
|
+
if (path18.startsWith("//"))
|
|
2401
2498
|
prepend = true;
|
|
2402
|
-
|
|
2499
|
+
path18 = path18.replace(DOUBLE_SLASH_RE, "/");
|
|
2403
2500
|
if (prepend)
|
|
2404
|
-
|
|
2405
|
-
return
|
|
2501
|
+
path18 = "/" + path18;
|
|
2502
|
+
return path18;
|
|
2406
2503
|
}
|
|
2407
2504
|
function matchPatterns(patterns, testString, stats) {
|
|
2408
|
-
const
|
|
2505
|
+
const path18 = normalizePath(testString);
|
|
2409
2506
|
for (let index = 0; index < patterns.length; index++) {
|
|
2410
2507
|
const pattern = patterns[index];
|
|
2411
|
-
if (pattern(
|
|
2508
|
+
if (pattern(path18, stats)) {
|
|
2412
2509
|
return true;
|
|
2413
2510
|
}
|
|
2414
2511
|
}
|
|
@@ -2446,19 +2543,19 @@ var toUnix = (string) => {
|
|
|
2446
2543
|
}
|
|
2447
2544
|
return str;
|
|
2448
2545
|
};
|
|
2449
|
-
var normalizePathToUnix = (
|
|
2450
|
-
var normalizeIgnored = (cwd = "") => (
|
|
2451
|
-
if (typeof
|
|
2452
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
2546
|
+
var normalizePathToUnix = (path18) => toUnix(sp2.normalize(toUnix(path18)));
|
|
2547
|
+
var normalizeIgnored = (cwd = "") => (path18) => {
|
|
2548
|
+
if (typeof path18 === "string") {
|
|
2549
|
+
return normalizePathToUnix(sp2.isAbsolute(path18) ? path18 : sp2.join(cwd, path18));
|
|
2453
2550
|
} else {
|
|
2454
|
-
return
|
|
2551
|
+
return path18;
|
|
2455
2552
|
}
|
|
2456
2553
|
};
|
|
2457
|
-
var getAbsolutePath = (
|
|
2458
|
-
if (sp2.isAbsolute(
|
|
2459
|
-
return
|
|
2554
|
+
var getAbsolutePath = (path18, cwd) => {
|
|
2555
|
+
if (sp2.isAbsolute(path18)) {
|
|
2556
|
+
return path18;
|
|
2460
2557
|
}
|
|
2461
|
-
return sp2.join(cwd,
|
|
2558
|
+
return sp2.join(cwd, path18);
|
|
2462
2559
|
};
|
|
2463
2560
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
2464
2561
|
var DirEntry = class {
|
|
@@ -2523,10 +2620,10 @@ var WatchHelper = class {
|
|
|
2523
2620
|
dirParts;
|
|
2524
2621
|
followSymlinks;
|
|
2525
2622
|
statMethod;
|
|
2526
|
-
constructor(
|
|
2623
|
+
constructor(path18, follow, fsw) {
|
|
2527
2624
|
this.fsw = fsw;
|
|
2528
|
-
const watchPath =
|
|
2529
|
-
this.path =
|
|
2625
|
+
const watchPath = path18;
|
|
2626
|
+
this.path = path18 = path18.replace(REPLACER_RE, "");
|
|
2530
2627
|
this.watchPath = watchPath;
|
|
2531
2628
|
this.fullWatchPath = sp2.resolve(watchPath);
|
|
2532
2629
|
this.dirParts = [];
|
|
@@ -2666,20 +2763,20 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2666
2763
|
this._closePromise = void 0;
|
|
2667
2764
|
let paths = unifyPaths(paths_);
|
|
2668
2765
|
if (cwd) {
|
|
2669
|
-
paths = paths.map((
|
|
2670
|
-
const absPath = getAbsolutePath(
|
|
2766
|
+
paths = paths.map((path18) => {
|
|
2767
|
+
const absPath = getAbsolutePath(path18, cwd);
|
|
2671
2768
|
return absPath;
|
|
2672
2769
|
});
|
|
2673
2770
|
}
|
|
2674
|
-
paths.forEach((
|
|
2675
|
-
this._removeIgnoredPath(
|
|
2771
|
+
paths.forEach((path18) => {
|
|
2772
|
+
this._removeIgnoredPath(path18);
|
|
2676
2773
|
});
|
|
2677
2774
|
this._userIgnored = void 0;
|
|
2678
2775
|
if (!this._readyCount)
|
|
2679
2776
|
this._readyCount = 0;
|
|
2680
2777
|
this._readyCount += paths.length;
|
|
2681
|
-
Promise.all(paths.map(async (
|
|
2682
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
2778
|
+
Promise.all(paths.map(async (path18) => {
|
|
2779
|
+
const res = await this._nodeFsHandler._addToNodeFs(path18, !_internal, void 0, 0, _origAdd);
|
|
2683
2780
|
if (res)
|
|
2684
2781
|
this._emitReady();
|
|
2685
2782
|
return res;
|
|
@@ -2701,17 +2798,17 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2701
2798
|
return this;
|
|
2702
2799
|
const paths = unifyPaths(paths_);
|
|
2703
2800
|
const { cwd } = this.options;
|
|
2704
|
-
paths.forEach((
|
|
2705
|
-
if (!sp2.isAbsolute(
|
|
2801
|
+
paths.forEach((path18) => {
|
|
2802
|
+
if (!sp2.isAbsolute(path18) && !this._closers.has(path18)) {
|
|
2706
2803
|
if (cwd)
|
|
2707
|
-
|
|
2708
|
-
|
|
2804
|
+
path18 = sp2.join(cwd, path18);
|
|
2805
|
+
path18 = sp2.resolve(path18);
|
|
2709
2806
|
}
|
|
2710
|
-
this._closePath(
|
|
2711
|
-
this._addIgnoredPath(
|
|
2712
|
-
if (this._watched.has(
|
|
2807
|
+
this._closePath(path18);
|
|
2808
|
+
this._addIgnoredPath(path18);
|
|
2809
|
+
if (this._watched.has(path18)) {
|
|
2713
2810
|
this._addIgnoredPath({
|
|
2714
|
-
path:
|
|
2811
|
+
path: path18,
|
|
2715
2812
|
recursive: true
|
|
2716
2813
|
});
|
|
2717
2814
|
}
|
|
@@ -2775,38 +2872,38 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2775
2872
|
* @param stats arguments to be passed with event
|
|
2776
2873
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
2777
2874
|
*/
|
|
2778
|
-
async _emit(event,
|
|
2875
|
+
async _emit(event, path18, stats) {
|
|
2779
2876
|
if (this.closed)
|
|
2780
2877
|
return;
|
|
2781
2878
|
const opts = this.options;
|
|
2782
2879
|
if (isWindows)
|
|
2783
|
-
|
|
2880
|
+
path18 = sp2.normalize(path18);
|
|
2784
2881
|
if (opts.cwd)
|
|
2785
|
-
|
|
2786
|
-
const args = [
|
|
2882
|
+
path18 = sp2.relative(opts.cwd, path18);
|
|
2883
|
+
const args = [path18];
|
|
2787
2884
|
if (stats != null)
|
|
2788
2885
|
args.push(stats);
|
|
2789
2886
|
const awf = opts.awaitWriteFinish;
|
|
2790
2887
|
let pw;
|
|
2791
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
2888
|
+
if (awf && (pw = this._pendingWrites.get(path18))) {
|
|
2792
2889
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
2793
2890
|
return this;
|
|
2794
2891
|
}
|
|
2795
2892
|
if (opts.atomic) {
|
|
2796
2893
|
if (event === EVENTS.UNLINK) {
|
|
2797
|
-
this._pendingUnlinks.set(
|
|
2894
|
+
this._pendingUnlinks.set(path18, [event, ...args]);
|
|
2798
2895
|
setTimeout(() => {
|
|
2799
|
-
this._pendingUnlinks.forEach((entry,
|
|
2896
|
+
this._pendingUnlinks.forEach((entry, path19) => {
|
|
2800
2897
|
this.emit(...entry);
|
|
2801
2898
|
this.emit(EVENTS.ALL, ...entry);
|
|
2802
|
-
this._pendingUnlinks.delete(
|
|
2899
|
+
this._pendingUnlinks.delete(path19);
|
|
2803
2900
|
});
|
|
2804
2901
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
2805
2902
|
return this;
|
|
2806
2903
|
}
|
|
2807
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
2904
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path18)) {
|
|
2808
2905
|
event = EVENTS.CHANGE;
|
|
2809
|
-
this._pendingUnlinks.delete(
|
|
2906
|
+
this._pendingUnlinks.delete(path18);
|
|
2810
2907
|
}
|
|
2811
2908
|
}
|
|
2812
2909
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -2824,16 +2921,16 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2824
2921
|
this.emitWithAll(event, args);
|
|
2825
2922
|
}
|
|
2826
2923
|
};
|
|
2827
|
-
this._awaitWriteFinish(
|
|
2924
|
+
this._awaitWriteFinish(path18, awf.stabilityThreshold, event, awfEmit);
|
|
2828
2925
|
return this;
|
|
2829
2926
|
}
|
|
2830
2927
|
if (event === EVENTS.CHANGE) {
|
|
2831
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
2928
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path18, 50);
|
|
2832
2929
|
if (isThrottled)
|
|
2833
2930
|
return this;
|
|
2834
2931
|
}
|
|
2835
2932
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
2836
|
-
const fullPath = opts.cwd ? sp2.join(opts.cwd,
|
|
2933
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path18) : path18;
|
|
2837
2934
|
let stats2;
|
|
2838
2935
|
try {
|
|
2839
2936
|
stats2 = await stat3(fullPath);
|
|
@@ -2864,23 +2961,23 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2864
2961
|
* @param timeout duration of time to suppress duplicate actions
|
|
2865
2962
|
* @returns tracking object or false if action should be suppressed
|
|
2866
2963
|
*/
|
|
2867
|
-
_throttle(actionType,
|
|
2964
|
+
_throttle(actionType, path18, timeout) {
|
|
2868
2965
|
if (!this._throttled.has(actionType)) {
|
|
2869
2966
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
2870
2967
|
}
|
|
2871
2968
|
const action = this._throttled.get(actionType);
|
|
2872
2969
|
if (!action)
|
|
2873
2970
|
throw new Error("invalid throttle");
|
|
2874
|
-
const actionPath = action.get(
|
|
2971
|
+
const actionPath = action.get(path18);
|
|
2875
2972
|
if (actionPath) {
|
|
2876
2973
|
actionPath.count++;
|
|
2877
2974
|
return false;
|
|
2878
2975
|
}
|
|
2879
2976
|
let timeoutObject;
|
|
2880
2977
|
const clear = () => {
|
|
2881
|
-
const item = action.get(
|
|
2978
|
+
const item = action.get(path18);
|
|
2882
2979
|
const count = item ? item.count : 0;
|
|
2883
|
-
action.delete(
|
|
2980
|
+
action.delete(path18);
|
|
2884
2981
|
clearTimeout(timeoutObject);
|
|
2885
2982
|
if (item)
|
|
2886
2983
|
clearTimeout(item.timeoutObject);
|
|
@@ -2888,7 +2985,7 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2888
2985
|
};
|
|
2889
2986
|
timeoutObject = setTimeout(clear, timeout);
|
|
2890
2987
|
const thr = { timeoutObject, clear, count: 0 };
|
|
2891
|
-
action.set(
|
|
2988
|
+
action.set(path18, thr);
|
|
2892
2989
|
return thr;
|
|
2893
2990
|
}
|
|
2894
2991
|
_incrReadyCount() {
|
|
@@ -2902,44 +2999,44 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2902
2999
|
* @param event
|
|
2903
3000
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
2904
3001
|
*/
|
|
2905
|
-
_awaitWriteFinish(
|
|
3002
|
+
_awaitWriteFinish(path18, threshold, event, awfEmit) {
|
|
2906
3003
|
const awf = this.options.awaitWriteFinish;
|
|
2907
3004
|
if (typeof awf !== "object")
|
|
2908
3005
|
return;
|
|
2909
3006
|
const pollInterval = awf.pollInterval;
|
|
2910
3007
|
let timeoutHandler;
|
|
2911
|
-
let fullPath =
|
|
2912
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
2913
|
-
fullPath = sp2.join(this.options.cwd,
|
|
3008
|
+
let fullPath = path18;
|
|
3009
|
+
if (this.options.cwd && !sp2.isAbsolute(path18)) {
|
|
3010
|
+
fullPath = sp2.join(this.options.cwd, path18);
|
|
2914
3011
|
}
|
|
2915
3012
|
const now = /* @__PURE__ */ new Date();
|
|
2916
3013
|
const writes = this._pendingWrites;
|
|
2917
3014
|
function awaitWriteFinishFn(prevStat) {
|
|
2918
3015
|
statcb(fullPath, (err, curStat) => {
|
|
2919
|
-
if (err || !writes.has(
|
|
3016
|
+
if (err || !writes.has(path18)) {
|
|
2920
3017
|
if (err && err.code !== "ENOENT")
|
|
2921
3018
|
awfEmit(err);
|
|
2922
3019
|
return;
|
|
2923
3020
|
}
|
|
2924
3021
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
2925
3022
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
2926
|
-
writes.get(
|
|
3023
|
+
writes.get(path18).lastChange = now2;
|
|
2927
3024
|
}
|
|
2928
|
-
const pw = writes.get(
|
|
3025
|
+
const pw = writes.get(path18);
|
|
2929
3026
|
const df = now2 - pw.lastChange;
|
|
2930
3027
|
if (df >= threshold) {
|
|
2931
|
-
writes.delete(
|
|
3028
|
+
writes.delete(path18);
|
|
2932
3029
|
awfEmit(void 0, curStat);
|
|
2933
3030
|
} else {
|
|
2934
3031
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
2935
3032
|
}
|
|
2936
3033
|
});
|
|
2937
3034
|
}
|
|
2938
|
-
if (!writes.has(
|
|
2939
|
-
writes.set(
|
|
3035
|
+
if (!writes.has(path18)) {
|
|
3036
|
+
writes.set(path18, {
|
|
2940
3037
|
lastChange: now,
|
|
2941
3038
|
cancelWait: () => {
|
|
2942
|
-
writes.delete(
|
|
3039
|
+
writes.delete(path18);
|
|
2943
3040
|
clearTimeout(timeoutHandler);
|
|
2944
3041
|
return event;
|
|
2945
3042
|
}
|
|
@@ -2950,8 +3047,8 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2950
3047
|
/**
|
|
2951
3048
|
* Determines whether user has asked to ignore this path.
|
|
2952
3049
|
*/
|
|
2953
|
-
_isIgnored(
|
|
2954
|
-
if (this.options.atomic && DOT_RE.test(
|
|
3050
|
+
_isIgnored(path18, stats) {
|
|
3051
|
+
if (this.options.atomic && DOT_RE.test(path18))
|
|
2955
3052
|
return true;
|
|
2956
3053
|
if (!this._userIgnored) {
|
|
2957
3054
|
const { cwd } = this.options;
|
|
@@ -2961,17 +3058,17 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2961
3058
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
2962
3059
|
this._userIgnored = anymatch(list, void 0);
|
|
2963
3060
|
}
|
|
2964
|
-
return this._userIgnored(
|
|
3061
|
+
return this._userIgnored(path18, stats);
|
|
2965
3062
|
}
|
|
2966
|
-
_isntIgnored(
|
|
2967
|
-
return !this._isIgnored(
|
|
3063
|
+
_isntIgnored(path18, stat4) {
|
|
3064
|
+
return !this._isIgnored(path18, stat4);
|
|
2968
3065
|
}
|
|
2969
3066
|
/**
|
|
2970
3067
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
2971
3068
|
* @param path file or directory pattern being watched
|
|
2972
3069
|
*/
|
|
2973
|
-
_getWatchHelpers(
|
|
2974
|
-
return new WatchHelper(
|
|
3070
|
+
_getWatchHelpers(path18) {
|
|
3071
|
+
return new WatchHelper(path18, this.options.followSymlinks, this);
|
|
2975
3072
|
}
|
|
2976
3073
|
// Directory helpers
|
|
2977
3074
|
// -----------------
|
|
@@ -3003,63 +3100,63 @@ var FSWatcher = class extends EventEmitter {
|
|
|
3003
3100
|
* @param item base path of item/directory
|
|
3004
3101
|
*/
|
|
3005
3102
|
_remove(directory, item, isDirectory) {
|
|
3006
|
-
const
|
|
3007
|
-
const fullPath = sp2.resolve(
|
|
3008
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
3009
|
-
if (!this._throttle("remove",
|
|
3103
|
+
const path18 = sp2.join(directory, item);
|
|
3104
|
+
const fullPath = sp2.resolve(path18);
|
|
3105
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path18) || this._watched.has(fullPath);
|
|
3106
|
+
if (!this._throttle("remove", path18, 100))
|
|
3010
3107
|
return;
|
|
3011
3108
|
if (!isDirectory && this._watched.size === 1) {
|
|
3012
3109
|
this.add(directory, item, true);
|
|
3013
3110
|
}
|
|
3014
|
-
const wp = this._getWatchedDir(
|
|
3111
|
+
const wp = this._getWatchedDir(path18);
|
|
3015
3112
|
const nestedDirectoryChildren = wp.getChildren();
|
|
3016
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
3113
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path18, nested));
|
|
3017
3114
|
const parent = this._getWatchedDir(directory);
|
|
3018
3115
|
const wasTracked = parent.has(item);
|
|
3019
3116
|
parent.remove(item);
|
|
3020
3117
|
if (this._symlinkPaths.has(fullPath)) {
|
|
3021
3118
|
this._symlinkPaths.delete(fullPath);
|
|
3022
3119
|
}
|
|
3023
|
-
let relPath =
|
|
3120
|
+
let relPath = path18;
|
|
3024
3121
|
if (this.options.cwd)
|
|
3025
|
-
relPath = sp2.relative(this.options.cwd,
|
|
3122
|
+
relPath = sp2.relative(this.options.cwd, path18);
|
|
3026
3123
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
3027
3124
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
3028
3125
|
if (event === EVENTS.ADD)
|
|
3029
3126
|
return;
|
|
3030
3127
|
}
|
|
3031
|
-
this._watched.delete(
|
|
3128
|
+
this._watched.delete(path18);
|
|
3032
3129
|
this._watched.delete(fullPath);
|
|
3033
3130
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
3034
|
-
if (wasTracked && !this._isIgnored(
|
|
3035
|
-
this._emit(eventName,
|
|
3036
|
-
this._closePath(
|
|
3131
|
+
if (wasTracked && !this._isIgnored(path18))
|
|
3132
|
+
this._emit(eventName, path18);
|
|
3133
|
+
this._closePath(path18);
|
|
3037
3134
|
}
|
|
3038
3135
|
/**
|
|
3039
3136
|
* Closes all watchers for a path
|
|
3040
3137
|
*/
|
|
3041
|
-
_closePath(
|
|
3042
|
-
this._closeFile(
|
|
3043
|
-
const dir = sp2.dirname(
|
|
3044
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
3138
|
+
_closePath(path18) {
|
|
3139
|
+
this._closeFile(path18);
|
|
3140
|
+
const dir = sp2.dirname(path18);
|
|
3141
|
+
this._getWatchedDir(dir).remove(sp2.basename(path18));
|
|
3045
3142
|
}
|
|
3046
3143
|
/**
|
|
3047
3144
|
* Closes only file-specific watchers
|
|
3048
3145
|
*/
|
|
3049
|
-
_closeFile(
|
|
3050
|
-
const closers = this._closers.get(
|
|
3146
|
+
_closeFile(path18) {
|
|
3147
|
+
const closers = this._closers.get(path18);
|
|
3051
3148
|
if (!closers)
|
|
3052
3149
|
return;
|
|
3053
3150
|
closers.forEach((closer) => closer());
|
|
3054
|
-
this._closers.delete(
|
|
3151
|
+
this._closers.delete(path18);
|
|
3055
3152
|
}
|
|
3056
|
-
_addPathCloser(
|
|
3153
|
+
_addPathCloser(path18, closer) {
|
|
3057
3154
|
if (!closer)
|
|
3058
3155
|
return;
|
|
3059
|
-
let list = this._closers.get(
|
|
3156
|
+
let list = this._closers.get(path18);
|
|
3060
3157
|
if (!list) {
|
|
3061
3158
|
list = [];
|
|
3062
|
-
this._closers.set(
|
|
3159
|
+
this._closers.set(path18, list);
|
|
3063
3160
|
}
|
|
3064
3161
|
list.push(closer);
|
|
3065
3162
|
}
|
|
@@ -3088,13 +3185,13 @@ function watch(paths, options = {}) {
|
|
|
3088
3185
|
}
|
|
3089
3186
|
var chokidar_default = { watch, FSWatcher };
|
|
3090
3187
|
|
|
3091
|
-
// src/watcher/
|
|
3092
|
-
import * as
|
|
3188
|
+
// src/watcher/file-watcher.ts
|
|
3189
|
+
import * as path8 from "path";
|
|
3093
3190
|
|
|
3094
3191
|
// src/utils/files.ts
|
|
3095
3192
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
3096
|
-
import { existsSync as
|
|
3097
|
-
import * as
|
|
3193
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, promises as fsPromises } from "fs";
|
|
3194
|
+
import * as path7 from "path";
|
|
3098
3195
|
var PROJECT_MARKERS = [
|
|
3099
3196
|
".git",
|
|
3100
3197
|
"package.json",
|
|
@@ -3113,7 +3210,7 @@ var PROJECT_MARKERS = [
|
|
|
3113
3210
|
];
|
|
3114
3211
|
function hasProjectMarker(projectRoot) {
|
|
3115
3212
|
for (const marker of PROJECT_MARKERS) {
|
|
3116
|
-
if (
|
|
3213
|
+
if (existsSync5(path7.join(projectRoot, marker))) {
|
|
3117
3214
|
return true;
|
|
3118
3215
|
}
|
|
3119
3216
|
}
|
|
@@ -3139,23 +3236,17 @@ function createIgnoreFilter(projectRoot) {
|
|
|
3139
3236
|
"**/*build*/**"
|
|
3140
3237
|
];
|
|
3141
3238
|
ig.add(defaultIgnores);
|
|
3142
|
-
const gitignorePath =
|
|
3143
|
-
if (
|
|
3144
|
-
const gitignoreContent =
|
|
3239
|
+
const gitignorePath = path7.join(projectRoot, ".gitignore");
|
|
3240
|
+
if (existsSync5(gitignorePath)) {
|
|
3241
|
+
const gitignoreContent = readFileSync4(gitignorePath, "utf-8");
|
|
3145
3242
|
ig.add(gitignoreContent);
|
|
3146
3243
|
}
|
|
3147
3244
|
return ig;
|
|
3148
3245
|
}
|
|
3149
3246
|
function shouldIncludeFile(filePath, projectRoot, includePatterns, excludePatterns, ignoreFilter) {
|
|
3150
|
-
const relativePath =
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
if (part.startsWith(".") && part !== "." && part !== "..") {
|
|
3154
|
-
return false;
|
|
3155
|
-
}
|
|
3156
|
-
if (part.toLowerCase().includes("build")) {
|
|
3157
|
-
return false;
|
|
3158
|
-
}
|
|
3247
|
+
const relativePath = path7.relative(projectRoot, filePath);
|
|
3248
|
+
if (hasFilteredPathSegment(relativePath, path7.sep)) {
|
|
3249
|
+
return false;
|
|
3159
3250
|
}
|
|
3160
3251
|
if (ignoreFilter.ignores(relativePath)) {
|
|
3161
3252
|
return false;
|
|
@@ -3192,15 +3283,15 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
|
|
|
3192
3283
|
const filesInDir = [];
|
|
3193
3284
|
const subdirs = [];
|
|
3194
3285
|
for (const entry of entries) {
|
|
3195
|
-
const fullPath =
|
|
3196
|
-
const relativePath =
|
|
3197
|
-
if (
|
|
3286
|
+
const fullPath = path7.join(dir, entry.name);
|
|
3287
|
+
const relativePath = path7.relative(projectRoot, fullPath);
|
|
3288
|
+
if (isHiddenPathSegment(entry.name)) {
|
|
3198
3289
|
if (entry.isDirectory()) {
|
|
3199
3290
|
skipped.push({ path: relativePath, reason: "excluded" });
|
|
3200
3291
|
}
|
|
3201
3292
|
continue;
|
|
3202
3293
|
}
|
|
3203
|
-
if (entry.isDirectory() && entry.name
|
|
3294
|
+
if (entry.isDirectory() && isBuildPathSegment(entry.name)) {
|
|
3204
3295
|
skipped.push({ path: relativePath, reason: "excluded" });
|
|
3205
3296
|
continue;
|
|
3206
3297
|
}
|
|
@@ -3242,7 +3333,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
|
|
|
3242
3333
|
yield f;
|
|
3243
3334
|
}
|
|
3244
3335
|
for (let i = options.maxFilesPerDirectory; i < filesInDir.length; i++) {
|
|
3245
|
-
skipped.push({ path:
|
|
3336
|
+
skipped.push({ path: path7.relative(projectRoot, filesInDir[i].path), reason: "excluded" });
|
|
3246
3337
|
}
|
|
3247
3338
|
const canRecurse = options.maxDepth === -1 || currentDepth < options.maxDepth;
|
|
3248
3339
|
if (canRecurse) {
|
|
@@ -3282,8 +3373,8 @@ async function collectFiles(projectRoot, includePatterns, excludePatterns, maxFi
|
|
|
3282
3373
|
if (additionalRoots && additionalRoots.length > 0) {
|
|
3283
3374
|
const normalizedRoots = /* @__PURE__ */ new Set();
|
|
3284
3375
|
for (const kbRoot of additionalRoots) {
|
|
3285
|
-
const resolved =
|
|
3286
|
-
|
|
3376
|
+
const resolved = path7.normalize(
|
|
3377
|
+
path7.isAbsolute(kbRoot) ? kbRoot : path7.resolve(projectRoot, kbRoot)
|
|
3287
3378
|
);
|
|
3288
3379
|
normalizedRoots.add(resolved);
|
|
3289
3380
|
}
|
|
@@ -3316,7 +3407,7 @@ async function collectFiles(projectRoot, includePatterns, excludePatterns, maxFi
|
|
|
3316
3407
|
return { files, skipped };
|
|
3317
3408
|
}
|
|
3318
3409
|
|
|
3319
|
-
// src/watcher/
|
|
3410
|
+
// src/watcher/file-watcher.ts
|
|
3320
3411
|
var FileWatcher = class {
|
|
3321
3412
|
watcher = null;
|
|
3322
3413
|
projectRoot;
|
|
@@ -3337,16 +3428,13 @@ var FileWatcher = class {
|
|
|
3337
3428
|
const ignoreFilter = createIgnoreFilter(this.projectRoot);
|
|
3338
3429
|
this.watcher = chokidar_default.watch(this.projectRoot, {
|
|
3339
3430
|
ignored: (filePath) => {
|
|
3340
|
-
const relativePath =
|
|
3431
|
+
const relativePath = path8.relative(this.projectRoot, filePath);
|
|
3341
3432
|
if (!relativePath) return false;
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
if (part.toLowerCase().includes("build")) {
|
|
3348
|
-
return true;
|
|
3349
|
-
}
|
|
3433
|
+
if (hasFilteredPathSegment(relativePath, path8.sep)) {
|
|
3434
|
+
return true;
|
|
3435
|
+
}
|
|
3436
|
+
if (isRestrictedDirectory(relativePath, path8.sep)) {
|
|
3437
|
+
return true;
|
|
3350
3438
|
}
|
|
3351
3439
|
if (ignoreFilter.ignores(relativePath)) {
|
|
3352
3440
|
return true;
|
|
@@ -3360,6 +3448,13 @@ var FileWatcher = class {
|
|
|
3360
3448
|
pollInterval: 100
|
|
3361
3449
|
}
|
|
3362
3450
|
});
|
|
3451
|
+
this.watcher.on("error", (error) => {
|
|
3452
|
+
const err = error instanceof Error ? error : null;
|
|
3453
|
+
if (err?.code === "EPERM" || err?.code === "EACCES") {
|
|
3454
|
+
return;
|
|
3455
|
+
}
|
|
3456
|
+
console.error("[codebase-index] Watcher error:", err?.message ?? error);
|
|
3457
|
+
});
|
|
3363
3458
|
this.watcher.on("add", (filePath) => this.handleChange("add", filePath));
|
|
3364
3459
|
this.watcher.on("change", (filePath) => this.handleChange("change", filePath));
|
|
3365
3460
|
this.watcher.on("unlink", (filePath) => this.handleChange("unlink", filePath));
|
|
@@ -3391,7 +3486,7 @@ var FileWatcher = class {
|
|
|
3391
3486
|
return;
|
|
3392
3487
|
}
|
|
3393
3488
|
const changes = Array.from(this.pendingChanges.entries()).map(
|
|
3394
|
-
([
|
|
3489
|
+
([path18, type]) => ({ path: path18, type })
|
|
3395
3490
|
);
|
|
3396
3491
|
this.pendingChanges.clear();
|
|
3397
3492
|
try {
|
|
@@ -3416,6 +3511,9 @@ var FileWatcher = class {
|
|
|
3416
3511
|
return this.watcher !== null;
|
|
3417
3512
|
}
|
|
3418
3513
|
};
|
|
3514
|
+
|
|
3515
|
+
// src/watcher/git-head-watcher.ts
|
|
3516
|
+
import * as path9 from "path";
|
|
3419
3517
|
var GitHeadWatcher = class {
|
|
3420
3518
|
watcher = null;
|
|
3421
3519
|
projectRoot;
|
|
@@ -3437,7 +3535,7 @@ var GitHeadWatcher = class {
|
|
|
3437
3535
|
this.onBranchChange = handler;
|
|
3438
3536
|
this.currentBranch = getCurrentBranch(this.projectRoot);
|
|
3439
3537
|
const headPath = getHeadPath(this.projectRoot);
|
|
3440
|
-
const refsPath =
|
|
3538
|
+
const refsPath = path9.join(this.projectRoot, ".git", "refs", "heads");
|
|
3441
3539
|
this.watcher = chokidar_default.watch([headPath, refsPath], {
|
|
3442
3540
|
persistent: true,
|
|
3443
3541
|
ignoreInitial: true,
|
|
@@ -3489,7 +3587,9 @@ var GitHeadWatcher = class {
|
|
|
3489
3587
|
return this.watcher !== null;
|
|
3490
3588
|
}
|
|
3491
3589
|
};
|
|
3492
|
-
|
|
3590
|
+
|
|
3591
|
+
// src/watcher/index.ts
|
|
3592
|
+
function createWatcherWithIndexer(getIndexer, projectRoot, config) {
|
|
3493
3593
|
const fileWatcher = new FileWatcher(projectRoot, config);
|
|
3494
3594
|
fileWatcher.start(async (changes) => {
|
|
3495
3595
|
const hasAddOrChange = changes.some(
|
|
@@ -3497,7 +3597,7 @@ function createWatcherWithIndexer(getIndexer2, projectRoot, config) {
|
|
|
3497
3597
|
);
|
|
3498
3598
|
const hasDelete = changes.some((c) => c.type === "unlink");
|
|
3499
3599
|
if (hasAddOrChange || hasDelete) {
|
|
3500
|
-
await
|
|
3600
|
+
await getIndexer().index();
|
|
3501
3601
|
}
|
|
3502
3602
|
});
|
|
3503
3603
|
let gitWatcher = null;
|
|
@@ -3505,7 +3605,7 @@ function createWatcherWithIndexer(getIndexer2, projectRoot, config) {
|
|
|
3505
3605
|
gitWatcher = new GitHeadWatcher(projectRoot);
|
|
3506
3606
|
gitWatcher.start(async (oldBranch, newBranch) => {
|
|
3507
3607
|
console.log(`Branch changed: ${oldBranch ?? "(none)"} -> ${newBranch}`);
|
|
3508
|
-
await
|
|
3608
|
+
await getIndexer().index();
|
|
3509
3609
|
});
|
|
3510
3610
|
}
|
|
3511
3611
|
return {
|
|
@@ -3522,8 +3622,8 @@ function createWatcherWithIndexer(getIndexer2, projectRoot, config) {
|
|
|
3522
3622
|
import { tool } from "@opencode-ai/plugin";
|
|
3523
3623
|
|
|
3524
3624
|
// src/indexer/index.ts
|
|
3525
|
-
import { existsSync as
|
|
3526
|
-
import * as
|
|
3625
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync2, renameSync, unlinkSync, mkdirSync as mkdirSync2, promises as fsPromises2 } from "fs";
|
|
3626
|
+
import * as path12 from "path";
|
|
3527
3627
|
import { performance as performance2 } from "perf_hooks";
|
|
3528
3628
|
|
|
3529
3629
|
// node_modules/eventemitter3/index.mjs
|
|
@@ -3548,7 +3648,7 @@ function pTimeout(promise, options) {
|
|
|
3548
3648
|
} = options;
|
|
3549
3649
|
let timer;
|
|
3550
3650
|
let abortHandler;
|
|
3551
|
-
const wrappedPromise = new Promise((
|
|
3651
|
+
const wrappedPromise = new Promise((resolve12, reject) => {
|
|
3552
3652
|
if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
|
|
3553
3653
|
throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
|
|
3554
3654
|
}
|
|
@@ -3562,7 +3662,7 @@ function pTimeout(promise, options) {
|
|
|
3562
3662
|
};
|
|
3563
3663
|
signal.addEventListener("abort", abortHandler, { once: true });
|
|
3564
3664
|
}
|
|
3565
|
-
promise.then(
|
|
3665
|
+
promise.then(resolve12, reject);
|
|
3566
3666
|
if (milliseconds === Number.POSITIVE_INFINITY) {
|
|
3567
3667
|
return;
|
|
3568
3668
|
}
|
|
@@ -3570,7 +3670,7 @@ function pTimeout(promise, options) {
|
|
|
3570
3670
|
timer = customTimers.setTimeout.call(void 0, () => {
|
|
3571
3671
|
if (fallback) {
|
|
3572
3672
|
try {
|
|
3573
|
-
|
|
3673
|
+
resolve12(fallback());
|
|
3574
3674
|
} catch (error) {
|
|
3575
3675
|
reject(error);
|
|
3576
3676
|
}
|
|
@@ -3580,7 +3680,7 @@ function pTimeout(promise, options) {
|
|
|
3580
3680
|
promise.cancel();
|
|
3581
3681
|
}
|
|
3582
3682
|
if (message === false) {
|
|
3583
|
-
|
|
3683
|
+
resolve12();
|
|
3584
3684
|
} else if (message instanceof Error) {
|
|
3585
3685
|
reject(message);
|
|
3586
3686
|
} else {
|
|
@@ -3982,7 +4082,7 @@ var PQueue = class extends import_index.default {
|
|
|
3982
4082
|
// Assign unique ID if not provided
|
|
3983
4083
|
id: options.id ?? (this.#idAssigner++).toString()
|
|
3984
4084
|
};
|
|
3985
|
-
return new Promise((
|
|
4085
|
+
return new Promise((resolve12, reject) => {
|
|
3986
4086
|
const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
|
|
3987
4087
|
let cleanupQueueAbortHandler = () => void 0;
|
|
3988
4088
|
const run = async () => {
|
|
@@ -4022,7 +4122,7 @@ var PQueue = class extends import_index.default {
|
|
|
4022
4122
|
})]);
|
|
4023
4123
|
}
|
|
4024
4124
|
const result = await operation;
|
|
4025
|
-
|
|
4125
|
+
resolve12(result);
|
|
4026
4126
|
this.emit("completed", result);
|
|
4027
4127
|
} catch (error) {
|
|
4028
4128
|
reject(error);
|
|
@@ -4210,13 +4310,13 @@ var PQueue = class extends import_index.default {
|
|
|
4210
4310
|
});
|
|
4211
4311
|
}
|
|
4212
4312
|
async #onEvent(event, filter) {
|
|
4213
|
-
return new Promise((
|
|
4313
|
+
return new Promise((resolve12) => {
|
|
4214
4314
|
const listener = () => {
|
|
4215
4315
|
if (filter && !filter()) {
|
|
4216
4316
|
return;
|
|
4217
4317
|
}
|
|
4218
4318
|
this.off(event, listener);
|
|
4219
|
-
|
|
4319
|
+
resolve12();
|
|
4220
4320
|
};
|
|
4221
4321
|
this.on(event, listener);
|
|
4222
4322
|
});
|
|
@@ -4502,7 +4602,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
|
|
|
4502
4602
|
const finalDelay = Math.min(delayTime, remainingTime);
|
|
4503
4603
|
options.signal?.throwIfAborted();
|
|
4504
4604
|
if (finalDelay > 0) {
|
|
4505
|
-
await new Promise((
|
|
4605
|
+
await new Promise((resolve12, reject) => {
|
|
4506
4606
|
const onAbort = () => {
|
|
4507
4607
|
clearTimeout(timeoutToken);
|
|
4508
4608
|
options.signal?.removeEventListener("abort", onAbort);
|
|
@@ -4510,7 +4610,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
|
|
|
4510
4610
|
};
|
|
4511
4611
|
const timeoutToken = setTimeout(() => {
|
|
4512
4612
|
options.signal?.removeEventListener("abort", onAbort);
|
|
4513
|
-
|
|
4613
|
+
resolve12();
|
|
4514
4614
|
}, finalDelay);
|
|
4515
4615
|
if (options.unref) {
|
|
4516
4616
|
timeoutToken.unref?.();
|
|
@@ -4571,17 +4671,17 @@ async function pRetry(input, options = {}) {
|
|
|
4571
4671
|
}
|
|
4572
4672
|
|
|
4573
4673
|
// src/embeddings/detector.ts
|
|
4574
|
-
import { existsSync as
|
|
4575
|
-
import * as
|
|
4674
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
|
|
4675
|
+
import * as path10 from "path";
|
|
4576
4676
|
import * as os3 from "os";
|
|
4577
4677
|
function getOpenCodeAuthPath() {
|
|
4578
|
-
return
|
|
4678
|
+
return path10.join(os3.homedir(), ".local", "share", "opencode", "auth.json");
|
|
4579
4679
|
}
|
|
4580
4680
|
function loadOpenCodeAuth() {
|
|
4581
4681
|
const authPath = getOpenCodeAuthPath();
|
|
4582
4682
|
try {
|
|
4583
|
-
if (
|
|
4584
|
-
return JSON.parse(
|
|
4683
|
+
if (existsSync6(authPath)) {
|
|
4684
|
+
return JSON.parse(readFileSync5(authPath, "utf-8"));
|
|
4585
4685
|
}
|
|
4586
4686
|
} catch {
|
|
4587
4687
|
}
|
|
@@ -4648,7 +4748,8 @@ function getGitHubCopilotCredentials() {
|
|
|
4648
4748
|
if (!copilotAuth || copilotAuth.type !== "oauth") {
|
|
4649
4749
|
return null;
|
|
4650
4750
|
}
|
|
4651
|
-
const
|
|
4751
|
+
const auth = copilotAuth;
|
|
4752
|
+
const baseUrl = auth.enterpriseUrl ? `https://copilot-api.${auth.enterpriseUrl.replace(/^https?:\/\//, "").replace(/\/$/, "")}` : "https://models.github.ai";
|
|
4652
4753
|
return {
|
|
4653
4754
|
provider: "github-copilot",
|
|
4654
4755
|
baseUrl,
|
|
@@ -4744,42 +4845,12 @@ function createCustomProviderInfo(config) {
|
|
|
4744
4845
|
};
|
|
4745
4846
|
}
|
|
4746
4847
|
|
|
4747
|
-
// src/embeddings/provider.ts
|
|
4748
|
-
var
|
|
4749
|
-
constructor(message) {
|
|
4750
|
-
super(message);
|
|
4751
|
-
this.name = "CustomProviderNonRetryableError";
|
|
4752
|
-
}
|
|
4753
|
-
};
|
|
4754
|
-
function createEmbeddingProvider(configuredProviderInfo) {
|
|
4755
|
-
switch (configuredProviderInfo.provider) {
|
|
4756
|
-
case "github-copilot":
|
|
4757
|
-
return new GitHubCopilotEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
4758
|
-
case "openai":
|
|
4759
|
-
return new OpenAIEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
4760
|
-
case "google":
|
|
4761
|
-
return new GoogleEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
4762
|
-
case "ollama":
|
|
4763
|
-
return new OllamaEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
4764
|
-
case "custom":
|
|
4765
|
-
return new CustomEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
4766
|
-
default: {
|
|
4767
|
-
const _exhaustive = configuredProviderInfo;
|
|
4768
|
-
throw new Error(`Unsupported embedding provider: ${_exhaustive.provider}`);
|
|
4769
|
-
}
|
|
4770
|
-
}
|
|
4771
|
-
}
|
|
4772
|
-
var GitHubCopilotEmbeddingProvider = class {
|
|
4848
|
+
// src/embeddings/provider-types.ts
|
|
4849
|
+
var BaseEmbeddingProvider = class {
|
|
4773
4850
|
constructor(credentials, modelInfo) {
|
|
4774
4851
|
this.credentials = credentials;
|
|
4775
4852
|
this.modelInfo = modelInfo;
|
|
4776
4853
|
}
|
|
4777
|
-
getToken() {
|
|
4778
|
-
if (!this.credentials.refreshToken) {
|
|
4779
|
-
throw new Error("No OAuth token available for GitHub");
|
|
4780
|
-
}
|
|
4781
|
-
return this.credentials.refreshToken;
|
|
4782
|
-
}
|
|
4783
4854
|
async embedQuery(query) {
|
|
4784
4855
|
const result = await this.embedBatch([query]);
|
|
4785
4856
|
return {
|
|
@@ -4794,69 +4865,204 @@ var GitHubCopilotEmbeddingProvider = class {
|
|
|
4794
4865
|
tokensUsed: result.totalTokensUsed
|
|
4795
4866
|
};
|
|
4796
4867
|
}
|
|
4797
|
-
async embedBatch(texts) {
|
|
4798
|
-
const token = this.getToken();
|
|
4799
|
-
const response = await fetch(`${this.credentials.baseUrl}/inference/embeddings`, {
|
|
4800
|
-
method: "POST",
|
|
4801
|
-
headers: {
|
|
4802
|
-
Authorization: `Bearer ${token}`,
|
|
4803
|
-
"Content-Type": "application/json",
|
|
4804
|
-
Accept: "application/vnd.github+json",
|
|
4805
|
-
"X-GitHub-Api-Version": "2022-11-28"
|
|
4806
|
-
},
|
|
4807
|
-
body: JSON.stringify({
|
|
4808
|
-
model: `openai/${this.modelInfo.model}`,
|
|
4809
|
-
input: texts
|
|
4810
|
-
})
|
|
4811
|
-
});
|
|
4812
|
-
if (!response.ok) {
|
|
4813
|
-
const error = await response.text();
|
|
4814
|
-
throw new Error(`GitHub Copilot embedding API error: ${response.status} - ${error}`);
|
|
4815
|
-
}
|
|
4816
|
-
const data = await response.json();
|
|
4817
|
-
return {
|
|
4818
|
-
embeddings: data.data.map((d) => d.embedding),
|
|
4819
|
-
totalTokensUsed: data.usage.total_tokens
|
|
4820
|
-
};
|
|
4821
|
-
}
|
|
4822
4868
|
getModelInfo() {
|
|
4823
4869
|
return this.modelInfo;
|
|
4824
4870
|
}
|
|
4825
4871
|
};
|
|
4826
|
-
var
|
|
4872
|
+
var CustomProviderNonRetryableError = class extends Error {
|
|
4873
|
+
constructor(message) {
|
|
4874
|
+
super(message);
|
|
4875
|
+
this.name = "CustomProviderNonRetryableError";
|
|
4876
|
+
}
|
|
4877
|
+
};
|
|
4878
|
+
|
|
4879
|
+
// src/utils/url-validation.ts
|
|
4880
|
+
var BLOCKED_METADATA_IPS = [
|
|
4881
|
+
/^169\.254\.169\.254$/,
|
|
4882
|
+
// AWS/Azure/GCP metadata
|
|
4883
|
+
/^169\.254\.170\.2$/,
|
|
4884
|
+
// AWS ECS task metadata
|
|
4885
|
+
/^fd00:ec2::254$/
|
|
4886
|
+
// AWS IMDSv2 IPv6
|
|
4887
|
+
];
|
|
4888
|
+
var BLOCKED_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
4889
|
+
"metadata.google.internal",
|
|
4890
|
+
"metadata.google",
|
|
4891
|
+
"metadata.goog",
|
|
4892
|
+
"kubernetes.default.svc"
|
|
4893
|
+
]);
|
|
4894
|
+
function validateExternalUrl(urlString) {
|
|
4895
|
+
let parsed;
|
|
4896
|
+
try {
|
|
4897
|
+
parsed = new URL(urlString);
|
|
4898
|
+
} catch {
|
|
4899
|
+
return { valid: false, reason: `Invalid URL: ${sanitizeUrlForError(urlString)}` };
|
|
4900
|
+
}
|
|
4901
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
4902
|
+
return { valid: false, reason: `Blocked protocol: ${parsed.protocol}` };
|
|
4903
|
+
}
|
|
4904
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
4905
|
+
if (BLOCKED_HOSTNAMES.has(hostname)) {
|
|
4906
|
+
return { valid: false, reason: `Blocked: cloud metadata service (${hostname})` };
|
|
4907
|
+
}
|
|
4908
|
+
for (const pattern of BLOCKED_METADATA_IPS) {
|
|
4909
|
+
if (pattern.test(hostname)) {
|
|
4910
|
+
return { valid: false, reason: `Blocked: cloud metadata IP (${hostname})` };
|
|
4911
|
+
}
|
|
4912
|
+
}
|
|
4913
|
+
if (/^169\.254\./.test(hostname)) {
|
|
4914
|
+
return { valid: false, reason: `Blocked: link-local address (${hostname})` };
|
|
4915
|
+
}
|
|
4916
|
+
return { valid: true };
|
|
4917
|
+
}
|
|
4918
|
+
function sanitizeUrlForError(url) {
|
|
4919
|
+
try {
|
|
4920
|
+
const parsed = new URL(url);
|
|
4921
|
+
parsed.username = "";
|
|
4922
|
+
parsed.password = "";
|
|
4923
|
+
return parsed.toString();
|
|
4924
|
+
} catch {
|
|
4925
|
+
const maxLen = 80;
|
|
4926
|
+
if (url.length > maxLen) {
|
|
4927
|
+
return url.slice(0, maxLen) + "...";
|
|
4928
|
+
}
|
|
4929
|
+
return url;
|
|
4930
|
+
}
|
|
4931
|
+
}
|
|
4932
|
+
|
|
4933
|
+
// src/embeddings/providers/custom.ts
|
|
4934
|
+
var CustomEmbeddingProvider = class extends BaseEmbeddingProvider {
|
|
4827
4935
|
constructor(credentials, modelInfo) {
|
|
4828
|
-
|
|
4829
|
-
this.modelInfo = modelInfo;
|
|
4936
|
+
super(credentials, modelInfo);
|
|
4830
4937
|
}
|
|
4831
|
-
|
|
4832
|
-
const
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4938
|
+
splitIntoRequestBatches(texts) {
|
|
4939
|
+
const maxBatchSize = this.modelInfo.maxBatchSize;
|
|
4940
|
+
if (!maxBatchSize || texts.length <= maxBatchSize) {
|
|
4941
|
+
return [texts];
|
|
4942
|
+
}
|
|
4943
|
+
const batches = [];
|
|
4944
|
+
for (let i = 0; i < texts.length; i += maxBatchSize) {
|
|
4945
|
+
batches.push(texts.slice(i, i + maxBatchSize));
|
|
4946
|
+
}
|
|
4947
|
+
return batches;
|
|
4948
|
+
}
|
|
4949
|
+
async embedRequest(texts) {
|
|
4950
|
+
if (texts.length === 0) {
|
|
4951
|
+
return {
|
|
4952
|
+
embeddings: [],
|
|
4953
|
+
totalTokensUsed: 0
|
|
4954
|
+
};
|
|
4955
|
+
}
|
|
4956
|
+
const headers = {
|
|
4957
|
+
"Content-Type": "application/json"
|
|
4836
4958
|
};
|
|
4959
|
+
if (this.credentials.apiKey) {
|
|
4960
|
+
headers.Authorization = `Bearer ${this.credentials.apiKey}`;
|
|
4961
|
+
}
|
|
4962
|
+
const baseUrl = this.credentials.baseUrl ?? "";
|
|
4963
|
+
const fullUrl = `${baseUrl}/embeddings`;
|
|
4964
|
+
const urlCheck = validateExternalUrl(fullUrl);
|
|
4965
|
+
if (!urlCheck.valid) {
|
|
4966
|
+
throw new CustomProviderNonRetryableError(
|
|
4967
|
+
`Custom embedding provider URL blocked (SSRF protection): ${urlCheck.reason}`
|
|
4968
|
+
);
|
|
4969
|
+
}
|
|
4970
|
+
const timeoutMs = this.modelInfo.timeoutMs;
|
|
4971
|
+
const controller = new AbortController();
|
|
4972
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
4973
|
+
let response;
|
|
4974
|
+
try {
|
|
4975
|
+
response = await fetch(fullUrl, {
|
|
4976
|
+
method: "POST",
|
|
4977
|
+
headers,
|
|
4978
|
+
body: JSON.stringify({
|
|
4979
|
+
model: this.modelInfo.model,
|
|
4980
|
+
input: texts
|
|
4981
|
+
}),
|
|
4982
|
+
signal: controller.signal
|
|
4983
|
+
});
|
|
4984
|
+
} catch (error) {
|
|
4985
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
4986
|
+
throw new Error(`Custom embedding API request timed out after ${timeoutMs}ms for ${sanitizeUrlForError(fullUrl)}`);
|
|
4987
|
+
}
|
|
4988
|
+
throw error;
|
|
4989
|
+
} finally {
|
|
4990
|
+
clearTimeout(timeout);
|
|
4991
|
+
}
|
|
4992
|
+
if (!response.ok) {
|
|
4993
|
+
const errorText = (await response.text()).slice(0, 500);
|
|
4994
|
+
if (response.status >= 400 && response.status < 500 && response.status !== 429) {
|
|
4995
|
+
throw new CustomProviderNonRetryableError(`Custom embedding API error (non-retryable): ${response.status} - ${errorText}`);
|
|
4996
|
+
}
|
|
4997
|
+
throw new Error(`Custom embedding API error: ${response.status} - ${errorText}`);
|
|
4998
|
+
}
|
|
4999
|
+
const data = await response.json();
|
|
5000
|
+
if (data.data && Array.isArray(data.data)) {
|
|
5001
|
+
if (data.data.length > 0) {
|
|
5002
|
+
const actualDims = data.data[0].embedding.length;
|
|
5003
|
+
if (actualDims !== this.modelInfo.dimensions) {
|
|
5004
|
+
throw new Error(
|
|
5005
|
+
`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.`
|
|
5006
|
+
);
|
|
5007
|
+
}
|
|
5008
|
+
}
|
|
5009
|
+
if (data.data.length !== texts.length) {
|
|
5010
|
+
throw new Error(
|
|
5011
|
+
`Embedding count mismatch: sent ${texts.length} texts but received ${data.data.length} embeddings. The custom embedding server may not support batch input.`
|
|
5012
|
+
);
|
|
5013
|
+
}
|
|
5014
|
+
return {
|
|
5015
|
+
embeddings: data.data.map((d) => d.embedding),
|
|
5016
|
+
totalTokensUsed: data.usage?.total_tokens ?? texts.reduce((sum, t) => sum + Math.ceil(t.length / 4), 0)
|
|
5017
|
+
};
|
|
5018
|
+
}
|
|
5019
|
+
throw new Error("Custom embedding API returned unexpected response format. Expected OpenAI-compatible format with data[].embedding.");
|
|
4837
5020
|
}
|
|
4838
|
-
async
|
|
4839
|
-
const
|
|
5021
|
+
async embedBatch(texts) {
|
|
5022
|
+
const requestBatches = this.splitIntoRequestBatches(texts);
|
|
5023
|
+
const embeddings = [];
|
|
5024
|
+
let totalTokensUsed = 0;
|
|
5025
|
+
for (const batch of requestBatches) {
|
|
5026
|
+
const result = await this.embedRequest(batch);
|
|
5027
|
+
embeddings.push(...result.embeddings);
|
|
5028
|
+
totalTokensUsed += result.totalTokensUsed;
|
|
5029
|
+
}
|
|
4840
5030
|
return {
|
|
4841
|
-
|
|
4842
|
-
|
|
5031
|
+
embeddings,
|
|
5032
|
+
totalTokensUsed
|
|
4843
5033
|
};
|
|
4844
5034
|
}
|
|
5035
|
+
};
|
|
5036
|
+
|
|
5037
|
+
// src/embeddings/providers/github-copilot.ts
|
|
5038
|
+
var GitHubCopilotEmbeddingProvider = class extends BaseEmbeddingProvider {
|
|
5039
|
+
constructor(credentials, modelInfo) {
|
|
5040
|
+
super(credentials, modelInfo);
|
|
5041
|
+
}
|
|
5042
|
+
getToken() {
|
|
5043
|
+
if (!this.credentials.refreshToken) {
|
|
5044
|
+
throw new Error("No OAuth token available for GitHub");
|
|
5045
|
+
}
|
|
5046
|
+
return this.credentials.refreshToken;
|
|
5047
|
+
}
|
|
4845
5048
|
async embedBatch(texts) {
|
|
4846
|
-
const
|
|
5049
|
+
const token = this.getToken();
|
|
5050
|
+
const response = await fetch(`${this.credentials.baseUrl}/inference/embeddings`, {
|
|
4847
5051
|
method: "POST",
|
|
4848
5052
|
headers: {
|
|
4849
|
-
Authorization: `Bearer ${
|
|
4850
|
-
"Content-Type": "application/json"
|
|
5053
|
+
Authorization: `Bearer ${token}`,
|
|
5054
|
+
"Content-Type": "application/json",
|
|
5055
|
+
Accept: "application/vnd.github+json",
|
|
5056
|
+
"X-GitHub-Api-Version": "2022-11-28"
|
|
4851
5057
|
},
|
|
4852
5058
|
body: JSON.stringify({
|
|
4853
|
-
model: this.modelInfo.model
|
|
5059
|
+
model: `openai/${this.modelInfo.model}`,
|
|
4854
5060
|
input: texts
|
|
4855
5061
|
})
|
|
4856
5062
|
});
|
|
4857
5063
|
if (!response.ok) {
|
|
4858
|
-
const error = await response.text();
|
|
4859
|
-
throw new Error(`
|
|
5064
|
+
const error = (await response.text()).slice(0, 500);
|
|
5065
|
+
throw new Error(`GitHub Copilot embedding API error: ${response.status} - ${error}`);
|
|
4860
5066
|
}
|
|
4861
5067
|
const data = await response.json();
|
|
4862
5068
|
return {
|
|
@@ -4864,16 +5070,14 @@ var OpenAIEmbeddingProvider = class {
|
|
|
4864
5070
|
totalTokensUsed: data.usage.total_tokens
|
|
4865
5071
|
};
|
|
4866
5072
|
}
|
|
4867
|
-
getModelInfo() {
|
|
4868
|
-
return this.modelInfo;
|
|
4869
|
-
}
|
|
4870
5073
|
};
|
|
4871
|
-
|
|
5074
|
+
|
|
5075
|
+
// src/embeddings/providers/google.ts
|
|
5076
|
+
var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider extends BaseEmbeddingProvider {
|
|
5077
|
+
static BATCH_SIZE = 20;
|
|
4872
5078
|
constructor(credentials, modelInfo) {
|
|
4873
|
-
|
|
4874
|
-
this.modelInfo = modelInfo;
|
|
5079
|
+
super(credentials, modelInfo);
|
|
4875
5080
|
}
|
|
4876
|
-
static BATCH_SIZE = 20;
|
|
4877
5081
|
async embedQuery(query) {
|
|
4878
5082
|
const taskType = this.modelInfo.taskAble ? "CODE_RETRIEVAL_QUERY" : void 0;
|
|
4879
5083
|
const result = await this.embedWithTaskType([query], taskType);
|
|
@@ -4894,12 +5098,6 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider {
|
|
|
4894
5098
|
const taskType = this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
4895
5099
|
return this.embedWithTaskType(texts, taskType);
|
|
4896
5100
|
}
|
|
4897
|
-
/**
|
|
4898
|
-
* Embeds texts using the Google embedContent API.
|
|
4899
|
-
* Sends multiple texts as parts in batched requests (up to BATCH_SIZE per call).
|
|
4900
|
-
* When taskType is provided (gemini-embedding-001), includes it in the request
|
|
4901
|
-
* for task-specific embedding optimization.
|
|
4902
|
-
*/
|
|
4903
5101
|
async embedWithTaskType(texts, taskType) {
|
|
4904
5102
|
const batches = [];
|
|
4905
5103
|
for (let i = 0; i < texts.length; i += _GoogleEmbeddingProvider.BATCH_SIZE) {
|
|
@@ -4916,17 +5114,18 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider {
|
|
|
4916
5114
|
outputDimensionality: this.modelInfo.dimensions
|
|
4917
5115
|
}));
|
|
4918
5116
|
const response = await fetch(
|
|
4919
|
-
`${this.credentials.baseUrl}/models/${this.modelInfo.model}:batchEmbedContents
|
|
5117
|
+
`${this.credentials.baseUrl}/models/${this.modelInfo.model}:batchEmbedContents`,
|
|
4920
5118
|
{
|
|
4921
5119
|
method: "POST",
|
|
4922
5120
|
headers: {
|
|
4923
|
-
"Content-Type": "application/json"
|
|
5121
|
+
"Content-Type": "application/json",
|
|
5122
|
+
...this.credentials.apiKey && { "x-goog-api-key": this.credentials.apiKey }
|
|
4924
5123
|
},
|
|
4925
5124
|
body: JSON.stringify({ requests })
|
|
4926
5125
|
}
|
|
4927
5126
|
);
|
|
4928
5127
|
if (!response.ok) {
|
|
4929
|
-
const error = await response.text();
|
|
5128
|
+
const error = (await response.text()).slice(0, 500);
|
|
4930
5129
|
throw new Error(`Google embedding API error: ${response.status} - ${error}`);
|
|
4931
5130
|
}
|
|
4932
5131
|
const data = await response.json();
|
|
@@ -4941,29 +5140,13 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider {
|
|
|
4941
5140
|
totalTokensUsed: batchResults.reduce((sum, r) => sum + r.tokensUsed, 0)
|
|
4942
5141
|
};
|
|
4943
5142
|
}
|
|
4944
|
-
getModelInfo() {
|
|
4945
|
-
return this.modelInfo;
|
|
4946
|
-
}
|
|
4947
5143
|
};
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
this.modelInfo = modelInfo;
|
|
4952
|
-
}
|
|
5144
|
+
|
|
5145
|
+
// src/embeddings/providers/ollama.ts
|
|
5146
|
+
var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddingProvider {
|
|
4953
5147
|
static MIN_TRUNCATION_CHARS = 512;
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
return {
|
|
4957
|
-
embedding: result.embeddings[0],
|
|
4958
|
-
tokensUsed: result.totalTokensUsed
|
|
4959
|
-
};
|
|
4960
|
-
}
|
|
4961
|
-
async embedDocument(document) {
|
|
4962
|
-
const result = await this.embedBatch([document]);
|
|
4963
|
-
return {
|
|
4964
|
-
embedding: result.embeddings[0],
|
|
4965
|
-
tokensUsed: result.totalTokensUsed
|
|
4966
|
-
};
|
|
5148
|
+
constructor(credentials, modelInfo) {
|
|
5149
|
+
super(credentials, modelInfo);
|
|
4967
5150
|
}
|
|
4968
5151
|
estimateTokens(text) {
|
|
4969
5152
|
return Math.ceil(text.length / 4);
|
|
@@ -5027,166 +5210,97 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider {
|
|
|
5027
5210
|
return await this.embedSingle(truncated);
|
|
5028
5211
|
} catch (retryError) {
|
|
5029
5212
|
if (!this.isContextLengthError(retryError)) {
|
|
5030
|
-
throw retryError;
|
|
5031
|
-
}
|
|
5032
|
-
lastError = retryError;
|
|
5033
|
-
}
|
|
5034
|
-
}
|
|
5035
|
-
throw lastError;
|
|
5036
|
-
}
|
|
5037
|
-
}
|
|
5038
|
-
async embedSingle(text) {
|
|
5039
|
-
const response = await fetch(`${this.credentials.baseUrl}/api/embeddings`, {
|
|
5040
|
-
method: "POST",
|
|
5041
|
-
headers: {
|
|
5042
|
-
"Content-Type": "application/json"
|
|
5043
|
-
},
|
|
5044
|
-
body: JSON.stringify({
|
|
5045
|
-
model: this.modelInfo.model,
|
|
5046
|
-
prompt: text,
|
|
5047
|
-
truncate: false
|
|
5048
|
-
})
|
|
5049
|
-
});
|
|
5050
|
-
if (!response.ok) {
|
|
5051
|
-
const error = await response.text();
|
|
5052
|
-
throw new Error(`Ollama embedding API error: ${response.status} - ${error}`);
|
|
5053
|
-
}
|
|
5054
|
-
const data = await response.json();
|
|
5055
|
-
return {
|
|
5056
|
-
embedding: data.embedding,
|
|
5057
|
-
tokensUsed: this.estimateTokens(text)
|
|
5058
|
-
};
|
|
5059
|
-
}
|
|
5060
|
-
async embedBatch(texts) {
|
|
5061
|
-
const results = [];
|
|
5062
|
-
for (const text of texts) {
|
|
5063
|
-
results.push(await this.embedSingleWithFallback(text));
|
|
5064
|
-
}
|
|
5065
|
-
return {
|
|
5066
|
-
embeddings: results.map((r) => r.embedding),
|
|
5067
|
-
totalTokensUsed: results.reduce((sum, r) => sum + r.tokensUsed, 0)
|
|
5068
|
-
};
|
|
5069
|
-
}
|
|
5070
|
-
getModelInfo() {
|
|
5071
|
-
return this.modelInfo;
|
|
5072
|
-
}
|
|
5073
|
-
};
|
|
5074
|
-
var CustomEmbeddingProvider = class {
|
|
5075
|
-
constructor(credentials, modelInfo) {
|
|
5076
|
-
this.credentials = credentials;
|
|
5077
|
-
this.modelInfo = modelInfo;
|
|
5078
|
-
}
|
|
5079
|
-
splitIntoRequestBatches(texts) {
|
|
5080
|
-
const maxBatchSize = this.modelInfo.maxBatchSize;
|
|
5081
|
-
if (!maxBatchSize || texts.length <= maxBatchSize) {
|
|
5082
|
-
return [texts];
|
|
5083
|
-
}
|
|
5084
|
-
const batches = [];
|
|
5085
|
-
for (let i = 0; i < texts.length; i += maxBatchSize) {
|
|
5086
|
-
batches.push(texts.slice(i, i + maxBatchSize));
|
|
5087
|
-
}
|
|
5088
|
-
return batches;
|
|
5089
|
-
}
|
|
5090
|
-
async embedRequest(texts) {
|
|
5091
|
-
if (texts.length === 0) {
|
|
5092
|
-
return {
|
|
5093
|
-
embeddings: [],
|
|
5094
|
-
totalTokensUsed: 0
|
|
5095
|
-
};
|
|
5096
|
-
}
|
|
5097
|
-
const headers = {
|
|
5098
|
-
"Content-Type": "application/json"
|
|
5099
|
-
};
|
|
5100
|
-
if (this.credentials.apiKey) {
|
|
5101
|
-
headers["Authorization"] = `Bearer ${this.credentials.apiKey}`;
|
|
5102
|
-
}
|
|
5103
|
-
const baseUrl = this.credentials.baseUrl ?? "";
|
|
5104
|
-
const timeoutMs = this.modelInfo.timeoutMs;
|
|
5105
|
-
const controller = new AbortController();
|
|
5106
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
5107
|
-
let response;
|
|
5108
|
-
try {
|
|
5109
|
-
response = await fetch(`${baseUrl}/embeddings`, {
|
|
5110
|
-
method: "POST",
|
|
5111
|
-
headers,
|
|
5112
|
-
body: JSON.stringify({
|
|
5113
|
-
model: this.modelInfo.model,
|
|
5114
|
-
input: texts
|
|
5115
|
-
}),
|
|
5116
|
-
signal: controller.signal
|
|
5117
|
-
});
|
|
5118
|
-
} catch (error) {
|
|
5119
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
5120
|
-
throw new Error(`Custom embedding API request timed out after ${timeoutMs}ms for ${baseUrl}/embeddings`);
|
|
5121
|
-
}
|
|
5122
|
-
throw error;
|
|
5123
|
-
} finally {
|
|
5124
|
-
clearTimeout(timeout);
|
|
5125
|
-
}
|
|
5126
|
-
if (!response.ok) {
|
|
5127
|
-
const errorText = await response.text();
|
|
5128
|
-
if (response.status >= 400 && response.status < 500 && response.status !== 429) {
|
|
5129
|
-
throw new CustomProviderNonRetryableError(`Custom embedding API error (non-retryable): ${response.status} - ${errorText}`);
|
|
5130
|
-
}
|
|
5131
|
-
throw new Error(`Custom embedding API error: ${response.status} - ${errorText}`);
|
|
5132
|
-
}
|
|
5133
|
-
const data = await response.json();
|
|
5134
|
-
if (data.data && Array.isArray(data.data)) {
|
|
5135
|
-
if (data.data.length > 0) {
|
|
5136
|
-
const actualDims = data.data[0].embedding.length;
|
|
5137
|
-
if (actualDims !== this.modelInfo.dimensions) {
|
|
5138
|
-
throw new Error(
|
|
5139
|
-
`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.`
|
|
5140
|
-
);
|
|
5213
|
+
throw retryError;
|
|
5214
|
+
}
|
|
5215
|
+
lastError = retryError;
|
|
5141
5216
|
}
|
|
5142
5217
|
}
|
|
5143
|
-
|
|
5144
|
-
throw new Error(
|
|
5145
|
-
`Embedding count mismatch: sent ${texts.length} texts but received ${data.data.length} embeddings. The custom embedding server may not support batch input.`
|
|
5146
|
-
);
|
|
5147
|
-
}
|
|
5148
|
-
return {
|
|
5149
|
-
embeddings: data.data.map((d) => d.embedding),
|
|
5150
|
-
// Rough estimate: ~4 chars per token. Used as fallback when the server
|
|
5151
|
-
// doesn't return usage.total_tokens (e.g. llama.cpp, some vLLM configs).
|
|
5152
|
-
totalTokensUsed: data.usage?.total_tokens ?? texts.reduce((sum, t) => sum + Math.ceil(t.length / 4), 0)
|
|
5153
|
-
};
|
|
5218
|
+
throw lastError;
|
|
5154
5219
|
}
|
|
5155
|
-
throw new Error("Custom embedding API returned unexpected response format. Expected OpenAI-compatible format with data[].embedding.");
|
|
5156
5220
|
}
|
|
5157
|
-
async
|
|
5158
|
-
const
|
|
5221
|
+
async embedSingle(text) {
|
|
5222
|
+
const response = await fetch(`${this.credentials.baseUrl}/api/embeddings`, {
|
|
5223
|
+
method: "POST",
|
|
5224
|
+
headers: {
|
|
5225
|
+
"Content-Type": "application/json"
|
|
5226
|
+
},
|
|
5227
|
+
body: JSON.stringify({
|
|
5228
|
+
model: this.modelInfo.model,
|
|
5229
|
+
prompt: text,
|
|
5230
|
+
truncate: false
|
|
5231
|
+
})
|
|
5232
|
+
});
|
|
5233
|
+
if (!response.ok) {
|
|
5234
|
+
const error = (await response.text()).slice(0, 500);
|
|
5235
|
+
throw new Error(`Ollama embedding API error: ${response.status} - ${error}`);
|
|
5236
|
+
}
|
|
5237
|
+
const data = await response.json();
|
|
5159
5238
|
return {
|
|
5160
|
-
embedding:
|
|
5161
|
-
tokensUsed:
|
|
5239
|
+
embedding: data.embedding,
|
|
5240
|
+
tokensUsed: this.estimateTokens(text)
|
|
5162
5241
|
};
|
|
5163
5242
|
}
|
|
5164
|
-
async
|
|
5165
|
-
const
|
|
5243
|
+
async embedBatch(texts) {
|
|
5244
|
+
const results = [];
|
|
5245
|
+
for (const text of texts) {
|
|
5246
|
+
results.push(await this.embedSingleWithFallback(text));
|
|
5247
|
+
}
|
|
5166
5248
|
return {
|
|
5167
|
-
|
|
5168
|
-
|
|
5249
|
+
embeddings: results.map((r) => r.embedding),
|
|
5250
|
+
totalTokensUsed: results.reduce((sum, r) => sum + r.tokensUsed, 0)
|
|
5169
5251
|
};
|
|
5170
5252
|
}
|
|
5253
|
+
};
|
|
5254
|
+
|
|
5255
|
+
// src/embeddings/providers/openai.ts
|
|
5256
|
+
var OpenAIEmbeddingProvider = class extends BaseEmbeddingProvider {
|
|
5257
|
+
constructor(credentials, modelInfo) {
|
|
5258
|
+
super(credentials, modelInfo);
|
|
5259
|
+
}
|
|
5171
5260
|
async embedBatch(texts) {
|
|
5172
|
-
const
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5261
|
+
const response = await fetch(`${this.credentials.baseUrl}/embeddings`, {
|
|
5262
|
+
method: "POST",
|
|
5263
|
+
headers: {
|
|
5264
|
+
Authorization: `Bearer ${this.credentials.apiKey}`,
|
|
5265
|
+
"Content-Type": "application/json"
|
|
5266
|
+
},
|
|
5267
|
+
body: JSON.stringify({
|
|
5268
|
+
model: this.modelInfo.model,
|
|
5269
|
+
input: texts
|
|
5270
|
+
})
|
|
5271
|
+
});
|
|
5272
|
+
if (!response.ok) {
|
|
5273
|
+
const error = (await response.text()).slice(0, 500);
|
|
5274
|
+
throw new Error(`OpenAI embedding API error: ${response.status} - ${error}`);
|
|
5179
5275
|
}
|
|
5276
|
+
const data = await response.json();
|
|
5180
5277
|
return {
|
|
5181
|
-
embeddings,
|
|
5182
|
-
totalTokensUsed
|
|
5278
|
+
embeddings: data.data.map((d) => d.embedding),
|
|
5279
|
+
totalTokensUsed: data.usage.total_tokens
|
|
5183
5280
|
};
|
|
5184
5281
|
}
|
|
5185
|
-
getModelInfo() {
|
|
5186
|
-
return this.modelInfo;
|
|
5187
|
-
}
|
|
5188
5282
|
};
|
|
5189
5283
|
|
|
5284
|
+
// src/embeddings/provider.ts
|
|
5285
|
+
function createEmbeddingProvider(configuredProviderInfo) {
|
|
5286
|
+
switch (configuredProviderInfo.provider) {
|
|
5287
|
+
case "github-copilot":
|
|
5288
|
+
return new GitHubCopilotEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
5289
|
+
case "openai":
|
|
5290
|
+
return new OpenAIEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
5291
|
+
case "google":
|
|
5292
|
+
return new GoogleEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
5293
|
+
case "ollama":
|
|
5294
|
+
return new OllamaEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
5295
|
+
case "custom":
|
|
5296
|
+
return new CustomEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
5297
|
+
default: {
|
|
5298
|
+
const _exhaustive = configuredProviderInfo;
|
|
5299
|
+
throw new Error(`Unsupported embedding provider: ${_exhaustive.provider}`);
|
|
5300
|
+
}
|
|
5301
|
+
}
|
|
5302
|
+
}
|
|
5303
|
+
|
|
5190
5304
|
// src/rerank/index.ts
|
|
5191
5305
|
function createReranker(config) {
|
|
5192
5306
|
if (!config.enabled) {
|
|
@@ -5222,7 +5336,10 @@ var SiliconFlowReranker = class {
|
|
|
5222
5336
|
if (this.config.apiKey) {
|
|
5223
5337
|
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
5224
5338
|
}
|
|
5225
|
-
const baseUrl = this.config.baseUrl
|
|
5339
|
+
const baseUrl = this.config.baseUrl;
|
|
5340
|
+
if (!baseUrl) {
|
|
5341
|
+
throw new Error("Reranker baseUrl is required. Configure reranker.baseUrl in your codebase-index.json.");
|
|
5342
|
+
}
|
|
5226
5343
|
const timeoutMs = this.config.timeoutMs ?? 3e4;
|
|
5227
5344
|
const controller = new AbortController();
|
|
5228
5345
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -5296,20 +5413,21 @@ function createCostEstimate(files, provider) {
|
|
|
5296
5413
|
}
|
|
5297
5414
|
function formatCostEstimate(estimate) {
|
|
5298
5415
|
const sizeFormatted = formatBytes(estimate.totalSizeBytes);
|
|
5416
|
+
const filesFormatted = `${estimate.filesCount.toLocaleString()} files`;
|
|
5299
5417
|
const costFormatted = estimate.isFree ? "Free" : `~$${estimate.estimatedCost.toFixed(4)}`;
|
|
5300
5418
|
return `
|
|
5301
5419
|
\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
|
|
5302
5420
|
\u2502 \u{1F4CA} Indexing Estimate \u2502
|
|
5303
5421
|
\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
|
|
5304
5422
|
\u2502 \u2502
|
|
5305
|
-
\u2502 Files to index: ${
|
|
5306
|
-
\u2502 Total size: ${
|
|
5307
|
-
\u2502 Estimated chunks: ${
|
|
5308
|
-
\u2502 Estimated tokens: ${
|
|
5423
|
+
\u2502 Files to index: ${filesFormatted.padEnd(40)}\u2502
|
|
5424
|
+
\u2502 Total size: ${sizeFormatted.padEnd(40)}\u2502
|
|
5425
|
+
\u2502 Estimated chunks: ${("~" + estimate.estimatedChunks.toLocaleString() + " chunks").padEnd(40)}\u2502
|
|
5426
|
+
\u2502 Estimated tokens: ${("~" + estimate.estimatedTokens.toLocaleString() + " tokens").padEnd(40)}\u2502
|
|
5309
5427
|
\u2502 \u2502
|
|
5310
|
-
\u2502 Provider: ${
|
|
5311
|
-
\u2502 Model: ${
|
|
5312
|
-
\u2502 Cost: ${
|
|
5428
|
+
\u2502 Provider: ${estimate.provider.padEnd(52)}\u2502
|
|
5429
|
+
\u2502 Model: ${estimate.model.padEnd(52)}\u2502
|
|
5430
|
+
\u2502 Cost: ${costFormatted.padEnd(52)}\u2502
|
|
5313
5431
|
\u2502 \u2502
|
|
5314
5432
|
\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
|
|
5315
5433
|
`;
|
|
@@ -5321,9 +5439,6 @@ function formatBytes(bytes) {
|
|
|
5321
5439
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
5322
5440
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i];
|
|
5323
5441
|
}
|
|
5324
|
-
function padRight(str, length) {
|
|
5325
|
-
return str.padEnd(length);
|
|
5326
|
-
}
|
|
5327
5442
|
|
|
5328
5443
|
// src/utils/logger.ts
|
|
5329
5444
|
var LOG_LEVEL_PRIORITY = {
|
|
@@ -5390,6 +5505,10 @@ var Logger = class {
|
|
|
5390
5505
|
this.logs.shift();
|
|
5391
5506
|
}
|
|
5392
5507
|
}
|
|
5508
|
+
withMetrics(fn) {
|
|
5509
|
+
if (!this.config.metrics) return;
|
|
5510
|
+
fn();
|
|
5511
|
+
}
|
|
5393
5512
|
search(level, message, data) {
|
|
5394
5513
|
if (this.config.logSearch) {
|
|
5395
5514
|
this.log(level, "search", message, data);
|
|
@@ -5428,89 +5547,107 @@ var Logger = class {
|
|
|
5428
5547
|
this.log("debug", "general", message, data);
|
|
5429
5548
|
}
|
|
5430
5549
|
recordIndexingStart() {
|
|
5431
|
-
|
|
5432
|
-
|
|
5550
|
+
this.withMetrics(() => {
|
|
5551
|
+
this.metrics.indexingStartTime = Date.now();
|
|
5552
|
+
});
|
|
5433
5553
|
}
|
|
5434
5554
|
recordIndexingEnd() {
|
|
5435
|
-
|
|
5436
|
-
|
|
5555
|
+
this.withMetrics(() => {
|
|
5556
|
+
this.metrics.indexingEndTime = Date.now();
|
|
5557
|
+
});
|
|
5437
5558
|
}
|
|
5438
5559
|
recordFilesScanned(count) {
|
|
5439
|
-
|
|
5440
|
-
|
|
5560
|
+
this.withMetrics(() => {
|
|
5561
|
+
this.metrics.filesScanned = count;
|
|
5562
|
+
});
|
|
5441
5563
|
}
|
|
5442
5564
|
recordFilesParsed(count) {
|
|
5443
|
-
|
|
5444
|
-
|
|
5565
|
+
this.withMetrics(() => {
|
|
5566
|
+
this.metrics.filesParsed = count;
|
|
5567
|
+
});
|
|
5445
5568
|
}
|
|
5446
5569
|
recordParseDuration(durationMs) {
|
|
5447
|
-
|
|
5448
|
-
|
|
5570
|
+
this.withMetrics(() => {
|
|
5571
|
+
this.metrics.parseMs = durationMs;
|
|
5572
|
+
});
|
|
5449
5573
|
}
|
|
5450
5574
|
recordChunksProcessed(count) {
|
|
5451
|
-
|
|
5452
|
-
|
|
5575
|
+
this.withMetrics(() => {
|
|
5576
|
+
this.metrics.chunksProcessed += count;
|
|
5577
|
+
});
|
|
5453
5578
|
}
|
|
5454
5579
|
recordChunksEmbedded(count) {
|
|
5455
|
-
|
|
5456
|
-
|
|
5580
|
+
this.withMetrics(() => {
|
|
5581
|
+
this.metrics.chunksEmbedded += count;
|
|
5582
|
+
});
|
|
5457
5583
|
}
|
|
5458
5584
|
recordChunksFromCache(count) {
|
|
5459
|
-
|
|
5460
|
-
|
|
5585
|
+
this.withMetrics(() => {
|
|
5586
|
+
this.metrics.chunksFromCache += count;
|
|
5587
|
+
});
|
|
5461
5588
|
}
|
|
5462
5589
|
recordChunksRemoved(count) {
|
|
5463
|
-
|
|
5464
|
-
|
|
5590
|
+
this.withMetrics(() => {
|
|
5591
|
+
this.metrics.chunksRemoved += count;
|
|
5592
|
+
});
|
|
5465
5593
|
}
|
|
5466
5594
|
recordEmbeddingApiCall(tokens) {
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5595
|
+
this.withMetrics(() => {
|
|
5596
|
+
this.metrics.embeddingApiCalls++;
|
|
5597
|
+
this.metrics.embeddingTokensUsed += tokens;
|
|
5598
|
+
});
|
|
5470
5599
|
}
|
|
5471
5600
|
recordEmbeddingError() {
|
|
5472
|
-
|
|
5473
|
-
|
|
5601
|
+
this.withMetrics(() => {
|
|
5602
|
+
this.metrics.embeddingErrors++;
|
|
5603
|
+
});
|
|
5474
5604
|
}
|
|
5475
5605
|
recordSearch(durationMs, breakdown) {
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5606
|
+
this.withMetrics(() => {
|
|
5607
|
+
this.metrics.searchCount++;
|
|
5608
|
+
this.metrics.searchTotalMs += durationMs;
|
|
5609
|
+
this.metrics.searchLastMs = durationMs;
|
|
5610
|
+
this.metrics.searchAvgMs = this.metrics.searchTotalMs / this.metrics.searchCount;
|
|
5611
|
+
if (breakdown) {
|
|
5612
|
+
this.metrics.embeddingCallMs = breakdown.embeddingMs;
|
|
5613
|
+
this.metrics.vectorSearchMs = breakdown.vectorMs;
|
|
5614
|
+
this.metrics.keywordSearchMs = breakdown.keywordMs;
|
|
5615
|
+
this.metrics.fusionMs = breakdown.fusionMs;
|
|
5616
|
+
}
|
|
5617
|
+
});
|
|
5487
5618
|
}
|
|
5488
5619
|
recordCacheHit() {
|
|
5489
|
-
|
|
5490
|
-
|
|
5620
|
+
this.withMetrics(() => {
|
|
5621
|
+
this.metrics.cacheHits++;
|
|
5622
|
+
});
|
|
5491
5623
|
}
|
|
5492
5624
|
recordCacheMiss() {
|
|
5493
|
-
|
|
5494
|
-
|
|
5625
|
+
this.withMetrics(() => {
|
|
5626
|
+
this.metrics.cacheMisses++;
|
|
5627
|
+
});
|
|
5495
5628
|
}
|
|
5496
5629
|
recordQueryCacheHit() {
|
|
5497
|
-
|
|
5498
|
-
|
|
5630
|
+
this.withMetrics(() => {
|
|
5631
|
+
this.metrics.queryCacheHits++;
|
|
5632
|
+
});
|
|
5499
5633
|
}
|
|
5500
5634
|
recordQueryCacheSimilarHit() {
|
|
5501
|
-
|
|
5502
|
-
|
|
5635
|
+
this.withMetrics(() => {
|
|
5636
|
+
this.metrics.queryCacheSimilarHits++;
|
|
5637
|
+
});
|
|
5503
5638
|
}
|
|
5504
5639
|
recordQueryCacheMiss() {
|
|
5505
|
-
|
|
5506
|
-
|
|
5640
|
+
this.withMetrics(() => {
|
|
5641
|
+
this.metrics.queryCacheMisses++;
|
|
5642
|
+
});
|
|
5507
5643
|
}
|
|
5508
5644
|
recordGc(orphans, chunks, embeddings) {
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5645
|
+
this.withMetrics(() => {
|
|
5646
|
+
this.metrics.gcRuns++;
|
|
5647
|
+
this.metrics.gcOrphansRemoved += orphans;
|
|
5648
|
+
this.metrics.gcChunksRemoved += chunks;
|
|
5649
|
+
this.metrics.gcEmbeddingsRemoved += embeddings;
|
|
5650
|
+
});
|
|
5514
5651
|
}
|
|
5515
5652
|
getMetrics() {
|
|
5516
5653
|
return { ...this.metrics };
|
|
@@ -5617,7 +5754,7 @@ function initializeLogger(config) {
|
|
|
5617
5754
|
}
|
|
5618
5755
|
|
|
5619
5756
|
// src/native/index.ts
|
|
5620
|
-
import * as
|
|
5757
|
+
import * as path11 from "path";
|
|
5621
5758
|
import * as os4 from "os";
|
|
5622
5759
|
import * as module from "module";
|
|
5623
5760
|
import { fileURLToPath } from "url";
|
|
@@ -5641,19 +5778,19 @@ function getNativeBinding() {
|
|
|
5641
5778
|
let currentDir;
|
|
5642
5779
|
let requireTarget;
|
|
5643
5780
|
if (typeof import.meta !== "undefined" && import.meta.url) {
|
|
5644
|
-
currentDir =
|
|
5781
|
+
currentDir = path11.dirname(fileURLToPath(import.meta.url));
|
|
5645
5782
|
requireTarget = import.meta.url;
|
|
5646
5783
|
} else if (typeof __dirname !== "undefined") {
|
|
5647
5784
|
currentDir = __dirname;
|
|
5648
5785
|
requireTarget = __filename;
|
|
5649
5786
|
} else {
|
|
5650
5787
|
currentDir = process.cwd();
|
|
5651
|
-
requireTarget =
|
|
5788
|
+
requireTarget = path11.join(currentDir, "index.js");
|
|
5652
5789
|
}
|
|
5653
5790
|
const normalizedDir = currentDir.replace(/\\/g, "/");
|
|
5654
|
-
const isDevMode = normalizedDir.includes("/src/native") || currentDir.includes(
|
|
5655
|
-
const packageRoot = isDevMode ?
|
|
5656
|
-
const nativePath =
|
|
5791
|
+
const isDevMode = normalizedDir.includes("/src/native") || currentDir.includes(path11.join("src", "native"));
|
|
5792
|
+
const packageRoot = isDevMode ? path11.resolve(currentDir, "../..") : path11.resolve(currentDir, "..");
|
|
5793
|
+
const nativePath = path11.join(packageRoot, "native", bindingName);
|
|
5657
5794
|
const require2 = module.createRequire(requireTarget);
|
|
5658
5795
|
return require2(nativePath);
|
|
5659
5796
|
}
|
|
@@ -6223,7 +6360,6 @@ var Database = class {
|
|
|
6223
6360
|
this.throwIfClosed();
|
|
6224
6361
|
return this.inner.getStats();
|
|
6225
6362
|
}
|
|
6226
|
-
// ── Symbol methods ──────────────────────────────────────────────
|
|
6227
6363
|
upsertSymbol(symbol) {
|
|
6228
6364
|
this.throwIfClosed();
|
|
6229
6365
|
this.inner.upsertSymbol(symbol);
|
|
@@ -6253,7 +6389,6 @@ var Database = class {
|
|
|
6253
6389
|
this.throwIfClosed();
|
|
6254
6390
|
return this.inner.deleteSymbolsByFile(filePath);
|
|
6255
6391
|
}
|
|
6256
|
-
// ── Call Edge methods ────────────────────────────────────────────
|
|
6257
6392
|
upsertCallEdge(edge) {
|
|
6258
6393
|
this.throwIfClosed();
|
|
6259
6394
|
this.inner.upsertCallEdge(edge);
|
|
@@ -6283,7 +6418,6 @@ var Database = class {
|
|
|
6283
6418
|
this.throwIfClosed();
|
|
6284
6419
|
this.inner.resolveCallEdge(edgeId, toSymbolId);
|
|
6285
6420
|
}
|
|
6286
|
-
// ── Branch Symbol methods ────────────────────────────────────────
|
|
6287
6421
|
addSymbolsToBranch(branch, symbolIds) {
|
|
6288
6422
|
this.throwIfClosed();
|
|
6289
6423
|
this.inner.addSymbolsToBranch(branch, symbolIds);
|
|
@@ -6316,7 +6450,6 @@ var Database = class {
|
|
|
6316
6450
|
if (symbolIds.length === 0) return 0;
|
|
6317
6451
|
return this.inner.deleteBranchSymbolsForBranch(branch, symbolIds);
|
|
6318
6452
|
}
|
|
6319
|
-
// ── GC methods for symbols/edges ─────────────────────────────────
|
|
6320
6453
|
gcOrphanSymbols() {
|
|
6321
6454
|
this.throwIfClosed();
|
|
6322
6455
|
return this.inner.gcOrphanSymbols();
|
|
@@ -6328,7 +6461,7 @@ var Database = class {
|
|
|
6328
6461
|
};
|
|
6329
6462
|
|
|
6330
6463
|
// src/indexer/index.ts
|
|
6331
|
-
var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig"]);
|
|
6464
|
+
var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig", "gdscript", "matlab"]);
|
|
6332
6465
|
var CASE_INSENSITIVE_LANGUAGES = /* @__PURE__ */ new Set(["apex"]);
|
|
6333
6466
|
var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
6334
6467
|
"function_declaration",
|
|
@@ -6355,7 +6488,15 @@ var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
|
6355
6488
|
"trigger_declaration",
|
|
6356
6489
|
"test_declaration",
|
|
6357
6490
|
"struct_declaration",
|
|
6358
|
-
"union_declaration"
|
|
6491
|
+
"union_declaration",
|
|
6492
|
+
// GDScript declarations whose names participate in the call graph.
|
|
6493
|
+
// `function_definition` and `class_definition` are already in the set
|
|
6494
|
+
// above (shared with Python/C/Bash and Python, respectively).
|
|
6495
|
+
"constructor_definition",
|
|
6496
|
+
"enum_definition",
|
|
6497
|
+
"signal_statement",
|
|
6498
|
+
"const_statement",
|
|
6499
|
+
"class_name_statement"
|
|
6359
6500
|
]);
|
|
6360
6501
|
function float32ArrayToBuffer(arr) {
|
|
6361
6502
|
const float32 = new Float32Array(arr);
|
|
@@ -6557,9 +6698,9 @@ function hasAllEmbeddingParts(parts, expectedPartCount) {
|
|
|
6557
6698
|
return true;
|
|
6558
6699
|
}
|
|
6559
6700
|
function isPathWithinRoot(filePath, rootPath) {
|
|
6560
|
-
const normalizedFilePath =
|
|
6561
|
-
const normalizedRoot =
|
|
6562
|
-
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${
|
|
6701
|
+
const normalizedFilePath = path12.resolve(filePath);
|
|
6702
|
+
const normalizedRoot = path12.resolve(rootPath);
|
|
6703
|
+
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path12.sep}`);
|
|
6563
6704
|
}
|
|
6564
6705
|
var rankingQueryTokenCache = /* @__PURE__ */ new Map();
|
|
6565
6706
|
var rankingNameTokenCache = /* @__PURE__ */ new Map();
|
|
@@ -7612,22 +7753,28 @@ var Indexer = class {
|
|
|
7612
7753
|
this.projectRoot = projectRoot;
|
|
7613
7754
|
this.config = config;
|
|
7614
7755
|
this.indexPath = this.getIndexPath();
|
|
7615
|
-
this.fileHashCachePath =
|
|
7616
|
-
this.failedBatchesPath =
|
|
7617
|
-
this.indexingLockPath =
|
|
7756
|
+
this.fileHashCachePath = path12.join(this.indexPath, "file-hashes.json");
|
|
7757
|
+
this.failedBatchesPath = path12.join(this.indexPath, "failed-batches.json");
|
|
7758
|
+
this.indexingLockPath = path12.join(this.indexPath, "indexing.lock");
|
|
7618
7759
|
this.logger = initializeLogger(config.debug);
|
|
7619
7760
|
}
|
|
7620
7761
|
getIndexPath() {
|
|
7621
7762
|
return resolveProjectIndexPath(this.projectRoot, this.config.scope);
|
|
7622
7763
|
}
|
|
7623
7764
|
loadFileHashCache() {
|
|
7765
|
+
if (!existsSync7(this.fileHashCachePath)) {
|
|
7766
|
+
return;
|
|
7767
|
+
}
|
|
7624
7768
|
try {
|
|
7625
|
-
|
|
7626
|
-
|
|
7627
|
-
|
|
7628
|
-
|
|
7629
|
-
|
|
7630
|
-
|
|
7769
|
+
const data = readFileSync6(this.fileHashCachePath, "utf-8");
|
|
7770
|
+
const parsed = JSON.parse(data);
|
|
7771
|
+
this.fileHashCache = new Map(Object.entries(parsed));
|
|
7772
|
+
} catch (error) {
|
|
7773
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7774
|
+
this.logger.warn("Failed to load file hash cache, resetting cache state", {
|
|
7775
|
+
fileHashCachePath: this.fileHashCachePath,
|
|
7776
|
+
error: message
|
|
7777
|
+
});
|
|
7631
7778
|
this.fileHashCache = /* @__PURE__ */ new Map();
|
|
7632
7779
|
}
|
|
7633
7780
|
}
|
|
@@ -7640,14 +7787,14 @@ var Indexer = class {
|
|
|
7640
7787
|
}
|
|
7641
7788
|
atomicWriteSync(targetPath, data) {
|
|
7642
7789
|
const tempPath = `${targetPath}.tmp`;
|
|
7643
|
-
mkdirSync2(
|
|
7790
|
+
mkdirSync2(path12.dirname(targetPath), { recursive: true });
|
|
7644
7791
|
writeFileSync2(tempPath, data);
|
|
7645
7792
|
renameSync(tempPath, targetPath);
|
|
7646
7793
|
}
|
|
7647
7794
|
getScopedRoots() {
|
|
7648
|
-
const roots = /* @__PURE__ */ new Set([
|
|
7795
|
+
const roots = /* @__PURE__ */ new Set([path12.resolve(this.projectRoot)]);
|
|
7649
7796
|
for (const kbRoot of this.config.knowledgeBases) {
|
|
7650
|
-
roots.add(
|
|
7797
|
+
roots.add(path12.resolve(this.projectRoot, kbRoot));
|
|
7651
7798
|
}
|
|
7652
7799
|
return Array.from(roots);
|
|
7653
7800
|
}
|
|
@@ -7656,22 +7803,22 @@ var Indexer = class {
|
|
|
7656
7803
|
if (this.config.scope !== "global") {
|
|
7657
7804
|
return branchName;
|
|
7658
7805
|
}
|
|
7659
|
-
const projectHash = hashContent(
|
|
7806
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7660
7807
|
return `${projectHash}:${branchName}`;
|
|
7661
7808
|
}
|
|
7662
7809
|
getLegacyBranchCatalogKey() {
|
|
7663
7810
|
return this.currentBranch || "default";
|
|
7664
7811
|
}
|
|
7665
7812
|
getLegacyMigrationMetadataKey() {
|
|
7666
|
-
const projectHash = hashContent(
|
|
7813
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7667
7814
|
return `index.globalBranchMigration.${projectHash}`;
|
|
7668
7815
|
}
|
|
7669
7816
|
getProjectEmbeddingStrategyMetadataKey() {
|
|
7670
|
-
const projectHash = hashContent(
|
|
7817
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7671
7818
|
return `index.embeddingStrategyVersion.${projectHash}`;
|
|
7672
7819
|
}
|
|
7673
7820
|
getProjectForceReembedMetadataKey() {
|
|
7674
|
-
const projectHash = hashContent(
|
|
7821
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7675
7822
|
return `index.forceReembed.${projectHash}`;
|
|
7676
7823
|
}
|
|
7677
7824
|
hasProjectForceReembedPending() {
|
|
@@ -7765,7 +7912,7 @@ var Indexer = class {
|
|
|
7765
7912
|
if (!this.database) {
|
|
7766
7913
|
return { chunkIds, symbolIds };
|
|
7767
7914
|
}
|
|
7768
|
-
const projectRootPath =
|
|
7915
|
+
const projectRootPath = path12.resolve(this.projectRoot);
|
|
7769
7916
|
const projectLocalFilePaths = /* @__PURE__ */ new Set([
|
|
7770
7917
|
...Array.from(this.fileHashCache.keys()).filter(
|
|
7771
7918
|
(filePath) => this.isFileInCurrentScope(filePath, roots) && isPathWithinRoot(filePath, projectRootPath)
|
|
@@ -7788,7 +7935,7 @@ var Indexer = class {
|
|
|
7788
7935
|
if (this.config.scope !== "global") {
|
|
7789
7936
|
return this.getBranchCatalogCleanupKeys();
|
|
7790
7937
|
}
|
|
7791
|
-
const projectHash = hashContent(
|
|
7938
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7792
7939
|
const keys = /* @__PURE__ */ new Set();
|
|
7793
7940
|
const projectChunkIdSet = new Set(projectChunkIds);
|
|
7794
7941
|
const projectSymbolIdSet = new Set(projectSymbolIds);
|
|
@@ -7872,7 +8019,7 @@ var Indexer = class {
|
|
|
7872
8019
|
if (!this.database || this.config.scope !== "global") {
|
|
7873
8020
|
return false;
|
|
7874
8021
|
}
|
|
7875
|
-
const projectHash = hashContent(
|
|
8022
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7876
8023
|
const roots = this.getScopedRoots();
|
|
7877
8024
|
const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots);
|
|
7878
8025
|
return this.database.getAllBranches().some(
|
|
@@ -7906,7 +8053,7 @@ var Indexer = class {
|
|
|
7906
8053
|
...Array.from(this.fileHashCache.keys()).filter((filePath) => this.isFileInCurrentScope(filePath, roots)),
|
|
7907
8054
|
...scopedEntries.map(({ metadata }) => metadata.filePath)
|
|
7908
8055
|
]);
|
|
7909
|
-
const projectRootPath =
|
|
8056
|
+
const projectRootPath = path12.resolve(this.projectRoot);
|
|
7910
8057
|
const projectLocalFilePaths = new Set(
|
|
7911
8058
|
Array.from(filePaths).filter((filePath) => isPathWithinRoot(filePath, projectRootPath))
|
|
7912
8059
|
);
|
|
@@ -7975,7 +8122,7 @@ var Indexer = class {
|
|
|
7975
8122
|
};
|
|
7976
8123
|
}
|
|
7977
8124
|
checkForInterruptedIndexing() {
|
|
7978
|
-
return
|
|
8125
|
+
return existsSync7(this.indexingLockPath);
|
|
7979
8126
|
}
|
|
7980
8127
|
acquireIndexingLock() {
|
|
7981
8128
|
const lockData = {
|
|
@@ -7985,13 +8132,13 @@ var Indexer = class {
|
|
|
7985
8132
|
writeFileSync2(this.indexingLockPath, JSON.stringify(lockData));
|
|
7986
8133
|
}
|
|
7987
8134
|
releaseIndexingLock() {
|
|
7988
|
-
if (
|
|
8135
|
+
if (existsSync7(this.indexingLockPath)) {
|
|
7989
8136
|
unlinkSync(this.indexingLockPath);
|
|
7990
8137
|
}
|
|
7991
8138
|
}
|
|
7992
8139
|
async recoverFromInterruptedIndexing() {
|
|
7993
8140
|
this.logger.warn("Detected interrupted indexing session, recovering...");
|
|
7994
|
-
if (
|
|
8141
|
+
if (existsSync7(this.fileHashCachePath)) {
|
|
7995
8142
|
unlinkSync(this.fileHashCachePath);
|
|
7996
8143
|
}
|
|
7997
8144
|
await this.healthCheck();
|
|
@@ -8001,15 +8148,20 @@ var Indexer = class {
|
|
|
8001
8148
|
loadFailedBatches(maxChunkTokens) {
|
|
8002
8149
|
try {
|
|
8003
8150
|
return this.loadSerializedFailedBatches().map((batch) => normalizeFailedBatch(batch, maxChunkTokens)).filter((batch) => batch !== null);
|
|
8004
|
-
} catch {
|
|
8151
|
+
} catch (error) {
|
|
8152
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
8153
|
+
this.logger.warn("Failed to load failed batch state, skipping persisted retries", {
|
|
8154
|
+
failedBatchesPath: this.failedBatchesPath,
|
|
8155
|
+
error: message
|
|
8156
|
+
});
|
|
8005
8157
|
return [];
|
|
8006
8158
|
}
|
|
8007
8159
|
}
|
|
8008
8160
|
loadSerializedFailedBatches() {
|
|
8009
|
-
if (!
|
|
8161
|
+
if (!existsSync7(this.failedBatchesPath)) {
|
|
8010
8162
|
return [];
|
|
8011
8163
|
}
|
|
8012
|
-
const data =
|
|
8164
|
+
const data = readFileSync6(this.failedBatchesPath, "utf-8");
|
|
8013
8165
|
const parsed = JSON.parse(data);
|
|
8014
8166
|
return parsed.map((batch) => {
|
|
8015
8167
|
const chunks = Array.isArray(batch.chunks) ? batch.chunks : [];
|
|
@@ -8026,7 +8178,7 @@ var Indexer = class {
|
|
|
8026
8178
|
}
|
|
8027
8179
|
saveFailedBatches(batches) {
|
|
8028
8180
|
if (batches.length === 0) {
|
|
8029
|
-
if (
|
|
8181
|
+
if (existsSync7(this.failedBatchesPath)) {
|
|
8030
8182
|
try {
|
|
8031
8183
|
unlinkSync(this.failedBatchesPath);
|
|
8032
8184
|
} catch {
|
|
@@ -8261,24 +8413,24 @@ var Indexer = class {
|
|
|
8261
8413
|
}
|
|
8262
8414
|
await fsPromises2.mkdir(this.indexPath, { recursive: true });
|
|
8263
8415
|
const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
|
|
8264
|
-
const storePath =
|
|
8416
|
+
const storePath = path12.join(this.indexPath, "vectors");
|
|
8265
8417
|
this.store = new VectorStore(storePath, dimensions);
|
|
8266
|
-
const indexFilePath =
|
|
8267
|
-
if (
|
|
8418
|
+
const indexFilePath = path12.join(this.indexPath, "vectors.usearch");
|
|
8419
|
+
if (existsSync7(indexFilePath)) {
|
|
8268
8420
|
this.store.load();
|
|
8269
8421
|
}
|
|
8270
|
-
const invertedIndexPath =
|
|
8422
|
+
const invertedIndexPath = path12.join(this.indexPath, "inverted-index.json");
|
|
8271
8423
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
8272
8424
|
try {
|
|
8273
8425
|
this.invertedIndex.load();
|
|
8274
8426
|
} catch {
|
|
8275
|
-
if (
|
|
8427
|
+
if (existsSync7(invertedIndexPath)) {
|
|
8276
8428
|
await fsPromises2.unlink(invertedIndexPath);
|
|
8277
8429
|
}
|
|
8278
8430
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
8279
8431
|
}
|
|
8280
|
-
const dbPath =
|
|
8281
|
-
let dbIsNew = !
|
|
8432
|
+
const dbPath = path12.join(this.indexPath, "codebase.db");
|
|
8433
|
+
let dbIsNew = !existsSync7(dbPath);
|
|
8282
8434
|
try {
|
|
8283
8435
|
this.database = new Database(dbPath);
|
|
8284
8436
|
} catch (error) {
|
|
@@ -8358,7 +8510,7 @@ var Indexer = class {
|
|
|
8358
8510
|
if (await this.tryResetCorruptedIndex("running automatic orphan garbage collection", error)) {
|
|
8359
8511
|
return {
|
|
8360
8512
|
resetCorruptedIndex: true,
|
|
8361
|
-
warning: this.getCorruptedIndexWarning(
|
|
8513
|
+
warning: this.getCorruptedIndexWarning(path12.join(this.indexPath, "codebase.db"))
|
|
8362
8514
|
};
|
|
8363
8515
|
}
|
|
8364
8516
|
throw error;
|
|
@@ -8373,7 +8525,7 @@ var Indexer = class {
|
|
|
8373
8525
|
return;
|
|
8374
8526
|
}
|
|
8375
8527
|
const retainedEntries = store.getAllMetadata().filter(({ key }) => !excludedSet.has(key));
|
|
8376
|
-
const storeBasePath =
|
|
8528
|
+
const storeBasePath = path12.join(this.indexPath, "vectors");
|
|
8377
8529
|
const storeIndexPath = `${storeBasePath}.usearch`;
|
|
8378
8530
|
const storeMetadataPath = `${storeBasePath}.meta.json`;
|
|
8379
8531
|
const backupIndexPath = `${storeIndexPath}.bak`;
|
|
@@ -8382,18 +8534,18 @@ var Indexer = class {
|
|
|
8382
8534
|
let backedUpMetadata = false;
|
|
8383
8535
|
let rebuiltCount = 0;
|
|
8384
8536
|
let skippedCount = 0;
|
|
8385
|
-
if (
|
|
8537
|
+
if (existsSync7(backupIndexPath)) {
|
|
8386
8538
|
unlinkSync(backupIndexPath);
|
|
8387
8539
|
}
|
|
8388
|
-
if (
|
|
8540
|
+
if (existsSync7(backupMetadataPath)) {
|
|
8389
8541
|
unlinkSync(backupMetadataPath);
|
|
8390
8542
|
}
|
|
8391
8543
|
try {
|
|
8392
|
-
if (
|
|
8544
|
+
if (existsSync7(storeIndexPath)) {
|
|
8393
8545
|
renameSync(storeIndexPath, backupIndexPath);
|
|
8394
8546
|
backedUpIndex = true;
|
|
8395
8547
|
}
|
|
8396
|
-
if (
|
|
8548
|
+
if (existsSync7(storeMetadataPath)) {
|
|
8397
8549
|
renameSync(storeMetadataPath, backupMetadataPath);
|
|
8398
8550
|
backedUpMetadata = true;
|
|
8399
8551
|
}
|
|
@@ -8414,10 +8566,10 @@ var Indexer = class {
|
|
|
8414
8566
|
rebuiltCount += 1;
|
|
8415
8567
|
}
|
|
8416
8568
|
store.save();
|
|
8417
|
-
if (backedUpIndex &&
|
|
8569
|
+
if (backedUpIndex && existsSync7(backupIndexPath)) {
|
|
8418
8570
|
unlinkSync(backupIndexPath);
|
|
8419
8571
|
}
|
|
8420
|
-
if (backedUpMetadata &&
|
|
8572
|
+
if (backedUpMetadata && existsSync7(backupMetadataPath)) {
|
|
8421
8573
|
unlinkSync(backupMetadataPath);
|
|
8422
8574
|
}
|
|
8423
8575
|
this.logger.gc("info", "Rebuilt vector store to avoid native remove", {
|
|
@@ -8430,16 +8582,16 @@ var Indexer = class {
|
|
|
8430
8582
|
store.clear();
|
|
8431
8583
|
} catch {
|
|
8432
8584
|
}
|
|
8433
|
-
if (
|
|
8585
|
+
if (existsSync7(storeIndexPath)) {
|
|
8434
8586
|
unlinkSync(storeIndexPath);
|
|
8435
8587
|
}
|
|
8436
|
-
if (
|
|
8588
|
+
if (existsSync7(storeMetadataPath)) {
|
|
8437
8589
|
unlinkSync(storeMetadataPath);
|
|
8438
8590
|
}
|
|
8439
|
-
if (backedUpIndex &&
|
|
8591
|
+
if (backedUpIndex && existsSync7(backupIndexPath)) {
|
|
8440
8592
|
renameSync(backupIndexPath, storeIndexPath);
|
|
8441
8593
|
}
|
|
8442
|
-
if (backedUpMetadata &&
|
|
8594
|
+
if (backedUpMetadata && existsSync7(backupMetadataPath)) {
|
|
8443
8595
|
renameSync(backupMetadataPath, storeMetadataPath);
|
|
8444
8596
|
}
|
|
8445
8597
|
if (backedUpIndex || backedUpMetadata) {
|
|
@@ -8458,7 +8610,7 @@ var Indexer = class {
|
|
|
8458
8610
|
if (!isSqliteCorruptionError(error)) {
|
|
8459
8611
|
return false;
|
|
8460
8612
|
}
|
|
8461
|
-
const dbPath =
|
|
8613
|
+
const dbPath = path12.join(this.indexPath, "codebase.db");
|
|
8462
8614
|
const warning = this.getCorruptedIndexWarning(dbPath);
|
|
8463
8615
|
const errorMessage = getErrorMessage(error);
|
|
8464
8616
|
if (this.config.scope === "global") {
|
|
@@ -8481,15 +8633,15 @@ var Indexer = class {
|
|
|
8481
8633
|
this.indexCompatibility = null;
|
|
8482
8634
|
this.fileHashCache.clear();
|
|
8483
8635
|
const resetPaths = [
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
|
|
8489
|
-
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
|
|
8636
|
+
path12.join(this.indexPath, "codebase.db"),
|
|
8637
|
+
path12.join(this.indexPath, "codebase.db-shm"),
|
|
8638
|
+
path12.join(this.indexPath, "codebase.db-wal"),
|
|
8639
|
+
path12.join(this.indexPath, "vectors.usearch"),
|
|
8640
|
+
path12.join(this.indexPath, "inverted-index.json"),
|
|
8641
|
+
path12.join(this.indexPath, "file-hashes.json"),
|
|
8642
|
+
path12.join(this.indexPath, "failed-batches.json"),
|
|
8643
|
+
path12.join(this.indexPath, "indexing.lock"),
|
|
8644
|
+
path12.join(this.indexPath, "vectors")
|
|
8493
8645
|
];
|
|
8494
8646
|
await Promise.all(resetPaths.map(async (targetPath) => {
|
|
8495
8647
|
try {
|
|
@@ -8754,7 +8906,7 @@ var Indexer = class {
|
|
|
8754
8906
|
for (const parsed of parsedFiles) {
|
|
8755
8907
|
currentFilePaths.add(parsed.path);
|
|
8756
8908
|
if (parsed.chunks.length === 0) {
|
|
8757
|
-
const relativePath =
|
|
8909
|
+
const relativePath = path12.relative(this.projectRoot, parsed.path);
|
|
8758
8910
|
stats.parseFailures.push(relativePath);
|
|
8759
8911
|
}
|
|
8760
8912
|
let fileChunkCount = 0;
|
|
@@ -9037,7 +9189,7 @@ var Indexer = class {
|
|
|
9037
9189
|
for (const requestBatch of requestBatches) {
|
|
9038
9190
|
queue.add(async () => {
|
|
9039
9191
|
if (rateLimitBackoffMs > 0) {
|
|
9040
|
-
await new Promise((
|
|
9192
|
+
await new Promise((resolve12) => setTimeout(resolve12, rateLimitBackoffMs));
|
|
9041
9193
|
}
|
|
9042
9194
|
try {
|
|
9043
9195
|
const result = await pRetry(
|
|
@@ -9598,8 +9750,8 @@ var Indexer = class {
|
|
|
9598
9750
|
this.indexCompatibility = compatibility;
|
|
9599
9751
|
return;
|
|
9600
9752
|
}
|
|
9601
|
-
const localProjectIndexPath =
|
|
9602
|
-
if (
|
|
9753
|
+
const localProjectIndexPath = path12.join(this.projectRoot, ".opencode", "index");
|
|
9754
|
+
if (path12.resolve(this.indexPath) !== path12.resolve(localProjectIndexPath)) {
|
|
9603
9755
|
throw new Error(
|
|
9604
9756
|
"Project-scoped force rebuild is unsafe while using an inherited worktree index. Create a local project config boundary before clearing the index."
|
|
9605
9757
|
);
|
|
@@ -9638,7 +9790,7 @@ var Indexer = class {
|
|
|
9638
9790
|
const removedChunkKeys = [];
|
|
9639
9791
|
const chunkKeysByRemovedFile = /* @__PURE__ */ new Map();
|
|
9640
9792
|
for (const [filePath, chunkKeys] of filePathsToChunkKeys) {
|
|
9641
|
-
if (!
|
|
9793
|
+
if (!existsSync7(filePath)) {
|
|
9642
9794
|
chunkKeysByRemovedFile.set(filePath, chunkKeys);
|
|
9643
9795
|
for (const key of chunkKeys) {
|
|
9644
9796
|
removedChunkKeys.push(key);
|
|
@@ -9687,7 +9839,7 @@ var Indexer = class {
|
|
|
9687
9839
|
gcOrphanSymbols: 0,
|
|
9688
9840
|
gcOrphanCallEdges: 0,
|
|
9689
9841
|
resetCorruptedIndex: true,
|
|
9690
|
-
warning: this.getCorruptedIndexWarning(
|
|
9842
|
+
warning: this.getCorruptedIndexWarning(path12.join(this.indexPath, "codebase.db"))
|
|
9691
9843
|
};
|
|
9692
9844
|
}
|
|
9693
9845
|
this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
|
|
@@ -10239,12 +10391,15 @@ function formatLogs(logs) {
|
|
|
10239
10391
|
return `[${l.timestamp}] [${l.level.toUpperCase()}] [${l.category}] ${l.message}${dataStr}`;
|
|
10240
10392
|
}).join("\n");
|
|
10241
10393
|
}
|
|
10394
|
+
function formatResultHeader(result, index) {
|
|
10395
|
+
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}`;
|
|
10396
|
+
}
|
|
10242
10397
|
function formatDefinitionLookup(results, query) {
|
|
10243
10398
|
if (results.length === 0) {
|
|
10244
10399
|
return `No definition found for "${query}". Try codebase_search for broader discovery, or verify the symbol name.`;
|
|
10245
10400
|
}
|
|
10246
10401
|
const formatted = results.map((r, idx) => {
|
|
10247
|
-
const header = r
|
|
10402
|
+
const header = formatResultHeader(r, idx);
|
|
10248
10403
|
return `${header} (score: ${r.score.toFixed(2)})
|
|
10249
10404
|
\`\`\`
|
|
10250
10405
|
${truncateContent(r.content)}
|
|
@@ -10254,7 +10409,7 @@ ${truncateContent(r.content)}
|
|
|
10254
10409
|
}
|
|
10255
10410
|
function formatSearchResults(results, scoreFormat = "similarity") {
|
|
10256
10411
|
const formatted = results.map((r, idx) => {
|
|
10257
|
-
const header = r
|
|
10412
|
+
const header = formatResultHeader(r, idx);
|
|
10258
10413
|
const scoreLabel = scoreFormat === "similarity" ? `(similarity: ${(r.score * 100).toFixed(1)}%)` : `(score: ${r.score.toFixed(2)})`;
|
|
10259
10414
|
return `${header} ${scoreLabel}
|
|
10260
10415
|
\`\`\`
|
|
@@ -10264,104 +10419,83 @@ ${truncateContent(r.content)}
|
|
|
10264
10419
|
return formatted.join("\n\n");
|
|
10265
10420
|
}
|
|
10266
10421
|
|
|
10267
|
-
// src/tools/
|
|
10268
|
-
import
|
|
10269
|
-
|
|
10270
|
-
var z = tool.schema;
|
|
10271
|
-
var sharedIndexer = null;
|
|
10272
|
-
var sharedProjectRoot = "";
|
|
10273
|
-
function initializeTools(projectRoot, config) {
|
|
10274
|
-
sharedProjectRoot = projectRoot;
|
|
10275
|
-
sharedIndexer = new Indexer(projectRoot, config);
|
|
10276
|
-
}
|
|
10277
|
-
function getSharedIndexer() {
|
|
10278
|
-
return getIndexer();
|
|
10279
|
-
}
|
|
10280
|
-
function refreshIndexerFromConfig() {
|
|
10281
|
-
if (!sharedProjectRoot) {
|
|
10282
|
-
throw new Error("Codebase index tools not initialized. Plugin may not be loaded correctly.");
|
|
10283
|
-
}
|
|
10284
|
-
sharedIndexer = new Indexer(sharedProjectRoot, parseConfig(loadRuntimeConfig()));
|
|
10285
|
-
}
|
|
10286
|
-
function shouldForceLocalizeProjectIndex() {
|
|
10287
|
-
const currentConfig = parseConfig(loadRuntimeConfig());
|
|
10288
|
-
if (currentConfig.scope !== "project") {
|
|
10289
|
-
return false;
|
|
10290
|
-
}
|
|
10291
|
-
const localIndexPath = path9.join(sharedProjectRoot, ".opencode", "index");
|
|
10292
|
-
const mainRepoRoot = resolveWorktreeMainRepoRoot(sharedProjectRoot);
|
|
10293
|
-
if (!mainRepoRoot) {
|
|
10294
|
-
return false;
|
|
10295
|
-
}
|
|
10296
|
-
const inheritedIndexPath = path9.join(mainRepoRoot, ".opencode", "index");
|
|
10297
|
-
return !existsSync7(localIndexPath) && existsSync7(inheritedIndexPath);
|
|
10298
|
-
}
|
|
10299
|
-
function getIndexer() {
|
|
10300
|
-
if (!sharedIndexer) {
|
|
10301
|
-
throw new Error("Codebase index tools not initialized. Plugin may not be loaded correctly.");
|
|
10302
|
-
}
|
|
10303
|
-
return sharedIndexer;
|
|
10304
|
-
}
|
|
10305
|
-
function getConfigPath() {
|
|
10306
|
-
return resolveWritableProjectConfigPath(sharedProjectRoot);
|
|
10307
|
-
}
|
|
10308
|
-
function normalizeConfigPathValue(value, baseDir) {
|
|
10422
|
+
// src/tools/knowledge-base-paths.ts
|
|
10423
|
+
import * as path13 from "path";
|
|
10424
|
+
function resolveConfigPathValue(value, baseDir) {
|
|
10309
10425
|
const trimmed = value.trim();
|
|
10310
10426
|
if (!trimmed) {
|
|
10311
10427
|
return trimmed;
|
|
10312
10428
|
}
|
|
10313
|
-
const absolutePath =
|
|
10314
|
-
return
|
|
10429
|
+
const absolutePath = path13.isAbsolute(trimmed) ? trimmed : path13.resolve(baseDir, trimmed);
|
|
10430
|
+
return path13.normalize(absolutePath);
|
|
10315
10431
|
}
|
|
10316
10432
|
function serializeConfigPathValue(value, baseDir) {
|
|
10317
10433
|
const trimmed = value.trim();
|
|
10318
10434
|
if (!trimmed) {
|
|
10319
10435
|
return trimmed;
|
|
10320
10436
|
}
|
|
10321
|
-
|
|
10322
|
-
|
|
10323
|
-
return normalizeRelativePath(path9.normalize(trimmed));
|
|
10437
|
+
if (!path13.isAbsolute(trimmed)) {
|
|
10438
|
+
return normalizePathSeparators(path13.normalize(trimmed));
|
|
10324
10439
|
}
|
|
10325
|
-
const relativePath =
|
|
10326
|
-
if (!relativePath || !relativePath.startsWith("..") && !
|
|
10327
|
-
return
|
|
10440
|
+
const relativePath = path13.relative(baseDir, trimmed);
|
|
10441
|
+
if (!relativePath || !relativePath.startsWith("..") && !path13.isAbsolute(relativePath)) {
|
|
10442
|
+
return normalizePathSeparators(path13.normalize(relativePath || "."));
|
|
10328
10443
|
}
|
|
10329
|
-
return
|
|
10444
|
+
return path13.normalize(trimmed);
|
|
10445
|
+
}
|
|
10446
|
+
function resolveKnowledgeBasePath(value, projectRoot) {
|
|
10447
|
+
return path13.isAbsolute(value) ? value : path13.resolve(projectRoot, value);
|
|
10448
|
+
}
|
|
10449
|
+
function normalizeKnowledgeBasePath2(value, projectRoot) {
|
|
10450
|
+
return path13.normalize(resolveKnowledgeBasePath(value, projectRoot));
|
|
10451
|
+
}
|
|
10452
|
+
function hasMatchingKnowledgeBasePath(knowledgeBases, inputPath, projectRoot) {
|
|
10453
|
+
const normalizedInput = path13.normalize(inputPath);
|
|
10454
|
+
return knowledgeBases.some((kb) => normalizeKnowledgeBasePath2(kb, projectRoot) === normalizedInput);
|
|
10330
10455
|
}
|
|
10331
|
-
function
|
|
10456
|
+
function findKnowledgeBasePathIndex(knowledgeBases, inputPath, projectRoot) {
|
|
10457
|
+
const normalizedInput = path13.normalize(inputPath);
|
|
10458
|
+
return knowledgeBases.findIndex(
|
|
10459
|
+
(kb) => path13.normalize(kb) === normalizedInput || normalizeKnowledgeBasePath2(kb, projectRoot) === normalizedInput
|
|
10460
|
+
);
|
|
10461
|
+
}
|
|
10462
|
+
|
|
10463
|
+
// src/tools/index.ts
|
|
10464
|
+
import { existsSync as existsSync9, realpathSync, statSync as statSync3 } from "fs";
|
|
10465
|
+
import * as path15 from "path";
|
|
10466
|
+
|
|
10467
|
+
// src/tools/config-state.ts
|
|
10468
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
10469
|
+
import * as path14 from "path";
|
|
10470
|
+
function normalizeKnowledgeBasePaths(config, projectRoot) {
|
|
10332
10471
|
const normalized = { ...config };
|
|
10333
10472
|
if (Array.isArray(normalized.knowledgeBases)) {
|
|
10334
|
-
normalized.knowledgeBases = normalized.knowledgeBases.map(
|
|
10335
|
-
|
|
10336
|
-
|
|
10473
|
+
normalized.knowledgeBases = normalized.knowledgeBases.map(
|
|
10474
|
+
(kb) => resolveConfigPathValue(kb, projectRoot)
|
|
10475
|
+
);
|
|
10337
10476
|
}
|
|
10338
10477
|
return normalized;
|
|
10339
10478
|
}
|
|
10340
|
-
function
|
|
10341
|
-
|
|
10342
|
-
|
|
10343
|
-
if (rawConfig && typeof rawConfig === "object") {
|
|
10344
|
-
for (const key of Object.keys(rawConfig)) {
|
|
10345
|
-
config[key] = rawConfig[key];
|
|
10346
|
-
}
|
|
10479
|
+
function toConfigRecord(rawConfig) {
|
|
10480
|
+
if (!rawConfig || typeof rawConfig !== "object") {
|
|
10481
|
+
return {};
|
|
10347
10482
|
}
|
|
10348
|
-
return
|
|
10483
|
+
return { ...rawConfig };
|
|
10349
10484
|
}
|
|
10350
|
-
function
|
|
10351
|
-
|
|
10352
|
-
|
|
10353
|
-
|
|
10354
|
-
|
|
10355
|
-
config[key] = rawConfig[key];
|
|
10356
|
-
}
|
|
10357
|
-
}
|
|
10358
|
-
return normalizeKnowledgeBasePaths(config);
|
|
10485
|
+
function getConfigPath(projectRoot) {
|
|
10486
|
+
return resolveWritableProjectConfigPath(projectRoot);
|
|
10487
|
+
}
|
|
10488
|
+
function loadRuntimeConfig(projectRoot) {
|
|
10489
|
+
return normalizeKnowledgeBasePaths(toConfigRecord(loadMergedConfig(projectRoot)), projectRoot);
|
|
10359
10490
|
}
|
|
10360
|
-
function
|
|
10361
|
-
|
|
10362
|
-
|
|
10363
|
-
|
|
10364
|
-
|
|
10491
|
+
function loadEditableConfig(projectRoot) {
|
|
10492
|
+
return normalizeKnowledgeBasePaths(toConfigRecord(loadProjectConfigLayer(projectRoot)), projectRoot);
|
|
10493
|
+
}
|
|
10494
|
+
function saveConfig(projectRoot, config) {
|
|
10495
|
+
const configPath = getConfigPath(projectRoot);
|
|
10496
|
+
const configDir = path14.dirname(configPath);
|
|
10497
|
+
const configBaseDir = path14.dirname(configDir);
|
|
10498
|
+
if (!existsSync8(configDir)) {
|
|
10365
10499
|
mkdirSync3(configDir, { recursive: true });
|
|
10366
10500
|
}
|
|
10367
10501
|
const serializableConfig = { ...config };
|
|
@@ -10372,6 +10506,53 @@ function saveConfig(config) {
|
|
|
10372
10506
|
}
|
|
10373
10507
|
writeFileSync3(configPath, JSON.stringify(serializableConfig, null, 2) + "\n", "utf-8");
|
|
10374
10508
|
}
|
|
10509
|
+
|
|
10510
|
+
// src/tools/index.ts
|
|
10511
|
+
import * as os5 from "os";
|
|
10512
|
+
function ensureStringArray(value) {
|
|
10513
|
+
return Array.isArray(value) ? value : [];
|
|
10514
|
+
}
|
|
10515
|
+
var z = tool.schema;
|
|
10516
|
+
var indexerMap = /* @__PURE__ */ new Map();
|
|
10517
|
+
var defaultProjectRoot = "";
|
|
10518
|
+
function initializeTools(projectRoot, config) {
|
|
10519
|
+
defaultProjectRoot = projectRoot;
|
|
10520
|
+
const indexer = new Indexer(projectRoot, config);
|
|
10521
|
+
indexerMap.set(projectRoot, indexer);
|
|
10522
|
+
}
|
|
10523
|
+
function refreshIndexerForDirectory(projectRoot) {
|
|
10524
|
+
if (!projectRoot) {
|
|
10525
|
+
throw new Error("Codebase index tools not initialized. Plugin may not be loaded correctly.");
|
|
10526
|
+
}
|
|
10527
|
+
const indexer = new Indexer(projectRoot, parseConfig(loadRuntimeConfig(projectRoot)));
|
|
10528
|
+
indexerMap.set(projectRoot, indexer);
|
|
10529
|
+
}
|
|
10530
|
+
function shouldForceLocalizeProjectIndex(projectRoot) {
|
|
10531
|
+
const currentConfig = parseConfig(loadRuntimeConfig(projectRoot));
|
|
10532
|
+
if (currentConfig.scope !== "project") {
|
|
10533
|
+
return false;
|
|
10534
|
+
}
|
|
10535
|
+
const localIndexPath = path15.join(projectRoot, ".opencode", "index");
|
|
10536
|
+
const mainRepoRoot = resolveWorktreeMainRepoRoot(projectRoot);
|
|
10537
|
+
if (!mainRepoRoot) {
|
|
10538
|
+
return false;
|
|
10539
|
+
}
|
|
10540
|
+
const inheritedIndexPath = path15.join(mainRepoRoot, ".opencode", "index");
|
|
10541
|
+
return !existsSync9(localIndexPath) && existsSync9(inheritedIndexPath);
|
|
10542
|
+
}
|
|
10543
|
+
function getIndexerForProject(directory) {
|
|
10544
|
+
const projectRoot = directory || defaultProjectRoot;
|
|
10545
|
+
if (!projectRoot) {
|
|
10546
|
+
throw new Error("Codebase index tools not initialized. Plugin may not be loaded correctly.");
|
|
10547
|
+
}
|
|
10548
|
+
let indexer = indexerMap.get(projectRoot);
|
|
10549
|
+
if (!indexer) {
|
|
10550
|
+
const config = parseConfig(loadRuntimeConfig(projectRoot));
|
|
10551
|
+
indexer = new Indexer(projectRoot, config);
|
|
10552
|
+
indexerMap.set(projectRoot, indexer);
|
|
10553
|
+
}
|
|
10554
|
+
return indexer;
|
|
10555
|
+
}
|
|
10375
10556
|
var codebase_peek = tool({
|
|
10376
10557
|
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.",
|
|
10377
10558
|
args: {
|
|
@@ -10381,8 +10562,8 @@ var codebase_peek = tool({
|
|
|
10381
10562
|
directory: z.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
|
|
10382
10563
|
chunkType: z.enum(["function", "class", "method", "interface", "type", "enum", "struct", "impl", "trait", "module", "other"]).optional().describe("Filter by code chunk type")
|
|
10383
10564
|
},
|
|
10384
|
-
async execute(args) {
|
|
10385
|
-
const indexer =
|
|
10565
|
+
async execute(args, context) {
|
|
10566
|
+
const indexer = getIndexerForProject(context?.worktree);
|
|
10386
10567
|
const results = await indexer.search(args.query, args.limit ?? 10, {
|
|
10387
10568
|
fileType: args.fileType,
|
|
10388
10569
|
directory: args.directory,
|
|
@@ -10400,16 +10581,17 @@ var index_codebase = tool({
|
|
|
10400
10581
|
verbose: z.boolean().optional().default(false).describe("Show detailed info about skipped files and parsing failures")
|
|
10401
10582
|
},
|
|
10402
10583
|
async execute(args, context) {
|
|
10403
|
-
|
|
10584
|
+
const projectRoot = context?.worktree || defaultProjectRoot;
|
|
10585
|
+
let indexer = getIndexerForProject(projectRoot);
|
|
10404
10586
|
if (args.estimateOnly) {
|
|
10405
10587
|
const estimate = await indexer.estimateCost();
|
|
10406
10588
|
return formatCostEstimate(estimate);
|
|
10407
10589
|
}
|
|
10408
10590
|
if (args.force) {
|
|
10409
|
-
if (shouldForceLocalizeProjectIndex()) {
|
|
10410
|
-
materializeLocalProjectConfig(
|
|
10411
|
-
|
|
10412
|
-
indexer =
|
|
10591
|
+
if (shouldForceLocalizeProjectIndex(projectRoot)) {
|
|
10592
|
+
materializeLocalProjectConfig(projectRoot, loadProjectConfigLayer(projectRoot));
|
|
10593
|
+
refreshIndexerForDirectory(projectRoot);
|
|
10594
|
+
indexer = getIndexerForProject(projectRoot);
|
|
10413
10595
|
}
|
|
10414
10596
|
await indexer.clearIndex();
|
|
10415
10597
|
}
|
|
@@ -10432,8 +10614,8 @@ var index_codebase = tool({
|
|
|
10432
10614
|
var index_status = tool({
|
|
10433
10615
|
description: "Check the status of the codebase index. Shows whether the codebase is indexed, how many chunks are stored, and the embedding provider being used.",
|
|
10434
10616
|
args: {},
|
|
10435
|
-
async execute() {
|
|
10436
|
-
const indexer =
|
|
10617
|
+
async execute(_args, context) {
|
|
10618
|
+
const indexer = getIndexerForProject(context?.worktree);
|
|
10437
10619
|
const status = await indexer.getStatus();
|
|
10438
10620
|
return formatStatus(status);
|
|
10439
10621
|
}
|
|
@@ -10441,8 +10623,8 @@ var index_status = tool({
|
|
|
10441
10623
|
var index_health_check = tool({
|
|
10442
10624
|
description: "Check index health and remove stale entries from deleted files. Run this to clean up the index after files have been deleted.",
|
|
10443
10625
|
args: {},
|
|
10444
|
-
async execute() {
|
|
10445
|
-
const indexer =
|
|
10626
|
+
async execute(_args, context) {
|
|
10627
|
+
const indexer = getIndexerForProject(context?.worktree);
|
|
10446
10628
|
const result = await indexer.healthCheck();
|
|
10447
10629
|
return formatHealthCheck(result);
|
|
10448
10630
|
}
|
|
@@ -10450,8 +10632,8 @@ var index_health_check = tool({
|
|
|
10450
10632
|
var index_metrics = tool({
|
|
10451
10633
|
description: "Get metrics and performance statistics for the codebase index. Shows indexing stats, search timings, cache hit rates, and API usage. Requires debug.enabled=true and debug.metrics=true in config.",
|
|
10452
10634
|
args: {},
|
|
10453
|
-
async execute() {
|
|
10454
|
-
const indexer =
|
|
10635
|
+
async execute(_args, context) {
|
|
10636
|
+
const indexer = getIndexerForProject(context?.worktree);
|
|
10455
10637
|
const logger = indexer.getLogger();
|
|
10456
10638
|
if (!logger.isEnabled()) {
|
|
10457
10639
|
return 'Debug mode is disabled. Enable it in your config:\n\n```json\n{\n "debug": {\n "enabled": true,\n "metrics": true\n }\n}\n```';
|
|
@@ -10469,8 +10651,8 @@ var index_logs = tool({
|
|
|
10469
10651
|
category: z.enum(["search", "embedding", "cache", "gc", "branch", "general"]).optional().describe("Filter by log category"),
|
|
10470
10652
|
level: z.enum(["error", "warn", "info", "debug"]).optional().describe("Filter by minimum log level")
|
|
10471
10653
|
},
|
|
10472
|
-
async execute(args) {
|
|
10473
|
-
const indexer =
|
|
10654
|
+
async execute(args, context) {
|
|
10655
|
+
const indexer = getIndexerForProject(context?.worktree);
|
|
10474
10656
|
const logger = indexer.getLogger();
|
|
10475
10657
|
if (!logger.isEnabled()) {
|
|
10476
10658
|
return 'Debug mode is disabled. Enable it in your config:\n\n```json\n{\n "debug": {\n "enabled": true\n }\n}\n```';
|
|
@@ -10496,8 +10678,8 @@ var find_similar = tool({
|
|
|
10496
10678
|
chunkType: z.enum(["function", "class", "method", "interface", "type", "enum", "struct", "impl", "trait", "module", "other"]).optional().describe("Filter by code chunk type"),
|
|
10497
10679
|
excludeFile: z.string().optional().describe("Exclude results from this file path (useful when searching for duplicates of code from a specific file)")
|
|
10498
10680
|
},
|
|
10499
|
-
async execute(args) {
|
|
10500
|
-
const indexer =
|
|
10681
|
+
async execute(args, context) {
|
|
10682
|
+
const indexer = getIndexerForProject(context?.worktree);
|
|
10501
10683
|
const results = await indexer.findSimilar(args.code, args.limit, {
|
|
10502
10684
|
fileType: args.fileType,
|
|
10503
10685
|
directory: args.directory,
|
|
@@ -10520,8 +10702,8 @@ var codebase_search = tool({
|
|
|
10520
10702
|
chunkType: z.enum(["function", "class", "method", "interface", "type", "enum", "struct", "impl", "trait", "module", "other"]).optional().describe("Filter by code chunk type"),
|
|
10521
10703
|
contextLines: z.number().optional().describe("Number of extra lines to include before/after each match (default: 0)")
|
|
10522
10704
|
},
|
|
10523
|
-
async execute(args) {
|
|
10524
|
-
const indexer =
|
|
10705
|
+
async execute(args, context) {
|
|
10706
|
+
const indexer = getIndexerForProject(context?.worktree);
|
|
10525
10707
|
const results = await indexer.search(args.query, args.limit ?? 5, {
|
|
10526
10708
|
fileType: args.fileType,
|
|
10527
10709
|
directory: args.directory,
|
|
@@ -10542,8 +10724,8 @@ var implementation_lookup = tool({
|
|
|
10542
10724
|
fileType: z.string().optional().describe("Filter by file extension (e.g., 'ts', 'py')"),
|
|
10543
10725
|
directory: z.string().optional().describe("Filter by directory path (e.g., 'src/utils')")
|
|
10544
10726
|
},
|
|
10545
|
-
async execute(args) {
|
|
10546
|
-
const indexer =
|
|
10727
|
+
async execute(args, context) {
|
|
10728
|
+
const indexer = getIndexerForProject(context?.worktree);
|
|
10547
10729
|
const results = await indexer.search(args.query, args.limit ?? 5, {
|
|
10548
10730
|
fileType: args.fileType,
|
|
10549
10731
|
directory: args.directory,
|
|
@@ -10559,8 +10741,8 @@ var call_graph = tool({
|
|
|
10559
10741
|
direction: z.enum(["callers", "callees"]).default("callers").describe("Direction: 'callers' finds who calls this function, 'callees' finds what this function calls"),
|
|
10560
10742
|
symbolId: z.string().optional().describe("Symbol ID (required for 'callees' direction, returned by previous call_graph queries)")
|
|
10561
10743
|
},
|
|
10562
|
-
async execute(args) {
|
|
10563
|
-
const indexer =
|
|
10744
|
+
async execute(args, context) {
|
|
10745
|
+
const indexer = getIndexerForProject(context?.worktree);
|
|
10564
10746
|
if (args.direction === "callees") {
|
|
10565
10747
|
if (!args.symbolId) {
|
|
10566
10748
|
return "Error: 'symbolId' is required when direction is 'callees'. First use direction='callers' to find the symbol ID.";
|
|
@@ -10589,38 +10771,67 @@ var add_knowledge_base = tool({
|
|
|
10589
10771
|
args: {
|
|
10590
10772
|
path: z.string().describe("Path to the folder to add as a knowledge base (absolute or relative to project root)")
|
|
10591
10773
|
},
|
|
10592
|
-
async execute(args) {
|
|
10774
|
+
async execute(args, context) {
|
|
10775
|
+
const projectRoot = context?.worktree || defaultProjectRoot;
|
|
10593
10776
|
const inputPath = args.path.trim();
|
|
10594
|
-
const
|
|
10595
|
-
|
|
10596
|
-
|
|
10777
|
+
const normalizedPath = path15.resolve(
|
|
10778
|
+
path15.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, projectRoot)
|
|
10779
|
+
);
|
|
10780
|
+
if (!existsSync9(normalizedPath)) {
|
|
10781
|
+
return `Error: Directory does not exist: ${normalizedPath}`;
|
|
10782
|
+
}
|
|
10783
|
+
let realPath;
|
|
10784
|
+
try {
|
|
10785
|
+
realPath = realpathSync(normalizedPath);
|
|
10786
|
+
} catch {
|
|
10787
|
+
return `Error: Cannot resolve path: ${normalizedPath}`;
|
|
10788
|
+
}
|
|
10789
|
+
const blockedPrefixes = [
|
|
10790
|
+
"/etc",
|
|
10791
|
+
"/proc",
|
|
10792
|
+
"/sys",
|
|
10793
|
+
"/dev",
|
|
10794
|
+
"/boot",
|
|
10795
|
+
"/root",
|
|
10796
|
+
"/var/run",
|
|
10797
|
+
"/var/log"
|
|
10798
|
+
];
|
|
10799
|
+
const homeDir = os5.homedir();
|
|
10800
|
+
const sensitiveDotDirs = [".ssh", ".gnupg", ".aws", ".config/gcloud", ".docker", ".kube"];
|
|
10801
|
+
for (const prefix of blockedPrefixes) {
|
|
10802
|
+
if (realPath === prefix || realPath.startsWith(prefix + "/")) {
|
|
10803
|
+
return `Error: Adding system directory as knowledge base is not allowed: ${normalizedPath}`;
|
|
10804
|
+
}
|
|
10805
|
+
}
|
|
10806
|
+
for (const dotDir of sensitiveDotDirs) {
|
|
10807
|
+
const sensitiveDir = path15.join(homeDir, dotDir);
|
|
10808
|
+
if (realPath === sensitiveDir || realPath.startsWith(sensitiveDir + "/")) {
|
|
10809
|
+
return `Error: Adding sensitive directory as knowledge base is not allowed: ${normalizedPath}`;
|
|
10810
|
+
}
|
|
10597
10811
|
}
|
|
10598
10812
|
try {
|
|
10599
|
-
const stat4 =
|
|
10813
|
+
const stat4 = statSync3(normalizedPath);
|
|
10600
10814
|
if (!stat4.isDirectory()) {
|
|
10601
|
-
return `Error: Path is not a directory: ${
|
|
10815
|
+
return `Error: Path is not a directory: ${normalizedPath}`;
|
|
10602
10816
|
}
|
|
10603
10817
|
} catch (error) {
|
|
10604
|
-
return `Error: Cannot access directory: ${
|
|
10818
|
+
return `Error: Cannot access directory: ${normalizedPath} - ${error instanceof Error ? error.message : String(error)}`;
|
|
10605
10819
|
}
|
|
10606
|
-
const config = loadEditableConfig();
|
|
10607
|
-
const knowledgeBases =
|
|
10608
|
-
const
|
|
10609
|
-
const alreadyExists = knowledgeBases.some(
|
|
10610
|
-
(kb) => path9.normalize(path9.isAbsolute(kb) ? kb : path9.resolve(sharedProjectRoot, kb)) === normalizedPath
|
|
10611
|
-
);
|
|
10820
|
+
const config = loadEditableConfig(projectRoot);
|
|
10821
|
+
const knowledgeBases = ensureStringArray(config.knowledgeBases);
|
|
10822
|
+
const alreadyExists = hasMatchingKnowledgeBasePath(knowledgeBases, normalizedPath, projectRoot);
|
|
10612
10823
|
if (alreadyExists) {
|
|
10613
|
-
return `Knowledge base already configured: ${
|
|
10824
|
+
return `Knowledge base already configured: ${normalizedPath}`;
|
|
10614
10825
|
}
|
|
10615
|
-
knowledgeBases.push(
|
|
10826
|
+
knowledgeBases.push(normalizedPath);
|
|
10616
10827
|
config.knowledgeBases = knowledgeBases;
|
|
10617
|
-
saveConfig(config);
|
|
10618
|
-
|
|
10619
|
-
let result = `${
|
|
10828
|
+
saveConfig(projectRoot, config);
|
|
10829
|
+
refreshIndexerForDirectory(projectRoot);
|
|
10830
|
+
let result = `${normalizedPath}
|
|
10620
10831
|
`;
|
|
10621
10832
|
result += `Total knowledge bases: ${knowledgeBases.length}
|
|
10622
10833
|
`;
|
|
10623
|
-
result += `Config saved to: ${getConfigPath()}
|
|
10834
|
+
result += `Config saved to: ${getConfigPath(projectRoot)}
|
|
10624
10835
|
`;
|
|
10625
10836
|
result += `
|
|
10626
10837
|
Run /index to rebuild the index with the new knowledge base.`;
|
|
@@ -10630,9 +10841,10 @@ Run /index to rebuild the index with the new knowledge base.`;
|
|
|
10630
10841
|
var list_knowledge_bases = tool({
|
|
10631
10842
|
description: "List all configured knowledge base folders that are indexed alongside the main project.",
|
|
10632
10843
|
args: {},
|
|
10633
|
-
async execute() {
|
|
10634
|
-
const
|
|
10635
|
-
const
|
|
10844
|
+
async execute(_args, context) {
|
|
10845
|
+
const projectRoot = context?.worktree || defaultProjectRoot;
|
|
10846
|
+
const config = loadRuntimeConfig(projectRoot);
|
|
10847
|
+
const knowledgeBases = ensureStringArray(config.knowledgeBases);
|
|
10636
10848
|
if (knowledgeBases.length === 0) {
|
|
10637
10849
|
return "No knowledge bases configured. Use add_knowledge_base to add folders.";
|
|
10638
10850
|
}
|
|
@@ -10641,8 +10853,8 @@ var list_knowledge_bases = tool({
|
|
|
10641
10853
|
`;
|
|
10642
10854
|
for (let i = 0; i < knowledgeBases.length; i++) {
|
|
10643
10855
|
const kb = knowledgeBases[i];
|
|
10644
|
-
const resolvedPath =
|
|
10645
|
-
const exists =
|
|
10856
|
+
const resolvedPath = resolveKnowledgeBasePath(kb, projectRoot);
|
|
10857
|
+
const exists = existsSync9(resolvedPath);
|
|
10646
10858
|
result += `[${i + 1}] ${kb}
|
|
10647
10859
|
`;
|
|
10648
10860
|
result += ` Resolved: ${resolvedPath}
|
|
@@ -10651,7 +10863,7 @@ var list_knowledge_bases = tool({
|
|
|
10651
10863
|
`;
|
|
10652
10864
|
if (exists) {
|
|
10653
10865
|
try {
|
|
10654
|
-
const stat4 =
|
|
10866
|
+
const stat4 = statSync3(resolvedPath);
|
|
10655
10867
|
result += ` Type: ${stat4.isDirectory() ? "Directory" : "File"}
|
|
10656
10868
|
`;
|
|
10657
10869
|
} catch {
|
|
@@ -10659,7 +10871,7 @@ var list_knowledge_bases = tool({
|
|
|
10659
10871
|
}
|
|
10660
10872
|
result += "\n";
|
|
10661
10873
|
}
|
|
10662
|
-
result += `Config file: ${getConfigPath()}`;
|
|
10874
|
+
result += `Config file: ${getConfigPath(projectRoot)}`;
|
|
10663
10875
|
return result;
|
|
10664
10876
|
}
|
|
10665
10877
|
});
|
|
@@ -10668,17 +10880,15 @@ var remove_knowledge_base = tool({
|
|
|
10668
10880
|
args: {
|
|
10669
10881
|
path: z.string().describe("Path of the knowledge base to remove (must match the configured path exactly)")
|
|
10670
10882
|
},
|
|
10671
|
-
async execute(args) {
|
|
10883
|
+
async execute(args, context) {
|
|
10884
|
+
const projectRoot = context?.worktree || defaultProjectRoot;
|
|
10672
10885
|
const inputPath = args.path.trim();
|
|
10673
|
-
const config = loadEditableConfig();
|
|
10674
|
-
const knowledgeBases =
|
|
10886
|
+
const config = loadEditableConfig(projectRoot);
|
|
10887
|
+
const knowledgeBases = ensureStringArray(config.knowledgeBases);
|
|
10675
10888
|
if (knowledgeBases.length === 0) {
|
|
10676
10889
|
return "No knowledge bases configured.";
|
|
10677
10890
|
}
|
|
10678
|
-
const
|
|
10679
|
-
const index = knowledgeBases.findIndex(
|
|
10680
|
-
(kb) => path9.normalize(kb) === normalizedInput || path9.normalize(path9.isAbsolute(kb) ? kb : path9.resolve(sharedProjectRoot, kb)) === normalizedInput
|
|
10681
|
-
);
|
|
10891
|
+
const index = findKnowledgeBasePathIndex(knowledgeBases, inputPath, projectRoot);
|
|
10682
10892
|
if (index === -1) {
|
|
10683
10893
|
let result2 = `Knowledge base not found: ${inputPath}
|
|
10684
10894
|
|
|
@@ -10693,14 +10903,14 @@ var remove_knowledge_base = tool({
|
|
|
10693
10903
|
}
|
|
10694
10904
|
const removed = knowledgeBases.splice(index, 1)[0];
|
|
10695
10905
|
config.knowledgeBases = knowledgeBases;
|
|
10696
|
-
saveConfig(config);
|
|
10697
|
-
|
|
10906
|
+
saveConfig(projectRoot, config);
|
|
10907
|
+
refreshIndexerForDirectory(projectRoot);
|
|
10698
10908
|
let result = `Removed: ${removed}
|
|
10699
10909
|
|
|
10700
10910
|
`;
|
|
10701
10911
|
result += `Remaining knowledge bases: ${knowledgeBases.length}
|
|
10702
10912
|
`;
|
|
10703
|
-
result += `Config saved to: ${getConfigPath()}
|
|
10913
|
+
result += `Config saved to: ${getConfigPath(projectRoot)}
|
|
10704
10914
|
`;
|
|
10705
10915
|
result += `
|
|
10706
10916
|
Run /index to rebuild the index without the removed knowledge base.`;
|
|
@@ -10709,8 +10919,8 @@ Run /index to rebuild the index without the removed knowledge base.`;
|
|
|
10709
10919
|
});
|
|
10710
10920
|
|
|
10711
10921
|
// src/commands/loader.ts
|
|
10712
|
-
import { existsSync as
|
|
10713
|
-
import * as
|
|
10922
|
+
import { existsSync as existsSync10, readdirSync as readdirSync2, readFileSync as readFileSync7 } from "fs";
|
|
10923
|
+
import * as path16 from "path";
|
|
10714
10924
|
function parseFrontmatter(content) {
|
|
10715
10925
|
const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
|
|
10716
10926
|
const match = content.match(frontmatterRegex);
|
|
@@ -10731,15 +10941,21 @@ function parseFrontmatter(content) {
|
|
|
10731
10941
|
}
|
|
10732
10942
|
function loadCommandsFromDirectory(commandsDir) {
|
|
10733
10943
|
const commands = /* @__PURE__ */ new Map();
|
|
10734
|
-
if (!
|
|
10944
|
+
if (!existsSync10(commandsDir)) {
|
|
10735
10945
|
return commands;
|
|
10736
10946
|
}
|
|
10737
10947
|
const files = readdirSync2(commandsDir).filter((f) => f.endsWith(".md"));
|
|
10738
10948
|
for (const file of files) {
|
|
10739
|
-
const filePath =
|
|
10740
|
-
|
|
10949
|
+
const filePath = path16.join(commandsDir, file);
|
|
10950
|
+
let content;
|
|
10951
|
+
try {
|
|
10952
|
+
content = readFileSync7(filePath, "utf-8");
|
|
10953
|
+
} catch (error) {
|
|
10954
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
10955
|
+
throw new Error(`Failed to load command file ${filePath}: ${message}`);
|
|
10956
|
+
}
|
|
10741
10957
|
const { frontmatter, body } = parseFrontmatter(content);
|
|
10742
|
-
const name =
|
|
10958
|
+
const name = path16.basename(file, ".md");
|
|
10743
10959
|
const description = frontmatter.description || `Run the ${name} command`;
|
|
10744
10960
|
commands.set(name, {
|
|
10745
10961
|
description,
|
|
@@ -10749,7 +10965,7 @@ function loadCommandsFromDirectory(commandsDir) {
|
|
|
10749
10965
|
return commands;
|
|
10750
10966
|
}
|
|
10751
10967
|
|
|
10752
|
-
// src/routing-hints.ts
|
|
10968
|
+
// src/routing-hints-patterns.ts
|
|
10753
10969
|
var EXTERNAL_HINTS = [
|
|
10754
10970
|
"docs",
|
|
10755
10971
|
"documentation",
|
|
@@ -10840,6 +11056,8 @@ var SNAKE_PATTERN = /\b[a-z0-9]+_[a-z0-9_]+\b/g;
|
|
|
10840
11056
|
var KEBAB_PATTERN = /\b[a-z0-9]+-[a-z0-9-]+\b/g;
|
|
10841
11057
|
var BACKTICK_IDENTIFIER_PATTERN = /`([^`]+)`/g;
|
|
10842
11058
|
var BACKTICK_IDENTIFIER_PRESENCE_PATTERN = /`([^`]+)`/;
|
|
11059
|
+
var DOUBLE_QUOTED_PATTERN = /"[^"]+"/;
|
|
11060
|
+
var SINGLE_QUOTED_PATTERN = /'[^']+'/;
|
|
10843
11061
|
function normalizeText(text) {
|
|
10844
11062
|
return text.trim().replace(/\s+/g, " ");
|
|
10845
11063
|
}
|
|
@@ -10852,6 +11070,21 @@ function countWords(text) {
|
|
|
10852
11070
|
}
|
|
10853
11071
|
return text.split(/\s+/).filter(Boolean).length;
|
|
10854
11072
|
}
|
|
11073
|
+
function isExternalLookup(text) {
|
|
11074
|
+
return URL_PATTERN.test(text) || includesHint(text, EXTERNAL_HINTS);
|
|
11075
|
+
}
|
|
11076
|
+
function hasConceptualDiscoveryHint(text) {
|
|
11077
|
+
return includesHint(text, CONCEPTUAL_DISCOVERY_HINTS);
|
|
11078
|
+
}
|
|
11079
|
+
function hasDefinitionHint(text) {
|
|
11080
|
+
return includesHint(text, DEFINITION_HINTS);
|
|
11081
|
+
}
|
|
11082
|
+
function hasExactMatchHint(text) {
|
|
11083
|
+
return includesHint(text, EXACT_MATCH_HINTS);
|
|
11084
|
+
}
|
|
11085
|
+
function hasNonDiscoveryHint(text) {
|
|
11086
|
+
return includesHint(text, NON_DISCOVERY_HINTS);
|
|
11087
|
+
}
|
|
10855
11088
|
function hasIdentifierShape(text) {
|
|
10856
11089
|
const matches = [
|
|
10857
11090
|
...text.match(CAMEL_OR_PASCAL_PATTERN) ?? [],
|
|
@@ -10867,11 +11100,13 @@ function hasIdentifierShape(text) {
|
|
|
10867
11100
|
});
|
|
10868
11101
|
}
|
|
10869
11102
|
function containsQuotedIdentifier(text) {
|
|
10870
|
-
return BACKTICK_IDENTIFIER_PRESENCE_PATTERN.test(text) ||
|
|
11103
|
+
return BACKTICK_IDENTIFIER_PRESENCE_PATTERN.test(text) || DOUBLE_QUOTED_PATTERN.test(text) || SINGLE_QUOTED_PATTERN.test(text);
|
|
10871
11104
|
}
|
|
10872
11105
|
function looksLikeDirectPath(text) {
|
|
10873
11106
|
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);
|
|
10874
11107
|
}
|
|
11108
|
+
|
|
11109
|
+
// src/routing-hints.ts
|
|
10875
11110
|
function extractUserText(parts) {
|
|
10876
11111
|
return normalizeText(
|
|
10877
11112
|
parts.filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text ?? "").join(" ")
|
|
@@ -10887,21 +11122,21 @@ function assessRoutingIntent(text) {
|
|
|
10887
11122
|
reason: "empty_text"
|
|
10888
11123
|
};
|
|
10889
11124
|
}
|
|
10890
|
-
if (
|
|
11125
|
+
if (isExternalLookup(lowered)) {
|
|
10891
11126
|
return {
|
|
10892
11127
|
intent: "external",
|
|
10893
11128
|
text: normalizedText,
|
|
10894
11129
|
reason: "external_lookup"
|
|
10895
11130
|
};
|
|
10896
11131
|
}
|
|
10897
|
-
const
|
|
10898
|
-
const
|
|
10899
|
-
const
|
|
10900
|
-
const
|
|
11132
|
+
const matchedConceptualHint = hasConceptualDiscoveryHint(lowered);
|
|
11133
|
+
const matchedDefinitionHint = hasDefinitionHint(lowered);
|
|
11134
|
+
const matchedExactMatchHint = hasExactMatchHint(lowered);
|
|
11135
|
+
const matchedNonDiscoveryHint = hasNonDiscoveryHint(lowered);
|
|
10901
11136
|
const hasIdentifier = hasIdentifierShape(normalizedText);
|
|
10902
11137
|
const hasQuotedIdentifier = containsQuotedIdentifier(normalizedText);
|
|
10903
11138
|
const shortQuery = countWords(lowered) <= 10;
|
|
10904
|
-
if (
|
|
11139
|
+
if (matchedNonDiscoveryHint && !matchedConceptualHint) {
|
|
10905
11140
|
return {
|
|
10906
11141
|
intent: "other",
|
|
10907
11142
|
text: normalizedText,
|
|
@@ -10915,21 +11150,21 @@ function assessRoutingIntent(text) {
|
|
|
10915
11150
|
reason: "direct_path_reference"
|
|
10916
11151
|
};
|
|
10917
11152
|
}
|
|
10918
|
-
if ((
|
|
11153
|
+
if ((matchedDefinitionHint || lowered.includes("where is") || lowered.includes("where are")) && (lowered.includes("defined") || lowered.includes("definition"))) {
|
|
10919
11154
|
return {
|
|
10920
11155
|
intent: "definition_lookup",
|
|
10921
11156
|
text: normalizedText,
|
|
10922
11157
|
reason: "definition_lookup_request"
|
|
10923
11158
|
};
|
|
10924
11159
|
}
|
|
10925
|
-
if ((
|
|
11160
|
+
if ((matchedExactMatchHint || hasQuotedIdentifier || hasIdentifier) && !matchedConceptualHint && shortQuery) {
|
|
10926
11161
|
return {
|
|
10927
11162
|
intent: "exact_identifier",
|
|
10928
11163
|
text: normalizedText,
|
|
10929
|
-
reason:
|
|
11164
|
+
reason: matchedExactMatchHint || hasQuotedIdentifier ? "exact_match_request" : "identifier_shaped_query"
|
|
10930
11165
|
};
|
|
10931
11166
|
}
|
|
10932
|
-
if (
|
|
11167
|
+
if (matchedConceptualHint) {
|
|
10933
11168
|
return {
|
|
10934
11169
|
intent: "local_conceptual",
|
|
10935
11170
|
text: normalizedText,
|
|
@@ -11018,33 +11253,55 @@ var RoutingHintController = class {
|
|
|
11018
11253
|
};
|
|
11019
11254
|
|
|
11020
11255
|
// src/index.ts
|
|
11021
|
-
var
|
|
11022
|
-
function replaceActiveWatcher(nextWatcher) {
|
|
11023
|
-
|
|
11024
|
-
|
|
11256
|
+
var activeWatchers = /* @__PURE__ */ new Map();
|
|
11257
|
+
function replaceActiveWatcher(projectRoot, nextWatcher) {
|
|
11258
|
+
const existing = activeWatchers.get(projectRoot);
|
|
11259
|
+
if (existing) {
|
|
11260
|
+
existing.stop();
|
|
11261
|
+
activeWatchers.delete(projectRoot);
|
|
11262
|
+
}
|
|
11263
|
+
if (nextWatcher) {
|
|
11264
|
+
activeWatchers.set(projectRoot, nextWatcher);
|
|
11265
|
+
}
|
|
11025
11266
|
}
|
|
11026
11267
|
function getCommandsDir() {
|
|
11027
11268
|
let currentDir = process.cwd();
|
|
11028
11269
|
if (typeof import.meta !== "undefined" && import.meta.url) {
|
|
11029
|
-
currentDir =
|
|
11270
|
+
currentDir = path17.dirname(fileURLToPath2(import.meta.url));
|
|
11271
|
+
}
|
|
11272
|
+
return path17.join(currentDir, "..", "commands");
|
|
11273
|
+
}
|
|
11274
|
+
function appendRoutingHints(output, hints, preferredRole) {
|
|
11275
|
+
const preferredBucket = preferredRole === "developer" ? output.developer : output.system;
|
|
11276
|
+
if (Array.isArray(preferredBucket)) {
|
|
11277
|
+
preferredBucket.push(...hints);
|
|
11278
|
+
return;
|
|
11279
|
+
}
|
|
11280
|
+
if (Array.isArray(output.system)) {
|
|
11281
|
+
output.system.push(...hints);
|
|
11030
11282
|
}
|
|
11031
|
-
return path11.join(currentDir, "..", "commands");
|
|
11032
11283
|
}
|
|
11033
|
-
var plugin = async ({ directory }) => {
|
|
11284
|
+
var plugin = async ({ directory, worktree }) => {
|
|
11034
11285
|
try {
|
|
11035
|
-
const projectRoot = directory;
|
|
11286
|
+
const projectRoot = worktree || directory;
|
|
11036
11287
|
const rawConfig = loadMergedConfig(projectRoot);
|
|
11037
11288
|
const config = parseConfig(rawConfig);
|
|
11038
11289
|
initializeTools(projectRoot, config);
|
|
11039
|
-
const
|
|
11040
|
-
const routingHints = config.search.routingHints ? new RoutingHintController(() =>
|
|
11041
|
-
const
|
|
11042
|
-
|
|
11290
|
+
const getProjectIndexer = () => getIndexerForProject(projectRoot);
|
|
11291
|
+
const routingHints = config.search.routingHints ? new RoutingHintController(() => getProjectIndexer().getStatus()) : null;
|
|
11292
|
+
const isHomeDir = path17.resolve(projectRoot) === path17.resolve(os6.homedir());
|
|
11293
|
+
const isValidProject = !isHomeDir && (!config.indexing.requireProjectMarker || hasProjectMarker(projectRoot));
|
|
11294
|
+
if (isHomeDir) {
|
|
11295
|
+
console.warn(
|
|
11296
|
+
`[codebase-index] Refusing to watch or index home directory "${projectRoot}". Open a specific project directory instead.`
|
|
11297
|
+
);
|
|
11298
|
+
} else if (!isValidProject) {
|
|
11043
11299
|
console.warn(
|
|
11044
11300
|
`[codebase-index] Skipping file watching and auto-indexing: no project marker found in "${projectRoot}". Set "indexing.requireProjectMarker": false in config to override.`
|
|
11045
11301
|
);
|
|
11046
11302
|
}
|
|
11047
11303
|
if (config.indexing.autoIndex && isValidProject) {
|
|
11304
|
+
const indexer = getProjectIndexer();
|
|
11048
11305
|
indexer.initialize().then(() => {
|
|
11049
11306
|
indexer.index().catch(() => {
|
|
11050
11307
|
});
|
|
@@ -11052,9 +11309,9 @@ var plugin = async ({ directory }) => {
|
|
|
11052
11309
|
});
|
|
11053
11310
|
}
|
|
11054
11311
|
if (config.indexing.watchFiles && isValidProject) {
|
|
11055
|
-
replaceActiveWatcher(createWatcherWithIndexer(
|
|
11312
|
+
replaceActiveWatcher(projectRoot, createWatcherWithIndexer(getProjectIndexer, projectRoot, config));
|
|
11056
11313
|
} else {
|
|
11057
|
-
replaceActiveWatcher(null);
|
|
11314
|
+
replaceActiveWatcher(projectRoot, null);
|
|
11058
11315
|
}
|
|
11059
11316
|
return {
|
|
11060
11317
|
tool: {
|
|
@@ -11076,8 +11333,18 @@ var plugin = async ({ directory }) => {
|
|
|
11076
11333
|
routingHints?.observeUserMessage(input.sessionID, output.parts);
|
|
11077
11334
|
},
|
|
11078
11335
|
async "experimental.chat.system.transform"(input, output) {
|
|
11336
|
+
if (config.search.routingHintRole !== "system") {
|
|
11337
|
+
return;
|
|
11338
|
+
}
|
|
11339
|
+
const hints = await routingHints?.getSystemHints(input.sessionID) ?? [];
|
|
11340
|
+
appendRoutingHints(output, hints, "system");
|
|
11341
|
+
},
|
|
11342
|
+
async "experimental.chat.developer.transform"(input, output) {
|
|
11343
|
+
if (config.search.routingHintRole !== "developer") {
|
|
11344
|
+
return;
|
|
11345
|
+
}
|
|
11079
11346
|
const hints = await routingHints?.getSystemHints(input.sessionID) ?? [];
|
|
11080
|
-
output
|
|
11347
|
+
appendRoutingHints(output, hints, "developer");
|
|
11081
11348
|
},
|
|
11082
11349
|
async "tool.execute.after"(input) {
|
|
11083
11350
|
routingHints?.markToolUsed(input.sessionID, input.tool);
|
|
@@ -11091,8 +11358,8 @@ var plugin = async ({ directory }) => {
|
|
|
11091
11358
|
}
|
|
11092
11359
|
}
|
|
11093
11360
|
};
|
|
11094
|
-
} catch
|
|
11095
|
-
console.error("[codebase-index] Failed to initialize plugin
|
|
11361
|
+
} catch {
|
|
11362
|
+
console.error("[codebase-index] Failed to initialize plugin (check config and network)");
|
|
11096
11363
|
return {
|
|
11097
11364
|
tool: void 0,
|
|
11098
11365
|
async config() {
|