memorix 1.2.9 → 1.2.10

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/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.2.10] - 2026-07-28
6
+
7
+ ### Added
8
+ - **Official MCP Registry publication path** -- Ships the checked `server.json` manifest and a GitHub OIDC publication step that runs after npm succeeds. This release is the first package version eligible for official Registry registration.
9
+
10
+ ### Fixed
11
+ - **Codex plugin ownership** -- `memorix setup --agent codex --global` now uses Codex's personal marketplace plugin path and never creates project-local `.codex` configuration as a fallback. Custom user MCP configuration stays untouched; legacy Memorix files are migrated only after Codex confirms the plugin is enabled.
12
+ - **Glama-compatible Docker build** -- Docker now installs every workspace dependency set before compiling. CI builds and starts the HTTP control-plane image, so a future container regression is caught on the pull request instead of by an external registry scan.
13
+
5
14
  ## [1.2.9] - 2026-07-28
6
15
 
7
16
  ### Fixed
package/README.md CHANGED
@@ -218,7 +218,7 @@ memorix setup --agent claude --global # or codex, copilot, cursor, pi, gemini-
218
218
 
219
219
  Legacy `memorix.yml`, `.env`, and `~/.memorix/config.json` are still read for compatibility, but new setup flows use TOML.
220
220
 
221
- If you want repo-local guidance or hooks for a specific repository, run the same setup command from inside that repo without `--global`.
221
+ If you want repo-local guidance or hooks for a specific repository, run the same setup command from inside that repo without `--global`. Codex is the exception: its supported path is the user-level plugin install, so Memorix leaves project `.codex` configuration alone.
222
222
 
223
223
  <h2 id="quick-start"><picture><source media="(prefers-color-scheme: dark)" srcset="assets/tags/light/section-quick-start.svg"><img src="assets/tags/section-quick-start.svg" alt="Quick Start" height="32" /></picture></h2>
224
224
 
@@ -246,7 +246,7 @@ memorix setup --agent omp --global
246
246
  What it installs depends on the target agent, but the goal is the same: make Memorix available wherever you open that agent without asking you to wire every repo by hand.
247
247
 
248
248
  - Claude Code: installs the Memorix plugin package, adds `CLAUDE.md` guidance, and enables hook capture when you do not pass `--noHooks`.
249
- - Codex: installs the Memorix plugin package with stdio MCP, skills, and lifecycle hooks, then adds `AGENTS.md` guidance. When Codex asks, review the plugin hook definition once with `/hooks`; `--noHooks` skips automatic capture.
249
+ - Codex: installs one user-level Memorix plugin with bundled stdio MCP, skills, and lifecycle hooks. It does not write project-local `.codex` config or change your model, approval, or sandbox settings. When Codex asks, review the plugin hook definition once with `/hooks`; `--noHooks` skips automatic capture.
250
250
  - GitHub Copilot CLI: installs the Copilot plugin package and official Memorix skills.
251
251
  - Pi: installs the user-level Pi package and official skills.
252
252
  - Cursor: writes Cursor MCP/rules/config entries in the chosen scope.
package/dist/cli/index.js CHANGED
@@ -3212,7 +3212,7 @@ ${import_picocolors.default.gray(m3)} ${s2}
3212
3212
 
3213
3213
  // src/cli/version.ts
3214
3214
  function getCliVersion() {
3215
- return true ? "1.2.9" : pkg.version;
3215
+ return true ? "1.2.10" : pkg.version;
3216
3216
  }
