@mrxkun/mcfast-mcp 4.0.14 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/memory/bootstrap/agents-md.js +173 -0
- package/src/memory/index.js +26 -13
- package/src/memory/layers/curated-memory.js +324 -0
- package/src/memory/layers/daily-logs.js +236 -0
- package/src/memory/memory-engine.js +472 -452
- package/src/memory/stores/codebase-database.js +418 -0
- package/src/memory/stores/memory-database.js +425 -0
- package/src/memory/utils/markdown-chunker.js +242 -0
- package/src/memory/watchers/file-watcher.js +286 -20
- package/src/tools/memory_get.js +139 -100
- package/src/tools/memory_search.js +118 -86
|
@@ -1,34 +1,66 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Memory Engine
|
|
3
|
-
*
|
|
2
|
+
* Memory Engine v4.1.0
|
|
3
|
+
* Refactored with Markdown source of truth + SQLite index
|
|
4
|
+
* Two-tier memory: Daily logs + Curated memory
|
|
5
|
+
* Hybrid search: Vector 70% + BM25 30%
|
|
4
6
|
*/
|
|
5
7
|
|
|
6
|
-
import
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import fs from 'fs/promises';
|
|
10
|
+
import crypto from 'crypto';
|
|
11
|
+
|
|
12
|
+
// Database stores
|
|
13
|
+
import { MemoryDatabase } from './stores/memory-database.js';
|
|
14
|
+
import { CodebaseDatabase } from './stores/codebase-database.js';
|
|
15
|
+
|
|
16
|
+
// Watchers
|
|
7
17
|
import { FileWatcher } from './watchers/file-watcher.js';
|
|
18
|
+
|
|
19
|
+
// Indexers
|
|
8
20
|
import { CodeIndexer } from './utils/indexer.js';
|
|
21
|
+
|
|
22
|
+
// Two-tier memory layers
|
|
23
|
+
import { DailyLogs } from './layers/daily-logs.js';
|
|
24
|
+
import { CuratedMemory } from './layers/curated-memory.js';
|
|
25
|
+
|
|
26
|
+
// Bootstrap
|
|
27
|
+
import { AgentsMdBootstrap } from './bootstrap/agents-md.js';
|
|
28
|
+
|
|
29
|
+
// Embedders
|
|
9
30
|
import { UltraEnhancedEmbedder } from './utils/ultra-embedder.js';
|
|
10
31
|
import { SmartRouter } from './utils/smart-router.js';
|
|
11
32
|
import { DashboardClient } from './utils/dashboard-client.js';
|
|
12
|
-
import { DailyLogs } from './utils/daily-logs.js';
|
|
13
33
|
import { SyncEngine } from './utils/sync-engine.js';
|
|
34
|
+
|
|
35
|
+
// Intelligence
|
|
14
36
|
import { PatternDetector, SuggestionEngine, StrategySelector } from '../intelligence/index.js';
|
|
15
|
-
import path from 'path';
|
|
16
37
|
|
|
17
38
|
export class MemoryEngine {
|
|
18
39
|
constructor(options = {}) {
|
|
19
|
-
this.db = new MemoryDatabase(options.dbPath);
|
|
20
|
-
this.indexer = new CodeIndexer(options.indexer);
|
|
21
|
-
this.watcher = null;
|
|
22
40
|
this.projectPath = null;
|
|
23
41
|
this.isInitialized = false;
|
|
24
42
|
|
|
25
|
-
//
|
|
26
|
-
this.memoryPath = options.memoryPath || null;
|
|
43
|
+
// Paths
|
|
44
|
+
this.memoryPath = options.memoryPath || null;
|
|
45
|
+
this.indexPath = options.indexPath || null;
|
|
46
|
+
|
|
47
|
+
// Databases (separate concerns)
|
|
48
|
+
this.memoryDb = null; // For notes/memory (Markdown source)
|
|
49
|
+
this.codebaseDb = null; // For code indexing
|
|
27
50
|
|
|
28
|
-
//
|
|
29
|
-
this.dailyLogs = null;
|
|
51
|
+
// Two-tier memory
|
|
52
|
+
this.dailyLogs = null;
|
|
53
|
+
this.curatedMemory = null;
|
|
30
54
|
|
|
31
|
-
//
|
|
55
|
+
// Bootstrap
|
|
56
|
+
this.agentsBootstrap = null;
|
|
57
|
+
|
|
58
|
+
// Indexing
|
|
59
|
+
this.indexer = new CodeIndexer(options.indexer);
|
|
60
|
+
this.watcher = null;
|
|
61
|
+
this.isScanning = false;
|
|
62
|
+
|
|
63
|
+
// Sync Engine
|
|
32
64
|
this.syncEngine = null;
|
|
33
65
|
this.syncEngineOptions = {
|
|
34
66
|
apiKey: options.apiKey || process.env.MCFAST_TOKEN,
|
|
@@ -36,32 +68,44 @@ export class MemoryEngine {
|
|
|
36
68
|
syncInterval: options.syncInterval || 5 * 60 * 1000
|
|
37
69
|
};
|
|
38
70
|
|
|
39
|
-
//
|
|
71
|
+
// Embedder
|
|
40
72
|
this.embedder = new UltraEnhancedEmbedder({
|
|
41
73
|
dimension: options.dimension || 1024,
|
|
42
74
|
cacheSize: options.cacheSize || 2000,
|
|
43
75
|
useMercury: options.useMercury || false
|
|
44
76
|
});
|
|
45
77
|
|
|
46
|
-
//
|
|
78
|
+
// Smart routing
|
|
47
79
|
this.dashboardClient = options.dashboardClient || new DashboardClient({
|
|
48
80
|
apiKey: process.env.MCFAST_TOKEN
|
|
49
81
|
});
|
|
50
|
-
|
|
51
|
-
// Smart router - intelligent local vs Mercury decision
|
|
52
82
|
this.smartRouter = new SmartRouter({
|
|
53
83
|
dashboardClient: this.dashboardClient
|
|
54
84
|
});
|
|
55
85
|
|
|
86
|
+
// Configuration
|
|
56
87
|
this.useUltraHybrid = options.useUltraHybrid !== false;
|
|
57
88
|
this.targetAccuracy = options.targetAccuracy || 90;
|
|
58
89
|
this.smartRoutingEnabled = options.smartRoutingEnabled !== false;
|
|
59
90
|
|
|
60
|
-
//
|
|
91
|
+
// Intelligence
|
|
61
92
|
this.intelligenceEnabled = options.intelligenceEnabled !== false;
|
|
62
93
|
this.patternDetector = null;
|
|
63
94
|
this.suggestionEngine = null;
|
|
64
95
|
this.strategySelector = null;
|
|
96
|
+
|
|
97
|
+
// Search configuration
|
|
98
|
+
this.searchConfig = {
|
|
99
|
+
hybrid: {
|
|
100
|
+
enabled: options.hybridSearch !== false,
|
|
101
|
+
vectorWeight: options.vectorWeight || 0.7,
|
|
102
|
+
textWeight: options.textWeight || 0.3,
|
|
103
|
+
minScore: options.minScore || 0.35,
|
|
104
|
+
candidateMultiplier: options.candidateMultiplier || 4
|
|
105
|
+
},
|
|
106
|
+
chunkSize: options.chunkSize || 400,
|
|
107
|
+
chunkOverlap: options.chunkOverlap || 80
|
|
108
|
+
};
|
|
65
109
|
}
|
|
66
110
|
|
|
67
111
|
async initialize(projectPath) {
|
|
@@ -69,24 +113,43 @@ export class MemoryEngine {
|
|
|
69
113
|
|
|
70
114
|
this.projectPath = projectPath;
|
|
71
115
|
|
|
72
|
-
//
|
|
116
|
+
// Setup paths
|
|
73
117
|
if (!this.memoryPath) {
|
|
74
|
-
this.memoryPath = path.join(projectPath, '.mcfast'
|
|
118
|
+
this.memoryPath = path.join(projectPath, '.mcfast');
|
|
75
119
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (!this.db.dbPath) {
|
|
79
|
-
this.db.dbPath = path.join(this.memoryPath, 'index.db');
|
|
120
|
+
if (!this.indexPath) {
|
|
121
|
+
this.indexPath = path.join(this.memoryPath, 'index');
|
|
80
122
|
}
|
|
81
123
|
|
|
82
|
-
|
|
124
|
+
// Create directories
|
|
125
|
+
await fs.mkdir(this.memoryPath, { recursive: true });
|
|
126
|
+
await fs.mkdir(this.indexPath, { recursive: true });
|
|
83
127
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
128
|
+
console.log(`[MemoryEngine] Initializing...`);
|
|
129
|
+
console.log(`[MemoryEngine] Project: ${projectPath}`);
|
|
130
|
+
console.log(`[MemoryEngine] Memory path: ${this.memoryPath}`);
|
|
131
|
+
|
|
132
|
+
// Initialize bootstrap (AGENTS.md)
|
|
133
|
+
this.agentsBootstrap = new AgentsMdBootstrap({ projectPath });
|
|
134
|
+
await this.agentsBootstrap.bootstrap();
|
|
135
|
+
|
|
136
|
+
// Initialize databases
|
|
137
|
+
this.memoryDb = new MemoryDatabase(
|
|
138
|
+
path.join(this.indexPath, 'memory.sqlite')
|
|
139
|
+
);
|
|
140
|
+
this.codebaseDb = new CodebaseDatabase(
|
|
141
|
+
path.join(this.indexPath, 'codebase.sqlite')
|
|
142
|
+
);
|
|
88
143
|
|
|
89
|
-
|
|
144
|
+
await this.memoryDb.initialize();
|
|
145
|
+
await this.codebaseDb.initialize();
|
|
146
|
+
|
|
147
|
+
// Initialize two-tier memory
|
|
148
|
+
this.dailyLogs = new DailyLogs({ memoryPath: this.memoryPath });
|
|
149
|
+
this.curatedMemory = new CuratedMemory({ memoryPath: this.memoryPath });
|
|
150
|
+
await this.curatedMemory.ensureFile(); // Create MEMORY.md if not exists
|
|
151
|
+
|
|
152
|
+
// Initialize sync engine
|
|
90
153
|
if (this.syncEngineOptions.apiKey && this.syncEngineOptions.enableSync !== false) {
|
|
91
154
|
this.syncEngine = new SyncEngine({
|
|
92
155
|
memoryPath: this.memoryPath,
|
|
@@ -96,31 +159,50 @@ export class MemoryEngine {
|
|
|
96
159
|
});
|
|
97
160
|
}
|
|
98
161
|
|
|
99
|
-
|
|
162
|
+
// Initialize file watcher
|
|
163
|
+
this.watcher = new FileWatcher(projectPath, this, {
|
|
164
|
+
debounceMs: 1500,
|
|
165
|
+
ignored: [
|
|
166
|
+
'**/node_modules/**',
|
|
167
|
+
'**/.git/**',
|
|
168
|
+
'**/dist/**',
|
|
169
|
+
'**/build/**',
|
|
170
|
+
'**/.mcfast/**',
|
|
171
|
+
'**/*.log'
|
|
172
|
+
]
|
|
173
|
+
});
|
|
100
174
|
await this.watcher.start();
|
|
101
|
-
|
|
175
|
+
|
|
176
|
+
// Perform initial scan
|
|
177
|
+
await this.performInitialScan();
|
|
178
|
+
|
|
179
|
+
// Initialize intelligence
|
|
180
|
+
await this.initializeIntelligence();
|
|
181
|
+
|
|
102
182
|
this.isInitialized = true;
|
|
103
|
-
|
|
183
|
+
|
|
184
|
+
console.log(`[MemoryEngine] ✅ Initialized successfully`);
|
|
185
|
+
console.log(`[MemoryEngine] Hybrid Search: ${this.searchConfig.hybrid.enabled ? 'ENABLED' : 'DISABLED'}`);
|
|
104
186
|
console.log(`[MemoryEngine] Smart Routing: ${this.smartRoutingEnabled ? 'ENABLED' : 'DISABLED'}`);
|
|
105
|
-
console.log(`[MemoryEngine] Intelligence
|
|
187
|
+
console.log(`[MemoryEngine] Intelligence: ${this.intelligenceEnabled ? 'ENABLED' : 'DISABLED'}`);
|
|
106
188
|
|
|
107
|
-
//
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
} catch (error) {
|
|
116
|
-
console.warn('[MemoryEngine] Failed to initialize intelligence engines:', error.message);
|
|
117
|
-
this.intelligenceEnabled = false;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
189
|
+
// Log initialization to daily logs
|
|
190
|
+
await this.dailyLogs.log('Memory Engine Initialized', `Project: ${projectPath}`, {
|
|
191
|
+
stats: this.getStats()
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async initializeIntelligence() {
|
|
196
|
+
if (!this.intelligenceEnabled) return;
|
|
120
197
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
198
|
+
try {
|
|
199
|
+
this.patternDetector = new PatternDetector({ memoryEngine: this });
|
|
200
|
+
this.suggestionEngine = new SuggestionEngine({ memoryEngine: this });
|
|
201
|
+
this.strategySelector = new StrategySelector({ memoryEngine: this });
|
|
202
|
+
|
|
203
|
+
console.log('[MemoryEngine] Intelligence engines initialized');
|
|
204
|
+
|
|
205
|
+
// Load models in background
|
|
124
206
|
(async () => {
|
|
125
207
|
try {
|
|
126
208
|
const loadPromises = [
|
|
@@ -129,63 +211,250 @@ export class MemoryEngine {
|
|
|
129
211
|
this.strategySelector.loadModel?.()
|
|
130
212
|
].filter(Boolean);
|
|
131
213
|
|
|
132
|
-
// Add timeout to prevent hanging
|
|
133
214
|
const timeoutPromise = new Promise((_, reject) =>
|
|
134
215
|
setTimeout(() => reject(new Error('Model loading timeout')), 5000)
|
|
135
216
|
);
|
|
136
217
|
|
|
137
|
-
await Promise.race([
|
|
138
|
-
Promise.all(loadPromises),
|
|
139
|
-
timeoutPromise
|
|
140
|
-
]);
|
|
141
|
-
|
|
218
|
+
await Promise.race([Promise.all(loadPromises), timeoutPromise]);
|
|
142
219
|
console.log('[MemoryEngine] ✅ Intelligence models loaded');
|
|
143
220
|
} catch (error) {
|
|
144
221
|
console.warn('[MemoryEngine] Model loading failed or timed out:', error.message);
|
|
145
|
-
// Don't disable intelligence - models are optional
|
|
146
222
|
}
|
|
147
223
|
})();
|
|
224
|
+
} catch (error) {
|
|
225
|
+
console.warn('[MemoryEngine] Failed to initialize intelligence engines:', error.message);
|
|
226
|
+
this.intelligenceEnabled = false;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async performInitialScan() {
|
|
231
|
+
if (this.isScanning) return;
|
|
232
|
+
this.isScanning = true;
|
|
233
|
+
|
|
234
|
+
console.log('[MemoryEngine] 🔍 Performing initial codebase scan...');
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
// Scan memory files first
|
|
238
|
+
await this.scanMemoryFiles();
|
|
239
|
+
|
|
240
|
+
// Scan codebase
|
|
241
|
+
await this.scanCodebase();
|
|
242
|
+
|
|
243
|
+
// Update indexing progress
|
|
244
|
+
this.codebaseDb?.completeIndexing?.();
|
|
245
|
+
|
|
246
|
+
console.log('[MemoryEngine] ✅ Initial scan complete');
|
|
247
|
+
console.log(`[MemoryEngine] Stats:`, this.getStats());
|
|
248
|
+
|
|
249
|
+
} catch (error) {
|
|
250
|
+
console.error('[MemoryEngine] Error during initial scan:', error);
|
|
251
|
+
} finally {
|
|
252
|
+
this.isScanning = false;
|
|
148
253
|
}
|
|
149
254
|
}
|
|
150
255
|
|
|
151
|
-
async
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
256
|
+
async scanMemoryFiles() {
|
|
257
|
+
console.log('[MemoryEngine] Scanning memory files...');
|
|
258
|
+
|
|
259
|
+
try {
|
|
260
|
+
// Index MEMORY.md
|
|
261
|
+
const memoryFile = path.join(this.memoryPath, 'MEMORY.md');
|
|
262
|
+
if (await this.fileExists(memoryFile)) {
|
|
263
|
+
await this.indexMarkdownFile(memoryFile);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Index daily logs
|
|
267
|
+
const memoryDir = path.join(this.memoryPath, 'memory');
|
|
268
|
+
if (await this.fileExists(memoryDir)) {
|
|
269
|
+
const files = await fs.readdir(memoryDir);
|
|
270
|
+
for (const file of files.filter(f => f.endsWith('.md'))) {
|
|
271
|
+
await this.indexMarkdownFile(path.join(memoryDir, file));
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
console.log('[MemoryEngine] Memory files indexed');
|
|
276
|
+
} catch (error) {
|
|
277
|
+
console.error('[MemoryEngine] Error scanning memory files:', error);
|
|
278
|
+
}
|
|
155
279
|
}
|
|
156
280
|
|
|
281
|
+
async scanCodebase() {
|
|
282
|
+
console.log('[MemoryEngine] Scanning codebase...');
|
|
283
|
+
|
|
284
|
+
const extensions = ['.js', '.jsx', '.ts', '.tsx', '.py', '.java', '.go', '.rs', '.cpp', '.c', '.h'];
|
|
285
|
+
const files = await this.findFiles(this.projectPath, extensions);
|
|
286
|
+
|
|
287
|
+
if (files.length === 0) {
|
|
288
|
+
console.log('[MemoryEngine] No code files found to index');
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
console.log(`[MemoryEngine] Found ${files.length} files to index`);
|
|
293
|
+
|
|
294
|
+
// Start indexing progress
|
|
295
|
+
this.codebaseDb?.startIndexing?.(files.length);
|
|
296
|
+
|
|
297
|
+
let indexed = 0;
|
|
298
|
+
let failed = 0;
|
|
299
|
+
|
|
300
|
+
for (const filePath of files) {
|
|
301
|
+
try {
|
|
302
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
303
|
+
const contentHash = crypto.createHash('md5').update(content).digest('hex');
|
|
304
|
+
|
|
305
|
+
// Skip if already indexed
|
|
306
|
+
if (this.codebaseDb?.isFileIndexed?.(filePath, contentHash)) {
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Index file
|
|
311
|
+
const indexedData = await this.indexer.indexFile(filePath, content);
|
|
312
|
+
await this.storeIndexed(indexedData);
|
|
313
|
+
|
|
314
|
+
indexed++;
|
|
315
|
+
if (indexed % 10 === 0) {
|
|
316
|
+
console.log(`[MemoryEngine] Indexed ${indexed}/${files.length} files...`);
|
|
317
|
+
}
|
|
318
|
+
} catch (error) {
|
|
319
|
+
console.warn(`[MemoryEngine] Failed to index ${filePath}:`, error.message);
|
|
320
|
+
failed++;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
console.log(`[MemoryEngine] Codebase scan complete: ${indexed} indexed, ${failed} failed`);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
async indexMarkdownFile(filePath) {
|
|
328
|
+
try {
|
|
329
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
330
|
+
const contentHash = crypto.createHash('md5').update(content).digest('hex');
|
|
331
|
+
const stats = await fs.stat(filePath);
|
|
332
|
+
const relativePath = path.relative(this.projectPath, filePath);
|
|
333
|
+
|
|
334
|
+
// Skip if unchanged
|
|
335
|
+
if (this.memoryDb?.isFileIndexed?.(relativePath, contentHash)) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Import chunker dynamically
|
|
340
|
+
const { MarkdownChunker } = await import('./utils/markdown-chunker.js');
|
|
341
|
+
const chunker = new MarkdownChunker({
|
|
342
|
+
chunkSize: this.searchConfig.chunkSize * 4, // chars
|
|
343
|
+
overlap: this.searchConfig.chunkOverlap * 4
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
const chunks = chunker.chunk(content, relativePath);
|
|
347
|
+
|
|
348
|
+
// Track file
|
|
349
|
+
this.memoryDb?.upsertFile?.(relativePath, contentHash, stats.mtimeMs, stats.size);
|
|
350
|
+
|
|
351
|
+
// Delete old chunks
|
|
352
|
+
this.memoryDb?.deleteChunksByFile?.(relativePath);
|
|
353
|
+
|
|
354
|
+
// Process chunks
|
|
355
|
+
for (const chunk of chunks) {
|
|
356
|
+
// Check embedding cache
|
|
357
|
+
let embedding = null;
|
|
358
|
+
const cached = this.memoryDb?.getCachedEmbedding?.(chunk.contentHash);
|
|
359
|
+
|
|
360
|
+
if (cached) {
|
|
361
|
+
embedding = new Float32Array(cached.embedding.buffer, cached.embedding.byteOffset, cached.dimensions);
|
|
362
|
+
} else {
|
|
363
|
+
// Generate embedding
|
|
364
|
+
const result = this.embedder.embedCode(chunk.content);
|
|
365
|
+
embedding = result.vector;
|
|
366
|
+
|
|
367
|
+
// Cache it
|
|
368
|
+
this.memoryDb?.cacheEmbedding?.(
|
|
369
|
+
chunk.contentHash,
|
|
370
|
+
Buffer.from(embedding.buffer),
|
|
371
|
+
'simple-embedder',
|
|
372
|
+
embedding.length
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Insert chunk
|
|
377
|
+
this.memoryDb?.insertChunk?.({
|
|
378
|
+
id: chunk.id,
|
|
379
|
+
file_path: chunk.filePath,
|
|
380
|
+
start_line: chunk.startLine,
|
|
381
|
+
end_line: chunk.endLine,
|
|
382
|
+
content: chunk.content,
|
|
383
|
+
content_hash: chunk.contentHash,
|
|
384
|
+
chunk_type: chunk.chunkType
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
// Insert embedding
|
|
388
|
+
this.memoryDb?.insertEmbedding?.({
|
|
389
|
+
chunk_id: chunk.id,
|
|
390
|
+
embedding: Buffer.from(embedding.buffer),
|
|
391
|
+
model: 'simple-embedder',
|
|
392
|
+
dimensions: embedding.length
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
} catch (error) {
|
|
397
|
+
console.warn(`[MemoryEngine] Failed to index markdown ${filePath}:`, error.message);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
async findFiles(dir, extensions) {
|
|
402
|
+
const files = [];
|
|
403
|
+
const ignored = ['node_modules', '.git', 'dist', 'build', '.mcfast'];
|
|
404
|
+
|
|
405
|
+
try {
|
|
406
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
407
|
+
|
|
408
|
+
for (const entry of entries) {
|
|
409
|
+
const fullPath = path.join(dir, entry.name);
|
|
410
|
+
|
|
411
|
+
if (entry.isDirectory() && !ignored.includes(entry.name)) {
|
|
412
|
+
const subFiles = await this.findFiles(fullPath, extensions);
|
|
413
|
+
files.push(...subFiles);
|
|
414
|
+
} else if (entry.isFile() && extensions.some(ext => entry.name.endsWith(ext))) {
|
|
415
|
+
files.push(fullPath);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
} catch (error) {
|
|
419
|
+
// Permission denied or other error, skip
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return files;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
async fileExists(filePath) {
|
|
426
|
+
try {
|
|
427
|
+
await fs.access(filePath);
|
|
428
|
+
return true;
|
|
429
|
+
} catch {
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// ========== Storage Operations ==========
|
|
435
|
+
|
|
157
436
|
async storeIndexed(indexed) {
|
|
158
|
-
this.
|
|
159
|
-
this.
|
|
160
|
-
this.
|
|
437
|
+
this.codebaseDb?.upsertFile?.(indexed.file);
|
|
438
|
+
this.codebaseDb?.deleteFactsByFile?.(indexed.file.id);
|
|
439
|
+
this.codebaseDb?.deleteChunksByFile?.(indexed.file.id);
|
|
161
440
|
|
|
162
441
|
for (const fact of indexed.facts) {
|
|
163
|
-
this.
|
|
442
|
+
this.codebaseDb?.insertFact?.(fact);
|
|
164
443
|
}
|
|
165
444
|
|
|
166
445
|
for (const chunk of indexed.chunks) {
|
|
167
|
-
this.
|
|
446
|
+
this.codebaseDb?.insertChunk?.(chunk);
|
|
168
447
|
}
|
|
169
448
|
|
|
170
449
|
for (const embedding of indexed.embeddings) {
|
|
171
|
-
this.
|
|
450
|
+
this.codebaseDb?.insertEmbedding?.(embedding);
|
|
172
451
|
}
|
|
173
452
|
}
|
|
174
453
|
|
|
175
|
-
|
|
176
|
-
return this.db.getFileByPath(filePath);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
async deleteFile(fileId) {
|
|
180
|
-
return this.db.deleteFile(fileId);
|
|
181
|
-
}
|
|
454
|
+
// ========== Search Operations ==========
|
|
182
455
|
|
|
183
456
|
async searchFacts(query, limit = 20) {
|
|
184
|
-
return this.
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
async searchFTS(query, limit = 20) {
|
|
188
|
-
return this.db.searchFTS(query, limit);
|
|
457
|
+
return this.codebaseDb?.searchFacts?.(query, limit) || [];
|
|
189
458
|
}
|
|
190
459
|
|
|
191
460
|
async searchVector(query, limit = 20) {
|
|
@@ -194,7 +463,7 @@ export class MemoryEngine {
|
|
|
194
463
|
const queryResult = this.embedder.embedCode(query);
|
|
195
464
|
const queryEmbedding = queryResult.vector;
|
|
196
465
|
|
|
197
|
-
const allEmbeddings = this.
|
|
466
|
+
const allEmbeddings = this.codebaseDb?.getAllEmbeddings?.() || [];
|
|
198
467
|
|
|
199
468
|
const scored = allEmbeddings.map(item => {
|
|
200
469
|
const vector = Array.from(new Float32Array(item.embedding.buffer, item.embedding.byteOffset, item.embedding.byteLength / 4));
|
|
@@ -221,465 +490,199 @@ export class MemoryEngine {
|
|
|
221
490
|
};
|
|
222
491
|
}
|
|
223
492
|
|
|
224
|
-
|
|
493
|
+
/**
|
|
494
|
+
* Hybrid search: Combine Vector + BM25
|
|
495
|
+
* Vector 70% + BM25 30%
|
|
496
|
+
*/
|
|
497
|
+
async searchHybrid(query, options = {}) {
|
|
225
498
|
const startTime = performance.now();
|
|
499
|
+
const limit = options.limit || 10;
|
|
226
500
|
|
|
227
|
-
|
|
228
|
-
const allChunks = this.db.getAllChunksWithContent();
|
|
229
|
-
|
|
230
|
-
// Chuẩn bị candidates cho ultra-hybrid search
|
|
231
|
-
const candidates = allChunks.map(chunk => ({
|
|
232
|
-
...chunk,
|
|
233
|
-
embedding: chunk.embedding ? Array.from(new Float32Array(chunk.embedding.buffer, chunk.embedding.byteOffset, chunk.embedding.byteLength / 4)) : null,
|
|
234
|
-
code: chunk.content,
|
|
235
|
-
filePath: chunk.path
|
|
236
|
-
}));
|
|
237
|
-
|
|
238
|
-
// Sử dụng ultra-hybrid search (6 techniques)
|
|
239
|
-
const searchResult = await this.embedder.ultraHybridSearch(query, candidates, { limit });
|
|
501
|
+
console.log(`[MemoryEngine] 🔍 Hybrid search: "${query}"`);
|
|
240
502
|
|
|
241
|
-
|
|
503
|
+
// Get vector results first
|
|
504
|
+
const vectorResult = await this.searchVector(query, limit * 4);
|
|
242
505
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
metadata: {
|
|
246
|
-
...searchResult.metadata,
|
|
247
|
-
totalDuration: duration.toFixed(2) + 'ms',
|
|
248
|
-
method: 'ultra-hybrid-v2',
|
|
249
|
-
targetAccuracy: this.targetAccuracy + '%'
|
|
250
|
-
}
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
async ultraSearch(query, options = {}) {
|
|
255
|
-
const startTime = performance.now();
|
|
506
|
+
// Use database hybrid search
|
|
507
|
+
const dbResults = this.memoryDb?.searchHybrid?.(query, vectorResult.results, limit);
|
|
256
508
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
}));
|
|
267
|
-
|
|
268
|
-
// Ultra-hybrid search với 6 techniques
|
|
269
|
-
const result = await this.embedder.ultraHybridSearch(query, candidates, {
|
|
270
|
-
limit: options.limit || 10,
|
|
271
|
-
...options
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
// Record edit history để học từ searches
|
|
275
|
-
if (options.currentFile) {
|
|
276
|
-
const topResult = result.results[0];
|
|
277
|
-
if (topResult && topResult.filePath) {
|
|
278
|
-
this.embedder.recordEdit(options.currentFile, [topResult.filePath]);
|
|
279
|
-
}
|
|
509
|
+
if (dbResults) {
|
|
510
|
+
const totalDuration = performance.now() - startTime;
|
|
511
|
+
return {
|
|
512
|
+
...dbResults,
|
|
513
|
+
metadata: {
|
|
514
|
+
...dbResults.metadata,
|
|
515
|
+
totalDuration: totalDuration.toFixed(2) + 'ms'
|
|
516
|
+
}
|
|
517
|
+
};
|
|
280
518
|
}
|
|
281
519
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
return {
|
|
285
|
-
...result,
|
|
286
|
-
metadata: {
|
|
287
|
-
...result.metadata,
|
|
288
|
-
totalDuration: duration.toFixed(2) + 'ms',
|
|
289
|
-
method: 'ultra-search',
|
|
290
|
-
accuracy: this.targetAccuracy + '%',
|
|
291
|
-
techniques: ['vector', 'keyword', 'synonym', 'context', 'pattern', 'history']
|
|
292
|
-
}
|
|
293
|
-
};
|
|
520
|
+
// Fallback to vector-only if hybrid fails
|
|
521
|
+
return vectorResult;
|
|
294
522
|
}
|
|
295
523
|
|
|
296
|
-
async
|
|
524
|
+
async searchMemory(query, options = {}) {
|
|
297
525
|
const {
|
|
298
526
|
limit = 10,
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
context = null
|
|
527
|
+
useHybrid = this.searchConfig.hybrid.enabled,
|
|
528
|
+
minScore = this.searchConfig.hybrid.minScore
|
|
302
529
|
} = options;
|
|
303
530
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
// Option B: Mercury re-ranking
|
|
307
|
-
if (useMercury && this.embedder.isMercuryEnabled()) {
|
|
308
|
-
const allChunks = this.db.getAllChunksWithContent();
|
|
309
|
-
const candidates = allChunks.map(chunk => ({
|
|
310
|
-
...chunk,
|
|
311
|
-
embedding: chunk.embedding ? Array.from(new Float32Array(chunk.embedding.buffer, chunk.embedding.byteOffset, chunk.embedding.byteLength / 4)) : null,
|
|
312
|
-
code: chunk.content,
|
|
313
|
-
filePath: chunk.path
|
|
314
|
-
}));
|
|
315
|
-
|
|
316
|
-
return this.embedder.searchWithOptionalReranking(query, candidates, {
|
|
317
|
-
limit,
|
|
318
|
-
useMercury: true
|
|
319
|
-
});
|
|
531
|
+
if (useHybrid) {
|
|
532
|
+
return this.searchHybrid(query, { limit, minScore });
|
|
320
533
|
}
|
|
321
534
|
|
|
322
|
-
|
|
323
|
-
if (useUltra) {
|
|
324
|
-
return this.ultraSearch(query, { limit, ...options });
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// Fallback: Enhanced search
|
|
328
|
-
return this.searchEnhanced(query, limit);
|
|
535
|
+
return this.searchVector(query, limit);
|
|
329
536
|
}
|
|
330
537
|
|
|
331
|
-
async
|
|
332
|
-
|
|
538
|
+
async searchCodebase(query, limit = 20) {
|
|
539
|
+
// Search codebase using existing methods
|
|
540
|
+
const vectorResults = await this.searchVector(query, limit * 2);
|
|
541
|
+
const ftsResults = this.codebaseDb?.searchFTS?.(query, limit * 2);
|
|
333
542
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
const localStart = performance.now();
|
|
338
|
-
const localResult = await this.ultraSearch(query, {
|
|
339
|
-
limit: 20, // Get more for smart decision
|
|
340
|
-
...options
|
|
341
|
-
});
|
|
342
|
-
const localDuration = performance.now() - localStart;
|
|
543
|
+
// Combine and deduplicate
|
|
544
|
+
const seen = new Set();
|
|
545
|
+
const combined = [];
|
|
343
546
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
console.log(`[MemoryEngine] Reason: ${decision.reason}`);
|
|
349
|
-
|
|
350
|
-
// 3. If Mercury needed and enabled
|
|
351
|
-
if (decision.useMercury && this.smartRouter.config.enableMercury) {
|
|
352
|
-
try {
|
|
353
|
-
const mercuryStart = performance.now();
|
|
354
|
-
|
|
355
|
-
// Call Dashboard for Mercury re-ranking
|
|
356
|
-
const mercuryResult = await this.dashboardClient.requestMercuryRerank(
|
|
357
|
-
query,
|
|
358
|
-
localResult.results
|
|
359
|
-
);
|
|
360
|
-
|
|
361
|
-
const mercuryDuration = performance.now() - mercuryStart;
|
|
362
|
-
const totalDuration = performance.now() - startTime;
|
|
363
|
-
|
|
364
|
-
// Log usage
|
|
365
|
-
this.smartRouter.logUsage(query, true, mercuryDuration);
|
|
366
|
-
|
|
367
|
-
// Record for learning
|
|
368
|
-
this.smartRouter.recordResult(query, true, true);
|
|
369
|
-
|
|
370
|
-
return {
|
|
371
|
-
results: mercuryResult.results,
|
|
372
|
-
metadata: {
|
|
373
|
-
...mercuryResult.metadata,
|
|
374
|
-
localDuration: `${localDuration.toFixed(1)}ms`,
|
|
375
|
-
mercuryDuration: `${mercuryDuration.toFixed(1)}ms`,
|
|
376
|
-
totalDuration: `${totalDuration.toFixed(1)}ms`,
|
|
377
|
-
method: 'intelligent-hybrid',
|
|
378
|
-
routingDecision: decision,
|
|
379
|
-
accuracy: '95%'
|
|
380
|
-
}
|
|
381
|
-
};
|
|
382
|
-
|
|
383
|
-
} catch (error) {
|
|
384
|
-
console.warn('[MemoryEngine] Mercury failed, fallback to local:', error.message);
|
|
385
|
-
|
|
386
|
-
// Fallback to local results
|
|
387
|
-
this.smartRouter.recordResult(query, false, true);
|
|
388
|
-
|
|
389
|
-
return {
|
|
390
|
-
...localResult,
|
|
391
|
-
metadata: {
|
|
392
|
-
...localResult.metadata,
|
|
393
|
-
fallback: true,
|
|
394
|
-
fallbackReason: error.message,
|
|
395
|
-
routingDecision: decision
|
|
396
|
-
}
|
|
397
|
-
};
|
|
547
|
+
for (const r of vectorResults.results) {
|
|
548
|
+
if (!seen.has(r.chunk_id)) {
|
|
549
|
+
seen.add(r.chunk_id);
|
|
550
|
+
combined.push({ ...r, source: 'vector' });
|
|
398
551
|
}
|
|
399
552
|
}
|
|
400
553
|
|
|
401
|
-
|
|
402
|
-
|
|
554
|
+
if (ftsResults?.results) {
|
|
555
|
+
for (const r of ftsResults.results) {
|
|
556
|
+
if (!seen.has(r.chunk_id)) {
|
|
557
|
+
seen.add(r.chunk_id);
|
|
558
|
+
combined.push({ ...r, source: 'fts' });
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
403
562
|
|
|
404
|
-
|
|
563
|
+
// Sort by score and limit
|
|
564
|
+
combined.sort((a, b) => (b.similarity || b.score) - (a.similarity || a.score));
|
|
405
565
|
|
|
406
566
|
return {
|
|
407
|
-
|
|
567
|
+
results: combined.slice(0, limit),
|
|
408
568
|
metadata: {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
routingDecision: decision,
|
|
413
|
-
accuracy: '90%'
|
|
569
|
+
vectorCandidates: vectorResults.results.length,
|
|
570
|
+
ftsCandidates: ftsResults?.results?.length || 0,
|
|
571
|
+
totalCandidates: combined.length
|
|
414
572
|
}
|
|
415
573
|
};
|
|
416
574
|
}
|
|
417
575
|
|
|
418
|
-
//
|
|
419
|
-
|
|
420
|
-
/**
|
|
421
|
-
* Enable Mercury re-ranking (Option B)
|
|
422
|
-
*/
|
|
423
|
-
enableMercuryReRanking(apiKey, options = {}) {
|
|
424
|
-
this.embedder.enableMercuryReRanking(apiKey, options);
|
|
425
|
-
this.targetAccuracy = 95; // Tăng target lên 95%
|
|
426
|
-
console.log(`[MemoryEngine] ✨ Mercury re-ranking enabled! Target accuracy: ${this.targetAccuracy}%`);
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
/**
|
|
430
|
-
* Disable Mercury re-ranking
|
|
431
|
-
*/
|
|
432
|
-
disableMercuryReRanking() {
|
|
433
|
-
this.embedder.disableMercuryReRanking();
|
|
434
|
-
this.targetAccuracy = 90; // Quay lại 90%
|
|
435
|
-
console.log(`[MemoryEngine] Mercury disabled. Target accuracy: ${this.targetAccuracy}%`);
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
/**
|
|
439
|
-
* Check Mercury status
|
|
440
|
-
*/
|
|
441
|
-
isMercuryEnabled() {
|
|
442
|
-
return this.embedder.isMercuryEnabled();
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
async recordEdit(edit) {
|
|
446
|
-
return this.db.recordEdit({
|
|
447
|
-
id: Math.random().toString(36).substring(2, 15),
|
|
448
|
-
...edit
|
|
449
|
-
});
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
getStats() {
|
|
453
|
-
return {
|
|
454
|
-
...this.db.getStats(),
|
|
455
|
-
projectPath: this.projectPath
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
/**
|
|
460
|
-
* Get recent chunks from database
|
|
461
|
-
* @param {number} limit - Number of chunks to retrieve
|
|
462
|
-
* @returns {Array} Recent chunks
|
|
463
|
-
*/
|
|
464
|
-
getRecentChunks(limit = 100) {
|
|
465
|
-
return this.db.getRecentChunks(limit);
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
// ==================== SMART ROUTING CONTROLS ====================
|
|
576
|
+
// ========== Two-Tier Memory Operations ==========
|
|
469
577
|
|
|
470
578
|
/**
|
|
471
|
-
*
|
|
579
|
+
* Log to daily logs (Layer 1)
|
|
472
580
|
*/
|
|
473
|
-
|
|
474
|
-
this.
|
|
475
|
-
console.log(`[MemoryEngine] Smart routing ${enabled ? 'ENABLED' : 'DISABLED'}`);
|
|
581
|
+
async logToDaily(title, content, metadata = {}) {
|
|
582
|
+
return this.dailyLogs?.log?.(title, content, metadata);
|
|
476
583
|
}
|
|
477
584
|
|
|
478
585
|
/**
|
|
479
|
-
*
|
|
586
|
+
* Add to curated memory (Layer 2)
|
|
480
587
|
*/
|
|
481
|
-
|
|
482
|
-
return this.
|
|
588
|
+
async addToCurated(type, title, content) {
|
|
589
|
+
return this.curatedMemory?.addEntry?.(type, title, content);
|
|
483
590
|
}
|
|
484
591
|
|
|
485
592
|
/**
|
|
486
|
-
*
|
|
593
|
+
* Get today's log
|
|
487
594
|
*/
|
|
488
|
-
async
|
|
489
|
-
|
|
490
|
-
console.log('[MemoryEngine] Config refreshed from Dashboard');
|
|
595
|
+
async getTodayLog() {
|
|
596
|
+
return this.dailyLogs?.getTodayLog?.();
|
|
491
597
|
}
|
|
492
598
|
|
|
493
|
-
// ==================== DAILY LOGS ====================
|
|
494
|
-
|
|
495
599
|
/**
|
|
496
|
-
*
|
|
600
|
+
* Get yesterday's log
|
|
497
601
|
*/
|
|
498
|
-
|
|
499
|
-
this.dailyLogs
|
|
602
|
+
async getYesterdayLog() {
|
|
603
|
+
return this.dailyLogs?.getYesterdayLog?.();
|
|
500
604
|
}
|
|
501
605
|
|
|
502
606
|
/**
|
|
503
|
-
*
|
|
607
|
+
* Get curated memory content
|
|
504
608
|
*/
|
|
505
|
-
|
|
506
|
-
this.
|
|
609
|
+
async getCuratedMemory() {
|
|
610
|
+
return this.curatedMemory?.read?.();
|
|
507
611
|
}
|
|
508
612
|
|
|
509
613
|
/**
|
|
510
|
-
* Log file
|
|
614
|
+
* Log file operations
|
|
511
615
|
*/
|
|
512
|
-
|
|
513
|
-
this.dailyLogs
|
|
616
|
+
async logFileCreated(filePath, facts = []) {
|
|
617
|
+
return this.dailyLogs?.logFileCreated?.(filePath, facts);
|
|
514
618
|
}
|
|
515
619
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
*/
|
|
519
|
-
logEditToMemory(edit) {
|
|
520
|
-
this.dailyLogs.logEdit(edit);
|
|
521
|
-
// Also record to database
|
|
522
|
-
this.recordEdit(edit);
|
|
620
|
+
async logFileModified(filePath, changes = {}) {
|
|
621
|
+
return this.dailyLogs?.logFileModified?.(filePath, changes);
|
|
523
622
|
}
|
|
524
623
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
*/
|
|
528
|
-
addCuratedMemory(title, content, tags = []) {
|
|
529
|
-
this.dailyLogs.addCuratedMemory(title, content, tags);
|
|
624
|
+
async logFileDeleted(filePath) {
|
|
625
|
+
return this.dailyLogs?.logFileDeleted?.(filePath);
|
|
530
626
|
}
|
|
531
627
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
getCuratedMemories() {
|
|
543
|
-
return this.dailyLogs.getCuratedMemories();
|
|
628
|
+
async logEdit(edit) {
|
|
629
|
+
// Log to daily
|
|
630
|
+
await this.dailyLogs?.logEdit?.(edit);
|
|
631
|
+
|
|
632
|
+
// Record to database
|
|
633
|
+
this.codebaseDb?.recordEdit?.({
|
|
634
|
+
id: Math.random().toString(36).substring(2, 15),
|
|
635
|
+
...edit,
|
|
636
|
+
timestamp: Date.now()
|
|
637
|
+
});
|
|
544
638
|
}
|
|
545
639
|
|
|
546
|
-
//
|
|
640
|
+
// ========== Stats & Info ==========
|
|
547
641
|
|
|
548
|
-
|
|
549
|
-
* Get sync status
|
|
550
|
-
*/
|
|
551
|
-
getSyncStatus() {
|
|
552
|
-
if (!this.syncEngine) {
|
|
553
|
-
return { enabled: false };
|
|
554
|
-
}
|
|
642
|
+
getStats() {
|
|
555
643
|
return {
|
|
556
|
-
|
|
557
|
-
|
|
644
|
+
memory: this.memoryDb?.getStats?.() || {},
|
|
645
|
+
codebase: this.codebaseDb?.getStats?.() || {},
|
|
646
|
+
indexing: this.codebaseDb?.getIndexingProgress?.() || {},
|
|
647
|
+
watcher: this.watcher?.getStats?.() || {},
|
|
648
|
+
projectPath: this.projectPath
|
|
558
649
|
};
|
|
559
650
|
}
|
|
560
651
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
*/
|
|
564
|
-
async triggerSync() {
|
|
565
|
-
if (!this.syncEngine) {
|
|
566
|
-
throw new Error('Sync engine not configured');
|
|
567
|
-
}
|
|
568
|
-
return this.syncEngine.sync();
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
/**
|
|
572
|
-
* Queue change for sync
|
|
573
|
-
*/
|
|
574
|
-
queueSyncChange(change) {
|
|
575
|
-
if (this.syncEngine) {
|
|
576
|
-
this.syncEngine.queueChange(change);
|
|
577
|
-
}
|
|
652
|
+
getRecentChunks(limit = 100) {
|
|
653
|
+
return this.codebaseDb?.getRecentChunks?.(limit) || [];
|
|
578
654
|
}
|
|
579
655
|
|
|
580
|
-
//
|
|
656
|
+
// ========== Intelligence Layer ==========
|
|
581
657
|
|
|
582
|
-
/**
|
|
583
|
-
* Detect patterns from edit history
|
|
584
|
-
* @returns {Array} Detected patterns with confidence scores
|
|
585
|
-
*/
|
|
586
658
|
async detectPatterns() {
|
|
587
659
|
if (!this.intelligenceEnabled || !this.patternDetector) {
|
|
588
660
|
throw new Error('Intelligence layer not enabled');
|
|
589
661
|
}
|
|
590
|
-
|
|
591
|
-
console.log('[MemoryEngine] 🔍 Detecting patterns...');
|
|
592
|
-
|
|
593
|
-
const editHistory = await this.db.getRecentEdits(100);
|
|
594
|
-
const patterns = await this.patternDetector.analyze(editHistory);
|
|
595
662
|
|
|
596
|
-
|
|
597
|
-
return
|
|
663
|
+
const editHistory = this.codebaseDb?.getRecentEdits?.(100) || [];
|
|
664
|
+
return this.patternDetector.analyze(editHistory);
|
|
598
665
|
}
|
|
599
666
|
|
|
600
|
-
/**
|
|
601
|
-
* Get intelligent suggestions based on current context
|
|
602
|
-
* @param {Object} context - Current editing context
|
|
603
|
-
* @returns {Array} Suggestions with confidence scores
|
|
604
|
-
*/
|
|
605
667
|
async getSuggestions(context = {}) {
|
|
606
668
|
if (!this.intelligenceEnabled || !this.suggestionEngine) {
|
|
607
669
|
throw new Error('Intelligence layer not enabled');
|
|
608
670
|
}
|
|
609
|
-
|
|
610
|
-
console.log('[MemoryEngine] 💡 Generating suggestions...');
|
|
611
|
-
|
|
612
|
-
const suggestions = await this.suggestionEngine.getSuggestions(context);
|
|
613
671
|
|
|
614
|
-
|
|
615
|
-
return suggestions;
|
|
672
|
+
return this.suggestionEngine.getSuggestions(context);
|
|
616
673
|
}
|
|
617
674
|
|
|
618
|
-
/**
|
|
619
|
-
* Select best strategy for edit operation
|
|
620
|
-
* @param {string} instruction - Edit instruction
|
|
621
|
-
* @param {Object} context - Edit context
|
|
622
|
-
* @returns {Object} Selected strategy with confidence
|
|
623
|
-
*/
|
|
624
675
|
async selectStrategy(instruction, context = {}) {
|
|
625
676
|
if (!this.intelligenceEnabled || !this.strategySelector) {
|
|
626
|
-
// Fallback: use simple heuristics
|
|
627
677
|
return this.fallbackStrategySelection(instruction, context);
|
|
628
678
|
}
|
|
629
|
-
|
|
630
|
-
console.log('[MemoryEngine] 🎯 Selecting strategy...');
|
|
631
|
-
|
|
632
|
-
const result = await this.strategySelector.selectStrategy(instruction, context);
|
|
633
679
|
|
|
634
|
-
|
|
635
|
-
return result;
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
/**
|
|
639
|
-
* Learn from edit result (improve future selections)
|
|
640
|
-
* @param {Object} edit - Edit data
|
|
641
|
-
* @param {Object} result - Edit result
|
|
642
|
-
*/
|
|
643
|
-
async learnFromEdit(edit, result) {
|
|
644
|
-
if (!this.intelligenceEnabled || !this.strategySelector) {
|
|
645
|
-
return;
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
await this.strategySelector.learn(edit, result);
|
|
649
|
-
console.log('[MemoryEngine] 🧠 Learned from edit result');
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
/**
|
|
653
|
-
* Get intelligence layer statistics
|
|
654
|
-
*/
|
|
655
|
-
getIntelligenceStats() {
|
|
656
|
-
if (!this.intelligenceEnabled) {
|
|
657
|
-
return { enabled: false };
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
return {
|
|
661
|
-
enabled: true,
|
|
662
|
-
patternDetector: this.patternDetector?.getStats() || null,
|
|
663
|
-
suggestionEngine: this.suggestionEngine?.getStats() || null,
|
|
664
|
-
strategySelector: this.strategySelector?.getStats() || null
|
|
665
|
-
};
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
/**
|
|
669
|
-
* Enable/disable intelligence layer
|
|
670
|
-
*/
|
|
671
|
-
setIntelligenceEnabled(enabled) {
|
|
672
|
-
this.intelligenceEnabled = enabled;
|
|
673
|
-
console.log(`[MemoryEngine] Intelligence layer ${enabled ? 'ENABLED' : 'DISABLED'}`);
|
|
680
|
+
return this.strategySelector.selectStrategy(instruction, context);
|
|
674
681
|
}
|
|
675
682
|
|
|
676
|
-
/**
|
|
677
|
-
* Fallback strategy selection when ML is disabled
|
|
678
|
-
*/
|
|
679
683
|
fallbackStrategySelection(instruction, context) {
|
|
680
684
|
const lower = instruction.toLowerCase();
|
|
681
685
|
|
|
682
|
-
// Simple keyword-based selection
|
|
683
686
|
if (lower.includes('replace') || lower.includes('change line') || lower.includes('exact')) {
|
|
684
687
|
return { strategy: 'deterministic', confidence: 0.8, reason: 'Simple replacement detected' };
|
|
685
688
|
}
|
|
@@ -691,19 +694,36 @@ export class MemoryEngine {
|
|
|
691
694
|
return { strategy: 'cached', confidence: 0.6, reason: 'Default selection' };
|
|
692
695
|
}
|
|
693
696
|
|
|
694
|
-
//
|
|
697
|
+
// ========== Sync Engine ==========
|
|
698
|
+
|
|
699
|
+
getSyncStatus() {
|
|
700
|
+
return this.syncEngine?.getStatus?.() || { enabled: false };
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
async triggerSync() {
|
|
704
|
+
if (!this.syncEngine) {
|
|
705
|
+
throw new Error('Sync engine not configured');
|
|
706
|
+
}
|
|
707
|
+
return this.syncEngine.sync();
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// ========== Lifecycle ==========
|
|
695
711
|
|
|
696
712
|
async shutdown() {
|
|
697
|
-
|
|
713
|
+
console.log('[MemoryEngine] Shutting down...');
|
|
714
|
+
|
|
698
715
|
if (this.intelligenceEnabled) {
|
|
699
716
|
await this.strategySelector?.saveModel?.();
|
|
700
|
-
console.log('[MemoryEngine] 💾 Intelligence models saved');
|
|
701
717
|
}
|
|
702
718
|
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
719
|
+
await this.watcher?.stop?.();
|
|
720
|
+
this.syncEngine?.shutdown?.();
|
|
721
|
+
|
|
722
|
+
this.memoryDb?.close?.();
|
|
723
|
+
this.codebaseDb?.close?.();
|
|
724
|
+
|
|
706
725
|
this.isInitialized = false;
|
|
726
|
+
console.log('[MemoryEngine] Shutdown complete');
|
|
707
727
|
}
|
|
708
728
|
}
|
|
709
729
|
|