@easysociable/cli 0.1.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 (69) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +95 -0
  3. package/bin/easysociable.js +15 -0
  4. package/dist/easysociable.js +35464 -0
  5. package/dist/easysociable.js.map +1 -0
  6. package/package.json +58 -0
  7. package/skills/ONBOARDING.md +61 -0
  8. package/skills/easysociable-content/SKILL.md +115 -0
  9. package/skills/easysociable-content/platforms/claude.md +5 -0
  10. package/skills/easysociable-content/platforms/codex.md +5 -0
  11. package/skills/easysociable-content/platforms/hermes.md +5 -0
  12. package/skills/easysociable-content/platforms/openclaw.md +5 -0
  13. package/skills/easysociable-content/references/CLI.md +27 -0
  14. package/skills/easysociable-content/references/MCP.md +20 -0
  15. package/skills/easysociable-content/references/PAGE_PACK.md +6 -0
  16. package/skills/easysociable-content/references/TROUBLESHOOTING.md +11 -0
  17. package/skills/easysociable-content/references/VAULT.md +20 -0
  18. package/skills/easysociable-content/scripts/setup.mjs +156 -0
  19. package/skills/easysociable-content/scripts/setup.test.mjs +71 -0
  20. package/skills/easysociable-content/scripts/starter.mjs +53 -0
  21. package/skills/easysociable-content/workflows/ANALYSIS_AND_COVERAGE.md +8 -0
  22. package/skills/easysociable-content/workflows/CONTENT_EXPORT.md +19 -0
  23. package/skills/easysociable-content/workflows/DEVICE_TASKS.md +5 -0
  24. package/skills/easysociable-content/workflows/EXPERIMENTS.md +6 -0
  25. package/skills/easysociable-content/workflows/MCP_FIRST.md +10 -0
  26. package/skills/easysociable-content/workflows/PUBLICATION.md +5 -0
  27. package/skills/easysociable-content/workflows/TOPIC_DRAFT.md +5 -0
  28. package/skills/easysociable-content/workflows/WORKING_DRAFT.md +10 -0
  29. package/skills/easysociable-slideshow/SKILL.md +77 -0
  30. package/skills/easysociable-slideshow/references/CONTENT_PACK.md +25 -0
  31. package/skills/easysociable-slideshow/references/GATEWAY.md +16 -0
  32. package/skills/easysociable-slideshow/references/PLATFORM_CAPS.md +17 -0
  33. package/skills/easysociable-slideshow/references/VAULT.md +18 -0
  34. package/skills/easysociable-slideshow/workflows/CREATE_RUN.md +7 -0
  35. package/skills/easysociable-slideshow/workflows/MCP_FIRST.md +14 -0
  36. package/skills/easysociable-slideshow/workflows/OUT_OF_SCOPE.md +8 -0
  37. package/skills/easysociable-slideshow/workflows/PUBLISH_RUN.md +4 -0
  38. package/skills/easysociable-slideshow/workflows/RENDER_RUN.md +6 -0
  39. package/skills/easysociable-template/SKILL.md +106 -0
  40. package/skills/easysociable-template/references/AUTHORING_SOURCE.md +129 -0
  41. package/skills/easysociable-template/references/CATALOG_METADATA.md +221 -0
  42. package/skills/easysociable-template/references/DESIGN_INTELLIGENCE.md +69 -0
  43. package/skills/easysociable-template/references/DESIGN_V2_1.md +278 -0
  44. package/skills/easysociable-template/references/EXTERNAL_RESOURCES.md +52 -0
  45. package/skills/easysociable-template/references/GATEWAY.md +25 -0
  46. package/skills/easysociable-template/references/PLATFORM_SPECS.md +134 -0
  47. package/skills/easysociable-template/references/SLOTS.md +104 -0
  48. package/skills/easysociable-template/references/THEME_PRESETS.md +48 -0
  49. package/skills/easysociable-template/references/THEME_TOKENS.md +89 -0
  50. package/skills/easysociable-template/references/fixtures/surface-gradient.design-plan.json +74 -0
  51. package/skills/easysociable-template/references/theme-presets.json +95 -0
  52. package/skills/easysociable-template/scripts/generate-theme-from-hex.mjs +402 -0
  53. package/skills/easysociable-template/scripts/validate-authoring-source.mjs +228 -0
  54. package/skills/easysociable-template/scripts/validate-design-v2.mjs +318 -0
  55. package/skills/easysociable-template/workflows/ASSET_PLAN.md +34 -0
  56. package/skills/easysociable-template/workflows/AUTHORING_FROM_BRIEF.md +52 -0
  57. package/skills/easysociable-template/workflows/AUTHORING_FROM_IMAGE.md +52 -0
  58. package/skills/easysociable-template/workflows/AUTHORING_STUDIO.md +122 -0
  59. package/skills/easysociable-template/workflows/DESIGN_BRIEF.md +92 -0
  60. package/skills/easysociable-template/workflows/DESIGN_CONCEPTS.md +41 -0
  61. package/skills/easysociable-template/workflows/DESIGN_PHILOSOPHY.md +69 -0
  62. package/skills/easysociable-template/workflows/FILL_AND_RENDER.md +86 -0
  63. package/skills/easysociable-template/workflows/MCP_FIRST.md +9 -0
  64. package/skills/easysociable-template/workflows/OUT_OF_SCOPE.md +15 -0
  65. package/skills/easysociable-template/workflows/PALETTE_REVIEW.md +71 -0
  66. package/skills/easysociable-template/workflows/PUBLISH_FROM_DRAFT.md +60 -0
  67. package/skills/easysociable-template/workflows/SURFACE_REVIEW.md +178 -0
  68. package/skills/easysociable-template/workflows/THEME_FROM_BRAND.md +73 -0
  69. package/skills/easysociable-template/workflows/VISUAL_QA.md +55 -0
