@buoy-design/cli 0.3.35 → 0.3.37

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 (26) hide show
  1. package/dist/commands/check.js.sync-conflict-20260313-114446-6PCZ3ZU.map +1 -0
  2. package/dist/commands/dock.js.sync-conflict-20260313-172700-6PCZ3ZU.map +1 -0
  3. package/dist/commands/show.d.ts.map +1 -1
  4. package/dist/commands/show.js +131 -12
  5. package/dist/commands/show.js.map +1 -1
  6. package/dist/config/loader.d.ts.sync-conflict-20260313-145931-6PCZ3ZU.map +1 -0
  7. package/dist/config/schema.sync-conflict-20260313-173553-6PCZ3ZU.d.ts +1812 -0
  8. package/dist/config/schema.sync-conflict-20260313-173553-6PCZ3ZU.d.ts.map +1 -0
  9. package/dist/config/schema.sync-conflict-20260313-173553-6PCZ3ZU.js +214 -0
  10. package/dist/config/schema.sync-conflict-20260313-173553-6PCZ3ZU.js.map +1 -0
  11. package/dist/detect/frameworks.sync-conflict-20260313-150557-6PCZ3ZU.js +252 -0
  12. package/dist/detect/monorepo-patterns.sync-conflict-20260311-182757-6PCZ3ZU.js +209 -0
  13. package/dist/hooks/index.sync-conflict-20260311-082258-6PCZ3ZU.js +616 -0
  14. package/dist/output/reporters.js.sync-conflict-20260313-164629-6PCZ3ZU.map +1 -0
  15. package/dist/services/drift-analysis.d.ts +2 -0
  16. package/dist/services/drift-analysis.d.ts.map +1 -1
  17. package/dist/services/drift-analysis.js +68 -1
  18. package/dist/services/drift-analysis.js.map +1 -1
  19. package/dist/services/drift-analysis.js.sync-conflict-20260313-152307-6PCZ3ZU.map +1 -0
  20. package/dist/services/remote-check.d.ts +4 -0
  21. package/dist/services/remote-check.d.ts.map +1 -1
  22. package/dist/services/remote-check.js +15 -1
  23. package/dist/services/remote-check.js.map +1 -1
  24. package/dist/services/skill-export.js.sync-conflict-20260313-155735-6PCZ3ZU.map +1 -0
  25. package/dist/services/skill-export.sync-conflict-20260313-161246-6PCZ3ZU.js +737 -0
  26. package/package.json +3 -3
