@mmerterden/multi-agent-pipeline 11.2.0 → 11.3.1

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 (335) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +2 -2
  3. package/docs/FIGMA_PIPELINE.md +12 -25
  4. package/docs/architecture.md +3 -5
  5. package/docs/features.md +8 -8
  6. package/install/_dev-only-files.mjs +0 -6
  7. package/install/claude.mjs +39 -17
  8. package/install/copilot.mjs +22 -16
  9. package/install/templates/copilot-instructions.md +3 -3
  10. package/package.json +1 -1
  11. package/pipeline/commands/{multi-agent.md → multi-agent/SKILL.md} +39 -39
  12. package/pipeline/commands/multi-agent/{analysis.md → analysis/SKILL.md} +18 -18
  13. package/pipeline/commands/multi-agent/{analysis-resolve.md → analysis-resolve/SKILL.md} +4 -4
  14. package/pipeline/commands/multi-agent/{autopilot.md → autopilot/SKILL.md} +4 -4
  15. package/pipeline/commands/multi-agent/{build-optimize.md → build-optimize/SKILL.md} +1 -1
  16. package/pipeline/commands/multi-agent/{channels.md → channels/SKILL.md} +12 -12
  17. package/pipeline/commands/multi-agent/{dev.md → dev/SKILL.md} +4 -4
  18. package/pipeline/commands/multi-agent/{dev-autopilot.md → dev-autopilot/SKILL.md} +4 -4
  19. package/pipeline/commands/multi-agent/{dev-local.md → dev-local/SKILL.md} +5 -5
  20. package/pipeline/commands/multi-agent/{dev-local-autopilot.md → dev-local-autopilot/SKILL.md} +4 -4
  21. package/pipeline/commands/multi-agent/{finish.md → finish/SKILL.md} +6 -6
  22. package/pipeline/commands/multi-agent/{generate.md → generate/SKILL.md} +3 -3
  23. package/pipeline/commands/multi-agent/{help.md → help/SKILL.md} +4 -4
  24. package/pipeline/commands/multi-agent/{local.md → local/SKILL.md} +5 -5
  25. package/pipeline/commands/multi-agent/{local-autopilot.md → local-autopilot/SKILL.md} +5 -5
  26. package/pipeline/commands/multi-agent/{manual-test.md → manual-test/SKILL.md} +1 -1
  27. package/pipeline/commands/multi-agent/refactor/SKILL.md +257 -0
  28. package/pipeline/commands/multi-agent/{resume.md → resume/SKILL.md} +1 -1
  29. package/pipeline/commands/multi-agent/{review.md → review/SKILL.md} +5 -5
  30. package/pipeline/commands/multi-agent/{setup.md → setup/SKILL.md} +1 -1
  31. package/pipeline/commands/multi-agent/{sync.md → sync/SKILL.md} +20 -58
  32. package/pipeline/lib/credential-store.sh +8 -4
  33. package/pipeline/{commands/multi-agent → multi-agent-refs}/_account-picker.md +2 -2
  34. package/pipeline/{commands/multi-agent → multi-agent-refs}/_dev-context.md +2 -2
  35. package/pipeline/{commands/multi-agent → multi-agent-refs}/_repo-picker.md +2 -2
  36. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/android-guide.md +1 -1
  37. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/jira.md +1 -1
  38. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/wiki.md +7 -7
  39. package/pipeline/multi-agent-refs/component-dispatch.md +92 -0
  40. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/cross-cli-contract.md +10 -77
  41. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/verify-by-test.md +1 -1
  42. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/generate-issue.md +2 -2
  43. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/issue-jira-triad.md +1 -1
  44. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/keychain.md +1 -1
  45. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/log-format.md +1 -1
  46. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/modes.md +2 -2
  47. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/operations.md +1 -1
  48. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-0-init.md +10 -10
  49. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-1-analysis.md +6 -6
  50. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-2-planning.md +3 -3
  51. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-3-dev.md +10 -10
  52. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-4-review.md +8 -8
  53. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-5-test.md +3 -3
  54. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-6-commit.md +4 -4
  55. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-7-report.md +6 -6
  56. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases.md +12 -12
  57. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/progress-contract.md +4 -4
  58. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/swiftui-guide.md +1 -1
  59. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/tracker-contract.md +3 -3
  60. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/wiki-capture.md +4 -4
  61. package/pipeline/preferences-template.json +17 -1
  62. package/pipeline/rules/figma-pipeline.md +2 -2
  63. package/pipeline/schemas/prefs.schema.json +0 -34
  64. package/pipeline/scripts/README.md +1 -3
  65. package/pipeline/scripts/build-skills-index.mjs +3 -6
  66. package/pipeline/scripts/eval-mine-corpus.mjs +187 -0
  67. package/pipeline/scripts/fixtures/install-layout.tsv +6 -5
  68. package/pipeline/scripts/gen-mode-dispatch.mjs +3 -3
  69. package/pipeline/scripts/lint-skills.mjs +26 -1
  70. package/pipeline/scripts/phase-banner.sh +2 -2
  71. package/pipeline/scripts/review-scope.mjs +94 -0
  72. package/pipeline/scripts/scan-agent-config.sh +2 -2
  73. package/pipeline/scripts/smoke-agent-log-cost.sh +1 -1
  74. package/pipeline/scripts/smoke-agent-model-routing.sh +4 -4
  75. package/pipeline/scripts/smoke-autopilot-circuit-breaker.sh +2 -2
  76. package/pipeline/scripts/smoke-channels-approval-gate.sh +2 -2
  77. package/pipeline/scripts/smoke-channels-flow.sh +5 -5
  78. package/pipeline/scripts/smoke-clarify.sh +1 -1
  79. package/pipeline/scripts/smoke-commands-skills-parity.sh +3 -3
  80. package/pipeline/scripts/smoke-community-gates.sh +1 -1
  81. package/pipeline/scripts/smoke-compliance-skills.sh +2 -2
  82. package/pipeline/scripts/smoke-cost-summary.sh +1 -1
  83. package/pipeline/scripts/smoke-cross-cli-behavior.sh +17 -43
  84. package/pipeline/scripts/smoke-cross-phase-cohesion.sh +1 -1
  85. package/pipeline/scripts/smoke-dev-critic.sh +1 -1
  86. package/pipeline/scripts/smoke-diff-explain.sh +1 -1
  87. package/pipeline/scripts/smoke-diff-risk.sh +1 -1
  88. package/pipeline/scripts/smoke-evidence-gate.sh +1 -1
  89. package/pipeline/scripts/smoke-figma-dispatch.sh +46 -57
  90. package/pipeline/scripts/smoke-gate-hooks.sh +1 -1
  91. package/pipeline/scripts/smoke-generate-issue.sh +8 -8
  92. package/pipeline/scripts/smoke-handoff-contract.sh +3 -3
  93. package/pipeline/scripts/smoke-identity-isolation.sh +2 -2
  94. package/pipeline/scripts/smoke-intent-guard.sh +1 -1
  95. package/pipeline/scripts/smoke-issue-comment-template.sh +2 -2
  96. package/pipeline/scripts/smoke-issue-jira-triad.sh +4 -4
  97. package/pipeline/scripts/smoke-language-axis.sh +6 -6
  98. package/pipeline/scripts/smoke-learnings-ledger.sh +3 -3
  99. package/pipeline/scripts/smoke-mode-dispatch-drift.sh +16 -16
  100. package/pipeline/scripts/smoke-model-fallback.sh +3 -3
  101. package/pipeline/scripts/smoke-multi-repo-integration.sh +2 -2
  102. package/pipeline/scripts/smoke-multi-repo-worktree.sh +1 -1
  103. package/pipeline/scripts/smoke-no-token-prompt.sh +5 -5
  104. package/pipeline/scripts/smoke-phase-0-multi-repo.sh +1 -1
  105. package/pipeline/scripts/smoke-phase-6-multi.sh +1 -1
  106. package/pipeline/scripts/smoke-phase4-gates.sh +45 -0
  107. package/pipeline/scripts/smoke-plan-approval-gate.sh +5 -5
  108. package/pipeline/scripts/smoke-plan-todos.sh +3 -3
  109. package/pipeline/scripts/smoke-pr-review-actions.sh +3 -3
  110. package/pipeline/scripts/smoke-prefs-language.sh +1 -1
  111. package/pipeline/scripts/smoke-progress-contract.sh +2 -2
  112. package/pipeline/scripts/smoke-push-retry.sh +1 -1
  113. package/pipeline/scripts/smoke-repo-map.sh +2 -2
  114. package/pipeline/scripts/smoke-shadow-git.sh +1 -1
  115. package/pipeline/scripts/smoke-skill-authoring.sh +4 -9
  116. package/pipeline/scripts/smoke-tasklist-ordering.sh +10 -10
  117. package/pipeline/scripts/smoke-test-gap.sh +1 -1
  118. package/pipeline/scripts/smoke-token-budget.sh +1 -1
  119. package/pipeline/scripts/smoke-token-preflight.sh +2 -2
  120. package/pipeline/scripts/smoke-tracker-contract.sh +13 -13
  121. package/pipeline/scripts/smoke-tracker-tokens-invocation.sh +5 -5
  122. package/pipeline/scripts/smoke-triage-memory.sh +2 -2
  123. package/pipeline/scripts/smoke-update-check.sh +1 -1
  124. package/pipeline/scripts/smoke-url-enrichment.sh +2 -2
  125. package/pipeline/scripts/smoke-validator-gates.sh +1 -1
  126. package/pipeline/scripts/smoke-verify-by-test.sh +3 -3
  127. package/pipeline/scripts/smoke-wiki-integration.sh +9 -33
  128. package/pipeline/scripts/smoke-work-summary.sh +1 -1
  129. package/pipeline/scripts/smoke-worktree-path-convention.sh +4 -4
  130. package/pipeline/scripts/test-integrity-gate.mjs +83 -0
  131. package/pipeline/scripts/uninstall.mjs +17 -6
  132. package/pipeline/skills/.skill-manifest.json +40 -160
  133. package/pipeline/skills/.skills-index.json +3 -373
  134. package/pipeline/skills/shared/README.md +1 -1
  135. package/pipeline/skills/shared/core/multi-agent/SKILL.md +1 -1
  136. package/pipeline/skills/shared/core/multi-agent-analysis/SKILL.md +2 -2
  137. package/pipeline/skills/shared/core/multi-agent-analysis-resolve/SKILL.md +3 -3
  138. package/pipeline/skills/shared/core/multi-agent-build-optimize/SKILL.md +1 -1
  139. package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +1 -1
  140. package/pipeline/skills/shared/core/multi-agent-finish/SKILL.md +1 -1
  141. package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +1 -1
  142. package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +116 -51
  143. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +8 -30
  144. package/pipeline/skills/shared/external/compose-components/SKILL.md +14 -14
  145. package/pipeline/skills/skills-index.md +3 -44
  146. package/pipeline/commands/multi-agent/refactor.md +0 -188
  147. package/pipeline/commands/multi-agent/refs/component-dispatch.md +0 -92
  148. package/pipeline/scripts/smoke-existing-discovery-gate.sh +0 -71
  149. package/pipeline/scripts/smoke-figma-android-parity.sh +0 -148
  150. package/pipeline/scripts/smoke-figma-credential-store.sh +0 -105
  151. package/pipeline/scripts/smoke-figma-cross-cli-inventory.sh +0 -177
  152. package/pipeline/scripts/smoke-figma-sync.sh +0 -149
  153. package/pipeline/scripts/smoke-sync-delegation.sh +0 -74
  154. package/pipeline/skills/figma-android/README.md +0 -38
  155. package/pipeline/skills/figma-android/figma-component-code-connect/SKILL.md +0 -62
  156. package/pipeline/skills/figma-android/figma-component-implement/SKILL.md +0 -158
  157. package/pipeline/skills/figma-android/figma-component-test/SKILL.md +0 -120
  158. package/pipeline/skills/figma-android/figma-component-wiki/SKILL.md +0 -35
  159. package/pipeline/skills/figma-android/figma-to-component/SKILL.md +0 -124
  160. package/pipeline/skills/figma-common/README.md +0 -64
  161. package/pipeline/skills/figma-common/figma-bottom-sheets/SKILL.md +0 -152
  162. package/pipeline/skills/figma-common/figma-cli-iterate/SKILL.md +0 -282
  163. package/pipeline/skills/figma-common/figma-cli-iterate-mend/SKILL.md +0 -503
  164. package/pipeline/skills/figma-common/figma-cli-lean-iterate/SKILL.md +0 -288
  165. package/pipeline/skills/figma-common/figma-cli-skip/SKILL.md +0 -362
  166. package/pipeline/skills/figma-common/figma-commit/COMMON_REBASE.md +0 -206
  167. package/pipeline/skills/figma-common/figma-commit/REVIEW.md +0 -337
  168. package/pipeline/skills/figma-common/figma-commit/SKILL.md +0 -216
  169. package/pipeline/skills/figma-common/figma-component-confluence-sync/SKILL.md +0 -223
  170. package/pipeline/skills/figma-common/figma-component-start/SKILL.md +0 -251
  171. package/pipeline/skills/figma-common/figma-component-status-update/SKILL.md +0 -78
  172. package/pipeline/skills/figma-common/figma-evolve-component/SKILL.md +0 -61
  173. package/pipeline/skills/figma-common/figma-fix/SKILL.md +0 -321
  174. package/pipeline/skills/figma-common/figma-form-integration/SKILL.md +0 -542
  175. package/pipeline/skills/figma-common/figma-issue/SKILL.md +0 -750
  176. package/pipeline/skills/figma-common/figma-iterate/SKILL.md +0 -208
  177. package/pipeline/skills/figma-common/figma-iteration-commit/SKILL.md +0 -1020
  178. package/pipeline/skills/figma-common/figma-mend/SKILL.md +0 -331
  179. package/pipeline/skills/figma-common/figma-navigation/SKILL.md +0 -156
  180. package/pipeline/skills/figma-common/figma-overlays/SKILL.md +0 -142
  181. package/pipeline/skills/figma-common/figma-price-integration/SKILL.md +0 -398
  182. package/pipeline/skills/figma-common/figma-remote-mcp-auth/SKILL.md +0 -104
  183. package/pipeline/skills/figma-common/figma-review/SKILL.md +0 -400
  184. package/pipeline/skills/figma-common/figma-setup/SKILL.md +0 -514
  185. package/pipeline/skills/figma-common/figma-setup/scripts/fetch-mcp-token.py +0 -592
  186. package/pipeline/skills/figma-common/figma-skip/SKILL.md +0 -129
  187. package/pipeline/skills/figma-common/figma-ui-patterns/SKILL.md +0 -105
  188. package/pipeline/skills/figma-common/figma-ui-patterns/patterns/animated-gradient-border.md +0 -116
  189. package/pipeline/skills/figma-common/figma-utility/SKILL.md +0 -274
  190. package/pipeline/skills/figma-common/figma-utility/scripts/figma-utility.py +0 -808
  191. package/pipeline/skills/figma-common/figma-validate/SKILL.md +0 -633
  192. package/pipeline/skills/figma-common/performance-iteration-commit-all/SKILL.md +0 -716
  193. package/pipeline/skills/figma-common/performance-review-next/SKILL.md +0 -238
  194. package/pipeline/skills/figma-common/performance-start/SKILL.md +0 -430
  195. package/pipeline/skills/figma-common/performance-swiftui/SKILL.md +0 -706
  196. package/pipeline/skills/figma-common/performance-tour/SKILL.md +0 -423
  197. package/pipeline/skills/figma-ios/REVIEW_CHECKLIST.md +0 -67
  198. package/pipeline/skills/figma-ios/figma-component-code-connect/SKILL.md +0 -178
  199. package/pipeline/skills/figma-ios/figma-component-implement/SKILL.md +0 -184
  200. package/pipeline/skills/figma-ios/figma-component-test/SKILL.md +0 -219
  201. package/pipeline/skills/figma-ios/figma-component-wiki/SKILL.md +0 -274
  202. package/pipeline/skills/figma-ios/figma-to-component/SKILL.md +0 -416
  203. package/pipeline/skills/figma-ios/figma-to-component/halt-return-protocol.md +0 -57
  204. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-0-init.md +0 -307
  205. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-1-gathering.md +0 -119
  206. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-1.5-existing-discovery.md +0 -174
  207. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2-orchestrator.md +0 -333
  208. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2a-testing-identifiers.md +0 -368
  209. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2b-localization.md +0 -393
  210. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2c-accessibility.md +0 -617
  211. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2d-analytics.md +0 -352
  212. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3-orchestrator.md +0 -337
  213. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3a-location.md +0 -206
  214. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3b-tokens.md +0 -235
  215. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3c-nested.md +0 -214
  216. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3d-patterns.md +0 -902
  217. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3e-assets.md +0 -156
  218. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3f-utilities.md +0 -175
  219. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3g-property-coverage.md +0 -176
  220. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3h-variant-config.md +0 -333
  221. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4-orchestrator.md +0 -412
  222. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4a-configuration.md +0 -336
  223. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4b-view.md +0 -705
  224. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4c-documentation.md +0 -332
  225. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4d-preview.md +0 -380
  226. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4e-modifiers.md +0 -262
  227. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5-orchestrator.md +0 -482
  228. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5a-viewinspector.md +0 -274
  229. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5b-snapshot.md +0 -636
  230. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5c-unit.md +0 -142
  231. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-6-code-connect.md +0 -547
  232. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-7-wiki.md +0 -39
  233. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-7a-confluence-generate.md +0 -659
  234. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-7a-wiki-generate.md +0 -580
  235. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-8-cleanup.md +0 -51
  236. package/pipeline/skills/figma-ios/figma-to-component/reference/accessibility.md +0 -183
  237. package/pipeline/skills/figma-ios/figma-to-component/reference/analytics-events.md +0 -64
  238. package/pipeline/skills/figma-ios/figma-to-component/reference/code-connect.md +0 -531
  239. package/pipeline/skills/figma-ios/figma-to-component/reference/confluence-api.md +0 -89
  240. package/pipeline/skills/figma-ios/figma-to-component/reference/confluence-xhtml.md +0 -155
  241. package/pipeline/skills/figma-ios/figma-to-component/reference/figma-to-swiftui-effects.md +0 -196
  242. package/pipeline/skills/figma-ios/figma-to-component/reference/halt-return-protocol.md +0 -57
  243. package/pipeline/skills/figma-ios/figma-to-component/reference/localization-naming.md +0 -89
  244. package/pipeline/skills/figma-ios/figma-to-component/reference/macros.md +0 -227
  245. package/pipeline/skills/figma-ios/figma-to-component/reference/missing-tokens.md +0 -157
  246. package/pipeline/skills/figma-ios/figma-to-component/reference/orchestrator-discipline.md +0 -90
  247. package/pipeline/skills/figma-ios/figma-to-component/reference/registry.md +0 -116
  248. package/pipeline/skills/figma-ios/figma-to-component/reference/remote-mcp-script.md +0 -153
  249. package/pipeline/skills/figma-ios/figma-to-component/reference/rest-api-script.md +0 -130
  250. package/pipeline/skills/figma-ios/figma-to-component/reference/scripts-inventory.md +0 -218
  251. package/pipeline/skills/figma-ios/figma-to-component/reference/snapshot-testing.md +0 -188
  252. package/pipeline/skills/figma-ios/figma-to-component/reference/subcomponent-graph.md +0 -93
  253. package/pipeline/skills/figma-ios/figma-to-component/reference/testing-identifiers-naming.md +0 -98
  254. package/pipeline/skills/figma-ios/figma-to-component/reference/tools.md +0 -261
  255. package/pipeline/skills/figma-ios/figma-to-component/reference/viewinspector.md +0 -147
  256. package/pipeline/skills/figma-ios/figma-to-component/reference/wiki-to-confluence-mapping.md +0 -182
  257. package/pipeline/skills/figma-ios/figma-to-component/scripts/apply-author-login-map.py +0 -185
  258. package/pipeline/skills/figma-ios/figma-to-component/scripts/backfill-status.py +0 -609
  259. package/pipeline/skills/figma-ios/figma-to-component/scripts/build-author-registry.py +0 -332
  260. package/pipeline/skills/figma-ios/figma-to-component/scripts/bulk-sync-issues.py +0 -261
  261. package/pipeline/skills/figma-ios/figma-to-component/scripts/code-connect-data-gather.py +0 -184
  262. package/pipeline/skills/figma-ios/figma-to-component/scripts/code-connect-publish.sh +0 -188
  263. package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-component-status-upload.py +0 -768
  264. package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-component-status.py +0 -191
  265. package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-data-gather.py +0 -420
  266. package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-page-ids.example.json +0 -9
  267. package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-publish.py +0 -336
  268. package/pipeline/skills/figma-ios/figma-to-component/scripts/figma-subcomponent-graph.py +0 -391
  269. package/pipeline/skills/figma-ios/figma-to-component/scripts/figma-update.py +0 -292
  270. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/__init__.py +0 -1
  271. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/issue_sync_propagate.py +0 -93
  272. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/registry_writer.py +0 -299
  273. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_backfill_status.py +0 -343
  274. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_figma_update.py +0 -206
  275. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_figma_update_http.py +0 -149
  276. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_phase_clis.py +0 -281
  277. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_registry_writer.py +0 -332
  278. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_skill_figma_issue.py +0 -176
  279. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_skill_figma_review.py +0 -98
  280. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_update_issue.py +0 -298
  281. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_update_issue_gh.py +0 -195
  282. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase1-gather.py +0 -1298
  283. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase2-finalize.py +0 -228
  284. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase3-scripts.py +0 -1089
  285. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase4-finalize.py +0 -141
  286. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase5-finalize.py +0 -106
  287. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase6-finalize.py +0 -162
  288. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase7-finalize.py +0 -105
  289. package/pipeline/skills/figma-ios/figma-to-component/scripts/register-icons-codeconnect.py +0 -179
  290. package/pipeline/skills/figma-ios/figma-to-component/scripts/remote-mcp-fetch.py +0 -260
  291. package/pipeline/skills/figma-ios/figma-to-component/scripts/resolve-author-logins.py +0 -260
  292. package/pipeline/skills/figma-ios/figma-to-component/scripts/run-uicomponents-tests.sh +0 -86
  293. package/pipeline/skills/figma-ios/figma-to-component/scripts/sidebar-generator.py +0 -321
  294. package/pipeline/skills/figma-ios/figma-to-component/scripts/update-issue-from-registry.py +0 -1470
  295. package/pipeline/skills/figma-ios/figma-to-component/scripts/validate-phase4.sh +0 -176
  296. package/pipeline/skills/figma-ios/figma-to-component/scripts/validate-phase6.sh +0 -147
  297. package/pipeline/skills/figma-ios/figma-to-component/scripts/validate-phase7a.py +0 -629
  298. /package/pipeline/commands/multi-agent/{delete.md → delete/SKILL.md} +0 -0
  299. /package/pipeline/commands/multi-agent/{diff-explain.md → diff-explain/SKILL.md} +0 -0
  300. /package/pipeline/commands/multi-agent/{garbage-collect.md → garbage-collect/SKILL.md} +0 -0
  301. /package/pipeline/commands/multi-agent/{issue.md → issue/SKILL.md} +0 -0
  302. /package/pipeline/commands/multi-agent/{jira.md → jira/SKILL.md} +0 -0
  303. /package/pipeline/commands/multi-agent/{kill.md → kill/SKILL.md} +0 -0
  304. /package/pipeline/commands/multi-agent/{language.md → language/SKILL.md} +0 -0
  305. /package/pipeline/commands/multi-agent/{log.md → log/SKILL.md} +0 -0
  306. /package/pipeline/commands/multi-agent/{prune-logs.md → prune-logs/SKILL.md} +0 -0
  307. /package/pipeline/commands/multi-agent/{purge.md → purge/SKILL.md} +0 -0
  308. /package/pipeline/commands/multi-agent/{scan.md → scan/SKILL.md} +0 -0
  309. /package/pipeline/commands/multi-agent/{search.md → search/SKILL.md} +0 -0
  310. /package/pipeline/commands/multi-agent/{stack.md → stack/SKILL.md} +0 -0
  311. /package/pipeline/commands/multi-agent/{status.md → status/SKILL.md} +0 -0
  312. /package/pipeline/commands/multi-agent/{test.md → test/SKILL.md} +0 -0
  313. /package/pipeline/commands/multi-agent/{update.md → update/SKILL.md} +0 -0
  314. /package/pipeline/{commands/multi-agent → multi-agent-refs}/_input-parser.md +0 -0
  315. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/analysis-template.md +0 -0
  316. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/audit-guide.md +0 -0
  317. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/backend-guide.md +0 -0
  318. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/confluence.md +0 -0
  319. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/issue-comment.md +0 -0
  320. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/pr-review-actions.md +0 -0
  321. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/pr.md +0 -0
  322. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/conventions-defaults.md +0 -0
  323. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/autopilot-circuit-breaker.md +0 -0
  324. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/dev-critic.md +0 -0
  325. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/external-context-injection.md +0 -0
  326. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/model-fallback.md +0 -0
  327. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/plan-todos.md +0 -0
  328. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/prior-fix-detection.md +0 -0
  329. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/repo-map.md +0 -0
  330. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/shadow-git.md +0 -0
  331. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/frontend-guide.md +0 -0
  332. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/knowledge.md +0 -0
  333. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/multi-repo-integration-build.md +0 -0
  334. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/picker-contract.md +0 -0
  335. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/rules.md +0 -0
