opencode-codebase-index 0.2.5 → 0.3.2
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 +176 -1
- package/commands/find.md +17 -5
- package/commands/index.md +16 -6
- package/commands/search.md +18 -3
- package/commands/status.md +15 -0
- package/dist/index.cjs +971 -286
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +970 -286
- package/dist/index.js.map +1 -1
- package/native/codebase-index-native.darwin-arm64.node +0 -0
- package/native/codebase-index-native.darwin-x64.node +0 -0
- package/native/codebase-index-native.linux-arm64-gnu.node +0 -0
- package/native/codebase-index-native.linux-x64-gnu.node +0 -0
- package/native/codebase-index-native.win32-x64-msvc.node +0 -0
- package/package.json +3 -1
- package/skill/SKILL.md +116 -1
package/dist/index.cjs
CHANGED
|
@@ -491,7 +491,7 @@ var require_ignore = __commonJS({
|
|
|
491
491
|
// path matching.
|
|
492
492
|
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
493
493
|
// @returns {TestResult} true if a file is ignored
|
|
494
|
-
test(
|
|
494
|
+
test(path9, checkUnignored, mode) {
|
|
495
495
|
let ignored = false;
|
|
496
496
|
let unignored = false;
|
|
497
497
|
let matchedRule;
|
|
@@ -500,7 +500,7 @@ var require_ignore = __commonJS({
|
|
|
500
500
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
501
501
|
return;
|
|
502
502
|
}
|
|
503
|
-
const matched = rule[mode].test(
|
|
503
|
+
const matched = rule[mode].test(path9);
|
|
504
504
|
if (!matched) {
|
|
505
505
|
return;
|
|
506
506
|
}
|
|
@@ -521,17 +521,17 @@ var require_ignore = __commonJS({
|
|
|
521
521
|
var throwError = (message, Ctor) => {
|
|
522
522
|
throw new Ctor(message);
|
|
523
523
|
};
|
|
524
|
-
var checkPath = (
|
|
525
|
-
if (!isString(
|
|
524
|
+
var checkPath = (path9, originalPath, doThrow) => {
|
|
525
|
+
if (!isString(path9)) {
|
|
526
526
|
return doThrow(
|
|
527
527
|
`path must be a string, but got \`${originalPath}\``,
|
|
528
528
|
TypeError
|
|
529
529
|
);
|
|
530
530
|
}
|
|
531
|
-
if (!
|
|
531
|
+
if (!path9) {
|
|
532
532
|
return doThrow(`path must not be empty`, TypeError);
|
|
533
533
|
}
|
|
534
|
-
if (checkPath.isNotRelative(
|
|
534
|
+
if (checkPath.isNotRelative(path9)) {
|
|
535
535
|
const r = "`path.relative()`d";
|
|
536
536
|
return doThrow(
|
|
537
537
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -540,7 +540,7 @@ var require_ignore = __commonJS({
|
|
|
540
540
|
}
|
|
541
541
|
return true;
|
|
542
542
|
};
|
|
543
|
-
var isNotRelative = (
|
|
543
|
+
var isNotRelative = (path9) => REGEX_TEST_INVALID_PATH.test(path9);
|
|
544
544
|
checkPath.isNotRelative = isNotRelative;
|
|
545
545
|
checkPath.convert = (p) => p;
|
|
546
546
|
var Ignore2 = class {
|
|
@@ -570,19 +570,19 @@ var require_ignore = __commonJS({
|
|
|
570
570
|
}
|
|
571
571
|
// @returns {TestResult}
|
|
572
572
|
_test(originalPath, cache, checkUnignored, slices) {
|
|
573
|
-
const
|
|
573
|
+
const path9 = originalPath && checkPath.convert(originalPath);
|
|
574
574
|
checkPath(
|
|
575
|
-
|
|
575
|
+
path9,
|
|
576
576
|
originalPath,
|
|
577
577
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
578
578
|
);
|
|
579
|
-
return this._t(
|
|
579
|
+
return this._t(path9, cache, checkUnignored, slices);
|
|
580
580
|
}
|
|
581
|
-
checkIgnore(
|
|
582
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
583
|
-
return this.test(
|
|
581
|
+
checkIgnore(path9) {
|
|
582
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path9)) {
|
|
583
|
+
return this.test(path9);
|
|
584
584
|
}
|
|
585
|
-
const slices =
|
|
585
|
+
const slices = path9.split(SLASH2).filter(Boolean);
|
|
586
586
|
slices.pop();
|
|
587
587
|
if (slices.length) {
|
|
588
588
|
const parent = this._t(
|
|
@@ -595,18 +595,18 @@ var require_ignore = __commonJS({
|
|
|
595
595
|
return parent;
|
|
596
596
|
}
|
|
597
597
|
}
|
|
598
|
-
return this._rules.test(
|
|
598
|
+
return this._rules.test(path9, false, MODE_CHECK_IGNORE);
|
|
599
599
|
}
|
|
600
|
-
_t(
|
|
601
|
-
if (
|
|
602
|
-
return cache[
|
|
600
|
+
_t(path9, cache, checkUnignored, slices) {
|
|
601
|
+
if (path9 in cache) {
|
|
602
|
+
return cache[path9];
|
|
603
603
|
}
|
|
604
604
|
if (!slices) {
|
|
605
|
-
slices =
|
|
605
|
+
slices = path9.split(SLASH2).filter(Boolean);
|
|
606
606
|
}
|
|
607
607
|
slices.pop();
|
|
608
608
|
if (!slices.length) {
|
|
609
|
-
return cache[
|
|
609
|
+
return cache[path9] = this._rules.test(path9, checkUnignored, MODE_IGNORE);
|
|
610
610
|
}
|
|
611
611
|
const parent = this._t(
|
|
612
612
|
slices.join(SLASH2) + SLASH2,
|
|
@@ -614,29 +614,29 @@ var require_ignore = __commonJS({
|
|
|
614
614
|
checkUnignored,
|
|
615
615
|
slices
|
|
616
616
|
);
|
|
617
|
-
return cache[
|
|
617
|
+
return cache[path9] = parent.ignored ? parent : this._rules.test(path9, checkUnignored, MODE_IGNORE);
|
|
618
618
|
}
|
|
619
|
-
ignores(
|
|
620
|
-
return this._test(
|
|
619
|
+
ignores(path9) {
|
|
620
|
+
return this._test(path9, this._ignoreCache, false).ignored;
|
|
621
621
|
}
|
|
622
622
|
createFilter() {
|
|
623
|
-
return (
|
|
623
|
+
return (path9) => !this.ignores(path9);
|
|
624
624
|
}
|
|
625
625
|
filter(paths) {
|
|
626
626
|
return makeArray(paths).filter(this.createFilter());
|
|
627
627
|
}
|
|
628
628
|
// @returns {TestResult}
|
|
629
|
-
test(
|
|
630
|
-
return this._test(
|
|
629
|
+
test(path9) {
|
|
630
|
+
return this._test(path9, this._testCache, true);
|
|
631
631
|
}
|
|
632
632
|
};
|
|
633
633
|
var factory = (options) => new Ignore2(options);
|
|
634
|
-
var isPathValid = (
|
|
634
|
+
var isPathValid = (path9) => checkPath(path9 && checkPath.convert(path9), path9, RETURN_FALSE);
|
|
635
635
|
var setupWindows = () => {
|
|
636
636
|
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
637
637
|
checkPath.convert = makePosix;
|
|
638
638
|
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
639
|
-
checkPath.isNotRelative = (
|
|
639
|
+
checkPath.isNotRelative = (path9) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path9) || isNotRelative(path9);
|
|
640
640
|
};
|
|
641
641
|
if (
|
|
642
642
|
// Detect `process` so that it can run in browsers.
|
|
@@ -657,9 +657,10 @@ __export(index_exports, {
|
|
|
657
657
|
default: () => index_default
|
|
658
658
|
});
|
|
659
659
|
module.exports = __toCommonJS(index_exports);
|
|
660
|
-
var
|
|
661
|
-
var
|
|
660
|
+
var import_fs6 = require("fs");
|
|
661
|
+
var path8 = __toESM(require("path"), 1);
|
|
662
662
|
var os3 = __toESM(require("os"), 1);
|
|
663
|
+
var import_url2 = require("url");
|
|
663
664
|
|
|
664
665
|
// src/config/schema.ts
|
|
665
666
|
var DEFAULT_INCLUDE = [
|
|
@@ -697,7 +698,10 @@ function getDefaultIndexingConfig() {
|
|
|
697
698
|
maxChunksPerFile: 100,
|
|
698
699
|
semanticOnly: false,
|
|
699
700
|
retries: 3,
|
|
700
|
-
retryDelayMs: 1e3
|
|
701
|
+
retryDelayMs: 1e3,
|
|
702
|
+
autoGc: true,
|
|
703
|
+
gcIntervalDays: 7,
|
|
704
|
+
gcOrphanThreshold: 100
|
|
701
705
|
};
|
|
702
706
|
}
|
|
703
707
|
function getDefaultSearchConfig() {
|
|
@@ -709,8 +713,21 @@ function getDefaultSearchConfig() {
|
|
|
709
713
|
contextLines: 0
|
|
710
714
|
};
|
|
711
715
|
}
|
|
716
|
+
function getDefaultDebugConfig() {
|
|
717
|
+
return {
|
|
718
|
+
enabled: false,
|
|
719
|
+
logLevel: "info",
|
|
720
|
+
logSearch: true,
|
|
721
|
+
logEmbedding: true,
|
|
722
|
+
logCache: true,
|
|
723
|
+
logGc: true,
|
|
724
|
+
logBranch: true,
|
|
725
|
+
metrics: true
|
|
726
|
+
};
|
|
727
|
+
}
|
|
712
728
|
var VALID_PROVIDERS = ["auto", "github-copilot", "openai", "google", "ollama"];
|
|
713
729
|
var VALID_SCOPES = ["project", "global"];
|
|
730
|
+
var VALID_LOG_LEVELS = ["error", "warn", "info", "debug"];
|
|
714
731
|
function isValidProvider(value) {
|
|
715
732
|
return typeof value === "string" && VALID_PROVIDERS.includes(value);
|
|
716
733
|
}
|
|
@@ -720,10 +737,14 @@ function isValidScope(value) {
|
|
|
720
737
|
function isStringArray(value) {
|
|
721
738
|
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
722
739
|
}
|
|
740
|
+
function isValidLogLevel(value) {
|
|
741
|
+
return typeof value === "string" && VALID_LOG_LEVELS.includes(value);
|
|
742
|
+
}
|
|
723
743
|
function parseConfig(raw) {
|
|
724
744
|
const input = raw && typeof raw === "object" ? raw : {};
|
|
725
745
|
const defaultIndexing = getDefaultIndexingConfig();
|
|
726
746
|
const defaultSearch = getDefaultSearchConfig();
|
|
747
|
+
const defaultDebug = getDefaultDebugConfig();
|
|
727
748
|
const rawIndexing = input.indexing && typeof input.indexing === "object" ? input.indexing : {};
|
|
728
749
|
const indexing = {
|
|
729
750
|
autoIndex: typeof rawIndexing.autoIndex === "boolean" ? rawIndexing.autoIndex : defaultIndexing.autoIndex,
|
|
@@ -732,7 +753,10 @@ function parseConfig(raw) {
|
|
|
732
753
|
maxChunksPerFile: typeof rawIndexing.maxChunksPerFile === "number" ? Math.max(1, rawIndexing.maxChunksPerFile) : defaultIndexing.maxChunksPerFile,
|
|
733
754
|
semanticOnly: typeof rawIndexing.semanticOnly === "boolean" ? rawIndexing.semanticOnly : defaultIndexing.semanticOnly,
|
|
734
755
|
retries: typeof rawIndexing.retries === "number" ? rawIndexing.retries : defaultIndexing.retries,
|
|
735
|
-
retryDelayMs: typeof rawIndexing.retryDelayMs === "number" ? rawIndexing.retryDelayMs : defaultIndexing.retryDelayMs
|
|
756
|
+
retryDelayMs: typeof rawIndexing.retryDelayMs === "number" ? rawIndexing.retryDelayMs : defaultIndexing.retryDelayMs,
|
|
757
|
+
autoGc: typeof rawIndexing.autoGc === "boolean" ? rawIndexing.autoGc : defaultIndexing.autoGc,
|
|
758
|
+
gcIntervalDays: typeof rawIndexing.gcIntervalDays === "number" ? Math.max(1, rawIndexing.gcIntervalDays) : defaultIndexing.gcIntervalDays,
|
|
759
|
+
gcOrphanThreshold: typeof rawIndexing.gcOrphanThreshold === "number" ? Math.max(0, rawIndexing.gcOrphanThreshold) : defaultIndexing.gcOrphanThreshold
|
|
736
760
|
};
|
|
737
761
|
const rawSearch = input.search && typeof input.search === "object" ? input.search : {};
|
|
738
762
|
const search = {
|
|
@@ -742,6 +766,17 @@ function parseConfig(raw) {
|
|
|
742
766
|
hybridWeight: typeof rawSearch.hybridWeight === "number" ? Math.min(1, Math.max(0, rawSearch.hybridWeight)) : defaultSearch.hybridWeight,
|
|
743
767
|
contextLines: typeof rawSearch.contextLines === "number" ? Math.min(50, Math.max(0, rawSearch.contextLines)) : defaultSearch.contextLines
|
|
744
768
|
};
|
|
769
|
+
const rawDebug = input.debug && typeof input.debug === "object" ? input.debug : {};
|
|
770
|
+
const debug = {
|
|
771
|
+
enabled: typeof rawDebug.enabled === "boolean" ? rawDebug.enabled : defaultDebug.enabled,
|
|
772
|
+
logLevel: isValidLogLevel(rawDebug.logLevel) ? rawDebug.logLevel : defaultDebug.logLevel,
|
|
773
|
+
logSearch: typeof rawDebug.logSearch === "boolean" ? rawDebug.logSearch : defaultDebug.logSearch,
|
|
774
|
+
logEmbedding: typeof rawDebug.logEmbedding === "boolean" ? rawDebug.logEmbedding : defaultDebug.logEmbedding,
|
|
775
|
+
logCache: typeof rawDebug.logCache === "boolean" ? rawDebug.logCache : defaultDebug.logCache,
|
|
776
|
+
logGc: typeof rawDebug.logGc === "boolean" ? rawDebug.logGc : defaultDebug.logGc,
|
|
777
|
+
logBranch: typeof rawDebug.logBranch === "boolean" ? rawDebug.logBranch : defaultDebug.logBranch,
|
|
778
|
+
metrics: typeof rawDebug.metrics === "boolean" ? rawDebug.metrics : defaultDebug.metrics
|
|
779
|
+
};
|
|
745
780
|
return {
|
|
746
781
|
embeddingProvider: isValidProvider(input.embeddingProvider) ? input.embeddingProvider : "auto",
|
|
747
782
|
embeddingModel: typeof input.embeddingModel === "string" ? input.embeddingModel : "auto",
|
|
@@ -749,7 +784,8 @@ function parseConfig(raw) {
|
|
|
749
784
|
include: isStringArray(input.include) ? input.include : DEFAULT_INCLUDE,
|
|
750
785
|
exclude: isStringArray(input.exclude) ? input.exclude : DEFAULT_EXCLUDE,
|
|
751
786
|
indexing,
|
|
752
|
-
search
|
|
787
|
+
search,
|
|
788
|
+
debug
|
|
753
789
|
};
|
|
754
790
|
}
|
|
755
791
|
var EMBEDDING_MODELS = {
|
|
@@ -814,6 +850,7 @@ function getDefaultModelForProvider(provider) {
|
|
|
814
850
|
// src/indexer/index.ts
|
|
815
851
|
var import_fs4 = require("fs");
|
|
816
852
|
var path5 = __toESM(require("path"), 1);
|
|
853
|
+
var import_perf_hooks = require("perf_hooks");
|
|
817
854
|
|
|
818
855
|
// node_modules/eventemitter3/index.mjs
|
|
819
856
|
var import_index = __toESM(require_eventemitter3(), 1);
|
|
@@ -2071,34 +2108,36 @@ var GoogleEmbeddingProvider = class {
|
|
|
2071
2108
|
};
|
|
2072
2109
|
}
|
|
2073
2110
|
async embedBatch(texts) {
|
|
2074
|
-
const
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
}
|
|
2088
|
-
}
|
|
2111
|
+
const results = await Promise.all(
|
|
2112
|
+
texts.map(async (text) => {
|
|
2113
|
+
const response = await fetch(
|
|
2114
|
+
`${this.credentials.baseUrl}/models/${this.modelInfo.model}:embedContent?key=${this.credentials.apiKey}`,
|
|
2115
|
+
{
|
|
2116
|
+
method: "POST",
|
|
2117
|
+
headers: {
|
|
2118
|
+
"Content-Type": "application/json"
|
|
2119
|
+
},
|
|
2120
|
+
body: JSON.stringify({
|
|
2121
|
+
content: {
|
|
2122
|
+
parts: [{ text }]
|
|
2123
|
+
}
|
|
2124
|
+
})
|
|
2125
|
+
}
|
|
2126
|
+
);
|
|
2127
|
+
if (!response.ok) {
|
|
2128
|
+
const error = await response.text();
|
|
2129
|
+
throw new Error(`Google embedding API error: ${response.status} - ${error}`);
|
|
2089
2130
|
}
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
totalTokens += Math.ceil(text.length / 4);
|
|
2098
|
-
}
|
|
2131
|
+
const data = await response.json();
|
|
2132
|
+
return {
|
|
2133
|
+
embedding: data.embedding.values,
|
|
2134
|
+
tokensUsed: Math.ceil(text.length / 4)
|
|
2135
|
+
};
|
|
2136
|
+
})
|
|
2137
|
+
);
|
|
2099
2138
|
return {
|
|
2100
|
-
embeddings,
|
|
2101
|
-
totalTokensUsed:
|
|
2139
|
+
embeddings: results.map((r) => r.embedding),
|
|
2140
|
+
totalTokensUsed: results.reduce((sum, r) => sum + r.tokensUsed, 0)
|
|
2102
2141
|
};
|
|
2103
2142
|
}
|
|
2104
2143
|
getModelInfo() {
|
|
@@ -2132,16 +2171,10 @@ var OllamaEmbeddingProvider = class {
|
|
|
2132
2171
|
};
|
|
2133
2172
|
}
|
|
2134
2173
|
async embedBatch(texts) {
|
|
2135
|
-
const
|
|
2136
|
-
let totalTokens = 0;
|
|
2137
|
-
for (const text of texts) {
|
|
2138
|
-
const result = await this.embed(text);
|
|
2139
|
-
embeddings.push(result.embedding);
|
|
2140
|
-
totalTokens += result.tokensUsed;
|
|
2141
|
-
}
|
|
2174
|
+
const results = await Promise.all(texts.map((text) => this.embed(text)));
|
|
2142
2175
|
return {
|
|
2143
|
-
embeddings,
|
|
2144
|
-
totalTokensUsed:
|
|
2176
|
+
embeddings: results.map((r) => r.embedding),
|
|
2177
|
+
totalTokensUsed: results.reduce((sum, r) => sum + r.tokensUsed, 0)
|
|
2145
2178
|
};
|
|
2146
2179
|
}
|
|
2147
2180
|
getModelInfo() {
|
|
@@ -2327,6 +2360,298 @@ function padRight(str, length) {
|
|
|
2327
2360
|
return str.padEnd(length);
|
|
2328
2361
|
}
|
|
2329
2362
|
|
|
2363
|
+
// src/utils/logger.ts
|
|
2364
|
+
var LOG_LEVEL_PRIORITY = {
|
|
2365
|
+
error: 0,
|
|
2366
|
+
warn: 1,
|
|
2367
|
+
info: 2,
|
|
2368
|
+
debug: 3
|
|
2369
|
+
};
|
|
2370
|
+
function createEmptyMetrics() {
|
|
2371
|
+
return {
|
|
2372
|
+
filesScanned: 0,
|
|
2373
|
+
filesParsed: 0,
|
|
2374
|
+
parseMs: 0,
|
|
2375
|
+
chunksProcessed: 0,
|
|
2376
|
+
chunksEmbedded: 0,
|
|
2377
|
+
chunksFromCache: 0,
|
|
2378
|
+
chunksRemoved: 0,
|
|
2379
|
+
embeddingApiCalls: 0,
|
|
2380
|
+
embeddingTokensUsed: 0,
|
|
2381
|
+
embeddingErrors: 0,
|
|
2382
|
+
searchCount: 0,
|
|
2383
|
+
searchTotalMs: 0,
|
|
2384
|
+
searchAvgMs: 0,
|
|
2385
|
+
searchLastMs: 0,
|
|
2386
|
+
embeddingCallMs: 0,
|
|
2387
|
+
vectorSearchMs: 0,
|
|
2388
|
+
keywordSearchMs: 0,
|
|
2389
|
+
fusionMs: 0,
|
|
2390
|
+
cacheHits: 0,
|
|
2391
|
+
cacheMisses: 0,
|
|
2392
|
+
queryCacheHits: 0,
|
|
2393
|
+
queryCacheSimilarHits: 0,
|
|
2394
|
+
queryCacheMisses: 0,
|
|
2395
|
+
gcRuns: 0,
|
|
2396
|
+
gcOrphansRemoved: 0,
|
|
2397
|
+
gcChunksRemoved: 0,
|
|
2398
|
+
gcEmbeddingsRemoved: 0
|
|
2399
|
+
};
|
|
2400
|
+
}
|
|
2401
|
+
var Logger = class {
|
|
2402
|
+
config;
|
|
2403
|
+
metrics;
|
|
2404
|
+
logs = [];
|
|
2405
|
+
maxLogs = 1e3;
|
|
2406
|
+
constructor(config) {
|
|
2407
|
+
this.config = config;
|
|
2408
|
+
this.metrics = createEmptyMetrics();
|
|
2409
|
+
}
|
|
2410
|
+
shouldLog(level) {
|
|
2411
|
+
if (!this.config.enabled) return false;
|
|
2412
|
+
return LOG_LEVEL_PRIORITY[level] <= LOG_LEVEL_PRIORITY[this.config.logLevel];
|
|
2413
|
+
}
|
|
2414
|
+
log(level, category, message, data) {
|
|
2415
|
+
if (!this.shouldLog(level)) return;
|
|
2416
|
+
const entry = {
|
|
2417
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2418
|
+
level,
|
|
2419
|
+
category,
|
|
2420
|
+
message,
|
|
2421
|
+
data
|
|
2422
|
+
};
|
|
2423
|
+
this.logs.push(entry);
|
|
2424
|
+
if (this.logs.length > this.maxLogs) {
|
|
2425
|
+
this.logs.shift();
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
search(level, message, data) {
|
|
2429
|
+
if (this.config.logSearch) {
|
|
2430
|
+
this.log(level, "search", message, data);
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
embedding(level, message, data) {
|
|
2434
|
+
if (this.config.logEmbedding) {
|
|
2435
|
+
this.log(level, "embedding", message, data);
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
cache(level, message, data) {
|
|
2439
|
+
if (this.config.logCache) {
|
|
2440
|
+
this.log(level, "cache", message, data);
|
|
2441
|
+
}
|
|
2442
|
+
}
|
|
2443
|
+
gc(level, message, data) {
|
|
2444
|
+
if (this.config.logGc) {
|
|
2445
|
+
this.log(level, "gc", message, data);
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
branch(level, message, data) {
|
|
2449
|
+
if (this.config.logBranch) {
|
|
2450
|
+
this.log(level, "branch", message, data);
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
info(message, data) {
|
|
2454
|
+
this.log("info", "general", message, data);
|
|
2455
|
+
}
|
|
2456
|
+
warn(message, data) {
|
|
2457
|
+
this.log("warn", "general", message, data);
|
|
2458
|
+
}
|
|
2459
|
+
error(message, data) {
|
|
2460
|
+
this.log("error", "general", message, data);
|
|
2461
|
+
}
|
|
2462
|
+
debug(message, data) {
|
|
2463
|
+
this.log("debug", "general", message, data);
|
|
2464
|
+
}
|
|
2465
|
+
recordIndexingStart() {
|
|
2466
|
+
if (!this.config.metrics) return;
|
|
2467
|
+
this.metrics.indexingStartTime = Date.now();
|
|
2468
|
+
}
|
|
2469
|
+
recordIndexingEnd() {
|
|
2470
|
+
if (!this.config.metrics) return;
|
|
2471
|
+
this.metrics.indexingEndTime = Date.now();
|
|
2472
|
+
}
|
|
2473
|
+
recordFilesScanned(count) {
|
|
2474
|
+
if (!this.config.metrics) return;
|
|
2475
|
+
this.metrics.filesScanned = count;
|
|
2476
|
+
}
|
|
2477
|
+
recordFilesParsed(count) {
|
|
2478
|
+
if (!this.config.metrics) return;
|
|
2479
|
+
this.metrics.filesParsed = count;
|
|
2480
|
+
}
|
|
2481
|
+
recordParseDuration(durationMs) {
|
|
2482
|
+
if (!this.config.metrics) return;
|
|
2483
|
+
this.metrics.parseMs = durationMs;
|
|
2484
|
+
}
|
|
2485
|
+
recordChunksProcessed(count) {
|
|
2486
|
+
if (!this.config.metrics) return;
|
|
2487
|
+
this.metrics.chunksProcessed += count;
|
|
2488
|
+
}
|
|
2489
|
+
recordChunksEmbedded(count) {
|
|
2490
|
+
if (!this.config.metrics) return;
|
|
2491
|
+
this.metrics.chunksEmbedded += count;
|
|
2492
|
+
}
|
|
2493
|
+
recordChunksFromCache(count) {
|
|
2494
|
+
if (!this.config.metrics) return;
|
|
2495
|
+
this.metrics.chunksFromCache += count;
|
|
2496
|
+
}
|
|
2497
|
+
recordChunksRemoved(count) {
|
|
2498
|
+
if (!this.config.metrics) return;
|
|
2499
|
+
this.metrics.chunksRemoved += count;
|
|
2500
|
+
}
|
|
2501
|
+
recordEmbeddingApiCall(tokens) {
|
|
2502
|
+
if (!this.config.metrics) return;
|
|
2503
|
+
this.metrics.embeddingApiCalls++;
|
|
2504
|
+
this.metrics.embeddingTokensUsed += tokens;
|
|
2505
|
+
}
|
|
2506
|
+
recordEmbeddingError() {
|
|
2507
|
+
if (!this.config.metrics) return;
|
|
2508
|
+
this.metrics.embeddingErrors++;
|
|
2509
|
+
}
|
|
2510
|
+
recordSearch(durationMs, breakdown) {
|
|
2511
|
+
if (!this.config.metrics) return;
|
|
2512
|
+
this.metrics.searchCount++;
|
|
2513
|
+
this.metrics.searchTotalMs += durationMs;
|
|
2514
|
+
this.metrics.searchLastMs = durationMs;
|
|
2515
|
+
this.metrics.searchAvgMs = this.metrics.searchTotalMs / this.metrics.searchCount;
|
|
2516
|
+
if (breakdown) {
|
|
2517
|
+
this.metrics.embeddingCallMs = breakdown.embeddingMs;
|
|
2518
|
+
this.metrics.vectorSearchMs = breakdown.vectorMs;
|
|
2519
|
+
this.metrics.keywordSearchMs = breakdown.keywordMs;
|
|
2520
|
+
this.metrics.fusionMs = breakdown.fusionMs;
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
recordCacheHit() {
|
|
2524
|
+
if (!this.config.metrics) return;
|
|
2525
|
+
this.metrics.cacheHits++;
|
|
2526
|
+
}
|
|
2527
|
+
recordCacheMiss() {
|
|
2528
|
+
if (!this.config.metrics) return;
|
|
2529
|
+
this.metrics.cacheMisses++;
|
|
2530
|
+
}
|
|
2531
|
+
recordQueryCacheHit() {
|
|
2532
|
+
if (!this.config.metrics) return;
|
|
2533
|
+
this.metrics.queryCacheHits++;
|
|
2534
|
+
}
|
|
2535
|
+
recordQueryCacheSimilarHit() {
|
|
2536
|
+
if (!this.config.metrics) return;
|
|
2537
|
+
this.metrics.queryCacheSimilarHits++;
|
|
2538
|
+
}
|
|
2539
|
+
recordQueryCacheMiss() {
|
|
2540
|
+
if (!this.config.metrics) return;
|
|
2541
|
+
this.metrics.queryCacheMisses++;
|
|
2542
|
+
}
|
|
2543
|
+
recordGc(orphans, chunks, embeddings) {
|
|
2544
|
+
if (!this.config.metrics) return;
|
|
2545
|
+
this.metrics.gcRuns++;
|
|
2546
|
+
this.metrics.gcOrphansRemoved += orphans;
|
|
2547
|
+
this.metrics.gcChunksRemoved += chunks;
|
|
2548
|
+
this.metrics.gcEmbeddingsRemoved += embeddings;
|
|
2549
|
+
}
|
|
2550
|
+
getMetrics() {
|
|
2551
|
+
return { ...this.metrics };
|
|
2552
|
+
}
|
|
2553
|
+
getLogs(limit) {
|
|
2554
|
+
const logs = [...this.logs];
|
|
2555
|
+
if (limit) {
|
|
2556
|
+
return logs.slice(-limit);
|
|
2557
|
+
}
|
|
2558
|
+
return logs;
|
|
2559
|
+
}
|
|
2560
|
+
getLogsByCategory(category, limit) {
|
|
2561
|
+
const filtered = this.logs.filter((l) => l.category === category);
|
|
2562
|
+
if (limit) {
|
|
2563
|
+
return filtered.slice(-limit);
|
|
2564
|
+
}
|
|
2565
|
+
return filtered;
|
|
2566
|
+
}
|
|
2567
|
+
getLogsByLevel(level, limit) {
|
|
2568
|
+
const filtered = this.logs.filter((l) => l.level === level);
|
|
2569
|
+
if (limit) {
|
|
2570
|
+
return filtered.slice(-limit);
|
|
2571
|
+
}
|
|
2572
|
+
return filtered;
|
|
2573
|
+
}
|
|
2574
|
+
resetMetrics() {
|
|
2575
|
+
this.metrics = createEmptyMetrics();
|
|
2576
|
+
}
|
|
2577
|
+
clearLogs() {
|
|
2578
|
+
this.logs = [];
|
|
2579
|
+
}
|
|
2580
|
+
formatMetrics() {
|
|
2581
|
+
const m = this.metrics;
|
|
2582
|
+
const lines = [];
|
|
2583
|
+
lines.push("=== Metrics ===");
|
|
2584
|
+
if (m.indexingStartTime && m.indexingEndTime) {
|
|
2585
|
+
const duration = m.indexingEndTime - m.indexingStartTime;
|
|
2586
|
+
lines.push(`Indexing duration: ${(duration / 1e3).toFixed(2)}s`);
|
|
2587
|
+
}
|
|
2588
|
+
lines.push("");
|
|
2589
|
+
lines.push("Indexing:");
|
|
2590
|
+
lines.push(` Files scanned: ${m.filesScanned}`);
|
|
2591
|
+
lines.push(` Files parsed: ${m.filesParsed}`);
|
|
2592
|
+
lines.push(` Chunks processed: ${m.chunksProcessed}`);
|
|
2593
|
+
lines.push(` Chunks embedded: ${m.chunksEmbedded}`);
|
|
2594
|
+
lines.push(` Chunks from cache: ${m.chunksFromCache}`);
|
|
2595
|
+
lines.push(` Chunks removed: ${m.chunksRemoved}`);
|
|
2596
|
+
lines.push("");
|
|
2597
|
+
lines.push("Embedding API:");
|
|
2598
|
+
lines.push(` API calls: ${m.embeddingApiCalls}`);
|
|
2599
|
+
lines.push(` Tokens used: ${m.embeddingTokensUsed.toLocaleString()}`);
|
|
2600
|
+
lines.push(` Errors: ${m.embeddingErrors}`);
|
|
2601
|
+
if (m.searchCount > 0) {
|
|
2602
|
+
lines.push("");
|
|
2603
|
+
lines.push("Search:");
|
|
2604
|
+
lines.push(` Total searches: ${m.searchCount}`);
|
|
2605
|
+
lines.push(` Average time: ${m.searchAvgMs.toFixed(2)}ms`);
|
|
2606
|
+
lines.push(` Last search: ${m.searchLastMs.toFixed(2)}ms`);
|
|
2607
|
+
if (m.embeddingCallMs > 0) {
|
|
2608
|
+
lines.push(` - Embedding: ${m.embeddingCallMs.toFixed(2)}ms`);
|
|
2609
|
+
lines.push(` - Vector search: ${m.vectorSearchMs.toFixed(2)}ms`);
|
|
2610
|
+
lines.push(` - Keyword search: ${m.keywordSearchMs.toFixed(2)}ms`);
|
|
2611
|
+
lines.push(` - Fusion: ${m.fusionMs.toFixed(2)}ms`);
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
const totalCacheOps = m.cacheHits + m.cacheMisses;
|
|
2615
|
+
if (totalCacheOps > 0) {
|
|
2616
|
+
lines.push("");
|
|
2617
|
+
lines.push("Cache:");
|
|
2618
|
+
lines.push(` Hits: ${m.cacheHits}`);
|
|
2619
|
+
lines.push(` Misses: ${m.cacheMisses}`);
|
|
2620
|
+
lines.push(` Hit rate: ${(m.cacheHits / totalCacheOps * 100).toFixed(1)}%`);
|
|
2621
|
+
}
|
|
2622
|
+
if (m.gcRuns > 0) {
|
|
2623
|
+
lines.push("");
|
|
2624
|
+
lines.push("Garbage Collection:");
|
|
2625
|
+
lines.push(` GC runs: ${m.gcRuns}`);
|
|
2626
|
+
lines.push(` Orphans removed: ${m.gcOrphansRemoved}`);
|
|
2627
|
+
lines.push(` Chunks removed: ${m.gcChunksRemoved}`);
|
|
2628
|
+
lines.push(` Embeddings removed: ${m.gcEmbeddingsRemoved}`);
|
|
2629
|
+
}
|
|
2630
|
+
return lines.join("\n");
|
|
2631
|
+
}
|
|
2632
|
+
formatRecentLogs(limit = 20) {
|
|
2633
|
+
const logs = this.getLogs(limit);
|
|
2634
|
+
if (logs.length === 0) {
|
|
2635
|
+
return "No logs recorded.";
|
|
2636
|
+
}
|
|
2637
|
+
return logs.map((l) => {
|
|
2638
|
+
const dataStr = l.data ? ` ${JSON.stringify(l.data)}` : "";
|
|
2639
|
+
return `[${l.timestamp}] [${l.level.toUpperCase()}] [${l.category}] ${l.message}${dataStr}`;
|
|
2640
|
+
}).join("\n");
|
|
2641
|
+
}
|
|
2642
|
+
isEnabled() {
|
|
2643
|
+
return this.config.enabled;
|
|
2644
|
+
}
|
|
2645
|
+
isMetricsEnabled() {
|
|
2646
|
+
return this.config.enabled && this.config.metrics;
|
|
2647
|
+
}
|
|
2648
|
+
};
|
|
2649
|
+
var globalLogger = null;
|
|
2650
|
+
function initializeLogger(config) {
|
|
2651
|
+
globalLogger = new Logger(config);
|
|
2652
|
+
return globalLogger;
|
|
2653
|
+
}
|
|
2654
|
+
|
|
2330
2655
|
// src/native/index.ts
|
|
2331
2656
|
var path3 = __toESM(require("path"), 1);
|
|
2332
2657
|
var os2 = __toESM(require("os"), 1);
|
|
@@ -2456,6 +2781,21 @@ var VectorStore = class {
|
|
|
2456
2781
|
metadata: JSON.parse(r.metadata)
|
|
2457
2782
|
}));
|
|
2458
2783
|
}
|
|
2784
|
+
getMetadata(id) {
|
|
2785
|
+
const result = this.inner.getMetadata(id);
|
|
2786
|
+
if (result === null || result === void 0) {
|
|
2787
|
+
return void 0;
|
|
2788
|
+
}
|
|
2789
|
+
return JSON.parse(result);
|
|
2790
|
+
}
|
|
2791
|
+
getMetadataBatch(ids) {
|
|
2792
|
+
const results = this.inner.getMetadataBatch(ids);
|
|
2793
|
+
const map = /* @__PURE__ */ new Map();
|
|
2794
|
+
for (const { key, metadata } of results) {
|
|
2795
|
+
map.set(key, JSON.parse(metadata));
|
|
2796
|
+
}
|
|
2797
|
+
return map;
|
|
2798
|
+
}
|
|
2459
2799
|
};
|
|
2460
2800
|
var CHARS_PER_TOKEN = 4;
|
|
2461
2801
|
var MAX_BATCH_TOKENS = 7500;
|
|
@@ -2683,12 +3023,20 @@ var Database = class {
|
|
|
2683
3023
|
upsertEmbedding(contentHash, embedding, chunkText, model) {
|
|
2684
3024
|
this.inner.upsertEmbedding(contentHash, embedding, chunkText, model);
|
|
2685
3025
|
}
|
|
3026
|
+
upsertEmbeddingsBatch(items) {
|
|
3027
|
+
if (items.length === 0) return;
|
|
3028
|
+
this.inner.upsertEmbeddingsBatch(items);
|
|
3029
|
+
}
|
|
2686
3030
|
getMissingEmbeddings(contentHashes) {
|
|
2687
3031
|
return this.inner.getMissingEmbeddings(contentHashes);
|
|
2688
3032
|
}
|
|
2689
3033
|
upsertChunk(chunk) {
|
|
2690
3034
|
this.inner.upsertChunk(chunk);
|
|
2691
3035
|
}
|
|
3036
|
+
upsertChunksBatch(chunks) {
|
|
3037
|
+
if (chunks.length === 0) return;
|
|
3038
|
+
this.inner.upsertChunksBatch(chunks);
|
|
3039
|
+
}
|
|
2692
3040
|
getChunk(chunkId) {
|
|
2693
3041
|
return this.inner.getChunk(chunkId) ?? null;
|
|
2694
3042
|
}
|
|
@@ -2701,6 +3049,10 @@ var Database = class {
|
|
|
2701
3049
|
addChunksToBranch(branch, chunkIds) {
|
|
2702
3050
|
this.inner.addChunksToBranch(branch, chunkIds);
|
|
2703
3051
|
}
|
|
3052
|
+
addChunksToBranchBatch(branch, chunkIds) {
|
|
3053
|
+
if (chunkIds.length === 0) return;
|
|
3054
|
+
this.inner.addChunksToBranchBatch(branch, chunkIds);
|
|
3055
|
+
}
|
|
2704
3056
|
clearBranch(branch) {
|
|
2705
3057
|
return this.inner.clearBranch(branch);
|
|
2706
3058
|
}
|
|
@@ -2842,12 +3194,18 @@ var Indexer = class {
|
|
|
2842
3194
|
failedBatchesPath = "";
|
|
2843
3195
|
currentBranch = "default";
|
|
2844
3196
|
baseBranch = "main";
|
|
3197
|
+
logger;
|
|
3198
|
+
queryEmbeddingCache = /* @__PURE__ */ new Map();
|
|
3199
|
+
maxQueryCacheSize = 100;
|
|
3200
|
+
queryCacheTtlMs = 5 * 60 * 1e3;
|
|
3201
|
+
querySimilarityThreshold = 0.85;
|
|
2845
3202
|
constructor(projectRoot, config) {
|
|
2846
3203
|
this.projectRoot = projectRoot;
|
|
2847
3204
|
this.config = config;
|
|
2848
3205
|
this.indexPath = this.getIndexPath();
|
|
2849
3206
|
this.fileHashCachePath = path5.join(this.indexPath, "file-hashes.json");
|
|
2850
3207
|
this.failedBatchesPath = path5.join(this.indexPath, "failed-batches.json");
|
|
3208
|
+
this.logger = initializeLogger(config.debug);
|
|
2851
3209
|
}
|
|
2852
3210
|
getIndexPath() {
|
|
2853
3211
|
if (this.config.scope === "global") {
|
|
@@ -2926,6 +3284,11 @@ var Indexer = class {
|
|
|
2926
3284
|
"No embedding provider available. Configure GitHub, OpenAI, Google, or Ollama."
|
|
2927
3285
|
);
|
|
2928
3286
|
}
|
|
3287
|
+
this.logger.info("Initializing indexer", {
|
|
3288
|
+
provider: this.detectedProvider.provider,
|
|
3289
|
+
model: this.detectedProvider.modelInfo.model,
|
|
3290
|
+
scope: this.config.scope
|
|
3291
|
+
});
|
|
2929
3292
|
this.provider = createEmbeddingProvider(
|
|
2930
3293
|
this.detectedProvider.credentials,
|
|
2931
3294
|
this.detectedProvider.modelInfo
|
|
@@ -2957,15 +3320,54 @@ var Indexer = class {
|
|
|
2957
3320
|
if (isGitRepo(this.projectRoot)) {
|
|
2958
3321
|
this.currentBranch = getBranchOrDefault(this.projectRoot);
|
|
2959
3322
|
this.baseBranch = getBaseBranch(this.projectRoot);
|
|
3323
|
+
this.logger.branch("info", "Detected git repository", {
|
|
3324
|
+
currentBranch: this.currentBranch,
|
|
3325
|
+
baseBranch: this.baseBranch
|
|
3326
|
+
});
|
|
2960
3327
|
} else {
|
|
2961
3328
|
this.currentBranch = "default";
|
|
2962
3329
|
this.baseBranch = "default";
|
|
3330
|
+
this.logger.branch("debug", "Not a git repository, using default branch");
|
|
3331
|
+
}
|
|
3332
|
+
if (this.config.indexing.autoGc) {
|
|
3333
|
+
await this.maybeRunAutoGc();
|
|
3334
|
+
}
|
|
3335
|
+
}
|
|
3336
|
+
async maybeRunAutoGc() {
|
|
3337
|
+
if (!this.database) return;
|
|
3338
|
+
const lastGcTimestamp = this.database.getMetadata("lastGcTimestamp");
|
|
3339
|
+
const now = Date.now();
|
|
3340
|
+
const intervalMs = this.config.indexing.gcIntervalDays * 24 * 60 * 60 * 1e3;
|
|
3341
|
+
let shouldRunGc = false;
|
|
3342
|
+
if (!lastGcTimestamp) {
|
|
3343
|
+
shouldRunGc = true;
|
|
3344
|
+
} else {
|
|
3345
|
+
const lastGcTime = parseInt(lastGcTimestamp, 10);
|
|
3346
|
+
if (!isNaN(lastGcTime) && now - lastGcTime > intervalMs) {
|
|
3347
|
+
shouldRunGc = true;
|
|
3348
|
+
}
|
|
3349
|
+
}
|
|
3350
|
+
if (shouldRunGc) {
|
|
3351
|
+
await this.healthCheck();
|
|
3352
|
+
this.database.setMetadata("lastGcTimestamp", now.toString());
|
|
3353
|
+
}
|
|
3354
|
+
}
|
|
3355
|
+
async maybeRunOrphanGc() {
|
|
3356
|
+
if (!this.database) return;
|
|
3357
|
+
const stats = this.database.getStats();
|
|
3358
|
+
if (!stats) return;
|
|
3359
|
+
const orphanCount = stats.embeddingCount - stats.chunkCount;
|
|
3360
|
+
if (orphanCount > this.config.indexing.gcOrphanThreshold) {
|
|
3361
|
+
this.database.gcOrphanEmbeddings();
|
|
3362
|
+
this.database.gcOrphanChunks();
|
|
3363
|
+
this.database.setMetadata("lastGcTimestamp", Date.now().toString());
|
|
2963
3364
|
}
|
|
2964
3365
|
}
|
|
2965
3366
|
migrateFromLegacyIndex() {
|
|
2966
3367
|
if (!this.store || !this.database) return;
|
|
2967
3368
|
const allMetadata = this.store.getAllMetadata();
|
|
2968
3369
|
const chunkIds = [];
|
|
3370
|
+
const chunkDataBatch = [];
|
|
2969
3371
|
for (const { key, metadata } of allMetadata) {
|
|
2970
3372
|
const chunkData = {
|
|
2971
3373
|
chunkId: key,
|
|
@@ -2977,10 +3379,13 @@ var Indexer = class {
|
|
|
2977
3379
|
name: metadata.name,
|
|
2978
3380
|
language: metadata.language
|
|
2979
3381
|
};
|
|
2980
|
-
|
|
3382
|
+
chunkDataBatch.push(chunkData);
|
|
2981
3383
|
chunkIds.push(key);
|
|
2982
3384
|
}
|
|
2983
|
-
|
|
3385
|
+
if (chunkDataBatch.length > 0) {
|
|
3386
|
+
this.database.upsertChunksBatch(chunkDataBatch);
|
|
3387
|
+
}
|
|
3388
|
+
this.database.addChunksToBranchBatch(this.currentBranch || "default", chunkIds);
|
|
2984
3389
|
}
|
|
2985
3390
|
async ensureInitialized() {
|
|
2986
3391
|
if (!this.store || !this.provider || !this.invertedIndex || !this.detectedProvider || !this.database) {
|
|
@@ -3006,6 +3411,8 @@ var Indexer = class {
|
|
|
3006
3411
|
}
|
|
3007
3412
|
async index(onProgress) {
|
|
3008
3413
|
const { store, provider, invertedIndex, database, detectedProvider } = await this.ensureInitialized();
|
|
3414
|
+
this.logger.recordIndexingStart();
|
|
3415
|
+
this.logger.info("Starting indexing", { projectRoot: this.projectRoot });
|
|
3009
3416
|
const startTime = Date.now();
|
|
3010
3417
|
const stats = {
|
|
3011
3418
|
totalFiles: 0,
|
|
@@ -3035,6 +3442,11 @@ var Indexer = class {
|
|
|
3035
3442
|
);
|
|
3036
3443
|
stats.totalFiles = files.length;
|
|
3037
3444
|
stats.skippedFiles = skipped;
|
|
3445
|
+
this.logger.recordFilesScanned(files.length);
|
|
3446
|
+
this.logger.cache("debug", "Scanning files for changes", {
|
|
3447
|
+
totalFiles: files.length,
|
|
3448
|
+
skippedFiles: skipped.length
|
|
3449
|
+
});
|
|
3038
3450
|
const changedFiles = [];
|
|
3039
3451
|
const unchangedFilePaths = /* @__PURE__ */ new Set();
|
|
3040
3452
|
const currentFileHashes = /* @__PURE__ */ new Map();
|
|
@@ -3043,11 +3455,17 @@ var Indexer = class {
|
|
|
3043
3455
|
currentFileHashes.set(f.path, currentHash);
|
|
3044
3456
|
if (this.fileHashCache.get(f.path) === currentHash) {
|
|
3045
3457
|
unchangedFilePaths.add(f.path);
|
|
3458
|
+
this.logger.recordCacheHit();
|
|
3046
3459
|
} else {
|
|
3047
3460
|
const content = await import_fs4.promises.readFile(f.path, "utf-8");
|
|
3048
3461
|
changedFiles.push({ path: f.path, content, hash: currentHash });
|
|
3462
|
+
this.logger.recordCacheMiss();
|
|
3049
3463
|
}
|
|
3050
3464
|
}
|
|
3465
|
+
this.logger.cache("info", "File hash cache results", {
|
|
3466
|
+
unchanged: unchangedFilePaths.size,
|
|
3467
|
+
changed: changedFiles.length
|
|
3468
|
+
});
|
|
3051
3469
|
onProgress?.({
|
|
3052
3470
|
phase: "parsing",
|
|
3053
3471
|
filesProcessed: 0,
|
|
@@ -3055,7 +3473,12 @@ var Indexer = class {
|
|
|
3055
3473
|
chunksProcessed: 0,
|
|
3056
3474
|
totalChunks: 0
|
|
3057
3475
|
});
|
|
3476
|
+
const parseStartTime = import_perf_hooks.performance.now();
|
|
3058
3477
|
const parsedFiles = parseFiles(changedFiles);
|
|
3478
|
+
const parseMs = import_perf_hooks.performance.now() - parseStartTime;
|
|
3479
|
+
this.logger.recordFilesParsed(parsedFiles.length);
|
|
3480
|
+
this.logger.recordParseDuration(parseMs);
|
|
3481
|
+
this.logger.debug("Parsed changed files", { parsedCount: parsedFiles.length, parseMs: parseMs.toFixed(2) });
|
|
3059
3482
|
const existingChunks = /* @__PURE__ */ new Map();
|
|
3060
3483
|
const existingChunksByFile = /* @__PURE__ */ new Map();
|
|
3061
3484
|
for (const { key, metadata } of store.getAllMetadata()) {
|
|
@@ -3076,6 +3499,7 @@ var Indexer = class {
|
|
|
3076
3499
|
}
|
|
3077
3500
|
}
|
|
3078
3501
|
}
|
|
3502
|
+
const chunkDataBatch = [];
|
|
3079
3503
|
for (const parsed of parsedFiles) {
|
|
3080
3504
|
currentFilePaths.add(parsed.path);
|
|
3081
3505
|
if (parsed.chunks.length === 0) {
|
|
@@ -3103,7 +3527,7 @@ var Indexer = class {
|
|
|
3103
3527
|
name: chunk.name,
|
|
3104
3528
|
language: chunk.language
|
|
3105
3529
|
};
|
|
3106
|
-
|
|
3530
|
+
chunkDataBatch.push(chunkData);
|
|
3107
3531
|
if (existingChunks.get(id) === contentHash) {
|
|
3108
3532
|
fileChunkCount++;
|
|
3109
3533
|
continue;
|
|
@@ -3122,6 +3546,9 @@ var Indexer = class {
|
|
|
3122
3546
|
fileChunkCount++;
|
|
3123
3547
|
}
|
|
3124
3548
|
}
|
|
3549
|
+
if (chunkDataBatch.length > 0) {
|
|
3550
|
+
database.upsertChunksBatch(chunkDataBatch);
|
|
3551
|
+
}
|
|
3125
3552
|
let removedCount = 0;
|
|
3126
3553
|
for (const [chunkId] of existingChunks) {
|
|
3127
3554
|
if (!currentChunkIds.has(chunkId)) {
|
|
@@ -3133,9 +3560,16 @@ var Indexer = class {
|
|
|
3133
3560
|
stats.totalChunks = pendingChunks.length;
|
|
3134
3561
|
stats.existingChunks = currentChunkIds.size - pendingChunks.length;
|
|
3135
3562
|
stats.removedChunks = removedCount;
|
|
3563
|
+
this.logger.recordChunksProcessed(currentChunkIds.size);
|
|
3564
|
+
this.logger.recordChunksRemoved(removedCount);
|
|
3565
|
+
this.logger.info("Chunk analysis complete", {
|
|
3566
|
+
pending: pendingChunks.length,
|
|
3567
|
+
existing: stats.existingChunks,
|
|
3568
|
+
removed: removedCount
|
|
3569
|
+
});
|
|
3136
3570
|
if (pendingChunks.length === 0 && removedCount === 0) {
|
|
3137
3571
|
database.clearBranch(this.currentBranch);
|
|
3138
|
-
database.
|
|
3572
|
+
database.addChunksToBranchBatch(this.currentBranch, Array.from(currentChunkIds));
|
|
3139
3573
|
this.fileHashCache = currentFileHashes;
|
|
3140
3574
|
this.saveFileHashCache();
|
|
3141
3575
|
stats.durationMs = Date.now() - startTime;
|
|
@@ -3150,7 +3584,7 @@ var Indexer = class {
|
|
|
3150
3584
|
}
|
|
3151
3585
|
if (pendingChunks.length === 0) {
|
|
3152
3586
|
database.clearBranch(this.currentBranch);
|
|
3153
|
-
database.
|
|
3587
|
+
database.addChunksToBranchBatch(this.currentBranch, Array.from(currentChunkIds));
|
|
3154
3588
|
store.save();
|
|
3155
3589
|
invertedIndex.save();
|
|
3156
3590
|
this.fileHashCache = currentFileHashes;
|
|
@@ -3176,6 +3610,11 @@ var Indexer = class {
|
|
|
3176
3610
|
const missingHashes = new Set(database.getMissingEmbeddings(allContentHashes));
|
|
3177
3611
|
const chunksNeedingEmbedding = pendingChunks.filter((c) => missingHashes.has(c.contentHash));
|
|
3178
3612
|
const chunksWithExistingEmbedding = pendingChunks.filter((c) => !missingHashes.has(c.contentHash));
|
|
3613
|
+
this.logger.cache("info", "Embedding cache lookup", {
|
|
3614
|
+
needsEmbedding: chunksNeedingEmbedding.length,
|
|
3615
|
+
fromCache: chunksWithExistingEmbedding.length
|
|
3616
|
+
});
|
|
3617
|
+
this.logger.recordChunksFromCache(chunksWithExistingEmbedding.length);
|
|
3179
3618
|
for (const chunk of chunksWithExistingEmbedding) {
|
|
3180
3619
|
const embeddingBuffer = database.getEmbedding(chunk.contentHash);
|
|
3181
3620
|
if (embeddingBuffer) {
|
|
@@ -3214,13 +3653,16 @@ var Indexer = class {
|
|
|
3214
3653
|
const message = getErrorMessage(error);
|
|
3215
3654
|
if (isRateLimitError(error)) {
|
|
3216
3655
|
rateLimitBackoffMs = Math.min(providerRateLimits.maxRetryMs, (rateLimitBackoffMs || providerRateLimits.minRetryMs) * 2);
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3656
|
+
this.logger.embedding("warn", `Rate limited, backing off`, {
|
|
3657
|
+
attempt: error.attemptNumber,
|
|
3658
|
+
retriesLeft: error.retriesLeft,
|
|
3659
|
+
backoffMs: rateLimitBackoffMs
|
|
3660
|
+
});
|
|
3220
3661
|
} else {
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3662
|
+
this.logger.embedding("error", `Embedding batch failed`, {
|
|
3663
|
+
attempt: error.attemptNumber,
|
|
3664
|
+
error: message
|
|
3665
|
+
});
|
|
3224
3666
|
}
|
|
3225
3667
|
}
|
|
3226
3668
|
}
|
|
@@ -3234,20 +3676,25 @@ var Indexer = class {
|
|
|
3234
3676
|
metadata: chunk.metadata
|
|
3235
3677
|
}));
|
|
3236
3678
|
store.addBatch(items);
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
);
|
|
3679
|
+
const embeddingBatchItems = batch.map((chunk, i) => ({
|
|
3680
|
+
contentHash: chunk.contentHash,
|
|
3681
|
+
embedding: float32ArrayToBuffer(result.embeddings[i]),
|
|
3682
|
+
chunkText: chunk.text,
|
|
3683
|
+
model: detectedProvider.modelInfo.model
|
|
3684
|
+
}));
|
|
3685
|
+
database.upsertEmbeddingsBatch(embeddingBatchItems);
|
|
3686
|
+
for (const chunk of batch) {
|
|
3246
3687
|
invertedIndex.removeChunk(chunk.id);
|
|
3247
3688
|
invertedIndex.addChunk(chunk.id, chunk.content);
|
|
3248
3689
|
}
|
|
3249
3690
|
stats.indexedChunks += batch.length;
|
|
3250
3691
|
stats.tokensUsed += result.totalTokensUsed;
|
|
3692
|
+
this.logger.recordChunksEmbedded(batch.length);
|
|
3693
|
+
this.logger.recordEmbeddingApiCall(result.totalTokensUsed);
|
|
3694
|
+
this.logger.embedding("debug", `Embedded batch`, {
|
|
3695
|
+
batchSize: batch.length,
|
|
3696
|
+
tokens: result.totalTokensUsed
|
|
3697
|
+
});
|
|
3251
3698
|
onProgress?.({
|
|
3252
3699
|
phase: "embedding",
|
|
3253
3700
|
filesProcessed: files.length,
|
|
@@ -3258,7 +3705,11 @@ var Indexer = class {
|
|
|
3258
3705
|
} catch (error) {
|
|
3259
3706
|
stats.failedChunks += batch.length;
|
|
3260
3707
|
this.addFailedBatch(batch, getErrorMessage(error));
|
|
3261
|
-
|
|
3708
|
+
this.logger.recordEmbeddingError();
|
|
3709
|
+
this.logger.embedding("error", `Failed to embed batch after retries`, {
|
|
3710
|
+
batchSize: batch.length,
|
|
3711
|
+
error: getErrorMessage(error)
|
|
3712
|
+
});
|
|
3262
3713
|
}
|
|
3263
3714
|
});
|
|
3264
3715
|
}
|
|
@@ -3271,12 +3722,25 @@ var Indexer = class {
|
|
|
3271
3722
|
totalChunks: pendingChunks.length
|
|
3272
3723
|
});
|
|
3273
3724
|
database.clearBranch(this.currentBranch);
|
|
3274
|
-
database.
|
|
3725
|
+
database.addChunksToBranchBatch(this.currentBranch, Array.from(currentChunkIds));
|
|
3275
3726
|
store.save();
|
|
3276
3727
|
invertedIndex.save();
|
|
3277
3728
|
this.fileHashCache = currentFileHashes;
|
|
3278
3729
|
this.saveFileHashCache();
|
|
3730
|
+
if (this.config.indexing.autoGc && stats.removedChunks > 0) {
|
|
3731
|
+
await this.maybeRunOrphanGc();
|
|
3732
|
+
}
|
|
3279
3733
|
stats.durationMs = Date.now() - startTime;
|
|
3734
|
+
this.logger.recordIndexingEnd();
|
|
3735
|
+
this.logger.info("Indexing complete", {
|
|
3736
|
+
files: stats.totalFiles,
|
|
3737
|
+
indexed: stats.indexedChunks,
|
|
3738
|
+
existing: stats.existingChunks,
|
|
3739
|
+
removed: stats.removedChunks,
|
|
3740
|
+
failed: stats.failedChunks,
|
|
3741
|
+
tokens: stats.tokensUsed,
|
|
3742
|
+
durationMs: stats.durationMs
|
|
3743
|
+
});
|
|
3280
3744
|
if (stats.failedChunks > 0) {
|
|
3281
3745
|
stats.failedBatchesPath = this.failedBatchesPath;
|
|
3282
3746
|
}
|
|
@@ -3289,18 +3753,96 @@ var Indexer = class {
|
|
|
3289
3753
|
});
|
|
3290
3754
|
return stats;
|
|
3291
3755
|
}
|
|
3756
|
+
async getQueryEmbedding(query, provider) {
|
|
3757
|
+
const now = Date.now();
|
|
3758
|
+
const cached = this.queryEmbeddingCache.get(query);
|
|
3759
|
+
if (cached && now - cached.timestamp < this.queryCacheTtlMs) {
|
|
3760
|
+
this.logger.cache("debug", "Query embedding cache hit (exact)", { query: query.slice(0, 50) });
|
|
3761
|
+
this.logger.recordQueryCacheHit();
|
|
3762
|
+
return cached.embedding;
|
|
3763
|
+
}
|
|
3764
|
+
const similarMatch = this.findSimilarCachedQuery(query, now);
|
|
3765
|
+
if (similarMatch) {
|
|
3766
|
+
this.logger.cache("debug", "Query embedding cache hit (similar)", {
|
|
3767
|
+
query: query.slice(0, 50),
|
|
3768
|
+
similarTo: similarMatch.key.slice(0, 50),
|
|
3769
|
+
similarity: similarMatch.similarity.toFixed(3)
|
|
3770
|
+
});
|
|
3771
|
+
this.logger.recordQueryCacheSimilarHit();
|
|
3772
|
+
return similarMatch.embedding;
|
|
3773
|
+
}
|
|
3774
|
+
this.logger.cache("debug", "Query embedding cache miss", { query: query.slice(0, 50) });
|
|
3775
|
+
this.logger.recordQueryCacheMiss();
|
|
3776
|
+
const { embedding, tokensUsed } = await provider.embed(query);
|
|
3777
|
+
this.logger.recordEmbeddingApiCall(tokensUsed);
|
|
3778
|
+
if (this.queryEmbeddingCache.size >= this.maxQueryCacheSize) {
|
|
3779
|
+
const oldestKey = this.queryEmbeddingCache.keys().next().value;
|
|
3780
|
+
if (oldestKey) {
|
|
3781
|
+
this.queryEmbeddingCache.delete(oldestKey);
|
|
3782
|
+
}
|
|
3783
|
+
}
|
|
3784
|
+
this.queryEmbeddingCache.set(query, { embedding, timestamp: now });
|
|
3785
|
+
return embedding;
|
|
3786
|
+
}
|
|
3787
|
+
findSimilarCachedQuery(query, now) {
|
|
3788
|
+
const queryTokens = this.tokenize(query);
|
|
3789
|
+
if (queryTokens.size === 0) return null;
|
|
3790
|
+
let bestMatch = null;
|
|
3791
|
+
for (const [cachedQuery, { embedding, timestamp }] of this.queryEmbeddingCache) {
|
|
3792
|
+
if (now - timestamp >= this.queryCacheTtlMs) continue;
|
|
3793
|
+
const cachedTokens = this.tokenize(cachedQuery);
|
|
3794
|
+
const similarity = this.jaccardSimilarity(queryTokens, cachedTokens);
|
|
3795
|
+
if (similarity >= this.querySimilarityThreshold) {
|
|
3796
|
+
if (!bestMatch || similarity > bestMatch.similarity) {
|
|
3797
|
+
bestMatch = { key: cachedQuery, embedding, similarity };
|
|
3798
|
+
}
|
|
3799
|
+
}
|
|
3800
|
+
}
|
|
3801
|
+
return bestMatch;
|
|
3802
|
+
}
|
|
3803
|
+
tokenize(text) {
|
|
3804
|
+
return new Set(
|
|
3805
|
+
text.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter((t) => t.length > 1)
|
|
3806
|
+
);
|
|
3807
|
+
}
|
|
3808
|
+
jaccardSimilarity(a, b) {
|
|
3809
|
+
if (a.size === 0 && b.size === 0) return 1;
|
|
3810
|
+
if (a.size === 0 || b.size === 0) return 0;
|
|
3811
|
+
let intersection = 0;
|
|
3812
|
+
for (const token of a) {
|
|
3813
|
+
if (b.has(token)) intersection++;
|
|
3814
|
+
}
|
|
3815
|
+
const union = a.size + b.size - intersection;
|
|
3816
|
+
return intersection / union;
|
|
3817
|
+
}
|
|
3292
3818
|
async search(query, limit, options) {
|
|
3819
|
+
const searchStartTime = import_perf_hooks.performance.now();
|
|
3293
3820
|
const { store, provider, database } = await this.ensureInitialized();
|
|
3294
3821
|
if (store.count() === 0) {
|
|
3822
|
+
this.logger.search("debug", "Search on empty index", { query });
|
|
3295
3823
|
return [];
|
|
3296
3824
|
}
|
|
3297
3825
|
const maxResults = limit ?? this.config.search.maxResults;
|
|
3298
3826
|
const hybridWeight = options?.hybridWeight ?? this.config.search.hybridWeight;
|
|
3299
3827
|
const filterByBranch = options?.filterByBranch ?? true;
|
|
3300
|
-
|
|
3828
|
+
this.logger.search("debug", "Starting search", {
|
|
3829
|
+
query,
|
|
3830
|
+
maxResults,
|
|
3831
|
+
hybridWeight,
|
|
3832
|
+
filterByBranch
|
|
3833
|
+
});
|
|
3834
|
+
const embeddingStartTime = import_perf_hooks.performance.now();
|
|
3835
|
+
const embedding = await this.getQueryEmbedding(query, provider);
|
|
3836
|
+
const embeddingMs = import_perf_hooks.performance.now() - embeddingStartTime;
|
|
3837
|
+
const vectorStartTime = import_perf_hooks.performance.now();
|
|
3301
3838
|
const semanticResults = store.search(embedding, maxResults * 4);
|
|
3839
|
+
const vectorMs = import_perf_hooks.performance.now() - vectorStartTime;
|
|
3840
|
+
const keywordStartTime = import_perf_hooks.performance.now();
|
|
3302
3841
|
const keywordResults = await this.keywordSearch(query, maxResults * 4);
|
|
3842
|
+
const keywordMs = import_perf_hooks.performance.now() - keywordStartTime;
|
|
3843
|
+
const fusionStartTime = import_perf_hooks.performance.now();
|
|
3303
3844
|
const combined = this.fuseResults(semanticResults, keywordResults, hybridWeight, maxResults * 4);
|
|
3845
|
+
const fusionMs = import_perf_hooks.performance.now() - fusionStartTime;
|
|
3304
3846
|
let branchChunkIds = null;
|
|
3305
3847
|
if (filterByBranch && this.currentBranch !== "default") {
|
|
3306
3848
|
branchChunkIds = new Set(database.getBranchChunkIds(this.currentBranch));
|
|
@@ -3321,6 +3863,22 @@ var Indexer = class {
|
|
|
3321
3863
|
}
|
|
3322
3864
|
return true;
|
|
3323
3865
|
}).slice(0, maxResults);
|
|
3866
|
+
const totalSearchMs = import_perf_hooks.performance.now() - searchStartTime;
|
|
3867
|
+
this.logger.recordSearch(totalSearchMs, {
|
|
3868
|
+
embeddingMs,
|
|
3869
|
+
vectorMs,
|
|
3870
|
+
keywordMs,
|
|
3871
|
+
fusionMs
|
|
3872
|
+
});
|
|
3873
|
+
this.logger.search("info", "Search complete", {
|
|
3874
|
+
query,
|
|
3875
|
+
results: filtered.length,
|
|
3876
|
+
totalMs: Math.round(totalSearchMs * 100) / 100,
|
|
3877
|
+
embeddingMs: Math.round(embeddingMs * 100) / 100,
|
|
3878
|
+
vectorMs: Math.round(vectorMs * 100) / 100,
|
|
3879
|
+
keywordMs: Math.round(keywordMs * 100) / 100,
|
|
3880
|
+
fusionMs: Math.round(fusionMs * 100) / 100
|
|
3881
|
+
});
|
|
3324
3882
|
return Promise.all(
|
|
3325
3883
|
filtered.map(async (r) => {
|
|
3326
3884
|
let content = "";
|
|
@@ -3359,11 +3917,8 @@ var Indexer = class {
|
|
|
3359
3917
|
if (scores.size === 0) {
|
|
3360
3918
|
return [];
|
|
3361
3919
|
}
|
|
3362
|
-
const
|
|
3363
|
-
const metadataMap =
|
|
3364
|
-
for (const { key, metadata } of allMetadata) {
|
|
3365
|
-
metadataMap.set(key, metadata);
|
|
3366
|
-
}
|
|
3920
|
+
const chunkIds = Array.from(scores.keys());
|
|
3921
|
+
const metadataMap = store.getMetadataBatch(chunkIds);
|
|
3367
3922
|
const results = [];
|
|
3368
3923
|
for (const [chunkId, score] of scores) {
|
|
3369
3924
|
const metadata = metadataMap.get(chunkId);
|
|
@@ -3415,14 +3970,18 @@ var Indexer = class {
|
|
|
3415
3970
|
};
|
|
3416
3971
|
}
|
|
3417
3972
|
async clearIndex() {
|
|
3418
|
-
const { store, invertedIndex } = await this.ensureInitialized();
|
|
3973
|
+
const { store, invertedIndex, database } = await this.ensureInitialized();
|
|
3419
3974
|
store.clear();
|
|
3420
3975
|
store.save();
|
|
3421
3976
|
invertedIndex.clear();
|
|
3422
3977
|
invertedIndex.save();
|
|
3978
|
+
this.fileHashCache.clear();
|
|
3979
|
+
this.saveFileHashCache();
|
|
3980
|
+
database.clearBranch(this.currentBranch);
|
|
3423
3981
|
}
|
|
3424
3982
|
async healthCheck() {
|
|
3425
3983
|
const { store, invertedIndex, database } = await this.ensureInitialized();
|
|
3984
|
+
this.logger.gc("info", "Starting health check");
|
|
3426
3985
|
const allMetadata = store.getAllMetadata();
|
|
3427
3986
|
const filePathsToChunkKeys = /* @__PURE__ */ new Map();
|
|
3428
3987
|
for (const { key, metadata } of allMetadata) {
|
|
@@ -3449,6 +4008,13 @@ var Indexer = class {
|
|
|
3449
4008
|
}
|
|
3450
4009
|
const gcOrphanEmbeddings = database.gcOrphanEmbeddings();
|
|
3451
4010
|
const gcOrphanChunks = database.gcOrphanChunks();
|
|
4011
|
+
this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
|
|
4012
|
+
this.logger.gc("info", "Health check complete", {
|
|
4013
|
+
removedStale: removedCount,
|
|
4014
|
+
orphanEmbeddings: gcOrphanEmbeddings,
|
|
4015
|
+
orphanChunks: gcOrphanChunks,
|
|
4016
|
+
removedFiles: removedFilePaths.length
|
|
4017
|
+
});
|
|
3452
4018
|
return { removed: removedCount, filePaths: removedFilePaths, gcOrphanEmbeddings, gcOrphanChunks };
|
|
3453
4019
|
}
|
|
3454
4020
|
async retryFailedBatches() {
|
|
@@ -3482,9 +4048,12 @@ var Indexer = class {
|
|
|
3482
4048
|
invertedIndex.removeChunk(chunk.id);
|
|
3483
4049
|
invertedIndex.addChunk(chunk.id, chunk.content);
|
|
3484
4050
|
}
|
|
4051
|
+
this.logger.recordChunksEmbedded(batch.chunks.length);
|
|
4052
|
+
this.logger.recordEmbeddingApiCall(result.totalTokensUsed);
|
|
3485
4053
|
succeeded += batch.chunks.length;
|
|
3486
4054
|
} catch (error) {
|
|
3487
4055
|
failed += batch.chunks.length;
|
|
4056
|
+
this.logger.recordEmbeddingError();
|
|
3488
4057
|
stillFailing.push({
|
|
3489
4058
|
...batch,
|
|
3490
4059
|
attemptCount: batch.attemptCount + 1,
|
|
@@ -3519,6 +4088,9 @@ var Indexer = class {
|
|
|
3519
4088
|
const { database } = await this.ensureInitialized();
|
|
3520
4089
|
return database.getStats();
|
|
3521
4090
|
}
|
|
4091
|
+
getLogger() {
|
|
4092
|
+
return this.logger;
|
|
4093
|
+
}
|
|
3522
4094
|
};
|
|
3523
4095
|
|
|
3524
4096
|
// node_modules/chokidar/index.js
|
|
@@ -3611,7 +4183,7 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
3611
4183
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
3612
4184
|
const statMethod = opts.lstat ? import_promises.lstat : import_promises.stat;
|
|
3613
4185
|
if (wantBigintFsStats) {
|
|
3614
|
-
this._stat = (
|
|
4186
|
+
this._stat = (path9) => statMethod(path9, { bigint: true });
|
|
3615
4187
|
} else {
|
|
3616
4188
|
this._stat = statMethod;
|
|
3617
4189
|
}
|
|
@@ -3636,8 +4208,8 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
3636
4208
|
const par = this.parent;
|
|
3637
4209
|
const fil = par && par.files;
|
|
3638
4210
|
if (fil && fil.length > 0) {
|
|
3639
|
-
const { path:
|
|
3640
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
4211
|
+
const { path: path9, depth } = par;
|
|
4212
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path9));
|
|
3641
4213
|
const awaited = await Promise.all(slice);
|
|
3642
4214
|
for (const entry of awaited) {
|
|
3643
4215
|
if (!entry)
|
|
@@ -3677,21 +4249,21 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
3677
4249
|
this.reading = false;
|
|
3678
4250
|
}
|
|
3679
4251
|
}
|
|
3680
|
-
async _exploreDir(
|
|
4252
|
+
async _exploreDir(path9, depth) {
|
|
3681
4253
|
let files;
|
|
3682
4254
|
try {
|
|
3683
|
-
files = await (0, import_promises.readdir)(
|
|
4255
|
+
files = await (0, import_promises.readdir)(path9, this._rdOptions);
|
|
3684
4256
|
} catch (error) {
|
|
3685
4257
|
this._onError(error);
|
|
3686
4258
|
}
|
|
3687
|
-
return { files, depth, path:
|
|
4259
|
+
return { files, depth, path: path9 };
|
|
3688
4260
|
}
|
|
3689
|
-
async _formatEntry(dirent,
|
|
4261
|
+
async _formatEntry(dirent, path9) {
|
|
3690
4262
|
let entry;
|
|
3691
|
-
const
|
|
4263
|
+
const basename4 = this._isDirent ? dirent.name : dirent;
|
|
3692
4264
|
try {
|
|
3693
|
-
const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(
|
|
3694
|
-
entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename:
|
|
4265
|
+
const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path9, basename4));
|
|
4266
|
+
entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename4 };
|
|
3695
4267
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
3696
4268
|
} catch (err) {
|
|
3697
4269
|
this._onError(err);
|
|
@@ -4090,16 +4662,16 @@ var delFromSet = (main, prop, item) => {
|
|
|
4090
4662
|
};
|
|
4091
4663
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
4092
4664
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
4093
|
-
function createFsWatchInstance(
|
|
4665
|
+
function createFsWatchInstance(path9, options, listener, errHandler, emitRaw) {
|
|
4094
4666
|
const handleEvent = (rawEvent, evPath) => {
|
|
4095
|
-
listener(
|
|
4096
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
4097
|
-
if (evPath &&
|
|
4098
|
-
fsWatchBroadcast(sp.resolve(
|
|
4667
|
+
listener(path9);
|
|
4668
|
+
emitRaw(rawEvent, evPath, { watchedPath: path9 });
|
|
4669
|
+
if (evPath && path9 !== evPath) {
|
|
4670
|
+
fsWatchBroadcast(sp.resolve(path9, evPath), KEY_LISTENERS, sp.join(path9, evPath));
|
|
4099
4671
|
}
|
|
4100
4672
|
};
|
|
4101
4673
|
try {
|
|
4102
|
-
return (0, import_node_fs.watch)(
|
|
4674
|
+
return (0, import_node_fs.watch)(path9, {
|
|
4103
4675
|
persistent: options.persistent
|
|
4104
4676
|
}, handleEvent);
|
|
4105
4677
|
} catch (error) {
|
|
@@ -4115,12 +4687,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
4115
4687
|
listener(val1, val2, val3);
|
|
4116
4688
|
});
|
|
4117
4689
|
};
|
|
4118
|
-
var setFsWatchListener = (
|
|
4690
|
+
var setFsWatchListener = (path9, fullPath, options, handlers) => {
|
|
4119
4691
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
4120
4692
|
let cont = FsWatchInstances.get(fullPath);
|
|
4121
4693
|
let watcher;
|
|
4122
4694
|
if (!options.persistent) {
|
|
4123
|
-
watcher = createFsWatchInstance(
|
|
4695
|
+
watcher = createFsWatchInstance(path9, options, listener, errHandler, rawEmitter);
|
|
4124
4696
|
if (!watcher)
|
|
4125
4697
|
return;
|
|
4126
4698
|
return watcher.close.bind(watcher);
|
|
@@ -4131,7 +4703,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
|
|
|
4131
4703
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
4132
4704
|
} else {
|
|
4133
4705
|
watcher = createFsWatchInstance(
|
|
4134
|
-
|
|
4706
|
+
path9,
|
|
4135
4707
|
options,
|
|
4136
4708
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
4137
4709
|
errHandler,
|
|
@@ -4146,7 +4718,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
|
|
|
4146
4718
|
cont.watcherUnusable = true;
|
|
4147
4719
|
if (isWindows && error.code === "EPERM") {
|
|
4148
4720
|
try {
|
|
4149
|
-
const fd = await (0, import_promises2.open)(
|
|
4721
|
+
const fd = await (0, import_promises2.open)(path9, "r");
|
|
4150
4722
|
await fd.close();
|
|
4151
4723
|
broadcastErr(error);
|
|
4152
4724
|
} catch (err) {
|
|
@@ -4177,7 +4749,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
|
|
|
4177
4749
|
};
|
|
4178
4750
|
};
|
|
4179
4751
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
4180
|
-
var setFsWatchFileListener = (
|
|
4752
|
+
var setFsWatchFileListener = (path9, fullPath, options, handlers) => {
|
|
4181
4753
|
const { listener, rawEmitter } = handlers;
|
|
4182
4754
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
4183
4755
|
const copts = cont && cont.options;
|
|
@@ -4199,7 +4771,7 @@ var setFsWatchFileListener = (path8, fullPath, options, handlers) => {
|
|
|
4199
4771
|
});
|
|
4200
4772
|
const currmtime = curr.mtimeMs;
|
|
4201
4773
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
4202
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
4774
|
+
foreach(cont.listeners, (listener2) => listener2(path9, curr));
|
|
4203
4775
|
}
|
|
4204
4776
|
})
|
|
4205
4777
|
};
|
|
@@ -4229,13 +4801,13 @@ var NodeFsHandler = class {
|
|
|
4229
4801
|
* @param listener on fs change
|
|
4230
4802
|
* @returns closer for the watcher instance
|
|
4231
4803
|
*/
|
|
4232
|
-
_watchWithNodeFs(
|
|
4804
|
+
_watchWithNodeFs(path9, listener) {
|
|
4233
4805
|
const opts = this.fsw.options;
|
|
4234
|
-
const directory = sp.dirname(
|
|
4235
|
-
const
|
|
4806
|
+
const directory = sp.dirname(path9);
|
|
4807
|
+
const basename4 = sp.basename(path9);
|
|
4236
4808
|
const parent = this.fsw._getWatchedDir(directory);
|
|
4237
|
-
parent.add(
|
|
4238
|
-
const absolutePath = sp.resolve(
|
|
4809
|
+
parent.add(basename4);
|
|
4810
|
+
const absolutePath = sp.resolve(path9);
|
|
4239
4811
|
const options = {
|
|
4240
4812
|
persistent: opts.persistent
|
|
4241
4813
|
};
|
|
@@ -4244,13 +4816,13 @@ var NodeFsHandler = class {
|
|
|
4244
4816
|
let closer;
|
|
4245
4817
|
if (opts.usePolling) {
|
|
4246
4818
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
4247
|
-
options.interval = enableBin && isBinaryPath(
|
|
4248
|
-
closer = setFsWatchFileListener(
|
|
4819
|
+
options.interval = enableBin && isBinaryPath(basename4) ? opts.binaryInterval : opts.interval;
|
|
4820
|
+
closer = setFsWatchFileListener(path9, absolutePath, options, {
|
|
4249
4821
|
listener,
|
|
4250
4822
|
rawEmitter: this.fsw._emitRaw
|
|
4251
4823
|
});
|
|
4252
4824
|
} else {
|
|
4253
|
-
closer = setFsWatchListener(
|
|
4825
|
+
closer = setFsWatchListener(path9, absolutePath, options, {
|
|
4254
4826
|
listener,
|
|
4255
4827
|
errHandler: this._boundHandleError,
|
|
4256
4828
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -4266,13 +4838,13 @@ var NodeFsHandler = class {
|
|
|
4266
4838
|
if (this.fsw.closed) {
|
|
4267
4839
|
return;
|
|
4268
4840
|
}
|
|
4269
|
-
const
|
|
4270
|
-
const
|
|
4271
|
-
const parent = this.fsw._getWatchedDir(
|
|
4841
|
+
const dirname5 = sp.dirname(file);
|
|
4842
|
+
const basename4 = sp.basename(file);
|
|
4843
|
+
const parent = this.fsw._getWatchedDir(dirname5);
|
|
4272
4844
|
let prevStats = stats;
|
|
4273
|
-
if (parent.has(
|
|
4845
|
+
if (parent.has(basename4))
|
|
4274
4846
|
return;
|
|
4275
|
-
const listener = async (
|
|
4847
|
+
const listener = async (path9, newStats) => {
|
|
4276
4848
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
4277
4849
|
return;
|
|
4278
4850
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -4286,18 +4858,18 @@ var NodeFsHandler = class {
|
|
|
4286
4858
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
4287
4859
|
}
|
|
4288
4860
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
4289
|
-
this.fsw._closeFile(
|
|
4861
|
+
this.fsw._closeFile(path9);
|
|
4290
4862
|
prevStats = newStats2;
|
|
4291
4863
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
4292
4864
|
if (closer2)
|
|
4293
|
-
this.fsw._addPathCloser(
|
|
4865
|
+
this.fsw._addPathCloser(path9, closer2);
|
|
4294
4866
|
} else {
|
|
4295
4867
|
prevStats = newStats2;
|
|
4296
4868
|
}
|
|
4297
4869
|
} catch (error) {
|
|
4298
|
-
this.fsw._remove(
|
|
4870
|
+
this.fsw._remove(dirname5, basename4);
|
|
4299
4871
|
}
|
|
4300
|
-
} else if (parent.has(
|
|
4872
|
+
} else if (parent.has(basename4)) {
|
|
4301
4873
|
const at = newStats.atimeMs;
|
|
4302
4874
|
const mt = newStats.mtimeMs;
|
|
4303
4875
|
if (!at || at <= mt || mt !== prevStats.mtimeMs) {
|
|
@@ -4322,7 +4894,7 @@ var NodeFsHandler = class {
|
|
|
4322
4894
|
* @param item basename of this item
|
|
4323
4895
|
* @returns true if no more processing is needed for this entry.
|
|
4324
4896
|
*/
|
|
4325
|
-
async _handleSymlink(entry, directory,
|
|
4897
|
+
async _handleSymlink(entry, directory, path9, item) {
|
|
4326
4898
|
if (this.fsw.closed) {
|
|
4327
4899
|
return;
|
|
4328
4900
|
}
|
|
@@ -4332,7 +4904,7 @@ var NodeFsHandler = class {
|
|
|
4332
4904
|
this.fsw._incrReadyCount();
|
|
4333
4905
|
let linkPath;
|
|
4334
4906
|
try {
|
|
4335
|
-
linkPath = await (0, import_promises2.realpath)(
|
|
4907
|
+
linkPath = await (0, import_promises2.realpath)(path9);
|
|
4336
4908
|
} catch (e) {
|
|
4337
4909
|
this.fsw._emitReady();
|
|
4338
4910
|
return true;
|
|
@@ -4342,12 +4914,12 @@ var NodeFsHandler = class {
|
|
|
4342
4914
|
if (dir.has(item)) {
|
|
4343
4915
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
4344
4916
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
4345
|
-
this.fsw._emit(EV.CHANGE,
|
|
4917
|
+
this.fsw._emit(EV.CHANGE, path9, entry.stats);
|
|
4346
4918
|
}
|
|
4347
4919
|
} else {
|
|
4348
4920
|
dir.add(item);
|
|
4349
4921
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
4350
|
-
this.fsw._emit(EV.ADD,
|
|
4922
|
+
this.fsw._emit(EV.ADD, path9, entry.stats);
|
|
4351
4923
|
}
|
|
4352
4924
|
this.fsw._emitReady();
|
|
4353
4925
|
return true;
|
|
@@ -4377,9 +4949,9 @@ var NodeFsHandler = class {
|
|
|
4377
4949
|
return;
|
|
4378
4950
|
}
|
|
4379
4951
|
const item = entry.path;
|
|
4380
|
-
let
|
|
4952
|
+
let path9 = sp.join(directory, item);
|
|
4381
4953
|
current.add(item);
|
|
4382
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
4954
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path9, item)) {
|
|
4383
4955
|
return;
|
|
4384
4956
|
}
|
|
4385
4957
|
if (this.fsw.closed) {
|
|
@@ -4388,8 +4960,8 @@ var NodeFsHandler = class {
|
|
|
4388
4960
|
}
|
|
4389
4961
|
if (item === target || !target && !previous.has(item)) {
|
|
4390
4962
|
this.fsw._incrReadyCount();
|
|
4391
|
-
|
|
4392
|
-
this._addToNodeFs(
|
|
4963
|
+
path9 = sp.join(dir, sp.relative(dir, path9));
|
|
4964
|
+
this._addToNodeFs(path9, initialAdd, wh, depth + 1);
|
|
4393
4965
|
}
|
|
4394
4966
|
}).on(EV.ERROR, this._boundHandleError);
|
|
4395
4967
|
return new Promise((resolve4, reject) => {
|
|
@@ -4458,13 +5030,13 @@ var NodeFsHandler = class {
|
|
|
4458
5030
|
* @param depth Child path actually targeted for watch
|
|
4459
5031
|
* @param target Child path actually targeted for watch
|
|
4460
5032
|
*/
|
|
4461
|
-
async _addToNodeFs(
|
|
5033
|
+
async _addToNodeFs(path9, initialAdd, priorWh, depth, target) {
|
|
4462
5034
|
const ready = this.fsw._emitReady;
|
|
4463
|
-
if (this.fsw._isIgnored(
|
|
5035
|
+
if (this.fsw._isIgnored(path9) || this.fsw.closed) {
|
|
4464
5036
|
ready();
|
|
4465
5037
|
return false;
|
|
4466
5038
|
}
|
|
4467
|
-
const wh = this.fsw._getWatchHelpers(
|
|
5039
|
+
const wh = this.fsw._getWatchHelpers(path9);
|
|
4468
5040
|
if (priorWh) {
|
|
4469
5041
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
4470
5042
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -4480,8 +5052,8 @@ var NodeFsHandler = class {
|
|
|
4480
5052
|
const follow = this.fsw.options.followSymlinks;
|
|
4481
5053
|
let closer;
|
|
4482
5054
|
if (stats.isDirectory()) {
|
|
4483
|
-
const absPath = sp.resolve(
|
|
4484
|
-
const targetPath = follow ? await (0, import_promises2.realpath)(
|
|
5055
|
+
const absPath = sp.resolve(path9);
|
|
5056
|
+
const targetPath = follow ? await (0, import_promises2.realpath)(path9) : path9;
|
|
4485
5057
|
if (this.fsw.closed)
|
|
4486
5058
|
return;
|
|
4487
5059
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -4491,29 +5063,29 @@ var NodeFsHandler = class {
|
|
|
4491
5063
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
4492
5064
|
}
|
|
4493
5065
|
} else if (stats.isSymbolicLink()) {
|
|
4494
|
-
const targetPath = follow ? await (0, import_promises2.realpath)(
|
|
5066
|
+
const targetPath = follow ? await (0, import_promises2.realpath)(path9) : path9;
|
|
4495
5067
|
if (this.fsw.closed)
|
|
4496
5068
|
return;
|
|
4497
5069
|
const parent = sp.dirname(wh.watchPath);
|
|
4498
5070
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
4499
5071
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
4500
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
5072
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path9, wh, targetPath);
|
|
4501
5073
|
if (this.fsw.closed)
|
|
4502
5074
|
return;
|
|
4503
5075
|
if (targetPath !== void 0) {
|
|
4504
|
-
this.fsw._symlinkPaths.set(sp.resolve(
|
|
5076
|
+
this.fsw._symlinkPaths.set(sp.resolve(path9), targetPath);
|
|
4505
5077
|
}
|
|
4506
5078
|
} else {
|
|
4507
5079
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
4508
5080
|
}
|
|
4509
5081
|
ready();
|
|
4510
5082
|
if (closer)
|
|
4511
|
-
this.fsw._addPathCloser(
|
|
5083
|
+
this.fsw._addPathCloser(path9, closer);
|
|
4512
5084
|
return false;
|
|
4513
5085
|
} catch (error) {
|
|
4514
5086
|
if (this.fsw._handleError(error)) {
|
|
4515
5087
|
ready();
|
|
4516
|
-
return
|
|
5088
|
+
return path9;
|
|
4517
5089
|
}
|
|
4518
5090
|
}
|
|
4519
5091
|
}
|
|
@@ -4556,24 +5128,24 @@ function createPattern(matcher) {
|
|
|
4556
5128
|
}
|
|
4557
5129
|
return () => false;
|
|
4558
5130
|
}
|
|
4559
|
-
function normalizePath(
|
|
4560
|
-
if (typeof
|
|
5131
|
+
function normalizePath(path9) {
|
|
5132
|
+
if (typeof path9 !== "string")
|
|
4561
5133
|
throw new Error("string expected");
|
|
4562
|
-
|
|
4563
|
-
|
|
5134
|
+
path9 = sp2.normalize(path9);
|
|
5135
|
+
path9 = path9.replace(/\\/g, "/");
|
|
4564
5136
|
let prepend = false;
|
|
4565
|
-
if (
|
|
5137
|
+
if (path9.startsWith("//"))
|
|
4566
5138
|
prepend = true;
|
|
4567
|
-
|
|
5139
|
+
path9 = path9.replace(DOUBLE_SLASH_RE, "/");
|
|
4568
5140
|
if (prepend)
|
|
4569
|
-
|
|
4570
|
-
return
|
|
5141
|
+
path9 = "/" + path9;
|
|
5142
|
+
return path9;
|
|
4571
5143
|
}
|
|
4572
5144
|
function matchPatterns(patterns, testString, stats) {
|
|
4573
|
-
const
|
|
5145
|
+
const path9 = normalizePath(testString);
|
|
4574
5146
|
for (let index = 0; index < patterns.length; index++) {
|
|
4575
5147
|
const pattern = patterns[index];
|
|
4576
|
-
if (pattern(
|
|
5148
|
+
if (pattern(path9, stats)) {
|
|
4577
5149
|
return true;
|
|
4578
5150
|
}
|
|
4579
5151
|
}
|
|
@@ -4611,19 +5183,19 @@ var toUnix = (string) => {
|
|
|
4611
5183
|
}
|
|
4612
5184
|
return str;
|
|
4613
5185
|
};
|
|
4614
|
-
var normalizePathToUnix = (
|
|
4615
|
-
var normalizeIgnored = (cwd = "") => (
|
|
4616
|
-
if (typeof
|
|
4617
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
5186
|
+
var normalizePathToUnix = (path9) => toUnix(sp2.normalize(toUnix(path9)));
|
|
5187
|
+
var normalizeIgnored = (cwd = "") => (path9) => {
|
|
5188
|
+
if (typeof path9 === "string") {
|
|
5189
|
+
return normalizePathToUnix(sp2.isAbsolute(path9) ? path9 : sp2.join(cwd, path9));
|
|
4618
5190
|
} else {
|
|
4619
|
-
return
|
|
5191
|
+
return path9;
|
|
4620
5192
|
}
|
|
4621
5193
|
};
|
|
4622
|
-
var getAbsolutePath = (
|
|
4623
|
-
if (sp2.isAbsolute(
|
|
4624
|
-
return
|
|
5194
|
+
var getAbsolutePath = (path9, cwd) => {
|
|
5195
|
+
if (sp2.isAbsolute(path9)) {
|
|
5196
|
+
return path9;
|
|
4625
5197
|
}
|
|
4626
|
-
return sp2.join(cwd,
|
|
5198
|
+
return sp2.join(cwd, path9);
|
|
4627
5199
|
};
|
|
4628
5200
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
4629
5201
|
var DirEntry = class {
|
|
@@ -4688,10 +5260,10 @@ var WatchHelper = class {
|
|
|
4688
5260
|
dirParts;
|
|
4689
5261
|
followSymlinks;
|
|
4690
5262
|
statMethod;
|
|
4691
|
-
constructor(
|
|
5263
|
+
constructor(path9, follow, fsw) {
|
|
4692
5264
|
this.fsw = fsw;
|
|
4693
|
-
const watchPath =
|
|
4694
|
-
this.path =
|
|
5265
|
+
const watchPath = path9;
|
|
5266
|
+
this.path = path9 = path9.replace(REPLACER_RE, "");
|
|
4695
5267
|
this.watchPath = watchPath;
|
|
4696
5268
|
this.fullWatchPath = sp2.resolve(watchPath);
|
|
4697
5269
|
this.dirParts = [];
|
|
@@ -4831,20 +5403,20 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
4831
5403
|
this._closePromise = void 0;
|
|
4832
5404
|
let paths = unifyPaths(paths_);
|
|
4833
5405
|
if (cwd) {
|
|
4834
|
-
paths = paths.map((
|
|
4835
|
-
const absPath = getAbsolutePath(
|
|
5406
|
+
paths = paths.map((path9) => {
|
|
5407
|
+
const absPath = getAbsolutePath(path9, cwd);
|
|
4836
5408
|
return absPath;
|
|
4837
5409
|
});
|
|
4838
5410
|
}
|
|
4839
|
-
paths.forEach((
|
|
4840
|
-
this._removeIgnoredPath(
|
|
5411
|
+
paths.forEach((path9) => {
|
|
5412
|
+
this._removeIgnoredPath(path9);
|
|
4841
5413
|
});
|
|
4842
5414
|
this._userIgnored = void 0;
|
|
4843
5415
|
if (!this._readyCount)
|
|
4844
5416
|
this._readyCount = 0;
|
|
4845
5417
|
this._readyCount += paths.length;
|
|
4846
|
-
Promise.all(paths.map(async (
|
|
4847
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
5418
|
+
Promise.all(paths.map(async (path9) => {
|
|
5419
|
+
const res = await this._nodeFsHandler._addToNodeFs(path9, !_internal, void 0, 0, _origAdd);
|
|
4848
5420
|
if (res)
|
|
4849
5421
|
this._emitReady();
|
|
4850
5422
|
return res;
|
|
@@ -4866,17 +5438,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
4866
5438
|
return this;
|
|
4867
5439
|
const paths = unifyPaths(paths_);
|
|
4868
5440
|
const { cwd } = this.options;
|
|
4869
|
-
paths.forEach((
|
|
4870
|
-
if (!sp2.isAbsolute(
|
|
5441
|
+
paths.forEach((path9) => {
|
|
5442
|
+
if (!sp2.isAbsolute(path9) && !this._closers.has(path9)) {
|
|
4871
5443
|
if (cwd)
|
|
4872
|
-
|
|
4873
|
-
|
|
5444
|
+
path9 = sp2.join(cwd, path9);
|
|
5445
|
+
path9 = sp2.resolve(path9);
|
|
4874
5446
|
}
|
|
4875
|
-
this._closePath(
|
|
4876
|
-
this._addIgnoredPath(
|
|
4877
|
-
if (this._watched.has(
|
|
5447
|
+
this._closePath(path9);
|
|
5448
|
+
this._addIgnoredPath(path9);
|
|
5449
|
+
if (this._watched.has(path9)) {
|
|
4878
5450
|
this._addIgnoredPath({
|
|
4879
|
-
path:
|
|
5451
|
+
path: path9,
|
|
4880
5452
|
recursive: true
|
|
4881
5453
|
});
|
|
4882
5454
|
}
|
|
@@ -4940,38 +5512,38 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
4940
5512
|
* @param stats arguments to be passed with event
|
|
4941
5513
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
4942
5514
|
*/
|
|
4943
|
-
async _emit(event,
|
|
5515
|
+
async _emit(event, path9, stats) {
|
|
4944
5516
|
if (this.closed)
|
|
4945
5517
|
return;
|
|
4946
5518
|
const opts = this.options;
|
|
4947
5519
|
if (isWindows)
|
|
4948
|
-
|
|
5520
|
+
path9 = sp2.normalize(path9);
|
|
4949
5521
|
if (opts.cwd)
|
|
4950
|
-
|
|
4951
|
-
const args = [
|
|
5522
|
+
path9 = sp2.relative(opts.cwd, path9);
|
|
5523
|
+
const args = [path9];
|
|
4952
5524
|
if (stats != null)
|
|
4953
5525
|
args.push(stats);
|
|
4954
5526
|
const awf = opts.awaitWriteFinish;
|
|
4955
5527
|
let pw;
|
|
4956
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
5528
|
+
if (awf && (pw = this._pendingWrites.get(path9))) {
|
|
4957
5529
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
4958
5530
|
return this;
|
|
4959
5531
|
}
|
|
4960
5532
|
if (opts.atomic) {
|
|
4961
5533
|
if (event === EVENTS.UNLINK) {
|
|
4962
|
-
this._pendingUnlinks.set(
|
|
5534
|
+
this._pendingUnlinks.set(path9, [event, ...args]);
|
|
4963
5535
|
setTimeout(() => {
|
|
4964
|
-
this._pendingUnlinks.forEach((entry,
|
|
5536
|
+
this._pendingUnlinks.forEach((entry, path10) => {
|
|
4965
5537
|
this.emit(...entry);
|
|
4966
5538
|
this.emit(EVENTS.ALL, ...entry);
|
|
4967
|
-
this._pendingUnlinks.delete(
|
|
5539
|
+
this._pendingUnlinks.delete(path10);
|
|
4968
5540
|
});
|
|
4969
5541
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
4970
5542
|
return this;
|
|
4971
5543
|
}
|
|
4972
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
5544
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path9)) {
|
|
4973
5545
|
event = EVENTS.CHANGE;
|
|
4974
|
-
this._pendingUnlinks.delete(
|
|
5546
|
+
this._pendingUnlinks.delete(path9);
|
|
4975
5547
|
}
|
|
4976
5548
|
}
|
|
4977
5549
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -4989,16 +5561,16 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
4989
5561
|
this.emitWithAll(event, args);
|
|
4990
5562
|
}
|
|
4991
5563
|
};
|
|
4992
|
-
this._awaitWriteFinish(
|
|
5564
|
+
this._awaitWriteFinish(path9, awf.stabilityThreshold, event, awfEmit);
|
|
4993
5565
|
return this;
|
|
4994
5566
|
}
|
|
4995
5567
|
if (event === EVENTS.CHANGE) {
|
|
4996
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
5568
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path9, 50);
|
|
4997
5569
|
if (isThrottled)
|
|
4998
5570
|
return this;
|
|
4999
5571
|
}
|
|
5000
5572
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
5001
|
-
const fullPath = opts.cwd ? sp2.join(opts.cwd,
|
|
5573
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path9) : path9;
|
|
5002
5574
|
let stats2;
|
|
5003
5575
|
try {
|
|
5004
5576
|
stats2 = await (0, import_promises3.stat)(fullPath);
|
|
@@ -5029,23 +5601,23 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
5029
5601
|
* @param timeout duration of time to suppress duplicate actions
|
|
5030
5602
|
* @returns tracking object or false if action should be suppressed
|
|
5031
5603
|
*/
|
|
5032
|
-
_throttle(actionType,
|
|
5604
|
+
_throttle(actionType, path9, timeout) {
|
|
5033
5605
|
if (!this._throttled.has(actionType)) {
|
|
5034
5606
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
5035
5607
|
}
|
|
5036
5608
|
const action = this._throttled.get(actionType);
|
|
5037
5609
|
if (!action)
|
|
5038
5610
|
throw new Error("invalid throttle");
|
|
5039
|
-
const actionPath = action.get(
|
|
5611
|
+
const actionPath = action.get(path9);
|
|
5040
5612
|
if (actionPath) {
|
|
5041
5613
|
actionPath.count++;
|
|
5042
5614
|
return false;
|
|
5043
5615
|
}
|
|
5044
5616
|
let timeoutObject;
|
|
5045
5617
|
const clear = () => {
|
|
5046
|
-
const item = action.get(
|
|
5618
|
+
const item = action.get(path9);
|
|
5047
5619
|
const count = item ? item.count : 0;
|
|
5048
|
-
action.delete(
|
|
5620
|
+
action.delete(path9);
|
|
5049
5621
|
clearTimeout(timeoutObject);
|
|
5050
5622
|
if (item)
|
|
5051
5623
|
clearTimeout(item.timeoutObject);
|
|
@@ -5053,7 +5625,7 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
5053
5625
|
};
|
|
5054
5626
|
timeoutObject = setTimeout(clear, timeout);
|
|
5055
5627
|
const thr = { timeoutObject, clear, count: 0 };
|
|
5056
|
-
action.set(
|
|
5628
|
+
action.set(path9, thr);
|
|
5057
5629
|
return thr;
|
|
5058
5630
|
}
|
|
5059
5631
|
_incrReadyCount() {
|
|
@@ -5067,44 +5639,44 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
5067
5639
|
* @param event
|
|
5068
5640
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
5069
5641
|
*/
|
|
5070
|
-
_awaitWriteFinish(
|
|
5642
|
+
_awaitWriteFinish(path9, threshold, event, awfEmit) {
|
|
5071
5643
|
const awf = this.options.awaitWriteFinish;
|
|
5072
5644
|
if (typeof awf !== "object")
|
|
5073
5645
|
return;
|
|
5074
5646
|
const pollInterval = awf.pollInterval;
|
|
5075
5647
|
let timeoutHandler;
|
|
5076
|
-
let fullPath =
|
|
5077
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
5078
|
-
fullPath = sp2.join(this.options.cwd,
|
|
5648
|
+
let fullPath = path9;
|
|
5649
|
+
if (this.options.cwd && !sp2.isAbsolute(path9)) {
|
|
5650
|
+
fullPath = sp2.join(this.options.cwd, path9);
|
|
5079
5651
|
}
|
|
5080
5652
|
const now = /* @__PURE__ */ new Date();
|
|
5081
5653
|
const writes = this._pendingWrites;
|
|
5082
5654
|
function awaitWriteFinishFn(prevStat) {
|
|
5083
5655
|
(0, import_node_fs2.stat)(fullPath, (err, curStat) => {
|
|
5084
|
-
if (err || !writes.has(
|
|
5656
|
+
if (err || !writes.has(path9)) {
|
|
5085
5657
|
if (err && err.code !== "ENOENT")
|
|
5086
5658
|
awfEmit(err);
|
|
5087
5659
|
return;
|
|
5088
5660
|
}
|
|
5089
5661
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
5090
5662
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
5091
|
-
writes.get(
|
|
5663
|
+
writes.get(path9).lastChange = now2;
|
|
5092
5664
|
}
|
|
5093
|
-
const pw = writes.get(
|
|
5665
|
+
const pw = writes.get(path9);
|
|
5094
5666
|
const df = now2 - pw.lastChange;
|
|
5095
5667
|
if (df >= threshold) {
|
|
5096
|
-
writes.delete(
|
|
5668
|
+
writes.delete(path9);
|
|
5097
5669
|
awfEmit(void 0, curStat);
|
|
5098
5670
|
} else {
|
|
5099
5671
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
5100
5672
|
}
|
|
5101
5673
|
});
|
|
5102
5674
|
}
|
|
5103
|
-
if (!writes.has(
|
|
5104
|
-
writes.set(
|
|
5675
|
+
if (!writes.has(path9)) {
|
|
5676
|
+
writes.set(path9, {
|
|
5105
5677
|
lastChange: now,
|
|
5106
5678
|
cancelWait: () => {
|
|
5107
|
-
writes.delete(
|
|
5679
|
+
writes.delete(path9);
|
|
5108
5680
|
clearTimeout(timeoutHandler);
|
|
5109
5681
|
return event;
|
|
5110
5682
|
}
|
|
@@ -5115,8 +5687,8 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
5115
5687
|
/**
|
|
5116
5688
|
* Determines whether user has asked to ignore this path.
|
|
5117
5689
|
*/
|
|
5118
|
-
_isIgnored(
|
|
5119
|
-
if (this.options.atomic && DOT_RE.test(
|
|
5690
|
+
_isIgnored(path9, stats) {
|
|
5691
|
+
if (this.options.atomic && DOT_RE.test(path9))
|
|
5120
5692
|
return true;
|
|
5121
5693
|
if (!this._userIgnored) {
|
|
5122
5694
|
const { cwd } = this.options;
|
|
@@ -5126,17 +5698,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
5126
5698
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
5127
5699
|
this._userIgnored = anymatch(list, void 0);
|
|
5128
5700
|
}
|
|
5129
|
-
return this._userIgnored(
|
|
5701
|
+
return this._userIgnored(path9, stats);
|
|
5130
5702
|
}
|
|
5131
|
-
_isntIgnored(
|
|
5132
|
-
return !this._isIgnored(
|
|
5703
|
+
_isntIgnored(path9, stat4) {
|
|
5704
|
+
return !this._isIgnored(path9, stat4);
|
|
5133
5705
|
}
|
|
5134
5706
|
/**
|
|
5135
5707
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
5136
5708
|
* @param path file or directory pattern being watched
|
|
5137
5709
|
*/
|
|
5138
|
-
_getWatchHelpers(
|
|
5139
|
-
return new WatchHelper(
|
|
5710
|
+
_getWatchHelpers(path9) {
|
|
5711
|
+
return new WatchHelper(path9, this.options.followSymlinks, this);
|
|
5140
5712
|
}
|
|
5141
5713
|
// Directory helpers
|
|
5142
5714
|
// -----------------
|
|
@@ -5168,63 +5740,63 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
5168
5740
|
* @param item base path of item/directory
|
|
5169
5741
|
*/
|
|
5170
5742
|
_remove(directory, item, isDirectory) {
|
|
5171
|
-
const
|
|
5172
|
-
const fullPath = sp2.resolve(
|
|
5173
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
5174
|
-
if (!this._throttle("remove",
|
|
5743
|
+
const path9 = sp2.join(directory, item);
|
|
5744
|
+
const fullPath = sp2.resolve(path9);
|
|
5745
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path9) || this._watched.has(fullPath);
|
|
5746
|
+
if (!this._throttle("remove", path9, 100))
|
|
5175
5747
|
return;
|
|
5176
5748
|
if (!isDirectory && this._watched.size === 1) {
|
|
5177
5749
|
this.add(directory, item, true);
|
|
5178
5750
|
}
|
|
5179
|
-
const wp = this._getWatchedDir(
|
|
5751
|
+
const wp = this._getWatchedDir(path9);
|
|
5180
5752
|
const nestedDirectoryChildren = wp.getChildren();
|
|
5181
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
5753
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path9, nested));
|
|
5182
5754
|
const parent = this._getWatchedDir(directory);
|
|
5183
5755
|
const wasTracked = parent.has(item);
|
|
5184
5756
|
parent.remove(item);
|
|
5185
5757
|
if (this._symlinkPaths.has(fullPath)) {
|
|
5186
5758
|
this._symlinkPaths.delete(fullPath);
|
|
5187
5759
|
}
|
|
5188
|
-
let relPath =
|
|
5760
|
+
let relPath = path9;
|
|
5189
5761
|
if (this.options.cwd)
|
|
5190
|
-
relPath = sp2.relative(this.options.cwd,
|
|
5762
|
+
relPath = sp2.relative(this.options.cwd, path9);
|
|
5191
5763
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
5192
5764
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
5193
5765
|
if (event === EVENTS.ADD)
|
|
5194
5766
|
return;
|
|
5195
5767
|
}
|
|
5196
|
-
this._watched.delete(
|
|
5768
|
+
this._watched.delete(path9);
|
|
5197
5769
|
this._watched.delete(fullPath);
|
|
5198
5770
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
5199
|
-
if (wasTracked && !this._isIgnored(
|
|
5200
|
-
this._emit(eventName,
|
|
5201
|
-
this._closePath(
|
|
5771
|
+
if (wasTracked && !this._isIgnored(path9))
|
|
5772
|
+
this._emit(eventName, path9);
|
|
5773
|
+
this._closePath(path9);
|
|
5202
5774
|
}
|
|
5203
5775
|
/**
|
|
5204
5776
|
* Closes all watchers for a path
|
|
5205
5777
|
*/
|
|
5206
|
-
_closePath(
|
|
5207
|
-
this._closeFile(
|
|
5208
|
-
const dir = sp2.dirname(
|
|
5209
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
5778
|
+
_closePath(path9) {
|
|
5779
|
+
this._closeFile(path9);
|
|
5780
|
+
const dir = sp2.dirname(path9);
|
|
5781
|
+
this._getWatchedDir(dir).remove(sp2.basename(path9));
|
|
5210
5782
|
}
|
|
5211
5783
|
/**
|
|
5212
5784
|
* Closes only file-specific watchers
|
|
5213
5785
|
*/
|
|
5214
|
-
_closeFile(
|
|
5215
|
-
const closers = this._closers.get(
|
|
5786
|
+
_closeFile(path9) {
|
|
5787
|
+
const closers = this._closers.get(path9);
|
|
5216
5788
|
if (!closers)
|
|
5217
5789
|
return;
|
|
5218
5790
|
closers.forEach((closer) => closer());
|
|
5219
|
-
this._closers.delete(
|
|
5791
|
+
this._closers.delete(path9);
|
|
5220
5792
|
}
|
|
5221
|
-
_addPathCloser(
|
|
5793
|
+
_addPathCloser(path9, closer) {
|
|
5222
5794
|
if (!closer)
|
|
5223
5795
|
return;
|
|
5224
|
-
let list = this._closers.get(
|
|
5796
|
+
let list = this._closers.get(path9);
|
|
5225
5797
|
if (!list) {
|
|
5226
5798
|
list = [];
|
|
5227
|
-
this._closers.set(
|
|
5799
|
+
this._closers.set(path9, list);
|
|
5228
5800
|
}
|
|
5229
5801
|
list.push(closer);
|
|
5230
5802
|
}
|
|
@@ -5319,7 +5891,7 @@ var FileWatcher = class {
|
|
|
5319
5891
|
return;
|
|
5320
5892
|
}
|
|
5321
5893
|
const changes = Array.from(this.pendingChanges.entries()).map(
|
|
5322
|
-
([
|
|
5894
|
+
([path9, type]) => ({ path: path9, type })
|
|
5323
5895
|
);
|
|
5324
5896
|
this.pendingChanges.clear();
|
|
5325
5897
|
try {
|
|
@@ -5499,7 +6071,7 @@ var index_codebase = (0, import_plugin.tool)({
|
|
|
5499
6071
|
estimateOnly: z.boolean().optional().default(false).describe("Only show cost estimate without indexing"),
|
|
5500
6072
|
verbose: z.boolean().optional().default(false).describe("Show detailed info about skipped files and parsing failures")
|
|
5501
6073
|
},
|
|
5502
|
-
async execute(args) {
|
|
6074
|
+
async execute(args, context) {
|
|
5503
6075
|
const indexer = getIndexer();
|
|
5504
6076
|
if (args.estimateOnly) {
|
|
5505
6077
|
const estimate = await indexer.estimateCost();
|
|
@@ -5508,7 +6080,19 @@ var index_codebase = (0, import_plugin.tool)({
|
|
|
5508
6080
|
if (args.force) {
|
|
5509
6081
|
await indexer.clearIndex();
|
|
5510
6082
|
}
|
|
5511
|
-
const stats = await indexer.index()
|
|
6083
|
+
const stats = await indexer.index((progress) => {
|
|
6084
|
+
context.metadata({
|
|
6085
|
+
title: formatProgressTitle(progress),
|
|
6086
|
+
metadata: {
|
|
6087
|
+
phase: progress.phase,
|
|
6088
|
+
filesProcessed: progress.filesProcessed,
|
|
6089
|
+
totalFiles: progress.totalFiles,
|
|
6090
|
+
chunksProcessed: progress.chunksProcessed,
|
|
6091
|
+
totalChunks: progress.totalChunks,
|
|
6092
|
+
percentage: calculatePercentage(progress)
|
|
6093
|
+
}
|
|
6094
|
+
});
|
|
6095
|
+
});
|
|
5512
6096
|
return formatIndexStats(stats, args.verbose ?? false);
|
|
5513
6097
|
}
|
|
5514
6098
|
});
|
|
@@ -5546,6 +6130,51 @@ var index_health_check = (0, import_plugin.tool)({
|
|
|
5546
6130
|
return lines.join("\n");
|
|
5547
6131
|
}
|
|
5548
6132
|
});
|
|
6133
|
+
var index_metrics = (0, import_plugin.tool)({
|
|
6134
|
+
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.",
|
|
6135
|
+
args: {},
|
|
6136
|
+
async execute() {
|
|
6137
|
+
const indexer = getIndexer();
|
|
6138
|
+
const logger = indexer.getLogger();
|
|
6139
|
+
if (!logger.isEnabled()) {
|
|
6140
|
+
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```';
|
|
6141
|
+
}
|
|
6142
|
+
if (!logger.isMetricsEnabled()) {
|
|
6143
|
+
return 'Metrics collection is disabled. Enable it in your config:\n\n```json\n{\n "debug": {\n "enabled": true,\n "metrics": true\n }\n}\n```';
|
|
6144
|
+
}
|
|
6145
|
+
return logger.formatMetrics();
|
|
6146
|
+
}
|
|
6147
|
+
});
|
|
6148
|
+
var index_logs = (0, import_plugin.tool)({
|
|
6149
|
+
description: "Get recent debug logs from the codebase indexer. Shows timestamped log entries with level and category. Requires debug.enabled=true in config.",
|
|
6150
|
+
args: {
|
|
6151
|
+
limit: z.number().optional().default(20).describe("Maximum number of log entries to return"),
|
|
6152
|
+
category: z.enum(["search", "embedding", "cache", "gc", "branch", "general"]).optional().describe("Filter by log category"),
|
|
6153
|
+
level: z.enum(["error", "warn", "info", "debug"]).optional().describe("Filter by minimum log level")
|
|
6154
|
+
},
|
|
6155
|
+
async execute(args) {
|
|
6156
|
+
const indexer = getIndexer();
|
|
6157
|
+
const logger = indexer.getLogger();
|
|
6158
|
+
if (!logger.isEnabled()) {
|
|
6159
|
+
return 'Debug mode is disabled. Enable it in your config:\n\n```json\n{\n "debug": {\n "enabled": true\n }\n}\n```';
|
|
6160
|
+
}
|
|
6161
|
+
let logs;
|
|
6162
|
+
if (args.category) {
|
|
6163
|
+
logs = logger.getLogsByCategory(args.category, args.limit);
|
|
6164
|
+
} else if (args.level) {
|
|
6165
|
+
logs = logger.getLogsByLevel(args.level, args.limit);
|
|
6166
|
+
} else {
|
|
6167
|
+
logs = logger.getLogs(args.limit);
|
|
6168
|
+
}
|
|
6169
|
+
if (logs.length === 0) {
|
|
6170
|
+
return "No logs recorded yet. Logs are captured during indexing and search operations.";
|
|
6171
|
+
}
|
|
6172
|
+
return logs.map((l) => {
|
|
6173
|
+
const dataStr = l.data ? ` ${JSON.stringify(l.data)}` : "";
|
|
6174
|
+
return `[${l.timestamp}] [${l.level.toUpperCase()}] [${l.category}] ${l.message}${dataStr}`;
|
|
6175
|
+
}).join("\n");
|
|
6176
|
+
}
|
|
6177
|
+
});
|
|
5549
6178
|
function formatIndexStats(stats, verbose = false) {
|
|
5550
6179
|
const lines = [];
|
|
5551
6180
|
if (stats.indexedChunks === 0 && stats.removedChunks === 0) {
|
|
@@ -5607,12 +6236,91 @@ function formatStatus(status) {
|
|
|
5607
6236
|
}
|
|
5608
6237
|
return lines.join("\n");
|
|
5609
6238
|
}
|
|
6239
|
+
function formatProgressTitle(progress) {
|
|
6240
|
+
switch (progress.phase) {
|
|
6241
|
+
case "scanning":
|
|
6242
|
+
return "Scanning files...";
|
|
6243
|
+
case "parsing":
|
|
6244
|
+
return `Parsing: ${progress.filesProcessed}/${progress.totalFiles} files`;
|
|
6245
|
+
case "embedding":
|
|
6246
|
+
return `Embedding: ${progress.chunksProcessed}/${progress.totalChunks} chunks`;
|
|
6247
|
+
case "storing":
|
|
6248
|
+
return "Storing index...";
|
|
6249
|
+
case "complete":
|
|
6250
|
+
return "Indexing complete";
|
|
6251
|
+
default:
|
|
6252
|
+
return "Indexing...";
|
|
6253
|
+
}
|
|
6254
|
+
}
|
|
6255
|
+
function calculatePercentage(progress) {
|
|
6256
|
+
if (progress.phase === "scanning") return 0;
|
|
6257
|
+
if (progress.phase === "complete") return 100;
|
|
6258
|
+
if (progress.phase === "parsing") {
|
|
6259
|
+
if (progress.totalFiles === 0) return 5;
|
|
6260
|
+
return Math.round(5 + progress.filesProcessed / progress.totalFiles * 15);
|
|
6261
|
+
}
|
|
6262
|
+
if (progress.phase === "embedding") {
|
|
6263
|
+
if (progress.totalChunks === 0) return 20;
|
|
6264
|
+
return Math.round(20 + progress.chunksProcessed / progress.totalChunks * 70);
|
|
6265
|
+
}
|
|
6266
|
+
if (progress.phase === "storing") return 95;
|
|
6267
|
+
return 0;
|
|
6268
|
+
}
|
|
6269
|
+
|
|
6270
|
+
// src/commands/loader.ts
|
|
6271
|
+
var import_fs5 = require("fs");
|
|
6272
|
+
var path7 = __toESM(require("path"), 1);
|
|
6273
|
+
function parseFrontmatter(content) {
|
|
6274
|
+
const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
|
|
6275
|
+
const match = content.match(frontmatterRegex);
|
|
6276
|
+
if (!match) {
|
|
6277
|
+
return { frontmatter: {}, body: content.trim() };
|
|
6278
|
+
}
|
|
6279
|
+
const frontmatterLines = match[1].split("\n");
|
|
6280
|
+
const frontmatter = {};
|
|
6281
|
+
for (const line of frontmatterLines) {
|
|
6282
|
+
const colonIndex = line.indexOf(":");
|
|
6283
|
+
if (colonIndex > 0) {
|
|
6284
|
+
const key = line.slice(0, colonIndex).trim();
|
|
6285
|
+
const value = line.slice(colonIndex + 1).trim();
|
|
6286
|
+
frontmatter[key] = value;
|
|
6287
|
+
}
|
|
6288
|
+
}
|
|
6289
|
+
return { frontmatter, body: match[2].trim() };
|
|
6290
|
+
}
|
|
6291
|
+
function loadCommandsFromDirectory(commandsDir) {
|
|
6292
|
+
const commands = /* @__PURE__ */ new Map();
|
|
6293
|
+
if (!(0, import_fs5.existsSync)(commandsDir)) {
|
|
6294
|
+
return commands;
|
|
6295
|
+
}
|
|
6296
|
+
const files = (0, import_fs5.readdirSync)(commandsDir).filter((f) => f.endsWith(".md"));
|
|
6297
|
+
for (const file of files) {
|
|
6298
|
+
const filePath = path7.join(commandsDir, file);
|
|
6299
|
+
const content = (0, import_fs5.readFileSync)(filePath, "utf-8");
|
|
6300
|
+
const { frontmatter, body } = parseFrontmatter(content);
|
|
6301
|
+
const name = path7.basename(file, ".md");
|
|
6302
|
+
const description = frontmatter.description || `Run the ${name} command`;
|
|
6303
|
+
commands.set(name, {
|
|
6304
|
+
description,
|
|
6305
|
+
template: body
|
|
6306
|
+
});
|
|
6307
|
+
}
|
|
6308
|
+
return commands;
|
|
6309
|
+
}
|
|
5610
6310
|
|
|
5611
6311
|
// src/index.ts
|
|
6312
|
+
var import_meta2 = {};
|
|
6313
|
+
function getCommandsDir() {
|
|
6314
|
+
let currentDir = process.cwd();
|
|
6315
|
+
if (typeof import_meta2 !== "undefined" && import_meta2.url) {
|
|
6316
|
+
currentDir = path8.dirname((0, import_url2.fileURLToPath)(import_meta2.url));
|
|
6317
|
+
}
|
|
6318
|
+
return path8.join(currentDir, "..", "commands");
|
|
6319
|
+
}
|
|
5612
6320
|
function loadJsonFile(filePath) {
|
|
5613
6321
|
try {
|
|
5614
|
-
if ((0,
|
|
5615
|
-
const content = (0,
|
|
6322
|
+
if ((0, import_fs6.existsSync)(filePath)) {
|
|
6323
|
+
const content = (0, import_fs6.readFileSync)(filePath, "utf-8");
|
|
5616
6324
|
return JSON.parse(content);
|
|
5617
6325
|
}
|
|
5618
6326
|
} catch {
|
|
@@ -5620,11 +6328,11 @@ function loadJsonFile(filePath) {
|
|
|
5620
6328
|
return null;
|
|
5621
6329
|
}
|
|
5622
6330
|
function loadPluginConfig(projectRoot) {
|
|
5623
|
-
const projectConfig = loadJsonFile(
|
|
6331
|
+
const projectConfig = loadJsonFile(path8.join(projectRoot, ".opencode", "codebase-index.json"));
|
|
5624
6332
|
if (projectConfig) {
|
|
5625
6333
|
return projectConfig;
|
|
5626
6334
|
}
|
|
5627
|
-
const globalConfigPath =
|
|
6335
|
+
const globalConfigPath = path8.join(os3.homedir(), ".config", "opencode", "codebase-index.json");
|
|
5628
6336
|
const globalConfig = loadJsonFile(globalConfigPath);
|
|
5629
6337
|
if (globalConfig) {
|
|
5630
6338
|
return globalConfig;
|
|
@@ -5652,40 +6360,17 @@ var plugin = async ({ directory }) => {
|
|
|
5652
6360
|
codebase_search,
|
|
5653
6361
|
index_codebase,
|
|
5654
6362
|
index_status,
|
|
5655
|
-
index_health_check
|
|
6363
|
+
index_health_check,
|
|
6364
|
+
index_metrics,
|
|
6365
|
+
index_logs
|
|
5656
6366
|
},
|
|
5657
6367
|
async config(cfg) {
|
|
5658
6368
|
cfg.command = cfg.command ?? {};
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
Return the most relevant results with file paths and line numbers.`
|
|
5666
|
-
};
|
|
5667
|
-
cfg.command["find"] = {
|
|
5668
|
-
description: "Find code using hybrid approach (semantic + grep)",
|
|
5669
|
-
template: `Find code related to: $ARGUMENTS
|
|
5670
|
-
|
|
5671
|
-
Strategy:
|
|
5672
|
-
1. First use \`codebase_search\` to find semantically related code
|
|
5673
|
-
2. From the results, identify specific function/class names
|
|
5674
|
-
3. Use grep to find all occurrences of those identifiers
|
|
5675
|
-
4. Combine findings into a comprehensive answer
|
|
5676
|
-
|
|
5677
|
-
If the semantic index doesn't exist, run \`index_codebase\` first.`
|
|
5678
|
-
};
|
|
5679
|
-
cfg.command["index"] = {
|
|
5680
|
-
description: "Index the codebase for semantic search",
|
|
5681
|
-
template: `Run the \`index_codebase\` tool to create or update the semantic search index.
|
|
5682
|
-
|
|
5683
|
-
Show progress and final statistics including:
|
|
5684
|
-
- Number of files processed
|
|
5685
|
-
- Number of chunks indexed
|
|
5686
|
-
- Tokens used
|
|
5687
|
-
- Duration`
|
|
5688
|
-
};
|
|
6369
|
+
const commandsDir = getCommandsDir();
|
|
6370
|
+
const commands = loadCommandsFromDirectory(commandsDir);
|
|
6371
|
+
for (const [name, definition] of commands) {
|
|
6372
|
+
cfg.command[name] = definition;
|
|
6373
|
+
}
|
|
5689
6374
|
}
|
|
5690
6375
|
};
|
|
5691
6376
|
};
|