Skip to content

AI-First Development

WebJs is designed from the ground up to be the framework AI agents can read, write, and ship. Every architectural decision (from the file layout to the naming conventions to the one-function-per-file rule) was made with one question in mind: can an LLM understand this without loading the entire codebase into context?

Why AI-First Matters

Modern AI coding assistants (Claude Code, GitHub Copilot, Cursor, Antigravity, etc.) are increasingly writing production code. But most frameworks were designed for humans who hold the whole project in their head. They rely on:

  • Implicit conventions: "you just know" where to put things.
  • Barrel files: re-exports that hide the real location of code.
  • Magic config: next.config.js, vite.config.ts, webpack aliases.
  • Build-time transforms: what the source says isn't what runs.
  • Scattered state: a single feature touches 5 files across 3 directories with no discoverable link.

These work fine for experienced developers who've memorised the conventions. They're hostile to AI agents that need to discover structure from the files themselves.

How WebJs Solves This

1. AGENTS.md: The Machine-Readable Contract

Every WebJs app has an AGENTS.md at the root. This is a structured document that AI agents read before touching any code. It contains:

  • File conventions table: which filename means what (page.ts, route.ts, middleware.ts, .server.ts, etc.).
  • Public API surface: every exported function from webjs and @webjsdev/server with a one-line description.
  • Invariants: rules that must never be broken ("never import node:sqlite from a component", "event holes must be unquoted").
  • Recipes: step-by-step instructions for "add a page", "add a server action", "add a component", "add a DB model".
  • What's deliberately deferred, so agents don't try to implement features that aren't supported yet.

An AI agent reads AGENTS.md once and knows: the shape of the app, what's safe to change, what's not, and how to add any feature. No guessing.

2. The WebJs MCP Server

Alongside the static AGENTS.md contract, every WebJs app ships a read-only Model Context Protocol server agents can wire up for live, version-accurate introspection of THIS app and the framework. It mutates nothing, so it is safe to call freely, and it is preferred over recalling WebJs from training data (which drifts). It is already available with no install (the WebJs CLI, a project dependency, has it built in as webjs mcp, and the equivalent standalone package runs as npx @webjsdev/mcp). It is an MCP STDIO server, so you do not run it in a terminal and read its output. Your MCP host (Claude Code, Cursor, etc.) launches it and surfaces its tools.

Claude Code is pre-wired by the scaffold. For another host, register the server by pointing it at the package:

// .cursor/mcp.json (or your host's MCP config)
{
  "mcpServers": {
    "webjs": {
      "command": "npx",
      "args": ["@webjsdev/mcp"]
    }
  }
}

Introspection of this app (read-only, no module load, no DB side effects):

  • list_routes: the live route table.
  • list_actions: server actions with their /__webjs/action/<hash>/<fn> RPC endpoints (the real hashes).
  • list_components: the registered custom-element tags.
  • check: the structured webjs check violations.
  • ui: the @webjsdev/ui kit inventory, or one component's helper signatures, paste-ready example, and a11y header.

Use these to learn the real route, action, and component surface before editing, instead of grepping or assuming.

Knowledge and authoring layer: an init tool (a read-first pointer with the mental model and invariants, the primer for a fresh setup), a docs tool (retrieve a topic or search the .agents/skills/webjs/ reference corpus), and a source tool that reads the framework's OWN no-build source from node_modules/@webjsdev/*/src (what actually runs). On top of the tools, the server exposes MCP resources (the docs corpus plus this app's AGENTS.md) and recipe prompts (guided page, route, action, and component workflows), so the no-build framework source and the docs/recipes are surfaced directly to the agent.

