What is the smallest coding agent that still counts as an agent? pi-from-scratch makes a strong claim: 767 lines of TypeScript with zero npm runtime dependencies — a runnable agent that reads files, edits code, and executes shell commands. It is a teaching project that deconstructs the pi agent's data flow into a minimal implementation you can read in one evening.
I read all five source files (agent, llm, tools, cli, tui). Here is what actually matters in them.
The "600-line" claim checks out. The five files total 767 lines; removing comments and blank lines leaves 616. And package.json's dependencies object is empty — it runs on Node built-ins only (fetch, fs, child_process, readline). The minimalism is real, not a headline.
The loop is one while(true) (src/agent.ts:80). Each round: optionally compact the context, stream the LLM while collecting text and tool_calls, append the assistant message, execute tools serially, append tool_result messages, repeat — until the model stops calling tools. No max_steps; the model decides when the task is done. When messages exceed 50, a summarizer compacts the older ones and keeps the last 20.
Three production traps are handled explicitly — and each is a failure mode you only hit when you have actually written an agent:
max_tokens truncation (agent.ts:125). When output is cut with
finish_reason: lengthand tool calls are pending, the arguments may be partial JSON. The code does not execute them. It writes an errortool_resultback into the context so the model can retry. Skip this branch and truncated tool calls execute with corrupt arguments.Abort consistency (agent.ts:107, 165). A dropped
tool_callstill needs its matchingtool_result— the OpenAI protocol requires a 1:1 correspondence, and a missing result breaks session resume. Every aborted call gets"error: aborted".Compaction failure (agent.ts:62). If summarization fails, the original context is kept untouched. An empty summary is worse than an overlong context.
The protocol layer (src/llm.ts) is where the API's quirks live: tool_call deltas accumulate by index with partial JSON arguments, flushed in order at stream end; finish_reason maps tool_calls → tool_use and length → max_tokens; assistant messages need non-null content or tool_calls (an empty string placeholder avoids HTTP 400); and tool_result blocks become separate role: "tool" messages. The context is plain JSON, which is why session persistence is only ~20 lines — append-only JSONL at ~/.nanopi/session.jsonl that tolerates corrupted lines on load (cli.ts:81).
The tools (src/tools.ts) are the minimal set: read_file, write_file, edit, run_bash. Output is truncated to the last 200 lines with the full output dumped to a temp file — error messages live at the end. edit requires a unique match and uses a function replacer so $ characters are not interpreted. run_bash has a 30-second timeout and 1MB buffer cap.
What 600 lines costs. The same source admits it: tool arguments are never validated, and run_bash executes any command with no approval gate (tools.ts:3-4, agent.ts:146). This is a teaching agent, not a safety baseline. The permission layer — approvals, sandboxing, argument validation — is exactly what separates this from your production harness, and that layer is absent here by design. Reading it against your own harness's permission model is the most productive exercise.
Who it is for / not for. It is for developers who want to understand the agent loop, tool-calling protocol, and context compaction, or who want a basis for writing a lightweight agent. It is not for anyone who needs a production-ready agent (no permission gate, no validation, no parallel tool execution), anyone who will not send an API key to a configurable base URL, or non-TypeScript stacks. The companion site (pi-from-scratch.vercel.app) ships pre-generated traces, so browsing it never calls a model API — a nice pattern for teaching costs.
Not tested / not run. I read the source; I did not install, build, or run the project, and I did not execute its test suite. The repository ships vitest tests for all five modules plus an e2e test. Star count (746) is from the GitHub API on 2026-08-13.
Sources: https://github.com/SaladDay/pi-from-scratch · https://github.com/SaladDay/pi-from-scratch/blob/main/src/agent.ts · https://github.com/SaladDay/pi-from-scratch/blob/main/src/llm.ts · https://github.com/SaladDay/pi-from-scratch/blob/main/src/tools.ts
Top comments (0)