@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/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 };