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.
Plugin built on the public OpenCode SDK. No internal dependencies.
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.
A real interaction:
You: "Add input validation to all API endpoints and write tests for each one."
The lead agent:
1. Creates a team called "validation"
2. Adds tasks to the shared board, one per endpoint
3. Spawns 3 teammates:
- alice: validate user endpoints (POST /users, PUT /users/:id)
- bob: validate order endpoints (POST /orders, PUT /orders/:id)
- carol: write integration tests for all validated endpoints
4. carol's tasks depend on alice and bob finishing first
Alice and bob work simultaneously. You see toast notifications as they progress:
[toast] Teammate alice spawned (build)
[toast] Teammate bob spawned (build)
[toast] Teammate carol spawned (build)
Teammates talk to each other and to the lead:
alice -> lead: "User validation done. Added zod schemas to POST /users and PUT /users/:id."
bob -> lead: "Order validation done. Found an edge case in PUT /orders/:id, negative quantities were allowed."
bob -> alice: "Did you handle email format validation? I want to match the pattern for order contact emails."
alice -> bob: "Yes, using z.string().email(). See src/validators/user.ts line 12."
When alice and bob finish, carol's blocked tasks unblock automatically. Carol starts writing tests using the validation schemas they created.
Check on things at any time:
You: "How's the team doing?"
Lead calls team_status:
Team: validation (you are the lead)
Members:
alice [idle] agent: build session: ses_abc
bob [idle] agent: build session: ses_def
carol [working] agent: build session: ses_ghi
Tasks: 5 total (3 completed, 1 in_progress, 1 pending)
Want to see what carol is doing? The lead can switch your view to her session:
You: "Show me what carol is working on."
Lead calls team_view({ member: "carol" })
-> TUI switches to carol's session, showing her full chat log
-> Use the session picker (ctrl+p) to go back to the lead
When everything is done, the lead shuts down teammates and cleans up:
[toast] alice shut down
[toast] bob shut down
[toast] carol shut down
Lead: "All validation and tests are complete. 5 endpoints validated,
12 test cases added. Team cleaned up."
Add the plugin to your OpenCode config. Project-level or global.
Project-level (opencode.json in your project root):
{
"plugin": ["@hueyexe/opencode-ensemble"]
}Global (~/.config/opencode/opencode.json):
{
"plugin": ["@hueyexe/opencode-ensemble"]
}OpenCode auto-installs npm plugins at startup (cached in ~/.cache/opencode/node_modules/). No npm install needed.
For working on the plugin itself, create .opencode/plugins/ensemble.ts in your project:
export { default } from "@hueyexe/opencode-ensemble"Or point directly at the source checkout:
export { default } from "/path/to/opencode-ensemble/src/index.ts"13 tools. The lead has all of them. Teammates get 6 (messaging + tasks).
Team lifecycle (lead only)
| Tool | What it does |
|---|---|
team_create |
Create a team. Caller becomes the lead. |
team_spawn |
Start a new teammate with a task. Supports plan_approval mode. |
team_shutdown |
Ask a teammate to stop. Supports force flag for immediate abort. |
team_cleanup |
Remove the team when done. |
team_status |
See all members, their status, and a task summary. |
team_view |
Switch the TUI to a teammate's session. |
Communication (everyone)
| Tool | What it does |
|---|---|
team_message |
Send a direct message to a teammate or the lead. Also handles plan approval/rejection. |
team_broadcast |
Message everyone on the team. |
team_results |
Retrieve full message content (messages to lead are truncated on delivery). |
Task board (everyone)
| Tool | What it does |
|---|---|
team_tasks_list |
See all tasks with status and assignee. |
team_tasks_add |
Add tasks to the shared board. |
team_tasks_complete |
Mark a task done. Unblocks dependents. |
team_claim |
Claim a pending task. Atomic, prevents double-claims. |
The plugin works within OpenCode's existing TUI. No custom team panel (that requires core TUI changes, which are in progress upstream).
What you get:
- Toast notifications when teammates spawn, finish, error, shut down, or get rate-limited
- Working progress toasts showing who's still active after every status change (e.g. "Working: alice, bob (2/3)")
- Rich tool titles in the sidebar (e.g. "Spawned alice (build)", "Message -> bob", "Task board (3 tasks)")
- Session switching via
team_viewto see any teammate's full chat log - Status checks via
team_statusfor a snapshot of the whole team
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.
- SQLite (
bun:sqlite, WAL mode) for teams, members, tasks, and messages - promptAsync for message delivery: injects a message and starts the prompt loop in one call
- Git worktree isolation: each teammate gets their own worktree by default, so multiple agents can edit files without conflicts. Opt out with
worktree: falsefor read-only agents. - 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.
- Compaction safety: team context is preserved when OpenCode compacts long conversations
- Shell environment: teammate shells get
ENSEMBLE_TEAM,ENSEMBLE_MEMBER,ENSEMBLE_ROLE, andENSEMBLE_BRANCHvariables - Sub-agent isolation: teammates' sub-agents can't use team tools (parent chain tracking, max depth 10)
- Crash recovery: stale busy members marked as errored on restart, orphaned sessions aborted, orphaned worktrees cleaned up, undelivered messages redelivered
- Spawn rollback: if the initial prompt fails, the member, session, and worktree are all cleaned up
- Timeout watchdog: teammates stuck busy beyond the TTL are automatically timed out and aborted
- Graceful shutdown: busy teammates receive a shutdown message and finish their current work. Use
force: trueto abort immediately. - Rate limiting: token bucket (configurable via
OPENCODE_ENSEMBLE_RATE_LIMIT, default 10 tokens/sec)
# Adjust rate limit (default: 10 tokens, refills 2/sec)
OPENCODE_ENSEMBLE_RATE_LIMIT=20
# Disable rate limiting
OPENCODE_ENSEMBLE_RATE_LIMIT=0
# Adjust teammate timeout (default: 1800000ms = 30 minutes)
OPENCODE_ENSEMBLE_TIMEOUT=3600000
# Disable timeout watchdog
OPENCODE_ENSEMBLE_TIMEOUT=0- Start with 2-3 teammates. More agents means more coordination overhead.
- Give each teammate specific, self-contained tasks. Vague prompts produce vague results.
- Spawn an explore agent first to understand the codebase, then spawn build agents with that context.
- Use
worktree: falsefor read-only agents (research, review, code analysis). - Use
plan_approval: truefor risky changes. The teammate sends a plan first, you review and approve before they write any code. - Don't micromanage. Teammates message you when done or when they're blocked.
- Don't poll
team_statusin a loop. Wait for messages.
Same coordination model (shared tasks, peer messaging, lead coordination) with some additions:
- Git worktree isolation by default: each teammate gets their own branch, no merge conflicts between parallel agents
- System prompt injection: the lead's system prompt is updated with team state so it stays aware across turns
- Compaction safety: team context is preserved when sessions get long
- Team-aware shell environment:
ENSEMBLE_TEAM,ENSEMBLE_MEMBER,ENSEMBLE_ROLE,ENSEMBLE_BRANCH - Graceful shutdown: teammates finish current work before stopping, with a force flag for emergencies
- Plan approval mode: review teammate plans before they write code
- Works today as a plugin: install and go, no upstream changes needed
bun install
bun run typecheck
bun test # 286 tests
bun run buildSee CONTRIBUTING.md for development guidelines.
MIT