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.
- package/README.md +160 -127
- package/assets/agents/0-orchestration/orchestrator.md +225 -0
- package/assets/agents/00-AGENT_INDEX.md +138 -0
- package/assets/agents/1-leadership/cto.md +186 -0
- package/assets/agents/1-leadership/planner.md +205 -0
- package/assets/agents/1-leadership/research.md +285 -0
- package/assets/agents/2-development/backend.md +472 -0
- package/assets/agents/2-development/database.md +516 -0
- package/assets/agents/2-development/frontend.md +144 -0
- package/assets/agents/3-security/auth.md +168 -0
- package/assets/agents/3-security/security.md +335 -0
- package/assets/agents/4-devops/devops.md +587 -0
- package/assets/agents/5-quality/debugger.md +188 -0
- package/assets/agents/5-quality/documentation.md +167 -0
- package/assets/agents/5-quality/reviewer.md +213 -0
- package/assets/agents/5-quality/testing.md +280 -0
- package/assets/agents/6-specialist/performance.md +323 -0
- package/assets/agents/6-specialist/refactoring.md +343 -0
- package/assets/agents/AGENT-INSTRUCTIONS.md +315 -0
- package/assets/agents/README.md +232 -0
- package/assets/cursor-rules/00-ultra-dex-core.mdc +48 -0
- package/assets/cursor-rules/01-database.mdc +50 -0
- package/assets/cursor-rules/02-api.mdc +81 -0
- package/assets/cursor-rules/03-auth.mdc +70 -0
- package/assets/cursor-rules/04-frontend.mdc +92 -0
- package/assets/cursor-rules/05-payments.mdc +88 -0
- package/assets/cursor-rules/06-testing.mdc +104 -0
- package/assets/cursor-rules/07-security.mdc +94 -0
- package/assets/cursor-rules/08-deployment.mdc +92 -0
- package/assets/cursor-rules/09-error-handling.mdc +137 -0
- package/assets/cursor-rules/10-performance.mdc +123 -0
- package/assets/cursor-rules/11-nextjs-v15.mdc +307 -0
- package/assets/cursor-rules/12-multi-tenancy.mdc +282 -0
- package/assets/cursor-rules/README.md +78 -0
- package/assets/cursor-rules/load.ps1 +108 -0
- package/assets/cursor-rules/load.sh +102 -0
- package/assets/docs/BUILD-AUTH-30M.md +113 -0
- package/assets/docs/CHECKLIST-21-STEP.md +86 -0
- package/assets/docs/CODEMAP.md +229 -0
- package/assets/docs/CUSTOMIZATION.md +127 -0
- package/assets/docs/LAUNCH-POSTS.md +238 -0
- package/assets/docs/QUICK-REFERENCE.md +338 -0
- package/assets/docs/README.md +21 -0
- package/assets/docs/ROADMAP.md +480 -0
- package/assets/docs/TROUBLESHOOTING.md +148 -0
- package/assets/docs/TUTORIAL.md +182 -0
- package/assets/docs/VERIFICATION.md +108 -0
- package/assets/docs/VISION-V2.md +187 -0
- package/assets/docs/WORKFLOW-DIAGRAMS.md +463 -0
- package/assets/docs/index.html +550 -0
- package/assets/live-templates/next15-prisma-clerk/.env.example +3 -0
- package/assets/live-templates/next15-prisma-clerk/README.md +10 -0
- package/assets/live-templates/next15-prisma-clerk/app/layout.tsx +7 -0
- package/assets/live-templates/next15-prisma-clerk/app/page.tsx +8 -0
- package/assets/live-templates/next15-prisma-clerk/next.config.js +6 -0
- package/assets/live-templates/next15-prisma-clerk/package.json +22 -0
- package/assets/live-templates/next15-prisma-clerk/prisma/schema.prisma +34 -0
- package/assets/live-templates/remix-supabase/.env.example +2 -0
- package/assets/live-templates/remix-supabase/README.md +9 -0
- package/assets/live-templates/remix-supabase/app/root.tsx +19 -0
- package/assets/live-templates/remix-supabase/app/routes/_index.tsx +8 -0
- package/assets/live-templates/remix-supabase/app/utils/supabase.server.ts +6 -0
- package/assets/live-templates/remix-supabase/package.json +20 -0
- package/assets/live-templates/remix-supabase/remix.config.js +6 -0
- package/assets/live-templates/sveltekit-drizzle/.env.example +1 -0
- package/assets/live-templates/sveltekit-drizzle/README.md +9 -0
- package/assets/live-templates/sveltekit-drizzle/drizzle/schema.ts +7 -0
- package/assets/live-templates/sveltekit-drizzle/drizzle.config.ts +5 -0
- package/assets/live-templates/sveltekit-drizzle/package.json +21 -0
- package/assets/live-templates/sveltekit-drizzle/src/lib/db.ts +5 -0
- package/assets/live-templates/sveltekit-drizzle/src/routes/+page.svelte +2 -0
- package/assets/live-templates/sveltekit-drizzle/svelte.config.js +5 -0
- package/assets/live-templates/sveltekit-drizzle/vite.config.js +5 -0
- package/assets/saas-plan/04-Imp-Template.md +5546 -0
- package/assets/templates/CASE-STUDY-TEMPLATE.md +139 -0
- package/assets/templates/MASTER-PLAN-TEMPLATE.md +647 -0
- package/assets/templates/ORDER-TRACKER-TEMPLATE.md +731 -0
- package/assets/templates/PHASE-TRACKER-TEMPLATE.md +577 -0
- package/assets/templates/README.md +419 -0
- package/bin/ultra-dex.js +1078 -422
- package/lib/commands/agents.js +154 -0
- package/lib/commands/audit.js +135 -0
- package/lib/commands/banner.js +21 -0
- package/lib/commands/build.js +214 -0
- package/lib/commands/examples.js +34 -0
- package/lib/commands/fetch.js +186 -0
- package/lib/commands/generate.js +217 -0
- package/lib/commands/hooks.js +105 -0
- package/lib/commands/init.js +337 -0
- package/lib/commands/placeholders.js +11 -0
- package/lib/commands/review.js +287 -0
- package/lib/commands/serve.js +56 -0
- package/lib/commands/suggest.js +126 -0
- package/lib/commands/validate.js +140 -0
- package/lib/commands/workflows.js +185 -0
- package/lib/config/paths.js +9 -0
- package/lib/config/urls.js +16 -0
- package/lib/providers/base.js +82 -0
- package/lib/providers/claude.js +177 -0
- package/lib/providers/gemini.js +170 -0
- package/lib/providers/index.js +93 -0
- package/lib/providers/openai.js +163 -0
- package/lib/templates/context.js +26 -0
- package/lib/templates/embedded.js +141 -0
- package/lib/templates/prompts/generate-plan.js +147 -0
- package/lib/templates/prompts/review-code.js +57 -0
- package/lib/templates/prompts/section-prompts.js +275 -0
- package/lib/templates/prompts/system-prompt.md +58 -0
- package/lib/templates/quick-start.js +43 -0
- package/lib/utils/build-helpers.js +257 -0
- package/lib/utils/fallback.js +36 -0
- package/lib/utils/files.js +67 -0
- package/lib/utils/network.js +18 -0
- package/lib/utils/output.js +20 -0
- package/lib/utils/parser.js +155 -0
- package/lib/utils/prompt-builder.js +93 -0
- package/lib/utils/review-helpers.js +334 -0
- package/lib/utils/validation.js +34 -0
- package/package.json +19 -5
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import fs from 'fs/promises';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
|
|
7
|
+
import { QUICK_START_TEMPLATE } from '../templates/quick-start.js';
|
|
8
|
+
import { CONTEXT_TEMPLATE } from '../templates/context.js';
|
|
9
|
+
import { validateProjectName, validateSafePath } from '../utils/validation.js';
|
|
10
|
+
import { ASSETS_ROOT, ROOT_FALLBACK } from '../config/paths.js';
|
|
11
|
+
import { githubBlobUrl, githubWebUrl } from '../config/urls.js';
|
|
12
|
+
import { copyWithFallback, listWithFallback, readWithFallback } from '../utils/fallback.js';
|
|
13
|
+
|
|
14
|
+
export function registerInitCommand(program) {
|
|
15
|
+
program
|
|
16
|
+
.command('init')
|
|
17
|
+
.description('Initialize a new Ultra-Dex project')
|
|
18
|
+
.option('-n, --name <name>', 'Project name')
|
|
19
|
+
.option('-d, --dir <directory>', 'Output directory', '.')
|
|
20
|
+
.option('--preview', 'Preview files without creating them')
|
|
21
|
+
.action(async (options) => {
|
|
22
|
+
console.log(chalk.cyan(program.banner));
|
|
23
|
+
console.log(chalk.bold('\nWelcome to Ultra-Dex! Let\'s plan your SaaS.\n'));
|
|
24
|
+
|
|
25
|
+
if (options.preview) {
|
|
26
|
+
console.log('\n📋 Files that would be created:\n');
|
|
27
|
+
console.log(' QUICK-START.md');
|
|
28
|
+
console.log(' CONTEXT.md');
|
|
29
|
+
console.log(' IMPLEMENTATION-PLAN.md');
|
|
30
|
+
console.log(' docs/CHECKLIST.md');
|
|
31
|
+
console.log(' docs/AI-PROMPTS.md');
|
|
32
|
+
console.log('\nRun without --preview to create files.');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const dirValidation = validateSafePath(options.dir, 'Output directory');
|
|
37
|
+
if (dirValidation !== true) {
|
|
38
|
+
console.log(chalk.red(dirValidation));
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const answers = await inquirer.prompt([
|
|
43
|
+
{
|
|
44
|
+
type: 'input',
|
|
45
|
+
name: 'projectName',
|
|
46
|
+
message: 'What\'s your project name?',
|
|
47
|
+
default: options.name || 'my-saas',
|
|
48
|
+
validate: validateProjectName,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
type: 'input',
|
|
52
|
+
name: 'ideaWhat',
|
|
53
|
+
message: 'What are you building? (1 sentence)',
|
|
54
|
+
validate: (input) => input.length > 0 || 'Please describe your idea',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
type: 'input',
|
|
58
|
+
name: 'ideaFor',
|
|
59
|
+
message: 'Who is it for?',
|
|
60
|
+
validate: (input) => input.length > 0 || 'Please specify your target users',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
type: 'input',
|
|
64
|
+
name: 'problem1',
|
|
65
|
+
message: 'Problem #1 you\'re solving:',
|
|
66
|
+
default: '',
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
type: 'input',
|
|
70
|
+
name: 'problem2',
|
|
71
|
+
message: 'Problem #2 you\'re solving:',
|
|
72
|
+
default: '',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
type: 'input',
|
|
76
|
+
name: 'problem3',
|
|
77
|
+
message: 'Problem #3 you\'re solving:',
|
|
78
|
+
default: '',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
type: 'input',
|
|
82
|
+
name: 'feature1',
|
|
83
|
+
message: 'Critical production feature:',
|
|
84
|
+
default: '',
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
type: 'list',
|
|
88
|
+
name: 'frontend',
|
|
89
|
+
message: 'Frontend framework:',
|
|
90
|
+
choices: ['Next.js', 'Remix', 'SvelteKit', 'Nuxt', 'Other'],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
type: 'list',
|
|
94
|
+
name: 'database',
|
|
95
|
+
message: 'Database:',
|
|
96
|
+
choices: ['PostgreSQL', 'Supabase', 'MongoDB', 'PlanetScale', 'Other'],
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
type: 'list',
|
|
100
|
+
name: 'auth',
|
|
101
|
+
message: 'Authentication:',
|
|
102
|
+
choices: ['NextAuth', 'Clerk', 'Auth0', 'Supabase Auth', 'Other'],
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
type: 'list',
|
|
106
|
+
name: 'payments',
|
|
107
|
+
message: 'Payments:',
|
|
108
|
+
choices: ['Stripe', 'Lemonsqueezy', 'Paddle', 'None (free)', 'Other'],
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
type: 'list',
|
|
112
|
+
name: 'hosting',
|
|
113
|
+
message: 'Hosting:',
|
|
114
|
+
choices: ['Vercel', 'Railway', 'Fly.io', 'AWS', 'Other'],
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
type: 'confirm',
|
|
118
|
+
name: 'includeCursorRules',
|
|
119
|
+
message: 'Include cursor-rules for AI assistants? (Cursor, Copilot)',
|
|
120
|
+
default: true,
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
type: 'confirm',
|
|
124
|
+
name: 'includeFullTemplate',
|
|
125
|
+
message: 'Copy full 34-section template locally?',
|
|
126
|
+
default: false,
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
type: 'confirm',
|
|
130
|
+
name: 'includeDocs',
|
|
131
|
+
message: 'Copy VERIFICATION.md & AGENT-INSTRUCTIONS.md to docs/?',
|
|
132
|
+
default: true,
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
type: 'confirm',
|
|
136
|
+
name: 'includeAgents',
|
|
137
|
+
message: 'Include AI agent prompts? (.agents/ folder)',
|
|
138
|
+
default: true,
|
|
139
|
+
},
|
|
140
|
+
]);
|
|
141
|
+
|
|
142
|
+
const spinner = ora('Creating project files...').start();
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
const outputDir = path.resolve(options.dir, answers.projectName);
|
|
146
|
+
|
|
147
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
148
|
+
await fs.mkdir(path.join(outputDir, 'docs'), { recursive: true });
|
|
149
|
+
|
|
150
|
+
const replacements = {
|
|
151
|
+
'{{PROJECT_NAME}}': answers.projectName,
|
|
152
|
+
'{{DATE}}': new Date().toISOString().split('T')[0],
|
|
153
|
+
'{{IDEA_WHAT}}': answers.ideaWhat,
|
|
154
|
+
'{{IDEA_FOR}}': answers.ideaFor,
|
|
155
|
+
'{{PROBLEM_1}}': answers.problem1 || 'Problem 1',
|
|
156
|
+
'{{PROBLEM_2}}': answers.problem2 || 'Problem 2',
|
|
157
|
+
'{{PROBLEM_3}}': answers.problem3 || 'Problem 3',
|
|
158
|
+
'{{FEATURE_1}}': answers.feature1 || 'Core feature',
|
|
159
|
+
'{{FRONTEND}}': answers.frontend,
|
|
160
|
+
'{{DATABASE}}': answers.database,
|
|
161
|
+
'{{AUTH}}': answers.auth,
|
|
162
|
+
'{{PAYMENTS}}': answers.payments,
|
|
163
|
+
'{{HOSTING}}': answers.hosting,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
let quickStart = QUICK_START_TEMPLATE;
|
|
167
|
+
let context = CONTEXT_TEMPLATE;
|
|
168
|
+
|
|
169
|
+
for (const [key, value] of Object.entries(replacements)) {
|
|
170
|
+
quickStart = quickStart.replace(new RegExp(key, 'g'), value);
|
|
171
|
+
context = context.replace(new RegExp(key, 'g'), value);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
await fs.writeFile(path.join(outputDir, 'QUICK-START.md'), quickStart);
|
|
175
|
+
await fs.writeFile(path.join(outputDir, 'CONTEXT.md'), context);
|
|
176
|
+
|
|
177
|
+
const planContent = `# ${answers.projectName} - Implementation Plan
|
|
178
|
+
|
|
179
|
+
> Generated with Ultra-Dex CLI
|
|
180
|
+
|
|
181
|
+
## Overview
|
|
182
|
+
|
|
183
|
+
${answers.ideaWhat} for ${answers.ideaFor}.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Next Steps
|
|
188
|
+
|
|
189
|
+
1. Open QUICK-START.md and complete the remaining sections
|
|
190
|
+
2. Copy sections from the full Ultra-Dex template as needed
|
|
191
|
+
3. Use the TaskFlow example as reference
|
|
192
|
+
4. Start building!
|
|
193
|
+
|
|
194
|
+
## Resources
|
|
195
|
+
|
|
196
|
+
- [Full Template](${githubBlobUrl('@%20Ultra%20DeX/Saas%20plan/04-Imp-Template.md')})
|
|
197
|
+
- [TaskFlow Example](${githubBlobUrl('@%20Ultra%20DeX/Saas%20plan/Examples/TaskFlow-Complete.md')})
|
|
198
|
+
- [Methodology](${githubBlobUrl('@%20Ultra%20DeX/Saas%20plan/03-METHODOLOGY.md')})
|
|
199
|
+
`;
|
|
200
|
+
|
|
201
|
+
await fs.writeFile(path.join(outputDir, 'IMPLEMENTATION-PLAN.md'), planContent);
|
|
202
|
+
|
|
203
|
+
if (answers.includeCursorRules) {
|
|
204
|
+
const rulesDir = path.join(outputDir, '.cursor', 'rules');
|
|
205
|
+
await fs.mkdir(rulesDir, { recursive: true });
|
|
206
|
+
|
|
207
|
+
const cursorRulesPath = path.join(ASSETS_ROOT, 'cursor-rules');
|
|
208
|
+
const fallbackRulesPath = path.join(ROOT_FALLBACK, 'cursor-rules');
|
|
209
|
+
try {
|
|
210
|
+
const ruleFiles = await listWithFallback(cursorRulesPath, fallbackRulesPath);
|
|
211
|
+
const sourcePath = ruleFiles ? cursorRulesPath : fallbackRulesPath;
|
|
212
|
+
for (const file of ruleFiles.filter(f => f.endsWith('.mdc'))) {
|
|
213
|
+
await copyWithFallback(
|
|
214
|
+
path.join(sourcePath, file),
|
|
215
|
+
null,
|
|
216
|
+
path.join(rulesDir, file)
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const coreRulePath = path.join(sourcePath, '00-ultra-dex-core.mdc');
|
|
221
|
+
try {
|
|
222
|
+
const coreContent = await readWithFallback(coreRulePath, null, 'utf-8');
|
|
223
|
+
const dotGithub = path.join(outputDir, '.github');
|
|
224
|
+
await fs.mkdir(dotGithub, { recursive: true });
|
|
225
|
+
await fs.writeFile(path.join(dotGithub, 'copilot-instructions.md'), coreContent);
|
|
226
|
+
} catch {
|
|
227
|
+
// Core rule not available - skip Copilot setup
|
|
228
|
+
}
|
|
229
|
+
} catch {
|
|
230
|
+
console.log(chalk.red('\n ❌ Cursor rules not found in assets or repo.'));
|
|
231
|
+
console.log(chalk.cyan(' Fetch: npx ultra-dex fetch --rules'));
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (answers.includeFullTemplate) {
|
|
236
|
+
const templatePath = path.join(ASSETS_ROOT, 'saas-plan', '04-Imp-Template.md');
|
|
237
|
+
const fallbackTemplatePath = path.join(ROOT_FALLBACK, '@ Ultra DeX', 'Saas plan', '04-Imp-Template.md');
|
|
238
|
+
try {
|
|
239
|
+
await copyWithFallback(templatePath, fallbackTemplatePath, path.join(outputDir, 'docs', 'MASTER-PLAN.md'));
|
|
240
|
+
} catch {
|
|
241
|
+
console.log(chalk.red('\n ❌ Full template not found in assets or repo.'));
|
|
242
|
+
console.log(chalk.cyan(' Fetch: npx ultra-dex fetch --docs'));
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (answers.includeDocs) {
|
|
247
|
+
const verificationPath = path.join(ASSETS_ROOT, 'docs', 'VERIFICATION.md');
|
|
248
|
+
const agentPath = path.join(ASSETS_ROOT, 'agents', 'AGENT-INSTRUCTIONS.md');
|
|
249
|
+
const fallbackVerificationPath = path.join(ROOT_FALLBACK, 'docs', 'VERIFICATION.md');
|
|
250
|
+
const fallbackAgentPath = path.join(ROOT_FALLBACK, 'agents', 'AGENT-INSTRUCTIONS.md');
|
|
251
|
+
try {
|
|
252
|
+
await copyWithFallback(verificationPath, fallbackVerificationPath, path.join(outputDir, 'docs', 'CHECKLIST.md'));
|
|
253
|
+
await copyWithFallback(agentPath, fallbackAgentPath, path.join(outputDir, 'docs', 'AI-PROMPTS.md'));
|
|
254
|
+
} catch {
|
|
255
|
+
console.log(chalk.red('\n ❌ Docs not found in assets or repo.'));
|
|
256
|
+
console.log(chalk.cyan(' Fetch: npx ultra-dex fetch --docs'));
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (answers.includeAgents) {
|
|
261
|
+
const agentsDir = path.join(outputDir, '.agents');
|
|
262
|
+
await fs.mkdir(agentsDir, { recursive: true });
|
|
263
|
+
|
|
264
|
+
const agentsSourcePath = path.join(ASSETS_ROOT, 'agents');
|
|
265
|
+
const fallbackAgentsPath = path.join(ROOT_FALLBACK, 'agents');
|
|
266
|
+
try {
|
|
267
|
+
const tiers = ['1-leadership', '2-development', '3-security', '4-devops', '5-quality', '6-specialist'];
|
|
268
|
+
let sourceRoot = agentsSourcePath;
|
|
269
|
+
try {
|
|
270
|
+
await fs.access(agentsSourcePath);
|
|
271
|
+
} catch {
|
|
272
|
+
sourceRoot = fallbackAgentsPath;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
for (const tier of tiers) {
|
|
276
|
+
const tierDir = path.join(agentsDir, tier);
|
|
277
|
+
await fs.mkdir(tierDir, { recursive: true });
|
|
278
|
+
|
|
279
|
+
const tierPath = path.join(sourceRoot, tier);
|
|
280
|
+
const tierFiles = await fs.readdir(tierPath);
|
|
281
|
+
for (const file of tierFiles.filter(f => f.endsWith('.md'))) {
|
|
282
|
+
await fs.copyFile(
|
|
283
|
+
path.join(tierPath, file),
|
|
284
|
+
path.join(tierDir, file)
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
await fs.copyFile(
|
|
290
|
+
path.join(sourceRoot, '00-AGENT_INDEX.md'),
|
|
291
|
+
path.join(agentsDir, '00-AGENT_INDEX.md')
|
|
292
|
+
);
|
|
293
|
+
await fs.copyFile(
|
|
294
|
+
path.join(sourceRoot, 'README.md'),
|
|
295
|
+
path.join(agentsDir, 'README.md')
|
|
296
|
+
);
|
|
297
|
+
} catch {
|
|
298
|
+
console.log(chalk.red('\n ❌ Agent prompts not found in assets or repo.'));
|
|
299
|
+
console.log(chalk.cyan(' Fetch: npx ultra-dex fetch --agents'));
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
spinner.succeed(chalk.green('Project created successfully!'));
|
|
304
|
+
|
|
305
|
+
console.log('\n' + chalk.bold('Files created:'));
|
|
306
|
+
console.log(chalk.gray(` ${outputDir}/`));
|
|
307
|
+
console.log(chalk.gray(' ├── QUICK-START.md'));
|
|
308
|
+
console.log(chalk.gray(' ├── CONTEXT.md'));
|
|
309
|
+
console.log(chalk.gray(' ├── IMPLEMENTATION-PLAN.md'));
|
|
310
|
+
if (answers.includeFullTemplate) {
|
|
311
|
+
console.log(chalk.gray(' ├── docs/MASTER-PLAN.md (34 sections)'));
|
|
312
|
+
}
|
|
313
|
+
if (answers.includeDocs) {
|
|
314
|
+
console.log(chalk.gray(' ├── docs/CHECKLIST.md'));
|
|
315
|
+
console.log(chalk.gray(' ├── docs/AI-PROMPTS.md'));
|
|
316
|
+
}
|
|
317
|
+
if (answers.includeCursorRules) {
|
|
318
|
+
console.log(chalk.gray(' ├── .cursor/rules/ (11 AI rule files)'));
|
|
319
|
+
}
|
|
320
|
+
if (answers.includeAgents) {
|
|
321
|
+
console.log(chalk.gray(' └── .agents/ (15 AI agent prompts in 6 tiers)'));
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
console.log('\n' + chalk.bold('Next steps:'));
|
|
325
|
+
console.log(chalk.cyan(` 1. cd ${answers.projectName}`));
|
|
326
|
+
console.log(chalk.cyan(' 2. Open QUICK-START.md and complete it'));
|
|
327
|
+
console.log(chalk.cyan(' 3. Start building! 🚀'));
|
|
328
|
+
|
|
329
|
+
console.log('\n' + chalk.gray('Full Ultra-Dex repo:'));
|
|
330
|
+
console.log(chalk.blue(` ${githubWebUrl()}`));
|
|
331
|
+
} catch (error) {
|
|
332
|
+
spinner.fail(chalk.red('Failed to create project'));
|
|
333
|
+
console.error(error);
|
|
334
|
+
process.exit(1);
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
// Note: build, review, and align commands are now in separate files:
|
|
4
|
+
// - build.js (registerBuildCommand)
|
|
5
|
+
// - review.js (registerReviewCommand)
|
|
6
|
+
// The align command is a simplified version of review
|
|
7
|
+
|
|
8
|
+
export function registerPlaceholderCommands(program) {
|
|
9
|
+
// Placeholder for future commands
|
|
10
|
+
// All v2 commands have been implemented in their own files
|
|
11
|
+
}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ultra-dex review command
|
|
3
|
+
* Reviews code against the implementation plan using AI
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import ora from 'ora';
|
|
8
|
+
import inquirer from 'inquirer';
|
|
9
|
+
import fs from 'fs/promises';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import { createProvider, getDefaultProvider, checkConfiguredProviders } from '../providers/index.js';
|
|
12
|
+
import { SYSTEM_PROMPT, generateReviewPrompt } from '../templates/prompts/review-code.js';
|
|
13
|
+
|
|
14
|
+
// File patterns to scan
|
|
15
|
+
const CODE_PATTERNS = {
|
|
16
|
+
database: ['**/prisma/schema.prisma', '**/schema.sql', '**/migrations/**', '**/models/**'],
|
|
17
|
+
api: ['**/api/**', '**/routes/**', '**/controllers/**', '**/src/app/api/**'],
|
|
18
|
+
auth: ['**/auth/**', '**/middleware/**', '**/*auth*', '**/*session*'],
|
|
19
|
+
frontend: ['**/components/**', '**/pages/**', '**/app/**', '**/src/app/**'],
|
|
20
|
+
testing: ['**/*.test.*', '**/*.spec.*', '**/tests/**', '**/__tests__/**'],
|
|
21
|
+
config: ['package.json', 'tsconfig.json', '.env.example', 'next.config.*'],
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
async function readFileSafe(filePath) {
|
|
25
|
+
try {
|
|
26
|
+
return await fs.readFile(filePath, 'utf-8');
|
|
27
|
+
} catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function fileExists(filePath) {
|
|
33
|
+
try {
|
|
34
|
+
await fs.access(filePath);
|
|
35
|
+
return true;
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function getDirectoryStructure(dir, depth = 3, prefix = '') {
|
|
42
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
43
|
+
let structure = '';
|
|
44
|
+
|
|
45
|
+
for (const entry of entries) {
|
|
46
|
+
// Skip common non-essential directories
|
|
47
|
+
if (['node_modules', '.git', '.next', 'dist', 'build', '.ultra-dex'].includes(entry.name)) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const entryPath = path.join(dir, entry.name);
|
|
52
|
+
|
|
53
|
+
if (entry.isDirectory()) {
|
|
54
|
+
structure += `${prefix}📁 ${entry.name}/\n`;
|
|
55
|
+
if (depth > 1) {
|
|
56
|
+
structure += await getDirectoryStructure(entryPath, depth - 1, prefix + ' ');
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
structure += `${prefix}📄 ${entry.name}\n`;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return structure;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function findKeyFiles(dir) {
|
|
67
|
+
const keyFiles = [];
|
|
68
|
+
const patterns = [
|
|
69
|
+
'package.json',
|
|
70
|
+
'prisma/schema.prisma',
|
|
71
|
+
'src/app/api',
|
|
72
|
+
'app/api',
|
|
73
|
+
'middleware.ts',
|
|
74
|
+
'middleware.js',
|
|
75
|
+
'auth.ts',
|
|
76
|
+
'auth.js',
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
for (const pattern of patterns) {
|
|
80
|
+
const filePath = path.join(dir, pattern);
|
|
81
|
+
if (await fileExists(filePath)) {
|
|
82
|
+
try {
|
|
83
|
+
const stat = await fs.stat(filePath);
|
|
84
|
+
if (stat.isFile()) {
|
|
85
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
86
|
+
keyFiles.push({ path: pattern, content: content.slice(0, 3000) });
|
|
87
|
+
}
|
|
88
|
+
} catch {
|
|
89
|
+
// Skip files we can't read
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return keyFiles;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function registerReviewCommand(program) {
|
|
98
|
+
program
|
|
99
|
+
.command('review')
|
|
100
|
+
.description('Review code against the implementation plan')
|
|
101
|
+
.option('-d, --dir <directory>', 'Directory to review', '.')
|
|
102
|
+
.option('-p, --provider <provider>', 'AI provider (claude, openai, gemini)')
|
|
103
|
+
.option('-k, --key <apiKey>', 'API key')
|
|
104
|
+
.option('--quick', 'Quick review without AI (checks file structure only)')
|
|
105
|
+
.option('--json', 'Output as JSON')
|
|
106
|
+
.action(async (options) => {
|
|
107
|
+
console.log(chalk.cyan('\n🔍 Ultra-Dex Code Review\n'));
|
|
108
|
+
|
|
109
|
+
const reviewDir = path.resolve(options.dir);
|
|
110
|
+
|
|
111
|
+
// Check for plan
|
|
112
|
+
const planPath = path.join(reviewDir, 'IMPLEMENTATION-PLAN.md');
|
|
113
|
+
const plan = await readFileSafe(planPath);
|
|
114
|
+
|
|
115
|
+
if (!plan) {
|
|
116
|
+
console.log(chalk.yellow('⚠️ No IMPLEMENTATION-PLAN.md found.\n'));
|
|
117
|
+
console.log(chalk.white('Run one of these first:'));
|
|
118
|
+
console.log(chalk.gray(' npx ultra-dex init'));
|
|
119
|
+
console.log(chalk.gray(' npx ultra-dex generate\n'));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Get directory structure
|
|
124
|
+
const spinner = ora('Scanning codebase...').start();
|
|
125
|
+
const structure = await getDirectoryStructure(reviewDir);
|
|
126
|
+
const keyFiles = await findKeyFiles(reviewDir);
|
|
127
|
+
spinner.succeed('Codebase scanned');
|
|
128
|
+
|
|
129
|
+
if (options.quick) {
|
|
130
|
+
// Quick review - just check structure
|
|
131
|
+
console.log(chalk.white('\n📁 Project Structure:\n'));
|
|
132
|
+
console.log(chalk.gray(structure));
|
|
133
|
+
|
|
134
|
+
// Quick checks
|
|
135
|
+
const checks = [
|
|
136
|
+
{ name: 'IMPLEMENTATION-PLAN.md', path: 'IMPLEMENTATION-PLAN.md' },
|
|
137
|
+
{ name: 'CONTEXT.md', path: 'CONTEXT.md' },
|
|
138
|
+
{ name: 'package.json', path: 'package.json' },
|
|
139
|
+
{ name: 'Database schema', path: 'prisma/schema.prisma' },
|
|
140
|
+
{ name: 'API routes', path: 'src/app/api' },
|
|
141
|
+
{ name: 'Tests', path: 'tests' },
|
|
142
|
+
];
|
|
143
|
+
|
|
144
|
+
console.log(chalk.white('\n📋 Quick Checks:\n'));
|
|
145
|
+
for (const check of checks) {
|
|
146
|
+
const exists = await fileExists(path.join(reviewDir, check.path));
|
|
147
|
+
const icon = exists ? chalk.green('✅') : chalk.red('❌');
|
|
148
|
+
console.log(` ${icon} ${check.name}`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
console.log(chalk.cyan('\n💡 For full AI-powered review, run without --quick\n'));
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Full AI review
|
|
156
|
+
const configured = checkConfiguredProviders();
|
|
157
|
+
const hasProvider = configured.some(p => p.configured) || options.key;
|
|
158
|
+
|
|
159
|
+
if (!hasProvider) {
|
|
160
|
+
console.log(chalk.yellow('\n⚠️ No AI provider configured for full review.\n'));
|
|
161
|
+
console.log(chalk.white('Options:'));
|
|
162
|
+
console.log(chalk.gray(' 1. Set API key: export ANTHROPIC_API_KEY=your-key'));
|
|
163
|
+
console.log(chalk.gray(' 2. Use --quick for structure-only review'));
|
|
164
|
+
console.log(chalk.gray(' 3. Use --key option: npx ultra-dex review --key sk-...\n'));
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const providerId = options.provider || getDefaultProvider();
|
|
169
|
+
console.log(chalk.gray(`Using provider: ${providerId}\n`));
|
|
170
|
+
|
|
171
|
+
let provider;
|
|
172
|
+
try {
|
|
173
|
+
provider = createProvider(providerId, {
|
|
174
|
+
apiKey: options.key,
|
|
175
|
+
maxTokens: 4000,
|
|
176
|
+
});
|
|
177
|
+
} catch (err) {
|
|
178
|
+
console.log(chalk.red(`Error: ${err.message}`));
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Build file summary
|
|
183
|
+
const filesSummary = keyFiles.map(f => `### ${f.path}\n\`\`\`\n${f.content}\n\`\`\``).join('\n\n');
|
|
184
|
+
|
|
185
|
+
spinner.start('Analyzing code against plan...');
|
|
186
|
+
|
|
187
|
+
try {
|
|
188
|
+
const result = await provider.generate(
|
|
189
|
+
SYSTEM_PROMPT,
|
|
190
|
+
generateReviewPrompt(plan.slice(0, 15000), structure, filesSummary)
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
spinner.succeed('Analysis complete');
|
|
194
|
+
|
|
195
|
+
// Parse the result
|
|
196
|
+
let report;
|
|
197
|
+
try {
|
|
198
|
+
// Try to extract JSON from the response
|
|
199
|
+
const jsonMatch = result.content.match(/\{[\s\S]*\}/);
|
|
200
|
+
if (jsonMatch) {
|
|
201
|
+
report = JSON.parse(jsonMatch[0]);
|
|
202
|
+
} else {
|
|
203
|
+
report = { raw: result.content };
|
|
204
|
+
}
|
|
205
|
+
} catch {
|
|
206
|
+
report = { raw: result.content };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (options.json) {
|
|
210
|
+
console.log(JSON.stringify(report, null, 2));
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Display formatted report
|
|
215
|
+
console.log(chalk.white('\n' + '═'.repeat(60)));
|
|
216
|
+
console.log(chalk.bold.cyan(' ULTRA-DEX CODE REVIEW REPORT'));
|
|
217
|
+
console.log(chalk.white('═'.repeat(60) + '\n'));
|
|
218
|
+
|
|
219
|
+
if (report.alignmentScore !== undefined) {
|
|
220
|
+
const score = report.alignmentScore;
|
|
221
|
+
const color = score >= 80 ? chalk.green : score >= 60 ? chalk.yellow : chalk.red;
|
|
222
|
+
console.log(chalk.white(' Alignment Score: ') + color.bold(`${score}/100`));
|
|
223
|
+
console.log();
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (report.summary) {
|
|
227
|
+
console.log(chalk.white(' Summary:'));
|
|
228
|
+
console.log(chalk.gray(` ${report.summary}\n`));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (report.sections) {
|
|
232
|
+
console.log(chalk.white(' Section Scores:\n'));
|
|
233
|
+
for (const [section, data] of Object.entries(report.sections)) {
|
|
234
|
+
const icon = data.status === 'aligned' ? '✅' :
|
|
235
|
+
data.status === 'deviated' ? '⚠️' : '❌';
|
|
236
|
+
const scoreColor = data.score >= 80 ? chalk.green :
|
|
237
|
+
data.score >= 60 ? chalk.yellow : chalk.red;
|
|
238
|
+
console.log(` ${icon} ${section.padEnd(12)} ${scoreColor(`${data.score}%`)} - ${data.notes || ''}`);
|
|
239
|
+
}
|
|
240
|
+
console.log();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (report.criticalIssues?.length) {
|
|
244
|
+
console.log(chalk.red.bold(' ⚠️ Critical Issues:\n'));
|
|
245
|
+
report.criticalIssues.forEach((issue, i) => {
|
|
246
|
+
console.log(chalk.red(` ${i + 1}. ${issue}`));
|
|
247
|
+
});
|
|
248
|
+
console.log();
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (report.suggestions?.length) {
|
|
252
|
+
console.log(chalk.yellow(' 💡 Suggestions:\n'));
|
|
253
|
+
report.suggestions.forEach((suggestion, i) => {
|
|
254
|
+
console.log(chalk.gray(` ${i + 1}. ${suggestion}`));
|
|
255
|
+
});
|
|
256
|
+
console.log();
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (report.nextSteps?.length) {
|
|
260
|
+
console.log(chalk.cyan(' 📋 Next Steps:\n'));
|
|
261
|
+
report.nextSteps.forEach((step, i) => {
|
|
262
|
+
console.log(chalk.white(` ${i + 1}. ${step}`));
|
|
263
|
+
});
|
|
264
|
+
console.log();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// If we couldn't parse, show raw
|
|
268
|
+
if (report.raw) {
|
|
269
|
+
console.log(chalk.white(' Analysis:\n'));
|
|
270
|
+
console.log(chalk.gray(report.raw));
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
console.log(chalk.white('═'.repeat(60) + '\n'));
|
|
274
|
+
|
|
275
|
+
// Cost info
|
|
276
|
+
const cost = provider.estimateCost(result.usage.inputTokens, result.usage.outputTokens);
|
|
277
|
+
console.log(chalk.gray(` Tokens: ${result.usage.inputTokens} in / ${result.usage.outputTokens} out`));
|
|
278
|
+
console.log(chalk.gray(` Cost: ~$${cost.total.toFixed(4)}\n`));
|
|
279
|
+
|
|
280
|
+
} catch (err) {
|
|
281
|
+
spinner.fail('Review failed');
|
|
282
|
+
console.log(chalk.red(`\nError: ${err.message}`));
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export default { registerReviewCommand };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import fs from 'fs/promises';
|
|
3
|
+
import http from 'http';
|
|
4
|
+
import { validateSafePath } from '../utils/validation.js';
|
|
5
|
+
|
|
6
|
+
async function readFileSafe(filePath, label) {
|
|
7
|
+
try {
|
|
8
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
9
|
+
return { label, content };
|
|
10
|
+
} catch {
|
|
11
|
+
return { label, content: '' };
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function registerServeCommand(program) {
|
|
16
|
+
program
|
|
17
|
+
.command('serve')
|
|
18
|
+
.description('Serve Ultra-Dex context over HTTP (MCP-compatible)')
|
|
19
|
+
.option('-p, --port <port>', 'Port to listen on', '3001')
|
|
20
|
+
.action(async (options) => {
|
|
21
|
+
const port = Number.parseInt(options.port, 10);
|
|
22
|
+
if (Number.isNaN(port)) {
|
|
23
|
+
console.log(chalk.red('Invalid port. Use a numeric value.'));
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const server = http.createServer(async (req, res) => {
|
|
28
|
+
if (!req.url || req.url === '/') {
|
|
29
|
+
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
30
|
+
res.end('Ultra-Dex MCP Server\n');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (req.url === '/context') {
|
|
35
|
+
const [context, plan, quickStart] = await Promise.all([
|
|
36
|
+
readFileSafe('CONTEXT.md', 'CONTEXT.md'),
|
|
37
|
+
readFileSafe('IMPLEMENTATION-PLAN.md', 'IMPLEMENTATION-PLAN.md'),
|
|
38
|
+
readFileSafe('QUICK-START.md', 'QUICK-START.md'),
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
42
|
+
res.end(JSON.stringify({ files: [context, plan, quickStart] }));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
47
|
+
res.end(JSON.stringify({ error: 'Not found' }));
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
server.listen(port, () => {
|
|
51
|
+
console.log(chalk.green(`\n✅ Ultra-Dex MCP server running on http://localhost:${port}`));
|
|
52
|
+
console.log(chalk.gray(' GET /context -> CONTEXT.md, IMPLEMENTATION-PLAN.md, QUICK-START.md'));
|
|
53
|
+
console.log(chalk.gray(' GET / -> health check\n'));
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|