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,152 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import { loadState, generateMarkdown } from '../commands/plan.js';
4
+ import { projectGraph } from './graph.js';
5
+
6
+ export function registerResources(server) {
7
+ // Resource: Graph Summary
8
+ server.resource(
9
+ "graph",
10
+ "ultradex://graph/summary",
11
+ async (uri) => {
12
+ if (projectGraph.nodes.size === 0) {
13
+ await projectGraph.scan();
14
+ }
15
+ return {
16
+ contents: [{
17
+ uri: uri.href,
18
+ text: JSON.stringify(projectGraph.getSummary(), null, 2)
19
+ }]
20
+ };
21
+ }
22
+ );
23
+
24
+ // Resource: Project Context
25
+ server.resource(
26
+ "context",
27
+ "ultradex://context",
28
+ async (uri) => {
29
+ try {
30
+ const content = await fs.readFile(path.resolve(process.cwd(), 'CONTEXT.md'), 'utf8');
31
+ return {
32
+ contents: [{
33
+ uri: uri.href,
34
+ text: content
35
+ }]
36
+ };
37
+ } catch (error) {
38
+ return {
39
+ contents: [{
40
+ uri: uri.href,
41
+ text: "_No CONTEXT.md found. Run `ultra-dex init` to create one._"
42
+ }]
43
+ };
44
+ }
45
+ }
46
+ );
47
+
48
+ // Resource: Implementation Plan
49
+ server.resource(
50
+ "plan",
51
+ "ultradex://plan",
52
+ async (uri) => {
53
+ try {
54
+ // Try to load state first for dynamic plan
55
+ const state = await loadState();
56
+ let content = "";
57
+ if (state) {
58
+ content = generateMarkdown(state);
59
+ } else {
60
+ // Fallback to file
61
+ content = await fs.readFile(path.resolve(process.cwd(), 'IMPLEMENTATION-PLAN.md'), 'utf8');
62
+ }
63
+ return {
64
+ contents: [{
65
+ uri: uri.href,
66
+ text: content
67
+ }]
68
+ };
69
+ } catch (error) {
70
+ return {
71
+ contents: [{
72
+ uri: uri.href,
73
+ text: "_No IMPLEMENTATION-PLAN.md found._"
74
+ }]
75
+ };
76
+ }
77
+ }
78
+ );
79
+
80
+ // Resource: Agents Index
81
+ server.resource(
82
+ "agents",
83
+ "ultradex://agents",
84
+ async (uri) => {
85
+ try {
86
+ const content = await fs.readFile(path.resolve(process.cwd(), 'agents/00-AGENT_INDEX.md'), 'utf8');
87
+ return {
88
+ contents: [{
89
+ uri: uri.href,
90
+ text: content
91
+ }]
92
+ };
93
+ } catch (error) {
94
+ return {
95
+ contents: [{
96
+ uri: uri.href,
97
+ text: "_No agent index found._"
98
+ }]
99
+ };
100
+ }
101
+ }
102
+ );
103
+
104
+ // Resource: Machine State
105
+ server.resource(
106
+ "state",
107
+ "ultradex://state",
108
+ async (uri) => {
109
+ try {
110
+ const state = await loadState();
111
+ return {
112
+ contents: [{
113
+ uri: uri.href,
114
+ text: JSON.stringify(state, null, 2)
115
+ }]
116
+ };
117
+ } catch (error) {
118
+ return {
119
+ contents: [{
120
+ uri: uri.href,
121
+ text: JSON.stringify({ error: "No state found" })
122
+ }]
123
+ };
124
+ }
125
+ }
126
+ );
127
+
128
+ // Resource: Code Property Graph (GOD MODE)
129
+ server.resource(
130
+ "graph",
131
+ "ultradex://graph",
132
+ async (uri) => {
133
+ try {
134
+ const { buildGraph } = await import('../utils/graph.js');
135
+ const graph = await buildGraph();
136
+ return {
137
+ contents: [{
138
+ uri: uri.href,
139
+ text: JSON.stringify(graph, null, 2)
140
+ }]
141
+ };
142
+ } catch (error) {
143
+ return {
144
+ contents: [{
145
+ uri: uri.href,
146
+ text: JSON.stringify({ error: `Graph build failed: ${error.message}` })
147
+ }]
148
+ };
149
+ }
150
+ }
151
+ );
152
+ }
@@ -0,0 +1,34 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { registerResources } from "./resources.js";
4
+ import { registerTools } from "./tools.js";
5
+ import { projectGraph } from "./graph.js";
6
+
7
+ export async function startMcpServer() {
8
+ // Initialize Graph
9
+ console.error("Initializing Ultra-Dex Active Kernel...");
10
+ try {
11
+ await projectGraph.scan();
12
+ console.error(`Graph loaded: ${projectGraph.nodes.size} nodes, ${projectGraph.edges.length} edges.`);
13
+ } catch (e) {
14
+ console.error("Graph initialization warning:", e.message);
15
+ }
16
+
17
+ // Create server instance
18
+ const server = new McpServer({
19
+ name: "Ultra-Dex Active Kernel",
20
+ version: "2.2.0"
21
+ });
22
+
23
+ // Register features
24
+ registerResources(server);
25
+ registerTools(server);
26
+
27
+ // Connect transport
28
+ const transport = new StdioServerTransport();
29
+ await server.connect(transport);
30
+
31
+ // Note: Stdio transport takes over stdin/stdout, so no logging to console.log here!
32
+ // Any logging must go to stderr
33
+ console.error("Ultra-Dex MCP Server running on Stdio...");
34
+ }
@@ -0,0 +1,481 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import { z } from 'zod';
4
+ import { loadState, saveState, generateMarkdown } from '../commands/plan.js';
5
+ import { projectGraph } from './graph.js';
6
+ import { swarmCommand } from '../commands/swarm.js';
7
+ import { ultraMemory } from './memory.js';
8
+ import { glob } from 'glob';
9
+
10
+ export function registerTools(server) {
11
+ // Tool: Remember Fact
12
+ server.tool(
13
+ "remember",
14
+ "Save a fact, decision, or piece of context to persistent memory for future reference",
15
+ {
16
+ text: z.string().describe("The fact or information to remember"),
17
+ tags: z.array(z.string()).optional().describe("Tags to categorize the information"),
18
+ source: z.string().optional().default("agent").describe("Source of the information")
19
+ },
20
+ async ({ text, tags, source }) => {
21
+ try {
22
+ await ultraMemory.remember(text, tags, source);
23
+ return {
24
+ content: [{ type: "text", text: `✅ Remembered: "${text.slice(0, 50)}..."` }]
25
+ };
26
+ } catch (error) {
27
+ return {
28
+ content: [{ type: "text", text: `Failed to remember: ${error.message}` }]
29
+ };
30
+ }
31
+ }
32
+ );
33
+
34
+ // Tool: Recall Context
35
+ server.tool(
36
+ "recall",
37
+ "Search persistent memory for relevant past context, decisions, or facts",
38
+ {
39
+ query: z.string().describe("Search query to find relevant memories"),
40
+ limit: z.number().optional().default(5).describe("Maximum number of memories to return")
41
+ },
42
+ async ({ query, limit }) => {
43
+ try {
44
+ const results = await ultraMemory.search(query, limit);
45
+ if (results.length === 0) {
46
+ return {
47
+ content: [{ type: "text", text: "No relevant memories found." }]
48
+ };
49
+ }
50
+
51
+ const formatted = results.map(r =>
52
+ `[${new Date(r.timestamp).toLocaleDateString()}] (${r.source}) ${r.tags?.length ? '#' + r.tags.join(' #') : ''}\n${r.text}`
53
+ ).join('\n\n---\n\n');
54
+
55
+ return {
56
+ content: [{ type: "text", text: `Relevant memories found:\n\n${formatted}` }]
57
+ };
58
+ } catch (error) {
59
+ return {
60
+ content: [{ type: "text", text: `Recall failed: ${error.message}` }]
61
+ };
62
+ }
63
+ }
64
+ );
65
+
66
+ // Tool: Clear Memory
67
+ server.tool(
68
+ "clear_memory",
69
+ "Clear all or part of the persistent memory",
70
+ {
71
+ before: z.string().optional().describe("Clear memories older than this date (ISO format)")
72
+ },
73
+ async ({ before }) => {
74
+ try {
75
+ await ultraMemory.clear(before);
76
+ return {
77
+ content: [{ type: "text", text: `✅ Memory cleared${before ? ' before ' + before : ''}.` }]
78
+ };
79
+ } catch (error) {
80
+ return {
81
+ content: [{ type: "text", text: `Failed to clear memory: ${error.message}` }]
82
+ };
83
+ }
84
+ }
85
+ );
86
+
87
+ // Tool: Start Swarm
88
+ server.tool(
89
+ "start_swarm",
90
+ "Start a multi-agent swarm workflow for a specific feature",
91
+ {
92
+ feature: z.string().describe("The feature or task to implement"),
93
+ provider: z.string().optional().describe("AI provider (claude, openai, gemini)"),
94
+ key: z.string().optional().describe("API Key for the provider")
95
+ },
96
+ async ({ feature, provider, key }) => {
97
+ try {
98
+ console.error(`[MCP] Starting Swarm for: ${feature}`);
99
+ // Run swarm command (this logs to stdout/stderr which MCP captures)
100
+ // We capture the output by intercepting the console logs or just trust the side effects
101
+ // Since swarmCommand is designed for CLI, we might need to wrap it or modify it to return result.
102
+ // For now, we trigger it and return a success message indicating it started.
103
+
104
+ await swarmCommand(feature, { provider, key });
105
+
106
+ return {
107
+ content: [{ type: "text", text: `✅ Swarm started for feature: "${feature}". Check server logs for progress.` }]
108
+ };
109
+ } catch (error) {
110
+ return {
111
+ content: [{ type: "text", text: `Swarm failed to start: ${error.message}` }]
112
+ };
113
+ }
114
+ }
115
+ );
116
+
117
+ // Tool: Update Task Status
118
+ server.tool(
119
+ "update_task_status",
120
+ "Update the status of a task in the project plan",
121
+ {
122
+ taskId: z.string().describe("The ID of the task (e.g., '1.1', '2.3')"),
123
+ status: z.enum(['pending', 'in_progress', 'completed']).describe("New status")
124
+ },
125
+ async ({ taskId, status }) => {
126
+ const state = await loadState();
127
+ if (!state) return { content: [{ type: "text", text: "Error: No state found." }] };
128
+
129
+ let taskFound = false;
130
+ let oldStatus = '';
131
+
132
+ for (const phase of state.phases) {
133
+ const step = phase.steps.find(s => s.id === taskId);
134
+ if (step) {
135
+ oldStatus = step.status;
136
+ step.status = status;
137
+ taskFound = true;
138
+ break;
139
+ }
140
+ }
141
+
142
+ if (taskFound) {
143
+ const success = await saveState(state);
144
+ if (success) {
145
+ // Also update the Markdown plan file
146
+ const md = generateMarkdown(state);
147
+ await fs.writeFile(path.resolve(process.cwd(), 'IMPLEMENTATION-PLAN.md'), md);
148
+
149
+ return {
150
+ content: [{ type: "text", text: `✅ Task ${taskId} updated: ${oldStatus} -> ${status}` }]
151
+ };
152
+ }
153
+ return { content: [{ type: "text", text: "Error: Failed to save state." }] };
154
+ }
155
+
156
+ return { content: [{ type: "text", text: `Error: Task ${taskId} not found.` }] };
157
+ }
158
+ );
159
+
160
+ // Tool: Query Codebase Graph
161
+ server.tool(
162
+ "query_codebase",
163
+ "Search the codebase structure and dependencies",
164
+ {
165
+ query: z.string().describe("Search term or file name"),
166
+ type: z.enum(['files', 'dependencies', 'reverse_deps']).default('files')
167
+ },
168
+ async ({ query, type }) => {
169
+ // Ensure graph is populated
170
+ if (projectGraph.nodes.size === 0) {
171
+ await projectGraph.scan();
172
+ }
173
+
174
+ const summary = projectGraph.getSummary();
175
+
176
+ if (type === 'files') {
177
+ const matches = summary.files.filter(f => f.toLowerCase().includes(query.toLowerCase()));
178
+ return {
179
+ content: [{ type: "text", text: `Found ${matches.length} files matching '${query}':\n${matches.slice(0, 20).join('\n')}${matches.length > 20 ? '\n...' : ''}` }]
180
+ };
181
+ }
182
+
183
+ if (type === 'dependencies') {
184
+ const deps = summary.dependencies.filter(e => e.from.includes(query));
185
+ return {
186
+ content: [{ type: "text", text: `Dependencies for files matching '${query}':\n${deps.map(d => `${d.from} -> ${d.to}`).slice(0, 20).join('\n')}` }]
187
+ };
188
+ }
189
+
190
+ if (type === 'reverse_deps') {
191
+ const refs = summary.dependencies.filter(e => e.to.includes(query));
192
+ return {
193
+ content: [{ type: "text", text: `Files depending on '${query}':\n${refs.map(d => `${d.from}`).slice(0, 20).join('\n')}` }]
194
+ };
195
+ }
196
+
197
+ return { content: [{ type: "text", text: "Invalid query type." }] };
198
+ }
199
+ );
200
+
201
+ // Tool: Verify Task
202
+ server.tool(
203
+ "verify_task",
204
+ "Run the 21-step verification framework for a specific task",
205
+ {
206
+ taskName: z.string().describe("The name or ID of the task to verify")
207
+ },
208
+ async ({ taskName }) => {
209
+ try {
210
+ const state = await loadState();
211
+ if (!state) {
212
+ return {
213
+ content: [{ type: "text", text: "Error: No project state found. Run `ultra-dex init` first." }]
214
+ };
215
+ }
216
+
217
+ let taskFound = null;
218
+ for (const phase of state.phases) {
219
+ if (phase.steps) {
220
+ const step = phase.steps.find(s => s.id === taskName || s.task.includes(taskName));
221
+ if (step) {
222
+ taskFound = step;
223
+ break;
224
+ }
225
+ }
226
+ }
227
+
228
+ const checklist = [
229
+ "1. Atomic Scope Defined", "2. Context Loaded", "3. Architecture Alignment",
230
+ "4. Security Patterns Applied", "5. Type Safety Check", "6. Error Handling Strategy",
231
+ "7. API Documentation Updated", "8. Database Schema Verified", "9. Environment Variables Set",
232
+ "10. Implementation Complete", "11. Console Logs Removed", "12. Edge Cases Handled",
233
+ "13. Performance Check", "14. Accessibility (A11y) Check", "15. Cross-browser Check",
234
+ "16. Unit Tests Passed", "17. Integration Tests Passed", "18. Linting & Formatting",
235
+ "19. Code Review Approved", "20. Migration Scripts Ready", "21. Deployment Readiness"
236
+ ];
237
+
238
+ const report = taskFound
239
+ ? `Verification Report for '${taskFound.task}' (${taskFound.id}):\nStatus: ${taskFound.status}\n\n`
240
+ : `General Verification Report for '${taskName}':\n\n`;
241
+
242
+ const fullReport = report + checklist.map((step, i) => `[ ] ${step}`).join('\n');
243
+
244
+ return {
245
+ content: [{
246
+ type: "text",
247
+ text: fullReport
248
+ }]
249
+ };
250
+
251
+ } catch (error) {
252
+ return {
253
+ content: [{ type: "text", text: `Verification failed: ${error.message}` }]
254
+ };
255
+ }
256
+ }
257
+ );
258
+
259
+ // Tool: Read Code
260
+ server.tool(
261
+ "read_code",
262
+ "Read a file from the codebase",
263
+ {
264
+ filePath: z.string().describe("Path to the file relative to project root")
265
+ },
266
+ async ({ filePath }) => {
267
+ try {
268
+ const fullPath = path.resolve(process.cwd(), filePath);
269
+ // Security check: ensure path is within process.cwd()
270
+ if (!fullPath.startsWith(process.cwd())) {
271
+ throw new Error("Access denied: Path outside project root");
272
+ }
273
+ const content = await fs.readFile(fullPath, 'utf8');
274
+ return {
275
+ content: [{ type: "text", text: content }]
276
+ };
277
+ } catch (error) {
278
+ return {
279
+ content: [{ type: "text", text: `Error reading file: ${error.message}` }]
280
+ };
281
+ }
282
+ }
283
+ );
284
+
285
+ // Tool: Write Code (God Mode)
286
+ server.tool(
287
+ "write_code",
288
+ "Write or update a file in the codebase",
289
+ {
290
+ filePath: z.string().describe("Path to the file relative to project root"),
291
+ content: z.string().describe("The new content for the file"),
292
+ description: z.string().optional().describe("Description of the change for audit logs")
293
+ },
294
+ async ({ filePath, content, description }) => {
295
+ try {
296
+ const fullPath = path.resolve(process.cwd(), filePath);
297
+ if (!fullPath.startsWith(process.cwd())) {
298
+ throw new Error("Access denied: Path outside project root");
299
+ }
300
+
301
+ // Ensure directory exists
302
+ await fs.mkdir(path.dirname(fullPath), { recursive: true });
303
+ await fs.writeFile(fullPath, content, 'utf8');
304
+
305
+ // Log to stderr for server visibility
306
+ console.error(`[MCP] Write: ${filePath} - ${description || 'No description'}`);
307
+
308
+ return {
309
+ content: [{ type: "text", text: `Successfully wrote ${filePath}` }]
310
+ };
311
+ } catch (error) {
312
+ return {
313
+ content: [{ type: "text", text: `Error writing file: ${error.message}` }]
314
+ };
315
+ }
316
+ }
317
+ );
318
+
319
+ // Tool: Search Code (Graph-Aware)
320
+ server.tool(
321
+ "search_code",
322
+ "Search for symbols, functions, or patterns using the Code Property Graph",
323
+ {
324
+ query: z.string().describe("The symbol or function name to search for"),
325
+ useGraph: z.boolean().default(true).describe("Use structural graph search instead of text grep")
326
+ },
327
+ async ({ query, useGraph }) => {
328
+ try {
329
+ const { buildGraph, queryGraph } = await import('../utils/graph.js');
330
+ const graph = await buildGraph();
331
+
332
+ if (useGraph) {
333
+ const nodes = queryGraph(graph, query);
334
+ if (nodes.length > 0) {
335
+ return {
336
+ content: [{ type: "text", text: `Found structural matches in graph:\n${JSON.stringify(nodes, null, 2)}` }]
337
+ };
338
+ }
339
+ }
340
+
341
+ // Fallback to basic text search
342
+ const files = await glob('**/*.{js,ts,jsx,tsx,md,json}', {
343
+ ignore: ['node_modules/**', '.git/**', 'dist/**'],
344
+ nodir: true
345
+ });
346
+
347
+ const results = [];
348
+ for (const file of files) {
349
+ const content = await fs.readFile(file, 'utf8');
350
+ if (content.includes(query)) {
351
+ results.push(file);
352
+ }
353
+ }
354
+
355
+ return {
356
+ content: [{ type: "text", text: `Matches found in files:\n${results.join('\n')}` }]
357
+ };
358
+ } catch (error) {
359
+ return {
360
+ content: [{ type: "text", text: `Search failed: ${error.message}` }]
361
+ };
362
+ }
363
+ }
364
+ );
365
+
366
+ // Tool: Analyze Impact
367
+ server.tool(
368
+ "analyze_impact",
369
+ "Determine which files or functions will be impacted by changing a specific file",
370
+ {
371
+ filePath: z.string().describe("The file path to analyze")
372
+ },
373
+ async ({ filePath }) => {
374
+ try {
375
+ const { buildGraph, getImpactAnalysis } = await import('../utils/graph.js');
376
+ const graph = await buildGraph();
377
+ const impacts = getImpactAnalysis(graph, filePath);
378
+
379
+ return {
380
+ content: [{
381
+ type: "text",
382
+ text: impacts.length > 0
383
+ ? `Changing ${filePath} may impact the following files:\n- ${impacts.join('\n- ')}`
384
+ : `No direct dependents found for ${filePath}.`
385
+ }]
386
+ };
387
+ } catch (error) {
388
+ return {
389
+ content: [{ type: "text", text: `Impact analysis failed: ${error.message}` }]
390
+ };
391
+ }
392
+ }
393
+ );
394
+
395
+ // Tool: Get Agent Prompt
396
+ server.tool(
397
+ "get_agent",
398
+ "Get the system prompt for a specialized agent",
399
+ {
400
+ agentName: z.string().describe("Name of the agent (e.g., 'backend', 'planner', 'cto')")
401
+ },
402
+ async ({ agentName }) => {
403
+ const lowerName = agentName.toLowerCase();
404
+ const potentialPaths = [
405
+ `agents/1-leadership/${lowerName}.md`,
406
+ `agents/2-development/${lowerName}.md`,
407
+ `agents/3-security/${lowerName}.md`,
408
+ `agents/4-devops/${lowerName}.md`,
409
+ `agents/5-quality/${lowerName}.md`,
410
+ `agents/6-specialist/${lowerName}.md`,
411
+ `agents/0-orchestration/${lowerName}.md`,
412
+ `agents/${lowerName}.md`
413
+ ];
414
+
415
+ for (const p of potentialPaths) {
416
+ try {
417
+ const fullPath = path.resolve(process.cwd(), p);
418
+ const content = await fs.readFile(fullPath, 'utf8');
419
+ return {
420
+ content: [{ type: "text", text: content }]
421
+ };
422
+ } catch (e) {
423
+ // continue
424
+ }
425
+ }
426
+
427
+ return {
428
+ content: [{ type: "text", text: `Agent '${agentName}' not found. List of agents available in agents/00-AGENT_INDEX.md` }]
429
+ };
430
+ }
431
+ );
432
+
433
+ // Tool: Start Swarm (Agent Orchestration)
434
+ server.tool(
435
+ "start_swarm",
436
+ "Trigger a multi-agent swarm to plan and implement a feature",
437
+ {
438
+ feature: z.string().describe("Description of the feature to build"),
439
+ mode: z.enum(['plan_only', 'execute']).default('plan_only').describe("Whether to just plan or also execute")
440
+ },
441
+ async ({ feature, mode }) => {
442
+ try {
443
+ const { runAgentLoop } = await import('../commands/run.js');
444
+ const { createProvider, getDefaultProvider } = await import('../providers/index.js');
445
+ const { loadState } = await import('../commands/plan.js');
446
+ const { projectGraph } = await import('./graph.js');
447
+
448
+ // Setup Context
449
+ const state = await loadState();
450
+ const context = {
451
+ state,
452
+ plan: state ? generateMarkdown(state) : '',
453
+ graph: projectGraph.getSummary()
454
+ };
455
+
456
+ const provider = createProvider(getDefaultProvider(), { maxTokens: 8000 });
457
+
458
+ // Step 1: Planning
459
+ const planOutput = await runAgentLoop('planner', feature, provider, context);
460
+
461
+ if (mode === 'plan_only') {
462
+ return {
463
+ content: [{ type: "text", text: `Swarm Planning Complete:\n\n${planOutput}` }]
464
+ };
465
+ }
466
+
467
+ // Step 2: Execution (Simplified for MCP - invoking CTO)
468
+ const execOutput = await runAgentLoop('cto', `Execute this plan:\n${planOutput}`, provider, context);
469
+
470
+ return {
471
+ content: [{ type: "text", text: `Swarm Execution Complete:\n\n${execOutput}` }]
472
+ };
473
+
474
+ } catch (error) {
475
+ return {
476
+ content: [{ type: "text", text: `Swarm failed: ${error.message}` }]
477
+ };
478
+ }
479
+ }
480
+ );
481
+ }