mech.app
Dev Tools

llm-coding-agent: What a 0.1 Alpha Built by Claude Reveals About Tool Design and Agent Approval Patterns

A coding agent built entirely by Claude exposes tool safety boundaries, approval workflows, and orchestration patterns through its five-tool suite and P...

Source: simonwillison.net
llm-coding-agent: What a 0.1 Alpha Built by Claude Reveals About Tool Design and Agent Approval Patterns

Simon Willison just shipped a 0.1 alpha of a coding agent built entirely by Claude Code (Fable). The project is interesting not because it works, but because it exposes the plumbing decisions an AI makes when designing an AI agent. The tool suite, approval mechanisms, and Python API surface reveal patterns you can reuse or avoid.

The Five-Tool Suite and Safety Boundaries

Claude implemented five tools with specific constraints that prevent common failure modes:

edit_file(path, old_string, new_string, replace_all=False)
Requires exact string matching, including whitespace. The old_string must identify a unique location unless replace_all is true. Returns a diff so the agent (or human) can verify the change. This prevents partial matches and ambiguous edits.

execute_command(command, timeout=120)
Runs shell commands with a 120-second default timeout (600s max). On timeout, the entire process tree is killed. Returns combined stdout/stderr plus an exit code line. This stops runaway processes and gives the agent structured feedback.

list_files(pattern=’/*’, path=’.’)**
Glob-based file listing that skips hidden directories, node_modules, __pycache__, and anything in .gitignore (if in a git repo). Returns at most 200 paths, sorted newest first. This prevents the agent from overwhelming itself with file lists.

read_file(path, offset=0, limit=2000)
Returns numbered lines like cat -n. Uses offset and limit for pagination. This forces the agent to handle large files in chunks rather than trying to load everything into context.

search_files(pattern, path=’.’, glob=None, max_results=100)
Regex search with optional glob filter (e.g., *.py). Returns path:line_number:line format, capped at 100 results. This keeps search results scannable and prevents context overflow.

Approval Workflow: —yolo vs. —allow Globs

The agent ships with three approval modes:

ModeBehaviorUse Case
DefaultPrompt for approval on every tool callSafe exploration, learning agent behavior
--yoloAuto-approve all tool callsTrusted environments, throwaway directories
--allow "pattern"Auto-approve commands matching glob patternsSelective trust (e.g., pytest*, git diff*)

The --allow flag is the interesting middle ground. You can run:

llm code --allow "pytest*" --allow "git diff*"

This lets the agent run tests and inspect diffs without approval, but still gates destructive operations. The glob matching happens at the command string level, so pytest tests/ matches pytest*, but rm -rf / does not.

The Unprompted Python API

Claude implemented a Python API without being asked:

from llm_coding_agent import CodingAgent

agent = CodingAgent(
    model="gpt-5.5",
    root="/path/to/project",
    approve=True  # or False for manual approval
)

result = agent.run("Fix the failing test in tests/test_parser.py")

This API shape suggests a few orchestration patterns:

  1. Stateless execution: Each .run() call is independent. No persistent conversation state between invocations.
  2. Root directory scoping: The root parameter enforces a safety boundary. All file operations are relative to this path.
  3. Approval as a constructor parameter: The human-in-the-loop decision is made at agent creation, not per-call. This simplifies batch operations.

The fact that Claude chose this API unprompted indicates it’s a natural interface for reusable agent patterns. You can wrap this in a CI job, a web service, or a Jupyter notebook without rethinking the approval flow each time.

Tool Design Trade-offs

The tool suite makes specific trade-offs between flexibility and safety:

edit_file’s exact-match requirement prevents the agent from making ambiguous edits, but it also means the agent must read the file first to get the exact string. This adds a tool call (and latency) to every edit operation.

execute_command’s timeout enforcement stops runaway processes, but it also means long-running builds or tests will fail. The 600-second max timeout is hardcoded, so you can’t run a 15-minute integration test suite without modifying the tool.

list_files’ 200-path limit prevents context overflow, but it also means the agent can’t see the full directory structure in large projects. The agent must use glob patterns to narrow the search, which requires it to know what it’s looking for.

read_file’s pagination forces the agent to handle large files in chunks, but it also means the agent must track offset state across multiple tool calls. This adds complexity to the agent’s internal logic.

search_files’ 100-result cap keeps output scannable, but it also means the agent might miss relevant matches if the search is too broad. The agent must refine its search pattern or use a more specific glob filter.

Real-World Test: SwiftUI CLI for ASCII Art Time

Willison tested the agent by asking it to build a SwiftUI CLI app for displaying the time in ASCII art. The agent correctly noted that “SwiftUI isn’t suitable for a true CLI” but built a working Swift app anyway. The app outputs block characters representing the current time when you run swift run AsciiTime.

This test reveals two things:

  1. The agent can reason about tool limitations (SwiftUI vs. CLI) but still deliver a working solution.
  2. The agent can handle multi-step workflows (create directory, write files, execute build commands) without explicit orchestration logic.

Failure Modes and Observability Gaps

The tool suite has no built-in observability. You can’t see:

  • How many tool calls the agent made
  • How long each tool call took
  • What the agent’s reasoning was between tool calls
  • Whether the agent retried failed operations

The CLI outputs tool calls and results to stdout, but there’s no structured logging or trace export. If you want to debug a failed agent run, you’re reading terminal output.

The approval workflow also has no audit trail. If you run --yolo, you can’t reconstruct what the agent did without reading the git history or file diffs.

Technical Verdict

Use this pattern when:

  • You need a coding agent with clear safety boundaries (exact-match edits, timeout enforcement, .gitignore awareness)
  • You want a simple approval workflow that supports selective trust (—allow globs)
  • You’re building a reusable agent interface and want a clean Python API shape to copy

Avoid this pattern when:

  • You need observability (structured logs, traces, retry counts)
  • You need to run long-running commands (the 600s timeout is hardcoded)
  • You need to handle large projects (the 200-path list limit will bite you)
  • You need an audit trail for compliance or debugging

The tool design is solid for a 0.1 alpha. The approval workflow is practical. The Python API is clean. The observability gaps are the biggest limitation. If you’re building a coding agent, this is a useful reference implementation for tool boundaries and approval patterns.