@softerist/heuristic-mcp 3.0.12 → 3.0.14
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 +79 -56
- package/config.jsonc +173 -102
- package/index.js +69 -57
- package/lib/cache.js +55 -26
- package/lib/config.js +528 -79
- package/lib/constants.js +27 -0
- package/lib/embed-query-process.js +7 -6
- package/lib/embedding-process.js +113 -27
- package/lib/embedding-worker.js +299 -180
- package/lib/project-detector.js +1 -1
- package/lib/vector-store-binary.js +64 -55
- package/lib/vector-store-sqlite.js +83 -73
- package/package.json +1 -1
package/lib/cache.js
CHANGED
|
@@ -356,13 +356,18 @@ export class EmbeddingsCache {
|
|
|
356
356
|
}
|
|
357
357
|
}
|
|
358
358
|
|
|
359
|
-
async ensureLoaded() {
|
|
359
|
+
async ensureLoaded({ preferDisk = false } = {}) {
|
|
360
360
|
if (!this.config.enableCache) return;
|
|
361
361
|
if (!this._clearedAfterIndex) return;
|
|
362
362
|
if (this._loadPromise) return this._loadPromise;
|
|
363
363
|
|
|
364
364
|
this._loadPromise = (async () => {
|
|
365
|
-
|
|
365
|
+
if (preferDisk && this.config.verbose) {
|
|
366
|
+
console.info('[Cache] ensureLoaded: forcing disk vector mode for incremental low-RAM reload');
|
|
367
|
+
}
|
|
368
|
+
await this.load({
|
|
369
|
+
forceVectorLoadMode: preferDisk ? 'disk' : undefined,
|
|
370
|
+
});
|
|
366
371
|
this._clearedAfterIndex = false;
|
|
367
372
|
})().finally(() => {
|
|
368
373
|
this._loadPromise = null;
|
|
@@ -507,7 +512,7 @@ export class EmbeddingsCache {
|
|
|
507
512
|
|
|
508
513
|
// -------------------- Load --------------------
|
|
509
514
|
|
|
510
|
-
async load() {
|
|
515
|
+
async load({ forceVectorLoadMode } = {}) {
|
|
511
516
|
if (!this.config.enableCache) return;
|
|
512
517
|
|
|
513
518
|
try {
|
|
@@ -637,16 +642,25 @@ export class EmbeddingsCache {
|
|
|
637
642
|
}
|
|
638
643
|
}
|
|
639
644
|
|
|
645
|
+
const configuredVectorLoadMode =
|
|
646
|
+
typeof this.config.vectorStoreLoadMode === 'string'
|
|
647
|
+
? this.config.vectorStoreLoadMode.toLowerCase()
|
|
648
|
+
: 'memory';
|
|
649
|
+
const effectiveVectorLoadMode =
|
|
650
|
+
forceVectorLoadMode === 'disk' || forceVectorLoadMode === 'memory'
|
|
651
|
+
? forceVectorLoadMode
|
|
652
|
+
: configuredVectorLoadMode;
|
|
653
|
+
|
|
640
654
|
if (useBinary) {
|
|
641
655
|
try {
|
|
642
656
|
this.binaryStore = await BinaryVectorStore.load(this.config.cacheDirectory, {
|
|
643
657
|
contentCacheEntries: this.config.contentCacheEntries,
|
|
644
658
|
vectorCacheEntries: this.config.vectorCacheEntries,
|
|
645
|
-
vectorLoadMode:
|
|
659
|
+
vectorLoadMode: effectiveVectorLoadMode,
|
|
646
660
|
});
|
|
647
661
|
cacheData = await this.binaryStore.toChunkViews({
|
|
648
662
|
includeContent: this.config.vectorStoreContentMode === 'inline',
|
|
649
|
-
includeVector:
|
|
663
|
+
includeVector: effectiveVectorLoadMode !== 'disk',
|
|
650
664
|
});
|
|
651
665
|
} catch (err) {
|
|
652
666
|
this.binaryStore = null;
|
|
@@ -661,7 +675,7 @@ export class EmbeddingsCache {
|
|
|
661
675
|
if (this.sqliteStore) {
|
|
662
676
|
cacheData = this.sqliteStore.toChunkViews({
|
|
663
677
|
includeContent: this.config.vectorStoreContentMode === 'inline',
|
|
664
|
-
includeVector:
|
|
678
|
+
includeVector: effectiveVectorLoadMode !== 'disk',
|
|
665
679
|
});
|
|
666
680
|
} else {
|
|
667
681
|
// SQLite file missing, need reindex
|
|
@@ -684,7 +698,13 @@ export class EmbeddingsCache {
|
|
|
684
698
|
const allowedExtensions = new Set(
|
|
685
699
|
(this.config.fileExtensions || []).map((ext) => `.${ext}`)
|
|
686
700
|
);
|
|
701
|
+
const allowedFileNames = new Set(this.config.fileNames || []);
|
|
687
702
|
const applyExtensionFilter = !this.binaryStore;
|
|
703
|
+
const shouldKeepFile = (filePath) => {
|
|
704
|
+
const ext = path.extname(filePath);
|
|
705
|
+
if (allowedExtensions.has(ext)) return true;
|
|
706
|
+
return allowedFileNames.has(path.basename(filePath));
|
|
707
|
+
};
|
|
688
708
|
|
|
689
709
|
const rawHashes = hasHashData ? new Map(Object.entries(hashData)) : new Map();
|
|
690
710
|
this.vectorStore = [];
|
|
@@ -693,8 +713,7 @@ export class EmbeddingsCache {
|
|
|
693
713
|
// Single-pass filter + normalization
|
|
694
714
|
for (const chunk of cacheData) {
|
|
695
715
|
if (applyExtensionFilter) {
|
|
696
|
-
|
|
697
|
-
if (!allowedExtensions.has(ext)) continue;
|
|
716
|
+
if (!shouldKeepFile(chunk.file)) continue;
|
|
698
717
|
}
|
|
699
718
|
normalizeChunkVector(chunk);
|
|
700
719
|
this.vectorStore.push(chunk);
|
|
@@ -707,7 +726,7 @@ export class EmbeddingsCache {
|
|
|
707
726
|
if (hasHashData) {
|
|
708
727
|
// Only keep hashes for allowed extensions
|
|
709
728
|
for (const [file, entry] of rawHashes) {
|
|
710
|
-
if (!applyExtensionFilter ||
|
|
729
|
+
if (!applyExtensionFilter || shouldKeepFile(file)) {
|
|
711
730
|
const normalized = normalizeFileHashEntry(entry);
|
|
712
731
|
if (normalized) {
|
|
713
732
|
this.fileHashes.set(file, normalized);
|
|
@@ -814,24 +833,22 @@ export class EmbeddingsCache {
|
|
|
814
833
|
const hashFile = path.join(this.config.cacheDirectory, 'file-hashes.json');
|
|
815
834
|
const metaFile = path.join(this.config.cacheDirectory, CACHE_META_FILE);
|
|
816
835
|
|
|
817
|
-
// Snapshot to avoid race conditions during async write
|
|
836
|
+
// Snapshot to avoid race conditions during async write.
|
|
837
|
+
// Keep this shallow for binary/sqlite to prevent multi-GB vector materialization.
|
|
838
|
+
const snapshotStore = Array.isArray(this.vectorStore) ? [...this.vectorStore] : [];
|
|
839
|
+
const supportsBackendVectorResolve =
|
|
840
|
+
this.config.vectorStoreFormat === 'binary' || this.config.vectorStoreFormat === 'sqlite';
|
|
841
|
+
const hasMissingVectors = snapshotStore.some(
|
|
842
|
+
(chunk) => chunk && (chunk.vector === undefined || chunk.vector === null)
|
|
843
|
+
);
|
|
818
844
|
const useDiskVectors =
|
|
819
|
-
|
|
820
|
-
(this.config.
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
if (!vector) {
|
|
827
|
-
throw new Error(
|
|
828
|
-
`Missing vector data for cache write in disk mode at index ${index}`
|
|
829
|
-
);
|
|
830
|
-
}
|
|
831
|
-
return { ...chunk, vector };
|
|
832
|
-
})
|
|
833
|
-
: [...this.vectorStore]
|
|
834
|
-
: [];
|
|
845
|
+
supportsBackendVectorResolve &&
|
|
846
|
+
(this.config.vectorStoreLoadMode === 'disk' || hasMissingVectors);
|
|
847
|
+
if (hasMissingVectors && !useDiskVectors) {
|
|
848
|
+
throw new Error(
|
|
849
|
+
'Missing vector data for cache write and backend vector resolution is unavailable'
|
|
850
|
+
);
|
|
851
|
+
}
|
|
835
852
|
|
|
836
853
|
this.cacheMeta = {
|
|
837
854
|
version: CACHE_META_VERSION,
|
|
@@ -856,7 +873,10 @@ export class EmbeddingsCache {
|
|
|
856
873
|
snapshotStore,
|
|
857
874
|
{
|
|
858
875
|
contentCacheEntries: this.config.contentCacheEntries,
|
|
876
|
+
vectorCacheEntries: this.config.vectorCacheEntries,
|
|
877
|
+
vectorLoadMode: useDiskVectors ? 'disk' : this.config.vectorStoreLoadMode,
|
|
859
878
|
getContent: (chunk, index) => this.getChunkContent(chunk, index),
|
|
879
|
+
getVector: useDiskVectors ? (chunk, index) => this.getChunkVector(chunk, index) : null,
|
|
860
880
|
preRename: async () => {
|
|
861
881
|
if (this.activeReads > 0) {
|
|
862
882
|
await this.waitForReadersWithTimeout(
|
|
@@ -890,6 +910,7 @@ export class EmbeddingsCache {
|
|
|
890
910
|
snapshotStore,
|
|
891
911
|
{
|
|
892
912
|
getContent: (chunk, index) => this.getChunkContent(chunk, index),
|
|
913
|
+
getVector: useDiskVectors ? (chunk, index) => this.getChunkVector(chunk, index) : null,
|
|
893
914
|
preRename: async () => {
|
|
894
915
|
if (this.activeReads > 0) {
|
|
895
916
|
await this.waitForReadersWithTimeout(
|
|
@@ -1582,6 +1603,14 @@ export class EmbeddingsCache {
|
|
|
1582
1603
|
}
|
|
1583
1604
|
}
|
|
1584
1605
|
this.binaryStore = null;
|
|
1606
|
+
if (this.sqliteStore) {
|
|
1607
|
+
try {
|
|
1608
|
+
this.sqliteStore.close();
|
|
1609
|
+
} catch {
|
|
1610
|
+
// ignore close errors
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
this.sqliteStore = null;
|
|
1585
1614
|
this.fileHashes = new Map();
|
|
1586
1615
|
this.invalidateAnnIndex();
|
|
1587
1616
|
await this.clearCallGraphData();
|