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.js
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.
|
|
@@ -652,9 +652,10 @@ var require_ignore = __commonJS({
|
|
|
652
652
|
});
|
|
653
653
|
|
|
654
654
|
// src/index.ts
|
|
655
|
-
import { existsSync as
|
|
656
|
-
import * as
|
|
655
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
|
|
656
|
+
import * as path8 from "path";
|
|
657
657
|
import * as os3 from "os";
|
|
658
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
658
659
|
|
|
659
660
|
// src/config/schema.ts
|
|
660
661
|
var DEFAULT_INCLUDE = [
|
|
@@ -692,7 +693,10 @@ function getDefaultIndexingConfig() {
|
|
|
692
693
|
maxChunksPerFile: 100,
|
|
693
694
|
semanticOnly: false,
|
|
694
695
|
retries: 3,
|
|
695
|
-
retryDelayMs: 1e3
|
|
696
|
+
retryDelayMs: 1e3,
|
|
697
|
+
autoGc: true,
|
|
698
|
+
gcIntervalDays: 7,
|
|
699
|
+
gcOrphanThreshold: 100
|
|
696
700
|
};
|
|
697
701
|
}
|
|
698
702
|
function getDefaultSearchConfig() {
|
|
@@ -704,8 +708,21 @@ function getDefaultSearchConfig() {
|
|
|
704
708
|
contextLines: 0
|
|
705
709
|
};
|
|
706
710
|
}
|
|
711
|
+
function getDefaultDebugConfig() {
|
|
712
|
+
return {
|
|
713
|
+
enabled: false,
|
|
714
|
+
logLevel: "info",
|
|
715
|
+
logSearch: true,
|
|
716
|
+
logEmbedding: true,
|
|
717
|
+
logCache: true,
|
|
718
|
+
logGc: true,
|
|
719
|
+
logBranch: true,
|
|
720
|
+
metrics: true
|
|
721
|
+
};
|
|
722
|
+
}
|
|
707
723
|
var VALID_PROVIDERS = ["auto", "github-copilot", "openai", "google", "ollama"];
|
|
708
724
|
var VALID_SCOPES = ["project", "global"];
|
|
725
|
+
var VALID_LOG_LEVELS = ["error", "warn", "info", "debug"];
|
|
709
726
|
function isValidProvider(value) {
|
|
710
727
|
return typeof value === "string" && VALID_PROVIDERS.includes(value);
|
|
711
728
|
}
|
|
@@ -715,10 +732,14 @@ function isValidScope(value) {
|
|
|
715
732
|
function isStringArray(value) {
|
|
716
733
|
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
717
734
|
}
|
|
735
|
+
function isValidLogLevel(value) {
|
|
736
|
+
return typeof value === "string" && VALID_LOG_LEVELS.includes(value);
|
|
737
|
+
}
|
|
718
738
|
function parseConfig(raw) {
|
|
719
739
|
const input = raw && typeof raw === "object" ? raw : {};
|
|
720
740
|
const defaultIndexing = getDefaultIndexingConfig();
|
|
721
741
|
const defaultSearch = getDefaultSearchConfig();
|
|
742
|
+
const defaultDebug = getDefaultDebugConfig();
|
|
722
743
|
const rawIndexing = input.indexing && typeof input.indexing === "object" ? input.indexing : {};
|
|
723
744
|
const indexing = {
|
|
724
745
|
autoIndex: typeof rawIndexing.autoIndex === "boolean" ? rawIndexing.autoIndex : defaultIndexing.autoIndex,
|
|
@@ -727,7 +748,10 @@ function parseConfig(raw) {
|
|
|
727
748
|
maxChunksPerFile: typeof rawIndexing.maxChunksPerFile === "number" ? Math.max(1, rawIndexing.maxChunksPerFile) : defaultIndexing.maxChunksPerFile,
|
|
728
749
|
semanticOnly: typeof rawIndexing.semanticOnly === "boolean" ? rawIndexing.semanticOnly : defaultIndexing.semanticOnly,
|
|
729
750
|
retries: typeof rawIndexing.retries === "number" ? rawIndexing.retries : defaultIndexing.retries,
|
|
730
|
-
retryDelayMs: typeof rawIndexing.retryDelayMs === "number" ? rawIndexing.retryDelayMs : defaultIndexing.retryDelayMs
|
|
751
|
+
retryDelayMs: typeof rawIndexing.retryDelayMs === "number" ? rawIndexing.retryDelayMs : defaultIndexing.retryDelayMs,
|
|
752
|
+
autoGc: typeof rawIndexing.autoGc === "boolean" ? rawIndexing.autoGc : defaultIndexing.autoGc,
|
|
753
|
+
gcIntervalDays: typeof rawIndexing.gcIntervalDays === "number" ? Math.max(1, rawIndexing.gcIntervalDays) : defaultIndexing.gcIntervalDays,
|
|
754
|
+
gcOrphanThreshold: typeof rawIndexing.gcOrphanThreshold === "number" ? Math.max(0, rawIndexing.gcOrphanThreshold) : defaultIndexing.gcOrphanThreshold
|
|
731
755
|
};
|
|
732
756
|
const rawSearch = input.search && typeof input.search === "object" ? input.search : {};
|
|
733
757
|
const search = {
|
|
@@ -737,6 +761,17 @@ function parseConfig(raw) {
|
|
|
737
761
|
hybridWeight: typeof rawSearch.hybridWeight === "number" ? Math.min(1, Math.max(0, rawSearch.hybridWeight)) : defaultSearch.hybridWeight,
|
|
738
762
|
contextLines: typeof rawSearch.contextLines === "number" ? Math.min(50, Math.max(0, rawSearch.contextLines)) : defaultSearch.contextLines
|
|
739
763
|
};
|
|
764
|
+
const rawDebug = input.debug && typeof input.debug === "object" ? input.debug : {};
|
|
765
|
+
const debug = {
|
|
766
|
+
enabled: typeof rawDebug.enabled === "boolean" ? rawDebug.enabled : defaultDebug.enabled,
|
|
767
|
+
logLevel: isValidLogLevel(rawDebug.logLevel) ? rawDebug.logLevel : defaultDebug.logLevel,
|
|
768
|
+
logSearch: typeof rawDebug.logSearch === "boolean" ? rawDebug.logSearch : defaultDebug.logSearch,
|
|
769
|
+
logEmbedding: typeof rawDebug.logEmbedding === "boolean" ? rawDebug.logEmbedding : defaultDebug.logEmbedding,
|
|
770
|
+
logCache: typeof rawDebug.logCache === "boolean" ? rawDebug.logCache : defaultDebug.logCache,
|
|
771
|
+
logGc: typeof rawDebug.logGc === "boolean" ? rawDebug.logGc : defaultDebug.logGc,
|
|
772
|
+
logBranch: typeof rawDebug.logBranch === "boolean" ? rawDebug.logBranch : defaultDebug.logBranch,
|
|
773
|
+
metrics: typeof rawDebug.metrics === "boolean" ? rawDebug.metrics : defaultDebug.metrics
|
|
774
|
+
};
|
|
740
775
|
return {
|
|
741
776
|
embeddingProvider: isValidProvider(input.embeddingProvider) ? input.embeddingProvider : "auto",
|
|
742
777
|
embeddingModel: typeof input.embeddingModel === "string" ? input.embeddingModel : "auto",
|
|
@@ -744,7 +779,8 @@ function parseConfig(raw) {
|
|
|
744
779
|
include: isStringArray(input.include) ? input.include : DEFAULT_INCLUDE,
|
|
745
780
|
exclude: isStringArray(input.exclude) ? input.exclude : DEFAULT_EXCLUDE,
|
|
746
781
|
indexing,
|
|
747
|
-
search
|
|
782
|
+
search,
|
|
783
|
+
debug
|
|
748
784
|
};
|
|
749
785
|
}
|
|
750
786
|
var EMBEDDING_MODELS = {
|
|
@@ -809,6 +845,7 @@ function getDefaultModelForProvider(provider) {
|
|
|
809
845
|
// src/indexer/index.ts
|
|
810
846
|
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync, promises as fsPromises2 } from "fs";
|
|
811
847
|
import * as path5 from "path";
|
|
848
|
+
import { performance as performance2 } from "perf_hooks";
|
|
812
849
|
|
|
813
850
|
// node_modules/eventemitter3/index.mjs
|
|
814
851
|
var import_index = __toESM(require_eventemitter3(), 1);
|
|
@@ -2066,34 +2103,36 @@ var GoogleEmbeddingProvider = class {
|
|
|
2066
2103
|
};
|
|
2067
2104
|
}
|
|
2068
2105
|
async embedBatch(texts) {
|
|
2069
|
-
const
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
}
|
|
2083
|
-
}
|
|
2106
|
+
const results = await Promise.all(
|
|
2107
|
+
texts.map(async (text) => {
|
|
2108
|
+
const response = await fetch(
|
|
2109
|
+
`${this.credentials.baseUrl}/models/${this.modelInfo.model}:embedContent?key=${this.credentials.apiKey}`,
|
|
2110
|
+
{
|
|
2111
|
+
method: "POST",
|
|
2112
|
+
headers: {
|
|
2113
|
+
"Content-Type": "application/json"
|
|
2114
|
+
},
|
|
2115
|
+
body: JSON.stringify({
|
|
2116
|
+
content: {
|
|
2117
|
+
parts: [{ text }]
|
|
2118
|
+
}
|
|
2119
|
+
})
|
|
2120
|
+
}
|
|
2121
|
+
);
|
|
2122
|
+
if (!response.ok) {
|
|
2123
|
+
const error = await response.text();
|
|
2124
|
+
throw new Error(`Google embedding API error: ${response.status} - ${error}`);
|
|
2084
2125
|
}
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
totalTokens += Math.ceil(text.length / 4);
|
|
2093
|
-
}
|
|
2126
|
+
const data = await response.json();
|
|
2127
|
+
return {
|
|
2128
|
+
embedding: data.embedding.values,
|
|
2129
|
+
tokensUsed: Math.ceil(text.length / 4)
|
|
2130
|
+
};
|
|
2131
|
+
})
|
|
2132
|
+
);
|
|
2094
2133
|
return {
|
|
2095
|
-
embeddings,
|
|
2096
|
-
totalTokensUsed:
|
|
2134
|
+
embeddings: results.map((r) => r.embedding),
|
|
2135
|
+
totalTokensUsed: results.reduce((sum, r) => sum + r.tokensUsed, 0)
|
|
2097
2136
|
};
|
|
2098
2137
|
}
|
|
2099
2138
|
getModelInfo() {
|
|
@@ -2127,16 +2166,10 @@ var OllamaEmbeddingProvider = class {
|
|
|
2127
2166
|
};
|
|
2128
2167
|
}
|
|
2129
2168
|
async embedBatch(texts) {
|
|
2130
|
-
const
|
|
2131
|
-
let totalTokens = 0;
|
|
2132
|
-
for (const text of texts) {
|
|
2133
|
-
const result = await this.embed(text);
|
|
2134
|
-
embeddings.push(result.embedding);
|
|
2135
|
-
totalTokens += result.tokensUsed;
|
|
2136
|
-
}
|
|
2169
|
+
const results = await Promise.all(texts.map((text) => this.embed(text)));
|
|
2137
2170
|
return {
|
|
2138
|
-
embeddings,
|
|
2139
|
-
totalTokensUsed:
|
|
2171
|
+
embeddings: results.map((r) => r.embedding),
|
|
2172
|
+
totalTokensUsed: results.reduce((sum, r) => sum + r.tokensUsed, 0)
|
|
2140
2173
|
};
|
|
2141
2174
|
}
|
|
2142
2175
|
getModelInfo() {
|
|
@@ -2322,6 +2355,298 @@ function padRight(str, length) {
|
|
|
2322
2355
|
return str.padEnd(length);
|
|
2323
2356
|
}
|
|
2324
2357
|
|
|
2358
|
+
// src/utils/logger.ts
|
|
2359
|
+
var LOG_LEVEL_PRIORITY = {
|
|
2360
|
+
error: 0,
|
|
2361
|
+
warn: 1,
|
|
2362
|
+
info: 2,
|
|
2363
|
+
debug: 3
|
|
2364
|
+
};
|
|
2365
|
+
function createEmptyMetrics() {
|
|
2366
|
+
return {
|
|
2367
|
+
filesScanned: 0,
|
|
2368
|
+
filesParsed: 0,
|
|
2369
|
+
parseMs: 0,
|
|
2370
|
+
chunksProcessed: 0,
|
|
2371
|
+
chunksEmbedded: 0,
|
|
2372
|
+
chunksFromCache: 0,
|
|
2373
|
+
chunksRemoved: 0,
|
|
2374
|
+
embeddingApiCalls: 0,
|
|
2375
|
+
embeddingTokensUsed: 0,
|
|
2376
|
+
embeddingErrors: 0,
|
|
2377
|
+
searchCount: 0,
|
|
2378
|
+
searchTotalMs: 0,
|
|
2379
|
+
searchAvgMs: 0,
|
|
2380
|
+
searchLastMs: 0,
|
|
2381
|
+
embeddingCallMs: 0,
|
|
2382
|
+
vectorSearchMs: 0,
|
|
2383
|
+
keywordSearchMs: 0,
|
|
2384
|
+
fusionMs: 0,
|
|
2385
|
+
cacheHits: 0,
|
|
2386
|
+
cacheMisses: 0,
|
|
2387
|
+
queryCacheHits: 0,
|
|
2388
|
+
queryCacheSimilarHits: 0,
|
|
2389
|
+
queryCacheMisses: 0,
|
|
2390
|
+
gcRuns: 0,
|
|
2391
|
+
gcOrphansRemoved: 0,
|
|
2392
|
+
gcChunksRemoved: 0,
|
|
2393
|
+
gcEmbeddingsRemoved: 0
|
|
2394
|
+
};
|
|
2395
|
+
}
|
|
2396
|
+
var Logger = class {
|
|
2397
|
+
config;
|
|
2398
|
+
metrics;
|
|
2399
|
+
logs = [];
|
|
2400
|
+
maxLogs = 1e3;
|
|
2401
|
+
constructor(config) {
|
|
2402
|
+
this.config = config;
|
|
2403
|
+
this.metrics = createEmptyMetrics();
|
|
2404
|
+
}
|
|
2405
|
+
shouldLog(level) {
|
|
2406
|
+
if (!this.config.enabled) return false;
|
|
2407
|
+
return LOG_LEVEL_PRIORITY[level] <= LOG_LEVEL_PRIORITY[this.config.logLevel];
|
|
2408
|
+
}
|
|
2409
|
+
log(level, category, message, data) {
|
|
2410
|
+
if (!this.shouldLog(level)) return;
|
|
2411
|
+
const entry = {
|
|
2412
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2413
|
+
level,
|
|
2414
|
+
category,
|
|
2415
|
+
message,
|
|
2416
|
+
data
|
|
2417
|
+
};
|
|
2418
|
+
this.logs.push(entry);
|
|
2419
|
+
if (this.logs.length > this.maxLogs) {
|
|
2420
|
+
this.logs.shift();
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
search(level, message, data) {
|
|
2424
|
+
if (this.config.logSearch) {
|
|
2425
|
+
this.log(level, "search", message, data);
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
embedding(level, message, data) {
|
|
2429
|
+
if (this.config.logEmbedding) {
|
|
2430
|
+
this.log(level, "embedding", message, data);
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
cache(level, message, data) {
|
|
2434
|
+
if (this.config.logCache) {
|
|
2435
|
+
this.log(level, "cache", message, data);
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
gc(level, message, data) {
|
|
2439
|
+
if (this.config.logGc) {
|
|
2440
|
+
this.log(level, "gc", message, data);
|
|
2441
|
+
}
|
|
2442
|
+
}
|
|
2443
|
+
branch(level, message, data) {
|
|
2444
|
+
if (this.config.logBranch) {
|
|
2445
|
+
this.log(level, "branch", message, data);
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
info(message, data) {
|
|
2449
|
+
this.log("info", "general", message, data);
|
|
2450
|
+
}
|
|
2451
|
+
warn(message, data) {
|
|
2452
|
+
this.log("warn", "general", message, data);
|
|
2453
|
+
}
|
|
2454
|
+
error(message, data) {
|
|
2455
|
+
this.log("error", "general", message, data);
|
|
2456
|
+
}
|
|
2457
|
+
debug(message, data) {
|
|
2458
|
+
this.log("debug", "general", message, data);
|
|
2459
|
+
}
|
|
2460
|
+
recordIndexingStart() {
|
|
2461
|
+
if (!this.config.metrics) return;
|
|
2462
|
+
this.metrics.indexingStartTime = Date.now();
|
|
2463
|
+
}
|
|
2464
|
+
recordIndexingEnd() {
|
|
2465
|
+
if (!this.config.metrics) return;
|
|
2466
|
+
this.metrics.indexingEndTime = Date.now();
|
|
2467
|
+
}
|
|
2468
|
+
recordFilesScanned(count) {
|
|
2469
|
+
if (!this.config.metrics) return;
|
|
2470
|
+
this.metrics.filesScanned = count;
|
|
2471
|
+
}
|
|
2472
|
+
recordFilesParsed(count) {
|
|
2473
|
+
if (!this.config.metrics) return;
|
|
2474
|
+
this.metrics.filesParsed = count;
|
|
2475
|
+
}
|
|
2476
|
+
recordParseDuration(durationMs) {
|
|
2477
|
+
if (!this.config.metrics) return;
|
|
2478
|
+
this.metrics.parseMs = durationMs;
|
|
2479
|
+
}
|
|
2480
|
+
recordChunksProcessed(count) {
|
|
2481
|
+
if (!this.config.metrics) return;
|
|
2482
|
+
this.metrics.chunksProcessed += count;
|
|
2483
|
+
}
|
|
2484
|
+
recordChunksEmbedded(count) {
|
|
2485
|
+
if (!this.config.metrics) return;
|
|
2486
|
+
this.metrics.chunksEmbedded += count;
|
|
2487
|
+
}
|
|
2488
|
+
recordChunksFromCache(count) {
|
|
2489
|
+
if (!this.config.metrics) return;
|
|
2490
|
+
this.metrics.chunksFromCache += count;
|
|
2491
|
+
}
|
|
2492
|
+
recordChunksRemoved(count) {
|
|
2493
|
+
if (!this.config.metrics) return;
|
|
2494
|
+
this.metrics.chunksRemoved += count;
|
|
2495
|
+
}
|
|
2496
|
+
recordEmbeddingApiCall(tokens) {
|
|
2497
|
+
if (!this.config.metrics) return;
|
|
2498
|
+
this.metrics.embeddingApiCalls++;
|
|
2499
|
+
this.metrics.embeddingTokensUsed += tokens;
|
|
2500
|
+
}
|
|
2501
|
+
recordEmbeddingError() {
|
|
2502
|
+
if (!this.config.metrics) return;
|
|
2503
|
+
this.metrics.embeddingErrors++;
|
|
2504
|
+
}
|
|
2505
|
+
recordSearch(durationMs, breakdown) {
|
|
2506
|
+
if (!this.config.metrics) return;
|
|
2507
|
+
this.metrics.searchCount++;
|
|
2508
|
+
this.metrics.searchTotalMs += durationMs;
|
|
2509
|
+
this.metrics.searchLastMs = durationMs;
|
|
2510
|
+
this.metrics.searchAvgMs = this.metrics.searchTotalMs / this.metrics.searchCount;
|
|
2511
|
+
if (breakdown) {
|
|
2512
|
+
this.metrics.embeddingCallMs = breakdown.embeddingMs;
|
|
2513
|
+
this.metrics.vectorSearchMs = breakdown.vectorMs;
|
|
2514
|
+
this.metrics.keywordSearchMs = breakdown.keywordMs;
|
|
2515
|
+
this.metrics.fusionMs = breakdown.fusionMs;
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
recordCacheHit() {
|
|
2519
|
+
if (!this.config.metrics) return;
|
|
2520
|
+
this.metrics.cacheHits++;
|
|
2521
|
+
}
|
|
2522
|
+
recordCacheMiss() {
|
|
2523
|
+
if (!this.config.metrics) return;
|
|
2524
|
+
this.metrics.cacheMisses++;
|
|
2525
|
+
}
|
|
2526
|
+
recordQueryCacheHit() {
|
|
2527
|
+
if (!this.config.metrics) return;
|
|
2528
|
+
this.metrics.queryCacheHits++;
|
|
2529
|
+
}
|
|
2530
|
+
recordQueryCacheSimilarHit() {
|
|
2531
|
+
if (!this.config.metrics) return;
|
|
2532
|
+
this.metrics.queryCacheSimilarHits++;
|
|
2533
|
+
}
|
|
2534
|
+
recordQueryCacheMiss() {
|
|
2535
|
+
if (!this.config.metrics) return;
|
|
2536
|
+
this.metrics.queryCacheMisses++;
|
|
2537
|
+
}
|
|
2538
|
+
recordGc(orphans, chunks, embeddings) {
|
|
2539
|
+
if (!this.config.metrics) return;
|
|
2540
|
+
this.metrics.gcRuns++;
|
|
2541
|
+
this.metrics.gcOrphansRemoved += orphans;
|
|
2542
|
+
this.metrics.gcChunksRemoved += chunks;
|
|
2543
|
+
this.metrics.gcEmbeddingsRemoved += embeddings;
|
|
2544
|
+
}
|
|
2545
|
+
getMetrics() {
|
|
2546
|
+
return { ...this.metrics };
|
|
2547
|
+
}
|
|
2548
|
+
getLogs(limit) {
|
|
2549
|
+
const logs = [...this.logs];
|
|
2550
|
+
if (limit) {
|
|
2551
|
+
return logs.slice(-limit);
|
|
2552
|
+
}
|
|
2553
|
+
return logs;
|
|
2554
|
+
}
|
|
2555
|
+
getLogsByCategory(category, limit) {
|
|
2556
|
+
const filtered = this.logs.filter((l) => l.category === category);
|
|
2557
|
+
if (limit) {
|
|
2558
|
+
return filtered.slice(-limit);
|
|
2559
|
+
}
|
|
2560
|
+
return filtered;
|
|
2561
|
+
}
|
|
2562
|
+
getLogsByLevel(level, limit) {
|
|
2563
|
+
const filtered = this.logs.filter((l) => l.level === level);
|
|
2564
|
+
if (limit) {
|
|
2565
|
+
return filtered.slice(-limit);
|
|
2566
|
+
}
|
|
2567
|
+
return filtered;
|
|
2568
|
+
}
|
|
2569
|
+
resetMetrics() {
|
|
2570
|
+
this.metrics = createEmptyMetrics();
|
|
2571
|
+
}
|
|
2572
|
+
clearLogs() {
|
|
2573
|
+
this.logs = [];
|
|
2574
|
+
}
|
|
2575
|
+
formatMetrics() {
|
|
2576
|
+
const m = this.metrics;
|
|
2577
|
+
const lines = [];
|
|
2578
|
+
lines.push("=== Metrics ===");
|
|
2579
|
+
if (m.indexingStartTime && m.indexingEndTime) {
|
|
2580
|
+
const duration = m.indexingEndTime - m.indexingStartTime;
|
|
2581
|
+
lines.push(`Indexing duration: ${(duration / 1e3).toFixed(2)}s`);
|
|
2582
|
+
}
|
|
2583
|
+
lines.push("");
|
|
2584
|
+
lines.push("Indexing:");
|
|
2585
|
+
lines.push(` Files scanned: ${m.filesScanned}`);
|
|
2586
|
+
lines.push(` Files parsed: ${m.filesParsed}`);
|
|
2587
|
+
lines.push(` Chunks processed: ${m.chunksProcessed}`);
|
|
2588
|
+
lines.push(` Chunks embedded: ${m.chunksEmbedded}`);
|
|
2589
|
+
lines.push(` Chunks from cache: ${m.chunksFromCache}`);
|
|
2590
|
+
lines.push(` Chunks removed: ${m.chunksRemoved}`);
|
|
2591
|
+
lines.push("");
|
|
2592
|
+
lines.push("Embedding API:");
|
|
2593
|
+
lines.push(` API calls: ${m.embeddingApiCalls}`);
|
|
2594
|
+
lines.push(` Tokens used: ${m.embeddingTokensUsed.toLocaleString()}`);
|
|
2595
|
+
lines.push(` Errors: ${m.embeddingErrors}`);
|
|
2596
|
+
if (m.searchCount > 0) {
|
|
2597
|
+
lines.push("");
|
|
2598
|
+
lines.push("Search:");
|
|
2599
|
+
lines.push(` Total searches: ${m.searchCount}`);
|
|
2600
|
+
lines.push(` Average time: ${m.searchAvgMs.toFixed(2)}ms`);
|
|
2601
|
+
lines.push(` Last search: ${m.searchLastMs.toFixed(2)}ms`);
|
|
2602
|
+
if (m.embeddingCallMs > 0) {
|
|
2603
|
+
lines.push(` - Embedding: ${m.embeddingCallMs.toFixed(2)}ms`);
|
|
2604
|
+
lines.push(` - Vector search: ${m.vectorSearchMs.toFixed(2)}ms`);
|
|
2605
|
+
lines.push(` - Keyword search: ${m.keywordSearchMs.toFixed(2)}ms`);
|
|
2606
|
+
lines.push(` - Fusion: ${m.fusionMs.toFixed(2)}ms`);
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
const totalCacheOps = m.cacheHits + m.cacheMisses;
|
|
2610
|
+
if (totalCacheOps > 0) {
|
|
2611
|
+
lines.push("");
|
|
2612
|
+
lines.push("Cache:");
|
|
2613
|
+
lines.push(` Hits: ${m.cacheHits}`);
|
|
2614
|
+
lines.push(` Misses: ${m.cacheMisses}`);
|
|
2615
|
+
lines.push(` Hit rate: ${(m.cacheHits / totalCacheOps * 100).toFixed(1)}%`);
|
|
2616
|
+
}
|
|
2617
|
+
if (m.gcRuns > 0) {
|
|
2618
|
+
lines.push("");
|
|
2619
|
+
lines.push("Garbage Collection:");
|
|
2620
|
+
lines.push(` GC runs: ${m.gcRuns}`);
|
|
2621
|
+
lines.push(` Orphans removed: ${m.gcOrphansRemoved}`);
|
|
2622
|
+
lines.push(` Chunks removed: ${m.gcChunksRemoved}`);
|
|
2623
|
+
lines.push(` Embeddings removed: ${m.gcEmbeddingsRemoved}`);
|
|
2624
|
+
}
|
|
2625
|
+
return lines.join("\n");
|
|
2626
|
+
}
|
|
2627
|
+
formatRecentLogs(limit = 20) {
|
|
2628
|
+
const logs = this.getLogs(limit);
|
|
2629
|
+
if (logs.length === 0) {
|
|
2630
|
+
return "No logs recorded.";
|
|
2631
|
+
}
|
|
2632
|
+
return logs.map((l) => {
|
|
2633
|
+
const dataStr = l.data ? ` ${JSON.stringify(l.data)}` : "";
|
|
2634
|
+
return `[${l.timestamp}] [${l.level.toUpperCase()}] [${l.category}] ${l.message}${dataStr}`;
|
|
2635
|
+
}).join("\n");
|
|
2636
|
+
}
|
|
2637
|
+
isEnabled() {
|
|
2638
|
+
return this.config.enabled;
|
|
2639
|
+
}
|
|
2640
|
+
isMetricsEnabled() {
|
|
2641
|
+
return this.config.enabled && this.config.metrics;
|
|
2642
|
+
}
|
|
2643
|
+
};
|
|
2644
|
+
var globalLogger = null;
|
|
2645
|
+
function initializeLogger(config) {
|
|
2646
|
+
globalLogger = new Logger(config);
|
|
2647
|
+
return globalLogger;
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2325
2650
|
// src/native/index.ts
|
|
2326
2651
|
import * as path3 from "path";
|
|
2327
2652
|
import * as os2 from "os";
|
|
@@ -2450,6 +2775,21 @@ var VectorStore = class {
|
|
|
2450
2775
|
metadata: JSON.parse(r.metadata)
|
|
2451
2776
|
}));
|
|
2452
2777
|
}
|
|
2778
|
+
getMetadata(id) {
|
|
2779
|
+
const result = this.inner.getMetadata(id);
|
|
2780
|
+
if (result === null || result === void 0) {
|
|
2781
|
+
return void 0;
|
|
2782
|
+
}
|
|
2783
|
+
return JSON.parse(result);
|
|
2784
|
+
}
|
|
2785
|
+
getMetadataBatch(ids) {
|
|
2786
|
+
const results = this.inner.getMetadataBatch(ids);
|
|
2787
|
+
const map = /* @__PURE__ */ new Map();
|
|
2788
|
+
for (const { key, metadata } of results) {
|
|
2789
|
+
map.set(key, JSON.parse(metadata));
|
|
2790
|
+
}
|
|
2791
|
+
return map;
|
|
2792
|
+
}
|
|
2453
2793
|
};
|
|
2454
2794
|
var CHARS_PER_TOKEN = 4;
|
|
2455
2795
|
var MAX_BATCH_TOKENS = 7500;
|
|
@@ -2677,12 +3017,20 @@ var Database = class {
|
|
|
2677
3017
|
upsertEmbedding(contentHash, embedding, chunkText, model) {
|
|
2678
3018
|
this.inner.upsertEmbedding(contentHash, embedding, chunkText, model);
|
|
2679
3019
|
}
|
|
3020
|
+
upsertEmbeddingsBatch(items) {
|
|
3021
|
+
if (items.length === 0) return;
|
|
3022
|
+
this.inner.upsertEmbeddingsBatch(items);
|
|
3023
|
+
}
|
|
2680
3024
|
getMissingEmbeddings(contentHashes) {
|
|
2681
3025
|
return this.inner.getMissingEmbeddings(contentHashes);
|
|
2682
3026
|
}
|
|
2683
3027
|
upsertChunk(chunk) {
|
|
2684
3028
|
this.inner.upsertChunk(chunk);
|
|
2685
3029
|
}
|
|
3030
|
+
upsertChunksBatch(chunks) {
|
|
3031
|
+
if (chunks.length === 0) return;
|
|
3032
|
+
this.inner.upsertChunksBatch(chunks);
|
|
3033
|
+
}
|
|
2686
3034
|
getChunk(chunkId) {
|
|
2687
3035
|
return this.inner.getChunk(chunkId) ?? null;
|
|
2688
3036
|
}
|
|
@@ -2695,6 +3043,10 @@ var Database = class {
|
|
|
2695
3043
|
addChunksToBranch(branch, chunkIds) {
|
|
2696
3044
|
this.inner.addChunksToBranch(branch, chunkIds);
|
|
2697
3045
|
}
|
|
3046
|
+
addChunksToBranchBatch(branch, chunkIds) {
|
|
3047
|
+
if (chunkIds.length === 0) return;
|
|
3048
|
+
this.inner.addChunksToBranchBatch(branch, chunkIds);
|
|
3049
|
+
}
|
|
2698
3050
|
clearBranch(branch) {
|
|
2699
3051
|
return this.inner.clearBranch(branch);
|
|
2700
3052
|
}
|
|
@@ -2836,12 +3188,18 @@ var Indexer = class {
|
|
|
2836
3188
|
failedBatchesPath = "";
|
|
2837
3189
|
currentBranch = "default";
|
|
2838
3190
|
baseBranch = "main";
|
|
3191
|
+
logger;
|
|
3192
|
+
queryEmbeddingCache = /* @__PURE__ */ new Map();
|
|
3193
|
+
maxQueryCacheSize = 100;
|
|
3194
|
+
queryCacheTtlMs = 5 * 60 * 1e3;
|
|
3195
|
+
querySimilarityThreshold = 0.85;
|
|
2839
3196
|
constructor(projectRoot, config) {
|
|
2840
3197
|
this.projectRoot = projectRoot;
|
|
2841
3198
|
this.config = config;
|
|
2842
3199
|
this.indexPath = this.getIndexPath();
|
|
2843
3200
|
this.fileHashCachePath = path5.join(this.indexPath, "file-hashes.json");
|
|
2844
3201
|
this.failedBatchesPath = path5.join(this.indexPath, "failed-batches.json");
|
|
3202
|
+
this.logger = initializeLogger(config.debug);
|
|
2845
3203
|
}
|
|
2846
3204
|
getIndexPath() {
|
|
2847
3205
|
if (this.config.scope === "global") {
|
|
@@ -2920,6 +3278,11 @@ var Indexer = class {
|
|
|
2920
3278
|
"No embedding provider available. Configure GitHub, OpenAI, Google, or Ollama."
|
|
2921
3279
|
);
|
|
2922
3280
|
}
|
|
3281
|
+
this.logger.info("Initializing indexer", {
|
|
3282
|
+
provider: this.detectedProvider.provider,
|
|
3283
|
+
model: this.detectedProvider.modelInfo.model,
|
|
3284
|
+
scope: this.config.scope
|
|
3285
|
+
});
|
|
2923
3286
|
this.provider = createEmbeddingProvider(
|
|
2924
3287
|
this.detectedProvider.credentials,
|
|
2925
3288
|
this.detectedProvider.modelInfo
|
|
@@ -2951,15 +3314,54 @@ var Indexer = class {
|
|
|
2951
3314
|
if (isGitRepo(this.projectRoot)) {
|
|
2952
3315
|
this.currentBranch = getBranchOrDefault(this.projectRoot);
|
|
2953
3316
|
this.baseBranch = getBaseBranch(this.projectRoot);
|
|
3317
|
+
this.logger.branch("info", "Detected git repository", {
|
|
3318
|
+
currentBranch: this.currentBranch,
|
|
3319
|
+
baseBranch: this.baseBranch
|
|
3320
|
+
});
|
|
2954
3321
|
} else {
|
|
2955
3322
|
this.currentBranch = "default";
|
|
2956
3323
|
this.baseBranch = "default";
|
|
3324
|
+
this.logger.branch("debug", "Not a git repository, using default branch");
|
|
3325
|
+
}
|
|
3326
|
+
if (this.config.indexing.autoGc) {
|
|
3327
|
+
await this.maybeRunAutoGc();
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3330
|
+
async maybeRunAutoGc() {
|
|
3331
|
+
if (!this.database) return;
|
|
3332
|
+
const lastGcTimestamp = this.database.getMetadata("lastGcTimestamp");
|
|
3333
|
+
const now = Date.now();
|
|
3334
|
+
const intervalMs = this.config.indexing.gcIntervalDays * 24 * 60 * 60 * 1e3;
|
|
3335
|
+
let shouldRunGc = false;
|
|
3336
|
+
if (!lastGcTimestamp) {
|
|
3337
|
+
shouldRunGc = true;
|
|
3338
|
+
} else {
|
|
3339
|
+
const lastGcTime = parseInt(lastGcTimestamp, 10);
|
|
3340
|
+
if (!isNaN(lastGcTime) && now - lastGcTime > intervalMs) {
|
|
3341
|
+
shouldRunGc = true;
|
|
3342
|
+
}
|
|
3343
|
+
}
|
|
3344
|
+
if (shouldRunGc) {
|
|
3345
|
+
await this.healthCheck();
|
|
3346
|
+
this.database.setMetadata("lastGcTimestamp", now.toString());
|
|
3347
|
+
}
|
|
3348
|
+
}
|
|
3349
|
+
async maybeRunOrphanGc() {
|
|
3350
|
+
if (!this.database) return;
|
|
3351
|
+
const stats = this.database.getStats();
|
|
3352
|
+
if (!stats) return;
|
|
3353
|
+
const orphanCount = stats.embeddingCount - stats.chunkCount;
|
|
3354
|
+
if (orphanCount > this.config.indexing.gcOrphanThreshold) {
|
|
3355
|
+
this.database.gcOrphanEmbeddings();
|
|
3356
|
+
this.database.gcOrphanChunks();
|
|
3357
|
+
this.database.setMetadata("lastGcTimestamp", Date.now().toString());
|
|
2957
3358
|
}
|
|
2958
3359
|
}
|
|
2959
3360
|
migrateFromLegacyIndex() {
|
|
2960
3361
|
if (!this.store || !this.database) return;
|
|
2961
3362
|
const allMetadata = this.store.getAllMetadata();
|
|
2962
3363
|
const chunkIds = [];
|
|
3364
|
+
const chunkDataBatch = [];
|
|
2963
3365
|
for (const { key, metadata } of allMetadata) {
|
|
2964
3366
|
const chunkData = {
|
|
2965
3367
|
chunkId: key,
|
|
@@ -2971,10 +3373,13 @@ var Indexer = class {
|
|
|
2971
3373
|
name: metadata.name,
|
|
2972
3374
|
language: metadata.language
|
|
2973
3375
|
};
|
|
2974
|
-
|
|
3376
|
+
chunkDataBatch.push(chunkData);
|
|
2975
3377
|
chunkIds.push(key);
|
|
2976
3378
|
}
|
|
2977
|
-
|
|
3379
|
+
if (chunkDataBatch.length > 0) {
|
|
3380
|
+
this.database.upsertChunksBatch(chunkDataBatch);
|
|
3381
|
+
}
|
|
3382
|
+
this.database.addChunksToBranchBatch(this.currentBranch || "default", chunkIds);
|
|
2978
3383
|
}
|
|
2979
3384
|
async ensureInitialized() {
|
|
2980
3385
|
if (!this.store || !this.provider || !this.invertedIndex || !this.detectedProvider || !this.database) {
|
|
@@ -3000,6 +3405,8 @@ var Indexer = class {
|
|
|
3000
3405
|
}
|
|
3001
3406
|
async index(onProgress) {
|
|
3002
3407
|
const { store, provider, invertedIndex, database, detectedProvider } = await this.ensureInitialized();
|
|
3408
|
+
this.logger.recordIndexingStart();
|
|
3409
|
+
this.logger.info("Starting indexing", { projectRoot: this.projectRoot });
|
|
3003
3410
|
const startTime = Date.now();
|
|
3004
3411
|
const stats = {
|
|
3005
3412
|
totalFiles: 0,
|
|
@@ -3029,6 +3436,11 @@ var Indexer = class {
|
|
|
3029
3436
|
);
|
|
3030
3437
|
stats.totalFiles = files.length;
|
|
3031
3438
|
stats.skippedFiles = skipped;
|
|
3439
|
+
this.logger.recordFilesScanned(files.length);
|
|
3440
|
+
this.logger.cache("debug", "Scanning files for changes", {
|
|
3441
|
+
totalFiles: files.length,
|
|
3442
|
+
skippedFiles: skipped.length
|
|
3443
|
+
});
|
|
3032
3444
|
const changedFiles = [];
|
|
3033
3445
|
const unchangedFilePaths = /* @__PURE__ */ new Set();
|
|
3034
3446
|
const currentFileHashes = /* @__PURE__ */ new Map();
|
|
@@ -3037,11 +3449,17 @@ var Indexer = class {
|
|
|
3037
3449
|
currentFileHashes.set(f.path, currentHash);
|
|
3038
3450
|
if (this.fileHashCache.get(f.path) === currentHash) {
|
|
3039
3451
|
unchangedFilePaths.add(f.path);
|
|
3452
|
+
this.logger.recordCacheHit();
|
|
3040
3453
|
} else {
|
|
3041
3454
|
const content = await fsPromises2.readFile(f.path, "utf-8");
|
|
3042
3455
|
changedFiles.push({ path: f.path, content, hash: currentHash });
|
|
3456
|
+
this.logger.recordCacheMiss();
|
|
3043
3457
|
}
|
|
3044
3458
|
}
|
|
3459
|
+
this.logger.cache("info", "File hash cache results", {
|
|
3460
|
+
unchanged: unchangedFilePaths.size,
|
|
3461
|
+
changed: changedFiles.length
|
|
3462
|
+
});
|
|
3045
3463
|
onProgress?.({
|
|
3046
3464
|
phase: "parsing",
|
|
3047
3465
|
filesProcessed: 0,
|
|
@@ -3049,7 +3467,12 @@ var Indexer = class {
|
|
|
3049
3467
|
chunksProcessed: 0,
|
|
3050
3468
|
totalChunks: 0
|
|
3051
3469
|
});
|
|
3470
|
+
const parseStartTime = performance2.now();
|
|
3052
3471
|
const parsedFiles = parseFiles(changedFiles);
|
|
3472
|
+
const parseMs = performance2.now() - parseStartTime;
|
|
3473
|
+
this.logger.recordFilesParsed(parsedFiles.length);
|
|
3474
|
+
this.logger.recordParseDuration(parseMs);
|
|
3475
|
+
this.logger.debug("Parsed changed files", { parsedCount: parsedFiles.length, parseMs: parseMs.toFixed(2) });
|
|
3053
3476
|
const existingChunks = /* @__PURE__ */ new Map();
|
|
3054
3477
|
const existingChunksByFile = /* @__PURE__ */ new Map();
|
|
3055
3478
|
for (const { key, metadata } of store.getAllMetadata()) {
|
|
@@ -3070,6 +3493,7 @@ var Indexer = class {
|
|
|
3070
3493
|
}
|
|
3071
3494
|
}
|
|
3072
3495
|
}
|
|
3496
|
+
const chunkDataBatch = [];
|
|
3073
3497
|
for (const parsed of parsedFiles) {
|
|
3074
3498
|
currentFilePaths.add(parsed.path);
|
|
3075
3499
|
if (parsed.chunks.length === 0) {
|
|
@@ -3097,7 +3521,7 @@ var Indexer = class {
|
|
|
3097
3521
|
name: chunk.name,
|
|
3098
3522
|
language: chunk.language
|
|
3099
3523
|
};
|
|
3100
|
-
|
|
3524
|
+
chunkDataBatch.push(chunkData);
|
|
3101
3525
|
if (existingChunks.get(id) === contentHash) {
|
|
3102
3526
|
fileChunkCount++;
|
|
3103
3527
|
continue;
|
|
@@ -3116,6 +3540,9 @@ var Indexer = class {
|
|
|
3116
3540
|
fileChunkCount++;
|
|
3117
3541
|
}
|
|
3118
3542
|
}
|
|
3543
|
+
if (chunkDataBatch.length > 0) {
|
|
3544
|
+
database.upsertChunksBatch(chunkDataBatch);
|
|
3545
|
+
}
|
|
3119
3546
|
let removedCount = 0;
|
|
3120
3547
|
for (const [chunkId] of existingChunks) {
|
|
3121
3548
|
if (!currentChunkIds.has(chunkId)) {
|
|
@@ -3127,9 +3554,16 @@ var Indexer = class {
|
|
|
3127
3554
|
stats.totalChunks = pendingChunks.length;
|
|
3128
3555
|
stats.existingChunks = currentChunkIds.size - pendingChunks.length;
|
|
3129
3556
|
stats.removedChunks = removedCount;
|
|
3557
|
+
this.logger.recordChunksProcessed(currentChunkIds.size);
|
|
3558
|
+
this.logger.recordChunksRemoved(removedCount);
|
|
3559
|
+
this.logger.info("Chunk analysis complete", {
|
|
3560
|
+
pending: pendingChunks.length,
|
|
3561
|
+
existing: stats.existingChunks,
|
|
3562
|
+
removed: removedCount
|
|
3563
|
+
});
|
|
3130
3564
|
if (pendingChunks.length === 0 && removedCount === 0) {
|
|
3131
3565
|
database.clearBranch(this.currentBranch);
|
|
3132
|
-
database.
|
|
3566
|
+
database.addChunksToBranchBatch(this.currentBranch, Array.from(currentChunkIds));
|
|
3133
3567
|
this.fileHashCache = currentFileHashes;
|
|
3134
3568
|
this.saveFileHashCache();
|
|
3135
3569
|
stats.durationMs = Date.now() - startTime;
|
|
@@ -3144,7 +3578,7 @@ var Indexer = class {
|
|
|
3144
3578
|
}
|
|
3145
3579
|
if (pendingChunks.length === 0) {
|
|
3146
3580
|
database.clearBranch(this.currentBranch);
|
|
3147
|
-
database.
|
|
3581
|
+
database.addChunksToBranchBatch(this.currentBranch, Array.from(currentChunkIds));
|
|
3148
3582
|
store.save();
|
|
3149
3583
|
invertedIndex.save();
|
|
3150
3584
|
this.fileHashCache = currentFileHashes;
|
|
@@ -3170,6 +3604,11 @@ var Indexer = class {
|
|
|
3170
3604
|
const missingHashes = new Set(database.getMissingEmbeddings(allContentHashes));
|
|
3171
3605
|
const chunksNeedingEmbedding = pendingChunks.filter((c) => missingHashes.has(c.contentHash));
|
|
3172
3606
|
const chunksWithExistingEmbedding = pendingChunks.filter((c) => !missingHashes.has(c.contentHash));
|
|
3607
|
+
this.logger.cache("info", "Embedding cache lookup", {
|
|
3608
|
+
needsEmbedding: chunksNeedingEmbedding.length,
|
|
3609
|
+
fromCache: chunksWithExistingEmbedding.length
|
|
3610
|
+
});
|
|
3611
|
+
this.logger.recordChunksFromCache(chunksWithExistingEmbedding.length);
|
|
3173
3612
|
for (const chunk of chunksWithExistingEmbedding) {
|
|
3174
3613
|
const embeddingBuffer = database.getEmbedding(chunk.contentHash);
|
|
3175
3614
|
if (embeddingBuffer) {
|
|
@@ -3208,13 +3647,16 @@ var Indexer = class {
|
|
|
3208
3647
|
const message = getErrorMessage(error);
|
|
3209
3648
|
if (isRateLimitError(error)) {
|
|
3210
3649
|
rateLimitBackoffMs = Math.min(providerRateLimits.maxRetryMs, (rateLimitBackoffMs || providerRateLimits.minRetryMs) * 2);
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3650
|
+
this.logger.embedding("warn", `Rate limited, backing off`, {
|
|
3651
|
+
attempt: error.attemptNumber,
|
|
3652
|
+
retriesLeft: error.retriesLeft,
|
|
3653
|
+
backoffMs: rateLimitBackoffMs
|
|
3654
|
+
});
|
|
3214
3655
|
} else {
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3656
|
+
this.logger.embedding("error", `Embedding batch failed`, {
|
|
3657
|
+
attempt: error.attemptNumber,
|
|
3658
|
+
error: message
|
|
3659
|
+
});
|
|
3218
3660
|
}
|
|
3219
3661
|
}
|
|
3220
3662
|
}
|
|
@@ -3228,20 +3670,25 @@ var Indexer = class {
|
|
|
3228
3670
|
metadata: chunk.metadata
|
|
3229
3671
|
}));
|
|
3230
3672
|
store.addBatch(items);
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
);
|
|
3673
|
+
const embeddingBatchItems = batch.map((chunk, i) => ({
|
|
3674
|
+
contentHash: chunk.contentHash,
|
|
3675
|
+
embedding: float32ArrayToBuffer(result.embeddings[i]),
|
|
3676
|
+
chunkText: chunk.text,
|
|
3677
|
+
model: detectedProvider.modelInfo.model
|
|
3678
|
+
}));
|
|
3679
|
+
database.upsertEmbeddingsBatch(embeddingBatchItems);
|
|
3680
|
+
for (const chunk of batch) {
|
|
3240
3681
|
invertedIndex.removeChunk(chunk.id);
|
|
3241
3682
|
invertedIndex.addChunk(chunk.id, chunk.content);
|
|
3242
3683
|
}
|
|
3243
3684
|
stats.indexedChunks += batch.length;
|
|
3244
3685
|
stats.tokensUsed += result.totalTokensUsed;
|
|
3686
|
+
this.logger.recordChunksEmbedded(batch.length);
|
|
3687
|
+
this.logger.recordEmbeddingApiCall(result.totalTokensUsed);
|
|
3688
|
+
this.logger.embedding("debug", `Embedded batch`, {
|
|
3689
|
+
batchSize: batch.length,
|
|
3690
|
+
tokens: result.totalTokensUsed
|
|
3691
|
+
});
|
|
3245
3692
|
onProgress?.({
|
|
3246
3693
|
phase: "embedding",
|
|
3247
3694
|
filesProcessed: files.length,
|
|
@@ -3252,7 +3699,11 @@ var Indexer = class {
|
|
|
3252
3699
|
} catch (error) {
|
|
3253
3700
|
stats.failedChunks += batch.length;
|
|
3254
3701
|
this.addFailedBatch(batch, getErrorMessage(error));
|
|
3255
|
-
|
|
3702
|
+
this.logger.recordEmbeddingError();
|
|
3703
|
+
this.logger.embedding("error", `Failed to embed batch after retries`, {
|
|
3704
|
+
batchSize: batch.length,
|
|
3705
|
+
error: getErrorMessage(error)
|
|
3706
|
+
});
|
|
3256
3707
|
}
|
|
3257
3708
|
});
|
|
3258
3709
|
}
|
|
@@ -3265,12 +3716,25 @@ var Indexer = class {
|
|
|
3265
3716
|
totalChunks: pendingChunks.length
|
|
3266
3717
|
});
|
|
3267
3718
|
database.clearBranch(this.currentBranch);
|
|
3268
|
-
database.
|
|
3719
|
+
database.addChunksToBranchBatch(this.currentBranch, Array.from(currentChunkIds));
|
|
3269
3720
|
store.save();
|
|
3270
3721
|
invertedIndex.save();
|
|
3271
3722
|
this.fileHashCache = currentFileHashes;
|
|
3272
3723
|
this.saveFileHashCache();
|
|
3724
|
+
if (this.config.indexing.autoGc && stats.removedChunks > 0) {
|
|
3725
|
+
await this.maybeRunOrphanGc();
|
|
3726
|
+
}
|
|
3273
3727
|
stats.durationMs = Date.now() - startTime;
|
|
3728
|
+
this.logger.recordIndexingEnd();
|
|
3729
|
+
this.logger.info("Indexing complete", {
|
|
3730
|
+
files: stats.totalFiles,
|
|
3731
|
+
indexed: stats.indexedChunks,
|
|
3732
|
+
existing: stats.existingChunks,
|
|
3733
|
+
removed: stats.removedChunks,
|
|
3734
|
+
failed: stats.failedChunks,
|
|
3735
|
+
tokens: stats.tokensUsed,
|
|
3736
|
+
durationMs: stats.durationMs
|
|
3737
|
+
});
|
|
3274
3738
|
if (stats.failedChunks > 0) {
|
|
3275
3739
|
stats.failedBatchesPath = this.failedBatchesPath;
|
|
3276
3740
|
}
|
|
@@ -3283,18 +3747,96 @@ var Indexer = class {
|
|
|
3283
3747
|
});
|
|
3284
3748
|
return stats;
|
|
3285
3749
|
}
|
|
3750
|
+
async getQueryEmbedding(query, provider) {
|
|
3751
|
+
const now = Date.now();
|
|
3752
|
+
const cached = this.queryEmbeddingCache.get(query);
|
|
3753
|
+
if (cached && now - cached.timestamp < this.queryCacheTtlMs) {
|
|
3754
|
+
this.logger.cache("debug", "Query embedding cache hit (exact)", { query: query.slice(0, 50) });
|
|
3755
|
+
this.logger.recordQueryCacheHit();
|
|
3756
|
+
return cached.embedding;
|
|
3757
|
+
}
|
|
3758
|
+
const similarMatch = this.findSimilarCachedQuery(query, now);
|
|
3759
|
+
if (similarMatch) {
|
|
3760
|
+
this.logger.cache("debug", "Query embedding cache hit (similar)", {
|
|
3761
|
+
query: query.slice(0, 50),
|
|
3762
|
+
similarTo: similarMatch.key.slice(0, 50),
|
|
3763
|
+
similarity: similarMatch.similarity.toFixed(3)
|
|
3764
|
+
});
|
|
3765
|
+
this.logger.recordQueryCacheSimilarHit();
|
|
3766
|
+
return similarMatch.embedding;
|
|
3767
|
+
}
|
|
3768
|
+
this.logger.cache("debug", "Query embedding cache miss", { query: query.slice(0, 50) });
|
|
3769
|
+
this.logger.recordQueryCacheMiss();
|
|
3770
|
+
const { embedding, tokensUsed } = await provider.embed(query);
|
|
3771
|
+
this.logger.recordEmbeddingApiCall(tokensUsed);
|
|
3772
|
+
if (this.queryEmbeddingCache.size >= this.maxQueryCacheSize) {
|
|
3773
|
+
const oldestKey = this.queryEmbeddingCache.keys().next().value;
|
|
3774
|
+
if (oldestKey) {
|
|
3775
|
+
this.queryEmbeddingCache.delete(oldestKey);
|
|
3776
|
+
}
|
|
3777
|
+
}
|
|
3778
|
+
this.queryEmbeddingCache.set(query, { embedding, timestamp: now });
|
|
3779
|
+
return embedding;
|
|
3780
|
+
}
|
|
3781
|
+
findSimilarCachedQuery(query, now) {
|
|
3782
|
+
const queryTokens = this.tokenize(query);
|
|
3783
|
+
if (queryTokens.size === 0) return null;
|
|
3784
|
+
let bestMatch = null;
|
|
3785
|
+
for (const [cachedQuery, { embedding, timestamp }] of this.queryEmbeddingCache) {
|
|
3786
|
+
if (now - timestamp >= this.queryCacheTtlMs) continue;
|
|
3787
|
+
const cachedTokens = this.tokenize(cachedQuery);
|
|
3788
|
+
const similarity = this.jaccardSimilarity(queryTokens, cachedTokens);
|
|
3789
|
+
if (similarity >= this.querySimilarityThreshold) {
|
|
3790
|
+
if (!bestMatch || similarity > bestMatch.similarity) {
|
|
3791
|
+
bestMatch = { key: cachedQuery, embedding, similarity };
|
|
3792
|
+
}
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3795
|
+
return bestMatch;
|
|
3796
|
+
}
|
|
3797
|
+
tokenize(text) {
|
|
3798
|
+
return new Set(
|
|
3799
|
+
text.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter((t) => t.length > 1)
|
|
3800
|
+
);
|
|
3801
|
+
}
|
|
3802
|
+
jaccardSimilarity(a, b) {
|
|
3803
|
+
if (a.size === 0 && b.size === 0) return 1;
|
|
3804
|
+
if (a.size === 0 || b.size === 0) return 0;
|
|
3805
|
+
let intersection = 0;
|
|
3806
|
+
for (const token of a) {
|
|
3807
|
+
if (b.has(token)) intersection++;
|
|
3808
|
+
}
|
|
3809
|
+
const union = a.size + b.size - intersection;
|
|
3810
|
+
return intersection / union;
|
|
3811
|
+
}
|
|
3286
3812
|
async search(query, limit, options) {
|
|
3813
|
+
const searchStartTime = performance2.now();
|
|
3287
3814
|
const { store, provider, database } = await this.ensureInitialized();
|
|
3288
3815
|
if (store.count() === 0) {
|
|
3816
|
+
this.logger.search("debug", "Search on empty index", { query });
|
|
3289
3817
|
return [];
|
|
3290
3818
|
}
|
|
3291
3819
|
const maxResults = limit ?? this.config.search.maxResults;
|
|
3292
3820
|
const hybridWeight = options?.hybridWeight ?? this.config.search.hybridWeight;
|
|
3293
3821
|
const filterByBranch = options?.filterByBranch ?? true;
|
|
3294
|
-
|
|
3822
|
+
this.logger.search("debug", "Starting search", {
|
|
3823
|
+
query,
|
|
3824
|
+
maxResults,
|
|
3825
|
+
hybridWeight,
|
|
3826
|
+
filterByBranch
|
|
3827
|
+
});
|
|
3828
|
+
const embeddingStartTime = performance2.now();
|
|
3829
|
+
const embedding = await this.getQueryEmbedding(query, provider);
|
|
3830
|
+
const embeddingMs = performance2.now() - embeddingStartTime;
|
|
3831
|
+
const vectorStartTime = performance2.now();
|
|
3295
3832
|
const semanticResults = store.search(embedding, maxResults * 4);
|
|
3833
|
+
const vectorMs = performance2.now() - vectorStartTime;
|
|
3834
|
+
const keywordStartTime = performance2.now();
|
|
3296
3835
|
const keywordResults = await this.keywordSearch(query, maxResults * 4);
|
|
3836
|
+
const keywordMs = performance2.now() - keywordStartTime;
|
|
3837
|
+
const fusionStartTime = performance2.now();
|
|
3297
3838
|
const combined = this.fuseResults(semanticResults, keywordResults, hybridWeight, maxResults * 4);
|
|
3839
|
+
const fusionMs = performance2.now() - fusionStartTime;
|
|
3298
3840
|
let branchChunkIds = null;
|
|
3299
3841
|
if (filterByBranch && this.currentBranch !== "default") {
|
|
3300
3842
|
branchChunkIds = new Set(database.getBranchChunkIds(this.currentBranch));
|
|
@@ -3315,6 +3857,22 @@ var Indexer = class {
|
|
|
3315
3857
|
}
|
|
3316
3858
|
return true;
|
|
3317
3859
|
}).slice(0, maxResults);
|
|
3860
|
+
const totalSearchMs = performance2.now() - searchStartTime;
|
|
3861
|
+
this.logger.recordSearch(totalSearchMs, {
|
|
3862
|
+
embeddingMs,
|
|
3863
|
+
vectorMs,
|
|
3864
|
+
keywordMs,
|
|
3865
|
+
fusionMs
|
|
3866
|
+
});
|
|
3867
|
+
this.logger.search("info", "Search complete", {
|
|
3868
|
+
query,
|
|
3869
|
+
results: filtered.length,
|
|
3870
|
+
totalMs: Math.round(totalSearchMs * 100) / 100,
|
|
3871
|
+
embeddingMs: Math.round(embeddingMs * 100) / 100,
|
|
3872
|
+
vectorMs: Math.round(vectorMs * 100) / 100,
|
|
3873
|
+
keywordMs: Math.round(keywordMs * 100) / 100,
|
|
3874
|
+
fusionMs: Math.round(fusionMs * 100) / 100
|
|
3875
|
+
});
|
|
3318
3876
|
return Promise.all(
|
|
3319
3877
|
filtered.map(async (r) => {
|
|
3320
3878
|
let content = "";
|
|
@@ -3353,11 +3911,8 @@ var Indexer = class {
|
|
|
3353
3911
|
if (scores.size === 0) {
|
|
3354
3912
|
return [];
|
|
3355
3913
|
}
|
|
3356
|
-
const
|
|
3357
|
-
const metadataMap =
|
|
3358
|
-
for (const { key, metadata } of allMetadata) {
|
|
3359
|
-
metadataMap.set(key, metadata);
|
|
3360
|
-
}
|
|
3914
|
+
const chunkIds = Array.from(scores.keys());
|
|
3915
|
+
const metadataMap = store.getMetadataBatch(chunkIds);
|
|
3361
3916
|
const results = [];
|
|
3362
3917
|
for (const [chunkId, score] of scores) {
|
|
3363
3918
|
const metadata = metadataMap.get(chunkId);
|
|
@@ -3409,14 +3964,18 @@ var Indexer = class {
|
|
|
3409
3964
|
};
|
|
3410
3965
|
}
|
|
3411
3966
|
async clearIndex() {
|
|
3412
|
-
const { store, invertedIndex } = await this.ensureInitialized();
|
|
3967
|
+
const { store, invertedIndex, database } = await this.ensureInitialized();
|
|
3413
3968
|
store.clear();
|
|
3414
3969
|
store.save();
|
|
3415
3970
|
invertedIndex.clear();
|
|
3416
3971
|
invertedIndex.save();
|
|
3972
|
+
this.fileHashCache.clear();
|
|
3973
|
+
this.saveFileHashCache();
|
|
3974
|
+
database.clearBranch(this.currentBranch);
|
|
3417
3975
|
}
|
|
3418
3976
|
async healthCheck() {
|
|
3419
3977
|
const { store, invertedIndex, database } = await this.ensureInitialized();
|
|
3978
|
+
this.logger.gc("info", "Starting health check");
|
|
3420
3979
|
const allMetadata = store.getAllMetadata();
|
|
3421
3980
|
const filePathsToChunkKeys = /* @__PURE__ */ new Map();
|
|
3422
3981
|
for (const { key, metadata } of allMetadata) {
|
|
@@ -3443,6 +4002,13 @@ var Indexer = class {
|
|
|
3443
4002
|
}
|
|
3444
4003
|
const gcOrphanEmbeddings = database.gcOrphanEmbeddings();
|
|
3445
4004
|
const gcOrphanChunks = database.gcOrphanChunks();
|
|
4005
|
+
this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
|
|
4006
|
+
this.logger.gc("info", "Health check complete", {
|
|
4007
|
+
removedStale: removedCount,
|
|
4008
|
+
orphanEmbeddings: gcOrphanEmbeddings,
|
|
4009
|
+
orphanChunks: gcOrphanChunks,
|
|
4010
|
+
removedFiles: removedFilePaths.length
|
|
4011
|
+
});
|
|
3446
4012
|
return { removed: removedCount, filePaths: removedFilePaths, gcOrphanEmbeddings, gcOrphanChunks };
|
|
3447
4013
|
}
|
|
3448
4014
|
async retryFailedBatches() {
|
|
@@ -3476,9 +4042,12 @@ var Indexer = class {
|
|
|
3476
4042
|
invertedIndex.removeChunk(chunk.id);
|
|
3477
4043
|
invertedIndex.addChunk(chunk.id, chunk.content);
|
|
3478
4044
|
}
|
|
4045
|
+
this.logger.recordChunksEmbedded(batch.chunks.length);
|
|
4046
|
+
this.logger.recordEmbeddingApiCall(result.totalTokensUsed);
|
|
3479
4047
|
succeeded += batch.chunks.length;
|
|
3480
4048
|
} catch (error) {
|
|
3481
4049
|
failed += batch.chunks.length;
|
|
4050
|
+
this.logger.recordEmbeddingError();
|
|
3482
4051
|
stillFailing.push({
|
|
3483
4052
|
...batch,
|
|
3484
4053
|
attemptCount: batch.attemptCount + 1,
|
|
@@ -3513,6 +4082,9 @@ var Indexer = class {
|
|
|
3513
4082
|
const { database } = await this.ensureInitialized();
|
|
3514
4083
|
return database.getStats();
|
|
3515
4084
|
}
|
|
4085
|
+
getLogger() {
|
|
4086
|
+
return this.logger;
|
|
4087
|
+
}
|
|
3516
4088
|
};
|
|
3517
4089
|
|
|
3518
4090
|
// node_modules/chokidar/index.js
|
|
@@ -3605,7 +4177,7 @@ var ReaddirpStream = class extends Readable {
|
|
|
3605
4177
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
3606
4178
|
const statMethod = opts.lstat ? lstat : stat;
|
|
3607
4179
|
if (wantBigintFsStats) {
|
|
3608
|
-
this._stat = (
|
|
4180
|
+
this._stat = (path9) => statMethod(path9, { bigint: true });
|
|
3609
4181
|
} else {
|
|
3610
4182
|
this._stat = statMethod;
|
|
3611
4183
|
}
|
|
@@ -3630,8 +4202,8 @@ var ReaddirpStream = class extends Readable {
|
|
|
3630
4202
|
const par = this.parent;
|
|
3631
4203
|
const fil = par && par.files;
|
|
3632
4204
|
if (fil && fil.length > 0) {
|
|
3633
|
-
const { path:
|
|
3634
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
4205
|
+
const { path: path9, depth } = par;
|
|
4206
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path9));
|
|
3635
4207
|
const awaited = await Promise.all(slice);
|
|
3636
4208
|
for (const entry of awaited) {
|
|
3637
4209
|
if (!entry)
|
|
@@ -3671,21 +4243,21 @@ var ReaddirpStream = class extends Readable {
|
|
|
3671
4243
|
this.reading = false;
|
|
3672
4244
|
}
|
|
3673
4245
|
}
|
|
3674
|
-
async _exploreDir(
|
|
4246
|
+
async _exploreDir(path9, depth) {
|
|
3675
4247
|
let files;
|
|
3676
4248
|
try {
|
|
3677
|
-
files = await readdir(
|
|
4249
|
+
files = await readdir(path9, this._rdOptions);
|
|
3678
4250
|
} catch (error) {
|
|
3679
4251
|
this._onError(error);
|
|
3680
4252
|
}
|
|
3681
|
-
return { files, depth, path:
|
|
4253
|
+
return { files, depth, path: path9 };
|
|
3682
4254
|
}
|
|
3683
|
-
async _formatEntry(dirent,
|
|
4255
|
+
async _formatEntry(dirent, path9) {
|
|
3684
4256
|
let entry;
|
|
3685
|
-
const
|
|
4257
|
+
const basename4 = this._isDirent ? dirent.name : dirent;
|
|
3686
4258
|
try {
|
|
3687
|
-
const fullPath = presolve(pjoin(
|
|
3688
|
-
entry = { path: prelative(this._root, fullPath), fullPath, basename:
|
|
4259
|
+
const fullPath = presolve(pjoin(path9, basename4));
|
|
4260
|
+
entry = { path: prelative(this._root, fullPath), fullPath, basename: basename4 };
|
|
3689
4261
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
3690
4262
|
} catch (err) {
|
|
3691
4263
|
this._onError(err);
|
|
@@ -4084,16 +4656,16 @@ var delFromSet = (main, prop, item) => {
|
|
|
4084
4656
|
};
|
|
4085
4657
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
4086
4658
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
4087
|
-
function createFsWatchInstance(
|
|
4659
|
+
function createFsWatchInstance(path9, options, listener, errHandler, emitRaw) {
|
|
4088
4660
|
const handleEvent = (rawEvent, evPath) => {
|
|
4089
|
-
listener(
|
|
4090
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
4091
|
-
if (evPath &&
|
|
4092
|
-
fsWatchBroadcast(sp.resolve(
|
|
4661
|
+
listener(path9);
|
|
4662
|
+
emitRaw(rawEvent, evPath, { watchedPath: path9 });
|
|
4663
|
+
if (evPath && path9 !== evPath) {
|
|
4664
|
+
fsWatchBroadcast(sp.resolve(path9, evPath), KEY_LISTENERS, sp.join(path9, evPath));
|
|
4093
4665
|
}
|
|
4094
4666
|
};
|
|
4095
4667
|
try {
|
|
4096
|
-
return fs_watch(
|
|
4668
|
+
return fs_watch(path9, {
|
|
4097
4669
|
persistent: options.persistent
|
|
4098
4670
|
}, handleEvent);
|
|
4099
4671
|
} catch (error) {
|
|
@@ -4109,12 +4681,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
4109
4681
|
listener(val1, val2, val3);
|
|
4110
4682
|
});
|
|
4111
4683
|
};
|
|
4112
|
-
var setFsWatchListener = (
|
|
4684
|
+
var setFsWatchListener = (path9, fullPath, options, handlers) => {
|
|
4113
4685
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
4114
4686
|
let cont = FsWatchInstances.get(fullPath);
|
|
4115
4687
|
let watcher;
|
|
4116
4688
|
if (!options.persistent) {
|
|
4117
|
-
watcher = createFsWatchInstance(
|
|
4689
|
+
watcher = createFsWatchInstance(path9, options, listener, errHandler, rawEmitter);
|
|
4118
4690
|
if (!watcher)
|
|
4119
4691
|
return;
|
|
4120
4692
|
return watcher.close.bind(watcher);
|
|
@@ -4125,7 +4697,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
|
|
|
4125
4697
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
4126
4698
|
} else {
|
|
4127
4699
|
watcher = createFsWatchInstance(
|
|
4128
|
-
|
|
4700
|
+
path9,
|
|
4129
4701
|
options,
|
|
4130
4702
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
4131
4703
|
errHandler,
|
|
@@ -4140,7 +4712,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
|
|
|
4140
4712
|
cont.watcherUnusable = true;
|
|
4141
4713
|
if (isWindows && error.code === "EPERM") {
|
|
4142
4714
|
try {
|
|
4143
|
-
const fd = await open(
|
|
4715
|
+
const fd = await open(path9, "r");
|
|
4144
4716
|
await fd.close();
|
|
4145
4717
|
broadcastErr(error);
|
|
4146
4718
|
} catch (err) {
|
|
@@ -4171,7 +4743,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
|
|
|
4171
4743
|
};
|
|
4172
4744
|
};
|
|
4173
4745
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
4174
|
-
var setFsWatchFileListener = (
|
|
4746
|
+
var setFsWatchFileListener = (path9, fullPath, options, handlers) => {
|
|
4175
4747
|
const { listener, rawEmitter } = handlers;
|
|
4176
4748
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
4177
4749
|
const copts = cont && cont.options;
|
|
@@ -4193,7 +4765,7 @@ var setFsWatchFileListener = (path8, fullPath, options, handlers) => {
|
|
|
4193
4765
|
});
|
|
4194
4766
|
const currmtime = curr.mtimeMs;
|
|
4195
4767
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
4196
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
4768
|
+
foreach(cont.listeners, (listener2) => listener2(path9, curr));
|
|
4197
4769
|
}
|
|
4198
4770
|
})
|
|
4199
4771
|
};
|
|
@@ -4223,13 +4795,13 @@ var NodeFsHandler = class {
|
|
|
4223
4795
|
* @param listener on fs change
|
|
4224
4796
|
* @returns closer for the watcher instance
|
|
4225
4797
|
*/
|
|
4226
|
-
_watchWithNodeFs(
|
|
4798
|
+
_watchWithNodeFs(path9, listener) {
|
|
4227
4799
|
const opts = this.fsw.options;
|
|
4228
|
-
const directory = sp.dirname(
|
|
4229
|
-
const
|
|
4800
|
+
const directory = sp.dirname(path9);
|
|
4801
|
+
const basename4 = sp.basename(path9);
|
|
4230
4802
|
const parent = this.fsw._getWatchedDir(directory);
|
|
4231
|
-
parent.add(
|
|
4232
|
-
const absolutePath = sp.resolve(
|
|
4803
|
+
parent.add(basename4);
|
|
4804
|
+
const absolutePath = sp.resolve(path9);
|
|
4233
4805
|
const options = {
|
|
4234
4806
|
persistent: opts.persistent
|
|
4235
4807
|
};
|
|
@@ -4238,13 +4810,13 @@ var NodeFsHandler = class {
|
|
|
4238
4810
|
let closer;
|
|
4239
4811
|
if (opts.usePolling) {
|
|
4240
4812
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
4241
|
-
options.interval = enableBin && isBinaryPath(
|
|
4242
|
-
closer = setFsWatchFileListener(
|
|
4813
|
+
options.interval = enableBin && isBinaryPath(basename4) ? opts.binaryInterval : opts.interval;
|
|
4814
|
+
closer = setFsWatchFileListener(path9, absolutePath, options, {
|
|
4243
4815
|
listener,
|
|
4244
4816
|
rawEmitter: this.fsw._emitRaw
|
|
4245
4817
|
});
|
|
4246
4818
|
} else {
|
|
4247
|
-
closer = setFsWatchListener(
|
|
4819
|
+
closer = setFsWatchListener(path9, absolutePath, options, {
|
|
4248
4820
|
listener,
|
|
4249
4821
|
errHandler: this._boundHandleError,
|
|
4250
4822
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -4260,13 +4832,13 @@ var NodeFsHandler = class {
|
|
|
4260
4832
|
if (this.fsw.closed) {
|
|
4261
4833
|
return;
|
|
4262
4834
|
}
|
|
4263
|
-
const
|
|
4264
|
-
const
|
|
4265
|
-
const parent = this.fsw._getWatchedDir(
|
|
4835
|
+
const dirname5 = sp.dirname(file);
|
|
4836
|
+
const basename4 = sp.basename(file);
|
|
4837
|
+
const parent = this.fsw._getWatchedDir(dirname5);
|
|
4266
4838
|
let prevStats = stats;
|
|
4267
|
-
if (parent.has(
|
|
4839
|
+
if (parent.has(basename4))
|
|
4268
4840
|
return;
|
|
4269
|
-
const listener = async (
|
|
4841
|
+
const listener = async (path9, newStats) => {
|
|
4270
4842
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
4271
4843
|
return;
|
|
4272
4844
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -4280,18 +4852,18 @@ var NodeFsHandler = class {
|
|
|
4280
4852
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
4281
4853
|
}
|
|
4282
4854
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
4283
|
-
this.fsw._closeFile(
|
|
4855
|
+
this.fsw._closeFile(path9);
|
|
4284
4856
|
prevStats = newStats2;
|
|
4285
4857
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
4286
4858
|
if (closer2)
|
|
4287
|
-
this.fsw._addPathCloser(
|
|
4859
|
+
this.fsw._addPathCloser(path9, closer2);
|
|
4288
4860
|
} else {
|
|
4289
4861
|
prevStats = newStats2;
|
|
4290
4862
|
}
|
|
4291
4863
|
} catch (error) {
|
|
4292
|
-
this.fsw._remove(
|
|
4864
|
+
this.fsw._remove(dirname5, basename4);
|
|
4293
4865
|
}
|
|
4294
|
-
} else if (parent.has(
|
|
4866
|
+
} else if (parent.has(basename4)) {
|
|
4295
4867
|
const at = newStats.atimeMs;
|
|
4296
4868
|
const mt = newStats.mtimeMs;
|
|
4297
4869
|
if (!at || at <= mt || mt !== prevStats.mtimeMs) {
|
|
@@ -4316,7 +4888,7 @@ var NodeFsHandler = class {
|
|
|
4316
4888
|
* @param item basename of this item
|
|
4317
4889
|
* @returns true if no more processing is needed for this entry.
|
|
4318
4890
|
*/
|
|
4319
|
-
async _handleSymlink(entry, directory,
|
|
4891
|
+
async _handleSymlink(entry, directory, path9, item) {
|
|
4320
4892
|
if (this.fsw.closed) {
|
|
4321
4893
|
return;
|
|
4322
4894
|
}
|
|
@@ -4326,7 +4898,7 @@ var NodeFsHandler = class {
|
|
|
4326
4898
|
this.fsw._incrReadyCount();
|
|
4327
4899
|
let linkPath;
|
|
4328
4900
|
try {
|
|
4329
|
-
linkPath = await fsrealpath(
|
|
4901
|
+
linkPath = await fsrealpath(path9);
|
|
4330
4902
|
} catch (e) {
|
|
4331
4903
|
this.fsw._emitReady();
|
|
4332
4904
|
return true;
|
|
@@ -4336,12 +4908,12 @@ var NodeFsHandler = class {
|
|
|
4336
4908
|
if (dir.has(item)) {
|
|
4337
4909
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
4338
4910
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
4339
|
-
this.fsw._emit(EV.CHANGE,
|
|
4911
|
+
this.fsw._emit(EV.CHANGE, path9, entry.stats);
|
|
4340
4912
|
}
|
|
4341
4913
|
} else {
|
|
4342
4914
|
dir.add(item);
|
|
4343
4915
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
4344
|
-
this.fsw._emit(EV.ADD,
|
|
4916
|
+
this.fsw._emit(EV.ADD, path9, entry.stats);
|
|
4345
4917
|
}
|
|
4346
4918
|
this.fsw._emitReady();
|
|
4347
4919
|
return true;
|
|
@@ -4371,9 +4943,9 @@ var NodeFsHandler = class {
|
|
|
4371
4943
|
return;
|
|
4372
4944
|
}
|
|
4373
4945
|
const item = entry.path;
|
|
4374
|
-
let
|
|
4946
|
+
let path9 = sp.join(directory, item);
|
|
4375
4947
|
current.add(item);
|
|
4376
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
4948
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path9, item)) {
|
|
4377
4949
|
return;
|
|
4378
4950
|
}
|
|
4379
4951
|
if (this.fsw.closed) {
|
|
@@ -4382,8 +4954,8 @@ var NodeFsHandler = class {
|
|
|
4382
4954
|
}
|
|
4383
4955
|
if (item === target || !target && !previous.has(item)) {
|
|
4384
4956
|
this.fsw._incrReadyCount();
|
|
4385
|
-
|
|
4386
|
-
this._addToNodeFs(
|
|
4957
|
+
path9 = sp.join(dir, sp.relative(dir, path9));
|
|
4958
|
+
this._addToNodeFs(path9, initialAdd, wh, depth + 1);
|
|
4387
4959
|
}
|
|
4388
4960
|
}).on(EV.ERROR, this._boundHandleError);
|
|
4389
4961
|
return new Promise((resolve4, reject) => {
|
|
@@ -4452,13 +5024,13 @@ var NodeFsHandler = class {
|
|
|
4452
5024
|
* @param depth Child path actually targeted for watch
|
|
4453
5025
|
* @param target Child path actually targeted for watch
|
|
4454
5026
|
*/
|
|
4455
|
-
async _addToNodeFs(
|
|
5027
|
+
async _addToNodeFs(path9, initialAdd, priorWh, depth, target) {
|
|
4456
5028
|
const ready = this.fsw._emitReady;
|
|
4457
|
-
if (this.fsw._isIgnored(
|
|
5029
|
+
if (this.fsw._isIgnored(path9) || this.fsw.closed) {
|
|
4458
5030
|
ready();
|
|
4459
5031
|
return false;
|
|
4460
5032
|
}
|
|
4461
|
-
const wh = this.fsw._getWatchHelpers(
|
|
5033
|
+
const wh = this.fsw._getWatchHelpers(path9);
|
|
4462
5034
|
if (priorWh) {
|
|
4463
5035
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
4464
5036
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -4474,8 +5046,8 @@ var NodeFsHandler = class {
|
|
|
4474
5046
|
const follow = this.fsw.options.followSymlinks;
|
|
4475
5047
|
let closer;
|
|
4476
5048
|
if (stats.isDirectory()) {
|
|
4477
|
-
const absPath = sp.resolve(
|
|
4478
|
-
const targetPath = follow ? await fsrealpath(
|
|
5049
|
+
const absPath = sp.resolve(path9);
|
|
5050
|
+
const targetPath = follow ? await fsrealpath(path9) : path9;
|
|
4479
5051
|
if (this.fsw.closed)
|
|
4480
5052
|
return;
|
|
4481
5053
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -4485,29 +5057,29 @@ var NodeFsHandler = class {
|
|
|
4485
5057
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
4486
5058
|
}
|
|
4487
5059
|
} else if (stats.isSymbolicLink()) {
|
|
4488
|
-
const targetPath = follow ? await fsrealpath(
|
|
5060
|
+
const targetPath = follow ? await fsrealpath(path9) : path9;
|
|
4489
5061
|
if (this.fsw.closed)
|
|
4490
5062
|
return;
|
|
4491
5063
|
const parent = sp.dirname(wh.watchPath);
|
|
4492
5064
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
4493
5065
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
4494
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
5066
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path9, wh, targetPath);
|
|
4495
5067
|
if (this.fsw.closed)
|
|
4496
5068
|
return;
|
|
4497
5069
|
if (targetPath !== void 0) {
|
|
4498
|
-
this.fsw._symlinkPaths.set(sp.resolve(
|
|
5070
|
+
this.fsw._symlinkPaths.set(sp.resolve(path9), targetPath);
|
|
4499
5071
|
}
|
|
4500
5072
|
} else {
|
|
4501
5073
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
4502
5074
|
}
|
|
4503
5075
|
ready();
|
|
4504
5076
|
if (closer)
|
|
4505
|
-
this.fsw._addPathCloser(
|
|
5077
|
+
this.fsw._addPathCloser(path9, closer);
|
|
4506
5078
|
return false;
|
|
4507
5079
|
} catch (error) {
|
|
4508
5080
|
if (this.fsw._handleError(error)) {
|
|
4509
5081
|
ready();
|
|
4510
|
-
return
|
|
5082
|
+
return path9;
|
|
4511
5083
|
}
|
|
4512
5084
|
}
|
|
4513
5085
|
}
|
|
@@ -4550,24 +5122,24 @@ function createPattern(matcher) {
|
|
|
4550
5122
|
}
|
|
4551
5123
|
return () => false;
|
|
4552
5124
|
}
|
|
4553
|
-
function normalizePath(
|
|
4554
|
-
if (typeof
|
|
5125
|
+
function normalizePath(path9) {
|
|
5126
|
+
if (typeof path9 !== "string")
|
|
4555
5127
|
throw new Error("string expected");
|
|
4556
|
-
|
|
4557
|
-
|
|
5128
|
+
path9 = sp2.normalize(path9);
|
|
5129
|
+
path9 = path9.replace(/\\/g, "/");
|
|
4558
5130
|
let prepend = false;
|
|
4559
|
-
if (
|
|
5131
|
+
if (path9.startsWith("//"))
|
|
4560
5132
|
prepend = true;
|
|
4561
|
-
|
|
5133
|
+
path9 = path9.replace(DOUBLE_SLASH_RE, "/");
|
|
4562
5134
|
if (prepend)
|
|
4563
|
-
|
|
4564
|
-
return
|
|
5135
|
+
path9 = "/" + path9;
|
|
5136
|
+
return path9;
|
|
4565
5137
|
}
|
|
4566
5138
|
function matchPatterns(patterns, testString, stats) {
|
|
4567
|
-
const
|
|
5139
|
+
const path9 = normalizePath(testString);
|
|
4568
5140
|
for (let index = 0; index < patterns.length; index++) {
|
|
4569
5141
|
const pattern = patterns[index];
|
|
4570
|
-
if (pattern(
|
|
5142
|
+
if (pattern(path9, stats)) {
|
|
4571
5143
|
return true;
|
|
4572
5144
|
}
|
|
4573
5145
|
}
|
|
@@ -4605,19 +5177,19 @@ var toUnix = (string) => {
|
|
|
4605
5177
|
}
|
|
4606
5178
|
return str;
|
|
4607
5179
|
};
|
|
4608
|
-
var normalizePathToUnix = (
|
|
4609
|
-
var normalizeIgnored = (cwd = "") => (
|
|
4610
|
-
if (typeof
|
|
4611
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
5180
|
+
var normalizePathToUnix = (path9) => toUnix(sp2.normalize(toUnix(path9)));
|
|
5181
|
+
var normalizeIgnored = (cwd = "") => (path9) => {
|
|
5182
|
+
if (typeof path9 === "string") {
|
|
5183
|
+
return normalizePathToUnix(sp2.isAbsolute(path9) ? path9 : sp2.join(cwd, path9));
|
|
4612
5184
|
} else {
|
|
4613
|
-
return
|
|
5185
|
+
return path9;
|
|
4614
5186
|
}
|
|
4615
5187
|
};
|
|
4616
|
-
var getAbsolutePath = (
|
|
4617
|
-
if (sp2.isAbsolute(
|
|
4618
|
-
return
|
|
5188
|
+
var getAbsolutePath = (path9, cwd) => {
|
|
5189
|
+
if (sp2.isAbsolute(path9)) {
|
|
5190
|
+
return path9;
|
|
4619
5191
|
}
|
|
4620
|
-
return sp2.join(cwd,
|
|
5192
|
+
return sp2.join(cwd, path9);
|
|
4621
5193
|
};
|
|
4622
5194
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
4623
5195
|
var DirEntry = class {
|
|
@@ -4682,10 +5254,10 @@ var WatchHelper = class {
|
|
|
4682
5254
|
dirParts;
|
|
4683
5255
|
followSymlinks;
|
|
4684
5256
|
statMethod;
|
|
4685
|
-
constructor(
|
|
5257
|
+
constructor(path9, follow, fsw) {
|
|
4686
5258
|
this.fsw = fsw;
|
|
4687
|
-
const watchPath =
|
|
4688
|
-
this.path =
|
|
5259
|
+
const watchPath = path9;
|
|
5260
|
+
this.path = path9 = path9.replace(REPLACER_RE, "");
|
|
4689
5261
|
this.watchPath = watchPath;
|
|
4690
5262
|
this.fullWatchPath = sp2.resolve(watchPath);
|
|
4691
5263
|
this.dirParts = [];
|
|
@@ -4825,20 +5397,20 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
4825
5397
|
this._closePromise = void 0;
|
|
4826
5398
|
let paths = unifyPaths(paths_);
|
|
4827
5399
|
if (cwd) {
|
|
4828
|
-
paths = paths.map((
|
|
4829
|
-
const absPath = getAbsolutePath(
|
|
5400
|
+
paths = paths.map((path9) => {
|
|
5401
|
+
const absPath = getAbsolutePath(path9, cwd);
|
|
4830
5402
|
return absPath;
|
|
4831
5403
|
});
|
|
4832
5404
|
}
|
|
4833
|
-
paths.forEach((
|
|
4834
|
-
this._removeIgnoredPath(
|
|
5405
|
+
paths.forEach((path9) => {
|
|
5406
|
+
this._removeIgnoredPath(path9);
|
|
4835
5407
|
});
|
|
4836
5408
|
this._userIgnored = void 0;
|
|
4837
5409
|
if (!this._readyCount)
|
|
4838
5410
|
this._readyCount = 0;
|
|
4839
5411
|
this._readyCount += paths.length;
|
|
4840
|
-
Promise.all(paths.map(async (
|
|
4841
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
5412
|
+
Promise.all(paths.map(async (path9) => {
|
|
5413
|
+
const res = await this._nodeFsHandler._addToNodeFs(path9, !_internal, void 0, 0, _origAdd);
|
|
4842
5414
|
if (res)
|
|
4843
5415
|
this._emitReady();
|
|
4844
5416
|
return res;
|
|
@@ -4860,17 +5432,17 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
4860
5432
|
return this;
|
|
4861
5433
|
const paths = unifyPaths(paths_);
|
|
4862
5434
|
const { cwd } = this.options;
|
|
4863
|
-
paths.forEach((
|
|
4864
|
-
if (!sp2.isAbsolute(
|
|
5435
|
+
paths.forEach((path9) => {
|
|
5436
|
+
if (!sp2.isAbsolute(path9) && !this._closers.has(path9)) {
|
|
4865
5437
|
if (cwd)
|
|
4866
|
-
|
|
4867
|
-
|
|
5438
|
+
path9 = sp2.join(cwd, path9);
|
|
5439
|
+
path9 = sp2.resolve(path9);
|
|
4868
5440
|
}
|
|
4869
|
-
this._closePath(
|
|
4870
|
-
this._addIgnoredPath(
|
|
4871
|
-
if (this._watched.has(
|
|
5441
|
+
this._closePath(path9);
|
|
5442
|
+
this._addIgnoredPath(path9);
|
|
5443
|
+
if (this._watched.has(path9)) {
|
|
4872
5444
|
this._addIgnoredPath({
|
|
4873
|
-
path:
|
|
5445
|
+
path: path9,
|
|
4874
5446
|
recursive: true
|
|
4875
5447
|
});
|
|
4876
5448
|
}
|
|
@@ -4934,38 +5506,38 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
4934
5506
|
* @param stats arguments to be passed with event
|
|
4935
5507
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
4936
5508
|
*/
|
|
4937
|
-
async _emit(event,
|
|
5509
|
+
async _emit(event, path9, stats) {
|
|
4938
5510
|
if (this.closed)
|
|
4939
5511
|
return;
|
|
4940
5512
|
const opts = this.options;
|
|
4941
5513
|
if (isWindows)
|
|
4942
|
-
|
|
5514
|
+
path9 = sp2.normalize(path9);
|
|
4943
5515
|
if (opts.cwd)
|
|
4944
|
-
|
|
4945
|
-
const args = [
|
|
5516
|
+
path9 = sp2.relative(opts.cwd, path9);
|
|
5517
|
+
const args = [path9];
|
|
4946
5518
|
if (stats != null)
|
|
4947
5519
|
args.push(stats);
|
|
4948
5520
|
const awf = opts.awaitWriteFinish;
|
|
4949
5521
|
let pw;
|
|
4950
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
5522
|
+
if (awf && (pw = this._pendingWrites.get(path9))) {
|
|
4951
5523
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
4952
5524
|
return this;
|
|
4953
5525
|
}
|
|
4954
5526
|
if (opts.atomic) {
|
|
4955
5527
|
if (event === EVENTS.UNLINK) {
|
|
4956
|
-
this._pendingUnlinks.set(
|
|
5528
|
+
this._pendingUnlinks.set(path9, [event, ...args]);
|
|
4957
5529
|
setTimeout(() => {
|
|
4958
|
-
this._pendingUnlinks.forEach((entry,
|
|
5530
|
+
this._pendingUnlinks.forEach((entry, path10) => {
|
|
4959
5531
|
this.emit(...entry);
|
|
4960
5532
|
this.emit(EVENTS.ALL, ...entry);
|
|
4961
|
-
this._pendingUnlinks.delete(
|
|
5533
|
+
this._pendingUnlinks.delete(path10);
|
|
4962
5534
|
});
|
|
4963
5535
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
4964
5536
|
return this;
|
|
4965
5537
|
}
|
|
4966
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
5538
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path9)) {
|
|
4967
5539
|
event = EVENTS.CHANGE;
|
|
4968
|
-
this._pendingUnlinks.delete(
|
|
5540
|
+
this._pendingUnlinks.delete(path9);
|
|
4969
5541
|
}
|
|
4970
5542
|
}
|
|
4971
5543
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -4983,16 +5555,16 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
4983
5555
|
this.emitWithAll(event, args);
|
|
4984
5556
|
}
|
|
4985
5557
|
};
|
|
4986
|
-
this._awaitWriteFinish(
|
|
5558
|
+
this._awaitWriteFinish(path9, awf.stabilityThreshold, event, awfEmit);
|
|
4987
5559
|
return this;
|
|
4988
5560
|
}
|
|
4989
5561
|
if (event === EVENTS.CHANGE) {
|
|
4990
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
5562
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path9, 50);
|
|
4991
5563
|
if (isThrottled)
|
|
4992
5564
|
return this;
|
|
4993
5565
|
}
|
|
4994
5566
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
4995
|
-
const fullPath = opts.cwd ? sp2.join(opts.cwd,
|
|
5567
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path9) : path9;
|
|
4996
5568
|
let stats2;
|
|
4997
5569
|
try {
|
|
4998
5570
|
stats2 = await stat3(fullPath);
|
|
@@ -5023,23 +5595,23 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
5023
5595
|
* @param timeout duration of time to suppress duplicate actions
|
|
5024
5596
|
* @returns tracking object or false if action should be suppressed
|
|
5025
5597
|
*/
|
|
5026
|
-
_throttle(actionType,
|
|
5598
|
+
_throttle(actionType, path9, timeout) {
|
|
5027
5599
|
if (!this._throttled.has(actionType)) {
|
|
5028
5600
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
5029
5601
|
}
|
|
5030
5602
|
const action = this._throttled.get(actionType);
|
|
5031
5603
|
if (!action)
|
|
5032
5604
|
throw new Error("invalid throttle");
|
|
5033
|
-
const actionPath = action.get(
|
|
5605
|
+
const actionPath = action.get(path9);
|
|
5034
5606
|
if (actionPath) {
|
|
5035
5607
|
actionPath.count++;
|
|
5036
5608
|
return false;
|
|
5037
5609
|
}
|
|
5038
5610
|
let timeoutObject;
|
|
5039
5611
|
const clear = () => {
|
|
5040
|
-
const item = action.get(
|
|
5612
|
+
const item = action.get(path9);
|
|
5041
5613
|
const count = item ? item.count : 0;
|
|
5042
|
-
action.delete(
|
|
5614
|
+
action.delete(path9);
|
|
5043
5615
|
clearTimeout(timeoutObject);
|
|
5044
5616
|
if (item)
|
|
5045
5617
|
clearTimeout(item.timeoutObject);
|
|
@@ -5047,7 +5619,7 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
5047
5619
|
};
|
|
5048
5620
|
timeoutObject = setTimeout(clear, timeout);
|
|
5049
5621
|
const thr = { timeoutObject, clear, count: 0 };
|
|
5050
|
-
action.set(
|
|
5622
|
+
action.set(path9, thr);
|
|
5051
5623
|
return thr;
|
|
5052
5624
|
}
|
|
5053
5625
|
_incrReadyCount() {
|
|
@@ -5061,44 +5633,44 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
5061
5633
|
* @param event
|
|
5062
5634
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
5063
5635
|
*/
|
|
5064
|
-
_awaitWriteFinish(
|
|
5636
|
+
_awaitWriteFinish(path9, threshold, event, awfEmit) {
|
|
5065
5637
|
const awf = this.options.awaitWriteFinish;
|
|
5066
5638
|
if (typeof awf !== "object")
|
|
5067
5639
|
return;
|
|
5068
5640
|
const pollInterval = awf.pollInterval;
|
|
5069
5641
|
let timeoutHandler;
|
|
5070
|
-
let fullPath =
|
|
5071
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
5072
|
-
fullPath = sp2.join(this.options.cwd,
|
|
5642
|
+
let fullPath = path9;
|
|
5643
|
+
if (this.options.cwd && !sp2.isAbsolute(path9)) {
|
|
5644
|
+
fullPath = sp2.join(this.options.cwd, path9);
|
|
5073
5645
|
}
|
|
5074
5646
|
const now = /* @__PURE__ */ new Date();
|
|
5075
5647
|
const writes = this._pendingWrites;
|
|
5076
5648
|
function awaitWriteFinishFn(prevStat) {
|
|
5077
5649
|
statcb(fullPath, (err, curStat) => {
|
|
5078
|
-
if (err || !writes.has(
|
|
5650
|
+
if (err || !writes.has(path9)) {
|
|
5079
5651
|
if (err && err.code !== "ENOENT")
|
|
5080
5652
|
awfEmit(err);
|
|
5081
5653
|
return;
|
|
5082
5654
|
}
|
|
5083
5655
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
5084
5656
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
5085
|
-
writes.get(
|
|
5657
|
+
writes.get(path9).lastChange = now2;
|
|
5086
5658
|
}
|
|
5087
|
-
const pw = writes.get(
|
|
5659
|
+
const pw = writes.get(path9);
|
|
5088
5660
|
const df = now2 - pw.lastChange;
|
|
5089
5661
|
if (df >= threshold) {
|
|
5090
|
-
writes.delete(
|
|
5662
|
+
writes.delete(path9);
|
|
5091
5663
|
awfEmit(void 0, curStat);
|
|
5092
5664
|
} else {
|
|
5093
5665
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
5094
5666
|
}
|
|
5095
5667
|
});
|
|
5096
5668
|
}
|
|
5097
|
-
if (!writes.has(
|
|
5098
|
-
writes.set(
|
|
5669
|
+
if (!writes.has(path9)) {
|
|
5670
|
+
writes.set(path9, {
|
|
5099
5671
|
lastChange: now,
|
|
5100
5672
|
cancelWait: () => {
|
|
5101
|
-
writes.delete(
|
|
5673
|
+
writes.delete(path9);
|
|
5102
5674
|
clearTimeout(timeoutHandler);
|
|
5103
5675
|
return event;
|
|
5104
5676
|
}
|
|
@@ -5109,8 +5681,8 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
5109
5681
|
/**
|
|
5110
5682
|
* Determines whether user has asked to ignore this path.
|
|
5111
5683
|
*/
|
|
5112
|
-
_isIgnored(
|
|
5113
|
-
if (this.options.atomic && DOT_RE.test(
|
|
5684
|
+
_isIgnored(path9, stats) {
|
|
5685
|
+
if (this.options.atomic && DOT_RE.test(path9))
|
|
5114
5686
|
return true;
|
|
5115
5687
|
if (!this._userIgnored) {
|
|
5116
5688
|
const { cwd } = this.options;
|
|
@@ -5120,17 +5692,17 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
5120
5692
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
5121
5693
|
this._userIgnored = anymatch(list, void 0);
|
|
5122
5694
|
}
|
|
5123
|
-
return this._userIgnored(
|
|
5695
|
+
return this._userIgnored(path9, stats);
|
|
5124
5696
|
}
|
|
5125
|
-
_isntIgnored(
|
|
5126
|
-
return !this._isIgnored(
|
|
5697
|
+
_isntIgnored(path9, stat4) {
|
|
5698
|
+
return !this._isIgnored(path9, stat4);
|
|
5127
5699
|
}
|
|
5128
5700
|
/**
|
|
5129
5701
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
5130
5702
|
* @param path file or directory pattern being watched
|
|
5131
5703
|
*/
|
|
5132
|
-
_getWatchHelpers(
|
|
5133
|
-
return new WatchHelper(
|
|
5704
|
+
_getWatchHelpers(path9) {
|
|
5705
|
+
return new WatchHelper(path9, this.options.followSymlinks, this);
|
|
5134
5706
|
}
|
|
5135
5707
|
// Directory helpers
|
|
5136
5708
|
// -----------------
|
|
@@ -5162,63 +5734,63 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
5162
5734
|
* @param item base path of item/directory
|
|
5163
5735
|
*/
|
|
5164
5736
|
_remove(directory, item, isDirectory) {
|
|
5165
|
-
const
|
|
5166
|
-
const fullPath = sp2.resolve(
|
|
5167
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
5168
|
-
if (!this._throttle("remove",
|
|
5737
|
+
const path9 = sp2.join(directory, item);
|
|
5738
|
+
const fullPath = sp2.resolve(path9);
|
|
5739
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path9) || this._watched.has(fullPath);
|
|
5740
|
+
if (!this._throttle("remove", path9, 100))
|
|
5169
5741
|
return;
|
|
5170
5742
|
if (!isDirectory && this._watched.size === 1) {
|
|
5171
5743
|
this.add(directory, item, true);
|
|
5172
5744
|
}
|
|
5173
|
-
const wp = this._getWatchedDir(
|
|
5745
|
+
const wp = this._getWatchedDir(path9);
|
|
5174
5746
|
const nestedDirectoryChildren = wp.getChildren();
|
|
5175
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
5747
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path9, nested));
|
|
5176
5748
|
const parent = this._getWatchedDir(directory);
|
|
5177
5749
|
const wasTracked = parent.has(item);
|
|
5178
5750
|
parent.remove(item);
|
|
5179
5751
|
if (this._symlinkPaths.has(fullPath)) {
|
|
5180
5752
|
this._symlinkPaths.delete(fullPath);
|
|
5181
5753
|
}
|
|
5182
|
-
let relPath =
|
|
5754
|
+
let relPath = path9;
|
|
5183
5755
|
if (this.options.cwd)
|
|
5184
|
-
relPath = sp2.relative(this.options.cwd,
|
|
5756
|
+
relPath = sp2.relative(this.options.cwd, path9);
|
|
5185
5757
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
5186
5758
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
5187
5759
|
if (event === EVENTS.ADD)
|
|
5188
5760
|
return;
|
|
5189
5761
|
}
|
|
5190
|
-
this._watched.delete(
|
|
5762
|
+
this._watched.delete(path9);
|
|
5191
5763
|
this._watched.delete(fullPath);
|
|
5192
5764
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
5193
|
-
if (wasTracked && !this._isIgnored(
|
|
5194
|
-
this._emit(eventName,
|
|
5195
|
-
this._closePath(
|
|
5765
|
+
if (wasTracked && !this._isIgnored(path9))
|
|
5766
|
+
this._emit(eventName, path9);
|
|
5767
|
+
this._closePath(path9);
|
|
5196
5768
|
}
|
|
5197
5769
|
/**
|
|
5198
5770
|
* Closes all watchers for a path
|
|
5199
5771
|
*/
|
|
5200
|
-
_closePath(
|
|
5201
|
-
this._closeFile(
|
|
5202
|
-
const dir = sp2.dirname(
|
|
5203
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
5772
|
+
_closePath(path9) {
|
|
5773
|
+
this._closeFile(path9);
|
|
5774
|
+
const dir = sp2.dirname(path9);
|
|
5775
|
+
this._getWatchedDir(dir).remove(sp2.basename(path9));
|
|
5204
5776
|
}
|
|
5205
5777
|
/**
|
|
5206
5778
|
* Closes only file-specific watchers
|
|
5207
5779
|
*/
|
|
5208
|
-
_closeFile(
|
|
5209
|
-
const closers = this._closers.get(
|
|
5780
|
+
_closeFile(path9) {
|
|
5781
|
+
const closers = this._closers.get(path9);
|
|
5210
5782
|
if (!closers)
|
|
5211
5783
|
return;
|
|
5212
5784
|
closers.forEach((closer) => closer());
|
|
5213
|
-
this._closers.delete(
|
|
5785
|
+
this._closers.delete(path9);
|
|
5214
5786
|
}
|
|
5215
|
-
_addPathCloser(
|
|
5787
|
+
_addPathCloser(path9, closer) {
|
|
5216
5788
|
if (!closer)
|
|
5217
5789
|
return;
|
|
5218
|
-
let list = this._closers.get(
|
|
5790
|
+
let list = this._closers.get(path9);
|
|
5219
5791
|
if (!list) {
|
|
5220
5792
|
list = [];
|
|
5221
|
-
this._closers.set(
|
|
5793
|
+
this._closers.set(path9, list);
|
|
5222
5794
|
}
|
|
5223
5795
|
list.push(closer);
|
|
5224
5796
|
}
|
|
@@ -5313,7 +5885,7 @@ var FileWatcher = class {
|
|
|
5313
5885
|
return;
|
|
5314
5886
|
}
|
|
5315
5887
|
const changes = Array.from(this.pendingChanges.entries()).map(
|
|
5316
|
-
([
|
|
5888
|
+
([path9, type]) => ({ path: path9, type })
|
|
5317
5889
|
);
|
|
5318
5890
|
this.pendingChanges.clear();
|
|
5319
5891
|
try {
|
|
@@ -5493,7 +6065,7 @@ var index_codebase = tool({
|
|
|
5493
6065
|
estimateOnly: z.boolean().optional().default(false).describe("Only show cost estimate without indexing"),
|
|
5494
6066
|
verbose: z.boolean().optional().default(false).describe("Show detailed info about skipped files and parsing failures")
|
|
5495
6067
|
},
|
|
5496
|
-
async execute(args) {
|
|
6068
|
+
async execute(args, context) {
|
|
5497
6069
|
const indexer = getIndexer();
|
|
5498
6070
|
if (args.estimateOnly) {
|
|
5499
6071
|
const estimate = await indexer.estimateCost();
|
|
@@ -5502,7 +6074,19 @@ var index_codebase = tool({
|
|
|
5502
6074
|
if (args.force) {
|
|
5503
6075
|
await indexer.clearIndex();
|
|
5504
6076
|
}
|
|
5505
|
-
const stats = await indexer.index()
|
|
6077
|
+
const stats = await indexer.index((progress) => {
|
|
6078
|
+
context.metadata({
|
|
6079
|
+
title: formatProgressTitle(progress),
|
|
6080
|
+
metadata: {
|
|
6081
|
+
phase: progress.phase,
|
|
6082
|
+
filesProcessed: progress.filesProcessed,
|
|
6083
|
+
totalFiles: progress.totalFiles,
|
|
6084
|
+
chunksProcessed: progress.chunksProcessed,
|
|
6085
|
+
totalChunks: progress.totalChunks,
|
|
6086
|
+
percentage: calculatePercentage(progress)
|
|
6087
|
+
}
|
|
6088
|
+
});
|
|
6089
|
+
});
|
|
5506
6090
|
return formatIndexStats(stats, args.verbose ?? false);
|
|
5507
6091
|
}
|
|
5508
6092
|
});
|
|
@@ -5540,6 +6124,51 @@ var index_health_check = tool({
|
|
|
5540
6124
|
return lines.join("\n");
|
|
5541
6125
|
}
|
|
5542
6126
|
});
|
|
6127
|
+
var index_metrics = tool({
|
|
6128
|
+
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.",
|
|
6129
|
+
args: {},
|
|
6130
|
+
async execute() {
|
|
6131
|
+
const indexer = getIndexer();
|
|
6132
|
+
const logger = indexer.getLogger();
|
|
6133
|
+
if (!logger.isEnabled()) {
|
|
6134
|
+
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```';
|
|
6135
|
+
}
|
|
6136
|
+
if (!logger.isMetricsEnabled()) {
|
|
6137
|
+
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```';
|
|
6138
|
+
}
|
|
6139
|
+
return logger.formatMetrics();
|
|
6140
|
+
}
|
|
6141
|
+
});
|
|
6142
|
+
var index_logs = tool({
|
|
6143
|
+
description: "Get recent debug logs from the codebase indexer. Shows timestamped log entries with level and category. Requires debug.enabled=true in config.",
|
|
6144
|
+
args: {
|
|
6145
|
+
limit: z.number().optional().default(20).describe("Maximum number of log entries to return"),
|
|
6146
|
+
category: z.enum(["search", "embedding", "cache", "gc", "branch", "general"]).optional().describe("Filter by log category"),
|
|
6147
|
+
level: z.enum(["error", "warn", "info", "debug"]).optional().describe("Filter by minimum log level")
|
|
6148
|
+
},
|
|
6149
|
+
async execute(args) {
|
|
6150
|
+
const indexer = getIndexer();
|
|
6151
|
+
const logger = indexer.getLogger();
|
|
6152
|
+
if (!logger.isEnabled()) {
|
|
6153
|
+
return 'Debug mode is disabled. Enable it in your config:\n\n```json\n{\n "debug": {\n "enabled": true\n }\n}\n```';
|
|
6154
|
+
}
|
|
6155
|
+
let logs;
|
|
6156
|
+
if (args.category) {
|
|
6157
|
+
logs = logger.getLogsByCategory(args.category, args.limit);
|
|
6158
|
+
} else if (args.level) {
|
|
6159
|
+
logs = logger.getLogsByLevel(args.level, args.limit);
|
|
6160
|
+
} else {
|
|
6161
|
+
logs = logger.getLogs(args.limit);
|
|
6162
|
+
}
|
|
6163
|
+
if (logs.length === 0) {
|
|
6164
|
+
return "No logs recorded yet. Logs are captured during indexing and search operations.";
|
|
6165
|
+
}
|
|
6166
|
+
return logs.map((l) => {
|
|
6167
|
+
const dataStr = l.data ? ` ${JSON.stringify(l.data)}` : "";
|
|
6168
|
+
return `[${l.timestamp}] [${l.level.toUpperCase()}] [${l.category}] ${l.message}${dataStr}`;
|
|
6169
|
+
}).join("\n");
|
|
6170
|
+
}
|
|
6171
|
+
});
|
|
5543
6172
|
function formatIndexStats(stats, verbose = false) {
|
|
5544
6173
|
const lines = [];
|
|
5545
6174
|
if (stats.indexedChunks === 0 && stats.removedChunks === 0) {
|
|
@@ -5601,12 +6230,90 @@ function formatStatus(status) {
|
|
|
5601
6230
|
}
|
|
5602
6231
|
return lines.join("\n");
|
|
5603
6232
|
}
|
|
6233
|
+
function formatProgressTitle(progress) {
|
|
6234
|
+
switch (progress.phase) {
|
|
6235
|
+
case "scanning":
|
|
6236
|
+
return "Scanning files...";
|
|
6237
|
+
case "parsing":
|
|
6238
|
+
return `Parsing: ${progress.filesProcessed}/${progress.totalFiles} files`;
|
|
6239
|
+
case "embedding":
|
|
6240
|
+
return `Embedding: ${progress.chunksProcessed}/${progress.totalChunks} chunks`;
|
|
6241
|
+
case "storing":
|
|
6242
|
+
return "Storing index...";
|
|
6243
|
+
case "complete":
|
|
6244
|
+
return "Indexing complete";
|
|
6245
|
+
default:
|
|
6246
|
+
return "Indexing...";
|
|
6247
|
+
}
|
|
6248
|
+
}
|
|
6249
|
+
function calculatePercentage(progress) {
|
|
6250
|
+
if (progress.phase === "scanning") return 0;
|
|
6251
|
+
if (progress.phase === "complete") return 100;
|
|
6252
|
+
if (progress.phase === "parsing") {
|
|
6253
|
+
if (progress.totalFiles === 0) return 5;
|
|
6254
|
+
return Math.round(5 + progress.filesProcessed / progress.totalFiles * 15);
|
|
6255
|
+
}
|
|
6256
|
+
if (progress.phase === "embedding") {
|
|
6257
|
+
if (progress.totalChunks === 0) return 20;
|
|
6258
|
+
return Math.round(20 + progress.chunksProcessed / progress.totalChunks * 70);
|
|
6259
|
+
}
|
|
6260
|
+
if (progress.phase === "storing") return 95;
|
|
6261
|
+
return 0;
|
|
6262
|
+
}
|
|
6263
|
+
|
|
6264
|
+
// src/commands/loader.ts
|
|
6265
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync5 } from "fs";
|
|
6266
|
+
import * as path7 from "path";
|
|
6267
|
+
function parseFrontmatter(content) {
|
|
6268
|
+
const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
|
|
6269
|
+
const match = content.match(frontmatterRegex);
|
|
6270
|
+
if (!match) {
|
|
6271
|
+
return { frontmatter: {}, body: content.trim() };
|
|
6272
|
+
}
|
|
6273
|
+
const frontmatterLines = match[1].split("\n");
|
|
6274
|
+
const frontmatter = {};
|
|
6275
|
+
for (const line of frontmatterLines) {
|
|
6276
|
+
const colonIndex = line.indexOf(":");
|
|
6277
|
+
if (colonIndex > 0) {
|
|
6278
|
+
const key = line.slice(0, colonIndex).trim();
|
|
6279
|
+
const value = line.slice(colonIndex + 1).trim();
|
|
6280
|
+
frontmatter[key] = value;
|
|
6281
|
+
}
|
|
6282
|
+
}
|
|
6283
|
+
return { frontmatter, body: match[2].trim() };
|
|
6284
|
+
}
|
|
6285
|
+
function loadCommandsFromDirectory(commandsDir) {
|
|
6286
|
+
const commands = /* @__PURE__ */ new Map();
|
|
6287
|
+
if (!existsSync5(commandsDir)) {
|
|
6288
|
+
return commands;
|
|
6289
|
+
}
|
|
6290
|
+
const files = readdirSync2(commandsDir).filter((f) => f.endsWith(".md"));
|
|
6291
|
+
for (const file of files) {
|
|
6292
|
+
const filePath = path7.join(commandsDir, file);
|
|
6293
|
+
const content = readFileSync5(filePath, "utf-8");
|
|
6294
|
+
const { frontmatter, body } = parseFrontmatter(content);
|
|
6295
|
+
const name = path7.basename(file, ".md");
|
|
6296
|
+
const description = frontmatter.description || `Run the ${name} command`;
|
|
6297
|
+
commands.set(name, {
|
|
6298
|
+
description,
|
|
6299
|
+
template: body
|
|
6300
|
+
});
|
|
6301
|
+
}
|
|
6302
|
+
return commands;
|
|
6303
|
+
}
|
|
5604
6304
|
|
|
5605
6305
|
// src/index.ts
|
|
6306
|
+
function getCommandsDir() {
|
|
6307
|
+
let currentDir = process.cwd();
|
|
6308
|
+
if (typeof import.meta !== "undefined" && import.meta.url) {
|
|
6309
|
+
currentDir = path8.dirname(fileURLToPath2(import.meta.url));
|
|
6310
|
+
}
|
|
6311
|
+
return path8.join(currentDir, "..", "commands");
|
|
6312
|
+
}
|
|
5606
6313
|
function loadJsonFile(filePath) {
|
|
5607
6314
|
try {
|
|
5608
|
-
if (
|
|
5609
|
-
const content =
|
|
6315
|
+
if (existsSync6(filePath)) {
|
|
6316
|
+
const content = readFileSync6(filePath, "utf-8");
|
|
5610
6317
|
return JSON.parse(content);
|
|
5611
6318
|
}
|
|
5612
6319
|
} catch {
|
|
@@ -5614,11 +6321,11 @@ function loadJsonFile(filePath) {
|
|
|
5614
6321
|
return null;
|
|
5615
6322
|
}
|
|
5616
6323
|
function loadPluginConfig(projectRoot) {
|
|
5617
|
-
const projectConfig = loadJsonFile(
|
|
6324
|
+
const projectConfig = loadJsonFile(path8.join(projectRoot, ".opencode", "codebase-index.json"));
|
|
5618
6325
|
if (projectConfig) {
|
|
5619
6326
|
return projectConfig;
|
|
5620
6327
|
}
|
|
5621
|
-
const globalConfigPath =
|
|
6328
|
+
const globalConfigPath = path8.join(os3.homedir(), ".config", "opencode", "codebase-index.json");
|
|
5622
6329
|
const globalConfig = loadJsonFile(globalConfigPath);
|
|
5623
6330
|
if (globalConfig) {
|
|
5624
6331
|
return globalConfig;
|
|
@@ -5646,40 +6353,17 @@ var plugin = async ({ directory }) => {
|
|
|
5646
6353
|
codebase_search,
|
|
5647
6354
|
index_codebase,
|
|
5648
6355
|
index_status,
|
|
5649
|
-
index_health_check
|
|
6356
|
+
index_health_check,
|
|
6357
|
+
index_metrics,
|
|
6358
|
+
index_logs
|
|
5650
6359
|
},
|
|
5651
6360
|
async config(cfg) {
|
|
5652
6361
|
cfg.command = cfg.command ?? {};
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
Return the most relevant results with file paths and line numbers.`
|
|
5660
|
-
};
|
|
5661
|
-
cfg.command["find"] = {
|
|
5662
|
-
description: "Find code using hybrid approach (semantic + grep)",
|
|
5663
|
-
template: `Find code related to: $ARGUMENTS
|
|
5664
|
-
|
|
5665
|
-
Strategy:
|
|
5666
|
-
1. First use \`codebase_search\` to find semantically related code
|
|
5667
|
-
2. From the results, identify specific function/class names
|
|
5668
|
-
3. Use grep to find all occurrences of those identifiers
|
|
5669
|
-
4. Combine findings into a comprehensive answer
|
|
5670
|
-
|
|
5671
|
-
If the semantic index doesn't exist, run \`index_codebase\` first.`
|
|
5672
|
-
};
|
|
5673
|
-
cfg.command["index"] = {
|
|
5674
|
-
description: "Index the codebase for semantic search",
|
|
5675
|
-
template: `Run the \`index_codebase\` tool to create or update the semantic search index.
|
|
5676
|
-
|
|
5677
|
-
Show progress and final statistics including:
|
|
5678
|
-
- Number of files processed
|
|
5679
|
-
- Number of chunks indexed
|
|
5680
|
-
- Tokens used
|
|
5681
|
-
- Duration`
|
|
5682
|
-
};
|
|
6362
|
+
const commandsDir = getCommandsDir();
|
|
6363
|
+
const commands = loadCommandsFromDirectory(commandsDir);
|
|
6364
|
+
for (const [name, definition] of commands) {
|
|
6365
|
+
cfg.command[name] = definition;
|
|
6366
|
+
}
|
|
5683
6367
|
}
|
|
5684
6368
|
};
|
|
5685
6369
|
};
|