refactor(core): replace bash tool with shell tool

This commit is contained in:
Dax Raad
2026-06-29 00:43:04 -04:00
parent 595c6bd4a7
commit 5ae93092aa
37 changed files with 2260 additions and 901 deletions
@@ -9,7 +9,7 @@ import { PtyTicket } from "@opencode-ai/core/pty/ticket"
import { LocationServiceMap, locationServiceMapLayer } from "@opencode-ai/core/location-services"
import { Location } from "@opencode-ai/core/location"
import { AbsolutePath } from "@opencode-ai/core/schema"
import { Shell } from "@opencode-ai/core/shell"
import { ShellSelect } from "@opencode-ai/core/shell/select"
import { CorsConfig, isAllowedRequestOrigin, type CorsOptions } from "@opencode-ai/server/cors"
import {
PTY_CONNECT_TICKET_QUERY,
@@ -58,7 +58,7 @@ export const ptyHandlers = HttpApiBuilder.group(InstanceHttpApi, "pty", (handler
})
const shells = Effect.fn("PtyHttpApi.shells")(function* () {
return yield* Effect.promise(() => Shell.list())
return yield* Effect.promise(() => ShellSelect.list())
})
const list = Effect.fn("PtyHttpApi.list")(function* () {
+4 -4
View File
@@ -35,7 +35,7 @@ import { Tool } from "@/tool/tool"
import { Permission } from "@/permission"
import { SessionStatus } from "./status"
import { LLM } from "./llm"
import { Shell } from "@opencode-ai/core/shell"
import { ShellSelect } from "@opencode-ai/core/shell/select"
import { ShellID } from "@/tool/shell/id"
import { FSUtil } from "@opencode-ai/core/fs-util"
import { Truncate } from "@/tool/truncate"
@@ -520,8 +520,8 @@ export const layer = Layer.effect(
}).pipe(Effect.ensuring(markReady))
const cfg = yield* config.get()
const sh = Shell.preferred(cfg.shell)
const args = Shell.args(sh, input.command, cwd)
const sh = ShellSelect.preferred(cfg.shell)
const args = ShellSelect.args(sh, input.command, cwd)
let output = ""
let aborted = false
@@ -1396,7 +1396,7 @@ export const layer = Layer.effect(
const shellMatches = ConfigMarkdown.shell(template)
if (shellMatches.length > 0) {
const cfg = yield* config.get()
const sh = Shell.preferred(cfg.shell)
const sh = ShellSelect.preferred(cfg.shell)
const results = yield* Effect.promise(() =>
Promise.all(
shellMatches.map(async ([, cmd]) => (await Process.text([cmd], { shell: sh, nothrow: true })).text),
+7 -7
View File
@@ -12,7 +12,7 @@ import { FSUtil } from "@opencode-ai/core/fs-util"
import { fileURLToPath } from "url"
import { Config } from "@/config/config"
import { RuntimeFlags } from "@/effect/runtime-flags"
import { Shell } from "@opencode-ai/core/shell"
import { ShellSelect } from "@opencode-ai/core/shell/select"
import { ShellID } from "./shell/id"
import * as Truncate from "./truncate"
@@ -291,7 +291,7 @@ const ask = Effect.fn("ShellTool.ask")(function* (ctx: Tool.Context, scan: Scan,
})
function cmd(shell: string, command: string, cwd: string, env: NodeJS.ProcessEnv) {
if (process.platform === "win32" && Shell.ps(shell)) {
if (process.platform === "win32" && ShellSelect.ps(shell)) {
return ChildProcess.make(shell, ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", command], {
cwd,
env,
@@ -357,7 +357,7 @@ export const ShellTool = Tool.define(
const resolvePath = Effect.fn("ShellTool.resolvePath")(function* (text: string, root: string, shell: string) {
if (process.platform === "win32") {
if (Shell.posix(shell) && text.startsWith("/") && FSUtil.windowsPath(text) === text) {
if (ShellSelect.posix(shell) && text.startsWith("/") && FSUtil.windowsPath(text) === text) {
const file = yield* cygpath(shell, text)
if (file) return file
}
@@ -387,7 +387,7 @@ export const ShellTool = Tool.define(
patterns: new Set<string>(),
always: new Set<string>(),
}
const shellKind = ShellID.toKind(Shell.name(shell))
const shellKind = ShellID.toKind(ShellSelect.name(shell))
for (const node of commands(root)) {
const command = parts(node)
@@ -597,8 +597,8 @@ export const ShellTool = Tool.define(
return () =>
Effect.gen(function* () {
const cfg = yield* config.get()
const shell = Shell.acceptable(cfg.shell)
const name = Shell.name(shell)
const shell = ShellSelect.acceptable(cfg.shell)
const name = ShellSelect.name(shell)
const limits = yield* trunc.limits()
const prompt = ShellPrompt.render(name, process.platform, limits, defaultTimeoutMs)
yield* Effect.logInfo("shell tool using shell", { shell })
@@ -616,7 +616,7 @@ export const ShellTool = Tool.define(
throw new Error(`Invalid timeout value: ${params.timeout}. Timeout must be a positive number.`)
}
const timeout = params.timeout ?? defaultTimeoutMs
const ps = Shell.ps(shell)
const ps = ShellSelect.ps(shell)
yield* Effect.scoped(
Effect.gen(function* () {
const tree = yield* Effect.acquireRelease(parse(params.command, ps), (tree) =>