@se-studio/skills 1.7.10 → 1.7.12
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 +12 -0
- package/package.json +1 -1
- package/references/agent-session/projects.registry.json +1 -1
- package/references/lockfile-sync/README.md +5 -1
- package/skills/coding-discipline/SKILL.md +411 -0
- package/skills/contentful-cms-core/SKILL.md +4 -8
- package/skills/se-marketing-sites-cms-routes-and-appshared/SKILL.md +10 -0
- package/skills/se-marketing-sites-create-page/SKILL.md +12 -3
- package/skills/se-marketing-sites-styling-system/SKILL.md +14 -0
- package/skills/site-workflows-deps-update/SKILL.md +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @se-studio/skills
|
|
2
2
|
|
|
3
|
+
## 1.7.12
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 76bc91c: Document Darwin hoisted-frozen install (clean tree, no win32 architectures) and require `next typegen` before `tsc` for typed-route PageProps.
|
|
8
|
+
|
|
9
|
+
## 1.7.11
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 1e65f12: Generated theme lookups return `undefined` (and warn when `LOG_CMS=1`) instead of throwing on unknown CMS names. `lookupTextStyleClassName` is now `string | undefined` — treat the result as optional, do not concatenate it. Tailwind `@source` should scan core-ui `src/**/*.tsx` plus `dist/**/*.js`, not the whole `dist` tree.
|
|
14
|
+
|
|
3
15
|
## 1.7.10
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
"contentfulSpaceId": "bkkox68g99rn",
|
|
114
114
|
"hostedMcp": "cms-edit-highlander",
|
|
115
115
|
"validate": "pnpm check && pnpm type-check && pnpm validate:routes",
|
|
116
|
-
"notes": "2026 stack is live on Vercel (develop preview, main production alias). Public DNS is still Bond/Netlify highlanderhealth.com until cutover. cms-edit
|
|
116
|
+
"notes": "2026 stack is live on Vercel (develop preview, main production alias). Public DNS is still Bond/Netlify highlanderhealth.com until cutover. Hosted cms-edit: https://highlander.content.se.studio (Git production branch main)."
|
|
117
117
|
}
|
|
118
118
|
]
|
|
119
119
|
}
|
|
@@ -34,9 +34,12 @@ See [`validate-workflow-snippet.yml`](validate-workflow-snippet.yml). Customer m
|
|
|
34
34
|
|
|
35
35
|
cms-edit Vercel and `check-lockfile-sync.sh` (full) run `pnpm install --frozen-lockfile --config.node-linker=hoisted`. That path requires lockfile snapshots for **linux-x64/glibc** optional binaries even when you generate the lockfile on macOS.
|
|
36
36
|
|
|
37
|
-
Every consumer `pnpm-workspace.yaml` must include `supportedArchitectures` (current + linux, current + x64, current + glibc). Canonical copy: skill `site-workflows-deps-update` Step 3. After adding it, run `pnpm install` (not frozen) so the lockfile gains those snapshots, then verify
|
|
37
|
+
Every consumer `pnpm-workspace.yaml` must include `supportedArchitectures` (current + linux, current + x64, current + glibc). Canonical copy: skill `site-workflows-deps-update` Step 3. After adding it, run `pnpm install` (not frozen) so the lockfile gains those snapshots, then verify.
|
|
38
|
+
|
|
39
|
+
**Local Darwin:** never run hoisted frozen on top of an isolated `node_modules`. That yields `ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY` for `@next/swc-win32-*`. Next lists every platform SWC as optional; we do **not** add `win32` to `supportedArchitectures`. Ubuntu CI `lockfile-hoisted` is authoritative.
|
|
38
40
|
|
|
39
41
|
```bash
|
|
42
|
+
rm -rf node_modules
|
|
40
43
|
pnpm install --frozen-lockfile --config.node-linker=hoisted
|
|
41
44
|
pnpm install # restore isolated linker for local workspace packages
|
|
42
45
|
```
|
|
@@ -62,5 +65,6 @@ Apply the full stack when touching deps or CI in each repo:
|
|
|
62
65
|
| se-website-2026 | yes | develop | yes | yes | yes |
|
|
63
66
|
| pointme | yes | develop | yes | yes | yes |
|
|
64
67
|
| hsd (`develop-hsd-website`; prod human-only: `production-hsd-website`) | yes | develop (+ `extended-port`) | yes | yes | yes |
|
|
68
|
+
| highlander-health-website-2026 | yes | develop | yes | yes | yes |
|
|
65
69
|
|
|
66
70
|
Update this table as repos adopt the pattern. Every cms-edit host `pnpm-workspace.yaml` must include `supportedArchitectures` (current + linux, x64, glibc).
|
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: coding-discipline
|
|
3
|
+
description: Behavioral guidelines to reduce common LLM coding mistakes in se-core-product. Use when writing or reviewing application code, not for CMS copy or one-line fixes.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# CLAUDE.md - AI Assistant Guide
|
|
7
|
+
Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed.
|
|
8
|
+
|
|
9
|
+
**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
|
|
10
|
+
|
|
11
|
+
## 1. Think Before Coding
|
|
12
|
+
|
|
13
|
+
**Don't assume. Don't hide confusion. Surface tradeoffs.**
|
|
14
|
+
|
|
15
|
+
Before implementing:
|
|
16
|
+
|
|
17
|
+
- State your assumptions explicitly. If uncertain, ask.
|
|
18
|
+
- If multiple interpretations exist, present them - don't pick silently.
|
|
19
|
+
- If a simpler approach exists, say so. Push back when warranted.
|
|
20
|
+
- If something is unclear, stop. Name what's confusing. Ask.
|
|
21
|
+
|
|
22
|
+
## 2. Simplicity First
|
|
23
|
+
|
|
24
|
+
**Minimum code that solves the problem. Nothing speculative.**
|
|
25
|
+
|
|
26
|
+
- No features beyond what was asked.
|
|
27
|
+
- No abstractions for single-use code.
|
|
28
|
+
- No "flexibility" or "configurability" that wasn't requested.
|
|
29
|
+
- No error handling for impossible scenarios.
|
|
30
|
+
- If you write 200 lines and it could be 50, rewrite it.
|
|
31
|
+
|
|
32
|
+
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
|
|
33
|
+
|
|
34
|
+
## 3. Surgical Changes
|
|
35
|
+
|
|
36
|
+
**Touch only what you must. Clean up only your own mess.**
|
|
37
|
+
|
|
38
|
+
When editing existing code:
|
|
39
|
+
|
|
40
|
+
- Don't "improve" adjacent code, comments, or formatting.
|
|
41
|
+
- Don't refactor things that aren't broken.
|
|
42
|
+
- Match existing style, even if you'd do it differently.
|
|
43
|
+
- If you notice unrelated dead code, mention it - don't delete it.
|
|
44
|
+
|
|
45
|
+
When your changes create orphans:
|
|
46
|
+
|
|
47
|
+
- Remove imports/variables/functions that YOUR changes made unused.
|
|
48
|
+
- Don't remove pre-existing dead code unless asked.
|
|
49
|
+
|
|
50
|
+
The test: Every changed line should trace directly to the user's request.
|
|
51
|
+
|
|
52
|
+
## 4. Goal-Driven Execution
|
|
53
|
+
|
|
54
|
+
**Define success criteria. Loop until verified.**
|
|
55
|
+
|
|
56
|
+
Transform tasks into verifiable goals:
|
|
57
|
+
|
|
58
|
+
- "Add validation" → "Write tests for invalid inputs, then make them pass"
|
|
59
|
+
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
|
|
60
|
+
- "Refactor X" → "Ensure tests pass before and after"
|
|
61
|
+
|
|
62
|
+
For multi-step tasks, state a brief plan:
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
This file provides guidance for AI assistants (Claude, Cursor, Copilot) working with this codebase.
|
|
67
|
+
|
|
68
|
+
## Quick Start
|
|
69
|
+
|
|
70
|
+
This is a **TypeScript monorepo** for building **Next.js 16 marketing sites** with **Contentful CMS**. Customer sites are on Next 16.3; `@se-studio/*` packages still declare peer `next` `>=15.5.0 <17` for older installs.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pnpm install # Install dependencies
|
|
74
|
+
pnpm build # Build all packages (required before dev)
|
|
75
|
+
pnpm dev # Run development mode
|
|
76
|
+
pnpm test # Run tests
|
|
77
|
+
pnpm format # Format code with Biome
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Command Reference
|
|
81
|
+
|
|
82
|
+
Use these commands from the **repository root**:
|
|
83
|
+
|
|
84
|
+
| Task | Command |
|
|
85
|
+
|------|---------|
|
|
86
|
+
| Install dependencies | `pnpm install` |
|
|
87
|
+
| Build all packages (required before dev) | `pnpm build` |
|
|
88
|
+
| Run dev (packages) | `pnpm dev` |
|
|
89
|
+
| Run a specific app | `pnpm --filter example-empty dev` (https://example.localhost), `pnpm --filter cms-edit-host dev` (https://cms-edit.localhost — template only; SE Studio prod: `se-website-2026/cms-edit/host`). Customer sites run from their own repos — see `docs/RELATED_PROJECTS.md`. |
|
|
90
|
+
| Run all apps (except ignored) | `pnpm dev:apps` or `cd apps && pnpm dev` (ignore via `SITEMAP_VALIDATE_IGNORE=true` in app’s `.env.local`) |
|
|
91
|
+
| Sitemap validate (prod vs portless `pnpm dev`) | Per app: `pnpm sitemap-validate` in app dir (needs `portless service install` + `pnpm dev`). Bulk: `pnpm sitemap-validate:all`. Set `SITEMAP_PROD_URL` in `.env.local`; build site-check first. |
|
|
92
|
+
| Smoke test (local) | Per app: `pnpm smoke-test` (`dev:dev` + HTTP on localhost port), `pnpm smoke-test:audit`, `pnpm smoke-test:cache` (`start:dev`). Uses `smoke.cases.json`; no Contentful. Regenerates: skill `se-marketing-sites-smoke-test-setup`. Set `SMOKE_TEST_IGNORE=true` to skip. |
|
|
93
|
+
| Smoke test (preview) | Per app: `pnpm smoke-test:preview` — curated HTTP checks against Vercel preview via `runPreviewStaticSmokeTest` (no local server). Requires `PREVIEW_SITE_URL` and `VERCEL_PROTECTION_BYPASS_TOKEN` in `.env.local`. Same `smoke.cases.json` as local smoke. |
|
|
94
|
+
| Run all tests | `pnpm test` |
|
|
95
|
+
| Test a specific package | `pnpm --filter @se-studio/core-ui test` |
|
|
96
|
+
| Format code | `pnpm format` |
|
|
97
|
+
| Type-check (packages + apps) | `pnpm type-check` |
|
|
98
|
+
| Lint | `pnpm lint` |
|
|
99
|
+
| Full validation (circular imports, skills, format, type-check) | `pnpm validate` |
|
|
100
|
+
| Validate skill frontmatter (Claude Code) | `pnpm skills:validate` |
|
|
101
|
+
| Sync skills to `.agents/skills/` | `pnpm skills:sync` |
|
|
102
|
+
| Worktree `.env.local` (canonical store, seed, audit, 1Password backup) | `pnpm --dir ~/source/se/se-core-product local-env seed --project <key>` — see `packages/skills/references/agent-session/local-env.md` |
|
|
103
|
+
|
|
104
|
+
## Guides for Common App Tasks
|
|
105
|
+
|
|
106
|
+
Step-by-step guides are available as **Cursor skills**. Use them when creating or changing app-level features so you follow the right patterns and don't miss steps (e.g. registration).
|
|
107
|
+
|
|
108
|
+
| Task | Skill / doc |
|
|
109
|
+
|------|-------------|
|
|
110
|
+
| **Agent session** (core vs customer, parallel features, handoff) | `.agents/skills/site-workflows-agent-session/SKILL.md`, `docs/WORKFLOW.md` |
|
|
111
|
+
| **Worktree `.env.local`** | `packages/skills/references/agent-session/local-env.md`, `pnpm --dir ~/source/se/se-core-product local-env seed --project <key>` |
|
|
112
|
+
| Create a new **CMS component** (app) | `.agents/skills/se-marketing-sites-create-component/SKILL.md` |
|
|
113
|
+
| Create a new **collection** (app) | `.agents/skills/se-marketing-sites-create-collection/SKILL.md` |
|
|
114
|
+
| Create a new **page** or route | `.agents/skills/se-marketing-sites-create-page/SKILL.md`, `.agents/skills/se-marketing-sites-cms-routes-and-appshared/SKILL.md` |
|
|
115
|
+
| **Register** components/collections | `.agents/skills/se-marketing-sites-register-cms-features/SKILL.md` |
|
|
116
|
+
**Editor-managed redirects** (rebuild pattern + migrations) | `.agents/skills/se-marketing-sites-redirects/SKILL.md` |
|
|
117
|
+
| **Media** (images, videos, animations) | `.agents/skills/se-marketing-sites-handling-media/SKILL.md` |
|
|
118
|
+
| **Styling** (typography, colours, grids) | `.agents/skills/se-marketing-sites-styling-system/SKILL.md` |
|
|
119
|
+
| **Lib/CMS structure** (cms.ts, registrations) | `.agents/skills/se-marketing-sites-lib-cms-structure/SKILL.md` |
|
|
120
|
+
| **Sync Contentful schema** to core (golden diff, migration runbook) | `.agents/skills/contentful-cms-sync-schema/SKILL.md`, `docs/schema-audit/` |
|
|
121
|
+
| **Edit CMS content** (pages, components, articles) | `.agents/skills/contentful-cms-core/SKILL.md` |
|
|
122
|
+
| **CMS guidelines — orchestration** (full **fresh** clean regen vs **sync** incremental + stale removal vs **merge-only**) | `.agents/skills/contentful-cms-update-cms-guidelines/SKILL.md` — if the user is vague (“regenerate guidelines”), confirm **fresh** vs **sync** before acting |
|
|
123
|
+
| **CMS guidelines — fragments** (full / single / merge-only; prefer **update-cms-guidelines** for true total regeneration) | `.agents/skills/contentful-cms-generate-cms-guidelines/SKILL.md` |
|
|
124
|
+
|
|
125
|
+
**Skills — canonical source and sync:** All SE Studio skills live in **`packages/skills/skills/`** (the `@se-studio/skills` npm package). **In the monorepo**, edit sources there, run **`pnpm skills:validate`**, then **`pnpm skills:sync`** to copy into **`.agents/skills`**. The repo root keeps **`.cursor/skills`** as a **symlink** to **`.agents/skills`**. **External projects** install skills via the `skills` CLI: `npx skills add @se-studio/skills`. Do not edit skills under **`.agents/skills`** by hand. Frontmatter rules: [`packages/skills/README.md`](packages/skills/README.md).
|
|
126
|
+
|
|
127
|
+
See also **docs/AI_QUICK_REFERENCE.md** for a short task → doc/skill index.
|
|
128
|
+
|
|
129
|
+
## Package Dependency Graph
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
┌──────────────────────────────────────────────────────────────────────────────┐
|
|
133
|
+
│ APPS │
|
|
134
|
+
│ ┌─────────────────┐ ┌─────────────────┐ (+ customer sites in separate repos) │
|
|
135
|
+
│ │ example-empty │ │ cms-edit-host │ see docs/RELATED_PROJECTS.md │
|
|
136
|
+
│ │ example.localhost│ │ cms-edit.localhost│ │
|
|
137
|
+
│ └────────┬────────┘ └────────┬────────┘ │
|
|
138
|
+
│ └────────────────────┘ │
|
|
139
|
+
│ ▼ │
|
|
140
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
141
|
+
│ PACKAGES │
|
|
142
|
+
│ │
|
|
143
|
+
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
144
|
+
│ │ core-ui │ │
|
|
145
|
+
│ │ React components, CMS rendering, analytics │ │
|
|
146
|
+
│ └─────────────────────────┬────────────────────────────────┘ │
|
|
147
|
+
│ │ │
|
|
148
|
+
│ ┌───────────────────┼────────────────────┐ │
|
|
149
|
+
│ ▼ ▼ ▼ │
|
|
150
|
+
│ ┌─────────────────┐ ┌─────────────────────┐ ┌──────────────┐ ┌────────────────┐ │
|
|
151
|
+
│ │ contentful- │ │ project-build │ │ ab-testing │ │ skills │ │
|
|
152
|
+
│ │ rest-api │ │ Build tools, │ │ A/B testing │ │ Agent skills │ │
|
|
153
|
+
│ │ CDA/CPA client │ │ Tailwind, CLI │ │ framework │ │ (@se-studio/ │ │
|
|
154
|
+
│ └────────┬────────┘ └─────────────────────┘ └──────┬───────┘ │ skills) │ │
|
|
155
|
+
│ │ │ └────────────────┘ │
|
|
156
|
+
│ │ │ │
|
|
157
|
+
│ │ │ │
|
|
158
|
+
│ ▼ │ │
|
|
159
|
+
│ ┌───────────────────────────────────────────────────────────▼─────┐ │
|
|
160
|
+
│ │ core-data-types │ │
|
|
161
|
+
│ │ TypeScript types, interfaces, type guards │ │
|
|
162
|
+
│ │ (No runtime dependencies - pure types) │ │
|
|
163
|
+
│ └─────────────────────────────────────────────────────────┘ │
|
|
164
|
+
│ │
|
|
165
|
+
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
166
|
+
│ │ markdown-renderer │ │
|
|
167
|
+
│ │ Utilities for converting Contentful to Markdown │ │
|
|
168
|
+
│ │ (Depends on contentful-rest-api, core-data-types) │ │
|
|
169
|
+
│ └─────────────────────────────────────────────────────────┘ │
|
|
170
|
+
│ │
|
|
171
|
+
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
172
|
+
│ │ search │ │
|
|
173
|
+
│ │ AI-powered site search with Upstash Search │ │
|
|
174
|
+
│ │ (Depends on contentful-rest-api, core-data-types, │ │
|
|
175
|
+
│ │ markdown-renderer, @upstash/search) │ │
|
|
176
|
+
│ └─────────────────────────────────────────────────────────┘ │
|
|
177
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Key Files to Understand
|
|
181
|
+
|
|
182
|
+
| File | Purpose |
|
|
183
|
+
|------|---------|
|
|
184
|
+
| `packages/core-data-types/src/index.ts` | All shared type exports |
|
|
185
|
+
| `packages/contentful-rest-api/src/api.ts` | Contentful API functions |
|
|
186
|
+
| `packages/contentful-rest-api/src/converters/` | Entry-to-model converters |
|
|
187
|
+
| `packages/core-ui/src/components/CmsContent.tsx` | Main CMS renderer |
|
|
188
|
+
| `packages/core-ui/CMS_INFRASTRUCTURE.md` | CMS system documentation |
|
|
189
|
+
| `packages/ab-testing/src/middleware/index.ts` | A/B testing middleware handler |
|
|
190
|
+
| `packages/search/src/types.ts` | Search types and configuration |
|
|
191
|
+
| `packages/search/src/indexing/rebuild.ts` | Full search index rebuild |
|
|
192
|
+
| `packages/search/src/webhook/handler.ts` | Webhook-driven search index updates |
|
|
193
|
+
| `packages/markdown-renderer/src/MarkdownConverter.ts` | Contentful to Markdown logic |
|
|
194
|
+
| `turbo.json` | Build pipeline configuration |
|
|
195
|
+
| `biome.json` | Code style rules |
|
|
196
|
+
|
|
197
|
+
## Common Tasks
|
|
198
|
+
|
|
199
|
+
### Adding a New Content Type
|
|
200
|
+
|
|
201
|
+
1. **Define types** in `core-data-types/src/types/`
|
|
202
|
+
2. **Export types** from `core-data-types/src/index.ts`
|
|
203
|
+
3. **Create converter** in `contentful-rest-api/src/converters/`
|
|
204
|
+
4. **Add API function** in `contentful-rest-api/src/api.ts`
|
|
205
|
+
5. **Create renderer** in the app's components
|
|
206
|
+
|
|
207
|
+
### Adding a New UI Component (in core-ui package)
|
|
208
|
+
|
|
209
|
+
For **shared package** components (used across apps):
|
|
210
|
+
|
|
211
|
+
1. Create component in `packages/core-ui/src/components/`
|
|
212
|
+
2. Export from `packages/core-ui/src/index.ts`
|
|
213
|
+
3. Add tests in `packages/core-ui/src/__tests__/`
|
|
214
|
+
4. Document props with JSDoc
|
|
215
|
+
|
|
216
|
+
### Adding a New CMS Component (in an app)
|
|
217
|
+
|
|
218
|
+
For **app-level** components that render CMS content (Hero, CTA, etc.):
|
|
219
|
+
|
|
220
|
+
1. Use the **create-component** skill: `.agents/skills/se-marketing-sites-create-component/SKILL.md`
|
|
221
|
+
2. Follow the four-layer pattern (Core, Wrapper, Renderer, Registration) and the template there
|
|
222
|
+
3. **Register** the component in the app's `src/lib/registrations.ts` (add to `componentRegistrationsList`). The **register-cms-features** skill describes this; missing registrations cause a type error in `cms.ts` via `buildComponentRecord`.
|
|
223
|
+
4. Ensure the `name` in `defineComponent` matches the CMS `componentType` exactly (name is defined once in the registration).
|
|
224
|
+
|
|
225
|
+
### Heading hierarchy
|
|
226
|
+
|
|
227
|
+
Use `Element` from `getSizingInformation(index)` for the main heading of each component/collection; use `ChildElement` for headings of items inside a collection. Render **preHeading** and **postHeading** as `<p>` with a typography class, never as heading tags. See `.cursorrules` (Heading and pre/post rules) and the create-component skill.
|
|
228
|
+
|
|
229
|
+
### Fetching Contentful Data
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
import { contentfulPageRest } from '@se-studio/contentful-rest-api';
|
|
233
|
+
|
|
234
|
+
const page = await contentfulPageRest(
|
|
235
|
+
{
|
|
236
|
+
spaceId: process.env.CONTENTFUL_SPACE_ID!,
|
|
237
|
+
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN!,
|
|
238
|
+
environment: 'master',
|
|
239
|
+
},
|
|
240
|
+
'home', // slug
|
|
241
|
+
{
|
|
242
|
+
preview: false,
|
|
243
|
+
cache: { tags: ['page#home'], revalidate: 3600 }
|
|
244
|
+
}
|
|
245
|
+
);
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Rendering CMS Content
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
import { CmsContent } from '@se-studio/core-ui';
|
|
252
|
+
|
|
253
|
+
<CmsContent
|
|
254
|
+
content={page}
|
|
255
|
+
config={rendererConfig}
|
|
256
|
+
context={{ pageLink: { slug: page.slug, title: page.title } }}
|
|
257
|
+
/>
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## TypeScript Patterns
|
|
261
|
+
|
|
262
|
+
### Type Guards (Runtime Type Checking)
|
|
263
|
+
```typescript
|
|
264
|
+
import { isPage, isComponent, isImage } from '@se-studio/core-data-types';
|
|
265
|
+
|
|
266
|
+
if (isPage(content)) {
|
|
267
|
+
console.log(content.slug); // TypeScript knows content is IBasePage
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Strict Index Access
|
|
272
|
+
The project uses `noUncheckedIndexedAccess`, so array/object index access returns `T | undefined`:
|
|
273
|
+
```typescript
|
|
274
|
+
const items = ['a', 'b', 'c'];
|
|
275
|
+
const first = items[0]; // string | undefined - must handle undefined!
|
|
276
|
+
if (first) {
|
|
277
|
+
console.log(first.toUpperCase()); // Now safe
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Component Props Pattern
|
|
282
|
+
```typescript
|
|
283
|
+
// Define explicit prop types for presentational components
|
|
284
|
+
export interface HeroProps {
|
|
285
|
+
heading: string | null;
|
|
286
|
+
body: unknown;
|
|
287
|
+
visual: IResponsiveVisual | undefined;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Extract props in the renderer
|
|
291
|
+
export const HeroRenderer: ComponentRenderer<ProjectConfig> = ({ information }) => {
|
|
292
|
+
return <HeroCore
|
|
293
|
+
heading={information.heading}
|
|
294
|
+
body={information.body}
|
|
295
|
+
visual={information.visual}
|
|
296
|
+
/>;
|
|
297
|
+
};
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## Code Style Requirements
|
|
301
|
+
|
|
302
|
+
- **Styling**: Tailwind only. Do not use CSS modules (`*.module.css`). Use Tailwind classes or `src/app/globals.css` for shared styles.
|
|
303
|
+
- **Biome** for linting/formatting (not ESLint/Prettier)
|
|
304
|
+
- Single quotes, semicolons, trailing commas
|
|
305
|
+
- `import type` for type-only imports
|
|
306
|
+
- `node:` protocol for Node.js built-ins
|
|
307
|
+
- 100 character line width
|
|
308
|
+
|
|
309
|
+
Run `pnpm format` before committing.
|
|
310
|
+
|
|
311
|
+
## Testing
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
pnpm test # Run all tests
|
|
315
|
+
pnpm --filter @se-studio/core-ui test # Test specific package
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Tests use **Vitest** and live in `__tests__/` directories.
|
|
319
|
+
|
|
320
|
+
## Environment Setup
|
|
321
|
+
|
|
322
|
+
**Worktrees:** `.env.local` is gitignored and will be missing after `git worktree add`. Seed from `~/source/local-env` with `pnpm --dir ~/source/se/se-core-product local-env seed --project <key>`. Do not invent a stub file or symlink at develop. Details: `packages/skills/references/agent-session/local-env.md`.
|
|
323
|
+
|
|
324
|
+
Apps need these environment variables in `.env.local`:
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
CONTENTFUL_SPACE_ID=your-space-id
|
|
328
|
+
CONTENTFUL_ACCESS_TOKEN=your-delivery-token
|
|
329
|
+
CONTENTFUL_PREVIEW_ACCESS_TOKEN=your-preview-token
|
|
330
|
+
CONTENTFUL_ENVIRONMENT=master
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Optional: Verbose logging
|
|
334
|
+
|
|
335
|
+
Uncomment in `.env.local` (or `.env.example`) to enable detailed console output from specific subsystems (silent by default):
|
|
336
|
+
|
|
337
|
+
```bash
|
|
338
|
+
# LOG_CMS=1 # Contentful fetching, resolution, revalidation, unknown component/collection types, unknown colour/text-style names
|
|
339
|
+
# LOG_SVG_SPRITE=1 # SVG icon processing and sprite generation warnings
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Server vs Client: cms-server
|
|
343
|
+
|
|
344
|
+
- **Do not import `cms-server` (or `@/lib/cms-server`) in files that have `'use client'`.** That module is server-only (Contentful config). Client components must not import it; pass server-rendered content as `children` from server components instead.
|
|
345
|
+
- **Do not import preview/fetch helpers from `cms-server` in components or collections.** That causes circular dependencies (registrations → components → cms-server → registrations). Use `getPreviewFieldProps(rendererConfig, id, fieldId)` and `getPreviewResponsiveVisualFieldProps(rendererConfig, id, 'visual', 'mobileVisual')` from `@se-studio/core-ui`, and `rendererConfig?.fetchHelpers` for data. Pass `previewHelpers` to `<Section>` and `rendererConfig` to `<SectionLinks>`.
|
|
346
|
+
- Each app's `cms-server.ts` has `import 'server-only'`. If a client component imports it, the build will fail with a clear "cannot be imported from a Client Component" error. See `docs/SERVER_CLIENT_BOUNDARIES.md`.
|
|
347
|
+
|
|
348
|
+
## Troubleshooting
|
|
349
|
+
|
|
350
|
+
| Problem | Solution |
|
|
351
|
+
|---------|----------|
|
|
352
|
+
| "Cannot find module '@se-studio/...'" | Run `pnpm build` from root |
|
|
353
|
+
| "Cannot find name 'PageProps'" in example-empty | `typedRoutes` defines `PageProps` in `.next/types/routes.d.ts`. `pnpm --filter example-empty type-check` runs `next typegen` first. Delete a stale `apps/example-empty/.tsbuildinfo` if tsc still misses it. |
|
|
354
|
+
| Port already in use / `.localhost` not on 443 | Install `portless service install`. `pnpm dev` uses named URLs. Smoke still uses localhost:3014 via `dev:dev`. Customer sites: `docs/RELATED_PROJECTS.md` |
|
|
355
|
+
| Contentful 401 error | Check access token and space ID |
|
|
356
|
+
| "This module cannot be imported from a Client Component" | You imported `cms-server` (or a file that uses it) in a `'use client'` file. Move content rendering to a server component and pass it as `children`. |
|
|
357
|
+
|
|
358
|
+
## Keeping Documentation Updated
|
|
359
|
+
|
|
360
|
+
When making changes, update the relevant documentation:
|
|
361
|
+
|
|
362
|
+
| Change | Update |
|
|
363
|
+
|--------|--------|
|
|
364
|
+
| New content type | `specs/CONTENT_MODEL.md` |
|
|
365
|
+
| New component pattern | `specs/COMPONENT_PATTERNS.md` |
|
|
366
|
+
| New API function | `packages/contentful-rest-api/README.md` and `packages/contentful-rest-api/docs/llms.md` |
|
|
367
|
+
| New package | `README.md`, `CLAUDE.md`, `docs/ARCHITECTURE.md` |
|
|
368
|
+
| New app | `README.md`, `CLAUDE.md`, `docs/DEVELOPMENT.md`, `docs/ARCHITECTURE.md`, port list |
|
|
369
|
+
| New env variable | All `.env.example` files |
|
|
370
|
+
| Workflow change | `docs/DEVELOPMENT.md`, `docs/WORKFLOW.md` |
|
|
371
|
+
| Worktree / local env handling | `packages/skills/references/agent-session/local-env.md` |
|
|
372
|
+
|
|
373
|
+
**Documentation locations:**
|
|
374
|
+
- `docs/` - Architecture and development guides
|
|
375
|
+
- `specs/` - Content model and component specifications
|
|
376
|
+
- `packages/*/README.md` - Package-specific docs (human-facing)
|
|
377
|
+
- `packages/*/docs/llms.md` - LLM-optimised quick-reference per package (also published to npm)
|
|
378
|
+
- `.cursorrules` - AI conventions (update if patterns change)
|
|
379
|
+
|
|
380
|
+
## Links
|
|
381
|
+
|
|
382
|
+
- [Related Projects](./docs/RELATED_PROJECTS.md) – Client and studio projects that consume these packages (Contentful space IDs, environments, repo paths)
|
|
383
|
+
- [AI Quick Reference](./docs/AI_QUICK_REFERENCE.md) – Task → doc/skill index for common app tasks
|
|
384
|
+
- [GitHub issues implementation status](./docs/GITHUB_ISSUES_IMPLEMENTATION_STATUS.md) – closed issues mapped to code; open backlog summary
|
|
385
|
+
- [Content Structure Guide](./docs/CONTENT_STRUCTURE_GUIDE.md) – Content author guide for CMS workflows
|
|
386
|
+
- [Server vs Client Boundaries](./docs/SERVER_CLIENT_BOUNDARIES.md) – cms-server rule and pattern
|
|
387
|
+
- [core-ui package export boundaries](./packages/core-ui/docs/PACKAGE_EXPORT_BOUNDARIES.md) – main vs `/server` entry; prevent client bundle CMS leaks
|
|
388
|
+
- [Contentful webhook revalidation](./docs/CONTENTFUL_WEBHOOK_REVALIDATION.md) – Contentful webhooks for cache revalidation
|
|
389
|
+
- [CMS Infrastructure Guide](./packages/core-ui/CMS_INFRASTRUCTURE.md)
|
|
390
|
+
- [Contentful REST API Docs](./packages/contentful-rest-api/README.md) — LLM ref: [docs/llms.md](./packages/contentful-rest-api/docs/llms.md)
|
|
391
|
+
- [Core Data Types Docs](./packages/core-data-types/README.md) — LLM ref: [docs/llms.md](./packages/core-data-types/docs/llms.md)
|
|
392
|
+
- [Core UI Docs](./packages/core-ui/README.md) — LLM ref: [docs/llms.md](./packages/core-ui/docs/llms.md)
|
|
393
|
+
- [A/B Testing Docs](./packages/ab-testing/README.md) — LLM ref: [docs/llms.md](./packages/ab-testing/docs/llms.md)
|
|
394
|
+
- [HubSpot Docs](./packages/hubspot/README.md) — LLM ref: [docs/llms.md](./packages/hubspot/docs/llms.md)
|
|
395
|
+
- [Search Docs](./packages/search/README.md) — LLM ref: [docs/llms.md](./packages/search/docs/llms.md)
|
|
396
|
+
- [Markdown Renderer Docs](./packages/markdown-renderer/README.md) — LLM ref: [docs/llms.md](./packages/markdown-renderer/docs/llms.md)
|
|
397
|
+
- [Contentful CMS CLI Docs](./packages/contentful-cms/README.md)
|
|
398
|
+
|
|
399
|
+
### For consuming projects (in node_modules)
|
|
400
|
+
|
|
401
|
+
When working in a project that installs `@se-studio` packages, tell LLMs to read the relevant doc in `node_modules/`:
|
|
402
|
+
|
|
403
|
+
- `node_modules/@se-studio/contentful-rest-api/docs/llms.md`
|
|
404
|
+
- `node_modules/@se-studio/core-data-types/docs/llms.md`
|
|
405
|
+
- `node_modules/@se-studio/core-ui/docs/llms.md`
|
|
406
|
+
- `node_modules/@se-studio/ab-testing/docs/llms.md`
|
|
407
|
+
- `node_modules/@se-studio/hubspot/docs/llms.md`
|
|
408
|
+
- `node_modules/@se-studio/search/docs/llms.md`
|
|
409
|
+
- `node_modules/@se-studio/markdown-renderer/docs/llms.md`
|
|
410
|
+
|
|
411
|
+
These are the source of truth — training data is outdated. See the example app `AGENTS.md` files for the ready-to-copy block.
|
|
@@ -31,7 +31,8 @@ Use this skill when you need to read or edit content in Contentful using **hoste
|
|
|
31
31
|
All `save` operations create **draft** versions. A human must review and publish in Contentful.
|
|
32
32
|
|
|
33
33
|
**Assets:** Upload **is** supported (`asset upload`). Default workflow: **search and reuse** existing assets first (`index sync` → `asset search`). When uploading:
|
|
34
|
-
- **
|
|
34
|
+
- **Raster/video:** `cms_edit_request_staged_upload` → curl → `cms_edit_media` prepare (`stagedUploadIds`) → wait → catalog → import with real alt. Never `asset upload --staged/--url/--base64` for jpeg/png/webp/gif/mp4.
|
|
35
|
+
- **SVG/PDF/Lottie:** `cms_edit_request_staged_upload` → curl → `asset upload --staged` (never `--base64`)
|
|
35
36
|
- Use a **sensible fileName** and **descriptive title** (and alt text where the site uses it)
|
|
36
37
|
- Avoid oversized images — width over **2000px** is usually wasteful for web (exact limits vary by project; check `asset audit` / media review guidance)
|
|
37
38
|
- Prefer `--if-exists-by-filename` to avoid duplicates
|
|
@@ -371,13 +372,8 @@ cms-edit asset search "hero background"
|
|
|
371
372
|
cms-edit asset search --filename istockphoto-123.jpg
|
|
372
373
|
cms-edit asset search --filename-match 'istockphoto-.*'
|
|
373
374
|
|
|
374
|
-
#
|
|
375
|
-
|
|
376
|
-
cms-edit asset upload --base64 "$B64" --mime image/png --file-name poster.png
|
|
377
|
-
|
|
378
|
-
# Hosted MCP — always staged upload for binary files
|
|
379
|
-
# cms_edit_request_staged_upload → curl uploadUrl → consumeArgs
|
|
380
|
-
# cms_edit ["asset", "upload", "--staged", "<uploadId>", "--mime", "image/png", "--file-name", "photo.png"]
|
|
375
|
+
# Raster/video: cms_edit_media prepare → wait → catalog → import (video: import only).
|
|
376
|
+
# SVG/PDF/Lottie: cms_edit_request_staged_upload → curl → asset upload --staged
|
|
381
377
|
|
|
382
378
|
# Upload and create a Media wrapper in one step (--media-name defaults to asset title)
|
|
383
379
|
cms-edit asset upload ./figure.png --with-media --media-position Middle
|
|
@@ -36,6 +36,16 @@ export default function CmsRoutesLayout({ children }: { children: React.ReactNod
|
|
|
36
36
|
|
|
37
37
|
Base paths come from `@/lib/constants` (e.g. ARTICLES_BASE = `/learning-hub` or `/articles`).
|
|
38
38
|
|
|
39
|
+
## Type-check (typed routes)
|
|
40
|
+
|
|
41
|
+
Next 16 puts `PageProps` in `.next/types`. A cold worktree has no `.next`, so bare `tsc --noEmit` fails on CMS `page.tsx`. Marketing apps must use:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
"type-check": "next typegen && tsc --noEmit --incremental false"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Keep committed `next-env.d.ts` importing `./.next/types/routes.d.ts` and `./.next/types/root-params.d.ts` — not `.next/dev/types` (that path is from `next dev`). Do not commit Next rewriting those imports to double quotes if Biome uses single quotes.
|
|
48
|
+
|
|
39
49
|
## appShared Modules
|
|
40
50
|
|
|
41
51
|
Route files are thin wrappers. Logic lives in `src/appShared/`:
|
|
@@ -55,7 +55,9 @@ Route files are thin wrappers. Data-fetching and rendering logic lives in `src/a
|
|
|
55
55
|
Each route page extracts params and delegates to the appropriate appShared function:
|
|
56
56
|
|
|
57
57
|
```typescript
|
|
58
|
+
import { joinCmsPathSegments } from '@se-studio/core-ui';
|
|
58
59
|
import type { ResolvingMetadata } from 'next';
|
|
60
|
+
import { notFound } from 'next/navigation';
|
|
59
61
|
import { generatePage, generatePageMetadata } from '@/appShared/pageShared';
|
|
60
62
|
|
|
61
63
|
export function generateStaticParams() {
|
|
@@ -64,8 +66,11 @@ export function generateStaticParams() {
|
|
|
64
66
|
|
|
65
67
|
async function extractDetails(props: PageProps<'/[level1]'>) {
|
|
66
68
|
const params = await props.params;
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
+
const slug = joinCmsPathSegments(params.level1);
|
|
70
|
+
if (!slug) {
|
|
71
|
+
notFound();
|
|
72
|
+
}
|
|
73
|
+
return { slug, path: `/${slug}/` };
|
|
69
74
|
}
|
|
70
75
|
|
|
71
76
|
export async function generateMetadata(props: PageProps<'/[level1]'>, parent: ResolvingMetadata) {
|
|
@@ -134,6 +139,7 @@ Direct visits to a variant's own CMS URL (normal `[...slugs]` route) continue to
|
|
|
134
139
|
|
|
135
140
|
```typescript
|
|
136
141
|
import { findCanonicalPath } from '@se-studio/ab-testing';
|
|
142
|
+
import { joinCmsPathSegments } from '@se-studio/core-ui';
|
|
137
143
|
import type { ResolvingMetadata } from 'next';
|
|
138
144
|
import { notFound } from 'next/navigation';
|
|
139
145
|
import { testsByPath } from '@/generated/abTests';
|
|
@@ -155,7 +161,10 @@ export function generateStaticParams() {
|
|
|
155
161
|
|
|
156
162
|
async function extractDetails(props: PageProps<'/page-test/[...slugs]'>) {
|
|
157
163
|
const params = await props.params;
|
|
158
|
-
const slug = params.slugs
|
|
164
|
+
const slug = joinCmsPathSegments(params.slugs);
|
|
165
|
+
if (!slug) {
|
|
166
|
+
notFound();
|
|
167
|
+
}
|
|
159
168
|
return { slug, path: `/page-test/${slug}/` };
|
|
160
169
|
}
|
|
161
170
|
|
|
@@ -17,6 +17,19 @@ The `tailwind.config.json` file in your app's root directory defines the followi
|
|
|
17
17
|
|
|
18
18
|
These configurations are processed by the build system to generate utility classes and TypeScript helpers.
|
|
19
19
|
|
|
20
|
+
## Tailwind `@source` (core-ui)
|
|
21
|
+
|
|
22
|
+
In `src/app/globals.css`:
|
|
23
|
+
|
|
24
|
+
```css
|
|
25
|
+
@source "../../node_modules/@se-studio/core-ui/src/**/*.tsx";
|
|
26
|
+
@source "../../node_modules/@se-studio/core-ui/dist/**/*.js";
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Do **not** `@source` the whole `dist` tree — that includes `*.map` source maps and makes `next dest` heavier. Scan `src/**/*.tsx` so monorepo workspace links pick up showcase/media-library classes without a core-ui rebuild; on npm that glob is a no-op (`src` is not published). Scan `dist/**/*.js` for the compiled package.
|
|
30
|
+
|
|
31
|
+
Unknown CMS colour / text-style names must not throw; generated lookups return `undefined`. Set `LOG_CMS=1` to log misspelled names.
|
|
32
|
+
|
|
20
33
|
## Typography
|
|
21
34
|
|
|
22
35
|
Typography utility classes (e.g., `h1`, `h2`, `p1`, `p2`) are automatically generated from the `fontTable` in `tailwind.config.json`.
|
|
@@ -66,6 +79,7 @@ Colors are defined in `colorOptions` within `tailwind.config.json`. The build sy
|
|
|
66
79
|
// Returns "bg-primary-blue text-white" (if White is opposite of Primary Blue)
|
|
67
80
|
lookupColourClassNames('Primary Blue')
|
|
68
81
|
```
|
|
82
|
+
Unknown CMS names return `undefined` (no page crash). Set `LOG_CMS=1` to warn.
|
|
69
83
|
|
|
70
84
|
## Grid System
|
|
71
85
|
|
|
@@ -208,12 +208,16 @@ rg '"node"' package.json apps/*/package.json packages/*/package.json 2>/dev/null
|
|
|
208
208
|
|
|
209
209
|
## Step 6 — Validate (unified for all repos)
|
|
210
210
|
|
|
211
|
-
If the registry project has `cms-edit/host` in `pnpm-workspace.yaml`, run the frozen hoisted install check first:
|
|
211
|
+
If the registry project has `cms-edit/host` in `pnpm-workspace.yaml`, run the frozen hoisted install check first. **Do not** mix an isolated-linker `node_modules` with hoisted frozen — on Darwin that fails with `ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY` for `@next/swc-win32-*` (Next lists every platform SWC as optional; `supportedArchitectures` does not include win32). Ubuntu CI `lockfile-hoisted` is the source of truth. Locally:
|
|
212
212
|
|
|
213
213
|
```bash
|
|
214
|
+
rm -rf node_modules
|
|
214
215
|
pnpm install --frozen-lockfile --config.node-linker=hoisted
|
|
216
|
+
pnpm install # restore isolated linker for day-to-day work
|
|
215
217
|
```
|
|
216
218
|
|
|
219
|
+
Do **not** add `win32` to `supportedArchitectures` to make Mac hoisted pass.
|
|
220
|
+
|
|
217
221
|
Always run the patches check **once**, then the project validate from the registry:
|
|
218
222
|
|
|
219
223
|
```bash
|
|
@@ -284,6 +288,7 @@ See [`references/lockfile-sync/README.md`](../../references/lockfile-sync/README
|
|
|
284
288
|
| Issue | Action |
|
|
285
289
|
|-------|--------|
|
|
286
290
|
| `ERR_PNPM_OUTDATED_LOCKFILE` on frozen/hoisted install | `package.json` and `pnpm-workspace.yaml` overrides out of sync with lockfile — re-run `pnpm update` for the package and align `overrides` |
|
|
291
|
+
| `ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY` (`@next/swc-win32-*`) on Darwin hoisted frozen | Isolated `node_modules` mixed with hoisted linker — `rm -rf node_modules`, hoisted frozen, then `pnpm install`. Do not add win32 to `supportedArchitectures`. Trust Ubuntu `lockfile-hoisted` |
|
|
287
292
|
| `ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY` (`rollup-linux-*`, Sharp, etc.) on hoisted frozen install | Missing `supportedArchitectures` (current + linux, x64, glibc) — add the Step 3 block, run `pnpm install`, then re-verify hoisted |
|
|
288
293
|
| `next` or `@types/node` still outdated to wrong major | Re-run overrides + safety re-pin; check `pnpm-workspace.yaml` overrides |
|
|
289
294
|
| Non-`@se-studio` package still outdated after update | Likely within 24h of npm publish — wait and re-run; **do not** bypass `minimumReleaseAge` |
|