@bettercms-ai/mcp 0.78.0 → 0.79.0
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 +148 -193
- package/dist/index.js.map +1 -1
- package/dist/kit-catalog.json +147 -0
- package/dist/skills/bettercms/SKILL.md +4 -4
- package/dist/skills/bettercms/references/animation.md +3 -3
- package/dist/skills/bettercms/references/content-modeling.md +10 -5
- package/dist/skills/bettercms/references/finish-site.md +13 -9
- package/dist/skills/bettercms/references/getting-started.md +23 -21
- package/dist/skills/bettercms/references/publishing.md +1 -1
- package/dist/skills/bettercms/references/site-blueprints.md +4 -4
- package/dist/skills/bettercms/references/write-limits.md +0 -1
- package/package.json +1 -1
package/dist/kit-catalog.json
CHANGED
|
@@ -1,5 +1,152 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$comment": "GENERATED from the dashboard's Kit Library (KIT_LIBRARY + EXAMPLES) by scripts/kit-export.ts. Do not edit. Regenerate (dashboard: bun run kit:export -- --backend <backend checkout>).",
|
|
3
|
+
"fieldConfig": {
|
|
4
|
+
"read": {
|
|
5
|
+
"*": [
|
|
6
|
+
"placeholder",
|
|
7
|
+
"zones",
|
|
8
|
+
"options",
|
|
9
|
+
"fields"
|
|
10
|
+
],
|
|
11
|
+
"text": [
|
|
12
|
+
"minLength",
|
|
13
|
+
"maxLength",
|
|
14
|
+
"pattern",
|
|
15
|
+
"patternMessage",
|
|
16
|
+
"inputType"
|
|
17
|
+
],
|
|
18
|
+
"longtext": [
|
|
19
|
+
"minLength",
|
|
20
|
+
"maxLength",
|
|
21
|
+
"pattern",
|
|
22
|
+
"patternMessage",
|
|
23
|
+
"inputType"
|
|
24
|
+
],
|
|
25
|
+
"url": [
|
|
26
|
+
"minLength",
|
|
27
|
+
"maxLength",
|
|
28
|
+
"pattern",
|
|
29
|
+
"patternMessage",
|
|
30
|
+
"inputType"
|
|
31
|
+
],
|
|
32
|
+
"richtext": [
|
|
33
|
+
"minLength",
|
|
34
|
+
"maxLength",
|
|
35
|
+
"pattern",
|
|
36
|
+
"patternMessage",
|
|
37
|
+
"inputType"
|
|
38
|
+
],
|
|
39
|
+
"document": [
|
|
40
|
+
"minLength",
|
|
41
|
+
"maxLength",
|
|
42
|
+
"pattern",
|
|
43
|
+
"patternMessage",
|
|
44
|
+
"inputType"
|
|
45
|
+
],
|
|
46
|
+
"number": [
|
|
47
|
+
"min",
|
|
48
|
+
"max",
|
|
49
|
+
"step",
|
|
50
|
+
"scale",
|
|
51
|
+
"unit",
|
|
52
|
+
"validation"
|
|
53
|
+
],
|
|
54
|
+
"rating": [
|
|
55
|
+
"min",
|
|
56
|
+
"max",
|
|
57
|
+
"step",
|
|
58
|
+
"scale",
|
|
59
|
+
"unit",
|
|
60
|
+
"validation"
|
|
61
|
+
],
|
|
62
|
+
"select": [
|
|
63
|
+
"multiSelect"
|
|
64
|
+
],
|
|
65
|
+
"multiselect": [
|
|
66
|
+
"min",
|
|
67
|
+
"max",
|
|
68
|
+
"allowCustom"
|
|
69
|
+
],
|
|
70
|
+
"tags": [
|
|
71
|
+
"min",
|
|
72
|
+
"max",
|
|
73
|
+
"allowCustom"
|
|
74
|
+
],
|
|
75
|
+
"code": [
|
|
76
|
+
"language",
|
|
77
|
+
"languages"
|
|
78
|
+
],
|
|
79
|
+
"date": [
|
|
80
|
+
"timezone",
|
|
81
|
+
"includeTime"
|
|
82
|
+
],
|
|
83
|
+
"datetime": [
|
|
84
|
+
"timezone",
|
|
85
|
+
"includeTime"
|
|
86
|
+
],
|
|
87
|
+
"reference": [
|
|
88
|
+
"contentModelId",
|
|
89
|
+
"polymorphic",
|
|
90
|
+
"allowedModelSlugs",
|
|
91
|
+
"targets",
|
|
92
|
+
"filter"
|
|
93
|
+
],
|
|
94
|
+
"multi-reference": [
|
|
95
|
+
"contentModelId",
|
|
96
|
+
"polymorphic",
|
|
97
|
+
"allowedModelSlugs",
|
|
98
|
+
"targets",
|
|
99
|
+
"filter",
|
|
100
|
+
"min",
|
|
101
|
+
"max"
|
|
102
|
+
],
|
|
103
|
+
"array": [
|
|
104
|
+
"itemType",
|
|
105
|
+
"minItems",
|
|
106
|
+
"maxItems",
|
|
107
|
+
"item"
|
|
108
|
+
],
|
|
109
|
+
"image": [
|
|
110
|
+
"media"
|
|
111
|
+
],
|
|
112
|
+
"modular": [
|
|
113
|
+
"blockSlugs",
|
|
114
|
+
"minItems",
|
|
115
|
+
"maxItems"
|
|
116
|
+
]
|
|
117
|
+
},
|
|
118
|
+
"host": {
|
|
119
|
+
"any": [
|
|
120
|
+
"sectionLabel",
|
|
121
|
+
"sectionShape",
|
|
122
|
+
"sectionTitle",
|
|
123
|
+
"format"
|
|
124
|
+
],
|
|
125
|
+
"richtext": [
|
|
126
|
+
"accept",
|
|
127
|
+
"allowMultipleParagraphs",
|
|
128
|
+
"inline",
|
|
129
|
+
"allowTargetBlank",
|
|
130
|
+
"components",
|
|
131
|
+
"hostTag",
|
|
132
|
+
"features"
|
|
133
|
+
]
|
|
134
|
+
},
|
|
135
|
+
"server": [
|
|
136
|
+
"seeded",
|
|
137
|
+
"shared",
|
|
138
|
+
"sharedValue",
|
|
139
|
+
"attr",
|
|
140
|
+
"numeric",
|
|
141
|
+
"iconFont",
|
|
142
|
+
"htmlClasses",
|
|
143
|
+
"units",
|
|
144
|
+
"lead",
|
|
145
|
+
"band",
|
|
146
|
+
"family",
|
|
147
|
+
"shape"
|
|
148
|
+
]
|
|
149
|
+
},
|
|
3
150
|
"categories": [
|
|
4
151
|
{
|
|
5
152
|
"name": "Navigation & chrome",
|
|
@@ -57,8 +57,8 @@ Listing tools needs no sign-in; the first tool **call** does.
|
|
|
57
57
|
These hold on every task. The reference in brackets has the detail and the codes.
|
|
58
58
|
|
|
59
59
|
1. **Confirm the bound project before any write**: §9 of the schema playbook, then `get_project`.
|
|
60
|
-
Its `authoringPreference`
|
|
61
|
-
|
|
60
|
+
Its `authoringPreference` defaults to components (recorded by a deploy, a promote or setup)
|
|
61
|
+
and is never asked; the owner can switch it to fields.
|
|
62
62
|
2. **Never create SEO fields** (§5b). Page and entry meta are native columns. [seo.md]
|
|
63
63
|
3. **A page is a list of sections**, never loose top-level blocks, and never a nav, footer or
|
|
64
64
|
modal: chrome is the Layout. A call to action is one `button` field, never a text field
|
|
@@ -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.
|
|
@@ -235,6 +235,11 @@ coverage as `help`. The platform writes a line only for the four reasons above a
|
|
|
235
235
|
elements with none; clear the finding by running `editor_setup`, or by writing one of the four
|
|
236
236
|
kinds yourself, never with filler.
|
|
237
237
|
|
|
238
|
+
**Config keys.** Write only the `config` keys a field type documents (`maxLength`, `min`/`max`,
|
|
239
|
+
`options`, `itemType`, `zones`, a rich text's `accept`/`inline`): any other key sends that field to
|
|
240
|
+
the old classic editor. The import's own keys (`attr`, `numeric`, `units`, `band`, `seeded`, …) are the
|
|
241
|
+
server's: both MCP surfaces drop them from what you write.
|
|
242
|
+
|
|
238
243
|
## 6. Changing a schema safely
|
|
239
244
|
|
|
240
245
|
A schema write alone is not a release. Before one, read the model with `get_content_model`, a few
|
|
@@ -323,7 +328,7 @@ production cannot store it. Translate before the first write.
|
|
|
323
328
|
| `boolean` | `boolean` | `boolean` |
|
|
324
329
|
| `slug` | `slug` (an entry's own slug stays on the entry) | `text` |
|
|
325
330
|
| `url`, `link` | `link` | `link`; `url` for a media or embed address |
|
|
326
|
-
| `email`, `phone` | `email
|
|
331
|
+
| `email`, `phone` | `email`; a phone number is `text` with `richText: false` (`phone` opens only in the classic editor) | `text` |
|
|
327
332
|
| `date`, `datetime` | `date`, `datetime` | `date` (`YYYY-MM-DD`); a datetime stays `text` |
|
|
328
333
|
| `color` | `color` | `color` |
|
|
329
334
|
| `select` | `select` | `select` with `config.options` |
|
|
@@ -341,14 +346,14 @@ production cannot store it. Translate before the first write.
|
|
|
341
346
|
| `object` | `group` | `group` |
|
|
342
347
|
| `array` of objects | `repeater`; item limits in `config.minItems` / `config.maxItems` (the editor shows "max N") | `table` |
|
|
343
348
|
| `array` of strings | `array` (`itemType: "text"`) | `table` |
|
|
344
|
-
| `array` of several item shapes | `
|
|
345
|
-
| `slot` | `sections` or `component-ref` | `slot` (one instance; a list with `config.multiple`) |
|
|
349
|
+
| `array` of several item shapes | one component per shape, placed as sections; on a model, a `repeater` whose rows carry each shape's fields (`modular` opens only in the classic editor) | separate components |
|
|
350
|
+
| `slot` | components placed on the page (componentize); never a `sections` or `component-ref` field, which the page form cannot edit | `slot` (one instance; a list with `config.multiple`) |
|
|
346
351
|
| a form `reference` | a `form` block inside the section | `form` |
|
|
347
352
|
|
|
348
353
|
V2 features with no production shape are dropped and reported, never faked: a per-cell table
|
|
349
354
|
type, per-breakpoint values (only `style.hideOn` differs by breakpoint) and per-field
|
|
350
355
|
localization. Two have a shape now: typed table columns are a `repeater` with one field per column
|
|
351
|
-
|
|
356
|
+
(never `ui.layout: "table"`, which the editor cannot draw), and a slot with several children is a `slot` prop with `config.multiple`
|
|
352
357
|
([components.md](components.md) §1).
|
|
353
358
|
|
|
354
359
|
| The plan says | Production word |
|
|
@@ -362,7 +367,7 @@ and `ui.layout: "table"`, and a slot with several children is a `slot` prop with
|
|
|
362
367
|
| Variant, layout | Components sharing one `sectionType` ([components.md](components.md)) |
|
|
363
368
|
| Listing | `collection` block (§4) |
|
|
364
369
|
| Picks | `multi-reference` |
|
|
365
|
-
| Polymorphic array, modules |
|
|
370
|
+
| Polymorphic array, modules | one component per shape |
|
|
366
371
|
|
|
367
372
|
Tools and reports use the production word. Labels and help an editor reads never say
|
|
368
373
|
"document": the `document` field is labelled Body.
|
|
@@ -87,9 +87,11 @@ its number, a `fixCode` and a `fix`, the call that closes it.
|
|
|
87
87
|
(`fixCode`, `count`, the `fix` of its first row, the first `routes`), two with the same count in a
|
|
88
88
|
fixed order. Run it on every row that carries that `fixCode`, call `get_next_steps`, and repeat
|
|
89
89
|
until `complete`: following it reaches `complete` with no step that changes nothing. A fix no
|
|
90
|
-
tool can make is named only when nothing else is left. A `fix` of `null` means no tool closes that row
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
tool can make is named only when nothing else is left. A `fix` of `null` means no tool closes that row by itself: its
|
|
91
|
+
`detail` names what to change in the site's code, so change it, commit, push and `rebuild_project`.
|
|
92
|
+
Only a collection's listing route, a URL that cannot be placed and two pages nobody but the owner
|
|
93
|
+
can choose between are left; record each as an exception with a true reason, never stop to ask. A
|
|
94
|
+
row its fix did not move twice is recorded the same way.
|
|
93
95
|
|
|
94
96
|
A `sections` or `style` row is a page nobody can add a section to, reorder or space, however
|
|
95
97
|
complete its copy is. Its `fixCode` names the one fix, and `fix` the call when a tool can make it:
|
|
@@ -97,8 +99,8 @@ complete its copy is. Its `fixCode` names the one fix, and `fix` the call when a
|
|
|
97
99
|
| `fixCode` | The fix |
|
|
98
100
|
|---|---|
|
|
99
101
|
| `run-sections-conversion` | Only the site's code can draw this page's sections: its app renders the route, a framework hydrates the page whole (`PAGE_HYDRATED`), or the release could not map its bands. In a checkout run `npx @bettercms-ai/convert@latest --sections --root .` ([componentize.md](componentize.md) §5), or call `componentize_sections` with `codemod: true` and the plan's `digest`; `--upgrade-sections` for a `style` row. Then push and release |
|
|
100
|
-
| `rebuild` | The build that serves is older than what makes the row pass: a published section, the wrap's commit (`wrap: "wrapped"` on a row that still fails), or the in-place switch.
|
|
101
|
-
| `enable-in-place-structure` | A static page the release can write by itself, and the project's in-place structure switch is off. Setup switches it on for a project it creates; on an existing one
|
|
102
|
+
| `rebuild` | The build that serves is older than what makes the row pass: a published section, the wrap's commit (`wrap: "wrapped"` on a row that still fails), or the in-place switch. Push what changed, then `rebuild_project` and `get_deploy_status { waitSeconds: 50 }` until it is live |
|
|
103
|
+
| `enable-in-place-structure` | A static page the release can write by itself, and the project's in-place structure switch is off. Setup switches it on for a project it creates; on an existing one, `update_project { structureInPlace: true }`. No repository change |
|
|
102
104
|
| `run-setup` | Nothing measured what the row reads, or a page is still to register: `editor_setup { force: true }`, then read again |
|
|
103
105
|
|
|
104
106
|
The other rows name their fix the same way:
|
|
@@ -189,7 +191,8 @@ Where the markup says no more it numbers them (`Pricing text 4`, `Hero image 2`)
|
|
|
189
191
|
whose classes name nothing is called by its heading's words. No build can say that a picture is
|
|
190
192
|
the dashboard screenshot; the site's code can. So these never hold the gate open: they are
|
|
191
193
|
`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
|
|
194
|
+
renamed. Do it in the same job, after the gate's own rows and before the final report: an
|
|
195
|
+
editor reads these names all day, and a setup that leaves them is not finished.
|
|
193
196
|
|
|
194
197
|
Name from the site's code, the way a developer writes a schema. Open the section's source file
|
|
195
198
|
and use what it calls things (`plan.cents`, `billingFrequency`, `StatsBand.astro`):
|
|
@@ -202,12 +205,13 @@ and use what it calls things (`plan.cents`, `billingFrequency`, `StatsBand.astro
|
|
|
202
205
|
| 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
206
|
| 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
207
|
|
|
205
|
-
**Advice is not work left
|
|
208
|
+
**Advice is not work left**, except the names. `gate.naming` is part of the job: rename every
|
|
209
|
+
row before you report, as above. `receipt.gate.advice`, `gate.families` and `gate.kit`
|
|
206
210
|
are things a person may want to change about the site's own content or build: a title longer than
|
|
207
211
|
a search result shows, no default share image, an element outside any section, a route family that
|
|
208
212
|
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,
|
|
213
|
+
every one of them standing. When `gate.complete` is true and `gate.naming` is absent or empty the setup is
|
|
214
|
+
DONE: say so, list the advice in a few lines as optional improvements, and never call any of it pending, blocked,
|
|
211
215
|
remaining or left over, and never end on a question about it.
|
|
212
216
|
|
|
213
217
|
A section says two things: what it IS (its kind) and what it SAYS (its title). Only the kind is
|
|
@@ -11,11 +11,9 @@ for what it leaves out.
|
|
|
11
11
|
|
|
12
12
|
## Communication style
|
|
13
13
|
|
|
14
|
-
Be succinct. Say what you did ("Converted 12 routes; 3 values still unmatched on /pricing")
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
Before starting, tell the user they can pause and resume anytime by saying
|
|
18
|
-
**"Continue BetterCMS setup"**.
|
|
14
|
+
Be succinct. Say what you did ("Converted 12 routes; 3 values still unmatched on /pricing") and
|
|
15
|
+
skip narrating what you are about to do. Give numbers, not adjectives. Finish the setup in this
|
|
16
|
+
run: do not stop to ask anything the sections below give an answer for.
|
|
19
17
|
|
|
20
18
|
**RESUME TRIGGER.** On "Continue BetterCMS setup", read `get_project`, `get_next_steps` and
|
|
21
19
|
`get_binding_report` for the project and the gap ledger at `.bettercms/ledger.json` if it exists,
|
|
@@ -42,17 +40,16 @@ Check: the MCP answers for this project id, and this folder is its repo.
|
|
|
42
40
|
added while you run loads only when the agent starts, and every step below works on that
|
|
43
41
|
folder's source.
|
|
44
42
|
|
|
45
|
-
### Phase 2 —
|
|
46
|
-
|
|
47
|
-
Check: `get_project` shows `authoringPreference` set and the user has answered media delivery.
|
|
43
|
+
### Phase 2 — No questions: the defaults are recorded
|
|
48
44
|
|
|
49
|
-
|
|
45
|
+
Nothing here is asked. Record and report:
|
|
50
46
|
|
|
51
|
-
1. **Authoring**:
|
|
52
|
-
|
|
53
|
-
2. **Media delivery**:
|
|
47
|
+
1. **Authoring**: components. A deploy, a promote and every `editor_setup` record it when nothing
|
|
48
|
+
is set; call `set_authoring_preference` only if the prompt you were started with names `fields`.
|
|
49
|
+
2. **Media delivery**: the BetterCMS CDN, the project's default. Leave it.
|
|
50
|
+
3. **Git**: the prompt you were started with says whether you may push. Follow it; never ask.
|
|
54
51
|
|
|
55
|
-
Everything else has a default. Do not ask about it; decide, then report what you decided:
|
|
52
|
+
Everything else has a default too. Do not ask about it; decide, then report what you decided:
|
|
56
53
|
|
|
57
54
|
- **Scope is the whole site**: every page, section, heading, paragraph, list item, link and image
|
|
58
55
|
the site renders. Not the home page first, not "the main copy".
|
|
@@ -62,8 +59,9 @@ Everything else has a default. Do not ask about it; decide, then report what you
|
|
|
62
59
|
|
|
63
60
|
### Phase 3 — Convert and componentize
|
|
64
61
|
|
|
65
|
-
Check: `editor_setup { check: true }` reads `ready`, `
|
|
66
|
-
and `get_componentize_plan` proposes nothing more.
|
|
62
|
+
Check: `editor_setup { check: true }` reads `ready`, `get_next_steps` returns
|
|
63
|
+
`receipt.complete: true` and `get_componentize_plan` proposes nothing more. Where the job was to
|
|
64
|
+
convert the source, `submit_conversion_receipt` also returned complete.
|
|
67
65
|
|
|
68
66
|
0. A build brings in only SEO and meta, media, the brand and the models `bcms-content.json`
|
|
69
67
|
declares. Pages, collections, forms, bindings and components come from `editor_setup`: run it
|
|
@@ -79,10 +77,13 @@ and `get_componentize_plan` proposes nothing more.
|
|
|
79
77
|
through you. If they are not found there and the site loads its content from another CMS,
|
|
80
78
|
move that content into BetterCMS ([external-content.md](external-content.md)). Never ask the
|
|
81
79
|
user for a key or read one: a site left depending on missing keys renders empty or crashes.
|
|
82
|
-
3.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
80
|
+
3. Work from `get_next_steps`: run `receipt.gate.nextAction`, read it again after each batch, and
|
|
81
|
+
repeat until `receipt.complete` is true. On a site set up from its build that gate is the only
|
|
82
|
+
finish line. Run the `convert_site` prompt only when the job is to convert the source (the gate
|
|
83
|
+
asks for it, the owner wants the code to declare its bindings, or the project is headless), and
|
|
84
|
+
then to its own complete receipt, never a partial one.
|
|
85
|
+
4. With components recorded: `get_componentize_plan`, then apply it with
|
|
86
|
+
`componentize_sections` (the owner can undo it), then the repository half (`componentize_sections { codemod: true }`
|
|
86
87
|
when there is no checkout) and `submit_componentize_receipt` ([componentize.md](componentize.md)).
|
|
87
88
|
5. Work the rest through [finish-site.md](finish-site.md) (gap ledger, repair order, proof loop).
|
|
88
89
|
The gate's `sections` and `style` rows are part of it: a setup whose pages cannot take a new
|
|
@@ -96,8 +97,9 @@ Check: the deployed site passes the numbers below on every route.
|
|
|
96
97
|
|
|
97
98
|
1. Deploy the way this project deploys: `deploy_project` on BetterCMS hosting; on a headless
|
|
98
99
|
project, push to the repo its host builds (never `deploy_project` there).
|
|
99
|
-
2. `
|
|
100
|
-
`unaddressable` below 2% on each route
|
|
100
|
+
2. `get_next_steps` reading `receipt.complete: true` is the finish line. `get_binding_report`
|
|
101
|
+
says why a route is short of it: `unaddressable` below 2% on each route, and, on a source you
|
|
102
|
+
converted, zero unmatched and an empty `coverage.pending`. `refreshReason: "never-generated"` on headless means
|
|
101
103
|
fetch the live site instead.
|
|
102
104
|
3. Run the verify loop in SKILL.md against the live URL, then the two-path smoke test (click
|
|
103
105
|
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 |
|
|
@@ -122,11 +122,11 @@ every entry and are not repeated in the tables. SEO fields are never listed.
|
|
|
122
122
|
| Map pin | `location` (lat, lng, optional label). Never two text fields |
|
|
123
123
|
| Tags, amenities, bullet lists | `array` with `itemType: "text"`. One choice from a fixed list is `select` |
|
|
124
124
|
| Rows of the same shape (hours, facts, metrics, tickets) | `repeater` of `text`, `number`, `select`, `boolean` children |
|
|
125
|
-
| Mixed ordered blocks in a case study |
|
|
125
|
+
| Mixed ordered blocks in a case study | one component per block shape, placed as sections |
|
|
126
126
|
| One or many other entries | `reference`, `multi-reference`. A missing target is refused on write |
|
|
127
|
-
| Call to action | `button` (label and link in one value). A bare link is `link`; also `email
|
|
127
|
+
| Call to action | `button` (label and link in one value). A bare link is `link`; also `email`; a phone number is `text` |
|
|
128
128
|
| Date, date and time | `date`, `datetime`. Keep the zone as a `text` IANA name beside it when the site shows times |
|
|
129
|
-
| A spec or comparison table | `repeater` with one field per column (any field type)
|
|
129
|
+
| A spec or comparison table | `repeater` with one field per column (any field type). There is no separate table field type |
|
|
130
130
|
|
|
131
131
|
An external system's id (`"store_product_id"`, `"ats_job_id"`, `"feed_id"`, `"lms_course_id"`)
|
|
132
132
|
is a `text` field kept as migration metadata and locked: `readOnly: "developer"` with a
|
|
@@ -261,7 +261,7 @@ Projects collection, `urlPattern` `/work/:slug` (or the established prefix):
|
|
|
261
261
|
| `"year"` | `number` | |
|
|
262
262
|
| `"services"` | `multi-reference` to Services, or `array` of text | References when service pages exist |
|
|
263
263
|
| `"cover_image"`, `"excerpt"` | `image`, `longtext` | Words baked into a hero image move into fields |
|
|
264
|
-
| `"modules"` or `"body"` |
|
|
264
|
+
| `"modules"` or `"body"` | components, or `document` | one component per block (text, image, gallery, quote, video, stats) for image-led studies; `document` when mostly prose |
|
|
265
265
|
| `"results"` | `repeater`: `"value"`, `"label"` | Only verified outcomes |
|
|
266
266
|
| `"credits"` | `repeater`: `"role"`, `"name"` | |
|
|
267
267
|
| `"testimonial"` | `reference` to Testimonials | |
|
|
@@ -207,7 +207,6 @@ write new rich text in that shape.
|
|
|
207
207
|
| 409 `unique_conflict` | Another entry already holds this value in a unique field. `conflicts` names it | Match to that entry and update it; do not create a twin |
|
|
208
208
|
| 409 `locale_conflict` | The translation group already has an entry in that locale. `errors` names `locale` or `translationOf` | Edit the existing translation |
|
|
209
209
|
| 409 `Version conflict` (entries), `VERSION_CONFLICT` (sidebar, section zones) | Someone wrote since your read. The entry body carries `currentVersion` | Read again, re-apply your change to the fresh value, write with the new version |
|
|
210
|
-
| 409 `AUTHORING_DECISION_REQUIRED` | Deploy or promote was called before a human chose components or fields. `message` is the question | Ask the user, then `set_authoring_preference`. Do not retry or route around it |
|
|
211
210
|
| 409 `stale-plan` | The site changed after the componentize plan was read; `digest` is the current one | `get_componentize_plan` again, re-confirm, resubmit with the new digest |
|
|
212
211
|
| 409 `style-edits` | The conversion receipt lists changed classes or styles. `styleEdits` holds up to 40 tokens | Restore every token, rerun `npx @bettercms-ai/convert --verify-styles`, resubmit the receipt |
|
|
213
212
|
| 400 `digest-mismatch` | The receipt was built for a different brief | Read the brief again and rebuild the receipt against it |
|
package/package.json
CHANGED