ultra-dex 1.7.3 → 2.2.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 (119) hide show
  1. package/README.md +160 -127
  2. package/assets/agents/0-orchestration/orchestrator.md +225 -0
  3. package/assets/agents/00-AGENT_INDEX.md +138 -0
  4. package/assets/agents/1-leadership/cto.md +186 -0
  5. package/assets/agents/1-leadership/planner.md +205 -0
  6. package/assets/agents/1-leadership/research.md +285 -0
  7. package/assets/agents/2-development/backend.md +472 -0
  8. package/assets/agents/2-development/database.md +516 -0
  9. package/assets/agents/2-development/frontend.md +144 -0
  10. package/assets/agents/3-security/auth.md +168 -0
  11. package/assets/agents/3-security/security.md +335 -0
  12. package/assets/agents/4-devops/devops.md +587 -0
  13. package/assets/agents/5-quality/debugger.md +188 -0
  14. package/assets/agents/5-quality/documentation.md +167 -0
  15. package/assets/agents/5-quality/reviewer.md +213 -0
  16. package/assets/agents/5-quality/testing.md +280 -0
  17. package/assets/agents/6-specialist/performance.md +323 -0
  18. package/assets/agents/6-specialist/refactoring.md +343 -0
  19. package/assets/agents/AGENT-INSTRUCTIONS.md +315 -0
  20. package/assets/agents/README.md +232 -0
  21. package/assets/cursor-rules/00-ultra-dex-core.mdc +48 -0
  22. package/assets/cursor-rules/01-database.mdc +50 -0
  23. package/assets/cursor-rules/02-api.mdc +81 -0
  24. package/assets/cursor-rules/03-auth.mdc +70 -0
  25. package/assets/cursor-rules/04-frontend.mdc +92 -0
  26. package/assets/cursor-rules/05-payments.mdc +88 -0
  27. package/assets/cursor-rules/06-testing.mdc +104 -0
  28. package/assets/cursor-rules/07-security.mdc +94 -0
  29. package/assets/cursor-rules/08-deployment.mdc +92 -0
  30. package/assets/cursor-rules/09-error-handling.mdc +137 -0
  31. package/assets/cursor-rules/10-performance.mdc +123 -0
  32. package/assets/cursor-rules/11-nextjs-v15.mdc +307 -0
  33. package/assets/cursor-rules/12-multi-tenancy.mdc +282 -0
  34. package/assets/cursor-rules/README.md +78 -0
  35. package/assets/cursor-rules/load.ps1 +108 -0
  36. package/assets/cursor-rules/load.sh +102 -0
  37. package/assets/docs/BUILD-AUTH-30M.md +113 -0
  38. package/assets/docs/CHECKLIST-21-STEP.md +86 -0
  39. package/assets/docs/CODEMAP.md +229 -0
  40. package/assets/docs/CUSTOMIZATION.md +127 -0
  41. package/assets/docs/LAUNCH-POSTS.md +238 -0
  42. package/assets/docs/QUICK-REFERENCE.md +338 -0
  43. package/assets/docs/README.md +21 -0
  44. package/assets/docs/ROADMAP.md +480 -0
  45. package/assets/docs/TROUBLESHOOTING.md +148 -0
  46. package/assets/docs/TUTORIAL.md +182 -0
  47. package/assets/docs/VERIFICATION.md +108 -0
  48. package/assets/docs/VISION-V2.md +187 -0
  49. package/assets/docs/WORKFLOW-DIAGRAMS.md +463 -0
  50. package/assets/docs/index.html +550 -0
  51. package/assets/live-templates/next15-prisma-clerk/.env.example +3 -0
  52. package/assets/live-templates/next15-prisma-clerk/README.md +10 -0
  53. package/assets/live-templates/next15-prisma-clerk/app/layout.tsx +7 -0
  54. package/assets/live-templates/next15-prisma-clerk/app/page.tsx +8 -0
  55. package/assets/live-templates/next15-prisma-clerk/next.config.js +6 -0
  56. package/assets/live-templates/next15-prisma-clerk/package.json +22 -0
  57. package/assets/live-templates/next15-prisma-clerk/prisma/schema.prisma +34 -0
  58. package/assets/live-templates/remix-supabase/.env.example +2 -0
  59. package/assets/live-templates/remix-supabase/README.md +9 -0
  60. package/assets/live-templates/remix-supabase/app/root.tsx +19 -0
  61. package/assets/live-templates/remix-supabase/app/routes/_index.tsx +8 -0
  62. package/assets/live-templates/remix-supabase/app/utils/supabase.server.ts +6 -0
  63. package/assets/live-templates/remix-supabase/package.json +20 -0
  64. package/assets/live-templates/remix-supabase/remix.config.js +6 -0
  65. package/assets/live-templates/sveltekit-drizzle/.env.example +1 -0
  66. package/assets/live-templates/sveltekit-drizzle/README.md +9 -0
  67. package/assets/live-templates/sveltekit-drizzle/drizzle/schema.ts +7 -0
  68. package/assets/live-templates/sveltekit-drizzle/drizzle.config.ts +5 -0
  69. package/assets/live-templates/sveltekit-drizzle/package.json +21 -0
  70. package/assets/live-templates/sveltekit-drizzle/src/lib/db.ts +5 -0
  71. package/assets/live-templates/sveltekit-drizzle/src/routes/+page.svelte +2 -0
  72. package/assets/live-templates/sveltekit-drizzle/svelte.config.js +5 -0
  73. package/assets/live-templates/sveltekit-drizzle/vite.config.js +5 -0
  74. package/assets/saas-plan/04-Imp-Template.md +5546 -0
  75. package/assets/templates/CASE-STUDY-TEMPLATE.md +139 -0
  76. package/assets/templates/MASTER-PLAN-TEMPLATE.md +647 -0
  77. package/assets/templates/ORDER-TRACKER-TEMPLATE.md +731 -0
  78. package/assets/templates/PHASE-TRACKER-TEMPLATE.md +577 -0
  79. package/assets/templates/README.md +419 -0
  80. package/bin/ultra-dex.js +1078 -422
  81. package/lib/commands/agents.js +154 -0
  82. package/lib/commands/audit.js +135 -0
  83. package/lib/commands/banner.js +21 -0
  84. package/lib/commands/build.js +214 -0
  85. package/lib/commands/examples.js +34 -0
  86. package/lib/commands/fetch.js +186 -0
  87. package/lib/commands/generate.js +217 -0
  88. package/lib/commands/hooks.js +105 -0
  89. package/lib/commands/init.js +337 -0
  90. package/lib/commands/placeholders.js +11 -0
  91. package/lib/commands/review.js +287 -0
  92. package/lib/commands/serve.js +56 -0
  93. package/lib/commands/suggest.js +126 -0
  94. package/lib/commands/validate.js +140 -0
  95. package/lib/commands/workflows.js +185 -0
  96. package/lib/config/paths.js +9 -0
  97. package/lib/config/urls.js +16 -0
  98. package/lib/providers/base.js +82 -0
  99. package/lib/providers/claude.js +177 -0
  100. package/lib/providers/gemini.js +170 -0
  101. package/lib/providers/index.js +93 -0
  102. package/lib/providers/openai.js +163 -0
  103. package/lib/templates/context.js +26 -0
  104. package/lib/templates/embedded.js +141 -0
  105. package/lib/templates/prompts/generate-plan.js +147 -0
  106. package/lib/templates/prompts/review-code.js +57 -0
  107. package/lib/templates/prompts/section-prompts.js +275 -0
  108. package/lib/templates/prompts/system-prompt.md +58 -0
  109. package/lib/templates/quick-start.js +43 -0
  110. package/lib/utils/build-helpers.js +257 -0
  111. package/lib/utils/fallback.js +36 -0
  112. package/lib/utils/files.js +67 -0
  113. package/lib/utils/network.js +18 -0
  114. package/lib/utils/output.js +20 -0
  115. package/lib/utils/parser.js +155 -0
  116. package/lib/utils/prompt-builder.js +93 -0
  117. package/lib/utils/review-helpers.js +334 -0
  118. package/lib/utils/validation.js +34 -0
  119. package/package.json +19 -5
