sbuilder-mcp 0.72.0 → 0.73.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/CHANGELOG.md CHANGED
@@ -6,6 +6,12 @@ All notable changes to this project are documented in this file.
6
6
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
7
7
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
8
8
 
9
+ ## [0.73.0] - 2026-09-28
10
+
11
+ ### Added
12
+ - `sb_review` gains a `no_h1` finding: every heading on a page ships as h2 (the renderer's default `htmlTag`, shared by `text` and `text-dataset`), so a page with headings and no h1 has no main title for a search engine or a screen reader, invisible on the canvas; it fires on the first heading in the page's own bands, skipping overlays.
13
+ - `sb_review` gains a `homepage` readiness gap: a site resolves "/" by the `isHomepage` flag, so a site whose pages all carry slugs answers 404 at its own front door while every page reviews clean; it reports a draft home page separately from no home page at all, and stays silent when the page list does not carry the flag.
14
+
9
15
  ## [0.72.0] - 2026-09-28
10
16
 
11
17
  ### Added
package/CHANGELOG.vi.md CHANGED
@@ -6,6 +6,12 @@ Mọi thay đổi đáng chú ý của dự án được ghi lại trong file n
6
6
  Định dạng dựa trên [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
7
7
  và dự án tuân theo [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
8
8
 
9
+ ## [0.73.0] - 2026-09-28
10
+
11
+ ### Added
12
+ - `sb_review` có thêm finding `no_h1`: mọi heading trên trang đều được dựng thành h2 (giá trị mặc định `htmlTag` của renderer, dùng chung với `text` và `text-dataset`), nên một trang có heading mà không có h1 thì không có tiêu đề chính cho máy tìm kiếm hay trình đọc màn hình, và điều này vô hình trên canvas; finding báo trên heading đầu tiên trong band của chính trang đó, bỏ qua overlay.
13
+ - `sb_review` có thêm gap readiness `homepage`: site phân giải "/" bằng cờ `isHomepage`, nên một site mà mọi trang đều có slug sẽ trả 404 ngay tại trang chủ dù mọi trang khác review sạch; gap báo riêng trường hợp trang chủ tồn tại nhưng chưa publish với trường hợp không trang nào là trang chủ, và im lặng khi danh sách trang không mang cờ này.
14
+
9
15
  ## [0.72.0] - 2026-09-28
10
16
 
11
17
  ### Added
@@ -86,6 +86,9 @@ export const FIX = {
86
86
  hover_dead: 'Rewrite it with the same values: sb_set id "<id>", namespace style, state "hover", ' +
87
87
  'keys { … } — read them off the node first, because that one call both writes them where ' +
88
88
  'the renderer looks AND clears the slot nobody reads. Nothing else to do afterwards.',
89
+ no_h1: 'Make the page\'s main title an h1: sb_set id "<id>", namespace specials, keys ' +
90
+ '{ "htmlTag": "h1" } — ONE per page, on the heading the page is about (the first one ' +
91
+ 'is named here; pick a different one if it is not the title). Every heading ships as h2.',
89
92
  stuck_no_host: 'Pin something: sb_set id "<id>" (or the section it lives in), namespace style, keys ' +
90
93
  '{ "position": "sticky" } — sb_set seeds the offset and the layer order with it. Until ' +
91
94
  'then the platform compiles no rule for this state at all, so the override is stored, ' +
@@ -84,6 +84,34 @@ const drafted = (pages, type) => pages.some((p) => p.type === type && p.status !
84
84
  export function readinessGaps(input) {
85
85
  const gaps = [];
86
86
  const pages = input.pages;
87
+ // NOTHING ANSWERS AT "/".
88
+ //
89
+ // storefront.go: the empty slug is the home page, resolved by the
90
+ // isHomepage flag — a site whose pages all carry slugs serves a 404 at its
91
+ // own front door, while every page on it is reachable and reviews clean.
92
+ // Reached by an agent that built pages by name and never named one home, or
93
+ // demoted the home page by moving the star (page.ts explains the flag). Only
94
+ // when the list carried the flag at all.
95
+ if (pages && pages.length > 0 && pages.some((p) => typeof p.isHomepage === 'boolean')) {
96
+ const home = pages.filter((p) => p.isHomepage === true);
97
+ if (!home.some((p) => p.status === 'published')) {
98
+ const draft = home.length > 0;
99
+ gaps.push({
100
+ id: 'homepage',
101
+ draft,
102
+ problem: draft
103
+ ? 'The home page exists but is not published, so "/" — the address on every card, ' +
104
+ 'link and search result — answers 404 while every other page is live.'
105
+ : 'No page is the home page, so "/" answers 404. Every page on the site is reachable ' +
106
+ 'by its slug and none of them is the front door.',
107
+ fix: draft
108
+ ? 'Publish the home page.'
109
+ : 'Make one: PATCH /api/v1/pages/{id} { "isHomepage": true } on the page that should ' +
110
+ 'answer at "/" — it moves the star and clears the slug. Or sb_page_create with ' +
111
+ 'is_homepage: true, which adopts the home page the site already has.',
112
+ });
113
+ }
114
+ }
87
115
  // A SITE WITH NO SHARED SECTION IS NOT A SITE, IT IS A STACK OF PAGES.
88
116
  //
89
117
  // Nothing asked this. Every tool here authors ONE page, so a build that never
@@ -697,5 +697,35 @@ opts = {}) {
697
697
  for (const f of out)
698
698
  if (inOverlay.has(f.nodeId))
699
699
  f.overlay = true;
700
+ // A PAGE WITH HEADINGS AND NO <h1>. `heading` ships as h2
701
+ // (render/nodes/heading/html.go: SpecialString "htmlTag", default "h2"), and
702
+ // `text` / `text-dataset` take the same key, so a page built here — and every
703
+ // page the platform seeds, product title included — has headings at every
704
+ // level but the one a search engine and a screen reader look for first.
705
+ // Nothing on the canvas shows the tag. Page bands only (`band` excludes the
706
+ // overlays): a drawer heading is not the page's.
707
+ {
708
+ const level = (id) => {
709
+ const n = d.nodes[id];
710
+ const tag = n.specials?.htmlTag;
711
+ if (typeof tag === 'string' && /^h[1-6]$/.test(tag))
712
+ return tag;
713
+ return n.data.type === 'heading' ? 'h2' : null;
714
+ };
715
+ const headings = walkOrder.filter((id) => band.has(id) && level(id) !== null);
716
+ if (headings.length > 0 && !headings.some((id) => level(id) === 'h1')) {
717
+ const first = headings[0];
718
+ out.push({
719
+ code: 'no_h1',
720
+ nodeId: first,
721
+ type: d.nodes[first].data.type,
722
+ problem: `${headings.length} heading${headings.length === 1 ? '' : 's'} and none is an h1 — ` +
723
+ 'every heading ships as h2, so the page has no main title for a search engine or a ' +
724
+ 'screen reader. Invisible on the canvas.',
725
+ key: 'htmlTag',
726
+ fix: fill('no_h1', { id: first }),
727
+ });
728
+ }
729
+ }
700
730
  return out;
701
731
  }
package/dist/smoke.js CHANGED
@@ -90,7 +90,9 @@ export async function runSmoke() {
90
90
  // of this check asserted the section built above was clean; it was not — the
91
91
  // heading still carried the placeholder the element ships with, and the
92
92
  // reviewer said so. The check was wrong, not the reviewer.
93
- doc.apply(setKeys(doc, built.ids[1], { text: 'Autumn sale' }, { namespace: 'specials' }));
93
+ // And make it the page's h1 — every heading ships as h2, and a page with
94
+ // headings and no h1 is a finding too (no_h1). Same lesson, one rule later.
95
+ doc.apply(setKeys(doc, built.ids[1], { text: 'Autumn sale', htmlTag: 'h1' }, { namespace: 'specials' }));
94
96
  check('a finished section reviews clean', reviewDesign(doc).length === 0);
95
97
  // ...and an unfilled one must. A reviewer that never fires is indistinguishable
96
98
  // from one that is not wired up.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sbuilder-mcp",
3
- "version": "0.72.0",
3
+ "version": "0.73.0",
4
4
  "description": "MCP server that designs and operates a Store Builder site — pages, data, theme and publish — through the platform's own API and live-edit protocol.",
5
5
  "mcpName": "io.github.vuluu2k/sbuilder-mcp",
6
6
  "type": "module",