figma-local 1.2.0 → 1.4.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/README.md CHANGED
@@ -99,32 +99,66 @@ npm install && npm install -g .
99
99
 
100
100
  ## Setup (one time only)
101
101
 
102
- ### 1. Import the Figma plugin
102
+ ### Step 1 Install the CLI
103
103
 
104
- 1. Open **Figma Desktop**
105
- 2. Hamburger menu → **Plugins → Development → Import plugin from manifest...**
106
- 3. Navigate to the `plugin/` folder in this repo (or `$(npm root -g)/figma-local/plugin/`)
107
- 4. Select `manifest.json` → click **Open**
108
- 5. Right-click **Figma Local** in the plugin list → **Add to toolbar**
104
+ Pick one of the install methods above (npm recommended):
109
105
 
110
- ### 2. Connect
106
+ ```bash
107
+ npm install -g figma-local
108
+ ```
109
+
110
+ Verify it worked:
111
111
 
112
112
  ```bash
113
- fig-start --safe
113
+ fig --help
114
+ ```
115
+
116
+ ### Step 2 — Import the Figma plugin into Figma Desktop
117
+
118
+ The plugin lets figma-local talk to Figma. You only need to do this once per Figma account.
119
+
120
+ 1. Open **Figma Desktop** (not the browser — the desktop app)
121
+ 2. Open any design file
122
+ 3. Click the **hamburger menu** (☰) in the top-left corner
123
+ 4. Go to **Plugins → Development → Import plugin from manifest...**
124
+ 5. In the file picker, navigate to the plugin folder:
125
+ - If you installed via **npm**: run `echo "$(npm root -g)/figma-local/plugin"` in your terminal to get the path, then navigate there
126
+ - If you cloned the **repo**: go to `figma-local/plugin/` in the cloned folder
127
+ 6. Select `manifest.json` → click **Open**
128
+ 7. You should see **"Figma Local"** appear in your plugin list
129
+ 8. *(Optional but recommended)* Right-click **Figma Local** in the plugin list → **Add to toolbar** for one-click access
130
+
131
+ ### Step 3 — Connect and verify
132
+
133
+ 1. In Figma, start the plugin: **Plugins → Development → Figma Local** (or click it in your toolbar)
134
+ 2. You should see a small widget that says **"Figma Local"** with a connecting status
135
+ 3. In your terminal, run:
136
+
137
+ ```bash
138
+ fig connect --safe
114
139
  ```
115
140
 
116
- This starts the daemon, waits for you to click Figma Local in Figma, then launches Claude Code.
141
+ 4. The plugin widget should show a **green dot** and say **"Connected"**
142
+ 5. Try reading your canvas:
143
+
144
+ ```bash
145
+ fig read
146
+ ```
147
+
148
+ If you see a list of frames, you're all set!
117
149
 
118
150
  ---
119
151
 
120
152
  ## Every session after that
121
153
 
122
- ```
123
- 1. Open Figma click Figma Local in the toolbar
124
- 2. In terminal: fig-start --safe
154
+ ```bash
155
+ # 1. Open Figma Desktop and your design file
156
+ # 2. Click "Figma Local" in the toolbar (or Plugins → Development → Figma Local)
157
+ # 3. In your terminal:
158
+ fig-start --safe
125
159
  ```
126
160
 
127
- Claude Code reads `CLAUDE.md` and knows every command automatically.
161
+ This connects to Figma and launches Claude Code. Claude reads `CLAUDE.md` and knows every `fig` command automatically.
128
162
 
129
163
  ---
130
164
 
@@ -255,13 +289,34 @@ fig prompt "Login" \
255
289
 
256
290
  Generates ~45 tokens of structured text instead of attaching a Figma frame (300–500+ hidden tokens). **91–97% smaller input, more consistent AI output.**
257
291
 
292
+ ### Screenshots
293
+
294
+ ```bash
295
+ fig screenshot # Screenshot current selection
296
+ fig screenshot --node "123:456" # Screenshot a specific node
297
+ fig screenshot --link "https://..." # Screenshot from a Figma link
298
+ fig screenshot -o design.png -s 2 # Save to file at 2x scale
299
+ fig screenshot -f svg -o icon.svg # Export as SVG
300
+ ```
301
+
258
302
  ### Verify & compare
259
303
 
260
304
  ```bash
