specweave 1.0.274 → 1.0.276

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 (38) hide show
  1. package/.claude-plugin/README.md +1 -2
  2. package/.claude-plugin/marketplace.json +0 -11
  3. package/dist/src/adapters/agents-md-generator.d.ts.map +1 -1
  4. package/dist/src/adapters/agents-md-generator.js +1 -4
  5. package/dist/src/adapters/agents-md-generator.js.map +1 -1
  6. package/dist/src/adapters/claude-md-generator.js +1 -1
  7. package/dist/src/adapters/claude-md-generator.js.map +1 -1
  8. package/dist/src/core/lazy-loading/llm-plugin-detector.d.ts +2 -2
  9. package/dist/src/core/lazy-loading/llm-plugin-detector.d.ts.map +1 -1
  10. package/dist/src/core/lazy-loading/llm-plugin-detector.js +0 -1
  11. package/dist/src/core/lazy-loading/llm-plugin-detector.js.map +1 -1
  12. package/dist/src/sync/github-reconciler.d.ts +9 -0
  13. package/dist/src/sync/github-reconciler.d.ts.map +1 -1
  14. package/dist/src/sync/github-reconciler.js +41 -9
  15. package/dist/src/sync/github-reconciler.js.map +1 -1
  16. package/dist/src/utils/auto-install.js +1 -1
  17. package/dist/src/utils/auto-install.js.map +1 -1
  18. package/dist/src/utils/generate-skills-index.d.ts.map +1 -1
  19. package/dist/src/utils/generate-skills-index.js +1 -3
  20. package/dist/src/utils/generate-skills-index.js.map +1 -1
  21. package/package.json +1 -1
  22. package/plugins/PLUGINS-INDEX.md +0 -1
  23. package/plugins/specweave/lib/vendor/sync/github-reconciler.d.ts +9 -0
  24. package/plugins/specweave/lib/vendor/sync/github-reconciler.js +41 -9
  25. package/plugins/specweave/lib/vendor/sync/github-reconciler.js.map +1 -1
  26. package/plugins/specweave/skills/npm/SKILL.md +155 -34
  27. package/plugins/specweave-frontend/PLUGIN.md +2 -1
  28. package/plugins/specweave-frontend/skills/design-system-architect/SKILL.md +1 -6
  29. package/plugins/specweave-frontend/skills/figma/SKILL.md +287 -0
  30. package/plugins/specweave-frontend/skills/frontend/SKILL.md +4 -0
  31. package/plugins/specweave-frontend/skills/frontend-architect/SKILL.md +1 -0
  32. package/plugins/specweave-frontend/skills/frontend-design/SKILL.md +1 -1
  33. package/plugins/specweave-release/commands/npm.md +158 -38
  34. package/plugins/specweave-figma/.claude-plugin/plugin.json +0 -23
  35. package/plugins/specweave-figma/PLUGIN.md +0 -34
  36. package/plugins/specweave-figma/commands/figma-import.md +0 -694
  37. package/plugins/specweave-figma/commands/figma-to-react.md +0 -838
  38. package/plugins/specweave-figma/commands/figma-tokens.md +0 -819
