jumpstart-mode 1.0.6 → 1.0.8
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/.cursorrules +3 -0
- package/.github/agents/jumpstart-analyst.agent.md +28 -7
- package/.github/agents/jumpstart-architect.agent.md +28 -7
- package/.github/agents/jumpstart-challenger.agent.md +37 -4
- package/.github/agents/jumpstart-developer.agent.md +19 -8
- package/.github/agents/jumpstart-facilitator.agent.md +75 -0
- package/.github/agents/jumpstart-pm.agent.md +13 -6
- package/.github/agents/jumpstart-scout.agent.md +50 -0
- package/.github/copilot-instructions.md +13 -5
- package/.github/instructions/specs.instructions.md +2 -0
- package/.github/prompts/jumpstart-review.prompt.md +2 -0
- package/.github/prompts/jumpstart-status.prompt.md +8 -1
- package/.jumpstart/agents/analyst.md +235 -28
- package/.jumpstart/agents/architect.md +163 -15
- package/.jumpstart/agents/challenger.md +91 -6
- package/.jumpstart/agents/developer.md +55 -2
- package/.jumpstart/agents/facilitator.md +227 -0
- package/.jumpstart/agents/pm.md +40 -5
- package/.jumpstart/agents/scout.md +358 -0
- package/.jumpstart/commands/commands.md +102 -26
- package/.jumpstart/config.yaml +46 -1
- package/.jumpstart/domain-complexity.csv +15 -0
- package/.jumpstart/roadmap.md +85 -0
- package/.jumpstart/templates/agents-md.md +93 -0
- package/.jumpstart/templates/architecture.md +48 -8
- package/.jumpstart/templates/challenger-brief.md +6 -4
- package/.jumpstart/templates/codebase-context.md +265 -0
- package/.jumpstart/templates/implementation-plan.md +9 -5
- package/.jumpstart/templates/insights.md +18 -18
- package/.jumpstart/templates/prd.md +7 -5
- package/.jumpstart/templates/product-brief.md +6 -4
- package/.jumpstart/templates/roadmap.md +79 -0
- package/AGENTS.md +31 -1
- package/CLAUDE.md +3 -0
- package/README.md +67 -8
- package/bin/cli.js +199 -3
- package/bin/context7-setup.js +405 -0
- package/package.json +1 -1
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// Context7 MCP Setup Module
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// Handles interactive setup of the Context7 MCP (Model Context Protocol)
|
|
6
|
+
// server for AI coding assistants. Integrates with the JumpStart CLI to
|
|
7
|
+
// optionally configure Context7 during framework installation.
|
|
8
|
+
//
|
|
9
|
+
// Supported clients:
|
|
10
|
+
// - VS Code (GitHub Copilot)
|
|
11
|
+
// - Cursor
|
|
12
|
+
// - Claude Code (CLI)
|
|
13
|
+
// - Claude Desktop
|
|
14
|
+
// - Windsurf
|
|
15
|
+
// - Generic (manual instructions)
|
|
16
|
+
// ============================================================================
|
|
17
|
+
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const chalk = require('chalk');
|
|
21
|
+
const prompts = require('prompts');
|
|
22
|
+
const { execSync } = require('child_process');
|
|
23
|
+
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Constants
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
const API_KEY_PREFIX = 'ctx7sk-';
|
|
29
|
+
|
|
30
|
+
// Client configuration templates
|
|
31
|
+
const CLIENT_CONFIGS = {
|
|
32
|
+
vscode: {
|
|
33
|
+
name: 'VS Code (GitHub Copilot)',
|
|
34
|
+
configFileName: '.vscode/mcp.json',
|
|
35
|
+
generateConfig: (apiKey) => ({
|
|
36
|
+
mcp: {
|
|
37
|
+
servers: {
|
|
38
|
+
context7: {
|
|
39
|
+
type: 'stdio',
|
|
40
|
+
command: 'npx',
|
|
41
|
+
args: ['-y', '@upstash/context7-mcp', '--api-key', apiKey]
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
},
|
|
47
|
+
cursor: {
|
|
48
|
+
name: 'Cursor',
|
|
49
|
+
configFileName: '.cursor/mcp.json',
|
|
50
|
+
generateConfig: (apiKey) => ({
|
|
51
|
+
mcpServers: {
|
|
52
|
+
context7: {
|
|
53
|
+
command: 'npx',
|
|
54
|
+
args: ['-y', '@upstash/context7-mcp', '--api-key', apiKey]
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
},
|
|
59
|
+
'claude-code': {
|
|
60
|
+
name: 'Claude Code (CLI)',
|
|
61
|
+
useCli: true,
|
|
62
|
+
cliCommand: (apiKey) =>
|
|
63
|
+
`claude mcp add context7 -- npx -y @upstash/context7-mcp --api-key ${apiKey}`
|
|
64
|
+
},
|
|
65
|
+
'claude-desktop': {
|
|
66
|
+
name: 'Claude Desktop',
|
|
67
|
+
configFileName: null, // Platform-specific path resolved at runtime
|
|
68
|
+
getConfigPath: () => {
|
|
69
|
+
if (process.platform === 'win32') {
|
|
70
|
+
return path.join(
|
|
71
|
+
process.env.APPDATA || '',
|
|
72
|
+
'Claude',
|
|
73
|
+
'claude_desktop_config.json'
|
|
74
|
+
);
|
|
75
|
+
} else if (process.platform === 'darwin') {
|
|
76
|
+
return path.join(
|
|
77
|
+
process.env.HOME || '',
|
|
78
|
+
'Library',
|
|
79
|
+
'Application Support',
|
|
80
|
+
'Claude',
|
|
81
|
+
'claude_desktop_config.json'
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
return path.join(process.env.HOME || '', '.config', 'claude', 'claude_desktop_config.json');
|
|
85
|
+
},
|
|
86
|
+
generateConfig: (apiKey) => ({
|
|
87
|
+
mcpServers: {
|
|
88
|
+
context7: {
|
|
89
|
+
command: 'npx',
|
|
90
|
+
args: ['-y', '@upstash/context7-mcp', '--api-key', apiKey]
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
},
|
|
95
|
+
windsurf: {
|
|
96
|
+
name: 'Windsurf',
|
|
97
|
+
configFileName: null,
|
|
98
|
+
getConfigPath: () => {
|
|
99
|
+
if (process.platform === 'win32') {
|
|
100
|
+
return path.join(
|
|
101
|
+
process.env.APPDATA || process.env.USERPROFILE || '',
|
|
102
|
+
'.codeium',
|
|
103
|
+
'windsurf',
|
|
104
|
+
'mcp_config.json'
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
return path.join(process.env.HOME || '', '.codeium', 'windsurf', 'mcp_config.json');
|
|
108
|
+
},
|
|
109
|
+
generateConfig: (apiKey) => ({
|
|
110
|
+
mcpServers: {
|
|
111
|
+
context7: {
|
|
112
|
+
command: 'npx',
|
|
113
|
+
args: ['-y', '@upstash/context7-mcp', '--api-key', apiKey]
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
// Validation
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Validates that an API key has the expected Context7 format.
|
|
126
|
+
*/
|
|
127
|
+
function validateApiKey(value) {
|
|
128
|
+
if (!value || typeof value !== 'string') {
|
|
129
|
+
return 'API key is required';
|
|
130
|
+
}
|
|
131
|
+
value = value.trim();
|
|
132
|
+
if (!value.startsWith(API_KEY_PREFIX)) {
|
|
133
|
+
return `Invalid format. Context7 API keys start with "${API_KEY_PREFIX}"`;
|
|
134
|
+
}
|
|
135
|
+
if (value.length < 10) {
|
|
136
|
+
return 'API key appears too short';
|
|
137
|
+
}
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
// Configuration writers
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Merges the Context7 MCP server block into an existing JSON config file,
|
|
147
|
+
* or creates the file if it doesn't exist.
|
|
148
|
+
*/
|
|
149
|
+
function mergeJsonConfig(filePath, newConfig) {
|
|
150
|
+
let existing = {};
|
|
151
|
+
|
|
152
|
+
if (fs.existsSync(filePath)) {
|
|
153
|
+
try {
|
|
154
|
+
const raw = fs.readFileSync(filePath, 'utf8');
|
|
155
|
+
existing = JSON.parse(raw);
|
|
156
|
+
} catch {
|
|
157
|
+
// If the file is malformed, back it up and start fresh
|
|
158
|
+
const backup = filePath + '.bak';
|
|
159
|
+
fs.copyFileSync(filePath, backup);
|
|
160
|
+
console.log(chalk.yellow(` ⚠ Backed up malformed ${path.basename(filePath)} to ${path.basename(backup)}`));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Deep-merge: add context7 into the appropriate nested key
|
|
165
|
+
const merged = deepMerge(existing, newConfig);
|
|
166
|
+
|
|
167
|
+
const dir = path.dirname(filePath);
|
|
168
|
+
if (!fs.existsSync(dir)) {
|
|
169
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
fs.writeFileSync(filePath, JSON.stringify(merged, null, 2) + '\n', 'utf8');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Simple recursive merge (source wins on conflict).
|
|
177
|
+
*/
|
|
178
|
+
function deepMerge(target, source) {
|
|
179
|
+
const output = { ...target };
|
|
180
|
+
for (const key of Object.keys(source)) {
|
|
181
|
+
if (
|
|
182
|
+
source[key] &&
|
|
183
|
+
typeof source[key] === 'object' &&
|
|
184
|
+
!Array.isArray(source[key]) &&
|
|
185
|
+
target[key] &&
|
|
186
|
+
typeof target[key] === 'object' &&
|
|
187
|
+
!Array.isArray(target[key])
|
|
188
|
+
) {
|
|
189
|
+
output[key] = deepMerge(target[key], source[key]);
|
|
190
|
+
} else {
|
|
191
|
+
output[key] = source[key];
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return output;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ---------------------------------------------------------------------------
|
|
198
|
+
// Client installers
|
|
199
|
+
// ---------------------------------------------------------------------------
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Installs MCP configuration for a specific client.
|
|
203
|
+
* @returns {{ success: boolean, message: string }}
|
|
204
|
+
*/
|
|
205
|
+
function installForClient(clientKey, apiKey, targetDir) {
|
|
206
|
+
const client = CLIENT_CONFIGS[clientKey];
|
|
207
|
+
if (!client) {
|
|
208
|
+
return { success: false, message: `Unknown client: ${clientKey}` };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// CLI-based installation (Claude Code)
|
|
212
|
+
if (client.useCli) {
|
|
213
|
+
const cmd = client.cliCommand(apiKey);
|
|
214
|
+
try {
|
|
215
|
+
execSync(cmd, { stdio: 'pipe', cwd: targetDir });
|
|
216
|
+
return { success: true, message: `Configured via CLI: ${cmd.split('--api-key')[0].trim()}...` };
|
|
217
|
+
} catch (err) {
|
|
218
|
+
return {
|
|
219
|
+
success: false,
|
|
220
|
+
message: `CLI command failed. You can run it manually:\n ${cmd}`
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// File-based installation
|
|
226
|
+
let configPath;
|
|
227
|
+
if (client.getConfigPath) {
|
|
228
|
+
configPath = client.getConfigPath();
|
|
229
|
+
} else if (client.configFileName) {
|
|
230
|
+
configPath = path.join(targetDir, client.configFileName);
|
|
231
|
+
} else {
|
|
232
|
+
return { success: false, message: 'Cannot determine config path for this client.' };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
const config = client.generateConfig(apiKey);
|
|
237
|
+
mergeJsonConfig(configPath, config);
|
|
238
|
+
return { success: true, message: `Config written to ${configPath}` };
|
|
239
|
+
} catch (err) {
|
|
240
|
+
return { success: false, message: `Failed to write config: ${err.message}` };
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ---------------------------------------------------------------------------
|
|
245
|
+
// Interactive setup (exported for use in CLI)
|
|
246
|
+
// ---------------------------------------------------------------------------
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Runs the interactive Context7 MCP setup flow.
|
|
250
|
+
* Called from the JumpStart CLI after the main installation.
|
|
251
|
+
*
|
|
252
|
+
* @param {Object} options
|
|
253
|
+
* @param {string} options.targetDir - Project target directory
|
|
254
|
+
* @param {boolean} [options.dryRun=false] - If true, show what would happen
|
|
255
|
+
* @returns {Promise<{ installed: boolean, clients: string[], apiKey?: string }>}
|
|
256
|
+
*/
|
|
257
|
+
async function setupContext7({ targetDir, dryRun = false }) {
|
|
258
|
+
console.log(chalk.cyan('\n🔌 Context7 MCP Integration\n'));
|
|
259
|
+
console.log(
|
|
260
|
+
chalk.gray(
|
|
261
|
+
' Context7 provides up-to-date library documentation to your AI coding\n' +
|
|
262
|
+
' assistant via the Model Context Protocol (MCP). This is optional.\n' +
|
|
263
|
+
' Get your API key at: https://context7.com\n'
|
|
264
|
+
)
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
// Step 1: Ask if user wants to install
|
|
268
|
+
const { installMcp } = await prompts({
|
|
269
|
+
type: 'confirm',
|
|
270
|
+
name: 'installMcp',
|
|
271
|
+
message: 'Would you like to install the Context7 MCP for your AI agents?',
|
|
272
|
+
initial: true
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
if (!installMcp) {
|
|
276
|
+
console.log(chalk.gray(' Skipped Context7 MCP setup.\n'));
|
|
277
|
+
return { installed: false, clients: [] };
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Step 2: Capture API key
|
|
281
|
+
const { apiKey } = await prompts({
|
|
282
|
+
type: 'text',
|
|
283
|
+
name: 'apiKey',
|
|
284
|
+
message: `Enter your Context7 API key (${API_KEY_PREFIX}...):`,
|
|
285
|
+
validate: validateApiKey
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
if (!apiKey) {
|
|
289
|
+
console.log(chalk.yellow(' No API key provided. Skipping Context7 setup.\n'));
|
|
290
|
+
return { installed: false, clients: [] };
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const trimmedKey = apiKey.trim();
|
|
294
|
+
|
|
295
|
+
// Step 3: Select target clients
|
|
296
|
+
const clientChoices = Object.entries(CLIENT_CONFIGS).map(([key, cfg]) => ({
|
|
297
|
+
title: cfg.name,
|
|
298
|
+
value: key,
|
|
299
|
+
selected: key === 'vscode' // Pre-select VS Code
|
|
300
|
+
}));
|
|
301
|
+
|
|
302
|
+
const { selectedClients } = await prompts({
|
|
303
|
+
type: 'multiselect',
|
|
304
|
+
name: 'selectedClients',
|
|
305
|
+
message: 'Which AI clients should be configured?',
|
|
306
|
+
choices: clientChoices,
|
|
307
|
+
hint: '- Space to select, Enter to confirm',
|
|
308
|
+
instructions: false,
|
|
309
|
+
min: 1
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
if (!selectedClients || selectedClients.length === 0) {
|
|
313
|
+
console.log(chalk.yellow(' No clients selected. Skipping Context7 setup.\n'));
|
|
314
|
+
return { installed: false, clients: [] };
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Step 4: Security notice
|
|
318
|
+
console.log(
|
|
319
|
+
chalk.yellow(
|
|
320
|
+
'\n ⚠ Your API key will be stored in local configuration files.\n' +
|
|
321
|
+
' Make sure these files are listed in your .gitignore.\n'
|
|
322
|
+
)
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
// Step 5: Install for each selected client
|
|
326
|
+
if (dryRun) {
|
|
327
|
+
console.log(chalk.yellow.bold(' [DRY RUN] Would configure Context7 for:'));
|
|
328
|
+
selectedClients.forEach((c) =>
|
|
329
|
+
console.log(chalk.gray(` - ${CLIENT_CONFIGS[c].name}`))
|
|
330
|
+
);
|
|
331
|
+
console.log('');
|
|
332
|
+
return { installed: false, clients: selectedClients };
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const results = [];
|
|
336
|
+
for (const clientKey of selectedClients) {
|
|
337
|
+
const clientName = CLIENT_CONFIGS[clientKey].name;
|
|
338
|
+
const result = installForClient(clientKey, trimmedKey, targetDir);
|
|
339
|
+
results.push({ clientKey, ...result });
|
|
340
|
+
|
|
341
|
+
if (result.success) {
|
|
342
|
+
console.log(chalk.green(` ✅ ${clientName}: ${result.message}`));
|
|
343
|
+
} else {
|
|
344
|
+
console.log(chalk.red(` ❌ ${clientName}: ${result.message}`));
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Step 6: Ensure .gitignore entries exist
|
|
349
|
+
ensureGitignoreEntries(targetDir, selectedClients, dryRun);
|
|
350
|
+
|
|
351
|
+
const successClients = results.filter((r) => r.success).map((r) => r.clientKey);
|
|
352
|
+
console.log(chalk.green(`\n Context7 MCP configured for ${successClients.length} client(s).\n`));
|
|
353
|
+
|
|
354
|
+
return { installed: true, clients: successClients, apiKey: trimmedKey };
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Ensures that MCP config files containing API keys are gitignored.
|
|
359
|
+
*/
|
|
360
|
+
function ensureGitignoreEntries(targetDir, selectedClients, dryRun) {
|
|
361
|
+
const gitignorePath = path.join(targetDir, '.gitignore');
|
|
362
|
+
const entriesToAdd = [];
|
|
363
|
+
|
|
364
|
+
// Map client keys to gitignore patterns
|
|
365
|
+
const gitignorePatterns = {
|
|
366
|
+
vscode: '.vscode/mcp.json',
|
|
367
|
+
cursor: '.cursor/mcp.json'
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
for (const clientKey of selectedClients) {
|
|
371
|
+
if (gitignorePatterns[clientKey]) {
|
|
372
|
+
entriesToAdd.push(gitignorePatterns[clientKey]);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (entriesToAdd.length === 0) return;
|
|
377
|
+
|
|
378
|
+
let existing = '';
|
|
379
|
+
if (fs.existsSync(gitignorePath)) {
|
|
380
|
+
existing = fs.readFileSync(gitignorePath, 'utf8');
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const missing = entriesToAdd.filter((e) => !existing.includes(e));
|
|
384
|
+
if (missing.length === 0) return;
|
|
385
|
+
|
|
386
|
+
if (dryRun) {
|
|
387
|
+
console.log(chalk.gray(` Would add to .gitignore: ${missing.join(', ')}`));
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const block = '\n# Context7 MCP config (contains API key)\n' + missing.join('\n') + '\n';
|
|
392
|
+
fs.appendFileSync(gitignorePath, block, 'utf8');
|
|
393
|
+
console.log(chalk.gray(` Updated .gitignore with: ${missing.join(', ')}`));
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// ---------------------------------------------------------------------------
|
|
397
|
+
// Exports
|
|
398
|
+
// ---------------------------------------------------------------------------
|
|
399
|
+
|
|
400
|
+
module.exports = {
|
|
401
|
+
setupContext7,
|
|
402
|
+
validateApiKey,
|
|
403
|
+
installForClient,
|
|
404
|
+
CLIENT_CONFIGS
|
|
405
|
+
};
|
package/package.json
CHANGED