@brutalsystems/muster 0.6.0 → 0.7.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.
@@ -38,6 +38,75 @@ with the keys `slugify`, `suffix`, `assign`, `resolve` in that order is:
38
38
  `a92936ef4b713641d29da3fbafccab0e6da79a54e27bfea3c6a779e801a925b3`.
39
39
  Both revisions produce this hash.
40
40
 
41
+ ## Privacy remediation edit — 2026-09-20
42
+
43
+ `test/fixtures/canonical-id.json` originally copied a real internal project
44
+ codename as an example `rawName`/`display`/`canonicalId` value. Tin Can made
45
+ the same change to its own copy and asked Muster to match; both projects
46
+ renamed the same two example strings to a neutral placeholder
47
+ (`billing-api`).
48
+
49
+ This is a **local text edit to the frozen 0.1.0 copy**, not a re-sync with
50
+ Tin Can's current upstream fixture. By the time of this edit, Tin Can's
51
+ fixture had moved to `tincan_version: 0.5.2` and gained real new content —
52
+ `opencode` suffix rules, new fixture cases, and new `CANONICAL_ID.md`
53
+ sections describing opencode session-id behavior. Muster's OpenCode
54
+ integration explicitly defers Tin Can/address-format work (see
55
+ `RELEASE_NOTES.md`), so adopting that upstream fixture wholesale would claim
56
+ verification Muster does not have. Per this document's own earlier
57
+ guidance — "their full-file hashes ... must not be compared to an entire
58
+ newer upstream fixture to infer a format change" — only the two leaked
59
+ strings were changed; every other byte of the 0.1.0-vintage fixture is
60
+ unchanged, including its 31 cases and `tincan_version: 0.1.0`.
61
+
62
+ New hash after this edit:
63
+
64
+ | File | SHA-256 |
65
+ | --- | --- |
66
+ | `test/fixtures/canonical-id.json` | `974cef9e06370a562cef588f1cd6d5c1d83b49afdb64808487759d6280bffff2` |
67
+
68
+ `CANONICAL_ID.md`'s hash is unchanged (`8cf4312d6af1c4d85ec0641d8739bec261be2feb35dd16bdd9d8be2b89551915`)
69
+ — the codename never appeared there.
70
+
71
+ Adopting Tin Can's opencode naming rules, if Muster's OpenCode/Tin Can
72
+ integration is ever un-deferred, is a separate, deliberate compatibility
73
+ check — not something this edit performs.
74
+
75
+ The `at commit fbaaea5842fd4a5c86849d7f51c8e16b8683b058` provenance line
76
+ above may no longer resolve in Tin Can's current history: Tin Can applied
77
+ the same codename remediation via a history rewrite (`git filter-repo
78
+ --replace-text`, force-pushed), which changes the hash of every commit
79
+ whose content or ancestry it touched. This is a historical acquisition
80
+ record, not a claim that the commit is still reachable.
81
+
82
+ ## Contract baseline moved to 0.5.2 — 2026-09-20
83
+
84
+ `@brutalsystems/tincan@0.2.0`, this document's verified baseline, was
85
+ withdrawn from the npm registry as part of Tin Can's own codename
86
+ remediation (every version through 0.5.1 carried the same leaked string
87
+ 0.7.0's fixture did, in Tin Can's README and fixture). Withdrawn version
88
+ numbers cannot be republished. This is a **registry availability problem,
89
+ not a format change** — Tin Can diffed `src/naming.ts` between 0.2.0 and
90
+ 0.5.2 and found `slugify`, `suffixOf`, `assignNames`, and `resolvePeer`
91
+ byte-identical; the only source change is the `RuntimeName` union widening
92
+ to add `'opencode'`. `test/fixtures/canonical-id.json` gained new opencode
93
+ cases with every pre-existing expectation unchanged.
94
+
95
+ Muster's own contract suite was run against a real installed
96
+ `@brutalsystems/tincan@0.5.2` and passed all four cases before this
97
+ re-baseline was made.
98
+
99
+ `0.3.0` through `0.5.1` were never verified by Muster's contract suite (see
100
+ "Upgrade boundary" below) and have also been withdrawn from the registry.
101
+ The verified, currently-available set is exactly `{0.5.2}`.
102
+
103
+ One caveat for anyone reconstructing a baseline from Tin Can's git history
104
+ rather than a published artifact: Tin Can's git tags predating 2026-09-20
105
+ no longer match what npm ever served at that version number, because the
106
+ history-rewrite in the section above applies retroactively to every commit,
107
+ tagged ones included. Git content at an old tag is not evidence of what a
108
+ withdrawn npm artifact actually contained.
109
+
41
110
  ## Upgrade boundary: listing and state
