diff --git a/packages/cli/src/mini/demo.ts b/packages/cli/src/mini/demo.ts index b37f7113175..930f278ef05 100644 --- a/packages/cli/src/mini/demo.ts +++ b/packages/cli/src/mini/demo.ts @@ -9,7 +9,7 @@ // Slash commands: // /permission [kind] → triggers a permission request variant // /question [kind] → triggers a question request variant -// /fmt → emits a specific tool/text type (text, reasoning, bash, +// /fmt → emits a specific tool/text type (text, reasoning, shell, // write, edit, patch, task, question, error, mix) // // Demo mode also handles permission and question replies locally, completing @@ -33,7 +33,7 @@ const KINDS = [ "table", "text", "reasoning", - "bash", + "shell", "write", "edit", "patch", @@ -42,7 +42,7 @@ const KINDS = [ "error", "mix", ] -const PERMISSIONS = ["edit", "bash", "read", "task", "external", "doom"] as const +const PERMISSIONS = ["edit", "shell", "read", "task", "external", "doom"] as const const QUESTIONS = ["multi", "single", "checklist", "custom"] as const type PermissionKind = (typeof PERMISSIONS)[number] @@ -436,7 +436,7 @@ function emitError(state: State, text: string): void { } async function emitBash(state: State, signal?: AbortSignal): Promise { - const ref = make(state, "bash", { + const ref = make(state, "shell", { command: "git status", workdir: process.cwd(), description: "Show git status", @@ -623,16 +623,16 @@ function emitPermission(state: State, kind: PermissionKind = "edit"): void { const root = process.cwd() const file = path.join(root, "src", "demo-format.ts") - if (kind === "bash") { + if (kind === "shell") { const command = "git status --short" - const ref = make(state, "bash", { + const ref = make(state, "shell", { command, workdir: root, description: "Inspect worktree changes", }) askPermission(state, { ref, - permission: "bash", + permission: "shell", patterns: [command], always: ["*"], done: { @@ -862,7 +862,7 @@ async function emitFmt(state: State, kind: string, body: string, signal?: AbortS return true } - if (kind === "bash") { + if (kind === "shell") { await emitBash(state, signal) return true } @@ -924,7 +924,7 @@ function intro(state: State): void { `- /question [kind] (${QUESTIONS.join(", ")})`, `- /fmt (${KINDS.join(", ")})`, "Examples:", - "- /permission bash", + "- /permission shell", "- /question custom", "- /fmt markdown", "- /fmt table", diff --git a/packages/cli/src/mini/stream-v2.transport.ts b/packages/cli/src/mini/stream-v2.transport.ts index bf2ccd5bf9a..057801d2cd8 100644 --- a/packages/cli/src/mini/stream-v2.transport.ts +++ b/packages/cli/src/mini/stream-v2.transport.ts @@ -218,7 +218,7 @@ function shellCommit( kind: "tool", source: "tool", partID: `shell:${callID}`, - tool: "bash", + tool: "shell", shell: { callID, command }, ...next, } diff --git a/packages/cli/src/mini/tool.ts b/packages/cli/src/mini/tool.ts index 3772debdc5b..fc30c565ec9 100644 --- a/packages/cli/src/mini/tool.ts +++ b/packages/cli/src/mini/tool.ts @@ -1,6 +1,6 @@ // Per-tool display rules shared across `opencode run` output paths. // -// Each known tool (bash, edit, write, task, etc.) has a ToolRule that controls +// Each known tool (shell, edit, write, task, etc.) has a ToolRule that controls // five display hooks: // // view → visibility policy for progress/final scrollback entries and @@ -114,7 +114,7 @@ type ToolPermissionCtx = { type ToolName = | "invalid" - | "bash" + | "shell" | "write" | "edit" | "patch" @@ -648,7 +648,7 @@ function scrollBashProgress(p: ToolProps): string { return fmt(out) } -function scrollBashFinal(p: ToolProps): string { +function scrollShellFinal(p: ToolProps): string { if (p.frame.status === "error") { return fail(p.frame) } @@ -657,13 +657,13 @@ function scrollBashFinal(p: ToolProps): string { const time = span(p.frame.state) if (code === undefined) { if (!time) { - return "bash completed" + return "shell completed" } - return `bash completed · ${time}` + return `shell completed · ${time}` } - return `bash completed (exit ${code})${time ? ` · ${time}` : ""}` + return `shell completed (exit ${code})${time ? ` · ${time}` : ""}` } function scrollReadStart(p: ToolProps): string { @@ -976,16 +976,16 @@ const TOOL_RULES = { start: () => "", }, }, - bash: { + shell: { view: { output: true, final: false, }, - run: runBash, + run: runShell, scroll: { start: scrollBashStart, progress: scrollBashProgress, - final: scrollBashFinal, + final: scrollShellFinal, }, permission: permBash, }, @@ -1202,7 +1202,7 @@ export function toolFrame(commit: StreamCommit, raw: string): ToolFrame { } } -function runBash(p: ToolProps): ToolInline { +function runShell(p: ToolProps): ToolInline { return { icon: "$", title: p.input.command || "", diff --git a/packages/cli/test/mini.test.ts b/packages/cli/test/mini.test.ts index 17ff0177c72..5fb8b3be7f4 100644 --- a/packages/cli/test/mini.test.ts +++ b/packages/cli/test/mini.test.ts @@ -2,6 +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" async function cli(args: string[]) { const child = Bun.spawn([process.execPath, "run", "src/index.ts", ...args], { @@ -18,6 +19,23 @@ async function cli(args: string[]) { } describe("mini command", () => { + test("renders the renamed shell tool with the shell rule", () => { + const part = { + id: "part-shell", + sessionID: "session-shell", + messageID: "message-shell", + callID: "call-shell", + tool: "shell", + state: { + status: "pending" as const, + input: { command: "pwd" }, + }, + } as const + + expect(toolView(part.tool)).toEqual({ output: true, final: false }) + expect(toolInlineInfo(part)).toMatchObject({ icon: "$", title: "pwd", mode: "block" }) + }) + 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")