@ericrisco/rsc 1.1.4 → 1.1.6

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 (66) hide show
  1. package/README.md +81 -11
  2. package/manifest.json +56 -231
  3. package/package.json +1 -1
  4. package/scripts/doctor.js +68 -1
  5. package/scripts/install-apply.js +91 -16
  6. package/scripts/lib/divergence.js +33 -0
  7. package/scripts/lib/domains.js +3 -3
  8. package/scripts/lib/manifest-file.js +53 -0
  9. package/scripts/lib/repair.js +156 -0
  10. package/scripts/lib/ui.js +5 -0
  11. package/scripts/rsc.js +84 -11
  12. package/skills/design/SKILL.md +3 -3
  13. package/skills/design/references/design-systems.md +1 -1
  14. package/skills/design/references/inspiration-sources.md +2 -2
  15. package/skills/design/references/motion-and-interaction.md +2 -2
  16. package/skills/design-dna/evals/cases.yaml +1 -1
  17. package/skills/design-loop/SKILL.md +3 -3
  18. package/skills/design-loop/evals/cases.yaml +3 -3
  19. package/skills/expo/SKILL.md +1 -1
  20. package/skills/motion-craft/SKILL.md +125 -0
  21. package/skills/motion-craft/evals/cases.yaml +38 -0
  22. package/skills/motion-craft/scripts/verify.sh +75 -0
  23. package/skills/react/SKILL.md +2 -2
  24. package/skills/react-native/SKILL.md +1 -1
  25. package/skills/suggest/SKILL.md +25 -1
  26. package/skills/swift-ios/SKILL.md +2 -2
  27. package/skills/ui-engineering/SKILL.md +114 -0
  28. package/skills/ui-engineering/evals/cases.yaml +38 -0
  29. package/skills/variant-explorer/SKILL.md +93 -0
  30. package/skills/variant-explorer/evals/cases.yaml +38 -0
  31. package/targets/_md-block.js +17 -4
  32. package/targets/claude.js +17 -7
  33. package/targets/index.js +66 -19
  34. package/skills/animate/SKILL.md +0 -851
  35. package/skills/animate/evals/cases.yaml +0 -36
  36. package/skills/animate/references/recipes.md +0 -324
  37. package/skills/animate-expo/SKILL.md +0 -281
  38. package/skills/animate-expo/evals/cases.yaml +0 -36
  39. package/skills/animate-expo/references/recipes.md +0 -385
  40. package/skills/animation-vocabulary/SKILL.md +0 -189
  41. package/skills/animation-vocabulary/evals/cases.yaml +0 -31
  42. package/skills/apple-design/SKILL.md +0 -301
  43. package/skills/apple-design/evals/cases.yaml +0 -33
  44. package/skills/ask-sonner/SKILL.md +0 -104
  45. package/skills/ask-sonner/evals/cases.yaml +0 -33
  46. package/skills/ask-sonner/references/api.md +0 -64
  47. package/skills/design-eng/SKILL.md +0 -88
  48. package/skills/design-eng/evals/cases.yaml +0 -36
  49. package/skills/find-animation-opportunities/SKILL.md +0 -150
  50. package/skills/find-animation-opportunities/evals/cases.yaml +0 -31
  51. package/skills/improve-animations/SKILL.md +0 -126
  52. package/skills/improve-animations/evals/cases.yaml +0 -31
  53. package/skills/improve-animations/references/audit.md +0 -115
  54. package/skills/improve-animations/references/plan-template.md +0 -73
  55. package/skills/pick-ui-library/SKILL.md +0 -101
  56. package/skills/pick-ui-library/evals/cases.yaml +0 -33
  57. package/skills/pick-ui-library/references/component-galleries.md +0 -59
  58. package/skills/prototype/SKILL.md +0 -113
  59. package/skills/prototype/evals/cases.yaml +0 -31
  60. package/skills/prototype/references/picker.md +0 -197
  61. package/skills/review-animations/SKILL.md +0 -188
  62. package/skills/review-animations/evals/cases.yaml +0 -34
  63. package/skills/review-animations/references/standards.md +0 -187
  64. package/skills/review-animations/scripts/verify.sh +0 -164
  65. package/skills/write-swift/SKILL.md +0 -413
  66. package/skills/write-swift/evals/cases.yaml +0 -36
package/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  <div align="center">
2
2
 
3
- <img src="https://raw.githubusercontent.com/ericrisco/rsc-harness/main/site/og.png" alt="rsc — your agent needs a memory (02-DOCS/, or it invents what it cannot remember), arms (01-TOOLS/, or it never touches your database), a trade (276 skills, or it guesses how the job is done), and reflexes (deterministic hooks, or it forgets the rule mid-session). One meta-harness for 17 coding assistants." width="960">
3
+ <img src="https://raw.githubusercontent.com/ericrisco/rsc-harness/main/site/og.png" alt="rsc — your agent needs a memory (02-DOCS/, or it invents what it cannot remember), arms (01-TOOLS/, or it never touches your database), a trade (272 skills, or it guesses how the job is done), and reflexes (deterministic hooks, or it forgets the rule mid-session). One meta-harness for 17 coding assistants." width="960">
4
4
 
