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,598 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ultra-Dex Swarm Coordination System v3.0
|
|
3
|
+
* Main entry point for agent orchestration.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import ora from 'ora';
|
|
8
|
+
import fs from 'fs/promises';
|
|
9
|
+
import {
|
|
10
|
+
AgentMessage,
|
|
11
|
+
HandoffPayload,
|
|
12
|
+
ExecutionTrace,
|
|
13
|
+
AgentSchemas,
|
|
14
|
+
createMessage,
|
|
15
|
+
createHandover
|
|
16
|
+
} from './protocol.js';
|
|
17
|
+
import {
|
|
18
|
+
TIERS,
|
|
19
|
+
AGENTS,
|
|
20
|
+
TIER_FLOW,
|
|
21
|
+
getAgent,
|
|
22
|
+
getAgentsByTier,
|
|
23
|
+
getTier,
|
|
24
|
+
getExecutionOrder,
|
|
25
|
+
findParallelGroups,
|
|
26
|
+
validatePipeline,
|
|
27
|
+
getAgentDependencies
|
|
28
|
+
} from './tiers.js';
|
|
29
|
+
|
|
30
|
+
// Re-export from protocol for convenience
|
|
31
|
+
export { AgentMessage, HandoffPayload, ExecutionTrace, AgentSchemas };
|
|
32
|
+
export { createMessage, createHandover };
|
|
33
|
+
|
|
34
|
+
// Re-export from tiers
|
|
35
|
+
export { TIERS, AGENTS, TIER_FLOW, getAgent, getAgentsByTier, getTier };
|
|
36
|
+
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// SWARM COORDINATOR CLASS
|
|
39
|
+
// ============================================================================
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Main coordinator class for managing agent swarms.
|
|
43
|
+
*/
|
|
44
|
+
export class SwarmCoordinator {
|
|
45
|
+
constructor(provider, options = {}) {
|
|
46
|
+
this.provider = provider;
|
|
47
|
+
this.agents = new Map();
|
|
48
|
+
this.history = [];
|
|
49
|
+
this.traces = new Map();
|
|
50
|
+
this.currentTrace = null;
|
|
51
|
+
this.options = {
|
|
52
|
+
verbose: options.verbose || false,
|
|
53
|
+
saveArtifacts: options.saveArtifacts !== false,
|
|
54
|
+
artifactDir: options.artifactDir || '.ultra-dex/swarm',
|
|
55
|
+
maxRetries: options.maxRetries || 3,
|
|
56
|
+
enableRollback: options.enableRollback !== false
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Register default agents from tiers
|
|
60
|
+
this._registerDefaultAgents();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Register default agents from the tier system.
|
|
65
|
+
*/
|
|
66
|
+
_registerDefaultAgents() {
|
|
67
|
+
for (const [name, config] of Object.entries(AGENTS)) {
|
|
68
|
+
this.agents.set(name, {
|
|
69
|
+
...config,
|
|
70
|
+
handler: null // Will be set when agent is loaded
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ==========================================================================
|
|
76
|
+
// AGENT MANAGEMENT
|
|
77
|
+
// ==========================================================================
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Add or update an agent in the swarm.
|
|
81
|
+
*/
|
|
82
|
+
addAgent(name, config) {
|
|
83
|
+
const normalized = name.toLowerCase().replace('@', '');
|
|
84
|
+
const existing = this.agents.get(normalized) || {};
|
|
85
|
+
|
|
86
|
+
this.agents.set(normalized, {
|
|
87
|
+
...existing,
|
|
88
|
+
...config,
|
|
89
|
+
name: config.name || existing.name || normalized
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
if (this.options.verbose) {
|
|
93
|
+
console.log(chalk.gray(`[Swarm] Registered agent: ${normalized}`));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return this;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Get an agent by name.
|
|
101
|
+
*/
|
|
102
|
+
getAgent(name) {
|
|
103
|
+
const normalized = name.toLowerCase().replace('@', '');
|
|
104
|
+
return this.agents.get(normalized) || null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* List all registered agents.
|
|
109
|
+
*/
|
|
110
|
+
listAgents() {
|
|
111
|
+
return Array.from(this.agents.entries()).map(([name, config]) => ({
|
|
112
|
+
name,
|
|
113
|
+
...config
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Check if an agent is registered.
|
|
119
|
+
*/
|
|
120
|
+
hasAgent(name) {
|
|
121
|
+
const normalized = name.toLowerCase().replace('@', '');
|
|
122
|
+
return this.agents.has(normalized);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ==========================================================================
|
|
126
|
+
// PIPELINE EXECUTION
|
|
127
|
+
// ==========================================================================
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Run a pipeline of agent tasks.
|
|
131
|
+
* @param {Object} options - Pipeline configuration
|
|
132
|
+
* @param {string} options.goal - The goal of the pipeline
|
|
133
|
+
* @param {Array} options.steps - Array of step objects { agent, task, context? }
|
|
134
|
+
* @param {boolean} options.parallel - Enable parallel execution where possible
|
|
135
|
+
*/
|
|
136
|
+
async runPipeline(options) {
|
|
137
|
+
const { goal, steps, parallel = false } = options;
|
|
138
|
+
|
|
139
|
+
// Create execution trace
|
|
140
|
+
const trace = new ExecutionTrace(null, goal);
|
|
141
|
+
this.currentTrace = trace;
|
|
142
|
+
this.traces.set(trace.taskId, trace);
|
|
143
|
+
|
|
144
|
+
// Add steps to trace
|
|
145
|
+
steps.forEach((step, index) => {
|
|
146
|
+
trace.addStep(index + 1, step.agent, step.task, step.dependencies || []);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Validate pipeline
|
|
150
|
+
const validation = validatePipeline(steps);
|
|
151
|
+
if (!validation.valid) {
|
|
152
|
+
console.log(chalk.red('\n❌ Pipeline validation failed:'));
|
|
153
|
+
validation.errors.forEach(err => {
|
|
154
|
+
console.log(chalk.red(` Step ${err.step} (${err.agent}): ${err.error}`));
|
|
155
|
+
});
|
|
156
|
+
trace.status = 'failed';
|
|
157
|
+
return trace;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Start execution
|
|
161
|
+
trace.start();
|
|
162
|
+
const spinner = ora(`🐝 Swarm: Executing pipeline for "${goal}"`).start();
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
if (parallel) {
|
|
166
|
+
await this._executeParallel(steps, trace, spinner);
|
|
167
|
+
} else {
|
|
168
|
+
await this._executeSequential(steps, trace, spinner);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
trace.complete(true);
|
|
172
|
+
spinner.succeed(chalk.green(`Pipeline completed: ${goal}`));
|
|
173
|
+
} catch (error) {
|
|
174
|
+
trace.complete(false);
|
|
175
|
+
spinner.fail(chalk.red(`Pipeline failed: ${error.message}`));
|
|
176
|
+
|
|
177
|
+
if (this.options.enableRollback) {
|
|
178
|
+
await this._attemptRollback(trace);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Save trace
|
|
183
|
+
await this._saveTrace(trace);
|
|
184
|
+
this.history.push(trace);
|
|
185
|
+
|
|
186
|
+
return trace;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Execute steps sequentially.
|
|
191
|
+
*/
|
|
192
|
+
async _executeSequential(steps, trace, spinner) {
|
|
193
|
+
let previousResult = null;
|
|
194
|
+
|
|
195
|
+
for (const step of steps) {
|
|
196
|
+
const stepNum = steps.indexOf(step) + 1;
|
|
197
|
+
spinner.text = `Step ${stepNum}/${steps.length}: [${step.agent}] ${step.task}`;
|
|
198
|
+
trace.startStep(stepNum);
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
// Create checkpoint before execution
|
|
202
|
+
if (this.options.enableRollback) {
|
|
203
|
+
trace.createCheckpoint(`before-step-${stepNum}`, { previousResult });
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Execute step
|
|
207
|
+
const result = await this._executeStep(step, previousResult, trace);
|
|
208
|
+
trace.recordResult(step.agent, result, true);
|
|
209
|
+
previousResult = result;
|
|
210
|
+
|
|
211
|
+
// Create handoff for next step
|
|
212
|
+
if (stepNum < steps.length) {
|
|
213
|
+
const nextStep = steps[stepNum];
|
|
214
|
+
const handoff = new HandoffPayload(step.agent, nextStep.agent, {
|
|
215
|
+
summary: `Completed: ${step.task}`,
|
|
216
|
+
artifacts: result.artifacts || [],
|
|
217
|
+
nextTask: nextStep.task
|
|
218
|
+
});
|
|
219
|
+
this.history.push(handoff.toMessage());
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
} catch (error) {
|
|
223
|
+
trace.recordResult(step.agent, error.message, false);
|
|
224
|
+
throw error;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Execute steps in parallel where possible.
|
|
231
|
+
*/
|
|
232
|
+
async _executeParallel(steps, trace, spinner) {
|
|
233
|
+
const agents = steps.map(s => s.agent);
|
|
234
|
+
const groups = findParallelGroups(agents);
|
|
235
|
+
|
|
236
|
+
let groupNum = 0;
|
|
237
|
+
let previousResults = {};
|
|
238
|
+
|
|
239
|
+
for (const group of groups) {
|
|
240
|
+
groupNum++;
|
|
241
|
+
spinner.text = `Group ${groupNum}/${groups.length}: Running ${group.join(', ')} in parallel`;
|
|
242
|
+
|
|
243
|
+
// Create checkpoint before group
|
|
244
|
+
if (this.options.enableRollback) {
|
|
245
|
+
trace.createCheckpoint(`before-group-${groupNum}`, { previousResults });
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Find steps for this group
|
|
249
|
+
const groupSteps = steps.filter(s =>
|
|
250
|
+
group.includes(s.agent.toLowerCase().replace('@', ''))
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
// Execute in parallel
|
|
254
|
+
const promises = groupSteps.map(async (step) => {
|
|
255
|
+
const stepNum = steps.indexOf(step) + 1;
|
|
256
|
+
trace.startStep(stepNum);
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
const result = await this._executeStep(step, previousResults, trace);
|
|
260
|
+
trace.recordResult(step.agent, result, true);
|
|
261
|
+
return { agent: step.agent, result, success: true };
|
|
262
|
+
} catch (error) {
|
|
263
|
+
trace.recordResult(step.agent, error.message, false);
|
|
264
|
+
return { agent: step.agent, error, success: false };
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
const results = await Promise.all(promises);
|
|
269
|
+
|
|
270
|
+
// Check for failures
|
|
271
|
+
const failures = results.filter(r => !r.success);
|
|
272
|
+
if (failures.length > 0) {
|
|
273
|
+
const failedAgents = failures.map(f => f.agent).join(', ');
|
|
274
|
+
throw new Error(`Parallel execution failed for: ${failedAgents}`);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Update previous results
|
|
278
|
+
for (const { agent, result } of results) {
|
|
279
|
+
previousResults[agent] = result;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Track parallel execution count
|
|
283
|
+
if (group.length > 1) {
|
|
284
|
+
trace.metadata.parallelExecutions++;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Execute a single step.
|
|
291
|
+
*/
|
|
292
|
+
async _executeStep(step, context, trace) {
|
|
293
|
+
const agent = this.getAgent(step.agent);
|
|
294
|
+
if (!agent) {
|
|
295
|
+
throw new Error(`Unknown agent: ${step.agent}`);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// If agent has a handler, use it
|
|
299
|
+
if (agent.handler) {
|
|
300
|
+
return await agent.handler(step.task, context, trace);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Otherwise, use provider to generate response
|
|
304
|
+
const systemPrompt = this._buildSystemPrompt(agent);
|
|
305
|
+
const userPrompt = this._buildUserPrompt(step, context);
|
|
306
|
+
|
|
307
|
+
const result = await this.provider.generate(systemPrompt, userPrompt);
|
|
308
|
+
|
|
309
|
+
// Parse result
|
|
310
|
+
return {
|
|
311
|
+
output: result.content,
|
|
312
|
+
artifacts: [],
|
|
313
|
+
timestamp: new Date().toISOString()
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Build system prompt for an agent.
|
|
319
|
+
*/
|
|
320
|
+
_buildSystemPrompt(agent) {
|
|
321
|
+
return `You are the ${agent.name} agent in the Ultra-Dex swarm.
|
|
322
|
+
Role: ${agent.role}
|
|
323
|
+
Capabilities: ${agent.capabilities?.join(', ') || 'General'}
|
|
324
|
+
|
|
325
|
+
Respond with actionable output. Be concise and specific.
|
|
326
|
+
If creating files, list them clearly.
|
|
327
|
+
If making decisions, explain the reasoning briefly.`;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Build user prompt for a step.
|
|
332
|
+
*/
|
|
333
|
+
_buildUserPrompt(step, context) {
|
|
334
|
+
let prompt = `Task: ${step.task}`;
|
|
335
|
+
|
|
336
|
+
if (context && typeof context === 'object') {
|
|
337
|
+
const contextStr = Object.entries(context)
|
|
338
|
+
.map(([k, v]) => `${k}: ${typeof v === 'object' ? JSON.stringify(v) : v}`)
|
|
339
|
+
.join('\n');
|
|
340
|
+
prompt += `\n\nContext from previous steps:\n${contextStr}`;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return prompt;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// ==========================================================================
|
|
347
|
+
// ROLLBACK SUPPORT
|
|
348
|
+
// ==========================================================================
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Attempt rollback on failure.
|
|
352
|
+
*/
|
|
353
|
+
async _attemptRollback(trace) {
|
|
354
|
+
const checkpoint = trace.getLastCheckpoint();
|
|
355
|
+
if (!checkpoint) {
|
|
356
|
+
console.log(chalk.yellow(' No checkpoint available for rollback'));
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
console.log(chalk.yellow(` Rolling back to checkpoint: ${checkpoint.name}`));
|
|
361
|
+
|
|
362
|
+
try {
|
|
363
|
+
const state = trace.rollbackTo(checkpoint.id);
|
|
364
|
+
console.log(chalk.green(` Rollback successful`));
|
|
365
|
+
return state;
|
|
366
|
+
} catch (error) {
|
|
367
|
+
console.log(chalk.red(` Rollback failed: ${error.message}`));
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Manual rollback to a specific checkpoint.
|
|
373
|
+
*/
|
|
374
|
+
rollback(taskId, checkpointId) {
|
|
375
|
+
const trace = this.traces.get(taskId);
|
|
376
|
+
if (!trace) {
|
|
377
|
+
throw new Error(`No trace found for task: ${taskId}`);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return trace.rollbackTo(checkpointId);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// ==========================================================================
|
|
384
|
+
// HISTORY & PERSISTENCE
|
|
385
|
+
// ==========================================================================
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Get execution history.
|
|
389
|
+
*/
|
|
390
|
+
getHistory() {
|
|
391
|
+
return this.history;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Get a specific trace by task ID.
|
|
396
|
+
*/
|
|
397
|
+
getTrace(taskId) {
|
|
398
|
+
return this.traces.get(taskId) || null;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Get all traces.
|
|
403
|
+
*/
|
|
404
|
+
getAllTraces() {
|
|
405
|
+
return Array.from(this.traces.values());
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Save trace to disk.
|
|
410
|
+
*/
|
|
411
|
+
async _saveTrace(trace) {
|
|
412
|
+
if (!this.options.saveArtifacts) return;
|
|
413
|
+
|
|
414
|
+
try {
|
|
415
|
+
await fs.mkdir(this.options.artifactDir, { recursive: true });
|
|
416
|
+
const filename = `${this.options.artifactDir}/trace-${trace.taskId}.json`;
|
|
417
|
+
await fs.writeFile(filename, JSON.stringify(trace.toJSON(), null, 2));
|
|
418
|
+
|
|
419
|
+
if (this.options.verbose) {
|
|
420
|
+
console.log(chalk.gray(` Trace saved to ${filename}`));
|
|
421
|
+
}
|
|
422
|
+
} catch (error) {
|
|
423
|
+
if (this.options.verbose) {
|
|
424
|
+
console.log(chalk.yellow(` Failed to save trace: ${error.message}`));
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Load trace from disk.
|
|
431
|
+
*/
|
|
432
|
+
async loadTrace(taskId) {
|
|
433
|
+
const filename = `${this.options.artifactDir}/trace-${taskId}.json`;
|
|
434
|
+
|
|
435
|
+
try {
|
|
436
|
+
const content = await fs.readFile(filename, 'utf-8');
|
|
437
|
+
const data = JSON.parse(content);
|
|
438
|
+
const trace = ExecutionTrace.fromJSON(data);
|
|
439
|
+
this.traces.set(taskId, trace);
|
|
440
|
+
return trace;
|
|
441
|
+
} catch (error) {
|
|
442
|
+
throw new Error(`Failed to load trace: ${error.message}`);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Clear history.
|
|
448
|
+
*/
|
|
449
|
+
clearHistory() {
|
|
450
|
+
this.history = [];
|
|
451
|
+
this.traces.clear();
|
|
452
|
+
this.currentTrace = null;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// ==========================================================================
|
|
456
|
+
// CONVENIENCE METHODS
|
|
457
|
+
// ==========================================================================
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Plan a feature using the planner agent.
|
|
461
|
+
*/
|
|
462
|
+
async plan(feature) {
|
|
463
|
+
const spinner = ora('🧠 Planning feature implementation...').start();
|
|
464
|
+
|
|
465
|
+
const plannerPrompt = `
|
|
466
|
+
You are the Hive Mind Planner.
|
|
467
|
+
Break down the feature "${feature}" into sequential atomic tasks for other agents.
|
|
468
|
+
|
|
469
|
+
Available Agents:
|
|
470
|
+
${Object.entries(AGENTS)
|
|
471
|
+
.filter(([_, a]) => a.tier > 0)
|
|
472
|
+
.map(([name, a]) => `- @${name} (${a.role})`)
|
|
473
|
+
.join('\n')}
|
|
474
|
+
|
|
475
|
+
Output STRICT JSON format only:
|
|
476
|
+
{
|
|
477
|
+
"tasks": [
|
|
478
|
+
{
|
|
479
|
+
"id": 1,
|
|
480
|
+
"agent": "agentname",
|
|
481
|
+
"task": "Description of the task",
|
|
482
|
+
"context": "Additional context",
|
|
483
|
+
"dependencies": []
|
|
484
|
+
}
|
|
485
|
+
]
|
|
486
|
+
}`;
|
|
487
|
+
|
|
488
|
+
try {
|
|
489
|
+
const result = await this.provider.generate(plannerPrompt, `Feature: ${feature}`);
|
|
490
|
+
|
|
491
|
+
let jsonStr = result.content.trim();
|
|
492
|
+
if (jsonStr.startsWith('```json')) {
|
|
493
|
+
jsonStr = jsonStr.replace(/^```json\n?/, '').replace(/\n?```$/, '');
|
|
494
|
+
} else if (jsonStr.startsWith('```')) {
|
|
495
|
+
jsonStr = jsonStr.replace(/^```\n?/, '').replace(/\n?```$/, '');
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const plan = JSON.parse(jsonStr);
|
|
499
|
+
spinner.succeed(`Plan generated: ${plan.tasks.length} tasks identified.`);
|
|
500
|
+
return plan.tasks;
|
|
501
|
+
} catch (error) {
|
|
502
|
+
spinner.fail('Planning failed.');
|
|
503
|
+
console.error(chalk.red(error.message));
|
|
504
|
+
return null;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Execute a pre-planned set of tasks.
|
|
510
|
+
*/
|
|
511
|
+
async execute(tasks) {
|
|
512
|
+
return this.runPipeline({
|
|
513
|
+
goal: 'Execute planned tasks',
|
|
514
|
+
steps: tasks.map(t => ({
|
|
515
|
+
agent: t.agent,
|
|
516
|
+
task: t.task,
|
|
517
|
+
context: t.context,
|
|
518
|
+
dependencies: t.dependencies
|
|
519
|
+
})),
|
|
520
|
+
parallel: false
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Run a single agent task.
|
|
526
|
+
*/
|
|
527
|
+
async runAgent(agent, task, context = {}) {
|
|
528
|
+
return this.runPipeline({
|
|
529
|
+
goal: task,
|
|
530
|
+
steps: [{ agent, task, context }],
|
|
531
|
+
parallel: false
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Get suggested agents for a task description.
|
|
537
|
+
*/
|
|
538
|
+
suggestAgents(taskDescription) {
|
|
539
|
+
const keywords = {
|
|
540
|
+
backend: ['api', 'endpoint', 'server', 'route', 'controller', 'service'],
|
|
541
|
+
frontend: ['ui', 'component', 'page', 'button', 'form', 'css', 'react', 'vue'],
|
|
542
|
+
database: ['schema', 'table', 'migration', 'query', 'sql', 'model'],
|
|
543
|
+
auth: ['login', 'authentication', 'authorization', 'password', 'session', 'jwt'],
|
|
544
|
+
security: ['vulnerability', 'audit', 'secure', 'encryption', 'xss', 'csrf'],
|
|
545
|
+
testing: ['test', 'spec', 'coverage', 'jest', 'mocha', 'e2e'],
|
|
546
|
+
devops: ['deploy', 'ci', 'cd', 'docker', 'kubernetes', 'aws', 'pipeline'],
|
|
547
|
+
performance: ['slow', 'optimize', 'cache', 'speed', 'latency', 'memory'],
|
|
548
|
+
debugger: ['bug', 'fix', 'error', 'crash', 'issue', 'debug'],
|
|
549
|
+
documentation: ['docs', 'readme', 'guide', 'api docs', 'comment'],
|
|
550
|
+
refactoring: ['refactor', 'clean', 'reorganize', 'pattern', 'simplify'],
|
|
551
|
+
planner: ['plan', 'break down', 'tasks', 'sprint', 'estimate'],
|
|
552
|
+
cto: ['architecture', 'tech stack', 'design', 'decision'],
|
|
553
|
+
research: ['compare', 'evaluate', 'research', 'options', 'alternatives'],
|
|
554
|
+
reviewer: ['review', 'approve', 'check', 'quality']
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
const lower = taskDescription.toLowerCase();
|
|
558
|
+
const matches = [];
|
|
559
|
+
|
|
560
|
+
for (const [agent, words] of Object.entries(keywords)) {
|
|
561
|
+
const matchCount = words.filter(w => lower.includes(w)).length;
|
|
562
|
+
if (matchCount > 0) {
|
|
563
|
+
matches.push({ agent, score: matchCount });
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
return matches
|
|
568
|
+
.sort((a, b) => b.score - a.score)
|
|
569
|
+
.map(m => m.agent);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// ============================================================================
|
|
574
|
+
// FACTORY FUNCTIONS
|
|
575
|
+
// ============================================================================
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Create a new SwarmCoordinator instance.
|
|
579
|
+
*/
|
|
580
|
+
export function createSwarm(provider, options = {}) {
|
|
581
|
+
return new SwarmCoordinator(provider, options);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// ============================================================================
|
|
585
|
+
// DEFAULT EXPORT
|
|
586
|
+
// ============================================================================
|
|
587
|
+
|
|
588
|
+
export default {
|
|
589
|
+
SwarmCoordinator,
|
|
590
|
+
createSwarm,
|
|
591
|
+
AgentMessage,
|
|
592
|
+
HandoffPayload,
|
|
593
|
+
ExecutionTrace,
|
|
594
|
+
AgentSchemas,
|
|
595
|
+
TIERS,
|
|
596
|
+
AGENTS,
|
|
597
|
+
TIER_FLOW
|
|
598
|
+
};
|