@pedroab0/skills 1.1.0 → 1.3.0
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/bin/cli.js +61 -11
- package/figma-extractor/README.md +2 -1
- package/figma-extractor/REFERENCE.md +111 -0
- package/figma-extractor/SKILL.md +45 -169
- package/figma-extractor/figma-plugin/code.js +461 -87
- package/figma-extractor/figma-plugin/ui.html +76 -23
- package/figma-extractor/scripts/fetch_figma.js +286 -35
- package/figma-node-builder/SKILL.md +82 -93
- package/figma-to-code/README.md +2 -1
- package/figma-to-code/REFERENCE.md +110 -0
- package/figma-to-code/SKILL.md +50 -181
- package/figma-to-code/scripts/fetch_figma.js +286 -35
- package/package.json +8 -3
- package/figma-extractor/demo/desktop_home.json +0 -2434
- package/figma-extractor/demo/dt_money_desktop_home.png +0 -0
- package/figma-extractor/demo/dt_money_preview.png +0 -0
- package/figma-extractor/demo/dt_money_selection.json +0 -472
- package/figma-extractor/demo/index.html +0 -188
- package/figma-extractor/demo/style.css +0 -360
- package/figma-extractor/figma-plugin/assets/cover-1920.png +0 -0
- package/figma-extractor/figma-plugin/assets/cover.jpg +0 -0
- package/figma-extractor/figma-plugin/assets/cover.png +0 -0
- package/figma-extractor/figma-plugin/assets/icon-128.png +0 -0
- package/figma-extractor/figma-plugin/assets/icon.png +0 -0
- package/figma-extractor/scripts/test_fetch_figma.js +0 -687
- package/figma-node-builder/scripts/test_inspect_spec.js +0 -163
- package/figma-to-code/scripts/test_fetch_figma.js +0 -660
package/bin/cli.js
CHANGED
|
@@ -72,9 +72,37 @@ ${style.bold('Examples:')}
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
// -----------------------------------------------------------------------------
|
|
75
|
-
// Skill Discovery
|
|
75
|
+
// Frontmatter & Skill Discovery
|
|
76
76
|
// -----------------------------------------------------------------------------
|
|
77
77
|
|
|
78
|
+
function parseFrontmatterField(content, field) {
|
|
79
|
+
const fm = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
80
|
+
if (!fm) return '';
|
|
81
|
+
const lines = fm[1].split(/\r?\n/);
|
|
82
|
+
const line = lines.find((l) => l.startsWith(`${field}:`));
|
|
83
|
+
if (!line) return '';
|
|
84
|
+
const raw = line.slice(field.length + 1).trim();
|
|
85
|
+
if (raw.startsWith("'") && raw.endsWith("'")) {
|
|
86
|
+
return raw.slice(1, -1).replace(/''/g, "'");
|
|
87
|
+
}
|
|
88
|
+
if (raw.startsWith('"') && raw.endsWith('"')) {
|
|
89
|
+
try {
|
|
90
|
+
return JSON.parse(raw);
|
|
91
|
+
} catch (_) {
|
|
92
|
+
return raw.slice(1, -1);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return raw;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function rewriteRelativeLinksForCursor(body, skill) {
|
|
99
|
+
return body.replace(/\]\((?!https?:|#|\/)([^)\s#]+\.md)(#[^)]*)?\)/g, (m, rel, hash = '') => {
|
|
100
|
+
return fs.existsSync(path.join(skill.path, rel))
|
|
101
|
+
? `](../skills/${skill.name}/${rel}${hash || ''})`
|
|
102
|
+
: m;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
78
106
|
function getAvailableSkills() {
|
|
79
107
|
const skills = [];
|
|
80
108
|
try {
|
|
@@ -87,10 +115,7 @@ function getAvailableSkills() {
|
|
|
87
115
|
let description = '';
|
|
88
116
|
try {
|
|
89
117
|
const content = fs.readFileSync(skillMdPath, 'utf8');
|
|
90
|
-
|
|
91
|
-
if (match) {
|
|
92
|
-
description = match[1].trim();
|
|
93
|
-
}
|
|
118
|
+
description = parseFrontmatterField(content, 'description');
|
|
94
119
|
} catch (_) {}
|
|
95
120
|
skills.push({
|
|
96
121
|
name: entry.name,
|
|
@@ -506,12 +531,32 @@ function checkFigmaToken(options = {}) {
|
|
|
506
531
|
// Skill Installation Logic
|
|
507
532
|
// -----------------------------------------------------------------------------
|
|
508
533
|
|
|
534
|
+
const EXCLUDED_SKILL_DIRS = new Set(['demo', 'demos', '.specs', 'node_modules', '.git']);
|
|
535
|
+
|
|
536
|
+
function shouldCopySkillEntry(rootSkillPath) {
|
|
537
|
+
return (srcPath) => {
|
|
538
|
+
const rel = path.relative(rootSkillPath, srcPath);
|
|
539
|
+
if (!rel) return true;
|
|
540
|
+
const parts = rel.split(/[\\/]/);
|
|
541
|
+
const base = parts[parts.length - 1];
|
|
542
|
+
if (base === '.DS_Store' || base === 'Thumbs.db' || /^test_.*\.js$/i.test(base)) {
|
|
543
|
+
return false;
|
|
544
|
+
}
|
|
545
|
+
for (let i = 0; i < parts.length; i++) {
|
|
546
|
+
if (EXCLUDED_SKILL_DIRS.has(parts[i])) return false;
|
|
547
|
+
if (parts[i] === 'figma-plugin' && parts[i + 1] === 'assets') return false;
|
|
548
|
+
}
|
|
549
|
+
return true;
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
|
|
509
553
|
function installSkill(skill, destDir, useSymlink = false, isCursor = false) {
|
|
510
554
|
if (!fs.existsSync(destDir)) {
|
|
511
555
|
fs.mkdirSync(destDir, { recursive: true });
|
|
512
556
|
}
|
|
513
557
|
|
|
514
558
|
const targetPath = path.join(destDir, skill.name);
|
|
559
|
+
const copyFilter = shouldCopySkillEntry(skill.path);
|
|
515
560
|
|
|
516
561
|
if (isCursor) {
|
|
517
562
|
const cursorSkillsDir = path.join(path.dirname(destDir), 'skills', skill.name);
|
|
@@ -520,14 +565,17 @@ function installSkill(skill, destDir, useSymlink = false, isCursor = false) {
|
|
|
520
565
|
if (fs.existsSync(cursorSkillsDir)) {
|
|
521
566
|
fs.rmSync(cursorSkillsDir, { recursive: true, force: true });
|
|
522
567
|
}
|
|
523
|
-
fs.cpSync(skill.path, cursorSkillsDir, { recursive: true });
|
|
568
|
+
fs.cpSync(skill.path, cursorSkillsDir, { recursive: true, filter: copyFilter });
|
|
524
569
|
|
|
525
570
|
const rawSkillMd = fs.readFileSync(path.join(skill.path, 'SKILL.md'), 'utf8');
|
|
526
|
-
const skillBody =
|
|
571
|
+
const skillBody = rewriteRelativeLinksForCursor(
|
|
572
|
+
rawSkillMd.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n*/, ''),
|
|
573
|
+
skill,
|
|
574
|
+
);
|
|
527
575
|
const ruleFile = path.join(destDir, `${skill.name}.mdc`);
|
|
528
576
|
const ruleContent = `---
|
|
529
|
-
description: ${skill.description}
|
|
530
|
-
|
|
577
|
+
description: ${JSON.stringify(skill.description)}
|
|
578
|
+
alwaysApply: false
|
|
531
579
|
---
|
|
532
580
|
|
|
533
581
|
${skillBody}`;
|
|
@@ -545,11 +593,11 @@ ${skillBody}`;
|
|
|
545
593
|
fs.symlinkSync(skill.path, targetPath, symlinkType);
|
|
546
594
|
return { targetPath, isSymlink: true };
|
|
547
595
|
} catch (e) {
|
|
548
|
-
fs.cpSync(skill.path, targetPath, { recursive: true });
|
|
596
|
+
fs.cpSync(skill.path, targetPath, { recursive: true, filter: copyFilter });
|
|
549
597
|
return { targetPath, isSymlink: false };
|
|
550
598
|
}
|
|
551
599
|
} else {
|
|
552
|
-
fs.cpSync(skill.path, targetPath, { recursive: true });
|
|
600
|
+
fs.cpSync(skill.path, targetPath, { recursive: true, filter: copyFilter });
|
|
553
601
|
const scriptsDir = path.join(targetPath, 'scripts');
|
|
554
602
|
if (fs.existsSync(scriptsDir)) {
|
|
555
603
|
try {
|
|
@@ -860,6 +908,8 @@ if (require.main === module) {
|
|
|
860
908
|
|
|
861
909
|
module.exports = {
|
|
862
910
|
selectPrompt,
|
|
911
|
+
parseFrontmatterField,
|
|
912
|
+
rewriteRelativeLinksForCursor,
|
|
863
913
|
getAvailableSkills,
|
|
864
914
|
getAgentConfigs,
|
|
865
915
|
checkFigmaToken,
|
|
@@ -129,6 +129,7 @@ node figma-extractor/scripts/fetch_figma.js "<FigmaURL>" --tokens --spec tokens
|
|
|
129
129
|
| `--spec <name>` | | **Atomic SDD mode**: Saves `./.specs/<name>.json` (add `-i` to also download preview PNG). |
|
|
130
130
|
| `--image`, `--download-image` | `-i` | Download and save rendered preview image from Figma (Tier 1 call; optional). |
|
|
131
131
|
| `--image-path <path>` | `-o <path>` | Custom destination path for saved preview image (e.g., `./.specs/card.png`). |
|
|
132
|
+
| `--force`, `--fresh` | `-f` | Ignore existing local spec/preview and re-fetch freshly from Figma. |
|
|
132
133
|
| `--tokens`, `--variables` | | Export only the file's design token dictionary (colors, spacing, radii). |
|
|
133
134
|
| `--shallow-instances` | | Collapse internal layers of component instances into clean props. |
|
|
134
135
|
| `--include-tokens` | | Bundle complete design tokens dictionary alongside the node tree. |
|
|
@@ -137,7 +138,7 @@ node figma-extractor/scripts/fetch_figma.js "<FigmaURL>" --tokens --spec tokens
|
|
|
137
138
|
| `--raw` | | Return full unpruned Figma API response without filtering. |
|
|
138
139
|
| `--depth <number>` | | Limit node tree traversal depth (e.g., `--depth 2` for top-level layout only). |
|
|
139
140
|
| `--scale <1\|2\|3\|4>` | | Multiplier for rendered image resolution (default: `2`). |
|
|
140
|
-
| `--format <png\|svg\|jpg>` | | Image format for downloaded preview (default: `png`). |
|
|
141
|
+
| `--format <png\|svg\|jpg\|pdf>` | | Image format for downloaded preview (default: `png`). |
|
|
141
142
|
| `--no-variables` | | Skip querying local variables to speed up extraction. |
|
|
142
143
|
| `--help` | `-h` | Display command usage and available flags. |
|
|
143
144
|
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Figma Extractor Reference
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- Token setup
|
|
6
|
+
- Corporate proxy and SSL
|
|
7
|
+
- CLI flags
|
|
8
|
+
- Output spec schema
|
|
9
|
+
- Rate-limit gate internals
|
|
10
|
+
|
|
11
|
+
## Token setup
|
|
12
|
+
|
|
13
|
+
A Figma Personal Access Token (or OAuth token) is required. The user creates one in **Figma Settings → Personal Access Tokens**. The script reads `FIGMA_ACCESS_TOKEN` (or `FIGMA_TOKEN`) from the environment, from `.env` / `.env.local` in the working directory, from `~/.env`, and from shell profiles.
|
|
14
|
+
|
|
15
|
+
| Platform | Command for the user |
|
|
16
|
+
| :--- | :--- |
|
|
17
|
+
| macOS / Linux | `export FIGMA_ACCESS_TOKEN="figd_your_token_here"` in `~/.zshrc` or `~/.bashrc` |
|
|
18
|
+
| Windows (PowerShell) | `[Environment]::SetEnvironmentVariable("FIGMA_ACCESS_TOKEN", "figd_your_token_here", "User")` |
|
|
19
|
+
| Any (workspace) | `FIGMA_ACCESS_TOKEN=figd_your_token_here` in `.env` |
|
|
20
|
+
|
|
21
|
+
## Corporate proxy and SSL
|
|
22
|
+
|
|
23
|
+
Behind a corporate proxy with self-signed certificates, the script fails with `FIGMA_SSL_CERTIFICATE_ERROR`. The user can disable certificate verification for Figma requests:
|
|
24
|
+
|
|
25
|
+
```env
|
|
26
|
+
FIGMA_IGNORE_SSL=true
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## CLI flags
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
node "<skill-dir>/scripts/fetch_figma.js" <FigmaURL> [options]
|
|
33
|
+
node "<skill-dir>/scripts/fetch_figma.js" <fileKey> [nodeId] [options]
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
| Flag | Shorthand | Description |
|
|
37
|
+
| :--- | :--- | :--- |
|
|
38
|
+
| `--spec <name>` | | Save `./.specs/<name>.json` (and `<name>.png` with `-i`). Reuses an existing spec with 0 API calls. |
|
|
39
|
+
| `--image`, `--download-image` | `-i` | Download the rendered preview image (+1 Tier 1 call). |
|
|
40
|
+
| `--image-path <path>`, `--output <path>` | `-o <path>` | Destination path for the preview image (implies `--image`). |
|
|
41
|
+
| `--force`, `--fresh` | `-f` | Ignore the existing local spec/preview and re-fetch from Figma. |
|
|
42
|
+
| `--tokens`, `--variables` | | Output only the design token dictionary (colors, spacing, radii). |
|
|
43
|
+
| `--shallow-instances` | | Collapse internal layers of component instances into props. |
|
|
44
|
+
| `--include-tokens` | | Include the full design token dictionary alongside the node tree. |
|
|
45
|
+
| `--include-ids` | | Keep internal Figma node IDs on each node. |
|
|
46
|
+
| `--pretty` | | Pretty-print JSON with 2-space indentation (default is compact). |
|
|
47
|
+
| `--raw` | | Return the full, unpruned Figma API response. Large; avoid unless debugging. |
|
|
48
|
+
| `--depth <number>` | | Limit node tree traversal depth (e.g. `--depth 2` for an overview). |
|
|
49
|
+
| `--scale <1\|2\|3\|4>` | | Preview render scale (default: `2`). |
|
|
50
|
+
| `--format <png\|svg\|jpg\|pdf>` | | Preview image format (default: `png`). |
|
|
51
|
+
| `--no-variables` | | Skip querying `/variables/local`. |
|
|
52
|
+
| `--help` | `-h` | Show the help message. |
|
|
53
|
+
|
|
54
|
+
## Output spec schema
|
|
55
|
+
|
|
56
|
+
Each node keeps only properties that differ from Figma defaults. Hidden layers are dropped. Pure vector groups collapse into a single `VECTOR` node with `isSvg: true`.
|
|
57
|
+
|
|
58
|
+
| Field | Meaning |
|
|
59
|
+
| :--- | :--- |
|
|
60
|
+
| `name`, `type` | Layer name and node type (`FRAME`, `INSTANCE`, `TEXT`, `VECTOR`, ...). |
|
|
61
|
+
| `bounds` | `{ width, height }` render dimensions. |
|
|
62
|
+
| `layoutMode`, `layoutWrap`, `primaryAxisAlignItems`, `counterAxisAlignItems` | Auto-layout direction and alignment. |
|
|
63
|
+
| `layoutGrow`, `layoutAlign`, `minWidth`, `maxWidth`, `minHeight`, `maxHeight` | Child sizing constraints. |
|
|
64
|
+
| `padding` / `paddingToken`, `itemSpacing` / `itemSpacingToken` | Spacing values and bound tokens. |
|
|
65
|
+
| `cornerRadius` / `cornerRadiusToken`, `cornerRadii` | Uniform or per-corner radius. |
|
|
66
|
+
| `fills`, `strokes`, `effects`, `opacity` | Paints (hex `color` + optional `token`), borders, shadows/blurs. |
|
|
67
|
+
| `characters`, `typography` | Text content and `fontFamily`, `fontSize`, `fontWeight`, `lineHeightPx`, `letterSpacing`, `textAlignHorizontal`, `textAlignVertical`. |
|
|
68
|
+
| `component`, `props` | Master component `name`/`variant` and normalized instance props. |
|
|
69
|
+
| `children` | Child nodes. |
|
|
70
|
+
| `image` | Present when `-i` was used: preview `path`, or `error`. |
|
|
71
|
+
|
|
72
|
+
### Resolved design tokens
|
|
73
|
+
|
|
74
|
+
When a layer is bound to Figma variables, the semantic token name is attached next to the computed fallback value:
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"name": "Card Container",
|
|
79
|
+
"type": "FRAME",
|
|
80
|
+
"layoutMode": "VERTICAL",
|
|
81
|
+
"padding": { "top": 16, "right": 16, "bottom": 16, "left": 16 },
|
|
82
|
+
"paddingToken": "spacing/md",
|
|
83
|
+
"itemSpacing": 12,
|
|
84
|
+
"itemSpacingToken": "spacing/sm",
|
|
85
|
+
"cornerRadius": 8,
|
|
86
|
+
"cornerRadiusToken": "radii/lg",
|
|
87
|
+
"fills": [{ "type": "SOLID", "color": "#1877F2", "token": "colors/brand/primary" }]
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Normalized component instances
|
|
92
|
+
|
|
93
|
+
Figma property hashes (`Label#12:3`) become clean props linked to the master component:
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"name": "Submit Button",
|
|
98
|
+
"type": "INSTANCE",
|
|
99
|
+
"component": { "name": "Button", "variant": "Variant=Primary, Size=Large" },
|
|
100
|
+
"props": { "variant": "Primary", "size": "Large", "hasIcon": true, "label": "Confirm Order" },
|
|
101
|
+
"bounds": { "width": 140, "height": 44 }
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Rate-limit gate internals
|
|
106
|
+
|
|
107
|
+
The script protects the file quota automatically. No agent action is needed beyond the Zero-Hammer rules in `SKILL.md`.
|
|
108
|
+
|
|
109
|
+
- **Sliding window**: at most 5 requests per minute per file key (Figma throttles at roughly 10/min).
|
|
110
|
+
- **Pacing**: at least 2.5 s between consecutive requests.
|
|
111
|
+
- **Circuit breaker**: after an HTTP 429, the lockout end time is stored in `~/.figma_ratelimit.json`. Later calls to that file key fail immediately with `FIGMA_CIRCUIT_BREAKER_ACTIVE` and send no network traffic, so the lockout is not extended.
|
package/figma-extractor/SKILL.md
CHANGED
|
@@ -1,201 +1,77 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: figma-extractor
|
|
3
|
-
description: '
|
|
3
|
+
description: 'Extracts Figma frames and components into compact JSON spec files (.specs/*.json) with resolved design tokens, normalized component instance props, and optional preview images, using the Figma REST API. Use when the user shares a figma.com link and wants to inspect, extract, document, or plan a design before implementation, or asks to sync Figma variables and design tokens. Produces specification artifacts only and never edits application code. For immediate one-shot code generation from a Figma link, use figma-to-code instead.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Figma Extractor
|
|
6
|
+
# Figma Extractor (SDD Spec Ingest)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Writes Figma designs to `.specs/<name>.json` (and optionally `.specs/<name>.png`) so a later implementation step can build from a fixed specification. A **spec** is the pruned Figma node tree with resolved tokens and component props.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
Requires Node.js >= 16.7. The scripts have zero dependencies (no `npm install`).
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
## Operational Boundary: Spec Ingestion Only
|
|
15
|
-
|
|
16
|
-
* **Specification Artifacts Only**: This skill strictly outputs machine-readable design specifications (`.specs/<name>.json`) and visual references (`.specs/<name>.png`).
|
|
17
|
-
* **Zero Application Code Changes**: Do **NOT** create, edit, or modify application source code (`.tsx`, `.vue`, `.html`, `.css`, etc.) while executing this skill.
|
|
18
|
-
* **Hand-off to Implementation**: Once specifications are generated in `./.specs/`, present a summary of the extracted tokens, components, and layout bounds. The implementation phase is handled separately by the developer or a downstream builder skill (such as `figma-node-builder`).
|
|
19
|
-
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
## Strict Guardrails: No Browser Scraping / Automation
|
|
23
|
-
|
|
24
|
-
> [!CAUTION]
|
|
25
|
-
> **NEVER use browser tools (Playwright, Puppeteer, Chrome DevTools, `browser_navigate`, `read_browser_page`) on Figma URLs.**
|
|
26
|
-
>
|
|
27
|
-
> 1. **Figma Canvas is WebGL/Wasm**: Figma renders design canvas elements inside an HTML5 `<canvas>` via WebAssembly. There are NO inspectable HTML DOM elements for frames, layers, texts, or styles. Browser automation cannot extract Figma nodes or properties.
|
|
28
|
-
> 2. **Never search for local browser binaries**: Do not search OS application directories (e.g., `/Applications`, `Program Files`, `/usr/bin`) or run browser automation scripts.
|
|
29
|
-
> 3. **API Errors Are Final**: If `fetch_figma.js` returns an API error (e.g., 401 Unauthorized, 403 Forbidden, or 429 Rate Limited), **report the error directly to the user**. Guide them to check their `FIGMA_ACCESS_TOKEN`, permissions, or wait out rate limits. Do NOT attempt browser scraping fallbacks.
|
|
30
|
-
|
|
31
|
-
---
|
|
12
|
+
## Boundaries
|
|
32
13
|
|
|
33
|
-
|
|
14
|
+
- Output only `.specs/` artifacts. **Never create or edit application code** (`.tsx`, `.vue`, `.html`, `.css`, ...) while using this skill.
|
|
15
|
+
- **Never open Figma URLs with browser tools** (Playwright, Puppeteer, Chrome DevTools). The Figma canvas is WebGL, so there is no DOM to read. Do not search for local browser binaries.
|
|
16
|
+
- **API errors are final**: report them to the user. Never fall back to scraping.
|
|
34
17
|
|
|
35
|
-
|
|
36
|
-
* **Max 5 requests per minute** per file key (sliding window limit).
|
|
37
|
-
* **2.5s inter-request pacing** between consecutive requests.
|
|
38
|
-
* **Persistent Circuit Breaker**: If Figma returns HTTP 429, the script records the lockout time locally in `.figma_ratelimit.json` inside the user's home directory. All future calls to that file key are **immediately blocked with zero network traffic**, preventing Figma from extending the lockout penalty.
|
|
18
|
+
## Workflow
|
|
39
19
|
|
|
40
|
-
|
|
41
|
-
1. **Single-Shot Execution**: Run `fetch_figma.js` **once** per target frame or component.
|
|
42
|
-
2. **Never Retry on Failure**: If `fetch_figma.js` returns an error, **DO NOT retry in a loop**. Report the error directly to the user.
|
|
43
|
-
3. **Handling `FIGMA_CIRCUIT_BREAKER_ACTIVE`**: If the circuit breaker trips, inform the user that the file key is on cooldown, and suggest duplicating the file in Figma Drafts (Right-click $\rightarrow$ **Duplicate**) to get a fresh URL.
|
|
20
|
+
Copy this checklist and track your progress:
|
|
44
21
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
Configure it using either:
|
|
52
|
-
1. **Environment Variable**:
|
|
53
|
-
- **macOS / Linux**: `export FIGMA_ACCESS_TOKEN="figd_your_token_here"` (or `FIGMA_TOKEN`)
|
|
54
|
-
- **Windows (PowerShell)**: `[Environment]::SetEnvironmentVariable("FIGMA_ACCESS_TOKEN", "figd_your_token_here", "User")` or `$env:FIGMA_ACCESS_TOKEN="figd_your_token_here"` in `$PROFILE`
|
|
55
|
-
2. **Workspace `.env` File**:
|
|
56
|
-
```env
|
|
57
|
-
FIGMA_ACCESS_TOKEN=figd_your_token_here
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### Corporate Proxy & SSL Certificates
|
|
61
|
-
If you are behind a corporate proxy or firewall with self-signed SSL certificates, enable SSL verification bypass:
|
|
62
|
-
```env
|
|
63
|
-
FIGMA_IGNORE_SSL=true
|
|
22
|
+
```
|
|
23
|
+
Extraction Progress:
|
|
24
|
+
- [ ] 1. Run the extraction (once)
|
|
25
|
+
- [ ] 2. Verify the result
|
|
26
|
+
- [ ] 3. Hand off a summary
|
|
64
27
|
```
|
|
65
28
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
## Tier 1 Quota Conservation: AST-Only by Default
|
|
69
|
-
|
|
70
|
-
> [!IMPORTANT]
|
|
71
|
-
> **Figma Starter plans enforce a strict limit of 20 Tier 1 requests/month.**
|
|
72
|
-
> Both `GET /v1/files/:key/nodes` and `GET /v1/images/:key` count as Tier 1 endpoints.
|
|
73
|
-
>
|
|
74
|
-
> 1. **Default to AST-only extraction**: Running `--spec <name>` without `-i` consumes **only 1 Tier 1 call**. The JSON AST contains 100% of layout rules (flexbox, padding, gap), dimensions, hex colors, corner radii, and text content needed for code generation.
|
|
75
|
-
> 2. **DO NOT pass `-i` / `--image` by default**: Only add `-i` if the user explicitly requests downloading a visual preview screenshot.
|
|
76
|
-
> 3. **Reuse existing specs**: If `.specs/<name>.json` exists, reuse it with **0 API calls**.
|
|
77
|
-
|
|
78
|
-
---
|
|
79
|
-
|
|
80
|
-
## Agent Execution Instructions
|
|
81
|
-
|
|
82
|
-
To extract a Figma frame or component as a specification artifact, run `fetch_figma.js` from this skill's `scripts/` directory:
|
|
29
|
+
**1. Run the extraction** (1 Tier 1 API call):
|
|
83
30
|
|
|
84
31
|
```bash
|
|
85
|
-
# Recommended: Atomic extraction (writes ./.specs/<name>.json - 1 Tier 1 call)
|
|
86
32
|
node "<skill-dir>/scripts/fetch_figma.js" "<FigmaURL>" --spec <name>
|
|
87
|
-
|
|
88
|
-
# Optional: With companion preview image (writes .json and .png - 2 Tier 1 calls)
|
|
89
|
-
node "<skill-dir>/scripts/fetch_figma.js" "<FigmaURL>" --spec <name> -i
|
|
90
33
|
```
|
|
91
34
|
|
|
92
|
-
|
|
93
|
-
Substitute `<skill-dir>` with the directory where this skill is installed — **never guess another agent's folder** (always keep double quotes around the script path for Windows/macOS/Linux compatibility):
|
|
94
|
-
- **Standard Skills (`SKILL.md`)**: `<skill-dir>` is the exact parent directory of this `SKILL.md` file.
|
|
95
|
-
- **Cursor Rule (`.cursor/rules/figma-extractor.mdc`)**: `<skill-dir>` is the sibling `.cursor/skills/figma-extractor` directory (replace `rules/figma-extractor.mdc` with `skills/figma-extractor`).
|
|
35
|
+
If `.specs/<name>.json` already exists, the script reuses it with 0 API calls. Pass `--force` only when the user explicitly asks to refresh the spec.
|
|
96
36
|
|
|
97
|
-
|
|
37
|
+
**2. Verify the result**
|
|
98
38
|
|
|
99
|
-
|
|
39
|
+
- Success: stdout shows `✔ Saved Figma specification` or `✔ Reusing local Figma specification`.
|
|
40
|
+
- Failure: stderr contains a JSON error payload. Follow [Errors](#errors). Do not retry.
|
|
100
41
|
|
|
101
|
-
|
|
42
|
+
**3. Hand off**: report the spec path and size, the root frame, the main component instances, and the design tokens found. Implementation is handled separately by the developer or by the `figma-node-builder` skill.
|
|
102
43
|
|
|
103
|
-
|
|
44
|
+
## Common variants
|
|
104
45
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
46
|
+
| Need | Command suffix |
|
|
47
|
+
| :--- | :--- |
|
|
48
|
+
| Preview PNG (only if the user asks; +1 Tier 1 call) | `--spec <name> -i` |
|
|
49
|
+
| Design tokens dictionary only | `--tokens --spec tokens` |
|
|
50
|
+
| Collapse instance internals into props | `--spec <name> --shallow-instances` |
|
|
51
|
+
| Top-level overview of a large frame | `--spec <name> --depth 2` |
|
|
108
52
|
|
|
109
|
-
|
|
110
|
-
node "<skill-dir>/scripts/fetch_figma.js" "https://www.figma.com/design/:fileKey/:name?node-id=4023-474" --spec card -i
|
|
53
|
+
All CLI flags, the output spec schema, token setup per OS, and proxy options: see [REFERENCE.md](REFERENCE.md) (located at `<skill-dir>/REFERENCE.md`).
|
|
111
54
|
|
|
112
|
-
|
|
113
|
-
node "<skill-dir>/scripts/fetch_figma.js" "https://www.figma.com/design/:fileKey/:name?node-id=4023-474" --spec card --shallow-instances
|
|
114
|
-
```
|
|
55
|
+
## Resolving `<skill-dir>`
|
|
115
56
|
|
|
116
|
-
|
|
57
|
+
Substitute `<skill-dir>` with the directory where this skill is installed. Never guess another agent's folder, and keep the double quotes around the script path (Windows/macOS/Linux):
|
|
117
58
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
node "<skill-dir>/scripts/fetch_figma.js" "https://www.figma.com/design/:fileKey/:name" --tokens --spec tokens
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
### 3. CLI Flags Reference
|
|
124
|
-
|
|
125
|
-
| Flag | Shorthand | Description |
|
|
126
|
-
| :--- | :--- | :--- |
|
|
127
|
-
| `--spec <name>` | | **Atomic SDD mode**: Saves `./.specs/<name>.json` (add `-i` to also download preview PNG). |
|
|
128
|
-
| `--image`, `--download-image` | `-i` | Download and save rendered preview image from Figma (Tier 1 call; optional). |
|
|
129
|
-
| `--image-path <path>` | `-o <path>` | Destination path for saved preview image (e.g. `./.specs/preview.png`). |
|
|
130
|
-
| `--tokens`, `--variables` | | Output only the design token dictionary (colors, spacing, radii). |
|
|
131
|
-
| `--shallow-instances` | | Collapse internal sub-layers of component instances into clean props. |
|
|
132
|
-
| `--pretty` | | Pretty-print JSON with 2-space indentation (default is compact single-line JSON). |
|
|
133
|
-
| `--include-tokens` | | Include full design tokens dictionary alongside node AST. |
|
|
134
|
-
| `--include-ids` | | Retain internal Figma node IDs on each node in the tree. |
|
|
135
|
-
| `--raw` | | Return full, unpruned raw Figma API AST. |
|
|
136
|
-
| `--depth <number>` | | Limit node tree traversal depth (e.g. `--depth 2` for top-level overview). |
|
|
137
|
-
| `--scale <1\|2\|3\|4>` | | Render resolution multiplier for preview images (default: `2`). |
|
|
138
|
-
| `--format <png\|svg\|jpg>` | | Rendered image file format (default: `png`). |
|
|
139
|
-
| `--no-variables` | | Skip querying `/variables/local`. |
|
|
140
|
-
| `--help` | `-h` | Show help message. |
|
|
141
|
-
|
|
142
|
-
---
|
|
59
|
+
- **Standard skills (`SKILL.md`)**: the parent directory of this `SKILL.md` file.
|
|
60
|
+
- **Cursor rule (`.cursor/rules/figma-extractor.mdc`)**: the sibling `.cursor/skills/figma-extractor` directory.
|
|
143
61
|
|
|
144
|
-
##
|
|
145
|
-
|
|
146
|
-
### 1. Resolved Design Tokens
|
|
147
|
-
When a layer uses Figma variables (design tokens), the extractor attaches semantic token names alongside computed fallback values:
|
|
148
|
-
|
|
149
|
-
```json
|
|
150
|
-
{
|
|
151
|
-
"name": "Card Container",
|
|
152
|
-
"type": "FRAME",
|
|
153
|
-
"layoutMode": "VERTICAL",
|
|
154
|
-
"padding": { "top": 16, "right": 16, "bottom": 16, "left": 16 },
|
|
155
|
-
"paddingToken": "spacing/md",
|
|
156
|
-
"itemSpacing": 12,
|
|
157
|
-
"itemSpacingToken": "spacing/sm",
|
|
158
|
-
"cornerRadius": 8,
|
|
159
|
-
"cornerRadiusToken": "radii/lg",
|
|
160
|
-
"fills": [
|
|
161
|
-
{
|
|
162
|
-
"type": "SOLID",
|
|
163
|
-
"color": "#1877F2",
|
|
164
|
-
"token": "colors/brand/primary"
|
|
165
|
-
}
|
|
166
|
-
]
|
|
167
|
-
}
|
|
168
|
-
```
|
|
62
|
+
## Quota rules (Zero-Hammer Protocol)
|
|
169
63
|
|
|
170
|
-
|
|
171
|
-
When an `INSTANCE` node is present, internal property hashes are converted into clean JavaScript props and linked to the master component name:
|
|
172
|
-
|
|
173
|
-
```json
|
|
174
|
-
{
|
|
175
|
-
"name": "Submit Button",
|
|
176
|
-
"type": "INSTANCE",
|
|
177
|
-
"component": {
|
|
178
|
-
"name": "Button",
|
|
179
|
-
"variant": "Variant=Primary, Size=Large"
|
|
180
|
-
},
|
|
181
|
-
"props": {
|
|
182
|
-
"variant": "Primary",
|
|
183
|
-
"size": "Large",
|
|
184
|
-
"hasIcon": true,
|
|
185
|
-
"label": "Confirm Order"
|
|
186
|
-
},
|
|
187
|
-
"bounds": { "width": 140, "height": 44 }
|
|
188
|
-
}
|
|
189
|
-
```
|
|
64
|
+
The script enforces a local rate gate and a persistent circuit breaker, but the agent must also follow these rules:
|
|
190
65
|
|
|
191
|
-
|
|
66
|
+
1. Run `fetch_figma.js` **once** per target frame or component. **Never retry in a loop.**
|
|
67
|
+
2. **Do not pass `-i`** unless the user explicitly requests a preview image. The JSON spec already contains layout, dimensions, colors, radii, typography, and text.
|
|
68
|
+
3. Figma Starter plans and View seats have very small monthly Tier 1 quotas, so every call matters. Prefer reusing existing specs.
|
|
192
69
|
|
|
193
|
-
##
|
|
70
|
+
## Errors
|
|
194
71
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
- Figma REST API has per-minute and monthly rate limits on Starter plans. If quota is exhausted or circuit breaker trips, inform the user they can export the specification directly via the companion **Figma Node JSON Extractor** desktop plugin (available on [Figma Community](https://www.figma.com/community/plugin/1691244420692136789/figma-node-json-extractor)) into `./.specs/<name>.json`.
|
|
72
|
+
| Error code | Action |
|
|
73
|
+
| :--- | :--- |
|
|
74
|
+
| `FIGMA_ACCESS_TOKEN is not set` / `FIGMA_TOKEN_EXPIRED_OR_INVALID` | Tell the user the token is missing, invalid, or expired. They create one in **Figma Settings → Personal Access Tokens** and set `FIGMA_ACCESS_TOKEN` (setup per OS in REFERENCE.md). |
|
|
75
|
+
| Node not found | Report that the `node-id` does not exist in the file. |
|
|
76
|
+
| `FIGMA_SSL_CERTIFICATE_ERROR` | Corporate proxy or self-signed certificate: suggest `FIGMA_IGNORE_SSL=true` in `.env`. |
|
|
77
|
+
| `FIGMA_RATE_LIMIT_EXCEEDED` / `FIGMA_CIRCUIT_BREAKER_ACTIVE` | Report the cooldown. Suggest duplicating the file in Figma Drafts (Right-click → **Duplicate**) for a fresh URL, or exporting the selection with the companion [Figma Node JSON Extractor](https://www.figma.com/community/plugin/1691244420692136789/figma-node-json-extractor) plugin into `.specs/<name>.json`. |
|