@se-studio/skills 1.4.2 → 1.4.4

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,17 @@
1
1
  # @se-studio/skills
2
2
 
3
+ ## 1.4.4
4
+
5
+ ### Patch Changes
6
+
7
+ - Add `cms-edit asset review` to audit visual assets (filename, alt text, dimensions, file size, GIF) from the Preview API index, plus the `contentful-cms-media-review` skill for spreadsheet workflows.
8
+
9
+ ## 1.4.3
10
+
11
+ ### Patch Changes
12
+
13
+ - Document SSG guardrails (Biome, route-build-policy, validate-build-routes) in se-marketing-sites-smoke-test-setup skill.
14
+
3
15
  ## 1.4.2
4
16
 
5
17
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@se-studio/skills",
3
- "version": "1.4.2",
3
+ "version": "1.4.4",
4
4
  "description": "SE Studio agent skills for marketing site development with Contentful CMS",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,123 @@
1
+ ---
2
+ name: contentful-cms-media-review
3
+ description: "Audit CMS images, videos, and Lottie animations for filename, alt text, dimensions, size, and GIF issues using cms-edit, then produce an Excel report of failures."
4
+ ---
5
+
6
+ # Skill: contentful-cms — Media Review
7
+
8
+ Use this skill to audit **visual assets** in a Contentful space and produce a spreadsheet of assets that fail quality checks.
9
+
10
+ ## When to use
11
+
12
+ - Cleaning up CMS media before or after a migration
13
+ - Finding oversized images, missing alt text, or animated GIFs
14
+ - Brightline, SE Studio, or any site with `cms-edit` configured
15
+
16
+ ## Prerequisites
17
+
18
+ - `cms-edit` configured for the target space (`cms-edit health`)
19
+ - `CONTENTFUL_PREVIEW_ACCESS_TOKEN` set (default index uses Preview API / drafts)
20
+ - Run `cms-edit index sync` before the review if the index is stale
21
+
22
+ ## Workflow
23
+
24
+ ### Step 1 — Sync the index
25
+
26
+ ```bash
27
+ cms-edit index sync --space <space-key>
28
+ ```
29
+
30
+ Use `--published` only when you want published/delivery content only.
31
+
32
+ ### Step 2 — Run the review
33
+
34
+ ```bash
35
+ cms-edit --json asset review --space <space-key> > /tmp/media-review.json
36
+ ```
37
+
38
+ Optional flags:
39
+
40
+ | Flag | Purpose |
41
+ |------|---------|
42
+ | `--include-passing` | Include assets with no failures |
43
+ | `--max-width 2000` | Max raster/video width (default 2000) |
44
+ | `--max-image-kb 800` | Max image file size |
45
+ | `--max-video-mb 8` | Max video file size |
46
+ | `--max-lottie-kb 400` | Max Lottie JSON size |
47
+ | `--published` | Use Delivery API index |
48
+
49
+ Exit code **1** when any asset has failing issues (useful for CI).
50
+
51
+ ### Step 3 — Build the Excel report
52
+
53
+ Create a workbook with **failures only** (default JSON output). Use the **xlsx** skill.
54
+
55
+ **Sheet: Issues** — one row per failing asset
56
+
57
+ | Column | Source field |
58
+ |--------|----------------|
59
+ | Asset ID | `assets[].id` |
60
+ | Contentful URL | `assets[].contentfulUrl` |
61
+ | CDN URL | `assets[].url` |
62
+ | Title | `assets[].title` |
63
+ | Filename | `assets[].fileName` |
64
+ | Alt text | `assets[].description` |
65
+ | Content type | `assets[].contentType` |
66
+ | Width | `assets[].width` |
67
+ | Height | `assets[].height` |
68
+ | Size (KB) | `assets[].sizeKb` |
69
+ | Media wrappers | `assets[].mediaEntryCount` |
70
+ | Issue codes | `assets[].issues[].code` (comma-separated fails) |
71
+ | Issue details | `assets[].issues[].message` (semicolon-separated) |
72
+ | Updated | `assets[].updatedAt` |
73
+
74
+ **Sheet: Summary**
75
+
76
+ - `space`, `spaceId`, `environment`, `generated`, `preview`
77
+ - `total`, `failingCount`, `warningCount`
78
+ - `issueCounts` breakdown
79
+ - Thresholds from `thresholds` object
80
+
81
+ Default output path: `docs/media-review-<space>-<date>.xlsx` in the app directory.
82
+
83
+ ### Step 4 — Present findings
84
+
85
+ Summarize for the user:
86
+
87
+ - Total assets scanned vs failures
88
+ - Top issue types (from `issueCounts`)
89
+ - Quick wins (missing alt, animated GIFs, obvious stock filenames)
90
+ - Note: `no_media_wrapper` is a **warning** — asset may still be used via rich text or be genuinely unused
91
+
92
+ ## What is checked
93
+
94
+ | Check | Issue code | Severity |
95
+ |-------|------------|----------|
96
+ | Descriptive filename | `bad_filename` | fail |
97
+ | Alt text present | `missing_alt` | fail |
98
+ | Alt text quality | `weak_alt` | fail |
99
+ | Width ≤ max (not SVG/Lottie) | `oversized_width` | fail |
100
+ | File size limits | `oversized_file` | fail / warn |
101
+ | Animated GIF | `animated_gif` | fail |
102
+ | No media wrapper in index | `no_media_wrapper` | warn |
103
+
104
+ ## Brightline example
105
+
106
+ ```bash
107
+ cd apps/brightline-website
108
+ cms-edit index sync --space brightline
109
+ cms-edit --json asset review --space brightline > /tmp/brightline-media-review.json
110
+ ```
111
+
112
+ Space key: `brightline` (space ID `96gdpqkm7elu`).
113
+
114
+ ## Phase 2 (not yet in cms-edit)
115
+
116
+ Production page URLs and definitive unused-asset detection need CMA `links_to_asset` or a production crawl. When available, add columns `prodUrls` and `cmsUsages` to the spreadsheet.
117
+
118
+ ## Related
119
+
120
+ - `contentful-cms-alt-text-audit` — per-page alt text fixes
121
+ - `contentful-cms-image-guide` — full image inventory with AI descriptions
122
+ - `cms-edit asset audit` — missing alt only (narrower, faster)
123
+ - `cms-edit asset set-description <id> "alt"` — fix alt text on an asset
@@ -29,6 +29,7 @@ Preview / `DRAFT_ONLY` Contentful access in local dev is expected and not a smok
29
29
  | `pnpm smoke-test:cache` | `build` + `start` + double-pass `x-nextjs-cache` verify |
