@se-studio/project-build 1.0.104 → 1.0.105

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # @se-studio/project-build
2
2
 
3
+ ## 1.0.105
4
+
5
+ ### Patch Changes
6
+
7
+ - Bulk version bump: patch for all packages
8
+
3
9
  ## 1.0.104
4
10
 
5
11
  ### Patch Changes
@@ -1,2 +1,4 @@
1
- export declare function syncSkills(targetDir?: string): Promise<void>;
1
+ export declare function syncSkills(targetDir?: string,
2
+ /** When true, exits silently (code 0) if not running as an installed package. Safe for postinstall. */
3
+ ifInstalled?: boolean): Promise<void>;
2
4
  //# sourceMappingURL=sync-skills.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sync-skills.d.ts","sourceRoot":"","sources":["../../src/management/sync-skills.ts"],"names":[],"mappings":"AAuBA,wBAAsB,UAAU,CAAC,SAAS,GAAE,MAAsB,iBA0BjE"}
1
+ {"version":3,"file":"sync-skills.d.ts","sourceRoot":"","sources":["../../src/management/sync-skills.ts"],"names":[],"mappings":"AAmCA,wBAAsB,UAAU,CAC9B,SAAS,GAAE,MAAsB;AACjC,uGAAuG;AACvG,WAAW,UAAQ,iBAsCpB"}
@@ -17,16 +17,37 @@ async function copyDir(src, dest) {
17
17
  }
18
18
  }
19
19
  }
