@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.
@@ -1,32 +1,37 @@
1
1
  /**
2
- * memory_get Tool v2.0
3
- * Read content from memory storage using MemoryEngine
4
- * Version: 2.0.0
2
+ * memory_get Tool v3.0
3
+ * Read content from memory storage using MemoryEngine v4.1.0
4
+ * Supports: Two-tier memory (daily logs + curated), AGENTS.md
5
5
  */
6
6
 
7
7
  import fs from 'fs/promises';
8
8
  import path from 'path';
9
- import os from 'os';
10
9
  import { MemoryEngine } from '../memory/index.js';
11
10
 
12
11
  // Memory engine instance (initialized lazily)
13
12
  let memoryEngine = null;
13
+ let enginePromise = null;
14
14
 
15
15
  async function getMemoryEngine() {
16
- if (!memoryEngine) {
16
+ if (memoryEngine) return memoryEngine;
17
+ if (enginePromise) return enginePromise;
18
+
19
+ enginePromise = (async () => {
17
20
  memoryEngine = new MemoryEngine({
18
21
  apiKey: process.env.MCFAST_TOKEN,
19
22
  enableSync: true
20
23
  });
21
24
  await memoryEngine.initialize(process.cwd());
22
- }
23
- return memoryEngine;
25
+ return memoryEngine;
26
+ })();
27
+
28
+ return enginePromise;
24
29
  }
25
30
 
