Jetzt mit KI-gestütztem Page Building über den MCP-Server

Die cmssy-CLI

init erzeugt die Verdrahtung in einer bestehenden App, add block gerüstet und registriert einen Block, link verbindet die App mit einem Workspace und beweist, dass es funktioniert.

Drei Befehle, jeder für einen Moment, der früher Handarbeit war: eine neue App verdrahten, einen Block hinzufügen, mit einem Workspace verbinden.

cmssy init

cmssy gerüstet nie deine App. Das Framework ist ein Adapter, nie das Fundament - du legst die App mit der CLI von Next.js an, und init ergänzt die cmssy-Verdrahtung.

npx create-next-app@latest my-site
cd my-site
npx @cmssy/cli init

Die CLI zielt auf Next.js mit App Router und braucht Node 18.18+. Astro, Remix und React Router unterstützt das SDK selbst - die verdrahtest du von Hand statt über init.

Sie ist idempotent: Eine bereits vorhandene Datei wird übersprungen und als übersprungen gemeldet, nie überschrieben. Zweimal ausgeführt ändert der zweite Lauf nichts.

Was es schreibt

cmssy.config.ts
proxy.ts
next.config.mjs
env.example
cmssy/blocks.ts          Block-Registry
cmssy/editor.tsx         Editor-Bridge
blocks/hero/block.ts
blocks/hero/Hero.tsx
blocks/hero/Hero.module.css
app/[[...path]]/page.tsx Catch-all-Route
app/api/draft/route.ts   Entwurfsvorschau

Beachte, was nicht dabei ist: kein app/sitemap.ts, kein app/robots.ts, keine /cmssy-edit-Route, kein Root-Layout, kein editierbares Layout.

Das fehlt nicht, es ist Absicht. Diese Dateien kodieren Entscheidungen über deine App - welche URLs du veröffentlichst, wie dein Chrome aussieht, wie du eine Sitemap formst - und eine Vorlage, die das rät, wäre für die meisten Projekte falsch. init gibt dir eine rendernde Seite und eine Draft-Route; den Rest ergänzt du nach Bedarf, siehe Routen und Seiten, Layouts und SEO.

Flags: --dir <pfad> zielt auf eine App außerhalb des Arbeitsverzeichnisses, --force überschreibt bestehende Verdrahtung.

cmssy add block

Jeder Block nach dem generierten hero hieß früher: Dateien von Hand kopieren und an die Registry denken.

cmssy add block pricing-table
cmssy add block faq-list --dir ../my-site

Alle Namen leiten sich vom Kebab-Case-Argument ab: pricing-table wird Typ pricing-table, Label Pricing Table, Komponente PricingTable, Export pricingTableBlock.

Es schreibt die Blockdateien und registriert sie in cmssy/blocks.ts - fügt den Import hinzu und hängt an das blocks-Array an, wobei deine Formatierung und bestehende Einträge erhalten bleiben.

Es weigert sich, irgendetwas Mehrdeutiges anzufassen: ein ungültiger Name, ein bereits registrierter Block, vorhandene Dateien oder eine Registry ohne export const blocks = [...]. Jeder Fall ist ein lauter Fehler mit ausgeschriebenem manuellem Schritt - nie ein stiller Teilschreibvorgang.

Der generierte Block startet mit einem erforderlichen heading und einem optionalen text. Props und Markup anpassen, Dev-Server neu starten, und der Editor übernimmt den neuen Typ aus dem Manifest-Handshake.

Eine App mit einem Workspace zu verbinden hieß früher, fünf Werte zwischen Dashboard und .env.local von Hand zu kopieren - und der Editor blieb tot, bis jeder davon stimmte.

npx @cmssy/cli link
cmssy link --token cs_... --workspace acme/shop --preview-url https://shop.example.com

Es authentifiziert sich mit einem API-Token (aus --token oder CMSSY_API_TOKEN; .env.local und .env werden zuerst gelesen, ohne Shell-Variablen zu überschreiben), wählt einen Workspace, liest das Draft-Secret, schreibt CMSSY_ORG_SLUG, CMSSY_WORKSPACE_SLUG und CMSSY_DRAFT_SECRET in .env.local - mergend, sodass bestehende Zeilen und Kommentare überleben - und führt dann den Preflight aus.

Das Lesen des Draft-Secrets braucht die Berechtigung PAGES_EDIT. Eine fehlende Berechtigung wird genau so gemeldet.

Die Preview-URL ist geteilt - localhost wird abgelehnt

--preview-url setzt den Origin, unter dem der Editor deine App für alle im Workspace einbettet. Ein localhost-Wert wird absichtlich abgelehnt: die geteilte Vorschau auf deine Maschine zu richten würde den Editor für jede Kollegin brechen.

Für lokale Entwicklung schalte den Dev-Modus im cmssy-Editor um und trage dort deinen lokalen Host ein. Dieses Ziel gilt pro Person und berührt nichts Geteiltes.

Die Checks

  • Workspace erreichbar - public.siteConfig antwortet für Org und Workspace. Unterscheidet falsche Slugs, Netzwerkprobleme und einen Workspace über seinem Delivery-Limit.
  • Draft-Secret - das Backend bestätigt, dass das geschriebene Secret passt. Auf einer Plattform ohne das Feld meldet es ? und macht weiter.
  • Editor-Deeplink - wird immer ausgegeben.
  • Entwurfs-Vorschaulink - wenn der Workspace eine Preview-URL meldet. Entwurfsmodus verlassen: /api/draft?disable=1.

Es gibt drei Status, nicht zwei: verifiziert, kaputt mit der Behebung in der nächsten Zeile und Exit-Code 1, und ? nicht verifizierbar - was nie blockiert. Dieser dritte Zustand existiert, weil „unbekannt“ und „kaputt“ verschiedene Dinge sind; ein Tool, das sie zusammenwirft, schreit entweder grundlos oder verbirgt einen echten Fehler.

Der Preflight ist auch eine API

Jeder Check ist eine reine Funktion in @cmssy/core, unter einem Subpfad exportiert, damit Dev-Tooling nie in dein Production-Bundle gerät:

import {
  checkWorkspaceReachable,
  checkDraftSecret,
  checkPreviewUrl,
  checkFrameAncestors,
  buildEditorUrl,
} from "@cmssy/core/preflight";

Jede liefert { status: "ok" | "fail" | "unknown", message, fix? }. Die ersten beiden sprechen mit der Delivery-API; der Rest ist reine String-Logik. Keine importiert ein Framework oder ein Node-Builtin, sie laufen also überall - Health-Endpunkt, CI-Schritt, Dashboard.

Wenn die Verdrahtung kaputt ist

In der Entwicklung schützen dieselben Checks die Edit-Route. Ein Editor-Request, der die Verifikation nicht besteht, liefert kein 404 - der Adapter rendert eine Diagnoseseite im Editor-Iframe, eine Zeile pro Check: fehlende Env-Variablen und wo man sie herbekommt, ein unerreichbarer Workspace, ein Draft-Secret-Mismatch, der Preview-URL-Vergleich und die Origins, die frame-ancestors erlauben muss.

Sie zeigt den Workspace-Slug und welcher Check fehlschlug, nie einen Secret-Wert.

Nächste Schritte