@rubytech/create-maxy-code 0.1.398 → 0.1.400
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/package.json +1 -1
- package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -0
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +17 -1
- package/payload/platform/plugins/docs/references/graph.md +15 -0
- package/payload/platform/plugins/docs/references/plugins-guide.md +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/connect-timeout-observability.test.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/connect-timeout-observability.test.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/connect-timeout-observability.test.js +123 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/connect-timeout-observability.test.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts +9 -0
- package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/imap.js +36 -12
- package/payload/platform/plugins/email/mcp/dist/lib/imap.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.js +18 -1
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.js.map +1 -1
- package/payload/platform/plugins/memory/references/schema-estate-agent.md +2 -0
- package/payload/platform/plugins/web-designer/.claude-plugin/plugin.json +8 -0
- package/payload/platform/plugins/web-designer/PLUGIN.md +46 -0
- package/payload/platform/plugins/web-designer/bin/resolve-cf.sh +56 -0
- package/payload/platform/plugins/web-designer/skills/brand-extract/SKILL.md +86 -0
- package/payload/platform/plugins/web-designer/skills/socials/SKILL.md +139 -0
- package/payload/platform/plugins/web-designer/skills/socials/references/pii-strip.md +64 -0
- package/payload/platform/plugins/web-designer/skills/socials/references/sources.md +66 -0
- package/payload/platform/plugins/web-designer/skills/socials/templates/og.html +92 -0
- package/payload/platform/plugins/web-designer/skills/socials/templates/social-posts.md +46 -0
- package/payload/platform/plugins/web-designer/skills/socials/templates/socials.html +87 -0
- package/payload/platform/plugins/web-designer/skills/web-design/SKILL.md +98 -0
- package/payload/platform/plugins/web-designer/skills/web-design/references/deploy.md +59 -0
- package/payload/platform/plugins/web-designer/skills/web-design/references/e-sign.md +39 -0
- package/payload/platform/plugins/web-designer/skills/web-design/references/gating.md +55 -0
- package/payload/platform/plugins/web-designer/skills/web-design/references/placeholders.md +110 -0
- package/payload/platform/plugins/web-designer/skills/web-design/references/sections.md +52 -0
- package/payload/platform/plugins/web-designer/skills/web-design/templates/index.html +1004 -0
- package/payload/platform/plugins/web-designer/skills/web-design/templates/llms.txt +32 -0
- package/payload/platform/plugins/web-designer/skills/web-intake/SKILL.md +107 -0
- package/payload/platform/plugins/web-designer/skills/web-intake/references/intake.md +103 -0
- package/payload/platform/plugins/whatsapp/PLUGIN.md +7 -1
- package/payload/platform/scripts/merge-whatsapp-split-conversations.mjs +264 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +22 -6
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/server/public/assets/{AdminLoginScreens-Bex48co9.js → AdminLoginScreens-rlk263u9.js} +1 -1
- package/payload/server/public/assets/{AdminShell-BOmS1NPG.js → AdminShell-C5vWTy2n.js} +1 -1
- package/payload/server/public/assets/{Checkbox-DDlXC5tq.js → Checkbox-Chz6bfy-.js} +1 -1
- package/payload/server/public/assets/{Transcript-CwItbNkx.js → Transcript-Bsx-bGgY.js} +2 -2
- package/payload/server/public/assets/{admin-n9zWE158.js → admin-BnZH758N.js} +1 -1
- package/payload/server/public/assets/{arc-C6-nr5UV.js → arc-DyzjCWmw.js} +1 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-XiVs-N7Z.js +1 -0
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-DIdho_T6.js → architectureDiagram-Q4EWVU46-qWfElD16.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-D5rTTBSy.js → blockDiagram-DXYQGD6D-DO2fFHC3.js} +1 -1
- package/payload/server/public/assets/{browser-CI8DR1YG.js → browser-CIrfyZWe.js} +1 -1
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-pfUVHdmy.js → c4Diagram-AHTNJAMY-Ca-q5gDe.js} +1 -1
- package/payload/server/public/assets/{calendar-WuIAN47H.js → calendar-D6Z8Kmrn.js} +1 -1
- package/payload/server/public/assets/channel-CYBkRz7E.js +1 -0
- package/payload/server/public/assets/chat-h7SytZHx.js +1 -0
- package/payload/server/public/assets/chevron-left-DSTeS1_g.js +1 -0
- package/payload/server/public/assets/{chunk-2KRD3SAO-EF6cdj8U.js → chunk-2KRD3SAO-CX2PxuDw.js} +1 -1
- package/payload/server/public/assets/{chunk-336JU56O-DE-lcZAc.js → chunk-336JU56O-D8l6U4Ne.js} +2 -2
- package/payload/server/public/assets/chunk-426QAEUC-CPhOHriU.js +1 -0
- package/payload/server/public/assets/{chunk-4BX2VUAB--ANCzzyn.js → chunk-4BX2VUAB-CG32Lkbh.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-DTO_X80J.js → chunk-4TB4RGXK-BnAQbO-v.js} +1 -1
- package/payload/server/public/assets/{chunk-55IACEB6-4J3nrz44.js → chunk-55IACEB6-ejqRH2M5.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-jH_eqAHL.js → chunk-5FUZZQ4R-ClkicQ-T.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-C-qU6p6k.js → chunk-5PVQY5BW-Be-mE_rx.js} +1 -1
- package/payload/server/public/assets/{chunk-67CJDMHE-DXxH9Snb.js → chunk-67CJDMHE-DPsemed2.js} +1 -1
- package/payload/server/public/assets/{chunk-7N4EOEYR-CCkxuP7A.js → chunk-7N4EOEYR-BQRQJETl.js} +1 -1
- package/payload/server/public/assets/{chunk-AA7GKIK3-CyItuM8d.js → chunk-AA7GKIK3-B1wcAVCK.js} +1 -1
- package/payload/server/public/assets/{chunk-BSJP7CBP-Bg7gxYNf.js → chunk-BSJP7CBP-DIua053h.js} +1 -1
- package/payload/server/public/assets/{chunk-CIAEETIT-BAXbOqv5.js → chunk-CIAEETIT-C9GHSCS9.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-vdoErQoc.js → chunk-EDXVE4YY-C1T3l1xj.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-Cz_z_6GK.js → chunk-ENJZ2VHE-8kPcXEQC.js} +1 -1
- package/payload/server/public/assets/{chunk-FMBD7UC4-Ck2jvOgW.js → chunk-FMBD7UC4-Bhoeq9sD.js} +1 -1
- package/payload/server/public/assets/{chunk-FOC6F5B3-DucR0Qo8.js → chunk-FOC6F5B3-BIBpJi8I.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-DolYDD7g.js → chunk-ICPOFSXX-DELXp6fj.js} +2 -2
- package/payload/server/public/assets/{chunk-K5T4RW27-FRzx7E5I.js → chunk-K5T4RW27-DdckXldP.js} +1 -1
- package/payload/server/public/assets/{chunk-KGLVRYIC-Bb5S4Bmc.js → chunk-KGLVRYIC-BJhXejKT.js} +1 -1
- package/payload/server/public/assets/{chunk-LIHQZDEY-c8VZJ-lU.js → chunk-LIHQZDEY-BSd8nyM2.js} +1 -1
- package/payload/server/public/assets/{chunk-ORNJ4GCN-BAeh-LZ1.js → chunk-ORNJ4GCN-CBi55uxq.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-BUg7Kysh.js → chunk-OYMX7WX6-Dfbzl8xh.js} +1 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-CDnAAZVJ.js +1 -0
- package/payload/server/public/assets/{chunk-U2HBQHQK-K4r17UTk.js → chunk-U2HBQHQK-BN1w8_Cq.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-Byh-CyxC.js → chunk-X2U36JSP-CrRay1NJ.js} +1 -1
- package/payload/server/public/assets/{chunk-XPW4576I-DC7lhr93.js → chunk-XPW4576I-C_ZE776O.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-B2cu7AEP.js → chunk-YZCP3GAM-CUSlqWBY.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-DH_p8sFX.js → chunk-ZZ45TVLE-BdPUfSiD.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-BdXUKVrS.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-DAq-uJr_.js +1 -0
- package/payload/server/public/assets/clone-DQl28-jK.js +1 -0
- package/payload/server/public/assets/{cose-bilkent-S5V4N54A-DeM6JI00.js → cose-bilkent-S5V4N54A-D4E9htTz.js} +1 -1
- package/payload/server/public/assets/{dagre-3drs3e4F.js → dagre-D1B5TQL1.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-C6S-VUtN.js → dagre-KV5264BT-ByuvYV7N.js} +1 -1
- package/payload/server/public/assets/data-CL7CyX1C.js +1 -0
- package/payload/server/public/assets/{diagram-5BDNPKRD-BkvA32I3.js → diagram-5BDNPKRD-B-0KduTa.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-CVwtRMPB.js → diagram-G4DWMVQ6-CSQmo3eB.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-DJ1JVMbd.js → diagram-MMDJMWI5-DK0kWiB3.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-rfj-crtX.js → diagram-TYMM5635-CYbK2reZ.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-TJPuweS5.js → erDiagram-SMLLAGMA-C-JQHvJF.js} +1 -1
- package/payload/server/public/assets/{flatten-BZkoyJ4e.js → flatten-ROVzlo1u.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-LQWEUjbV.js → flowDiagram-DWJPFMVM-OTOd2czg.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-DdYsxKRw.js → ganttDiagram-T4ZO3ILL-BWJ778ou.js} +1 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-BM-KaDZy.js +1 -0
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-sx8-fokM.js → gitGraphDiagram-UUTBAWPF-CkNcyISe.js} +1 -1
- package/payload/server/public/assets/graph-XZJZHzvv.js +51 -0
- package/payload/server/public/assets/{graph-labels-DigwgCWY.js → graph-labels-Cabqmf35.js} +1 -1
- package/payload/server/public/assets/{graphlib-DixeJdCf.js → graphlib-8z59Ng6C.js} +1 -1
- package/payload/server/public/assets/info-OMHHGYJF-CtZajyAB.js +1 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-C-_WYlmI.js +2 -0
- package/payload/server/public/assets/{isEmpty-VRKFml5R.js → isEmpty-DXf_sApt.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-BKUCuKhZ.js → ishikawaDiagram-UXIWVN3A-Cg7pRRGp.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-BafZP6pM.js → journeyDiagram-VCZTEJTY-CZT_4r_Z.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-DQ4c6wRv.js → kanban-definition-6JOO6SKY-Dp1Vd5YM.js} +1 -1
- package/payload/server/public/assets/{line-0_x1Wivu.js → line-UPAcwY66.js} +1 -1
- package/payload/server/public/assets/{linear-CZrU-J2V.js → linear-BeeahLKw.js} +1 -1
- package/payload/server/public/assets/maximize-2-DPe-Noig.js +1 -0
- package/payload/server/public/assets/{mermaid-parser.core-DwJ1cyfO.js → mermaid-parser.core-CLKle8df.js} +2 -2
- package/payload/server/public/assets/{mermaid.core-DnUst0He.js → mermaid.core-CwRj5rL5.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-oc-GwerE.js → mindmap-definition-QFDTVHPH-CNQUY9UJ.js} +1 -1
- package/payload/server/public/assets/{operator-D-Ad9OQ5.js → operator-BM_CDyCb.js} +1 -1
- package/payload/server/public/assets/{ordinal-v4prQcuq.js → ordinal-B2r5L1v4.js} +1 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-T-SYedp7.js +1 -0
- package/payload/server/public/assets/{page-BjAKwjlS.js → page-CMIbXzSg.js} +3 -3
- package/payload/server/public/assets/{page-CpSVszGe.js → page-E7I5sEVt.js} +1 -1
- package/payload/server/public/assets/{pdf-render-NQmflZ7K.js → pdf-render-m08Xm2Nm.js} +1 -1
- package/payload/server/public/assets/pie-ZZUOXDRM-BWECFVVd.js +1 -0
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-BnBtQNHM.js → pieDiagram-DEJITSTG-Scnq58FX.js} +1 -1
- package/payload/server/public/assets/{public-CbkiISZZ.js → public-CrdWCSk2.js} +1 -1
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-CkNqtzHI.js → quadrantDiagram-34T5L4WZ-wPfZo_vs.js} +1 -1
- package/payload/server/public/assets/radar-PYXPWWZC-Dx0YUklX.js +1 -0
- package/payload/server/public/assets/{reduce-0Mm1v1Rr.js → reduce-CFlDlJf5.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-D7qc27KT.js → requirementDiagram-MS252O5E-yYkH12K2.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-BYmlylsD.js → rotate-ccw-Dwtj0NJ7.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-2Y4sqRY7.js → sankeyDiagram-XADWPNL6-fVWRBzzV.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-hXOiSHuT.js → sequenceDiagram-FGHM5R23-Dbap3X6h.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-Dq4hhkLA.js → stateDiagram-FHFEXIEX-DkPLAqIh.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-DceWO1u6.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-Dn9wBUEw.js → timeline-definition-GMOUNBTQ-DabKBJyd.js} +1 -1
- package/payload/server/public/assets/treeView-SZITEDCU-C3197Rac.js +1 -0
- package/payload/server/public/assets/treemap-W4RFUUIX-BPrpx29h.js +1 -0
- package/payload/server/public/assets/{useSubAccountSwitcher-DxHQSOzT.css → useSubAccountSwitcher-BH-YjP0H.css} +1 -1
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-BKVHJiIt.js → vennDiagram-DHZGUBPP-CaKT7c-c.js} +1 -1
- package/payload/server/public/assets/wardley-RL74JXVD-DW4Jlbhw.js +1 -0
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-Bn9vvpdK.js → wardleyDiagram-NUSXRM2D-BZ5TTSOL.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-FIoMdPB3.js → xychartDiagram-5P7HB3ND-CLfeklJ9.js} +1 -1
- package/payload/server/public/browser.html +4 -4
- package/payload/server/public/calendar.html +5 -5
- package/payload/server/public/chat.html +10 -9
- package/payload/server/public/data.html +9 -8
- package/payload/server/public/graph.html +9 -8
- package/payload/server/public/index.html +12 -11
- package/payload/server/public/operator.html +12 -11
- package/payload/server/public/public.html +10 -9
- package/payload/server/server.js +41 -6
- package/payload/server/public/assets/architecture-YZFGNWBL-DdgmZ93B.js +0 -1
- package/payload/server/public/assets/channel-Da-UUyqx.js +0 -1
- package/payload/server/public/assets/chat-L6IVvC9k.js +0 -1
- package/payload/server/public/assets/chevron-left-uJSGyCGe.js +0 -1
- package/payload/server/public/assets/chunk-426QAEUC-B1kFAxo1.js +0 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-BjE-Glwf.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-Dol3Ogeh.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-VUMj7gWH.js +0 -1
- package/payload/server/public/assets/clone-m7oGrdBC.js +0 -1
- package/payload/server/public/assets/data-4K_negqE.js +0 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-cJEaiQq4.js +0 -1
- package/payload/server/public/assets/graph-UHR4Ez-V.js +0 -51
- package/payload/server/public/assets/info-OMHHGYJF-BUxfOTKa.js +0 -1
- package/payload/server/public/assets/infoDiagram-42DDH7IO-CD-d3Rgf.js +0 -2
- package/payload/server/public/assets/packet-4T2RLAQJ-B9U_2639.js +0 -1
- package/payload/server/public/assets/pie-ZZUOXDRM-Glh-lJOT.js +0 -1
- package/payload/server/public/assets/radar-PYXPWWZC-CTSgob8w.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-DTj9o2vZ.js +0 -1
- package/payload/server/public/assets/treeView-SZITEDCU-CXApkW1l.js +0 -1
- package/payload/server/public/assets/treemap-W4RFUUIX-DzvTXAcT.js +0 -1
- package/payload/server/public/assets/wardley-RL74JXVD-BExXV4oI.js +0 -1
- /package/payload/server/public/assets/{_baseFor-C0Y1mc3g.js → _baseFor-BVuIxZJh.js} +0 -0
- /package/payload/server/public/assets/{array-DqLCdDFv.js → array-B9UHiPd-.js} +0 -0
- /package/payload/server/public/assets/{cytoscape.esm-Cn5b4Cdu.js → cytoscape.esm-D5_TRwB-.js} +0 -0
- /package/payload/server/public/assets/{defaultLocale-CrJzLgRD.js → defaultLocale-5eAKkKJC.js} +0 -0
- /package/payload/server/public/assets/{dist-Dlks0Bfw.js → dist-CADCHk0t.js} +0 -0
- /package/payload/server/public/assets/{init-C0r9Gk5G.js → init-DlZdxViB.js} +0 -0
- /package/payload/server/public/assets/{katex-BFwFoEts.js → katex-Xo0SyWvP.js} +0 -0
- /package/payload/server/public/assets/{path-DIKpVbHL.js → path-6uRLdFF7.js} +0 -0
- /package/payload/server/public/assets/{preload-helper-C5gCWwwF.js → preload-helper-CvXuAEud.js} +0 -0
- /package/payload/server/public/assets/{rough.esm-DsHEmXUZ.js → rough.esm-Djo4Abte.js} +0 -0
- /package/payload/server/public/assets/{src-Bv7ZWAzF.js → src-Bi0fLV9t.js} +0 -0
- /package/payload/server/public/assets/{useSubAccountSwitcher-BRWAIbzr.js → useSubAccountSwitcher-BVCMAnWK.js} +0 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: socials
|
|
3
|
+
description: Render share-ready Open Graph and social tiles plus per-platform captions for a named subject — a built site or a bespoke topic — with a PII gate for any source that can carry client data. Resolves the subject through a source adapter into candidate images and copy, overlays brand typography, screenshots landscape (1200×628), square (1200×1200) and portrait (1080×1350) PNG tiles via the on-device browser, assembles a gallery and a captions file for all five platforms, and emits a redaction report reconciling every source item. For a built site it also wires the site's og:image. Never posts — the bundle is for operator review. Trigger phrases include "og images for the site", "Instagram tiles + captions for <subject>", "social collateral for <business>".
|
|
4
|
+
allowed-tools:
|
|
5
|
+
- Bash
|
|
6
|
+
- Read
|
|
7
|
+
- Write
|
|
8
|
+
- Edit
|
|
9
|
+
- Glob
|
|
10
|
+
- Grep
|
|
11
|
+
- mcp__plugin_browser_browser__browser-render
|
|
12
|
+
- mcp__plugin_browser_browser__browser-navigate
|
|
13
|
+
- mcp__plugin_browser_browser__browser-evaluate
|
|
14
|
+
- mcp__plugin_browser_browser__browser-resize
|
|
15
|
+
- mcp__plugin_browser_browser__browser-screenshot
|
|
16
|
+
- mcp__plugin_browser_browser__browser-wait-for
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# socials
|
|
20
|
+
|
|
21
|
+
One skill, one subject at a time. You take a named **subject**, resolve it through a
|
|
22
|
+
source adapter, and emit a publication bundle: exact-size PNG social tiles, captions
|
|
23
|
+
for all five platforms, a gallery, and — for a subject that can carry client data —
|
|
24
|
+
a redaction report. You **never post**. The bundle is for the operator to review
|
|
25
|
+
and sign off. You ship no business content; every fact is the operator's own data,
|
|
26
|
+
read from the resolved source.
|
|
27
|
+
|
|
28
|
+
## Inputs
|
|
29
|
+
|
|
30
|
+
- **subject** — a name (the business, the topic).
|
|
31
|
+
- **mode** — one of `microsite`, `bespoke`.
|
|
32
|
+
- **material** — the path the adapter reads (a built `site/`, or the operator's own
|
|
33
|
+
photos and notes).
|
|
34
|
+
- **output** — where the bundle lands (for `microsite`, the site's own `site/`).
|
|
35
|
+
|
|
36
|
+
## 1. Resolve the subject, and echo it
|
|
37
|
+
|
|
38
|
+
Resolve the subject through its adapter in [`references/sources.md`](references/sources.md)
|
|
39
|
+
to the normalized intermediate (`topic`, headline/sub candidates, copy facts,
|
|
40
|
+
candidate images, `piiBearing`). **Before you render anything, state the resolved
|
|
41
|
+
subject and the source mode back** — e.g. "Resolving Meridian Advisory in
|
|
42
|
+
`microsite` mode from its built site." A mis-resolution must be visible here, not
|
|
43
|
+
discovered in a finished tile.
|
|
44
|
+
|
|
45
|
+
## 2. PII gate (if `piiBearing`)
|
|
46
|
+
|
|
47
|
+
If the adapter's `piiBearing` is true (`bespoke`, unless the operator declared the
|
|
48
|
+
material brand-safe), run the gate in [`references/pii-strip.md`](references/pii-strip.md)
|
|
49
|
+
over **every** candidate image and **every** copy fact — before any of it reaches a
|
|
50
|
+
template. Write `redaction-report.md` reconciling every source photo and text field
|
|
51
|
+
as *used / redacted / rejected*. If every photo is rejected, stop and ask for a
|
|
52
|
+
usable image; never emit an imageless bundle. **Do not present a bundle without the
|
|
53
|
+
report.** `microsite` skips this step (the site is already published copy).
|
|
54
|
+
|
|
55
|
+
## 3. Render the tiles (browser plugin, PNG)
|
|
56
|
+
|
|
57
|
+
1. Build the resolved render page: copy [`templates/og.html`](templates/og.html) to
|
|
58
|
+
`<output>/og.html` and substitute `{{ CANDIDATES_JSON }}` with the vetted
|
|
59
|
+
candidate set (`{ key: { img, pos, head, sub }, … }`) plus the scalar tokens
|
|
60
|
+
(palette, eyebrow, specs, domain, phone). The logo and hero images resolve from
|
|
61
|
+
the output dir's `images/`.
|
|
62
|
+
2. Serve `<output>/` over a local HTTP server so relative paths and webfonts load
|
|
63
|
+
(`cd <output> && python3 -m http.server <port>` via `Bash`; `file://` will not
|
|
64
|
+
load the fonts).
|
|
65
|
+
3. For each tile, drive the on-device browser:
|
|
66
|
+
- `browser-navigate` to `http://localhost:<port>/og.html?format=F&hero=H`,
|
|
67
|
+
- `browser-resize` to the exact size — landscape `1200×628`, square `1200×1200`,
|
|
68
|
+
portrait `1080×1350`,
|
|
69
|
+
- `browser-evaluate` `document.fonts.ready.then(() => true)` (and a short settle)
|
|
70
|
+
so the webfonts have landed before capture,
|
|
71
|
+
- `browser-screenshot` to `<output>/socials/og-F-H.png`.
|
|
72
|
+
A **person** hero (`isPerson: true`) renders **portrait only** — a landscape or
|
|
73
|
+
square crop cuts off the head.
|
|
74
|
+
4. `Read` each finished PNG to confirm it rendered: logo legible, copy readable,
|
|
75
|
+
scrim adequate. A **sub-20KB** file is a blank page (server not running, wrong
|
|
76
|
+
port, or the page threw) — diagnose before re-running, never ship it.
|
|
77
|
+
|
|
78
|
+
## 4. Captions — all five platforms, plain register
|
|
79
|
+
|
|
80
|
+
Build `<output>/social-posts.md` from [`templates/social-posts.md`](templates/social-posts.md):
|
|
81
|
+
one block per tile, with copy for **Instagram, LinkedIn, TikTok, Facebook, and X**.
|
|
82
|
+
Write every line in the plain register (the `plainly` skill): no em-dashes, no
|
|
83
|
+
inflation vocabulary, no "it's not X, it's Y", no rhetorical openers, no emoji faking
|
|
84
|
+
enthusiasm. For a `piiBearing` subject, no caption names the client, the address or
|
|
85
|
+
postcode, any figure, any individual, or any email sender.
|
|
86
|
+
|
|
87
|
+
## 5. Gallery, and (microsite only) the og:image
|
|
88
|
+
|
|
89
|
+
Build `<output>/socials.html` from [`templates/socials.html`](templates/socials.html):
|
|
90
|
+
repeat the group block once per tile key. For a **person** hero (`isPerson: true`),
|
|
91
|
+
write only the portrait `<div class="tile">` cell for that key — delete the landscape
|
|
92
|
+
and square cells, since those files are never rendered for a person.
|
|
93
|
+
|
|
94
|
+
The OG snippet (`<div class="snippet">…</div>`) is **microsite only**. In `microsite`
|
|
95
|
+
mode, keep it, render the tiles into the site's `site/socials/`, and set the site's
|
|
96
|
+
`og:image`/`twitter:image` to `<WEBSITE_URL>socials/og-landscape-main.png` (a true
|
|
97
|
+
1200×628 card beats a raw hero for link previews; `WEBSITE_URL` is the site's absolute
|
|
98
|
+
URL from the brand pack). For a **bespoke** bundle there is no site to wire —
|
|
99
|
+
**delete the whole `<div class="snippet">` block** from the copied template before
|
|
100
|
+
substituting, so no `WEBSITE_URL` token survives.
|
|
101
|
+
|
|
102
|
+
## Output contract
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
<output>/
|
|
106
|
+
og.html # the resolved render page (kept for re-runs)
|
|
107
|
+
socials/
|
|
108
|
+
og-landscape-<key>.png og-square-<key>.png og-portrait-<key>.png # per tile
|
|
109
|
+
og-portrait-<person-key>.png # a person hero: portrait only
|
|
110
|
+
socials.html # gallery: each tile at size, download links
|
|
111
|
+
social-posts.md # captions per tile, all five platforms
|
|
112
|
+
redaction-report.md # piiBearing (bespoke) subjects only
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Completion criteria
|
|
116
|
+
|
|
117
|
+
- Every tile exists at its exact pixel size; a person tile is portrait only.
|
|
118
|
+
- Captions cover all five platforms; `socials.html` previews each tile with a working
|
|
119
|
+
download link.
|
|
120
|
+
- For a `piiBearing` subject, `redaction-report.md` is present and accounts for
|
|
121
|
+
**every** source photo and text field; no output names the client, the address, a
|
|
122
|
+
figure, an individual, or an email sender.
|
|
123
|
+
- In `microsite` mode, the site's `og:image`/`twitter:image` point at the landscape
|
|
124
|
+
main tile.
|
|
125
|
+
- `Read` each finished PNG — file size alone is not proof.
|
|
126
|
+
|
|
127
|
+
## When NOT to use
|
|
128
|
+
|
|
129
|
+
- The operator wants to **post** — this skill never posts; it produces a reviewable
|
|
130
|
+
bundle and stops.
|
|
131
|
+
- The deliverable is the site itself → `web-design`. The full run (site + tiles) →
|
|
132
|
+
`web-intake`, which calls this skill in `microsite` mode.
|
|
133
|
+
|
|
134
|
+
## Editions
|
|
135
|
+
|
|
136
|
+
Tile rendering drives the on-device `browser` plugin. Inside maxy-code that plugin is
|
|
137
|
+
present. A standalone (marketplace) install needs a browser MCP to render tiles; this
|
|
138
|
+
is a stated host dependency for the `socials` skill, not a silent gap. The `web-design`
|
|
139
|
+
build and its deploy do not depend on it.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# socials — PII gate
|
|
2
|
+
|
|
3
|
+
The gate runs for every `piiBearing` subject ([`sources.md`](sources.md)): `bespoke`
|
|
4
|
+
unless the operator declares the material brand-safe. It runs **before** anything
|
|
5
|
+
reaches a template — over every candidate image and every copy fact. `microsite`
|
|
6
|
+
skips it (the site is already published copy).
|
|
7
|
+
|
|
8
|
+
Detection is the agent's own visual and textual review against the taxonomy below
|
|
9
|
+
plus operator confirmation. No computer-vision model ships; the operator-approval
|
|
10
|
+
gate is the backstop. The skill **never posts** — the bundle is for review.
|
|
11
|
+
|
|
12
|
+
## Text never emitted
|
|
13
|
+
|
|
14
|
+
"Rendered-tile copy" means **every** text field that reaches a tile or a caption:
|
|
15
|
+
the headline and sub (from the candidate set), the eyebrow, the four spec tags
|
|
16
|
+
(`copyFacts.specs[]`), and every platform caption. The gate reviews all of them.
|
|
17
|
+
|
|
18
|
+
No caption and no rendered-tile copy may contain:
|
|
19
|
+
|
|
20
|
+
- the **client name** (person or company),
|
|
21
|
+
- the **site address or postcode** (or any fragment that identifies the address),
|
|
22
|
+
- any **contract, invoice, or payment figure** — any currency amount, any
|
|
23
|
+
percentage that reveals a financial position,
|
|
24
|
+
- any **named individual** (worker, client, neighbour, surveyor),
|
|
25
|
+
- any **email sender** address or name lifted from an instruction email.
|
|
26
|
+
|
|
27
|
+
The work is the story. "New studio open on the high street" is publishable.
|
|
28
|
+
"Invoice 3 for the Smiths at 14 Mill Lane, £42,310 due" is not.
|
|
29
|
+
|
|
30
|
+
## Photos that are rejected
|
|
31
|
+
|
|
32
|
+
Reject a candidate image (do not crop around it — reject the whole photo) if it
|
|
33
|
+
shows:
|
|
34
|
+
|
|
35
|
+
- a recognizable **face**,
|
|
36
|
+
- a **vehicle number plate**,
|
|
37
|
+
- a **house number, street sign, or nameplate**,
|
|
38
|
+
- **visible client paperwork** — a letter, an invoice, a drawing with a title block,
|
|
39
|
+
a screen showing client data.
|
|
40
|
+
|
|
41
|
+
A photo of the work itself — a finished space, a product, a completed project —
|
|
42
|
+
with none of the above is usable.
|
|
43
|
+
|
|
44
|
+
## The redaction report
|
|
45
|
+
|
|
46
|
+
Every `piiBearing` run writes `redaction-report.md` to the output. It reconciles
|
|
47
|
+
**every** source photo and **every** source text field to one disposition:
|
|
48
|
+
|
|
49
|
+
- **used** — appears in the bundle (which tile / caption),
|
|
50
|
+
- **redacted** — the source carried PII that was removed before use (name the field
|
|
51
|
+
and what was stripped),
|
|
52
|
+
- **rejected** — not used at all (name the reason: which photo category, which text
|
|
53
|
+
rule).
|
|
54
|
+
|
|
55
|
+
The report is the standing reconciliation that makes a silent leak visible: a source
|
|
56
|
+
PII field that appears in an output but is absent from the report's redacted/rejected
|
|
57
|
+
list is the failure signature. A **missing or empty report is itself a failure** —
|
|
58
|
+
the skill refuses to present a bundle without it.
|
|
59
|
+
|
|
60
|
+
## When every photo is rejected
|
|
61
|
+
|
|
62
|
+
If vetting rejects every candidate image, stop and ask the operator for a usable
|
|
63
|
+
photo. Never emit an imageless bundle, and never fall back to a stock or invented
|
|
64
|
+
image — the report shows all photos *rejected* and no tile is written.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# socials — source adapters
|
|
2
|
+
|
|
3
|
+
The `socials` skill resolves a named **subject** to a normalized intermediate that
|
|
4
|
+
the shared render core consumes. One adapter per subject kind. This file is the
|
|
5
|
+
contract and the **extension point**: a future subject (a testimonial, a launch, an
|
|
6
|
+
event) is a new row here, not a new skill.
|
|
7
|
+
|
|
8
|
+
## The normalized intermediate
|
|
9
|
+
|
|
10
|
+
Every adapter resolves its subject to this shape:
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
{
|
|
14
|
+
topic, // one short phrase naming what the tiles are about
|
|
15
|
+
headlineCandidates, // [string] — lead lines, strongest first
|
|
16
|
+
subCandidates, // [string] — supporting lines, paired to headlines
|
|
17
|
+
copyFacts, // { eyebrow, specs[], domain, phone } — overlay scalars
|
|
18
|
+
candidateImages, // [{ key, path, isPerson }] — vetted backgrounds
|
|
19
|
+
piiBearing // boolean — does this source carry client data?
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`candidateImages[].key` becomes the tile `hero` and the filename suffix
|
|
24
|
+
(`og-<format>-<key>.png`). `isPerson: true` marks a hero that must render
|
|
25
|
+
**portrait only** (a landscape or square crop cuts off the head). `piiBearing`
|
|
26
|
+
decides whether the PII gate in [`pii-strip.md`](pii-strip.md) runs before any of
|
|
27
|
+
this reaches a template.
|
|
28
|
+
|
|
29
|
+
## Adapters
|
|
30
|
+
|
|
31
|
+
### `microsite` — `piiBearing: false`
|
|
32
|
+
|
|
33
|
+
Reads a built `site/` directory (the output of `web-design`): the brand pack
|
|
34
|
+
(`brand.md` tokens, `images/logo.png`) and the curated hero photos at the fixed
|
|
35
|
+
paths the site uses:
|
|
36
|
+
|
|
37
|
+
| key | image | leads with |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| `main` | `images/hero-01.webp` | the business's lead line |
|
|
40
|
+
| `detail` | `images/hero-03.webp` | a quality / detail line |
|
|
41
|
+
| `work` | `images/hero-06.webp` | a what-we-do line |
|
|
42
|
+
| `founder` | `images/founder-portrait.webp` | "meet the…" — **`isPerson: true`, portrait only** |
|
|
43
|
+
|
|
44
|
+
`copyFacts` for `microsite` is the brand pack's own credential cells (sector,
|
|
45
|
+
services, areas, years) plus `domain`, `phone`, and the site's full `WEBSITE_URL`
|
|
46
|
+
(the microsite-only og:image wiring needs the absolute URL, not just the domain).
|
|
47
|
+
|
|
48
|
+
Brand-safe by nature (the site is already published copy), so the PII gate is
|
|
49
|
+
skipped. This is the path `web-intake` calls as its final stage; it also wires the
|
|
50
|
+
site's `og:image`/`twitter:image` to the landscape `main` tile. If the site has no
|
|
51
|
+
figurehead, drop the `founder` key.
|
|
52
|
+
|
|
53
|
+
### `bespoke` — `piiBearing: true` (operator may waive)
|
|
54
|
+
|
|
55
|
+
An operator-chosen topic with the operator's own photos and notes. The operator
|
|
56
|
+
supplies the topic, the headline/sub ideas, and the candidate images. The PII gate
|
|
57
|
+
runs by default; the operator may declare the material brand-safe (their own
|
|
58
|
+
marketing photos, no client identifiable) to skip it — that declaration is the only
|
|
59
|
+
way the gate is waived for a bespoke subject.
|
|
60
|
+
|
|
61
|
+
## Adding a subject later
|
|
62
|
+
|
|
63
|
+
A new subject kind (testimonial, launch, event, …) is a new adapter section here:
|
|
64
|
+
name what it reads, how it maps to the normalized intermediate, and its `piiBearing`
|
|
65
|
+
value. No new skill, no new render core, no new caption register. If the source can
|
|
66
|
+
carry client data, set `piiBearing: true` and the existing gate covers it.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en-GB">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>{{ COMPANY_SHORT }} social tile renderer</title>
|
|
6
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
7
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
8
|
+
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,500;1,400;1,500&family=Lora:ital,wght@0,400;1,400&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
|
|
9
|
+
<style>
|
|
10
|
+
/* Brand tokens — substituted per subject by the resolving adapter. */
|
|
11
|
+
:root{
|
|
12
|
+
--ink:{{ INK_900 }}; --ink-2:{{ INK_700 }}; --paper:{{ PAPER_100 }}; --paper-2:{{ PAPER_200 }};
|
|
13
|
+
--copper:{{ ACCENT }}; --copper-soft:{{ ACCENT_SOFT }}; --brand-grey:{{ BRAND_GREY }};
|
|
14
|
+
--serif:'Cormorant Garamond',Garamond,serif; --body:'Lora',Georgia,serif; --sans:'Inter',system-ui,sans-serif;
|
|
15
|
+
}
|
|
16
|
+
*{margin:0;padding:0;box-sizing:border-box;}
|
|
17
|
+
html,body{background:var(--ink);}
|
|
18
|
+
body{display:flex;align-items:flex-start;justify-content:flex-start;}
|
|
19
|
+
.stage{position:relative;overflow:hidden;background:var(--ink);color:#fff;}
|
|
20
|
+
.stage.landscape{width:1200px;min-width:1200px;max-width:1200px;height:628px;min-height:628px;max-height:628px;}
|
|
21
|
+
.stage.square{width:1200px;min-width:1200px;max-width:1200px;height:1200px;min-height:1200px;max-height:1200px;}
|
|
22
|
+
.stage.portrait{width:1080px;min-width:1080px;max-width:1080px;height:1350px;min-height:1350px;max-height:1350px;}
|
|
23
|
+
.hero{position:absolute;inset:0;background-size:cover;background-position:center;}
|
|
24
|
+
.scrim{position:absolute;inset:0;
|
|
25
|
+
background:linear-gradient(180deg, rgba(0,0,0,.22) 0%, rgba(0,0,0,.10) 32%, rgba(0,0,0,.55) 72%, rgba(0,0,0,.84) 100%);}
|
|
26
|
+
/* real brand logo on a white chip so it reads on any photo */
|
|
27
|
+
.logo-chip{position:absolute;top:48px;left:64px;background:#fff;padding:16px 22px;
|
|
28
|
+
box-shadow:0 6px 22px rgba(0,0,0,.30);border-top:3px solid var(--copper);}
|
|
29
|
+
.logo-chip img{display:block;height:84px;width:auto;}
|
|
30
|
+
.copy{position:absolute;left:64px;right:64px;bottom:60px;}
|
|
31
|
+
.eyebrow{font-family:var(--sans);font-weight:600;font-size:17px;letter-spacing:.30em;text-transform:uppercase;color:var(--copper);margin-bottom:18px;}
|
|
32
|
+
.headline{font-family:var(--serif);font-style:italic;font-weight:500;font-size:72px;line-height:1.02;color:#fff;}
|
|
33
|
+
.sub{font-family:var(--body);font-style:italic;font-size:26px;color:#fff;opacity:.94;margin-top:14px;}
|
|
34
|
+
.rule{width:64px;height:2px;background:var(--copper);margin:24px 0 20px;}
|
|
35
|
+
.specs{font-family:var(--sans);font-weight:500;font-size:16px;letter-spacing:.10em;text-transform:uppercase;color:#fff;opacity:.92;}
|
|
36
|
+
.specs span+span::before{content:"·";color:var(--copper);margin:0 9px;}
|
|
37
|
+
.foot{display:flex;justify-content:space-between;align-items:center;margin-top:26px;padding-top:18px;
|
|
38
|
+
border-top:1px solid rgba(255,255,255,.28);
|
|
39
|
+
font-family:var(--sans);font-weight:600;font-size:15px;letter-spacing:.16em;text-transform:uppercase;color:#fff;}
|
|
40
|
+
.foot .r{color:var(--copper);}
|
|
41
|
+
.stage.square .headline,.stage.portrait .headline{font-size:84px;}
|
|
42
|
+
.stage.square .sub,.stage.portrait .sub{font-size:30px;}
|
|
43
|
+
.stage.square .eyebrow,.stage.portrait .eyebrow{font-size:19px;}
|
|
44
|
+
.stage.square .logo-chip img,.stage.portrait .logo-chip img{height:62px;}
|
|
45
|
+
.stage.portrait .copy{bottom:78px;}
|
|
46
|
+
.stage.square .copy{bottom:72px;}
|
|
47
|
+
</style>
|
|
48
|
+
</head>
|
|
49
|
+
<body>
|
|
50
|
+
<div class="stage" id="stage">
|
|
51
|
+
<div class="hero" id="hero"></div>
|
|
52
|
+
<div class="scrim"></div>
|
|
53
|
+
<div class="logo-chip"><img src="images/logo.png" alt="{{ COMPANY_NAME }}"></div>
|
|
54
|
+
<div class="copy">
|
|
55
|
+
<div class="eyebrow">{{ TILE_EYEBROW }}</div>
|
|
56
|
+
<h1 class="headline" id="headline"></h1>
|
|
57
|
+
<div class="sub" id="sub"></div>
|
|
58
|
+
<div class="rule"></div>
|
|
59
|
+
<div class="specs"><span>{{ TILE_SPEC_1 }}</span><span>{{ TILE_SPEC_2 }}</span><span>{{ TILE_SPEC_3 }}</span><span>{{ TILE_SPEC_4 }}</span></div>
|
|
60
|
+
<div class="foot"><span>{{ WEBSITE_DOMAIN }}</span><span class="r">{{ PHONE_DISPLAY }}</span></div>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
<script>
|
|
64
|
+
// CANDIDATES is substituted by the socials skill AFTER the PII gate. It is an
|
|
65
|
+
// object keyed by hero name; each entry names a background image (a path that
|
|
66
|
+
// resolves from this file's directory) and the headline/sub it leads with:
|
|
67
|
+
// { main: { img:'images/hero-01.webp', pos:'center', head:'…', sub:'…' }, … }
|
|
68
|
+
// For a built site the adapter supplies the brand themes; for a bespoke
|
|
69
|
+
// subject it supplies the vetted candidate images and the copy facts that
|
|
70
|
+
// survived redaction. No client PII ever reaches this object — the gate strips
|
|
71
|
+
// it upstream.
|
|
72
|
+
//
|
|
73
|
+
// A person hero (a founder portrait, a person) must be rendered
|
|
74
|
+
// PORTRAIT ONLY: a landscape or square crop cuts off the head. The socials
|
|
75
|
+
// skill enforces this by only emitting the portrait format for a person hero.
|
|
76
|
+
const CANDIDATES = {{ CANDIDATES_JSON }};
|
|
77
|
+
// ?format=landscape|square|portrait & ?hero=<key from CANDIDATES>
|
|
78
|
+
const q = new URLSearchParams(location.search);
|
|
79
|
+
const keys = Object.keys(CANDIDATES);
|
|
80
|
+
const fmt = q.get('format') || 'landscape';
|
|
81
|
+
const hero = q.get('hero') || keys[0];
|
|
82
|
+
const h = CANDIDATES[hero] || CANDIDATES[keys[0]];
|
|
83
|
+
const stage = document.getElementById('stage');
|
|
84
|
+
stage.classList.add(fmt);
|
|
85
|
+
const heroEl = document.getElementById('hero');
|
|
86
|
+
heroEl.style.backgroundImage = "url('" + h.img + "')";
|
|
87
|
+
heroEl.style.backgroundPosition = h.pos || 'center';
|
|
88
|
+
document.getElementById('headline').textContent = h.head;
|
|
89
|
+
document.getElementById('sub').textContent = h.sub;
|
|
90
|
+
</script>
|
|
91
|
+
</body>
|
|
92
|
+
</html>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# {{ COMPANY_NAME }} — social captions
|
|
2
|
+
|
|
3
|
+
Paste-ready copy for each tile. Phone {{ PHONE_DISPLAY }} · {{ WEBSITE_DOMAIN }}
|
|
4
|
+
{{ FACEBOOK_HANDLE }} · {{ INSTAGRAM_HANDLE }} · {{ TIKTOK_HANDLE }}
|
|
5
|
+
|
|
6
|
+
Write every caption in the plain register (the `plainly` skill): no em-dashes, no
|
|
7
|
+
inflation words (discover, unlock, nestled, stunning, boasts, bespoke-where-it-
|
|
8
|
+
isn't), no "it's not X, it's Y", no rhetorical openers, no emoji used to fake
|
|
9
|
+
enthusiasm. Read each one aloud; if it sounds like a marketing bot, rewrite it.
|
|
10
|
+
|
|
11
|
+
For a PII-bearing (bespoke) subject no caption may name the client, the address
|
|
12
|
+
or postcode, any contract / invoice / payment figure, any named individual, or
|
|
13
|
+
any email sender. The work is the story; the people and the money are not.
|
|
14
|
+
|
|
15
|
+
Repeat the block below once per rendered tile. `{{ TILE_KEY }}` matches the tile
|
|
16
|
+
filename (`og-*-{{ TILE_KEY }}.png`); a person tile is portrait only, so its block
|
|
17
|
+
references the portrait image alone.
|
|
18
|
+
|
|
19
|
+
## Per-platform shape (a cap, not a target)
|
|
20
|
+
|
|
21
|
+
- **Instagram (feed + portrait)** — name first, then what is visible, then "link in
|
|
22
|
+
bio" if there is a site, then 4–6 hashtags. The portrait line leads with what is
|
|
23
|
+
in that specific hero.
|
|
24
|
+
- **LinkedIn** — three short paragraphs, professional, plain.
|
|
25
|
+
- **TikTok** — one short hook line plus 3–5 hashtags.
|
|
26
|
+
- **Facebook** — three short paragraphs, warmer than LinkedIn.
|
|
27
|
+
- **X** — under 280 characters, one clear line.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## {{ TILE_HEAD }} — `og-*-{{ TILE_KEY }}.png`
|
|
32
|
+
|
|
33
|
+
**Instagram (feed + portrait)**
|
|
34
|
+
{{ TILE_KEY_INSTAGRAM }}
|
|
35
|
+
|
|
36
|
+
**LinkedIn**
|
|
37
|
+
{{ TILE_KEY_LINKEDIN }}
|
|
38
|
+
|
|
39
|
+
**TikTok**
|
|
40
|
+
{{ TILE_KEY_TIKTOK }}
|
|
41
|
+
|
|
42
|
+
**Facebook**
|
|
43
|
+
{{ TILE_KEY_FACEBOOK }}
|
|
44
|
+
|
|
45
|
+
**X**
|
|
46
|
+
{{ TILE_KEY_X }}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en-GB">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>{{ COMPANY_NAME }} · Social collateral</title>
|
|
7
|
+
<link rel="icon" href="favicon.png" type="image/png">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
9
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
10
|
+
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,500;1,400&family=Lora:ital,wght@0,400;1,400&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
|
|
11
|
+
<style>
|
|
12
|
+
/* Brand tokens — substituted per subject by the resolving adapter. */
|
|
13
|
+
:root{
|
|
14
|
+
--ink:{{ INK_900 }}; --ink-2:{{ INK_700 }}; --slate:{{ INK_400 }}; --paper:{{ PAPER_100 }}; --paper-2:{{ PAPER_200 }};
|
|
15
|
+
--rule:{{ RULE_100 }}; --gold:{{ ACCENT_TEXT }}; --copper:{{ ACCENT }}; --brand-grey:{{ BRAND_GREY }};
|
|
16
|
+
--serif:'Cormorant Garamond',Garamond,serif; --body:'Lora',Georgia,serif; --sans:'Inter',system-ui,sans-serif;
|
|
17
|
+
}
|
|
18
|
+
*{margin:0;padding:0;box-sizing:border-box;}
|
|
19
|
+
body{font-family:var(--body);background:var(--paper-2);color:var(--ink);line-height:1.6;}
|
|
20
|
+
img{display:block;max-width:100%;height:auto;}
|
|
21
|
+
a{color:inherit;}
|
|
22
|
+
header{background:var(--paper);border-bottom:1px solid var(--rule);padding:22px 32px;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:16px;}
|
|
23
|
+
header img{height:64px;width:auto;}
|
|
24
|
+
header .t{font-family:var(--sans);font-size:11px;font-weight:600;letter-spacing:.28em;text-transform:uppercase;color:var(--brand-grey);}
|
|
25
|
+
.wrap{max-width:1100px;margin:0 auto;padding:48px 32px 64px;}
|
|
26
|
+
.lede{font-family:var(--serif);font-style:italic;font-size:28px;color:var(--ink);margin-bottom:8px;}
|
|
27
|
+
.lede::after{content:'';display:block;width:40px;height:2px;background:var(--copper);margin-top:16px;}
|
|
28
|
+
.intro{font-size:15px;color:var(--ink-2);max-width:680px;margin-bottom:14px;}
|
|
29
|
+
.snippet{background:var(--ink);color:#fff;border-radius:2px;padding:20px 22px;margin:22px 0 40px;overflow-x:auto;}
|
|
30
|
+
.snippet .h{font-family:var(--sans);font-size:10px;font-weight:600;letter-spacing:.24em;text-transform:uppercase;color:var(--copper);margin-bottom:12px;}
|
|
31
|
+
.snippet pre{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px;line-height:1.7;color:#e9e6df;white-space:pre;}
|
|
32
|
+
.group{margin-top:48px;}
|
|
33
|
+
.group h2{font-family:var(--serif);font-style:italic;font-size:24px;color:var(--ink);margin-bottom:4px;}
|
|
34
|
+
.group h2 .role{font-family:var(--sans);font-size:10px;font-weight:600;letter-spacing:.24em;text-transform:uppercase;color:var(--copper);margin-left:12px;vertical-align:middle;}
|
|
35
|
+
.group .cap{font-size:13.5px;color:var(--ink-2);max-width:680px;margin:8px 0 20px;border-left:3px solid var(--copper);padding-left:14px;}
|
|
36
|
+
.tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:22px;align-items:end;}
|
|
37
|
+
.tile{background:var(--paper);border:1px solid var(--rule);padding:12px;}
|
|
38
|
+
.tile img{width:100%;border:1px solid var(--rule);}
|
|
39
|
+
.tile .meta{display:flex;align-items:center;justify-content:space-between;margin-top:10px;}
|
|
40
|
+
.tile .fmt{font-family:var(--sans);font-size:10px;font-weight:600;letter-spacing:.16em;text-transform:uppercase;color:var(--slate);}
|
|
41
|
+
.tile .dl{font-family:var(--sans);font-size:10px;font-weight:600;letter-spacing:.16em;text-transform:uppercase;color:var(--paper);background:var(--ink);text-decoration:none;padding:7px 12px;}
|
|
42
|
+
.tile .dl:hover{background:var(--copper);}
|
|
43
|
+
footer{border-top:1px solid var(--rule);padding:28px 32px;text-align:center;font-family:var(--sans);font-size:10px;font-weight:500;letter-spacing:.2em;text-transform:uppercase;color:var(--slate);}
|
|
44
|
+
</style>
|
|
45
|
+
</head>
|
|
46
|
+
<body>
|
|
47
|
+
<header>
|
|
48
|
+
<img src="images/logo.png" alt="{{ COMPANY_NAME }}">
|
|
49
|
+
<span class="t">Social collateral</span>
|
|
50
|
+
</header>
|
|
51
|
+
<div class="wrap">
|
|
52
|
+
<div class="lede">{{ GALLERY_LEDE }}</div>
|
|
53
|
+
<p class="intro">{{ GALLERY_INTRO }} Landscape (1200×628) for Facebook, LinkedIn and link previews; square (1200×1200) for the Instagram feed; portrait (1080×1350) for Instagram and Reels covers. Captions for every tile are in <a href="social-posts.md" style="text-decoration:underline;text-decoration-color:rgba(0,0,0,.25);">social-posts.md</a>.</p>
|
|
54
|
+
|
|
55
|
+
<!--
|
|
56
|
+
OG SNIPPET — microsite mode only. A bespoke bundle has no site to wire, so
|
|
57
|
+
omit this whole block for a bespoke subject. When present, it points at the
|
|
58
|
+
landscape main tile (PNG).
|
|
59
|
+
-->
|
|
60
|
+
<div class="snippet">
|
|
61
|
+
<div class="h">Paste into the page <head> for link previews</div>
|
|
62
|
+
<pre><meta property="og:image" content="{{ WEBSITE_URL }}socials/og-landscape-main.png">
|
|
63
|
+
<meta property="og:image:width" content="1200">
|
|
64
|
+
<meta property="og:image:height" content="628">
|
|
65
|
+
<meta name="twitter:card" content="summary_large_image">
|
|
66
|
+
<meta name="twitter:image" content="{{ WEBSITE_URL }}socials/og-landscape-main.png"></pre>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<!--
|
|
70
|
+
GROUP BLOCK — repeat once per resolved tile key. {{ TILE_KEY }} matches the
|
|
71
|
+
filename suffix; {{ TILE_HEAD }} / {{ TILE_CAPTION }} are the subject's copy.
|
|
72
|
+
A standard tile has all three formats. A PERSON tile is portrait only: emit
|
|
73
|
+
just the portrait cell for it (drop the landscape and square cells).
|
|
74
|
+
-->
|
|
75
|
+
<div class="group">
|
|
76
|
+
<h2>{{ TILE_HEAD }} <span class="role">{{ TILE_KEY }}</span></h2>
|
|
77
|
+
<p class="cap">{{ TILE_CAPTION }}</p>
|
|
78
|
+
<div class="tiles">
|
|
79
|
+
<div class="tile"><img src="socials/og-landscape-{{ TILE_KEY }}.png" alt="{{ TILE_HEAD }}, landscape tile"><div class="meta"><span class="fmt">Landscape · 1200×628</span><a class="dl" href="socials/og-landscape-{{ TILE_KEY }}.png" download>Download</a></div></div>
|
|
80
|
+
<div class="tile"><img src="socials/og-square-{{ TILE_KEY }}.png" alt="{{ TILE_HEAD }}, square tile"><div class="meta"><span class="fmt">Square · 1200×1200</span><a class="dl" href="socials/og-square-{{ TILE_KEY }}.png" download>Download</a></div></div>
|
|
81
|
+
<div class="tile"><img src="socials/og-portrait-{{ TILE_KEY }}.png" alt="{{ TILE_HEAD }}, portrait tile"><div class="meta"><span class="fmt">Portrait · 1080×1350</span><a class="dl" href="socials/og-portrait-{{ TILE_KEY }}.png" download>Download</a></div></div>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
<footer>© {{ COPYRIGHT_YEAR }} {{ COMPANY_LEGAL_NAME }} · {{ TAGLINE }}</footer>
|
|
86
|
+
</body>
|
|
87
|
+
</html>
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: web-design
|
|
3
|
+
description: Build a finished, scrollable single-page website for any business from the placeholder template, a brand pack, and the operator's curated photos and copy. Produces a self-contained site/ directory — index.html (hero rotator, about, what-we-do, how-we-work, optional founder section, testimonial, services, key facts, FAQ, CTA, footer, back-to-top) plus an llms.txt — with structured data (JSON-LD) whose type is operator-supplied. Domain-neutral, so no business category is baked in. Use after brand-extract, or directly when the brand pack and copy already exist. Trigger phrases include "build the site", "lay out the landing page", "turn this brand plus photos into a one-page site".
|
|
4
|
+
allowed-tools:
|
|
5
|
+
- Bash
|
|
6
|
+
- Read
|
|
7
|
+
- Write
|
|
8
|
+
- Edit
|
|
9
|
+
- Glob
|
|
10
|
+
- Grep
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# web-design
|
|
14
|
+
|
|
15
|
+
Renders the canonical single-page site from `templates/index.html` and
|
|
16
|
+
`templates/llms.txt` by substituting every `{{ PLACEHOLDER }}`. The template is
|
|
17
|
+
the spec: copy it, fill it, never rebuild it from scratch from this prose.
|
|
18
|
+
|
|
19
|
+
## Canonical template — the only starting point
|
|
20
|
+
|
|
21
|
+
`templates/index.html` ships with this skill. It encodes the entire site —
|
|
22
|
+
structure, CSS, the JS hero rotator + drawer + sticky sub-nav + back-to-top, the
|
|
23
|
+
responsive rules, and the JSON-LD — with every business-specific value as a
|
|
24
|
+
`{{ TOKEN }}`. Copy it to `<output>/site/index.html` and substitute. Do the same
|
|
25
|
+
with `templates/llms.txt`. The full token list and what fills each is in
|
|
26
|
+
[`references/placeholders.md`](references/placeholders.md); the section catalogue
|
|
27
|
+
and the image-naming convention are in [`references/sections.md`](references/sections.md).
|
|
28
|
+
|
|
29
|
+
A run that hand-builds the HTML from this prose, or starts from a previous
|
|
30
|
+
client's rendered `index.html`, is a defect — it drifts every cycle. Copy the
|
|
31
|
+
canonical and substitute.
|
|
32
|
+
|
|
33
|
+
## Substitution contract
|
|
34
|
+
|
|
35
|
+
- After substitution, `grep "{{" site/index.html site/llms.txt` returns **zero**
|
|
36
|
+
matches. Any surviving `{{ }}` is a bug.
|
|
37
|
+
- Palette tokens come from the brand pack's ten values. `ACCENT_TEXT` is the
|
|
38
|
+
deepened, legible form of `ACCENT` — not the same value.
|
|
39
|
+
- `SCHEMA_TYPE` is operator-supplied. Default to `LocalBusiness`; use
|
|
40
|
+
`Organization` for a business with no premises, or a more specific schema.org
|
|
41
|
+
subtype when one fits. Never fall back to a trade or industry type.
|
|
42
|
+
- Copy tokens are written in the plain register: no em-dashes in body text, no
|
|
43
|
+
inflation vocabulary, no marketing cadence. A real client testimonial is used
|
|
44
|
+
**verbatim**; never reword it.
|
|
45
|
+
- Never invent a fact to fill a token. If intake did not collect it, go back and
|
|
46
|
+
ask. An empty contact detail or a guessed year is a defect.
|
|
47
|
+
|
|
48
|
+
## Images — curate by content, name by convention
|
|
49
|
+
|
|
50
|
+
The template references images by the fixed convention in
|
|
51
|
+
[`references/sections.md`](references/sections.md) so the operator can tell what
|
|
52
|
+
each slot is from the filename. Curate by **content, verified by eye**, never by
|
|
53
|
+
filename: lead the hero with the strongest "arrive here" shot, put the best
|
|
54
|
+
result in the testimonial, distribute the rest. A person's photo is founder-only;
|
|
55
|
+
it never goes in the rotator as a landscape crop. Every `alt` token is filled
|
|
56
|
+
from the real photo it sits on.
|
|
57
|
+
|
|
58
|
+
### The 2000px rule
|
|
59
|
+
|
|
60
|
+
Never `Read` an image whose longest edge exceeds 2000px — measure first, preview a
|
|
61
|
+
downscaled `/tmp` copy if larger, delete it. Optimise to webp: body tier ~1300px
|
|
62
|
+
q82; hero tier source-resolution q88; the portrait kept tall.
|
|
63
|
+
|
|
64
|
+
## Verify every page state
|
|
65
|
+
|
|
66
|
+
Serve `site/` over a local HTTP server and check, at a 1280px desktop and a 390px
|
|
67
|
+
mobile viewport: no horizontal overflow, no broken images, the hero rotates, the
|
|
68
|
+
drawer opens with every link, the sticky sub-nav reveals on scroll, the
|
|
69
|
+
back-to-top appears and works, and both JSON-LD blocks are valid JSON once
|
|
70
|
+
substituted. Bottom-align paired images and equal-height cards where the layout
|
|
71
|
+
calls for it.
|
|
72
|
+
|
|
73
|
+
## Completion criteria
|
|
74
|
+
|
|
75
|
+
- `site/index.html` and `site/llms.txt` exist with zero surviving `{{ }}`.
|
|
76
|
+
- Every referenced image resolves; alts describe the real photos.
|
|
77
|
+
- Desktop + mobile verified; hero, drawer, sub-nav, back-to-top all work.
|
|
78
|
+
- Both JSON-LD blocks (the business, typed by the operator's `SCHEMA_TYPE`, and
|
|
79
|
+
`FAQPage`) parse and match the visible copy.
|
|
80
|
+
|
|
81
|
+
## Deploy hand-off
|
|
82
|
+
|
|
83
|
+
The build ends at a self-contained `site/` tree. To go live, the tree becomes the
|
|
84
|
+
canonical Cloudflare Pages source at `<root>/pages/<project>/`, where `<root>`
|
|
85
|
+
comes from the resolver — never hardcode it:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
ROOT=$(sh "$(dirname "$0")/../../bin/resolve-cf.sh" site-root) # from the skill dir
|
|
89
|
+
# assemble <ROOT>/pages/<project>/ with the built site as its output dir
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Then follow [`references/deploy.md`](references/deploy.md) to publish, attach the
|
|
93
|
+
custom domain, wire any form to D1, and gate "done" on a live `200`. For a page or
|
|
94
|
+
document that must sit behind a login, follow [`references/gating.md`](references/gating.md).
|
|
95
|
+
For a document that must be signed, follow [`references/e-sign.md`](references/e-sign.md).
|
|
96
|
+
|
|
97
|
+
This skill runs in the admin/main session. It dispatches no specialist worker and
|
|
98
|
+
writes to no graph or memory store.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Deploy hand-off
|
|
2
|
+
|
|
3
|
+
The build produces one canonical source tree at `<root>/pages/<project>/`, where
|
|
4
|
+
`<root>` is `bin/resolve-cf.sh site-root` and `<project>` is the site slug. That
|
|
5
|
+
tree holds the built output directory, `wrangler.toml`, and
|
|
6
|
+
`functions/api/<name>.ts` for any form the site captures. There is exactly one
|
|
7
|
+
tree per site; never deploy from a `.bak` clone or a stale sibling.
|
|
8
|
+
|
|
9
|
+
This reference composes existing deploy mechanics. It does not fork them.
|
|
10
|
+
|
|
11
|
+
## Inside maxy-code: hand off to `cloudflare:site-deploy`
|
|
12
|
+
|
|
13
|
+
When the host carries the `cloudflare` plugin, hand the canonical tree to the
|
|
14
|
+
`cloudflare:site-deploy` skill and let it run unchanged. It resolves its own
|
|
15
|
+
account Pages-and-D1 token, runs `wrangler pages deploy` to the production alias,
|
|
16
|
+
attaches the custom domain, wires a contact or waitlist form to D1, checks Open
|
|
17
|
+
Graph correctness, and gates "done" on a cache-busted live `200` on the custom
|
|
18
|
+
domain. Do not re-implement any of that here — `site-deploy` is the deploy
|
|
19
|
+
authority; this skill only produces the tree it consumes.
|
|
20
|
+
|
|
21
|
+
## Standalone: the generic Cloudflare Pages sequence
|
|
22
|
+
|
|
23
|
+
When `cloudflare:site-deploy` is not present (the marketplace edition), publish
|
|
24
|
+
with `wrangler` directly, following the public mechanics in the Cloudflare
|
|
25
|
+
references. Resolve the token through the seam — the resolver prints the **key
|
|
26
|
+
name**, never the value:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
KEY=$(sh bin/resolve-cf.sh token-key) # -> CLOUDFLARE_API_TOKEN, standalone
|
|
30
|
+
eval "export CLOUDFLARE_API_TOKEN=\"\$$KEY\""
|
|
31
|
+
cd "$(sh bin/resolve-cf.sh site-root)/pages/<project>"
|
|
32
|
+
wrangler pages deploy <output-dir> --project-name <project> --branch=main
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Then attach the custom domain to the Pages project and confirm a DNS record points
|
|
36
|
+
the hostname at `<project>.pages.dev` (a subdomain needs a `CNAME`; an apex needs
|
|
37
|
+
a proxied flattened `CNAME` plus a `www` `CNAME`). The token that carries
|
|
38
|
+
**Account · Cloudflare Pages · Edit** and **Account · D1 · Edit** is the one the
|
|
39
|
+
form work below also needs.
|
|
40
|
+
|
|
41
|
+
## Forms and buckets: D1
|
|
42
|
+
|
|
43
|
+
A form on the site POSTs to a Pages Function (`functions/api/<name>.ts`) that
|
|
44
|
+
`INSERT`s into a D1 table bound in `wrangler.toml` as `[[d1_databases]]`. The token
|
|
45
|
+
must carry **both** Pages Edit and D1 Edit — a Pages-only token cannot touch D1,
|
|
46
|
+
and even a `SELECT` needs D1 Edit. One form is one table; additional form "buckets"
|
|
47
|
+
(a waitlist, a callback request, a survey) are additional tables and additional
|
|
48
|
+
`functions/api/<name>.ts` handlers on the same pattern. The full mechanics — table
|
|
49
|
+
schema, the `swept` read-cursor, the token discipline, and the D1 round-trip
|
|
50
|
+
proof — are in the host's `cloudflare/references/d1-data-capture.md`; follow it
|
|
51
|
+
rather than restating it.
|
|
52
|
+
|
|
53
|
+
## Done-gate
|
|
54
|
+
|
|
55
|
+
"Done" is a live behavioural signal on the production custom domain, surfaced in
|
|
56
|
+
chat: a cache-busted `HTTP/2 200`, a content marker for this site (not a stale one
|
|
57
|
+
from a previous brand), brand-correct `og:*` tags, and — for a form-bearing site —
|
|
58
|
+
a test POST that appears in D1 unswept. Inside maxy-code this gate is
|
|
59
|
+
`site-deploy`'s; standalone it is the same curl-plus-D1-SELECT check.
|