@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.
- package/README.md +9 -403
- package/bin/bootspring.js +1 -96
- package/dist/cli/index.js +65134 -0
- package/dist/cli-launcher.js +92 -0
- package/dist/core/index.d.ts +2110 -5582
- package/dist/core/index.js +2 -0
- package/dist/core.js +21123 -5413
- package/dist/mcp/index.d.ts +357 -1
- package/dist/mcp/index.js +2 -0
- package/dist/mcp-server.js +51948 -1976
- package/package.json +27 -63
- package/scripts/postinstall.cjs +144 -0
- package/LICENSE +0 -29
- package/dist/cli/index.cjs +0 -20776
- package/generators/api-docs.js +0 -827
- package/generators/decisions.js +0 -655
- package/generators/generate.js +0 -595
- package/generators/health.js +0 -942
- package/generators/index.ts +0 -82
- package/generators/presets/full.js +0 -28
- package/generators/presets/index.js +0 -12
- package/generators/presets/minimal.js +0 -29
- package/generators/presets/standard.js +0 -28
- package/generators/questionnaire.js +0 -414
- package/generators/sections/advanced.js +0 -136
- package/generators/sections/ai.js +0 -106
- package/generators/sections/auth.js +0 -89
- package/generators/sections/backend.js +0 -146
- package/generators/sections/business.js +0 -118
- package/generators/sections/content.js +0 -300
- package/generators/sections/deployment.js +0 -139
- package/generators/sections/features.js +0 -122
- package/generators/sections/frontend.js +0 -118
- package/generators/sections/identity.js +0 -76
- package/generators/sections/index.js +0 -40
- package/generators/sections/instructions.js +0 -146
- package/generators/sections/payments.js +0 -104
- package/generators/sections/plugins.js +0 -142
- package/generators/sections/pre-build.js +0 -130
- package/generators/sections/security.js +0 -127
- package/generators/sections/technical.js +0 -171
- package/generators/sections/testing.js +0 -125
- package/generators/sections/workflow.js +0 -104
- package/generators/sprint.js +0 -675
- package/generators/templates/agents.template.js +0 -199
- package/generators/templates/assistant-context.template.js +0 -83
- package/generators/templates/build-planning.template.js +0 -708
- package/generators/templates/claude.template.js +0 -379
- package/generators/templates/content.template.js +0 -819
- package/generators/templates/index.js +0 -16
- package/generators/templates/planning.template.js +0 -515
- package/generators/templates/seed.template.js +0 -109
- package/generators/visual-doc-generator.js +0 -910
- package/scripts/postinstall.js +0 -197
- /package/{claude-commands → assets/claude-commands}/agent.md +0 -0
- /package/{claude-commands → assets/claude-commands}/bs.md +0 -0
- /package/{claude-commands → assets/claude-commands}/build.md +0 -0
- /package/{claude-commands → assets/claude-commands}/skill.md +0 -0
- /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
|
-
};
|