@mrxkun/mcfast-mcp 4.1.10 → 4.1.12

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.
@@ -59,12 +59,12 @@ export class CodeIndexer {
59
59
  const fileId = this.generateFileId(filePath);
60
60
 
61
61
  try {
62
- // Use getQuery to compile the query properly
62
+ // Try tree-sitter query first (for functions)
63
63
  const query = await getQuery(language, 'definitions');
64
-
65
- if (query) {
64
+
65
+ if (query && ast?.rootNode) {
66
66
  const captures = query.captures(ast.rootNode);
67
-
67
+
68
68
  for (const capture of captures) {
69
69
  if (capture.name === 'name' || capture.name === 'function') {
70
70
  facts.push({
@@ -81,6 +81,95 @@ export class CodeIndexer {
81
81
  }
82
82
  }
83
83
  }
84
+
85
+ // Fallback/enhancement: Extract more facts using regex patterns
86
+ // This works across languages and captures additional symbols
87
+ const content = ast?.text || '';
88
+
89
+ // Extract classes
90
+ const classMatches = content.matchAll(/class\s+(\w+)(?:\s+extends\s+(\w+))?/g);
91
+ for (const match of classMatches) {
92
+ if (!facts.find(f => f.type === 'class' && f.name === match[1])) {
93
+ facts.push({
94
+ id: this.generateFactId(fileId, match[1], 'class'),
95
+ file_id: fileId,
96
+ type: 'class',
97
+ name: match[1],
98
+ line_start: this.findLineNumber(content, match[0]) || 0,
99
+ line_end: 0,
100
+ signature: match[2] ? `extends ${match[2]}` : '',
101
+ exported: this.isExported(content, match[1]),
102
+ confidence: 0.9
103
+ });
104
+ }
105
+ }
106
+
107
+ // Extract interfaces (TypeScript)
108
+ if (language === 'typescript') {
109
+ const interfaceMatches = content.matchAll(/interface\s+(\w+)(?:\s+extends\s+([^{]+))?/g);
110
+ for (const match of interfaceMatches) {
111
+ if (!facts.find(f => f.type === 'interface' && f.name === match[1])) {
112
+ facts.push({
113
+ id: this.generateFactId(fileId, match[1], 'interface'),
114
+ file_id: fileId,
115
+ type: 'interface',
116
+ name: match[1],
117
+ line_start: this.findLineNumber(content, match[0]) || 0,
118
+ line_end: 0,
119
+ signature: match[2] ? `extends ${match[2].trim()}` : '',
120
+ exported: this.isExported(content, match[1]),
121
+ confidence: 0.9
122
+ });
123
+ }
124
+ }
125
+ }
126
+
127
+ // Extract imports
128
+ const importMatches = content.matchAll(/import\s+(?:(\w+)|\{\s*([^}]+)\s*\}|\*\s+as\s+(\w+))\s+from\s+['"]([^'"]+)['"]/g);
129
+ for (const match of importMatches) {
130
+ const imported = match[1] || match[2]?.split(',').map(s => s.trim().split(' ')[0]).join(', ') || match[3];
131
+ facts.push({
132
+ id: this.generateFactId(fileId, imported, 'import'),
133
+ file_id: fileId,
134
+ type: 'import',
135
+ name: imported,
136
+ line_start: this.findLineNumber(content, match[0]) || 0,
137
+ line_end: 0,
138
+ signature: match[4],
139
+ exported: false,
140
+ confidence: 1.0
141
+ });
142
+ }
143
+
144
+ // Extract exports
145
+ const exportMatches = content.matchAll(/export\s+(?:default\s+)?(?:const|let|var|function|class|interface|type)\s+(\w+)/g);
146
+ for (const match of exportMatches) {
147
+ if (!facts.find(f => f.name === match[1] && f.exported)) {
148
+ const existing = facts.find(f => f.name === match[1]);
149
+ if (existing) {
150
+ existing.exported = true;
151
+ }
152
+ }
153
+ }
154
+
155
+ // Extract type definitions (TypeScript)
156
+ if (language === 'typescript') {
157
+ const typeMatches = content.matchAll(/type\s+(\w+)\s*=/g);
158
+ for (const match of typeMatches) {
159
+ facts.push({
160
+ id: this.generateFactId(fileId, match[1], 'type'),
161
+ file_id: fileId,
162
+ type: 'type',
163
+ name: match[1],
164
+ line_start: this.findLineNumber(content, match[0]) || 0,
165
+ line_end: 0,
166
+ signature: 'type alias',
167
+ exported: this.isExported(content, match[1]),
168
+ confidence: 0.8
169
+ });
170
+ }
171
+ }
172
+
84
173
  } catch (error) {
85
174
  console.warn(`[Indexer] Failed to extract facts:`, error.message);
86
175
  }
@@ -88,6 +177,23 @@ export class CodeIndexer {
88
177
  return facts;
89
178
  }
90
179
 
180
+ // Helper to find line number of a string in content
181
+ findLineNumber(content, searchStr) {
182
+ const lines = content.split('\n');
183
+ for (let i = 0; i < lines.length; i++) {
184
+ if (lines[i].includes(searchStr)) {
185
+ return i + 1;
186
+ }
187
+ }
188
+ return 0;
189
+ }
190
+
191
+ // Check if a symbol is exported
192
+ isExported(content, symbolName) {
193
+ return new RegExp(`export\\s+.*\\b${symbolName}\\b`).test(content) ||
194
+ new RegExp(`export\\s+default`).test(content);
195
+ }
196
+
91
197
  async generateEmbeddings(chunks, language = 'javascript') {
92
198
  const embeddings = [];
93
199
 
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Logger Utility for Memory System
3
+ * Provides structured logging with different levels
4
+ *
5
+ * Features:
6
+ * - Log levels: debug, info, warn, error
7
+ * - Timestamp formatting
8
+ * - Module context
9
+ * - Configurable output
10
+ */
11
+
12
+ const LOG_LEVELS = {
13
+ debug: 0,
14
+ info: 1,
15
+ warn: 2,
16
+ error: 3
17
+ };
18
+
19
+ class Logger {
20
+ constructor(options = {}) {
21
+ this.module = options.module || 'Memory';
22
+ this.level = options.level || 'info';
23
+ this.prefix = options.prefix || '';
24
+
25
+ // Set minimum level
26
+ this.minLevel = LOG_LEVELS[options.minLevel?.toLowerCase()] ?? LOG_LEVELS.info;
27
+ }
28
+
29
+ /**
30
+ * Set log level
31
+ */
32
+ setLevel(level) {
33
+ this.minLevel = LOG_LEVELS[level?.toLowerCase()] ?? LOG_LEVELS.info;
34
+ }
35
+
36
+ /**
37
+ * Format timestamp
38
+ */
39
+ _formatTime() {
40
+ return new Date().toISOString();
41
+ }
42
+
43
+ /**
44
+ * Format log message
45
+ */
46
+ _format(level, message, data = null) {
47
+ const timestamp = this._formatTime();
48
+ const context = this.prefix ? `[${this.prefix}]` : `[${this.module}]`;
49
+
50
+ let formatted = `${timestamp} ${context} ${level.toUpperCase()}: ${message}`;
51
+
52
+ if (data) {
53
+ if (data instanceof Error) {
54
+ formatted += `\n Stack: ${data.stack}`;
55
+ } else if (typeof data === 'object') {
56
+ formatted += `\n Data: ${JSON.stringify(data, null, 2)}`;
57
+ } else {
58
+ formatted += `\n ${data}`;
59
+ }
60
+ }
61
+
62
+ return formatted;
63
+ }
64
+
65
+ /**
66
+ * Debug level logging
67
+ */
68
+ debug(message, data = null) {
69
+ if (this.minLevel > LOG_LEVELS.debug) return;
70
+ console.error(this._format('debug', message, data));
71
+ }
72
+
73
+ /**
74
+ * Info level logging
75
+ */
76
+ info(message, data = null) {
77
+ if (this.minLevel > LOG_LEVELS.info) return;
78
+ console.error(this._format('info', message, data));
79
+ }
80
+
81
+ /**
82
+ * Warn level logging
83
+ */
84
+ warn(message, data = null) {
85
+ if (this.minLevel > LOG_LEVELS.warn) return;
86
+ console.error(this._format('warn', message, data));
87
+ }
88
+
89
+ /**
90
+ * Error level logging
91
+ */
92
+ error(message, data = null) {
93
+ if (this.minLevel > LOG_LEVELS.error) return;
94
+ console.error(this._format('error', message, data));
95
+ }
96
+
97
+ /**
98
+ * Create child logger with additional context
99
+ */
100
+ child(options = {}) {
101
+ return new Logger({
102
+ ...options,
103
+ module: this.module,
104
+ minLevel: Object.keys(LOG_LEVELS).find(key => LOG_LEVELS[key] === this.minLevel)
105
+ });
106
+ }
107
+
108
+ /**
109
+ * Timer for performance tracking
110
+ */
111
+ timer(label = 'operation') {
112
+ return new Timer(this, label);
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Timer class for performance tracking
118
+ */
119
+ class Timer {
120
+ constructor(logger, label) {
121
+ this.logger = logger;
122
+ this.label = label;
123
+ this.start = performance.now();
124
+ }
125
+
126
+ end() {
127
+ const duration = performance.now() - this.start;
128
+ this.logger.debug(`${this.label} completed in ${duration.toFixed(2)}ms`);
129
+ return duration;
130
+ }
131
+ }
132
+
133
+ // Pre-configured loggers for common modules
134
+ export const loggers = {
135
+ memory: new Logger({ module: 'MemoryEngine', minLevel: 'info' }),
136
+ watcher: new Logger({ module: 'FileWatcher', minLevel: 'info' }),
137
+ indexer: new Logger({ module: 'Indexer', minLevel: 'info' }),
138
+ database: new Logger({ module: 'Database', minLevel: 'info' }),
139
+ embedder: new Logger({ module: 'Embedder', minLevel: 'info' }),
140
+ intelligence: new Logger({ module: 'Intelligence', minLevel: 'info' })
141
+ };
142
+
143
+ /**
144
+ * Get logger for specific module
145
+ */
146
+ export function getLogger(module, options = {}) {
147
+ if (loggers[module]) {
148
+ return loggers[module].child(options);
149
+ }
150
+ return new Logger({ module, ...options });
151
+ }
152
+
153
+ /**
154
+ * Set global log level
155
+ */
156
+ export function setGlobalLevel(level) {
157
+ Object.values(loggers).forEach(logger => {
158
+ logger.setLevel(level);
159
+ });
160
+ }
161
+
162
+ export default Logger;
@@ -0,0 +1,241 @@
1
+ /**
2
+ * Vector Index for fast similarity search
3
+ * Implements HNSW-like in-memory index for embeddings
4
+ *
5
+ * This provides O(log n) search instead of O(n) linear scan
6
+ */
7
+
8
+ export class VectorIndex {
9
+ constructor(options = {}) {
10
+ this.dimension = options.dimension || 1024;
11
+ this.maxElements = options.maxElements || 10000;
12
+ this.efConstruction = options.efConstruction || 200;
13
+ this.M = options.M || 16;
14
+
15
+ // In-memory storage
16
+ this.vectors = new Map(); // id -> Float32Array
17
+ this.metadata = new Map(); // id -> metadata
18
+
19
+ // Simple HNSW-like structure (layer 0 only for simplicity)
20
+ this.connections = new Map(); // id -> [neighbor_ids]
21
+
22
+ // Entry point for search
23
+ this.entryPoint = null;
24
+
25
+ // Stats
26
+ this.stats = {
27
+ searches: 0,
28
+ totalDuration: 0,
29
+ avgDuration: 0
30
+ };
31
+ }
32
+
33
+ /**
34
+ * Add vector to index
35
+ */
36
+ add(id, vector, metadata = {}) {
37
+ if (vector.length !== this.dimension) {
38
+ throw new Error(`Vector dimension ${vector.length} != expected ${this.dimension}`);
39
+ }
40
+
41
+ const vec = vector instanceof Float32Array ? vector : new Float32Array(vector);
42
+ this.vectors.set(id, vec);
43
+ this.metadata.set(id, metadata);
44
+
45
+ // Initialize empty connections
46
+ if (!this.connections.has(id)) {
47
+ this.connections.set(id, []);
48
+ }
49
+
50
+ // Set entry point if first element
51
+ if (!this.entryPoint) {
52
+ this.entryPoint = id;
53
+ }
54
+
55
+ // Build connections (simplified - just connect to nearest)
56
+ this._buildConnections(id, vec);
57
+
58
+ return this;
59
+ }
60
+
61
+ /**
62
+ * Build connections to nearest neighbors
63
+ */
64
+ _buildConnections(id, vector) {
65
+ // Find nearest neighbors
66
+ const neighbors = [];
67
+
68
+ for (const [otherId, otherVec] of this.vectors) {
69
+ if (otherId === id) continue;
70
+
71
+ const dist = this._cosineDistance(vector, otherVec);
72
+ neighbors.push({ id: otherId, dist });
73
+ }
74
+
75
+ // Sort by distance and take M nearest
76
+ neighbors.sort((a, b) => a.dist - b.dist);
77
+ const nearest = neighbors.slice(0, this.M);
78
+
79
+ // Update connections
80
+ const connections = this.connections.get(id) || [];
81
+ nearest.forEach(n => connections.push(n.id));
82
+ this.connections.set(id, connections);
83
+
84
+ // Add reverse connections
85
+ nearest.forEach(n => {
86
+ const reverseConns = this.connections.get(n.id) || [];
87
+ if (!reverseConns.includes(id)) {
88
+ reverseConns.push(id);
89
+ this.connections.set(n.id, reverseConns);
90
+ }
91
+ });
92
+ }
93
+
94
+ /**
95
+ * Search for nearest neighbors
96
+ */
97
+ search(queryVector, k = 5, ef = 10) {
98
+ const startTime = performance.now();
99
+
100
+ if (!this.entryPoint || this.vectors.size === 0) {
101
+ return [];
102
+ }
103
+
104
+ const query = queryVector instanceof Float32Array
105
+ ? queryVector
106
+ : new Float32Array(queryVector);
107
+
108
+ // Greedy search from entry point
109
+ let current = this.entryPoint;
110
+ let bestDist = this._cosineDistance(query, this.vectors.get(current));
111
+ let visited = new Set([current]);
112
+
113
+ // Explore neighbors
114
+ let candidates = [current];
115
+ let results = [{ id: current, dist: bestDist }];
116
+
117
+ while (candidates.length > 0) {
118
+ const candidate = candidates.shift();
119
+ const candidateVec = this.vectors.get(candidate);
120
+ const candidateDist = this._cosineDistance(query, candidateVec);
121
+
122
+ // Update results
123
+ if (results.length < k || candidateDist < results[results.length - 1].dist) {
124
+ results.push({ id: candidate, dist: candidateDist });
125
+ results.sort((a, b) => a.dist - b.dist);
126
+ results = results.slice(0, k);
127
+ }
128
+
129
+ // Get neighbors
130
+ const neighbors = this.connections.get(candidate) || [];
131
+ for (const neighbor of neighbors) {
132
+ if (visited.has(neighbor)) continue;
133
+ visited.add(neighbor);
134
+
135
+ const neighborDist = this._cosineDistance(query, this.vectors.get(neighbor));
136
+
137
+ // Add to candidates if promising
138
+ if (results.length < k || neighborDist < results[results.length - 1].dist) {
139
+ candidates.push(neighbor);
140
+ }
141
+ }
142
+ }
143
+
144
+ // Sort final results
145
+ results.sort((a, b) => a.dist - b.dist);
146
+
147
+ // Convert distance to similarity
148
+ const searchResults = results.slice(0, k).map(r => ({
149
+ id: r.id,
150
+ similarity: 1 - r.dist,
151
+ distance: r.dist,
152
+ metadata: this.metadata.get(r.id)
153
+ }));
154
+
155
+ // Update stats
156
+ const duration = performance.now() - startTime;
157
+ this.stats.searches++;
158
+ this.stats.totalDuration += duration;
159
+ this.stats.avgDuration = this.stats.totalDuration / this.stats.searches;
160
+
161
+ return searchResults;
162
+ }
163
+
164
+ /**
165
+ * Cosine distance (1 - cosine similarity)
166
+ */
167
+ _cosineDistance(a, b) {
168
+ let dotProduct = 0;
169
+ let normA = 0;
170
+ let normB = 0;
171
+
172
+ for (let i = 0; i < a.length; i++) {
173
+ dotProduct += a[i] * b[i];
174
+ normA += a[i] * a[i];
175
+ normB += b[i] * b[i];
176
+ }
177
+
178
+ const similarity = dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
179
+ return 1 - similarity;
180
+ }
181
+
182
+ /**
183
+ * Bulk add vectors
184
+ */
185
+ bulkAdd(items) {
186
+ for (const item of items) {
187
+ this.add(item.id, item.vector, item.metadata);
188
+ }
189
+ return this;
190
+ }
191
+
192
+ /**
193
+ * Get statistics
194
+ */
195
+ getStats() {
196
+ return {
197
+ ...this.stats,
198
+ size: this.vectors.size,
199
+ dimension: this.dimension,
200
+ maxElements: this.maxElements
201
+ };
202
+ }
203
+
204
+ /**
205
+ * Clear index
206
+ */
207
+ clear() {
208
+ this.vectors.clear();
209
+ this.metadata.clear();
210
+ this.connections.clear();
211
+ this.entryPoint = null;
212
+ this.stats = { searches: 0, totalDuration: 0, avgDuration: 0 };
213
+ }
214
+
215
+ /**
216
+ * Serialize to JSON
217
+ */
218
+ toJSON() {
219
+ const data = {};
220
+ for (const [id, vec] of this.vectors) {
221
+ data[id] = {
222
+ vector: Array.from(vec),
223
+ metadata: this.metadata.get(id)
224
+ };
225
+ }
226
+ return data;
227
+ }
228
+
229
+ /**
230
+ * Load from JSON
231
+ */
232
+ static fromJSON(json, options = {}) {
233
+ const index = new VectorIndex(options);
234
+ for (const [id, item] of Object.entries(json)) {
235
+ index.add(id, item.vector, item.metadata);
236
+ }
237
+ return index;
238
+ }
239
+ }
240
+
241
+ export default VectorIndex;