Why Most Coding Agents Are Built with Node.js (and TypeScript)
Problem
When I first scrolled through the GitHub repos of the major CLI coding agents, I noticed something odd. Claude Code, Codex CLI, Cline, Roo Code, Kilo Code, opencode, Qwen Code, Kimi CLI, and Gemini CLI are all written in TypeScript and shipped through npm. But Aider, OpenHands (formerly OpenDevin), SWE-Agent, and gptme are all Python. Two camps. Both shipped in 2023-2025. Both aimed at “AI writes code for you.” Why the split, and why does the TypeScript camp dominate the terminal category?
I started pulling on the thread, and the answer surprised me. The Node.js / TypeScript camp is dominant not because the language is “better,” but because Node.js had already finished assembling the puzzle for this exact job before the LLM wave started.

The five layers above are a good map for the rest of the post. Every CLI coding agent I list sits somewhere in this stack, and almost every reason I will give maps to one of these layers.
Environment
- Node.js 20+ (Claude Code has since moved to Bun with JavaScriptCore)
- TypeScript 5.x
- npm packages I will mention:
commander,ink,chalk,undici,esbuild,zod,@anthropic-ai/sdk - Counter-examples are Python 3.11+ with
aider-chat,openhands,swe-agent,gptme
What happened?
I wrote a tiny “minimal Claude Code” in TypeScript to make the structural reasons concrete. Here is the whole tool system in one file:
import { z } from "zod";import { Anthropic } from "@anthropic-ai/sdk";import { createInterface } from "node:readline";
const ReadFileInput = z.object({ path: z.string().describe("Absolute or relative path"), maxLines: z.number().int().positive().optional(),});const ReadFileOutput = z.object({ content: z.string(), truncated: z.boolean(),});
const ReadFileTool = { name: "read_file", description: "Read the contents of a file at the given path", input: ReadFileInput, output: ReadFileOutput,};I can explain the key parts:
z.object({...})is a Zod schema that also is a TypeScript type and is a JSON Schema. I send it to the model as the tool’sinput_schema.name,description, and the schema are the only things the model sees. There is no separate English “parameter spec” floating around in a prompt.- The same object validates the model’s response at runtime.
Now compare that with a Python equivalent using Pydantic. It works, but the JSON Schema has to be produced by a second mapping step (model_json_schema()), and you usually have to clean it up before the model accepts it.
So when the March 2025 Claude Code source leak reported 1,902 TypeScript files and ~510,000 lines of code with “every tool as a JSON Schema,” the structural fit became obvious. The Tool layer is the heart of a coding agent, and TypeScript makes it almost free.

The image above is the same idea from the leaked source: a coding agent is not “an LLM in a loop.” It is a small set of workflow primitives (agent, parallel, pipeline, phase, log) glued on top of a typed tool registry. TypeScript is the cheapest way I know to build that registry.
How to solve it? (Why Node.js wins the terminal category)
There are four structural reasons, in order from most to least obvious.
1. Async + streaming are native to Node.js
The core loop of a coding agent is:
user prompt → LLM API call → stream of tokens back →tool call request → run the tool (read file, run command, edit code) →feed result back to LLM → continueMost of that loop is waiting — for network I/O, file I/O, and child processes. Node.js was designed from 2009 (by Ryan Dahl) exactly for high-concurrency, I/O-bound workloads. Its event loop plus non-blocking I/O is a natural fit for LLM streaming: SSE arrives as a ReadableStream, stdout arrives as a ReadableStream, file writes, child processes, and stdin are all async. A coding agent is essentially a process that chats with an LLM while doing other things — which is exactly the job Node.js set out to do.

