@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 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
- const match = content.match(/description:\s*['"]?([^'"\n\r]+)['"]?/);
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 = rawSkillMd.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n*/, '');
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
- globs: *
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.
@@ -1,201 +1,77 @@
1
1
  ---
2
2
  name: figma-extractor
3
- description: 'Extract compact JSON node hierarchies, design tokens, component instance props, and automated preview images from Figma using the Figma REST API. Trigger this skill during the planning or specification phase whenever a Figma link is provided or when the user asks to inspect, extract, or sync design specifications and tokens from Figma into .specs/. This skill strictly produces specification artifacts and does not modify application code.'
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 Skill (SDD Spec Ingest)
6
+ # Figma Extractor (SDD Spec Ingest)
7
7
 
8
- Extract design node metadata, component hierarchies, design tokens/variables, and rendered preview images directly from Figma via the Figma REST API into structured specification artifacts (`.specs/`).
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
- This skill serves as the **Specification Ingestion Layer** in a **Spec-Driven Development (SDD)** workflow. It is **Design-System Aware**, high-performance, token-efficient, and has zero external dependencies.
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
- ## Built-in Rate-Limit Gate & Circuit Breaker
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
- To protect your Figma file quota and prevent lockout penalties, `fetch_figma.js` enforces an automatic safety gate:
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
- ### Agent Behavioral Rules (Zero-Hammer Protocol)
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
- ## Prerequisites
48
-
49
- A Figma Access Token (Personal Access Token or OAuth Token) is required.
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
- **Resolving `<skill-dir>` (Agent-Agnostic)**:
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
- ## Usage & Command Formats
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
- ### 1. Atomic Spec Extraction (Recommended SDD Workflow)
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
- Extract node metadata directly into `./.specs/`:
44
+ ## Common variants
104
45
 
105
- ```bash
106
- # Atomic AST extraction (1 Tier 1 call - recommended)
107
- node "<skill-dir>/scripts/fetch_figma.js" "https://www.figma.com/design/:fileKey/:name?node-id=4023-474" --spec card
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
- # Optional: With rendered preview image (2 Tier 1 calls)
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
- # Shallow instances mode (collapses internal component layers into clean props)
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
- ### 2. Design Tokens Dictionary Mode
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
- ```bash
119
- # Export only the file's design token dictionary (colors, spacing, radii) into ./.specs/tokens.json
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
- ## Output Spec Structure
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
- ### 2. Normalized Component Instances & Props
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
- ## Error Handling & Troubleshooting
70
+ ## Errors
194
71
 
195
- - **Token Expired / Missing (`FIGMA_TOKEN_EXPIRED_OR_INVALID` / `isTokenExpiredOrInvalid`)**:
196
- - Alert the user that their Figma access token is missing, invalid, or expired.
197
- - Instruct the user to create a token in **Figma Settings → Personal Access Tokens** and configure `FIGMA_ACCESS_TOKEN` in their environment or workspace `.env` file.
198
- - **Node Not Found**:
199
- - Alert the user that the node ID could not be found in the specified file.
200
- - **Rate Limited (`FIGMA_RATE_LIMIT_EXCEEDED` / `FIGMA_CIRCUIT_BREAKER_ACTIVE`)**:
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`. |