ultra-dex 2.2.1 ā 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.
- package/README.md +84 -128
- package/assets/agents/00-AGENT_INDEX.md +1 -1
- 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 +95 -4
- package/lib/commands/advanced.js +471 -0
- package/lib/commands/agent-builder.js +226 -0
- package/lib/commands/agents.js +99 -42
- package/lib/commands/auto-implement.js +68 -0
- package/lib/commands/build.js +73 -187
- 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 +397 -0
- package/lib/commands/export.js +408 -0
- package/lib/commands/fix.js +96 -0
- package/lib/commands/generate.js +96 -72
- package/lib/commands/hooks.js +251 -76
- package/lib/commands/init.js +53 -1
- 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/serve.js +177 -146
- package/lib/commands/state.js +354 -0
- package/lib/commands/swarm.js +284 -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/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/custom-agent.md +10 -0
- package/lib/utils/files.js +14 -0
- package/lib/utils/graph.js +108 -0
- 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
|
+
}
|
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');
|
|
@@ -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 };
|