@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.
- package/LICENSE +21 -0
- package/README.md +341 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +308 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +78 -0
- package/dist/index.js.map +1 -0
- package/dist/services/config.service.d.ts +92 -0
- package/dist/services/config.service.d.ts.map +1 -0
- package/dist/services/config.service.js +284 -0
- package/dist/services/config.service.js.map +1 -0
- package/dist/services/git.service.d.ts +175 -0
- package/dist/services/git.service.d.ts.map +1 -0
- package/dist/services/git.service.js +395 -0
- package/dist/services/git.service.js.map +1 -0
- package/dist/services/provider.service.d.ts +74 -0
- package/dist/services/provider.service.d.ts.map +1 -0
- package/dist/services/provider.service.js +182 -0
- package/dist/services/provider.service.js.map +1 -0
- package/dist/services/storage.service.d.ts +84 -0
- package/dist/services/storage.service.d.ts.map +1 -0
- package/dist/services/storage.service.js +320 -0
- package/dist/services/storage.service.js.map +1 -0
- package/dist/tools/demerzel/index.d.ts +21 -0
- package/dist/tools/demerzel/index.d.ts.map +1 -0
- package/dist/tools/demerzel/index.js +166 -0
- package/dist/tools/demerzel/index.js.map +1 -0
- package/dist/tools/gaia/index.d.ts +27 -0
- package/dist/tools/gaia/index.d.ts.map +1 -0
- package/dist/tools/gaia/index.js +466 -0
- package/dist/tools/gaia/index.js.map +1 -0
- package/dist/tools/seldon/index.d.ts +43 -0
- package/dist/tools/seldon/index.d.ts.map +1 -0
- package/dist/tools/seldon/index.js +886 -0
- package/dist/tools/seldon/index.js.map +1 -0
- package/dist/types/index.d.ts +392 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +10 -0
- package/dist/types/index.js.map +1 -0
- 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
|