@champpaba/claude-agent-kit 1.1.0 → 1.1.1

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 (82) hide show
  1. package/.claude/CHANGELOG-v1.1.1.md +259 -0
  2. package/.claude/CLAUDE.md +555 -0
  3. package/.claude/agents/01-integration.md +797 -0
  4. package/.claude/agents/02-uxui-frontend.md +955 -0
  5. package/.claude/agents/03-test-debug.md +759 -0
  6. package/.claude/agents/04-frontend.md +1099 -0
  7. package/.claude/agents/05-backend.md +1217 -0
  8. package/.claude/agents/06-database.md +969 -0
  9. package/.claude/commands/agentsetup.md +1464 -0
  10. package/.claude/commands/cdev.md +327 -0
  11. package/.claude/commands/csetup.md +447 -0
  12. package/.claude/commands/cstatus.md +60 -0
  13. package/.claude/commands/cview.md +364 -0
  14. package/.claude/commands/designsetup.md +838 -0
  15. package/.claude/commands/extract.md +1005 -0
  16. package/.claude/commands/pageplan.md +327 -0
  17. package/.claude/commands/psetup.md +101 -0
  18. package/.claude/contexts/design/accessibility.md +611 -0
  19. package/.claude/contexts/design/box-thinking.md +566 -0
  20. package/.claude/contexts/design/color-theory.md +519 -0
  21. package/.claude/contexts/design/index.md +290 -0
  22. package/.claude/contexts/design/layout.md +400 -0
  23. package/.claude/contexts/design/responsive.md +551 -0
  24. package/.claude/contexts/design/shadows.md +522 -0
  25. package/.claude/contexts/design/spacing.md +445 -0
  26. package/.claude/contexts/design/typography.md +465 -0
  27. package/.claude/contexts/domain/README.md +164 -0
  28. package/.claude/contexts/patterns/agent-coordination.md +388 -0
  29. package/.claude/contexts/patterns/agent-discovery.md +182 -0
  30. package/.claude/contexts/patterns/change-workflow.md +538 -0
  31. package/.claude/contexts/patterns/code-standards.md +515 -0
  32. package/.claude/contexts/patterns/development-principles.md +513 -0
  33. package/.claude/contexts/patterns/error-handling.md +478 -0
  34. package/.claude/contexts/patterns/error-recovery.md +365 -0
  35. package/.claude/contexts/patterns/frontend-component-strategy.md +365 -0
  36. package/.claude/contexts/patterns/git-workflow.md +207 -0
  37. package/.claude/contexts/patterns/logging.md +424 -0
  38. package/.claude/contexts/patterns/task-breakdown.md +452 -0
  39. package/.claude/contexts/patterns/task-classification.md +523 -0
  40. package/.claude/contexts/patterns/tdd-classification.md +516 -0
  41. package/.claude/contexts/patterns/testing.md +413 -0
  42. package/.claude/contexts/patterns/ui-component-consistency.md +304 -0
  43. package/.claude/contexts/patterns/validation-framework.md +776 -0
  44. package/.claude/lib/README.md +39 -0
  45. package/.claude/lib/agent-executor.md +258 -0
  46. package/.claude/lib/agent-router.md +572 -0
  47. package/.claude/lib/flags-updater.md +469 -0
  48. package/.claude/lib/tdd-classifier.md +345 -0
  49. package/.claude/lib/validation-gates.md +484 -0
  50. package/.claude/settings.local.json +42 -0
  51. package/.claude/templates/STYLE_GUIDE.template.md +879 -0
  52. package/.claude/templates/context-template.md +45 -0
  53. package/.claude/templates/flags-template.json +42 -0
  54. package/.claude/templates/page-plan-example.md +562 -0
  55. package/.claude/templates/phase-templates.json +124 -0
  56. package/.claude/templates/phases-sections/accessibility-test.md +17 -0
  57. package/.claude/templates/phases-sections/api-design.md +37 -0
  58. package/.claude/templates/phases-sections/backend-tests.md +16 -0
  59. package/.claude/templates/phases-sections/backend.md +37 -0
  60. package/.claude/templates/phases-sections/business-logic-validation.md +16 -0
  61. package/.claude/templates/phases-sections/component-tests.md +17 -0
  62. package/.claude/templates/phases-sections/contract-backend.md +16 -0
  63. package/.claude/templates/phases-sections/contract-frontend.md +16 -0
  64. package/.claude/templates/phases-sections/database.md +35 -0
  65. package/.claude/templates/phases-sections/documentation.md +17 -0
  66. package/.claude/templates/phases-sections/e2e-tests.md +16 -0
  67. package/.claude/templates/phases-sections/fix-implementation.md +17 -0
  68. package/.claude/templates/phases-sections/frontend-integration.md +18 -0
  69. package/.claude/templates/phases-sections/frontend-mockup.md +123 -0
  70. package/.claude/templates/phases-sections/manual-flow-test.md +15 -0
  71. package/.claude/templates/phases-sections/manual-ux-test.md +16 -0
  72. package/.claude/templates/phases-sections/refactor-implementation.md +17 -0
  73. package/.claude/templates/phases-sections/refactor.md +16 -0
  74. package/.claude/templates/phases-sections/regression-tests.md +15 -0
  75. package/.claude/templates/phases-sections/report.md +16 -0
  76. package/.claude/templates/phases-sections/responsive-test.md +16 -0
  77. package/.claude/templates/phases-sections/script-implementation.md +43 -0
  78. package/.claude/templates/phases-sections/test-coverage.md +16 -0
  79. package/.claude/templates/phases-sections/user-approval.md +14 -0
  80. package/lib/init.js +1 -1
  81. package/lib/update.js +1 -1
  82. package/package.json +2 -2
