Lego bricks for AI-composable web apps
A model composes a SiteManifest from a fixed catalog of typed blocks — its entire
API surface. The engine validates it and renders one self-contained static HTML document. No raw HTML from
the model, no framework runtime, nothing to trust at render time.
1 · the brief
“a neighbourhood café with a menu, a gallery and opening hours” generateSite(brief, callModel)
2 · the manifest
"meta": { "title": "Brew & Bloom — Neighbourhood Café" },
"design": { "palette": { "primary": "#3a5a40" } },
"blocks": [
{ "id": "nav", "type": "nav" },
{ "id": "hero", "type": "hero" },
{ "id": "menu", "type": "services-catalogue" },
{ "id": "gallery", "type": "gallery" },
… 4 more
]3 · the document
typed JSON in → validate → render → one HTML file out
Install
See it rendered
For AI agents
The consumer of this package is a model, so the contract is fetchable without installing anything. One request each — no npm, no build step.
/llms.txtIndex of everything below, in the convention models look for.
/AGENT.mdPrime directives, composing a manifest, editing with ops, guarantees.
/catalog.jsonAll 54 block types with full JSON Schema for their config.
/catalog.txtThe same vocabulary, one line per block, cheap to put in a prompt.
/tools.jsonA ready-to-use compose_site function-calling definition.
Why it is built this way
A closed vocabulary
A block exists only if it is registered. An unknown type is skipped, so a confused model produces a plainer page — never a broken one. illegal states unrepresentable
The renderer is total
Every block fills defaults, escapes its input and never throws. Validity is independent of how good the model's output was. validity ⟂ AI quality
Static first
Pages ship zero JavaScript until a block earns it; interactive blocks hydrate from a small island, and turning the behaviour off ships nothing. 54 blocks, 8 islands
Yours to host
Output is one document with inlined CSS and no external dependencies. Node >=20, MIT, v0.12.0. 0 runtime deps