mini: fix shell tool output display (#37711)

This commit is contained in:
Simon Klee
2026-07-19 09:31:38 +02:00
committed by GitHub
parent cbcf191fdb
commit cf6e5b3604
4 changed files with 29 additions and 10 deletions
+2 -4
View File
@@ -2,6 +2,7 @@ import type { EventSubscribeOutput, OpenCodeClient } from "@opencode-ai/client/p
import { SessionMessage } from "@opencode-ai/schema/session-message"
import { EOL } from "node:os"
import { readFile } from "node:fs/promises"
import { toolOutputText } from "./tool"
import { UI } from "./ui"
import type { MiniToolPart } from "./types"
@@ -274,10 +275,7 @@ export async function runNonInteractivePrompt(input: Input) {
state: {
status: "completed",
input: current.input,
output: event.data.content
.filter((item) => item.type === "text")
.map((item) => item.text)
.join("\n"),
output: toolOutputText(current.tool, event.data.content),
title: current.tool,
metadata: {
structured: event.data.structured,
+2 -5
View File
@@ -23,6 +23,7 @@ import type {
} from "@opencode-ai/client/promise"
import { Locale } from "@opencode-ai/tui/util/locale"
import type { FooterSubagentDetail, FooterSubagentState, FooterSubagentTab, MiniToolPart, StreamCommit } from "./types"
import { toolOutputText } from "./tool"
const CHILD_MESSAGE_LIMIT = 80
const CHILD_FRAME_LIMIT = 80
@@ -32,10 +33,6 @@ const FALLBACK_LABEL = "Subagent"
type V2Event = EventSubscribeOutput
export function outputText(content: ReadonlyArray<{ type: string; text?: string }>) {
return content.flatMap((item) => (item.type === "text" && item.text ? [item.text] : [])).join("\n")
}
export function miniTool(input: {
sessionID: string
messageID: string
@@ -82,7 +79,7 @@ export function miniTool(input: {
state: {
status: "completed",
input: tool.state.input,
output: outputText(tool.state.content),
output: toolOutputText(tool.name, tool.state.content),
title: tool.name,
metadata: {
structured: tool.state.structured,
+6
View File
@@ -177,6 +177,12 @@ function text(v: unknown): string {
return typeof v === "string" ? v : ""
}
export function toolOutputText(name: string, content: ReadonlyArray<{ type: string; text?: string }>) {
// V2 shell content appends model-only status after the user-visible command output.
if (name === "shell") return content.find((item) => item.type === "text")?.text ?? ""
return content.flatMap((item) => (item.type === "text" && item.text ? [item.text] : [])).join("\n")
}
function num(v: unknown): number | undefined {
if (typeof v !== "number" || !Number.isFinite(v)) {
return undefined
+19 -1
View File
@@ -2,7 +2,7 @@ import { describe, expect, test } from "bun:test"
import { InstallationVersion } from "@opencode-ai/core/installation/version"
import path from "node:path"
import { mergeInteractiveInput, mergeNonInteractiveInput, parseRunModel, pickRunModel } from "../src/mini"
import { toolInlineInfo, toolView } from "../src/mini/tool"
import { toolInlineInfo, toolOutputText, toolView } from "../src/mini/tool"
async function cli(args: string[]) {
const child = Bun.spawn([process.execPath, "run", "src/index.ts", ...args], {
@@ -36,6 +36,24 @@ describe("mini command", () => {
expect(toolInlineInfo(part)).toMatchObject({ icon: "$", title: "pwd", mode: "block" })
})
test("uses non-empty V2 shell output without the model-facing status", () => {
expect(
toolOutputText("shell", [
{ type: "text", text: "mini-output\n" },
{ type: "text", text: "Command exited with code 0." },
]),
).toBe("mini-output\n")
})
test("keeps empty V2 shell output empty", () => {
expect(
toolOutputText("shell", [
{ type: "text", text: "" },
{ type: "text", text: "Command exited with code 0." },
]),
).toBe("")
})
test("uses piped stdin as the initial prompt", () => {
expect(mergeInteractiveInput("from stdin", undefined)).toBe("from stdin")
expect(mergeInteractiveInput("from stdin", "from flag")).toBe("from stdin\nfrom flag")