Skip to content

Commit 0e6e96f

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

14 files changed

Lines changed: 513 additions & 176 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
@@ -22,7 +22,8 @@ import { WriteTool } from "../../tool/write"
2222
import { WebSearchTool } from "../../tool/websearch"
2323
import { TaskTool } from "../../tool/task"
2424
import { SkillTool } from "../../tool/skill"
25-
import { BashTool } from "../../tool/bash"
25+
import { ShellTool } from "../../tool/shell"
26+
import { ShellToolID } from "../../tool/shell/id"
2627
import { TodoWriteTool } from "../../tool/todo"
2728
import { Locale } from "@/util/locale"
2829
import { AppRuntime } from "@/effect/app-runtime"
@@ -175,7 +176,7 @@ function skill(info: ToolProps<typeof SkillTool>) {
175176
})
176177
}
177178

178-
function bash(info: ToolProps<typeof BashTool>) {
179+
function shell(info: ToolProps<typeof ShellTool>) {
179180
const output = info.part.state.status === "completed" ? info.part.state.output?.trim() : undefined
180181
block(
181182
{
@@ -405,7 +406,7 @@ export const RunCommand = cmd({
405406
async function execute(sdk: OpencodeClient) {
406407
function tool(part: ToolPart) {
407408
try {
408-
if (part.tool === "bash") return bash(props<typeof BashTool>(part))
409+
if (part.tool === ShellToolID.id) return shell(props<typeof ShellTool>(part))
409410
if (part.tool === "glob") return glob(props<typeof GlobTool>(part))
410411
if (part.tool === "grep") return grep(props<typeof GrepTool>(part))
411412
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"
@@ -1558,8 +1559,8 @@ function ToolPart(props: { last: boolean; part: ToolPart; message: AssistantMess
15581559
return (
15591560
<Show when={!shouldHide()}>
15601561
<Switch>
1561-
<Match when={props.part.tool === "bash"}>
1562-
<Bash {...toolprops} />
1562+
<Match when={props.part.tool === ShellToolID.id}>
1563+
<Shell {...toolprops} />
15631564
</Match>
15641565
<Match when={props.part.tool === "glob"}>
15651566
<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"
@@ -780,7 +781,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
780781
id: PartID.ascending(),
781782
messageID: msg.id,
782783
sessionID: input.sessionID,
783-
tool: "bash",
784+
tool: ShellToolID.id,
784785
callID: ulid(),
785786
state: {
786787
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"
@@ -106,7 +106,7 @@ export const layer: Layer.Layer<
106106
const plan = yield* PlanExitTool
107107
const webfetch = yield* WebFetchTool
108108
const websearch = yield* WebSearchTool
109-
const bash = yield* BashTool
109+
const shell = yield* ShellTool
110110
const globtool = yield* GlobTool
111111
const writetool = yield* WriteTool
112112
const edit = yield* EditTool
@@ -186,7 +186,7 @@ export const layer: Layer.Layer<
186186

187187
const tool = yield* Effect.all({
188188
invalid: Tool.init(invalid),
189-
bash: Tool.init(bash),
189+
shell: Tool.init(shell),
190190
read: Tool.init(read),
191191
glob: Tool.init(globtool),
192192
grep: Tool.init(greptool),
@@ -208,7 +208,7 @@ export const layer: Layer.Layer<
208208
builtin: [
209209
tool.invalid,
210210
...(questionEnabled ? [tool.question] : []),
211-
tool.bash,
211+
tool.shell,
212212
tool.read,
213213
tool.glob,
214214
tool.grep,

0 commit comments

Comments
 (0)