@membank/cli 0.0.1 → 0.0.3

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.
Files changed (2) hide show
  1. package/dist/index.mjs +165 -62
  2. package/package.json +3 -3
package/dist/index.mjs CHANGED
@@ -7,6 +7,8 @@ import { dirname, join } from "node:path";
7
7
  import * as readline from "node:readline";
8
8
  import { createInterface } from "node:readline";
9
9
  import { homedir, tmpdir } from "node:os";
10
+ import { execFile } from "node:child_process";
11
+ import { promisify } from "node:util";
10
12
  import { EventEmitter } from "node:events";
11
13
  import { pipeline } from "@huggingface/transformers";
12
14
  //#region src/commands/add.ts
@@ -80,12 +82,12 @@ const MEMORY_TYPES = new Set([
80
82
  function isValidRecord(r) {
81
83
  if (typeof r !== "object" || r === null) return false;
82
84
  const rec = r;
83
- return typeof rec["id"] === "string" && rec["id"].length > 0 && typeof rec["content"] === "string" && typeof rec["type"] === "string" && MEMORY_TYPES.has(rec["type"]) && typeof rec["scope"] === "string" && rec["scope"].length > 0;
85
+ return typeof rec.id === "string" && rec.id.length > 0 && typeof rec.content === "string" && typeof rec.type === "string" && MEMORY_TYPES.has(rec.type) && typeof rec.scope === "string" && rec.scope.length > 0;
84
86
  }
85
87
  function isExportFile(parsed) {
86
88
  if (typeof parsed !== "object" || parsed === null) return false;
87
89
  const obj = parsed;
88
- return obj["version"] === 1 && Array.isArray(obj["memories"]);
90
+ return obj.version === 1 && Array.isArray(obj.memories);
89
91
  }
90
92
  async function importCommand(filePath, db, formatter, prompt) {
91
93
  let raw;
@@ -314,19 +316,50 @@ var PromptHelper = class {
314
316
  }
315
317
  };
316
318
  //#endregion
319
+ //#region src/utils/execFileNoThrow.ts
320
+ const execFileAsync = promisify(execFile);
321
+ function resolveCmd(cmd) {
322
+ return process.platform === "win32" ? `${cmd}.cmd` : cmd;
323
+ }
324
+ async function execFileNoThrow(cmd, args) {
325
+ try {
326
+ const { stdout, stderr } = await execFileAsync(resolveCmd(cmd), args, { encoding: "utf8" });
327
+ return {
328
+ stdout: stdout ?? "",
329
+ stderr: stderr ?? "",
330
+ exitCode: 0
331
+ };
332
+ } catch (err) {
333
+ if (err !== null && typeof err === "object" && "code" in err) {
334
+ const e = err;
335
+ if (e.code === "ENOENT") return {
336
+ stdout: "",
337
+ stderr: `Command not found: ${cmd}`,
338
+ exitCode: 127
339
+ };
340
+ return {
341
+ stdout: typeof e.stdout === "string" ? e.stdout : "",
342
+ stderr: typeof e.stderr === "string" ? e.stderr : "",
343
+ exitCode: typeof e.code === "number" ? e.code : 1
344
+ };
345
+ }
346
+ return {
347
+ stdout: "",
348
+ stderr: err instanceof Error ? err.message : String(err),
349
+ exitCode: 1
350
+ };
351
+ }
352
+ }
353
+ //#endregion
317
354
  //#region src/setup/harness-config-writer.ts
318
355
  const defaultPathResolver = {
319
356
  home: () => {
320
- const h = process.env["HOME"] ?? process.env["USERPROFILE"];
357
+ const h = process.env.HOME ?? process.env.USERPROFILE;
321
358
  if (!h) throw new Error("Cannot determine home directory");
322
359
  return h;
323
360
  },
324
361
  cwd: () => process.cwd()
325
362
  };
326
- const MEMBANK_ENTRY = {
327
- command: "npx",
328
- args: ["@membank/cli", "--mcp"]
329
- };
330
363
  function readJson(path) {
331
364
  try {
332
365
  return JSON.parse(readFileSync(path, "utf8"));
@@ -343,66 +376,133 @@ function writeJsonAtomic(path, data) {
343
376
  function hasKey(container, key) {
344
377
  return container !== null && typeof container === "object" && key in container;
345
378
  }
379
+ function assertCliFound(result, cli) {
380
+ if (result.exitCode === 127) throw new Error(`${cli} CLI not found — install ${cli} first`);
381
+ }
382
+ const MEMBANK_NPX_ARGS = [
383
+ "npx",
384
+ "@membank/cli@latest",
385
+ "--mcp"
386
+ ];
346
387
  const writers = {
347
- "claude-code": {
348
- configPath: (r) => join(r.home(), ".claude", "settings.json"),
349
- isConfigured: (cfg) => hasKey(cfg["mcpServers"], "membank"),
350
- merge: (cfg) => ({
351
- ...cfg,
352
- mcpServers: {
353
- ...cfg["mcpServers"],
354
- membank: MEMBANK_ENTRY
355
- }
356
- })
357
- },
358
- vscode: {
359
- configPath: (r) => join(r.cwd(), ".vscode", "mcp.json"),
360
- isConfigured: (cfg) => hasKey(cfg["servers"], "membank"),
361
- merge: (cfg) => ({
362
- ...cfg,
363
- servers: {
364
- ...cfg["servers"],
365
- membank: MEMBANK_ENTRY
366
- }
367
- })
368
- },
369
- codex: {
370
- configPath: (r) => join(r.home(), ".codex", "config.json"),
371
- isConfigured: (cfg) => hasKey(cfg["mcpServers"], "membank"),
372
- merge: (cfg) => ({
373
- ...cfg,
374
- mcpServers: {
375
- ...cfg["mcpServers"],
376
- membank: MEMBANK_ENTRY
377
- }
378
- })
379
- },
380
- opencode: {
381
- configPath: (r) => join(r.home(), ".config", "opencode", "config.json"),
382
- isConfigured: (cfg) => hasKey(cfg["mcp"], "membank"),
383
- merge: (cfg) => ({
388
+ "claude-code": { async write(resolver, run, { overwrite = false } = {}) {
389
+ const configured = hasKey(readJson(join(resolver.home(), ".claude.json")).mcpServers, "membank");
390
+ if (configured && !overwrite) return { status: "already-configured" };
391
+ if (configured) {
392
+ const remove = await run("claude", [
393
+ "mcp",
394
+ "remove",
395
+ "--scope",
396
+ "user",
397
+ "membank"
398
+ ]);
399
+ assertCliFound(remove, "claude");
400
+ if (remove.exitCode !== 0) throw new Error(`claude mcp remove failed: ${remove.stderr}`);
401
+ }
402
+ const add = await run("claude", [
403
+ "mcp",
404
+ "add",
405
+ "--scope",
406
+ "user",
407
+ "membank",
408
+ "--",
409
+ ...MEMBANK_NPX_ARGS
410
+ ]);
411
+ assertCliFound(add, "claude");
412
+ if (add.exitCode !== 0) throw new Error(`claude mcp add failed: ${add.stderr || add.stdout}`);
413
+ return { status: "written" };
414
+ } },
415
+ vscode: { async write(resolver, run, { overwrite = false } = {}) {
416
+ const cfgPath = join(resolver.cwd(), ".vscode", "mcp.json");
417
+ const cfg = readJson(cfgPath);
418
+ const configured = hasKey(cfg.servers, "membank");
419
+ if (configured && !overwrite) return { status: "already-configured" };
420
+ if (configured) {
421
+ writeJsonAtomic(cfgPath, {
422
+ ...cfg,
423
+ servers: {
424
+ ...cfg.servers,
425
+ membank: {
426
+ command: "npx",
427
+ args: ["@membank/cli@latest", "--mcp"]
428
+ }
429
+ }
430
+ });
431
+ return { status: "written" };
432
+ }
433
+ const payload = JSON.stringify({
434
+ name: "membank",
435
+ command: "npx",
436
+ args: ["@membank/cli@latest", "--mcp"]
437
+ });
438
+ const result = await run("code", [
439
+ "--folder-uri",
440
+ resolver.cwd(),
441
+ "--add-mcp",
442
+ payload
443
+ ]);
444
+ assertCliFound(result, "code");
445
+ if (result.exitCode !== 0) throw new Error(`code --add-mcp failed: ${result.stderr || result.stdout}`);
446
+ return { status: "written" };
447
+ } },
448
+ codex: { async write(_resolver, run, { overwrite = false } = {}) {
449
+ const list = await run("codex", ["mcp", "list"]);
450
+ assertCliFound(list, "codex");
451
+ const configured = list.exitCode === 0 && list.stdout.includes("membank");
452
+ if (configured && !overwrite) return { status: "already-configured" };
453
+ if (configured) {
454
+ const remove = await run("codex", [
455
+ "mcp",
456
+ "remove",
457
+ "membank"
458
+ ]);
459
+ assertCliFound(remove, "codex");
460
+ if (remove.exitCode !== 0) throw new Error(`codex mcp remove failed: ${remove.stderr}`);
461
+ }
462
+ const add = await run("codex", [
463
+ "mcp",
464
+ "add",
465
+ "membank",
466
+ "--",
467
+ ...MEMBANK_NPX_ARGS
468
+ ]);
469
+ assertCliFound(add, "codex");
470
+ if (add.exitCode !== 0) throw new Error(`codex mcp add failed: ${add.stderr || add.stdout}`);
471
+ return { status: "written" };
472
+ } },
473
+ opencode: { async write(resolver, _run, { overwrite = false } = {}) {
474
+ const cfgPath = join(resolver.home(), ".config", "opencode", "opencode.json");
475
+ const cfg = readJson(cfgPath);
476
+ if (hasKey(cfg.mcp, "membank") && !overwrite) return { status: "already-configured" };
477
+ writeJsonAtomic(cfgPath, {
384
478
  ...cfg,
385
479
  mcp: {
386
- ...cfg["mcp"],
387
- membank: MEMBANK_ENTRY
480
+ ...cfg.mcp,
481
+ membank: {
482
+ type: "local",
483
+ command: [
484
+ "npx",
485
+ "@membank/cli@latest",
486
+ "--mcp"
487
+ ]
488
+ }
388
489
  }
389
- })
390
- }
490
+ });
491
+ return { status: "written" };
492
+ } }
391
493
  };
392
494
  const SUPPORTED_HARNESSES = Object.keys(writers);
393
495
  var HarnessConfigWriter = class {
394
496
  #resolver;
395
- constructor(resolver = defaultPathResolver) {
497
+ #run;
498
+ constructor(resolver = defaultPathResolver, run = execFileNoThrow) {
396
499
  this.#resolver = resolver;
500
+ this.#run = run;
397
501
  }
398
- write(harness, { overwrite = false } = {}) {
502
+ async write(harness, { overwrite = false } = {}) {
399
503
  const writer = writers[harness];
400
504
  if (!writer) throw new Error(`Unknown harness: ${harness}`);
401
- const path = writer.configPath(this.#resolver);
402
- const existing = readJson(path);
403
- if (!overwrite && writer.isConfigured(existing)) return { status: "already-configured" };
404
- writeJsonAtomic(path, writer.merge(existing));
405
- return { status: "written" };
505
+ return writer.write(this.#resolver, this.#run, { overwrite });
406
506
  }
407
507
  };
408
508
  //#endregion
@@ -481,7 +581,8 @@ function harnessConfigs(resolver) {
481
581
  return [
482
582
  {
483
583
  name: "claude-code",
484
- configPath: join(home, ".claude", "settings.json")
584
+ configPath: join(home, ".claude.json"),
585
+ fallbackPaths: [join(home, ".claude", "settings.json")]
485
586
  },
486
587
  {
487
588
  name: "vscode",
@@ -489,16 +590,18 @@ function harnessConfigs(resolver) {
489
590
  },
490
591
  {
491
592
  name: "codex",
492
- configPath: join(home, ".codex", "config.json")
593
+ configPath: join(home, ".codex", "config.toml"),
594
+ fallbackPaths: [join(home, ".codex", "config.json")]
493
595
  },
494
596
  {
495
597
  name: "opencode",
496
- configPath: join(home, ".config", "opencode", "config.json")
598
+ configPath: join(home, ".config", "opencode", "opencode.json"),
599
+ fallbackPaths: [join(home, ".config", "opencode", "config.json")]
497
600
  }
498
601
  ];
499
602
  }
500
603
  function detectHarnesses(resolver = defaultResolver) {
501
- return harnessConfigs(resolver).filter((h) => existsSync(h.configPath)).map((h) => ({
604
+ return harnessConfigs(resolver).filter((h) => existsSync(h.configPath) || (h.fallbackPaths?.some((p) => existsSync(p)) ?? false)).map((h) => ({
502
605
  name: h.name,
503
606
  configPath: h.configPath
504
607
  }));
@@ -582,7 +685,7 @@ var SetupOrchestrator = class {
582
685
  for (const h of detected) {
583
686
  let writeResult;
584
687
  try {
585
- writeResult = this.#writer.write(h.name);
688
+ writeResult = await this.#writer.write(h.name);
586
689
  } catch (err) {
587
690
  const msg = err instanceof Error ? err.message : String(err);
588
691
  out(` ✗ ${h.name}: ${msg}`);
@@ -605,7 +708,7 @@ var SetupOrchestrator = class {
605
708
  continue;
606
709
  }
607
710
  try {
608
- this.#writer.write(h.name, { overwrite: true });
711
+ await this.#writer.write(h.name, { overwrite: true });
609
712
  out(` ✓ ${h.name}: written (overwritten)`);
610
713
  results.push({
611
714
  harness: h.name,
@@ -812,6 +915,6 @@ program.on("command:*", () => {
812
915
  program.outputHelp();
813
916
  process.exit(1);
814
917
  });
815
- program.parse(process.argv);
918
+ if (!process.argv.includes("--mcp")) program.parse(process.argv);
816
919
  //#endregion
817
920
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@membank/cli",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -17,8 +17,8 @@
17
17
  "@huggingface/transformers": "^4.2.0",
18
18
  "commander": "^14.0.3",
19
19
  "ora": "^9.4.0",
20
- "@membank/core": "0.0.1",
21
- "@membank/mcp": "0.0.1"
20
+ "@membank/core": "0.0.3",
21
+ "@membank/mcp": "0.0.3"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@types/node": "^25.6.0",