@girardmedia/bootspring 2.5.0 → 2.5.2

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.
Files changed (59) hide show
  1. package/README.md +9 -403
  2. package/bin/bootspring.js +1 -96
  3. package/dist/cli/index.js +65134 -0
  4. package/dist/cli-launcher.js +92 -0
  5. package/dist/core/index.d.ts +2110 -5582
  6. package/dist/core/index.js +2 -0
  7. package/dist/core.js +21123 -5413
  8. package/dist/mcp/index.d.ts +357 -1
  9. package/dist/mcp/index.js +2 -0
  10. package/dist/mcp-server.js +51948 -1976
  11. package/package.json +27 -63
  12. package/scripts/postinstall.cjs +144 -0
  13. package/LICENSE +0 -29
  14. package/dist/cli/index.cjs +0 -20776
  15. package/generators/api-docs.js +0 -827
  16. package/generators/decisions.js +0 -655
  17. package/generators/generate.js +0 -595
  18. package/generators/health.js +0 -942
  19. package/generators/index.ts +0 -82
  20. package/generators/presets/full.js +0 -28
  21. package/generators/presets/index.js +0 -12
  22. package/generators/presets/minimal.js +0 -29
  23. package/generators/presets/standard.js +0 -28
  24. package/generators/questionnaire.js +0 -414
  25. package/generators/sections/advanced.js +0 -136
  26. package/generators/sections/ai.js +0 -106
  27. package/generators/sections/auth.js +0 -89
  28. package/generators/sections/backend.js +0 -146
  29. package/generators/sections/business.js +0 -118
  30. package/generators/sections/content.js +0 -300
  31. package/generators/sections/deployment.js +0 -139
  32. package/generators/sections/features.js +0 -122
  33. package/generators/sections/frontend.js +0 -118
  34. package/generators/sections/identity.js +0 -76
  35. package/generators/sections/index.js +0 -40
  36. package/generators/sections/instructions.js +0 -146
  37. package/generators/sections/payments.js +0 -104
  38. package/generators/sections/plugins.js +0 -142
  39. package/generators/sections/pre-build.js +0 -130
  40. package/generators/sections/security.js +0 -127
  41. package/generators/sections/technical.js +0 -171
  42. package/generators/sections/testing.js +0 -125
  43. package/generators/sections/workflow.js +0 -104
  44. package/generators/sprint.js +0 -675
  45. package/generators/templates/agents.template.js +0 -199
  46. package/generators/templates/assistant-context.template.js +0 -83
  47. package/generators/templates/build-planning.template.js +0 -708
  48. package/generators/templates/claude.template.js +0 -379
  49. package/generators/templates/content.template.js +0 -819
  50. package/generators/templates/index.js +0 -16
  51. package/generators/templates/planning.template.js +0 -515
  52. package/generators/templates/seed.template.js +0 -109
  53. package/generators/visual-doc-generator.js +0 -910
  54. package/scripts/postinstall.js +0 -197
  55. /package/{claude-commands → assets/claude-commands}/agent.md +0 -0
  56. /package/{claude-commands → assets/claude-commands}/bs.md +0 -0
  57. /package/{claude-commands → assets/claude-commands}/build.md +0 -0
  58. /package/{claude-commands → assets/claude-commands}/skill.md +0 -0
  59. /package/{claude-commands → assets/claude-commands}/todo.md +0 -0
