ayjnt
v0.1 · Bun-first · for Cloudflare Workers

Write the agent.
ayjnt writes everything else.

Agent-first framework for Cloudflare. File-based routing, auto-generated wrangler config, typed inter-agent RPC, co-located React UIs, and MCP support — so you can ship Durable Object agents without ever opening wrangler.jsonc.

Quick start

One command scaffolds a project with a working agent. A second command starts a local Cloudflare worker you can hit from curl or the Agents client SDK.

  • New project with minimal or with-ui template
  • Generated wrangler config, migrations, and entry point
  • HMR-backed dev server that rebuilds on file-tree changes
terminal — zsh
agents/ — the whole API
agent.tsstate + methods
app.tsxReact UI
middleware.tsauth
agent.ts
agent.ts

The folder is the framework

The shape you draft in agents/ maps 1:1 to URLs, DO bindings, migrations, and the worker entrypoint. There is no config file to author — just files and folders.

agent.ts
One default-exported class per folder. Name + state + methods.
middleware.ts
Applies to descendant agents. Root → leaf chaining, Hono-style next().
app.tsx
Optional React UI. Gets a typed useAgent() bound to this folder.
(parens)
Route groups — share middleware without nesting the URL.

What you get

Six tools-in-one: routing, migrations, RPC, UI, MCP, dev loop. None of the glue. click any card for a visual deep-dive

Agents that call each other, typed.

getAgent<T> returns a typed DurableObject stub. Method autocomplete from the target class. Errors propagate across the RPC boundary as exceptions. Rename a method and both sides break at compile time.

  • native Workers RPC → no HTTP, no JSON round-trip
  • method autocomplete → from the target class
  • exceptions propagate → throw in callee, catch in caller
agents/orders/agent.ts ts
// agents/orders/agent.ts
import { Agent } from "agents";
import { getAgent } from "ayjnt/rpc";
import type InventoryAgent from "../inventory/agent.ts";

export default class OrdersAgent extends Agent<Env, State> {
  override async onRequest(request: Request): Promise<Response> {
    const { sku, qty } = await request.json();
    const inventory = await getAgent<InventoryAgent>(
      this.env.INVENTORY_AGENT,
      "main",
    );
    const remaining = await inventory.decrement(sku, qty);
    return Response.json({ ok: true, remaining });
  }
}

Under the hood

Every build is a pure function of your file tree. The pipeline is small and inspectable — peek at .ayjnt/dist/entry.ts any time.

Build pipeline

  1. agents/**/agent.ts
    source
  2. scan()
    op
  3. .ayjnt/migrations.json
    persistent
  4. diff + emit
    op
  5. .ayjnt/dist/entry.ts
    artifact
  6. wrangler deploy
    delivery

Every step is a pure function of its inputs. I/O lives at the edges (scan, readLockfile, writeLockfile). Everything in between is tested as pure data transforms — see src/codegen/.

Build these

Every example in the gallery has a scaffold command, the code you'd add to which file, an animated terminal walkthrough, and the deploy step.

Ready when you are.

One command scaffolds the project. One command ships it.