Saltar al contenido
Anuar Harb

Quillami Code

An open-source coding agent for the terminal, built from scratch on the raw Anthropic API with no orchestration framework.

Rol
Sole author. Design, implementation, tests, evals and release.
Periodo
2026
Stack
TypeScript, Node.js 22, Anthropic SDK, MiniMax (Anthropic-compatible), node:test, GitHub Actions
lines of TypeScript, one runtime dependency
~2,200
tools behind a permission gate, with per-turn undo
8

Este caso está disponible solo en inglés.

The problem

I wanted to understand what a coding agent harness actually does between a prompt and the model before relying on one: how tool calls are dispatched, when context gets compacted, what "undo" really restores. Writing one from scratch was the fastest way to find out.

The project started as Killami Code and was later renamed Quillami Code. The npm package is quillami-code.

Constraints

  • No orchestration framework. Only the official Anthropic SDK. Every behavior in the loop has to be code I wrote and can explain.
  • It runs on the user's machine. The agent edits real files and runs real shell commands, so consent and recovery are part of the design, not a feature added later.
  • Cost is visible. Every turn prints tokens and an estimated cost in USD.
  • Small enough to read in an afternoon. About 2,200 lines of TypeScript in src/, one runtime dependency.

Architecture

Quillami Code architecture The CLI sends each turn into an agent loop. The loop compacts history over 20k tokens, streams the model response, asks permission for risky tools, snapshots files for undo, runs one of eight tools and feeds the result back. Model routing picks Anthropic or MiniMax. Memory files, a usage ledger, and tests, evals and CI sit around the loop. Developer terminal prompt CLI src/index.ts Model routing Anthropic, MiniMax turn stream API Agent loop src/agent/loop.ts, up to 20 tool steps per turn compact over 20k tokens stream model response permission write, edit, bash checkpoint snapshot for /undo run tool 8 tools tool_result Memory QUILLAMI.md, AGENTS.md Usage ledger tokens and USD per turn Tests, evals, CI node:test, 4 evals, Actions
Architecture of Quillami Code. The CLI feeds each turn into an agent loop that compacts history, streams the model response, gates risky tools, snapshots files and runs tools until the model stops asking for them.

The CLI reads a prompt and hands it to runTurn. Inside, a loop runs up to 20 tool steps per turn:

  1. Compact the history if it passed about 20k tokens. The older prefix is summarized by the model and the most recent ~7k tokens are kept verbatim.
  2. Stream the model response to the terminal as it arrives, and record usage for the cost ledger.
  3. If the model asked for tools, pass each call through the permission gate.
  4. Snapshot any file that write or edit is about to touch, so /undo can restore the turn.
  5. Run the tool and send the result back to the model as a tool_result.

The loop ends when the model stops requesting tools.

Key decisions and trade-offs

A permission gate instead of a sandbox, for now. write, edit, bash and remember_user ask before running. The user can allow once, deny, or allow for the session. It's honest about what it is: consent, not isolation. Real sandboxing is on the roadmap.

const RISKY_TOOLS = new Set(["write", "edit", "bash", "remember_user"]);

export function createGate(ask: AskFn): PermissionGate {
  let allowSession = false;
  return {
    async authorize(name, input) {
      if (!RISKY_TOOLS.has(name) || allowSession) return true;
      const decision = await askDecision(ask);
      if (decision === "deny") return false;
      if (decision === "allow_session") allowSession = true;
      return true;
    },
  };
}

Denied tools become information, not errors. When the user says no, the model receives a tool result that says the action was denied and should not be retried unless asked. The turn continues instead of crashing.

Checkpoints per turn, not per file. /undo restores every file touched by write or edit in the last turn, and deletes files the turn created. It doesn't undo bash, and the docs say so.

Compaction that never breaks a tool call. The cut point for the verbatim tail walks back past any tool_result message, so a tool call is never separated from its result. If the summary request fails, the full history is kept and the turn goes on.

Model routing as configuration. Anthropic models and MiniMax (through its Anthropic-compatible endpoint) share one client. The default resolves from a flag, then environment variables, then a sensible fallback, and /model switches mid-session.

Two layers of memory. Repo memory lives in QUILLAMI.md or AGENTS.md. Global memory lives in ~/.quillami and is only written through a dedicated tool that also requires permission.

Evaluation

There are two layers, and they run in CI in that order.

Harness tests run without an API key: permissions, checkpoints, compaction, memory, model resolution, providers, tools and usage accounting.

Programmatic evals run the real agent against the real API in a temporary workspace, then check the files it left behind with code, not with a judge model.

EvalWhat it checks
rewrite-greetingChanges one value in a file and touches nothing else
create-sumCreates a file with exactly the requested function
fix-doubleFinds and fixes a bug in an existing function
respect-memoryFollows conventions written in the project memory file

GitHub Actions runs typecheck, tests and build on every push and pull request. The eval job runs after the harness job, with the API key stored as a repository secret.

Results

  • Published to npm as quillami-code, installable with npm install -g quillami-code.
  • About 2,200 lines of TypeScript in src/, one runtime dependency.
  • Eight tools: read, write, edit, bash, grep, glob, ls and remember_user.
  • Ten harness test files and four programmatic evals, all wired into CI.

What I'd do differently

I'd add prompt caching to the loop from day one. The system prompt and memory are resent on every step, which is exactly the case caching is built for.

I'd also treat the sandbox as part of the core instead of the roadmap. A permission prompt protects against surprises, not against a bad command the user approved.

Next

Firecracker-style sandboxing, an MCP client, prompt caching, subagents, retries on rate limits, persistent sessions and pass rates per model in the eval suite.