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
package/lib/commands/init.js
CHANGED
|
@@ -7,29 +7,40 @@ import path from 'path';
|
|
|
7
7
|
import { QUICK_START_TEMPLATE } from '../templates/quick-start.js';
|
|
8
8
|
import { CONTEXT_TEMPLATE } from '../templates/context.js';
|
|
9
9
|
import { validateProjectName, validateSafePath } from '../utils/validation.js';
|
|
10
|
-
import { ASSETS_ROOT, ROOT_FALLBACK } from '../config/paths.js';
|
|
11
|
-
import { githubBlobUrl
|
|
10
|
+
import { ASSETS_ROOT, ROOT_FALLBACK, LIVE_TEMPLATES_ROOT } from '../config/paths.js';
|
|
11
|
+
import { githubBlobUrl } from '../config/urls.js';
|
|
12
12
|
import { copyWithFallback, listWithFallback, readWithFallback } from '../utils/fallback.js';
|
|
13
|
+
import { copyDirectory, pathExists } from '../utils/files.js';
|
|
14
|
+
import { getRandomMessage } from '../utils/messages.js';
|
|
15
|
+
|
|
16
|
+
const LIVE_STACKS = {
|
|
17
|
+
'next15-prisma-clerk': 'Next.js 15 + Prisma + Clerk',
|
|
18
|
+
'remix-supabase': 'Remix + Supabase',
|
|
19
|
+
'sveltekit-drizzle': 'SvelteKit + Drizzle',
|
|
20
|
+
};
|
|
13
21
|
|
|
14
22
|
export function registerInitCommand(program) {
|
|
15
23
|
program
|
|
16
24
|
.command('init')
|
|
17
|
-
.description('Initialize a new Ultra-Dex
|
|
25
|
+
.description('Initialize a new Ultra-Dex Project')
|
|
18
26
|
.option('-n, --name <name>', 'Project name')
|
|
19
27
|
.option('-d, --dir <directory>', 'Output directory', '.')
|
|
20
28
|
.option('--preview', 'Preview files without creating them')
|
|
29
|
+
.option('--live', 'Generate a runnable scaffold')
|
|
30
|
+
.option('--stack <preset>', 'Preset: next15-prisma-clerk, remix-supabase, sveltekit-drizzle')
|
|
21
31
|
.action(async (options) => {
|
|
22
32
|
console.log(chalk.cyan(program.banner));
|
|
23
|
-
console.log(chalk.bold('\
|
|
33
|
+
console.log(chalk.hex('#8b5cf6').bold('\nā” INITIALIZING PROJECT PROTOCOL...\n'));
|
|
34
|
+
console.log(chalk.italic(chalk.gray(`"${getRandomMessage('start')}"`)));
|
|
35
|
+
console.log('');
|
|
24
36
|
|
|
25
37
|
if (options.preview) {
|
|
26
|
-
console.log('\nš Files that would be created:\n');
|
|
38
|
+
console.log('\nš Project Preview (Files that would be created):\n');
|
|
27
39
|
console.log(' QUICK-START.md');
|
|
28
40
|
console.log(' CONTEXT.md');
|
|
29
41
|
console.log(' IMPLEMENTATION-PLAN.md');
|
|
30
42
|
console.log(' docs/CHECKLIST.md');
|
|
31
43
|
console.log(' docs/AI-PROMPTS.md');
|
|
32
|
-
console.log('\nRun without --preview to create files.');
|
|
33
44
|
return;
|
|
34
45
|
}
|
|
35
46
|
|
|
@@ -39,107 +50,150 @@ export function registerInitCommand(program) {
|
|
|
39
50
|
process.exit(1);
|
|
40
51
|
}
|
|
41
52
|
|
|
53
|
+
if (options.live) {
|
|
54
|
+
const preset = options.stack || 'next15-prisma-clerk';
|
|
55
|
+
if (!LIVE_STACKS[preset]) {
|
|
56
|
+
console.log(chalk.red(`Unknown preset: ${preset}`));
|
|
57
|
+
console.log(chalk.gray(`Available presets: ${Object.keys(LIVE_STACKS).join(', ')}`));
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const outputDir = path.resolve(options.dir);
|
|
62
|
+
if (await pathExists(outputDir, 'dir')) {
|
|
63
|
+
const existing = await fs.readdir(outputDir);
|
|
64
|
+
if (existing.length > 0) {
|
|
65
|
+
console.log(chalk.red('Target directory is not empty. Execution halted to prevent data loss.'));
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const liveSourcePath = path.join(LIVE_TEMPLATES_ROOT, preset);
|
|
71
|
+
const fallbackLivePath = path.join(ROOT_FALLBACK, 'cli', 'assets', 'live-templates', preset);
|
|
72
|
+
let sourcePath = liveSourcePath;
|
|
73
|
+
try {
|
|
74
|
+
await fs.access(liveSourcePath);
|
|
75
|
+
} catch {
|
|
76
|
+
sourcePath = fallbackLivePath;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const spinner = ora(`Generating ${LIVE_STACKS[preset]} scaffold...`).start();
|
|
80
|
+
try {
|
|
81
|
+
await copyDirectory(sourcePath, outputDir);
|
|
82
|
+
spinner.succeed(chalk.green('Project scaffold generated successfully!'));
|
|
83
|
+
console.log(chalk.gray(`\nPreset: ${preset}`));
|
|
84
|
+
console.log(chalk.gray(`Next steps:`));
|
|
85
|
+
console.log(chalk.cyan(` 1. cd ${outputDir}`));
|
|
86
|
+
console.log(chalk.cyan(' 2. npm install'));
|
|
87
|
+
console.log(chalk.cyan(' 3. npm run dev\n'));
|
|
88
|
+
} catch (error) {
|
|
89
|
+
spinner.fail(chalk.red('Failed to generate project scaffold'));
|
|
90
|
+
console.error(`[init] ${error?.message ?? error}`);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
42
96
|
const answers = await inquirer.prompt([
|
|
43
97
|
{
|
|
44
98
|
type: 'input',
|
|
45
99
|
name: 'projectName',
|
|
46
|
-
message: 'What
|
|
100
|
+
message: 'What is the name of this project?',
|
|
47
101
|
default: options.name || 'my-saas',
|
|
48
102
|
validate: validateProjectName,
|
|
49
103
|
},
|
|
50
104
|
{
|
|
51
105
|
type: 'input',
|
|
52
106
|
name: 'ideaWhat',
|
|
53
|
-
message: 'What are you building?
|
|
54
|
-
validate: (input) => input.length > 0 || 'Please describe your
|
|
107
|
+
message: 'Define the core purpose (What are you building?):',
|
|
108
|
+
validate: (input) => input.length > 0 || 'Please describe your project',
|
|
55
109
|
},
|
|
56
110
|
{
|
|
57
111
|
type: 'input',
|
|
58
112
|
name: 'ideaFor',
|
|
59
|
-
message: 'Who
|
|
60
|
-
validate: (input) => input.length > 0 || 'Please specify your target
|
|
113
|
+
message: 'Target audience (Who are the users)?',
|
|
114
|
+
validate: (input) => input.length > 0 || 'Please specify your target audience',
|
|
61
115
|
},
|
|
62
116
|
{
|
|
63
117
|
type: 'input',
|
|
64
118
|
name: 'problem1',
|
|
65
|
-
message: '
|
|
119
|
+
message: 'Primary problem to solve:',
|
|
66
120
|
default: '',
|
|
67
121
|
},
|
|
68
122
|
{
|
|
69
123
|
type: 'input',
|
|
70
124
|
name: 'problem2',
|
|
71
|
-
message: '
|
|
125
|
+
message: 'Secondary problem to solve:',
|
|
72
126
|
default: '',
|
|
73
127
|
},
|
|
74
128
|
{
|
|
75
129
|
type: 'input',
|
|
76
130
|
name: 'problem3',
|
|
77
|
-
message: '
|
|
131
|
+
message: 'Tertiary problem to solve:',
|
|
78
132
|
default: '',
|
|
79
133
|
},
|
|
80
134
|
{
|
|
81
135
|
type: 'input',
|
|
82
136
|
name: 'feature1',
|
|
83
|
-
message: '
|
|
137
|
+
message: 'Core feature:',
|
|
84
138
|
default: '',
|
|
85
139
|
},
|
|
86
140
|
{
|
|
87
141
|
type: 'list',
|
|
88
142
|
name: 'frontend',
|
|
89
|
-
message: 'Frontend
|
|
143
|
+
message: 'Frontend Technology Stack:',
|
|
90
144
|
choices: ['Next.js', 'Remix', 'SvelteKit', 'Nuxt', 'Other'],
|
|
91
145
|
},
|
|
92
146
|
{
|
|
93
147
|
type: 'list',
|
|
94
148
|
name: 'database',
|
|
95
|
-
message: 'Database:',
|
|
149
|
+
message: 'Database Infrastructure:',
|
|
96
150
|
choices: ['PostgreSQL', 'Supabase', 'MongoDB', 'PlanetScale', 'Other'],
|
|
97
151
|
},
|
|
98
152
|
{
|
|
99
153
|
type: 'list',
|
|
100
154
|
name: 'auth',
|
|
101
|
-
message: 'Authentication:',
|
|
155
|
+
message: 'Authentication Provider:',
|
|
102
156
|
choices: ['NextAuth', 'Clerk', 'Auth0', 'Supabase Auth', 'Other'],
|
|
103
157
|
},
|
|
104
158
|
{
|
|
105
159
|
type: 'list',
|
|
106
160
|
name: 'payments',
|
|
107
|
-
message: '
|
|
161
|
+
message: 'Payment Processor:',
|
|
108
162
|
choices: ['Stripe', 'Lemonsqueezy', 'Paddle', 'None (free)', 'Other'],
|
|
109
163
|
},
|
|
110
164
|
{
|
|
111
165
|
type: 'list',
|
|
112
166
|
name: 'hosting',
|
|
113
|
-
message: '
|
|
167
|
+
message: 'Deployment Platform:',
|
|
114
168
|
choices: ['Vercel', 'Railway', 'Fly.io', 'AWS', 'Other'],
|
|
115
169
|
},
|
|
116
170
|
{
|
|
117
171
|
type: 'confirm',
|
|
118
172
|
name: 'includeCursorRules',
|
|
119
|
-
message: '
|
|
173
|
+
message: 'Install IDE intelligence protocols? (Cursor/Copilot Rules)',
|
|
120
174
|
default: true,
|
|
121
175
|
},
|
|
122
176
|
{
|
|
123
177
|
type: 'confirm',
|
|
124
178
|
name: 'includeFullTemplate',
|
|
125
|
-
message: '
|
|
179
|
+
message: 'Include full 34-section project template?',
|
|
126
180
|
default: false,
|
|
127
181
|
},
|
|
128
182
|
{
|
|
129
183
|
type: 'confirm',
|
|
130
184
|
name: 'includeDocs',
|
|
131
|
-
message: '
|
|
185
|
+
message: 'Include project documentation standards?',
|
|
132
186
|
default: true,
|
|
133
187
|
},
|
|
134
188
|
{
|
|
135
189
|
type: 'confirm',
|
|
136
190
|
name: 'includeAgents',
|
|
137
|
-
message: '
|
|
191
|
+
message: 'Configure AI agent orchestration?',
|
|
138
192
|
default: true,
|
|
139
193
|
},
|
|
140
194
|
]);
|
|
141
195
|
|
|
142
|
-
const spinner = ora('
|
|
196
|
+
const spinner = ora(getRandomMessage('loading')).start();
|
|
143
197
|
|
|
144
198
|
try {
|
|
145
199
|
const outputDir = path.resolve(options.dir, answers.projectName);
|
|
@@ -187,15 +241,8 @@ ${answers.ideaWhat} for ${answers.ideaFor}.
|
|
|
187
241
|
## Next Steps
|
|
188
242
|
|
|
189
243
|
1. Open QUICK-START.md and complete the remaining sections
|
|
190
|
-
2.
|
|
191
|
-
3.
|
|
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')})
|
|
244
|
+
2. Customize the implementation plan based on your requirements
|
|
245
|
+
3. Start the agent orchestration to begin development
|
|
199
246
|
`;
|
|
200
247
|
|
|
201
248
|
await fs.writeFile(path.join(outputDir, 'IMPLEMENTATION-PLAN.md'), planContent);
|
|
@@ -222,11 +269,11 @@ ${answers.ideaWhat} for ${answers.ideaFor}.
|
|
|
222
269
|
await fs.mkdir(dotGithub, { recursive: true });
|
|
223
270
|
await fs.writeFile(path.join(dotGithub, 'copilot-instructions.md'), coreContent);
|
|
224
271
|
} catch {
|
|
225
|
-
// Core rule not available
|
|
272
|
+
// Core rule not available
|
|
226
273
|
}
|
|
227
274
|
} catch {
|
|
228
|
-
console.log(chalk.red('\n
|
|
229
|
-
console.log(chalk.cyan('
|
|
275
|
+
console.log(chalk.red('\n ā IDE intelligence protocols not found.'));
|
|
276
|
+
console.log(chalk.cyan(' Run: ultra-dex fetch --rules'));
|
|
230
277
|
}
|
|
231
278
|
}
|
|
232
279
|
|
|
@@ -236,8 +283,8 @@ ${answers.ideaWhat} for ${answers.ideaFor}.
|
|
|
236
283
|
try {
|
|
237
284
|
await copyWithFallback(templatePath, fallbackTemplatePath, path.join(outputDir, 'docs', 'MASTER-PLAN.md'));
|
|
238
285
|
} catch {
|
|
239
|
-
console.log(chalk.red('\n
|
|
240
|
-
console.log(chalk.cyan('
|
|
286
|
+
console.log(chalk.red('\n ā Project template not found.'));
|
|
287
|
+
console.log(chalk.cyan(' Run: ultra-dex fetch --docs'));
|
|
241
288
|
}
|
|
242
289
|
}
|
|
243
290
|
|
|
@@ -250,8 +297,8 @@ ${answers.ideaWhat} for ${answers.ideaFor}.
|
|
|
250
297
|
await copyWithFallback(verificationPath, fallbackVerificationPath, path.join(outputDir, 'docs', 'CHECKLIST.md'));
|
|
251
298
|
await copyWithFallback(agentPath, fallbackAgentPath, path.join(outputDir, 'docs', 'AI-PROMPTS.md'));
|
|
252
299
|
} catch {
|
|
253
|
-
console.log(chalk.red('\n
|
|
254
|
-
console.log(chalk.cyan('
|
|
300
|
+
console.log(chalk.red('\n ā Documentation standards not found.'));
|
|
301
|
+
console.log(chalk.cyan(' Run: ultra-dex fetch --docs'));
|
|
255
302
|
}
|
|
256
303
|
}
|
|
257
304
|
|
|
@@ -293,43 +340,44 @@ ${answers.ideaWhat} for ${answers.ideaFor}.
|
|
|
293
340
|
path.join(agentsDir, 'README.md')
|
|
294
341
|
);
|
|
295
342
|
} catch {
|
|
296
|
-
console.log(chalk.red('\n
|
|
297
|
-
console.log(chalk.cyan('
|
|
343
|
+
console.log(chalk.red('\n ā Agent orchestration assets not found.'));
|
|
344
|
+
console.log(chalk.cyan(' Run: ultra-dex fetch --agents'));
|
|
298
345
|
}
|
|
299
346
|
}
|
|
300
347
|
|
|
301
|
-
spinner.succeed(chalk.green('Project
|
|
348
|
+
spinner.succeed(chalk.green('Project initialized successfully!'));
|
|
302
349
|
|
|
303
|
-
console.log('\n' + chalk.bold('
|
|
350
|
+
console.log('\n' + chalk.bold('Artifacts created:'));
|
|
304
351
|
console.log(chalk.gray(` ${outputDir}/`));
|
|
305
352
|
console.log(chalk.gray(' āāā QUICK-START.md'));
|
|
306
353
|
console.log(chalk.gray(' āāā CONTEXT.md'));
|
|
307
354
|
console.log(chalk.gray(' āāā IMPLEMENTATION-PLAN.md'));
|
|
308
355
|
if (answers.includeFullTemplate) {
|
|
309
|
-
console.log(chalk.gray(' āāā docs/MASTER-PLAN.md
|
|
356
|
+
console.log(chalk.gray(' āāā docs/MASTER-PLAN.md'));
|
|
310
357
|
}
|
|
311
358
|
if (answers.includeDocs) {
|
|
312
359
|
console.log(chalk.gray(' āāā docs/CHECKLIST.md'));
|
|
313
360
|
console.log(chalk.gray(' āāā docs/AI-PROMPTS.md'));
|
|
314
361
|
}
|
|
315
362
|
if (answers.includeCursorRules) {
|
|
316
|
-
console.log(chalk.gray(' āāā .cursor/rules/
|
|
363
|
+
console.log(chalk.gray(' āāā .cursor/rules/'));
|
|
317
364
|
}
|
|
318
365
|
if (answers.includeAgents) {
|
|
319
|
-
console.log(chalk.gray(' āāā .agents/
|
|
366
|
+
console.log(chalk.gray(' āāā .agents/'));
|
|
320
367
|
}
|
|
321
368
|
|
|
322
369
|
console.log('\n' + chalk.bold('Next steps:'));
|
|
323
370
|
console.log(chalk.cyan(` 1. cd ${answers.projectName}`));
|
|
324
|
-
console.log(chalk.cyan(' 2. Open QUICK-START.md
|
|
325
|
-
console.log(chalk.cyan(' 3.
|
|
371
|
+
console.log(chalk.cyan(' 2. Open QUICK-START.md'));
|
|
372
|
+
console.log(chalk.cyan(' 3. ultra-dex swarm "Analyze requirements"'));
|
|
326
373
|
|
|
327
|
-
console.log('\n' + chalk.
|
|
328
|
-
console.log(
|
|
374
|
+
console.log('\n' + chalk.hex('#8b5cf6').bold(' ā SYSTEM READY.'));
|
|
375
|
+
console.log('');
|
|
376
|
+
|
|
329
377
|
} catch (error) {
|
|
330
|
-
spinner.fail(chalk.red('Failed to
|
|
378
|
+
spinner.fail(chalk.red('Failed to initialize project'));
|
|
331
379
|
console.error(`[init] ${error?.message ?? error}`);
|
|
332
380
|
process.exit(1);
|
|
333
381
|
}
|
|
334
382
|
});
|
|
335
|
-
}
|
|
383
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ultra-dex memory command
|
|
3
|
+
* Manage persistent memory for AI agents
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { ultraMemory } from '../mcp/memory.js';
|
|
8
|
+
|
|
9
|
+
export function registerMemoryCommand(program) {
|
|
10
|
+
const memory = program
|
|
11
|
+
.command('memory')
|
|
12
|
+
.description('Manage persistent agent memory');
|
|
13
|
+
|
|
14
|
+
memory
|
|
15
|
+
.command('list')
|
|
16
|
+
.description('List all remembered facts')
|
|
17
|
+
.option('--json', 'Output as JSON')
|
|
18
|
+
.action(async (options) => {
|
|
19
|
+
const items = await ultraMemory.getAll();
|
|
20
|
+
|
|
21
|
+
if (options.json) {
|
|
22
|
+
console.log(JSON.stringify(items, null, 2));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
console.log(chalk.cyan.bold('\nš§ Ultra-Dex Persistent Memory\n'));
|
|
27
|
+
|
|
28
|
+
if (items.length === 0) {
|
|
29
|
+
console.log(chalk.gray(' Memory is empty.'));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
items.forEach((item, i) => {
|
|
34
|
+
console.log(chalk.white(`${i + 1}. [${new Date(item.timestamp).toLocaleDateString()}] (${item.source})`));
|
|
35
|
+
console.log(chalk.gray(` ${item.text}`));
|
|
36
|
+
if (item.tags && item.tags.length > 0) {
|
|
37
|
+
console.log(chalk.blue(` Tags: ${item.tags.join(', ')}`));
|
|
38
|
+
}
|
|
39
|
+
console.log();
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
memory
|
|
44
|
+
.command('add <text>')
|
|
45
|
+
.description('Add a fact to memory')
|
|
46
|
+
.option('-t, --tags <tags>', 'Comma-separated tags')
|
|
47
|
+
.action(async (text, options) => {
|
|
48
|
+
const tags = options.tags ? options.tags.split(',').map(t => t.trim()) : [];
|
|
49
|
+
await ultraMemory.remember(text, tags, 'manual');
|
|
50
|
+
console.log(chalk.green('ā
Fact remembered.'));
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
memory
|
|
54
|
+
.command('search <query>')
|
|
55
|
+
.description('Search memory')
|
|
56
|
+
.action(async (query) => {
|
|
57
|
+
const results = await ultraMemory.search(query);
|
|
58
|
+
console.log(chalk.cyan.bold(`\nš Search Results for "${query}":\n`));
|
|
59
|
+
|
|
60
|
+
if (results.length === 0) {
|
|
61
|
+
console.log(chalk.gray(' No matches found.'));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
results.forEach((item, i) => {
|
|
66
|
+
console.log(chalk.white(`${i + 1}. [${new Date(item.timestamp).toLocaleDateString()}]`));
|
|
67
|
+
console.log(chalk.gray(` ${item.text}`));
|
|
68
|
+
console.log();
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
memory
|
|
73
|
+
.command('clear')
|
|
74
|
+
.description('Clear all memory')
|
|
75
|
+
.option('--before <date>', 'Clear before date (ISO)')
|
|
76
|
+
.action(async (options) => {
|
|
77
|
+
await ultraMemory.clear(options.before);
|
|
78
|
+
console.log(chalk.green('ā
Memory cleared.'));
|
|
79
|
+
});
|
|
80
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
|
|
5
|
+
export async function loadState() {
|
|
6
|
+
try {
|
|
7
|
+
const content = await fs.readFile(path.resolve(process.cwd(), '.ultra/state.json'), 'utf8');
|
|
8
|
+
return JSON.parse(content);
|
|
9
|
+
} catch (error) {
|
|
10
|
+
console.error(chalk.red('Failed to load .ultra/state.json. Is the project initialized?'));
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function saveState(state) {
|
|
16
|
+
try {
|
|
17
|
+
await fs.mkdir(path.resolve(process.cwd(), '.ultra'), { recursive: true });
|
|
18
|
+
await fs.writeFile(
|
|
19
|
+
path.resolve(process.cwd(), '.ultra/state.json'),
|
|
20
|
+
JSON.stringify(state, null, 2)
|
|
21
|
+
);
|
|
22
|
+
return true;
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error(chalk.red('Failed to save state:'), error);
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function generateMarkdown(state) {
|
|
30
|
+
const { project, phases } = state;
|
|
31
|
+
let md = `# ${project.name} - Implementation Plan\n\n`;
|
|
32
|
+
md += `> **Generated by Ultra-Dex Core**\n`;
|
|
33
|
+
md += `> Version: ${project.version}\n`;
|
|
34
|
+
md += `> Mode: ${project.mode}\n\n`;
|
|
35
|
+
|
|
36
|
+
md += `## š Execution Phases\n\n`;
|
|
37
|
+
|
|
38
|
+
phases.forEach(phase => {
|
|
39
|
+
const statusIcon = phase.status === 'completed' ? 'ā
' : phase.status === 'in_progress' ? 'š' : 'ā³';
|
|
40
|
+
md += `### ${statusIcon} ${phase.name}\n\n`;
|
|
41
|
+
|
|
42
|
+
if (phase.steps && phase.steps.length > 0) {
|
|
43
|
+
phase.steps.forEach(step => {
|
|
44
|
+
const stepIcon = step.status === 'completed' ? '- [x]' : '- [ ]';
|
|
45
|
+
md += `${stepIcon} **${step.id}**: ${step.task}\n`;
|
|
46
|
+
});
|
|
47
|
+
} else {
|
|
48
|
+
md += `_No steps defined for this phase._\n`;
|
|
49
|
+
}
|
|
50
|
+
md += `\n`;
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
md += `## š¤ Agent Registry\n\n`;
|
|
54
|
+
if (state.agents && state.agents.registry) {
|
|
55
|
+
state.agents.registry.forEach(agent => {
|
|
56
|
+
const active = state.agents.active.includes(agent) ? '(Active)' : '';
|
|
57
|
+
md += `- ${agent} ${active}\n`;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
md += `\n---\n`;
|
|
62
|
+
md += `*This file is strictly read-only. Edit .ultra/state.json to update.*`;
|
|
63
|
+
|
|
64
|
+
return md;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function registerPlanCommand(program) {
|
|
68
|
+
program
|
|
69
|
+
.command('plan')
|
|
70
|
+
.description('Generate IMPLEMENTATION-PLAN.md from state.json')
|
|
71
|
+
.action(async () => {
|
|
72
|
+
const state = await loadState();
|
|
73
|
+
if (!state) return;
|
|
74
|
+
|
|
75
|
+
console.log(chalk.blue(`Generating plan for ${state.project.name}...`));
|
|
76
|
+
|
|
77
|
+
const markdown = generateMarkdown(state);
|
|
78
|
+
await fs.writeFile(path.resolve(process.cwd(), 'IMPLEMENTATION-PLAN.md'), markdown);
|
|
79
|
+
|
|
80
|
+
console.log(chalk.green(`ā
IMPLEMENTATION-PLAN.md generated successfully.`));
|
|
81
|
+
});
|
|
82
|
+
}
|
package/lib/commands/review.js
CHANGED
|
@@ -10,6 +10,8 @@ import fs from 'fs/promises';
|
|
|
10
10
|
import path from 'path';
|
|
11
11
|
import { createProvider, getDefaultProvider, checkConfiguredProviders } from '../providers/index.js';
|
|
12
12
|
import { SYSTEM_PROMPT, generateReviewPrompt } from '../templates/prompts/review-code.js';
|
|
13
|
+
import { validateSafePath } from '../utils/validation.js';
|
|
14
|
+
import { buildGraph, queryGraph, getImpactAnalysis } from '../utils/graph.js'; // Import CPG utils
|
|
13
15
|
|
|
14
16
|
// File patterns to scan
|
|
15
17
|
const CODE_PATTERNS = {
|
|
@@ -106,6 +108,12 @@ export function registerReviewCommand(program) {
|
|
|
106
108
|
.action(async (options) => {
|
|
107
109
|
console.log(chalk.cyan('\nš Ultra-Dex Code Review\n'));
|
|
108
110
|
|
|
111
|
+
const dirValidation = validateSafePath(options.dir, 'Review directory');
|
|
112
|
+
if (dirValidation !== true) {
|
|
113
|
+
console.log(chalk.red(dirValidation));
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
|
|
109
117
|
const reviewDir = path.resolve(options.dir);
|
|
110
118
|
|
|
111
119
|
// Check for plan
|
|
@@ -120,11 +128,29 @@ export function registerReviewCommand(program) {
|
|
|
120
128
|
return;
|
|
121
129
|
}
|
|
122
130
|
|
|
123
|
-
// Get directory structure
|
|
124
|
-
const spinner = ora('Scanning codebase...').start();
|
|
131
|
+
// Get directory structure & Build Graph
|
|
132
|
+
const spinner = ora('Scanning codebase & Building Graph...').start();
|
|
125
133
|
const structure = await getDirectoryStructure(reviewDir);
|
|
126
134
|
const keyFiles = await findKeyFiles(reviewDir);
|
|
127
|
-
|
|
135
|
+
|
|
136
|
+
// GOD MODE: Build CPG
|
|
137
|
+
let graphSummary = "Graph Not Available";
|
|
138
|
+
try {
|
|
139
|
+
const graph = await buildGraph();
|
|
140
|
+
graphSummary = `
|
|
141
|
+
Code Property Graph Stats:
|
|
142
|
+
- Files: ${graph.nodes.filter(n => n.type === 'file').length}
|
|
143
|
+
- Functions: ${graph.nodes.filter(n => n.type === 'function').length}
|
|
144
|
+
- Dependencies (Edges): ${graph.edges.length}
|
|
145
|
+
|
|
146
|
+
Top Dependencies:
|
|
147
|
+
${graph.edges.slice(0, 10).map(e => `- ${e.source} -> ${e.target}`).join('\n')}
|
|
148
|
+
`;
|
|
149
|
+
} catch (e) {
|
|
150
|
+
// Fallback if graph fails
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
spinner.succeed('Codebase scanned & Graph built');
|
|
128
154
|
|
|
129
155
|
if (options.quick) {
|
|
130
156
|
// Quick review - just check structure
|
|
@@ -181,13 +207,16 @@ export function registerReviewCommand(program) {
|
|
|
181
207
|
|
|
182
208
|
// Build file summary
|
|
183
209
|
const filesSummary = keyFiles.map(f => `### ${f.path}\n\`\`\`\n${f.content}\n\`\`\``).join('\n\n');
|
|
210
|
+
|
|
211
|
+
// Inject Graph Summary into context
|
|
212
|
+
const contextWithGraph = `${structure}\n\n## ARCHITECTURAL GRAPH (TRUTH)\n${graphSummary}`;
|
|
184
213
|
|
|
185
|
-
spinner.start('Analyzing code against plan...');
|
|
214
|
+
spinner.start('Analyzing code & graph against plan...');
|
|
186
215
|
|
|
187
216
|
try {
|
|
188
217
|
const result = await provider.generate(
|
|
189
218
|
SYSTEM_PROMPT,
|
|
190
|
-
generateReviewPrompt(plan.slice(0, 15000),
|
|
219
|
+
generateReviewPrompt(plan.slice(0, 15000), contextWithGraph, filesSummary)
|
|
191
220
|
);
|
|
192
221
|
|
|
193
222
|
spinner.succeed('Analysis complete');
|