Преглед на файлове

feat(animation): auto-install motion lib + detection across init/onboard/advisor

Add lib/animation-lib-check.sh with detect_anim_eligibility,
is_anim_lib_installed and recommend_anim_install_cmd helpers.
Wire it into the framework:
- init-project STEP 5e: silent auto-install after scaffold validated
- onboard STEP 2.5: propose + wait for user confirmation (opt-in on
  existing projects)
- plugin-advisor PHASE 1/2/3: read-only detection only, never installs
- scaffolder PHASE 4: clarifies boundary (orchestrator owns motion install)
- design-gate filesystem signals: motion / motion-v / framer-motion /
  gsap / lottie-react / react-spring / popmotion / auto-animate

Recommends `motion` (rebranded from framer-motion in Nov 2024) for
React-family and Svelte stacks, `motion-v` for Vue 3 / Nuxt. Excludes
React Native (use react-native-reanimated), backend, embedded, Flutter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bastien преди 2 седмици
родител
ревизия
f8f0a09832
променени са 7 файла, в които са добавени 220 реда и са изтрити 0 реда
  1. 21 0
      .claude/tasks/TODO.md
  2. 18 0
      agents/plugin-advisor.md
  3. 4 0
      agents/scaffolder.md
  4. 117 0
      lib/animation-lib-check.sh
  5. 1 0
      lib/design-gate.md
  6. 27 0
      skills/init-project/SKILL.md
  7. 32 0
      skills/onboard/SKILL.md

+ 21 - 0
.claude/tasks/TODO.md

@@ -68,6 +68,27 @@ Subtasks :
 - [x] Mettre à jour `skills/seo/SKILL.md` — cross-ref /validate pour W3C/WCAG
 - [x] Grep cohérence : refs /validate correctes, skill détecté par la harness
 