@@ -0,0 +1,737 @@
1
+ /**
2
+ * SkillExportService - Generate design system skills for AI agents
3
+ *
4
+ * Exports tokens, components, patterns, and anti-patterns as markdown files
5
+ * optimized for progressive disclosure in AI agent workflows.
6
+ */
7
+ import { join } from 'path';
8
+ /**
9
+ * Service for exporting design system data as AI agent skills
10
+ */
11
+ export class SkillExportService {
12
+ constructor(_projectName) {
13
+ // Project name is passed for future extensibility but currently
14
+ // derived from ScanData.projectName in export()
15
+ }
16
+ /**
17
+ * Export design system data as skill files
18
+ */
19
+ async export(data, options) {
20
+ const files = [];
21
+ const { sections, outputPath } = options;
22
+ // Always include SKILL.md
23
+ files.push({
24
+ path: join(outputPath, 'SKILL.md'),
25
+ content: this.generateSkillMd(data),
26
+ });
27
+ // Tokens section
28
+ if (sections.includes('tokens')) {
29
+ files.push({
30
+ path: join(outputPath, 'tokens', '_index.md'),
31
+ content: this.generateTokensIndex(data.tokens),
32
+ });
33
+ files.push({
34
+ path: join(outputPath, 'tokens', 'colors.md'),
35
+ content: this.generateColorTokens(data.tokens),
36
+ });
37
+ files.push({
38
+ path: join(outputPath, 'tokens', 'spacing.md'),
39
+ content: this.generateSpacingTokens(data.tokens),
40
+ });
41
+ files.push({
42
+ path: join(outputPath, 'tokens', 'typography.md'),
43
+ content: this.generateTypographyTokens(data.tokens),
44
+ });
45
+ }
46
+ // Components section
47
+ if (sections.includes('components')) {
48
+ files.push({
49
+ path: join(outputPath, 'components', '_inventory.md'),
50
+ content: this.generateComponentInventory(data.components),
51
+ });
52
+ }
53
+ // Patterns section
54
+ if (sections.includes('patterns')) {
55
+ files.push({
56
+ path: join(outputPath, 'patterns', '_common.md'),
57
+ content: this.generatePatternsIndex(data.components),
58
+ });
59
+ }
60
+ // Anti-patterns section
61
+ if (sections.includes('anti-patterns')) {
62
+ files.push({
63
+ path: join(outputPath, 'anti-patterns', '_avoid.md'),
64
+ content: this.generateAntiPatterns(data.drifts),
65
+ });
66
+ }
67
+ // Calculate stats
68
+ const colorTokens = data.tokens.filter((t) => t.category === 'color');
69
+ const spacingTokens = data.tokens.filter((t) => t.category === 'spacing');
70
+ const typographyTokens = data.tokens.filter((t) => t.category === 'typography');
71
+ const detectedPatterns = this.detectPatterns(data.components);
72
+ return {
73
+ files,
74
+ stats: {
75
+ tokens: {
76
+ colors: colorTokens.length,
77
+ spacing: spacingTokens.length,
78
+ typography: typographyTokens.length,
79
+ total: data.tokens.length,
80
+ },
81
+ components: data.components.length,
82
+ patterns: detectedPatterns,
83
+ },
84
+ };
85
+ }
86
+ /**
87
+ * Generate the main SKILL.md entry point
88
+ */
89
+ generateSkillMd(data) {
90
+ const hasTokens = data.tokens.length > 0;
91
+ const hasComponents = data.components.length > 0;
92
+ const rules = [];
93
+ if (hasTokens) {
94
+ const hasColors = data.tokens.some((t) => t.category === 'color');
95
+ const hasSpacing = data.tokens.some((t) => t.category === 'spacing');
96
+ const hasTypography = data.tokens.some((t) => t.category === 'typography');
97
+ if (hasColors) {
98
+ rules.push('1. NEVER hardcode colors - use tokens from `tokens/colors.md`');
99
+ }
100
+ if (hasSpacing) {
101
+ rules.push('2. NEVER use arbitrary spacing - use scale from `tokens/spacing.md`');
102
+ }
103
+ if (hasTypography) {
104
+ rules.push('3. NEVER hardcode fonts - use tokens from `tokens/typography.md`');
105
+ }
106
+ }
107
+ if (hasComponents) {
108
+ rules.push(`${rules.length + 1}. NEVER create new components without checking \`components/_inventory.md\` first`);
109
+ }
110
+ return `---
111
+ name: design-system
112
+ description: Use when building UI components, styling, or layouts for ${data.projectName}
113
+ triggers:
114
+ - building UI
115
+ - styling components
116
+ - adding colors
117
+ - creating layouts
118
+ - form design
119
+ - component creation
120
+ ---
121
+
122
+ # ${data.projectName} Design System
123
+
124
+ This skill provides design system context for AI code generation.
125
+
126
+ ## Quick Start
127
+
128
+ ${hasComponents ? '1. **Before generating UI code**, check `components/_inventory.md`' : ''}
129
+ ${hasTokens ? '2. **For styling**, use tokens from `tokens/_index.md`' : ''}
130
+ 3. **For patterns**, see \`patterns/_common.md\`
131
+
132
+ ## Rules
133
+
134
+ ${rules.length > 0 ? rules.join('\n') : 'No specific rules defined yet.'}
135
+
136
+ ## Progressive Loading
137
+
138
+ - Start with \`_index.md\` files for quick reference
139
+ - Load specific files when you need details
140
+ - The \`anti-patterns/_avoid.md\` file lists what NEVER to do
141
+
142
+ ## Feedback Loop
143
+
144
+ If you create something not in the design system:
145
+ 1. Check if a similar component exists
146
+ 2. If truly new, flag for design system team review
147
+ 3. Use closest existing pattern as base
148
+
149
+ ## Validation
150
+
151
+ Run \`buoy drift check\` before committing to validate compliance.
152
+
153
+ \`\`\`bash
154
+ buoy drift check # Quick validation
155
+ buoy show drift # Detailed drift analysis
156
+ \`\`\`
157
+ `;
158
+ }
159
+ /**
160
+ * Generate tokens index with category counts
161
+ */
162
+ generateTokensIndex(tokens) {
163
+ if (tokens.length === 0) {
164
+ return `# Design Tokens
165
+
166
+ No tokens detected in this project.
167
+
168
+ Run \`buoy show all\` to detect tokens from your codebase.
169
+ `;
170
+ }
171
+ const colorCount = tokens.filter((t) => t.category === 'color').length;
172
+ const spacingCount = tokens.filter((t) => t.category === 'spacing').length;
173
+ const typographyCount = tokens.filter((t) => t.category === 'typography').length;
174
+ const otherCount = tokens.length - colorCount - spacingCount - typographyCount;
175
+ const categories = [];
176
+ if (colorCount > 0) {
177
+ categories.push(`| Color | ${colorCount} | [colors.md](./colors.md) |`);
178
+ }
179
+ if (spacingCount > 0) {
180
+ categories.push(`| Spacing | ${spacingCount} | [spacing.md](./spacing.md) |`);
181
+ }
182
+ if (typographyCount > 0) {
183
+ categories.push(`| Typography | ${typographyCount} | [typography.md](./typography.md) |`);
184
+ }
185
+ if (otherCount > 0) {
186
+ categories.push(`| Other | ${otherCount} | - |`);
187
+ }
188
+ return `# Design Tokens
189
+
190
+ Quick reference for all design tokens in this project.
191
+
192
+ ## Categories
193
+
194
+ | Category | Count | Details |
195
+ |----------|-------|---------|
196
+ ${categories.join('\n')}
197
+
198
+ ## Usage
199
+
200
+ Always use tokens instead of hardcoded values:
201
+
202
+ \`\`\`tsx
203
+ // Bad
204
+ <div style={{ color: '#2563EB' }}>...</div>
205
+
206
+ // Good
207
+ <div className="text-primary">...</div>
208
+ \`\`\`
209
+
210
+ See individual files for complete token lists with usage guidance.
211
+ `;
212
+ }
213
+ /**
214
+ * Generate color tokens markdown
215
+ */
216
+ generateColorTokens(tokens) {
217
+ const colorTokens = tokens.filter((t) => t.category === 'color');
218
+ if (colorTokens.length === 0) {
219
+ return `# Color Tokens
220
+
221
+ No color tokens detected in this project.
222
+ `;
223
+ }
224
+ const rows = colorTokens.map((token) => {
225
+ const value = token.value.type === 'color' ? token.value.hex : String(token.value);
226
+ const usage = token.metadata?.description || 'General use';
227
+ return `| \`${token.name}\` | ${value} | ${usage} |`;
228
+ });
229
+ return `# Color Tokens
230
+
231
+ ## All Colors
232
+
233
+ | Token | Value | Usage |
234
+ |-------|-------|-------|
235
+ ${rows.join('\n')}
236
+
237
+ ## Usage Guidelines
238
+
239
+ - **Primary colors**: Use for main CTAs and brand elements
240
+ - **Semantic colors**: Use success/error/warning for feedback states
241
+ - **Neutral colors**: Use for text, backgrounds, and borders
242
+
243
+ ## Common Mistakes
244
+
245
+ ❌ Using hex values directly:
246
+ \`\`\`tsx
247
+ style={{ color: '#2563EB' }}
248
+ \`\`\`
249
+
250
+ ✅ Using tokens:
251
+ \`\`\`tsx
252
+ className="text-primary"
253
+ // or
254
+ color={tokens.primary}
255
+ \`\`\`
256
+ `;
257
+ }
258
+ /**
259
+ * Generate spacing tokens markdown
260
+ */
261
+ generateSpacingTokens(tokens) {
262
+ const spacingTokens = tokens.filter((t) => t.category === 'spacing');
263
+ if (spacingTokens.length === 0) {
264
+ return `# Spacing Tokens
265
+
266
+ No spacing tokens detected in this project.
267
+ `;
268
+ }
269
+ const rows = spacingTokens.map((token) => {
270
+ let value = '';
271
+ if (token.value.type === 'spacing') {
272
+ value = `${token.value.value}${token.value.unit}`;
273
+ }
274
+ const usage = token.metadata?.description || 'General spacing';
275
+ return `| \`${token.name}\` | ${value} | ${usage} |`;
276
+ });
277
+ return `# Spacing Tokens
278
+
279
+ ## Spacing Scale
280
+
281
+ | Token | Value | Usage |
282
+ |-------|-------|-------|
283
+ ${rows.join('\n')}
284
+
285
+ ## Usage Guidelines
286
+
287
+ - Use consistent spacing from the scale
288
+ - Avoid arbitrary values like \`17px\` or \`13px\`
289
+ - Prefer spacing utilities over inline styles
290
+
291
+ ## Common Mistakes
292
+
293
+ ❌ Arbitrary spacing:
294
+ \`\`\`tsx
295
+ style={{ padding: '17px' }}
296
+ className="p-[13px]"
297
+ \`\`\`
298
+
299
+ ✅ Scale-based spacing:
300
+ \`\`\`tsx
301
+ className="p-4"
302
+ style={{ padding: tokens.space4 }}
303
+ \`\`\`
304
+ `;
305
+ }
306
+ /**
307
+ * Generate typography tokens markdown
308
+ */
309
+ generateTypographyTokens(tokens) {
310
+ const typographyTokens = tokens.filter((t) => t.category === 'typography');
311
+ if (typographyTokens.length === 0) {
312
+ return `# Typography Tokens
313
+
314
+ No typography tokens detected in this project.
315
+ `;
316
+ }
317
+ const rows = typographyTokens.map((token) => {
318
+ let value = '';
319
+ if (token.value.type === 'typography') {
320
+ value = `${token.value.fontFamily}, ${token.value.fontSize}px, ${token.value.fontWeight}`;
321
+ }
322
+ return `| \`${token.name}\` | ${value} |`;
323
+ });
324
+ return `# Typography Tokens
325
+
326
+ ## Font Definitions
327
+
328
+ | Token | Value |
329
+ |-------|-------|
330
+ ${rows.join('\n')}
331
+
332
+ ## Usage Guidelines
333
+
334
+ - Use typography tokens for consistent text styling
335
+ - Avoid hardcoding font families or sizes
336
+ - Prefer text utility classes
337
+
338
+ ## Common Mistakes
339
+
340
+ ❌ Hardcoded fonts:
341
+ \`\`\`tsx
342
+ style={{ fontFamily: 'Inter', fontSize: '16px' }}
343
+ \`\`\`
344
+
345
+ ✅ Typography tokens:
346
+ \`\`\`tsx
347
+ className="text-body"
348
+ // or
349
+ style={{ ...tokens.textBody }}
350
+ \`\`\`
351
+ `;
352
+ }
353
+ /**
354
+ * Generate component inventory markdown
355
+ */
356
+ generateComponentInventory(components) {
357
+ if (components.length === 0) {
358
+ return `# Component Inventory
359
+
360
+ No components detected in this project.
361
+
362
+ Run \`buoy show all\` to detect components from your codebase.
363
+ `;
364
+ }
365
+ const rows = components.map((comp) => {
366
+ const props = comp.props.map((p) => p.name).join(', ') || '-';
367
+ // Handle different source types - some have path, some have other identifiers
368
+ let path = '-';
369
+ if ('path' in comp.source) {
370
+ path = comp.source.path;
371
+ }
372
+ else if (comp.source.type === 'figma') {
373
+ path = `Figma: ${comp.source.fileKey}`;
374
+ }
375
+ else if (comp.source.type === 'storybook') {
376
+ path = `Storybook: ${comp.source.storyId}`;
377
+ }
378
+ return `| \`${comp.name}\` | ${path} | ${props} |`;
379
+ });
380
+ return `# Component Inventory
381
+
382
+ ${components.length} components available in this design system.
383
+
384
+ ## All Components
385
+
386
+ | Component | Path | Props |
387
+ |-----------|------|-------|
388
+ ${rows.join('\n')}
389
+
390
+ ## Usage Guidelines
391
+
392
+ 1. **Check this inventory first** before creating a new component
393
+ 2. Use existing components when possible
394
+ 3. Extend existing components rather than duplicating
395
+
396
+ ## Before Creating New Components
397
+
398
+ Ask yourself:
399
+ - Does a similar component already exist?
400
+ - Can an existing component be extended?
401
+ - Will this component be reused elsewhere?
402
+ `;
403
+ }
404
+ /**
405
+ * Generate patterns index based on component names
406
+ */
407
+ generatePatternsIndex(components) {
408
+ const patterns = this.detectPatterns(components);
409
+ if (patterns.length === 0) {
410
+ return `# Common Patterns
411
+
412
+ No specific patterns detected yet.
413
+
414
+ As your design system grows, patterns will be detected from:
415
+ - Component naming conventions
416
+ - Common component groupings
417
+ - Usage patterns
418
+ `;
419
+ }
420
+ const patternSections = patterns
421
+ .map((pattern) => {
422
+ const relatedComponents = this.getComponentsForPattern(pattern, components);
423
+ const componentList = relatedComponents
424
+ .map((c) => `- \`${c.name}\``)
425
+ .join('\n');
426
+ return `## ${this.capitalizePattern(pattern)} Pattern
427
+
428
+ ${componentList}
429
+ `;
430
+ })
431
+ .join('\n');
432
+ return `# Common Patterns
433
+
434
+ Detected patterns based on component organization.
435
+
436
+ ${patternSections}
437
+
438
+ ## Using Patterns
439
+
440
+ When building features, look for existing patterns first.
441
+ Patterns help maintain consistency across the codebase.
442
+ `;
443
+ }
444
+ /**
445
+ * Generate anti-patterns from drift signals
446
+ */
447
+ generateAntiPatterns(drifts) {
448
+ if (drifts.length === 0) {
449
+ return `# Anti-Patterns
450
+
451
+ No known anti-patterns detected. Your codebase is clean!
452
+
453
+ ## General Guidelines
454
+
455
+ Even without detected issues, avoid:
456
+ - Hardcoded color values
457
+ - Arbitrary spacing values
458
+ - Inline styles for design tokens
459
+ - Creating components that duplicate existing ones
460
+ `;
461
+ }
462
+ // Group by drift type
463
+ const byType = new Map();
464
+ for (const drift of drifts) {
465
+ const existing = byType.get(drift.type) || [];
466
+ existing.push(drift);
467
+ byType.set(drift.type, existing);
468
+ }
469
+ const sections = [];
470
+ for (const [type, typeDrifts] of byType) {
471
+ const severity = typeDrifts[0]?.severity || 'warning';
472
+ const severityBadge = severity === 'critical'
473
+ ? '🔴 Critical'
474
+ : severity === 'warning'
475
+ ? '🟡 Warning'
476
+ : '🔵 Info';
477
+ const examples = typeDrifts
478
+ .slice(0, 3)
479
+ .map((d) => `- ${d.source.entityName}: ${d.message}`)
480
+ .join('\n');
481
+ sections.push(`## ${this.formatDriftType(type)}
482
+
483
+ ${severityBadge}
484
+
485
+ ${examples}
486
+ `);
487
+ }
488
+ return `# Anti-Patterns
489
+
490
+ These patterns have been detected as violations of the design system.
491
+
492
+ ${sections.join('\n')}
493
+
494
+ ## How to Fix
495
+
496
+ Run \`buoy drift check\` to see current violations with fix suggestions.
497
+ `;
498
+ }
499
+ /**
500
+ * Detect patterns from component names
501
+ */
502
+ detectPatterns(components) {
503
+ const patterns = new Set();
504
+ const names = components.map((c) => c.name.toLowerCase());
505
+ // Form patterns
506
+ if (names.some((n) => n.includes('form') ||
507
+ n.includes('input') ||
508
+ n.includes('select') ||
509
+ n.includes('checkbox'))) {
510
+ patterns.add('form');
511
+ }
512
+ // Navigation patterns
513
+ if (names.some((n) => n.includes('nav') ||
514
+ n.includes('menu') ||
515
+ n.includes('sidebar') ||
516
+ n.includes('header'))) {
517
+ patterns.add('navigation');
518
+ }
519
+ // Card patterns
520
+ if (names.some((n) => n.includes('card') || n.includes('tile'))) {
521
+ patterns.add('card');
522
+ }
523
+ // Modal patterns
524
+ if (names.some((n) => n.includes('modal') || n.includes('dialog') || n.includes('drawer'))) {
525
+ patterns.add('modal');
526
+ }
527
+ // Table patterns
528
+ if (names.some((n) => n.includes('table') || n.includes('grid') || n.includes('list'))) {
529
+ patterns.add('data-display');
530
+ }
531
+ return Array.from(patterns);
532
+ }
533
+ /**
534
+ * Get components that match a pattern
535
+ */
536
+ getComponentsForPattern(pattern, components) {
537
+ const keywords = {
538
+ form: ['form', 'input', 'select', 'checkbox', 'radio', 'textarea'],
539
+ navigation: ['nav', 'menu', 'sidebar', 'header', 'footer', 'link'],
540
+ card: ['card', 'tile', 'panel'],
541
+ modal: ['modal', 'dialog', 'drawer', 'overlay'],
542
+ 'data-display': ['table', 'grid', 'list', 'row', 'cell'],
543
+ };
544
+ const patternKeywords = keywords[pattern] || [];
545
+ return components.filter((c) => patternKeywords.some((k) => c.name.toLowerCase().includes(k)));
546
+ }
547
+ /**
548
+ * Capitalize pattern name for display
549
+ */
550
+ capitalizePattern(pattern) {
551
+ return pattern
552
+ .split('-')
553
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
554
+ .join(' ');
555
+ }
556
+ /**
557
+ * Format drift type for display
558
+ */
559
+ formatDriftType(type) {
560
+ return type
561
+ .split('-')
562
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
563
+ .join(' ');
564
+ }
565
+ /**
566
+ * Format token value for display
567
+ * Handles all token types: color, spacing, typography, shadow, border, etc.
568
+ */
569
+ formatTokenValue(token) {
570
+ const value = token.value;
571
+ if (typeof value !== 'object' || value === null) {
572
+ return String(value);
573
+ }
574
+ // Handle typed token values
575
+ if ('type' in value) {
576
+ switch (value.type) {
577
+ case 'color':
578
+ return value.hex || String(value);
579
+ case 'spacing':
580
+ return `${value.value}${value.unit}`;
581
+ case 'typography':
582
+ return `${value.fontFamily || 'inherit'}, ${value.fontSize || 16}px`;
583
+ case 'shadow':
584
+ // Format shadow as CSS-like value
585
+ if (value.x !== undefined && value.y !== undefined) {
586
+ return `${value.x}px ${value.y}px ${value.blur || 0}px ${value.color || '#000'}`;
587
+ }
588
+ return 'shadow';
589
+ case 'border':
590
+ // Format border as CSS-like value
591
+ return `${value.width || 1}px ${value.style || 'solid'} ${value.color || '#000'}`;
592
+ default:
593
+ // Unknown type - try to extract meaningful properties
594
+ if ('value' in value) {
595
+ return String(value.value);
596
+ }
597
+ }
598
+ }
599
+ // Fallback for objects without type - try to find meaningful properties
600
+ const valueObj = value;
601
+ if ('hex' in valueObj)
602
+ return String(valueObj.hex);
603
+ if ('value' in valueObj && 'unit' in valueObj)
604
+ return `${valueObj.value}${valueObj.unit}`;
605
+ if ('value' in valueObj)
606
+ return String(valueObj.value);
607
+ // Last resort - JSON stringify but truncate
608
+ const json = JSON.stringify(value);
609
+ return json.length > 50 ? json.slice(0, 47) + '...' : json;
610
+ }
611
+ /**
612
+ * Generate condensed context for session hook injection
613
+ * Contains all crucial info in a compact format (~500-1500 tokens)
614
+ *
615
+ * IMPORTANT: This is informative context, not a task list.
616
+ * We tell Claude what TO DO, not what's currently broken.
617
+ */
618
+ generateCondensedContext(data) {
619
+ const lines = [];
620
+ lines.push(`# ${data.projectName} Design System`);
621
+ lines.push('');
622
+ lines.push('This context helps you write code that follows the design system.');
623
+ lines.push('');
624
+ // Components - compact list
625
+ if (data.components.length > 0) {
626
+ lines.push('## Available Components');
627
+ lines.push('Check these before creating new components:');
628
+ for (const comp of data.components.slice(0, 30)) {
629
+ const props = comp.props.slice(0, 5).map(p => p.name).join(', ');
630
+ lines.push(`- \`${comp.name}\`${props ? ` (${props})` : ''}`);
631
+ }
632
+ if (data.components.length > 30) {
633
+ lines.push(`- ... and ${data.components.length - 30} more`);
634
+ }
635
+ lines.push('');
636
+ }
637
+ // Tokens - grouped and compact
638
+ if (data.tokens.length > 0) {
639
+ lines.push('## Design Tokens');
640
+ lines.push('Use these instead of hardcoded values:');
641
+ lines.push('');
642
+ const colorTokens = data.tokens.filter(t => t.category === 'color');
643
+ const spacingTokens = data.tokens.filter(t => t.category === 'spacing');
644
+ const typographyTokens = data.tokens.filter(t => t.category === 'typography');
645
+ if (colorTokens.length > 0) {
646
+ lines.push('**Colors:**');
647
+ for (const token of colorTokens.slice(0, 20)) {
648
+ lines.push(`- \`${token.name}\` = ${this.formatTokenValue(token)}`);
649
+ }
650
+ if (colorTokens.length > 20) {
651
+ lines.push(`- ... and ${colorTokens.length - 20} more`);
652
+ }
653
+ lines.push('');
654
+ }
655
+ if (spacingTokens.length > 0) {
656
+ lines.push('**Spacing:**');
657
+ for (const token of spacingTokens.slice(0, 15)) {
658
+ lines.push(`- \`${token.name}\` = ${this.formatTokenValue(token)}`);
659
+ }
660
+ if (spacingTokens.length > 15) {
661
+ lines.push(`- ... and ${spacingTokens.length - 15} more`);
662
+ }
663
+ lines.push('');
664
+ }
665
+ if (typographyTokens.length > 0) {
666
+ lines.push('**Typography:**');
667
+ for (const token of typographyTokens.slice(0, 10)) {
668
+ lines.push(`- \`${token.name}\` = ${this.formatTokenValue(token)}`);
669
+ }
670
+ if (typographyTokens.length > 10) {
671
+ lines.push(`- ... and ${typographyTokens.length - 10} more`);
672
+ }
673
+ lines.push('');
674
+ }
675
+ }
676
+ // Learnings from drift analysis - show real examples of what to avoid
677
+ if (data.drifts.length > 0) {
678
+ lines.push('## What We Learned (from drift analysis)');
679
+ lines.push('Recent issues found in this codebase - avoid these patterns:');
680
+ lines.push('');
681
+ // Group drifts by type
682
+ const driftsByType = new Map();
683
+ for (const drift of data.drifts) {
684
+ const list = driftsByType.get(drift.type) || [];
685
+ list.push(drift);
686
+ driftsByType.set(drift.type, list);
687
+ }
688
+ // Get top 3 drift types by frequency
689
+ const topDrifts = Array.from(driftsByType.entries())
690
+ .sort((a, b) => b[1].length - a[1].length)
691
+ .slice(0, 3);
692
+ for (const [type, drifts] of topDrifts) {
693
+ const critical = drifts.filter(d => d.severity === 'critical').length;
694
+ const badge = critical > 0 ? '🔴' : '🟡';
695
+ const example = drifts[0];
696
+ if (!example)
697
+ continue;
698
+ lines.push(`${badge} **${this.formatDriftType(type)}** (${drifts.length} found)`);
699
+ lines.push(` Example: ${example.message}`);
700
+ // Show fix suggestion if available
701
+ const suggestions = example.details?.suggestions;
702
+ if (suggestions?.length) {
703
+ lines.push(` Fix: Use \`${suggestions[0]}\` instead`);
704
+ }
705
+ else if (example.details?.expected) {
706
+ lines.push(` Fix: Use \`${example.details.expected}\` instead`);
707
+ }
708
+ lines.push('');
709
+ }
710
+ }
711
+ // Guidelines - what to do (not what's broken)
712
+ // Only show guidelines section if we have components or tokens
713
+ if (data.components.length > 0 || data.tokens.length > 0) {
714
+ lines.push('## Guidelines');
715
+ lines.push('When writing UI code:');
716
+ if (data.components.length > 0) {
717
+ lines.push('- Use existing components from the list above');
718
+ lines.push('- Extend existing components rather than duplicating');
719
+ }
720
+ if (data.tokens.length > 0) {
721
+ lines.push('- Use design tokens for colors, spacing, typography');
722
+ lines.push('- Never hardcode values that have token equivalents');
723
+ }
724
+ lines.push('');
725
+ }
726
+ else {
727
+ // No data yet - provide helpful guidance
728
+ lines.push('## Getting Started');
729
+ lines.push('No components or tokens detected yet.');
730
+ lines.push('Run `buoy show all` to scan your codebase.');
731
+ lines.push('');
732
+ }
733
+ lines.push('To validate compliance: `buoy drift check`');
734
+ return lines.join('\n');
735
+ }
736
+ }
737
+ //# sourceMappingURL=skill-export.js.map