@memrosetta/cli 0.3.2 → 0.4.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 (112) hide show
  1. package/dist/chunk-356QK7TK.js +533 -0
  2. package/dist/chunk-3LSITXZ4.js +69 -0
  3. package/dist/chunk-6IOTBMUB.js +570 -0
  4. package/dist/chunk-BGXZ3MDD.js +341 -0
  5. package/dist/chunk-EZVP3OY7.js +52 -0
  6. package/dist/chunk-FCHV2JMZ.js +347 -0
  7. package/dist/{chunk-F7ZMZ6HN.js → chunk-HXZ7MAT6.js} +2 -1
  8. package/dist/chunk-IM6H35RB.js +341 -0
  9. package/dist/chunk-JGE6RXXH.js +48 -0
  10. package/dist/chunk-JPBSMZ26.js +343 -0
  11. package/dist/chunk-KSKRPUZZ.js +43 -0
  12. package/dist/chunk-L6S3TXHR.js +326 -0
  13. package/dist/{chunk-326TFH4F.js → chunk-OCCU5YEG.js} +11 -42
  14. package/dist/chunk-PW52BB6L.js +61 -0
  15. package/dist/chunk-RABFL4EN.js +528 -0
  16. package/dist/chunk-RDUU53MG.js +343 -0
  17. package/dist/chunk-RYPYJJ2K.js +70 -0
  18. package/dist/{chunk-CATBN3ZT.js → chunk-TQOH7ZXN.js} +6 -18
  19. package/dist/chunk-Y6NH6K27.js +209 -0
  20. package/dist/{clear-MTL4CQM3.js → clear-4RQW6SYW.js} +10 -5
  21. package/dist/clear-BYRCL5ZN.js +39 -0
  22. package/dist/clear-ETQ7XFMV.js +39 -0
  23. package/dist/clear-P6H55OKZ.js +39 -0
  24. package/dist/clear-T3TWJQKL.js +39 -0
  25. package/dist/{compress-Z46R4N4M.js → compress-HDNYPXZ3.js} +10 -5
  26. package/dist/compress-S6MS4QW7.js +33 -0
  27. package/dist/compress-TVWXLW3L.js +33 -0
  28. package/dist/compress-VLYNZ5BQ.js +33 -0
  29. package/dist/compress-ZXWRRGHT.js +33 -0
  30. package/dist/{count-4TZ3C524.js → count-26AGY5XL.js} +10 -5
  31. package/dist/count-AVG5ZIRW.js +24 -0
  32. package/dist/count-CJIYYJKN.js +24 -0
  33. package/dist/count-KFFD4ML7.js +24 -0
  34. package/dist/count-UUAD3GEJ.js +24 -0
  35. package/dist/{get-B6AL75EW.js → get-75MGS4LN.js} +4 -2
  36. package/dist/ingest-A3BAI2C4.js +95 -0
  37. package/dist/ingest-E7QDD5NY.js +95 -0
  38. package/dist/ingest-IGI7RXR4.js +95 -0
  39. package/dist/ingest-JPIHSH7W.js +95 -0
  40. package/dist/ingest-QGXA4Y6C.js +95 -0
  41. package/dist/init-2PRW64KV.js +146 -0
  42. package/dist/init-C3CONL23.js +146 -0
  43. package/dist/init-DAKOWQSW.js +169 -0
  44. package/dist/init-FYPMJDRN.js +146 -0
  45. package/dist/{init-C335O4TX.js → init-GCT4XEI6.js} +2 -2
  46. package/dist/init-HBEIXY3N.js +146 -0
  47. package/dist/init-MISGIVCC.js +146 -0
  48. package/dist/init-MNM4TXXJ.js +146 -0
  49. package/dist/{init-YAGOXYWG.js → init-OQW3KXTR.js} +2 -2
  50. package/dist/init-SIEKAILM.js +113 -0
  51. package/dist/init-TM7GTHTJ.js +146 -0
  52. package/dist/init-VYWOSISP.js +146 -0
  53. package/dist/init-WCL7FZOJ.js +182 -0
  54. package/dist/init-ZLUDTJAP.js +182 -0
  55. package/dist/{invalidate-C54IVIGL.js → invalidate-D2O4VWZU.js} +4 -2
  56. package/dist/{maintain-ZPHG47YY.js → maintain-B65WIMGJ.js} +10 -5
  57. package/dist/maintain-EWOU3DGT.js +37 -0
  58. package/dist/maintain-FELKLG7O.js +37 -0
  59. package/dist/maintain-WRRDXEG3.js +37 -0
  60. package/dist/maintain-XTCSOQBU.js +37 -0
  61. package/dist/{relate-R6DQUJCQ.js → relate-2QMG5H2I.js} +5 -3
  62. package/dist/relate-C22YYJZT.js +46 -0
  63. package/dist/relate-V5RYMJJ5.js +47 -0
  64. package/dist/relate-W4BXPFJA.js +46 -0
  65. package/dist/reset-45EUG44R.js +95 -0
  66. package/dist/reset-5NDHFUC3.js +95 -0
  67. package/dist/reset-C7I3LA5M.js +95 -0
  68. package/dist/reset-F6F2R6BR.js +95 -0
  69. package/dist/reset-K3K4K5CT.js +95 -0
  70. package/dist/reset-K4WZJ4WU.js +95 -0
  71. package/dist/{reset-P63V46RN.js → reset-NNQQJN6L.js} +2 -2
  72. package/dist/{reset-GPV46GSD.js → reset-P5FXLI4E.js} +2 -2
  73. package/dist/reset-SORHIEKY.js +112 -0
  74. package/dist/reset-SVJMWYAK.js +95 -0
  75. package/dist/reset-W22RJGYZ.js +112 -0
  76. package/dist/reset-W3QVA632.js +95 -0
  77. package/dist/reset-WYEU6XJQ.js +112 -0
  78. package/dist/{search-YEYKOEXC.js → search-2SU5WQYK.js} +9 -4
  79. package/dist/search-5EE3XB2R.js +48 -0
  80. package/dist/search-L6P3XY47.js +48 -0
  81. package/dist/search-UA7Y55LQ.js +48 -0
  82. package/dist/search-ZKLRJXFT.js +48 -0
  83. package/dist/{status-PYD6U7U7.js → status-3XVXJF7M.js} +1 -42
  84. package/dist/status-4THJLSJL.js +131 -0
  85. package/dist/status-4UYY7TOE.js +131 -0
  86. package/dist/status-7G3RMR6A.js +139 -0
  87. package/dist/status-7M4TJVDH.js +170 -0
  88. package/dist/status-7MEEKEC2.js +131 -0
  89. package/dist/status-BQL4VJ6Y.js +149 -0
  90. package/dist/status-BTEVCZ5K.js +125 -0
  91. package/dist/status-F3NZGGPH.js +131 -0
  92. package/dist/status-GQMXOMRN.js +131 -0
  93. package/dist/status-HJGTMHTD.js +131 -0
  94. package/dist/{status-AMMF6QGI.js → status-IEKR6SSH.js} +35 -6
  95. package/dist/status-JJGVWESB.js +139 -0
  96. package/dist/status-LDVRVYZH.js +131 -0
  97. package/dist/{status-XNQRRFWF.js → status-TATIX3R6.js} +1 -1
  98. package/dist/{store-ZMERYHI6.js → store-6D5OJ2DX.js} +12 -7
  99. package/dist/store-I4JNJWAZ.js +91 -0
  100. package/dist/store-R4LXYE57.js +91 -0
  101. package/dist/store-UMR2NOT7.js +91 -0
  102. package/dist/store-ZDI5AH4M.js +91 -0
  103. package/dist/update-XJLMXCDZ.js +36 -0
  104. package/dist/{working-memory-2RRQFX4Q.js → working-memory-2U33JHJB.js} +10 -6
  105. package/dist/working-memory-35TMFU3P.js +53 -0
  106. package/dist/working-memory-IBWGIXLW.js +53 -0
  107. package/dist/working-memory-RZE3GYQZ.js +53 -0
  108. package/dist/working-memory-WGB5DZ3B.js +53 -0
  109. package/package.json +4 -4
  110. package/dist/chunk-MWLPPS7U.js +0 -133
  111. package/dist/ingest-ZOR3XSAE.js +0 -152
  112. package/dist/init-7VFBCA4O.js +0 -26
