Agent-Native Fullstack Framework
Architecture that doesn't break even when AI agents write your code
한국어 | English
Two install paths — pick whichever matches how you plan to use it. Full details in docs/install.md.
Standalone binary — no prerequisites, one file:
# Linux / macOS
curl -fsSL https://raw.githubusercontent.com/konamgil/mandu/main/install.sh | sh
# Windows (PowerShell)
iwr https://raw.githubusercontent.com/konamgil/mandu/main/install.ps1 -useb | iexnpm / Bun package — smaller download, requires Bun 1.3.12+:
bun install -g @mandujs/cliTrade-offs: the binary bundles the Bun runtime (~132 MB, zero prerequisites), the npm package is ~5 MB but needs Bun pre-installed. Both expose the same mandu command.
# Verify either install
mandu --versionmandu init my-app # if you installed the binary or `bun install -g @mandujs/cli`
# or
bunx @mandujs/cli init my-app # zero-install alternative
cd my-app
bun installRealtime chat starter template:
bunx @mandujs/cli init my-chat-app --template realtime-chatbun run devThe generated app maps bun run dev to mandu dev.
Your app is now running at http://localhost:3333
Create app/page.tsx:
export default function Home() {
return (
<div>
<h1>Welcome to Mandu!</h1>
<p>Edit this file and see changes instantly.</p>
</div>
);
}Create app/api/hello/route.ts:
export function GET() {
return Response.json({ message: "Hello from Mandu!" });
}Now visit http://localhost:3333/api/hello
bun run buildThat's it! You're ready to build with Mandu.
If you're new to Mandu, this section will help you understand the basics.
my-app/
├── app/ # Your code goes here (FS Routes)
│ ├── page.tsx # Home page (/)
│ └── api/
│ └── health/
│ └── route.ts # Health check API (/api/health)
├── src/ # Architecture layers
│ ├── client/ # Client (FSD)
│ ├── server/ # Server (Clean)
│ └── shared/ # Universal shared
│ ├── contracts/ # Client-safe contracts
│ ├── types/
│ ├── utils/
│ │ ├── client/ # Client-safe utils
│ │ └── server/ # Server-only utils
│ ├── schema/ # Server-only schema
│ └── env/ # Server-only env
├── spec/
│ └── routes.manifest.json # Route definitions (auto-managed)
├── .mandu/ # Build output (auto-generated)
├── package.json
└── tsconfig.json
| File Name | Purpose | URL |
|---|---|---|
app/page.tsx |
Home page | / |
app/about/page.tsx |
About page | /about |
app/users/[id]/page.tsx |
Dynamic user page | /users/123 |
app/api/users/route.ts |
Users API | /api/users |
app/layout.tsx |
Shared layout | Wraps all pages |
Create app/about/page.tsx:
export default function About() {
return (
<div>
<h1>About Us</h1>
<p>Welcome to our site!</p>
</div>
);
}Visit http://localhost:3333/about
Create app/users/[id]/page.tsx:
export default function UserProfile({ params }: { params: { id: string } }) {
return (
<div>
<h1>User Profile</h1>
<p>User ID: {params.id}</p>
</div>
);
}Visit http://localhost:3333/users/123
Create app/api/users/route.ts:
// GET /api/users
export function GET() {
return Response.json({
users: [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" }
]
});
}
// POST /api/users
export async function POST(request: Request) {
const body = await request.json();
return Response.json({
message: "User created",
user: body
}, { status: 201 });
}Create app/layout.tsx:
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<head>
<title>My Mandu App</title>
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
<main>{children}</main>
<footer>© 2025 My App</footer>
</body>
</html>
);
}| Command | What it does |
|---|---|
bunx @mandujs/cli init my-app |
Create a new project called "my-app" |
bun install |
Install all dependencies |
bun run dev |
Start development server at http://localhost:3333 |
bun run build |
Build for production (mandu build) |
bun run test |
Run tests |
# Check all available commands
bunx mandu --help
# Show all routes in your app
bunx mandu routes list
# Check architecture rules
bunx mandu guard arch
# Watch for architecture violations (real-time)
bunx mandu guard arch --watch
# Setup and run automated E2E tests
bunx mandu add test
bunx mandu test:auto| Technology | Version | Purpose |
|---|---|---|
| Bun | 1.0+ | JavaScript runtime & package manager |
| React | 19.x | UI library |
| TypeScript | 5.x | Type safety |
- Read the FS Routes section to understand routing patterns
- Try Mandu Guard to enforce architecture rules
- Explore MCP Server for AI agent integration
| Problem | Solution |
|---|---|
command not found: bun |
Install Bun: curl -fsSL https://bun.sh/install | bash |
| Port 3333 already in use | Stop other servers or use PORT=3334 bun run dev |
| Changes not reflecting | Restart dev server with bun run dev |
| TypeScript errors | Run bun install to ensure types are installed |
Mandu is a Bun + TypeScript + React fullstack framework designed for AI-assisted development.
Not "how fast AI can code" but enforcing architecture that AI cannot break
Current AI coding has a fundamental problem: the more agents code, the more architecture deteriorates. Mandu solves this with:
- FS Routes: File-system based routing (like Next.js) - structure IS the API
- Mandu Guard: Real-time architecture enforcement - violations detected instantly
- Slot System: Isolated spaces where agents safely write business logic
┌─────────────────────────────────────────────────────────────┐
│ Mandu Architecture │
├─────────────────────────────────────────────────────────────┤
│ │
│ 📁 app/ File-System Routes (structure = API) │
│ ↓ │
│ 🛡️ Guard Real-time architecture enforcement │
│ ↓ │
│ 🎯 Slot Agent's permitted workspace │
│ ↓ │
│ 🏝️ Island Selective client-side hydration │
│ │
└─────────────────────────────────────────────────────────────┘
| Feature | Description |
|---|---|
| FS Routes | File-system based routing — app/users/page.tsx → /users |
| Mandu Guard | Real-time architecture checker with 6 presets (FSD, Clean, Hexagonal, Atomic, CQRS, Mandu) |
| Self-Healing Guard | Detect violations AND provide actionable fix suggestions with auto-fix |
| Decision Memory | ADR storage for AI to reference past architecture decisions |
| Architecture Negotiation | AI-Framework dialog before implementation |
| Slot System | Isolated areas where agents safely write business logic |
| Semantic Slots | Purpose & constraints validation for AI-generated code |
| Feature | Description |
|---|---|
| Filling API | 8-stage lifecycle (loader → guard → action → render) with fluent chaining |
| Island Architecture | 5 hydration strategies: load, idle, visible, media, never — Zero-JS by default |
| ISR/SWR Cache | Built-in incremental regeneration with revalidatePath / revalidateTag |
| PPR (Partial Prerendering) | Cached shell + fresh dynamic data |
| Streaming SSR | React 19 streaming with deferred data |
| Per-Island Code Splitting | Independent JS bundles per island file |
| WebSocket | Built-in filling.ws() chaining handlers |
| Session Management | Cookie-based with HMAC signing + secret rotation |
| Image Handler | Built-in /_mandu/image with optimization |
| Middleware | CORS, JWT, compress, logger, timeout — all built-in |
| Form (Progressive Enhancement) | <Form> works without JS, enhanced when JS loads |
| View Transitions API | Smooth navigation transitions with state preservation |
| Feature | Description |
|---|---|
| Contract API | One Zod schema → type inference + runtime validation + OpenAPI 3.0 |
| Client/Server Type Inference | End-to-end type safety from Contract to client fetch |
| SEO Module | Next.js Metadata API compatible, sitemap/robots generation, JSON-LD helpers |
| Feature | Description |
|---|---|
| MCP Server | 85+ tools, 4 resources, 3 prompts for AI agents to directly manipulate the framework |
| Claude Code Skills | 9 SKILL.md plugins (@mandujs/skills) for guided AI workflows |
| Transaction API | Atomic changes with snapshot-based rollback |
| Activity Log Observability | EventBus + correlation ID tracking + SQLite store + OpenTelemetry export |
mandu://activity resource |
AI agents can query observability data directly |
| Feature | Description |
|---|---|
| ATE (Automation Test Engine) | AI-driven E2E testing — Extract → Generate → Run → Heal |
| Smart Test Selection | Git-diff based intelligent route selection with priority scoring |
| Coverage Gap Detection | Find untested route transitions, API calls, form actions |
| Pre-commit Hook | Auto-detect changes that need testing before commit |
| Self-Healing Tests | 7-category failure classification + history-based confidence boost |
| L0/L1/L2/L3 Oracle Levels | Smoke → structural → contract → behavioral assertions |
| Feature | Description |
|---|---|
| HMR Support | Hot reload for SSR pages, API routes, CSS, and islands |
| Kitchen DevTools | Built-in dashboard at /__kitchen with 7 tabs (Errors, Network, Islands, Requests, MCP, Cache, Metrics) |
mandu monitor CLI |
EventBus-based observability stream with filtering and stats |
| Tailwind v4 Auto-build | Self-managed CSS watcher (no --watch needed) |
| Lockfile Validation | Config integrity check before dev/build |
# 1. Create project
bunx @mandujs/cli init my-app
# 2. Create pages in app/ folder
# app/page.tsx → /
# app/users/page.tsx → /users
# app/api/users/route.ts → /api/users
# 3. Start development (Guard auto-enabled)
bunx mandu dev
# 4. Build for production
bunx mandu buildProject Lifecycle
| Command | Description |
|---|---|
mandu init [name] |
Create new project (with templates: default, realtime-chat) |
mandu dev |
Start dev server (FS Routes + Guard + HMR auto-enabled) |
mandu dev:safe |
Start dev with lockfile pre-validation |
mandu build |
Build for production |
mandu start |
Start production server |
mandu check |
Run integrated routes + architecture + config checks |
mandu lock |
Generate/refresh lockfile for config integrity |
Architecture & Quality
| Command | Description |
|---|---|
mandu guard arch |
Run architecture check |
mandu guard arch --watch |
Real-time architecture violation detection |
mandu guard heal |
Apply auto-fix suggestions to violations |
mandu routes list |
Show all routes |
mandu manifest validate |
Validate route manifest schema |
Observability
| Command | Description |
|---|---|
mandu monitor |
Live event stream from dev server (HTTP/MCP/Guard/Build) |
mandu monitor --type mcp |
Filter by event type |
mandu monitor --severity error |
Filter by severity |
mandu monitor --trace <id> |
Show all events for a correlation ID |
mandu monitor --stats --since 5m |
Aggregated stats over time window |
Testing (ATE)
| Command | Description |
|---|---|
mandu add test |
Setup ATE (Automation Test Engine) |
mandu test:auto |
Run automated E2E tests |
mandu test:auto --ci |
CI mode (exit 1 on failure) |
mandu test:heal |
Auto-heal failed tests with confidence scoring |
MCP & Skills
| Command | Description |
|---|---|
bunx mandu-mcp |
Start MCP server (manual mode) |
bunx @mandujs/skills |
Install Claude Code skills |
Mandu loads configuration from mandu.config.ts, mandu.config.js, or mandu.config.json.
For guard-only overrides, .mandu/guard.json is also supported.
mandu devandmandu buildvalidate the config and print errors if invalid- CLI flags override config values
// mandu.config.ts
export default {
server: {
port: 3333,
hostname: "localhost",
cors: false,
streaming: false,
rateLimit: {
windowMs: 60_000,
max: 100,
},
},
dev: {
hmr: true,
watchDirs: ["src/shared", "shared"],
},
build: {
outDir: ".mandu",
minify: true,
sourcemap: false,
},
guard: {
preset: "mandu",
srcDir: "src",
exclude: ["**/*.test.ts"],
realtime: true,
// rules/contractRequired are used by legacy spec guard
},
seo: {
enabled: true,
defaultTitle: "My App",
titleTemplate: "%s | My App",
},
};server.rateLimit applies to API routes only (client IP + route key). Exceeded requests return 429 with X-RateLimit-* headers.
Create routes by simply adding files to the app/ directory:
app/
├── page.tsx → /
├── layout.tsx → Layout for all pages
├── users/
│ ├── page.tsx → /users
│ ├── [id]/
│ │ └── page.tsx → /users/:id
│ └── [...slug]/
│ └── page.tsx → /users/* (catch-all)
├── api/
│ └── users/
│ └── route.ts → /api/users (API endpoint)
└── (auth)/ → Route group (no URL segment)
├── login/
│ └── page.tsx → /login
└── register/
└── page.tsx → /register
| File | Purpose |
|---|---|
page.tsx |
Page component |
layout.tsx |
Shared layout wrapper |
route.ts |
API endpoint handler |
loading.tsx |
Loading state |
error.tsx |
Error boundary |
slot.ts |
Server-side business logic |
client.tsx |
Client-side interactive component (Island) |
Real-time architecture enforcement with preset support.
| Preset | Description | Use Case |
|---|---|---|
mandu |
FSD + Clean Architecture hybrid (default) | Fullstack projects |
fsd |
Feature-Sliced Design | Frontend-focused |
clean |
Clean Architecture | Backend-focused |
hexagonal |
Hexagonal / Ports & Adapters | Domain-driven |
atomic |
Atomic Design | UI component libraries |
cqrs |
Command Query Responsibility Segregation | Event-sourced apps |
# One-time check
bunx mandu guard arch
# Watch mode
bunx mandu guard arch --watch
# CI mode (exit 1 on errors)
bunx mandu guard arch --ci
# With specific preset
bunx mandu guard arch --preset fsd
# Generate report
bunx mandu guard arch --output report.md --report-format markdownClient (FSD) Shared (strict) Server (Clean)
────────────────── ─────────────── ─────────────────
client/app shared/contracts server/api
↓ shared/types ↓
client/pages shared/utils/client server/application
↓ shared/schema (server-only) ↓
client/widgets shared/utils/server server/domain
↓ shared/env (server-only) ↓
client/features server/infra
↓ ↓
client/entities server/core
↓
client/shared
Upper layers can only import from lower layers. Guard detects violations in real-time.
Write business logic in isolated slot files:
// spec/slots/users.slot.ts
import { Mandu } from "@mandujs/core";
export default Mandu.filling()
.guard((ctx) => {
if (!ctx.get("user")) return ctx.unauthorized("Login required");
})
.get(async (ctx) => {
const users = await db.users.findMany();
return ctx.ok({ data: users });
})
.post(async (ctx) => {
const body = await ctx.body<{ name: string; email: string }>();
const user = await db.users.create({ data: body });
return ctx.created({ data: user });
});| Method | Description |
|---|---|
ctx.ok(data) |
200 OK |
ctx.created(data) |
201 Created |
ctx.error(message) |
400 Bad Request |
ctx.unauthorized(message) |
401 Unauthorized |
ctx.notFound(message) |
404 Not Found |
ctx.body<T>() |
Parse request body |
ctx.params |
Route parameters |
ctx.query |
Query parameters |
Zero-JS by default. Ship interactive components only where needed, with 5 hydration strategies.
| Strategy | When JS Loads | Use Case |
|---|---|---|
load |
Immediately on page load | Critical interactive UI (chat, forms) |
idle |
During browser idle (requestIdleCallback) |
Non-critical widgets |
visible |
When element enters viewport (default) | Below-the-fold components |
media |
When CSS media query matches | Mobile-only or desktop-only widgets |
never |
Never hydrated (pure SSR HTML) | Static content |
// app/page.tsx
import { island } from "@mandujs/core";
import Counter from "@/client/widgets/counter";
const VisibleCounter = island("visible", Counter);
export default function Home() {
return (
<main>
<h1>Welcome</h1>
<VisibleCounter initial={0} />
</main>
);
}// app/widgets/counter.client.tsx — must import from "@mandujs/core/client"
import { island } from "@mandujs/core/client";
import { useState } from "react";
export default island<{ initial: number }>({
setup: ({ props }) => ({ count: props.initial }),
render: ({ data }) => {
const [count, setCount] = useState(data.count);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(c => c + 1)}>+</button>
</div>
);
},
});Each island file is bundled independently. The browser only downloads the JS for islands actually used on the current page.
Type-safe API contracts with full inference:
import { Mandu } from "@mandujs/core";
import { z } from "zod";
// Define contract
const userContract = Mandu.contract({
request: {
GET: { query: z.object({ id: z.string() }) },
POST: { body: z.object({ name: z.string(), email: z.string().email() }) }
},
response: {
200: z.object({ data: z.any() }),
400: z.object({ error: z.string() })
}
});
// Create handlers (fully typed)
const handlers = Mandu.handler(userContract, {
GET: (ctx) => ({ data: fetchUser(ctx.query.id) }),
POST: (ctx) => ({ data: createUser(ctx.body) })
});
// Type-safe client
const client = Mandu.client(userContract, { baseUrl: "/api/users" });
const result = await client.GET({ query: { id: "123" } });8-stage lifecycle for route handlers with fluent chaining.
// app/api/todos/route.ts
import { Mandu } from "@mandujs/core";
import { db } from "@/server/infra/db";
import { jwtMiddleware, corsMiddleware } from "@mandujs/core/middleware";
export default Mandu.filling()
// 1. Middleware (composable)
.use(corsMiddleware({ origin: "*" }))
.use(jwtMiddleware({ secret: process.env.JWT_SECRET! }))
// 2. Guard (early return on failure)
.guard((ctx) => {
if (!ctx.user) return ctx.unauthorized("Login required");
})
// 3. Loader (cached with ISR)
.loader(async (ctx) => {
return { todos: await db.todos.list(ctx.user.id) };
}, { revalidate: 30, tags: ["todos"] })
// 4. Named actions (auto-revalidate after mutation)
.action("create", async (ctx) => {
const { title } = await ctx.body<{ title: string }>();
const todo = await db.todos.create(ctx.user.id, title);
return ctx.created({ todo });
})
.action("delete", async (ctx) => {
const { id } = ctx.params;
await db.todos.delete(id);
return ctx.noContent();
})
// 5. Render mode
.render("isr", { revalidate: 60 });- Middleware — composable plugins (cors, jwt, compress, logger, timeout)
- Guard — early return for auth/permissions
- Loader — fetch data (cached with ISR/SWR/PPR)
- Actions — named mutation handlers with auto-revalidation
- Layout chain — nested route data loading (parallel)
- Render — SSR with hydration strategy
- Response — apply cookies, headers, mapResponse hooks
- Cache — store result by route + query
| Mode | Behavior |
|---|---|
dynamic |
Always SSR fresh (default) |
isr |
Cache full HTML, regenerate on stale + tag invalidation |
swr |
Serve stale, regenerate in background |
ppr |
Cache shell, fresh dynamic data per request |
Built-in middleware plugins. All return MiddlewarePlugin (beforeHandle + afterHandle + mapResponse).
import { Mandu } from "@mandujs/core";
import {
corsMiddleware,
jwtMiddleware,
compressMiddleware,
loggerMiddleware,
timeoutMiddleware,
} from "@mandujs/core/middleware";
export default Mandu.filling()
.use(corsMiddleware({ origin: ["https://example.com"], credentials: true }))
.use(jwtMiddleware({ secret: process.env.JWT_SECRET!, algorithms: ["HS256"] }))
.use(compressMiddleware({ threshold: 1024 }))
.use(loggerMiddleware())
.use(timeoutMiddleware({ ms: 30_000 }))
.loader(async (ctx) => ({ user: ctx.user })); // ctx.user typed by jwtMiddleware| Middleware | Features |
|---|---|
corsMiddleware |
Origin allowlist, credentials, preflight |
jwtMiddleware |
HS256/HS384/HS512, algorithm allowlist, nbf validation, 8KB token limit |
compressMiddleware |
gzip/deflate with threshold |
loggerMiddleware |
Structured request logging |
timeoutMiddleware |
Per-request timeout with abort |
Cookie-based sessions with HMAC signing and secret rotation.
import { createCookieSessionStorage } from "@mandujs/core";
const sessionStorage = createCookieSessionStorage({
cookie: {
name: "_mandu_session",
secrets: [process.env.SESSION_SECRET!, process.env.OLD_SECRET], // rotation
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
httpOnly: true,
maxAge: 60 * 60 * 24 * 7, // 7 days
},
});
// In a route
export default Mandu.filling()
.loader(async (ctx) => {
const session = await sessionStorage.getSession(ctx.request.headers.get("cookie"));
const user = session.get("user");
return { user };
})
.action("login", async (ctx) => {
const session = await sessionStorage.getSession(ctx.request.headers.get("cookie"));
session.set("user", { id: 1, name: "Alice" });
session.flash("message", "Welcome back!"); // one-time message
return ctx.ok({ ok: true }, {
headers: { "Set-Cookie": await sessionStorage.commitSession(session) },
});
});Built-in incremental static regeneration with tag-based invalidation.
import { Mandu, revalidatePath, revalidateTag } from "@mandujs/core";
// Cache for 60s, tag with "posts"
export default Mandu.filling()
.loader(async () => ({ posts: await db.posts.list() }), {
revalidate: 60,
tags: ["posts"],
});
// Invalidate from a mutation handler
export async function POST() {
await db.posts.create({ ... });
revalidateTag("posts"); // invalidate all tagged caches
revalidatePath("/blog"); // invalidate specific path
return new Response(null, { status: 201 });
}| Mode | Behavior |
|---|---|
| ISR | Cache full HTML; regenerate on stale or tag invalidation |
| SWR | Serve stale immediately, regenerate in background |
| PPR | Cache shell only (HTML structure), fetch fresh data per request |
EventBus-based observability with 6 phases of features. Every HTTP request, MCP tool call, Guard violation, and build event flows through a unified bus.
import { eventBus } from "@mandujs/core/observability";
// Subscribe to all events
const unsubscribe = eventBus.on("*", (event) => {
console.log(event.type, event.message, event.duration);
});
// Subscribe to specific type
eventBus.on("http", (event) => {
if (event.severity === "error") {
console.error("HTTP error:", event.message);
}
});
// Emit custom events
eventBus.emit({
type: "build",
severity: "info",
source: "my-plugin",
message: "Custom build step completed",
duration: 120,
});Every HTTP request gets a correlationId (from x-mandu-request-id header or auto-generated UUID). All events triggered by that request share the same ID, enabling distributed tracing.
# Live event stream
mandu monitor
# Filter by type
mandu monitor --type mcp
# Filter by severity
mandu monitor --severity error
# Trace a specific request
mandu monitor --trace req-abc-123
# Aggregated stats over 5 minutes
mandu monitor --stats --since 5mVisit http://localhost:3333/__kitchen to see:
| Tab | Description |
|---|---|
| Errors | Persistent error log (.mandu/errors.jsonl) with stack traces |
| Network | Fetch/XHR proxied requests (with response bodies) |
| Islands | Active island bundles and hydration status |
| Requests | HTTP request log with correlation-linked detail view |
| MCP | MCP tool call timeline grouped by correlation ID |
| Cache | ISR/SWR cache stats (entries, hit rate, stale, tags) |
| Metrics | TTFB p50/p95/p99, MCP avg duration, error rate |
import {
startSqliteStore,
queryEvents,
queryStats,
exportJsonl,
exportOtlp,
} from "@mandujs/core/observability";
// Auto-started by `mandu dev` (configurable via dev.observability)
await startSqliteStore(rootDir); // → .mandu/observability.db
// Query historical events
const events = queryEvents({
type: "http",
severity: "error",
sinceMs: Date.now() - 60_000,
limit: 100,
});
// Aggregated stats over time window
const stats = queryStats(5 * 60 * 1000); // last 5 minutes
// Export for external tools
const jsonl = exportJsonl({ type: "http" });
const otlp = exportOtlp({}); // OpenTelemetry-compatiblemandu://activity → Recent 20 events + 5-minute stats
AI agents can query observability data directly via MCP without parsing log files.
AI-driven end-to-end testing automation with self-healing capabilities.
ATE automatically:
- Extracts your app's interaction graph from source code (AST-based)
- Generates Playwright test specs with domain-aware assertions (4 scenario kinds: ssr-verify, island-hydration, sse-stream, form-action)
- Runs E2E tests with detailed reporting and
--grepfiltering - Heals failures with 7-category classification and history-based confidence scoring
- Smart-selects routes to test based on git diff (Phase 5)
- Detects coverage gaps in the interaction graph (Phase 5)
# 1. Setup ATE
bunx mandu add test
# 2. Run automated E2E tests
bunx mandu test:auto
# 3. If tests fail, auto-heal them
bunx mandu test:heal| Feature | Description |
|---|---|
| AST-based Extraction | Analyzes TypeScript/React code to find routes, contracts, islands, SSE, actions |
| Domain Detection | Auto-detects domain type (ecommerce, blog, dashboard, auth, generic) |
| Oracle Levels (4) | L0 smoke → L1 structural → L2 contract schema → L3 behavioral assertions |
| Mandu Scenario Kinds | ssr-verify, island-hydration, sse-stream, form-action |
| Selector Fallback | 4-tier fallback chain: mandu-id → text → class → role → xpath |
| Trace Parser | Analyzes Playwright traces to identify failure causes |
| Impact Analysis | Git diff-based subset testing (only test affected routes) |
| Smart Test Selection (Phase 5) | Priority scoring: contract→HIGH, guard→HIGH, route→MEDIUM, shared→LOW |
| Coverage Gap Detection (Phase 5) | Find untested route transitions, API calls, form actions, island interactions |
| Pre-commit Hook (Phase 5) | Auto-detect staged changes that need testing |
| Auto-Healing | 7-category classification (selector-stale, api-shape-changed, race-condition, timeout, etc.) |
| Heal History Learning | Past success rate boosts auto-apply confidence (≥80% → +2 priority) |
| testFilling Unit Tests | Generate Bun unit tests in addition to Playwright E2E |
| MCP Integration | 12 MCP tools (9 ATE + 3 Phase 5) for AI agents |
// Pipeline automation
mandu.ate.auto_pipeline // Extract → Generate → Run → Report → Heal
// Individual steps
mandu.ate.extract // Extract interaction graph
mandu.ate.generate // Generate Playwright specs
mandu.ate.run // Run tests
mandu.ate.report // Generate reports (JSON/HTML)
mandu.ate.heal // Generate heal suggestions
mandu.ate.impact // Calculate affected routes
// Feedback loop
mandu.ate.feedback // Analyze failures with 7-category classification
mandu.ate.apply_heal // Apply heal diffs safely (with backup)
// Phase 5: Intelligent test selection (NEW)
mandu.test.smart // Smart route selection from git diff
mandu.test.coverage // Detect coverage gaps in interaction graph
mandu.test.precommit // Pre-commit hook: should we test before committing?# AI agent can run the entire pipeline with one MCP call:
{
"tool": "mandu.ate.auto_pipeline",
"arguments": {
"repoRoot": "/path/to/project",
"baseURL": "http://localhost:3333",
"oracleLevel": "L1",
"useImpactAnalysis": true,
"autoHeal": true
}
}ATE includes GitHub Actions templates:
# .github/workflows/ate-e2e.yml (auto-generated)
name: ATE E2E Tests
on: [pull_request, push]
jobs:
e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v1
- run: bun install
- run: bunx playwright install --with-deps chromium
- run: bun run test:e2e:ci
- uses: actions/upload-artifact@v4
with:
name: playwright-report
path: .mandu/reports/Mandu includes a full MCP (Model Context Protocol) server with 85+ tools, 4 resources, and 3 prompts for AI agent integration.
// .mcp.json
{
"mcpServers": {
"mandu": {
"command": "bunx",
"args": ["mandu-mcp"],
"cwd": "/path/to/project"
}
}
}Note: Use
mandu-mcp(not@mandujs/mcp) to avoid conflicts with Python'smcpCLI on PATH (#174).
| Category | Tools | Examples |
|---|---|---|
| Project | 4 | mandu.project.init, mandu.dev.start, mandu.dev.stop |
| Routes | 5 | mandu.route.list, mandu.route.add, mandu.route.delete, mandu.route.get, mandu.manifest.validate |
| Generate | 2 | mandu.generate, mandu.generate.status |
| Guard | 4 | mandu.guard.check, mandu.guard.analyze, mandu.guard.heal, mandu.guard.explain |
| Decisions | 4 | mandu.decision.save, mandu.decision.check, mandu.decision.list, mandu.decision.architecture |
| Negotiate | 3 | mandu.negotiate, mandu.negotiate.analyze, mandu.negotiate.scaffold |
| Slot | 3 | mandu.slot.read, mandu.slot.validate, mandu.slot.constraints |
| Hydration | 4 | mandu.island.add, mandu.island.list, mandu.hydration.set, mandu.hydration.addClientSlot |
| Contract | 7 | mandu.contract.create, mandu.contract.validate, mandu.contract.openapi, mandu.contract.sync, etc. |
| Resource | 5 | mandu.resource.create, mandu.resource.list, mandu.resource.get, mandu.resource.addField, mandu.resource.removeField |
| Brain | 4 | mandu.brain.doctor, mandu.brain.architecture, mandu.brain.checkImport, mandu.brain.checkLocation |
| Runtime | 5 | mandu.runtime.config, mandu.runtime.contractOptions, mandu.runtime.loggerConfig, etc. |
| SEO | 6 | mandu.seo.analyze, mandu.seo.jsonld, mandu.seo.sitemap, mandu.seo.robots, mandu.seo.preview, mandu.seo.write |
| History | 3 | mandu.history.snapshot, mandu.history.list, mandu.history.prune |
| Transaction | 4 | mandu.tx.begin, mandu.tx.commit, mandu.tx.rollback, mandu.tx.status |
| Watch | 3 | mandu.watch.start, mandu.watch.stop, mandu.watch.status |
| Kitchen | 1 | mandu.kitchen.errors |
| ATE | 9 | mandu.ate.extract, mandu.ate.generate, mandu.ate.run, mandu.ate.report, mandu.ate.heal, mandu.ate.impact, mandu.ate.auto_pipeline, mandu.ate.feedback, mandu.ate.apply_heal |
| Test (Phase 5) | 4 | mandu.test.smart, mandu.test.coverage, mandu.test.precommit, mandu.test.route |
| Composite | 7 | mandu.feature.create, mandu.diagnose, mandu.middleware.add, mandu.deploy.check, mandu.cache.manage, etc. |
| Build | 2 | mandu.build, mandu.build.status |
| Component | 1 | mandu.component.add |
All tools use dot notation (mandu.guard.check) with backward-compatible underscore aliases (mandu_guard_check).
| URI | Description |
|---|---|
mandu://routes |
Current routes manifest |
mandu://config |
Parsed mandu.config.ts settings |
mandu://errors |
Recent build and runtime errors |
mandu://activity |
NEW: Recent observability events + 5-minute stats from EventBus |
Filter tools by profile via MANDU_MCP_PROFILE env var:
| Profile | Tools | Use Case |
|---|---|---|
minimal |
~15 | Read-only operations, safe for autonomous agents |
standard |
~50 | Default — most common operations |
full |
85+ | All tools including destructive operations |
Mandu ships with 9 SKILL.md plugins for Claude Code at @mandujs/skills.
bunx @mandujs/skills install| Skill | Purpose |
|---|---|
create-feature |
Guided feature scaffolding with Guard validation |
create-api |
API route + Contract + Filling generation |
debug |
Root cause analysis with Mandu observability |
explain |
Code explanation with framework context |
guard-guide |
Architecture preset selection guide |
deploy |
Production deployment checklist |
slot |
Slot file authoring with semantic constraints |
fs-routes |
FS Routes patterns and conventions |
hydration |
Island hydration strategy selection |
my-app/
├── app/ # FS Routes (pages, layouts, API)
│ ├── page.tsx
│ └── api/
├── spec/
│ ├── routes.manifest.json # Route definitions
│ └── slots/ # Business logic
├── .mandu/
│ ├── client/ # Built bundles
│ └── manifest.json # Bundle manifest
└── package.json
mandu/
├── packages/
│ ├── core/ # @mandujs/core - Runtime, Guard, Router, Bundler, Observability
│ ├── cli/ # @mandujs/cli - 38+ CLI commands
│ ├── mcp/ # @mandujs/mcp - MCP server (85+ tools, 4 resources, 3 prompts)
│ ├── ate/ # @mandujs/ate - Automation Test Engine (Phase 1-6)
│ └── skills/ # @mandujs/skills - Claude Code skills plugin
├── docs-site/ # Astro Starlight documentation site
└── docs/ # Roadmaps, RFCs, architecture decisions
| Area | Technology |
|---|---|
| Runtime | Bun 1.0+ (bun:sqlite, Bun.spawn, Bun.serve) |
| Language | TypeScript 5.x |
| Frontend | React 19 (Streaming SSR, Suspense, useTransition) |
| Validation | Zod (Contract API) |
| Testing | Bun Test + Playwright (via ATE) |
| AI Protocol | MCP (Model Context Protocol) |
| Build | Bun bundler + Tailwind CSS v4 (Oxide) |
| Storage | bun:sqlite (observability), JSONL (errors, activity) |
Core Runtime
- Filling API with 8-stage lifecycle + named actions + auto-revalidation
- Streaming SSR with React 19
- Middleware composition (cors, jwt, compress, logger, timeout)
- Runtime logger with structured output
- Cookie-based session storage with HMAC signing + secret rotation
- WebSocket via
filling.ws()chaining - Image handler (
/_mandu/image) - Form Progressive Enhancement
- View Transitions API integration
Routing & Layout
- FS Routes (scanner, patterns, generator, watcher)
- Nested layout chain with parallel data loading
- Advanced routes (catch-all, optional params, route groups)
- Client-side router (Link, NavLink, hooks, prefetch)
- Race-condition-free navigation with AbortController
Architecture (Guard)
- 6 presets (mandu, fsd, clean, hexagonal, atomic, cqrs)
- AST-based import analysis
- Real-time violation detection with file watcher
- Self-Healing Guard with auto-fix suggestions
- Decision Memory (ADR storage + consistency checking)
- Semantic Slots (purpose & constraint validation)
- Architecture Negotiation (AI-Framework pre-implementation dialog)
Cache & Performance
- ISR (Incremental Static Regeneration) with tag invalidation
- SWR (stale-while-revalidate) with background regeneration
- PPR (Partial Prerendering) — cached shell + fresh data
-
revalidatePath/revalidateTagglobal API - LRU memory cache with tag index
- ETag + 304 Not Modified for static files
Hydration
- 5 island strategies (load, idle, visible, media, never)
- Per-island code splitting (independent JS bundles)
- Declarative + client island patterns
- React Internals shim for Bun compatibility
- HMR support for SSR pages, API routes, CSS, islands
Type Safety & Contracts
- Contract API with Zod
- Type-safe handlers & clients with end-to-end inference
- OpenAPI 3.0 generator
- Schema normalization (strip/strict/passthrough)
-
defineContractlow-level API
SEO
- Next.js Metadata API compatible types
- Layout chain metadata merging
- Open Graph & Twitter Cards
- JSON-LD structured data (12 helpers)
- Sitemap.xml & robots.txt generation
- SSR integration with
<head>injection
AI Integration (RFC-001: From Guard to Guide)
- MCP server: 85+ tools, 4 resources, 3 prompts
- Tool profiles (minimal/standard/full) via
MANDU_MCP_PROFILE - Brain (Doctor, Watcher, Architecture analyzer)
- Transaction API with snapshots (
tx-lockfor multi-agent safety) - 9 Claude Code skills (
@mandujs/skillsplugin)
ATE (Automation Test Engine)
- Phase 1-3: Extract → Generate → Run → Report → Heal pipeline
- Phase 1: L0/L1/L2/L3 Oracle levels
- Phase 2: Mandu scenario kinds (ssr-verify, island-hydration, sse-stream, form-action)
- Phase 3: testFilling unit codegen +
--grepfiltering - Phase 4: Heal 7-category classification + history-based confidence
- Phase 5.1: Smart test selection (git diff → priority scoring)
- Phase 5.2: Coverage gap detection
- Phase 5.3: Pre-commit hook helper
- Phase 6.1: SSR rendering tests (36 tests)
- 12 MCP tools (9 ATE + 3 Phase 5)
Activity Log & Observability (NEW)
- Phase 1: EventBus core + correlation ID + Logger/MCP adapters
- Phase 2: dev terminal 1-line logs +
mkey MCP toggle - Phase 3: Monitor CLI with filtering, stats, SSE streaming
- Phase 4: Kitchen DevTools 5 new tabs (Requests, MCP, Cache, Metrics, Errors persistence)
- Phase 5: AI agent observability (sessionId tracking,
mandu://activityresource) - Phase 6: SQLite persistent store + time-series queries + JSONL/OTLP export
Security
- Path traversal prevention (realpath verification)
- Port validation
- LFI vulnerability protection
- Null byte attack detection
- JWT algorithm allowlist + nbf validation + 8KB token limit
- HMAC session signing with secret rotation
- Rate limiting (per-IP + per-route)
Developer Experience
- HMR for SSR-only pages (no islands required)
- API route hot-reload (route.ts changes auto-reload)
- Tailwind v4 self-managed CSS watcher
- Improved error messages (10 critical paths)
-
.well-known/directory serving (RFC 8615) - Cache-Control headers in dev mode
-
<link>tag auto-hoisting (body → head)
ATE Advanced
- L2 Oracle deep contract validation (Zod schema parsing + edge case generation)
- L3 Oracle behavioral verification (LLM-based state change assertions)
- ATE Watch mode (
mandu test --watch) - Accessibility (a11y) testing with
@axe-core/playwright - devtools/brain/watcher test coverage (currently 0)
- CI E2E job + codecov integration
Build & Integration (Astro/Fresh-inspired)
- Build Hooks (start/setup/done lifecycle)
- Plugin API for build extensions
- Integration hooks with timeout warnings & dedicated logger
- Bundle analyzer with size reporting
-
bun --hotserver module integration
Data Layer (Astro-inspired)
- Loader API with LoaderContext (store, meta, logger, watcher)
- File Loader & API Loader implementations
- Cache Store adapter (Redis, KV)
- Content collections with type-safe queries
AOT Optimization (Elysia-inspired)
- AOT Handler Generation (runtime precompile)
- Context inference for minimal runtime overhead
- JIT/AOT mode selection (
mandu build --aot)
Advanced Hydration (Qwik/Fresh-inspired)
- React Fast Refresh integration (state-preserving HMR)
- Client Reviver (DOM marker-based restoration)
- Resumable POC / QRL-lite (lazy event handler loading)
- Serializer Registry (pluggable type serializers)
Realtime (Phoenix-inspired)
- WebSocket Channels (join/handle_in/handle_out pattern)
- Channel/Socket separation model
- Presence tracking
- Pub/Sub with adapters
Developer Experience
- Error overlay in development with source maps
- Enhanced TypeScript inference for Filling chains
- More project templates (e-commerce, blog, dashboard)
- Visual route inspector
| Package | Tests | Files |
|---|---|---|
@mandujs/core (src) |
543 | 35 |
@mandujs/core (tests) |
874 | 62 |
@mandujs/ate |
242 | 19 |
@mandujs/mcp |
69 | 6 |
| Total | 1728 | 122 |
bun test # Run all tests
bun test packages/core/src # Test specific package
bun test --watch # Watch modedocs/README.md— Documentation indexdocs/api/api-reference.md— API referencedocs/status.md— Implementation statusdocs/specs/— Technical specifications
git clone https://github.com/konamgil/mandu.git
cd mandu && bun install
bun testLike a dumpling (mandu), the wrapper (generated code) stays consistent while the filling (slot) can vary infinitely. No matter how much agents code, the dumpling shape (architecture) is preserved. 🥟
MPL-2.0
Built with 🥟 by the Mandu Team
