memorix 1.4.3 → 1.4.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 (158) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +20 -6
  3. package/README.zh-CN.md +16 -3
  4. package/TEAM.md +86 -86
  5. package/assets/star-history-dark.svg +8 -8
  6. package/assets/star-history-light.svg +8 -8
  7. package/assets/star-history.json +16 -2
  8. package/dist/cli/index.js +539 -233
  9. package/dist/cli/memcode.js +0 -0
  10. package/dist/index.js +72 -15
  11. package/dist/maintenance-runner.js +9 -7
  12. package/dist/media-audio-runner.js +9 -7
  13. package/dist/media-video-runner.js +9 -7
  14. package/dist/memcode/index.js +9 -7
  15. package/dist/memcode-runtime/CHANGELOG.md +34 -0
  16. package/dist/sdk.js +71 -14
  17. package/dist/types.d.ts +1 -1
  18. package/dist/vector-backfill-runner.js +9 -7
  19. package/docs/AGENT_OPERATOR_PLAYBOOK.md +6 -5
  20. package/docs/API_REFERENCE.md +9 -5
  21. package/docs/CONFIGURATION.md +15 -5
  22. package/docs/DESIGN_DECISIONS.md +357 -357
  23. package/docs/DEVELOPMENT.md +2 -2
  24. package/docs/INTEGRATIONS.md +9 -2
  25. package/docs/KNOWN_ISSUES_AND_ROADMAP.md +4 -3
  26. package/docs/MEMCODE.md +1 -1
  27. package/docs/MODULES.md +1 -1
  28. package/docs/README.md +1 -1
  29. package/docs/SETUP.md +12 -4
  30. package/docs/hooks-architecture.md +2 -1
  31. package/llms-full.txt +7 -6
  32. package/llms.txt +2 -2
  33. package/package.json +1 -1
  34. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  35. package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
  36. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  37. package/plugins/copilot/memorix/plugin.json +1 -1
  38. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  39. package/plugins/omp/memorix/package.json +1 -1
  40. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  41. package/plugins/pi/memorix/package.json +1 -1
  42. package/server.json +2 -2
  43. package/src/audit/index.ts +156 -156
  44. package/src/cli/commands/agent-integrations.ts +7 -1
  45. package/src/cli/commands/audit-list.ts +89 -89
  46. package/src/cli/commands/formation.ts +48 -48
  47. package/src/cli/commands/git-hook-install.ts +111 -111
  48. package/src/cli/commands/handoff.ts +54 -54
  49. package/src/cli/commands/hooks-install.ts +2 -0
  50. package/src/cli/commands/hooks-preview.ts +5 -0
  51. package/src/cli/commands/hooks-status.ts +63 -63
  52. package/src/cli/commands/ingest.ts +44 -44
  53. package/src/cli/commands/integrate-shared.ts +15 -15
  54. package/src/cli/commands/integrate.ts +2 -0
  55. package/src/cli/commands/lock.ts +82 -82
  56. package/src/cli/commands/media-image.ts +27 -0
  57. package/src/cli/commands/media.ts +8 -1
  58. package/src/cli/commands/memory.ts +44 -34
  59. package/src/cli/commands/message.ts +104 -104
  60. package/src/cli/commands/operator-shared.ts +62 -18
  61. package/src/cli/commands/poll.ts +58 -58
  62. package/src/cli/commands/purge-all-memory.ts +85 -85
  63. package/src/cli/commands/purge-project-memory.ts +83 -83
  64. package/src/cli/commands/reasoning.ts +118 -118
  65. package/src/cli/commands/setup.ts +57 -2
  66. package/src/cli/commands/skills.ts +114 -114
  67. package/src/cli/commands/task.ts +167 -167
  68. package/src/cli/commands/transfer.ts +47 -47
  69. package/src/cli/commands/uninstall-project-artifacts.ts +85 -85
  70. package/src/cli/commands/uninstall.ts +20 -0
  71. package/src/cli/tui/ChatView.tsx +234 -234
  72. package/src/cli/tui/CommandBar.tsx +312 -312
  73. package/src/cli/tui/ContextRail.tsx +118 -118
  74. package/src/cli/tui/HeaderBar.tsx +72 -72
  75. package/src/cli/tui/LogoBanner.tsx +51 -51
  76. package/src/cli/tui/Sidebar.tsx +179 -179
  77. package/src/cli/tui/index.ts +41 -41
  78. package/src/cli/tui/markdown-render.tsx +371 -371
  79. package/src/cli/tui/use-mouse.ts +157 -157
  80. package/src/cli/tui/useNavigation.ts +56 -56
  81. package/src/cli/version.ts +7 -7
  82. package/src/cli/workbench.ts +1 -1
  83. package/src/dashboard/project-classification.ts +64 -64
  84. package/src/git/hooks-path.ts +85 -85
  85. package/src/hooks/installers/index.ts +43 -2
  86. package/src/hooks/pattern-detector.ts +173 -173
  87. package/src/hooks/significance-filter.ts +250 -250
  88. package/src/hooks/types.ts +2 -0
  89. package/src/integrations/registry.ts +9 -0
  90. package/src/llm/memory-manager.ts +328 -328
  91. package/src/llm/provider.ts +885 -885
  92. package/src/llm/quality.ts +248 -248
  93. package/src/media/minimax.ts +9 -1
  94. package/src/memory/attribution-guard.ts +249 -249
  95. package/src/memory/disclosure-policy.ts +135 -135
  96. package/src/memory/entity-extractor.ts +197 -197
  97. package/src/memory/formation/evaluate.ts +217 -217
  98. package/src/memory/formation/extract.ts +361 -361
  99. package/src/memory/formation/index.ts +417 -417
  100. package/src/memory/formation/resolve.ts +344 -344
  101. package/src/memory/formation/types.ts +315 -315
  102. package/src/memory/freshness.ts +122 -122
  103. package/src/memory/graph.ts +197 -197
  104. package/src/memory/refs.ts +94 -94
  105. package/src/memory/secret-filter.ts +79 -79
  106. package/src/orchestrate/adapters/claude-stream.ts +192 -192
  107. package/src/orchestrate/adapters/claude.ts +111 -111
  108. package/src/orchestrate/adapters/codex-stream.ts +134 -134
  109. package/src/orchestrate/adapters/codex.ts +41 -41
  110. package/src/orchestrate/adapters/gemini-stream.ts +166 -166
  111. package/src/orchestrate/adapters/gemini.ts +42 -42
  112. package/src/orchestrate/adapters/index.ts +73 -73
  113. package/src/orchestrate/adapters/opencode-stream.ts +143 -143
  114. package/src/orchestrate/adapters/opencode.ts +47 -47
  115. package/src/orchestrate/adapters/types.ts +77 -77
  116. package/src/orchestrate/capability-router.ts +284 -284
  117. package/src/orchestrate/context-compact.ts +188 -188
  118. package/src/orchestrate/cost-tracker.ts +219 -219
  119. package/src/orchestrate/error-recovery.ts +191 -191
  120. package/src/orchestrate/evidence.ts +140 -140
  121. package/src/orchestrate/ledger.ts +110 -110
  122. package/src/orchestrate/memorix-bridge.ts +343 -343
  123. package/src/orchestrate/output-budget.ts +80 -80
  124. package/src/orchestrate/permission.ts +152 -152
  125. package/src/orchestrate/pipeline-trace.ts +131 -131
  126. package/src/orchestrate/prompt-builder.ts +155 -155
  127. package/src/orchestrate/ring-buffer.ts +37 -37
  128. package/src/orchestrate/task-graph.ts +389 -389
  129. package/src/project/aliases.ts +374 -374
  130. package/src/rules/adapters/trae.ts +56 -56
  131. package/src/rules/syncer.ts +235 -235
  132. package/src/search/query-expansion.ts +52 -52
  133. package/src/server/formation-timeout.ts +27 -27
  134. package/src/server.ts +23 -1
  135. package/src/skills/mini-skills.ts +386 -386
  136. package/src/store/chat-store.ts +119 -119
  137. package/src/store/graph-store.ts +249 -249
  138. package/src/store/mini-skill-store.ts +349 -349
  139. package/src/store/persistence-json.ts +212 -212
  140. package/src/store/persistence.ts +291 -291
  141. package/src/store/project-affinity.ts +195 -195
  142. package/src/team/event-bus.ts +76 -76
  143. package/src/team/file-locks.ts +173 -173
  144. package/src/team/handoff.ts +161 -161
  145. package/src/team/messages.ts +203 -203
  146. package/src/team/poll.ts +132 -132
  147. package/src/team/tasks.ts +211 -211
  148. package/src/types.ts +1 -1
  149. package/src/workspace/mcp-adapters/codex.ts +191 -191
  150. package/src/workspace/mcp-adapters/copilot.ts +105 -105
  151. package/src/workspace/mcp-adapters/cursor.ts +53 -53
  152. package/src/workspace/mcp-adapters/dsh.ts +147 -0
  153. package/src/workspace/mcp-adapters/kiro.ts +64 -64
  154. package/src/workspace/mcp-adapters/opencode.ts +123 -123
  155. package/src/workspace/mcp-adapters/trae.ts +134 -134
  156. package/src/workspace/mcp-adapters/windsurf.ts +91 -91
  157. package/src/workspace/sanitizer.ts +60 -60
  158. package/dist/memcode-runtime/docs/images/exy.png +0 -0
