create-propelkit 1.0.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.
@@ -0,0 +1,429 @@
1
+ /**
2
+ * Lovable Prompt Generator
3
+ *
4
+ * Generates comprehensive Lovable prompts from design system and page list.
5
+ * Uses array-based markdown generation pattern (PropelKit convention).
6
+ */
7
+
8
+ /**
9
+ * Typography configurations for each style reference.
10
+ * @param {string} styleReference - One of: linear, notion, stripe, vercel, airbnb
11
+ * @returns {{ headings: string, body: string, characteristics: string }}
12
+ */
13
+ function getTypography(styleReference) {
14
+ const typographyMap = {
15
+ linear: {
16
+ headings: 'Inter, system-ui, sans-serif',
17
+ body: 'Inter, system-ui, sans-serif',
18
+ characteristics: 'Clean, technical, precise'
19
+ },
20
+ notion: {
21
+ headings: 'Inter, ui-sans-serif, sans-serif',
22
+ body: 'Inter, ui-sans-serif, sans-serif',
23
+ characteristics: 'Friendly, approachable, readable'
24
+ },
25
+ stripe: {
26
+ headings: 'Sohne, system-ui, sans-serif',
27
+ body: 'Sohne, system-ui, sans-serif',
28
+ characteristics: 'Professional, elegant, refined'
29
+ },
30
+ vercel: {
31
+ headings: 'Geist Sans, system-ui, sans-serif',
32
+ body: 'Geist Sans, system-ui, sans-serif',
33
+ characteristics: 'Modern, geometric, minimal'
34
+ },
35
+ airbnb: {
36
+ headings: 'Cereal, system-ui, sans-serif',
37
+ body: 'Cereal, system-ui, sans-serif',
38
+ characteristics: 'Warm, inviting, human'
39
+ }
40
+ };
41
+
42
+ const key = styleReference.toLowerCase();
43
+ return typographyMap[key] || typographyMap.linear;
44
+ }
45
+
46
+ /**
47
+ * Get human-readable style description.
48
+ * @param {string} styleReference - One of: linear, notion, stripe, vercel, airbnb
49
+ * @returns {string}
50
+ */
51
+ function getStyleDescription(styleReference) {
52
+ const descriptions = {
53
+ linear: 'Linear (minimal, dark mode friendly, clean typography)',
54
+ notion: 'Notion (friendly, content-focused, approachable)',
55
+ stripe: 'Stripe (professional, elegant, trustworthy)',
56
+ vercel: 'Vercel (modern, geometric, developer-focused)',
57
+ airbnb: 'Airbnb (warm, inviting, human-centered)'
58
+ };
59
+
60
+ const key = styleReference.toLowerCase();
61
+ return descriptions[key] || descriptions.linear;
62
+ }
63
+
64
+ /**
65
+ * Page detail templates for each known page type.
66
+ * @param {string} pageName - Name of the page
67
+ * @returns {{ purpose: string, elements: string[], components: string, userFlow: string }}
68
+ */
69
+ function getPageDetails(pageName) {
70
+ const pageTemplates = {
71
+ // Core pages
72
+ 'Home': {
73
+ purpose: 'Landing page that introduces the product and drives conversions',
74
+ elements: [
75
+ 'Hero section with value proposition',
76
+ 'Feature highlights (3-4 key benefits)',
77
+ 'Social proof (testimonials or logos)',
78
+ 'Pricing preview or CTA section',
79
+ 'Footer with navigation links'
80
+ ],
81
+ components: 'Hero, FeatureCard, Testimonial, PricingPreview, Footer',
82
+ userFlow: 'User lands, scans value prop, reviews features, clicks CTA to sign up'
83
+ },
84
+ 'Dashboard': {
85
+ purpose: 'Central hub showing key metrics and quick actions',
86
+ elements: [
87
+ 'Welcome message with user name',
88
+ 'Metrics cards (4-6 key numbers)',
89
+ 'Recent activity feed',
90
+ 'Quick action buttons',
91
+ 'Status indicators or alerts'
92
+ ],
93
+ components: 'MetricCard, ActivityFeed, Button, Card, Badge',
94
+ userFlow: 'User logs in, sees overview, takes quick action or dives deeper'
95
+ },
96
+ 'Settings': {
97
+ purpose: 'User preferences and account management',
98
+ elements: [
99
+ 'Profile section (name, email, avatar)',
100
+ 'Notification preferences',
101
+ 'Security settings (password, 2FA)',
102
+ 'Connected accounts',
103
+ 'Danger zone (delete account)'
104
+ ],
105
+ components: 'Form, Input, Switch, Avatar, Tabs, AlertDialog',
106
+ userFlow: 'User navigates to settings, updates preferences, saves changes'
107
+ },
108
+ '404': {
109
+ purpose: 'Friendly error page for missing routes',
110
+ elements: [
111
+ 'Clear error message',
112
+ 'Illustration or icon',
113
+ 'Link to home page',
114
+ 'Search suggestion',
115
+ 'Help contact option'
116
+ ],
117
+ components: 'Card, Button, Input',
118
+ userFlow: 'User hits broken link, sees error, clicks to return home'
119
+ },
120
+
121
+ // Auth pages
122
+ 'Login': {
123
+ purpose: 'Authenticate returning users',
124
+ elements: [
125
+ 'Email input field',
126
+ 'Password input with visibility toggle',
127
+ 'Remember me checkbox',
128
+ 'Login button',
129
+ 'Forgot password link',
130
+ 'Sign up redirect link'
131
+ ],
132
+ components: 'Form, Input, Button, Checkbox, Card',
133
+ userFlow: 'User enters credentials, clicks login, redirected to dashboard'
134
+ },
135
+ 'Signup': {
136
+ purpose: 'Register new users',
137
+ elements: [
138
+ 'Name input field',
139
+ 'Email input field',
140
+ 'Password input with strength indicator',
141
+ 'Terms and conditions checkbox',
142
+ 'Sign up button',
143
+ 'Login redirect link'
144
+ ],
145
+ components: 'Form, Input, Button, Checkbox, Card, PasswordStrength',
146
+ userFlow: 'User fills form, accepts terms, clicks signup, receives welcome email'
147
+ },
148
+ 'Forgot Password': {
149
+ purpose: 'Initiate password reset flow',
150
+ elements: [
151
+ 'Email input field',
152
+ 'Submit button',
153
+ 'Success message area',
154
+ 'Back to login link'
155
+ ],
156
+ components: 'Form, Input, Button, Card, Alert',
157
+ userFlow: 'User enters email, clicks submit, checks inbox for reset link'
158
+ },
159
+ 'Reset Password': {
160
+ purpose: 'Set new password after reset request',
161
+ elements: [
162
+ 'New password input',
163
+ 'Confirm password input',
164
+ 'Password requirements list',
165
+ 'Submit button',
166
+ 'Success redirect'
167
+ ],
168
+ components: 'Form, Input, Button, Card, Alert',
169
+ userFlow: 'User sets new password, confirms it, clicks submit, redirected to login'
170
+ },
171
+
172
+ // Payment pages
173
+ 'Pricing': {
174
+ purpose: 'Display plans and drive subscription decisions',
175
+ elements: [
176
+ 'Plan cards (3 tiers recommended)',
177
+ 'Feature comparison table',
178
+ 'Toggle for monthly/annual pricing',
179
+ 'CTA buttons per plan',
180
+ 'FAQ section',
181
+ 'Money-back guarantee badge'
182
+ ],
183
+ components: 'PricingCard, Table, Toggle, Button, Accordion, Badge',
184
+ userFlow: 'User compares plans, toggles billing cycle, selects plan, proceeds to checkout'
185
+ },
186
+ 'Checkout': {
187
+ purpose: 'Complete payment transaction',
188
+ elements: [
189
+ 'Order summary card',
190
+ 'Payment method selection',
191
+ 'Payment form (card details)',
192
+ 'Billing address form',
193
+ 'Apply coupon input',
194
+ 'Secure payment badge',
195
+ 'Pay button with amount'
196
+ ],
197
+ components: 'Card, Form, Input, Button, RadioGroup, Badge',
198
+ userFlow: 'User reviews order, enters payment details, clicks pay, sees confirmation'
199
+ },
200
+ 'Billing History': {
201
+ purpose: 'View past invoices and payment methods',
202
+ elements: [
203
+ 'Invoice table with download links',
204
+ 'Current subscription status',
205
+ 'Payment methods list',
206
+ 'Add payment method button',
207
+ 'Cancel subscription option'
208
+ ],
209
+ components: 'Table, Card, Button, Badge, Dialog',
210
+ userFlow: 'User views invoices, downloads receipts, manages payment methods'
211
+ },
212
+
213
+ // Multi-tenancy pages
214
+ 'Team Settings': {
215
+ purpose: 'Manage team configuration and branding',
216
+ elements: [
217
+ 'Team name input',
218
+ 'Team logo upload',
219
+ 'Team URL/slug setting',
220
+ 'Member list with roles',
221
+ 'Remove member actions',
222
+ 'Team deletion option'
223
+ ],
224
+ components: 'Form, Input, Avatar, Table, Button, AlertDialog',
225
+ userFlow: 'Admin updates team info, manages members, saves changes'
226
+ },
227
+ 'Invite Members': {
228
+ purpose: 'Add new team members via email invitation',
229
+ elements: [
230
+ 'Email input field',
231
+ 'Role selector dropdown',
232
+ 'Send invite button',
233
+ 'Pending invitations list',
234
+ 'Resend/cancel invite actions'
235
+ ],
236
+ components: 'Form, Input, Select, Button, Table, Badge',
237
+ userFlow: 'Admin enters email, selects role, sends invite, tracks pending invites'
238
+ },
239
+ 'Role Management': {
240
+ purpose: 'Configure team roles and permissions',
241
+ elements: [
242
+ 'Roles table',
243
+ 'Permission matrix (role x permission)',
244
+ 'Create role button',
245
+ 'Edit role dialog',
246
+ 'Default role indicator'
247
+ ],
248
+ components: 'Table, Checkbox, Button, Dialog, Badge',
249
+ userFlow: 'Admin views roles, edits permissions, creates custom roles'
250
+ },
251
+
252
+ // Credits pages
253
+ 'Usage Dashboard': {
254
+ purpose: 'Monitor credit consumption and balance',
255
+ elements: [
256
+ 'Credit balance card',
257
+ 'Usage chart (time series)',
258
+ 'Usage breakdown by feature',
259
+ 'Low credit warning',
260
+ 'Top up CTA button'
261
+ ],
262
+ components: 'Card, Chart, Progress, Alert, Button',
263
+ userFlow: 'User checks balance, reviews usage patterns, tops up if needed'
264
+ },
265
+ 'Purchase Credits': {
266
+ purpose: 'Buy additional credits',
267
+ elements: [
268
+ 'Credit package options',
269
+ 'Quantity selector',
270
+ 'Bulk discount indicator',
271
+ 'Price summary',
272
+ 'Checkout button'
273
+ ],
274
+ components: 'Card, RadioGroup, Input, Button, Badge',
275
+ userFlow: 'User selects package, adjusts quantity, proceeds to payment'
276
+ },
277
+
278
+ // Admin pages
279
+ 'Admin Panel': {
280
+ purpose: 'System administration and monitoring',
281
+ elements: [
282
+ 'User statistics card',
283
+ 'Revenue metrics',
284
+ 'System health indicators',
285
+ 'Recent signups list',
286
+ 'Quick admin actions'
287
+ ],
288
+ components: 'Card, Chart, Table, Badge, Button',
289
+ userFlow: 'Admin reviews metrics, monitors health, takes administrative actions'
290
+ },
291
+
292
+ // Profile page
293
+ 'Profile': {
294
+ purpose: 'View and edit user profile information',
295
+ elements: [
296
+ 'Avatar with upload option',
297
+ 'Display name field',
298
+ 'Email field (read-only)',
299
+ 'Bio/about text area',
300
+ 'Save button'
301
+ ],
302
+ components: 'Form, Input, Textarea, Avatar, Button, Card',
303
+ userFlow: 'User views profile, updates information, saves changes'
304
+ },
305
+
306
+ // Reports page
307
+ 'Reports': {
308
+ purpose: 'View detailed analytics and generate reports',
309
+ elements: [
310
+ 'Date range selector',
311
+ 'Report type selector',
312
+ 'Data visualization charts',
313
+ 'Export options (CSV, PDF)',
314
+ 'Saved reports list'
315
+ ],
316
+ components: 'DatePicker, Select, Chart, Button, Table',
317
+ userFlow: 'User selects date range, chooses report type, views data, exports'
318
+ }
319
+ };
320
+
321
+ // Return template or fallback for unknown pages
322
+ return pageTemplates[pageName] || {
323
+ purpose: `${pageName} functionality`,
324
+ elements: [
325
+ 'Main content area',
326
+ 'Navigation elements',
327
+ 'Action buttons',
328
+ 'Status indicators'
329
+ ],
330
+ components: 'Card, Button, Form',
331
+ userFlow: 'User interacts with page content and takes relevant actions'
332
+ };
333
+ }
334
+
335
+ /**
336
+ * Generate comprehensive Lovable prompt from design system and pages.
337
+ * @param {{ primaryColor: string, accentColor: string, styleReference: string, brandVoice: string }} designSystem
338
+ * @param {string[]} pages - Array of page names
339
+ * @param {string} projectName - Name of the project
340
+ * @returns {string} - Complete markdown prompt for Lovable
341
+ */
342
+ function generateLovablePrompt(designSystem, pages, projectName) {
343
+ const lines = [];
344
+ const { primaryColor, accentColor, styleReference, brandVoice } = designSystem;
345
+ const typography = getTypography(styleReference);
346
+ const styleDescription = getStyleDescription(styleReference);
347
+
348
+ // Header
349
+ lines.push(`# ${projectName} - Complete UI Design`);
350
+ lines.push('');
351
+
352
+ // Design System section
353
+ lines.push('## Design System');
354
+ lines.push('');
355
+ lines.push(`**Style Reference:** ${styleDescription}`);
356
+ lines.push(`**Primary Color:** ${primaryColor}`);
357
+ lines.push(`**Accent Color:** ${accentColor}`);
358
+ lines.push(`**Brand Voice:** ${brandVoice}`);
359
+ lines.push('');
360
+ lines.push('**Typography:**');
361
+ lines.push(`- Headings: ${typography.headings}`);
362
+ lines.push(`- Body: ${typography.body}`);
363
+ lines.push(`- Characteristics: ${typography.characteristics}`);
364
+ lines.push('');
365
+
366
+ // Anti-Patterns section (CRITICAL for 2026)
367
+ lines.push('## Anti-Patterns to Avoid');
368
+ lines.push('');
369
+ lines.push('Create distinctive, human-centered design. DO NOT use:');
370
+ lines.push('');
371
+ lines.push('- Generic purple-to-blue gradients (the "AI aesthetic")');
372
+ lines.push('- Stock photo placeholder images with perfect diversity casting');
373
+ lines.push('- Overly rounded everything (8px border-radius on all elements)');
374
+ lines.push('- Generic "Get Started" hero sections with floating mockups');
375
+ lines.push('- Emoji-fueled faux-minimalism that feels hollow');
376
+ lines.push('- Same-y corporate filler aesthetics with meaningless taglines');
377
+ lines.push('');
378
+ lines.push('**Instead:** Add texture, intentional friction, human imperfection, and genuine brand personality. Make it feel crafted, not generated.');
379
+ lines.push('');
380
+
381
+ // Pages section
382
+ lines.push('## Pages');
383
+ lines.push('');
384
+
385
+ for (let i = 0; i < pages.length; i++) {
386
+ const pageName = pages[i];
387
+ const details = getPageDetails(pageName);
388
+
389
+ lines.push(`### ${i + 1}. ${pageName}`);
390
+ lines.push('');
391
+ lines.push(`**Purpose:** ${details.purpose}`);
392
+ lines.push('');
393
+ lines.push('**Key Elements:**');
394
+ for (const element of details.elements) {
395
+ lines.push(`- ${element}`);
396
+ }
397
+ lines.push('');
398
+ lines.push(`**Components:** ${details.components}`);
399
+ lines.push('');
400
+ lines.push(`**User Flow:** ${details.userFlow}`);
401
+ lines.push('');
402
+ }
403
+
404
+ // Global Notes section
405
+ lines.push('## Global Notes');
406
+ lines.push('');
407
+ lines.push('- Use Tailwind CSS with shadcn/ui components');
408
+ lines.push('- Follow responsive design principles (mobile-first)');
409
+ lines.push('- Ensure accessibility compliance (WCAG 2.1 AA)');
410
+ lines.push('- Include loading states for async operations');
411
+ lines.push('- Include empty states for lists and tables');
412
+ lines.push('- Add subtle hover effects and micro-interactions');
413
+ lines.push('- Use consistent spacing based on 4px/8px grid');
414
+ lines.push('- Implement smooth transitions (150-300ms duration)');
415
+ lines.push('');
416
+
417
+ // Footer
418
+ lines.push('---');
419
+ lines.push('*Generated by PropelKit AI PM*');
420
+
421
+ return lines.join('\n');
422
+ }
423
+
424
+ module.exports = {
425
+ generateLovablePrompt,
426
+ getTypography,
427
+ getStyleDescription,
428
+ getPageDetails
429
+ };
@@ -0,0 +1,217 @@
1
+ const { execSync } = require('child_process');
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+ const inquirer = require('inquirer');
5
+ const chalk = require('chalk');
6
+ const _messages = require('./messages'); // Reserved for future use
7
+ const { REPOS } = require('./config');
8
+
9
+ /**
10
+ * Ask user which scenario applies
11
+ * @returns {Promise<'fresh'|'existing'>}
12
+ */
13
+ async function askScenario() {
14
+ const { scenario } = await inquirer.prompt([
15
+ {
16
+ type: 'list',
17
+ name: 'scenario',
18
+ message: 'What would you like to do?',
19
+ choices: [
20
+ {
21
+ name: 'Start fresh - Clone PropelKit and create new project',
22
+ value: 'fresh'
23
+ },
24
+ {
25
+ name: 'Configure existing - I already cloned PropelKit',
26
+ value: 'existing'
27
+ }
28
+ ]
29
+ }
30
+ ]);
31
+ return scenario;
32
+ }
33
+
34
+ /**
35
+ * Handle fresh start scenario - clone repo
36
+ * @param {string} tier - License tier ('starter' or 'pro')
37
+ * @param {object} clis - CLI detection results
38
+ * @param {boolean} useGitHub - Whether user wants GitHub integration (from CLI-03 prompt)
39
+ * @returns {Promise<string>} - Path to project directory
40
+ */
41
+ async function handleFreshStart(tier, clis, useGitHub) {
42
+ // Ask for project name/directory
43
+ const { projectName } = await inquirer.prompt([
44
+ {
45
+ type: 'input',
46
+ name: 'projectName',
47
+ message: 'Project name (will create directory):',
48
+ default: 'my-saas',
49
+ validate: (input) => {
50
+ if (!input.trim()) return 'Project name is required';
51
+ if (fs.existsSync(input)) return `Directory "${input}" already exists`;
52
+ if (!/^[a-z0-9-_]+$/i.test(input)) return 'Use letters, numbers, dashes, underscores only';
53
+ return true;
54
+ }
55
+ }
56
+ ]);
57
+
58
+ const targetDir = path.resolve(process.cwd(), projectName);
59
+ const repoUrl = REPOS[tier]; // 'starter' or 'pro'
60
+ const tierLabel = tier.charAt(0).toUpperCase() + tier.slice(1);
61
+
62
+ console.log('');
63
+ console.log(chalk.cyan(`Cloning PropelKit ${tierLabel}...`));
64
+ console.log(chalk.dim(` From: ${repoUrl}`));
65
+ console.log(chalk.dim(` To: ${targetDir}`));
66
+ console.log('');
67
+
68
+ try {
69
+ // Clone the repo
70
+ execSync(`git clone ${repoUrl} "${targetDir}"`, {
71
+ stdio: 'inherit'
72
+ });
73
+
74
+ // Remove .git to start fresh
75
+ const gitDir = path.join(targetDir, '.git');
76
+ if (fs.existsSync(gitDir)) {
77
+ fs.rmSync(gitDir, { recursive: true, force: true });
78
+ }
79
+
80
+ // Initialize fresh git repo
81
+ execSync('git init', { cwd: targetDir, stdio: 'pipe' });
82
+
83
+ console.log('');
84
+ console.log(chalk.green(`PropelKit ${tierLabel} cloned successfully!`));
85
+ console.log('');
86
+
87
+ // Write initial config with CLI availability, useGitHub preference, and tier
88
+ await writeInitialConfig(targetDir, clis, useGitHub, tier);
89
+
90
+ return targetDir;
91
+ } catch (error) {
92
+ throw new Error(`Failed to clone PropelKit: ${error.message}`);
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Handle existing clone scenario
98
+ * @param {string} tier - License tier ('starter' or 'pro')
99
+ * @param {object} clis - CLI detection results
100
+ * @param {boolean} useGitHub - Whether user wants GitHub integration (from CLI-03 prompt)
101
+ * @returns {Promise<string>} - Path to project directory
102
+ */
103
+ async function handleExistingClone(tier, clis, useGitHub) {
104
+ const currentDir = process.cwd();
105
+
106
+ // Check if this looks like a PropelKit project
107
+ const hasPackageJson = fs.existsSync(path.join(currentDir, 'package.json'));
108
+ const hasPropelkitCommands = fs.existsSync(path.join(currentDir, '.claude', 'commands', 'propelkit'));
109
+
110
+ if (!hasPackageJson) {
111
+ console.log('');
112
+ console.log(chalk.yellow('Warning: No package.json found in current directory.'));
113
+ console.log(chalk.dim('Make sure you\'re in the PropelKit project root.'));
114
+ console.log('');
115
+
116
+ const { proceed } = await inquirer.prompt([
117
+ {
118
+ type: 'confirm',
119
+ name: 'proceed',
120
+ message: 'Continue anyway?',
121
+ default: false
122
+ }
123
+ ]);
124
+
125
+ if (!proceed) {
126
+ throw new Error('Aborted by user');
127
+ }
128
+ }
129
+
130
+ if (!hasPropelkitCommands) {
131
+ console.log('');
132
+ console.log(chalk.yellow('Warning: PropelKit commands not found at .claude/commands/propelkit/'));
133
+ console.log(chalk.dim('This may not be a PropelKit project or it may be outdated.'));
134
+ console.log('');
135
+ }
136
+
137
+ // Write config with CLI availability, useGitHub preference, and tier
138
+ await writeInitialConfig(currentDir, clis, useGitHub, tier);
139
+
140
+ return currentDir;
141
+ }
142
+
143
+ /**
144
+ * Write initial .planning/config.json with CLI availability, GitHub preference, and tier
145
+ * @param {string} projectDir - Project directory
146
+ * @param {object[]} clis - CLI detection results
147
+ * @param {boolean} useGitHub - Whether user wants GitHub integration (CLI-03)
148
+ * @param {string} tier - License tier ('starter' or 'pro')
149
+ */
150
+ async function writeInitialConfig(projectDir, clis, useGitHub, tier) {
151
+ const planningDir = path.join(projectDir, '.planning');
152
+
153
+ // Create .planning directory if needed
154
+ if (!fs.existsSync(planningDir)) {
155
+ fs.mkdirSync(planningDir, { recursive: true });
156
+ }
157
+
158
+ // Build cliAvailable object from detection results
159
+ const cliAvailable = {};
160
+ for (const cli of clis) {
161
+ cliAvailable[cli.name] = cli.installed;
162
+ }
163
+
164
+ // Check if config already exists
165
+ const configPath = path.join(planningDir, 'config.json');
166
+ let config = {};
167
+
168
+ if (fs.existsSync(configPath)) {
169
+ try {
170
+ config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
171
+ } catch (_e) {
172
+ // Invalid JSON, start fresh
173
+ }
174
+ }
175
+
176
+ // Merge CLI availability into config
177
+ config.cliAvailable = cliAvailable;
178
+
179
+ // Store GitHub integration preference (CLI-03 requirement)
180
+ config.useGitHub = useGitHub;
181
+
182
+ // Store tier for later use
183
+ config.tier = tier;
184
+
185
+ // Write config
186
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
187
+
188
+ console.log(chalk.dim(`CLI availability saved to .planning/config.json`));
189
+ console.log(chalk.dim(`Tier: ${tier}`));
190
+ if (useGitHub) {
191
+ console.log(chalk.dim(`GitHub integration: enabled`));
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Main scenario handler
197
+ * @param {string} tier - License tier ('starter' or 'pro')
198
+ * @param {object[]} clis - CLI detection results
199
+ * @param {boolean} useGitHub - Whether user wants GitHub integration (from CLI-03 prompt)
200
+ * @returns {Promise<string>} - Path to project directory
201
+ */
202
+ async function handleScenario(tier, clis, useGitHub) {
203
+ const scenario = await askScenario();
204
+
205
+ if (scenario === 'fresh') {
206
+ return handleFreshStart(tier, clis, useGitHub);
207
+ } else {
208
+ return handleExistingClone(tier, clis, useGitHub);
209
+ }
210
+ }
211
+
212
+ module.exports = {
213
+ askScenario,
214
+ handleFreshStart,
215
+ handleExistingClone,
216
+ handleScenario
217
+ };