Introduction: Building a Claude Code from an Empty Loop
Chapter Goals
Explain what this project does, why it's worth building from scratch, and what you end up with — then get it running in five minutes.
From "Giving Suggestions" to "Taking Action"
AI-assisted programming has gone through three phases: code completion (Copilot), chat assistant (Cursor Chat), and autonomous Agent (Claude Code). The first two share one ceiling — the model can only suggest, not act. Ask it to fix a bug and it hands back a snippet, but it can't run the tests, see the error, and revise based on the result.
Claude Code crosses that line. Say "add user registration to this project" and it searches the routes, reads the database models, creates the handler, writes tests, runs npm test, sees the failure, revises, and runs again — a dozen rounds until the tests pass. It isn't completing code; it's carrying out a task.
What makes this possible isn't some elaborate magic. It's a loop:
while (true) {
call the model, get its reply
reply has a tool call? -> run the tool, feed the result back, continue
reply is just text? -> task done, exit
}In a traditional program, the next step is written in advance by the programmer with if/else. This loop flips that around: the model decides the next step, and the code only keeps the loop turning and hands the tools over. That single reversal is the entire difference between a coding agent and an ordinary chatbot.
Why Build from Scratch Instead of Reading the Source
The real Claude Code wraps that loop in half a million lines of TypeScript — 66 tools, a React/Ink terminal UI, the MCP protocol, OAuth, a multi-agent system. Reading through all of that, it's easy to drown in edge cases and abstraction layers and still not be able to say what the loop looks like.
This project goes the other way: it takes the loop out on its own and rebuilds it with the smallest amount of code, one piece at a time. The starting point is a dozen-line loop that can only chat; each chapter adds one capability, and every piece runs on its own. By the end it's an agent that reads code, edits files, runs tests, and loops until they pass — about 5,500 lines in TypeScript, about 5,000 in Python, the two mirroring each other. It's like understanding a car through a go-kart: engine, steering, brakes all there, air conditioning and stereo left out, but every important bolt tightened in plain sight.
What Each Chapter Builds
This table is the core build line. Each row is a chapter; the right side is what the agent newly learns to do by the end of it — from "only chats" all the way to "gets work done on its own":
| Chapter | After this chapter, the agent can… |
|---|---|
| 1. Agent Loop | call tools and feed results back to itself, instead of only chatting |
| 2. Tool System | read/write files, run Shell, search code — actually change the project |
| 3. System Prompt | know what OS, directory, and Git state it's working in |
| 4. CLI & Sessions | run an interactive command line; save conversations and --resume them |
| 5. Streaming Output | display as it generates, and talk to OpenAI-compatible models |
| 6. Permissions & Security | ask before dangerous actions; deny rules stop overreach |
| 7. Context Management | auto-compress long conversations; run dozens of turns without overflowing |
| 8. Memory System | remember preferences and project facts across sessions, recalled on demand |
| 9. Skills System | package common operations into reusable skills, invoked as needed |
| 10. Plan Mode | present a read-only plan first, act only once approved |
| 11. Multi-Agent | fork a sub-agent for a task too big for one context, bring the result back |
| 12. MCP Integration | connect external tool servers to extend the tool set |
| 13. Architecture Comparison | line up against the real Claude Code and see where the minimal build differs |
Two chapters sit beyond this line: Chapter 14 checks whether the agent really runs, with 22 manual scenarios; Chapter 15 adds the "autonomy" trio (/goal, /loop, Auto Mode), letting the agent keep pushing a task forward and judge permissions action by action.
What It Looks Like When Finished
Assembled, the pieces connect like this. No need to understand every box here; each later chapter adds one of them:
graph TB
User[User Input] --> CLI[cli.ts<br/>CLI Entry / REPL]
CLI --> Agent[agent.ts<br/>Agent Main Loop]
Agent --> Prompt[prompt.ts<br/>System Prompt]
Agent --> API{API Backend}
API -->|Anthropic| AnthropicSDK[Anthropic SDK]
API -->|OpenAI-compatible| OpenAISDK[OpenAI SDK]
Agent --> Tools[tools.ts<br/>Tool System]
Tools --> FS[File I/O]
Tools --> Shell[Shell Commands]
Tools --> Search[Search Tools]
Tools --> SkillTool[skill tool]
Tools --> WebFetch[web_fetch]
Agent --> SubAgent[subagent.ts<br/>Sub-Agent]
SubAgent -.->|fork-return| Agent
Agent --> Memory[memory.ts<br/>Memory System]
Prompt --> Memory
Prompt --> Skills[skills.ts<br/>Skills System]
Agent --> MCP[mcp.ts<br/>MCP Integration]
MCP --> ExtTools[External Tool Servers]
Agent --> Session[session.ts<br/>Session Management]
Agent --> UI[ui.ts<br/>Terminal UI]
style Agent fill:#7c5cfc,color:#fff
style Tools fill:#e8e0ff
style CLI fill:#e8e0ff
style Memory fill:#ffe0e0
style Skills fill:#ffe0e0
style SubAgent fill:#e0ffe0
style MCP fill:#e0f0ffThe main line in one sentence: input comes in, the CLI hands it to the Agent Loop, the model decides which tool to call, the code runs it and feeds the result back, and the loop turns until the model says "done." At the center, agent.ts is the engine (~2,169 lines) — message assembly, API calls, tool orchestration, context compression, and budget control all live here. The rest each mind one thing, none of them large:
| File | Lines | Responsibility |
|---|---|---|
agent.ts | ~2169 | Agent main loop: message construction, API calls, tool orchestration, streaming execution, sub-agents, 4-layer compression, budget, Plan Mode, autonomy trio |
tools.ts | ~884 | Tool definitions and execution: 12 resident tools, 6 permission modes, mtime protection, lazy loading (/loop dynamic additionally mounts schedule_wakeup temporarily) |
autonomy.ts | ~464 | Autonomy trio: /goal evaluator, /loop scheduling, Auto Mode classifier |
cli.ts | ~416 | CLI entry, argument parsing, REPL interaction |
memory.ts | ~392 | Memory system: 4 types, file storage, semantic recall, async prefetch |
mcp.ts | ~277 | MCP client: JSON-RPC over stdio, tool discovery and forwarding |
prompt.ts | ~253 | System Prompt construction: template, @include, variable substitution, memory/skill injection |
ui.ts | ~215 | Terminal output: color, formatting |
subagent.ts | ~199 | Sub-agent config: 3 built-in types + custom agent discovery |
skills.ts | ~175 | Skills system: directory discovery, frontmatter parsing, inline/fork modes |
session.ts | ~63 | Session persistence: JSON file storage |
frontmatter.ts | ~41 | YAML frontmatter parsing |
The Python version is a complete mirror of the same structure, in the python/mini_claude/ package, about 5,000 lines.
Technology Stack
The dependencies fit in one glance — no framework, no build toolchain.
Get It Running
Install, give it an API key, run it — five minutes.
On startup:
Mini Claude Code — A minimal coding agent
Type your request, or 'exit' to quit.
Commands: /clear /cost /compact /memory /skills /plan
>Try read src/agent.ts and explain the main loop and watch it read the file and explain on its own.
The command line has these switches too; each is covered later where its underlying feature is built:
mini-claude --yolo "run all tests" # skip all confirmations
mini-claude --plan "analyze this codebase" # analyze only, no changes
mini-claude --accept-edits "refactor" # auto-approve file edits
mini-claude --dont-ask "check style" # auto-deny actions needing confirmation
mini-claude --auto "fix the failing test" # Auto Mode: a classifier judges each action
mini-claude --thinking "analyze this bug" # enable Extended Thinking
mini-claude --resume # resume the last session
mini-claude --max-cost 0.50 --max-turns 20 # budget controlWhere to Find the Corresponding Source
Each chapter explains how the minimal version is built, and points to the matching location in the real Claude Code source, so you can compare after building:
| Chapter | mini-claude file | Claude Code source |
|---|---|---|
| Phase 1: Build a Working Coding Agent | ||
| 1. Agent Loop | chatAnthropic() in agent.ts | queryLoop in src/query.ts |
| 2. Tool System | tools.ts | src/Tool.ts + src/tools/ (66+ tools) |
| 3. System Prompt | prompt.ts | src/constants/prompts.ts |
| 4. CLI & Sessions | cli.ts + session.ts | src/entrypoints/cli.tsx |
| 5. Streaming Output | the two stream methods in agent.ts | src/services/api/claude.ts |
| 6. Permissions & Security | checkPermission() + rule config in tools.ts | src/utils/permissions/ (52KB) |
| 7. Context Management | checkAndCompact() in agent.ts | src/services/compact/ |
| Phase 2: Advanced Capabilities | ||
| 8. Memory System | memory.ts | src/utils/memory.ts |
| 9. Skills System | skills.ts | src/utils/skills.ts + src/tools/SkillTool/ |
| 10. Plan Mode | agent.ts + tools.ts + cli.ts | EnterPlanMode / ExitPlanMode |
| 11. Multi-Agent | subagent.ts + agent.ts | src/tools/AgentTool/ |
| 12. MCP Integration | mcp.ts | src/services/mcpClient.ts |
| 13. Architecture Comparison | global comparison | global comparison |
| Phase 3: Autonomous Operation | ||
| 15. Autonomy & Continuation | autonomy.ts | /goal · /loop · Auto Mode |
Next chapter: start from the dumbest loop — a
whilethat only chats — and turn it, step by step, into an agent that gets work done.