@se-studio/skills 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/package.json +25 -0
- package/skills/contentful-cms-alt-text-audit/SKILL.md +60 -0
- package/skills/contentful-cms-cms-guidelines/README.md +166 -0
- package/skills/contentful-cms-cms-guidelines/colour-hint-prompt.md +77 -0
- package/skills/contentful-cms-cms-guidelines/evaluation-prompt.md +84 -0
- package/skills/contentful-cms-cms-guidelines/generate-component-guidelines.md +126 -0
- package/skills/contentful-cms-cms-guidelines/generation-prompt.md +231 -0
- package/skills/contentful-cms-cms-guidelines/html-component-authoring.md +401 -0
- package/skills/contentful-cms-cms-guidelines/validation-prompt.md +170 -0
- package/skills/contentful-cms-cms-guidelines/variant-loop.md +189 -0
- package/skills/contentful-cms-cms-guidelines/variant-proposal-prompt.md +131 -0
- package/skills/contentful-cms-core/SKILL.md +793 -0
- package/skills/contentful-cms-generate-all-guidelines/SKILL.md +313 -0
- package/skills/contentful-cms-generate-cms-guidelines/SKILL.md +313 -0
- package/skills/contentful-cms-image-guide/SKILL.md +240 -0
- package/skills/contentful-cms-navigation/SKILL.md +23 -0
- package/skills/contentful-cms-rich-text/SKILL.md +96 -0
- package/skills/contentful-cms-schema-org/SKILL.md +74 -0
- package/skills/contentful-cms-screenshots/SKILL.md +46 -0
- package/skills/contentful-cms-seo-descriptions/SKILL.md +54 -0
- package/skills/contentful-cms-templates/SKILL.md +21 -0
- package/skills/contentful-cms-update-cms-guidelines/SKILL.md +348 -0
- package/skills/performance-audit/SKILL.md +344 -0
- package/skills/se-marketing-sites-cms-routes-and-appshared/SKILL.md +99 -0
- package/skills/se-marketing-sites-create-collection/SKILL.md +295 -0
- package/skills/se-marketing-sites-create-component/SKILL.md +250 -0
- package/skills/se-marketing-sites-create-page/SKILL.md +183 -0
- package/skills/se-marketing-sites-curate-showcase-mocks/SKILL.md +344 -0
- package/skills/se-marketing-sites-handling-media/SKILL.md +195 -0
- package/skills/se-marketing-sites-lib-cms-structure/SKILL.md +83 -0
- package/skills/se-marketing-sites-register-cms-features/SKILL.md +95 -0
- package/skills/se-marketing-sites-styling-system/SKILL.md +122 -0
- package/skills/site-workflows-brand-context-builder/SKILL.md +98 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-page-route
|
|
3
|
+
description: Guide for creating new Next.js App Router pages and dynamic routes in the SE Core Product framework. Use when creating CMS-driven pages, adding route segments, or setting up routing.
|
|
4
|
+
license: Private
|
|
5
|
+
metadata:
|
|
6
|
+
author: se-core-product
|
|
7
|
+
version: "2.0.0"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Creating Pages and Routes
|
|
11
|
+
|
|
12
|
+
This guide explains how to create new pages and handle routing in the SE Core Product Next.js framework. The system uses Next.js App Router with Contentful-driven dynamic routing.
|
|
13
|
+
|
|
14
|
+
Reference apps: **example-se2026**, **example-brightline**, **example-om1**.
|
|
15
|
+
|
|
16
|
+
## Page Architecture
|
|
17
|
+
|
|
18
|
+
### 1. (cms-routes) Route Group
|
|
19
|
+
|
|
20
|
+
All CMS-driven pages live under `src/app/(cms-routes)/`. The layout enables dynamic params:
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
// layout.tsx
|
|
24
|
+
export const dynamicParams = true;
|
|
25
|
+
|
|
26
|
+
export default function CmsRoutesLayout({ children }: { children: React.ReactNode }) {
|
|
27
|
+
return children;
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 2. Route Structure
|
|
32
|
+
|
|
33
|
+
| Path | File | Purpose |
|
|
34
|
+
|------|------|---------|
|
|
35
|
+
| `/` | `page.ts` | Home (slug: `index`) |
|
|
36
|
+
| `/{slug}/` | `[level1]/page.ts` | Single-level page |
|
|
37
|
+
| `/{slug1}/{slug2}/...` | `[level1]/[...slugs]/page.ts` | Multi-level pages |
|
|
38
|
+
| `/articles/` | `articles/page.ts` | Articles index (uses ARTICLES_BASE from constants) |
|
|
39
|
+
| `/articles/{type}/` | `articles/[articleType]/page.ts` | Article type index |
|
|
40
|
+
| `/articles/{type}/{tag}/` | `articles/[articleType]/[tag]/page.ts` | Article type + tag index |
|
|
41
|
+
| `/articles/{type}/{tag}/{article}/` | `articles/[articleType]/[tag]/[...slugs]/page.ts` | Individual article |
|
|
42
|
+
| `/tags/`, `/tags/{tag}/` | `tags/` | Tag index (if enabled) |
|
|
43
|
+
| `/people/`, `/people/{person}/` | `people/` | People (if enabled) |
|
|
44
|
+
|
|
45
|
+
Base paths (e.g. `/articles` vs `/learning-hub`) come from `@/lib/constants`.
|
|
46
|
+
|
|
47
|
+
### 3. appShared Pattern
|
|
48
|
+
|
|
49
|
+
Route files are thin wrappers. Data-fetching and rendering logic lives in `src/appShared/`:
|
|
50
|
+
|
|
51
|
+
- `pageShared.tsx` – `generatePage`, `generatePageMetadata`
|
|
52
|
+
- `articleShared.tsx` – articles
|
|
53
|
+
- `articleTypeShared.tsx`, `articleTypesIndexShared.tsx`, `articleTypeTagShared.tsx` – article indices
|
|
54
|
+
- `customTypeShared.tsx` – custom types
|
|
55
|
+
- `personShared.tsx`, `tagShared.tsx`, `tagsIndexShared.tsx`, `teamIndexShared.tsx`
|
|
56
|
+
|
|
57
|
+
## Route Page Template
|
|
58
|
+
|
|
59
|
+
Each route page extracts params and delegates to the appropriate appShared function:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import type { ResolvingMetadata } from 'next';
|
|
63
|
+
import { generatePage, generatePageMetadata } from '@/appShared/pageShared';
|
|
64
|
+
|
|
65
|
+
export function generateStaticParams() {
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function extractDetails(props: PageProps<'/[level1]'>) {
|
|
70
|
+
const params = await props.params;
|
|
71
|
+
const { level1 } = params;
|
|
72
|
+
return { slug: level1, path: `/${level1}/` };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function generateMetadata(props: PageProps<'/[level1]'>, parent: ResolvingMetadata) {
|
|
76
|
+
const { slug, path } = await extractDetails(props);
|
|
77
|
+
return generatePageMetadata(slug, path, true, parent);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export default async function (props: PageProps<'/[level1]'>) {
|
|
81
|
+
const { slug, path } = await extractDetails(props);
|
|
82
|
+
return generatePage(slug, path, true);
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
For the home page (`/`), use slug `'index'` and path `'/'` directly.
|
|
87
|
+
|
|
88
|
+
## CmsContent Usage
|
|
89
|
+
|
|
90
|
+
The appShared modules render content with:
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
<CmsContent
|
|
94
|
+
contents={page.contents}
|
|
95
|
+
rendererConfig={projectRendererConfig}
|
|
96
|
+
pageContext={{ page }}
|
|
97
|
+
/>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- `contents` – array of page/article contents (not the full page object)
|
|
101
|
+
- `rendererConfig` – from `@/lib/cms-server`
|
|
102
|
+
- `pageContext` – provides page/article/customType to child components
|
|
103
|
+
|
|
104
|
+
For route files that only need `converterContext` or `buildInformation` (e.g. `generateStaticParams`, sitemap, path computation), import from `@/lib/converter-context` when that module exists, so you don't pull in cms-server.
|
|
105
|
+
|
|
106
|
+
## Creating a Specialized Route
|
|
107
|
+
|
|
108
|
+
For a route that behaves differently from standard CMS pages (e.g. search, dashboard):
|
|
109
|
+
|
|
110
|
+
1. Create the route under `(cms-routes)` or a separate route group.
|
|
111
|
+
2. Either create a new appShared module (if the pattern is reusable) or implement inline.
|
|
112
|
+
3. Use `getPageWithErrors`, `getArticleWithErrors`, etc. from `@/lib/cms-server`.
|
|
113
|
+
4. Use `projectRendererConfig` from `@/lib/cms-server`.
|
|
114
|
+
5. Render with `CmsContent` using `contents`, `rendererConfig`, and `pageContext`.
|
|
115
|
+
|
|
116
|
+
## Custom Layouts
|
|
117
|
+
|
|
118
|
+
If a route needs a different layout (e.g. no header/footer), create `layout.tsx` in that route directory.
|
|
119
|
+
|
|
120
|
+
## Troubleshooting
|
|
121
|
+
|
|
122
|
+
- **404 on new route**: Re-run the dev server or build after adding a dynamic route segment.
|
|
123
|
+
- **Static params missing**: Dynamic routes use `generateStaticParams() { return []; }` for on-demand rendering.
|
|
124
|
+
- **Preview mode**: Handled by `draftOnly` in `@/lib/server-config`; appShared modules use it automatically.
|
|
125
|
+
|
|
126
|
+
## A/B Test Variant Route (`page-test`)
|
|
127
|
+
|
|
128
|
+
When A/B testing is enabled, variant pages are served through a `/page-test/[...slugs]` route that the middleware rewrites to. This route **must** use `generatePageTestMetadata` (not `generatePageMetadata`) so that `robots`/`indexed` comes from the **canonical/control** page — variant entries are intentionally `indexed: false` in Contentful.
|
|
129
|
+
|
|
130
|
+
If the slug is not a known variant in `testsByPath`, **return 404** (`notFound()`): there is no control page to merge, and `/page-test/…` is only valid for configured tests.
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
import { findCanonicalPath } from '@se-studio/ab-testing';
|
|
134
|
+
import type { ResolvingMetadata } from 'next';
|
|
135
|
+
import { notFound } from 'next/navigation';
|
|
136
|
+
import { testsByPath } from '@/generated/abTests';
|
|
137
|
+
import { generatePage, generatePageTestMetadata } from '@/lib/route-handlers';
|
|
138
|
+
import { getPageRouteConfig } from '@/lib/routeConfig';
|
|
139
|
+
|
|
140
|
+
/** Variant slug must appear in testsByPath; otherwise this route is not valid. */
|
|
141
|
+
function requireCanonicalPath(slug: string): string {
|
|
142
|
+
const canonicalPath = findCanonicalPath(testsByPath, slug);
|
|
143
|
+
if (!canonicalPath) {
|
|
144
|
+
notFound();
|
|
145
|
+
}
|
|
146
|
+
return canonicalPath;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function generateStaticParams() {
|
|
150
|
+
return [];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function extractDetails(props: PageProps<'/page-test/[...slugs]'>) {
|
|
154
|
+
const params = await props.params;
|
|
155
|
+
const slug = params.slugs.join('/');
|
|
156
|
+
return { slug, path: `/page-test/${slug}/` };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export async function generateMetadata(
|
|
160
|
+
props: PageProps<'/page-test/[...slugs]'>,
|
|
161
|
+
parent: ResolvingMetadata,
|
|
162
|
+
) {
|
|
163
|
+
const { slug } = await extractDetails(props);
|
|
164
|
+
const canonicalPath = requireCanonicalPath(slug);
|
|
165
|
+
const canonicalSlug = canonicalPath.replace(/^\/|\/$/g, '') || slug;
|
|
166
|
+
// Use generatePageTestMetadata: merges variant title/description with
|
|
167
|
+
// canonical page's indexed flag so robots directives are always correct.
|
|
168
|
+
return generatePageTestMetadata(slug, canonicalSlug, canonicalPath, getPageRouteConfig(canonicalSlug), parent);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export default async function (props: PageProps<'/page-test/[...slugs]'>) {
|
|
172
|
+
const { slug, path } = await extractDetails(props);
|
|
173
|
+
requireCanonicalPath(slug);
|
|
174
|
+
return generatePage(slug, path, getPageRouteConfig(slug));
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Also export `generatePageTestMetadata` from the app's `src/lib/route-handlers.ts` destructuring alongside `generatePageMetadata`.
|
|
179
|
+
|
|
180
|
+
## See Also
|
|
181
|
+
|
|
182
|
+
- **cms-routes-and-appshared** – Full route structure and appShared pattern
|
|
183
|
+
- **lib-cms-structure** – lib directory layout and cms-server usage
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: curate-showcase-mocks
|
|
3
|
+
description: Extracts real component/collection data from Contentful and curates the best examples into showcase-mocks.json, making the CMS showcase display realistic content instead of generic placeholder text.
|
|
4
|
+
license: Private
|
|
5
|
+
metadata:
|
|
6
|
+
author: se-core-product
|
|
7
|
+
version: "2.1.0"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Curate Showcase Mocks
|
|
11
|
+
|
|
12
|
+
This skill refreshes the CMS showcase with realistic content from the live Contentful space.
|
|
13
|
+
It uses a **two-phase CLI pipeline** — extraction followed by LLM-driven curation — to produce
|
|
14
|
+
`src/generated/showcase-mocks.json`, which the showcase loads at runtime.
|
|
15
|
+
|
|
16
|
+
It is **Step 2** of **`update-cms-guidelines`** mode **`fresh`** (after the clean slate). Realistic mocks and `accepted-variants` files are **required** before bulk screenshot capture in **generate-all-guidelines**.
|
|
17
|
+
|
|
18
|
+
## When to use
|
|
19
|
+
|
|
20
|
+
Run this skill when:
|
|
21
|
+
- A new app has been populated in Contentful and the showcase still shows placeholder text
|
|
22
|
+
- The CMS content has been significantly updated and the showcase looks stale
|
|
23
|
+
- New component or collection types have been added and need good mock data
|
|
24
|
+
- You are doing a **full CMS guidelines regeneration** and need fresh showcase data before Phase 1 / 1b screenshots
|
|
25
|
+
|
|
26
|
+
## Prerequisites
|
|
27
|
+
|
|
28
|
+
- The app's `.env.local` must contain `CONTENTFUL_SPACE_ID`, `CONTENTFUL_ACCESS_TOKEN`, and `CONTENTFUL_ENVIRONMENT_NAME`
|
|
29
|
+
- An LLM API key in `.env.local`: `OPENAI_API_KEY` (preferred) or `ANTHROPIC_API_KEY`
|
|
30
|
+
- Optional: `CONTENTFUL_PREVIEW_ACCESS_TOKEN` to also include draft/unpublished entries
|
|
31
|
+
- For monorepo apps: all packages must be built (`pnpm build` from repo root)
|
|
32
|
+
- The app must be fully set up (see **First-time setup** below if not yet done)
|
|
33
|
+
|
|
34
|
+
## First-time setup (new apps)
|
|
35
|
+
|
|
36
|
+
Before running this skill on a new app, four one-time changes are required.
|
|
37
|
+
|
|
38
|
+
### 1 — Add scripts to `package.json`
|
|
39
|
+
|
|
40
|
+
**Monorepo app** (uses local packages via relative paths):
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
"generate-showcase": "node ../../packages/project-build/dist/generate-showcase-data.js",
|
|
44
|
+
"generate-showcase-mocks": "node ../../packages/project-build/dist/generate-showcase-mocks.js"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**External/standalone project** (uses npm-installed packages):
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
"generate-showcase": "node node_modules/@se-studio/project-build/dist/generate-showcase-data.js",
|
|
51
|
+
"generate-showcase-mocks": "node node_modules/@se-studio/project-build/dist/generate-showcase-mocks.js"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Alternatively for external projects, use the installed binaries directly (available on PATH after install):
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
generate-showcase-data # runs in-place from the project root
|
|
58
|
+
generate-showcase-mocks
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 2 — Gitignore the generated files
|
|
62
|
+
|
|
63
|
+
In the app's `.gitignore`, add:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
# Generated showcase files (large, regenerable — curated mocks committed separately)
|
|
67
|
+
src/generated/showcase-examples.json
|
|
68
|
+
src/generated/showcase-mocks-draft.json
|
|
69
|
+
src/generated/cms-discovery/accepted-variants/
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 3 — Wire the render pages to use curated mocks
|
|
73
|
+
|
|
74
|
+
Both `src/app/(cms-dev)/cms/showcase/render/page.tsx` and `render-all/page.tsx` need to import
|
|
75
|
+
`mergeShowcaseMocks` and the curated mocks file.
|
|
76
|
+
|
|
77
|
+
**`render/page.tsx`** — change from:
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
import { DEFAULT_SHOWCASE_CONTROL_STATE, ShowcaseRenderPage } from '@se-studio/core-ui';
|
|
81
|
+
// ... other imports ...
|
|
82
|
+
|
|
83
|
+
export default async function Page(...) {
|
|
84
|
+
return (
|
|
85
|
+
<ShowcaseRenderPage
|
|
86
|
+
componentMockMap={componentMockMap}
|
|
87
|
+
collectionMockMap={collectionMockMap}
|
|
88
|
+
collectionCardMockMap={collectionCardMockMap}
|
|
89
|
+
...
|
|
90
|
+
/>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
to:
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
import { DEFAULT_SHOWCASE_CONTROL_STATE, ShowcaseRenderPage, mergeShowcaseMocks } from '@se-studio/core-ui';
|
|
99
|
+
// ... other imports ...
|
|
100
|
+
import curatedMocks from '@/generated/showcase-mocks.json';
|
|
101
|
+
|
|
102
|
+
const { componentMockMap: mergedComponentMocks, collectionMockMap: mergedCollectionMocks, collectionCardMockMap: mergedCardMocks } =
|
|
103
|
+
mergeShowcaseMocks(curatedMocks, { componentMockMap, collectionMockMap, collectionCardMockMap });
|
|
104
|
+
|
|
105
|
+
export default async function Page(...) {
|
|
106
|
+
return (
|
|
107
|
+
<ShowcaseRenderPage
|
|
108
|
+
componentMockMap={mergedComponentMocks}
|
|
109
|
+
collectionMockMap={mergedCollectionMocks}
|
|
110
|
+
collectionCardMockMap={mergedCardMocks}
|
|
111
|
+
...
|
|
112
|
+
/>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Apply the same pattern to **`render-all/page.tsx`**.
|
|
118
|
+
|
|
119
|
+
### 4 — Create an empty `showcase-mocks.json` stub
|
|
120
|
+
|
|
121
|
+
Create `src/generated/showcase-mocks.json` so the import compiles before curated data exists:
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"curatedAt": "2026-01-01T00:00:00.000Z",
|
|
126
|
+
"components": {},
|
|
127
|
+
"collections": {}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Once these four steps are done, proceed with the steps below.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Pipeline Overview
|
|
136
|
+
|
|
137
|
+
The workflow is now a **three-step pipeline**:
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
Step 1: generate-showcase-data → showcase-examples.json (all CMS data, sorted by completeness)
|
|
141
|
+
Step 2: generate-showcase-mocks → showcase-mocks-draft.json (LLM selects best + proposes variants)
|
|
142
|
+
Step 3: AI review (this skill) → showcase-mocks.json (final curated mocks, committed)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Steps 1 and 2 are automated CLI commands. Step 3 is where you (the AI) review the draft,
|
|
146
|
+
adjust anything the LLM got wrong, handle layout variants, and write the final committed file.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Steps
|
|
151
|
+
|
|
152
|
+
### Step 1 — Run the extraction script
|
|
153
|
+
|
|
154
|
+
Run the CLI to fetch all component/collection entries from Contentful:
|
|
155
|
+
|
|
156
|
+
**Monorepo:**
|
|
157
|
+
```bash
|
|
158
|
+
pnpm --filter {appName} generate-showcase
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**External project (from project root):**
|
|
162
|
+
```bash
|
|
163
|
+
node node_modules/@se-studio/project-build/dist/generate-showcase-data.js
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Optional flags:**
|
|
167
|
+
- `--include-drafts` — also fetches draft/unpublished entries via the Preview API (requires `CONTENTFUL_PREVIEW_ACCESS_TOKEN`)
|
|
168
|
+
- `--all-types` — fetches all entry content types without a filter (instead of fetching component and collection separately)
|
|
169
|
+
|
|
170
|
+
This writes `src/generated/showcase-examples.json` — every example grouped by type, sorted by
|
|
171
|
+
`fieldCompleteness` descending. This file is gitignored.
|
|
172
|
+
|
|
173
|
+
### Step 2 — Run the LLM curation script
|
|
174
|
+
|
|
175
|
+
**Monorepo:**
|
|
176
|
+
```bash
|
|
177
|
+
pnpm --filter {appName} generate-showcase-mocks
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**External project:**
|
|
181
|
+
```bash
|
|
182
|
+
node node_modules/@se-studio/project-build/dist/generate-showcase-mocks.js
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
This reads `showcase-examples.json`, calls the LLM (OpenAI or Anthropic — auto-detected from
|
|
186
|
+
`.env.local`), and produces:
|
|
187
|
+
- `src/generated/showcase-mocks-draft.json` — best mock per type + proposed variant param sets
|
|
188
|
+
- `src/generated/cms-discovery/accepted-variants/{components|collections|externals}/<slug>.json` — per-type variant files (in mode subdir)
|
|
189
|
+
|
|
190
|
+
**Optional flags:**
|
|
191
|
+
- `--model <model>` — override the LLM model (e.g. `--model gpt-4o`)
|
|
192
|
+
- `--types <Type1,Type2>` — limit to specific type names (comma-separated, useful for re-running single types)
|
|
193
|
+
|
|
194
|
+
### Step 3 — Read the draft and registrations
|
|
195
|
+
|
|
196
|
+
Read these files:
|
|
197
|
+
|
|
198
|
+
1. `src/generated/showcase-mocks-draft.json` — the LLM's selected mocks + variant proposals
|
|
199
|
+
2. `src/lib/registrations.ts` — the app's component/collection registrations
|
|
200
|
+
|
|
201
|
+
From the registrations file, collect:
|
|
202
|
+
- The full list of registered component type names
|
|
203
|
+
- The full list of registered collection type names
|
|
204
|
+
- Any registrations with `showcaseExclude: true` — these types must be skipped entirely
|
|
205
|
+
|
|
206
|
+
Check the draft against the registrations: are there registered types missing from the draft?
|
|
207
|
+
If so, check `showcase-examples.json` directly to find examples for those types.
|
|
208
|
+
|
|
209
|
+
### Step 4 — Review and refine mocks
|
|
210
|
+
|
|
211
|
+
For each type in the draft:
|
|
212
|
+
|
|
213
|
+
**Validate the LLM selection:**
|
|
214
|
+
- Confirm the selected mock has a real heading (not a test entry)
|
|
215
|
+
- Confirm it has a working visual URL (if the field exists)
|
|
216
|
+
- Confirm the mock fields are complete and reflect real brand content
|
|
217
|
+
|
|
218
|
+
**Add layout variants not in the CMS:**
|
|
219
|
+
Some types have meaningful variants that may not exist as separate CMS entries — a flipped
|
|
220
|
+
layout, a narrow/wide version, or a no-visual state. These can be represented by adjusting
|
|
221
|
+
the base mock with URL param overrides. Review each type's variant proposals from the LLM
|
|
222
|
+
(in `showcase-mocks-draft.json`) and add any important ones that are missing:
|
|
223
|
+
|
|
224
|
+
- For a Hero with a left-layout variant: add a variant with `{ "showcaseParams": { "flip": "true" } }` or similar
|
|
225
|
+
- For a content block with a narrow option: `{ "showcaseParams": { "width": "narrow" } }`
|
|
226
|
+
- Adjust the LLM's proposed params to match the actual param names used in the component's showcase controls
|
|
227
|
+
|
|
228
|
+
**For collections — curating cards:**
|
|
229
|
+
- Select ALL cards from the best example (do not cap — use the real count)
|
|
230
|
+
- If fewer than 4 cards, check the next-best example for additional cards
|
|
231
|
+
- Cards with no heading and no body are useless — skip them
|
|
232
|
+
|
|
233
|
+
### Step 5 — Write showcase-mocks.json
|
|
234
|
+
|
|
235
|
+
Write the curated data to `src/generated/showcase-mocks.json`. This file IS committed to git.
|
|
236
|
+
|
|
237
|
+
Use the LLM draft as the starting point and apply your corrections from Step 4.
|
|
238
|
+
|
|
239
|
+
**Format:**
|
|
240
|
+
|
|
241
|
+
```json
|
|
242
|
+
{
|
|
243
|
+
"curatedAt": "2026-02-28T14:00:00.000Z",
|
|
244
|
+
"components": {
|
|
245
|
+
"Hero": {
|
|
246
|
+
"heading": "Mental health care for kids and teens. Parenting support for you.",
|
|
247
|
+
"preHeading": "Brightline",
|
|
248
|
+
"body": { "json": { "nodeType": "document", "content": [...] } },
|
|
249
|
+
"visual": { "width": 1200, "height": 800, "url": "https://images.ctfassets.net/..." },
|
|
250
|
+
"backgroundColour": "Off White"
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
"collections": {
|
|
254
|
+
"FAQ": {
|
|
255
|
+
"mock": { "heading": "Top FAQs" },
|
|
256
|
+
"cards": [
|
|
257
|
+
{ "heading": "Who is Brightline?", "body": { "json": { ... } } }
|
|
258
|
+
]
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**Rules for the output:**
|
|
265
|
+
- Set `curatedAt` to the current ISO timestamp
|
|
266
|
+
- Only include types that have real CMS data
|
|
267
|
+
- For component mocks: include only fields present in the source data
|
|
268
|
+
- For collection mocks: always include `mock` and `cards`
|
|
269
|
+
- Rich Text body fields must be `{ "json": <Document> }` — copy as-is
|
|
270
|
+
- Visual fields must include `width`, `height`, AND `url` — never strip `url`
|
|
271
|
+
- Include `widthPercent` when present on media/externalVideo entries
|
|
272
|
+
- Include `otherMedia` when present — copy the full array including `url` and `widthPercent`
|
|
273
|
+
- Do NOT invent or modify content — use the real data from the examples file
|
|
274
|
+
- Include `backgroundColour` and `textColour` where the source has them
|
|
275
|
+
- For cards: include ALL fields present in source, including `backgroundColour`, `textColour`, `links`
|
|
276
|
+
|
|
277
|
+
### Step 6 — Verify
|
|
278
|
+
|
|
279
|
+
After writing, briefly confirm:
|
|
280
|
+
- The file is valid JSON
|
|
281
|
+
- Component and collection counts look reasonable (non-zero)
|
|
282
|
+
- Key types (Hero, main collections) have entries
|
|
283
|
+
- Visual fields include `url` properties
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Output files
|
|
288
|
+
|
|
289
|
+
| File | Location | Committed |
|
|
290
|
+
|------|----------|-----------|
|
|
291
|
+
| `showcase-examples.json` | `src/generated/showcase-examples.json` | No (gitignored) |
|
|
292
|
+
| `showcase-mocks-draft.json` | `src/generated/showcase-mocks-draft.json` | No (gitignored) |
|
|
293
|
+
| `accepted-variants/` | `src/generated/cms-discovery/accepted-variants/{components\|collections\|externals}/` | No (gitignored) |
|
|
294
|
+
| `showcase-mocks.json` | `src/generated/showcase-mocks.json` | Yes |
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## How the showcase uses this
|
|
299
|
+
|
|
300
|
+
The showcase uses a **two-layer model**:
|
|
301
|
+
|
|
302
|
+
1. **Base: mock data** — `showcase-mocks.json` is merged over inline registration mocks via
|
|
303
|
+
`mergeShowcaseMocks()` from `@se-studio/core-ui`. Curated data takes precedence; types without
|
|
304
|
+
curated data fall back to their inline `mock:` in the registration.
|
|
305
|
+
|
|
306
|
+
2. **Patch: URL params** — Any control field in the URL (e.g. `?backgroundColour=Navy`) overrides
|
|
307
|
+
the mock. The control bar encodes only values that differ from the mock baseline.
|
|
308
|
+
|
|
309
|
+
`?clean=true` skips mock data entirely and uses the default showcase control state only.
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## External project setup
|
|
314
|
+
|
|
315
|
+
### Installing skills
|
|
316
|
+
|
|
317
|
+
For standalone projects not in the monorepo, install SE Studio skills via the `skills` CLI:
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
npx skills add @se-studio/skills
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
This installs all SE Studio agent skills (including this one) into the project's agent skill
|
|
324
|
+
directories (e.g. `.claude/skills/` for Claude Code).
|
|
325
|
+
|
|
326
|
+
### Auto-sync on package update
|
|
327
|
+
|
|
328
|
+
Add `@se-studio/skills` as a devDependency and a `postinstall` script so skills reinstall
|
|
329
|
+
whenever `pnpm install` is run:
|
|
330
|
+
|
|
331
|
+
```json
|
|
332
|
+
"devDependencies": {
|
|
333
|
+
"@se-studio/skills": "^1.0.0"
|
|
334
|
+
},
|
|
335
|
+
"scripts": {
|
|
336
|
+
"postinstall": "npx skills update -y"
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Script paths
|
|
341
|
+
|
|
342
|
+
The CLI scripts are available as installed binaries after `npm install @se-studio/project-build`.
|
|
343
|
+
Both `generate-showcase-data` and `generate-showcase-mocks` read `.env.local` from `process.cwd()`,
|
|
344
|
+
so run them from the project root.
|