Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
84a4102
feat: attemp to inject shell advice in bash tool description, needs t…
ariane-emory Nov 28, 2025
cac1c5e
fix: safer windows corner case
ariane-emory Nov 28, 2025
e444456
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice-ta…
ariane-emory Nov 29, 2025
2d2adce
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice-ta…
ariane-emory Nov 29, 2025
daa6d17
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice-ta…
ariane-emory Nov 29, 2025
21a3816
...
ariane-emory Nov 30, 2025
a7f6cc9
fix: revise Bash tool test to account for the changes.
ariane-emory Nov 30, 2025
083a2ce
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Nov 30, 2025
7511cf2
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Nov 30, 2025
ac56976
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Nov 30, 2025
ebcedac
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 1, 2025
92c5e69
chore: format code
actions-user Dec 1, 2025
4c93e68
Update Nix flake.lock and hashes
actions-user Dec 1, 2025
f1de95e
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 1, 2025
5630a29
Merge upstream/dev into feat/shell-advice
ariane-emory Dec 1, 2025
880db56
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 2, 2025
281fa16
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 2, 2025
23a96fb
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 2, 2025
6e56b0d
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 2, 2025
33645e9
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 3, 2025
9920506
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 3, 2025
053ae51
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 3, 2025
04948f8
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 3, 2025
e1112ce
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 4, 2025
b606010
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 4, 2025
e929696
Merge branch 'dev' into feat/shell-advice
ariane-emory Dec 5, 2025
b19abfc
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 5, 2025
7850e46
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 6, 2025
dc1ab00
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 6, 2025
7cf3f90
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 7, 2025
8473843
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 7, 2025
36b3130
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 7, 2025
661d530
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 7, 2025
732662d
fix: enable fish shell detection in Bash tool
ariane-emory Dec 8, 2025
e132249
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 8, 2025
ba37a27
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 8, 2025
17f8800
Fix TypeScript error: remove cacheKey from FileContents interface usage
ariane-emory Dec 8, 2025
f6871ea
fix: revert damaged file
ariane-emory Dec 8, 2025
dfbda50
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 9, 2025
d4e24ab
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 9, 2025
76a6853
Merge branch 'dev' into feat/shell-advice
ariane-emory Dec 9, 2025
de35be5
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 10, 2025
851179b
Merge branch 'dev' into feat/shell-advice
ariane-emory Dec 10, 2025
40ef162
Merge branch 'dev' into feat/shell-advice
ariane-emory Dec 10, 2025
f9a573f
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 10, 2025
50a4cca
Merge branch 'dev' into feat/shell-advice
ariane-emory Dec 10, 2025
16ff994
Merge remote-tracking branch 'upstream/dev' into feat/shell-advice
ariane-emory Dec 10, 2025
fd3f10a
Fix type error: useKittyKeyboard should be boolean
ariane-emory Dec 10, 2025
776186d
Merge upstream/dev into feat/shell-advice
ariane-emory Dec 10, 2025
cec520b
fix: uncorrupt
ariane-emory Dec 11, 2025
2cb8ed4
Merge branch 'dev' into feat/shell-advice
ariane-emory Dec 11, 2025
7654ed9
Merge branch 'dev' into feat/shell-advice
ariane-emory Dec 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
...
  • Loading branch information
