跪拜 Guibai
← All articles
Frontend

A Minimal Claude Code Clone in 200 Lines

By 两只羊ovo ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

The mental model that a coding agent is just an LLM plus filesystem and shell access, orchestrated by a stateless loop, demystifies tools like Claude Code and Devin. Understanding this loop makes it practical to build custom agents for niche workflows without heavy frameworks.

Summary

The core of a Claude Code-style coding agent fits into roughly 200 lines of JavaScript. An LLM receives a task like "create a react+vite todolist," breaks it into steps, and decides which tool to invoke at each turn. The tool palette is deliberately minimal: write files and run shell commands. LangChain handles model abstraction and tool binding, while four message types (System, Human, AI, ToolMessage) maintain the conversation context across stateless LLM calls. The agent runs inside a `while(true)` loop that invokes the model, executes any requested tool calls in parallel via `Promise.all`, feeds results back as ToolMessages keyed by `tool_call_id`, and exits only when the model returns a response with no further tool calls. Five common pitfalls are flagged: forgetting to push AI responses back into the messages array, omitting `tool_call_id` on ToolMessages, missing loop exit protection, skipping try/catch on tool execution, and treating async functions as synchronous.

Takeaways
An LLM given a coding task first plans steps, then decides which tool to call for each step.
A minimal coding agent needs only two tools: write files (fs) and run shell commands (cli).
LangChain's `bindTools` switches the model into tool-use mode; without it, the LLM returns pseudocode instead of tool calls.
Four message types maintain agent context: SystemMessage, HumanMessage, AIMessage (with `tool_calls`), and ToolMessage (with `tool_call_id`).
LangChain elevates `tool_calls` to the top level of the response, unlike the native OpenAI SDK which buries them in `additional_kwargs.tools`.
The agent loop is a `while(true)` that invokes the model, runs requested tools in parallel, feeds results back, and breaks when no tool calls remain.
Tools should be stored as a name-to-function map so the LLM's `call.name` directly dispatches execution.
Five common bugs: not pushing responses back into messages, missing `tool_call_id`, infinite loops without a max-rounds guard, uncaught tool errors, and forgetting `await` on async calls.
Conclusions

LangChain's value proposition is not enabling new capabilities but reducing the friction of switching LLM providers and parsing tool-call responses.

The `tool_call_id` field is the linchpin of multi-tool parallelism: without it, the LLM cannot match results to the calls that produced them.

Wrapping tool execution in try/catch and feeding errors back to the LLM as structured results lets the model self-correct rather than crashing the agent.

The entire agent architecture is stateless at the model level; all continuity lives in the messages array that grows with each loop iteration.

Concepts & terms
Agent Loop
A while-true loop that repeatedly invokes an LLM, executes any tool calls it requests, feeds results back as ToolMessages, and exits when the model returns a response with no further tool calls.
tool_call_id
A unique identifier attached to each tool call in an AIMessage. The corresponding ToolMessage must carry the same id so the LLM can match execution results to the specific calls that requested them.
bindTools
A LangChain method that attaches tool definitions to a chat model instance, switching it from text-only mode into tool-use mode where it can return structured tool call requests instead of plain text.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