20
- export async function syncSkills(targetDir = process.cwd()) {
20
+ /**
21
+ * Returns true when this script is running from inside a pnpm/npm workspace
22
+ * (i.e. as a local package, not as an installed dependency from the registry).
23
+ *
24
+ * Detection: if the path to this file does NOT contain "node_modules" then we
25
+ * are running from the workspace source — syncing would overwrite the canonical
26
+ * skill files with the distributed copies, which is the wrong direction.
27
+ */
28
+ function isWorkspacePackage() {
29
+ return !__dirname.includes('node_modules');
30
+ }
31
+ export async function syncSkills(targetDir = process.cwd(),
32
+ /** When true, exits silently (code 0) if not running as an installed package. Safe for postinstall. */
33
+ ifInstalled = false) {
34
+ if (isWorkspacePackage()) {
35
+ if (ifInstalled) {
36
+ // Running inside the monorepo during a workspace install — skip silently.
37
+ return;
38
+ }
39
+ console.warn('Warning: seskills sync is running from a workspace package (not from node_modules).\n' +
40
+ 'Skipping to avoid overwriting canonical skill files in the monorepo.\n' +
41
+ 'Run `seskills sync` from an external project that has @se-studio/project-build installed.');
42
+ return;
43
+ }
21
44
  // Locate the skills directory relative to this file (dist/management/sync-skills.js)
22
- // We want to go up to the package root and then into skills
23
- // dist/management/ -> dist/ -> root -> skills
45
+ // dist/management/ -> dist/ -> package root -> skills/
24
46
  const packageRoot = path.resolve(__dirname, '..', '..');
25
47
  const skillsSourceDir = path.join(packageRoot, 'skills');
26
48
  const skillsTargetDir = path.join(targetDir, '.cursor', 'skills');
27
49
  console.log(`Syncing skills from ${skillsSourceDir} to ${skillsTargetDir}...`);
28
50
  try {
29
- // Check if source exists
30
51
  try {
31
52
  await fs.promises.access(skillsSourceDir);
32
53
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sync-skills.js","sourceRoot":"","sources":["../../src/management/sync-skills.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAE3C,KAAK,UAAU,OAAO,CAAC,GAAW,EAAE,IAAY;IAC9C,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAExE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAE7C,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,MAAM,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACnC,CAAC;aAAM,CAAC;YACN,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,YAAoB,OAAO,CAAC,GAAG,EAAE;IAChE,qFAAqF;IACrF,4DAA4D;IAC5D,8CAA8C;IAC9C,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACxD,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IACzD,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;IAElE,OAAO,CAAC,GAAG,CAAC,uBAAuB,eAAe,OAAO,eAAe,KAAK,CAAC,CAAC;IAE/E,IAAI,CAAC;QACH,yBAAyB;QACzB,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;QAC5C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,KAAK,CAAC,wCAAwC,eAAe,EAAE,CAAC,CAAC;YACzE,OAAO,CAAC,KAAK,CAAC,uEAAuE,CAAC,CAAC;YACvF,OAAO;QACT,CAAC;QAED,MAAM,OAAO,CAAC,eAAe,EAAE,eAAe,CAAC,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,KAAK,CAAC,CAAC;QAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"sync-skills.js","sourceRoot":"","sources":["../../src/management/sync-skills.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAE3C,KAAK,UAAU,OAAO,CAAC,GAAW,EAAE,IAAY;IAC9C,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAExE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAE7C,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,MAAM,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACnC,CAAC;aAAM,CAAC;YACN,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,kBAAkB;IACzB,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,YAAoB,OAAO,CAAC,GAAG,EAAE;AACjC,uGAAuG;AACvG,WAAW,GAAG,KAAK;IAEnB,IAAI,kBAAkB,EAAE,EAAE,CAAC;QACzB,IAAI,WAAW,EAAE,CAAC;YAChB,0EAA0E;YAC1E,OAAO;QACT,CAAC;QACD,OAAO,CAAC,IAAI,CACV,uFAAuF;YACrF,wEAAwE;YACxE,2FAA2F,CAC9F,CAAC;QACF,OAAO;IACT,CAAC;IAED,qFAAqF;IACrF,uDAAuD;IACvD,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACxD,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IACzD,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;IAElE,OAAO,CAAC,GAAG,CAAC,uBAAuB,eAAe,OAAO,eAAe,KAAK,CAAC,CAAC;IAE/E,IAAI,CAAC;QACH,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;QAC5C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,KAAK,CAAC,wCAAwC,eAAe,EAAE,CAAC,CAAC;YACzE,OAAO,CAAC,KAAK,CAAC,uEAAuE,CAAC,CAAC;YACvF,OAAO;QACT,CAAC;QAED,MAAM,OAAO,CAAC,eAAe,EAAE,eAAe,CAAC,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,KAAK,CAAC,CAAC;QAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
package/dist/seskills.js CHANGED
@@ -10,11 +10,18 @@ async function main() {
10
10
  const args = process.argv.slice(2);
11
11
  const command = args[0];
12
12
  if (command === 'sync') {
13
- const targetDir = args[1] || process.cwd();
14
- await syncSkills(targetDir);
13
+ // Filter out flags from positional args
14
+ const positional = args.slice(1).filter((a) => !a.startsWith('--'));
15
+ const ifInstalled = args.includes('--if-installed');
16
+ const targetDir = positional[0] || process.cwd();
17
+ await syncSkills(targetDir, ifInstalled);
15
18
  }
16
19
  else {
17
- console.log('Usage: seskills sync [target-dir]');
20
+ console.log('Usage: seskills sync [target-dir] [--if-installed]');
21
+ console.log('');
22
+ console.log(' --if-installed Exit silently when running as a workspace package.');
23
+ console.log(' Use this in a postinstall script so it only runs in');
24
+ console.log(' external projects, not inside the monorepo.');
18
25
  process.exit(1);
19
26
  }
20
27
  }
@@ -1 +1 @@
1
- {"version":3,"file":"seskills.js","sourceRoot":"","sources":["../src/seskills.ts"],"names":[],"mappings":";AACA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAEzD,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IAExB,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACvB,MAAM,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAC3C,MAAM,UAAU,CAAC,SAAS,CAAC,CAAC;IAC9B,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC;QACjD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"seskills.js","sourceRoot":"","sources":["../src/seskills.ts"],"names":[],"mappings":";AACA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAEzD,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IAExB,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACvB,wCAAwC;QACxC,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;QACpE,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;QACpD,MAAM,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QACjD,MAAM,UAAU,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IAC3C,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,oDAAoD,CAAC,CAAC;QAClE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,sEAAsE,CAAC,CAAC;QACpF,OAAO,CAAC,GAAG,CAAC,uEAAuE,CAAC,CAAC;QACrF,OAAO,CAAC,GAAG,CAAC,+DAA+D,CAAC,CAAC;QAC7E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@se-studio/project-build",
3
- "version": "1.0.104",
3
+ "version": "1.0.105",
4
4
  "description": "Build tools and management scripts for SE Studio projects",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,302 @@
1
+ # Skill: Generate CMS Guidelines
2
+
3
+ Use this skill when you need to generate, regenerate, or merge CMS guideline markdown fragments for any app in the monorepo.
4
+
5
+ ---
6
+
7
+ ## When to use this skill
8
+
9
+ - **Full from scratch:** Generating guidelines for all components, collections, and externals in an app for the first time.
10
+ - **Single type:** Regenerating the guideline for one component, collection, or external (e.g. after editing the component code).
11
+ - **Merge only:** Rebuilding `COMPONENT_GUIDELINES_FOR_LLM.md` after manual edits to guideline fragments.
12
+ - **Regenerate all:** Re-running everything after bulk code changes or prompt tweaks.
13
+
14
+ ---
15
+
16
+ ## Prerequisite: Showcase lifecycle
17
+
18
+ Guidelines are only meaningful when the showcase is populated with realistic data. Complete these steps **before** running guideline generation for the first time:
19
+
20
+ 1. **Build the site** and ensure all content is correct and published in Contentful.
21
+ 2. **Generate showcase data:**
22
+ ```bash
23
+ pnpm --filter <appName> generate-showcase
24
+ ```
25
+ This writes `src/cms-showcase/showcase-examples.json` to the app.
26
+ 3. **Curate showcase mocks:** Follow the [curate-showcase-mocks skill](.cursor/skills/se-marketing-sites/curate-showcase-mocks/SKILL.md) to produce `showcase-mocks.json`.
27
+ 4. **Generate field list:**
28
+ ```bash
29
+ # From the app directory:
30
+ pnpm run cms-discovery:field-list
31
+ # Or from repo root:
32
+ pnpm --filter <appName> exec cms-generate-field-list --app-dir .
33
+ ```
34
+ This writes `generated/cms-discovery/field-list.json`.
35
+
36
+ ---
37
+
38
+ ## Inputs
39
+
40
+ | Input | Description |
41
+ |---|---|
42
+ | **App directory** | Repo-relative path, e.g. `apps/example-om1` or `apps/example-se2026` |
43
+ | **Mode** | `full` \| `single` \| `merge-only` |
44
+ | **Type name** | Required for `single` mode — the exact type name from discovery, e.g. `"Hero"` or `"Cards Grid"` |
45
+ | **Discovery URL** | URL to the live app's discovery endpoint, e.g. `http://localhost:3013/api/cms/discovery/` |
46
+
47
+ **App ports** (app must be running for `full` and `single` modes):
48
+
49
+ | App | Port | Discovery URL |
50
+ |---|---|---|
51
+ | example-brightline | 3010 | `http://localhost:3010/api/cms/discovery/` |
52
+ | example-se2026 | 3012 | `http://localhost:3012/api/cms/discovery/` |
53
+ | example-om1 | 3013 | `http://localhost:3013/api/cms/discovery/` |
54
+ | example-empty | 3014 | `http://localhost:3014/api/cms/discovery/` |
55
+ | example-brightlifekids | 3015 | `http://localhost:3015/api/cms/discovery/` |
56
+
57
+ ---
58
+
59
+ ## Mode: full
60
+
61
+ Run all steps from scratch. Generates guidelines for all collections, externals, and components.
62
+
63
+ ### Step 1 — Verify prerequisites
64
+
65
+ Confirm that:
66
+ - The app is running (check `http://localhost:<port>/api/cms/discovery/` returns JSON).
67
+ - `generated/cms-discovery/field-list.json` exists in the app directory.
68
+ - Showcase has been generated (see Prerequisite section above).
69
+
70
+ ### Step 2 — Generate collections and externals
71
+
72
+ Run the project-independent generator. It reads discovery, writes all `collections/*.md`, creates any missing `externals/*.md` templates, and updates `colour-hints.json`:
73
+
74
+ ```bash
75
+ cms-generate-collection-guidelines \
76
+ --discovery-url <discoveryUrl> \
77
+ --app-dir <appDir>
78
+ ```
79
+
80
+ **Overwrite behaviour:**
81
+ - **Collections:** always overwritten (regenerated from discovery on each run).
82
+ - **Externals:** created only if the file does not exist. Existing external files are preserved to protect hand-authored schema documentation.
83
+
84
+ ### Step 2b — Fill in external component schemas (first time only)
85
+
86
+ For each external component file created in `docs/cms-guidelines/externals/`, open the file and fill in the **Fields & Schema** and **Usage** sections. These files are not regenerated once created, so the schema documentation you add here is permanent.
87
+
88
+ For each external type:
89
+ 1. Check the Contentful content type schema (or ask the project owner) for the list of fields, their types, and purpose.
90
+ 2. Replace the `<!-- TODO: ... -->` block in `## Fields & Schema` with a filled-in Markdown table.
91
+ 3. Replace the `## Usage` placeholder with a short description of how to add and configure the component via `cms-edit`.
92
+ 4. Commit the completed files to version control.
93
+
94
+ Example of a completed `## Fields & Schema` section:
95
+
96
+ ```markdown
97
+ ## Fields & Schema
98
+
99
+ | Field | Type | Required | Notes |
100
+ |-------|------|----------|-------|
101
+ | externalComponentType | Symbol | Yes | Must be set to `ResearchChart` |
102
+ | data | Object | No | Chart config payload — see project data-viz spec |
103
+ | cmsLabel | Symbol | Yes | Internal label for CMS authoring |
104
+ ```
105
+
106
+ ### Step 3 — Generate component guidelines
107
+
108
+ For each component in discovery order, run the full per-component pipeline defined in
109
+ [`packages/contentful-cms/skills/cms-guidelines/generate-component-guidelines.md`](../../../packages/contentful-cms/skills/cms-guidelines/generate-component-guidelines.md).
110
+
111
+ To find unfinished components:
112
+ 1. Fetch `<discoveryUrl>` and collect all `components[].name` values.
113
+ 2. Compute slug for each: `name.toLowerCase().replace(/\s+/g, '-').replace(/[()]/g, '')`.
114
+ 3. Check which slugs lack a corresponding file in `<appDir>/docs/cms-guidelines/components/<slug>.md`.
115
+ 4. Process only the unfinished ones (or all, if regenerating).
116
+
117
+ **For each unfinished component** (process in batches of 5 if preferred):
118
+
119
+ Run a Cursor subagent with this prompt, substituting `<COMPONENT_TYPE>` and `<APP_DIR>`:
120
+
121
+ ```
122
+ Run the full guideline generation pipeline for <COMPONENT_TYPE>.
123
+ Target app: <APP_DIR> (all paths relative to repo root / that directory).
124
+
125
+ Read these pipeline docs first (they are in packages/contentful-cms/skills/cms-guidelines/):
126
+ - generate-component-guidelines.md (overall pipeline)
127
+ - variant-loop.md (Phase A: variant loop)
128
+ - generation-prompt.md (Phase B: generation)
129
+ - colour-hint-prompt.md (Phase C: colour hint)
130
+ - validation-prompt.md (Phase D: validation)
131
+
132
+ === Phase A: Variant loop ===
133
+ Follow variant-loop.md.
134
+ 1. Read src/project/components/<TypeName>.tsx (or collections/<TypeName>.tsx).
135
+ 2. Read generated/cms-discovery/field-list.json for field metadata.
136
+ 3. Propose 5 variants using variant-proposal-prompt.md.
137
+ 4. Capture screenshots:
138
+ cms-capture-screenshots --variants /tmp/<type-slug>-variants.json --app-dir <APP_DIR>
139
+ This also updates docs/cms-guidelines/screenshots/index.json automatically.
140
+ 5. Evaluate screenshots using evaluation-prompt.md.
141
+ When done: save accepted list to generated/cms-discovery/accepted-variants/<type-slug>.json
142
+
143
+ === Phase B: Generate guideline ===
144
+ Follow generation-prompt.md.
145
+ 1. Describe screenshots (Step 1 prompt).
146
+ 2. Extract behaviour from code (Step 2 prompt).
147
+ 3. Merge into guideline block (Step 3 prompt).
148
+ IMPORTANT: Read docs/cms-guidelines/screenshots/index.json and .contentful-cms.json first.
149
+ The guideline must include a ## Screenshots section with image links:
150
+ ![Default](<devBaseUrl>/cms/screenshot?file=<type-slug>-default.png)
151
+ Only include variants present in index.json. Use devBaseUrl from .contentful-cms.json.
152
+ 4. Save to docs/cms-guidelines/components/<type-slug>.md.
153
+
154
+ === Phase C: Colour hint ===
155
+ Follow colour-hint-prompt.md.
156
+ Run:
157
+ cms-update-colour-hints --app-dir <APP_DIR> \
158
+ --type "<COMPONENT_TYPE>" --hint "<hint>" --notes "<brief reason>"
159
+
160
+ === Phase D: Validate ===
161
+ Follow validation-prompt.md. Apply fixes until APPROVED.
162
+
163
+ Do NOT run merge yet — merge runs once after all components.
164
+ ```
165
+
166
+ ### Step 4 — Merge
167
+
168
+ After all components are done, run merge:
169
+
170
+ ```bash
171
+ cms-merge-guidelines --app-dir <appDir>
172
+ ```
173
+
174
+ Or from the app directory: `pnpm run cms-guidelines:merge`
175
+
176
+ ### Step 5 — Commit (optional)
177
+
178
+ ```bash
179
+ git add <appDir>/docs/cms-guidelines/ <appDir>/generated/cms-discovery/
180
+ git commit -m "chore(<app>): generate all CMS guidelines"
181
+ ```
182
+
183
+ ---
184
+
185
+ ## Mode: single
186
+
187
+ Regenerate the guideline for one type. Useful after editing a component's code.
188
+
189
+ ### Step 1 — Fetch discovery to determine type category
190
+
191
+ ```bash
192
+ curl -sL '<discoveryUrl>' | node -e "
193
+ const data = JSON.parse(require('fs').readFileSync('/dev/stdin','utf8'));
194
+ const name = '<TYPE_NAME>';
195
+ const inComponents = data.components.some(c => c.name === name);
196
+ const inCollections = data.collections.some(c => c.name === name);
197
+ const inExternals = data.externals.some(c => c.name === name);
198
+ console.log(inComponents ? 'component' : inCollections ? 'collection' : inExternals ? 'external' : 'not found');
199
+ "
200
+ ```
201
+
202
+ ### Step 2a — If component
203
+
204
+ Run the full per-component pipeline (Phases A–D) from the subagent prompt in Mode: full → Step 3, for just this one type.
205
+
206
+ ### Step 2b — If collection or external
207
+
208
+ For a **collection:** re-run the generator — it always overwrites collection files:
209
+
210
+ ```bash
211
+ cms-generate-collection-guidelines \
212
+ --discovery-url <discoveryUrl> \
213
+ --app-dir <appDir>
214
+ ```
215
+
216
+ For an **external:** the generator will not overwrite an existing file. Edit `docs/cms-guidelines/externals/<slug>.md` directly, updating the `## Fields & Schema` and `## Usage` sections as needed. If you need to regenerate from the template (e.g. for a new external), delete the file first and re-run the generator, then fill in the schema sections.
217
+
218
+ ### Step 3 — Merge
219
+
220
+ ```bash
221
+ cms-merge-guidelines --app-dir <appDir>
222
+ ```
223
+
224
+ ---
225
+
226
+ ## Mode: merge-only
227
+
228
+ Use when guideline fragments have been edited manually or generated outside this skill.
229
+
230
+ ```bash
231
+ cms-merge-guidelines --app-dir <appDir>
232
+ ```
233
+
234
+ Or from the app directory: `pnpm run cms-guidelines:merge`
235
+
236
+ This rebuilds `docs/cms-guidelines/COMPONENT_GUIDELINES_FOR_LLM.md` from all existing `components/*.md`, `collections/*.md`, and `externals/*.md` fragments.
237
+
238
+ ---
239
+
240
+ ## Output files
241
+
242
+ All produced under `<appDir>`:
243
+
244
+ | File | Description |
245
+ |---|---|
246
+ | `docs/cms-guidelines/components/<slug>.md` | Per-component guideline fragment (includes screenshot links) |
247
+ | `docs/cms-guidelines/collections/<slug>.md` | Per-collection guideline fragment |
248
+ | `docs/cms-guidelines/externals/<slug>.md` | Per-external guideline fragment |
249
+ | `docs/cms-guidelines/screenshots/index.json` | Index of all captured screenshots (type, variant, file, date) |
250
+ | `docs/cms-guidelines/COMPONENT_GUIDELINES_FOR_LLM.md` | Merged full document (includes screenshot links) |
251
+ | `generated/cms-discovery/accepted-variants/<slug>.json` | Accepted variant list (components only) |
252
+ | `generated/cms-discovery/colour-hints.json` | Colour application hints for all types |
253
+
254
+ ### Screenshot links in guidelines
255
+
256
+ Each component guideline contains a `## Screenshots` section with image links:
257
+
258
+ ```markdown
259
+ ## Screenshots
260
+
261
+ | Variant | Preview |
262
+ |---------|---------|
263
+ | Default | ![Default](http://localhost:3010/cms/screenshot?file=hero-default.png) |
264
+ | Navy | ![Navy](http://localhost:3010/cms/screenshot?file=hero-navy.png) |
265
+ ```
266
+
267
+ These links:
268
+ - Resolve when the dev server is running (the `/cms/screenshot` route serves the PNG files)
269
+ - Use the `devBaseUrl` from `.contentful-cms.json` for the host
270
+ - Work for both human reviewers browsing the guidelines and AI assistants reading the markdown
271
+ - Are preserved in `COMPONENT_GUIDELINES_FOR_LLM.md` when `cms-merge-guidelines` runs
272
+
273
+ ---
274
+
275
+ ## Pipeline reference
276
+
277
+ All per-component pipeline docs live in `packages/contentful-cms/skills/cms-guidelines/`:
278
+
279
+ | File | Purpose |
280
+ |---|---|
281
+ | `generate-component-guidelines.md` | Full pipeline overview (Phases A–F) |
282
+ | `variant-loop.md` | Phase A: propose → screenshot → evaluate |
283
+ | `variant-proposal-prompt.md` | LLM prompt for proposing variants |
284
+ | `evaluation-prompt.md` | LLM prompt for evaluating screenshots |
285
+ | `generation-prompt.md` | Phase B: three-step guideline generation |
286
+ | `colour-hint-prompt.md` | Phase C: derive colour application hint |
287
+ | `validation-prompt.md` | Phase D: 9-check validation |
288
+
289
+ ---
290
+
291
+ ## New CLI reference
292
+
293
+ ```
294
+ cms-generate-collection-guidelines --discovery-url <url> --app-dir <path>
295
+ ```
296
+
297
+ | Flag | Description |
298
+ |---|---|
299
+ | `--discovery-url <url>` | Discovery API URL (required). App must be running. |
300
+ | `--app-dir <path>` | App directory (default: cwd). |
301
+
302
+ Writes `docs/cms-guidelines/collections/*.md`, `docs/cms-guidelines/externals/*.md`, and updates `generated/cms-discovery/colour-hints.json`. Project-independent: works for any app in the monorepo.
@@ -0,0 +1,340 @@
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.0.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
+ ## When to use
17
+
18
+ Run this skill when:
19
+ - A new app has been populated in Contentful and the showcase still shows placeholder text
20
+ - The CMS content has been significantly updated and the showcase looks stale
21
+ - New component or collection types have been added and need good mock data
22
+
23
+ ## Prerequisites
24
+
25
+ - The app's `.env.local` must contain `CONTENTFUL_SPACE_ID`, `CONTENTFUL_ACCESS_TOKEN`, and `CONTENTFUL_ENVIRONMENT_NAME`
26
+ - An LLM API key in `.env.local`: `OPENAI_API_KEY` (preferred) or `ANTHROPIC_API_KEY`
27
+ - Optional: `CONTENTFUL_PREVIEW_ACCESS_TOKEN` to also include draft/unpublished entries
28
+ - For monorepo apps: all packages must be built (`pnpm build` from repo root)
29
+ - The app must be fully set up (see **First-time setup** below if not yet done)
30
+
31
+ ## First-time setup (new apps)
32
+
33
+ Before running this skill on a new app, four one-time changes are required.
34
+
35
+ ### 1 — Add scripts to `package.json`
36
+
37
+ **Monorepo app** (uses local packages via relative paths):
38
+
39
+ ```json
40
+ "generate-showcase": "node ../../packages/project-build/dist/generate-showcase-data.js",
41
+ "generate-showcase-mocks": "node ../../packages/project-build/dist/generate-showcase-mocks.js"
42
+ ```
43
+
44
+ **External/standalone project** (uses npm-installed packages):
45
+
46
+ ```json
47
+ "generate-showcase": "node node_modules/@se-studio/project-build/dist/generate-showcase-data.js",
48
+ "generate-showcase-mocks": "node node_modules/@se-studio/project-build/dist/generate-showcase-mocks.js"
49
+ ```
50
+
51
+ Alternatively for external projects, use the installed binaries directly (available on PATH after install):
52
+
53
+ ```bash
54
+ generate-showcase-data # runs in-place from the project root
55
+ generate-showcase-mocks
56
+ ```
57
+
58
+ ### 2 — Gitignore the generated files
59
+
60
+ In the app's `.gitignore`, add:
61
+
62
+ ```
63
+ # Generated showcase files (large, regenerable — curated mocks committed separately)
64
+ src/generated/showcase-examples.json
65
+ src/generated/showcase-mocks-draft.json
66
+ src/generated/cms-discovery/accepted-variants/
67
+ ```
68
+
69
+ ### 3 — Wire the render pages to use curated mocks
70
+
71
+ Both `src/app/(cms-dev)/cms/showcase/render/page.tsx` and `render-all/page.tsx` need to import
72
+ `mergeShowcaseMocks` and the curated mocks file.
73
+
74
+ **`render/page.tsx`** — change from:
75
+
76
+ ```tsx
77
+ import { DEFAULT_SHOWCASE_CONTROL_STATE, ShowcaseRenderPage } from '@se-studio/core-ui';
78
+ // ... other imports ...
79
+
80
+ export default async function Page(...) {
81
+ return (
82
+ <ShowcaseRenderPage
83
+ componentMockMap={componentMockMap}
84
+ collectionMockMap={collectionMockMap}
85
+ collectionCardMockMap={collectionCardMockMap}
86
+ ...
87
+ />
88
+ );
89
+ }
90
+ ```
91
+
92
+ to:
93
+
94
+ ```tsx
95
+ import { DEFAULT_SHOWCASE_CONTROL_STATE, ShowcaseRenderPage, mergeShowcaseMocks } from '@se-studio/core-ui';
96
+ // ... other imports ...
97
+ import curatedMocks from '@/generated/showcase-mocks.json';
98
+
99
+ const { componentMockMap: mergedComponentMocks, collectionMockMap: mergedCollectionMocks, collectionCardMockMap: mergedCardMocks } =
100
+ mergeShowcaseMocks(curatedMocks, { componentMockMap, collectionMockMap, collectionCardMockMap });
101
+
102
+ export default async function Page(...) {
103
+ return (
104
+ <ShowcaseRenderPage
105
+ componentMockMap={mergedComponentMocks}
106
+ collectionMockMap={mergedCollectionMocks}
107
+ collectionCardMockMap={mergedCardMocks}
108
+ ...
109
+ />
110
+ );
111
+ }
112
+ ```
113
+
114
+ Apply the same pattern to **`render-all/page.tsx`**.
115
+
116
+ ### 4 — Create an empty `showcase-mocks.json` stub
117
+
118
+ Create `src/generated/showcase-mocks.json` so the import compiles before curated data exists:
119
+
120
+ ```json
121
+ {
122
+ "curatedAt": "2026-01-01T00:00:00.000Z",
123
+ "components": {},
124
+ "collections": {}
125
+ }
126
+ ```
127
+
128
+ Once these four steps are done, proceed with the steps below.
129
+
130
+ ---
131
+
132
+ ## Pipeline Overview
133
+
134
+ The workflow is now a **three-step pipeline**:
135
+
136
+ ```
137
+ Step 1: generate-showcase-data → showcase-examples.json (all CMS data, sorted by completeness)
138
+ Step 2: generate-showcase-mocks → showcase-mocks-draft.json (LLM selects best + proposes variants)
139
+ Step 3: AI review (this skill) → showcase-mocks.json (final curated mocks, committed)
140
+ ```
141
+
142
+ Steps 1 and 2 are automated CLI commands. Step 3 is where you (the AI) review the draft,
143
+ adjust anything the LLM got wrong, handle layout variants, and write the final committed file.
144
+
145
+ ---
146
+
147
+ ## Steps
148
+
149
+ ### Step 1 — Run the extraction script
150
+
151
+ Run the CLI to fetch all component/collection entries from Contentful:
152
+
153
+ **Monorepo:**
154
+ ```bash
155
+ pnpm --filter {appName} generate-showcase
156
+ ```
157
+
158
+ **External project (from project root):**
159
+ ```bash
160
+ node node_modules/@se-studio/project-build/dist/generate-showcase-data.js
161
+ ```
162
+
163
+ **Optional flags:**
164
+ - `--include-drafts` — also fetches draft/unpublished entries via the Preview API (requires `CONTENTFUL_PREVIEW_ACCESS_TOKEN`)
165
+ - `--all-types` — fetches all entry content types without a filter (instead of fetching component and collection separately)
166
+
167
+ This writes `src/generated/showcase-examples.json` — every example grouped by type, sorted by
168
+ `fieldCompleteness` descending. This file is gitignored.
169
+
170
+ ### Step 2 — Run the LLM curation script
171
+
172
+ **Monorepo:**
173
+ ```bash
174
+ pnpm --filter {appName} generate-showcase-mocks
175
+ ```
176
+
177
+ **External project:**
178
+ ```bash
179
+ node node_modules/@se-studio/project-build/dist/generate-showcase-mocks.js
180
+ ```
181
+
182
+ This reads `showcase-examples.json`, calls the LLM (OpenAI or Anthropic — auto-detected from
183
+ `.env.local`), and produces:
184
+ - `src/generated/showcase-mocks-draft.json` — best mock per type + proposed variant param sets
185
+ - `src/generated/cms-discovery/accepted-variants/<slug>.json` — per-type variant files
186
+
187
+ **Optional flags:**
188
+ - `--model <model>` — override the LLM model (e.g. `--model gpt-4o`)
189
+ - `--types <Type1,Type2>` — limit to specific type names (comma-separated, useful for re-running single types)
190
+
191
+ ### Step 3 — Read the draft and registrations
192
+
193
+ Read these files:
194
+
195
+ 1. `src/generated/showcase-mocks-draft.json` — the LLM's selected mocks + variant proposals
196
+ 2. `src/lib/registrations.ts` — the app's component/collection registrations
197
+
198
+ From the registrations file, collect:
199
+ - The full list of registered component type names
200
+ - The full list of registered collection type names
201
+ - Any registrations with `showcaseExclude: true` — these types must be skipped entirely
202
+
203
+ Check the draft against the registrations: are there registered types missing from the draft?
204
+ If so, check `showcase-examples.json` directly to find examples for those types.
205
+
206
+ ### Step 4 — Review and refine mocks
207
+
208
+ For each type in the draft:
209
+
210
+ **Validate the LLM selection:**
211
+ - Confirm the selected mock has a real heading (not a test entry)
212
+ - Confirm it has a working visual URL (if the field exists)
213
+ - Confirm the mock fields are complete and reflect real brand content
214
+
215
+ **Add layout variants not in the CMS:**
216
+ Some types have meaningful variants that may not exist as separate CMS entries — a flipped
217
+ layout, a narrow/wide version, or a no-visual state. These can be represented by adjusting
218
+ the base mock with URL param overrides. Review each type's variant proposals from the LLM
219
+ (in `showcase-mocks-draft.json`) and add any important ones that are missing:
220
+
221
+ - For a Hero with a left-layout variant: add a variant with `{ "showcaseParams": { "flip": "true" } }` or similar
222
+ - For a content block with a narrow option: `{ "showcaseParams": { "width": "narrow" } }`
223
+ - Adjust the LLM's proposed params to match the actual param names used in the component's showcase controls
224
+
225
+ **For collections — curating cards:**
226
+ - Select ALL cards from the best example (do not cap — use the real count)
227
+ - If fewer than 4 cards, check the next-best example for additional cards
228
+ - Cards with no heading and no body are useless — skip them
229
+
230
+ ### Step 5 — Write showcase-mocks.json
231
+
232
+ Write the curated data to `src/generated/showcase-mocks.json`. This file IS committed to git.
233
+
234
+ Use the LLM draft as the starting point and apply your corrections from Step 4.
235
+
236
+ **Format:**
237
+
238
+ ```json
239
+ {
240
+ "curatedAt": "2026-02-28T14:00:00.000Z",
241
+ "components": {
242
+ "Hero": {
243
+ "heading": "Mental health care for kids and teens. Parenting support for you.",
244
+ "preHeading": "Brightline",
245
+ "body": { "json": { "nodeType": "document", "content": [...] } },
246
+ "visual": { "width": 1200, "height": 800, "url": "https://images.ctfassets.net/..." },
247
+ "backgroundColour": "Off White"
248
+ }
249
+ },
250
+ "collections": {
251
+ "FAQ": {
252
+ "mock": { "heading": "Top FAQs" },
253
+ "cards": [
254
+ { "heading": "Who is Brightline?", "body": { "json": { ... } } }
255
+ ]
256
+ }
257
+ }
258
+ }
259
+ ```
260
+
261
+ **Rules for the output:**
262
+ - Set `curatedAt` to the current ISO timestamp
263
+ - Only include types that have real CMS data
264
+ - For component mocks: include only fields present in the source data
265
+ - For collection mocks: always include `mock` and `cards`
266
+ - Rich Text body fields must be `{ "json": <Document> }` — copy as-is
267
+ - Visual fields must include `width`, `height`, AND `url` — never strip `url`
268
+ - Include `widthPercent` when present on media/externalVideo entries
269
+ - Include `otherMedia` when present — copy the full array including `url` and `widthPercent`
270
+ - Do NOT invent or modify content — use the real data from the examples file
271
+ - Include `backgroundColour` and `textColour` where the source has them
272
+ - For cards: include ALL fields present in source, including `backgroundColour`, `textColour`, `links`
273
+
274
+ ### Step 6 — Verify
275
+
276
+ After writing, briefly confirm:
277
+ - The file is valid JSON
278
+ - Component and collection counts look reasonable (non-zero)
279
+ - Key types (Hero, main collections) have entries
280
+ - Visual fields include `url` properties
281
+
282
+ ---
283
+
284
+ ## Output files
285
+
286
+ | File | Location | Committed |
287
+ |------|----------|-----------|
288
+ | `showcase-examples.json` | `src/generated/showcase-examples.json` | No (gitignored) |
289
+ | `showcase-mocks-draft.json` | `src/generated/showcase-mocks-draft.json` | No (gitignored) |
290
+ | `accepted-variants/` | `src/generated/cms-discovery/accepted-variants/` | No (gitignored) |
291
+ | `showcase-mocks.json` | `src/generated/showcase-mocks.json` | Yes |
292
+
293
+ ---
294
+
295
+ ## How the showcase uses this
296
+
297
+ The showcase uses a **two-layer model**:
298
+
299
+ 1. **Base: mock data** — `showcase-mocks.json` is merged over inline registration mocks via
300
+ `mergeShowcaseMocks()` from `@se-studio/core-ui`. Curated data takes precedence; types without
301
+ curated data fall back to their inline `mock:` in the registration.
302
+
303
+ 2. **Patch: URL params** — Any control field in the URL (e.g. `?backgroundColour=Navy`) overrides
304
+ the mock. The control bar encodes only values that differ from the mock baseline.
305
+
306
+ `?clean=true` skips mock data entirely and uses the default showcase control state only.
307
+
308
+ ---
309
+
310
+ ## External project setup (seskills)
311
+
312
+ ### Installing skills
313
+
314
+ For standalone projects not in the monorepo, install or update skills via:
315
+
316
+ ```bash
317
+ seskills sync
318
+ ```
319
+
320
+ This copies all SE Studio cursor skills (including this one) into the project's `.cursor/skills/`
321
+ directory. The `seskills` binary is provided by `@se-studio/project-build`.
322
+
323
+ ### Auto-sync on package update
324
+
325
+ Add a `postinstall` script to the project's `package.json` so skills automatically sync
326
+ whenever `pnpm install` is run (including after bumping `@se-studio/project-build`):
327
+
328
+ ```json
329
+ "postinstall": "seskills sync --if-installed"
330
+ ```
331
+
332
+ The `--if-installed` flag makes the sync a no-op if run inside the monorepo itself
333
+ (where `project-build` is a workspace package, not an installed npm package), so it
334
+ is safe to add to any project.
335
+
336
+ ### Script paths
337
+
338
+ The CLI scripts are available as installed binaries after `npm install @se-studio/project-build`.
339
+ Both `generate-showcase-data` and `generate-showcase-mocks` read `.env.local` from `process.cwd()`,
340
+ so run them from the project root.