@pedroab0/skills 1.0.0 → 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pedro Barbosa
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,6 +1,13 @@
1
1
  # Skills
2
2
 
3
- A curated collection of modular, production-ready skills developed for AI coding agents (such as Claude Code, OpenCode, Codex, Google Antigravity, and Cursor).
3
+ <p align="center">
4
+ <a href="https://www.npmjs.com/package/@pedroab0/skills"><img src="https://img.shields.io/npm/dm/@pedroab0/skills.svg" alt="npm downloads" /></a>
5
+ <a href="https://www.npmjs.com/package/@pedroab0/skills"><img src="https://img.shields.io/npm/v/@pedroab0/skills.svg" alt="npm version" /></a>
6
+ <a href="https://www.figma.com/community/plugin/1691244420692136789/figma-node-json-extractor"><img src="https://img.shields.io/badge/Figma_Community-Plugin-000000?style=flat&logo=figma&logoColor=white" alt="Figma Community Plugin" /></a>
7
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT" /></a>
8
+ </p>
9
+
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).
4
11
 
5
12
  Each skill in this repository is designed to give AI assistants specialized, autonomous capabilities while maintaining:
6
13
  - **Token Efficiency**: Pruning unnecessary data and noise to keep agent context windows lean and fast.
@@ -14,7 +21,7 @@ Each skill in this repository is designed to give AI assistants specialized, aut
14
21
  | Skill | Role / Workflow | Description | Links |
15
22
  | :--- | :--- | :--- | :--- |
16
23
  | **[`figma-to-code`](./figma-to-code)** | **One-Shot / Fast Prototyping** | Direct, single-turn conversion of Figma designs into clean, production-ready UI components. Best for rapid prototyping and standalone components. | [Guide](./figma-to-code/README.md) · [`SKILL.md`](./figma-to-code/SKILL.md) |
