@bettercms-ai/mcp 0.64.1 → 0.65.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 +72 -19
- package/dist/index.js.map +1 -1
- package/dist/skills/bettercms/SKILL.md +3 -1
- package/dist/skills/bettercms/references/astro.md +5 -0
- package/dist/skills/bettercms/references/componentize.md +4 -2
- package/dist/skills/bettercms/references/components.md +24 -0
- package/dist/skills/bettercms/references/content-modeling.md +10 -0
- package/dist/skills/bettercms/references/next.md +2 -0
- package/dist/skills/bettercms/references/portable-text.md +2 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2270,7 +2270,8 @@ var SKILLS_ROUTING_INSTRUCTION = "Before writing code in a repo that uses Better
|
|
|
2270
2270
|
var DOCK_UI_ROUTING_INSTRUCTION = "How fields and props look in the editor (groups, tabs, rows, icons, item names) is playbook \xA714 or the `field-ui` prompt: write it with update_content_model field* or update_component prop* plus ifMatch, read it back, and finish when get_next_steps reports no dock-* step.";
|
|
2271
2271
|
var RECEIPT_FIRST_INSTRUCTION = "Start every project with get_next_steps: its `receipt` lists what is already done (`done`), what is still missing (`missing`, each item citing the count it reacted to) and the one `nextAction`. Work from that receipt instead of re-reading the site, call get_next_steps again after each batch of edits, and before you tell the user you are finished. Fix what it lists, or say why you are leaving it.";
|
|
2272
2272
|
var NEXT_STEPS_DESCRIPTION = `Call this FIRST on a project. Returns \`receipt\` \u2014 \`done\` (live release, bound elements, conversion coverage, composed pages, published components and Layout), \`missing\`, \`nextAction\` and \`notes\` (unjudged: unknown, not done) \u2014 plus \`data\`, the unfinished items: missing meta descriptions, drafts, empty collections, unnotified forms, writes awaiting approval, unstamped bindings or sections, \`authoring-undecided\` and the Layout lane (\`layout-write-blocked\`, \`layout-field-unread\`, \`layout-locator-stale\`, \`layout-rebuild-required\`, \`chrome-duplicated\`). Each item cites its count and may carry \`payload\` {rows, total}, at most 20 rows. Call it again after a batch of edits and before telling the user you are done. \`organise-content\`: pages and collections are in no folder \u2014 read ${STRUCTURE_PLAYBOOK_URI}, call suggest_content_structure and, once the user agrees, set_content_structure.`;
|
|
2273
|
-
var LAYOUT_CHROME_INSTRUCTION = 'When the user asks to make the navigation, footer or other site chrome editable, or get_next_steps lists a `layout-*` or `chrome-duplicated` item: this is playbook \xA711 item 3. Chrome is the project Layout, never page fields: in the source, wrap each Section in `data-bcms-layout-section` (the slot, its base slug) and mark each value `data-bcms-layout-field="layout:<sections[slug].id>:<fieldId>"`, never `data-bcms-field` inside it, then push; the strip removes the page copies, never delete them by hand. On a site BetterCMS serves, publish_layout writes Layout VALUES into the live pages; a structure change waits for a Rebuild.';
|
|
2273
|
+
var LAYOUT_CHROME_INSTRUCTION = 'When the user asks to make the navigation, footer or other site chrome editable, or get_next_steps lists a `layout-*` or `chrome-duplicated` item: this is playbook \xA711 item 3. Chrome is the project Layout, never page fields: in the source, wrap each Section in `data-bcms-layout-section` (the slot, its base slug) and mark each value `data-bcms-layout-field="layout:<sections[slug].id>:<fieldId>"`, never `data-bcms-field` inside it, then push; the strip removes the page copies, never delete them by hand. On a site BetterCMS serves, publish_layout writes Layout VALUES into the live pages; a structure change waits for a Rebuild. Never place nav, footer or modal blocks or fields on a page: the Layout (or the site\'s own layout) draws chrome, so a page copy either duplicates it or renders nowhere (get_componentize_plan reports one as LAYOUT_CHROME).';
|
|
2274
|
+
var CTA_BUTTON_INSTRUCTION = "A call to action is ONE `button` field (value {label, href, variant?, size?, arrow?, icon?, newTab?}), rendered with BcmsButton from @bettercms-ai/astro or @bettercms-ai/next: never a text field beside a link field. A page that already holds such a pair (primaryCtaText + primaryCtaHref) becomes one button with update_field `mergeHref`. Inside a component a CTA is a `button` block whose text and href two props target.";
|
|
2274
2275
|
var STRUCTURE_EXAMPLE_PAYLOAD = {
|
|
2275
2276
|
version: 0,
|
|
2276
2277
|
doc: {
|
|
@@ -2687,9 +2688,11 @@ var PARAM_TEXT = {
|
|
|
2687
2688
|
pageUnpublishAt: "ISO datetime at which the published page returns to draft; null cancels; omitting it never cancels it",
|
|
2688
2689
|
sourceUrl: "instead of data: a public http(s) URL the server fetches and stores (private or internal addresses are refused)",
|
|
2689
2690
|
focalX: "focal point, 0-1 across from the left edge; null resets to the centre",
|
|
2690
|
-
focalY: "focal point, 0-1 down from the top edge; null resets to the centre"
|
|
2691
|
+
focalY: "focal point, 0-1 down from the top edge; null resets to the centre",
|
|
2692
|
+
mergeHref: "pageId only: the link field paired with `path` (the label). Both become ONE `button` field keyed `key` (default: their stem, primaryCtaHref \u2192 primaryCta). Each entry's DRAFT gets {label, href} and keeps the old values; the live copy changes on publish. ifMatch is the page's version"
|
|
2691
2693
|
};
|
|
2692
|
-
var
|
|
2694
|
+
var BUTTON_FIELD_NOTE = "'button' is ONE call to action, value {label, href, variant?, size?, arrow?, icon?, newTab?}: never a text field beside a link field.";
|
|
2695
|
+
var UPDATE_FIELD_DESCRIPTION = "Change ONE existing field of a content model (`modelId`) or of a page's schema (`pageId`), addressed by `path`: its key, or dot-separated keys through groups and repeaters ('hero.title'). Set its label, helpText, required, validation `config` (merged by key; null deletes one), showIf, ui or placement (null clears those). The rest of the schema is written back unchanged at the version read; a 409 means it changed since, so read again. `type` changes how every stored value is read and needs allowRetype: true. `key` renames it: stored values stay under the old key, so the route answers 409 with the entry count unless allowFieldRemoval: true discards them \u2014 migrate the values first. Ask the user before a retype or a rename. A page's label + link pair (primaryCtaText + primaryCtaHref) becomes one button with `mergeHref`.";
|
|
2693
2696
|
var GET_SEO_DESCRIPTION = "Get the connected project's site-wide SEO settings \u2014 every key update_seo writes: seoDefaults (metaTitle, metaDescription, ogImage, twitterHandle, og, twitter, collectionSchemaTypes), siteSchema (the site's JSON-LD), robotsConfig, sitemapConfig, rssConfig, indexnowConfig and searchConfig.";
|
|
2694
2697
|
var UPDATE_SEO_DESCRIPTION = "Set the connected project's site-wide SEO defaults and config. Each key you send is REPLACED whole (not deep-merged), so read get_seo and send the complete object. `seoDefaults` applies to every page that does not override it, and its `collectionSchemaTypes` maps a collection's model id to the schema.org type of its entries. `siteSchema` is the site's own JSON-LD (Organization/WebSite, emitted on the home page). `robotsConfig`, `sitemapConfig` and `rssConfig` shape robots.txt, sitemap.xml and the feed; `indexnowConfig` pings search engines on publish; `searchConfig` is site search. A key the plan does not include answers 403 FEATURE_LOCKED. Rebuilds the site.";
|
|
2695
2698
|
var SEO_KEY_TEXT = {
|
|
@@ -2905,7 +2908,7 @@ var fieldShape = {
|
|
|
2905
2908
|
"machine field key. \u{1F534} UNIQUE ACROSS THE WHOLE MODEL \u2014 API IDs share ONE FLAT NAMESPACE, so a field nested inside a group must NOT reuse a key used by another field or group. Every section's heading cannot be 'title'. Prefix with the section: 'hero_title', 'pricing_title', 'faq_title'. Reusing a key is refused, and if it slips through it leaves permanent errors in the editor and breaks conditional-visibility rules, which reference fields by bare key."
|
|
2906
2909
|
),
|
|
2907
2910
|
label: z.string().min(1).describe("human label shown in the editor"),
|
|
2908
|
-
type: fieldType,
|
|
2911
|
+
type: fieldType.describe(`field type. ${BUTTON_FIELD_NOTE}`),
|
|
2909
2912
|
required: z.boolean().optional().describe(
|
|
2910
2913
|
"an entry cannot be saved without a value \u2014 set it only where the site genuinely cannot render without one"
|
|
2911
2914
|
),
|
|
@@ -3118,7 +3121,7 @@ var ENTRY_SCHEDULED_AT_NOTE = "ISO datetime to publish this draft at (needs publ
|
|
|
3118
3121
|
var ENTRY_UNPUBLISH_AT_NOTE = "ISO datetime at which the published entry returns to draft; null cancels. Omitting it never cancels it.";
|
|
3119
3122
|
var ENTRY_LIST_LIMIT_NOTE = "entries per page, 1-200; 50 when omitted. Read `meta.totalPages` and ask for the next `page`.";
|
|
3120
3123
|
var PAGE_HEAD_NOTE = "noindex, canonical, og and twitter set the page's head extras; each MERGES into what is stored (a key you leave out is kept, null or an empty string clears it).";
|
|
3121
|
-
var STRUCTURE_FENCE = "ORGANISATION ONLY: Content sidebar folders never change a URL, slug, content or publish state, and nothing is rebuilt or delivered; URL folders (page folders) are set in the dashboard. Read the resource bettercms://playbook/structure before you organise anything: it holds the rules this refuses on \u2014 system folders, type purity, the Admin/Developer access check, root pins and page-bound models.
|
|
3124
|
+
var STRUCTURE_FENCE = "ORGANISATION ONLY: Content sidebar folders never change a URL, slug, content or publish state, and nothing is rebuilt or delivered; URL folders (page folders) are set in the dashboard. Read the resource bettercms://playbook/structure before you organise anything: it holds the rules this refuses on \u2014 system folders, type purity, the Admin/Developer access check, root pins and page-bound models.";
|
|
3122
3125
|
var STRUCTURE_POINTER = "Sidebar only, never a URL; rules: bettercms://playbook/structure.";
|
|
3123
3126
|
var BRAND_KIT_POINTER = "The brand kit's rules (consume its tokens, never copy values or invent brand facts) are on get_project.";
|
|
3124
3127
|
function structureResult(d) {
|
|
@@ -3806,7 +3809,7 @@ function buildToolDefs(deps) {
|
|
|
3806
3809
|
def(
|
|
3807
3810
|
"get_content_structure",
|
|
3808
3811
|
"Get the Content sidebar structure",
|
|
3809
|
-
`Returns { doc, version, summary }: \`doc\` is the raw document ({ schema: 1, folders: [{ id, parentId, name, icon, sort }], items: [{ kind: 'page'|'collection', id, folderId, sort, icon }] }), \`version\` is what set_content_structure needs, and \`summary\` is the resolved tree with page and collection TITLES (\`outline\` is a readable version), ids that no longer resolve (\`missing\`), and the pages and collections not in any folder yet. Call it before changing the structure
|
|
3812
|
+
`Returns { doc, version, summary }: \`doc\` is the raw document ({ schema: 1, folders: [{ id, parentId, name, icon, sort }], items: [{ kind: 'page'|'collection', id, folderId, sort, icon }] }), \`version\` is what set_content_structure needs, and \`summary\` is the resolved tree with page and collection TITLES (\`outline\` is a readable version), ids that no longer resolve (\`missing\`), and the pages and collections not in any folder yet. Call it before changing the structure; a 403 SCHEMA_ACCESS_REQUIRED on a change means this connection lacks Admin or Developer: stop and tell the user. ${STRUCTURE_FENCE}`,
|
|
3810
3813
|
z.object({}).shape,
|
|
3811
3814
|
async (c) => {
|
|
3812
3815
|
const d = await data(c, "GET", `/management/content-structure`);
|
|
@@ -4451,11 +4454,22 @@ ${res.warnings.join("\n")}` : summary, res.data);
|
|
|
4451
4454
|
key: fieldKey.optional().describe("a new key (flat namespace, unique in the schema)"),
|
|
4452
4455
|
allowRetype: z.boolean().optional().describe("confirm a type change, once the user has agreed"),
|
|
4453
4456
|
allowFieldRemoval: z.boolean().optional().describe("with a rename: discard the old key's stored values"),
|
|
4457
|
+
mergeHref: z.string().min(1).optional().describe(PARAM_TEXT.mergeHref),
|
|
4454
4458
|
ifMatch: z.number().int().min(0).optional().describe("the model's optimisticVersion; the version read when omitted")
|
|
4455
4459
|
}).shape,
|
|
4456
4460
|
// Read the model (a page's through its backingModel), patch ONE field with the helper the
|
|
4457
|
-
// hosted catalogue uses, and PATCH the whole list at the version read.
|
|
4461
|
+
// hosted catalogue uses, and PATCH the whole list at the version read. `mergeHref` is the
|
|
4462
|
+
// page route's "Make it a full button" instead, at the PAGE's version.
|
|
4458
4463
|
async (c, a) => {
|
|
4464
|
+
if (a.mergeHref !== void 0) {
|
|
4465
|
+
if (!a.pageId) return fail("mergeHref merges two fields of a PAGE: pass pageId.");
|
|
4466
|
+
const ifMatch = a.ifMatch ?? (await data(c, "GET", `/management/pages/${s(a.pageId)}`))?.optimisticVersion ?? 0;
|
|
4467
|
+
const merged = await c.fetchJSON(c.url(`/management/pages/${s(a.pageId)}/fields/merge-button`), {
|
|
4468
|
+
method: "POST",
|
|
4469
|
+
body: JSON.stringify({ labelKey: a.path, hrefKey: a.mergeHref, ...a.key !== void 0 ? { key: a.key } : {}, ifMatch })
|
|
4470
|
+
});
|
|
4471
|
+
return ok(`Made '${s(a.path)}' + '${s(a.mergeHref)}' one button field '${merged.key ?? ""}' (${merged.entries ?? 0} draft entries).`, merged);
|
|
4472
|
+
}
|
|
4459
4473
|
let modelId = a.modelId;
|
|
4460
4474
|
if (a.pageId) {
|
|
4461
4475
|
const page = await data(c, "GET", `/management/pages/${s(a.pageId)}`);
|
|
@@ -5523,7 +5537,7 @@ ${notes.join("\n")}` : summary, created);
|
|
|
5523
5537
|
name: "publish_components",
|
|
5524
5538
|
config: {
|
|
5525
5539
|
title: "Publish many components in one call",
|
|
5526
|
-
description: "Publish up to 100 components in ONE call \u2014 the same act as publish_component, once per item, copying each DRAFT definition to the live copy. Each item reports `{ ok, status, error, readiness }` and a failure never stops the run. \u{1F534} A 409 COMPONENT_IMPLEMENTATION_NOT_READY is NOT retried
|
|
5540
|
+
description: "Publish up to 100 components in ONE call \u2014 the same act as publish_component, once per item, copying each DRAFT definition to the live copy. Each item reports `{ ok, status, error, readiness }` and a failure never stops the run. \u{1F534} A 409 COMPONENT_IMPLEMENTATION_NOT_READY is NOT retried: only the owner, in the dashboard, clears it (publish_component says when); relay the item's `readiness` to the user as what only they can approve. 403 PUBLISH_NOT_GRANTED means this connection may author but not publish; 422 means the publish would break a Layout, and the item names which.",
|
|
5527
5541
|
inputSchema: publishComponentsInput.shape
|
|
5528
5542
|
},
|
|
5529
5543
|
handler: guard(
|
|
@@ -5891,6 +5905,9 @@ DERIVED at import, where the componentize lane in \xA710 reuses every field.
|
|
|
5891
5905
|
A fixed cluster of fields? -> group (never a 1-item repeater)
|
|
5892
5906
|
A repeating cluster? -> repeater
|
|
5893
5907
|
A list of plain strings? -> array (never a repeater of one text)
|
|
5908
|
+
A call to action (words + link)? -> ONE \`button\` field {label, href, variant?, size?,
|
|
5909
|
+
arrow?, icon?, newTab?} \u2014 never text + link fields
|
|
5910
|
+
(in a component: a \`button\` block, \xA73)
|
|
5894
5911
|
Site-wide nav/footer/modal? -> the LAYOUT (get_layout/update_layout), never a
|
|
5895
5912
|
page section and never one component per page
|
|
5896
5913
|
|
|
@@ -5912,6 +5929,12 @@ Section's root section has no \`style\`; treat it as a defect, not a note:
|
|
|
5912
5929
|
] }
|
|
5913
5930
|
}
|
|
5914
5931
|
|
|
5932
|
+
\`style\` takes the design tokens the editor's dock sliders and pickers write: \`theme\`, \`bg\` /
|
|
5933
|
+
\`bgCustom\` / \`bgImage\` (an image url), \`spaceTop\` / \`spaceBottom\` / \`spaceSides\` (token steps,
|
|
5934
|
+
winning over the \`padding*\` pixels), \`contentWidth\`, \`contentAlign\`, \`opacity\` (0\u2013100), \`corner\`,
|
|
5935
|
+
\`shadow\`, \`border\` / \`borderTop\` / \`borderBottom\`, and on a button \`hover\` { bg, bgCustom, textColor,
|
|
5936
|
+
border }.
|
|
5937
|
+
|
|
5915
5938
|
Then declare what an editor may change, via \`props\`:
|
|
5916
5939
|
|
|
5917
5940
|
props: [
|
|
@@ -6010,7 +6033,10 @@ Section zone, which also owns the document canvas. Use \`richtext\` for a short
|
|
|
6010
6033
|
Then the pages:
|
|
6011
6034
|
|
|
6012
6035
|
create_page slug:"blog" pageType:"dynamic" fields:[...the post schema...]
|
|
6013
|
-
blockJson: [
|
|
6036
|
+
blockJson: [ heading, { type:"collection", id:"list", props:{} } ]
|
|
6037
|
+
|
|
6038
|
+
Never put a \`navbar\` or \`footer\` block on a page: site chrome is the Layout (get_layout /
|
|
6039
|
+
update_layout), drawn around every page already, so a page copy duplicates it.
|
|
6014
6040
|
|
|
6015
6041
|
create_content_entry per post, with pageId set to that page
|
|
6016
6042
|
|
|
@@ -6266,6 +6292,25 @@ real design, with no publish and no Draft render detour. A bridge that answers w
|
|
|
6266
6292
|
toggle. Sites without one keep Draft render's approximate fallback, and a route the build does
|
|
6267
6293
|
not carry falls back always (a static build has no file to frame).
|
|
6268
6294
|
|
|
6295
|
+
**Sections on a site whose CODE renders the page.** The editor's outline is the page's \`blockJson\`,
|
|
6296
|
+
but a code-built site ships only what its own templates draw:
|
|
6297
|
+
- An added or moved section reaches the live site only when the site renders the page's block list
|
|
6298
|
+
itself, in order, each section root stamped \`data-bcms-block="<blockId>"\`. \`npx @bettercms-ai/convert
|
|
6299
|
+
--componentize\` writes that loop (\`src/lib/bcms-sections.ts\`) and the stamps. Until it ships, the canvas
|
|
6300
|
+
only ARRANGES the static build to match the draft (a section the build never rendered is stood in for
|
|
6301
|
+
by a server render), the editor locks Add section and reordering on routes whose code sets the
|
|
6302
|
+
sections, and each such route leads \`submit_conversion_receipt\`'s \`pending\` with a
|
|
6303
|
+
\`SECTIONS_UNSTAMPED\` row, so \`complete\` stays false. Never hard-code a page's bands in a template
|
|
6304
|
+
whose order the CMS owns.
|
|
6305
|
+
- A placement the site never renders (a leftover nav, footer or modal its layout draws) moves
|
|
6306
|
+
nothing: the editor counts section moves in RENDERED sections only and locks that row. Remove
|
|
6307
|
+
such placements; do not reorder around them.
|
|
6308
|
+
- Section spacing and style handles need every stamped root to also carry \`data-bcms-style\` and
|
|
6309
|
+
paint \`block.style\`: spread \`sectionRootAttrs(block)\` (\`@bettercms-ai/astro\`, \`@bettercms-ai/next\`)
|
|
6310
|
+
on the root. A site converted before that existed shows none (\`get_next_steps\`
|
|
6311
|
+
\`upgrade-section-roots\`): run \`npx @bettercms-ai/convert@latest --upgrade-sections --root .\` (with
|
|
6312
|
+
\`--dry-run\` first), fix the roots it lists as skipped by hand, check \`--verify-styles\`, push, release.
|
|
6313
|
+
|
|
6269
6314
|
**Hosting decides whether a canvas exists at all.** A site deployed here is framed through a
|
|
6270
6315
|
same-origin proxy \u2014 that is what the canvas requires. A site hosted elsewhere (your own Vercel,
|
|
6271
6316
|
your own server) has NO canvas today: the SDK's draft mode with \`stega: true\` embeds invisible
|
|
@@ -6300,7 +6345,9 @@ below is one heading of that ask.
|
|
|
6300
6345
|
the \`add-component\` command. CTA buttons are \`button\` BLOCKS inside the section that uses them,
|
|
6301
6346
|
exposed as \`text\` + \`url\` props that target that ONE block \u2014 never the words as a separate text
|
|
6302
6347
|
block beside it, or the Button panel edits nothing \u2014 variant, size and fullWidth are that block's own props and
|
|
6303
|
-
\`style
|
|
6348
|
+
\`style\`. Do not hand-build a Button component: every button look you write joins the project's Button
|
|
6349
|
+
family (Globals/Buttons) on its own, and a new look is \`create_component\` with \`variantOf\` naming a
|
|
6350
|
+
Button. On a page's FIELDS a CTA is one \`button\` field (\xA72). Rich text is a \`richtext\` prop over a
|
|
6304
6351
|
\`richtext\`/\`text\` block. Forms are \`create_form\` plus a \`form\` block, and form FIELDS are the
|
|
6305
6352
|
one place the platform models \`required\`, \`placeholder\`, \`helpText\` and real \`validation\`
|
|
6306
6353
|
(emailPolicy, min/max, phoneFormat, pattern) \u2014 put a site's validated inputs there. Cards,
|
|
@@ -6308,7 +6355,8 @@ badges, tags, links, lists and media are BLOCKS inside a section, exposed throug
|
|
|
6308
6355
|
repeatable row of them is ONE \`table\` prop whose \`config.fields\` describe the row. Accordions
|
|
6309
6356
|
and FAQs: start from the \`builtin:faq-list\` blueprint, or use a \`tabs\` block. NOT MODELLED \u2014
|
|
6310
6357
|
cookie banners, modals, breadcrumbs and pagination have no primitive at all. Leave them in the
|
|
6311
|
-
site's code and say so in your final summary.
|
|
6358
|
+
site's code and say so in your final summary. A modal is chrome, like the nav: never a page section
|
|
6359
|
+
(the plan reports it \`LAYOUT_CHROME\`).
|
|
6312
6360
|
|
|
6313
6361
|
**2. One component per distinct section, one-offs included.** \`create_component\` with a
|
|
6314
6362
|
\`sectionType\` IS the section. \`sectionType\` is the FAMILY (Hero, Features, Testimonials,
|
|
@@ -6739,7 +6787,7 @@ Importance orders a screen: required fields first, then array order.
|
|
|
6739
6787
|
| FAQ | \`preview:{title:'question'}\`, \`itemLabel:'Question'\`, \`icon:'help-circle'\` |
|
|
6740
6788
|
| Nav links | \`preview:{title:'label', subtitle:'href'}\`, \`itemLabel:'Link'\`, \`icon:'link'\` (never a table: a label and its href are one link) |
|
|
6741
6789
|
| Stats | \`layout:'table'\` over \u22643 scalar columns (value, label, suffix), \`itemLabel:'Stat'\`, \`icon:'bar-chart'\` |
|
|
6742
|
-
| CTA | one \`kind:'group'\` fieldset holding the heading, text
|
|
6790
|
+
| CTA | one \`kind:'group'\` fieldset holding the heading, text and ONE \`button\` field, \`icon:'launch'\` |
|
|
6743
6791
|
| Dimensions | one \`kind:'row'\` fieldset of up to 3 numbers (width, height, gap) |
|
|
6744
6792
|
|
|
6745
6793
|
**Tabs.** A \`kind: 'tab'\` fieldset is a tab:
|
|
@@ -6778,10 +6826,12 @@ Importance orders a screen: required fields first, then array order.
|
|
|
6778
6826
|
- \`fieldset_unknown\` / \`fieldset_orphan\` \u2014 a \`fieldsetId\` naming no fieldset, or a list that drops a
|
|
6779
6827
|
fieldset its fields still name.
|
|
6780
6828
|
- \`row_member_type\`, \`row_member_count\`, \`row_member_control\` \u2014 the row rule.
|
|
6781
|
-
- \`pair_split\` \u2014 a button's label and href in different fieldsets.
|
|
6829
|
+
- \`pair_split\` \u2014 a legacy button's label and href in different fieldsets.
|
|
6782
6830
|
- \`cta_pair\` \u2014 a NEW text field beside a link keyed like it plus \`-href\` (\`hero-span-go\` + \`hero-a-go-href\`): one
|
|
6783
6831
|
call to action. Add ONE \`type: "button"\` field (value \`{ label, href }\`); on a component, point both props
|
|
6784
|
-
at one button block.
|
|
6832
|
+
at one button block. A pair a page ALREADY holds (\`primaryCtaText\` + \`primaryCtaHref\`) is never refused:
|
|
6833
|
+
turn it into one button with \`update_field { pageId, path: <label key>, mergeHref: <link key> }\` \u2014
|
|
6834
|
+
drafts only, the old values stay, the live copy changes on publish.
|
|
6785
6835
|
- \`ui.icon\` or \`ui.itemLabel\` on a leaf; an icon name not in \`bettercms://icons/dock\`.
|
|
6786
6836
|
- \`layout_grid\` \u2014 a grid with no image or file child. \`layout_table\` \u2014 more than 3 columns, or a
|
|
6787
6837
|
column that is not a scalar.
|
|
@@ -6923,10 +6973,12 @@ Author a page (the page-first schema). Ask, in order, via AskUserQuestion:
|
|
|
6923
6973
|
2. **Identity** \u2014 title; derive a slug (lowercase, a\u2013z 0\u20139 -) and confirm; optional
|
|
6924
6974
|
metaTitle/metaDescription.
|
|
6925
6975
|
3. **Fields (loop until done)** \u2014 for each: key (^[a-zA-Z0-9_]+$), label, type,
|
|
6926
|
-
required?. Types (
|
|
6976
|
+
required?. Types (14): text, richtext, image, boolean, number, select (needs
|
|
6927
6977
|
\`options: string[]\`), reference / multi-reference (\`config.contentModelId\`,
|
|
6928
6978
|
multi adds min/max), array (primitives \u2014 \`config.itemType\`: text|number|date),
|
|
6929
|
-
date (\`config.includeTime\`?), datetime, **
|
|
6979
|
+
date (\`config.includeTime\`?), datetime, **button** (ONE call to action, value
|
|
6980
|
+
{label, href, variant?, size?, arrow?, icon?, newTab?} \u2014 never a text field plus a
|
|
6981
|
+
link field), **group** (Non-Repeatable Zone: ONE
|
|
6930
6982
|
nested object \u2014 recurse to collect its \`fields\`, e.g. blog_hero \u2192 heading,
|
|
6931
6983
|
description, hero_image), **repeater** (Repeatable Zone: an ARRAY of such objects
|
|
6932
6984
|
\u2014 recurse to collect item \`fields\`, e.g. testimonials \u2192 quote, author). Nesting
|
|
@@ -6937,7 +6989,7 @@ Author a page (the page-first schema). Ask, in order, via AskUserQuestion:
|
|
|
6937
6989
|
var FIELD_FLOW = `### Add a field \u2192 \`add_field\` (a model or a page)
|
|
6938
6990
|
Append a field to an existing schema. First decide the target: a **content model**
|
|
6939
6991
|
or a **page** (Home, About, a blog template). Ask: which target (id \u2014 use
|
|
6940
|
-
\`list_pages\` to find a page id), then the field (key, label, type \u2014 any of the
|
|
6992
|
+
\`list_pages\` to find a page id), then the field (key, label, type \u2014 any of the 14
|
|
6941
6993
|
above, including nested group/repeater), required?. Confirm, then call:
|
|
6942
6994
|
- a model \u2192 \`add_field\` { modelId, key, label, type, ... }
|
|
6943
6995
|
- a page \u2192 \`add_field\` { pageId, key, label, type, ... }
|
|
@@ -6992,7 +7044,8 @@ Read \`bettercms://playbook/schema\` \xA712 first \u2014 this is the short form
|
|
|
6992
7044
|
1. **Global elements.** Navbar / mobile nav / header / footer are the LAYOUT (\`get_layout\`,
|
|
6993
7045
|
\`update_layout\`; a chrome component is placed with the \`add-component\` command). CTA buttons
|
|
6994
7046
|
are \`button\` BLOCKS inside the section that uses them, exposed as \`text\` + \`url\` props \u2014
|
|
6995
|
-
variant/size/icon are that block's own props and \`style
|
|
7047
|
+
variant/size/icon are that block's own props and \`style\`; each look joins the project's Button
|
|
7048
|
+
family by itself, so never hand-build a Button component. Rich text
|
|
6996
7049
|
is a \`richtext\` prop. Forms are \`create_form\` + a \`form\` block, and form FIELDS are the only
|
|
6997
7050
|
place with real \`required\` / \`placeholder\` / \`helpText\` / \`validation\`. Cards, badges, tags,
|
|
6998
7051
|
links, lists and media are blocks inside a section, repeatables as one \`table\` prop.
|
|
@@ -7539,7 +7592,7 @@ function buildServer(deps) {
|
|
|
7539
7592
|
// 🔴 The plain-language ask, routed — the stdio twin of the hosted connector's line
|
|
7540
7593
|
// (src/routes/mcp/index.ts MCP_INSTRUCTIONS). "Make my site editable" has one recipe and
|
|
7541
7594
|
// one definition of done; without this an agent converts the page it landed on and stops.
|
|
7542
|
-
instructions: RECEIPT_FIRST_INSTRUCTION + " When the user asks to make a site or all of its pages editable, to convert it, or to bind its fields: this is playbook \xA713. Read `bettercms://playbook/schema` \xA713, call get_binding_report and get_conversion_brief { complete: true }, convert EVERY route the brief lists, and finish only when get_binding_report shows coverage.pending empty on every route \u2014 not when the first page works. On a workspace-wide connection pass projectId on every call; never ask the user to re-scope the connection. " + LAYOUT_CHROME_INSTRUCTION + " When the user asks to componentize the whole site, to turn every section into a component, or to build a component library from the site: this is playbook \xA712. Read `bettercms://playbook/schema` \xA712, start with get_site_composition, and use the batch tools \u2014 componentize_sections, compose_pages, update_layout with `commands`, publish_components \u2014 rather than one call per component. Finish with get_site_composition and tell the user what the platform does not model (cookie banners, modals, breadcrumbs, pagination) and which components still need the owner's approval in the dashboard before they can be published. " + // The default after authoring (the structure standard): organise what you made. Same
|
|
7595
|
+
instructions: RECEIPT_FIRST_INSTRUCTION + " When the user asks to make a site or all of its pages editable, to convert it, or to bind its fields: this is playbook \xA713. Read `bettercms://playbook/schema` \xA713, call get_binding_report and get_conversion_brief { complete: true }, convert EVERY route the brief lists, and finish only when get_binding_report shows coverage.pending empty on every route \u2014 not when the first page works. On a workspace-wide connection pass projectId on every call; never ask the user to re-scope the connection. " + LAYOUT_CHROME_INSTRUCTION + " " + CTA_BUTTON_INSTRUCTION + " When the user asks to componentize the whole site, to turn every section into a component, or to build a component library from the site: this is playbook \xA712. Read `bettercms://playbook/schema` \xA712, start with get_site_composition, and use the batch tools \u2014 componentize_sections, compose_pages, update_layout with `commands`, publish_components \u2014 rather than one call per component. Finish with get_site_composition and tell the user what the platform does not model (cookie banners, modals, breadcrumbs, pagination) and which components still need the owner's approval in the dashboard before they can be published. " + // The default after authoring (the structure standard): organise what you made. Same
|
|
7543
7596
|
// sentence as the hosted connector's MCP_INSTRUCTIONS.
|
|
7544
7597
|
STRUCTURE_DEFAULT_INSTRUCTION + " " + SKILLS_ROUTING_INSTRUCTION + " " + DOCK_UI_ROUTING_INSTRUCTION
|
|
7545
7598
|
}
|