How to Build a Coding Agent with Seven Core Tools

A Coding Agent autonomously reads, writes, and executes code using seven pure-Python tools—Read, Write, Edit, Grep, Glob, LS, and Bash—registered in a central registry and orchestrated through a planning loop that isolates the LLM from direct side effects.

The Coding Agent architecture from the bojieli/ai-agent-book repository demonstrates how to build a general-purpose autonomous agent that manipulates codebases safely. By isolating the inference engine from file system side effects through a curated toolbox, the design ensures robust, testable, and extensible automation. This guide explains how to implement the seven core tools that power this agent using the source code provided in Chapter 5.

Architectural Overview

The agent follows a deterministic loop that separates planning from execution. Three primary components manage this workflow: the inference engine (LLM), the Tool Registry, and the System State manager.

The Core Loop

The agent receives a user request, formats a system prompt listing available tools, and iterates through three steps: planning the next action, executing the selected tool, and observing the result. This cycle continues until the objective is satisfied or a stop condition triggers. The system prompt construction and loop orchestration are implemented in chapter5/coding-agent/main.py.

Tool Registry and System State

All available tools are registered in chapter5/coding-agent/tool_registry.py, which maps tool names like "read" or "edit" to concrete class implementations. The SystemState object, instantiated in the main execution flow and passed to each tool, tracks the current working directory, open shell sessions, and tool usage history to ensure reproducibility across iterations.

The Seven Core Tools

The agent's capabilities derive from seven pure-Python utilities defined in chapter5/coding-agent/tools/. These tools operate without external binary dependencies, enabling safe sandboxing in restricted environments.

ReadTool

Located in chapter5/coding-agent/tools/read_tool.py, the ReadTool handles file reading for plain text, images, PDFs, and Jupyter notebooks. It enforces a critical safety constraint: the agent must read a file before editing it, preventing blind modifications.

WriteTool

The WriteTool in chapter5/coding-agent/tools/write_tool.py creates new files or overwrites existing ones. It includes automatic lint checking to validate code quality immediately upon writing.

EditTool

Implemented in chapter5/coding-agent/tools/edit_tool.py, the EditTool performs search-and-replace edits. It strictly requires a prior ReadTool call on the target file, ensuring the agent operates with current context before modifying content.

GrepTool

The GrepTool in chapter5/coding-agent/tools/grep_tool.py provides pure-Python regular expression search across entire codebases without relying on external rg or grep binaries. This ensures portability and sandbox compatibility.

GlobTool

Located in chapter5/coding-agent/tools/glob_tool.py, the GlobTool performs fast, recursive file-pattern matching. It scales efficiently to handle codebases of any size without external dependencies.

LSTool

The LSTool in chapter5/coding-agent/tools/ls_tool.py lists directory contents, enabling the agent to discover available files and folder structures before attempting file operations.

BashTool

The BashTool in chapter5/coding-agent/tools/bash_tool.py executes arbitrary shell commands in persistent sessions. It allows the agent to run scripts, install dependencies, or query the operating system while maintaining session state across calls.

Implementing the Tool Interface

Each tool inherits from the BaseTool class defined in chapter5/coding-agent/tools/base.py. This abstract base handles argument validation, call counting, and JSON result formatting. The registry pattern in chapter5/coding-agent/tool_registry.py instantiates tools on demand, injecting the current SystemState to maintain context.

The tool execution flow follows this sequence:

  1. The LLM generates a JSON-encoded tool request containing the tool name and arguments.
  2. The registry retrieves the tool class and calls execute() with the provided arguments.
  3. The tool performs the operation and returns a structured dictionary with status, output, and optional metadata.
  4. The agent observes the result and feeds it back into the next planning iteration.

Programmatic Usage Example

To drive the agent programmatically, initialize the system state and registry, then execute tools by name. The following example demonstrates reading a file, editing it, and running a shell command:

from chapter5.coding_agent.system_state import SystemState
from chapter5.coding_agent.tool_registry import ToolRegistry

# Initialize state and registry

state = SystemState()
registry = ToolRegistry()

# 1. Read a file (required before editing)

read_tool = registry.get_tool("read", state)
result = read_tool.execute({"path": "chapter5/coding-agent/main.py"})
print(result["content"][:200])

# 2. Edit the file using search and replace

edit_tool = registry.get_tool("edit", state)
edit_result = edit_tool.execute({
    "path": "chapter5/coding-agent/main.py",
    "search": "def main(",
    "replace": "def main(args):"
})
print(edit_result["status"])

# 3. Execute a shell command in persistent session

bash_tool = registry.get_tool("bash", state)
bash_result = bash_tool.execute({"command": "pip install -r requirements.txt"})
print(bash_result["stdout"])

Safety Mechanisms and Validation

The architecture enforces safety through precondition checks implemented in individual tools. For example, EditTool validates that the target file exists in the read history stored in SystemState before applying changes. Each tool returns structured JSON containing status codes, output content, and metadata, allowing the LLM to interpret failures and adjust its plan without entering undefined states.

The pure-Python implementation of tools like GrepTool and GlobTool eliminates external binary dependencies, reducing attack surface and ensuring the agent operates correctly in sandboxed containers where system utilities may be unavailable.

Summary

  • The Coding Agent architecture isolates the LLM from side effects through seven pure-Python tools registered in chapter5/coding-agent/tool_registry.py.
  • ReadTool, WriteTool, EditTool, GrepTool, GlobTool, LSTool, and BashTool provide comprehensive filesystem and shell interaction capabilities.
  • The BaseTool class in chapter5/coding-agent/tools/base.py standardizes argument validation and result formatting across all implementations.
  • Safety constraints, such as requiring prior reads before edits, prevent blind modifications and ensure reproducible execution.
  • The agent operates through a planning loop that selects tools by name, executes them via the registry, and feeds structured observations back to the LLM.

Frequently Asked Questions

What programming language are the seven core tools written in?

The seven core tools are implemented in pure Python without external binary dependencies. This design choice, evident in files like chapter5/coding-agent/tools/grep_tool.py, enables safe sandboxing and ensures the agent runs in restricted environments without requiring system utilities like grep or ripgrep.

Why does the EditTool require a prior ReadTool call?

The EditTool enforces a read-before-edit policy to prevent the LLM from modifying files blindly. By checking the read history in the SystemState object, the tool ensures the agent has current context about the file's contents before attempting search-and-replace operations, reducing the risk of corrupted code or incorrect substitutions.

How does the Tool Registry manage tool execution?

The ToolRegistry in chapter5/coding-agent/tool_registry.py acts as a central dispatcher that maps string names (e.g., "bash", "grep") to tool classes. When the LLM requests a tool, the registry instantiates the appropriate class, injects the current SystemState, and routes the JSON arguments to the execute method defined in the BaseTool abstract class.

Can the BashTool maintain state between commands?

Yes, the BashTool implemented in chapter5/coding-agent/tools/bash_tool.py executes commands in persistent shell sessions. This allows the agent to change directories, set environment variables, or install packages in one call and reference those changes in subsequent commands, maintaining context across the agent's execution loop.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →