@bettercms-ai/mcp 0.78.0 → 0.78.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/skills/bettercms/SKILL.md +2 -2
- package/dist/skills/bettercms/references/animation.md +3 -3
- package/dist/skills/bettercms/references/finish-site.md +6 -4
- package/dist/skills/bettercms/references/getting-started.md +11 -6
- package/dist/skills/bettercms/references/publishing.md +1 -1
- package/package.json +1 -1
|
@@ -186,8 +186,8 @@ Stop at the first step that is not clean, fix, then restart. A report that does
|
|
|
186
186
|
|
|
187
187
|
1. **Fetch the live URL**, cache-busted: a 200 whose body contains copy you just published. The
|
|
188
188
|
steps below read the control plane, and all of them can be green on a site that answers 404.
|
|
189
|
-
2. `get_binding_report` for every route you touched: `
|
|
190
|
-
`mode: "declared"
|
|
189
|
+
2. `get_binding_report` for every route you touched: `unaddressable` under 2%, and, where the
|
|
190
|
+
source declares its bindings, `coverage.pending` empty and `mode: "declared"`. `refreshReason: "never-generated"` means no BetterCMS
|
|
191
191
|
release wrote one (on headless none ever will, so step 1 is the proof); `"outdated"` means
|
|
192
192
|
the next release rewrites it.
|
|
193
193
|
3. `get_site_composition`: every page you touched is made of sections, with the intended
|
|
@@ -265,7 +265,7 @@ Check: change the bound string, publish, reload: the drawn text reads the new va
|
|
|
265
265
|
## 12. In the editor's canvas
|
|
266
266
|
|
|
267
267
|
The Visual Editor shows the real page with its scripts running, and owns three things there: the
|
|
268
|
-
scroll (
|
|
268
|
+
scroll (the page scrolls its own window, as it does live), the pointer (a click selects a field)
|
|
269
269
|
and the place on the page (picking a field in the panel scrolls to it). Motion that reads the
|
|
270
270
|
window's scroll or the clock runs there as it does live. Only what takes one of those three away
|
|
271
271
|
has to stand down, and the site decides that with one question:
|
|
@@ -284,7 +284,7 @@ canvas, and false on the published page. What each kind of page does there:
|
|
|
284
284
|
| Native scroll, CSS scroll-driven animations (`animation-timeline`), Motion `useScroll` / `whileInView`, ScrollTrigger pin and scrub | runs as live | nothing |
|
|
285
285
|
| Lenis, Locomotive Scroll 5 | runs; the wheel has no easing (section 6) | nothing |
|
|
286
286
|
| GSAP ScrollSmoother | runs; the window still scrolls, the content eases behind it | nothing |
|
|
287
|
-
| Locomotive Scroll 4, any script that locks the window and moves the content by a transform | the page works under its own wheel, but the
|
|
287
|
+
| Locomotive Scroll 4, any script that locks the window and moves the content by a transform | the page works under its own wheel, but the editor cannot scroll it and a field picked in the panel is not brought into view | `smooth: !inEditor()`, so the window scrolls |
|
|
288
288
|
| A wheel-driven slide deck (GSAP `Observer`, fullPage) | the same | do not create the observer when `inEditor()`; let the sections stack and scroll |
|
|
289
289
|
| A scene with its own scroll layer (react-three-fiber `ScrollControls`, a fixed `overflow: auto` layer) | runs; picking a field scrolls that layer | nothing |
|
|
290
290
|
| An intro or preloader that holds the page (`overflow: hidden` until it ends, a full-screen cover) | plays on every reload of the canvas, and covers the page while it does | skip it when `inEditor()` |
|
|
@@ -292,5 +292,5 @@ canvas, and false on the published page. What each kind of page does there:
|
|
|
292
292
|
Never use `inEditor()` to change what a field renders, to hide a section or to stop a timeline:
|
|
293
293
|
the canvas must show what a visitor gets. It is for scroll takeover, intros and nothing else.
|
|
294
294
|
|
|
295
|
-
Check: open the page in the Visual Editor, scroll with the wheel, drag the
|
|
295
|
+
Check: open the page in the Visual Editor, scroll with the wheel, drag the page's scrollbar to the
|
|
296
296
|
footer, then pick a field of the first section in the panel: the canvas returns to it.
|
|
@@ -189,7 +189,8 @@ Where the markup says no more it numbers them (`Pricing text 4`, `Hero image 2`)
|
|
|
189
189
|
whose classes name nothing is called by its heading's words. No build can say that a picture is
|
|
190
190
|
the dashboard screenshot; the site's code can. So these never hold the gate open: they are
|
|
191
191
|
`receipt.gate.naming`, and `get_next_steps` lists the step `names-from-code` until each is
|
|
192
|
-
renamed. Do it in the same job, after the gate's own rows
|
|
192
|
+
renamed. Do it in the same job, after the gate's own rows and before the final report: an
|
|
193
|
+
editor reads these names all day, and a setup that leaves them is not finished.
|
|
193
194
|
|
|
194
195
|
Name from the site's code, the way a developer writes a schema. Open the section's source file
|
|
195
196
|
and use what it calls things (`plan.cents`, `billingFrequency`, `StatsBand.astro`):
|
|
@@ -202,12 +203,13 @@ and use what it calls things (`plan.cents`, `billingFrequency`, `StatsBand.astro
|
|
|
202
203
|
| A field or prop | what it HOLDS there, one to three words | `Dismiss key`, `Starter plan price`, `Dashboard screenshot`, `Billing frequency` | its own copy, a role with a number (`Text 4`), the section's name alone |
|
|
203
204
|
| Help text | most fields get none. One plain sentence under 90 characters, only for a format or unit, a place the canvas cannot show, a real constraint or a fallback. No semicolons, no dashes, no values from the page | `Leave empty to use the page title.` | the label or the value said again, writing advice, a figure copied from another page |
|
|
204
205
|
|
|
205
|
-
**Advice is not work left
|
|
206
|
+
**Advice is not work left**, except the names. `gate.naming` is part of the job: rename every
|
|
207
|
+
row before you report, as above. `receipt.gate.advice`, `gate.families` and `gate.kit`
|
|
206
208
|
are things a person may want to change about the site's own content or build: a title longer than
|
|
207
209
|
a search result shows, no default share image, an element outside any section, a route family that
|
|
208
210
|
could be a collection, a kit entry to confirm, a name to sharpen. The site is fully editable with
|
|
209
|
-
every one of them standing. When `gate.complete` is true
|
|
210
|
-
advice in a few lines as optional improvements, and never call any of it pending, blocked,
|
|
211
|
+
every one of them standing. When `gate.complete` is true and `gate.naming` is absent or empty the setup is
|
|
212
|
+
DONE: say so, list the advice in a few lines as optional improvements, and never call any of it pending, blocked,
|
|
211
213
|
remaining or left over, and never end on a question about it.
|
|
212
214
|
|
|
213
215
|
A section says two things: what it IS (its kind) and what it SAYS (its title). Only the kind is
|
|
@@ -62,8 +62,9 @@ Everything else has a default. Do not ask about it; decide, then report what you
|
|
|
62
62
|
|
|
63
63
|
### Phase 3 — Convert and componentize
|
|
64
64
|
|
|
65
|
-
Check: `editor_setup { check: true }` reads `ready`, `
|
|
66
|
-
and `get_componentize_plan` proposes nothing more.
|
|
65
|
+
Check: `editor_setup { check: true }` reads `ready`, `get_next_steps` returns
|
|
66
|
+
`receipt.complete: true` and `get_componentize_plan` proposes nothing more. Where the job was to
|
|
67
|
+
convert the source, `submit_conversion_receipt` also returned complete.
|
|
67
68
|
|
|
68
69
|
0. A build brings in only SEO and meta, media, the brand and the models `bcms-content.json`
|
|
69
70
|
declares. Pages, collections, forms, bindings and components come from `editor_setup`: run it
|
|
@@ -79,8 +80,11 @@ and `get_componentize_plan` proposes nothing more.
|
|
|
79
80
|
through you. If they are not found there and the site loads its content from another CMS,
|
|
80
81
|
move that content into BetterCMS ([external-content.md](external-content.md)). Never ask the
|
|
81
82
|
user for a key or read one: a site left depending on missing keys renders empty or crashes.
|
|
82
|
-
3.
|
|
83
|
-
|
|
83
|
+
3. Work from `get_next_steps`: run `receipt.gate.nextAction`, read it again after each batch, and
|
|
84
|
+
repeat until `receipt.complete` is true. On a site set up from its build that gate is the only
|
|
85
|
+
finish line. Run the `convert_site` prompt only when the job is to convert the source (the gate
|
|
86
|
+
asks for it, the owner wants the code to declare its bindings, or the project is headless), and
|
|
87
|
+
then to its own complete receipt, never a partial one.
|
|
84
88
|
4. When the answer was components: `get_componentize_plan`, confirm it with the user, apply it
|
|
85
89
|
with `componentize_sections`, then the repository half (`componentize_sections { codemod: true }`
|
|
86
90
|
when there is no checkout) and `submit_componentize_receipt` ([componentize.md](componentize.md)).
|
|
@@ -96,8 +100,9 @@ Check: the deployed site passes the numbers below on every route.
|
|
|
96
100
|
|
|
97
101
|
1. Deploy the way this project deploys: `deploy_project` on BetterCMS hosting; on a headless
|
|
98
102
|
project, push to the repo its host builds (never `deploy_project` there).
|
|
99
|
-
2. `
|
|
100
|
-
`unaddressable` below 2% on each route
|
|
103
|
+
2. `get_next_steps` reading `receipt.complete: true` is the finish line. `get_binding_report`
|
|
104
|
+
says why a route is short of it: `unaddressable` below 2% on each route, and, on a source you
|
|
105
|
+
converted, zero unmatched and an empty `coverage.pending`. `refreshReason: "never-generated"` on headless means
|
|
101
106
|
fetch the live site instead.
|
|
102
107
|
3. Run the verify loop in SKILL.md against the live URL, then the two-path smoke test (click
|
|
103
108
|
through to a detail page, clean console, direct reload) in [frameworks.md](frameworks.md)
|
|
@@ -266,7 +266,7 @@ for a content value the sync got wrong is §4.
|
|
|
266
266
|
| Need | Surface | Owner |
|
|
267
267
|
|---|---|---|
|
|
268
268
|
| Rebuild or revalidate a site on publish | The publish webhook, signed `X-BetterCMS-Signature` (HMAC-SHA256 of the raw body) with the project's webhook secret ([frameworks.md](frameworks.md) Revalidation) | Secret set in the dashboard; your route verifies it |
|
|
269
|
-
| Notify another system of an event | Event webhooks, `list_webhooks` / `create_webhook`: `content.publish`, `content.unpublish`, `content.stage_changed` (workflow stage), `content.created`, `content.updated`, `content.deleted` and `deployment.triggered`; HMAC-SHA256 signed in `X-BetterCMS-Signature`; payloads carry ids, never content. The signing `secret` is returned ONCE, on create. A receiver that writes back on `content.updated` loops unless it skips its own writes. Per-subscription payload filters: dashboard | You |
|
|
269
|
+
| Notify another system of an event | Event webhooks, `list_webhooks` / `create_webhook`: `content.publish`, `content.unpublish`, `content.stage_changed` (workflow stage), `content.created`, `content.updated`, `content.deleted` and `deployment.triggered`; HMAC-SHA256 signed in `X-BetterCMS-Signature`; payloads carry ids, never content: an entry's `content.publish`/`content.unpublish` carries `data.entryId`, a page's carries `data.pageId` and `kind: "page"` (so `entryId` is not always present). The signing `secret` is returned ONCE, on create. A receiver that writes back on `content.updated` loops unless it skips its own writes. Per-subscription payload filters: dashboard | You |
|
|
270
270
|
| Send form submissions somewhere | Per-form integrations: Google Sheets, Slack, a webhook, email | Dashboard only |
|
|
271
271
|
| Go live or retire at a time | `scheduledAt` (publish a draft at a time; needs publish permission) and `unpublishAt` (return a published row to draft) on `update_content_entry` and `update_page`; `null` cancels either. Campaigns: `publish_campaign` takes both | You. An edit that omits `scheduledAt` CANCELS the pending publish, so resend it with every edit before it fires; omitting `unpublishAt` never cancels it |
|
|
272
272
|
| Show one section only for a period | `window` `{ from?, until? }` on a page block (`update_page` `blockJson`) or on a Layout Section (`update_layout` `add-section`, `update-section`). Shown from `from` until `until`; each is an ISO 8601 datetime with an offset (`2026-11-27T00:00:00+01:00`), stored as UTC, and either may be left out. `null` clears it. Refusals: `invalid_window` (no offset, or not a date), `window_inverted` (`from` not before `until`), `window_in_component` (inside a component's `blockJson`: put it on the placement), `window_in_entry` (on a block inside an entry's sections value: put it on the page block or Layout Section that shows the entry). A Section a page adds or customizes takes a window like a Global one | You. Outside its window the block is in no published output: the page, its Markdown, the delivery API and search. A draft preview draws it, marked. A page BetterCMS renders per request changes at the boundary; a static site catches up within about a minute, in place when a section goes and with a build when one appears. Site code that renders its own header ignores a Section's window |
|
package/package.json
CHANGED