17
- | **[`figma-extractor`](./figma-extractor)** | **SDD Step 1 (Ingest)** | Pure specification scraper (CLI / REST API) and companion Figma Desktop plugin that pull compact layout trees, design tokens, and preview images into `./.specs/`. Never touches application code. | [Guide](./figma-extractor/README.md) · [Plugin](./figma-extractor/figma-plugin/README.md) · [`SKILL.md`](./figma-extractor/SKILL.md) |
24
+ | **[`figma-extractor`](./figma-extractor)** | **SDD Step 1 (Ingest)** | Pure specification scraper (CLI / REST API) and companion Figma Desktop plugin that pull compact layout trees, design tokens, and preview images into `./.specs/`. Never touches application code. | [Guide](./figma-extractor/README.md) · [Plugin](./figma-extractor/figma-plugin/README.md) · [Community](https://www.figma.com/community/plugin/1691244420692136789/figma-node-json-extractor) · [`SKILL.md`](./figma-extractor/SKILL.md) |
18
25
  | **[`figma-node-builder`](./figma-node-builder)** | **SDD Step 2 (Implement)** | Grounded UI builder that reads extracted Figma Node specifications (`.specs/*.json`) and preview images (`*.png`) to build components without CSS hallucinations. | [Guide](./figma-node-builder/README.md) · [`SKILL.md`](./figma-node-builder/SKILL.md) |
19
26
 
20
27
  ---
@@ -40,10 +47,17 @@ flowchart TD
40
47
  * **Use `figma-to-code`** when you want fast execution: you provide a Figma URL, and the agent extracts and writes the component in one turn.
41
48
  * **Use `figma-extractor` + `figma-node-builder`** in formal Spec-Driven Development (SDD): the design specification is extracted to disk first (via CLI or the companion Figma Desktop plugin), reviewed/approved, and then systematically implemented into your design system without premature code edits.
42
49
 
43
- > [!TIP]
44
- > **Zero-API Figma Desktop Plugin**: To extract specifications directly inside Figma Desktop without API tokens or rate limits, use the companion **Figma Node JSON Extractor** plugin:
45
- > * **Figma Community (Recommended)**: Search for **Figma Node JSON Extractor** in Plugins.
46
- > * **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 skills folder (e.g. `~/.claude/skills/figma-extractor/figma-plugin/manifest.json` or `~/.gemini/config/skills/figma-extractor/figma-plugin/manifest.json`).
50
+ ---
51
+
52
+ ### Zero-API Companion: Figma Desktop Plugin
53
+
54
+ <p align="center">
55
+ <img src="./figma-extractor/figma-plugin/assets/cover.png" alt="Figma Node JSON Extractor" width="100%" />
56
+ </p>
57
+
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
+ * **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 skill directory (`<skill-dir>/figma-plugin/manifest.json`).
47
61
 
48
62
  ---
49
63
 
@@ -61,7 +75,7 @@ npx github:pedroab0/skills
61
75
 
62
76
  The installer features a clean, interactive terminal UI:
63
77
  * **Navigate**: Use keyboard arrows (`↑` / `↓`) or vim keys (`k` / `j`) to move between options.
64
- * **Select**: Press `[Enter]` or press number keys (`1` - `5`) to select directly.
78
+ * **Select**: Press `[Enter]` or press number keys (`1` - `6`) to select directly.
65
79
  * **Exit**: Press `[ESC]` or `q` at any time to cancel cleanly.
66
80
 
67
81
  ### Selection Options & Presets
@@ -77,6 +91,9 @@ Or specify flags for fast, non-interactive setup:
77
91
  # Install all skills for Claude Code globally
78
92
  npx @pedroab0/skills --agent claude --skill all
79
93
 
94
+ # Install SDD skills for Kiro (IDE & CLI)
95
+ npx @pedroab0/skills --agent kiro --skill sdd
96
+
80
97
  # Install SDD skills for Google Antigravity
81
98
  npx @pedroab0/skills --agent antigravity --skill figma-extractor,figma-node-builder
82
99
 
@@ -142,6 +159,19 @@ ln -s /path/to/skills/figma-node-builder ~/.gemini/config/skills/figma-node-buil
142
159
 
143
160
  ---
144
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
+
145
175
  ## Repository Structure
146
176
 
147
177
  ```text
@@ -172,3 +202,10 @@ When adding new skills to this curation:
172
202
  3. Provide a `README.md` with detailed skill-specific documentation and manual execution flags.
173
203
  4. Keep scripts self-contained with minimal external dependencies.
174
204
  5. Update this root `README.md` catalog to list the new skill.
205
+
206
+ ---
207
+
208
+ ## License
209
+
210
+ This project is licensed under the [MIT License](LICENSE).
211
+
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 OpenCode.
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(home, '.config', 'opencode', 'skills'),
149
+ globalPath: path.join(xdgConfig, 'opencode', 'skills'),
138
150
  localPath: path.join(cwd, '.opencode', 'skills'),
139
- isDetected: fs.existsSync(path.join(home, '.config', 'opencode')),
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 checkFigmaToken() {
374
- if (process.env.FIGMA_ACCESS_TOKEN || process.env.FIGMA_TOKEN) {
375
- return { found: true, source: 'environment variable' };
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
- const home = os.homedir();
378
- const candidates = [
379
- path.join(process.cwd(), '.env'),
380
- path.join(process.cwd(), '.env.local'),
381
- path.join(home, '.zshrc'),
382
- path.join(home, '.bashrc'),
383
- path.join(home, '.profile'),
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, 'utf8');
389
- if (/FIGMA_ACCESS_TOKEN|FIGMA_TOKEN/.test(text)) {
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
- ${fs.readFileSync(path.join(skill.path, 'SKILL.md'), 'utf8')}
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
- fs.symlinkSync(skill.path, targetPath, 'dir');
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, flagDest),
704
+ path: path.resolve(cwd, expandedDest),
595
705
  isCursor: false,
596
706
  });
597
707
  } else if (flagAgent) {
598
- const isGlobal = flagScope !== 'local';
599
- if (flagAgent === 'all') {
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: isGlobal ? cfg.globalPath : cfg.localPath,
715
+ path: useGlobal ? cfg.globalPath : cfg.localPath,
604
716
  isCursor: !!cfg.isCursorRule,
605
717
  });
606
718
  }
607
- } else if (agentConfigs[flagAgent]) {
608
- const cfg = agentConfigs[flagAgent];
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: isGlobal ? cfg.globalPath : cfg.localPath,
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 = true;
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: isGlobal ? cfg.globalPath : cfg.localPath,
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
- ui.item(`${style.cyan('export FIGMA_ACCESS_TOKEN="figd_your_token_here"')}`);
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();
@@ -1,5 +1,11 @@
1
1
  # Figma Extractor
2
2
 
3
+ <p align="center">
4
+ <a href="https://www.figma.com/community/plugin/1691244420692136789/figma-node-json-extractor"><img src="https://img.shields.io/badge/Figma_Community-Plugin-000000?style=flat&logo=figma&logoColor=white" alt="Figma Community Plugin" /></a>
5
+ <a href="https://www.npmjs.com/package/@pedroab0/skills"><img src="https://img.shields.io/npm/v/@pedroab0/skills.svg?label=@pedroab0/skills" alt="npm package" /></a>
6
+ <a href="../LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT" /></a>
7
+ </p>
8
+
3
9
  A lightweight, zero-dependency Node.js CLI tool and agent skill that extracts design node metadata, component hierarchies, design tokens, and rendered preview images from Figma via the Figma REST API into `./.specs/`.
4
10
 
5
11
  This skill serves as the **Specification Ingestion Layer** in a **Spec-Driven Development (SDD)** workflow.
@@ -76,10 +82,16 @@ Choose one of the following terminal commands:
76
82
  ```bash
77
83
  export FIGMA_ACCESS_TOKEN="figd_your_token_here"
78
84
  ```
79
- - **Persist globally across all terminal sessions (`~/.zshrc`)**:
85
+ - **Persist globally on macOS / Linux (`~/.zshrc`)**:
80
86
  ```bash
81
87
  echo 'export FIGMA_ACCESS_TOKEN="figd_your_token_here"' >> ~/.zshrc && source ~/.zshrc
82
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
+ ```
83
95
 
84
96
  *(Optional: If behind a corporate proxy with self-signed SSL certificates, run `echo 'FIGMA_IGNORE_SSL=true' >> .env`).*
85
97
 
@@ -104,8 +116,8 @@ node figma-extractor/scripts/fetch_figma.js "<FigmaURL>" --spec card -i
104
116
  # Atomic extraction with shallow instances (collapses component internals into props)
105
117
  node figma-extractor/scripts/fetch_figma.js "<FigmaURL>" --spec card --shallow-instances
106
118
 
107
- # Extract only design tokens dictionary (colors, spacing, radii)
108
- node figma-extractor/scripts/fetch_figma.js "<FigmaURL>" --tokens > ./.specs/tokens.json
119
+ # Extract only design tokens dictionary into ./.specs/tokens.json
120
+ node figma-extractor/scripts/fetch_figma.js "<FigmaURL>" --tokens --spec tokens
109
121
  ```
110
122
 
111
123
  ---
@@ -133,13 +145,19 @@ node figma-extractor/scripts/fetch_figma.js "<FigmaURL>" --tokens > ./.specs/tok
133
145
 
134
146
  ## Zero-API Companion: Figma Node JSON Extractor (Figma Plugin)
135
147
 
148
+ <p align="center">
149
+ <img src="./figma-plugin/assets/icon.png" alt="Figma Node JSON Extractor" width="200" />
150
+ </p>
151
+
136
152
  For developers who prefer exporting specifications directly from Figma Desktop without configuring API access tokens or consuming REST API quotas:
137
153
 
138
- * **Published Figma Plugin (Recommended)**: Search for **Figma Node JSON Extractor** in Figma Community / Plugins and click **Open in...** or **Save**.
139
- * **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:
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.
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):
140
156
  * **Claude Code**: `~/.claude/skills/figma-extractor/figma-plugin/manifest.json`
