Skip to content

pr-review.yaml's Console-mode bash chains break local runs on Windows #155

Description

@hooolio

pr-review.yaml's Console-mode bash chains break local runs on Windows

Summary

Since #150 expanded the Console-mode base-detection in review-pr/agents/pr-review.yaml, running the agent locally via docker agent run is broken on Windows hosts. cagent's shell tool invokes commands through Windows PowerShell 5.1, which rejects the bash syntax in the new git merge-base fallback chain. The agent thrashes through several PowerShell parser-error retries (typically 3 to 10 tool-call rounds, adding 1 to 3 minutes of wall time) before either self-correcting or giving up. End users see either silent failure or a partial review.

This isn't theoretical: docker/hivemind ships a wrapper skill (local-security-review) that runs pr-review.yaml locally to pre-flight pushes, and it stopped working for Windows users on or around April 25, 2026 (the day the upstream cache TTL picked up #150).

What changed

#150 expanded Step 1's Console-mode base-detection from a single git merge-base call to a seven-line fallback chain. The new block (current main):

BASE=$(git merge-base upstream/main HEAD 2>/dev/null \
  || git merge-base upstream/master HEAD 2>/dev/null \
  || git merge-base origin/HEAD HEAD 2>/dev/null \
  || git merge-base origin/main HEAD 2>/dev/null \
  || git merge-base origin/master HEAD 2>/dev/null \
  || git merge-base main HEAD 2>/dev/null \
  || git merge-base master HEAD 2>/dev/null)
if [ -z "$BASE" ]; then
  echo "ERROR: ..." >&2
  exit 1
fi

Every line in this snippet uses syntax PowerShell 5.1 cannot parse:

  • || and && are not statement separators
  • [ -z "$BASE" ] is not a valid PowerShell test
  • 2>/dev/null is not valid PowerShell redirection (2>$null is)
  • heredocs are not supported

Step 0 (echo $GITHUB_ACTIONS for mode detection) is more forgiving, since $GITHUB_ACTIONS happens to expand to empty in PowerShell when unset, but Step 1's chain is unrecoverable inside the agent's tool loop.

Reproduction

On Windows, with docker agent v1.42.0 (Docker Desktop 4.63+), inside Git Bash:

mkdir /tmp/cagent-probe && cd /tmp/cagent-probe
cat > probe.yaml <<'EOF'
version: "6"
models:
  sonnet:
    provider: anthropic
    model: claude-haiku-4-5
agents:
  root:
    model: sonnet
    instruction: |
      Run exactly: `[ -d /tmp ] && echo POSIX_OK || echo no_tmp`
    toolsets:
      - type: shell
EOF
ANTHROPIC_API_KEY=sk-ant-... \
  docker agent run --exec --yolo probe.yaml "go"

Result:

shell response → (
Error executing command: exit status 1
Output: At line:1 char:13
+ [ -d /tmp ] && echo POSIX_OK || echo no_tmp
+             ~~
The token '&&' is not a valid statement separator in this version.
)

The shell tool is hitting powershell.exe directly. bash -c "..." (with double quotes) does work because Git Bash is on PATH; that's the workaround docker/hivemind#253 uses for the local wrapper.

Impact

For local users on Windows, every Console-mode run pays a 3 to 10 round retry tax in the shell tool, adding 1 to 3 minutes of wall time before the agent either limps to a partial review or gives up. CI runs on Linux runners are unaffected. Mac users on bash/zsh are unaffected. So the bug only surfaces for the local-development path on Windows, which is exactly the path that #150 was supposed to keep working ("Console output mode").

Possible fixes

In rough order of cleanliness:

  1. Make cagent's shell tool prefer bash on Windows. When bash is on PATH (Git Bash or WSL), route shell-tool commands through it instead of powershell.exe. This fixes the entire class of bash-syntax issues without touching agent YAMLs. Most other agentcatalog YAMLs that use shell have similar issues, and a single point fix here would benefit all of them.

  2. Rewrite Step 1 to be cross-shell-compatible. Drop the || chain in favor of multiple discrete tool calls, each with select(.success) style logic in the prompt rather than shell. This is the most surgical fix but adds turns to every Console-mode run.

  3. Add a platform-detection prompt block to Step 0. Tell the agent up front that on Windows it must wrap any bash-syntax in bash -c "..." (with double quotes; the single-quote variant gets mangled by PowerShell's quote handling). This is a documentation-level fix; the tradeoff is a longer system prompt and a dependency on the agent following instructions.

Option 1 is what docker/hivemind#253 effectively simulates from the wrapper side: it pre-computes the values Step 1 would shell out for, pre-stages the diff, and tells the agent up front that any bash-syntax shell call must be wrapped in bash -c "...". The wrapper can do this because it controls the local-run prompt, but it can't fix the YAML for users who run pr-review.yaml directly.

Reference

  • Local wrapper that works around this: docker/hivemind#253 (merged 2026-04-27)
  • Initial breaking change: docker/cagent-action#150
  • cagent version: v1.42.0
  • Reported environment: Windows 11, Git Bash (MSYS2), Docker Desktop 4.63+

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions