@pedroab0/skills 1.0.1 → 1.1.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 +19 -3
- package/bin/cli.js +152 -32
- package/figma-extractor/README.md +14 -6
- package/figma-extractor/SKILL.md +18 -16
- package/figma-extractor/figma-plugin/README.md +10 -5
- package/figma-extractor/scripts/fetch_figma.js +180 -26
- package/figma-extractor/scripts/test_fetch_figma.js +90 -4
- package/figma-node-builder/README.md +1 -1
- package/figma-node-builder/SKILL.md +7 -8
- package/figma-node-builder/scripts/inspect_spec.js +15 -2
- package/figma-node-builder/scripts/test_inspect_spec.js +16 -1
- package/figma-to-code/README.md +10 -4
- package/figma-to-code/SKILL.md +18 -16
- package/figma-to-code/scripts/fetch_figma.js +163 -18
- package/figma-to-code/scripts/test_fetch_figma.js +79 -0
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT" /></a>
|
|
8
8
|
</p>
|
|
9
9
|
|
|
10
|
-
A curated collection of modular, production-ready skills developed for AI coding agents (such as Claude Code, OpenCode, Codex, Google Antigravity, and
|
|
10
|
+
A curated collection of modular, production-ready skills developed for AI coding agents (such as Claude Code, OpenCode, Codex, Google Antigravity, Cursor, and Kiro).
|
|
11
11
|
|
|
12
12
|
Each skill in this repository is designed to give AI assistants specialized, autonomous capabilities while maintaining:
|
|
13
13
|
- **Token Efficiency**: Pruning unnecessary data and noise to keep agent context windows lean and fast.
|
|
@@ -57,7 +57,7 @@ flowchart TD
|
|
|
57
57
|
|
|
58
58
|
To extract specifications directly inside Figma Desktop without API tokens or rate limits, use the companion [**Figma Node JSON Extractor**](./figma-extractor/figma-plugin/README.md) plugin:
|
|
59
59
|
* **Figma Community (Recommended)**: Install [**Figma Node JSON Extractor** on Figma Community](https://www.figma.com/community/plugin/1691244420692136789/figma-node-json-extractor) directly, or search for it under Figma Plugins.
|
|
60
|
-
* **Manual Manifest Import (Local Backup)**: In Figma Desktop, go to **Plugins** $\rightarrow$ **Development** $\rightarrow$ **Import plugin from manifest...** and select `manifest.json` from your installed
|
|
60
|
+
* **Manual Manifest Import (Local Backup)**: In Figma Desktop, go to **Plugins** $\rightarrow$ **Development** $\rightarrow$ **Import plugin from manifest...** and select `manifest.json` from your installed skill directory (`<skill-dir>/figma-plugin/manifest.json`).
|
|
61
61
|
|
|
62
62
|
---
|
|
63
63
|
|
|
@@ -75,7 +75,7 @@ npx github:pedroab0/skills
|
|
|
75
75
|
|
|
76
76
|
The installer features a clean, interactive terminal UI:
|
|
77
77
|
* **Navigate**: Use keyboard arrows (`↑` / `↓`) or vim keys (`k` / `j`) to move between options.
|
|
78
|
-
* **Select**: Press `[Enter]` or press number keys (`1` - `
|
|
78
|
+
* **Select**: Press `[Enter]` or press number keys (`1` - `6`) to select directly.
|
|
79
79
|
* **Exit**: Press `[ESC]` or `q` at any time to cancel cleanly.
|
|
80
80
|
|
|
81
81
|
### Selection Options & Presets
|
|
@@ -91,6 +91,9 @@ Or specify flags for fast, non-interactive setup:
|
|
|
91
91
|
# Install all skills for Claude Code globally
|
|
92
92
|
npx @pedroab0/skills --agent claude --skill all
|
|
93
93
|
|
|
94
|
+
# Install SDD skills for Kiro (IDE & CLI)
|
|
95
|
+
npx @pedroab0/skills --agent kiro --skill sdd
|
|
96
|
+
|
|
94
97
|
# Install SDD skills for Google Antigravity
|
|
95
98
|
npx @pedroab0/skills --agent antigravity --skill figma-extractor,figma-node-builder
|
|
96
99
|
|
|
@@ -156,6 +159,19 @@ ln -s /path/to/skills/figma-node-builder ~/.gemini/config/skills/figma-node-buil
|
|
|
156
159
|
|
|
157
160
|
---
|
|
158
161
|
|
|
162
|
+
### 5. Kiro
|
|
163
|
+
|
|
164
|
+
Kiro discovers standard `SKILL.md` packages locally in `.kiro/skills/` or globally in `~/.kiro/skills/` (or `$KIRO_HOME/skills/`):
|
|
165
|
+
```bash
|
|
166
|
+
mkdir -p ~/.kiro/skills
|
|
167
|
+
ln -s /path/to/skills/figma-to-code ~/.kiro/skills/figma-to-code
|
|
168
|
+
ln -s /path/to/skills/figma-extractor ~/.kiro/skills/figma-extractor
|
|
169
|
+
ln -s /path/to/skills/figma-node-builder ~/.kiro/skills/figma-node-builder
|
|
170
|
+
```
|
|
171
|
+
- **Activation**: Kiro loads matching skills automatically based on your prompt or explicitly via `/figma-extractor`, `/figma-node-builder`, and `/figma-to-code` in Kiro IDE chat or Kiro CLI.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
159
175
|
## Repository Structure
|
|
160
176
|
|
|
161
177
|
```text
|
package/bin/cli.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Skills Installer CLI
|
|
5
5
|
* https://github.com/pedroab0/skills
|
|
6
6
|
*
|
|
7
|
-
* Install curated AI agent skills for Claude Code, Cursor, Antigravity, and
|
|
7
|
+
* Install curated AI agent skills for Claude Code, Cursor, Antigravity, OpenCode, and Kiro.
|
|
8
8
|
* Usage: npx @pedroab0/skills [options]
|
|
9
9
|
*/
|
|
10
10
|
|
|
@@ -41,7 +41,7 @@ const ui = {
|
|
|
41
41
|
|
|
42
42
|
function printBanner() {
|
|
43
43
|
ui.header(`Skills Installer ${style.dim('(by @pedroab0)')}`);
|
|
44
|
-
ui.item(style.dim('Curated AI Agent Skills for Claude Code, Antigravity, Cursor & OpenCode'));
|
|
44
|
+
ui.item(style.dim('Curated AI Agent Skills for Claude Code, Antigravity, Cursor, Kiro & OpenCode'));
|
|
45
45
|
ui.item(style.dim('https://github.com/pedroab0/skills'));
|
|
46
46
|
ui.bar();
|
|
47
47
|
}
|
|
@@ -53,7 +53,7 @@ ${style.bold('Usage:')}
|
|
|
53
53
|
(or: npx github:pedroab0/skills [options])
|
|
54
54
|
|
|
55
55
|
${style.bold('Options:')}
|
|
56
|
-
--agent <name> Target agent: claude, antigravity, cursor, opencode, all
|
|
56
|
+
--agent <name> Target agent: claude, antigravity, cursor, kiro, opencode, all
|
|
57
57
|
--skill <name> Skill to install (or comma-separated, or "all", "sdd")
|
|
58
58
|
--global Install globally (available across all projects on your machine)
|
|
59
59
|
--local Install in current project workspace only
|
|
@@ -65,6 +65,7 @@ ${style.bold('Options:')}
|
|
|
65
65
|
${style.bold('Examples:')}
|
|
66
66
|
npx @pedroab0/skills
|
|
67
67
|
npx @pedroab0/skills --agent claude --skill all
|
|
68
|
+
npx @pedroab0/skills --agent kiro --skill sdd
|
|
68
69
|
npx @pedroab0/skills --agent antigravity --skill figma-extractor,figma-node-builder
|
|
69
70
|
npx @pedroab0/skills --dest ~/.claude/skills --skill figma-to-code
|
|
70
71
|
`);
|
|
@@ -111,6 +112,11 @@ function getAvailableSkills() {
|
|
|
111
112
|
|
|
112
113
|
function getAgentConfigs(cwd) {
|
|
113
114
|
const home = os.homedir();
|
|
115
|
+
const kiroHome = process.env.KIRO_HOME
|
|
116
|
+
? process.env.KIRO_HOME.replace(/^~(?=$|[\\/])/, home)
|
|
117
|
+
: path.join(home, '.kiro');
|
|
118
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME || path.join(home, '.config');
|
|
119
|
+
|
|
114
120
|
return {
|
|
115
121
|
claude: {
|
|
116
122
|
name: 'Claude Code',
|
|
@@ -132,11 +138,17 @@ function getAgentConfigs(cwd) {
|
|
|
132
138
|
isDetected: fs.existsSync(path.join(cwd, '.cursor')) || fs.existsSync(path.join(home, '.cursor')),
|
|
133
139
|
isCursorRule: true,
|
|
134
140
|
},
|
|
141
|
+
kiro: {
|
|
142
|
+
name: 'Kiro',
|
|
143
|
+
globalPath: path.join(kiroHome, 'skills'),
|
|
144
|
+
localPath: path.join(cwd, '.kiro', 'skills'),
|
|
145
|
+
isDetected: fs.existsSync(kiroHome) || fs.existsSync(path.join(cwd, '.kiro')),
|
|
146
|
+
},
|
|
135
147
|
opencode: {
|
|
136
148
|
name: 'OpenCode',
|
|
137
|
-
globalPath: path.join(
|
|
149
|
+
globalPath: path.join(xdgConfig, 'opencode', 'skills'),
|
|
138
150
|
localPath: path.join(cwd, '.opencode', 'skills'),
|
|
139
|
-
isDetected: fs.existsSync(path.join(
|
|
151
|
+
isDetected: fs.existsSync(path.join(xdgConfig, 'opencode')),
|
|
140
152
|
},
|
|
141
153
|
};
|
|
142
154
|
}
|
|
@@ -370,23 +382,118 @@ async function selectPrompt({ title, items, defaultIndex = 0, onConfirm }) {
|
|
|
370
382
|
// Token Verification
|
|
371
383
|
// -----------------------------------------------------------------------------
|
|
372
384
|
|
|
373
|
-
function
|
|
374
|
-
if (
|
|
375
|
-
|
|
385
|
+
function decodeEnvBuffer(buf) {
|
|
386
|
+
if (!Buffer.isBuffer(buf)) {
|
|
387
|
+
const str = String(buf || '');
|
|
388
|
+
return str.charCodeAt(0) === 0xfeff ? str.slice(1) : str;
|
|
376
389
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
390
|
+
if (buf.length >= 2 && buf[0] === 0xff && buf[1] === 0xfe) {
|
|
391
|
+
return buf.toString('utf16le', 2);
|
|
392
|
+
}
|
|
393
|
+
const str = buf.toString('utf8');
|
|
394
|
+
return str.charCodeAt(0) === 0xfeff ? str.slice(1) : str;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function getPowerShellProfilePaths(home) {
|
|
398
|
+
const paths = [];
|
|
399
|
+
if (process.env.PROFILE && process.env.PROFILE.toLowerCase().endsWith('.ps1')) {
|
|
400
|
+
paths.push(process.env.PROFILE);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const docRoots = [];
|
|
404
|
+
if (home) {
|
|
405
|
+
docRoots.push(
|
|
406
|
+
path.join(home, 'Documents'),
|
|
407
|
+
path.join(home, 'Documentos'),
|
|
408
|
+
path.join(home, 'OneDrive', 'Documents'),
|
|
409
|
+
path.join(home, 'OneDrive', 'Documentos'),
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const oneDriveEnvs = [
|
|
414
|
+
process.env.OneDrive,
|
|
415
|
+
process.env.OneDriveConsumer,
|
|
416
|
+
process.env.OneDriveCommercial,
|
|
417
|
+
].filter(Boolean);
|
|
418
|
+
|
|
419
|
+
for (const od of oneDriveEnvs) {
|
|
420
|
+
docRoots.push(path.join(od, 'Documents'), path.join(od, 'Documentos'));
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const shellDirs = ['PowerShell', 'WindowsPowerShell'];
|
|
424
|
+
const profileFiles = [
|
|
425
|
+
'Microsoft.PowerShell_profile.ps1',
|
|
426
|
+
'Microsoft.VSCode_profile.ps1',
|
|
427
|
+
'profile.ps1',
|
|
384
428
|
];
|
|
429
|
+
|
|
430
|
+
for (const docRoot of docRoots) {
|
|
431
|
+
if (!fs.existsSync(docRoot)) continue;
|
|
432
|
+
for (const shellDir of shellDirs) {
|
|
433
|
+
const fullShellDir = path.join(docRoot, shellDir);
|
|
434
|
+
if (!fs.existsSync(fullShellDir)) continue;
|
|
435
|
+
for (const profileFile of profileFiles) {
|
|
436
|
+
paths.push(path.join(fullShellDir, profileFile));
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
if (home) {
|
|
442
|
+
const xdgPsDir = path.join(process.env.XDG_CONFIG_HOME || path.join(home, '.config'), 'powershell');
|
|
443
|
+
if (fs.existsSync(xdgPsDir)) {
|
|
444
|
+
for (const profileFile of profileFiles) {
|
|
445
|
+
paths.push(path.join(xdgPsDir, profileFile));
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
return Array.from(new Set(paths));
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function hasFigmaTokenInText(text) {
|
|
454
|
+
if (!text) return false;
|
|
455
|
+
const lines = text.split(/\r?\n/);
|
|
456
|
+
for (const rawLine of lines) {
|
|
457
|
+
const trimmed = rawLine.replace(/^\uFEFF/, '').trim();
|
|
458
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
459
|
+
if (
|
|
460
|
+
/^(?:export\s+|set\s+"?|\$env:|\$\{env:)?(?:FIGMA_ACCESS_TOKEN|FIGMA_TOKEN)\}?[\s="']/i.test(trimmed) ||
|
|
461
|
+
/^\[(?:System\.)?Environment\]::SetEnvironmentVariable\(\s*['"](?:FIGMA_ACCESS_TOKEN|FIGMA_TOKEN)['"]/i.test(trimmed)
|
|
462
|
+
) {
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
return false;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function checkFigmaToken(options = {}) {
|
|
470
|
+
const ignoreProcessEnv = Boolean(options.ignoreProcessEnv);
|
|
471
|
+
if (!ignoreProcessEnv && (process.env.FIGMA_ACCESS_TOKEN || process.env.FIGMA_TOKEN)) {
|
|
472
|
+
return { found: true, source: 'environment variable' };
|
|
473
|
+
}
|
|
474
|
+
const cwd = options.cwd || process.cwd();
|
|
475
|
+
const home = options.home !== undefined ? options.home : (process.env.HOME || process.env.USERPROFILE || os.homedir());
|
|
476
|
+
const candidates = Array.from(
|
|
477
|
+
new Set(
|
|
478
|
+
[
|
|
479
|
+
path.join(cwd, '.env'),
|
|
480
|
+
path.join(cwd, '.env.local'),
|
|
481
|
+
home ? path.join(home, '.env') : null,
|
|
482
|
+
home ? path.join(home, '.zshrc') : null,
|
|
483
|
+
home ? path.join(home, '.zprofile') : null,
|
|
484
|
+
home ? path.join(home, '.zshenv') : null,
|
|
485
|
+
home ? path.join(home, '.bashrc') : null,
|
|
486
|
+
home ? path.join(home, '.bash_profile') : null,
|
|
487
|
+
home ? path.join(home, '.profile') : null,
|
|
488
|
+
...getPowerShellProfilePaths(home),
|
|
489
|
+
].filter(Boolean),
|
|
490
|
+
),
|
|
491
|
+
);
|
|
385
492
|
for (const c of candidates) {
|
|
386
493
|
if (fs.existsSync(c)) {
|
|
387
494
|
try {
|
|
388
|
-
const text = fs.readFileSync(c
|
|
389
|
-
if (
|
|
495
|
+
const text = decodeEnvBuffer(fs.readFileSync(c));
|
|
496
|
+
if (hasFigmaTokenInText(text)) {
|
|
390
497
|
return { found: true, source: path.basename(c) };
|
|
391
498
|
}
|
|
392
499
|
} catch (_) {}
|
|
@@ -415,14 +522,15 @@ function installSkill(skill, destDir, useSymlink = false, isCursor = false) {
|
|
|
415
522
|
}
|
|
416
523
|
fs.cpSync(skill.path, cursorSkillsDir, { recursive: true });
|
|
417
524
|
|
|
525
|
+
const rawSkillMd = fs.readFileSync(path.join(skill.path, 'SKILL.md'), 'utf8');
|
|
526
|
+
const skillBody = rawSkillMd.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n*/, '');
|
|
418
527
|
const ruleFile = path.join(destDir, `${skill.name}.mdc`);
|
|
419
528
|
const ruleContent = `---
|
|
420
529
|
description: ${skill.description}
|
|
421
530
|
globs: *
|
|
422
531
|
---
|
|
423
532
|
|
|
424
|
-
${
|
|
425
|
-
`;
|
|
533
|
+
${skillBody}`;
|
|
426
534
|
fs.writeFileSync(ruleFile, ruleContent, 'utf8');
|
|
427
535
|
return { targetPath: ruleFile, isCursorRule: true };
|
|
428
536
|
}
|
|
@@ -433,7 +541,8 @@ ${fs.readFileSync(path.join(skill.path, 'SKILL.md'), 'utf8')}
|
|
|
433
541
|
|
|
434
542
|
if (useSymlink) {
|
|
435
543
|
try {
|
|
436
|
-
|
|
544
|
+
const symlinkType = process.platform === 'win32' ? 'junction' : 'dir';
|
|
545
|
+
fs.symlinkSync(skill.path, targetPath, symlinkType);
|
|
437
546
|
return { targetPath, isSymlink: true };
|
|
438
547
|
} catch (e) {
|
|
439
548
|
fs.cpSync(skill.path, targetPath, { recursive: true });
|
|
@@ -589,30 +698,34 @@ async function main() {
|
|
|
589
698
|
let targetDestinations = [];
|
|
590
699
|
|
|
591
700
|
if (flagDest) {
|
|
701
|
+
const expandedDest = flagDest.replace(/^~(?=$|[\\/])/, os.homedir());
|
|
592
702
|
targetDestinations.push({
|
|
593
703
|
name: 'Custom Directory',
|
|
594
|
-
path: path.resolve(cwd,
|
|
704
|
+
path: path.resolve(cwd, expandedDest),
|
|
595
705
|
isCursor: false,
|
|
596
706
|
});
|
|
597
707
|
} else if (flagAgent) {
|
|
598
|
-
const
|
|
599
|
-
|
|
708
|
+
const normalizedAgent = flagAgent === 'kiro-ide' || flagAgent === 'kiro-cli' ? 'kiro' : flagAgent;
|
|
709
|
+
const resolveScopeIsGlobal = (agentKey) => (flagScope ? flagScope === 'global' : agentKey !== 'cursor');
|
|
710
|
+
if (normalizedAgent === 'all') {
|
|
600
711
|
for (const [key, cfg] of Object.entries(agentConfigs)) {
|
|
712
|
+
const useGlobal = resolveScopeIsGlobal(key);
|
|
601
713
|
targetDestinations.push({
|
|
602
714
|
name: cfg.name,
|
|
603
|
-
path:
|
|
715
|
+
path: useGlobal ? cfg.globalPath : cfg.localPath,
|
|
604
716
|
isCursor: !!cfg.isCursorRule,
|
|
605
717
|
});
|
|
606
718
|
}
|
|
607
|
-
} else if (agentConfigs[
|
|
608
|
-
const cfg = agentConfigs[
|
|
719
|
+
} else if (agentConfigs[normalizedAgent]) {
|
|
720
|
+
const cfg = agentConfigs[normalizedAgent];
|
|
721
|
+
const useGlobal = resolveScopeIsGlobal(normalizedAgent);
|
|
609
722
|
targetDestinations.push({
|
|
610
723
|
name: cfg.name,
|
|
611
|
-
path:
|
|
724
|
+
path: useGlobal ? cfg.globalPath : cfg.localPath,
|
|
612
725
|
isCursor: !!cfg.isCursorRule,
|
|
613
726
|
});
|
|
614
727
|
} else {
|
|
615
|
-
console.error(style.yellow(`⚠ Unknown agent "${flagAgent}". Options: claude, antigravity, cursor, opencode, all`));
|
|
728
|
+
console.error(style.yellow(`⚠ Unknown agent "${flagAgent}". Options: claude, antigravity, cursor, kiro, opencode, all`));
|
|
616
729
|
process.exit(1);
|
|
617
730
|
}
|
|
618
731
|
} else {
|
|
@@ -620,6 +733,7 @@ async function main() {
|
|
|
620
733
|
{ id: 'claude', ...agentConfigs.claude },
|
|
621
734
|
{ id: 'antigravity', ...agentConfigs.antigravity },
|
|
622
735
|
{ id: 'cursor', ...agentConfigs.cursor },
|
|
736
|
+
{ id: 'kiro', ...agentConfigs.kiro },
|
|
623
737
|
{ id: 'opencode', ...agentConfigs.opencode },
|
|
624
738
|
{ id: 'all', name: 'All detected agents' },
|
|
625
739
|
];
|
|
@@ -639,8 +753,8 @@ async function main() {
|
|
|
639
753
|
},
|
|
640
754
|
});
|
|
641
755
|
|
|
642
|
-
// Scope selection (Global vs Local)
|
|
643
|
-
let isGlobal =
|
|
756
|
+
// Scope selection (Global vs Local — Cursor rules live in workspace .cursor/rules by default)
|
|
757
|
+
let isGlobal = selectedAgent.id !== 'cursor';
|
|
644
758
|
if (selectedAgent.id !== 'cursor') {
|
|
645
759
|
const scopeOptions = [
|
|
646
760
|
{
|
|
@@ -668,9 +782,10 @@ async function main() {
|
|
|
668
782
|
if (selectedAgent.id === 'all') {
|
|
669
783
|
for (const [key, cfg] of Object.entries(agentConfigs)) {
|
|
670
784
|
if (cfg.isDetected) {
|
|
785
|
+
const useGlobal = key === 'cursor' ? false : isGlobal;
|
|
671
786
|
targetDestinations.push({
|
|
672
787
|
name: cfg.name,
|
|
673
|
-
path:
|
|
788
|
+
path: useGlobal ? cfg.globalPath : cfg.localPath,
|
|
674
789
|
isCursor: !!cfg.isCursorRule,
|
|
675
790
|
});
|
|
676
791
|
}
|
|
@@ -713,7 +828,12 @@ async function main() {
|
|
|
713
828
|
} else {
|
|
714
829
|
ui.item(`${style.yellow('⚠')} ${style.bold('FIGMA_ACCESS_TOKEN')} not detected.`);
|
|
715
830
|
ui.item(`${style.dim('To scrape designs from Figma, set your token:')}`);
|
|
716
|
-
|
|
831
|
+
if (process.platform === 'win32') {
|
|
832
|
+
ui.item(`${style.cyan('[Environment]::SetEnvironmentVariable("FIGMA_ACCESS_TOKEN", "figd_your_token_here", "User")')}`);
|
|
833
|
+
ui.item(`${style.dim('or in PowerShell ($PROFILE): $env:FIGMA_ACCESS_TOKEN="figd_your_token_here"')}`);
|
|
834
|
+
} else {
|
|
835
|
+
ui.item(`${style.cyan('export FIGMA_ACCESS_TOKEN="figd_your_token_here"')}`);
|
|
836
|
+
}
|
|
717
837
|
ui.item(`${style.dim('or add it to your project\'s .env file.')}`);
|
|
718
838
|
}
|
|
719
839
|
ui.bar();
|
|
@@ -82,10 +82,16 @@ Choose one of the following terminal commands:
|
|
|
82
82
|
```bash
|
|
83
83
|
export FIGMA_ACCESS_TOKEN="figd_your_token_here"
|
|
84
84
|
```
|
|
85
|
-
- **Persist globally
|
|
85
|
+
- **Persist globally on macOS / Linux (`~/.zshrc`)**:
|
|
86
86
|
```bash
|
|
87
87
|
echo 'export FIGMA_ACCESS_TOKEN="figd_your_token_here"' >> ~/.zshrc && source ~/.zshrc
|
|
88
88
|
```
|
|
89
|
+
- **Persist globally on Windows (PowerShell)**:
|
|
90
|
+
```powershell
|
|
91
|
+
[Environment]::SetEnvironmentVariable("FIGMA_ACCESS_TOKEN", "figd_your_token_here", "User")
|
|
92
|
+
# Or inside your PowerShell profile ($PROFILE):
|
|
93
|
+
# $env:FIGMA_ACCESS_TOKEN="figd_your_token_here"
|
|
94
|
+
```
|
|
89
95
|
|
|
90
96
|
*(Optional: If behind a corporate proxy with self-signed SSL certificates, run `echo 'FIGMA_IGNORE_SSL=true' >> .env`).*
|
|
91
97
|
|
|
@@ -110,8 +116,8 @@ node figma-extractor/scripts/fetch_figma.js "<FigmaURL>" --spec card -i
|
|
|
110
116
|
# Atomic extraction with shallow instances (collapses component internals into props)
|
|
111
117
|
node figma-extractor/scripts/fetch_figma.js "<FigmaURL>" --spec card --shallow-instances
|
|
112
118
|
|
|
113
|
-
# Extract only design tokens dictionary
|
|
114
|
-
node figma-extractor/scripts/fetch_figma.js "<FigmaURL>" --tokens
|
|
119
|
+
# Extract only design tokens dictionary into ./.specs/tokens.json
|
|
120
|
+
node figma-extractor/scripts/fetch_figma.js "<FigmaURL>" --tokens --spec tokens
|
|
115
121
|
```
|
|
116
122
|
|
|
117
123
|
---
|
|
@@ -146,10 +152,12 @@ node figma-extractor/scripts/fetch_figma.js "<FigmaURL>" --tokens > ./.specs/tok
|
|
|
146
152
|
For developers who prefer exporting specifications directly from Figma Desktop without configuring API access tokens or consuming REST API quotas:
|
|
147
153
|
|
|
148
154
|
* **Published Figma Plugin (Recommended)**: Install [**Figma Node JSON Extractor** on Figma Community](https://www.figma.com/community/plugin/1691244420692136789/figma-node-json-extractor) directly, or search for it under Figma Plugins.
|
|
149
|
-
* **Manual Local Installation (Local / Offline Backup)**: In Figma Desktop, go to **Plugins** $\rightarrow$ **Development** $\rightarrow$ **Import plugin from manifest...** and select `manifest.json` from your installed skills directory:
|
|
155
|
+
* **Manual Local Installation (Local / Offline Backup)**: In Figma Desktop, go to **Plugins** $\rightarrow$ **Development** $\rightarrow$ **Import plugin from manifest...** and select `manifest.json` from your installed skills directory (`~` corresponds to `%USERPROFILE%` on Windows):
|
|
150
156
|
* **Claude Code**: `~/.claude/skills/figma-extractor/figma-plugin/manifest.json`
|
|
151
|
-
* **
|
|
157
|
+
* **Cursor**: `.cursor/skills/figma-extractor/figma-plugin/manifest.json` (or `~/.cursor/skills/...`)
|
|
152
158
|
* **OpenCode**: `~/.config/opencode/skills/figma-extractor/figma-plugin/manifest.json`
|
|
159
|
+
* **Google Antigravity**: `~/.gemini/config/skills/figma-extractor/figma-plugin/manifest.json`
|
|
160
|
+
* **Kiro**: `~/.kiro/skills/figma-extractor/figma-plugin/manifest.json`
|
|
153
161
|
* **Cloned Git Repo**: `<repo-root>/figma-extractor/figma-plugin/manifest.json`
|
|
154
162
|
* **Export Options**: Select any frame or component, then click:
|
|
155
163
|
* **📋 Copy Node JSON**: Copies the clean AST specification to your clipboard.
|
|
@@ -165,7 +173,7 @@ The skill includes the following resources:
|
|
|
165
173
|
|
|
166
174
|
- **Agent Instruction Guide (`SKILL.md`)**: The core skill definition read by AI agents containing workflow triggers and operational boundaries (spec ingestion only).
|
|
167
175
|
- **Core Extraction Script (`scripts/fetch_figma.js`)**: The standalone, zero-dependency Node.js script that interfaces with the Figma REST API, resolves tokens, prunes the AST, and downloads preview images.
|
|
168
|
-
- **Unit Test Suite (`scripts/test_fetch_figma.js`)**:
|
|
176
|
+
- **Unit Test Suite (`scripts/test_fetch_figma.js`)**: Automated unit tests verifying environment/profile parsing, URL parsing, token formatting, component prop cleaning, rate-limit gates, and node pruning without requiring network access.
|
|
169
177
|
- **Companion Figma Plugin (`figma-plugin/`)**: Zero-API desktop exporter for extracting AST and previews directly inside Figma Desktop.
|
|
170
178
|
- **Reference Documentation (`README.md`)**: This guide explaining architecture, comparison, and operational flags.
|
|
171
179
|
|
package/figma-extractor/SKILL.md
CHANGED
|
@@ -25,7 +25,7 @@ This skill serves as the **Specification Ingestion Layer** in a **Spec-Driven De
|
|
|
25
25
|
> **NEVER use browser tools (Playwright, Puppeteer, Chrome DevTools, `browser_navigate`, `read_browser_page`) on Figma URLs.**
|
|
26
26
|
>
|
|
27
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
|
|
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
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
30
|
|
|
31
31
|
---
|
|
@@ -35,7 +35,7 @@ This skill serves as the **Specification Ingestion Layer** in a **Spec-Driven De
|
|
|
35
35
|
To protect your Figma file quota and prevent lockout penalties, `fetch_figma.js` enforces an automatic safety gate:
|
|
36
36
|
* **Max 5 requests per minute** per file key (sliding window limit).
|
|
37
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
|
|
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.
|
|
39
39
|
|
|
40
40
|
### Agent Behavioral Rules (Zero-Hammer Protocol)
|
|
41
41
|
1. **Single-Shot Execution**: Run `fetch_figma.js` **once** per target frame or component.
|
|
@@ -49,7 +49,9 @@ To protect your Figma file quota and prevent lockout penalties, `fetch_figma.js`
|
|
|
49
49
|
A Figma Access Token (Personal Access Token or OAuth Token) is required.
|
|
50
50
|
|
|
51
51
|
Configure it using either:
|
|
52
|
-
1. **Environment Variable**:
|
|
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`
|
|
53
55
|
2. **Workspace `.env` File**:
|
|
54
56
|
```env
|
|
55
57
|
FIGMA_ACCESS_TOKEN=figd_your_token_here
|
|
@@ -77,20 +79,20 @@ FIGMA_IGNORE_SSL=true
|
|
|
77
79
|
|
|
78
80
|
## Agent Execution Instructions
|
|
79
81
|
|
|
80
|
-
To extract a Figma frame or component as a specification artifact, run `fetch_figma.js` from
|
|
82
|
+
To extract a Figma frame or component as a specification artifact, run `fetch_figma.js` from this skill's `scripts/` directory:
|
|
81
83
|
|
|
82
84
|
```bash
|
|
83
85
|
# Recommended: Atomic extraction (writes ./.specs/<name>.json - 1 Tier 1 call)
|
|
84
|
-
node <skill-dir>/scripts/fetch_figma.js "<FigmaURL>" --spec <name>
|
|
86
|
+
node "<skill-dir>/scripts/fetch_figma.js" "<FigmaURL>" --spec <name>
|
|
85
87
|
|
|
86
88
|
# Optional: With companion preview image (writes .json and .png - 2 Tier 1 calls)
|
|
87
|
-
node <skill-dir>/scripts/fetch_figma.js "<FigmaURL>" --spec <name> -i
|
|
89
|
+
node "<skill-dir>/scripts/fetch_figma.js" "<FigmaURL>" --spec <name> -i
|
|
88
90
|
```
|
|
89
91
|
|
|
90
|
-
**
|
|
91
|
-
- **
|
|
92
|
-
- **
|
|
93
|
-
- **
|
|
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`).
|
|
94
96
|
|
|
95
97
|
---
|
|
96
98
|
|
|
@@ -102,20 +104,20 @@ Extract node metadata directly into `./.specs/`:
|
|
|
102
104
|
|
|
103
105
|
```bash
|
|
104
106
|
# Atomic AST extraction (1 Tier 1 call - recommended)
|
|
105
|
-
node
|
|
107
|
+
node "<skill-dir>/scripts/fetch_figma.js" "https://www.figma.com/design/:fileKey/:name?node-id=4023-474" --spec card
|
|
106
108
|
|
|
107
109
|
# Optional: With rendered preview image (2 Tier 1 calls)
|
|
108
|
-
node
|
|
110
|
+
node "<skill-dir>/scripts/fetch_figma.js" "https://www.figma.com/design/:fileKey/:name?node-id=4023-474" --spec card -i
|
|
109
111
|
|
|
110
112
|
# Shallow instances mode (collapses internal component layers into clean props)
|
|
111
|
-
node
|
|
113
|
+
node "<skill-dir>/scripts/fetch_figma.js" "https://www.figma.com/design/:fileKey/:name?node-id=4023-474" --spec card --shallow-instances
|
|
112
114
|
```
|
|
113
115
|
|
|
114
116
|
### 2. Design Tokens Dictionary Mode
|
|
115
117
|
|
|
116
118
|
```bash
|
|
117
|
-
# Export only the file's design token dictionary (colors, spacing, radii)
|
|
118
|
-
node
|
|
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
|
|
119
121
|
```
|
|
120
122
|
|
|
121
123
|
### 3. CLI Flags Reference
|
|
@@ -192,7 +194,7 @@ When an `INSTANCE` node is present, internal property hashes are converted into
|
|
|
192
194
|
|
|
193
195
|
- **Token Expired / Missing (`FIGMA_TOKEN_EXPIRED_OR_INVALID` / `isTokenExpiredOrInvalid`)**:
|
|
194
196
|
- Alert the user that their Figma access token is missing, invalid, or expired.
|
|
195
|
-
- Instruct the user to create a token in **Figma Settings → Personal Access Tokens** and
|
|
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.
|
|
196
198
|
- **Node Not Found**:
|
|
197
199
|
- Alert the user that the node ID could not be found in the specified file.
|
|
198
200
|
- **Rate Limited (`FIGMA_RATE_LIMIT_EXCEEDED` / `FIGMA_CIRCUIT_BREAKER_ACTIVE`)**:
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<a href="../../LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT" /></a>
|
|
11
11
|
</p>
|
|
12
12
|
|
|
13
|
-
Inspect, copy, and export compact, design-system-aware Node JSON and high-resolution companion preview images directly from **Figma Desktop** for AI coding assistants (Claude Code,
|
|
13
|
+
Inspect, copy, and export compact, design-system-aware Node JSON and high-resolution companion preview images directly from **Figma Desktop** for AI coding assistants (Claude Code, Kiro, Cursor, OpenCode, Antigravity) and frontend engineering.
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
@@ -37,14 +37,19 @@ To install the plugin directly from your local skills folder or repository:
|
|
|
37
37
|
1. Open the **Figma Desktop app**.
|
|
38
38
|
2. In the top-left menu, navigate to:
|
|
39
39
|
$$\text{Plugins} \longrightarrow \text{Development} \longrightarrow \text{Import plugin from manifest...}$$
|
|
40
|
-
3. Select `manifest.json` from your machine based on your setup:
|
|
40
|
+
3. Select `manifest.json` from your machine based on your setup (`~` corresponds to `%USERPROFILE%` on Windows):
|
|
41
41
|
* **Installed via `npx @pedroab0/skills` (Global)**:
|
|
42
42
|
* **Claude Code**: `~/.claude/skills/figma-extractor/figma-plugin/manifest.json`
|
|
43
|
-
* **
|
|
43
|
+
* **Cursor**: `~/.cursor/skills/figma-extractor/figma-plugin/manifest.json`
|
|
44
44
|
* **OpenCode**: `~/.config/opencode/skills/figma-extractor/figma-plugin/manifest.json`
|
|
45
|
+
* **Google Antigravity**: `~/.gemini/config/skills/figma-extractor/figma-plugin/manifest.json`
|
|
46
|
+
* **Kiro**: `~/.kiro/skills/figma-extractor/figma-plugin/manifest.json`
|
|
45
47
|
* **Installed in local workspace (`--local`)**:
|
|
46
48
|
* `.claude/skills/figma-extractor/figma-plugin/manifest.json`
|
|
49
|
+
* `.cursor/skills/figma-extractor/figma-plugin/manifest.json`
|
|
50
|
+
* `.opencode/skills/figma-extractor/figma-plugin/manifest.json`
|
|
47
51
|
* `.gemini/skills/figma-extractor/figma-plugin/manifest.json`
|
|
52
|
+
* `.kiro/skills/figma-extractor/figma-plugin/manifest.json`
|
|
48
53
|
* **Cloned Git repository**:
|
|
49
54
|
* `<repo-root>/figma-extractor/figma-plugin/manifest.json`
|
|
50
55
|
4. The plugin is now permanently loaded under **Plugins** $\rightarrow$ **Development**.
|
|
@@ -54,7 +59,7 @@ To install the plugin directly from your local skills folder or repository:
|
|
|
54
59
|
## How to Extract Node JSON
|
|
55
60
|
|
|
56
61
|
1. **Select any frame or component** on your canvas (e.g., Transaction Card, Navbar, Modal).
|
|
57
|
-
2. Right-click the element $\rightarrow$ **Plugins** $\rightarrow$ **Figma Node JSON Extractor** (or press `⌥ ⌘ P` to re-run).
|
|
62
|
+
2. Right-click the element $\rightarrow$ **Plugins** $\rightarrow$ **Figma Node JSON Extractor** (or press `⌥ ⌘ P` on macOS / `Ctrl + Alt + P` on Windows to re-run).
|
|
58
63
|
3. In the plugin popup, choose your export format:
|
|
59
64
|
* **📋 Copy Node JSON**: Copies the clean AST specification directly to your clipboard (ready to paste into chat).
|
|
60
65
|
* **💾 .json**: Downloads `<name>.json` containing layout, dimensions, tokens, and props.
|
|
@@ -72,7 +77,7 @@ To install the plugin directly from your local skills folder or repository:
|
|
|
72
77
|
|
|
73
78
|
## How to Tell Your Agent to Build the UI
|
|
74
79
|
|
|
75
|
-
Once the specification is in `./.specs/`, simply instruct your agent in
|
|
80
|
+
Once the specification is in `./.specs/`, simply instruct your agent in Claude Code, Kiro, Cursor, OpenCode, or Antigravity:
|
|
76
81
|
|
|
77
82
|
```text
|
|
78
83
|
Build the component from .specs/card.json
|