@@ -1,76 +0,0 @@
1
- /**
2
- * Project Identity Section
3
- *
4
- * Collects project name, description, and basic metadata.
5
- *
6
- * @package bootspring
7
- * @module generators/sections/identity
8
- */
9
-
10
- const path = require('path');
11
- const utils = require('../../core/utils');
12
-
13
- const title = 'Project Identity';
14
- const description = 'Tell me about your project';
15
-
16
- /**
17
- * Run the identity section
18
- */
19
- async function run(rl, previousAnswers, helpers) {
20
- const { askText, askChoice } = helpers;
21
-
22
- // Detect default name from directory
23
- const defaultName = path.basename(process.cwd());
24
-
25
- // Try to get name from package.json
26
- const pkg = utils.getPackageJson(process.cwd());
27
- const detectedName = pkg?.name || defaultName;
28
-
29
- const answers = {};
30
-
31
- // Project name
32
- answers.projectName = await askText(
33
- rl,
34
- 'Project name',
35
- detectedName
36
- );
37
-
38
- // Description
39
- answers.projectDescription = await askText(
40
- rl,
41
- 'Brief description (one line)',
42
- pkg?.description || ''
43
- );
44
-
45
- // Project status
46
- answers.projectStatus = await askChoice(
47
- rl,
48
- 'Current project status:',
49
- [
50
- { label: 'Planning', value: 'planning', description: 'Still in planning phase' },
51
- { label: 'Development', value: 'development', description: 'Actively building' },
52
- { label: 'MVP', value: 'mvp', description: 'Have a working MVP' },
53
- { label: 'Production', value: 'production', description: 'Live with users' }
54
- ],
55
- previousAnswers.hasExistingMVP ? 2 : 1
56
- );
57
-
58
- // Version (if MVP or later)
59
- if (['mvp', 'production'].includes(answers.projectStatus)) {
60
- answers.projectVersion = await askText(
61
- rl,
62
- 'Current version',
63
- pkg?.version || '0.1.0'
64
- );
65
- } else {
66
- answers.projectVersion = '0.1.0';
67
- }
68
-
69
- return answers;
70
- }
71
-
72
- module.exports = {
73
- title,
74
- description,
75
- run
76
- };
@@ -1,40 +0,0 @@
1
- /**
2
- * Questionnaire Sections Index
3
- *
4
- * @package bootspring
5
- * @module generators/sections
6
- */
7
-
8
- module.exports = {
9
- // Core sections
10
- 'pre-build': require('./pre-build'),
11
- 'identity': require('./identity'),
12
- 'business': require('./business'),
13
- 'technical': require('./technical'),
14
-
15
- // Stack sections
16
- 'frontend': require('./frontend'),
17
- 'backend': require('./backend'),
18
-
19
- // Integration sections
20
- 'auth': require('./auth'),
21
- 'payments': require('./payments'),
22
- 'ai': require('./ai'),
23
-
24
- // Quality & DevOps sections
25
- 'testing': require('./testing'),
26
- 'deployment': require('./deployment'),
27
- 'security': require('./security'),
28
-
29
- // Features & Workflow sections
30
- 'features': require('./features'),
31
- 'workflow': require('./workflow'),
32
- 'instructions': require('./instructions'),
33
-
34
- // Content & Documentation section
35
- 'content': require('./content'),
36
-
37
- // Legacy sections
38
- 'plugins': require('./plugins'),
39
- 'advanced': require('./advanced')
40
- };
@@ -1,146 +0,0 @@
1
- /**
2
- * Custom Instructions Section
3
- *
4
- * Collects build rules, forbidden patterns, and preferences.
5
- *
6
- * @package bootspring
7
- * @module generators/sections/instructions
8
- */
9
-
10
- const title = 'Custom Instructions';
11
- const description = 'Your development preferences';
12
-
13
- /**
14
- * Skip in minimal preset
15
- */
16
- function shouldSkip(_previousAnswers) {
17
- return false; // Can be skipped manually
18
- }
19
-
20
- /**
21
- * Run the instructions section
22
- */
23
- async function run(rl, previousAnswers, helpers) {
24
- const { askYesNo, askMultiSelect, askText } = helpers;
25
-
26
- const answers = {
27
- customInstructions: {}
28
- };
29
-
30
- console.log('\nThese instructions will be included in your CLAUDE.md');
31
-
32
- // Component preferences (for frontend frameworks)
33
- const isFrontend = ['nextjs', 'remix', 'nuxt', 'sveltekit'].includes(previousAnswers.framework);
34
-
35
- if (isFrontend) {
36
- answers.customInstructions.componentPreferences = await askMultiSelect(
37
- rl,
38
- 'Component preferences:',
39
- [
40
- { label: 'Prefer Server Components', value: 'server-components' },
41
- { label: 'Use Server Actions for mutations', value: 'server-actions' },
42
- { label: 'Keep components small (<100 lines)', value: 'small-components' },
43
- { label: 'Colocate styles with components', value: 'colocate-styles' },
44
- { label: 'Use composition over inheritance', value: 'composition' }
45
- ],
46
- ['server-components', 'server-actions', 'small-components']
47
- );
48
-
49
- answers.customInstructions.stylingPreferences = await askMultiSelect(
50
- rl,
51
- 'Styling preferences:',
52
- [
53
- { label: 'Tailwind CSS', value: 'tailwind' },
54
- { label: 'CSS Modules', value: 'css-modules' },
55
- { label: 'Styled Components', value: 'styled-components' },
56
- { label: 'Mobile-first responsive', value: 'mobile-first' }
57
- ],
58
- ['tailwind', 'mobile-first']
59
- );
60
- }
61
-
62
- // Validation preferences
63
- answers.customInstructions.validationPreferences = await askMultiSelect(
64
- rl,
65
- 'Validation and type safety:',
66
- [
67
- { label: 'Use Zod for all input validation', value: 'zod' },
68
- { label: 'Strict TypeScript mode', value: 'strict-ts' },
69
- { label: 'Validate at API boundaries', value: 'api-validation' },
70
- { label: 'Runtime type checking', value: 'runtime-types' }
71
- ],
72
- ['zod', 'strict-ts', 'api-validation']
73
- );
74
-
75
- // Security rules
76
- answers.customInstructions.securityRules = await askMultiSelect(
77
- rl,
78
- 'Security rules:',
79
- [
80
- { label: 'Never expose API keys to client', value: 'no-client-keys' },
81
- { label: 'Use environment variables for secrets', value: 'env-secrets' },
82
- { label: 'Sanitize all user input', value: 'sanitize-input' },
83
- { label: 'Use parameterized queries', value: 'parameterized-queries' },
84
- { label: 'Implement rate limiting', value: 'rate-limiting' }
85
- ],
86
- ['no-client-keys', 'env-secrets', 'sanitize-input', 'parameterized-queries']
87
- );
88
-
89
- // Testing rules
90
- if (previousAnswers.enableTesting) {
91
- answers.customInstructions.testingRules = await askMultiSelect(
92
- rl,
93
- 'Testing rules:',
94
- [
95
- { label: 'Write tests for new features', value: 'test-new-features' },
96
- { label: 'Maintain 80%+ coverage', value: 'coverage-80' },
97
- { label: 'Test edge cases explicitly', value: 'test-edge-cases' },
98
- { label: 'Use meaningful test names', value: 'meaningful-names' }
99
- ],
100
- ['test-new-features', 'test-edge-cases']
101
- );
102
- }
103
-
104
- // Forbidden patterns
105
- const wantForbidden = await askYesNo(
106
- rl,
107
- 'Define forbidden patterns (things to avoid)?',
108
- false
109
- );
110
-
111
- if (wantForbidden) {
112
- const forbiddenInput = await askText(
113
- rl,
114
- 'Forbidden patterns (comma-separated)',
115
- 'any, console.log in production, TODO comments in PRs'
116
- );
117
- answers.customInstructions.forbiddenPatterns = forbiddenInput
118
- .split(',')
119
- .map(s => s.trim())
120
- .filter(Boolean);
121
- }
122
-
123
- // Additional instructions
124
- const wantAdditional = await askYesNo(
125
- rl,
126
- 'Add custom build instructions?',
127
- false
128
- );
129
-
130
- if (wantAdditional) {
131
- answers.customInstructions.additionalInstructions = await askText(
132
- rl,
133
- 'Custom instructions (one line)',
134
- ''
135
- );
136
- }
137
-
138
- return answers;
139
- }
140
-
141
- module.exports = {
142
- title,
143
- description,
144
- shouldSkip,
145
- run
146
- };
@@ -1,104 +0,0 @@
1
- /**
2
- * Payments Section
3
- *
4
- * Payment processing, subscriptions, and monetization.
5
- *
6
- * @package bootspring
7
- * @module generators/sections/payments
8
- */
9
-
10
- const title = 'Payments & Monetization';
11
- const description = 'Payment processing setup';
12
-
13
- /**
14
- * Run the payments section
15
- */
16
- async function run(rl, previousAnswers, helpers) {
17
- const { askChoice, askMultiSelect, askYesNo } = helpers;
18
- const answers = {};
19
-
20
- // Enable payments
21
- answers.enablePayments = await askYesNo(
22
- rl,
23
- 'Does your app need payment processing?',
24
- false
25
- );
26
-
27
- if (!answers.enablePayments) {
28
- return answers;
29
- }
30
-
31
- // Payment Provider
32
- const providerOptions = [
33
- { label: 'Stripe', value: 'stripe', description: 'Industry standard (Recommended)' },
34
- { label: 'Lemon Squeezy', value: 'lemonsqueezy', description: 'Merchant of record' },
35
- { label: 'Paddle', value: 'paddle', description: 'Merchant of record' },
36
- { label: 'PayPal', value: 'paypal', description: 'PayPal payments' },
37
- { label: 'Square', value: 'square', description: 'Square payments' }
38
- ];
39
-
40
- answers.paymentsProvider = await askChoice(
41
- rl,
42
- 'Payment provider:',
43
- providerOptions,
44
- 0
45
- );
46
-
47
- // Payment Types
48
- const typeOptions = [
49
- { label: 'One-time payments', value: 'onetime' },
50
- { label: 'Subscriptions', value: 'subscriptions' },
51
- { label: 'Usage-based billing', value: 'usage' },
52
- { label: 'Per-seat pricing', value: 'seats' },
53
- { label: 'Credits/tokens', value: 'credits' }
54
- ];
55
-
56
- answers.paymentTypes = await askMultiSelect(
57
- rl,
58
- 'Payment types needed:',
59
- typeOptions,
60
- ['subscriptions']
61
- );
62
-
63
- // Pricing Tiers
64
- if (answers.paymentTypes.includes('subscriptions')) {
65
- const tierOptions = [
66
- { label: 'Free + Pro', value: 'freepro', description: 'Simple freemium' },
67
- { label: 'Free + Pro + Enterprise', value: 'freeproent', description: 'Standard SaaS' },
68
- { label: 'Multiple tiers', value: 'multi', description: 'Complex pricing' },
69
- { label: 'Custom', value: 'custom', description: 'Define later' }
70
- ];
71
-
72
- answers.pricingTiers = await askChoice(
73
- rl,
74
- 'Pricing model:',
75
- tierOptions,
76
- 1
77
- );
78
- }
79
-
80
- // Additional features
81
- const featureOptions = [
82
- { label: 'Invoices', value: 'invoices' },
83
- { label: 'Tax handling', value: 'tax' },
84
- { label: 'Refunds', value: 'refunds' },
85
- { label: 'Coupons/discounts', value: 'coupons' },
86
- { label: 'Free trials', value: 'trials' },
87
- { label: 'Customer portal', value: 'portal' }
88
- ];
89
-
90
- answers.paymentFeatures = await askMultiSelect(
91
- rl,
92
- 'Payment features:',
93
- featureOptions,
94
- ['portal', 'refunds']
95
- );
96
-
97
- return answers;
98
- }
99
-
100
- module.exports = {
101
- title,
102
- description,
103
- run
104
- };
@@ -1,142 +0,0 @@
1
- /**
2
- * Core Plugins Section
3
- *
4
- * Collects plugin choices: auth, payments, AI, testing.
5
- *
6
- * @package bootspring
7
- * @module generators/sections/plugins
8
- */
9
-
10
- const title = 'Core Plugins';
11
- const description = 'Select the features you need';
12
-
13
- /**
14
- * Run the plugins section
15
- */
16
- async function run(rl, previousAnswers, helpers) {
17
- const { askYesNo, askChoice, askMultiSelect } = helpers;
18
-
19
- const answers = {};
20
-
21
- // Authentication
22
- answers.enableAuth = await askYesNo(
23
- rl,
24
- 'Enable authentication?',
25
- true
26
- );
27
-
28
- if (answers.enableAuth) {
29
- answers.authProvider = await askChoice(
30
- rl,
31
- 'Auth provider:',
32
- [
33
- { label: 'Clerk', value: 'clerk', description: 'Full-featured, easy setup' },
34
- { label: 'NextAuth/Auth.js', value: 'nextauth', description: 'Flexible, self-hosted' },
35
- { label: 'Supabase Auth', value: 'supabase', description: 'With Supabase backend' },
36
- { label: 'Custom', value: 'custom', description: 'Build your own' }
37
- ],
38
- 0
39
- );
40
-
41
- answers.authFeatures = [];
42
- const wantOAuth = await askYesNo(rl, 'Include OAuth providers (Google, GitHub)?', true);
43
- if (wantOAuth) answers.authFeatures.push('oauth');
44
-
45
- const wantMagicLink = await askYesNo(rl, 'Include magic link / passwordless?', false);
46
- if (wantMagicLink) answers.authFeatures.push('magic-link');
47
-
48
- const wantMFA = await askYesNo(rl, 'Include MFA / 2FA?', false);
49
- if (wantMFA) answers.authFeatures.push('mfa');
50
- }
51
-
52
- // Payments
53
- answers.enablePayments = await askYesNo(
54
- rl,
55
- 'Enable payments/billing?',
56
- false
57
- );
58
-
59
- if (answers.enablePayments) {
60
- answers.paymentsProvider = await askChoice(
61
- rl,
62
- 'Payments provider:',
63
- [
64
- { label: 'Stripe', value: 'stripe', description: 'Most popular, full-featured' },
65
- { label: 'Paddle', value: 'paddle', description: 'MoR, handles taxes' },
66
- { label: 'Lemon Squeezy', value: 'lemonsqueezy', description: 'Simple, MoR' }
67
- ],
68
- 0
69
- );
70
-
71
- answers.paymentFeatures = [];
72
- const wantSubscriptions = await askYesNo(rl, 'Include subscriptions?', true);
73
- if (wantSubscriptions) answers.paymentFeatures.push('subscriptions');
74
-
75
- const wantOneTime = await askYesNo(rl, 'Include one-time payments?', false);
76
- if (wantOneTime) answers.paymentFeatures.push('one-time');
77
-
78
- const wantWebhooks = await askYesNo(rl, 'Include webhook handling?', true);
79
- if (wantWebhooks) answers.paymentFeatures.push('webhooks');
80
- }
81
-
82
- // AI Integration
83
- answers.enableAI = await askYesNo(
84
- rl,
85
- 'Enable AI integration?',
86
- false
87
- );
88
-
89
- if (answers.enableAI) {
90
- answers.aiProviders = await askMultiSelect(
91
- rl,
92
- 'AI providers (select one or more):',
93
- [
94
- { label: 'Anthropic (Claude)', value: 'anthropic', description: 'Claude API' },
95
- { label: 'OpenAI', value: 'openai', description: 'GPT models' },
96
- { label: 'Google AI', value: 'google', description: 'Gemini models' },
97
- { label: 'Cohere', value: 'cohere', description: 'Enterprise NLP' },
98
- { label: 'Hugging Face', value: 'huggingface', description: 'Open source models' }
99
- ],
100
- ['anthropic']
101
- );
102
-
103
- answers.aiFeatures = [];
104
- const wantChat = await askYesNo(rl, 'Include chat/conversation?', true);
105
- if (wantChat) answers.aiFeatures.push('chat');
106
-
107
- const wantStreaming = await askYesNo(rl, 'Include streaming responses?', true);
108
- if (wantStreaming) answers.aiFeatures.push('streaming');
109
-
110
- const wantToolUse = await askYesNo(rl, 'Include tool use / function calling?', false);
111
- if (wantToolUse) answers.aiFeatures.push('tool-use');
112
- }
113
-
114
- // Testing
115
- answers.enableTesting = await askYesNo(
116
- rl,
117
- 'Enable testing?',
118
- true
119
- );
120
-
121
- if (answers.enableTesting) {
122
- answers.testingProvider = await askChoice(
123
- rl,
124
- 'Testing framework:',
125
- [
126
- { label: 'Vitest', value: 'vitest', description: 'Fast, Vite-native' },
127
- { label: 'Jest', value: 'jest', description: 'Classic, full-featured' },
128
- { label: 'Playwright', value: 'playwright', description: 'E2E testing' },
129
- { label: 'Both unit + E2E', value: 'both', description: 'Vitest + Playwright' }
130
- ],
131
- 0
132
- );
133
- }
134
-
135
- return answers;
136
- }
137
-
138
- module.exports = {
139
- title,
140
- description,
141
- run
142
- };
@@ -1,130 +0,0 @@
1
- /**
2
- * Pre-Build Assets Section
3
- *
4
- * Detects existing MVP code, business docs, and assets
5
- * before starting the project setup.
6
- *
7
- * @package bootspring
8
- * @module generators/sections/pre-build
9
- */
10
-
11
- const fs = require('fs');
12
- const path = require('path');
13
-
14
- const title = 'Pre-Build Assets';
15
- const description = 'Let me check what you already have...';
16
-
17
- /**
18
- * Detect existing project assets
19
- */
20
- function detectAssets(projectRoot) {
21
- const assets = {
22
- hasPackageJson: false,
23
- hasReadme: false,
24
- hasExistingCode: false,
25
- hasPrismaSchema: false,
26
- hasEnvExample: false,
27
- hasDocumentation: false,
28
- hasMVP: false,
29
- detectedFiles: []
30
- };
31
-
32
- // Check for common files
33
- const checks = [
34
- { file: 'package.json', flag: 'hasPackageJson' },
35
- { file: 'README.md', flag: 'hasReadme' },
36
- { file: 'prisma/schema.prisma', flag: 'hasPrismaSchema' },
37
- { file: '.env.example', flag: 'hasEnvExample' },
38
- { file: 'docs', flag: 'hasDocumentation', isDir: true },
39
- { file: 'src', flag: 'hasExistingCode', isDir: true },
40
- { file: 'app', flag: 'hasExistingCode', isDir: true }
41
- ];
42
-
43
- for (const check of checks) {
44
- const fullPath = path.join(projectRoot, check.file);
45
- const exists = check.isDir
46
- ? fs.existsSync(fullPath) && fs.statSync(fullPath).isDirectory()
47
- : fs.existsSync(fullPath);
48
-
49
- if (exists) {
50
- assets[check.flag] = true;
51
- assets.detectedFiles.push(check.file);
52
- }
53
- }
54
-
55
- // Detect MVP indicators
56
- assets.hasMVP = assets.hasExistingCode && assets.hasPackageJson;
57
-
58
- return assets;
59
- }
60
-
61
- /**
62
- * Run the pre-build section
63
- */
64
- async function run(rl, previousAnswers, helpers) {
65
- const { askYesNo, askText } = helpers;
66
- const projectRoot = process.cwd();
67
-
68
- // Detect existing assets
69
- const assets = detectAssets(projectRoot);
70
-
71
- console.log('\nDetected assets:');
72
- if (assets.detectedFiles.length > 0) {
73
- assets.detectedFiles.forEach(f => {
74
- console.log(` ✓ ${f}`);
75
- });
76
- } else {
77
- console.log(' (No existing files detected - fresh project)');
78
- }
79
-
80
- const answers = {
81
- preBuildAssets: assets
82
- };
83
-
84
- // Ask about existing MVP
85
- if (assets.hasMVP) {
86
- answers.hasExistingMVP = await askYesNo(
87
- rl,
88
- '\nYou have existing code. Is this an MVP you want to build upon?',
89
- true
90
- );
91
-
92
- if (answers.hasExistingMVP) {
93
- answers.mvpDescription = await askText(
94
- rl,
95
- 'Briefly describe what the MVP does',
96
- ''
97
- );
98
- }
99
- }
100
-
101
- // Ask about business docs
102
- answers.hasBusinessDocs = await askYesNo(
103
- rl,
104
- 'Do you have existing business documents (PRD, pitch deck, etc.)?',
105
- false
106
- );
107
-
108
- if (answers.hasBusinessDocs) {
109
- answers.businessDocsLocation = await askText(
110
- rl,
111
- 'Where are they located?',
112
- './docs'
113
- );
114
- }
115
-
116
- // Ask about design assets
117
- answers.hasDesignAssets = await askYesNo(
118
- rl,
119
- 'Do you have design assets (Figma, mockups, etc.)?',
120
- false
121
- );
122
-
123
- return answers;
124
- }
125
-
126
- module.exports = {
127
- title,
128
- description,
129
- run
130
- };