@@ -0,0 +1,318 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'node:fs'
4
+ import path from 'node:path'
5
+
6
+ const filePath = process.argv[2]
7
+
8
+ if (!filePath) {
9
+ console.error('Usage: node scripts/validate-design-v2.mjs <design-plan.json|theme-tokens.json|visual-qa.json|->')
10
+ process.exit(2)
11
+ }
12
+
13
+ let value
14
+ try {
15
+ const input = filePath === '-'
16
+ ? fs.readFileSync(0, 'utf8')
17
+ : fs.readFileSync(path.resolve(filePath), 'utf8')
18
+ value = JSON.parse(input)
19
+ }
20
+ catch (error) {
21
+ console.error(`Invalid JSON: ${error.message}`)
22
+ process.exit(1)
23
+ }
24
+
25
+ const errors = []
26
+ const nonEmpty = (candidate) => typeof candidate === 'string' && candidate.trim().length > 0
27
+ const HEX_RE = /^#[0-9A-Fa-f]{6}$/
28
+ const SHADES = ['50', '100', '200', '300', '400', '500', '600', '700', '800', '900', '950']
29
+ const THEME_COLOR_KEYS = ['canvas', 'text', 'accent', 'muted']
30
+
31
+ function validatePaletteCandidates(candidates, index) {
32
+ if (!Array.isArray(candidates))
33
+ return
34
+ candidates.forEach((candidate, candidateIndex) => {
35
+ if (!HEX_RE.test(candidate?.brandHex ?? ''))
36
+ errors.push(`concepts[${index}].paletteCandidates[${candidateIndex}].brandHex must be #RRGGBB`)
37
+ if (!['light', 'dark'].includes(candidate?.mode))
38
+ errors.push(`concepts[${index}].paletteCandidates[${candidateIndex}].mode must be light or dark`)
39
+ if (candidate?.name !== undefined && !nonEmpty(candidate.name))
40
+ errors.push(`concepts[${index}].paletteCandidates[${candidateIndex}].name must be non-empty when present`)
41
+ if (candidate?.bestFor !== undefined && !nonEmpty(candidate.bestFor))
42
+ errors.push(`concepts[${index}].paletteCandidates[${candidateIndex}].bestFor must be non-empty when present`)
43
+ })
44
+ }
45
+
46
+ function validateThemePreview(preview) {
47
+ if (!preview)
48
+ return
49
+ if (!HEX_RE.test(preview.brandHex ?? ''))
50
+ errors.push('themePreview.brandHex must be #RRGGBB')
51
+ if (!['light', 'dark'].includes(preview.activeMode))
52
+ errors.push('themePreview.activeMode must be light or dark')
53
+ if (typeof preview.contrastPass !== 'boolean')
54
+ errors.push('themePreview.contrastPass must be boolean')
55
+ if (!nonEmpty(preview.themeTokensPath))
56
+ errors.push('themePreview.themeTokensPath is required')
57
+ if (preview.name !== undefined && !nonEmpty(preview.name))
58
+ errors.push('themePreview.name must be non-empty when present')
59
+ if (preview.userConfirmed !== undefined && typeof preview.userConfirmed !== 'boolean')
60
+ errors.push('themePreview.userConfirmed must be boolean when present')
61
+ }
62
+
63
+ function validateSurfacePreview(preview) {
64
+ if (!preview)
65
+ return
66
+ const kinds = new Set(['flat', 'gradient', 'image'])
67
+ if (!kinds.has(preview.kind))
68
+ errors.push('surfacePreview.kind must be flat, gradient, or image')
69
+ if (typeof preview.userConfirmed !== 'boolean')
70
+ errors.push('surfacePreview.userConfirmed must be boolean')
71
+ if (preview.kind === 'gradient' || preview.kind === 'image') {
72
+ if (preview.userConfirmed !== true)
73
+ errors.push(`surfacePreview.userConfirmed must be true when kind is ${preview.kind}`)
74
+ }
75
+ if (preview.kind === 'gradient' && preview.userConfirmed === true && !nonEmpty(preview.recipeId))
76
+ errors.push('surfacePreview.recipeId is required when kind is gradient and confirmed')
77
+ if (preview.kind === 'image' && preview.userConfirmed === true) {
78
+ if (!nonEmpty(preview.imageId) && preview.importPending !== true)
79
+ errors.push('surfacePreview.imageId is required when kind is image and confirmed (or set importPending true)')
80
+ if (preview.imageMode !== undefined && !['fixed', 'slot_default'].includes(preview.imageMode))
81
+ errors.push('surfacePreview.imageMode must be fixed or slot_default when present')
82
+ }
83
+ if (preview.textSafeZone !== undefined
84
+ && !['light_end', 'dark_end', 'needs_scrim'].includes(preview.textSafeZone)) {
85
+ errors.push('surfacePreview.textSafeZone must be light_end, dark_end, or needs_scrim when present')
86
+ }
87
+ if (preview.contrastAnchor !== undefined && !HEX_RE.test(preview.contrastAnchor))
88
+ errors.push('surfacePreview.contrastAnchor must be #RRGGBB when present')
89
+ }
90
+
91
+ const PHILOSOPHY_REQUIRED_FIELDS = [
92
+ 'movementName',
93
+ 'space',
94
+ 'color',
95
+ 'rhythm',
96
+ 'composition',
97
+ 'typographyAccent',
98
+ 'craftBar',
99
+ ]
100
+
101
+ function validatePhilosophy(philosophy, required) {
102
+ if (!philosophy || typeof philosophy !== 'object') {
103
+ if (required)
104
+ errors.push('philosophy is required')
105
+ return
106
+ }
107
+ PHILOSOPHY_REQUIRED_FIELDS.forEach((field) => {
108
+ if (!nonEmpty(philosophy[field]))
109
+ errors.push(`philosophy.${field} is required`)
110
+ })
111
+ }
112
+
113
+ function validateThemeTokensBlock(tokens, label) {
114
+ const colors = tokens?.colors ?? {}
115
+ THEME_COLOR_KEYS.forEach((key) => {
116
+ if (!HEX_RE.test(colors[key] ?? ''))
117
+ errors.push(`${label}.colors.${key} must be #RRGGBB`)
118
+ })
119
+ if (!nonEmpty(tokens?.fonts?.heading))
120
+ errors.push(`${label}.fonts.heading is required`)
121
+ if (!nonEmpty(tokens?.fonts?.body))
122
+ errors.push(`${label}.fonts.body is required`)
123
+ if (!nonEmpty(tokens?.surfaces?.radius))
124
+ errors.push(`${label}.surfaces.radius is required`)
125
+ if (!nonEmpty(tokens?.surfaces?.shadow))
126
+ errors.push(`${label}.surfaces.shadow is required`)
127
+ }
128
+
129
+ function validateDesignPlan(plan) {
130
+ const allowedSourceKinds = new Set(['text_brief', 'image_reference'])
131
+ const allowedRoles = new Set(['background', 'hero', 'decorative', 'avatar', 'logo', 'icon'])
132
+ const allowedSources = new Set([
133
+ 'user_upload',
134
+ 'brand_kit',
135
+ 'css_svg',
136
+ 'gradient_catalog',
137
+ 'asset_library',
138
+ 'better_icons',
139
+ 'template_icon_catalog',
140
+ 'external_photo',
141
+ 'generated',
142
+ 'none',
143
+ ])
144
+ const conceptFields = [
145
+ 'id',
146
+ 'name',
147
+ 'rationale',
148
+ 'visualMotif',
149
+ 'layout',
150
+ 'hierarchy',
151
+ 'typography',
152
+ 'colorStrategy',
153
+ 'assetStrategy',
154
+ ]
155
+
156
+ if (!allowedSourceKinds.has(plan.sourceKind))
157
+ errors.push('sourceKind must be text_brief or image_reference')
158
+ if (!nonEmpty(plan.intent?.jobToBeDone))
159
+ errors.push('intent.jobToBeDone is required')
160
+ if (!nonEmpty(plan.intent?.primaryPlatform))
161
+ errors.push('intent.primaryPlatform is required')
162
+ if (!(Number(plan.intent?.canvas?.width) > 0) || !(Number(plan.intent?.canvas?.height) > 0))
163
+ errors.push('intent.canvas width and height must be positive numbers')
164
+
165
+ const concepts = Array.isArray(plan.concepts) ? plan.concepts : []
166
+ const minimum = plan.sourceKind === 'image_reference' ? 1 : 2
167
+ if (concepts.length < minimum || concepts.length > 3)
168
+ errors.push(`${plan.sourceKind || 'text_brief'} requires ${minimum}–3 concepts`)
169
+
170
+ // Both source kinds need a philosophy so Visual QA has a craft bar.
171
+ validatePhilosophy(plan.philosophy, true)
172
+
173
+ const ids = new Set()
174
+ concepts.forEach((concept, index) => {
175
+ conceptFields.forEach((field) => {
176
+ if (!nonEmpty(concept?.[field]))
177
+ errors.push(`concepts[${index}].${field} is required`)
178
+ })
179
+ if (!nonEmpty(concept?.philosophyExpression))
180
+ errors.push(`concepts[${index}].philosophyExpression is required`)
181
+ if (nonEmpty(concept?.id)) {
182
+ if (ids.has(concept.id))
183
+ errors.push(`Duplicate concept id: ${concept.id}`)
184
+ ids.add(concept.id)
185
+ }
186
+ if (!Array.isArray(concept?.risks))
187
+ errors.push(`concepts[${index}].risks must be an array`)
188
+ validatePaletteCandidates(concept?.paletteCandidates, index)
189
+ })
190
+
191
+ if (!nonEmpty(plan.selectedConceptId) || !ids.has(plan.selectedConceptId))
192
+ errors.push('selectedConceptId must reference an existing concept')
193
+ if (!nonEmpty(plan.selectionReason))
194
+ errors.push('selectionReason is required')
195
+ validateThemePreview(plan.themePreview)
196
+ validateSurfacePreview(plan.surfacePreview)
197
+ if (!Array.isArray(plan.assetPlan))
198
+ errors.push('assetPlan must be an array (it may be empty)')
199
+ else {
200
+ plan.assetPlan.forEach((asset, index) => {
201
+ if (!allowedRoles.has(asset?.role))
202
+ errors.push(`assetPlan[${index}].role is invalid`)
203
+ if (!allowedSources.has(asset?.source))
204
+ errors.push(`assetPlan[${index}].source is invalid`)
205
+ if (typeof asset?.required !== 'boolean')
206
+ errors.push(`assetPlan[${index}].required must be boolean`)
207
+ if (!nonEmpty(asset?.description))
208
+ errors.push(`assetPlan[${index}].description is required`)
209
+ })
210
+ }
211
+ }
212
+
213
+ function validateThemeTokens(theme) {
214
+ if (!HEX_RE.test(theme.brandHex ?? ''))
215
+ errors.push('brandHex must be #RRGGBB')
216
+ if (!['light', 'dark'].includes(theme.activeMode))
217
+ errors.push('activeMode must be light or dark')
218
+ if (theme.contrastPass !== true)
219
+ errors.push('contrastPass must be true — regenerate theme or adjust hex/mode before authoring')
220
+
221
+ SHADES.forEach((shade) => {
222
+ if (!HEX_RE.test(theme.scale?.[shade] ?? ''))
223
+ errors.push(`scale.${shade} must be #RRGGBB`)
224
+ })
225
+ ;['light', 'dark'].forEach((mode) => {
226
+ const palette = theme.semantic?.[mode] ?? {}
227
+ ;['canvas', 'text', 'accent', 'muted', 'primary', 'border'].forEach((key) => {
228
+ if (!HEX_RE.test(palette[key] ?? ''))
229
+ errors.push(`semantic.${mode}.${key} must be #RRGGBB`)
230
+ })
231
+ })
232
+
233
+ validateThemeTokensBlock(theme.themeTokens, 'themeTokens')
234
+ validateThemeTokensBlock(theme.themeTokensLight, 'themeTokensLight')
235
+ validateThemeTokensBlock(theme.themeTokensDark, 'themeTokensDark')
236
+
237
+ if (!Array.isArray(theme.contrast) || theme.contrast.length === 0)
238
+ errors.push('contrast must be a non-empty array')
239
+ else {
240
+ theme.contrast.forEach((entry, index) => {
241
+ if (!nonEmpty(entry?.pair))
242
+ errors.push(`contrast[${index}].pair is required`)
243
+ if (!['light', 'dark'].includes(entry?.mode))
244
+ errors.push(`contrast[${index}].mode must be light or dark`)
245
+ if (!HEX_RE.test(entry?.foreground ?? '') || !HEX_RE.test(entry?.background ?? ''))
246
+ errors.push(`contrast[${index}] foreground and background must be #RRGGBB`)
247
+ if (typeof entry?.ratio !== 'number' || entry.ratio <= 0)
248
+ errors.push(`contrast[${index}].ratio must be a positive number`)
249
+ if (typeof entry?.wcagAA !== 'boolean' || typeof entry?.wcagAAA !== 'boolean')
250
+ errors.push(`contrast[${index}] wcagAA and wcagAAA must be boolean`)
251
+ })
252
+ }
253
+ }
254
+
255
+ function validateVisualQa(qa) {
256
+ const maxima = {
257
+ hierarchy: 20,
258
+ composition: 15,
259
+ spacingAlignment: 15,
260
+ typography: 15,
261
+ colorContrast: 10,
262
+ assetRelevance: 10,
263
+ contentResilience: 10,
264
+ editability: 5,
265
+ }
266
+
267
+ if (!nonEmpty(qa.draftId))
268
+ errors.push('draftId is required')
269
+ if (!Number.isInteger(qa.iteration) || qa.iteration < 0 || qa.iteration > 2)
270
+ errors.push('iteration must be an integer from 0 to 2')
271
+ if (!nonEmpty(qa.screenshotEvidence))
272
+ errors.push('screenshotEvidence is required')
273
+ if (!Array.isArray(qa.hardFailures))
274
+ errors.push('hardFailures must be an array')
275
+ if (!Array.isArray(qa.issues))
276
+ errors.push('issues must be an array')
277
+ if (!Array.isArray(qa.changesMade))
278
+ errors.push('changesMade must be an array')
279
+ if (!['pass', 'revise'].includes(qa.verdict))
280
+ errors.push('verdict must be pass or revise')
281
+
282
+ let calculatedTotal = 0
283
+ for (const [field, maximum] of Object.entries(maxima)) {
284
+ const score = qa.scores?.[field]
285
+ if (typeof score !== 'number' || score < 0 || score > maximum)
286
+ errors.push(`scores.${field} must be between 0 and ${maximum}`)
287
+ else
288
+ calculatedTotal += score
289
+ }
290
+
291
+ if (qa.total !== calculatedTotal)
292
+ errors.push(`total must equal calculated score ${calculatedTotal}`)
293
+ if (qa.verdict === 'pass') {
294
+ if (qa.hardFailures?.length > 0)
295
+ errors.push('pass verdict cannot contain hardFailures')
296
+ if (calculatedTotal < 80)
297
+ errors.push('pass verdict requires a score of at least 80')
298
+ }
299
+ if (qa.verdict === 'revise' && calculatedTotal >= 80 && qa.hardFailures?.length === 0)
300
+ errors.push('revise verdict conflicts with a passing score and no hardFailures')
301
+ }
302
+
303
+ if (value?.schemaVersion === 'design_plan_v2_1')
304
+ validateDesignPlan(value)
305
+ else if (value?.schemaVersion === 'theme_tokens_v2')
306
+ validateThemeTokens(value)
307
+ else if (value?.schemaVersion === 'visual_qa_v2_1')
308
+ validateVisualQa(value)
309
+ else
310
+ errors.push('schemaVersion must be design_plan_v2_1, theme_tokens_v2, or visual_qa_v2_1')
311
+
312
+ if (errors.length > 0) {
313
+ console.error(`V2.1 validation failed (${errors.length}):`)
314
+ errors.forEach((error) => console.error(`- ${error}`))
315
+ process.exit(1)
316
+ }
317
+
318
+ console.log(`V2.1 validation passed: ${value.schemaVersion}`)
@@ -0,0 +1,34 @@
1
+ # Asset plan (V2.1)
2
+
3
+ Run after concept selection and before authoring. Give every non-text visual a clear semantic role and source.
4
+
5
+ When atmosphere, photography, or icons are involved, run `workflows/SURFACE_REVIEW.md` (and read `references/EXTERNAL_RESOURCES.md`) in the same pass.
6
+
7
+ ## Source priority
8
+
9
+ Choose the first adequate option:
10
+
11
+ 1. `user_upload` — user avatar, logo, product screenshot, supplied photo
12
+ 2. `brand_kit` — approved reusable brand asset
13
+ 3. `template_icon_catalog` — **required for any icon/glyph** (arrow, chevron, check, social mark, next-page cue, …). Confirmed Gateway `search_icons` → user confirm → `get_icon` (`iconAssetId` + `canonicalSvg`). **Not optional when catalog works.**
14
+ 4. `gradient_catalog` — confirmed recipe from Gateway `gradients.*` (inline CSS decoration)
15
+ 5. `css_svg` — local CSS/SVG **geometry only** (hairlines, rails, anonymous discs/masks). **Do not** use this source for symbol icons when `template_icon_catalog` is available.
16
+ 6. `asset_library` — vetted texture, frame, or decorative primitive (non-icon)
17
+ 7. `external_photo` — confirmed host Unsplash/Pexels/Pinterest pick (Gateway import → CDN/`imageId`)
18
+ 8. `generated` — topic-specific hero, illustration, object, or complex background
19
+ 9. `none` — omit visuals that do not improve communication
20
+
21
+ ## Rules
22
+
23
+ - Record `role`, `source`, `required`, `description`, and treatment for every asset.
24
+ - Roles: `background | hero | decorative | avatar | logo | icon`.
25
+ - For `external_photo` / `template_icon_catalog` / `gradient_catalog`: discovery → bounded showcase → **user confirm** → materialize. Unselected → no EasySociable write.
26
+ - **`role: icon` ⇒ `source` MUST be `template_icon_catalog`** (unless catalog failed and user approved degrade). Never invent path SVG for icons “to go faster”.
27
+ - Never put third-party temporary URLs into `authoringSource`.
28
+ - Use generated imagery only when it supports the selected concept. Prefer transparent, text-free, crop-safe elements for independent decorations.
29
+ - Never ask image generation to render the final poster, headline, metric, avatar name, or other replaceable content.
30
+ - Keep generated prompts and returned asset IDs/paths as provenance when available.
31
+ - Define focal point/crop behavior for image slots. Ensure foreground text retains contrast over backgrounds (`surfacePreview.contrastAnchor` / scrim when needed).
32
+ - When no asset service/library exists, degrade per `EXTERNAL_RESOURCES.md`; do not invent asset IDs or scrape providers at runtime.
33
+
34
+ Write the result into `design-plan.json` (`assetPlan` + `surfacePreview` when applicable) and validate before creating `authoringSource`.
@@ -0,0 +1,52 @@
1
+ # Author from text brief
2
+
3
+ Primary intake when the user describes a single-page template in words (no reference image, or image is secondary).
4
+
5
+ ## Goal
6
+
7
+ One publishable **single-page** template draft: Design Brief → Design Philosophy → 2–3 Concepts → Asset Plan → Theme lock → slot table + absolute layout → screenshot QA → Authoring Studio.
8
+
9
+ ## Steps
10
+
11
+ 1. Confirm this is **one page**, not a slideshow. If they want a multi-slide deck, stop and hand off (`OUT_OF_SCOPE.md`).
12
+ 2. Read `references/PLATFORM_SPECS.md` and lock platforms + **Recommended** canvas (preset + pixels).
13
+ 3. **Mandatory — V2.1 design plan first** (no reference image):
14
+ - Follow `workflows/DESIGN_BRIEF.md`
15
+ - Follow `workflows/DESIGN_PHILOSOPHY.md`; lock shared `philosophy` (+ companion `design-philosophy.md`)
16
+ - Follow `workflows/DESIGN_CONCEPTS.md`; create 2–3 spatial expressions of that philosophy and select one (`philosophyExpression` on each)
17
+ - Follow `workflows/ASSET_PLAN.md`; give every planned visual asset a role and source
18
+ - Follow `workflows/SURFACE_REVIEW.md` when atmosphere, photography, or icons are needed (`flat` | `gradient` | `image`)
19
+ - Follow `workflows/PALETTE_REVIEW.md` and `workflows/THEME_FROM_BRAND.md` when co-creating Theme (no `themeId`)
20
+ - Write and validate `design-plan.json` and `theme-tokens.json` using `scripts/validate-design-v2.mjs`
21
+ - Do not build `authoringSource` before philosophy, concept selection, theme lock, surface confirmation (when applicable), and design-plan validation
22
+ 4. After design-plan acceptance, build:
23
+ - `selection-metadata.json` with all P0 fields (+ P1 when known) — `references/CATALOG_METADATA.md`
24
+ - Slot table per `references/SLOTS.md` (no `list`; checklists = multiple `text_n` + `fixedItemCount`)
25
+ - Flat absolutely positioned nodes (canvas siblings only; no absolute-inside-absolute wrappers); bind fillable text/number with **`data-variable`** (Studio/Gateway compile); images use `data-image-mode` + binding per image docs
26
+ - Inline `style={{...}}` for exact geometry and critical typography; no Tailwind arbitrary-value layout classes
27
+ - Surface: gradient → unbound CSS decoration sibling; image → CDN/`imageId` background (`fixed` default)
28
+ - **Icons:** only Gateway `search_icons` → confirm → `get_icon` → `canonicalSvg` + `iconAssetId` (`SURFACE_REVIEW.md`). Never hand-draw icon paths when catalog works.
29
+ - Other decorations as CSS geometry (rails, hairlines, discs) that obey `philosophy.rhythm` — not new illustrations or fake icons by default
30
+ - `authoringSource` per `references/AUTHORING_SOURCE.md` (flat tree + text-nesting exception)
31
+ - Theme per `references/THEME_TOKENS.md` — use `themeTokens` from validated `theme-tokens.json` for `theme.create`
32
+ 5. Run `scripts/validate-authoring-source.mjs <authoring-source.json>` and fix every error.
33
+ 6. Continue with `workflows/AUTHORING_STUDIO.md` (`source.kind = text_brief`); its Visual QA loop is required before handoff.
34
+
35
+ ## Checklist pages
36
+
37
+ - Prefer a **fixed** number of required text slots matching the design brief.
38
+ - Do not promise automatic packing when some lines are empty (absolute layout).
39
+ - Optional text lines are allowed but omitting them leaves gaps.
40
+
41
+ ## Do not
42
+
43
+ - Create multi-page packs
44
+ - Invent `list` / bullets array bindings
45
+ - Skip Design Brief, Philosophy, Concepts, or Asset Plan when there is no reference image
46
+ - Produce concepts that differ only by color or ignore the shared philosophy
47
+ - Generate a whole flattened poster instead of editable text/image/layout nodes
48
+ - Pick a non-recommended canvas without telling the user why
49
+ - Use `left-[...]`, `top-[...]`, `w-[...]`, `text-[...]`, or other arbitrary-value classes for critical runtime layout
50
+ - Nest absolutely positioned nodes inside cards/groups (`profile-card` wrapping avatar + name, etc.)
51
+ - Auto-publish without user Save + explicit “publish” request (`PUBLISH_FROM_DRAFT.md`)
52
+ - Hand-draw SVG icon paths (arrows, chevrons, checks, next cues, …) when Icon Catalog search works — use `search_icons` → confirm → `get_icon`
@@ -0,0 +1,52 @@
1
+ # Author from reference image
2
+
3
+ Intake when the user provides a poster / card image to turn into a **single-page** template.
4
+
5
+ ## Goal
6
+
7
+ Vision/LLM understanding stays **local**. Output is a structured candidate (`authoringSource` + slot table + Theme), not a claim of pixel-perfect clone. User always finishes in Authoring Studio.
8
+
9
+ ## Steps
10
+
11
+ 1. Confirm single-page intent (not “rebuild this whole carousel as one Agent pack”).
12
+ 2. Read `references/PLATFORM_SPECS.md` when mapping detected aspect → platform defaults.
13
+ 3. Inspect the image and extract:
14
+ - Canvas aspect / preset + suggested pixel size; likely `platforms`
15
+ - Likely **pageRoles** (1–2), contentFamily / archetypes, density, visualStyle
16
+ - Layout regions (title, metric, image well, footer, …)
17
+ - **Variable vs fixed**: only variable regions become slots
18
+ - Theme candidates (canvas/text/accent/muted, font feel)
19
+ 4. Reverse-engineer a short shared philosophy (`workflows/DESIGN_PHILOSOPHY.md`) from the reference — movement name + space/color/rhythm/composition/type/craftBar. Write it into `design-plan.json` `philosophy`.
20
+ 5. Treat the reconstruction direction as the selected concept (one concept is enough), with `philosophyExpression` describing how the rebuilt zones express that philosophy. Then follow `workflows/ASSET_PLAN.md` and `workflows/SURFACE_REVIEW.md` when the rebuild needs wash, slot photography, or icons:
21
+ - Reuse the supplied bitmap only when it is meant to remain a background/hero asset
22
+ - Rebuild editable typography, data, and simple decoration with nodes/CSS/SVG
23
+ - Never flatten replaceable text or data into a generated image
24
+ - Never keep third-party temporary image URLs in Authoring Source
25
+ - Write and validate `design-plan.json`; `image_reference` may contain one concept
26
+ - When co-creating Theme, run `THEME_FROM_BRAND.md` (sample colors from reference → `brandHex`) and validate `theme-tokens.json`
27
+ 6. Build **P0 selection metadata** (`references/CATALOG_METADATA.md`) — infer, then confirm with the user.
28
+ 7. Build an explicit **slot table** (`references/SLOTS.md`):
29
+ - Types: `text` | `number` | `image` only
30
+ - Keys may be `text_0`, `image_0`, …
31
+ - Every `text` slot includes `maxLines` (+ `maxCharacters` when known)
32
+ - No `list` type — repeated lines → `text_1`…`text_n` + `fixedItemCount` when checklist/steps
33
+ 8. Map regions to **flat absolutely positioned** canvas siblings (no absolute-inside-absolute groups; card chrome = decoration sibling). Set `bindingKey` on fillable nodes only. Put exact geometry and critical typography in inline `style={{...}}`; do not use Tailwind arbitrary-value layout classes. Fill `slotSummary` / `requiresImage` / `imageRoles`.
34
+ 9. Propose Theme reuse or `ThemeTokens` create from `theme-tokens.json` (`references/THEME_TOKENS.md`).
35
+ 10. Show the user: selection metadata, slots, Theme, and honesty about approximation limits. Proceed when they accept.
36
+ 11. Run `scripts/validate-authoring-source.mjs <authoring-source.json>` and fix every error.
37
+ 12. Continue with `workflows/AUTHORING_STUDIO.md` (`source.kind = image_reference`, brief = path or short note); run Visual QA before handoff.
38
+
39
+ ## Limits (state upfront when relevant)
40
+
41
+ - Complex photography collages, heavy effects, or handwritten type → structure + Theme approximation; Studio required.
42
+ - Absolute layout: do not promise empty lines will pack upward.
43
+ - Never Publish from Agent until the user has Saved in Studio and explicitly asked to publish (`PUBLISH_FROM_DRAFT.md`).
44
+
45
+ ## Do not
46
+
47
+ - Treat the reference bitmap as the template (must become slotted authoring source)
48
+ - Skip reverse-engineered philosophy / `philosophyExpression`
49
+ - Copy private Binding/AST formats into Gateway requests
50
+ - Use runtime Tailwind arbitrary-value classes for critical geometry or typography
51
+ - Nest absolute frames inside wrapper cards/groups (flatten to canvas siblings instead)
52
+ - Start Content Skill / render a slideshow from this path
@@ -0,0 +1,122 @@
1
+ # Authoring Studio (create → edit → Save → Agent publish)
2
+
3
+ Primary interactive path for **single-page** template drafts. Create the draft, run screenshot-based Visual QA, then hand off the reviewed draft. Do **not** create a Hosted Preview Job merely to show the user a URL.
4
+
5
+ Studio has **Save only** (no Publish button). Publishing is an Agent step after the user saves and asks to publish — see `PUBLISH_FROM_DRAFT.md`.
6
+
7
+ ## Preconditions
8
+
9
+ Complete intake first:
10
+
11
+ - `AUTHORING_FROM_BRIEF.md` or `AUTHORING_FROM_IMAGE.md`
12
+ - Selected concept + Asset Plan in a valid `design-plan.json`
13
+ - Slot table + `authoringSource` + Theme decision ready
14
+ - `authoringSource` passes `scripts/validate-authoring-source.mjs`
15
+ - Optional draft-stage `selectionMetadata` (P0 can be completed at publish time)
16
+ - References: `SLOTS.md`, `AUTHORING_SOURCE.md`, `THEME_TOKENS.md`, `CATALOG_METADATA.md`
17
+
18
+ ## Flow
19
+
20
+ 1. Run the required local preflights (resolve scripts relative to this Skill directory):
21
+
22
+ ```bash
23
+ node scripts/validate-design-v2.mjs ./design-plan.json
24
+ node scripts/validate-authoring-source.mjs ./authoring-source.json
25
+ ```
26
+
27
+ Do not create or update a draft when this command fails.
28
+
29
+ 2. Create the remote draft:
30
+
31
+ ```bash
32
+ easysociable templates create-draft --file ./draft.json --json
33
+ ```
34
+
35
+ The JSON file contains the public `name`, `source`, `authoringSource`, optional
36
+ `selectionMetadata`, and optional `theme`/`themeId` fields. Do not pass private
37
+ AST or Binding Maps.
38
+
39
+ 3. Read `data.id` / `data.templateId` / `data.themeId` / `data.authoringStudioUrl`.
40
+
41
+ 4. **Before handoff, run `workflows/VISUAL_QA.md`.** Open the actual Studio render with available browser control and inspect the screenshot, not only source code. When the draft fails, fix `authoring-source.json`, validate it, update the same draft, and re-render:
42
+
43
+ ```bash
44
+ node scripts/validate-authoring-source.mjs ./authoring-source.json
45
+ easysociable templates update --id <id> --file ./draft-patch.json --json
46
+ ```
47
+
48
+ Stop after two automatic revisions unless the user explicitly requests more. Validate the passing QA artifact:
49
+
50
+ ```bash
51
+ node scripts/validate-design-v2.mjs ./visual-qa.json
52
+ ```
53
+
54
+ 5. **Required user handoff message** (paste only after QA — customize the URL):
55
+
56
+ ```text
57
+ 已创建草稿,请打开 Authoring Studio 预览/编辑:
58
+ {authoringStudioUrl}
59
+
60
+ 可直接在页面上改布局、文案占位和 Theme。满意后点 Save(页面上没有 Publish)。
61
+ 保存完成后回复我「publish」或「发布该模板」,我会分析画布、补齐选型元数据并发布到 Catalog。
62
+ ```
63
+
64
+ English equivalent when the session is EN:
65
+
66
+ ```text
67
+ Draft created. Open Authoring Studio to preview/edit:
68
+ {authoringStudioUrl}
69
+
70
+ Edit layout, placeholders, and Theme directly on the page. When satisfied, click Save (there is no Publish button).
71
+ Then reply “publish” and I will analyze the draft, complete catalog metadata, and publish to the Catalog.
72
+ ```
73
+
74
+ 6. User previews/edits in the **slim** Authoring Studio:
75
+ - Canvas + right property panel
76
+ - Left: Theme tokens + optional Catalog hints
77
+ - Top bar: **Save** and **Fork** only. Final PNG rendering is delegated to the unified CLI/MCP after the user confirms the saved draft.
78
+ - **No** Publish button; **No** left SidebarMenu (no AI / select-template / typeset rail)
79
+
80
+ 7. When the user says publish → run `workflows/PUBLISH_FROM_DRAFT.md`.
81
+
82
+ ## SDK shape
83
+
84
+ ```ts
85
+ const draft = await client.createDraft({
86
+ name: 'Dark CTA Poster',
87
+ source: { kind: 'text_brief', brief: '1080x1350 KPI milestone card' },
88
+ authoringSource: {
89
+ canvas: { width: 1080, height: 1350 },
90
+ json: { /* Studio projection; fillable nodes carry bindingKey e.g. text_0 */ },
91
+ },
92
+ theme: {
93
+ create: {
94
+ name: 'Dark CTA',
95
+ tokens: {
96
+ colors: { canvas: '#0f1419', text: '#e8eef5', accent: '#5b8cff', muted: '#8b9bb0' },
97
+ fonts: { heading: 'Inter', body: 'Inter' },
98
+ surfaces: { radius: '12px', shadow: '0 8px 24px rgba(0,0,0,.35)' },
99
+ },
100
+ },
101
+ },
102
+ })
103
+ // Always send the handoff message with draft.authoringStudioUrl
104
+ ```
105
+
106
+ ## Rules
107
+
108
+ - Agent vision/LLM understanding stays local; Gateway stores/compiles `authoringSource` and Theme.
109
+ - Single-page only — never create Carousel packs via this skill.
110
+ - Phase 1: new templates consume Theme via `--es-*` variables.
111
+ - Exact geometry and critical typography use inline styles; runtime arbitrary-value Tailwind classes are forbidden.
112
+ - CLI/MCP must not start Local Runtime or import `template-renderer-react`.
113
+ - There is no separate `easysociable-template preview` command in V1; Authoring
114
+ Studio is the visual QA surface.
115
+ - Never claim Visual QA passed without inspectable screenshot evidence. If no visual surface is available, disclose that QA was skipped and ask the user to review in Studio.
116
+ - Do not hand off a draft with hard visual failures or a score below 80/100 unless the user explicitly accepts it.
117
+ - The only V1 authoring surface is `/authoring/{draftId}`; the retired full
118
+ product editor and Carousel routes are not public entry points.
119
+ - Do not instruct the user to Publish in Studio — instruct **Save**, then Agent publish.
120
+ - Remind user: empty optional slots omit nodes at render; absolute layout does not reflow.
121
+ - Flat canvas tree: no absolute-inside-absolute wrappers (validator enforces); visual cards = decoration sibling + content siblings.
122
+ - **Icons:** when adding/changing arrows, chevrons, checks, or any glyph, follow `SURFACE_REVIEW.md` § Icons (`search_icons` → confirm → `get_icon`). Do not hand-draw icon paths while the Icon Catalog works. Fillable text uses **`data-variable`**, not inventing bindings only in private AST.
@@ -0,0 +1,92 @@
1
+ # Design brief (required when there is no reference image)
2
+
3
+ When the user gives **only a text description** (no template/reference image), do **not** jump straight to nodes/slots.
4
+
5
+ Act as a **professional editorial / social-card strategist**. First produce an intent-and-constraint **Design Brief**. Do not treat this brief as the final visual solution: continue to `DESIGN_PHILOSOPHY.md`, then `DESIGN_CONCEPTS.md`, before materializing nodes.
6
+
7
+ Skip this workflow when `AUTHORING_FROM_IMAGE.md` applies and a reference image is the primary input (optional short brief still OK).
8
+
9
+ ## When to run
10
+
11
+ - `source.kind = text_brief`
12
+ - User has no uploaded layout reference
13
+ - Or user says “按描述做一页模板 / 帮我设计”
14
+
15
+ ## Process
16
+
17
+ ### 1. Gather intent (batch questions; skip known answers)
18
+
19
+ - Platforms (multi-select) + canvas from `references/PLATFORM_SPECS.md`
20
+ - Page roles (1–2) + contentFamily / archetypes / density (`CATALOG_METADATA.md`)
21
+ - Content job: what must the viewer understand in one glance?
22
+ - Must-have slots: title / metric / image / CTA / fixed N checklist lines…
23
+ - Theme: reuse `themeId` or co-create direction (swiss / editorial / bold_promo / minimal / dark). If co-creating, note brand hex or archetype for `DESIGN_INTELLIGENCE.md` palette seeding.
24
+
25
+ ### 2. Write the Design Brief
26
+
27
+ Use this structure (Chinese or English to match the user):
28
+
29
+ ```markdown
30
+ # Design Brief — <template name>
31
+
32
+ ## 1. Job to be done
33
+ - One-sentence purpose of this page
34
+ - Primary platform(s) + canvas (preset + pixels) — cite PLATFORM_SPECS recommended row
35
+
36
+ ## 2. Visual constraints
37
+ - Desired/forbidden moods or brand rules
38
+ - Existing Theme/brand assets to reuse
39
+ - Accessibility or locale constraints
40
+
41
+ ## 3. Information hierarchy
42
+ - One primary focal message
43
+ - Secondary evidence and author/brand attribution
44
+ - Content priority when space is constrained
45
+ - **Flat frames only:** describe regions as independent absolute boxes on the canvas. If a “card” appears, it is a background panel **plus** separate content boxes — not one parent wrapping children.
46
+
47
+ ## 4. Slot plan
48
+ - Table: key | type | required | maxLines/maxCharacters | purpose
49
+ - fixedItemCount if checklist/steps
50
+ - Optional regions that omit-when-empty (independent blocks only)
51
+
52
+ ## 5. Catalog metadata draft
53
+ - platforms, canvasPreset, supportedRoles, contentFamily, archetypes, density, requiresImage…
54
+
55
+ ## 6. Risks / open questions
56
+ - e.g. need user photo? checklist line count? LinkedIn PDF vs single image?
57
+ ```
58
+
59
+ Design rules to enforce in the brief:
60
+
61
+ - One primary focal idea per page
62
+ - Strong type hierarchy; avoid SaaS-card clutter
63
+ - Theme owns color/font; page owns structure
64
+ - Match **Recommended** canvas for the primary platform
65
+ - Absolute layout: do not promise packing empty checklist lines
66
+ - No nested absolute groups (avatar/name inside a card wrapper); text-only inline nesting is fine
67
+
68
+ ### 3. Confirm intent when needed
69
+
70
+ Ask once:
71
+
72
+ > 先确认内容目标和约束;OK 后我会先定视觉哲学,再给 2–3 个版式方向,最后生成可编辑模板。
73
+
74
+ Incorporate edits. If the user already said “直接做” or supplied enough constraints, continue without another blocking question.
75
+
76
+ ### 4. Continue to art direction
77
+
78
+ Do not create nodes yet. Continue in this order:
79
+
80
+ 1. `DESIGN_PHILOSOPHY.md` — name the movement and lock shared visual philosophy
81
+ 2. `DESIGN_CONCEPTS.md` — 2–3 spatial expressions of that philosophy; select one
82
+ 3. `ASSET_PLAN.md` — decide reuse / CSS-SVG / generated assets
83
+ 4. `PALETTE_REVIEW.md` + `THEME_FROM_BRAND.md` — lock `theme-tokens.json` when co-creating Theme
84
+ 5. Validate `design-plan.json` and `theme-tokens.json`
85
+ 6. `AUTHORING_FROM_BRIEF.md`
86
+
87
+ ## Anti-patterns
88
+
89
+ - Generating HTML/nodes before philosophy and concept selection when there is no reference image
90
+ - Vague briefs (“好看一点”) without zones, hierarchy, or slot plan
91
+ - Ignoring PLATFORM_SPECS recommended sizes
92
+ - Treating one generic “minimal/professional” paragraph as a design concept or philosophy