ariane-emory committed Nov 30, 2025
commit 21a3816ad07aa7fa9c750f5ebe4f62656e98d9a4
13 changes: 11 additions & 2 deletions packages/opencode/src/tool/bash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,13 @@ export const BashTool = Tool.define("bash", async () => {
return process.platform === "win32" ? "cmd" : "bash"
}
if (typeof shell === "string") {
const name = path.basename(shell)
let name = path.basename(shell)
// Handle Windows paths (both forward and back slashes)
if (shell.includes("\\") || shell.includes("/")) {
// Extract the last part after both types of separators
const parts = shell.split(/[\\/]/)
name = parts[parts.length - 1]
}
// Handle Windows executables
if (name.toLowerCase().endsWith(".exe")) {
return name.slice(0, -4)
Expand All @@ -104,7 +110,10 @@ export const BashTool = Tool.define("bash", async () => {

const description = `**Shell**: You are executing commands in \`${shellName}\`. Ensure your command syntax is compatible with this shell.

${DESCRIPTION}`
${DESCRIPTION.replace(/\$\{shellName\} command/g, `${shellName} command`).replace(
/\$\{shellName\} commands/g,
`${shellName} commands`,
)}`

return {
description,
Expand Down
6 changes: 3 additions & 3 deletions packages/opencode/src/tool/bash.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Executes a given bash command in a persistent shell session with optional timeout, ensuring proper handling and security measures.
Executes a given ${shellName} command in a persistent shell session with optional timeout, ensuring proper handling and security measures.

Before executing the command, please follow these steps:

Expand Down Expand Up @@ -37,7 +37,7 @@ Usage notes:

If and only if the user asks you to create a new git commit, follow these steps carefully:

1. You have the capability to call multiple tools in a single response. When multiple independent pieces of information are requested, batch your tool calls together for optimal performance. ALWAYS run the following bash commands in parallel, each using the Bash tool:
1. You have the capability to call multiple tools in a single response. When multiple independent pieces of information are requested, batch your tool calls together for optimal performance. ALWAYS run the following ${shellName} commands in parallel, each using the Bash tool:
- Run a git status command to see all untracked files.
- Run a git diff command to see both staged and unstaged changes that will be committed.
- Run a git log command to see recent commit messages, so that you can follow this repository's commit message style.
Expand Down Expand Up @@ -78,7 +78,7 @@ Use the gh command via the Bash tool for ALL GitHub-related tasks including work

IMPORTANT: When the user asks you to create a pull request, follow these steps carefully:

1. You have the capability to call multiple tools in a single response. When multiple independent pieces of information are requested, batch your tool calls together for optimal performance. ALWAYS run the following bash commands in parallel using the Bash tool, in order to understand the current state of the branch since it diverged from the main branch:
1. You have the capability to call multiple tools in a single response. When multiple independent pieces of information are requested, batch your tool calls together for optimal performance. ALWAYS run the following ${shellName} commands in parallel using the Bash tool, in order to understand the current state of the branch since it diverged from the main branch:
- Run a git status command to see all untracked files
- Run a git diff command to see both staged and unstaged changes that will be committed
- Check if the current branch tracks a remote branch and is up to date with the remote, so you know if you need to push to the remote
Expand Down
114 changes: 108 additions & 6 deletions packages/opencode/test/tool/bash.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,115 @@ describe("tool.bash", () => {

// Verify detected shell is appropriate for the platform
if (process.platform === "win32") {
// On Windows, should detect cmd, powershell, bash (WSL/Git Bash), or similar
expect(["cmd", "powershell", "pwsh", "bash", "zsh"].some((s) => detectedShell?.toLowerCase().includes(s))).toBe(
true,
)
expect(["cmd", "powershell"]).toContain(detectedShell!)
} else {
// On Unix-like systems, should detect bash, zsh, fish, sh, or similar
expect(["bash", "zsh", "fish", "sh", "nu"].some((s) => detectedShell === s)).toBe(true)
expect(["bash", "zsh", "fish", "ksh", "csh", "tcsh", "dash"]).toContain(detectedShell!)
}
})

test("description uses dynamic shell-specific language", async () => {
const bash = await BashTool.init()
const shellMatch = bash.description.match(/You are executing commands in `([^`]+)`/)
const detectedShell = shellMatch?.[1]

expect(detectedShell).toBeTruthy()

// Should contain shell-specific command references
if (detectedShell) {
expect(bash.description).toContain(`${detectedShell} command`)
expect(bash.description).toContain(`${detectedShell} commands`)
}

// Should NOT contain generic "bash command" references
expect(bash.description).not.toContain("bash command")
expect(bash.description).not.toContain("bash commands")

// Should still contain "Bash tool" references (tool name)
expect(bash.description).toContain("Bash tool")
})

test("shell-specific language works for different shell types", async () => {
// Test with fish shell (current environment)
const originalShell = process.env.SHELL

try {
// Mock fish shell environment
process.env.SHELL = "/opt/homebrew/bin/fish"
const bashFish = await BashTool.init()
expect(bashFish.description).toContain("fish command")
expect(bashFish.description).toContain("fish commands")

// Mock zsh shell environment
process.env.SHELL = "/bin/zsh"
const bashZsh = await BashTool.init()
expect(bashZsh.description).toContain("zsh command")
expect(bashZsh.description).toContain("zsh commands")

// Mock bash shell environment
process.env.SHELL = "/bin/bash"
const bashBash = await BashTool.init()
expect(bashBash.description).toContain("bash command")
expect(bashBash.description).toContain("bash commands")
} finally {
// Restore original shell
if (originalShell) {
process.env.SHELL = originalShell
} else {
delete process.env.SHELL
}
}
})

test("description uses dynamic shell-specific language", async () => {
const bash = await BashTool.init()
const shellMatch = bash.description.match(/You are executing commands in `([^`]+)`/)
const detectedShell = shellMatch?.[1]

expect(detectedShell).toBeTruthy()

// Should contain shell-specific command references
if (detectedShell) {
expect(bash.description).toContain(`${detectedShell} command`)
expect(bash.description).toContain(`${detectedShell} commands`)
}

// Should NOT contain generic "bash command" references
expect(bash.description).not.toContain("bash command")
expect(bash.description).not.toContain("bash commands")

// Should still contain "Bash tool" references (tool name)
expect(bash.description).toContain("Bash tool")
})

test("shell-specific language works for different shell types", async () => {
// Test with fish shell (current environment)
const originalShell = process.env.SHELL

try {
// Mock fish shell environment
process.env.SHELL = "/opt/homebrew/bin/fish"
const bashFish = await BashTool.init()
expect(bashFish.description).toContain("fish command")
expect(bashFish.description).toContain("fish commands")

// Mock zsh shell environment
process.env.SHELL = "/bin/zsh"
const bashZsh = await BashTool.init()
expect(bashZsh.description).toContain("zsh command")
expect(bashZsh.description).toContain("zsh commands")

// Mock bash shell environment
process.env.SHELL = "/bin/bash"
const bashBash = await BashTool.init()
expect(bashBash.description).toContain("bash command")
expect(bashBash.description).toContain("bash commands")
} finally {
// Restore original shell
if (originalShell) {
process.env.SHELL = originalShell
} else {
delete process.env.SHELL
}
}
})

Expand Down