@fprad0/skill-master-mcp 0.0.9 → 0.0.11

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.
Files changed (240) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +43 -9
  3. package/VERSION.md +3 -3
  4. package/bin/lib/client-config.mjs +268 -0
  5. package/bin/lib/menu-core.mjs +678 -33
  6. package/bin/skill-master-bootstrap-global.mjs +15 -1
  7. package/bin/skill-master-doctor.mjs +181 -0
  8. package/bin/skill-master-install-global-skills.mjs +30 -10
  9. package/bin/skill-master-menu.mjs +184 -36
  10. package/bin/skill-master-register-clients.mjs +43 -99
  11. package/dist/index.js +30 -5
  12. package/dist/index.js.map +1 -1
  13. package/docs/operations/GUIA_MULTI_COMPUTADOR.md +255 -0
  14. package/docs/operations/GUIA_NPM_PUBLICO.md +147 -0
  15. package/docs/skill-candidates/v0.0.10/cli-creator/LICENSE.txt +201 -0
  16. package/docs/skill-candidates/v0.0.10/cli-creator/SKILL.md +160 -0
  17. package/docs/skill-candidates/v0.0.10/cli-creator/agents/openai.yaml +4 -0
  18. package/docs/skill-candidates/v0.0.10/cli-creator/references/agent-cli-patterns.md +154 -0
  19. package/docs/skill-candidates/v0.0.10/developer-workstation-ops/SKILL.md +32 -0
  20. package/docs/skill-candidates/v0.0.10/figma/LICENSE.txt +2 -0
  21. package/docs/skill-candidates/v0.0.10/figma/SKILL.md +42 -0
  22. package/docs/skill-candidates/v0.0.10/figma/agents/openai.yaml +14 -0
  23. package/docs/skill-candidates/v0.0.10/figma/assets/figma-small.svg +3 -0
  24. package/docs/skill-candidates/v0.0.10/figma/assets/figma.png +0 -0
  25. package/docs/skill-candidates/v0.0.10/figma/assets/icon.svg +28 -0
  26. package/docs/skill-candidates/v0.0.10/figma/references/figma-mcp-config.md +35 -0
  27. package/docs/skill-candidates/v0.0.10/figma/references/figma-tools-and-prompts.md +34 -0
  28. package/docs/skill-candidates/v0.0.10/figma-code-connect-components/LICENSE.TXT +2 -0
  29. package/docs/skill-candidates/v0.0.10/figma-code-connect-components/SKILL.md +349 -0
  30. package/docs/skill-candidates/v0.0.10/figma-code-connect-components/agents/openai.yaml +14 -0
  31. package/docs/skill-candidates/v0.0.10/figma-code-connect-components/assets/figma-small.svg +3 -0
  32. package/docs/skill-candidates/v0.0.10/figma-code-connect-components/assets/figma.png +0 -0
  33. package/docs/skill-candidates/v0.0.10/figma-code-connect-components/assets/icon.svg +28 -0
  34. package/docs/skill-candidates/v0.0.10/figma-code-connect-components/references/mapping-checklist.md +7 -0
  35. package/docs/skill-candidates/v0.0.10/figma-code-connect-components/scripts/normalize_node_id.py +25 -0
  36. package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/LICENSE.TXT +2 -0
  37. package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/SKILL.md +537 -0
  38. package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/agents/openai.yaml +14 -0
  39. package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/assets/figma-small.svg +3 -0
  40. package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/assets/figma.png +0 -0
  41. package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/assets/icon.svg +28 -0
  42. package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/references/rule-template.md +15 -0
  43. package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/scripts/check_agents_md.sh +9 -0
  44. package/docs/skill-candidates/v0.0.10/figma-generate-design/LICENSE.TXT +2 -0
  45. package/docs/skill-candidates/v0.0.10/figma-generate-design/SKILL.md +341 -0
  46. package/docs/skill-candidates/v0.0.10/figma-generate-design/agents/openai.yaml +14 -0
  47. package/docs/skill-candidates/v0.0.10/figma-generate-design/assets/figma-small.svg +3 -0
  48. package/docs/skill-candidates/v0.0.10/figma-generate-design/assets/figma.png +0 -0
  49. package/docs/skill-candidates/v0.0.10/figma-generate-design/assets/icon.svg +28 -0
  50. package/docs/skill-candidates/v0.0.10/figma-generate-design/maintainers.yml +1 -0
  51. package/docs/skill-candidates/v0.0.10/figma-generate-library/LICENSE.TXT +2 -0
  52. package/docs/skill-candidates/v0.0.10/figma-generate-library/SKILL.md +314 -0
  53. package/docs/skill-candidates/v0.0.10/figma-generate-library/agents/openai.yaml +14 -0
  54. package/docs/skill-candidates/v0.0.10/figma-generate-library/assets/figma-small.svg +3 -0
  55. package/docs/skill-candidates/v0.0.10/figma-generate-library/assets/figma.png +0 -0
  56. package/docs/skill-candidates/v0.0.10/figma-generate-library/assets/icon.svg +28 -0
  57. package/docs/skill-candidates/v0.0.10/figma-generate-library/maintainers.yml +3 -0
  58. package/docs/skill-candidates/v0.0.10/figma-generate-library/references/code-connect-setup.md +260 -0
  59. package/docs/skill-candidates/v0.0.10/figma-generate-library/references/component-creation.md +1014 -0
  60. package/docs/skill-candidates/v0.0.10/figma-generate-library/references/discovery-phase.md +518 -0
  61. package/docs/skill-candidates/v0.0.10/figma-generate-library/references/documentation-creation.md +834 -0
  62. package/docs/skill-candidates/v0.0.10/figma-generate-library/references/error-recovery.md +540 -0
  63. package/docs/skill-candidates/v0.0.10/figma-generate-library/references/naming-conventions.md +527 -0
  64. package/docs/skill-candidates/v0.0.10/figma-generate-library/references/token-creation.md +962 -0
  65. package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/bindVariablesToComponent.js +110 -0
  66. package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/cleanupOrphans.js +127 -0
  67. package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/createComponentWithVariants.js +148 -0
  68. package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/createDocumentationPage.js +139 -0
  69. package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/createSemanticTokens.js +108 -0
  70. package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/createVariableCollection.js +49 -0
  71. package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/inspectFileStructure.js +121 -0
  72. package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/rehydrateState.js +92 -0
  73. package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/validateCreation.js +83 -0
  74. package/docs/skill-candidates/v0.0.10/figma-implement-design/LICENSE.txt +2 -0
  75. package/docs/skill-candidates/v0.0.10/figma-implement-design/SKILL.md +258 -0
  76. package/docs/skill-candidates/v0.0.10/figma-implement-design/agents/openai.yaml +14 -0
  77. package/docs/skill-candidates/v0.0.10/figma-implement-design/assets/figma-small.svg +3 -0
  78. package/docs/skill-candidates/v0.0.10/figma-implement-design/assets/figma.png +0 -0
  79. package/docs/skill-candidates/v0.0.10/figma-implement-design/assets/icon.svg +28 -0
  80. package/docs/skill-candidates/v0.0.10/figma-use/LICENSE.TXT +2 -0
  81. package/docs/skill-candidates/v0.0.10/figma-use/SKILL.md +233 -0
  82. package/docs/skill-candidates/v0.0.10/figma-use/agents/openai.yaml +14 -0
  83. package/docs/skill-candidates/v0.0.10/figma-use/assets/figma-small.svg +3 -0
  84. package/docs/skill-candidates/v0.0.10/figma-use/assets/figma.png +0 -0
  85. package/docs/skill-candidates/v0.0.10/figma-use/assets/icon.svg +28 -0
  86. package/docs/skill-candidates/v0.0.10/figma-use/maintainers.yml +1 -0
  87. package/docs/skill-candidates/v0.0.10/figma-use/references/api-reference.md +301 -0
  88. package/docs/skill-candidates/v0.0.10/figma-use/references/common-patterns.md +512 -0
  89. package/docs/skill-candidates/v0.0.10/figma-use/references/component-patterns.md +488 -0
  90. package/docs/skill-candidates/v0.0.10/figma-use/references/effect-style-patterns.md +123 -0
  91. package/docs/skill-candidates/v0.0.10/figma-use/references/gotchas.md +599 -0
  92. package/docs/skill-candidates/v0.0.10/figma-use/references/maintainers.yml +12 -0
  93. package/docs/skill-candidates/v0.0.10/figma-use/references/plugin-api-patterns.md +513 -0
  94. package/docs/skill-candidates/v0.0.10/figma-use/references/plugin-api-standalone.d.ts +11293 -0
  95. package/docs/skill-candidates/v0.0.10/figma-use/references/plugin-api-standalone.index.md +441 -0
  96. package/docs/skill-candidates/v0.0.10/figma-use/references/text-style-patterns.md +203 -0
  97. package/docs/skill-candidates/v0.0.10/figma-use/references/validation-and-recovery.md +109 -0
  98. package/docs/skill-candidates/v0.0.10/figma-use/references/variable-patterns.md +354 -0
  99. package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/maintainers.yml +9 -0
  100. package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-components--creating.md +17 -0
  101. package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-components--using.md +17 -0
  102. package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-components.md +50 -0
  103. package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-effect-styles.md +52 -0
  104. package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-text-styles.md +90 -0
  105. package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-variables--creating.md +13 -0
  106. package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-variables--using.md +13 -0
  107. package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-variables.md +64 -0
  108. package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds.md +41 -0
  109. package/docs/skill-candidates/v0.0.10/frontend-design/LICENSE.txt +177 -0
  110. package/docs/skill-candidates/v0.0.10/frontend-design/SKILL.md +55 -0
  111. package/docs/skill-candidates/v0.0.10/frontend-ui-ux-systems/SKILL.md +32 -0
  112. package/docs/skill-candidates/v0.0.10/github/SKILL.md +74 -0
  113. package/docs/skill-candidates/v0.0.10/github/agents/openai.yaml +6 -0
  114. package/docs/skill-candidates/v0.0.10/github/assets/github-small.svg +3 -0
  115. package/docs/skill-candidates/v0.0.10/github/assets/github.png +0 -0
  116. package/docs/skill-candidates/v0.0.10/image-graphic-design-rendering/SKILL.md +28 -0
  117. package/docs/skill-candidates/v0.0.10/language-quality-pt-en-fr-it-ru/SKILL.md +28 -0
  118. package/docs/skill-candidates/v0.0.10/math-physics-reasoning/SKILL.md +28 -0
  119. package/docs/skill-candidates/v0.0.10/mcp-builder/LICENSE.txt +202 -0
  120. package/docs/skill-candidates/v0.0.10/mcp-builder/SKILL.md +236 -0
  121. package/docs/skill-candidates/v0.0.10/mcp-builder/reference/evaluation.md +602 -0
  122. package/docs/skill-candidates/v0.0.10/mcp-builder/reference/mcp_best_practices.md +249 -0
  123. package/docs/skill-candidates/v0.0.10/mcp-builder/reference/node_mcp_server.md +970 -0
  124. package/docs/skill-candidates/v0.0.10/mcp-builder/reference/python_mcp_server.md +719 -0
  125. package/docs/skill-candidates/v0.0.10/mcp-builder/scripts/connections.py +151 -0
  126. package/docs/skill-candidates/v0.0.10/mcp-builder/scripts/evaluation.py +373 -0
  127. package/docs/skill-candidates/v0.0.10/mcp-builder/scripts/example_evaluation.xml +22 -0
  128. package/docs/skill-candidates/v0.0.10/mcp-builder/scripts/requirements.txt +2 -0
  129. package/docs/skill-candidates/v0.0.10/mcp-client-readiness/SKILL.md +31 -0
  130. package/docs/skill-candidates/v0.0.10/openai-docs/LICENSE.txt +201 -0
  131. package/docs/skill-candidates/v0.0.10/openai-docs/SKILL.md +161 -0
  132. package/docs/skill-candidates/v0.0.10/openai-docs/agents/openai.yaml +14 -0
  133. package/docs/skill-candidates/v0.0.10/openai-docs/assets/openai-small.svg +3 -0
  134. package/docs/skill-candidates/v0.0.10/openai-docs/assets/openai.png +0 -0
  135. package/docs/skill-candidates/v0.0.10/openai-docs/references/latest-model.md +37 -0
  136. package/docs/skill-candidates/v0.0.10/openai-docs/references/prompting-guide.md +244 -0
  137. package/docs/skill-candidates/v0.0.10/openai-docs/references/upgrade-guide.md +181 -0
  138. package/docs/skill-candidates/v0.0.10/openai-docs/scripts/fetch-codex-manual.mjs +598 -0
  139. package/docs/skill-candidates/v0.0.10/openai-docs/scripts/resolve-latest-model-info.js +147 -0
  140. package/docs/skill-candidates/v0.0.10/playwright/LICENSE.txt +201 -0
  141. package/docs/skill-candidates/v0.0.10/playwright/NOTICE.txt +14 -0
  142. package/docs/skill-candidates/v0.0.10/playwright/SKILL.md +147 -0
  143. package/docs/skill-candidates/v0.0.10/playwright/agents/openai.yaml +6 -0
  144. package/docs/skill-candidates/v0.0.10/playwright/assets/playwright-small.svg +3 -0
  145. package/docs/skill-candidates/v0.0.10/playwright/assets/playwright.png +0 -0
  146. package/docs/skill-candidates/v0.0.10/playwright/references/cli.md +116 -0
  147. package/docs/skill-candidates/v0.0.10/playwright/references/workflows.md +95 -0
  148. package/docs/skill-candidates/v0.0.10/playwright/scripts/playwright_cli.sh +25 -0
  149. package/docs/skill-candidates/v0.0.10/polyglot-backend-engineering/SKILL.md +32 -0
  150. package/docs/skill-candidates/v0.0.10/screenshot/LICENSE.txt +201 -0
  151. package/docs/skill-candidates/v0.0.10/screenshot/SKILL.md +267 -0
  152. package/docs/skill-candidates/v0.0.10/screenshot/agents/openai.yaml +6 -0
  153. package/docs/skill-candidates/v0.0.10/screenshot/assets/screenshot-small.svg +5 -0
  154. package/docs/skill-candidates/v0.0.10/screenshot/assets/screenshot.png +0 -0
  155. package/docs/skill-candidates/v0.0.10/screenshot/scripts/ensure_macos_permissions.sh +54 -0
  156. package/docs/skill-candidates/v0.0.10/screenshot/scripts/macos_display_info.swift +22 -0
  157. package/docs/skill-candidates/v0.0.10/screenshot/scripts/macos_permissions.swift +40 -0
  158. package/docs/skill-candidates/v0.0.10/screenshot/scripts/macos_window_info.swift +126 -0
  159. package/docs/skill-candidates/v0.0.10/screenshot/scripts/take_screenshot.ps1 +163 -0
  160. package/docs/skill-candidates/v0.0.10/screenshot/scripts/take_screenshot.py +585 -0
  161. package/docs/skill-candidates/v0.0.10/skill-master-orchestrator/SKILL.md +62 -0
  162. package/docs/skill-candidates/v0.0.10/skill-master-orchestrator/agents/openai.yaml +4 -0
  163. package/docs/skill-candidates/v0.0.10/skill-master-orchestrator/references/activation-policy.md +77 -0
  164. package/docs/skill-candidates/v0.0.10/skill-master-orchestrator/references/human-approval-policy.md +83 -0
  165. package/docs/skill-candidates/v0.0.10/skill-master-orchestrator/references/persona-dev-senior-master.md +46 -0
  166. package/docs/skill-candidates/v0.0.10/terminal-menu-operations/SKILL.md +30 -0
  167. package/docs/skill-candidates/v0.0.10/terminal-pixel-art-tui/SKILL.md +43 -0
  168. package/docs/skill-candidates/v0.0.10/webapp-testing/LICENSE.txt +202 -0
  169. package/docs/skill-candidates/v0.0.10/webapp-testing/SKILL.md +96 -0
  170. package/docs/skill-candidates/v0.0.10/webapp-testing/examples/console_logging.py +35 -0
  171. package/docs/skill-candidates/v0.0.10/webapp-testing/examples/element_discovery.py +40 -0
  172. package/docs/skill-candidates/v0.0.10/webapp-testing/examples/static_html_automation.py +33 -0
  173. package/docs/skill-candidates/v0.0.10/webapp-testing/scripts/with_server.py +106 -0
  174. package/docs/skill-candidates/v0.0.10/winui-app/LICENSE.txt +202 -0
  175. package/docs/skill-candidates/v0.0.10/winui-app/SKILL.md +94 -0
  176. package/docs/skill-candidates/v0.0.10/winui-app/agents/openai.yaml +5 -0
  177. package/docs/skill-candidates/v0.0.10/winui-app/assets/winui.png +0 -0
  178. package/docs/skill-candidates/v0.0.10/winui-app/config.yaml +50 -0
  179. package/docs/skill-candidates/v0.0.10/winui-app/references/_sections.md +96 -0
  180. package/docs/skill-candidates/v0.0.10/winui-app/references/accessibility-input-and-localization.md +51 -0
  181. package/docs/skill-candidates/v0.0.10/winui-app/references/build-run-and-launch-verification.md +72 -0
  182. package/docs/skill-candidates/v0.0.10/winui-app/references/community-toolkit-controls-and-helpers.md +57 -0
  183. package/docs/skill-candidates/v0.0.10/winui-app/references/controls-layout-and-adaptive-ui.md +84 -0
  184. package/docs/skill-candidates/v0.0.10/winui-app/references/foundation-environment-audit-and-remediation.md +82 -0
  185. package/docs/skill-candidates/v0.0.10/winui-app/references/foundation-setup-and-project-selection.md +67 -0
  186. package/docs/skill-candidates/v0.0.10/winui-app/references/foundation-template-first-recovery.md +62 -0
  187. package/docs/skill-candidates/v0.0.10/winui-app/references/foundation-winui-app-structure.md +62 -0
  188. package/docs/skill-candidates/v0.0.10/winui-app/references/motion-animations-and-polish.md +45 -0
  189. package/docs/skill-candidates/v0.0.10/winui-app/references/performance-diagnostics-and-responsiveness.md +46 -0
  190. package/docs/skill-candidates/v0.0.10/winui-app/references/sample-source-map.md +37 -0
  191. package/docs/skill-candidates/v0.0.10/winui-app/references/shell-navigation-and-windowing.md +67 -0
  192. package/docs/skill-candidates/v0.0.10/winui-app/references/styling-theming-materials-and-icons.md +71 -0
  193. package/docs/skill-candidates/v0.0.10/winui-app/references/testing-debugging-and-review-checklists.md +77 -0
  194. package/docs/skill-candidates/v0.0.10/winui-app/references/windows-app-sdk-lifecycle-notifications-and-deployment.md +52 -0
  195. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/SKILL.md +399 -0
  196. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/common-patterns.md +331 -0
  197. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/complete-examples.md +872 -0
  198. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/component-patterns.md +502 -0
  199. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/data-fetching.md +767 -0
  200. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/file-organization.md +502 -0
  201. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/loading-and-error-states.md +501 -0
  202. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/performance.md +406 -0
  203. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/routing-guide.md +364 -0
  204. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/styling-guide.md +428 -0
  205. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/typescript-standards.md +418 -0
  206. package/docs/skill-candidates/v0.0.11/git-version-control-ops/SKILL.md +34 -0
  207. package/docs/skill-candidates/v0.0.11/go-engineering/SKILL.md +34 -0
  208. package/docs/skill-candidates/v0.0.11/java-engineering/SKILL.md +34 -0
  209. package/docs/skill-candidates/v0.0.11/javascript-engineering/SKILL.md +34 -0
  210. package/docs/skill-candidates/v0.0.11/json-contract-design/SKILL.md +34 -0
  211. package/docs/skill-candidates/v0.0.11/multi-client-mcp-ops/SKILL.md +36 -0
  212. package/docs/skill-candidates/v0.0.11/nextjs/SKILL.md +745 -0
  213. package/docs/skill-candidates/v0.0.11/nextjs/agents/openai.yaml +3 -0
  214. package/docs/skill-candidates/v0.0.11/nextjs/references/app-router-files.md +94 -0
  215. package/docs/skill-candidates/v0.0.11/python-engineering/SKILL.md +34 -0
  216. package/docs/skill-candidates/v0.0.11/ruby-engineering/SKILL.md +34 -0
  217. package/docs/skill-candidates/v0.0.11/senior-fullstack/SKILL.md +209 -0
  218. package/docs/skill-candidates/v0.0.11/senior-fullstack/references/architecture_patterns.md +103 -0
  219. package/docs/skill-candidates/v0.0.11/senior-fullstack/references/development_workflows.md +103 -0
  220. package/docs/skill-candidates/v0.0.11/senior-fullstack/references/tech_stack_guide.md +103 -0
  221. package/docs/skill-candidates/v0.0.11/senior-fullstack/scripts/code_quality_analyzer.py +114 -0
  222. package/docs/skill-candidates/v0.0.11/senior-fullstack/scripts/fullstack_scaffolder.py +114 -0
  223. package/docs/skill-candidates/v0.0.11/senior-fullstack/scripts/project_scaffolder.py +114 -0
  224. package/docs/skill-candidates/v0.0.11/shadcn/SKILL.md +573 -0
  225. package/docs/skill-candidates/v0.0.11/shadcn/agents/openai.yaml +3 -0
  226. package/docs/skill-candidates/v0.0.11/sql-postgresql-engineering/SKILL.md +34 -0
  227. package/docs/skill-candidates/v0.0.11/terminal-shell-ops/SKILL.md +34 -0
  228. package/docs/skill-candidates/v0.0.11/typescript-expert/SKILL.md +429 -0
  229. package/docs/skill-candidates/v0.0.11/typescript-expert/references/tsconfig-strict.json +92 -0
  230. package/docs/skill-candidates/v0.0.11/typescript-expert/references/typescript-cheatsheet.md +383 -0
  231. package/docs/skill-candidates/v0.0.11/typescript-expert/references/utility-types.ts +335 -0
  232. package/docs/skill-candidates/v0.0.11/typescript-expert/scripts/ts_diagnostic.py +203 -0
  233. package/docs/skill-candidates/v0.0.11/ui-component-primitives/SKILL.md +34 -0
  234. package/docs/skill-candidates/v0.0.11/web-mobile-design-systems/SKILL.md +34 -0
  235. package/docs/skill-candidates/v0.0.11/windows-linux-platform-ops/SKILL.md +34 -0
  236. package/manifests/channels/beta.json +7 -7
  237. package/manifests/channels/stable.json +8 -8
  238. package/network/unapproved-skill-candidates.json +34 -1
  239. package/package.json +7 -2
  240. package/scripts/verify-menu-actions.mjs +115 -0
