Builder CLI Commands
The Site Builder provides four CLI commands: scaffolding from starters, installing a frontend asset pipeline, inspecting registered routes, and managing template version history.
| Command | Purpose |
|---|---|
builder:init |
Scaffold a new site from a starter template |
builder:frontend |
Install a Vite-based frontend asset pipeline |
builder:routes |
List every route the page router would serve, with conflicts flagged |
builder:history |
List, view, or restore template snapshot versions |
All commands accept --json for machine-readable output.
builder:init
Scaffold a new site from a bundled starter template.
tcms builder:init business
tcms builder:init --list
tcms builder:init blog --force
tcms builder:init --json
What It Does
- Copies template files (layouts, pages, partials) from the starter into
tcms-data/builder/ - Creates the
builder-pagescollection with thebuilder-pageschema (if it doesn't exist) - Imports the starter's
jumpstart.json— pages, plus any supporting schemas, collections, and sample objects - (with
--frontend) installs the Vite frontend scaffold — same as runningtcms builder:frontend
Arguments
| Argument | Required | Description |
|---|---|---|
starter |
No | Starter template name. Omit to list available starters. |
Options
| Option | Description |
|---|---|
--list, -l |
List available starters and exit |
--force, -f |
Overwrite existing template files (existing page records in the collection are left alone — JumpStart skips objects that already exist) |
--frontend |
Also install the Vite frontend scaffold (equivalent to running tcms builder:frontend) |
--json |
Output result as JSON |
Starter Data
Every starter includes a jumpstart.json next to its manifest.json. The CLI runs it through the JumpStart importer after templates are copied so pages and any seed content land in the right places. For the blog starter that means 5 pages + 5 sample posts; for business that means 5 pages + a service schema + a services collection + 4 sample services.
If the import fails (e.g., schema validation), the scaffold itself still succeeds — templates are already on disk, and you can re-run the import manually with tcms jumpstart:import resources/builder/starters/<name>/jumpstart.json.
--frontend Behavior
Convenience flag for the greenfield happy path. Runs tcms builder:frontend immediately after the scaffold completes, installing the Vite scaffold into <projectRoot>/frontend/. If you don't pass --frontend, you can always add the pipeline later — see builder:frontend.
--force Behavior
By default, scaffolding aborts if any page templates already exist. With --force:
- Template files are overwritten (existing files replaced with the starter's versions)
- Asset files in the docroot are overwritten as well
Page records and the order file are left alone — the JumpStart importer skips objects that already exist, so a re-init won't clobber edits made in the admin. To reset a page, delete it first then re-run, or import the page directly with tcms jumpstart:import.
--force is destructive against template files — back up your project first if you've made customizations.
Available Starters
| Starter | Pages | Description |
|---|---|---|
minimal |
Home | Single page with clean layout |
business |
Home, About, Services, Contact | Professional business site |
blog |
Home, Blog, Blog Post, About | Blog-focused site with dynamic post routing |
portfolio |
Home, Work, About, Contact | Portfolio site with project cards |
Example Workflow
# See what's available
tcms builder:init --list
# Scaffold a business site
tcms builder:init business
# Visit your site — routing is automatic, no generation step needed
Since the Site Builder uses middleware-based routing, pages are routed dynamically from the collection data. There is no generation or deployment step — pages work immediately after creating them in the admin.
builder:frontend
Install a Vite-based frontend asset pipeline scaffold into your project. Idempotent — safe to re-run, won't overwrite customizations unless you pass --force.
tcms builder:frontend
tcms builder:frontend --force
tcms builder:frontend --json
What It Does
Copies the contents of resources/builder/frontend/ into <projectRoot>/frontend/:
vite.config.js— emits hashed assets to../public/assets/with manifestpackage.json—dev/build/watchscripts; Vite as the only dependencycss/style.css— minimal starter stylesheetjs/app.js— minimal entry pointREADME.md— quick reference for the install/build workflow.gitignore— excludesnode_modules/,dist/, etc.
After running, cd frontend && npm install && npm run build produces compiled assets at public/assets/ that T3's cms.builder.css() and cms.builder.js() Twig helpers automatically resolve via the manifest.
Options
| Option | Description |
|---|---|
--force, -f |
Overwrite existing files in frontend/ (use with care — any customizations are lost) |
--json |
Output result as JSON |
Idempotency
The default behavior is skip files that already exist, so re-running builder:frontend is safe — it only adds missing files. The result tells you what was skipped and reminds you to re-run with --force if you want a hard reset.
When to Use This vs. --frontend on builder:init
| Scenario | Use |
|---|---|
| Greenfield project, scaffolding for the first time | tcms builder:init <starter> --frontend |
| Existing project, adding a frontend pipeline now | tcms builder:frontend |
| Pull missing files after the scaffold was edited | tcms builder:frontend (skips existing) |
| Reset the scaffold to upstream defaults | tcms builder:frontend --force |
See Frontend Assets for the full asset-pipeline reference (Sass, Tailwind, etc.).
builder:routes
Print every route the page router would serve, in priority order, with duplicates flagged. Useful for auditing a site, hunting down route conflicts, and confirming that a collection URL is actually being matched the way you expect.
tcms builder:routes
tcms builder:routes --json
What It Shows
For each route:
| Column | Description |
|---|---|
| Route | The effective pattern the router would match |
| Source | page (builder page) or collection (collection URL) |
| ID | Page id or collection id |
| Template | Template path that gets rendered on a match |
| Status | HTTP status code the page returns |
| Notes | draft if excluded from routing, ⚠ duplicate if another route declares the same pattern |
Effective Routes for Collections
Collection URLs are normalized for display so you see what the router actually matches:
| Stored URL | Effective Route |
|---|---|
/blog |
/blog/{id} (router auto-appends an id segment) |
/blog/{{ id }} |
/blog/{id} (Twig syntax → standard) |
/products/{{ category }}/{{ id }} |
/products/{category}/{id} |
This makes it easy to spot when a builder page route like /blog/{id} collides with a collection URL /blog — both produce /blog/{id} and the duplicate flag fires.
Example Output
Route Source ID Template Status Notes
/ page home pages/index.twig 200
/about page about pages/about.twig 200
/blog page blog-list pages/blog-list.twig 200
/blog/{id} page blog-post pages/blog-post.twig 200
/blog/{id} collection blog pages/blog.twig 200 ⚠ duplicate
/maintenance page offline pages/503.twig 503 draft
1 duplicate route(s) detected.
The duplicate above means both a builder page (/blog/{id}) and the blog collection's URL (also /blog/{id} after normalization) compete for the same pattern. The builder page wins because it's checked first, but the collection URL is unreachable — pick one or the other.
builder:history
List, view, or restore snapshot versions of a builder template. Every template save captures a snapshot of the previous content; this command exposes that history at the CLI.
# List versions
tcms builder:history pages/about
# View a specific snapshot
tcms builder:history pages/about --show=1714588200
# Restore the snapshot
tcms builder:history pages/about --restore=1714588200
Arguments
| Argument | Required | Description |
|---|---|---|
path |
Yes | Template path without extension (e.g. pages/about, layouts/default, partials/nav) |
Options
| Option | Description |
|---|---|
--show=<timestamp> |
Print the snapshot's contents to stdout |
--restore=<timestamp> |
Restore the snapshot — overwrite the current template file |
--json |
Output result as JSON |
List Mode (default)
With no options, lists all snapshots for the template, newest first:
Versions for pages/about:
Timestamp Date Age
---------- ------------------- ----------
1714588200 2026-05-01 10:30:00 2h ago
1714501800 2026-04-30 10:30:00 1d ago
1714415400 2026-04-29 10:30:00 2d ago
Restore with: tcms builder:history pages/about --restore=<timestamp>
Show Mode (--show)
Prints the verbatim contents of the snapshot to stdout — useful for diffing against the current file:
tcms builder:history pages/about --show=1714501800 > /tmp/old.twig
diff /tmp/old.twig tcms-data/builder/pages/about.twig
Restore Mode (--restore)
Restores the snapshot to be the current template content. The restore is reversible — saving captures a fresh snapshot of the current contents before overwriting, so you can always undo a restore by restoring the new newest timestamp.
# Roll back to yesterday's version
tcms builder:history pages/about --restore=1714501800
# → Restored to version 2026-04-30 10:30:00
# Decide it was a mistake — list to find the snapshot from the restore
tcms builder:history pages/about
# Restore the version that was current before the rollback
tcms builder:history pages/about --restore=<that-newest-timestamp>
Storage and Retention
Snapshots live at tcms-data/builder/.history/{path}/{timestamp}.twig. The 50 newest snapshots per template are retained; older ones are pruned automatically on each save. You don't need to manage them manually.
See Template History for the same workflow from the admin UI perspective.