|
| 1 | +<div align="center"> |
| 2 | + |
1 | 3 | # TinyCode |
2 | 4 |
|
3 | | -**A minimal but complete Coding Agent Harness — built on [Pi](https://github.com/earendil-works/pi).** |
| 5 | +**A minimal but complete Coding Agent Harness — the readable way to learn how coding agents actually work.** |
4 | 6 |
|
5 | | -``` |
6 | | -TinyCode |
| 7 | +[](https://github.com/helsome/tinycode/actions/workflows/ci.yml) |
| 8 | +[](./LICENSE) |
| 9 | +[](./package.json) |
| 10 | +[](./tests) |
7 | 11 |
|
8 | | -Model + Agent Loop + Tools + Permissions + Session |
9 | | -+ Context + Skills + MCP + Sub-Agents + TUI |
10 | | -``` |
| 12 | +Built on [Pi](https://github.com/earendil-works/pi) · TypeScript · ESM · ~6k lines, every one meant to be read. |
11 | 13 |
|
12 | | -TinyCode is a learning project: read this repository and you can understand how a modern |
13 | | -coding agent (Claude Code, Codex CLI, Pi, …) actually works. Every module is small, |
14 | | -readable TypeScript with explicit boundaries. |
15 | | - |
16 | | -```bash |
17 | | -npm install |
18 | | -npm run build |
19 | | -npm run dev # full-screen terminal agent |
20 | | -``` |
21 | | - |
22 | | -Configure a provider with an environment variable (`ANTHROPIC_API_KEY`, |
23 | | -`OPENAI_API_KEY`, `GROQ_API_KEY`, …) or try it offline: |
24 | | - |
25 | | -```bash |
26 | | -TINYCODE_MODEL=mock npm run dev # scripted mock model, no network, no key |
27 | | -``` |
| 14 | +</div> |
28 | 15 |
|
29 | 16 | --- |
30 | 17 |
|
31 | | -## What is a Coding Agent? |
32 | | - |
33 | | -A coding agent is an LLM placed in a loop with tools: |
| 18 | +Most coding agents are products: hundreds of thousands of lines, closed or sprawling, impossible |
| 19 | +to hold in your head. TinyCode is the opposite — a **complete agent harness you can finish |
| 20 | +reading in an afternoon**, with every subsystem a production agent has: |
34 | 21 |
|
35 | 22 | ``` |
36 | | -User task |
37 | | - ↓ prompt |
38 | | -Agent Runtime (Pi) ⇄ Model (streaming LLM API) |
39 | | - ↓ "call read src/x.ts" ← the model decides which tool to use |
40 | | -Tool Execution (TinyCode) |
41 | | - ↓ file contents as tool result |
42 | | -Agent Runtime ⇄ Model ← loop until the model stops calling tools |
43 | | - ↓ |
44 | | -Final Answer |
| 23 | +Model + Agent Loop + Tools + Permissions + Session |
| 24 | ++ Context Engineering + Skills + MCP + Sub-Agents + TUI |
45 | 25 | ``` |
46 | 26 |
|
47 | | -The **model** reasons and decides; the **harness** (everything in `src/`) provides |
48 | | -tools, enforces rules, manages context, and renders the experience. Neither trusts |
49 | | -the other blindly: |
50 | | - |
51 | | -- The model cannot touch your machine — only registered tools run. |
52 | | -- Tools cannot run unchecked — every call passes the permission layer. |
53 | | -- Nothing is unbounded — tool output is truncated and old context is compacted. |
54 | | - |
55 | | -## Why Pi? |
56 | | - |
57 | | -[Pi](https://github.com/earendil-works/pi) provides the hard runtime parts so TinyCode |
58 | | -can focus on harness logic: |
59 | | - |
60 | | -| From Pi | What it does | |
61 | | -|---|---| |
62 | | -| `@earendil-works/pi-agent-core` | `Agent` class: the loop, tool-call dispatch, streaming events, abort | |
63 | | -| `@earendil-works/pi-ai` | Provider catalog (`builtinModels`), auth from env vars, typed streaming, schema validation | |
64 | | -| `@earendil-works/pi-tui` | Alt-screen terminal UI: diff rendering, editor, scroll view, overlays | |
65 | | - |
66 | | -What TinyCode builds itself: tools, permissions, sessions, context policy, skills, |
67 | | -MCP integration, sub-agents, TUI composition, configuration, CLI. See |
68 | | -[ARCHITECTURE.md](./ARCHITECTURE.md) for the exact split and how each piece works. |
| 27 | +It runs real tasks against real LLM providers — and because it is built on |
| 28 | +[Pi](https://github.com/earendil-works/pi)'s runtime (`pi-agent-core`, `pi-ai`, `pi-tui`), |
| 29 | +none of the code is scaffolding theater. The loop streams, tools execute, sessions persist. |
69 | 30 |
|
70 | | -## Feature tour |
| 31 | +## Demo |
71 | 32 |
|
72 | 33 | ```text |
73 | 34 | $ tinycode |
@@ -96,85 +57,129 @@ $ tinycode |
96 | 57 | └──────────────────────────────────────────────────────────┘ |
97 | 58 | ``` |
98 | 59 |
|
99 | | -- **Streaming** — model output renders token-by-token; tool calls show live status. |
100 | | -- **7 built-in tools** — `read`, `write`, `edit`, `bash`, `grep`, `find`, `ls`, plus |
101 | | - `load_skill` and sub-agent tools, all through one registry. |
102 | | -- **Permissions** — read-only work inside the project runs freely; writes, installs and |
103 | | - dangerous shell commands trigger a dialog (*Allow once / Always allow this pattern / Deny*). |
104 | | - `--permission-mode auto` approves asks automatically for CI. |
105 | | -- **Sessions** — JSONL persistence under `~/.tinycode/sessions`; resume with |
106 | | - `tinycode --continue` or `--session <id>`, or `/resume` inside the app. |
107 | | -- **Context engineering** — oversized tool results are truncated head+tail with the full |
108 | | - output saved as an artifact; above a token budget older turns are summarized into a |
109 | | - `<conversation-summary>` message (`/compact` to force it). |
110 | | -- **Project memory** — `TINY.md` in the repo root is injected into the system prompt |
111 | | - (`AGENTS.md`/`CLAUDE.md` are also honored). |
112 | | -- **Skills** — `.tinycode/skills/<name>/SKILL.md` folders with frontmatter; only names and |
113 | | - descriptions enter the prompt, bodies load on demand via `load_skill`. |
114 | | -- **MCP** — stdio servers configured in `.tinycode/config.json` connect at startup and their |
115 | | - tools merge into the same registry; `/mcp` shows status. |
116 | | -- **Sub-agents** — the root agent can spawn up to 3 read-only workers with independent |
117 | | - contexts (`spawn_agent`/`wait_agent`/`list_agents`/`close_agent`); the status bar shows |
118 | | - `SUB-AGENTS n/3 RUNNING`. |
119 | | -- **Slash commands** — `/help /new /clear /resume /sessions /model /skills /mcp /agents |
120 | | - /compact /status /exit`. |
| 60 | +## How it compares |
| 61 | + |
| 62 | +TinyCode does not compete with production agents on features — it competes on |
| 63 | +**being understandable**. If you have ever wanted to know what happens between your |
| 64 | +prompt and `rm -rf`, this table is for you. |
| 65 | + |
| 66 | +| Project | Language | Source | Positioning | Readable as a first agent? | |
| 67 | +|---|---|---|---|---| |
| 68 | +| **TinyCode** | TypeScript | MIT | Complete learning harness | ✅ ~6k lines, guided tour in README + ARCHITECTURE | |
| 69 | +| Claude Code | TypeScript* | ✗ proprietary | Production agent product | ✗ | |
| 70 | +| OpenAI Codex CLI | Rust | OSS | Production agent CLI | ⚠️ large | |
| 71 | +| OpenCode | TS + Go | OSS | Production agent IDE/CLI | ⚠️ multi-process | |
| 72 | +| Gemini CLI | TypeScript | Apache-2.0 | Production agent CLI | ⚠️ large | |
| 73 | +| Aider | Python | Apache-2.0 | AI pair programmer (git-centric) | ⚠️ different architecture | |
| 74 | +| pi coding-agent | TypeScript | OSS | Full-featured agent + SDK on Pi | ✅ great next step after TinyCode | |
| 75 | +| MiniCode | TypeScript | OSS | Educational mini agent | ✅ similar spirit | |
121 | 76 |
|
122 | | -## Non-interactive mode |
| 77 | +<sub>* Claude Code ships minified; internals are inferred from behavior.</sub> |
| 78 | + |
| 79 | +**What TinyCode has that most tutorials don't:** permissions with a real approval dialog, |
| 80 | +resumable JSONL sessions, context compaction, progressive-disclosure skills, live MCP |
| 81 | +integration, supervised read-only sub-agents — all wired to a streaming TUI, all tested |
| 82 | +offline against a scripted model. |
| 83 | + |
| 84 | +## Quick start |
| 85 | + |
| 86 | +```bash |
| 87 | +git clone https://github.com/helsome/tinycode.git |
| 88 | +cd tinycode && npm install && npm run build |
| 89 | +``` |
123 | 90 |
|
124 | 91 | ```bash |
125 | | -tinycode -p "describe this project" |
126 | | -tinycode -p "fix the failing test" --permission-mode auto --model anthropic/claude-sonnet-4 |
| 92 | +npm run dev # full-screen terminal agent (needs a provider key) |
| 93 | +ANTHROPIC_API_KEY=sk-… npm run dev # e.g. Anthropic — keys come from env only |
| 94 | +TINYCODE_MODEL=mock npm run dev # offline: scripted mock model, zero setup |
| 95 | +tinycode -p "describe this project" # one-shot non-interactive mode |
127 | 96 | ``` |
128 | 97 |
|
129 | | -Prints the final answer and exits — the same harness, no UI. |
| 98 | +Supported providers include Anthropic, OpenAI, Groq, DeepSeek, Mistral, OpenRouter, |
| 99 | +Google and [more](https://github.com/earendil-works/pi) — everything Pi's catalog covers. |
| 100 | + |
| 101 | +## Features |
| 102 | + |
| 103 | +- **7 built-in tools** — `read` (windowed, line-numbered), `write`, `edit` (exact-match with |
| 104 | + diff preview), `bash` (timeout, abort, output capping), `grep`, `find`, `ls` — plus |
| 105 | + `load_skill` and four sub-agent tools through one registry. |
| 106 | +- **Streaming TUI** — tokens render live; tool calls show `● bash npm test → ✓ exit 0 · 2.4s`; |
| 107 | + edits show `+12 -3` diffs. |
| 108 | +- **Permissions** — reads inside the project flow freely; writes, installs and dangerous shell |
| 109 | + commands open an approval dialog (*Allow once / Always allow this pattern / Deny*). |
| 110 | + A heuristic classifier routes `npm test` vs `rm -rf` vs `curl … | sh`. |
| 111 | +- **Sessions** — append-only JSONL under `~/.tinycode/sessions`; resume with `--continue`, |
| 112 | + `--session <id>` or `/resume`. Crash-tolerant by design. |
| 113 | +- **Context engineering** — oversized tool results truncate head+tail with full output saved |
| 114 | + as artifacts; past a token budget, old turns compact into a `<conversation-summary>` |
| 115 | + while recent messages stay verbatim. |
| 116 | +- **Project memory** — `TINY.md` in the repo root joins the system prompt |
| 117 | + (`AGENTS.md`/`CLAUDE.md` honored too). |
| 118 | +- **Skills** — `.tinycode/skills/<name>/SKILL.md`; only names/descriptions enter the prompt, |
| 119 | + bodies load on demand via `load_skill`. |
| 120 | +- **MCP** — stdio servers from `.tinycode/config.json` connect at startup; their tools merge |
| 121 | + into the same registry. One broken server never takes the app down. |
| 122 | +- **Sub-agents** — up to 3 read-only workers with independent contexts and aborts; |
| 123 | + `spawn_agent` / `wait_agent` / `list_agents` / `close_agent`. |
| 124 | +- **Slash commands** — `/help /new /clear /resume /sessions /model /skills /mcp /agents |
| 125 | + /compact /status /exit`. |
130 | 126 |
|
131 | 127 | ## Configuration |
132 | 128 |
|
133 | | -`.tinycode/config.json` (project) — all keys optional: |
| 129 | +`.tinycode/config.json` (all keys optional): |
134 | 130 |
|
135 | 131 | ```json |
136 | 132 | { |
137 | 133 | "provider": "anthropic", |
138 | 134 | "model": "claude-sonnet-4", |
139 | 135 | "permissionMode": "ask", |
140 | | - "context": { |
141 | | - "compactAboveTokens": 80000, |
142 | | - "keepRecentMessages": 12, |
143 | | - "maxToolResultChars": 30000 |
144 | | - }, |
| 136 | + "context": { "compactAboveTokens": 80000, "keepRecentMessages": 12 }, |
145 | 137 | "mcpServers": { |
146 | 138 | "example": { "command": "node", "args": ["server.js"] } |
147 | 139 | } |
148 | 140 | } |
149 | 141 | ``` |
150 | 142 |
|
151 | | -Environment: `ANTHROPIC_API_KEY` & co. (never put keys in files), `TINYCODE_MODEL` |
152 | | -(`provider/model` or `mock`), `TINYCODE_PERMISSION_MODE=ask|auto`, |
153 | | -`TINYCODE_HOME` (redirect data dir — used by the test suite). |
| 143 | +Environment: provider API keys, `TINYCODE_MODEL=provider/model` (or `mock`), |
| 144 | +`TINYCODE_PERMISSION_MODE=ask|auto`, `TINYCODE_HOME` (data-dir redirect used by tests). |
| 145 | + |
| 146 | +## Documentation |
| 147 | + |
| 148 | +| Doc | Contents | |
| 149 | +|---|---| |
| 150 | +| [ARCHITECTURE.md](./ARCHITECTURE.md) | The map: modules, data flow, what comes from Pi vs TinyCode | |
| 151 | +| `src/agent/runtime.ts` | Start here — how the Pi `Agent` gets its policies (~100 lines) | |
| 152 | +| `tests/harness.e2e.test.ts` | The whole story as one executable scenario | |
154 | 153 |
|
155 | 154 | ## Testing |
156 | 155 |
|
| 156 | +Fully offline — no API key needed, ever: |
| 157 | + |
157 | 158 | ```bash |
158 | | -npm test # 115 tests across 11 files |
159 | | -npm run typecheck |
160 | | -npm run lint |
161 | | -npm run build |
| 159 | +npm test # 115 tests: unit, integration, E2E harness, TUI, CLI smoke |
| 160 | +npm run typecheck # strict TypeScript |
| 161 | +npm run lint # eslint |
| 162 | +npm run build # tsc → dist/ |
162 | 163 | ``` |
163 | 164 |
|
164 | | -The suite runs fully offline. A scripted **mock model** drives the real agent loop: |
165 | | -the E2E test fixes a deliberately broken fixture project via |
166 | | -`bash → read → edit → bash → final` and asserts the fixture's tests pass. |
167 | | -An MCP integration test spawns a real stdio server from `fixtures/mock-mcp/`. |
| 165 | +The flagship test scripts a deterministic mock model that drives the **real** agent loop |
| 166 | +through `bash → read → edit → bash → final` to fix a deliberately broken fixture project, |
| 167 | +then asserts the fixture's tests pass and the session file is complete. An MCP integration |
| 168 | +test spawns a genuine stdio server process. CI runs the same gates on Node 22 and 24. |
| 169 | + |
| 170 | +## Contributing |
168 | 171 |
|
169 | | -## Learn more |
| 172 | +Issues and PRs welcome. The bar for new code is the one the project already sets: |
| 173 | +small modules, explicit boundaries, tests that run without network access. |
| 174 | +If a feature needs more than ~300 lines to explain, it probably belongs in a layer below. |
170 | 175 |
|
171 | | -Read in this order: |
| 176 | +## Acknowledgements |
172 | 177 |
|
173 | | -1. [ARCHITECTURE.md](./ARCHITECTURE.md) — the map: modules, data flow, design decisions. |
174 | | -2. `src/agent/runtime.ts` — how the Pi `Agent` gets wired to harness policies (~100 lines). |
175 | | -3. `src/tools/read.ts` … — each tool is one self-contained file. |
176 | | -4. `tests/harness.e2e.test.ts` — the whole story in one executable scenario. |
| 178 | +- [Pi](https://github.com/earendil-works/pi) by Earendil — runtime foundation and the best |
| 179 | + reference implementation of a modern coding agent. |
| 180 | +- [MiniCode](https://github.com/LiuMengxuan04/MiniCode) — inspiration for the |
| 181 | + *learn-by-building-a-harness* format. |
177 | 182 |
|
178 | 183 | ## License |
179 | 184 |
|
180 | | -MIT |
| 185 | +[MIT](./LICENSE) |
0 commit comments