42
111
 
43
112
  The upstream release notes report that 0.3.0 expands
package/README.md CHANGED
@@ -2,21 +2,24 @@
2
2
 
3
3
  [![license](https://img.shields.io/github/license/BrutalSystems/muster)](./LICENSE)
4
4
 
5
- Muster launches instructed Codex or Claude Code agents on this machine. A
5
+ Muster launches instructed Codex, Claude Code, or OpenCode agents on this machine. A
6
6
  `session` returns an address only after its runtime is reachable. A `task`
7
7
  runs once, captures output, and never advertises a peer address.
8
8
 
9
9
  **Live launch verification:** Codex and Claude both launched and answered their
10
- initial prompts without terminal interaction. Claude requires a directory
11
- already trusted by the operator. The automated suite uses fake runtimes,
12
- not real models.
10
+ initial prompts without terminal interaction. OpenCode 1.18.31 passed the
11
+ loopback API and local-provider protocol checks. Claude requires a directory
12
+ already trusted by the operator. The automated suite uses fake runtimes, not
13
+ real models.
13
14
 
14
15
  ## Build and run locally
15
16
 
16
- Requires Node 22.12+ (tested with 24.16), TypeScript 5, and Codex 0.155.1 / Claude
17
- Code 2.1.267. Terminal drivers use tmux or node-pty. Process discovery currently
18
- requires a POSIX host with `ps` and `lsof`; the terminal interface itself is
19
- platform-neutral. The macOS Terminal driver is an unavailable v1 stub.
17
+ Requires Node 22.12+ (tested with 24.16), TypeScript 5, Codex 0.155.1, Claude
18
+ Code 2.1.267, and OpenCode 1.18.31 or newer. Newer OpenCode releases must retain
19
+ the CLI and loopback HTTP API used by Muster. Terminal drivers use tmux or
20
+ node-pty. Process discovery currently requires a POSIX host with `ps` and
21
+ `lsof`; the terminal interface itself is platform-neutral. The macOS Terminal
22
+ driver is an unavailable v1 stub.
20
23
 
21
24
  Before launching a Claude session, open Claude normally in the target directory
22
25
  and complete its workspace-trust review yourself, then exit that setup session.
@@ -29,7 +32,10 @@ npm ci
29
32
  npm run build
30
33
  node dist/muster.js run codex --prompt 'Review the authentication flow'
31
34
  node dist/muster.js run claude --prompt 'Summarize the project' --host tmux
35
+ node dist/muster.js run opencode --prompt 'Review this project'
36
+ node dist/muster.js run opencode --prompt 'Build a plan' -- --model coldfire-local/qwen3-30b-a3b
32
37
  node dist/muster.js run codex --kind task --prompt 'Explain the test layout'
38
+ node dist/muster.js run opencode --kind task --prompt 'Summarize the tests'
33
39
  node dist/muster.js list
34
40
  node dist/muster.js list --format human
35
41
  node dist/muster.js list --kind task
@@ -64,11 +70,25 @@ red for failures or nonzero exit codes, and cyan for follow-up commands. IDs
64
70
  and paths retain the normal text color. Set `NO_COLOR=1` to disable colors;
65
71
  piped output and `TERM=dumb` are always uncolored. JSON never includes colors.
66
72
 
67
- `run` accepts an optional `--` followed by runtime arguments. The normal allowlist
68
- is `--model` / `-m`, plus Claude's `--effort`. Unknown options, bundled short
69
- options, config injection and raw permission overrides are refused, even with
70
- `allow_dangerous_flags` enabled. Use Muster's normalized permission flags. Prompt strings
71
- are passed as a single literal argument after the runtime's option terminator.
73
+ `run` accepts an optional `--` followed by runtime arguments. The normal
74
+ allowlist is `--model` / `-m`, plus Claude's `--effort` and OpenCode's `--agent`
75
+ and `--variant`. Unknown options, bundled short options, config injection and
76
+ raw permission overrides are refused, even with `allow_dangerous_flags`
77
+ enabled. Use Muster's normalized permission flags. Prompt strings are always
78
+ passed as one literal argument. Codex and Claude launches and OpenCode tasks put
79
+ that value after `--`; OpenCode sessions use the dedicated `--prompt` value.
80
+
81
+ OpenCode inherits the provider and model definitions from its normal user and
82
+ project configuration, so configured local OpenAI-compatible providers such as
83
+ `coldfire-local/qwen3-30b-a3b` remain available. Muster never rewrites those
84
+ files. Put runtime options after `--`, as in the local-model example above.
85
+
86
+ ```sh
87
+ muster run opencode --prompt 'Review this project'
88
+ muster run opencode --prompt 'Build a plan' -- --model coldfire-local/qwen3-30b-a3b
89
+ muster run opencode --kind task --prompt 'Summarize the tests'
90
+ muster run opencode --mcp tincan --permissions auto --sandbox workspace-write --prompt 'Coordinate'
91
+ ```
72
92
 
73
93
  ## Terminal lifetime
74
94
 
@@ -77,6 +97,7 @@ Add `--open` to display the launched session in a new Terminal.app window on mac
77
97
  ```sh
78
98
  muster run codex --open --prompt "Review this project" --format human
79
99
  muster run claude --open --terminal ghostty --prompt "Review this project" --format human
100
+ muster run opencode --open --terminal iterm2 --prompt "Review this project" --format human
80
101
  muster run codex --open --terminal iterm2 --prompt "Review this project" --format human
81
102
  ```
82
103
 
@@ -116,6 +137,16 @@ and `attach_hint`. Watchability is separate from the runtime's idle/busy state.
116
137
  or startup failure cleans the process tree and host window, logs failure, and
117
138
  returns an error instead of a peer record.
118
139
 
140
+ Each OpenCode session owns a separate HTTP server bound to `127.0.0.1` on a
141
+ Muster-selected port. Muster verifies that the listener belongs to the launched
142
+ process tree, stores its `server_url` and durable `session_id`, and never attaches
143
+ to an existing personal OpenCode server. `list` refreshes the session through
144
+ that endpoint; `stop` aborts it best-effort and still performs authoritative
145
+ process-tree cleanup. The URL is local control metadata, not a public address.
146
+ OpenCode sessions otherwise use the same tmux/pty, viewer, list, stop, and human
147
+ output behavior described above; OpenCode tasks use the same detached output
148
+ and exit-status lifecycle as other tasks.
149
+
119
150
  ## Configuration and permissions
120
151
 
121
152
  Muster reads `~/.muster/config.toml` once at startup and **never writes it**.
@@ -146,11 +177,11 @@ muster run claude --permissions auto --sandbox workspace-write \
146
177
  --prompt 'Implement the change' --format human
147
178
  ```
148
179
 
149
- | Muster permissions | Codex translation | Claude Code translation |
150
- |---|---|---|
151
- | `deny` (default) | Approval policy `never` | Permission mode `dontAsk` |
152
- | `auto` | `--approve-for-me` | Permission mode `auto` |
153
- | `bypass` | Bypass approvals and sandbox | Permission mode `bypassPermissions` |
180
+ | Muster permissions | Codex translation | Claude Code translation | OpenCode translation |
181
+ | ------------------ | ---------------------------- | ----------------------------------- | ------------------------------------------------------------------------------- |
182
+ | `deny` (default) | Approval policy `never` | Permission mode `dontAsk` | Deny write, shell, task, external-directory, web, and unselected MCP operations |
183
+ | `auto` | `--approve-for-me` | Permission mode `auto` | `--auto` with explicit deny rules |
184
+ | `bypass` | Bypass approvals and sandbox | Permission mode `bypassPermissions` | Allow supported operations |
154
185
 
155
186
  `deny` refuses actions that would require approval; it does not prohibit tools
156
187
  already allowed by the sandbox or permission rules. `auto` delegates permission
@@ -175,6 +206,12 @@ launch settings, not a continuous attestation of remote policy or user changes.
175
206
  See [Codex auto-review](https://learn.chatgpt.com/docs/sandboxing/auto-review) and
176
207
  [Claude permission modes](https://code.claude.com/docs/en/permission-modes).
177
208
 
209
+ OpenCode permissions are tool policy, not an OS-level filesystem sandbox.
210
+ Muster's read-only OpenCode policy denies editing, shell, subagent,
211
+ external-directory, web, and MCP operations except explicitly selected MCP
212
+ tools; it does not claim kernel-enforced isolation. Auto still requires
213
+ `workspace-write`, and bypass still requires authorized `full-access`.
214
+
178
215
  The concurrency cap is shared by separate CLI/MCP processes, including pending
179
216
  launches. Settings apply to both kinds; tasks are read-only by default.
180
217
 
@@ -194,10 +231,17 @@ arrive after startup; this v1 is not an enterprise policy-enforcement layer.
194
231
  Claude permissions and its command sandbox are different mechanisms; neither
195
232
  claim implies that every external tool is OS-sandboxed.
196
233
 
234
+ OpenCode keeps normal provider/model configuration but launches with `--pure`,
235
+ which disables inherited external plugins. Before launch, Muster inspects the
236
+ effective OpenCode configuration, disables every inherited MCP server and tool
237
+ family, enables only the selected Muster definitions, and fails closed if a
238
+ higher-precedence managed policy prevents that isolation from being proven.
239
+
197
240
  These child restrictions prevent automatically propagating spawn authority.
198
241
  External tools are enabled only through the operator configuration and launch
199
- selection described below. The child's runtime inbox remains reachable from an
200
- external Tin Can even when it has no MCP tools of its own.
242
+ selection described below. Codex and Claude runtime inboxes remain reachable
243
+ from an external Tin Can even when they have no MCP tools of their own. OpenCode
244
+ delivery remains deferred as documented under Tin Can compatibility.
201
245
  No user-level runtime configuration is rewritten, and workspace-trust dialogs
202
246
  are never accepted automatically.
203
247
 
@@ -224,11 +268,13 @@ tools = ["peers", "send_peer", "message_log"]
224
268
  required = false
225
269
  ```
226
270
 
227
- Then either runtime can use it:
271
+ Then any runtime can use it:
228
272
 
229
273
  ```sh
230
274
  muster run codex --mcp tincan --prompt 'Coordinate with the other agent'
231
275
  muster run claude --mcp tincan --prompt 'Coordinate with the other agent'
276
+ muster run opencode --mcp tincan --permissions auto --sandbox workspace-write \
277
+ --prompt 'Coordinate'
232
278
  muster run codex --no-mcp --prompt 'Work without external tools'
233
279
  ```
234
280
 
@@ -261,6 +307,9 @@ Codex receives isolated definitions, using a fresh runtime name when an inherite
261
307
  server has the same name, and its native tool allowlist. Claude receives a private
262
308
  Muster stdio connection that filters both tool discovery and calls; newly added
263
309
  upstream tools cannot bypass the selection. Only selected tools are preapproved.
310
+ OpenCode disables inherited definitions and tool families in its verified
311
+ per-launch overlay, then enables only selected definitions. It also uses
312
+ `--pure`, so inherited plugins do not enter the child process.
264
313
  Claude connection specifications are stored with owner-only permissions under
265
314
  `~/.muster/mcp`, removed on stop, task completion, or when `list` observes an ended
266
315
  session. MCP server access is separate from filesystem sandbox permissions: a
@@ -306,6 +355,11 @@ Codex `idle` means reachable and not known to be busy, not guaranteed free.
306
355
  The querying app-server can report `notLoaded` for a live thread; Muster maps
307
356
  that to `idle`. Claude state comes from its session registry.
308
357
 
358
+ OpenCode peers expose a durable `session_id` and loopback-only `server_url` so a
359
+ future Tin Can adapter has a stable integration seam. Tin Can does not yet send
360
+ to OpenCode through Muster: choosing native OpenCode messaging versus A2A, and
361
+ updating Tin Can for that transport, are explicitly deferred follow-up work.
362
+
309
363
  ## Verification
310
364
 
311
365
  ```sh
@@ -325,7 +379,7 @@ For reproducible verification, install that exact release outside Muster:
325
379
 
326
380
  ```sh
327
381
  contract_dir=$(mktemp -d)
328
- npm install --prefix "$contract_dir" --no-save @brutalsystems/tincan@0.2.0
382
+ npm install --prefix "$contract_dir" --no-save @brutalsystems/tincan@0.5.2
329
383
  MUSTER_TINCAN_BIN="$contract_dir/node_modules/.bin/tincan" npm run test:contract
330
384
  ```
331
385
 
package/dist/format.js CHANGED
@@ -35,6 +35,8 @@ function recordHuman(record, color) {
35
35
  ];
36
36
  if (record.canonical_id)
37
37
  lines.push(field("Address", clean(record.canonical_id)));
38
+ if (record.runtime === "opencode" && record.server_url)
39
+ lines.push(field("Control URL", clean(record.server_url)));
38
40
  lines.push(field("Directory", clean(record.cwd)));
39
41
  if (record.permissions)
40
42
  lines.push(field("Permissions", clean(record.permissions)));
package/dist/guard.js CHANGED
@@ -3,7 +3,7 @@ import { resolve } from "node:path";
3
3
  import { mcpName, claudeMcpConfig } from "./mcp.js";
4
4
  export const runSchema = z
5
5
  .object({
6
- runtime: z.enum(["codex", "claude"]),
6
+ runtime: z.enum(["codex", "claude", "opencode"]),
7
7
  prompt: z
8
8
  .string()
9
9
  .refine((s) => s.trim().length > 0, "prompt must not be blank"),
@@ -37,12 +37,16 @@ export function resolvePermissions(req, config) {
37
37
  throw new Error("--permissions bypass requires --sandbox full-access");
38
38
  return { permissions, sandbox };
39
39
  }
40
- function extras(req) {
40
+ export function runtimeArgs(req) {
41
41
  const result = [];
42
+ const allowed = req.runtime === "claude"
43
+ ? ["--model", "-m", "--effort"]
44
+ : req.runtime === "opencode"
45
+ ? ["--model", "-m", "--agent", "--variant"]
46
+ : ["--model", "-m"];
42
47
  for (let i = 0; i < req.args.length; i++) {
43
48
  const arg = req.args[i], flag = arg.split("=")[0].toLowerCase().replaceAll("_", "-");
44
- if (!["--model", "-m", "--effort"].includes(flag) ||
45
- (flag === "--effort" && req.runtime !== "claude"))
49
+ if (!allowed.includes(flag))
46
50
  throw new Error(`Runtime option refused: ${arg}`);
47
51
  if (arg.includes("="))
48
52
  result.push(arg);
@@ -56,7 +60,7 @@ function extras(req) {
56
60
  return result;
57
61
  }
58
62
  export function launchArgs(req, config, mcp = [], bridgePath) {
59
- const extra = extras(req);
63
+ const extra = runtimeArgs(req);
60
64
  const policy = resolvePermissions(req, config);
61
65
  if (req.runtime === "codex")
62
66
  return [
@@ -0,0 +1,86 @@
1
+ import { realpath } from "node:fs/promises";
2
+ import { OpenCodeHttp, OpenCodeProtocolError, } from "../opencode-http.js";
3
+ import { command, delay, descendants, processRef } from "./processes.js";
4
+ const POLL_INTERVAL_MS = 50;
5
+ function rawName(session) {
6
+ return session.title.trim() === "" ? null : session.title;
7
+ }
8
+ export async function openCodeEndpointOwnedBy(rootPid, serverUrl, deadline) {
9
+ try {
10
+ const endpoint = new URL(serverUrl);
11
+ const port = Number(endpoint.port);
12
+ if (!Number.isInteger(port) || port <= 0 || deadline <= Date.now())
13
+ return false;
14
+ const family = new Set(await descendants(rootPid, deadline));
15
+ const output = await command("lsof", [
16
+ "-nP",
17
+ "-a",
18
+ `-iTCP@${endpoint.hostname}:${port}`,
19
+ "-sTCP:LISTEN",
20
+ "-Fp",
21
+ ], Math.max(1, Math.min(1000, deadline - Date.now())));
22
+ const listeners = output
23
+ .split("\n")
24
+ .filter((line) => /^p\d+$/.test(line))
25
+ .map((line) => Number(line.slice(1)));
26
+ return listeners.length > 0 && listeners.every((pid) => family.has(pid));
27
+ }
28
+ catch {
29
+ return false;
30
+ }
31
+ }
32
+ export async function resolveOpenCode(rootPid, serverUrl, cwd, createdAfter, deadline) {
33
+ const resolvedCwd = await realpath(cwd);
34
+ const client = new OpenCodeHttp(serverUrl);
35
+ let ownershipProven = false;
36
+ while (Date.now() < deadline) {
37
+ if (!(await processRef(rootPid)))
38
+ return;
39
+ if (!ownershipProven &&
40
+ !(await openCodeEndpointOwnedBy(rootPid, serverUrl, deadline))) {
41
+ const remaining = deadline - Date.now();
42
+ if (remaining > 0)
43
+ await delay(Math.min(POLL_INTERVAL_MS, remaining));
44
+ continue;
45
+ }
46
+ ownershipProven = true;
47
+ try {
48
+ const health = await client.health(deadline);
49
+ if (health.healthy) {
50
+ const sessions = await client.sessions(deadline);
51
+ if (!(await processRef(rootPid)))
52
+ return;
53
+ const candidates = sessions.filter((session) => session.directory === resolvedCwd &&
54
+ session.time.created >= createdAfter);
55
+ const statuses = candidates.length
56
+ ? await client.statuses(deadline)
57
+ : {};
58
+ if (!(await processRef(rootPid)))
59
+ return;
60
+ if (!(await openCodeEndpointOwnedBy(rootPid, serverUrl, deadline))) {
61
+ ownershipProven = false;
62
+ continue;
63
+ }
64
+ const matches = candidates.filter((session) => Object.prototype.hasOwnProperty.call(statuses, session.id));
65
+ if (matches.length > 1)
66
+ throw new Error("Multiple OpenCode sessions matched the launch endpoint, cwd, and creation window");
67
+ const match = matches[0];
68
+ if (match)
69
+ return {
70
+ id: match.id,
71
+ pid: rootPid,
72
+ rawName: rawName(match),
73
+ cwd: resolvedCwd,
74
+ serverUrl,
75
+ };
76
+ }
77
+ }
78
+ catch (error) {
79
+ if (!(error instanceof OpenCodeProtocolError))
80
+ throw error;
81
+ }
82
+ const remaining = deadline - Date.now();
83
+ if (remaining > 0)
84
+ await delay(Math.min(POLL_INTERVAL_MS, remaining));
85
+ }
86
+ }
package/dist/muster.js CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire } from "node:module";
2
3
  import { parseArgs } from "node:util";
3
4
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
4
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -12,12 +13,14 @@ const listSchema = z
12
13
  .object({ kind: z.enum(["session", "task"]).optional() })
13
14
  .strict();
14
15
  const idSchema = z.object({ id: z.string().min(1) }).strict();
16
+ const { version } = createRequire(import.meta.url)("../package.json");
15
17
  const help = `Usage:
16
- muster run <codex|claude> --prompt TEXT [--cwd DIR] [--kind session|task] [--host ID] [--open] [--terminal APP] [-- RUNTIME_OPTIONS]
18
+ muster run <codex|claude|opencode> --prompt TEXT [--cwd DIR] [--kind session|task] [--host ID] [--open] [--terminal APP] [-- RUNTIME_OPTIONS]
17
19
  muster list [--kind session|task] [--format json|human]
18
20
  muster stop <id> [--format json|human]
19
21
  muster output <id>
20
22
  muster mcp
23
+ muster --version
21
24
 
22
25
  run also accepts --format json|human, --permissions auto|deny|bypass,
23
26
  and --sandbox read-only|workspace-write|full-access before -- RUNTIME_OPTIONS.
@@ -40,6 +43,10 @@ async function main() {
40
43
  process.stdout.write(help);
41
44
  return;
42
45
  }
46
+ if (argv[0] === "--version" || argv[0] === "-v") {
47
+ process.stdout.write(`${version}\n`);
48
+ return;
49
+ }
43
50
  const mcp = argv.length === 0 || argv[0] === "mcp";
44
51
  if (mcp && argv.length > 1)
45
52
  throw new Error("mcp takes no arguments");
@@ -56,7 +63,7 @@ async function main() {
56
63
  });
57
64
  });
