Skip to content

Commit 8a94a2a

Browse files
Apply PR #20039: feat: bash->shell tool + pwsh/powershell/cmd/bash specific tool definitions so agents work better
2 parents c744717 + 529a6ed commit 8a94a2a

14 files changed

Lines changed: 512 additions & 173 deletions

File tree

packages/opencode/src/acp/agent.ts

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import { LoadAPIKeyError } from "ai"
5151
import type { AssistantMessage, Event, OpencodeClient, SessionMessageResponse, ToolPart } from "@opencode-ai/sdk/v2"
5252
import { applyPatch } from "diff"
5353
import { InstallationVersion } from "@opencode-ai/core/installation/version"
54+
import { ShellToolID } from "@/tool/shell/id"
5455

5556
type ModeOption = { id: string; name: string; description?: string }
5657
type ModelOption = { modelId: string; name: string }
@@ -144,7 +145,7 @@ export class Agent implements ACPAgent {
144145
private sessionManager: ACPSessionManager
145146
private eventAbort = new AbortController()
146147
private eventStarted = false
147-
private bashSnapshots = new Map<string, string>()
148+
private shellSnapshots = new Map<string, string>()
148149
private toolStarts = new Set<string>()
149150
private permissionQueues = new Map<string, Promise<void>>()
150151
private permissionOptions: PermissionOption[] = [
@@ -283,16 +284,16 @@ export class Agent implements ACPAgent {
283284

284285
switch (part.state.status) {
285286
case "pending":
286-
this.bashSnapshots.delete(part.callID)
287+
this.shellSnapshots.delete(part.callID)
287288
return
288289

289290
case "running":
290-
const output = this.bashOutput(part)
291+
const output = this.shellOutput(part)
291292
const content: ToolCallContent[] = []
292293
if (output) {
293294
const hash = Hash.fast(output)
294-
if (part.tool === "bash") {
295-
if (this.bashSnapshots.get(part.callID) === hash) {
295+
if (part.tool === ShellToolID.id) {
296+
if (this.shellSnapshots.get(part.callID) === hash) {
296297
await this.connection
297298
.sessionUpdate({
298299
sessionId,
@@ -311,7 +312,7 @@ export class Agent implements ACPAgent {
311312
})
312313
return
313314
}
314-
this.bashSnapshots.set(part.callID, hash)
315+
this.shellSnapshots.set(part.callID, hash)
315316
}
316317
content.push({
317318
type: "content",
@@ -342,7 +343,7 @@ export class Agent implements ACPAgent {
342343

343344
case "completed": {
344345
this.toolStarts.delete(part.callID)
345-
this.bashSnapshots.delete(part.callID)
346+
this.shellSnapshots.delete(part.callID)
346347
const kind = toToolKind(part.tool)
347348
const content: ToolCallContent[] = [
348349
{
@@ -423,7 +424,7 @@ export class Agent implements ACPAgent {
423424
}
424425
case "error":
425426
this.toolStarts.delete(part.callID)
426-
this.bashSnapshots.delete(part.callID)
427+
this.shellSnapshots.delete(part.callID)
427428
await this.connection
428429
.sessionUpdate({
429430
sessionId,
@@ -837,10 +838,10 @@ export class Agent implements ACPAgent {
837838
await this.toolStart(sessionId, part)
838839
switch (part.state.status) {
839840
case "pending":
840-
this.bashSnapshots.delete(part.callID)
841+
this.shellSnapshots.delete(part.callID)
841842
break
842843
case "running":
843-
const output = this.bashOutput(part)
844+
const output = this.shellOutput(part)
844845
const runningContent: ToolCallContent[] = []
845846
if (output) {
846847
runningContent.push({
@@ -871,7 +872,7 @@ export class Agent implements ACPAgent {
871872
break
872873
case "completed":
873874
this.toolStarts.delete(part.callID)
874-
this.bashSnapshots.delete(part.callID)
875+
this.shellSnapshots.delete(part.callID)
875876
const kind = toToolKind(part.tool)
876877
const content: ToolCallContent[] = [
877878
{
@@ -951,7 +952,7 @@ export class Agent implements ACPAgent {
951952
break
952953
case "error":
953954
this.toolStarts.delete(part.callID)
954-
this.bashSnapshots.delete(part.callID)
955+
this.shellSnapshots.delete(part.callID)
955956
await this.connection
956957
.sessionUpdate({
957958
sessionId,
@@ -1105,8 +1106,8 @@ export class Agent implements ACPAgent {
11051106
}
11061107
}
11071108

1108-
private bashOutput(part: ToolPart) {
1109-
if (part.tool !== "bash") return
1109+
private shellOutput(part: ToolPart) {
1110+
if (part.tool !== ShellToolID.id) return
11101111
if (!("metadata" in part.state) || !part.state.metadata || typeof part.state.metadata !== "object") return
11111112
const output = part.state.metadata["output"]
11121113
if (typeof output !== "string") return
@@ -1549,9 +1550,11 @@ export class Agent implements ACPAgent {
15491550

15501551
function toToolKind(toolName: string): ToolKind {
15511552
const tool = toolName.toLocaleLowerCase()
1553+
15521554
switch (tool) {
1553-
case "bash":
1555+
case ShellToolID.id:
15541556
return "execute"
1557+
15551558
case "webfetch":
15561559
return "fetch"
15571560

@@ -1576,6 +1579,7 @@ function toToolKind(toolName: string): ToolKind {
15761579

15771580
function toLocations(toolName: string, input: Record<string, any>): { path: string }[] {
15781581
const tool = toolName.toLocaleLowerCase()
1582+
15791583
switch (tool) {
15801584
case "read":
15811585
case "edit":
@@ -1584,7 +1588,7 @@ function toLocations(toolName: string, input: Record<string, any>): { path: stri
15841588
case "glob":
15851589
case "grep":
15861590
return input["path"] ? [{ path: input["path"] }] : []
1587-
case "bash":
1591+
case ShellToolID.id:
15881592
return []
15891593
default:
15901594
return []

packages/opencode/src/cli/cmd/github.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -879,7 +879,7 @@ export const GithubRunCommand = cmd({
879879
function subscribeSessionEvents() {
880880
const TOOL: Record<string, [string, string]> = {
881881
todowrite: ["Todo", UI.Style.TEXT_WARNING_BOLD],
882-
bash: ["Bash", UI.Style.TEXT_DANGER_BOLD],
882+
bash: ["Shell", UI.Style.TEXT_DANGER_BOLD],
883883
edit: ["Edit", UI.Style.TEXT_SUCCESS_BOLD],
884884
glob: ["Glob", UI.Style.TEXT_INFO_BOLD],
885885
grep: ["Grep", UI.Style.TEXT_INFO_BOLD],

packages/opencode/src/cli/cmd/run.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ import { CodeSearchTool } from "../../tool/codesearch"
2323
import { WebSearchTool } from "../../tool/websearch"
2424
import { TaskTool } from "../../tool/task"
2525
import { SkillTool } from "../../tool/skill"
26-
import { BashTool } from "../../tool/bash"
26+
import { ShellTool } from "../../tool/shell"
27+
import { ShellToolID } from "../../tool/shell/id"
2728
import { TodoWriteTool } from "../../tool/todo"
2829
import { Locale } from "@/util/locale"
2930
import { AppRuntime } from "@/effect/app-runtime"
@@ -183,7 +184,7 @@ function skill(info: ToolProps<typeof SkillTool>) {
183184
})
184185
}
185186

186-
function bash(info: ToolProps<typeof BashTool>) {
187+
function shell(info: ToolProps<typeof ShellTool>) {
187188
const output = info.part.state.status === "completed" ? info.part.state.output?.trim() : undefined
188189
block(
189190
{
@@ -413,7 +414,7 @@ export const RunCommand = cmd({
413414
async function execute(sdk: OpencodeClient) {
414415
function tool(part: ToolPart) {
415416
try {
416-
if (part.tool === "bash") return bash(props<typeof BashTool>(part))
417+
if (part.tool === ShellToolID.id) return shell(props<typeof ShellTool>(part))
417418
if (part.tool === "glob") return glob(props<typeof GlobTool>(part))
418419
if (part.tool === "grep") return grep(props<typeof GrepTool>(part))
419420
if (part.tool === "read") return read(props<typeof ReadTool>(part))

packages/opencode/src/cli/cmd/tui/routes/session/index.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ import { Locale } from "@/util/locale"
3737
import type { Tool } from "@/tool/tool"
3838
import type { ReadTool } from "@/tool/read"
3939
import type { WriteTool } from "@/tool/write"
40-
import { BashTool } from "@/tool/bash"
40+
import { ShellTool } from "@/tool/shell"
41+
import { ShellToolID } from "@/tool/shell/id"
4142
import type { GlobTool } from "@/tool/glob"
4243
import { TodoWriteTool } from "@/tool/todo"
4344
import type { GrepTool } from "@/tool/grep"
@@ -1555,8 +1556,8 @@ function ToolPart(props: { last: boolean; part: ToolPart; message: AssistantMess
15551556
return (
15561557
<Show when={!shouldHide()}>
15571558
<Switch>
1558-
<Match when={props.part.tool === "bash"}>
1559-
<Bash {...toolprops} />
1559+
<Match when={props.part.tool === ShellToolID.id}>
1560+
<Shell {...toolprops} />
15601561
</Match>
15611562
<Match when={props.part.tool === "glob"}>
15621563
<Glob {...toolprops} />
@@ -1790,7 +1791,7 @@ function BlockTool(props: {
17901791
)
17911792
}
17921793

1793-
function Bash(props: ToolProps<typeof BashTool>) {
1794+
function Shell(props: ToolProps<typeof ShellTool>) {
17941795
const { theme } = useTheme()
17951796
const sync = useSync()
17961797
const isRunning = createMemo(() => props.part.state.status === "running")

packages/opencode/src/cli/cmd/tui/routes/session/permission.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { LANGUAGE_EXTENSIONS } from "@/lsp/language"
1515
import { Keybind } from "@/util/keybind"
1616
import { Locale } from "@/util/locale"
1717
import { Global } from "@opencode-ai/core/global"
18+
import { ShellToolID } from "@/tool/shell/id"
1819
import { useDialog } from "../../ui/dialog"
1920
import { getScrollAcceleration } from "../../util/scroll"
2021
import { useTuiConfig } from "../../context/tui-config"
@@ -287,7 +288,7 @@ export function PermissionPrompt(props: { request: PermissionRequest }) {
287288
}
288289
}
289290

290-
if (permission === "bash") {
291+
if (permission === ShellToolID.id) {
291292
const title =
292293
typeof data.description === "string" && data.description ? data.description : "Shell command"
293294
const command = typeof data.command === "string" ? data.command : ""

packages/opencode/src/session/prompt.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import { Permission } from "@/permission"
4141
import { SessionStatus } from "./status"
4242
import { LLM } from "./llm"
4343
import { Shell } from "@/shell/shell"
44+
import { ShellToolID } from "@/tool/shell/id"
4445
import { AppFileSystem } from "@opencode-ai/core/filesystem"
4546
import { Truncate } from "@/tool/truncate"
4647
import { decodeDataUrl } from "@/util/data-url"
@@ -778,7 +779,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
778779
id: PartID.ascending(),
779780
messageID: msg.id,
780781
sessionID: input.sessionID,
781-
tool: "bash",
782+
tool: ShellToolID.id,
782783
callID: ulid(),
783784
state: {
784785
status: "running",

packages/opencode/src/tool/registry.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { PlanExitTool } from "./plan"
22
import { Session } from "@/session/session"
33
import { QuestionTool } from "./question"
4-
import { BashTool } from "./bash"
4+
import { ShellTool } from "./shell"
55
import { EditTool } from "./edit"
66
import { GlobTool } from "./glob"
77
import { GrepTool } from "./grep"
@@ -107,7 +107,7 @@ export const layer: Layer.Layer<
107107
const plan = yield* PlanExitTool
108108
const webfetch = yield* WebFetchTool
109109
const websearch = yield* WebSearchTool
110-
const bash = yield* BashTool
110+
const shell = yield* ShellTool
111111
const codesearch = yield* CodeSearchTool
112112
const globtool = yield* GlobTool
113113
const writetool = yield* WriteTool
@@ -188,7 +188,7 @@ export const layer: Layer.Layer<
188188

189189
const tool = yield* Effect.all({
190190
invalid: Tool.init(invalid),
191-
bash: Tool.init(bash),
191+
shell: Tool.init(shell),
192192
read: Tool.init(read),
193193
glob: Tool.init(globtool),
194194
grep: Tool.init(greptool),
@@ -211,7 +211,7 @@ export const layer: Layer.Layer<
211211
builtin: [
212212
tool.invalid,
213213
...(questionEnabled ? [tool.question] : []),
214-
tool.bash,
214+
tool.shell,
215215
tool.read,
216216
tool.glob,
217217
tool.grep,

0 commit comments

Comments
 (0)