dotai-cli 1.0.0
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/README.md +186 -0
- package/package.json +54 -0
- package/src/commands/config.js +93 -0
- package/src/commands/create.js +120 -0
- package/src/commands/install.js +120 -0
- package/src/commands/list.js +68 -0
- package/src/commands/open.js +82 -0
- package/src/commands/sync.js +71 -0
- package/src/commands/uninstall.js +92 -0
- package/src/index.js +141 -0
- package/src/lib/config.js +90 -0
- package/src/lib/paths.js +60 -0
- package/src/lib/skills.js +261 -0
- package/src/providers/index.js +125 -0
- package/src/providers/mcp.js +184 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import { join, basename, dirname } from 'path';
|
|
3
|
+
import matter from 'gray-matter';
|
|
4
|
+
import { getSkillsRepoDir } from './paths.js';
|
|
5
|
+
import { providers, getGlobalSkillPath, getProjectSkillPath } from '../providers/index.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Skill template with frontmatter
|
|
9
|
+
*/
|
|
10
|
+
export function createSkillTemplate(name, description, instructions = '') {
|
|
11
|
+
return `---
|
|
12
|
+
name: ${name}
|
|
13
|
+
description: ${description}
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# ${name}
|
|
17
|
+
|
|
18
|
+
${instructions || `Instructions for the ${name} skill.
|
|
19
|
+
|
|
20
|
+
## When to use this skill
|
|
21
|
+
|
|
22
|
+
- Use this when...
|
|
23
|
+
- This is helpful for...
|
|
24
|
+
|
|
25
|
+
## How to use it
|
|
26
|
+
|
|
27
|
+
Step-by-step guidance, conventions, and patterns the agent should follow.
|
|
28
|
+
|
|
29
|
+
## Examples
|
|
30
|
+
|
|
31
|
+
Include examples to help the agent understand expected behavior.
|
|
32
|
+
`}
|
|
33
|
+
`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Parse a SKILL.md file
|
|
38
|
+
*/
|
|
39
|
+
export async function parseSkillFile(filePath) {
|
|
40
|
+
try {
|
|
41
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
42
|
+
const { data, content: body } = matter(content);
|
|
43
|
+
return {
|
|
44
|
+
name: data.name || basename(dirname(filePath)),
|
|
45
|
+
description: data.description || '',
|
|
46
|
+
metadata: data,
|
|
47
|
+
body,
|
|
48
|
+
filePath
|
|
49
|
+
};
|
|
50
|
+
} catch (err) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* List all skills in the central repository
|
|
57
|
+
*/
|
|
58
|
+
export async function listCentralSkills() {
|
|
59
|
+
const skillsDir = getSkillsRepoDir();
|
|
60
|
+
|
|
61
|
+
if (!await fs.pathExists(skillsDir)) {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const entries = await fs.readdir(skillsDir, { withFileTypes: true });
|
|
66
|
+
const skills = [];
|
|
67
|
+
|
|
68
|
+
for (const entry of entries) {
|
|
69
|
+
if (entry.isDirectory()) {
|
|
70
|
+
const skillMdPath = join(skillsDir, entry.name, 'SKILL.md');
|
|
71
|
+
if (await fs.pathExists(skillMdPath)) {
|
|
72
|
+
const skill = await parseSkillFile(skillMdPath);
|
|
73
|
+
if (skill) {
|
|
74
|
+
skills.push({
|
|
75
|
+
...skill,
|
|
76
|
+
folder: entry.name,
|
|
77
|
+
path: join(skillsDir, entry.name)
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return skills;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Get a skill from central repo by name
|
|
89
|
+
*/
|
|
90
|
+
export async function getCentralSkill(name) {
|
|
91
|
+
const skillsDir = getSkillsRepoDir();
|
|
92
|
+
const skillPath = join(skillsDir, name);
|
|
93
|
+
const skillMdPath = join(skillPath, 'SKILL.md');
|
|
94
|
+
|
|
95
|
+
if (!await fs.pathExists(skillMdPath)) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const skill = await parseSkillFile(skillMdPath);
|
|
100
|
+
if (skill) {
|
|
101
|
+
skill.folder = name;
|
|
102
|
+
skill.path = skillPath;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return skill;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Create a new skill in the central repository
|
|
110
|
+
*/
|
|
111
|
+
export async function createSkill(name, description, instructions = '') {
|
|
112
|
+
const skillsDir = getSkillsRepoDir();
|
|
113
|
+
const skillPath = join(skillsDir, name);
|
|
114
|
+
const skillMdPath = join(skillPath, 'SKILL.md');
|
|
115
|
+
|
|
116
|
+
// Check if skill already exists
|
|
117
|
+
if (await fs.pathExists(skillPath)) {
|
|
118
|
+
throw new Error(`Skill '${name}' already exists`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Create skill directory
|
|
122
|
+
await fs.ensureDir(skillPath);
|
|
123
|
+
|
|
124
|
+
// Create SKILL.md
|
|
125
|
+
const content = createSkillTemplate(name, description, instructions);
|
|
126
|
+
await fs.writeFile(skillMdPath, content, 'utf-8');
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
name,
|
|
130
|
+
description,
|
|
131
|
+
path: skillPath,
|
|
132
|
+
skillMdPath
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Install a skill to a specific provider
|
|
138
|
+
*/
|
|
139
|
+
export async function installSkillToProvider(skillName, providerId, scope = 'global', projectRoot = process.cwd()) {
|
|
140
|
+
const provider = providers[providerId];
|
|
141
|
+
if (!provider) {
|
|
142
|
+
throw new Error(`Unknown provider: ${providerId}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Get source skill
|
|
146
|
+
const skill = await getCentralSkill(skillName);
|
|
147
|
+
if (!skill) {
|
|
148
|
+
throw new Error(`Skill '${skillName}' not found in central repository`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Determine target path
|
|
152
|
+
const targetPath = scope === 'global'
|
|
153
|
+
? getGlobalSkillPath(providerId, skillName)
|
|
154
|
+
: getProjectSkillPath(providerId, skillName, projectRoot);
|
|
155
|
+
|
|
156
|
+
// Copy skill folder
|
|
157
|
+
await fs.ensureDir(targetPath);
|
|
158
|
+
await fs.copy(skill.path, targetPath, { overwrite: true });
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
skillName,
|
|
162
|
+
providerId,
|
|
163
|
+
scope,
|
|
164
|
+
targetPath
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Install a skill to multiple providers
|
|
170
|
+
*/
|
|
171
|
+
export async function installSkill(skillName, providerIds, scope = 'global', projectRoot = process.cwd()) {
|
|
172
|
+
const results = [];
|
|
173
|
+
|
|
174
|
+
for (const providerId of providerIds) {
|
|
175
|
+
try {
|
|
176
|
+
const result = await installSkillToProvider(skillName, providerId, scope, projectRoot);
|
|
177
|
+
results.push({ ...result, success: true });
|
|
178
|
+
} catch (err) {
|
|
179
|
+
results.push({
|
|
180
|
+
skillName,
|
|
181
|
+
providerId,
|
|
182
|
+
scope,
|
|
183
|
+
success: false,
|
|
184
|
+
error: err.message
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return results;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Uninstall a skill from a provider
|
|
194
|
+
*/
|
|
195
|
+
export async function uninstallSkillFromProvider(skillName, providerId, scope = 'global', projectRoot = process.cwd()) {
|
|
196
|
+
const provider = providers[providerId];
|
|
197
|
+
if (!provider) {
|
|
198
|
+
throw new Error(`Unknown provider: ${providerId}`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const targetPath = scope === 'global'
|
|
202
|
+
? getGlobalSkillPath(providerId, skillName)
|
|
203
|
+
: getProjectSkillPath(providerId, skillName, projectRoot);
|
|
204
|
+
|
|
205
|
+
if (await fs.pathExists(targetPath)) {
|
|
206
|
+
await fs.remove(targetPath);
|
|
207
|
+
return { removed: true, path: targetPath };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return { removed: false, path: targetPath };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Check where a skill is installed
|
|
215
|
+
*/
|
|
216
|
+
export async function getSkillInstallStatus(skillName) {
|
|
217
|
+
const status = {};
|
|
218
|
+
|
|
219
|
+
for (const [providerId, provider] of Object.entries(providers)) {
|
|
220
|
+
const globalPath = getGlobalSkillPath(providerId, skillName);
|
|
221
|
+
const projectPath = getProjectSkillPath(providerId, skillName);
|
|
222
|
+
|
|
223
|
+
status[providerId] = {
|
|
224
|
+
name: provider.name,
|
|
225
|
+
global: await fs.pathExists(globalPath),
|
|
226
|
+
globalPath,
|
|
227
|
+
project: await fs.pathExists(projectPath),
|
|
228
|
+
projectPath
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return status;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Import a skill from an external path to central repo
|
|
237
|
+
*/
|
|
238
|
+
export async function importSkill(sourcePath) {
|
|
239
|
+
const skillMdPath = join(sourcePath, 'SKILL.md');
|
|
240
|
+
|
|
241
|
+
if (!await fs.pathExists(skillMdPath)) {
|
|
242
|
+
throw new Error(`No SKILL.md found in ${sourcePath}`);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const skill = await parseSkillFile(skillMdPath);
|
|
246
|
+
if (!skill) {
|
|
247
|
+
throw new Error(`Failed to parse SKILL.md in ${sourcePath}`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const skillName = skill.name || basename(sourcePath);
|
|
251
|
+
const targetPath = join(getSkillsRepoDir(), skillName);
|
|
252
|
+
|
|
253
|
+
// Copy to central repo
|
|
254
|
+
await fs.copy(sourcePath, targetPath, { overwrite: true });
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
name: skillName,
|
|
258
|
+
path: targetPath,
|
|
259
|
+
imported: true
|
|
260
|
+
};
|
|
261
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import { getHomeDir } from '../lib/paths.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Provider definitions with their skill paths
|
|
6
|
+
* Each provider has:
|
|
7
|
+
* - name: Display name
|
|
8
|
+
* - id: Unique identifier
|
|
9
|
+
* - globalPath: Where global skills are stored
|
|
10
|
+
* - projectPath: Where project skills are stored (relative to project root)
|
|
11
|
+
* - skillFile: The instruction file name (usually SKILL.md)
|
|
12
|
+
* - enabled: Whether this provider is enabled by default
|
|
13
|
+
* - notes: Any special handling notes
|
|
14
|
+
*/
|
|
15
|
+
export const providers = {
|
|
16
|
+
'claude-code': {
|
|
17
|
+
name: 'Claude Code',
|
|
18
|
+
id: 'claude-code',
|
|
19
|
+
globalPath: () => join(getHomeDir(), '.claude', 'skills'),
|
|
20
|
+
projectPath: '.claude/skills',
|
|
21
|
+
skillFile: 'SKILL.md',
|
|
22
|
+
enabled: true,
|
|
23
|
+
description: 'Anthropic Claude Code CLI',
|
|
24
|
+
website: 'https://claude.ai/code'
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
'cursor': {
|
|
28
|
+
name: 'Cursor',
|
|
29
|
+
id: 'cursor',
|
|
30
|
+
globalPath: () => join(getHomeDir(), '.cursor', 'skills'),
|
|
31
|
+
projectPath: '.cursor/skills',
|
|
32
|
+
skillFile: 'SKILL.md',
|
|
33
|
+
enabled: true,
|
|
34
|
+
description: 'Cursor AI IDE',
|
|
35
|
+
website: 'https://cursor.com'
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
'gemini-cli': {
|
|
39
|
+
name: 'Gemini CLI',
|
|
40
|
+
id: 'gemini-cli',
|
|
41
|
+
globalPath: () => join(getHomeDir(), '.gemini', 'skills'),
|
|
42
|
+
projectPath: '.gemini/skills',
|
|
43
|
+
skillFile: 'SKILL.md',
|
|
44
|
+
enabled: true,
|
|
45
|
+
description: 'Google Gemini CLI',
|
|
46
|
+
website: 'https://geminicli.com'
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
'opencode': {
|
|
50
|
+
name: 'OpenCode',
|
|
51
|
+
id: 'opencode',
|
|
52
|
+
globalPath: () => join(getHomeDir(), '.config', 'opencode', 'skill'),
|
|
53
|
+
projectPath: '.opencode/skill',
|
|
54
|
+
skillFile: 'SKILL.md',
|
|
55
|
+
enabled: true,
|
|
56
|
+
description: 'OpenCode AI coding agent',
|
|
57
|
+
website: 'https://opencode.ai'
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
'codex-cli': {
|
|
61
|
+
name: 'Codex CLI',
|
|
62
|
+
id: 'codex-cli',
|
|
63
|
+
globalPath: () => join(getHomeDir(), '.codex', 'skills'),
|
|
64
|
+
projectPath: 'skills',
|
|
65
|
+
skillFile: 'SKILL.md',
|
|
66
|
+
enabled: true,
|
|
67
|
+
description: 'OpenAI Codex CLI',
|
|
68
|
+
website: 'https://openai.com/codex',
|
|
69
|
+
// Codex also uses AGENTS.md for global instructions
|
|
70
|
+
supportsAgentsMd: true,
|
|
71
|
+
agentsMdGlobalPath: () => join(getHomeDir(), '.codex', 'AGENTS.md'),
|
|
72
|
+
agentsMdProjectPath: 'AGENTS.md'
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
'antigravity': {
|
|
76
|
+
name: 'Antigravity',
|
|
77
|
+
id: 'antigravity',
|
|
78
|
+
globalPath: () => join(getHomeDir(), '.gemini', 'antigravity', 'skills'),
|
|
79
|
+
projectPath: '.agent/skills',
|
|
80
|
+
skillFile: 'SKILL.md',
|
|
81
|
+
enabled: true,
|
|
82
|
+
description: 'Antigravity agentic system',
|
|
83
|
+
website: 'https://github.com/vuralserhat86/antigravity-agentic-skills'
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Get all provider IDs
|
|
89
|
+
*/
|
|
90
|
+
export function getProviderIds() {
|
|
91
|
+
return Object.keys(providers);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get provider by ID
|
|
96
|
+
*/
|
|
97
|
+
export function getProvider(id) {
|
|
98
|
+
return providers[id];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Get all enabled providers
|
|
103
|
+
*/
|
|
104
|
+
export function getEnabledProviders(config = {}) {
|
|
105
|
+
const enabledProviders = config.enabledProviders || getProviderIds();
|
|
106
|
+
return enabledProviders.map(id => providers[id]).filter(Boolean);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get global skill path for a provider and skill name
|
|
111
|
+
*/
|
|
112
|
+
export function getGlobalSkillPath(providerId, skillName) {
|
|
113
|
+
const provider = providers[providerId];
|
|
114
|
+
if (!provider) return null;
|
|
115
|
+
return join(provider.globalPath(), skillName);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get project skill path for a provider and skill name
|
|
120
|
+
*/
|
|
121
|
+
export function getProjectSkillPath(providerId, skillName, projectRoot = process.cwd()) {
|
|
122
|
+
const provider = providers[providerId];
|
|
123
|
+
if (!provider) return null;
|
|
124
|
+
return join(projectRoot, provider.projectPath, skillName);
|
|
125
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import { homedir, platform } from 'os';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* MCP Server provider definitions
|
|
6
|
+
* Each provider has different config file locations and formats
|
|
7
|
+
*/
|
|
8
|
+
export const mcpProviders = {
|
|
9
|
+
'claude-code': {
|
|
10
|
+
name: 'Claude Code',
|
|
11
|
+
id: 'claude-code',
|
|
12
|
+
globalPath: () => join(homedir(), '.claude.json'),
|
|
13
|
+
projectPath: '.mcp.json',
|
|
14
|
+
configKey: 'mcpServers',
|
|
15
|
+
format: 'json',
|
|
16
|
+
enabled: true,
|
|
17
|
+
description: 'Anthropic Claude Code CLI'
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
'claude-desktop': {
|
|
21
|
+
name: 'Claude Desktop',
|
|
22
|
+
id: 'claude-desktop',
|
|
23
|
+
globalPath: () => {
|
|
24
|
+
if (platform() === 'win32') {
|
|
25
|
+
return join(process.env.APPDATA || '', 'Claude', 'claude_desktop_config.json');
|
|
26
|
+
}
|
|
27
|
+
return join(homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
28
|
+
},
|
|
29
|
+
projectPath: null, // No project-level config
|
|
30
|
+
configKey: 'mcpServers',
|
|
31
|
+
format: 'json',
|
|
32
|
+
enabled: true,
|
|
33
|
+
description: 'Claude Desktop App'
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
'cursor': {
|
|
37
|
+
name: 'Cursor',
|
|
38
|
+
id: 'cursor',
|
|
39
|
+
globalPath: () => join(homedir(), '.cursor', 'mcp.json'),
|
|
40
|
+
projectPath: '.cursor/mcp.json',
|
|
41
|
+
configKey: 'mcpServers',
|
|
42
|
+
format: 'json',
|
|
43
|
+
enabled: true,
|
|
44
|
+
description: 'Cursor AI IDE'
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
'windsurf': {
|
|
48
|
+
name: 'Windsurf',
|
|
49
|
+
id: 'windsurf',
|
|
50
|
+
globalPath: () => join(homedir(), '.codeium', 'windsurf', 'mcp_config.json'),
|
|
51
|
+
projectPath: null,
|
|
52
|
+
configKey: 'mcpServers',
|
|
53
|
+
format: 'json',
|
|
54
|
+
enabled: true,
|
|
55
|
+
description: 'Windsurf IDE by Codeium'
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
'vscode': {
|
|
59
|
+
name: 'VS Code',
|
|
60
|
+
id: 'vscode',
|
|
61
|
+
globalPath: () => {
|
|
62
|
+
// VS Code stores in different locations per OS
|
|
63
|
+
if (platform() === 'win32') {
|
|
64
|
+
return join(process.env.APPDATA || '', 'Code', 'User', 'settings.json');
|
|
65
|
+
} else if (platform() === 'darwin') {
|
|
66
|
+
return join(homedir(), 'Library', 'Application Support', 'Code', 'User', 'settings.json');
|
|
67
|
+
}
|
|
68
|
+
return join(homedir(), '.config', 'Code', 'User', 'settings.json');
|
|
69
|
+
},
|
|
70
|
+
projectPath: '.vscode/mcp.json',
|
|
71
|
+
configKey: 'mcpServers', // For project config; global uses mcp.servers in settings
|
|
72
|
+
globalConfigKey: 'mcp.servers', // Different key for global settings.json
|
|
73
|
+
format: 'json',
|
|
74
|
+
enabled: true,
|
|
75
|
+
description: 'Visual Studio Code with GitHub Copilot'
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
'cline': {
|
|
79
|
+
name: 'Cline',
|
|
80
|
+
id: 'cline',
|
|
81
|
+
globalPath: () => {
|
|
82
|
+
// Cline stores in VS Code extension storage
|
|
83
|
+
if (platform() === 'win32') {
|
|
84
|
+
return join(process.env.APPDATA || '', 'Code', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json');
|
|
85
|
+
} else if (platform() === 'darwin') {
|
|
86
|
+
return join(homedir(), 'Library', 'Application Support', 'Code', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json');
|
|
87
|
+
}
|
|
88
|
+
return join(homedir(), '.config', 'Code', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json');
|
|
89
|
+
},
|
|
90
|
+
projectPath: null,
|
|
91
|
+
configKey: 'mcpServers',
|
|
92
|
+
format: 'json',
|
|
93
|
+
enabled: true,
|
|
94
|
+
description: 'Cline VS Code Extension'
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
'zed': {
|
|
98
|
+
name: 'Zed',
|
|
99
|
+
id: 'zed',
|
|
100
|
+
globalPath: () => {
|
|
101
|
+
if (platform() === 'darwin') {
|
|
102
|
+
return join(homedir(), '.config', 'zed', 'settings.json');
|
|
103
|
+
}
|
|
104
|
+
return join(homedir(), '.config', 'zed', 'settings.json');
|
|
105
|
+
},
|
|
106
|
+
projectPath: null,
|
|
107
|
+
configKey: 'context_servers', // Zed uses different key!
|
|
108
|
+
format: 'json',
|
|
109
|
+
enabled: true,
|
|
110
|
+
description: 'Zed Editor'
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
'roo-code': {
|
|
114
|
+
name: 'Roo Code',
|
|
115
|
+
id: 'roo-code',
|
|
116
|
+
globalPath: () => {
|
|
117
|
+
if (platform() === 'win32') {
|
|
118
|
+
return join(process.env.APPDATA || '', 'Code', 'User', 'globalStorage', 'rooveterinaryinc.roo-cline', 'settings', 'mcp_settings.json');
|
|
119
|
+
} else if (platform() === 'darwin') {
|
|
120
|
+
return join(homedir(), 'Library', 'Application Support', 'Code', 'User', 'globalStorage', 'rooveterinaryinc.roo-cline', 'settings', 'mcp_settings.json');
|
|
121
|
+
}
|
|
122
|
+
return join(homedir(), '.config', 'Code', 'User', 'globalStorage', 'rooveterinaryinc.roo-cline', 'settings', 'mcp_settings.json');
|
|
123
|
+
},
|
|
124
|
+
projectPath: '.roo/mcp.json',
|
|
125
|
+
configKey: 'mcpServers',
|
|
126
|
+
format: 'json',
|
|
127
|
+
enabled: true,
|
|
128
|
+
description: 'Roo Code VS Code Extension'
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
'antigravity': {
|
|
132
|
+
name: 'Antigravity',
|
|
133
|
+
id: 'antigravity',
|
|
134
|
+
globalPath: () => {
|
|
135
|
+
// Antigravity stores MCP config in its own location
|
|
136
|
+
// Access via MCP Store > Manage MCP Servers > View raw config
|
|
137
|
+
if (platform() === 'darwin') {
|
|
138
|
+
return join(homedir(), '.antigravity', 'mcp_config.json');
|
|
139
|
+
}
|
|
140
|
+
return join(homedir(), '.antigravity', 'mcp_config.json');
|
|
141
|
+
},
|
|
142
|
+
projectPath: null,
|
|
143
|
+
configKey: 'mcpServers',
|
|
144
|
+
format: 'json',
|
|
145
|
+
enabled: true,
|
|
146
|
+
description: 'Antigravity Editor',
|
|
147
|
+
note: 'Config accessible via MCP Store > Manage MCP Servers > View raw config'
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Get all MCP provider IDs
|
|
153
|
+
*/
|
|
154
|
+
export function getMcpProviderIds() {
|
|
155
|
+
return Object.keys(mcpProviders);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Get MCP provider by ID
|
|
160
|
+
*/
|
|
161
|
+
export function getMcpProvider(id) {
|
|
162
|
+
return mcpProviders[id];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Get all enabled MCP providers
|
|
167
|
+
*/
|
|
168
|
+
export function getEnabledMcpProviders(config = {}) {
|
|
169
|
+
const enabledProviders = config.enabledMcpProviders || getMcpProviderIds();
|
|
170
|
+
return enabledProviders.map(id => mcpProviders[id]).filter(Boolean);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Standard MCP server config format (most providers use this)
|
|
175
|
+
*/
|
|
176
|
+
export function createMcpServerConfig(name, command, args = [], env = {}) {
|
|
177
|
+
return {
|
|
178
|
+
[name]: {
|
|
179
|
+
command,
|
|
180
|
+
args,
|
|
181
|
+
...(Object.keys(env).length > 0 ? { env } : {})
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|