claude-cli-advanced-starter-pack 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/LICENSE +21 -0
- package/OVERVIEW.md +597 -0
- package/README.md +439 -0
- package/bin/gtask.js +282 -0
- package/bin/postinstall.js +53 -0
- package/package.json +69 -0
- package/src/agents/phase-dev-templates.js +1011 -0
- package/src/agents/templates.js +668 -0
- package/src/analysis/checklist-parser.js +414 -0
- package/src/analysis/codebase.js +481 -0
- package/src/cli/menu.js +958 -0
- package/src/commands/claude-audit.js +1482 -0
- package/src/commands/claude-settings.js +2243 -0
- package/src/commands/create-agent.js +681 -0
- package/src/commands/create-command.js +337 -0
- package/src/commands/create-hook.js +262 -0
- package/src/commands/create-phase-dev/codebase-analyzer.js +813 -0
- package/src/commands/create-phase-dev/documentation-generator.js +352 -0
- package/src/commands/create-phase-dev/post-completion.js +404 -0
- package/src/commands/create-phase-dev/scale-calculator.js +344 -0
- package/src/commands/create-phase-dev/wizard.js +492 -0
- package/src/commands/create-phase-dev.js +481 -0
- package/src/commands/create-skill.js +313 -0
- package/src/commands/create.js +446 -0
- package/src/commands/decompose.js +392 -0
- package/src/commands/detect-tech-stack.js +768 -0
- package/src/commands/explore-mcp/claude-md-updater.js +252 -0
- package/src/commands/explore-mcp/mcp-installer.js +346 -0
- package/src/commands/explore-mcp/mcp-registry.js +438 -0
- package/src/commands/explore-mcp.js +638 -0
- package/src/commands/gtask-init.js +641 -0
- package/src/commands/help.js +128 -0
- package/src/commands/init.js +1890 -0
- package/src/commands/install.js +250 -0
- package/src/commands/list.js +116 -0
- package/src/commands/roadmap.js +750 -0
- package/src/commands/setup-wizard.js +482 -0
- package/src/commands/setup.js +351 -0
- package/src/commands/sync.js +534 -0
- package/src/commands/test-run.js +456 -0
- package/src/commands/test-setup.js +456 -0
- package/src/commands/validate.js +67 -0
- package/src/config/tech-stack.defaults.json +182 -0
- package/src/config/tech-stack.schema.json +502 -0
- package/src/github/client.js +359 -0
- package/src/index.js +84 -0
- package/src/templates/claude-command.js +244 -0
- package/src/templates/issue-body.js +284 -0
- package/src/testing/config.js +411 -0
- package/src/utils/template-engine.js +398 -0
- package/src/utils/validate-templates.js +223 -0
- package/src/utils.js +396 -0
- package/templates/commands/ccasp-setup.template.md +113 -0
- package/templates/commands/context-audit.template.md +97 -0
- package/templates/commands/create-task-list.template.md +382 -0
- package/templates/commands/deploy-full.template.md +261 -0
- package/templates/commands/github-task-start.template.md +99 -0
- package/templates/commands/github-update.template.md +69 -0
- package/templates/commands/happy-start.template.md +117 -0
- package/templates/commands/phase-track.template.md +142 -0
- package/templates/commands/tunnel-start.template.md +127 -0
- package/templates/commands/tunnel-stop.template.md +106 -0
- package/templates/hooks/context-guardian.template.js +173 -0
- package/templates/hooks/deployment-orchestrator.template.js +219 -0
- package/templates/hooks/github-progress-hook.template.js +197 -0
- package/templates/hooks/happy-checkpoint-manager.template.js +222 -0
- package/templates/hooks/phase-dev-enforcer.template.js +183 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLAUDE.md Updater
|
|
3
|
+
*
|
|
4
|
+
* Updates CLAUDE.md with MCP server documentation.
|
|
5
|
+
* Adds tool references and usage examples for installed MCPs.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import ora from 'ora';
|
|
10
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
11
|
+
import { join } from 'path';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* MCP section marker
|
|
15
|
+
*/
|
|
16
|
+
const MCP_SECTION_START = '<!-- MCP-SERVERS-START -->';
|
|
17
|
+
const MCP_SECTION_END = '<!-- MCP-SERVERS-END -->';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Generate MCP documentation section
|
|
21
|
+
*
|
|
22
|
+
* @param {Array} installedMcps - Array of installed MCP definitions
|
|
23
|
+
* @returns {string} Markdown documentation
|
|
24
|
+
*/
|
|
25
|
+
export function generateMcpDocumentation(installedMcps) {
|
|
26
|
+
if (installedMcps.length === 0) {
|
|
27
|
+
return `
|
|
28
|
+
${MCP_SECTION_START}
|
|
29
|
+
## MCP Servers
|
|
30
|
+
|
|
31
|
+
No MCP servers configured. Run \`gtask explore-mcp\` to discover and install MCP servers.
|
|
32
|
+
${MCP_SECTION_END}
|
|
33
|
+
`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const sections = installedMcps.map((mcp) => {
|
|
37
|
+
const tools = mcp.tools?.slice(0, 5).join(', ') || 'See documentation';
|
|
38
|
+
const hasMoreTools = mcp.tools?.length > 5;
|
|
39
|
+
|
|
40
|
+
return `### ${mcp.name}
|
|
41
|
+
|
|
42
|
+
**ID:** \`${mcp.id}\`
|
|
43
|
+
**Category:** ${mcp.category}
|
|
44
|
+
|
|
45
|
+
${mcp.description}
|
|
46
|
+
|
|
47
|
+
**Key Tools:** ${tools}${hasMoreTools ? ` (+${mcp.tools.length - 5} more)` : ''}
|
|
48
|
+
|
|
49
|
+
${mcp.note ? `> **Note:** ${mcp.note}` : ''}`;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Group by category
|
|
53
|
+
const byCategory = {};
|
|
54
|
+
for (const mcp of installedMcps) {
|
|
55
|
+
const cat = mcp.category || 'other';
|
|
56
|
+
if (!byCategory[cat]) byCategory[cat] = [];
|
|
57
|
+
byCategory[cat].push(mcp);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const categorySections = Object.entries(byCategory)
|
|
61
|
+
.map(([category, mcps]) => {
|
|
62
|
+
const categoryName = category.charAt(0).toUpperCase() + category.slice(1);
|
|
63
|
+
const mcpDocs = mcps
|
|
64
|
+
.map((mcp) => {
|
|
65
|
+
const tools = mcp.tools?.slice(0, 3).join(', ') || 'See docs';
|
|
66
|
+
return `| ${mcp.name} | \`${mcp.id}\` | ${tools} |`;
|
|
67
|
+
})
|
|
68
|
+
.join('\n');
|
|
69
|
+
|
|
70
|
+
return `### ${categoryName}
|
|
71
|
+
|
|
72
|
+
| Server | ID | Key Tools |
|
|
73
|
+
|--------|----|-----------|
|
|
74
|
+
${mcpDocs}`;
|
|
75
|
+
})
|
|
76
|
+
.join('\n\n');
|
|
77
|
+
|
|
78
|
+
return `
|
|
79
|
+
${MCP_SECTION_START}
|
|
80
|
+
## MCP Servers
|
|
81
|
+
|
|
82
|
+
MCP (Model Context Protocol) servers extend Claude's capabilities. These are configured in \`.mcp.json\`.
|
|
83
|
+
|
|
84
|
+
${categorySections}
|
|
85
|
+
|
|
86
|
+
### Usage
|
|
87
|
+
|
|
88
|
+
MCP tools are accessed via the \`ToolSearch\` tool or directly if loaded:
|
|
89
|
+
|
|
90
|
+
\`\`\`
|
|
91
|
+
# Search for MCP tools
|
|
92
|
+
ToolSearch query: "playwright screenshot"
|
|
93
|
+
|
|
94
|
+
# Use directly (after loading)
|
|
95
|
+
mcp__playwright__browser_screenshot
|
|
96
|
+
\`\`\`
|
|
97
|
+
|
|
98
|
+
### Restart Required
|
|
99
|
+
|
|
100
|
+
After modifying \`.mcp.json\`, restart Claude Code for changes to take effect:
|
|
101
|
+
|
|
102
|
+
\`\`\`bash
|
|
103
|
+
# Exit and re-enter the project
|
|
104
|
+
cd .. && cd your-project
|
|
105
|
+
\`\`\`
|
|
106
|
+
|
|
107
|
+
*Generated by gtask explore-mcp*
|
|
108
|
+
${MCP_SECTION_END}
|
|
109
|
+
`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Load CLAUDE.md content
|
|
114
|
+
*/
|
|
115
|
+
export function loadClaudeMd(cwd = process.cwd()) {
|
|
116
|
+
const claudeMdPath = join(cwd, 'CLAUDE.md');
|
|
117
|
+
|
|
118
|
+
if (existsSync(claudeMdPath)) {
|
|
119
|
+
return readFileSync(claudeMdPath, 'utf8');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Update CLAUDE.md with MCP documentation
|
|
127
|
+
*
|
|
128
|
+
* @param {Array} installedMcps - Array of installed MCP definitions
|
|
129
|
+
* @param {Object} options - Options
|
|
130
|
+
* @returns {Object} Update result
|
|
131
|
+
*/
|
|
132
|
+
export async function updateClaudeMd(installedMcps, options = {}) {
|
|
133
|
+
const spinner = ora('Updating CLAUDE.md...').start();
|
|
134
|
+
const cwd = options.cwd || process.cwd();
|
|
135
|
+
const claudeMdPath = join(cwd, 'CLAUDE.md');
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
// Generate new MCP section
|
|
139
|
+
const mcpSection = generateMcpDocumentation(installedMcps);
|
|
140
|
+
|
|
141
|
+
let content = loadClaudeMd(cwd);
|
|
142
|
+
|
|
143
|
+
if (!content) {
|
|
144
|
+
// No CLAUDE.md exists - create minimal one with MCP section
|
|
145
|
+
spinner.text = 'Creating CLAUDE.md...';
|
|
146
|
+
content = `# Project
|
|
147
|
+
|
|
148
|
+
<!-- Project-specific instructions for Claude Code -->
|
|
149
|
+
|
|
150
|
+
${mcpSection}
|
|
151
|
+
`;
|
|
152
|
+
writeFileSync(claudeMdPath, content, 'utf8');
|
|
153
|
+
spinner.succeed('Created CLAUDE.md with MCP documentation');
|
|
154
|
+
return {
|
|
155
|
+
success: true,
|
|
156
|
+
action: 'created',
|
|
157
|
+
path: claudeMdPath,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Check if MCP section exists
|
|
162
|
+
const hasStartMarker = content.includes(MCP_SECTION_START);
|
|
163
|
+
const hasEndMarker = content.includes(MCP_SECTION_END);
|
|
164
|
+
|
|
165
|
+
if (hasStartMarker && hasEndMarker) {
|
|
166
|
+
// Replace existing section
|
|
167
|
+
spinner.text = 'Replacing existing MCP section...';
|
|
168
|
+
const regex = new RegExp(
|
|
169
|
+
`${MCP_SECTION_START}[\\s\\S]*?${MCP_SECTION_END}`,
|
|
170
|
+
'g'
|
|
171
|
+
);
|
|
172
|
+
content = content.replace(regex, mcpSection.trim());
|
|
173
|
+
} else {
|
|
174
|
+
// Append to end
|
|
175
|
+
spinner.text = 'Appending MCP section...';
|
|
176
|
+
|
|
177
|
+
// Find good insertion point (before any existing sections like "---" at end)
|
|
178
|
+
const lines = content.split('\n');
|
|
179
|
+
let insertIndex = lines.length;
|
|
180
|
+
|
|
181
|
+
// Look for common ending patterns
|
|
182
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
183
|
+
const line = lines[i].trim();
|
|
184
|
+
if (line === '---' || line.startsWith('*Generated') || line.startsWith('<!-- ')) {
|
|
185
|
+
insertIndex = i;
|
|
186
|
+
} else if (line.length > 0) {
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
lines.splice(insertIndex, 0, '', mcpSection.trim(), '');
|
|
192
|
+
content = lines.join('\n');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
writeFileSync(claudeMdPath, content, 'utf8');
|
|
196
|
+
spinner.succeed('Updated CLAUDE.md with MCP documentation');
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
success: true,
|
|
200
|
+
action: hasStartMarker ? 'updated' : 'appended',
|
|
201
|
+
path: claudeMdPath,
|
|
202
|
+
};
|
|
203
|
+
} catch (error) {
|
|
204
|
+
spinner.fail(`Failed to update CLAUDE.md: ${error.message}`);
|
|
205
|
+
return {
|
|
206
|
+
success: false,
|
|
207
|
+
error: error.message,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Remove MCP section from CLAUDE.md
|
|
214
|
+
*/
|
|
215
|
+
export function removeMcpSection(cwd = process.cwd()) {
|
|
216
|
+
const claudeMdPath = join(cwd, 'CLAUDE.md');
|
|
217
|
+
|
|
218
|
+
if (!existsSync(claudeMdPath)) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
let content = readFileSync(claudeMdPath, 'utf8');
|
|
223
|
+
|
|
224
|
+
const hasStartMarker = content.includes(MCP_SECTION_START);
|
|
225
|
+
const hasEndMarker = content.includes(MCP_SECTION_END);
|
|
226
|
+
|
|
227
|
+
if (hasStartMarker && hasEndMarker) {
|
|
228
|
+
const regex = new RegExp(
|
|
229
|
+
`\\n?${MCP_SECTION_START}[\\s\\S]*?${MCP_SECTION_END}\\n?`,
|
|
230
|
+
'g'
|
|
231
|
+
);
|
|
232
|
+
content = content.replace(regex, '\n');
|
|
233
|
+
writeFileSync(claudeMdPath, content, 'utf8');
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Generate a quick-reference comment for MCP tools
|
|
242
|
+
*/
|
|
243
|
+
export function generateMcpQuickRef(installedMcps) {
|
|
244
|
+
const lines = ['<!-- MCP Quick Reference'];
|
|
245
|
+
|
|
246
|
+
for (const mcp of installedMcps) {
|
|
247
|
+
lines.push(` ${mcp.id}: ${mcp.tools?.slice(0, 3).join(', ') || 'see docs'}`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
lines.push('-->');
|
|
251
|
+
return lines.join('\n');
|
|
252
|
+
}
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Installer
|
|
3
|
+
*
|
|
4
|
+
* Handles installation of MCP servers:
|
|
5
|
+
* - Generates .mcp.json configuration
|
|
6
|
+
* - Updates .claude/settings.json permissions
|
|
7
|
+
* - Supports Windows (cmd /c wrapper)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import ora from 'ora';
|
|
12
|
+
import inquirer from 'inquirer';
|
|
13
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
14
|
+
import { join } from 'path';
|
|
15
|
+
import { execSync } from 'child_process';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Check if running on Windows
|
|
19
|
+
*/
|
|
20
|
+
export function isWindows() {
|
|
21
|
+
return process.platform === 'win32';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Generate MCP server configuration for .mcp.json
|
|
26
|
+
*
|
|
27
|
+
* @param {Object} mcp - MCP server definition from registry
|
|
28
|
+
* @param {Object} envValues - Environment variable values provided by user
|
|
29
|
+
* @returns {Object} Server configuration object
|
|
30
|
+
*/
|
|
31
|
+
export function generateMcpConfig(mcp, envValues = {}) {
|
|
32
|
+
const config = {
|
|
33
|
+
env: { ...envValues },
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Handle different command types
|
|
37
|
+
if (mcp.command === 'uvx') {
|
|
38
|
+
// Python-based MCP (like claude-mem)
|
|
39
|
+
config.command = 'uvx';
|
|
40
|
+
config.args = [...(mcp.args || [])];
|
|
41
|
+
} else if (mcp.npmPackage) {
|
|
42
|
+
// NPM-based MCP
|
|
43
|
+
if (isWindows()) {
|
|
44
|
+
config.command = 'cmd';
|
|
45
|
+
config.args = ['/c', 'npx', '-y', mcp.npmPackage];
|
|
46
|
+
} else {
|
|
47
|
+
config.command = 'npx';
|
|
48
|
+
config.args = ['-y', mcp.npmPackage];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Add any additional args
|
|
52
|
+
if (mcp.args) {
|
|
53
|
+
config.args.push(...mcp.args);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return config;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Load existing .mcp.json or create empty structure
|
|
62
|
+
*/
|
|
63
|
+
export function loadMcpJson(cwd = process.cwd()) {
|
|
64
|
+
const mcpPath = join(cwd, '.mcp.json');
|
|
65
|
+
|
|
66
|
+
if (existsSync(mcpPath)) {
|
|
67
|
+
try {
|
|
68
|
+
const content = readFileSync(mcpPath, 'utf8');
|
|
69
|
+
return JSON.parse(content);
|
|
70
|
+
} catch {
|
|
71
|
+
return { mcpServers: {} };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { mcpServers: {} };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Save .mcp.json
|
|
80
|
+
*/
|
|
81
|
+
export function saveMcpJson(config, cwd = process.cwd()) {
|
|
82
|
+
const mcpPath = join(cwd, '.mcp.json');
|
|
83
|
+
const content = JSON.stringify(config, null, 2);
|
|
84
|
+
writeFileSync(mcpPath, content, 'utf8');
|
|
85
|
+
return mcpPath;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Load existing .claude/settings.json or create empty structure
|
|
90
|
+
*/
|
|
91
|
+
export function loadClaudeSettings(cwd = process.cwd()) {
|
|
92
|
+
const settingsPath = join(cwd, '.claude', 'settings.json');
|
|
93
|
+
|
|
94
|
+
if (existsSync(settingsPath)) {
|
|
95
|
+
try {
|
|
96
|
+
const content = readFileSync(settingsPath, 'utf8');
|
|
97
|
+
return JSON.parse(content);
|
|
98
|
+
} catch {
|
|
99
|
+
return { permissions: { allow: [], deny: [] } };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return { permissions: { allow: [], deny: [] } };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Save .claude/settings.json
|
|
108
|
+
*/
|
|
109
|
+
export function saveClaudeSettings(settings, cwd = process.cwd()) {
|
|
110
|
+
const claudeDir = join(cwd, '.claude');
|
|
111
|
+
const settingsPath = join(claudeDir, 'settings.json');
|
|
112
|
+
|
|
113
|
+
if (!existsSync(claudeDir)) {
|
|
114
|
+
mkdirSync(claudeDir, { recursive: true });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const content = JSON.stringify(settings, null, 2);
|
|
118
|
+
writeFileSync(settingsPath, content, 'utf8');
|
|
119
|
+
return settingsPath;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Add MCP permission to settings
|
|
124
|
+
*/
|
|
125
|
+
export function addMcpPermission(settings, mcpId) {
|
|
126
|
+
if (!settings.permissions) {
|
|
127
|
+
settings.permissions = { allow: [], deny: [] };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (!settings.permissions.allow) {
|
|
131
|
+
settings.permissions.allow = [];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const permission = `MCP(${mcpId}:*)`;
|
|
135
|
+
|
|
136
|
+
if (!settings.permissions.allow.includes(permission)) {
|
|
137
|
+
settings.permissions.allow.push(permission);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return settings;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Enable MCP server in settings
|
|
145
|
+
*/
|
|
146
|
+
export function enableMcpServer(settings, mcpId) {
|
|
147
|
+
if (!settings.enableAllProjectMcpServers) {
|
|
148
|
+
if (!settings.enabledMcpjsonServers) {
|
|
149
|
+
settings.enabledMcpjsonServers = [];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (!settings.enabledMcpjsonServers.includes(mcpId)) {
|
|
153
|
+
settings.enabledMcpjsonServers.push(mcpId);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return settings;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Prompt user for required environment variables
|
|
162
|
+
*/
|
|
163
|
+
export async function promptForEnvVars(mcp) {
|
|
164
|
+
const envValues = {};
|
|
165
|
+
const required = mcp.requiredEnv || {};
|
|
166
|
+
const optional = mcp.optionalEnv || {};
|
|
167
|
+
|
|
168
|
+
// Prompt for required vars
|
|
169
|
+
for (const [key, info] of Object.entries(required)) {
|
|
170
|
+
const { value } = await inquirer.prompt([
|
|
171
|
+
{
|
|
172
|
+
type: 'password',
|
|
173
|
+
name: 'value',
|
|
174
|
+
message: `${key} (${info.description}):`,
|
|
175
|
+
mask: '*',
|
|
176
|
+
validate: (input) => input.length > 0 || 'This field is required',
|
|
177
|
+
},
|
|
178
|
+
]);
|
|
179
|
+
envValues[key] = value;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Ask about optional vars
|
|
183
|
+
if (Object.keys(optional).length > 0) {
|
|
184
|
+
const { configureOptional } = await inquirer.prompt([
|
|
185
|
+
{
|
|
186
|
+
type: 'confirm',
|
|
187
|
+
name: 'configureOptional',
|
|
188
|
+
message: 'Configure optional settings?',
|
|
189
|
+
default: false,
|
|
190
|
+
},
|
|
191
|
+
]);
|
|
192
|
+
|
|
193
|
+
if (configureOptional) {
|
|
194
|
+
for (const [key, info] of Object.entries(optional)) {
|
|
195
|
+
const { value } = await inquirer.prompt([
|
|
196
|
+
{
|
|
197
|
+
type: 'input',
|
|
198
|
+
name: 'value',
|
|
199
|
+
message: `${key} (${info.description}):`,
|
|
200
|
+
default: info.default || '',
|
|
201
|
+
},
|
|
202
|
+
]);
|
|
203
|
+
if (value) {
|
|
204
|
+
envValues[key] = value;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return envValues;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Install a single MCP server
|
|
215
|
+
*
|
|
216
|
+
* @param {Object} mcp - MCP definition from registry
|
|
217
|
+
* @param {Object} options - Installation options
|
|
218
|
+
* @returns {Object} Installation result
|
|
219
|
+
*/
|
|
220
|
+
export async function installMcp(mcp, options = {}) {
|
|
221
|
+
const spinner = ora(`Installing ${mcp.name}...`).start();
|
|
222
|
+
const cwd = options.cwd || process.cwd();
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
// 1. Get environment variables
|
|
226
|
+
spinner.text = 'Configuring environment variables...';
|
|
227
|
+
let envValues = options.envValues || {};
|
|
228
|
+
|
|
229
|
+
if (!options.skipEnvPrompt && Object.keys(mcp.requiredEnv || {}).length > 0) {
|
|
230
|
+
spinner.stop();
|
|
231
|
+
envValues = await promptForEnvVars(mcp);
|
|
232
|
+
spinner.start();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// 2. Generate MCP config
|
|
236
|
+
spinner.text = 'Generating configuration...';
|
|
237
|
+
const mcpConfig = generateMcpConfig(mcp, envValues);
|
|
238
|
+
|
|
239
|
+
// 3. Update .mcp.json
|
|
240
|
+
spinner.text = 'Updating .mcp.json...';
|
|
241
|
+
const mcpJson = loadMcpJson(cwd);
|
|
242
|
+
mcpJson.mcpServers = mcpJson.mcpServers || {};
|
|
243
|
+
mcpJson.mcpServers[mcp.id] = mcpConfig;
|
|
244
|
+
|
|
245
|
+
// Add helpful note
|
|
246
|
+
if (!mcpJson._NOTE) {
|
|
247
|
+
mcpJson._NOTE = 'MCP servers configured by gtask explore-mcp';
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const mcpPath = saveMcpJson(mcpJson, cwd);
|
|
251
|
+
|
|
252
|
+
// 4. Update .claude/settings.json
|
|
253
|
+
spinner.text = 'Updating Claude settings...';
|
|
254
|
+
const settings = loadClaudeSettings(cwd);
|
|
255
|
+
addMcpPermission(settings, mcp.id);
|
|
256
|
+
enableMcpServer(settings, mcp.id);
|
|
257
|
+
const settingsPath = saveClaudeSettings(settings, cwd);
|
|
258
|
+
|
|
259
|
+
spinner.succeed(`Installed ${mcp.name}`);
|
|
260
|
+
|
|
261
|
+
return {
|
|
262
|
+
success: true,
|
|
263
|
+
mcp,
|
|
264
|
+
files: {
|
|
265
|
+
mcpJson: mcpPath,
|
|
266
|
+
settings: settingsPath,
|
|
267
|
+
},
|
|
268
|
+
config: mcpConfig,
|
|
269
|
+
};
|
|
270
|
+
} catch (error) {
|
|
271
|
+
spinner.fail(`Failed to install ${mcp.name}: ${error.message}`);
|
|
272
|
+
return {
|
|
273
|
+
success: false,
|
|
274
|
+
mcp,
|
|
275
|
+
error: error.message,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Install multiple MCPs
|
|
282
|
+
*/
|
|
283
|
+
export async function installMultipleMcps(mcps, options = {}) {
|
|
284
|
+
const results = [];
|
|
285
|
+
|
|
286
|
+
for (const mcp of mcps) {
|
|
287
|
+
const result = await installMcp(mcp, options);
|
|
288
|
+
results.push(result);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return results;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Check if an MCP is already installed
|
|
296
|
+
*/
|
|
297
|
+
export function isMcpInstalled(mcpId, cwd = process.cwd()) {
|
|
298
|
+
const mcpJson = loadMcpJson(cwd);
|
|
299
|
+
return mcpJson.mcpServers && mcpId in mcpJson.mcpServers;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Get list of installed MCPs
|
|
304
|
+
*/
|
|
305
|
+
export function getInstalledMcps(cwd = process.cwd()) {
|
|
306
|
+
const mcpJson = loadMcpJson(cwd);
|
|
307
|
+
return Object.keys(mcpJson.mcpServers || {});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Remove an MCP
|
|
312
|
+
*/
|
|
313
|
+
export function removeMcp(mcpId, cwd = process.cwd()) {
|
|
314
|
+
// Remove from .mcp.json
|
|
315
|
+
const mcpJson = loadMcpJson(cwd);
|
|
316
|
+
if (mcpJson.mcpServers && mcpId in mcpJson.mcpServers) {
|
|
317
|
+
delete mcpJson.mcpServers[mcpId];
|
|
318
|
+
saveMcpJson(mcpJson, cwd);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Remove from settings
|
|
322
|
+
const settings = loadClaudeSettings(cwd);
|
|
323
|
+
if (settings.enabledMcpjsonServers) {
|
|
324
|
+
settings.enabledMcpjsonServers = settings.enabledMcpjsonServers.filter(
|
|
325
|
+
(id) => id !== mcpId
|
|
326
|
+
);
|
|
327
|
+
saveClaudeSettings(settings, cwd);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return true;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Verify MCP installation by checking npm package exists
|
|
335
|
+
*/
|
|
336
|
+
export async function verifyMcpPackage(npmPackage) {
|
|
337
|
+
try {
|
|
338
|
+
execSync(`npm view ${npmPackage.split('@')[0]} --json`, {
|
|
339
|
+
stdio: 'pipe',
|
|
340
|
+
encoding: 'utf8',
|
|
341
|
+
});
|
|
342
|
+
return true;
|
|
343
|
+
} catch {
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
}
|