ultra-dex 3.1.0 → 3.3.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.
Files changed (52) hide show
  1. package/README.md +79 -74
  2. package/assets/code-patterns/clerk-middleware.ts +138 -0
  3. package/assets/code-patterns/prisma-schema.prisma +224 -0
  4. package/assets/code-patterns/rls-policies.sql +246 -0
  5. package/assets/code-patterns/server-actions.ts +191 -0
  6. package/assets/code-patterns/trpc-router.ts +258 -0
  7. package/assets/cursor-rules/13-ai-integration.mdc +155 -0
  8. package/assets/cursor-rules/14-server-components.mdc +81 -0
  9. package/assets/cursor-rules/15-server-actions.mdc +102 -0
  10. package/assets/cursor-rules/16-edge-middleware.mdc +105 -0
  11. package/assets/cursor-rules/17-streaming-ssr.mdc +138 -0
  12. package/bin/ultra-dex.js +50 -1
  13. package/lib/commands/agents.js +16 -13
  14. package/lib/commands/banner.js +43 -21
  15. package/lib/commands/build.js +26 -17
  16. package/lib/commands/cloud.js +780 -0
  17. package/lib/commands/doctor.js +98 -79
  18. package/lib/commands/exec.js +434 -0
  19. package/lib/commands/generate.js +19 -16
  20. package/lib/commands/github.js +475 -0
  21. package/lib/commands/init.js +52 -56
  22. package/lib/commands/scaffold.js +151 -0
  23. package/lib/commands/search.js +477 -0
  24. package/lib/commands/serve.js +15 -13
  25. package/lib/commands/state.js +43 -70
  26. package/lib/commands/swarm.js +31 -9
  27. package/lib/config/theme.js +47 -0
  28. package/lib/mcp/client.js +502 -0
  29. package/lib/providers/agent-sdk.js +630 -0
  30. package/lib/providers/anthropic-agents.js +580 -0
  31. package/lib/templates/code/clerk-middleware.ts +138 -0
  32. package/lib/templates/code/prisma-schema.prisma +224 -0
  33. package/lib/templates/code/rls-policies.sql +246 -0
  34. package/lib/templates/code/server-actions.ts +191 -0
  35. package/lib/templates/code/trpc-router.ts +258 -0
  36. package/lib/themes/doomsday.js +229 -0
  37. package/lib/ui/index.js +5 -0
  38. package/lib/ui/interface.js +241 -0
  39. package/lib/ui/spinners.js +116 -0
  40. package/lib/ui/theme.js +183 -0
  41. package/lib/utils/agents.js +32 -0
  42. package/lib/utils/browser.js +373 -0
  43. package/lib/utils/help.js +64 -0
  44. package/lib/utils/messages.js +35 -0
  45. package/lib/utils/progress.js +24 -0
  46. package/lib/utils/prompts.js +47 -0
  47. package/lib/utils/spinners.js +46 -0
  48. package/lib/utils/status.js +31 -0
  49. package/lib/utils/tables.js +41 -0
  50. package/lib/utils/theme-state.js +9 -0
  51. package/lib/utils/version-display.js +32 -0
  52. package/package.json +19 -4
@@ -8,9 +8,10 @@ import { QUICK_START_TEMPLATE } from '../templates/quick-start.js';
8
8
  import { CONTEXT_TEMPLATE } from '../templates/context.js';
9
9
  import { validateProjectName, validateSafePath } from '../utils/validation.js';
10
10
  import { ASSETS_ROOT, ROOT_FALLBACK, LIVE_TEMPLATES_ROOT } from '../config/paths.js';
11
- import { githubBlobUrl, githubWebUrl } from '../config/urls.js';
11
+ import { githubBlobUrl } from '../config/urls.js';
12
12
  import { copyWithFallback, listWithFallback, readWithFallback } from '../utils/fallback.js';
13
13
  import { copyDirectory, pathExists } from '../utils/files.js';
14
+ import { getRandomMessage } from '../utils/messages.js';
14
15
 
