ultra-dex 2.2.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +84 -122
  2. package/assets/agents/0-orchestration/orchestrator.md +2 -2
  3. package/assets/agents/00-AGENT_INDEX.md +1 -1
  4. package/assets/docs/LAUNCH-POSTS.md +1 -1
  5. package/assets/docs/QUICK-REFERENCE.md +12 -7
  6. package/assets/docs/ROADMAP.md +5 -5
  7. package/assets/docs/VISION-V2.md +1 -1
  8. package/assets/docs/WORKFLOW-DIAGRAMS.md +1 -1
  9. package/assets/hooks/pre-commit +98 -0
  10. package/assets/saas-plan/04-Imp-Template.md +1 -1
  11. package/assets/templates/README.md +1 -1
  12. package/bin/ultra-dex.js +93 -2096
  13. package/lib/commands/advanced.js +471 -0
  14. package/lib/commands/agent-builder.js +226 -0
  15. package/lib/commands/agents.js +101 -47
  16. package/lib/commands/auto-implement.js +68 -0
  17. package/lib/commands/build.js +73 -187
  18. package/lib/commands/ci-monitor.js +84 -0
  19. package/lib/commands/config.js +207 -0
  20. package/lib/commands/dashboard.js +770 -0
  21. package/lib/commands/diff.js +233 -0
  22. package/lib/commands/doctor.js +397 -0
  23. package/lib/commands/export.js +408 -0
  24. package/lib/commands/fix.js +96 -0
  25. package/lib/commands/generate.js +96 -72
  26. package/lib/commands/hooks.js +251 -76
  27. package/lib/commands/init.js +56 -6
  28. package/lib/commands/memory.js +80 -0
  29. package/lib/commands/plan.js +82 -0
  30. package/lib/commands/review.js +34 -5
  31. package/lib/commands/run.js +233 -0
  32. package/lib/commands/serve.js +188 -40
  33. package/lib/commands/state.js +354 -0
  34. package/lib/commands/swarm.js +284 -0
  35. package/lib/commands/sync.js +94 -0
  36. package/lib/commands/team.js +275 -0
  37. package/lib/commands/upgrade.js +190 -0
  38. package/lib/commands/validate.js +34 -0
  39. package/lib/commands/verify.js +81 -0
  40. package/lib/commands/watch.js +79 -0
  41. package/lib/mcp/graph.js +92 -0
  42. package/lib/mcp/memory.js +95 -0
  43. package/lib/mcp/resources.js +152 -0
  44. package/lib/mcp/server.js +34 -0
  45. package/lib/mcp/tools.js +481 -0
  46. package/lib/mcp/websocket.js +117 -0
  47. package/lib/providers/index.js +49 -4
  48. package/lib/providers/ollama.js +136 -0
  49. package/lib/providers/router.js +63 -0
  50. package/lib/quality/scanner.js +128 -0
  51. package/lib/swarm/coordinator.js +97 -0
  52. package/lib/swarm/index.js +598 -0
  53. package/lib/swarm/protocol.js +677 -0
  54. package/lib/swarm/tiers.js +485 -0
  55. package/lib/templates/context.js +2 -2
  56. package/lib/templates/custom-agent.md +10 -0
  57. package/lib/utils/fallback.js +4 -2
  58. package/lib/utils/files.js +7 -34
  59. package/lib/utils/graph.js +108 -0
  60. package/lib/utils/sync.js +216 -0
  61. package/package.json +22 -13
@@ -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
+ }
@@ -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
- spinner.succeed('Codebase scanned');
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), structure, filesSummary)
219
+ generateReviewPrompt(plan.slice(0, 15000), contextWithGraph, filesSummary)
191
220
  );
192
221
 
193
222
  spinner.succeed('Analysis complete');
@@ -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 };