@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 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.19",
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 path3 = __toESM(require("path"), 1);
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("Usage: memoraone-mcp [--version] [--help] [--daemon --project-id <uuid>]");
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.includes("--daemon")) {
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(path3.delimiter).map((s) => s.trim()).filter(Boolean)) {
245
- parts.push(path3.resolve(p));
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((resolve3, reject) => {
574
+ return new Promise((resolve4, reject) => {
260
575
  const tryConnect = (attempt) => {
261
576
  const socket = net.connect(socketPath, () => {
262
- resolve3(socket);
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));