Skip to content

Commit 2765155

Browse files
committed
Fix shell tool on windows
Signed-off-by: Christopher Petito <chrisjpetito@gmail.com>
1 parent f682584 commit 2765155

1 file changed

Lines changed: 38 additions & 8 deletions

File tree

pkg/tools/builtin/shell.go

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"os"
88
"os/exec"
9+
"runtime"
910

1011
"github.com/docker/cagent/pkg/tools"
1112
)
@@ -18,7 +19,8 @@ type ShellTool struct {
1819
var _ tools.ToolSet = (*ShellTool)(nil)
1920

2021
type shellHandler struct {
21-
shell string
22+
shell string
23+
shellArgsPrefix []string
2224
}
2325

2426
func (h *shellHandler) CallTool(ctx context.Context, toolCall tools.ToolCall) (*tools.ToolCallResult, error) {
@@ -31,12 +33,15 @@ func (h *shellHandler) CallTool(ctx context.Context, toolCall tools.ToolCall) (*
3133
return nil, fmt.Errorf("invalid arguments: %w", err)
3234
}
3335

34-
cmd := exec.CommandContext(ctx, h.shell, "-c", params.Cmd)
36+
cmd := exec.CommandContext(ctx, h.shell, append(h.shellArgsPrefix, params.Cmd)...)
3537
cmd.Env = os.Environ()
3638
if params.Cwd != "" {
3739
cmd.Dir = params.Cwd
3840
} else {
39-
cmd.Dir = os.Getenv("PWD")
41+
// Use the current working directory; avoid PWD on Windows (may be MSYS-style like /c/...)
42+
if wd, err := os.Getwd(); err == nil {
43+
cmd.Dir = wd
44+
}
4045
}
4146

4247
output, err := cmd.CombinedOutput()
@@ -52,14 +57,39 @@ func (h *shellHandler) CallTool(ctx context.Context, toolCall tools.ToolCall) (*
5257
}
5358

5459
func NewShellTool() *ShellTool {
55-
shell := os.Getenv("SHELL")
56-
if shell == "" {
57-
shell = "/bin/sh" // Fallback to /bin/sh if SHELL is not set
60+
var shell string
61+
var argsPrefix []string
62+
63+
if runtime.GOOS == "windows" {
64+
// Prefer PowerShell (pwsh or Windows PowerShell) when available, otherwise fall back to cmd.exe
65+
if path, err := exec.LookPath("pwsh.exe"); err == nil {
66+
shell = path
67+
argsPrefix = []string{"-NoProfile", "-NonInteractive", "-Command"}
68+
} else if path, err := exec.LookPath("powershell.exe"); err == nil {
69+
shell = path
70+
argsPrefix = []string{"-NoProfile", "-NonInteractive", "-Command"}
71+
} else {
72+
// Use ComSpec if available, otherwise default to cmd.exe
73+
if comspec := os.Getenv("ComSpec"); comspec != "" {
74+
shell = comspec
75+
} else {
76+
shell = "cmd.exe"
77+
}
78+
argsPrefix = []string{"/C"}
79+
}
80+
} else {
81+
// Unix-like: use SHELL or default to /bin/sh
82+
shell = os.Getenv("SHELL")
83+
if shell == "" {
84+
shell = "/bin/sh"
85+
}
86+
argsPrefix = []string{"-c"}
5887
}
5988

6089
return &ShellTool{
6190
handler: &shellHandler{
62-
shell: shell,
91+
shell: shell,
92+
shellArgsPrefix: argsPrefix,
6393
},
6494
}
6595
}
@@ -71,7 +101,7 @@ Execute shell commands in the user's environment with full control over working
71101
72102
## Core Concepts
73103
74-
**Execution Context**: Commands run in the user's default shell (${SHELL}) with access to all environment variables and the current workspace.
104+
**Execution Context**: Commands run in the user's default shell with access to all environment variables and the current workspace. On Windows, PowerShell (pwsh/powershell) is used when available; otherwise, cmd.exe is used. On Unix-like systems, ${SHELL} is used or /bin/sh as fallback.
75105
76106
**Working Directory Management**:
77107
- Default execution location: workspace root

0 commit comments

Comments
 (0)