+## Animation lib (`motion`) — install + détection
+
+Problème : `motion` (ex-`framer-motion`, rebrandé nov 2024) n'est ni installé par les scripts ni détecté par plugin-advisor / design-gate. Ajouter détection + install conditionnel.
+
+Décisions :
+- **Package** : `motion` (npm `motion`, import `motion/react`). `motion-v` pour Vue 3 (package séparé). Svelte/vanilla → `motion`.
+- **Éligibilité** : tout projet qui peut consommer l'API. ✅ React/Next/Remix/Astro+React, Vue3/Nuxt, Svelte. ❌ Backend, CLI, embedded, Flutter, WordPress/Drupal/Strapi, RN (réservé `react-native-reanimated`).
+- **init-project** STEP 5 : auto-install si éligible + absent (l'utilisateur a déjà validé scaffold).
+- **onboard** STEP 2.5 : propose + attendre OK (projet existant, opt-in).
+- **plugin-advisor** : read-only — détecte + reporte ("✅ motion installed" ou "ℹ️ eligible but absent — run /onboard").
+- **design-gate** : ajouter motion/motion-v/framer-motion (legacy) dans filesystem signals.
+
+Subtasks :
+- [x] Créer `lib/animation-lib-check.sh` — fonctions `detect_anim_eligibility()` + `is_anim_lib_installed()` + `recommend_anim_install_cmd()`
+- [x] Patcher `agents/scaffolder.md` PHASE 4 — note (le scaffolder n'installe PAS, l'orchestrateur init-project STEP 5e gère)
+- [x] Patcher `skills/init-project/SKILL.md` — STEP 5e ANIMATION LIB (auto-install si éligible)
+- [x] Patcher `skills/onboard/SKILL.md` — STEP 2.5 ANIMATION LIB (propose + attendre yes/skip)
+- [x] Patcher `agents/plugin-advisor.md` PHASE 1 (sourcing du helper) + PHASE 2 (signaux `anim-lib-eligible`/`anim-lib-installed`) + PHASE 3 (section ANIMATION LIB read-only)
+- [x] Patcher `lib/design-gate.md` — ajouter motion/motion-v/framer-motion + autres anim-libs dans filesystem signals
+- [x] Tester : shellcheck OK ; matrix React/Vue/RN/backend/with-motion/no-package/pnpm tous corrects
+
 ## Helper `--help` / `help` sur tous les skills (option C)
 Problème : aucun skill ne gère `--help` aujourd'hui. `argument-hint` affiche juste la syntaxe en autocomplétion, pas de description/exemples. L'utilisateur doit lire le SKILL.md ou deviner.
 

+ 18 - 0
agents/plugin-advisor.md

@@ -35,6 +35,13 @@ ls package.json pyproject.toml Cargo.toml go.mod 2>/dev/null | head -5
 grep -rl "next\|react\|vue\|prisma\|supabase" package.json 2>/dev/null | head -3 || true
 find . -name "*.tsx" -o -name "*.jsx" 2>/dev/null | head -3 | wc -l
 find . -name "docker-compose*" -o -name "Dockerfile" 2>/dev/null | head -3 | wc -l
+
+# Animation lib status (motion / motion-v) — read-only detection
+if [ -f "$HOME/.claude/lib/animation-lib-check.sh" ]; then
+  source "$HOME/.claude/lib/animation-lib-check.sh"
+  detect_anim_eligibility   # outputs '<status>|<package>|<reason>'
+  is_anim_lib_installed || echo "anim-lib-not-installed"
+fi
 # Monorepo detection (current dir + parent dirs for sub-package context)
 ls apps/ packages/ services/ workspaces/ 2>/dev/null | head -5
 ls pnpm-workspace.yaml turbo.json nx.json lerna.json 2>/dev/null
@@ -70,6 +77,8 @@ Detect signals from the project description and filesystem scan:
 | `skill-creation` | "create a skill", "new skill", "custom skill", `/plugin-dev:create-plugin` in description |
 | `embedded` | "firmware", "bare-metal", "microcontroller", "STM32", "ESP32", "RTOS", "driver", "kernel", "bootloader" in description; **or** `platformio.ini` present; **or** linker script (`*.ld`, `*.lds`) present; **or** `Makefile` + `src/*.c` + no `package.json`/`Cargo.toml`/`go.mod`/`setup.py`/`pyproject.toml` (C project without standard ecosystems). Note: `.c` files with a Rust/Node/Go manifest = FFI binding, NOT embedded. |
 | `simple` | single file, hotfix, quick script, no frontend, no deploy |
+| `anim-lib-eligible` | output of `detect_anim_eligibility` starts with `eligible|` (React/Vue/Svelte stack) |
+| `anim-lib-installed` | `is_anim_lib_installed` returns 0 (any of motion / motion-v / framer-motion / gsap / lottie-react / react-spring / popmotion / auto-animate present) |
 
 ---
 
@@ -117,11 +126,20 @@ RECOMMENDATIONS:
   ℹ️  OPTIONAL: [plugin] — [marginal benefit, low priority]
   🖥️  CLI     : [gsd v2] — [run 'gsd' in terminal if multi-session]
 
+ANIMATION LIB:
+  ✅ <lib> installed                                     (anim-lib-installed)
+  ℹ️  eligible (<reason>) — install via /onboard or /init  (eligible, not installed)
+  —  not eligible (<reason>)                              (no UI framework / RN / backend)
+
 CONFLICTS: [plugin A ↔ plugin B — overlap on X] or none
 BLOCKING: [issues] or none
 ACTION REQUIRED? YES / NO
 ```
 
+> ANIMATION LIB is **read-only** in this report. The advisor never installs
+> packages itself — it just states the status. Installation happens in
+> `/init-project` STEP 5e (auto) or `/onboard` STEP 2.5 (opt-in).
+
 ## PHASE 4 — AUTO-ACTIVATION (when called from /init-project or /ship-feature)
 
 After presenting RECOMMENDATIONS, if any plugin has ⚡ ENABLE status:

+ 4 - 0
agents/scaffolder.md

@@ -89,6 +89,10 @@ Add `COMPOSE_PROJECT_NAME=<slug>` to `.env.example`.
 On failure: read error → fix config → retry once → if still failing: report and stop.
 If DOCKER_RELEVANT: `docker --version && docker compose version` — failure is warning, not blocker.
 
+> **Note**: do NOT install `motion` / `motion-v` / animation libs here.
+> The init-project orchestrator handles that in STEP 5e via
+> `lib/animation-lib-check.sh`. Scaffolder stays language-agnostic.
+
 ---
 
 ## PHASE 5 — VERIFY BUILD

+ 117 - 0
lib/animation-lib-check.sh

@@ -0,0 +1,117 @@
+#!/usr/bin/env bash
+# ============================================================
+# lib/animation-lib-check.sh — Animation library detection
+# Sourced by: skills/init-project, skills/onboard, agents/plugin-advisor.
+#
+# Recommends `motion` (ex-`framer-motion`, rebranded Nov 2024) for
+# React-family / Svelte stacks, and `motion-v` for Vue 3 / Nuxt.
+#
+# Override the project root with: ANIM_PROJECT_ROOT=/path before sourcing.
+# ============================================================
+
+ANIM_PROJECT_ROOT="${ANIM_PROJECT_ROOT:-$PWD}"
+
+# Match a literal dep key in package.json (deps + devDeps + peerDeps).
+# Args: $1 = exact dep name. Returns 0 if found, 1 otherwise. No output.
+_anim_has_dep() {
+  local pkg_json="$ANIM_PROJECT_ROOT/package.json"
+  [ -f "$pkg_json" ] || return 1
+  # The closing escaped quote anchors the match so "react" does not
+  # collide with "react-native" or "react-dom".
+  grep -Eq "\"$1\"[[:space:]]*:" "$pkg_json"
+}
+
+# Decide whether the project can consume the motion library.
+# Outputs on stdout: '<status>|<package>|<reason>'
+#   status  : eligible | no
+#   package : motion | motion-v | -
+#   reason  : short human-readable note
+# Returns 0 if eligible, 1 if not.
+detect_anim_eligibility() {
+  local pkg_json="$ANIM_PROJECT_ROOT/package.json"
+
+  if [ ! -f "$pkg_json" ]; then
+    echo "no|-|no package.json (not a Node project)"
+    return 1
+  fi
+
+  # React Native / Expo are excluded: motion targets the DOM, RN apps
+  # should use react-native-reanimated instead.
+  if _anim_has_dep "react-native" || _anim_has_dep "expo"; then
+    echo "no|-|React Native stack — use react-native-reanimated"
+    return 1
+  fi
+
+  if _anim_has_dep "react" || _anim_has_dep "next" \
+     || _anim_has_dep "@remix-run/react" || _anim_has_dep "@astrojs/react"; then
+    echo "eligible|motion|React-family stack"
+    return 0
+  fi
+
+  if _anim_has_dep "vue" || _anim_has_dep "nuxt"; then
+    echo "eligible|motion-v|Vue 3 / Nuxt stack"
+    return 0
+  fi
+
+  if _anim_has_dep "svelte" || _anim_has_dep "@sveltejs/kit"; then
+    echo "eligible|motion|Svelte stack"
+    return 0
+  fi
+
+  if _anim_has_dep "astro"; then
+    echo "no|-|Astro without a React/Vue/Svelte integration"
+    return 1
+  fi
+
+  echo "no|-|no supported UI framework detected"
+  return 1
+}
+
+# Look for any installed animation library.
+# Outputs the detected package name on stdout (empty if none).
+# Returns 0 if found, 1 otherwise.
+is_anim_lib_installed() {
+  local pkg_json="$ANIM_PROJECT_ROOT/package.json"
+  [ -f "$pkg_json" ] || return 1
+
+  local libs=(
+    motion
+    motion-v
+    framer-motion
+    gsap
+    "@gsap/react"
+    lottie-react
+    react-spring
+    "@react-spring/web"
+    popmotion
+    "@formkit/auto-animate"
+  )
+
+  local lib
+  for lib in "${libs[@]}"; do
+    if _anim_has_dep "$lib"; then
+      echo "$lib"
+      return 0
+    fi
+  done
+  return 1
+}
+
+# Build the install command for the recommended package.
+# Args: $1 = package name (motion | motion-v).
+# Outputs the command on stdout. Returns 1 on missing arg.
+recommend_anim_install_cmd() {
+  local pkg="$1"
+  [ -z "$pkg" ] && return 1
+  local root="$ANIM_PROJECT_ROOT"
+
+  if [ -f "$root/pnpm-lock.yaml" ]; then
+    echo "pnpm add $pkg"
+  elif [ -f "$root/yarn.lock" ]; then
+    echo "yarn add $pkg"
+  elif [ -f "$root/bun.lockb" ] || [ -f "$root/bun.lock" ]; then
+    echo "bun add $pkg"
+  else
+    echo "npm install $pkg"
+  fi
+}

+ 1 - 0
lib/design-gate.md

@@ -21,6 +21,7 @@ Check BOTH the task description AND the filesystem:
 - `tailwind.config` or `postcss.config` present in project root
 - `tokens/`, `theme/`, or `design-system/` directory exists
 - Storybook config (`.storybook/`) present
+- Animation lib in `package.json` deps: `motion`, `motion-v`, `framer-motion` (legacy), `gsap`, `@gsap/react`, `lottie-react`, `react-spring`, `popmotion`, `@formkit/auto-animate`
 
 ## DECISION
 

+ 27 - 0
skills/init-project/SKILL.md

@@ -87,6 +87,33 @@ If `graphify` CLI is installed AND complexity >= 30%:
 3. Print: `🔗 Scaffold graph generated at graphify-out/`
 If `graphify` not installed or complexity < 30% → skip silently.
 
+## STEP 5e — ANIMATION LIB (auto-install)
+Install `motion` (ex-`framer-motion`, rebranded Nov 2024) when the stack supports it.
+The scaffold has just been validated by the user, so install proceeds silently.
+
+```bash
+source "$HOME/.claude/lib/animation-lib-check.sh"
+if result=$(detect_anim_eligibility); then
+  pkg=$(echo "$result" | cut -d'|' -f2)
+  if ! is_anim_lib_installed >/dev/null; then
+    cmd=$(recommend_anim_install_cmd "$pkg")
+    echo "🎬 Installing animation lib: $cmd"
+    eval "$cmd"
+  else
+    installed=$(is_anim_lib_installed)
+    echo "🎬 Animation lib already present: $installed — skipping install"
+  fi
+else
+  echo "🎬 Animation lib: stack not eligible — skipping ($(echo "$result" | cut -d'|' -f3))"
+fi
+```
+
+Rules:
+- `motion` for React-family / Svelte / vanilla JS stacks.
+- `motion-v` for Vue 3 / Nuxt.
+- React Native, Flutter, backend, embedded, static HTML → skipped.
+- If another animation lib (gsap, lottie-react, react-spring, …) is already present → skipped.
+
 ## STEP 6 — PLAN
 Invoke `superpowers:writing-plans` with BRIEF + skeleton.
 Granular tasks (2-5 min each), exact file paths, TDD: tests before code.

+ 32 - 0
skills/onboard/SKILL.md

@@ -103,6 +103,38 @@ Si `CLAUDE.md` existe déjà : lire son contenu, ne PAS écraser — fusionner a
 
 ---
 
+## STEP 2.5 — ANIMATION LIB (propose, opt-in)
+Vérifier si le stack peut consommer `motion` (ex-`framer-motion`, rebranded nov 2024)
+et proposer l'install si absent. **Aucun install sans confirmation utilisateur** —
+on ajoute une dep à un projet existant.
+
+```bash
+source "$HOME/.claude/lib/animation-lib-check.sh"
+result=$(detect_anim_eligibility)
+status=$(echo "$result" | cut -d'|' -f1)
+pkg=$(echo "$result" | cut -d'|' -f2)
+reason=$(echo "$result" | cut -d'|' -f3)
+```
+
+Cas :
+- **`status=eligible` AND aucune lib anim détectée** → proposer :
+  ```
+  🎬 ANIMATION LIB
+  Stack: <reason>. Aucune lib d'animation détectée.
+  Install `<cmd>` ? (yes / skip)
+  ```
+  Sur `yes` → exécuter `recommend_anim_install_cmd "$pkg"` puis confirmer.
+  Sur `skip` → continuer silencieusement.
+
+- **`status=eligible` AND une lib anim déjà présente** (motion, framer-motion, gsap, lottie, react-spring, popmotion, auto-animate) → log info uniquement :
+  ```
+  🎬 Animation lib déjà présente : <lib> — pas d'action.
+  ```
+
+- **`status=no`** → skip silencieusement (raison loggée seulement en mode verbose).
+
+---
+
 ## STEP 3 — DEEP INTERVIEW
 
 L'orchestrateur pilote directement l'interview (l'agent `interviewer.md` est laissé pour `/init-project` où le BRIEF format est attendu ; ici on reste en markdown libre dans la CLAUDE.md).