30
30
  | `pnpm smoke-test:deploy-check` | Post-build gate: `start` (no rebuild) + functional smoke — use in Vercel `buildCommand` |
31
31
  | `pnpm smoke-test:live` | Live deployment URL smoke — GitHub Action + Vercel Deployment Checks |
32
+ | `pnpm revalidation-test` | `build` + `start` + revalidation matrix (warm → invalidate → cold → warm); see `revalidation.cases.json` and [CONTENTFUL_WEBHOOK_REVALIDATION.md](../../../../docs/CONTENTFUL_WEBHOOK_REVALIDATION.md) |
32
33
 
33
34
  Example `package.json` entries:
34
35
 
@@ -39,7 +40,8 @@ Example `package.json` entries:
39
40
  "smoke-test:audit": "SMOKE_TEST_AUDIT_CACHE_LOGS=true smoke-test-one 3012",
40
41
  "smoke-test:cache": "SMOKE_TEST_SERVER_SCRIPT=start SMOKE_TEST_VERIFY_CACHE=true smoke-test-one 3012",
41
42
  "smoke-test:deploy-check": "smoke-test-deploy-check",
42
- "smoke-test:live": "smoke-test-live"
43
+ "smoke-test:live": "smoke-test-live",
44
+ "revalidation-test": "node ../../scripts/revalidation-matrix-test.mjs 3012"
43
45
  ```
44
46
 
45
47
  **Vercel build gate** — append to root `vercel.json` so failed smoke fails the build before deploy:
@@ -161,8 +163,9 @@ Read (do not guess from constants alone):
161
163
  | `page` | Top-level CMS pages (not home, not article trees) | 2 |
162
164
  | `article-type-index` | e.g. `/work/`, `/blog/` | 1 |
163
165
  | `article` | Nested article/case-study URLs | 2 |
164
- | `tag` / `tags-index` | If in sitemap and enabled | 1–2 each |
165
- | `person` / `people-listing` | If in sitemap and enabled | 1–2 each |
166
+ | `tag` / `tags-index` | If routes exist (sitemap optional) | 1–2 each |
167
+ | `person` / `people-listing` | If routes exist (sitemap optional) | 1–2 each |
168
+ | `not-found` | Non-existent path | 1 (`expectHtmlStatus: 404`) |
166
169
 
167
170
  **Exclude** obvious non-prod slugs: `tmp-*`, draft pages, unless intentionally tested.
168
171
 
@@ -187,11 +190,20 @@ curl -sI "http://localhost:<port>/some-path.md"
187
190
  "port": 3012,
188
191
  "cases": [
189
192
  { "category": "home", "label": "Home", "path": "/", "expectMarkdown": true },
190
- { "category": "page", "label": "About", "path": "/about/", "expectMarkdown": true }
193
+ { "category": "page", "label": "About", "path": "/about/", "expectMarkdown": true },
194
+ {
195
+ "category": "not-found",
196
+ "label": "404 — unknown path",
197
+ "path": "/smoke-test-does-not-exist/",
198
+ "expectMarkdown": false,
199
+ "expectHtmlStatus": 404
200
+ }
191
201
  ]
192
202
  }
193
203
  ```
