@buoy-design/cli 0.3.32 → 0.3.34
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/dist/commands/check.d.ts.sync-conflict-20260305-170128-6PCZ3ZU.map +1 -0
- package/dist/commands/check.sync-conflict-20260305-155016-6PCZ3ZU.d.ts +26 -0
- package/dist/commands/check.sync-conflict-20260305-155016-6PCZ3ZU.d.ts.map +1 -0
- package/dist/commands/check.sync-conflict-20260305-155016-6PCZ3ZU.js +438 -0
- package/dist/commands/check.sync-conflict-20260305-155016-6PCZ3ZU.js.map +1 -0
- package/dist/commands/dock.sync-conflict-20260309-191923-6PCZ3ZU.js +1006 -0
- package/dist/commands/show.d.ts.map +1 -1
- package/dist/commands/show.d.ts.sync-conflict-20260306-165917-6PCZ3ZU.map +1 -0
- package/dist/commands/show.js +6 -0
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/show.sync-conflict-20260305-140755-6PCZ3ZU.js +1735 -0
- package/dist/commands/show.sync-conflict-20260309-130326-6PCZ3ZU.d.ts +11 -0
- package/dist/commands/show.sync-conflict-20260309-130326-6PCZ3ZU.d.ts.map +1 -0
- package/dist/commands/show.sync-conflict-20260309-130326-6PCZ3ZU.js +1735 -0
- package/dist/commands/show.sync-conflict-20260309-130326-6PCZ3ZU.js.map +1 -0
- package/dist/config/loader.js +1 -1
- package/dist/config/loader.js.map +1 -1
- package/dist/config/loader.js.sync-conflict-20260309-033512-6PCZ3ZU.map +1 -0
- package/dist/config/loader.sync-conflict-20260307-175208-6PCZ3ZU.d.ts +8 -0
- package/dist/config/loader.sync-conflict-20260307-175208-6PCZ3ZU.d.ts.map +1 -0
- package/dist/config/loader.sync-conflict-20260307-175208-6PCZ3ZU.js +162 -0
- package/dist/config/loader.sync-conflict-20260307-175208-6PCZ3ZU.js.map +1 -0
- package/dist/config/schema.d.ts.sync-conflict-20260309-154654-6PCZ3ZU.map +1 -0
- package/dist/config/schema.sync-conflict-20260309-135703-6PCZ3ZU.js +214 -0
- package/dist/detect/frameworks.js.sync-conflict-20260306-123756-6PCZ3ZU.map +1 -0
- package/dist/detect/monorepo-patterns.js.sync-conflict-20260309-155400-6PCZ3ZU.map +1 -0
- package/dist/hooks/index.d.ts.sync-conflict-20260306-220901-6PCZ3ZU.map +1 -0
- package/dist/output/formatters.js.sync-conflict-20260306-134702-6PCZ3ZU.map +1 -0
- package/dist/output/formatters.sync-conflict-20260306-180804-6PCZ3ZU.js +867 -0
- package/dist/output/formatters.sync-conflict-20260307-131418-5SN2GZG.d.ts +29 -0
- package/dist/output/formatters.sync-conflict-20260307-131418-5SN2GZG.d.ts.map +1 -0
- package/dist/output/formatters.sync-conflict-20260307-131418-5SN2GZG.js +867 -0
- package/dist/output/formatters.sync-conflict-20260307-131418-5SN2GZG.js.map +1 -0
- package/dist/output/formatters.sync-conflict-20260307-143122-5SN2GZG.d.ts +29 -0
- package/dist/output/formatters.sync-conflict-20260307-143122-5SN2GZG.d.ts.map +1 -0
- package/dist/output/formatters.sync-conflict-20260307-143122-5SN2GZG.js +867 -0
- package/dist/output/formatters.sync-conflict-20260307-143122-5SN2GZG.js.map +1 -0
- package/dist/output/index.sync-conflict-20260309-222859-6PCZ3ZU.js +5 -0
- package/dist/output/reporters.d.sync-conflict-20260309-193820-6PCZ3ZU.ts +38 -0
- package/dist/output/reporters.d.ts.sync-conflict-20260306-193811-6PCZ3ZU.map +1 -0
- package/dist/output/reporters.sync-conflict-20260309-030558-6PCZ3ZU.js +182 -0
- package/dist/output/reports.d.ts.sync-conflict-20260307-172149-6PCZ3ZU.map +1 -0
- package/dist/output/reports.js.sync-conflict-20260305-161643-6PCZ3ZU.map +1 -0
- package/dist/output/reports.sync-conflict-20260305-211951-6PCZ3ZU.js +393 -0
- package/dist/output/visuals.d.ts +53 -0
- package/dist/output/visuals.d.ts.map +1 -0
- package/dist/output/visuals.js +194 -0
- package/dist/output/visuals.js.map +1 -0
- package/dist/services/drift-analysis.d.sync-conflict-20260306-151016-6PCZ3ZU.ts +194 -0
- package/dist/services/drift-analysis.d.ts.sync-conflict-20260307-175904-6PCZ3ZU.map +1 -0
- package/dist/services/drift-analysis.sync-conflict-20260309-133811-6PCZ3ZU.d.ts +194 -0
- package/dist/services/drift-analysis.sync-conflict-20260309-133811-6PCZ3ZU.d.ts.map +1 -0
- package/dist/services/drift-analysis.sync-conflict-20260309-133811-6PCZ3ZU.js +1022 -0
- package/dist/services/drift-analysis.sync-conflict-20260309-133811-6PCZ3ZU.js.map +1 -0
- package/dist/services/skill-export.d.ts.sync-conflict-20260309-171021-6PCZ3ZU.map +1 -0
- package/dist/services/skill-export.sync-conflict-20260309-144621-6PCZ3ZU.d.ts +109 -0
- package/dist/services/skill-export.sync-conflict-20260309-144621-6PCZ3ZU.d.ts.map +1 -0
- package/dist/services/skill-export.sync-conflict-20260309-144621-6PCZ3ZU.js +737 -0
- package/dist/services/skill-export.sync-conflict-20260309-144621-6PCZ3ZU.js.map +1 -0
- package/package.json +14 -14
- package/LICENSE +0 -21
|
@@ -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.sync-conflict-20260309-144621-6PCZ3ZU.js.map
|