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
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ollama AI Provider (Local)
|
|
3
|
+
* Provides local intelligence for Ultra-Dex
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { BaseProvider } from './base.js';
|
|
7
|
+
|
|
8
|
+
const MODELS = [
|
|
9
|
+
{ id: 'llama3:8b', name: 'Llama 3 (8B)', maxTokens: 8192, default: true },
|
|
10
|
+
{ id: 'mistral', name: 'Mistral', maxTokens: 8192 },
|
|
11
|
+
{ id: 'phi3', name: 'Phi-3', maxTokens: 4096 },
|
|
12
|
+
{ id: 'codellama', name: 'CodeLlama', maxTokens: 8192 },
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
export class OllamaProvider extends BaseProvider {
|
|
16
|
+
constructor(apiKey, options = {}) {
|
|
17
|
+
// Ollama doesn't typically require an API key
|
|
18
|
+
super(apiKey || 'not-required', options);
|
|
19
|
+
this.baseUrl = options.baseUrl || 'http://localhost:11434/api';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
getName() {
|
|
23
|
+
return 'Ollama (Local)';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
getDefaultModel() {
|
|
27
|
+
return 'llama3:8b';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
getAvailableModels() {
|
|
31
|
+
return MODELS;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
estimateCost(inputTokens, outputTokens) {
|
|
35
|
+
// Local is free!
|
|
36
|
+
return {
|
|
37
|
+
input: 0,
|
|
38
|
+
output: 0,
|
|
39
|
+
total: 0,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async generate(systemPrompt, userPrompt, options = {}) {
|
|
44
|
+
const response = await fetch(`${this.baseUrl}/generate`, {
|
|
45
|
+
method: 'POST',
|
|
46
|
+
headers: {
|
|
47
|
+
'Content-Type': 'application/json',
|
|
48
|
+
},
|
|
49
|
+
body: JSON.stringify({
|
|
50
|
+
model: this.model,
|
|
51
|
+
prompt: `${systemPrompt}\n\n${userPrompt}`,
|
|
52
|
+
stream: false,
|
|
53
|
+
options: {
|
|
54
|
+
num_predict: options.maxTokens || this.maxTokens,
|
|
55
|
+
},
|
|
56
|
+
}),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
if (!response.ok) {
|
|
60
|
+
const error = await response.text().catch(() => response.statusText);
|
|
61
|
+
throw new Error(`Ollama API error: ${error}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const data = await response.json();
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
content: data.response || '',
|
|
68
|
+
usage: {
|
|
69
|
+
inputTokens: data.prompt_eval_count || 0,
|
|
70
|
+
outputTokens: data.eval_count || 0,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async generateStream(systemPrompt, userPrompt, onChunk, options = {}) {
|
|
76
|
+
const response = await fetch(`${this.baseUrl}/generate`, {
|
|
77
|
+
method: 'POST',
|
|
78
|
+
headers: {
|
|
79
|
+
'Content-Type': 'application/json',
|
|
80
|
+
},
|
|
81
|
+
body: JSON.stringify({
|
|
82
|
+
model: this.model,
|
|
83
|
+
prompt: `${systemPrompt}\n\n${userPrompt}`,
|
|
84
|
+
stream: true,
|
|
85
|
+
options: {
|
|
86
|
+
num_predict: options.maxTokens || this.maxTokens,
|
|
87
|
+
},
|
|
88
|
+
}),
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
if (!response.ok) {
|
|
92
|
+
throw new Error(`Ollama API error: ${response.statusText}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const reader = response.body.getReader();
|
|
96
|
+
const decoder = new TextDecoder();
|
|
97
|
+
let fullContent = '';
|
|
98
|
+
|
|
99
|
+
while (true) {
|
|
100
|
+
const { done, value } = await reader.read();
|
|
101
|
+
if (done) break;
|
|
102
|
+
|
|
103
|
+
const chunk = decoder.decode(value);
|
|
104
|
+
const lines = chunk.split('\n');
|
|
105
|
+
|
|
106
|
+
for (const line of lines) {
|
|
107
|
+
if (!line.trim()) continue;
|
|
108
|
+
try {
|
|
109
|
+
const parsed = JSON.parse(line);
|
|
110
|
+
if (parsed.response) {
|
|
111
|
+
fullContent += parsed.response;
|
|
112
|
+
onChunk(parsed.response);
|
|
113
|
+
}
|
|
114
|
+
} catch {
|
|
115
|
+
// Skip malformed JSON
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
content: fullContent,
|
|
122
|
+
usage: { inputTokens: 0, outputTokens: 0 } // Ollama streaming usage is complex to track line by line
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async validateApiKey() {
|
|
127
|
+
try {
|
|
128
|
+
const response = await fetch(`${this.baseUrl}/tags`);
|
|
129
|
+
return response.ok;
|
|
130
|
+
} catch {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export default OllamaProvider;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic Router AI Provider
|
|
3
|
+
* Routes tasks between local and cloud intelligence
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { BaseProvider } from './base.js';
|
|
7
|
+
|
|
8
|
+
export class RouterProvider extends BaseProvider {
|
|
9
|
+
constructor(apiKey, options = {}) {
|
|
10
|
+
super(apiKey, options);
|
|
11
|
+
this.localProvider = options.localProvider;
|
|
12
|
+
this.cloudProvider = options.cloudProvider;
|
|
13
|
+
this.threshold = options.threshold || 'medium'; // complexity threshold
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
getName() {
|
|
17
|
+
return `Semantic Router (${this.localProvider?.getName() || 'Local'} + ${this.cloudProvider?.getName() || 'Cloud'})`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
getDefaultModel() {
|
|
21
|
+
return 'router-v1';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async generate(systemPrompt, userPrompt, options = {}) {
|
|
25
|
+
const isComplex = this.assessComplexity(systemPrompt, userPrompt);
|
|
26
|
+
const provider = (isComplex || !this.localProvider) ? this.cloudProvider : this.localProvider;
|
|
27
|
+
|
|
28
|
+
console.error(`[Router] Routing to ${provider.getName()} (Complexity: ${isComplex ? 'High' : 'Low'})`);
|
|
29
|
+
|
|
30
|
+
return provider.generate(systemPrompt, userPrompt, options);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async generateStream(systemPrompt, userPrompt, onChunk, options = {}) {
|
|
34
|
+
const isComplex = this.assessComplexity(systemPrompt, userPrompt);
|
|
35
|
+
const provider = (isComplex || !this.localProvider) ? this.cloudProvider : this.localProvider;
|
|
36
|
+
|
|
37
|
+
console.error(`[Router] Routing to ${provider.getName()} (Complexity: ${isComplex ? 'High' : 'Low'})`);
|
|
38
|
+
|
|
39
|
+
return provider.generateStream(systemPrompt, userPrompt, onChunk, options);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
assessComplexity(systemPrompt, userPrompt) {
|
|
43
|
+
const combined = (systemPrompt + userPrompt).toLowerCase();
|
|
44
|
+
|
|
45
|
+
// Heuristics for "High Complexity"
|
|
46
|
+
const complexKeywords = [
|
|
47
|
+
'refactor', 'architect', 'security audit', 'design pattern',
|
|
48
|
+
'migration', 'performance optimization', 'complex', 'fix the bug'
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
const isComplexKeyword = complexKeywords.some(k => combined.includes(k));
|
|
52
|
+
const isLongPrompt = combined.length > 2000;
|
|
53
|
+
|
|
54
|
+
return isComplexKeyword || isLongPrompt;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async validateApiKey() {
|
|
58
|
+
const cloudValid = await this.cloudProvider?.validateApiKey();
|
|
59
|
+
return !!cloudValid;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export default RouterProvider;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
// Define the Quality Rules
|
|
5
|
+
const RULES = [
|
|
6
|
+
{
|
|
7
|
+
id: 'api-zod-validation',
|
|
8
|
+
name: 'API Input Validation',
|
|
9
|
+
description: 'API endpoints must validate input using Zod',
|
|
10
|
+
severity: 'error',
|
|
11
|
+
// Regex based pattern matching for file path
|
|
12
|
+
pattern: /app\/api\/.*\.ts|src\/routes\/.*\.ts|pages\/api\/.*\.ts/,
|
|
13
|
+
check: (content) => {
|
|
14
|
+
const isApi = /NextRequest|NextResponse|express\.Router|fastify/.test(content);
|
|
15
|
+
if (!isApi) return true;
|
|
16
|
+
return /import.*zod|require\(['"]zod['"]\)/.test(content);
|
|
17
|
+
},
|
|
18
|
+
message: 'API files must import "zod" for validation.'
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: 'no-explicit-any',
|
|
22
|
+
name: 'No Explicit Any',
|
|
23
|
+
description: 'Avoid using "any" type in TypeScript',
|
|
24
|
+
severity: 'warning',
|
|
25
|
+
pattern: /.*\.tsx?$/,
|
|
26
|
+
check: (content) => {
|
|
27
|
+
return !/:\s*any\b|<\s*any\s*>/.test(content);
|
|
28
|
+
},
|
|
29
|
+
message: 'Found explicit "any" type. Use unknown or a specific type.'
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: 'console-log-in-api',
|
|
33
|
+
name: 'No Console Log in Prod',
|
|
34
|
+
description: 'Use a logger instead of console.log in API routes',
|
|
35
|
+
severity: 'warning',
|
|
36
|
+
pattern: /app\/api\/.*|src\/routes\/.*/,
|
|
37
|
+
check: (content) => {
|
|
38
|
+
return !/console\.log\(/.test(content);
|
|
39
|
+
},
|
|
40
|
+
message: 'Found console.log in API. Use a proper logger or console.error/warn.'
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'secret-leak',
|
|
44
|
+
name: 'Secret Key Leak',
|
|
45
|
+
description: 'Do not commit secrets starting with sk_ or similar',
|
|
46
|
+
severity: 'critical',
|
|
47
|
+
pattern: /.*/,
|
|
48
|
+
check: (content) => {
|
|
49
|
+
// Obfuscate regex even further to avoid self-triggering on the string literals
|
|
50
|
+
const p1 = 'sk' + '_live' + '_';
|
|
51
|
+
const p2 = 'sk' + '_test' + '_';
|
|
52
|
+
const p3 = 'gh' + 'p_';
|
|
53
|
+
const p4 = 'ey' + 'J';
|
|
54
|
+
const pattern = new RegExp(`${p1}|${p2}|${p3}|${p4}`);
|
|
55
|
+
return !pattern.test(content);
|
|
56
|
+
},
|
|
57
|
+
message: 'Potential secret key detected!'
|
|
58
|
+
}
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
async function getFiles(dir) {
|
|
62
|
+
const dirents = await fs.readdir(dir, { withFileTypes: true });
|
|
63
|
+
const files = await Promise.all(dirents.map((dirent) => {
|
|
64
|
+
const res = path.resolve(dir, dirent.name);
|
|
65
|
+
if (dirent.isDirectory()) {
|
|
66
|
+
if (['node_modules', '.git', '.next', 'dist', 'build'].includes(dirent.name)) return [];
|
|
67
|
+
return getFiles(res);
|
|
68
|
+
}
|
|
69
|
+
return res;
|
|
70
|
+
}));
|
|
71
|
+
return files.flat();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function runQualityScan(dir) {
|
|
75
|
+
const results = {
|
|
76
|
+
passed: 0,
|
|
77
|
+
failed: 0,
|
|
78
|
+
warnings: 0,
|
|
79
|
+
filesScanned: 0,
|
|
80
|
+
details: []
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const projectRoot = path.resolve(dir);
|
|
84
|
+
const allFiles = await getFiles(projectRoot);
|
|
85
|
+
|
|
86
|
+
for (const filePath of allFiles) {
|
|
87
|
+
// Relative path for pattern matching
|
|
88
|
+
const relativePath = path.relative(projectRoot, filePath);
|
|
89
|
+
|
|
90
|
+
// Skip non-code/text files roughly
|
|
91
|
+
if (/\.(png|jpg|jpeg|gif|ico|woff|woff2|ttf|eot|pdf|lock)$/.test(filePath)) continue;
|
|
92
|
+
|
|
93
|
+
let content = '';
|
|
94
|
+
try {
|
|
95
|
+
content = await fs.readFile(filePath, 'utf8');
|
|
96
|
+
} catch { continue; }
|
|
97
|
+
|
|
98
|
+
results.filesScanned++;
|
|
99
|
+
|
|
100
|
+
for (const rule of RULES) {
|
|
101
|
+
if (rule.pattern.test(relativePath) || rule.pattern.test(filePath)) { // Match against both just in case
|
|
102
|
+
try {
|
|
103
|
+
const passed = rule.check(content);
|
|
104
|
+
if (!passed) {
|
|
105
|
+
const issue = {
|
|
106
|
+
ruleId: rule.id,
|
|
107
|
+
ruleName: rule.name,
|
|
108
|
+
file: relativePath,
|
|
109
|
+
severity: rule.severity,
|
|
110
|
+
message: rule.message
|
|
111
|
+
};
|
|
112
|
+
results.details.push(issue);
|
|
113
|
+
|
|
114
|
+
if (rule.severity === 'error' || rule.severity === 'critical') {
|
|
115
|
+
results.failed++;
|
|
116
|
+
} else {
|
|
117
|
+
results.warnings++;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
} catch (err) {
|
|
121
|
+
// Ignore check errors
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return results;
|
|
128
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { runAgentLoop } from '../commands/run.js';
|
|
4
|
+
import { createProvider, getDefaultProvider } from '../providers/index.js';
|
|
5
|
+
import { loadState } from '../commands/plan.js';
|
|
6
|
+
import fs from 'fs/promises';
|
|
7
|
+
|
|
8
|
+
export class SwarmCoordinator {
|
|
9
|
+
constructor(provider, context) {
|
|
10
|
+
this.provider = provider;
|
|
11
|
+
this.context = context;
|
|
12
|
+
this.history = [];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async plan(feature) {
|
|
16
|
+
const spinner = ora('š§ Hive Mind: Planning feature implementation...').start();
|
|
17
|
+
|
|
18
|
+
// System prompt to force JSON output for the plan
|
|
19
|
+
const plannerPrompt = `
|
|
20
|
+
You are the Hive Mind Planner.
|
|
21
|
+
Your goal: Break down the feature "${feature}" into sequential atomic tasks for other agents.
|
|
22
|
+
|
|
23
|
+
Available Agents:
|
|
24
|
+
- @Backend (API, logic)
|
|
25
|
+
- @Frontend (UI, React)
|
|
26
|
+
- @Database (Schema, migrations)
|
|
27
|
+
- @Auth (Authentication)
|
|
28
|
+
- @Testing (Tests)
|
|
29
|
+
|
|
30
|
+
Output STRICT JSON format only:
|
|
31
|
+
{
|
|
32
|
+
"tasks": [
|
|
33
|
+
{
|
|
34
|
+
"id": 1,
|
|
35
|
+
"agent": "backend",
|
|
36
|
+
"task": "Create API endpoint for...",
|
|
37
|
+
"context": "Needs to handle..."
|
|
38
|
+
},
|
|
39
|
+
...
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
`;
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const result = await this.provider.generate(plannerPrompt, `Feature: ${feature}`);
|
|
46
|
+
|
|
47
|
+
// Attempt to parse JSON (handling potential markdown code blocks)
|
|
48
|
+
let jsonStr = result.content.trim();
|
|
49
|
+
if (jsonStr.startsWith('```json')) {
|
|
50
|
+
jsonStr = jsonStr.replace(/^```json\n?/, '').replace(/\n?```$/, '');
|
|
51
|
+
} else if (jsonStr.startsWith('```')) {
|
|
52
|
+
jsonStr = jsonStr.replace(/^```\n?/, '').replace(/\n?```$/, '');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const plan = JSON.parse(jsonStr);
|
|
56
|
+
spinner.succeed(`Plan generated: ${plan.tasks.length} tasks identified.`);
|
|
57
|
+
return plan.tasks;
|
|
58
|
+
} catch (error) {
|
|
59
|
+
spinner.fail('Planning failed.');
|
|
60
|
+
console.error(chalk.red(error.message));
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async execute(tasks) {
|
|
66
|
+
console.log(chalk.bold('\nš Swarm Execution Started\n'));
|
|
67
|
+
|
|
68
|
+
for (const task of tasks) {
|
|
69
|
+
console.log(chalk.bold.cyan(`\nš¹ Step ${task.id}: [${task.agent.toUpperCase()}] ${task.task}`));
|
|
70
|
+
|
|
71
|
+
// Inject previous history into context
|
|
72
|
+
const currentContext = {
|
|
73
|
+
...this.context,
|
|
74
|
+
history: this.history.join('\n\n---\n\n')
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const output = await runAgentLoop(task.agent, task.task, this.provider, currentContext);
|
|
79
|
+
|
|
80
|
+
// Save to history
|
|
81
|
+
this.history.push(`## Task ${task.id} (${task.agent})\n**Goal:** ${task.task}\n\n**Output:**\n${output}`);
|
|
82
|
+
|
|
83
|
+
// Save artifact
|
|
84
|
+
const filename = `swarm-task-${task.id}-${task.agent}.md`;
|
|
85
|
+
await fs.writeFile(filename, output);
|
|
86
|
+
console.log(chalk.green(` ā Output saved to ${filename}`));
|
|
87
|
+
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.log(chalk.red(` ā Task failed: ${error.message}`));
|
|
90
|
+
// Decide whether to continue or stop
|
|
91
|
+
// For now, we continue
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
console.log(chalk.bold.green('\nā
Swarm Mission Complete'));
|
|
96
|
+
}
|
|
97
|
+
}
|