@@ -1,808 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Figma Utility Script
4
- Standalone tool for querying Figma data via REST API and MCP.
5
-
6
- Usage:
7
- python3 figma-utility.py <command> [options]
8
-
9
- Commands:
10
- screenshot --url <figma-url> [--output path] [--scale 1|2|3|4]
11
- metadata --url <figma-url> [--output path]
12
- properties --url <figma-url> [--output path]
13
- tokens --url <figma-url> [--output path]
14
- context --url <figma-url> [--output path]
15
- code-connect --url <figma-url> [--output path]
16
- node --url <figma-url> [--output path] [--depth N]
17
- components --url <figma-url> [--output path]
18
- styles --url <figma-url> [--output path]
19
- file --url <figma-url> [--output path] [--depth N]
20
- whoami
21
-
22
- Token resolution: --figma-token / --mcp-token CLI arg > macOS Keychain > Claude Code credentials
23
-
24
- Exit 0 = success (prints result to stdout)
25
- Exit 1 = failure (prints JSON error to stderr)
26
- """
27
-
28
- import argparse
29
- import base64
30
- import http.client
31
- import json
32
- import os
33
- import re
34
- import subprocess
35
- import sys
36
- from pathlib import Path
37
- from urllib.error import HTTPError, URLError
38
- from urllib.parse import urlparse
39
- from urllib.request import Request, urlopen
40
-
41
-
42
- # ─── Globals ─────────────────────────────────────────────────────────────────
43
-
44
- MCP_MODE = None # "remote" or "local"
45
- MCP_URL = "http://127.0.0.1:3845/mcp"
46
- MCP_TOKEN = None
47
- SESSION_ID = None
48
- FIGMA_TOKEN = None
49
-
50
-
51
- def log(msg):
52
- print(f"[figma-utility] {msg}", file=sys.stderr)
53
-
54
-
55
- def error_exit(code, message, hint=""):
56
- print(json.dumps({
57
- "status": "error",
58
- "message": message,
59
- "hint": hint,
60
- }, indent=2), file=sys.stderr)
61
- sys.exit(code)
62
-
63
-
64
- # ─── URL Parsing ─────────────────────────────────────────────────────────────
65
-
66
- def parse_figma_url(url):
67
- """Extract fileKey and nodeId from a Figma URL.
68
-
69
- Returns (fileKey, nodeId) where nodeId uses colon format (e.g. '123:456').
70
- nodeId may be None if not present in the URL.
71
- """
72
- if not url:
73
- return None, None
74
-
75
- # Match: figma.com/design/{fileKey}/... or figma.com/file/{fileKey}/...
76
- m = re.search(r'figma\.com/(?:design|file)/([A-Za-z0-9]+)', url)
77
- if not m:
78
- # Try branch URL
79
- m = re.search(r'figma\.com/(?:design|file)/[^/]+/[^?]*\?.*branch-id=([A-Za-z0-9]+)', url)
80
- file_key = m.group(1) if m else None
81
-
82
- # Extract node-id from query params
83
- node_id = None
84
- nm = re.search(r'node-id=([0-9]+-[0-9]+)', url)
85
- if nm:
86
- node_id = nm.group(1).replace("-", ":")
87
- else:
88
- nm = re.search(r'node-id=([0-9]+%3A[0-9]+)', url)
89
- if nm:
90
- node_id = nm.group(1).replace("%3A", ":")
91
-
92
- return file_key, node_id
93
-
94
-
95
- # ─── Token Resolution ────────────────────────────────────────────────────────
96
-
97
- def get_figma_token(cli_token=None):
98
- """Resolve Figma REST API token: CLI arg > Keychain FIGMA_ACCESS_TOKEN."""
99
- if cli_token:
100
- return cli_token
101
- try:
102
- token = subprocess.check_output(
103
- ["security", "find-generic-password", "-a", os.environ["USER"],
104
- "-s", "FIGMA_ACCESS_TOKEN", "-w"],
105
- stderr=subprocess.DEVNULL,
106
- ).decode().strip()
107
- if token:
108
- log("REST token from Keychain: FIGMA_ACCESS_TOKEN")
109
- return token
110
- except (subprocess.CalledProcessError, KeyError):
111
- pass
112
- return None
113
-
114
-
115
- def get_mcp_token(cli_token=None):
116
- """Resolve MCP OAuth token: CLI arg > Keychain FIGMA_MCP_TOKEN > Claude Code credentials."""
117
- if cli_token:
118
- return cli_token
119
- # Keychain: dedicated entry
120
- try:
121
- token = subprocess.check_output(
122
- ["security", "find-generic-password", "-a", os.environ["USER"],
123
- "-s", "FIGMA_MCP_TOKEN", "-w"],
124
- stderr=subprocess.DEVNULL,
125
- ).decode().strip()
126
- if token:
127
- log("MCP token from Keychain: FIGMA_MCP_TOKEN")
128
- return token
129
- except (subprocess.CalledProcessError, KeyError):
130
- pass
131
- # Keychain: extract from Claude Code credentials
132
- try:
133
- raw = subprocess.check_output(
134
- ["security", "find-generic-password", "-s", "Claude Code-credentials",
135
- "-a", os.environ["USER"], "-w"],
136
- stderr=subprocess.DEVNULL,
137
- ).decode()
138
- match = re.search(
139
- r'"figma-remote\|[^"]+":\{[^}]*"accessToken":"([^"]+)"', raw
140
- )
141
- if match:
142
- log("MCP token extracted from Claude Code credentials")
143
- return match.group(1)
144
- except (subprocess.CalledProcessError, KeyError):
145
- pass
146
- return None
147
-
148
-
149
- # ─── REST API ────────────────────────────────────────────────────────────────
150
-
151
- def figma_get(endpoint):
152
- """GET from Figma REST API. Returns parsed JSON."""
153
- if not FIGMA_TOKEN:
154
- error_exit(1, "No Figma REST API token found.",
155
- "Run /figma-setup or pass --figma-token.")
156
- url = f"https://api.figma.com/v1/{endpoint}"
157
- req = Request(url, headers={"X-Figma-Token": FIGMA_TOKEN})
158
- try:
159
- with urlopen(req, timeout=60) as resp:
160
- return json.loads(resp.read().decode())
161
- except HTTPError as e:
162
- body = e.read().decode() if hasattr(e, 'read') else ""
163
- error_exit(1, f"Figma API HTTP {e.code}: {body[:300]}",
164
- f"Endpoint: {endpoint}")
165
- except URLError as e:
166
- error_exit(1, f"Figma API connection error: {e.reason}",
167
- "Check your network connection.")
168
-
169
-
170
- def download_file(url, dest):
171
- """Download a URL to a local file path."""
172
- req = Request(url)
173
- with urlopen(req, timeout=60) as resp:
174
- Path(dest).parent.mkdir(parents=True, exist_ok=True)
175
- with open(dest, "wb") as f:
176
- f.write(resp.read())
177
- return True
178
-
179
-
180
- # ─── MCP Helpers ─────────────────────────────────────────────────────────────
181
-
182
- def _parse_sse_json(raw):
183
- """Parse JSON from SSE response. Handles multiline data payloads."""
184
- data_start = raw.find("\ndata: ")
185
- if data_start == -1:
186
- data_start = raw.find("data: ")
187
- if data_start == -1:
188
- return None
189
-
190
- json_str = raw[raw.index("data: ", data_start) + 6:].strip()
191
- try:
192
- return json.loads(json_str)
193
- except json.JSONDecodeError:
194
- depth = 0
195
- end = 0
196
- for i, ch in enumerate(json_str):
197
- if ch == "{":
198
- depth += 1
199
- elif ch == "}":
200
- depth -= 1
201
- if depth == 0:
202
- end = i + 1
203
- break
204
- if end > 0:
205
- try:
206
- return json.loads(json_str[:end])
207
- except json.JSONDecodeError:
208
- pass
209
- return None
210
-
211
-
212
- def _mcp_post_local(body_dict):
213
- """POST to local MCP (HTTP, session-based)."""
214
- parsed = urlparse(MCP_URL)
215
- conn = http.client.HTTPConnection(parsed.hostname, parsed.port, timeout=120)
216
- headers = {
217
- "Content-Type": "application/json",
218
- "Accept": "application/json, text/event-stream",
219
- }
220
- if SESSION_ID:
221
- headers["mcp-session-id"] = SESSION_ID
222
- conn.request("POST", parsed.path, body=json.dumps(body_dict), headers=headers)
223
- resp = conn.getresponse()
224
- session = resp.getheader("mcp-session-id")
225
- raw = resp.read().decode()
226
- conn.close()
227
- return _parse_sse_json(raw), session
228
-
229
-
230
- def _mcp_post_remote(body_dict):
231
- """POST to remote MCP (HTTPS, Bearer auth, stateless)."""
232
- conn = http.client.HTTPSConnection("mcp.figma.com", timeout=120)
233
- headers = {
234
- "Content-Type": "application/json",
235
- "Accept": "application/json, text/event-stream",
236
- "Authorization": f"Bearer {MCP_TOKEN}",
237
- }
238
- conn.request("POST", "/mcp", body=json.dumps(body_dict), headers=headers)
239
- resp = conn.getresponse()
240
- raw = resp.read().decode()
241
- conn.close()
242
- if resp.status != 200:
243
- raise RuntimeError(f"Remote MCP HTTP {resp.status}: {raw[:200]}")
244
- return _parse_sse_json(raw), None
245
-
246
-
247
- def _mcp_post(body_dict):
248
- if MCP_MODE == "remote":
249
- return _mcp_post_remote(body_dict)
250
- else:
251
- return _mcp_post_local(body_dict)
252
-
253
-
254
- def mcp_init():
255
- """Initialize MCP connection. Tries local first, falls back to remote."""
256
- global MCP_MODE, SESSION_ID
257
-
258
- init_body = {
259
- "jsonrpc": "2.0",
260
- "method": "initialize",
261
- "params": {
262
- "capabilities": {},
263
- "clientInfo": {"name": "figma-utility", "version": "1.0.0"},
264
- "protocolVersion": "2025-03-26",
265
- },
266
- "id": 1,
267
- }
268
-
269
- # Try local first (faster, no rate limits)
270
- try:
271
- log(f"MCP: trying local ({MCP_URL})...")
272
- result, session = _mcp_post_local(init_body)
273
- if session:
274
- SESSION_ID = session
275
- MCP_MODE = "local"
276
- log(f"MCP: local connected (session: {SESSION_ID[:12]}...)")
277
- return True
278
- except Exception as exc:
279
- log(f"MCP: local failed ({exc})")
280
-
281
- # Fall back to remote
282
- if MCP_TOKEN:
283
- try:
284
- log("MCP: trying remote (mcp.figma.com)...")
285
- result, _ = _mcp_post_remote(init_body)
286
- if result:
287
- MCP_MODE = "remote"
288
- log("MCP: remote connected")
289
- return True
290
- except Exception as exc:
291
- log(f"MCP: remote failed ({exc})")
292
-
293
- return False
294
-
295
-
296
- _MCP_CALL_ID = 10
297
-
298
-
299
- def mcp_call(tool_name, arguments, file_key=None):
300
- """Call an MCP tool. Returns (texts, images) lists."""
301
- global _MCP_CALL_ID
302
- _MCP_CALL_ID += 1
303
-
304
- # Remote MCP requires fileKey
305
- if MCP_MODE == "remote" and file_key and "fileKey" not in arguments:
306
- arguments = {**arguments, "fileKey": file_key}
307
-
308
- body = {
309
- "jsonrpc": "2.0",
310
- "method": "tools/call",
311
- "params": {"name": tool_name, "arguments": arguments},
312
- "id": _MCP_CALL_ID,
313
- }
314
-
315
- result_json, _ = _mcp_post(body)
316
- if not result_json:
317
- raise RuntimeError(f"MCP {tool_name}: empty response")
318
- if "error" in result_json:
319
- raise RuntimeError(f"MCP {tool_name}: {result_json['error']}")
320
-
321
- texts = []
322
- images = []
323
- content = (result_json.get("result") or {}).get("content", [])
324
- for item in content:
325
- if item.get("type") == "text":
326
- texts.append(item["text"])
327
- elif item.get("type") == "image":
328
- images.append({
329
- "mimeType": item.get("mimeType", "image/png"),
330
- "data": item.get("data", ""),
331
- })
332
- return texts, images
333
-
334
-
335
- # ─── Commands ────────────────────────────────────────────────────────────────
336
-
337
- def cmd_screenshot(file_key, node_id, output_path=None, scale=2):
338
- """Get PNG screenshot of a node."""
339
- # Try REST API first (returns URL, more efficient for saving)
340
- if FIGMA_TOKEN:
341
- log(f"Fetching screenshot via REST API (scale={scale})...")
342
- data = figma_get(f"images/{file_key}?ids={node_id}&format=png&scale={scale}")
343
- images = data.get("images", {})
344
- img_url = images.get(node_id)
345
- if img_url:
346
- if output_path:
347
- download_file(img_url, output_path)
348
- log(f"Screenshot saved: {output_path}")
349
- print(json.dumps({
350
- "status": "ok",
351
- "command": "screenshot",
352
- "output": output_path,
353
- "source": "rest-api",
354
- }, indent=2))
355
- return
356
- else:
357
- print(json.dumps({
358
- "status": "ok",
359
- "command": "screenshot",
360
- "url": img_url,
361
- "source": "rest-api",
362
- "hint": "Use --output to save to file",
363
- }, indent=2))
364
- return
365
-
366
- # Fallback to MCP
367
- if MCP_MODE:
368
- log("Fetching screenshot via MCP...")
369
- texts, images = mcp_call("get_screenshot", {"nodeId": node_id}, file_key)
370
- if images:
371
- img_data = base64.b64decode(images[0]["data"])
372
- if output_path:
373
- Path(output_path).parent.mkdir(parents=True, exist_ok=True)
374
- with open(output_path, "wb") as f:
375
- f.write(img_data)
376
- log(f"Screenshot saved: {output_path} ({len(img_data)} bytes)")
377
- print(json.dumps({
378
- "status": "ok",
379
- "command": "screenshot",
380
- "output": output_path,
381
- "bytes": len(img_data),
382
- "source": f"mcp-{MCP_MODE}",
383
- }, indent=2))
384
- return
385
- else:
386
- print(json.dumps({
387
- "status": "ok",
388
- "command": "screenshot",
389
- "bytes": len(img_data),
390
- "source": f"mcp-{MCP_MODE}",
391
- "hint": "Use --output to save to file (base64 data available via MCP)",
392
- }, indent=2))
393
- return
394
-
395
- error_exit(1, "No screenshot source available.",
396
- "Provide --figma-token for REST API or ensure MCP is available.")
397
-
398
-
399
- def cmd_metadata(file_key, node_id, output_path=None):
400
- """Get layer hierarchy (XML) of a node."""
401
- if not MCP_MODE:
402
- error_exit(1, "MCP required for metadata.", "Ensure Figma Desktop or remote MCP is available.")
403
-
404
- log("Fetching metadata via MCP...")
405
- texts, _ = mcp_call("get_metadata", {"nodeId": node_id}, file_key)
406
- result = "\n".join(texts)
407
-
408
- if output_path:
409
- Path(output_path).parent.mkdir(parents=True, exist_ok=True)
410
- Path(output_path).write_text(result)
411
- log(f"Metadata saved: {output_path} ({len(result)} chars)")
412
-
413
- print(result)
414
-
415
-
416
- def cmd_properties(file_key, node_id, output_path=None):
417
- """Get component properties/variants via REST API."""
418
- log("Fetching node data via REST API...")
419
- data = figma_get(f"files/{file_key}/nodes?ids={node_id}")
420
-
421
- nodes = data.get("nodes", {})
422
- node_data = nodes.get(node_id, {})
423
- document = node_data.get("document", {})
424
-
425
- # Extract component property definitions
426
- prop_defs = document.get("componentPropertyDefinitions", {})
427
-
428
- # Also check children for component set
429
- if not prop_defs and document.get("type") == "COMPONENT_SET":
430
- children = document.get("children", [])
431
- if children:
432
- prop_defs = children[0].get("componentPropertyDefinitions", {})
433
-
434
- # Build structured output
435
- properties = {}
436
- for prop_name, prop_def in prop_defs.items():
437
- properties[prop_name] = {
438
- "type": prop_def.get("type"),
439
- "defaultValue": prop_def.get("defaultValue"),
440
- }
441
- if prop_def.get("variantOptions"):
442
- properties[prop_name]["options"] = prop_def["variantOptions"]
443
-
444
- result = {
445
- "status": "ok",
446
- "command": "properties",
447
- "nodeId": node_id,
448
- "name": document.get("name", ""),
449
- "type": document.get("type", ""),
450
- "propertyCount": len(properties),
451
- "properties": properties,
452
- }
453
-
454
- # Count variants if component set
455
- if document.get("type") == "COMPONENT_SET":
456
- result["variantCount"] = len(document.get("children", []))
457
-
458
- output = json.dumps(result, indent=2)
459
- if output_path:
460
- Path(output_path).parent.mkdir(parents=True, exist_ok=True)
461
- Path(output_path).write_text(output)
462
- log(f"Properties saved: {output_path}")
463
-
464
- print(output)
465
-
466
-
467
- def cmd_tokens(file_key, node_id, output_path=None):
468
- """Get design tokens/variables for a node."""
469
- if not MCP_MODE:
470
- # Fallback to REST API (may 403 on non-Enterprise)
471
- log("MCP unavailable, trying REST API for variables...")
472
- try:
473
- data = figma_get(f"files/{file_key}/variables/local")
474
- output = json.dumps({"status": "ok", "command": "tokens", "source": "rest-api", "data": data}, indent=2)
475
- if output_path:
476
- Path(output_path).parent.mkdir(parents=True, exist_ok=True)
477
- Path(output_path).write_text(output)
478
- print(output)
479
- return
480
- except SystemExit:
481
- error_exit(1, "Neither MCP nor REST API available for tokens.",
482
- "Ensure Figma Desktop or remote MCP is available.")
483
-
484
- log("Fetching tokens via MCP...")
485
- texts, _ = mcp_call("get_variable_defs", {"nodeId": node_id}, file_key)
486
- result_text = "\n".join(texts)
487
-
488
- # Try to parse as JSON for structured output
489
- try:
490
- token_data = json.loads(result_text)
491
- output = json.dumps({
492
- "status": "ok",
493
- "command": "tokens",
494
- "source": f"mcp-{MCP_MODE}",
495
- "tokenCount": len(token_data) if isinstance(token_data, dict) else None,
496
- "data": token_data,
497
- }, indent=2)
498
- except json.JSONDecodeError:
499
- output = result_text
500
-
501
- if output_path:
502
- Path(output_path).parent.mkdir(parents=True, exist_ok=True)
503
- Path(output_path).write_text(output)
504
- log(f"Tokens saved: {output_path}")
505
-
506
- print(output)
507
-
508
-
509
- def cmd_context(file_key, node_id, output_path=None):
510
- """Get design context (code generation) for a node."""
511
- if not MCP_MODE:
512
- error_exit(1, "MCP required for design context.", "Ensure Figma Desktop or remote MCP is available.")
513
-
514
- log("Fetching design context via MCP...")
515
- args = {
516
- "nodeId": node_id,
517
- "disableCodeConnect": True,
518
- }
519
- texts, _ = mcp_call("get_design_context", args, file_key)
520
- result = "\n".join(texts)
521
-
522
- if output_path:
523
- Path(output_path).parent.mkdir(parents=True, exist_ok=True)
524
- Path(output_path).write_text(result)
525
- log(f"Design context saved: {output_path} ({len(result)} chars)")
526
-
527
- print(result)
528
-
529
-
530
- def cmd_code_connect(file_key, node_id, output_path=None):
531
- """Get Code Connect mappings for a node."""
532
- if not MCP_MODE:
533
- error_exit(1, "MCP required for Code Connect.", "Ensure Figma Desktop or remote MCP is available.")
534
-
535
- log("Fetching Code Connect mappings via MCP...")
536
- args = {"nodeId": node_id, "codeConnectLabel": "SwiftUI"}
537
- texts, _ = mcp_call("get_code_connect_map", args, file_key)
538
- result = "\n".join(texts)
539
-
540
- # Try to structure the output
541
- try:
542
- cc_data = json.loads(result)
543
- output = json.dumps({
544
- "status": "ok",
545
- "command": "code-connect",
546
- "source": f"mcp-{MCP_MODE}",
547
- "mappings": cc_data,
548
- }, indent=2)
549
- except json.JSONDecodeError:
550
- output = result
551
-
552
- if output_path:
553
- Path(output_path).parent.mkdir(parents=True, exist_ok=True)
554
- Path(output_path).write_text(output)
555
- log(f"Code Connect saved: {output_path}")
556
-
557
- print(output)
558
-
559
-
560
- def cmd_node(file_key, node_id, output_path=None, depth=None):
561
- """Get raw node data from REST API."""
562
- log("Fetching node data via REST API...")
563
- endpoint = f"files/{file_key}/nodes?ids={node_id}"
564
- if depth is not None:
565
- endpoint += f"&depth={depth}"
566
-
567
- data = figma_get(endpoint)
568
- output = json.dumps(data, indent=2)
569
-
570
- if output_path:
571
- Path(output_path).parent.mkdir(parents=True, exist_ok=True)
572
- Path(output_path).write_text(output)
573
- log(f"Node data saved: {output_path} ({len(output)} chars)")
574
-
575
- print(output)
576
-
577
-
578
- def cmd_components(file_key, output_path=None):
579
- """List all components in a Figma file."""
580
- log("Fetching components via REST API...")
581
- data = figma_get(f"files/{file_key}/components")
582
-
583
- components = data.get("meta", {}).get("components", [])
584
- result = {
585
- "status": "ok",
586
- "command": "components",
587
- "fileKey": file_key,
588
- "count": len(components),
589
- "components": [
590
- {
591
- "key": c.get("key"),
592
- "name": c.get("name"),
593
- "nodeId": c.get("node_id"),
594
- "containingFrame": c.get("containing_frame", {}).get("name"),
595
- "pageName": c.get("containing_frame", {}).get("pageName"),
596
- }
597
- for c in components
598
- ],
599
- }
600
- output = json.dumps(result, indent=2)
601
-
602
- if output_path:
603
- Path(output_path).parent.mkdir(parents=True, exist_ok=True)
604
- Path(output_path).write_text(output)
605
- log(f"Components saved: {output_path}")
606
-
607
- print(output)
608
-
609
-
610
- def cmd_styles(file_key, output_path=None):
611
- """Get all styles in a Figma file."""
612
- log("Fetching styles via REST API...")
613
- data = figma_get(f"files/{file_key}/styles")
614
-
615
- styles = data.get("meta", {}).get("styles", [])
616
- result = {
617
- "status": "ok",
618
- "command": "styles",
619
- "fileKey": file_key,
620
- "count": len(styles),
621
- "styles": [
622
- {
623
- "key": s.get("key"),
624
- "name": s.get("name"),
625
- "styleType": s.get("style_type"),
626
- "nodeId": s.get("node_id"),
627
- "description": s.get("description"),
628
- }
629
- for s in styles
630
- ],
631
- }
632
- output = json.dumps(result, indent=2)
633
-
634
- if output_path:
635
- Path(output_path).parent.mkdir(parents=True, exist_ok=True)
636
- Path(output_path).write_text(output)
637
- log(f"Styles saved: {output_path}")
638
-
639
- print(output)
640
-
641
-
642
- def cmd_file(file_key, output_path=None, depth=None):
643
- """Get file document tree (summary)."""
644
- log("Fetching file tree via REST API...")
645
- endpoint = f"files/{file_key}"
646
- if depth is not None:
647
- endpoint += f"?depth={depth}"
648
-
649
- data = figma_get(endpoint)
650
-
651
- # Build summary
652
- doc = data.get("document", {})
653
- pages = []
654
- for page in doc.get("children", []):
655
- page_info = {
656
- "name": page.get("name"),
657
- "id": page.get("id"),
658
- "childCount": len(page.get("children", [])),
659
- }
660
- pages.append(page_info)
661
-
662
- result = {
663
- "status": "ok",
664
- "command": "file",
665
- "fileName": data.get("name"),
666
- "lastModified": data.get("lastModified"),
667
- "version": data.get("version"),
668
- "pageCount": len(pages),
669
- "pages": pages,
670
- }
671
-
672
- # If output requested, save full data; otherwise print summary
673
- if output_path:
674
- full_output = json.dumps(data, indent=2)
675
- Path(output_path).parent.mkdir(parents=True, exist_ok=True)
676
- Path(output_path).write_text(full_output)
677
- log(f"File data saved: {output_path} ({len(full_output)} chars)")
678
- print(json.dumps(result, indent=2))
679
- else:
680
- print(json.dumps(result, indent=2))
681
-
682
-
683
- def cmd_whoami():
684
- """Check MCP auth status."""
685
- if not MCP_MODE:
686
- error_exit(1, "MCP not available.", "Ensure Figma Desktop or remote MCP is available.")
687
-
688
- if MCP_MODE == "local":
689
- print(json.dumps({
690
- "status": "ok",
691
- "command": "whoami",
692
- "mode": "local",
693
- "message": "Connected to local Figma MCP (Figma Desktop)",
694
- "endpoint": MCP_URL,
695
- }, indent=2))
696
- return
697
-
698
- # Remote: call whoami
699
- log("Calling whoami on remote MCP...")
700
- texts, _ = mcp_call("whoami", {})
701
- result = "\n".join(texts)
702
- try:
703
- user_data = json.loads(result)
704
- print(json.dumps({
705
- "status": "ok",
706
- "command": "whoami",
707
- "mode": "remote",
708
- "data": user_data,
709
- }, indent=2))
710
- except json.JSONDecodeError:
711
- print(json.dumps({
712
- "status": "ok",
713
- "command": "whoami",
714
- "mode": "remote",
715
- "raw": result,
716
- }, indent=2))
717
-
718
-
719
- # ─── Main ────────────────────────────────────────────────────────────────────
720
-
721
- def main():
722
- parser = argparse.ArgumentParser(
723
- description="Figma Utility - query Figma data via REST API and MCP",
724
- formatter_class=argparse.RawDescriptionHelpFormatter,
725
- )
726
- parser.add_argument("command", choices=[
727
- "screenshot", "metadata", "properties", "tokens", "context",
728
- "code-connect", "node", "components", "styles", "file", "whoami",
729
- ])
730
- parser.add_argument("--url", help="Figma URL")
731
- parser.add_argument("--file-key", help="Figma file key (alternative to URL)")
732
- parser.add_argument("--node-id", help="Node ID in colon format e.g. 123:456 (alternative to URL)")
733
- parser.add_argument("--output", "-o", help="Save output to file path")
734
- parser.add_argument("--scale", type=int, default=2, choices=[1, 2, 3, 4],
735
- help="Screenshot scale (default: 2)")
736
- parser.add_argument("--depth", type=int, help="Depth limit for node/file tree")
737
- parser.add_argument("--figma-token", help="Figma REST API token (falls back to Keychain)")
738
- parser.add_argument("--mcp-token", help="MCP OAuth token (falls back to Keychain)")
739
- parser.add_argument("--mcp-url", default="http://127.0.0.1:3845/mcp",
740
- help="Local MCP endpoint URL")
741
-
742
- args = parser.parse_args()
743
-
744
- # ── Resolve tokens ──
745
- global FIGMA_TOKEN, MCP_TOKEN, MCP_URL
746
- FIGMA_TOKEN = get_figma_token(args.figma_token)
747
- MCP_TOKEN = get_mcp_token(args.mcp_token)
748
- MCP_URL = args.mcp_url
749
-
750
- # ── Parse URL or use explicit file-key/node-id ──
751
- file_key = args.file_key
752
- node_id = args.node_id
753
-
754
- if args.url:
755
- url_fk, url_nid = parse_figma_url(args.url)
756
- file_key = file_key or url_fk
757
- node_id = node_id or url_nid
758
-
759
- # ── Validate required params ──
760
- if args.command == "whoami":
761
- # whoami doesn't need URL
762
- pass
763
- elif args.command in ("components", "styles", "file"):
764
- # These only need file_key
765
- if not file_key:
766
- error_exit(1, "File key required.", "Provide --url or --file-key.")
767
- else:
768
- # All other commands need file_key + node_id
769
- if not file_key:
770
- error_exit(1, "File key required.", "Provide --url or --file-key.")
771
- if not node_id:
772
- error_exit(1, "Node ID required.", "Provide a URL with node-id or --node-id.")
773
-
774
- # ── Init MCP for commands that need it ──
775
- mcp_commands = {"metadata", "tokens", "context", "code-connect", "whoami", "screenshot"}
776
- if args.command in mcp_commands:
777
- mcp_ok = mcp_init()
778
- if not mcp_ok and args.command not in ("screenshot", "tokens"):
779
- error_exit(1, "MCP connection failed.",
780
- "Ensure Figma Desktop is running (Dev Mode) or remote MCP token is available.")
781
-
782
- # ── Dispatch ──
783
- if args.command == "screenshot":
784
- cmd_screenshot(file_key, node_id, args.output, args.scale)
785
- elif args.command == "metadata":
786
- cmd_metadata(file_key, node_id, args.output)
787
- elif args.command == "properties":
788
- cmd_properties(file_key, node_id, args.output)
789
- elif args.command == "tokens":
790
- cmd_tokens(file_key, node_id, args.output)
791
- elif args.command == "context":
792
- cmd_context(file_key, node_id, args.output)
793
- elif args.command == "code-connect":
794
- cmd_code_connect(file_key, node_id, args.output)
795
- elif args.command == "node":
796
- cmd_node(file_key, node_id, args.output, args.depth)
797
- elif args.command == "components":
798
- cmd_components(file_key, args.output)
799
- elif args.command == "styles":
800
- cmd_styles(file_key, args.output)
801
- elif args.command == "file":
802
- cmd_file(file_key, args.output, args.depth)
803
- elif args.command == "whoami":
804
- cmd_whoami()
805
-
806
-
807
- if __name__ == "__main__":
808
- main()