@girardmedia/bootspring 2.1.2 → 2.2.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/bin/bootspring.js +102 -82
- package/generators/api-docs.js +3 -3
- package/generators/decisions.js +14 -14
- package/generators/health.js +6 -6
- package/generators/sprint.js +4 -4
- package/generators/templates/build-planning.template.js +2 -2
- package/generators/visual-doc-generator.js +1 -1
- package/package.json +2 -15
- package/cli/agent.js +0 -799
- package/cli/auth.js +0 -896
- package/cli/billing.js +0 -320
- package/cli/build.js +0 -1442
- package/cli/dashboard.js +0 -123
- package/cli/init.js +0 -669
- package/cli/mcp.js +0 -240
- package/cli/orchestrator.js +0 -240
- package/cli/project.js +0 -825
- package/cli/quality.js +0 -281
- package/cli/skill.js +0 -503
- package/cli/switch.js +0 -453
- package/cli/todo.js +0 -629
- package/cli/update.js +0 -132
package/cli/init.js
DELETED
|
@@ -1,669 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bootspring Init Command
|
|
3
|
-
* Initialize Bootspring in a new or existing project
|
|
4
|
-
*
|
|
5
|
-
* Now with questionnaire-driven setup for a magical experience!
|
|
6
|
-
*
|
|
7
|
-
* @package bootspring
|
|
8
|
-
* @command init
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const path = require('path');
|
|
12
|
-
const fs = require('fs');
|
|
13
|
-
const readline = require('readline');
|
|
14
|
-
const { execSync } = require('child_process');
|
|
15
|
-
const config = require('../core/config');
|
|
16
|
-
const utils = require('../core/utils');
|
|
17
|
-
const contextLoader = require('../core/context-loader');
|
|
18
|
-
const { getManagedMcpServerConfig, ensureProjectMcpConfig } = require('../core/mcp-config');
|
|
19
|
-
const { runQuestionnaire } = require('../generators/questionnaire');
|
|
20
|
-
const claudeTemplate = require('../generators/templates/claude.template');
|
|
21
|
-
const seedTemplate = require('../generators/templates/seed.template');
|
|
22
|
-
const planningTemplate = require('../generators/templates/planning.template');
|
|
23
|
-
const api = require('../core/api-client');
|
|
24
|
-
const { requireProjectSelection } = require('./auth');
|
|
25
|
-
// Presets available via runQuestionnaire('preset-name')
|
|
26
|
-
|
|
27
|
-
// Available options (for legacy quick mode)
|
|
28
|
-
const FRAMEWORKS = ['nextjs', 'remix', 'nuxt', 'sveltekit', 'express', 'fastify', 'other'];
|
|
29
|
-
const LANGUAGES = ['typescript', 'javascript'];
|
|
30
|
-
const DATABASES = ['postgresql', 'mysql', 'mongodb', 'sqlite', 'none'];
|
|
31
|
-
const HOSTINGS = ['vercel', 'netlify', 'railway', 'fly', 'aws', 'self-hosted'];
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Create readline interface for interactive prompts
|
|
35
|
-
*/
|
|
36
|
-
function createPrompt() {
|
|
37
|
-
return readline.createInterface({
|
|
38
|
-
input: process.stdin,
|
|
39
|
-
output: process.stdout
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Ask a question and return the answer
|
|
45
|
-
*/
|
|
46
|
-
function ask(rl, question, defaultValue = '') {
|
|
47
|
-
const prompt = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;
|
|
48
|
-
return new Promise((resolve) => {
|
|
49
|
-
rl.question(prompt, (answer) => {
|
|
50
|
-
resolve(answer.trim() || defaultValue);
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Ask a multiple choice question
|
|
57
|
-
*/
|
|
58
|
-
function askChoice(rl, question, options, defaultIndex = 0) {
|
|
59
|
-
return new Promise((resolve) => {
|
|
60
|
-
console.log(`\n${question}`);
|
|
61
|
-
options.forEach((opt, i) => {
|
|
62
|
-
const marker = i === defaultIndex ? `${utils.COLORS.cyan}→${utils.COLORS.reset}` : ' ';
|
|
63
|
-
console.log(` ${marker} ${i + 1}. ${opt}`);
|
|
64
|
-
});
|
|
65
|
-
rl.question(`\nSelect [1-${options.length}] (default: ${defaultIndex + 1}): `, (answer) => {
|
|
66
|
-
const index = parseInt(answer, 10) - 1;
|
|
67
|
-
if (index >= 0 && index < options.length) {
|
|
68
|
-
resolve(options[index]);
|
|
69
|
-
} else {
|
|
70
|
-
resolve(options[defaultIndex]);
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Ask yes/no question
|
|
78
|
-
*/
|
|
79
|
-
function askYesNo(rl, question, defaultYes = true) {
|
|
80
|
-
const hint = defaultYes ? '[Y/n]' : '[y/N]';
|
|
81
|
-
return new Promise((resolve) => {
|
|
82
|
-
rl.question(`${question} ${hint}: `, (answer) => {
|
|
83
|
-
const a = answer.trim().toLowerCase();
|
|
84
|
-
if (a === '') {
|
|
85
|
-
resolve(defaultYes);
|
|
86
|
-
} else {
|
|
87
|
-
resolve(a === 'y' || a === 'yes');
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Check if bootspring is installed globally
|
|
95
|
-
*/
|
|
96
|
-
function isGloballyInstalled() {
|
|
97
|
-
try {
|
|
98
|
-
execSync('npm list -g @girardmedia/bootspring', { stdio: 'pipe', encoding: 'utf-8' });
|
|
99
|
-
return true;
|
|
100
|
-
} catch {
|
|
101
|
-
// Also check for 'bootspring' without scope
|
|
102
|
-
try {
|
|
103
|
-
execSync('npm list -g bootspring', { stdio: 'pipe', encoding: 'utf-8' });
|
|
104
|
-
return true;
|
|
105
|
-
} catch {
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Get global Claude Code settings path
|
|
113
|
-
*/
|
|
114
|
-
function getGlobalMcpPath() {
|
|
115
|
-
const home = process.env.HOME || process.env.USERPROFILE;
|
|
116
|
-
const platform = process.platform;
|
|
117
|
-
|
|
118
|
-
if (platform === 'darwin') {
|
|
119
|
-
return path.join(home, '.config', 'claude-code', 'settings.json');
|
|
120
|
-
} else if (platform === 'win32') {
|
|
121
|
-
return path.join(home, 'AppData', 'Roaming', 'claude-code', 'settings.json');
|
|
122
|
-
} else {
|
|
123
|
-
return path.join(home, '.config', 'claude-code', 'settings.json');
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Check if MCP is configured globally
|
|
129
|
-
*/
|
|
130
|
-
function isGlobalMcpConfigured() {
|
|
131
|
-
const globalPath = getGlobalMcpPath();
|
|
132
|
-
if (!utils.fileExists(globalPath)) {
|
|
133
|
-
return false;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
try {
|
|
137
|
-
const content = fs.readFileSync(globalPath, 'utf-8');
|
|
138
|
-
const settings = JSON.parse(content);
|
|
139
|
-
return settings?.mcpServers?.bootspring !== undefined;
|
|
140
|
-
} catch {
|
|
141
|
-
return false;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Detect project info from existing files
|
|
147
|
-
*/
|
|
148
|
-
function detectProject(projectRoot) {
|
|
149
|
-
const detected = {
|
|
150
|
-
name: path.basename(projectRoot),
|
|
151
|
-
framework: 'nextjs',
|
|
152
|
-
language: 'typescript',
|
|
153
|
-
database: 'postgresql',
|
|
154
|
-
hosting: 'vercel'
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
// Check package.json
|
|
158
|
-
const pkg = utils.getPackageJson(projectRoot);
|
|
159
|
-
if (pkg) {
|
|
160
|
-
detected.name = pkg.name || detected.name;
|
|
161
|
-
|
|
162
|
-
// Detect framework
|
|
163
|
-
if (pkg.dependencies?.next || pkg.devDependencies?.next) {
|
|
164
|
-
detected.framework = 'nextjs';
|
|
165
|
-
} else if (pkg.dependencies?.remix || pkg.devDependencies?.remix) {
|
|
166
|
-
detected.framework = 'remix';
|
|
167
|
-
} else if (pkg.dependencies?.nuxt || pkg.devDependencies?.nuxt) {
|
|
168
|
-
detected.framework = 'nuxt';
|
|
169
|
-
} else if (pkg.dependencies?.['@sveltejs/kit']) {
|
|
170
|
-
detected.framework = 'sveltekit';
|
|
171
|
-
} else if (pkg.dependencies?.express) {
|
|
172
|
-
detected.framework = 'express';
|
|
173
|
-
} else if (pkg.dependencies?.fastify) {
|
|
174
|
-
detected.framework = 'fastify';
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Detect database
|
|
178
|
-
if (pkg.dependencies?.['@prisma/client']) {
|
|
179
|
-
detected.database = 'postgresql';
|
|
180
|
-
} else if (pkg.dependencies?.mongoose) {
|
|
181
|
-
detected.database = 'mongodb';
|
|
182
|
-
} else if (pkg.dependencies?.mysql2) {
|
|
183
|
-
detected.database = 'mysql';
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Detect TypeScript
|
|
188
|
-
if (utils.fileExists(path.join(projectRoot, 'tsconfig.json'))) {
|
|
189
|
-
detected.language = 'typescript';
|
|
190
|
-
} else {
|
|
191
|
-
detected.language = 'javascript';
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Detect Vercel
|
|
195
|
-
if (utils.fileExists(path.join(projectRoot, 'vercel.json'))) {
|
|
196
|
-
detected.hosting = 'vercel';
|
|
197
|
-
} else if (utils.fileExists(path.join(projectRoot, 'netlify.toml'))) {
|
|
198
|
-
detected.hosting = 'netlify';
|
|
199
|
-
} else if (utils.fileExists(path.join(projectRoot, 'fly.toml'))) {
|
|
200
|
-
detected.hosting = 'fly';
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return detected;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Generate CLAUDE.md content
|
|
208
|
-
*/
|
|
209
|
-
function generateClaudeMd(projectConfig) {
|
|
210
|
-
const date = utils.formatDate();
|
|
211
|
-
|
|
212
|
-
return `# ${projectConfig.project.name} - AI Context
|
|
213
|
-
|
|
214
|
-
**Generated by**: Bootspring v1.0.0
|
|
215
|
-
**Last Updated**: ${date}
|
|
216
|
-
**Documentation**: https://bootspring.com/docs
|
|
217
|
-
|
|
218
|
-
---
|
|
219
|
-
|
|
220
|
-
## Project Overview
|
|
221
|
-
|
|
222
|
-
${projectConfig.project.description || 'A project scaffolded with Bootspring.'}
|
|
223
|
-
|
|
224
|
-
---
|
|
225
|
-
|
|
226
|
-
## Tech Stack
|
|
227
|
-
|
|
228
|
-
- **Framework**: ${projectConfig.stack.framework}
|
|
229
|
-
- **Language**: ${projectConfig.stack.language}
|
|
230
|
-
- **Database**: ${projectConfig.stack.database}
|
|
231
|
-
- **Hosting**: ${projectConfig.stack.hosting}
|
|
232
|
-
|
|
233
|
-
---
|
|
234
|
-
|
|
235
|
-
## Bootspring Commands
|
|
236
|
-
|
|
237
|
-
Use these commands with your AI assistant:
|
|
238
|
-
|
|
239
|
-
| Command | Description |
|
|
240
|
-
|---------|-------------|
|
|
241
|
-
| \`bootspring todo add "task"\` | Add a new todo item |
|
|
242
|
-
| \`bootspring todo list\` | List all todos |
|
|
243
|
-
| \`bootspring todo done <id>\` | Mark todo as complete |
|
|
244
|
-
| \`bootspring agent list\` | List available agents |
|
|
245
|
-
| \`bootspring agent invoke <name>\` | Get specialized help |
|
|
246
|
-
| \`bootspring skill search <query>\` | Find code patterns |
|
|
247
|
-
| \`bootspring dashboard\` | Start real-time dashboard |
|
|
248
|
-
| \`bootspring quality pre-commit\` | Run quality checks |
|
|
249
|
-
|
|
250
|
-
---
|
|
251
|
-
|
|
252
|
-
## Enabled Plugins
|
|
253
|
-
|
|
254
|
-
${Object.entries(projectConfig.plugins)
|
|
255
|
-
.filter(([_key, p]) => p.enabled !== false)
|
|
256
|
-
.map(([name, plugin]) => `### ${name.charAt(0).toUpperCase() + name.slice(1)}
|
|
257
|
-
- Provider: ${plugin.provider || 'default'}
|
|
258
|
-
- Features: ${(plugin.features || []).join(', ') || 'default'}`)
|
|
259
|
-
.join('\n\n')}
|
|
260
|
-
|
|
261
|
-
---
|
|
262
|
-
|
|
263
|
-
## Development Workflow
|
|
264
|
-
|
|
265
|
-
1. **Start dashboard**: \`bootspring dashboard\` for real-time visibility
|
|
266
|
-
2. **Track work**: Use \`bootspring todo\` to manage tasks
|
|
267
|
-
3. **Get help**: Invoke agents for specialized expertise
|
|
268
|
-
4. **Quality**: Run \`bootspring quality pre-commit\` before committing
|
|
269
|
-
|
|
270
|
-
---
|
|
271
|
-
|
|
272
|
-
## Custom Instructions
|
|
273
|
-
|
|
274
|
-
- Use Server Components by default (Next.js)
|
|
275
|
-
- Prefer Server Actions over API routes for mutations
|
|
276
|
-
- Use Zod for all input validation
|
|
277
|
-
- Follow existing file naming conventions
|
|
278
|
-
- Keep components small and focused
|
|
279
|
-
- Write tests for new features
|
|
280
|
-
- Never expose API keys to client-side code
|
|
281
|
-
|
|
282
|
-
---
|
|
283
|
-
|
|
284
|
-
*Generated by [Bootspring](https://bootspring.com) - Development scaffolding with intelligence*
|
|
285
|
-
`;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* Generate todo.md content
|
|
290
|
-
*/
|
|
291
|
-
function generateTodoMd(projectName) {
|
|
292
|
-
const date = utils.formatDate();
|
|
293
|
-
|
|
294
|
-
return `# ${projectName} - Todo List
|
|
295
|
-
|
|
296
|
-
> Last updated: ${date}
|
|
297
|
-
|
|
298
|
-
## In Progress
|
|
299
|
-
|
|
300
|
-
- [ ] Complete project setup
|
|
301
|
-
|
|
302
|
-
## Pending
|
|
303
|
-
|
|
304
|
-
- [ ] Configure environment variables
|
|
305
|
-
- [ ] Set up database schema
|
|
306
|
-
- [ ] Implement core features
|
|
307
|
-
|
|
308
|
-
## Completed
|
|
309
|
-
|
|
310
|
-
`;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Run init command
|
|
315
|
-
*/
|
|
316
|
-
async function run(args) {
|
|
317
|
-
const parsedArgs = utils.parseArgs(args);
|
|
318
|
-
const force = parsedArgs.force || parsedArgs.f;
|
|
319
|
-
const quick = parsedArgs.quick || parsedArgs.q;
|
|
320
|
-
const listPresetsFlag = parsedArgs['list-presets'] || parsedArgs.presets;
|
|
321
|
-
const presetArg = parsedArgs.preset || (quick ? 'minimal' : null);
|
|
322
|
-
const projectRoot = config.findProjectRoot();
|
|
323
|
-
|
|
324
|
-
// Handle --list-presets flag (no auth required for help)
|
|
325
|
-
if (listPresetsFlag) {
|
|
326
|
-
console.log(`
|
|
327
|
-
${utils.COLORS.cyan}${utils.COLORS.bold}Available Config Presets${utils.COLORS.reset}
|
|
328
|
-
|
|
329
|
-
${config.listPresets({ verbose: true }).map(p =>
|
|
330
|
-
` ${utils.COLORS.bold}${p.key}${utils.COLORS.reset}
|
|
331
|
-
${p.description}
|
|
332
|
-
${utils.COLORS.dim}Tags: ${p.tags.join(', ')}${p.extends ? ` | Extends: ${p.extends}` : ''}${utils.COLORS.reset}
|
|
333
|
-
`).join('\n')}
|
|
334
|
-
${utils.COLORS.dim}Usage: bootspring init --preset <preset-name>
|
|
335
|
-
bootspring init --preset saas-starter,ai-app (combine presets)${utils.COLORS.reset}
|
|
336
|
-
`);
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// Require authentication before proceeding
|
|
341
|
-
try {
|
|
342
|
-
api.requireAuth();
|
|
343
|
-
} catch (error) {
|
|
344
|
-
utils.print.error(error.message);
|
|
345
|
-
utils.print.dim('Run: bootspring auth login');
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// Ensure project is selected
|
|
350
|
-
await requireProjectSelection();
|
|
351
|
-
|
|
352
|
-
// Parse and validate preset(s)
|
|
353
|
-
let configPresets = null;
|
|
354
|
-
if (presetArg && !['minimal', 'standard', 'full'].includes(presetArg)) {
|
|
355
|
-
// This is a config preset (saas-starter, api-only, etc.), not a questionnaire preset
|
|
356
|
-
configPresets = config.parsePresetString(presetArg);
|
|
357
|
-
const validation = config.validatePresets(configPresets);
|
|
358
|
-
if (!validation.valid) {
|
|
359
|
-
utils.print.error('Invalid preset(s):');
|
|
360
|
-
validation.errors.forEach(err => utils.print.dim(` - ${err}`));
|
|
361
|
-
console.log(`\n${utils.COLORS.dim}Run 'bootspring init --list-presets' to see available presets${utils.COLORS.reset}`);
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// Determine questionnaire preset (for interactive mode)
|
|
367
|
-
const questionnairePreset = !configPresets && presetArg && ['minimal', 'standard', 'full'].includes(presetArg)
|
|
368
|
-
? presetArg
|
|
369
|
-
: null;
|
|
370
|
-
|
|
371
|
-
console.log(`
|
|
372
|
-
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Init${utils.COLORS.reset}
|
|
373
|
-
${utils.COLORS.dim}Development scaffolding with intelligence${utils.COLORS.reset}
|
|
374
|
-
`);
|
|
375
|
-
|
|
376
|
-
// Check if already initialized
|
|
377
|
-
const existingConfig = config.findConfigFile(projectRoot);
|
|
378
|
-
if (existingConfig && !force) {
|
|
379
|
-
utils.print.warning(`Project already initialized at ${projectRoot}`);
|
|
380
|
-
utils.print.dim('Use --force to reinitialize');
|
|
381
|
-
return;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// Detect existing project settings
|
|
385
|
-
const detected = detectProject(projectRoot);
|
|
386
|
-
utils.print.info(`Detected project: ${detected.name}`);
|
|
387
|
-
utils.print.dim(`Root: ${projectRoot}`);
|
|
388
|
-
|
|
389
|
-
let projectConfig;
|
|
390
|
-
|
|
391
|
-
// Config preset mode (saas-starter, api-only, etc.)
|
|
392
|
-
if (configPresets && configPresets.length > 0) {
|
|
393
|
-
const presetDisplay = configPresets.join(' + ');
|
|
394
|
-
utils.print.info(`Using config preset(s): ${presetDisplay}`);
|
|
395
|
-
|
|
396
|
-
try {
|
|
397
|
-
// Apply presets (combining if multiple)
|
|
398
|
-
projectConfig = config.applyPreset(configPresets, {
|
|
399
|
-
project: {
|
|
400
|
-
name: detected.name,
|
|
401
|
-
description: '',
|
|
402
|
-
version: '1.0.0'
|
|
403
|
-
}
|
|
404
|
-
});
|
|
405
|
-
projectConfig._configPreset = configPresets.join(',');
|
|
406
|
-
projectConfig._preset = 'config'; // Mark as config preset for template generation
|
|
407
|
-
} catch (error) {
|
|
408
|
-
utils.print.error(`Failed to apply preset: ${error.message}`);
|
|
409
|
-
return;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
// Questionnaire preset mode (minimal, standard, full)
|
|
413
|
-
else if (questionnairePreset) {
|
|
414
|
-
utils.print.info(`Using ${questionnairePreset} preset questionnaire`);
|
|
415
|
-
|
|
416
|
-
try {
|
|
417
|
-
projectConfig = await runQuestionnaire(questionnairePreset);
|
|
418
|
-
projectConfig._preset = questionnairePreset;
|
|
419
|
-
} catch (error) {
|
|
420
|
-
utils.print.error(`Questionnaire failed: ${error.message}`);
|
|
421
|
-
return;
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
// Quick mode - use detected values (legacy behavior)
|
|
425
|
-
else if (quick) {
|
|
426
|
-
utils.print.info('Using detected settings (quick mode)');
|
|
427
|
-
projectConfig = {
|
|
428
|
-
project: {
|
|
429
|
-
name: detected.name,
|
|
430
|
-
description: '',
|
|
431
|
-
version: '1.0.0'
|
|
432
|
-
},
|
|
433
|
-
stack: {
|
|
434
|
-
framework: detected.framework,
|
|
435
|
-
language: detected.language,
|
|
436
|
-
database: detected.database,
|
|
437
|
-
hosting: detected.hosting
|
|
438
|
-
},
|
|
439
|
-
plugins: {
|
|
440
|
-
auth: { enabled: false, provider: 'clerk' },
|
|
441
|
-
payments: { enabled: false, provider: 'stripe' },
|
|
442
|
-
database: { enabled: detected.database !== 'none', provider: 'prisma' },
|
|
443
|
-
testing: { enabled: true, provider: 'vitest' },
|
|
444
|
-
security: { enabled: true },
|
|
445
|
-
ai: { enabled: false, provider: 'anthropic' }
|
|
446
|
-
},
|
|
447
|
-
dashboard: { port: 3456, autoOpen: false },
|
|
448
|
-
quality: { preCommit: true, prePush: false, strictMode: false },
|
|
449
|
-
paths: { context: 'CLAUDE.md', config: 'bootspring.config.js', todo: 'todo.md' }
|
|
450
|
-
};
|
|
451
|
-
}
|
|
452
|
-
// Default: Interactive questionnaire mode
|
|
453
|
-
else {
|
|
454
|
-
try {
|
|
455
|
-
projectConfig = await runQuestionnaire('standard');
|
|
456
|
-
projectConfig._preset = 'standard';
|
|
457
|
-
} catch (_error) {
|
|
458
|
-
// Fall back to legacy interactive mode
|
|
459
|
-
utils.print.warning('Falling back to basic interactive mode');
|
|
460
|
-
|
|
461
|
-
const rl = createPrompt();
|
|
462
|
-
|
|
463
|
-
console.log(`\n${utils.COLORS.bold}Project Configuration${utils.COLORS.reset}`);
|
|
464
|
-
|
|
465
|
-
const name = await ask(rl, 'Project name', detected.name);
|
|
466
|
-
const description = await ask(rl, 'Description', '');
|
|
467
|
-
const framework = await askChoice(rl, 'Framework:', FRAMEWORKS, FRAMEWORKS.indexOf(detected.framework));
|
|
468
|
-
const language = await askChoice(rl, 'Language:', LANGUAGES, LANGUAGES.indexOf(detected.language));
|
|
469
|
-
const database = await askChoice(rl, 'Database:', DATABASES, DATABASES.indexOf(detected.database));
|
|
470
|
-
const hosting = await askChoice(rl, 'Hosting:', HOSTINGS, HOSTINGS.indexOf(detected.hosting));
|
|
471
|
-
|
|
472
|
-
console.log(`\n${utils.COLORS.bold}Plugins${utils.COLORS.reset}`);
|
|
473
|
-
|
|
474
|
-
const enableAuth = await askYesNo(rl, 'Enable auth plugin?', false);
|
|
475
|
-
const enablePayments = await askYesNo(rl, 'Enable payments plugin?', false);
|
|
476
|
-
const enableTesting = await askYesNo(rl, 'Enable testing plugin?', true);
|
|
477
|
-
const enableAI = await askYesNo(rl, 'Enable AI plugin?', false);
|
|
478
|
-
|
|
479
|
-
rl.close();
|
|
480
|
-
|
|
481
|
-
projectConfig = {
|
|
482
|
-
project: { name, description, version: '1.0.0' },
|
|
483
|
-
stack: { framework, language, database, hosting },
|
|
484
|
-
plugins: {
|
|
485
|
-
auth: { enabled: enableAuth, provider: 'clerk' },
|
|
486
|
-
payments: { enabled: enablePayments, provider: 'stripe' },
|
|
487
|
-
database: { enabled: database !== 'none', provider: 'prisma' },
|
|
488
|
-
testing: { enabled: enableTesting, provider: 'vitest' },
|
|
489
|
-
security: { enabled: true },
|
|
490
|
-
ai: { enabled: enableAI, provider: 'anthropic' }
|
|
491
|
-
},
|
|
492
|
-
dashboard: { port: 3456, autoOpen: false },
|
|
493
|
-
quality: { preCommit: true, prePush: false, strictMode: false },
|
|
494
|
-
paths: { context: 'CLAUDE.md', config: 'bootspring.config.js', todo: 'todo.md' }
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
// Create files
|
|
500
|
-
console.log(`\n${utils.COLORS.bold}Creating files...${utils.COLORS.reset}\n`);
|
|
501
|
-
|
|
502
|
-
// 1. bootspring.config.js
|
|
503
|
-
const configPath = path.join(projectRoot, 'bootspring.config.js');
|
|
504
|
-
const configSpinner = utils.createSpinner('Creating bootspring.config.js').start();
|
|
505
|
-
if (config.save(projectConfig, configPath)) {
|
|
506
|
-
configSpinner.succeed('Created bootspring.config.js');
|
|
507
|
-
} else {
|
|
508
|
-
configSpinner.fail('Failed to create bootspring.config.js');
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
// 2. CLAUDE.md (use template if from questionnaire)
|
|
512
|
-
const claudePath = path.join(projectRoot, 'CLAUDE.md');
|
|
513
|
-
const claudeSpinner = utils.createSpinner('Creating CLAUDE.md').start();
|
|
514
|
-
const claudeContent = projectConfig._preset
|
|
515
|
-
? claudeTemplate.generate(projectConfig)
|
|
516
|
-
: generateClaudeMd(projectConfig);
|
|
517
|
-
if (utils.writeFile(claudePath, claudeContent)) {
|
|
518
|
-
claudeSpinner.succeed('Created CLAUDE.md');
|
|
519
|
-
} else {
|
|
520
|
-
claudeSpinner.fail('Failed to create CLAUDE.md');
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
// 2b. SEED.md (for questionnaire mode)
|
|
524
|
-
if (projectConfig._preset) {
|
|
525
|
-
const seedPath = path.join(projectRoot, 'SEED.md');
|
|
526
|
-
const seedSpinner = utils.createSpinner('Creating SEED.md').start();
|
|
527
|
-
if (utils.writeFile(seedPath, seedTemplate.generate(projectConfig))) {
|
|
528
|
-
seedSpinner.succeed('Created SEED.md');
|
|
529
|
-
} else {
|
|
530
|
-
seedSpinner.fail('Failed to create SEED.md');
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
// 3. todo.md (if not exists)
|
|
535
|
-
const todoPath = path.join(projectRoot, 'todo.md');
|
|
536
|
-
if (!utils.fileExists(todoPath)) {
|
|
537
|
-
const todoSpinner = utils.createSpinner('Creating todo.md').start();
|
|
538
|
-
if (utils.writeFile(todoPath, generateTodoMd(projectConfig.project.name))) {
|
|
539
|
-
todoSpinner.succeed('Created todo.md');
|
|
540
|
-
} else {
|
|
541
|
-
todoSpinner.fail('Failed to create todo.md');
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
// 4. MCP configuration (local or global)
|
|
546
|
-
const globalMcpPath = getGlobalMcpPath();
|
|
547
|
-
const isGlobal = isGloballyInstalled();
|
|
548
|
-
const hasGlobalMcp = isGlobalMcpConfigured();
|
|
549
|
-
|
|
550
|
-
// Determine MCP scope
|
|
551
|
-
let mcpScope = 'local'; // default
|
|
552
|
-
|
|
553
|
-
if (isGlobal && !hasGlobalMcp) {
|
|
554
|
-
// Global install detected but no global MCP config - suggest global
|
|
555
|
-
utils.print.info('Global bootspring installation detected');
|
|
556
|
-
|
|
557
|
-
if (!quick && !presetArg) {
|
|
558
|
-
const rl = createPrompt();
|
|
559
|
-
const useGlobal = await askYesNo(rl, 'Configure MCP server globally (for all projects)?', true);
|
|
560
|
-
rl.close();
|
|
561
|
-
mcpScope = useGlobal ? 'global' : 'local';
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
if (mcpScope === 'global') {
|
|
566
|
-
const globalSpinner = utils.createSpinner('Configuring global MCP server').start();
|
|
567
|
-
try {
|
|
568
|
-
// Ensure directory exists
|
|
569
|
-
const globalDir = path.dirname(globalMcpPath);
|
|
570
|
-
if (!fs.existsSync(globalDir)) {
|
|
571
|
-
fs.mkdirSync(globalDir, { recursive: true });
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
// Read or create settings
|
|
575
|
-
let settings = {};
|
|
576
|
-
if (utils.fileExists(globalMcpPath)) {
|
|
577
|
-
settings = JSON.parse(fs.readFileSync(globalMcpPath, 'utf-8'));
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
// Add bootspring MCP server
|
|
581
|
-
if (!settings.mcpServers) {
|
|
582
|
-
settings.mcpServers = {};
|
|
583
|
-
}
|
|
584
|
-
settings.mcpServers.bootspring = getManagedMcpServerConfig();
|
|
585
|
-
|
|
586
|
-
if (utils.writeFile(globalMcpPath, JSON.stringify(settings, null, 2))) {
|
|
587
|
-
globalSpinner.succeed(`Configured global MCP at ${globalMcpPath}`);
|
|
588
|
-
} else {
|
|
589
|
-
globalSpinner.fail('Failed to configure global MCP');
|
|
590
|
-
}
|
|
591
|
-
} catch (error) {
|
|
592
|
-
globalSpinner.fail(`Global MCP config failed: ${error.message}`);
|
|
593
|
-
}
|
|
594
|
-
} else {
|
|
595
|
-
const mcpSpinner = utils.createSpinner('Creating .mcp.json').start();
|
|
596
|
-
const result = ensureProjectMcpConfig(projectRoot, { createIfMissing: true });
|
|
597
|
-
if (result.status === 'created') {
|
|
598
|
-
mcpSpinner.succeed('Created .mcp.json (MCP server config)');
|
|
599
|
-
} else if (result.status === 'updated' || result.status === 'added') {
|
|
600
|
-
mcpSpinner.succeed('Updated .mcp.json (MCP server config)');
|
|
601
|
-
} else if (result.status === 'unchanged') {
|
|
602
|
-
mcpSpinner.succeed('Verified .mcp.json (MCP server config)');
|
|
603
|
-
} else {
|
|
604
|
-
mcpSpinner.fail(`Failed to configure .mcp.json${result.error ? `: ${result.error.message}` : ''}`);
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
// 5. Create /planning folder with templates
|
|
609
|
-
const planningDir = path.join(projectRoot, 'planning');
|
|
610
|
-
if (!fs.existsSync(planningDir)) {
|
|
611
|
-
const planningSpinner = utils.createSpinner('Creating planning folder').start();
|
|
612
|
-
try {
|
|
613
|
-
fs.mkdirSync(planningDir, { recursive: true });
|
|
614
|
-
|
|
615
|
-
// Get all planning templates
|
|
616
|
-
const templates = planningTemplate.getTemplates(projectConfig);
|
|
617
|
-
let filesCreated = 0;
|
|
618
|
-
|
|
619
|
-
for (const [filename, content] of Object.entries(templates)) {
|
|
620
|
-
const filePath = path.join(planningDir, filename);
|
|
621
|
-
if (utils.writeFile(filePath, content)) {
|
|
622
|
-
filesCreated++;
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
planningSpinner.succeed(`Created planning folder (${filesCreated} templates)`);
|
|
627
|
-
} catch (error) {
|
|
628
|
-
planningSpinner.fail(`Failed to create planning folder: ${error.message}`);
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
// 6. Build context index
|
|
633
|
-
const indexSpinner = utils.createSpinner('Building context index').start();
|
|
634
|
-
try {
|
|
635
|
-
const cfg = config.load();
|
|
636
|
-
contextLoader.buildIndex({ config: cfg });
|
|
637
|
-
indexSpinner.succeed('Built context index');
|
|
638
|
-
} catch (error) {
|
|
639
|
-
indexSpinner.warn(`Context index: ${error.message}`);
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
// Success message
|
|
643
|
-
console.log(`
|
|
644
|
-
${utils.COLORS.green}${utils.COLORS.bold}✓ Bootspring initialized successfully!${utils.COLORS.reset}
|
|
645
|
-
|
|
646
|
-
${utils.COLORS.bold}Created:${utils.COLORS.reset}
|
|
647
|
-
• bootspring.config.js - Project configuration
|
|
648
|
-
• CLAUDE.md - AI context file
|
|
649
|
-
• SEED.md - Project seed data
|
|
650
|
-
• planning/ - Planning documents
|
|
651
|
-
• ${mcpScope === 'global' ? 'Global MCP config' : '.mcp.json'} - MCP server config
|
|
652
|
-
|
|
653
|
-
${utils.COLORS.bold}Next steps:${utils.COLORS.reset}
|
|
654
|
-
|
|
655
|
-
1. Review ${utils.COLORS.cyan}planning/MASTER_PLAN.md${utils.COLORS.reset} and define your vision
|
|
656
|
-
2. Fill out ${utils.COLORS.cyan}planning/PRD.md${utils.COLORS.reset} with requirements
|
|
657
|
-
3. Start the dashboard: ${utils.COLORS.cyan}bootspring dashboard${utils.COLORS.reset}
|
|
658
|
-
4. Add your first todo: ${utils.COLORS.cyan}bootspring todo add "Your first task"${utils.COLORS.reset}
|
|
659
|
-
|
|
660
|
-
${utils.COLORS.bold}Context commands:${utils.COLORS.reset}
|
|
661
|
-
bootspring context list ${utils.COLORS.dim}# See all context files${utils.COLORS.reset}
|
|
662
|
-
bootspring context read plan ${utils.COLORS.dim}# View master plan${utils.COLORS.reset}
|
|
663
|
-
bootspring context search "feature" ${utils.COLORS.dim}# Search across docs${utils.COLORS.reset}
|
|
664
|
-
|
|
665
|
-
${utils.COLORS.dim}Documentation: https://bootspring.com/docs${utils.COLORS.reset}
|
|
666
|
-
`);
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
module.exports = { run };
|