@@ -0,0 +1,1014 @@
1
+ > Part of the [figma-generate-library skill](../SKILL.md).
2
+
3
+ # Component Creation Reference
4
+
5
+ Complete guide for Phase 3: building components with variant matrices, variable bindings, component properties, and documentation.
6
+
7
+ ---
8
+
9
+ ## 1. Component Architecture
10
+
11
+ ### Dependency Ordering: Atoms Before Molecules
12
+
13
+ Always build in dependency order. A molecule that contains an atom instance cannot exist until the atom is published. Suggested ordering:
14
+
15
+ ```
16
+ Tier 0 (atoms): Icon, Avatar, Badge, Spinner
17
+ Tier 1 (molecules): Button, Checkbox, Toggle, Input, Select
18
+ Tier 2 (organisms): Card, Dialog, Menu, Navigation, Form
19
+ ```
20
+
21
+ If a component embeds an instance of another component, the embedded component must be created first. Build your dependency graph during Phase 0 and encode the creation order in the plan.
22
+
23
+ ### Building Blocks Sub-Components (M3 Pattern)
24
+
25
+ For complex components with independent sub-element state machines, extract the sub-element into its own component set prefixed with `Building Blocks/` (public) or `.Building Blocks/` (hidden from assets panel). The dot-prefix is a Figma convention for suppressing a component from the public assets panel.
26
+
27
+ **When to use Building Blocks:**
28
+ - The sub-element has its own variant axes (state, selection) that would cause combinatorial explosion in the parent
29
+ - The sub-element repeats (nav items, table cells, calendar cells, segmented button segments)
30
+ - The sub-element has different variant axes than the parent
31
+
32
+ **Example (M3 Segmented Button):**
33
+ ```
34
+ Building Blocks/Segmented button/Button segment (start) [27 variants: Config × State × Selected]
35
+ Building Blocks/Segmented button/Button segment (middle) [27 variants]
36
+ Building Blocks/Segmented button/Button segment (end) [27 variants]
37
+
38
+ Segmented button [16 variants: Segments=2-5 × Density=0/-1/-2/-3]
39
+ Each variant contains instances of the appropriate Building Block segment components.
40
+ ```
41
+
42
+ The parent manages composition and configuration; the Building Block manages its own interaction states.
43
+
44
+ ### Private Components (`__` Prefix)
45
+
46
+ Use the `__` prefix for internal helper components that should not appear in the team library (Shop Minis pattern). Use `_` for documentation-only components (UI3 pattern).
47
+
48
+ ```
49
+ __asset // private icon/asset holder
50
+ _Label/Direction // documentation annotation helper
51
+ ```
52
+
53
+ ---
54
+
55
+ ## 2. Creating the Component Page
56
+
57
+ Each component lives on its own dedicated page (one page per component is the default). The page contains: a documentation frame at top-left and the component set positioned to its right or below.
58
+
59
+ ```javascript
60
+ (async () => {
61
+ try {
62
+ // Create or find the component page
63
+ let page = figma.root.children.find(p => p.name === 'Button');
64
+ if (!page) {
65
+ page = figma.createPage();
66
+ page.name = 'Button';
67
+ }
68
+ await figma.setCurrentPageAsync(page);
69
+
70
+ // Documentation frame — positioned at (40, 40)
71
+ const docFrame = figma.createFrame();
72
+ docFrame.name = 'Button / Documentation';
73
+ docFrame.x = 40;
74
+ docFrame.y = 40;
75
+ docFrame.resize(600, 400);
76
+ docFrame.fills = [{ type: 'SOLID', color: { r: 1, g: 1, b: 1 } }];
77
+ docFrame.layoutMode = 'VERTICAL';
78
+ docFrame.primaryAxisSizingMode = 'AUTO';
79
+ docFrame.counterAxisSizingMode = 'FIXED';
80
+ docFrame.paddingTop = 40;
81
+ docFrame.paddingBottom = 40;
82
+ docFrame.paddingLeft = 40;
83
+ docFrame.paddingRight = 40;
84
+ docFrame.itemSpacing = 16;
85
+
86
+ // Title text node
87
+ await figma.loadFontAsync({ family: 'Inter', style: 'Bold' });
88
+ const title = figma.createText();
89
+ title.fontName = { family: 'Inter', style: 'Bold' };
90
+ title.fontSize = 32;
91
+ title.characters = 'Button';
92
+ docFrame.appendChild(title);
93
+
94
+ // Description text node
95
+ await figma.loadFontAsync({ family: 'Inter', style: 'Regular' });
96
+ const desc = figma.createText();
97
+ desc.fontName = { family: 'Inter', style: 'Regular' };
98
+ desc.fontSize = 14;
99
+ desc.characters = 'Buttons allow users to take actions and make choices with a single tap.';
100
+ docFrame.appendChild(desc);
101
+
102
+ // Tag docFrame with pluginData for idempotency
103
+ docFrame.setPluginData('dsb_run_id', RUN_ID);
104
+ docFrame.setPluginData('dsb_key', 'doc/button');
105
+
106
+ figma.closePlugin(JSON.stringify({ docFrameId: docFrame.id, pageId: page.id }));
107
+ } catch (e) { figma.closePluginWithFailure(e.toString()); }
108
+ })();
109
+ ```
110
+
111
+ ---
112
+
113
+ ## 3. Base Component: Auto-Layout, Child Nodes, Variable Bindings
114
+
115
+ The base component is the template from which all variants are cloned. It must have:
116
+ 1. Auto-layout (not manual positioning)
117
+ 2. All child nodes present
118
+ 3. ALL visual properties bound to variables (no hardcoded values)
119
+
120
+ ### Complete Button Base Component Example
121
+
122
+ ```javascript
123
+ (async () => {
124
+ try {
125
+ const RUN_ID = 'ds-build-2024-001'; // replace with your actual run ID
126
+ await figma.setCurrentPageAsync(
127
+ figma.root.children.find(p => p.name === 'Button')
128
+ );
129
+
130
+ // Rehydrate variables from IDs stored in state ledger
131
+ const bgVar = await figma.variables.getVariableByIdAsync('VAR_ID_color_bg_primary');
132
+ const textVar = await figma.variables.getVariableByIdAsync('VAR_ID_color_text_on_primary');
133
+ const paddingVar = await figma.variables.getVariableByIdAsync('VAR_ID_spacing_md');
134
+ const radiusVar = await figma.variables.getVariableByIdAsync('VAR_ID_radius_md');
135
+ const gapVar = await figma.variables.getVariableByIdAsync('VAR_ID_spacing_sm');
136
+
137
+ // --- Base component frame ---
138
+ const comp = figma.createComponent();
139
+ comp.name = 'Size=Medium, Style=Primary, State=Default';
140
+ comp.layoutMode = 'HORIZONTAL';
141
+ comp.primaryAxisSizingMode = 'AUTO';
142
+ comp.counterAxisSizingMode = 'AUTO';
143
+ comp.counterAxisAlignItems = 'CENTER';
144
+ comp.primaryAxisAlignItems = 'CENTER';
145
+
146
+ // Padding — bound to spacing variables
147
+ comp.setBoundVariable('paddingTop', paddingVar);
148
+ comp.setBoundVariable('paddingBottom', paddingVar);
149
+ comp.setBoundVariable('paddingLeft', paddingVar);
150
+ comp.setBoundVariable('paddingRight', paddingVar);
151
+ comp.setBoundVariable('itemSpacing', gapVar);
152
+
153
+ // Corner radius — bound to radius variable
154
+ comp.setBoundVariable('topLeftRadius', radiusVar);
155
+ comp.setBoundVariable('topRightRadius', radiusVar);
156
+ comp.setBoundVariable('bottomLeftRadius', radiusVar);
157
+ comp.setBoundVariable('bottomRightRadius', radiusVar);
158
+
159
+ // Background fill — bound to color variable
160
+ const bgPaint = figma.variables.setBoundVariableForPaint(
161
+ { type: 'SOLID', color: { r: 0, g: 0, b: 0 } },
162
+ 'color',
163
+ bgVar
164
+ );
165
+ comp.fills = [bgPaint];
166
+
167
+ // --- Label text node ---
168
+ await figma.loadFontAsync({ family: 'Inter', style: 'Medium' });
169
+ const label = figma.createText();
170
+ label.name = 'label';
171
+ label.fontName = { family: 'Inter', style: 'Medium' };
172
+ label.fontSize = 14;
173
+ label.characters = 'Button';
174
+ label.layoutSizingHorizontal = 'HUG';
175
+ label.layoutSizingVertical = 'HUG';
176
+
177
+ // Text fill — bound to color variable
178
+ const textPaint = figma.variables.setBoundVariableForPaint(
179
+ { type: 'SOLID', color: { r: 1, g: 1, b: 1 } },
180
+ 'color',
181
+ textVar
182
+ );
183
+ label.fills = [textPaint];
184
+ comp.appendChild(label);
185
+
186
+ // --- Icon placeholder (Rectangle for now — will be INSTANCE_SWAP) ---
187
+ const iconBox = figma.createFrame();
188
+ iconBox.name = 'icon';
189
+ iconBox.resize(16, 16);
190
+ iconBox.fills = [];
191
+ iconBox.layoutSizingHorizontal = 'FIXED';
192
+ iconBox.layoutSizingVertical = 'FIXED';
193
+ comp.appendChild(iconBox);
194
+
195
+ // Tag for idempotency
196
+ comp.setPluginData('dsb_run_id', RUN_ID);
197
+ comp.setPluginData('dsb_phase', 'phase3');
198
+ comp.setPluginData('dsb_key', 'component/button/base');
199
+
200
+ figma.closePlugin(JSON.stringify({ baseCompId: comp.id }));
201
+ } catch (e) { figma.closePluginWithFailure(e.toString()); }
202
+ })();
203
+ ```
204
+
205
+ **ALL of these must be variable-bound (never hardcoded):**
206
+
207
+ | Property | Variable type | API method |
208
+ |---|---|---|
209
+ | Fill color | COLOR | `setBoundVariableForPaint(..., 'color', var)` |
210
+ | Stroke color | COLOR | `setBoundVariableForPaint(..., 'color', var)` |
211
+ | Text fill | COLOR | `setBoundVariableForPaint(..., 'color', var)` |
212
+ | Padding (all 4 sides) | FLOAT | `comp.setBoundVariable('paddingTop', var)` |
213
+ | Gap / itemSpacing | FLOAT | `comp.setBoundVariable('itemSpacing', var)` |
214
+ | Corner radius (all 4) | FLOAT | `comp.setBoundVariable('topLeftRadius', var)` etc. |
215
+ | Stroke weight | FLOAT | `comp.setBoundVariable('strokeWeight', var)` |
216
+
217
+ ---
218
+
219
+ ## 4. Variant Matrix
220
+
221
+ ### Defining Axes
222
+
223
+ For each component, identify its variant axes before writing any code. Standard axes:
224
+
225
+ ```
226
+ Button:
227
+ Size → [Small, Medium, Large]
228
+ Style → [Primary, Secondary, Outline, Ghost]
229
+ State → [Default, Hover, Focused, Pressed, Disabled]
230
+ Total = 3 × 4 × 5 = 60 combinations — exceeds 30 limit → split by Style
231
+ ```
232
+
233
+ ### The 30-Combination Cap and Split Strategy
234
+
235
+ When the product of all variant axes exceeds 30 combinations, split the matrix. Options:
236
+
237
+ 1. **Split by a primary axis**: Create separate component sets, one per Style (Primary Button, Secondary Button, etc.)
238
+ 2. **Use INSTANCE_SWAP**: Remove a visual axis (like Icon) from the variant matrix entirely and expose it as an INSTANCE_SWAP property instead
239
+ 3. **Use Building Blocks**: Extract sub-elements with their own state axes into Building Block component sets
240
+
241
+ For Button with Size × State = 15 combinations, add Style as a variant axis only if Style ≤ 2 options (15 × 2 = 30). For more Styles, split.
242
+
243
+ ### Creating All Variants with use_figma
244
+
245
+ Build each variant by cloning the base component and adjusting the variable bindings that differ per variant. Pass in the base component ID from the previous call's state.
246
+
247
+ ```javascript
248
+ (async () => {
249
+ try {
250
+ const RUN_ID = 'ds-build-2024-001';
251
+ const BASE_COMP_ID = 'BASE_ID_FROM_STATE'; // from state ledger
252
+
253
+ await figma.setCurrentPageAsync(
254
+ figma.root.children.find(p => p.name === 'Button')
255
+ );
256
+
257
+ const base = await figma.getNodeByIdAsync(BASE_COMP_ID);
258
+
259
+ // Variable IDs from state ledger
260
+ const vars = {
261
+ // Primary style
262
+ bg_primary: await figma.variables.getVariableByIdAsync('VAR_ID_color_bg_primary'),
263
+ text_primary: await figma.variables.getVariableByIdAsync('VAR_ID_color_text_on_primary'),
264
+ // Secondary style
265
+ bg_secondary: await figma.variables.getVariableByIdAsync('VAR_ID_color_bg_secondary'),
266
+ text_secondary: await figma.variables.getVariableByIdAsync('VAR_ID_color_text_secondary'),
267
+ // Disabled
268
+ bg_disabled: await figma.variables.getVariableByIdAsync('VAR_ID_color_bg_disabled'),
269
+ text_disabled: await figma.variables.getVariableByIdAsync('VAR_ID_color_text_disabled'),
270
+ // Sizes
271
+ padding_sm: await figma.variables.getVariableByIdAsync('VAR_ID_spacing_sm'),
272
+ padding_md: await figma.variables.getVariableByIdAsync('VAR_ID_spacing_md'),
273
+ padding_lg: await figma.variables.getVariableByIdAsync('VAR_ID_spacing_lg'),
274
+ };
275
+
276
+ const axes = {
277
+ Size: ['Small', 'Medium', 'Large'],
278
+ Style: ['Primary', 'Secondary'],
279
+ State: ['Default', 'Hover', 'Disabled'],
280
+ };
281
+
282
+ const paddingBySize = { Small: vars.padding_sm, Medium: vars.padding_md, Large: vars.padding_lg };
283
+
284
+ const components = [];
285
+
286
+ for (const size of axes.Size) {
287
+ for (const style of axes.Style) {
288
+ for (const state of axes.State) {
289
+ const clone = base.clone();
290
+ clone.name = `Size=${size}, Style=${style}, State=${state}`;
291
+
292
+ // Bind padding by size
293
+ clone.setBoundVariable('paddingTop', paddingBySize[size]);
294
+ clone.setBoundVariable('paddingBottom', paddingBySize[size]);
295
+ clone.setBoundVariable('paddingLeft', paddingBySize[size]);
296
+ clone.setBoundVariable('paddingRight', paddingBySize[size]);
297
+
298
+ // Bind fill by style + state
299
+ const isDisabled = state === 'Disabled';
300
+ const bgVar = isDisabled ? vars.bg_disabled : (style === 'Primary' ? vars.bg_primary : vars.bg_secondary);
301
+ const txtVar = isDisabled ? vars.text_disabled : (style === 'Primary' ? vars.text_primary : vars.text_secondary);
302
+
303
+ const bgPaint = figma.variables.setBoundVariableForPaint(
304
+ { type: 'SOLID', color: { r: 0, g: 0, b: 0 } }, 'color', bgVar
305
+ );
306
+ clone.fills = [bgPaint];
307
+
308
+ const labelNode = clone.findOne(n => n.name === 'label');
309
+ const textPaint = figma.variables.setBoundVariableForPaint(
310
+ { type: 'SOLID', color: { r: 1, g: 1, b: 1 } }, 'color', txtVar
311
+ );
312
+ labelNode.fills = [textPaint];
313
+
314
+ clone.setPluginData('dsb_run_id', RUN_ID);
315
+ clone.setPluginData('dsb_key', `component/button/variant/${size}/${style}/${state}`);
316
+
317
+ components.push(clone);
318
+ }
319
+ }
320
+ }
321
+
322
+ figma.closePlugin(JSON.stringify({ variantIds: components.map(c => c.id) }));
323
+ } catch (e) { figma.closePluginWithFailure(e.toString()); }
324
+ })();
325
+ ```
326
+
327
+ ---
328
+
329
+ ## 5. `combineAsVariants` + Grid Layout
330
+
331
+ After all variant components exist, combine them into a ComponentSet and position them in a grid. This MUST be a separate `use_figma` call — you must pass in all variant IDs from the previous call's return value.
332
+
333
+ ### Grid Design Conventions
334
+
335
+ Professional design systems lay out variants in a readable grid where:
336
+ - **Columns** = the property users interact with most (typically **State**: Default, Hover, Focused, Pressed, Disabled)
337
+ - **Rows** = structural axes grouped together (typically **Size × Style**, where Size varies fastest)
338
+ - **Gap** = 16–40px between variants (20px is a safe default; match existing file if one exists)
339
+ - **Padding** = 40px around the grid inside the ComponentSet frame
340
+
341
+ ```
342
+ Visual structure:
343
+ Default Hover Focused Pressed Disabled
344
+ ┌──────────────────────────────────────────────────────────────────┐
345
+ │ Small/Primary [comp] [comp] [comp] [comp] [comp] │
346
+ │ Small/Secondary [comp] [comp] [comp] [comp] [comp] │
347
+ │ Medium/Primary [comp] [comp] [comp] [comp] [comp] │
348
+ │ Medium/Secondary[comp] [comp] [comp] [comp] [comp] │
349
+ │ Large/Primary [comp] [comp] [comp] [comp] [comp] │
350
+ │ Large/Secondary [comp] [comp] [comp] [comp] [comp] │
351
+ └──────────────────────────────────────────────────────────────────┘
352
+ ```
353
+
354
+ **Why State on columns?** State is the axis designers scan horizontally to verify interaction consistency. Size/Style define the "identity" of each row. This matches how professional design systems (M3, Polaris, Simple DS) organize their grids.
355
+
356
+ ### Adding Row/Column Header Labels
357
+
358
+ After laying out the grid, add text labels OUTSIDE the ComponentSet to help navigation. These are siblings of the ComponentSet on the page — not children of it:
359
+
360
+ ```javascript
361
+ // Add column headers above the component set
362
+ const colLabels = ['Default', 'Hover', 'Focused', 'Pressed', 'Disabled'];
363
+ await figma.loadFontAsync({ family: 'Inter', style: 'Medium' });
364
+ for (let i = 0; i < colLabels.length; i++) {
365
+ const label = figma.createText();
366
+ label.fontName = { family: 'Inter', style: 'Medium' };
367
+ label.characters = colLabels[i];
368
+ label.fontSize = 11;
369
+ label.fills = [{ type: 'SOLID', color: { r: 0.5, g: 0.5, b: 0.5 } }];
370
+ label.x = cs.x + padding + i * (childWidth + gap);
371
+ label.y = cs.y - 20;
372
+ }
373
+
374
+ // Add row headers to the left of the component set
375
+ const rowLabels = ['Small / Primary', 'Small / Secondary', 'Med / Primary', ...];
376
+ for (let i = 0; i < rowLabels.length; i++) {
377
+ const label = figma.createText();
378
+ label.fontName = { family: 'Inter', style: 'Medium' };
379
+ label.characters = rowLabels[i];
380
+ label.fontSize = 11;
381
+ label.fills = [{ type: 'SOLID', color: { r: 0.5, g: 0.5, b: 0.5 } }];
382
+ label.x = cs.x - 120;
383
+ label.y = cs.y + padding + i * (childHeight + gap) + childHeight / 2 - 6;
384
+ }
385
+ ```
386
+
387
+ **Note:** These labels are documentation aids, not part of the component itself. They help designers navigate the variant grid.
388
+
389
+ ### Grid layout code
390
+
391
+ ```javascript
392
+ (async () => {
393
+ try {
394
+ const VARIANT_IDS = ['ID1', 'ID2', '...']; // from state ledger
395
+ const PAGE_ID = 'PAGE_ID'; // from state ledger
396
+
397
+ await figma.setCurrentPageAsync(await figma.getNodeByIdAsync(PAGE_ID));
398
+
399
+ // Collect component nodes
400
+ const components = await Promise.all(
401
+ VARIANT_IDS.map(id => figma.getNodeByIdAsync(id))
402
+ );
403
+
404
+ // Combine as variants
405
+ const cs = figma.combineAsVariants(components, figma.currentPage);
406
+ cs.name = 'Button';
407
+
408
+ // Grid layout: position each variant based on its property values
409
+ // Determine column axis (State) and row axes (Size × Style)
410
+ const axes = {
411
+ Size: ['Small', 'Medium', 'Large'],
412
+ Style: ['Primary', 'Secondary'],
413
+ State: ['Default', 'Hover', 'Disabled'],
414
+ };
415
+ const COL_AXIS = 'State'; // columns
416
+ const ROW_AXES = ['Size', 'Style']; // rows (Size changes fastest)
417
+
418
+ const gap = 16;
419
+ const padding = 40;
420
+
421
+ // Measure child dimensions (all should be same height within Size tier)
422
+ // Use the first child as reference for column width
423
+ const childWidth = 120; // approximate; refine after first screenshot
424
+ const childHeight = 40;
425
+
426
+ cs.children.forEach(child => {
427
+ const props = {};
428
+ child.name.split(', ').forEach(part => {
429
+ const [k, v] = part.split('=');
430
+ props[k] = v;
431
+ });
432
+
433
+ const colIdx = axes[COL_AXIS].indexOf(props[COL_AXIS]);
434
+ // Row = Size index * number of styles + Style index
435
+ const rowIdx = axes.Size.indexOf(props.Size) * axes.Style.length
436
+ + axes.Style.indexOf(props.Style);
437
+
438
+ child.x = padding + colIdx * (childWidth + gap);
439
+ child.y = padding + rowIdx * (childHeight + gap);
440
+ });
441
+
442
+ // Resize component set to fit all children + padding
443
+ let maxX = 0, maxY = 0;
444
+ for (const child of cs.children) {
445
+ maxX = Math.max(maxX, child.x + child.width);
446
+ maxY = Math.max(maxY, child.y + child.height);
447
+ }
448
+ cs.resizeWithoutConstraints(maxX + padding, maxY + padding);
449
+
450
+ // Style the component set frame
451
+ cs.fills = [{ type: 'SOLID', color: { r: 0.95, g: 0.95, b: 0.98 } }];
452
+ cs.cornerRadius = 8;
453
+
454
+ // Position component set on page (to the right of doc frame)
455
+ cs.x = 680;
456
+ cs.y = 40;
457
+
458
+ cs.setPluginData('dsb_run_id', 'ds-build-2024-001');
459
+ cs.setPluginData('dsb_key', 'componentset/button');
460
+
461
+ figma.closePlugin(JSON.stringify({ componentSetId: cs.id }));
462
+ } catch (e) { figma.closePluginWithFailure(e.toString()); }
463
+ })();
464
+ ```
465
+
466
+ **Critical rules for combineAsVariants:**
467
+ - `components` must be a non-empty array containing ONLY `ComponentNode` objects (not frames, not groups)
468
+ - After combining, children are placed at (0,0) and overlap — you MUST manually position them
469
+ - `resizeWithoutConstraints` is required after positioning to make the component set frame fit its contents
470
+ - There is no `figma.createComponentSet()` — you cannot create an empty component set
471
+
472
+ ---
473
+
474
+ ## 6. Component Properties
475
+
476
+ Add TEXT, BOOLEAN, and INSTANCE_SWAP properties to the ComponentSet (not to individual variants). The return value of `addComponentProperty` is the actual property key (it gets a `#id:id` suffix appended) — save this key and use it immediately when setting `componentPropertyReferences`.
477
+
478
+ ### TEXT Properties
479
+
480
+ Expose editable text in instances:
481
+
482
+ ```javascript
483
+ // On the ComponentSetNode (cs):
484
+ const labelKey = cs.addComponentProperty('Label', 'TEXT', 'Button');
485
+ // labelKey is now something like "Label#0:1"
486
+
487
+ // Wire to the label child in each variant:
488
+ for (const child of cs.children) {
489
+ const labelNode = child.findOne(n => n.name === 'label');
490
+ if (labelNode) {
491
+ labelNode.componentPropertyReferences = { characters: labelKey };
492
+ }
493
+ }
494
+ ```
495
+
496
+ ### BOOLEAN Properties
497
+
498
+ Toggle child node visibility:
499
+
500
+ ```javascript
501
+ const showIconKey = cs.addComponentProperty('Show Icon', 'BOOLEAN', true);
502
+
503
+ for (const child of cs.children) {
504
+ const iconNode = child.findOne(n => n.name === 'icon');
505
+ if (iconNode) {
506
+ iconNode.componentPropertyReferences = { visible: showIconKey };
507
+ }
508
+ }
509
+ ```
510
+
511
+ ### INSTANCE_SWAP Properties
512
+
513
+ Allow swapping a nested component instance (e.g., swap the icon):
514
+
515
+ ```javascript
516
+ // defaultIconCompId is the ID of the default icon component (from state ledger)
517
+ const iconKey = cs.addComponentProperty('Icon', 'INSTANCE_SWAP', DEFAULT_ICON_COMP_ID);
518
+
519
+ for (const child of cs.children) {
520
+ const iconSlot = child.findOne(n => n.name === 'icon');
521
+ if (iconSlot && iconSlot.type === 'INSTANCE') {
522
+ iconSlot.componentPropertyReferences = { mainComponent: iconKey };
523
+ }
524
+ }
525
+ ```
526
+
527
+ **Use INSTANCE_SWAP instead of creating a variant per icon.** Never add "Icon=ChevronRight, Icon=ChevronLeft, ..." as VARIANT axes — that causes combinatorial explosion. One INSTANCE_SWAP property covers all icons.
528
+
529
+ ### Creating Icon Components for INSTANCE_SWAP
530
+
531
+ INSTANCE_SWAP needs a real Component ID as its default value. Before wiring INSTANCE_SWAP, you need at least one icon component. Here's how to create icons from SVG:
532
+
533
+ ```javascript
534
+ (async () => {
535
+ try {
536
+ // Create a simple icon component from SVG
537
+ const svgNode = figma.createNodeFromSvg(
538
+ '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">' +
539
+ '<path d="M9 18l6-6-6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>' +
540
+ '</svg>'
541
+ );
542
+
543
+ // Wrap in a component
544
+ const iconComp = figma.createComponent();
545
+ iconComp.name = 'Icon/ChevronRight';
546
+ iconComp.resize(24, 24);
547
+ iconComp.clipsContent = true;
548
+
549
+ // Move SVG children into the component
550
+ for (const child of [...svgNode.children]) {
551
+ iconComp.appendChild(child);
552
+ }
553
+ svgNode.remove();
554
+
555
+ // Bind the icon fill to a color variable (so it respects themes)
556
+ // Find vector children and bind their fills
557
+ iconComp.findAll(n => n.type === 'VECTOR').forEach(vec => {
558
+ // For stroke-based icons:
559
+ if (vec.strokes.length > 0) {
560
+ const strokePaint = figma.variables.setBoundVariableForPaint(
561
+ { type: 'SOLID', color: { r: 0, g: 0, b: 0 } }, 'color', iconColorVar
562
+ );
563
+ vec.strokes = [strokePaint];
564
+ }
565
+ });
566
+
567
+ iconComp.setPluginData('dsb_run_id', RUN_ID);
568
+ iconComp.setPluginData('dsb_key', 'icon/chevron-right');
569
+
570
+ figma.closePlugin(JSON.stringify({ iconCompId: iconComp.id }));
571
+ } catch (e) { figma.closePluginWithFailure(e.toString()); }
572
+ })();
573
+ ```
574
+
575
+ **Then use the returned `iconCompId` as the default value for INSTANCE_SWAP:**
576
+ ```javascript
577
+ const iconKey = cs.addComponentProperty('Icon', 'INSTANCE_SWAP', ICON_COMP_ID);
578
+ ```
579
+
580
+ **Constraining swap options with `preferredValues`:**
581
+ After adding the INSTANCE_SWAP property, you can optionally limit which components appear in the swap picker:
582
+ ```javascript
583
+ // Get the property definitions to find the exact key
584
+ const props = cs.componentPropertyDefinitions;
585
+ const iconPropKey = Object.keys(props).find(k => k.startsWith('Icon'));
586
+
587
+ // Set preferred values (array of component keys or instance IDs)
588
+ cs.editComponentProperty(iconPropKey, {
589
+ preferredValues: [
590
+ { type: 'COMPONENT', key: chevronRightComp.key },
591
+ { type: 'COMPONENT', key: chevronLeftComp.key },
592
+ { type: 'COMPONENT', key: closeComp.key },
593
+ ],
594
+ });
595
+ ```
596
+
597
+ **Icon library tip:** Create all icon components on a dedicated `Icons` page before building any UI components. Then reference their IDs when wiring INSTANCE_SWAP properties.
598
+
599
+ ### `componentPropertyReferences` mapping
600
+
601
+ The `componentPropertyReferences` object maps a node's own property to a component property key:
602
+
603
+ | Node property | Component property type | Used for |
604
+ |---|---|---|
605
+ | `characters` | TEXT | Editable text content |
606
+ | `visible` | BOOLEAN | Show/hide toggle |
607
+ | `mainComponent` | INSTANCE_SWAP | Swap nested instances |
608
+
609
+ ---
610
+
611
+ ## 7. `pluginData` Tagging for Idempotency
612
+
613
+ Tag EVERY created node immediately after creation. This enables safe cleanup, resumability, and idempotency checks.
614
+
615
+ ```javascript
616
+ // After creating any node:
617
+ node.setPluginData('dsb_run_id', RUN_ID); // identifies the build run
618
+ node.setPluginData('dsb_phase', 'phase3'); // which phase created it
619
+ node.setPluginData('dsb_key', KEY); // unique logical key for this entity
620
+
621
+ // Reading back:
622
+ const runId = node.getPluginData('dsb_run_id'); // '' if not set
623
+ const key = node.getPluginData('dsb_key');
624
+ ```
625
+
626
+ **Key naming convention:** use `/`-separated logical paths that mirror the entity hierarchy:
627
+ ```
628
+ 'component/button/base'
629
+ 'component/button/variant/Medium/Primary/Default'
630
+ 'componentset/button'
631
+ 'doc/button'
632
+ 'page/button'
633
+ ```
634
+
635
+ **Idempotency check before creating:** before creating a node, scan the current page for an existing node with the same `dsb_key`:
636
+
637
+ ```javascript
638
+ const existing = figma.currentPage.findAll(n =>
639
+ n.getPluginData('dsb_key') === 'componentset/button'
640
+ );
641
+ if (existing.length > 0) {
642
+ // Skip creation — already done. Return existing node's ID.
643
+ figma.closePlugin(JSON.stringify({ componentSetId: existing[0].id }));
644
+ return;
645
+ }
646
+ ```
647
+
648
+ ---
649
+
650
+ ## 8. Documentation
651
+
652
+ ### Page title + description frame
653
+
654
+ The documentation frame (see Section 2) should contain:
655
+ 1. Component name as a large title (32px+ Bold)
656
+ 2. 1–3 sentence description of what the component is and when to use it
657
+ 3. Spec notes (sizes, spacing values, accessibility notes)
658
+
659
+ ### Component `description` property
660
+
661
+ Set the description on the ComponentSet — it appears in the Figma properties panel and is exported as documentation:
662
+
663
+ ```javascript
664
+ cs.description = 'Buttons allow users to take actions and make choices. Use Primary for the highest-emphasis action on a page.';
665
+ ```
666
+
667
+ ### `documentationLinks`
668
+
669
+ Link to external documentation (Storybook, design spec, tokens reference):
670
+
671
+ ```javascript
672
+ cs.documentationLinks = [
673
+ { uri: 'https://your-storybook.com/button' }
674
+ ];
675
+ ```
676
+
677
+ ### Node names and organization
678
+
679
+ - ComponentSet: plain component name — `'Button'`
680
+ - Individual variants: `'Property=Value, Property=Value'` format (match the file's existing casing)
681
+ - Child nodes: semantic names — `'label'`, `'icon'`, `'container'`, `'state-layer'`
682
+ - Documentation frames: `'ComponentName / Documentation'`
683
+
684
+ ---
685
+
686
+ ## 9. Validation
687
+
688
+ Always validate after creating or modifying a component before proceeding to the next one.
689
+
690
+ ### `get_metadata` structural checks
691
+
692
+ After creating the component set, call `get_metadata` on the ComponentSet node and verify:
693
+ - `variantGroupProperties` lists the expected axes with the correct value arrays
694
+ - `componentPropertyDefinitions` contains the expected TEXT/BOOLEAN/INSTANCE_SWAP properties
695
+ - `children.length` equals the expected variant count (e.g., 18 for 3×2×3)
696
+ - No children are named `'Component 1'` (unnamed components are a sign of a bug)
697
+
698
+ ### `get_screenshot` — Visual Validation (Critical)
699
+
700
+ `get_screenshot` returns an **image** of the specified node. Call it on the **component page node** (not the component set) to see the full page including documentation and grid labels.
701
+
702
+ ```
703
+ Tool: get_screenshot
704
+ Args: { nodeId: "PAGE_NODE_ID", fileKey: "FILE_KEY" }
705
+ ```
706
+
707
+ **How to use the screenshot:**
708
+
709
+ 1. **Display it to the user** — this is the primary purpose. Show the screenshot as part of the user checkpoint: "Here's the Button component. Does it look right?"
710
+ 2. **Analyze it yourself** — if you have vision capabilities, check the visual checklist below. If you don't (text-only agent), fall back to structural validation only via `get_metadata` and describe what you created textually.
711
+
712
+ **Visual validation checklist** (check each item when viewing the screenshot):
713
+
714
+ | # | Check | What "good" looks like | What "broken" looks like |
715
+ |---|-------|----------------------|------------------------|
716
+ | 1 | **Grid layout** | Variants in neat rows and columns with consistent spacing | All variants piled at top-left (0,0 stacking bug) |
717
+ | 2 | **Color fills** | Components show distinct, correct colors per style variant | All components are black or same color (variable binding failed) |
718
+ | 3 | **Size differentiation** | Small variants are visibly smaller than Large variants | All variants are the same size (height/padding not bound to variables) |
719
+ | 4 | **Text readability** | Labels are visible with correct font and color | Text is invisible (white on white), missing, or shows "undefined" |
720
+ | 5 | **Spacing/padding** | Interior padding visible, components aren't "shrink-wrapped" | Components look cramped or have no visible internal space |
721
+ | 6 | **State differentiation** | Hover/Pressed variants have visible color differences from Default | All states look identical (state-specific fills not applied) |
722
+ | 7 | **Disabled state** | Lower opacity or muted colors compared to active states | Disabled looks identical to Default |
723
+ | 8 | **Documentation frame** | Title + description text visible above or beside the component grid | No documentation, or it overlaps the component set |
724
+ | 9 | **Grid labels** | Row/column headers visible around the component set (if added) | Labels overlap the grid or are missing |
725
+ | 10 | **Component set boundary** | Gray background frame wraps all variants with even padding | Frame is too small (variants clipped) or way too large |
726
+
727
+ **Screenshot → diagnosis → fix mapping:**
728
+
729
+ | Screenshot shows | Diagnosis | Fix script |
730
+ |-----------------|-----------|------------|
731
+ | All variants stacked top-left | Grid layout wasn't applied after `combineAsVariants` | Re-run the grid layout script (§5) |
732
+ | Everything black/same color | Variable bindings failed or variables don't have values for the active mode | Re-run variable binding, check mode values |
733
+ | No text visible | Font wasn't loaded, or text fill is same color as background | Check `loadFontAsync` was called; bind text fill to `color/text/*` variable |
734
+ | Variants all same size | Padding/height not bound to size variables | Re-run `bindVariablesToComponent` with size-specific tokens |
735
+ | Component set frame tiny | `resizeWithoutConstraints` wasn't called or used wrong dimensions | Re-calculate bounds from children and resize |
736
+ | Doc frame overlaps components | Component set positioned at same x,y as doc frame | Move component set: `cs.x = docFrame.x + docFrame.width + 60` |
737
+
738
+ **When visual analysis isn't available:**
739
+ If your model can't process images (text-only mode), validate structurally instead:
740
+ 1. Call `get_metadata` on the component set — verify child count, property definitions, variant names
741
+ 2. Run an `use_figma` that samples key properties:
742
+ ```javascript
743
+ (async () => {
744
+ try {
745
+ const cs = await figma.getNodeByIdAsync(CS_ID);
746
+ const sample = cs.children.slice(0, 3).map(c => ({
747
+ name: c.name,
748
+ width: c.width, height: c.height,
749
+ x: c.x, y: c.y,
750
+ fills: c.fills?.map(f => f.type === 'SOLID' ?
751
+ { r: f.color.r.toFixed(2), g: f.color.g.toFixed(2), b: f.color.b.toFixed(2), boundVar: f.boundVariables?.color?.id } : f.type
752
+ ),
753
+ }));
754
+ figma.closePlugin(JSON.stringify({ sampleVariants: sample, totalChildren: cs.children.length }));
755
+ } catch (e) { figma.closePluginWithFailure(e.toString()); }
756
+ })();
757
+ ```
758
+ This gives you positions (grid working?), dimensions (size differentiation?), and fill info (bindings working?) without needing vision.
759
+
760
+ **When to take a screenshot:**
761
+ - After EVERY completed component (mandatory — part of the user checkpoint)
762
+ - After creating the foundations documentation page
763
+ - After final QA (screenshot every page)
764
+ - Do NOT screenshot after every intermediate step (wastes tool calls)
765
+
766
+ ### Common issues
767
+
768
+ | Symptom | Likely cause | Fix |
769
+ |---|---|---|
770
+ | All variants stacked at (0,0) | `combineAsVariants` was called but children were never repositioned | Re-run grid layout script |
771
+ | Variants show wrong colors | Variable bindings applied after `combineAsVariants` instead of before | Rebind on component set children |
772
+ | Variant count wrong | Clone loop indexing error | Print `components.map(c => c.name)` before combining |
773
+ | BOOLEAN property has no effect | `componentPropertyReferences` was set on the component set frame, not on the child node | Find the actual child node and set references there |
774
+ | INSTANCE_SWAP shows no swap option | Default value was not a valid component ID | Pass a real existing component ID as `defaultValue` |
775
+ | `combineAsVariants` throws | At least one node in the array is not a `ComponentNode` | Filter array: `nodes.filter(n => n.type === 'COMPONENT')` |
776
+ | `addComponentProperty` returns unexpected key | Expected — the key gets a `#id:id` suffix | Save the returned value immediately: `const key = cs.addComponentProperty(...)` |
777
+
778
+ ---
779
+
780
+ ## 10. Complete Worked Example: Button Component
781
+
782
+ This shows the full sequence of `use_figma` calls for a Button component, including state passing between calls. Replace `RUN_ID` and variable IDs with your actual values from the state ledger.
783
+
784
+ ### Call 1: Create the component page
785
+
786
+ **Goal:** Create (or find) the Button page.
787
+ **State input:** None
788
+ **State output:** `{ pageId }`
789
+
790
+ ```javascript
791
+ (async () => {
792
+ try {
793
+ let page = figma.root.children.find(p => p.name === 'Button');
794
+ if (!page) { page = figma.createPage(); page.name = 'Button'; }
795
+ page.setPluginData('dsb_run_id', 'ds-build-2024-001');
796
+ page.setPluginData('dsb_key', 'page/button');
797
+ figma.closePlugin(JSON.stringify({ pageId: page.id }));
798
+ } catch (e) { figma.closePluginWithFailure(e.toString()); }
799
+ })();
800
+ ```
801
+
802
+ ### Call 2: Create documentation frame
803
+
804
+ **Goal:** Add title + description frame.
805
+ **State input:** `{ pageId }`
806
+ **State output:** `{ docFrameId }`
807
+
808
+ ```javascript
809
+ (async () => {
810
+ try {
811
+ const PAGE_ID = 'PAGE_ID_FROM_STATE';
812
+ const page = await figma.getNodeByIdAsync(PAGE_ID);
813
+ await figma.setCurrentPageAsync(page);
814
+
815
+ // Idempotency check
816
+ const existing = page.findAll(n => n.getPluginData('dsb_key') === 'doc/button');
817
+ if (existing.length > 0) {
818
+ figma.closePlugin(JSON.stringify({ docFrameId: existing[0].id }));
819
+ return;
820
+ }
821
+
822
+ await figma.loadFontAsync({ family: 'Inter', style: 'Bold' });
823
+ await figma.loadFontAsync({ family: 'Inter', style: 'Regular' });
824
+
825
+ const docFrame = figma.createFrame();
826
+ docFrame.name = 'Button / Documentation';
827
+ docFrame.x = 40; docFrame.y = 40;
828
+ docFrame.layoutMode = 'VERTICAL';
829
+ docFrame.primaryAxisSizingMode = 'AUTO';
830
+ docFrame.counterAxisSizingMode = 'FIXED';
831
+ docFrame.resize(560, 100);
832
+ docFrame.paddingTop = 40; docFrame.paddingBottom = 40;
833
+ docFrame.paddingLeft = 40; docFrame.paddingRight = 40;
834
+ docFrame.itemSpacing = 16;
835
+ docFrame.fills = [{ type: 'SOLID', color: { r: 1, g: 1, b: 1 } }];
836
+
837
+ const title = figma.createText();
838
+ title.fontName = { family: 'Inter', style: 'Bold' };
839
+ title.fontSize = 32;
840
+ title.characters = 'Button';
841
+ docFrame.appendChild(title);
842
+
843
+ const desc = figma.createText();
844
+ desc.fontName = { family: 'Inter', style: 'Regular' };
845
+ desc.fontSize = 14;
846
+ desc.characters = 'Buttons allow users to take actions with a single tap. Use Primary for the highest-emphasis action on a page, Secondary for supporting actions.';
847
+ desc.layoutSizingHorizontal = 'FILL';
848
+ docFrame.appendChild(desc);
849
+
850
+ docFrame.setPluginData('dsb_run_id', 'ds-build-2024-001');
851
+ docFrame.setPluginData('dsb_key', 'doc/button');
852
+
853
+ figma.closePlugin(JSON.stringify({ docFrameId: docFrame.id }));
854
+ } catch (e) { figma.closePluginWithFailure(e.toString()); }
855
+ })();
856
+ ```
857
+
858
+ ### Call 3: Create base component
859
+
860
+ **Goal:** Create the base component with auto-layout and all variable bindings.
861
+ **State input:** `{ pageId }` + variable IDs from Phase 1
862
+ **State output:** `{ baseCompId }`
863
+
864
+ *(See Section 3 for full code — substituting the actual variable IDs from the state ledger.)*
865
+
866
+ ### Call 4: Create all variants
867
+
868
+ **Goal:** Clone base and produce all 18 variants (3 Size × 2 Style × 3 State).
869
+ **State input:** `{ pageId, baseCompId }` + variable IDs
870
+ **State output:** `{ variantIds: ['id1', 'id2', ..., 'id18'] }`
871
+
872
+ ```javascript
873
+ (async () => {
874
+ try {
875
+ const RUN_ID = 'ds-build-2024-001';
876
+ const BASE_ID = 'BASE_COMP_ID_FROM_STATE';
877
+ const PAGE_ID = 'PAGE_ID_FROM_STATE';
878
+ // Variable IDs from state ledger:
879
+ const VAR = {
880
+ bg_primary: 'VAR_ID_1',
881
+ text_primary: 'VAR_ID_2',
882
+ bg_secondary: 'VAR_ID_3',
883
+ text_secondary: 'VAR_ID_4',
884
+ bg_disabled: 'VAR_ID_5',
885
+ text_disabled: 'VAR_ID_6',
886
+ padding_sm: 'VAR_ID_7',
887
+ padding_md: 'VAR_ID_8',
888
+ padding_lg: 'VAR_ID_9',
889
+ };
890
+
891
+ const page = await figma.getNodeByIdAsync(PAGE_ID);
892
+ await figma.setCurrentPageAsync(page);
893
+
894
+ const base = await figma.getNodeByIdAsync(BASE_ID);
895
+
896
+ // Load all variables
897
+ const vars = {};
898
+ for (const [k, v] of Object.entries(VAR)) {
899
+ vars[k] = await figma.variables.getVariableByIdAsync(v);
900
+ }
901
+
902
+ const axes = {
903
+ Size: ['Small', 'Medium', 'Large'],
904
+ Style: ['Primary', 'Secondary'],
905
+ State: ['Default', 'Hover', 'Disabled'],
906
+ };
907
+ const paddingMap = { Small: vars.padding_sm, Medium: vars.padding_md, Large: vars.padding_lg };
908
+
909
+ const components = [];
910
+ for (const size of axes.Size) {
911
+ for (const style of axes.Style) {
912
+ for (const state of axes.State) {
913
+ const clone = base.clone();
914
+ clone.name = `Size=${size}, Style=${style}, State=${state}`;
915
+
916
+ clone.setBoundVariable('paddingTop', paddingMap[size]);
917
+ clone.setBoundVariable('paddingBottom', paddingMap[size]);
918
+ clone.setBoundVariable('paddingLeft', paddingMap[size]);
919
+ clone.setBoundVariable('paddingRight', paddingMap[size]);
920
+
921
+ const isDisabled = state === 'Disabled';
922
+ const bgV = isDisabled ? vars.bg_disabled : (style === 'Primary' ? vars.bg_primary : vars.bg_secondary);
923
+ const txV = isDisabled ? vars.text_disabled : (style === 'Primary' ? vars.text_primary : vars.text_secondary);
924
+
925
+ clone.fills = [figma.variables.setBoundVariableForPaint(
926
+ { type: 'SOLID', color: { r: 0, g: 0, b: 0 } }, 'color', bgV
927
+ )];
928
+
929
+ const labelNode = clone.findOne(n => n.name === 'label');
930
+ labelNode.fills = [figma.variables.setBoundVariableForPaint(
931
+ { type: 'SOLID', color: { r: 1, g: 1, b: 1 } }, 'color', txV
932
+ )];
933
+
934
+ clone.setPluginData('dsb_run_id', RUN_ID);
935
+ clone.setPluginData('dsb_key', `component/button/variant/${size}/${style}/${state}`);
936
+ components.push(clone);
937
+ }
938
+ }
939
+ }
940
+
941
+ figma.closePlugin(JSON.stringify({ variantIds: components.map(c => c.id) }));
942
+ } catch (e) { figma.closePluginWithFailure(e.toString()); }
943
+ })();
944
+ ```
945
+
946
+ ### Call 5: combineAsVariants + grid layout
947
+
948
+ **Goal:** Combine all 18 variants into a ComponentSet and lay them out in a grid.
949
+ **State input:** `{ pageId, variantIds }` (18 IDs)
950
+ **State output:** `{ componentSetId }`
951
+
952
+ *(See Section 5 for full code.)*
953
+
954
+ ### Call 6: Add component properties
955
+
956
+ **Goal:** Add TEXT, BOOLEAN, INSTANCE_SWAP properties and wire them to child nodes.
957
+ **State input:** `{ pageId, componentSetId }`
958
+ **State output:** `{ componentSetId, properties: { labelKey, showIconKey, iconKey } }`
959
+
960
+ ```javascript
961
+ (async () => {
962
+ try {
963
+ const CS_ID = 'CS_ID_FROM_STATE';
964
+ const DEFAULT_ICON_ID = 'ICON_COMP_ID_FROM_STATE';
965
+ const page = figma.root.children.find(p => p.name === 'Button');
966
+ await figma.setCurrentPageAsync(page);
967
+
968
+ const cs = await figma.getNodeByIdAsync(CS_ID);
969
+ cs.description = 'Buttons allow users to take actions and make choices with a single tap.';
970
+ cs.documentationLinks = [{ uri: 'https://your-storybook.com/button' }];
971
+
972
+ // Add properties — save returned keys
973
+ const labelKey = cs.addComponentProperty('Label', 'TEXT', 'Button');
974
+ const showIconKey = cs.addComponentProperty('Show Icon', 'BOOLEAN', true);
975
+ const iconKey = cs.addComponentProperty('Icon', 'INSTANCE_SWAP', DEFAULT_ICON_ID);
976
+
977
+ // Wire to children
978
+ for (const child of cs.children) {
979
+ const labelNode = child.findOne(n => n.name === 'label');
980
+ if (labelNode) labelNode.componentPropertyReferences = { characters: labelKey };
981
+
982
+ const iconNode = child.findOne(n => n.name === 'icon');
983
+ if (iconNode) {
984
+ iconNode.componentPropertyReferences = {
985
+ visible: showIconKey,
986
+ ...(iconNode.type === 'INSTANCE' ? { mainComponent: iconKey } : {}),
987
+ };
988
+ }
989
+ }
990
+
991
+ figma.closePlugin(JSON.stringify({
992
+ componentSetId: cs.id,
993
+ properties: { labelKey, showIconKey, iconKey },
994
+ }));
995
+ } catch (e) { figma.closePluginWithFailure(e.toString()); }
996
+ })();
997
+ ```
998
+
999
+ ### Call 7: Validate with get_metadata
1000
+
1001
+ **Goal:** Structural check — variant count, properties, axes.
1002
+ **Action:** Call `get_metadata` on the ComponentSet node ID (from state). Verify in the result:
1003
+ - `children.length === 18`
1004
+ - `variantGroupProperties` has `Size`, `Style`, `State` keys with correct value arrays
1005
+ - `componentPropertyDefinitions` has `Label`, `Show Icon`, `Icon` entries
1006
+
1007
+ ### Call 8: Validate with get_screenshot
1008
+
1009
+ **Goal:** Visual check — layout, colors, text.
1010
+ **Action:** Call `get_screenshot` on the Button page. Inspect the screenshot. If variants are stacked, re-run Call 5. If colors look wrong, inspect variable bindings.
1011
+
1012
+ ### Checkpoint
1013
+
1014
+ After Call 8: show the screenshot to the user. Ask: "Here's the Button component with 18 variants. Does this look correct?" Do not proceed to the next component until the user approves.