Developing the engine
The engine is built by using it. This repo is an npm workspace holding the package and the sites it is developed against, so a change to a component template or the serializer shows up in a running site in the same second.
ai-cms/ package.json the workspace root packages/ai-cms/ the published package, @fivepaths/ai-cms dev/sites/kitchen-sink/ fixture: every component, one page dev/sites/gtfs-media/ the real site, migrating page by page
Setup
npm install
That is the whole linking step. npm workspaces symlinks the package into node_modules/@fivepaths/ai-cms, so every site in dev/sites/ resolves cms to the working copy in packages/ai-cms. No npm link, no global install, no path in a config file to keep in sync.
Confirm it is a symlink rather than a copy:
ls -l node_modules/@fivepaths/ai-cms
The loop
npm run dev
Serves the kitchen-sink fixture at http://localhost:8630, rebuilds on save, and runs the fast check on every rebuild. It watches the site's src/ and the engine's src/, components/, layouts/, and partials/, which is what makes developing the engine against a real site work: edit a component template, see the page change.
A build failure serves an overlay carrying the message and the fix instead of a stale page, so a broken schema is visible in the browser and not only in the terminal.
npm run dev:gtfs # the same loop against the gtfs.media migration
Starting a new site
cms init ~/Sites/my-site --host my-site.example.com cd ~/Sites/my-site && npm install && npx cms sync
init scaffolds a site that builds and passes the gate on its first run, with three real pages rather than placeholders: a scaffold that fails its own checks teaches the wrong thing on day one.
The engine link depends on where the folder is. Inside dev/sites/, npm workspaces handles it. Anywhere else, init writes a file: dependency pointing at this checkout, which npm resolves as a symlink, so editing a component template here changes that site's next build. Verified rather than assumed:
node_modules/@fivepaths/ai-cms -> ../../../../Code/src/ai-cms/packages/ai-cms
npx cms sync then writes the Claude Code skill, the hooks, and docs/components/, so a session opening that folder knows the format without being told.
Running the engine against a site outside this repo
Every command takes --root, and the engine reads nothing outside it. So a checkout can drive any site directory without being installed into it:
node packages/ai-cms/bin/cms.js dev --root ~/Sites/some-other-site
That falls out of the CLI being a pure function of the repo, which is the same property multi-tenancy will need later.
The two sites, and what each is for
kitchen-sink carries every component once, in both bands. Its job is to fail loudly: a change to a template, a token, or the serializer produces a diff here before it reaches anything real. It has almost no site.css, which is the ideal a real site is measured against.
gtfs-media is the live site's content in the new format: twelve pages, 133 blocks. Its job is to be representative rather than complete. It is where the component catalogue gets tested against copy nobody wrote for it, and MIGRATION-REPORT.md is the list of what the catalogue is missing.
The first migration ran against nine components and fell back to prose for 65% of its blocks. Five of the gaps it named are now built, so the honest next step is a second pass that converts those prose fallbacks and re-measures.
Commands
cms build render src/ to public/ cms dev --port 8630 local server, rebuild on save cms outline [route] numbered block list, for addressing edits cms explain cards one component's reference, from its schema cms build --json machine-readable, as every command is
Two things the engine already proves
cms build twice in a row produces byte-identical output. That is what lets a change be verified by diffing rather than by reading.
The class allowlist is read from the stylesheets themselves, so it catches a class no sheet defines. It currently reports six occurrences of the element-duplicating classes MARKUP.md is about, at warning level, because the v2 markup predates that rule. Set markup: v3 in a site's site.yml to make them errors.
What is built, and what is not
Working: the parser, schema validation with the error rules from AGENT-INTERFACE.md, fourteen components, the Nunjucks render pipeline, the deterministic serializer, the class allowlist, the duplicate-id check, and build/dev/outline/explain.
The dev server restarts itself when the engine's own src/ changes, because Node caches an imported module for the life of the process. Templates, schemas, and content are read from disk on every build and only need a rebuild.
Also working: the media pipeline, the font pipeline, and weight budgets.
Not built: axe and the rest of the gate, translation, the conformance ledger, site context, ingest, cms sync and the hooks, and the CI template. ROADMAP.md has the order and the acceptance test for each.