ultra-dex 1.8.0 → 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 (41) hide show
  1. package/README.md +162 -143
  2. package/bin/ultra-dex.js +1005 -777
  3. package/lib/commands/agents.js +154 -0
  4. package/lib/commands/audit.js +135 -0
  5. package/lib/commands/banner.js +21 -0
  6. package/lib/commands/build.js +214 -0
  7. package/lib/commands/examples.js +34 -0
  8. package/lib/commands/fetch.js +186 -0
  9. package/lib/commands/generate.js +217 -0
  10. package/lib/commands/hooks.js +105 -0
  11. package/lib/commands/init.js +337 -0
  12. package/lib/commands/placeholders.js +11 -0
  13. package/lib/commands/review.js +287 -0
  14. package/lib/commands/serve.js +56 -0
  15. package/lib/commands/suggest.js +126 -0
  16. package/lib/commands/validate.js +140 -0
  17. package/lib/commands/workflows.js +185 -0
  18. package/lib/config/paths.js +9 -0
  19. package/lib/config/urls.js +16 -0
  20. package/lib/providers/base.js +82 -0
  21. package/lib/providers/claude.js +177 -0
  22. package/lib/providers/gemini.js +170 -0
  23. package/lib/providers/index.js +93 -0
  24. package/lib/providers/openai.js +163 -0
  25. package/lib/templates/context.js +26 -0
  26. package/lib/templates/embedded.js +141 -0
  27. package/lib/templates/prompts/generate-plan.js +147 -0
  28. package/lib/templates/prompts/review-code.js +57 -0
  29. package/lib/templates/prompts/section-prompts.js +275 -0
  30. package/lib/templates/prompts/system-prompt.md +58 -0
  31. package/lib/templates/quick-start.js +43 -0
  32. package/lib/utils/build-helpers.js +257 -0
  33. package/lib/utils/fallback.js +36 -0
  34. package/lib/utils/files.js +67 -0
  35. package/lib/utils/network.js +18 -0
  36. package/lib/utils/output.js +20 -0
  37. package/lib/utils/parser.js +155 -0
  38. package/lib/utils/prompt-builder.js +93 -0
  39. package/lib/utils/review-helpers.js +334 -0
  40. package/lib/utils/validation.js +34 -0
  41. package/package.json +17 -3
