opencode-orchestrator 0.1.55 → 0.1.57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -3
- package/bin/orchestrator-linux-arm64 +0 -0
- package/bin/orchestrator-linux-x64 +0 -0
- package/dist/agents/coder.d.ts +2 -0
- package/dist/agents/definitions.d.ts +2 -0
- package/dist/agents/fixer.d.ts +2 -0
- package/dist/agents/orchestrator.d.ts +2 -0
- package/dist/agents/planner.d.ts +2 -0
- package/dist/agents/reviewer.d.ts +2 -0
- package/dist/agents/searcher.d.ts +2 -0
- package/dist/agents/types.d.ts +7 -0
- package/dist/cli.js +9 -3
- package/dist/core/state.d.ts +14 -0
- package/dist/core/tasks.d.ts +29 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +323 -239
- package/dist/tools/callAgent.d.ts +19 -0
- package/dist/tools/rust.d.ts +1 -0
- package/dist/tools/search.d.ts +22 -0
- package/dist/tools/slashCommand.d.ts +14 -0
- package/dist/utils/binary.d.ts +1 -0
- package/dist/utils/common.d.ts +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,11 +15,12 @@
|
|
|
15
15
|
|
|
16
16
|
A 6-agent collaborative system that turns **affordable LLMs into reliable engineering teams**.
|
|
17
17
|
|
|
18
|
+
- **Relentless Loop** — Auto-continues until mission complete
|
|
18
19
|
- **Atomic Decomposition** — Tasks broken into verifiable micro-units
|
|
19
20
|
- **PDCA Loop** — Plan → Do → Check → Act (self-correcting)
|
|
20
21
|
- **Parallel DAG** — Independent tasks run concurrently
|
|
21
|
-
- **
|
|
22
|
-
|
|
22
|
+
- **Quality Gate** — Reviewer catches all errors before merge
|
|
23
|
+
- **Structure** — **TypeScript Brain** for logic + **Rust Muscle** for speed
|
|
23
24
|
---
|
|
24
25
|
|
|
25
26
|
## Installation
|
|
@@ -34,16 +35,27 @@ Restart OpenCode after installation.
|
|
|
34
35
|
|
|
35
36
|
## Usage
|
|
36
37
|
|
|
38
|
+
### Option 1: Select Orchestrator Agent
|
|
39
|
+
Press `tab` → Select **Orchestrator** → Just type your request!
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
"Implement user authentication with JWT"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Option 2: Use /task Command
|
|
37
46
|
```bash
|
|
38
47
|
/task "Implement user authentication with JWT"
|
|
39
48
|
```
|
|
40
49
|
|
|
41
|
-
|
|
50
|
+
Both methods activate **Relentless Loop** — the agent continues automatically until mission complete.
|
|
51
|
+
|
|
52
|
+
### How It Works:
|
|
42
53
|
1. **Plan** — Decompose into atomic tasks
|
|
43
54
|
2. **Search** — Find patterns and context
|
|
44
55
|
3. **Code** — Implement with precision
|
|
45
56
|
4. **Review** — Verify via quality gate
|
|
46
57
|
5. **Fix** — Self-heal if errors occur
|
|
58
|
+
6. **Loop** — Repeat until ✅ MISSION COMPLETE
|
|
47
59
|
|
|
48
60
|
---
|
|
49
61
|
|
|
Binary file
|
|
Binary file
|
package/dist/cli.js
CHANGED
|
@@ -2,13 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { spawn } from "child_process";
|
|
5
|
+
import { existsSync as existsSync2 } from "fs";
|
|
6
|
+
import { platform as platform2, arch as arch2 } from "os";
|
|
7
|
+
|
|
8
|
+
// src/utils/binary.ts
|
|
5
9
|
import { join, dirname } from "path";
|
|
6
10
|
import { fileURLToPath } from "url";
|
|
7
11
|
import { platform, arch } from "os";
|
|
8
12
|
import { existsSync } from "fs";
|
|
9
13
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
14
|
function getBinaryPath() {
|
|
11
|
-
const binDir = join(__dirname, "..", "bin");
|
|
15
|
+
const binDir = join(__dirname, "..", "..", "bin");
|
|
12
16
|
const os = platform();
|
|
13
17
|
const cpu = arch();
|
|
14
18
|
let binaryName;
|
|
@@ -25,10 +29,12 @@ function getBinaryPath() {
|
|
|
25
29
|
}
|
|
26
30
|
return binaryPath;
|
|
27
31
|
}
|
|
32
|
+
|
|
33
|
+
// src/cli.ts
|
|
28
34
|
var binary = getBinaryPath();
|
|
29
35
|
var args = process.argv.slice(2);
|
|
30
|
-
if (!
|
|
31
|
-
console.error(`Error: Orchestrator binary not found for your platform (${
|
|
36
|
+
if (!existsSync2(binary)) {
|
|
37
|
+
console.error(`Error: Orchestrator binary not found for your platform (${platform2()} ${arch2()})`);
|
|
32
38
|
console.error(`Expected at: ${binary}`);
|
|
33
39
|
process.exit(1);
|
|
34
40
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { TaskGraph } from "./tasks.js";
|
|
2
|
+
export interface SessionState {
|
|
3
|
+
enabled: boolean;
|
|
4
|
+
iterations: number;
|
|
5
|
+
taskRetries: Map<string, number>;
|
|
6
|
+
currentTask: string;
|
|
7
|
+
graph?: TaskGraph;
|
|
8
|
+
}
|
|
9
|
+
export declare const state: {
|
|
10
|
+
missionActive: boolean;
|
|
11
|
+
maxIterations: number;
|
|
12
|
+
maxRetries: number;
|
|
13
|
+
sessions: Map<string, SessionState>;
|
|
14
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task management for DAG-based orchestration
|
|
3
|
+
*/
|
|
4
|
+
export type TaskStatus = "pending" | "running" | "completed" | "failed";
|
|
5
|
+
export interface Task {
|
|
6
|
+
id: string;
|
|
7
|
+
description: string;
|
|
8
|
+
action: string;
|
|
9
|
+
file: string;
|
|
10
|
+
dependencies: string[];
|
|
11
|
+
status: TaskStatus;
|
|
12
|
+
result?: string;
|
|
13
|
+
retryCount: number;
|
|
14
|
+
complexity: number;
|
|
15
|
+
type: "infrastructure" | "logic" | "integration";
|
|
16
|
+
}
|
|
17
|
+
export declare class TaskGraph {
|
|
18
|
+
private tasks;
|
|
19
|
+
constructor(tasks?: Task[]);
|
|
20
|
+
addTask(task: Task): void;
|
|
21
|
+
getTask(id: string): Task | undefined;
|
|
22
|
+
updateTask(id: string, updates: Partial<Task>): void;
|
|
23
|
+
getReadyTasks(): Task[];
|
|
24
|
+
isCompleted(): boolean;
|
|
25
|
+
hasFailed(): boolean;
|
|
26
|
+
getTaskSummary(): string;
|
|
27
|
+
toJSON(): string;
|
|
28
|
+
static fromJSON(json: string): TaskGraph;
|
|
29
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,86 +1,8 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import { platform, arch } from "os";
|
|
7
|
-
import { tool } from "@opencode-ai/plugin";
|
|
8
|
-
|
|
9
|
-
// src/tasks.ts
|
|
10
|
-
var TaskGraph = class _TaskGraph {
|
|
11
|
-
tasks = /* @__PURE__ */ new Map();
|
|
12
|
-
constructor(tasks) {
|
|
13
|
-
if (tasks) {
|
|
14
|
-
tasks.forEach((t) => this.addTask(t));
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
addTask(task) {
|
|
18
|
-
this.tasks.set(task.id, { ...task, status: "pending", retryCount: 0 });
|
|
19
|
-
}
|
|
20
|
-
getTask(id) {
|
|
21
|
-
return this.tasks.get(id);
|
|
22
|
-
}
|
|
23
|
-
updateTask(id, updates) {
|
|
24
|
-
const task = this.tasks.get(id);
|
|
25
|
-
if (task) {
|
|
26
|
-
this.tasks.set(id, { ...task, ...updates });
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
getReadyTasks() {
|
|
30
|
-
return Array.from(this.tasks.values()).filter((task) => {
|
|
31
|
-
if (task.status !== "pending") return false;
|
|
32
|
-
return task.dependencies.every((depId) => {
|
|
33
|
-
const dep = this.tasks.get(depId);
|
|
34
|
-
return dep && dep.status === "completed";
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
isCompleted() {
|
|
39
|
-
return Array.from(this.tasks.values()).every((t) => t.status === "completed");
|
|
40
|
-
}
|
|
41
|
-
hasFailed() {
|
|
42
|
-
return Array.from(this.tasks.values()).some((t) => t.status === "failed" && t.retryCount >= 3);
|
|
43
|
-
}
|
|
44
|
-
getTaskSummary() {
|
|
45
|
-
const tasks = Array.from(this.tasks.values());
|
|
46
|
-
const completed = tasks.filter((t) => t.status === "completed");
|
|
47
|
-
const notCompleted = tasks.filter((t) => t.status !== "completed");
|
|
48
|
-
let summary = "\u{1F4CB} **Mission Status**\n";
|
|
49
|
-
if (completed.length > 0) {
|
|
50
|
-
summary += `\u2705 Completed: ${completed.length} tasks (Hidden to save tokens)
|
|
51
|
-
`;
|
|
52
|
-
}
|
|
53
|
-
for (const task of notCompleted) {
|
|
54
|
-
const icon = task.status === "running" ? "\u23F3" : task.status === "failed" ? "\u274C" : "\u{1F4A4}";
|
|
55
|
-
summary += `${icon} [${task.id}] ${task.description}
|
|
56
|
-
`;
|
|
57
|
-
}
|
|
58
|
-
return summary;
|
|
59
|
-
}
|
|
60
|
-
toJSON() {
|
|
61
|
-
return JSON.stringify(Array.from(this.tasks.values()), null, 2);
|
|
62
|
-
}
|
|
63
|
-
static fromJSON(json) {
|
|
64
|
-
try {
|
|
65
|
-
const tasks = JSON.parse(json);
|
|
66
|
-
return new _TaskGraph(tasks);
|
|
67
|
-
} catch (e) {
|
|
68
|
-
console.error("Failed to parse TaskGraph JSON:", e);
|
|
69
|
-
return new _TaskGraph();
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
// src/index.ts
|
|
75
|
-
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
76
|
-
var AGENTS = {
|
|
77
|
-
// ═══════════════════════════════════════════════════════════════
|
|
78
|
-
// ORCHESTRATOR - Team Leader & Decision Maker
|
|
79
|
-
// ═══════════════════════════════════════════════════════════════
|
|
80
|
-
orchestrator: {
|
|
81
|
-
id: "orchestrator",
|
|
82
|
-
description: "Team leader - manages the Mission and parallel work streams",
|
|
83
|
-
systemPrompt: `You are the Orchestrator - the mission commander.
|
|
1
|
+
// src/agents/orchestrator.ts
|
|
2
|
+
var orchestrator = {
|
|
3
|
+
id: "orchestrator",
|
|
4
|
+
description: "Team leader - manages the Mission and parallel work streams",
|
|
5
|
+
systemPrompt: `You are the Orchestrator - the mission commander.
|
|
84
6
|
|
|
85
7
|
## Core Philosophy: Micro-Tasking & Quality Gates
|
|
86
8
|
- Even small models (Phi, Gemma) succeed when tasks are tiny and verified.
|
|
@@ -88,51 +10,86 @@ var AGENTS = {
|
|
|
88
10
|
- NEVER proceed to a task if its dependencies are not 100% VERIFIED.
|
|
89
11
|
|
|
90
12
|
## Operational SOP (Standard Operating Procedure)
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
- **
|
|
94
|
-
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
-
|
|
109
|
-
-
|
|
110
|
-
-
|
|
13
|
+
|
|
14
|
+
1. **PHASE 0: INTENT GATE & CLASSIFICATION**
|
|
15
|
+
- **Trivial**: Single file, direct answer -> Execute linearly.
|
|
16
|
+
- **Complex**: Multiple modules, "Refactor", "Add feature" -> **Engage Planner**.
|
|
17
|
+
- **GitHub Work**: Mentions of PR/Issue -> Cycle: Investigate -> Implement -> Verify (STOP).
|
|
18
|
+
|
|
19
|
+
### Intent Classification Table
|
|
20
|
+
| Type | Action |
|
|
21
|
+
|------|--------|
|
|
22
|
+
| **Trivial** | Direct tools only |
|
|
23
|
+
| **Explicit** | Execute directly |
|
|
24
|
+
| **Exploratory** | Fire searcher + tools in parallel |
|
|
25
|
+
| **GitHub Work** | Investigate \u2192 Implement \u2192 Verify \u2192 Report Ready |
|
|
26
|
+
| **Ambiguous** | Ask ONE clarifying question |
|
|
27
|
+
|
|
28
|
+
2. **PHASE 1: RESEARCH & PLAN**
|
|
29
|
+
- Call **searcher** to read docs and find patterns.
|
|
30
|
+
- **Tool Selection**:
|
|
31
|
+
- \`grep\`, \`glob\`, \`lsp_*\`: Standard search.
|
|
32
|
+
- \`searcher\` agent: Contextual/Reference search (docs, examples).
|
|
33
|
+
- **MapReduce**: Shard huge context into temporary files (\`temp_context_*.md\`).
|
|
34
|
+
|
|
35
|
+
3. **PHASE 2: EXECUTE (The Loop)**
|
|
36
|
+
- Execute tasks in DAG order.
|
|
37
|
+
|
|
38
|
+
### Frontend Decision Gate (CRITICAL)
|
|
39
|
+
Before touching .tsx/.css files, ask: **"Is this LOOKS or WORKS?"**
|
|
40
|
+
- **LOOKS** (Visual/UI): Delegate to human or specialized UI agent.
|
|
41
|
+
- **WORKS** (Logic): Call **coder** for atomic implementation.
|
|
42
|
+
|
|
43
|
+
### Delegation Prompt Structure (MANDATORY)
|
|
44
|
+
When calling subagents, your prompt MUST include:
|
|
45
|
+
1. **TASK**: Atomic, specific goal
|
|
46
|
+
2. **EXPECTED OUTCOME**: Concrete deliverables
|
|
47
|
+
3. **REQUIRED TOOLS**: Explicit tool whitelist
|
|
48
|
+
4. **MUST DO**: Exhaustive requirements
|
|
49
|
+
5. **MUST NOT DO**: Forbidden actions
|
|
50
|
+
6. **CONTEXT**: File paths, patterns, constraints
|
|
51
|
+
|
|
52
|
+
4. **PHASE 3: VERIFY & FIX**
|
|
53
|
+
- Call **reviewer** after EVERY implementation step.
|
|
54
|
+
- **5-Point Check**: Syntax, Style, Logic, Integrity, Data Flow.
|
|
55
|
+
- **Evidence Requirements**:
|
|
56
|
+
- File edit: \`lsp_diagnostics\` clean
|
|
57
|
+
- Build/Test: Exit code 0
|
|
58
|
+
- If Fail: Call **fixer** (minimal changes).
|
|
59
|
+
|
|
60
|
+
5. **PHASE 4: COMPLETION**
|
|
61
|
+
- Confirm all planned tasks are done.
|
|
62
|
+
- **Cleanup**: Delete temporary mission/shard files.
|
|
63
|
+
- **Final Report**: "\u2705 MISSION COMPLETE"
|
|
64
|
+
|
|
65
|
+
## GitHub Workflow (If mentioned in PR/Issue)
|
|
66
|
+
1. **Investigate**: Read issue, search codebase.
|
|
67
|
+
2. **Implement**: Minimal changes, follow patterns.
|
|
68
|
+
3. **Verify**: Build, Test, Check Regressions.
|
|
69
|
+
4. **Report**: State "Ready for human review/PR". **DO NOT push or create PR yourself.**
|
|
70
|
+
|
|
71
|
+
## Hard Rules (NEVER violate)
|
|
72
|
+
- **NO GIT PUSH**: You are NOT allowed to push code.
|
|
73
|
+
- **NO PR CREATION**: Do not create Pull Requests.
|
|
74
|
+
- **NO GIT COMMITS**: Do not commit unless explicitly asked by user.
|
|
75
|
+
|
|
76
|
+
## Oracle Usage (Senior Advisor)
|
|
77
|
+
Use **searcher** (context) for most things. Use **Oracle** (high-IQ) ONLY for:
|
|
78
|
+
- Complex architecture design
|
|
79
|
+
- 2+ failed fix attempts
|
|
80
|
+
- Multi-system tradeoffs
|
|
81
|
+
|
|
82
|
+
## Communication Style
|
|
83
|
+
- **Concise**: Start work immediately. No "I'm on it".
|
|
84
|
+
- **Direct**: Answer directly without preamble.
|
|
85
|
+
- **No Flattery**: No "Great question!".
|
|
86
|
+
- **Status Not Updates**: Use "Mission Status" block instead of chatty updates.
|
|
111
87
|
|
|
112
88
|
## Global Consistency Rules (Mandatory)
|
|
113
89
|
- **State Persistence**: Independent nodes MUST communicate via files, not memory.
|
|
114
90
|
- **Import Sync**: Any export change MUST trigger an update in all importing files.
|
|
115
|
-
- **Signature Sync**: Function signature changes MUST be propagated to all callers in the same DAG layer.
|
|
116
|
-
- **Type Sync**: Shared types MUST be modified in isolation before logic implementation.
|
|
117
91
|
- **Atomic Integrity**: Parallel tasks MUST NOT modify the same line of code in the same file.
|
|
118
|
-
|
|
119
|
-
## Memory Management Strategy (Infinite Context Simulation)
|
|
120
|
-
- **Sharding**: Never hold raw code in context. Write it to a file, keep the reference.
|
|
121
|
-
- **Garbage Collection**: If a task is done, summarize its outcome ("Task A: Success, Output at /file/path") and FORGET the details.
|
|
122
|
-
- **Value Judgment**: Do not summarize "process". Summarize "state changes".
|
|
123
|
-
|
|
124
|
-
## Safety & Boundary SOP
|
|
125
|
-
- **Safety Gate**: Verify alignment with project core before any execution.
|
|
126
|
-
- **Sync Sentinel**: You are responsible for cross-task logic consistency. If tasks drift, HALT and re-sync.
|
|
127
|
-
|
|
128
|
-
## Failure Recovery SOP
|
|
129
|
-
- **Error 1-2**: Call fixer as usual.
|
|
130
|
-
- **Error 3**: Pivot. Call searcher for similar fixes or planner to split the task further.
|
|
131
|
-
- **Syntax Error**: Fixer MUST only fix syntax, no logic changes.
|
|
132
|
-
|
|
133
|
-
## Reliable Execution with Fixed Models
|
|
134
|
-
- This system is optimized for fixed, low-performance models (Phi, Gemma, etc.).
|
|
135
|
-
- Performance is achieved through granularity, not model upgrades.
|
|
92
|
+
- **Trust No One**: Subagents can hallucinate. Verify their outputs with tools.
|
|
136
93
|
|
|
137
94
|
## Progress Status
|
|
138
95
|
Always show the Mission status at the end of your turns:
|
|
@@ -140,16 +97,15 @@ Always show the Mission status at the end of your turns:
|
|
|
140
97
|
[TASK-001] \u2705 Completed
|
|
141
98
|
[TASK-002] \u23F3 Running
|
|
142
99
|
[TASK-003] \u{1F4A4} Pending`,
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
systemPrompt: `You are the Planner - the master architect.
|
|
100
|
+
canWrite: false,
|
|
101
|
+
canBash: false
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// src/agents/planner.ts
|
|
105
|
+
var planner = {
|
|
106
|
+
id: "planner",
|
|
107
|
+
description: "Architect - decomposes work into a JSON Mission",
|
|
108
|
+
systemPrompt: `You are the Planner - the master architect.
|
|
153
109
|
|
|
154
110
|
## Your Mission
|
|
155
111
|
1. **Understand & Filter**: Read documentation, but **FILTER** out irrelevant parts. determine what is truly important.
|
|
@@ -197,16 +153,15 @@ Produce a JSON array of tasks:
|
|
|
197
153
|
- Break circular dependencies.
|
|
198
154
|
- Ensure all files are identified by absolute or relative path from project root.
|
|
199
155
|
- Keep complexity < 7. If higher, split the task.`,
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
systemPrompt: `You are the Coder - implementation specialist.
|
|
156
|
+
canWrite: false,
|
|
157
|
+
canBash: false
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// src/agents/coder.ts
|
|
161
|
+
var coder = {
|
|
162
|
+
id: "coder",
|
|
163
|
+
description: "Implementation - executes one atomic task with complete, working code",
|
|
164
|
+
systemPrompt: `You are the Coder - implementation specialist.
|
|
210
165
|
|
|
211
166
|
## Your Job
|
|
212
167
|
Execute the ONE atomic task you're given. Produce complete, working code.
|
|
@@ -253,16 +208,15 @@ Provide COMPLETE code that:
|
|
|
253
208
|
\`\`\`
|
|
254
209
|
|
|
255
210
|
Brief explanation if needed.`,
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
systemPrompt: `You are the Reviewer - the Style Guardian and Sync Sentinel.
|
|
211
|
+
canWrite: true,
|
|
212
|
+
canBash: true
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
// src/agents/reviewer.ts
|
|
216
|
+
var reviewer = {
|
|
217
|
+
id: "reviewer",
|
|
218
|
+
description: "Style Guardian & Sync Sentinel - ensures total code consistency",
|
|
219
|
+
systemPrompt: `You are the Reviewer - the Style Guardian and Sync Sentinel.
|
|
266
220
|
|
|
267
221
|
## Your Job
|
|
268
222
|
1. **Task Review**: Verify individual code changes (Syntax, Style, Logic).
|
|
@@ -293,16 +247,15 @@ Brief explanation if needed.`,
|
|
|
293
247
|
...
|
|
294
248
|
\`\`\`
|
|
295
249
|
`,
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
systemPrompt: `You are the Fixer - error resolution specialist.
|
|
250
|
+
canWrite: false,
|
|
251
|
+
canBash: true
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
// src/agents/fixer.ts
|
|
255
|
+
var fixer = {
|
|
256
|
+
id: "fixer",
|
|
257
|
+
description: "Error resolution - applies targeted fixes based on reviewer feedback",
|
|
258
|
+
systemPrompt: `You are the Fixer - error resolution specialist.
|
|
306
259
|
|
|
307
260
|
## Your Job
|
|
308
261
|
Fix the SPECIFIC errors reported by reviewer.
|
|
@@ -349,16 +302,15 @@ You receive error reports like:
|
|
|
349
302
|
- Ask for clarification
|
|
350
303
|
- Show what you understand
|
|
351
304
|
- Propose alternative fix`,
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
systemPrompt: `You are the Searcher - the context oracle.
|
|
305
|
+
canWrite: true,
|
|
306
|
+
canBash: true
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
// src/agents/searcher.ts
|
|
310
|
+
var searcher = {
|
|
311
|
+
id: "searcher",
|
|
312
|
+
description: "Context provider - finds documentation and codebase patterns",
|
|
313
|
+
systemPrompt: `You are the Searcher - the context oracle.
|
|
362
314
|
|
|
363
315
|
## Mission
|
|
364
316
|
Your primary job is to find the **Truth** in the codebase.
|
|
@@ -384,64 +336,86 @@ OR
|
|
|
384
336
|
### 1. Architectural Boundaries (from docs)
|
|
385
337
|
### 2. Relevant Patterns (code snippets)
|
|
386
338
|
### 3. Recommendations`,
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
}
|
|
339
|
+
canWrite: false,
|
|
340
|
+
canBash: false
|
|
390
341
|
};
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
342
|
+
|
|
343
|
+
// src/agents/definitions.ts
|
|
344
|
+
var AGENTS = {
|
|
345
|
+
orchestrator,
|
|
346
|
+
planner,
|
|
347
|
+
coder,
|
|
348
|
+
reviewer,
|
|
349
|
+
fixer,
|
|
350
|
+
searcher
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
// src/core/tasks.ts
|
|
354
|
+
var TaskGraph = class _TaskGraph {
|
|
355
|
+
tasks = /* @__PURE__ */ new Map();
|
|
356
|
+
constructor(tasks) {
|
|
357
|
+
if (tasks) {
|
|
358
|
+
tasks.forEach((t) => this.addTask(t));
|
|
359
|
+
}
|
|
402
360
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
binaryPath = join(binDir, os === "win32" ? "orchestrator.exe" : "orchestrator");
|
|
361
|
+
addTask(task) {
|
|
362
|
+
this.tasks.set(task.id, { ...task, status: "pending", retryCount: 0 });
|
|
406
363
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
async function callRustTool(name, args) {
|
|
410
|
-
const binary = getBinaryPath();
|
|
411
|
-
if (!existsSync(binary)) {
|
|
412
|
-
return JSON.stringify({ error: `Binary not found: ${binary}` });
|
|
364
|
+
getTask(id) {
|
|
365
|
+
return this.tasks.get(id);
|
|
413
366
|
}
|
|
414
|
-
|
|
415
|
-
const
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
proc.stdin.end();
|
|
428
|
-
const timeout = setTimeout(() => {
|
|
429
|
-
proc.kill();
|
|
430
|
-
resolve(JSON.stringify({ error: "Timeout" }));
|
|
431
|
-
}, 6e4);
|
|
432
|
-
proc.on("close", () => {
|
|
433
|
-
clearTimeout(timeout);
|
|
434
|
-
try {
|
|
435
|
-
const lines = stdout.trim().split("\n");
|
|
436
|
-
const response = JSON.parse(lines[lines.length - 1]);
|
|
437
|
-
const text = response?.result?.content?.[0]?.text;
|
|
438
|
-
resolve(text || JSON.stringify(response.result));
|
|
439
|
-
} catch {
|
|
440
|
-
resolve(stdout || "No output");
|
|
441
|
-
}
|
|
367
|
+
updateTask(id, updates) {
|
|
368
|
+
const task = this.tasks.get(id);
|
|
369
|
+
if (task) {
|
|
370
|
+
this.tasks.set(id, { ...task, ...updates });
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
getReadyTasks() {
|
|
374
|
+
return Array.from(this.tasks.values()).filter((task) => {
|
|
375
|
+
if (task.status !== "pending") return false;
|
|
376
|
+
return task.dependencies.every((depId) => {
|
|
377
|
+
const dep = this.tasks.get(depId);
|
|
378
|
+
return dep && dep.status === "completed";
|
|
379
|
+
});
|
|
442
380
|
});
|
|
443
|
-
}
|
|
444
|
-
|
|
381
|
+
}
|
|
382
|
+
isCompleted() {
|
|
383
|
+
return Array.from(this.tasks.values()).every((t) => t.status === "completed");
|
|
384
|
+
}
|
|
385
|
+
hasFailed() {
|
|
386
|
+
return Array.from(this.tasks.values()).some((t) => t.status === "failed" && t.retryCount >= 3);
|
|
387
|
+
}
|
|
388
|
+
getTaskSummary() {
|
|
389
|
+
const tasks = Array.from(this.tasks.values());
|
|
390
|
+
const completed = tasks.filter((t) => t.status === "completed");
|
|
391
|
+
const notCompleted = tasks.filter((t) => t.status !== "completed");
|
|
392
|
+
let summary = "\u{1F4CB} **Mission Status**\n";
|
|
393
|
+
if (completed.length > 0) {
|
|
394
|
+
summary += `\u2705 Completed: ${completed.length} tasks (Hidden to save tokens)
|
|
395
|
+
`;
|
|
396
|
+
}
|
|
397
|
+
for (const task of notCompleted) {
|
|
398
|
+
const icon = task.status === "running" ? "\u23F3" : task.status === "failed" ? "\u274C" : "\u{1F4A4}";
|
|
399
|
+
summary += `${icon} [${task.id}] ${task.description}
|
|
400
|
+
`;
|
|
401
|
+
}
|
|
402
|
+
return summary;
|
|
403
|
+
}
|
|
404
|
+
toJSON() {
|
|
405
|
+
return JSON.stringify(Array.from(this.tasks.values()), null, 2);
|
|
406
|
+
}
|
|
407
|
+
static fromJSON(json) {
|
|
408
|
+
try {
|
|
409
|
+
const tasks = JSON.parse(json);
|
|
410
|
+
return new _TaskGraph(tasks);
|
|
411
|
+
} catch (e) {
|
|
412
|
+
console.error("Failed to parse TaskGraph JSON:", e);
|
|
413
|
+
return new _TaskGraph();
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
// src/core/state.ts
|
|
445
419
|
var state = {
|
|
446
420
|
missionActive: false,
|
|
447
421
|
maxIterations: 1e3,
|
|
@@ -449,6 +423,9 @@ var state = {
|
|
|
449
423
|
maxRetries: 3,
|
|
450
424
|
sessions: /* @__PURE__ */ new Map()
|
|
451
425
|
};
|
|
426
|
+
|
|
427
|
+
// src/tools/callAgent.ts
|
|
428
|
+
import { tool } from "@opencode-ai/plugin";
|
|
452
429
|
var callAgentTool = tool({
|
|
453
430
|
description: `Call a team member to perform specific work.
|
|
454
431
|
|
|
@@ -503,6 +480,9 @@ Execute according to your role. Be thorough and precise.
|
|
|
503
480
|
return prompt;
|
|
504
481
|
}
|
|
505
482
|
});
|
|
483
|
+
|
|
484
|
+
// src/tools/slashCommand.ts
|
|
485
|
+
import { tool as tool2 } from "@opencode-ai/plugin";
|
|
506
486
|
var COMMANDS = {
|
|
507
487
|
"task": {
|
|
508
488
|
description: "Execute a mission using Distributed Cognitive Architecture (PDCA Cycle)",
|
|
@@ -611,12 +591,12 @@ function createSlashcommandTool() {
|
|
|
611
591
|
const hint = cmd.argumentHint ? ` ${cmd.argumentHint}` : "";
|
|
612
592
|
return `- /${name}${hint}: ${cmd.description}`;
|
|
613
593
|
}).join("\n");
|
|
614
|
-
return
|
|
594
|
+
return tool2({
|
|
615
595
|
description: `Commands
|
|
616
596
|
|
|
617
597
|
${commandList}`,
|
|
618
598
|
args: {
|
|
619
|
-
command:
|
|
599
|
+
command: tool2.schema.string().describe("Command (without slash)")
|
|
620
600
|
},
|
|
621
601
|
async execute(args) {
|
|
622
602
|
const cmdName = (args.command || "").replace(/^\//, "").split(/\s+/)[0].toLowerCase();
|
|
@@ -631,11 +611,83 @@ ${commandList}`;
|
|
|
631
611
|
}
|
|
632
612
|
});
|
|
633
613
|
}
|
|
634
|
-
|
|
614
|
+
|
|
615
|
+
// src/tools/search.ts
|
|
616
|
+
import { tool as tool3 } from "@opencode-ai/plugin";
|
|
617
|
+
|
|
618
|
+
// src/tools/rust.ts
|
|
619
|
+
import { spawn } from "child_process";
|
|
620
|
+
import { existsSync as existsSync2 } from "fs";
|
|
621
|
+
|
|
622
|
+
// src/utils/binary.ts
|
|
623
|
+
import { join, dirname } from "path";
|
|
624
|
+
import { fileURLToPath } from "url";
|
|
625
|
+
import { platform, arch } from "os";
|
|
626
|
+
import { existsSync } from "fs";
|
|
627
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
628
|
+
function getBinaryPath() {
|
|
629
|
+
const binDir = join(__dirname, "..", "..", "bin");
|
|
630
|
+
const os = platform();
|
|
631
|
+
const cpu = arch();
|
|
632
|
+
let binaryName;
|
|
633
|
+
if (os === "win32") {
|
|
634
|
+
binaryName = "orchestrator-windows-x64.exe";
|
|
635
|
+
} else if (os === "darwin") {
|
|
636
|
+
binaryName = cpu === "arm64" ? "orchestrator-macos-arm64" : "orchestrator-macos-x64";
|
|
637
|
+
} else {
|
|
638
|
+
binaryName = cpu === "arm64" ? "orchestrator-linux-arm64" : "orchestrator-linux-x64";
|
|
639
|
+
}
|
|
640
|
+
let binaryPath = join(binDir, binaryName);
|
|
641
|
+
if (!existsSync(binaryPath)) {
|
|
642
|
+
binaryPath = join(binDir, os === "win32" ? "orchestrator.exe" : "orchestrator");
|
|
643
|
+
}
|
|
644
|
+
return binaryPath;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// src/tools/rust.ts
|
|
648
|
+
async function callRustTool(name, args) {
|
|
649
|
+
const binary = getBinaryPath();
|
|
650
|
+
if (!existsSync2(binary)) {
|
|
651
|
+
return JSON.stringify({ error: `Binary not found: ${binary}` });
|
|
652
|
+
}
|
|
653
|
+
return new Promise((resolve) => {
|
|
654
|
+
const proc = spawn(binary, ["serve"], { stdio: ["pipe", "pipe", "pipe"] });
|
|
655
|
+
let stdout = "";
|
|
656
|
+
proc.stdout.on("data", (data) => {
|
|
657
|
+
stdout += data.toString();
|
|
658
|
+
});
|
|
659
|
+
const request = JSON.stringify({
|
|
660
|
+
jsonrpc: "2.0",
|
|
661
|
+
id: 1,
|
|
662
|
+
method: "tools/call",
|
|
663
|
+
params: { name, arguments: args }
|
|
664
|
+
});
|
|
665
|
+
proc.stdin.write(request + "\n");
|
|
666
|
+
proc.stdin.end();
|
|
667
|
+
const timeout = setTimeout(() => {
|
|
668
|
+
proc.kill();
|
|
669
|
+
resolve(JSON.stringify({ error: "Timeout" }));
|
|
670
|
+
}, 6e4);
|
|
671
|
+
proc.on("close", () => {
|
|
672
|
+
clearTimeout(timeout);
|
|
673
|
+
try {
|
|
674
|
+
const lines = stdout.trim().split("\n");
|
|
675
|
+
const response = JSON.parse(lines[lines.length - 1]);
|
|
676
|
+
const text = response?.result?.content?.[0]?.text;
|
|
677
|
+
resolve(text || JSON.stringify(response.result));
|
|
678
|
+
} catch {
|
|
679
|
+
resolve(stdout || "No output");
|
|
680
|
+
}
|
|
681
|
+
});
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// src/tools/search.ts
|
|
686
|
+
var grepSearchTool = (directory) => tool3({
|
|
635
687
|
description: "Search code patterns",
|
|
636
688
|
args: {
|
|
637
|
-
pattern:
|
|
638
|
-
dir:
|
|
689
|
+
pattern: tool3.schema.string().describe("Regex pattern"),
|
|
690
|
+
dir: tool3.schema.string().optional().describe("Directory")
|
|
639
691
|
},
|
|
640
692
|
async execute(args) {
|
|
641
693
|
return callRustTool("grep_search", {
|
|
@@ -644,11 +696,11 @@ var grepSearchTool = (directory) => tool({
|
|
|
644
696
|
});
|
|
645
697
|
}
|
|
646
698
|
});
|
|
647
|
-
var globSearchTool = (directory) =>
|
|
699
|
+
var globSearchTool = (directory) => tool3({
|
|
648
700
|
description: "Find files by pattern",
|
|
649
701
|
args: {
|
|
650
|
-
pattern:
|
|
651
|
-
dir:
|
|
702
|
+
pattern: tool3.schema.string().describe("Glob pattern"),
|
|
703
|
+
dir: tool3.schema.string().optional().describe("Directory")
|
|
652
704
|
},
|
|
653
705
|
async execute(args) {
|
|
654
706
|
return callRustTool("glob_search", {
|
|
@@ -657,11 +709,15 @@ var globSearchTool = (directory) => tool({
|
|
|
657
709
|
});
|
|
658
710
|
}
|
|
659
711
|
});
|
|
712
|
+
|
|
713
|
+
// src/utils/common.ts
|
|
660
714
|
function detectSlashCommand(text) {
|
|
661
715
|
const match = text.trim().match(/^\/([a-zA-Z0-9_-]+)(?:\s+(.*))?$/);
|
|
662
716
|
if (!match) return null;
|
|
663
717
|
return { command: match[1], args: match[2] || "" };
|
|
664
718
|
}
|
|
719
|
+
|
|
720
|
+
// src/index.ts
|
|
665
721
|
var OrchestratorPlugin = async (input) => {
|
|
666
722
|
const { directory } = input;
|
|
667
723
|
return {
|
|
@@ -705,6 +761,17 @@ var OrchestratorPlugin = async (input) => {
|
|
|
705
761
|
if (textPartIndex === -1) return;
|
|
706
762
|
const originalText = parts[textPartIndex].text || "";
|
|
707
763
|
const parsed = detectSlashCommand(originalText);
|
|
764
|
+
const agentName = input2.agent?.toLowerCase() || "";
|
|
765
|
+
if (agentName === "orchestrator" && !state.missionActive) {
|
|
766
|
+
const sessionID = input2.sessionID;
|
|
767
|
+
state.sessions.set(sessionID, {
|
|
768
|
+
enabled: true,
|
|
769
|
+
iterations: 0,
|
|
770
|
+
taskRetries: /* @__PURE__ */ new Map(),
|
|
771
|
+
currentTask: ""
|
|
772
|
+
});
|
|
773
|
+
state.missionActive = true;
|
|
774
|
+
}
|
|
708
775
|
if (parsed) {
|
|
709
776
|
const command = COMMANDS[parsed.command];
|
|
710
777
|
if (command) {
|
|
@@ -738,12 +805,8 @@ var OrchestratorPlugin = async (input) => {
|
|
|
738
805
|
}
|
|
739
806
|
}
|
|
740
807
|
if (session.iterations >= state.maxIterations) {
|
|
808
|
+
state.missionActive = false;
|
|
741
809
|
session.enabled = false;
|
|
742
|
-
output.output += `
|
|
743
|
-
|
|
744
|
-
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
745
|
-
\u26A0\uFE0F ITERATION LIMIT (${state.maxIterations})
|
|
746
|
-
Review progress and continue manually.`;
|
|
747
810
|
return;
|
|
748
811
|
}
|
|
749
812
|
if (output.output.includes("[") && output.output.includes("]") && output.output.includes("{") && input2.tool === "call_agent") {
|
|
@@ -839,6 +902,27 @@ ${session.graph.getTaskSummary()}${guidance}`;
|
|
|
839
902
|
|
|
840
903
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
841
904
|
[DAG STEP: ${session.iterations}/${state.maxIterations}]`;
|
|
905
|
+
},
|
|
906
|
+
// Relentless Loop: Auto-continue until mission complete
|
|
907
|
+
"assistant.done": async (input2, output) => {
|
|
908
|
+
if (!state.missionActive) return;
|
|
909
|
+
const session = state.sessions.get(input2.sessionID);
|
|
910
|
+
if (!session?.enabled) return;
|
|
911
|
+
const text = output.text || "";
|
|
912
|
+
const isComplete = text.includes("\u2705 MISSION COMPLETE") || text.includes("MISSION COMPLETE") || text.includes("\uBAA8\uB4E0 \uD0DC\uC2A4\uD06C \uC644\uB8CC") || text.includes("All tasks completed") || session.graph && session.graph.isCompleted?.();
|
|
913
|
+
if (isComplete) {
|
|
914
|
+
session.enabled = false;
|
|
915
|
+
state.missionActive = false;
|
|
916
|
+
state.sessions.delete(input2.sessionID);
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
if (session.iterations >= state.maxIterations) {
|
|
920
|
+
session.enabled = false;
|
|
921
|
+
state.missionActive = false;
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
output.continue = true;
|
|
925
|
+
output.continueMessage = "continue";
|
|
842
926
|
}
|
|
843
927
|
};
|
|
844
928
|
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const callAgentTool: {
|
|
2
|
+
description: string;
|
|
3
|
+
args: {
|
|
4
|
+
agent: import("zod").ZodEnum<{
|
|
5
|
+
planner: "planner";
|
|
6
|
+
coder: "coder";
|
|
7
|
+
reviewer: "reviewer";
|
|
8
|
+
fixer: "fixer";
|
|
9
|
+
searcher: "searcher";
|
|
10
|
+
}>;
|
|
11
|
+
task: import("zod").ZodString;
|
|
12
|
+
context: import("zod").ZodOptional<import("zod").ZodString>;
|
|
13
|
+
};
|
|
14
|
+
execute(args: {
|
|
15
|
+
agent: "planner" | "coder" | "reviewer" | "fixer" | "searcher";
|
|
16
|
+
task: string;
|
|
17
|
+
context?: string | undefined;
|
|
18
|
+
}, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
19
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function callRustTool(name: string, args: Record<string, unknown>): Promise<string>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare const grepSearchTool: (directory: string) => {
|
|
2
|
+
description: string;
|
|
3
|
+
args: {
|
|
4
|
+
pattern: import("zod").ZodString;
|
|
5
|
+
dir: import("zod").ZodOptional<import("zod").ZodString>;
|
|
6
|
+
};
|
|
7
|
+
execute(args: {
|
|
8
|
+
pattern: string;
|
|
9
|
+
dir?: string | undefined;
|
|
10
|
+
}, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
11
|
+
};
|
|
12
|
+
export declare const globSearchTool: (directory: string) => {
|
|
13
|
+
description: string;
|
|
14
|
+
args: {
|
|
15
|
+
pattern: import("zod").ZodString;
|
|
16
|
+
dir: import("zod").ZodOptional<import("zod").ZodString>;
|
|
17
|
+
};
|
|
18
|
+
execute(args: {
|
|
19
|
+
pattern: string;
|
|
20
|
+
dir?: string | undefined;
|
|
21
|
+
}, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
22
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const COMMANDS: Record<string, {
|
|
2
|
+
description: string;
|
|
3
|
+
template: string;
|
|
4
|
+
argumentHint?: string;
|
|
5
|
+
}>;
|
|
6
|
+
export declare function createSlashcommandTool(): {
|
|
7
|
+
description: string;
|
|
8
|
+
args: {
|
|
9
|
+
command: import("zod").ZodString;
|
|
10
|
+
};
|
|
11
|
+
execute(args: {
|
|
12
|
+
command: string;
|
|
13
|
+
}, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
14
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getBinaryPath(): string;
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "opencode-orchestrator",
|
|
3
3
|
"displayName": "OpenCode Orchestrator",
|
|
4
4
|
"description": "Distributed Cognitive Architecture for OpenCode. Turns simple prompts into specialized multi-agent workflows (Planner, Coder, Reviewer).",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.57",
|
|
6
6
|
"author": "agnusdei1207",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"repository": {
|