@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,110 @@
1
+ /**
2
+ * bindVariablesToComponent
3
+ *
4
+ * Binds design token variables to the visual properties of a component node.
5
+ * Supports fills, strokes, all padding directions, item spacing, and corner radius.
6
+ * Only binds properties for which a variable ID is provided in `bindings`.
7
+ *
8
+ * This function should be called on each variant individually within a component
9
+ * set, OR on the component set itself for properties shared by all variants.
10
+ *
11
+ * @param {ComponentNode | FrameNode | RectangleNode} component
12
+ * The Figma node to mutate. Usually a ComponentNode or one of its children.
13
+ * @param {{
14
+ * fills?: string,
15
+ * strokes?: string,
16
+ * paddingTop?: string,
17
+ * paddingBottom?: string,
18
+ * paddingLeft?: string,
19
+ * paddingRight?: string,
20
+ * itemSpacing?: string,
21
+ * cornerRadius?: string
22
+ * }} bindings
23
+ * Each key is a visual property name; each value is a Figma Variable ID
24
+ * (e.g. "VariableID:123:456"). Omit a key to skip binding that property.
25
+ * @returns {{ mutatedNodeIds: string[] }}
26
+ * List of node IDs that were mutated (for audit/validation purposes).
27
+ */
28
+ function bindVariablesToComponent(component, bindings) {
29
+ const mutatedNodeIds = []
30
+
31
+ if (!component) {
32
+ return { mutatedNodeIds }
33
+ }
34
+
35
+ // --- Fills ---
36
+ if (bindings.fills) {
37
+ const fillVar = figma.variables.getVariableById(bindings.fills)
38
+ if (fillVar) {
39
+ const existingFills = component.fills
40
+ if (Array.isArray(existingFills) && existingFills.length > 0) {
41
+ // Bind the color of the first fill to the variable
42
+ const boundFill = figma.variables.setBoundVariableForPaint(
43
+ existingFills[0],
44
+ 'color',
45
+ fillVar,
46
+ )
47
+ component.fills = [boundFill, ...existingFills.slice(1)]
48
+ } else {
49
+ // No existing fill — create a solid fill bound to the variable
50
+ const boundFill = figma.variables.setBoundVariableForPaint(
51
+ { type: 'SOLID', color: { r: 0.5, g: 0.5, b: 0.5 } },
52
+ 'color',
53
+ fillVar,
54
+ )
55
+ component.fills = [boundFill]
56
+ }
57
+ mutatedNodeIds.push(component.id)
58
+ }
59
+ }
60
+
61
+ // --- Strokes ---
62
+ if (bindings.strokes) {
63
+ const strokeVar = figma.variables.getVariableById(bindings.strokes)
64
+ if (strokeVar) {
65
+ const existingStrokes = component.strokes
66
+ if (Array.isArray(existingStrokes) && existingStrokes.length > 0) {
67
+ const boundStroke = figma.variables.setBoundVariableForPaint(
68
+ existingStrokes[0],
69
+ 'color',
70
+ strokeVar,
71
+ )
72
+ component.strokes = [boundStroke, ...existingStrokes.slice(1)]
73
+ } else {
74
+ const boundStroke = figma.variables.setBoundVariableForPaint(
75
+ { type: 'SOLID', color: { r: 0.5, g: 0.5, b: 0.5 } },
76
+ 'color',
77
+ strokeVar,
78
+ )
79
+ component.strokes = [boundStroke]
80
+ }
81
+ if (!mutatedNodeIds.includes(component.id)) {
82
+ mutatedNodeIds.push(component.id)
83
+ }
84
+ }
85
+ }
86
+
87
+ // --- Spacing properties (FLOAT variables bound via setBoundVariable) ---
88
+ const floatBindings = [
89
+ ['paddingTop', 'paddingTop'],
90
+ ['paddingBottom', 'paddingBottom'],
91
+ ['paddingLeft', 'paddingLeft'],
92
+ ['paddingRight', 'paddingRight'],
93
+ ['itemSpacing', 'itemSpacing'],
94
+ ['cornerRadius', 'cornerRadius'],
95
+ ]
96
+
97
+ for (const [bindingKey, figmaProp] of floatBindings) {
98
+ if (bindings[bindingKey]) {
99
+ const variable = figma.variables.getVariableById(bindings[bindingKey])
100
+ if (variable) {
101
+ component.setBoundVariable(figmaProp, variable)
102
+ if (!mutatedNodeIds.includes(component.id)) {
103
+ mutatedNodeIds.push(component.id)
104
+ }
105
+ }
106
+ }
107
+ }
108
+
109
+ return { mutatedNodeIds }
110
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * cleanupOrphans
3
+ *
4
+ * Finds and removes all Figma nodes (pages, frames, components, variables,
5
+ * and variable collections) that were tagged with the given `dsb_run_id`
6
+ * by a previous build run. This is safe cleanup: it uses plugin data tags,
7
+ * never name-prefix matching, so it cannot accidentally delete user-owned nodes.
8
+ *
9
+ * Use this when a build run fails mid-way and you need to reset to a clean
10
+ * slate before retrying. The function traverses the entire document looking
11
+ * for `dsb_run_id` plugin data matching `runId`.
12
+ *
13
+ * Variables and variable collections are handled separately (they are not
14
+ * scene nodes and cannot be discovered via node traversal).
15
+ *
16
+ * @param {string} runId - The dsb_run_id value to match (e.g. "ds-build-2024-001").
17
+ * @returns {Promise<{
18
+ * removedCount: number,
19
+ * removedIds: string[]
20
+ * }>}
21
+ */
22
+ async function cleanupOrphans(runId) {
23
+ if (!runId) {
24
+ throw new Error('cleanupOrphans: runId is required.')
25
+ }
26
+
27
+ const removedIds = []
28
+ const originalPage = figma.currentPage
29
+
30
+ // --- Remove tagged scene nodes (pages, frames, components, etc.) ---
31
+ // Collect pages to remove (can't remove during iteration)
32
+ const pagesToRemove = []
33
+
34
+ for (const page of figma.root.children) {
35
+ if (page.getPluginData('dsb_run_id') === runId) {
36
+ pagesToRemove.push(page)
37
+ continue
38
+ }
39
+
40
+ // Traverse all nodes on this page
41
+ await figma.setCurrentPageAsync(page)
42
+
43
+ const nodesToRemove = []
44
+ page.findAll((node) => {
45
+ if (node.getPluginData('dsb_run_id') === runId) {
46
+ nodesToRemove.push(node)
47
+ return false // Don't descend — removing the parent removes its children
48
+ }
49
+ return true
50
+ })
51
+
52
+ // Remove deepest nodes first (children before parents) to avoid
53
+ // "parent no longer exists" errors
54
+ const sorted = nodesToRemove.sort((a, b) => {
55
+ // Sort by depth descending: deeper nodes first
56
+ return getDepth(b) - getDepth(a)
57
+ })
58
+
59
+ for (const node of sorted) {
60
+ if (node && node.parent) {
61
+ removedIds.push(node.id)
62
+ node.remove()
63
+ }
64
+ }
65
+ }
66
+
67
+ // Remove tagged pages last
68
+ for (const page of pagesToRemove) {
69
+ // Cannot remove the last page in the document
70
+ if (figma.root.children.length <= 1) {
71
+ break
72
+ }
73
+ removedIds.push(page.id)
74
+ page.remove()
75
+ }
76
+
77
+ // --- Remove tagged variables ---
78
+ const allVariables = figma.variables.getLocalVariables()
79
+ for (const variable of allVariables) {
80
+ if (variable.getPluginData('dsb_run_id') === runId) {
81
+ removedIds.push(variable.id)
82
+ variable.remove()
83
+ }
84
+ }
85
+
86
+ // --- Remove tagged variable collections ---
87
+ // Must be done after variables are removed
88
+ const allCollections = figma.variables.getLocalVariableCollections()
89
+ for (const collection of allCollections) {
90
+ if (collection.getPluginData('dsb_run_id') === runId) {
91
+ removedIds.push(collection.id)
92
+ collection.remove()
93
+ }
94
+ }
95
+
96
+ // Restore original page (if it still exists)
97
+ try {
98
+ await figma.setCurrentPageAsync(originalPage)
99
+ } catch (_) {
100
+ // Original page was removed — switch to first available page
101
+ if (figma.root.children.length > 0) {
102
+ await figma.setCurrentPageAsync(figma.root.children[0])
103
+ }
104
+ }
105
+
106
+ return {
107
+ removedCount: removedIds.length,
108
+ removedIds,
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Returns the depth of a node in the document tree.
114
+ * Root children (pages) have depth 1; their children have depth 2; etc.
115
+ *
116
+ * @param {BaseNode} node
117
+ * @returns {number}
118
+ */
119
+ function getDepth(node) {
120
+ let depth = 0
121
+ let current = node
122
+ while (current.parent) {
123
+ depth++
124
+ current = current.parent
125
+ }
126
+ return depth
127
+ }
@@ -0,0 +1,148 @@
1
+ /**
2
+ * createComponentWithVariants
3
+ *
4
+ * Creates a component set by generating all combinations of `variantAxes`,
5
+ * building one Figma component per combination, then calling
6
+ * `figma.combineAsVariants` to produce the component set. After combining,
7
+ * the variants are repositioned into a grid so they don't all stack at (0, 0).
8
+ *
9
+ * @param {{
10
+ * name: string,
11
+ * variantAxes: Record<string, string[]>,
12
+ * baseProps: {
13
+ * width: number,
14
+ * height: number,
15
+ * fills?: Paint[],
16
+ * padding?: {top?: number, bottom?: number, left?: number, right?: number},
17
+ * radius?: number,
18
+ * layoutMode?: 'HORIZONTAL' | 'VERTICAL' | 'NONE',
19
+ * itemSpacing?: number
20
+ * },
21
+ * page: PageNode
22
+ * }} config
23
+ * - `name`: Component set name (e.g. "Button").
24
+ * - `variantAxes`: Each key is a variant property name; each value is an array of
25
+ * allowed values. All combinations are generated (Cartesian product).
26
+ * Example: { Size: ['Small', 'Medium', 'Large'], Style: ['Primary', 'Ghost'] }
27
+ * produces 6 variants.
28
+ * - `baseProps`: Visual properties applied to every variant.
29
+ * - `page`: The PageNode to create components on (must be set as current page by caller).
30
+ * @param {string} [runId] - Optional dsb_run_id to tag every node.
31
+ * @returns {Promise<{
32
+ * componentSet: ComponentSetNode,
33
+ * variants: ComponentNode[]
34
+ * }>}
35
+ */
36
+ async function createComponentWithVariants(config, runId) {
37
+ const { name, variantAxes, baseProps, page } = config
38
+
39
+ // Ensure we are on the correct page
40
+ await figma.setCurrentPageAsync(page)
41
+
42
+ // Compute Cartesian product of variant axes
43
+ const axisNames = Object.keys(variantAxes)
44
+ const axisValues = axisNames.map((k) => variantAxes[k])
45
+ const combinations = cartesianProduct(axisValues)
46
+
47
+ // Build one component per combination
48
+ const components = []
49
+ for (const combo of combinations) {
50
+ const comp = figma.createComponent()
51
+
52
+ // Name: "Property=Value, Property=Value, ..."
53
+ comp.name = axisNames.map((ax, i) => `${ax}=${combo[i]}`).join(', ')
54
+
55
+ // Base geometry
56
+ comp.resize(baseProps.width, baseProps.height)
57
+
58
+ // Fills
59
+ if (baseProps.fills !== undefined) {
60
+ comp.fills = baseProps.fills
61
+ } else {
62
+ comp.fills = [{ type: 'SOLID', color: { r: 0.9, g: 0.9, b: 0.9 } }]
63
+ }
64
+
65
+ // Corner radius
66
+ if (baseProps.radius !== undefined) {
67
+ comp.cornerRadius = baseProps.radius
68
+ }
69
+
70
+ // Auto-layout
71
+ if (baseProps.layoutMode && baseProps.layoutMode !== 'NONE') {
72
+ comp.layoutMode = baseProps.layoutMode
73
+ comp.primaryAxisAlignItems = 'CENTER'
74
+ comp.counterAxisAlignItems = 'CENTER'
75
+ if (baseProps.itemSpacing !== undefined) {
76
+ comp.itemSpacing = baseProps.itemSpacing
77
+ }
78
+ }
79
+
80
+ // Padding
81
+ if (baseProps.padding) {
82
+ comp.paddingTop = baseProps.padding.top ?? 0
83
+ comp.paddingBottom = baseProps.padding.bottom ?? 0
84
+ comp.paddingLeft = baseProps.padding.left ?? 0
85
+ comp.paddingRight = baseProps.padding.right ?? 0
86
+ }
87
+
88
+ // Plugin data
89
+ const variantKey = axisNames.map((ax, i) => `${ax}:${combo[i]}`).join('|')
90
+ comp.setPluginData('dsb_key', `component/${name}/${variantKey}`)
91
+ if (runId) {
92
+ comp.setPluginData('dsb_run_id', runId)
93
+ }
94
+
95
+ page.appendChild(comp)
96
+ components.push(comp)
97
+ }
98
+
99
+ // Combine into a component set
100
+ const componentSet = figma.combineAsVariants(components, page)
101
+ componentSet.name = name
102
+ componentSet.setPluginData('dsb_key', `componentSet/${name}`)
103
+ if (runId) {
104
+ componentSet.setPluginData('dsb_run_id', runId)
105
+ }
106
+
107
+ // Grid layout — variants stack at (0, 0) after combineAsVariants; reposition them.
108
+ const GRID_GAP = 16
109
+ const cols = Math.max(1, axisValues[axisValues.length - 1]?.length ?? 1)
110
+ const variantWidth = baseProps.width
111
+ const variantHeight = baseProps.height
112
+
113
+ componentSet.children.forEach((variant, idx) => {
114
+ const col = idx % cols
115
+ const row = Math.floor(idx / cols)
116
+ variant.x = col * (variantWidth + GRID_GAP)
117
+ variant.y = row * (variantHeight + GRID_GAP)
118
+ })
119
+
120
+ // Resize component set to wrap its children with padding
121
+ const totalCols = Math.min(cols, combinations.length)
122
+ const totalRows = Math.ceil(combinations.length / cols)
123
+ const PADDING = 40
124
+ componentSet.resize(
125
+ totalCols * variantWidth + (totalCols - 1) * GRID_GAP + PADDING * 2,
126
+ totalRows * variantHeight + (totalRows - 1) * GRID_GAP + PADDING * 2,
127
+ )
128
+
129
+ // Position component set at a safe canvas location
130
+ componentSet.x = 480
131
+ componentSet.y = 80
132
+
133
+ return { componentSet, variants: componentSet.children }
134
+ }
135
+
136
+ /**
137
+ * Computes the Cartesian product of multiple arrays.
138
+ * cartesianProduct([[A, B], [1, 2]]) → [[A,1], [A,2], [B,1], [B,2]]
139
+ *
140
+ * @param {Array<string[]>} arrays
141
+ * @returns {string[][]}
142
+ */
143
+ function cartesianProduct(arrays) {
144
+ return arrays.reduce(
145
+ (acc, curr) => acc.flatMap((combo) => curr.map((val) => [...combo, val])),
146
+ [[]],
147
+ )
148
+ }
@@ -0,0 +1,139 @@
1
+ /**
2
+ * createDocumentationPage
3
+ *
4
+ * Creates a new Figma page with a standardized documentation layout: a page
5
+ * title, optional description, and an ordered list of sections each built by
6
+ * a caller-supplied `contentFn`. The content function receives the section
7
+ * frame and may append any nodes to it.
8
+ *
9
+ * This function is used for standalone documentation pages (e.g. a Foundations
10
+ * page, a Getting Started page, or a component page with documentation).
11
+ * It does not handle component sets — those live on separate pages created by
12
+ * createComponentWithVariants.
13
+ *
14
+ * @param {string} pageName - The Figma page name (e.g. "Foundations", "Getting Started").
15
+ * @param {{
16
+ * title: string,
17
+ * description?: string,
18
+ * sections: Array<{
19
+ * name: string,
20
+ * contentFn: (sectionFrame: FrameNode) => Promise<void>
21
+ * }>
22
+ * }} config
23
+ * - `title`: Large heading displayed at the top of the page.
24
+ * - `description`: Optional subtitle displayed below the heading.
25
+ * - `sections`: Ordered list of sections. Each section gets its own frame
26
+ * with a heading and is passed to `contentFn` for population.
27
+ * @param {string} [runId] - Optional dsb_run_id to tag every created node.
28
+ * @returns {Promise<{
29
+ * page: PageNode,
30
+ * titleNode: TextNode,
31
+ * frameIds: string[]
32
+ * }>}
33
+ * `frameIds` is an ordered list of IDs for the root frame and each section frame.
34
+ */
35
+ async function createDocumentationPage(pageName, config, runId) {
36
+ await figma.loadFontAsync({ family: 'Inter', style: 'Bold' })
37
+ await figma.loadFontAsync({ family: 'Inter', style: 'Regular' })
38
+ await figma.loadFontAsync({ family: 'Inter', style: 'Medium' })
39
+
40
+ // Create and activate the page
41
+ const page = figma.createPage()
42
+ page.name = pageName
43
+ await figma.setCurrentPageAsync(page)
44
+
45
+ if (runId) {
46
+ page.setPluginData('dsb_run_id', runId)
47
+ page.setPluginData('dsb_key', `page/${pageName}`)
48
+ }
49
+
50
+ const frameIds = []
51
+
52
+ // Root scroll container — 1440px wide, auto-height
53
+ const root = figma.createFrame()
54
+ root.name = pageName
55
+ root.layoutMode = 'VERTICAL'
56
+ root.primaryAxisAlignItems = 'MIN'
57
+ root.counterAxisAlignItems = 'MIN'
58
+ root.itemSpacing = 80
59
+ root.paddingTop = 80
60
+ root.paddingBottom = 120
61
+ root.paddingLeft = 80
62
+ root.paddingRight = 80
63
+ root.layoutSizingHorizontal = 'FIXED'
64
+ root.layoutSizingVertical = 'HUG'
65
+ root.resize(1440, 1)
66
+ root.fills = [{ type: 'SOLID', color: { r: 1, g: 1, b: 1 } }]
67
+ root.x = 0
68
+ root.y = 0
69
+ page.appendChild(root)
70
+
71
+ if (runId) {
72
+ root.setPluginData('dsb_run_id', runId)
73
+ root.setPluginData('dsb_key', `frame/root/${pageName}`)
74
+ }
75
+
76
+ frameIds.push(root.id)
77
+
78
+ // Page header: title + optional description
79
+ const header = figma.createFrame()
80
+ header.name = 'Header'
81
+ header.layoutMode = 'VERTICAL'
82
+ header.itemSpacing = 12
83
+ header.layoutSizingHorizontal = 'FILL'
84
+ header.layoutSizingVertical = 'HUG'
85
+ header.fills = []
86
+ root.appendChild(header)
87
+
88
+ const titleNode = figma.createText()
89
+ titleNode.fontName = { family: 'Inter', style: 'Bold' }
90
+ titleNode.characters = config.title
91
+ titleNode.fontSize = 40
92
+ titleNode.fills = [{ type: 'SOLID', color: { r: 0.07, g: 0.07, b: 0.07 } }]
93
+ titleNode.layoutSizingHorizontal = 'FILL'
94
+ header.appendChild(titleNode)
95
+
96
+ if (config.description) {
97
+ const descNode = figma.createText()
98
+ descNode.fontName = { family: 'Inter', style: 'Regular' }
99
+ descNode.characters = config.description
100
+ descNode.fontSize = 16
101
+ descNode.lineHeight = { value: 24, unit: 'PIXELS' }
102
+ descNode.fills = [{ type: 'SOLID', color: { r: 0.4, g: 0.4, b: 0.4 } }]
103
+ descNode.layoutSizingHorizontal = 'FILL'
104
+ header.appendChild(descNode)
105
+ }
106
+
107
+ // Sections
108
+ for (const section of config.sections) {
109
+ const sectionFrame = figma.createFrame()
110
+ sectionFrame.name = `Section/${section.name}`
111
+ sectionFrame.layoutMode = 'VERTICAL'
112
+ sectionFrame.itemSpacing = 20
113
+ sectionFrame.layoutSizingHorizontal = 'FILL'
114
+ sectionFrame.layoutSizingVertical = 'HUG'
115
+ sectionFrame.fills = []
116
+ root.appendChild(sectionFrame)
117
+
118
+ if (runId) {
119
+ sectionFrame.setPluginData('dsb_run_id', runId)
120
+ sectionFrame.setPluginData('dsb_key', `frame/section/${pageName}/${section.name}`)
121
+ }
122
+
123
+ // Section heading
124
+ const sectionHeading = figma.createText()
125
+ sectionHeading.fontName = { family: 'Inter', style: 'Bold' }
126
+ sectionHeading.characters = section.name
127
+ sectionHeading.fontSize = 24
128
+ sectionHeading.fills = [{ type: 'SOLID', color: { r: 0.07, g: 0.07, b: 0.07 } }]
129
+ sectionHeading.layoutSizingHorizontal = 'FILL'
130
+ sectionFrame.appendChild(sectionHeading)
131
+
132
+ // Invoke the caller's content function to populate the section
133
+ await section.contentFn(sectionFrame)
134
+
135
+ frameIds.push(sectionFrame.id)
136
+ }
137
+
138
+ return { page, titleNode, frameIds }
139
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * createSemanticTokens
3
+ *
4
+ * Creates a batch of Figma variables in the given collection, one per entry in
5
+ * `tokenMap`. Supports raw values, variable alias references, code syntax, and
6
+ * scopes. Returns a map of token name → Variable for use in subsequent steps.
7
+ *
8
+ * @param {VariableCollection} collection - The target variable collection.
9
+ * @param {Record<string, string>} modeIds - Map of {modeName: modeId} from createVariableCollection.
10
+ * @param {Array<{
11
+ * name: string,
12
+ * type: 'COLOR' | 'FLOAT' | 'STRING' | 'BOOLEAN',
13
+ * values: Record<string, string | number | boolean | {type: 'VARIABLE_ALIAS', id: string}>,
14
+ * scopes?: VariableScope[],
15
+ * codeSyntax?: {WEB?: string, ANDROID?: string, iOS?: string}
16
+ * }>} tokenMap - Ordered list of token definitions.
17
+ * - `name`: Variable name using slash hierarchy (e.g. "color/bg/primary").
18
+ * - `type`: Figma variable type.
19
+ * - `values`: Map of {modeName: value}. Values can be raw (hex string for COLOR,
20
+ * number for FLOAT) or alias objects {type: 'VARIABLE_ALIAS', id: variableId}.
21
+ * For COLOR, raw values are accepted as hex strings ("#rrggbb" or "#rrggbbaa")
22
+ * and converted to {r, g, b, a} automatically.
23
+ * - `scopes`: Array of VariableScope strings. Omit to use [] (hidden/primitive).
24
+ * - `codeSyntax`: Platform code syntax strings. Omit to skip.
25
+ * @param {string} [runId] - Optional dsb_run_id to tag every variable.
26
+ * @returns {Promise<{variables: Record<string, Variable>}>}
27
+ * `variables` maps each token name to its created Variable object.
28
+ */
29
+ async function createSemanticTokens(collection, modeIds, tokenMap, runId) {
30
+ const variables = {}
31
+
32
+ for (const token of tokenMap) {
33
+ // Create the variable
34
+ const variable = figma.variables.createVariable(token.name, collection, token.type)
35
+
36
+ // Tag for cleanup
37
+ variable.setPluginData('dsb_key', `variable/${token.name}`)
38
+ if (runId) {
39
+ variable.setPluginData('dsb_run_id', runId)
40
+ }
41
+
42
+ // Set values for each mode
43
+ for (const [modeName, rawValue] of Object.entries(token.values)) {
44
+ const modeId = modeIds[modeName]
45
+ if (!modeId) {
46
+ throw new Error(
47
+ `createSemanticTokens: mode "${modeName}" not found in modeIds for token "${token.name}". ` +
48
+ `Available modes: ${Object.keys(modeIds).join(', ')}`,
49
+ )
50
+ }
51
+
52
+ let value = rawValue
53
+
54
+ // Convert hex strings to Figma RGBA for COLOR type
55
+ if (token.type === 'COLOR' && typeof rawValue === 'string' && rawValue.startsWith('#')) {
56
+ value = hexToFigmaColor(rawValue)
57
+ }
58
+
59
+ variable.setValueForMode(modeId, value)
60
+ }
61
+
62
+ // Set scopes (default: empty array = hidden from property pickers / primitives)
63
+ variable.scopes = token.scopes || []
64
+
65
+ // Set code syntax per platform
66
+ if (token.codeSyntax) {
67
+ if (token.codeSyntax.WEB) {
68
+ variable.setVariableCodeSyntax('WEB', token.codeSyntax.WEB)
69
+ }
70
+ if (token.codeSyntax.ANDROID) {
71
+ variable.setVariableCodeSyntax('ANDROID', token.codeSyntax.ANDROID)
72
+ }
73
+ if (token.codeSyntax.iOS) {
74
+ variable.setVariableCodeSyntax('iOS', token.codeSyntax.iOS)
75
+ }
76
+ }
77
+
78
+ variables[token.name] = variable
79
+ }
80
+
81
+ return { variables }
82
+ }
83
+
84
+ /**
85
+ * Converts a hex color string to a Figma RGBA object.
86
+ * Supports "#rgb", "#rrggbb", and "#rrggbbaa".
87
+ *
88
+ * @param {string} hex
89
+ * @returns {{ r: number, g: number, b: number, a: number }}
90
+ */
91
+ function hexToFigmaColor(hex) {
92
+ let h = hex.replace('#', '')
93
+
94
+ // Expand shorthand #rgb → #rrggbb
95
+ if (h.length === 3) {
96
+ h = h
97
+ .split('')
98
+ .map((c) => c + c)
99
+ .join('')
100
+ }
101
+
102
+ const r = parseInt(h.substring(0, 2), 16) / 255
103
+ const g = parseInt(h.substring(2, 4), 16) / 255
104
+ const b = parseInt(h.substring(4, 6), 16) / 255
105
+ const a = h.length === 8 ? parseInt(h.substring(6, 8), 16) / 255 : 1
106
+
107
+ return { r, g, b, a }
108
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * createVariableCollection
3
+ *
4
+ * Creates a new Figma variable collection with the specified name and modes.
5
+ * If `modeNames` has more than one entry, the first mode is renamed from
6
+ * Figma's default "Mode 1" to the first name, and additional modes are added.
7
+ *
8
+ * Every created collection is tagged with `dsb_key` plugin data so it can be
9
+ * found and cleaned up idempotently by `cleanupOrphans`.
10
+ *
11
+ * @param {string} name - The display name of the collection (e.g. "Color", "Spacing").
12
+ * @param {string[]} modeNames - Ordered list of mode names (e.g. ["Light", "Dark"] or ["Value"]).
13
+ * @param {string} [runId] - Optional dsb_run_id to tag for cleanup.
14
+ * @returns {Promise<{
15
+ * collection: VariableCollection,
16
+ * modeIds: Record<string, string>
17
+ * }>}
18
+ * `modeIds` maps each mode name to its modeId string.
19
+ */
20
+ async function createVariableCollection(name, modeNames, runId) {
21
+ if (!modeNames || modeNames.length === 0) {
22
+ throw new Error('createVariableCollection: modeNames must have at least one entry.')
23
+ }
24
+
25
+ // Create the collection — Figma always creates it with one mode named "Mode 1".
26
+ const collection = figma.variables.createVariableCollection(name)
27
+
28
+ // Tag for idempotent cleanup
29
+ collection.setPluginData('dsb_key', `collection/${name}`)
30
+ if (runId) {
31
+ collection.setPluginData('dsb_run_id', runId)
32
+ }
33
+
34
+ // modeIds accumulator
35
+ const modeIds = {}
36
+
37
+ // Rename the default first mode
38
+ const defaultMode = collection.modes[0]
39
+ collection.renameMode(defaultMode.modeId, modeNames[0])
40
+ modeIds[modeNames[0]] = defaultMode.modeId
41
+
42
+ // Add additional modes
43
+ for (let i = 1; i < modeNames.length; i++) {
44
+ const newModeId = collection.addMode(modeNames[i])
45
+ modeIds[modeNames[i]] = newModeId
46
+ }
47
+
48
+ return { collection, modeIds }
49
+ }