141
- * **Google Antigravity**: `~/.gemini/config/skills/figma-extractor/figma-plugin/manifest.json`
157
+ * **Cursor**: `.cursor/skills/figma-extractor/figma-plugin/manifest.json` (or `~/.cursor/skills/...`)
142
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`
143
161
  * **Cloned Git Repo**: `<repo-root>/figma-extractor/figma-plugin/manifest.json`
144
162
  * **Export Options**: Select any frame or component, then click:
145
163
  * **📋 Copy Node JSON**: Copies the clean AST specification to your clipboard.
@@ -155,7 +173,7 @@ The skill includes the following resources:
155
173
 
156
174
  - **Agent Instruction Guide (`SKILL.md`)**: The core skill definition read by AI agents containing workflow triggers and operational boundaries (spec ingestion only).
157
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.
158
- - **Unit Test Suite (`scripts/test_fetch_figma.js`)**: A suite of 40 automated unit tests verifying URL parsing, token formatting, component prop cleaning, rate-limit gates, and node pruning without requiring network access.
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.
159
177
  - **Companion Figma Plugin (`figma-plugin/`)**: Zero-API desktop exporter for extracting AST and previews directly inside Figma Desktop.
160
178
  - **Reference Documentation (`README.md`)**: This guide explaining architecture, comparison, and operational flags.
161
179
 
@@ -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 `/Applications` for browsers**: Do not search for installed browser binaries or run browser automation scripts.
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 `~/.figma_ratelimit.json`. All future calls to that file key are **immediately blocked with zero network traffic**, preventing Figma from extending the lockout penalty.
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**: `export FIGMA_ACCESS_TOKEN="figd_your_token_here"` or `export FIGMA_TOKEN="..."`
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 the skill's scripts directory:
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
- **Script Path Resolution**:
91
- - **Global Antigravity install**: `node ~/.gemini/config/skills/figma-extractor/scripts/fetch_figma.js "<FigmaURL>" --spec <name>`
92
- - **Global Claude Code install**: `node ~/.claude/skills/figma-extractor/scripts/fetch_figma.js "<FigmaURL>" --spec <name>`
93
- - **Project local install**: `node figma-extractor/scripts/fetch_figma.js "<FigmaURL>" --spec <name>`
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 figma-extractor/scripts/fetch_figma.js "https://www.figma.com/design/:fileKey/:name?node-id=4023-474" --spec card
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 figma-extractor/scripts/fetch_figma.js "https://www.figma.com/design/:fileKey/:name?node-id=4023-474" --spec card -i
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 figma-extractor/scripts/fetch_figma.js "https://www.figma.com/design/:fileKey/:name?node-id=4023-474" --spec card --shallow-instances
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 figma-extractor/scripts/fetch_figma.js "https://www.figma.com/design/:fileKey/:name" --tokens > ./.specs/tokens.json
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,8 +194,8 @@ 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 export `FIGMA_ACCESS_TOKEN=your_token` or update `.env`.
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`)**:
199
- - 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 into `./.specs/<name>.json`.
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`.