@memoraone/mcp 0.1.19 → 0.1.21
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/cli.cjs +323 -14
- package/dist/daemon.cjs +296 -101
- package/dist/index.cjs +296 -101
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -30,7 +30,7 @@ var require_package = __commonJS({
|
|
|
30
30
|
"package.json"(exports2, module2) {
|
|
31
31
|
module2.exports = {
|
|
32
32
|
name: "@memoraone/mcp",
|
|
33
|
-
version: "0.1.
|
|
33
|
+
version: "0.1.21",
|
|
34
34
|
type: "module",
|
|
35
35
|
main: "dist/index.cjs",
|
|
36
36
|
bin: {
|
|
@@ -67,7 +67,7 @@ var require_package = __commonJS({
|
|
|
67
67
|
});
|
|
68
68
|
|
|
69
69
|
// src/cli.ts
|
|
70
|
-
var
|
|
70
|
+
var path4 = __toESM(require("path"), 1);
|
|
71
71
|
var net = __toESM(require("net"), 1);
|
|
72
72
|
var import_node_child_process = require("child_process");
|
|
73
73
|
|
|
@@ -219,6 +219,313 @@ function encodeResolvedBinding(binding) {
|
|
|
219
219
|
return Buffer.from(JSON.stringify(binding), "utf8").toString("base64");
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
+
// src/setupIdeFiles.ts
|
|
223
|
+
var fs3 = __toESM(require("fs/promises"), 1);
|
|
224
|
+
var path3 = __toESM(require("path"), 1);
|
|
225
|
+
var MANAGED_MARKER = "<!-- MemoraOne managed IDE helper -->";
|
|
226
|
+
var GITIGNORE_MEMORAONE_COMMENT = "# MemoraOne local project binding / API key";
|
|
227
|
+
var GITIGNORE_MEMORAONE_ENTRY = "memoraone.m1";
|
|
228
|
+
var MEMORAONE_MCP_SERVER = {
|
|
229
|
+
command: "npx",
|
|
230
|
+
args: ["-y", "@memoraone/mcp"]
|
|
231
|
+
};
|
|
232
|
+
function assertUnderRepoRoot(repoRoot, absPath) {
|
|
233
|
+
const normRoot = path3.resolve(repoRoot) + path3.sep;
|
|
234
|
+
const normPath = path3.resolve(absPath);
|
|
235
|
+
if (normPath !== path3.resolve(repoRoot) && !normPath.startsWith(normRoot)) {
|
|
236
|
+
throw new Error(`[setup-ide-files] Refusing to write outside repo root: ${absPath}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
async function pathExists(filePath) {
|
|
240
|
+
try {
|
|
241
|
+
await fs3.access(filePath);
|
|
242
|
+
return true;
|
|
243
|
+
} catch {
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function gitignoreAlreadyIgnoresMemoraoneM1(content) {
|
|
248
|
+
for (const line of content.split(/\r?\n/)) {
|
|
249
|
+
const trimmed = line.trim();
|
|
250
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
251
|
+
if (/^\/?memoraone\.m1\s*$/.test(trimmed)) return true;
|
|
252
|
+
}
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
function memoraoneGitignoreBlock() {
|
|
256
|
+
return `${GITIGNORE_MEMORAONE_COMMENT}
|
|
257
|
+
${GITIGNORE_MEMORAONE_ENTRY}
|
|
258
|
+
`;
|
|
259
|
+
}
|
|
260
|
+
async function ensureGitignoreMemoraone(repoRoot, opts) {
|
|
261
|
+
if (opts.noGitignore) return "skipped";
|
|
262
|
+
const abs = path3.join(repoRoot, ".gitignore");
|
|
263
|
+
assertUnderRepoRoot(repoRoot, abs);
|
|
264
|
+
let prior = "";
|
|
265
|
+
let existed = false;
|
|
266
|
+
try {
|
|
267
|
+
prior = await fs3.readFile(abs, "utf8");
|
|
268
|
+
existed = true;
|
|
269
|
+
} catch (err) {
|
|
270
|
+
const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
|
|
271
|
+
if (code !== "ENOENT") throw err;
|
|
272
|
+
}
|
|
273
|
+
if (gitignoreAlreadyIgnoresMemoraoneM1(prior)) return "skipped";
|
|
274
|
+
const block = memoraoneGitignoreBlock();
|
|
275
|
+
const separator = existed && prior.length > 0 ? prior.endsWith("\n") ? "\n" : "\n\n" : "";
|
|
276
|
+
const next = (existed ? prior : "") + separator + block;
|
|
277
|
+
if (opts.dryRun) return existed ? "updated" : "created";
|
|
278
|
+
await fs3.writeFile(abs, next, "utf8");
|
|
279
|
+
return existed ? "updated" : "created";
|
|
280
|
+
}
|
|
281
|
+
async function findRepoRoot(startDir) {
|
|
282
|
+
let current = path3.resolve(startDir);
|
|
283
|
+
const root = path3.parse(current).root;
|
|
284
|
+
while (true) {
|
|
285
|
+
const gitPath = path3.join(current, ".git");
|
|
286
|
+
const m1Path = path3.join(current, "memoraone.m1");
|
|
287
|
+
if (await pathExists(gitPath) || await pathExists(m1Path)) {
|
|
288
|
+
return current;
|
|
289
|
+
}
|
|
290
|
+
if (current === root) {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
current = path3.dirname(current);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
function stripLeadingLineComments(text) {
|
|
297
|
+
return text.split("\n").filter((line) => !/^\s*\/\//.test(line)).join("\n");
|
|
298
|
+
}
|
|
299
|
+
function cursorRuleBody() {
|
|
300
|
+
return `${MANAGED_MARKER}
|
|
301
|
+
|
|
302
|
+
## MemoraOne MCP (IDE agent only)
|
|
303
|
+
|
|
304
|
+
This repository uses **MemoraOne** via the MCP server named **user-memoraone** (Cursor MCP configuration). This guidance applies to the **IDE coding agent** only \u2014 not the MemoraOne Studio runtime path.
|
|
305
|
+
|
|
306
|
+
### Tools
|
|
307
|
+
|
|
308
|
+
- Before answering questions about **prior decisions**, **remembered facts**, **identity or personal recall**, **preferences**, **repo history**, or **what to do next**, call **\`memora_ask_with_memory\`** so replies stay aligned with MemoraOne memory.
|
|
309
|
+
- After **meaningful repository changes** (decisions, fixes, new endpoints or schema, migrations, meaningful wiring), call **\`memora_post_event\`** to append a concise timeline note.
|
|
310
|
+
|
|
311
|
+
Keep tool usage proportional to the task; do not spam events for trivial edits.
|
|
312
|
+
`;
|
|
313
|
+
}
|
|
314
|
+
function copilotAndJetBrainsBody(title) {
|
|
315
|
+
return `${MANAGED_MARKER}
|
|
316
|
+
|
|
317
|
+
# ${title}
|
|
318
|
+
|
|
319
|
+
This repo is set up to use **MemoraOne** through MCP where your editor exposes it. These instructions apply to the **IDE assistant** in this workspace, not to other MemoraOne runtimes.
|
|
320
|
+
|
|
321
|
+
## Behavior
|
|
322
|
+
|
|
323
|
+
- For questions about **earlier decisions**, **stored facts**, **personal or identity recall**, **preferences**, **project history**, or **recommended next steps**, use MemoraOne memory tools (e.g. **\`memora_ask_with_memory\`**) when available before answering.
|
|
324
|
+
- After **substantive changes** to this codebase (feature work, bugfixes, API/schema changes, migrations, meaningful integration steps), record a short timeline note with **\`memora_post_event\`** when that tool is available.
|
|
325
|
+
|
|
326
|
+
Use judgment: skip logging for trivial typo-only edits.
|
|
327
|
+
`;
|
|
328
|
+
}
|
|
329
|
+
function mcpJsonHeader() {
|
|
330
|
+
return `// ${MANAGED_MARKER}
|
|
331
|
+
`;
|
|
332
|
+
}
|
|
333
|
+
function buildMcpJsonBody(existing) {
|
|
334
|
+
const base = existing && typeof existing === "object" ? { ...existing } : { mcpServers: {} };
|
|
335
|
+
const servers = typeof base.mcpServers === "object" && base.mcpServers !== null && !Array.isArray(base.mcpServers) ? { ...base.mcpServers } : {};
|
|
336
|
+
servers.memoraone = { ...MEMORAONE_MCP_SERVER };
|
|
337
|
+
const merged = { ...base, mcpServers: servers };
|
|
338
|
+
return mcpJsonHeader() + JSON.stringify(merged, null, 2) + "\n";
|
|
339
|
+
}
|
|
340
|
+
async function writeManagedMarkdown(repoRoot, relPath, fullContent, opts) {
|
|
341
|
+
const abs = path3.join(repoRoot, relPath);
|
|
342
|
+
assertUnderRepoRoot(repoRoot, abs);
|
|
343
|
+
let prior = "";
|
|
344
|
+
let existed = false;
|
|
345
|
+
try {
|
|
346
|
+
prior = await fs3.readFile(abs, "utf8");
|
|
347
|
+
existed = true;
|
|
348
|
+
} catch (err) {
|
|
349
|
+
if (err?.code !== "ENOENT") throw err;
|
|
350
|
+
}
|
|
351
|
+
if (!existed) {
|
|
352
|
+
if (opts.dryRun) return "created";
|
|
353
|
+
await fs3.mkdir(path3.dirname(abs), { recursive: true });
|
|
354
|
+
await fs3.writeFile(abs, fullContent, "utf8");
|
|
355
|
+
return "created";
|
|
356
|
+
}
|
|
357
|
+
if (prior.includes(MANAGED_MARKER)) {
|
|
358
|
+
if (prior === fullContent) return "skipped";
|
|
359
|
+
if (opts.dryRun) return "updated";
|
|
360
|
+
await fs3.mkdir(path3.dirname(abs), { recursive: true });
|
|
361
|
+
await fs3.writeFile(abs, fullContent, "utf8");
|
|
362
|
+
return "updated";
|
|
363
|
+
}
|
|
364
|
+
if (!opts.force) return "skipped-untracked";
|
|
365
|
+
if (opts.dryRun) return "updated";
|
|
366
|
+
await fs3.mkdir(path3.dirname(abs), { recursive: true });
|
|
367
|
+
await fs3.writeFile(abs, fullContent, "utf8");
|
|
368
|
+
return "updated";
|
|
369
|
+
}
|
|
370
|
+
async function writeMcpJson(repoRoot, opts) {
|
|
371
|
+
const abs = path3.join(repoRoot, ".vscode/mcp.json");
|
|
372
|
+
assertUnderRepoRoot(repoRoot, abs);
|
|
373
|
+
let raw = "";
|
|
374
|
+
let existed = false;
|
|
375
|
+
try {
|
|
376
|
+
raw = await fs3.readFile(abs, "utf8");
|
|
377
|
+
existed = true;
|
|
378
|
+
} catch (err) {
|
|
379
|
+
if (err?.code !== "ENOENT") throw err;
|
|
380
|
+
}
|
|
381
|
+
if (!existed) {
|
|
382
|
+
const body = buildMcpJsonBody(null);
|
|
383
|
+
if (opts.dryRun) return "created";
|
|
384
|
+
await fs3.mkdir(path3.dirname(abs), { recursive: true });
|
|
385
|
+
await fs3.writeFile(abs, body, "utf8");
|
|
386
|
+
return "created";
|
|
387
|
+
}
|
|
388
|
+
const managed = raw.includes(MANAGED_MARKER);
|
|
389
|
+
if (!managed && !opts.force) return "skipped-untracked";
|
|
390
|
+
let parsed = null;
|
|
391
|
+
try {
|
|
392
|
+
parsed = JSON.parse(stripLeadingLineComments(raw));
|
|
393
|
+
} catch {
|
|
394
|
+
parsed = null;
|
|
395
|
+
}
|
|
396
|
+
if (!parsed && !opts.force) return "skipped-untracked";
|
|
397
|
+
const next = buildMcpJsonBody(parsed);
|
|
398
|
+
if (managed && next === raw) return "skipped";
|
|
399
|
+
if (opts.dryRun) return "updated";
|
|
400
|
+
await fs3.mkdir(path3.dirname(abs), { recursive: true });
|
|
401
|
+
await fs3.writeFile(abs, next, "utf8");
|
|
402
|
+
return "updated";
|
|
403
|
+
}
|
|
404
|
+
function parseSetupIdeFlags(argv) {
|
|
405
|
+
let cursor = false;
|
|
406
|
+
let vscode = false;
|
|
407
|
+
let jetbrains = false;
|
|
408
|
+
let all = false;
|
|
409
|
+
let force = false;
|
|
410
|
+
let dryRun = false;
|
|
411
|
+
let noGitignore = false;
|
|
412
|
+
const unknown = [];
|
|
413
|
+
for (const a of argv) {
|
|
414
|
+
if (a === "--cursor") cursor = true;
|
|
415
|
+
else if (a === "--vscode") vscode = true;
|
|
416
|
+
else if (a === "--jetbrains") jetbrains = true;
|
|
417
|
+
else if (a === "--all") all = true;
|
|
418
|
+
else if (a === "--force") force = true;
|
|
419
|
+
else if (a === "--dry-run") dryRun = true;
|
|
420
|
+
else if (a === "--no-gitignore") noGitignore = true;
|
|
421
|
+
else if (a.startsWith("-")) unknown.push(a);
|
|
422
|
+
}
|
|
423
|
+
const specific = cursor || vscode || jetbrains;
|
|
424
|
+
let targets;
|
|
425
|
+
if (all || !specific) {
|
|
426
|
+
targets = { cursor: true, vscode: true, jetbrains: true };
|
|
427
|
+
} else {
|
|
428
|
+
targets = { cursor, vscode, jetbrains };
|
|
429
|
+
}
|
|
430
|
+
return { targets, force, dryRun, noGitignore, unknown };
|
|
431
|
+
}
|
|
432
|
+
function summarizeOutcomes(outcomes) {
|
|
433
|
+
const created = [];
|
|
434
|
+
const updated = [];
|
|
435
|
+
const skipped = [];
|
|
436
|
+
const skippedUntracked = [];
|
|
437
|
+
for (const [file, o] of Object.entries(outcomes)) {
|
|
438
|
+
if (o === "created") created.push(file);
|
|
439
|
+
else if (o === "updated") updated.push(file);
|
|
440
|
+
else if (o === "skipped") skipped.push(file);
|
|
441
|
+
else skippedUntracked.push(file);
|
|
442
|
+
}
|
|
443
|
+
const lines = ["[setup-ide-files] Summary"];
|
|
444
|
+
if (created.length) lines.push(` created: ${created.join(", ")}`);
|
|
445
|
+
if (updated.length) lines.push(` updated: ${updated.join(", ")}`);
|
|
446
|
+
if (skipped.length) lines.push(` skipped (unchanged): ${skipped.join(", ")}`);
|
|
447
|
+
if (skippedUntracked.length) {
|
|
448
|
+
lines.push(` skipped (unmanaged existing file, use --force): ${skippedUntracked.join(", ")}`);
|
|
449
|
+
}
|
|
450
|
+
console.log(lines.join("\n"));
|
|
451
|
+
}
|
|
452
|
+
async function runSetupIdeFiles(o) {
|
|
453
|
+
const outcomes = {};
|
|
454
|
+
const repoRoot = await findRepoRoot(o.cwd);
|
|
455
|
+
if (!repoRoot) {
|
|
456
|
+
return {
|
|
457
|
+
exitCode: 1,
|
|
458
|
+
repoRoot: null,
|
|
459
|
+
outcomes,
|
|
460
|
+
error: "[setup-ide-files] No repo root found (looked for .git or memoraone.m1)."
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
outcomes[".gitignore"] = await ensureGitignoreMemoraone(repoRoot, {
|
|
464
|
+
dryRun: o.dryRun,
|
|
465
|
+
noGitignore: o.noGitignore ?? false
|
|
466
|
+
});
|
|
467
|
+
const cursorContent = `---
|
|
468
|
+
description: MemoraOne MCP \u2014 IDE agent instructions
|
|
469
|
+
---
|
|
470
|
+
|
|
471
|
+
` + cursorRuleBody();
|
|
472
|
+
if (o.targets.cursor) {
|
|
473
|
+
outcomes[".cursor/rules/memoraone-mcp.mdc"] = await writeManagedMarkdown(
|
|
474
|
+
repoRoot,
|
|
475
|
+
".cursor/rules/memoraone-mcp.mdc",
|
|
476
|
+
cursorContent,
|
|
477
|
+
{ force: o.force, dryRun: o.dryRun }
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
if (o.targets.vscode) {
|
|
481
|
+
outcomes[".vscode/mcp.json"] = await writeMcpJson(repoRoot, {
|
|
482
|
+
force: o.force,
|
|
483
|
+
dryRun: o.dryRun
|
|
484
|
+
});
|
|
485
|
+
outcomes[".github/copilot-instructions.md"] = await writeManagedMarkdown(
|
|
486
|
+
repoRoot,
|
|
487
|
+
".github/copilot-instructions.md",
|
|
488
|
+
copilotAndJetBrainsBody("MemoraOne MCP \u2014 GitHub Copilot"),
|
|
489
|
+
{ force: o.force, dryRun: o.dryRun }
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
if (o.targets.jetbrains) {
|
|
493
|
+
outcomes[".aiassistant/rules/memoraone.md"] = await writeManagedMarkdown(
|
|
494
|
+
repoRoot,
|
|
495
|
+
".aiassistant/rules/memoraone.md",
|
|
496
|
+
copilotAndJetBrainsBody("MemoraOne MCP \u2014 JetBrains AI Assistant"),
|
|
497
|
+
{ force: o.force, dryRun: o.dryRun }
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
return { exitCode: 0, repoRoot, outcomes };
|
|
501
|
+
}
|
|
502
|
+
async function cliSetupIdeFiles(argv) {
|
|
503
|
+
const { targets, force, dryRun, noGitignore, unknown } = parseSetupIdeFlags(argv);
|
|
504
|
+
if (unknown.length) {
|
|
505
|
+
console.error(`[setup-ide-files] Unknown option(s): ${unknown.join(", ")}`);
|
|
506
|
+
return 1;
|
|
507
|
+
}
|
|
508
|
+
const result = await runSetupIdeFiles({
|
|
509
|
+
cwd: process.cwd(),
|
|
510
|
+
targets,
|
|
511
|
+
force,
|
|
512
|
+
dryRun,
|
|
513
|
+
noGitignore
|
|
514
|
+
});
|
|
515
|
+
if (result.error) {
|
|
516
|
+
console.error(result.error);
|
|
517
|
+
return result.exitCode;
|
|
518
|
+
}
|
|
519
|
+
if (result.repoRoot) {
|
|
520
|
+
console.log(`[setup-ide-files] Repo root: ${result.repoRoot}`);
|
|
521
|
+
}
|
|
522
|
+
summarizeOutcomes(result.outcomes);
|
|
523
|
+
if (dryRun) {
|
|
524
|
+
console.log("[setup-ide-files] Dry run: no files written.");
|
|
525
|
+
}
|
|
526
|
+
return 0;
|
|
527
|
+
}
|
|
528
|
+
|
|
222
529
|
// src/cli.ts
|
|
223
530
|
var { version } = require_package();
|
|
224
531
|
var args = process.argv.slice(2);
|
|
@@ -227,10 +534,18 @@ if (args.includes("--version") || args.includes("-v")) {
|
|
|
227
534
|
process.exit(0);
|
|
228
535
|
}
|
|
229
536
|
if (args.includes("--help") || args.includes("-h")) {
|
|
230
|
-
console.log(
|
|
537
|
+
console.log(
|
|
538
|
+
"Usage: memoraone-mcp [--version] [--help] [--daemon --project-id <uuid>]\n memoraone-mcp setup-ide-files [--all|--cursor|--vscode|--jetbrains] [--force] [--dry-run] [--no-gitignore]"
|
|
539
|
+
);
|
|
231
540
|
process.exit(0);
|
|
232
541
|
}
|
|
233
|
-
if (args
|
|
542
|
+
if (args[0] === "setup-ide-files") {
|
|
543
|
+
cliSetupIdeFiles(args.slice(1)).then((code) => process.exit(code)).catch((err) => {
|
|
544
|
+
process.stderr.write(`[memoraone-mcp] setup-ide-files fatal: ${String(err)}
|
|
545
|
+
`);
|
|
546
|
+
process.exit(1);
|
|
547
|
+
});
|
|
548
|
+
} else if (args.includes("--daemon")) {
|
|
234
549
|
import("./daemon.cjs").then(({ runDaemon }) => runDaemon()).catch((err) => {
|
|
235
550
|
process.stderr.write(`[memoraone-mcp] daemon fatal: ${String(err)}
|
|
236
551
|
`);
|
|
@@ -241,8 +556,8 @@ if (args.includes("--daemon")) {
|
|
|
241
556
|
const raw = process.env.WORKSPACE_FOLDER_PATHS;
|
|
242
557
|
const parts = [];
|
|
243
558
|
if (raw !== void 0 && raw.trim() !== "") {
|
|
244
|
-
for (const p of raw.split(
|
|
245
|
-
parts.push(
|
|
559
|
+
for (const p of raw.split(path4.delimiter).map((s) => s.trim()).filter(Boolean)) {
|
|
560
|
+
parts.push(path4.resolve(p));
|
|
246
561
|
}
|
|
247
562
|
}
|
|
248
563
|
parts.push(process.cwd());
|
|
@@ -256,10 +571,10 @@ if (args.includes("--daemon")) {
|
|
|
256
571
|
}
|
|
257
572
|
return deduped;
|
|
258
573
|
}, connectWithRetry = function(socketPath) {
|
|
259
|
-
return new Promise((
|
|
574
|
+
return new Promise((resolve4, reject) => {
|
|
260
575
|
const tryConnect = (attempt) => {
|
|
261
576
|
const socket = net.connect(socketPath, () => {
|
|
262
|
-
|
|
577
|
+
resolve4(socket);
|
|
263
578
|
});
|
|
264
579
|
socket.on("error", (err) => {
|
|
265
580
|
if (attempt >= MAX_RETRIES) {
|
|
@@ -308,12 +623,6 @@ if (args.includes("--daemon")) {
|
|
|
308
623
|
}
|
|
309
624
|
log("bridge connected");
|
|
310
625
|
log("forwarding active");
|
|
311
|
-
process.stdin.on("data", (chunk) => {
|
|
312
|
-
const text = chunk.toString("utf8");
|
|
313
|
-
const hasToolsCall = text.includes('"tools/call"') || text.includes("tools/call");
|
|
314
|
-
const hasInitialize = text.includes('"initialize"');
|
|
315
|
-
log(`[stdin-diagnostic] bytes=${chunk.length} tools/call=${hasToolsCall} initialize=${hasInitialize}`);
|
|
316
|
-
});
|
|
317
626
|
process.stdin.pipe(socket);
|
|
318
627
|
socket.pipe(process.stdout);
|
|
319
628
|
socket.on("close", () => process.exit(0));
|