toon-memory 1.7.0 → 2.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/mcp/server.js CHANGED
@@ -16506,8 +16506,8 @@ var require_resolve = /* @__PURE__ */ __commonJSMin(((exports) => {
16506
16506
  }
16507
16507
  return count;
16508
16508
  }
16509
- function getFullPath(resolver, id = "", normalize) {
16510
- if (normalize !== false) id = normalizeId(id);
16509
+ function getFullPath(resolver, id = "", normalize2) {
16510
+ if (normalize2 !== false) id = normalizeId(id);
16511
16511
  return _getFullPath(resolver, resolver.parse(id));
16512
16512
  }
16513
16513
  exports.getFullPath = getFullPath;
@@ -17602,7 +17602,7 @@ var require_schemes = /* @__PURE__ */ __commonJSMin(((exports, module) => {
17602
17602
  var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
17603
17603
  const { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils();
17604
17604
  const { SCHEMES, getSchemeHandler } = require_schemes();
17605
- function normalize(uri, options) {
17605
+ function normalize2(uri, options) {
17606
17606
  if (typeof uri === "string") uri = serialize(parse3(uri, options), options);
17607
17607
  else if (typeof uri === "object") uri = parse3(serialize(uri, options), options);
17608
17608
  return uri;
@@ -17780,7 +17780,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
17780
17780
  }
17781
17781
  const fastUri = {
17782
17782
  SCHEMES,
17783
- normalize,
17783
+ normalize: normalize2,
17784
17784
  resolve,
17785
17785
  resolveComponent,
17786
17786
  equal,
@@ -27644,10 +27644,11 @@ import { dirname, join } from "path";
27644
27644
  import { fileURLToPath } from "url";
27645
27645
  import { randomBytes, createCipheriv, createDecipheriv } from "crypto";
27646
27646
  var __dirname = dirname(fileURLToPath(import.meta.url));
27647
- var MEMORY_DIR = join(process.cwd(), ".opencode", "memory");
27647
+ var MEMORY_DIR = join(process.cwd(), ".toon-memory", "memory");
27648
27648
  var MEMORY_FILE = join(MEMORY_DIR, "data.toon");
27649
27649
  var ARCHIVE_FILE = join(MEMORY_DIR, "archive.toon");
27650
27650
  var CONFIG_FILE = join(MEMORY_DIR, "config.json");
27651
+ var MAX_ENTRIES = 100;
27651
27652
  var ARCHIVE_DAYS = 30;
27652
27653
  var ALGORITHM = "aes-256-gcm";
27653
27654
  function loadConfig() {
@@ -27673,7 +27674,7 @@ function ensureMemoryDir() {
27673
27674
  function ensureMemoryFile() {
27674
27675
  ensureMemoryDir();
27675
27676
  if (!existsSync(MEMORY_FILE)) {
27676
- writeFileSync(MEMORY_FILE, "version: 1\nentries[0|]{id|category|key|content|file|tags|date}:\n");
27677
+ writeFileSync(MEMORY_FILE, "version: 1\nentries[0|]{id|category|key|content|file|tags|date|ttl}:\n");
27677
27678
  }
27678
27679
  }
27679
27680
  function encrypt(text, key) {
@@ -27726,6 +27727,96 @@ function writeMemory(content) {
27726
27727
  function generateId() {
27727
27728
  return randomBytes(4).toString("hex");
27728
27729
  }
27730
+ function parseTTL(ttl) {
27731
+ if (!ttl || !ttl.trim()) return "";
27732
+ const trimmed = ttl.trim();
27733
+ const dayMatch = trimmed.match(/^(\d+)d$/);
27734
+ if (dayMatch) {
27735
+ const days = parseInt(dayMatch[1]);
27736
+ const d = /* @__PURE__ */ new Date();
27737
+ d.setDate(d.getDate() + days);
27738
+ return d.toISOString().split("T")[0];
27739
+ }
27740
+ if (/^\d{4}-\d{2}-\d{2}$/.test(trimmed)) return trimmed;
27741
+ return "";
27742
+ }
27743
+ function isExpired(ttl) {
27744
+ if (!ttl) return false;
27745
+ const ttlDate = parseTTL(ttl) || ttl;
27746
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
27747
+ return ttlDate <= today;
27748
+ }
27749
+ var TAG_VOCABULARY = {
27750
+ "redis": ["redis", "cache", "caching", "memcached"],
27751
+ "auth": ["auth", "authentication", "authorization", "login", "token", "jwt", "session", "oauth"],
27752
+ "api": ["api", "endpoint", "rest", "graphql", "route", "router", "controller"],
27753
+ "db": ["database", "db", "sql", "postgres", "mysql", "mongo", "query", "migration", "schema"],
27754
+ "security": ["security", "encrypt", "decrypt", "vulnerability", "xss", "csrf", "cors", "sanitiz"],
27755
+ "test": ["test", "testing", "vitest", "jest", "spec", "mock", "assert", "coverage"],
27756
+ "deploy": ["deploy", "docker", "ci/cd", "github actions", "pipeline", "kubernetes", "k8s"],
27757
+ "config": ["config", "configuration", "settings", "env", "environment", "dotenv"],
27758
+ "performance": ["performance", "optimize", "benchmark", "latency", "throughput", "cache"],
27759
+ "refactor": ["refactor", "cleanup", "restructure", "reorganize", "rework"],
27760
+ "error": ["error", "exception", "throw", "catch", "handling", "retry", "fallback"],
27761
+ "logging": ["log", "logging", "logger", "debug", "trace", "monitor", "observability"],
27762
+ "types": ["typescript", "types", "type", "interface", "generic", "enum", "zod", "schema"],
27763
+ "async": ["async", "await", "promise", "concurrent", "parallel", "worker", "queue"],
27764
+ "state": ["state", "store", "redux", "context", "reducer", "action", "observable"],
27765
+ "ui": ["ui", "component", "render", "dom", "css", "style", "layout", "responsive"],
27766
+ "storage": ["storage", "file", "filesystem", "s3", "blob", "upload", "download"],
27767
+ "email": ["email", "mail", "smtp", "sendgrid", "newsletter", "notification"],
27768
+ "payment": ["payment", "stripe", "billing", "invoice", "checkout", "subscription"],
27769
+ "webhook": ["webhook", "callback", "event", "listener", "hook"]
27770
+ };
27771
+ function inferTags(content, key) {
27772
+ const text = `${key} ${content}`.toLowerCase();
27773
+ const matched = [];
27774
+ for (const [tag, keywords] of Object.entries(TAG_VOCABULARY)) {
27775
+ if (keywords.some((kw) => text.includes(kw))) {
27776
+ matched.push(tag);
27777
+ }
27778
+ }
27779
+ return matched.join(";");
27780
+ }
27781
+ function parseRelativeDate(since) {
27782
+ const trimmed = since.trim();
27783
+ const hourMatch = trimmed.match(/^(\d+)h$/);
27784
+ if (hourMatch) {
27785
+ const d = /* @__PURE__ */ new Date();
27786
+ d.setHours(d.getHours() - parseInt(hourMatch[1]));
27787
+ return d.toISOString().split("T")[0];
27788
+ }
27789
+ const dayMatch = trimmed.match(/^(\d+)d$/);
27790
+ if (dayMatch) {
27791
+ const d = /* @__PURE__ */ new Date();
27792
+ d.setDate(d.getDate() - parseInt(dayMatch[1]));
27793
+ return d.toISOString().split("T")[0];
27794
+ }
27795
+ if (/^\d{4}-\d{2}-\d{2}$/.test(trimmed)) return trimmed;
27796
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
27797
+ return today;
27798
+ }
27799
+ var normalize = (s) => s.toLowerCase().replace(/[-_]/g, " ").replace(/\s+/g, " ").trim();
27800
+ function findRelatedEntries(text, excludeKey = "", limit = 3) {
27801
+ const data = readMemory();
27802
+ const lines = data.split("\n").filter((l) => l.startsWith(" ") && l.includes("|") && !l.startsWith(" summaries:"));
27803
+ const queryTokens = normalize(text).split(" ").filter(Boolean);
27804
+ const scored = lines.map((line) => {
27805
+ const trimmed = line.trim();
27806
+ const parts = trimmed.split("|");
27807
+ if (parts.length < 7) return null;
27808
+ const [id, cat, key, content, file2, tags, date5] = parts;
27809
+ if (key === excludeKey) return null;
27810
+ const searchStr = normalize(`${id} ${cat} ${key} ${content} ${file2} ${tags}`);
27811
+ let score = 0;
27812
+ for (const token of queryTokens) {
27813
+ if (searchStr.includes(token)) score++;
27814
+ }
27815
+ if (score === 0) return null;
27816
+ return { id, cat, key, content, file: file2, tags, date: date5, score };
27817
+ }).filter(Boolean).sort((a, b) => b.score - a.score).slice(0, limit);
27818
+ return scored;
27819
+ }
27729
27820
  function archiveOldEntries() {
27730
27821
  const data = readMemory();
27731
27822
  const lines = data.split("\n");
@@ -27742,7 +27833,10 @@ function archiveOldEntries() {
27742
27833
  const parts = line.trim().split("|");
27743
27834
  if (parts.length >= 7) {
27744
27835
  const date5 = parts[6];
27745
- if (date5 < cutoffStr) {
27836
+ const ttl = parts[7] || "";
27837
+ const isOld = date5 < cutoffStr;
27838
+ const isTtlExpired = ttl && isExpired(ttl);
27839
+ if (isOld || isTtlExpired) {
27746
27840
  toArchive.push(line.trim());
27747
27841
  } else {
27748
27842
  toKeep.push(line.trim());
@@ -27779,8 +27873,58 @@ archived:
27779
27873
  return { archived: toArchive.length, kept: toKeep.length };
27780
27874
  }
27781
27875
  var server = new McpServer(
27782
- { name: "toon-memory", version: "1.1.0" },
27783
- { capabilities: { tools: { listChanged: true } } }
27876
+ { name: "toon-memory", version: "2.0.0" },
27877
+ { capabilities: { tools: { listChanged: true }, resources: { listChanged: true } } }
27878
+ );
27879
+ server.registerResource(
27880
+ "memory-entries",
27881
+ "toon://memory/entries",
27882
+ { title: "Memory Entries", mimeType: "text/plain" },
27883
+ async (uri) => {
27884
+ const data = readMemory();
27885
+ return { contents: [{ uri: uri.href, text: data }] };
27886
+ }
27887
+ );
27888
+ server.registerResource(
27889
+ "memory-stats",
27890
+ "toon://memory/stats",
27891
+ { title: "Memory Stats", mimeType: "text/plain" },
27892
+ async (uri) => {
27893
+ const data = readMemory();
27894
+ const lines = data.split("\n").filter((l) => l.startsWith(" ") && l.includes("|") && !l.startsWith(" summaries:"));
27895
+ const entries = lines.map((l) => {
27896
+ const parts = l.trim().split("|");
27897
+ return { category: parts[1] || "unknown", ttl: parts[7] || "" };
27898
+ });
27899
+ const byCategory = {};
27900
+ for (const e of entries) {
27901
+ byCategory[e.category] = (byCategory[e.category] || 0) + 1;
27902
+ }
27903
+ const withTtl = entries.filter((e) => e.ttl).length;
27904
+ const expired = entries.filter((e) => e.ttl && isExpired(e.ttl)).length;
27905
+ const text = [
27906
+ `Entradas totales: ${entries.length}`,
27907
+ `TTL: ${withTtl} con expiraci\xF3n, ${expired} expiradas`,
27908
+ "Por categor\xEDa:",
27909
+ ...Object.entries(byCategory).map(([k, v]) => ` ${k}: ${v}`)
27910
+ ].join("\n");
27911
+ return { contents: [{ uri: uri.href, text }] };
27912
+ }
27913
+ );
27914
+ server.registerResource(
27915
+ "memory-summaries",
27916
+ "toon://memory/summaries",
27917
+ { title: "Memory Summaries", mimeType: "text/plain" },
27918
+ async (uri) => {
27919
+ const data = readMemory();
27920
+ const lines = data.split("\n");
27921
+ const summaryIdx = lines.findIndex((l) => l.trim().startsWith("summaries:"));
27922
+ if (summaryIdx === -1) {
27923
+ return { contents: [{ uri: uri.href, text: "No hay res\xFAmenes guardados" }] };
27924
+ }
27925
+ const summaryText = lines.slice(summaryIdx + 1).filter((l) => l.includes(":")).join("\n");
27926
+ return { contents: [{ uri: uri.href, text: summaryText || "No hay res\xFAmenes guardados" }] };
27927
+ }
27784
27928
  );
27785
27929
  server.registerTool(
27786
27930
  "memory_remember",
@@ -27792,10 +27936,11 @@ server.registerTool(
27792
27936
  key: external_exports.string().describe("T\xEDtulo corto en kebab-case (ej: risk-engine-prioridad)"),
27793
27937
  content: external_exports.string().describe("Contenido detallado del hecho"),
27794
27938
  file: external_exports.string().optional().default("").describe("Archivo o l\xEDnea relacionada (ej: spec.md:145)"),
27795
- tags: external_exports.string().optional().default("").describe("Tags separados por punto y coma (ej: risk;spec)")
27939
+ tags: external_exports.string().optional().default("").describe("Tags separados por punto y coma (ej: risk;spec)"),
27940
+ ttl: external_exports.string().optional().default("").describe("Time to live (ej: 7d, 2026-07-17). Vac\xEDo = sin expiraci\xF3n")
27796
27941
  }
27797
27942
  },
27798
- async ({ category, key, content, file: file2, tags }) => {
27943
+ async ({ category, key, content, file: file2, tags, ttl }) => {
27799
27944
  const data = readMemory();
27800
27945
  const newId = generateId();
27801
27946
  const date5 = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -27819,8 +27964,11 @@ server.registerTool(
27819
27964
  }
27820
27965
  }
27821
27966
  const entryId = existingIdx !== -1 ? existingId : newId;
27822
- const newEntry = `${entryId}|${category}|${key}|${content}|${file2 || ""}|${tags || ""}|${date5}`;
27967
+ const resolvedTtl = parseTTL(ttl);
27968
+ const resolvedTags = tags ? tags : inferTags(content, key);
27969
+ const newEntry = `${entryId}|${category}|${key}|${content}|${file2 || ""}|${resolvedTags}|${date5}|${resolvedTtl}`;
27823
27970
  let action = "Guardado";
27971
+ const tagsInferred = !tags && resolvedTags ? true : false;
27824
27972
  if (existingIdx !== -1) {
27825
27973
  lines[existingIdx] = ` ${newEntry}`;
27826
27974
  action = "Actualizado";
@@ -27831,9 +27979,32 @@ server.registerTool(
27831
27979
  lines[headerIdx] = lines[headerIdx].replace(/\[\d+\|/, `[${count + 1}|`);
27832
27980
  }
27833
27981
  writeMemory(lines.join("\n"));
27982
+ const headerMatch = lines[headerIdx].match(/\[(\d+)\|/);
27983
+ const entryCount = headerMatch ? parseInt(headerMatch[1]) : 0;
27984
+ let archiveMsg = "";
27985
+ if (entryCount > MAX_ENTRIES) {
27986
+ const result = archiveOldEntries();
27987
+ if (result.archived > 0) {
27988
+ archiveMsg = `
27989
+ \u{1F4E6} Auto-archived ${result.archived} old entries (${result.kept} kept)`;
27990
+ }
27991
+ }
27992
+ const ttlMsg = resolvedTtl ? `
27993
+ \u23F0 TTL: ${resolvedTtl}` : "";
27994
+ const inferredMsg = tagsInferred ? `
27995
+ \u{1F3F7}\uFE0F Tags inferidos: ${resolvedTags}` : "";
27996
+ const related = findRelatedEntries(`${key} ${content} ${resolvedTags}`, key, 3);
27997
+ let relatedMsg = "";
27998
+ if (related.length > 0) {
27999
+ const items = related.map((r) => ` [${r.cat}] ${r.key} \u2014 ${r.content.slice(0, 80)}`).join("\n");
28000
+ relatedMsg = `
28001
+
28002
+ \u{1F517} Entradas relacionadas:
28003
+ ${items}`;
28004
+ }
27834
28005
  return {
27835
28006
  content: [{ type: "text", text: `\u{1F9E0} ${action}: ${category}/${key} (${entryId})
27836
- ${content}` }]
28007
+ ${content}${ttlMsg}${inferredMsg}${archiveMsg}${relatedMsg}` }]
27837
28008
  };
27838
28009
  }
27839
28010
  );
@@ -27852,17 +28023,18 @@ server.registerTool(
27852
28023
  async ({ query, category, from_date, to_date }) => {
27853
28024
  const data = readMemory();
27854
28025
  const lines = data.split("\n").filter((l) => l.startsWith(" ") && l.includes("|") && !l.startsWith("summaries:"));
27855
- const normalize = (s) => s.toLowerCase().replace(/[-_]/g, " ").replace(/\s+/g, " ").trim();
27856
- const queryTokens = normalize(query).split(" ").filter(Boolean);
28026
+ const normalize2 = (s) => s.toLowerCase().replace(/[-_]/g, " ").replace(/\s+/g, " ").trim();
28027
+ const queryTokens = normalize2(query).split(" ").filter(Boolean);
27857
28028
  const results = lines.map((line) => {
27858
28029
  const trimmed = line.trim();
27859
28030
  const parts = trimmed.split("|");
27860
28031
  if (parts.length < 7) return null;
27861
- const [id, cat, key, content, file2, tags, date5] = parts;
28032
+ const [id, cat, key, content, file2, tags, date5, ttl] = parts;
27862
28033
  if (category && cat !== category) return null;
27863
28034
  if (from_date && date5 < from_date) return null;
27864
28035
  if (to_date && date5 > to_date) return null;
27865
- const searchStr = normalize(`${id} ${cat} ${key} ${content} ${file2} ${tags}`);
28036
+ if (ttl && isExpired(ttl)) return null;
28037
+ const searchStr = normalize2(`${id} ${cat} ${key} ${content} ${file2} ${tags}`);
27866
28038
  if (!queryTokens.every((token) => searchStr.includes(token))) return null;
27867
28039
  return { id, cat, key, content, file: file2, tags, date: date5 };
27868
28040
  }).filter(Boolean);
@@ -27919,12 +28091,14 @@ server.registerTool(
27919
28091
  const lines = data.split("\n").filter((l) => l.startsWith(" ") && l.includes("|") && !l.startsWith(" summaries:"));
27920
28092
  const entries = lines.map((l) => {
27921
28093
  const parts = l.trim().split("|");
27922
- return { category: parts[1] || "unknown" };
28094
+ return { category: parts[1] || "unknown", ttl: parts[7] || "" };
27923
28095
  });
27924
28096
  const byCategory = {};
27925
28097
  for (const e of entries) {
27926
28098
  byCategory[e.category] = (byCategory[e.category] || 0) + 1;
27927
28099
  }
28100
+ const withTtl = entries.filter((e) => e.ttl).length;
28101
+ const expired = entries.filter((e) => e.ttl && isExpired(e.ttl)).length;
27928
28102
  const summaryLines = data.split("\n").filter((l) => l.includes(":") && !l.startsWith(" ") && !l.startsWith("version") && !l.startsWith("entries") && !/^\[\d+\|]/.test(l));
27929
28103
  const stats = [
27930
28104
  `Entradas totales: ${entries.length}`,
@@ -27933,15 +28107,90 @@ server.registerTool(
27933
28107
  "Por categor\xEDa:",
27934
28108
  ...Object.entries(byCategory).map(([k, v]) => ` ${k}: ${v}`),
27935
28109
  "",
28110
+ `TTL: ${withTtl} con expiraci\xF3n, ${expired} expiradas`,
28111
+ "",
27936
28112
  `\xDAltimas 5 entradas:`,
27937
28113
  ...lines.slice(-5).map((l) => {
27938
28114
  const parts = l.trim().split("|");
27939
- return ` [${parts[1]}] ${parts[2]} (${parts[0]})`;
28115
+ const ttlInfo = parts[7] ? ` | TTL: ${parts[7]}` : "";
28116
+ return ` [${parts[1]}] ${parts[2]} (${parts[0]})${ttlInfo}`;
27940
28117
  })
27941
28118
  ];
27942
28119
  return { content: [{ type: "text", text: stats.join("\n") }] };
27943
28120
  }
27944
28121
  );
28122
+ server.registerTool(
28123
+ "memory_diff",
28124
+ {
28125
+ title: "Memory Diff",
28126
+ description: "Muestra qu\xE9 cambi\xF3 en la memoria desde una fecha. \xDAtil para saber qu\xE9 se aprendi\xF3 desde la \xFAltima sesi\xF3n.",
28127
+ inputSchema: {
28128
+ since: external_exports.string().describe("Desde cu\xE1ndo mostrar cambios (ej: 24h, 7d, 2026-07-10)"),
28129
+ type: external_exports.enum(["all", "created", "updated"]).optional().default("all").describe("Filtrar por tipo de cambio")
28130
+ }
28131
+ },
28132
+ async ({ since, type }) => {
28133
+ const sinceDate = parseRelativeDate(since);
28134
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
28135
+ const data = readMemory();
28136
+ const lines = data.split("\n").filter((l) => l.startsWith(" ") && l.includes("|") && !l.startsWith(" summaries:"));
28137
+ const results = lines.map((line) => {
28138
+ const trimmed = line.trim();
28139
+ const parts = trimmed.split("|");
28140
+ if (parts.length < 7) return null;
28141
+ const [id, cat, key, content, file2, tags, date5] = parts;
28142
+ if (date5 < sinceDate) return null;
28143
+ const changeType = date5 === today ? "created" : "updated";
28144
+ if (type !== "all" && changeType !== type) return null;
28145
+ return { id, cat, key, content, file: file2, tags, date: date5, changeType };
28146
+ }).filter(Boolean);
28147
+ if (results.length === 0) {
28148
+ return { content: [{ type: "text", text: `No hay cambios desde ${sinceDate}` }] };
28149
+ }
28150
+ const created = results.filter((r) => r.changeType === "created");
28151
+ const updated = results.filter((r) => r.changeType === "updated");
28152
+ const sections = [`\u{1F4CB} Cambios desde ${sinceDate}:`, ""];
28153
+ if (created.length > 0 && (type === "all" || type === "created")) {
28154
+ sections.push(`\u2795 Nuevas (${created.length}):`);
28155
+ for (const r of created) {
28156
+ sections.push(` [${r.cat}] ${r.key} (${r.id})
28157
+ ${r.content}`);
28158
+ }
28159
+ sections.push("");
28160
+ }
28161
+ if (updated.length > 0 && (type === "all" || type === "updated")) {
28162
+ sections.push(`\u270F\uFE0F Actualizadas (${updated.length}):`);
28163
+ for (const r of updated) {
28164
+ sections.push(` [${r.cat}] ${r.key} (${r.id}) \u2014 ${r.date}`);
28165
+ }
28166
+ sections.push("");
28167
+ }
28168
+ return { content: [{ type: "text", text: sections.join("\n") }] };
28169
+ }
28170
+ );
28171
+ server.registerTool(
28172
+ "memory_suggest",
28173
+ {
28174
+ title: "Suggest Related Memories",
28175
+ description: "Sugiere entradas de memoria relacionadas con un contexto dado. \xDAtil para obtener contexto antes de una tarea.",
28176
+ inputSchema: {
28177
+ context: external_exports.string().describe("Texto o contexto para buscar sugerencias"),
28178
+ limit: external_exports.number().optional().default(5).describe("M\xE1ximo de sugerencias")
28179
+ }
28180
+ },
28181
+ async ({ context, limit }) => {
28182
+ const related = findRelatedEntries(context, "", limit);
28183
+ if (related.length === 0) {
28184
+ return { content: [{ type: "text", text: `No se encontraron entradas relacionadas con "${context}"` }] };
28185
+ }
28186
+ const formatted = related.map((r) => `[${r.cat}] ${r.key} (${r.id})
28187
+ ${r.content}
28188
+ File: ${r.file} | Tags: ${r.tags} | Date: ${r.date}`).join("\n\n");
28189
+ return { content: [{ type: "text", text: `\u{1F50D} Sugerencias para "${context}":
28190
+
28191
+ ${formatted}` }] };
28192
+ }
28193
+ );
27945
28194
  server.registerTool(
27946
28195
  "memory_summary",
27947
28196
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toon-memory",
3
- "version": "1.7.0",
3
+ "version": "2.0.0",
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": {
@@ -45,6 +45,14 @@
45
45
  "windsurf",
46
46
  "cline",
47
47
  "continue",
48
+ "codex",
49
+ "gemini",
50
+ "zed",
51
+ "antigravity",
52
+ "aider",
53
+ "kilocode",
54
+ "openclaw",
55
+ "kiro",
48
56
  "ai-agent",
49
57
  "memory",
50
58
  "toon",
@@ -6,16 +6,23 @@ A persistent memory system for AI coding agents. It remembers decisions, pattern
6
6
 
7
7
  ## Supported Agents
8
8
 
9
- | Agent | Config File | Format |
10
- |-------|-------------|--------|
11
- | OpenCode | `.opencode/opencode.json` | MCP server |
12
- | VS Code / Copilot | `.vscode/mcp.json` | MCP server |
13
- | Claude | `.claude/settings.json` | MCP server |
14
- | Cursor | `.cursor/mcp.json` | MCP server |
15
- | Windsurf | `.windsurfrules` | MCP server |
16
- | Cline | `.cline/mcp.json` | MCP server |
17
- | Continue | `.continue/config.json` | MCP server |
18
- | Aider | `.aider.conf.yml` | Manual config |
9
+ | Agent | Config File | Format | Hooks |
10
+ |-------|-------------|--------|-------|
11
+ | OpenCode | `.opencode/opencode.json` | MCP server | — |
12
+ | VS Code / Copilot | `.vscode/mcp.json` | MCP server | — |
13
+ | Claude Code | `.claude/settings.json` | MCP server | SessionStart |
14
+ | Cursor | `.cursor/mcp.json` | MCP server | — |
15
+ | Windsurf | `~/.codeium/windsurf/mcp_config.json` | MCP server | — |
16
+ | Cline | `.cline/mcp.json` | MCP server | — |
17
+ | Continue | `.continue/config.json` | MCP server | — |
18
+ | Codex CLI | `.codex/config.toml` | TOML | SessionStart |
19
+ | Gemini CLI | `.gemini/settings.json` | MCP server | SessionStart |
20
+ | Zed | `~/.config/zed/settings.json` | JSONC | — |
21
+ | Antigravity | `.gemini/config/mcp_config.json` | MCP server | SessionStart |
22
+ | Aider | — | Instructions only | — |
23
+ | KiloCode | `~/.kilocode/mcp_settings.json` | MCP server | — |
24
+ | OpenClaw | `.openclaw.json` | MCP server | — |
25
+ | Kiro | `.kiro/settings/mcp.json` | MCP server | — |
19
26
 
20
27
  ## Tools
21
28
 
@@ -26,6 +33,9 @@ A persistent memory system for AI coding agents. It remembers decisions, pattern
26
33
  | `memory_forget` | Remove an entry by key or id |
27
34
  | `memory_stats` | View memory state |
28
35
  | `memory_summary` | Save/retrieve file summaries |
36
+ | `memory_archive` | Archive old entries |
37
+ | `memory_encrypt` | Enable encryption |
38
+ | `memory_decrypt` | Disable encryption |
29
39
 
30
40
  ## Installation
31
41
 
@@ -37,8 +47,9 @@ npx toon-memory
37
47
 
38
48
  This will:
39
49
  1. Detect installed agents
40
- 2. Ask if you want local or global installation
41
- 3. Configure the MCP server automatically
50
+ 2. Let you select which agents to configure
51
+ 3. Ask for local or global installation
52
+ 4. Configure MCP server, instructions, and hooks automatically
42
53
 
43
54
  ### Manual installation
44
55
 
@@ -71,7 +82,7 @@ Add to your agent's MCP config:
71
82
  ### When exploring the project
72
83
 
73
84
  1. First: `memory_recall(query="what you need to know")`
74
- 2. If no result: use CodeGraph or grep
85
+ 2. If no result: use grep or search tools
75
86
  3. Only if both fail: read files directly
76
87
 
77
88
  ### At the END of every session
@@ -91,7 +102,7 @@ Add to your agent's MCP config:
91
102
 
92
103
  ## File format
93
104
 
94
- Data is stored in `.opencode/memory/data.toon` using TOON (Token-Oriented Object Notation):
105
+ Data is stored in `.toon-memory/memory/data.toon` using TOON (Token-Oriented Object Notation):
95
106
 
96
107
  ```
97
108
  version: 1