194
204
 
205
+ `expectHtmlStatus` requires `@se-studio/site-check@^2.7.2`. Omit for normal 2xx HTML checks.
206
+
195
207
  Optional `cmsIntegrity` (local only — see section below):
196
208
 
197
209
  ```json
@@ -270,8 +282,67 @@ Remove legacy `smoke-test:validate-links`, `smoke-test-preload.cjs`, and bespoke
270
282
  3. Update `@se-studio/site-check` to `^2.6.1` when adopting integrity; otherwise `^2.1.2` minimum.
271
283
  4. Follow workflow above to create `smoke.cases.json`.
272
284
 
285
+ ## SSG guardrails (all marketing sites)
286
+
287
+ Prevent `DYNAMIC_SERVER_USAGE` / production 500s on on-demand SSG routes (`●` in `next build`). Three layers — lint, build policy, production smoke.
288
+
289
+ ### 1. Biome — ban dynamic request APIs in SSG surfaces
290
+
291
+ Copy the override block from `@se-studio/site-check/templates/biome-ssg-guardrails.override.json` into the app `biome.json` `overrides` array.
292
+
293
+ Restricts request-time Next.js APIs in:
294
+
295
+ - `src/app/layout.tsx`
296
+ - `src/app/(cms-routes)/**`
297
+ - `src/project/**`
298
+
299
+ Banned: `next/headers` (headers, cookies, draftMode), `connection` from `next/server`, `unstable_noStore` from `next/cache`. `unstable_cache` and other `next/cache` exports remain allowed.
300
+
301
+ Dynamic by design (no restriction): `(cms-dev)`, `preview`, `api`, `middleware.ts`.
302
+
303
+ Lint is necessary but not sufficient — route policy + production smoke still required.
304
+
305
+ Runs on every `pnpm check` / `pnpm validate`.
306
+
307
+ ### 2. Build route policy — no surprise `ƒ` on CMS segment routes
308
+
309
+ Requires `@se-studio/site-check` **^2.7.0**.
310
+
311
+ Commit `route-build-policy.json` beside `smoke.cases.json`. Start from `@se-studio/site-check/templates/route-build-policy.example.json` and adjust route segments to match the app.
312
+
313
+ ```json
314
+ {
315
+ "allowedDynamic": ["/", "/articles", "/tags", "/people", "/_not-found"],
316
+ "mustBeSsgOrStatic": [
317
+ "/[level1]",
318
+ "/[level1]/[...slugs]",
319
+ "/articles/[articleType]",
320
+ "/articles/[articleType]/[...slugs]"
321
+ ]
322
+ }
323
+ ```
324
+
325
+ - `mustBeSsgOrStatic` — must be `●` or `○` in `next build`, never `ƒ`.
326
+ - `allowedDynamic` — documents known `ƒ` routes; fail if a new `ƒ` appears without updating the policy.
327
+
328
+ **Scripts:**
329
+
330
+ ```json
331
+ "validate:routes": "validate-build-routes",
332
+ "validate": "pnpm check && pnpm type-check && pnpm validate:routes"
333
+ ```
334
+
335
+ `validate-build-routes` runs `pnpm build` and compares the Route (app) table to the policy. `smoke-test-one` also validates after build when `route-build-policy.json` exists (`SMOKE_TEST_VALIDATE_ROUTES=false` to skip).
336
+
337
+ ### 3. Production smoke (runtime safety net)
338
+
339
+ `pnpm smoke-test:cache` / `smoke-test:deploy-check` catches breakage lint/policy miss. Keep mandatory in CI / Vercel build gates.
340
+
341
+ **HubSpot bootstrap:** use `createHubSpotBootstrapScript()` with no args in root layout — not `headers()` + middleware pathname.
342
+
273
343
  ## Reference
274
344
 
275
345
  - HTTP + integrity example: `apps/example-empty/smoke.cases.json` and `scripts/smoke-test-run.ts` (monorepo).
276
- - Package API: `runStaticSmokeTest`, `runStaticSmokeTestWithIntegrity`, `runPreviewStaticSmokeTest`, `auditCacheLogs` from `@se-studio/site-check/smoke-test`.
346
+ - SSG guardrails reference app: `apps/example-empty/route-build-policy.json` and `biome.json` overrides.
347
+ - Package API: `runStaticSmokeTest`, `runStaticSmokeTestWithIntegrity`, `runPreviewStaticSmokeTest`, `auditCacheLogs`, `validateBuildRoutesFromBuildOutput` from `@se-studio/site-check/smoke-test`.
277
348
  - CMS integrity types: `@se-studio/site-check/cms-integrity`.
@@ -0,0 +1,134 @@
1
+ ---
2
+ name: vercel-logs-audit
3
+ description: "Pull and triage Vercel error logs across SE Studio marketing sites (develop + production). Use when checking production errors, monitoring Vercel runtime issues, or running incremental log audits."
4
+ ---
5
+
6
+ # Vercel logs audit
7
+
8
+ Cross-project error monitoring for the 7 marketing sites in `docs/vercel-logs/projects.registry.json`.
9
+
10
+ **Requires:** Vercel CLI (`vercel`), authenticated via `vercel login` or `VERCEL_TOKEN` in `.env.local`.
11
+
12
+ **Output:** Generated under `docs/vercel-logs/` — **do not commit** except `projects.registry.json` and `README.md`.
13
+
14
+ ---
15
+
16
+ ## Quick start
17
+
18
+ From repo root:
19
+
20
+ ```bash
21
+ # Full pipeline: pull → filter → analyze
22
+ pnpm vercel-logs
23
+
24
+ # Subset
25
+ pnpm vercel-logs --sites se2026,brightline --envs develop
26
+
27
+ # Re-backfill 7 days (resets checkpoints for selected targets)
28
+ pnpm vercel-logs --full
29
+
30
+ # Plan fetches without calling Vercel
31
+ pnpm vercel-logs:pull --dry-run
32
+ ```
33
+
34
+ Individual steps:
35
+
36
+ ```bash
37
+ pnpm vercel-logs:pull
38
+ pnpm vercel-logs:filter
39
+ pnpm vercel-logs:analyze
40
+ ```
41
+
42
+ ---
43
+
44
+ ## Incremental behaviour
45
+
46
+ | Run | Window |
47
+ |-----|--------|
48
+ | First run (no `state.json`) | Last **7 days** per site × environment |
49
+ | Subsequent runs | Since last checkpoint minus 5-minute overlap |
50
+ | `--full` | Clears checkpoints and re-backfills 7 days |
51
+ | `--since 2d` | Override start time for selected pull |
52
+
53
+ Checkpoints live in `docs/vercel-logs/state.json` (gitignored).
54
+
55
+ If a pull hits the Vercel log limit, the checkpoint is set to the **oldest fetched timestamp** (not `now`) so the next run can backfill older entries.
56
+
57
+ ---
58
+
59
+ ## Pipeline steps
60
+
61
+ | Step | Input | Output |
62
+ |------|-------|--------|
63
+ | `pull` | Vercel CLI | `raw/<runId>/.../logs.jsonl` (raw JSON) |
64
+ | `filter` | Raw JSONL + manifests | `errors/<runId>.jsonl` (normalized) |
65
+ | `analyze` | Filtered JSONL | `reports/<runId>-summary.md` |
66
+
67
+ ---
68
+
69
+ ## Teams and projects
70
+
71
+ The registry maps each site to a Vercel project and team scope:
72
+
73
+ | Team | Sites |
74
+ |------|-------|
75
+ | `se-studio` | se2026, om1, pedestal, headwater |
76
+ | `brightline` | brightline, brightlifekids |
77
+ | `engineeringpointmes-projects` | pointme |
78
+
79
+ Your Vercel account must have access to all three teams. Verify with `vercel whoami`.
80
+
81
+ ---
82
+
83
+ ## Reading results
84
+
85
+ After `pnpm vercel-logs:analyze`, open:
86
+
87
+ `docs/vercel-logs/reports/<runId>-summary.md`
88
+
89
+ Triage in this order:
90
+
91
+ 1. **By site × environment** — which deploy surface is failing?
92
+ 2. **Top messages** — group identical errors (often one root cause)
93
+ 3. **Status codes** — 5xx vs application-level errors
94
+ 4. **Sample paths** — affected routes
95
+ 5. **Changes since previous run** — new regressions
96
+
97
+ Filtered entries: `docs/vercel-logs/errors/<runId>.jsonl`
98
+
99
+ Exit code **2** from analyze means errors were found (useful for future CI).
100
+
101
+ ---
102
+
103
+ ## What counts as an error
104
+
105
+ Pull uses two CLI passes per target:
106
+
107
+ - Log level `error` or `fatal`
108
+ - HTTP status `500`–`508`
109
+
110
+ Filter normalizes raw JSON and keeps entries matching:
111
+
112
+ - Log level `error` or `fatal`
113
+ - HTTP status `5xx`
114
+
115
+ Optional: `pnpm vercel-logs:filter --include-warnings`
116
+
117
+ ---
118
+
119
+ ## Troubleshooting
120
+
121
+ | Problem | Fix |
122
+ |---------|-----|
123
+ | `vercel logs failed` / auth error | `vercel login` or set `VERCEL_TOKEN` |
124
+ | Wrong team / project not found | Check `team` in `projects.registry.json`; use `vercel projects ls --scope <team>` |
125
+ | No targets matched | Check `--sites` / `--envs` keys match registry `key` fields |
126
+ | Empty report after errors expected | Widen with `--full` or `--since 7d`; confirm develop uses `--branch develop` |
127
+
128
+ ---
129
+
130
+ ## Maintenance
131
+
132
+ - Registry: `docs/vercel-logs/projects.registry.json`
133
+ - Scripts: `scripts/vercel-logs/`
134
+ - Tests: `pnpm vercel-logs:test`