toon-memory 1.6.1 → 1.6.3

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/dist/cli/setup.js CHANGED
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync, cpSync, unlinkSync, readdirSync, statSync } 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";
@@ -66,15 +66,11 @@ function detectAgents() {
66
66
  return agents2;
67
67
  }
68
68
  function installOpenCodeTools() {
69
- const toolsDir = join(projectRoot, ".opencode", "tools");
70
69
  const memoryDir = join(projectRoot, ".opencode", "memory");
71
70
  const memoryFile = join(memoryDir, "data.toon");
72
- if (!existsSync(toolsDir)) mkdirSync(toolsDir, { recursive: true });
73
71
  if (!existsSync(memoryDir)) mkdirSync(memoryDir, { recursive: true });
74
- cpSync(join(sourceDir, "memory.ts"), join(toolsDir, "memory.ts"));
75
- console.log(" Copied memory.ts to .opencode/tools/");
76
72
  if (!existsSync(memoryFile)) {
77
- writeFileSync(memoryFile, "version: 1\nentries[0|]{id|category|key|content|file|tags|date}:\n");
73
+ writeFileSync(memoryFile, "version: 1\n[0|]\n");
78
74
  console.log(" Created .opencode/memory/data.toon");
79
75
  }
80
76
  }
package/mcp/server.js CHANGED
@@ -27796,19 +27796,36 @@ 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+)\|/);
27805
- const count = match ? parseInt(match[1]) : 0;
27804
+ let existingIdx = -1;
27805
+ for (let i = headerIdx + 1; i < lines.length; i++) {
27806
+ const line = lines[i];
27807
+ if (!line.startsWith(" ") || !line.includes("|")) continue;
27808
+ if (line.startsWith(" summaries:")) break;
27809
+ const parts = line.trim().split("|");
27810
+ if (parts[2] === key) {
27811
+ existingIdx = i;
27812
+ break;
27813
+ }
27814
+ }
27806
27815
  const newEntry = `${id}|${category}|${key}|${content}|${file2 || ""}|${tags || ""}|${date5}`;