26
31
  /**
27
32
  * Execute memory get
28
33
  * @param {Object} args - Tool arguments
29
- * @param {string} args.path - Path to memory file (relative to ~/.mcfast/memory/)
34
+ * @param {string} args.path - Path to memory file
30
35
  * @param {number} [args.from=1] - Start line
31
36
  * @param {number} [args.lines=50] - Number of lines to read
32
37
  * @returns {Promise<Object>} File content
@@ -50,43 +55,48 @@ export async function execute(args) {
50
55
 
51
56
  try {
52
57
  // Special paths
53
- if (relativePath === 'logs' || relativePath === 'memory/logs') {
54
- return await getMemoryLogs(from, lines);
55
- }
56
-
57
- if (relativePath === 'stats' || relativePath === 'memory/stats') {
58
- return await getMemoryStats();
58
+ if (relativePath === 'agents' || relativePath === 'AGENTS.md') {
59
+ return await getAgentsMd();
59
60
  }
60
61
 
61
62
  if (relativePath === 'today' || relativePath === 'memory/today') {
62
63
  return await getTodayLog();
63
64
  }
64
65
 
65
- if (relativePath === 'curated' || relativePath === 'memory/curated') {
66
- return await getCuratedMemories();
66
+ if (relativePath === 'yesterday' || relativePath === 'memory/yesterday') {
67
+ return await getYesterdayLog();
67
68
  }
68
69
 
69
- // Security: Only allow reading from memory directory
70
- if (!relativePath.startsWith('memory/') && !relativePath.startsWith('/')) {
71
- return {
72
- content: [{
73
- type: "text",
74
- text: "❌ Error: Invalid path. Must be in memory/ directory"
75
- }],
76
- isError: true
77
- };
70
+ if (relativePath === 'curated' || relativePath === 'MEMORY.md') {
71
+ return await getCuratedMemory();
72
+ }
73
+
74
+ if (relativePath === 'stats' || relativePath === 'memory/stats') {
75
+ return await getMemoryStats();
78
76
  }
79
77
 
78
+ // Regular file path - resolve relative to project
79
+ const projectPath = process.cwd();
80
+ const mcfastPath = path.join(projectPath, '.mcfast');
81
+
80
82
  // Construct full path
81
- const memoryDir = path.join(os.homedir(), '.mcfast', 'memory');
82
- const fullPath = path.join(memoryDir, relativePath.replace(/^\//, ''));
83
+ let fullPath;
84
+ if (relativePath.startsWith('/')) {
85
+ fullPath = relativePath;
86
+ } else if (relativePath.startsWith('memory/')) {
87
+ fullPath = path.join(mcfastPath, relativePath);
88
+ } else if (relativePath.startsWith('.mcfast/')) {
89
+ fullPath = path.join(projectPath, relativePath);
90
+ } else {
91
+ fullPath = path.join(mcfastPath, relativePath);
92
+ }
83
93
 
84
- // Security: Ensure path is within memory directory
85
- if (!fullPath.startsWith(memoryDir)) {
94
+ // Security: Ensure path is within .mcfast directory
95
+ if (!fullPath.startsWith(mcfastPath)) {
86
96
  return {
87
97
  content: [{
88
98
  type: "text",
89
- text: "❌ Error: Path traversal not allowed"
99
+ text: "❌ Error: Path must be within .mcfast directory"
90
100
  }],
91
101
  isError: true
92
102
  };
@@ -108,7 +118,7 @@ export async function execute(args) {
108
118
  return {
109
119
  content: [{
110
120
  type: "text",
111
- text: `❌ File not found: ${relativePath}`
121
+ text: `❌ File not found: ${relativePath}\n\nTip: Use 'memory_search' to find available files.`
112
122
  }],
113
123
  isError: true
114
124
  };
@@ -125,11 +135,16 @@ export async function execute(args) {
125
135
  const extractedContent = extractedLines.join('\n');
126
136
 
127
137
  // Format output
128
- let output = `📄 Memory: ${relativePath}\n`;
138
+ let output = `📄 ${path.basename(fullPath)}\n`;
139
+ output += `Path: ${relativePath}\n`;
129
140
  output += `Lines: ${startLine}-${endLine} of ${allLines.length}\n`;
130
- output += '─'.repeat(40) + '\n';
141
+ output += '─'.repeat(40) + '\n\n';
131
142
  output += extractedContent;
132
143
 
144
+ if (endLine < allLines.length) {
145
+ output += `\n\n... (${allLines.length - endLine} more lines)`;
146
+ }
147
+
133
148
  return {
134
149
  content: [{
135
150
  type: "text",
@@ -138,7 +153,9 @@ export async function execute(args) {
138
153
  metadata: {
139
154
  path: relativePath,
140
155
  lines: extractedLines.length,
141
- totalLines: allLines.length
156
+ totalLines: allLines.length,
157
+ from: startLine,
158
+ to: endLine
142
159
  }
143
160
  };
144
161
  } catch (error) {
@@ -153,19 +170,47 @@ export async function execute(args) {
153
170
  }
154
171
  }
155
172
 
173
+ /**
174
+ * Get AGENTS.md content
175
+ */
176
+ async function getAgentsMd() {
177
+ try {
178
+ const projectPath = process.cwd();
179
+ const agentsPath = path.join(projectPath, '.mcfast', 'AGENTS.md');
180
+
181
+ const content = await fs.readFile(agentsPath, 'utf8');
182
+
183
+ return {
184
+ content: [{
185
+ type: "text",
186
+ text: `📋 Agent Instructions (AGENTS.md)\n\n${content}`
187
+ }],
188
+ metadata: { path: 'AGENTS.md' }
189
+ };
190
+ } catch (error) {
191
+ return {
192
+ content: [{
193
+ type: "text",
194
+ text: "❌ AGENTS.md not found. Run initialization first."
195
+ }],
196
+ isError: true
197
+ };
198
+ }
199
+ }
200
+
156
201
  /**
157
202
  * Get today's daily log
158
203
  */