15
16
  const LIVE_STACKS = {
16
17
  'next15-prisma-clerk': 'Next.js 15 + Prisma + Clerk',
@@ -21,7 +22,7 @@ const LIVE_STACKS = {
21
22
  export function registerInitCommand(program) {
22
23
  program
23
24
  .command('init')
24
- .description('Initialize a new Ultra-Dex project')
25
+ .description('Initialize a new Ultra-Dex Project')
25
26
  .option('-n, --name <name>', 'Project name')
26
27
  .option('-d, --dir <directory>', 'Output directory', '.')
27
28
  .option('--preview', 'Preview files without creating them')
@@ -29,16 +30,17 @@ export function registerInitCommand(program) {
29
30
  .option('--stack <preset>', 'Preset: next15-prisma-clerk, remix-supabase, sveltekit-drizzle')
30
31
  .action(async (options) => {
31
32
  console.log(chalk.cyan(program.banner));
32
- console.log(chalk.bold('\nWelcome to Ultra-Dex! Let\'s plan your SaaS.\n'));
33
+ console.log(chalk.hex('#8b5cf6').bold('\n⚔ INITIALIZING PROJECT PROTOCOL...\n'));
34
+ console.log(chalk.italic(chalk.gray(`"${getRandomMessage('start')}"`)));
35
+ console.log('');
33
36
 
34
37
  if (options.preview) {
35
- console.log('\nšŸ“‹ Files that would be created:\n');
38
+ console.log('\nšŸ“‹ Project Preview (Files that would be created):\n');
36
39
  console.log(' QUICK-START.md');
37
40
  console.log(' CONTEXT.md');
38
41
  console.log(' IMPLEMENTATION-PLAN.md');
39
42
  console.log(' docs/CHECKLIST.md');
40
43
  console.log(' docs/AI-PROMPTS.md');
41
- console.log('\nRun without --preview to create files.');
42
44
  return;
43
45
  }
44
46
 
@@ -60,7 +62,7 @@ export function registerInitCommand(program) {
60
62
  if (await pathExists(outputDir, 'dir')) {
61
63
  const existing = await fs.readdir(outputDir);
62
64
  if (existing.length > 0) {
63
- console.log(chalk.red('Target directory is not empty.'));
65
+ console.log(chalk.red('Target directory is not empty. Execution halted to prevent data loss.'));
64
66
  process.exit(1);
65
67
  }
66
68
  }
@@ -77,14 +79,14 @@ export function registerInitCommand(program) {
77
79
  const spinner = ora(`Generating ${LIVE_STACKS[preset]} scaffold...`).start();
78
80
  try {
79
81
  await copyDirectory(sourcePath, outputDir);
80
- spinner.succeed(chalk.green('Live scaffold created successfully!'));
82
+ spinner.succeed(chalk.green('Project scaffold generated successfully!'));
81
83
  console.log(chalk.gray(`\nPreset: ${preset}`));
82
84
  console.log(chalk.gray(`Next steps:`));
83
85
  console.log(chalk.cyan(` 1. cd ${outputDir}`));
84
86
  console.log(chalk.cyan(' 2. npm install'));
85
87
  console.log(chalk.cyan(' 3. npm run dev\n'));
86
88
  } catch (error) {
87
- spinner.fail(chalk.red('Failed to create live scaffold'));
89
+ spinner.fail(chalk.red('Failed to generate project scaffold'));
88
90
  console.error(`[init] ${error?.message ?? error}`);
89
91
  process.exit(1);
90
92
  }
@@ -95,103 +97,103 @@ export function registerInitCommand(program) {
95
97
  {
96
98
  type: 'input',
97
99
  name: 'projectName',
98
- message: 'What\'s your project name?',
100
+ message: 'What is the name of this project?',
99
101
  default: options.name || 'my-saas',
100
102
  validate: validateProjectName,
101
103
  },
102
104
  {
103
105
  type: 'input',
104
106
  name: 'ideaWhat',
105
- message: 'What are you building? (1 sentence)',
106
- validate: (input) => input.length > 0 || 'Please describe your idea',
107
+ message: 'Define the core purpose (What are you building?):',
108
+ validate: (input) => input.length > 0 || 'Please describe your project',
107
109
  },
108
110
  {
109
111
  type: 'input',
110
112
  name: 'ideaFor',
111
- message: 'Who is it for?',
112
- validate: (input) => input.length > 0 || 'Please specify your target users',
113
+ message: 'Target audience (Who are the users)?',
114
+ validate: (input) => input.length > 0 || 'Please specify your target audience',
113
115
  },
114
116
  {
115
117
  type: 'input',
116
118
  name: 'problem1',
117
- message: 'Problem #1 you\'re solving:',
119
+ message: 'Primary problem to solve:',
118
120
  default: '',
119
121
  },
120
122
  {
121
123
  type: 'input',
122
124
  name: 'problem2',
123
- message: 'Problem #2 you\'re solving:',
125
+ message: 'Secondary problem to solve:',
124
126
  default: '',
125
127
  },
126
128
  {
127
129
  type: 'input',
128
130
  name: 'problem3',
129
- message: 'Problem #3 you\'re solving:',
131
+ message: 'Tertiary problem to solve:',
130
132
  default: '',
131
133
  },
132
134
  {
133
135
  type: 'input',
134
136
  name: 'feature1',
135
- message: 'Critical production feature:',
137
+ message: 'Core feature:',
136
138
  default: '',
137
139
  },
138
140
  {
139
141
  type: 'list',
140
142
  name: 'frontend',
141
- message: 'Frontend framework:',
143
+ message: 'Frontend Technology Stack:',
142
144
  choices: ['Next.js', 'Remix', 'SvelteKit', 'Nuxt', 'Other'],
143
145
  },
144
146
  {
145
147
  type: 'list',
146
148
  name: 'database',
147
- message: 'Database:',
149
+ message: 'Database Infrastructure:',
148
150
  choices: ['PostgreSQL', 'Supabase', 'MongoDB', 'PlanetScale', 'Other'],
149
151
  },
150
152
  {
151
153
  type: 'list',
152
154
  name: 'auth',
153
- message: 'Authentication:',
155
+ message: 'Authentication Provider:',
154
156
  choices: ['NextAuth', 'Clerk', 'Auth0', 'Supabase Auth', 'Other'],
155
157
  },
156
158
  {
157
159
  type: 'list',
158
160
  name: 'payments',
159
- message: 'Payments:',
161
+ message: 'Payment Processor:',
160
162
  choices: ['Stripe', 'Lemonsqueezy', 'Paddle', 'None (free)', 'Other'],
161
163
  },
162
164
  {
163
165
  type: 'list',
164
166
  name: 'hosting',
165
- message: 'Hosting:',
167
+ message: 'Deployment Platform:',
166
168
  choices: ['Vercel', 'Railway', 'Fly.io', 'AWS', 'Other'],
167
169
  },
168
170
  {
169
171
  type: 'confirm',
170
172
  name: 'includeCursorRules',
171
- message: 'Include cursor-rules for AI assistants? (Cursor, Copilot)',
173
+ message: 'Install IDE intelligence protocols? (Cursor/Copilot Rules)',
172
174
  default: true,
173
175
  },
174
176
  {
175
177
  type: 'confirm',
176
178
  name: 'includeFullTemplate',
177
- message: 'Copy full 34-section template locally?',
179
+ message: 'Include full 34-section project template?',
178
180
  default: false,
179
181
  },
180
182
  {
181
183
  type: 'confirm',
182
184
  name: 'includeDocs',
183
- message: 'Copy VERIFICATION.md & AGENT-INSTRUCTIONS.md to docs/?',
185
+ message: 'Include project documentation standards?',
184
186
  default: true,
185
187
  },
186
188
  {
187
189
  type: 'confirm',
188
190
  name: 'includeAgents',
189
- message: 'Include AI agent prompts? (.agents/ folder)',
191
+ message: 'Configure AI agent orchestration?',
190
192
  default: true,
191
193
  },
192
194
  ]);
193
195
 
194
- const spinner = ora('Creating project files...').start();
196
+ const spinner = ora(getRandomMessage('loading')).start();
195
197
 
196
198
  try {
197
199
  const outputDir = path.resolve(options.dir, answers.projectName);
@@ -239,15 +241,8 @@ ${answers.ideaWhat} for ${answers.ideaFor}.
239
241
  ## Next Steps
240
242
 
241
243
  1. Open QUICK-START.md and complete the remaining sections
242
- 2. Copy sections from the full Ultra-Dex template as needed
243
- 3. Use the TaskFlow example as reference
244
- 4. Start building!
245
-
246
- ## Resources
247
-
248
- - [Full Template](${githubBlobUrl('@%20Ultra%20DeX/Saas%20plan/04-Imp-Template.md')})
249
- - [TaskFlow Example](${githubBlobUrl('@%20Ultra%20DeX/Saas%20plan/Examples/TaskFlow-Complete.md')})
250
- - [Methodology](${githubBlobUrl('@%20Ultra%20DeX/Saas%20plan/03-METHODOLOGY.md')})
244
+ 2. Customize the implementation plan based on your requirements
245
+ 3. Start the agent orchestration to begin development
251
246
  `;
252
247
 
253
248
  await fs.writeFile(path.join(outputDir, 'IMPLEMENTATION-PLAN.md'), planContent);
@@ -274,11 +269,11 @@ ${answers.ideaWhat} for ${answers.ideaFor}.
274
269
  await fs.mkdir(dotGithub, { recursive: true });
275
270
  await fs.writeFile(path.join(dotGithub, 'copilot-instructions.md'), coreContent);
276
271
  } catch {
277
- // Core rule not available - skip Copilot setup
272
+ // Core rule not available
278
273
  }
279
274
  } catch {
280
- console.log(chalk.red('\n āŒ Cursor rules not found in assets or repo.'));
281
- console.log(chalk.cyan(' Fetch: npx ultra-dex fetch --rules'));
275
+ console.log(chalk.red('\n āœ• IDE intelligence protocols not found.'));
276
+ console.log(chalk.cyan(' Run: ultra-dex fetch --rules'));
282
277
  }
283
278
  }
284
279
 
@@ -288,8 +283,8 @@ ${answers.ideaWhat} for ${answers.ideaFor}.
288
283
  try {
289
284
  await copyWithFallback(templatePath, fallbackTemplatePath, path.join(outputDir, 'docs', 'MASTER-PLAN.md'));
290
285
  } catch {
291
- console.log(chalk.red('\n āŒ Full template not found in assets or repo.'));
292
- console.log(chalk.cyan(' Fetch: npx ultra-dex fetch --docs'));
286
+ console.log(chalk.red('\n āœ• Project template not found.'));
287
+ console.log(chalk.cyan(' Run: ultra-dex fetch --docs'));
293
288
  }
294
289
  }
295
290
 
@@ -302,8 +297,8 @@ ${answers.ideaWhat} for ${answers.ideaFor}.
302
297
  await copyWithFallback(verificationPath, fallbackVerificationPath, path.join(outputDir, 'docs', 'CHECKLIST.md'));
303
298
  await copyWithFallback(agentPath, fallbackAgentPath, path.join(outputDir, 'docs', 'AI-PROMPTS.md'));
304
299
  } catch {
305
- console.log(chalk.red('\n āŒ Docs not found in assets or repo.'));
306
- console.log(chalk.cyan(' Fetch: npx ultra-dex fetch --docs'));
300
+ console.log(chalk.red('\n āœ• Documentation standards not found.'));
301
+ console.log(chalk.cyan(' Run: ultra-dex fetch --docs'));
307
302
  }
308
303
  }
309
304
 
@@ -345,43 +340,44 @@ ${answers.ideaWhat} for ${answers.ideaFor}.
345
340
  path.join(agentsDir, 'README.md')
346
341
  );
347
342
  } catch {
348
- console.log(chalk.red('\n āŒ Agent prompts not found in assets or repo.'));
349
- console.log(chalk.cyan(' Fetch: npx ultra-dex fetch --agents'));
343
+ console.log(chalk.red('\n āœ• Agent orchestration assets not found.'));
344
+ console.log(chalk.cyan(' Run: ultra-dex fetch --agents'));
350
345
  }
351
346
  }
352
347
 
353
- spinner.succeed(chalk.green('Project created successfully!'));
348
+ spinner.succeed(chalk.green('Project initialized successfully!'));
354
349
 
355
- console.log('\n' + chalk.bold('Files created:'));
350
+ console.log('\n' + chalk.bold('Artifacts created:'));
356
351
  console.log(chalk.gray(` ${outputDir}/`));
357
352
  console.log(chalk.gray(' ā”œā”€ā”€ QUICK-START.md'));
358
353
  console.log(chalk.gray(' ā”œā”€ā”€ CONTEXT.md'));
359
354
  console.log(chalk.gray(' ā”œā”€ā”€ IMPLEMENTATION-PLAN.md'));
360
355
  if (answers.includeFullTemplate) {
361
- console.log(chalk.gray(' ā”œā”€ā”€ docs/MASTER-PLAN.md (34 sections)'));
356
+ console.log(chalk.gray(' ā”œā”€ā”€ docs/MASTER-PLAN.md'));
362
357
  }
363
358
  if (answers.includeDocs) {
364
359
  console.log(chalk.gray(' ā”œā”€ā”€ docs/CHECKLIST.md'));
365
360
  console.log(chalk.gray(' ā”œā”€ā”€ docs/AI-PROMPTS.md'));
366
361
  }
367
362
  if (answers.includeCursorRules) {
368
- console.log(chalk.gray(' ā”œā”€ā”€ .cursor/rules/ (11 AI rule files)'));
363
+ console.log(chalk.gray(' ā”œā”€ā”€ .cursor/rules/'));
369
364
  }
370
365
  if (answers.includeAgents) {
371
- console.log(chalk.gray(' └── .agents/ (15 AI agent prompts in 6 tiers)'));
366
+ console.log(chalk.gray(' └── .agents/'));
372
367
  }
373
368
 
374
369
  console.log('\n' + chalk.bold('Next steps:'));
375
370
  console.log(chalk.cyan(` 1. cd ${answers.projectName}`));
376
- console.log(chalk.cyan(' 2. Open QUICK-START.md and complete it'));
377
- console.log(chalk.cyan(' 3. Start building! šŸš€'));
371
+ console.log(chalk.cyan(' 2. Open QUICK-START.md'));
372
+ console.log(chalk.cyan(' 3. ultra-dex swarm "Analyze requirements"'));
378
373
 
379
- console.log('\n' + chalk.gray('Full Ultra-Dex repo:'));
380
- console.log(chalk.blue(` ${githubWebUrl()}`));
374
+ console.log('\n' + chalk.hex('#8b5cf6').bold(' āœ“ SYSTEM READY.'));
375
+ console.log('');
376
+
381
377
  } catch (error) {
382
- spinner.fail(chalk.red('Failed to create project'));
378
+ spinner.fail(chalk.red('Failed to initialize project'));
383
379
  console.error(`[init] ${error?.message ?? error}`);
384
380
  process.exit(1);
385
381
  }
386
382
  });
387
- }
383
+ }
@@ -0,0 +1,151 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import fs from 'fs/promises';
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import inquirer from 'inquirer';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+ const TEMPLATES = {
12
+ 'next15-prisma-clerk': {
13
+ name: 'Next.js 15 + Prisma + Clerk',
14
+ description: 'Full-stack SaaS with App Router, Prisma ORM, and Clerk auth',
15
+ stack: ['Next.js 15', 'Prisma', 'Clerk', 'PostgreSQL', 'Tailwind CSS'],
16
+ },
17
+ 'remix-supabase': {
18
+ name: 'Remix + Supabase',
19
+ description: 'Full-stack app with Remix and Supabase backend',
20
+ stack: ['Remix', 'Supabase', 'PostgreSQL', 'Tailwind CSS'],
21
+ },
22
+ 'sveltekit-drizzle': {
23
+ name: 'SvelteKit + Drizzle',
24
+ description: 'SvelteKit app with Drizzle ORM',
25
+ stack: ['SvelteKit', 'Drizzle', 'PostgreSQL', 'Tailwind CSS'],
26
+ },
27
+ };
28
+
29
+ async function copyDirectory(src, dest) {
30
+ await fs.mkdir(dest, { recursive: true });
31
+ const entries = await fs.readdir(src, { withFileTypes: true });
32
+
33
+ for (const entry of entries) {
34
+ const srcPath = path.join(src, entry.name);
35
+ const destPath = path.join(dest, entry.name);
36
+
37
+ if (entry.isDirectory()) {
38
+ await copyDirectory(srcPath, destPath);
39
+ } else {
40
+ await fs.copyFile(srcPath, destPath);
41
+ }
42
+ }
43
+ }
44
+
45
+ export async function scaffoldCommand(templateName, options) {
46
+ console.log(chalk.cyan('\nšŸ—ļø Ultra-Dex Scaffold\n'));
47
+
48
+ // If no template specified, show selection
49
+ if (!templateName) {
50
+ const { selected } = await inquirer.prompt([
51
+ {
52
+ type: 'list',
53
+ name: 'selected',
54
+ message: 'Select a template:',
55
+ choices: Object.entries(TEMPLATES).map(([key, val]) => ({
56
+ name: `${val.name} - ${val.description}`,
57
+ value: key,
58
+ })),
59
+ },
60
+ ]);
61
+ templateName = selected;
62
+ }
63
+
64
+ const template = TEMPLATES[templateName];
65
+ if (!template) {
66
+ console.log(chalk.red(`\nāŒ Template "${templateName}" not found.\n`));
67
+ console.log(chalk.gray('Available templates:'));
68
+ Object.entries(TEMPLATES).forEach(([key, val]) => {
69
+ console.log(chalk.cyan(` - ${key}`) + chalk.gray(` (${val.name})`));
70
+ });
71
+ process.exit(1);
72
+ }
73
+
74
+ const outputDir = options.output || templateName;
75
+ const spinner = ora(`Scaffolding ${template.name}...`).start();
76
+
77
+ try {
78
+ // Find template directory
79
+ const assetsDir = path.resolve(__dirname, '../../assets/live-templates', templateName);
80
+
81
+ try {
82
+ await fs.access(assetsDir);
83
+ } catch {
84
+ spinner.fail('Template files not found in assets');
85
+ console.log(chalk.yellow('\nšŸ’” Templates are bundled with the npm package.'));
86
+ console.log(chalk.gray(' Make sure you have the full package installed.\n'));
87
+ process.exit(1);
88
+ }
89
+
90
+ // Copy template
91
+ await copyDirectory(assetsDir, outputDir);
92
+
93
+ spinner.succeed(`Scaffolded ${template.name}`);
94
+
95
+ console.log(chalk.bold('\nšŸ“ Created files:\n'));
96
+
97
+ async function listFiles(dir, prefix = '') {
98
+ const entries = await fs.readdir(dir, { withFileTypes: true });
99
+ for (const entry of entries) {
100
+ if (entry.isDirectory()) {
101
+ console.log(chalk.gray(` ${prefix}${entry.name}/`));
102
+ await listFiles(path.join(dir, entry.name), prefix + ' ');
103
+ } else {
104
+ console.log(chalk.green(` ${prefix}${entry.name}`));
105
+ }
106
+ }
107
+ }
108
+ await listFiles(outputDir);
109
+
110
+ console.log(chalk.bold('\nšŸš€ Next steps:\n'));
111
+ console.log(chalk.cyan(` cd ${outputDir}`));
112
+ console.log(chalk.cyan(' npm install'));
113
+ console.log(chalk.cyan(' cp .env.example .env.local'));
114
+ console.log(chalk.cyan(' npm run dev'));
115
+
116
+ console.log(chalk.bold('\nšŸ“š Stack:\n'));
117
+ template.stack.forEach(tech => {
118
+ console.log(chalk.gray(` • ${tech}`));
119
+ });
120
+
121
+ console.log(chalk.bold('\nšŸ’” Tips:\n'));
122
+ console.log(chalk.gray(' • Run "ultra-dex init" to add Ultra-Dex planning docs'));
123
+ console.log(chalk.gray(' • Run "ultra-dex generate" to create implementation plan'));
124
+ console.log(chalk.gray(' • Run "ultra-dex agents" to see available AI agents\n'));
125
+
126
+ } catch (error) {
127
+ spinner.fail('Failed to scaffold');
128
+ console.error(chalk.red(error.message));
129
+ process.exit(1);
130
+ }
131
+ }
132
+
133
+ export function registerScaffoldCommand(program) {
134
+ program
135
+ .command('scaffold [template]')
136
+ .description('Generate a production-ready project from a template')
137
+ .option('-o, --output <dir>', 'Output directory')
138
+ .option('--list', 'List available templates')
139
+ .action(async (template, options) => {
140
+ if (options.list) {
141
+ console.log(chalk.cyan('\nšŸ“¦ Available Templates\n'));
142
+ Object.entries(TEMPLATES).forEach(([key, val]) => {
143
+ console.log(chalk.bold(` ${key}`));
144
+ console.log(chalk.gray(` ${val.description}`));
145
+ console.log(chalk.gray(` Stack: ${val.stack.join(', ')}\n`));
146
+ });
147
+ return;
148
+ }
149
+ await scaffoldCommand(template, options);
150
+ });
151
+ }