aidimag 1.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.
Files changed (91) hide show
  1. package/LICENSE +102 -0
  2. package/README.md +113 -0
  3. package/dist/capture/bootstrap.d.ts +25 -0
  4. package/dist/capture/bootstrap.js +188 -0
  5. package/dist/capture/commit-miner.d.ts +59 -0
  6. package/dist/capture/commit-miner.js +381 -0
  7. package/dist/capture/harvest.d.ts +50 -0
  8. package/dist/capture/harvest.js +207 -0
  9. package/dist/capture/pr-miner.d.ts +57 -0
  10. package/dist/capture/pr-miner.js +185 -0
  11. package/dist/capture/session-briefing.d.ts +36 -0
  12. package/dist/capture/session-briefing.js +150 -0
  13. package/dist/capture/session-extraction.d.ts +23 -0
  14. package/dist/capture/session-extraction.js +54 -0
  15. package/dist/capture/triage.d.ts +30 -0
  16. package/dist/capture/triage.js +108 -0
  17. package/dist/cli/commands/capture.d.ts +5 -0
  18. package/dist/cli/commands/capture.js +357 -0
  19. package/dist/cli/commands/hosts.d.ts +5 -0
  20. package/dist/cli/commands/hosts.js +98 -0
  21. package/dist/cli/commands/knowledge.d.ts +5 -0
  22. package/dist/cli/commands/knowledge.js +121 -0
  23. package/dist/cli/commands/memory.d.ts +6 -0
  24. package/dist/cli/commands/memory.js +392 -0
  25. package/dist/cli/commands/sync.d.ts +5 -0
  26. package/dist/cli/commands/sync.js +307 -0
  27. package/dist/cli/commands/tickets.d.ts +6 -0
  28. package/dist/cli/commands/tickets.js +328 -0
  29. package/dist/cli/commands/verify.d.ts +5 -0
  30. package/dist/cli/commands/verify.js +136 -0
  31. package/dist/cli/index.d.ts +19 -0
  32. package/dist/cli/index.js +46 -0
  33. package/dist/cli/shared.d.ts +37 -0
  34. package/dist/cli/shared.js +175 -0
  35. package/dist/config.d.ts +55 -0
  36. package/dist/config.js +51 -0
  37. package/dist/context/generate.d.ts +24 -0
  38. package/dist/context/generate.js +115 -0
  39. package/dist/critique/critique.d.ts +40 -0
  40. package/dist/critique/critique.js +110 -0
  41. package/dist/db/schema.d.ts +28 -0
  42. package/dist/db/schema.js +269 -0
  43. package/dist/db/store.d.ts +182 -0
  44. package/dist/db/store.js +906 -0
  45. package/dist/debug.d.ts +14 -0
  46. package/dist/debug.js +25 -0
  47. package/dist/embeddings/provider.d.ts +16 -0
  48. package/dist/embeddings/provider.js +100 -0
  49. package/dist/embeddings/search.d.ts +22 -0
  50. package/dist/embeddings/search.js +95 -0
  51. package/dist/index.d.ts +11 -0
  52. package/dist/index.js +12 -0
  53. package/dist/knowledge/chunk.d.ts +9 -0
  54. package/dist/knowledge/chunk.js +78 -0
  55. package/dist/knowledge/extract.d.ts +34 -0
  56. package/dist/knowledge/extract.js +113 -0
  57. package/dist/knowledge/ingest.d.ts +79 -0
  58. package/dist/knowledge/ingest.js +305 -0
  59. package/dist/knowledge/llm.d.ts +21 -0
  60. package/dist/knowledge/llm.js +110 -0
  61. package/dist/mcp/server.d.ts +11 -0
  62. package/dist/mcp/server.js +532 -0
  63. package/dist/security/evidence.d.ts +11 -0
  64. package/dist/security/evidence.js +15 -0
  65. package/dist/security/seal.d.ts +3 -0
  66. package/dist/security/seal.js +28 -0
  67. package/dist/security/sync-push.d.ts +2 -0
  68. package/dist/security/sync-push.js +37 -0
  69. package/dist/security/url.d.ts +6 -0
  70. package/dist/security/url.js +67 -0
  71. package/dist/sync/client.d.ts +84 -0
  72. package/dist/sync/client.js +391 -0
  73. package/dist/sync/server.d.ts +75 -0
  74. package/dist/sync/server.js +659 -0
  75. package/dist/tickets/provider.d.ts +80 -0
  76. package/dist/tickets/provider.js +375 -0
  77. package/dist/types.d.ts +133 -0
  78. package/dist/types.js +5 -0
  79. package/dist/ui/page.d.ts +5 -0
  80. package/dist/ui/page.js +841 -0
  81. package/dist/ui/server.d.ts +10 -0
  82. package/dist/ui/server.js +437 -0
  83. package/dist/verify/check.d.ts +38 -0
  84. package/dist/verify/check.js +121 -0
  85. package/dist/verify/engine.d.ts +39 -0
  86. package/dist/verify/engine.js +178 -0
  87. package/dist/verify/hooks.d.ts +24 -0
  88. package/dist/verify/hooks.js +125 -0
  89. package/dist/verify/runners.d.ts +26 -0
  90. package/dist/verify/runners.js +193 -0
  91. package/package.json +78 -0
