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.
This commit is contained in:
parent
95577c75a3
commit
6ad6358eb1
4 changed files with 51 additions and 7 deletions
31
packages/opencode/.opencode/package-lock.json
generated
Normal file
31
packages/opencode/.opencode/package-lock.json
generated
Normal file
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,11 @@ export function preview(text: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace ShellRunner {
|
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) {
|
export async function shellEnv(ctx: Tool.Context, cwd: string) {
|
||||||
const extra = await Plugin.trigger("shell.env", { cwd, sessionID: ctx.sessionID, callID: ctx.callID }, { env: {} })
|
const extra = await Plugin.trigger("shell.env", { cwd, sessionID: ctx.sessionID, callID: ctx.callID }, { env: {} })
|
||||||
return {
|
return {
|
||||||
|
|
@ -21,7 +26,7 @@ export namespace ShellRunner {
|
||||||
|
|
||||||
export function launch(shell: string, name: string, command: string, cwd: string, env: NodeJS.ProcessEnv) {
|
export function launch(shell: string, name: string, command: string, cwd: string, env: NodeJS.ProcessEnv) {
|
||||||
if (process.platform === "win32" && (name === "powershell" || name === "pwsh")) {
|
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,
|
cwd,
|
||||||
env,
|
env,
|
||||||
stdio: ["ignore", "pipe", "pipe"],
|
stdio: ["ignore", "pipe", "pipe"],
|
||||||
|
|
@ -54,6 +59,7 @@ export namespace ShellRunner {
|
||||||
) {
|
) {
|
||||||
const proc = launch(input.shell, input.name, input.command, input.cwd, input.env)
|
const proc = launch(input.shell, input.name, input.command, input.cwd, input.env)
|
||||||
let output = ""
|
let output = ""
|
||||||
|
let code: number | null = null
|
||||||
|
|
||||||
ctx.metadata({
|
ctx.metadata({
|
||||||
metadata: {
|
metadata: {
|
||||||
|
|
@ -103,12 +109,14 @@ export namespace ShellRunner {
|
||||||
ctx.abort.removeEventListener("abort", abort)
|
ctx.abort.removeEventListener("abort", abort)
|
||||||
}
|
}
|
||||||
|
|
||||||
proc.once("exit", () => {
|
proc.once("exit", (next) => {
|
||||||
exited = true
|
exited = true
|
||||||
|
code = next
|
||||||
})
|
})
|
||||||
|
|
||||||
proc.once("close", () => {
|
proc.once("close", (next) => {
|
||||||
exited = true
|
exited = true
|
||||||
|
code = next
|
||||||
cleanup()
|
cleanup()
|
||||||
resolve()
|
resolve()
|
||||||
})
|
})
|
||||||
|
|
@ -131,7 +139,7 @@ export namespace ShellRunner {
|
||||||
title: input.description,
|
title: input.description,
|
||||||
metadata: {
|
metadata: {
|
||||||
output: preview(output),
|
output: preview(output),
|
||||||
exit: proc.exitCode,
|
exit: code,
|
||||||
description: input.description,
|
description: input.description,
|
||||||
},
|
},
|
||||||
output,
|
output,
|
||||||
|
|
|
||||||
|
|
@ -269,6 +269,8 @@ export type ToolInfo = {
|
||||||
subtitle?: string
|
subtitle?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SHELL = new Set(["bash", "pwsh", "powershell"])
|
||||||
|
|
||||||
function agentTitle(i18n: UiI18n, type?: string) {
|
function agentTitle(i18n: UiI18n, type?: string) {
|
||||||
if (!type) return i18n.t("ui.tool.agent.default")
|
if (!type) return i18n.t("ui.tool.agent.default")
|
||||||
return i18n.t("ui.tool.agent", { type })
|
return i18n.t("ui.tool.agent", { type })
|
||||||
|
|
@ -331,6 +333,8 @@ export function getToolInfo(tool: string, input: any = {}): ToolInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "bash":
|
case "bash":
|
||||||
|
case "pwsh":
|
||||||
|
case "powershell":
|
||||||
return {
|
return {
|
||||||
icon: "console",
|
icon: "console",
|
||||||
title: i18n.t("ui.tool.shell"),
|
title: i18n.t("ui.tool.shell"),
|
||||||
|
|
@ -518,7 +522,7 @@ function renderable(part: PartType, showReasoningSummaries = true) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function toolDefaultOpen(tool: string, shell = false, edit = false) {
|
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
|
if (tool === "edit" || tool === "write" || tool === "apply_patch") return edit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import type { Diagnostic } from "vscode-languageserver-types"
|
||||||
import styles from "./part.module.css"
|
import styles from "./part.module.css"
|
||||||
|
|
||||||
const MIN_DURATION = 2000
|
const MIN_DURATION = 2000
|
||||||
|
const SHELL = new Set(["bash", "pwsh", "powershell"])
|
||||||
|
|
||||||
export interface PartProps {
|
export interface PartProps {
|
||||||
index: number
|
index: number
|
||||||
|
|
@ -90,7 +91,7 @@ export function Part(props: PartProps) {
|
||||||
<Match when={props.part.type === "tool" && props.part.tool === "todowrite"}>
|
<Match when={props.part.type === "tool" && props.part.tool === "todowrite"}>
|
||||||
<IconQueueList width={18} height={18} />
|
<IconQueueList width={18} height={18} />
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={props.part.type === "tool" && props.part.tool === "bash"}>
|
<Match when={props.part.type === "tool" && SHELL.has(props.part.tool)}>
|
||||||
<IconCommandLine width={18} height={18} />
|
<IconCommandLine width={18} height={18} />
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={props.part.type === "tool" && props.part.tool === "edit"}>
|
<Match when={props.part.type === "tool" && props.part.tool === "edit"}>
|
||||||
|
|
@ -240,7 +241,7 @@ export function Part(props: PartProps) {
|
||||||
state={props.part.state}
|
state={props.part.state}
|
||||||
/>
|
/>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={props.part.tool === "bash"}>
|
<Match when={SHELL.has(props.part.tool)}>
|
||||||
<BashTool
|
<BashTool
|
||||||
id={props.part.id}
|
id={props.part.id}
|
||||||
tool={props.part.tool}
|
tool={props.part.tool}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue