Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions packages/cli/src/mini/noninteractive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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,
Expand Down
7 changes: 2 additions & 5 deletions packages/cli/src/mini/stream-v2.subagent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions packages/cli/src/mini/tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 19 additions & 1 deletion packages/cli/test/mini.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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], {
Expand Down Expand Up @@ -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")
Expand Down
Loading