@@ -0,0 +1,392 @@
1
+ /**
2
+ * Core memory commands: init, remember, recall, reindex, status, log, gaps,
3
+ * refute, pin, unpin, forget.
4
+ */
5
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, appendFileSync } from "node:fs";
6
+ import path from "node:path";
7
+ import { MemoryStore, findRepoRoot, dbPathFor, AIDIMAG_DIR } from "../../db/store.js";
8
+ import { installGitHooks } from "../../verify/hooks.js";
9
+ import { hybridSearch, indexMemory, reindexAll } from "../../embeddings/search.js";
10
+ import { resolveKnowledgeConfig } from "../../config.js";
11
+ import { KINDS, GUARDRAIL_LEVELS, fail, autoSync, printMemory } from "../shared.js";
12
+ import { debugLog } from "../../debug.js";
13
+ export function registerMemoryCommands(program) {
14
+ program
15
+ .command("init")
16
+ .description("Initialize aidimag in the current repo")
17
+ .action(async () => {
18
+ const root = findRepoRoot() ?? process.cwd();
19
+ const dir = path.join(root, AIDIMAG_DIR);
20
+ const fresh = !existsSync(dbPathFor(root));
21
+ mkdirSync(dir, { recursive: true });
22
+ const store = new MemoryStore(dbPathFor(root));
23
+ store.close();
24
+ // keep the DB out of git by default (team-sync mode comes later)
25
+ const gitignore = path.join(dir, ".gitignore");
26
+ if (!existsSync(gitignore)) {
27
+ writeFileSync(gitignore, "memory.db\nmemory.db-wal\nmemory.db-shm\nknowledge/\n");
28
+ }
29
+ else if (!readFileSync(gitignore, "utf8").includes("knowledge/")) {
30
+ appendFileSync(gitignore, "knowledge/\n");
31
+ }
32
+ // knowledge inbox: a drop folder for project docs (summaries/backups live in .aidimag/)
33
+ const knowledgeInbox = path.join(root, resolveKnowledgeConfig(root).folder);
34
+ mkdirSync(knowledgeInbox, { recursive: true });
35
+ const gitkeep = path.join(knowledgeInbox, ".gitkeep");
36
+ if (!existsSync(gitkeep)) {
37
+ writeFileSync(gitkeep, "# Drop project docs here (design docs, ADRs, style guides, runbooks).\n" +
38
+ "# aidimag summarizes them into reviewed, pinned memories — see `dim knowledge`.\n");
39
+ }
40
+ // create .cursorrules for automatic MCP integration
41
+ const cursorrules = path.join(root, ".cursorrules");
42
+ if (!existsSync(cursorrules)) {
43
+ writeFileSync(cursorrules, `# Project Memory Integration
44
+
45
+ At the start of EVERY new chat session, you MUST:
46
+ 1. Read the \`aidimag://session-briefing\` resource to load project memory, conventions, and guardrails
47
+ 2. Review all GUARDRAILS before making any code changes
48
+ 3. Search project memory using \`memory_search\` when working on specific features
49
+
50
+ Before making any changes to code:
51
+ - Check if there are relevant memories using \`memory_search\`
52
+ - Respect all GUARDRAIL rules (ALWAYS = block, ASK-FIRST = confirm, NEVER = refuse)
53
+ - Use \`context_note\` to capture any new conventions or decisions the user mentions
54
+
55
+ This project uses aiDimag for persistent memory. Always consult memory before proceeding.
56
+ `);
57
+ console.log(`Created ${cursorrules} (tells Cursor/Claude to auto-load memory)`);
58
+ }
59
+ // suggest MCP wiring
60
+ console.log(fresh ? `Initialized aidimag in ${dir}` : `aidimag already initialized in ${dir}`);
61
+ const hooks = installGitHooks(root);
62
+ if (hooks.installed.length) {
63
+ console.log(`Installed git hooks: ${hooks.installed.join(", ")} (re-verify on pull/checkout)`);
64
+ }
65
+ else if (hooks.alreadyPresent.length) {
66
+ console.log(`Git hooks already installed: ${hooks.alreadyPresent.join(", ")}`);
67
+ }
68
+ console.log(`\nAdd the MCP server to your agent config, e.g. for Claude Code (.mcp.json):`);
69
+ console.log(JSON.stringify({ mcpServers: { aidimag: { command: "npx", args: ["-y", "aidimag", "mcp"], env: { AIDIMAG_REPO: root } } } }, null, 2));
70
+ // append .aidimag DB files to repo .gitignore if a git repo
71
+ const rootIgnore = path.join(root, ".gitignore");
72
+ if (existsSync(path.join(root, ".git"))) {
73
+ const current = existsSync(rootIgnore) ? readFileSync(rootIgnore, "utf8") : "";
74
+ const folder = resolveKnowledgeConfig(root).folder;
75
+ const additions = [];
76
+ if (!current.includes(".aidimag/memory.db"))
77
+ additions.push(".aidimag/memory.db*");
78
+ // keep dropped knowledge docs (may contain secrets) out of git, but track the folder
79
+ if (!current.includes(`${folder}/*`))
80
+ additions.push(`${folder}/*`, `!${folder}/.gitkeep`);
81
+ // generated context files (users can commit them if they want, but default is gitignored)
82
+ if (!current.includes("CLAUDE.md"))
83
+ additions.push("CLAUDE.md");
84
+ if (!current.includes(".cursorrules"))
85
+ additions.push(".cursorrules");
86
+ if (!current.includes(".windsurfrules"))
87
+ additions.push(".windsurfrules");
88
+ if (!current.includes("AGENTS.md"))
89
+ additions.push("AGENTS.md");
90
+ if (additions.length) {
91
+ appendFileSync(rootIgnore, `${current.endsWith("\n") || current === "" ? "" : "\n"}${additions.join("\n")}\n`);
92
+ console.log(`\nUpdated ${rootIgnore} (ignored memory.db + ${folder}/ drops + generated context files)`);
93
+ }
94
+ }
95
+ console.log(`\nNext: \`dim bootstrap\` gives this repo an instant starter brain (surveys docs/structure/history, queues reviewable memories).`);
96
+ // Ask if user wants to generate context files for non-MCP tools
97
+ const { createPrompter } = await import("../shared.js");
98
+ const prompter = await createPrompter();
99
+ console.log(`\n📝 Generate context files for non-MCP AI tools?`);
100
+ console.log(` This creates CLAUDE.md, .cursorrules, .windsurfrules, AGENTS.md, and copilot-instructions.md`);
101
+ console.log(` (useful for Copilot, Cursor, Windsurf, and other tools that read static files)`);
102
+ console.log(` Options: y (all) | n (skip) | claude | cursorrules | copilot | windsurfrules | agents\n`);
103
+ const choice = (await prompter.ask("Generate now? ")).trim().toLowerCase();
104
+ prompter.close();
105
+ if (choice === "y" || choice === "all" || ["claude", "cursorrules", "copilot", "windsurfrules", "agents"].includes(choice)) {
106
+ const { generateContext } = await import("../../context/generate.js");
107
+ const { writeConfig } = await import("../../config.js");
108
+ const format = choice === "y" || choice === "all" ? "all" : choice;
109
+ const store = MemoryStore.open(root);
110
+ const r = generateContext(store, root, format);
111
+ store.close();
112
+ writeConfig(root, { generateContext: { auto: true, format: format } });
113
+ console.log(`\n✅ Generated ${r.files.join(", ")} with auto-regeneration enabled.`);
114
+ if (r.total === 0) {
115
+ console.log(` (no memories yet — files will populate after \`dim bootstrap\` or \`dim remember\`)`);
116
+ }
117
+ }
118
+ else {
119
+ console.log(`\n Skipped. Run \`dim generate-context --auto --format all\` later if needed.`);
120
+ }
121
+ });
122
+ program
123
+ .command("remember")
124
+ .description("Store a memory (write the claim as a falsifiable statement)")
125
+ .argument("<claim>", "The claim to remember")
126
+ .option("-k, --kind <kind>", `Memory kind: ${KINDS.join("|")}`, "GOTCHA")
127
+ .option("-p, --path <paths...>", "Paths this memory applies to")
128
+ .option("-s, --symbol <symbols...>", "Symbols this memory applies to")
129
+ .option("-e, --evidence <spec...>", "Evidence as TYPE:payload, e.g. COMMIT_REF:abc123 or STATIC_CHECK:'grep ...'")
130
+ .option("-g, --guardrail-level <level>", `For kind=GUARDRAIL: ${GUARDRAIL_LEVELS.join("|")}`)
131
+ .option("--pin", "Pin the memory: it never decays with age (evidence failure can still mark it stale)")
132
+ .action(async (claim, opts) => {
133
+ const kind = String(opts.kind).toUpperCase();
134
+ if (!KINDS.includes(kind))
135
+ fail(`invalid kind '${opts.kind}'. Use one of: ${KINDS.join(", ")}`);
136
+ let guardrailLevel;
137
+ if (kind === "GUARDRAIL") {
138
+ guardrailLevel = (opts.guardrailLevel ?? "ask-first");
139
+ if (!GUARDRAIL_LEVELS.includes(guardrailLevel)) {
140
+ fail(`invalid --guardrail-level '${opts.guardrailLevel}'. Use one of: ${GUARDRAIL_LEVELS.join(", ")}`);
141
+ }
142
+ }
143
+ else if (opts.guardrailLevel) {
144
+ fail("--guardrail-level only applies to --kind GUARDRAIL");
145
+ }
146
+ const evidence = opts.evidence?.map((spec) => {
147
+ const idx = spec.indexOf(":");
148
+ if (idx < 1)
149
+ fail(`invalid evidence '${spec}'. Format: TYPE:payload`);
150
+ const type = spec.slice(0, idx).toUpperCase();
151
+ return { type, payload: spec.slice(idx + 1) };
152
+ });
153
+ const store = MemoryStore.open(process.cwd(), { create: true });
154
+ const entry = store.write({
155
+ kind, claim, paths: opts.path, symbols: opts.symbol, evidence,
156
+ createdBy: "human", pinned: Boolean(opts.pin), guardrailLevel,
157
+ trustExecutableEvidence: true,
158
+ });
159
+ console.log("🧠 Got it — I'll remember:");
160
+ printMemory(entry, true);
161
+ if (!evidence?.length) {
162
+ console.log(`\nTip: claims with evidence re-verify themselves as the code evolves —\n` +
163
+ ` e.g. -e "STATIC_CHECK:grep -q something src/file.ts"`);
164
+ }
165
+ await indexMemory(store, entry).catch(() => false);
166
+ await autoSync(store);
167
+ store.close();
168
+ });
169
+ program
170
+ .command("recall")
171
+ .description("Search memories — hybrid keyword + semantic when embeddings are configured")
172
+ .argument("[query...]", "Keywords to search")
173
+ .option("-p, --path <paths...>", "Restrict to memories scoped to these paths")
174
+ .option("-k, --kind <kind>", "Filter by kind")
175
+ .option("-n, --limit <n>", "Max results", "10")
176
+ .option("--all", "Include refuted memories")
177
+ .action(async (query, opts) => {
178
+ const store = MemoryStore.open();
179
+ const { results, semantic } = await hybridSearch(store, {
180
+ query: query.join(" "),
181
+ paths: opts.path,
182
+ kind: opts.kind ? String(opts.kind).toUpperCase() : undefined,
183
+ limit: parseInt(opts.limit, 10),
184
+ includeRefuted: Boolean(opts.all),
185
+ });
186
+ if (query.length) {
187
+ try {
188
+ store.logSearch(query.join(" "), opts.path ?? [], results.length, "cli");
189
+ }
190
+ catch (err) {
191
+ // gap logging is best-effort; never break recall
192
+ debugLog("cli search-gap logging", err);
193
+ }
194
+ }
195
+ if (results.length === 0)
196
+ console.log("No matching memories.");
197
+ for (const m of results)
198
+ printMemory(m, true);
199
+ if (query.length && !semantic) {
200
+ console.log("\n(keyword search only — set up Ollama or OPENAI_API_KEY for semantic recall, then `dim reindex`)");
201
+ }
202
+ store.close();
203
+ });
204
+ program
205
+ .command("reindex")
206
+ .description("Build/refresh semantic embeddings for all memories")
207
+ .action(async () => {
208
+ const store = MemoryStore.open();
209
+ if (!store.vecAvailable)
210
+ fail("sqlite-vec extension failed to load on this platform");
211
+ const { indexed, provider } = await reindexAll(store);
212
+ if (!provider) {
213
+ fail("no embedding provider available — run Ollama locally or set OPENAI_API_KEY (see AIDIMAG_EMBEDDINGS)");
214
+ }
215
+ console.log(`Indexed ${indexed} memorie(s) with ${provider.name}/${provider.model} (${provider.dim}d).`);
216
+ store.close();
217
+ });
218
+ program
219
+ .command("status")
220
+ .description("Memory store summary")
221
+ .action(() => {
222
+ const store = MemoryStore.open();
223
+ const s = store.statusSummary();
224
+ console.log(`aidimag @ ${s.dbPath}`);
225
+ console.log(`total memories: ${s.total}`);
226
+ console.log(` by status: ${Object.entries(s.byStatus).map(([k, v]) => `${k}=${v}`).join(" ")}`);
227
+ if (Object.keys(s.byKind).length) {
228
+ console.log(` by kind: ${Object.entries(s.byKind).map(([k, v]) => `${k}=${v}`).join(" ")}`);
229
+ }
230
+ if (s.pinned) {
231
+ console.log(` pinned: ${s.pinned} (exempt from time decay)`);
232
+ }
233
+ if (s.pendingProposals) {
234
+ console.log(`\n${s.pendingProposals} proposal(s) awaiting review — run \`dim review\``);
235
+ }
236
+ store.close();
237
+ });
238
+ program
239
+ .command("log")
240
+ .description("Show recent memories")
241
+ .option("-n, --limit <n>", "Max entries", "20")
242
+ .action((opts) => {
243
+ const store = MemoryStore.open();
244
+ const memories = store.list(parseInt(opts.limit, 10));
245
+ if (memories.length === 0)
246
+ console.log("No memories yet. Try `dim remember \"...\"`.");
247
+ for (const m of memories)
248
+ printMemory(m);
249
+ store.close();
250
+ });
251
+ program
252
+ .command("gaps")
253
+ .description("Knowledge gaps: searches agents/you ran that returned NOTHING — the facts your brain is missing")
254
+ .option("-d, --days <n>", "Look-back window in days", "30")
255
+ .option("-n, --limit <n>", "Max entries", "20")
256
+ .option("--clear", "Clear the search log after showing")
257
+ .action((opts) => {
258
+ const store = MemoryStore.open();
259
+ const gaps = store.searchGaps({ sinceDays: parseInt(opts.days, 10), limit: parseInt(opts.limit, 10) });
260
+ if (gaps.length === 0) {
261
+ console.log(`No knowledge gaps in the last ${opts.days} day(s) — every search found something.`);
262
+ }
263
+ else {
264
+ console.log(`${gaps.length} knowledge gap(s) in the last ${opts.days} day(s) — most-asked first:\n`);
265
+ for (const g of gaps) {
266
+ const scope = g.paths.length ? ` [scope: ${g.paths.join(", ")}]` : "";
267
+ console.log(` ${String(g.misses).padStart(3)}× "${g.query}"${scope} (last: ${g.lastAsked.slice(0, 10)})`);
268
+ }
269
+ console.log(`\nFill a gap: dim remember "<the answer>" -k <kind> [-e TYPE:proof]`);
270
+ }
271
+ if (opts.clear) {
272
+ const n = store.clearSearchGaps();
273
+ console.log(`\nCleared ${n} logged search(es).`);
274
+ }
275
+ store.close();
276
+ });
277
+ program
278
+ .command("refute")
279
+ .description("Mark a memory REFUTED (kept as negative knowledge, unlike forget)")
280
+ .argument("<id>", "Memory id (full or 8-char prefix)")
281
+ .option("-s, --superseded-by <id>", "Id of a newer memory replacing it")
282
+ .action(async (id, opts) => {
283
+ const store = MemoryStore.open();
284
+ const match = store.list(1000).find((m) => m.id === id || m.id.startsWith(id));
285
+ if (!match)
286
+ fail(`no memory matching id '${id}'`);
287
+ store.refute(match.id, opts.supersededBy);
288
+ console.log(`✗ refuted ${match.id.slice(0, 8)}: "${match.claim}"`);
289
+ await autoSync(store);
290
+ store.close();
291
+ });
292
+ program
293
+ .command("pin")
294
+ .description("Pin a memory: it stays with the project forever — never decays with age (evidence failure can still mark it stale)")
295
+ .argument("<id>", "Memory id (full or 8-char prefix)")
296
+ .action(async (id) => {
297
+ const store = MemoryStore.open();
298
+ const match = store.list(1000).find((m) => m.id === id || m.id.startsWith(id));
299
+ if (!match)
300
+ fail(`no memory matching id '${id}'`);
301
+ store.setPinned(match.id, true);
302
+ console.log(`📌 pinned ${match.id.slice(0, 8)}: "${match.claim}"`);
303
+ console.log(` It won't decay with age. Evidence checks still apply — a failing check marks it stale.`);
304
+ await autoSync(store);
305
+ store.close();
306
+ });
307
+ program
308
+ .command("unpin")
309
+ .description("Unpin a memory — normal confidence decay resumes")
310
+ .argument("<id>", "Memory id (full or 8-char prefix)")
311
+ .action(async (id) => {
312
+ const store = MemoryStore.open();
313
+ const match = store.list(1000).find((m) => m.id === id || m.id.startsWith(id));
314
+ if (!match)
315
+ fail(`no memory matching id '${id}'`);
316
+ store.setPinned(match.id, false);
317
+ console.log(`unpinned ${match.id.slice(0, 8)}: "${match.claim}" — normal decay resumes.`);
318
+ await autoSync(store);
319
+ store.close();
320
+ });
321
+ program
322
+ .command("update")
323
+ .description("Update a memory's claim, kind, or add/remove evidence")
324
+ .argument("<id>", "Memory id (full or 8-char prefix)")
325
+ .option("-c, --claim <text>", "Update the claim text")
326
+ .option("-k, --kind <kind>", `Change memory kind: ${KINDS.join("|")}`)
327
+ .option("-g, --guardrail-level <level>", `For kind=GUARDRAIL: ${GUARDRAIL_LEVELS.join("|")}`)
328
+ .option("-e, --evidence <ev...>", "Add evidence (format: TYPE:payload)")
329
+ .option("--remove-evidence <id>", "Remove evidence by id prefix")
330
+ .action(async (id, opts) => {
331
+ const store = MemoryStore.open();
332
+ const match = store.list(1000).find((m) => m.id === id || m.id.startsWith(id));
333
+ if (!match)
334
+ fail(`no memory matching id '${id}'`);
335
+ const updates = {};
336
+ if (opts.claim)
337
+ updates.claim = opts.claim.trim();
338
+ if (opts.kind) {
339
+ if (!KINDS.includes(opts.kind))
340
+ fail(`invalid kind '${opts.kind}' — must be one of: ${KINDS.join(", ")}`);
341
+ updates.kind = opts.kind;
342
+ }
343
+ if (opts.guardrailLevel) {
344
+ if (updates.kind !== "GUARDRAIL" && match.kind !== "GUARDRAIL") {
345
+ fail("--guardrail-level only applies to GUARDRAIL memories");
346
+ }
347
+ if (!GUARDRAIL_LEVELS.includes(opts.guardrailLevel)) {
348
+ fail(`invalid --guardrail-level '${opts.guardrailLevel}' — must be one of: ${GUARDRAIL_LEVELS.join(", ")}`);
349
+ }
350
+ updates.guardrailLevel = opts.guardrailLevel;
351
+ }
352
+ if (Object.keys(updates).length > 0) {
353
+ store.update(match.id, updates);
354
+ console.log(`✓ Updated memory ${match.id.slice(0, 8)}`);
355
+ }
356
+ // Add evidence
357
+ for (const ev of opts.evidence || []) {
358
+ const [type, ...payloadParts] = ev.split(":");
359
+ const payload = payloadParts.join(":");
360
+ if (!payload)
361
+ fail(`evidence format: TYPE:payload (e.g. STATIC_CHECK:grep -r "foo" src/)`);
362
+ store.addEvidence(match.id, { type: type, payload });
363
+ console.log(`✓ Added evidence: ${type}`);
364
+ }
365
+ // Remove evidence
366
+ if (opts.removeEvidence) {
367
+ const evidence = match.grounding.find((e) => e.id === opts.removeEvidence || e.id.startsWith(opts.removeEvidence));
368
+ if (!evidence)
369
+ fail(`no evidence matching id '${opts.removeEvidence}'`);
370
+ store.removeEvidence(evidence.id);
371
+ console.log(`✓ Removed evidence ${evidence.id.slice(0, 8)}`);
372
+ }
373
+ await autoSync(store);
374
+ store.close();
375
+ });
376
+ program
377
+ .command("forget")
378
+ .description("Delete a memory permanently (prefer refuting via agents)")
379
+ .argument("<id>", "Memory id (full or 8-char prefix)")
380
+ .action(async (id) => {
381
+ const store = MemoryStore.open();
382
+ // allow prefix match
383
+ const match = store.list(1000).find((m) => m.id === id || m.id.startsWith(id));
384
+ if (!match)
385
+ fail(`no memory matching id '${id}'`);
386
+ store.forget(match.id);
387
+ console.log(`Forgot memory ${match.id}: "${match.claim}"`);
388
+ await autoSync(store);
389
+ store.close();
390
+ });
391
+ }
392
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Team-sync commands: serve, cloud, login, logout, sync, keys.
3
+ */
4
+ import type { Command } from "commander";
5
+ export declare function registerSyncCommands(program: Command): void;