@sashabogi/foundation 0.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 (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +341 -0
  3. package/dist/cli.d.ts +12 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +308 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/index.d.ts +32 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +78 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/services/config.service.d.ts +92 -0
  12. package/dist/services/config.service.d.ts.map +1 -0
  13. package/dist/services/config.service.js +284 -0
  14. package/dist/services/config.service.js.map +1 -0
  15. package/dist/services/git.service.d.ts +175 -0
  16. package/dist/services/git.service.d.ts.map +1 -0
  17. package/dist/services/git.service.js +395 -0
  18. package/dist/services/git.service.js.map +1 -0
  19. package/dist/services/provider.service.d.ts +74 -0
  20. package/dist/services/provider.service.d.ts.map +1 -0
  21. package/dist/services/provider.service.js +182 -0
  22. package/dist/services/provider.service.js.map +1 -0
  23. package/dist/services/storage.service.d.ts +84 -0
  24. package/dist/services/storage.service.d.ts.map +1 -0
  25. package/dist/services/storage.service.js +320 -0
  26. package/dist/services/storage.service.js.map +1 -0
  27. package/dist/tools/demerzel/index.d.ts +21 -0
  28. package/dist/tools/demerzel/index.d.ts.map +1 -0
  29. package/dist/tools/demerzel/index.js +166 -0
  30. package/dist/tools/demerzel/index.js.map +1 -0
  31. package/dist/tools/gaia/index.d.ts +27 -0
  32. package/dist/tools/gaia/index.d.ts.map +1 -0
  33. package/dist/tools/gaia/index.js +466 -0
  34. package/dist/tools/gaia/index.js.map +1 -0
  35. package/dist/tools/seldon/index.d.ts +43 -0
  36. package/dist/tools/seldon/index.d.ts.map +1 -0
  37. package/dist/tools/seldon/index.js +886 -0
  38. package/dist/tools/seldon/index.js.map +1 -0
  39. package/dist/types/index.d.ts +392 -0
  40. package/dist/types/index.d.ts.map +1 -0
  41. package/dist/types/index.js +10 -0
  42. package/dist/types/index.js.map +1 -0
  43. package/package.json +85 -0
@@ -0,0 +1,886 @@
1
+ /**
2
+ * Seldon Module Tools
3
+ *
4
+ * Multi-agent orchestration with 13+ providers.
5
+ * Named after Hari Seldon - the mathematician who created psychohistory
6
+ * to predict and guide humanity's future through the Seldon Plan.
7
+ *
8
+ * Tools:
9
+ *
10
+ * Agent Invocation:
11
+ * - seldon_invoke: Invoke a single agent by role
12
+ * - seldon_compare: Run same task through multiple agents
13
+ * - seldon_critique: Get adversarial plan critique
14
+ * - seldon_review: Get code review feedback
15
+ * - seldon_design: Get UI/UX design feedback
16
+ *
17
+ * Planning:
18
+ * - seldon_plan: Generate a detailed implementation plan
19
+ * - seldon_phase_create: Create execution phases from intent
20
+ * - seldon_phase_list: List phases and their status
21
+ *
22
+ * Verification (The Loop):
23
+ * - seldon_verify: Verify implementation against plan
24
+ * - seldon_fix: Generate fixes for verification issues
25
+ * - seldon_execute_verified: Execute with verification loop (YOLO mode)
26
+ * - seldon_execute_parallel: Execute multiple phases in parallel
27
+ *
28
+ * Pipeline:
29
+ * - seldon_pipeline_create: Create multi-step DAG workflow
30
+ * - seldon_pipeline_execute: Execute a pipeline
31
+ * - seldon_pipeline_status: Get pipeline execution status
32
+ *
33
+ * Task Management:
34
+ * - seldon_task_execute: Execute task in isolated worktree
35
+ * - seldon_task_claim: Claim next available task
36
+ *
37
+ * Providers:
38
+ * - seldon_providers_list: List available providers
39
+ * - seldon_providers_test: Test provider connectivity
40
+ */
41
+ import { z } from 'zod';
42
+ import { nanoid } from 'nanoid';
43
+ import { ProviderService } from '../../services/provider.service.js';
44
+ import { StorageService } from '../../services/storage.service.js';
45
+ import { GitService } from '../../services/git.service.js';
46
+ // =============================================================================
47
+ // Schemas
48
+ // =============================================================================
49
+ const AgentRoleSchema = z.enum([
50
+ 'orchestrator',
51
+ 'coder',
52
+ 'critic',
53
+ 'reviewer',
54
+ 'designer',
55
+ 'researcher',
56
+ 'verifier', // New role for verification
57
+ ]);
58
+ const IssueSeveritySchema = z.enum(['critical', 'major', 'minor']);
59
+ const VerificationCategorySchema = z.enum([
60
+ 'missing_implementation',
61
+ 'incorrect_implementation',
62
+ 'regression',
63
+ 'type_error',
64
+ 'logic_error',
65
+ 'security',
66
+ 'performance',
67
+ 'style',
68
+ 'test_failure',
69
+ 'other',
70
+ ]);
71
+ // =============================================================================
72
+ // In-memory state for phases and plans (will be moved to StorageService)
73
+ // =============================================================================
74
+ const phases = new Map();
75
+ const plans = new Map();
76
+ const verificationResults = new Map();
77
+ // =============================================================================
78
+ // Tool Registration
79
+ // =============================================================================
80
+ export function registerSeldonTools(server) {
81
+ const providers = ProviderService.getInstance();
82
+ const storage = StorageService.getInstance();
83
+ const git = GitService.getInstance();
84
+ // ===========================================================================
85
+ // Agent Invocation Tools
86
+ // ===========================================================================
87
+ // seldon_invoke
88
+ server.tool('seldon_invoke', 'Invoke a specialized AI agent by role. Routes to configured provider with automatic failover.', {
89
+ role: AgentRoleSchema.describe('Agent role to invoke'),
90
+ task: z.string().describe('Task description for the agent'),
91
+ context: z.string().optional().describe('Additional context for the agent'),
92
+ }, async ({ role, task, context }) => {
93
+ const result = await providers.invoke({ role, task, context });
94
+ return {
95
+ content: [
96
+ {
97
+ type: 'text',
98
+ text: result.success
99
+ ? `**${role}** (${result.provider}/${result.model}):\n\n${result.output}`
100
+ : `Error: ${result.error}`,
101
+ },
102
+ ],
103
+ };
104
+ });
105
+ // seldon_compare
106
+ server.tool('seldon_compare', 'Run the same task through multiple agents and compare their responses.', {
107
+ roles: z.array(AgentRoleSchema).describe('Agent roles to compare'),
108
+ task: z.string().describe('Task to send to all agents'),
109
+ context: z.string().optional().describe('Additional context'),
110
+ }, async ({ roles, task, context }) => {
111
+ const results = await Promise.all(roles.map((role) => providers.invoke({ role, task, context })));
112
+ const output = results
113
+ .map((r, i) => `## ${roles[i]} (${r.provider})\n\n${r.success ? r.output : `Error: ${r.error}`}`)
114
+ .join('\n\n---\n\n');
115
+ return {
116
+ content: [{ type: 'text', text: output }],
117
+ };
118
+ });
119
+ // seldon_critique
120
+ server.tool('seldon_critique', 'Get adversarial critique on a plan, PRD, or architecture from the critic role.', {
121
+ content: z.string().describe('Plan or document to critique'),
122
+ focus: z.string().optional().describe('Specific areas to focus critique on'),
123
+ }, async ({ content, focus }) => {
124
+ const task = focus
125
+ ? `Critique this plan, focusing on: ${focus}\n\n${content}`
126
+ : `Critique this plan thoroughly:\n\n${content}`;
127
+ const result = await providers.invoke({ role: 'critic', task });
128
+ return {
129
+ content: [
130
+ {
131
+ type: 'text',
132
+ text: result.success ? result.output : `Error: ${result.error}`,
133
+ },
134
+ ],
135
+ };
136
+ });
137
+ // seldon_review
138
+ server.tool('seldon_review', 'Get code review feedback from the reviewer role.', {
139
+ code: z.string().describe('Code to review'),
140
+ context: z.string().optional().describe('Context about the code (purpose, language, etc.)'),
141
+ focus: z.array(z.string()).optional().describe('Areas to focus on (e.g., ["security", "performance"])'),
142
+ }, async ({ code, context, focus }) => {
143
+ let task = `Review this code:\n\n\`\`\`\n${code}\n\`\`\``;
144
+ if (context)
145
+ task += `\n\nContext: ${context}`;
146
+ if (focus?.length)
147
+ task += `\n\nFocus areas: ${focus.join(', ')}`;
148
+ const result = await providers.invoke({ role: 'reviewer', task });
149
+ return {
150
+ content: [
151
+ {
152
+ type: 'text',
153
+ text: result.success ? result.output : `Error: ${result.error}`,
154
+ },
155
+ ],
156
+ };
157
+ });
158
+ // seldon_design
159
+ server.tool('seldon_design', 'Get UI/UX design feedback from the designer role.', {
160
+ description: z.string().describe('Description of the design or component'),
161
+ context: z.string().optional().describe('Additional context (target users, platform, etc.)'),
162
+ }, async ({ description, context }) => {
163
+ let task = `Provide UI/UX feedback on:\n\n${description}`;
164
+ if (context)
165
+ task += `\n\nContext: ${context}`;
166
+ const result = await providers.invoke({ role: 'designer', task });
167
+ return {
168
+ content: [
169
+ {
170
+ type: 'text',
171
+ text: result.success ? result.output : `Error: ${result.error}`,
172
+ },
173
+ ],
174
+ };
175
+ });
176
+ // ===========================================================================
177
+ // Planning Tools
178
+ // ===========================================================================
179
+ // seldon_plan
180
+ server.tool('seldon_plan', 'Generate a detailed implementation plan from an intent. Creates file-level instructions.', {
181
+ intent: z.string().describe('What you want to accomplish'),
182
+ context: z.string().optional().describe('Additional context (existing code, constraints, etc.)'),
183
+ phaseId: z.string().optional().describe('Associate with a phase'),
184
+ }, async ({ intent, context, phaseId }) => {
185
+ const planPrompt = `You are a senior software architect. Create a detailed implementation plan for the following intent.
186
+
187
+ INTENT: ${intent}
188
+
189
+ ${context ? `CONTEXT:\n${context}\n` : ''}
190
+
191
+ Respond with a structured plan in the following format:
192
+
193
+ ## Overview
194
+ [Brief description of what will be implemented]
195
+
196
+ ## Files to Modify/Create
197
+
198
+ ### [file path]
199
+ - **Action**: create | modify | delete
200
+ - **Description**: [what this file does]
201
+ - **Details**:
202
+ - [specific implementation detail 1]
203
+ - [specific implementation detail 2]
204
+ - ...
205
+
206
+ ### [next file...]
207
+
208
+ ## Acceptance Criteria
209
+ 1. [criterion 1]
210
+ 2. [criterion 2]
211
+ ...
212
+
213
+ ## Dependencies
214
+ - [any dependencies between files or external packages]
215
+
216
+ ## Risks/Considerations
217
+ - [potential issues to watch for]`;
218
+ const result = await providers.invoke({
219
+ role: 'orchestrator',
220
+ task: planPrompt,
221
+ });
222
+ if (!result.success) {
223
+ return {
224
+ content: [{ type: 'text', text: `Error generating plan: ${result.error}` }],
225
+ };
226
+ }
227
+ // Create plan object
228
+ const plan = {
229
+ id: `plan_${nanoid(8)}`,
230
+ phaseId: phaseId || `phase_${nanoid(8)}`,
231
+ intent,
232
+ description: result.output,
233
+ files: [], // Would be parsed from output in real implementation
234
+ acceptanceCriteria: [], // Would be parsed from output
235
+ generatedAt: Date.now(),
236
+ generatedBy: result.provider,
237
+ };
238
+ plans.set(plan.id, plan);
239
+ // Update phase if provided
240
+ if (phaseId && phases.has(phaseId)) {
241
+ const phase = phases.get(phaseId);
242
+ phase.planId = plan.id;
243
+ phase.status = 'ready';
244
+ phases.set(phaseId, phase);
245
+ }
246
+ return {
247
+ content: [
248
+ {
249
+ type: 'text',
250
+ text: `# Plan ${plan.id}\n\n${result.output}\n\n---\n*Generated by ${result.provider}*`,
251
+ },
252
+ ],
253
+ };
254
+ });
255
+ // seldon_phase_create
256
+ server.tool('seldon_phase_create', 'Create execution phases from a high-level intent. Breaks work into manageable chunks.', {
257
+ intent: z.string().describe('High-level description of what to build'),
258
+ numPhases: z.number().optional().describe('Suggested number of phases (default: auto)'),
259
+ }, async ({ intent, numPhases }) => {
260
+ const phasePrompt = `You are a senior software architect. Break down the following project intent into sequential execution phases.
261
+
262
+ INTENT: ${intent}
263
+
264
+ ${numPhases ? `Target approximately ${numPhases} phases.` : 'Determine the appropriate number of phases based on complexity.'}
265
+
266
+ Each phase should be:
267
+ - Self-contained and independently verifiable
268
+ - Buildable incrementally on previous phases
269
+ - Clear in scope and acceptance criteria
270
+
271
+ Respond with phases in this format:
272
+
273
+ ## Phase 1: [Name]
274
+ **Description**: [What this phase accomplishes]
275
+ **Depends On**: [None or previous phase numbers]
276
+ **Acceptance Criteria**:
277
+ - [criterion 1]
278
+ - [criterion 2]
279
+
280
+ ## Phase 2: [Name]
281
+ ...`;
282
+ const result = await providers.invoke({
283
+ role: 'orchestrator',
284
+ task: phasePrompt,
285
+ });
286
+ if (!result.success) {
287
+ return {
288
+ content: [{ type: 'text', text: `Error creating phases: ${result.error}` }],
289
+ };
290
+ }
291
+ // Parse phases from output (simplified - would use regex in real implementation)
292
+ const phaseMatches = result.output.match(/## Phase \d+:/g) || [];
293
+ const createdPhases = [];
294
+ for (let i = 0; i < phaseMatches.length; i++) {
295
+ const phase = {
296
+ id: `phase_${nanoid(8)}`,
297
+ name: `Phase ${i + 1}`,
298
+ description: '', // Would be parsed
299
+ order: i + 1,
300
+ status: 'pending',
301
+ dependsOn: i > 0 ? [createdPhases[i - 1].id] : undefined,
302
+ createdAt: Date.now(),
303
+ };
304
+ phases.set(phase.id, phase);
305
+ createdPhases.push(phase);
306
+ }
307
+ let output = `# Created ${createdPhases.length} Phases\n\n`;
308
+ output += createdPhases.map(p => `- **${p.id}**: ${p.name} (${p.status})`).join('\n');
309
+ output += `\n\n---\n\n${result.output}`;
310
+ return {
311
+ content: [{ type: 'text', text: output }],
312
+ };
313
+ });
314
+ // seldon_phase_list
315
+ server.tool('seldon_phase_list', 'List all phases and their current status.', {}, async () => {
316
+ if (phases.size === 0) {
317
+ return {
318
+ content: [{ type: 'text', text: 'No phases created. Use `seldon_phase_create` to create phases.' }],
319
+ };
320
+ }
321
+ const sortedPhases = Array.from(phases.values()).sort((a, b) => a.order - b.order);
322
+ const statusEmoji = {
323
+ pending: '⏳',
324
+ planning: '📝',
325
+ ready: '✅',
326
+ executing: '🔨',
327
+ verifying: '🔍',
328
+ fixing: '🔧',
329
+ passed: '✓',
330
+ failed: '✗',
331
+ blocked: '🚫',
332
+ cancelled: '⊘',
333
+ };
334
+ let output = '# Phases\n\n';
335
+ output += '| # | ID | Name | Status | Plan |\n';
336
+ output += '|---|-----|------|--------|------|\n';
337
+ for (const phase of sortedPhases) {
338
+ const emoji = statusEmoji[phase.status] || '?';
339
+ output += `| ${phase.order} | ${phase.id} | ${phase.name} | ${emoji} ${phase.status} | ${phase.planId || '-'} |\n`;
340
+ }
341
+ return {
342
+ content: [{ type: 'text', text: output }],
343
+ };
344
+ });
345
+ // ===========================================================================
346
+ // Verification Tools (The Loop)
347
+ // ===========================================================================
348
+ // seldon_verify
349
+ server.tool('seldon_verify', 'Verify implementation against a plan. Returns issues categorized by severity (critical/major/minor).', {
350
+ planId: z.string().describe('Plan ID to verify against'),
351
+ files: z.array(z.string()).optional().describe('Specific files to check (default: all plan files)'),
352
+ runTests: z.boolean().optional().describe('Run test suite as part of verification (default: true)'),
353
+ testCommand: z.string().optional().describe('Custom test command (default: npm test)'),
354
+ }, async ({ planId, files, runTests = true, testCommand = 'npm test' }) => {
355
+ const plan = plans.get(planId);
356
+ if (!plan) {
357
+ return {
358
+ content: [{ type: 'text', text: `Plan not found: ${planId}` }],
359
+ };
360
+ }
361
+ const startTime = Date.now();
362
+ // Build verification prompt
363
+ const verifyPrompt = `You are a meticulous code reviewer verifying an implementation against a plan.
364
+
365
+ ## PLAN
366
+ ${plan.description}
367
+
368
+ ## ACCEPTANCE CRITERIA
369
+ ${plan.acceptanceCriteria.map((c, i) => `${i + 1}. ${c}`).join('\n') || 'See plan description'}
370
+
371
+ ## TASK
372
+ Analyze the implementation and identify any issues. For each issue, provide:
373
+ - Severity: critical (blocks core functionality), major (significant issue), or minor (polish)
374
+ - Category: missing_implementation, incorrect_implementation, regression, type_error, logic_error, security, performance, style, test_failure, other
375
+ - File and line number if applicable
376
+ - Clear description of the issue
377
+ - Suggested fix
378
+
379
+ Respond in this format:
380
+
381
+ ## VERIFICATION RESULT
382
+
383
+ ### Summary
384
+ - Critical: [count]
385
+ - Major: [count]
386
+ - Minor: [count]
387
+ - Passed: [yes/no]
388
+
389
+ ### Issues
390
+
391
+ #### Issue 1
392
+ - **Severity**: critical | major | minor
393
+ - **Category**: [category]
394
+ - **File**: [path]
395
+ - **Line**: [number or range]
396
+ - **Message**: [description]
397
+ - **Suggestion**: [how to fix]
398
+
399
+ #### Issue 2
400
+ ...
401
+
402
+ If no issues found, respond with:
403
+
404
+ ## VERIFICATION RESULT
405
+ ### Summary
406
+ - Critical: 0
407
+ - Major: 0
408
+ - Minor: 0
409
+ - Passed: yes
410
+
411
+ No issues found. Implementation matches plan.`;
412
+ const result = await providers.invoke({
413
+ role: 'verifier',
414
+ task: verifyPrompt,
415
+ });
416
+ if (!result.success) {
417
+ return {
418
+ content: [{ type: 'text', text: `Verification error: ${result.error}` }],
419
+ };
420
+ }
421
+ // Parse verification result (simplified)
422
+ const output = result.output;
423
+ const criticalMatch = output.match(/Critical:\s*(\d+)/i);
424
+ const majorMatch = output.match(/Major:\s*(\d+)/i);
425
+ const minorMatch = output.match(/Minor:\s*(\d+)/i);
426
+ const passedMatch = output.match(/Passed:\s*(yes|no)/i);
427
+ const critical = criticalMatch ? parseInt(criticalMatch[1]) : 0;
428
+ const major = majorMatch ? parseInt(majorMatch[1]) : 0;
429
+ const minor = minorMatch ? parseInt(minorMatch[1]) : 0;
430
+ const passed = passedMatch ? passedMatch[1].toLowerCase() === 'yes' : (critical === 0 && major === 0);
431
+ // Create verification result
432
+ const verificationResult = {
433
+ id: `ver_${nanoid(8)}`,
434
+ phaseId: plan.phaseId,
435
+ planId,
436
+ passed,
437
+ timestamp: Date.now(),
438
+ issues: [], // Would be parsed from output
439
+ summary: {
440
+ critical,
441
+ major,
442
+ minor,
443
+ total: critical + major + minor,
444
+ },
445
+ filesChecked: files || plan.files.map(f => f.path),
446
+ duration: Date.now() - startTime,
447
+ };
448
+ verificationResults.set(verificationResult.id, verificationResult);
449
+ // Update phase status
450
+ const phase = phases.get(plan.phaseId);
451
+ if (phase) {
452
+ phase.status = passed ? 'passed' : 'fixing';
453
+ phases.set(phase.id, phase);
454
+ }
455
+ const statusEmoji = passed ? '✅' : '❌';
456
+ let responseOutput = `# Verification ${verificationResult.id} ${statusEmoji}\n\n`;
457
+ responseOutput += `**Plan**: ${planId}\n`;
458
+ responseOutput += `**Phase**: ${plan.phaseId}\n`;
459
+ responseOutput += `**Duration**: ${verificationResult.duration}ms\n\n`;
460
+ responseOutput += result.output;
461
+ return {
462
+ content: [{ type: 'text', text: responseOutput }],
463
+ };
464
+ });
465
+ // seldon_fix
466
+ server.tool('seldon_fix', 'Generate fixes for verification issues. Returns code changes to apply.', {
467
+ verificationId: z.string().describe('Verification result ID'),
468
+ severityThreshold: IssueSeveritySchema.optional().describe('Only fix issues at or above this severity (default: major)'),
469
+ autoApply: z.boolean().optional().describe('Automatically apply fixes (default: false)'),
470
+ }, async ({ verificationId, severityThreshold = 'major', autoApply = false }) => {
471
+ const verification = verificationResults.get(verificationId);
472
+ if (!verification) {
473
+ return {
474
+ content: [{ type: 'text', text: `Verification result not found: ${verificationId}` }],
475
+ };
476
+ }
477
+ const plan = plans.get(verification.planId);
478
+ if (!plan) {
479
+ return {
480
+ content: [{ type: 'text', text: `Plan not found: ${verification.planId}` }],
481
+ };
482
+ }
483
+ // Filter issues by severity
484
+ const severityOrder = ['critical', 'major', 'minor'];
485
+ const thresholdIndex = severityOrder.indexOf(severityThreshold);
486
+ const issuesToFix = verification.issues.filter(issue => severityOrder.indexOf(issue.severity) <= thresholdIndex);
487
+ if (issuesToFix.length === 0) {
488
+ return {
489
+ content: [{ type: 'text', text: `No issues at or above '${severityThreshold}' severity to fix.` }],
490
+ };
491
+ }
492
+ // Generate fix prompt
493
+ const fixPrompt = `You are a senior developer fixing verification issues. For each issue, provide the exact code fix.
494
+
495
+ ## ORIGINAL PLAN
496
+ ${plan.description}
497
+
498
+ ## ISSUES TO FIX
499
+ ${issuesToFix.map((issue, i) => `
500
+ ### Issue ${i + 1}: ${issue.severity.toUpperCase()}
501
+ - **Category**: ${issue.category}
502
+ - **File**: ${issue.file}
503
+ - **Line**: ${issue.line || 'N/A'}
504
+ - **Message**: ${issue.message}
505
+ - **Suggestion**: ${issue.suggestion || 'None provided'}
506
+ `).join('\n')}
507
+
508
+ ## TASK
509
+ For each issue, provide:
510
+ 1. The file to modify
511
+ 2. The original code (if modifying existing code)
512
+ 3. The fixed code
513
+ 4. Brief explanation
514
+
515
+ Format your response as:
516
+
517
+ ### Fix for Issue 1
518
+ **File**: [path]
519
+ **Confidence**: high | medium | low
520
+
521
+ \`\`\`[language]
522
+ // ORIGINAL (lines X-Y)
523
+ [original code]
524
+
525
+ // FIXED
526
+ [fixed code]
527
+ \`\`\`
528
+
529
+ **Explanation**: [why this fix works]
530
+
531
+ ### Fix for Issue 2
532
+ ...`;
533
+ const result = await providers.invoke({
534
+ role: 'coder',
535
+ task: fixPrompt,
536
+ });
537
+ if (!result.success) {
538
+ return {
539
+ content: [{ type: 'text', text: `Error generating fixes: ${result.error}` }],
540
+ };
541
+ }
542
+ // Create fix result
543
+ const fixes = issuesToFix.map((issue, i) => ({
544
+ issueId: issue.id,
545
+ file: issue.file,
546
+ originalCode: undefined, // Would be parsed
547
+ fixedCode: '', // Would be parsed from result
548
+ explanation: '', // Would be parsed
549
+ confidence: 'medium',
550
+ }));
551
+ let output = `# Fixes for Verification ${verificationId}\n\n`;
552
+ output += `**Issues Fixed**: ${issuesToFix.length}\n`;
553
+ output += `**Severity Threshold**: ${severityThreshold}\n`;
554
+ output += `**Auto-Apply**: ${autoApply}\n\n`;
555
+ output += result.output;
556
+ if (autoApply) {
557
+ output += `\n\n---\n⚠️ Auto-apply is enabled but not yet implemented. Fixes shown above for manual application.`;
558
+ }
559
+ return {
560
+ content: [{ type: 'text', text: output }],
561
+ };
562
+ });
563
+ // seldon_execute_verified
564
+ server.tool('seldon_execute_verified', 'Execute a phase with verification loop (YOLO mode). Automatically verifies and fixes until passing or max attempts.', {
565
+ phaseId: z.string().describe('Phase ID to execute'),
566
+ maxAttempts: z.number().optional().describe('Maximum verification/fix cycles (default: 3)'),
567
+ severityThreshold: IssueSeveritySchema.optional().describe('Stop on issues at this severity (default: major)'),
568
+ autoFix: z.boolean().optional().describe('Automatically apply fixes (default: true)'),
569
+ runTests: z.boolean().optional().describe('Run tests during verification (default: true)'),
570
+ }, async ({ phaseId, maxAttempts = 3, severityThreshold = 'major', autoFix = true, runTests = true }) => {
571
+ const phase = phases.get(phaseId);
572
+ if (!phase) {
573
+ return {
574
+ content: [{ type: 'text', text: `Phase not found: ${phaseId}` }],
575
+ };
576
+ }
577
+ if (!phase.planId) {
578
+ return {
579
+ content: [{ type: 'text', text: `Phase ${phaseId} has no plan. Use \`seldon_plan\` first.` }],
580
+ };
581
+ }
582
+ const plan = plans.get(phase.planId);
583
+ if (!plan) {
584
+ return {
585
+ content: [{ type: 'text', text: `Plan not found: ${phase.planId}` }],
586
+ };
587
+ }
588
+ const startTime = Date.now();
589
+ const attempts = [];
590
+ let currentAttempt = 0;
591
+ let finalStatus = 'error';
592
+ // Update phase status
593
+ phase.status = 'executing';
594
+ phase.startedAt = Date.now();
595
+ phases.set(phase.id, phase);
596
+ // The Loop
597
+ while (currentAttempt < maxAttempts) {
598
+ currentAttempt++;
599
+ const attemptStart = Date.now();
600
+ // Step 1: Execute (invoke coder with plan)
601
+ if (currentAttempt === 1) {
602
+ phase.status = 'executing';
603
+ phases.set(phase.id, phase);
604
+ // In real implementation, this would invoke the coder
605
+ // For now, we simulate
606
+ }
607
+ // Step 2: Verify
608
+ phase.status = 'verifying';
609
+ phases.set(phase.id, phase);
610
+ const verifyPrompt = `Verify implementation of phase "${phase.name}" against plan.`;
611
+ const verifyResult = await providers.invoke({
612
+ role: 'verifier',
613
+ task: verifyPrompt,
614
+ context: plan.description,
615
+ });
616
+ // Parse verification (simplified)
617
+ const passed = verifyResult.output.toLowerCase().includes('passed: yes') ||
618
+ verifyResult.output.toLowerCase().includes('no issues found');
619
+ const verificationResult = {
620
+ id: `ver_${nanoid(8)}`,
621
+ phaseId: phase.id,
622
+ planId: plan.id,
623
+ passed,
624
+ timestamp: Date.now(),
625
+ issues: [],
626
+ summary: { critical: 0, major: 0, minor: 0, total: 0 },
627
+ filesChecked: plan.files.map(f => f.path),
628
+ duration: Date.now() - attemptStart,
629
+ };
630
+ verificationResults.set(verificationResult.id, verificationResult);
631
+ if (passed) {
632
+ attempts.push({
633
+ attemptNumber: currentAttempt,
634
+ startedAt: attemptStart,
635
+ completedAt: Date.now(),
636
+ verificationResult,
637
+ outcome: 'passed',
638
+ });
639
+ finalStatus = 'passed';
640
+ break;
641
+ }
642
+ // Step 3: Fix if not passed and autoFix enabled
643
+ if (autoFix && currentAttempt < maxAttempts) {
644
+ phase.status = 'fixing';
645
+ phases.set(phase.id, phase);
646
+ const fixPrompt = `Fix issues found in verification:\n\n${verifyResult.output}`;
647
+ const fixResult = await providers.invoke({
648
+ role: 'coder',
649
+ task: fixPrompt,
650
+ context: plan.description,
651
+ });
652
+ attempts.push({
653
+ attemptNumber: currentAttempt,
654
+ startedAt: attemptStart,
655
+ completedAt: Date.now(),
656
+ verificationResult,
657
+ outcome: 'fixed_and_retrying',
658
+ });
659
+ }
660
+ else {
661
+ attempts.push({
662
+ attemptNumber: currentAttempt,
663
+ startedAt: attemptStart,
664
+ completedAt: Date.now(),
665
+ verificationResult,
666
+ outcome: 'failed',
667
+ });
668
+ }
669
+ }
670
+ // Final status
671
+ if (finalStatus !== 'passed') {
672
+ finalStatus = currentAttempt >= maxAttempts ? 'max_attempts' : 'failed';
673
+ }
674
+ phase.status = finalStatus === 'passed' ? 'passed' : 'failed';
675
+ phase.completedAt = Date.now();
676
+ phases.set(phase.id, phase);
677
+ const result = {
678
+ phaseId: phase.id,
679
+ status: finalStatus,
680
+ attempts,
681
+ totalDuration: Date.now() - startTime,
682
+ finalVerification: verificationResults.get(attempts[attempts.length - 1]?.verificationResult.id),
683
+ };
684
+ // Build output
685
+ const statusEmoji = finalStatus === 'passed' ? '✅' : finalStatus === 'max_attempts' ? '⚠️' : '❌';
686
+ let output = `# Execute Verified ${statusEmoji}\n\n`;
687
+ output += `**Phase**: ${phase.name} (${phase.id})\n`;
688
+ output += `**Status**: ${finalStatus}\n`;
689
+ output += `**Attempts**: ${attempts.length}/${maxAttempts}\n`;
690
+ output += `**Duration**: ${result.totalDuration}ms\n\n`;
691
+ output += `## Attempt History\n\n`;
692
+ for (const attempt of attempts) {
693
+ const emoji = attempt.outcome === 'passed' ? '✓' : attempt.outcome === 'fixed_and_retrying' ? '🔄' : '✗';
694
+ output += `### Attempt ${attempt.attemptNumber} ${emoji}\n`;
695
+ output += `- Outcome: ${attempt.outcome}\n`;
696
+ output += `- Duration: ${attempt.completedAt - attempt.startedAt}ms\n`;
697
+ output += `- Verification: ${attempt.verificationResult.passed ? 'passed' : 'failed'}\n\n`;
698
+ }
699
+ return {
700
+ content: [{ type: 'text', text: output }],
701
+ };
702
+ });
703
+ // seldon_execute_parallel
704
+ server.tool('seldon_execute_parallel', 'Execute multiple phases in parallel, each in its own worktree. Merges when all pass.', {
705
+ phaseIds: z.array(z.string()).describe('Phase IDs to execute in parallel'),
706
+ worktreePerPhase: z.boolean().optional().describe('Create separate worktree per phase (default: true)'),
707
+ maxAttempts: z.number().optional().describe('Max verification attempts per phase (default: 3)'),
708
+ severityThreshold: IssueSeveritySchema.optional().describe('Severity threshold for all phases (default: major)'),
709
+ mergeStrategy: z.enum(['sequential', 'parallel', 'manual']).optional().describe('How to merge completed phases (default: sequential)'),
710
+ }, async ({ phaseIds, worktreePerPhase = true, maxAttempts = 3, severityThreshold = 'major', mergeStrategy = 'sequential' }) => {
711
+ const startTime = Date.now();
712
+ // Validate all phases exist
713
+ const phasesToExecute = [];
714
+ for (const id of phaseIds) {
715
+ const phase = phases.get(id);
716
+ if (!phase) {
717
+ return {
718
+ content: [{ type: 'text', text: `Phase not found: ${id}` }],
719
+ };
720
+ }
721
+ phasesToExecute.push(phase);
722
+ }
723
+ // Create worktrees if requested
724
+ const worktreeAssignments = new Map();
725
+ if (worktreePerPhase) {
726
+ for (const phase of phasesToExecute) {
727
+ try {
728
+ const repoRoot = await git.getRepoRoot();
729
+ const worktreePath = `${repoRoot}-phase-${phase.id.slice(-4)}`;
730
+ const branchName = `phase/${phase.id}`;
731
+ // Would create worktree here in real implementation
732
+ worktreeAssignments.set(phase.id, worktreePath);
733
+ phase.worktreeId = `wt_${nanoid(8)}`;
734
+ phases.set(phase.id, phase);
735
+ }
736
+ catch (error) {
737
+ // Git operations may fail, continue anyway
738
+ }
739
+ }
740
+ }
741
+ // Execute phases in parallel
742
+ let output = `# Parallel Execution\n\n`;
743
+ output += `**Phases**: ${phaseIds.length}\n`;
744
+ output += `**Worktrees**: ${worktreePerPhase ? 'Yes' : 'No'}\n`;
745
+ output += `**Max Attempts**: ${maxAttempts}\n`;
746
+ output += `**Merge Strategy**: ${mergeStrategy}\n\n`;
747
+ output += `## Phase Status\n\n`;
748
+ output += `| Phase | Status | Worktree | Attempts |\n`;
749
+ output += `|-------|--------|----------|----------|\n`;
750
+ // In real implementation, these would run concurrently
751
+ // For now, we show what would happen
752
+ for (const phase of phasesToExecute) {
753
+ const worktree = worktreeAssignments.get(phase.id) || '-';
754
+ output += `| ${phase.id} | ⏳ pending | ${worktree.slice(-20)} | 0/${maxAttempts} |\n`;
755
+ }
756
+ output += `\n---\n\n`;
757
+ output += `⚠️ **Parallel execution is simulated**. In production, each phase would:\n\n`;
758
+ output += `1. Run in its own worktree\n`;
759
+ output += `2. Execute with verification loop independently\n`;
760
+ output += `3. Report progress via status updates\n`;
761
+ output += `4. Merge when all phases pass (strategy: ${mergeStrategy})\n`;
762
+ return {
763
+ content: [{ type: 'text', text: output }],
764
+ };
765
+ });
766
+ // ===========================================================================
767
+ // Pipeline Tools
768
+ // ===========================================================================
769
+ // seldon_pipeline_create
770
+ server.tool('seldon_pipeline_create', 'Create a multi-step DAG workflow with dependencies between steps.', {
771
+ name: z.string().describe('Pipeline name'),
772
+ steps: z.array(z.object({
773
+ id: z.string().describe('Unique step identifier'),
774
+ role: AgentRoleSchema.describe('Agent role for this step'),
775
+ task: z.string().describe('Task description'),
776
+ dependsOn: z.array(z.string()).optional().describe('Step IDs this depends on'),
777
+ })).describe('Pipeline steps with dependencies'),
778
+ }, async ({ name, steps }) => {
779
+ // TODO: Port full implementation from hari-seldon
780
+ const pipelineId = `pipe_${nanoid(8)}`;
781
+ return {
782
+ content: [
783
+ {
784
+ type: 'text',
785
+ text: `✓ Created pipeline "${name}" (${pipelineId}) with ${steps.length} steps\n\nSteps:\n${steps.map(s => `- ${s.id}: ${s.role} → ${s.dependsOn?.join(', ') || 'none'}`).join('\n')}`,
786
+ },
787
+ ],
788
+ };
789
+ });
790
+ // seldon_pipeline_execute
791
+ server.tool('seldon_pipeline_execute', 'Execute a previously created pipeline.', {
792
+ pipelineId: z.string().describe('Pipeline ID to execute'),
793
+ context: z.string().optional().describe('Initial context for the pipeline'),
794
+ }, async ({ pipelineId, context }) => {
795
+ // TODO: Port implementation from hari-seldon
796
+ return {
797
+ content: [
798
+ {
799
+ type: 'text',
800
+ text: `[Stub] Would execute pipeline ${pipelineId}`,
801
+ },
802
+ ],
803
+ };
804
+ });
805
+ // seldon_pipeline_status
806
+ server.tool('seldon_pipeline_status', 'Get the status of a pipeline execution.', {
807
+ pipelineId: z.string().describe('Pipeline ID to check'),
808
+ }, async ({ pipelineId }) => {
809
+ // TODO: Port implementation from hari-seldon
810
+ return {
811
+ content: [
812
+ {
813
+ type: 'text',
814
+ text: `[Stub] Would get status of pipeline ${pipelineId}`,
815
+ },
816
+ ],
817
+ };
818
+ });
819
+ // ===========================================================================
820
+ // Task Management Tools
821
+ // ===========================================================================
822
+ // seldon_task_execute
823
+ server.tool('seldon_task_execute', 'Execute a coding task in an isolated git worktree.', {
824
+ role: AgentRoleSchema.describe('Agent role (usually "coder")'),
825
+ task: z.string().describe('Task description'),
826
+ baseBranch: z.string().optional().describe('Base branch for worktree (default: main)'),
827
+ }, async ({ role, task, baseBranch = 'main' }) => {
828
+ // TODO: Port implementation from hari-seldon
829
+ return {
830
+ content: [
831
+ {
832
+ type: 'text',
833
+ text: `[Stub] Would execute task with ${role} in worktree based on ${baseBranch}`,
834
+ },
835
+ ],
836
+ };
837
+ });
838
+ // seldon_task_claim
839
+ server.tool('seldon_task_claim', 'Claim the next available task from the queue (for worker pattern).', {
840
+ role: AgentRoleSchema.optional().describe('Only claim tasks for this role'),
841
+ }, async ({ role }) => {
842
+ // TODO: Port implementation from hari-seldon
843
+ return {
844
+ content: [
845
+ {
846
+ type: 'text',
847
+ text: `[Stub] Would claim next task${role ? ` for role ${role}` : ''}`,
848
+ },
849
+ ],
850
+ };
851
+ });
852
+ // ===========================================================================
853
+ // Provider Tools
854
+ // ===========================================================================
855
+ // seldon_providers_list
856
+ server.tool('seldon_providers_list', 'List all available AI providers and their health status.', {}, async () => {
857
+ const available = providers.listProviders();
858
+ const health = providers.getProviderHealth();
859
+ let output = '## Available Providers\n\n';
860
+ for (const p of available) {
861
+ const h = health.find((h) => h.provider === p);
862
+ const status = h?.healthy ? '✓' : h ? '✗' : '?';
863
+ output += `- **${p}** ${status}\n`;
864
+ }
865
+ return {
866
+ content: [{ type: 'text', text: output }],
867
+ };
868
+ });
869
+ // seldon_providers_test
870
+ server.tool('seldon_providers_test', 'Test connectivity to a specific provider.', {
871
+ provider: z.string().describe('Provider name to test'),
872
+ }, async ({ provider }) => {
873
+ const result = await providers.testProvider(provider);
874
+ return {
875
+ content: [
876
+ {
877
+ type: 'text',
878
+ text: result.success
879
+ ? `✓ ${provider} is healthy (${result.latencyMs}ms)`
880
+ : `✗ ${provider} failed: ${result.error}`,
881
+ },
882
+ ],
883
+ };
884
+ });
885
+ }
886
+ //# sourceMappingURL=index.js.map