opencode-mux 0.1.0

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 opencode-mux contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,173 @@
1
+ # opencode-mux
2
+
3
+ A drop-in wrapper for [opencode](https://opencode.ai) that opens tmux panes for
4
+ subagent sessions automatically — and makes the whole layout look coherent no
5
+ matter what your tmux is configured to look like.
6
+
7
+ When a session running inside opencode-mux spawns a subagent, a new tmux pane
8
+ opens showing that session live. When the subagent finishes, the pane closes
9
+ itself. The subagent panes run opencode's minimal `mini` frontend, so they show
10
+ nothing but the conversation — no tab bar, no agent switcher.
11
+
12
+ ```
13
+ ┌──────────────────────────────────────────┬──────────────┐
14
+ │ │ subagent │
15
+ │ main opencode session │ (mini UI) │
16
+ │ (full TUI) ├──────────────┤
17
+ │ │ subagent │
18
+ │ │ (mini UI) │
19
+ └──────────────────────────────────────────┴──────────────┘
20
+ ```
21
+
22
+ - **Drop-in** — run it exactly like `opencode`; every other argument is passed
23
+ through unchanged.
24
+ - **Zero config intrusion** — never writes tmux `-g` options and never touches
25
+ your `~/.tmux.conf`, `~/.config/opencode/cli.json`, or `tui.json`.
26
+ - **Coherent look** — pane borders are styled to match your active opencode
27
+ theme, and the session opts are scoped to the mux session only.
28
+
29
+ ## Requirements
30
+
31
+ | Tool | Version |
32
+ | ---- | ------- |
33
+ | Node.js | >= 20 |
34
+ | tmux | 3.x (tested on 3.7) |
35
+ | opencode | v2 (2.0.0–2.0.5 and 2.0.6+ both supported) |
36
+
37
+ ## Install
38
+
39
+ ```sh
40
+ npm install -g opencode-mux
41
+ ```
42
+
43
+ To use it as your default `opencode`:
44
+
45
+ ```sh
46
+ alias opencode="opencode-mux"
47
+ ```
48
+
49
+ (binaries are `opencode-mux`, version `0.1.0`)
50
+
51
+ ## Usage
52
+
53
+ ```sh
54
+ opencode-mux [mux options] [opencode arguments...]
55
+ ```
56
+
57
+ ### Examples
58
+
59
+ ```sh
60
+ # Just work in the current directory
61
+ opencode-mux
62
+
63
+ # Continue an existing session
64
+ opencode-mux -s ses_abc123
65
+
66
+ # Pass opencode's own flags through, with a tiled pane layout
67
+ opencode-mux --layout tiled --model fast
68
+
69
+ # Running inside an existing tmux session also works — subagent panes are
70
+ # opened in the session you're already in, and already-running subagents are
71
+ # adopted without a startup flash.
72
+ ```
73
+
74
+ ### Mux options
75
+
76
+ | Option | Description | Default |
77
+ | ------ | ----------- | ------- |
78
+ | `--config <path>` | Config file to use | `~/.config/opencode-mux.conf` |
79
+ | `--layout <name>` | Pane layout: `main-vertical`, `main-horizontal`, `tiled`, `even-horizontal`, `even-vertical` | `main-vertical` |
80
+ | `--main-pane-size <pct>` | Main pane size for `main-*` layouts, percent of the window | `60` |
81
+ | `--close <mode>` | Pane lifecycle: `auto` (close when the subagent finishes) or `keep` | `auto` |
82
+ | `--grace <seconds>` | Delay before a finished subagent's pane closes | `1` |
83
+ | `--parent <id>` | Only spawn panes for children of this session | inferred from `-s`/`--session` |
84
+ | `--session-name <name>` | Owned tmux session name (when starting outside tmux) | `mux` |
85
+ | `-h`, `--help` | Show help | |
86
+ | `--version` | Print the version | |
87
+
88
+ Everything else (e.g. `--model`, `--agent`, `-i` prompts, a directory) is passed
89
+ through to opencode unchanged.
90
+
91
+ ## Config file
92
+
93
+ `~/.config/opencode-mux.conf` (honors `XDG_CONFIG_HOME`) is optional and is
94
+ auto-created with defaults on first run. Keys are `key=value`, lines starting
95
+ with `#` are comments, and unknown keys are ignored for forward compatibility.
96
+
97
+ ```ini
98
+ # opencode-mux configuration
99
+ session_name=mux
100
+ layout=main-vertical
101
+ main_pane_size=60
102
+ close_panes=auto
103
+ grace=1
104
+ parent=
105
+ ```
106
+
107
+ Precedence: **CLI flags > config file > defaults**.
108
+
109
+ ## How it works
110
+
111
+ - **Outside tmux**: opencode-mux creates a detached tmux session named
112
+ `mux` (or `--session-name`), applies session-scoped `status off` and
113
+ `mouse on`, re-executes itself inside, and attaches. When the main TUI
114
+ exits, the owned session is cleaned up.
115
+ - **Inside tmux**: the wrapper runs as a watcher. It connects to the opencode
116
+ server (same background service you're already using), listens for
117
+ subagent-session events, opens a pane per child session via
118
+ `opencode mini -s <id>`, styles the pane borders to match the active
119
+ opencode theme, and closes panes once their session finishes (after the
120
+ grace period).
121
+
122
+ ## Features
123
+
124
+ ### Subagent panes without the chrome
125
+
126
+ Subagent panes attach with `opencode mini`, the minimal frontend: no top tab
127
+ strip, no agent switcher or model panel — just the conversation and a one-line
128
+ footer. It streams live updates while the subagent works, accepts input, and
129
+ stays attached until the watcher closes the pane.
130
+
131
+ ### Theme-matched borders
132
+
133
+ Pane borders follow your active opencode theme (`border.default`, falling back
134
+ to the border color). Paraphrased, it looks good no matter what anyone has
135
+ configured their setup to look like:
136
+
137
+ - Theme is resolved from your opencode config: project
138
+ `.opencode/cli.json` → project `.opencode/tui.json` → global `cli.json` →
139
+ global `tui.json` (first match wins; v2 object and legacy string forms both
140
+ supported).
141
+ - Built-in themes come from a vendored table of all opencode v2.0.5 built-in
142
+ themes; custom theme files are resolved from your config/theme dirs.
143
+ - A `system` theme or an unknown/unresolvable theme results in **no border
144
+ styling** — never wrong colors.
145
+ - Config changes are re-applied live (checked on the watcher tick), and
146
+ borders are cleared when the theme becomes unresolvable. Zero tmux calls
147
+ while nothing changed.
148
+
149
+ ### Never touches your setup
150
+
151
+ All tmux writes are runtime-scoped: window options are applied with `-w`
152
+ (pane-border colors) and session options with `-t <session>` (status/mouse).
153
+ No `-g`, no writes to `~/.tmux.conf`, no changes to your opencode configs.
154
+
155
+ ## Requirements & compatibility
156
+
157
+ - Handles opencode's two v2 server generations transparently: 2.0.0–2.0.5
158
+ (`/api/status`) and 2.0.6+ (`/api/info`).
159
+ - Depends on tmux; fails with a clear error if tmux or opencode v2 is missing.
160
+
161
+ ## Development
162
+
163
+ ```sh
164
+ npm install
165
+ npm run check # typecheck (tsc --noEmit)
166
+ npm test # vitest suite
167
+ npm run build # tsc -p tsconfig.build.json → dist/
168
+ npm run smoke # e2e smoke (needs a staging environment; blocked locally)
169
+ ```
170
+
171
+ ## License
172
+
173
+ MIT
package/dist/args.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ import type { CliFlags } from "./config.js";
2
+ export interface ParsedArgs {
3
+ flags: CliFlags;
4
+ configPath?: string;
5
+ help: boolean;
6
+ version: boolean;
7
+ watch: boolean;
8
+ passThrough: string[];
9
+ }
10
+ export declare function parseArgs(argv: string[]): ParsedArgs;
11
+ /** Returns the parent session id from pass-through `-s`/`--session` opencode args, else null. */
12
+ export declare function parentFromPassThrough(passThrough: string[]): string | null;
package/dist/args.js ADDED
@@ -0,0 +1,114 @@
1
+ const LAYOUTS = new Set(["main-vertical", "main-horizontal", "tiled", "even-horizontal", "even-vertical"]);
2
+ const CLOSE_MODES = new Set(["auto", "keep"]);
3
+ export function parseArgs(argv) {
4
+ const out = { flags: {}, help: false, version: false, watch: false, passThrough: [] };
5
+ for (let i = 0; i < argv.length; i++) {
6
+ const arg = argv[i];
7
+ if (arg === "--mux-watch") {
8
+ out.watch = true;
9
+ continue;
10
+ }
11
+ if (arg === "-h" || arg === "--help") {
12
+ out.help = true;
13
+ continue;
14
+ }
15
+ if (arg === "--version") {
16
+ out.version = true;
17
+ continue;
18
+ }
19
+ const eq = arg.indexOf("=");
20
+ const name = eq >= 0 ? arg.slice(0, eq) : arg;
21
+ const inline = eq >= 0 ? arg.slice(eq + 1) : null;
22
+ const value = () => {
23
+ if (inline !== null)
24
+ return inline;
25
+ const next = argv[i + 1];
26
+ if (next !== undefined && !next.startsWith("-")) {
27
+ i++;
28
+ return next;
29
+ }
30
+ return null;
31
+ };
32
+ switch (name) {
33
+ case "--config": {
34
+ const v = value();
35
+ if (v === null)
36
+ throw new Error("opencode-mux: --config requires a path");
37
+ out.configPath = v;
38
+ break;
39
+ }
40
+ case "--layout": {
41
+ const v = value();
42
+ if (v === null || !LAYOUTS.has(v)) {
43
+ throw new Error("opencode-mux: --layout requires main-vertical | main-horizontal | tiled | even-horizontal | even-vertical");
44
+ }
45
+ out.flags.layout = v;
46
+ break;
47
+ }
48
+ case "--main-pane-size": {
49
+ const v = value();
50
+ const n = v === null ? Number.NaN : Number(v);
51
+ if (!Number.isFinite(n) || n < 20 || n > 80) {
52
+ throw new Error("opencode-mux: --main-pane-size requires a number between 20 and 80");
53
+ }
54
+ out.flags.mainPaneSize = Math.floor(n);
55
+ break;
56
+ }
57
+ case "--close": {
58
+ const v = value();
59
+ if (v === null || !CLOSE_MODES.has(v)) {
60
+ throw new Error("opencode-mux: --close requires auto | keep");
61
+ }
62
+ out.flags.closePanes = v;
63
+ break;
64
+ }
65
+ case "--grace": {
66
+ const v = value();
67
+ const n = v === null ? Number.NaN : Number(v);
68
+ if (!Number.isFinite(n) || n < 0) {
69
+ throw new Error("opencode-mux: --grace requires a non-negative number of seconds");
70
+ }
71
+ out.flags.graceSeconds = Math.floor(n);
72
+ break;
73
+ }
74
+ case "--parent": {
75
+ const v = value();
76
+ if (v === null)
77
+ throw new Error("opencode-mux: --parent requires a session id");
78
+ out.flags.parent = v === "" ? null : v;
79
+ break;
80
+ }
81
+ case "--session-name": {
82
+ const v = value();
83
+ if (v === null || v === "")
84
+ throw new Error("opencode-mux: --session-name requires a name");
85
+ out.flags.sessionName = v;
86
+ break;
87
+ }
88
+ default:
89
+ if (name.startsWith("--mux-")) {
90
+ throw new Error(`opencode-mux: unknown flag ${name}`);
91
+ }
92
+ out.passThrough.push(arg);
93
+ }
94
+ }
95
+ return out;
96
+ }
97
+ /** Returns the parent session id from pass-through `-s`/`--session` opencode args, else null. */
98
+ export function parentFromPassThrough(passThrough) {
99
+ for (let i = 0; i < passThrough.length; i++) {
100
+ const arg = passThrough[i];
101
+ if (arg === "-s" || arg === "--session") {
102
+ const next = passThrough[i + 1];
103
+ if (next !== undefined && next.startsWith("ses_"))
104
+ return next;
105
+ }
106
+ if (arg.startsWith("--session=")) {
107
+ const id = arg.slice("--session=".length);
108
+ if (id.startsWith("ses_"))
109
+ return id;
110
+ }
111
+ }
112
+ return null;
113
+ }
114
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
package/dist/bin.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/bin.js ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ import { fileURLToPath } from "node:url";
3
+ import { main, nodeSpawn } from "./cli.js";
4
+ try {
5
+ process.exitCode = await main(process.argv.slice(2), {
6
+ env: process.env,
7
+ spawn: nodeSpawn,
8
+ entryPath: fileURLToPath(new URL("./bin.js", import.meta.url)),
9
+ });
10
+ }
11
+ catch (error) {
12
+ console.error(String(error));
13
+ process.exitCode = 1;
14
+ }
15
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE3C,IAAI,CAAC;IACH,OAAO,CAAC,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;QACnD,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,KAAK,EAAE,SAAS;QAChB,SAAS,EAAE,aAAa,CAAC,IAAI,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;KAC/D,CAAC,CAAC;AACL,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACf,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7B,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,62 @@
1
+ import type { CliFlags } from "./config.js";
2
+ import type { ConnectServerDeps, ServerConnection, ServiceGeneration } from "./server.js";
3
+ export interface SpawnResult {
4
+ ok: boolean;
5
+ code: number | null;
6
+ error: string | null;
7
+ stdout: string;
8
+ stderr: string;
9
+ }
10
+ export interface SpawnOpts {
11
+ stdoutFile?: string;
12
+ stderrFile?: string;
13
+ inherit?: boolean;
14
+ }
15
+ export interface SpawnHandle {
16
+ pid: number | null;
17
+ done: Promise<SpawnResult>;
18
+ kill(): void;
19
+ }
20
+ export type SpawnFn = (command: string, args: string[], opts?: SpawnOpts) => SpawnHandle;
21
+ /** Real subprocess launcher with optional file redirects (never throws). */
22
+ export declare function nodeSpawn(command: string, args: string[], opts?: SpawnOpts): SpawnHandle;
23
+ export interface CliDeps {
24
+ env: NodeJS.ProcessEnv;
25
+ spawn: SpawnFn;
26
+ entryPath: string;
27
+ opencodeCommand?: string;
28
+ nodeCommand?: string;
29
+ tmuxCommand?: string;
30
+ cwd?: string;
31
+ /** Injectable so the watcher path is unit-testable; defaults to the real server connection. */
32
+ connectServer?: (deps?: ConnectServerDeps) => Promise<ServerConnection>;
33
+ }
34
+ export declare function usageText(): string;
35
+ export declare function watchLogPath(env: NodeJS.ProcessEnv): string;
36
+ /** Parses "2.0.5" or "opencode v2.0.5" into the major version; junk → null. */
37
+ export declare function parseMajorVersion(version: string): number | null;
38
+ /**
39
+ * Maps an opencode version to the client protocol generation it speaks: 2.0.0–2.0.5
40
+ * serve GET /api/status (legacy), 2.0.6+ serve GET /api/info (new). Each client
41
+ * generation's Service.ensure() probes only its own endpoint and stops a mismatched
42
+ * registered server, so the binary's generation must select the matching client.
43
+ * Returns null for unsupported or unparseable versions.
44
+ */
45
+ export declare function serviceGeneration(version: string): ServiceGeneration | null;
46
+ /** Re-emits resolved CLI flags as mux argv (for the watcher child process). */
47
+ export declare function flagsToArgv(flags: CliFlags): string[];
48
+ /** argv for the spawned watcher process: marker + config path + explicit flags. */
49
+ export declare function buildWatcherArgv(opts: {
50
+ configPath?: string;
51
+ flags: CliFlags;
52
+ }): string[];
53
+ /** argv the re-exec'd instance runs inside the fresh tmux session. */
54
+ export declare function buildReexecArgv(entryPath: string, originalArgv: string[]): string[];
55
+ /** tmux new-session argv that boots the detached session and its command. */
56
+ export declare function buildNewSessionCommand(input: {
57
+ entryPath: string;
58
+ originalArgv: string[];
59
+ sessionName: string;
60
+ cwd: string;
61
+ }): string[];
62
+ export declare function main(argv: string[], deps: CliDeps): Promise<number>;