Summary
On Windows, the exec tool routes single-line commands to cmd.exe (via asyncio.create_subprocess_shell) but multi-line commands to powershell. This split is inconsistent and cmd.exe's semantics are unfriendly to agents authoring cross-platform commands:
cd across drives silently fails (cmd needs cd /d)
- POSIX-style expansion (
$VAR, $(...)) is not supported and stays literal
- The
shell parameter is hard-rejected on Windows, so there is no escape hatch
The result is that single-line and multi-line commands behave differently, and cross-drive cd / $VAR workflows silently fail. Agents are forced into workarounds (writing logic to a .sh/.ps1 file and executing that instead).
Environment
- OS: Windows
- Source:
nanobot/agent/tools/shell.py
Root cause (source analysis)
ExecTool._spawn chooses the shell purely on whether the command contains a newline:
# nanobot/agent/tools/shell.py — _spawn()
if _IS_WINDOWS:
if "\n" in command:
return await asyncio.create_subprocess_exec(
"powershell", "-NoProfile", "-Command", command, ...)
return await asyncio.create_subprocess_shell(command, ...) # → cmd.exe /c
- single-line →
asyncio.create_subprocess_shell → cmd.exe /c
- multi-line (contains
\n) → powershell -NoProfile -Command
The shell parameter is explicitly rejected on Windows:
# _resolve_shell()
if _IS_WINDOWS:
return None, "Error: shell parameter is not supported on Windows"
_build_env (Windows branch) forwards a curated set of system variables but omits OS, so even %OS% stays literal under cmd.exe — making it look like no shell expands variables at all.
Reproduction
On Windows, invoke the agent's exec tool:
cd "D:\some\dir" && pwd (cross-drive) → cd silently does not switch drives; pwd stays on the original drive. (cmd requires cd /d.)
echo $HOME → outputs the literal $HOME (cmd does not expand POSIX-style variables).
echo $(date) → literal (cmd has no command substitution).
- A multi-line version of the same operations behaves differently because it runs under PowerShell.
shell="powershell" → Error: shell parameter is not supported on Windows.
Impact
- Agents authoring cross-platform commands hit silent failures (especially cross-drive
cd).
- Behavior diverges based on a single
\n, which is hard to predict.
- No escape hatch via the
shell parameter, even when PowerShell (or Git-for-Windows bash / MSYS bash) is available on the system.
Suggested fix
A few non-exclusive options:
- Allow the
shell parameter on Windows (e.g. accept powershell, and/or bash when available).
- Route single-line commands through PowerShell too, to unify single-/multi-line semantics.
- If bash is detected (Git for Windows / MSYS), consider offering it as a selectable default.
Happy to open a PR if there's a preferred direction.
Summary
On Windows, the
exectool routes single-line commands tocmd.exe(viaasyncio.create_subprocess_shell) but multi-line commands topowershell. This split is inconsistent andcmd.exe's semantics are unfriendly to agents authoring cross-platform commands:cdacross drives silently fails (cmd needscd /d)$VAR,$(...)) is not supported and stays literalshellparameter is hard-rejected on Windows, so there is no escape hatchThe result is that single-line and multi-line commands behave differently, and cross-drive
cd/$VARworkflows silently fail. Agents are forced into workarounds (writing logic to a.sh/.ps1file and executing that instead).Environment
nanobot/agent/tools/shell.pyRoot cause (source analysis)
ExecTool._spawnchooses the shell purely on whether the command contains a newline:asyncio.create_subprocess_shell→cmd.exe /c\n) →powershell -NoProfile -CommandThe
shellparameter is explicitly rejected on Windows:_build_env(Windows branch) forwards a curated set of system variables but omitsOS, so even%OS%stays literal under cmd.exe — making it look like no shell expands variables at all.Reproduction
On Windows, invoke the agent's
exectool:cd "D:\some\dir" && pwd(cross-drive) →cdsilently does not switch drives;pwdstays on the original drive. (cmd requirescd /d.)echo $HOME→ outputs the literal$HOME(cmd does not expand POSIX-style variables).echo $(date)→ literal (cmd has no command substitution).shell="powershell"→Error: shell parameter is not supported on Windows.Impact
cd).\n, which is hard to predict.shellparameter, even when PowerShell (or Git-for-Windows bash / MSYS bash) is available on the system.Suggested fix
A few non-exclusive options:
shellparameter on Windows (e.g. acceptpowershell, and/orbashwhen available).Happy to open a PR if there's a preferred direction.