Claude Code's Most Powerful Tools Write No Code
Claude Code’s Most Powerful Tools Write No Code
Last week I was helping a colleague debug an issue. He’d used some agent to change an if condition in a Vue component. When I opened git diff, the entire file had been rewritten — imports reordered, single quotes flipped to double quotes, two unrelated comments deleted. The actually useful change was one line, but the diff was 87 lines. Reviewing it, you couldn’t tell what he’d really changed.
Later I used Claude Code on a similar condition myself. Same kind of task — its diff was only 3 lines: old condition removed, new condition added, done. Not a single character of the surrounding code was touched.
At first I put this down to model temperament — some models are cautious, others are bold. But after tearing apart the tool definitions of several mainstream coding agents (Pi, Codex, DSH, opencode), I realized this isn’t a temperament problem. It’s a tool design problem.
Hidden inside Claude Code’s toolset is a batch of tools that don’t write code. They don’t touch files and don’t run commands — what they do is constrain the agent’s own behavior. Some force the agent to stop and present a plan before acting. Some force it to reference the real old content before touching anything. Some put permission chains around a Shell that can do anything.
These are the tools that actually separate Claude Code from other agents.
Two kinds of tools: doers vs. managers
Grouped by function, Claude Code’s tool list (official reference, count varies by version) is just a pile of “read, write, execute”. But switch the lens — group them by what they act on — and a very different structure appears:
| Acts on code & environment | Acts on the agent itself | |
|---|---|---|
| Examples | Read, Grep, Edit, Bash | PlanMode, AskUserQuestion, Permission, Worktree |
| What it does | Lets the model get work done | Lets the model constrain its own behavior |
| Do other frameworks have it | All of them | Almost none |
That right-hand column is where Claude Code’s controllability actually comes from.
Pi has only 4 tools (read, bash, edit, write), all from the left column — give the model tools and let it work. opencode writes in its prompt “You MUST iterate and keep going until the problem is solved” — never give up, but there’s also no mechanism to “stop and think”. Claude Code’s approach is different: hand the model tools, and install brakes at the same time — some operations require reading before writing, some require human approval, and some it simply isn’t allowed to do.
Below are the three designs worth the most attention, each backed by a distinct judgment call.
PlanMode Isn’t a “Suggestion” — It’s a Circuit Breaker
EnterPlanMode does nothing at all. It doesn’t change code, read files, or run commands. It does exactly one thing: make the agent stop, switch to read-only mode, present a plan, and wait for approval before touching anything.
So why can’t you just write “plan first for complex tasks” in the prompt?
Because a prompt is advice; a tool is a mechanism.
The model may follow a prompt-level “please plan first” while its context is short. But as the context grows and the task gets messy, prompt-level constraints are the first thing it drops. Tool calls, by contrast, are explicit and interceptable by the harness — the model calls EnterPlanMode, and the harness switches state; until ExitPlanMode gets the user’s approval, write operations simply don’t execute.
sequenceDiagram
participant U as User
participant H as Harness
participant M as Model
U->>H: "Refactor this module"
H->>M: context
M->>H: EnterPlanMode
Note over H: 🔒 read-only mode
M->>H: Read / Grep / Glob
H-->>M: file contents
M->>H: ExitPlanMode + plan
H->>U: "Here's the plan. Run it?"
U->>H: Approve
Note over H: 🔓 write access restored
M->>H: Edit
Fundamentally, PlanMode is a circuit breaker — the agent decides when to flip the switch on, and the harness refuses to let current through until the conditions are met. This design leaves “when to be careful” to the model, and hands “how to enforce that care” to the harness.
Other coding-agent harnesses rarely do this. I compared the system prompts and tool definitions of several mainstream ones: Pi has 4 tools in a 154-line prompt and no concept of “planning” at all; Codex has 10 tools and no explicit planning tool either; opencode’s prompt says “You MUST iterate” and pushes the model to grind through, but nothing makes it stop and plan first. Making PlanMode a tool is something I’ve only seen Claude Code do.
Edit’s old_string Isn’t Find-and-Replace — It’s a Safety Mechanism
Edit’s interface is simple: give an old snippet and a new one, match and replace.
But think about it more carefully: this design isn’t solving “how to change a file”. It’s solving “how to stop the model from changing a file recklessly”.
An agent has two paths for changing code:
| Whole-file rewrite | Precise local replacement | |
|---|---|---|
| Approach | Model outputs a full new version of the file | Only supply the small snippet being changed |
| Risk | Changing one if may reorder imports, reformat code, drop comments | Scope is minimal by construction |
| diff | A huge blob, hard to review | Clean — you can see at a glance what changed |
| Prerequisite | No need to read old content precisely | Must first read the real old content |
Edit forces the second path. The model can’t generate new content out of thin air — it has to fill the actual old content into old_string, which means it must Read first. If the file has changed and old_string no longer matches, Edit fails loudly instead of silently overwriting.
This design welds the “read before write” cognitive flow into the tool’s interface contract. It doesn’t rely on model discipline; the interface enforces it.
You might ask: can’t Bash run sed? Why not change files with Bash?
You can — but Claude Code’s system prompt explicitly steers the model toward Edit first. Editing via Bash has no “you must know the old content before you change it” constraint like old_string. sed -i 's/old/new/' needs no context; it replaces whatever matches, whether or not that “old” is the “old” you think it is.
Edit’s old_string is essentially an optimistic lock: I only change what I actually read. If the world moved on (someone else changed the file), I stop.
The contrast with the other two is clear. Codex uses apply_patch — the model generates free-form patch text and the harness applies it. A patch can contain any change; the model doesn’t have to quote the real old content first — it can “describe” what to change instead of “reference” it. Pi uses edits[] arrays — one call can change multiple non-overlapping locations at once, which is more efficient, but also means a single mistake has a larger blast radius.
Three designs, three levels of trust in the model:
- Pi: trusts the model to get multiple locations right in one shot — gives it batch capability
- Codex: trusts the model to produce a correct patch — gives it free-form format
- Claude Code: doesn’t trust the model to conjure changes out of thin air — requires it to read first, then reference, then change
Bash: Unlimited Power, So It Must Be Limited
Bash is the most contradictory tool in Claude Code’s toolkit.
Without it the agent can’t even run tests — Git, npm, compilers, database clients: every bit of environment capability flows through Bash. But with it, the agent can theoretically do anything: curl to push data out, npm install to execute postinstall scripts, git commit to trigger pre-commit hooks.
Bash’s capability boundary is the entire operating system, but its security boundary has to be far smaller.
Harnesses handle this contradiction very differently. Pi’s Bash has only 2 parameters (command + timeout): no permission controls, no approval, no sandbox — full YOLO mode, trusting both the model and the user. DSH (DeepSeek Harness) swings to the other extreme: Bash has 7 parameters, and every call must declare sandbox_permissions (workspace-write or danger-full-access) plus a justification explaining why the command should run; if refused, it can escalate and retry.
Claude Code sits in the middle — not as freewheeling as Pi, not as heavy as DSH. Its defenses stack in three layers:
- Command grading: common read-only commands (
ls,git status,cat) pass automatically; write operations and unknown commands ask for confirmation - Execution-chain review: it doesn’t just look at the command itself, but also pipes, subcommands, aliases, and script calls —
git commitlooks read-only, yet its pre-commit hook may run arbitrary scripts - Sandbox as a backstop: in some deployment environments Bash runs inside a restricted sandbox
None of these defenses is perfect. A carefully crafted command can slip past static checks. Bash’s security ultimately rests on three things holding up together: model judgment + user vigilance + harness backstops. Remove any one and it fails.
This is Claude Code’s biggest trade-off on Bash: give the model an omnipotent tool, then use every other mechanism to limit when and how it’s used. Ship a restricted Edit plus an audited Bash — not an omnipotent FileEditor.
What Tool Design Can’t Fix
Reading this far, it’s easy to get the impression that good tool design equals a controllable agent.
It doesn’t.
Tool design solves structural constraints — which operations must read first, which must wait for approval, which are isolated from one another. It can’t solve the quality of the model’s decisions within those constraints.
Pitfalls I’ve actually hit:
- The model should have
Grepped for content but usedGlobfor filenames instead — and found nothing - The model skipped
Readand went straight toEdit;old_stringmismatched, so it read, then edited, going around in circles - Three subagents running in parallel produced three contradictory conclusions, and the main agent had no idea how to arbitrate
None of these is tool design’s fault — they sit at the boundary of model reasoning. Claude Code chose the “20+ tools + layered constraints” route: more flexibility for the model, but also more room for it to mess up.
The trade-off in one sentence: tool architecture sets the ceiling on controllability; model capability sets the floor. When the underlying model is strong, this architecture unlocks results far beyond what a simple toolset could; when the model fumbles at a decision point, no layering of good tools can catch it.
Appendix: Full Tool Quick Reference
| Layer | Tools | Role |
|---|---|---|
| Perception | Glob Grep Read LSP | Find and read by path / content / semantics; zero side effects |
| Action | Edit NotebookEdit Bash Monitor | Modify files, run commands, watch processes |
| Control | AskUserQuestion EnterPlanMode ExitPlanMode Skill | Constrain agent behavior: ask, plan approval, reuse workflows |
| Orchestration | Agent Task* TodoWrite TaskStop/Output | Subagent delegation, task state management |
| Isolation | EnterWorktree ExitWorktree | Git worktree isolation for parallel edits |
| Extension | MCP resource read/write, SendMessage ListAgents | External resources, agent-to-agent communication |
| Delivery | Artifact SendUserFile PushNotification | Publish artifacts, deliver files, send push notifications |
| Scheduling | Cron* ScheduleWakeup | In-session scheduling and wakeups |
| Other | ReportFindings SendFeedback RemoteTrigger ShareOnboardingGuide EndConversation | Review reports, feedback, remote routines, session control |