From 6d523443832e793a2cdd5cdbe1bdde5855ce5dc7 Mon Sep 17 00:00:00 2001 From: Alex Southwell <4596216+saphid@users.noreply.github.com> Date: Tue, 3 Nov 2026 11:23:13 +1100 Subject: [PATCH] fix(opencode): nested custom commands like /git/commit run as commands Co-Authored-By: Claude Opus 5.5 --- .../orchestration-v2/Adapters/OpenCode2AdapterV2.test.ts | 6 +++--- packages/provider-opencode/src/server/adapter.test.ts | 6 +++--- packages/provider-opencode/src/server/adapter.ts | 2 +- packages/provider-opencode/src/server/v2/adapter.ts | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.test.ts index bb52bb92daea..7f4146000575 100644 --- a/apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.test.ts @@ -2240,7 +2240,7 @@ it.layer(McpProviderSessions.layer)("OpenCode2 adapter", (it) => { // session.command returns 204 without an inbox id; the stream drops before session.inbox.enqueued provides it. const { runtime, thread } = yield* resumed([ out("command.list", ""), - reply("command.list", { location: { directory: WORK }, data: [{ name: "hello" }] }), + reply("command.list", { location: { directory: WORK }, data: [{ name: "git/hello" }] }), out("message.list", { sessionID: SESSION, order: "desc", limit: "1" }), reply("message.list", { data: [ @@ -2248,7 +2248,7 @@ it.layer(McpProviderSessions.layer)("OpenCode2 adapter", (it) => { ], cursor: {}, }), - out("session.command", { sessionID: SESSION, name: "hello", text: "WORLD" }), + out("session.command", { sessionID: SESSION, name: "git/hello", text: "WORLD" }), reply("session.command", null), { type: "runtime_exit", status: "success" } as const, out("event.subscribe"), @@ -2285,7 +2285,7 @@ it.layer(McpProviderSessions.layer)("OpenCode2 adapter", (it) => { ); yield* runtime.startTurn({ ...turnInput(thread), - message: { ...turnInput(thread).message, text: "/hello WORLD" }, + message: { ...turnInput(thread).message, text: "/git/hello WORLD" }, }); const collected = yield* Fiber.join(events); const texts = collected.flatMap((event) => diff --git a/packages/provider-opencode/src/server/adapter.test.ts b/packages/provider-opencode/src/server/adapter.test.ts index 2ef7f2c5bbd0..bc54b91ca888 100644 --- a/packages/provider-opencode/src/server/adapter.test.ts +++ b/packages/provider-opencode/src/server/adapter.test.ts @@ -1363,7 +1363,7 @@ describe("OpenCodeAdapterV2", () => { return { stream: nativeEvents.stream }; }, }, - command: { list: async () => ({ data: [{ name: "review" }] }) }, + command: { list: async () => ({ data: [{ name: "git/review" }] }) }, session: { create: async () => ({ data: { id: sessionId, time: { created: 1, updated: 1 } } }), command: async (input: { @@ -1382,9 +1382,9 @@ describe("OpenCodeAdapterV2", () => { }, }, }); - const start = yield* harness.startTurn("/review staged changes").pipe(Effect.forkScoped); + const start = yield* harness.startTurn("/git/review staged changes").pipe(Effect.forkScoped); const call = yield* Queue.take(calls); - assert.equal(call.command, "review"); + assert.equal(call.command, "git/review"); assert.equal(call.arguments, "staged changes"); assert.equal(call.model, "anthropic/claude-sonnet"); yield* Effect.promise(() => diff --git a/packages/provider-opencode/src/server/adapter.ts b/packages/provider-opencode/src/server/adapter.ts index 271259631ea4..d818aa433c2d 100644 --- a/packages/provider-opencode/src/server/adapter.ts +++ b/packages/provider-opencode/src/server/adapter.ts @@ -2977,7 +2977,7 @@ export const makeOpenCodeAdapterV2 = Effect.fn("makeOpenCodeAdapterV2")(function .map((part) => part.text) .join("\n") .trim(); - const match = text?.match(/^\/([^\s/]+)(?:\s+([\s\S]*))?$/); + const match = text?.match(/^\/(\S+)(?:\s+([\s\S]*))?$/); const command = match ? (yield* OpenCodeRuntime.loadOpenCodeCommands(client).pipe( Effect.timeout("10 seconds"), diff --git a/packages/provider-opencode/src/server/v2/adapter.ts b/packages/provider-opencode/src/server/v2/adapter.ts index ae74eb2908e8..eb59388e22cb 100644 --- a/packages/provider-opencode/src/server/v2/adapter.ts +++ b/packages/provider-opencode/src/server/v2/adapter.ts @@ -626,7 +626,7 @@ const agentFor = (input: ProviderAdapter.ProviderAdapterV2TurnInput) => input.runtimePolicy.interactionMode === "plan" ? "plan" : "build"; const commandOf = (text: string) => { - const match = /^\/([^\s/]+)(?:\s+([\s\S]*))?$/.exec(text.trim()); + const match = /^\/(\S+)(?:\s+([\s\S]*))?$/.exec(text.trim()); return match === null ? undefined : { name: match[1]!, text: match[2] ?? "" }; };