Skip to content

Builder CLI Commands Since 3.5.0

The Site Builder provides four CLI commands: scaffolding from starters, installing a frontend asset pipeline, inspecting registered routes, and managing template version history.

CommandPurpose
builder:initScaffold a new site from a starter template
builder:frontendInstall a Vite-based frontend asset pipeline
builder:routesList every route the page router would serve, with conflicts flagged
builder:historyList, view, or restore template snapshot versions

All commands accept --json for machine-readable output.

Scaffold a new site from a bundled starter template.

Terminal window
tcms builder:init business
tcms builder:init --list
tcms builder:init blog --force
tcms builder:init --json
  1. Copies template files (layouts, pages, partials) from the starter into tcms-data/builder/
  2. Creates the builder-pages collection with the builder-page schema (if it doesn’t exist)
  3. Imports the starter’s jumpstart.json — pages, plus any supporting schemas, collections, and sample objects
  4. (with --frontend) installs the Vite frontend scaffold — same as running tcms builder:frontend
ArgumentRequiredDescription
starterNoStarter template name. Omit to list available starters.
OptionDescription
--list, -lList available starters and exit
--force, -fOverwrite existing template files (existing page records in the collection are left alone — JumpStart skips objects that already exist)
--frontendAlso install the Vite frontend scaffold (equivalent to running tcms builder:frontend)
--jsonOutput result as JSON

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.

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.

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.

StarterPagesDescription
minimalHomeSingle page with clean layout
businessHome, About, Services, ContactProfessional business site
blogHome, Blog, Blog Post, AboutBlog-focused site with dynamic post routing
portfolioHome, Work, About, ContactPortfolio site with project cards
Terminal window
# 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.

Install a Vite-based frontend asset pipeline scaffold into your project. Idempotent — safe to re-run, won’t overwrite customizations unless you pass --force.

Terminal window
tcms builder:frontend
tcms builder:frontend --force
tcms builder:frontend --json

Copies the contents of resources/builder/frontend/ into <projectRoot>/frontend/:

  • vite.config.js — emits hashed assets to ../public/assets/ with manifest
  • package.jsondev / build / watch scripts; Vite as the only dependency
  • src/css/style.css — minimal starter stylesheet
  • src/js/app.js — minimal entry point
  • README.md — quick reference for the install/build workflow
  • .gitignore — excludes node_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.

OptionDescription
--force, -fOverwrite existing files in frontend/ (use with care — any customizations are lost)
--jsonOutput result as JSON

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

Section titled “When to Use This vs. --frontend on builder:init”
ScenarioUse
Greenfield project, scaffolding for the first timetcms builder:init <starter> --frontend
Existing project, adding a frontend pipeline nowtcms builder:frontend
Pull missing files after the scaffold was editedtcms builder:frontend (skips existing)
Reset the scaffold to upstream defaultstcms builder:frontend --force

See Frontend Assets for the full asset-pipeline reference (Sass, Tailwind, etc.).

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.

Terminal window
tcms builder:routes
tcms builder:routes --json

For each route:

ColumnDescription
RouteThe effective pattern the router would match
Sourcepage (builder page) or collection (collection URL)
IDPage id or collection id
TemplateTemplate path that gets rendered on a match
StatusHTTP status code the page returns
Notesdraft if excluded from routing, ⚠ duplicate if another route declares the same pattern

Collection URLs are normalized for display so you see what the router actually matches:

Stored URLEffective 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.

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.

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.

Terminal window
# 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
ArgumentRequiredDescription
pathYesTemplate path without extension (e.g. pages/about, layouts/default, partials/nav)
OptionDescription
--show=<timestamp>Print the snapshot’s contents to stdout
--restore=<timestamp>Restore the snapshot — overwrite the current template file
--jsonOutput result as JSON

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>

Prints the verbatim contents of the snapshot to stdout — useful for diffing against the current file:

Terminal window
tcms builder:history pages/about --show=1714501800 > /tmp/old.twig
diff /tmp/old.twig tcms-data/builder/pages/about.twig

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.

Terminal window
# 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>

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.