memorix 1.8.3 → 1.8.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 (74) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/CLAUDE.md +3 -0
  3. package/README.md +4 -4
  4. package/README.zh-CN.md +5 -3
  5. package/dist/cli/index.js +1544 -635
  6. package/dist/dashboard/static/app.js +29 -885
  7. package/dist/dashboard/static/index.html +1 -5
  8. package/dist/dashboard/static/style.css +36 -503
  9. package/dist/index.d.ts +4 -4
  10. package/dist/index.js +214 -73
  11. package/dist/memcode/index.js +28035 -169
  12. package/dist/memcode-runtime/CHANGELOG.md +53 -0
  13. package/dist/sdk.js +30 -14
  14. package/docs/1.8.0-RELEASE-CANDIDATE-SPEC.md +22 -16
  15. package/docs/AGENT_OPERATOR_PLAYBOOK.md +2 -2
  16. package/docs/API_REFERENCE.md +10 -8
  17. package/docs/DASHBOARD.md +23 -0
  18. package/docs/DEVELOPMENT.md +2 -2
  19. package/docs/DOCKER.md +6 -3
  20. package/docs/INTEGRATIONS.md +1 -1
  21. package/docs/MODULES.md +46 -20
  22. package/docs/PERFORMANCE.md +1 -1
  23. package/docs/README.md +8 -7
  24. package/docs/SETUP.md +3 -3
  25. package/package.json +11 -11
  26. package/plugins/antigravity/memorix/plugin.json +1 -1
  27. package/plugins/antigravity/memorix/rules/memorix.md +5 -0
  28. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  29. package/plugins/claude/memorix/skills/memorix-memory/SKILL.md +3 -0
  30. package/plugins/claude/memorix/skills/memorix-mini-skills/SKILL.md +2 -0
  31. package/plugins/claude/memorix/skills/memorix-orchestrate/SKILL.md +2 -0
  32. package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
  33. package/plugins/codebuddy/memorix/skills/memorix-memory/SKILL.md +3 -0
  34. package/plugins/codebuddy/memorix/skills/memorix-mini-skills/SKILL.md +2 -0
  35. package/plugins/codebuddy/memorix/skills/memorix-orchestrate/SKILL.md +2 -0
  36. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  37. package/plugins/codex/memorix/skills/memorix-memory/SKILL.md +3 -0
  38. package/plugins/codex/memorix/skills/memorix-mini-skills/SKILL.md +2 -0
  39. package/plugins/codex/memorix/skills/memorix-orchestrate/SKILL.md +2 -0
  40. package/plugins/copilot/memorix/plugin.json +1 -1
  41. package/plugins/copilot/memorix/skills/memorix-memory/SKILL.md +3 -0
  42. package/plugins/copilot/memorix/skills/memorix-mini-skills/SKILL.md +2 -0
  43. package/plugins/copilot/memorix/skills/memorix-orchestrate/SKILL.md +2 -0
  44. package/plugins/cursor/memorix/skills/memorix-memory/SKILL.md +3 -0
  45. package/plugins/cursor/memorix/skills/memorix-mini-skills/SKILL.md +2 -0
  46. package/plugins/cursor/memorix/skills/memorix-orchestrate/SKILL.md +2 -0
  47. package/plugins/gemini/memorix/GEMINI.md +5 -0
  48. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  49. package/plugins/hermes/memorix/plugin.yaml +7 -1
  50. package/plugins/omp/memorix/package.json +1 -1
  51. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  52. package/plugins/pi/memorix/package.json +1 -1
  53. package/plugins/pi/memorix/skills/memorix-memory/SKILL.md +3 -0
  54. package/plugins/pi/memorix/skills/memorix-mini-skills/SKILL.md +2 -0
  55. package/plugins/pi/memorix/skills/memorix-orchestrate/SKILL.md +2 -0
  56. package/server.json +2 -2
  57. package/src/cli/command-guide.ts +52 -1
  58. package/src/cli/commands/background.ts +166 -6
  59. package/src/cli/commands/serve-http.ts +74 -86
  60. package/src/cli/commands/serve.ts +95 -93
  61. package/src/cli/index.ts +1 -1
  62. package/src/dashboard/static/index.html +1 -5
  63. package/src/dashboard/static/style.css +36 -503
  64. package/src/hooks/installers/index.ts +3 -0
  65. package/src/hooks/official-skills.ts +13 -6
  66. package/src/memory/graph.ts +2 -2
  67. package/src/server/mcp-diagnostics.ts +9 -7
  68. package/src/server/mcp-discovery.ts +0 -1
  69. package/src/server/modern-mcp-bridge.ts +108 -0
  70. package/src/server/stdio-startup-gate.ts +41 -2
  71. package/src/server/tool-profile.ts +7 -6
  72. package/src/server.ts +1 -1
  73. package/src/store/orama-store.ts +2 -2
  74. package/src/wiki/types.ts +2 -2