An agent has two complementary ways to understand the framework, and either beats guessing from training data: grep the full no-build source under node_modules/@webjsdev/*/src (the real code that runs, no sourcemaps), and the MCP for live app introspection plus the curated init / docs / source knowledge tools.

3. Predictable File Layout

app/
  page.ts              → always the page component for this URL
  layout.ts            → always the wrapping layout
  route.ts             → always the HTTP handler
  middleware.ts         → always the request interceptor
  error.ts             → always the error boundary
  not-found.ts         → always the 404 page
modules/
  <feature>/
    actions/           → mutations, one file per function
    queries/           → reads, one file per function
    components/        → feature-owned UI
    utils/             → pure helpers
    types.ts           → shared type definitions

Every file has one job. An AI agent looking for "the function that creates a post" searches modules/posts/actions/, not a 500-line utils.ts or a re-exported barrel index. One grep, one result.

4. One Function Per File

Server actions and queries follow a strict one exported function per file convention:

modules/posts/actions/create-post.server.ts   → exports createPost()
modules/posts/actions/delete-post.server.ts   → exports deletePost()
modules/posts/queries/list-posts.server.ts    → exports listPosts()
modules/posts/queries/get-post.server.ts      → exports getPost()

This is the single most AI-friendly decision in the architecture. When an LLM needs to modify createPost, it reads exactly one file. It doesn't need to understand the rest of the module. Context window usage is minimal. The blast radius of a change is visible from the filename.

5. No Build Step = What You See Is What Runs

Frameworks with build pipelines transform source code before it executes. The JSX you write becomes React.createElement calls. Your imports become webpack chunks. Your CSS modules get hashed classnames. An AI agent reading the source sees one thing, while the runtime does another.

WebJs has no build step you run. The .ts file you see is the file that runs. .ts imports are stripped of types by the runtime's stripper (Node 24+'s built-in module.stripTypeScriptTypes, or amaro on Bun), which is whitespace replacement: every (line, column) in the source maps to the same position in the output, so stack traces stay byte-exact. The same transform runs server-side and on browser-bound requests. No intermediate representation, no generated code, no output directory. An AI agent can reason about what the code does by reading the file, because the file IS what runs. See No-Build Model for the full pipeline.

6. Explicit Server Boundary

The .server.ts extension is a visible, greppable marker that says "this code runs only on the server." An AI agent never accidentally puts a database call in a component, because the naming convention prevents it. And the framework enforces it: .server.ts files are rewritten to RPC stubs for the browser.

Compare with NextJs where 'use client' / 'use server' directives are easy to forget and their scope rules are subtle. The .server.ts convention is filename-level. You can't accidentally import server code without the filename literally telling you.

7. Typed RPC Without Schema

When an AI agent writes a server action:

// modules/posts/actions/create-post.server.ts
export async function createPost(
  input: { title: string; body: string }
): Promise<ActionResult<PostFormatted>> { ... }

The TypeScript signature IS the API contract. No separate schema file, no OpenAPI spec, no GraphQL SDL. The client component imports the function, TypeScript checks the types, and webjs's built-in serializer preserves them on the wire. An AI agent can:

  1. Read the function signature to understand the API.
  2. Modify the function and know every call site that breaks (via tsc).
  3. Add a new action by copying the pattern from an existing one.

Zero indirection. Zero codegen. Zero schema drift.

8. JSDoc or TypeScript: Agent's Choice

Some AI agents work better with TypeScript, others prefer JSDoc. WebJs supports both equally. The type-checking story is identical either way, since the TS language server reads both. An agent can generate whichever format it's more fluent in.

9. Cross-Agent Config, One Source

The scaffold ships a single cross-agent source of truth rather than a per-tool rule duplicate for each agent:

  • AGENTS.md, read natively by Cursor, opencode, Antigravity, and the Copilot coding agent
  • .agents/skills/webjs/SKILL.md, the shipped routing skill carrying the framework context
  • .agents/rules/workflow.md, the git, test, and review workflow rules
  • Thin bridges for tools that do not read AGENTS.md natively: CLAUDE.md (Claude Code), GEMINI.md (Gemini CLI), and .github/copilot-instructions.md (Copilot in VS Code), each pointing at AGENTS.md

Every agent gets the same rules from that one source: check the branch before coding, sync with parent before starting, auto-generate tests, auto-update docs, ask before merging (with delete/keep prompt), no AI attribution in commits.

10. Autonomous Mode

In sandbox or bypass-permissions mode, agents auto-decide using best-practice defaults: create feature branches, rebase before starting, fix failing tests, generate meaningful commits, delete feature branches after merge. Same quality bar, no blocking on questions.

11. Automatic Tests and Docs

In a WebJs project, the user never has to say "also write tests" or "also update the docs." Agents do this automatically with every code change. The convention lives in AGENTS.md and the shipped skill (.agents/skills/webjs/SKILL.md), and is enforced via webjs test, and webjs check. For an agent lint-and-fix loop, run webjs check --json: the structured output lets the agent parse violations, fix them, and re-run until the report is clean (the same correctness data the MCP check tool returns).

12. Scaffold + Persistence Defaults

When a layman user says "create a todo app with webjs", the agent should produce a real full-stack app with a real database, not a JSON-file simulation. WebJs enforces this with three guardrails:

  • Exactly two scaffolds. webjs create <name> (full-stack default) and --template api. The CLI rejects any other --template value, so an agent can't hallucinate --template todo or --template blog. Auth is one of the full-stack gallery cards, not a separate template.
  • Drizzle + SQLite wired up by default. Every scaffold ships db/schema.server.ts, db/columns.server.ts, db/connection.server.ts (exports db), the webjs.dev.before + webjs.start.before steps running webjs db migrate (idempotent, so a db:generate'd migration applies on the next boot), and npm run db:generate / db:migrate / db:studio / db:seed. The agent edits the schema, runs db:generate, and npm run dev applies it, and won't accidentally fall back to JSON files for persistence.
  • Persist with Drizzle, not JSON files. A data/todos.json or db.json used as a database resets on reload and cannot scale. This is a project convention in AGENTS.md and the shipped skill, so an agent reading it takes the database path.

Picking the right scaffold from the user's prompt:

User asks for…                                          Scaffold
─────────────────────────────────────────────────────────────────────
Todo app, blog, notes, dashboard, marketplace, social   default
App with auth / login / signup / accounts (SaaS)        default (auth card)
HTTP/JSON API only, no UI                                --template api

The scaffold is REFERENCE, not the final product. The agent's job after scaffolding is to replace the example app/page.ts ("Hello from …"), the example User model in db/schema.server.ts, and the example components with the app the user actually requested. The infrastructure (Drizzle wiring, test config, agent rules, route conventions) stays.

When the scaffolded AGENTS.md doesn't cover what you need (an obscure directive, an auth-provider recipe, deployment specifics, edge cases), the full hosted documentation is at the rest of these docs. Every API, every recipe, every example lives there. Reach for it before guessing or hand-rolling.

What an AI Agent Can Do with webjs

Given a WebJs app + AGENTS.md, an AI coding assistant can:

  • Add a new page: create app/about/page.ts, export a function returning html`...`. Done. No router config.
  • Add a new API endpoint: create app/api/users/route.ts, export GET / POST. Done. No Express boilerplate.
  • Add a server action: create modules/foo/actions/bar.server.ts, export an async function. Import it from a component. Done. No tRPC setup.
  • Add a component: create a file, extend the WebComponent({ ... }) factory with your property shape, implement render(), call ClassName.register('tag-name'). Done. No framework CLI scaffolding.
  • Add authentication: follow the recipe in AGENTS.md. Create lib/session.server.ts, modules/auth/*, middleware.ts. The pattern is documented step by step.
  • Add a database model: edit db/schema.server.ts, run webjs db generate then webjs db migrate. Create queries + actions in a new module. Done.
  • Debug an issue: read the failing route file, trace imports, find the action, check types. No build-artifact archaeology.

Design Principles for AI-Friendly Code

If you're writing a WebJs app that AI agents will work on (and they will), follow these principles:

  1. One function per file for actions and queries. Name the file after the function.
  2. Type everything. Function signatures are the API contract. An untyped function is invisible to the agent's reasoning.
  3. Keep routes thin. Business logic goes in modules. Routes are 5-line adapters that call a module function and return a Response.
  4. Use the modules convention. modules/feature/{actions,queries,components,utils,types.ts}. An agent knows where to look and where to put things.
  5. Keep AGENTS.md up to date. When you add a new convention, document it. When you add a new module, add a recipe. The agent reads this file on every session.
  6. No barrel files. Import from the specific file, not from an index.ts that re-exports. Agents work with individual files, not module graphs.
  7. No magic. If something happens implicitly (auto-registration, runtime transforms, generated code), document it explicitly in AGENTS.md. What the agent can't see, it can't reason about.

Comparison: AI-Friendliness

This is an opinionated comparison. Every framework has trade-offs.
                       webjs        NextJs       Express
──────────────────────────────────────────────────────────
AGENTS.md contract     ✅ built-in   ❌ none       ❌ none
MCP server (read-only) ✅ webjs mcp  ❌ none       ❌ none
Cross-agent configs    ✅ 5 agents   ❌ none       ❌ none
Auto tests + docs      ✅ enforced   ❌ manual     ❌ manual
Branch guardrails      ✅ hooks      ❌ none       ❌ none
Convention validator   ✅ webjs check ❌ none      ❌ none
File = function        ✅ one/file   ⚠️ varies     ❌ free-form
No build transforms    ✅ none       ❌ SWC/webpack ✅ none
Explicit server bound. ✅ .server.ts ⚠️ 'use srv'  n/a
Typed RPC (no schema)  ✅ rich types ⚠️ Flight     ❌ manual
Autonomous mode        ✅ defaults   ❌ n/a        ❌ n/a

The AGENTS.md File

Here's what a WebJs app's AGENTS.md contains (the blog example ships a complete one):

## What webjs is
## App layout (file conventions table)
## Public API: webjs
## Public API: @webjsdev/server
## Modules architecture
## File conventions in detail (pages, layouts, routes, actions, components)
## Invariants (rules agents must follow)
## Recipes (step-by-step: add a page, add an action, add a component...)
## Security checklist for REST endpoints
## Advanced features (Suspense, bundling, rate limiting, WebSockets...)
## Runtime targets (Node, embedded, edge)
## Deliberately deferred (what NOT to implement)

You can read the full AGENTS.md on GitHub.