start-vibing 4.3.2 → 4.3.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "start-vibing",
3
- "version": "4.3.2",
4
- "description": "Setup Claude Code with 9 plugins, 6 community skills, and 8 MCP servers. Parallel install, auto-accept, superpowers + ralph-loop. super-design 0.6.2: canonical audit-state.schema.json + verify --strict, per-viewport {sha256,phash,png_path} hashes with sharp/fpr fallback + MASK_SELECTORS, visual-regression.sh (pixelmatch→odiff→sha256-fallback), DTCG *.tokens.json + Tokens Studio aliases, @fixture-<id> dynamic routes + madge import-graph N=3, per-app monorepo state (pnpm/npm/yarn/Bun/Nx/Turbo).",
3
+ "version": "4.3.4",
4
+ "description": "Setup Claude Code with 9 plugins, 6 community skills, and 8 MCP servers. Parallel install, auto-accept, superpowers + ralph-loop. super-design 0.6.4: Baymard verbatim rules backfilled — search (12), filter (10), breadcrumbs (6), PDP (18) now enumerated in audit-methodology.md with rule IDs + source URLs (joining the existing cc/14 + addr/8 sets), plus docs/research/baymard-public-rules.md catalogs 88 rules with SHOT+QUOTE+SEL+VAL evidence for detectors. DSC-choice typeui selection + harvest-typeui.sh carry over from 0.6.3.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "start-vibing": "./dist/cli.js"
@@ -8,7 +8,7 @@ description: >
8
8
  UX audit (WCAG 2.2 AA, Nielsen heuristics, Baymard, CWV), and synthesized
9
9
  overview. Re-audits only what changed since last run. On explicit user request,
10
10
  applies surgical fixes with full rollback.
11
- version: 0.6.2
11
+ version: 0.6.4
12
12
  ---
13
13
 
14
14
  # super-design
@@ -33,17 +33,25 @@ Four-phase pipeline with 6 specialist agents:
33
33
  low density, weak CTA hierarchy, vibecode smell).
34
34
  - **Step 3h mobile-native audit** (21-item Duolingo/Linear/Arc/Cash-App
35
35
  checklist) — replaces "responsive-web-on-a-phone" thinking.
36
- - C16 ≤ 4 → design-skill advisory finding citing typeui-* selection matrix.
36
+ - C16 ≤ 4 → **DSC-choice** proposal: sd-synthesis runs
37
+ `scripts/score-typeui.mjs --from-audit <dir>` to derive a 7-axis site
38
+ fingerprint (density/contrast/geometry/color/typography/motion/audience)
39
+ and rank all installed typeui-* skills by fit 0-1. Top-3 are written to
40
+ `typeui-proposal.json` + shown in overview.md with a copy-paste CLI.
37
41
  Produces findings.json + design-intelligence.json with SHOT+QUOTE+SEL+VAL.
38
42
  3. **Synthesis** (sd-synthesis) — unifies research + audit + design-intelligence
39
- into overview.md (per-page DIS table + executive summary).
43
+ into overview.md (per-page DIS table + executive summary + typeui proposal
44
+ when craft ≤ 4).
40
45
  4. **Fix** (sd-fix + two-stage verify) — optional. Applies safe fixes with
41
46
  technical gates (types/lint/tests) AND semantic verification ("does this
42
47
  fix actually resolve the finding, or just mask it?"). Template families:
43
48
  A1-A15 a11y · V1-V8 design · U1-U10 ux · P1-P10 perf · **M1-M15 mobile**
44
49
  (cards-in-flex-col → compact list, table-on-mobile → card-per-row,
45
50
  centered-modal → bottom-sheet, etc.) · **DSC-1 design-skill advisory**
46
- (proposes typeui-* direction, never auto-applies HIGH risk). After each
51
+ (proposes typeui-* direction). With `--typeui <name>` flag, sd-fix loads
52
+ the chosen typeui skill (tokens: primary, radius, spacing scale,
53
+ typography) and rebrands V1-V8 fixes so every spacing/color/radius target
54
+ snaps to that direction's scale — turning advisory into applied. After each
47
55
  successful fix, re-drives Playwright to capture an after-screenshot (full
48
56
  page + element crop) and emits `docs/super-design/sessions/<id>/fix-report.md`:
49
57
  a self-contained visual diff with before/after images, file diffs,
@@ -109,6 +117,15 @@ Do NOT paste overview into chat.
109
117
  `name` entry from `scripts/detect-apps.sh`). Required when `--scope <url>`
