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.
- package/CHANGELOG.md +34 -0
- package/README.md +20 -6
- package/README.zh-CN.md +16 -3
- package/TEAM.md +86 -86
- package/assets/star-history-dark.svg +8 -8
- package/assets/star-history-light.svg +8 -8
- package/assets/star-history.json +16 -2
- package/dist/cli/index.js +539 -233
- package/dist/cli/memcode.js +0 -0
- package/dist/index.js +72 -15
- package/dist/maintenance-runner.js +9 -7
- package/dist/media-audio-runner.js +9 -7
- package/dist/media-video-runner.js +9 -7
- package/dist/memcode/index.js +9 -7
- package/dist/memcode-runtime/CHANGELOG.md +34 -0
- package/dist/sdk.js +71 -14
- package/dist/types.d.ts +1 -1
- package/dist/vector-backfill-runner.js +9 -7
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +6 -5
- package/docs/API_REFERENCE.md +9 -5
- package/docs/CONFIGURATION.md +15 -5
- package/docs/DESIGN_DECISIONS.md +357 -357
- package/docs/DEVELOPMENT.md +2 -2
- package/docs/INTEGRATIONS.md +9 -2
- package/docs/KNOWN_ISSUES_AND_ROADMAP.md +4 -3
- package/docs/MEMCODE.md +1 -1
- package/docs/MODULES.md +1 -1
- package/docs/README.md +1 -1
- package/docs/SETUP.md +12 -4
- package/docs/hooks-architecture.md +2 -1
- package/llms-full.txt +7 -6
- package/llms.txt +2 -2
- package/package.json +1 -1
- package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
- package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/copilot/memorix/plugin.json +1 -1
- package/plugins/gemini/memorix/gemini-extension.json +1 -1
- package/plugins/omp/memorix/package.json +1 -1
- package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/pi/memorix/package.json +1 -1
- package/server.json +2 -2
- package/src/audit/index.ts +156 -156
- package/src/cli/commands/agent-integrations.ts +7 -1
- package/src/cli/commands/audit-list.ts +89 -89
- package/src/cli/commands/formation.ts +48 -48
- package/src/cli/commands/git-hook-install.ts +111 -111
- package/src/cli/commands/handoff.ts +54 -54
- package/src/cli/commands/hooks-install.ts +2 -0
- package/src/cli/commands/hooks-preview.ts +5 -0
- package/src/cli/commands/hooks-status.ts +63 -63
- package/src/cli/commands/ingest.ts +44 -44
- package/src/cli/commands/integrate-shared.ts +15 -15
- package/src/cli/commands/integrate.ts +2 -0
- package/src/cli/commands/lock.ts +82 -82
- package/src/cli/commands/media-image.ts +27 -0
- package/src/cli/commands/media.ts +8 -1
- package/src/cli/commands/memory.ts +44 -34
- package/src/cli/commands/message.ts +104 -104
- package/src/cli/commands/operator-shared.ts +62 -18
- package/src/cli/commands/poll.ts +58 -58
- package/src/cli/commands/purge-all-memory.ts +85 -85
- package/src/cli/commands/purge-project-memory.ts +83 -83
- package/src/cli/commands/reasoning.ts +118 -118
- package/src/cli/commands/setup.ts +57 -2
- package/src/cli/commands/skills.ts +114 -114
- package/src/cli/commands/task.ts +167 -167
- package/src/cli/commands/transfer.ts +47 -47
- package/src/cli/commands/uninstall-project-artifacts.ts +85 -85
- package/src/cli/commands/uninstall.ts +20 -0
- package/src/cli/tui/ChatView.tsx +234 -234
- package/src/cli/tui/CommandBar.tsx +312 -312
- package/src/cli/tui/ContextRail.tsx +118 -118
- package/src/cli/tui/HeaderBar.tsx +72 -72
- package/src/cli/tui/LogoBanner.tsx +51 -51
- package/src/cli/tui/Sidebar.tsx +179 -179
- package/src/cli/tui/index.ts +41 -41
- package/src/cli/tui/markdown-render.tsx +371 -371
- package/src/cli/tui/use-mouse.ts +157 -157
- package/src/cli/tui/useNavigation.ts +56 -56
- package/src/cli/version.ts +7 -7
- package/src/cli/workbench.ts +1 -1
- package/src/dashboard/project-classification.ts +64 -64
- package/src/git/hooks-path.ts +85 -85
- package/src/hooks/installers/index.ts +43 -2
- package/src/hooks/pattern-detector.ts +173 -173
- package/src/hooks/significance-filter.ts +250 -250
- package/src/hooks/types.ts +2 -0
- package/src/integrations/registry.ts +9 -0
- package/src/llm/memory-manager.ts +328 -328
- package/src/llm/provider.ts +885 -885
- package/src/llm/quality.ts +248 -248
- package/src/media/minimax.ts +9 -1
- package/src/memory/attribution-guard.ts +249 -249
- package/src/memory/disclosure-policy.ts +135 -135
- package/src/memory/entity-extractor.ts +197 -197
- package/src/memory/formation/evaluate.ts +217 -217
- package/src/memory/formation/extract.ts +361 -361
- package/src/memory/formation/index.ts +417 -417
- package/src/memory/formation/resolve.ts +344 -344
- package/src/memory/formation/types.ts +315 -315
- package/src/memory/freshness.ts +122 -122
- package/src/memory/graph.ts +197 -197
- package/src/memory/refs.ts +94 -94
- package/src/memory/secret-filter.ts +79 -79
- package/src/orchestrate/adapters/claude-stream.ts +192 -192
- package/src/orchestrate/adapters/claude.ts +111 -111
- package/src/orchestrate/adapters/codex-stream.ts +134 -134
- package/src/orchestrate/adapters/codex.ts +41 -41
- package/src/orchestrate/adapters/gemini-stream.ts +166 -166
- package/src/orchestrate/adapters/gemini.ts +42 -42
- package/src/orchestrate/adapters/index.ts +73 -73
- package/src/orchestrate/adapters/opencode-stream.ts +143 -143
- package/src/orchestrate/adapters/opencode.ts +47 -47
- package/src/orchestrate/adapters/types.ts +77 -77
- package/src/orchestrate/capability-router.ts +284 -284
- package/src/orchestrate/context-compact.ts +188 -188
- package/src/orchestrate/cost-tracker.ts +219 -219
- package/src/orchestrate/error-recovery.ts +191 -191
- package/src/orchestrate/evidence.ts +140 -140
- package/src/orchestrate/ledger.ts +110 -110
- package/src/orchestrate/memorix-bridge.ts +343 -343
- package/src/orchestrate/output-budget.ts +80 -80
- package/src/orchestrate/permission.ts +152 -152
- package/src/orchestrate/pipeline-trace.ts +131 -131
- package/src/orchestrate/prompt-builder.ts +155 -155
- package/src/orchestrate/ring-buffer.ts +37 -37
- package/src/orchestrate/task-graph.ts +389 -389
- package/src/project/aliases.ts +374 -374
- package/src/rules/adapters/trae.ts +56 -56
- package/src/rules/syncer.ts +235 -235
- package/src/search/query-expansion.ts +52 -52
- package/src/server/formation-timeout.ts +27 -27
- package/src/server.ts +23 -1
- package/src/skills/mini-skills.ts +386 -386
- package/src/store/chat-store.ts +119 -119
- package/src/store/graph-store.ts +249 -249
- package/src/store/mini-skill-store.ts +349 -349
- package/src/store/persistence-json.ts +212 -212
- package/src/store/persistence.ts +291 -291
- package/src/store/project-affinity.ts +195 -195
- package/src/team/event-bus.ts +76 -76
- package/src/team/file-locks.ts +173 -173
- package/src/team/handoff.ts +161 -161
- package/src/team/messages.ts +203 -203
- package/src/team/poll.ts +132 -132
- package/src/team/tasks.ts +211 -211
- package/src/types.ts +1 -1
- package/src/workspace/mcp-adapters/codex.ts +191 -191
- package/src/workspace/mcp-adapters/copilot.ts +105 -105
- package/src/workspace/mcp-adapters/cursor.ts +53 -53
- package/src/workspace/mcp-adapters/dsh.ts +147 -0
- package/src/workspace/mcp-adapters/kiro.ts +64 -64
- package/src/workspace/mcp-adapters/opencode.ts +123 -123
- package/src/workspace/mcp-adapters/trae.ts +134 -134
- package/src/workspace/mcp-adapters/windsurf.ts +91 -91
- package/src/workspace/sanitizer.ts +60 -60
- 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
|
+
}
|