@@ -0,0 +1,43 @@
1
+ export const QUICK_START_TEMPLATE = `# {{PROJECT_NAME}} - Quick Start
2
+
3
+ ## 1. Your Idea (2 sentences max)
4
+
5
+ **What:** {{IDEA_WHAT}}
6
+ **For whom:** {{IDEA_FOR}}
7
+
8
+ ## 2. The Problem (3 bullets)
9
+
10
+ - {{PROBLEM_1}}
11
+ - {{PROBLEM_2}}
12
+ - {{PROBLEM_3}}
13
+
14
+ ## 3. Core Production Features (5 max)
15
+
16
+ | Feature | Priority | Justification |
17
+ |---------|----------|---------------|
18
+ | {{FEATURE_1}} | P0 | |
19
+ | | P0 | |
20
+ | | P1 | |
21
+ | | P1 | |
22
+ | | P2 | |
23
+
24
+ ## 4. Tech Stack
25
+
26
+ | Layer | Your Choice |
27
+ |-------|-------------|
28
+ | Frontend | {{FRONTEND}} |
29
+ | Database | {{DATABASE}} |
30
+ | Auth | {{AUTH}} |
31
+ | Payments | {{PAYMENTS}} |
32
+ | Hosting | {{HOSTING}} |
33
+
34
+ ## 5. First 3 Tasks
35
+
36
+ 1. [ ] Set up project with chosen stack
37
+ 2. [ ] Implement core feature #1
38
+ 3. [ ] Deploy to staging
39
+
40
+ ---
41
+
42
+ **Next:** Fill out the full implementation plan using the Ultra-Dex template.
43
+ `;
@@ -0,0 +1,257 @@
1
+ /**
2
+ * Build Command Utilities
3
+ * Helpers for the AI-assisted build workflow
4
+ */
5
+
6
+ import fs from 'fs/promises';
7
+ import path from 'path';
8
+
9
+ /**
10
+ * Load implementation plan from current directory
11
+ * @param {string} dir - Directory to search
12
+ * @returns {Promise<{content: string, path: string}|null>}
13
+ */
14
+ export async function loadImplementationPlan(dir = '.') {
15
+ const possibleNames = [
16
+ 'IMPLEMENTATION-PLAN.md',
17
+ 'implementation-plan.md',
18
+ 'PLAN.md',
19
+ 'plan.md',
20
+ ];
21
+
22
+ for (const name of possibleNames) {
23
+ const filePath = path.join(dir, name);
24
+ try {
25
+ const content = await fs.readFile(filePath, 'utf-8');
26
+ return { content, path: filePath };
27
+ } catch {
28
+ continue;
29
+ }
30
+ }
31
+ return null;
32
+ }
33
+
34
+ /**
35
+ * Load context file
36
+ * @param {string} dir - Directory to search
37
+ * @returns {Promise<string|null>}
38
+ */
39
+ export async function loadContext(dir = '.') {
40
+ const possibleNames = ['CONTEXT.md', 'context.md'];
41
+
42
+ for (const name of possibleNames) {
43
+ try {
44
+ return await fs.readFile(path.join(dir, name), 'utf-8');
45
+ } catch {
46
+ continue;
47
+ }
48
+ }
49
+ return null;
50
+ }
51
+
52
+ /**
53
+ * Extract tasks from implementation plan
54
+ * @param {string} planContent - Implementation plan content
55
+ * @returns {Array<{id: string, title: string, section: number, status: string}>}
56
+ */
57
+ export function extractTasks(planContent) {
58
+ const tasks = [];
59
+
60
+ // Match task patterns: - [ ] Task name or - [x] Task name
61
+ const taskRegex = /- \[([ x])\]\s*(.+?)(?=\n|$)/gi;
62
+ let match;
63
+ let currentSection = 0;
64
+
65
+ // Track current section
66
+ const lines = planContent.split('\n');
67
+ let lineIndex = 0;
68
+
69
+ for (const line of lines) {
70
+ const sectionMatch = line.match(/## SECTION (\d+):/i);
71
+ if (sectionMatch) {
72
+ currentSection = parseInt(sectionMatch[1], 10);
73
+ }
74
+
75
+ const taskMatch = line.match(/- \[([ x])\]\s*(.+)/i);
76
+ if (taskMatch) {
77
+ tasks.push({
78
+ id: `task-${tasks.length + 1}`,
79
+ title: taskMatch[2].trim(),
80
+ section: currentSection,
81
+ status: taskMatch[1] === 'x' ? 'complete' : 'pending',
82
+ line: lineIndex,
83
+ });
84
+ }
85
+ lineIndex++;
86
+ }
87
+
88
+ return tasks;
89
+ }
90
+
91
+ /**
92
+ * Extract atomic tasks (4-9 hour tasks) from Section 16
93
+ * @param {string} planContent - Implementation plan content
94
+ * @returns {Array<{id: string, title: string, estimate: string, dependencies: string[]}>}
95
+ */
96
+ export function extractAtomicTasks(planContent) {
97
+ const tasks = [];
98
+
99
+ // Find Section 16 content
100
+ const section16Match = planContent.match(/## SECTION 16:.*?(?=## SECTION 17:|$)/is);
101
+ if (!section16Match) return tasks;
102
+
103
+ const section16 = section16Match[0];
104
+
105
+ // Match task patterns with estimates
106
+ // e.g., "1. Setup project structure (4h)" or "- [ ] Configure database (6-8h)"
107
+ const taskPatterns = [
108
+ /(?:^|\n)\d+\.\s*(.+?)\s*\((\d+(?:-\d+)?h?)\)/gi,
109
+ /- \[[ x]\]\s*(.+?)\s*\((\d+(?:-\d+)?h?)\)/gi,
110
+ /\|\s*(.+?)\s*\|\s*(\d+(?:-\d+)?\s*h(?:ours?)?)\s*\|/gi,
111
+ ];
112
+
113
+ for (const pattern of taskPatterns) {
114
+ let match;
115
+ while ((match = pattern.exec(section16)) !== null) {
116
+ tasks.push({
117
+ id: `atomic-${tasks.length + 1}`,
118
+ title: match[1].trim(),
119
+ estimate: match[2].trim(),
120
+ dependencies: [],
121
+ });
122
+ }
123
+ }
124
+
125
+ return tasks;
126
+ }
127
+
128
+ /**
129
+ * Get pending tasks only
130
+ * @param {Array} tasks - All tasks
131
+ * @returns {Array} Pending tasks
132
+ */
133
+ export function getPendingTasks(tasks) {
134
+ return tasks.filter(t => t.status === 'pending');
135
+ }
136
+
137
+ /**
138
+ * Group tasks by section
139
+ * @param {Array} tasks - All tasks
140
+ * @returns {Map<number, Array>} Tasks grouped by section number
141
+ */
142
+ export function groupTasksBySection(tasks) {
143
+ const grouped = new Map();
144
+
145
+ for (const task of tasks) {
146
+ if (!grouped.has(task.section)) {
147
+ grouped.set(task.section, []);
148
+ }
149
+ grouped.get(task.section).push(task);
150
+ }
151
+
152
+ return grouped;
153
+ }
154
+
155
+ /**
156
+ * Format context for AI agent
157
+ * @param {Object} options
158
+ * @returns {string} Formatted context
159
+ */
160
+ export function formatAgentContext({ plan, context, task, section }) {
161
+ let formatted = `# Project Context\n\n`;
162
+
163
+ if (context) {
164
+ formatted += `## Overview\n\n${context}\n\n`;
165
+ }
166
+
167
+ if (task) {
168
+ formatted += `## Current Task\n\n**${task.title}**\n`;
169
+ if (task.estimate) {
170
+ formatted += `- Estimated time: ${task.estimate}\n`;
171
+ }
172
+ if (task.section) {
173
+ formatted += `- From Section ${task.section}\n`;
174
+ }
175
+ formatted += '\n';
176
+ }
177
+
178
+ if (section && plan) {
179
+ const sectionContent = extractSection(plan, section);
180
+ if (sectionContent) {
181
+ formatted += `## Relevant Section\n\n${sectionContent}\n\n`;
182
+ }
183
+ }
184
+
185
+ formatted += `## Instructions\n\n`;
186
+ formatted += `1. Focus on completing the current task\n`;
187
+ formatted += `2. Follow the implementation plan specifications\n`;
188
+ formatted += `3. Write production-ready code with tests\n`;
189
+ formatted += `4. Document any deviations from the plan\n`;
190
+
191
+ return formatted;
192
+ }
193
+
194
+ /**
195
+ * Extract a specific section from the plan
196
+ * @param {string} planContent - Full plan content
197
+ * @param {number} sectionNum - Section number (1-34)
198
+ * @returns {string|null}
199
+ */
200
+ export function extractSection(planContent, sectionNum) {
201
+ const nextSection = sectionNum < 34 ? `## SECTION ${sectionNum + 1}:` : '═══════════════';
202
+ const regex = new RegExp(`(## SECTION ${sectionNum}:.*?)(?=${nextSection}|$)`, 'is');
203
+ const match = planContent.match(regex);
204
+ return match ? match[1].trim() : null;
205
+ }
206
+
207
+ /**
208
+ * Get section titles
209
+ */
210
+ export const SECTION_TITLES = {
211
+ 1: 'High-Level Summary',
212
+ 2: 'Core Features',
213
+ 3: 'User Stories',
214
+ 4: 'User Personas',
215
+ 5: 'Competitive Analysis',
216
+ 6: 'Screen Map',
217
+ 7: 'Wireframes',
218
+ 8: 'Design System',
219
+ 9: 'UI/UX Specifications',
220
+ 10: 'Data Model',
221
+ 11: 'API Blueprint',
222
+ 12: 'System Architecture',
223
+ 13: 'Authentication & Authorization',
224
+ 14: 'Payment Integration',
225
+ 15: 'Tech Stack',
226
+ 16: 'Implementation Plan',
227
+ 17: 'Timeline',
228
+ 18: 'Risk Assessment',
229
+ 19: 'Deployment Plan',
230
+ 20: 'Testing Strategy',
231
+ 21: 'Security Guidelines',
232
+ 22: 'Performance Requirements',
233
+ 23: 'Monitoring & Logging',
234
+ 24: 'Maintenance Plan',
235
+ 25: 'Documentation',
236
+ 26: 'Analytics',
237
+ 27: 'Error Handling',
238
+ 28: 'Legal & Compliance',
239
+ 29: 'SEO',
240
+ 30: 'Internationalization',
241
+ 31: 'Accessibility',
242
+ 32: 'Feature Flags',
243
+ 33: 'AI/ML Integration',
244
+ 34: 'Future Roadmap',
245
+ };
246
+
247
+ export default {
248
+ loadImplementationPlan,
249
+ loadContext,
250
+ extractTasks,
251
+ extractAtomicTasks,
252
+ getPendingTasks,
253
+ groupTasksBySection,
254
+ formatAgentContext,
255
+ extractSection,
256
+ SECTION_TITLES,
257
+ };
@@ -0,0 +1,36 @@
1
+ import fs from 'fs/promises';
2
+
3
+ export async function readWithFallback(primaryPath, fallbackPath, encoding = 'utf-8') {
4
+ try {
5
+ return await fs.readFile(primaryPath, encoding);
6
+ } catch (primaryError) {
7
+ if (!fallbackPath) {
8
+ throw primaryError;
9
+ }
10
+ return await fs.readFile(fallbackPath, encoding);
11
+ }
12
+ }
13
+
14
+ export async function copyWithFallback(primaryPath, fallbackPath, destinationPath) {
15
+ try {
16
+ await fs.copyFile(primaryPath, destinationPath);
17
+ return 'primary';
18
+ } catch (primaryError) {
19
+ if (!fallbackPath) {
20
+ throw primaryError;
21
+ }
22
+ await fs.copyFile(fallbackPath, destinationPath);
23
+ return 'fallback';
24
+ }
25
+ }
26
+
27
+ export async function listWithFallback(primaryPath, fallbackPath) {
28
+ try {
29
+ return await fs.readdir(primaryPath);
30
+ } catch (primaryError) {
31
+ if (!fallbackPath) {
32
+ throw primaryError;
33
+ }
34
+ return await fs.readdir(fallbackPath);
35
+ }
36
+ }
@@ -0,0 +1,67 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+
4
+ export async function readFileSafe(filePath, label = '') {
5
+ try {
6
+ const content = await fs.readFile(filePath, 'utf-8');
7
+ return { label, content };
8
+ } catch (err) {
9
+ return { label, content: '' };
10
+ }
11
+ }
12
+
13
+ export async function pathExists(targetPath, type = 'file') {
14
+ try {
15
+ const stats = await fs.stat(targetPath);
16
+ if (type === 'file') return stats.isFile();
17
+ if (type === 'dir') return stats.isDirectory();
18
+ return false;
19
+ } catch {
20
+ return false;
21
+ }
22
+ }
23
+
24
+ export async function copyWithFallback({ primary, fallback, destination, onPrimaryMissing }) {
25
+ try {
26
+ await fs.copyFile(primary, destination);
27
+ return 'primary';
28
+ } catch (primaryError) {
29
+ if (onPrimaryMissing) {
30
+ onPrimaryMissing(primaryError);
31
+ }
32
+ if (!fallback) {
33
+ throw primaryError;
34
+ }
35
+ await fs.copyFile(fallback, destination);
36
+ return 'fallback';
37
+ }
38
+ }
39
+
40
+ export async function readWithFallback({ primary, fallback, encoding = 'utf-8' }) {
41
+ try {
42
+ return await fs.readFile(primary, encoding);
43
+ } catch (primaryError) {
44
+ if (!fallback) {
45
+ throw primaryError;
46
+ }
47
+ return await fs.readFile(fallback, encoding);
48
+ }
49
+ }
50
+
51
+ export function resolveAssetPath(basePath, relativePath) {
52
+ return path.join(basePath, relativePath);
53
+ }
54
+
55
+ export async function copyDirectory(srcDir, destDir) {
56
+ await fs.mkdir(destDir, { recursive: true });
57
+ const entries = await fs.readdir(srcDir, { withFileTypes: true });
58
+ for (const entry of entries) {
59
+ const srcPath = path.join(srcDir, entry.name);
60
+ const destPath = path.join(destDir, entry.name);
61
+ if (entry.isDirectory()) {
62
+ await copyDirectory(srcPath, destPath);
63
+ } else if (entry.isFile()) {
64
+ await fs.copyFile(srcPath, destPath);
65
+ }
66
+ }
67
+ }
@@ -0,0 +1,18 @@
1
+ export async function fetchWithRetry(url, options = {}, retries = 2, delayMs = 400) {
2
+ let lastError;
3
+ for (let attempt = 0; attempt <= retries; attempt++) {
4
+ try {
5
+ const response = await fetch(url, options);
6
+ if (!response.ok) {
7
+ throw new Error(`HTTP ${response.status}`);
8
+ }
9
+ return response;
10
+ } catch (err) {
11
+ lastError = err;
12
+ if (attempt < retries) {
13
+ await new Promise(resolve => setTimeout(resolve, delayMs));
14
+ }
15
+ }
16
+ }
17
+ throw lastError;
18
+ }
@@ -0,0 +1,20 @@
1
+ import chalk from 'chalk';
2
+
3
+ export function printError(message, err) {
4
+ console.log(chalk.red(message));
5
+ if (err?.message) {
6
+ console.log(chalk.gray(` → ${err.message}`));
7
+ }
8
+ }
9
+
10
+ export function printWarning(message) {
11
+ console.log(chalk.yellow(message));
12
+ }
13
+
14
+ export function printInfo(message) {
15
+ console.log(chalk.cyan(message));
16
+ }
17
+
18
+ export function printSuccess(message) {
19
+ console.log(chalk.green(message));
20
+ }
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Parser Utility
3
+ * Parses AI responses into structured output
4
+ */
5
+
6
+ /**
7
+ * Extract project name from implementation plan
8
+ * @param {string} content - Generated implementation plan
9
+ * @returns {string} Project name
10
+ */
11
+ export function extractProjectName(content) {
12
+ // Try to find PROJECT: line
13
+ const projectMatch = content.match(/PROJECT:\s*(.+)/i);
14
+ if (projectMatch) {
15
+ return projectMatch[1].trim();
16
+ }
17
+
18
+ // Try to find product name in Section 1
19
+ const productMatch = content.match(/Product Vision.*?:\s*(.+?)(?:\.|$)/im);
20
+ if (productMatch) {
21
+ return productMatch[1].trim().slice(0, 50);
22
+ }
23
+
24
+ return 'My SaaS Project';
25
+ }
26
+
27
+ /**
28
+ * Extract summary from implementation plan
29
+ * @param {string} content - Generated implementation plan
30
+ * @returns {string} Brief summary
31
+ */
32
+ export function extractSummary(content) {
33
+ // Find Section 1.1 or Problem Statement
34
+ const visionMatch = content.match(/### 1\.1 Product Vision.*?\n(.+?)(?=\n###|\n##|$)/is);
35
+ if (visionMatch) {
36
+ return visionMatch[1].trim().slice(0, 200);
37
+ }
38
+
39
+ const problemMatch = content.match(/### 1\.2 Problem Statement.*?\n(.+?)(?=\n###|\n##|$)/is);
40
+ if (problemMatch) {
41
+ return problemMatch[1].trim().slice(0, 200);
42
+ }
43
+
44
+ return 'A SaaS application';
45
+ }
46
+
47
+ /**
48
+ * Extract tech stack from implementation plan
49
+ * @param {string} content - Generated implementation plan
50
+ * @returns {Object} Tech stack details
51
+ */
52
+ export function extractTechStack(content) {
53
+ const defaults = {
54
+ frontend: 'Next.js + TypeScript',
55
+ backend: 'Next.js API Routes',
56
+ database: 'PostgreSQL + Prisma',
57
+ auth: 'NextAuth.js',
58
+ payments: 'Stripe',
59
+ hosting: 'Vercel',
60
+ };
61
+
62
+ // Try to find tech stack section
63
+ const stackSection = content.match(/## SECTION 15.*?(?=## SECTION 16|$)/is);
64
+ if (!stackSection) return defaults;
65
+
66
+ const text = stackSection[0];
67
+
68
+ // Extract each layer
69
+ const frontendMatch = text.match(/Frontend.*?:\s*(.+?)(?=\n|$)/i);
70
+ const backendMatch = text.match(/Backend.*?:\s*(.+?)(?=\n|$)/i);
71
+ const databaseMatch = text.match(/Database.*?:\s*(.+?)(?=\n|$)/i);
72
+
73
+ return {
74
+ frontend: frontendMatch?.[1]?.trim() || defaults.frontend,
75
+ backend: backendMatch?.[1]?.trim() || defaults.backend,
76
+ database: databaseMatch?.[1]?.trim() || defaults.database,
77
+ auth: defaults.auth,
78
+ payments: defaults.payments,
79
+ hosting: defaults.hosting,
80
+ };
81
+ }
82
+
83
+ /**
84
+ * Validate implementation plan completeness
85
+ * @param {string} content - Generated implementation plan
86
+ * @returns {{complete: boolean, missingSections: number[], percentage: number}}
87
+ */
88
+ export function validateCompleteness(content) {
89
+ const missingSections = [];
90
+
91
+ for (let i = 1; i <= 34; i++) {
92
+ const regex = new RegExp(`## SECTION ${i}:`, 'i');
93
+ if (!regex.test(content)) {
94
+ missingSections.push(i);
95
+ }
96
+ }
97
+
98
+ const percentage = Math.round(((34 - missingSections.length) / 34) * 100);
99
+
100
+ return {
101
+ complete: missingSections.length === 0,
102
+ missingSections,
103
+ percentage,
104
+ };
105
+ }
106
+
107
+ /**
108
+ * Split content into sections
109
+ * @param {string} content - Generated implementation plan
110
+ * @returns {Map<number, string>} Map of section number to content
111
+ */
112
+ export function splitIntoSections(content) {
113
+ const sections = new Map();
114
+
115
+ for (let i = 1; i <= 34; i++) {
116
+ const nextSection = i < 34 ? `## SECTION ${i + 1}:` : '═══════════════';
117
+ const regex = new RegExp(`(## SECTION ${i}:.*?)(?=${nextSection}|$)`, 'is');
118
+ const match = content.match(regex);
119
+
120
+ if (match) {
121
+ sections.set(i, match[1].trim());
122
+ }
123
+ }
124
+
125
+ return sections;
126
+ }
127
+
128
+ /**
129
+ * Format token usage for display
130
+ * @param {{inputTokens: number, outputTokens: number}} usage
131
+ * @returns {string}
132
+ */
133
+ export function formatUsage(usage) {
134
+ const total = usage.inputTokens + usage.outputTokens;
135
+ return `${total.toLocaleString()} tokens (${usage.inputTokens.toLocaleString()} in / ${usage.outputTokens.toLocaleString()} out)`;
136
+ }
137
+
138
+ /**
139
+ * Format cost for display
140
+ * @param {{input: number, output: number, total: number}} cost
141
+ * @returns {string}
142
+ */
143
+ export function formatCost(cost) {
144
+ return `$${cost.total.toFixed(4)} (input: $${cost.input.toFixed(4)}, output: $${cost.output.toFixed(4)})`;
145
+ }
146
+
147
+ export default {
148
+ extractProjectName,
149
+ extractSummary,
150
+ extractTechStack,
151
+ validateCompleteness,
152
+ splitIntoSections,
153
+ formatUsage,
154
+ formatCost,
155
+ };
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Prompt Builder Utility
3
+ * Assembles prompts for the AI providers
4
+ */
5
+
6
+ import fs from 'fs/promises';
7
+ import path from 'path';
8
+ import { fileURLToPath } from 'url';
9
+ import { USER_PROMPT_TEMPLATE, QUICK_START_PROMPT, CONTEXT_PROMPT } from '../templates/prompts/section-prompts.js';
10
+
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = path.dirname(__filename);
13
+
14
+ /**
15
+ * Load the system prompt from file
16
+ * @returns {Promise<string>}
17
+ */
18
+ export async function loadSystemPrompt() {
19
+ const promptPath = path.join(__dirname, '../templates/prompts/system-prompt.md');
20
+ try {
21
+ return await fs.readFile(promptPath, 'utf-8');
22
+ } catch {
23
+ // Fallback embedded prompt if file not found
24
+ return `You are an expert SaaS architect. Generate a comprehensive 34-section implementation plan.
25
+ Be specific, production-ready, and thorough. Include code examples, time estimates, and realistic constraints.`;
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Build the user prompt for implementation plan generation
31
+ * @param {string} idea - The user's SaaS idea
32
+ * @returns {string}
33
+ */
34
+ export function buildImplementationPrompt(idea) {
35
+ return USER_PROMPT_TEMPLATE.replace(/\{\{IDEA\}\}/g, idea);
36
+ }
37
+
38
+ /**
39
+ * Build the prompt for QUICK-START.md generation
40
+ * @returns {string}
41
+ */
42
+ export function buildQuickStartPrompt() {
43
+ return QUICK_START_PROMPT;
44
+ }
45
+
46
+ /**
47
+ * Build the prompt for CONTEXT.md generation
48
+ * @returns {string}
49
+ */
50
+ export function buildContextPrompt() {
51
+ return CONTEXT_PROMPT;
52
+ }
53
+
54
+ /**
55
+ * Estimate token count for a string (rough approximation)
56
+ * @param {string} text - Text to estimate
57
+ * @returns {number} Estimated token count
58
+ */
59
+ export function estimateTokens(text) {
60
+ // Rough approximation: ~4 characters per token
61
+ return Math.ceil(text.length / 4);
62
+ }
63
+
64
+ /**
65
+ * Calculate estimated cost for generation
66
+ * @param {Object} provider - AI provider instance
67
+ * @param {string} idea - User's idea
68
+ * @returns {{inputTokens: number, outputTokens: number, cost: Object}}
69
+ */
70
+ export function estimateGenerationCost(provider, idea) {
71
+ // Estimate input: system prompt (~600 tokens) + user prompt (~2000 tokens) + idea
72
+ const inputTokens = 2600 + estimateTokens(idea);
73
+
74
+ // Estimate output: ~40,000 tokens for full 34-section plan
75
+ const outputTokens = 40000;
76
+
77
+ const cost = provider.estimateCost(inputTokens, outputTokens);
78
+
79
+ return {
80
+ inputTokens,
81
+ outputTokens,
82
+ cost,
83
+ };
84
+ }
85
+
86
+ export default {
87
+ loadSystemPrompt,
88
+ buildImplementationPrompt,
89
+ buildQuickStartPrompt,
90
+ buildContextPrompt,
91
+ estimateTokens,
92
+ estimateGenerationCost,
93
+ };