@hasna/mementos 0.14.22 → 0.14.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -2
- package/dist/agent-setup.d.ts.map +1 -1
- package/dist/cli.js +282 -7
- package/dist/index.js +242 -6
- package/dist/mcp-runtime.d.ts +1 -0
- package/dist/mcp-runtime.d.ts.map +1 -1
- package/dist/mcp.js +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,8 +18,9 @@ mementos auth signup
|
|
|
18
18
|
mementos setup agents
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
`mementos setup agents`
|
|
22
|
-
Codex, OpenCode, and Antigravity
|
|
21
|
+
`mementos setup agents` registers the hosted `mementos-mcp` server in Claude
|
|
22
|
+
Code, Codex, OpenCode, and Antigravity config files. It also writes managed
|
|
23
|
+
diagnostic snippets under `~/.config/mementos/agent-integrations`.
|
|
23
24
|
|
|
24
25
|
## MCP
|
|
25
26
|
|
|
@@ -36,3 +37,9 @@ MEMENTOS_API_TOKEN=<token> mementos inject "project context"
|
|
|
36
37
|
```
|
|
37
38
|
|
|
38
39
|
The command prints a prompt-ready memory block from the hosted API.
|
|
40
|
+
|
|
41
|
+
Claude Code also receives `UserPromptSubmit`, `SessionStart`, and `Stop` hook
|
|
42
|
+
entries that call `mementos hook ...` and return hosted memory as hook context.
|
|
43
|
+
Codex does not expose a native hook file in `~/.codex/config.toml`, so the
|
|
44
|
+
installer registers MCP there and keeps the injection command in the managed
|
|
45
|
+
integration snippets.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-setup.d.ts","sourceRoot":"","sources":["../src/agent-setup.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,qBAAqB,8DAA+D,CAAC;AAElG,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAExE,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,WAAW,CAAC;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,kBAAkB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,kBAAkB,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;CAChF;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,WAAW,EAAE,oBAAoB,EAAE,CAAC;IACpC,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,iBAAsB,GAAG,cAAc,CA0CnF;AAED,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,iBAAsB,GAAG,cAAc,
|
|
1
|
+
{"version":3,"file":"agent-setup.d.ts","sourceRoot":"","sources":["../src/agent-setup.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,qBAAqB,8DAA+D,CAAC;AAElG,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAExE,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,WAAW,CAAC;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,kBAAkB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,kBAAkB,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;CAChF;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,WAAW,EAAE,oBAAoB,EAAE,CAAC;IACpC,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,iBAAsB,GAAG,cAAc,CA0CnF;AAED,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,iBAAsB,GAAG,cAAc,CAqCjF;AAED,wBAAgB,wBAAwB,CAAC,MAAM,SAAwB;;;EAStE;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAW9D"}
|
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// @bun
|
|
3
3
|
|
|
4
4
|
// src/agent-setup.ts
|
|
5
|
-
import { existsSync, mkdirSync, rmSync, writeFileSync } from "fs";
|
|
5
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
6
6
|
import { homedir } from "os";
|
|
7
7
|
import { dirname, join } from "path";
|
|
8
8
|
var SUPPORTED_AGENT_HOSTS = ["claude-code", "codex", "opencode", "antigravity"];
|
|
@@ -45,7 +45,7 @@ function buildAgentSetupPlan(options = {}) {
|
|
|
45
45
|
return {
|
|
46
46
|
baseDir,
|
|
47
47
|
files,
|
|
48
|
-
hostResults: hosts.map((host) => ({ host, path:
|
|
48
|
+
hostResults: hosts.map((host) => ({ host, path: hostNativeConfigPath(home, host), status: "planned" })),
|
|
49
49
|
hosts,
|
|
50
50
|
mcpCommand
|
|
51
51
|
};
|
|
@@ -57,7 +57,7 @@ function installAgentSetup(options = {}) {
|
|
|
57
57
|
...plan,
|
|
58
58
|
hostResults: plan.hostResults.map((result) => ({
|
|
59
59
|
...result,
|
|
60
|
-
status:
|
|
60
|
+
status: hasNativeHostInstall(result.path, result.host) ? "present" : "missing"
|
|
61
61
|
}))
|
|
62
62
|
};
|
|
63
63
|
}
|
|
@@ -69,7 +69,9 @@ function installAgentSetup(options = {}) {
|
|
|
69
69
|
};
|
|
70
70
|
}
|
|
71
71
|
for (const result of plan.hostResults)
|
|
72
|
-
|
|
72
|
+
uninstallNativeHost(result.path, result.host);
|
|
73
|
+
for (const host of plan.hosts)
|
|
74
|
+
rmSync(hostManagedConfigPath(options.homeDir ?? homedir(), host), { force: true });
|
|
73
75
|
return {
|
|
74
76
|
...plan,
|
|
75
77
|
hostResults: plan.hostResults.map((result) => ({ ...result, status: "removed" }))
|
|
@@ -81,6 +83,8 @@ function installAgentSetup(options = {}) {
|
|
|
81
83
|
mkdirSync(dirname(file.path), { recursive: true });
|
|
82
84
|
writeFileSync(file.path, file.content, { mode: file.executable ? 493 : 420 });
|
|
83
85
|
}
|
|
86
|
+
for (const result of plan.hostResults)
|
|
87
|
+
installNativeHost(result.path, result.host, plan);
|
|
84
88
|
return {
|
|
85
89
|
...plan,
|
|
86
90
|
hostResults: plan.hostResults.map((result) => ({ ...result, status: "installed" }))
|
|
@@ -155,7 +159,9 @@ function renderManagedHostConfig(host, commands) {
|
|
|
155
159
|
return {
|
|
156
160
|
...base,
|
|
157
161
|
hooks: {
|
|
158
|
-
|
|
162
|
+
UserPromptSubmit: [{ matcher: "", hooks: [{ type: "command", command: "mementos hook user-prompt-submit" }] }],
|
|
163
|
+
SessionStart: [{ matcher: "", hooks: [{ type: "command", command: "mementos hook session-start" }] }],
|
|
164
|
+
Stop: [{ matcher: "", hooks: [{ type: "command", command: commands.hookCommand }] }]
|
|
159
165
|
}
|
|
160
166
|
};
|
|
161
167
|
}
|
|
@@ -184,6 +190,235 @@ function hostManagedConfigPath(home, host) {
|
|
|
184
190
|
return join(home, ".config", "antigravity", "mementos.managed.json");
|
|
185
191
|
}
|
|
186
192
|
}
|
|
193
|
+
function hostNativeConfigPath(home, host) {
|
|
194
|
+
switch (host) {
|
|
195
|
+
case "claude-code":
|
|
196
|
+
return join(home, ".claude", "settings.json");
|
|
197
|
+
case "codex":
|
|
198
|
+
return join(home, ".codex", "config.toml");
|
|
199
|
+
case "opencode":
|
|
200
|
+
return join(home, ".config", "opencode", "opencode.json");
|
|
201
|
+
case "antigravity":
|
|
202
|
+
return join(home, ".antigravity", "mcp.json");
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
function installNativeHost(path, host, plan) {
|
|
206
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
207
|
+
if (host === "codex") {
|
|
208
|
+
writeFileSync(path, installCodexToml(readTextFile(path), plan), { mode: 420 });
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
const config = readJsonObject(path);
|
|
212
|
+
if (host === "claude-code") {
|
|
213
|
+
writeJsonObject(path, installClaudeConfig(config, plan));
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (host === "opencode") {
|
|
217
|
+
writeJsonObject(path, installOpenCodeConfig(config, plan));
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
writeJsonObject(path, installAntigravityConfig(config, plan));
|
|
221
|
+
}
|
|
222
|
+
function uninstallNativeHost(path, host) {
|
|
223
|
+
if (!existsSync(path))
|
|
224
|
+
return;
|
|
225
|
+
if (host === "codex") {
|
|
226
|
+
writeFileSync(path, uninstallCodexToml(readTextFile(path)), { mode: 420 });
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const config = readJsonObject(path);
|
|
230
|
+
if (host === "claude-code")
|
|
231
|
+
writeJsonObject(path, uninstallClaudeConfig(config));
|
|
232
|
+
if (host === "opencode")
|
|
233
|
+
writeJsonObject(path, uninstallOpenCodeConfig(config));
|
|
234
|
+
if (host === "antigravity")
|
|
235
|
+
writeJsonObject(path, uninstallMcpServersConfig(config));
|
|
236
|
+
}
|
|
237
|
+
function hasNativeHostInstall(path, host) {
|
|
238
|
+
if (!existsSync(path))
|
|
239
|
+
return false;
|
|
240
|
+
if (host === "codex")
|
|
241
|
+
return /\[mcp_servers\.mementos\]/.test(readTextFile(path));
|
|
242
|
+
const config = readJsonObject(path);
|
|
243
|
+
if (host === "opencode")
|
|
244
|
+
return isRecord(config.mcp) && isRecord(config.mcp.mementos);
|
|
245
|
+
return isRecord(config.mcpServers) && isRecord(config.mcpServers.mementos);
|
|
246
|
+
}
|
|
247
|
+
function installClaudeConfig(config, plan) {
|
|
248
|
+
return {
|
|
249
|
+
...config,
|
|
250
|
+
mcpServers: {
|
|
251
|
+
...recordValue(config.mcpServers),
|
|
252
|
+
mementos: stdioMcpServer(plan, true)
|
|
253
|
+
},
|
|
254
|
+
hooks: mergeAgentHooks(recordValue(config.hooks), {
|
|
255
|
+
UserPromptSubmit: "mementos hook user-prompt-submit",
|
|
256
|
+
SessionStart: "mementos hook session-start",
|
|
257
|
+
Stop: "mementos hook stop"
|
|
258
|
+
})
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
function uninstallClaudeConfig(config) {
|
|
262
|
+
return {
|
|
263
|
+
...config,
|
|
264
|
+
mcpServers: omitKey(recordValue(config.mcpServers), "mementos"),
|
|
265
|
+
hooks: removeAgentHooks(recordValue(config.hooks), [
|
|
266
|
+
"mementos hook user-prompt-submit",
|
|
267
|
+
"mementos hook session-start",
|
|
268
|
+
"mementos hook stop"
|
|
269
|
+
])
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
function installOpenCodeConfig(config, plan) {
|
|
273
|
+
return {
|
|
274
|
+
...config,
|
|
275
|
+
mcp: {
|
|
276
|
+
...recordValue(config.mcp),
|
|
277
|
+
mementos: {
|
|
278
|
+
type: "local",
|
|
279
|
+
command: [plan.mcpCommand],
|
|
280
|
+
enabled: true,
|
|
281
|
+
environment: remoteEnv(baseApiUrlFromPlan(plan))
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
function uninstallOpenCodeConfig(config) {
|
|
287
|
+
return {
|
|
288
|
+
...config,
|
|
289
|
+
mcp: omitKey(recordValue(config.mcp), "mementos")
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
function installAntigravityConfig(config, plan) {
|
|
293
|
+
return {
|
|
294
|
+
...config,
|
|
295
|
+
mcpServers: {
|
|
296
|
+
...recordValue(config.mcpServers),
|
|
297
|
+
mementos: stdioMcpServer(plan, false)
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
function uninstallMcpServersConfig(config) {
|
|
302
|
+
return {
|
|
303
|
+
...config,
|
|
304
|
+
mcpServers: omitKey(recordValue(config.mcpServers), "mementos")
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
function installCodexToml(current, plan) {
|
|
308
|
+
const withoutExisting = removeTomlTable(removeManagedCodexBlock(current), "mcp_servers.mementos");
|
|
309
|
+
const block = [
|
|
310
|
+
"# BEGIN @hasna/mementos",
|
|
311
|
+
"[mcp_servers.mementos]",
|
|
312
|
+
`command = ${JSON.stringify(plan.mcpCommand)}`,
|
|
313
|
+
"[mcp_servers.mementos.env]",
|
|
314
|
+
`MEMENTOS_API_URL = ${JSON.stringify(baseApiUrlFromPlan(plan))}`,
|
|
315
|
+
'MEMENTOS_API_TOKEN = "$MEMENTOS_API_TOKEN"',
|
|
316
|
+
"# END @hasna/mementos",
|
|
317
|
+
""
|
|
318
|
+
].join(`
|
|
319
|
+
`);
|
|
320
|
+
return `${withoutExisting.trimEnd()}
|
|
321
|
+
|
|
322
|
+
${block}`;
|
|
323
|
+
}
|
|
324
|
+
function uninstallCodexToml(current) {
|
|
325
|
+
return `${removeTomlTable(removeManagedCodexBlock(current), "mcp_servers.mementos").trimEnd()}
|
|
326
|
+
`;
|
|
327
|
+
}
|
|
328
|
+
function mergeAgentHooks(hooks, commands) {
|
|
329
|
+
const next = { ...hooks };
|
|
330
|
+
for (const [event, command] of Object.entries(commands)) {
|
|
331
|
+
const current = next[event];
|
|
332
|
+
const entries = Array.isArray(current) ? [...current] : [];
|
|
333
|
+
if (!entries.some((entry) => JSON.stringify(entry).includes(command))) {
|
|
334
|
+
entries.push({ matcher: "", hooks: [{ type: "command", command }] });
|
|
335
|
+
}
|
|
336
|
+
next[event] = entries;
|
|
337
|
+
}
|
|
338
|
+
return next;
|
|
339
|
+
}
|
|
340
|
+
function removeAgentHooks(hooks, commands) {
|
|
341
|
+
const next = { ...hooks };
|
|
342
|
+
for (const [event, entries] of Object.entries(next)) {
|
|
343
|
+
if (!Array.isArray(entries))
|
|
344
|
+
continue;
|
|
345
|
+
next[event] = entries.filter((entry) => !commands.some((command) => JSON.stringify(entry).includes(command)));
|
|
346
|
+
}
|
|
347
|
+
return next;
|
|
348
|
+
}
|
|
349
|
+
function stdioMcpServer(plan, includeType) {
|
|
350
|
+
return {
|
|
351
|
+
...includeType ? { type: "stdio" } : {},
|
|
352
|
+
command: plan.mcpCommand,
|
|
353
|
+
env: remoteEnv(baseApiUrlFromPlan(plan))
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
function remoteEnv(apiUrl) {
|
|
357
|
+
return {
|
|
358
|
+
MEMENTOS_API_URL: apiUrl,
|
|
359
|
+
MEMENTOS_API_TOKEN: "$MEMENTOS_API_TOKEN"
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
function baseApiUrlFromPlan(plan) {
|
|
363
|
+
const mcpFile = plan.files.find((file) => file.path.endsWith("mcp-server.json"));
|
|
364
|
+
if (!mcpFile)
|
|
365
|
+
return "https://mementos.md";
|
|
366
|
+
try {
|
|
367
|
+
const parsed = JSON.parse(mcpFile.content);
|
|
368
|
+
return parsed.mcpServers?.mementos?.env?.MEMENTOS_API_URL ?? "https://mementos.md";
|
|
369
|
+
} catch {
|
|
370
|
+
return "https://mementos.md";
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
function removeManagedCodexBlock(value) {
|
|
374
|
+
return value.replace(/\n?# BEGIN @hasna\/mementos\n[\s\S]*?# END @hasna\/mementos\n?/g, `
|
|
375
|
+
`);
|
|
376
|
+
}
|
|
377
|
+
function removeTomlTable(value, table) {
|
|
378
|
+
const tablePattern = table.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
379
|
+
const lines = value.split(`
|
|
380
|
+
`);
|
|
381
|
+
const output = [];
|
|
382
|
+
let skipping = false;
|
|
383
|
+
for (const line of lines) {
|
|
384
|
+
if (new RegExp(`^\\[${tablePattern}(?:\\.|\\])`).test(line.trim())) {
|
|
385
|
+
skipping = true;
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
if (skipping && /^\[[^\]]+\]/.test(line.trim()))
|
|
389
|
+
skipping = false;
|
|
390
|
+
if (!skipping)
|
|
391
|
+
output.push(line);
|
|
392
|
+
}
|
|
393
|
+
return output.join(`
|
|
394
|
+
`);
|
|
395
|
+
}
|
|
396
|
+
function readTextFile(path) {
|
|
397
|
+
return existsSync(path) ? readFileSync(path, "utf8") : "";
|
|
398
|
+
}
|
|
399
|
+
function readJsonObject(path) {
|
|
400
|
+
if (!existsSync(path))
|
|
401
|
+
return {};
|
|
402
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
403
|
+
if (!isRecord(parsed))
|
|
404
|
+
throw new Error(`${path} must contain a JSON object`);
|
|
405
|
+
return parsed;
|
|
406
|
+
}
|
|
407
|
+
function writeJsonObject(path, value) {
|
|
408
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
409
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
410
|
+
`, { mode: 420 });
|
|
411
|
+
}
|
|
412
|
+
function recordValue(value) {
|
|
413
|
+
return isRecord(value) ? value : {};
|
|
414
|
+
}
|
|
415
|
+
function isRecord(value) {
|
|
416
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
417
|
+
}
|
|
418
|
+
function omitKey(record, key) {
|
|
419
|
+
const { [key]: _removed, ...rest } = record;
|
|
420
|
+
return rest;
|
|
421
|
+
}
|
|
187
422
|
function renderInjectionInstructions({ apiUrl, injectCommand }) {
|
|
188
423
|
return `# mementos.md Memory Injection
|
|
189
424
|
|
|
@@ -453,12 +688,52 @@ async function injectMemory(args) {
|
|
|
453
688
|
}
|
|
454
689
|
async function runHook(args) {
|
|
455
690
|
const subcommand = args[0] ?? "help";
|
|
691
|
+
const hookEvents = {
|
|
692
|
+
"user-prompt-submit": "UserPromptSubmit",
|
|
693
|
+
"session-start": "SessionStart",
|
|
694
|
+
"subagent-start": "SubagentStart",
|
|
695
|
+
setup: "Setup"
|
|
696
|
+
};
|
|
697
|
+
if (subcommand in hookEvents) {
|
|
698
|
+
const query2 = await readHookQuery();
|
|
699
|
+
const payload = await client.injectionContext({ query: query2, limit: 12 });
|
|
700
|
+
const block = readTextPayload(payload) ?? formatMemoryInjection(payload);
|
|
701
|
+
return {
|
|
702
|
+
suppressOutput: true,
|
|
703
|
+
hookSpecificOutput: {
|
|
704
|
+
hookEventName: hookEvents[subcommand],
|
|
705
|
+
additionalContext: block
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
}
|
|
456
709
|
if (subcommand !== "stop") {
|
|
457
|
-
throw new Error("usage: mementos hook stop");
|
|
710
|
+
throw new Error("usage: mementos hook <stop|user-prompt-submit|session-start|subagent-start|setup>");
|
|
458
711
|
}
|
|
459
712
|
const query = process.env.MEMENTOS_INJECTION_QUERY ?? process.cwd();
|
|
460
713
|
return injectMemory([query]);
|
|
461
714
|
}
|
|
715
|
+
async function readHookQuery() {
|
|
716
|
+
if (process.env.MEMENTOS_INJECTION_QUERY)
|
|
717
|
+
return process.env.MEMENTOS_INJECTION_QUERY;
|
|
718
|
+
const stdin = await readOptionalStdin();
|
|
719
|
+
if (!stdin)
|
|
720
|
+
return process.cwd();
|
|
721
|
+
try {
|
|
722
|
+
const parsed = JSON.parse(stdin);
|
|
723
|
+
const prompt = parsed.prompt ?? parsed.userPrompt ?? parsed.message ?? parsed.cwd;
|
|
724
|
+
if (typeof prompt === "string" && prompt.trim())
|
|
725
|
+
return prompt.trim();
|
|
726
|
+
} catch {
|
|
727
|
+
if (stdin.trim())
|
|
728
|
+
return stdin.trim();
|
|
729
|
+
}
|
|
730
|
+
return process.cwd();
|
|
731
|
+
}
|
|
732
|
+
async function readOptionalStdin() {
|
|
733
|
+
if (process.stdin.isTTY)
|
|
734
|
+
return "";
|
|
735
|
+
return new Response(Bun.stdin.stream()).text();
|
|
736
|
+
}
|
|
462
737
|
function parseSetupOptions(args) {
|
|
463
738
|
const options = {};
|
|
464
739
|
for (let index = 0;index < args.length; index += 1) {
|
|
@@ -502,7 +777,7 @@ Usage:
|
|
|
502
777
|
mementos auth signup
|
|
503
778
|
mementos setup agents [--dry-run|--verify|--uninstall]
|
|
504
779
|
mementos inject <query>
|
|
505
|
-
mementos hook stop
|
|
780
|
+
mementos hook <stop|user-prompt-submit|session-start|subagent-start|setup>
|
|
506
781
|
mementos capabilities
|
|
507
782
|
mementos agents
|
|
508
783
|
mementos usage
|
package/dist/index.js
CHANGED
|
@@ -131,6 +131,7 @@ function readErrorMessage(payload, status) {
|
|
|
131
131
|
}
|
|
132
132
|
// src/mcp-runtime.ts
|
|
133
133
|
import { createInterface } from "readline";
|
|
134
|
+
var MEMENTOS_MCP_VERSION = "0.14.24";
|
|
134
135
|
var AUTHLESS_SCHEMA = {
|
|
135
136
|
type: "object",
|
|
136
137
|
properties: {},
|
|
@@ -245,7 +246,7 @@ async function handleMcpMessage(message, client = createRemoteClientFromEnv()) {
|
|
|
245
246
|
if (message?.method === "initialize") {
|
|
246
247
|
return jsonRpcResult(id, {
|
|
247
248
|
protocolVersion: "2024-11-05",
|
|
248
|
-
serverInfo: { name: "mementos", version:
|
|
249
|
+
serverInfo: { name: "mementos", version: MEMENTOS_MCP_VERSION },
|
|
249
250
|
capabilities: { tools: {} }
|
|
250
251
|
});
|
|
251
252
|
}
|
|
@@ -291,7 +292,7 @@ function requiredText(input, message) {
|
|
|
291
292
|
return input.trim();
|
|
292
293
|
}
|
|
293
294
|
// src/agent-setup.ts
|
|
294
|
-
import { existsSync, mkdirSync, rmSync, writeFileSync } from "fs";
|
|
295
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
295
296
|
import { homedir } from "os";
|
|
296
297
|
import { dirname, join } from "path";
|
|
297
298
|
var SUPPORTED_AGENT_HOSTS = ["claude-code", "codex", "opencode", "antigravity"];
|
|
@@ -334,7 +335,7 @@ function buildAgentSetupPlan(options = {}) {
|
|
|
334
335
|
return {
|
|
335
336
|
baseDir,
|
|
336
337
|
files,
|
|
337
|
-
hostResults: hosts.map((host) => ({ host, path:
|
|
338
|
+
hostResults: hosts.map((host) => ({ host, path: hostNativeConfigPath(home, host), status: "planned" })),
|
|
338
339
|
hosts,
|
|
339
340
|
mcpCommand
|
|
340
341
|
};
|
|
@@ -346,7 +347,7 @@ function installAgentSetup(options = {}) {
|
|
|
346
347
|
...plan,
|
|
347
348
|
hostResults: plan.hostResults.map((result) => ({
|
|
348
349
|
...result,
|
|
349
|
-
status:
|
|
350
|
+
status: hasNativeHostInstall(result.path, result.host) ? "present" : "missing"
|
|
350
351
|
}))
|
|
351
352
|
};
|
|
352
353
|
}
|
|
@@ -358,7 +359,9 @@ function installAgentSetup(options = {}) {
|
|
|
358
359
|
};
|
|
359
360
|
}
|
|
360
361
|
for (const result of plan.hostResults)
|
|
361
|
-
|
|
362
|
+
uninstallNativeHost(result.path, result.host);
|
|
363
|
+
for (const host of plan.hosts)
|
|
364
|
+
rmSync(hostManagedConfigPath(options.homeDir ?? homedir(), host), { force: true });
|
|
362
365
|
return {
|
|
363
366
|
...plan,
|
|
364
367
|
hostResults: plan.hostResults.map((result) => ({ ...result, status: "removed" }))
|
|
@@ -370,6 +373,8 @@ function installAgentSetup(options = {}) {
|
|
|
370
373
|
mkdirSync(dirname(file.path), { recursive: true });
|
|
371
374
|
writeFileSync(file.path, file.content, { mode: file.executable ? 493 : 420 });
|
|
372
375
|
}
|
|
376
|
+
for (const result of plan.hostResults)
|
|
377
|
+
installNativeHost(result.path, result.host, plan);
|
|
373
378
|
return {
|
|
374
379
|
...plan,
|
|
375
380
|
hostResults: plan.hostResults.map((result) => ({ ...result, status: "installed" }))
|
|
@@ -444,7 +449,9 @@ function renderManagedHostConfig(host, commands) {
|
|
|
444
449
|
return {
|
|
445
450
|
...base,
|
|
446
451
|
hooks: {
|
|
447
|
-
|
|
452
|
+
UserPromptSubmit: [{ matcher: "", hooks: [{ type: "command", command: "mementos hook user-prompt-submit" }] }],
|
|
453
|
+
SessionStart: [{ matcher: "", hooks: [{ type: "command", command: "mementos hook session-start" }] }],
|
|
454
|
+
Stop: [{ matcher: "", hooks: [{ type: "command", command: commands.hookCommand }] }]
|
|
448
455
|
}
|
|
449
456
|
};
|
|
450
457
|
}
|
|
@@ -473,6 +480,235 @@ function hostManagedConfigPath(home, host) {
|
|
|
473
480
|
return join(home, ".config", "antigravity", "mementos.managed.json");
|
|
474
481
|
}
|
|
475
482
|
}
|
|
483
|
+
function hostNativeConfigPath(home, host) {
|
|
484
|
+
switch (host) {
|
|
485
|
+
case "claude-code":
|
|
486
|
+
return join(home, ".claude", "settings.json");
|
|
487
|
+
case "codex":
|
|
488
|
+
return join(home, ".codex", "config.toml");
|
|
489
|
+
case "opencode":
|
|
490
|
+
return join(home, ".config", "opencode", "opencode.json");
|
|
491
|
+
case "antigravity":
|
|
492
|
+
return join(home, ".antigravity", "mcp.json");
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
function installNativeHost(path, host, plan) {
|
|
496
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
497
|
+
if (host === "codex") {
|
|
498
|
+
writeFileSync(path, installCodexToml(readTextFile(path), plan), { mode: 420 });
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
const config = readJsonObject(path);
|
|
502
|
+
if (host === "claude-code") {
|
|
503
|
+
writeJsonObject(path, installClaudeConfig(config, plan));
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
if (host === "opencode") {
|
|
507
|
+
writeJsonObject(path, installOpenCodeConfig(config, plan));
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
writeJsonObject(path, installAntigravityConfig(config, plan));
|
|
511
|
+
}
|
|
512
|
+
function uninstallNativeHost(path, host) {
|
|
513
|
+
if (!existsSync(path))
|
|
514
|
+
return;
|
|
515
|
+
if (host === "codex") {
|
|
516
|
+
writeFileSync(path, uninstallCodexToml(readTextFile(path)), { mode: 420 });
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
const config = readJsonObject(path);
|
|
520
|
+
if (host === "claude-code")
|
|
521
|
+
writeJsonObject(path, uninstallClaudeConfig(config));
|
|
522
|
+
if (host === "opencode")
|
|
523
|
+
writeJsonObject(path, uninstallOpenCodeConfig(config));
|
|
524
|
+
if (host === "antigravity")
|
|
525
|
+
writeJsonObject(path, uninstallMcpServersConfig(config));
|
|
526
|
+
}
|
|
527
|
+
function hasNativeHostInstall(path, host) {
|
|
528
|
+
if (!existsSync(path))
|
|
529
|
+
return false;
|
|
530
|
+
if (host === "codex")
|
|
531
|
+
return /\[mcp_servers\.mementos\]/.test(readTextFile(path));
|
|
532
|
+
const config = readJsonObject(path);
|
|
533
|
+
if (host === "opencode")
|
|
534
|
+
return isRecord(config.mcp) && isRecord(config.mcp.mementos);
|
|
535
|
+
return isRecord(config.mcpServers) && isRecord(config.mcpServers.mementos);
|
|
536
|
+
}
|
|
537
|
+
function installClaudeConfig(config, plan) {
|
|
538
|
+
return {
|
|
539
|
+
...config,
|
|
540
|
+
mcpServers: {
|
|
541
|
+
...recordValue(config.mcpServers),
|
|
542
|
+
mementos: stdioMcpServer(plan, true)
|
|
543
|
+
},
|
|
544
|
+
hooks: mergeAgentHooks(recordValue(config.hooks), {
|
|
545
|
+
UserPromptSubmit: "mementos hook user-prompt-submit",
|
|
546
|
+
SessionStart: "mementos hook session-start",
|
|
547
|
+
Stop: "mementos hook stop"
|
|
548
|
+
})
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
function uninstallClaudeConfig(config) {
|
|
552
|
+
return {
|
|
553
|
+
...config,
|
|
554
|
+
mcpServers: omitKey(recordValue(config.mcpServers), "mementos"),
|
|
555
|
+
hooks: removeAgentHooks(recordValue(config.hooks), [
|
|
556
|
+
"mementos hook user-prompt-submit",
|
|
557
|
+
"mementos hook session-start",
|
|
558
|
+
"mementos hook stop"
|
|
559
|
+
])
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
function installOpenCodeConfig(config, plan) {
|
|
563
|
+
return {
|
|
564
|
+
...config,
|
|
565
|
+
mcp: {
|
|
566
|
+
...recordValue(config.mcp),
|
|
567
|
+
mementos: {
|
|
568
|
+
type: "local",
|
|
569
|
+
command: [plan.mcpCommand],
|
|
570
|
+
enabled: true,
|
|
571
|
+
environment: remoteEnv(baseApiUrlFromPlan(plan))
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
function uninstallOpenCodeConfig(config) {
|
|
577
|
+
return {
|
|
578
|
+
...config,
|
|
579
|
+
mcp: omitKey(recordValue(config.mcp), "mementos")
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
function installAntigravityConfig(config, plan) {
|
|
583
|
+
return {
|
|
584
|
+
...config,
|
|
585
|
+
mcpServers: {
|
|
586
|
+
...recordValue(config.mcpServers),
|
|
587
|
+
mementos: stdioMcpServer(plan, false)
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
function uninstallMcpServersConfig(config) {
|
|
592
|
+
return {
|
|
593
|
+
...config,
|
|
594
|
+
mcpServers: omitKey(recordValue(config.mcpServers), "mementos")
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
function installCodexToml(current, plan) {
|
|
598
|
+
const withoutExisting = removeTomlTable(removeManagedCodexBlock(current), "mcp_servers.mementos");
|
|
599
|
+
const block = [
|
|
600
|
+
"# BEGIN @hasna/mementos",
|
|
601
|
+
"[mcp_servers.mementos]",
|
|
602
|
+
`command = ${JSON.stringify(plan.mcpCommand)}`,
|
|
603
|
+
"[mcp_servers.mementos.env]",
|
|
604
|
+
`MEMENTOS_API_URL = ${JSON.stringify(baseApiUrlFromPlan(plan))}`,
|
|
605
|
+
'MEMENTOS_API_TOKEN = "$MEMENTOS_API_TOKEN"',
|
|
606
|
+
"# END @hasna/mementos",
|
|
607
|
+
""
|
|
608
|
+
].join(`
|
|
609
|
+
`);
|
|
610
|
+
return `${withoutExisting.trimEnd()}
|
|
611
|
+
|
|
612
|
+
${block}`;
|
|
613
|
+
}
|
|
614
|
+
function uninstallCodexToml(current) {
|
|
615
|
+
return `${removeTomlTable(removeManagedCodexBlock(current), "mcp_servers.mementos").trimEnd()}
|
|
616
|
+
`;
|
|
617
|
+
}
|
|
618
|
+
function mergeAgentHooks(hooks, commands) {
|
|
619
|
+
const next = { ...hooks };
|
|
620
|
+
for (const [event, command] of Object.entries(commands)) {
|
|
621
|
+
const current = next[event];
|
|
622
|
+
const entries = Array.isArray(current) ? [...current] : [];
|
|
623
|
+
if (!entries.some((entry) => JSON.stringify(entry).includes(command))) {
|
|
624
|
+
entries.push({ matcher: "", hooks: [{ type: "command", command }] });
|
|
625
|
+
}
|
|
626
|
+
next[event] = entries;
|
|
627
|
+
}
|
|
628
|
+
return next;
|
|
629
|
+
}
|
|
630
|
+
function removeAgentHooks(hooks, commands) {
|
|
631
|
+
const next = { ...hooks };
|
|
632
|
+
for (const [event, entries] of Object.entries(next)) {
|
|
633
|
+
if (!Array.isArray(entries))
|
|
634
|
+
continue;
|
|
635
|
+
next[event] = entries.filter((entry) => !commands.some((command) => JSON.stringify(entry).includes(command)));
|
|
636
|
+
}
|
|
637
|
+
return next;
|
|
638
|
+
}
|
|
639
|
+
function stdioMcpServer(plan, includeType) {
|
|
640
|
+
return {
|
|
641
|
+
...includeType ? { type: "stdio" } : {},
|
|
642
|
+
command: plan.mcpCommand,
|
|
643
|
+
env: remoteEnv(baseApiUrlFromPlan(plan))
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
function remoteEnv(apiUrl) {
|
|
647
|
+
return {
|
|
648
|
+
MEMENTOS_API_URL: apiUrl,
|
|
649
|
+
MEMENTOS_API_TOKEN: "$MEMENTOS_API_TOKEN"
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
function baseApiUrlFromPlan(plan) {
|
|
653
|
+
const mcpFile = plan.files.find((file) => file.path.endsWith("mcp-server.json"));
|
|
654
|
+
if (!mcpFile)
|
|
655
|
+
return "https://mementos.md";
|
|
656
|
+
try {
|
|
657
|
+
const parsed = JSON.parse(mcpFile.content);
|
|
658
|
+
return parsed.mcpServers?.mementos?.env?.MEMENTOS_API_URL ?? "https://mementos.md";
|
|
659
|
+
} catch {
|
|
660
|
+
return "https://mementos.md";
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
function removeManagedCodexBlock(value) {
|
|
664
|
+
return value.replace(/\n?# BEGIN @hasna\/mementos\n[\s\S]*?# END @hasna\/mementos\n?/g, `
|
|
665
|
+
`);
|
|
666
|
+
}
|
|
667
|
+
function removeTomlTable(value, table) {
|
|
668
|
+
const tablePattern = table.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
669
|
+
const lines = value.split(`
|
|
670
|
+
`);
|
|
671
|
+
const output = [];
|
|
672
|
+
let skipping = false;
|
|
673
|
+
for (const line of lines) {
|
|
674
|
+
if (new RegExp(`^\\[${tablePattern}(?:\\.|\\])`).test(line.trim())) {
|
|
675
|
+
skipping = true;
|
|
676
|
+
continue;
|
|
677
|
+
}
|
|
678
|
+
if (skipping && /^\[[^\]]+\]/.test(line.trim()))
|
|
679
|
+
skipping = false;
|
|
680
|
+
if (!skipping)
|
|
681
|
+
output.push(line);
|
|
682
|
+
}
|
|
683
|
+
return output.join(`
|
|
684
|
+
`);
|
|
685
|
+
}
|
|
686
|
+
function readTextFile(path) {
|
|
687
|
+
return existsSync(path) ? readFileSync(path, "utf8") : "";
|
|
688
|
+
}
|
|
689
|
+
function readJsonObject(path) {
|
|
690
|
+
if (!existsSync(path))
|
|
691
|
+
return {};
|
|
692
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
693
|
+
if (!isRecord(parsed))
|
|
694
|
+
throw new Error(`${path} must contain a JSON object`);
|
|
695
|
+
return parsed;
|
|
696
|
+
}
|
|
697
|
+
function writeJsonObject(path, value) {
|
|
698
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
699
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
700
|
+
`, { mode: 420 });
|
|
701
|
+
}
|
|
702
|
+
function recordValue(value) {
|
|
703
|
+
return isRecord(value) ? value : {};
|
|
704
|
+
}
|
|
705
|
+
function isRecord(value) {
|
|
706
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
707
|
+
}
|
|
708
|
+
function omitKey(record, key) {
|
|
709
|
+
const { [key]: _removed, ...rest } = record;
|
|
710
|
+
return rest;
|
|
711
|
+
}
|
|
476
712
|
function renderInjectionInstructions({ apiUrl, injectCommand }) {
|
|
477
713
|
return `# mementos.md Memory Injection
|
|
478
714
|
|
package/dist/mcp-runtime.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-runtime.d.ts","sourceRoot":"","sources":["../src/mcp-runtime.ts"],"names":[],"mappings":"AACA,OAAO,EAA6B,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAElF,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,oBAAoB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACzF;AAaD,eAAO,MAAM,gBAAgB,EAAE,aAAa,EA4F3C,CAAC;AAEF,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACnC,MAAM,uBAA8B,oBAKrC;AAED,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,MAAM,uBAA8B;;;;;;;;;;;GA8BxF;AAED,wBAAgB,aAAa,CAAC,MAAM,uBAA8B,QAUjE"}
|
|
1
|
+
{"version":3,"file":"mcp-runtime.d.ts","sourceRoot":"","sources":["../src/mcp-runtime.ts"],"names":[],"mappings":"AACA,OAAO,EAA6B,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAElF,eAAO,MAAM,oBAAoB,YAAY,CAAC;AAE9C,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,oBAAoB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACzF;AAaD,eAAO,MAAM,gBAAgB,EAAE,aAAa,EA4F3C,CAAC;AAEF,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACnC,MAAM,uBAA8B,oBAKrC;AAED,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,MAAM,uBAA8B;;;;;;;;;;;GA8BxF;AAED,wBAAgB,aAAa,CAAC,MAAM,uBAA8B,QAUjE"}
|
package/dist/mcp.js
CHANGED
|
@@ -136,6 +136,7 @@ function readErrorMessage(payload, status) {
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
// src/mcp-runtime.ts
|
|
139
|
+
var MEMENTOS_MCP_VERSION = "0.14.24";
|
|
139
140
|
var AUTHLESS_SCHEMA = {
|
|
140
141
|
type: "object",
|
|
141
142
|
properties: {},
|
|
@@ -250,7 +251,7 @@ async function handleMcpMessage(message, client = createRemoteClientFromEnv()) {
|
|
|
250
251
|
if (message?.method === "initialize") {
|
|
251
252
|
return jsonRpcResult(id, {
|
|
252
253
|
protocolVersion: "2024-11-05",
|
|
253
|
-
serverInfo: { name: "mementos", version:
|
|
254
|
+
serverInfo: { name: "mementos", version: MEMENTOS_MCP_VERSION },
|
|
254
255
|
capabilities: { tools: {} }
|
|
255
256
|
});
|
|
256
257
|
}
|