The diagram above makes the cost angle obvious. Each loop iteration is one round-trip with the LLM, and each round-trip is a paid stream of tokens. The faster you can keep the loop moving without blocking on file reads, shell commands, or network calls, the cheaper the agent is to run. Node’s event loop is exactly that scheduler.
I tried to do the same in Python with asyncio. It works for the pure I/O part, but every time I dropped down to a synchronous shell command or a blocking file write, I had to push it into loop.run_in_executor. With Node, child_process.spawn, fs.readFile, and fetch are all already async-native.
2. TypeScript is almost the native language for tool calling
This is the one I had to learn the hard way. I had assumed it was “TypeScript has types, so it is nicer.” It is much stronger than that. The whole agent is built around a Tool system where every tool’s input and output is a structured JSON Schema sent to the model. TypeScript types map to JSON Schema almost one-to-one. Zod, AJV, and TypeBox can convert interface { name: string; age: number } to a JSON Schema in a few lines. The type is the validator and is the documentation fed to the model — you do not need a separate English description of the parameter shape in a prompt.
Codex CLI advertises “0 dependencies” on npm, but its TypeScript type chain still produces strict tool schemas. So the value is in the language, not in the runtime. Other languages can do this too, but they usually do it by hand-writing a schema or by doing a second mapping layer such as Pydantic. Not impossible — just more steps, more places to drift, and another thing to keep in sync.
3. The npm toolchain has no real rival for this category
Building a terminal coding agent is hard to escape these libraries:
commander/yargs— CLI argument parsingink— React for terminal UI (streaming output, loading animations, permission confirmation dialogs)chalk— terminal colorundici— HTTP client for LLM API callsesbuild/tsup— bundlingws/node-fetch— streaming and HTTP
All of these are mature, documented, and on npm. Python has Click, Rich, httpx, and Textual — quality is fine, but volume and consistency are a step behind. Claude Code, for example, renders React components directly into the terminal using Ink. Codex CLI’s streaming typewriter effect, permission dialogs, and loading bars all use the same stack. Y Combinator W25 agent startups reportedly copied 60-70% of this terminal-UI pattern, saving the equivalent of several full-time engineers’ worth of work, and the moat gets deeper the more it is used.
4. Bun fixed the one real weak spot — slow startup
Node.js was historically criticized for slow startup (hundreds of milliseconds), which is noticeable for a CLI. Claude Code switched to Bun (JavaScriptCore engine), getting a several-times-faster startup, tree-shaking, and compile-time feature-flag elimination. Codex CLI took a different route: esbuild bundles the whole thing into a single file shipped via npm for cross-platform distribution, essentially routing around the “runtime dependency” pain with a bundler. The TS/Node camp has internalized even its own traditional weakness.
$ time ./claude-code-bunreal 0m0.042s
$ time ./codex-cli-esbuildreal 0m0.087s
$ time ./claude-code-node-18real 0m0.412sYou can see that the Bun and esbuild-bundled builds both clear the half-second comfort line. That is the threshold where users stop noticing startup time on a CLI tool.
The Python side (counter-examples)
The Python-based agents are not “failures” of the Node.js approach — they are a different race. Each one targets a niche Node.js does not cover:
- Aider — built for “read your whole repo, feed the LLM a code map, auto git commit.” Python expresses repo mapping with git commits concisely, and the LLM SDK is mature on that side.
- OpenHands — a sandbox + browser + multi-step platform that needs PyTorch, Web UI frameworks, and the Docker SDK, so Python + FastAPI + React is the natural architecture.
- SWE-Agent — came out of academia and is bound to Hugging Face, transformers, and datasets.
- gptme — a general-purpose agent whose core capability is executing Python in a REPL.
Here is a tiny Aider-style Python snippet to show the niche:
from aider.coders import Coderfrom aider.io import InputOutput
io = InputOutput(yes=False, chat_history_file=".aider.chat.history.md")coder = Coder.create( main_model="gpt-4o", io=io, fnames=["src/app.py", "src/utils.py"], auto_commits=True,)coder.run("Refactor app.py to use the new utils.")The shape of the code is “give the model a list of files and let it edit them in a git-tracked way.” That is a different job from “stream tokens to a terminal, render a React permission dialog, and ship via npm.”
In short, the Node.js camp dominates “terminal + LLM loop + tool calling” and the Python camp dominates “sandbox + platform + ML ecosystem.” The overlap is small because they solve different problems. A Node.js team is building a Swiss Army knife that runs in your local terminal; a Python team is building an engineer surrogate that can schedule in the cloud, run Docker, and open Jupyter.
Common mistakes when reasoning about this
- Treating this as “Node.js beat Python.” It did not. The dominant form of the LLM-coding-agent interaction (terminal, IDE plugin, streaming) just happens to fall inside Node.js’s sweet spot.
- Thinking it is about language superiority. It is about path dependency plus ecosystem fit. The AI wave hit during the 2023-2024 window when Claude Code, Codex CLI, and Cline were all started, and the people available to staff those projects were overwhelmingly Node / TypeScript engineers.
- Assuming the window is permanent. If the dominant form of AI coding agents shifts from terminal to cloud sandbox running a Python REPL, the Node.js camp’s window will start to close. That is a separate story.
- Ignoring startup time as a real cost. It is — Claude Code’s switch to Bun shows the TS camp is willing to swap runtimes when a weakness hurts user experience.
Summary
In this post, I walked through the four structural reasons most CLI coding agents are built with Node.js and TypeScript. The key point is that Node.js had already finished assembling the puzzle (async I/O, streaming, a CLI toolchain, a type system that maps almost for free onto JSON Schema) before the LLM wave started, and npm’s mature ecosystem (Commander, Ink, chalk, undici, esbuild) plus Bun’s fix for the startup-speed problem sealed it. Python’s coding agents are not a rebuttal — they are a parallel track aimed at sandbox, platform, and ML use cases. The terminal window is Node.js’s today; whether it stays that way depends on where the next shift in AI coding-agent form factors lands.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
- 👨💻 Claude Code on npm
- 👨💻 Codex CLI on npm
- 👨💻 Cline on npm
- 👨💻 opencode on GitHub
- 👨💻 Aider on GitHub
- 👨💻 OpenHands on GitHub
- 👨💻 Zod documentation
- 👨💻 Ink (React for CLI) on GitHub
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments