← all projects

HexCode — terminal AI coding agent

ADR-005 · TypeScript · Bun · React 19 · Hono · Prisma · Postgres · agents

The problem

A terminal-based AI coding assistant has an uncomfortable requirement at its center: to be useful it needs to read your source, edit files, and run shell commands. The naive architecture — ship the repo to the server, let the server execute tools — means every user's proprietary code and shell access transit a third party. For most teams that alone disqualifies the tool.

The second problem is trust in the agent itself. "Plan mode" that works by asking the model nicely not to edit files is not a safety property. It's a suggestion, and models under pressure route around suggestions.

Decision 1: schema-only tool contracts, client-side execution

The server defines tool contracts — names, JSON schemas, descriptions — and nothing else. It never holds an implementation. The CLI receives the contract, executes the tool locally against the filesystem, and returns only the result the model needs.

The consequence that matters: source code and shell access never leave the user's machine. The server sees tool results the user's own client chose to send, not the repository.

This also inverts the agent loop. Because execution is client-side, the CLI auto-continues the loop locally — call tool, feed result back, continue — rather than round-tripping control to the server on every step. Fewer hops, lower latency, and the server stays a stateless model proxy.

Execution is sandboxed to the project root. Path traversal outside the working directory is rejected at the boundary, not by convention.

Decision 2: permission modes at the contract layer

Plan and Build modes don't work by instructing the model. They work by changing which tools exist.

In Plan mode the model is handed a read-only toolset — read, search, list. The write, edit, and shell contracts are simply not in the exposed schema. The model cannot choose to edit a file for the same reason it cannot choose to send an email: no such tool was offered.

This is the difference between a policy and a guarantee. Prompt-level restrictions degrade with context length and adversarial input; a toolset that doesn't contain a write tool degrades not at all.

Decision 3: metered billing as an accounting problem

Token billing is deceptively easy to get wrong, because it's really double-entry accounting wearing a SaaS costume. The rules I settled on:

Auth is Clerk OAuth with PKCE; sessions and full tool-call history persist in Postgres via Prisma, which doubles as the audit trail for what the agent actually did.

Stack

TypeScript · Bun · React 19 · OpenTUI · Hono · Vercel AI SDK · Anthropic & OpenAI · Prisma · Postgres · Clerk · Polar · Zod

What I'd do differently

The tool contract schema and the client executor are versioned together implicitly — an older CLI against a newer server is a failure mode I handled defensively rather than structurally. A negotiated capability handshake at session start would be the right fix.