@@ -0,0 +1,334 @@
1
+ /**
2
+ * Review Command Utilities
3
+ * Helpers for code-to-plan alignment checking
4
+ */
5
+
6
+ import fs from 'fs/promises';
7
+ import path from 'path';
8
+ import { extractSection, SECTION_TITLES } from './build-helpers.js';
9
+
10
+ /**
11
+ * Scan directory for code files
12
+ * @param {string} dir - Directory to scan
13
+ * @param {string[]} extensions - File extensions to include
14
+ * @returns {Promise<string[]>} List of file paths
15
+ */
16
+ export async function scanCodeFiles(dir, extensions = ['.ts', '.tsx', '.js', '.jsx', '.py', '.go']) {
17
+ const files = [];
18
+
19
+ async function scan(currentDir) {
20
+ try {
21
+ const entries = await fs.readdir(currentDir, { withFileTypes: true });
22
+
23
+ for (const entry of entries) {
24
+ const fullPath = path.join(currentDir, entry.name);
25
+
26
+ // Skip common non-code directories
27
+ if (entry.isDirectory()) {
28
+ if (!['node_modules', '.git', '.next', 'dist', 'build', '__pycache__', '.venv'].includes(entry.name)) {
29
+ await scan(fullPath);
30
+ }
31
+ } else if (entry.isFile()) {
32
+ const ext = path.extname(entry.name).toLowerCase();
33
+ if (extensions.includes(ext)) {
34
+ files.push(fullPath);
35
+ }
36
+ }
37
+ }
38
+ } catch {
39
+ // Directory not accessible
40
+ }
41
+ }
42
+
43
+ await scan(dir);
44
+ return files;
45
+ }
46
+
47
+ /**
48
+ * Extract code structure from files
49
+ * @param {string[]} files - List of file paths
50
+ * @returns {Promise<Object>} Code structure analysis
51
+ */
52
+ export async function analyzeCodeStructure(files) {
53
+ const structure = {
54
+ components: [],
55
+ apiRoutes: [],
56
+ models: [],
57
+ services: [],
58
+ tests: [],
59
+ configs: [],
60
+ };
61
+
62
+ for (const file of files) {
63
+ const relativePath = file;
64
+ const filename = path.basename(file);
65
+ const dir = path.dirname(file);
66
+
67
+ // Categorize files
68
+ if (relativePath.includes('/components/') || relativePath.includes('/Components/')) {
69
+ structure.components.push(relativePath);
70
+ } else if (relativePath.includes('/api/') || relativePath.includes('/routes/')) {
71
+ structure.apiRoutes.push(relativePath);
72
+ } else if (relativePath.includes('/models/') || relativePath.includes('/schema/') || filename.includes('schema')) {
73
+ structure.models.push(relativePath);
74
+ } else if (relativePath.includes('/services/') || relativePath.includes('/lib/')) {
75
+ structure.services.push(relativePath);
76
+ } else if (relativePath.includes('/test') || relativePath.includes('.test.') || relativePath.includes('.spec.')) {
77
+ structure.tests.push(relativePath);
78
+ } else if (filename.includes('config') || filename.includes('.env')) {
79
+ structure.configs.push(relativePath);
80
+ }
81
+ }
82
+
83
+ return structure;
84
+ }
85
+
86
+ /**
87
+ * Check alignment between code and plan section
88
+ * @param {string} planContent - Full plan content
89
+ * @param {Object} codeStructure - Analyzed code structure
90
+ * @param {number} sectionNum - Section to check
91
+ * @returns {Object} Alignment result
92
+ */
93
+ export function checkSectionAlignment(planContent, codeStructure, sectionNum) {
94
+ const section = extractSection(planContent, sectionNum);
95
+ if (!section) {
96
+ return { section: sectionNum, score: 0, status: 'missing', issues: ['Section not found in plan'] };
97
+ }
98
+
99
+ const result = {
100
+ section: sectionNum,
101
+ title: SECTION_TITLES[sectionNum] || `Section ${sectionNum}`,
102
+ score: 0,
103
+ status: 'unknown',
104
+ issues: [],
105
+ suggestions: [],
106
+ };
107
+
108
+ // Section-specific checks
109
+ switch (sectionNum) {
110
+ case 10: // Data Model
111
+ result.score = checkDataModelAlignment(section, codeStructure);
112
+ break;
113
+ case 11: // API Blueprint
114
+ result.score = checkApiAlignment(section, codeStructure);
115
+ break;
116
+ case 13: // Authentication
117
+ result.score = checkAuthAlignment(section, codeStructure);
118
+ break;
119
+ case 20: // Testing Strategy
120
+ result.score = checkTestingAlignment(section, codeStructure);
121
+ break;
122
+ default:
123
+ result.score = 50; // Default partial score for unchecked sections
124
+ result.status = 'not-verified';
125
+ result.issues.push('Automated verification not available for this section');
126
+ }
127
+
128
+ // Set status based on score
129
+ if (result.score >= 90) {
130
+ result.status = 'complete';
131
+ } else if (result.score >= 70) {
132
+ result.status = 'partial';
133
+ } else if (result.score >= 30) {
134
+ result.status = 'incomplete';
135
+ } else {
136
+ result.status = 'missing';
137
+ }
138
+
139
+ return result;
140
+ }
141
+
142
+ /**
143
+ * Check data model alignment (Section 10)
144
+ */
145
+ function checkDataModelAlignment(section, codeStructure) {
146
+ let score = 0;
147
+
148
+ // Check if schema/model files exist
149
+ if (codeStructure.models.length > 0) {
150
+ score += 50;
151
+ }
152
+
153
+ // Check for Prisma schema
154
+ const hasPrisma = codeStructure.models.some(f => f.includes('prisma') || f.includes('schema.prisma'));
155
+ if (hasPrisma) {
156
+ score += 30;
157
+ }
158
+
159
+ // Check for migrations
160
+ const hasMigrations = codeStructure.models.some(f => f.includes('migration'));
161
+ if (hasMigrations) {
162
+ score += 20;
163
+ }
164
+
165
+ return Math.min(score, 100);
166
+ }
167
+
168
+ /**
169
+ * Check API alignment (Section 11)
170
+ */
171
+ function checkApiAlignment(section, codeStructure) {
172
+ let score = 0;
173
+
174
+ // Check if API routes exist
175
+ if (codeStructure.apiRoutes.length > 0) {
176
+ score += 40;
177
+ }
178
+
179
+ // Count endpoints mentioned in plan vs implemented
180
+ const planEndpoints = (section.match(/(?:GET|POST|PUT|DELETE|PATCH)\s+\/[^\s]+/gi) || []).length;
181
+ const implementedRoutes = codeStructure.apiRoutes.length;
182
+
183
+ if (planEndpoints > 0) {
184
+ const coverage = Math.min(implementedRoutes / planEndpoints, 1);
185
+ score += Math.round(coverage * 60);
186
+ } else {
187
+ score += 30; // No specific endpoints in plan
188
+ }
189
+
190
+ return Math.min(score, 100);
191
+ }
192
+
193
+ /**
194
+ * Check authentication alignment (Section 13)
195
+ */
196
+ function checkAuthAlignment(section, codeStructure) {
197
+ let score = 0;
198
+
199
+ // Check for auth-related files
200
+ const authFiles = [...codeStructure.apiRoutes, ...codeStructure.services].filter(f =>
201
+ f.toLowerCase().includes('auth') ||
202
+ f.toLowerCase().includes('login') ||
203
+ f.toLowerCase().includes('session')
204
+ );
205
+
206
+ if (authFiles.length > 0) {
207
+ score += 50;
208
+ }
209
+
210
+ // Check for NextAuth config
211
+ const hasNextAuth = codeStructure.configs.some(f => f.includes('auth'));
212
+ if (hasNextAuth) {
213
+ score += 30;
214
+ }
215
+
216
+ // Check for middleware
217
+ const hasMiddleware = codeStructure.services.some(f => f.includes('middleware'));
218
+ if (hasMiddleware) {
219
+ score += 20;
220
+ }
221
+
222
+ return Math.min(score, 100);
223
+ }
224
+
225
+ /**
226
+ * Check testing alignment (Section 20)
227
+ */
228
+ function checkTestingAlignment(section, codeStructure) {
229
+ let score = 0;
230
+
231
+ // Check if test files exist
232
+ if (codeStructure.tests.length > 0) {
233
+ score += 40;
234
+ }
235
+
236
+ // Calculate test coverage ratio (tests vs code files)
237
+ const codeFiles = codeStructure.components.length + codeStructure.apiRoutes.length + codeStructure.services.length;
238
+ if (codeFiles > 0) {
239
+ const testRatio = codeStructure.tests.length / codeFiles;
240
+ score += Math.min(Math.round(testRatio * 100), 60);
241
+ }
242
+
243
+ return Math.min(score, 100);
244
+ }
245
+
246
+ /**
247
+ * Generate alignment report
248
+ * @param {Object[]} results - Section alignment results
249
+ * @returns {Object} Full report
250
+ */
251
+ export function generateAlignmentReport(results) {
252
+ const totalScore = results.reduce((sum, r) => sum + r.score, 0);
253
+ const avgScore = Math.round(totalScore / results.length);
254
+
255
+ const complete = results.filter(r => r.status === 'complete').length;
256
+ const partial = results.filter(r => r.status === 'partial').length;
257
+ const incomplete = results.filter(r => r.status === 'incomplete').length;
258
+ const missing = results.filter(r => r.status === 'missing').length;
259
+
260
+ return {
261
+ overallScore: avgScore,
262
+ grade: getGrade(avgScore),
263
+ summary: {
264
+ complete,
265
+ partial,
266
+ incomplete,
267
+ missing,
268
+ total: results.length,
269
+ },
270
+ sections: results,
271
+ topIssues: results
272
+ .filter(r => r.score < 70)
273
+ .sort((a, b) => a.score - b.score)
274
+ .slice(0, 5)
275
+ .map(r => ({
276
+ section: r.section,
277
+ title: r.title,
278
+ score: r.score,
279
+ issues: r.issues,
280
+ })),
281
+ };
282
+ }
283
+
284
+ /**
285
+ * Get letter grade from score
286
+ */
287
+ function getGrade(score) {
288
+ if (score >= 90) return 'A';
289
+ if (score >= 80) return 'B';
290
+ if (score >= 70) return 'C';
291
+ if (score >= 60) return 'D';
292
+ return 'F';
293
+ }
294
+
295
+ /**
296
+ * Format report for CLI output
297
+ * @param {Object} report - Alignment report
298
+ * @returns {string} Formatted output
299
+ */
300
+ export function formatReportForCLI(report) {
301
+ let output = '';
302
+
303
+ output += `\n╔════════════════════════════════════════╗\n`;
304
+ output += `║ Ultra-Dex Alignment Report ║\n`;
305
+ output += `╚════════════════════════════════════════╝\n\n`;
306
+
307
+ output += `Overall Score: ${report.overallScore}% (Grade: ${report.grade})\n\n`;
308
+
309
+ output += `Summary:\n`;
310
+ output += ` ✅ Complete: ${report.summary.complete} sections\n`;
311
+ output += ` ⚠️ Partial: ${report.summary.partial} sections\n`;
312
+ output += ` ❌ Incomplete: ${report.summary.incomplete} sections\n`;
313
+ output += ` ⬜ Missing: ${report.summary.missing} sections\n\n`;
314
+
315
+ if (report.topIssues.length > 0) {
316
+ output += `Top Issues:\n`;
317
+ for (const issue of report.topIssues) {
318
+ output += ` • Section ${issue.section} (${issue.title}): ${issue.score}%\n`;
319
+ for (const i of issue.issues) {
320
+ output += ` - ${i}\n`;
321
+ }
322
+ }
323
+ }
324
+
325
+ return output;
326
+ }
327
+
328
+ export default {
329
+ scanCodeFiles,
330
+ analyzeCodeStructure,
331
+ checkSectionAlignment,
332
+ generateAlignmentReport,
333
+ formatReportForCLI,
334
+ };
@@ -0,0 +1,34 @@
1
+ const PROJECT_NAME_REGEX = /^[a-z0-9-]+$/i;
2
+
3
+ export function validateProjectName(name) {
4
+ if (!name || !name.trim()) {
5
+ return 'Project name is required';
6
+ }
7
+ const trimmed = name.trim();
8
+ if (!PROJECT_NAME_REGEX.test(trimmed)) {
9
+ return 'Project name must use only letters, numbers, and dashes';
10
+ }
11
+ if (trimmed.includes('..') || trimmed.includes('/') || trimmed.includes('\\')) {
12
+ return 'Project name cannot include path separators';
13
+ }
14
+ return true;
15
+ }
16
+
17
+ export function validateSafePath(input, label = 'Path') {
18
+ if (!input || !input.trim()) {
19
+ return `${label} is required`;
20
+ }
21
+ const trimmed = input.trim();
22
+ if (trimmed.includes('..')) {
23
+ return `${label} cannot include ".."`;
24
+ }
25
+ return true;
26
+ }
27
+
28
+ export function assertValidPath(input, label) {
29
+ const result = validateSafePath(input, label);
30
+ if (result !== true) {
31
+ throw new Error(result);
32
+ }
33
+ return input;
34
+ }
package/package.json CHANGED
@@ -1,14 +1,20 @@
1
1
  {
2
2
  "name": "ultra-dex",
3
- "version": "1.8.0",
4
- "description": "CLI for Ultra-Dex SaaS Implementation Framework with 16 Production Agents",
3
+ "version": "2.2.0",
4
+ "description": "CLI for Ultra-Dex SaaS Implementation Framework with AI-Powered Plan Generation, Build Mode, and Code Review",
5
5
  "keywords": [
6
6
  "saas",
7
7
  "template",
8
8
  "implementation",
9
9
  "planning",
10
10
  "startup",
11
- "framework"
11
+ "framework",
12
+ "ai",
13
+ "claude",
14
+ "openai",
15
+ "gemini",
16
+ "orchestration",
17
+ "mcp"
12
18
  ],
13
19
  "author": "Srujan Sai Karna",
14
20
  "license": "MIT",
@@ -22,8 +28,10 @@
22
28
  },
23
29
  "files": [
24
30
  "bin",
31
+ "lib",
25
32
  "assets"
26
33
  ],
34
+ "type": "module",
27
35
  "engines": {
28
36
  "node": ">=18"
29
37
  },
@@ -32,5 +40,11 @@
32
40
  "commander": "^11.1.0",
33
41
  "inquirer": "^9.2.12",
34
42
  "ora": "^8.0.1"
43
+ },
44
+ "optionalDependencies": {
45
+ "@anthropic-ai/sdk": "^0.30.0",
46
+ "openai": "^4.70.0",
47
+ "@google/generative-ai": "^0.21.0",
48
+ "dotenv": "^16.4.5"
35
49
  }
36
50
  }