@s_s/mnemo 1.0.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/README.md +193 -0
- package/build/core/config.d.ts +54 -0
- package/build/core/config.js +61 -0
- package/build/core/config.js.map +1 -0
- package/build/core/embedding.d.ts +37 -0
- package/build/core/embedding.js +119 -0
- package/build/core/embedding.js.map +1 -0
- package/build/core/notes.d.ts +36 -0
- package/build/core/notes.js +187 -0
- package/build/core/notes.js.map +1 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +31 -0
- package/build/index.js.map +1 -0
- package/build/prompts/templates.d.ts +21 -0
- package/build/prompts/templates.js +102 -0
- package/build/prompts/templates.js.map +1 -0
- package/build/tools/compress.d.ts +5 -0
- package/build/tools/compress.js +197 -0
- package/build/tools/compress.js.map +1 -0
- package/build/tools/save.d.ts +5 -0
- package/build/tools/save.js +67 -0
- package/build/tools/save.js.map +1 -0
- package/build/tools/search.d.ts +5 -0
- package/build/tools/search.js +107 -0
- package/build/tools/search.js.map +1 -0
- package/build/tools/setup.d.ts +5 -0
- package/build/tools/setup.js +120 -0
- package/build/tools/setup.js.map +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import crypto from 'node:crypto';
|
|
4
|
+
import { getNotesDir, ensureDir } from './config.js';
|
|
5
|
+
/**
|
|
6
|
+
* Generate a timestamp-based unique ID.
|
|
7
|
+
* Format: YYYYMMDD-HHmmss-xxxx (4-char random suffix to avoid same-second collisions)
|
|
8
|
+
*/
|
|
9
|
+
function generateId() {
|
|
10
|
+
const d = new Date();
|
|
11
|
+
const ts = d.getFullYear().toString() +
|
|
12
|
+
String(d.getMonth() + 1).padStart(2, '0') +
|
|
13
|
+
String(d.getDate()).padStart(2, '0') +
|
|
14
|
+
'-' +
|
|
15
|
+
String(d.getHours()).padStart(2, '0') +
|
|
16
|
+
String(d.getMinutes()).padStart(2, '0') +
|
|
17
|
+
String(d.getSeconds()).padStart(2, '0');
|
|
18
|
+
const suffix = crypto.randomUUID().slice(0, 4);
|
|
19
|
+
return `${ts}-${suffix}`;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Get current ISO timestamp
|
|
23
|
+
*/
|
|
24
|
+
function now() {
|
|
25
|
+
return new Date().toISOString();
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Parse a note markdown file into Note object
|
|
29
|
+
*/
|
|
30
|
+
export function parseNote(raw) {
|
|
31
|
+
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
32
|
+
if (!match)
|
|
33
|
+
return null;
|
|
34
|
+
const frontmatter = match[1];
|
|
35
|
+
const content = match[2].trim();
|
|
36
|
+
const meta = {};
|
|
37
|
+
for (const line of frontmatter.split('\n')) {
|
|
38
|
+
const [key, ...rest] = line.split(': ');
|
|
39
|
+
const value = rest.join(': ').trim();
|
|
40
|
+
switch (key.trim()) {
|
|
41
|
+
case 'id':
|
|
42
|
+
meta.id = value;
|
|
43
|
+
break;
|
|
44
|
+
case 'created':
|
|
45
|
+
meta.created = value;
|
|
46
|
+
break;
|
|
47
|
+
case 'updated':
|
|
48
|
+
meta.updated = value;
|
|
49
|
+
break;
|
|
50
|
+
case 'source':
|
|
51
|
+
meta.source = value;
|
|
52
|
+
break;
|
|
53
|
+
case 'tags':
|
|
54
|
+
meta.tags = value
|
|
55
|
+
.replace(/^\[/, '')
|
|
56
|
+
.replace(/\]$/, '')
|
|
57
|
+
.split(',')
|
|
58
|
+
.map((t) => t.trim())
|
|
59
|
+
.filter(Boolean);
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (!meta.id)
|
|
64
|
+
return null;
|
|
65
|
+
return {
|
|
66
|
+
meta: {
|
|
67
|
+
id: meta.id,
|
|
68
|
+
created: meta.created || now(),
|
|
69
|
+
updated: meta.updated || now(),
|
|
70
|
+
tags: meta.tags || [],
|
|
71
|
+
source: meta.source || 'unknown',
|
|
72
|
+
},
|
|
73
|
+
content,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Serialize a Note to markdown string
|
|
78
|
+
*/
|
|
79
|
+
export function serializeNote(note) {
|
|
80
|
+
const lines = [
|
|
81
|
+
'---',
|
|
82
|
+
`id: ${note.meta.id}`,
|
|
83
|
+
`created: ${note.meta.created}`,
|
|
84
|
+
`updated: ${note.meta.updated}`,
|
|
85
|
+
`tags: [${note.meta.tags.join(', ')}]`,
|
|
86
|
+
`source: ${note.meta.source}`,
|
|
87
|
+
'---',
|
|
88
|
+
'',
|
|
89
|
+
note.content,
|
|
90
|
+
];
|
|
91
|
+
return lines.join('\n') + '\n';
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Save a new note to disk and return it
|
|
95
|
+
*/
|
|
96
|
+
export async function saveNote(content, tags = [], source = 'unknown') {
|
|
97
|
+
const notesDir = getNotesDir();
|
|
98
|
+
await ensureDir(notesDir);
|
|
99
|
+
const id = generateId();
|
|
100
|
+
const timestamp = now();
|
|
101
|
+
const note = {
|
|
102
|
+
meta: {
|
|
103
|
+
id,
|
|
104
|
+
created: timestamp,
|
|
105
|
+
updated: timestamp,
|
|
106
|
+
tags,
|
|
107
|
+
source,
|
|
108
|
+
},
|
|
109
|
+
content,
|
|
110
|
+
};
|
|
111
|
+
const filePath = path.join(notesDir, `${id}.md`);
|
|
112
|
+
await fs.writeFile(filePath, serializeNote(note), 'utf-8');
|
|
113
|
+
return note;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Read a single note by ID
|
|
117
|
+
*/
|
|
118
|
+
export async function readNote(id) {
|
|
119
|
+
const filePath = path.join(getNotesDir(), `${id}.md`);
|
|
120
|
+
try {
|
|
121
|
+
const raw = await fs.readFile(filePath, 'utf-8');
|
|
122
|
+
return parseNote(raw);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Read all notes from disk
|
|
130
|
+
*/
|
|
131
|
+
export async function readAllNotes() {
|
|
132
|
+
const notesDir = getNotesDir();
|
|
133
|
+
try {
|
|
134
|
+
await ensureDir(notesDir);
|
|
135
|
+
const files = await fs.readdir(notesDir);
|
|
136
|
+
const mdFiles = files.filter((f) => f.endsWith('.md'));
|
|
137
|
+
const notes = [];
|
|
138
|
+
for (const file of mdFiles) {
|
|
139
|
+
const raw = await fs.readFile(path.join(notesDir, file), 'utf-8');
|
|
140
|
+
const note = parseNote(raw);
|
|
141
|
+
if (note)
|
|
142
|
+
notes.push(note);
|
|
143
|
+
}
|
|
144
|
+
// Sort by created time, newest first
|
|
145
|
+
notes.sort((a, b) => new Date(b.meta.created).getTime() - new Date(a.meta.created).getTime());
|
|
146
|
+
return notes;
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Delete a note by ID
|
|
154
|
+
*/
|
|
155
|
+
export async function deleteNote(id) {
|
|
156
|
+
const filePath = path.join(getNotesDir(), `${id}.md`);
|
|
157
|
+
try {
|
|
158
|
+
await fs.unlink(filePath);
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Delete multiple notes by ID
|
|
167
|
+
*/
|
|
168
|
+
export async function deleteNotes(ids) {
|
|
169
|
+
let count = 0;
|
|
170
|
+
for (const id of ids) {
|
|
171
|
+
if (await deleteNote(id))
|
|
172
|
+
count++;
|
|
173
|
+
}
|
|
174
|
+
return count;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Get note stats (count and total size)
|
|
178
|
+
*/
|
|
179
|
+
export async function getNoteStats() {
|
|
180
|
+
const notes = await readAllNotes();
|
|
181
|
+
let totalSize = 0;
|
|
182
|
+
for (const note of notes) {
|
|
183
|
+
totalSize += note.content.length;
|
|
184
|
+
}
|
|
185
|
+
return { count: notes.length, totalSize };
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=notes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notes.js","sourceRoot":"","sources":["../../src/core/notes.ts"],"names":[],"mappings":"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"}
|
package/build/index.d.ts
ADDED
package/build/index.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { registerSetupTool } from './tools/setup.js';
|
|
5
|
+
import { registerSaveTool } from './tools/save.js';
|
|
6
|
+
import { registerSearchTool } from './tools/search.js';
|
|
7
|
+
import { registerCompressTool } from './tools/compress.js';
|
|
8
|
+
import { preloadEmbedding } from './core/embedding.js';
|
|
9
|
+
const server = new McpServer({
|
|
10
|
+
name: 'mnemo',
|
|
11
|
+
version: '0.1.0',
|
|
12
|
+
description: 'Memory management for AI coding assistants',
|
|
13
|
+
});
|
|
14
|
+
// Register all tools
|
|
15
|
+
registerSetupTool(server);
|
|
16
|
+
registerSaveTool(server);
|
|
17
|
+
registerSearchTool(server);
|
|
18
|
+
registerCompressTool(server);
|
|
19
|
+
async function main() {
|
|
20
|
+
// Start loading the embedding model in the background immediately
|
|
21
|
+
// so it's ready before the first tool call
|
|
22
|
+
preloadEmbedding();
|
|
23
|
+
const transport = new StdioServerTransport();
|
|
24
|
+
await server.connect(transport);
|
|
25
|
+
console.error('Mnemo MCP server running on stdio');
|
|
26
|
+
}
|
|
27
|
+
main().catch((error) => {
|
|
28
|
+
console.error('Fatal error:', error);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
});
|
|
31
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IACzB,IAAI,EAAE,OAAO;IACb,OAAO,EAAE,OAAO;IAChB,WAAW,EAAE,4CAA4C;CAC5D,CAAC,CAAC;AAEH,qBAAqB;AACrB,iBAAiB,CAAC,MAAM,CAAC,CAAC;AAC1B,gBAAgB,CAAC,MAAM,CAAC,CAAC;AACzB,kBAAkB,CAAC,MAAM,CAAC,CAAC;AAC3B,oBAAoB,CAAC,MAAM,CAAC,CAAC;AAE7B,KAAK,UAAU,IAAI;IACf,kEAAkE;IAClE,2CAA2C;IAC3C,gBAAgB,EAAE,CAAC;IAEnB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;AACvD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type AgentType } from '../core/config.js';
|
|
2
|
+
/**
|
|
3
|
+
* Get the prompt block wrapped with markers
|
|
4
|
+
*/
|
|
5
|
+
export declare function getPromptBlock(): string;
|
|
6
|
+
/**
|
|
7
|
+
* Check if content already has Mnemo prompt injected
|
|
8
|
+
*/
|
|
9
|
+
export declare function hasPromptInjected(content: string): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Inject or replace Mnemo prompt in content
|
|
12
|
+
*/
|
|
13
|
+
export declare function injectPrompt(existingContent: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Get config file info for an agent type
|
|
16
|
+
*/
|
|
17
|
+
export declare function getAgentConfig(agentType: AgentType): {
|
|
18
|
+
fileName: string;
|
|
19
|
+
globalPath: (home: string) => string;
|
|
20
|
+
projectPath: (cwd: string) => string;
|
|
21
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The memory management prompt to inject into agent configuration files.
|
|
3
|
+
* This instructs the LLM when and how to use Mnemo's memory tools.
|
|
4
|
+
*/
|
|
5
|
+
const MEMORY_PROMPT = `
|
|
6
|
+
## Mnemo - Memory Management
|
|
7
|
+
|
|
8
|
+
You have access to a persistent memory system (Mnemo). Use it to retain important information across conversations.
|
|
9
|
+
|
|
10
|
+
### When to save memory (memory_save):
|
|
11
|
+
- Key decisions or conclusions reached during discussion
|
|
12
|
+
- User preferences, habits, or requirements discovered
|
|
13
|
+
- Technical architecture or design choices
|
|
14
|
+
- Important context that would be useful in future conversations
|
|
15
|
+
- Task outcomes and lessons learned
|
|
16
|
+
- When context window is nearly full, save key information from the current conversation to preserve continuity
|
|
17
|
+
|
|
18
|
+
### When to search memory (memory_search):
|
|
19
|
+
- At the START of each conversation, search for relevant context based on the user's first message
|
|
20
|
+
- When the user references past discussions or decisions
|
|
21
|
+
- When you need background context for a task
|
|
22
|
+
- When the user asks "do you remember..." or similar
|
|
23
|
+
|
|
24
|
+
### When to compress memory (memory_compress):
|
|
25
|
+
- When you notice the conversation has generated many memory notes
|
|
26
|
+
- When explicitly asked to organize or clean up memories
|
|
27
|
+
- Periodically during long conversations
|
|
28
|
+
- Workflow: call memory_compress to get all notes → distill them into fewer, concise notes → call memory_compress_apply with the distilled notes and old IDs to atomically save new + delete old
|
|
29
|
+
|
|
30
|
+
### Guidelines:
|
|
31
|
+
- Save memories in concise, distilled form - capture the essence, not raw conversation
|
|
32
|
+
- Use descriptive tags to categorize memories
|
|
33
|
+
- Always include relevant project/topic context in the memory content
|
|
34
|
+
- Do not save trivial or temporary information
|
|
35
|
+
- When searching, use semantic queries that describe the information you need
|
|
36
|
+
`.trim();
|
|
37
|
+
/**
|
|
38
|
+
* Agent-specific configuration file paths
|
|
39
|
+
*/
|
|
40
|
+
const AGENT_CONFIG = {
|
|
41
|
+
opencode: {
|
|
42
|
+
fileName: 'AGENTS.md',
|
|
43
|
+
globalPath: (home) => `${home}/.config/opencode/AGENTS.md`,
|
|
44
|
+
projectPath: (cwd) => `${cwd}/AGENTS.md`,
|
|
45
|
+
},
|
|
46
|
+
'claude-code': {
|
|
47
|
+
fileName: 'CLAUDE.md',
|
|
48
|
+
globalPath: (home) => `${home}/.claude/CLAUDE.md`,
|
|
49
|
+
projectPath: (cwd) => `${cwd}/CLAUDE.md`,
|
|
50
|
+
},
|
|
51
|
+
openclaw: {
|
|
52
|
+
fileName: 'AGENTS.md',
|
|
53
|
+
globalPath: (home) => `${home}/.openclaw/workspace/AGENTS.md`,
|
|
54
|
+
projectPath: (cwd) => `${cwd}/AGENTS.md`,
|
|
55
|
+
},
|
|
56
|
+
codex: {
|
|
57
|
+
fileName: 'AGENTS.md',
|
|
58
|
+
globalPath: (home) => `${home}/.codex/AGENTS.md`,
|
|
59
|
+
projectPath: (cwd) => `${cwd}/AGENTS.md`,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Marker used to identify Mnemo's section in agent config files
|
|
64
|
+
*/
|
|
65
|
+
const MARKER_START = '<!-- mnemo:start -->';
|
|
66
|
+
const MARKER_END = '<!-- mnemo:end -->';
|
|
67
|
+
/**
|
|
68
|
+
* Get the prompt block wrapped with markers
|
|
69
|
+
*/
|
|
70
|
+
export function getPromptBlock() {
|
|
71
|
+
return `${MARKER_START}\n${MEMORY_PROMPT}\n${MARKER_END}`;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Check if content already has Mnemo prompt injected
|
|
75
|
+
*/
|
|
76
|
+
export function hasPromptInjected(content) {
|
|
77
|
+
return content.includes(MARKER_START);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Inject or replace Mnemo prompt in content
|
|
81
|
+
*/
|
|
82
|
+
export function injectPrompt(existingContent) {
|
|
83
|
+
const block = getPromptBlock();
|
|
84
|
+
if (hasPromptInjected(existingContent)) {
|
|
85
|
+
// Replace existing block
|
|
86
|
+
const regex = new RegExp(`${escapeRegex(MARKER_START)}[\\s\\S]*?${escapeRegex(MARKER_END)}`, 'g');
|
|
87
|
+
return existingContent.replace(regex, block);
|
|
88
|
+
}
|
|
89
|
+
// Append to end
|
|
90
|
+
const separator = existingContent.trim() ? '\n\n' : '';
|
|
91
|
+
return existingContent.trimEnd() + separator + block + '\n';
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Get config file info for an agent type
|
|
95
|
+
*/
|
|
96
|
+
export function getAgentConfig(agentType) {
|
|
97
|
+
return AGENT_CONFIG[agentType];
|
|
98
|
+
}
|
|
99
|
+
function escapeRegex(str) {
|
|
100
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=templates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/prompts/templates.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+BrB,CAAC,IAAI,EAAE,CAAC;AAET;;GAEG;AACH,MAAM,YAAY,GAOd;IACA,QAAQ,EAAE;QACN,QAAQ,EAAE,WAAW;QACrB,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,6BAA6B;QAC1D,WAAW,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,YAAY;KAC3C;IACD,aAAa,EAAE;QACX,QAAQ,EAAE,WAAW;QACrB,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,oBAAoB;QACjD,WAAW,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,YAAY;KAC3C;IACD,QAAQ,EAAE;QACN,QAAQ,EAAE,WAAW;QACrB,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,gCAAgC;QAC7D,WAAW,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,YAAY;KAC3C;IACD,KAAK,EAAE;QACH,QAAQ,EAAE,WAAW;QACrB,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,mBAAmB;QAChD,WAAW,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,YAAY;KAC3C;CACJ,CAAC;AAEF;;GAEG;AACH,MAAM,YAAY,GAAG,sBAAsB,CAAC;AAC5C,MAAM,UAAU,GAAG,oBAAoB,CAAC;AAExC;;GAEG;AACH,MAAM,UAAU,cAAc;IAC1B,OAAO,GAAG,YAAY,KAAK,aAAa,KAAK,UAAU,EAAE,CAAC;AAC9D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe;IAC7C,OAAO,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;AAC1C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,eAAuB;IAChD,MAAM,KAAK,GAAG,cAAc,EAAE,CAAC;IAE/B,IAAI,iBAAiB,CAAC,eAAe,CAAC,EAAE,CAAC;QACrC,yBAAyB;QACzB,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,GAAG,WAAW,CAAC,YAAY,CAAC,aAAa,WAAW,CAAC,UAAU,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QAClG,OAAO,eAAe,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACjD,CAAC;IAED,gBAAgB;IAChB,MAAM,SAAS,GAAG,eAAe,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACvD,OAAO,eAAe,CAAC,OAAO,EAAE,GAAG,SAAS,GAAG,KAAK,GAAG,IAAI,CAAC;AAChE,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,SAAoB;IAC/C,OAAO,YAAY,CAAC,SAAS,CAAC,CAAC;AACnC,CAAC;AAED,SAAS,WAAW,CAAC,GAAW;IAC5B,OAAO,GAAG,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACtD,CAAC"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { readAllNotes, saveNote, deleteNotes, getNoteStats } from '../core/notes.js';
|
|
3
|
+
import { indexNote, removeMultipleFromIndex } from '../core/embedding.js';
|
|
4
|
+
/**
|
|
5
|
+
* Register the memory_compress tool
|
|
6
|
+
*/
|
|
7
|
+
export function registerCompressTool(server) {
|
|
8
|
+
server.registerTool('memory_compress', {
|
|
9
|
+
title: 'Compress Memory',
|
|
10
|
+
description: 'Consolidate and compress existing memory notes. This tool reads all current memories, asks you to distill them into fewer, more concise notes, and replaces the originals. Use this when memory storage grows large, or to periodically organize and clean up memories. The tool returns all current memories for you to review and distill - respond with the compressed version using memory_save.',
|
|
11
|
+
inputSchema: {
|
|
12
|
+
strategy: z
|
|
13
|
+
.enum(['review', 'auto_tag'])
|
|
14
|
+
.default('review')
|
|
15
|
+
.optional()
|
|
16
|
+
.describe("'review' (default): Returns all notes for you to manually distill. 'auto_tag': Only re-organizes tags without changing content."),
|
|
17
|
+
older_than_days: z
|
|
18
|
+
.number()
|
|
19
|
+
.int()
|
|
20
|
+
.min(1)
|
|
21
|
+
.optional()
|
|
22
|
+
.describe('Only include notes older than this many days. If not specified, includes all notes.'),
|
|
23
|
+
},
|
|
24
|
+
}, async ({ strategy, older_than_days }) => {
|
|
25
|
+
try {
|
|
26
|
+
const allNotes = await readAllNotes();
|
|
27
|
+
if (allNotes.length === 0) {
|
|
28
|
+
return {
|
|
29
|
+
content: [
|
|
30
|
+
{
|
|
31
|
+
type: 'text',
|
|
32
|
+
text: 'No memories to compress.',
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
// Filter by age if specified
|
|
38
|
+
let targetNotes = allNotes;
|
|
39
|
+
if (older_than_days) {
|
|
40
|
+
const cutoff = new Date();
|
|
41
|
+
cutoff.setDate(cutoff.getDate() - older_than_days);
|
|
42
|
+
targetNotes = allNotes.filter((n) => new Date(n.meta.created) < cutoff);
|
|
43
|
+
}
|
|
44
|
+
if (targetNotes.length === 0) {
|
|
45
|
+
return {
|
|
46
|
+
content: [
|
|
47
|
+
{
|
|
48
|
+
type: 'text',
|
|
49
|
+
text: `No memories older than ${older_than_days} days to compress.`,
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const stats = await getNoteStats();
|
|
55
|
+
if (strategy === 'auto_tag') {
|
|
56
|
+
// Just return stats, no content modification
|
|
57
|
+
const tagMap = new Map();
|
|
58
|
+
for (const note of targetNotes) {
|
|
59
|
+
for (const tag of note.meta.tags) {
|
|
60
|
+
tagMap.set(tag, (tagMap.get(tag) || 0) + 1);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const tagSummary = Array.from(tagMap.entries())
|
|
64
|
+
.sort((a, b) => b[1] - a[1])
|
|
65
|
+
.map(([tag, count]) => ` - ${tag}: ${count} notes`)
|
|
66
|
+
.join('\n');
|
|
67
|
+
return {
|
|
68
|
+
content: [
|
|
69
|
+
{
|
|
70
|
+
type: 'text',
|
|
71
|
+
text: `Memory statistics:\n- Total notes: ${stats.count}\n- Total size: ${Math.round(stats.totalSize / 1000)}KB\n- Target notes for compression: ${targetNotes.length}\n\nTag distribution:\n${tagSummary}`,
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
// Strategy: review - return all notes for LLM to distill
|
|
77
|
+
const notesText = targetNotes
|
|
78
|
+
.map((n) => `[ID: ${n.meta.id}] [Tags: ${n.meta.tags.join(', ')}] [Created: ${n.meta.created}]\n${n.content}`)
|
|
79
|
+
.join('\n\n---\n\n');
|
|
80
|
+
const noteIds = targetNotes.map((n) => n.meta.id);
|
|
81
|
+
return {
|
|
82
|
+
content: [
|
|
83
|
+
{
|
|
84
|
+
type: 'text',
|
|
85
|
+
text: `Found ${targetNotes.length} memories to compress (${Math.round(stats.totalSize / 1000)}KB total).\n\nPlease review the following memories and distill them into fewer, more concise notes. After reviewing, use memory_compress_apply to submit the compressed versions — it will atomically save the new notes and delete the originals.\n\nOriginal note IDs to delete after compression: [${noteIds.join(', ')}]\n\n---\n\n${notesText}`,
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
return {
|
|
92
|
+
content: [
|
|
93
|
+
{
|
|
94
|
+
type: 'text',
|
|
95
|
+
text: `Failed to compress memories: ${error instanceof Error ? error.message : String(error)}`,
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
isError: true,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
// Register the delete tool
|
|
103
|
+
server.registerTool('memory_delete', {
|
|
104
|
+
title: 'Delete Memory',
|
|
105
|
+
description: 'Delete memory notes by their IDs. Primarily used after memory_compress to remove the original notes that have been consolidated into compressed versions.',
|
|
106
|
+
inputSchema: {
|
|
107
|
+
ids: z.array(z.string()).describe('Array of note IDs to delete'),
|
|
108
|
+
},
|
|
109
|
+
}, async ({ ids }) => {
|
|
110
|
+
try {
|
|
111
|
+
// Remove from vector index
|
|
112
|
+
await removeMultipleFromIndex(ids);
|
|
113
|
+
// Remove from disk
|
|
114
|
+
const deletedCount = await deleteNotes(ids);
|
|
115
|
+
return {
|
|
116
|
+
content: [
|
|
117
|
+
{
|
|
118
|
+
type: 'text',
|
|
119
|
+
text: `Deleted ${deletedCount} of ${ids.length} memory notes.`,
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
return {
|
|
126
|
+
content: [
|
|
127
|
+
{
|
|
128
|
+
type: 'text',
|
|
129
|
+
text: `Failed to delete memories: ${error instanceof Error ? error.message : String(error)}`,
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
isError: true,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
// Register the compress_apply tool (atomic save new + delete old)
|
|
137
|
+
server.registerTool('memory_compress_apply', {
|
|
138
|
+
title: 'Apply Compression',
|
|
139
|
+
description: 'Atomically apply memory compression results. Saves the distilled notes and deletes the originals in one operation. Use this after memory_compress returns notes for review — distill them and submit the results here.',
|
|
140
|
+
inputSchema: {
|
|
141
|
+
notes: z
|
|
142
|
+
.array(z.object({
|
|
143
|
+
content: z.string().describe('The distilled note content'),
|
|
144
|
+
tags: z.array(z.string()).optional().describe('Tags for this note'),
|
|
145
|
+
}))
|
|
146
|
+
.describe('Array of distilled notes to save'),
|
|
147
|
+
old_ids: z
|
|
148
|
+
.array(z.string())
|
|
149
|
+
.describe('IDs of the original notes to delete (from memory_compress output)'),
|
|
150
|
+
source: z.string().optional().describe("Source identifier, defaults to 'unknown'"),
|
|
151
|
+
},
|
|
152
|
+
}, async ({ notes, old_ids, source }) => {
|
|
153
|
+
try {
|
|
154
|
+
// Step 1: Save all new notes
|
|
155
|
+
const savedNotes = [];
|
|
156
|
+
for (const n of notes) {
|
|
157
|
+
const saved = await saveNote(n.content, n.tags || [], source || 'unknown');
|
|
158
|
+
savedNotes.push(saved);
|
|
159
|
+
}
|
|
160
|
+
// Step 2: Index new notes
|
|
161
|
+
const indexWarnings = [];
|
|
162
|
+
for (const saved of savedNotes) {
|
|
163
|
+
try {
|
|
164
|
+
await indexNote(saved);
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
168
|
+
indexWarnings.push(`${saved.meta.id}: ${reason}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// Step 3: Remove old notes from index
|
|
172
|
+
await removeMultipleFromIndex(old_ids);
|
|
173
|
+
// Step 4: Delete old notes from disk
|
|
174
|
+
const deletedCount = await deleteNotes(old_ids);
|
|
175
|
+
const newIds = savedNotes.map((n) => n.meta.id);
|
|
176
|
+
let result = `Compression applied successfully.\n\n- New notes saved: ${savedNotes.length} [${newIds.join(', ')}]\n- Old notes deleted: ${deletedCount} of ${old_ids.length}`;
|
|
177
|
+
if (indexWarnings.length > 0) {
|
|
178
|
+
result += `\n\nWarning: Some notes could not be indexed (will be available after embedding model loads):\n${indexWarnings.join('\n')}`;
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
content: [{ type: 'text', text: result }],
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
return {
|
|
186
|
+
content: [
|
|
187
|
+
{
|
|
188
|
+
type: 'text',
|
|
189
|
+
text: `Failed to apply compression: ${error instanceof Error ? error.message : String(error)}`,
|
|
190
|
+
},
|
|
191
|
+
],
|
|
192
|
+
isError: true,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=compress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compress.js","sourceRoot":"","sources":["../../src/tools/compress.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { saveNote, getNoteStats } from '../core/notes.js';
|
|
3
|
+
import { indexNote } from '../core/embedding.js';
|
|
4
|
+
import { COMPRESS_THRESHOLDS } from '../core/config.js';
|
|
5
|
+
/**
|
|
6
|
+
* Register the memory_save tool
|
|
7
|
+
*/
|
|
8
|
+
export function registerSaveTool(server) {
|
|
9
|
+
server.registerTool('memory_save', {
|
|
10
|
+
title: 'Save Memory',
|
|
11
|
+
description: 'Save a piece of important information as a persistent memory note. Use this to record key decisions, user preferences, technical choices, task outcomes, or any context that should be remembered across conversations. Content should be distilled and concise, not raw conversation.',
|
|
12
|
+
inputSchema: {
|
|
13
|
+
content: z
|
|
14
|
+
.string()
|
|
15
|
+
.describe('The memory content to save. Should be concise and distilled - capture the essence of what needs to be remembered.'),
|
|
16
|
+
tags: z
|
|
17
|
+
.array(z.string())
|
|
18
|
+
.optional()
|
|
19
|
+
.describe("Tags to categorize this memory. E.g., ['architecture', 'decision'], ['user-preference'], ['project-mnemo']"),
|
|
20
|
+
source: z
|
|
21
|
+
.string()
|
|
22
|
+
.optional()
|
|
23
|
+
.describe("Source identifier, e.g., 'opencode', 'claude-code'. Defaults to 'unknown'."),
|
|
24
|
+
},
|
|
25
|
+
}, async ({ content, tags, source }) => {
|
|
26
|
+
try {
|
|
27
|
+
// Save the note to disk first (fast, reliable)
|
|
28
|
+
const note = await saveNote(content, tags || [], source || 'unknown');
|
|
29
|
+
// Try to index for semantic search (may be slow on first call)
|
|
30
|
+
let indexWarning = '';
|
|
31
|
+
try {
|
|
32
|
+
await indexNote(note);
|
|
33
|
+
}
|
|
34
|
+
catch (indexError) {
|
|
35
|
+
const reason = indexError instanceof Error ? indexError.message : String(indexError);
|
|
36
|
+
indexWarning = `\n\nWarning: Memory saved to disk but semantic indexing failed (${reason}). The note will be available via memory_search after the embedding model finishes loading.`;
|
|
37
|
+
console.error('Mnemo: indexing failed for note', note.meta.id, reason);
|
|
38
|
+
}
|
|
39
|
+
// Check if compression might be needed (program-level guardrail)
|
|
40
|
+
const stats = await getNoteStats();
|
|
41
|
+
let compressHint = '';
|
|
42
|
+
if (stats.count > COMPRESS_THRESHOLDS.maxNotes || stats.totalSize > COMPRESS_THRESHOLDS.maxTotalSize) {
|
|
43
|
+
compressHint = `\n\nNote: Memory storage is growing large (${stats.count} notes, ${Math.round(stats.totalSize / 1000)}KB). Consider running memory_compress to consolidate and distill older memories.`;
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
content: [
|
|
47
|
+
{
|
|
48
|
+
type: 'text',
|
|
49
|
+
text: `Memory saved successfully.\n\nID: ${note.meta.id}\nTags: [${note.meta.tags.join(', ')}]${indexWarning}${compressHint}`,
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
return {
|
|
56
|
+
content: [
|
|
57
|
+
{
|
|
58
|
+
type: 'text',
|
|
59
|
+
text: `Failed to save memory: ${error instanceof Error ? error.message : String(error)}`,
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
isError: true,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=save.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"save.js","sourceRoot":"","sources":["../../src/tools/save.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAoB,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAiB;IAC9C,MAAM,CAAC,YAAY,CACf,aAAa,EACb;QACI,KAAK,EAAE,aAAa;QACpB,WAAW,EACP,wRAAwR;QAC5R,WAAW,EAAE;YACT,OAAO,EAAE,CAAC;iBACL,MAAM,EAAE;iBACR,QAAQ,CACL,mHAAmH,CACtH;YACL,IAAI,EAAE,CAAC;iBACF,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;iBACjB,QAAQ,EAAE;iBACV,QAAQ,CACL,4GAA4G,CAC/G;YACL,MAAM,EAAE,CAAC;iBACJ,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,4EAA4E,CAAC;SAC9F;KACJ,EACD,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE;QAChC,IAAI,CAAC;YACD,+CAA+C;YAC/C,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,IAAI,IAAI,EAAE,EAAE,MAAM,IAAI,SAAS,CAAC,CAAC;YAEtE,+DAA+D;YAC/D,IAAI,YAAY,GAAG,EAAE,CAAC;YACtB,IAAI,CAAC;gBACD,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC;YAC1B,CAAC;YAAC,OAAO,UAAU,EAAE,CAAC;gBAClB,MAAM,MAAM,GAAG,UAAU,YAAY,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;gBACrF,YAAY,GAAG,mEAAmE,MAAM,6FAA6F,CAAC;gBACtL,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;YAC3E,CAAC;YAED,iEAAiE;YACjE,MAAM,KAAK,GAAG,MAAM,YAAY,EAAE,CAAC;YACnC,IAAI,YAAY,GAAG,EAAE,CAAC;YAEtB,IAAI,KAAK,CAAC,KAAK,GAAG,mBAAmB,CAAC,QAAQ,IAAI,KAAK,CAAC,SAAS,GAAG,mBAAmB,CAAC,YAAY,EAAE,CAAC;gBACnG,YAAY,GAAG,8CAA8C,KAAK,CAAC,KAAK,WAAW,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,kFAAkF,CAAC;YAC5M,CAAC;YAED,OAAO;gBACH,OAAO,EAAE;oBACL;wBACI,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,qCAAqC,IAAI,CAAC,IAAI,CAAC,EAAE,YAAY,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,YAAY,GAAG,YAAY,EAAE;qBAChI;iBACJ;aACJ,CAAC;QACN,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO;gBACH,OAAO,EAAE;oBACL;wBACI,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,0BAA0B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;qBAC3F;iBACJ;gBACD,OAAO,EAAE,IAAI;aAChB,CAAC;QACN,CAAC;IACL,CAAC,CACJ,CAAC;AACN,CAAC"}
|