ultra-dex 2.2.1 ā 3.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 +112 -151
- package/assets/agents/00-AGENT_INDEX.md +1 -1
- package/assets/code-patterns/clerk-middleware.ts +138 -0
- package/assets/code-patterns/prisma-schema.prisma +224 -0
- package/assets/code-patterns/rls-policies.sql +246 -0
- package/assets/code-patterns/server-actions.ts +191 -0
- package/assets/code-patterns/trpc-router.ts +258 -0
- package/assets/cursor-rules/13-ai-integration.mdc +155 -0
- package/assets/cursor-rules/14-server-components.mdc +81 -0
- package/assets/cursor-rules/15-server-actions.mdc +102 -0
- package/assets/cursor-rules/16-edge-middleware.mdc +105 -0
- package/assets/cursor-rules/17-streaming-ssr.mdc +138 -0
- package/assets/docs/LAUNCH-POSTS.md +1 -1
- package/assets/docs/QUICK-REFERENCE.md +9 -4
- package/assets/docs/VISION-V2.md +1 -1
- package/assets/hooks/pre-commit +98 -0
- package/assets/saas-plan/04-Imp-Template.md +1 -1
- package/bin/ultra-dex.js +132 -4
- package/lib/commands/advanced.js +471 -0
- package/lib/commands/agent-builder.js +226 -0
- package/lib/commands/agents.js +102 -42
- package/lib/commands/auto-implement.js +68 -0
- package/lib/commands/banner.js +43 -21
- package/lib/commands/build.js +78 -183
- package/lib/commands/ci-monitor.js +84 -0
- package/lib/commands/config.js +207 -0
- package/lib/commands/dashboard.js +770 -0
- package/lib/commands/diff.js +233 -0
- package/lib/commands/doctor.js +416 -0
- package/lib/commands/export.js +408 -0
- package/lib/commands/fix.js +96 -0
- package/lib/commands/generate.js +105 -78
- package/lib/commands/hooks.js +251 -76
- package/lib/commands/init.js +102 -54
- package/lib/commands/memory.js +80 -0
- package/lib/commands/plan.js +82 -0
- package/lib/commands/review.js +34 -5
- package/lib/commands/run.js +233 -0
- package/lib/commands/scaffold.js +151 -0
- package/lib/commands/serve.js +179 -146
- package/lib/commands/state.js +327 -0
- package/lib/commands/swarm.js +306 -0
- package/lib/commands/sync.js +82 -23
- package/lib/commands/team.js +275 -0
- package/lib/commands/upgrade.js +190 -0
- package/lib/commands/validate.js +34 -0
- package/lib/commands/verify.js +81 -0
- package/lib/commands/watch.js +79 -0
- package/lib/config/theme.js +47 -0
- package/lib/mcp/graph.js +92 -0
- package/lib/mcp/memory.js +95 -0
- package/lib/mcp/resources.js +152 -0
- package/lib/mcp/server.js +34 -0
- package/lib/mcp/tools.js +481 -0
- package/lib/mcp/websocket.js +117 -0
- package/lib/providers/index.js +49 -4
- package/lib/providers/ollama.js +136 -0
- package/lib/providers/router.js +63 -0
- package/lib/quality/scanner.js +128 -0
- package/lib/swarm/coordinator.js +97 -0
- package/lib/swarm/index.js +598 -0
- package/lib/swarm/protocol.js +677 -0
- package/lib/swarm/tiers.js +485 -0
- package/lib/templates/code/clerk-middleware.ts +138 -0
- package/lib/templates/code/prisma-schema.prisma +224 -0
- package/lib/templates/code/rls-policies.sql +246 -0
- package/lib/templates/code/server-actions.ts +191 -0
- package/lib/templates/code/trpc-router.ts +258 -0
- package/lib/templates/custom-agent.md +10 -0
- package/lib/themes/doomsday.js +229 -0
- package/lib/ui/index.js +5 -0
- package/lib/ui/interface.js +241 -0
- package/lib/ui/spinners.js +116 -0
- package/lib/ui/theme.js +183 -0
- package/lib/utils/agents.js +32 -0
- package/lib/utils/files.js +14 -0
- package/lib/utils/graph.js +108 -0
- package/lib/utils/help.js +64 -0
- package/lib/utils/messages.js +35 -0
- package/lib/utils/progress.js +24 -0
- package/lib/utils/prompts.js +47 -0
- package/lib/utils/spinners.js +46 -0
- package/lib/utils/status.js +31 -0
- package/lib/utils/tables.js +41 -0
- package/lib/utils/theme-state.js +9 -0
- package/lib/utils/version-display.js +32 -0
- package/package.json +31 -13
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ultra-dex run command
|
|
3
|
+
* Execute agent tasks automatically (the "swarm" approach)
|
|
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 { validateSafePath } from '../utils/validation.js';
|
|
13
|
+
import { projectGraph } from '../mcp/graph.js';
|
|
14
|
+
|
|
15
|
+
const AGENTS = {
|
|
16
|
+
planner: {
|
|
17
|
+
name: '@Planner',
|
|
18
|
+
role: 'Task Breakdown Specialist',
|
|
19
|
+
systemPrompt: `You are @Planner. Break down features into atomic tasks.
|
|
20
|
+
Use >> SEARCH_CODE: "query" to find existing patterns.
|
|
21
|
+
Output format:
|
|
22
|
+
## Task Breakdown
|
|
23
|
+
### Task 1: [Name]
|
|
24
|
+
- Agent: @Backend | @Frontend | ...
|
|
25
|
+
- Description: ...
|
|
26
|
+
`,
|
|
27
|
+
},
|
|
28
|
+
cto: {
|
|
29
|
+
name: '@CTO',
|
|
30
|
+
role: 'Technical Architecture Lead',
|
|
31
|
+
systemPrompt: `You are @CTO. Make tech decisions, design architecture, set standards.
|
|
32
|
+
Use >> READ_CODE: "path/to/file" to review existing architecture.`,
|
|
33
|
+
},
|
|
34
|
+
backend: {
|
|
35
|
+
name: '@Backend',
|
|
36
|
+
role: 'API & Business Logic Developer',
|
|
37
|
+
systemPrompt: `You are @Backend. Write API/Service code.
|
|
38
|
+
Available commands:
|
|
39
|
+
>> READ_CODE: "path" - Read a file
|
|
40
|
+
>> WRITE_CODE: "path" "content" - Create/Update a file
|
|
41
|
+
>> SEARCH_CODE: "query" - Search codebase
|
|
42
|
+
>> DELEGATE: @AgentName "Task" - Delegate work`,
|
|
43
|
+
},
|
|
44
|
+
frontend: {
|
|
45
|
+
name: '@Frontend',
|
|
46
|
+
role: 'UI/UX Developer',
|
|
47
|
+
systemPrompt: `You are @Frontend. Build React/Next.js components.
|
|
48
|
+
Available commands:
|
|
49
|
+
>> READ_CODE: "path"
|
|
50
|
+
>> WRITE_CODE: "path" "content"
|
|
51
|
+
>> SEARCH_CODE: "query"`,
|
|
52
|
+
},
|
|
53
|
+
database: {
|
|
54
|
+
name: '@Database',
|
|
55
|
+
role: 'Database Architect',
|
|
56
|
+
systemPrompt: `You are @Database. Design schemas.
|
|
57
|
+
Available commands:
|
|
58
|
+
>> READ_CODE: "path"
|
|
59
|
+
>> WRITE_CODE: "path" "content"`,
|
|
60
|
+
},
|
|
61
|
+
testing: {
|
|
62
|
+
name: '@Testing',
|
|
63
|
+
role: 'QA Engineer',
|
|
64
|
+
systemPrompt: `You are @Testing. Write tests.
|
|
65
|
+
Available commands:
|
|
66
|
+
>> READ_CODE: "path"
|
|
67
|
+
>> WRITE_CODE: "path" "content"`,
|
|
68
|
+
},
|
|
69
|
+
reviewer: {
|
|
70
|
+
name: '@Reviewer',
|
|
71
|
+
role: 'Code Review Specialist',
|
|
72
|
+
systemPrompt: `You are @Reviewer. Audit code.
|
|
73
|
+
Available commands:
|
|
74
|
+
>> READ_CODE: "path"
|
|
75
|
+
>> SEARCH_CODE: "query"`,
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
async function readProjectContext() {
|
|
80
|
+
const context = {};
|
|
81
|
+
try { context.plan = await fs.readFile('IMPLEMENTATION-PLAN.md', 'utf8'); } catch { context.plan = null; }
|
|
82
|
+
try { context.context = await fs.readFile('CONTEXT.md', 'utf8'); } catch { context.context = null; }
|
|
83
|
+
try {
|
|
84
|
+
context.state = JSON.parse(await fs.readFile('.ultra/state.json', 'utf8'));
|
|
85
|
+
} catch {
|
|
86
|
+
try {
|
|
87
|
+
context.state = JSON.parse(await fs.readFile('.ultra-dex/state.json', 'utf8'));
|
|
88
|
+
} catch {
|
|
89
|
+
context.state = null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Graph Scan (God Mode)
|
|
94
|
+
try {
|
|
95
|
+
await projectGraph.scan();
|
|
96
|
+
context.graph = projectGraph.getSummary();
|
|
97
|
+
} catch (e) {
|
|
98
|
+
context.graph = null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return context;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function runAgentLoop(agentName, task, provider, projectContext, depth = 0) {
|
|
105
|
+
if (depth > 5) return `[System]: Max delegation depth reached.`;
|
|
106
|
+
|
|
107
|
+
const agent = AGENTS[agentName.toLowerCase()];
|
|
108
|
+
if (!agent) return `[System]: Unknown agent @${agentName}`;
|
|
109
|
+
|
|
110
|
+
const spinner = ora(`${agent.name} is working...`).start();
|
|
111
|
+
|
|
112
|
+
const graphInfo = projectContext.graph
|
|
113
|
+
? `## Codebase Graph\n- Files: ${projectContext.graph.nodeCount}\n- Dependencies: ${projectContext.graph.edgeCount}\n`
|
|
114
|
+
: '';
|
|
115
|
+
|
|
116
|
+
const historySection = projectContext.history ? `## Execution History\n${projectContext.history}\n\n` : '';
|
|
117
|
+
const contextSection = projectContext.context ? `## Context\n${projectContext.context.slice(0, 3000)}\n\n${graphInfo}${historySection}` : '';
|
|
118
|
+
const prompt = `${contextSection}## Task\n${task}\n\nYou can use tools by outputting:
|
|
119
|
+
>> READ_CODE: "filePath"
|
|
120
|
+
>> WRITE_CODE: "filePath" "fullContent"
|
|
121
|
+
>> SEARCH_CODE: "query"
|
|
122
|
+
>> DELEGATE: @AgentName "Task"`;
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const result = await provider.generate(agent.systemPrompt, prompt);
|
|
126
|
+
spinner.succeed(`${agent.name} completed.`);
|
|
127
|
+
|
|
128
|
+
let content = result.content;
|
|
129
|
+
|
|
130
|
+
// Tool Execution Logic (God Mode)
|
|
131
|
+
const readMatch = content.match(/>>\s*READ_CODE:\s*["'](.+?)["']/);
|
|
132
|
+
const writeMatch = content.match(/>>\s*WRITE_CODE:\s*["'](.+?)["']\s*["']([\s\S]+?)["']/);
|
|
133
|
+
const searchMatch = content.match(/>>\s*SEARCH_CODE:\s*["'](.+?)["']/);
|
|
134
|
+
const delegateMatch = content.match(/>>\s*DELEGATE:\s*@(\w+)\s*["'](.+?)["']/);
|
|
135
|
+
|
|
136
|
+
if (readMatch) {
|
|
137
|
+
const filePath = readMatch[1];
|
|
138
|
+
console.log(chalk.cyan(`\nš ${agent.name} is reading ${filePath}...`));
|
|
139
|
+
try {
|
|
140
|
+
const fileContent = await fs.readFile(path.resolve(process.cwd(), filePath), 'utf8');
|
|
141
|
+
const nextPrompt = `Output of READ_CODE "${filePath}":\n\`\`\`\n${fileContent}\n\`\`\`\n\nPlease proceed with your task.`;
|
|
142
|
+
return await runAgentLoop(agentName, `${task}\n\n${nextPrompt}`, provider, projectContext, depth + 1);
|
|
143
|
+
} catch (e) {
|
|
144
|
+
return await runAgentLoop(agentName, `${task}\n\nError reading ${filePath}: ${e.message}`, provider, projectContext, depth + 1);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (writeMatch) {
|
|
149
|
+
const filePath = writeMatch[1];
|
|
150
|
+
const newContent = writeMatch[2];
|
|
151
|
+
console.log(chalk.green(`\nš¾ ${agent.name} is writing to ${filePath}...`));
|
|
152
|
+
try {
|
|
153
|
+
await fs.mkdir(path.dirname(path.resolve(process.cwd(), filePath)), { recursive: true });
|
|
154
|
+
await fs.writeFile(path.resolve(process.cwd(), filePath), newContent, 'utf8');
|
|
155
|
+
const nextPrompt = `Successfully wrote ${filePath}. Please proceed or delegate verification.`;
|
|
156
|
+
return await runAgentLoop(agentName, `${task}\n\n${nextPrompt}`, provider, projectContext, depth + 1);
|
|
157
|
+
} catch (e) {
|
|
158
|
+
return await runAgentLoop(agentName, `${task}\n\nError writing ${filePath}: ${e.message}`, provider, projectContext, depth + 1);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (delegateMatch) {
|
|
163
|
+
const nextAgent = delegateMatch[1];
|
|
164
|
+
const nextTask = delegateMatch[2];
|
|
165
|
+
console.log(chalk.cyan(`\nāŖļø ${agent.name} is delegating to @${nextAgent}: "${nextTask}"`));
|
|
166
|
+
const subResult = await runAgentLoop(nextAgent, nextTask, provider, projectContext, depth + 1);
|
|
167
|
+
return `${content}\n\n---\n\n## Delegated Result from @${nextAgent}\n${subResult}`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return content;
|
|
171
|
+
} catch (err) {
|
|
172
|
+
spinner.fail(`${agent.name} failed: ${err.message}`);
|
|
173
|
+
return `[Error]: ${err.message}`;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function registerRunCommand(program) {
|
|
178
|
+
program.command('run <agent>')
|
|
179
|
+
.description('Execute an agent task automatically')
|
|
180
|
+
.option('-t, --task <task>', 'Task to execute')
|
|
181
|
+
.option('-p, --provider <provider>', 'AI provider')
|
|
182
|
+
.option('-k, --key <apiKey>', 'API key')
|
|
183
|
+
.option('-o, --output <file>', 'Output file')
|
|
184
|
+
.action(async (agentName, options) => {
|
|
185
|
+
const configured = checkConfiguredProviders();
|
|
186
|
+
const hasProvider = configured.some(p => p.configured) || options.key;
|
|
187
|
+
|
|
188
|
+
if (!hasProvider) {
|
|
189
|
+
console.log(chalk.yellow('ā ļø No AI provider configured.'));
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
let task = options.task;
|
|
194
|
+
if (!task) {
|
|
195
|
+
const { taskInput } = await inquirer.prompt([{
|
|
196
|
+
type: 'input', name: 'taskInput', message: `Task for ${agentName}?`
|
|
197
|
+
}]);
|
|
198
|
+
task = taskInput;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const context = await readProjectContext();
|
|
202
|
+
const providerId = options.provider || getDefaultProvider();
|
|
203
|
+
const provider = createProvider(providerId, { apiKey: options.key, maxTokens: 8000 });
|
|
204
|
+
|
|
205
|
+
const finalOutput = await runAgentLoop(agentName, task, provider, context);
|
|
206
|
+
|
|
207
|
+
if (options.output) {
|
|
208
|
+
await fs.writeFile(options.output, finalOutput);
|
|
209
|
+
console.log(chalk.green(`\nā
Saved to ${options.output}`));
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function registerSwarmCommand(program) {
|
|
215
|
+
program.command('swarm <feature>')
|
|
216
|
+
.description('Run a full agent swarm for a feature')
|
|
217
|
+
.option('-p, --provider <provider>', 'AI provider')
|
|
218
|
+
.option('-k, --key <apiKey>', 'API key')
|
|
219
|
+
.action(async (feature, options) => {
|
|
220
|
+
console.log(chalk.cyan('\nš Ultra-Dex Agent Swarm\n'));
|
|
221
|
+
const context = await readProjectContext();
|
|
222
|
+
const providerId = options.provider || getDefaultProvider();
|
|
223
|
+
const provider = createProvider(providerId, { apiKey: options.key, maxTokens: 8000 });
|
|
224
|
+
|
|
225
|
+
console.log(chalk.bold('Step 1: š @Planner breaking down feature...'));
|
|
226
|
+
const plan = await runAgentLoop('planner', feature, provider, context);
|
|
227
|
+
|
|
228
|
+
console.log(chalk.bold('\nStep 2: šļø @CTO reviewing architecture...'));
|
|
229
|
+
await runAgentLoop('cto', `Review plan:\n${plan}`, provider, context);
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export default { registerRunCommand, registerSwarmCommand };
|
|
@@ -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
|
+
}
|