Bash for coding agents on native Windows. sill is a drop-in bash.exe: a Bash interpreter (a fork of brush) with a Windows adapter built for the way agents run commands. It needs no Git, MSYS2 or WSL. Point Claude Code, opencode or Codex at it, or add it to Codex CLI or Gemini CLI as an MCP server, and the agent's shell commands behave the way the model expects from Linux, on Windows' own programs and files.
An agent on Windows loses most of its failed commands at the boundary between the shell and Windows, not in its own reasoning:
- Arguments:
*and quotes get re-expanded or mangled on their way to.exeand.batprograms. - Paths:
/c/...andC:\...forms get mixed up. - Encoding: non-English text turns to mojibake.
- Console windows: one flashes up per command.
- Background servers: they hold the harness's output pipe until it times out.
- Missing tools:
make,rg,ps,lsofaren't there.
sill takes each of these on directly, with a test for each.
Measured on Windows with the Git Bash that ships with Git for Windows (and, for agents, PowerShell) as the comparison. Every suite, task and runner below is public in sill-bash-tests, with a workflow to rerun it.
Conformance suite: 85 cases of what agents rely on, each with the reason it matters, run through bash.exe -c and through the MCP server. sill passes 85/85 both ways; Git Bash passes 64.
| area | cases | sill | Git Bash |
|---|---|---|---|
| Encoding (non-English text through pipes, file names, git paths) | 7 | 7 | 2 |
Arguments to native programs (*, quotes, / switches, .bat) |
13 | 13 | 10 |
Tools agents use (rg, ps, pgrep, make, jq, ...) |
11 | 11 | 9 |
| Line endings and bytes | 5 | 5 | 3 |
| Size limits (long scripts, thousands of arguments) | 4 | 4 | 3 |
Programs that prompt (git commit with no message, vi, pagers) |
4 | 4 | 1 |
| Look-alikes (inputs sill's rules must leave alone) | 12 | 12 | 7 |
| Paths, processes, files, scripts, errors | 29 | 29 | 29 |
On a Chinese-locale laptop (code page 936), 0.4.0 passed the first 56 cases, Git Bash 39.
Real agents, fewer failed commands: 18 tasks with automatic checks (a Makefile project, an npm dev server started and stopped, Unicode paths, a .bat with % arguments, 4,000 files, an MSVC build), DeepSeek's low-cost model, two runs per task on the Chinese-locale laptop:
| harness | sill | Git Bash | PowerShell |
|---|---|---|---|
| Claude Code | 34/36 passed, 1 failed call of 120, 493 s | 35/36, 8 of 154, 637 s | 35/36, 9 of 141, 777 s |
| opencode | 35/36, 3 of 104, 403 s | 35/36, 18 of 149, 622 s | 29/36, 15 of 132, 904 s |
| Codex CLI | 36/36, 6 of 136, 634 s | 36/36, 22 of 172, 846 s | 35/36, 44 of 208, 1094 s |
For harnesses whose shell is PowerShell, sill's MCP server, added as it comes, on the same tasks:
| harness | with sill's MCP server | PowerShell only |
|---|---|---|
| Codex CLI | 36/36 passed, 10 failed calls of 119, 501 s | 35/36, 44 of 208, 1094 s |
| Gemini CLI | 36/36, 5 of 90, 335 s | 31/36, 5 of 150, 1195 s |
Given the choice, the models moved their shell work to sill: all of it under Gemini CLI, most of it under Codex.
Two public benchmarks, limited to the tasks that work on native Windows at all, with Claude Code and DeepSeek's model. On these, all three shells solve about as many tasks (the differences are within run-to-run variation); sill gets there with fewer failed shell calls.
Terminal-Bench 2.1, the 26 tasks whose reference solution runs on Windows (build software, process data, train and query models, crack ciphers, debug), two runs each:
| sill | Git Bash | PowerShell | |
|---|---|---|---|
| tasks solved | 45/52 | 48/52 | 45/52 |
| failed shell calls | 72 of 1,120 (6%) | 78 of 1,150 (7%) | 89 of 1,033 (9%) |
| turns | 1,100 | 1,302 | 1,429 |
| time | 6.0 h | 6.6 h | 6.3 h |
Run without a model, the tasks' reference solutions (correct bash scripts) pass in sill as in Git Bash: 26 against 25.
SWE-bench Verified, the 43 of 83 sampled tasks whose tests run on Windows (astropy, django, pytest, requests, sympy, ...), one run each:
| sill | Git Bash | PowerShell | |
|---|---|---|---|
| tasks solved | 38/43 | 35/43 | 38/43 |
| failed shell calls | 20 of 568 (4%) | 29 of 613 (5%) | 75 of 409 (18%) |
Runs that say nothing about the shell aren't counted: where the model's API failed, a task's environment didn't build, or the agent looked up the task's tests or solution (on the machine or online). The runner reruns just those.
Bash itself: brush's compatibility suite, with Git Bash as the reference, passes 2,435 of 2,933 runnable cases on Windows (83%). The rest are mostly Unix signals and job control (Windows has no equivalent), coproc, debugging options (extdebug, functrace), rarely used declare forms, and Git Bash's own differences, such as its /usr/bin paths.
With Scoop:
scoop bucket add sill https://github.com/lemonadesaltbagel/scoop-sill
scoop install sill/sill
This puts sill on PATH. The drop-in bash.exe is ~\scoop\apps\sill\current\bash.exe; see With an agent harness.
Or with the installer script:
-
Download
sill-windows-x64.zipandinstall-windows.ps1from the latest release. -
In PowerShell, run:
powershell -ExecutionPolicy Bypass -File install-windows.ps1 -Zip sill-windows-x64.zip -AddToPath -ClaudeCode
The installer unpacks to %LOCALAPPDATA%\Programs\sill. The options:
-
-AddToPathputs sill on your user PATH. -
-ClaudeCodepoints Claude Code's Bash tool at sill, in~\.claude\settings.json. Your other settings are kept, and the old file is saved assettings.json.bak. -
-Mcp codex,gemini,claudeadds sill's MCP server to any of these:- Codex CLI (
~\.codex\config.toml); - Gemini CLI (
~\.gemini\settings.json); - Claude Desktop (
%APPDATA%\Claude\claude_desktop_config.json).
Each is merged with what's there, and the old file is saved as
.bak. Approvals stay as you've set them in the client. - Codex CLI (
The installer changes nothing else. sill itself creates one folder: \tmp on the current drive, for /tmp (see Known limits).
| harness | how |
|---|---|
| Claude Code | CLAUDE_CODE_GIT_BASH_PATH = ...\sill\bash.exe (what -ClaudeCode sets) |
| opencode | SHELL = ...\sill\bash.exe |
| Codex CLI, Gemini CLI, Claude Desktop | As an MCP server: -Mcp codex,gemini,claude |
| any MCP client | Command ...\sill\bash.exe, arguments --mcp (stdio). Allow tool calls up to 11 minutes |
bash.exe --mcp gives the model three tools:
bashruns a command and returns its stdout, stderr and exit code. The working directory, exported variables, functions, aliases and options carry over to the next call (notset -e, and not unexported variables), however the command ends, its owntrap ... EXITincluded. A timeout (default 2 minutes, at most 10) ends the command and every program it started. Withrun_in_background, it starts a server or watcher and returns a job id at once.bash_outputreads a background job's new output.bash_killstops a background job and everything it started.
Each command runs in a fresh sill process, inside a Job Object it joins before it starts. So a crash or a hang can't take the server down, and a timeout or bash_kill ends every program the command started. Programs a command leaves running become a background job: the result says so, bash_output and bash_kill work on them, and they end with the session.
The first command runs in the project the client names (its MCP roots), if it offers one. Otherwise it runs where the client started the server, or in your home folder when that's the client's own install folder (as Claude Desktop does). SILL_MCP_ROOT sets it explicitly.
Output is read as UTF-8; a line that isn't is read in the system's ANSI code page (GBK on Chinese Windows), which some programs write into a pipe whatever the console's is. Java does so since JDK 18, and loses what that code page can't hold (Chinese comes out as ?? on English Windows), so the server sets JAVA_TOOL_OPTIONS=-Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 for its commands, and leaves out the Picked up JAVA_TOOL_OPTIONS note each JVM prints for it. A value of your own (even an empty one) is kept.
Running programs
- Exact arguments: command lines are encoded for each target, using the C runtime's rules for
.exeand cmd.exe's rules for.bat/.cmd.*and?arrive as typed. - Long argument lists: sill's own tools and the bundled GNU tools accept any number of arguments, past Windows' 32,767-character limit, through an args file. Other programs that hit the limit fail with "Argument list too long" and a note on what to use instead.
#!scripts run through their interpreter:./build.sh,./gradlew,#!/usr/bin/env python3.shis sill.cmd /c '... >nul': Claude Code rewrites>nulto>/dev/null; sill reads that back asnul.execruns the program and exits with its status, so wrapper scripts work.apply_patch, the file-editing command Codex's models are trained on, takes its patch from stdin or as an argument, and keeps each file's line endings.- Background jobs (
server > log 2>&1 &) don't hold the harness's output pipe.kill,pkillandkillallend whole process trees, but never the harness or the Windows session's processes.
Paths and text
- Paths:
$PWD,~,$HOMEand$TMPDIRuseC:/...form, and/c/...and/tmpwork wherever the shell opens a file. An argument to another program is rewritten only when the whole argument is a/c/...path to something that exists (python /d/work/solve.pygetsD:/work/solve.py); switches like/cor/s, a bare drive, a path that doesn't exist and--opt=/c/...reach the program as written. - No console windows: a shell started without a console, as Claude Code starts it, gets one invisible UTF-8 console. No window opens per command, and Windows' messages arrive readable.
- Error messages: system errors use POSIX wording ("No such file or directory"), not Windows' sentence in the user's language.
- git prints non-ASCII paths unescaped.
Bash behavior
trap ... EXIT INT TERM, process substitution (diff <(a) <(b)),read -t,ulimit,$OSTYPE=msys.$EUIDis a regular user's, even when elevated, as in Git Bash.
Package managers: npm, npx, yarn and pnpm run package.json scripts in sill, unless you've configured a script shell. Scripts that look written for cmd.exe get a one-line note.
Visual C++: msvc-env [arch] loads Visual Studio's C/C++ environment into the shell, cached: about 55 ms against 2 s for vcvars64.bat.
Bundled tools (all native Windows programs):
- Built into sill: the coreutils (uutils) and
find(uutils findutils, printing/paths, also for-exec). - GNU builds: grep, sed, diffutils and make, with a UTF-8 code page, so any file name works.
- Others: ripgrep, jq, and busybox-w32 for awk, xargs, patch, gzip and the rest.
- For Windows:
ps,pgrep,pkill,killall,kill,lsof(files and ports),file,cygpath,nohupandsetsid.
Defaults for agents:
- UTF-8 Python.
- No pagers, prompts or update notices.
- Telemetry off.
- English .NET and MSBuild messages.
A value you've already set is kept. All of these are visible with env.
| variable | effect |
|---|---|
SILL_NO_ENV_DEFAULTS=1 |
Turns the defaults above off, and English for sill's own messages too |
LANG, LC_ALL, LC_MESSAGES |
A language other than English keeps sill's own messages in it |
SILL_CONSOLE_CP |
The code page of the invisible console (default 65001, UTF-8) |
SILL_TRACE=<file> |
Appends each invocation's arguments and working directory to the file, one JSON line each |
/tmpis\tmpon the current drive, as Windows and Claude Code's own file tools read/tmp/x. sill creates the folder when it's missing. Mind three things:- It's a different folder on each drive: what was written to
/tmpwhile working onD:isn't there fromC:. - It's created at the drive's root and inherits that folder's permissions: on a machine several people use, the others can typically change files in it, as in a
/tmpwithout the sticky bit. - Use
$TMPDIR(your user's temporary folder) for anything private.
- It's a different folder on each drive: what was written to
- Windows has no signals. Traps for INT and TERM are accepted and listed, but only
EXIT,ERR,DEBUGandRETURNrun.killends processes. - Unsupported:
coproc, stopped jobs (fgafter Ctrl+Z), passing descriptors above 2 to a program,exec -a, andcp <(cmd) file. - Programs a finished command leaves running keep running (
start, an editor, a build daemon), as from a terminal. A command killed before it finishes, by a harness's timeout for one, takes everything it started with it. Through the MCP server, such leftovers become a background job of the session and end with it. - Java writes its console output in the ANSI code page when it goes to a pipe (JDK 18 and later), so through
bash.exe -cnon-ASCII text from Java arrives garbled (GBK on Chinese Windows) or as??(characters the code page can't hold). sill doesn't change Java's settings there, since the fix makes every JVM print a note on stderr, which breaks scripts that readjava -version 2>&1. To get UTF-8, setJAVA_TOOL_OPTIONS=-Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8in your environment. The MCP server does this for you. - Process substitution passes a named pipe (
\\.\pipe\...), which programs open like a file. - Elevation: when sill runs elevated, network drives mapped in your normal session aren't visible to it, as for any elevated program.
- No
sudo,aptorsystemctl: sill doesn't emulate Linux administration. These commands fail with a note naming the Windows way.
You need a Windows machine with Rust (the version in rust-toolchain.toml) and Visual Studio's C++ tools:
cargo build --release -p sill
This gives target\release\bash.exe. The bundled tools are cross-built on Linux with mingw-w64:
scripts/build-busybox.shscripts/build-gnu-tools.shscripts/fetch-tools.sh(pinned and checksummed)
scripts/package-windows.ps1 assembles the zip, and scripts/dist-check.sh checks it on a PATH with only Windows' own directories. .github/workflows/build.yml runs all of this, and publishes a release for a v* tag.
| path | what |
|---|---|
crates/sill |
The app: bash.exe and sill.exe, the console and environment setup, the MCP server (src/mcp.rs), and the tests that replay Claude Code's shell contract |
crates/sill-winargs |
argv to Windows command line, checked against spec/argv-test-vectors.json |
crates/sill-errno |
POSIX wording for Windows error codes |
third_party/brush |
The Bash interpreter, vendored; sill's changes are marked sill: |
third_party/findutils, third_party/uucore |
uutils crates, vendored for small changes, marked sill: |
third_party/gnu-tools, third_party/busybox-w32 |
What the GNU and busybox builds add: a UTF-8 manifest, the args-file shim, a sed patch |
scripts/ |
Building the tools, packaging, installing, and checking the distribution |
See CHANGELOG.md for what each release changed.