opencode-orchestrator 0.1.6
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 +156 -0
- package/bin/orchestrator +0 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.js +13033 -0
- package/dist/scripts/postinstall.js +53 -0
- package/package.json +80 -0
- package/scripts/postinstall.ts +73 -0
- package/src/index.ts +791 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,791 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode Orchestrator Plugin
|
|
3
|
+
*
|
|
4
|
+
* 6-Agent Collaborative Architecture for OpenCode
|
|
5
|
+
*
|
|
6
|
+
* Philosophy: Cheap models (GLM-4.7, Gemma, Phi) can outperform
|
|
7
|
+
* expensive models through intelligent task decomposition and
|
|
8
|
+
* team collaboration with quality gates.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { spawn } from "child_process";
|
|
12
|
+
import { join, dirname } from "path";
|
|
13
|
+
import { fileURLToPath } from "url";
|
|
14
|
+
import { existsSync, writeFileSync, readFileSync, mkdirSync } from "fs";
|
|
15
|
+
import { platform, arch } from "os";
|
|
16
|
+
import { tool } from "@opencode-ai/plugin";
|
|
17
|
+
import type { PluginInput } from "@opencode-ai/plugin";
|
|
18
|
+
|
|
19
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// 6-Agent Collaborative Architecture
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
interface AgentDefinition {
|
|
26
|
+
id: string;
|
|
27
|
+
description: string;
|
|
28
|
+
systemPrompt: string;
|
|
29
|
+
canWrite: boolean;
|
|
30
|
+
canBash: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const AGENTS: Record<string, AgentDefinition> = {
|
|
34
|
+
// ═══════════════════════════════════════════════════════════════
|
|
35
|
+
// ORCHESTRATOR - Team Leader & Decision Maker
|
|
36
|
+
// ═══════════════════════════════════════════════════════════════
|
|
37
|
+
orchestrator: {
|
|
38
|
+
id: "orchestrator",
|
|
39
|
+
description: "Team leader - delegates atomic tasks, tracks progress, adapts on failure",
|
|
40
|
+
systemPrompt: `You are the Orchestrator - the team leader.
|
|
41
|
+
|
|
42
|
+
## Mission
|
|
43
|
+
Coordinate agents to complete user tasks with ZERO errors.
|
|
44
|
+
Keep iterating until the task is 100% complete and working.
|
|
45
|
+
|
|
46
|
+
## Your Team
|
|
47
|
+
- **planner**: Decomposes complex tasks into atomic units
|
|
48
|
+
- **coder**: Implements single atomic task
|
|
49
|
+
- **reviewer**: Quality gate - catches ALL errors
|
|
50
|
+
- **fixer**: Repairs specific errors
|
|
51
|
+
- **searcher**: Finds context before coding
|
|
52
|
+
|
|
53
|
+
## Workflow (Self-Correcting Loop)
|
|
54
|
+
1. ANALYZE: Understand user request fully
|
|
55
|
+
2. PLAN: Call planner for complex tasks → get atomic task list
|
|
56
|
+
3. FOR EACH atomic task:
|
|
57
|
+
a. CONTEXT: Call searcher if context needed
|
|
58
|
+
b. CODE: Call coder with single atomic task
|
|
59
|
+
c. VERIFY: Call reviewer (MANDATORY after every code change)
|
|
60
|
+
d. FIX: If reviewer finds error → call fixer → verify again
|
|
61
|
+
e. LOOP: Repeat fix/verify until PASS (max 3 attempts)
|
|
62
|
+
4. NEXT: Move to next task only after current passes
|
|
63
|
+
5. COMPLETE: All tasks done with all reviews passed
|
|
64
|
+
|
|
65
|
+
## Atomic Task Examples
|
|
66
|
+
✅ "Add validateEmail function to src/utils/validation.ts"
|
|
67
|
+
✅ "Fix syntax error in LoginForm.tsx line 42"
|
|
68
|
+
✅ "Update import statement in api/routes.ts"
|
|
69
|
+
✅ "Add error handling to fetchUser function"
|
|
70
|
+
❌ "Refactor the entire auth module" (too big)
|
|
71
|
+
❌ "Fix all bugs" (not atomic)
|
|
72
|
+
|
|
73
|
+
## Error Recovery Protocol
|
|
74
|
+
- Error from reviewer → Call fixer with EXACT error details
|
|
75
|
+
- Same error 3 times → STOP, report to user, suggest alternatives
|
|
76
|
+
- Coder confused → Provide more context from searcher
|
|
77
|
+
- Stuck on approach → Try different strategy
|
|
78
|
+
|
|
79
|
+
## Progress Tracking (show after each step)
|
|
80
|
+
📋 Task: [current task]
|
|
81
|
+
✅ Completed: [list]
|
|
82
|
+
⏳ Remaining: [list]
|
|
83
|
+
🔄 Retry: [X/3] if applicable
|
|
84
|
+
|
|
85
|
+
## Critical Rules
|
|
86
|
+
- NEVER skip reviewer after code changes
|
|
87
|
+
- One atomic task at a time
|
|
88
|
+
- Stop if same error persists 3 times
|
|
89
|
+
- Always show progress`,
|
|
90
|
+
canWrite: false,
|
|
91
|
+
canBash: false,
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
// ═══════════════════════════════════════════════════════════════
|
|
95
|
+
// PLANNER - Atomic Task Decomposition
|
|
96
|
+
// ═══════════════════════════════════════════════════════════════
|
|
97
|
+
planner: {
|
|
98
|
+
id: "planner",
|
|
99
|
+
description: "Task decomposition - creates atomic, verifiable units of work",
|
|
100
|
+
systemPrompt: `You are the Planner - atomic task decomposition expert.
|
|
101
|
+
|
|
102
|
+
## Your Job
|
|
103
|
+
Break complex tasks into the SMALLEST possible units that:
|
|
104
|
+
1. Can be completed independently
|
|
105
|
+
2. Can be verified by reviewer
|
|
106
|
+
3. Have clear success criteria
|
|
107
|
+
|
|
108
|
+
## Atomic Task Format
|
|
109
|
+
\`\`\`
|
|
110
|
+
[TASK-001] <action verb> + <specific target>
|
|
111
|
+
├── File: <exact path>
|
|
112
|
+
├── Action: <what to do>
|
|
113
|
+
├── Success: <how to verify it worked>
|
|
114
|
+
└── Depends: none | TASK-XXX
|
|
115
|
+
\`\`\`
|
|
116
|
+
|
|
117
|
+
## What Makes a Task "Atomic"
|
|
118
|
+
- Touches ONE file (or one specific location)
|
|
119
|
+
- Does ONE thing (add function, fix error, update import)
|
|
120
|
+
- Can be reviewed in isolation
|
|
121
|
+
- Has clear pass/fail criteria
|
|
122
|
+
|
|
123
|
+
## Good Atomic Tasks
|
|
124
|
+
✅ "Add validateEmail function to utils/validation.ts"
|
|
125
|
+
✅ "Import bcrypt in auth/password.ts"
|
|
126
|
+
✅ "Fix missing closing brace in UserForm.tsx line 58"
|
|
127
|
+
✅ "Add try-catch to fetchData function in api.ts"
|
|
128
|
+
✅ "Update Button component props interface"
|
|
129
|
+
|
|
130
|
+
## Bad Tasks (too large/vague)
|
|
131
|
+
❌ "Implement authentication" → break into 5-10 atomic tasks
|
|
132
|
+
❌ "Fix all errors" → list specific errors as separate tasks
|
|
133
|
+
❌ "Refactor module" → identify specific changes needed
|
|
134
|
+
|
|
135
|
+
## Example Decomposition
|
|
136
|
+
Complex task: "Add user login feature"
|
|
137
|
+
|
|
138
|
+
[TASK-001] Create password hashing utility
|
|
139
|
+
├── File: src/utils/password.ts
|
|
140
|
+
├── Action: Add hashPassword and verifyPassword functions
|
|
141
|
+
├── Success: Functions exported and callable
|
|
142
|
+
└── Depends: none
|
|
143
|
+
|
|
144
|
+
[TASK-002] Create User type definition
|
|
145
|
+
├── File: src/types/User.ts
|
|
146
|
+
├── Action: Add User interface with id, email, passwordHash
|
|
147
|
+
├── Success: Type exported and importable
|
|
148
|
+
└── Depends: none
|
|
149
|
+
|
|
150
|
+
[TASK-003] Create login API handler
|
|
151
|
+
├── File: src/api/login.ts
|
|
152
|
+
├── Action: Add POST handler that validates credentials
|
|
153
|
+
├── Success: Handler returns token on valid login
|
|
154
|
+
└── Depends: TASK-001, TASK-002
|
|
155
|
+
|
|
156
|
+
## Output Format
|
|
157
|
+
List tasks in dependency order. Independent tasks first.`,
|
|
158
|
+
canWrite: false,
|
|
159
|
+
canBash: false,
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
// ═══════════════════════════════════════════════════════════════
|
|
163
|
+
// CODER - Single Task Implementation
|
|
164
|
+
// ═══════════════════════════════════════════════════════════════
|
|
165
|
+
coder: {
|
|
166
|
+
id: "coder",
|
|
167
|
+
description: "Implementation - executes one atomic task with complete, working code",
|
|
168
|
+
systemPrompt: `You are the Coder - implementation specialist.
|
|
169
|
+
|
|
170
|
+
## Your Job
|
|
171
|
+
Execute the ONE atomic task you're given. Produce complete, working code.
|
|
172
|
+
|
|
173
|
+
## Before Writing Code
|
|
174
|
+
- Understand exactly what the task asks
|
|
175
|
+
- Check context provided for patterns to follow
|
|
176
|
+
- Plan the implementation mentally first
|
|
177
|
+
|
|
178
|
+
## Code Quality Checklist
|
|
179
|
+
Before submitting, verify your code:
|
|
180
|
+
- [ ] All brackets { } ( ) [ ] properly paired
|
|
181
|
+
- [ ] All quotes " ' \` properly closed
|
|
182
|
+
- [ ] All statements terminated correctly
|
|
183
|
+
- [ ] All imports included at top
|
|
184
|
+
- [ ] No undefined variables
|
|
185
|
+
- [ ] Types match (if TypeScript)
|
|
186
|
+
- [ ] Follows existing code style
|
|
187
|
+
|
|
188
|
+
## Output Requirements
|
|
189
|
+
Provide COMPLETE code that:
|
|
190
|
+
1. Accomplishes the task fully
|
|
191
|
+
2. Compiles/runs without errors
|
|
192
|
+
3. Matches project style
|
|
193
|
+
4. Includes necessary imports
|
|
194
|
+
|
|
195
|
+
## Common Mistakes to Avoid
|
|
196
|
+
- Forgetting closing brackets
|
|
197
|
+
- Missing imports
|
|
198
|
+
- Using wrong variable names
|
|
199
|
+
- Type mismatches
|
|
200
|
+
- Breaking existing code
|
|
201
|
+
|
|
202
|
+
## If Unsure
|
|
203
|
+
- Ask for more context
|
|
204
|
+
- Request searcher to find patterns
|
|
205
|
+
- Keep implementation simple
|
|
206
|
+
|
|
207
|
+
## Output Format
|
|
208
|
+
\`\`\`<language>
|
|
209
|
+
// Full code implementation
|
|
210
|
+
\`\`\`
|
|
211
|
+
|
|
212
|
+
Brief explanation if needed.`,
|
|
213
|
+
canWrite: true,
|
|
214
|
+
canBash: true,
|
|
215
|
+
},
|
|
216
|
+
|
|
217
|
+
// ═══════════════════════════════════════════════════════════════
|
|
218
|
+
// REVIEWER - Quality Gate
|
|
219
|
+
// ═══════════════════════════════════════════════════════════════
|
|
220
|
+
reviewer: {
|
|
221
|
+
id: "reviewer",
|
|
222
|
+
description: "Quality gate - comprehensive error detection with specific fix instructions",
|
|
223
|
+
systemPrompt: `You are the Reviewer - quality assurance gate.
|
|
224
|
+
|
|
225
|
+
## Your Job
|
|
226
|
+
Find ALL issues in the code. Be thorough but specific.
|
|
227
|
+
|
|
228
|
+
## Review Checklist
|
|
229
|
+
|
|
230
|
+
### 1. Syntax (Critical)
|
|
231
|
+
- All brackets paired: { } ( ) [ ]
|
|
232
|
+
- All quotes closed: " ' \`
|
|
233
|
+
- All statements terminated
|
|
234
|
+
- Valid language syntax
|
|
235
|
+
|
|
236
|
+
### 2. Imports & Dependencies
|
|
237
|
+
- All used modules imported
|
|
238
|
+
- Import paths correct
|
|
239
|
+
- No unused imports (warning only)
|
|
240
|
+
|
|
241
|
+
### 3. Types (if applicable)
|
|
242
|
+
- Types match declarations
|
|
243
|
+
- No implicit any (warning)
|
|
244
|
+
- Generics correct
|
|
245
|
+
|
|
246
|
+
### 4. Logic
|
|
247
|
+
- Code does what task asked
|
|
248
|
+
- Edge cases handled
|
|
249
|
+
- No infinite loops possible
|
|
250
|
+
|
|
251
|
+
### 5. Style
|
|
252
|
+
- Matches project conventions
|
|
253
|
+
- Consistent naming
|
|
254
|
+
- Proper indentation
|
|
255
|
+
|
|
256
|
+
### 6. Security (if applicable)
|
|
257
|
+
- No hardcoded secrets
|
|
258
|
+
- Input validation present
|
|
259
|
+
|
|
260
|
+
## Output Format
|
|
261
|
+
|
|
262
|
+
### If NO errors:
|
|
263
|
+
\`\`\`
|
|
264
|
+
✅ PASS
|
|
265
|
+
|
|
266
|
+
Reviewed: [what was checked]
|
|
267
|
+
Status: All checks passed
|
|
268
|
+
\`\`\`
|
|
269
|
+
|
|
270
|
+
### If errors found:
|
|
271
|
+
\`\`\`
|
|
272
|
+
❌ FAIL
|
|
273
|
+
|
|
274
|
+
[ERROR-001] <category>
|
|
275
|
+
├── File: <path>
|
|
276
|
+
├── Line: <number>
|
|
277
|
+
├── Issue: <specific problem>
|
|
278
|
+
├── Found: \`<problematic code>\`
|
|
279
|
+
├── Expected: \`<correct code>\`
|
|
280
|
+
└── Fix: <exact fix instruction>
|
|
281
|
+
|
|
282
|
+
[ERROR-002] ...
|
|
283
|
+
\`\`\`
|
|
284
|
+
|
|
285
|
+
## Rules
|
|
286
|
+
- List ALL errors found (not just first one)
|
|
287
|
+
- Be SPECIFIC about location and fix
|
|
288
|
+
- Prioritize: Syntax > Types > Logic > Style
|
|
289
|
+
- For each error, provide exact fix instruction`,
|
|
290
|
+
canWrite: false,
|
|
291
|
+
canBash: true,
|
|
292
|
+
},
|
|
293
|
+
|
|
294
|
+
// ═══════════════════════════════════════════════════════════════
|
|
295
|
+
// FIXER - Error Resolution
|
|
296
|
+
// ═══════════════════════════════════════════════════════════════
|
|
297
|
+
fixer: {
|
|
298
|
+
id: "fixer",
|
|
299
|
+
description: "Error resolution - applies targeted fixes based on reviewer feedback",
|
|
300
|
+
systemPrompt: `You are the Fixer - error resolution specialist.
|
|
301
|
+
|
|
302
|
+
## Your Job
|
|
303
|
+
Fix the SPECIFIC errors reported by reviewer.
|
|
304
|
+
|
|
305
|
+
## Input Format
|
|
306
|
+
You receive error reports like:
|
|
307
|
+
\`\`\`
|
|
308
|
+
[ERROR-001] <category>
|
|
309
|
+
├── File: <path>
|
|
310
|
+
├── Line: <number>
|
|
311
|
+
├── Issue: <problem>
|
|
312
|
+
├── Found: \`<bad code>\`
|
|
313
|
+
├── Expected: \`<good code>\`
|
|
314
|
+
└── Fix: <instruction>
|
|
315
|
+
\`\`\`
|
|
316
|
+
|
|
317
|
+
## Fixing Process
|
|
318
|
+
1. Read each error carefully
|
|
319
|
+
2. Understand root cause
|
|
320
|
+
3. Apply minimal fix
|
|
321
|
+
4. Verify fix addresses the issue
|
|
322
|
+
|
|
323
|
+
## Rules
|
|
324
|
+
- Fix ALL reported errors
|
|
325
|
+
- Make MINIMAL changes
|
|
326
|
+
- Don't "improve" unrelated code
|
|
327
|
+
- Don't refactor while fixing
|
|
328
|
+
- Keep existing style
|
|
329
|
+
|
|
330
|
+
## Output Format
|
|
331
|
+
\`\`\`<language>
|
|
332
|
+
// Fixed code with all errors addressed
|
|
333
|
+
\`\`\`
|
|
334
|
+
|
|
335
|
+
### Changes Made
|
|
336
|
+
- [ERROR-001]: <what was fixed>
|
|
337
|
+
- [ERROR-002]: <what was fixed>
|
|
338
|
+
|
|
339
|
+
## If Fix Unclear
|
|
340
|
+
- Ask for clarification
|
|
341
|
+
- Show what you understand
|
|
342
|
+
- Propose alternative fix`,
|
|
343
|
+
canWrite: true,
|
|
344
|
+
canBash: true,
|
|
345
|
+
},
|
|
346
|
+
|
|
347
|
+
// ═══════════════════════════════════════════════════════════════
|
|
348
|
+
// SEARCHER - Context Provider
|
|
349
|
+
// ═══════════════════════════════════════════════════════════════
|
|
350
|
+
searcher: {
|
|
351
|
+
id: "searcher",
|
|
352
|
+
description: "Context provider - finds patterns, examples, and project conventions",
|
|
353
|
+
systemPrompt: `You are the Searcher - context provider.
|
|
354
|
+
|
|
355
|
+
## Your Job
|
|
356
|
+
Find relevant patterns and context BEFORE coder starts working.
|
|
357
|
+
|
|
358
|
+
## Tools
|
|
359
|
+
- grep_search: Find text/code patterns
|
|
360
|
+
- glob_search: Find files by name
|
|
361
|
+
|
|
362
|
+
## What to Find
|
|
363
|
+
1. Similar implementations in codebase
|
|
364
|
+
2. Import patterns and style
|
|
365
|
+
3. Type definitions being used
|
|
366
|
+
4. Existing utility functions
|
|
367
|
+
5. Project conventions
|
|
368
|
+
|
|
369
|
+
## Output Format
|
|
370
|
+
\`\`\`
|
|
371
|
+
### Found Patterns
|
|
372
|
+
|
|
373
|
+
[PATTERN-1] <name>
|
|
374
|
+
File: <path>
|
|
375
|
+
Relevant code:
|
|
376
|
+
\`\`\`<lang>
|
|
377
|
+
<code snippet>
|
|
378
|
+
\`\`\`
|
|
379
|
+
|
|
380
|
+
[PATTERN-2] ...
|
|
381
|
+
|
|
382
|
+
### Recommendations for Coder
|
|
383
|
+
- Use <pattern> from <file>
|
|
384
|
+
- Follow <convention>
|
|
385
|
+
- Import from <path>
|
|
386
|
+
\`\`\`
|
|
387
|
+
|
|
388
|
+
## Guidelines
|
|
389
|
+
- Show actual code, not just file paths
|
|
390
|
+
- Focus on most relevant 3-5 findings
|
|
391
|
+
- Note project conventions
|
|
392
|
+
- Warn about gotchas`,
|
|
393
|
+
canWrite: false,
|
|
394
|
+
canBash: false,
|
|
395
|
+
},
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
// ============================================================================
|
|
399
|
+
// Binary Management
|
|
400
|
+
// ============================================================================
|
|
401
|
+
|
|
402
|
+
function getBinaryPath(): string {
|
|
403
|
+
const binDir = join(__dirname, "..", "bin");
|
|
404
|
+
const os = platform();
|
|
405
|
+
const cpu = arch();
|
|
406
|
+
|
|
407
|
+
let binaryName: string;
|
|
408
|
+
if (os === "win32") {
|
|
409
|
+
binaryName = "orchestrator-windows-x64.exe";
|
|
410
|
+
} else if (os === "darwin") {
|
|
411
|
+
binaryName = cpu === "arm64" ? "orchestrator-macos-arm64" : "orchestrator-macos-x64";
|
|
412
|
+
} else {
|
|
413
|
+
binaryName = cpu === "arm64" ? "orchestrator-linux-arm64" : "orchestrator-linux-x64";
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
let binaryPath = join(binDir, binaryName);
|
|
417
|
+
if (!existsSync(binaryPath)) {
|
|
418
|
+
binaryPath = join(binDir, os === "win32" ? "orchestrator.exe" : "orchestrator");
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return binaryPath;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
async function callRustTool(name: string, args: Record<string, unknown>): Promise<string> {
|
|
425
|
+
const binary = getBinaryPath();
|
|
426
|
+
if (!existsSync(binary)) {
|
|
427
|
+
return JSON.stringify({ error: `Binary not found: ${binary}` });
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return new Promise((resolve) => {
|
|
431
|
+
const proc = spawn(binary, ["serve"], { stdio: ["pipe", "pipe", "pipe"] });
|
|
432
|
+
let stdout = "";
|
|
433
|
+
|
|
434
|
+
proc.stdout.on("data", (data) => { stdout += data.toString(); });
|
|
435
|
+
|
|
436
|
+
const request = JSON.stringify({
|
|
437
|
+
jsonrpc: "2.0",
|
|
438
|
+
id: 1,
|
|
439
|
+
method: "tools/call",
|
|
440
|
+
params: { name, arguments: args },
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
proc.stdin.write(request + "\n");
|
|
444
|
+
proc.stdin.end();
|
|
445
|
+
|
|
446
|
+
const timeout = setTimeout(() => { proc.kill(); resolve(JSON.stringify({ error: "Timeout" })); }, 60000);
|
|
447
|
+
|
|
448
|
+
proc.on("close", () => {
|
|
449
|
+
clearTimeout(timeout);
|
|
450
|
+
try {
|
|
451
|
+
const lines = stdout.trim().split("\n");
|
|
452
|
+
const response = JSON.parse(lines[lines.length - 1]);
|
|
453
|
+
const text = response?.result?.content?.[0]?.text;
|
|
454
|
+
resolve(text || JSON.stringify(response.result));
|
|
455
|
+
} catch {
|
|
456
|
+
resolve(stdout || "No output");
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// ============================================================================
|
|
463
|
+
// State Management
|
|
464
|
+
// ============================================================================
|
|
465
|
+
|
|
466
|
+
const state = {
|
|
467
|
+
autoEnabled: false,
|
|
468
|
+
maxIterations: 100,
|
|
469
|
+
maxRetries: 3,
|
|
470
|
+
sessions: new Map<string, {
|
|
471
|
+
enabled: boolean;
|
|
472
|
+
iterations: number;
|
|
473
|
+
taskRetries: Map<string, number>;
|
|
474
|
+
currentTask: string;
|
|
475
|
+
}>(),
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
// ============================================================================
|
|
479
|
+
// call_agent Tool
|
|
480
|
+
// ============================================================================
|
|
481
|
+
|
|
482
|
+
const callAgentTool = tool({
|
|
483
|
+
description: `Call a team member to perform specific work.
|
|
484
|
+
|
|
485
|
+
## Team
|
|
486
|
+
- **planner**: Decompose complex task into atomic units
|
|
487
|
+
- **coder**: Implement single atomic task
|
|
488
|
+
- **reviewer**: Quality check (ALWAYS after coder)
|
|
489
|
+
- **fixer**: Fix specific errors from reviewer
|
|
490
|
+
- **searcher**: Find patterns and context
|
|
491
|
+
|
|
492
|
+
## Self-Correcting Workflow
|
|
493
|
+
1. planner → atomic tasks
|
|
494
|
+
2. For each task:
|
|
495
|
+
- searcher (if needed)
|
|
496
|
+
- coder
|
|
497
|
+
- reviewer (mandatory)
|
|
498
|
+
- fixer (if errors) → reviewer again
|
|
499
|
+
3. Continue until all pass`,
|
|
500
|
+
args: {
|
|
501
|
+
agent: tool.schema
|
|
502
|
+
.enum(["planner", "coder", "reviewer", "fixer", "searcher"])
|
|
503
|
+
.describe("Team member to call"),
|
|
504
|
+
task: tool.schema.string().describe("Atomic task or specific error to address"),
|
|
505
|
+
context: tool.schema.string().optional().describe("Relevant context from previous steps"),
|
|
506
|
+
},
|
|
507
|
+
async execute(args) {
|
|
508
|
+
const agentDef = AGENTS[args.agent];
|
|
509
|
+
if (!agentDef) {
|
|
510
|
+
return `Error: Unknown agent: ${args.agent}`;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const prompt = `
|
|
514
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
515
|
+
${agentDef.id.toUpperCase()} AGENT
|
|
516
|
+
${agentDef.description}
|
|
517
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
518
|
+
|
|
519
|
+
<system>
|
|
520
|
+
${agentDef.systemPrompt}
|
|
521
|
+
</system>
|
|
522
|
+
|
|
523
|
+
<task>
|
|
524
|
+
${args.task}
|
|
525
|
+
</task>
|
|
526
|
+
|
|
527
|
+
${args.context ? `<context>\n${args.context}\n</context>` : ""}
|
|
528
|
+
|
|
529
|
+
Execute according to your role. Be thorough and precise.
|
|
530
|
+
`;
|
|
531
|
+
|
|
532
|
+
return prompt;
|
|
533
|
+
},
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
// ============================================================================
|
|
537
|
+
// Slash Commands
|
|
538
|
+
// ============================================================================
|
|
539
|
+
|
|
540
|
+
const COMMANDS: Record<string, { description: string; template: string; argumentHint?: string }> = {
|
|
541
|
+
"auto": {
|
|
542
|
+
description: "Autonomous execution with self-correcting loop",
|
|
543
|
+
template: `<command-instruction>
|
|
544
|
+
🚀 AUTO MODE - Self-Correcting Agent Loop
|
|
545
|
+
|
|
546
|
+
## Protocol
|
|
547
|
+
1. Call planner to decompose into atomic tasks
|
|
548
|
+
2. For EACH atomic task:
|
|
549
|
+
- Call searcher if context needed
|
|
550
|
+
- Call coder to implement
|
|
551
|
+
- Call reviewer to verify (MANDATORY)
|
|
552
|
+
- If FAIL: Call fixer → reviewer again (max 3 retries)
|
|
553
|
+
- If PASS: Move to next task
|
|
554
|
+
3. Continue until all tasks complete with PASS
|
|
555
|
+
|
|
556
|
+
## Error Recovery
|
|
557
|
+
- Same error 3x → Stop and ask user
|
|
558
|
+
- New error → Apply fix and retry
|
|
559
|
+
- Stuck → Try different approach
|
|
560
|
+
|
|
561
|
+
## Goal
|
|
562
|
+
Complete "$ARGUMENTS" with zero errors.
|
|
563
|
+
Keep iterating until done.
|
|
564
|
+
</command-instruction>
|
|
565
|
+
|
|
566
|
+
<user-task>
|
|
567
|
+
$ARGUMENTS
|
|
568
|
+
</user-task>`,
|
|
569
|
+
argumentHint: '"task description"',
|
|
570
|
+
},
|
|
571
|
+
"plan": {
|
|
572
|
+
description: "Decompose task into atomic units",
|
|
573
|
+
template: `<agent-prompt agent="planner">
|
|
574
|
+
Decompose into atomic tasks:
|
|
575
|
+
$ARGUMENTS
|
|
576
|
+
</agent-prompt>`,
|
|
577
|
+
argumentHint: '"complex task"',
|
|
578
|
+
},
|
|
579
|
+
"review": {
|
|
580
|
+
description: "Quality check with error detection",
|
|
581
|
+
template: `<agent-prompt agent="reviewer">
|
|
582
|
+
Review for ALL issues:
|
|
583
|
+
$ARGUMENTS
|
|
584
|
+
</agent-prompt>`,
|
|
585
|
+
argumentHint: '"code to review"',
|
|
586
|
+
},
|
|
587
|
+
"fix": {
|
|
588
|
+
description: "Fix specific errors",
|
|
589
|
+
template: `<agent-prompt agent="fixer">
|
|
590
|
+
Fix these errors:
|
|
591
|
+
$ARGUMENTS
|
|
592
|
+
</agent-prompt>`,
|
|
593
|
+
argumentHint: '"error details"',
|
|
594
|
+
},
|
|
595
|
+
"search": {
|
|
596
|
+
description: "Find patterns and context",
|
|
597
|
+
template: `<agent-prompt agent="searcher">
|
|
598
|
+
Find patterns for:
|
|
599
|
+
$ARGUMENTS
|
|
600
|
+
</agent-prompt>`,
|
|
601
|
+
argumentHint: '"what to find"',
|
|
602
|
+
},
|
|
603
|
+
"agents": {
|
|
604
|
+
description: "Show agent team",
|
|
605
|
+
template: `## 6-Agent Collaborative Architecture
|
|
606
|
+
|
|
607
|
+
| Agent | Role |
|
|
608
|
+
|-------|------|
|
|
609
|
+
| planner | Decompose into atomic tasks |
|
|
610
|
+
| coder | Implement single task |
|
|
611
|
+
| reviewer | Quality gate (mandatory) |
|
|
612
|
+
| fixer | Apply specific fixes |
|
|
613
|
+
| searcher | Find context |
|
|
614
|
+
|
|
615
|
+
## Self-Correcting Loop
|
|
616
|
+
\`\`\`
|
|
617
|
+
plan → (search → code → review → fix?) → repeat
|
|
618
|
+
\`\`\``,
|
|
619
|
+
},
|
|
620
|
+
"cancel-auto": {
|
|
621
|
+
description: "Stop auto mode",
|
|
622
|
+
template: `Auto mode stopped.`,
|
|
623
|
+
},
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
// ============================================================================
|
|
627
|
+
// Slash Command Tool
|
|
628
|
+
// ============================================================================
|
|
629
|
+
|
|
630
|
+
function createSlashcommandTool() {
|
|
631
|
+
const commandList = Object.entries(COMMANDS)
|
|
632
|
+
.map(([name, cmd]) => {
|
|
633
|
+
const hint = cmd.argumentHint ? ` ${cmd.argumentHint}` : "";
|
|
634
|
+
return `- /${name}${hint}: ${cmd.description}`;
|
|
635
|
+
})
|
|
636
|
+
.join("\n");
|
|
637
|
+
|
|
638
|
+
return tool({
|
|
639
|
+
description: `Commands\n\n${commandList}`,
|
|
640
|
+
args: {
|
|
641
|
+
command: tool.schema.string().describe("Command (without slash)"),
|
|
642
|
+
},
|
|
643
|
+
async execute(args) {
|
|
644
|
+
const cmdName = (args.command || "").replace(/^\//, "").split(/\s+/)[0].toLowerCase();
|
|
645
|
+
const cmdArgs = (args.command || "").replace(/^\/?\\S+\s*/, "");
|
|
646
|
+
|
|
647
|
+
if (!cmdName) return `Commands:\n${commandList}`;
|
|
648
|
+
|
|
649
|
+
const command = COMMANDS[cmdName];
|
|
650
|
+
if (!command) return `Unknown: /${cmdName}\n\n${commandList}`;
|
|
651
|
+
|
|
652
|
+
return command.template.replace(/\$ARGUMENTS/g, cmdArgs || "continue");
|
|
653
|
+
},
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// ============================================================================
|
|
658
|
+
// Search Tools
|
|
659
|
+
// ============================================================================
|
|
660
|
+
|
|
661
|
+
const grepSearchTool = (directory: string) => tool({
|
|
662
|
+
description: "Search code patterns",
|
|
663
|
+
args: {
|
|
664
|
+
pattern: tool.schema.string().describe("Regex pattern"),
|
|
665
|
+
dir: tool.schema.string().optional().describe("Directory"),
|
|
666
|
+
},
|
|
667
|
+
async execute(args) {
|
|
668
|
+
return callRustTool("grep_search", {
|
|
669
|
+
pattern: args.pattern,
|
|
670
|
+
directory: args.dir || directory,
|
|
671
|
+
});
|
|
672
|
+
},
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
const globSearchTool = (directory: string) => tool({
|
|
676
|
+
description: "Find files by pattern",
|
|
677
|
+
args: {
|
|
678
|
+
pattern: tool.schema.string().describe("Glob pattern"),
|
|
679
|
+
dir: tool.schema.string().optional().describe("Directory"),
|
|
680
|
+
},
|
|
681
|
+
async execute(args) {
|
|
682
|
+
return callRustTool("glob_search", {
|
|
683
|
+
pattern: args.pattern,
|
|
684
|
+
directory: args.dir || directory,
|
|
685
|
+
});
|
|
686
|
+
},
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
// ============================================================================
|
|
690
|
+
// Utilities
|
|
691
|
+
// ============================================================================
|
|
692
|
+
|
|
693
|
+
function detectSlashCommand(text: string): { command: string; args: string } | null {
|
|
694
|
+
const match = text.trim().match(/^\/([a-zA-Z0-9_-]+)(?:\s+(.*))?$/);
|
|
695
|
+
if (!match) return null;
|
|
696
|
+
return { command: match[1], args: match[2] || "" };
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// ============================================================================
|
|
700
|
+
// Plugin
|
|
701
|
+
// ============================================================================
|
|
702
|
+
|
|
703
|
+
const OrchestratorPlugin = async (input: PluginInput) => {
|
|
704
|
+
const { directory } = input;
|
|
705
|
+
|
|
706
|
+
return {
|
|
707
|
+
tool: {
|
|
708
|
+
call_agent: callAgentTool,
|
|
709
|
+
slashcommand: createSlashcommandTool(),
|
|
710
|
+
grep_search: grepSearchTool(directory),
|
|
711
|
+
glob_search: globSearchTool(directory),
|
|
712
|
+
},
|
|
713
|
+
|
|
714
|
+
"chat.message": async (input: any, output: any) => {
|
|
715
|
+
const parts = output.parts as Array<{ type: string; text?: string }>;
|
|
716
|
+
const textPartIndex = parts.findIndex(p => p.type === "text" && p.text);
|
|
717
|
+
if (textPartIndex === -1) return;
|
|
718
|
+
|
|
719
|
+
const originalText = parts[textPartIndex].text || "";
|
|
720
|
+
const parsed = detectSlashCommand(originalText);
|
|
721
|
+
|
|
722
|
+
if (parsed) {
|
|
723
|
+
const command = COMMANDS[parsed.command];
|
|
724
|
+
if (command) {
|
|
725
|
+
parts[textPartIndex].text = command.template.replace(/\$ARGUMENTS/g, parsed.args || "continue");
|
|
726
|
+
|
|
727
|
+
if (parsed.command === "auto") {
|
|
728
|
+
const sessionID = input.sessionID;
|
|
729
|
+
state.sessions.set(sessionID, {
|
|
730
|
+
enabled: true,
|
|
731
|
+
iterations: 0,
|
|
732
|
+
taskRetries: new Map(),
|
|
733
|
+
currentTask: "",
|
|
734
|
+
});
|
|
735
|
+
state.autoEnabled = true;
|
|
736
|
+
} else if (parsed.command === "cancel-auto") {
|
|
737
|
+
state.sessions.delete(input.sessionID);
|
|
738
|
+
state.autoEnabled = false;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
},
|
|
743
|
+
|
|
744
|
+
"tool.execute.after": async (
|
|
745
|
+
input: { tool: string; sessionID: string; callID: string },
|
|
746
|
+
output: { title: string; output: string; metadata: any }
|
|
747
|
+
) => {
|
|
748
|
+
if (!state.autoEnabled) return;
|
|
749
|
+
|
|
750
|
+
const session = state.sessions.get(input.sessionID);
|
|
751
|
+
if (!session?.enabled) return;
|
|
752
|
+
|
|
753
|
+
session.iterations++;
|
|
754
|
+
|
|
755
|
+
// Circuit breaker: max iterations
|
|
756
|
+
if (session.iterations >= state.maxIterations) {
|
|
757
|
+
session.enabled = false;
|
|
758
|
+
output.output += `\n\n━━━━━━━━━━━━\n⚠️ ITERATION LIMIT (${state.maxIterations})\nReview progress and continue manually.`;
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// Detect errors and track retries
|
|
763
|
+
const errorMatch = output.output.match(/\[ERROR-(\d+)\]/);
|
|
764
|
+
if (errorMatch) {
|
|
765
|
+
const errorId = `error-${session.currentTask || 'unknown'}`;
|
|
766
|
+
const retries = (session.taskRetries.get(errorId) || 0) + 1;
|
|
767
|
+
session.taskRetries.set(errorId, retries);
|
|
768
|
+
|
|
769
|
+
if (retries >= state.maxRetries) {
|
|
770
|
+
session.enabled = false;
|
|
771
|
+
output.output += `\n\n━━━━━━━━━━━━\n🛑 RETRY LIMIT (${state.maxRetries}x same error)\nReview manually or try different approach.`;
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
output.output += `\n\n━━━━━━━━━━━━\n🔄 RETRY ${retries}/${state.maxRetries}\nApply fix and verify again.`;
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
// Clear retries on PASS
|
|
780
|
+
if (output.output.includes("✅ PASS")) {
|
|
781
|
+
session.taskRetries.clear();
|
|
782
|
+
output.output += `\n\n━━━━━━━━━━━━\n✅ VERIFIED - Continue to next task\n[${session.iterations}/${state.maxIterations}]`;
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
output.output += `\n\n━━━━━━━━━━━━\n[${session.iterations}/${state.maxIterations}]`;
|
|
787
|
+
},
|
|
788
|
+
};
|
|
789
|
+
};
|
|
790
|
+
|
|
791
|
+
export default OrchestratorPlugin;
|