159
204
  async function getTodayLog() {
160
205
  try {
161
206
  const engine = await getMemoryEngine();
162
- const todayLog = engine.getTodayLog();
207
+ const todayLog = await engine.getTodayLog();
163
208
 
164
- if (!todayLog || todayLog.length === 0) {
209
+ if (!todayLog) {
165
210
  return {
166
211
  content: [{
167
212
  type: "text",
168
- text: "📝 No activity logged today"
213
+ text: "📝 No activity logged today\n\nToday's log will be created when events are recorded."
169
214
  }],
170
215
  metadata: { empty: true }
171
216
  };
@@ -179,7 +224,8 @@ async function getTodayLog() {
179
224
  content: [{
180
225
  type: "text",
181
226
  text: output
182
- }]
227
+ }],
228
+ metadata: { path: 'memory/YYYY-MM-DD.md' }
183
229
  };
184
230
  } catch (error) {
185
231
  return {
@@ -193,44 +239,39 @@ async function getTodayLog() {
193
239
  }
194
240
 
195
241
  /**
196
- * Get curated memories
242
+ * Get yesterday's daily log
197
243
  */
198
- async function getCuratedMemories() {
244
+ async function getYesterdayLog() {
199
245
  try {
200
246
  const engine = await getMemoryEngine();
201
- const memories = engine.getCuratedMemories();
247
+ const yesterdayLog = await engine.getYesterdayLog();
202
248
 
203
- if (!memories || memories.length === 0) {
249
+ if (!yesterdayLog) {
204
250
  return {
205
251
  content: [{
206
252
  type: "text",
207
- text: "📝 No curated memories found"
253
+ text: "📝 No activity logged yesterday"
208
254
  }],
209
255
  metadata: { empty: true }
210
256
  };
211
257
  }
212
258
 
213
- let output = `📚 Curated Memories\n`;
214
- output += `Found: ${memories.length} memory(s)\n`;
259
+ let output = `📅 Yesterday's Activity Log\n`;
215
260
  output += '─'.repeat(40) + '\n\n';
216
-
217
- memories.forEach((memory, idx) => {
218
- output += `[${idx + 1}] ${memory.title}\n`;
219
- output += `Tags: ${memory.tags?.join(', ') || 'none'}\n`;
220
- output += `${memory.content?.substring(0, 200) || ''}...\n\n`;
221
- });
261
+ output += yesterdayLog;
222
262
 
223
263
  return {
224
264
  content: [{
225
265
  type: "text",
226
266
  text: output
227
- }]
267
+ }],
268
+ metadata: { path: 'memory/YYYY-MM-DD.md' }
228
269
  };
229
270
  } catch (error) {
230
271
  return {
231
272
  content: [{
232
273
  type: "text",
233
- text: `❌ Error getting curated memories: ${error.message}`
274
+ text: `❌ Error getting yesterday's log: ${error.message}`
234
275
  }],
235
276
  isError: true
236
277
  };
@@ -238,58 +279,41 @@ async function getCuratedMemories() {
238
279
  }
239
280
 
240
281
  /**
241
- * Get memory logs (daily logs)
282
+ * Get curated memory (MEMORY.md)
242
283
  */
243
- async function getMemoryLogs(from, lines) {
244
- const memoryDir = path.join(os.homedir(), '.mcfast', 'memory');
245
- const logsDir = path.join(memoryDir, 'logs');
246
-
284
+ async function getCuratedMemory() {
247
285
  try {
248
- const entries = await fs.readdir(logsDir);
249
- const logFiles = entries
250
- .filter(f => f.endsWith('.md'))
251
- .sort()
252
- .reverse();
253
-
254
- if (logFiles.length === 0) {
286
+ const engine = await getMemoryEngine();
287
+ const content = await engine.getCuratedMemory();
288
+
289
+ if (!content) {
255
290
  return {
256
291
  content: [{
257
292
  type: "text",
258
- text: "📝 No memory logs found"
293
+ text: "📚 MEMORY.md\n\nNo curated memory found. This file stores long-term knowledge."
259
294
  }],
260
295
  metadata: { empty: true }
261
296
  };
262
297
  }
263
298
 
264
- // Read latest logs
265
- let output = `📝 Memory Logs\n`;
266
- output += `Found: ${logFiles.length} log file(s)\n`;
299
+ let output = `📚 Curated Memory (MEMORY.md)\n`;
267
300
  output += '─'.repeat(40) + '\n\n';
268
-
269
- const filesToRead = logFiles.slice(0, 5);
270
- for (const file of filesToRead) {
271
- const filePath = path.join(logsDir, file);
272
- const content = await fs.readFile(filePath, 'utf8');
273
- const fileLines = content.split('\n').slice(0, 20);
274
-
275
- output += `📅 ${file.replace('.md', '')}\n`;
276
- output += fileLines.join('\n');
277
- output += '\n\n---\n\n';
278
- }
301
+ output += content;
279
302
 
280
303
  return {
281
304
  content: [{
282
305
  type: "text",
283
306
  text: output
284
- }]
307
+ }],
308
+ metadata: { path: 'MEMORY.md' }
285
309
  };
286
- } catch (e) {
310
+ } catch (error) {
287
311
  return {
288
312
  content: [{
289
313
  type: "text",
290
- text: "📝 No memory logs found"
314
+ text: `❌ Error getting curated memory: ${error.message}`
291
315
  }],
292
- metadata: { empty: true }
316
+ isError: true
293
317
  };
294
318
  }
295
319
  }
@@ -301,25 +325,40 @@ async function getMemoryStats() {
301
325
  try {
302
326
  const engine = await getMemoryEngine();
303
327
  const stats = engine.getStats();
304
- const syncStatus = engine.getSyncStatus();
305
-
306
- let output = `📊 Memory Statistics\n`;
307
- output += '─'.repeat(40) + '\n';
308
- output += `Indexed Files: ${stats.files || 0}\n`;
309
- output += `Facts Stored: ${stats.facts || 0}\n`;
310
- output += `Code Chunks: ${stats.chunks || 0}\n`;
311
- output += `Embeddings: ${stats.embeddings || 0}\n`;
312
- output += `Edit History: ${stats.edits || 0}\n\n`;
313
328
 
314
- output += `Sync Engine: ${syncStatus.enabled ? '✅ Enabled' : '❌ Disabled'}\n`;
315
- if (syncStatus.enabled) {
316
- output += `Sync Status: ${syncStatus.status || 'unknown'}\n`;
317
- output += `Last Sync: ${syncStatus.lastSync ? new Date(syncStatus.lastSync).toISOString() : 'Never'}\n`;
318
- output += `Pending Changes: ${syncStatus.pendingChanges || 0}\n`;
329
+ let output = `📊 Memory System Statistics\n`;
330
+ output += '─'.repeat(40) + '\n\n';
331
+
332
+ // Memory stats
333
+ output += `📝 Memory Index\n`;
334
+ output += ` Files: ${stats.memory?.files || 0}\n`;
335
+ output += ` Chunks: ${stats.memory?.chunks || 0}\n`;
336
+ output += ` Embeddings: ${stats.memory?.embeddings || 0}\n\n`;
337
+
338
+ // Codebase stats
339
+ output += `💻 Codebase Index\n`;
340
+ output += ` Files: ${stats.codebase?.files || 0}\n`;
341
+ output += ` Facts: ${stats.codebase?.facts || 0}\n`;
342
+ output += ` Chunks: ${stats.codebase?.chunks || 0}\n`;
343
+ output += ` Embeddings: ${stats.codebase?.embeddings || 0}\n`;
344
+ output += ` Edits: ${stats.codebase?.edits || 0}\n\n`;
345
+
346
+ // Indexing status
347
+ if (stats.indexing?.is_complete !== undefined) {
348
+ output += `🔄 Indexing\n`;
349
+ output += ` Complete: ${stats.indexing.is_complete ? '✅' : '⏳'}\n`;
350
+ output += ` Indexed: ${stats.indexing.indexed_files || 0}/${stats.indexing.total_files || 0}\n`;
351
+ output += ` Failed: ${stats.indexing.failed_files || 0}\n\n`;
319
352
  }
320
353
 
321
- output += `\nProject: ${stats.projectPath || 'N/A'}\n`;
322
- output += `Smart Routing: ${engine.smartRoutingEnabled ? '✅ Enabled' : '❌ Disabled'}\n`;
354
+ // Sync status
355
+ const syncStatus = engine.getSyncStatus();
356
+ output += `☁️ Sync\n`;
357
+ output += ` Enabled: ${syncStatus.enabled ? '✅' : '❌'}\n`;
358
+ if (syncStatus.enabled) {
359
+ output += ` Status: ${syncStatus.status || 'unknown'}\n`;
360
+ output += ` Pending: ${syncStatus.pendingChanges || 0}\n`;
361
+ }
323
362
 
324
363
  return {
325
364
  content: [{