toon-memory 1.6.9 → 1.8.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 +270 -34
- package/dist/cli/setup.js +409 -96
- package/mcp/server.js +161 -20
- package/package.json +9 -1
- package/skills/toon-memory.md +25 -14
- package/src/cli/setup.ts +650 -335
- package/src/mcp/server.ts +198 -15
- package/uninstall.sh +33 -5
package/mcp/server.js
CHANGED
|
@@ -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(), ".
|
|
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) {
|
|
@@ -27696,9 +27697,6 @@ function decrypt(encryptedData, key) {
|
|
|
27696
27697
|
decrypted += decipher.final("utf8");
|
|
27697
27698
|
return decrypted;
|
|
27698
27699
|
}
|
|
27699
|
-
function generateKey() {
|
|
27700
|
-
return randomBytes(32).toString("hex");
|
|
27701
|
-
}
|
|
27702
27700
|
function readMemory() {
|
|
27703
27701
|
ensureMemoryFile();
|
|
27704
27702
|
const config2 = loadConfig();
|
|
@@ -27729,6 +27727,75 @@ function writeMemory(content) {
|
|
|
27729
27727
|
function generateId() {
|
|
27730
27728
|
return randomBytes(4).toString("hex");
|
|
27731
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
|
+
}
|
|
27732
27799
|
function archiveOldEntries() {
|
|
27733
27800
|
const data = readMemory();
|
|
27734
27801
|
const lines = data.split("\n");
|
|
@@ -27745,7 +27812,10 @@ function archiveOldEntries() {
|
|
|
27745
27812
|
const parts = line.trim().split("|");
|
|
27746
27813
|
if (parts.length >= 7) {
|
|
27747
27814
|
const date5 = parts[6];
|
|
27748
|
-
|
|
27815
|
+
const ttl = parts[7] || "";
|
|
27816
|
+
const isOld = date5 < cutoffStr;
|
|
27817
|
+
const isTtlExpired = ttl && isExpired(ttl);
|
|
27818
|
+
if (isOld || isTtlExpired) {
|
|
27749
27819
|
toArchive.push(line.trim());
|
|
27750
27820
|
} else {
|
|
27751
27821
|
toKeep.push(line.trim());
|
|
@@ -27795,10 +27865,11 @@ server.registerTool(
|
|
|
27795
27865
|
key: external_exports.string().describe("T\xEDtulo corto en kebab-case (ej: risk-engine-prioridad)"),
|
|
27796
27866
|
content: external_exports.string().describe("Contenido detallado del hecho"),
|
|
27797
27867
|
file: external_exports.string().optional().default("").describe("Archivo o l\xEDnea relacionada (ej: spec.md:145)"),
|
|
27798
|
-
tags: external_exports.string().optional().default("").describe("Tags separados por punto y coma (ej: risk;spec)")
|
|
27868
|
+
tags: external_exports.string().optional().default("").describe("Tags separados por punto y coma (ej: risk;spec)"),
|
|
27869
|
+
ttl: external_exports.string().optional().default("").describe("Time to live (ej: 7d, 2026-07-17). Vac\xEDo = sin expiraci\xF3n")
|
|
27799
27870
|
}
|
|
27800
27871
|
},
|
|
27801
|
-
async ({ category, key, content, file: file2, tags }) => {
|
|
27872
|
+
async ({ category, key, content, file: file2, tags, ttl }) => {
|
|
27802
27873
|
const data = readMemory();
|
|
27803
27874
|
const newId = generateId();
|
|
27804
27875
|
const date5 = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -27822,8 +27893,11 @@ server.registerTool(
|
|
|
27822
27893
|
}
|
|
27823
27894
|
}
|
|
27824
27895
|
const entryId = existingIdx !== -1 ? existingId : newId;
|
|
27825
|
-
const
|
|
27896
|
+
const resolvedTtl = parseTTL(ttl);
|
|
27897
|
+
const resolvedTags = tags ? tags : inferTags(content, key);
|
|
27898
|
+
const newEntry = `${entryId}|${category}|${key}|${content}|${file2 || ""}|${resolvedTags}|${date5}|${resolvedTtl}`;
|
|
27826
27899
|
let action = "Guardado";
|
|
27900
|
+
const tagsInferred = !tags && resolvedTags ? true : false;
|
|
27827
27901
|
if (existingIdx !== -1) {
|
|
27828
27902
|
lines[existingIdx] = ` ${newEntry}`;
|
|
27829
27903
|
action = "Actualizado";
|
|
@@ -27834,9 +27908,23 @@ server.registerTool(
|
|
|
27834
27908
|
lines[headerIdx] = lines[headerIdx].replace(/\[\d+\|/, `[${count + 1}|`);
|
|
27835
27909
|
}
|
|
27836
27910
|
writeMemory(lines.join("\n"));
|
|
27911
|
+
const headerMatch = lines[headerIdx].match(/\[(\d+)\|/);
|
|
27912
|
+
const entryCount = headerMatch ? parseInt(headerMatch[1]) : 0;
|
|
27913
|
+
let archiveMsg = "";
|
|
27914
|
+
if (entryCount > MAX_ENTRIES) {
|
|
27915
|
+
const result = archiveOldEntries();
|
|
27916
|
+
if (result.archived > 0) {
|
|
27917
|
+
archiveMsg = `
|
|
27918
|
+
\u{1F4E6} Auto-archived ${result.archived} old entries (${result.kept} kept)`;
|
|
27919
|
+
}
|
|
27920
|
+
}
|
|
27921
|
+
const ttlMsg = resolvedTtl ? `
|
|
27922
|
+
\u23F0 TTL: ${resolvedTtl}` : "";
|
|
27923
|
+
const inferredMsg = tagsInferred ? `
|
|
27924
|
+
\u{1F3F7}\uFE0F Tags inferidos: ${resolvedTags}` : "";
|
|
27837
27925
|
return {
|
|
27838
27926
|
content: [{ type: "text", text: `\u{1F9E0} ${action}: ${category}/${key} (${entryId})
|
|
27839
|
-
${content}` }]
|
|
27927
|
+
${content}${ttlMsg}${inferredMsg}${archiveMsg}` }]
|
|
27840
27928
|
};
|
|
27841
27929
|
}
|
|
27842
27930
|
);
|
|
@@ -27861,10 +27949,11 @@ server.registerTool(
|
|
|
27861
27949
|
const trimmed = line.trim();
|
|
27862
27950
|
const parts = trimmed.split("|");
|
|
27863
27951
|
if (parts.length < 7) return null;
|
|
27864
|
-
const [id, cat, key, content, file2, tags, date5] = parts;
|
|
27952
|
+
const [id, cat, key, content, file2, tags, date5, ttl] = parts;
|
|
27865
27953
|
if (category && cat !== category) return null;
|
|
27866
27954
|
if (from_date && date5 < from_date) return null;
|
|
27867
27955
|
if (to_date && date5 > to_date) return null;
|
|
27956
|
+
if (ttl && isExpired(ttl)) return null;
|
|
27868
27957
|
const searchStr = normalize(`${id} ${cat} ${key} ${content} ${file2} ${tags}`);
|
|
27869
27958
|
if (!queryTokens.every((token) => searchStr.includes(token))) return null;
|
|
27870
27959
|
return { id, cat, key, content, file: file2, tags, date: date5 };
|
|
@@ -27922,12 +28011,14 @@ server.registerTool(
|
|
|
27922
28011
|
const lines = data.split("\n").filter((l) => l.startsWith(" ") && l.includes("|") && !l.startsWith(" summaries:"));
|
|
27923
28012
|
const entries = lines.map((l) => {
|
|
27924
28013
|
const parts = l.trim().split("|");
|
|
27925
|
-
return { category: parts[1] || "unknown" };
|
|
28014
|
+
return { category: parts[1] || "unknown", ttl: parts[7] || "" };
|
|
27926
28015
|
});
|
|
27927
28016
|
const byCategory = {};
|
|
27928
28017
|
for (const e of entries) {
|
|
27929
28018
|
byCategory[e.category] = (byCategory[e.category] || 0) + 1;
|
|
27930
28019
|
}
|
|
28020
|
+
const withTtl = entries.filter((e) => e.ttl).length;
|
|
28021
|
+
const expired = entries.filter((e) => e.ttl && isExpired(e.ttl)).length;
|
|
27931
28022
|
const summaryLines = data.split("\n").filter((l) => l.includes(":") && !l.startsWith(" ") && !l.startsWith("version") && !l.startsWith("entries") && !/^\[\d+\|]/.test(l));
|
|
27932
28023
|
const stats = [
|
|
27933
28024
|
`Entradas totales: ${entries.length}`,
|
|
@@ -27936,15 +28027,67 @@ server.registerTool(
|
|
|
27936
28027
|
"Por categor\xEDa:",
|
|
27937
28028
|
...Object.entries(byCategory).map(([k, v]) => ` ${k}: ${v}`),
|
|
27938
28029
|
"",
|
|
28030
|
+
`TTL: ${withTtl} con expiraci\xF3n, ${expired} expiradas`,
|
|
28031
|
+
"",
|
|
27939
28032
|
`\xDAltimas 5 entradas:`,
|
|
27940
28033
|
...lines.slice(-5).map((l) => {
|
|
27941
28034
|
const parts = l.trim().split("|");
|
|
27942
|
-
|
|
28035
|
+
const ttlInfo = parts[7] ? ` | TTL: ${parts[7]}` : "";
|
|
28036
|
+
return ` [${parts[1]}] ${parts[2]} (${parts[0]})${ttlInfo}`;
|
|
27943
28037
|
})
|
|
27944
28038
|
];
|
|
27945
28039
|
return { content: [{ type: "text", text: stats.join("\n") }] };
|
|
27946
28040
|
}
|
|
27947
28041
|
);
|
|
28042
|
+
server.registerTool(
|
|
28043
|
+
"memory_diff",
|
|
28044
|
+
{
|
|
28045
|
+
title: "Memory Diff",
|
|
28046
|
+
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.",
|
|
28047
|
+
inputSchema: {
|
|
28048
|
+
since: external_exports.string().describe("Desde cu\xE1ndo mostrar cambios (ej: 24h, 7d, 2026-07-10)"),
|
|
28049
|
+
type: external_exports.enum(["all", "created", "updated"]).optional().default("all").describe("Filtrar por tipo de cambio")
|
|
28050
|
+
}
|
|
28051
|
+
},
|
|
28052
|
+
async ({ since, type }) => {
|
|
28053
|
+
const sinceDate = parseRelativeDate(since);
|
|
28054
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
28055
|
+
const data = readMemory();
|
|
28056
|
+
const lines = data.split("\n").filter((l) => l.startsWith(" ") && l.includes("|") && !l.startsWith(" summaries:"));
|
|
28057
|
+
const results = lines.map((line) => {
|
|
28058
|
+
const trimmed = line.trim();
|
|
28059
|
+
const parts = trimmed.split("|");
|
|
28060
|
+
if (parts.length < 7) return null;
|
|
28061
|
+
const [id, cat, key, content, file2, tags, date5] = parts;
|
|
28062
|
+
if (date5 < sinceDate) return null;
|
|
28063
|
+
const changeType = date5 === today ? "created" : "updated";
|
|
28064
|
+
if (type !== "all" && changeType !== type) return null;
|
|
28065
|
+
return { id, cat, key, content, file: file2, tags, date: date5, changeType };
|
|
28066
|
+
}).filter(Boolean);
|
|
28067
|
+
if (results.length === 0) {
|
|
28068
|
+
return { content: [{ type: "text", text: `No hay cambios desde ${sinceDate}` }] };
|
|
28069
|
+
}
|
|
28070
|
+
const created = results.filter((r) => r.changeType === "created");
|
|
28071
|
+
const updated = results.filter((r) => r.changeType === "updated");
|
|
28072
|
+
const sections = [`\u{1F4CB} Cambios desde ${sinceDate}:`, ""];
|
|
28073
|
+
if (created.length > 0 && (type === "all" || type === "created")) {
|
|
28074
|
+
sections.push(`\u2795 Nuevas (${created.length}):`);
|
|
28075
|
+
for (const r of created) {
|
|
28076
|
+
sections.push(` [${r.cat}] ${r.key} (${r.id})
|
|
28077
|
+
${r.content}`);
|
|
28078
|
+
}
|
|
28079
|
+
sections.push("");
|
|
28080
|
+
}
|
|
28081
|
+
if (updated.length > 0 && (type === "all" || type === "updated")) {
|
|
28082
|
+
sections.push(`\u270F\uFE0F Actualizadas (${updated.length}):`);
|
|
28083
|
+
for (const r of updated) {
|
|
28084
|
+
sections.push(` [${r.cat}] ${r.key} (${r.id}) \u2014 ${r.date}`);
|
|
28085
|
+
}
|
|
28086
|
+
sections.push("");
|
|
28087
|
+
}
|
|
28088
|
+
return { content: [{ type: "text", text: sections.join("\n") }] };
|
|
28089
|
+
}
|
|
28090
|
+
);
|
|
27948
28091
|
server.registerTool(
|
|
27949
28092
|
"memory_summary",
|
|
27950
28093
|
{
|
|
@@ -28025,18 +28168,16 @@ server.registerTool(
|
|
|
28025
28168
|
if (config2.encrypted) {
|
|
28026
28169
|
return { content: [{ type: "text", text: "La encriptaci\xF3n ya est\xE1 habilitada" }] };
|
|
28027
28170
|
}
|
|
28028
|
-
const key =
|
|
28171
|
+
const key = getKey();
|
|
28172
|
+
if (!key) {
|
|
28173
|
+
return { content: [{ type: "text", text: "\u274C Define TOON_MEMORY_KEY en el entorno antes de encriptar" }] };
|
|
28174
|
+
}
|
|
28029
28175
|
const data = readFileSync(MEMORY_FILE, "utf-8");
|
|
28030
28176
|
const encrypted = encrypt(data, key);
|
|
28031
28177
|
writeFileSync(MEMORY_FILE, encrypted);
|
|
28032
28178
|
saveConfig({ encrypted: true });
|
|
28033
28179
|
return {
|
|
28034
|
-
content: [{
|
|
28035
|
-
type: "text",
|
|
28036
|
-
text: `\u{1F510} Encriptaci\xF3n habilitada
|
|
28037
|
-
\u26A0\uFE0F Guarda esta clave en TOON_MEMORY_KEY (no se puede recuperar):
|
|
28038
|
-
${key}`
|
|
28039
|
-
}]
|
|
28180
|
+
content: [{ type: "text", text: "\u{1F510} Encriptaci\xF3n habilitada" }]
|
|
28040
28181
|
};
|
|
28041
28182
|
}
|
|
28042
28183
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toon-memory",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.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",
|
package/skills/toon-memory.md
CHANGED
|
@@ -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 |
|
|
16
|
-
| Cline | `.cline/mcp.json` | MCP server |
|
|
17
|
-
| Continue | `.continue/config.json` | MCP server |
|
|
18
|
-
|
|
|
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.
|
|
41
|
-
3.
|
|
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
|
|
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 `.
|
|
105
|
+
Data is stored in `.toon-memory/memory/data.toon` using TOON (Token-Oriented Object Notation):
|
|
95
106
|
|
|
96
107
|
```
|
|
97
108
|
version: 1
|