@pyxmate/memory 0.22.4 → 0.22.5

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/cli/pyx-mem.mjs +331 -29
  2. package/package.json +1 -1
@@ -15479,7 +15479,7 @@ var ALL_TOOL_NAMES = ALL_TOOLS.map((t) => t.name);
15479
15479
  // src/mcp/server.ts
15480
15480
  async function runMcpServer(opts) {
15481
15481
  const fetchImpl = opts.fetchImpl ?? fetch;
15482
- const version2 = opts.version ?? (true ? "0.22.4" : "0.0.0-dev");
15482
+ const version2 = opts.version ?? (true ? "0.22.5" : "0.0.0-dev");
15483
15483
  const server = new McpServer(
15484
15484
  { name: "pyx-memory", version: version2 },
15485
15485
  { instructions: PYX_MEMORY_INSTRUCTIONS, capabilities: { tools: {} } }
@@ -15518,21 +15518,136 @@ async function mcpCommand() {
15518
15518
  }
15519
15519
 
15520
15520
  // src/cli/commands/mcp-install.ts
15521
- import { spawnSync } from "child_process";
15521
+ import { spawnSync as nodeSpawnSync } from "child_process";
15522
+ import { homedir } from "os";
15523
+ import { join } from "path";
15524
+
15525
+ // src/cli/commands/mcp-install-merge.ts
15526
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
15527
+ import { dirname } from "path";
15528
+ function mergeWriteJsonMcpEntry(opts) {
15529
+ const { filePath, serverName, entry } = opts;
15530
+ let existingDoc = {};
15531
+ if (existsSync(filePath)) {
15532
+ const raw = readFileSync(filePath, "utf8").trim();
15533
+ if (raw.length > 0) {
15534
+ try {
15535
+ const parsed = JSON.parse(raw);
15536
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
15537
+ return {
15538
+ exitCode: EXIT.USAGE,
15539
+ status: "parse-error",
15540
+ message: `Refusing to write: ${filePath} is not a JSON object at its root.
15541
+ Fix the file manually, then re-run.
15542
+ `
15543
+ };
15544
+ }
15545
+ existingDoc = parsed;
15546
+ } catch (err) {
15547
+ const reason = err instanceof Error ? err.message : String(err);
15548
+ return {
15549
+ exitCode: EXIT.USAGE,
15550
+ status: "parse-error",
15551
+ message: `Refusing to write: ${filePath} contains invalid JSON (${reason}).
15552
+ Fix the file manually, then re-run.
15553
+ `
15554
+ };
15555
+ }
15556
+ }
15557
+ }
15558
+ const rawServers = existingDoc.mcpServers;
15559
+ const servers = rawServers !== null && typeof rawServers === "object" && !Array.isArray(rawServers) ? { ...rawServers } : {};
15560
+ const existingEntry = servers[serverName];
15561
+ if (existingEntry !== void 0) {
15562
+ if (entriesEqual(existingEntry, entry)) {
15563
+ return {
15564
+ exitCode: EXIT.OK,
15565
+ status: "already-installed",
15566
+ message: `pyx-memory is already installed in ${filePath} (no changes written).
15567
+ `
15568
+ };
15569
+ }
15570
+ return {
15571
+ exitCode: EXIT.USAGE,
15572
+ status: "conflict",
15573
+ message: `Refusing to overwrite existing \`${serverName}\` entry in ${filePath}.
15574
+
15575
+ Found:
15576
+ ${indent(JSON.stringify(existingEntry, null, 2))}
15577
+
15578
+ Proposed:
15579
+ ${indent(JSON.stringify(entry, null, 2))}
15580
+
15581
+ Remove or rename the existing entry, then re-run.
15582
+ `
15583
+ };
15584
+ }
15585
+ servers[serverName] = entry;
15586
+ const nextDoc = { ...existingDoc, mcpServers: servers };
15587
+ const payload = `${JSON.stringify(nextDoc, null, 2)}
15588
+ `;
15589
+ const parent = dirname(filePath);
15590
+ if (!existsSync(parent)) {
15591
+ mkdirSync(parent, { recursive: true });
15592
+ }
15593
+ const tmpPath = `${filePath}.tmp`;
15594
+ writeFileSync(tmpPath, payload, { mode: 384 });
15595
+ renameSync(tmpPath, filePath);
15596
+ return {
15597
+ exitCode: EXIT.OK,
15598
+ status: "installed",
15599
+ message: `Installed \`${serverName}\` MCP server entry in ${filePath}.
15600
+ `
15601
+ };
15602
+ }
15603
+ function entriesEqual(a, b) {
15604
+ if (a === null || typeof a !== "object") return false;
15605
+ const left = a;
15606
+ if (left.command !== b.command) return false;
15607
+ if (!arraysEqual(left.args, b.args)) return false;
15608
+ if (!recordsEqual(left.env, b.env)) return false;
15609
+ return true;
15610
+ }
15611
+ function arraysEqual(a, b) {
15612
+ const left = Array.isArray(a) ? a : void 0;
15613
+ const right = b;
15614
+ if (left === void 0 && right === void 0) return true;
15615
+ if (left === void 0 || right === void 0)
15616
+ return (left ?? []).length === 0 && (right ?? []).length === 0;
15617
+ if (left.length !== right.length) return false;
15618
+ for (let i = 0; i < left.length; i++) {
15619
+ if (left[i] !== right[i]) return false;
15620
+ }
15621
+ return true;
15622
+ }
15623
+ function recordsEqual(a, b) {
15624
+ const left = a !== null && typeof a === "object" && !Array.isArray(a) ? a : void 0;
15625
+ const right = b;
15626
+ if (left === void 0 && right === void 0) return true;
15627
+ const leftKeys = left ? Object.keys(left) : [];
15628
+ const rightKeys = right ? Object.keys(right) : [];
15629
+ if (leftKeys.length === 0 && rightKeys.length === 0) return true;
15630
+ if (leftKeys.length !== rightKeys.length) return false;
15631
+ for (const k of leftKeys) {
15632
+ if (left?.[k] !== right?.[k]) return false;
15633
+ }
15634
+ return true;
15635
+ }
15636
+ function indent(text) {
15637
+ return text.split("\n").map((line) => ` ${line}`).join("\n");
15638
+ }
15639
+
15640
+ // src/cli/commands/mcp-install.ts
15522
15641
  var VALID_SCOPES = /* @__PURE__ */ new Set(["local", "user", "project"]);
15523
15642
  var SERVER_NAME = "pyx-memory";
15643
+ var ENTRY = { command: "pyx-mem", args: ["mcp"] };
15524
15644
  function mcpInstallClaudeCodeCommand(opts = {}) {
15525
15645
  const scope = opts.scope ?? "local";
15526
- if (!VALID_SCOPES.has(scope)) {
15527
- process.stderr.write(
15528
- `Error: invalid --scope \`${scope}\`. Expected: local | user | project.
15529
- `
15530
- );
15531
- return EXIT.USAGE;
15532
- }
15533
- const claudeProbe = spawnSync("claude", ["--version"], { stdio: "pipe" });
15534
- if (claudeProbe.error || claudeProbe.status !== 0) {
15535
- printManualInstructions(scope);
15646
+ if (!validateScope(scope)) return EXIT.USAGE;
15647
+ const spawnSync = opts._spawnSync ?? nodeSpawnSync;
15648
+ const probe = spawnSync("claude", ["--version"], { stdio: "pipe" });
15649
+ if (probe.error || probe.status !== 0) {
15650
+ printClaudeCodeManualInstructions(scope);
15536
15651
  return EXIT.OK;
15537
15652
  }
15538
15653
  const cmd = ["mcp", "add", SERVER_NAME, "-s", scope, "--", "pyx-mem", "mcp"];
@@ -15540,7 +15655,7 @@ function mcpInstallClaudeCodeCommand(opts = {}) {
15540
15655
  if (res.status !== 0) {
15541
15656
  process.stderr.write(`\`claude ${cmd.join(" ")}\` exited with code ${res.status ?? "null"}.
15542
15657
  `);
15543
- printManualInstructions(scope);
15658
+ printClaudeCodeManualInstructions(scope);
15544
15659
  return EXIT.USAGE;
15545
15660
  }
15546
15661
  process.stdout.write(
@@ -15550,7 +15665,7 @@ Restart Claude Code to make the tools available. No API key was written to .mcp.
15550
15665
  );
15551
15666
  return EXIT.OK;
15552
15667
  }
15553
- function printManualInstructions(scope) {
15668
+ function printClaudeCodeManualInstructions(scope) {
15554
15669
  process.stdout.write(
15555
15670
  [
15556
15671
  "The `claude` CLI is not on PATH, or the install command failed.",
@@ -15560,19 +15675,182 @@ function printManualInstructions(scope) {
15560
15675
  "",
15561
15676
  "Or add this entry manually to your Claude Code MCP config (.mcp.json):",
15562
15677
  JSON.stringify(
15563
- {
15564
- mcpServers: {
15565
- [SERVER_NAME]: { command: "pyx-mem", args: ["mcp"] }
15566
- }
15567
- },
15678
+ { mcpServers: { [SERVER_NAME]: { command: "pyx-mem", args: ["mcp"] } } },
15679
+ null,
15680
+ 2
15681
+ ),
15682
+ "",
15683
+ "Do NOT add your pyx-memory API key here. Run `pyx-mem login` to store it in the OS credential store.",
15684
+ ""
15685
+ ].join("\n")
15686
+ );
15687
+ }
15688
+ function mcpInstallCodexCommand(opts = {}) {
15689
+ const scope = opts.scope ?? "user";
15690
+ if (!validateScope(scope)) return EXIT.USAGE;
15691
+ const spawnSync = opts._spawnSync ?? nodeSpawnSync;
15692
+ const home = opts._homeDir ?? homedir();
15693
+ const probe = spawnSync("codex", ["--version"], { stdio: "pipe" });
15694
+ if (probe.error || probe.status !== 0) {
15695
+ printCodexManualInstructions(home);
15696
+ return EXIT.OK;
15697
+ }
15698
+ const cmd = ["mcp", "add", SERVER_NAME, "--", "pyx-mem", "mcp"];
15699
+ const res = spawnSync("codex", cmd, { stdio: "inherit" });
15700
+ if (res.status !== 0) {
15701
+ process.stderr.write(`\`codex ${cmd.join(" ")}\` exited with code ${res.status ?? "null"}.
15702
+ `);
15703
+ printCodexManualInstructions(home);
15704
+ return EXIT.USAGE;
15705
+ }
15706
+ process.stdout.write(
15707
+ `Installed pyx-memory MCP server in Codex CLI.
15708
+ Restart Codex CLI to make the tools available. No API key was written to config.toml \u2014 credentials live in the OS credential store.
15709
+ `
15710
+ );
15711
+ return EXIT.OK;
15712
+ }
15713
+ function printCodexManualInstructions(home) {
15714
+ const configPath = join(home, ".codex", "config.toml");
15715
+ process.stdout.write(
15716
+ [
15717
+ "The `codex` CLI is not on PATH, or the install command failed.",
15718
+ "",
15719
+ "Run this once Codex CLI is installed:",
15720
+ ` codex mcp add ${SERVER_NAME} -- pyx-mem mcp`,
15721
+ "",
15722
+ `Or add this section manually to ${configPath}:`,
15723
+ "",
15724
+ `[mcp_servers.${SERVER_NAME}]`,
15725
+ `command = "pyx-mem"`,
15726
+ `args = ["mcp"]`,
15727
+ "",
15728
+ "Do NOT add your pyx-memory API key here. Run `pyx-mem login` to store it in the OS credential store.",
15729
+ ""
15730
+ ].join("\n")
15731
+ );
15732
+ }
15733
+ function mcpInstallCursorCommand(opts = {}) {
15734
+ const scope = opts.scope ?? "user";
15735
+ if (!validateScope(scope)) return EXIT.USAGE;
15736
+ const home = opts._homeDir ?? homedir();
15737
+ const filePath = scope === "project" ? join(process.cwd(), ".cursor", "mcp.json") : join(home, ".cursor", "mcp.json");
15738
+ return writeJsonAndReport(filePath, "Cursor");
15739
+ }
15740
+ function mcpInstallClineCommand(opts = {}) {
15741
+ const scope = opts.scope ?? "user";
15742
+ if (!validateScope(scope)) return EXIT.USAGE;
15743
+ process.stdout.write(
15744
+ [
15745
+ "Cline MCP config is managed through the Cline UI in VS Code (the file",
15746
+ "path differs by OS, VS Code variant, and Cline version).",
15747
+ "",
15748
+ "1. Open VS Code (or Cursor / Windsurf running the Cline extension).",
15749
+ "2. Click the Cline icon in the sidebar.",
15750
+ '3. Click "MCP Servers" \u2192 "Configure MCP Servers".',
15751
+ `4. Add this entry under "mcpServers":`,
15752
+ "",
15753
+ JSON.stringify(
15754
+ { mcpServers: { [SERVER_NAME]: { command: "pyx-mem", args: ["mcp"] } } },
15568
15755
  null,
15569
15756
  2
15570
15757
  ),
15571
15758
  "",
15759
+ "5. Save the file. Cline will reload the MCP servers automatically.",
15760
+ "",
15572
15761
  "Do NOT add your pyx-memory API key here. Run `pyx-mem login` to store it in the OS credential store.",
15573
15762
  ""
15574
15763
  ].join("\n")
15575
15764
  );
15765
+ return EXIT.OK;
15766
+ }
15767
+ function mcpInstallContinueCommand(opts = {}) {
15768
+ const scope = opts.scope ?? "user";
15769
+ if (!validateScope(scope)) return EXIT.USAGE;
15770
+ const home = opts._homeDir ?? homedir();
15771
+ const yamlPath = join(home, ".continue", "mcpServers", `${SERVER_NAME}.yaml`);
15772
+ process.stdout.write(
15773
+ [
15774
+ "Continue uses YAML config. The recommended way to add an MCP server is a",
15775
+ "standalone block file:",
15776
+ "",
15777
+ ` ${yamlPath}`,
15778
+ "",
15779
+ "Create that file with these contents:",
15780
+ "",
15781
+ `name: ${SERVER_NAME}`,
15782
+ "version: 0.0.1",
15783
+ "schema: v1",
15784
+ "mcpServers:",
15785
+ ` - name: ${SERVER_NAME}`,
15786
+ ` command: pyx-mem`,
15787
+ ` args:`,
15788
+ ` - mcp`,
15789
+ "",
15790
+ "Continue auto-loads new YAML blocks on next chat session \u2014 no restart",
15791
+ "needed in most setups.",
15792
+ "",
15793
+ "Do NOT add your pyx-memory API key here. Run `pyx-mem login` to store it in the OS credential store.",
15794
+ ""
15795
+ ].join("\n")
15796
+ );
15797
+ return EXIT.OK;
15798
+ }
15799
+ function mcpInstallWindsurfCommand(opts = {}) {
15800
+ const scope = opts.scope ?? "user";
15801
+ if (!validateScope(scope)) return EXIT.USAGE;
15802
+ const home = opts._homeDir ?? homedir();
15803
+ const filePath = join(home, ".codeium", "windsurf", "mcp_config.json");
15804
+ return writeJsonAndReport(filePath, "Windsurf");
15805
+ }
15806
+ function mcpInstallGeminiCliCommand(opts = {}) {
15807
+ const scope = opts.scope ?? "user";
15808
+ if (!validateScope(scope)) return EXIT.USAGE;
15809
+ const spawnSync = opts._spawnSync ?? nodeSpawnSync;
15810
+ const home = opts._homeDir ?? homedir();
15811
+ const probe = spawnSync("gemini", ["--version"], { stdio: "pipe" });
15812
+ if (probe.error || probe.status !== 0) {
15813
+ return writeJsonAndReport(geminiConfigPath(home, scope), "Gemini CLI");
15814
+ }
15815
+ const geminiScope = scope === "local" ? "project" : scope;
15816
+ const cmd = ["mcp", "add", "-s", geminiScope, SERVER_NAME, "pyx-mem", "mcp"];
15817
+ const res = spawnSync("gemini", cmd, { stdio: "inherit" });
15818
+ if (res.status !== 0) {
15819
+ process.stderr.write(`\`gemini ${cmd.join(" ")}\` exited with code ${res.status ?? "null"}.
15820
+ `);
15821
+ return writeJsonAndReport(geminiConfigPath(home, scope), "Gemini CLI");
15822
+ }
15823
+ process.stdout.write(
15824
+ `Installed pyx-memory MCP server in Gemini CLI (scope: ${geminiScope}).
15825
+ Restart Gemini CLI to make the tools available. No API key was written to settings.json \u2014 credentials live in the OS credential store.
15826
+ `
15827
+ );
15828
+ return EXIT.OK;
15829
+ }
15830
+ function geminiConfigPath(home, scope) {
15831
+ return scope === "project" ? join(process.cwd(), ".gemini", "settings.json") : join(home, ".gemini", "settings.json");
15832
+ }
15833
+ function validateScope(scope) {
15834
+ if (VALID_SCOPES.has(scope)) return true;
15835
+ process.stderr.write(`Error: invalid --scope \`${scope}\`. Expected: local | user | project.
15836
+ `);
15837
+ return false;
15838
+ }
15839
+ function writeJsonAndReport(filePath, agentLabel) {
15840
+ const result = mergeWriteJsonMcpEntry({
15841
+ filePath,
15842
+ serverName: SERVER_NAME,
15843
+ entry: ENTRY
15844
+ });
15845
+ const stream = result.exitCode === EXIT.OK ? process.stdout : process.stderr;
15846
+ stream.write(result.message);
15847
+ if (result.status === "installed") {
15848
+ process.stdout.write(
15849
+ `Restart ${agentLabel} to make the tools available. No API key was written \u2014 credentials live in the OS credential store.
15850
+ `
15851
+ );
15852
+ }
15853
+ return result.exitCode;
15576
15854
  }
15577
15855
 
15578
15856
  // src/cli/commands/status.ts
@@ -15630,8 +15908,9 @@ Commands:
15630
15908
  logout Delete stored pyx-memory credentials.
15631
15909
  doctor [--json] Diagnose keychain, credentials, backend, MCP startup.
15632
15910
  mcp Start stdio MCP server.
15633
- mcp install claude-code [--scope user|local|project]
15634
- Install Claude Code MCP config for pyx-mem mcp.
15911
+ mcp install <target> [--scope user|local|project]
15912
+ Install pyx-memory MCP config for your AI agent.
15913
+ Targets: claude-code, codex, cursor, cline, continue, windsurf, gemini-cli.
15635
15914
 
15636
15915
  Notes:
15637
15916
  - Credentials are stored only in the OS credential store (Keychain / libsecret / Credential Manager).
@@ -15684,6 +15963,15 @@ function parseArgs(argv) {
15684
15963
  }
15685
15964
 
15686
15965
  // src/cli/pyx-mem.ts
15966
+ var VALID_TARGETS = [
15967
+ "claude-code",
15968
+ "codex",
15969
+ "cursor",
15970
+ "cline",
15971
+ "continue",
15972
+ "windsurf",
15973
+ "gemini-cli"
15974
+ ];
15687
15975
  async function main() {
15688
15976
  const parsed = parseArgs(process.argv.slice(2));
15689
15977
  if (parsed.flags.help === true || parsed.flags.h === true) {
@@ -15710,15 +15998,29 @@ async function main() {
15710
15998
  if (parsed.subcommand === void 0) return mcpCommand();
15711
15999
  if (parsed.subcommand === "install") {
15712
16000
  const target = parsed.positional[0];
15713
- if (target === "claude-code") {
15714
- const scope = typeof parsed.flags.scope === "string" ? parsed.flags.scope : void 0;
15715
- return mcpInstallClaudeCodeCommand({ scope });
15716
- }
15717
- process.stderr.write(
15718
- `Error: unknown install target \`${target ?? ""}\`. Expected: claude-code.
16001
+ const scope = typeof parsed.flags.scope === "string" ? parsed.flags.scope : void 0;
16002
+ switch (target) {
16003
+ case "claude-code":
16004
+ return mcpInstallClaudeCodeCommand({ scope });
16005
+ case "codex":
16006
+ return mcpInstallCodexCommand({ scope });
16007
+ case "cursor":
16008
+ return mcpInstallCursorCommand({ scope });
16009
+ case "cline":
16010
+ return mcpInstallClineCommand({ scope });
16011
+ case "continue":
16012
+ return mcpInstallContinueCommand({ scope });
16013
+ case "windsurf":
16014
+ return mcpInstallWindsurfCommand({ scope });
16015
+ case "gemini-cli":
16016
+ return mcpInstallGeminiCliCommand({ scope });
16017
+ default:
16018
+ process.stderr.write(
16019
+ `Error: unknown install target \`${target ?? ""}\`. Expected: ${VALID_TARGETS.join(", ")}.
15719
16020
  `
15720
- );
15721
- return EXIT.USAGE;
16021
+ );
16022
+ return EXIT.USAGE;
16023
+ }
15722
16024
  }
15723
16025
  process.stderr.write(
15724
16026
  `Error: unknown subcommand \`mcp ${parsed.subcommand}\`. Run \`pyx-mem --help\`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyxmate/memory",
3
- "version": "0.22.4",
3
+ "version": "0.22.5",
4
4
  "type": "module",
5
5
  "description": "SDK for pyx-memory — Memory as a Service for AI agents",
6
6
  "license": "MIT",