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}
/>
-
+