@wootsup/yt-builder-mcp 1.10.0 → 1.12.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.
Files changed (215) hide show
  1. package/README.md +12 -11
  2. package/bin/yt-builder-mcp.js +1 -1
  3. package/dist/auth.d.ts +1 -1
  4. package/dist/auth.js +1 -1
  5. package/dist/catalog/tool-catalog-markdown.d.ts.map +1 -1
  6. package/dist/catalog/tool-catalog-markdown.js +13 -1
  7. package/dist/catalog/tool-catalog-markdown.js.map +1 -1
  8. package/dist/cli/doctor-command.d.ts.map +1 -1
  9. package/dist/cli/doctor-command.js +22 -2
  10. package/dist/cli/doctor-command.js.map +1 -1
  11. package/dist/client.d.ts +3 -3
  12. package/dist/client.js +1 -1
  13. package/dist/client.js.map +1 -1
  14. package/dist/clients/index.d.ts +1 -1
  15. package/dist/clients/index.js +1 -1
  16. package/dist/diagnostics/ca-reexec.js +2 -2
  17. package/dist/diagnostics/diagnose-network-error.d.ts +1 -1
  18. package/dist/diagnostics/doctor.d.ts +34 -2
  19. package/dist/diagnostics/doctor.d.ts.map +1 -1
  20. package/dist/diagnostics/doctor.js +63 -2
  21. package/dist/diagnostics/doctor.js.map +1 -1
  22. package/dist/diagnostics/startup-self-probe.js +1 -1
  23. package/dist/gateway/advanced-read-tool.d.ts.map +1 -1
  24. package/dist/gateway/advanced-read-tool.js +14 -2
  25. package/dist/gateway/advanced-read-tool.js.map +1 -1
  26. package/dist/gateway/advanced-tool/discovery.d.ts.map +1 -1
  27. package/dist/gateway/advanced-tool/discovery.js +12 -1
  28. package/dist/gateway/advanced-tool/discovery.js.map +1 -1
  29. package/dist/gateway/advanced-tool/domains.d.ts.map +1 -1
  30. package/dist/gateway/advanced-tool/domains.js +5 -0
  31. package/dist/gateway/advanced-tool/domains.js.map +1 -1
  32. package/dist/gateway/advanced-tool/execute.d.ts +7 -1
  33. package/dist/gateway/advanced-tool/execute.d.ts.map +1 -1
  34. package/dist/gateway/advanced-tool/execute.js +58 -4
  35. package/dist/gateway/advanced-tool/execute.js.map +1 -1
  36. package/dist/gateway/essentials.d.ts +4 -4
  37. package/dist/gateway/essentials.d.ts.map +1 -1
  38. package/dist/gateway/essentials.js +12 -7
  39. package/dist/gateway/essentials.js.map +1 -1
  40. package/dist/install-skill.js +1 -1
  41. package/dist/net/extra-headers.d.ts +1 -1
  42. package/dist/net/extra-headers.js +1 -1
  43. package/dist/net/internal-host.js +2 -2
  44. package/dist/net/proxy-dispatcher.js +1 -1
  45. package/dist/net/proxy-dispatcher.js.map +1 -1
  46. package/dist/net/site-basic-auth.d.ts +2 -2
  47. package/dist/net/site-basic-auth.js +2 -2
  48. package/dist/proxy/bridge.d.ts +2 -2
  49. package/dist/proxy/bridge.js +3 -3
  50. package/dist/proxy/bridge.js.map +1 -1
  51. package/dist/server.d.ts +3 -3
  52. package/dist/server.js +3 -3
  53. package/dist/setup-cli.d.ts +2 -2
  54. package/dist/setup-cli.js +2 -2
  55. package/dist/setup-cli.js.map +1 -1
  56. package/dist/setup-npx-spec.d.ts +1 -1
  57. package/dist/setup-npx-spec.js +1 -1
  58. package/dist/setup-wizard-handshake.js +1 -1
  59. package/dist/setup-wizard-types.d.ts +1 -1
  60. package/dist/setup-wizard.js +3 -3
  61. package/dist/setup-wizard.js.map +1 -1
  62. package/dist/sites/client-pool.d.ts +1 -1
  63. package/dist/sites/client-pool.js +1 -1
  64. package/dist/sites/tools/sites-list.d.ts.map +1 -1
  65. package/dist/sites/tools/sites-list.js +26 -4
  66. package/dist/sites/tools/sites-list.js.map +1 -1
  67. package/dist/sites/tools/sites-test.js +1 -1
  68. package/dist/sites/tools/sites-test.js.map +1 -1
  69. package/dist/sites/tools/use-site.js +2 -2
  70. package/dist/sites/tools/use-site.js.map +1 -1
  71. package/dist/tools/budgeted-table.d.ts +21 -0
  72. package/dist/tools/budgeted-table.d.ts.map +1 -0
  73. package/dist/tools/budgeted-table.js +137 -0
  74. package/dist/tools/budgeted-table.js.map +1 -0
  75. package/dist/tools/elements/builders.d.ts.map +1 -1
  76. package/dist/tools/elements/builders.js +102 -40
  77. package/dist/tools/elements/builders.js.map +1 -1
  78. package/dist/tools/elements/handlers-write.d.ts +21 -0
  79. package/dist/tools/elements/handlers-write.d.ts.map +1 -1
  80. package/dist/tools/elements/handlers-write.js +231 -11
  81. package/dist/tools/elements/handlers-write.js.map +1 -1
  82. package/dist/tools/elements/handlers.d.ts +20 -12
  83. package/dist/tools/elements/handlers.d.ts.map +1 -1
  84. package/dist/tools/elements/handlers.js +77 -26
  85. package/dist/tools/elements/handlers.js.map +1 -1
  86. package/dist/tools/elements/schema-validation.d.ts +147 -7
  87. package/dist/tools/elements/schema-validation.d.ts.map +1 -1
  88. package/dist/tools/elements/schema-validation.js +508 -28
  89. package/dist/tools/elements/schema-validation.js.map +1 -1
  90. package/dist/tools/format/elements-format.d.ts.map +1 -1
  91. package/dist/tools/format/elements-format.js +56 -12
  92. package/dist/tools/format/elements-format.js.map +1 -1
  93. package/dist/tools/format/health-format.d.ts +16 -0
  94. package/dist/tools/format/health-format.d.ts.map +1 -1
  95. package/dist/tools/format/health-format.js +9 -0
  96. package/dist/tools/format/health-format.js.map +1 -1
  97. package/dist/tools/format/inspection-format.d.ts +7 -0
  98. package/dist/tools/format/inspection-format.d.ts.map +1 -1
  99. package/dist/tools/format/inspection-format.js +18 -0
  100. package/dist/tools/format/inspection-format.js.map +1 -1
  101. package/dist/tools/format/pages-format.d.ts +1 -1
  102. package/dist/tools/format/pages-format.d.ts.map +1 -1
  103. package/dist/tools/format/pages-format.js +7 -0
  104. package/dist/tools/format/pages-format.js.map +1 -1
  105. package/dist/tools/format/sources-format.d.ts +12 -5
  106. package/dist/tools/format/sources-format.d.ts.map +1 -1
  107. package/dist/tools/format/sources-format.js +31 -6
  108. package/dist/tools/format/sources-format.js.map +1 -1
  109. package/dist/tools/health.d.ts.map +1 -1
  110. package/dist/tools/health.js +85 -11
  111. package/dist/tools/health.js.map +1 -1
  112. package/dist/tools/index.d.ts.map +1 -1
  113. package/dist/tools/index.js +7 -0
  114. package/dist/tools/index.js.map +1 -1
  115. package/dist/tools/inspection.d.ts +34 -0
  116. package/dist/tools/inspection.d.ts.map +1 -1
  117. package/dist/tools/inspection.js +666 -62
  118. package/dist/tools/inspection.js.map +1 -1
  119. package/dist/tools/layout-flatten.d.ts +7 -1
  120. package/dist/tools/layout-flatten.d.ts.map +1 -1
  121. package/dist/tools/layout-flatten.js +24 -13
  122. package/dist/tools/layout-flatten.js.map +1 -1
  123. package/dist/tools/library.d.ts +29 -2
  124. package/dist/tools/library.d.ts.map +1 -1
  125. package/dist/tools/library.js +247 -34
  126. package/dist/tools/library.js.map +1 -1
  127. package/dist/tools/local-content/builders.js +14 -14
  128. package/dist/tools/local-content/builders.js.map +1 -1
  129. package/dist/tools/local-content/handlers.d.ts +3 -1
  130. package/dist/tools/local-content/handlers.d.ts.map +1 -1
  131. package/dist/tools/local-content/handlers.js +42 -9
  132. package/dist/tools/local-content/handlers.js.map +1 -1
  133. package/dist/tools/local-content/schemas.d.ts +9 -4
  134. package/dist/tools/local-content/schemas.d.ts.map +1 -1
  135. package/dist/tools/local-content/schemas.js +14 -6
  136. package/dist/tools/local-content/schemas.js.map +1 -1
  137. package/dist/tools/multi-items/builders.d.ts.map +1 -1
  138. package/dist/tools/multi-items/builders.js +11 -10
  139. package/dist/tools/multi-items/builders.js.map +1 -1
  140. package/dist/tools/navigation/builders.d.ts +18 -0
  141. package/dist/tools/navigation/builders.d.ts.map +1 -0
  142. package/dist/tools/navigation/builders.js +47 -0
  143. package/dist/tools/navigation/builders.js.map +1 -0
  144. package/dist/tools/navigation/handlers.d.ts +30 -0
  145. package/dist/tools/navigation/handlers.d.ts.map +1 -0
  146. package/dist/tools/navigation/handlers.js +41 -0
  147. package/dist/tools/navigation/handlers.js.map +1 -0
  148. package/dist/tools/navigation/index.d.ts +15 -0
  149. package/dist/tools/navigation/index.d.ts.map +1 -0
  150. package/dist/tools/navigation/index.js +14 -0
  151. package/dist/tools/navigation/index.js.map +1 -0
  152. package/dist/tools/navigation/schemas.d.ts +46 -0
  153. package/dist/tools/navigation/schemas.d.ts.map +1 -0
  154. package/dist/tools/navigation/schemas.js +41 -0
  155. package/dist/tools/navigation/schemas.js.map +1 -0
  156. package/dist/tools/pages/builders.d.ts.map +1 -1
  157. package/dist/tools/pages/builders.js +213 -81
  158. package/dist/tools/pages/builders.js.map +1 -1
  159. package/dist/tools/pages/handlers-audit.d.ts +112 -3
  160. package/dist/tools/pages/handlers-audit.d.ts.map +1 -1
  161. package/dist/tools/pages/handlers-audit.js +623 -7
  162. package/dist/tools/pages/handlers-audit.js.map +1 -1
  163. package/dist/tools/pages/handlers-read.d.ts +51 -8
  164. package/dist/tools/pages/handlers-read.d.ts.map +1 -1
  165. package/dist/tools/pages/handlers-read.js +348 -69
  166. package/dist/tools/pages/handlers-read.js.map +1 -1
  167. package/dist/tools/pages/handlers-write.d.ts +7 -3
  168. package/dist/tools/pages/handlers-write.d.ts.map +1 -1
  169. package/dist/tools/pages/handlers-write.js +69 -25
  170. package/dist/tools/pages/handlers-write.js.map +1 -1
  171. package/dist/tools/pages/header-transparency.d.ts +64 -0
  172. package/dist/tools/pages/header-transparency.d.ts.map +1 -0
  173. package/dist/tools/pages/header-transparency.js +119 -0
  174. package/dist/tools/pages/header-transparency.js.map +1 -0
  175. package/dist/tools/pages/schemas.d.ts +284 -13
  176. package/dist/tools/pages/schemas.d.ts.map +1 -1
  177. package/dist/tools/pages/schemas.js +434 -26
  178. package/dist/tools/pages/schemas.js.map +1 -1
  179. package/dist/tools/pages/target.d.ts +162 -0
  180. package/dist/tools/pages/target.d.ts.map +1 -0
  181. package/dist/tools/pages/target.js +239 -0
  182. package/dist/tools/pages/target.js.map +1 -0
  183. package/dist/tools/response-budget.d.ts +77 -0
  184. package/dist/tools/response-budget.d.ts.map +1 -0
  185. package/dist/tools/response-budget.js +134 -0
  186. package/dist/tools/response-budget.js.map +1 -0
  187. package/dist/tools/shared-schemas.d.ts +45 -0
  188. package/dist/tools/shared-schemas.d.ts.map +1 -1
  189. package/dist/tools/shared-schemas.js +50 -0
  190. package/dist/tools/shared-schemas.js.map +1 -1
  191. package/dist/tools/sources/builders.d.ts.map +1 -1
  192. package/dist/tools/sources/builders.js +55 -39
  193. package/dist/tools/sources/builders.js.map +1 -1
  194. package/dist/tools/sources/handlers-bind.d.ts +10 -11
  195. package/dist/tools/sources/handlers-bind.d.ts.map +1 -1
  196. package/dist/tools/sources/handlers-bind.js +110 -9
  197. package/dist/tools/sources/handlers-bind.js.map +1 -1
  198. package/dist/tools/sources/handlers.d.ts +4 -2
  199. package/dist/tools/sources/handlers.d.ts.map +1 -1
  200. package/dist/tools/sources/handlers.js +11 -7
  201. package/dist/tools/sources/handlers.js.map +1 -1
  202. package/dist/tools/sparse-fields.d.ts +61 -8
  203. package/dist/tools/sparse-fields.d.ts.map +1 -1
  204. package/dist/tools/sparse-fields.js +124 -12
  205. package/dist/tools/sparse-fields.js.map +1 -1
  206. package/dist/tools/sublayout/builders.d.ts.map +1 -1
  207. package/dist/tools/sublayout/builders.js +1 -2
  208. package/dist/tools/sublayout/builders.js.map +1 -1
  209. package/dist/tools/tool-builder/results.d.ts +19 -6
  210. package/dist/tools/tool-builder/results.d.ts.map +1 -1
  211. package/dist/tools/tool-builder/results.js +133 -6
  212. package/dist/tools/tool-builder/results.js.map +1 -1
  213. package/manifest.json +1 -1
  214. package/package.json +2 -2
  215. package/skills/yt-builder-mcp/SKILL.md +1713 -72
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Navigation — tool barrel (proxy-mode arm).
3
+ *
4
+ * yootheme_builder_navigation
5
+ * → GET /navigation — the site's menus as a parent -> children tree,
6
+ * each item carrying `page_role` + (when derivable for free)
7
+ * `target_page`. Optional `menu` narrows to one menu by name.
8
+ *
9
+ * READ-ONLY; routed via the read-only advanced gateway, NOT first-class.
10
+ *
11
+ * @license MIT
12
+ */
13
+ export { buildNavigationTools } from './builders.js';
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/navigation/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Navigation — zod input/output schema for `yootheme_builder_navigation`.
3
+ *
4
+ * T-C4 (mega-push plan 2026-07-24-02, customer P1.4). The proxy-mode arm of
5
+ * the Navigation Tree feature — the site's menus (WP nav menus / Joomla
6
+ * menus) as a parent -> children TREE, mirroring the WordPress REST
7
+ * controller ({@see \WootsUp\BuilderMcp\LocalContent\NavigationController})
8
+ * 1:1:
9
+ *
10
+ * yootheme_builder_navigation → GET /navigation
11
+ *
12
+ * Each menu item node is a RECURSIVE tree ({id, title, url, parent_id, type,
13
+ * target_page, page_role, children}) — modelled as a free
14
+ * `z.record(z.string(), z.unknown())` (mirrors
15
+ * `LOCAL_CONTENT_READ_OUTPUT_SCHEMA.items`'s heterogeneous rows) rather than
16
+ * a `z.lazy()` self-referential schema: there is no precedent in this
17
+ * codebase's zod4 `z.toJSONSchema()` catalog pipeline for a recursive shape,
18
+ * and a menu's nesting depth is unbounded by design. The concrete per-node
19
+ * shape is documented here + in the shared PHP
20
+ * {@see \WootsUp\BuilderMcp\LocalContent\MenuTreeBuilder} /
21
+ * {@see \WootsUp\BuilderMcp\LocalContent\LocalContentProjection::menuItem()}.
22
+ *
23
+ * @license MIT
24
+ */
25
+ import { z } from 'zod';
26
+ /**
27
+ * One MENU's already-nested tree. `items` is a list of recursive tree nodes:
28
+ * `{id: number, title: string, url: string|null, parent_id: number|null,
29
+ * type: string|null, target_page: string|null, page_role: string,
30
+ * children: <same shape>[]}`.
31
+ */
32
+ export declare const NAVIGATION_MENU_SCHEMA: z.ZodObject<{
33
+ menu_id: z.ZodNullable<z.ZodNumber>;
34
+ menu_name: z.ZodString;
35
+ item_count: z.ZodNumber;
36
+ items: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
37
+ }, z.core.$strip>;
38
+ export declare const NAVIGATION_OUTPUT_SCHEMA: z.ZodObject<{
39
+ menus: z.ZodArray<z.ZodObject<{
40
+ menu_id: z.ZodNullable<z.ZodNumber>;
41
+ menu_name: z.ZodString;
42
+ item_count: z.ZodNumber;
43
+ items: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
44
+ }, z.core.$strip>>;
45
+ }, z.core.$strip>;
46
+ //# sourceMappingURL=schemas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../../src/tools/navigation/schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB;;;;;iBAKjC,CAAC;AAEH,eAAO,MAAM,wBAAwB;;;;;;;iBAEnC,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Navigation — zod input/output schema for `yootheme_builder_navigation`.
3
+ *
4
+ * T-C4 (mega-push plan 2026-07-24-02, customer P1.4). The proxy-mode arm of
5
+ * the Navigation Tree feature — the site's menus (WP nav menus / Joomla
6
+ * menus) as a parent -> children TREE, mirroring the WordPress REST
7
+ * controller ({@see \WootsUp\BuilderMcp\LocalContent\NavigationController})
8
+ * 1:1:
9
+ *
10
+ * yootheme_builder_navigation → GET /navigation
11
+ *
12
+ * Each menu item node is a RECURSIVE tree ({id, title, url, parent_id, type,
13
+ * target_page, page_role, children}) — modelled as a free
14
+ * `z.record(z.string(), z.unknown())` (mirrors
15
+ * `LOCAL_CONTENT_READ_OUTPUT_SCHEMA.items`'s heterogeneous rows) rather than
16
+ * a `z.lazy()` self-referential schema: there is no precedent in this
17
+ * codebase's zod4 `z.toJSONSchema()` catalog pipeline for a recursive shape,
18
+ * and a menu's nesting depth is unbounded by design. The concrete per-node
19
+ * shape is documented here + in the shared PHP
20
+ * {@see \WootsUp\BuilderMcp\LocalContent\MenuTreeBuilder} /
21
+ * {@see \WootsUp\BuilderMcp\LocalContent\LocalContentProjection::menuItem()}.
22
+ *
23
+ * @license MIT
24
+ */
25
+ import { z } from 'zod';
26
+ /**
27
+ * One MENU's already-nested tree. `items` is a list of recursive tree nodes:
28
+ * `{id: number, title: string, url: string|null, parent_id: number|null,
29
+ * type: string|null, target_page: string|null, page_role: string,
30
+ * children: <same shape>[]}`.
31
+ */
32
+ export const NAVIGATION_MENU_SCHEMA = z.object({
33
+ menu_id: z.number().nullable(),
34
+ menu_name: z.string(),
35
+ item_count: z.number(),
36
+ items: z.array(z.record(z.string(), z.unknown())),
37
+ });
38
+ export const NAVIGATION_OUTPUT_SCHEMA = z.object({
39
+ menus: z.array(NAVIGATION_MENU_SCHEMA),
40
+ });
41
+ //# sourceMappingURL=schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../../src/tools/navigation/schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;GAKG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;IACrB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;CACpD,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,sBAAsB,CAAC;CACzC,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../../../src/tools/pages/builders.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAY7D,OAAO,EAOH,KAAK,iBAAiB,EACzB,MAAM,oBAAoB,CAAC;AAkD5B;;;;;;;;;GASG;AACH,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC7B;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,GAAG,aAAa,CAMhB;AAyCD,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,iBAAiB,EAAE,CAigB9E"}
1
+ {"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../../../src/tools/pages/builders.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAa7D,OAAO,EAOH,KAAK,iBAAiB,EACzB,MAAM,oBAAoB,CAAC;AAwD5B;;;;;;;;;GASG;AACH,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC7B;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,GAAG,aAAa,CAMhB;AAyCD,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,iBAAiB,EAAE,CA8pB9E"}
@@ -14,13 +14,14 @@
14
14
  */
15
15
  import { z } from 'zod';
16
16
  import { withSiteRetry, withWriteSiteRetry } from '../pool-resolve-helper.js';
17
- import { ETAG as ETAG_STRICT, POST_ID, RESOLVE_URL, SITE_ID_SCHEMA, TEMPLATE_ID, TEMPLATE_ID_OPTIONAL, } from '../shared-schemas.js';
17
+ import { ETAG as ETAG_STRICT, INCLUDE_META_SCHEMA, POST_ID, RESOLVE_URL, SITE_ID_SCHEMA, TEMPLATE_ID, TEMPLATE_ID_OPTIONAL, } from '../shared-schemas.js';
18
18
  import { FIELDS, FLAT, MAX_CHARS } from '../sparse-fields.js';
19
19
  import { creating, defineTool, destructive, mutating, readOnly, withSiteMeta, } from '../tool-builder.js';
20
20
  import { handlePagesAudit } from './handlers-audit.js';
21
- import { handleGetEtag, handlePageGetLayout, handlePageGetSchema, handlePagesList, handlePagesResolve, handleRenderPreview, handleTemplateSummary, } from './handlers-read.js';
21
+ import { handleHeaderTransparencySet, HEADER_TRANSPARENCY_SET_INPUT, } from './header-transparency.js';
22
+ import { handleGetEtag, handleHeadingOutline, handlePageGetLayout, handlePageGetSchema, handlePagesList, handlePagesResolve, handleRenderPreview, handleTemplateSummary, } from './handlers-read.js';
22
23
  import { handlePageDynamize, handlePagePublish, handlePagesCreate, handlePagesDelete, handlePageSave, handleTemplateSetType, TEMPLATE_SET_TYPE_OUTPUT_SCHEMA, } from './handlers-write.js';
23
- import { ETAG_OUTPUT_SCHEMA, PAGE_DYNAMIZE_INPUT, PAGES_AUDIT_INPUT, PAGES_AUDIT_OUTPUT_SCHEMA, PAGES_CREATE_INPUT, PAGES_DELETE_INPUT, PAGES_DELETE_OUTPUT_SCHEMA, PAGES_LIST_OUTPUT_SCHEMA, PAGES_RESOLVE_INPUT, PAGES_RESOLVE_OUTPUT_SCHEMA, RENDER_PREVIEW_OUTPUT_SCHEMA, SCHEMA_OUTPUT_SCHEMA, TEMPLATE_SUMMARY_OUTPUT_SCHEMA, } from './schemas.js';
24
+ import { ETAG_OUTPUT_SCHEMA, HEADING_OUTLINE_OUTPUT_SCHEMA, PAGE_DYNAMIZE_INPUT, PAGES_AUDIT_INPUT, PAGES_AUDIT_OUTPUT_SCHEMA, PAGES_CREATE_INPUT, PAGES_DELETE_INPUT, PAGES_DELETE_OUTPUT_SCHEMA, PAGES_LIST_OUTPUT_SCHEMA, PAGES_RESOLVE_INPUT, PAGES_RESOLVE_OUTPUT_SCHEMA, RENDER_PREVIEW_OUTPUT_SCHEMA, SCHEMA_OUTPUT_SCHEMA, TEMPLATE_SUMMARY_OUTPUT_SCHEMA, } from './schemas.js';
24
25
  // F-14: page-level save/publish accept OPTIONAL ETag (no precondition lock
25
26
  // at template level — element/binding mutations REQUIRE one via the strict
26
27
  // ETAG in shared-schemas.ts). Schema and description now agree: optional
@@ -80,12 +81,12 @@ export function buildPagesTools(pool) {
80
81
  name: 'yootheme_builder_pages_list',
81
82
  description: 'List pages, templates, and layouts in the YOOtheme Pro builder ' +
82
83
  '(id, label, type, elements_count, frontend_url, builder_url, ' +
83
- 'serves_context_kind, serves_context_ids per row). ' +
84
+ 'serves_context_kind, serves_context_ids, page_role per row). ' +
84
85
  'CALL THIS FIRST to discover template IDs before page_get_layout / element_list / ' +
85
86
  'page_get_schema. Paginates: up to `limit` rows (default 50) plus `total` and a ' +
86
87
  '`next_cursor` (pass it back as `cursor`) when more remain. Filter with ' +
87
88
  '`label_contains`. Slim rows with `fields:["id","label"]`; bound output ' +
88
- 'with `max_chars`. Operates on the default site unless site_id is provided.',
89
+ 'with `max_chars`.',
89
90
  inputSchema: {
90
91
  site_id: SITE_ID_SCHEMA,
91
92
  fields: FIELDS,
@@ -103,8 +104,10 @@ export function buildPagesTools(pool) {
103
104
  label_contains: z
104
105
  .string()
105
106
  .optional()
106
- .describe('Case-insensitive substring filter on the page label / id / type (e.g. "blog").'),
107
+ .describe('Case-insensitive substring filter on the page label / id / type / frontend_url ' +
108
+ '(also matches a slug or URL fragment, e.g. "blog" or "product-launch").'),
107
109
  max_chars: MAX_CHARS,
110
+ include_meta: INCLUDE_META_SCHEMA,
108
111
  },
109
112
  outputSchema: PAGES_LIST_OUTPUT_SCHEMA,
110
113
  annotations: readOnly('List Pages'),
@@ -112,14 +115,24 @@ export function buildPagesTools(pool) {
112
115
  }),
113
116
  defineTool({
114
117
  name: 'yootheme_builder_page_get_layout',
118
+ // B-1 (SEO-routing fix, 2026-07-21): a generic "SEO audit" prompt
119
+ // from a COLD agent (no SKILL.md) otherwise loops this tool
120
+ // page-by-page into its Max-Iterations limit instead of the one-call
121
+ // pages_audit sweep (empirically confirmed on a customer site —
122
+ // followup_pages_audit_routing_generic_seo_request). The negative
123
+ // steer at the tail routes a whole-site audit to pages_audit (an L2
124
+ // read-only tool reached via yootheme_builder_advanced_read). The
125
+ // per-arg details dropped from this prose (bindings-skeleton / force
126
+ // / cursor semantics) already live in each param's own .describe().
115
127
  description: 'Get the layout tree for one template — address it by `template_id` ' +
116
- '(from pages_list), `post_id` (numeric post/article id), or `url`. For ' +
117
- 'dynamize/bind prefer `mode:"bindings_only"` (binding skeleton, no style ' +
118
- 'props). Default `mode:"full"` returns nested `{layout, etag}`; `flat:true` ' +
119
- 'gives a depth-first `{elements:[...]}` array (with `fields[]`). A repeated ' +
120
- 'unchanged read auto-skips (`force:true` re-fetches). `max_chars` bounds the ' +
121
- 'text; a clipped read returns `next_cursor` (pass back as `cursor`) to page ' +
122
- 'the rest. Operates on the default site unless site_id is provided.',
128
+ '(from pages_list), `post_id`, or `url`. `mode:"bindings_only"` for ' +
129
+ 'dynamize/bind; `mode:"full"` (default) is nested `{layout, etag}`, ' +
130
+ '`flat:true` a depth-first array. `max_chars` bounds the text; a clipped ' +
131
+ 'read returns `next_cursor` to page the rest. For a site-wide ' +
132
+ 'SEO/structure/content/links audit (h1, image alt, thin pages, dead ' +
133
+ 'links) do NOT loop this tool page-by-page call ' +
134
+ 'yootheme_builder_pages_audit (one sweep via ' +
135
+ 'yootheme_builder_advanced_read).',
123
136
  inputSchema: {
124
137
  site_id: SITE_ID_SCHEMA,
125
138
  // G-A (Track-C URL→layout): three interchangeable addressing modes
@@ -154,7 +167,7 @@ export function buildPagesTools(pool) {
154
167
  flat: FLAT,
155
168
  fields: FIELDS,
156
169
  max_chars: MAX_CHARS,
157
- // B7 (Glöckle continuation cursor): resume a clipped full/
170
+ // B7 (customer continuation cursor): resume a clipped full/
158
171
  // bindings_only text leg from its char offset. Opaque
159
172
  // base64url `o:<n>` — the SAME offset-cursor contract
160
173
  // pages_list/sources_list use. Omit to read from the start.
@@ -175,8 +188,7 @@ export function buildPagesTools(pool) {
175
188
  description: 'Get the flat schema for a template — nodes with their JSON-Pointer ' +
176
189
  'paths and element types. Lighter than page_get_layout, enough to locate ' +
177
190
  'elements before editing. Address by `template_id` (pages_list), `post_id`, ' +
178
- 'or `url`. `fields[]` slims each node; `max_chars` bounds output. ' +
179
- 'Operates on the default site unless site_id is provided.',
191
+ 'or `url`. `fields[]` slims each node; `max_chars` bounds output.',
180
192
  inputSchema: {
181
193
  site_id: SITE_ID_SCHEMA,
182
194
  // G-A (Track-C URL→layout): three interchangeable addressing modes.
@@ -185,6 +197,7 @@ export function buildPagesTools(pool) {
185
197
  url: RESOLVE_URL,
186
198
  fields: FIELDS,
187
199
  max_chars: MAX_CHARS,
200
+ include_meta: INCLUDE_META_SCHEMA,
188
201
  },
189
202
  outputSchema: SCHEMA_OUTPUT_SCHEMA,
190
203
  annotations: readOnly('Get Page Schema'),
@@ -202,9 +215,8 @@ export function buildPagesTools(pool) {
202
215
  'Joomla (incl. deep sub-category paths), not only raw ?option= URLs. A singular page ' +
203
216
  '→ its per-page layout; a category/tag/CPT/author archive → the governing THEME ' +
204
217
  'TEMPLATE, and for a category/term the SPECIFICALLY-ASSIGNED template (honoring ' +
205
- 'include-child-categories), not a generic one. Returns {template_id, post_id, type, ' +
206
- 'frontend_url, context, resolved_via}. ' +
207
- 'Operates on the default site unless site_id is provided.',
218
+ 'include-child-categories), not a generic one. Or a `target`. Returns {template_id, ' +
219
+ 'post_id, type, frontend_url, context, resolved_via}.',
208
220
  inputSchema: { site_id: SITE_ID_SCHEMA, ...PAGES_RESOLVE_INPUT },
209
221
  outputSchema: PAGES_RESOLVE_OUTPUT_SCHEMA,
210
222
  annotations: readOnly('Resolve URL to Template'),
@@ -212,13 +224,12 @@ export function buildPagesTools(pool) {
212
224
  }),
213
225
  defineTool({
214
226
  name: 'yootheme_builder_get_etag',
215
- description: 'Get the current ETag (state revision) for the YOOtheme builder. ' +
216
- 'Returns sha256+revision string used for optimistic locking on writes. ' +
217
- 'Pass the returned value back as `etag` on any write tool (page_save, page_publish, ' +
218
- 'element_add, element_update_settings, element_clone, element_move, element_delete). ' +
219
- 'The server returns HTTP 412 if the ETag has changed since you read it. ' +
220
- 'Keywords: get etag, current etag, state revision, optimistic lock, version stamp. ' +
221
- 'Operates on the default site unless site_id is provided.',
227
+ description: 'Get the current ETag (state revision) for the YOOtheme builder. Returns a ' +
228
+ 'sha256+revision string for optimistic locking. This ETag is SITE-WIDE: it covers ' +
229
+ 'the ENTIRE builder state, so ANY write to ANY template (by any client) bumps it — ' +
230
+ 'a 412 means the site state advanced, not necessarily that YOUR template changed. ' +
231
+ 'It is the SAME token the read tools (page_get_layout, element_list) return in ' +
232
+ 'their `etag`. Pass it back as `etag` on any write tool; on 412, re-read and retry.',
222
233
  inputSchema: { site_id: SITE_ID_SCHEMA },
223
234
  outputSchema: ETAG_OUTPUT_SCHEMA,
224
235
  annotations: readOnly('Get ETag'),
@@ -227,17 +238,58 @@ export function buildPagesTools(pool) {
227
238
  defineTool({
228
239
  name: 'yootheme_builder_template_summary',
229
240
  description: 'Token-efficient template overview: element counts by type, binding ' +
230
- 'count, max nesting depth, and named landmark sections computed ' +
241
+ 'count, max nesting depth, and named landmark sections, computed ' +
231
242
  'server-side in one call. Use this to grasp a large template before ' +
232
- 'pulling element_list or page_get_layout. ' +
243
+ 'pulling element_list or page_get_layout. Pass `include_nodes:true` for a ' +
244
+ '`content_nodes` per-node breakdown (status/effective_visible/headings/alts), ' +
245
+ 'pageable via `limit`/`cursor`. Pass ' +
246
+ '`template_ids:[...]` for a batch: response carries `summaries` + ' +
247
+ '`not_found`. ' +
233
248
  // F206 (P3): concrete first-call example. The Desktop client
234
249
  // rejected the first guessed call because no example showed the
235
250
  // exact arg shape (the whole yt-builder package used none, while
236
251
  // apimapper's schema.ts does). Mirrors apimapper's 'Example:'
237
252
  // pattern.
238
- 'Example: yootheme_builder_template_summary({ template_id: "default" }). ' +
239
- 'Operates on the default site unless site_id is provided.',
240
- inputSchema: { site_id: SITE_ID_SCHEMA, template_id: TEMPLATE_ID },
253
+ 'Example: yootheme_builder_template_summary({ template_id: "default" }).',
254
+ inputSchema: {
255
+ site_id: SITE_ID_SCHEMA,
256
+ template_id: TEMPLATE_ID.optional().describe('Template ID (e.g. "default"). Provide this OR `template_ids` (batch) - ' +
257
+ 'exactly one, not both. Use yootheme_builder_pages_list to discover.'),
258
+ // T-C5 (mega-push plan 2026-07-24-02, customer P1.5): batch mode -
259
+ // one REST round-trip for N templates instead of N calls.
260
+ template_ids: z
261
+ .array(z.string().min(1))
262
+ .min(1)
263
+ .optional()
264
+ .describe('Batch of template IDs (2+) - one call instead of N. Provide this OR ' +
265
+ '`template_id` (single) - exactly one, not both. Response carries ' +
266
+ '`summaries` (keyed by id) + `not_found`.'),
267
+ // T-E2 (mega-push plan 2026-07-24-02, Thomas-Entscheid C):
268
+ // content_nodes is opt-in - omit for the compact aggregate-only
269
+ // shape, pass true to also get the per-node breakdown.
270
+ include_nodes: z
271
+ .boolean()
272
+ .optional()
273
+ .describe('When true, also return `content_nodes` (per-node status/' +
274
+ 'effective_visible/hidden_on_mobile/headings/alts). Default false ' +
275
+ '(the compact aggregate-only shape).'),
276
+ // T-C5: page content_nodes when include_nodes:true (same
277
+ // limit/cursor contract as pages_list/element_list).
278
+ limit: z
279
+ .number()
280
+ .int()
281
+ .min(1)
282
+ .max(200)
283
+ .optional()
284
+ .describe('Page size for `content_nodes` when `include_nodes:true` (1-200). ' +
285
+ 'Omit for the full unpaged list. The response carries `next_cursor` ' +
286
+ 'when more remain.'),
287
+ cursor: z
288
+ .string()
289
+ .optional()
290
+ .describe('Opaque cursor from a previous response\'s next_cursor - resumes ' +
291
+ 'content_nodes pagination (use with include_nodes:true + limit).'),
292
+ },
241
293
  outputSchema: TEMPLATE_SUMMARY_OUTPUT_SCHEMA,
242
294
  annotations: readOnly('Template Summary'),
243
295
  handler: async ({ site_id, ...rest }) => withSiteRetry(pool, site_id, async (client, site) => withSiteMeta(await handleTemplateSummary(client, rest), site)),
@@ -250,19 +302,19 @@ export function buildPagesTools(pool) {
250
302
  // automatically (annotation-driven, no allowlist edit).
251
303
  defineTool({
252
304
  name: 'yootheme_builder_pages_audit',
253
- description: 'One-call site-wide SEO/structure/content/links audit — severity-sorted ' +
254
- 'findings with machine-executable fix_hints (missing/multiple h1, empty/thin ' +
255
- 'pages, missing image alt, duplicate layouts, title collisions, non-pretty ' +
256
- 'URLs, stale content, dead anchors). Filter with `types`/`checks`. Disclaimer: ' +
257
- 'SEO-plugin meta (title/description/OG) NOT covered. Drill in via ' +
258
- "template_summary; fix one page at a time via each finding's fix_hint. " +
259
- 'Operates on the default site unless site_id is provided.',
305
+ description: 'One-call site-wide SEO/structure/content/links/design audit — severity-sorted findings ' +
306
+ 'with fix_hints (a props fragment to merge: supply template_id+path from the ' +
307
+ 'locator). Covers h1/headings, thin pages, image alt, duplicate layouts, title ' +
308
+ 'collisions, non-pretty URLs, dead anchors. Only active (non-disabled) nodes ' +
309
+ 'produce findings. A site sweep caps hits/page, drops content_snippet; `target` ' +
310
+ 'scopes to ONE page for the full set (cap 100). Filter with `types`/`checks`. ' +
311
+ 'Disclaimer: SEO-plugin meta (title/description/OG) NOT covered.',
260
312
  inputSchema: { site_id: SITE_ID_SCHEMA, ...PAGES_AUDIT_INPUT },
261
313
  outputSchema: PAGES_AUDIT_OUTPUT_SCHEMA,
262
314
  annotations: readOnly('Pages Audit'),
263
315
  handler: async ({ site_id, ...rest }) => withSiteRetry(pool, site_id, async (client, site) => withSiteMeta(await handlePagesAudit(client, rest), site)),
264
316
  }),
265
- // ─── F5 + N5 (Glöckle HANDOFF-4/5) — render_preview ───────────
317
+ // ─── F5 + N5 (customer HANDOFF-4/5) — render_preview ───────────
266
318
  // The authenticated post-write render HEALTHCHECK. READ-ONLY: renders
267
319
  // the template server-side over the existing Bearer channel and reports
268
320
  // {rendered, item_count, empty, error?} + a cache_purge_hint — answering
@@ -273,21 +325,23 @@ export function buildPagesTools(pool) {
273
325
  // template_id OR url (exactly one).
274
326
  defineTool({
275
327
  name: 'yootheme_builder_render_preview',
276
- description: 'Render-check a saved template over the authenticated channel does it ' +
277
- 'actually render, with how many items, or is it blank? Address by ' +
278
- '`template_id` (from pages_list) OR `url` (resolved for you). Returns ' +
279
- '{rendered, item_count, empty, error?, cache_purge_hint}; with `expect`, also ' +
280
- '{matched, snippet}. Use AFTER a page_save / element_bind_source to catch a ' +
281
- 'silently-empty live page (a bad binding "saves" then renders blank); the ' +
282
- 'swallowed YOOtheme render error is surfaced in `error`. Read-only — renders ' +
283
- 'without persisting. Operates on the default site unless site_id is provided.',
328
+ description: 'Render-check a saved template does it actually render, with how many items, or ' +
329
+ 'is it blank? Address by `template_id` (from pages_list), `post_id`, or `url` ' +
330
+ '(resolved for you) exactly one. `item_count` = items yielded by the ' +
331
+ 'template\'s dynamic/Multi-Items bindings (rendered repeat-source rows); 0 with ' +
332
+ 'empty:true means a binding returned nothing. Returns {rendered, item_count, empty, ' +
333
+ 'error?, cache_purge_hint}; with `expect`, also {matched, snippet}. Use AFTER a ' +
334
+ 'save/bind to catch a silently-empty page. Read-only.',
284
335
  inputSchema: {
285
336
  site_id: SITE_ID_SCHEMA,
286
- // Exactly one of template_id / url mirrors page_get_layout's
287
- // TEMPLATE_ID_OPTIONAL + url addressing (resolveEffectiveTemplateId).
337
+ // WAVE-1 follow-up (a): render_preview threads the SAME three
338
+ // addressing modes page_get_layout uses (template_id | post_id |
339
+ // url, exactly one) — the handler already resolved post_id via
340
+ // resolveEffectiveTemplateId; this exposes it at the MCP boundary.
288
341
  template_id: TEMPLATE_ID_OPTIONAL,
342
+ post_id: POST_ID,
289
343
  url: RESOLVE_URL,
290
- // Glöckle HANDOFF-6 #4: optional substring the rendered HTML must
344
+ // customer HANDOFF-6 #4: optional substring the rendered HTML must
291
345
  // contain. When set, the response adds {matched, snippet} so "did
292
346
  // my edit reach the render?" is answerable in one call.
293
347
  expect: z
@@ -295,7 +349,9 @@ export function buildPagesTools(pool) {
295
349
  .min(1)
296
350
  .optional()
297
351
  .describe('Optional substring to find in the rendered HTML (e.g. a headline you ' +
298
- 'just bound). When set, the response adds `matched` (bool) + ' +
352
+ 'just bound). Matched CASE-SENSITIVELY against the RAW, un-decoded ' +
353
+ 'response markup — pass raw HTML, not HTML-escaped text (search for ' +
354
+ '"<h1", not "&lt;h1"). When set, the response adds `matched` (bool) + ' +
299
355
  '`snippet` (≈200-char window, or null) — confirm an edit reached ' +
300
356
  'the render in one call.'),
301
357
  },
@@ -303,16 +359,48 @@ export function buildPagesTools(pool) {
303
359
  annotations: readOnly('Render Preview'),
304
360
  handler: async ({ site_id, ...rest }) => withSiteRetry(pool, site_id, async (client, site) => withSiteMeta(await handleRenderPreview(client, rest), site)),
305
361
  }),
362
+ // ─── T-A5 (heading_outline tool, mega-push plan 2026-07-24-02) ────
363
+ // L2 (advanced_read): a real, doc-order H1-H6 outline of ONE
364
+ // template attributed to its source element, plus the alt-status of
365
+ // every image. Deliberately NOT in ESSENTIAL_TOOLS — `readOnly()`
366
+ // routes it into the advanced_read gateway automatically, same as
367
+ // pages_audit/layout_query.
368
+ defineTool({
369
+ name: 'yootheme_builder_heading_outline',
370
+ description: 'A real, doc-order H1-H6 outline of ONE template: each heading with its TEXT ' +
371
+ 'and the source element that carries it (`element_type`/`path`), plus the ' +
372
+ 'alt-status of every image (including images inside html/text/code raw markup). ' +
373
+ 'Read-only STRUCTURE inspection, not findings; use yootheme_builder_pages_audit ' +
374
+ 'for severity-sorted SEO findings across the whole site. Returns ' +
375
+ '{outline, images, h1_count, has_level_skip, images_missing_alt}. Address by ' +
376
+ '`template_id` (from pages_list or pages_resolve).',
377
+ inputSchema: {
378
+ site_id: SITE_ID_SCHEMA,
379
+ template_id: TEMPLATE_ID,
380
+ mode: z
381
+ .enum(['builder', 'rendered'])
382
+ .optional()
383
+ .describe('Outline source. "builder" (default) reads the saved layout tree: fast, ' +
384
+ 'but blind to module/widget output and anything a plugin injects only ' +
385
+ 'at render time. "rendered" renders the page first (via the SAME ' +
386
+ 'engine render_preview / pages_audit use) and reads the ACTUAL DOM - ' +
387
+ 'element_type is empty ("") in this mode, since there is no builder ' +
388
+ 'node to point at; locate a heading by its `path` (a synthetic ' +
389
+ '"rendered:heading:N" marker) or the page\'s frontend_url instead.'),
390
+ },
391
+ outputSchema: HEADING_OUTLINE_OUTPUT_SCHEMA,
392
+ annotations: readOnly('Heading Outline'),
393
+ handler: async ({ site_id, ...rest }) => withSiteRetry(pool, site_id, async (client, site) => withSiteMeta(await handleHeadingOutline(client, rest), site)),
394
+ }),
306
395
  defineTool({
307
396
  name: 'yootheme_builder_page_save',
308
397
  description: 'Re-run save-transforms + flush caches. Optionally REPLACE the template tree by ' +
309
- 'passing the INNER {type:"layout",children:[...]} tree as `layout` — NOT the full ' +
310
- 'template node page_get_layout returns (rejected 422, would blank the page). ' +
311
- 'Validated + ETag-locked; on reject nothing persists. An unrenderable layout is ' +
312
- 'refused PRE-WRITE (422). `dry_run:true` preflights; the success response carries ' +
313
- '`previous_layout` for 1-call rollback. Omit `layout` to re-run transforms only. ' +
314
- 'ETag optional (412 on conflict; else last-write-wins). ' +
315
- 'Operates on the default site unless site_id is provided.',
398
+ 'passing the INNER {type:"layout",children:[...]} tree as `layout` — page_get_layout\'s ' +
399
+ '`.layout` child, NOT the whole response it returns (rejected 422, would blank the ' +
400
+ 'page). Validated + ETag-locked; nothing persists on reject. Does NOT change CMS ' +
401
+ 'post status a draft stays invisible to visitors; set status via pages_create. ' +
402
+ '`dry_run:true` preflights; success carries `previous_layout` for rollback. ETag ' +
403
+ 'optional (412 on conflict; else last-write-wins).',
316
404
  inputSchema: {
317
405
  site_id: SITE_ID_SCHEMA,
318
406
  template_id: TEMPLATE_ID,
@@ -323,11 +411,13 @@ export function buildPagesTools(pool) {
323
411
  .record(z.string(), z.unknown())
324
412
  .optional()
325
413
  .describe('Optional whole YT layout-node object — the INNER tree ' +
326
- '{type:"layout",children:[...]}, NOT the full template node ' +
327
- 'page_get_layout returns (passing that is rejected as a double-nest). ' +
328
- 'Replaces the template tree atomically. Validated + ETag-locked. ' +
329
- 'Omit to just re-run save-transforms.'),
330
- // U3/F3 (Glöckle HANDOFF-4): optional render preflight.
414
+ '{type:"layout",children:[...]}, i.e. page_get_layout\'s `.layout` ' +
415
+ 'child, NOT the whole response it returns (passing that is rejected ' +
416
+ 'as a double-nest). Replaces the template tree atomically. Validated ' +
417
+ '+ ETag-locked, BUT prop KEYS are NOT schema-validated on a whole-tree ' +
418
+ 'save YT-Pro silently drops unknown keys; use element_update_settings ' +
419
+ 'for validated per-element edits. Omit to just re-run save-transforms.'),
420
+ // U3/F3 (customer HANDOFF-4): optional render preflight.
331
421
  dry_run: z
332
422
  .boolean()
333
423
  .optional()
@@ -357,9 +447,11 @@ export function buildPagesTools(pool) {
357
447
  defineTool({
358
448
  name: 'yootheme_builder_page_publish',
359
449
  description: 'Publish a template — persist state, flush YT + WP caches, snapshot the ' +
360
- 'published-state ETag. ETag optional when provided, 412 on conflict; when ' +
361
- 'omitted, last-write-wins. Recommended for collaborative edits. ' +
362
- 'Operates on the default site unless site_id is provided.',
450
+ 'published-state ETag. YOOtheme templates already publish on save, so this is a ' +
451
+ 'cache-flush + state-snapshot op. Neither publish nor save changes the CMS post ' +
452
+ 'status a draft page stays invisible to visitors; set status at creation via ' +
453
+ 'pages_create/library_insert. ETag optional — when provided, 412 on conflict; when ' +
454
+ 'omitted, last-write-wins.',
363
455
  inputSchema: {
364
456
  site_id: SITE_ID_SCHEMA,
365
457
  template_id: TEMPLATE_ID,
@@ -386,24 +478,23 @@ export function buildPagesTools(pool) {
386
478
  locale: localeForSite(site),
387
479
  }))),
388
480
  }),
389
- // ─── N3 (Glöckle HANDOFF-5) — template_set_type ───────────────
481
+ // ─── N3 (customer HANDOFF-5) — template_set_type ───────────────
390
482
  // L2 (advanced): set which archive/single a YOOtheme theme template
391
483
  // is assigned to (the `templates.<id>.type` condition pages_list
392
484
  // reads). The #1 missing step in a taxonomy/CPT-rename workflow —
393
485
  // after categoryFaq→categoryHelp the `taxonomy-category_faq` /
394
- // `single-gloeckle_faq` templates are orphaned and must be re-typed.
486
+ // `single-example_faq` templates are orphaned and must be re-typed.
395
487
  defineTool({
396
488
  name: 'yootheme_builder_template_set_type',
397
489
  description:
398
- // N3 (Glöckle HANDOFF-5) — archive/single TYPE reassignment.
490
+ // N3 (customer HANDOFF-5) — archive/single TYPE reassignment.
399
491
  'Reassign which archive/single a YOOtheme THEME template applies to — ' +
400
492
  'set its `type` condition (the value pages_list shows) in ONE ETag-locked ' +
401
493
  'write. Use after a taxonomy/CPT rename orphans a template (e.g. re-type ' +
402
- '"taxonomy-category_faq" → "taxonomy-category_help", "single-gloeckle_faq" ' +
403
- '→ "single-gloeckle_help"). Returns {type, previous_type, new_etag} — ' +
404
- 'previous_type is the read of the prior assignment, folded in. THEME ' +
405
- 'templates only: a page:/article: layout is refused (422). ETag req. ' +
406
- 'Operates on the default site unless site_id is provided.',
494
+ '"taxonomy-category_faq" → "taxonomy-category_help"). Returns ' +
495
+ '{type, previous_type, new_etag}. Site-wide + immediate preview with ' +
496
+ 'dry_run:true, reversible via previous_type. ' +
497
+ 'THEME templates only: page:/article: is refused (422). ETag req.',
407
498
  inputSchema: {
408
499
  site_id: SITE_ID_SCHEMA,
409
500
  template_id: TEMPLATE_ID,
@@ -412,12 +503,23 @@ export function buildPagesTools(pool) {
412
503
  .min(1)
413
504
  .describe('The new archive/single TYPE assignment (the `type` condition ' +
414
505
  'pages_list surfaces), e.g. "taxonomy-category_help", ' +
415
- '"single-gloeckle_help", "archive-post", or the bare "template". ' +
506
+ '"single-example_help", "archive-post", or the bare "template". ' +
416
507
  'Read the current value via pages_list (the `type` column) or ' +
417
508
  'GET the template first. `page`/`article` are NOT valid here.'),
418
509
  // Required strict ETag (the re-type is a real mutation; the PHP
419
510
  // controller enforces If-Match → 428 if omitted, 412 on stale).
420
511
  etag: ETAG_STRICT,
512
+ // PAGES_LIBRARY_MEDIA-14: preflight the re-type without persisting.
513
+ dry_run: z
514
+ .boolean()
515
+ .optional()
516
+ .describe('Preflight ONLY. When true, validate the re-type (authz + ETag ' +
517
+ 'lock + theme-template-only + type validity + template-exists) ' +
518
+ 'and return {previous_type, would_change, dry_run:true} WITHOUT ' +
519
+ 'persisting — the template TYPE assignment governs which items ' +
520
+ 'render this layout SITE-WIDE and applies immediately once ' +
521
+ 'written, so preview first. Nothing is written (etag == new_etag); ' +
522
+ 'reversible anytime by re-calling with the returned previous_type.'),
421
523
  },
422
524
  outputSchema: TEMPLATE_SET_TYPE_OUTPUT_SCHEMA,
423
525
  annotations: mutating('Set Template Type'),
@@ -439,8 +541,7 @@ export function buildPagesTools(pool) {
439
541
  'menu_item (Joomla SEF route), dry_run (preflight, no persist). Returns page_id ' +
440
542
  '(WP) / article_id (Joomla), slug, template_id (page:{id} or article:{id}), etag, ' +
441
543
  'warnings. Joomla: a published article has NO SEF route until a menu item points ' +
442
- 'at it — pass menu_item, else frontend_url may 404. ' +
443
- 'Operates on the default site unless site_id is provided.',
544
+ 'at it — pass menu_item, else frontend_url may 404.',
444
545
  inputSchema: { site_id: SITE_ID_SCHEMA, ...PAGES_CREATE_INPUT },
445
546
  // No outputSchema: the response shape is a discriminated union
446
547
  // (201 wet vs 200 dry-run). page_get_layout uses the same pattern.
@@ -465,10 +566,9 @@ export function buildPagesTools(pool) {
465
566
  'hand-editing JSON. Give a REFERENCE (section_name from ' +
466
567
  'template_summary, or element_path) + list_source_name + a leaf_map ' +
467
568
  '(original leaf field-name → flat flow field; #parent + tuned props kept). ' +
468
- 'A section with several source blocks (Played/Next/Later) dynamizes in ONE ' +
569
+ 'A section with several source blocks dynamizes in ONE ' +
469
570
  'call via source_map ({match, source_name, leaf_map?}[]). Sub-query panels: ' +
470
- 'key by "<subquery>.<leaf>". Returns page + `summary`. Requires page.title. ' +
471
- 'Operates on the default site unless site_id is provided.',
571
+ 'key by "<subquery>.<leaf>". Returns page + `summary`. Preview with dry_run:true.',
472
572
  inputSchema: { site_id: SITE_ID_SCHEMA, ...PAGE_DYNAMIZE_INPUT },
473
573
  // No outputSchema: the response is the create wire-shape (WP
474
574
  // page_id / Joomla article_id discriminated union) plus the
@@ -481,13 +581,45 @@ export function buildPagesTools(pool) {
481
581
  locale: localeForSite(site),
482
582
  }))),
483
583
  }),
584
+ // ─── Task 5 (transparent-header plan) — header_transparency_set ───
585
+ // L2 (advanced): inspect + repair the three `header_transparent*`
586
+ // props on the section that actually governs the site header. NOT an
587
+ // L1 essential — it is the remediation for ONE specific
588
+ // misconfiguration, reached from a `header-contrast-*` finding's
589
+ // fix_hint or from an explicit customer request, not a hot path. The
590
+ // tool budget (~40 in `tools/list`) is spent on the workflow spine.
591
+ defineTool({
592
+ name: 'yootheme_builder_header_transparency_set',
593
+ description:
594
+ // ≤600 chars (catalog constraint). Every clause is a behaviour
595
+ // an agent gets WRONG without it — see the behavior-truth pins.
596
+ 'Inspect + repair the transparent header (nav over the hero). Always targets ' +
597
+ 'the FIRST section — YOOtheme reads these props there only; later ones are ' +
598
+ 'silently dead. dry_run defaults TRUE: returns current state, the ' +
599
+ 'theme-declared --uk-inverse, risk_class, confidence, recommendation, ' +
600
+ 'would_change. mode:"auto" writes the theme-declared colour; over a hero ' +
601
+ 'image/video it writes NO colour and says to LOOK. mode:"explicit" refuses a ' +
602
+ 'colour contradicting the theme (409) unless acknowledge_contrast_risk. ' +
603
+ 'transparent:false is always allowed.',
604
+ inputSchema: HEADER_TRANSPARENCY_SET_INPUT,
605
+ // No outputSchema: the payload is a rich diagnosis object whose
606
+ // `changes` map is keyed by whichever props moved — a strict
607
+ // structuredContent schema would either be permissive to the point
608
+ // of uselessness or reject a legitimate response.
609
+ annotations: mutating('Set Header Transparency'),
610
+ handler: async ({ site_id, ...rest }, extra) => withWriteSiteRetry(pool, site_id, 'yootheme_builder_header_transparency_set', async (client, site) => withRenderContext(withSiteMeta(await handleHeaderTransparencySet(client, rest, extra), site), buildRenderContext({
611
+ siteUrl: site.url,
612
+ parentTemplateId: rest.template_id,
613
+ locale: localeForSite(site),
614
+ }))),
615
+ }),
484
616
  defineTool({
485
617
  name: 'yootheme_builder_pages_delete',
486
618
  description: 'Delete a page (WordPress) or article (Joomla 5/6) with its YOOtheme per-page ' +
487
619
  'layout. Requires confirm:true (gate against accidental deletion). Default: trash ' +
488
620
  '(recoverable). force:true: permanent delete. Refuses non-page/article types with ' +
489
621
  '403 (Layer 9 defense). Returns deleted:true, page_id (WP) / article_id (Joomla), ' +
490
- 'force_deleted, bumped etag. Operates on the default site unless site_id is provided.',
622
+ 'force_deleted, bumped etag.',
491
623
  inputSchema: { site_id: SITE_ID_SCHEMA, ...PAGES_DELETE_INPUT },
492
624
  outputSchema: PAGES_DELETE_OUTPUT_SCHEMA,
493
625
  // A page/article delete (force:true = PERMANENT, non-idempotent) is