261
- fig verify # Screenshot of selection for AI review
262
- fig verify --compare "https://..." # Diff prototype vs Figma design → correction prompts
305
+ fig verify # Screenshot of selection for AI review
306
+ fig verify --link "https://..." # Verify from a Figma link
307
+ fig verify --node "123:456" # Verify a specific node
308
+ fig verify --compare "https://..." # Diff prototype vs Figma design → correction prompts
309
+
310
+ # Visual comparison between any two sources
311
+ fig compare --a selection --b "123:456" # Compare selection vs a node
312
+ fig compare --a design.png --b "123:456" # Compare a screenshot file vs a Figma node
313
+ fig compare --a design.png --b coded.png # Compare two screenshot files
314
+ fig compare --a-link "https://..." --b-link "https://..." # Compare two Figma links
315
+ fig compare --a "123:456" --b "789:012" # Compare two nodes by ID
263
316
  ```
264
317
 
318
+ Sources for `--a` and `--b` can be: `selection`, a node ID (`123:456`), or a file path (`screenshot.png`). Use `--a-link` / `--b-link` for Figma selection URLs.
319
+
265
320
  ### Export
266
321
 
267
322
  ```bash
@@ -340,7 +395,7 @@ fig-start --safe --here # launch from your project dir; Claude sees both you
340
395
 
341
396
  ## Claude Code Plugin (Skills)
342
397
 
343
- figma-local ships as a Claude Code plugin with 6 skills that teach coding agents how to use it automatically:
398
+ figma-local ships as a Claude Code plugin with 8 skills that teach coding agents how to use it automatically:
344
399
 
345
400
  | Skill | Triggers on |
346
401
  |-------|------------|
@@ -350,6 +405,8 @@ figma-local ships as a Claude Code plugin with 6 skills that teach coding agents
350
405
  | **figma-styles** | "style guide", "extract colors/fonts", "spacing scale" |
351
406
  | **figma-measure** | "measure spacing", "gap between elements" |
352
407
  | **figma-document** | "document this component", "full spec sheet", "deep breakdown" |
408
+ | **figma-screenshot** | "take a screenshot", "export as PNG/SVG", "capture this" |
409
+ | **figma-compare** | "compare", "does this match", "visual diff", "check against design" |
353
410
 
354
411
  ### Install the skills
355
412
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "figma-local",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "Control Figma Desktop with Claude Code. Smart read, write, and AI-prompt export. No API key required.",
5
5
  "author": "elvke",
6
6
  "license": "MIT",
