@memrosetta/cli 0.5.0 → 0.5.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-4LNXT25H.js +891 -0
- package/dist/hooks/enforce-codex.js +88 -0
- package/dist/index.js +3 -3
- package/dist/init-ISP73KEC.js +210 -0
- package/dist/reset-P62B444X.js +132 -0
- package/dist/status-FWHUUZ4R.js +184 -0
- package/package.json +5 -4
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/hooks/enforce-codex.ts
|
|
4
|
+
import { spawnSync } from "child_process";
|
|
5
|
+
import { mkdtempSync, readFileSync, writeFileSync } from "fs";
|
|
6
|
+
import { tmpdir } from "os";
|
|
7
|
+
import { join } from "path";
|
|
8
|
+
function readStdinSync() {
|
|
9
|
+
try {
|
|
10
|
+
return readFileSync(0, "utf-8");
|
|
11
|
+
} catch {
|
|
12
|
+
return "";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function parseEvent(raw) {
|
|
16
|
+
if (!raw.trim()) return {};
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(raw);
|
|
19
|
+
} catch {
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function resolveMemrosettaCli() {
|
|
24
|
+
return process.env.MEMROSETTA_BIN ?? "memrosetta";
|
|
25
|
+
}
|
|
26
|
+
function main() {
|
|
27
|
+
try {
|
|
28
|
+
const stdin = readStdinSync();
|
|
29
|
+
const event = parseEvent(stdin);
|
|
30
|
+
if (event.stop_hook_active) {
|
|
31
|
+
process.stdout.write("{}\n");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const assistantMessage = event.last_assistant_message ?? "";
|
|
35
|
+
if (!assistantMessage.trim()) {
|
|
36
|
+
process.stdout.write("{}\n");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const dir = mkdtempSync(join(tmpdir(), "mr-enforce-codex-"));
|
|
40
|
+
const eventFile = join(dir, "event.json");
|
|
41
|
+
writeFileSync(
|
|
42
|
+
eventFile,
|
|
43
|
+
JSON.stringify({
|
|
44
|
+
client: "codex",
|
|
45
|
+
turnId: event.turn_id ?? event.session_id,
|
|
46
|
+
assistantMessage,
|
|
47
|
+
cwd: event.cwd,
|
|
48
|
+
transcriptPath: event.transcript_path ?? void 0,
|
|
49
|
+
attempt: 1
|
|
50
|
+
}),
|
|
51
|
+
"utf-8"
|
|
52
|
+
);
|
|
53
|
+
const cli = resolveMemrosettaCli();
|
|
54
|
+
const res = spawnSync(
|
|
55
|
+
cli,
|
|
56
|
+
["enforce", "stop", "--client", "codex", "--event-json", eventFile, "--format", "json"],
|
|
57
|
+
{ encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }
|
|
58
|
+
);
|
|
59
|
+
if (res.stderr) {
|
|
60
|
+
process.stderr.write(res.stderr);
|
|
61
|
+
}
|
|
62
|
+
let envelope = null;
|
|
63
|
+
if (res.stdout) {
|
|
64
|
+
try {
|
|
65
|
+
envelope = JSON.parse(res.stdout);
|
|
66
|
+
} catch {
|
|
67
|
+
envelope = null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (envelope?.status === "needs-continuation" && envelope.reason) {
|
|
71
|
+
process.stdout.write(
|
|
72
|
+
JSON.stringify({
|
|
73
|
+
decision: "block",
|
|
74
|
+
reason: `MemRosetta enforce: ${envelope.reason} \u2014 please revisit the turn and call memrosetta_store for anything worth keeping.`
|
|
75
|
+
}) + "\n"
|
|
76
|
+
);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
process.stdout.write("{}\n");
|
|
80
|
+
} catch (err) {
|
|
81
|
+
process.stderr.write(
|
|
82
|
+
`[memrosetta enforce codex wrapper] ${err instanceof Error ? err.message : String(err)}
|
|
83
|
+
`
|
|
84
|
+
);
|
|
85
|
+
process.stdout.write("{}\n");
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
main();
|
package/dist/index.js
CHANGED
|
@@ -177,17 +177,17 @@ async function main() {
|
|
|
177
177
|
break;
|
|
178
178
|
}
|
|
179
179
|
case "status": {
|
|
180
|
-
const mod = await import("./status-
|
|
180
|
+
const mod = await import("./status-FWHUUZ4R.js");
|
|
181
181
|
await mod.run(commandOptions);
|
|
182
182
|
break;
|
|
183
183
|
}
|
|
184
184
|
case "init": {
|
|
185
|
-
const mod = await import("./init-
|
|
185
|
+
const mod = await import("./init-ISP73KEC.js");
|
|
186
186
|
await mod.run(commandOptions);
|
|
187
187
|
break;
|
|
188
188
|
}
|
|
189
189
|
case "reset": {
|
|
190
|
-
const mod = await import("./reset-
|
|
190
|
+
const mod = await import("./reset-P62B444X.js");
|
|
191
191
|
await mod.run(commandOptions);
|
|
192
192
|
break;
|
|
193
193
|
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getAgentsMdPath,
|
|
3
|
+
getCodexConfigFilePath,
|
|
4
|
+
getCodexHooksPath,
|
|
5
|
+
getCursorMcpConfigPath,
|
|
6
|
+
getCursorRulesPath,
|
|
7
|
+
getGeminiMdPath,
|
|
8
|
+
getGeminiSettingsFilePath,
|
|
9
|
+
getGenericMCPPath,
|
|
10
|
+
isClaudeCodeInstalled,
|
|
11
|
+
registerClaudeCodeHooks,
|
|
12
|
+
registerCodexHooks,
|
|
13
|
+
registerCodexMCP,
|
|
14
|
+
registerCursorMCP,
|
|
15
|
+
registerGeminiMCP,
|
|
16
|
+
registerGenericMCP,
|
|
17
|
+
updateClaudeMd
|
|
18
|
+
} from "./chunk-4LNXT25H.js";
|
|
19
|
+
import {
|
|
20
|
+
hasFlag,
|
|
21
|
+
optionalOption
|
|
22
|
+
} from "./chunk-SYPVELIW.js";
|
|
23
|
+
import {
|
|
24
|
+
getDefaultDbPath,
|
|
25
|
+
getEngine
|
|
26
|
+
} from "./chunk-VAVUPQZA.js";
|
|
27
|
+
import {
|
|
28
|
+
output
|
|
29
|
+
} from "./chunk-ET6TNQOJ.js";
|
|
30
|
+
import {
|
|
31
|
+
getConfig,
|
|
32
|
+
writeConfig
|
|
33
|
+
} from "./chunk-SEPYQK3J.js";
|
|
34
|
+
|
|
35
|
+
// src/commands/init.ts
|
|
36
|
+
import { existsSync } from "fs";
|
|
37
|
+
var LANG_FLAG_TO_PRESET = {
|
|
38
|
+
en: "en",
|
|
39
|
+
multi: "multilingual",
|
|
40
|
+
ko: "ko"
|
|
41
|
+
};
|
|
42
|
+
async function run(options) {
|
|
43
|
+
const { args, format, db, noEmbeddings } = options;
|
|
44
|
+
const wantClaudeCode = hasFlag(args, "--claude-code");
|
|
45
|
+
const wantCursor = hasFlag(args, "--cursor");
|
|
46
|
+
const wantCodex = hasFlag(args, "--codex");
|
|
47
|
+
const wantGemini = hasFlag(args, "--gemini");
|
|
48
|
+
const langFlag = optionalOption(args, "--lang");
|
|
49
|
+
const embeddingPreset = langFlag ? LANG_FLAG_TO_PRESET[langFlag] : void 0;
|
|
50
|
+
if (langFlag && !LANG_FLAG_TO_PRESET[langFlag]) {
|
|
51
|
+
process.stderr.write(
|
|
52
|
+
`Unknown --lang value: "${langFlag}". Supported: en, multi, ko
|
|
53
|
+
`
|
|
54
|
+
);
|
|
55
|
+
process.exitCode = 1;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
{
|
|
59
|
+
const config2 = getConfig();
|
|
60
|
+
const updates = {};
|
|
61
|
+
if (db) {
|
|
62
|
+
updates.dbPath = db;
|
|
63
|
+
}
|
|
64
|
+
if (noEmbeddings) {
|
|
65
|
+
updates.enableEmbeddings = false;
|
|
66
|
+
}
|
|
67
|
+
if (embeddingPreset) {
|
|
68
|
+
updates.embeddingPreset = embeddingPreset;
|
|
69
|
+
}
|
|
70
|
+
if (Object.keys(updates).length > 0) {
|
|
71
|
+
writeConfig({ ...config2, ...updates });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const config = getConfig();
|
|
75
|
+
const dbPath = db ?? config.dbPath ?? getDefaultDbPath();
|
|
76
|
+
const existed = existsSync(dbPath);
|
|
77
|
+
const engine = await getEngine({ db: dbPath, noEmbeddings });
|
|
78
|
+
await engine.close();
|
|
79
|
+
const result = {
|
|
80
|
+
database: {
|
|
81
|
+
path: dbPath,
|
|
82
|
+
created: !existed
|
|
83
|
+
},
|
|
84
|
+
integrations: {}
|
|
85
|
+
};
|
|
86
|
+
registerGenericMCP();
|
|
87
|
+
result.integrations.mcp = {
|
|
88
|
+
registered: true,
|
|
89
|
+
path: getGenericMCPPath()
|
|
90
|
+
};
|
|
91
|
+
if (wantClaudeCode) {
|
|
92
|
+
const hooksOk = registerClaudeCodeHooks();
|
|
93
|
+
const claudeMdOk = updateClaudeMd();
|
|
94
|
+
result.integrations.claudeCode = {
|
|
95
|
+
hooks: hooksOk,
|
|
96
|
+
mcp: true,
|
|
97
|
+
claudeMd: claudeMdOk
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (wantCursor) {
|
|
101
|
+
const cursorRulesUpdated = registerCursorMCP();
|
|
102
|
+
result.integrations.cursor = {
|
|
103
|
+
mcp: true,
|
|
104
|
+
path: getCursorMcpConfigPath(),
|
|
105
|
+
cursorRules: cursorRulesUpdated,
|
|
106
|
+
cursorRulesPath: getCursorRulesPath()
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
if (wantCodex) {
|
|
110
|
+
const agentsMdUpdated = registerCodexMCP();
|
|
111
|
+
const hooksRegistered = registerCodexHooks();
|
|
112
|
+
result.integrations.codex = {
|
|
113
|
+
mcp: true,
|
|
114
|
+
path: getCodexConfigFilePath(),
|
|
115
|
+
agentsMd: agentsMdUpdated,
|
|
116
|
+
agentsMdPath: getAgentsMdPath(),
|
|
117
|
+
stopHook: hooksRegistered,
|
|
118
|
+
stopHookPath: getCodexHooksPath()
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
if (wantGemini) {
|
|
122
|
+
const geminiMdUpdated = registerGeminiMCP();
|
|
123
|
+
result.integrations.gemini = {
|
|
124
|
+
mcp: true,
|
|
125
|
+
path: getGeminiSettingsFilePath(),
|
|
126
|
+
geminiMd: geminiMdUpdated,
|
|
127
|
+
geminiMdPath: getGeminiMdPath()
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
if (format === "text") {
|
|
131
|
+
printTextOutput(result, wantClaudeCode, wantCursor, wantCodex, wantGemini);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
output(result, format);
|
|
135
|
+
}
|
|
136
|
+
function printTextOutput(result, claudeCode, cursor, codex = false, gemini = false) {
|
|
137
|
+
const w = (s) => process.stdout.write(s);
|
|
138
|
+
w("\nMemRosetta initialized successfully.\n\n");
|
|
139
|
+
w(" What was set up:\n");
|
|
140
|
+
w(" ----------------------------------------\n");
|
|
141
|
+
w(` Database: ${result.database.path}`);
|
|
142
|
+
w(result.database.created ? " (created)\n" : " (already exists)\n");
|
|
143
|
+
w(` MCP Server: ${result.integrations.mcp.path} (always included)
|
|
144
|
+
`);
|
|
145
|
+
const currentConfig = getConfig();
|
|
146
|
+
if (currentConfig.embeddingPreset && currentConfig.embeddingPreset !== "en") {
|
|
147
|
+
const presetLabels = {
|
|
148
|
+
multilingual: "multilingual (multilingual-e5-small)",
|
|
149
|
+
ko: "Korean (ko-sroberta-multitask)"
|
|
150
|
+
};
|
|
151
|
+
w(` Embeddings: ${presetLabels[currentConfig.embeddingPreset] ?? currentConfig.embeddingPreset}
|
|
152
|
+
`);
|
|
153
|
+
}
|
|
154
|
+
if (claudeCode) {
|
|
155
|
+
const cc = result.integrations.claudeCode;
|
|
156
|
+
if (cc.hooks) {
|
|
157
|
+
w(" Stop Hook: ~/.claude/settings.json (auto-save on session end)\n");
|
|
158
|
+
} else if (!isClaudeCodeInstalled()) {
|
|
159
|
+
w(" Stop Hook: SKIPPED (Claude Code not found at ~/.claude)\n");
|
|
160
|
+
w(' Install Claude Code first, then run "memrosetta init --claude-code" again.\n');
|
|
161
|
+
}
|
|
162
|
+
if (cc.claudeMd) {
|
|
163
|
+
w(" CLAUDE.md: ~/.claude/CLAUDE.md (memory instructions added)\n");
|
|
164
|
+
} else {
|
|
165
|
+
w(" CLAUDE.md: already configured\n");
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (cursor) {
|
|
169
|
+
w(` Cursor MCP: ${result.integrations.cursor.path}
|
|
170
|
+
`);
|
|
171
|
+
if (result.integrations.cursor.cursorRules) {
|
|
172
|
+
w(` .cursorrules: ${result.integrations.cursor.cursorRulesPath} (memory instructions added)
|
|
173
|
+
`);
|
|
174
|
+
} else {
|
|
175
|
+
w(" .cursorrules: already configured\n");
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (codex) {
|
|
179
|
+
w(` Codex MCP: ${result.integrations.codex.path}
|
|
180
|
+
`);
|
|
181
|
+
if (result.integrations.codex.agentsMd) {
|
|
182
|
+
w(` AGENTS.md: ${result.integrations.codex.agentsMdPath} (memory instructions added)
|
|
183
|
+
`);
|
|
184
|
+
} else {
|
|
185
|
+
w(" AGENTS.md: already configured\n");
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (gemini) {
|
|
189
|
+
w(` Gemini MCP: ${result.integrations.gemini.path}
|
|
190
|
+
`);
|
|
191
|
+
if (result.integrations.gemini.geminiMd) {
|
|
192
|
+
w(` GEMINI.md: ${result.integrations.gemini.geminiMdPath} (memory instructions added)
|
|
193
|
+
`);
|
|
194
|
+
} else {
|
|
195
|
+
w(" GEMINI.md: already configured\n");
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
w("\n");
|
|
199
|
+
if (!claudeCode && !cursor && !codex && !gemini) {
|
|
200
|
+
w(" MCP is ready. Add --claude-code, --cursor, --codex, or --gemini for tool-specific setup.\n");
|
|
201
|
+
w(" Example: memrosetta init --claude-code\n");
|
|
202
|
+
w("\n");
|
|
203
|
+
}
|
|
204
|
+
if (claudeCode) {
|
|
205
|
+
w(" Restart Claude Code to activate.\n\n");
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
export {
|
|
209
|
+
run
|
|
210
|
+
};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import {
|
|
2
|
+
removeAgentsMdSection,
|
|
3
|
+
removeClaudeCodeHooks,
|
|
4
|
+
removeClaudeMdSection,
|
|
5
|
+
removeCodexHooks,
|
|
6
|
+
removeCodexMCP,
|
|
7
|
+
removeCursorMCP,
|
|
8
|
+
removeCursorRulesSection,
|
|
9
|
+
removeGeminiMCP,
|
|
10
|
+
removeGeminiMdSection,
|
|
11
|
+
removeGenericMCP
|
|
12
|
+
} from "./chunk-4LNXT25H.js";
|
|
13
|
+
import {
|
|
14
|
+
hasFlag
|
|
15
|
+
} from "./chunk-SYPVELIW.js";
|
|
16
|
+
import {
|
|
17
|
+
output
|
|
18
|
+
} from "./chunk-ET6TNQOJ.js";
|
|
19
|
+
|
|
20
|
+
// src/commands/reset.ts
|
|
21
|
+
async function run(options) {
|
|
22
|
+
const { args, format } = options;
|
|
23
|
+
const wantClaudeCode = hasFlag(args, "--claude-code");
|
|
24
|
+
const wantCursor = hasFlag(args, "--cursor");
|
|
25
|
+
const wantCodex = hasFlag(args, "--codex");
|
|
26
|
+
const wantGemini = hasFlag(args, "--gemini");
|
|
27
|
+
const wantMCP = hasFlag(args, "--mcp");
|
|
28
|
+
const wantAll = hasFlag(args, "--all");
|
|
29
|
+
const noFlags = !wantClaudeCode && !wantCursor && !wantCodex && !wantGemini && !wantMCP && !wantAll;
|
|
30
|
+
if (noFlags) {
|
|
31
|
+
const msg = "Usage: memrosetta reset [--claude-code] [--cursor] [--codex] [--gemini] [--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 --gemini Remove Gemini MCP configuration and GEMINI.md section\n --mcp Remove generic MCP configuration (~/.mcp.json)\n --all Remove all integrations\n";
|
|
32
|
+
if (format === "text") {
|
|
33
|
+
process.stdout.write(msg);
|
|
34
|
+
} else {
|
|
35
|
+
output({ error: "No flags specified. Use --claude-code, --cursor, --codex, --gemini, --mcp, or --all." }, format);
|
|
36
|
+
}
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const result = {
|
|
40
|
+
removed: {
|
|
41
|
+
claudeCodeHooks: false,
|
|
42
|
+
claudeMd: false,
|
|
43
|
+
mcp: false,
|
|
44
|
+
cursor: false,
|
|
45
|
+
cursorRules: false,
|
|
46
|
+
codex: false,
|
|
47
|
+
agentsMd: false,
|
|
48
|
+
gemini: false,
|
|
49
|
+
geminiMd: false
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
if (wantClaudeCode || wantAll) {
|
|
53
|
+
const hooksRemoved = removeClaudeCodeHooks();
|
|
54
|
+
const mdRemoved = removeClaudeMdSection();
|
|
55
|
+
const mcpRemoved = removeGenericMCP();
|
|
56
|
+
result.removed.claudeCodeHooks = hooksRemoved;
|
|
57
|
+
result.removed.claudeMd = mdRemoved;
|
|
58
|
+
if (!wantMCP) {
|
|
59
|
+
result.removed.mcp = mcpRemoved;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (wantCursor || wantAll) {
|
|
63
|
+
const removed = removeCursorMCP();
|
|
64
|
+
result.removed.cursor = removed;
|
|
65
|
+
const rulesRemoved = removeCursorRulesSection();
|
|
66
|
+
result.removed.cursorRules = rulesRemoved;
|
|
67
|
+
}
|
|
68
|
+
if (wantCodex || wantAll) {
|
|
69
|
+
const removed = removeCodexMCP();
|
|
70
|
+
result.removed.codex = removed;
|
|
71
|
+
const mdRemoved = removeAgentsMdSection();
|
|
72
|
+
result.removed.agentsMd = mdRemoved;
|
|
73
|
+
const stopHookRemoved = removeCodexHooks();
|
|
74
|
+
result.removed.codexStopHook = stopHookRemoved;
|
|
75
|
+
}
|
|
76
|
+
if (wantGemini || wantAll) {
|
|
77
|
+
const removed = removeGeminiMCP();
|
|
78
|
+
result.removed.gemini = removed;
|
|
79
|
+
const mdRemoved = removeGeminiMdSection();
|
|
80
|
+
result.removed.geminiMd = mdRemoved;
|
|
81
|
+
}
|
|
82
|
+
if (wantMCP || wantAll) {
|
|
83
|
+
const removed = removeGenericMCP();
|
|
84
|
+
result.removed.mcp = removed;
|
|
85
|
+
}
|
|
86
|
+
if (format === "text") {
|
|
87
|
+
printTextOutput(result);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
output(result, format);
|
|
91
|
+
}
|
|
92
|
+
function printTextOutput(result) {
|
|
93
|
+
const w = (s) => process.stdout.write(s);
|
|
94
|
+
const removed = result.removed;
|
|
95
|
+
if (removed.claudeCodeHooks) {
|
|
96
|
+
w("Removed Claude Code hooks from ~/.claude/settings.json\n");
|
|
97
|
+
}
|
|
98
|
+
if (removed.claudeMd) {
|
|
99
|
+
w("Removed MemRosetta section from ~/.claude/CLAUDE.md\n");
|
|
100
|
+
}
|
|
101
|
+
if (removed.mcp) {
|
|
102
|
+
w("Removed MCP server from ~/.mcp.json\n");
|
|
103
|
+
}
|
|
104
|
+
if (removed.cursor) {
|
|
105
|
+
w("Removed Cursor MCP from ~/.cursor/mcp.json\n");
|
|
106
|
+
}
|
|
107
|
+
if (removed.cursorRules) {
|
|
108
|
+
w("Removed MemRosetta section from ~/.cursorrules\n");
|
|
109
|
+
}
|
|
110
|
+
if (removed.codex) {
|
|
111
|
+
w("Removed Codex MCP from ~/.codex/config.toml\n");
|
|
112
|
+
}
|
|
113
|
+
if (removed.agentsMd) {
|
|
114
|
+
w("Removed MemRosetta section from AGENTS.md\n");
|
|
115
|
+
}
|
|
116
|
+
if (removed.gemini) {
|
|
117
|
+
w("Removed Gemini MCP from ~/.gemini/settings.json\n");
|
|
118
|
+
}
|
|
119
|
+
if (removed.geminiMd) {
|
|
120
|
+
w("Removed MemRosetta section from GEMINI.md\n");
|
|
121
|
+
}
|
|
122
|
+
const anyRemoved = removed.claudeCodeHooks || removed.claudeMd || removed.mcp || removed.cursor || removed.cursorRules || removed.codex || removed.agentsMd || removed.gemini || removed.geminiMd;
|
|
123
|
+
if (!anyRemoved) {
|
|
124
|
+
w("Nothing to remove (no integrations were configured).\n");
|
|
125
|
+
}
|
|
126
|
+
w(
|
|
127
|
+
"\nNote: ~/.memrosetta/ directory preserved. Delete manually if needed:\n rm -rf ~/.memrosetta\n"
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
export {
|
|
131
|
+
run
|
|
132
|
+
};
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isClaudeCodeConfigured,
|
|
3
|
+
isCodexConfigured,
|
|
4
|
+
isCursorConfigured,
|
|
5
|
+
isGeminiConfigured,
|
|
6
|
+
isGenericMCPConfigured
|
|
7
|
+
} from "./chunk-4LNXT25H.js";
|
|
8
|
+
import {
|
|
9
|
+
resolveCliVersion
|
|
10
|
+
} from "./chunk-YXK6FDB6.js";
|
|
11
|
+
import {
|
|
12
|
+
getDefaultDbPath
|
|
13
|
+
} from "./chunk-VAVUPQZA.js";
|
|
14
|
+
import {
|
|
15
|
+
output
|
|
16
|
+
} from "./chunk-ET6TNQOJ.js";
|
|
17
|
+
import {
|
|
18
|
+
getConfig
|
|
19
|
+
} from "./chunk-SEPYQK3J.js";
|
|
20
|
+
|
|
21
|
+
// src/commands/status.ts
|
|
22
|
+
import { existsSync, statSync } from "fs";
|
|
23
|
+
async function run(options) {
|
|
24
|
+
const { format, db, noEmbeddings } = options;
|
|
25
|
+
const config = getConfig();
|
|
26
|
+
const dbPath = db ?? config.dbPath ?? getDefaultDbPath();
|
|
27
|
+
const exists = existsSync(dbPath);
|
|
28
|
+
let sizeBytes = 0;
|
|
29
|
+
let sizeFormatted = "0B";
|
|
30
|
+
let memoryCount = 0;
|
|
31
|
+
let userList = [];
|
|
32
|
+
let qualityFresh = 0;
|
|
33
|
+
let qualityInvalidated = 0;
|
|
34
|
+
let qualityWithRelations = 0;
|
|
35
|
+
let qualityAvgActivation = 0;
|
|
36
|
+
const embeddingsEnabled = !noEmbeddings && config.enableEmbeddings !== false;
|
|
37
|
+
if (exists) {
|
|
38
|
+
const stat = statSync(dbPath);
|
|
39
|
+
sizeBytes = stat.size;
|
|
40
|
+
sizeFormatted = formatSize(sizeBytes);
|
|
41
|
+
try {
|
|
42
|
+
const Database = (await import("better-sqlite3")).default;
|
|
43
|
+
const dbConn = new Database(dbPath);
|
|
44
|
+
dbConn.pragma("journal_mode = WAL");
|
|
45
|
+
const countRow = dbConn.prepare("SELECT COUNT(*) as count FROM memories").get();
|
|
46
|
+
memoryCount = countRow.count;
|
|
47
|
+
const userRows = dbConn.prepare("SELECT DISTINCT user_id FROM memories ORDER BY user_id").all();
|
|
48
|
+
userList = userRows.map((r) => r.user_id);
|
|
49
|
+
const freshRow = dbConn.prepare(
|
|
50
|
+
"SELECT COUNT(*) as c FROM memories WHERE is_latest = 1 AND invalidated_at IS NULL"
|
|
51
|
+
).get();
|
|
52
|
+
qualityFresh = freshRow.c;
|
|
53
|
+
const invalidatedRow = dbConn.prepare(
|
|
54
|
+
"SELECT COUNT(*) as c FROM memories WHERE invalidated_at IS NOT NULL"
|
|
55
|
+
).get();
|
|
56
|
+
qualityInvalidated = invalidatedRow.c;
|
|
57
|
+
const relationsRow = dbConn.prepare(
|
|
58
|
+
"SELECT COUNT(DISTINCT src_memory_id) + COUNT(DISTINCT dst_memory_id) as c FROM memory_relations"
|
|
59
|
+
).get();
|
|
60
|
+
qualityWithRelations = relationsRow.c;
|
|
61
|
+
const avgRow = dbConn.prepare(
|
|
62
|
+
"SELECT AVG(activation_score) as avg FROM memories WHERE is_latest = 1"
|
|
63
|
+
).get();
|
|
64
|
+
qualityAvgActivation = avgRow.avg ?? 0;
|
|
65
|
+
dbConn.close();
|
|
66
|
+
} catch {
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const claudeCodeStatus = isClaudeCodeConfigured();
|
|
70
|
+
const cursorStatus = isCursorConfigured();
|
|
71
|
+
const codexStatus = isCodexConfigured();
|
|
72
|
+
const geminiStatus = isGeminiConfigured();
|
|
73
|
+
const mcpStatus = isGenericMCPConfigured();
|
|
74
|
+
if (format === "text") {
|
|
75
|
+
process.stdout.write("MemRosetta Status\n");
|
|
76
|
+
process.stdout.write(`${"=".repeat(40)}
|
|
77
|
+
|
|
78
|
+
`);
|
|
79
|
+
process.stdout.write(
|
|
80
|
+
`Database: ${dbPath} (${exists ? `exists, ${sizeFormatted}` : "not found"})
|
|
81
|
+
`
|
|
82
|
+
);
|
|
83
|
+
process.stdout.write(`Memories: ${memoryCount}
|
|
84
|
+
`);
|
|
85
|
+
if (userList.length > 0) {
|
|
86
|
+
process.stdout.write(
|
|
87
|
+
`Users: ${userList.length} (${userList.join(", ")})
|
|
88
|
+
`
|
|
89
|
+
);
|
|
90
|
+
} else {
|
|
91
|
+
process.stdout.write("Users: 0\n");
|
|
92
|
+
}
|
|
93
|
+
const embeddingModelLabel = getEmbeddingModelLabel();
|
|
94
|
+
process.stdout.write(
|
|
95
|
+
`Embeddings: ${embeddingsEnabled ? `enabled (${embeddingModelLabel})` : "disabled"}
|
|
96
|
+
`
|
|
97
|
+
);
|
|
98
|
+
if (memoryCount > 0) {
|
|
99
|
+
process.stdout.write("\nQuality:\n");
|
|
100
|
+
process.stdout.write(
|
|
101
|
+
` Fresh (is_latest=1): ${qualityFresh} / ${memoryCount}
|
|
102
|
+
`
|
|
103
|
+
);
|
|
104
|
+
process.stdout.write(` Invalidated: ${qualityInvalidated}
|
|
105
|
+
`);
|
|
106
|
+
process.stdout.write(` With relations: ${qualityWithRelations}
|
|
107
|
+
`);
|
|
108
|
+
process.stdout.write(
|
|
109
|
+
` Avg activation: ${qualityAvgActivation.toFixed(2)}
|
|
110
|
+
`
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
process.stdout.write("\nIntegrations:\n");
|
|
114
|
+
process.stdout.write(
|
|
115
|
+
` Claude Code: ${claudeCodeStatus ? "configured (hooks + MCP)" : "not configured"}
|
|
116
|
+
`
|
|
117
|
+
);
|
|
118
|
+
process.stdout.write(
|
|
119
|
+
` Cursor: ${cursorStatus ? "configured (MCP)" : "not configured"}
|
|
120
|
+
`
|
|
121
|
+
);
|
|
122
|
+
process.stdout.write(
|
|
123
|
+
` Codex: ${codexStatus ? "configured (MCP)" : "not configured"}
|
|
124
|
+
`
|
|
125
|
+
);
|
|
126
|
+
process.stdout.write(
|
|
127
|
+
` Gemini: ${geminiStatus ? "configured (MCP)" : "not configured"}
|
|
128
|
+
`
|
|
129
|
+
);
|
|
130
|
+
process.stdout.write(
|
|
131
|
+
` MCP (generic): ${mcpStatus ? "configured" : "not configured"}
|
|
132
|
+
`
|
|
133
|
+
);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
output(
|
|
137
|
+
{
|
|
138
|
+
version: resolveCliVersion(),
|
|
139
|
+
database: {
|
|
140
|
+
path: dbPath,
|
|
141
|
+
exists,
|
|
142
|
+
sizeBytes,
|
|
143
|
+
sizeFormatted
|
|
144
|
+
},
|
|
145
|
+
memories: memoryCount,
|
|
146
|
+
users: userList,
|
|
147
|
+
quality: {
|
|
148
|
+
fresh: qualityFresh,
|
|
149
|
+
invalidated: qualityInvalidated,
|
|
150
|
+
withRelations: qualityWithRelations,
|
|
151
|
+
avgActivation: qualityAvgActivation
|
|
152
|
+
},
|
|
153
|
+
embeddings: embeddingsEnabled,
|
|
154
|
+
embeddingModel: getEmbeddingModelLabel(),
|
|
155
|
+
embeddingPreset: getConfig().embeddingPreset ?? "en",
|
|
156
|
+
integrations: {
|
|
157
|
+
claudeCode: claudeCodeStatus,
|
|
158
|
+
cursor: cursorStatus,
|
|
159
|
+
codex: codexStatus,
|
|
160
|
+
gemini: geminiStatus,
|
|
161
|
+
mcp: mcpStatus
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
format
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
function formatSize(bytes) {
|
|
168
|
+
if (bytes < 1024) return `${bytes}B`;
|
|
169
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
170
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
171
|
+
}
|
|
172
|
+
var PRESET_MODEL_LABELS = {
|
|
173
|
+
en: "bge-small-en-v1.5",
|
|
174
|
+
multilingual: "multilingual-e5-small",
|
|
175
|
+
ko: "ko-sroberta-multitask"
|
|
176
|
+
};
|
|
177
|
+
function getEmbeddingModelLabel() {
|
|
178
|
+
const config = getConfig();
|
|
179
|
+
const preset = config.embeddingPreset ?? "en";
|
|
180
|
+
return PRESET_MODEL_LABELS[preset] ?? preset;
|
|
181
|
+
}
|
|
182
|
+
export {
|
|
183
|
+
run
|
|
184
|
+
};
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memrosetta/cli",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"memrosetta": "./dist/index.js",
|
|
8
8
|
"memrosetta-on-stop": "./dist/hooks/on-stop.js",
|
|
9
9
|
"memrosetta-on-prompt": "./dist/hooks/on-prompt.js",
|
|
10
|
-
"memrosetta-enforce-claude-code": "./dist/hooks/enforce-claude-code.js"
|
|
10
|
+
"memrosetta-enforce-claude-code": "./dist/hooks/enforce-claude-code.js",
|
|
11
|
+
"memrosetta-enforce-codex": "./dist/hooks/enforce-codex.js"
|
|
11
12
|
},
|
|
12
13
|
"files": [
|
|
13
14
|
"dist",
|
|
@@ -16,8 +17,8 @@
|
|
|
16
17
|
],
|
|
17
18
|
"dependencies": {
|
|
18
19
|
"better-sqlite3": "^11.0.0",
|
|
19
|
-
"@memrosetta/core": "0.4.0",
|
|
20
20
|
"@memrosetta/embeddings": "0.3.0",
|
|
21
|
+
"@memrosetta/core": "0.4.0",
|
|
21
22
|
"@memrosetta/types": "0.4.0",
|
|
22
23
|
"@memrosetta/sync-client": "0.1.5"
|
|
23
24
|
},
|
|
@@ -31,7 +32,7 @@
|
|
|
31
32
|
"vitest": "^2.0.0"
|
|
32
33
|
},
|
|
33
34
|
"scripts": {
|
|
34
|
-
"build": "tsup src/index.ts src/hooks/on-stop.ts src/hooks/on-prompt.ts src/hooks/enforce-claude-code.ts --format esm --external better-sqlite3 --external @memrosetta/core --external @memrosetta/embeddings --external @memrosetta/sync-client --external @memrosetta/types --external @memrosetta/llm --external @memrosetta/extractor --external @huggingface/transformers --external sqlite-vec --external onnxruntime-node",
|
|
35
|
+
"build": "tsup src/index.ts src/hooks/on-stop.ts src/hooks/on-prompt.ts src/hooks/enforce-claude-code.ts src/hooks/enforce-codex.ts --format esm --external better-sqlite3 --external @memrosetta/core --external @memrosetta/embeddings --external @memrosetta/sync-client --external @memrosetta/types --external @memrosetta/llm --external @memrosetta/extractor --external @huggingface/transformers --external sqlite-vec --external onnxruntime-node",
|
|
35
36
|
"start": "node dist/index.js",
|
|
36
37
|
"test": "vitest run",
|
|
37
38
|
"test:watch": "vitest",
|