@girardmedia/bootspring 1.1.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/README.md +255 -0
- package/agents/README.md +93 -0
- package/agents/api-expert/context.md +416 -0
- package/agents/architecture-expert/context.md +454 -0
- package/agents/backend-expert/context.md +483 -0
- package/agents/code-review-expert/context.md +365 -0
- package/agents/database-expert/context.md +250 -0
- package/agents/devops-expert/context.md +446 -0
- package/agents/frontend-expert/context.md +364 -0
- package/agents/index.js +140 -0
- package/agents/performance-expert/context.md +377 -0
- package/agents/security-expert/context.md +343 -0
- package/agents/testing-expert/context.md +414 -0
- package/agents/ui-ux-expert/context.md +448 -0
- package/agents/vercel-expert/context.md +426 -0
- package/bin/bootspring.js +310 -0
- package/cli/agent.js +337 -0
- package/cli/context.js +194 -0
- package/cli/dashboard.js +150 -0
- package/cli/generate.js +294 -0
- package/cli/init.js +410 -0
- package/cli/loop.js +421 -0
- package/cli/mcp.js +241 -0
- package/cli/memory.js +303 -0
- package/cli/orchestrator.js +400 -0
- package/cli/plugin.js +451 -0
- package/cli/quality.js +332 -0
- package/cli/skill.js +369 -0
- package/cli/task.js +628 -0
- package/cli/telemetry.js +114 -0
- package/cli/todo.js +614 -0
- package/cli/update.js +312 -0
- package/core/config.js +245 -0
- package/core/context.js +329 -0
- package/core/entitlements.js +209 -0
- package/core/index.js +43 -0
- package/core/policies.js +68 -0
- package/core/telemetry.js +247 -0
- package/core/utils.js +380 -0
- package/dashboard/server.js +818 -0
- package/docs/integrations/claude-code.md +42 -0
- package/docs/integrations/codex.md +42 -0
- package/docs/mcp-api-platform.md +102 -0
- package/generators/generate.js +598 -0
- package/generators/index.js +18 -0
- package/hooks/context-detector.js +177 -0
- package/hooks/index.js +35 -0
- package/hooks/prompt-enhancer.js +289 -0
- package/intelligence/git-memory.js +551 -0
- package/intelligence/index.js +59 -0
- package/intelligence/orchestrator.js +964 -0
- package/intelligence/prd.js +447 -0
- package/intelligence/recommendation-weights.json +18 -0
- package/intelligence/recommendations.js +234 -0
- package/mcp/capabilities.js +71 -0
- package/mcp/contracts/mcp-contract.v1.json +497 -0
- package/mcp/registry.js +213 -0
- package/mcp/response-formatter.js +462 -0
- package/mcp/server.js +99 -0
- package/mcp/tools/agent-tool.js +137 -0
- package/mcp/tools/capabilities-tool.js +54 -0
- package/mcp/tools/context-tool.js +49 -0
- package/mcp/tools/dashboard-tool.js +58 -0
- package/mcp/tools/generate-tool.js +46 -0
- package/mcp/tools/loop-tool.js +134 -0
- package/mcp/tools/memory-tool.js +180 -0
- package/mcp/tools/orchestrator-tool.js +232 -0
- package/mcp/tools/plugin-tool.js +76 -0
- package/mcp/tools/quality-tool.js +47 -0
- package/mcp/tools/skill-tool.js +233 -0
- package/mcp/tools/telemetry-tool.js +95 -0
- package/mcp/tools/todo-tool.js +133 -0
- package/package.json +98 -0
- package/plugins/index.js +141 -0
- package/quality/index.js +380 -0
- package/quality/lint-budgets.json +19 -0
- package/skills/index.js +787 -0
- package/skills/patterns/README.md +163 -0
- package/skills/patterns/api/route-handler.md +217 -0
- package/skills/patterns/api/server-action.md +249 -0
- package/skills/patterns/auth/clerk.md +132 -0
- package/skills/patterns/database/prisma.md +180 -0
- package/skills/patterns/payments/stripe.md +272 -0
- package/skills/patterns/security/validation.md +268 -0
- package/skills/patterns/testing/vitest.md +307 -0
- package/templates/bootspring.config.js +83 -0
- package/templates/mcp.json +9 -0
package/cli/loop.js
ADDED
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Bootspring Loop CLI
|
|
5
|
+
*
|
|
6
|
+
* Autonomous task execution loop - spawns fresh AI instances
|
|
7
|
+
* iteratively until all PRD stories are complete.
|
|
8
|
+
*
|
|
9
|
+
* @package bootspring
|
|
10
|
+
* @module cli/loop
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { spawn, execSync } = require('child_process');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const prd = require('../intelligence/prd');
|
|
17
|
+
|
|
18
|
+
// Colors
|
|
19
|
+
const c = {
|
|
20
|
+
reset: '\x1b[0m',
|
|
21
|
+
bold: '\x1b[1m',
|
|
22
|
+
dim: '\x1b[2m',
|
|
23
|
+
green: '\x1b[32m',
|
|
24
|
+
blue: '\x1b[34m',
|
|
25
|
+
yellow: '\x1b[33m',
|
|
26
|
+
cyan: '\x1b[36m',
|
|
27
|
+
red: '\x1b[31m'
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Check if a tool is available
|
|
32
|
+
*/
|
|
33
|
+
function toolAvailable(tool) {
|
|
34
|
+
try {
|
|
35
|
+
execSync(`which ${tool}`, { stdio: 'ignore' });
|
|
36
|
+
return true;
|
|
37
|
+
} catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Detect available AI tool
|
|
44
|
+
*/
|
|
45
|
+
function detectTool() {
|
|
46
|
+
if (toolAvailable('claude')) return 'claude';
|
|
47
|
+
if (toolAvailable('amp')) return 'amp';
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Run the loop via shell script
|
|
53
|
+
*/
|
|
54
|
+
function runLoop(options = {}) {
|
|
55
|
+
const scriptPath = path.join(__dirname, '..', 'scripts', 'loop.sh');
|
|
56
|
+
|
|
57
|
+
if (!fs.existsSync(scriptPath)) {
|
|
58
|
+
console.error(`${c.red}Loop script not found: ${scriptPath}${c.reset}`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const args = [
|
|
63
|
+
options.iterations || '10',
|
|
64
|
+
'--tool', options.tool || 'claude'
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
if (options.prd) {
|
|
68
|
+
args.push('--prd', options.prd);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
console.log(`${c.cyan}Starting autonomous loop...${c.reset}`);
|
|
72
|
+
console.log(`${c.dim}Tool: ${options.tool || 'claude'} | Iterations: ${options.iterations || 10}${c.reset}\n`);
|
|
73
|
+
|
|
74
|
+
const child = spawn('bash', [scriptPath, ...args], {
|
|
75
|
+
stdio: 'inherit',
|
|
76
|
+
cwd: process.cwd()
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
child.on('close', (code) => {
|
|
80
|
+
process.exit(code);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Create a new PRD from template or markdown
|
|
86
|
+
*/
|
|
87
|
+
function createPRD(name, options = {}) {
|
|
88
|
+
const prdDir = prd.DEFAULT_PRD_DIR;
|
|
89
|
+
|
|
90
|
+
if (!fs.existsSync(prdDir)) {
|
|
91
|
+
fs.mkdirSync(prdDir, { recursive: true });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Check if markdown file provided
|
|
95
|
+
if (options.from && fs.existsSync(options.from)) {
|
|
96
|
+
const markdown = fs.readFileSync(options.from, 'utf-8');
|
|
97
|
+
const parsed = prd.parseMarkdownPRD(markdown);
|
|
98
|
+
parsed.name = name || parsed.name;
|
|
99
|
+
|
|
100
|
+
const outPath = prd.savePRD(parsed);
|
|
101
|
+
console.log(`${c.green}Created PRD from markdown: ${outPath}${c.reset}`);
|
|
102
|
+
console.log(`Stories: ${parsed.stories.length}`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Interactive template
|
|
107
|
+
const template = prd.createPRD(
|
|
108
|
+
name || 'new-feature',
|
|
109
|
+
'Describe your feature here',
|
|
110
|
+
[
|
|
111
|
+
{
|
|
112
|
+
title: 'Setup and configuration',
|
|
113
|
+
description: 'Initial setup required for the feature',
|
|
114
|
+
acceptance: ['Configuration file created', 'Dependencies installed']
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
title: 'Core implementation',
|
|
118
|
+
description: 'Main functionality implementation',
|
|
119
|
+
acceptance: ['Feature works as specified', 'Tests pass']
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
title: 'Documentation and cleanup',
|
|
123
|
+
description: 'Final polish and documentation',
|
|
124
|
+
acceptance: ['README updated', 'No lint errors']
|
|
125
|
+
}
|
|
126
|
+
]
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
const outPath = prd.savePRD(template);
|
|
130
|
+
console.log(`${c.green}Created PRD template: ${outPath}${c.reset}`);
|
|
131
|
+
console.log(`${c.dim}Edit the file to define your stories, then run: bootspring loop start${c.reset}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Show PRD status
|
|
136
|
+
*/
|
|
137
|
+
function showStatus(prdPath) {
|
|
138
|
+
const filePath = prdPath || path.join(prd.DEFAULT_PRD_DIR, prd.DEFAULT_PRD_FILE);
|
|
139
|
+
const data = prd.loadPRD(filePath);
|
|
140
|
+
|
|
141
|
+
if (!data) {
|
|
142
|
+
console.log(`${c.yellow}No PRD found at ${filePath}${c.reset}`);
|
|
143
|
+
console.log(`Create one with: bootspring loop prd <name>`);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const progress = prd.getProgress(data);
|
|
148
|
+
|
|
149
|
+
console.log(`\n${c.cyan}+--------------------------------------------------------+${c.reset}`);
|
|
150
|
+
console.log(`${c.cyan}|${c.reset}${c.bold} PRD: ${data.name.padEnd(47)}${c.reset}${c.cyan}|${c.reset}`);
|
|
151
|
+
console.log(`${c.cyan}+--------------------------------------------------------+${c.reset}\n`);
|
|
152
|
+
|
|
153
|
+
// Progress bar
|
|
154
|
+
const barWidth = 40;
|
|
155
|
+
const filled = Math.round((progress.percent / 100) * barWidth);
|
|
156
|
+
const bar = '█'.repeat(filled) + '░'.repeat(barWidth - filled);
|
|
157
|
+
console.log(` Progress: [${c.green}${bar}${c.reset}] ${progress.percent}%`);
|
|
158
|
+
console.log(` Complete: ${progress.complete}/${progress.total} | Pending: ${progress.pending} | Blocked: ${progress.blocked}\n`);
|
|
159
|
+
|
|
160
|
+
// Stories
|
|
161
|
+
console.log(`${c.bold}Stories:${c.reset}\n`);
|
|
162
|
+
|
|
163
|
+
for (const story of data.stories) {
|
|
164
|
+
let icon, color;
|
|
165
|
+
switch (story.status) {
|
|
166
|
+
case 'complete':
|
|
167
|
+
icon = '✓';
|
|
168
|
+
color = c.green;
|
|
169
|
+
break;
|
|
170
|
+
case 'blocked':
|
|
171
|
+
icon = '!';
|
|
172
|
+
color = c.red;
|
|
173
|
+
break;
|
|
174
|
+
case 'in_progress':
|
|
175
|
+
icon = '→';
|
|
176
|
+
color = c.yellow;
|
|
177
|
+
break;
|
|
178
|
+
default:
|
|
179
|
+
icon = ' ';
|
|
180
|
+
color = c.dim;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
console.log(` ${color}[${icon}]${c.reset} ${story.title}`);
|
|
184
|
+
if (story.status === 'blocked') {
|
|
185
|
+
console.log(` ${c.red}Blocked${c.reset}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Next up
|
|
190
|
+
const next = prd.getNextStory(data);
|
|
191
|
+
if (next) {
|
|
192
|
+
console.log(`\n${c.bold}Next:${c.reset} ${next.title}`);
|
|
193
|
+
} else if (progress.percent === 100) {
|
|
194
|
+
console.log(`\n${c.green}All stories complete!${c.reset}`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
console.log('');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Generate PRD skill for Claude Code
|
|
202
|
+
*/
|
|
203
|
+
function generateSkill() {
|
|
204
|
+
const skillDir = path.join(__dirname, '..', '.claude-plugin', 'skills');
|
|
205
|
+
|
|
206
|
+
if (!fs.existsSync(skillDir)) {
|
|
207
|
+
fs.mkdirSync(skillDir, { recursive: true });
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// PRD skill
|
|
211
|
+
const prdSkill = `# /prd - Generate Product Requirements Document
|
|
212
|
+
|
|
213
|
+
Generate a detailed PRD with right-sized user stories for autonomous execution.
|
|
214
|
+
|
|
215
|
+
## Instructions
|
|
216
|
+
|
|
217
|
+
When the user invokes /prd <feature-name>:
|
|
218
|
+
|
|
219
|
+
1. Ask clarifying questions about the feature
|
|
220
|
+
2. Generate a detailed PRD with 3-7 user stories
|
|
221
|
+
3. Each story should be:
|
|
222
|
+
- Completable in a single AI context window
|
|
223
|
+
- Have clear acceptance criteria
|
|
224
|
+
- Be independently testable
|
|
225
|
+
|
|
226
|
+
## Output Format
|
|
227
|
+
|
|
228
|
+
Create a file at \`tasks/prd-<feature-name>.md\` with this structure:
|
|
229
|
+
|
|
230
|
+
\`\`\`markdown
|
|
231
|
+
# <Feature Name>
|
|
232
|
+
|
|
233
|
+
<Brief description>
|
|
234
|
+
|
|
235
|
+
## Story 1: <Title>
|
|
236
|
+
|
|
237
|
+
<Description of what needs to be done>
|
|
238
|
+
|
|
239
|
+
### Acceptance Criteria
|
|
240
|
+
- [ ] Criteria 1
|
|
241
|
+
- [ ] Criteria 2
|
|
242
|
+
|
|
243
|
+
## Story 2: <Title>
|
|
244
|
+
...
|
|
245
|
+
\`\`\`
|
|
246
|
+
|
|
247
|
+
## Right-Sizing Guidelines
|
|
248
|
+
|
|
249
|
+
Stories should be small enough to complete in one session:
|
|
250
|
+
- Adding a database column + migration
|
|
251
|
+
- Creating a single API endpoint
|
|
252
|
+
- Building one UI component
|
|
253
|
+
- Writing tests for one module
|
|
254
|
+
|
|
255
|
+
Stories that are TOO LARGE:
|
|
256
|
+
- "Build the authentication system"
|
|
257
|
+
- "Implement payment processing"
|
|
258
|
+
- "Create the admin dashboard"
|
|
259
|
+
|
|
260
|
+
Break large features into multiple stories.
|
|
261
|
+
|
|
262
|
+
## After Generation
|
|
263
|
+
|
|
264
|
+
Tell the user:
|
|
265
|
+
1. Review the PRD in \`tasks/prd-<feature-name>.md\`
|
|
266
|
+
2. Convert to JSON: \`bootspring loop prd --from tasks/prd-<feature-name>.md\`
|
|
267
|
+
3. Start the loop: \`bootspring loop start\`
|
|
268
|
+
`;
|
|
269
|
+
|
|
270
|
+
fs.writeFileSync(path.join(skillDir, 'prd.md'), prdSkill);
|
|
271
|
+
|
|
272
|
+
// Loop skill
|
|
273
|
+
const loopSkill = `# /loop - Autonomous Task Loop
|
|
274
|
+
|
|
275
|
+
Start an autonomous execution loop that iterates through PRD stories.
|
|
276
|
+
|
|
277
|
+
## Prerequisites
|
|
278
|
+
|
|
279
|
+
- A \`tasks/prd.json\` file with defined stories
|
|
280
|
+
- Either \`claude\` or \`amp\` CLI installed
|
|
281
|
+
|
|
282
|
+
## Usage
|
|
283
|
+
|
|
284
|
+
/loop [iterations] [--tool claude|amp]
|
|
285
|
+
|
|
286
|
+
## What Happens
|
|
287
|
+
|
|
288
|
+
1. The loop reads the next incomplete story from \`tasks/prd.json\`
|
|
289
|
+
2. A fresh AI instance works on that single story
|
|
290
|
+
3. Quality gates run (tests, lint, typecheck)
|
|
291
|
+
4. On success: commit + mark story complete
|
|
292
|
+
5. Repeat until all stories done or max iterations reached
|
|
293
|
+
|
|
294
|
+
## Memory Model
|
|
295
|
+
|
|
296
|
+
Context persists through:
|
|
297
|
+
- Git commits (what changed)
|
|
298
|
+
- CLAUDE.md (project knowledge)
|
|
299
|
+
- prd.json (task status)
|
|
300
|
+
|
|
301
|
+
Each iteration starts with fresh context to avoid degradation.
|
|
302
|
+
|
|
303
|
+
## Commands
|
|
304
|
+
|
|
305
|
+
\`\`\`bash
|
|
306
|
+
bootspring loop start # Start the loop
|
|
307
|
+
bootspring loop status # Check PRD progress
|
|
308
|
+
bootspring loop prd <name> # Create new PRD
|
|
309
|
+
\`\`\`
|
|
310
|
+
`;
|
|
311
|
+
|
|
312
|
+
fs.writeFileSync(path.join(skillDir, 'loop.md'), loopSkill);
|
|
313
|
+
|
|
314
|
+
// Plugin manifest
|
|
315
|
+
const manifest = {
|
|
316
|
+
name: 'bootspring',
|
|
317
|
+
version: '1.0.0',
|
|
318
|
+
description: 'Intelligent scaffolding and autonomous task execution',
|
|
319
|
+
skills: ['prd', 'loop']
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const manifestPath = path.join(__dirname, '..', '.claude-plugin', 'manifest.json');
|
|
323
|
+
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
324
|
+
|
|
325
|
+
console.log(`${c.green}Generated Claude Code plugin:${c.reset}`);
|
|
326
|
+
console.log(` .claude-plugin/manifest.json`);
|
|
327
|
+
console.log(` .claude-plugin/skills/prd.md`);
|
|
328
|
+
console.log(` .claude-plugin/skills/loop.md`);
|
|
329
|
+
console.log(`\n${c.dim}Users can install with: /plugin add bootspring${c.reset}`);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Show help
|
|
334
|
+
*/
|
|
335
|
+
function showHelp() {
|
|
336
|
+
console.log(`
|
|
337
|
+
${c.bold}Bootspring Autonomous Loop${c.reset}
|
|
338
|
+
|
|
339
|
+
${c.cyan}Usage:${c.reset}
|
|
340
|
+
bootspring loop <command> [options]
|
|
341
|
+
|
|
342
|
+
${c.cyan}Commands:${c.reset}
|
|
343
|
+
start [n] [--tool] Start loop (n iterations, default 10)
|
|
344
|
+
status Show PRD progress
|
|
345
|
+
prd <name> Create new PRD template
|
|
346
|
+
prd --from <file> Create PRD from markdown
|
|
347
|
+
skill Generate Claude Code plugin skills
|
|
348
|
+
|
|
349
|
+
${c.cyan}Options:${c.reset}
|
|
350
|
+
--tool <claude|amp> AI tool to use (default: claude)
|
|
351
|
+
--prd <path> Path to prd.json
|
|
352
|
+
|
|
353
|
+
${c.cyan}Examples:${c.reset}
|
|
354
|
+
bootspring loop prd user-auth
|
|
355
|
+
bootspring loop start 20 --tool claude
|
|
356
|
+
bootspring loop status
|
|
357
|
+
|
|
358
|
+
${c.cyan}Workflow:${c.reset}
|
|
359
|
+
1. Create PRD: bootspring loop prd my-feature
|
|
360
|
+
2. Edit stories: Edit tasks/prd.json
|
|
361
|
+
3. Start loop: bootspring loop start
|
|
362
|
+
4. Monitor: bootspring loop status
|
|
363
|
+
`);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Main CLI handler
|
|
368
|
+
*/
|
|
369
|
+
function main(args) {
|
|
370
|
+
const command = args[0] || 'help';
|
|
371
|
+
|
|
372
|
+
switch (command) {
|
|
373
|
+
case 'start': {
|
|
374
|
+
const tool = detectTool();
|
|
375
|
+
if (!tool) {
|
|
376
|
+
console.error(`${c.red}No AI tool found. Install claude or amp CLI.${c.reset}`);
|
|
377
|
+
process.exit(1);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const iterations = args.find(a => /^\d+$/.test(a)) || '10';
|
|
381
|
+
const toolArg = args.includes('--tool') ?
|
|
382
|
+
args[args.indexOf('--tool') + 1] : tool;
|
|
383
|
+
const prdArg = args.includes('--prd') ?
|
|
384
|
+
args[args.indexOf('--prd') + 1] : null;
|
|
385
|
+
|
|
386
|
+
runLoop({ iterations, tool: toolArg, prd: prdArg });
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
case 'status':
|
|
391
|
+
showStatus(args[1]);
|
|
392
|
+
break;
|
|
393
|
+
|
|
394
|
+
case 'prd': {
|
|
395
|
+
// Skip the 'prd' command itself, find the feature name
|
|
396
|
+
const prdArgs = args.slice(1);
|
|
397
|
+
const name = prdArgs.find(a => !a.startsWith('--'));
|
|
398
|
+
const fromFile = prdArgs.includes('--from') ?
|
|
399
|
+
prdArgs[prdArgs.indexOf('--from') + 1] : null;
|
|
400
|
+
|
|
401
|
+
createPRD(name, { from: fromFile });
|
|
402
|
+
break;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
case 'skill':
|
|
406
|
+
case 'skills':
|
|
407
|
+
generateSkill();
|
|
408
|
+
break;
|
|
409
|
+
|
|
410
|
+
case 'help':
|
|
411
|
+
default:
|
|
412
|
+
showHelp();
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// CLI execution
|
|
417
|
+
if (require.main === module) {
|
|
418
|
+
main(process.argv.slice(2));
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
module.exports = { main, runLoop, createPRD, showStatus };
|
package/cli/mcp.js
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootspring MCP Command
|
|
3
|
+
* Control the MCP server
|
|
4
|
+
*
|
|
5
|
+
* @package bootspring
|
|
6
|
+
* @command mcp
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { spawn } = require('child_process');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const config = require('../core/config');
|
|
12
|
+
const utils = require('../core/utils');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Start MCP server (for direct invocation by Claude Code)
|
|
16
|
+
*/
|
|
17
|
+
function startServer() {
|
|
18
|
+
// When called directly by MCP, just require the server module
|
|
19
|
+
// This allows Claude Code to invoke it via stdio
|
|
20
|
+
require('../mcp/server.js');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Show MCP status and configuration info
|
|
25
|
+
*/
|
|
26
|
+
function showStatus() {
|
|
27
|
+
const cfg = config.load();
|
|
28
|
+
|
|
29
|
+
console.log(`
|
|
30
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring MCP Server${utils.COLORS.reset}
|
|
31
|
+
${utils.COLORS.dim}Model Context Protocol integration${utils.COLORS.reset}
|
|
32
|
+
|
|
33
|
+
${utils.COLORS.bold}Status${utils.COLORS.reset}
|
|
34
|
+
The MCP server runs on-demand when Claude Code invokes it.
|
|
35
|
+
It does not run as a persistent background service.
|
|
36
|
+
|
|
37
|
+
${utils.COLORS.bold}Configuration${utils.COLORS.reset}
|
|
38
|
+
Project Root: ${cfg._projectRoot}
|
|
39
|
+
Config File: ${cfg._configPath || 'not found'}
|
|
40
|
+
|
|
41
|
+
${utils.COLORS.bold}Available Tools${utils.COLORS.reset}
|
|
42
|
+
${utils.COLORS.cyan}bootspring_context${utils.COLORS.reset} - Show/validate project context
|
|
43
|
+
${utils.COLORS.cyan}bootspring_agent${utils.COLORS.reset} - Work with specialized agents
|
|
44
|
+
${utils.COLORS.cyan}bootspring_todo${utils.COLORS.reset} - Manage todo items
|
|
45
|
+
${utils.COLORS.cyan}bootspring_skill${utils.COLORS.reset} - Search code patterns
|
|
46
|
+
${utils.COLORS.cyan}bootspring_quality${utils.COLORS.reset} - Run quality gates
|
|
47
|
+
${utils.COLORS.cyan}bootspring_generate${utils.COLORS.reset} - Regenerate context
|
|
48
|
+
${utils.COLORS.cyan}bootspring_plugin${utils.COLORS.reset} - Manage plugins
|
|
49
|
+
${utils.COLORS.cyan}bootspring_dashboard${utils.COLORS.reset} - Dashboard info
|
|
50
|
+
|
|
51
|
+
${utils.COLORS.bold}Resources${utils.COLORS.reset}
|
|
52
|
+
${utils.COLORS.dim}bootspring://context/project${utils.COLORS.reset} - Project context JSON
|
|
53
|
+
${utils.COLORS.dim}bootspring://context/claude.md${utils.COLORS.reset} - Generated AI context
|
|
54
|
+
${utils.COLORS.dim}bootspring://context/todo.md${utils.COLORS.reset} - Todo list
|
|
55
|
+
${utils.COLORS.dim}bootspring://agents/list${utils.COLORS.reset} - Available agents
|
|
56
|
+
|
|
57
|
+
${utils.COLORS.bold}Setup${utils.COLORS.reset}
|
|
58
|
+
Ensure .mcp.json exists with:
|
|
59
|
+
${utils.COLORS.dim}{
|
|
60
|
+
"mcpServers": {
|
|
61
|
+
"bootspring": {
|
|
62
|
+
"command": "npx",
|
|
63
|
+
"args": ["bootspring", "mcp"],
|
|
64
|
+
"env": {}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}${utils.COLORS.reset}
|
|
68
|
+
|
|
69
|
+
${utils.COLORS.bold}Test${utils.COLORS.reset}
|
|
70
|
+
Run: ${utils.COLORS.cyan}bootspring mcp test${utils.COLORS.reset}
|
|
71
|
+
`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Test MCP tools
|
|
76
|
+
*/
|
|
77
|
+
async function testMcp() {
|
|
78
|
+
console.log(`
|
|
79
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring MCP Test${utils.COLORS.reset}
|
|
80
|
+
`);
|
|
81
|
+
|
|
82
|
+
const config = require('../core/config');
|
|
83
|
+
const context = require('../core/context');
|
|
84
|
+
|
|
85
|
+
// Test config loading
|
|
86
|
+
const spinner1 = utils.createSpinner('Testing config loader').start();
|
|
87
|
+
try {
|
|
88
|
+
const cfg = config.load();
|
|
89
|
+
if (cfg.project) {
|
|
90
|
+
spinner1.succeed(`Config loaded: ${cfg.project.name}`);
|
|
91
|
+
} else {
|
|
92
|
+
spinner1.warn('Config loaded but missing project info');
|
|
93
|
+
}
|
|
94
|
+
} catch (error) {
|
|
95
|
+
spinner1.fail(`Config error: ${error.message}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Test context
|
|
99
|
+
const spinner2 = utils.createSpinner('Testing context detector').start();
|
|
100
|
+
try {
|
|
101
|
+
const cfg = config.load();
|
|
102
|
+
const ctx = context.get({ config: cfg });
|
|
103
|
+
spinner2.succeed(`Context loaded: phase=${ctx.state.phase}, health=${ctx.state.health}`);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
spinner2.fail(`Context error: ${error.message}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Test hooks
|
|
109
|
+
const spinner3 = utils.createSpinner('Testing context detection hooks').start();
|
|
110
|
+
try {
|
|
111
|
+
const detector = require('../hooks/context-detector');
|
|
112
|
+
const result = detector.detectContext('implement user authentication with Clerk');
|
|
113
|
+
if (result.matches.length > 0) {
|
|
114
|
+
spinner3.succeed(`Detection works: found ${result.matches.length} matches (${result.suggestedAgents.join(', ')})`);
|
|
115
|
+
} else {
|
|
116
|
+
spinner3.warn('Detection works but found no matches');
|
|
117
|
+
}
|
|
118
|
+
} catch (error) {
|
|
119
|
+
spinner3.fail(`Detection error: ${error.message}`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Test prompt enhancer
|
|
123
|
+
const spinner4 = utils.createSpinner('Testing prompt enhancer').start();
|
|
124
|
+
try {
|
|
125
|
+
const detector = require('../hooks/context-detector');
|
|
126
|
+
const enhancer = require('../hooks/prompt-enhancer');
|
|
127
|
+
const detection = detector.detectContext('create a database schema for users');
|
|
128
|
+
const enhancement = enhancer.enhance('create a database schema', detection, {});
|
|
129
|
+
if (enhancement) {
|
|
130
|
+
spinner4.succeed(`Enhancer works: category=${enhancement.category}`);
|
|
131
|
+
} else {
|
|
132
|
+
spinner4.warn('Enhancer works but produced no enhancement');
|
|
133
|
+
}
|
|
134
|
+
} catch (error) {
|
|
135
|
+
spinner4.fail(`Enhancer error: ${error.message}`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
console.log(`
|
|
139
|
+
${utils.COLORS.bold}Summary${utils.COLORS.reset}
|
|
140
|
+
All core MCP components are functional.
|
|
141
|
+
The server will work when invoked by Claude Code.
|
|
142
|
+
|
|
143
|
+
${utils.COLORS.dim}Tip: Use the tools via Claude Code for full integration testing${utils.COLORS.reset}
|
|
144
|
+
`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Generate .mcp.json configuration
|
|
149
|
+
*/
|
|
150
|
+
function generateConfig() {
|
|
151
|
+
const cfg = config.load();
|
|
152
|
+
const mcpPath = path.join(cfg._projectRoot, '.mcp.json');
|
|
153
|
+
|
|
154
|
+
const mcpConfig = {
|
|
155
|
+
mcpServers: {
|
|
156
|
+
bootspring: {
|
|
157
|
+
command: 'npx',
|
|
158
|
+
args: ['bootspring', 'mcp'],
|
|
159
|
+
env: {}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const spinner = utils.createSpinner('Generating .mcp.json').start();
|
|
165
|
+
|
|
166
|
+
if (utils.writeFile(mcpPath, JSON.stringify(mcpConfig, null, 2))) {
|
|
167
|
+
spinner.succeed(`Created ${mcpPath}`);
|
|
168
|
+
console.log(`
|
|
169
|
+
${utils.COLORS.dim}Contents:${utils.COLORS.reset}
|
|
170
|
+
${JSON.stringify(mcpConfig, null, 2)}
|
|
171
|
+
`);
|
|
172
|
+
} else {
|
|
173
|
+
spinner.fail('Failed to create .mcp.json');
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Show MCP help
|
|
179
|
+
*/
|
|
180
|
+
function showHelp() {
|
|
181
|
+
console.log(`
|
|
182
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring MCP${utils.COLORS.reset}
|
|
183
|
+
${utils.COLORS.dim}Model Context Protocol server control${utils.COLORS.reset}
|
|
184
|
+
|
|
185
|
+
${utils.COLORS.bold}Usage:${utils.COLORS.reset}
|
|
186
|
+
bootspring mcp [command]
|
|
187
|
+
|
|
188
|
+
${utils.COLORS.bold}Commands:${utils.COLORS.reset}
|
|
189
|
+
${utils.COLORS.cyan}(default)${utils.COLORS.reset} Start MCP server (used by Claude Code)
|
|
190
|
+
${utils.COLORS.cyan}status${utils.COLORS.reset} Show MCP configuration and tools
|
|
191
|
+
${utils.COLORS.cyan}test${utils.COLORS.reset} Test MCP components
|
|
192
|
+
${utils.COLORS.cyan}config${utils.COLORS.reset} Generate .mcp.json file
|
|
193
|
+
${utils.COLORS.cyan}help${utils.COLORS.reset} Show this help
|
|
194
|
+
|
|
195
|
+
${utils.COLORS.bold}Examples:${utils.COLORS.reset}
|
|
196
|
+
bootspring mcp status # Check configuration
|
|
197
|
+
bootspring mcp test # Test components
|
|
198
|
+
bootspring mcp config # Generate .mcp.json
|
|
199
|
+
`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Run MCP command
|
|
204
|
+
*/
|
|
205
|
+
async function run(args) {
|
|
206
|
+
const subcommand = args[0];
|
|
207
|
+
|
|
208
|
+
// Default: start the MCP server (for Claude Code invocation)
|
|
209
|
+
if (!subcommand) {
|
|
210
|
+
startServer();
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
switch (subcommand) {
|
|
215
|
+
case 'status':
|
|
216
|
+
showStatus();
|
|
217
|
+
break;
|
|
218
|
+
|
|
219
|
+
case 'test':
|
|
220
|
+
await testMcp();
|
|
221
|
+
break;
|
|
222
|
+
|
|
223
|
+
case 'config':
|
|
224
|
+
case 'init':
|
|
225
|
+
case 'setup':
|
|
226
|
+
generateConfig();
|
|
227
|
+
break;
|
|
228
|
+
|
|
229
|
+
case 'help':
|
|
230
|
+
case '-h':
|
|
231
|
+
case '--help':
|
|
232
|
+
showHelp();
|
|
233
|
+
break;
|
|
234
|
+
|
|
235
|
+
default:
|
|
236
|
+
utils.print.error(`Unknown subcommand: ${subcommand}`);
|
|
237
|
+
showHelp();
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
module.exports = { run, startServer, showStatus, testMcp };
|