@@ -0,0 +1,124 @@
1
+ ---
2
+ name: figma-compare
3
+ description: |
4
+ Use this skill when the user wants to visually compare two things — two Figma elements, a screenshot vs a Figma component, two screenshots, or a design vs coded output. Triggers on: "compare", "diff", "does this match", "check against", "visual comparison", "how close is this", "spot the differences", "compare design to code", "compare these two", "are they the same". Also use when verifying coded UI matches a Figma design. Requires the fig CLI to be connected.
5
+ allowed-tools:
6
+ - Bash(fig compare *)
7
+ - Bash(fig compare)
8
+ - Bash(fig verify *)
9
+ - Bash(fig verify)
10
+ - Bash(fig screenshot *)
11
+ - Bash(fig screenshot)
12
+ - Read
13
+ ---
14
+
15
+ # Figma Compare
16
+
17
+ Visually compare any two sources: Figma selections, node IDs, Figma links, or screenshot files. Outputs both images and a structured gap report template for analysis.
18
+
19
+ ## Prerequisites
20
+
21
+ The `fig` CLI must be connected. Check with `fig daemon status`. If not connected: `fig connect --safe`.
22
+
23
+ ## Usage
24
+
25
+ ### Compare current selection vs a node
26
+
27
+ ```bash
28
+ fig compare --a selection --b "123:456"
29
+ ```
30
+
31
+ ### Compare two screenshot files
32
+
33
+ ```bash
34
+ fig compare --a design.png --b coded-output.png
35
+ ```
36
+
37
+ ### Compare a screenshot file vs a Figma node
38
+
39
+ ```bash
40
+ fig compare --a screenshot.png --b "123:456"
41
+ ```
42
+
43
+ ### Compare two Figma links
44
+
45
+ ```bash
46
+ fig compare --a-link "https://www.figma.com/...?node-id=1-2" --b-link "https://www.figma.com/...?node-id=3-4"
47
+ ```
48
+
49
+ ### Compare selection vs a Figma link
50
+
51
+ ```bash
52
+ fig compare --a selection --b-link "https://www.figma.com/...?node-id=123-456"
53
+ ```
54
+
55
+ ### Compare two nodes by ID
56
+
57
+ ```bash
58
+ fig compare --a "123:456" --b "789:012"
59
+ ```
60
+
61
+ ## Source types
62
+
63
+ The `--a` and `--b` options accept three source types:
64
+
65
+ | Source | Example | Description |
66
+ |--------|---------|-------------|
67
+ | `selection` | `--a selection` | Whatever is currently selected in Figma |
68
+ | Node ID | `--a "123:456"` | A specific Figma node by its ID |
69
+ | File path | `--a design.png` | An existing screenshot/image file on disk |
70
+
71
+ For Figma selection URLs, use `--a-link` / `--b-link` instead.
72
+
73
+ ## Options
74
+
75
+ ```bash
76
+ fig compare --a selection --b "123:456" -s 2 # 2x scale for exports
77
+ fig compare --a selection --b "123:456" --max 3000 # Max dimension 3000px
78
+ fig compare --a selection --b "123:456" --save-dir . # Save images to current dir
79
+ ```
80
+
81
+ ## Output
82
+
83
+ The command exports both sources as PNG images (saved to `/tmp/` by default) and outputs:
84
+
85
+ 1. **File paths** for both images — use `Read` tool to view them
86
+ 2. **Structured JSON** with a gap report template:
87
+ - `matches` — elements that are the same
88
+ - `differences` — table of element, property, value in A, value in B, severity
89
+ - `summary` — overall assessment
90
+
91
+ ## Workflow: Design vs Code Comparison
92
+
93
+ 1. Take a screenshot of the coded output (browser screenshot or `fig screenshot`)
94
+ 2. Get the Figma node ID or link for the original design
95
+ 3. Run compare:
96
+ ```bash
97
+ fig compare --a coded-output.png --b-link "https://www.figma.com/...?node-id=123-456"
98
+ ```
99
+ 4. Read both output images to visually analyze differences
100
+ 5. Use `fig inspect` on the Figma node to get exact specs for fixing differences
101
+
102
+ ## Workflow: Before/After Comparison
103
+
104
+ 1. Screenshot the current state: `fig screenshot --node "123:456" -o before.png`
105
+ 2. Make changes in Figma
106
+ 3. Compare: `fig compare --a before.png --b "123:456"`
107
+
108
+ ## Verify command (simpler alternative)
109
+
110
+ For quick verification of a single element without a full comparison:
111
+
112
+ ```bash
113
+ fig verify # Screenshot selection for AI review
114
+ fig verify --node "123:456" # Verify specific node
115
+ fig verify --link "https://..." # Verify from Figma link
116
+ fig verify --compare "https://..." # Compare against a prototype URL
117
+ ```
118
+
119
+ ## Tips
120
+
121
+ - Use `--save-dir .` to save comparison images in your project directory instead of `/tmp/`
122
+ - For AI-powered analysis, read both output images with the `Read` tool after running compare
123
+ - Combine with `fig inspect --deep` on the Figma source to get exact specs for fixing any differences
124
+ - When comparing design to code, screenshot the browser at the same viewport width as the Figma frame for best results
@@ -0,0 +1,73 @@
1
+ ---
2
+ name: figma-screenshot
3
+ description: |
4
+ Use this skill when the user wants to take a screenshot or export an image from Figma — current selection, a specific node, or from a Figma link. Triggers on: "screenshot", "take a screenshot", "export as PNG", "export as SVG", "capture this", "save as image", "export this frame", "get an image of". Requires the fig CLI to be connected.
5
+ allowed-tools:
6
+ - Bash(fig screenshot *)
7
+ - Bash(fig screenshot)
8
+ ---
9
+
10
+ # Figma Screenshot
11
+
12
+ Export any Figma element as an image (PNG, JPG, SVG, or PDF).
13
+
14
+ ## Prerequisites
15
+
16
+ The `fig` CLI must be connected. Check with `fig daemon status`. If not connected: `fig connect --safe`.
17
+
18
+ ## Usage
19
+
20
+ ### Screenshot current selection
21
+
22
+ The user must select an element in Figma first, then:
23
+
24
+ ```bash
25
+ fig screenshot
26
+ ```
27
+
28
+ Saves to `screenshot.png` in the current directory by default.
29
+
30
+ ### Screenshot from a Figma link
31
+
32
+ ```bash
33
+ fig screenshot --link "https://www.figma.com/design/FILEID/Name?node-id=123-456"
34
+ ```
35
+
36
+ ### Screenshot a specific node by ID
37
+
38
+ ```bash
39
+ fig screenshot --node "123:456"
40
+ ```
41
+
42
+ ### Options
43
+
44
+ ```bash
45
+ fig screenshot -o design.png # Custom output file name
46
+ fig screenshot -s 2 # 2x scale (default is 2)
47
+ fig screenshot -s 4 # 4x scale for high-res
48
+ fig screenshot -f svg # SVG format
49
+ fig screenshot -f jpg # JPG format
50
+ fig screenshot -f pdf # PDF format
51
+ ```
52
+
53
+ ### Combine options
54
+
55
+ ```bash
56
+ fig screenshot --link "https://..." -o hero-section.png -s 3
57
+ fig screenshot --node "123:456" -f svg -o icon.svg
58
+ ```
59
+
60
+ ## Export a specific node by ID (alternative)
61
+
62
+ ```bash
63
+ fig export node "123:456" # PNG at 2x
64
+ fig export node "123:456" -f svg -o out.svg # SVG
65
+ ```
66
+
67
+ ## Tips
68
+
69
+ - Default scale is 2x which gives crisp images on retina displays
70
+ - Use `-s 1` for pixel-accurate exports (1:1 with Figma dimensions)
71
+ - SVG format is best for icons and vector graphics
72
+ - For AI verification of created components, use `fig verify` instead (optimized for smaller file size)
73
+ - Screenshots can be used with `fig compare` to visually diff against other screenshots or Figma nodes
package/src/index.js CHANGED
@@ -5205,18 +5205,30 @@ const exp = program
5205
5205
 
