If you have used coding agents, you may have noticed that they started to use Bash for tasks that would have traditionally used dedicated tools such as read, edit, and search. Well, models became superhuman in bash and might consider dedicated tools not good enough. Every few months, I rebuild an agent harness from scratch to learn what changed and what I can delete. This time I removed every model-facing tool except for a bash and a media viewer.
The outcome matched what I had already seen in daily use. Task completion stayed in the same range, while the agent moved through complex work and verifications with fewer tool boundaries.
Frontier models have become superhuman in Bash. I mean that in a narrow sense: they synthesize disposable command-line programs, in seconds, that most developers would need much longer to assemble and verify.
What “superhuman in Bash” means
You probably know the individual pieces git, rg, jq, Python, temporary files, process substitution, and test runners. Writing a one-off 40-line workflow that combines them under time pressure is different. Most of us work through the problem interactively, checking syntax and state between steps.
Coding agents assemble these workflows from patterns learned across POSIX utilities, programming languages, build systems, and public source code. They still make mistakes. The difference is how much correct orchestration they can attempt at once.
Bash is the routing layer here. The heavy work may happen in Python, Git, SQLite, a compiler, or a project-specific CLI. One shell interface lets the agent compose all of them without forcing the runtime designer to predict every useful operation.
The following are simplified examples from my personal agent traces.
Example 1: In-place multi-file edits You work on a project and you want to rename a variable across an implementation, its callers, and the tests.
The script first counts every old snippet. If a file has the wrong number of matches, it exits before writing anything. Only then does it apply the four-file rename in one pass, so you never sit in a half-updated tree. After the write it formats, lints, type-checks, runs the focused tests, and prints a compact diff. Failed checks stay in the working tree, so the next turn can inspect and fix them. That verify-before-done cycle is the inner loop.
Example 2: Reproducing and bisecting a flaky regression
You have a test that fails only sometimes. You want the first commit that introduced the flake, without moving your current checkout.
The extra worktree keeps your local files in place. Each commit is classified with five seeds, and it only counts as good if fewer than three runs fail, so one unlucky flake does not poison the bisect. When it lands, the script prints the first bad commit, a truncated diff of the suspected change, and the tail of the log. You get a diagnosis, not every test run in the prompt.
Example 3: Correlating compressed production logs
You have compressed production logs that are too large to load into the model. You want the failing endpoints, their error classes, and P95 latency.
Python reads the rotated files in batches and never dumps them into the prompt. SQLite does the join, the P95 ranking, and the error-class aggregation. What comes back is five JSON records: endpoint, failure count, average latency, P95, and error classes. Context off keeps the intermediate data in the environment, and puts only the summary in the model.
Why atomic tools were right
A few months ago, I argued that coding agents should start with robust atomic tools and avoid shell commands such as cat, sed, and echo. A careless command could flood the context window, return an opaque error, or corrupt an edit through bad quoting. Text output also cannot carry visual information into a vision model.
Those limits still apply to an uninstrumented shell. Foundational models are now much better at composing small Python patchers, Git commands, quoted heredocs, and focused tests. The Harness also absorbs the protections that made atomic tools useful:
Output control: truncate large results and tell the agent how to request a narrower slice.
Diagnostics: return the exit status, duration, timeout state, and process information.
Isolation and policy: gate paths, network access, and destructive actions outside the model.
Asynchronous processes: let the agent start, inspect, and stop long-running commands without blocking a turn.
The one exception multimodal input:
Text output cannot make a screenshot/image visible. A shell command can render a page, capture a chart, or save a video frame, but the pixels still need to enter the model through a multimodal channel.
What this means for harness engineering
I compared a shell-centered setup with a configuration that exposed separate tools for file reading, writing, editing, and search. Both ran against the same coding task set under the same conditions. The shell-centered setup achieved on par or better performance.
That is the Bitter Lesson applied to harness design: general methods that scale with computation beat hand-built shortcuts. Bash is that general computation layer.
A good agent should have more tools when they provide a better interface to a capability. If the model can do something better with a tool, it should use the tool. Browser control is a good example.
A browser tool can navigate, click, type, wait for the page to settle, and return a screenshot in the same call. Doing this through Bash means invoking a CLI, locating the captured artifact, and passing it through view_media in a second step. Service integrations can benefit in a similar way, or stay on the shell with something like mcp-cli so the schemas never sit in the prompt.
Here is what to try:
Delete the micro-tools: Try to use Bash to handle file reading, search, multi-file edits, diffs, and verification.
Use subagents as execution firewalls: Delegate messy exploration and debugging, then return a clean result to the parent context. Keep the system instructions minimal: Give the agent more room for repository instructions, domain knowledge, and the task itself.
Our goal is a smaller interface with a larger action space.