@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
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrxkun/mcfast-mcp",
|
|
3
|
-
"version": "4.0
|
|
4
|
-
"description": "Ultra-fast code editing with WASM acceleration, fuzzy patching, multi-layer caching, and 8 unified tools. Optimized for AI code assistants with 80-98% latency reduction. Phase
|
|
3
|
+
"version": "4.1.0",
|
|
4
|
+
"description": "Ultra-fast code editing with WASM acceleration, fuzzy patching, multi-layer caching, and 8 unified tools. Optimized for AI code assistants with 80-98% latency reduction. Phase 5: Memory v4.1.0 - Markdown source, Hybrid Search, Two-tier memory.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"mcfast-mcp": "src/index.js"
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AGENTS.md Bootstrap
|
|
3
|
+
* Tạo file AGENTS.md hướng dẫn agent cách sử dụng memory
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import fs from 'fs/promises';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
|
|
9
|
+
export class AgentsMdBootstrap {
|
|
10
|
+
constructor(options = {}) {
|
|
11
|
+
this.projectPath = options.projectPath || process.cwd();
|
|
12
|
+
this.agentsFile = path.join(this.projectPath, '.mcfast', 'AGENTS.md');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Check if AGENTS.md exists
|
|
17
|
+
*/
|
|
18
|
+
async exists() {
|
|
19
|
+
try {
|
|
20
|
+
await fs.access(this.agentsFile);
|
|
21
|
+
return true;
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Create AGENTS.md if it doesn't exist
|
|
29
|
+
*/
|
|
30
|
+
async bootstrap() {
|
|
31
|
+
if (await this.exists()) {
|
|
32
|
+
console.log(`[AgentsMdBootstrap] AGENTS.md already exists`);
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
await fs.mkdir(path.dirname(this.agentsFile), { recursive: true });
|
|
37
|
+
await fs.writeFile(this.agentsFile, this.getContent());
|
|
38
|
+
|
|
39
|
+
console.log(`[AgentsMdBootstrap] Created ${this.agentsFile}`);
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Force regenerate AGENTS.md
|
|
45
|
+
*/
|
|
46
|
+
async regenerate() {
|
|
47
|
+
await fs.mkdir(path.dirname(this.agentsFile), { recursive: true });
|
|
48
|
+
await fs.writeFile(this.agentsFile, this.getContent());
|
|
49
|
+
|
|
50
|
+
console.log(`[AgentsMdBootstrap] Regenerated ${this.agentsFile}`);
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Get AGENTS.md content
|
|
56
|
+
*/
|
|
57
|
+
getContent() {
|
|
58
|
+
return `# Agent Instructions
|
|
59
|
+
|
|
60
|
+
## 🎯 Every Session - MANDATORY
|
|
61
|
+
|
|
62
|
+
Before doing anything else, you MUST:
|
|
63
|
+
|
|
64
|
+
1. **Read Recent Context** (ALWAYS)
|
|
65
|
+
- Read memory/YYYY-MM-DD.md for TODAY
|
|
66
|
+
- Read memory/YYYY-MM-DD.md for YESTERDAY
|
|
67
|
+
- This gives you recent context and running notes
|
|
68
|
+
|
|
69
|
+
2. **Read Long-term Memory** (If in main session)
|
|
70
|
+
- Read MEMORY.md for curated knowledge
|
|
71
|
+
- This contains decisions, preferences, contacts
|
|
72
|
+
|
|
73
|
+
3. **Search Memory** (When relevant)
|
|
74
|
+
- Use memory_search tool when user asks about:
|
|
75
|
+
- Previous work or decisions
|
|
76
|
+
- Dates, people, preferences
|
|
77
|
+
- Todos or plans
|
|
78
|
+
- Any prior context
|
|
79
|
+
|
|
80
|
+
## 📝 Writing to Memory
|
|
81
|
+
|
|
82
|
+
### Daily Logs (memory/YYYY-MM-DD.md)
|
|
83
|
+
Write here for:
|
|
84
|
+
- Day-to-day activities
|
|
85
|
+
- Temporary notes
|
|
86
|
+
- Running context
|
|
87
|
+
- Session summaries
|
|
88
|
+
|
|
89
|
+
Example:
|
|
90
|
+
\`\`\`markdown
|
|
91
|
+
## 2:30 PM - API Discussion
|
|
92
|
+
Discussed REST vs GraphQL with user.
|
|
93
|
+
Decision: Use REST for simplicity.
|
|
94
|
+
\`\`\`
|
|
95
|
+
|
|
96
|
+
### Curated Memory (MEMORY.md)
|
|
97
|
+
Write here for:
|
|
98
|
+
- Important decisions
|
|
99
|
+
- User preferences
|
|
100
|
+
- Key contacts
|
|
101
|
+
- Project context
|
|
102
|
+
- Lessons learned
|
|
103
|
+
|
|
104
|
+
Example:
|
|
105
|
+
\`\`\`markdown
|
|
106
|
+
## Important Decisions
|
|
107
|
+
- **2026-01-20:** Chose PostgreSQL for database (scales better)
|
|
108
|
+
|
|
109
|
+
## User Preferences
|
|
110
|
+
- Prefers TypeScript over JavaScript
|
|
111
|
+
- Likes concise explanations
|
|
112
|
+
\`\`\`
|
|
113
|
+
|
|
114
|
+
## 🧠 Memory Search Strategy
|
|
115
|
+
|
|
116
|
+
When user asks about past context:
|
|
117
|
+
|
|
118
|
+
1. **ALWAYS search first**: Use memory_search tool
|
|
119
|
+
2. **Read relevant files**: Use memory_get to read full content
|
|
120
|
+
3. **Reference in response**: Cite the source file
|
|
121
|
+
|
|
122
|
+
Example workflow:
|
|
123
|
+
\`\`\`
|
|
124
|
+
User: "What did we decide about the API?"
|
|
125
|
+
|
|
126
|
+
Agent:
|
|
127
|
+
1. memory_search({ query: "API decision REST GraphQL" })
|
|
128
|
+
2. memory_get({ path: "MEMORY.md", from: 1, lines: 50 })
|
|
129
|
+
3. Answer with citation: "According to MEMORY.md (line 15), we decided..."
|
|
130
|
+
\`\`\`
|
|
131
|
+
|
|
132
|
+
## ⚠️ Important Rules
|
|
133
|
+
|
|
134
|
+
1. **Don't ask permission** - Just read memory files automatically
|
|
135
|
+
2. **Don't keep it in RAM** - Write important info to disk
|
|
136
|
+
3. **Search before answering** - About past work, always search first
|
|
137
|
+
4. **Cite your sources** - Tell user which memory file you referenced
|
|
138
|
+
5. **Write proactively** - When you learn something important, write it down
|
|
139
|
+
|
|
140
|
+
## 🔄 Memory Maintenance
|
|
141
|
+
|
|
142
|
+
- Daily logs are append-only
|
|
143
|
+
- Curated memory is for long-term storage
|
|
144
|
+
- Promote important items from daily logs to MEMORY.md
|
|
145
|
+
- Memory is searchable - trust the search system
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
*This file guides the agent on how to use the memory system effectively.*
|
|
150
|
+
*Generated by mcfast v4.1.0*
|
|
151
|
+
`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Read AGENTS.md content
|
|
156
|
+
*/
|
|
157
|
+
async read() {
|
|
158
|
+
try {
|
|
159
|
+
return await fs.readFile(this.agentsFile, 'utf-8');
|
|
160
|
+
} catch (error) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Get file path
|
|
167
|
+
*/
|
|
168
|
+
getPath() {
|
|
169
|
+
return this.agentsFile;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export default AgentsMdBootstrap;
|
package/src/memory/index.js
CHANGED
|
@@ -1,17 +1,30 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Memory System v4.1.0
|
|
3
|
+
* Export all memory components
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Main engine
|
|
7
|
+
export { MemoryEngine } from './memory-engine.js';
|
|
8
|
+
|
|
9
|
+
// Databases
|
|
10
|
+
export { MemoryDatabase } from './stores/memory-database.js';
|
|
11
|
+
export { CodebaseDatabase } from './stores/codebase-database.js';
|
|
12
|
+
|
|
13
|
+
// Watchers
|
|
2
14
|
export { FileWatcher } from './watchers/file-watcher.js';
|
|
15
|
+
|
|
16
|
+
// Utils
|
|
17
|
+
export { MarkdownChunker } from './utils/markdown-chunker.js';
|
|
18
|
+
export { CodeIndexer } from './utils/indexer.js';
|
|
3
19
|
export { Chunker } from './utils/chunker.js';
|
|
4
20
|
export { Embedder } from './utils/embedder.js';
|
|
5
|
-
export { SimpleEmbedder } from './utils/simple-embedder.js';
|
|
6
|
-
export { EnhancedSimpleEmbedder } from './utils/enhanced-embedder.js';
|
|
7
|
-
export { UltraEnhancedEmbedder } from './utils/ultra-embedder.js';
|
|
8
|
-
export { SmartRouter } from './utils/smart-router.js';
|
|
9
|
-
export { DashboardClient } from './utils/dashboard-client.js';
|
|
10
|
-
export { CodeIndexer } from './utils/indexer.js';
|
|
11
|
-
export { DailyLogs } from './utils/daily-logs.js';
|
|
12
|
-
export { SyncEngine } from './utils/sync-engine.js';
|
|
13
|
-
export { MemoryEngine } from './memory-engine.js';
|
|
14
|
-
export { MemoryEngine as default } from './memory-engine.js';
|
|
15
21
|
|
|
16
|
-
//
|
|
17
|
-
export {
|
|
22
|
+
// Two-tier memory layers
|
|
23
|
+
export { DailyLogs } from './layers/daily-logs.js';
|
|
24
|
+
export { CuratedMemory } from './layers/curated-memory.js';
|
|
25
|
+
|
|
26
|
+
// Bootstrap
|
|
27
|
+
export { AgentsMdBootstrap } from './bootstrap/agents-md.js';
|
|
28
|
+
|
|
29
|
+
// Legacy exports for backward compatibility
|
|
30
|
+
export { MemoryEngine as default } from './memory-engine.js';
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Curated Memory Layer
|
|
3
|
+
* Layer 2: Long-term curated knowledge
|
|
4
|
+
* File: MEMORY.md
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import fs from 'fs/promises';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
|
|
10
|
+
export class CuratedMemory {
|
|
11
|
+
constructor(options = {}) {
|
|
12
|
+
this.memoryPath = options.memoryPath || '.mcfast';
|
|
13
|
+
this.memoryFile = path.join(this.memoryPath, 'MEMORY.md');
|
|
14
|
+
this.sections = new Map();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Ensure memory file exists
|
|
19
|
+
*/
|
|
20
|
+
async ensureFile() {
|
|
21
|
+
try {
|
|
22
|
+
await fs.access(this.memoryFile);
|
|
23
|
+
} catch {
|
|
24
|
+
// Create default MEMORY.md
|
|
25
|
+
const defaultContent = this.getDefaultContent();
|
|
26
|
+
await fs.mkdir(path.dirname(this.memoryFile), { recursive: true });
|
|
27
|
+
await fs.writeFile(this.memoryFile, defaultContent);
|
|
28
|
+
console.log(`[CuratedMemory] Created ${this.memoryFile}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Get default content for new MEMORY.md
|
|
34
|
+
*/
|
|
35
|
+
getDefaultContent() {
|
|
36
|
+
return `# Long-term Memory
|
|
37
|
+
|
|
38
|
+
This file contains curated, persistent knowledge.
|
|
39
|
+
|
|
40
|
+
## User Preferences
|
|
41
|
+
|
|
42
|
+
<!-- Add user preferences here -->
|
|
43
|
+
|
|
44
|
+
## Important Decisions
|
|
45
|
+
|
|
46
|
+
<!-- Add important decisions here -->
|
|
47
|
+
|
|
48
|
+
## Key Contacts
|
|
49
|
+
|
|
50
|
+
<!-- Add key contacts here -->
|
|
51
|
+
|
|
52
|
+
## Project Context
|
|
53
|
+
|
|
54
|
+
<!-- Add project context here -->
|
|
55
|
+
|
|
56
|
+
## Lessons Learned
|
|
57
|
+
|
|
58
|
+
<!-- Add lessons learned here -->
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
*Last updated: ${new Date().toISOString()}*
|
|
62
|
+
`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Read entire memory file
|
|
67
|
+
*/
|
|
68
|
+
async read() {
|
|
69
|
+
await this.ensureFile();
|
|
70
|
+
return await fs.readFile(this.memoryFile, 'utf-8');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Read specific section
|
|
75
|
+
*/
|
|
76
|
+
async readSection(sectionName) {
|
|
77
|
+
const content = await this.read();
|
|
78
|
+
const sections = this.parseSections(content);
|
|
79
|
+
return sections.get(sectionName) || null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Add or update a section
|
|
84
|
+
*/
|
|
85
|
+
async updateSection(sectionName, content, append = false) {
|
|
86
|
+
await this.ensureFile();
|
|
87
|
+
|
|
88
|
+
let fileContent = await this.read();
|
|
89
|
+
const sections = this.parseSections(fileContent);
|
|
90
|
+
|
|
91
|
+
const timestamp = new Date().toISOString();
|
|
92
|
+
const sectionHeader = `## ${sectionName}`;
|
|
93
|
+
|
|
94
|
+
if (sections.has(sectionName)) {
|
|
95
|
+
// Update existing section
|
|
96
|
+
const existingContent = sections.get(sectionName);
|
|
97
|
+
const newContent = append
|
|
98
|
+
? `${existingContent}\n\n${content}\n\n*Updated: ${timestamp}*`
|
|
99
|
+
: `${content}\n\n*Updated: ${timestamp}*`;
|
|
100
|
+
|
|
101
|
+
fileContent = fileContent.replace(
|
|
102
|
+
new RegExp(`${sectionHeader}\\n[\\s\\S]*?(?=\\n## |\\n---|$)`),
|
|
103
|
+
`${sectionHeader}\n\n${newContent}\n`
|
|
104
|
+
);
|
|
105
|
+
} else {
|
|
106
|
+
// Add new section before the footer
|
|
107
|
+
const newSection = `${sectionHeader}\n\n${content}\n\n*Added: ${timestamp}*\n\n`;
|
|
108
|
+
fileContent = fileContent.replace(
|
|
109
|
+
/(---\n\*Last updated:.*\*)/,
|
|
110
|
+
`${newSection}$1`
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Update timestamp
|
|
115
|
+
fileContent = fileContent.replace(
|
|
116
|
+
/\*Last updated:.*\*/,
|
|
117
|
+
`*Last updated: ${timestamp}*`
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
await fs.writeFile(this.memoryFile, fileContent);
|
|
121
|
+
console.log(`[CuratedMemory] Updated section: ${sectionName}`);
|
|
122
|
+
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Parse sections from content
|
|
128
|
+
*/
|
|
129
|
+
parseSections(content) {
|
|
130
|
+
const sections = new Map();
|
|
131
|
+
const lines = content.split('\n');
|
|
132
|
+
let currentSection = null;
|
|
133
|
+
let currentContent = [];
|
|
134
|
+
|
|
135
|
+
for (const line of lines) {
|
|
136
|
+
const match = line.match(/^##\s+(.+)$/);
|
|
137
|
+
|
|
138
|
+
if (match) {
|
|
139
|
+
// Save previous section
|
|
140
|
+
if (currentSection) {
|
|
141
|
+
sections.set(currentSection, currentContent.join('\n').trim());
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Start new section
|
|
145
|
+
currentSection = match[1];
|
|
146
|
+
currentContent = [];
|
|
147
|
+
} else if (currentSection) {
|
|
148
|
+
currentContent.push(line);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Don't forget the last section
|
|
153
|
+
if (currentSection) {
|
|
154
|
+
sections.set(currentSection, currentContent.join('\n').trim());
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return sections;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Add user preference
|
|
162
|
+
*/
|
|
163
|
+
async addUserPreference(key, value) {
|
|
164
|
+
const content = `- **${key}:** ${value}`;
|
|
165
|
+
return this.updateSection('User Preferences', content, true);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Add important decision
|
|
170
|
+
*/
|
|
171
|
+
async addDecision(decision, context = '') {
|
|
172
|
+
const timestamp = new Date().toISOString().split('T')[0];
|
|
173
|
+
let content = `- **${timestamp}:** ${decision}`;
|
|
174
|
+
if (context) {
|
|
175
|
+
content += `\n - Context: ${context}`;
|
|
176
|
+
}
|
|
177
|
+
return this.updateSection('Important Decisions', content, true);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Add contact
|
|
182
|
+
*/
|
|
183
|
+
async addContact(name, info, role = '') {
|
|
184
|
+
let content = `- **${name}**`;
|
|
185
|
+
if (role) content += ` (${role})`;
|
|
186
|
+
content += `: ${info}`;
|
|
187
|
+
return this.updateSection('Key Contacts', content, true);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Add project context
|
|
192
|
+
*/
|
|
193
|
+
async addProjectContext(key, value) {
|
|
194
|
+
const content = `- **${key}:** ${value}`;
|
|
195
|
+
return this.updateSection('Project Context', content, true);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Add lesson learned
|
|
200
|
+
*/
|
|
201
|
+
async addLesson(lesson, context = '') {
|
|
202
|
+
const timestamp = new Date().toISOString().split('T')[0];
|
|
203
|
+
let content = `- **${timestamp}:** ${lesson}`;
|
|
204
|
+
if (context) {
|
|
205
|
+
content += `\n - Context: ${context}`;
|
|
206
|
+
}
|
|
207
|
+
return this.updateSection('Lessons Learned', content, true);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Add curated memory entry
|
|
212
|
+
* Smart routing to appropriate section
|
|
213
|
+
*/
|
|
214
|
+
async addEntry(type, title, content) {
|
|
215
|
+
switch (type.toLowerCase()) {
|
|
216
|
+
case 'preference':
|
|
217
|
+
case 'pref':
|
|
218
|
+
return this.addUserPreference(title, content);
|
|
219
|
+
|
|
220
|
+
case 'decision':
|
|
221
|
+
return this.addDecision(title, content);
|
|
222
|
+
|
|
223
|
+
case 'contact':
|
|
224
|
+
return this.addContact(title, content);
|
|
225
|
+
|
|
226
|
+
case 'project':
|
|
227
|
+
case 'context':
|
|
228
|
+
return this.addProjectContext(title, content);
|
|
229
|
+
|
|
230
|
+
case 'lesson':
|
|
231
|
+
case 'learned':
|
|
232
|
+
return this.addLesson(title, content);
|
|
233
|
+
|
|
234
|
+
default:
|
|
235
|
+
// Add to custom section
|
|
236
|
+
return this.updateSection(title, content);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Search in memory file
|
|
242
|
+
* Simple text search (not using FTS5 here - that's handled by MemoryDatabase)
|
|
243
|
+
*/
|
|
244
|
+
async search(query) {
|
|
245
|
+
const content = await this.read();
|
|
246
|
+
const lines = content.split('\n');
|
|
247
|
+
const matches = [];
|
|
248
|
+
|
|
249
|
+
const lowerQuery = query.toLowerCase();
|
|
250
|
+
|
|
251
|
+
lines.forEach((line, index) => {
|
|
252
|
+
if (line.toLowerCase().includes(lowerQuery)) {
|
|
253
|
+
matches.push({
|
|
254
|
+
line: index + 1,
|
|
255
|
+
content: line,
|
|
256
|
+
context: this.getContext(lines, index)
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
return matches;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Get context around a line
|
|
266
|
+
*/
|
|
267
|
+
getContext(lines, index, contextSize = 2) {
|
|
268
|
+
const start = Math.max(0, index - contextSize);
|
|
269
|
+
const end = Math.min(lines.length, index + contextSize + 1);
|
|
270
|
+
return lines.slice(start, end).join('\n');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Get all sections
|
|
275
|
+
*/
|
|
276
|
+
async getSections() {
|
|
277
|
+
const content = await this.read();
|
|
278
|
+
const sections = this.parseSections(content);
|
|
279
|
+
return Array.from(sections.keys());
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Check if file exists
|
|
284
|
+
*/
|
|
285
|
+
async exists() {
|
|
286
|
+
try {
|
|
287
|
+
await fs.access(this.memoryFile);
|
|
288
|
+
return true;
|
|
289
|
+
} catch {
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Get file stats
|
|
296
|
+
*/
|
|
297
|
+
async getStats() {
|
|
298
|
+
await this.ensureFile();
|
|
299
|
+
const stats = await fs.stat(this.memoryFile);
|
|
300
|
+
const content = await this.read();
|
|
301
|
+
const sections = this.parseSections(content);
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
path: this.memoryFile,
|
|
305
|
+
size: stats.size,
|
|
306
|
+
lastModified: stats.mtime,
|
|
307
|
+
sections: sections.size,
|
|
308
|
+
sectionNames: Array.from(sections.keys())
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Promote entry from daily log to curated memory
|
|
314
|
+
*/
|
|
315
|
+
async promoteFromDailyLog(date, entryTitle, sectionName) {
|
|
316
|
+
// This would require reading from daily log file
|
|
317
|
+
// Implementation depends on DailyLogs integration
|
|
318
|
+
console.log(`[CuratedMemory] Promote: ${entryTitle} from ${date} to ${sectionName}`);
|
|
319
|
+
// TODO: Implement promotion logic
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export default CuratedMemory;
|