@hueyexe/opencode-ensemble 0.5.0 → 0.5.1
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 +47 -44
- package/dist/index.js +4 -3
- package/package.json +1 -1
- package/src/index.ts +4 -3
package/README.md
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
# OpenCode Ensemble
|
|
2
2
|
|
|
3
|
-
Run
|
|
3
|
+
Run parallel AI agents in OpenCode. Each agent gets its own session, context window, and task. They coordinate through messaging and a shared task board.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Plugin built on the public OpenCode SDK. No internal dependencies.
|
|
6
6
|
|
|
7
7
|
## What actually happens
|
|
8
8
|
|
|
9
|
-
You ask the agent to do something complex.
|
|
9
|
+
You ask the agent to do something complex. It creates a team, spawns teammates, and they work in parallel. Each teammate runs in its own OpenCode session with a fresh context window.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
A real interaction:
|
|
12
12
|
|
|
13
13
|
```
|
|
14
14
|
You: "Add input validation to all API endpoints and write tests for each one."
|
|
15
15
|
|
|
16
16
|
The lead agent:
|
|
17
17
|
1. Creates a team called "validation"
|
|
18
|
-
2. Adds tasks to the shared board
|
|
18
|
+
2. Adds tasks to the shared board, one per endpoint
|
|
19
19
|
3. Spawns 3 teammates:
|
|
20
20
|
- alice: validate user endpoints (POST /users, PUT /users/:id)
|
|
21
21
|
- bob: validate order endpoints (POST /orders, PUT /orders/:id)
|
|
@@ -23,7 +23,7 @@ The lead agent:
|
|
|
23
23
|
4. carol's tasks depend on alice and bob finishing first
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
Alice and bob work simultaneously. You see toast notifications as they progress:
|
|
27
27
|
|
|
28
28
|
```
|
|
29
29
|
[toast] Teammate alice spawned (build)
|
|
@@ -31,18 +31,18 @@ From here, things happen in parallel. Alice and bob work simultaneously in their
|
|
|
31
31
|
[toast] Teammate carol spawned (build)
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
Teammates talk to each other and to the lead:
|
|
35
35
|
|
|
36
36
|
```
|
|
37
37
|
alice -> lead: "User validation done. Added zod schemas to POST /users and PUT /users/:id."
|
|
38
|
-
bob -> lead: "Order validation done. Found an edge case in PUT /orders/:id
|
|
38
|
+
bob -> lead: "Order validation done. Found an edge case in PUT /orders/:id, negative quantities were allowed."
|
|
39
39
|
bob -> alice: "Did you handle email format validation? I want to match the pattern for order contact emails."
|
|
40
|
-
alice -> bob: "Yes, using z.string().email()
|
|
40
|
+
alice -> bob: "Yes, using z.string().email(). See src/validators/user.ts line 12."
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
When alice and bob finish, carol's blocked tasks unblock automatically. Carol starts writing tests using the
|
|
43
|
+
When alice and bob finish, carol's blocked tasks unblock automatically. Carol starts writing tests using the validation schemas they created.
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
Check on things at any time:
|
|
46
46
|
|
|
47
47
|
```
|
|
48
48
|
You: "How's the team doing?"
|
|
@@ -56,7 +56,7 @@ Lead calls team_status:
|
|
|
56
56
|
Tasks: 5 total (3 completed, 1 in_progress, 1 pending)
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
Want to see what carol is
|
|
59
|
+
Want to see what carol is doing? The lead can switch your view to her session:
|
|
60
60
|
|
|
61
61
|
```
|
|
62
62
|
You: "Show me what carol is working on."
|
|
@@ -79,9 +79,9 @@ Lead: "All validation and tests are complete. 5 endpoints validated,
|
|
|
79
79
|
|
|
80
80
|
## Install
|
|
81
81
|
|
|
82
|
-
Add the plugin to your OpenCode config.
|
|
82
|
+
Add the plugin to your OpenCode config. Project-level or global.
|
|
83
83
|
|
|
84
|
-
**Project-level**
|
|
84
|
+
**Project-level** (`opencode.json` in your project root):
|
|
85
85
|
|
|
86
86
|
```json
|
|
87
87
|
{
|
|
@@ -89,7 +89,7 @@ Add the plugin to your OpenCode config. This can be project-level or global.
|
|
|
89
89
|
}
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
-
**Global**
|
|
92
|
+
**Global** (`~/.config/opencode/opencode.json`):
|
|
93
93
|
|
|
94
94
|
```json
|
|
95
95
|
{
|
|
@@ -97,7 +97,7 @@ Add the plugin to your OpenCode config. This can be project-level or global.
|
|
|
97
97
|
}
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
OpenCode auto-installs npm plugins at startup (cached in `~/.cache/opencode/node_modules/`). No `npm install` needed.
|
|
101
101
|
|
|
102
102
|
### Local development
|
|
103
103
|
|
|
@@ -115,15 +115,15 @@ export { default } from "/path/to/opencode-ensemble/src/index.ts"
|
|
|
115
115
|
|
|
116
116
|
## Tools
|
|
117
117
|
|
|
118
|
-
13 tools
|
|
118
|
+
13 tools. The lead has all of them. Teammates get 6 (messaging + tasks).
|
|
119
119
|
|
|
120
120
|
**Team lifecycle** (lead only)
|
|
121
121
|
|
|
122
122
|
| Tool | What it does |
|
|
123
123
|
|------|-------------|
|
|
124
124
|
| `team_create` | Create a team. Caller becomes the lead. |
|
|
125
|
-
| `team_spawn` | Start a new teammate with a task. |
|
|
126
|
-
| `team_shutdown` | Ask a teammate to stop. |
|
|
125
|
+
| `team_spawn` | Start a new teammate with a task. Supports `plan_approval` mode. |
|
|
126
|
+
| `team_shutdown` | Ask a teammate to stop. Supports `force` flag for immediate abort. |
|
|
127
127
|
| `team_cleanup` | Remove the team when done. |
|
|
128
128
|
| `team_status` | See all members, their status, and a task summary. |
|
|
129
129
|
| `team_view` | Switch the TUI to a teammate's session. |
|
|
@@ -132,7 +132,7 @@ export { default } from "/path/to/opencode-ensemble/src/index.ts"
|
|
|
132
132
|
|
|
133
133
|
| Tool | What it does |
|
|
134
134
|
|------|-------------|
|
|
135
|
-
| `team_message` | Send a direct message to a teammate or the lead. |
|
|
135
|
+
| `team_message` | Send a direct message to a teammate or the lead. Also handles plan approval/rejection. |
|
|
136
136
|
| `team_broadcast` | Message everyone on the team. |
|
|
137
137
|
| `team_results` | Retrieve full message content (messages to lead are truncated on delivery). |
|
|
138
138
|
|
|
@@ -147,28 +147,31 @@ export { default } from "/path/to/opencode-ensemble/src/index.ts"
|
|
|
147
147
|
|
|
148
148
|
## What you see in the TUI
|
|
149
149
|
|
|
150
|
-
The plugin works within OpenCode's existing TUI.
|
|
150
|
+
The plugin works within OpenCode's existing TUI. No custom team panel (that requires core TUI changes, which are [in progress upstream](https://github.com/sst/opencode)).
|
|
151
151
|
|
|
152
|
-
What you
|
|
152
|
+
What you get:
|
|
153
153
|
|
|
154
154
|
- **Toast notifications** when teammates spawn, finish, error, shut down, or get rate-limited
|
|
155
155
|
- **Working progress toasts** showing who's still active after every status change (e.g. "Working: alice, bob (2/3)")
|
|
156
156
|
- **Rich tool titles** in the sidebar (e.g. "Spawned alice (build)", "Message -> bob", "Task board (3 tasks)")
|
|
157
157
|
- **Session switching** via `team_view` to see any teammate's full chat log
|
|
158
|
-
- **Status checks** via `team_status` for a snapshot of the whole team
|
|
158
|
+
- **Status checks** via `team_status` for a snapshot of the whole team
|
|
159
159
|
|
|
160
|
-
Teammate messages arrive in the lead's session as `[Team message from alice]: ...` blocks. They look like user messages
|
|
160
|
+
Teammate messages arrive in the lead's session as `[Team message from alice]: ...` blocks. They look like user messages because that's how `promptAsync` delivery works. Content is clearly labeled with the sender's name.
|
|
161
161
|
|
|
162
162
|
## Architecture
|
|
163
163
|
|
|
164
164
|
- **SQLite** (`bun:sqlite`, WAL mode) for teams, members, tasks, and messages
|
|
165
165
|
- **promptAsync** for message delivery: injects a message and starts the prompt loop in one call
|
|
166
|
-
- **Git worktree isolation**: each teammate gets their own worktree by default, so multiple agents can edit files without conflicts.
|
|
166
|
+
- **Git worktree isolation**: each teammate gets their own worktree by default, so multiple agents can edit files without conflicts. Opt out with `worktree: false` for read-only agents.
|
|
167
|
+
- **System prompt injection**: the lead's system prompt includes team state (member statuses, task counts) on every LLM call. Teammates get a short role reminder.
|
|
168
|
+
- **Compaction safety**: team context is preserved when OpenCode compacts long conversations
|
|
169
|
+
- **Shell environment**: teammate shells get `ENSEMBLE_TEAM`, `ENSEMBLE_MEMBER`, `ENSEMBLE_ROLE`, and `ENSEMBLE_BRANCH` variables
|
|
167
170
|
- **Sub-agent isolation**: teammates' sub-agents can't use team tools (parent chain tracking, max depth 10)
|
|
168
171
|
- **Crash recovery**: stale busy members marked as errored on restart, orphaned sessions aborted, orphaned worktrees cleaned up, undelivered messages redelivered
|
|
169
172
|
- **Spawn rollback**: if the initial prompt fails, the member, session, and worktree are all cleaned up
|
|
170
173
|
- **Timeout watchdog**: teammates stuck busy beyond the TTL are automatically timed out and aborted
|
|
171
|
-
- **
|
|
174
|
+
- **Graceful shutdown**: busy teammates receive a shutdown message and finish their current work. Use `force: true` to abort immediately.
|
|
172
175
|
- **Rate limiting**: token bucket (configurable via `OPENCODE_ENSEMBLE_RATE_LIMIT`, default 10 tokens/sec)
|
|
173
176
|
|
|
174
177
|
## Configuration
|
|
@@ -187,34 +190,34 @@ OPENCODE_ENSEMBLE_TIMEOUT=3600000
|
|
|
187
190
|
OPENCODE_ENSEMBLE_TIMEOUT=0
|
|
188
191
|
```
|
|
189
192
|
|
|
190
|
-
|
|
193
|
+
## Best practices
|
|
191
194
|
|
|
192
|
-
- Start with 2-
|
|
193
|
-
- Give each teammate
|
|
194
|
-
-
|
|
195
|
-
- Use `worktree: false` for read-only agents (research, review, code analysis).
|
|
196
|
-
- Use `plan_approval: true` for risky changes
|
|
197
|
-
-
|
|
198
|
-
-
|
|
195
|
+
- Start with 2-3 teammates. More agents means more coordination overhead.
|
|
196
|
+
- Give each teammate specific, self-contained tasks. Vague prompts produce vague results.
|
|
197
|
+
- Spawn an explore agent first to understand the codebase, then spawn build agents with that context.
|
|
198
|
+
- Use `worktree: false` for read-only agents (research, review, code analysis).
|
|
199
|
+
- Use `plan_approval: true` for risky changes. The teammate sends a plan first, you review and approve before they write any code.
|
|
200
|
+
- Don't micromanage. Teammates message you when done or when they're blocked.
|
|
201
|
+
- Don't poll `team_status` in a loop. Wait for messages.
|
|
199
202
|
|
|
200
|
-
|
|
203
|
+
## How this differs from Claude Code agent teams
|
|
201
204
|
|
|
202
|
-
|
|
205
|
+
Same coordination model (shared tasks, peer messaging, lead coordination) with some additions:
|
|
203
206
|
|
|
204
|
-
- **Git worktree isolation by default
|
|
205
|
-
- **System prompt injection
|
|
206
|
-
- **Compaction safety
|
|
207
|
-
- **Team-aware shell environment
|
|
208
|
-
- **Graceful shutdown
|
|
209
|
-
- **Plan approval mode
|
|
210
|
-
- **Works today as a plugin
|
|
207
|
+
- **Git worktree isolation by default**: each teammate gets their own branch, no merge conflicts between parallel agents
|
|
208
|
+
- **System prompt injection**: the lead's system prompt is updated with team state so it stays aware across turns
|
|
209
|
+
- **Compaction safety**: team context is preserved when sessions get long
|
|
210
|
+
- **Team-aware shell environment**: `ENSEMBLE_TEAM`, `ENSEMBLE_MEMBER`, `ENSEMBLE_ROLE`, `ENSEMBLE_BRANCH`
|
|
211
|
+
- **Graceful shutdown**: teammates finish current work before stopping, with a force flag for emergencies
|
|
212
|
+
- **Plan approval mode**: review teammate plans before they write code
|
|
213
|
+
- **Works today as a plugin**: install and go, no upstream changes needed
|
|
211
214
|
|
|
212
215
|
## Development
|
|
213
216
|
|
|
214
217
|
```bash
|
|
215
218
|
bun install
|
|
216
219
|
bun run typecheck
|
|
217
|
-
bun test #
|
|
220
|
+
bun test # 286 tests
|
|
218
221
|
bun run build
|
|
219
222
|
```
|
|
220
223
|
|
package/dist/index.js
CHANGED
|
@@ -13708,9 +13708,10 @@ var plugin = async (input) => {
|
|
|
13708
13708
|
args: {},
|
|
13709
13709
|
async execute(_args, ctx) {
|
|
13710
13710
|
const result = await executeTeamStatus(deps, ctx.sessionID);
|
|
13711
|
-
const
|
|
13712
|
-
const
|
|
13713
|
-
|
|
13711
|
+
const statusMap = { busy: "working", ready: "idle", shutdown_requested: "stopping", shutdown: "done", error: "error" };
|
|
13712
|
+
const members = deps.db.query("SELECT name, status FROM team_member WHERE team_id IN (SELECT id FROM team WHERE lead_session_id = ? OR id IN (SELECT team_id FROM team_member WHERE session_id = ?))").all(ctx.sessionID, ctx.sessionID);
|
|
13713
|
+
const summary = members.map((m) => `${m.name}: ${statusMap[m.status] ?? m.status}`).join(", ");
|
|
13714
|
+
ctx.metadata({ title: summary || "No teammates" });
|
|
13714
13715
|
return result;
|
|
13715
13716
|
}
|
|
13716
13717
|
}),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hueyexe/opencode-ensemble",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Agent teams for OpenCode — parallel agents with peer-to-peer communication, shared tasks, and coordinated execution",
|
|
5
5
|
"module": "src/index.ts",
|
|
6
6
|
"main": "dist/index.js",
|
package/src/index.ts
CHANGED
|
@@ -346,9 +346,10 @@ const plugin: Plugin = async (input) => {
|
|
|
346
346
|
args: {},
|
|
347
347
|
async execute(_args, ctx) {
|
|
348
348
|
const result = await executeTeamStatus(deps, ctx.sessionID)
|
|
349
|
-
const
|
|
350
|
-
const
|
|
351
|
-
|
|
349
|
+
const statusMap: Record<string, string> = { busy: "working", ready: "idle", shutdown_requested: "stopping", shutdown: "done", error: "error" }
|
|
350
|
+
const members = deps.db.query("SELECT name, status FROM team_member WHERE team_id IN (SELECT id FROM team WHERE lead_session_id = ? OR id IN (SELECT team_id FROM team_member WHERE session_id = ?))").all(ctx.sessionID, ctx.sessionID) as Array<{ name: string; status: string }>
|
|
351
|
+
const summary = members.map(m => `${m.name}: ${statusMap[m.status] ?? m.status}`).join(", ")
|
|
352
|
+
ctx.metadata({ title: summary || "No teammates" })
|
|
352
353
|
return result
|
|
353
354
|
},
|
|
354
355
|
}),
|