refactor(core): replace bash tool with shell tool

This commit is contained in:
Dax Raad
2026-06-29 00:44:17 -04:00
parent 595c6bd4a7
commit 5ae93092aa
37 changed files with 2260 additions and 901 deletions
@@ -43,7 +43,7 @@ import { SessionV2 } from "@opencode-ai/core/session"
import { SessionExecution } from "@opencode-ai/core/session/execution"
import { Skill } from "../../src/skill"
import { SystemPrompt } from "../../src/session/system"
import { Shell } from "@opencode-ai/core/shell"
import { ShellSelect } from "@opencode-ai/core/shell/select"
import { Snapshot } from "../../src/snapshot"
import { ToolRegistry } from "@/tool/registry"
import { Truncate } from "@/tool/truncate"
@@ -77,7 +77,7 @@ function withSh<A, E, R>(fx: () => Effect.Effect<A, E, R>) {
Effect.sync(() => {
const prev = process.env.SHELL
process.env.SHELL = "/bin/sh"
Shell.preferred.reset()
ShellSelect.preferred.reset()
return prev
}),
() => fx(),
@@ -85,7 +85,7 @@ function withSh<A, E, R>(fx: () => Effect.Effect<A, E, R>) {
Effect.sync(() => {
if (prev === undefined) delete process.env.SHELL
else process.env.SHELL = prev
Shell.preferred.reset()
ShellSelect.preferred.reset()
}),
)
}
+14 -14
View File
@@ -5,7 +5,7 @@ import type * as Scope from "effect/Scope"
import os from "os"
import path from "path"
import { Config } from "@/config/config"
import { Shell } from "@opencode-ai/core/shell"
import { ShellSelect } from "@opencode-ai/core/shell/select"
import { ShellTool } from "../../src/tool/shell"
import { Filesystem } from "@/util/filesystem"
import { provideInstance, testInstanceStoreLayer, tmpdirScoped } from "../fixture/fixture"
@@ -77,25 +77,25 @@ const ctx = {
ask: () => Effect.void,
}
Shell.acceptable.reset()
ShellSelect.acceptable.reset()
const quote = (text: string) => `"${text}"`
const squote = (text: string) => `'${text}'`
const projectRoot = path.join(__dirname, "../..")
const bin = quote(process.execPath.replaceAll("\\", "/"))
const bash = (() => {
const shell = Shell.acceptable()
if (Shell.name(shell) === "bash") return shell
return Shell.gitbash()
const shell = ShellSelect.acceptable()
if (ShellSelect.name(shell) === "bash") return shell
return ShellSelect.gitbash()
})()
const shells = (() => {
if (process.platform !== "win32") {
const shell = Shell.acceptable()
return [{ label: Shell.name(shell), shell }]
const shell = ShellSelect.acceptable()
return [{ label: ShellSelect.name(shell), shell }]
}
const list = [bash, Bun.which("pwsh"), Bun.which("powershell"), process.env.COMSPEC || Bun.which("cmd.exe")]
.filter((shell): shell is string => Boolean(shell))
.map((shell) => ({ label: Shell.name(shell), shell }))
.map((shell) => ({ label: ShellSelect.name(shell), shell }))
return list.filter(
(item, i) => list.findIndex((other) => other.shell.toLowerCase() === item.shell.toLowerCase()) === i,
@@ -105,7 +105,7 @@ const PS = new Set(["pwsh", "powershell"])
const ps = shells.filter((item) => PS.has(item.label))
const cmdShell = shells.find((item) => item.label === "cmd")
const sh = () => Shell.name(Shell.acceptable())
const sh = () => ShellSelect.name(ShellSelect.acceptable())
const evalarg = (text: string) => (sh() === "cmd" ? quote(text) : squote(text))
const fill = (mode: "lines" | "bytes", n: number) => {
@@ -133,8 +133,8 @@ const withShell = <A, E, R>(item: { label: string; shell: string }, self: Effect
Effect.sync(() => {
const prev = process.env.SHELL
process.env.SHELL = item.shell
Shell.acceptable.reset()
Shell.preferred.reset()
ShellSelect.acceptable.reset()
ShellSelect.preferred.reset()
return prev
}),
() => self,
@@ -142,8 +142,8 @@ const withShell = <A, E, R>(item: { label: string; shell: string }, self: Effect
Effect.sync(() => {
if (prev === undefined) delete process.env.SHELL
else process.env.SHELL = prev
Shell.acceptable.reset()
Shell.preferred.reset()
ShellSelect.acceptable.reset()
ShellSelect.preferred.reset()
}),
)
@@ -196,7 +196,7 @@ describe("tool.shell", () => {
tmp,
Effect.gen(function* () {
const bash = yield* initBash()
const fallback = Shell.name(Shell.acceptable("fish"))
const fallback = ShellSelect.name(ShellSelect.acceptable("fish"))
expect(fallback).not.toBe("fish")
expect(bash.description).toContain(fallback)