toon-memory 1.2.0 → 1.4.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 +78 -7
- package/dist/cli/setup.js +19 -0
- package/mcp/server.js +81 -2
- package/package.json +1 -1
- package/src/cli/setup.ts +22 -0
- package/src/mcp/server.ts +96 -1
package/README.md
CHANGED
|
@@ -22,11 +22,14 @@ AI agents forget everything between sessions. toon-memory fixes this by providin
|
|
|
22
22
|
|
|
23
23
|
## Features
|
|
24
24
|
|
|
25
|
-
- **
|
|
25
|
+
- **6 MCP tools** — `memory_remember`, `memory_recall`, `memory_forget`, `memory_stats`, `memory_summary`, `memory_archive`
|
|
26
26
|
- **7 agents supported** — OpenCode, VS Code/Copilot, Claude Code, Cursor, Windsurf, Cline, Continue
|
|
27
27
|
- **TOON format** — 40% fewer tokens than JSON, better LLM comprehension
|
|
28
28
|
- **Per-project memory** — each project gets its own memory file
|
|
29
29
|
- **Zero config** — just install and use
|
|
30
|
+
- **Auto gitignore** — automatically adds `.opencode/memory/` to `.gitignore`
|
|
31
|
+
- **Date filtering** — search memory by date range
|
|
32
|
+
- **Auto-archive** — old entries (>30 days) moved to archive automatically
|
|
30
33
|
|
|
31
34
|
---
|
|
32
35
|
|
|
@@ -87,6 +90,31 @@ memory_remember # Save important decisions
|
|
|
87
90
|
| `memory_forget` | Remove an entry by key or id |
|
|
88
91
|
| `memory_stats` | View memory state |
|
|
89
92
|
| `memory_summary` | Save/retrieve file summaries |
|
|
93
|
+
| `memory_archive` | Archive old entries (>30 days) |
|
|
94
|
+
|
|
95
|
+
### Date Filtering
|
|
96
|
+
|
|
97
|
+
Search memory by date range:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
// Search with date filter
|
|
101
|
+
memory_recall({
|
|
102
|
+
query: "redis",
|
|
103
|
+
from_date: "2026-07-01",
|
|
104
|
+
to_date: "2026-07-31"
|
|
105
|
+
})
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Auto-Archive
|
|
109
|
+
|
|
110
|
+
Entries older than 30 days are automatically archived to keep memory clean:
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
// Manually trigger archiving
|
|
114
|
+
memory_archive()
|
|
115
|
+
// 📦 Archivadas 5 entradas antiguas
|
|
116
|
+
// 📋 Quedan 42 entradas activas
|
|
117
|
+
```
|
|
90
118
|
|
|
91
119
|
---
|
|
92
120
|
|
|
@@ -114,12 +142,55 @@ summaries:
|
|
|
114
142
|
## CLI Commands
|
|
115
143
|
|
|
116
144
|
```bash
|
|
117
|
-
npx toon-memory
|
|
118
|
-
npx toon-memory init
|
|
119
|
-
npx toon-memory mcp
|
|
120
|
-
npx toon-memory status
|
|
121
|
-
npx toon-memory
|
|
122
|
-
npx toon-memory
|
|
145
|
+
npx toon-memory # Interactive installer
|
|
146
|
+
npx toon-memory init # Quick setup (no prompts)
|
|
147
|
+
npx toon-memory mcp # Run MCP server directly
|
|
148
|
+
npx toon-memory status # Check installation status
|
|
149
|
+
npx toon-memory stats # View memory statistics
|
|
150
|
+
npx toon-memory export # Export memory to JSON
|
|
151
|
+
npx toon-memory import <file> # Import memory from JSON
|
|
152
|
+
npx toon-memory upgrade # Update to latest version
|
|
153
|
+
npx toon-memory uninstall # Remove from all agents
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Stats
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
$ npx toon-memory stats
|
|
160
|
+
|
|
161
|
+
🧠 toon-memory stats
|
|
162
|
+
|
|
163
|
+
📊 Memory Stats
|
|
164
|
+
━━━━━━━━━━━━━━━━━━
|
|
165
|
+
Total entries: 45
|
|
166
|
+
├── decision: 12
|
|
167
|
+
├── pattern: 18
|
|
168
|
+
├── bug: 8
|
|
169
|
+
└── knowledge: 7
|
|
170
|
+
Last updated: 2026-07-10
|
|
171
|
+
File size: 12.4 KB
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Export
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
$ npx toon-memory export
|
|
178
|
+
|
|
179
|
+
🧠 toon-memory export
|
|
180
|
+
|
|
181
|
+
Exported 45 entries to:
|
|
182
|
+
/path/to/project/toon-memory-export.json
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Import
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
$ npx toon-memory import backup.json
|
|
189
|
+
|
|
190
|
+
🧠 toon-memory import
|
|
191
|
+
|
|
192
|
+
Imported 3 new entries
|
|
193
|
+
Skipped 2 duplicates
|
|
123
194
|
```
|
|
124
195
|
|
|
125
196
|
---
|
package/dist/cli/setup.js
CHANGED
|
@@ -77,6 +77,23 @@ function installOpenCodeTools() {
|
|
|
77
77
|
console.log(" Created .opencode/memory/data.toon");
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
+
function ensureGitignore() {
|
|
81
|
+
const gitignorePath = join(projectRoot, ".gitignore");
|
|
82
|
+
const entry = ".opencode/memory/";
|
|
83
|
+
if (!existsSync(gitignorePath)) {
|
|
84
|
+
writeFileSync(gitignorePath, `${entry}
|
|
85
|
+
`);
|
|
86
|
+
console.log(" Created .gitignore with memory exclusion");
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const content = readFileSync(gitignorePath, "utf-8");
|
|
90
|
+
if (!content.includes(entry)) {
|
|
91
|
+
writeFileSync(gitignorePath, `${content.trim()}
|
|
92
|
+
${entry}
|
|
93
|
+
`);
|
|
94
|
+
console.log(" Added .opencode/memory/ to .gitignore");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
80
97
|
function installMCPConfig(agent, scope) {
|
|
81
98
|
const configPath = scope === "global" ? agent.global : agent.local;
|
|
82
99
|
if (!configPath) {
|
|
@@ -139,6 +156,7 @@ function init(scope = "local") {
|
|
|
139
156
|
installMCPConfig(agent, scope);
|
|
140
157
|
console.log("");
|
|
141
158
|
}
|
|
159
|
+
ensureGitignore();
|
|
142
160
|
console.log("Done! Restart your agent to use memory tools.\n");
|
|
143
161
|
}
|
|
144
162
|
function status() {
|
|
@@ -365,6 +383,7 @@ Installing ${scope}ly...
|
|
|
365
383
|
installMCPConfig(agent, scope);
|
|
366
384
|
console.log("");
|
|
367
385
|
}
|
|
386
|
+
ensureGitignore();
|
|
368
387
|
console.log("Done! Restart your agent to use memory tools.");
|
|
369
388
|
console.log("Run 'npx toon-memory uninstall' to remove.\n");
|
|
370
389
|
rl.close();
|
package/mcp/server.js
CHANGED
|
@@ -27646,6 +27646,8 @@ import { randomBytes } from "crypto";
|
|
|
27646
27646
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
27647
27647
|
var MEMORY_DIR = join(process.cwd(), ".opencode", "memory");
|
|
27648
27648
|
var MEMORY_FILE = join(MEMORY_DIR, "data.toon");
|
|
27649
|
+
var ARCHIVE_FILE = join(MEMORY_DIR, "archive.toon");
|
|
27650
|
+
var ARCHIVE_DAYS = 30;
|
|
27649
27651
|
function ensureMemoryFile() {
|
|
27650
27652
|
if (!existsSync(MEMORY_DIR)) mkdirSync(MEMORY_DIR, { recursive: true });
|
|
27651
27653
|
if (!existsSync(MEMORY_FILE)) {
|
|
@@ -27663,6 +27665,58 @@ function writeMemory(content) {
|
|
|
27663
27665
|
function generateId() {
|
|
27664
27666
|
return randomBytes(4).toString("hex");
|
|
27665
27667
|
}
|
|
27668
|
+
function archiveOldEntries() {
|
|
27669
|
+
const data = readMemory();
|
|
27670
|
+
const lines = data.split("\n");
|
|
27671
|
+
const headerIdx = lines.findIndex((l) => l.startsWith("entries["));
|
|
27672
|
+
if (headerIdx === -1) return { archived: 0, kept: 0 };
|
|
27673
|
+
const today = /* @__PURE__ */ new Date();
|
|
27674
|
+
const cutoff = new Date(today);
|
|
27675
|
+
cutoff.setDate(cutoff.getDate() - ARCHIVE_DAYS);
|
|
27676
|
+
const cutoffStr = cutoff.toISOString().split("T")[0];
|
|
27677
|
+
const entryLines = lines.slice(headerIdx + 1).filter((l) => l.trim().length > 0);
|
|
27678
|
+
const toArchive = [];
|
|
27679
|
+
const toKeep = [];
|
|
27680
|
+
for (const line of entryLines) {
|
|
27681
|
+
const parts = line.trim().split("|");
|
|
27682
|
+
if (parts.length >= 7) {
|
|
27683
|
+
const date5 = parts[6];
|
|
27684
|
+
if (date5 < cutoffStr) {
|
|
27685
|
+
toArchive.push(line.trim());
|
|
27686
|
+
} else {
|
|
27687
|
+
toKeep.push(line.trim());
|
|
27688
|
+
}
|
|
27689
|
+
} else {
|
|
27690
|
+
toKeep.push(line.trim());
|
|
27691
|
+
}
|
|
27692
|
+
}
|
|
27693
|
+
if (toArchive.length === 0) return { archived: 0, kept: toKeep.length };
|
|
27694
|
+
let archiveContent = "";
|
|
27695
|
+
if (existsSync(ARCHIVE_FILE)) {
|
|
27696
|
+
archiveContent = readFileSync(ARCHIVE_FILE, "utf-8");
|
|
27697
|
+
} else {
|
|
27698
|
+
archiveContent = `version: 1
|
|
27699
|
+
archived:
|
|
27700
|
+
`;
|
|
27701
|
+
}
|
|
27702
|
+
const archiveLines = archiveContent.split("\n");
|
|
27703
|
+
let archiveHeaderIdx = archiveLines.findIndex((l) => l.startsWith("archived["));
|
|
27704
|
+
if (archiveHeaderIdx === -1) {
|
|
27705
|
+
archiveLines.push(`archived[0|]{id|category|key|content|file|tags|date}:`);
|
|
27706
|
+
archiveHeaderIdx = archiveLines.length - 1;
|
|
27707
|
+
}
|
|
27708
|
+
const archiveMatch = archiveLines[archiveHeaderIdx].match(/archived\[(\d+)\|/);
|
|
27709
|
+
const archiveCount = archiveMatch ? parseInt(archiveMatch[1]) : 0;
|
|
27710
|
+
for (const entry of toArchive) {
|
|
27711
|
+
archiveLines.splice(archiveHeaderIdx + 1, 0, ` ${entry}`);
|
|
27712
|
+
}
|
|
27713
|
+
archiveLines[archiveHeaderIdx] = archiveLines[archiveHeaderIdx].replace(/archived\[\d+\|/, `[${archiveCount + toArchive.length}|`);
|
|
27714
|
+
writeFileSync(ARCHIVE_FILE, archiveLines.join("\n"));
|
|
27715
|
+
lines[headerIdx] = lines[headerIdx].replace(/entries\[\d+\|/, `[${toKeep.length}|`);
|
|
27716
|
+
lines.splice(headerIdx + 1, entryLines.length, ...toKeep.map((l) => ` ${l}`));
|
|
27717
|
+
writeMemory(lines.join("\n"));
|
|
27718
|
+
return { archived: toArchive.length, kept: toKeep.length };
|
|
27719
|
+
}
|
|
27666
27720
|
var server = new McpServer(
|
|
27667
27721
|
{ name: "toon-memory", version: "1.1.0" },
|
|
27668
27722
|
{ capabilities: { tools: { listChanged: true } } }
|
|
@@ -27709,10 +27763,12 @@ server.registerTool(
|
|
|
27709
27763
|
description: "Busca en la memoria persistente del proyecto. Devuelve entradas relevantes. Usar ANTES de leer archivos.",
|
|
27710
27764
|
inputSchema: {
|
|
27711
27765
|
query: external_exports.string().describe("Texto a buscar"),
|
|
27712
|
-
category: external_exports.string().optional().default("").describe("Filtrar por categor\xEDa (vac\xEDo = todos)")
|
|
27766
|
+
category: external_exports.string().optional().default("").describe("Filtrar por categor\xEDa (vac\xEDo = todos)"),
|
|
27767
|
+
from_date: external_exports.string().optional().default("").describe("Fecha inicio filtro (YYYY-MM-DD)"),
|
|
27768
|
+
to_date: external_exports.string().optional().default("").describe("Fecha fin filtro (YYYY-MM-DD)")
|
|
27713
27769
|
}
|
|
27714
27770
|
},
|
|
27715
|
-
async ({ query, category }) => {
|
|
27771
|
+
async ({ query, category, from_date, to_date }) => {
|
|
27716
27772
|
const data = readMemory();
|
|
27717
27773
|
const lines = data.split("\n").filter((l) => l.startsWith(" ") && l.includes("|"));
|
|
27718
27774
|
const queryLower = query.toLowerCase();
|
|
@@ -27722,6 +27778,8 @@ server.registerTool(
|
|
|
27722
27778
|
if (parts.length < 7) return null;
|
|
27723
27779
|
const [id, cat, key, content, file2, tags, date5] = parts;
|
|
27724
27780
|
if (category && cat !== category) return null;
|
|
27781
|
+
if (from_date && date5 < from_date) return null;
|
|
27782
|
+
if (to_date && date5 > to_date) return null;
|
|
27725
27783
|
const searchStr = `${id} ${cat} ${key} ${content} ${file2} ${tags}`.toLowerCase();
|
|
27726
27784
|
if (!searchStr.includes(queryLower)) return null;
|
|
27727
27785
|
return { id, cat, key, content, file: file2, tags, date: date5 };
|
|
@@ -27849,6 +27907,27 @@ server.registerTool(
|
|
|
27849
27907
|
};
|
|
27850
27908
|
}
|
|
27851
27909
|
);
|
|
27910
|
+
server.registerTool(
|
|
27911
|
+
"memory_archive",
|
|
27912
|
+
{
|
|
27913
|
+
title: "Archive Old Entries",
|
|
27914
|
+
description: "Mover entradas antiguas (>30 d\xEDas) a archive.toon para mantener la memoria limpia.",
|
|
27915
|
+
inputSchema: {}
|
|
27916
|
+
},
|
|
27917
|
+
async () => {
|
|
27918
|
+
const result = archiveOldEntries();
|
|
27919
|
+
if (result.archived === 0) {
|
|
27920
|
+
return { content: [{ type: "text", text: "No hay entradas antiguas para archivar" }] };
|
|
27921
|
+
}
|
|
27922
|
+
return {
|
|
27923
|
+
content: [{
|
|
27924
|
+
type: "text",
|
|
27925
|
+
text: `\u{1F4E6} Archivadas ${result.archived} entradas antiguas
|
|
27926
|
+
\u{1F4CB} Quedan ${result.kept} entradas activas`
|
|
27927
|
+
}]
|
|
27928
|
+
};
|
|
27929
|
+
}
|
|
27930
|
+
);
|
|
27852
27931
|
var transport = new StdioServerTransport();
|
|
27853
27932
|
await server.connect(transport);
|
|
27854
27933
|
/*! Bundled license information:
|
package/package.json
CHANGED
package/src/cli/setup.ts
CHANGED
|
@@ -112,6 +112,24 @@ function installOpenCodeTools(): void {
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
// Add .opencode/memory/ to .gitignore if not present
|
|
116
|
+
function ensureGitignore(): void {
|
|
117
|
+
const gitignorePath = join(projectRoot, ".gitignore")
|
|
118
|
+
const entry = ".opencode/memory/"
|
|
119
|
+
|
|
120
|
+
if (!existsSync(gitignorePath)) {
|
|
121
|
+
writeFileSync(gitignorePath, `${entry}\n`)
|
|
122
|
+
console.log(" Created .gitignore with memory exclusion")
|
|
123
|
+
return
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const content = readFileSync(gitignorePath, "utf-8")
|
|
127
|
+
if (!content.includes(entry)) {
|
|
128
|
+
writeFileSync(gitignorePath, `${content.trim()}\n${entry}\n`)
|
|
129
|
+
console.log(" Added .opencode/memory/ to .gitignore")
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
115
133
|
// Install MCP server config for different agents
|
|
116
134
|
function installMCPConfig(agent: Agent, scope: string): void {
|
|
117
135
|
const configPath = scope === "global" ? agent.global : agent.local
|
|
@@ -197,6 +215,8 @@ function init(scope: string = "local"): void {
|
|
|
197
215
|
console.log("")
|
|
198
216
|
}
|
|
199
217
|
|
|
218
|
+
ensureGitignore()
|
|
219
|
+
|
|
200
220
|
console.log("Done! Restart your agent to use memory tools.\n")
|
|
201
221
|
}
|
|
202
222
|
|
|
@@ -487,6 +507,8 @@ rl.question("Install (1) Local or (2) Global? [1/2]: ", (answer: string) => {
|
|
|
487
507
|
console.log("")
|
|
488
508
|
}
|
|
489
509
|
|
|
510
|
+
ensureGitignore()
|
|
511
|
+
|
|
490
512
|
console.log("Done! Restart your agent to use memory tools.")
|
|
491
513
|
console.log("Run 'npx toon-memory uninstall' to remove.\n")
|
|
492
514
|
rl.close()
|
package/src/mcp/server.ts
CHANGED
|
@@ -9,6 +9,9 @@ import { randomBytes } from "crypto"
|
|
|
9
9
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
10
10
|
const MEMORY_DIR = join(process.cwd(), ".opencode", "memory")
|
|
11
11
|
const MEMORY_FILE = join(MEMORY_DIR, "data.toon")
|
|
12
|
+
const ARCHIVE_FILE = join(MEMORY_DIR, "archive.toon")
|
|
13
|
+
const MAX_ENTRIES = 100
|
|
14
|
+
const ARCHIVE_DAYS = 30
|
|
12
15
|
|
|
13
16
|
function ensureMemoryFile() {
|
|
14
17
|
if (!existsSync(MEMORY_DIR)) mkdirSync(MEMORY_DIR, { recursive: true })
|
|
@@ -31,6 +34,71 @@ function generateId(): string {
|
|
|
31
34
|
return randomBytes(4).toString("hex")
|
|
32
35
|
}
|
|
33
36
|
|
|
37
|
+
function archiveOldEntries(): { archived: number; kept: number } {
|
|
38
|
+
const data = readMemory()
|
|
39
|
+
const lines = data.split("\n")
|
|
40
|
+
const headerIdx = lines.findIndex((l) => l.startsWith("entries["))
|
|
41
|
+
|
|
42
|
+
if (headerIdx === -1) return { archived: 0, kept: 0 }
|
|
43
|
+
|
|
44
|
+
const today = new Date()
|
|
45
|
+
const cutoff = new Date(today)
|
|
46
|
+
cutoff.setDate(cutoff.getDate() - ARCHIVE_DAYS)
|
|
47
|
+
const cutoffStr = cutoff.toISOString().split("T")[0]
|
|
48
|
+
|
|
49
|
+
const entryLines = lines.slice(headerIdx + 1).filter((l) => l.trim().length > 0)
|
|
50
|
+
const toArchive: string[] = []
|
|
51
|
+
const toKeep: string[] = []
|
|
52
|
+
|
|
53
|
+
for (const line of entryLines) {
|
|
54
|
+
const parts = line.trim().split("|")
|
|
55
|
+
if (parts.length >= 7) {
|
|
56
|
+
const date = parts[6]
|
|
57
|
+
if (date < cutoffStr) {
|
|
58
|
+
toArchive.push(line.trim())
|
|
59
|
+
} else {
|
|
60
|
+
toKeep.push(line.trim())
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
toKeep.push(line.trim())
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (toArchive.length === 0) return { archived: 0, kept: toKeep.length }
|
|
68
|
+
|
|
69
|
+
// Write archived entries
|
|
70
|
+
let archiveContent = ""
|
|
71
|
+
if (existsSync(ARCHIVE_FILE)) {
|
|
72
|
+
archiveContent = readFileSync(ARCHIVE_FILE, "utf-8")
|
|
73
|
+
} else {
|
|
74
|
+
archiveContent = `version: 1\narchived:\n`
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const archiveLines = archiveContent.split("\n")
|
|
78
|
+
let archiveHeaderIdx = archiveLines.findIndex((l) => l.startsWith("archived["))
|
|
79
|
+
if (archiveHeaderIdx === -1) {
|
|
80
|
+
archiveLines.push(`archived[0|]{id|category|key|content|file|tags|date}:`)
|
|
81
|
+
archiveHeaderIdx = archiveLines.length - 1
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const archiveMatch = archiveLines[archiveHeaderIdx].match(/archived\[(\d+)\|/)
|
|
85
|
+
const archiveCount = archiveMatch ? parseInt(archiveMatch[1]) : 0
|
|
86
|
+
|
|
87
|
+
for (const entry of toArchive) {
|
|
88
|
+
archiveLines.splice(archiveHeaderIdx + 1, 0, ` ${entry}`)
|
|
89
|
+
}
|
|
90
|
+
archiveLines[archiveHeaderIdx] = archiveLines[archiveHeaderIdx].replace(/archived\[\d+\|/, `[${archiveCount + toArchive.length}|`)
|
|
91
|
+
|
|
92
|
+
writeFileSync(ARCHIVE_FILE, archiveLines.join("\n"))
|
|
93
|
+
|
|
94
|
+
// Update main file
|
|
95
|
+
lines[headerIdx] = lines[headerIdx].replace(/entries\[\d+\|/, `[${toKeep.length}|`)
|
|
96
|
+
lines.splice(headerIdx + 1, entryLines.length, ...toKeep.map((l) => ` ${l}`))
|
|
97
|
+
writeMemory(lines.join("\n"))
|
|
98
|
+
|
|
99
|
+
return { archived: toArchive.length, kept: toKeep.length }
|
|
100
|
+
}
|
|
101
|
+
|
|
34
102
|
const server = new McpServer(
|
|
35
103
|
{ name: "toon-memory", version: "1.1.0" },
|
|
36
104
|
{ capabilities: { tools: { listChanged: true } } }
|
|
@@ -83,9 +151,11 @@ server.registerTool(
|
|
|
83
151
|
inputSchema: {
|
|
84
152
|
query: z.string().describe("Texto a buscar"),
|
|
85
153
|
category: z.string().optional().default("").describe("Filtrar por categoría (vacío = todos)"),
|
|
154
|
+
from_date: z.string().optional().default("").describe("Fecha inicio filtro (YYYY-MM-DD)"),
|
|
155
|
+
to_date: z.string().optional().default("").describe("Fecha fin filtro (YYYY-MM-DD)"),
|
|
86
156
|
},
|
|
87
157
|
},
|
|
88
|
-
async ({ query, category }) => {
|
|
158
|
+
async ({ query, category, from_date, to_date }) => {
|
|
89
159
|
const data = readMemory()
|
|
90
160
|
const lines = data.split("\n").filter((l) => l.startsWith(" ") && l.includes("|"))
|
|
91
161
|
const queryLower = query.toLowerCase()
|
|
@@ -97,6 +167,8 @@ server.registerTool(
|
|
|
97
167
|
if (parts.length < 7) return null
|
|
98
168
|
const [id, cat, key, content, file, tags, date] = parts
|
|
99
169
|
if (category && cat !== category) return null
|
|
170
|
+
if (from_date && date < from_date) return null
|
|
171
|
+
if (to_date && date > to_date) return null
|
|
100
172
|
const searchStr = `${id} ${cat} ${key} ${content} ${file} ${tags}`.toLowerCase()
|
|
101
173
|
if (!searchStr.includes(queryLower)) return null
|
|
102
174
|
return { id, cat, key, content, file, tags, date }
|
|
@@ -247,5 +319,28 @@ server.registerTool(
|
|
|
247
319
|
}
|
|
248
320
|
)
|
|
249
321
|
|
|
322
|
+
server.registerTool(
|
|
323
|
+
"memory_archive",
|
|
324
|
+
{
|
|
325
|
+
title: "Archive Old Entries",
|
|
326
|
+
description: "Mover entradas antiguas (>30 días) a archive.toon para mantener la memoria limpia.",
|
|
327
|
+
inputSchema: {},
|
|
328
|
+
},
|
|
329
|
+
async () => {
|
|
330
|
+
const result = archiveOldEntries()
|
|
331
|
+
|
|
332
|
+
if (result.archived === 0) {
|
|
333
|
+
return { content: [{ type: "text" as const, text: "No hay entradas antiguas para archivar" }] }
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return {
|
|
337
|
+
content: [{
|
|
338
|
+
type: "text" as const,
|
|
339
|
+
text: `📦 Archivadas ${result.archived} entradas antiguas\n📋 Quedan ${result.kept} entradas activas`
|
|
340
|
+
}],
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
)
|
|
344
|
+
|
|
250
345
|
const transport = new StdioServerTransport()
|
|
251
346
|
await server.connect(transport)
|