27807
- lines.splice(headerIdx + 1, 0, ` ${newEntry}`);
27808
- lines[headerIdx] = lines[headerIdx].replace(/entries\[\d+\|/, `[${count + 1}|`);
27816
+ let action = "Guardado";
27817
+ if (existingIdx !== -1) {
27818
+ lines[existingIdx] = ` ${newEntry}`;
27819
+ action = "Actualizado";
27820
+ } else {
27821
+ const match = lines[headerIdx].match(/\[(\d+)\|/);
27822
+ const count = match ? parseInt(match[1]) : 0;
27823
+ lines.splice(headerIdx + 1, 0, ` ${newEntry}`);
27824
+ lines[headerIdx] = lines[headerIdx].replace(/\[\d+\|/, `[${count + 1}|`);
27825
+ }
27809
27826
  writeMemory(lines.join("\n"));
27810
27827
  return {
27811
- content: [{ type: "text", text: `\u{1F9E0} Guardado: ${category}/${key} (${id})
27828
+ content: [{ type: "text", text: `\u{1F9E0} ${action}: ${category}/${key} (${id})
27812
27829
  ${content}` }]
27813
27830
  };
27814
27831
  }
@@ -27827,7 +27844,7 @@ server.registerTool(
27827
27844
  },
27828
27845
  async ({ query, category, from_date, to_date }) => {
27829
27846
  const data = readMemory();
27830
- const lines = data.split("\n").filter((l) => l.startsWith(" ") && l.includes("|"));
27847
+ const lines = data.split("\n").filter((l) => l.startsWith(" ") && l.includes("|") && !l.startsWith("summaries:"));
27831
27848
  const queryLower = query.toLowerCase();
27832
27849
  const results = lines.map((line) => {
27833
27850
  const trimmed = line.trim();
@@ -27862,19 +27879,19 @@ server.registerTool(
27862
27879
  async ({ key }) => {
27863
27880
  const data = readMemory();
27864
27881
  const lines = data.split("\n");
27865
- const headerIdx = lines.findIndex((l) => l.startsWith("entries["));
27882
+ const headerIdx = lines.findIndex((l) => l.startsWith("entries[") || /^\[\d+\|]/.test(l));
27866
27883
  if (headerIdx === -1) {
27867
27884
  return { content: [{ type: "text", text: "No hay entradas en memoria" }] };
27868
27885
  }
27869
- const entryLines = lines.slice(headerIdx + 1).filter((l) => l.trim().length > 0);
27886
+ const entryLines = lines.slice(headerIdx + 1).filter((l) => l.trim().length > 0 && !l.startsWith("summaries:"));
27870
27887
  const filtered = entryLines.filter((l) => {
27871
27888
  const parts = l.trim().split("|");
27872
27889
  return parts[0] !== key && parts[2] !== key;
27873
27890
  });
27874
27891
  const removed = entryLines.length - filtered.length;
27875
- const match = lines[headerIdx].match(/entries\[(\d+)\|/);
27892
+ const match = lines[headerIdx].match(/\[(\d+)\|/);
27876
27893
  const count = match ? parseInt(match[1]) : 0;
27877
- lines[headerIdx] = lines[headerIdx].replace(/entries\[\d+\|/, `[${count - removed}|`);
27894
+ lines[headerIdx] = lines[headerIdx].replace(/\[\d+\|/, `[${count - removed}|`);
27878
27895
  lines.splice(headerIdx + 1, entryLines.length, ...filtered.map((l) => ` ${l.trim()}`));
27879
27896
  writeMemory(lines.join("\n"));
27880
27897
  return {
@@ -27891,7 +27908,7 @@ server.registerTool(
27891
27908
  },
27892
27909
  async () => {
27893
27910
  const data = readMemory();
27894
- const lines = data.split("\n").filter((l) => l.startsWith(" ") && l.includes("|"));
27911
+ const lines = data.split("\n").filter((l) => l.startsWith(" ") && l.includes("|") && !l.startsWith(" summaries:"));
27895
27912
  const entries = lines.map((l) => {
27896
27913
  const parts = l.trim().split("|");
27897
27914
  return { category: parts[1] || "unknown" };
@@ -27900,7 +27917,7 @@ server.registerTool(
27900
27917
  for (const e of entries) {
27901
27918
  byCategory[e.category] = (byCategory[e.category] || 0) + 1;
27902
27919
  }
27903
- const summaryLines = data.split("\n").filter((l) => l.includes(":") && !l.startsWith(" ") && !l.startsWith("version") && !l.startsWith("entries"));
27920
+ const summaryLines = data.split("\n").filter((l) => l.includes(":") && !l.startsWith(" ") && !l.startsWith("version") && !l.startsWith("entries") && !/^\[\d+\|]/.test(l));
27904
27921
  const stats = [
27905
27922
  `Entradas totales: ${entries.length}`,
27906
27923
  `Res\xFAmenes de archivos: ${summaryLines.length}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toon-memory",
3
- "version": "1.6.1",
3
+ "version": "1.6.3",
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": {
package/src/cli/setup.ts CHANGED
@@ -116,10 +116,10 @@ function detectAgents(): Agent[] {
116
116
  }
117
117
 
118
118
  /**
119
- * Install custom tools and memory directory for OpenCode.
119
+ * Install memory directory for OpenCode.
120
120
  *
121
- * Creates `.opencode/tools/` and `.opencode/memory/` directories,
122
- * copies `memory.ts` tool, and creates initial `data.toon` if needed.
121
+ * Creates `.opencode/memory/` directory and initial `data.toon` if needed.
122
+ * The MCP server handles all memory operations - no plugin needed.
123
123
  *
124
124
  * @example
125
125
  * ```bash
@@ -127,18 +127,13 @@ function detectAgents(): Agent[] {
127
127
  * ```
128
128
  */
129
129
  function installOpenCodeTools(): void {
130
- const toolsDir = join(projectRoot, ".opencode", "tools")
131
130
  const memoryDir = join(projectRoot, ".opencode", "memory")
132
131
  const memoryFile = join(memoryDir, "data.toon")
133
132
 
134
- if (!existsSync(toolsDir)) mkdirSync(toolsDir, { recursive: true })
135
133
  if (!existsSync(memoryDir)) mkdirSync(memoryDir, { recursive: true })
136
134
 
137
- cpSync(join(sourceDir, "memory.ts"), join(toolsDir, "memory.ts"))
138
- console.log(" Copied memory.ts to .opencode/tools/")
139
-
140
135
  if (!existsSync(memoryFile)) {
141
- writeFileSync(memoryFile, "version: 1\nentries[0|]{id|category|key|content|file|tags|date}:\n")
136
+ writeFileSync(memoryFile, "version: 1\n[0|]\n")
142
137
  console.log(" Created .opencode/memory/data.toon")
143
138
  }
144
139
  }
package/src/mcp/server.ts CHANGED
@@ -310,22 +310,43 @@ server.registerTool(
310
310
  const date = new Date().toISOString().split("T")[0]
311
311
  const lines = data.split("\n")
312
312
 
313
- let headerIdx = lines.findIndex((l) => l.startsWith("entries["))
313
+ let headerIdx = lines.findIndex((l) => l.startsWith("entries[") || /^\[\d+\|]/.test(l))
314
314
  if (headerIdx === -1) {
315
- lines.push(`entries[0|]{id|category|key|content|file|tags|date}:`)
315
+ lines.push(`[0|]`)
316
316
  headerIdx = lines.length - 1
317
317
  }
318
318
 
319
- const match = lines[headerIdx].match(/entries\[(\d+)\|/)
320
- const count = match ? parseInt(match[1]) : 0
319
+ // Find existing entry with same key (upsert)
320
+ let existingIdx = -1
321
+ for (let i = headerIdx + 1; i < lines.length; i++) {
322
+ const line = lines[i]
323
+ if (!line.startsWith(" ") || !line.includes("|")) continue
324
+ if (line.startsWith(" summaries:")) break
325
+ const parts = line.trim().split("|")
326
+ if (parts[2] === key) {
327
+ existingIdx = i
328
+ break
329
+ }
330
+ }
331
+
321
332
  const newEntry = `${id}|${category}|${key}|${content}|${file || ""}|${tags || ""}|${date}`
333
+ let action = "Guardado"
322
334
 
323
- lines.splice(headerIdx + 1, 0, ` ${newEntry}`)
324
- lines[headerIdx] = lines[headerIdx].replace(/entries\[\d+\|/, `[${count + 1}|`)
335
+ if (existingIdx !== -1) {
336
+ // Update existing entry
337
+ lines[existingIdx] = ` ${newEntry}`
338
+ action = "Actualizado"
339
+ } else {
340
+ // Create new entry
341
+ const match = lines[headerIdx].match(/\[(\d+)\|/)
342
+ const count = match ? parseInt(match[1]) : 0
343
+ lines.splice(headerIdx + 1, 0, ` ${newEntry}`)
344
+ lines[headerIdx] = lines[headerIdx].replace(/\[\d+\|/, `[${count + 1}|`)
345
+ }
325
346
 
326
347
  writeMemory(lines.join("\n"))
327
348
  return {
328
- content: [{ type: "text" as const, text: `🧠 Guardado: ${category}/${key} (${id})\n${content}` }],
349
+ content: [{ type: "text" as const, text: `🧠 ${action}: ${category}/${key} (${id})\n${content}` }],
329
350
  }
330
351
  }
331
352
  )
@@ -355,7 +376,7 @@ server.registerTool(
355
376
  },
356
377
  async ({ query, category, from_date, to_date }) => {
357
378
  const data = readMemory()
358
- const lines = data.split("\n").filter((l) => l.startsWith(" ") && l.includes("|"))
379
+ const lines = data.split("\n").filter((l) => l.startsWith(" ") && l.includes("|") && !l.startsWith("summaries:"))
359
380
  const queryLower = query.toLowerCase()
360
381
 
361
382
  const results = lines
@@ -406,22 +427,22 @@ server.registerTool(
406
427
  async ({ key }) => {
407
428
  const data = readMemory()
408
429
  const lines = data.split("\n")
409
- const headerIdx = lines.findIndex((l) => l.startsWith("entries["))
430
+ const headerIdx = lines.findIndex((l) => l.startsWith("entries[") || /^\[\d+\|]/.test(l))
410
431
 
411
432
  if (headerIdx === -1) {
412
433
  return { content: [{ type: "text" as const, text: "No hay entradas en memoria" }] }
413
434
  }
414
435
 
415
- const entryLines = lines.slice(headerIdx + 1).filter((l) => l.trim().length > 0)
436
+ const entryLines = lines.slice(headerIdx + 1).filter((l) => l.trim().length > 0 && !l.startsWith("summaries:"))
416
437
  const filtered = entryLines.filter((l) => {
417
438
  const parts = l.trim().split("|")
418
439
  return parts[0] !== key && parts[2] !== key
419
440
  })
420
441
 
421
442
  const removed = entryLines.length - filtered.length
422
- const match = lines[headerIdx].match(/entries\[(\d+)\|/)
443
+ const match = lines[headerIdx].match(/\[(\d+)\|/)
423
444
  const count = match ? parseInt(match[1]) : 0
424
- lines[headerIdx] = lines[headerIdx].replace(/entries\[\d+\|/, `[${count - removed}|`)
445
+ lines[headerIdx] = lines[headerIdx].replace(/\[\d+\|/, `[${count - removed}|`)
425
446
  lines.splice(headerIdx + 1, entryLines.length, ...filtered.map((l) => ` ${l.trim()}`))
426
447
 
427
448
  writeMemory(lines.join("\n"))
@@ -449,7 +470,7 @@ server.registerTool(
449
470
  },
450
471
  async () => {
451
472
  const data = readMemory()
452
- const lines = data.split("\n").filter((l) => l.startsWith(" ") && l.includes("|"))
473
+ const lines = data.split("\n").filter((l) => l.startsWith(" ") && l.includes("|") && !l.startsWith(" summaries:"))
453
474
  const entries = lines.map((l) => {
454
475
  const parts = l.trim().split("|")
455
476
  return { category: parts[1] || "unknown" }
@@ -460,7 +481,7 @@ server.registerTool(
460
481
  byCategory[e.category] = (byCategory[e.category] || 0) + 1
461
482
  }
462
483
 
463
- const summaryLines = data.split("\n").filter((l) => l.includes(":") && !l.startsWith(" ") && !l.startsWith("version") && !l.startsWith("entries"))
484
+ const summaryLines = data.split("\n").filter((l) => l.includes(":") && !l.startsWith(" ") && !l.startsWith("version") && !l.startsWith("entries") && !/^\[\d+\|]/.test(l))
464
485
  const stats = [
465
486
  `Entradas totales: ${entries.length}`,
466
487
  `Resúmenes de archivos: ${summaryLines.length}`,