The 4 Tools That Let Claude Code Actually Modify and Run Your Code
The registry-and-interface pattern shown here is the same dispatch layer that production coding agents use to scale from one tool to dozens without rewriting the agent loop. Understanding it demystifies how tools like Claude Code stay extensible, and the path-safety and edit-uniqueness checks are exactly the kind of unglamorous guards that prevent an agent from silently corrupting a project.
A hand-built AI coding agent gains the ability to create files, make precise edits, and run terminal commands through four standardized tools: read_file, write_file, edit_file, and bash. Each tool implements a common interface that supplies a name, a model-facing definition, and an execute function, while a central Registry stores, looks up, and dispatches every tool by name. The Agent loop no longer hard-codes tool logic; it asks the Registry for definitions, sends them to the model, and delegates execution through a single call. File tools enforce path safety by resolving all paths against the project root and rejecting any that escape it. The edit_file tool refuses ambiguous modifications by counting occurrences of old_text and demanding a unique match before writing. The bash tool wraps Node's child_process with a timeout, output buffer limit, and working-directory lock, then feeds both stdout and stderr back to the model so it can see build or test failures. The result is a minimal but functional read-modify-run loop that mirrors the core architecture inside Claude Code and similar agents.
The edit_file uniqueness check is a design choice that prioritizes safety over convenience: it forces the model to provide enough context to identify a single location, rather than guessing and risking silent corruption.
Using bash -c as the execution model gives the agent the full power of shell composition (pipes, &&, etc.) but also inherits all the security risks of the current user's permissions, which the article correctly labels as a local-only teaching harness.
Separating file tools from bash is not redundant; structured parameters avoid shell-escaping bugs, and the separation enables finer-grained permission control later, such as allowing reads while blocking writes and command execution.
The split().length - 1 trick for counting substring occurrences is a zero-dependency alternative to regex matching, but it fails if old_text contains characters that have special meaning in a regex context, which is avoided here by using a plain string split.