@@ -0,0 +1,1005 @@
1
+ # /extract - Extract Design System from Website (Component-Level)
2
+
3
+ You are an expert design systems engineer with deep knowledge of CSS, animations, and UX patterns.
4
+
5
+ Your task is to extract comprehensive design data from a website and save it as reusable YAML files with component-level detail.
6
+
7
+ ---
8
+
9
+ ## 📖 Usage
10
+
11
+ ```bash
12
+ /extract <URL>
13
+
14
+ Arguments:
15
+ URL Required. Website URL to extract from
16
+
17
+ Examples:
18
+ /extract https://airbnb.com
19
+ /extract https://blackbird.com
20
+ /extract https://linear.app
21
+ ```
22
+
23
+ ---
24
+
25
+ ## 🎯 Mission
26
+
27
+ Extract ALL design data from a website and save to `design-system/extracted/{site-name}/`:
28
+ - `data.yaml` - Complete 17-section design data + animations
29
+ - `analysis.md` - Psychology & design philosophy analysis
30
+ - `screenshots/` - Component screenshots (default + hover/focus states)
31
+
32
+ **Key Principles:**
33
+ 1. **Component-Level Detail**: Extract every component type with all states
34
+ 2. **Animation Capture**: Before/after screenshots for all interactive states
35
+ 3. **17 Sections Mandatory**: Template-based extraction (fallback if not found)
36
+ 4. **Reusable**: Output can be used by multiple projects
37
+
38
+ ---
39
+
40
+ ## 🔍 STEP 0: Parse Input & Setup
41
+
42
+ ```javascript
43
+ // Parse URL
44
+ const input = args[0];
45
+ if (!input) {
46
+ return error('URL required. Usage: /extract https://airbnb.com');
47
+ }
48
+
49
+ // Normalize URL
50
+ let url = input.trim();
51
+ if (!url.startsWith('http://') && !url.startsWith('https://')) {
52
+ url = 'https://' + url;
53
+ }
54
+
55
+ // Auto-detect site name
56
+ const siteName = new URL(url).hostname
57
+ .replace('www.', '')
58
+ .replace(/\.[^.]+$/, ''); // Remove TLD
59
+ // e.g., "airbnb.com" → "airbnb"
60
+
61
+ // Check if already extracted
62
+ const extractedPath = `design-system/extracted/${siteName}`;
63
+ if (exists(extractedPath + '/data.yaml')) {
64
+ const existingData = YAML.parse(Read(extractedPath + '/data.yaml'));
65
+ const extractedDate = existingData.meta.extracted_at;
66
+
67
+ const response = await AskUserQuestion({
68
+ questions: [{
69
+ question: `Site "${siteName}" was already extracted on ${extractedDate}. Re-extract?`,
70
+ header: "Re-extract?",
71
+ multiSelect: false,
72
+ options: [
73
+ { label: "Yes, re-extract", description: "Overwrite previous data" },
74
+ { label: "No, cancel", description: "Keep existing data" }
75
+ ]
76
+ }]
77
+ });
78
+
79
+ if (response.answers["Re-extract?"] === "No, cancel") {
80
+ return output('Extraction cancelled. Existing data preserved.');
81
+ }
82
+ }
83
+
84
+ // Create directories
85
+ Bash: mkdir -p design-system/extracted/${siteName}/screenshots
86
+ ```
87
+
88
+ **Report:**
89
+ ```
90
+ 🚀 Extraction Started
91
+
92
+ 📍 URL: ${url}
93
+ 📁 Site: ${siteName}
94
+ 📂 Output: design-system/extracted/${siteName}/
95
+
96
+ ⏳ Navigating to site...
97
+ ```
98
+
99
+ ---
100
+
101
+ ## STEP 1: Navigate & Wait
102
+
103
+ ```javascript
104
+ // Navigate
105
+ await mcp__chrome-devtools__navigate_page({ url });
106
+
107
+ // Smart wait
108
+ try {
109
+ const snapshot = await mcp__chrome-devtools__take_snapshot({ verbose: false });
110
+
111
+ // Find main heading
112
+ const headings = snapshot.split('\n')
113
+ .filter(line => line.includes('[heading]'))
114
+ .slice(0, 3);
115
+
116
+ if (headings.length > 0) {
117
+ const mainText = headings[0].split('"')[1]; // Extract text
118
+
119
+ await mcp__chrome-devtools__wait_for({
120
+ text: mainText,
121
+ timeout: 15000
122
+ });
123
+ } else {
124
+ // Fallback: fixed wait
125
+ await sleep(5000);
126
+ }
127
+ } catch {
128
+ await sleep(5000);
129
+ }
130
+
131
+ // Verify loaded
132
+ const readyState = await mcp__chrome-devtools__evaluate_script({
133
+ function: '() => document.readyState'
134
+ });
135
+
136
+ if (readyState !== 'complete') {
137
+ await sleep(3000);
138
+ }
139
+ ```
140
+
141
+ **Report:**
142
+ ```
143
+ ✅ Page loaded successfully
144
+
145
+ 🔄 Extracting CSS data (17 sections)...
146
+ ```
147
+
148
+ ---
149
+
150
+ ## STEP 2: Extract CSS Data (17 Sections in Parallel)
151
+
152
+ Run all extraction scripts in parallel for speed:
153
+
154
+ ```javascript
155
+ const extractionPromises = [
156
+ extractColors(),
157
+ extractTypography(),
158
+ extractShadows(),
159
+ extractSpacing(),
160
+ extractButtons(),
161
+ extractCards(),
162
+ extractInputs(),
163
+ extractAnimations()
164
+ ];
165
+
166
+ const [
167
+ colors,
168
+ typography,
169
+ shadows,
170
+ spacing,
171
+ buttons,
172
+ cards,
173
+ inputs,
174
+ animations
175
+ ] = await Promise.all(extractionPromises);
176
+ ```
177
+
178
+ ### 2.1: Extract Colors
179
+
180
+ ```javascript
181
+ async function extractColors() {
182
+ return await mcp__chrome-devtools__evaluate_script({
183
+ function: `() => {
184
+ const allElements = document.querySelectorAll('*');
185
+ const colors = {
186
+ backgrounds: new Set(),
187
+ texts: new Set(),
188
+ borders: new Set()
189
+ };
190
+
191
+ allElements.forEach(el => {
192
+ const s = window.getComputedStyle(el);
193
+ if (s.backgroundColor && s.backgroundColor !== 'rgba(0, 0, 0, 0)') {
194
+ colors.backgrounds.add(s.backgroundColor);
195
+ }
196
+ if (s.color) colors.texts.add(s.color);
197
+ if (s.borderColor && s.borderColor !== 'rgba(0, 0, 0, 0)') {
198
+ colors.borders.add(s.borderColor);
199
+ }
200
+ });
201
+
202
+ // Convert RGB to HEX
203
+ const rgbToHex = (rgb) => {
204
+ const match = rgb.match(/\\d+/g);
205
+ if (!match) return rgb;
206
+ const hex = match.slice(0, 3).map(x => {
207
+ const h = parseInt(x).toString(16);
208
+ return h.length === 1 ? '0' + h : h;
209
+ });
210
+ return '#' + hex.join('').toUpperCase();
211
+ };
212
+
213
+ return {
214
+ backgrounds: Array.from(colors.backgrounds).slice(0, 30).map(c => ({
215
+ rgb: c,
216
+ hex: rgbToHex(c)
217
+ })),
218
+ texts: Array.from(colors.texts).slice(0, 20).map(c => ({
219
+ rgb: c,
220
+ hex: rgbToHex(c)
221
+ })),
222
+ borders: Array.from(colors.borders).slice(0, 15).map(c => ({
223
+ rgb: c,
224
+ hex: rgbToHex(c)
225
+ }))
226
+ };
227
+ }`
228
+ });
229
+ }
230
+ ```
231
+
232
+ ### 2.2: Extract Typography
233
+
234
+ ```javascript
235
+ async function extractTypography() {
236
+ return await mcp__chrome-devtools__evaluate_script({
237
+ function: `() => {
238
+ const fonts = new Set();
239
+ const weights = new Set();
240
+ const sizes = new Set();
241
+
242
+ const typography = {
243
+ h1: [],
244
+ h2: [],
245
+ h3: [],
246
+ body: []
247
+ };
248
+
249
+ // Headings
250
+ ['h1', 'h2', 'h3'].forEach(tag => {
251
+ Array.from(document.querySelectorAll(tag)).slice(0, 3).forEach(el => {
252
+ const s = window.getComputedStyle(el);
253
+ typography[tag].push({
254
+ text: el.textContent.trim().substring(0, 50),
255
+ fontSize: s.fontSize,
256
+ fontWeight: s.fontWeight,
257
+ fontFamily: s.fontFamily,
258
+ lineHeight: s.lineHeight,
259
+ letterSpacing: s.letterSpacing,
260
+ textTransform: s.textTransform,
261
+ color: s.color
262
+ });
263
+ fonts.add(s.fontFamily);
264
+ weights.add(s.fontWeight);
265
+ sizes.add(s.fontSize);
266
+ });
267
+ });
268
+
269
+ // Body text
270
+ Array.from(document.querySelectorAll('p, div, span')).slice(0, 20).forEach(el => {
271
+ const s = window.getComputedStyle(el);
272
+ if (el.textContent.trim().length > 20) {
273
+ typography.body.push({
274
+ fontSize: s.fontSize,
275
+ fontWeight: s.fontWeight,
276
+ lineHeight: s.lineHeight,
277
+ fontFamily: s.fontFamily,
278
+ color: s.color
279
+ });
280
+ fonts.add(s.fontFamily);
281
+ weights.add(s.fontWeight);
282
+ sizes.add(s.fontSize);
283
+ }
284
+ });
285
+
286
+ return {
287
+ ...typography,
288
+ allFonts: Array.from(fonts),
289
+ allWeights: Array.from(weights).sort((a, b) => parseInt(a) - parseInt(b)),
290
+ allSizes: Array.from(sizes).sort((a, b) => parseFloat(a) - parseFloat(b))
291
+ };
292
+ }`
293
+ });
294
+ }
295
+ ```
296
+
297
+ ### 2.3: Extract Shadows & Effects
298
+
299
+ ```javascript
300
+ async function extractShadows() {
301
+ return await mcp__chrome-devtools__evaluate_script({
302
+ function: `() => {
303
+ const allElements = document.querySelectorAll('*');
304
+ const effects = {
305
+ shadows: new Set(),
306
+ borderRadii: new Set(),
307
+ borderWidths: new Set()
308
+ };
309
+
310
+ allElements.forEach(el => {
311
+ const s = window.getComputedStyle(el);
312
+ if (s.boxShadow && s.boxShadow !== 'none') {
313
+ effects.shadows.add(s.boxShadow);
314
+ }
315
+ if (s.borderRadius && s.borderRadius !== '0px') {
316
+ effects.borderRadii.add(s.borderRadius);
317
+ }
318
+ if (s.borderWidth && s.borderWidth !== '0px') {
319
+ effects.borderWidths.add(s.borderWidth);
320
+ }
321
+ });
322
+
323
+ return {
324
+ shadows: Array.from(effects.shadows).slice(0, 15),
325
+ borderRadii: Array.from(effects.borderRadii).slice(0, 15),
326
+ borderWidths: Array.from(effects.borderWidths).slice(0, 10)
327
+ };
328
+ }`
329
+ });
330
+ }
331
+ ```
332
+
333
+ ### 2.4: Extract Spacing
334
+
335
+ ```javascript
336
+ async function extractSpacing() {
337
+ return await mcp__chrome-devtools__evaluate_script({
338
+ function: `() => {
339
+ const spacing = {
340
+ paddings: new Set(),
341
+ margins: new Set(),
342
+ gaps: new Set()
343
+ };
344
+
345
+ Array.from(document.querySelectorAll('*')).slice(0, 100).forEach(el => {
346
+ const s = window.getComputedStyle(el);
347
+
348
+ // Padding
349
+ if (s.padding && s.padding !== '0px') {
350
+ [s.padding, s.paddingTop, s.paddingRight, s.paddingBottom, s.paddingLeft]
351
+ .forEach(v => spacing.paddings.add(v));
352
+ }
353
+
354
+ // Margin
355
+ if (s.margin && s.margin !== '0px') {
356
+ [s.marginTop, s.marginBottom].forEach(v => {
357
+ if (v && v !== '0px' && v !== 'auto') spacing.margins.add(v);
358
+ });
359
+ }
360
+
361
+ // Gap
362
+ if (s.gap && s.gap !== 'normal' && s.gap !== '0px') {
363
+ spacing.gaps.add(s.gap);
364
+ }
365
+ });
366
+
367
+ // Detect grid pattern
368
+ const allValues = [
369
+ ...spacing.paddings,
370
+ ...spacing.margins,
371
+ ...spacing.gaps
372
+ ]
373
+ .map(v => parseFloat(v))
374
+ .filter(v => !isNaN(v) && v > 0)
375
+ .sort((a, b) => a - b);
376
+
377
+ // Find GCD (grid base)
378
+ const gcd = (a, b) => b === 0 ? a : gcd(b, a % b);
379
+ const gridBase = allValues.length > 1
380
+ ? allValues.reduce((acc, val) => gcd(acc, val), allValues[0])
381
+ : 8;
382
+
383
+ return {
384
+ paddings: Array.from(spacing.paddings).slice(0, 20),
385
+ margins: Array.from(spacing.margins).slice(0, 20),
386
+ gaps: Array.from(spacing.gaps).slice(0, 10),
387
+ detectedGrid: Math.round(gridBase) || 8,
388
+ commonValues: [...new Set(allValues)].slice(0, 15)
389
+ };
390
+ }`
391
+ });
392
+ }
393
+ ```
394
+
395
+ ### 2.5-2.7: Extract Components
396
+
397
+ ```javascript
398
+ async function extractButtons() {
399
+ return await mcp__chrome-devtools__evaluate_script({
400
+ function: `() => {
401
+ return Array.from(document.querySelectorAll('button, a[role="button"], .btn, [class*="button"], [class*="Button"]'))
402
+ .slice(0, 10)
403
+ .map((btn, i) => {
404
+ btn.setAttribute('data-extract-id', 'button-' + i);
405
+ const s = window.getComputedStyle(btn);
406
+ return {
407
+ id: 'button-' + i,
408
+ text: btn.textContent.trim().substring(0, 30),
409
+ backgroundColor: s.backgroundColor,
410
+ color: s.color,
411
+ padding: s.padding,
412
+ paddingTop: s.paddingTop,
413
+ paddingRight: s.paddingRight,
414
+ paddingBottom: s.paddingBottom,
415
+ paddingLeft: s.paddingLeft,
416
+ border: s.border,
417
+ borderWidth: s.borderWidth,
418
+ borderColor: s.borderColor,
419
+ borderRadius: s.borderRadius,
420
+ fontSize: s.fontSize,
421
+ fontWeight: s.fontWeight,
422
+ textTransform: s.textTransform,
423
+ letterSpacing: s.letterSpacing,
424
+ boxShadow: s.boxShadow,
425
+ transition: s.transition
426
+ };
427
+ });
428
+ }`
429
+ });
430
+ }
431
+
432
+ async function extractCards() {
433
+ return await mcp__chrome-devtools__evaluate_script({
434
+ function: `() => {
435
+ const selectors = [
436
+ '[class*="card"]', '[class*="Card"]',
437
+ 'article', 'section',
438
+ '[class*="box"]', '[class*="Box"]'
439
+ ];
440
+
441
+ return Array.from(document.querySelectorAll(selectors.join(', ')))
442
+ .slice(0, 10)
443
+ .map((card, i) => {
444
+ card.setAttribute('data-extract-id', 'card-' + i);
445
+ const s = window.getComputedStyle(card);
446
+ return {
447
+ id: 'card-' + i,
448
+ className: card.className,
449
+ backgroundColor: s.backgroundColor,
450
+ padding: s.padding,
451
+ border: s.border,
452
+ borderRadius: s.borderRadius,
453
+ boxShadow: s.boxShadow,
454
+ transition: s.transition
455
+ };
456
+ });
457
+ }`
458
+ });
459
+ }
460
+
461
+ async function extractInputs() {
462
+ return await mcp__chrome-devtools__evaluate_script({
463
+ function: `() => {
464
+ return Array.from(document.querySelectorAll('input[type="text"], input[type="email"], input[type="password"], textarea'))
465
+ .slice(0, 5)
466
+ .map((input, i) => {
467
+ input.setAttribute('data-extract-id', 'input-' + i);
468
+ const s = window.getComputedStyle(input);
469
+ return {
470
+ id: 'input-' + i,
471
+ type: input.type || 'textarea',
472
+ height: s.height,
473
+ padding: s.padding,
474
+ border: s.border,
475
+ borderRadius: s.borderRadius,
476
+ fontSize: s.fontSize,
477
+ backgroundColor: s.backgroundColor,
478
+ transition: s.transition
479
+ };
480
+ });
481
+ }`
482
+ });
483
+ }
484
+ ```
485
+
486
+ ### 2.8: Extract Animations (@keyframes + transitions)
487
+
488
+ ```javascript
489
+ async function extractAnimations() {
490
+ return await mcp__chrome-devtools__evaluate_script({
491
+ function: `() => {
492
+ const keyframes = [];
493
+ const transitions = [];
494
+
495
+ // Extract @keyframes
496
+ Array.from(document.styleSheets).forEach(sheet => {
497
+ try {
498
+ Array.from(sheet.cssRules || []).forEach(rule => {
499
+ if (rule.type === CSSRule.KEYFRAMES_RULE) {
500
+ keyframes.push({
501
+ name: rule.name,
502
+ css: rule.cssText
503
+ });
504
+ }
505
+ });
506
+ } catch(e) {
507
+ // CORS - skip
508
+ }
509
+ });
510
+
511
+ // Extract elements with transitions
512
+ Array.from(document.querySelectorAll('*')).slice(0, 50).forEach(el => {
513
+ const s = window.getComputedStyle(el);
514
+ if (s.transition && s.transition !== 'all 0s ease 0s') {
515
+ transitions.push({
516
+ selector: el.className || el.tagName,
517
+ transition: s.transition,
518
+ transitionDuration: s.transitionDuration,
519
+ transitionTimingFunction: s.transitionTimingFunction,
520
+ transitionProperty: s.transitionProperty
521
+ });
522
+ }
523
+ });
524
+
525
+ return { keyframes, transitions };
526
+ }`
527
+ });
528
+ }
529
+ ```
530
+
531
+ **Report:**
532
+ ```
533
+ ✅ CSS Data Extracted!
534
+
535
+ 📊 Summary:
536
+ - Colors: ${colors.backgrounds.length} backgrounds, ${colors.texts.length} texts, ${colors.borders.length} borders
537
+ - Typography: ${typography.allFonts.length} fonts, ${typography.allWeights.length} weights
538
+ - Shadows: ${shadows.shadows.length} unique values
539
+ - Spacing: ${spacing.detectedGrid}px grid detected
540
+ - Buttons: ${buttons.length} extracted
541
+ - Cards: ${cards.length} extracted
542
+ - Inputs: ${inputs.length} extracted
543
+ - Animations: ${animations.keyframes.length} @keyframes, ${animations.transitions.length} transitions
544
+
545
+ 🔄 Extracting component animations (hover/focus states)...
546
+ ```
547
+
548
+ ---
549
+
550
+ ## STEP 3: Extract Component Animations (Interactive States)
551
+
552
+ For each component, capture before/after states:
553
+
554
+ ```javascript
555
+ const componentAnimations = {};
556
+
557
+ // Buttons
558
+ for (let i = 0; i < Math.min(buttons.length, 3); i++) {
559
+ const btnId = `button-${i}`;
560
+
561
+ // Screenshot: Default state
562
+ await mcp__chrome-devtools__evaluate_script({
563
+ function: `() => {
564
+ const el = document.querySelector('[data-extract-id="${btnId}"]');
565
+ if (el) el.scrollIntoView({ block: 'center' });
566
+ }`
567
+ });
568
+ await sleep(500);
569
+
570
+ await mcp__chrome-devtools__take_screenshot({
571
+ filePath: `design-system/extracted/${siteName}/screenshots/${btnId}-default.png`
572
+ });
573
+
574
+ // Get default computed styles
575
+ const defaultStyle = await mcp__chrome-devtools__evaluate_script({
576
+ function: `() => {
577
+ const el = document.querySelector('[data-extract-id="${btnId}"]');
578
+ if (!el) return null;
579
+ const s = window.getComputedStyle(el);
580
+ return {
581
+ background: s.backgroundColor,
582
+ color: s.color,
583
+ border: s.border,
584
+ borderRadius: s.borderRadius,
585
+ boxShadow: s.boxShadow,
586
+ transform: s.transform,
587
+ opacity: s.opacity
588
+ };
589
+ }`
590
+ });
591
+
592
+ // Trigger hover
593
+ await mcp__chrome-devtools__evaluate_script({
594
+ function: `() => {
595
+ const el = document.querySelector('[data-extract-id="${btnId}"]');
596
+ if (el) el.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }));
597
+ }`
598
+ });
599
+ await sleep(500); // Wait for transition
600
+
601
+ // Screenshot: Hover state
602
+ await mcp__chrome-devtools__take_screenshot({
603
+ filePath: `design-system/extracted/${siteName}/screenshots/${btnId}-hover.png`
604
+ });
605
+
606
+ // Get hover computed styles
607
+ const hoverStyle = await mcp__chrome-devtools__evaluate_script({
608
+ function: `() => {
609
+ const el = document.querySelector('[data-extract-id="${btnId}"]');
610
+ if (!el) return null;
611
+ const s = window.getComputedStyle(el);
612
+ return {
613
+ background: s.backgroundColor,
614
+ color: s.color,
615
+ border: s.border,
616
+ borderRadius: s.borderRadius,
617
+ boxShadow: s.boxShadow,
618
+ transform: s.transform,
619
+ opacity: s.opacity
620
+ };
621
+ }`
622
+ });
623
+
624
+ // Remove hover
625
+ await mcp__chrome-devtools__evaluate_script({
626
+ function: `() => {
627
+ const el = document.querySelector('[data-extract-id="${btnId}"]');
628
+ if (el) el.dispatchEvent(new MouseEvent('mouseleave'));
629
+ }`
630
+ });
631
+
632
+ // Calculate changes
633
+ componentAnimations[btnId] = {
634
+ type: 'button',
635
+ states: {
636
+ default: defaultStyle,
637
+ hover: hoverStyle
638
+ },
639
+ changes: {
640
+ background_changed: defaultStyle.background !== hoverStyle.background,
641
+ shadow_changed: defaultStyle.boxShadow !== hoverStyle.boxShadow,
642
+ transform_changed: defaultStyle.transform !== hoverStyle.transform,
643
+ border_changed: defaultStyle.border !== hoverStyle.border
644
+ },
645
+ transition: buttons[i].transition,
646
+ description: generateDescription(defaultStyle, hoverStyle)
647
+ };
648
+ }
649
+
650
+ // Cards (same pattern)
651
+ for (let i = 0; i < Math.min(cards.length, 3); i++) {
652
+ // ... same process for cards ...
653
+ }
654
+
655
+ // Inputs (focus state)
656
+ for (let i = 0; i < Math.min(inputs.length, 3); i++) {
657
+ // ... same process with 'focus' event instead of 'mouseenter' ...
658
+ }
659
+
660
+ function generateDescription(defaultStyle, hoverStyle) {
661
+ const changes = [];
662
+
663
+ if (defaultStyle.boxShadow !== hoverStyle.boxShadow) {
664
+ if (defaultStyle.boxShadow === 'none' && hoverStyle.boxShadow !== 'none') {
665
+ changes.push('Shadow appears');
666
+ } else if (defaultStyle.boxShadow !== 'none' && hoverStyle.boxShadow !== 'none') {
667
+ changes.push('Shadow changes');
668
+ }
669
+ }
670
+
671
+ if (defaultStyle.transform !== hoverStyle.transform) {
672
+ if (hoverStyle.transform.includes('scale')) {
673
+ changes.push('Scales up');
674
+ }
675
+ if (hoverStyle.transform.includes('translateY')) {
676
+ changes.push('Moves up');
677
+ }
678
+ }
679
+
680
+ if (defaultStyle.background !== hoverStyle.background) {
681
+ changes.push('Background changes');
682
+ }
683
+
684
+ return changes.length > 0 ? changes.join(' + ') : 'No visible changes';
685
+ }
686
+ ```
687
+
688
+ **Report:**
689
+ ```
690
+ ✅ Component Animations Extracted!
691
+
692
+ 📸 Screenshots captured:
693
+ - ${Object.keys(componentAnimations).length} components
694
+ - ${Object.keys(componentAnimations).length * 2} screenshots (default + hover/focus)
695
+
696
+ 🔄 Capturing full-page screenshot...
697
+ ```
698
+
699
+ ---
700
+
701
+ ## STEP 4: Full-Page Screenshot
702
+
703
+ ```javascript
704
+ // Create temp directory
705
+ await Bash: mkdir -p design-system/extracted/${siteName}/screenshots
706
+
707
+ // Try fullpage
708
+ try {
709
+ await mcp__chrome-devtools__take_screenshot({
710
+ fullPage: true,
711
+ format: 'png',
712
+ filePath: `design-system/extracted/${siteName}/screenshots/full-page.png`
713
+ });
714
+ } catch {
715
+ // Fallback: viewport only
716
+ await mcp__chrome-devtools__take_screenshot({
717
+ fullPage: false,
718
+ format: 'png',
719
+ filePath: `design-system/extracted/${siteName}/screenshots/viewport.png`
720
+ });
721
+ }
722
+ ```
723
+
724
+ ---
725
+
726
+ ## STEP 5: AI Psychology Analysis
727
+
728
+ Read screenshot and analyze:
729
+
730
+ ```javascript
731
+ const screenshotPath = exists(`design-system/extracted/${siteName}/screenshots/full-page.png`)
732
+ ? `design-system/extracted/${siteName}/screenshots/full-page.png`
733
+ : `design-system/extracted/${siteName}/screenshots/viewport.png`;
734
+
735
+ const screenshot = Read(screenshotPath);
736
+
737
+ const analysisPrompt = `
738
+ You are a UX/UI design psychologist and systems architect.
739
+
740
+ Analyze this website's design and provide deep psychology insights.
741
+
742
+ Visual Screenshot: [attached]
743
+
744
+ Extracted CSS Data:
745
+ - Colors: ${JSON.stringify(colors, null, 2)}
746
+ - Typography: ${JSON.stringify(typography, null, 2)}
747
+ - Shadows: ${JSON.stringify(shadows, null, 2)}
748
+ - Spacing: ${JSON.stringify(spacing, null, 2)}
749
+ - Button Styles: ${JSON.stringify(buttons.slice(0, 2), null, 2)}
750
+ - Card Styles: ${JSON.stringify(cards.slice(0, 2), null, 2)}
751
+
752
+ Component Animations:
753
+ ${Object.entries(componentAnimations).slice(0, 3).map(([id, anim]) =>
754
+ `- ${id}: ${anim.description}`
755
+ ).join('\n')}
756
+
757
+ Wrap your analysis in <pondering> tags and include:
758
+
759
+ 1. **Design Style Classification** (Neo-Brutalism, Minimalist, Modern SaaS, etc.)
760
+ 2. **Visual Principles** (what design principles are used?)
761
+ 3. **Psychology** (what emotions does it evoke? why?)
762
+ 4. **Target Audience** (who is this for?)
763
+ 5. **Key Differentiators** (how is it different from typical sites?)
764
+ 6. **Design Philosophy** (core beliefs that guide this design)
765
+
766
+ Be specific with examples from the CSS data.
767
+ `;
768
+
769
+ const analysis = await LLM({
770
+ prompt: analysisPrompt,
771
+ images: [screenshot]
772
+ });
773
+
774
+ Write(`design-system/extracted/${siteName}/analysis.md`, analysis);
775
+ ```
776
+
777
+ **Report:**
778
+ ```
779
+ ✅ Psychology Analysis Complete!
780
+
781
+ 🧠 Design style detected from analysis
782
+ 📁 Saved: analysis.md
783
+
784
+ 🔄 Generating final YAML output...
785
+ ```
786
+
787
+ ---
788
+
789
+ ## STEP 6: Generate data.yaml (17 Sections)
790
+
791
+ ```javascript
792
+ const yamlData = {
793
+ meta: {
794
+ site_name: siteName,
795
+ url: url,
796
+ extracted_at: new Date().toISOString(),
797
+ extractor_version: '2.0.0',
798
+ coverage: {
799
+ total_sections: 17,
800
+ detected_sections: 15, // Count how many have detected: true
801
+ percentage: Math.round((15 / 17) * 100)
802
+ }
803
+ },
804
+
805
+ sections: {
806
+ overview: {
807
+ detected: true,
808
+ style: 'Auto-detected from analysis',
809
+ tech_stack: 'Framework-agnostic'
810
+ },
811
+
812
+ design_philosophy: {
813
+ detected: true,
814
+ from_analysis: true
815
+ },
816
+
817
+ color_palette: {
818
+ detected: true,
819
+ primary: colors.backgrounds.slice(0, 5),
820
+ secondary: colors.backgrounds.slice(5, 10),
821
+ text_colors: colors.texts,
822
+ border_colors: colors.borders
823
+ },
824
+
825
+ typography: {
826
+ detected: true,
827
+ fonts: typography.allFonts,
828
+ weights: typography.allWeights,
829
+ sizes: typography.allSizes,
830
+ h1: typography.h1,
831
+ h2: typography.h2,
832
+ h3: typography.h3,
833
+ body: typography.body
834
+ },
835
+
836
+ spacing_system: {
837
+ detected: true,
838
+ grid_base: spacing.detectedGrid,
839
+ paddings: spacing.paddings,
840
+ margins: spacing.margins,
841
+ gaps: spacing.gaps,
842
+ common_values: spacing.commonValues
843
+ },
844
+
845
+ component_styles: {
846
+ detected: true,
847
+ buttons: buttons.map((btn, i) => ({
848
+ ...btn,
849
+ animation: componentAnimations[`button-${i}`] || null
850
+ })),
851
+ cards: cards.map((card, i) => ({
852
+ ...card,
853
+ animation: componentAnimations[`card-${i}`] || null
854
+ })),
855
+ inputs: inputs.map((input, i) => ({
856
+ ...input,
857
+ animation: componentAnimations[`input-${i}`] || null
858
+ }))
859
+ },
860
+
861
+ shadows_elevation: {
862
+ detected: true,
863
+ values: shadows.shadows
864
+ },
865
+
866
+ animations_transitions: {
867
+ detected: true,
868
+ keyframes: animations.keyframes,
869
+ transitions: animations.transitions
870
+ },
871
+
872
+ border_styles: {
873
+ detected: true,
874
+ widths: shadows.borderWidths
875
+ },
876
+
877
+ border_radius: {
878
+ detected: true,
879
+ values: shadows.borderRadii
880
+ },
881
+
882
+ opacity_transparency: {
883
+ detected: true,
884
+ values: [0.5, 0.7, 0.9] // Standard
885
+ },
886
+
887
+ z_index_layers: {
888
+ detected: false,
889
+ fallback: 'standard'
890
+ },
891
+
892
+ responsive_breakpoints: {
893
+ detected: false,
894
+ fallback: 'standard'
895
+ },
896
+
897
+ css_variables: {
898
+ generated: true
899
+ },
900
+
901
+ layout_patterns: {
902
+ detected: true,
903
+ container_width: '1280px',
904
+ grid_columns: 12
905
+ },
906
+
907
+ example_components: {
908
+ generated: true
909
+ },
910
+
911
+ additional_sections: {
912
+ accessibility: { detected: true },
913
+ best_practices: { generated: true }
914
+ }
915
+ },
916
+
917
+ animations: componentAnimations
918
+ };
919
+
920
+ const yamlContent = YAML.stringify(yamlData, null, 2);
921
+ Write(`design-system/extracted/${siteName}/data.yaml`, yamlContent);
922
+ ```
923
+
924
+ ---
925
+
926
+ ## STEP 7: Final Report
927
+
928
+ ```
929
+ ✅ Extraction Complete: ${siteName}
930
+
931
+ 📊 Coverage: ${yamlData.meta.coverage.detected_sections}/17 sections (${yamlData.meta.coverage.percentage}%)
932
+ ✅ Colors (${colors.backgrounds.length + colors.texts.length + colors.borders.length} total)
933
+ ✅ Typography (${typography.allFonts.length} fonts, ${typography.allWeights.length} weights)
934
+ ✅ Spacing (${spacing.detectedGrid}px grid detected)
935
+ ✅ Components (${buttons.length} buttons, ${cards.length} cards, ${inputs.length} inputs)
936
+ ✅ Shadows (${shadows.shadows.length} unique values)
937
+ ✅ Animations (${animations.keyframes.length} @keyframes, ${animations.transitions.length} transitions)
938
+ ${yamlData.sections.z_index_layers.detected ? '✅' : '❌'} Z-index
939
+ ${yamlData.sections.responsive_breakpoints.detected ? '✅' : '❌'} Breakpoints
940
+
941
+ 📸 Screenshots: ${Object.keys(componentAnimations).length * 2 + 1} captured
942
+ - full-page.png (or viewport.png)
943
+ - ${Object.keys(componentAnimations).length} components × 2 states
944
+
945
+ 📁 Output:
946
+ ✓ design-system/extracted/${siteName}/data.yaml
947
+ ✓ design-system/extracted/${siteName}/analysis.md
948
+ ✓ design-system/extracted/${siteName}/screenshots/ (${Object.keys(componentAnimations).length * 2 + 1} files)
949
+
950
+ ⏱️ Time: ${Date.now() - startTime}ms
951
+
952
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
953
+
954
+ 🚀 Next Steps:
955
+
956
+ 1. Extract more sites:
957
+ /extract https://blackbird.com
958
+ /extract https://linear.app
959
+
960
+ 2. Generate style guide:
961
+ /designsetup @prd.md @project.md
962
+
963
+ 3. Or review extracted data:
964
+ cat design-system/extracted/${siteName}/analysis.md
965
+ ```
966
+
967
+ ---
968
+
969
+ ## Error Handling
970
+
971
+ ```javascript
972
+ try {
973
+ await mcp__chrome-devtools__navigate_page({ url });
974
+ } catch (error) {
975
+ return error(`
976
+ ❌ Failed to load URL: ${url}
977
+
978
+ Error: ${error.message}
979
+
980
+ Check:
981
+ - Is the URL accessible?
982
+ - Is Chrome DevTools MCP running?
983
+ - Try with --verbose for details
984
+ `);
985
+ }
986
+
987
+ // Extraction failures are non-critical
988
+ try {
989
+ const colors = await extractColors();
990
+ } catch (error) {
991
+ console.warn('Color extraction failed:', error.message);
992
+ colors = { backgrounds: [], texts: [], borders: [] };
993
+ }
994
+
995
+ // Screenshot failures are non-critical
996
+ try {
997
+ await takeScreenshot();
998
+ } catch (error) {
999
+ console.warn('Screenshot failed, continuing...');
1000
+ }
1001
+ ```
1002
+
1003
+ ---
1004
+
1005
+ **Now execute the extraction.**