5
- # `rsc` — 276 agent skills, one CLI, zero bloat
5
+ # `rsc` — 272 agent skills, one CLI, zero bloat
6
6
 
7
7
  [![npm](https://img.shields.io/npm/v/@ericrisco/rsc?color=63d68a&labelColor=12161c&label=npm)](https://www.npmjs.com/package/@ericrisco/rsc)
8
8
  [![downloads](https://img.shields.io/npm/dm/@ericrisco/rsc?color=63d68a&labelColor=12161c&label=downloads)](https://www.npmjs.com/package/@ericrisco/rsc)
9
- [![skills](https://img.shields.io/badge/skills-281-63d68a?labelColor=12161c)](#the-catalog)
9
+ [![skills](https://img.shields.io/badge/skills-272-63d68a?labelColor=12161c)](#the-catalog)
10
10
  [![license](https://img.shields.io/badge/license-MIT-63d68a?labelColor=12161c)](LICENSE)
11
11
  [![stars](https://img.shields.io/github/stars/ericrisco/rsc-harness?color=63d68a&labelColor=12161c)](https://github.com/ericrisco/rsc-harness/stargazers)
12
12
 
@@ -17,7 +17,7 @@ skills that fit — one at a time — into every assistant you pick, and keeps t
17
17
  equipped as you work.
18
18
 
19
19
  From *"document my company"* to *"ship a FastAPI service"* to *"grow my YouTube
20
- channel"* — **281 skills across 23 domains**, every one researched against live
20
+ channel"* — **272 skills across 23 domains**, every one researched against live
21
21
  2025-2026 sources and **adversarially scored ≥ 8.5/10** before it shipped.
22
22
 
23
23
  ```bash
@@ -26,7 +26,7 @@ npx @ericrisco/rsc # plain-language wizard — no jargon, installs wh
26
26
 
27
27
  <br>
28
28
 
29
- <img src="https://raw.githubusercontent.com/ericrisco/rsc-harness/main/site/meta-harness.png" alt="rsc — the model (Claude, GPT, Gemini) is the brain your coding agent rents, and you already have that. Claude Code, Codex and opencode sit in the middle. Below them the rsc meta-harness adds everything the model lacks: memory (02-DOCS/) so it stops inventing, arms (01-TOOLS/) so it can execute, a trade (281 skills) so it stops guessing, and reflexes (deterministic hooks) so it cannot forget the rule mid-session." width="960">
29
+ <img src="https://raw.githubusercontent.com/ericrisco/rsc-harness/main/site/meta-harness.png" alt="rsc — the model (Claude, GPT, Gemini) is the brain your coding agent rents, and you already have that. Claude Code, Codex and opencode sit in the middle. Below them the rsc meta-harness adds everything the model lacks: memory (02-DOCS/) so it stops inventing, arms (01-TOOLS/) so it can execute, a trade (272 skills) so it stops guessing, and reflexes (deterministic hooks) so it cannot forget the rule mid-session." width="960">
30
30
 
31
31
  **The model is the only part your agent already has.** `rsc` is the meta-harness that adds
32
32
  the rest: a **memory** so it stops inventing, **arms** so it can execute, a **trade** so it stops
@@ -52,6 +52,7 @@ user wants**, reads the repo, then installs the floor (`orient` + `rsc-suggest`
52
52
  `init`) plus only the skills that fit — one at a time. No global install and no API key needed.
53
53
 
54
54
  - **Choose assistants non-interactively:** `npx @ericrisco/rsc@latest --target claude` (comma-separate for several).
55
+ - **Two assistants already installed?** rsc asks instead of guessing. `--target` settles it in one word.
55
56
  - **Already installed, just refreshing skills + hooks:** `rsc sync` (or re-run the command above).
56
57
  - **Add one skill by id:** `rsc add <id>` · **browse the catalog:** `rsc consult "<what you want>"` or `rsc list`.
57
58
 
@@ -139,7 +140,7 @@ $ rsc
139
140
  ██████╗ ███████╗ ██████╗ ← animated gradient wordmark
140
141
  ██╔══██╗██╔════╝██╔════╝
141
142
  ██████╔╝███████╗██║
142
- 281 skills · one CLI · zero bloat
143
+ 272 skills · one CLI · zero bloat
143
144
 
144
145
  What do you want to do? ↑↓ move · enter select
145
146
  ❯ Base install — the essentials (8 skills)
@@ -187,7 +188,7 @@ rsc add youtube-api remotion-video # …grow a channel, edit with Remotion
187
188
  rsc add fastapi --target claude,codex # install into several assistants at once
188
189
  rsc install --profile minimal # the base: orient + suggest + bro + unslop + show-me + eli5 + harness + init
189
190
  rsc install --profile core # floor + the full SDD workflow
190
- rsc install --profile full # everything (all 281 skills)
191
+ rsc install --profile full # everything (all 272 skills)
191
192
  rsc install --profile full --without go
192
193
  rsc consult "I want to launch a SaaS" # recommend only, no install
193
194
  rsc registry refresh # write .rsc/skill-registry.{json,md}
@@ -203,6 +204,75 @@ rsc uninstall postgresdb --dry-run # preview a removal
203
204
 
204
205
  ---
205
206
 
207
+
208
+ ## 👥 Sharing a harness with your team
209
+
210
+ The harness travels by git, but not all of it — and the split is the point.
211
+
212
+ **Commit these:**
213
+
214
+ | | |
215
+ | --- | --- |
216
+ | `.rsc.json` | The decision: which assistants, which skills, **which catalog version**, the developer tier, which gates you disarmed |
217
+ | `01-TOOLS/` · `02-DOCS/` | Your tooling and your wiki, if you use them |
218
+ | Skills and agents you wrote by hand | They are yours. rsc does not claim them, does not count them as drift, and does not touch them |
219
+
220
+ **Do not commit these** — rsc adds them to `.gitignore` for you:
221
+
222
+ | | Why |
223
+ | --- | --- |
224
+ | `.rsc/` | Machine state: hook scripts, seals, logs |
225
+ | The skill entries rsc manages | Symlinks on macOS/Linux, real copies on Windows — two incompatible shapes of one thing |
226
+
227
+ Whoever clones runs **one command** and ends up with the same harness:
228
+
229
+ ```bash
230
+ npx @ericrisco/rsc@latest sync
231
+ ```
232
+
233
+ Same skills, same version — `.rsc.json` pins the catalog, so a teammate who clones in three
234
+ months gets what you had, not what shipped since. Upgrading is a deliberate act, never a side
235
+ effect of rebuilding.
236
+
237
+ When someone changes the harness and you `git pull`, `rsc doctor` tells you what no longer
238
+ matches. **Nothing is ever written to your machine by a pull** — you are told, and you decide.
239
+
240
+ **Own skills.** A skill your team wrote lives in the repo and already works for whoever clones,
241
+ with no command at all. Declare it in `.rsc.json` under `ownSkills` and `doctor` will also say
242
+ when someone is missing it — that is all declaring does. rsc never installs, updates or
243
+ overwrites it: its version is the commit.
244
+
245
+
246
+ ## 🩹 Something's off? One command
247
+
248
+ Recognise any of these? They are all the same fix.
249
+
250
+ | What you see | |
251
+ | --- | --- |
252
+ | `"target": "codex"` when you work in Claude Code | |
253
+ | `This target has no hook injection` and you did not expect that | |
254
+ | Skills appear that you never asked for | |
255
+ | You cloned a repo and your assistant sees no skills at all | |
256
+ | A hook seems to run several times per turn | |
257
+ | Template lines showed up inside your hand-written `AGENTS.md` | |
258
+
259
+ ```bash
260
+ npx @ericrisco/rsc@latest repair
261
+ ```
262
+
263
+ Safe in any folder: with no rsc there, it says so and writes nothing. It shows what it
264
+ found before touching anything, keeps a recoverable copy, and running it twice changes
265
+ nothing the second time. Add `--dry-run` to see the whole pass without a single write.
266
+
267
+ **What it fixes on its own** — putting the harness back to what was already declared:
268
+ dangling links from a clone, hooks wired several times, the 0.1 layout no assistant reads.
269
+
270
+ **What it asks about** — anything that changes a decision: moving the harness to another
271
+ assistant, or touching files you already committed.
272
+
273
+ **What it never touches:** skills and agents you wrote by hand. rsc did not install them,
274
+ so rsc does not repair, move or delete them — not even when rebuilding from scratch.
275
+
206
276
  ## Update
207
277
 
208
278
  `rsc` is an npm package, so updating is two steps — bump the package, then
@@ -253,7 +323,7 @@ just asks in plain language.
253
323
 
254
324
  ## The catalog
255
325
 
256
- 281 skills, grouped by what you're trying to do. Click any skill to read its
326
+ 272 skills, grouped by what you're trying to do. Click any skill to read its
257
327
  `SKILL.md`. It fires on its own when a task matches.
258
328
 
259
329
  ### 🧭 Core & control plane
@@ -344,11 +414,11 @@ Train and adapt open models end to end: classic ML, deep learning, NLP, fine-tun
344
414
 
345
415
  ### 🗣️ Languages
346
416
 
347
- [typescript](skills/typescript/) · [python](skills/python/) · [java](skills/java/) · [csharp-dotnet](skills/csharp-dotnet/) · [php](skills/php/) · [ruby](skills/ruby/) · [cpp](skills/cpp/) · [elixir](skills/elixir/) · [bash-scripting](skills/bash-scripting/) · [sql](skills/sql/) · [go](skills/go/) · [write-swift](skills/write-swift/)
417
+ [typescript](skills/typescript/) · [python](skills/python/) · [java](skills/java/) · [csharp-dotnet](skills/csharp-dotnet/) · [php](skills/php/) · [ruby](skills/ruby/) · [cpp](skills/cpp/) · [elixir](skills/elixir/) · [bash-scripting](skills/bash-scripting/) · [sql](skills/sql/) · [go](skills/go/)
348
418
 
349
419
  ### 🏗️ Frameworks & app stacks
350
420
 
351
- [fastapi](skills/fastapi/) · [nextjs](skills/nextjs/) · [react](skills/react/) · [react-native](skills/react-native/) · [vue-nuxt](skills/vue-nuxt/) · [angular](skills/angular/) · [svelte](skills/svelte/) · [astro](skills/astro/) · [solid-js](skills/solid-js/) · [htmx](skills/htmx/) · [nodejs](skills/nodejs/) · [nestjs](skills/nestjs/) · [django](skills/django/) · [laravel](skills/laravel/) · [rails](skills/rails/) · [spring-boot](skills/spring-boot/) · [phoenix](skills/phoenix/) · [flutter](skills/flutter/) · [swift-ios](skills/swift-ios/) · [kotlin-android](skills/kotlin-android/) · [compose-multiplatform](skills/compose-multiplatform/) · [expo](skills/expo/) · [tauri](skills/tauri/) · [electron](skills/electron/) · [rust](skills/rust/) · [wordpress](skills/wordpress/) · [shopify](skills/shopify/) · [no-code-app](skills/no-code-app/) · [chrome-extension](skills/chrome-extension/) · [api-design](skills/api-design/) · [pick-ui-library](skills/pick-ui-library/) · [ask-sonner](skills/ask-sonner/)
421
+ [fastapi](skills/fastapi/) · [nextjs](skills/nextjs/) · [react](skills/react/) · [react-native](skills/react-native/) · [vue-nuxt](skills/vue-nuxt/) · [angular](skills/angular/) · [svelte](skills/svelte/) · [astro](skills/astro/) · [solid-js](skills/solid-js/) · [htmx](skills/htmx/) · [nodejs](skills/nodejs/) · [nestjs](skills/nestjs/) · [django](skills/django/) · [laravel](skills/laravel/) · [rails](skills/rails/) · [spring-boot](skills/spring-boot/) · [phoenix](skills/phoenix/) · [flutter](skills/flutter/) · [swift-ios](skills/swift-ios/) · [kotlin-android](skills/kotlin-android/) · [compose-multiplatform](skills/compose-multiplatform/) · [expo](skills/expo/) · [tauri](skills/tauri/) · [electron](skills/electron/) · [rust](skills/rust/) · [wordpress](skills/wordpress/) · [shopify](skills/shopify/) · [no-code-app](skills/no-code-app/) · [chrome-extension](skills/chrome-extension/) · [api-design](skills/api-design/)
352
422
 
353
423
  ### 🎮 Game development
354
424
 
@@ -374,7 +444,7 @@ Three engines + engine-agnostic disciplines. Every engine skill pins the current
374
444
 
375
445
  ### 🌀 Motion & interface craft
376
446
 
377
- [design-eng](skills/design-eng/) · [animate](skills/animate/) · [animate-expo](skills/animate-expo/) · [review-animations](skills/review-animations/) · [improve-animations](skills/improve-animations/) · [find-animation-opportunities](skills/find-animation-opportunities/) · [animation-vocabulary](skills/animation-vocabulary/) · [apple-design](skills/apple-design/) · [prototype](skills/prototype/)
447
+ [motion-craft](skills/motion-craft/) · [ui-engineering](skills/ui-engineering/) · [variant-explorer](skills/variant-explorer/)
378
448
 
379
449
  ### 🎨 Design & content craft
380
450
 
package/manifest.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
- "version": "1.1.4",
2
+ "version": "1.1.6",
3
3
  "counts": {
4
- "skills": 281
4
+ "skills": 272
5
5
  },
6
6
  "skills": [
7
7
  {
@@ -182,62 +182,6 @@
182
182
  ],
183
183
  "profiles": []
184
184
  },
185
- {
186
- "id": "animate",
187
- "description": "Build a web animation: whether it should animate at all, the purpose, the tool, the properties, the easing curve, the duration, interruption and exit. Writes the CSS or Motion code for dropdowns, modals, toasts, tooltips, accordions, press feedback, stagger and scroll reveals. Use to animate something or make a component feel alive.",
188
- "tags": [
189
- "motion",
190
- "animation",
191
- "css",
192
- "transitions",
193
- "ui"
194
- ],
195
- "recommends": [
196
- "review-animations",
197
- "pick-ui-library",
198
- "apple-design"
199
- ],
200
- "profiles": [
201
- "full"
202
- ]
203
- },
204
- {
205
- "id": "animate-expo",
206
- "description": "Build animation and gestures in React Native and Expo with Reanimated worklets on the UI thread — sheets, drawers, screen transitions, swipe, drag, press feedback, haptics. Use when adding gestures to an Expo app, or when motion stutters, janks or drops frames on a real Android or iOS device.",
207
- "tags": [
208
- "motion",
209
- "animation",
210
- "expo",
211
- "react-native",
212
- "gestures"
213
- ],
214
- "recommends": [
215
- "expo",
216
- "react-native",
217
- "animate"
218
- ],
219
- "profiles": [
220
- "full"
221
- ]
222
- },
223
- {
224
- "id": "animation-vocabulary",
225
- "description": "Reverse glossary: turn a vague description of a motion effect into its exact name — a popover growing from its trigger is origin-aware, iOS overscroll is rubber-banding, items appearing one after another with a delay is a stagger. Use for \\\"what's it called when…\\\" or \\\"what do you call it when…\\\".",
226
- "tags": [
227
- "motion",
228
- "animation",
229
- "glossary",
230
- "terminology",
231
- "naming"
232
- ],
233
- "recommends": [
234
- "animate",
235
- "apple-design"
236
- ],
237
- "profiles": [
238
- "full"
239
- ]
240
- },
241
185
  {
242
186
  "id": "api-connector-builder",
243
187
  "description": "Use when writing a client for someone else's REST or GraphQL API: auth flow choice and token refresh, pagination to exhaustion, retry-with-jitter on transient failures only, rate-limit-aware throttling. NOT inbound callbacks (that is `webhooks`), NOT chaining services (that is `automation-flows`), NOT designing your own API (that is `api-design`).",
@@ -289,25 +233,6 @@
289
233
  ],
290
234
  "profiles": []
291
235
  },
292
- {
293
- "id": "apple-design",
294
- "description": "Apple's fluid-interface craft on the web: spring damping and response, velocity handoff, momentum projection on a flick, rubber-banding at a boundary, interruptible motion the user can grab mid-flight, translucent materials and backdrop blur, letter-spacing and leading by size. Use for iOS-grade feel, sheets and drag.",
295
- "tags": [
296
- "design",
297
- "motion",
298
- "springs",
299
- "gestures",
300
- "apple"
301
- ],
302
- "recommends": [
303
- "animate",
304
- "design",
305
- "swift-ios"
306
- ],
307
- "profiles": [
308
- "full"
309
- ]
310
- },
311
236
  {
312
237
  "id": "article-writing",
313
238
  "description": "Use when writing one long-form article end to end — answer-first lede, question-shaped headings, plus its on-page surface (title, meta, slug, FAQ, Article/FAQPage JSON-LD) — or fixing a draft that buries the answer or reads AI-padded. NOT keyword research or topic selection (that is `seo-geo`), NOT the editorial calendar (that is `content-engine`).",
@@ -331,25 +256,6 @@
331
256
  ],
332
257
  "profiles": []
333
258
  },
334
- {
335
- "id": "ask-sonner",
336
- "description": "Sonner, the React toast library: mounting Toaster once, toast() calls, promise and loading toasts, updating, dismissing, styling, theming, dark mode, positioning. Use when toasts do not appear, appear twice, sit behind a modal, ignore Tailwind classes, or do nothing from a server action.",
337
- "tags": [
338
- "sonner",
339
- "toasts",
340
- "notifications",
341
- "react",
342
- "library"
343
- ],
344
- "recommends": [
345
- "react",
346
- "nextjs",
347
- "pick-ui-library"
348
- ],
349
- "profiles": [
350
- "full"
351
- ]
352
- },
353
259
  {
354
260
  "id": "astro",
355
261
  "description": "Use when building a content-driven or marketing site with Astro 6: static-first pages, islands and partial hydration, content collections, server islands, per-route on-demand rendering, deploy adapters, and Astro 5→6 migration. NOT app-router React with server actions and heavy client interactivity (that is `nextjs`).",
@@ -1433,28 +1339,6 @@
1433
1339
  ],
1434
1340
  "profiles": []
1435
1341
  },
1436
- {
1437
- "id": "design-eng",
1438
- "description": "Umbrella for frontend craft: routes a UI request to the sibling that owns it — build motion, review it, audit it, find where it is missing, name an effect, gesture physics, prototype variants, pick a library. Use when an app works but feels cheap or unpolished, or when a frontend request could belong to several of them.",
1439
- "tags": [
1440
- "design",
1441
- "frontend",
1442
- "ui",
1443
- "routing",
1444
- "craft"
1445
- ],
1446
- "recommends": [
1447
- "design-loop",
1448
- "animate",
1449
- "review-animations",
1450
- "apple-design",
1451
- "design"
1452
- ],
1453
- "profiles": [
1454
- "core",
1455
- "full"
1456
- ]
1457
- },
1458
1342
  {
1459
1343
  "id": "design-loop",
1460
1344
  "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`).",
@@ -1972,24 +1856,6 @@
1972
1856
  ],
1973
1857
  "profiles": []
1974
1858
  },
1975
- {
1976
- "id": "find-animation-opportunities",
1977
- "description": "Sweep a UI for what does not animate but should, and reject what should not — each suggestion with an exact curve and duration, plus the rejected candidates. Read-only. Use for 'what could be animated here', 'where would motion help', or an interface that feels static and dead.",
1978
- "tags": [
1979
- "motion",
1980
- "animation",
1981
- "restraint",
1982
- "opportunities",
1983
- "ui"
1984
- ],
1985
- "recommends": [
1986
- "animate",
1987
- "improve-animations"
1988
- ],
1989
- "profiles": [
1990
- "full"
1991
- ]
1992
- },
1993
1859
  {
1994
1860
  "id": "finetuning",
1995
1861
  "description": "Use when adapting an open-weight model to a target form or behavior — tone, output format, reasoning pattern — via LoRA/QLoRA or full fine-tuning with TRL SFTTrainer, then preference optimization (DPO/ORPO/KTO/GRPO), and for fine-tune vs prompt vs RAG. NOT adding facts to a model (that is rag); NOT the single-GPU Unsloth backend or GGUF export (that is unsloth).",
@@ -2583,25 +2449,6 @@
2583
2449
  "full"
2584
2450
  ]
2585
2451
  },
2586
- {
2587
- "id": "improve-animations",
2588
- "description": "Audit a whole codebase's animation and motion, then write prioritized findings and self-contained plans another agent can execute. Read-only. Use for 'improve the animations', 'make the app feel better', or a roadmap of motion fixes — rather than a verdict on one diff.",
2589
- "tags": [
2590
- "motion",
2591
- "animation",
2592
- "audit",
2593
- "plan",
2594
- "codebase"
2595
- ],
2596
- "recommends": [
2597
- "review-animations",
2598
- "animate",
2599
- "plan"
2600
- ],
2601
- "profiles": [
2602
- "full"
2603
- ]
2604
- },
2605
2452
  {
2606
2453
  "id": "init",
2607
2454
  "description": "Use when starting from nothing or pointing rsc at an existing project — the front door. Gauges technical level and the accompaniment dial first (non-technical by default), discovers what the user wants to build or govern (any stack, or a non-code harness: company/ops, research, knowledge, content), writes the profile to 02-DOCS, and installs the skills discovery justified. NOT the scaffolder (that is `harness`), NOT a stack skill.",
@@ -3276,6 +3123,23 @@
3276
3123
  ],
3277
3124
  "profiles": []
3278
3125
  },
3126
+ {
3127
+ "id": "motion-craft",
3128
+ "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`).",
3129
+ "tags": [
3130
+ "motion",
3131
+ "animation",
3132
+ "interface",
3133
+ "craft",
3134
+ "frontend"
3135
+ ],
3136
+ "recommends": [
3137
+ "design"
3138
+ ],
3139
+ "profiles": [
3140
+ "full"
3141
+ ]
3142
+ },
3279
3143
  {
3280
3144
  "id": "mysql",
3281
3145
  "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`).",
@@ -3677,25 +3541,6 @@
3677
3541
  ],
3678
3542
  "profiles": []
3679
3543
  },
3680
- {
3681
- "id": "pick-ui-library",
3682
- "description": "Pick the frontend library for a task from a curated list: dialogs and popovers, command palette, toasts, one-time-password input, charts, virtualization, drag and drop, state, conditional classNames, variants, dark mode. Use instead of hand-rolling a component or installing an abandoned package.",
3683
- "tags": [
3684
- "frontend",
3685
- "libraries",
3686
- "dependencies",
3687
- "react",
3688
- "ui"
3689
- ],
3690
- "recommends": [
3691
- "react",
3692
- "nextjs",
3693
- "animate"
3694
- ],
3695
- "profiles": [
3696
- "full"
3697
- ]
3698
- },
3699
3544
  {
3700
3545
  "id": "pitch-deck",
3701
3546
  "description": "Use when building or fixing an investor fundraising deck — the narrative arc, the slide-by-slide story, and the few numbers that actually move an investment decision, for a pre-seed, seed or Series A raise (or an accelerator application deck). Decides what goes on each slide, in what order, with which metric, sequenced the way an investor evaluates risk. NOT rendering, theming or exporting the slides into a designed PDF or PPTX (that is `presentations`), NOT the projection spreadsheet behind the numbers (that is `financial-model`).",
@@ -3970,25 +3815,6 @@
3970
3815
  ],
3971
3816
  "profiles": []
3972
3817
  },
3973
- {
3974
- "id": "prototype",
3975
- "description": "Build several genuinely different versions — variants, takes, options, directions — of one UI piece behind a visual picker, so you flip through them live and promote the winner. Use to explore a look before committing, or when you do not yet know what you want something to look like.",
3976
- "tags": [
3977
- "design",
3978
- "variants",
3979
- "exploration",
3980
- "picker",
3981
- "ui"
3982
- ],
3983
- "recommends": [
3984
- "design",
3985
- "animate",
3986
- "nextjs"
3987
- ],
3988
- "profiles": [
3989
- "full"
3990
- ]
3991
- },
3992
3818
  {
3993
3819
  "id": "python",
3994
3820
  "description": "Use when the task is Python itself, in any framework or none: PEP 695 generics, mypy --strict typing, dataclass/Protocol/TypedDict/Enum choices, asyncio.TaskGroup, stdlib idioms, src/ layout + pyproject.toml with uv, ruff+mypy+pytest gate. NOT a FastAPI/ASGI service (that is fastapi), NOT a deep pytest suite (that is testing-py).",
@@ -4317,25 +4143,6 @@
4317
4143
  "full"
4318
4144
  ]
4319
4145
  },
4320
- {
4321
- "id": "review-animations",
4322
- "description": "Review, critique or grade existing animation and motion code against a strict craft bar — easing, duration, transform-origin, interruptibility, GPU-only properties, reduced motion, enter/exit asymmetry, stagger. Default is to flag; approval is earned. Use on the transitions in a diff or a component.",
4323
- "tags": [
4324
- "motion",
4325
- "animation",
4326
- "review",
4327
- "craft",
4328
- "ui"
4329
- ],
4330
- "recommends": [
4331
- "animate",
4332
- "improve-animations",
4333
- "code-review"
4334
- ],
4335
- "profiles": [
4336
- "full"
4337
- ]
4338
- },
4339
4146
  {
4340
4147
  "id": "review-management",
4341
4148
  "description": "Use when a public review needs an on-voice reply, when you need to earn more reviews legally, or when an aggregate rating is slipping across Google Business Profile, Trustpilot, the App Store or Google Play — including deciding reply versus flag, getting an unfair one-star removed rather than merely answered, and wiring programmatic replies. NOT a private support ticket against an SLA (that is `customer-support`), NOT churn, NPS or save plays (that is `retention`).",
@@ -5341,6 +5148,26 @@
5341
5148
  ],
5342
5149
  "profiles": []
5343
5150
  },
5151
+ {
5152
+ "id": "ui-engineering",
5153
+ "description": "Use when the question is how interface should be BUILT — component boundaries, state that belongs in the URL vs the component, loading/empty/error states, forms, optimistic updates, build vs adopt. NOT the visual system (`design`), NOT framework mechanics (`react`/`nextjs`).",
5154
+ "tags": [
5155
+ "interface",
5156
+ "frontend",
5157
+ "architecture",
5158
+ "components",
5159
+ "state",
5160
+ "craft"
5161
+ ],
5162
+ "recommends": [
5163
+ "design",
5164
+ "motion-craft"
5165
+ ],
5166
+ "profiles": [
5167
+ "core",
5168
+ "full"
5169
+ ]
5170
+ },
5344
5171
  {
5345
5172
  "id": "unit-economics",
5346
5173
  "description": "Use when computing or improving the per-customer economics of a business — CAC, LTV, CAC payback period and contribution margin — judging whether the ratios are healthy against current norms, and naming the one lever that fixes the worst number. Covers a blended CAC that hides a bleeding channel, and a model that shows profit while each customer loses money. NOT the multi-year P&L or scenario projection (that is `financial-model`).",
@@ -5454,6 +5281,23 @@
5454
5281
  ],
5455
5282
  "profiles": []
5456
5283
  },
5284
+ {
5285
+ "id": "variant-explorer",
5286
+ "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`).",
5287
+ "tags": [
5288
+ "prototyping",
5289
+ "divergence",
5290
+ "options",
5291
+ "exploration",
5292
+ "decision"
5293
+ ],
5294
+ "recommends": [
5295
+ "design-loop"
5296
+ ],
5297
+ "profiles": [
5298
+ "full"
5299
+ ]
5300
+ },
5457
5301
  {
5458
5302
  "id": "vector-db",
5459
5303
  "description": "Use when operating a vector store as a data layer — choosing or migrating between Pinecone, Qdrant, Weaviate and pgvector; designing a collection or index (distance metric, dimensions, HNSW parameters, named vectors); filtering on metadata; hybrid dense-plus-sparse search; and quantization to cut RAM and cost. Covers garbage results, silently ignored filters, low recall, slow queries, and filtered queries returning fewer than k rows. NOT producing, chunking or judging embeddings (that is `embeddings-search`).",
@@ -5694,25 +5538,6 @@
5694
5538
  "full"
5695
5539
  ]
5696
5540
  },
5697
- {
5698
- "id": "write-swift",
5699
- "description": "Modern Swift at the language level: structs and value types versus classes, Swift 6 data-race safety, actors, async/await and task groups, `some` versus `any`, ARC and retain cycles, Swift Testing, macros. Use for a deadlock, a hang, a data race, a concurrency error, or migrating to Swift 6.",
5700
- "tags": [
5701
- "swift",
5702
- "language",
5703
- "concurrency",
5704
- "generics",
5705
- "testing"
5706
- ],
5707
- "recommends": [
5708
- "swift-ios",
5709
- "apple-design",
5710
- "testing-web"
5711
- ],
5712
- "profiles": [
5713
- "full"
5714
- ]
5715
- },
5716
5541
  {
5717
5542
  "id": "youtube-api",
5718
5543
  "description": "Use when wiring code to a real YouTube channel: user OAuth 2.0, resumable videos.insert uploads, editing metadata after publish, pulling views/watch time/retention/traffic from the Analytics API v2 into a dated 02-DOCS/wiki/youtube/ feedback log. NOT what to publish or how to title and thumbnail it (that is youtube-strategy / youtube-packaging).",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ericrisco/rsc",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "description": "Eric Risco's agent-skills catalog as a granular, self-recommending CLI installer.",
5
5
  "type": "module",
6
6
  "bin": {
package/scripts/doctor.js CHANGED
@@ -3,6 +3,7 @@ import { join } from 'node:path';
3
3
  import { homedir } from 'node:os';
4
4
  import { targetPaths, TARGET_IDS } from '../targets/index.js';
5
5
  import { readState } from './lib/state.js';
6
+ import { divergence } from './lib/divergence.js';
6
7
  import { loadManifest } from './lib/manifest.js';
7
8
  import { listBackups } from './lib/backups.js';
8
9
  import { SDD_GATE_TEXT } from '../targets/hook-once.mjs';
@@ -40,6 +41,40 @@ function selloStatus(root) {
40
41
  } catch { return { enabled: false }; }
41
42
  }
42
43
 
44
+ // A wired hook is a promise to run a file. `settings.json` existing only proves the
45
+ // promise was made — and a fresh clone brings that file (committed) without .rsc/
46
+ // (ignored), so every session start fails while the report says all-clear. Read the
47
+ // commands we wired and check the files they name actually exist.
48
+ //
49
+ // Hookless targets get an empty list by construction: their always-on surface is a
50
+ // markdown block with no script behind it, so there is nothing that can go missing.
51
+ export function missingHookScripts({ target, home = homedir(), cwd = process.cwd() } = {}) {
52
+ if (HOOKLESS_TARGETS.has(target)) return [];
53
+ const file = targetPaths(target, home, cwd).hookTarget;
54
+ let settings;
55
+ try { settings = JSON.parse(readFileSync(file, 'utf8')); } catch { return []; }
56
+ const commands = [];
57
+ for (const entries of Object.values(settings?.hooks || {})) {
58
+ if (!Array.isArray(entries)) continue;
59
+ for (const entry of entries) for (const h of entry?.hooks || []) {
60
+ if (typeof h?.command === 'string') commands.push(h.command);
61
+ }
62
+ }
63
+ const seen = new Set();
64
+ // Commands name the project through ${CLAUDE_PROJECT_DIR}, which only the client
65
+ // expands. Resolve it here or every wired script reads as missing and the report
66
+ // calls a healthy install broken — the mirror image of the bug this check fixes.
67
+ const expand = (c) => c.split('${CLAUDE_PROJECT_DIR}').join(cwd);
68
+ for (const raw of commands) {
69
+ const cmd = expand(raw);
70
+ for (const m of cmd.matchAll(/["']?([^"'\s]*[\\/]\.rsc[\\/][^"'\s]+\.mjs)["']?/g)) {
71
+ const script = m[1];
72
+ if (!existsSync(script)) seen.add(script);
73
+ }
74
+ }
75
+ return [...seen];
76
+ }
77
+
43
78
  export function doctor({ target, home, cwd }) {
44
79
  const root = cwd || process.cwd();
45
80
  const paths = targetPaths(target, home, cwd);
@@ -50,7 +85,8 @@ export function doctor({ target, home, cwd }) {
50
85
  target,
51
86
  installed: Object.keys(state.skills),
52
87
  missing: [],
53
- hookWired: existsSync(paths.hookTarget),
88
+ // Wired means it can actually run, not merely that the file declaring it is there.
89
+ hookWired: existsSync(paths.hookTarget) && missingHookScripts({ target, home, cwd }).length === 0,
54
90
  manifestSkills: manifest.counts.skills,
55
91
  backups: {
56
92
  exists: existsSync(join(root, '.rsc', 'backups')),
@@ -285,6 +321,37 @@ export function contextBudget({ target, home = homedir(), cwd = process.cwd() }
285
321
  + 'root, or update both to a version that de-duplicates at runtime.',
286
322
  });
287
323
  }
324
+ const orphanScripts = missingHookScripts({ target, home, cwd });
325
+ if (orphanScripts.length) {
326
+ // ONE finding, not one per file: a clone is missing all eight at once, and a list of
327
+ // eight paths buries the single thing the reader has to do (principle 5).
328
+ findings.push({
329
+ id: 'hook-scripts-missing',
330
+ severity: 'high',
331
+ summary: `${orphanScripts.length} hook script(s) named by the wiring are not on disk, so those hooks `
332
+ + 'fail every time they fire. This is what a fresh clone looks like: the settings travelled, the '
333
+ + 'scripts did not.',
334
+ action: 'Rebuild them with `npx @ericrisco/rsc repair` from this project root.',
335
+ });
336
+ }
337
+ const drift = divergence({ cwd, target, home });
338
+ if (drift.missing.length || drift.extra.length || drift.ownMissing.length) {
339
+ // The day-two case: a teammate changed the harness and this checkout has not caught
340
+ // up. Reported always, even after someone declines to align — the divergence does not
341
+ // stop being true because they said no.
342
+ const parts = [];
343
+ if (drift.missing.length) parts.push(`missing ${drift.missing.join(', ')}`);
344
+ if (drift.ownMissing.length) parts.push(`${drift.ownMissing.join(', ')} declared by the team but not in this repo`);
345
+ if (drift.extra.length) parts.push(`${drift.extra.join(', ')} installed but no longer declared`);
346
+ findings.push({
347
+ id: 'manifest-divergence',
348
+ severity: 'medium',
349
+ summary: `This checkout does not match what .rsc.json declares: ${parts.join('; ')}.`,
350
+ action: drift.ownMissing.length && !drift.missing.length && !drift.extra.length
351
+ ? 'Those come from the repo, not from rsc — pull, or ask whoever wrote them.'
352
+ : 'Align with `npx @ericrisco/rsc sync`. Nothing is written until you run it.',
353
+ });
354
+ }
288
355
  findings.push(...duplicateEntryFindings(scopes));
289
356
  for (const s of scopes) {
290
357
  if (s.hookCountsUnknown) {