Building a TypeScript framework that agents can actually reason
The stack isn't novel: Hono for HTTP, Drizzle for ORM, Zod for validation, Inertia + React + Vite for the frontend. What's different is the convention layer. A feature is a route, controller, model, and view — authentication, queues, mail, scheduling, and policies are already wired before you write a line.
Controllers follow the shape you'd expect
import { Controller } from '@guren/core'
import { pages } from '@/.guren/pages.gen'
export class PostController extends Controller {
async show() {
const { id } = this.validateParams(PostIdParamSchema)
const post = await Post.findOrFail(id) // throws 404 automatically
return this.inertia(pages.posts.Show, { post })
}
async store() {
const data = await this.validateBody(CreatePostSchema) // 422 on failure
const user = await this.auth.userOrFail() // 401 if anonymous
await Post.create({ ...data, authorId: user.id })
return this.redirect('/posts')
}
}Batteries included in the Laravel sense: OAuth providers, queues and jobs, mail, events/listeners, cache, notifications, broadcasting, scheduling, storage, i18n, policies, console commands.
Types that cross the server/client boundary
Bind a Zod schema to a route and the frontend form derives its type automatically:
posts.post('/', { name: 'posts.store', body: PostPayloadSchema }, [PostController, 'store'])type PostFormData = RouteBody
const form = useForm({ title: '', body: '' })Add a field to the schema on the server and the form's type follows. Rename the route and route('posts.store') stops compiling. Page props are checked against the component's own Props interface — a misspelled prop is a typecheck failure, not a blank space on the page.
The evaluation that changed the roadmap
I ran a cost evaluation across frameworks to see what a feature actually costs an agent. Expected Guren to win because decisions are pre-made. First measurement: $5.54 median, 2.7× Hono's cost.
Stream analysis explained it. Agents spent 17–46% of all tool actions reverse-engineering @guren internals — reading generated types, jumping through convention files, reconstructing mental models that the framework should have made explicit.
That's the real problem. A framework designed for agents needs to be legible to agents, not just convenient for humans. The conventions that save me keystrokes were costing the agent thousands of tokens in exploration.
What I'm changing
- Generated
.genfiles now ship with JSDoc comments that explain the convention, not just the shape - Route registration emits a machine-readable manifest the agent can query directly
- Controller base classes expose their capabilities via static analysis hints
- Validation schemas are co-located with route definitions in a way the agent can trace without grep
The goal: an agent that opens the repo, reads the manifest, and writes correct code on the first pass — no 40-turn exploration loops.
Guren is at guren.dev and github.com/gurenjs/guren if you want to see the current shape. Still early, but the direction feels right: treat agent legibility as a first-class design constraint, not an afterthought.