3217
3217
  var init_version = __esm({
3218
3218
  "src/cli/version.ts"() {
@@ -35531,6 +35531,8 @@ __export(setup_exports, {
35531
35531
  installOpenClawBundlePackage: () => installOpenClawBundlePackage,
35532
35532
  installPiPackage: () => installPiPackage,
35533
35533
  installPluginPackage: () => installPluginPackage,
35534
+ migrateLegacyCodexIntegration: () => migrateLegacyCodexIntegration,
35535
+ parseCodexPluginState: () => parseCodexPluginState,
35534
35536
  removeLegacyCodexMemorixMcpConfig: () => removeLegacyCodexMemorixMcpConfig,
35535
35537
  tryInstallCodexPlugin: () => tryInstallCodexPlugin
35536
35538
  });
@@ -35779,6 +35781,21 @@ function mergeTomlMcpConfig(existingContent, generatedContent) {
35779
35781
  return `${parts.join("\n\n")}
35780
35782
  `;
35781
35783
  }
35784
+ function getTomlSection(content, section) {
35785
+ const lines = content.split(/\r?\n/);
35786
+ const header = `[${section}]`;
35787
+ const start = lines.findIndex((line) => line.trim() === header);
35788
+ if (start < 0) return null;
35789
+ let end = lines.length;
35790
+ for (let index = start + 1; index < lines.length; index += 1) {
35791
+ const trimmed = lines[index].trim();
35792
+ if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
35793
+ end = index;
35794
+ break;
35795
+ }
35796
+ }
35797
+ return lines.slice(start + 1, end).join("\n");
35798
+ }
35782
35799
  function isLegacyCodexMemorixNodeServer(server) {
35783
35800
  if (server.name !== "memorix" || server.command.trim().toLowerCase() !== "node") return false;
35784
35801
  const startsMemorixStdio = server.args.some((arg) => arg.trim().toLowerCase() === "serve");
@@ -35788,6 +35805,19 @@ function isLegacyCodexMemorixNodeServer(server) {
35788
35805
  });
35789
35806
  return startsMemorixStdio && sourceEntry;
35790
35807
  }
35808
+ function isOwnedLegacyCodexMcp(content) {
35809
+ const section = getTomlSection(content, "mcp_servers.memorix");
35810
+ if (section === null || getTomlSection(content, "mcp_servers.memorix.env") !== null) return false;
35811
+ const allowedKeys = /* @__PURE__ */ new Set(["command", "args", "type", "startup_timeout_sec", "tool_timeout_sec", "enabled"]);
35812
+ for (const rawLine of section.split(/\r?\n/)) {
35813
+ const line = rawLine.trim();
35814
+ if (!line || line.startsWith("#")) continue;
35815
+ const key = line.match(/^([A-Za-z0-9_]+)\s*=/)?.[1];
35816
+ if (!key || !allowedKeys.has(key)) return false;
35817
+ }
35818
+ const server = new CodexMCPAdapter().parse(content).find((entry) => entry.name === "memorix");
35819
+ return Boolean(server && isLegacyCodexMemorixNodeServer(server));
35820
+ }
35791
35821
  async function removeLegacyCodexMemorixMcpConfig(configPath = path23.join(homedir24(), ".codex", "config.toml")) {
35792
35822
  let existingContent;
35793
35823
  try {
@@ -35795,15 +35825,82 @@ async function removeLegacyCodexMemorixMcpConfig(configPath = path23.join(homedi
35795
35825
  } catch {
35796
35826
  return { configPath, removed: false };
35797
35827
  }
35798
- const adapter = new CodexMCPAdapter();
35799
- const legacyServer = adapter.parse(existingContent).find(isLegacyCodexMemorixNodeServer);
35800
- if (!legacyServer) return { configPath, removed: false };
35828
+ if (!isOwnedLegacyCodexMcp(existingContent)) return { configPath, removed: false };
35801
35829
  let nextContent = removeTomlSection(existingContent, "mcp_servers.memorix.env");
35802
35830
  nextContent = removeTomlSection(nextContent, "mcp_servers.memorix");
35803
35831
  await writeFile5(configPath, nextContent ? `${nextContent}
35804
35832
  ` : "", "utf-8");
35805
35833
  return { configPath, removed: true };
35806
35834
  }
35835
+ function collectHookCommands(value, commands) {
35836
+ if (Array.isArray(value)) {
35837
+ for (const item of value) collectHookCommands(item, commands);
35838
+ return;
35839
+ }
35840
+ if (!value || typeof value !== "object") return;
35841
+ for (const [key, child] of Object.entries(value)) {
35842
+ if ((key === "command" || key === "commandWindows") && typeof child === "string") {
35843
+ commands.push(child);
35844
+ } else {
35845
+ collectHookCommands(child, commands);
35846
+ }
35847
+ }
35848
+ }
35849
+ function isOwnedLegacyCodexHooks(content) {
35850
+ try {
35851
+ const parsed = JSON.parse(content);
35852
+ if (!parsed.hooks || typeof parsed.hooks !== "object") return false;
35853
+ const commands = [];
35854
+ collectHookCommands(parsed.hooks, commands);
35855
+ return commands.length > 0 && commands.every((command) => /^memorix(?:\.cmd)?\s+hook(?:\s|$)/i.test(command.trim()));
35856
+ } catch {
35857
+ return false;
35858
+ }
35859
+ }
35860
+ async function removeOwnedLegacyCodexPluginFolder(homeDir) {
35861
+ const canonicalPath = path23.join(homeDir, "plugins", "memorix");
35862
+ const legacyPath = path23.join(homeDir, ".codex", "plugins", "memorix");
35863
+ if (!existsSync12(legacyPath)) return "missing";
35864
+ if (!existsSync12(canonicalPath)) return "preserved";
35865
+ try {
35866
+ const manifest = JSON.parse(await readFile5(path23.join(legacyPath, ".codex-plugin", "plugin.json"), "utf-8"));
35867
+ const repository = String(manifest.repository ?? "");
35868
+ if (manifest.name !== "memorix" || !repository.includes("github.com/AVIDS2/memorix")) return "preserved";
35869
+ await rm(legacyPath, { recursive: true, force: true });
35870
+ return "removed";
35871
+ } catch {
35872
+ return "preserved";
35873
+ }
35874
+ }
35875
+ async function migrateLegacyCodexIntegration(options2 = {}) {
35876
+ const homeDir = options2.homeDir ?? homedir24();
35877
+ const projectRoot = options2.projectRoot ?? process.cwd();
35878
+ const configPath = path23.join(homeDir, ".codex", "config.toml");
35879
+ let mcp = "missing";
35880
+ try {
35881
+ const content = await readFile5(configPath, "utf-8");
35882
+ if (getTomlSection(content, "mcp_servers.memorix") !== null) {
35883
+ mcp = (await removeLegacyCodexMemorixMcpConfig(configPath)).removed ? "removed" : "preserved";
35884
+ }
35885
+ } catch {
35886
+ mcp = "missing";
35887
+ }
35888
+ const hooksPath = path23.join(projectRoot, ".codex", "hooks.json");
35889
+ let projectHooks = "missing";
35890
+ try {
35891
+ const content = await readFile5(hooksPath, "utf-8");
35892
+ if (isOwnedLegacyCodexHooks(content)) {
35893
+ await rm(hooksPath, { force: true });
35894
+ projectHooks = "removed";
35895
+ } else {
35896
+ projectHooks = "preserved";
35897
+ }
35898
+ } catch {
35899
+ projectHooks = "missing";
35900
+ }
35901
+ const pluginFolder = await removeOwnedLegacyCodexPluginFolder(homeDir);
35902
+ return { mcp, projectHooks, pluginFolder };
35903
+ }
35807
35904
  function mergeYamlMcpConfig(existingContent, generatedContent) {
35808
35905
  let existing = {};
35809
35906
  try {
@@ -35926,7 +36023,7 @@ async function installPluginPackage(options2) {
35926
36023
  throw new Error(`Plugin package template not found: ${source}`);
35927
36024
  }
35928
36025
  if (options2.agent === "codex") {
35929
- const pluginPath2 = path23.join(home, ".codex", "plugins", "memorix");
36026
+ const pluginPath2 = path23.join(home, "plugins", "memorix");
35930
36027
  const marketplacePath2 = path23.join(home, ".agents", "plugins", "marketplace.json");
35931
36028
  await copyDir(source, pluginPath2);
35932
36029
  await stampCodexPluginVersion(pluginPath2);
@@ -36110,19 +36207,56 @@ ${install.stdout || ""}`.toLowerCase();
36110
36207
  message: installDetail ? `claude: marketplace registered, but automatic plugin install did not finish: ${installDetail}` : "claude: marketplace registered. Run `claude plugin install memorix@memorix-local`."
36111
36208
  };
36112
36209
  }
36210
+ function parseCodexPluginState(content) {
36211
+ try {
36212
+ const start = content.indexOf("{");
36213
+ const end = content.lastIndexOf("}");
36214
+ if (start < 0 || end < start) return "unknown";
36215
+ const parsed = JSON.parse(content.slice(start, end + 1));
36216
+ if (!Array.isArray(parsed.installed)) return "unknown";
36217
+ const plugin = parsed.installed.find((entry) => Boolean(entry) && typeof entry === "object" && entry.pluginId === "memorix@personal");
36218
+ if (!plugin || plugin.installed !== true) return "missing";
36219
+ return plugin.enabled === true ? "ready" : "disabled";
36220
+ } catch {
36221
+ return "unknown";
36222
+ }
36223
+ }
36224
+ function getCodexPluginState() {
36225
+ const result = spawnSync("codex", ["plugin", "list", "--json"], {
36226
+ encoding: "utf-8",
36227
+ stdio: "pipe",
36228
+ shell: process.platform === "win32"
36229
+ });
36230
+ if (result.status !== 0) return "unknown";
36231
+ return parseCodexPluginState(result.stdout || "");
36232
+ }
36113
36233
  function tryInstallCodexPlugin() {
36114
36234
  const result = spawnSync("codex", ["plugin", "add", "memorix@personal", "--json"], {
36115
36235
  encoding: "utf-8",
36116
36236
  stdio: "pipe",
36117
36237
  shell: process.platform === "win32"
36118
36238
  });
36119
- if (result.status === 0) {
36120
- return { ok: true, message: "codex: plugin installed from Personal marketplace" };
36121
- }
36122
36239
  const output = `${result.stderr || ""}
36123
36240
  ${result.stdout || ""}`.toLowerCase();
36124
- if (output.includes("already")) {
36125
- return { ok: true, message: "codex: plugin already installed from Personal marketplace" };
36241
+ const addFinished = result.status === 0 || output.includes("already");
36242
+ if (addFinished) {
36243
+ const pluginState = getCodexPluginState();
36244
+ if (pluginState === "ready") {
36245
+ return {
36246
+ ok: true,
36247
+ message: result.status === 0 ? "codex: plugin installed and enabled from Personal marketplace" : "codex: plugin already installed and enabled from Personal marketplace"
36248
+ };
36249
+ }
36250
+ if (pluginState === "disabled") {
36251
+ return {
36252
+ ok: false,
36253
+ message: "codex: Memorix plugin is installed but disabled. Enable it in Codex Plugins before any legacy MCP or project hooks are removed."
36254
+ };
36255
+ }
36256
+ return {
36257
+ ok: false,
36258
+ message: "codex: plugin registration finished, but Codex did not confirm an enabled Memorix plugin. Legacy MCP and project hooks were kept unchanged."
36259
+ };
36126
36260
  }
36127
36261
  const detail = (result.stderr || result.stdout || result.error?.message || "").trim();
36128
36262
  return {
@@ -36268,14 +36402,14 @@ async function installAgentSetup(agent, plan, global2) {
36268
36402
  const hasAntigravityPlugin = plan.actions.includes("antigravity-plugin") && agent === "antigravity";
36269
36403
  const hasHermesPlugin = plan.actions.includes("hermes-plugin") && agent === "hermes";
36270
36404
  const hasOmpPackage = plan.actions.includes("omp-package") && agent === "omp";
36271
- const wantsMcpConfig = plan.mcp !== "none" && agent !== "pi";
36405
+ const wantsMcpConfig = plan.mcp !== "none" && agent !== "pi" && !(agent === "codex" && !global2);
36272
36406
  if (PLUGIN_PACKAGE_AGENTS.has(agent) && plan.includePlugin && !global2) {
36273
36407
  v3.info(`${agent}: project setup leaves user-level plugins untouched. Run \`memorix setup --agent ${agent} --global\` to install its plugin, skills, and lifecycle hooks.`);
36274
36408
  }
36275
36409
  if (hasPluginPackage) {
36276
36410
  const result = await installPluginPackage({
36277
36411
  agent,
36278
- includeHooks: plan.actions.includes("hooks")
36412
+ includeHooks: plan.includeHooks
36279
36413
  });
36280
36414
  v3.success(`${agent}: plugin package -> ${result.pluginPath}`);
36281
36415
  if (result.marketplacePath) {
@@ -36283,17 +36417,16 @@ async function installAgentSetup(agent, plan, global2) {
36283
36417
  }
36284
36418
  if (agent === "codex") {
36285
36419
  const install = tryInstallCodexPlugin();
36286
- if (install.ok) v3.success(install.message);
36287
- else v3.warn(install.message);
36288
36420
  if (install.ok) {
36289
- try {
36290
- const migration = await removeLegacyCodexMemorixMcpConfig();
36291
- if (migration.removed) {
36292
- v3.info(`codex: removed legacy source-path MCP config from ${migration.configPath}`);
36293
- }
36294
- } catch {
36295
- v3.warn("codex: plugin installed, but the legacy MCP config could not be checked");
36296
- }
36421
+ v3.success(install.message);
36422
+ const migration = await migrateLegacyCodexIntegration({ projectRoot: process.cwd() });
36423
+ if (migration.mcp === "removed") v3.info("codex: removed the legacy source-path MCP block; the enabled plugin now owns MCP.");
36424
+ if (migration.mcp === "preserved") v3.warn("codex: kept a customized legacy Memorix MCP block; remove it manually only after confirming plugin MCP works.");
36425
+ if (migration.projectHooks === "removed") v3.info("codex: removed a legacy project-local Memorix hooks.json; plugin hooks now own lifecycle capture.");
36426
+ if (migration.projectHooks === "preserved") v3.warn("codex: kept a project .codex/hooks.json because it contains non-Memorix commands.");
36427
+ if (migration.pluginFolder === "removed") v3.info("codex: removed the migrated legacy plugin folder under ~/.codex/plugins.");
36428
+ } else {
36429
+ v3.warn(install.message);
36297
36430
  }
36298
36431
  } else if (agent === "claude" && result.marketplaceRoot) {
36299
36432
  const install = tryInstallClaudePlugin(result.marketplaceRoot);
@@ -36371,6 +36504,8 @@ async function installAgentSetup(agent, plan, global2) {
36371
36504
  const result = await installMcpConfig({ agent, projectRoot: targetRoot, global: global2, mcp });
36372
36505
  v3.success(`${agent}: MCP config -> ${result.configPath}`);
36373
36506
  }
36507
+ } else if (agent === "codex" && !global2 && plan.mcp !== "none") {
36508
+ v3.info("codex: project-local .codex config is intentionally untouched. Run `memorix setup --agent codex --global` to install the user-level plugin.");
36374
36509
  }
36375
36510
  if (hasPluginPackage) {
36376
36511
  if (plan.actions.includes("project-guidance") && (agent === "claude" || agent === "codex")) {
@@ -36463,7 +36598,7 @@ var init_setup = __esm({
36463
36598
  ANTIGRAVITY_PLUGIN_AGENTS = /* @__PURE__ */ new Set(["antigravity"]);
36464
36599
  HERMES_PLUGIN_AGENTS = /* @__PURE__ */ new Set(["hermes"]);
36465
36600
  OMP_PACKAGE_AGENTS = /* @__PURE__ */ new Set(["omp"]);
36466
- PACKAGE_OWNED_INTEGRATION_AGENTS = /* @__PURE__ */ new Set(["antigravity", "openclaw", "hermes", "omp"]);
36601
+ PACKAGE_OWNED_INTEGRATION_AGENTS = /* @__PURE__ */ new Set(["codex", "antigravity", "openclaw", "hermes", "omp"]);
36467
36602
  SUPPORTED_SETUP_AGENTS = [
36468
36603
  "claude",
36469
36604
  "codex",
@@ -71498,7 +71633,7 @@ The path should point to a directory containing a .git folder.`
71498
71633
  };
71499
71634
  const server = existingServer ?? new McpServer({
71500
71635
  name: "memorix",
71501
- version: true ? "1.2.9" : "1.0.1"
71636
+ version: true ? "1.2.10" : "1.0.1"
71502
71637
  });
71503
71638
  const originalRegisterTool = server.registerTool.bind(server);
71504
71639
  server.registerTool = ((name, ...args) => {
@@ -82633,7 +82768,7 @@ function asRecordArray(value) {
82633
82768
  return Array.isArray(value) ? value.map(asRecord2).filter((entry) => entry !== null) : [];
82634
82769
  }
82635
82770
  function codexPluginPath() {
82636
- return `${homedir32()}/.codex/plugins/${CODEX_PLUGIN_NAME}`;
82771
+ return `${homedir32()}/plugins/${CODEX_PLUGIN_NAME}`;
82637
82772
  }
82638
82773
  function codexPluginMcpPath() {
82639
82774
  return `${codexPluginPath()}/.mcp.json`;
@@ -82652,7 +82787,10 @@ function isMemorixCodexPlugin(value) {
82652
82787
  }
82653
82788
  function parseCodexPluginList(output) {
82654
82789
  try {
82655
- const parsed = asRecord2(JSON.parse(output));
82790
+ const start = output.indexOf("{");
82791
+ const end = output.lastIndexOf("}");
82792
+ if (start < 0 || end < start) return null;
82793
+ const parsed = asRecord2(JSON.parse(output.slice(start, end + 1)));
82656
82794
  if (!parsed) return null;
82657
82795
  const entries = [...asRecordArray(parsed.installed), ...asRecordArray(parsed.available)];
82658
82796
  const plugin = entries.find(isMemorixCodexPlugin);
@@ -82795,7 +82933,7 @@ async function inspectCodexMarketplace() {
82795
82933
  }
82796
82934
  const source = asRecord2(entry.source);
82797
82935
  const sourcePath = typeof source?.path === "string" ? source.path : "";
82798
- const issues = source?.source === "local" && normalizeMarketplacePath(sourcePath) === ".codex/plugins/memorix" ? [] : ["codex-marketplace-entry-stale"];
82936
+ const issues = source?.source === "local" && normalizeMarketplacePath(sourcePath) === "plugins/memorix" ? [] : ["codex-marketplace-entry-stale"];
82799
82937
  return {
82800
82938
  scope: "global",
82801
82939
  kind: "marketplace",
@@ -83362,9 +83500,11 @@ async function repairAgentIntegrations(options2 = {}) {
83362
83500
  } else {
83363
83501
  if (!options2.dry) {
83364
83502
  await installPluginPackage({ agent: "codex" });
83365
- if (needsInstall) {
83366
- const install = tryInstallCodexPlugin();
83367
- if (!install.ok) skipped.push("codex:plugin:global:install-pending");
83503
+ const install = tryInstallCodexPlugin();
83504
+ if (install.ok) {
83505
+ await migrateLegacyCodexIntegration({ projectRoot });
83506
+ } else {
83507
+ skipped.push("codex:plugin:global:install-pending");
83368
83508
  }
83369
83509
  }
83370
83510
  changed.push("codex:plugin:global");