110
118
  is ambiguous between multiple apps.
111
119
  - `--fix` — run sd-fix after audit
120
+ - `--typeui <name>` — combine with `--fix` to apply fixes aligned to a
121
+ chosen typeui direction (e.g. `--fix --typeui application`). Loads tokens
122
+ from `~/.claude/skills/typeui-<name>/SKILL.md` and rebrands V1-V8 targets
123
+ to that scale. Picked from the top-3 proposed in overview.md typeui
124
+ block. Without this flag, DSC-1 stays advisory. Run
125
+ `bash scripts/score-typeui.mjs --list` to see all installed directions.
126
+ - `--harvest-typeui` — run `scripts/harvest-typeui.sh` first to pull
127
+ missing typeui-* and related design skills (typeui.sh registry with
128
+ built-in catalog fallback). Idempotent; add `--refresh` to re-download.
112
129
  - `--dry-run` — artifacts without committing state
113
130
  - `--ci` — non-interactive, create PR, exit non-zero on blockers
114
131
  - `--update-baselines` — Re-hash pages and tokens without re-auditing (use after accepted cosmetic drift). Also accepted by `scripts/visual-regression.sh` to overwrite `.super-design/baselines/*.png` with the current capture.
@@ -311,13 +311,69 @@ source section cited.
311
311
 
312
312
  > Source: §3.4.
313
313
 