@@ -1,5 +1,10 @@
1
1
  /**
2
- * memorix serve — Start MCP Server on stdio
2
+ * memorix serve - Start MCP Server on stdio transport.
3
+ *
4
+ * The official MCP v2 stdio entry owns modern discovery, per-request metadata,
5
+ * legacy compatibility, and protocol lifecycle. Memorix keeps its startup
6
+ * gate in front of that entry so early claim-less discovery remains compatible
7
+ * with clients shipped before the 2026 protocol revision.
3
8
  */
4
9
 
5
10
  import { defineCommand } from 'citty';
@@ -7,6 +12,7 @@ import { resolveToolProfile } from '../../server/tool-profile.js';
7
12
  import { getMcpServerInfo } from '../../server/mcp-discovery.js';
8
13
  import { StdioStartupGate } from '../../server/stdio-startup-gate.js';
9
14
  import { mcpFileUriToPath } from '../mcp-root-path.js';
15
+ import type { ModernMcpBridge } from '../../server/modern-mcp-bridge.js';
10
16
 
11
17
  export default defineCommand({
12
18
  meta: {
@@ -31,40 +37,42 @@ export default defineCommand({
31
37
  },
32
38
  },
33
39
  run: async ({ args }) => {
34
- // Take ownership of stdin before project/config/runtime initialization. The
35
- // gate answers handshake-free discovery and queues every other JSON-RPC line
36
- // until the SDK transport is connected, so cold starts cannot lose requests.
40
+ const { serveStdio, StdioServerTransport } = await import('@modelcontextprotocol/server/stdio');
41
+ const { createModernMcpBridge } = await import('../../server/modern-mcp-bridge.js');
42
+ const { detectProject, findGitInSubdirs, isSystemDirectory } = await import('../../project/detector.js');
43
+ const { homedir } = await import('node:os');
44
+ const { writeLastProjectRoot } = await import('../../project/launch-root.js');
45
+ const { resolveServeProject } = await import('./serve-shared.js');
46
+
47
+ // Take ownership of stdin before project/config/runtime initialization.
48
+ // The gate answers the old claim-less discover probe and queues every
49
+ // other line until the official v2 transport is listening.
50
+ let stdioHandle: { close: () => Promise<void> } | undefined;
51
+ let stdinEndedBeforeHandle = false;
37
52
  const startupGate = new StdioStartupGate({
38
53
  stdin: process.stdin,
39
54
  stdout: process.stdout,
40
55
  serverInfo: getMcpServerInfo(),
41
56
  onError: (error) => {
42
57
  console.error(`[memorix] stdio startup gate error: ${error.message}`);
43
- // The stream is no longer safe to replay after an input-limit or I/O
44
- // failure. Exit instead of leaving a half-connected MCP process alive.
45
58
  process.exit(1);
46
59
  },
47
60
  onEnd: () => {
48
- console.error('[memorix] stdin closed — exiting');
49
- // The gate has already ended the transport input when ready. Set the
50
- // exit code and let pending JSON-RPC stdout writes drain naturally.
51
- process.exitCode = 0;
61
+ console.error('[memorix] stdin closed - exiting');
62
+ if (stdioHandle) {
63
+ void stdioHandle.close().catch((error) => {
64
+ console.error(`[memorix] stdio close error: ${error instanceof Error ? error.message : String(error)}`);
65
+ });
66
+ } else {
67
+ stdinEndedBeforeHandle = true;
68
+ }
52
69
  },
53
70
  });
54
71
  startupGate.start();
55
72
 
56
- const { StdioServerTransport } = await import(
57
- '@modelcontextprotocol/sdk/server/stdio.js'
58
- );
59
- const { createMemorixServer } = await import('../../server.js');
60
- const { detectProject, findGitInSubdirs, isSystemDirectory } = await import('../../project/detector.js');
61
- const { homedir } = await import('node:os');
62
- const { resolveServeProject } = await import('./serve-shared.js');
63
-
64
- // Priority: explicit --cwd arg > MEMORIX_PROJECT_ROOT env > INIT_CWD (npm lifecycle) > process.cwd()
73
+ // Priority: explicit --cwd arg > MEMORIX_PROJECT_ROOT > INIT_CWD > process.cwd().
65
74
  let safeCwd: string;
66
75
  try { safeCwd = process.cwd(); } catch { safeCwd = homedir(); }
67
-
68
76
  const resolution = resolveServeProject(
69
77
  {
70
78
  cwdArg: args.cwd,
@@ -75,101 +83,95 @@ export default defineCommand({
75
83
  },
76
84
  { detectProject, findGitInSubdirs, isSystemDirectory },
77
85
  );
78
-
79
- for (const message of resolution.messages) {
80
- console.error(message);
81
- }
86
+ for (const message of resolution.messages) console.error(message);
82
87
 
83
88
  if (!resolution.detectedProject) {
84
89
  console.error(`[memorix] [WARN] ${resolution.error}`);
85
- console.error(`[memorix] Starting in deferred-binding mode — project will bind via MCP roots or memorix_session_start.`);
86
- console.error(`[memorix] For non-git directories, use --allow-untracked to enable untracked/ fallback.`);
87
- // Don't exit — allow deferred binding via session_start or MCP roots (fixes Cursor stdio #75)
90
+ console.error('[memorix] Starting in deferred-binding mode - bind with memorix_session_start(projectRoot=...) when needed.');
91
+ console.error('[memorix] For non-git directories, use --allow-untracked to enable untracked/ fallback.');
88
92
  }
89
93
 
90
94
  const detected = resolution.detectedProject;
91
95
  const projectRoot = resolution.projectRoot;
92
- if (detected) {
93
- const { writeLastProjectRoot } = await import('../../project/launch-root.js');
94
- writeLastProjectRoot(detected.rootPath);
95
- }
96
+ if (detected) writeLastProjectRoot(detected.rootPath);
96
97
 
97
- // Always register ALL tools BEFORE connecting transport.
98
- // This ensures tools/list returns the full tool set immediately on connect.
99
- // When no project detected, use deferred binding (allowUntrackedFallback=false, deferProjectInitUntilBound=true)
100
98
  const allowUntracked = args['allow-untracked'] ?? false;
101
- const toolProfile = resolveToolProfile({ explicit: args.mode, envValue: process.env.MEMORIX_MODE, fallback: 'micro' });
102
- const serverOptions = detected
103
- ? { toolProfile, deferProjectRuntimeInit: true }
104
- : { allowUntrackedFallback: allowUntracked, deferProjectInitUntilBound: !allowUntracked, deferProjectRuntimeInit: true, toolProfile };
105
- const { server, projectId, deferredInit, switchProject } = await createMemorixServer(projectRoot, undefined, undefined, serverOptions);
106
- const transport = new StdioServerTransport(startupGate.input, process.stdout);
107
- await server.connect(transport);
108
- startupGate.markReady();
109
-
110
- console.error(`[memorix] MCP Server running on stdio (project: ${projectId})`);
111
- console.error(`[memorix] Project root: ${detected?.rootPath ?? projectRoot}`);
99
+ const toolProfile = resolveToolProfile({
100
+ explicit: args.mode,
101
+ envValue: process.env.MEMORIX_MODE,
102
+ fallback: 'micro',
103
+ });
112
104
 
113
- // ── MCP Roots Protocol ──────────────────────────────────────────
114
- // After connect, request workspace roots from the client (IDE).
115
- // This is the proper way to discover the user's workspace —
116
- // no --cwd needed if the IDE supports roots capability.
117
- const tryRootsSwitch = async () => {
118
- try {
119
- const { roots } = await server.server.listRoots();
120
- if (!roots || roots.length === 0) return;
105
+ const transport = new StdioServerTransport(startupGate.input, process.stdout);
106
+ const createBridge = async (): Promise<ModernMcpBridge> => {
107
+ const bridge = await createModernMcpBridge({
108
+ projectRoot,
109
+ allowUntrackedFallback: allowUntracked,
110
+ deferProjectInitUntilBound: !allowUntracked,
111
+ deferProjectRuntimeInit: true,
112
+ toolProfile,
113
+ });
121
114
 
122
- for (const root of roots) {
123
- if (!root.uri.startsWith('file://')) continue;
124
- const rootPath = mcpFileUriToPath(root.uri);
125
- if (!rootPath) continue;
115
+ const tryRootsSwitch = async (): Promise<void> => {
116
+ try {
117
+ if (bridge.memorix.isExplicitlyBound()) return;
118
+ const { roots } = await bridge.server.listRoots();
119
+ for (const root of roots ?? []) {
120
+ if (!root.uri.startsWith('file://')) continue;
121
+ const rootPath = mcpFileUriToPath(root.uri);
122
+ if (!rootPath) continue;
126
123
 
127
- const rootDetected = detectProject(rootPath);
128
- if (rootDetected) {
129
- const switched = await switchProject(rootPath);
124
+ const switched = await bridge.memorix.switchProject(rootPath, 'mcp-roots');
130
125
  if (switched) {
131
- console.error(`[memorix] [UPDATED] Project updated via MCP roots: ${rootDetected.id}`);
126
+ console.error(`[memorix] [UPDATED] Project updated via MCP roots: ${rootPath}`);
127
+ return;
132
128
  }
133
- return; // use first valid root
134
- }
135
- // Root itself has no .git — try its subdirs
136
- const subGit = findGitInSubdirs(rootPath);
137
- if (subGit) {
138
- const switched = await switchProject(subGit);
139
- if (switched) {
129
+ const subGit = findGitInSubdirs(rootPath);
130
+ if (subGit && await bridge.memorix.switchProject(subGit, 'mcp-roots')) {
140
131
  console.error(`[memorix] [UPDATED] Project updated via MCP roots (subdir): ${subGit}`);
132
+ return;
141
133
  }
142
- return;
143
134
  }
135
+ } catch (error) {
136
+ console.error(`[memorix] MCP roots not available (${error instanceof Error ? error.message : 'unsupported'})`);
144
137
  }
145
- } catch (err) {
146
- // Client doesn't support roots — that's OK, fall back to existing detection
147
- console.error(`[memorix] MCP roots not available (${(err as Error).message ?? 'unsupported'})`);
148
- }
149
- };
150
-
151
- // Do NOT proactively call listRoots() after connect — this violates MCP SEP-2260
152
- // which requires server-initiated requests to be associated with a client request.
153
- // Some clients (e.g. Codex) treat standalone roots/list as unexpected and may
154
- // fail to inject MCP tools. Instead, rely on:
155
- // 1. RootsListChangedNotification (client-initiated, then we respond)
156
- // 2. memorix_session_start({ projectRoot }) for explicit binding
157
- // 3. cwd-based detection as fallback (already done in deferred-binding)
138
+ };
158
139
 
159
- // Listen for roots changes (user switches workspace)
160
- try {
161
- const { RootsListChangedNotificationSchema } = await import('@modelcontextprotocol/sdk/types.js');
162
- server.server.setNotificationHandler(RootsListChangedNotificationSchema, async () => {
163
- console.error(`[memorix] Roots changed — re-detecting project...`);
140
+ // roots/list is a legacy-era request. The v2 SDK rejects it on the
141
+ // modern era, while retaining this handler keeps existing IDE clients.
142
+ bridge.server.setNotificationHandler('notifications/roots/list_changed', async () => {
164
143
  await tryRootsSwitch();
165
144
  });
166
- } catch { /* notification handler setup is optional */ }
167
145
 
168
- const deferredInitTimer = setTimeout(() => {
169
- deferredInit().catch(e => console.error(`[memorix] Deferred init error:`, e));
170
- }, 5_000);
171
- deferredInitTimer.unref?.();
172
- // Fire-and-forget: background update check. Default is notify-only.
146
+ const deferredInitTimer = setTimeout(() => {
147
+ bridge.memorix.deferredInit().catch((error) => {
148
+ console.error(`[memorix] Deferred init error: ${error instanceof Error ? error.message : String(error)}`);
149
+ });
150
+ }, 5_000);
151
+ deferredInitTimer.unref?.();
152
+
153
+ const originalClose = bridge.close.bind(bridge);
154
+ bridge.close = async () => {
155
+ clearTimeout(deferredInitTimer);
156
+ await originalClose();
157
+ };
158
+ return bridge;
159
+ };
160
+
161
+ stdioHandle = serveStdio(createBridge, {
162
+ transport,
163
+ legacy: 'serve',
164
+ onerror: (error) => console.error(`[memorix] stdio MCP error: ${error.message}`),
165
+ });
166
+ startupGate.markReady();
167
+ if (stdinEndedBeforeHandle) {
168
+ void stdioHandle.close().catch((error) => {
169
+ console.error(`[memorix] stdio close error: ${error instanceof Error ? error.message : String(error)}`);
170
+ });
171
+ }
172
+
173
+ console.error(`[memorix] MCP Server running on stdio (profile: ${toolProfile}, project: ${detected?.id ?? 'deferred'})`);
174
+ console.error(`[memorix] Project root: ${detected?.rootPath ?? projectRoot}`);
173
175
  import('../update-checker.js').then(m => m.checkForUpdates()).catch(() => {});
174
176
  },
175
177
  });
package/src/cli/index.ts CHANGED
@@ -389,7 +389,7 @@ const main = defineCommand({
389
389
  console.error(' integrate Install one IDE integration into the current repo');
390
390
  console.error(' status Show project info + stats');
391
391
  console.error(' dashboard Open standalone dashboard (read-mostly)');
392
- console.error(' hooks Open legacy hook installer menu');
392
+ console.error(' hooks Install, inspect, or remove agent hooks');
393
393
  console.error(' cleanup Remove old memories');
394
394
  console.error(' purge Retire all memories (project by default, --all for everything)');
395
395
  console.error(' sync Rules/workspace sync plus interactive wizard');
@@ -142,7 +142,7 @@
142
142
  <path d="M23 21v-2a4 4 0 0 0-3-3.87"/>
143
143
  <path d="M16 3.13a4 4 0 0 1 0 7.75"/>
144
144
  </svg>
145
- <span class="nav-label">Team</span>
145
+ <span class="nav-label">Coordination</span>
146
146
  </button>
147
147
  </div>
148
148
  <div class="sidebar-spacer"></div>
@@ -211,10 +211,6 @@
211
211
 
212
212
  <!-- Apache ECharts 5 — Knowledge Graph renderer -->
213
213
  <script src="https://cdn.jsdelivr.net/npm/echarts@5.5.1/dist/echarts.min.js"></script>
214
- <!-- Cytoscape.js + Dagre — legacy entity-graph fallback -->
215
- <script src="https://unpkg.com/cytoscape@3.30.4/dist/cytoscape.min.js"></script>
216
- <script src="https://unpkg.com/dagre@0.8.5/dist/dagre.min.js"></script>
217
- <script src="https://unpkg.com/cytoscape-dagre@2.5.0/cytoscape-dagre.js"></script>
218
214
  <script>
219
215
  // Cache-bust app.js by appending mtime-based version so edits always hit the browser.
220
216
  (function () {