opencode-mux
v1.3.0
Published
Drop-in opencode wrapper that opens tmux panes for subagent sessions automatically
Maintainers
Readme
opencode-mux
A drop-in wrapper for opencode that opens tmux panes for subagent sessions automatically — and makes the whole layout look coherent no matter what your tmux is configured to look like.
When a session running inside opencode-mux spawns a subagent, a new tmux pane
opens showing that session live. When the subagent finishes, the pane closes
itself and the remaining subagent panes re-tile to share the space evenly. The subagent panes use opencode's mini frontend by default (configurable via --pane-command or pane_command in the config file) — no tab bar, no agent switcher.
┌──────────────────────────────────────────┬──────────────┐
│ │ subagent │
│ main opencode session │ (mini UI) │
│ (full TUI) ├──────────────┤
│ │ subagent │
│ │ (mini UI) │
└──────────────────────────────────────────┴──────────────┘- Drop-in — run it exactly like
opencode; every other argument is passed through unchanged. - Zero config intrusion — never writes tmux
-goptions and never touches your~/.tmux.conf,~/.config/opencode/cli.json, ortui.json. - Coherent look — pane borders are styled to match your active opencode theme, and the session opts are scoped to the mux session only.
- Concurrent instances — one mux per project, each with its own tmux session and its own subagent panes.
Requirements
| Tool | Version | | ---- | ------- | | Node.js | >= 20 | | tmux | 3.x (tested on 3.7) | | opencode | v2 (2.0.0–2.0.5 and 2.0.6+ both supported) |
Install
npm install -g opencode-muxTo use it as your default opencode:
alias opencode="opencode-mux"(binaries are opencode-mux, version 1.3.0)
Usage
opencode-mux [mux options] [opencode arguments...]Examples
# Just work in the current directory
opencode-mux
# Continue an existing session
opencode-mux -s ses_abc123
# Pass opencode's own flags through, with a tiled pane layout
opencode-mux --layout tiled --model fast
# Running inside an existing tmux session also works — subagent panes are
# opened in the session you're already in, and already-running subagents are
# adopted without a startup flash.
# A second instance (another project, another terminal) is fine: it gets its
# own tmux session and only panes that project's subagents.Mux options
| Option | Description | Default |
| ------ | ----------- | ------- |
| --config <path> | Config file to use | ~/.config/opencode-mux.conf |
| --layout <name> | Pane layout: main-vertical, main-horizontal, tiled, even-horizontal, even-vertical | main-vertical |
| --main-pane-size <pct> | Main pane size for main-* layouts, percent of the window | 60 |
| --close <mode> | Pane lifecycle: auto (close when the subagent finishes) or keep | auto |
| --pane-command <cmd> | Subagent pane frontend: mini (minimal UI) or tui (full TUI) | mini |
| --grace <seconds> | Delay before a finished subagent's pane closes | 1 |
| --parent <id> | Only spawn panes for children of this session | inferred from -s/--session |
| --session-name <name> | Base name for this instance's tmux session (suffixed when the name is taken) | mux |
| -h, --help | Show help | |
| --version | Print the version | |
Everything else (e.g. --model, --agent, -i prompts, a directory) is passed
through to opencode unchanged.
Config file
~/.config/opencode-mux.conf (honors XDG_CONFIG_HOME) is optional and is
auto-created with defaults on first run. Keys are key=value, lines starting
with # are comments, and unknown keys are ignored for forward compatibility.
# opencode-mux configuration
session_name=mux
layout=main-vertical
main_pane_size=60
close_panes=auto
pane_command=mini
grace=1
parent=Precedence: CLI flags > config file > defaults.
How it works
- Outside tmux: opencode-mux creates a detached tmux session named
mux(or--session-name), applies session-scopedstatus offandmouse on, re-executes itself inside, and attaches. Nothing kills that session: it ends when its TUI pane exits, so detaching (prefix+d) keeps it running for reattach, and any window you open inside it survives. Ownership is never passed through tmux session environment, so a nested launch (a shell tool, a second window in that session) can never tear its host down. Session options and attach target the session id tmux printed at creation (new-session -P -F '#{session_id}', validated as$N), so a stale name can never match another instance's session. When tmux prints no usable id, the fallback targets the bare session name forset-option(the only form it resolves) and=namefor attach; appearance failures stay non-fatal. - Inside tmux: the wrapper runs as a watcher. It connects to the opencode
server (same background service you're already using), listens for
subagent-session events, opens a pane per child session via
opencode mini -s <id>, styles the pane borders to match the active opencode theme, and closes panes once their session finishes (after the grace period). If the opencode daemon restarts, the watcher resubscribes after about a second and re-adopts the subagents that are still running. Every pane split and every pane close re-applies the configured layout, so the main pane keeps its configured size and the subagent panes always split the remaining space evenly.
Several instances at once
Each instance owns its tmux session, so you can run one per project concurrently — in different terminals, all sharing the same opencode background service:
- Own session per instance. Outside tmux the wrapper takes the configured
base name (
mux) when it is free. If another instance (or a leftover) already owns it, the next name is used and reported:mux-<project>, thenmux-<project>-2, and so on. A session is never shared between instances, and nothing ever kills one: it ends when its own TUI pane exits, so a detached session keeps running for reattach. - Own panes per instance. Without an explicit
--parent/-s, a watcher only panes subagents whose parent session belongs to its directory (the cwd, or an explicit opencode<directory>argument). Two instances on different projects therefore never mirror each other's subagents. Inside one project, pin the parent with--parent <session-id>when you run two instances side by side. A-svalue that is not a session id is reported rather than silently widening the scope. - Own window per instance. Each instance tags the panes it creates
(pane-scoped
@mux-instance) and re-tiles only windows it has to itself. Running two instances inside one window is not a supported layout — the panes and the layout belong to the window, not to an instance — but it degrades predictably instead of oscillating: the first instance keeps the geometry, and a second instance never clears window-scoped border styles.
Features
Subagent panes without the chrome
Subagent panes attach with opencode mini, the minimal frontend: no top tab
strip, no agent switcher or model panel — just the conversation and a one-line
footer. It streams live updates while the subagent works, accepts input, and
stays attached until the watcher closes the pane.
Theme-matched borders
Pane borders follow your active opencode theme (border.default, falling back
to the border color). Paraphrased, it looks good no matter what anyone has
configured their setup to look like:
- Theme is resolved from your opencode config: project
.opencode/cli.json→ project.opencode/tui.json→ globalcli.json→ globaltui.json(first match wins; v2 object and legacy string forms both supported). - Built-in themes come from a vendored table of all opencode v2.0.5 built-in themes; custom theme files are resolved from your config/theme dirs.
- A
systemtheme or an unknown/unresolvable theme results in no border styling — never wrong colors. - Config changes are re-applied live (checked on the watcher tick), and borders are cleared when the theme becomes unresolvable — unless another instance shares the window, in which case the clear is skipped and retried on the next pane open/close instead. Zero tmux calls while nothing changed.
Never touches your setup
All tmux writes are runtime-scoped: window options are applied with -w
(pane-border colors), session options with -t <session> (status/mouse), and
every pane this instance creates is tagged with a pane-scoped @mux-instance
option, so a second instance can tell the window is shared. No -g, no writes
to ~/.tmux.conf, no changes to your opencode configs.
Requirements & compatibility
- Handles opencode's two v2 server generations transparently: 2.0.0–2.0.5
(
/api/status) and 2.0.6+ (/api/info). - Depends on tmux; fails with a clear error if tmux or opencode v2 is missing.
Development
npm install
npm run check # typecheck (tsc --noEmit)
npm test # vitest suite
npm run build # tsc -p tsconfig.build.json → dist/
npm run smoke # e2e smoke (needs a staging environment; blocked locally)License
MIT