@@ -0,0 +1,343 @@
1
+ // src/integrations/claude-code.ts
2
+ import { join } from "path";
3
+ import { homedir } from "os";
4
+ import { existsSync, readFileSync, writeFileSync } from "fs";
5
+ var CLAUDE_DIR = join(homedir(), ".claude");
6
+ var CLAUDE_SETTINGS_PATH = join(CLAUDE_DIR, "settings.json");
7
+ var CLAUDE_MD_PATH = join(CLAUDE_DIR, "CLAUDE.md");
8
+ function isMemrosettaHook(command) {
9
+ return command.includes("memrosetta") && (command.includes("on-stop") || command.includes("on-prompt"));
10
+ }
11
+ function readClaudeSettings() {
12
+ if (!existsSync(CLAUDE_SETTINGS_PATH)) return {};
13
+ try {
14
+ return JSON.parse(
15
+ readFileSync(CLAUDE_SETTINGS_PATH, "utf-8")
16
+ );
17
+ } catch {
18
+ return {};
19
+ }
20
+ }
21
+ function writeClaudeSettings(settings) {
22
+ if (!existsSync(CLAUDE_DIR)) {
23
+ throw new Error(
24
+ "~/.claude directory does not exist. Is Claude Code installed?"
25
+ );
26
+ }
27
+ writeFileSync(
28
+ CLAUDE_SETTINGS_PATH,
29
+ JSON.stringify(settings, null, 2),
30
+ "utf-8"
31
+ );
32
+ }
33
+ function removeMemrosettaHooksFromSettings(settings) {
34
+ if (!settings.hooks) return settings;
35
+ const cleaned = {};
36
+ for (const [eventType, hookConfigs] of Object.entries(settings.hooks)) {
37
+ const filtered = hookConfigs.filter(
38
+ (hc) => !hc.hooks.some((h) => isMemrosettaHook(h.command))
39
+ );
40
+ cleaned[eventType] = filtered;
41
+ }
42
+ return { ...settings, hooks: cleaned };
43
+ }
44
+ function isClaudeCodeInstalled() {
45
+ return existsSync(CLAUDE_DIR);
46
+ }
47
+ function isClaudeCodeConfigured() {
48
+ const settings = readClaudeSettings();
49
+ const stopHooks = settings.hooks?.["Stop"] || [];
50
+ return stopHooks.some(
51
+ (hc) => hc.hooks.some((h) => isMemrosettaHook(h.command))
52
+ );
53
+ }
54
+ function registerClaudeCodeHooks() {
55
+ if (!isClaudeCodeInstalled()) return false;
56
+ let settings = readClaudeSettings();
57
+ if (!settings.hooks) {
58
+ settings = { ...settings, hooks: {} };
59
+ }
60
+ settings = removeMemrosettaHooksFromSettings(settings);
61
+ const stopHookConfigs = settings.hooks["Stop"] || [];
62
+ settings.hooks["Stop"] = [
63
+ ...stopHookConfigs,
64
+ {
65
+ matcher: "*",
66
+ hooks: [
67
+ {
68
+ type: "command",
69
+ command: "npx -y @memrosetta/cli memrosetta-on-stop",
70
+ timeout: 15
71
+ }
72
+ ]
73
+ }
74
+ ];
75
+ writeClaudeSettings(settings);
76
+ return true;
77
+ }
78
+ function removeClaudeCodeHooks() {
79
+ if (!existsSync(CLAUDE_SETTINGS_PATH)) return false;
80
+ const settings = readClaudeSettings();
81
+ if (!settings.hooks) return false;
82
+ const cleaned = removeMemrosettaHooksFromSettings(settings);
83
+ writeClaudeSettings(cleaned);
84
+ return true;
85
+ }
86
+ function updateClaudeMd() {
87
+ if (!isClaudeCodeInstalled()) return false;
88
+ const marker = "## MemRosetta (Long-term Memory)";
89
+ const existing = existsSync(CLAUDE_MD_PATH) ? readFileSync(CLAUDE_MD_PATH, "utf-8") : "";
90
+ if (existing.includes(marker)) return false;
91
+ const memorySection = `
92
+
93
+ ${marker}
94
+
95
+ MCP server \`memory-service\` provides long-term memory across sessions.
96
+ userId defaults to the system username -- no need to specify it.
97
+
98
+ ### Search (mcp__memory-service__memrosetta_search)
99
+ When you need information not in the current context, search past memories.
100
+ No need to specify userId -- it defaults to the system username.
101
+
102
+ ### Store (mcp__memory-service__memrosetta_store)
103
+ You MUST store the following immediately when encountered:
104
+ - **Decisions**: Any technical choice ("We decided to use X", "Going with approach B")
105
+ - **Facts**: Key project facts ("API runs on port 8080", "Database is PostgreSQL")
106
+ - **Preferences**: User preferences ("I prefer functional style", "Always use TypeScript")
107
+ - **Events**: Completed milestones ("Auth system deployed", "Migration completed")
108
+
109
+ Always include 2-3 keywords. Example:
110
+ content: "Decided to use OAuth2 with PKCE for auth"
111
+ type: "decision"
112
+ keywords: "auth, oauth2, pkce"
113
+
114
+ Do NOT store:
115
+ - Code itself (belongs in git)
116
+ - File operations ("Created file X", "Modified Y")
117
+ - Debugging steps and attempts
118
+ - Simple confirmations or acknowledgments
119
+ - Implementation details (HOW you did it)
120
+
121
+ Focus on WHAT was decided, learned, or achieved -- not HOW.
122
+ No need to specify userId -- it defaults to the system username.
123
+ `;
124
+ writeFileSync(CLAUDE_MD_PATH, existing + memorySection, "utf-8");
125
+ return true;
126
+ }
127
+ function removeClaudeMdSection() {
128
+ if (!existsSync(CLAUDE_MD_PATH)) return false;
129
+ const content = readFileSync(CLAUDE_MD_PATH, "utf-8");
130
+ const marker = "## MemRosetta (Long-term Memory)";
131
+ const markerIdx = content.indexOf(marker);
132
+ if (markerIdx === -1) return false;
133
+ const afterMarker = content.slice(markerIdx + marker.length);
134
+ const nextHeadingMatch = afterMarker.match(/\n## (?!MemRosetta)/);
135
+ const endIdx = nextHeadingMatch ? markerIdx + marker.length + (nextHeadingMatch.index ?? afterMarker.length) : content.length;
136
+ const before = content.slice(0, markerIdx).replace(/\n+$/, "");
137
+ const after = content.slice(endIdx);
138
+ const updated = (before + after).trimEnd() + "\n";
139
+ writeFileSync(CLAUDE_MD_PATH, updated, "utf-8");
140
+ return true;
141
+ }
142
+
143
+ // src/integrations/mcp.ts
144
+ import { join as join2 } from "path";
145
+ import { homedir as homedir2 } from "os";
146
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
147
+ var MCP_CONFIG_PATH = join2(homedir2(), ".mcp.json");
148
+ var SERVER_NAME = "memory-service";
149
+ function readMcpConfig(path) {
150
+ if (!existsSync2(path)) return {};
151
+ try {
152
+ return JSON.parse(readFileSync2(path, "utf-8"));
153
+ } catch {
154
+ return {};
155
+ }
156
+ }
157
+ function writeMcpConfig(path, config) {
158
+ writeFileSync2(path, JSON.stringify(config, null, 2), "utf-8");
159
+ }
160
+ function mcpServerEntry() {
161
+ return {
162
+ command: "npx",
163
+ args: ["-y", "@memrosetta/mcp"],
164
+ env: {
165
+ MEMROSETTA_EMBEDDINGS: "false"
166
+ }
167
+ };
168
+ }
169
+ function isGenericMCPConfigured() {
170
+ const config = readMcpConfig(MCP_CONFIG_PATH);
171
+ return !!config.mcpServers?.[SERVER_NAME];
172
+ }
173
+ function registerGenericMCP() {
174
+ const config = readMcpConfig(MCP_CONFIG_PATH);
175
+ const servers = config.mcpServers ?? {};
176
+ writeMcpConfig(MCP_CONFIG_PATH, {
177
+ ...config,
178
+ mcpServers: { ...servers, [SERVER_NAME]: mcpServerEntry() }
179
+ });
180
+ }
181
+ function removeGenericMCP() {
182
+ if (!existsSync2(MCP_CONFIG_PATH)) return false;
183
+ const config = readMcpConfig(MCP_CONFIG_PATH);
184
+ if (!config.mcpServers?.[SERVER_NAME]) return false;
185
+ const { [SERVER_NAME]: _, ...rest } = config.mcpServers;
186
+ writeMcpConfig(MCP_CONFIG_PATH, { ...config, mcpServers: rest });
187
+ return true;
188
+ }
189
+ function getGenericMCPPath() {
190
+ return MCP_CONFIG_PATH;
191
+ }
192
+
193
+ // src/integrations/cursor.ts
194
+ import { join as join3 } from "path";
195
+ import { homedir as homedir3 } from "os";
196
+ import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync } from "fs";
197
+ var SERVER_NAME2 = "memory-service";
198
+ var CURSOR_RULES_PATH_GETTER = () => join3(homedir3(), ".cursorrules");
199
+ var MEMROSETTA_CURSOR_RULES_MARKER = "## MemRosetta (Long-term Memory)";
200
+ var MEMROSETTA_CURSOR_RULES = `
201
+
202
+ ${MEMROSETTA_CURSOR_RULES_MARKER}
203
+
204
+ MCP server \`memory-service\` provides persistent memory across sessions.
205
+ userId defaults to the system username -- no need to specify it.
206
+
207
+ ### When to search (memrosetta_search)
208
+ When you need information not in the current context, search past memories.
209
+ No need to specify userId -- it defaults to the system username.
210
+
211
+ ### When to store (memrosetta_store)
212
+ You MUST store the following immediately when encountered:
213
+ - **Decisions**: Any technical choice ("We decided to use X", "Going with approach B")
214
+ - **Facts**: Key project facts ("API runs on port 8080", "Database is PostgreSQL")
215
+ - **Preferences**: User preferences ("I prefer functional style", "Always use TypeScript")
216
+ - **Events**: Completed milestones ("Auth system deployed", "Migration completed")
217
+
218
+ Always include 2-3 keywords. Example:
219
+ content: "Decided to use OAuth2 with PKCE for auth"
220
+ type: "decision"
221
+ keywords: "auth, oauth2, pkce"
222
+
223
+ Do NOT store:
224
+ - Code itself (belongs in git)
225
+ - File operations ("Created file X", "Modified Y")
226
+ - Debugging steps and attempts
227
+ - Simple confirmations or acknowledgments
228
+ - Implementation details (HOW you did it)
229
+
230
+ Focus on WHAT was decided, learned, or achieved -- not HOW.
231
+ No need to specify userId -- it defaults to the system username.
232
+
233
+ ### When to relate (memrosetta_relate)
234
+ When new information updates or contradicts existing memories, create a relation.
235
+
236
+ ### Working memory (memrosetta_working_memory)
237
+ Call this at the start of complex tasks to load relevant context.
238
+ No need to specify userId -- it defaults to the system username.
239
+ `;
240
+ function getCursorConfigDir() {
241
+ return join3(homedir3(), ".cursor");
242
+ }
243
+ function getCursorMcpPath() {
244
+ return join3(getCursorConfigDir(), "mcp.json");
245
+ }
246
+ function readCursorConfig(path) {
247
+ if (!existsSync3(path)) return {};
248
+ try {
249
+ return JSON.parse(readFileSync3(path, "utf-8"));
250
+ } catch {
251
+ return {};
252
+ }
253
+ }
254
+ function writeCursorConfig(path, config) {
255
+ const dir = getCursorConfigDir();
256
+ if (!existsSync3(dir)) {
257
+ mkdirSync(dir, { recursive: true });
258
+ }
259
+ writeFileSync3(path, JSON.stringify(config, null, 2), "utf-8");
260
+ }
261
+ function mcpServerEntry2() {
262
+ return {
263
+ command: "npx",
264
+ args: ["-y", "@memrosetta/mcp"],
265
+ env: {
266
+ MEMROSETTA_EMBEDDINGS: "false"
267
+ }
268
+ };
269
+ }
270
+ function isCursorConfigured() {
271
+ const path = getCursorMcpPath();
272
+ const config = readCursorConfig(path);
273
+ return !!config.mcpServers?.[SERVER_NAME2];
274
+ }
275
+ function registerCursorMCP() {
276
+ const path = getCursorMcpPath();
277
+ const config = readCursorConfig(path);
278
+ const servers = config.mcpServers ?? {};
279
+ writeCursorConfig(path, {
280
+ ...config,
281
+ mcpServers: { ...servers, [SERVER_NAME2]: mcpServerEntry2() }
282
+ });
283
+ updateCursorRules();
284
+ }
285
+ function removeCursorMCP() {
286
+ const path = getCursorMcpPath();
287
+ if (!existsSync3(path)) return false;
288
+ const config = readCursorConfig(path);
289
+ if (!config.mcpServers?.[SERVER_NAME2]) return false;
290
+ const { [SERVER_NAME2]: _, ...rest } = config.mcpServers;
291
+ writeCursorConfig(path, { ...config, mcpServers: rest });
292
+ removeCursorRulesSection();
293
+ return true;
294
+ }
295
+ function getCursorMcpConfigPath() {
296
+ return getCursorMcpPath();
297
+ }
298
+ function getCursorRulesPath() {
299
+ return CURSOR_RULES_PATH_GETTER();
300
+ }
301
+ function updateCursorRules() {
302
+ const rulesPath = CURSOR_RULES_PATH_GETTER();
303
+ const existing = existsSync3(rulesPath) ? readFileSync3(rulesPath, "utf-8") : "";
304
+ if (existing.includes(MEMROSETTA_CURSOR_RULES_MARKER)) return false;
305
+ writeFileSync3(rulesPath, existing + MEMROSETTA_CURSOR_RULES, "utf-8");
306
+ return true;
307
+ }
308
+ function removeCursorRulesSection() {
309
+ const rulesPath = CURSOR_RULES_PATH_GETTER();
310
+ if (!existsSync3(rulesPath)) return false;
311
+ const content = readFileSync3(rulesPath, "utf-8");
312
+ const markerIdx = content.indexOf(MEMROSETTA_CURSOR_RULES_MARKER);
313
+ if (markerIdx === -1) return false;
314
+ const afterMarker = content.slice(
315
+ markerIdx + MEMROSETTA_CURSOR_RULES_MARKER.length
316
+ );
317
+ const nextHeadingMatch = afterMarker.match(/\n## (?!MemRosetta)/);
318
+ const endIdx = nextHeadingMatch ? markerIdx + MEMROSETTA_CURSOR_RULES_MARKER.length + (nextHeadingMatch.index ?? afterMarker.length) : content.length;
319
+ const before = content.slice(0, markerIdx).replace(/\n+$/, "");
320
+ const after = content.slice(endIdx);
321
+ const updated = (before + after).trimEnd() + "\n";
322
+ writeFileSync3(rulesPath, updated, "utf-8");
323
+ return true;
324
+ }
325
+
326
+ export {
327
+ isClaudeCodeInstalled,
328
+ isClaudeCodeConfigured,
329
+ registerClaudeCodeHooks,
330
+ removeClaudeCodeHooks,
331
+ updateClaudeMd,
332
+ removeClaudeMdSection,
333
+ isGenericMCPConfigured,
334
+ registerGenericMCP,
335
+ removeGenericMCP,
336
+ getGenericMCPPath,
337
+ isCursorConfigured,
338
+ registerCursorMCP,
339
+ removeCursorMCP,
340
+ getCursorMcpConfigPath,
341
+ getCursorRulesPath,
342
+ removeCursorRulesSection
343
+ };
@@ -0,0 +1,70 @@
1
+ import {
2
+ ensureDir,
3
+ getConfig
4
+ } from "./chunk-JGE6RXXH.js";
5
+
6
+ // src/hooks/engine-manager.ts
7
+ import { SqliteMemoryEngine } from "@memrosetta/core";
8
+ var engineInstance = null;
9
+ async function getEngine() {
10
+ if (engineInstance) return engineInstance;
11
+ const config = getConfig();
12
+ ensureDir();
13
+ let embedder;
14
+ if (config.enableEmbeddings) {
15
+ try {
16
+ const { HuggingFaceEmbedder } = await import("@memrosetta/embeddings");
17
+ const preset = config.embeddingPreset ?? "en";
18
+ embedder = new HuggingFaceEmbedder({ preset });
19
+ await embedder.initialize();
20
+ } catch (err) {
21
+ const message = err instanceof Error ? err.message : String(err);
22
+ process.stderr.write(
23
+ `[memrosetta] Failed to load embeddings, continuing without: ${message}
24
+ `
25
+ );
26
+ }
27
+ }
28
+ engineInstance = new SqliteMemoryEngine({
29
+ dbPath: config.dbPath,
30
+ embedder
31
+ });
32
+ await engineInstance.initialize();
33
+ return engineInstance;
34
+ }
35
+ async function closeEngine() {
36
+ if (engineInstance) {
37
+ await engineInstance.close();
38
+ engineInstance = null;
39
+ }
40
+ }
41
+ async function getEngineWithTimeout(timeoutMs) {
42
+ let timer;
43
+ const enginePromise = getEngine();
44
+ const timeoutPromise = new Promise((resolve2) => {
45
+ timer = setTimeout(() => resolve2(null), timeoutMs);
46
+ });
47
+ const result = await Promise.race([enginePromise, timeoutPromise]);
48
+ clearTimeout(timer);
49
+ return result;
50
+ }
51
+
52
+ // src/hooks/path-validation.ts
53
+ import { resolve } from "path";
54
+ import { homedir } from "os";
55
+ var CLAUDE_DIR = resolve(homedir(), ".claude");
56
+ function isValidTranscriptPath(p) {
57
+ const resolved = resolve(p);
58
+ return resolved.startsWith(CLAUDE_DIR) && resolved.endsWith(".jsonl");
59
+ }
60
+ function sanitizeSessionId(sessionId) {
61
+ return sessionId.replace(/[^a-zA-Z0-9_-]/g, "");
62
+ }
63
+
64
+ export {
65
+ getEngine,
66
+ closeEngine,
67
+ getEngineWithTimeout,
68
+ isValidTranscriptPath,
69
+ sanitizeSessionId
70
+ };
@@ -211,12 +211,6 @@ Do NOT store:
211
211
 
212
212
  This checklist ensures nothing important is lost, including the last response before session ends.
213
213
  No need to specify userId -- it defaults to the system username.
214
-
215
- ### Feedback (mcp__memory-service__memrosetta_feedback)
216
- After using a retrieved memory, report whether it was helpful:
217
- - Memory was accurate and useful -> feedback(memoryId, helpful=true)
218
- - Memory was outdated or wrong -> feedback(memoryId, helpful=false)
219
- This improves future search ranking automatically.
220
214
  `;
221
215
  writeFileSync(CLAUDE_MD_PATH, existing + memorySection, "utf-8");
222
216
  return true;
@@ -330,12 +324,6 @@ When new information updates or contradicts existing memories, create a relation
330
324
  ### Working memory (memrosetta_working_memory)
331
325
  Call this at the start of complex tasks to load relevant context.
332
326
  No need to specify userId -- it defaults to the system username.
333
-
334
- ### Feedback (memrosetta_feedback)
335
- After using a retrieved memory, report whether it was helpful:
336
- - Memory was accurate and useful -> feedback(memoryId, helpful=true)
337
- - Memory was outdated or wrong -> feedback(memoryId, helpful=false)
338
- This improves future search ranking automatically.
339
327
  `;
340
328
  function getCursorConfigDir() {
341
329
  return join4(homedir3(), ".cursor");
@@ -422,20 +410,20 @@ function removeCursorRulesSection() {
422
410
  import { join as join5 } from "path";
423
411
  import { homedir as homedir4 } from "os";
424
412
  import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync2 } from "fs";
425
- var SERVER_NAME3 = "memory-service";
413
+ var SERVER_NAME3 = "memrosetta";
426
414
  var CODEX_CONFIG_PATH_GETTER = () => join5(homedir4(), ".codex", "config.toml");
427
415
  var AGENTS_MD_MARKER = "## MemRosetta (Long-term Memory)";
428
416
  var MEMROSETTA_AGENTS_MD = `
429
417
 
430
418
  ${AGENTS_MD_MARKER}
431
419
 
432
- MCP server \`memory-service\` provides persistent memory across sessions.
420
+ MCP server \`memrosetta\` provides persistent memory across sessions.
433
421
  userId defaults to the system username -- no need to specify it.
434
422
 
435
- ### When to search (mcp__memory-service__memrosetta_search)
423
+ ### When to search (memrosetta_search)
436
424
  When you need information not in the current context, search past memories.
437
425
 
438
- ### When to store (mcp__memory-service__memrosetta_store)
426
+ ### When to store (memrosetta_store)
439
427
 
440
428
  **After EVERY response, run this checklist:**
441
429
  1. Did I encounter a DECISION? (tech choice, approach selection) -> store as "decision"
@@ -455,10 +443,10 @@ Do NOT store:
455
443
  - Debugging steps and attempts
456
444
  - Simple confirmations or acknowledgments
457
445
 
458
- ### When to relate (mcp__memory-service__memrosetta_relate)
446
+ ### When to relate (memrosetta_relate)
459
447
  When new information updates or contradicts existing memories, create a relation.
460
448
 
461
- ### Working memory (mcp__memory-service__memrosetta_working_memory)
449
+ ### Working memory (memrosetta_working_memory)
462
450
  Call this at the start of complex tasks to load relevant context.
463
451
  `;
464
452
  function getCodexConfigDir() {
@@ -0,0 +1,209 @@
1
+ // src/hooks/transcript-parser.ts
2
+ import { readFileSync } from "fs";
3
+ function stripSystemReminders(text) {
4
+ let result = text;
5
+ while (result.includes("<system-reminder>") && result.includes("</system-reminder>")) {
6
+ const start = result.indexOf("<system-reminder>");
7
+ const end = result.indexOf("</system-reminder>") + "</system-reminder>".length;
8
+ result = result.slice(0, start) + result.slice(end);
9
+ }
10
+ return result.trim();
11
+ }
12
+ function extractAssistantText(content) {
13
+ if (typeof content === "string") {
14
+ return content.trim();
15
+ }
16
+ if (Array.isArray(content)) {
17
+ return content.filter(
18
+ (block) => block !== null && typeof block === "object" && block.type === "text" && typeof block.text === "string"
19
+ ).map((block) => block.text).join("\n").trim();
20
+ }
21
+ return "";
22
+ }
23
+ function deduplicateTurns(turns) {
24
+ const result = [];
25
+ for (const turn of turns) {
26
+ if (result.length === 0 || result[result.length - 1].content !== turn.content) {
27
+ result.push(turn);
28
+ }
29
+ }
30
+ return result;
31
+ }
32
+ function parseTranscript(transcriptPath) {
33
+ const content = readFileSync(transcriptPath, "utf-8");
34
+ return parseTranscriptContent(content);
35
+ }
36
+ function parseTranscriptContent(content) {
37
+ const lines = content.split("\n").filter((l) => l.trim());
38
+ let cwd = "";
39
+ let sessionId = "";
40
+ const turns = [];
41
+ for (const line of lines) {
42
+ let entry;
43
+ try {
44
+ entry = JSON.parse(line);
45
+ } catch {
46
+ continue;
47
+ }
48
+ if (!cwd && entry.cwd) {
49
+ cwd = entry.cwd;
50
+ }
51
+ if (!sessionId && entry.sessionId) {
52
+ sessionId = entry.sessionId;
53
+ }
54
+ const msg = entry.message;
55
+ if (!msg || !msg.role) continue;
56
+ if (msg.role === "user" && typeof msg.content === "string") {
57
+ const clean = stripSystemReminders(msg.content);
58
+ if (clean && clean.length > 5) {
59
+ turns.push({ role: "user", content: clean });
60
+ }
61
+ } else if (msg.role === "assistant" && msg.content !== void 0) {
62
+ const text = extractAssistantText(
63
+ msg.content
64
+ );
65
+ if (text && text.length > 10) {
66
+ turns.push({ role: "assistant", content: text });
67
+ }
68
+ }
69
+ }
70
+ return {
71
+ turns: deduplicateTurns(turns),
72
+ cwd,
73
+ sessionId
74
+ };
75
+ }
76
+
77
+ // src/hooks/memory-extractor.ts
78
+ import { userInfo } from "os";
79
+ var KEYWORD_PATTERNS = {
80
+ typescript: "TypeScript",
81
+ sqlite: "SQLite",
82
+ api: "API",
83
+ benchmark: "benchmark",
84
+ docker: "Docker",
85
+ git: "Git",
86
+ react: "React",
87
+ hono: "Hono",
88
+ test: "test",
89
+ deploy: "deploy",
90
+ refactor: "refactor",
91
+ bug: "bug",
92
+ memrosetta: "MemRosetta",
93
+ vector: "vector",
94
+ embedding: "embedding",
95
+ fts5: "FTS5",
96
+ search: "search",
97
+ memory: "memory",
98
+ database: "database",
99
+ postgresql: "PostgreSQL",
100
+ nextjs: "Next.js",
101
+ "next.js": "Next.js",
102
+ node: "Node.js",
103
+ python: "Python",
104
+ rust: "Rust",
105
+ kubernetes: "Kubernetes",
106
+ ci: "CI/CD",
107
+ security: "security",
108
+ auth: "authentication",
109
+ cache: "cache",
110
+ performance: "performance"
111
+ };
112
+ function classifyTurn(turn) {
113
+ const lower = turn.content.toLowerCase();
114
+ if (turn.role === "user") {
115
+ if (lower.includes("decide") || lower.includes("go with") || lower.includes("let's do") || lower.includes("proceed") || lower.includes("approved")) {
116
+ return "decision";
117
+ }
118
+ if (lower.includes("prefer") || lower.includes("i like") || lower.includes("i want") || lower.includes("i need")) {
119
+ return "preference";
120
+ }
121
+ return "event";
122
+ }
123
+ return "fact";
124
+ }
125
+ function extractKeywords(text) {
126
+ const keywords = /* @__PURE__ */ new Set();
127
+ const lower = text.toLowerCase();
128
+ for (const [pattern, keyword] of Object.entries(KEYWORD_PATTERNS)) {
129
+ if (lower.includes(pattern)) {
130
+ keywords.add(keyword);
131
+ }
132
+ }
133
+ return [...keywords];
134
+ }
135
+ function resolveUserId(_cwd) {
136
+ return userInfo().username;
137
+ }
138
+ function extractFirstSentence(text, maxLen = 200) {
139
+ const lines = text.split("\n");
140
+ for (const line of lines) {
141
+ const trimmed = line.trim();
142
+ if (!trimmed) continue;
143
+ if (trimmed.startsWith("```")) continue;
144
+ if (trimmed.startsWith("|")) continue;
145
+ if (trimmed.startsWith("#")) {
146
+ const headerText = trimmed.replace(/^#+\s*/, "");
147
+ if (headerText.length > 10) {
148
+ return headerText.length > maxLen ? headerText.slice(0, maxLen - 3) + "..." : headerText;
149
+ }
150
+ continue;
151
+ }
152
+ if (trimmed.startsWith("- [") || trimmed.startsWith("> ")) continue;
153
+ if (trimmed.length < 10) continue;
154
+ return trimmed.length > maxLen ? trimmed.slice(0, maxLen - 3) + "..." : trimmed;
155
+ }
156
+ const clean = text.replace(/\s+/g, " ").trim();
157
+ return clean.length > maxLen ? clean.slice(0, maxLen - 3) + "..." : clean;
158
+ }
159
+ function isWorthStoring(turn) {
160
+ const content = turn.content;
161
+ if (content.length < 30) return false;
162
+ if (turn.role === "user") {
163
+ const lower = content.toLowerCase().trim();
164
+ if (lower.length < 15) return false;
165
+ if (lower.startsWith("/")) return false;
166
+ if (/^(y|n|yes|no|ok)$/i.test(lower)) return false;
167
+ return true;
168
+ }
169
+ const codeBlockCount = (content.match(/```/g) || []).length / 2;
170
+ const textLines = content.split("\n").filter((l) => {
171
+ const t = l.trim();
172
+ return t && !t.startsWith("```") && !t.startsWith("|") && !t.startsWith("- [");
173
+ });
174
+ if (codeBlockCount > 3 && textLines.length < 5) return false;
175
+ return true;
176
+ }
177
+ function extractMemories(data, userId) {
178
+ const memories = [];
179
+ const sessionShort = data.sessionId ? data.sessionId.slice(0, 8) : "unknown";
180
+ const now = (/* @__PURE__ */ new Date()).toISOString();
181
+ const seen = /* @__PURE__ */ new Set();
182
+ for (let i = 0; i < data.turns.length; i++) {
183
+ const turn = data.turns[i];
184
+ if (!isWorthStoring(turn)) continue;
185
+ const content = extractFirstSentence(turn.content);
186
+ if (seen.has(content)) continue;
187
+ seen.add(content);
188
+ memories.push({
189
+ userId,
190
+ namespace: `session-${sessionShort}`,
191
+ memoryType: classifyTurn(turn),
192
+ content,
193
+ documentDate: now,
194
+ sourceId: `cc-${sessionShort}-${i}`,
195
+ confidence: turn.role === "user" ? 0.9 : 0.8,
196
+ keywords: extractKeywords(content)
197
+ });
198
+ }
199
+ return memories;
200
+ }
201
+
202
+ export {
203
+ stripSystemReminders,
204
+ parseTranscript,
205
+ parseTranscriptContent,
206
+ classifyTurn,
207
+ resolveUserId,
208
+ extractMemories
209
+ };