5206
5206
  exp
5207
5207
  .command('screenshot')
5208
- .description('Take a screenshot of selected node or current page')
5208
+ .description('Take a screenshot of selected node, specific node, or from a Figma link')
5209
5209
  .option('-o, --output <file>', 'Output file', 'screenshot.png')
5210
5210
  .option('-s, --scale <number>', 'Export scale (1-4)', '2')
5211
5211
  .option('-f, --format <format>', 'Format: png, jpg, svg, pdf', 'png')
5212
+ .option('--node <id>', 'Screenshot a specific node by ID')
5213
+ .option('--link <url>', 'Screenshot a node from a Figma selection link')
5212
5214
  .action((options) => {
5213
5215
  checkConnection();
5214
5216
  const format = options.format.toUpperCase();
5215
5217
  const scale = parseFloat(options.scale);
5218
+ let nodeResolver;
5219
+ if (options.link) {
5220
+ const nodeId = parseNodeIdFromLink(options.link);
5221
+ if (!nodeId) { console.error(chalk.red('✗'), 'Could not parse node ID from link'); process.exit(1); }
5222
+ nodeResolver = `node = await figma.getNodeByIdAsync('${nodeId}');`;
5223
+ } else if (options.node) {
5224
+ nodeResolver = `node = await figma.getNodeByIdAsync('${options.node}');`;
5225
+ } else {
5226
+ nodeResolver = `const sel = figma.currentPage.selection; node = sel.length > 0 ? sel[0] : figma.currentPage;`;
5227
+ }
5216
5228
  const code = `(async () => {
5217
- const sel = figma.currentPage.selection;
5218
- const node = sel.length > 0 ? sel[0] : figma.currentPage;
5219
- if (!node) return { error: 'No page or selection' };
5229
+ let node;
5230
+ ${nodeResolver}
5231
+ if (!node) return { error: 'No node found' };
5220
5232
  if (!('exportAsync' in node)) return { error: 'Node cannot be exported' };
5221
5233
  const bytes = await node.exportAsync({ format: '${format}', constraint: { type: 'SCALE', value: ${scale} } });
5222
5234
  return {
@@ -5333,14 +5345,25 @@ program
5333
5345
  .option('--save [path]', 'Save as PNG file (default: /tmp/figma-verify-{id}.png)')
5334
5346
  .option('--compare <url>', 'Compare against a prototype/preview URL and generate correction prompts')
5335
5347
  .option('--compare-save <path>', 'Save prototype screenshot to this path when using --compare')
5348
+ .option('--link <url>', 'Verify a node from a Figma selection link')
5349
+ .option('--node <id>', 'Verify a specific node by ID')
5336
5350
  .action(async (nodeId, options) => {
5337
5351
  checkConnection();
5338
5352
  const scale = parseFloat(options.scale);
5339
5353
  const maxDim = parseInt(options.max);
5340
5354
 
5355
+ // Resolve node: --link > --node > positional nodeId > selection
5356
+ let resolvedNodeId = nodeId;
5357
+ if (options.link) {
5358
+ resolvedNodeId = parseNodeIdFromLink(options.link);
5359
+ if (!resolvedNodeId) { console.error(chalk.red('✗'), 'Could not parse node ID from link'); process.exit(1); }
5360
+ } else if (options.node) {
5361
+ resolvedNodeId = options.node;
5362
+ }
5363
+
5341
5364
  const code = `(async () => {
5342
5365
  let node;
5343
- ${nodeId ? `node = await figma.getNodeByIdAsync('${nodeId}');` : `
5366
+ ${resolvedNodeId ? `node = await figma.getNodeByIdAsync('${resolvedNodeId}');` : `
5344
5367
  const sel = figma.currentPage.selection;
5345
5368
  node = sel.length > 0 ? sel[0] : null;
5346
5369
  `}
@@ -5441,6 +5464,137 @@ program
5441
5464
  }
5442
5465
  });
5443
5466
 
5467
+ // ============ COMPARE (Visual Comparison) ============
5468
+
5469
+ function exportNodeScreenshot(nodeResolver, scale, maxDim) {
5470
+ const code = `(async () => {
5471
+ let node;
5472
+ ${nodeResolver}
5473
+ if (!node) return { error: 'No node found' };
5474
+ if (!('exportAsync' in node)) return { error: 'Node cannot be exported' };
5475
+ const nodeWidth = node.width || 100;
5476
+ const nodeHeight = node.height || 100;
5477
+ let finalScale = ${scale};
5478
+ const maxNodeDim = Math.max(nodeWidth, nodeHeight);
5479
+ if (maxNodeDim * finalScale > ${maxDim}) finalScale = ${maxDim} / maxNodeDim;
5480
+ if (maxNodeDim * finalScale > 7500) finalScale = 7500 / maxNodeDim;
5481
+ const bytes = await node.exportAsync({ format: 'PNG', constraint: { type: 'SCALE', value: finalScale } });
5482
+ return {
5483
+ name: node.name,
5484
+ id: node.id,
5485
+ type: node.type,
5486
+ width: Math.round(nodeWidth * finalScale),
5487
+ height: Math.round(nodeHeight * finalScale),
5488
+ originalWidth: Math.round(nodeWidth),
5489
+ originalHeight: Math.round(nodeHeight),
5490
+ bytes: Array.from(bytes)
5491
+ };
5492
+ })()`;
5493
+ return figmaEvalSync(code);
5494
+ }
5495
+
5496
+ program
5497
+ .command('compare')
5498
+ .description('Compare two things visually: screenshots, Figma nodes, or a mix. Outputs both images for AI analysis.')
5499
+ .option('--a <source>', 'First source: file path, node ID, or "selection"', 'selection')
5500
+ .option('--b <source>', 'Second source: file path, node ID, or Figma link')
5501
+ .option('--a-link <url>', 'First source from a Figma selection link')
5502
+ .option('--b-link <url>', 'Second source from a Figma selection link')
5503
+ .option('-s, --scale <number>', 'Export scale for Figma nodes', '1')
5504
+ .option('--max <pixels>', 'Max dimension for exports', '2000')
5505
+ .option('--save-dir <dir>', 'Directory to save comparison images', '/tmp')
5506
+ .addHelpText('after', `
5507
+ Examples:
5508
+ fig compare --a selection --b "123:456" Compare selection vs a node
5509
+ fig compare --a design.png --b "123:456" Compare a screenshot file vs a Figma node
5510
+ fig compare --a design.png --b coded.png Compare two screenshot files
5511
+ fig compare --a-link "https://..." --b-link "https://..." Compare two Figma links
5512
+ fig compare --a selection --b-link "https://..." Compare selection vs a Figma link
5513
+ fig compare --a "123:456" --b "789:012" Compare two nodes by ID
5514
+ `)
5515
+ .action((options) => {
5516
+ checkConnection();
5517
+ const scale = parseFloat(options.scale);
5518
+ const maxDim = parseInt(options.max);
5519
+ const saveDir = options.saveDir;
5520
+ const timestamp = Date.now();
5521
+
5522
+ function resolveSource(source, linkOpt, label) {
5523
+ // If it's a --link option
5524
+ if (linkOpt) {
5525
+ const nodeId = parseNodeIdFromLink(linkOpt);
5526
+ if (!nodeId) { console.error(chalk.red('✗'), `Could not parse node ID from ${label} link`); process.exit(1); }
5527
+ const result = exportNodeScreenshot(`node = await figma.getNodeByIdAsync('${nodeId}');`, scale, maxDim);
5528
+ if (result.error) { console.error(chalk.red('✗'), `${label}: ${result.error}`); process.exit(1); }
5529
+ const filePath = `${saveDir}/figma-compare-${label}-${timestamp}.png`;
5530
+ writeFileSync(filePath, Buffer.from(result.bytes));
5531
+ return { type: 'figma-node', name: result.name, id: result.id, nodeType: result.type, width: result.width, height: result.height, originalWidth: result.originalWidth, originalHeight: result.originalHeight, path: filePath };
5532
+ }
5533
+
5534
+ // File path (existing screenshot)
5535
+ if (source && existsSync(source)) {
5536
+ return { type: 'file', name: source.split('/').pop(), path: source };
5537
+ }
5538
+
5539
+ // "selection"
5540
+ if (source === 'selection') {
5541
+ const result = exportNodeScreenshot(`const sel = figma.currentPage.selection; node = sel.length > 0 ? sel[0] : null;`, scale, maxDim);
5542
+ if (result.error) { console.error(chalk.red('✗'), `${label}: ${result.error}`); process.exit(1); }
5543
+ const filePath = `${saveDir}/figma-compare-${label}-${timestamp}.png`;
5544
+ writeFileSync(filePath, Buffer.from(result.bytes));
5545
+ return { type: 'figma-node', name: result.name, id: result.id, nodeType: result.type, width: result.width, height: result.height, originalWidth: result.originalWidth, originalHeight: result.originalHeight, path: filePath };
5546
+ }
5547
+
5548
+ // Node ID (contains ":")
5549
+ if (source && source.includes(':')) {
5550
+ const result = exportNodeScreenshot(`node = await figma.getNodeByIdAsync('${source}');`, scale, maxDim);
5551
+ if (result.error) { console.error(chalk.red('✗'), `${label}: ${result.error}`); process.exit(1); }
5552
+ const filePath = `${saveDir}/figma-compare-${label}-${timestamp}.png`;
5553
+ writeFileSync(filePath, Buffer.from(result.bytes));
5554
+ return { type: 'figma-node', name: result.name, id: result.id, nodeType: result.type, width: result.width, height: result.height, originalWidth: result.originalWidth, originalHeight: result.originalHeight, path: filePath };
5555
+ }
5556
+
5557
+ console.error(chalk.red('✗'), `${label}: "${source}" is not a valid file path, node ID, or "selection"`);
5558
+ process.exit(1);
5559
+ }
5560
+
5561
+ const sourceA = resolveSource(options.a, options.aLink, 'a');
5562
+ const sourceB = resolveSource(options.b, options.bLink, 'b');
5563
+
5564
+ console.log(chalk.bold('\n## Visual Comparison\n'));
5565
+ console.log(chalk.cyan('Source A:'), sourceA.name, sourceA.type === 'figma-node' ? `(${sourceA.nodeType} ${sourceA.id}, ${sourceA.originalWidth}x${sourceA.originalHeight})` : '(file)');
5566
+ console.log(chalk.cyan('Source B:'), sourceB.name, sourceB.type === 'figma-node' ? `(${sourceB.nodeType} ${sourceB.id}, ${sourceB.originalWidth}x${sourceB.originalHeight})` : '(file)');
5567
+ console.log('');
5568
+ console.log(chalk.green('Image A:'), sourceA.path);
5569
+ console.log(chalk.green('Image B:'), sourceB.path);
5570
+ console.log('');
5571
+
5572
+ // Output structured data for AI agents
5573
+ console.log(JSON.stringify({
5574
+ mode: 'visual-comparison',
5575
+ sourceA: { ...sourceA, bytes: undefined },
5576
+ sourceB: { ...sourceB, bytes: undefined },
5577
+ instructions: [
5578
+ `1. Open and examine Image A: ${sourceA.path}`,
5579
+ `2. Open and examine Image B: ${sourceB.path}`,
5580
+ '3. Compare them visually for differences in:',
5581
+ ' - Layout and spacing (padding, margins, gaps)',
5582
+ ' - Colors (backgrounds, text, borders)',
5583
+ ' - Typography (font size, weight, line-height)',
5584
+ ' - Border radius and shadows',
5585
+ ' - Missing or extra elements',
5586
+ ' - Alignment and positioning',
5587
+ '4. Output a structured gap report with specific differences',
5588
+ '5. For each difference, provide the exact values from both sources',
5589
+ ],
5590
+ gapReportTemplate: {
5591
+ matches: '(list elements that match between A and B)',
5592
+ differences: '(table: element | property | value_in_A | value_in_B | severity)',
5593
+ summary: '(brief overall assessment: how closely do they match)',
5594
+ }
5595
+ }, null, 2));
5596
+ });
5597
+
5444
5598
  // ============ EVAL ============
5445
5599
 
5446
5600
  program