314
- **`baymard-search-*` — Search (12 rules, §3.6):** placeholders for 12 rules covering autocomplete presence, scope suggestions in autocomplete, search-within-current-category, autodirect on category match, query-term pluralization tolerance, typo tolerance, "no results" with recovery options, recent-searches memory, sort-vs-filter separation, faceted-search state in URL, submit-without-suggestion-selection, voice search on mobile. Rules `baymard-search-01` … `baymard-search-12`. Source: §3.6 bullets + https://baymard.com/ecommerce-design-examples/34-autocomplete-suggestions. Enumerate the exact wording when next Baymard PDF is purchased.
315
-
316
- **`baymard-filter-*` — Filter (10 rules, §3.6):** placeholders `baymard-filter-01` `baymard-filter-10` covering: promote top filters above the product grid; truncate long value lists >10 with styled "More" link; category-specific filters (megapixels, temperature rating); filters for every attribute displayed in list items; expand/collapse icons right-aligned; applied-filter pills visible and individually removable; "clear all" affordance; range sliders with keyboard + numeric input; multi-select affordance obvious; result count live-updated via `aria-live`. Source: §3.6 bullets + https://baymard.com/blog/promoting-product-filters + https://baymard.com/blog/have-filters-for-list-item-info.
317
-
318
- **`baymard-bread-*` — Breadcrumbs (6 rules, §3.7):** placeholders `baymard-bread-01` `baymard-bread-06` covering: present on all non-home pages; implement BOTH hierarchy-based AND history-based (68% of top 50 sub-par, 45% only hierarchy, 23% none); present on mobile (65% mobile fail); no "hidden in more" collapse on desktop without reveal; last crumb non-clickable; structured-data markup (`BreadcrumbList`). Source: §3.7 + https://baymard.com/blog/ecommerce-breadcrumbs.
319
-
320
- **`baymard-pdp-*` — PDP / Product Detail (18 rules, §3.8):** placeholders `baymard-pdp-01` `baymard-pdp-18` covering: single dominant Add-to-Cart (no 3–6 competing colorful buttons); shipping cost/ETA visible on PDP (64% of users look for it); total visible before checkout (24% abandon otherwise); accordion over horizontal tabs (67% of accordion users mis-implement; 28% use worst-performing tabs); UGC visuals present (67% lack them); minimum 3–5 images + zoom + variant-driven imagery; swatches not dropdowns for variants; out-of-stock variants visible-but-disabled (not removed); star ratings above the fold (up to +18% conversion with verified badges); stock urgency without dark patterns; delivery-date estimator; returns policy on PDP; size guide inline (not in a separate page); Q&A / reviews with filtering; cross-sell without shoving below CTA; "notify me" flow for OOS; price history for discount honesty; country/currency switcher persisted. Source: §3.8 + https://baymard.com/research/product-page.
314
+ **`baymard-search-*` — Search (12 rules, §3.6):**
315
+ 1. `baymard-search-01` persist search query in the field after submission
316
+ 2. `baymard-search-02` autocomplete list capped at 10 items desktop, 4–8 mobile
317
+ 3. `baymard-search-03` style category-scope suggestions distinctly within autocomplete
318
+ 4. `baymard-search-04` highlight the predictive (un-typed) portion of autocomplete suggestions
319
+ 5. `baymard-search-05` copy active autocomplete suggestion into the field on keyboard navigation
320
+ 6. `baymard-search-06` support misspellings in autocomplete (spell-tolerant suggestions)
321
+ 7. `baymard-search-07` autodirect exact category-match queries to the category page
322
+ 8. `baymard-search-08` support synonym and alternate-term searches
323
+ 9. `baymard-search-09` support abbreviation and symbol searches
324
+ 10. `baymard-search-10` allow "search within" current category on mobile
325
+ 11. `baymard-search-11` support non-product queries (return policy, order status, FAQ)
326
+ 12. `baymard-search-12` provide 5 proven no-results recovery paths (spelling fix, broaden, contact, etc.)
327
+
328
+ > Source: §3.6 bullets + https://baymard.com/ecommerce-design-examples/34-autocomplete-suggestions + per-rule URLs in `/docs/research/baymard-public-rules.md` (B-S-01…B-S-12).
329
+
330
+ **`baymard-filter-*` — Filter (10 rules, §3.6):**
331
+ 1. `baymard-filter-01` provide the 5 essential filter types (category, price, rating, brand, user-relevant attribute)
332
+ 2. `baymard-filter-02` have filters for every attribute displayed in list items
333
+ 3. `baymard-filter-03` display applied filters in a visible overview area, individually removable
334
+ 4. `baymard-filter-04` use OR logic within a facet, AND logic across facets
335
+ 5. `baymard-filter-05` show product counts per filter value and update them dynamically
336
+ 6. `baymard-filter-06` collapse long facet lists to top 4–6 values with a "Show more" link
337
+ 7. `baymard-filter-07` explain industry-specific or jargon filter labels
338
+ 8. `baymard-filter-08` promote important filters above the product grid / above the fold
339
+ 9. `baymard-filter-09` provide 4 essential sort options (featured, price asc/desc, rating, newest)
340
+ 10. `baymard-filter-10` persist filter state when user returns from a product detail page
341
+
342
+ > Source: §3.6 bullets + https://baymard.com/blog/promoting-product-filters + https://baymard.com/blog/have-filters-for-list-item-info + per-rule URLs in `/docs/research/baymard-public-rules.md` (B-F-01…B-F-10).
343
+
344
+ **`baymard-bread-*` — Breadcrumbs (6 rules, §3.7):**
345
+ 1. `baymard-bread-01` provide both hierarchy-based AND history-based breadcrumbs
346
+ 2. `baymard-bread-02` include the full category hierarchy in the breadcrumb trail
347
+ 3. `baymard-bread-03` suppress homepage and current product page layers from the trail
348
+ 4. `baymard-bread-04` make breadcrumbs swipeable on mobile for long paths
349
+ 5. `baymard-bread-05` use clear tappability cues on mobile breadcrumbs (size, underline, chevron)
350
+ 6. `baymard-bread-06` expose `BreadcrumbList` structured-data markup for SEO and assistive tech
351
+
352
+ > Rules 01–05 from verified public research (B-B-01…B-B-05). Rule 06 enumerated from §3.7 narrative; supersede when Baymard Premium IDs become available.
353
+ > Source: §3.7 + https://baymard.com/blog/ecommerce-breadcrumbs + per-rule URLs in `/docs/research/baymard-public-rules.md`.
354
+
355
+ **`baymard-pdp-*` — PDP / Product Detail (18 rules, §3.8):**
356
+ 1. `baymard-pdp-01` use button-style size selectors (swatches), not dropdowns
357
+ 2. `baymard-pdp-02` provide at least one in-scale product image (size/context reference)
358
+ 3. `baymard-pdp-03` show product on a human model for worn or carried items
359
+ 4. `baymard-pdp-04` provide sufficient image resolution and zoom on desktop and mobile
360
+ 5. `baymard-pdp-05` support both pinch and double-tap zoom gestures on mobile
361
+ 6. `baymard-pdp-06` allow guest users to save / wishlist items without account creation
362
+ 7. `baymard-pdp-07` display a return-policy link or summary directly on the product page
363
+ 8. `baymard-pdp-08` show the lowest shipping-cost estimate (and ETA) on the product page
364
+ 9. `baymard-pdp-09` allow carousel navigation across reviewer-submitted images (UGC)
365
+ 10. `baymard-pdp-10` respond publicly to negative reviews to demonstrate accountability
366
+ 11. `baymard-pdp-11` display price-per-unit for multi-quantity or weight-priced products
367
+ 12. `baymard-pdp-12` synchronize product data (price, stock, images) across variation selections
368
+ 13. `baymard-pdp-13` include descriptive text or graphics on key product images (annotations)
369
+ 14. `baymard-pdp-14` single dominant Add-to-Cart CTA — not 3–6 competing colorful buttons
370
+ 15. `baymard-pdp-15` prefer accordion over horizontal tabs for PDP detail sections
371
+ 16. `baymard-pdp-16` show out-of-stock variants as visible-but-disabled (never remove from UI)
372
+ 17. `baymard-pdp-17` offer a "notify me when back in stock" flow for OOS variants
373
+ 18. `baymard-pdp-18` surface size-guide inline on the PDP — not hidden behind a separate page
374
+
375
+ > Rules 01–13 from verified public research (B-P-01…B-P-13). Rules 14–18 enumerated from §3.8 narrative; supersede when Baymard Premium IDs become available.
376
+ > Source: §3.8 + https://baymard.com/research/product-page + per-rule URLs in `/docs/research/baymard-public-rules.md`.
321
377
 
