@ericrisco/rsc 3.1.3 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/README.md +8 -6
  2. package/manifest.json +43 -69
  3. package/package.json +6 -1
  4. package/scripts/design-upstream.js +92 -0
  5. package/scripts/doctor.js +0 -24
  6. package/scripts/lib/default-skill-floor.js +5 -1
  7. package/scripts/lib/domains.js +2 -2
  8. package/scripts/lib/recommend.js +1 -3
  9. package/scripts/lib/retired-skills.js +7 -1
  10. package/skills/ai-media/SKILL.md +2 -2
  11. package/skills/design/IMPECCABLE-LICENSE.txt +191 -0
  12. package/skills/design/SKILL.md +79 -328
  13. package/skills/design/THIRD_PARTY.md +26 -0
  14. package/skills/design/engine/LICENSE +21 -0
  15. package/skills/design/engine/RUN.md +515 -0
  16. package/skills/design/engine/UPSTREAM.json +4 -0
  17. package/skills/design/engine/assets/app.css +216 -0
  18. package/skills/design/engine/assets/base.css +105 -0
  19. package/skills/design/engine/assets/base.js +37 -0
  20. package/skills/design/engine/assets/charts.js +617 -0
  21. package/skills/design/engine/assets/scenes/stage.css +28 -0
  22. package/skills/design/engine/assets/scenes/stage.js +133 -0
  23. package/skills/design/engine/library/app.json +1327 -0
  24. package/skills/design/engine/library/directions.json +1078 -0
  25. package/skills/design/engine/library/heroes.json +1086 -0
  26. package/skills/design/engine/library/motions.json +92 -0
  27. package/skills/design/engine/library/palettes.json +1485 -0
  28. package/skills/design/engine/library/photos.json +7240 -0
  29. package/skills/design/engine/library/sections.json +4429 -0
  30. package/skills/design/engine/library/sites.json +8350 -0
  31. package/skills/design/engine/library/type.json +2024 -0
  32. package/skills/design/engine/references/app-screens.md +1199 -0
  33. package/skills/design/engine/references/colour.md +141 -0
  34. package/skills/design/engine/references/copy.md +177 -0
  35. package/skills/design/engine/references/critique.md +182 -0
  36. package/skills/design/engine/references/imagery.md +791 -0
  37. package/skills/design/engine/references/layout.md +292 -0
  38. package/skills/design/engine/references/motion.md +980 -0
  39. package/skills/design/engine/references/research.md +356 -0
  40. package/skills/design/engine/references/scenes.md +174 -0
  41. package/skills/design/engine/references/tells.md +115 -0
  42. package/skills/design/engine/references/type.md +170 -0
  43. package/skills/design/engine/scripts/assets.mjs +865 -0
  44. package/skills/design/engine/scripts/draw.mjs +584 -0
  45. package/skills/design/engine/scripts/inspect.mjs +303 -0
  46. package/skills/design/engine/scripts/lib/app-draw.mjs +203 -0
  47. package/skills/design/engine/scripts/lib/cdp.mjs +248 -0
  48. package/skills/design/engine/scripts/lib/probe.mjs +909 -0
  49. package/skills/design/engine/scripts/lib/rules.mjs +243 -0
  50. package/skills/design/engine/scripts/lint.mjs +119 -0
  51. package/skills/design/engine/scripts/scenes.mjs +124 -0
  52. package/skills/design/evals/cases.yaml +35 -46
  53. package/skills/design/references/design-md.md +49 -0
  54. package/skills/design/references/finish-review.md +46 -0
  55. package/skills/design/references/modes.md +51 -0
  56. package/skills/expo/SKILL.md +3 -4
  57. package/skills/marketing/references/landing-copy.md +2 -2
  58. package/skills/react/SKILL.md +2 -2
  59. package/skills/react-native/SKILL.md +3 -3
  60. package/skills/scroll-film/SKILL.md +220 -0
  61. package/skills/scroll-film/assets/page-template.html +128 -0
  62. package/skills/scroll-film/assets/scrub-engine.js +352 -0
  63. package/skills/scroll-film/evals/README.md +26 -0
  64. package/skills/scroll-film/evals/cases.yaml +81 -0
  65. package/skills/scroll-film/references/chain.md +218 -0
  66. package/skills/scroll-film/references/engine.md +132 -0
  67. package/skills/scroll-film/scripts/verify.sh +158 -0
  68. package/skills/swift-ios/SKILL.md +1 -1
  69. package/skills/ui-engineering/SKILL.md +3 -4
  70. package/skills/ui-engineering/evals/cases.yaml +2 -2
  71. package/skills/variant-explorer/SKILL.md +4 -4
  72. package/skills/variant-explorer/evals/cases.yaml +1 -1
  73. package/targets/clone-bootstrap.mjs +1 -1
  74. package/scripts/lib/design-identity.js +0 -117
  75. package/scripts/lib/starting-point.js +0 -314
  76. package/skills/design/evals/README.md +0 -53
  77. package/skills/design/references/ai-tells.md +0 -75
  78. package/skills/design/references/brand-grounding.md +0 -197
  79. package/skills/design/references/copywriting-frameworks.md +0 -138
  80. package/skills/design/references/design-systems.md +0 -49
  81. package/skills/design/references/inspiration-sources.md +0 -96
  82. package/skills/design/references/landing-anatomy-and-cro.md +0 -202
  83. package/skills/design/references/motion-and-interaction.md +0 -228
  84. package/skills/design/references/research-method.md +0 -154
  85. package/skills/design/references/signature-and-craft.md +0 -148
  86. package/skills/design/references/starting-point.md +0 -75
  87. package/skills/design/references/trends-2026.md +0 -80
  88. package/skills/design/references/visual-system.md +0 -237
  89. package/skills/design/scripts/verify.sh +0 -357
  90. package/skills/design-dna/PROMPT.md +0 -211
  91. package/skills/design-dna/SKILL.md +0 -162
  92. package/skills/design-dna/design-dna.schema.json +0 -230
  93. package/skills/design-dna/evals/README.md +0 -64
  94. package/skills/design-dna/evals/cases.yaml +0 -68
  95. package/skills/design-dna/references/method.md +0 -436
  96. package/skills/design-dna/scripts/emit.py +0 -164
  97. package/skills/design-dna/templates/PROMPT.md.tmpl +0 -42
  98. package/skills/design-dna/templates/SKILL.md.tmpl +0 -42
  99. package/skills/design-dna/templates/check.py.tmpl +0 -143
  100. package/skills/design-loop/SKILL.md +0 -160
  101. package/skills/design-loop/evals/README.md +0 -56
  102. package/skills/design-loop/evals/cases.yaml +0 -60
  103. package/skills/motion-craft/SKILL.md +0 -125
  104. package/skills/motion-craft/evals/cases.yaml +0 -38
  105. package/skills/motion-craft/scripts/verify.sh +0 -75
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  [![npm](https://img.shields.io/npm/v/@ericrisco/rsc?color=63d68a&labelColor=12161c&label=npm)](https://www.npmjs.com/package/@ericrisco/rsc)
10
10
  [![downloads](https://img.shields.io/npm/dm/@ericrisco/rsc?color=63d68a&labelColor=12161c&label=downloads)](https://www.npmjs.com/package/@ericrisco/rsc)
11
- [![skills](https://img.shields.io/badge/skills-271-63d68a?labelColor=12161c)](#the-catalog)
11
+ [![skills](https://img.shields.io/badge/skills-269-63d68a?labelColor=12161c)](#the-catalog)
12
12
  [![license](https://img.shields.io/badge/license-MIT-63d68a?labelColor=12161c)](LICENSE)
13
13
  [![stars](https://img.shields.io/github/stars/ericrisco/rsc-harness?color=63d68a&labelColor=12161c)](https://github.com/ericrisco/rsc-harness/stargazers)
14
14
 
@@ -60,7 +60,7 @@ The model is the brain your agent rents, and you already have it. rsc adds the r
60
60
  | --- | --- | --- |
61
61
  | **Memory** | `02-DOCS/` | A wiki the agent reads before it works and keeps up to date while it works. |
62
62
  | **Hands** | `01-TOOLS/` | One folder per tool, with a test that proves the connection works. |
63
- | **Trade** | skills | Only the skills your project needs: 271 available, a handful installed. |
63
+ | **Trade** | skills | Only the skills your project needs: 269 available, a handful installed. |
64
64
  | **Reflexes** | hooks | Rules that hold even when the agent forgets them mid-session. |
65
65
 
66
66
  ### Why documentation comes first
@@ -468,7 +468,7 @@ snapshots the project first, so `rsc restore latest` undoes a bad update.
468
468
 
469
469
  ## The catalog
470
470
 
471
- 271 skills, grouped by what you are trying to do. Click any skill to read its `SKILL.md`. It fires on
471
+ 269 skills, grouped by what you are trying to do. Click any skill to read its `SKILL.md`. It fires on
472
472
  its own when a task matches.
473
473
 
474
474
  ### 🧭 Core & control plane
@@ -561,11 +561,11 @@ whether, what and which platform.
561
561
 
562
562
  ### 🌀 Motion & interface craft
563
563
 
564
- [motion-craft](skills/motion-craft/) · [ui-engineering](skills/ui-engineering/) · [variant-explorer](skills/variant-explorer/)
564
+ [ui-engineering](skills/ui-engineering/) · [variant-explorer](skills/variant-explorer/) · [scroll-film](skills/scroll-film/)
565
565
 
566
566
  ### 🎨 Design & content craft
567
567
 
568
- [design-loop](skills/design-loop/) · [design](skills/design/) · [design-dna](skills/design-dna/) · [presentations](skills/presentations/) · [course-storytelling](skills/course-storytelling/) · [course-builder](skills/course-builder/) · [technical-writing](skills/technical-writing/) · [translation-l10n](skills/translation-l10n/)
568
+ [design](skills/design/) · [presentations](skills/presentations/) · [course-storytelling](skills/course-storytelling/) · [course-builder](skills/course-builder/) · [technical-writing](skills/technical-writing/) · [translation-l10n](skills/translation-l10n/)
569
569
 
570
570
  ### 🧠 Knowledge & meta
571
571
 
@@ -626,8 +626,10 @@ Most of this catalog is written here. These are not, and they keep their author'
626
626
  | Skills | Source | License |
627
627
  | --- | --- | --- |
628
628
  | `design-eng` · `animate` · `animate-expo` · `review-animations` · `improve-animations` · `find-animation-opportunities` · `animation-vocabulary` · `apple-design` · `prototype` · `pick-ui-library` · `write-swift` · `ask-sonner` | [emilkowalski/skills](https://github.com/emilkowalski/skills) by Emil Kowalski, commit `d23d7f8` | MIT |
629
- | part of the AI-tell corpus in `design` | [Leonxlnx/taste-skill](https://github.com/Leonxlnx/taste-skill) | MIT |
629
+ | the engine of `design` (`skills/design/engine/`: direction draw, browser inspector, study library), shipped unmodified | [Leonxlnx/taste-skill](https://github.com/Leonxlnx/taste-skill) v2.1.4 | MIT |
630
+ | the four modes, the fresh review and the `DESIGN.md` record in `design` (text rewritten) | [pbakaus/impeccable](https://github.com/pbakaus/impeccable) by Paul Bakaus, `skill-v4.7.0` | Apache-2.0 |
630
631
  | the answer-first habits in `orient` | [ayghri/i-have-adhd](https://github.com/ayghri/i-have-adhd) | MIT |
632
+ | the scroll-scrubbed camera flight, the frame handoff between clips and the phone fixes behind `scroll-film` (prose and engine rewritten) | [oso95/scroll-world](https://github.com/oso95/scroll-world) | MIT |
631
633
 
632
634
  Adapted, not mirrored: each carries rsc frontmatter, routing evals and hand-offs to its siblings. The
633
635
  craft bar in them is the original author's.
package/manifest.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
- "version": "3.1.3",
2
+ "version": "3.3.0",
3
3
  "counts": {
4
- "skills": 271,
4
+ "skills": 269,
5
5
  "agents": 34,
6
6
  "commands": 53,
7
7
  "fixedCommands": 20
@@ -124,7 +124,8 @@
124
124
  "fal",
125
125
  "replicate",
126
126
  "remotion-video",
127
- "video-shorts"
127
+ "video-shorts",
128
+ "scroll-film"
128
129
  ],
129
130
  "profiles": []
130
131
  },
@@ -1310,62 +1311,29 @@
1310
1311
  },
1311
1312
  {
1312
1313
  "id": "design",
1313
- "description": "Use when designing or refreshing a web UI or landing page — visual concept, type/color/spacing/motion tokens, composition, rescuing a UI that reads AI-generic, or a graded design review. Brand-grounded and research-first; ships Tailwind v4 + Next.js 15 under WCAG 2.2 AA and Core Web Vitals budgets. NOT the words on the page (that is `marketing`), NOT the App Router build (that is `nextjs`).",
1314
+ "description": "Use when designing, redesigning, restyling or reviewing a web page or app screen (landing, product site, portfolio, dashboard, docs) or when a UI looks generic or AI-made. Deals a researched direction, takes the idea from the subject, uses real pictures, proves it in a browser. NOT the words (landing-copy), NOT component state (ui-engineering).",
1314
1315
  "tags": [
1315
1316
  "design",
1316
- "ux",
1317
1317
  "ui",
1318
+ "ux",
1318
1319
  "landing",
1319
- "conversion"
1320
+ "dashboard",
1321
+ "frontend",
1322
+ "taste"
1320
1323
  ],
1321
1324
  "recommends": [
1322
- "design-loop",
1323
- "design-dna",
1324
- "nextjs",
1325
- "marketing"
1325
+ "landing-copy",
1326
+ "ui-engineering",
1327
+ "accessibility",
1328
+ "brand-identity",
1329
+ "nextjs"
1326
1330
  ],
1327
1331
  "profiles": [
1332
+ "minimal",
1333
+ "core",
1328
1334
  "full"
1329
1335
  ]
1330
1336
  },
1331
- {
1332
- "id": "design-dna",
1333
- "description": "Use when a design has won and its look must survive: codify it into a permanent style skill with measured ratios, bans and tests that can fail, then build new pieces in that exact look. Fires on 'turn this into a skill', 'keep the same style', 'my output drifted'. NOT finding the winner (that is `design-loop`), NOT the words (that is `marketing`).",
1334
- "tags": [
1335
- "design",
1336
- "style",
1337
- "dna",
1338
- "codify",
1339
- "skill-factory",
1340
- "consistency"
1341
- ],
1342
- "recommends": [
1343
- "design-loop",
1344
- "design",
1345
- "nextjs",
1346
- "marketing"
1347
- ],
1348
- "profiles": []
1349
- },
1350
- {
1351
- "id": "design-loop",
1352
- "description": "Use when something must be built well and there is a real reference it should beat: turn what makes the reference good into measured mechanisms, then run a builder and three fresh-context critics until all three pass. Fires on 'design loop', 'make it as good as <site>', 'it looks generic'. NOT keeping an already-won look (that is `design-dna`).",
1353
- "tags": [
1354
- "design",
1355
- "critique",
1356
- "loop",
1357
- "reference",
1358
- "quality",
1359
- "convergence"
1360
- ],
1361
- "recommends": [
1362
- "design",
1363
- "design-dna",
1364
- "marketing",
1365
- "nextjs"
1366
- ],
1367
- "profiles": []
1368
- },
1369
1337
  {
1370
1338
  "id": "digitalocean",
1371
1339
  "description": "Use when deploying or operating a workload on DigitalOcean — Droplet vs App Platform vs Functions, doctl, app spec YAML, Managed Postgres/MySQL/Valkey on the VPC, S3-compatible Spaces + CDN. NOT host-agnostic CI/CD or rollback strategy (that is `deployment`), NOT a bare Hetzner VPS (that is `hetzner`), NOT another managed PaaS (that is `railway`).",
@@ -3130,23 +3098,6 @@
3130
3098
  ],
3131
3099
  "profiles": []
3132
3100
  },
3133
- {
3134
- "id": "motion-craft",
3135
- "description": "Use when an interface moves and the movement has to feel right — durations and easing, what must never animate, fixing motion that reads sluggish, cheap or janky, and reduced-motion. Web and native. NOT the visual system (that is `design`), NOT the graded critique (that is `design-loop`).",
3136
- "tags": [
3137
- "motion",
3138
- "animation",
3139
- "interface",
3140
- "craft",
3141
- "frontend"
3142
- ],
3143
- "recommends": [
3144
- "design"
3145
- ],
3146
- "profiles": [
3147
- "full"
3148
- ]
3149
- },
3150
3101
  {
3151
3102
  "id": "mysql",
3152
3103
  "description": "Use when designing, querying, indexing or operating a MySQL or MariaDB database and engine-specific behaviour matters — schema and type choices, index design, reading EXPLAIN, online schema change, replication and replica lag, locking and InnoDB deadlocks, charset traps, and server config. NOT portable SELECT/JOIN/window-function craft (that is `sql`), NOT PostgreSQL engine behaviour like VACUUM or JSONB (that is `postgresdb`), NOT the PlanetScale/Vitess branch-and-deploy workflow (that is `planetscale`).",
@@ -4300,6 +4251,30 @@
4300
4251
  ],
4301
4252
  "profiles": []
4302
4253
  },
4254
+ {
4255
+ "id": "scroll-film",
4256
+ "description": "Use when a page's hero is a pre-rendered camera flight the visitor scrubs by scrolling, Apple-style: AI clips chained frame to frame so seams never pop, encoded for seeking, mounted with an engine that holds up on phones. NOT scroll reveals or pinning (that is `design`), NOT a finished MP4 (that is `ai-media`).",
4257
+ "tags": [
4258
+ "scroll-scrub",
4259
+ "scroll-video",
4260
+ "scrollytelling",
4261
+ "landing-hero",
4262
+ "image-to-video",
4263
+ "camera-flight",
4264
+ "seamless-video"
4265
+ ],
4266
+ "recommends": [
4267
+ "ai-media",
4268
+ "fal",
4269
+ "replicate-images",
4270
+ "landing-copy",
4271
+ "design",
4272
+ "performance"
4273
+ ],
4274
+ "profiles": [
4275
+ "full"
4276
+ ]
4277
+ },
4303
4278
  {
4304
4279
  "id": "sdd",
4305
4280
  "description": "Use when you want a disciplined, spec-driven path from a feature idea to shipped, verified software — the SDD dispatcher and front door, in any language. States the method, reads the user's register from 02-DOCS, and routes to the right phase: constitution -> specify -> clarify -> plan -> tasks -> analyze -> implement -> verify -> review -> ship, with debug / worktrees / parallel on demand. Use it to start a feature, to govern the whole flow, or when unsure which phase you are in. NOT a single phase itself (it dispatches), NOT the workspace harness (that is `harness`), NOT a stack build skill.",
@@ -5145,8 +5120,7 @@
5145
5120
  "craft"
5146
5121
  ],
5147
5122
  "recommends": [
5148
- "design",
5149
- "motion-craft"
5123
+ "design"
5150
5124
  ],
5151
5125
  "profiles": [
5152
5126
  "core",
@@ -5270,7 +5244,7 @@
5270
5244
  },
5271
5245
  {
5272
5246
  "id": "variant-explorer",
5273
- "description": "Use when someone wants OPTIONS rather than an answer — 'show me a few versions', picking between approaches by seeing them. Genuinely different candidates, cheap to throw away, trade-off named. NOT the critique that judges one (`design-loop`), NOT the spec (`specify`).",
5247
+ "description": "Use when someone wants OPTIONS rather than an answer — 'show me a few versions', picking between approaches by seeing them. Genuinely different candidates, cheap to throw away, trade-off named. NOT the critique that judges one (`design`), NOT the spec (`specify`).",
5274
5248
  "tags": [
5275
5249
  "prototyping",
5276
5250
  "divergence",
@@ -5279,7 +5253,7 @@
5279
5253
  "decision"
5280
5254
  ],
5281
5255
  "recommends": [
5282
- "design-loop"
5256
+ "design"
5283
5257
  ],
5284
5258
  "profiles": [
5285
5259
  "full"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ericrisco/rsc",
3
- "version": "3.1.3",
3
+ "version": "3.3.0",
4
4
  "description": "A guided harness builder that turns your goal into a minimal, verified AI-agent setup using strict best practices.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -19,6 +19,7 @@
19
19
  "knowledge:doctor": "node scripts/knowledge-doctor.js",
20
20
  "spec:gate": "node scripts/spec-gate.js",
21
21
  "routing:audit": "node scripts/routing-audit.js",
22
+ "design:upstream": "node scripts/design-upstream.js",
22
23
  "originality:check": "node scripts/originality-check.js",
23
24
  "test": "node --test",
24
25
  "coverage": "c8 --reporter=text node --test",
@@ -115,6 +116,10 @@
115
116
  {
116
117
  "fixedIn": "2.1.0",
117
118
  "symptom": "Answers were long and leaned on hidden reasoning, and onboarding asked an accompaniment level few could answer; now: short stand-alone sentences, one question"
119
+ },
120
+ {
121
+ "fixedIn": "3.3.0",
122
+ "symptom": "Pages and screens designed with rsc came out generic and AI-looking, and most projects had no design skill installed at all (found by Carlos del Corral)"
118
123
  }
119
124
  ]
120
125
  }
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env node
2
+ // design-upstream.js — is the `design` engine behind the projects it comes from?
3
+ //
4
+ // `skills/design/engine/` is taste-skill shipped unmodified, and three of its references adapt
5
+ // impeccable's text. Both upstreams ship weekly. Nothing reminded anyone, so the previous design
6
+ // skill drifted into the state that got it rebuilt (spec design-from-scratch). This is the reminder.
7
+ //
8
+ // node scripts/design-upstream.js report: silent and exit 0 when current,
9
+ // one line per upstream that moved and exit 1
10
+ // node scripts/design-upstream.js --sync DIR copy a REVIEWED taste-skill checkout over engine/
11
+ //
12
+ // It never downloads code to apply it. Bringing a new version in is a person reading the diff and
13
+ // running --sync on a checkout they cloned themselves (a public skill is untrusted input).
14
+ import { readFileSync, writeFileSync, rmSync, cpSync, existsSync } from 'node:fs';
15
+ import { join, dirname } from 'node:path';
16
+ import { fileURLToPath } from 'node:url';
17
+ import { execFileSync } from 'node:child_process';
18
+
19
+ const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
20
+ export const ENGINE = join(ROOT, 'skills', 'design', 'engine');
21
+ const RUNTIME = ['scripts', 'library', 'assets', 'references'];
22
+
23
+ export function readUpstream(engine = ENGINE) {
24
+ return JSON.parse(readFileSync(join(engine, 'UPSTREAM.json'), 'utf8'));
25
+ }
26
+
27
+ async function getJson(fetchImpl, url) {
28
+ const res = await fetchImpl(url, { headers: { accept: 'application/vnd.github+json', 'user-agent': 'rsc-design-upstream' } });
29
+ if (!res.ok) throw new Error(`${url} → HTTP ${res.status}`);
30
+ return res.json();
31
+ }
32
+
33
+ /** One entry per upstream that moved; an empty list means the engine is current. */
34
+ export async function checkUpstream({ upstream = readUpstream(), fetchImpl = fetch } = {}) {
35
+ const moved = [];
36
+ const t = upstream['taste-skill'];
37
+ const commits = await getJson(fetchImpl, `https://api.github.com/repos/${t.repo}/commits?path=${t.path}&per_page=1`);
38
+ const latest = commits[0]?.sha;
39
+ // Different ids do not mean newer: the shipped commit is often a merge that already contains the
40
+ // latest commit on the path. Only a latest commit AHEAD of what shipped is news.
41
+ const ahead = latest && latest !== t.commit
42
+ && ['ahead', 'diverged'].includes((await getJson(fetchImpl, `https://api.github.com/repos/${t.repo}/compare/${t.commit}...${latest}`)).status);
43
+ if (ahead) {
44
+ moved.push({ id: 'taste-skill', shipped: t.commit.slice(0, 7), latest: latest.slice(0, 7), url: `https://github.com/${t.repo}/compare/${t.commit}...${latest}` });
45
+ }
46
+ const i = upstream.impeccable;
47
+ const releases = await getJson(fetchImpl, `https://api.github.com/repos/${i.repo}/releases?per_page=30`);
48
+ const skill = releases.find((r) => typeof r.tag_name === 'string' && r.tag_name.startsWith('skill-v'));
49
+ if (skill && skill.tag_name !== i.version) {
50
+ moved.push({ id: 'impeccable', shipped: i.version, latest: skill.tag_name, url: `https://github.com/${i.repo}/releases/tag/${skill.tag_name}` });
51
+ }
52
+ return moved;
53
+ }
54
+
55
+ /** Copy a reviewed taste-skill checkout over engine/, and record which commit it was. */
56
+ export function syncTaste(checkout, engine = ENGINE) {
57
+ const src = join(checkout, 'skills', 'taste-skill');
58
+ if (!existsSync(join(src, 'SKILL.md'))) throw new Error(`${checkout} is not a taste-skill checkout (no skills/taste-skill/SKILL.md)`);
59
+ for (const dir of RUNTIME) {
60
+ rmSync(join(engine, dir), { recursive: true, force: true });
61
+ if (existsSync(join(src, dir))) cpSync(join(src, dir), join(engine, dir), { recursive: true });
62
+ }
63
+ cpSync(join(checkout, 'LICENSE'), join(engine, 'LICENSE'));
64
+ // RUN.md is the upstream SKILL.md without its front matter, so it is never discovered as a skill.
65
+ writeFileSync(join(engine, 'RUN.md'), readFileSync(join(src, 'SKILL.md'), 'utf8').replace(/^---\n[\s\S]*?\n---\n/, ''));
66
+ const upstream = readUpstream(engine);
67
+ // The last commit that touched the skill folder; checkUpstream asks GitHub whether anything newer
68
+ // on that path is ahead of it.
69
+ upstream['taste-skill'].commit = execFileSync('git', ['-C', checkout, 'log', '-1', '--format=%H', '--', 'skills/taste-skill'], { encoding: 'utf8' }).trim();
70
+ const changelog = join(checkout, 'CHANGELOG.md');
71
+ const version = existsSync(changelog) && /^## (\d+\.\d+\.\d+)/m.exec(readFileSync(changelog, 'utf8'));
72
+ if (version) upstream['taste-skill'].version = version[1];
73
+ writeFileSync(join(engine, 'UPSTREAM.json'), `${JSON.stringify(upstream, null, 2)}\n`);
74
+ return upstream['taste-skill'];
75
+ }
76
+
77
+ async function main() {
78
+ const at = process.argv.indexOf('--sync');
79
+ if (at !== -1) {
80
+ const synced = syncTaste(process.argv[at + 1]);
81
+ console.log(`engine synced to taste-skill ${synced.version} (${synced.commit.slice(0, 7)}). Run the tests and read the diff before committing.`);
82
+ return;
83
+ }
84
+ const moved = await checkUpstream();
85
+ for (const m of moved) console.log(`${m.id}: shipped ${m.shipped}, upstream ${m.latest} → ${m.url}`);
86
+ if (moved.length) {
87
+ console.log('Review the change, clone it yourself, then: node scripts/design-upstream.js --sync <taste-skill checkout>');
88
+ process.exitCode = 1;
89
+ }
90
+ }
91
+
92
+ if (import.meta.url === `file://${process.argv[1]}`) main().catch((e) => { console.error(e.message); process.exitCode = 2; });
package/scripts/doctor.js CHANGED
@@ -11,8 +11,6 @@ import { readManifest as readProjectManifest } from './lib/manifest-file.js';
11
11
  import { listBackups } from './lib/backups.js';
12
12
  import { SDD_GATE_TEXT } from '../targets/hook-once.mjs';
13
13
  import { isEnabled, checkSello, readSello, countFindings, readEffectiveConfig, validateRiskConfig } from '../targets/sello.mjs';
14
- import { designIdentity } from './lib/design-identity.js';
15
- import { startingPointSummary } from './lib/starting-point.js';
16
14
  import { countGaps, listSkills, listAgents } from './lib/capabilities.js';
17
15
  import { resolveCommands, commandPath, targetHasCommands } from '../targets/commands.js';
18
16
  import { inspectMemoryWiring } from '../targets/memory.js';
@@ -104,17 +102,6 @@ export function missingHookScripts({ target, home = homedir(), cwd = process.cwd
104
102
  // Is the post-merge trigger actually armed in this clone? Three states, because they need three
105
103
  // different answers: armed, absent (repair it), or someone else's (leave it — chaining is our job
106
104
  // at install time, and overwriting a husky hook to fix a convenience would be indefensible).
107
- // The skills that read or write a design identity (02-DOCS/wiki/brand/) or a starting point. The
108
- // state is always reported; the FIX — "run design-loop, then design-dna" — is advice, and advice needs
109
- // evidence. A todo web app with none of these installed was told to run two skills it does not have
110
- // and never asked for (E2E 2026-10-07).
111
- const DESIGN_SKILLS = new Set(['design', 'design-loop', 'design-dna', 'brand-voice', 'content-engine', 'marketing', 'presentations', 'make']);
112
- function withoutUnfoundedFix(summary, inPlay) {
113
- if (inPlay || !summary || !summary.fix) return summary;
114
- const { fix, ...rest } = summary;
115
- return { ...rest, note: 'no design skill is installed here, so nothing is recommended' };
116
- }
117
-
118
105
  function worktreeCleanupStatus(root, policy) {
119
106
  if (!existsSync(join(root, '.git'))) return { state: 'not-a-repo' };
120
107
  if (existsSync(join(root, '.rsc', '.no-worktree-cleanup'))) return { state: 'off-by-choice' };
@@ -250,9 +237,6 @@ export function doctor({ target, home, cwd }) {
250
237
  const path = agentPath(target, root, id);
251
238
  return path && !existsSync(path);
252
239
  }).map((id) => ({ id, path: agentPath(target, root, id), action: 'Run `npx @ericrisco/rsc sync` to restore this managed agent.' }));
253
- // What THIS harness installed, not every skill visible from here: a design skill in the user's
254
- // global scope is how a todo app's doctor came to recommend design-dna (E2E 2026-10-07).
255
- const designInPlay = Object.keys(state.skills || {}).some((id) => DESIGN_SKILLS.has(id));
256
240
  const report = {
257
241
  target,
258
242
  installed: Object.keys(state.skills),
@@ -273,14 +257,6 @@ export function doctor({ target, home, cwd }) {
273
257
  worktreeHygiene: worktreeHygiene(root),
274
258
  ownSkills: ownSkillsIn(paths, readProjectManifest(root)?.ownSkills || []),
275
259
  sello: selloStatus(root),
276
- // Whether this harness has a design identity at all. `design` has always DECLARED that it
277
- // stops without one; until lib/design-identity.js nothing checked it (P2). Reported here,
278
- // never nagged about and never blocking: a missing identity is low risk (P7).
279
- designIdentity: withoutUnfoundedFix(designIdentity(root), designInPlay),
280
- // The other half of the same question: an identity is what this project settled on, a starting
281
- // point is what it has to settle FROM. Three skills promise to propose one and none could look;
282
- // this is the look. One field, not a section — a report grows for every user who runs it (P5).
283
- designStartingPoint: withoutUnfoundedFix(startingPointSummary(root), designInPlay),
284
260
  // An inert gate must never read as an armed one (the same rule the sello follows):
285
261
  // the gitmoji guard has a per-project kill switch, so say which state it is in.
286
262
  gitmojiGuard: state.policy?.gitmojiGuard === false
@@ -14,7 +14,11 @@
14
14
  // `connect-tool` joined in 3.0.13 (issue #303): rsc ships no ready-made connections, so "connect my
15
15
  // Holded" is answered by a method, not a list, and that method has to be there the first time the
16
16
  // user asks, in any kind of project. Like `unslop`, only its description is paid for on other turns.
17
- export const DEFAULT_SKILL_FLOOR = Object.freeze(['orient', 'suggest', 'unslop', 'ftd', 'connect-tool']);
17
+ //
18
+ // `design` joined in 3.3.0 (spec design-from-scratch): it was `full`-only, so most projects designed
19
+ // with no design skill at all. Only its description is paid for on other turns; the engine it ships
20
+ // (scripts and data) is never loaded into context.
21
+ export const DEFAULT_SKILL_FLOOR = Object.freeze(['orient', 'suggest', 'unslop', 'ftd', 'connect-tool', 'design']);
18
22
 
19
23
  export function withDefaultSkillFloor(skillIds = []) {
20
24
  return [...new Set([...DEFAULT_SKILL_FLOOR, ...skillIds])];
@@ -24,8 +24,8 @@ export const DOMAINS = [
24
24
  { title: 'Ship & operate — platforms', ids: ['vercel', 'netlify', 'cloudflare', 'railway', 'render', 'fly-io', 'coolify', 'hetzner', 'digitalocean', 'aws-essentials', 'gcp-essentials'] },
25
25
  { title: 'Ship & operate — devops', ids: ['docker', 'github-actions', 'git-workflow', 'domains-dns', 'monitoring', 'email-deliverability', 'scaling', 'deployment', 'deprecation'] },
26
26
  { title: 'Ship & operate — quality & security', ids: ['code-review', 'security-scan', 'secure-coding', 'testing-py', 'testing-web', 'testing-go', 'e2e-testing', 'accessibility', 'performance', 'error-handling', 'observability'] },
27
- { title: 'Motion & interface craft', ids: ['motion-craft', 'ui-engineering', 'variant-explorer'] },
28
- { title: 'Design & content craft', ids: ['design-loop', 'design', 'design-dna', 'presentations', 'course-storytelling', 'course-builder', 'technical-writing', 'translation-l10n'] },
27
+ { title: 'Motion & interface craft', ids: ['ui-engineering', 'variant-explorer', 'scroll-film'] },
28
+ { title: 'Design & content craft', ids: ['design', 'presentations', 'course-storytelling', 'course-builder', 'technical-writing', 'translation-l10n'] },
29
29
  { title: 'Knowledge & meta', ids: ['knowledge-ops', 'codebase-onboarding', 'research-ops', 'decision-records', 'continuous-learning', 'skill-scout', 'context-budget', 'roast-me', 'fable-operator'] },
30
30
  ];
31
31
 
@@ -10,9 +10,7 @@ const OUTCOMES = {
10
10
  fastapi: 'Your API / Python backend',
11
11
  go: 'Your Go backend service',
12
12
  postgresdb: 'Store your data reliably',
13
- design: 'Make it look good and convert',
14
- 'design-loop': 'Make it as good as the site you admire',
15
- 'design-dna': 'Keep every page looking like yours',
13
+ design: 'Pages and screens that look designed, not generated',
16
14
  marketing: 'Copy that sells',
17
15
  presentations: 'On-brand presentations',
18
16
  'course-storytelling': 'Teach so it actually lands',
@@ -8,7 +8,13 @@
8
8
  //
9
9
  // `targets/clone-bootstrap.mjs` is committed into user repos and cannot import this file; it carries
10
10
  // an inline copy, and tests/retired-skills.test.js holds the two equal in behaviour.
11
- export const RETIRED_SKILLS = Object.freeze({ eli5: 'orient', 'show-me': 'orient', bro: 'unslop' });
11
+ //
12
+ // `design-loop`, `design-dna` and `motion-craft` folded into the rebuilt `design` in 3.3.0 (spec
13
+ // design-from-scratch): its engine deals the direction, measures the render and owns the motion layer.
14
+ export const RETIRED_SKILLS = Object.freeze({
15
+ eli5: 'orient', 'show-me': 'orient', bro: 'unslop',
16
+ 'design-loop': 'design', 'design-dna': 'design', 'motion-craft': 'design',
17
+ });
12
18
 
13
19
  const retiredTo = (id) => (Object.hasOwn(RETIRED_SKILLS, id) ? RETIRED_SKILLS[id] : id);
14
20
 
@@ -2,7 +2,7 @@
2
2
  name: ai-media
3
3
  description: "Use when a creative goal must become a finished media file: pick and order generative-media models per modality — AI voiceover, image-to-video clips, score — then glue them with ffmpeg (mux, duck, loudnorm, concat). NOT still-image generation/editing (that is `replicate-images`); NOT code-rendered React compositing (that is `remotion-video`)."
4
4
  tags: [ai-media, text-to-speech, image-to-video, voiceover, music-generation, ffmpeg, media-pipeline, elevenlabs]
5
- recommends: [replicate-images, fal, replicate, remotion-video, video-shorts]
5
+ recommends: [replicate-images, fal, replicate, remotion-video, video-shorts, scroll-film]
6
6
  origin: risco
7
7
  ---
8
8
 
@@ -23,7 +23,7 @@ Map the goal to modalities and an ordered step list, and **lock that plan before
23
23
  | Just a clip from a still | img→video only | still (input) → clip | `fal`/`replicate` |
24
24
  | Code-rendered explainer | none of the above | render from React/TS | **stop — route to `remotion-video`** |
25
25
 
26
- If the video is *rendered from data/code* (charts, timelines, JSON-driven scenes), this is not your job → `../remotion-video/SKILL.md`. You handle *model-generated + ffmpeg-glued*.
26
+ If the video is *rendered from data/code* (charts, timelines, JSON-driven scenes), this is not your job → `../remotion-video/SKILL.md`. You handle *model-generated + ffmpeg-glued*. If the clips will be scrubbed by scroll on a page instead of played as a file, the chain, its seams and the page are `../scroll-film/SKILL.md`.
27
27
 
28
28
  ## Modality 1 — Voice (TTS)
29
29