58
65
  if (mcp) {
59
- const server = new Server({ name: "muster", version: "0.6.0" }, { capabilities: { tools: {} } });
66
+ const server = new Server({ name: "muster", version }, { capabilities: { tools: {} } });
60
67
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
61
68
  tools: [
62
69
  {
@@ -65,7 +72,10 @@ async function main() {
65
72
  inputSchema: {
66
73
  type: "object",
67
74
  properties: {
68
- runtime: { type: "string", enum: ["codex", "claude"] },
75
+ runtime: {
76
+ type: "string",
77
+ enum: ["codex", "claude", "opencode"],
78
+ },
69
79
  prompt: { type: "string", minLength: 1 },
70
80
  cwd: { type: "string" },
71
81
  kind: {
package/dist/naming.js CHANGED
@@ -1,5 +1,6 @@
1
- /** Vendored from Tin Can src/naming.ts at fbaaea5842fd4a5c86849d7f51c8e16b8683b058 (MIT). */
2
- /** Peer naming per the handoff §7: slug for humans, canonical id for the log. */
1
+ export function runtimeName(runtime) {
2
+ return runtime === "claude" ? "claude-code" : runtime;
3
+ }
3
4
  export function slugify(raw) {
4
5
  return raw
5
6
  .toLowerCase()