@claudetools/cli 0.10.0 → 0.11.1
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/dist/onboard/agents-md-builder.d.ts +1 -13
- package/dist/onboard/agents-md-builder.d.ts.map +1 -1
- package/dist/onboard/agents-md-builder.js +1 -0
- package/dist/onboard/agents-md-builder.js.map +1 -1
- package/dist/onboard/context7-fetcher.d.ts +1 -1
- package/dist/onboard/context7-fetcher.d.ts.map +1 -1
- package/dist/onboard/context7-fetcher.js +52 -18
- package/dist/onboard/context7-fetcher.js.map +1 -1
- package/dist/onboard/docs-builder.d.ts.map +1 -1
- package/dist/onboard/docs-builder.js +144 -0
- package/dist/onboard/docs-builder.js.map +1 -1
- package/dist/onboard/questions.d.ts +20 -6
- package/dist/onboard/questions.d.ts.map +1 -1
- package/dist/onboard/questions.js +386 -233
- package/dist/onboard/questions.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,274 +1,427 @@
|
|
|
1
1
|
// =============================================================================
|
|
2
|
-
// Interactive Onboarding Questions
|
|
2
|
+
// Interactive Onboarding Questions - Progressive Q&A System
|
|
3
3
|
// =============================================================================
|
|
4
|
-
//
|
|
4
|
+
// Comprehensive questioning that adapts based on project type and user answers
|
|
5
5
|
import prompts from 'prompts';
|
|
6
6
|
import chalk from 'chalk';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Run comprehensive interactive onboarding with progressive questions
|
|
9
|
+
*/
|
|
10
|
+
export async function runInteractiveOnboarding(stack, options = {}) {
|
|
11
|
+
const answers = {};
|
|
12
|
+
const isNew = options.isNewProject || stack.isEmpty;
|
|
13
|
+
console.log('');
|
|
14
|
+
console.log(chalk.dim(' ┌─ Project Understanding ─────────────────'));
|
|
15
|
+
console.log(chalk.dim(' │'));
|
|
16
|
+
console.log(chalk.dim(' │ Answer questions to help generate better docs.'));
|
|
17
|
+
console.log(chalk.dim(' │ Press Enter to skip, Ctrl+C to finish early.'));
|
|
18
|
+
console.log(chalk.dim(' │ The more you share, the better the results.'));
|
|
19
|
+
console.log(chalk.dim(' │'));
|
|
20
|
+
console.log(chalk.dim(' └──────────────────────────────────────────'));
|
|
21
|
+
console.log('');
|
|
22
|
+
// =========================================================================
|
|
23
|
+
// PHASE 1: Essential Questions (Always ask)
|
|
24
|
+
// =========================================================================
|
|
25
|
+
console.log(chalk.cyan.bold(' 📋 Project Basics\n'));
|
|
26
|
+
// Q1: What is this project?
|
|
27
|
+
const descResponse = await safePrompt({
|
|
12
28
|
type: 'text',
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
29
|
+
name: 'projectDescription',
|
|
30
|
+
message: isNew
|
|
31
|
+
? 'What do you want to build? (describe your project)'
|
|
32
|
+
: 'What is this project? (brief description)',
|
|
33
|
+
});
|
|
34
|
+
if (descResponse?.projectDescription && typeof descResponse.projectDescription === 'string') {
|
|
35
|
+
answers.projectDescription = descResponse.projectDescription;
|
|
36
|
+
}
|
|
37
|
+
// Q2: Project type
|
|
38
|
+
const typeResponse = await safePrompt({
|
|
18
39
|
type: 'select',
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
'
|
|
23
|
-
'
|
|
24
|
-
'
|
|
25
|
-
'
|
|
26
|
-
'
|
|
27
|
-
'
|
|
28
|
-
'
|
|
40
|
+
name: 'projectType',
|
|
41
|
+
message: 'What type of project is this?',
|
|
42
|
+
choices: [
|
|
43
|
+
{ title: 'Web Application', value: 'web-app', description: 'Full-stack or frontend web app' },
|
|
44
|
+
{ title: 'API / Backend', value: 'api', description: 'REST API, GraphQL, or backend service' },
|
|
45
|
+
{ title: 'CLI Tool', value: 'cli', description: 'Command-line application' },
|
|
46
|
+
{ title: 'Library / Package', value: 'library', description: 'Reusable code library' },
|
|
47
|
+
{ title: 'Mobile App', value: 'mobile', description: 'iOS, Android, or cross-platform' },
|
|
48
|
+
{ title: 'Desktop App', value: 'desktop', description: 'Electron, Tauri, etc.' },
|
|
49
|
+
{ title: 'Monorepo', value: 'monorepo', description: 'Multiple packages in one repo' },
|
|
50
|
+
{ title: 'Other', value: 'other', description: 'Something else' },
|
|
29
51
|
],
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
{
|
|
33
|
-
|
|
34
|
-
|
|
52
|
+
initial: guessProjectType(stack),
|
|
53
|
+
});
|
|
54
|
+
if (typeResponse?.projectType && typeof typeResponse.projectType === 'string') {
|
|
55
|
+
answers.projectType = typeResponse.projectType;
|
|
56
|
+
}
|
|
57
|
+
// Q3: Primary purpose - more specific based on type
|
|
58
|
+
const purposeResponse = await safePrompt({
|
|
59
|
+
type: 'text',
|
|
60
|
+
name: 'primaryPurpose',
|
|
61
|
+
message: 'What problem does this solve? (be specific)',
|
|
62
|
+
});
|
|
63
|
+
if (purposeResponse?.primaryPurpose && typeof purposeResponse.primaryPurpose === 'string') {
|
|
64
|
+
answers.primaryPurpose = purposeResponse.primaryPurpose;
|
|
65
|
+
}
|
|
66
|
+
// Q4: Key features (if they described the project)
|
|
67
|
+
if (answers.projectDescription || answers.primaryPurpose) {
|
|
68
|
+
const featuresResponse = await safePrompt({
|
|
69
|
+
type: 'text',
|
|
70
|
+
name: 'keyFeatures',
|
|
71
|
+
message: 'What are the main features? (comma-separated)',
|
|
72
|
+
});
|
|
73
|
+
if (featuresResponse?.keyFeatures && typeof featuresResponse.keyFeatures === 'string') {
|
|
74
|
+
answers.keyFeatures = featuresResponse.keyFeatures.split(',').map((f) => f.trim()).filter(Boolean);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// =========================================================================
|
|
78
|
+
// PHASE 2: Context Questions
|
|
79
|
+
// =========================================================================
|
|
80
|
+
console.log('');
|
|
81
|
+
console.log(chalk.cyan.bold(' 👥 Context\n'));
|
|
82
|
+
// Q5: Target audience
|
|
83
|
+
const audienceResponse = await safePrompt({
|
|
35
84
|
type: 'select',
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
'
|
|
40
|
-
'
|
|
41
|
-
'
|
|
85
|
+
name: 'targetAudience',
|
|
86
|
+
message: 'Who will use this?',
|
|
87
|
+
choices: [
|
|
88
|
+
{ title: 'End users (consumers)', value: 'consumers' },
|
|
89
|
+
{ title: 'Business users (B2B)', value: 'business' },
|
|
90
|
+
{ title: 'Developers', value: 'developers' },
|
|
91
|
+
{ title: 'Internal team only', value: 'internal' },
|
|
92
|
+
{ title: 'Mixed audience', value: 'mixed' },
|
|
42
93
|
],
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
94
|
+
});
|
|
95
|
+
if (audienceResponse?.targetAudience && typeof audienceResponse.targetAudience === 'string') {
|
|
96
|
+
answers.targetAudience = audienceResponse.targetAudience;
|
|
97
|
+
}
|
|
98
|
+
// Q6: Experience level - affects documentation style
|
|
99
|
+
const expResponse = await safePrompt({
|
|
48
100
|
type: 'select',
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
'
|
|
53
|
-
'
|
|
54
|
-
'
|
|
55
|
-
'Event-driven',
|
|
56
|
-
'Not sure / Mixed',
|
|
101
|
+
name: 'experienceLevel',
|
|
102
|
+
message: 'Your experience with this stack?',
|
|
103
|
+
choices: [
|
|
104
|
+
{ title: 'Beginner', value: 'beginner', description: 'Learning as I go - need detailed explanations' },
|
|
105
|
+
{ title: 'Intermediate', value: 'intermediate', description: 'Comfortable but still learning' },
|
|
106
|
+
{ title: 'Expert', value: 'expert', description: 'Very experienced - just need references' },
|
|
57
107
|
],
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
108
|
+
initial: 1,
|
|
109
|
+
});
|
|
110
|
+
if (expResponse?.experienceLevel && typeof expResponse.experienceLevel === 'string') {
|
|
111
|
+
const level = expResponse.experienceLevel;
|
|
112
|
+
if (level === 'beginner' || level === 'intermediate' || level === 'expert') {
|
|
113
|
+
answers.experienceLevel = level;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Q7: Team size
|
|
117
|
+
const teamResponse = await safePrompt({
|
|
64
118
|
type: 'select',
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
'
|
|
69
|
-
'
|
|
70
|
-
'
|
|
71
|
-
'
|
|
72
|
-
'URL state',
|
|
73
|
-
'No client state management',
|
|
74
|
-
'Other',
|
|
119
|
+
name: 'teamSize',
|
|
120
|
+
message: 'Team size?',
|
|
121
|
+
choices: [
|
|
122
|
+
{ title: 'Solo developer', value: 'solo' },
|
|
123
|
+
{ title: 'Small team (2-5)', value: 'small' },
|
|
124
|
+
{ title: 'Medium team (6-15)', value: 'medium' },
|
|
125
|
+
{ title: 'Large team (15+)', value: 'large' },
|
|
75
126
|
],
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
127
|
+
});
|
|
128
|
+
if (teamResponse?.teamSize && typeof teamResponse.teamSize === 'string') {
|
|
129
|
+
answers.teamSize = teamResponse.teamSize;
|
|
130
|
+
}
|
|
131
|
+
// =========================================================================
|
|
132
|
+
// PHASE 3: Technical Questions (based on project type)
|
|
133
|
+
// =========================================================================
|
|
134
|
+
const isWebOrMobile = ['web-app', 'mobile'].includes(answers.projectType || '');
|
|
135
|
+
const isApi = answers.projectType === 'api';
|
|
136
|
+
const hasFrontendFramework = stack.frameworks.some(f => ['react', 'next', 'vue', 'nuxt', 'svelte', 'sveltekit', 'angular'].includes(f.name.toLowerCase()));
|
|
137
|
+
if (isWebOrMobile || hasFrontendFramework) {
|
|
138
|
+
console.log('');
|
|
139
|
+
console.log(chalk.cyan.bold(' ⚙️ Technical Choices\n'));
|
|
140
|
+
// Architecture
|
|
141
|
+
const archResponse = await safePrompt({
|
|
142
|
+
type: 'select',
|
|
143
|
+
name: 'architectureStyle',
|
|
144
|
+
message: 'How is the code organized?',
|
|
145
|
+
choices: [
|
|
146
|
+
{ title: 'Feature-based', value: 'feature-based', description: 'Folders by feature/domain' },
|
|
147
|
+
{ title: 'Component-based', value: 'component-based', description: 'UI components hierarchy' },
|
|
148
|
+
{ title: 'Layered', value: 'layered', description: 'UI → Business → Data layers' },
|
|
149
|
+
{ title: 'Atomic Design', value: 'atomic', description: 'Atoms → Molecules → Organisms' },
|
|
150
|
+
{ title: 'Not organized yet', value: 'none', description: 'Still figuring it out' },
|
|
151
|
+
{ title: 'Not sure', value: 'unsure', description: 'Let AI analyze and document' },
|
|
152
|
+
],
|
|
153
|
+
});
|
|
154
|
+
if (archResponse?.architectureStyle && typeof archResponse.architectureStyle === 'string') {
|
|
155
|
+
answers.architectureStyle = archResponse.architectureStyle;
|
|
156
|
+
}
|
|
157
|
+
// State management (frontend only)
|
|
158
|
+
if (hasFrontendFramework) {
|
|
159
|
+
const stateResponse = await safePrompt({
|
|
160
|
+
type: 'select',
|
|
161
|
+
name: 'stateManagement',
|
|
162
|
+
message: 'State management approach?',
|
|
163
|
+
choices: getStateManagementChoices(stack),
|
|
164
|
+
});
|
|
165
|
+
if (stateResponse?.stateManagement && typeof stateResponse.stateManagement === 'string') {
|
|
166
|
+
answers.stateManagement = stateResponse.stateManagement;
|
|
167
|
+
}
|
|
168
|
+
// Data fetching
|
|
169
|
+
const dataResponse = await safePrompt({
|
|
170
|
+
type: 'select',
|
|
171
|
+
name: 'dataFetching',
|
|
172
|
+
message: 'How do you fetch data?',
|
|
173
|
+
choices: [
|
|
174
|
+
{ title: 'TanStack Query', value: 'tanstack-query', description: 'Powerful async state' },
|
|
175
|
+
{ title: 'SWR', value: 'swr', description: 'Stale-while-revalidate' },
|
|
176
|
+
{ title: 'tRPC', value: 'trpc', description: 'End-to-end typesafe' },
|
|
177
|
+
{ title: 'Plain fetch/axios', value: 'fetch', description: 'Simple HTTP' },
|
|
178
|
+
{ title: 'GraphQL', value: 'graphql', description: 'Apollo, urql, etc.' },
|
|
179
|
+
{ title: 'Server Components', value: 'rsc', description: 'Next.js RSC' },
|
|
180
|
+
{ title: 'Not sure', value: 'unsure' },
|
|
181
|
+
],
|
|
182
|
+
initial: detectDataFetchingChoice(stack),
|
|
183
|
+
});
|
|
184
|
+
if (dataResponse?.dataFetching && typeof dataResponse.dataFetching === 'string') {
|
|
185
|
+
answers.dataFetching = dataResponse.dataFetching;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// Authentication (API or web app)
|
|
190
|
+
if (isApi || isWebOrMobile) {
|
|
191
|
+
const authResponse = await safePrompt({
|
|
192
|
+
type: 'select',
|
|
193
|
+
name: 'authenticationMethod',
|
|
194
|
+
message: 'Authentication method?',
|
|
195
|
+
choices: [
|
|
196
|
+
{ title: 'None yet', value: 'none' },
|
|
197
|
+
{ title: 'JWT tokens', value: 'jwt' },
|
|
198
|
+
{ title: 'Session cookies', value: 'session' },
|
|
199
|
+
{ title: 'OAuth (Google, GitHub)', value: 'oauth' },
|
|
200
|
+
{ title: 'Auth provider (Auth0, Clerk)', value: 'provider' },
|
|
201
|
+
{ title: 'API keys', value: 'api-keys' },
|
|
202
|
+
{ title: 'Not sure', value: 'unsure' },
|
|
203
|
+
],
|
|
204
|
+
});
|
|
205
|
+
if (authResponse?.authenticationMethod && typeof authResponse.authenticationMethod === 'string') {
|
|
206
|
+
answers.authenticationMethod = authResponse.authenticationMethod;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// =========================================================================
|
|
210
|
+
// PHASE 4: Development Practices
|
|
211
|
+
// =========================================================================
|
|
212
|
+
console.log('');
|
|
213
|
+
console.log(chalk.cyan.bold(' 🛠️ Development Practices\n'));
|
|
214
|
+
// Testing
|
|
215
|
+
const testResponse = await safePrompt({
|
|
216
|
+
type: 'multiselect',
|
|
217
|
+
name: 'testingApproach',
|
|
218
|
+
message: 'Testing approach? (Space to select)',
|
|
219
|
+
choices: [
|
|
220
|
+
{ title: 'Unit tests', value: 'unit' },
|
|
221
|
+
{ title: 'Integration tests', value: 'integration' },
|
|
222
|
+
{ title: 'E2E tests (Playwright/Cypress)', value: 'e2e' },
|
|
223
|
+
{ title: 'Component tests', value: 'component' },
|
|
224
|
+
{ title: 'API tests', value: 'api' },
|
|
225
|
+
{ title: 'No tests yet', value: 'none' },
|
|
92
226
|
],
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
227
|
+
});
|
|
228
|
+
if (testResponse?.testingApproach && Array.isArray(testResponse.testingApproach) && testResponse.testingApproach.length > 0) {
|
|
229
|
+
answers.testingApproach = testResponse.testingApproach;
|
|
230
|
+
}
|
|
231
|
+
// Coding conventions
|
|
232
|
+
const convResponse = await safePrompt({
|
|
99
233
|
type: 'multiselect',
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
'
|
|
104
|
-
'
|
|
105
|
-
'
|
|
106
|
-
'
|
|
107
|
-
'
|
|
234
|
+
name: 'codingConventions',
|
|
235
|
+
message: 'Coding conventions? (Space to select)',
|
|
236
|
+
choices: [
|
|
237
|
+
{ title: 'TypeScript strict mode', value: 'typescript-strict', selected: hasTypeScript(stack) },
|
|
238
|
+
{ title: 'ESLint + Prettier', value: 'eslint-prettier', selected: hasEslint(stack) },
|
|
239
|
+
{ title: 'Conventional commits', value: 'conventional-commits' },
|
|
240
|
+
{ title: 'Feature folders', value: 'feature-folders' },
|
|
241
|
+
{ title: 'Barrel exports (index.ts)', value: 'barrel-exports' },
|
|
242
|
+
{ title: 'No strict conventions', value: 'none' },
|
|
108
243
|
],
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
244
|
+
});
|
|
245
|
+
if (convResponse?.codingConventions && Array.isArray(convResponse.codingConventions) && convResponse.codingConventions.length > 0) {
|
|
246
|
+
answers.codingConventions = convResponse.codingConventions;
|
|
247
|
+
}
|
|
248
|
+
// Important patterns
|
|
249
|
+
const patternResponse = await safePrompt({
|
|
114
250
|
type: 'multiselect',
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
'
|
|
119
|
-
'
|
|
120
|
-
'
|
|
121
|
-
'
|
|
122
|
-
'
|
|
123
|
-
'
|
|
251
|
+
name: 'importantPatterns',
|
|
252
|
+
message: 'Key patterns used? (Space to select)',
|
|
253
|
+
choices: [
|
|
254
|
+
{ title: 'Custom hooks', value: 'custom-hooks' },
|
|
255
|
+
{ title: 'Compound components', value: 'compound-components' },
|
|
256
|
+
{ title: 'Repository pattern', value: 'repository' },
|
|
257
|
+
{ title: 'Factory pattern', value: 'factory' },
|
|
258
|
+
{ title: 'Dependency injection', value: 'di' },
|
|
259
|
+
{ title: 'None specifically', value: 'none' },
|
|
124
260
|
],
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
261
|
+
});
|
|
262
|
+
if (patternResponse?.importantPatterns && Array.isArray(patternResponse.importantPatterns) && patternResponse.importantPatterns.length > 0) {
|
|
263
|
+
answers.importantPatterns = patternResponse.importantPatterns;
|
|
264
|
+
}
|
|
265
|
+
// =========================================================================
|
|
266
|
+
// PHASE 5: Priorities and Challenges
|
|
267
|
+
// =========================================================================
|
|
268
|
+
console.log('');
|
|
269
|
+
console.log(chalk.cyan.bold(' 🎯 Current Focus\n'));
|
|
270
|
+
// Priorities
|
|
271
|
+
const priorityResponse = await safePrompt({
|
|
130
272
|
type: 'multiselect',
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
'
|
|
135
|
-
'
|
|
136
|
-
'
|
|
137
|
-
'
|
|
138
|
-
'
|
|
139
|
-
'
|
|
140
|
-
'
|
|
273
|
+
name: 'currentPriorities',
|
|
274
|
+
message: 'Current priorities? (pick top 3)',
|
|
275
|
+
choices: [
|
|
276
|
+
{ title: 'Get MVP working', value: 'mvp' },
|
|
277
|
+
{ title: 'Ship new features', value: 'features' },
|
|
278
|
+
{ title: 'Fix bugs', value: 'bugs' },
|
|
279
|
+
{ title: 'Improve performance', value: 'performance' },
|
|
280
|
+
{ title: 'Add tests', value: 'testing' },
|
|
281
|
+
{ title: 'Refactor/cleanup', value: 'refactor' },
|
|
282
|
+
{ title: 'Better documentation', value: 'docs' },
|
|
141
283
|
],
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
{
|
|
145
|
-
|
|
146
|
-
|
|
284
|
+
max: 3,
|
|
285
|
+
});
|
|
286
|
+
if (priorityResponse?.currentPriorities && Array.isArray(priorityResponse.currentPriorities) && priorityResponse.currentPriorities.length > 0) {
|
|
287
|
+
answers.currentPriorities = priorityResponse.currentPriorities;
|
|
288
|
+
}
|
|
289
|
+
// Challenges - free text for rich context
|
|
290
|
+
const challengeResponse = await safePrompt({
|
|
147
291
|
type: 'text',
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
console.log(chalk.dim(' Press Enter to skip any question.\n'));
|
|
169
|
-
for (const q of QUESTIONS) {
|
|
170
|
-
// Check condition
|
|
171
|
-
if (q.condition && !q.condition(stack, answers)) {
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
174
|
-
// Show category header
|
|
175
|
-
if (q.category !== currentCategory) {
|
|
176
|
-
currentCategory = q.category;
|
|
177
|
-
console.log(chalk.cyan.bold(`\n ${currentCategory}`));
|
|
292
|
+
name: 'knownChallenges',
|
|
293
|
+
message: 'Any pain points or challenges?',
|
|
294
|
+
});
|
|
295
|
+
if (challengeResponse?.knownChallenges && typeof challengeResponse.knownChallenges === 'string') {
|
|
296
|
+
answers.knownChallenges = challengeResponse.knownChallenges;
|
|
297
|
+
}
|
|
298
|
+
// =========================================================================
|
|
299
|
+
// PHASE 6: New Project Stack (if empty project)
|
|
300
|
+
// =========================================================================
|
|
301
|
+
if (isNew && stack.isEmpty) {
|
|
302
|
+
console.log('');
|
|
303
|
+
console.log(chalk.cyan.bold(' 🚀 Stack Setup\n'));
|
|
304
|
+
const recResponse = await safePrompt({
|
|
305
|
+
type: 'confirm',
|
|
306
|
+
name: 'needsRecommendation',
|
|
307
|
+
message: 'Want AI to recommend a tech stack?',
|
|
308
|
+
initial: true,
|
|
309
|
+
});
|
|
310
|
+
if (recResponse?.needsRecommendation !== undefined && typeof recResponse.needsRecommendation === 'boolean') {
|
|
311
|
+
answers.needsRecommendation = recResponse.needsRecommendation;
|
|
178
312
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
if (response && response.trim()) {
|
|
188
|
-
answers[q.id] = response.trim();
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
else if (q.type === 'select') {
|
|
192
|
-
const { selected } = await prompts({
|
|
193
|
-
type: 'select',
|
|
194
|
-
name: 'selected',
|
|
195
|
-
message: q.question,
|
|
196
|
-
choices: [
|
|
197
|
-
...(q.options || []).map(opt => ({ title: opt, value: opt })),
|
|
198
|
-
{ title: chalk.dim('Skip'), value: '__skip__' },
|
|
199
|
-
],
|
|
200
|
-
}, { onCancel: () => { throw new Error('cancelled'); } });
|
|
201
|
-
if (selected && selected !== '__skip__') {
|
|
202
|
-
answers[q.id] = selected;
|
|
203
|
-
}
|
|
313
|
+
if (!answers.needsRecommendation) {
|
|
314
|
+
const stackResponse = await safePrompt({
|
|
315
|
+
type: 'text',
|
|
316
|
+
name: 'preferredStack',
|
|
317
|
+
message: 'What stack do you want?',
|
|
318
|
+
});
|
|
319
|
+
if (stackResponse?.preferredStack && typeof stackResponse.preferredStack === 'string') {
|
|
320
|
+
answers.preferredStack = stackResponse.preferredStack;
|
|
204
321
|
}
|
|
205
|
-
else if (q.type === 'multiselect') {
|
|
206
|
-
const { selected } = await prompts({
|
|
207
|
-
type: 'multiselect',
|
|
208
|
-
name: 'selected',
|
|
209
|
-
message: q.question,
|
|
210
|
-
choices: (q.options || []).map(opt => ({ title: opt, value: opt })),
|
|
211
|
-
hint: '- Space to select, Enter to confirm',
|
|
212
|
-
}, { onCancel: () => { throw new Error('cancelled'); } });
|
|
213
|
-
if (selected && selected.length > 0) {
|
|
214
|
-
answers[q.id] = selected;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
catch {
|
|
219
|
-
// User cancelled
|
|
220
|
-
console.log(chalk.dim('\n Onboarding cancelled. Using auto-detected information only.\n'));
|
|
221
|
-
break;
|
|
222
322
|
}
|
|
223
323
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
324
|
+
console.log('');
|
|
325
|
+
const answeredCount = Object.keys(answers).filter(k => answers[k] !== undefined).length;
|
|
326
|
+
console.log(chalk.dim(` Collected ${answeredCount} responses. Generating documentation...\n`));
|
|
227
327
|
return answers;
|
|
228
328
|
}
|
|
329
|
+
// =========================================================================
|
|
330
|
+
// Safe prompt wrapper - handles cancellation gracefully
|
|
331
|
+
// =========================================================================
|
|
332
|
+
async function safePrompt(config) {
|
|
333
|
+
try {
|
|
334
|
+
const response = await prompts(config, {
|
|
335
|
+
onCancel: () => {
|
|
336
|
+
throw new Error('cancelled');
|
|
337
|
+
},
|
|
338
|
+
});
|
|
339
|
+
return response;
|
|
340
|
+
}
|
|
341
|
+
catch {
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
// =========================================================================
|
|
346
|
+
// Helper Functions
|
|
347
|
+
// =========================================================================
|
|
348
|
+
function guessProjectType(stack) {
|
|
349
|
+
if (stack.frameworks.some(f => ['next', 'nuxt', 'sveltekit', 'remix', 'astro'].includes(f.name.toLowerCase()))) {
|
|
350
|
+
return 0; // web-app
|
|
351
|
+
}
|
|
352
|
+
if (stack.frameworks.some(f => ['express', 'fastify', 'hono', 'nestjs', 'koa'].includes(f.name.toLowerCase()))) {
|
|
353
|
+
return 1; // api
|
|
354
|
+
}
|
|
355
|
+
if (stack.frameworks.some(f => ['react-native', 'expo'].includes(f.name.toLowerCase()))) {
|
|
356
|
+
return 4; // mobile
|
|
357
|
+
}
|
|
358
|
+
if (stack.frameworks.some(f => ['electron', 'tauri'].includes(f.name.toLowerCase()))) {
|
|
359
|
+
return 5; // desktop
|
|
360
|
+
}
|
|
361
|
+
return 0;
|
|
362
|
+
}
|
|
363
|
+
function getStateManagementChoices(stack) {
|
|
364
|
+
const choices = [
|
|
365
|
+
{ title: 'React Context / useState', value: 'context' },
|
|
366
|
+
{ title: 'Zustand', value: 'zustand' },
|
|
367
|
+
{ title: 'Jotai', value: 'jotai' },
|
|
368
|
+
{ title: 'Redux Toolkit', value: 'redux' },
|
|
369
|
+
{ title: 'Server state only', value: 'server-state' },
|
|
370
|
+
{ title: 'Not sure', value: 'unsure' },
|
|
371
|
+
];
|
|
372
|
+
// Detect from stack
|
|
373
|
+
const detected = stack.libraries.find(l => ['zustand', 'jotai', 'redux', '@reduxjs/toolkit', 'recoil', 'mobx'].includes(l.name));
|
|
374
|
+
if (detected) {
|
|
375
|
+
const name = detected.name.replace('@reduxjs/toolkit', 'redux');
|
|
376
|
+
const idx = choices.findIndex(c => c.value === name);
|
|
377
|
+
if (idx > 0) {
|
|
378
|
+
const [item] = choices.splice(idx, 1);
|
|
379
|
+
if (item)
|
|
380
|
+
choices.unshift(item);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return choices;
|
|
384
|
+
}
|
|
385
|
+
function detectDataFetchingChoice(stack) {
|
|
386
|
+
if (stack.libraries.some(l => l.name.includes('tanstack') || l.name.includes('react-query')))
|
|
387
|
+
return 0;
|
|
388
|
+
if (stack.libraries.some(l => l.name === 'swr'))
|
|
389
|
+
return 1;
|
|
390
|
+
if (stack.libraries.some(l => l.name.includes('trpc')))
|
|
391
|
+
return 2;
|
|
392
|
+
if (stack.frameworks.some(f => f.name === 'next'))
|
|
393
|
+
return 5;
|
|
394
|
+
return 3;
|
|
395
|
+
}
|
|
396
|
+
function hasTypeScript(stack) {
|
|
397
|
+
return stack.primaryLanguage === 'typescript';
|
|
398
|
+
}
|
|
399
|
+
function hasEslint(stack) {
|
|
400
|
+
return stack.devTools.some(t => t.name === 'eslint' || t.name === 'prettier');
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Format answers for documentation
|
|
404
|
+
*/
|
|
229
405
|
export function formatAnswersForDocs(answers) {
|
|
230
406
|
const sections = [];
|
|
231
407
|
if (answers.projectDescription) {
|
|
232
|
-
sections.push(`## Project
|
|
408
|
+
sections.push(`## Project Overview\n\n${answers.projectDescription}`);
|
|
233
409
|
}
|
|
234
|
-
if (answers.primaryPurpose
|
|
235
|
-
|
|
236
|
-
if (answers.primaryPurpose) {
|
|
237
|
-
overview += `**Purpose:** ${answers.primaryPurpose}\n`;
|
|
238
|
-
}
|
|
239
|
-
if (answers.targetAudience) {
|
|
240
|
-
overview += `**Target Audience:** ${answers.targetAudience}\n`;
|
|
241
|
-
}
|
|
242
|
-
sections.push(overview);
|
|
410
|
+
if (answers.primaryPurpose) {
|
|
411
|
+
sections.push(`## Purpose\n\n${answers.primaryPurpose}`);
|
|
243
412
|
}
|
|
244
|
-
if (answers.
|
|
245
|
-
|
|
246
|
-
if (answers.architectureStyle) {
|
|
247
|
-
arch += `**Style:** ${answers.architectureStyle}\n`;
|
|
248
|
-
}
|
|
249
|
-
if (answers.stateManagement) {
|
|
250
|
-
arch += `**State Management:** ${answers.stateManagement}\n`;
|
|
251
|
-
}
|
|
252
|
-
if (answers.dataFetching) {
|
|
253
|
-
arch += `**Data Fetching:** ${answers.dataFetching}\n`;
|
|
254
|
-
}
|
|
255
|
-
sections.push(arch);
|
|
256
|
-
}
|
|
257
|
-
if (answers.codingConventions && Array.isArray(answers.codingConventions) && answers.codingConventions.length > 0) {
|
|
258
|
-
sections.push(`## Coding Conventions\n\n${answers.codingConventions.map((c) => `- ${c}`).join('\n')}`);
|
|
413
|
+
if (answers.keyFeatures?.length) {
|
|
414
|
+
sections.push(`## Key Features\n\n${answers.keyFeatures.map(f => `- ${f}`).join('\n')}`);
|
|
259
415
|
}
|
|
260
|
-
if (answers.
|
|
261
|
-
sections.push(`##
|
|
416
|
+
if (answers.targetAudience) {
|
|
417
|
+
sections.push(`## Target Audience\n\n${answers.targetAudience}`);
|
|
262
418
|
}
|
|
263
|
-
if (answers.
|
|
264
|
-
sections.push(`##
|
|
419
|
+
if (answers.architectureStyle && answers.architectureStyle !== 'unsure') {
|
|
420
|
+
sections.push(`## Architecture\n\n**Style:** ${answers.architectureStyle}`);
|
|
265
421
|
}
|
|
266
422
|
if (answers.knownChallenges) {
|
|
267
423
|
sections.push(`## Known Challenges\n\n${answers.knownChallenges}`);
|
|
268
424
|
}
|
|
269
|
-
if (answers.teamSize) {
|
|
270
|
-
sections.push(`## Team\n\n**Size:** ${answers.teamSize}`);
|
|
271
|
-
}
|
|
272
425
|
return sections.join('\n\n');
|
|
273
426
|
}
|
|
274
427
|
//# sourceMappingURL=questions.js.map
|