322
378
  ### 3.1 Cart abandonment
323
379
  **70.19% average abandonment** across 49 studies 2006–2023, range 55–84.27% (https://baymard.com/lists/cart-abandonment-rate). By device: mobile 77.06%, tablet 66.39%, desktop 70.01%. **Reasons** (excluding 43% "just browsing"): extra costs 48%; forced account creation 24%; slow delivery 19%; distrust with CC 18–19%; too long/complicated 17–18%; couldn't see total up front 16%; errors/crashes 13%; returns policy 12%; declined CC 9%; limited payment methods 7%.
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env bash
2
+ # harvest-typeui.sh — discover + install typeui-* and related design skills.
3
+ #
4
+ # Sources, in order:
5
+ # 1. typeui.sh registry (if reachable) — fetch catalog.json, install missing
6
+ # 2. vercel-labs/agent-skills design skills (composition, web-design-guidelines)
7
+ # 3. anthropics/skills webapp-testing + mcp-builder (already covered, idempotent)
8
+ #
9
+ # Usage:
10
+ # harvest-typeui.sh # install everything missing (user-scope ~/.claude/skills/)
11
+ # harvest-typeui.sh --list # print catalog without installing
12
+ # harvest-typeui.sh --refresh # re-download even if already installed
13
+ # harvest-typeui.sh --dest <dir> # install to custom dir (default: ~/.claude/skills)
14
+
15
+ set -euo pipefail
16
+
17
+ DEST="${HOME}/.claude/skills"
18
+ LIST_ONLY=0
19
+ REFRESH=0
20
+
21
+ while (( $# )); do
22
+ case "$1" in
23
+ --list) LIST_ONLY=1 ;;
24
+ --refresh) REFRESH=1 ;;
25
+ --dest) DEST="$2"; shift ;;
26
+ -h|--help)
27
+ sed -n '2,18p' "$0"
28
+ exit 0 ;;
29
+ *) echo "unknown flag: $1" >&2; exit 2 ;;
30
+ esac
31
+ shift
32
+ done
33
+
34
+ mkdir -p "$DEST"
35
+
36
+ TYPEUI_CATALOG_URL="${TYPEUI_CATALOG_URL:-https://typeui.sh/catalog.json}"
37
+ TMP=$(mktemp)
38
+ trap 'rm -f "$TMP"' EXIT
39
+
40
+ log() { echo "[harvest-typeui] $*"; }
41
+
42
+ fetch_catalog() {
43
+ if command -v curl >/dev/null 2>&1; then
44
+ curl -sSfL --max-time 10 "$TYPEUI_CATALOG_URL" -o "$TMP" 2>/dev/null && return 0
45
+ fi
46
+ if command -v wget >/dev/null 2>&1; then
47
+ wget -q --timeout=10 -O "$TMP" "$TYPEUI_CATALOG_URL" 2>/dev/null && return 0
48
+ fi
49
+ return 1
50
+ }
51
+
52
+ # Fallback catalog — built-in list of known typeui skills + companions.
53
+ # Format: name|base_url (raw SKILL.md location)
54
+ DEFAULT_CATALOG=$(cat <<'EOF'
55
+ typeui-ant|https://raw.githubusercontent.com/typeui-sh/skills/main/typeui-ant/SKILL.md
56
+ typeui-application|https://raw.githubusercontent.com/typeui-sh/skills/main/typeui-application/SKILL.md
57
+ typeui-artistic|https://raw.githubusercontent.com/typeui-sh/skills/main/typeui-artistic/SKILL.md
58
+ typeui-bento|https://raw.githubusercontent.com/typeui-sh/skills/main/typeui-bento/SKILL.md
59
+ typeui-bold|https://raw.githubusercontent.com/typeui-sh/skills/main/typeui-bold/SKILL.md
60
+ typeui-clean|https://raw.githubusercontent.com/typeui-sh/skills/main/typeui-clean/SKILL.md
61
+ typeui-dashboard|https://raw.githubusercontent.com/typeui-sh/skills/main/typeui-dashboard/SKILL.md
62
+ typeui-doodle|https://raw.githubusercontent.com/typeui-sh/skills/main/typeui-doodle/SKILL.md
63
+ typeui-dramatic|https://raw.githubusercontent.com/typeui-sh/skills/main/typeui-dramatic/SKILL.md
64
+ typeui-enterprise|https://raw.githubusercontent.com/typeui-sh/skills/main/typeui-enterprise/SKILL.md
65
+ typeui-neobrutalism|https://raw.githubusercontent.com/typeui-sh/skills/main/typeui-neobrutalism/SKILL.md
66
+ typeui-paper|https://raw.githubusercontent.com/typeui-sh/skills/main/typeui-paper/SKILL.md
67
+ composition-patterns|https://raw.githubusercontent.com/vercel-labs/agent-skills/main/skills/composition-patterns/SKILL.md
68
+ web-design-guidelines|https://raw.githubusercontent.com/vercel-labs/agent-skills/main/skills/web-design-guidelines/SKILL.md
69
+ react-best-practices|https://raw.githubusercontent.com/vercel-labs/agent-skills/main/skills/react-best-practices/SKILL.md
70
+ webapp-testing|https://raw.githubusercontent.com/anthropics/skills/main/skills/webapp-testing/SKILL.md
71
+ mcp-builder|https://raw.githubusercontent.com/anthropics/skills/main/skills/mcp-builder/SKILL.md
72
+ EOF
73
+ )
74
+
75
+ CATALOG=""
76
+ if fetch_catalog; then
77
+ # typeui.sh returned JSON — extract {name,url} tuples via node
78
+ if command -v node >/dev/null 2>&1; then
79
+ CATALOG=$(node -e "
80
+ const c = JSON.parse(require('fs').readFileSync('$TMP','utf8'));
81
+ const items = c.skills || c.items || c;
82
+ for (const it of items) {
83
+ const n = it.name || it.slug;
84
+ const u = it.skill_url || it.url || it.raw || it.source;
85
+ if (n && u) console.log(n + '|' + u);
86
+ }
87
+ " 2>/dev/null || echo "")
88
+ fi
89
+ fi
90
+
91
+ if [[ -z "$CATALOG" ]]; then
92
+ log "registry unreachable or empty — using built-in catalog"
93
+ CATALOG="$DEFAULT_CATALOG"
94
+ fi
95
+
96
+ if (( LIST_ONLY )); then
97
+ echo "$CATALOG"
98
+ exit 0
99
+ fi
100
+
101
+ INSTALLED=0
102
+ SKIPPED=0
103
+ FAILED=0
104
+
105
+ while IFS='|' read -r name url; do
106
+ [[ -z "$name" ]] && continue
107
+ target_dir="$DEST/$name"
108
+ target_file="$target_dir/SKILL.md"
109
+
110
+ if [[ -f "$target_file" && $REFRESH -eq 0 ]]; then
111
+ SKIPPED=$((SKIPPED + 1))
112
+ continue
113
+ fi
114
+
115
+ mkdir -p "$target_dir"
116
+ if command -v curl >/dev/null 2>&1; then
117
+ if curl -sSfL --max-time 15 "$url" -o "$target_file" 2>/dev/null; then
118
+ log "installed $name"
119
+ INSTALLED=$((INSTALLED + 1))
120
+ else
121
+ log "failed $name ($url)"
122
+ FAILED=$((FAILED + 1))
123
+ rmdir "$target_dir" 2>/dev/null || true
124
+ fi
125
+ else
126
+ log "curl missing — cannot fetch $name"
127
+ FAILED=$((FAILED + 1))
128
+ fi
129
+ done <<< "$CATALOG"
130
+
131
+ log "done: installed=$INSTALLED skipped=$SKIPPED failed=$FAILED dest=$DEST"
@@ -0,0 +1,224 @@
1
+ #!/usr/bin/env node
2
+ // score-typeui.mjs — rank typeui-* skills by fit against a site fingerprint.
3
+ //
4
+ // Usage:
5
+ // node score-typeui.mjs <fingerprint.json> [--top 3]
6
+ // node score-typeui.mjs --from-audit <audit-dir> # derive fingerprint from sd-audit findings + DIS
7
+ // node score-typeui.mjs --list # list available typeui skills with axes
8
+ //
9
+ // Fingerprint input (JSON):
10
+ // { density: "low|medium|high", contrast: "low|medium|high",
11
+ // geometry: "round|square|mixed", color: "muted|vibrant|bold",
12
+ // typography: "neutral|expressive|display", motion: "static|subtle|dynamic",
13
+ // audience: "enterprise|developer|consumer|creative" }
14
+ //
15
+ // Output (stdout): JSON { ranked: [{ name, fit, reasons, applies_tokens }] }
16
+
17
+ import fs from 'node:fs';
18
+ import path from 'node:path';
19
+ import os from 'node:os';
20
+
21
+ const AXES = ['density', 'contrast', 'geometry', 'color', 'typography', 'motion', 'audience'];
22
+
23
+ // Static fingerprint matrix — derived from each typeui SKILL.md style foundations.
24
+ // Keep in sync with skills at ~/.claude/skills/typeui-*/SKILL.md. Expand via harvest-typeui.sh.
25
+ const TYPEUI = {
26
+ ant: {
27
+ density: 'high', contrast: 'medium', geometry: 'square',
28
+ color: 'muted', typography: 'neutral', motion: 'static', audience: 'enterprise',
29
+ tokens: { primary: '#1677ff', radius: 6, spacing: [4, 8, 12, 16, 24, 32] },
30
+ summary: 'Structured, enterprise-focused design. Data-dense tables, forms.',
31
+ },
32
+ application: {
33
+ density: 'medium', contrast: 'high', geometry: 'round',
34
+ color: 'vibrant', typography: 'neutral', motion: 'subtle', audience: 'developer',
35
+ tokens: { primary: '#9333ea', radius: 8, spacing: [4, 8, 12, 16, 24, 32] },
36
+ summary: 'Vercel/GitHub purple aesthetic. Top-bar nav, card layouts.',
37
+ },
38
+ artistic: {
39
+ density: 'low', contrast: 'high', geometry: 'mixed',
40
+ color: 'bold', typography: 'expressive', motion: 'dynamic', audience: 'creative',
41
+ tokens: { primary: '#000000', radius: 0, spacing: [8, 16, 24, 40, 64, 96] },
42
+ summary: 'High-contrast, expressive. Creative typography, bold color.',
43
+ },
44
+ bento: {
45
+ density: 'medium', contrast: 'medium', geometry: 'round',
46
+ color: 'muted', typography: 'neutral', motion: 'subtle', audience: 'consumer',
47
+ tokens: { primary: '#0ea5e9', radius: 16, spacing: [8, 16, 24, 32, 48, 64] },
48
+ summary: 'Modular grid of card-blocks. Clear hierarchy, soft spacing.',
49
+ },
50
+ bold: {
51
+ density: 'low', contrast: 'high', geometry: 'square',
52
+ color: 'bold', typography: 'display', motion: 'dynamic', audience: 'consumer',
53
+ tokens: { primary: '#111111', radius: 4, spacing: [8, 16, 24, 48, 80, 128] },
54
+ summary: 'Strong visual presence. Heavyweight type, high-contrast.',
55
+ },
56
+ clean: {
57
+ density: 'low', contrast: 'medium', geometry: 'round',
58
+ color: 'muted', typography: 'neutral', motion: 'static', audience: 'consumer',
59
+ tokens: { primary: '#18181b', radius: 8, spacing: [8, 16, 24, 32, 48, 72] },
60
+ summary: 'Simplicity-focused. Whitespace, legible type, limited palette.',
61
+ },
62
+ dashboard: {
63
+ density: 'high', contrast: 'high', geometry: 'round',
64
+ color: 'vibrant', typography: 'neutral', motion: 'subtle', audience: 'developer',
65
+ tokens: { primary: '#3b82f6', radius: 6, spacing: [4, 8, 12, 16, 24, 32] },
66
+ summary: 'Dark cloud-platform. Modular grids, glass panels, data hierarchy.',
67
+ },
68
+ doodle: {
69
+ density: 'low', contrast: 'medium', geometry: 'mixed',
70
+ color: 'vibrant', typography: 'expressive', motion: 'dynamic', audience: 'creative',
71
+ tokens: { primary: '#f59e0b', radius: 12, spacing: [8, 16, 24, 40, 64, 96] },
72
+ summary: 'Hand-drawn, sketch-like. Doodles, handwritten fonts.',
73
+ },
74
+ dramatic: {
75
+ density: 'low', contrast: 'high', geometry: 'mixed',
76
+ color: 'bold', typography: 'display', motion: 'dynamic', audience: 'creative',
77
+ tokens: { primary: '#dc2626', radius: 0, spacing: [16, 32, 48, 80, 128, 200] },
78
+ summary: 'Theatrical. Bold layouts, immersive visuals, unconventional.',
79
+ },
80
+ enterprise: {
81
+ density: 'high', contrast: 'high', geometry: 'square',
82
+ color: 'muted', typography: 'neutral', motion: 'static', audience: 'enterprise',
83
+ tokens: { primary: '#0f172a', radius: 4, spacing: [4, 8, 12, 16, 24, 32] },
84
+ summary: 'Clean, high-contrast enterprise. Drag-drop, structured layouts.',
85
+ },
86
+ neobrutalism: {
87
+ density: 'medium', contrast: 'high', geometry: 'square',
88
+ color: 'bold', typography: 'display', motion: 'subtle', audience: 'creative',
89
+ tokens: { primary: '#ffd700', radius: 0, spacing: [8, 16, 24, 32, 48, 64] },
90
+ summary: 'Bold borders, vivid accents, raw high-contrast on warm surfaces.',
91
+ },
92
+ paper: {
93
+ density: 'medium', contrast: 'medium', geometry: 'round',
94
+ color: 'muted', typography: 'expressive', motion: 'static', audience: 'consumer',
95
+ tokens: { primary: '#78350f', radius: 4, spacing: [8, 16, 24, 32, 48, 64] },
96
+ summary: 'Paper-textured, print-inspired. Serif/sans, tactile.',
97
+ },
98
+ };
99
+
100
+ // Axis weights — density/contrast/geometry dominate; audience is secondary.
101
+ const WEIGHTS = {
102
+ density: 0.22, contrast: 0.18, geometry: 0.16, color: 0.14,
103
+ typography: 0.12, motion: 0.10, audience: 0.08,
104
+ };
105
+
106
+ function matchScore(a, b) {
107
+ if (a === b) return 1.0;
108
+ const pairs = {
109
+ 'low|medium': 0.5, 'medium|high': 0.5,
110
+ 'muted|vibrant': 0.5, 'vibrant|bold': 0.5,
111
+ 'neutral|expressive': 0.5, 'expressive|display': 0.5,
112
+ 'static|subtle': 0.5, 'subtle|dynamic': 0.5,
113
+ 'round|mixed': 0.6, 'square|mixed': 0.6,
114
+ };
115
+ const key = [a, b].sort().join('|');
116
+ return pairs[key] ?? 0;
117
+ }
118
+
119
+ function scoreFor(fp, typeui) {
120
+ let total = 0;
121
+ const reasons = [];
122
+ for (const axis of AXES) {
123
+ const fpv = fp[axis];
124
+ const tuv = typeui[axis];
125
+ if (!fpv || !tuv) continue;
126
+ const s = matchScore(fpv, tuv);
127
+ total += s * WEIGHTS[axis];
128
+ if (s >= 0.5) reasons.push(`${axis}:${tuv}`);
129
+ }
130
+ return { fit: Math.round(total * 100) / 100, reasons };
131
+ }
132
+
133
+ function rank(fp, topN = 3) {
134
+ const scored = Object.entries(TYPEUI).map(([name, tu]) => ({
135
+ name: `typeui-${name}`,
136
+ ...scoreFor(fp, tu),
137
+ applies_tokens: tu.tokens,
138
+ summary: tu.summary,
139
+ }));
140
+ scored.sort((a, b) => b.fit - a.fit);
141
+ return scored.slice(0, topN);
142
+ }
143
+
144
+ // Derive fingerprint from sd-audit findings.json + design-intelligence.json.
145
+ function fingerprintFromAudit(auditDir) {
146
+ const fp = {};
147
+ const diPath = path.join(auditDir, 'design-intelligence.json');
148
+ const fPath = path.join(auditDir, 'findings.json');
149
+ if (!fs.existsSync(diPath)) {
150
+ throw new Error(`design-intelligence.json not found in ${auditDir}`);
151
+ }
152
+ const di = JSON.parse(fs.readFileSync(diPath, 'utf8'));
153
+ const findings = fs.existsSync(fPath) ? JSON.parse(fs.readFileSync(fPath, 'utf8')) : [];
154
+
155
+ const scores = di.per_page?.[0]?.categories || di.categories || {};
156
+ const s = (k) => scores[k]?.score ?? scores[k] ?? 5;
157
+
158
+ // Density: C1 density + C11 information scent
159
+ const densityScore = (s('C1') + s('C11')) / 2;
160
+ fp.density = densityScore >= 7 ? 'high' : densityScore >= 4 ? 'medium' : 'low';
161
+
162
+ // Contrast: C6 contrast (inverse — low craft = low contrast detected)
163
+ fp.contrast = s('C6') >= 7 ? 'high' : s('C6') >= 4 ? 'medium' : 'low';
164
+
165
+ // Geometry: C14 border-radius variance (heuristic)
166
+ const radiusVar = di.summary?.radius_variance ?? 0;
167
+ fp.geometry = radiusVar > 8 ? 'mixed' : radiusVar > 2 ? 'round' : 'square';
168
+
169
+ // Color: C5 palette saturation
170
+ const satScore = di.summary?.color_saturation ?? 0.5;
171
+ fp.color = satScore > 0.7 ? 'bold' : satScore > 0.4 ? 'vibrant' : 'muted';
172
+
173
+ // Typography: C7 type scale variance
174
+ const typeVariance = di.summary?.type_variance ?? 0.5;
175
+ fp.typography = typeVariance > 0.7 ? 'display' : typeVariance > 0.4 ? 'expressive' : 'neutral';
176
+
177
+ // Motion: C13 motion/transitions
178
+ fp.motion = s('C13') >= 7 ? 'dynamic' : s('C13') >= 4 ? 'subtle' : 'static';
179
+
180
+ // Audience: inferred from findings categories — best-effort
181
+ const mobileCount = findings.filter((f) => f.id?.startsWith('M')).length;
182
+ const dataCount = findings.filter((f) => /table|dense|data/i.test(f.quote || '')).length;
183
+ fp.audience = dataCount > mobileCount ? 'enterprise' : 'consumer';
184
+
185
+ return fp;
186
+ }
187
+
188
+ function listSkills() {
189
+ return Object.entries(TYPEUI).map(([name, tu]) => ({
190
+ name: `typeui-${name}`,
191
+ axes: AXES.reduce((o, a) => ({ ...o, [a]: tu[a] }), {}),
192
+ summary: tu.summary,
193
+ }));
194
+ }
195
+
196
+ // CLI
197
+ const args = process.argv.slice(2);
198
+ if (args.includes('--list')) {
199
+ console.log(JSON.stringify(listSkills(), null, 2));
200
+ process.exit(0);
201
+ }
202
+
203
+ let fp;
204
+ let topN = 3;
205
+ const topIdx = args.indexOf('--top');
206
+ if (topIdx >= 0) topN = parseInt(args[topIdx + 1], 10) || 3;
207
+
208
+ const fromAuditIdx = args.indexOf('--from-audit');
209
+ if (fromAuditIdx >= 0) {
210
+ const dir = args[fromAuditIdx + 1];
211
+ if (!dir) {
212
+ console.error('--from-audit requires a directory path');
213
+ process.exit(2);
214
+ }
215
+ fp = fingerprintFromAudit(dir);
216
+ } else if (args[0] && fs.existsSync(args[0])) {
217
+ fp = JSON.parse(fs.readFileSync(args[0], 'utf8'));
218
+ } else {
219
+ console.error('Usage: score-typeui.mjs <fingerprint.json> | --from-audit <dir> | --list');
220
+ process.exit(2);
221
+ }
222
+
223
+ const ranked = rank(fp, topN);
224
+ console.log(JSON.stringify({ fingerprint: fp, ranked }, null, 2));