Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
e1fd3a7
Resolve conflicts from stash pop after rebase onto main (integrate PO…
euxaristia Jul 4, 2026
9c49bca
fix(tools): improve Windows POSIX pipe detection and guidance to prev…
euxaristia Jul 4, 2026
d6fdeb3
fix: address static analysis findings for depth limit, persistence er…
euxaristia Jul 4, 2026
e6694c5
fix: address CodeRabbit review comments on PR 468
euxaristia Jul 4, 2026
b7f5af7
test(tools): increase slack for Windows CI jitter in TestCollectRespe…
euxaristia Jul 4, 2026
b6bc362
test(agent): align Windows shell prompt assertion with updated guidance
euxaristia Jul 4, 2026
2d0b14c
fix(imageinput): keep clipboard capture on powershell -Command
euxaristia Jul 4, 2026
940ee4b
refactor: slim PR to Windows shell guidance, drop split-out fixes
euxaristia Jul 4, 2026
49d0ebf
fix(tools): cover bare `| cat` and drop duplicate interactive-suggest…
euxaristia Jul 4, 2026
6e62c9f
fix(sandbox): make blocked-pager guidance platform-specific
euxaristia Jul 5, 2026
0e08481
fix(tools): make Windows POSIX-utility detection quote-aware, drop bl…
euxaristia Jul 5, 2026
e8eeda6
fix(tools): neutralize cmd.exe caret escapes in Windows shell heuristic
euxaristia Jul 5, 2026
b0c4765
fix(tools): treat cmd operators as boundaries for POSIX utility detec…
euxaristia Jul 5, 2026
a678d7c
Merge remote-tracking branch 'origin/main' into HEAD
euxaristia Jul 5, 2026
8624882
fix(sandbox): drop duplicate #414 pager-suggestion slice again
euxaristia Jul 5, 2026
a45fbfa
revert(tools): drop shell_runtime.go POSIX/MSYS detection, defer to #476
euxaristia Jul 5, 2026
712bf00
fix(agent,tools): fix broken quoting examples and extend guidance to …
euxaristia Jul 6, 2026
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
13 changes: 12 additions & 1 deletion internal/agent/loop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2907,9 +2907,20 @@ func TestBuildSystemPromptInjectsHostShellContext(t *testing.T) {
t.Fatalf("expected operating system in environment block, got %q", prompt)
}
if runtime.GOOS == "windows" {
if !strings.Contains(prompt, "Windows cmd.exe syntax") || !strings.Contains(prompt, "cwd argument") {
if !strings.Contains(prompt, "Windows cmd.exe syntax") || !strings.Contains(prompt, "use double quotes around the value") {
t.Fatalf("expected Windows cmd.exe shell guidance in prompt, got %q", prompt)
}
// The examples must themselves use the safe (double-quoted) form; a
// single-quoted example here would teach the model the exact syntax
// that fails under cmd.exe.
for _, want := range []string{`--jq ".a | b"`, `-run "A|B"`} {
if !strings.Contains(prompt, want) {
t.Fatalf("expected double-quoted example %q in Windows shell guidance, got %q", want, prompt)
}
}
if strings.Contains(prompt, `'.a | b'`) || strings.Contains(prompt, `'A|B'`) {
t.Fatalf("Windows shell guidance must not show the unsafe single-quoted form, got %q", prompt)
}
} else if !strings.Contains(prompt, "/bin/sh syntax") {
t.Fatalf("expected POSIX shell guidance in prompt, got %q", prompt)
}
Expand Down
2 changes: 1 addition & 1 deletion internal/agent/system_prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ func workspaceContext(cwd string) string {
b.WriteString("Working directory: " + cwd + "\n")
b.WriteString("Operating system: " + runtime.GOOS + "\n")
if runtime.GOOS == "windows" {
b.WriteString("Shell syntax: Windows cmd.exe syntax for exec_command/bash tools; prefer the workdir/cwd argument instead of cd when changing directories.\n")
b.WriteString("Shell syntax: Windows cmd.exe syntax for exec_command/bash tools. To put | & > < etc inside an arg value, use double quotes around the value, not single quotes (single quotes do not protect metachars in cmd.exe): gh --jq \".a | b\", go test -run \"A|B\". Avoid POSIX-only commands (head, tail, ls, cat, grep, ps, ...); prefer workdir/cwd over cd; prefer native tools (list_directory, read_file, grep, glob) over shell for file ops.\n")
} else {
b.WriteString("Shell syntax: /bin/sh syntax for exec_command/bash tools; prefer the workdir/cwd argument instead of cd when changing directories.\n")
}
Expand Down
8 changes: 7 additions & 1 deletion internal/imageinput/clipboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,13 @@ func readClipboardImageWindows() ([]byte, error) {
tmpPath := tmpFile.Name()
tmpFile.Close()
defer os.Remove(tmpPath)
script := `Add-Type -AssemblyName System.Windows.Forms; Add-Type -AssemblyName System.Drawing; $img = [System.Windows.Forms.Clipboard]::GetImage(); if ($img -ne $null) { $img.Save('` + tmpPath + `', [System.Drawing.Imaging.ImageFormat]::Png) }`

// Stay on -Command: -File is subject to script execution policy (default
// Restricted on Windows clients) and Windows PowerShell decodes BOM-less
// UTF-8 .ps1 files as ANSI. Doubling single quotes is all the escaping the
// single-quoted PowerShell literal needs.
escapedTmpPath := strings.ReplaceAll(tmpPath, "'", "''")
script := `Add-Type -AssemblyName System.Windows.Forms; Add-Type -AssemblyName System.Drawing; $img = [System.Windows.Forms.Clipboard]::GetImage(); if ($img -ne $null) { $img.Save('` + escapedTmpPath + `', [System.Drawing.Imaging.ImageFormat]::Png) }`
cmd := exec.Command("powershell", "-NoProfile", "-Command", script)
if err := cmd.Run(); err != nil {
return nil, nil
Expand Down
3 changes: 3 additions & 0 deletions internal/tools/bash_tool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ func TestBashToolDescribesHostShellSyntax(t *testing.T) {
if !strings.Contains(description, "cmd.exe") || !strings.Contains(description, "cwd") {
t.Fatalf("expected Windows cmd.exe and cwd guidance in bash description, got %q", description)
}
if !strings.Contains(description, "double quotes") || !strings.Contains(description, `--jq ".a | b"`) {
t.Fatalf("expected the double-quote metacharacter rule in bash description, got %q", description)
}
return
}
if !strings.Contains(description, "/bin/sh") {
Expand Down
9 changes: 5 additions & 4 deletions internal/tools/exec_command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -501,10 +501,11 @@ func TestCollectRespectsDeadlineUnderContinuousOutput(t *testing.T) {
elapsed := time.Since(start)

// Generous slack over `wait` for scheduling jitter under a continuously
// writing goroutine — this must stay a small multiple of wait, not
// "however long the writer keeps going" (which is what the bug produced:
// this test would hang past the 30s test timeout without the fix).
if elapsed > 3*wait {
// writing goroutine (worse on Windows CI under load). This must stay a
// small multiple of wait, not "however long the writer keeps going"
// (which is what the bug produced: this test would hang past the 30s
// test timeout without the fix).
if elapsed > 5*wait {
t.Fatalf("collect took %v under continuous output, want close to the %v deadline", elapsed, wait)
}
}
Expand Down
2 changes: 1 addition & 1 deletion internal/tools/shell_runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func detectShellRuntime(goos string) shellRuntime {
func shellGuidanceForGOOS(goos string) string {
runtime := detectShellRuntime(goos)
if goos == "windows" {
return "Uses " + runtime.Syntax + " syntax on Windows; prefer cwd over cd when changing directories."
return "Uses " + runtime.Syntax + " syntax on Windows; prefer cwd over cd when changing directories. To include | & > < or other metacharacters in an argument value, wrap the value in double quotes (e.g. --jq \".a | b\"); single quotes do not protect metacharacters in cmd.exe."
}
guidance := "Uses " + runtime.Syntax + " syntax."
if goos == "darwin" {
Expand Down