From 6ad6358eb152db4a8a5d1ab27f2ce3da098a1b6f Mon Sep 17 00:00:00 2001 From: LukeParkerDev <10430890+Hona@users.noreply.github.com> Date: Fri, 3 Apr 2026 14:01:13 +1000 Subject: [PATCH] fix: render pwsh and powershell tools correctly in UI This fixes regressions from splitting the shell tools where powershell commands were missing their native exit codes and their correct UI rendering. --- packages/opencode/.opencode/package-lock.json | 31 +++++++++++++++++++ packages/opencode/src/tool/shell/runner.ts | 16 +++++++--- packages/ui/src/components/message-part.tsx | 6 +++- packages/web/src/components/share/part.tsx | 5 +-- 4 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 packages/opencode/.opencode/package-lock.json diff --git a/packages/opencode/.opencode/package-lock.json b/packages/opencode/.opencode/package-lock.json new file mode 100644 index 0000000000..c36f597373 --- /dev/null +++ b/packages/opencode/.opencode/package-lock.json @@ -0,0 +1,31 @@ +{ + "name": ".opencode", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "@opencode-ai/plugin": "*" + } + }, + "node_modules/@opencode-ai/plugin": { + "version": "1.2.24", + "license": "MIT", + "dependencies": { + "@opencode-ai/sdk": "1.2.24", + "zod": "4.1.8" + } + }, + "node_modules/@opencode-ai/sdk": { + "version": "1.2.24", + "license": "MIT" + }, + "node_modules/zod": { + "version": "4.1.8", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/packages/opencode/src/tool/shell/runner.ts b/packages/opencode/src/tool/shell/runner.ts index 9bdfbb66f9..0c4f7ee05d 100644 --- a/packages/opencode/src/tool/shell/runner.ts +++ b/packages/opencode/src/tool/shell/runner.ts @@ -11,6 +11,11 @@ export function preview(text: string) { } export namespace ShellRunner { + function wrap(name: string, command: string) { + if (name !== "powershell" && name !== "pwsh") return command + return `${command}; if ($null -ne $LASTEXITCODE) { exit $LASTEXITCODE }; if ($?) { exit 0 }; exit 1` + } + export async function shellEnv(ctx: Tool.Context, cwd: string) { const extra = await Plugin.trigger("shell.env", { cwd, sessionID: ctx.sessionID, callID: ctx.callID }, { env: {} }) return { @@ -21,7 +26,7 @@ export namespace ShellRunner { export function launch(shell: string, name: string, command: string, cwd: string, env: NodeJS.ProcessEnv) { if (process.platform === "win32" && (name === "powershell" || name === "pwsh")) { - return spawn(shell, ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", command], { + return spawn(shell, ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", wrap(name, command)], { cwd, env, stdio: ["ignore", "pipe", "pipe"], @@ -54,6 +59,7 @@ export namespace ShellRunner { ) { const proc = launch(input.shell, input.name, input.command, input.cwd, input.env) let output = "" + let code: number | null = null ctx.metadata({ metadata: { @@ -103,12 +109,14 @@ export namespace ShellRunner { ctx.abort.removeEventListener("abort", abort) } - proc.once("exit", () => { + proc.once("exit", (next) => { exited = true + code = next }) - proc.once("close", () => { + proc.once("close", (next) => { exited = true + code = next cleanup() resolve() }) @@ -131,7 +139,7 @@ export namespace ShellRunner { title: input.description, metadata: { output: preview(output), - exit: proc.exitCode, + exit: code, description: input.description, }, output, diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx index 03477e5a7f..a2e644d887 100644 --- a/packages/ui/src/components/message-part.tsx +++ b/packages/ui/src/components/message-part.tsx @@ -269,6 +269,8 @@ export type ToolInfo = { subtitle?: string } +const SHELL = new Set(["bash", "pwsh", "powershell"]) + function agentTitle(i18n: UiI18n, type?: string) { if (!type) return i18n.t("ui.tool.agent.default") return i18n.t("ui.tool.agent", { type }) @@ -331,6 +333,8 @@ export function getToolInfo(tool: string, input: any = {}): ToolInfo { } } case "bash": + case "pwsh": + case "powershell": return { icon: "console", title: i18n.t("ui.tool.shell"), @@ -518,7 +522,7 @@ function renderable(part: PartType, showReasoningSummaries = true) { } function toolDefaultOpen(tool: string, shell = false, edit = false) { - if (tool === "bash") return shell + if (SHELL.has(tool)) return shell if (tool === "edit" || tool === "write" || tool === "apply_patch") return edit } diff --git a/packages/web/src/components/share/part.tsx b/packages/web/src/components/share/part.tsx index c7d177df7d..3558fd9452 100644 --- a/packages/web/src/components/share/part.tsx +++ b/packages/web/src/components/share/part.tsx @@ -33,6 +33,7 @@ import type { Diagnostic } from "vscode-languageserver-types" import styles from "./part.module.css" const MIN_DURATION = 2000 +const SHELL = new Set(["bash", "pwsh", "powershell"]) export interface PartProps { index: number @@ -90,7 +91,7 @@ export function Part(props: PartProps) { - + @@ -240,7 +241,7 @@ export function Part(props: PartProps) { state={props.part.state} /> - +