@@ -1,694 +0,0 @@
1
- ---
2
- description: Import Figma designs into your project using Figma REST API or MCP server integration.
3
- ---
4
-
5
- # /sw-figma:import
6
-
7
- Import Figma designs into your project using Figma REST API or MCP server integration.
8
-
9
- You are a Figma integration expert who automates design imports with comprehensive metadata extraction.
10
-
11
- ## Your Task
12
-
13
- Import Figma designs (files, frames, components, styles) into your project with full metadata, assets, and specifications.
14
-
15
- ### 1. Figma API Overview
16
-
17
- **REST API Capabilities**:
18
- - Fetch files, frames, components, and styles
19
- - Export images (PNG, JPG, SVG, PDF)
20
- - Access design tokens (colors, typography, spacing)
21
- - Read component metadata and descriptions
22
- - Retrieve version history
23
-
24
- **API Endpoints**:
25
- ```
26
- GET /v1/files/:file_key # Get file metadata
27
- GET /v1/files/:file_key/nodes # Get specific nodes
28
- GET /v1/images/:file_key # Export images
29
- GET /v1/files/:file_key/components # Get components
30
- GET /v1/files/:file_key/styles # Get styles
31
- GET /v1/teams/:team_id/projects # List projects
32
- GET /v1/files/:file_key/versions # Version history
33
- ```
34
-
35
- **Authentication**:
36
- ```bash
37
- # Personal Access Token (recommended for server-side)
38
- curl -H "X-Figma-Token: YOUR_TOKEN" https://api.figma.com/v1/files/:file_key
39
-
40
- # OAuth 2.0 (recommended for user-facing apps)
41
- # Authorization: Bearer YOUR_OAUTH_TOKEN
42
- ```
43
-
44
- ### 2. Setup Configuration
45
-
46
- **Environment Variables (.env)**:
47
- ```bash
48
- # Required
49
- FIGMA_ACCESS_TOKEN=figd_XXXXXXXXXXXXXXXXXXXX
50
-
51
- # Optional (MCP server)
52
- FIGMA_MCP_ENABLED=true
53
- FIGMA_MCP_SERVER_PATH=/path/to/figma-mcp-server
54
- ```
55
-
56
- **Configuration File (figma.config.json)**:
57
- ```json
58
- {
59
- "fileKey": "ABC123XYZ456",
60
- "fileUrl": "https://www.figma.com/file/ABC123XYZ456/ProjectName",
61
- "importSettings": {
62
- "components": true,
63
- "styles": true,
64
- "assets": true,
65
- "exportFormats": ["svg", "png@2x"],
66
- "skipHidden": true,
67
- "includeMetadata": true
68
- },
69
- "exportPaths": {
70
- "components": "./src/components/figma",
71
- "assets": "./public/assets/figma",
72
- "tokens": "./src/design-tokens",
73
- "metadata": "./.figma/metadata.json"
74
- },
75
- "naming": {
76
- "componentPrefix": "Figma",
77
- "useKebabCase": true,
78
- "preserveFigmaNames": false
79
- },
80
- "mcp": {
81
- "enabled": false,
82
- "serverPath": null,
83
- "cacheDir": "./.figma/cache"
84
- }
85
- }
86
- ```
87
-
88
- ### 3. Figma REST API Integration
89
-
90
- **TypeScript/JavaScript Implementation**:
91
-
92
- ```typescript
93
- import axios from 'axios';
94
- import fs from 'fs/promises';
95
- import path from 'path';
96
-
97
- interface FigmaConfig {
98
- accessToken: string;
99
- fileKey: string;
100
- exportFormats?: string[];
101
- }
102
-
103
- class FigmaImporter {
104
- private baseUrl = 'https://api.figma.com/v1';
105
- private headers: Record<string, string>;
106
- private fileKey: string;
107
-
108
- constructor(config: FigmaConfig) {
109
- this.headers = {
110
- 'X-Figma-Token': config.accessToken,
111
- };
112
- this.fileKey = config.fileKey;
113
- }
114
-
115
- /**
116
- * Fetch file metadata and structure
117
- */
118
- async fetchFile() {
119
- const response = await axios.get(
120
- `${this.baseUrl}/files/${this.fileKey}`,
121
- { headers: this.headers }
122
- );
123
-
124
- return response.data;
125
- }
126
-
127
- /**
128
- * Fetch specific nodes by ID
129
- */
130
- async fetchNodes(nodeIds: string[]) {
131
- const ids = nodeIds.join(',');
132
- const response = await axios.get(
133
- `${this.baseUrl}/files/${this.fileKey}/nodes?ids=${ids}`,
134
- { headers: this.headers }
135
- );
136
-
137
- return response.data.nodes;
138
- }
139
-
140
- /**
141
- * Export images for nodes
142
- */
143
- async exportImages(
144
- nodeIds: string[],
145
- options: {
146
- format?: 'png' | 'jpg' | 'svg' | 'pdf';
147
- scale?: number;
148
- useAbsoluteBounds?: boolean;
149
- } = {}
150
- ) {
151
- const { format = 'png', scale = 2, useAbsoluteBounds = false } = options;
152
- const ids = nodeIds.join(',');
153
-
154
- const response = await axios.get(
155
- `${this.baseUrl}/images/${this.fileKey}?ids=${ids}&format=${format}&scale=${scale}&use_absolute_bounds=${useAbsoluteBounds}`,
156
- { headers: this.headers }
157
- );
158
-
159
- return response.data.images;
160
- }
161
-
162
- /**
163
- * Fetch all components in file
164
- */
165
- async fetchComponents() {
166
- const response = await axios.get(
167
- `${this.baseUrl}/files/${this.fileKey}/components`,
168
- { headers: this.headers }
169
- );
170
-
171
- return response.data.meta.components;
172
- }
173
-
174
- /**
175
- * Fetch all styles (colors, text, effects, grids)
176
- */
177
- async fetchStyles() {
178
- const response = await axios.get(
179
- `${this.baseUrl}/files/${this.fileKey}/styles`,
180
- { headers: this.headers }
181
- );
182
-
183
- return response.data.meta.styles;
184
- }
185
-
186
- /**
187
- * Download image from URL
188
- */
189
- async downloadImage(imageUrl: string, outputPath: string) {
190
- const response = await axios.get(imageUrl, {
191
- responseType: 'arraybuffer',
192
- });
193
-
194
- await fs.mkdir(path.dirname(outputPath), { recursive: true });
195
- await fs.writeFile(outputPath, response.data);
196
-
197
- return outputPath;
198
- }
199
-
200
- /**
201
- * Traverse Figma node tree recursively
202
- */
203
- traverseNodes(node: any, callback: (node: any) => void) {
204
- callback(node);
205
-
206
- if (node.children) {
207
- node.children.forEach((child: any) => {
208
- this.traverseNodes(child, callback);
209
- });
210
- }
211
- }
212
-
213
- /**
214
- * Extract design tokens from file
215
- */
216
- async extractDesignTokens() {
217
- const file = await this.fetchFile();
218
- const styles = await this.fetchStyles();
219
-
220
- const tokens = {
221
- colors: {},
222
- typography: {},
223
- spacing: {},
224
- effects: {},
225
- };
226
-
227
- // Extract color tokens
228
- styles.forEach((style: any) => {
229
- if (style.style_type === 'FILL') {
230
- const node = this.findNodeById(file.document, style.node_id);
231
- if (node?.fills?.[0]) {
232
- const color = node.fills[0];
233
- if (color.type === 'SOLID') {
234
- tokens.colors[style.name] = this.rgbaToHex(color.color);
235
- }
236
- }
237
- }
238
-
239
- // Extract text styles
240
- if (style.style_type === 'TEXT') {
241
- const node = this.findNodeById(file.document, style.node_id);
242
- if (node?.style) {
243
- tokens.typography[style.name] = {
244
- fontFamily: node.style.fontFamily,
245
- fontSize: node.style.fontSize,
246
- fontWeight: node.style.fontWeight,
247
- lineHeight: node.style.lineHeightPx,
248
- letterSpacing: node.style.letterSpacing,
249
- };
250
- }
251
- }
252
-
253
- // Extract effect styles (shadows, blurs)
254
- if (style.style_type === 'EFFECT') {
255
- const node = this.findNodeById(file.document, style.node_id);
256
- if (node?.effects) {
257
- tokens.effects[style.name] = node.effects;
258
- }
259
- }
260
- });
261
-
262
- return tokens;
263
- }
264
-
265
- /**
266
- * Find node by ID in tree
267
- */
268
- private findNodeById(node: any, id: string): any {
269
- if (node.id === id) return node;
270
-
271
- if (node.children) {
272
- for (const child of node.children) {
273
- const found = this.findNodeById(child, id);
274
- if (found) return found;
275
- }
276
- }
277
-
278
- return null;
279
- }
280
-
281
- /**
282
- * Convert RGBA to HEX
283
- */
284
- private rgbaToHex(color: { r: number; g: number; b: number; a?: number }): string {
285
- const r = Math.round(color.r * 255);
286
- const g = Math.round(color.g * 255);
287
- const b = Math.round(color.b * 255);
288
-
289
- return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
290
- }
291
- }
292
-
293
- // Usage
294
- async function importFigmaDesigns() {
295
- const importer = new FigmaImporter({
296
- accessToken: process.env.FIGMA_ACCESS_TOKEN!,
297
- fileKey: 'ABC123XYZ456',
298
- });
299
-
300
- // Fetch file structure
301
- const file = await importer.fetchFile();
302
- console.log('File:', file.name);
303
-
304
- // Fetch components
305
- const components = await importer.fetchComponents();
306
- console.log(`Found ${components.length} components`);
307
-
308
- // Export component images
309
- const componentIds = components.map((c: any) => c.node_id);
310
- const images = await importer.exportImages(componentIds, {
311
- format: 'svg',
312
- scale: 1,
313
- });
314
-
315
- // Download images
316
- for (const [nodeId, imageUrl] of Object.entries(images)) {
317
- const component = components.find((c: any) => c.node_id === nodeId);
318
- const outputPath = `./assets/${component.name}.svg`;
319
- await importer.downloadImage(imageUrl as string, outputPath);
320
- console.log(`Exported: ${outputPath}`);
321
- }
322
-
323
- // Extract design tokens
324
- const tokens = await importer.extractDesignTokens();
325
- await fs.writeFile(
326
- './src/design-tokens/figma-tokens.json',
327
- JSON.stringify(tokens, null, 2)
328
- );
329
- }
330
-
331
- importFigmaDesigns().catch(console.error);
332
- ```
333
-
334
- ### 4. MCP Server Integration
335
-
336
- **MCP Server for Figma** (Recommended for Claude Code):
337
-
338
- MCP (Model Context Protocol) servers provide structured access to Figma via Claude Code.
339
-
340
- **Available MCP Servers**:
341
- 1. **@modelcontextprotocol/server-figma** (Official)
342
- 2. **figma-mcp-server** (Community)
343
- 3. **claude-figma-bridge** (Custom)
344
-
345
- **Setup MCP Server**:
346
-
347
- ```bash
348
- # Install MCP server
349
- npm install -g @modelcontextprotocol/server-figma
350
-
351
- # Configure in Claude Code settings
352
- # ~/.claude/config.json
353
- {
354
- "mcpServers": {
355
- "figma": {
356
- "command": "mcp-server-figma",
357
- "args": [],
358
- "env": {
359
- "FIGMA_ACCESS_TOKEN": "figd_XXXXXXXXXXXXXXXXXXXX"
360
- }
361
- }
362
- }
363
- }
364
- ```
365
-
366
- **Using MCP in Claude Code**:
367
-
368
- Once MCP server is configured, Claude Code can directly access Figma:
369
-
370
- ```typescript
371
- // Claude Code will use MCP tools automatically
372
- // Example: mcp__figma__getFile, mcp__figma__exportImages
373
-
374
- // In your prompt:
375
- // "Import all components from Figma file ABC123XYZ456 and export as SVG"
376
-
377
- // Claude Code will invoke:
378
- // - mcp__figma__getFile({ fileKey: "ABC123XYZ456" })
379
- // - mcp__figma__exportImages({ nodeIds: [...], format: "svg" })
380
- ```
381
-
382
- **MCP Server Benefits**:
383
- - No need to manage API tokens in code
384
- - Automatic rate limiting
385
- - Built-in caching
386
- - Structured tool interface
387
- - Better error handling
388
-
389
- ### 5. Import Workflow
390
-
391
- **Step 1: Analyze Figma File**
392
- ```typescript
393
- // Discover structure
394
- const file = await importer.fetchFile();
395
-
396
- // Find components, frames, pages
397
- const components = [];
398
- const frames = [];
399
-
400
- importer.traverseNodes(file.document, (node) => {
401
- if (node.type === 'COMPONENT') {
402
- components.push({
403
- id: node.id,
404
- name: node.name,
405
- description: node.description,
406
- componentPropertyDefinitions: node.componentPropertyDefinitions,
407
- });
408
- }
409
-
410
- if (node.type === 'FRAME') {
411
- frames.push({
412
- id: node.id,
413
- name: node.name,
414
- width: node.absoluteBoundingBox.width,
415
- height: node.absoluteBoundingBox.height,
416
- });
417
- }
418
- });
419
-
420
- console.log(`Components: ${components.length}, Frames: ${frames.length}`);
421
- ```
422
-
423
- **Step 2: Export Assets**
424
- ```typescript
425
- // Export all components as SVG
426
- const componentIds = components.map(c => c.id);
427
- const svgImages = await importer.exportImages(componentIds, {
428
- format: 'svg',
429
- scale: 1,
430
- });
431
-
432
- // Export all frames as PNG@2x
433
- const frameIds = frames.map(f => f.id);
434
- const pngImages = await importer.exportImages(frameIds, {
435
- format: 'png',
436
- scale: 2,
437
- });
438
- ```
439
-
440
- **Step 3: Save Metadata**
441
- ```typescript
442
- const metadata = {
443
- fileKey: importer.fileKey,
444
- fileName: file.name,
445
- version: file.version,
446
- lastModified: file.lastModified,
447
- components: components.map(c => ({
448
- id: c.id,
449
- name: c.name,
450
- description: c.description,
451
- exportedAs: `./assets/components/${c.name}.svg`,
452
- })),
453
- frames: frames.map(f => ({
454
- id: f.id,
455
- name: f.name,
456
- dimensions: { width: f.width, height: f.height },
457
- exportedAs: `./assets/frames/${f.name}@2x.png`,
458
- })),
459
- importedAt: new Date().toISOString(),
460
- };
461
-
462
- await fs.writeFile(
463
- './.figma/metadata.json',
464
- JSON.stringify(metadata, null, 2)
465
- );
466
- ```
467
-
468
- ### 6. CLI Command
469
-
470
- **NPM Script (package.json)**:
471
- ```json
472
- {
473
- "scripts": {
474
- "figma:import": "tsx scripts/figma-import.ts",
475
- "figma:import:watch": "tsx scripts/figma-import.ts --watch",
476
- "figma:sync": "tsx scripts/figma-sync.ts"
477
- }
478
- }
479
- ```
480
-
481
- **CLI with Arguments**:
482
- ```typescript
483
- // scripts/figma-import.ts
484
- import yargs from 'yargs';
485
-
486
- const argv = yargs(process.argv.slice(2))
487
- .option('file-key', {
488
- type: 'string',
489
- description: 'Figma file key',
490
- required: true,
491
- })
492
- .option('components', {
493
- type: 'boolean',
494
- description: 'Import components',
495
- default: true,
496
- })
497
- .option('assets', {
498
- type: 'boolean',
499
- description: 'Export assets',
500
- default: true,
501
- })
502
- .option('tokens', {
503
- type: 'boolean',
504
- description: 'Extract design tokens',
505
- default: true,
506
- })
507
- .option('format', {
508
- type: 'string',
509
- description: 'Export format (svg, png, jpg)',
510
- default: 'svg',
511
- })
512
- .parseSync();
513
-
514
- const importer = new FigmaImporter({
515
- accessToken: process.env.FIGMA_ACCESS_TOKEN!,
516
- fileKey: argv.fileKey,
517
- });
518
-
519
- if (argv.components) {
520
- const components = await importer.fetchComponents();
521
- console.log(`Importing ${components.length} components...`);
522
- }
523
-
524
- if (argv.assets) {
525
- // Export logic
526
- }
527
-
528
- if (argv.tokens) {
529
- const tokens = await importer.extractDesignTokens();
530
- // Save tokens
531
- }
532
- ```
533
-
534
- **Usage**:
535
- ```bash
536
- # Import everything
537
- npm run figma:import -- --file-key ABC123XYZ456
538
-
539
- # Import only components
540
- npm run figma:import -- --file-key ABC123XYZ456 --no-assets --no-tokens
541
-
542
- # Export as PNG
543
- npm run figma:import -- --file-key ABC123XYZ456 --format png
544
- ```
545
-
546
- ### 7. Rate Limiting and Caching
547
-
548
- **Rate Limits** (Figma API):
549
- - 30 requests per minute per IP
550
- - 1000 requests per hour per token
551
-
552
- **Implement Caching**:
553
- ```typescript
554
- import NodeCache from 'node-cache';
555
-
556
- class CachedFigmaImporter extends FigmaImporter {
557
- private cache: NodeCache;
558
-
559
- constructor(config: FigmaConfig) {
560
- super(config);
561
- this.cache = new NodeCache({ stdTTL: 3600 }); // 1 hour TTL
562
- }
563
-
564
- async fetchFile() {
565
- const cacheKey = `file:${this.fileKey}`;
566
- const cached = this.cache.get(cacheKey);
567
-
568
- if (cached) {
569
- console.log('Using cached file data');
570
- return cached;
571
- }
572
-
573
- const file = await super.fetchFile();
574
- this.cache.set(cacheKey, file);
575
- return file;
576
- }
577
- }
578
- ```
579
-
580
- **Rate Limiting**:
581
- ```typescript
582
- import Bottleneck from 'bottleneck';
583
-
584
- class RateLimitedFigmaImporter extends FigmaImporter {
585
- private limiter: Bottleneck;
586
-
587
- constructor(config: FigmaConfig) {
588
- super(config);
589
-
590
- // 30 requests per minute
591
- this.limiter = new Bottleneck({
592
- minTime: 2000, // 2s between requests
593
- maxConcurrent: 1,
594
- });
595
- }
596
-
597
- async fetchFile() {
598
- return this.limiter.schedule(() => super.fetchFile());
599
- }
600
-
601
- async exportImages(...args: any[]) {
602
- return this.limiter.schedule(() => super.exportImages(...args));
603
- }
604
- }
605
- ```
606
-
607
- ### 8. Error Handling
608
-
609
- **Common API Errors**:
610
- - 401 Unauthorized: Invalid access token
611
- - 403 Forbidden: No permission to access file
612
- - 404 Not Found: File or node doesn't exist
613
- - 429 Too Many Requests: Rate limit exceeded
614
- - 500 Server Error: Figma API issue
615
-
616
- **Error Handling Pattern**:
617
- ```typescript
618
- async function importWithRetry(fileKey: string, retries = 3) {
619
- for (let i = 0; i < retries; i++) {
620
- try {
621
- const importer = new FigmaImporter({
622
- accessToken: process.env.FIGMA_ACCESS_TOKEN!,
623
- fileKey,
624
- });
625
-
626
- return await importer.fetchFile();
627
- } catch (error) {
628
- if (axios.isAxiosError(error)) {
629
- const status = error.response?.status;
630
-
631
- if (status === 401) {
632
- throw new Error('Invalid Figma access token');
633
- }
634
-
635
- if (status === 403) {
636
- throw new Error('No permission to access this file');
637
- }
638
-
639
- if (status === 429) {
640
- const retryAfter = error.response?.headers['retry-after'] || 60;
641
- console.log(`Rate limited. Retrying after ${retryAfter}s...`);
642
- await sleep(retryAfter * 1000);
643
- continue;
644
- }
645
-
646
- if (status === 500 && i < retries - 1) {
647
- console.log(`Server error. Retrying (${i + 1}/${retries})...`);
648
- await sleep(2000);
649
- continue;
650
- }
651
- }
652
-
653
- throw error;
654
- }
655
- }
656
-
657
- throw new Error('Max retries exceeded');
658
- }
659
- ```
660
-
661
- ## Workflow
662
-
663
- 1. Ask about Figma file URL and access requirements
664
- 2. Extract file key from URL
665
- 3. Verify Figma access token (from .env or prompt)
666
- 4. Fetch file metadata and analyze structure
667
- 5. Ask about import preferences (components, assets, tokens)
668
- 6. Set up directory structure for exports
669
- 7. Export assets with specified formats and scales
670
- 8. Extract design tokens and metadata
671
- 9. Save all data to configured paths
672
- 10. Generate import report with statistics
673
-
674
- ## When to Use
675
-
676
- - Starting new projects with Figma designs
677
- - Syncing design systems with code
678
- - Automating asset exports from Figma
679
- - Extracting design tokens for theme configuration
680
- - Migrating from manual Figma exports to automated workflow
681
- - Setting up CI/CD for design-to-code pipeline
682
-
683
- ## Best Practices
684
-
685
- 1. **Security**: Store access tokens in .env (never commit)
686
- 2. **Caching**: Cache file data to reduce API calls
687
- 3. **Rate Limiting**: Respect Figma API limits (30 req/min)
688
- 4. **Versioning**: Track Figma file versions in metadata
689
- 5. **Organization**: Structure exports by component type
690
- 6. **Documentation**: Include component descriptions from Figma
691
- 7. **Automation**: Use watch mode for continuous sync
692
- 8. **Testing**: Validate exports before committing
693
-
694
- Import Figma designs with production-ready automation!