@memrosetta/cli 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-CATBN3ZT.js +596 -0
- package/dist/index.js +3 -3
- package/dist/init-C335O4TX.js +182 -0
- package/dist/reset-P63V46RN.js +112 -0
- package/dist/status-PYD6U7U7.js +211 -0
- package/package.json +3 -3
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
// src/integrations/claude-code.ts
|
|
2
|
+
import { join as join2 } from "path";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
import { existsSync as existsSync2, readFileSync, writeFileSync } from "fs";
|
|
5
|
+
|
|
6
|
+
// src/integrations/resolve-command.ts
|
|
7
|
+
import { execSync } from "child_process";
|
|
8
|
+
import { createRequire } from "module";
|
|
9
|
+
import { join, dirname, resolve } from "path";
|
|
10
|
+
import { existsSync } from "fs";
|
|
11
|
+
import { fileURLToPath } from "url";
|
|
12
|
+
function isInPath(command) {
|
|
13
|
+
try {
|
|
14
|
+
const cmd = process.platform === "win32" ? `where ${command}` : `command -v ${command}`;
|
|
15
|
+
execSync(cmd, { stdio: "ignore" });
|
|
16
|
+
return true;
|
|
17
|
+
} catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function findUpwards(startDir, target, maxLevels = 6) {
|
|
22
|
+
let dir = startDir;
|
|
23
|
+
for (let i = 0; i < maxLevels; i++) {
|
|
24
|
+
const candidate = join(dir, target);
|
|
25
|
+
if (existsSync(candidate)) return candidate;
|
|
26
|
+
const parent = dirname(dir);
|
|
27
|
+
if (parent === dir) break;
|
|
28
|
+
dir = parent;
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
33
|
+
function resolveMcpCommand() {
|
|
34
|
+
if (isInPath("memrosetta-mcp")) {
|
|
35
|
+
return { command: "memrosetta-mcp", args: [] };
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const require2 = createRequire(import.meta.url);
|
|
39
|
+
const mcpPkgPath = dirname(
|
|
40
|
+
require2.resolve("@memrosetta/mcp/package.json")
|
|
41
|
+
);
|
|
42
|
+
const entryPoint = join(mcpPkgPath, "dist", "index.js");
|
|
43
|
+
if (existsSync(entryPoint)) {
|
|
44
|
+
return { command: "node", args: [entryPoint] };
|
|
45
|
+
}
|
|
46
|
+
} catch {
|
|
47
|
+
}
|
|
48
|
+
const workspaceRoot = findUpwards(__dirname, "pnpm-workspace.yaml");
|
|
49
|
+
if (workspaceRoot) {
|
|
50
|
+
const rootDir = dirname(workspaceRoot);
|
|
51
|
+
const mcpEntry = join(rootDir, "adapters", "mcp", "dist", "index.js");
|
|
52
|
+
if (existsSync(mcpEntry)) {
|
|
53
|
+
return { command: "node", args: [mcpEntry] };
|
|
54
|
+
}
|
|
55
|
+
const mcpSrc = join(rootDir, "adapters", "mcp", "src", "index.ts");
|
|
56
|
+
if (existsSync(mcpSrc)) {
|
|
57
|
+
return { command: "npx", args: ["tsx", mcpSrc] };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return { command: "memrosetta-mcp", args: [] };
|
|
61
|
+
}
|
|
62
|
+
function resolveHookCommand(hookName) {
|
|
63
|
+
if (isInPath(hookName)) {
|
|
64
|
+
return hookName;
|
|
65
|
+
}
|
|
66
|
+
const hookFile = hookName === "memrosetta-on-stop" ? "hooks/on-stop.js" : "hooks/on-prompt.js";
|
|
67
|
+
try {
|
|
68
|
+
const require2 = createRequire(import.meta.url);
|
|
69
|
+
const cliPkgPath = dirname(
|
|
70
|
+
require2.resolve("@memrosetta/cli/package.json")
|
|
71
|
+
);
|
|
72
|
+
const entryPoint = join(cliPkgPath, "dist", hookFile);
|
|
73
|
+
if (existsSync(entryPoint)) {
|
|
74
|
+
return `node "${entryPoint}"`;
|
|
75
|
+
}
|
|
76
|
+
} catch {
|
|
77
|
+
}
|
|
78
|
+
const distHook = resolve(__dirname, "..", "..", "dist", hookFile);
|
|
79
|
+
if (existsSync(distHook)) {
|
|
80
|
+
return `node "${distHook}"`;
|
|
81
|
+
}
|
|
82
|
+
const workspaceRoot = findUpwards(__dirname, "pnpm-workspace.yaml");
|
|
83
|
+
if (workspaceRoot) {
|
|
84
|
+
const rootDir = dirname(workspaceRoot);
|
|
85
|
+
const hookEntry = join(rootDir, "packages", "cli", "dist", hookFile);
|
|
86
|
+
if (existsSync(hookEntry)) {
|
|
87
|
+
return `node "${hookEntry}"`;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return hookName;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/integrations/claude-code.ts
|
|
94
|
+
var CLAUDE_DIR = join2(homedir(), ".claude");
|
|
95
|
+
var CLAUDE_SETTINGS_PATH = join2(CLAUDE_DIR, "settings.json");
|
|
96
|
+
var CLAUDE_MD_PATH = join2(CLAUDE_DIR, "CLAUDE.md");
|
|
97
|
+
function isMemrosettaHook(command) {
|
|
98
|
+
return command.includes("memrosetta") && (command.includes("on-stop") || command.includes("on-prompt"));
|
|
99
|
+
}
|
|
100
|
+
function readClaudeSettings() {
|
|
101
|
+
if (!existsSync2(CLAUDE_SETTINGS_PATH)) return {};
|
|
102
|
+
try {
|
|
103
|
+
return JSON.parse(
|
|
104
|
+
readFileSync(CLAUDE_SETTINGS_PATH, "utf-8")
|
|
105
|
+
);
|
|
106
|
+
} catch {
|
|
107
|
+
return {};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function writeClaudeSettings(settings) {
|
|
111
|
+
if (!existsSync2(CLAUDE_DIR)) {
|
|
112
|
+
throw new Error(
|
|
113
|
+
"~/.claude directory does not exist. Is Claude Code installed?"
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
writeFileSync(
|
|
117
|
+
CLAUDE_SETTINGS_PATH,
|
|
118
|
+
JSON.stringify(settings, null, 2),
|
|
119
|
+
"utf-8"
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
function removeMemrosettaHooksFromSettings(settings) {
|
|
123
|
+
if (!settings.hooks) return settings;
|
|
124
|
+
const cleaned = {};
|
|
125
|
+
for (const [eventType, hookConfigs] of Object.entries(settings.hooks)) {
|
|
126
|
+
const filtered = hookConfigs.filter(
|
|
127
|
+
(hc) => !hc.hooks.some((h) => isMemrosettaHook(h.command))
|
|
128
|
+
);
|
|
129
|
+
cleaned[eventType] = filtered;
|
|
130
|
+
}
|
|
131
|
+
return { ...settings, hooks: cleaned };
|
|
132
|
+
}
|
|
133
|
+
function isClaudeCodeInstalled() {
|
|
134
|
+
return existsSync2(CLAUDE_DIR);
|
|
135
|
+
}
|
|
136
|
+
function isClaudeCodeConfigured() {
|
|
137
|
+
const settings = readClaudeSettings();
|
|
138
|
+
const stopHooks = settings.hooks?.["Stop"] || [];
|
|
139
|
+
return stopHooks.some(
|
|
140
|
+
(hc) => hc.hooks.some((h) => isMemrosettaHook(h.command))
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
function registerClaudeCodeHooks() {
|
|
144
|
+
if (!isClaudeCodeInstalled()) return false;
|
|
145
|
+
let settings = readClaudeSettings();
|
|
146
|
+
if (!settings.hooks) {
|
|
147
|
+
settings = { ...settings, hooks: {} };
|
|
148
|
+
}
|
|
149
|
+
settings = removeMemrosettaHooksFromSettings(settings);
|
|
150
|
+
const stopHookConfigs = settings.hooks["Stop"] || [];
|
|
151
|
+
settings.hooks["Stop"] = [
|
|
152
|
+
...stopHookConfigs,
|
|
153
|
+
{
|
|
154
|
+
matcher: "*",
|
|
155
|
+
hooks: [
|
|
156
|
+
{
|
|
157
|
+
type: "command",
|
|
158
|
+
command: resolveHookCommand("memrosetta-on-stop"),
|
|
159
|
+
timeout: 15
|
|
160
|
+
}
|
|
161
|
+
]
|
|
162
|
+
}
|
|
163
|
+
];
|
|
164
|
+
writeClaudeSettings(settings);
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
function removeClaudeCodeHooks() {
|
|
168
|
+
if (!existsSync2(CLAUDE_SETTINGS_PATH)) return false;
|
|
169
|
+
const settings = readClaudeSettings();
|
|
170
|
+
if (!settings.hooks) return false;
|
|
171
|
+
const cleaned = removeMemrosettaHooksFromSettings(settings);
|
|
172
|
+
writeClaudeSettings(cleaned);
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
function updateClaudeMd() {
|
|
176
|
+
if (!isClaudeCodeInstalled()) return false;
|
|
177
|
+
const marker = "## MemRosetta (Long-term Memory)";
|
|
178
|
+
const existing = existsSync2(CLAUDE_MD_PATH) ? readFileSync(CLAUDE_MD_PATH, "utf-8") : "";
|
|
179
|
+
if (existing.includes(marker)) return false;
|
|
180
|
+
const memorySection = `
|
|
181
|
+
|
|
182
|
+
${marker}
|
|
183
|
+
|
|
184
|
+
MCP server \`memory-service\` provides long-term memory across sessions.
|
|
185
|
+
userId defaults to the system username -- no need to specify it.
|
|
186
|
+
|
|
187
|
+
### Search (mcp__memory-service__memrosetta_search)
|
|
188
|
+
When you need information not in the current context, search past memories.
|
|
189
|
+
No need to specify userId -- it defaults to the system username.
|
|
190
|
+
|
|
191
|
+
### Store (mcp__memory-service__memrosetta_store)
|
|
192
|
+
|
|
193
|
+
**After EVERY response, run this checklist (zero extra cost):**
|
|
194
|
+
1. Did I encounter a DECISION? (tech choice, approach selection) -> store as "decision"
|
|
195
|
+
2. Did I learn a new FACT? (config, architecture, project info) -> store as "fact"
|
|
196
|
+
3. Did the user state a PREFERENCE? (style, tool choice, pattern) -> store as "preference"
|
|
197
|
+
4. Did we COMPLETE something? (deploy, migration, fix) -> store as "event"
|
|
198
|
+
5. None of the above? -> skip, do not store.
|
|
199
|
+
|
|
200
|
+
Always include 2-3 keywords. Example:
|
|
201
|
+
content: "Decided to use OAuth2 with PKCE for auth"
|
|
202
|
+
type: "decision"
|
|
203
|
+
keywords: "auth, oauth2, pkce"
|
|
204
|
+
|
|
205
|
+
Do NOT store:
|
|
206
|
+
- Code itself (belongs in git)
|
|
207
|
+
- File operations ("Created file X", "Modified Y")
|
|
208
|
+
- Debugging steps and attempts
|
|
209
|
+
- Simple confirmations or acknowledgments
|
|
210
|
+
- Implementation details (HOW you did it -- only WHAT)
|
|
211
|
+
|
|
212
|
+
This checklist ensures nothing important is lost, including the last response before session ends.
|
|
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
|
+
`;
|
|
221
|
+
writeFileSync(CLAUDE_MD_PATH, existing + memorySection, "utf-8");
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
function removeClaudeMdSection() {
|
|
225
|
+
if (!existsSync2(CLAUDE_MD_PATH)) return false;
|
|
226
|
+
const content = readFileSync(CLAUDE_MD_PATH, "utf-8");
|
|
227
|
+
const marker = "## MemRosetta (Long-term Memory)";
|
|
228
|
+
const markerIdx = content.indexOf(marker);
|
|
229
|
+
if (markerIdx === -1) return false;
|
|
230
|
+
const afterMarker = content.slice(markerIdx + marker.length);
|
|
231
|
+
const nextHeadingMatch = afterMarker.match(/\n## (?!MemRosetta)/);
|
|
232
|
+
const endIdx = nextHeadingMatch ? markerIdx + marker.length + (nextHeadingMatch.index ?? afterMarker.length) : content.length;
|
|
233
|
+
const before = content.slice(0, markerIdx).replace(/\n+$/, "");
|
|
234
|
+
const after = content.slice(endIdx);
|
|
235
|
+
const updated = (before + after).trimEnd() + "\n";
|
|
236
|
+
writeFileSync(CLAUDE_MD_PATH, updated, "utf-8");
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// src/integrations/mcp.ts
|
|
241
|
+
import { join as join3 } from "path";
|
|
242
|
+
import { homedir as homedir2 } from "os";
|
|
243
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
244
|
+
var MCP_CONFIG_PATH = join3(homedir2(), ".mcp.json");
|
|
245
|
+
var SERVER_NAME = "memory-service";
|
|
246
|
+
function readMcpConfig(path) {
|
|
247
|
+
if (!existsSync3(path)) return {};
|
|
248
|
+
try {
|
|
249
|
+
return JSON.parse(readFileSync2(path, "utf-8"));
|
|
250
|
+
} catch {
|
|
251
|
+
return {};
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
function writeMcpConfig(path, config) {
|
|
255
|
+
writeFileSync2(path, JSON.stringify(config, null, 2), "utf-8");
|
|
256
|
+
}
|
|
257
|
+
function mcpServerEntry() {
|
|
258
|
+
const { command, args } = resolveMcpCommand();
|
|
259
|
+
return { command, args };
|
|
260
|
+
}
|
|
261
|
+
function isGenericMCPConfigured() {
|
|
262
|
+
const config = readMcpConfig(MCP_CONFIG_PATH);
|
|
263
|
+
return !!config.mcpServers?.[SERVER_NAME];
|
|
264
|
+
}
|
|
265
|
+
function registerGenericMCP() {
|
|
266
|
+
const config = readMcpConfig(MCP_CONFIG_PATH);
|
|
267
|
+
const servers = config.mcpServers ?? {};
|
|
268
|
+
writeMcpConfig(MCP_CONFIG_PATH, {
|
|
269
|
+
...config,
|
|
270
|
+
mcpServers: { ...servers, [SERVER_NAME]: mcpServerEntry() }
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
function removeGenericMCP() {
|
|
274
|
+
if (!existsSync3(MCP_CONFIG_PATH)) return false;
|
|
275
|
+
const config = readMcpConfig(MCP_CONFIG_PATH);
|
|
276
|
+
if (!config.mcpServers?.[SERVER_NAME]) return false;
|
|
277
|
+
const { [SERVER_NAME]: _, ...rest } = config.mcpServers;
|
|
278
|
+
writeMcpConfig(MCP_CONFIG_PATH, { ...config, mcpServers: rest });
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
function getGenericMCPPath() {
|
|
282
|
+
return MCP_CONFIG_PATH;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// src/integrations/cursor.ts
|
|
286
|
+
import { join as join4 } from "path";
|
|
287
|
+
import { homedir as homedir3 } from "os";
|
|
288
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync } from "fs";
|
|
289
|
+
var SERVER_NAME2 = "memory-service";
|
|
290
|
+
var CURSOR_RULES_PATH_GETTER = () => join4(homedir3(), ".cursorrules");
|
|
291
|
+
var MEMROSETTA_CURSOR_RULES_MARKER = "## MemRosetta (Long-term Memory)";
|
|
292
|
+
var MEMROSETTA_CURSOR_RULES = `
|
|
293
|
+
|
|
294
|
+
${MEMROSETTA_CURSOR_RULES_MARKER}
|
|
295
|
+
|
|
296
|
+
MCP server \`memory-service\` provides persistent memory across sessions.
|
|
297
|
+
userId defaults to the system username -- no need to specify it.
|
|
298
|
+
|
|
299
|
+
### When to search (memrosetta_search)
|
|
300
|
+
When you need information not in the current context, search past memories.
|
|
301
|
+
No need to specify userId -- it defaults to the system username.
|
|
302
|
+
|
|
303
|
+
### When to store (memrosetta_store)
|
|
304
|
+
|
|
305
|
+
**After EVERY response, run this checklist (zero extra cost):**
|
|
306
|
+
1. Did I encounter a DECISION? (tech choice, approach selection) -> store as "decision"
|
|
307
|
+
2. Did I learn a new FACT? (config, architecture, project info) -> store as "fact"
|
|
308
|
+
3. Did the user state a PREFERENCE? (style, tool choice, pattern) -> store as "preference"
|
|
309
|
+
4. Did we COMPLETE something? (deploy, migration, fix) -> store as "event"
|
|
310
|
+
5. None of the above? -> skip, do not store.
|
|
311
|
+
|
|
312
|
+
Always include 2-3 keywords. Example:
|
|
313
|
+
content: "Decided to use OAuth2 with PKCE for auth"
|
|
314
|
+
type: "decision"
|
|
315
|
+
keywords: "auth, oauth2, pkce"
|
|
316
|
+
|
|
317
|
+
Do NOT store:
|
|
318
|
+
- Code itself (belongs in git)
|
|
319
|
+
- File operations ("Created file X", "Modified Y")
|
|
320
|
+
- Debugging steps and attempts
|
|
321
|
+
- Simple confirmations or acknowledgments
|
|
322
|
+
- Implementation details (HOW you did it -- only WHAT)
|
|
323
|
+
|
|
324
|
+
This checklist ensures nothing important is lost, including the last response before session ends.
|
|
325
|
+
No need to specify userId -- it defaults to the system username.
|
|
326
|
+
|
|
327
|
+
### When to relate (memrosetta_relate)
|
|
328
|
+
When new information updates or contradicts existing memories, create a relation.
|
|
329
|
+
|
|
330
|
+
### Working memory (memrosetta_working_memory)
|
|
331
|
+
Call this at the start of complex tasks to load relevant context.
|
|
332
|
+
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
|
+
`;
|
|
340
|
+
function getCursorConfigDir() {
|
|
341
|
+
return join4(homedir3(), ".cursor");
|
|
342
|
+
}
|
|
343
|
+
function getCursorMcpPath() {
|
|
344
|
+
return join4(getCursorConfigDir(), "mcp.json");
|
|
345
|
+
}
|
|
346
|
+
function readCursorConfig(path) {
|
|
347
|
+
if (!existsSync4(path)) return {};
|
|
348
|
+
try {
|
|
349
|
+
return JSON.parse(readFileSync3(path, "utf-8"));
|
|
350
|
+
} catch {
|
|
351
|
+
return {};
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
function writeCursorConfig(path, config) {
|
|
355
|
+
const dir = getCursorConfigDir();
|
|
356
|
+
if (!existsSync4(dir)) {
|
|
357
|
+
mkdirSync(dir, { recursive: true });
|
|
358
|
+
}
|
|
359
|
+
writeFileSync3(path, JSON.stringify(config, null, 2), "utf-8");
|
|
360
|
+
}
|
|
361
|
+
function mcpServerEntry2() {
|
|
362
|
+
const { command, args } = resolveMcpCommand();
|
|
363
|
+
return { command, args };
|
|
364
|
+
}
|
|
365
|
+
function isCursorConfigured() {
|
|
366
|
+
const path = getCursorMcpPath();
|
|
367
|
+
const config = readCursorConfig(path);
|
|
368
|
+
return !!config.mcpServers?.[SERVER_NAME2];
|
|
369
|
+
}
|
|
370
|
+
function registerCursorMCP() {
|
|
371
|
+
const path = getCursorMcpPath();
|
|
372
|
+
const config = readCursorConfig(path);
|
|
373
|
+
const servers = config.mcpServers ?? {};
|
|
374
|
+
writeCursorConfig(path, {
|
|
375
|
+
...config,
|
|
376
|
+
mcpServers: { ...servers, [SERVER_NAME2]: mcpServerEntry2() }
|
|
377
|
+
});
|
|
378
|
+
return updateCursorRules();
|
|
379
|
+
}
|
|
380
|
+
function removeCursorMCP() {
|
|
381
|
+
const path = getCursorMcpPath();
|
|
382
|
+
if (!existsSync4(path)) return false;
|
|
383
|
+
const config = readCursorConfig(path);
|
|
384
|
+
if (!config.mcpServers?.[SERVER_NAME2]) return false;
|
|
385
|
+
const { [SERVER_NAME2]: _, ...rest } = config.mcpServers;
|
|
386
|
+
writeCursorConfig(path, { ...config, mcpServers: rest });
|
|
387
|
+
removeCursorRulesSection();
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
function getCursorMcpConfigPath() {
|
|
391
|
+
return getCursorMcpPath();
|
|
392
|
+
}
|
|
393
|
+
function getCursorRulesPath() {
|
|
394
|
+
return CURSOR_RULES_PATH_GETTER();
|
|
395
|
+
}
|
|
396
|
+
function updateCursorRules() {
|
|
397
|
+
const rulesPath = CURSOR_RULES_PATH_GETTER();
|
|
398
|
+
const existing = existsSync4(rulesPath) ? readFileSync3(rulesPath, "utf-8") : "";
|
|
399
|
+
if (existing.includes(MEMROSETTA_CURSOR_RULES_MARKER)) return false;
|
|
400
|
+
writeFileSync3(rulesPath, existing + MEMROSETTA_CURSOR_RULES, "utf-8");
|
|
401
|
+
return true;
|
|
402
|
+
}
|
|
403
|
+
function removeCursorRulesSection() {
|
|
404
|
+
const rulesPath = CURSOR_RULES_PATH_GETTER();
|
|
405
|
+
if (!existsSync4(rulesPath)) return false;
|
|
406
|
+
const content = readFileSync3(rulesPath, "utf-8");
|
|
407
|
+
const markerIdx = content.indexOf(MEMROSETTA_CURSOR_RULES_MARKER);
|
|
408
|
+
if (markerIdx === -1) return false;
|
|
409
|
+
const afterMarker = content.slice(
|
|
410
|
+
markerIdx + MEMROSETTA_CURSOR_RULES_MARKER.length
|
|
411
|
+
);
|
|
412
|
+
const nextHeadingMatch = afterMarker.match(/\n## (?!MemRosetta)/);
|
|
413
|
+
const endIdx = nextHeadingMatch ? markerIdx + MEMROSETTA_CURSOR_RULES_MARKER.length + (nextHeadingMatch.index ?? afterMarker.length) : content.length;
|
|
414
|
+
const before = content.slice(0, markerIdx).replace(/\n+$/, "");
|
|
415
|
+
const after = content.slice(endIdx);
|
|
416
|
+
const updated = (before + after).trimEnd() + "\n";
|
|
417
|
+
writeFileSync3(rulesPath, updated, "utf-8");
|
|
418
|
+
return true;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// src/integrations/codex.ts
|
|
422
|
+
import { join as join5 } from "path";
|
|
423
|
+
import { homedir as homedir4 } from "os";
|
|
424
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync2 } from "fs";
|
|
425
|
+
var SERVER_NAME3 = "memory-service";
|
|
426
|
+
var CODEX_CONFIG_PATH_GETTER = () => join5(homedir4(), ".codex", "config.toml");
|
|
427
|
+
var AGENTS_MD_MARKER = "## MemRosetta (Long-term Memory)";
|
|
428
|
+
var MEMROSETTA_AGENTS_MD = `
|
|
429
|
+
|
|
430
|
+
${AGENTS_MD_MARKER}
|
|
431
|
+
|
|
432
|
+
MCP server \`memory-service\` provides persistent memory across sessions.
|
|
433
|
+
userId defaults to the system username -- no need to specify it.
|
|
434
|
+
|
|
435
|
+
### When to search (mcp__memory-service__memrosetta_search)
|
|
436
|
+
When you need information not in the current context, search past memories.
|
|
437
|
+
|
|
438
|
+
### When to store (mcp__memory-service__memrosetta_store)
|
|
439
|
+
|
|
440
|
+
**After EVERY response, run this checklist:**
|
|
441
|
+
1. Did I encounter a DECISION? (tech choice, approach selection) -> store as "decision"
|
|
442
|
+
2. Did I learn a new FACT? (config, architecture, project info) -> store as "fact"
|
|
443
|
+
3. Did the user state a PREFERENCE? (style, tool choice, pattern) -> store as "preference"
|
|
444
|
+
4. Did we COMPLETE something? (deploy, migration, fix) -> store as "event"
|
|
445
|
+
5. None of the above? -> skip, do not store.
|
|
446
|
+
|
|
447
|
+
Always include 2-3 keywords. Example:
|
|
448
|
+
content: "Decided to use OAuth2 with PKCE for auth"
|
|
449
|
+
type: "decision"
|
|
450
|
+
keywords: "auth, oauth2, pkce"
|
|
451
|
+
|
|
452
|
+
Do NOT store:
|
|
453
|
+
- Code itself (belongs in git)
|
|
454
|
+
- File operations ("Created file X", "Modified Y")
|
|
455
|
+
- Debugging steps and attempts
|
|
456
|
+
- Simple confirmations or acknowledgments
|
|
457
|
+
|
|
458
|
+
### When to relate (mcp__memory-service__memrosetta_relate)
|
|
459
|
+
When new information updates or contradicts existing memories, create a relation.
|
|
460
|
+
|
|
461
|
+
### Working memory (mcp__memory-service__memrosetta_working_memory)
|
|
462
|
+
Call this at the start of complex tasks to load relevant context.
|
|
463
|
+
`;
|
|
464
|
+
function getCodexConfigDir() {
|
|
465
|
+
return join5(homedir4(), ".codex");
|
|
466
|
+
}
|
|
467
|
+
function getCodexConfigPath() {
|
|
468
|
+
return CODEX_CONFIG_PATH_GETTER();
|
|
469
|
+
}
|
|
470
|
+
function readCodexConfig(path) {
|
|
471
|
+
if (!existsSync5(path)) return "";
|
|
472
|
+
try {
|
|
473
|
+
return readFileSync4(path, "utf-8");
|
|
474
|
+
} catch {
|
|
475
|
+
return "";
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
function escapeTomlString(s) {
|
|
479
|
+
return s.replace(/\\/g, "\\\\");
|
|
480
|
+
}
|
|
481
|
+
function buildMcpServerToml() {
|
|
482
|
+
const { command, args } = resolveMcpCommand();
|
|
483
|
+
const escapedCommand = escapeTomlString(command);
|
|
484
|
+
const argsLine = args.length > 0 ? `
|
|
485
|
+
args = [${args.map((a) => `"${escapeTomlString(a)}"`).join(", ")}]` : "";
|
|
486
|
+
return `
|
|
487
|
+
[mcp_servers.${SERVER_NAME3}]
|
|
488
|
+
command = "${escapedCommand}"${argsLine}
|
|
489
|
+
`;
|
|
490
|
+
}
|
|
491
|
+
function hasMcpServer(content) {
|
|
492
|
+
return content.includes(`[mcp_servers.${SERVER_NAME3}]`);
|
|
493
|
+
}
|
|
494
|
+
function removeMcpServerSection(content) {
|
|
495
|
+
const marker = `[mcp_servers.${SERVER_NAME3}]`;
|
|
496
|
+
const idx = content.indexOf(marker);
|
|
497
|
+
if (idx === -1) return content;
|
|
498
|
+
const afterMarker = content.slice(idx + marker.length);
|
|
499
|
+
const nextSectionMatch = afterMarker.match(/\n\[(?!mcp_servers\.memrosetta)/);
|
|
500
|
+
const endIdx = nextSectionMatch ? idx + marker.length + (nextSectionMatch.index ?? afterMarker.length) : content.length;
|
|
501
|
+
return (content.slice(0, idx).trimEnd() + "\n" + content.slice(endIdx)).trim() + "\n";
|
|
502
|
+
}
|
|
503
|
+
function isCodexConfigured() {
|
|
504
|
+
const content = readCodexConfig(getCodexConfigPath());
|
|
505
|
+
return hasMcpServer(content);
|
|
506
|
+
}
|
|
507
|
+
function registerCodexMCP() {
|
|
508
|
+
const configPath = getCodexConfigPath();
|
|
509
|
+
const dir = getCodexConfigDir();
|
|
510
|
+
if (!existsSync5(dir)) {
|
|
511
|
+
mkdirSync2(dir, { recursive: true });
|
|
512
|
+
}
|
|
513
|
+
let content = readCodexConfig(configPath);
|
|
514
|
+
if (hasMcpServer(content)) {
|
|
515
|
+
content = removeMcpServerSection(content);
|
|
516
|
+
}
|
|
517
|
+
writeFileSync4(configPath, content + buildMcpServerToml(), "utf-8");
|
|
518
|
+
return updateAgentsMd();
|
|
519
|
+
}
|
|
520
|
+
function removeCodexMCP() {
|
|
521
|
+
const configPath = getCodexConfigPath();
|
|
522
|
+
if (!existsSync5(configPath)) return false;
|
|
523
|
+
const content = readCodexConfig(configPath);
|
|
524
|
+
if (!hasMcpServer(content)) return false;
|
|
525
|
+
const lines = content.split("\n");
|
|
526
|
+
const filtered = [];
|
|
527
|
+
let skipping = false;
|
|
528
|
+
for (const line of lines) {
|
|
529
|
+
if (line.trim() === `[mcp_servers.${SERVER_NAME3}]`) {
|
|
530
|
+
skipping = true;
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
if (skipping && line.trim().startsWith("[")) {
|
|
534
|
+
skipping = false;
|
|
535
|
+
}
|
|
536
|
+
if (!skipping) {
|
|
537
|
+
filtered.push(line);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
writeFileSync4(configPath, filtered.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd() + "\n", "utf-8");
|
|
541
|
+
removeAgentsMdSection();
|
|
542
|
+
return true;
|
|
543
|
+
}
|
|
544
|
+
function getCodexConfigFilePath() {
|
|
545
|
+
return getCodexConfigPath();
|
|
546
|
+
}
|
|
547
|
+
function getAgentsMdPath() {
|
|
548
|
+
return join5(process.cwd(), "AGENTS.md");
|
|
549
|
+
}
|
|
550
|
+
function updateAgentsMd() {
|
|
551
|
+
const agentsPath = getAgentsMdPath();
|
|
552
|
+
const existing = existsSync5(agentsPath) ? readFileSync4(agentsPath, "utf-8") : "";
|
|
553
|
+
if (existing.includes(AGENTS_MD_MARKER)) return false;
|
|
554
|
+
writeFileSync4(agentsPath, existing + MEMROSETTA_AGENTS_MD, "utf-8");
|
|
555
|
+
return true;
|
|
556
|
+
}
|
|
557
|
+
function removeAgentsMdSection() {
|
|
558
|
+
const agentsPath = getAgentsMdPath();
|
|
559
|
+
if (!existsSync5(agentsPath)) return false;
|
|
560
|
+
const content = readFileSync4(agentsPath, "utf-8");
|
|
561
|
+
const markerIdx = content.indexOf(AGENTS_MD_MARKER);
|
|
562
|
+
if (markerIdx === -1) return false;
|
|
563
|
+
const afterMarker = content.slice(markerIdx + AGENTS_MD_MARKER.length);
|
|
564
|
+
const nextHeadingMatch = afterMarker.match(/\n## (?!MemRosetta)/);
|
|
565
|
+
const endIdx = nextHeadingMatch ? markerIdx + AGENTS_MD_MARKER.length + (nextHeadingMatch.index ?? afterMarker.length) : content.length;
|
|
566
|
+
const before = content.slice(0, markerIdx).replace(/\n+$/, "");
|
|
567
|
+
const after = content.slice(endIdx);
|
|
568
|
+
const updated = (before + after).trimEnd() + "\n";
|
|
569
|
+
writeFileSync4(agentsPath, updated, "utf-8");
|
|
570
|
+
return true;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
export {
|
|
574
|
+
isClaudeCodeInstalled,
|
|
575
|
+
isClaudeCodeConfigured,
|
|
576
|
+
registerClaudeCodeHooks,
|
|
577
|
+
removeClaudeCodeHooks,
|
|
578
|
+
updateClaudeMd,
|
|
579
|
+
removeClaudeMdSection,
|
|
580
|
+
isGenericMCPConfigured,
|
|
581
|
+
registerGenericMCP,
|
|
582
|
+
removeGenericMCP,
|
|
583
|
+
getGenericMCPPath,
|
|
584
|
+
isCursorConfigured,
|
|
585
|
+
registerCursorMCP,
|
|
586
|
+
removeCursorMCP,
|
|
587
|
+
getCursorMcpConfigPath,
|
|
588
|
+
getCursorRulesPath,
|
|
589
|
+
removeCursorRulesSection,
|
|
590
|
+
isCodexConfigured,
|
|
591
|
+
registerCodexMCP,
|
|
592
|
+
removeCodexMCP,
|
|
593
|
+
getCodexConfigFilePath,
|
|
594
|
+
getAgentsMdPath,
|
|
595
|
+
removeAgentsMdSection
|
|
596
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -150,17 +150,17 @@ async function main() {
|
|
|
150
150
|
break;
|
|
151
151
|
}
|
|
152
152
|
case "status": {
|
|
153
|
-
const mod = await import("./status-
|
|
153
|
+
const mod = await import("./status-PYD6U7U7.js");
|
|
154
154
|
await mod.run(commandOptions);
|
|
155
155
|
break;
|
|
156
156
|
}
|
|
157
157
|
case "init": {
|
|
158
|
-
const mod = await import("./init-
|
|
158
|
+
const mod = await import("./init-C335O4TX.js");
|
|
159
159
|
await mod.run(commandOptions);
|
|
160
160
|
break;
|
|
161
161
|
}
|
|
162
162
|
case "reset": {
|
|
163
|
-
const mod = await import("./reset-
|
|
163
|
+
const mod = await import("./reset-P63V46RN.js");
|
|
164
164
|
await mod.run(commandOptions);
|
|
165
165
|
break;
|
|
166
166
|
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getAgentsMdPath,
|
|
3
|
+
getCodexConfigFilePath,
|
|
4
|
+
getCursorMcpConfigPath,
|
|
5
|
+
getCursorRulesPath,
|
|
6
|
+
getGenericMCPPath,
|
|
7
|
+
isClaudeCodeInstalled,
|
|
8
|
+
registerClaudeCodeHooks,
|
|
9
|
+
registerCodexMCP,
|
|
10
|
+
registerCursorMCP,
|
|
11
|
+
registerGenericMCP,
|
|
12
|
+
updateClaudeMd
|
|
13
|
+
} from "./chunk-CATBN3ZT.js";
|
|
14
|
+
import {
|
|
15
|
+
hasFlag,
|
|
16
|
+
optionalOption
|
|
17
|
+
} from "./chunk-VZQURGWB.js";
|
|
18
|
+
import {
|
|
19
|
+
getDefaultDbPath,
|
|
20
|
+
getEngine
|
|
21
|
+
} from "./chunk-POK32V2J.js";
|
|
22
|
+
import {
|
|
23
|
+
output
|
|
24
|
+
} from "./chunk-ET6TNQOJ.js";
|
|
25
|
+
import {
|
|
26
|
+
getConfig,
|
|
27
|
+
writeConfig
|
|
28
|
+
} from "./chunk-TU5EHSDE.js";
|
|
29
|
+
|
|
30
|
+
// src/commands/init.ts
|
|
31
|
+
import { existsSync } from "fs";
|
|
32
|
+
var LANG_FLAG_TO_PRESET = {
|
|
33
|
+
en: "en",
|
|
34
|
+
multi: "multilingual",
|
|
35
|
+
ko: "ko"
|
|
36
|
+
};
|
|
37
|
+
async function run(options) {
|
|
38
|
+
const { args, format, db, noEmbeddings } = options;
|
|
39
|
+
const wantClaudeCode = hasFlag(args, "--claude-code");
|
|
40
|
+
const wantCursor = hasFlag(args, "--cursor");
|
|
41
|
+
const wantCodex = hasFlag(args, "--codex");
|
|
42
|
+
const langFlag = optionalOption(args, "--lang");
|
|
43
|
+
const embeddingPreset = langFlag ? LANG_FLAG_TO_PRESET[langFlag] : void 0;
|
|
44
|
+
if (langFlag && !LANG_FLAG_TO_PRESET[langFlag]) {
|
|
45
|
+
process.stderr.write(
|
|
46
|
+
`Unknown --lang value: "${langFlag}". Supported: en, multi, ko
|
|
47
|
+
`
|
|
48
|
+
);
|
|
49
|
+
process.exitCode = 1;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
{
|
|
53
|
+
const config2 = getConfig();
|
|
54
|
+
const updates = {};
|
|
55
|
+
if (db) {
|
|
56
|
+
updates.dbPath = db;
|
|
57
|
+
}
|
|
58
|
+
if (noEmbeddings) {
|
|
59
|
+
updates.enableEmbeddings = false;
|
|
60
|
+
}
|
|
61
|
+
if (embeddingPreset) {
|
|
62
|
+
updates.embeddingPreset = embeddingPreset;
|
|
63
|
+
}
|
|
64
|
+
if (Object.keys(updates).length > 0) {
|
|
65
|
+
writeConfig({ ...config2, ...updates });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const config = getConfig();
|
|
69
|
+
const dbPath = db ?? config.dbPath ?? getDefaultDbPath();
|
|
70
|
+
const existed = existsSync(dbPath);
|
|
71
|
+
const engine = await getEngine({ db: dbPath, noEmbeddings });
|
|
72
|
+
await engine.close();
|
|
73
|
+
const result = {
|
|
74
|
+
database: {
|
|
75
|
+
path: dbPath,
|
|
76
|
+
created: !existed
|
|
77
|
+
},
|
|
78
|
+
integrations: {}
|
|
79
|
+
};
|
|
80
|
+
registerGenericMCP();
|
|
81
|
+
result.integrations.mcp = {
|
|
82
|
+
registered: true,
|
|
83
|
+
path: getGenericMCPPath()
|
|
84
|
+
};
|
|
85
|
+
if (wantClaudeCode) {
|
|
86
|
+
const hooksOk = registerClaudeCodeHooks();
|
|
87
|
+
const claudeMdOk = updateClaudeMd();
|
|
88
|
+
result.integrations.claudeCode = {
|
|
89
|
+
hooks: hooksOk,
|
|
90
|
+
mcp: true,
|
|
91
|
+
claudeMd: claudeMdOk
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
if (wantCursor) {
|
|
95
|
+
const cursorRulesUpdated = registerCursorMCP();
|
|
96
|
+
result.integrations.cursor = {
|
|
97
|
+
mcp: true,
|
|
98
|
+
path: getCursorMcpConfigPath(),
|
|
99
|
+
cursorRules: cursorRulesUpdated,
|
|
100
|
+
cursorRulesPath: getCursorRulesPath()
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
if (wantCodex) {
|
|
104
|
+
const agentsMdUpdated = registerCodexMCP();
|
|
105
|
+
result.integrations.codex = {
|
|
106
|
+
mcp: true,
|
|
107
|
+
path: getCodexConfigFilePath(),
|
|
108
|
+
agentsMd: agentsMdUpdated,
|
|
109
|
+
agentsMdPath: getAgentsMdPath()
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (format === "text") {
|
|
113
|
+
printTextOutput(result, wantClaudeCode, wantCursor, wantCodex);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
output(result, format);
|
|
117
|
+
}
|
|
118
|
+
function printTextOutput(result, claudeCode, cursor, codex = false) {
|
|
119
|
+
const w = (s) => process.stdout.write(s);
|
|
120
|
+
w("\nMemRosetta initialized successfully.\n\n");
|
|
121
|
+
w(" What was set up:\n");
|
|
122
|
+
w(" ----------------------------------------\n");
|
|
123
|
+
w(` Database: ${result.database.path}`);
|
|
124
|
+
w(result.database.created ? " (created)\n" : " (already exists)\n");
|
|
125
|
+
w(` MCP Server: ${result.integrations.mcp.path} (always included)
|
|
126
|
+
`);
|
|
127
|
+
const currentConfig = getConfig();
|
|
128
|
+
if (currentConfig.embeddingPreset && currentConfig.embeddingPreset !== "en") {
|
|
129
|
+
const presetLabels = {
|
|
130
|
+
multilingual: "multilingual (multilingual-e5-small)",
|
|
131
|
+
ko: "Korean (ko-sroberta-multitask)"
|
|
132
|
+
};
|
|
133
|
+
w(` Embeddings: ${presetLabels[currentConfig.embeddingPreset] ?? currentConfig.embeddingPreset}
|
|
134
|
+
`);
|
|
135
|
+
}
|
|
136
|
+
if (claudeCode) {
|
|
137
|
+
const cc = result.integrations.claudeCode;
|
|
138
|
+
if (cc.hooks) {
|
|
139
|
+
w(" Stop Hook: ~/.claude/settings.json (auto-save on session end)\n");
|
|
140
|
+
} else if (!isClaudeCodeInstalled()) {
|
|
141
|
+
w(" Stop Hook: SKIPPED (Claude Code not found at ~/.claude)\n");
|
|
142
|
+
w(' Install Claude Code first, then run "memrosetta init --claude-code" again.\n');
|
|
143
|
+
}
|
|
144
|
+
if (cc.claudeMd) {
|
|
145
|
+
w(" CLAUDE.md: ~/.claude/CLAUDE.md (memory instructions added)\n");
|
|
146
|
+
} else {
|
|
147
|
+
w(" CLAUDE.md: already configured\n");
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (cursor) {
|
|
151
|
+
w(` Cursor MCP: ${result.integrations.cursor.path}
|
|
152
|
+
`);
|
|
153
|
+
if (result.integrations.cursor.cursorRules) {
|
|
154
|
+
w(` .cursorrules: ${result.integrations.cursor.cursorRulesPath} (memory instructions added)
|
|
155
|
+
`);
|
|
156
|
+
} else {
|
|
157
|
+
w(" .cursorrules: already configured\n");
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (codex) {
|
|
161
|
+
w(` Codex MCP: ${result.integrations.codex.path}
|
|
162
|
+
`);
|
|
163
|
+
if (result.integrations.codex.agentsMd) {
|
|
164
|
+
w(` AGENTS.md: ${result.integrations.codex.agentsMdPath} (memory instructions added)
|
|
165
|
+
`);
|
|
166
|
+
} else {
|
|
167
|
+
w(" AGENTS.md: already configured\n");
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
w("\n");
|
|
171
|
+
if (!claudeCode && !cursor && !codex) {
|
|
172
|
+
w(" MCP is ready. Add --claude-code, --cursor, or --codex for tool-specific setup.\n");
|
|
173
|
+
w(" Example: memrosetta init --claude-code\n");
|
|
174
|
+
w("\n");
|
|
175
|
+
}
|
|
176
|
+
if (claudeCode) {
|
|
177
|
+
w(" Restart Claude Code to activate.\n\n");
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
export {
|
|
181
|
+
run
|
|
182
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import {
|
|
2
|
+
removeAgentsMdSection,
|
|
3
|
+
removeClaudeCodeHooks,
|
|
4
|
+
removeClaudeMdSection,
|
|
5
|
+
removeCodexMCP,
|
|
6
|
+
removeCursorMCP,
|
|
7
|
+
removeCursorRulesSection,
|
|
8
|
+
removeGenericMCP
|
|
9
|
+
} from "./chunk-CATBN3ZT.js";
|
|
10
|
+
import {
|
|
11
|
+
hasFlag
|
|
12
|
+
} from "./chunk-VZQURGWB.js";
|
|
13
|
+
import {
|
|
14
|
+
output
|
|
15
|
+
} from "./chunk-ET6TNQOJ.js";
|
|
16
|
+
|
|
17
|
+
// src/commands/reset.ts
|
|
18
|
+
async function run(options) {
|
|
19
|
+
const { args, format } = options;
|
|
20
|
+
const wantClaudeCode = hasFlag(args, "--claude-code");
|
|
21
|
+
const wantCursor = hasFlag(args, "--cursor");
|
|
22
|
+
const wantCodex = hasFlag(args, "--codex");
|
|
23
|
+
const wantMCP = hasFlag(args, "--mcp");
|
|
24
|
+
const wantAll = hasFlag(args, "--all");
|
|
25
|
+
const noFlags = !wantClaudeCode && !wantCursor && !wantCodex && !wantMCP && !wantAll;
|
|
26
|
+
if (noFlags) {
|
|
27
|
+
const msg = "Usage: memrosetta reset [--claude-code] [--cursor] [--codex] [--mcp] [--all]\n\nFlags:\n --claude-code Remove Claude Code hooks, MCP, and CLAUDE.md section\n --cursor Remove Cursor MCP configuration\n --codex Remove Codex MCP configuration and AGENTS.md section\n --mcp Remove generic MCP configuration (~/.mcp.json)\n --all Remove all integrations\n";
|
|
28
|
+
if (format === "text") {
|
|
29
|
+
process.stdout.write(msg);
|
|
30
|
+
} else {
|
|
31
|
+
output({ error: "No flags specified. Use --claude-code, --cursor, --mcp, or --all." }, format);
|
|
32
|
+
}
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const result = {
|
|
36
|
+
removed: {
|
|
37
|
+
claudeCodeHooks: false,
|
|
38
|
+
claudeMd: false,
|
|
39
|
+
mcp: false,
|
|
40
|
+
cursor: false,
|
|
41
|
+
cursorRules: false,
|
|
42
|
+
codex: false,
|
|
43
|
+
agentsMd: false
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
if (wantClaudeCode || wantAll) {
|
|
47
|
+
const hooksRemoved = removeClaudeCodeHooks();
|
|
48
|
+
const mdRemoved = removeClaudeMdSection();
|
|
49
|
+
const mcpRemoved = removeGenericMCP();
|
|
50
|
+
result.removed.claudeCodeHooks = hooksRemoved;
|
|
51
|
+
result.removed.claudeMd = mdRemoved;
|
|
52
|
+
if (!wantMCP) {
|
|
53
|
+
result.removed.mcp = mcpRemoved;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (wantCursor || wantAll) {
|
|
57
|
+
const removed = removeCursorMCP();
|
|
58
|
+
result.removed.cursor = removed;
|
|
59
|
+
const rulesRemoved = removeCursorRulesSection();
|
|
60
|
+
result.removed.cursorRules = rulesRemoved;
|
|
61
|
+
}
|
|
62
|
+
if (wantCodex || wantAll) {
|
|
63
|
+
const removed = removeCodexMCP();
|
|
64
|
+
result.removed.codex = removed;
|
|
65
|
+
const mdRemoved = removeAgentsMdSection();
|
|
66
|
+
result.removed.agentsMd = mdRemoved;
|
|
67
|
+
}
|
|
68
|
+
if (wantMCP || wantAll) {
|
|
69
|
+
const removed = removeGenericMCP();
|
|
70
|
+
result.removed.mcp = removed;
|
|
71
|
+
}
|
|
72
|
+
if (format === "text") {
|
|
73
|
+
printTextOutput(result);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
output(result, format);
|
|
77
|
+
}
|
|
78
|
+
function printTextOutput(result) {
|
|
79
|
+
const w = (s) => process.stdout.write(s);
|
|
80
|
+
const removed = result.removed;
|
|
81
|
+
if (removed.claudeCodeHooks) {
|
|
82
|
+
w("Removed Claude Code hooks from ~/.claude/settings.json\n");
|
|
83
|
+
}
|
|
84
|
+
if (removed.claudeMd) {
|
|
85
|
+
w("Removed MemRosetta section from ~/.claude/CLAUDE.md\n");
|
|
86
|
+
}
|
|
87
|
+
if (removed.mcp) {
|
|
88
|
+
w("Removed MCP server from ~/.mcp.json\n");
|
|
89
|
+
}
|
|
90
|
+
if (removed.cursor) {
|
|
91
|
+
w("Removed Cursor MCP from ~/.cursor/mcp.json\n");
|
|
92
|
+
}
|
|
93
|
+
if (removed.cursorRules) {
|
|
94
|
+
w("Removed MemRosetta section from ~/.cursorrules\n");
|
|
95
|
+
}
|
|
96
|
+
if (removed.codex) {
|
|
97
|
+
w("Removed Codex MCP from ~/.codex/config.toml\n");
|
|
98
|
+
}
|
|
99
|
+
if (removed.agentsMd) {
|
|
100
|
+
w("Removed MemRosetta section from AGENTS.md\n");
|
|
101
|
+
}
|
|
102
|
+
const anyRemoved = removed.claudeCodeHooks || removed.claudeMd || removed.mcp || removed.cursor || removed.cursorRules || removed.codex || removed.agentsMd;
|
|
103
|
+
if (!anyRemoved) {
|
|
104
|
+
w("Nothing to remove (no integrations were configured).\n");
|
|
105
|
+
}
|
|
106
|
+
w(
|
|
107
|
+
"\nNote: ~/.memrosetta/ directory preserved. Delete manually if needed:\n rm -rf ~/.memrosetta\n"
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
export {
|
|
111
|
+
run
|
|
112
|
+
};
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isClaudeCodeConfigured,
|
|
3
|
+
isCodexConfigured,
|
|
4
|
+
isCursorConfigured,
|
|
5
|
+
isGenericMCPConfigured
|
|
6
|
+
} from "./chunk-CATBN3ZT.js";
|
|
7
|
+
import {
|
|
8
|
+
getDefaultDbPath
|
|
9
|
+
} from "./chunk-POK32V2J.js";
|
|
10
|
+
import {
|
|
11
|
+
output
|
|
12
|
+
} from "./chunk-ET6TNQOJ.js";
|
|
13
|
+
import {
|
|
14
|
+
getConfig
|
|
15
|
+
} from "./chunk-TU5EHSDE.js";
|
|
16
|
+
|
|
17
|
+
// src/commands/status.ts
|
|
18
|
+
import { existsSync, statSync, readFileSync } from "fs";
|
|
19
|
+
import { dirname, join } from "path";
|
|
20
|
+
import { fileURLToPath } from "url";
|
|
21
|
+
import { createRequire } from "module";
|
|
22
|
+
function getVersion() {
|
|
23
|
+
const strategies = [
|
|
24
|
+
// 1. Relative to source (dev/source checkout via tsx)
|
|
25
|
+
() => {
|
|
26
|
+
const require2 = createRequire(import.meta.url);
|
|
27
|
+
return require2("../../package.json").version;
|
|
28
|
+
},
|
|
29
|
+
// 2. Resolve from npm package
|
|
30
|
+
() => {
|
|
31
|
+
const require2 = createRequire(import.meta.url);
|
|
32
|
+
return require2("@memrosetta/cli/package.json").version;
|
|
33
|
+
},
|
|
34
|
+
// 3. Walk up to find package.json from current file location
|
|
35
|
+
() => {
|
|
36
|
+
const dir = dirname(fileURLToPath(import.meta.url));
|
|
37
|
+
for (let d = dir, i = 0; i < 5; i++) {
|
|
38
|
+
const candidate = join(d, "package.json");
|
|
39
|
+
if (existsSync(candidate)) {
|
|
40
|
+
const pkg = JSON.parse(readFileSync(candidate, "utf-8"));
|
|
41
|
+
if (pkg.name?.includes("memrosetta") && pkg.version) return pkg.version;
|
|
42
|
+
}
|
|
43
|
+
d = dirname(d);
|
|
44
|
+
}
|
|
45
|
+
throw new Error("not found");
|
|
46
|
+
}
|
|
47
|
+
];
|
|
48
|
+
for (const strategy of strategies) {
|
|
49
|
+
try {
|
|
50
|
+
return strategy();
|
|
51
|
+
} catch {
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return "unknown";
|
|
55
|
+
}
|
|
56
|
+
async function run(options) {
|
|
57
|
+
const { format, db, noEmbeddings } = options;
|
|
58
|
+
const config = getConfig();
|
|
59
|
+
const dbPath = db ?? config.dbPath ?? getDefaultDbPath();
|
|
60
|
+
const exists = existsSync(dbPath);
|
|
61
|
+
let sizeBytes = 0;
|
|
62
|
+
let sizeFormatted = "0B";
|
|
63
|
+
let memoryCount = 0;
|
|
64
|
+
let userList = [];
|
|
65
|
+
let qualityFresh = 0;
|
|
66
|
+
let qualityInvalidated = 0;
|
|
67
|
+
let qualityWithRelations = 0;
|
|
68
|
+
let qualityAvgActivation = 0;
|
|
69
|
+
const embeddingsEnabled = !noEmbeddings && config.enableEmbeddings !== false;
|
|
70
|
+
if (exists) {
|
|
71
|
+
const stat = statSync(dbPath);
|
|
72
|
+
sizeBytes = stat.size;
|
|
73
|
+
sizeFormatted = formatSize(sizeBytes);
|
|
74
|
+
try {
|
|
75
|
+
const Database = (await import("better-sqlite3")).default;
|
|
76
|
+
const dbConn = new Database(dbPath);
|
|
77
|
+
dbConn.pragma("journal_mode = WAL");
|
|
78
|
+
const countRow = dbConn.prepare("SELECT COUNT(*) as count FROM memories").get();
|
|
79
|
+
memoryCount = countRow.count;
|
|
80
|
+
const userRows = dbConn.prepare("SELECT DISTINCT user_id FROM memories ORDER BY user_id").all();
|
|
81
|
+
userList = userRows.map((r) => r.user_id);
|
|
82
|
+
const freshRow = dbConn.prepare(
|
|
83
|
+
"SELECT COUNT(*) as c FROM memories WHERE is_latest = 1 AND invalidated_at IS NULL"
|
|
84
|
+
).get();
|
|
85
|
+
qualityFresh = freshRow.c;
|
|
86
|
+
const invalidatedRow = dbConn.prepare(
|
|
87
|
+
"SELECT COUNT(*) as c FROM memories WHERE invalidated_at IS NOT NULL"
|
|
88
|
+
).get();
|
|
89
|
+
qualityInvalidated = invalidatedRow.c;
|
|
90
|
+
const relationsRow = dbConn.prepare(
|
|
91
|
+
"SELECT COUNT(DISTINCT src_memory_id) + COUNT(DISTINCT dst_memory_id) as c FROM memory_relations"
|
|
92
|
+
).get();
|
|
93
|
+
qualityWithRelations = relationsRow.c;
|
|
94
|
+
const avgRow = dbConn.prepare(
|
|
95
|
+
"SELECT AVG(activation_score) as avg FROM memories WHERE is_latest = 1"
|
|
96
|
+
).get();
|
|
97
|
+
qualityAvgActivation = avgRow.avg ?? 0;
|
|
98
|
+
dbConn.close();
|
|
99
|
+
} catch {
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const claudeCodeStatus = isClaudeCodeConfigured();
|
|
103
|
+
const cursorStatus = isCursorConfigured();
|
|
104
|
+
const codexStatus = isCodexConfigured();
|
|
105
|
+
const mcpStatus = isGenericMCPConfigured();
|
|
106
|
+
if (format === "text") {
|
|
107
|
+
process.stdout.write("MemRosetta Status\n");
|
|
108
|
+
process.stdout.write(`${"=".repeat(40)}
|
|
109
|
+
|
|
110
|
+
`);
|
|
111
|
+
process.stdout.write(
|
|
112
|
+
`Database: ${dbPath} (${exists ? `exists, ${sizeFormatted}` : "not found"})
|
|
113
|
+
`
|
|
114
|
+
);
|
|
115
|
+
process.stdout.write(`Memories: ${memoryCount}
|
|
116
|
+
`);
|
|
117
|
+
if (userList.length > 0) {
|
|
118
|
+
process.stdout.write(
|
|
119
|
+
`Users: ${userList.length} (${userList.join(", ")})
|
|
120
|
+
`
|
|
121
|
+
);
|
|
122
|
+
} else {
|
|
123
|
+
process.stdout.write("Users: 0\n");
|
|
124
|
+
}
|
|
125
|
+
const embeddingModelLabel = getEmbeddingModelLabel();
|
|
126
|
+
process.stdout.write(
|
|
127
|
+
`Embeddings: ${embeddingsEnabled ? `enabled (${embeddingModelLabel})` : "disabled"}
|
|
128
|
+
`
|
|
129
|
+
);
|
|
130
|
+
if (memoryCount > 0) {
|
|
131
|
+
process.stdout.write("\nQuality:\n");
|
|
132
|
+
process.stdout.write(
|
|
133
|
+
` Fresh (is_latest=1): ${qualityFresh} / ${memoryCount}
|
|
134
|
+
`
|
|
135
|
+
);
|
|
136
|
+
process.stdout.write(` Invalidated: ${qualityInvalidated}
|
|
137
|
+
`);
|
|
138
|
+
process.stdout.write(` With relations: ${qualityWithRelations}
|
|
139
|
+
`);
|
|
140
|
+
process.stdout.write(
|
|
141
|
+
` Avg activation: ${qualityAvgActivation.toFixed(2)}
|
|
142
|
+
`
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
process.stdout.write("\nIntegrations:\n");
|
|
146
|
+
process.stdout.write(
|
|
147
|
+
` Claude Code: ${claudeCodeStatus ? "configured (hooks + MCP)" : "not configured"}
|
|
148
|
+
`
|
|
149
|
+
);
|
|
150
|
+
process.stdout.write(
|
|
151
|
+
` Cursor: ${cursorStatus ? "configured (MCP)" : "not configured"}
|
|
152
|
+
`
|
|
153
|
+
);
|
|
154
|
+
process.stdout.write(
|
|
155
|
+
` Codex: ${codexStatus ? "configured (MCP)" : "not configured"}
|
|
156
|
+
`
|
|
157
|
+
);
|
|
158
|
+
process.stdout.write(
|
|
159
|
+
` MCP (generic): ${mcpStatus ? "configured" : "not configured"}
|
|
160
|
+
`
|
|
161
|
+
);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
output(
|
|
165
|
+
{
|
|
166
|
+
version: getVersion(),
|
|
167
|
+
database: {
|
|
168
|
+
path: dbPath,
|
|
169
|
+
exists,
|
|
170
|
+
sizeBytes,
|
|
171
|
+
sizeFormatted
|
|
172
|
+
},
|
|
173
|
+
memories: memoryCount,
|
|
174
|
+
users: userList,
|
|
175
|
+
quality: {
|
|
176
|
+
fresh: qualityFresh,
|
|
177
|
+
invalidated: qualityInvalidated,
|
|
178
|
+
withRelations: qualityWithRelations,
|
|
179
|
+
avgActivation: qualityAvgActivation
|
|
180
|
+
},
|
|
181
|
+
embeddings: embeddingsEnabled,
|
|
182
|
+
embeddingModel: getEmbeddingModelLabel(),
|
|
183
|
+
embeddingPreset: getConfig().embeddingPreset ?? "en",
|
|
184
|
+
integrations: {
|
|
185
|
+
claudeCode: claudeCodeStatus,
|
|
186
|
+
cursor: cursorStatus,
|
|
187
|
+
codex: codexStatus,
|
|
188
|
+
mcp: mcpStatus
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
format
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
function formatSize(bytes) {
|
|
195
|
+
if (bytes < 1024) return `${bytes}B`;
|
|
196
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
197
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
198
|
+
}
|
|
199
|
+
var PRESET_MODEL_LABELS = {
|
|
200
|
+
en: "bge-small-en-v1.5",
|
|
201
|
+
multilingual: "multilingual-e5-small",
|
|
202
|
+
ko: "ko-sroberta-multitask"
|
|
203
|
+
};
|
|
204
|
+
function getEmbeddingModelLabel() {
|
|
205
|
+
const config = getConfig();
|
|
206
|
+
const preset = config.embeddingPreset ?? "en";
|
|
207
|
+
return PRESET_MODEL_LABELS[preset] ?? preset;
|
|
208
|
+
}
|
|
209
|
+
export {
|
|
210
|
+
run
|
|
211
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memrosetta/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"better-sqlite3": "^11.0.0",
|
|
18
18
|
"@memrosetta/core": "0.3.0",
|
|
19
|
-
"@memrosetta/
|
|
20
|
-
"@memrosetta/
|
|
19
|
+
"@memrosetta/types": "0.3.0",
|
|
20
|
+
"@memrosetta/embeddings": "0.3.0"
|
|
21
21
|
},
|
|
22
22
|
"optionalDependencies": {
|
|
23
23
|
"@memrosetta/llm": "0.3.0"
|