ultra-dex 3.1.0 ā 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -74
- 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/bin/ultra-dex.js +50 -1
- package/lib/commands/agents.js +16 -13
- package/lib/commands/banner.js +43 -21
- package/lib/commands/build.js +26 -17
- package/lib/commands/cloud.js +780 -0
- package/lib/commands/doctor.js +98 -79
- package/lib/commands/exec.js +434 -0
- package/lib/commands/generate.js +19 -16
- package/lib/commands/github.js +475 -0
- package/lib/commands/init.js +52 -56
- package/lib/commands/scaffold.js +151 -0
- package/lib/commands/search.js +477 -0
- package/lib/commands/serve.js +15 -13
- package/lib/commands/state.js +43 -70
- package/lib/commands/swarm.js +31 -9
- package/lib/config/theme.js +47 -0
- package/lib/mcp/client.js +502 -0
- package/lib/providers/agent-sdk.js +630 -0
- package/lib/providers/anthropic-agents.js +580 -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/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/browser.js +373 -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 +19 -4
package/lib/commands/doctor.js
CHANGED
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import chalk from 'chalk';
|
|
7
|
-
import ora from 'ora';
|
|
8
7
|
import inquirer from 'inquirer';
|
|
9
8
|
import fs from 'fs/promises';
|
|
10
9
|
import path from 'path';
|
|
11
10
|
import { execSync } from 'child_process';
|
|
12
11
|
import { checkConfiguredProviders } from '../providers/index.js';
|
|
12
|
+
import { icons, header, statusLine } from '../utils/status.js';
|
|
13
|
+
import { createSpinner } from '../utils/spinners.js';
|
|
13
14
|
|
|
14
15
|
// Default configuration
|
|
15
16
|
const DEFAULT_CONFIG = {
|
|
@@ -55,17 +56,18 @@ async function saveConfig(config, global = false) {
|
|
|
55
56
|
export function registerDoctorCommand(program) {
|
|
56
57
|
program
|
|
57
58
|
.command('doctor')
|
|
58
|
-
.description('
|
|
59
|
+
.description('System Diagnostics - Check System Health')
|
|
59
60
|
.option('--fix', 'Attempt to fix issues automatically')
|
|
60
61
|
.action(async (options) => {
|
|
61
|
-
|
|
62
|
-
console.log(chalk.gray('
|
|
62
|
+
header('System Health Diagnostics');
|
|
63
|
+
console.log(chalk.gray(' Analyzing system components...\n'));
|
|
63
64
|
|
|
64
65
|
const checks = [];
|
|
65
66
|
let hasErrors = false;
|
|
66
67
|
|
|
67
68
|
// Check 1: Node.js version
|
|
68
|
-
const nodeSpinner =
|
|
69
|
+
const nodeSpinner = createSpinner('Scanning Node.js environment...');
|
|
70
|
+
nodeSpinner.start();
|
|
69
71
|
try {
|
|
70
72
|
const nodeVersion = process.version;
|
|
71
73
|
const major = parseInt(nodeVersion.slice(1).split('.')[0]);
|
|
@@ -83,7 +85,8 @@ export function registerDoctorCommand(program) {
|
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
// Check 2: Git
|
|
86
|
-
const gitSpinner =
|
|
88
|
+
const gitSpinner = createSpinner('Checking Git repository...');
|
|
89
|
+
gitSpinner.start();
|
|
87
90
|
try {
|
|
88
91
|
const gitVersion = execSync('git --version', { encoding: 'utf8' }).trim();
|
|
89
92
|
gitSpinner.succeed(`${gitVersion} ā`);
|
|
@@ -95,20 +98,22 @@ export function registerDoctorCommand(program) {
|
|
|
95
98
|
}
|
|
96
99
|
|
|
97
100
|
// Check 3: AI Providers
|
|
98
|
-
const providerSpinner =
|
|
101
|
+
const providerSpinner = createSpinner('Locating AI Providers...');
|
|
102
|
+
providerSpinner.start();
|
|
99
103
|
const providers = checkConfiguredProviders();
|
|
100
104
|
const configuredProviders = providers.filter(p => p.configured);
|
|
101
105
|
|
|
102
106
|
if (configuredProviders.length > 0) {
|
|
103
|
-
providerSpinner.succeed(`
|
|
107
|
+
providerSpinner.succeed(`Providers found: ${configuredProviders.map(p => p.name).join(', ')} ā`);
|
|
104
108
|
checks.push({ name: 'AI Providers', status: 'ok', detail: configuredProviders.map(p => p.name).join(', ') });
|
|
105
109
|
} else {
|
|
106
|
-
providerSpinner.warn('No AI
|
|
110
|
+
providerSpinner.warn('No AI Providers found');
|
|
107
111
|
checks.push({ name: 'AI Providers', status: 'warn', detail: 'Set ANTHROPIC_API_KEY, OPENAI_API_KEY, or GEMINI_API_KEY' });
|
|
108
112
|
}
|
|
109
113
|
|
|
110
114
|
// Check 4: Project structure
|
|
111
|
-
const structureSpinner =
|
|
115
|
+
const structureSpinner = createSpinner('Verifying Project Structure...');
|
|
116
|
+
structureSpinner.start();
|
|
112
117
|
const requiredFiles = ['CONTEXT.md', 'IMPLEMENTATION-PLAN.md'];
|
|
113
118
|
const optionalFiles = ['CHECKLIST.md', 'QUICK-START.md', '.ultra/state.json'];
|
|
114
119
|
const foundRequired = [];
|
|
@@ -129,26 +134,27 @@ export function registerDoctorCommand(program) {
|
|
|
129
134
|
}
|
|
130
135
|
|
|
131
136
|
if (foundRequired.length === requiredFiles.length) {
|
|
132
|
-
structureSpinner.succeed(`
|
|
137
|
+
structureSpinner.succeed(`Structure valid: ${foundRequired.length}/${requiredFiles.length} required artifacts ā`);
|
|
133
138
|
checks.push({ name: 'Project Structure', status: 'ok', detail: `${foundRequired.join(', ')}` });
|
|
134
139
|
} else if (foundRequired.length > 0) {
|
|
135
|
-
structureSpinner.warn(`
|
|
140
|
+
structureSpinner.warn(`Structure incomplete: ${foundRequired.length}/${requiredFiles.length} required artifacts`);
|
|
136
141
|
checks.push({ name: 'Project Structure', status: 'warn', detail: `Missing: ${requiredFiles.filter(f => !foundRequired.includes(f)).join(', ')}` });
|
|
137
142
|
} else {
|
|
138
143
|
structureSpinner.info('No Ultra-Dex project found');
|
|
139
|
-
checks.push({ name: 'Project Structure', status: 'info', detail: 'Run `ultra-dex init` to create a project' });
|
|
144
|
+
checks.push({ name: 'Project Structure', status: 'info', detail: 'Run `ultra-dex init` to create a new project' });
|
|
140
145
|
}
|
|
141
146
|
|
|
142
147
|
// Check 5: Git hooks
|
|
143
|
-
const hooksSpinner =
|
|
148
|
+
const hooksSpinner = createSpinner('Checking Git hooks...');
|
|
149
|
+
hooksSpinner.start();
|
|
144
150
|
try {
|
|
145
151
|
const hookPath = path.resolve(process.cwd(), '.git/hooks/pre-commit');
|
|
146
152
|
const hookContent = await fs.readFile(hookPath, 'utf8');
|
|
147
153
|
if (hookContent.includes('ultra-dex')) {
|
|
148
|
-
hooksSpinner.succeed('Pre-commit
|
|
154
|
+
hooksSpinner.succeed('Pre-commit active ā');
|
|
149
155
|
checks.push({ name: 'Git Hooks', status: 'ok', detail: 'Pre-commit active' });
|
|
150
156
|
} else {
|
|
151
|
-
hooksSpinner.info('Pre-commit
|
|
157
|
+
hooksSpinner.info('Pre-commit active but not Ultra-Dex');
|
|
152
158
|
checks.push({ name: 'Git Hooks', status: 'info', detail: 'Custom hook present' });
|
|
153
159
|
}
|
|
154
160
|
} catch {
|
|
@@ -157,13 +163,15 @@ export function registerDoctorCommand(program) {
|
|
|
157
163
|
}
|
|
158
164
|
|
|
159
165
|
// Check 6: Configuration
|
|
160
|
-
const configSpinner =
|
|
166
|
+
const configSpinner = createSpinner('Reading Configuration...');
|
|
167
|
+
configSpinner.start();
|
|
161
168
|
const config = await loadConfig();
|
|
162
|
-
configSpinner.succeed(`
|
|
169
|
+
configSpinner.succeed(`Configuration loaded from: ${config.source}`);
|
|
163
170
|
checks.push({ name: 'Configuration', status: 'ok', detail: `Source: ${config.source}` });
|
|
164
171
|
|
|
165
172
|
// Check 7: MCP Server port
|
|
166
|
-
const portSpinner =
|
|
173
|
+
const portSpinner = createSpinner('Checking MCP Port...');
|
|
174
|
+
portSpinner.start();
|
|
167
175
|
try {
|
|
168
176
|
const net = await import('net');
|
|
169
177
|
const server = net.createServer();
|
|
@@ -175,51 +183,52 @@ export function registerDoctorCommand(program) {
|
|
|
175
183
|
});
|
|
176
184
|
server.listen(config.mcpPort);
|
|
177
185
|
});
|
|
178
|
-
portSpinner.succeed(`Port ${config.mcpPort}
|
|
186
|
+
portSpinner.succeed(`Port ${config.mcpPort} open ā`);
|
|
179
187
|
checks.push({ name: 'MCP Port', status: 'ok', detail: `Port ${config.mcpPort} free` });
|
|
180
188
|
} catch {
|
|
181
|
-
portSpinner.warn(`
|
|
189
|
+
portSpinner.warn(`Portal ${config.mcpPort} blocked`);
|
|
182
190
|
checks.push({ name: 'MCP Port', status: 'warn', detail: `Port ${config.mcpPort} busy - change with config` });
|
|
183
191
|
}
|
|
184
192
|
|
|
185
193
|
// Summary
|
|
186
|
-
|
|
187
|
-
console.log(chalk.gray('ā'.repeat(50)));
|
|
194
|
+
header('Diagnostics Report');
|
|
188
195
|
|
|
189
196
|
const okCount = checks.filter(c => c.status === 'ok').length;
|
|
190
197
|
const warnCount = checks.filter(c => c.status === 'warn').length;
|
|
191
198
|
const errorCount = checks.filter(c => c.status === 'error').length;
|
|
192
199
|
|
|
193
200
|
checks.forEach(check => {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
201
|
+
let icon;
|
|
202
|
+
if (check.status === 'ok') icon = icons.success;
|
|
203
|
+
else if (check.status === 'warn') icon = icons.warning;
|
|
204
|
+
else if (check.status === 'error') icon = icons.error;
|
|
205
|
+
else icon = icons.info;
|
|
206
|
+
|
|
207
|
+
statusLine(icon, `${check.name.padEnd(18)} ${chalk.gray(check.detail)}`);
|
|
199
208
|
});
|
|
200
209
|
|
|
201
|
-
console.log(chalk.gray('ā'.repeat(50)));
|
|
210
|
+
console.log(chalk.gray(' ' + 'ā'.repeat(50)));
|
|
202
211
|
console.log(` ${chalk.green(okCount + ' passed')} ${chalk.yellow(warnCount + ' warnings')} ${chalk.red(errorCount + ' errors')}`);
|
|
203
212
|
|
|
204
213
|
if (hasErrors) {
|
|
205
|
-
console.log(chalk.red('\nā
|
|
214
|
+
console.log(chalk.red('\nā System check failed. Fix issues above.\n'));
|
|
206
215
|
process.exit(1);
|
|
207
216
|
} else if (warnCount > 0) {
|
|
208
|
-
console.log(chalk.yellow('\nā ļø
|
|
217
|
+
console.log(chalk.yellow('\nā ļø System operational but has warnings.\n'));
|
|
209
218
|
} else {
|
|
210
|
-
console.log(chalk.green('\nā
All
|
|
219
|
+
console.log(chalk.green('\nā
All systems operational.\n'));
|
|
211
220
|
}
|
|
212
221
|
|
|
213
222
|
// Suggestions
|
|
214
223
|
if (configuredProviders.length === 0) {
|
|
215
|
-
console.log(chalk.cyan('š” To
|
|
224
|
+
console.log(chalk.cyan('š” To configure AI providers, set an API key:'));
|
|
216
225
|
console.log(chalk.gray(' export ANTHROPIC_API_KEY=sk-ant-...'));
|
|
217
226
|
console.log(chalk.gray(' export OPENAI_API_KEY=sk-...'));
|
|
218
227
|
console.log(chalk.gray(' export GEMINI_API_KEY=...\n'));
|
|
219
228
|
}
|
|
220
229
|
|
|
221
230
|
if (foundRequired.length === 0) {
|
|
222
|
-
console.log(chalk.cyan('š” To
|
|
231
|
+
console.log(chalk.cyan('š” To initialize a new project:'));
|
|
223
232
|
console.log(chalk.gray(' ultra-dex init\n'));
|
|
224
233
|
}
|
|
225
234
|
});
|
|
@@ -257,7 +266,7 @@ export function registerConfigCommand(program) {
|
|
|
257
266
|
|
|
258
267
|
console.log(chalk.cyan('\nš Config file locations:'));
|
|
259
268
|
console.log(chalk.gray(' macOS: ~/Library/Application Support/Claude/claude_desktop_config.json'));
|
|
260
|
-
console.log(chalk.gray(' Windows: %APPDATA
|
|
269
|
+
console.log(chalk.gray(' Windows: %APPDATA%\Claude\claude_desktop_config.json'));
|
|
261
270
|
console.log(chalk.gray(' Linux: ~/.config/Claude/claude_desktop_config.json\n'));
|
|
262
271
|
return;
|
|
263
272
|
}
|
|
@@ -313,19 +322,21 @@ export function registerConfigCommand(program) {
|
|
|
313
322
|
// Interactive mode
|
|
314
323
|
console.log(chalk.cyan('\nāļø Ultra-Dex Configuration\n'));
|
|
315
324
|
|
|
316
|
-
const { action } = await inquirer.prompt([
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
325
|
+
const { action } = await inquirer.prompt([
|
|
326
|
+
{
|
|
327
|
+
type: 'list',
|
|
328
|
+
name: 'action',
|
|
329
|
+
message: 'What would you like to do?',
|
|
330
|
+
choices: [
|
|
331
|
+
{ name: 'View current config', value: 'view' },
|
|
332
|
+
{ name: 'Set default AI provider', value: 'provider' },
|
|
333
|
+
{ name: 'Set minimum alignment score', value: 'minScore' },
|
|
334
|
+
{ name: 'Set MCP server port', value: 'mcpPort' },
|
|
335
|
+
{ name: 'Generate MCP config for Claude', value: 'mcp' },
|
|
336
|
+
{ name: 'Create new config file', value: 'init' },
|
|
337
|
+
]
|
|
338
|
+
}
|
|
339
|
+
]);
|
|
329
340
|
|
|
330
341
|
switch (action) {
|
|
331
342
|
case 'view':
|
|
@@ -333,39 +344,45 @@ export function registerConfigCommand(program) {
|
|
|
333
344
|
break;
|
|
334
345
|
|
|
335
346
|
case 'provider':
|
|
336
|
-
const { provider } = await inquirer.prompt([
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
347
|
+
const { provider } = await inquirer.prompt([
|
|
348
|
+
{
|
|
349
|
+
type: 'list',
|
|
350
|
+
name: 'provider',
|
|
351
|
+
message: 'Select default AI provider:',
|
|
352
|
+
choices: ['claude', 'openai', 'gemini'],
|
|
353
|
+
default: config.provider
|
|
354
|
+
}
|
|
355
|
+
]);
|
|
343
356
|
config.provider = provider;
|
|
344
357
|
await saveConfig(config, options.global);
|
|
345
358
|
console.log(chalk.green(`\nā
Default provider set to: ${provider}\n`));
|
|
346
359
|
break;
|
|
347
360
|
|
|
348
361
|
case 'minScore':
|
|
349
|
-
const { minScore } = await inquirer.prompt([
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
362
|
+
const { minScore } = await inquirer.prompt([
|
|
363
|
+
{
|
|
364
|
+
type: 'number',
|
|
365
|
+
name: 'minScore',
|
|
366
|
+
message: 'Minimum alignment score (0-100):',
|
|
367
|
+
default: config.minScore,
|
|
368
|
+
validate: n => n >= 0 && n <= 100 || 'Must be 0-100'
|
|
369
|
+
}
|
|
370
|
+
]);
|
|
356
371
|
config.minScore = minScore;
|
|
357
372
|
await saveConfig(config, options.global);
|
|
358
373
|
console.log(chalk.green(`\nā
Minimum score set to: ${minScore}\n`));
|
|
359
374
|
break;
|
|
360
375
|
|
|
361
376
|
case 'mcpPort':
|
|
362
|
-
const { mcpPort } = await inquirer.prompt([
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
377
|
+
const { mcpPort } = await inquirer.prompt([
|
|
378
|
+
{
|
|
379
|
+
type: 'number',
|
|
380
|
+
name: 'mcpPort',
|
|
381
|
+
message: 'MCP server port:',
|
|
382
|
+
default: config.mcpPort,
|
|
383
|
+
validate: n => n > 0 && n < 65536 || 'Invalid port'
|
|
384
|
+
}
|
|
385
|
+
]);
|
|
369
386
|
config.mcpPort = mcpPort;
|
|
370
387
|
await saveConfig(config, options.global);
|
|
371
388
|
console.log(chalk.green(`\nā
MCP port set to: ${mcpPort}\n`));
|
|
@@ -378,15 +395,17 @@ export function registerConfigCommand(program) {
|
|
|
378
395
|
break;
|
|
379
396
|
|
|
380
397
|
case 'init':
|
|
381
|
-
const { scope } = await inquirer.prompt([
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
398
|
+
const { scope } = await inquirer.prompt([
|
|
399
|
+
{
|
|
400
|
+
type: 'list',
|
|
401
|
+
name: 'scope',
|
|
402
|
+
message: 'Create config in:',
|
|
403
|
+
choices: [
|
|
404
|
+
{ name: 'This project (.ultra-dex.json)', value: 'project' },
|
|
405
|
+
{ name: 'Global (~/.ultra-dex.json)', value: 'global' },
|
|
406
|
+
]
|
|
407
|
+
}
|
|
408
|
+
]);
|
|
390
409
|
const configPath = await saveConfig(DEFAULT_CONFIG, scope === 'global');
|
|
391
410
|
console.log(chalk.green(`\nā
Config created: ${configPath}\n`));
|
|
392
411
|
break;
|
|
@@ -394,4 +413,4 @@ export function registerConfigCommand(program) {
|
|
|
394
413
|
});
|
|
395
414
|
}
|
|
396
415
|
|
|
397
|
-
export default { registerDoctorCommand, registerConfigCommand };
|
|
416
|
+
export default { registerDoctorCommand, registerConfigCommand };
|