@memoraone/mcp 0.1.19 → 0.1.20

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.20",
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,270 @@ 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 MEMORAONE_MCP_SERVER = {
227
+ command: "npx",
228
+ args: ["-y", "@memoraone/mcp"]
229
+ };
230
+ function assertUnderRepoRoot(repoRoot, absPath) {
231
+ const normRoot = path3.resolve(repoRoot) + path3.sep;
232
+ const normPath = path3.resolve(absPath);
233
+ if (normPath !== path3.resolve(repoRoot) && !normPath.startsWith(normRoot)) {
234
+ throw new Error(`[setup-ide-files] Refusing to write outside repo root: ${absPath}`);
235
+ }
236
+ }
237
+ async function pathExists(filePath) {
238
+ try {
239
+ await fs3.access(filePath);
240
+ return true;
241
+ } catch {
242
+ return false;
243
+ }
244
+ }
245
+ async function findRepoRoot(startDir) {
246
+ let current = path3.resolve(startDir);
247
+ const root = path3.parse(current).root;
248
+ while (true) {
249
+ const gitPath = path3.join(current, ".git");
250
+ const m1Path = path3.join(current, "memoraone.m1");
251
+ if (await pathExists(gitPath) || await pathExists(m1Path)) {
252
+ return current;
253
+ }
254
+ if (current === root) {
255
+ return null;
256
+ }
257
+ current = path3.dirname(current);
258
+ }
259
+ }
260
+ function stripLeadingLineComments(text) {
261
+ return text.split("\n").filter((line) => !/^\s*\/\//.test(line)).join("\n");
262
+ }
263
+ function cursorRuleBody() {
264
+ return `${MANAGED_MARKER}
265
+
266
+ ## MemoraOne MCP (IDE agent only)
267
+
268
+ 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.
269
+
270
+ ### Tools
271
+
272
+ - 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.
273
+ - After **meaningful repository changes** (decisions, fixes, new endpoints or schema, migrations, meaningful wiring), call **\`memora_post_event\`** to append a concise timeline note.
274
+
275
+ Keep tool usage proportional to the task; do not spam events for trivial edits.
276
+ `;
277
+ }
278
+ function copilotAndJetBrainsBody(title) {
279
+ return `${MANAGED_MARKER}
280
+
281
+ # ${title}
282
+
283
+ 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.
284
+
285
+ ## Behavior
286
+
287
+ - 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.
288
+ - 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.
289
+
290
+ Use judgment: skip logging for trivial typo-only edits.
291
+ `;
292
+ }
293
+ function mcpJsonHeader() {
294
+ return `// ${MANAGED_MARKER}
295
+ `;
296
+ }
297
+ function buildMcpJsonBody(existing) {
298
+ const base = existing && typeof existing === "object" ? { ...existing } : { mcpServers: {} };
299
+ const servers = typeof base.mcpServers === "object" && base.mcpServers !== null && !Array.isArray(base.mcpServers) ? { ...base.mcpServers } : {};
300
+ servers.memoraone = { ...MEMORAONE_MCP_SERVER };
301
+ const merged = { ...base, mcpServers: servers };
302
+ return mcpJsonHeader() + JSON.stringify(merged, null, 2) + "\n";
303
+ }
304
+ async function writeManagedMarkdown(repoRoot, relPath, fullContent, opts) {
305
+ const abs = path3.join(repoRoot, relPath);
306
+ assertUnderRepoRoot(repoRoot, abs);
307
+ let prior = "";
308
+ let existed = false;
309
+ try {
310
+ prior = await fs3.readFile(abs, "utf8");
311
+ existed = true;
312
+ } catch (err) {
313
+ if (err?.code !== "ENOENT") throw err;
314
+ }
315
+ if (!existed) {
316
+ if (opts.dryRun) return "created";
317
+ await fs3.mkdir(path3.dirname(abs), { recursive: true });
318
+ await fs3.writeFile(abs, fullContent, "utf8");
319
+ return "created";
320
+ }
321
+ if (prior.includes(MANAGED_MARKER)) {
322
+ if (prior === fullContent) return "skipped";
323
+ if (opts.dryRun) return "updated";
324
+ await fs3.mkdir(path3.dirname(abs), { recursive: true });
325
+ await fs3.writeFile(abs, fullContent, "utf8");
326
+ return "updated";
327
+ }
328
+ if (!opts.force) return "skipped-untracked";
329
+ if (opts.dryRun) return "updated";
330
+ await fs3.mkdir(path3.dirname(abs), { recursive: true });
331
+ await fs3.writeFile(abs, fullContent, "utf8");
332
+ return "updated";
333
+ }
334
+ async function writeMcpJson(repoRoot, opts) {
335
+ const abs = path3.join(repoRoot, ".vscode/mcp.json");
336
+ assertUnderRepoRoot(repoRoot, abs);
337
+ let raw = "";
338
+ let existed = false;
339
+ try {
340
+ raw = await fs3.readFile(abs, "utf8");
341
+ existed = true;
342
+ } catch (err) {
343
+ if (err?.code !== "ENOENT") throw err;
344
+ }
345
+ if (!existed) {
346
+ const body = buildMcpJsonBody(null);
347
+ if (opts.dryRun) return "created";
348
+ await fs3.mkdir(path3.dirname(abs), { recursive: true });
349
+ await fs3.writeFile(abs, body, "utf8");
350
+ return "created";
351
+ }
352
+ const managed = raw.includes(MANAGED_MARKER);
353
+ if (!managed && !opts.force) return "skipped-untracked";
354
+ let parsed = null;
355
+ try {
356
+ parsed = JSON.parse(stripLeadingLineComments(raw));
357
+ } catch {
358
+ parsed = null;
359
+ }
360
+ if (!parsed && !opts.force) return "skipped-untracked";
361
+ const next = buildMcpJsonBody(parsed);
362
+ if (managed && next === raw) return "skipped";
363
+ if (opts.dryRun) return "updated";
364
+ await fs3.mkdir(path3.dirname(abs), { recursive: true });
365
+ await fs3.writeFile(abs, next, "utf8");
366
+ return "updated";
367
+ }
368
+ function parseSetupIdeFlags(argv) {
369
+ let cursor = false;
370
+ let vscode = false;
371
+ let jetbrains = false;
372
+ let all = false;
373
+ let force = false;
374
+ let dryRun = false;
375
+ const unknown = [];
376
+ for (const a of argv) {
377
+ if (a === "--cursor") cursor = true;
378
+ else if (a === "--vscode") vscode = true;
379
+ else if (a === "--jetbrains") jetbrains = true;
380
+ else if (a === "--all") all = true;
381
+ else if (a === "--force") force = true;
382
+ else if (a === "--dry-run") dryRun = true;
383
+ else if (a.startsWith("-")) unknown.push(a);
384
+ }
385
+ const specific = cursor || vscode || jetbrains;
386
+ let targets;
387
+ if (all || !specific) {
388
+ targets = { cursor: true, vscode: true, jetbrains: true };
389
+ } else {
390
+ targets = { cursor, vscode, jetbrains };
391
+ }
392
+ return { targets, force, dryRun, unknown };
393
+ }
394
+ function summarizeOutcomes(outcomes) {
395
+ const created = [];
396
+ const updated = [];
397
+ const skipped = [];
398
+ const skippedUntracked = [];
399
+ for (const [file, o] of Object.entries(outcomes)) {
400
+ if (o === "created") created.push(file);
401
+ else if (o === "updated") updated.push(file);
402
+ else if (o === "skipped") skipped.push(file);
403
+ else skippedUntracked.push(file);
404
+ }
405
+ const lines = ["[setup-ide-files] Summary"];
406
+ if (created.length) lines.push(` created: ${created.join(", ")}`);
407
+ if (updated.length) lines.push(` updated: ${updated.join(", ")}`);
408
+ if (skipped.length) lines.push(` skipped (unchanged): ${skipped.join(", ")}`);
409
+ if (skippedUntracked.length) {
410
+ lines.push(` skipped (unmanaged existing file, use --force): ${skippedUntracked.join(", ")}`);
411
+ }
412
+ console.log(lines.join("\n"));
413
+ }
414
+ async function runSetupIdeFiles(o) {
415
+ const outcomes = {};
416
+ const repoRoot = await findRepoRoot(o.cwd);
417
+ if (!repoRoot) {
418
+ return {
419
+ exitCode: 1,
420
+ repoRoot: null,
421
+ outcomes,
422
+ error: "[setup-ide-files] No repo root found (looked for .git or memoraone.m1)."
423
+ };
424
+ }
425
+ const cursorContent = `---
426
+ description: MemoraOne MCP \u2014 IDE agent instructions
427
+ ---
428
+
429
+ ` + cursorRuleBody();
430
+ if (o.targets.cursor) {
431
+ outcomes[".cursor/rules/memoraone-mcp.mdc"] = await writeManagedMarkdown(
432
+ repoRoot,
433
+ ".cursor/rules/memoraone-mcp.mdc",
434
+ cursorContent,
435
+ { force: o.force, dryRun: o.dryRun }
436
+ );
437
+ }
438
+ if (o.targets.vscode) {
439
+ outcomes[".vscode/mcp.json"] = await writeMcpJson(repoRoot, {
440
+ force: o.force,
441
+ dryRun: o.dryRun
442
+ });
443
+ outcomes[".github/copilot-instructions.md"] = await writeManagedMarkdown(
444
+ repoRoot,
445
+ ".github/copilot-instructions.md",
446
+ copilotAndJetBrainsBody("MemoraOne MCP \u2014 GitHub Copilot"),
447
+ { force: o.force, dryRun: o.dryRun }
448
+ );
449
+ }
450
+ if (o.targets.jetbrains) {
451
+ outcomes[".aiassistant/rules/memoraone.md"] = await writeManagedMarkdown(
452
+ repoRoot,
453
+ ".aiassistant/rules/memoraone.md",
454
+ copilotAndJetBrainsBody("MemoraOne MCP \u2014 JetBrains AI Assistant"),
455
+ { force: o.force, dryRun: o.dryRun }
456
+ );
457
+ }
458
+ return { exitCode: 0, repoRoot, outcomes };
459
+ }
460
+ async function cliSetupIdeFiles(argv) {
461
+ const { targets, force, dryRun, unknown } = parseSetupIdeFlags(argv);
462
+ if (unknown.length) {
463
+ console.error(`[setup-ide-files] Unknown option(s): ${unknown.join(", ")}`);
464
+ return 1;
465
+ }
466
+ const result = await runSetupIdeFiles({
467
+ cwd: process.cwd(),
468
+ targets,
469
+ force,
470
+ dryRun
471
+ });
472
+ if (result.error) {
473
+ console.error(result.error);
474
+ return result.exitCode;
475
+ }
476
+ if (result.repoRoot) {
477
+ console.log(`[setup-ide-files] Repo root: ${result.repoRoot}`);
478
+ }
479
+ summarizeOutcomes(result.outcomes);
480
+ if (dryRun) {
481
+ console.log("[setup-ide-files] Dry run: no files written.");
482
+ }
483
+ return 0;
484
+ }
485
+
222
486
  // src/cli.ts
223
487
  var { version } = require_package();
224
488
  var args = process.argv.slice(2);
@@ -227,10 +491,18 @@ if (args.includes("--version") || args.includes("-v")) {
227
491
  process.exit(0);
228
492
  }
229
493
  if (args.includes("--help") || args.includes("-h")) {
230
- console.log("Usage: memoraone-mcp [--version] [--help] [--daemon --project-id <uuid>]");
494
+ console.log(
495
+ "Usage: memoraone-mcp [--version] [--help] [--daemon --project-id <uuid>]\n memoraone-mcp setup-ide-files [--all|--cursor|--vscode|--jetbrains] [--force] [--dry-run]"
496
+ );
231
497
  process.exit(0);
232
498
  }
233
- if (args.includes("--daemon")) {
499
+ if (args[0] === "setup-ide-files") {
500
+ cliSetupIdeFiles(args.slice(1)).then((code) => process.exit(code)).catch((err) => {
501
+ process.stderr.write(`[memoraone-mcp] setup-ide-files fatal: ${String(err)}
502
+ `);
503
+ process.exit(1);
504
+ });
505
+ } else if (args.includes("--daemon")) {
234
506
  import("./daemon.cjs").then(({ runDaemon }) => runDaemon()).catch((err) => {
235
507
  process.stderr.write(`[memoraone-mcp] daemon fatal: ${String(err)}
236
508
  `);
@@ -241,8 +513,8 @@ if (args.includes("--daemon")) {
241
513
  const raw = process.env.WORKSPACE_FOLDER_PATHS;
242
514
  const parts = [];
243
515
  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));
516
+ for (const p of raw.split(path4.delimiter).map((s) => s.trim()).filter(Boolean)) {
517
+ parts.push(path4.resolve(p));
246
518
  }
247
519
  }
248
520
  parts.push(process.cwd());
@@ -256,10 +528,10 @@ if (args.includes("--daemon")) {
256
528
  }
257
529
  return deduped;
258
530
  }, connectWithRetry = function(socketPath) {
259
- return new Promise((resolve3, reject) => {
531
+ return new Promise((resolve4, reject) => {
260
532
  const tryConnect = (attempt) => {
261
533
  const socket = net.connect(socketPath, () => {
262
- resolve3(socket);
534
+ resolve4(socket);
263
535
  });
264
536
  socket.on("error", (err) => {
265
537
  if (attempt >= MAX_RETRIES) {
@@ -308,12 +580,6 @@ if (args.includes("--daemon")) {
308
580
  }
309
581
  log("bridge connected");
310
582
  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
583
  process.stdin.pipe(socket);
318
584
  socket.pipe(process.stdout);
319
585
  socket.on("close", () => process.exit(0));