@@ -1,53 +1,53 @@
1
- import type { MCPConfigAdapter, MCPServerEntry } from '../../types.js';
2
- import { homedir } from 'node:os';
3
- import { join } from 'node:path';
4
-
5
- /**
6
- * Cursor MCP config adapter.
7
- * Format: JSON file at ~/.cursor/mcp.json or .cursor/mcp.json (project-level)
8
- * Structure: { mcpServers: { [name]: { command, args, env?, url? } } }
9
- */
10
- export class CursorMCPAdapter implements MCPConfigAdapter {
11
- readonly source = 'cursor' as const;
12
-
13
- parse(content: string): MCPServerEntry[] {
14
- try {
15
- const config = JSON.parse(content);
16
- const servers = config.mcpServers ?? {};
17
- return Object.entries(servers).map(([name, entry]: [string, any]) => ({
18
- name,
19
- command: entry.command ?? '',
20
- args: entry.args ?? [],
21
- ...(entry.env && Object.keys(entry.env).length > 0 ? { env: entry.env } : {}),
22
- ...(entry.url ? { url: entry.url } : {}),
23
- }));
24
- } catch {
25
- return [];
26
- }
27
- }
28
-
29
- generate(servers: MCPServerEntry[]): string {
30
- const mcpServers: Record<string, any> = {};
31
- for (const s of servers) {
32
- const entry: Record<string, any> = {};
33
- if (s.url) {
34
- entry.url = s.url;
35
- } else {
36
- entry.command = s.command;
37
- entry.args = s.args;
38
- }
39
- if (s.env && Object.keys(s.env).length > 0) {
40
- entry.env = s.env;
41
- }
42
- mcpServers[s.name] = entry;
43
- }
44
- return JSON.stringify({ mcpServers }, null, 2);
45
- }
46
-
47
- getConfigPath(projectRoot?: string): string {
48
- if (projectRoot) {
49
- return join(projectRoot, '.cursor', 'mcp.json');
50
- }
51
- return join(homedir(), '.cursor', 'mcp.json');
52
- }
53
- }
1
+ import type { MCPConfigAdapter, MCPServerEntry } from '../../types.js';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+
5
+ /**
6
+ * Cursor MCP config adapter.
7
+ * Format: JSON file at ~/.cursor/mcp.json or .cursor/mcp.json (project-level)
8
+ * Structure: { mcpServers: { [name]: { command, args, env?, url? } } }
9
+ */
10
+ export class CursorMCPAdapter implements MCPConfigAdapter {
11
+ readonly source = 'cursor' as const;
12
+
13
+ parse(content: string): MCPServerEntry[] {
14
+ try {
15
+ const config = JSON.parse(content);
16
+ const servers = config.mcpServers ?? {};
17
+ return Object.entries(servers).map(([name, entry]: [string, any]) => ({
18
+ name,
19
+ command: entry.command ?? '',
20
+ args: entry.args ?? [],
21
+ ...(entry.env && Object.keys(entry.env).length > 0 ? { env: entry.env } : {}),
22
+ ...(entry.url ? { url: entry.url } : {}),
23
+ }));
24
+ } catch {
25
+ return [];
26
+ }
27
+ }
28
+
29
+ generate(servers: MCPServerEntry[]): string {
30
+ const mcpServers: Record<string, any> = {};
31
+ for (const s of servers) {
32
+ const entry: Record<string, any> = {};
33
+ if (s.url) {
34
+ entry.url = s.url;
35
+ } else {
36
+ entry.command = s.command;
37
+ entry.args = s.args;
38
+ }
39
+ if (s.env && Object.keys(s.env).length > 0) {
40
+ entry.env = s.env;
41
+ }
42
+ mcpServers[s.name] = entry;
43
+ }
44
+ return JSON.stringify({ mcpServers }, null, 2);
45
+ }
46
+
47
+ getConfigPath(projectRoot?: string): string {
48
+ if (projectRoot) {
49
+ return join(projectRoot, '.cursor', 'mcp.json');
50
+ }
51
+ return join(homedir(), '.cursor', 'mcp.json');
52
+ }
53
+ }
@@ -0,0 +1,147 @@
1
+ import { homedir } from 'node:os';
2
+ import { join } from 'node:path';
3
+ import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
4
+ import type { MCPConfigAdapter, MCPServerEntry } from '../../types.js';
5
+
6
+ /**
7
+ * DeepSeek Harness (DSH) MCP Configuration Adapter.
8
+ *
9
+ * DSH composes its runtime from Cordis patch layers. External MCP servers are
10
+ * registered as rows of the `@deepseek-ai/dsh-mcp-client` plugin inside a
11
+ * patch list. The user-level layer that applies to every profile lives at:
12
+ *
13
+ * $DSH_HOME/cordis.patch.yml (default: ~/.dsh/cordis.patch.yml)
14
+ *
15
+ * Row shape follows DSH's own shipped Memorix reference
16
+ * (examples/mcp-memory/memorix.cordis.yml in deepseek-ai/deepseek-harness):
17
+ *
18
+ * - insert:
19
+ * - id: memory-memorix
20
+ * name: '@deepseek-ai/dsh-mcp-client'
21
+ * config:
22
+ * serverName: memorix
23
+ * transport: stdio
24
+ * command: memorix
25
+ * args: [serve]
26
+ *
27
+ * The `cwd` key is deliberately omitted so the MCP child inherits the DSH
28
+ * process working directory — the workspace DSH was launched from — which is
29
+ * what keeps Memorix bound to the current Git project.
30
+ *
31
+ * Source: https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/mcp/mcp-client
32
+ */
33
+
34
+ const MCP_CLIENT_PLUGIN = '@deepseek-ai/dsh-mcp-client';
35
+ const MEMORIX_ROW_ID = 'memory-memorix';
36
+
37
+ function resolveDshHome(): string {
38
+ const configured = process.env.DSH_HOME?.trim();
39
+ if (configured) return configured;
40
+ return join(homedir(), '.dsh');
41
+ }
42
+
43
+ function isMemorixRow(row: unknown): row is Record<string, unknown> {
44
+ if (!row || typeof row !== 'object' || Array.isArray(row)) return false;
45
+ const record = row as Record<string, unknown>;
46
+ if (record.name !== MCP_CLIENT_PLUGIN) return false;
47
+ const config = record.config;
48
+ return Boolean(
49
+ config && typeof config === 'object' && !Array.isArray(config)
50
+ && (config as Record<string, unknown>).serverName === 'memorix',
51
+ );
52
+ }
53
+
54
+ function rowToEntry(row: Record<string, unknown>): MCPServerEntry | null {
55
+ const config = row.config as Record<string, unknown>;
56
+ if (!config || typeof config !== 'object') return null;
57
+ const entry: MCPServerEntry = {
58
+ name: 'memorix',
59
+ command: typeof config.command === 'string' ? config.command : '',
60
+ args: Array.isArray(config.args) ? config.args.filter((arg): arg is string => typeof arg === 'string') : [],
61
+ };
62
+ if (typeof config.url === 'string' && config.url) entry.url = config.url;
63
+ if (config.env && typeof config.env === 'object' && !Array.isArray(config.env)) {
64
+ const env: Record<string, string> = {};
65
+ for (const [key, value] of Object.entries(config.env as Record<string, unknown>)) {
66
+ if (typeof value === 'string') env[key] = value;
67
+ }
68
+ if (Object.keys(env).length > 0) entry.env = env;
69
+ }
70
+ if (config.disabled === true) entry.disabled = true;
71
+ return entry;
72
+ }
73
+
74
+ /** Flatten one parsed patch document into its row list, tolerating both
75
+ * `- insert: [...]` operations and bare row lists. */
76
+ function patchRows(document: unknown): Record<string, unknown>[] {
77
+ const rows: Record<string, unknown>[] = [];
78
+ if (!Array.isArray(document)) return rows;
79
+ for (const item of document) {
80
+ if (!item || typeof item !== 'object' || Array.isArray(item)) continue;
81
+ const record = item as Record<string, unknown>;
82
+ if (Array.isArray(record.insert)) {
83
+ for (const row of record.insert) {
84
+ if (row && typeof row === 'object' && !Array.isArray(row)) {
85
+ rows.push(row as Record<string, unknown>);
86
+ }
87
+ }
88
+ continue;
89
+ }
90
+ rows.push(record);
91
+ }
92
+ return rows;
93
+ }
94
+
95
+ export class DshMCPAdapter implements MCPConfigAdapter {
96
+ readonly source = 'dsh' as const;
97
+
98
+ parse(content: string): MCPServerEntry[] {
99
+ if (!content.trim()) return [];
100
+ let document: unknown;
101
+ try {
102
+ document = parseYaml(content);
103
+ } catch {
104
+ return [];
105
+ }
106
+ return patchRows(document)
107
+ .filter(isMemorixRow)
108
+ .map(rowToEntry)
109
+ .filter((entry): entry is MCPServerEntry => entry !== null);
110
+ }
111
+
112
+ generate(servers: MCPServerEntry[]): string {
113
+ const rows = servers
114
+ .filter((server) => server.name === 'memorix')
115
+ .map((server) => {
116
+ const config: Record<string, unknown> = {
117
+ serverName: server.name,
118
+ };
119
+ if (server.url) {
120
+ config.transport = 'streamable-http';
121
+ config.url = server.url;
122
+ if (server.headers && Object.keys(server.headers).length > 0) config.headers = server.headers;
123
+ } else {
124
+ config.transport = 'stdio';
125
+ config.command = server.command;
126
+ if (server.args && server.args.length > 0) config.args = server.args;
127
+ if (server.env && Object.keys(server.env).length > 0) config.env = server.env;
128
+ }
129
+ const row: Record<string, unknown> = {
130
+ id: MEMORIX_ROW_ID,
131
+ name: MCP_CLIENT_PLUGIN,
132
+ config,
133
+ };
134
+ if (server.disabled === true) row.disabled = true;
135
+ return row;
136
+ });
137
+
138
+ if (rows.length === 0) return '';
139
+ return stringifyYaml([{ insert: rows }], { lineWidth: 0 }) + '\n';
140
+ }
141
+
142
+ getConfigPath(_projectRoot?: string): string {
143
+ return join(resolveDshHome(), 'cordis.patch.yml');
144
+ }
145
+ }
146
+
147
+ export { MEMORIX_ROW_ID };
@@ -1,64 +1,64 @@
1
- import type { MCPConfigAdapter, MCPServerEntry } from '../../types.js';
2
- import { homedir } from 'node:os';
3
- import { join } from 'node:path';
4
-
5
- /**
6
- * Kiro MCP config adapter.
7
- * Format: JSON file at .kiro/settings/mcp.json (project-level)
8
- * or ~/.kiro/settings/mcp.json (user-level)
9
- * Structure: { mcpServers: { ... }, powers?: { mcpServers: { ... } } }
10
- *
11
- * Kiro stores user-added servers in `mcpServers` and power-installed
12
- * servers (context7, figma, postman, supabase etc.) in `powers.mcpServers`.
13
- * Both sections use the same entry format.
14
- *
15
- * Source: Kiro official MCP documentation.
16
- */
17
- export class KiroMCPAdapter implements MCPConfigAdapter {
18
- readonly source = 'kiro' as const;
19
-
20
- parse(content: string): MCPServerEntry[] {
21
- try {
22
- const config = JSON.parse(content);
23
- // Merge top-level mcpServers and powers.mcpServers (dedup by name)
24
- const topLevel = config.mcpServers ?? {};
25
- const powers = config.powers?.mcpServers ?? {};
26
- const merged = { ...powers, ...topLevel };
27
- return Object.entries(merged).map(([name, entry]: [string, any]) => ({
28
- name,
29
- command: entry.command ?? '',
30
- args: entry.args ?? [],
31
- ...(entry.env && Object.keys(entry.env).length > 0 ? { env: entry.env } : {}),
32
- ...(entry.url ? { url: entry.url } : {}),
33
- ...(entry.disabled === true ? { disabled: true } : {}),
34
- }));
35
- } catch {
36
- return [];
37
- }
38
- }
39
-
40
- generate(servers: MCPServerEntry[]): string {
41
- const mcpServers: Record<string, any> = {};
42
- for (const s of servers) {
43
- const entry: Record<string, any> = {};
44
- if (s.url) {
45
- entry.url = s.url;
46
- } else {
47
- entry.command = s.command;
48
- entry.args = s.args;
49
- }
50
- if (s.env && Object.keys(s.env).length > 0) {
51
- entry.env = s.env;
52
- }
53
- mcpServers[s.name] = entry;
54
- }
55
- return JSON.stringify({ mcpServers }, null, 2);
56
- }
57
-
58
- getConfigPath(projectRoot?: string): string {
59
- if (projectRoot) {
60
- return join(projectRoot, '.kiro', 'settings', 'mcp.json');
61
- }
62
- return join(homedir(), '.kiro', 'settings', 'mcp.json');
63
- }
64
- }
1
+ import type { MCPConfigAdapter, MCPServerEntry } from '../../types.js';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+
5
+ /**
6
+ * Kiro MCP config adapter.
7
+ * Format: JSON file at .kiro/settings/mcp.json (project-level)
8
+ * or ~/.kiro/settings/mcp.json (user-level)
9
+ * Structure: { mcpServers: { ... }, powers?: { mcpServers: { ... } } }
10
+ *
11
+ * Kiro stores user-added servers in `mcpServers` and power-installed
12
+ * servers (context7, figma, postman, supabase etc.) in `powers.mcpServers`.
13
+ * Both sections use the same entry format.
14
+ *
15
+ * Source: Kiro official MCP documentation.
16
+ */
17
+ export class KiroMCPAdapter implements MCPConfigAdapter {
18
+ readonly source = 'kiro' as const;
19
+
20
+ parse(content: string): MCPServerEntry[] {
21
+ try {
22
+ const config = JSON.parse(content);
23
+ // Merge top-level mcpServers and powers.mcpServers (dedup by name)
24
+ const topLevel = config.mcpServers ?? {};
25
+ const powers = config.powers?.mcpServers ?? {};
26
+ const merged = { ...powers, ...topLevel };
27
+ return Object.entries(merged).map(([name, entry]: [string, any]) => ({
28
+ name,
29
+ command: entry.command ?? '',
30
+ args: entry.args ?? [],
31
+ ...(entry.env && Object.keys(entry.env).length > 0 ? { env: entry.env } : {}),
32
+ ...(entry.url ? { url: entry.url } : {}),
33
+ ...(entry.disabled === true ? { disabled: true } : {}),
34
+ }));
35
+ } catch {
36
+ return [];
37
+ }
38
+ }
39
+
40
+ generate(servers: MCPServerEntry[]): string {
41
+ const mcpServers: Record<string, any> = {};
42
+ for (const s of servers) {
43
+ const entry: Record<string, any> = {};
44
+ if (s.url) {
45
+ entry.url = s.url;
46
+ } else {
47
+ entry.command = s.command;
48
+ entry.args = s.args;
49
+ }
50
+ if (s.env && Object.keys(s.env).length > 0) {
51
+ entry.env = s.env;
52
+ }
53
+ mcpServers[s.name] = entry;
54
+ }
55
+ return JSON.stringify({ mcpServers }, null, 2);
56
+ }
57
+
58
+ getConfigPath(projectRoot?: string): string {
59
+ if (projectRoot) {
60
+ return join(projectRoot, '.kiro', 'settings', 'mcp.json');
61
+ }
62
+ return join(homedir(), '.kiro', 'settings', 'mcp.json');
63
+ }
64
+ }
@@ -1,123 +1,123 @@
1
- import type { MCPConfigAdapter, MCPServerEntry } from '../../types.js';
2
- import { homedir } from 'node:os';
3
- import { join } from 'node:path';
4
-
5
- /**
6
- * OpenCode MCP Configuration Adapter.
7
- *
8
- * OpenCode uses JSON config files for MCP servers:
9
- * 1. Project-level: opencode.json in project root
10
- * 2. Global: ~/.config/opencode/opencode.json
11
- *
12
- * Format:
13
- * {
14
- * "$schema": "https://opencode.ai/config.json",
15
- * "mcp": {
16
- * "name": {
17
- * "type": "local",
18
- * "command": ["memorix", "serve"],
19
- * "environment": { "KEY": "value" },
20
- * "enabled": true
21
- * }
22
- * }
23
- * }
24
- *
25
- * Remote (HTTP) servers:
26
- * {
27
- * "mcp": {
28
- * "name": {
29
- * "type": "remote",
30
- * "url": "https://...",
31
- * "headers": { "Authorization": "Bearer ..." }
32
- * }
33
- * }
34
- * }
35
- *
36
- * Source: https://opencode.ai/docs/mcp-servers/
37
- */
38
- export class OpenCodeMCPAdapter implements MCPConfigAdapter {
39
- readonly source = 'opencode' as const;
40
-
41
- parse(content: string): MCPServerEntry[] {
42
- try {
43
- const config = JSON.parse(content);
44
- const servers = config.mcp ?? {};
45
- return Object.entries(servers).map(([name, entry]: [string, any]) => {
46
- const result: MCPServerEntry = {
47
- name,
48
- command: '',
49
- args: [],
50
- };
51
-
52
- if (entry.type === 'remote' && entry.url) {
53
- // HTTP transport
54
- result.url = entry.url;
55
- if (entry.headers && typeof entry.headers === 'object' && Object.keys(entry.headers).length > 0) {
56
- result.headers = entry.headers;
57
- }
58
- } else {
59
- // Local (stdio) transport — command is an array in OpenCode
60
- if (Array.isArray(entry.command) && entry.command.length > 0) {
61
- result.command = entry.command[0];
62
- result.args = entry.command.slice(1);
63
- } else if (typeof entry.command === 'string') {
64
- result.command = entry.command;
65
- }
66
- }
67
-
68
- // Environment variables (OpenCode uses "environment" not "env")
69
- const env = entry.environment ?? entry.env;
70
- if (env && typeof env === 'object' && Object.keys(env).length > 0) {
71
- result.env = env;
72
- }
73
-
74
- // Disabled flag
75
- if (entry.enabled === false) {
76
- result.disabled = true;
77
- }
78
-
79
- return result;
80
- });
81
- } catch {
82
- return [];
83
- }
84
- }
85
-
86
- generate(servers: MCPServerEntry[]): string {
87
- const mcp: Record<string, any> = {};
88
- for (const s of servers) {
89
- const entry: Record<string, any> = {};
90
-
91
- if (s.url) {
92
- // HTTP transport
93
- entry.type = 'remote';
94
- entry.url = s.url;
95
- if (s.headers && Object.keys(s.headers).length > 0) {
96
- entry.headers = s.headers;
97
- }
98
- } else {
99
- // stdio transport — OpenCode uses command as array
100
- entry.type = 'local';
101
- entry.command = [s.command, ...s.args];
102
- }
103
-
104
- if (s.env && Object.keys(s.env).length > 0) {
105
- entry.environment = s.env;
106
- }
107
-
108
- if (s.disabled === true) {
109
- entry.enabled = false;
110
- }
111
-
112
- mcp[s.name] = entry;
113
- }
114
- return JSON.stringify({ $schema: 'https://opencode.ai/config.json', mcp }, null, 2);
115
- }
116
-
117
- getConfigPath(projectRoot?: string): string {
118
- if (projectRoot) {
119
- return join(projectRoot, 'opencode.json');
120
- }
121
- return join(homedir(), '.config', 'opencode', 'opencode.json');
122
- }
123
- }
1
+ import type { MCPConfigAdapter, MCPServerEntry } from '../../types.js';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+
5
+ /**
6
+ * OpenCode MCP Configuration Adapter.
7
+ *
8
+ * OpenCode uses JSON config files for MCP servers:
9
+ * 1. Project-level: opencode.json in project root
10
+ * 2. Global: ~/.config/opencode/opencode.json
11
+ *
12
+ * Format:
13
+ * {
14
+ * "$schema": "https://opencode.ai/config.json",
15
+ * "mcp": {
16
+ * "name": {
17
+ * "type": "local",
18
+ * "command": ["memorix", "serve"],
19
+ * "environment": { "KEY": "value" },
20
+ * "enabled": true
21
+ * }
22
+ * }
23
+ * }
24
+ *
25
+ * Remote (HTTP) servers:
26
+ * {
27
+ * "mcp": {
28
+ * "name": {
29
+ * "type": "remote",
30
+ * "url": "https://...",
31
+ * "headers": { "Authorization": "Bearer ..." }
32
+ * }
33
+ * }
34
+ * }
35
+ *
36
+ * Source: https://opencode.ai/docs/mcp-servers/
37
+ */
38
+ export class OpenCodeMCPAdapter implements MCPConfigAdapter {
39
+ readonly source = 'opencode' as const;
40
+
41
+ parse(content: string): MCPServerEntry[] {
42
+ try {
43
+ const config = JSON.parse(content);
44
+ const servers = config.mcp ?? {};
45
+ return Object.entries(servers).map(([name, entry]: [string, any]) => {
46
+ const result: MCPServerEntry = {
47
+ name,
48
+ command: '',
49
+ args: [],
50
+ };
51
+
52
+ if (entry.type === 'remote' && entry.url) {
53
+ // HTTP transport
54
+ result.url = entry.url;
55
+ if (entry.headers && typeof entry.headers === 'object' && Object.keys(entry.headers).length > 0) {
56
+ result.headers = entry.headers;
57
+ }
58
+ } else {
59
+ // Local (stdio) transport — command is an array in OpenCode
60
+ if (Array.isArray(entry.command) && entry.command.length > 0) {
61
+ result.command = entry.command[0];
62
+ result.args = entry.command.slice(1);
63
+ } else if (typeof entry.command === 'string') {
64
+ result.command = entry.command;
65
+ }
66
+ }
67
+
68
+ // Environment variables (OpenCode uses "environment" not "env")
69
+ const env = entry.environment ?? entry.env;
70
+ if (env && typeof env === 'object' && Object.keys(env).length > 0) {
71
+ result.env = env;
72
+ }
73
+
74
+ // Disabled flag
75
+ if (entry.enabled === false) {
76
+ result.disabled = true;
77
+ }
78
+
79
+ return result;
80
+ });
81
+ } catch {
82
+ return [];
83
+ }
84
+ }
85
+
86
+ generate(servers: MCPServerEntry[]): string {
87
+ const mcp: Record<string, any> = {};
88
+ for (const s of servers) {
89
+ const entry: Record<string, any> = {};
90
+
91
+ if (s.url) {
92
+ // HTTP transport
93
+ entry.type = 'remote';
94
+ entry.url = s.url;
95
+ if (s.headers && Object.keys(s.headers).length > 0) {
96
+ entry.headers = s.headers;
97
+ }
98
+ } else {
99
+ // stdio transport — OpenCode uses command as array
100
+ entry.type = 'local';
101
+ entry.command = [s.command, ...s.args];
102
+ }
103
+
104
+ if (s.env && Object.keys(s.env).length > 0) {
105
+ entry.environment = s.env;
106
+ }
107
+
108
+ if (s.disabled === true) {
109
+ entry.enabled = false;
110
+ }
111
+
112
+ mcp[s.name] = entry;
113
+ }
114
+ return JSON.stringify({ $schema: 'https://opencode.ai/config.json', mcp }, null, 2);
115
+ }
116
+
117
+ getConfigPath(projectRoot?: string): string {
118
+ if (projectRoot) {
119
+ return join(projectRoot, 'opencode.json');
120
+ }
121
+ return join(homedir(), '.config', 'opencode', 'opencode.json');
122
+ }
123
+ }