a11y-devkit-deploy 0.7.2 → 0.8.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/config/a11y.json +90 -8
- package/package.json +1 -1
- package/src/cli.js +327 -55
- package/src/installers/git-mcp.js +209 -0
- package/src/installers/mcp.js +8 -1
- package/src/paths.js +42 -6
- package/src/prompts/git-mcp.js +140 -0
package/config/a11y.json
CHANGED
|
@@ -3,31 +3,45 @@
|
|
|
3
3
|
"readmeTemplate": "deploy-README.md",
|
|
4
4
|
"skills": [
|
|
5
5
|
{
|
|
6
|
-
"name": "
|
|
6
|
+
"name": "A11y Base Web Skill",
|
|
7
|
+
"npmName": "a11y-base-web-skill",
|
|
8
|
+
"npmURL": "https://www.npmjs.com/package/a11y-base-web-skill",
|
|
7
9
|
"description": "Core accessibility testing utilities"
|
|
8
10
|
},
|
|
9
11
|
{
|
|
10
|
-
"name": "
|
|
12
|
+
"name": "A11y Issue Writer Skill",
|
|
13
|
+
"npmName": "a11y-issue-writer-skill",
|
|
14
|
+
"npmURL": "https://www.npmjs.com/package/a11y-issue-writer-skill",
|
|
11
15
|
"description": "Document accessibility issues"
|
|
12
16
|
},
|
|
13
17
|
{
|
|
14
|
-
"name": "
|
|
18
|
+
"name": "A11y Tester Skill",
|
|
19
|
+
"npmName": "a11y-tester-skill",
|
|
20
|
+
"npmURL": "https://www.npmjs.com/package/a11y-tester-skill",
|
|
15
21
|
"description": "Run accessibility tests"
|
|
16
22
|
},
|
|
17
23
|
{
|
|
18
|
-
"name": "
|
|
24
|
+
"name": "A11y Remediator Skill",
|
|
25
|
+
"npmName": "a11y-remediator-skill",
|
|
26
|
+
"npmURL": "https://www.npmjs.com/package/a11y-remediator-skill",
|
|
19
27
|
"description": "Fix accessibility issues"
|
|
20
28
|
},
|
|
21
29
|
{
|
|
22
|
-
"name": "
|
|
30
|
+
"name": "A11y Validator Skill",
|
|
31
|
+
"npmName": "a11y-validator-skill",
|
|
32
|
+
"npmURL": "https://www.npmjs.com/package/a11y-validator-skill",
|
|
23
33
|
"description": "Validate accessibility compliance"
|
|
24
34
|
},
|
|
25
35
|
{
|
|
26
|
-
"name": "
|
|
36
|
+
"name": "Web Standards Skill",
|
|
37
|
+
"npmName": "web-standards-skill",
|
|
38
|
+
"npmURL": "https://www.npmjs.com/package/web-standards-skill",
|
|
27
39
|
"description": "Web standards reference"
|
|
28
40
|
},
|
|
29
41
|
{
|
|
30
|
-
"name": "
|
|
42
|
+
"name": "A11y Audit Fix Agent Orchestrator Skill",
|
|
43
|
+
"npmName": "a11y-audit-fix-agent-orchestrator-skill",
|
|
44
|
+
"npmURL": "https://www.npmjs.com/package/a11y-audit-fix-agent-orchestrator-skill",
|
|
31
45
|
"description": "Orchestrate accessibility audits"
|
|
32
46
|
}
|
|
33
47
|
],
|
|
@@ -58,7 +72,17 @@
|
|
|
58
72
|
"displayName": "VSCode",
|
|
59
73
|
"mcpServerKey": "servers",
|
|
60
74
|
"skillsFolder": ".github/skills",
|
|
61
|
-
"mcpConfigFile": ".github/mcp.json"
|
|
75
|
+
"mcpConfigFile": ".github/mcp.json",
|
|
76
|
+
"globalPaths": {
|
|
77
|
+
"Win": {
|
|
78
|
+
"mcpConfigFile": "Code/User/mcp.json",
|
|
79
|
+
"skillsFolder": "Code/User/skills"
|
|
80
|
+
},
|
|
81
|
+
"macOS": {
|
|
82
|
+
"mcpConfigFile": "Code/User/mcp.json",
|
|
83
|
+
"skillsFolder": "Code/User/skills"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
62
86
|
},
|
|
63
87
|
{
|
|
64
88
|
"id": "windsurf",
|
|
@@ -106,5 +130,63 @@
|
|
|
106
130
|
"command": "npx",
|
|
107
131
|
"args": ["-y", "arc-issues-mcp"]
|
|
108
132
|
}
|
|
133
|
+
],
|
|
134
|
+
"profiles": [
|
|
135
|
+
{
|
|
136
|
+
"id": "developer",
|
|
137
|
+
"displayName": "Developer",
|
|
138
|
+
"description": "For developers building accessible applications",
|
|
139
|
+
"skills": ["a11y-base-web-skill"],
|
|
140
|
+
"mcpServers": ["magentaa11y"]
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"id": "tester",
|
|
144
|
+
"displayName": "Tester/QA",
|
|
145
|
+
"description": "For QA engineers testing accessibility",
|
|
146
|
+
"skills": ["a11y-tester-skill"],
|
|
147
|
+
"mcpServers": ["arc-issues"]
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"id": "a11y-sme",
|
|
151
|
+
"displayName": "Accessibility SME",
|
|
152
|
+
"description": "For accessibility subject matter experts",
|
|
153
|
+
"skills": ["web-standards-skill"],
|
|
154
|
+
"mcpServers": ["magentaa11y"]
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"id": "product",
|
|
158
|
+
"displayName": "Product",
|
|
159
|
+
"description": "For product managers and owners",
|
|
160
|
+
"skills": ["a11y-base-web-skill"],
|
|
161
|
+
"mcpServers": ["magentaa11y"]
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"id": "design",
|
|
165
|
+
"displayName": "Design",
|
|
166
|
+
"description": "For designers creating accessible experiences",
|
|
167
|
+
"skills": ["web-standards-skill"],
|
|
168
|
+
"mcpServers": ["aria"]
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"id": "hybrid",
|
|
172
|
+
"displayName": "Hybrid (I want all the things)",
|
|
173
|
+
"description": "All skills and MCP servers for comprehensive accessibility support",
|
|
174
|
+
"skills": [
|
|
175
|
+
"a11y-base-web-skill",
|
|
176
|
+
"a11y-issue-writer-skill",
|
|
177
|
+
"a11y-tester-skill",
|
|
178
|
+
"a11y-remediator-skill",
|
|
179
|
+
"a11y-validator-skill",
|
|
180
|
+
"web-standards-skill",
|
|
181
|
+
"a11y-audit-fix-agent-orchestrator-skill"
|
|
182
|
+
],
|
|
183
|
+
"mcpServers": [
|
|
184
|
+
"wcag",
|
|
185
|
+
"aria",
|
|
186
|
+
"magentaa11y",
|
|
187
|
+
"a11y-personas",
|
|
188
|
+
"arc-issues"
|
|
189
|
+
]
|
|
190
|
+
}
|
|
109
191
|
]
|
|
110
192
|
}
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -4,9 +4,11 @@ import { fileURLToPath } from "url";
|
|
|
4
4
|
import prompts from "prompts";
|
|
5
5
|
|
|
6
6
|
import { header, info, warn, success, startSpinner, formatPath } from "./ui.js";
|
|
7
|
-
import { getPlatform, getIdePaths, getTempDir } from "./paths.js";
|
|
7
|
+
import { getPlatform, getIdePaths, getTempDir, getMcpRepoDir } from "./paths.js";
|
|
8
8
|
import { installSkillsFromNpm, cleanupTemp } from "./installers/skills.js";
|
|
9
9
|
import { installMcpConfig } from "./installers/mcp.js";
|
|
10
|
+
import { getGitMcpPrompts, parseArgsString } from "./prompts/git-mcp.js";
|
|
11
|
+
import { installGitMcp } from "./installers/git-mcp.js";
|
|
10
12
|
|
|
11
13
|
const __filename = fileURLToPath(import.meta.url);
|
|
12
14
|
const __dirname = path.dirname(__filename);
|
|
@@ -32,6 +34,7 @@ function parseArgs(argv) {
|
|
|
32
34
|
: args.has("--local")
|
|
33
35
|
? "local"
|
|
34
36
|
: null,
|
|
37
|
+
gitMcp: args.has("--git-mcp"),
|
|
35
38
|
};
|
|
36
39
|
}
|
|
37
40
|
|
|
@@ -52,26 +55,115 @@ async function run() {
|
|
|
52
55
|
|
|
53
56
|
header(
|
|
54
57
|
`A11y Devkit Deploy v${pkg.version}`,
|
|
55
|
-
|
|
58
|
+
args.gitMcp
|
|
59
|
+
? "Install MCP server from Git repository"
|
|
60
|
+
: "Install skills + MCP servers across IDEs",
|
|
56
61
|
);
|
|
57
62
|
info(`Detected OS: ${formatOs(platformInfo)}`);
|
|
58
63
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
? "No description"
|
|
65
|
-
: skill.description || "No description";
|
|
66
|
-
console.log(`- ${name}: ${description}`);
|
|
67
|
-
});
|
|
64
|
+
// Branch to Git MCP installation flow
|
|
65
|
+
if (args.gitMcp) {
|
|
66
|
+
await runGitMcpInstallation(projectRoot, platformInfo, config, idePaths, args);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
68
69
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
// Prompt for profile selection
|
|
71
|
+
let selectedProfile = null;
|
|
72
|
+
let skillsToInstall = [];
|
|
73
|
+
let mcpServersToInstall = [];
|
|
74
|
+
let profileConfirmed = false;
|
|
75
|
+
|
|
76
|
+
while (!profileConfirmed) {
|
|
77
|
+
if (!args.autoYes && config.profiles) {
|
|
78
|
+
const profileChoices = config.profiles.map((profile) => ({
|
|
79
|
+
title: profile.displayName,
|
|
80
|
+
description: profile.description,
|
|
81
|
+
value: profile.id,
|
|
82
|
+
}));
|
|
83
|
+
|
|
84
|
+
const profileResponse = await prompts(
|
|
85
|
+
{
|
|
86
|
+
type: "select",
|
|
87
|
+
name: "profile",
|
|
88
|
+
message: "Select your profile:",
|
|
89
|
+
choices: profileChoices,
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
onCancel: () => {
|
|
93
|
+
warn("Setup cancelled.");
|
|
94
|
+
process.exit(0);
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
selectedProfile = config.profiles.find((p) => p.id === profileResponse.profile);
|
|
100
|
+
|
|
101
|
+
if (selectedProfile) {
|
|
102
|
+
// Filter skills based on profile
|
|
103
|
+
skillsToInstall = config.skills.filter((skill) => {
|
|
104
|
+
const skillNpmName = typeof skill === "string" ? skill : skill.npmName;
|
|
105
|
+
return selectedProfile.skills.includes(skillNpmName);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Filter MCP servers based on profile
|
|
109
|
+
mcpServersToInstall = config.mcpServers.filter((server) =>
|
|
110
|
+
selectedProfile.mcpServers.includes(server.name),
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
console.log(`\n${selectedProfile.displayName} profile selected`);
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
// If no profiles or auto-yes, use all skills and servers
|
|
117
|
+
skillsToInstall = config.skills;
|
|
118
|
+
mcpServersToInstall = config.mcpServers;
|
|
119
|
+
profileConfirmed = true; // Skip confirmation for auto-yes
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Show what will be installed
|
|
123
|
+
if (!args.autoYes) {
|
|
124
|
+
console.log("\nSkills to install:");
|
|
125
|
+
skillsToInstall.forEach((skill) => {
|
|
126
|
+
const name = typeof skill === "string" ? skill : skill.name;
|
|
127
|
+
const description =
|
|
128
|
+
typeof skill === "string"
|
|
129
|
+
? "No description"
|
|
130
|
+
: skill.description || "No description";
|
|
131
|
+
console.log(` • ${name}`);
|
|
132
|
+
console.log(` ${description}`);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
console.log("\nMCP Servers to install:");
|
|
136
|
+
mcpServersToInstall.forEach((server) => {
|
|
137
|
+
const description = server.description || "No description";
|
|
138
|
+
console.log(` • ${server.name}`);
|
|
139
|
+
console.log(` ${description}`);
|
|
140
|
+
});
|
|
141
|
+
console.log("");
|
|
142
|
+
|
|
143
|
+
// Confirmation prompt
|
|
144
|
+
const confirmResponse = await prompts(
|
|
145
|
+
{
|
|
146
|
+
type: "confirm",
|
|
147
|
+
name: "continue",
|
|
148
|
+
message: "Continue with this configuration?",
|
|
149
|
+
initial: true,
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
onCancel: () => {
|
|
153
|
+
warn("Setup cancelled.");
|
|
154
|
+
process.exit(0);
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
if (confirmResponse.continue) {
|
|
160
|
+
profileConfirmed = true;
|
|
161
|
+
} else {
|
|
162
|
+
// User wants to go back - loop will restart profile selection
|
|
163
|
+
console.log("");
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
75
167
|
|
|
76
168
|
const ideChoices = config.ides.map((ide) => ({
|
|
77
169
|
title: ide.displayName,
|
|
@@ -81,7 +173,6 @@ async function run() {
|
|
|
81
173
|
let scope = args.scope;
|
|
82
174
|
let mcpScope = null;
|
|
83
175
|
let ideSelection = config.ides.map((ide) => ide.id);
|
|
84
|
-
let installSkills = true;
|
|
85
176
|
|
|
86
177
|
if (!args.autoYes) {
|
|
87
178
|
const response = await prompts(
|
|
@@ -89,7 +180,7 @@ async function run() {
|
|
|
89
180
|
{
|
|
90
181
|
type: scope ? null : "select",
|
|
91
182
|
name: "scope",
|
|
92
|
-
message: "Install skills
|
|
183
|
+
message: "Install skills locally or globally?",
|
|
93
184
|
choices: [
|
|
94
185
|
{
|
|
95
186
|
title: `Local to this project (${formatPath(projectRoot)})`,
|
|
@@ -121,18 +212,10 @@ async function run() {
|
|
|
121
212
|
{
|
|
122
213
|
type: "multiselect",
|
|
123
214
|
name: "ides",
|
|
124
|
-
message: "Configure
|
|
215
|
+
message: "Configure for which IDEs?",
|
|
125
216
|
choices: ideChoices,
|
|
126
217
|
initial: ideChoices.map((_, index) => index),
|
|
127
218
|
},
|
|
128
|
-
{
|
|
129
|
-
type: "toggle",
|
|
130
|
-
name: "installSkills",
|
|
131
|
-
message: "Install skills into IDE skills folders?",
|
|
132
|
-
active: "yes",
|
|
133
|
-
inactive: "no",
|
|
134
|
-
initial: true,
|
|
135
|
-
},
|
|
136
219
|
],
|
|
137
220
|
{
|
|
138
221
|
onCancel: () => {
|
|
@@ -145,7 +228,6 @@ async function run() {
|
|
|
145
228
|
scope = scope || response.scope;
|
|
146
229
|
mcpScope = response.mcpScope || "local";
|
|
147
230
|
ideSelection = response.ides || ideSelection;
|
|
148
|
-
installSkills = response.installSkills;
|
|
149
231
|
}
|
|
150
232
|
|
|
151
233
|
if (!scope) {
|
|
@@ -166,33 +248,29 @@ async function run() {
|
|
|
166
248
|
// Create temp directory for npm install
|
|
167
249
|
const tempDir = path.join(getTempDir(), `.a11y-devkit-${Date.now()}`);
|
|
168
250
|
|
|
169
|
-
|
|
170
|
-
const skillsSpinner = startSpinner("Installing skills from npm...");
|
|
251
|
+
const skillsSpinner = startSpinner("Installing skills from npm...");
|
|
171
252
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
253
|
+
try {
|
|
254
|
+
const skillTargets =
|
|
255
|
+
scope === "local"
|
|
256
|
+
? ideSelection.map((ide) => idePaths[ide].localSkillsDir)
|
|
257
|
+
: ideSelection.map((ide) => idePaths[ide].skillsDir);
|
|
177
258
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
} else {
|
|
195
|
-
warn("Skipping skills install to IDE folders.");
|
|
259
|
+
const skillNames = skillsToInstall.map((skill) =>
|
|
260
|
+
typeof skill === "string" ? skill : skill.npmName,
|
|
261
|
+
);
|
|
262
|
+
const result = await installSkillsFromNpm(
|
|
263
|
+
skillNames,
|
|
264
|
+
skillTargets,
|
|
265
|
+
tempDir,
|
|
266
|
+
config.skillsFolder,
|
|
267
|
+
config.readmeTemplate,
|
|
268
|
+
);
|
|
269
|
+
skillsSpinner.succeed(
|
|
270
|
+
`${result.installed} skills installed to ${skillTargets.length} IDE location(s).`,
|
|
271
|
+
);
|
|
272
|
+
} catch (error) {
|
|
273
|
+
skillsSpinner.fail(`Failed to install skills: ${error.message}`);
|
|
196
274
|
}
|
|
197
275
|
|
|
198
276
|
// Configure MCP servers using npx (no local installation needed!)
|
|
@@ -206,7 +284,7 @@ async function run() {
|
|
|
206
284
|
const ide = ideSelection[i];
|
|
207
285
|
await installMcpConfig(
|
|
208
286
|
mcpConfigPaths[i],
|
|
209
|
-
|
|
287
|
+
mcpServersToInstall,
|
|
210
288
|
idePaths[ide].mcpServerKey,
|
|
211
289
|
);
|
|
212
290
|
}
|
|
@@ -239,4 +317,198 @@ async function run() {
|
|
|
239
317
|
info("Documentation: https://github.com/joe-watkins/a11y-devkit#readme");
|
|
240
318
|
}
|
|
241
319
|
|
|
320
|
+
async function runGitMcpInstallation(projectRoot, platformInfo, config, idePaths, args) {
|
|
321
|
+
// Check if --yes flag is used with --git-mcp
|
|
322
|
+
if (args.autoYes) {
|
|
323
|
+
warn("--yes flag not supported for Git MCP installation");
|
|
324
|
+
info("Interactive prompts required for Git MCP configuration");
|
|
325
|
+
process.exit(1);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
console.log("\n");
|
|
329
|
+
info("Installing MCP server from Git repository");
|
|
330
|
+
console.log("");
|
|
331
|
+
|
|
332
|
+
// Collect Git MCP information
|
|
333
|
+
const gitMcpPrompts = getGitMcpPrompts();
|
|
334
|
+
const mcpInfo = await prompts(gitMcpPrompts, {
|
|
335
|
+
onCancel: () => {
|
|
336
|
+
warn("Git MCP installation cancelled.");
|
|
337
|
+
process.exit(0);
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// Parse args string into array
|
|
342
|
+
const argsArray = parseArgsString(mcpInfo.args);
|
|
343
|
+
|
|
344
|
+
// Prompt for Repo Clone Scope (where to clone the Git repository)
|
|
345
|
+
const repoScopeResponse = await prompts(
|
|
346
|
+
{
|
|
347
|
+
type: "select",
|
|
348
|
+
name: "repoScope",
|
|
349
|
+
message: "Where to clone the Git repository?",
|
|
350
|
+
choices: [
|
|
351
|
+
{
|
|
352
|
+
title: `Local to this project (${formatPath(projectRoot)})`,
|
|
353
|
+
value: "local",
|
|
354
|
+
description: "Clone to .a11y-devkit/mcp-repos/ in project",
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
title: "Global for this user",
|
|
358
|
+
value: "global",
|
|
359
|
+
description: "Clone to user-level app support directory",
|
|
360
|
+
},
|
|
361
|
+
],
|
|
362
|
+
initial: 0,
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
onCancel: () => {
|
|
366
|
+
warn("Git MCP installation cancelled.");
|
|
367
|
+
process.exit(0);
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
// Prompt for MCP Config Scope (where to write MCP configurations)
|
|
373
|
+
const mcpScopeResponse = await prompts(
|
|
374
|
+
{
|
|
375
|
+
type: "select",
|
|
376
|
+
name: "mcpScope",
|
|
377
|
+
message: "Where to write MCP configurations?",
|
|
378
|
+
choices: [
|
|
379
|
+
{
|
|
380
|
+
title: `Local to this project (${formatPath(projectRoot)})`,
|
|
381
|
+
value: "local",
|
|
382
|
+
description: "Write to project-level IDE config folders",
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
title: "Global for this user",
|
|
386
|
+
value: "global",
|
|
387
|
+
description: "Write to user-level IDE config folders",
|
|
388
|
+
},
|
|
389
|
+
],
|
|
390
|
+
initial: 0,
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
onCancel: () => {
|
|
394
|
+
warn("Git MCP installation cancelled.");
|
|
395
|
+
process.exit(0);
|
|
396
|
+
},
|
|
397
|
+
},
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
// Prompt for IDE selection
|
|
401
|
+
const ideChoices = config.ides.map((ide) => ({
|
|
402
|
+
title: ide.displayName,
|
|
403
|
+
value: ide.id,
|
|
404
|
+
}));
|
|
405
|
+
|
|
406
|
+
const ideResponse = await prompts(
|
|
407
|
+
{
|
|
408
|
+
type: "multiselect",
|
|
409
|
+
name: "ides",
|
|
410
|
+
message: "Configure MCP for which IDEs?",
|
|
411
|
+
choices: ideChoices,
|
|
412
|
+
initial: ideChoices.map((_, index) => index),
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
onCancel: () => {
|
|
416
|
+
warn("Git MCP installation cancelled.");
|
|
417
|
+
process.exit(0);
|
|
418
|
+
},
|
|
419
|
+
},
|
|
420
|
+
);
|
|
421
|
+
|
|
422
|
+
const ideSelection = ideResponse.ides || [];
|
|
423
|
+
|
|
424
|
+
if (!ideSelection.length) {
|
|
425
|
+
warn("No IDEs selected. MCP installation requires at least one IDE.");
|
|
426
|
+
process.exit(1);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const repoScope = repoScopeResponse.repoScope;
|
|
430
|
+
const mcpScope = mcpScopeResponse.mcpScope;
|
|
431
|
+
|
|
432
|
+
info(`Repository clone scope: ${repoScope === "local" ? "Local" : "Global"}`);
|
|
433
|
+
info(`MCP config scope: ${mcpScope === "local" ? "Local" : "Global"}`);
|
|
434
|
+
|
|
435
|
+
// Install Git MCP
|
|
436
|
+
const gitSpinner = startSpinner("Cloning Git repository...");
|
|
437
|
+
|
|
438
|
+
let mcpServer;
|
|
439
|
+
try {
|
|
440
|
+
mcpServer = await installGitMcp(
|
|
441
|
+
{
|
|
442
|
+
name: mcpInfo.name,
|
|
443
|
+
repoUrl: mcpInfo.repoUrl,
|
|
444
|
+
type: mcpInfo.type,
|
|
445
|
+
command: mcpInfo.command,
|
|
446
|
+
args: argsArray,
|
|
447
|
+
buildCommand: mcpInfo.buildCommand,
|
|
448
|
+
},
|
|
449
|
+
repoScope,
|
|
450
|
+
projectRoot,
|
|
451
|
+
platformInfo,
|
|
452
|
+
getMcpRepoDir,
|
|
453
|
+
);
|
|
454
|
+
gitSpinner.succeed(`Repository cloned to ${mcpServer.repoPath}`);
|
|
455
|
+
} catch (error) {
|
|
456
|
+
gitSpinner.fail(`Failed to install Git MCP: ${error.message}`);
|
|
457
|
+
process.exit(1);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Install MCP configurations to selected IDEs
|
|
461
|
+
const mcpConfigSpinner = startSpinner("Updating MCP configurations...");
|
|
462
|
+
|
|
463
|
+
const mcpConfigPaths =
|
|
464
|
+
mcpScope === "local"
|
|
465
|
+
? ideSelection.map((ide) => idePaths[ide].localMcpConfig)
|
|
466
|
+
: ideSelection.map((ide) => idePaths[ide].mcpConfig);
|
|
467
|
+
|
|
468
|
+
// Construct the MCP server configuration with absolute path
|
|
469
|
+
const mcpServerConfig = {
|
|
470
|
+
name: mcpServer.name,
|
|
471
|
+
type: mcpServer.type,
|
|
472
|
+
command: mcpServer.command,
|
|
473
|
+
args: mcpServer.args.length > 0 ? mcpServer.args : undefined,
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
// If args are provided, prepend the repo path to the first argument
|
|
477
|
+
// Otherwise, use the repo path as the only argument
|
|
478
|
+
if (mcpServerConfig.args && mcpServerConfig.args.length > 0) {
|
|
479
|
+
// Assume first arg is a relative path within the repo
|
|
480
|
+
mcpServerConfig.args = [
|
|
481
|
+
path.join(mcpServer.repoPath, mcpServerConfig.args[0]),
|
|
482
|
+
...mcpServerConfig.args.slice(1),
|
|
483
|
+
];
|
|
484
|
+
} else {
|
|
485
|
+
// No args provided - this might need to be handled differently
|
|
486
|
+
// For now, don't add any args
|
|
487
|
+
delete mcpServerConfig.args;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
for (let i = 0; i < ideSelection.length; i++) {
|
|
491
|
+
const ide = ideSelection[i];
|
|
492
|
+
await installMcpConfig(
|
|
493
|
+
mcpConfigPaths[i],
|
|
494
|
+
[mcpServerConfig],
|
|
495
|
+
idePaths[ide].mcpServerKey,
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
mcpConfigSpinner.succeed(
|
|
500
|
+
`MCP configs updated for ${ideSelection.length} IDE(s) (${mcpScope} scope).`,
|
|
501
|
+
);
|
|
502
|
+
|
|
503
|
+
// Display success message
|
|
504
|
+
success("Git MCP installation complete!");
|
|
505
|
+
info(`Repository location: ${mcpServer.repoPath}`);
|
|
506
|
+
info(`MCP server '${mcpServer.name}' configured in ${ideSelection.length} IDE(s)`);
|
|
507
|
+
console.log("");
|
|
508
|
+
success("Next Steps:");
|
|
509
|
+
info("Restart your IDE to load the new MCP server");
|
|
510
|
+
info(`Repository cloned to: ${mcpServer.repoPath}`);
|
|
511
|
+
info("You can manually edit the MCP configuration files if needed");
|
|
512
|
+
}
|
|
513
|
+
|
|
242
514
|
export { run };
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git repository operations and MCP installation orchestration
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from "fs/promises";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import { spawn } from "child_process";
|
|
8
|
+
import prompts from "prompts";
|
|
9
|
+
import { warn, error as errorMsg } from "../ui.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Helper function to check if a path exists
|
|
13
|
+
* @param {string} target - Path to check
|
|
14
|
+
* @returns {Promise<boolean>}
|
|
15
|
+
*/
|
|
16
|
+
async function pathExists(target) {
|
|
17
|
+
try {
|
|
18
|
+
await fs.access(target);
|
|
19
|
+
return true;
|
|
20
|
+
} catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Runs a command using spawn
|
|
27
|
+
* @param {string} command - Command to run
|
|
28
|
+
* @param {string[]} args - Command arguments
|
|
29
|
+
* @param {object} options - Spawn options
|
|
30
|
+
* @returns {Promise<{stdout: string, stderr: string}>}
|
|
31
|
+
*/
|
|
32
|
+
function run(command, args, options = {}) {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
const child = spawn(command, args, {
|
|
35
|
+
stdio: "pipe",
|
|
36
|
+
shell: true,
|
|
37
|
+
...options,
|
|
38
|
+
});
|
|
39
|
+
let stdout = "";
|
|
40
|
+
let stderr = "";
|
|
41
|
+
|
|
42
|
+
child.stdout?.on("data", (data) => {
|
|
43
|
+
stdout += data;
|
|
44
|
+
});
|
|
45
|
+
child.stderr?.on("data", (data) => {
|
|
46
|
+
stderr += data;
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
child.on("error", reject);
|
|
50
|
+
child.on("close", (code) => {
|
|
51
|
+
if (code === 0) {
|
|
52
|
+
resolve({ stdout, stderr });
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
reject(
|
|
56
|
+
new Error(
|
|
57
|
+
`${command} ${args.join(" ")} failed with code ${code}: ${stderr}`,
|
|
58
|
+
),
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Clones a Git repository to the target directory
|
|
66
|
+
* @param {string} repoUrl - Git repository URL
|
|
67
|
+
* @param {string} targetDir - Directory to clone into
|
|
68
|
+
* @returns {Promise<{success: boolean, stdout: string, stderr: string}>}
|
|
69
|
+
*/
|
|
70
|
+
export async function cloneGitRepo(repoUrl, targetDir) {
|
|
71
|
+
try {
|
|
72
|
+
const result = await run("git", ["clone", repoUrl, targetDir]);
|
|
73
|
+
return {
|
|
74
|
+
success: true,
|
|
75
|
+
stdout: result.stdout,
|
|
76
|
+
stderr: result.stderr,
|
|
77
|
+
};
|
|
78
|
+
} catch (err) {
|
|
79
|
+
// Provide helpful error messages based on error type
|
|
80
|
+
const errorMessage = err.message.toLowerCase();
|
|
81
|
+
|
|
82
|
+
if (errorMessage.includes("authentication") || errorMessage.includes("fatal: could not read")) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
`Authentication required for ${repoUrl}. Please ensure the repository is public or configure Git credentials.`,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (errorMessage.includes("repository not found") || errorMessage.includes("not found")) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`Repository not found: ${repoUrl}. Please verify the URL is correct.`,
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (errorMessage.includes("git: command not found") || errorMessage.includes("'git' is not recognized")) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
"Git is not installed. Please install Git and try again.",
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Generic error
|
|
101
|
+
throw new Error(`Failed to clone repository: ${err.message}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Executes build command in the repository directory
|
|
107
|
+
* @param {string} repoDir - Repository directory path
|
|
108
|
+
* @param {string} buildCommand - Build command to execute
|
|
109
|
+
* @returns {Promise<{success: boolean, stdout: string, stderr: string}>}
|
|
110
|
+
*/
|
|
111
|
+
export async function buildGitRepo(repoDir, buildCommand) {
|
|
112
|
+
try {
|
|
113
|
+
// Parse build command (handle multi-command strings like "npm install && npm run build")
|
|
114
|
+
// We'll execute the entire command string via shell
|
|
115
|
+
const result = await run(buildCommand, [], { cwd: repoDir });
|
|
116
|
+
return {
|
|
117
|
+
success: true,
|
|
118
|
+
stdout: result.stdout,
|
|
119
|
+
stderr: result.stderr,
|
|
120
|
+
};
|
|
121
|
+
} catch (err) {
|
|
122
|
+
return {
|
|
123
|
+
success: false,
|
|
124
|
+
stdout: err.stdout || "",
|
|
125
|
+
stderr: err.stderr || err.message,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Removes a cloned repository directory
|
|
132
|
+
* @param {string} repoPath - Path to repository to remove
|
|
133
|
+
* @returns {Promise<void>}
|
|
134
|
+
*/
|
|
135
|
+
export async function cleanupGitRepo(repoPath) {
|
|
136
|
+
if (await pathExists(repoPath)) {
|
|
137
|
+
await fs.rm(repoPath, { recursive: true, force: true });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Main orchestrator function for Git MCP installation
|
|
143
|
+
* @param {object} mcpConfig - MCP configuration {name, repoUrl, type, command, args, buildCommand}
|
|
144
|
+
* @param {string} repoScope - Where to clone repo ('local' | 'global')
|
|
145
|
+
* @param {string} projectRoot - Project root directory
|
|
146
|
+
* @param {object} platformInfo - Platform info object
|
|
147
|
+
* @param {Function} getMcpRepoDir - Function to get MCP repo directory
|
|
148
|
+
* @returns {Promise<{name: string, type: string, command: string, args: string[], repoPath: string}>}
|
|
149
|
+
*/
|
|
150
|
+
export async function installGitMcp(
|
|
151
|
+
mcpConfig,
|
|
152
|
+
repoScope,
|
|
153
|
+
projectRoot,
|
|
154
|
+
platformInfo,
|
|
155
|
+
getMcpRepoDir,
|
|
156
|
+
) {
|
|
157
|
+
const { name, repoUrl, type, command, args, buildCommand } = mcpConfig;
|
|
158
|
+
|
|
159
|
+
// Determine repo storage location
|
|
160
|
+
const repoPath = getMcpRepoDir(repoScope, projectRoot, platformInfo, name);
|
|
161
|
+
|
|
162
|
+
// Check for existing MCP with same name
|
|
163
|
+
if (await pathExists(repoPath)) {
|
|
164
|
+
warn(`MCP server '${name}' already exists at ${repoPath}`);
|
|
165
|
+
|
|
166
|
+
const overwriteResponse = await prompts(
|
|
167
|
+
{
|
|
168
|
+
type: "confirm",
|
|
169
|
+
name: "overwrite",
|
|
170
|
+
message: "Overwrite existing installation?",
|
|
171
|
+
initial: false,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
onCancel: () => {
|
|
175
|
+
throw new Error("Installation cancelled by user");
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
if (!overwriteResponse.overwrite) {
|
|
181
|
+
throw new Error("Installation cancelled - existing MCP not overwritten");
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Remove old directory
|
|
185
|
+
await cleanupGitRepo(repoPath);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Clone repository
|
|
189
|
+
await cloneGitRepo(repoUrl, repoPath);
|
|
190
|
+
|
|
191
|
+
// Run build command (continue with warning if fails)
|
|
192
|
+
const buildResult = await buildGitRepo(repoPath, buildCommand);
|
|
193
|
+
if (!buildResult.success) {
|
|
194
|
+
warn("Build command failed but continuing with installation");
|
|
195
|
+
if (buildResult.stderr) {
|
|
196
|
+
console.error("Build error output:");
|
|
197
|
+
console.error(buildResult.stderr);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Return MCP server object for config installation
|
|
202
|
+
return {
|
|
203
|
+
name,
|
|
204
|
+
type,
|
|
205
|
+
command,
|
|
206
|
+
args,
|
|
207
|
+
repoPath,
|
|
208
|
+
};
|
|
209
|
+
}
|
package/src/installers/mcp.js
CHANGED
|
@@ -33,10 +33,17 @@ function mergeServers(existing, incoming, serverKey = "servers") {
|
|
|
33
33
|
const merged = { ...existing, [serverKey]: { ...existingServers } };
|
|
34
34
|
|
|
35
35
|
for (const server of incoming) {
|
|
36
|
-
|
|
36
|
+
const serverConfig = {
|
|
37
37
|
command: server.command,
|
|
38
38
|
args: server.args || []
|
|
39
39
|
};
|
|
40
|
+
|
|
41
|
+
// Include type if provided
|
|
42
|
+
if (server.type) {
|
|
43
|
+
serverConfig.type = server.type;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
merged[serverKey][server.name] = serverConfig;
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
return merged;
|
package/src/paths.js
CHANGED
|
@@ -32,19 +32,45 @@ function getAppSupportDir(platformInfo = getPlatform()) {
|
|
|
32
32
|
|
|
33
33
|
function getIdePaths(projectRoot, platformInfo = getPlatform(), ideConfigs = []) {
|
|
34
34
|
const home = os.homedir();
|
|
35
|
+
const appSupport = getAppSupportDir(platformInfo);
|
|
35
36
|
const paths = {};
|
|
36
37
|
|
|
37
38
|
for (const ide of ideConfigs) {
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
// Default paths (used for local scope, and global scope if no overrides)
|
|
40
|
+
let skillsFolder = ide.skillsFolder || `.${ide.id}/skills`;
|
|
41
|
+
let mcpConfigFile = ide.mcpConfigFile || `.${ide.id}/mcp.json`;
|
|
42
|
+
|
|
43
|
+
// Check for platform-specific global path overrides
|
|
44
|
+
let globalSkillsFolder = skillsFolder;
|
|
45
|
+
let globalMcpConfigFile = mcpConfigFile;
|
|
46
|
+
|
|
47
|
+
if (ide.globalPaths) {
|
|
48
|
+
const platformKey = platformInfo.isWindows ? 'Win' : platformInfo.isMac ? 'macOS' : null;
|
|
49
|
+
|
|
50
|
+
if (platformKey && ide.globalPaths[platformKey]) {
|
|
51
|
+
const globalOverrides = ide.globalPaths[platformKey];
|
|
52
|
+
|
|
53
|
+
// Use app support directory + override path for global
|
|
54
|
+
if (globalOverrides.skillsFolder) {
|
|
55
|
+
globalSkillsFolder = globalOverrides.skillsFolder;
|
|
56
|
+
}
|
|
57
|
+
if (globalOverrides.mcpConfigFile) {
|
|
58
|
+
globalMcpConfigFile = globalOverrides.mcpConfigFile;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Determine base directory for global paths
|
|
64
|
+
const useAppSupport = ide.globalPaths &&
|
|
65
|
+
(platformInfo.isWindows || platformInfo.isMac);
|
|
66
|
+
const globalBase = useAppSupport ? appSupport : home;
|
|
41
67
|
|
|
42
68
|
paths[ide.id] = {
|
|
43
69
|
name: ide.displayName,
|
|
44
|
-
mcpConfig: path.join(
|
|
70
|
+
mcpConfig: path.join(globalBase, globalMcpConfigFile),
|
|
45
71
|
localMcpConfig: path.join(projectRoot, mcpConfigFile),
|
|
46
72
|
mcpServerKey: ide.mcpServerKey,
|
|
47
|
-
skillsDir: path.join(
|
|
73
|
+
skillsDir: path.join(globalBase, globalSkillsFolder),
|
|
48
74
|
localSkillsDir: path.join(projectRoot, skillsFolder)
|
|
49
75
|
};
|
|
50
76
|
}
|
|
@@ -52,9 +78,19 @@ function getIdePaths(projectRoot, platformInfo = getPlatform(), ideConfigs = [])
|
|
|
52
78
|
return paths;
|
|
53
79
|
}
|
|
54
80
|
|
|
81
|
+
function getMcpRepoDir(scope, projectRoot, platformInfo, mcpName) {
|
|
82
|
+
if (scope === 'local') {
|
|
83
|
+
return path.join(projectRoot, '.a11y-devkit', 'mcp-repos', mcpName);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const appSupport = getAppSupportDir(platformInfo);
|
|
87
|
+
return path.join(appSupport, 'a11y-devkit', 'mcp-repos', mcpName);
|
|
88
|
+
}
|
|
89
|
+
|
|
55
90
|
export {
|
|
56
91
|
getPlatform,
|
|
57
92
|
getAppSupportDir,
|
|
58
93
|
getIdePaths,
|
|
59
|
-
getTempDir
|
|
94
|
+
getTempDir,
|
|
95
|
+
getMcpRepoDir
|
|
60
96
|
};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git MCP-specific prompt definitions and validation logic
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Validates MCP name according to rules:
|
|
7
|
+
* - Not empty
|
|
8
|
+
* - No spaces
|
|
9
|
+
* - Only alphanumeric characters and hyphens
|
|
10
|
+
* @param {string} name - MCP name to validate
|
|
11
|
+
* @returns {true | string} - true if valid, error message if invalid
|
|
12
|
+
*/
|
|
13
|
+
export function validateMcpName(name) {
|
|
14
|
+
if (!name || name.trim() === "") {
|
|
15
|
+
return "MCP name is required";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (name.includes(" ")) {
|
|
19
|
+
return "MCP name cannot contain spaces";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const validPattern = /^[a-z0-9-]+$/i;
|
|
23
|
+
if (!validPattern.test(name)) {
|
|
24
|
+
return "MCP name can only contain letters, numbers, and hyphens";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Validates Git URL format (GitHub or GitLab)
|
|
32
|
+
* @param {string} url - Git repository URL to validate
|
|
33
|
+
* @returns {{ valid: boolean, provider: 'github'|'gitlab'|null, error: string|null }}
|
|
34
|
+
*/
|
|
35
|
+
export function validateGitUrl(url) {
|
|
36
|
+
if (!url || url.trim() === "") {
|
|
37
|
+
return {
|
|
38
|
+
valid: false,
|
|
39
|
+
provider: null,
|
|
40
|
+
error: "Git repository URL is required",
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const gitUrlPattern =
|
|
45
|
+
/^https:\/\/(github\.com|gitlab\.com)\/[\w-]+\/[\w-]+(\.git)?$/;
|
|
46
|
+
const match = url.match(gitUrlPattern);
|
|
47
|
+
|
|
48
|
+
if (!match) {
|
|
49
|
+
if (url.includes("github.com") || url.includes("gitlab.com")) {
|
|
50
|
+
return {
|
|
51
|
+
valid: false,
|
|
52
|
+
provider: null,
|
|
53
|
+
error:
|
|
54
|
+
"Invalid Git URL format. Must be: https://github.com/user/repo.git or https://gitlab.com/user/repo.git",
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
valid: false,
|
|
59
|
+
provider: null,
|
|
60
|
+
error: "Only GitHub and GitLab repositories are supported",
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const provider = match[1].includes("github") ? "github" : "gitlab";
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
valid: true,
|
|
68
|
+
provider,
|
|
69
|
+
error: null,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Parses comma-separated arguments string into array
|
|
75
|
+
* @param {string} argsString - Comma-separated arguments
|
|
76
|
+
* @returns {string[]} - Array of trimmed argument strings
|
|
77
|
+
*/
|
|
78
|
+
export function parseArgsString(argsString) {
|
|
79
|
+
if (!argsString || argsString.trim() === "") {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return argsString
|
|
84
|
+
.split(",")
|
|
85
|
+
.map((arg) => arg.trim())
|
|
86
|
+
.filter((arg) => arg !== "");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Returns prompts array for Git MCP configuration
|
|
91
|
+
* @returns {Array} - Array of prompt objects for use with prompts library
|
|
92
|
+
*/
|
|
93
|
+
export function getGitMcpPrompts() {
|
|
94
|
+
return [
|
|
95
|
+
{
|
|
96
|
+
type: "text",
|
|
97
|
+
name: "name",
|
|
98
|
+
message: "MCP Name (no spaces, alphanumeric with hyphens):",
|
|
99
|
+
validate: (value) => validateMcpName(value),
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
type: "text",
|
|
103
|
+
name: "repoUrl",
|
|
104
|
+
message: "Git Repository URL (GitHub or GitLab):",
|
|
105
|
+
validate: (value) => {
|
|
106
|
+
const result = validateGitUrl(value);
|
|
107
|
+
return result.valid ? true : result.error;
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
type: "select",
|
|
112
|
+
name: "type",
|
|
113
|
+
message: "MCP server transport type:",
|
|
114
|
+
choices: [
|
|
115
|
+
{ title: "stdio", value: "stdio" },
|
|
116
|
+
{ title: "http", value: "http" },
|
|
117
|
+
{ title: "sse", value: "sse" },
|
|
118
|
+
],
|
|
119
|
+
initial: 0,
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
type: "text",
|
|
123
|
+
name: "command",
|
|
124
|
+
message: "Command to run the MCP server:",
|
|
125
|
+
initial: "node",
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
type: "text",
|
|
129
|
+
name: "args",
|
|
130
|
+
message: "Arguments for the command (typically relative path to mcp .js example: js/index.js):",
|
|
131
|
+
initial: "",
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
type: "text",
|
|
135
|
+
name: "buildCommand",
|
|
136
|
+
message: "Build command:",
|
|
137
|
+
initial: "npm install",
|
|
138
|
+
},
|
|
139
|
+
];
|
|
140
|
+
}
|