@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,41 @@
1
+ # Design concepts (text brief V2.1)
2
+
3
+ Run after `DESIGN_PHILOSOPHY.md` (and `DESIGN_BRIEF.md` for text intake). Act as an art director expressing one shared philosophy, not a slogan generator.
4
+
5
+ ## Output
6
+
7
+ Create **2–3 materially different concepts**. They are **spatial variations of the same `philosophy`**, like canvas-design multi-page twists — not three unrelated moods.
8
+
9
+ Each concept must specify:
10
+
11
+ - `id`, name, one-sentence rationale, and a recognisable visual motif
12
+ - `philosophyExpression` — how this layout expresses the shared philosophy in space (required)
13
+ - Canvas zones with approximate `x/y/width/height`
14
+ - Focal hierarchy: primary, secondary, attribution
15
+ - Typography system: display/body contrast and approximate scale ratio (traceable to `philosophy.typographyAccent`)
16
+ - Color strategy: canvas/ink/muted/one dominant accent (mood only; hex via Theme pipeline)
17
+ - `paletteCandidates` (optional but recommended): named options such as `[{ "name": "Ocean Depths", "presetId": "ocean-depths", "brandHex": "#1A6B6B", "mode": "light", "bestFor": "…" }]` — prefer `references/THEME_PRESETS.md` when user has no brand color
18
+ - Asset strategy: none, CSS/SVG, reuse, or generate
19
+ - Whitespace/density strategy and concrete risks
20
+
21
+ Changing only colors, radius, or shadows does not create a different concept. Change at least two of: composition, focal placement, type treatment, visual motif, asset role.
22
+
23
+ `layout` / `hierarchy` / `typography` must be clearly derivable from `philosophy.space` / `composition` / `rhythm` / `typographyAccent`.
24
+
25
+ ## Selection
26
+
27
+ - Show concise concept summaries when the user asked to choose or collaborate.
28
+ - Otherwise recommend and select the strongest concept; state why it best serves the one-glance job **and** the shared philosophy.
29
+ - Keep rejected concepts in `design-plan.json` so the decision remains auditable.
30
+ - Use one concept only for `source.kind = image_reference`, where the reference already determines composition (still require `philosophyExpression`).
31
+
32
+ ## Guardrails
33
+
34
+ - Use one dominant focal idea.
35
+ - Prefer editorial hierarchy over a grid of generic SaaS cards.
36
+ - Make decorative choices reinforce the philosophy’s rhythm; do not add illustration by default.
37
+ - Keep replaceable text, numbers, avatars, and logos editable.
38
+ - Design for realistic stress content: long name, large number, missing optional asset.
39
+ - Do not invent a second philosophy per concept.
40
+
41
+ Continue to `ASSET_PLAN.md`, then `PALETTE_REVIEW.md` and `THEME_FROM_BRAND.md` when co-creating a theme. Validate `design-plan.json` and `theme-tokens.json` using `references/DESIGN_V2_1.md`.
@@ -0,0 +1,69 @@
1
+ # Design philosophy (after brief, before concepts)
2
+
3
+ Borrow canvas-design’s order: name a visual movement, write the aesthetic worldview, then express it as layout concepts. This is **not** a free-art poster workflow — the philosophy must serve a one-glance social template with editable slots.
4
+
5
+ ## When to run
6
+
7
+ - After `DESIGN_BRIEF.md` for `source.kind = text_brief`
8
+ - After image inspection for `AUTHORING_FROM_IMAGE.md` (short reverse-engineered philosophy is enough)
9
+ - Before `DESIGN_CONCEPTS.md`
10
+
11
+ Skip only when reusing an already validated `design-plan.json` that already contains `philosophy`.
12
+
13
+ ## Goal
14
+
15
+ Produce one shared philosophy that later concepts will express differently in space. Do **not** invent three unrelated slogans.
16
+
17
+ ## Steps
18
+
19
+ ### 1. Name the movement (1–2 words)
20
+
21
+ Examples adapted for social templates: `Editorial Ledger`, `Terminal Pulse`, `Quiet Proof`, `Monument Metric`, `Warm Folio`.
22
+
23
+ Seed from `references/DESIGN_INTELLIGENCE.md` when stuck.
24
+
25
+ ### 2. Write the philosophy (4–6 short paragraphs or structured fields)
26
+
27
+ Cover each once (no redundancy):
28
+
29
+ | Field | Ask |
30
+ |-------|-----|
31
+ | `space` | Margins, breathing room, density, where emptiness works |
32
+ | `color` | Mood and limit strategy (not final hex — that comes from Theme pipeline) |
33
+ | `rhythm` | Repeating system: rules, dots, grids, cadence of decoration |
34
+ | `composition` | Zones, focal placement, what stays quiet |
35
+ | `typographyAccent` | Display vs body ratio; where type is a gesture vs metadata |
36
+ | `craftBar` | One sentence: museum-grade spacing, no overlap, one focal, refine before adding |
37
+
38
+ Optional but recommended:
39
+
40
+ | Field | Ask |
41
+ |-------|-----|
42
+ | `subtleReference` | Quiet conceptual DNA (jazz quote, not a label sticker) |
43
+ | `templateConstraints` | Slots stay bindable; flat absolute siblings; Theme owns color; never flatten copy into a poster |
44
+
45
+ Also write companion `design-philosophy.md` for humans using the structure in `references/DESIGN_V2_1.md`.
46
+
47
+ ### 3. Template hard constraints (must appear in philosophy or `templateConstraints`)
48
+
49
+ Rewrite canvas-design’s “minimal text / pure art” into template-safe rules:
50
+
51
+ 1. **Slots win clarity** — required brief slots become bindable nodes; philosophy may demand sparse chrome, not missing content.
52
+ 2. **Theme owns hex** — philosophy states mood/limit only; concrete colors go through `PALETTE_REVIEW.md` + `THEME_FROM_BRAND.md`.
53
+ 3. **Decoration is rhythm** — prefer CSS/SVG repeats (lines, dots, grids) over new illustrations.
54
+ 4. **Flat absolute layout** — zones are canvas siblings; card chrome is a background sibling, never a nested absolute group.
55
+ 5. **Readable focal** — subtle reference is allowed, but title/metric must be one-glance clear on social canvases.
56
+
57
+ ### 4. Hand off
58
+
59
+ Continue to `DESIGN_CONCEPTS.md`. Every concept must include `philosophyExpression` describing how **this** spatial plan expresses the shared philosophy.
60
+
61
+ Embed `philosophy` into `design-plan.json` before validation.
62
+
63
+ ## Do not
64
+
65
+ - Skip philosophy and jump to SaaS three-card layouts
66
+ - Write a philosophy that forbids required text slots
67
+ - Encode final brand hex only inside philosophy prose (use Theme pipeline)
68
+ - Treat philosophy as a PNG art brief that replaces `authoringSource`
69
+ - Ship canvas-design font files; map voice → Theme fonts via `DESIGN_INTELLIGENCE.md`
@@ -0,0 +1,86 @@
1
+ # Fill slots and Hosted Render (published template)
2
+
3
+ Primary **Agent use** of a published single-page template: read the public slot table, fill values, submit a Hosted Render Job, poll for Artifact / Manifest.
4
+
5
+ This path belongs to **Template Skill** (ADR-0021 / AC-25~26). Do **not** hand off to Content Skill for single-page fill-slots out图.
6
+
7
+ ## Preconditions
8
+
9
+ ```bash
10
+ easysociable auth status --json
11
+ ```
12
+
13
+ Prefer Template MCP tools when available; CLI is the fallback.
14
+
15
+ ## Steps
16
+
17
+ ### 1. Get template + public slots
18
+
19
+ **MCP:** `get_template({ templateId })`
20
+ **CLI:** `easysociable templates get --id <id> --json`
21
+
22
+ Response MUST include `slots[]` with `key`, `type`, `required`, optional `maxCharacters` / `overflow` / `aiHint` / `label`.
23
+ MUST NOT include AST, Binding Map, or RenderPlan.
24
+
25
+ ### 2. Fill slots
26
+
27
+ | Slot type | Agent action |
28
+ |---|---|
29
+ | `text` / `number` | Generate from user brief; respect `maxCharacters` / required |
30
+ | `image` | Ask user: upload or choose an existing account image. After confirmation, use `import_template_image` / the unified image import flow so the bytes are stored through Gateway → R2 and the metadata is recorded in the existing Directus `Image` collection; put the returned **`imageId`** in the slot. Never pass temporary Unsplash/Pexels/Pinterest URLs, local paths or transient asset IDs as authority. |
31
+
32
+ Omit optional empty slots (`null` / `''`) rather than inventing filler.
33
+
34
+ All V1 image uploads use the account image import flow. There is no separate
35
+ transient render-asset command.
36
+
37
+ ### 3. Create Hosted Render Job
38
+
39
+ **MCP:** `create_render_job({ templateId, slots, outputFormats, idempotencyKey? })`
40
+ **CLI:**
41
+
42
+ ```bash
43
+ easysociable renders create \
44
+ --template-id <id> \
45
+ --slots '{"title":"Hello","caption":"World"}' \
46
+ --format png \
47
+ --idempotency-key fill_1 \
48
+ --json
49
+ ```
50
+
51
+ **Forbidden inputs:** `ContentDocument`, `contentDocumentHash`, client `RenderPlan`, AST, Binding Map.
52
+
53
+ ### 4. Poll Job / Manifest
54
+
55
+ **MCP:** `get_render_job({ jobId })` → when completed, `get_render_manifest` if needed
56
+ **CLI:** `easysociable renders wait --id <id> --json`
57
+
58
+ Success: signed Artifact URL(s) on the public Manifest.
59
+ Job may also return `previewUrl` + `capture` (signed Studio-parity page). Deliver the **Hosted Artifact**, not a local screenshot of that preview URL.
60
+
61
+ ### 5. Hosted Capture failure
62
+
63
+ V1 does not expose a second local capture command. If Hosted Capture fails,
64
+ surface the Gateway error and retry only when it is marked retryable; do not
65
+ invent a browser URL, selector or viewport and do not treat an Authoring Studio
66
+ screenshot as a billable final Artifact.
67
+
68
+ #### Error-code table
69
+
70
+ | Code / signal | Local capture? | Agent action |
71
+ |---|---|---|
72
+ | Job `completed` + Manifest Artifact | No | Deliver signed Artifact URL(s) |
73
+ | Retryable worker/queue/5xx error | No | Retry Hosted with the same idempotency key |
74
+ | Rate limit | No | Respect retry-after/backoff and retry once if allowed |
75
+ | `VALIDATION_ERROR` / missing required slots | **No** | Fix slots; no fallback expected |
76
+ | Auth / entitlement / tier denied | **No** | Fix credentials/plan; no fallback |
77
+ Failed jobs without a retryable error are surfaced to the user; no freestyle
78
+ browser screenshot is accepted as final output.
79
+
80
+ ## Anti-patterns
81
+
82
+ - Building or submitting a local RenderPlan / AST / Binding Map
83
+ - Using ContentDocument export as a render prerequisite
84
+ - Treating Studio html-to-image as Hosted delivery or as the default on success
85
+ - Local capture when Hosted succeeded or when `fallback.capture` is absent
86
+ - Calling Content MCP/CLI for this single-page fill-slots flow
@@ -0,0 +1,9 @@
1
+ # MCP-first
2
+
3
+ Use the unified EasySociable MCP for Template authoring, images, icons, Profiles,
4
+ Themes and Render Jobs. Fall back to the equivalent `easysociable` CLI command
5
+ only when MCP is unavailable.
6
+
7
+ Neither path may call Local API, SQLite, Directus, R2, Stripe, private Template
8
+ Service or a third-party provider through EasySociable. Host-installed image
9
+ MCPs are optional discovery tools and are never auto-installed.
@@ -0,0 +1,15 @@
1
+ # Out of scope for template-skill
2
+
3
+ If the user asks for any of the following, **do not** execute them under this skill:
4
+
5
+ | Request | Hand off |
6
+ |---|---|
7
+ | Generate爆款 copy / topic / Working Draft as Content product | Content Skill |
8
+ | Export `ContentDocument` / checkpoint Content Version | Content Skill |
9
+ | Compose or render a **multi-slide** slideshow Run | **easysociable-slideshow** |
10
+ | Pick Theme as slideshow “Look” for a whole Run | **easysociable-slideshow** + Profile Theme |
11
+ | Local Obsidian content authoring | Content Skill |
12
+ | UGC/publication/social posting | Deferred in V1 |
13
+ | Submit `contentDocumentHash` / ContentDocument to Template render | **Forbidden** — use fill-slots (`FILL_AND_RENDER.md`) |
14
+
15
+ **In scope:** single-page template authoring/publish **and** fill-slots Hosted Render of one published PageTemplate (`get_template` → slots → `create_render_job`).
@@ -0,0 +1,71 @@
1
+ # Palette review
2
+
3
+ Choose between 2–3 palette directions and lock theme tokens before layout authoring.
4
+ Adapted from Anthropic `theme-factory`: **show → ask → wait for explicit choice → apply**.
5
+
6
+ ## When to use
7
+
8
+ - After concept selection in `DESIGN_CONCEPTS.md`
9
+ - Before `AUTHORING_FROM_BRIEF.md` or `AUTHORING_FROM_IMAGE.md` build step
10
+ - When co-creating Theme (no reusable `themeId`)
11
+
12
+ ## Steps
13
+
14
+ 1. Build **2–3 named candidates** (not anonymous A/B hexes):
15
+ - From user brand hex → light/dark (or slight hex variants) via generator
16
+ - Or from `references/THEME_PRESETS.md` / `theme-presets.json` when no brand hex
17
+ - Each candidate needs: `name`, `brandHex`, `mode`, `bestFor` (and optional `notFor` / `presetId`)
18
+ 2. Generate a candidate file per option:
19
+
20
+ ```bash
21
+ node scripts/generate-theme-from-hex.mjs --preset ocean-depths -o theme-candidate-ocean.json
22
+ node scripts/generate-theme-from-hex.mjs --preset tech-innovation -o theme-candidate-tech.json
23
+ node scripts/generate-theme-from-hex.mjs #0D9488 --mode light -o theme-candidate-custom.json
24
+ ```
25
+
26
+ 3. **Showcase (required)** — present a swatch table before asking the user to choose. Do not dump raw JSON as the only view:
27
+
28
+ | Name | Preset / custom | Mode | brandHex | Accent | Contrast | bestFor |
29
+ |------|-----------------|------|----------|--------|----------|---------|
30
+ | Ocean Depths | ocean-depths | light | #1A6B6B | … | pass | Finance, trust metrics |
31
+ | Tech Innovation | tech-innovation | dark | #2563EB | … | pass | Startups, launches |
32
+
33
+ Include one-line canvas/accent feel from each candidate’s `themeTokens.colors`.
34
+
35
+ 4. **Ask and wait** — which theme to apply? Do **not** call `theme.create` or finalize `theme-tokens.json` until the user explicitly confirms (or they already said “直接用 X / 你定”).
36
+ 5. On confirmation, copy the winner to `theme-tokens.json` and set Theme create **name** to the candidate `name` (e.g. `Ocean Depths`), never `Theme draft_xxx`.
37
+ 6. Update `design-plan.json`:
38
+
39
+ ```json
40
+ "themePreview": {
41
+ "name": "Ocean Depths",
42
+ "presetId": "ocean-depths",
43
+ "brandHex": "#1A6B6B",
44
+ "activeMode": "light",
45
+ "contrastPass": true,
46
+ "bestFor": "Finance, consulting, trust-building metric cards",
47
+ "themeTokensPath": "theme-tokens.json",
48
+ "userConfirmed": true
49
+ }
50
+ ```
51
+
52
+ 7. Run `node scripts/validate-design-v2.mjs design-plan.json` and `theme-tokens.json`.
53
+ 8. Continue to `THEME_FROM_BRAND.md` only if tokens still need regeneration; otherwise proceed to authoring with the locked file.
54
+
55
+ ## Contrast checklist (before authoring)
56
+
57
+ - [ ] `text` on `canvas` ≥ 4.5:1 (AA body text)
58
+ - [ ] `muted` on `canvas` ≥ 4.5:1
59
+ - [ ] `accent` on `canvas` ≥ 3:1 (accent text / links)
60
+ - [ ] `label` on `primary` ≥ 3:1 (filled buttons — light text on dark primary in dark mode)
61
+ - [ ] Screenshot spot-check: no neon-on-neon or gray-on-gray failures
62
+
63
+ If any pair fails, regenerate or switch mode — do not proceed to `authoringSource`.
64
+
65
+ ## Do not
66
+
67
+ - Apply a theme without showcase + explicit confirmation (unless user waived choice)
68
+ - Offer palettes that differ only by one shade step
69
+ - Skip `theme-tokens.json` when creating a new theme
70
+ - Override failed contrast with arbitrary hex without re-running the generator
71
+ - Name Gateway themes as `Theme <draftId>`
@@ -0,0 +1,60 @@
1
+ # Publish from saved draft (Agent path)
2
+
3
+ Authoring Studio has **Save only** — no Publish button.
4
+ After the user saves and says 「publish」/「发布该模板」, the Agent analyzes the draft, completes catalog metadata, and publishes via Gateway.
5
+
6
+ ## When to run
7
+
8
+ - User finished editing in `/authoring/{draftId}` and clicked **Save**
9
+ - User explicitly asks to publish (do not auto-publish on create)
10
+
11
+ ## Flow
12
+
13
+ 1. Load the saved draft:
14
+
15
+ ```bash
16
+ easysociable templates get --id <draftId> --json
17
+ ```
18
+
19
+ 2. Analyze public fields only (`authoringSource`, canvas, themeId, existing `selectionMetadata`):
20
+ - Infer `platforms` / `canvasPreset` from canvas + brief
21
+ - Infer `supportedRoles` (1–2) from layout intent / node types
22
+ - Derive `slotSummary` / `requiresImage` / `imageRoles` from fillable slots
23
+ - Fill `contentFamily`, `contentArchetypes`, `density`, `textDensityRange`
24
+ - Prefer P1: `bestFor`, `notFor`, `selectionHints`, `visualStyle`, `locale`
25
+
26
+ 3. Show a short P0/P1 summary to the user. Wait for confirmation unless they already said 「直接发布」.
27
+
28
+ 4. Write metadata, then publish:
29
+
30
+ ```bash
31
+ easysociable templates publish --id <draftId> --json
32
+ ```
33
+
34
+ Or two steps:
35
+
36
+ ```bash
37
+ easysociable templates update --id <draftId> --file ./draft-patch.json --json
38
+ easysociable templates publish --id <draftId> --json
39
+ ```
40
+
41
+ 5. Verify catalog discoverability:
42
+
43
+ ```bash
44
+ easysociable templates list --json
45
+ ```
46
+
47
+ ## Rules
48
+
49
+ - Never invent private AST / Binding Map from Agent-side.
50
+ - Gateway rejects Publish if any P0 field is missing — fix metadata and retry.
51
+ - Incomplete published pages must not appear in `list` (enforced server-side).
52
+ - Theme co-create drafts may still be published via theme APIs when product requires it; page publish is independent.
53
+
54
+ ## Failure recovery
55
+
56
+ | Error | Action |
57
+ |---|---|
58
+ | `VALIDATION_ERROR` missing P0 | Fill missing fields, `update` + `publish` again |
59
+ | `NOT_FOUND` | Confirm `draftId`; user may not have saved |
60
+ | `VERSION_IMMUTABLE` | Draft already published — create a new template instead |
@@ -0,0 +1,178 @@
1
+ # Surface review (atmosphere layer)
2
+
3
+ Choose and confirm the canvas **surface** before authoring. Surface is independent of Theme semantic colors.
4
+
5
+ ```text
6
+ Theme = identity (solid semantic colors + fonts)
7
+ Surface = atmosphere (flat | gradient | image)
8
+ ```
9
+
10
+ Aligned with ADR-0020 (photos) and ADR-0023 (icons): photo discovery may use host MCPs; icon discovery uses Gateway Icon Catalog; materialization only through Template Gateway.
11
+
12
+ ## When to run
13
+
14
+ - After concept selection (`DESIGN_CONCEPTS.md`) and Theme lock (`PALETTE_REVIEW` / `THEME_FROM_BRAND`) when co-creating
15
+ - During `ASSET_PLAN.md` when the brief needs wash, photography backdrop, or icons
16
+ - Skip only when surface is clearly `flat` (typography-only / Theme.canvas) and no icons/photos are required
17
+
18
+ ## Surface kinds
19
+
20
+ | `kind` | Discovery | After user confirms | Authoring result |
21
+ |---|---|---|---|
22
+ | `flat` | None | — | Theme.canvas only |
23
+ | `gradient` | Template MCP/CLI `gradients list\|search\|get` | Inline CSS decoration sibling (no `bindingKey`) | Atmosphere wash / band |
24
+ | `image` | Host **Pinterest / Unsplash / Pexels** MCP (not Template MCP) | Gateway draft image import → stable CDN `imageId` | Background usually `fixed` |
25
+ | (+ icons) | Template MCP/CLI `search_icons` / `get_icon` | Materialize `TemplateIconAsset` → inline `canonicalSvg` / `iconAssetId` (Theme via `currentColor`) | Icon chrome |
26
+
27
+ Photography for **slots** (avatar, hero well) also uses host image MCPs + Gateway import; ask `fixed` vs `slot_default` (surface background defaults to **`fixed`**).
28
+
29
+ ## Steps
30
+
31
+ ### 1. Decide kind
32
+
33
+ From brief + philosophy `color` / `rhythm` + selected concept:
34
+
35
+ - Sparse copy + atmospheric mood → prefer `gradient` or soft `image`
36
+ - Dense metrics / checklist → prefer `flat` or a thin gradient band (not full-bleed noisy photo)
37
+ - User asks for photo backdrop / Unsplash vibe → `image`
38
+ - User asks for gradient / wash → `gradient`
39
+ - Default when unsure → `flat`, then ask
40
+
41
+ Record intent in `design-plan.json` → `surfacePreview.kind` (may be unconfirmed).
42
+
43
+ ### 2. Discover (bounded candidates only)
44
+
45
+ **Gradient**
46
+
47
+ ```bash
48
+ easysociable templates gradients search --q "<mood>" --limit 8 --json
49
+ # or MCP: search_gradients / list_gradients
50
+ easysociable templates gradients get --id <id> --json # only after shortlist interest
51
+ ```
52
+
53
+ Show a **text table** (MVP: no thumbnails): name, stopSummary, tags, textSafeZone, bestFor.
54
+
55
+ **Image (surface or slot)**
56
+
57
+ 1. Call host Unsplash / Pexels / Pinterest MCP with a short query.
58
+ 2. Show ≤8 candidates (thumb + attribution if the host MCP returns them).
59
+ 3. If the MCP is missing → degrade: say which provider is missing; offer `flat`, `gradient`, user upload, or skip. Do **not** invent URLs or proxy search through Template MCP.
60
+
61
+ **Icons (hard rule)**
62
+
63
+ ```bash
64
+ easysociable icons search --query "<concept>" --limit 8 --json
65
+ # or MCP: search_icons
66
+ easysociable icons get --icon-id <iconId> --idempotency-key <key> --json # only after user confirms
67
+ # or MCP: get_icon
68
+ ```
69
+
70
+ **MUST path (every icon, including “just add an arrow”):**
71
+
72
+ 1. Call Template `search_icons` / CLI `icons search` (Gateway Icon Catalog). Show ≤8 candidates with `iconId`, collection, style, license.
73
+ 2. Wait for user choice — or explicit waive of choice: “你定 / 用第 N 个 / 随便好看一点的向右箭头”.
74
+ Unselected search hits produce **zero** `TemplateIconAsset` writes.
75
+ 3. After confirm, call `get_icon` / CLI `icons get` with a **stable** `idempotencyKey`.
76
+ Embed returned **`canonicalSvg`** only; record **`iconAssetId`**, `iconId`, collection, license, contentHash on the decoration node (e.g. `data-icon-asset-id`, `data-icon-id`).
77
+ 4. Color with Theme `currentColor` / `--es-*` on the wrapper; never bake Theme hex into the SVG paths.
78
+ 5. If Gateway Icon Catalog / auth **fails** → say so → degrade to **omit** or temporary CSS/SVG shape **only after user agrees**.
79
+ Do **not** fall back to Better Icons MCP, client Iconify, or silent hand-drawn paths.
80
+
81
+ **Forbidden by default (not a shortcut):**
82
+
83
+ - Hand-writing SVG `<path d="…">` for arrows, chevrons, checks, social marks, “next page” cues, or any symbol the catalog can supply.
84
+ - Using emoji as a substitute for catalog icons.
85
+ - Skipping search because “it’s only a small arrow” or “faster to draw”.
86
+
87
+ **Allowed without catalog (not icons):** pure geometry with no symbol meaning — hairlines, solid rails, anonymous disc washes. If it **reads as a UI/icon glyph**, it is an icon → catalog path.
88
+
89
+ ### 3. Confirm gate (required)
90
+
91
+ Do **not** mutate Draft / Authoring Source until the user:
92
+
93
+ - picks one candidate, or
94
+ - explicitly waives (“flat 就行 / 你定 / 不要背景”)
95
+
96
+ Unselected candidates produce **zero** EasySociable writes (no R2, no Directus Image, no decoration node, no gradient get baked into draft).
97
+
98
+ ### 4. Materialize (Gateway / Authoring only)
99
+
100
+ | Kind | Materialize |
101
+ |---|---|
102
+ | `flat` | Nothing extra |
103
+ | `gradient` | `gradients get` → write absolute background decoration with `style.backgroundImage = <css>`; record `recipeId`, provider, license in `surfacePreview` / node comment |
104
+ | `image` | Import via Template Gateway draft images (when available) or document the import step for TMP-024; Authoring references **CDN / imageId only** — never third-party temp URLs. Mode: surface background → `fixed`; slot → ask `fixed` \| `slot_default` |
105
+ | icon | Inline sanitized SVG as decoration sibling; record `iconAssetId`, provider, collection, license, contentHash |
106
+
107
+ Prefer band or panel geometry over full-bleed when copy is dense. Keep Theme.canvas as a safe solid under translucent washes when needed (`textSafeZone` / `needs_scrim`).
108
+
109
+ ### 5. Record in `design-plan.json`
110
+
111
+ ```json
112
+ "surfacePreview": {
113
+ "kind": "gradient",
114
+ "userConfirmed": true,
115
+ "recipeId": "grad_001_warm-flame",
116
+ "name": "Warm Flame",
117
+ "provider": "webgradients",
118
+ "textSafeZone": "light_end",
119
+ "contrastAnchor": "#FAD0C4",
120
+ "placement": "full_bleed",
121
+ "notes": "Dark type on light end"
122
+ }
123
+ ```
124
+
125
+ For image:
126
+
127
+ ```json
128
+ "surfacePreview": {
129
+ "kind": "image",
130
+ "userConfirmed": true,
131
+ "imageMode": "fixed",
132
+ "imageId": "img_…",
133
+ "provider": "unsplash",
134
+ "query": "soft paper texture",
135
+ "rightsState": "licensed_for_publish",
136
+ "placement": "full_bleed"
137
+ }
138
+ ```
139
+
140
+ For flat: `{ "kind": "flat", "userConfirmed": true }`.
141
+
142
+ Also mirror assets in `assetPlan` with sources `gradient_catalog` | `external_photo` | `template_icon_catalog` when used.
143
+
144
+ ### 6. Validate
145
+
146
+ ```bash
147
+ node scripts/validate-design-v2.mjs design-plan.json
148
+ ```
149
+
150
+ If `surfacePreview.kind` is `gradient` or `image`, `userConfirmed` must be `true` and required ids must be present before authoring.
151
+
152
+ ## Fixture paths (Skill smoke)
153
+
154
+ | Path | Expectation |
155
+ |---|---|
156
+ | flat | No host MCP; no gradients get into draft |
157
+ | gradient | Template `gradients.*` only; confirmation → CSS decoration |
158
+ | image | Host image MCP → confirm → CDN/`imageId`; no temp URL in source |
159
+ | icon | `search_icons` → confirm → `get_icon` → inline canonical SVG / `iconAssetId` |
160
+ | none / waive | Explicit waive; remain flat |
161
+
162
+ ## Do not
163
+
164
+ - Proxy Unsplash / Pexels / Pinterest **search** through Template MCP/CLI
165
+ - Require Better Icons MCP, client Iconify credentials, or runtime Provider fetches for icons
166
+ - **Hand-draw icon paths when Icon Catalog search works** (arrows, chevrons, checks, …)
167
+ - Put third-party temporary URLs or local paths into Authoring Source / Publish
168
+ - Call `theme.create` with gradient CSS (Theme stays solid semantics)
169
+ - Stack a second rainbow signature when a full-bleed gradient is already the signature
170
+ - Treat unselected search hits as EasySociable assets
171
+ - Require `easysociable-media` Skill/CLI/MCP
172
+
173
+ ## Agent self-check before Save handoff (icons)
174
+
175
+ - [ ] Every symbol-like decoration has `iconAssetId` / came from `get_icon`
176
+ - [ ] No hand-authored icon `<path>` when catalog was available
177
+ - [ ] User confirmed pick (or explicit “你定 / 第 N 个”) before `get_icon`
178
+ - [ ] Icon color uses `currentColor` / `--es-*` (dark/light safe)
@@ -0,0 +1,73 @@
1
+ # Theme from brand color
2
+
3
+ Generate deterministic, WCAG-checked theme tokens from a brand hex or curated preset before `theme.create`.
4
+
5
+ ## When to use
6
+
7
+ - User provides a brand color (hex) or brand kit with primary color
8
+ - User confirmed a preset from `PALETTE_REVIEW.md` / `references/THEME_PRESETS.md`
9
+ - Selected concept has a concrete color direction (not only free-text `colorStrategy`)
10
+ - No existing `themeId` to reuse
11
+
12
+ Skip when the user already selected a published theme or only wants to tweak an existing theme.
13
+
14
+ ## Inputs
15
+
16
+ | Input | Required | Notes |
17
+ |-------|----------|-------|
18
+ | `brandHex` **or** `--preset <id>` | Yes | Preset supplies hex + fonts + suggested mode |
19
+ | `activeMode` | Yes | `light` or `dark` — match concept / preset bias |
20
+ | `heading` / `body` font | No | Default from preset or `Inter` |
21
+ | Theme `name` | Yes for create | Human name (preset `name` or custom), not `Theme draft_xxx` |
22
+ | User confirmation | Yes | From `PALETTE_REVIEW` unless user waived |
23
+
24
+ ## Steps
25
+
26
+ 1. Resolve inputs:
27
+ - Brand hex from brief / brand kit / user message, **or**
28
+ - `--preset` id from `references/theme-presets.json` after user confirmation
29
+ 2. Pick `activeMode` from selected concept / preset:
30
+ - Light canvas / editorial paper → `light`
31
+ - Dark canvas / terminal / neon → `dark`
32
+ 3. Run:
33
+
34
+ ```bash
35
+ node scripts/generate-theme-from-hex.mjs <#hex> \
36
+ --mode <light|dark> \
37
+ --heading "<Font>" \
38
+ --body "<Font>" \
39
+ --output theme-tokens.json
40
+
41
+ # or
42
+ node scripts/generate-theme-from-hex.mjs --preset ocean-depths \
43
+ --output theme-tokens.json
44
+
45
+ node scripts/generate-theme-from-hex.mjs --list-presets
46
+ ```
47
+
48
+ 4. Validate:
49
+
50
+ ```bash
51
+ node scripts/validate-design-v2.mjs theme-tokens.json
52
+ ```
53
+
54
+ 5. If `contrastPass` is `false`, adjust before authoring:
55
+ - Try the other mode (`light` ↔ `dark`)
56
+ - Shift brand hex slightly (darker/lighter) and regenerate
57
+ - Try another preset
58
+ - Document the trade-off in `design-plan.json` `selectionReason`
59
+ 6. **Confirmation gate:** do not call Gateway `theme.create` until `themePreview.userConfirmed` is true (or the user explicitly waived palette choice).
60
+ 7. Use `themeTokens` from `theme-tokens.json` for Gateway `theme.create` — includes embedded `palette` for `theme_schema_v2`. Pass a readable `name` (preset name or custom label).
61
+ 8. Keep `theme-tokens.json` alongside `design-plan.json` for Visual QA contrast reference.
62
+
63
+ ## Output artifact
64
+
65
+ `theme-tokens.json` — schema `theme_tokens_v2`. See `references/DESIGN_V2_1.md`. May include `preset: { id, name, bestFor, notFor }` when generated from `--preset`.
66
+
67
+ ## Do not
68
+
69
+ - Hand-pick four colors without running the generator when a brand hex or preset exists
70
+ - Put scale shades (`500`, `700`) directly on page nodes as hard-coded hex — prefer `--es-color-*` / `--es-scale-*`
71
+ - Skip contrast validation when `colorContrast` is scored in Visual QA
72
+ - Create `authoringSource` or `theme.create` before `theme-tokens.json` validates and the user confirmed (when no `themeId`)
73
+ - Create themes named only after draft ids
@@ -0,0 +1,55 @@
1
+ # Screenshot-based Visual QA (V2.1)
2
+
3
+ Run after draft creation and before the user handoff. Source inspection alone is not visual QA.
4
+
5
+ ## Render evidence
6
+
7
+ 1. Prefer opening `authoringStudioUrl` with available browser control and capture the rendered canvas.
8
+ 2. If browser control is unavailable, record QA as pending; the unified CLI has
9
+ no separate `easysociable-template preview` command. A final render is not a
10
+ substitute for inspecting Authoring Studio.
11
+ 3. Record an inspectable screenshot path or URL in `visual-qa.json`.
12
+ 4. Never mark QA as passed when the page did not render or the screenshot could not be inspected.
13
+
14
+ ## Review order
15
+
16
+ 0. If `theme-tokens.json` exists, compare screenshot contrast to its `contrast` report for the active mode. Failed pairs are hard failures or cap `colorContrast` at 5. When `surfacePreview.kind` is `gradient` or `image`, also check text against `contrastAnchor` / scrim — do not assume Theme.canvas alone matches the wash.
17
+
18
+ 1. **Craft Pass (philosophy discipline)** — before numeric scoring:
19
+ - Read `design-plan.json` → `philosophy.craftBar` and the selected concept’s `philosophyExpression`
20
+ - If the screenshot contradicts the philosophy (e.g. generic SaaS three-card grid vs “monumental single metric”), treat that as a composition/hierarchy issue and plan a revise toward the stated expression
21
+ - For `iteration >= 1`, default revisions **tighten existing nodes** (size, position, whitespace, type scale, contrast). Do **not** add large new decoration layers or new illustrations to chase points
22
+ - If a full-bleed gradient or photo surface is already the signature, do not add a second competing atmosphere layer
23
+ - Prefer high-impact changes: focal size/position, whitespace, alignment, type scale, contrast, then decoration
24
+
25
+ 2. Check hard failures:
26
+
27
+ - render/runtime error
28
+ - clipped or overflowing text
29
+ - overlapping content
30
+ - off-canvas elements or unsafe margins
31
+ - unreadable contrast
32
+ - missing required slot content/image
33
+ - background or decoration obscuring the focal message
34
+ - third-party temp URLs or broken CDN for image surface/slots
35
+ 3. Score the fixed rubric in `references/DESIGN_V2_1.md`:
36
+
37
+ - hierarchy 20
38
+ - composition 15
39
+ - spacing/alignment 15
40
+ - typography 15
41
+ - color/contrast 10
42
+ - asset relevance 10
43
+ - content resilience 10
44
+ - editability 5
45
+
46
+ ## Revision loop
47
+
48
+ - Pass only with no hard failures and total score `>= 80`.
49
+ - For a failure, list concrete issues by region/node and revise `authoringSource`.
50
+ - In `changesMade`, describe refine-first edits; avoid “added new decorative panel/illustration” unless the user explicitly asked or an assetPlan required asset was missing.
51
+ - Validate the updated source, update the same `draftId`, and capture a new screenshot.
52
+ - Perform at most **two automatic revisions** (`iteration` 0, 1, 2). Ask before doing more.
53
+ - Do not hide a layout problem by rasterising the whole design.
54
+
55
+ Validate the final `visual-qa.json` with `scripts/validate-design-v2.mjs`. If visual tools are unavailable, disclose the missing QA instead of fabricating a passing artifact.