toon-memory 1.5.0 → 1.6.2

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 CHANGED
@@ -1,10 +1,29 @@
1
1
  # toon-memory
2
2
 
3
- Persistent memory for AI coding agents — remember decisions, patterns, and bugs between sessions.
3
+ > Persistent memory for AI coding agents — remember decisions, patterns, and bugs between sessions.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/toon-memory.svg)](https://www.npmjs.com/package/toon-memory)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
7
  [![CI](https://github.com/LuiggiVal08/toon-memory/actions/workflows/ci.yml/badge.svg)](https://github.com/LuiggiVal08/toon-memory/actions/workflows/ci.yml)
8
+ [![Docs](https://img.shields.io/badge/docs-online-blue)](https://luiggival08.github.io/toon-memory/)
9
+
10
+ ---
11
+
12
+ ## Table of Contents
13
+
14
+ - [What is toon-memory?](#what-is-toon-memory)
15
+ - [Blog Post](#blog-post)
16
+ - [Features](#features)
17
+ - [Quick Start](#quick-start)
18
+ - [Supported Agents](#supported-agents)
19
+ - [MCP Tools](#mcp-tools)
20
+ - [CLI Commands](#cli-commands)
21
+ - [Configuration](#configuration)
22
+ - [How It Works](#how-it-works)
23
+ - [Why TOON?](#why-toon)
24
+ - [Development](#development)
25
+ - [Contributing](#contributing)
26
+ - [License](#license)
8
27
 
9
28
  ---
10
29
 
@@ -12,6 +31,8 @@ Persistent memory for AI coding agents — remember decisions, patterns, and bug
12
31
 
13
32
  AI agents forget everything between sessions. toon-memory fixes this by providing persistent memory that survives restarts.
14
33
 
34
+ 📖 **[Read the documentation](https://luiggival08.github.io/toon-memory/)**
35
+
15
36
  **Use cases:**
16
37
  - Remember design decisions ("Why did we choose X over Y?")
17
38
  - Track patterns ("This project uses Zod for validation")
@@ -20,18 +41,24 @@ AI agents forget everything between sessions. toon-memory fixes this by providin
20
41
 
21
42
  ---
22
43
 
44
+ ## Blog Post
45
+
46
+ Read [How toon-memory Makes Your AI Agent Smarter](https://luiggival08.github.io/toon-memory/blog) to see a real-world demo of persistent memory in action.
47
+
48
+ ---
49
+
23
50
  ## Features
24
51
 
25
- - **8 MCP tools** — `memory_remember`, `memory_recall`, `memory_forget`, `memory_stats`, `memory_summary`, `memory_archive`, `memory_encrypt`, `memory_decrypt`
52
+ - **8 MCP tools** — Full memory management via Model Context Protocol
26
53
  - **7 agents supported** — OpenCode, VS Code/Copilot, Claude Code, Cursor, Windsurf, Cline, Continue
27
54
  - **TOON format** — 40% fewer tokens than JSON, better LLM comprehension
28
- - **Per-project memory** — each project gets its own memory file
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
55
+ - **Per-project memory** — Each project gets its own memory file
56
+ - **Zero config** — Just install and use
57
+ - **Auto gitignore** — Automatically adds `.opencode/memory/` to `.gitignore`
58
+ - **Date filtering** — Search memory by date range
59
+ - **Auto-archive** — Old entries (>30 days) moved to archive automatically
33
60
  - **Encryption** — AES-256-GCM encryption for sensitive data
34
- - **Watch mode** — auto-backup every N minutes
61
+ - **Watch mode** — Auto-backup every N minutes
35
62
 
36
63
  ---
37
64
 
@@ -83,7 +110,7 @@ memory_remember # Save important decisions
83
110
 
84
111
  ---
85
112
 
86
- ## Tools
113
+ ## MCP Tools
87
114
 
88
115
  | Tool | Description |
89
116
  |------|-------------|
@@ -96,12 +123,33 @@ memory_remember # Save important decisions
96
123
  | `memory_encrypt` | Enable AES-256-GCM encryption |
97
124
  | `memory_decrypt` | Disable encryption |
98
125
 
99
- ### Date Filtering
126
+ ### Examples
127
+
128
+ #### Remember a decision
100
129
 
101
- Search memory by date range:
130
+ ```typescript
131
+ memory_remember({
132
+ category: "decision",
133
+ key: "use-zod",
134
+ content: "Use Zod for validation",
135
+ file: "src/types.ts",
136
+ tags: "validation;types"
137
+ })
138
+ // 🧠 Guardado: decision/use-zod (a1b2c3d4)
139
+ ```
140
+
141
+ #### Search memory
142
+
143
+ ```typescript
144
+ memory_recall({ query: "redis" })
145
+ // [bug] redis-pool-fix (i9j0k1l2)
146
+ // Added max_connections=20
147
+ // File: redis.ts | Tags: redis;fix | Date: 2026-07-10
148
+ ```
149
+
150
+ #### Search with date filter
102
151
 
103
152
  ```typescript
104
- // Search with date filter
105
153
  memory_recall({
106
154
  query: "redis",
107
155
  from_date: "2026-07-01",
@@ -109,52 +157,21 @@ memory_recall({
109
157
  })
110
158
  ```
111
159
 
112
- ### Auto-Archive
113
-
114
- Entries older than 30 days are automatically archived to keep memory clean:
160
+ #### Archive old entries
115
161
 
116
162
  ```typescript
117
- // Manually trigger archiving
118
163
  memory_archive()
119
164
  // 📦 Archivadas 5 entradas antiguas
120
165
  // 📋 Quedan 42 entradas activas
121
166
  ```
122
167
 
123
- ### Encryption
124
-
125
- Enable AES-256-GCM encryption for sensitive data:
168
+ #### Enable encryption
126
169
 
127
170
  ```typescript
128
- // Enable encryption
129
171
  memory_encrypt()
130
172
  // 🔐 Encriptación habilitada
131
173
  // ⚠️ Guarda esta clave (no se puede recuperar):
132
174
  // a1b2c3d4...
133
-
134
- // Disable encryption
135
- memory_decrypt({ key: "a1b2c3d4..." })
136
- // 🔓 Encriptación deshabilitada
137
- ```
138
-
139
- ---
140
-
141
- ## How It Works
142
-
143
- 1. **MCP Server** — runs locally, talks to your agent via stdio
144
- 2. **TOON Format** — stores data in Token-Oriented Object Notation (~40% fewer tokens than JSON)
145
- 3. **Per-project memory** — each project gets `.opencode/memory/data.toon`
146
- 4. **Zero config** — just install and use
147
-
148
- ### Memory File Format
149
-
150
- ```
151
- version: 1
152
- entries[3|]{id|category|key|content|file|tags|date}:
153
- a1b2c3d4|decision|use-zod|Use Zod for validation|src/types.ts|validation;types|2026-07-10
154
- e5f6g7h8|pattern|pydantic-configs|Project uses Pydantic v2|config.py|python;patterns|2026-07-10
155
- i9j0k1l2|bug|redis-pool-fix|Added max_connections=20|redis.ts|redis;fix|2026-07-10
156
- summaries:
157
- src/services/redis.ts: Redis connection pool with retry logic
158
175
  ```
159
176
 
160
177
  ---
@@ -169,12 +186,14 @@ npx toon-memory status # Check installation status
169
186
  npx toon-memory stats # View memory statistics
170
187
  npx toon-memory export # Export memory to JSON
171
188
  npx toon-memory import <file> # Import memory from JSON
172
- npx toon-memory watch [mins] # Auto-backup every N minutes (default: 5)
189
+ npx toon-memory watch [options] # Auto-backup with options
173
190
  npx toon-memory upgrade # Update to latest version
174
191
  npx toon-memory uninstall # Remove from all agents
175
192
  ```
176
193
 
177
- ### Stats
194
+ ### Examples
195
+
196
+ #### Stats
178
197
 
179
198
  ```bash
180
199
  $ npx toon-memory stats
@@ -192,7 +211,7 @@ Last updated: 2026-07-10
192
211
  File size: 12.4 KB
193
212
  ```
194
213
 
195
- ### Export
214
+ #### Export
196
215
 
197
216
  ```bash
198
217
  $ npx toon-memory export
@@ -203,7 +222,7 @@ Exported 45 entries to:
203
222
  /path/to/project/toon-memory-export.json
204
223
  ```
205
224
 
206
- ### Import
225
+ #### Import
207
226
 
208
227
  ```bash
209
228
  $ npx toon-memory import backup.json
@@ -214,6 +233,34 @@ Imported 3 new entries
214
233
  Skipped 2 duplicates
215
234
  ```
216
235
 
236
+ #### Watch
237
+
238
+ ```bash
239
+ $ npx toon-memory watch 15 -c -m 20
240
+
241
+ 🧠 toon-memory watch
242
+
243
+ Watching memory file every 15 minutes...
244
+ Max backups: 20
245
+ Compression: enabled
246
+ Logging: disabled
247
+ Press Ctrl+C to stop
248
+
249
+ 📦 Backup #1 created: 2026-07-11T16-00-00-000Z
250
+ 📦 Backup #2 created: 2026-07-11T16-15-00-000Z
251
+ ^C
252
+ ✅ Watch stopped. 2 backups created.
253
+ ```
254
+
255
+ **Watch Options:**
256
+
257
+ | Option | Description | Default |
258
+ |--------|-------------|---------|
259
+ | `[interval]` | Backup interval in minutes | 5 |
260
+ | `-c, --compress` | Enable gzip compression | off |
261
+ | `-l, --log [path]` | Enable file logging | off |
262
+ | `-m, --max-backups <n>` | Max backups to keep (0=unlimited) | 10 |
263
+
217
264
  ---
218
265
 
219
266
  ## Configuration
@@ -267,6 +314,41 @@ Add to `.vscode/mcp.json`:
267
314
 
268
315
  ---
269
316
 
317
+ ## How It Works
318
+
319
+ 1. **MCP Server** — Runs locally, talks to your agent via stdio
320
+ 2. **TOON Format** — Stores data in Token-Oriented Object Notation (~40% fewer tokens than JSON)
321
+ 3. **Per-project memory** — Each project gets `.opencode/memory/data.toon`
322
+ 4. **Zero config** — Just install and use
323
+
324
+ ### Memory File Format
325
+
326
+ ```
327
+ version: 1
328
+ entries[3|]{id|category|key|content|file|tags|date}:
329
+ a1b2c3d4|decision|use-zod|Use Zod for validation|src/types.ts|validation;types|2026-07-10
330
+ e5f6g7h8|pattern|pydantic-configs|Project uses Pydantic v2|config.py|python;patterns|2026-07-10
331
+ i9j0k1l2|bug|redis-pool-fix|Added max_connections=20|redis.ts|redis;fix|2026-07-10
332
+ summaries:
333
+ src/services/redis.ts: Redis connection pool with retry logic
334
+ ```
335
+
336
+ ### File Structure
337
+
338
+ ```
339
+ .opencode/
340
+ ├── memory/
341
+ │ ├── data.toon # Main memory file
342
+ │ ├── archive.toon # Archived entries (>30 days)
343
+ │ ├── config.json # Encryption settings
344
+ │ └── backups/ # Watch mode backups
345
+ │ ├── backup-2026-07-11T16-00-00-000Z.toon
346
+ │ └── backup-2026-07-11T16-10-00-000Z.toon
347
+ └── opencode.json # MCP server config
348
+ ```
349
+
350
+ ---
351
+
270
352
  ## Why TOON?
271
353
 
272
354
  TOON (Token-Oriented Object Notation) is designed for LLMs:
@@ -278,8 +360,8 @@ TOON (Token-Oriented Object Notation) is designed for LLMs:
278
360
  | **TOON** | **60** | **76.4%** |
279
361
 
280
362
  - **40% fewer tokens** than JSON
281
- - **Lossless roundtrip** — no data loss
282
- - **Better LLM comprehension** — structured for AI consumption
363
+ - **Lossless roundtrip** — No data loss
364
+ - **Better LLM comprehension** — Structured for AI consumption
283
365
 
284
366
  ---
285
367
 
@@ -293,6 +375,30 @@ npm run build
293
375
  npm test
294
376
  ```
295
377
 
378
+ ### Project Structure
379
+
380
+ ```
381
+ toon-memory/
382
+ ├── src/
383
+ │ ├── bin/
384
+ │ │ └── toon-memory.ts # Entry point
385
+ │ ├── cli/
386
+ │ │ ├── setup.ts # CLI commands
387
+ │ │ └── toon-memory.ts # CLI runner
388
+ │ ├── mcp/
389
+ │ │ └── server.ts # MCP server (8 tools)
390
+ │ └── memory.ts # Custom tool (OpenCode)
391
+ ├── tests/
392
+ │ ├── cli.test.ts # CLI tests
393
+ │ └── memory.test.ts # Memory tests
394
+ ├── .github/workflows/
395
+ │ ├── ci.yml # CI (Node.js 20/22)
396
+ │ └── publish.yml # Auto-publish on release
397
+ ├── package.json
398
+ ├── tsconfig.json
399
+ └── vitest.config.ts
400
+ ```
401
+
296
402
  ---
297
403
 
298
404
  ## Contributing
File without changes
package/dist/cli/setup.js CHANGED
@@ -1,9 +1,10 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync, cpSync, unlinkSync } from "fs";
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync, readdirSync, statSync } from "fs";
2
2
  import { basename, dirname, join } from "path";
3
3
  import { fileURLToPath } from "url";
4
4
  import { execSync } from "child_process";
5
5
  import { createInterface } from "readline";
6
6
  import { createRequire } from "module";
7
+ import { gzipSync, gunzipSync } from "zlib";
7
8
  const __dirname = dirname(fileURLToPath(import.meta.url));
8
9
  const projectRoot = process.cwd();
9
10
  const sourceDir = join(__dirname, "..", "..", "src");
@@ -65,15 +66,11 @@ function detectAgents() {
65
66
  return agents2;
66
67
  }
67
68
  function installOpenCodeTools() {
68
- const toolsDir = join(projectRoot, ".opencode", "tools");
69
69
  const memoryDir = join(projectRoot, ".opencode", "memory");
70
70
  const memoryFile = join(memoryDir, "data.toon");
71
- if (!existsSync(toolsDir)) mkdirSync(toolsDir, { recursive: true });
72
71
  if (!existsSync(memoryDir)) mkdirSync(memoryDir, { recursive: true });
73
- cpSync(join(sourceDir, "memory.ts"), join(toolsDir, "memory.ts"));
74
- console.log(" Copied memory.ts to .opencode/tools/");
75
72
  if (!existsSync(memoryFile)) {
76
- writeFileSync(memoryFile, "version: 1\nentries[0|]{id|category|key|content|file|tags|date}:\n");
73
+ writeFileSync(memoryFile, "version: 1\n[0|]\n");
77
74
  console.log(" Created .opencode/memory/data.toon");
78
75
  }
79
76
  }
@@ -112,10 +109,18 @@ function installMCPConfig(agent, scope) {
112
109
  }
113
110
  const mcpKey = agent.mcpKey || "mcpServers";
114
111
  if (!config[mcpKey]) config[mcpKey] = {};
115
- config[mcpKey]["toon-memory"] = {
116
- command: "npx",
117
- args: ["-y", "toon-memory", "mcp"]
118
- };
112
+ if (agent.name === "opencode") {
113
+ config[mcpKey]["toon-memory"] = {
114
+ enabled: true,
115
+ type: "local",
116
+ command: ["npx", "-y", "toon-memory", "mcp"]
117
+ };
118
+ } else {
119
+ config[mcpKey]["toon-memory"] = {
120
+ command: "npx",
121
+ args: ["-y", "toon-memory", "mcp"]
122
+ };
123
+ }
119
124
  writeFileSync(configPath, JSON.stringify(config, null, 2));
120
125
  console.log(` MCP server added to ${configPath}`);
121
126
  }
@@ -335,6 +340,56 @@ ${newLines}
335
340
  console.log(`Skipped ${importData.entries.length - newEntries.length} duplicates
336
341
  `);
337
342
  }
343
+ function parseWatchOptions(args2) {
344
+ const opts = {
345
+ interval: 5,
346
+ maxBackups: 10,
347
+ compress: false,
348
+ logFile: false,
349
+ logPath: ""
350
+ };
351
+ for (let i = 1; i < args2.length; i++) {
352
+ const arg = args2[i];
353
+ if (arg === "--compress" || arg === "-c") {
354
+ opts.compress = true;
355
+ } else if (arg === "--log" || arg === "-l") {
356
+ opts.logFile = true;
357
+ opts.logPath = args2[++i] || join(projectRoot, ".opencode", "memory", "watch.log");
358
+ } else if (arg === "--max-backups" || arg === "-m") {
359
+ opts.maxBackups = parseInt(args2[++i]) || 10;
360
+ } else if (!arg.startsWith("-")) {
361
+ opts.interval = parseInt(arg) || 5;
362
+ }
363
+ }
364
+ return opts;
365
+ }
366
+ function writeWatchLog(logPath, message) {
367
+ if (!logPath) return;
368
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
369
+ const logLine = `[${timestamp}] ${message}
370
+ `;
371
+ writeFileSync(logPath, logLine, { flag: "a" });
372
+ }
373
+ function getBackupFiles(backupDir) {
374
+ if (!existsSync(backupDir)) return [];
375
+ return readdirSync(backupDir).filter((f) => f.startsWith("backup-") && (f.endsWith(".toon") || f.endsWith(".gz"))).map((f) => join(backupDir, f)).sort((a, b) => statSync(a).mtimeMs - statSync(b).mtimeMs);
376
+ }
377
+ function pruneBackups(backupDir, maxBackups) {
378
+ if (maxBackups <= 0) return 0;
379
+ const files = getBackupFiles(backupDir);
380
+ const excess = files.length - maxBackups;
381
+ if (excess <= 0) return 0;
382
+ for (let i = 0; i < excess; i++) {
383
+ unlinkSync(files[i]);
384
+ }
385
+ return excess;
386
+ }
387
+ function compressData(data) {
388
+ return gzipSync(Buffer.from(data, "utf-8"));
389
+ }
390
+ function decompressData(data) {
391
+ return gunzipSync(data).toString("utf-8");
392
+ }
338
393
  function watch() {
339
394
  console.log("\n\u{1F9E0} toon-memory watch\n");
340
395
  const memoryFile = join(projectRoot, ".opencode", "memory", "data.toon");
@@ -344,30 +399,68 @@ function watch() {
344
399
  return;
345
400
  }
346
401
  if (!existsSync(backupDir)) mkdirSync(backupDir, { recursive: true });
347
- const intervalMinutes = parseInt(args[1]) || 5;
348
- console.log(`Watching memory file every ${intervalMinutes} minutes...`);
402
+ const opts = parseWatchOptions(args);
403
+ if (opts.logFile) {
404
+ const logDir = dirname(opts.logPath);
405
+ if (!existsSync(logDir)) mkdirSync(logDir, { recursive: true });
406
+ }
407
+ console.log(`Watching memory file every ${opts.interval} minutes...`);
408
+ console.log(`Max backups: ${opts.maxBackups === 0 ? "unlimited" : opts.maxBackups}`);
409
+ console.log(`Compression: ${opts.compress ? "enabled" : "disabled"}`);
410
+ console.log(`Logging: ${opts.logFile ? `enabled (${opts.logPath})` : "disabled"}`);
349
411
  console.log(`Press Ctrl+C to stop
350
412
  `);
351
413
  let lastContent = readFileSync(memoryFile, "utf-8");
414
+ let lastHash = hashContent(lastContent);
352
415
  let backupCount = 0;
416
+ if (opts.logFile) writeWatchLog(opts.logPath, "Watch started");
353
417
  const backup = () => {
354
- const currentContent = readFileSync(memoryFile, "utf-8");
355
- if (currentContent !== lastContent) {
356
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
357
- const backupFile = join(backupDir, `backup-${timestamp}.toon`);
358
- writeFileSync(backupFile, currentContent);
359
- backupCount++;
360
- console.log(`\u{1F4E6} Backup #${backupCount} created: ${timestamp}`);
361
- lastContent = currentContent;
418
+ try {
419
+ const currentContent = readFileSync(memoryFile, "utf-8");
420
+ const currentHash = hashContent(currentContent);
421
+ if (currentHash !== lastHash) {
422
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
423
+ const ext = opts.compress ? ".toon.gz" : ".toon";
424
+ const backupFile = join(backupDir, `backup-${timestamp}${ext}`);
425
+ if (opts.compress) {
426
+ writeFileSync(backupFile, compressData(currentContent));
427
+ } else {
428
+ writeFileSync(backupFile, currentContent);
429
+ }
430
+ backupCount++;
431
+ console.log(`\u{1F4E6} Backup #${backupCount} created: ${timestamp}`);
432
+ if (opts.logFile) writeWatchLog(opts.logPath, `Backup #${backupCount}: ${timestamp}`);
433
+ lastContent = currentContent;
434
+ lastHash = currentHash;
435
+ const pruned = pruneBackups(backupDir, opts.maxBackups);
436
+ if (pruned > 0) {
437
+ console.log(`\u{1F5D1}\uFE0F Pruned ${pruned} old backup(s)`);
438
+ if (opts.logFile) writeWatchLog(opts.logPath, `Pruned ${pruned} old backup(s)`);
439
+ }
440
+ }
441
+ } catch (err) {
442
+ const msg = `Error creating backup: ${err.message}`;
443
+ console.error(`\u274C ${msg}`);
444
+ if (opts.logFile) writeWatchLog(opts.logPath, msg);
362
445
  }
363
446
  };
447
+ function hashContent(content) {
448
+ let hash = 0;
449
+ for (let i = 0; i < content.length; i++) {
450
+ const char = content.charCodeAt(i);
451
+ hash = (hash << 5) - hash + char;
452
+ hash = hash & hash;
453
+ }
454
+ return hash.toString(36);
455
+ }
364
456
  backup();
365
- const interval = setInterval(backup, intervalMinutes * 60 * 1e3);
457
+ const interval = setInterval(backup, opts.interval * 60 * 1e3);
366
458
  process.on("SIGINT", () => {
367
459
  clearInterval(interval);
368
460
  console.log(`
369
461
  \u2705 Watch stopped. ${backupCount} backups created.
370
462
  `);
463
+ if (opts.logFile) writeWatchLog(opts.logPath, `Watch stopped. ${backupCount} backups created.`);
371
464
  process.exit(0);
372
465
  });
373
466
  process.on("SIGTERM", () => {
package/mcp/server.js CHANGED
@@ -27796,16 +27796,16 @@ server.registerTool(
27796
27796
  const id = generateId();
27797
27797
  const date5 = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
27798
27798
  const lines = data.split("\n");
27799
- let headerIdx = lines.findIndex((l) => l.startsWith("entries["));
27799
+ let headerIdx = lines.findIndex((l) => l.startsWith("entries[") || /^\[\d+\|]/.test(l));
27800
27800
  if (headerIdx === -1) {
27801
- lines.push(`entries[0|]{id|category|key|content|file|tags|date}:`);
27801
+ lines.push(`[0|]`);
27802
27802
  headerIdx = lines.length - 1;
27803
27803
  }
27804
- const match = lines[headerIdx].match(/entries\[(\d+)\|/);
27804
+ const match = lines[headerIdx].match(/\[(\d+)\|/);
27805
27805
  const count = match ? parseInt(match[1]) : 0;
27806
27806
  const newEntry = `${id}|${category}|${key}|${content}|${file2 || ""}|${tags || ""}|${date5}`;
27807
27807
  lines.splice(headerIdx + 1, 0, ` ${newEntry}`);
27808
- lines[headerIdx] = lines[headerIdx].replace(/entries\[\d+\|/, `[${count + 1}|`);
27808
+ lines[headerIdx] = lines[headerIdx].replace(/\[\d+\|/, `[${count + 1}|`);
27809
27809
  writeMemory(lines.join("\n"));
27810
27810
  return {
27811
27811
  content: [{ type: "text", text: `\u{1F9E0} Guardado: ${category}/${key} (${id})
@@ -27827,7 +27827,7 @@ server.registerTool(
27827
27827
  },
27828
27828
  async ({ query, category, from_date, to_date }) => {
27829
27829
  const data = readMemory();
27830
- const lines = data.split("\n").filter((l) => l.startsWith(" ") && l.includes("|"));
27830
+ const lines = data.split("\n").filter((l) => l.startsWith(" ") && l.includes("|") && !l.startsWith("summaries:"));
27831
27831
  const queryLower = query.toLowerCase();
27832
27832
  const results = lines.map((line) => {
27833
27833
  const trimmed = line.trim();
@@ -27862,19 +27862,19 @@ server.registerTool(
27862
27862
  async ({ key }) => {
27863
27863
  const data = readMemory();
27864
27864
  const lines = data.split("\n");
27865
- const headerIdx = lines.findIndex((l) => l.startsWith("entries["));
27865
+ const headerIdx = lines.findIndex((l) => l.startsWith("entries[") || /^\[\d+\|]/.test(l));
27866
27866
  if (headerIdx === -1) {
27867
27867
  return { content: [{ type: "text", text: "No hay entradas en memoria" }] };
27868
27868
  }
27869
- const entryLines = lines.slice(headerIdx + 1).filter((l) => l.trim().length > 0);
27869
+ const entryLines = lines.slice(headerIdx + 1).filter((l) => l.trim().length > 0 && !l.startsWith("summaries:"));
27870
27870
  const filtered = entryLines.filter((l) => {
27871
27871
  const parts = l.trim().split("|");
27872
27872
  return parts[0] !== key && parts[2] !== key;
27873
27873
  });
27874
27874
  const removed = entryLines.length - filtered.length;
27875
- const match = lines[headerIdx].match(/entries\[(\d+)\|/);
27875
+ const match = lines[headerIdx].match(/\[(\d+)\|/);
27876
27876
  const count = match ? parseInt(match[1]) : 0;
27877
- lines[headerIdx] = lines[headerIdx].replace(/entries\[\d+\|/, `[${count - removed}|`);
27877
+ lines[headerIdx] = lines[headerIdx].replace(/\[\d+\|/, `[${count - removed}|`);
27878
27878
  lines.splice(headerIdx + 1, entryLines.length, ...filtered.map((l) => ` ${l.trim()}`));
27879
27879
  writeMemory(lines.join("\n"));
27880
27880
  return {
@@ -27891,7 +27891,7 @@ server.registerTool(
27891
27891
  },
27892
27892
  async () => {
27893
27893
  const data = readMemory();
27894
- const lines = data.split("\n").filter((l) => l.startsWith(" ") && l.includes("|"));
27894
+ const lines = data.split("\n").filter((l) => l.startsWith(" ") && l.includes("|") && !l.startsWith(" summaries:"));
27895
27895
  const entries = lines.map((l) => {
27896
27896
  const parts = l.trim().split("|");
27897
27897
  return { category: parts[1] || "unknown" };
@@ -27900,7 +27900,7 @@ server.registerTool(
27900
27900
  for (const e of entries) {
27901
27901
  byCategory[e.category] = (byCategory[e.category] || 0) + 1;
27902
27902
  }
27903
- const summaryLines = data.split("\n").filter((l) => l.includes(":") && !l.startsWith(" ") && !l.startsWith("version") && !l.startsWith("entries"));
27903
+ const summaryLines = data.split("\n").filter((l) => l.includes(":") && !l.startsWith(" ") && !l.startsWith("version") && !l.startsWith("entries") && !/^\[\d+\|]/.test(l));
27904
27904
  const stats = [
27905
27905
  `Entradas totales: ${entries.length}`,
27906
27906
  `Res\xFAmenes de archivos: ${summaryLines.length}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toon-memory",
3
- "version": "1.5.0",
3
+ "version": "1.6.2",
4
4
  "description": "Persistent memory system for AI coding agents using TOON format (40% fewer tokens than JSON)",
5
5
  "type": "module",
6
6
  "bin": {