fix(cli): rename bash tool to shell

This commit is contained in:
Dax Raad
2026-07-14 21:31:42 -04:00
parent 387bff8fd9
commit 05fdbcce04
4 changed files with 38 additions and 20 deletions
+9 -9
View File
@@ -9,7 +9,7 @@
// Slash commands:
// /permission [kind] → triggers a permission request variant
// /question [kind] → triggers a question request variant
// /fmt <kind> → emits a specific tool/text type (text, reasoning, bash,
// /fmt <kind> → 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<void> {
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 <kind> (${KINDS.join(", ")})`,
"Examples:",
"- /permission bash",
"- /permission shell",
"- /question custom",
"- /fmt markdown",
"- /fmt table",
+1 -1
View File
@@ -218,7 +218,7 @@ function shellCommit(
kind: "tool",
source: "tool",
partID: `shell:${callID}`,
tool: "bash",
tool: "shell",
shell: { callID, command },
...next,
}
+10 -10
View File
@@ -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 || "",
+18
View File
@@ -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")