@mmerterden/multi-agent-pipeline 11.2.0 → 11.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (331) hide show
  1. package/CHANGELOG.md +37 -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 +33 -15
  8. package/install/copilot.mjs +16 -14
  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/{commands/multi-agent → multi-agent-refs}/_account-picker.md +2 -2
  33. package/pipeline/{commands/multi-agent → multi-agent-refs}/_dev-context.md +2 -2
  34. package/pipeline/{commands/multi-agent → multi-agent-refs}/_repo-picker.md +2 -2
  35. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/android-guide.md +1 -1
  36. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/jira.md +1 -1
  37. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/wiki.md +7 -7
  38. package/pipeline/multi-agent-refs/component-dispatch.md +92 -0
  39. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/cross-cli-contract.md +10 -77
  40. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/verify-by-test.md +1 -1
  41. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/generate-issue.md +2 -2
  42. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/issue-jira-triad.md +1 -1
  43. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/keychain.md +1 -1
  44. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/log-format.md +1 -1
  45. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/modes.md +2 -2
  46. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/operations.md +1 -1
  47. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-0-init.md +10 -10
  48. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-1-analysis.md +6 -6
  49. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-2-planning.md +3 -3
  50. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-3-dev.md +10 -10
  51. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-4-review.md +8 -8
  52. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-5-test.md +3 -3
  53. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-6-commit.md +4 -4
  54. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-7-report.md +6 -6
  55. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases.md +12 -12
  56. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/progress-contract.md +4 -4
  57. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/swiftui-guide.md +1 -1
  58. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/tracker-contract.md +3 -3
  59. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/wiki-capture.md +4 -4
  60. package/pipeline/preferences-template.json +17 -1
  61. package/pipeline/rules/figma-pipeline.md +2 -2
  62. package/pipeline/schemas/prefs.schema.json +0 -34
  63. package/pipeline/scripts/README.md +1 -3
  64. package/pipeline/scripts/fixtures/install-layout.tsv +6 -5
  65. package/pipeline/scripts/gen-mode-dispatch.mjs +3 -3
  66. package/pipeline/scripts/phase-banner.sh +2 -2
  67. package/pipeline/scripts/review-scope.mjs +94 -0
  68. package/pipeline/scripts/scan-agent-config.sh +2 -2
  69. package/pipeline/scripts/smoke-agent-log-cost.sh +1 -1
  70. package/pipeline/scripts/smoke-agent-model-routing.sh +4 -4
  71. package/pipeline/scripts/smoke-autopilot-circuit-breaker.sh +2 -2
  72. package/pipeline/scripts/smoke-channels-approval-gate.sh +2 -2
  73. package/pipeline/scripts/smoke-channels-flow.sh +5 -5
  74. package/pipeline/scripts/smoke-clarify.sh +1 -1
  75. package/pipeline/scripts/smoke-commands-skills-parity.sh +3 -3
  76. package/pipeline/scripts/smoke-community-gates.sh +1 -1
  77. package/pipeline/scripts/smoke-compliance-skills.sh +2 -2
  78. package/pipeline/scripts/smoke-cost-summary.sh +1 -1
  79. package/pipeline/scripts/smoke-cross-cli-behavior.sh +17 -43
  80. package/pipeline/scripts/smoke-cross-phase-cohesion.sh +1 -1
  81. package/pipeline/scripts/smoke-dev-critic.sh +1 -1
  82. package/pipeline/scripts/smoke-diff-explain.sh +1 -1
  83. package/pipeline/scripts/smoke-diff-risk.sh +1 -1
  84. package/pipeline/scripts/smoke-evidence-gate.sh +1 -1
  85. package/pipeline/scripts/smoke-figma-dispatch.sh +46 -57
  86. package/pipeline/scripts/smoke-gate-hooks.sh +1 -1
  87. package/pipeline/scripts/smoke-generate-issue.sh +8 -8
  88. package/pipeline/scripts/smoke-handoff-contract.sh +3 -3
  89. package/pipeline/scripts/smoke-identity-isolation.sh +2 -2
  90. package/pipeline/scripts/smoke-intent-guard.sh +1 -1
  91. package/pipeline/scripts/smoke-issue-comment-template.sh +2 -2
  92. package/pipeline/scripts/smoke-issue-jira-triad.sh +4 -4
  93. package/pipeline/scripts/smoke-language-axis.sh +6 -6
  94. package/pipeline/scripts/smoke-learnings-ledger.sh +3 -3
  95. package/pipeline/scripts/smoke-mode-dispatch-drift.sh +16 -16
  96. package/pipeline/scripts/smoke-model-fallback.sh +3 -3
  97. package/pipeline/scripts/smoke-multi-repo-integration.sh +2 -2
  98. package/pipeline/scripts/smoke-multi-repo-worktree.sh +1 -1
  99. package/pipeline/scripts/smoke-no-token-prompt.sh +5 -5
  100. package/pipeline/scripts/smoke-phase-0-multi-repo.sh +1 -1
  101. package/pipeline/scripts/smoke-phase-6-multi.sh +1 -1
  102. package/pipeline/scripts/smoke-phase4-gates.sh +45 -0
  103. package/pipeline/scripts/smoke-plan-approval-gate.sh +5 -5
  104. package/pipeline/scripts/smoke-plan-todos.sh +3 -3
  105. package/pipeline/scripts/smoke-pr-review-actions.sh +3 -3
  106. package/pipeline/scripts/smoke-prefs-language.sh +1 -1
  107. package/pipeline/scripts/smoke-progress-contract.sh +2 -2
  108. package/pipeline/scripts/smoke-push-retry.sh +1 -1
  109. package/pipeline/scripts/smoke-repo-map.sh +2 -2
  110. package/pipeline/scripts/smoke-shadow-git.sh +1 -1
  111. package/pipeline/scripts/smoke-skill-authoring.sh +4 -9
  112. package/pipeline/scripts/smoke-tasklist-ordering.sh +10 -10
  113. package/pipeline/scripts/smoke-test-gap.sh +1 -1
  114. package/pipeline/scripts/smoke-token-budget.sh +1 -1
  115. package/pipeline/scripts/smoke-token-preflight.sh +2 -2
  116. package/pipeline/scripts/smoke-tracker-contract.sh +13 -13
  117. package/pipeline/scripts/smoke-tracker-tokens-invocation.sh +5 -5
  118. package/pipeline/scripts/smoke-triage-memory.sh +2 -2
  119. package/pipeline/scripts/smoke-update-check.sh +1 -1
  120. package/pipeline/scripts/smoke-url-enrichment.sh +2 -2
  121. package/pipeline/scripts/smoke-validator-gates.sh +1 -1
  122. package/pipeline/scripts/smoke-verify-by-test.sh +3 -3
  123. package/pipeline/scripts/smoke-wiki-integration.sh +9 -33
  124. package/pipeline/scripts/smoke-work-summary.sh +1 -1
  125. package/pipeline/scripts/smoke-worktree-path-convention.sh +4 -4
  126. package/pipeline/scripts/test-integrity-gate.mjs +83 -0
  127. package/pipeline/scripts/uninstall.mjs +2 -2
  128. package/pipeline/skills/.skill-manifest.json +40 -160
  129. package/pipeline/skills/.skills-index.json +3 -372
  130. package/pipeline/skills/shared/README.md +1 -1
  131. package/pipeline/skills/shared/core/multi-agent/SKILL.md +1 -1
  132. package/pipeline/skills/shared/core/multi-agent-analysis/SKILL.md +2 -2
  133. package/pipeline/skills/shared/core/multi-agent-analysis-resolve/SKILL.md +3 -3
  134. package/pipeline/skills/shared/core/multi-agent-build-optimize/SKILL.md +1 -1
  135. package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +1 -1
  136. package/pipeline/skills/shared/core/multi-agent-finish/SKILL.md +1 -1
  137. package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +1 -1
  138. package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +116 -51
  139. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +8 -30
  140. package/pipeline/skills/shared/external/compose-components/SKILL.md +14 -14
  141. package/pipeline/skills/skills-index.md +2 -43
  142. package/pipeline/commands/multi-agent/refactor.md +0 -188
  143. package/pipeline/commands/multi-agent/refs/component-dispatch.md +0 -92
  144. package/pipeline/scripts/smoke-existing-discovery-gate.sh +0 -71
  145. package/pipeline/scripts/smoke-figma-android-parity.sh +0 -148
  146. package/pipeline/scripts/smoke-figma-credential-store.sh +0 -105
  147. package/pipeline/scripts/smoke-figma-cross-cli-inventory.sh +0 -177
  148. package/pipeline/scripts/smoke-figma-sync.sh +0 -149
  149. package/pipeline/scripts/smoke-sync-delegation.sh +0 -74
  150. package/pipeline/skills/figma-android/README.md +0 -38
  151. package/pipeline/skills/figma-android/figma-component-code-connect/SKILL.md +0 -62
  152. package/pipeline/skills/figma-android/figma-component-implement/SKILL.md +0 -158
  153. package/pipeline/skills/figma-android/figma-component-test/SKILL.md +0 -120
  154. package/pipeline/skills/figma-android/figma-component-wiki/SKILL.md +0 -35
  155. package/pipeline/skills/figma-android/figma-to-component/SKILL.md +0 -124
  156. package/pipeline/skills/figma-common/README.md +0 -64
  157. package/pipeline/skills/figma-common/figma-bottom-sheets/SKILL.md +0 -152
  158. package/pipeline/skills/figma-common/figma-cli-iterate/SKILL.md +0 -282
  159. package/pipeline/skills/figma-common/figma-cli-iterate-mend/SKILL.md +0 -503
  160. package/pipeline/skills/figma-common/figma-cli-lean-iterate/SKILL.md +0 -288
  161. package/pipeline/skills/figma-common/figma-cli-skip/SKILL.md +0 -362
  162. package/pipeline/skills/figma-common/figma-commit/COMMON_REBASE.md +0 -206
  163. package/pipeline/skills/figma-common/figma-commit/REVIEW.md +0 -337
  164. package/pipeline/skills/figma-common/figma-commit/SKILL.md +0 -216
  165. package/pipeline/skills/figma-common/figma-component-confluence-sync/SKILL.md +0 -223
  166. package/pipeline/skills/figma-common/figma-component-start/SKILL.md +0 -251
  167. package/pipeline/skills/figma-common/figma-component-status-update/SKILL.md +0 -78
  168. package/pipeline/skills/figma-common/figma-evolve-component/SKILL.md +0 -61
  169. package/pipeline/skills/figma-common/figma-fix/SKILL.md +0 -321
  170. package/pipeline/skills/figma-common/figma-form-integration/SKILL.md +0 -542
  171. package/pipeline/skills/figma-common/figma-issue/SKILL.md +0 -750
  172. package/pipeline/skills/figma-common/figma-iterate/SKILL.md +0 -208
  173. package/pipeline/skills/figma-common/figma-iteration-commit/SKILL.md +0 -1020
  174. package/pipeline/skills/figma-common/figma-mend/SKILL.md +0 -331
  175. package/pipeline/skills/figma-common/figma-navigation/SKILL.md +0 -156
  176. package/pipeline/skills/figma-common/figma-overlays/SKILL.md +0 -142
  177. package/pipeline/skills/figma-common/figma-price-integration/SKILL.md +0 -398
  178. package/pipeline/skills/figma-common/figma-remote-mcp-auth/SKILL.md +0 -104
  179. package/pipeline/skills/figma-common/figma-review/SKILL.md +0 -400
  180. package/pipeline/skills/figma-common/figma-setup/SKILL.md +0 -514
  181. package/pipeline/skills/figma-common/figma-setup/scripts/fetch-mcp-token.py +0 -592
  182. package/pipeline/skills/figma-common/figma-skip/SKILL.md +0 -129
  183. package/pipeline/skills/figma-common/figma-ui-patterns/SKILL.md +0 -105
  184. package/pipeline/skills/figma-common/figma-ui-patterns/patterns/animated-gradient-border.md +0 -116
  185. package/pipeline/skills/figma-common/figma-utility/SKILL.md +0 -274
  186. package/pipeline/skills/figma-common/figma-utility/scripts/figma-utility.py +0 -808
  187. package/pipeline/skills/figma-common/figma-validate/SKILL.md +0 -633
  188. package/pipeline/skills/figma-common/performance-iteration-commit-all/SKILL.md +0 -716
  189. package/pipeline/skills/figma-common/performance-review-next/SKILL.md +0 -238
  190. package/pipeline/skills/figma-common/performance-start/SKILL.md +0 -430
  191. package/pipeline/skills/figma-common/performance-swiftui/SKILL.md +0 -706
  192. package/pipeline/skills/figma-common/performance-tour/SKILL.md +0 -423
  193. package/pipeline/skills/figma-ios/REVIEW_CHECKLIST.md +0 -67
  194. package/pipeline/skills/figma-ios/figma-component-code-connect/SKILL.md +0 -178
  195. package/pipeline/skills/figma-ios/figma-component-implement/SKILL.md +0 -184
  196. package/pipeline/skills/figma-ios/figma-component-test/SKILL.md +0 -219
  197. package/pipeline/skills/figma-ios/figma-component-wiki/SKILL.md +0 -274
  198. package/pipeline/skills/figma-ios/figma-to-component/SKILL.md +0 -416
  199. package/pipeline/skills/figma-ios/figma-to-component/halt-return-protocol.md +0 -57
  200. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-0-init.md +0 -307
  201. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-1-gathering.md +0 -119
  202. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-1.5-existing-discovery.md +0 -174
  203. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2-orchestrator.md +0 -333
  204. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2a-testing-identifiers.md +0 -368
  205. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2b-localization.md +0 -393
  206. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2c-accessibility.md +0 -617
  207. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2d-analytics.md +0 -352
  208. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3-orchestrator.md +0 -337
  209. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3a-location.md +0 -206
  210. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3b-tokens.md +0 -235
  211. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3c-nested.md +0 -214
  212. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3d-patterns.md +0 -902
  213. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3e-assets.md +0 -156
  214. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3f-utilities.md +0 -175
  215. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3g-property-coverage.md +0 -176
  216. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3h-variant-config.md +0 -333
  217. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4-orchestrator.md +0 -412
  218. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4a-configuration.md +0 -336
  219. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4b-view.md +0 -705
  220. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4c-documentation.md +0 -332
  221. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4d-preview.md +0 -380
  222. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4e-modifiers.md +0 -262
  223. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5-orchestrator.md +0 -482
  224. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5a-viewinspector.md +0 -274
  225. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5b-snapshot.md +0 -636
  226. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5c-unit.md +0 -142
  227. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-6-code-connect.md +0 -547
  228. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-7-wiki.md +0 -39
  229. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-7a-confluence-generate.md +0 -659
  230. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-7a-wiki-generate.md +0 -580
  231. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-8-cleanup.md +0 -51
  232. package/pipeline/skills/figma-ios/figma-to-component/reference/accessibility.md +0 -183
  233. package/pipeline/skills/figma-ios/figma-to-component/reference/analytics-events.md +0 -64
  234. package/pipeline/skills/figma-ios/figma-to-component/reference/code-connect.md +0 -531
  235. package/pipeline/skills/figma-ios/figma-to-component/reference/confluence-api.md +0 -89
  236. package/pipeline/skills/figma-ios/figma-to-component/reference/confluence-xhtml.md +0 -155
  237. package/pipeline/skills/figma-ios/figma-to-component/reference/figma-to-swiftui-effects.md +0 -196
  238. package/pipeline/skills/figma-ios/figma-to-component/reference/halt-return-protocol.md +0 -57
  239. package/pipeline/skills/figma-ios/figma-to-component/reference/localization-naming.md +0 -89
  240. package/pipeline/skills/figma-ios/figma-to-component/reference/macros.md +0 -227
  241. package/pipeline/skills/figma-ios/figma-to-component/reference/missing-tokens.md +0 -157
  242. package/pipeline/skills/figma-ios/figma-to-component/reference/orchestrator-discipline.md +0 -90
  243. package/pipeline/skills/figma-ios/figma-to-component/reference/registry.md +0 -116
  244. package/pipeline/skills/figma-ios/figma-to-component/reference/remote-mcp-script.md +0 -153
  245. package/pipeline/skills/figma-ios/figma-to-component/reference/rest-api-script.md +0 -130
  246. package/pipeline/skills/figma-ios/figma-to-component/reference/scripts-inventory.md +0 -218
  247. package/pipeline/skills/figma-ios/figma-to-component/reference/snapshot-testing.md +0 -188
  248. package/pipeline/skills/figma-ios/figma-to-component/reference/subcomponent-graph.md +0 -93
  249. package/pipeline/skills/figma-ios/figma-to-component/reference/testing-identifiers-naming.md +0 -98
  250. package/pipeline/skills/figma-ios/figma-to-component/reference/tools.md +0 -261
  251. package/pipeline/skills/figma-ios/figma-to-component/reference/viewinspector.md +0 -147
  252. package/pipeline/skills/figma-ios/figma-to-component/reference/wiki-to-confluence-mapping.md +0 -182
  253. package/pipeline/skills/figma-ios/figma-to-component/scripts/apply-author-login-map.py +0 -185
  254. package/pipeline/skills/figma-ios/figma-to-component/scripts/backfill-status.py +0 -609
  255. package/pipeline/skills/figma-ios/figma-to-component/scripts/build-author-registry.py +0 -332
  256. package/pipeline/skills/figma-ios/figma-to-component/scripts/bulk-sync-issues.py +0 -261
  257. package/pipeline/skills/figma-ios/figma-to-component/scripts/code-connect-data-gather.py +0 -184
  258. package/pipeline/skills/figma-ios/figma-to-component/scripts/code-connect-publish.sh +0 -188
  259. package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-component-status-upload.py +0 -768
  260. package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-component-status.py +0 -191
  261. package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-data-gather.py +0 -420
  262. package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-page-ids.example.json +0 -9
  263. package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-publish.py +0 -336
  264. package/pipeline/skills/figma-ios/figma-to-component/scripts/figma-subcomponent-graph.py +0 -391
  265. package/pipeline/skills/figma-ios/figma-to-component/scripts/figma-update.py +0 -292
  266. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/__init__.py +0 -1
  267. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/issue_sync_propagate.py +0 -93
  268. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/registry_writer.py +0 -299
  269. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_backfill_status.py +0 -343
  270. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_figma_update.py +0 -206
  271. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_figma_update_http.py +0 -149
  272. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_phase_clis.py +0 -281
  273. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_registry_writer.py +0 -332
  274. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_skill_figma_issue.py +0 -176
  275. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_skill_figma_review.py +0 -98
  276. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_update_issue.py +0 -298
  277. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_update_issue_gh.py +0 -195
  278. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase1-gather.py +0 -1298
  279. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase2-finalize.py +0 -228
  280. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase3-scripts.py +0 -1089
  281. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase4-finalize.py +0 -141
  282. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase5-finalize.py +0 -106
  283. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase6-finalize.py +0 -162
  284. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase7-finalize.py +0 -105
  285. package/pipeline/skills/figma-ios/figma-to-component/scripts/register-icons-codeconnect.py +0 -179
  286. package/pipeline/skills/figma-ios/figma-to-component/scripts/remote-mcp-fetch.py +0 -260
  287. package/pipeline/skills/figma-ios/figma-to-component/scripts/resolve-author-logins.py +0 -260
  288. package/pipeline/skills/figma-ios/figma-to-component/scripts/run-uicomponents-tests.sh +0 -86
  289. package/pipeline/skills/figma-ios/figma-to-component/scripts/sidebar-generator.py +0 -321
  290. package/pipeline/skills/figma-ios/figma-to-component/scripts/update-issue-from-registry.py +0 -1470
  291. package/pipeline/skills/figma-ios/figma-to-component/scripts/validate-phase4.sh +0 -176
  292. package/pipeline/skills/figma-ios/figma-to-component/scripts/validate-phase6.sh +0 -147
  293. package/pipeline/skills/figma-ios/figma-to-component/scripts/validate-phase7a.py +0 -629
  294. /package/pipeline/commands/multi-agent/{delete.md → delete/SKILL.md} +0 -0
  295. /package/pipeline/commands/multi-agent/{diff-explain.md → diff-explain/SKILL.md} +0 -0
  296. /package/pipeline/commands/multi-agent/{garbage-collect.md → garbage-collect/SKILL.md} +0 -0
  297. /package/pipeline/commands/multi-agent/{issue.md → issue/SKILL.md} +0 -0
  298. /package/pipeline/commands/multi-agent/{jira.md → jira/SKILL.md} +0 -0
  299. /package/pipeline/commands/multi-agent/{kill.md → kill/SKILL.md} +0 -0
  300. /package/pipeline/commands/multi-agent/{language.md → language/SKILL.md} +0 -0
  301. /package/pipeline/commands/multi-agent/{log.md → log/SKILL.md} +0 -0
  302. /package/pipeline/commands/multi-agent/{prune-logs.md → prune-logs/SKILL.md} +0 -0
  303. /package/pipeline/commands/multi-agent/{purge.md → purge/SKILL.md} +0 -0
  304. /package/pipeline/commands/multi-agent/{scan.md → scan/SKILL.md} +0 -0
  305. /package/pipeline/commands/multi-agent/{search.md → search/SKILL.md} +0 -0
  306. /package/pipeline/commands/multi-agent/{stack.md → stack/SKILL.md} +0 -0
  307. /package/pipeline/commands/multi-agent/{status.md → status/SKILL.md} +0 -0
  308. /package/pipeline/commands/multi-agent/{test.md → test/SKILL.md} +0 -0
  309. /package/pipeline/commands/multi-agent/{update.md → update/SKILL.md} +0 -0
  310. /package/pipeline/{commands/multi-agent → multi-agent-refs}/_input-parser.md +0 -0
  311. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/analysis-template.md +0 -0
  312. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/audit-guide.md +0 -0
  313. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/backend-guide.md +0 -0
  314. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/confluence.md +0 -0
  315. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/issue-comment.md +0 -0
  316. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/pr-review-actions.md +0 -0
  317. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/pr.md +0 -0
  318. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/conventions-defaults.md +0 -0
  319. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/autopilot-circuit-breaker.md +0 -0
  320. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/dev-critic.md +0 -0
  321. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/external-context-injection.md +0 -0
  322. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/model-fallback.md +0 -0
  323. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/plan-todos.md +0 -0
  324. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/prior-fix-detection.md +0 -0
  325. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/repo-map.md +0 -0
  326. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/shadow-git.md +0 -0
  327. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/frontend-guide.md +0 -0
  328. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/knowledge.md +0 -0
  329. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/multi-repo-integration-build.md +0 -0
  330. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/picker-contract.md +0 -0
  331. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/rules.md +0 -0
@@ -1,391 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Figma Sub-Component Graph Generator
4
-
5
- Queries the Figma REST API to discover which components use other components
6
- as instances (sub-components). Produces sub-component-graph.json.
7
-
8
- Data flow:
9
- 1. Read component-catalog.json (source of truth for component list)
10
- 2. For each Figma file, batch-fetch node trees via REST API
11
- 3. Walk each node tree for INSTANCE children
12
- 4. Resolve INSTANCE.componentId → catalog component via response metadata
13
- 5. Write sub-component-graph.json
14
-
15
- Usage:
16
- python3 figma-subcomponent-graph.py [--dry-run] [--output path] [--skip-assets]
17
- """
18
-
19
- import argparse
20
- import json
21
- import os
22
- import re
23
- import subprocess
24
- import sys
25
- import time
26
- import urllib.parse
27
- import urllib.request
28
-
29
- FIGMA_API = "https://api.figma.com/v1"
30
- BATCH_SIZE = 25
31
- RATE_DELAY = 1.0 # seconds between API calls (respect rate limits)
32
-
33
-
34
- def get_token():
35
- """Retrieve Figma API token from macOS Keychain."""
36
- r = subprocess.run(
37
- ["security", "find-generic-password", "-a", os.environ["USER"],
38
- "-s", "FIGMA_ACCESS_TOKEN", "-w"],
39
- capture_output=True, text=True, check=True
40
- )
41
- return r.stdout.strip()
42
-
43
-
44
- def api_get(token, path):
45
- """GET request to Figma REST API."""
46
- url = f"{FIGMA_API}{path}"
47
- req = urllib.request.Request(url)
48
- req.add_header("X-Figma-Token", token)
49
- try:
50
- with urllib.request.urlopen(req, timeout=30) as resp:
51
- return json.loads(resp.read())
52
- except urllib.error.HTTPError as e:
53
- body = e.read().decode("utf-8", errors="replace")[:300]
54
- raise RuntimeError(f"Figma API {e.code}: {body}")
55
-
56
-
57
- def extract_file_key(figma_url):
58
- """Extract fileKey from a Figma design URL."""
59
- m = re.search(r'/design/([^/]+)/', figma_url)
60
- return m.group(1) if m else None
61
-
62
-
63
- def sanitize_type_name(name):
64
- """
65
- Replicate FigmaRegistryUtility.sanitizeTypeName() in Python.
66
- Splits on non-alphanumeric, PascalCases each part, joins.
67
- """
68
- parts = re.split(r'[^a-zA-Z0-9]+', name)
69
- result = []
70
- for part in parts:
71
- if not part:
72
- continue
73
- has_upper = any(c.isupper() for c in part)
74
- has_lower = any(c.islower() for c in part)
75
- if has_upper and has_lower:
76
- result.append(part[0].upper() + part[1:])
77
- else:
78
- result.append(part[0].upper() + part[1:].lower())
79
- return "".join(result)
80
-
81
-
82
- def find_instances(node):
83
- """Recursively find all INSTANCE componentIds in a node subtree."""
84
- found = set()
85
- if not isinstance(node, dict):
86
- return found
87
- if node.get("type") == "INSTANCE":
88
- cid = node.get("componentId")
89
- if cid:
90
- found.add(cid)
91
- for child in node.get("children", []):
92
- found |= find_instances(child)
93
- return found
94
-
95
-
96
- def find_catalog(script_dir):
97
- """Locate component-catalog.json from various possible working directories."""
98
- candidates = [
99
- os.path.join(script_dir, "..", "..", "..", "..",
100
- "{repos.packagesContainer}/Packages/"
101
- "{project.slug}-common/"
102
- "Shared/Figma/Generated/component-catalog.json"),
103
- "{repos.packagesContainer}/Packages/"
104
- "{project.slug}-common/"
105
- "Shared/Figma/Generated/component-catalog.json",
106
- "Shared/Figma/Generated/component-catalog.json",
107
- ]
108
- for c in candidates:
109
- p = os.path.normpath(c)
110
- if os.path.isfile(p):
111
- return p
112
- return None
113
-
114
-
115
- def find_output_dir(script_dir):
116
- """Locate Shared/Figma/ directory for output."""
117
- candidates = [
118
- os.path.join(script_dir, "..", "..", "..", "..",
119
- "{repos.packagesContainer}/Packages/"
120
- "{project.slug}-common/Shared/Figma"),
121
- "{repos.packagesContainer}/Packages/"
122
- "{project.slug}-common/Shared/Figma",
123
- "Shared/Figma",
124
- ]
125
- for c in candidates:
126
- p = os.path.normpath(c)
127
- if os.path.isdir(p):
128
- return p
129
- return None
130
-
131
-
132
- def main():
133
- parser = argparse.ArgumentParser(
134
- description="Generate sub-component dependency graph from Figma REST API")
135
- parser.add_argument("--dry-run", action="store_true",
136
- help="Preview API calls without executing")
137
- parser.add_argument("--output", help="Override output file path")
138
- parser.add_argument("--skip-assets", action="store_true",
139
- help="Skip Visual Assets file (icons/flags rarely have sub-components)")
140
- parser.add_argument("--verbose", action="store_true",
141
- help="Print detailed progress")
142
- args = parser.parse_args()
143
-
144
- script_dir = os.path.dirname(os.path.abspath(__file__))
145
-
146
- # --- 1. Load catalog ---
147
- cat_path = find_catalog(script_dir)
148
- if not cat_path:
149
- print("ERROR: component-catalog.json not found")
150
- print("Run: FigmaRegistryUtility catalog")
151
- sys.exit(1)
152
-
153
- with open(cat_path) as f:
154
- catalog = json.load(f)
155
-
156
- print(f"Catalog: {cat_path}")
157
- print(f"Total: {catalog['summary']['totalComponents']} components")
158
-
159
- # --- 2. Build lookup tables ---
160
- # catalog_by_node: nodeId → {name, figmaName, fileKey}
161
- # catalog_by_name: sanitizedName → nodeId
162
- # file_groups: fileKey → [(nodeId, name, fileLabel), ...]
163
- catalog_by_node = {}
164
- catalog_by_name = {}
165
- file_groups = {}
166
-
167
- for file_entry in catalog["files"]:
168
- file_label = file_entry["fileLabel"]
169
- for page in file_entry["pages"]:
170
- for comp in page["components"]:
171
- name = comp["name"]
172
- node_id = comp["nodeId"]
173
- figma_url = comp.get("figmaUrl", "")
174
- file_key = extract_file_key(figma_url)
175
-
176
- catalog_by_node[node_id] = {
177
- "name": name,
178
- "figmaName": comp.get("figmaName", ""),
179
- "fileKey": file_key,
180
- "fileLabel": file_label,
181
- }
182
- catalog_by_name[name] = node_id
183
-
184
- if file_key:
185
- if args.skip_assets and file_label == "Visual Assets":
186
- continue
187
- file_groups.setdefault(file_key, {
188
- "label": file_label,
189
- "components": []
190
- })["components"].append((node_id, name))
191
-
192
- print(f"Files: {len(file_groups)}")
193
- for fk, fg in file_groups.items():
194
- print(f" {fg['label']}: {len(fg['components'])} components ({fk[:8]}...)")
195
-
196
- if args.dry_run:
197
- total_batches = sum(
198
- (len(fg["components"]) + BATCH_SIZE - 1) // BATCH_SIZE
199
- for fg in file_groups.values()
200
- )
201
- print(f"\n[DRY RUN] Would make ~{total_batches} API requests")
202
- print(f"[DRY RUN] Rate: {RATE_DELAY}s delay → ~{total_batches * RATE_DELAY:.0f}s total")
203
- return
204
-
205
- # --- 3. Fetch from Figma REST API ---
206
- token = get_token()
207
- graph = {} # nodeId → {name, subComponents: [{nodeId, name}], instanceCount}
208
- errors = []
209
- api_calls = 0
210
-
211
- for file_key, fg in file_groups.items():
212
- file_label = fg["label"]
213
- components = fg["components"]
214
- print(f"\n{'='*60}")
215
- print(f"File: {file_label} ({len(components)} components)")
216
- print(f"{'='*60}")
217
-
218
- node_ids = [nid for nid, _ in components]
219
-
220
- for i in range(0, len(node_ids), BATCH_SIZE):
221
- batch = node_ids[i:i + BATCH_SIZE]
222
- batch_num = i // BATCH_SIZE + 1
223
- total_batches = (len(node_ids) + BATCH_SIZE - 1) // BATCH_SIZE
224
-
225
- ids_param = urllib.parse.quote(",".join(batch))
226
- path = f"/files/{file_key}/nodes?ids={ids_param}"
227
-
228
- print(f" Batch {batch_num}/{total_batches} ({len(batch)} nodes)...", end=" ", flush=True)
229
-
230
- try:
231
- data = api_get(token, path)
232
- api_calls += 1
233
- except Exception as e:
234
- print(f"ERROR: {e}")
235
- errors.append({"batch": batch, "error": str(e)})
236
- time.sleep(RATE_DELAY)
237
- continue
238
-
239
- nodes_data = data.get("nodes", {})
240
- batch_subs = 0
241
-
242
- for nid in batch:
243
- node_result = nodes_data.get(nid)
244
- if not node_result:
245
- graph[nid] = {
246
- "name": catalog_by_node[nid]["name"],
247
- "subComponents": [],
248
- "instanceCount": 0,
249
- "error": "node not returned"
250
- }
251
- continue
252
-
253
- doc = node_result.get("document", {})
254
- components_map = node_result.get("components", {})
255
- component_sets_map = node_result.get("componentSets", {})
256
-
257
- # Find all INSTANCE componentIds in this subtree
258
- instance_ids = find_instances(doc)
259
-
260
- # Resolve each instance to a catalog component
261
- sub_components = []
262
- seen = set()
263
-
264
- for cid in instance_ids:
265
- # Look up in response components map
266
- comp_meta = components_map.get(cid, {})
267
- comp_set_id = comp_meta.get("componentSetId")
268
-
269
- # The "real" component is the COMPONENT_SET if it exists,
270
- # otherwise the COMPONENT itself
271
- resolved_id = comp_set_id if comp_set_id else cid
272
-
273
- # Check if resolved ID is in our catalog
274
- if resolved_id in catalog_by_node and resolved_id != nid:
275
- if resolved_id not in seen:
276
- seen.add(resolved_id)
277
- sub_components.append({
278
- "nodeId": resolved_id,
279
- "name": catalog_by_node[resolved_id]["name"]
280
- })
281
- continue
282
-
283
- # Fallback: try matching by sanitized name
284
- raw_name = comp_meta.get("name", "")
285
- if not raw_name and comp_set_id and comp_set_id in component_sets_map:
286
- raw_name = component_sets_map[comp_set_id].get("name", "")
287
-
288
- if raw_name:
289
- sanitized = sanitize_type_name(raw_name)
290
- matched_nid = catalog_by_name.get(sanitized)
291
- if matched_nid and matched_nid != nid and matched_nid not in seen:
292
- seen.add(matched_nid)
293
- sub_components.append({
294
- "nodeId": matched_nid,
295
- "name": sanitized
296
- })
297
-
298
- sub_components.sort(key=lambda x: x["name"])
299
- comp_name = catalog_by_node[nid]["name"]
300
-
301
- graph[nid] = {
302
- "name": comp_name,
303
- "subComponents": sub_components,
304
- "instanceCount": len(instance_ids),
305
- }
306
-
307
- if sub_components:
308
- batch_subs += 1
309
- if args.verbose:
310
- names = [s["name"] for s in sub_components]
311
- print(f"\n {comp_name} → {names}")
312
-
313
- print(f"OK ({batch_subs} with subs)")
314
- time.sleep(RATE_DELAY)
315
-
316
- # --- 4. Compute reverse map (usedBy) ---
317
- used_by = {} # nodeId → [nodeId, ...]
318
- for nid, entry in graph.items():
319
- for sub in entry["subComponents"]:
320
- used_by.setdefault(sub["nodeId"], []).append(nid)
321
-
322
- for nid in used_by:
323
- used_by[nid] = sorted(
324
- used_by[nid],
325
- key=lambda x: graph.get(x, {}).get("name", "")
326
- )
327
-
328
- # --- 5. Build output ---
329
- with_subs = sum(1 for v in graph.values() if v["subComponents"])
330
- used_as_sub = len(used_by)
331
-
332
- output = {
333
- "version": "1.0",
334
- "generatedAt": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
335
- "source": "Figma REST API /files/:key/nodes - INSTANCE child inspection",
336
- "stats": {
337
- "totalComponents": len(graph),
338
- "componentsWithSubComponents": with_subs,
339
- "componentsUsedAsSub": used_as_sub,
340
- "apiCalls": api_calls,
341
- "errors": len(errors),
342
- },
343
- "graph": {},
344
- }
345
-
346
- for nid, entry in sorted(graph.items(), key=lambda x: x[1]["name"]):
347
- node_entry = {
348
- "name": entry["name"],
349
- "subComponents": [s["name"] for s in entry["subComponents"]],
350
- }
351
- # Add usedBy if this component is used by others
352
- if nid in used_by:
353
- node_entry["usedBy"] = [
354
- graph[uid]["name"] for uid in used_by[nid] if uid in graph
355
- ]
356
- # Only include instanceCount if there are unresolved instances
357
- unresolved = entry["instanceCount"] - len(entry["subComponents"])
358
- if unresolved > 0:
359
- node_entry["unresolvedInstances"] = unresolved
360
- if entry.get("error"):
361
- node_entry["error"] = entry["error"]
362
-
363
- output["graph"][nid] = node_entry
364
-
365
- # --- 6. Write output ---
366
- out_path = args.output
367
- if not out_path:
368
- out_dir = find_output_dir(script_dir)
369
- if out_dir:
370
- out_path = os.path.join(out_dir, "sub-component-graph.json")
371
- else:
372
- out_path = "sub-component-graph.json"
373
-
374
- with open(out_path, "w") as f:
375
- json.dump(output, f, indent=2, sort_keys=False, ensure_ascii=False)
376
- f.write("\n")
377
-
378
- print(f"\n{'='*60}")
379
- print(f"Written: {out_path}")
380
- print(f"Components: {len(graph)}")
381
- print(f"With sub-comps: {with_subs}")
382
- print(f"Used as sub-comp: {used_as_sub}")
383
- print(f"API calls: {api_calls}")
384
- if errors:
385
- print(f"Errors: {len(errors)}")
386
- for err in errors[:5]:
387
- print(f" {err['error'][:100]}")
388
-
389
-
390
- if __name__ == "__main__":
391
- main()
@@ -1,292 +0,0 @@
1
- #!/usr/bin/env python3
2
- """figma-update.py - detect Figma-side changes and mark components stale.
3
-
4
- Fetches the file-level `lastModified` timestamp from the Figma REST API
5
- (`GET /v1/files/:key`) and compares it to the `figmaUpdatedAt` stored in
6
- each component registry entry. If Figma has newer data, the script calls
7
- `lib.registry_writer.mark_stale()` for every registry entry that belongs
8
- to that file - setting `status.<platform>.stale = true` on any platform
9
- that has been implemented.
10
-
11
- This is a FILE-LEVEL check. Any change anywhere in the Figma file will
12
- mark all components in that file as stale. Conservative but correct:
13
- never misses a real change, only produces false positives that a
14
- developer can acknowledge by re-running Phase 4 (which clears stale via
15
- `set_implemented`'s side effect).
16
-
17
- Usage:
18
- # Check one file explicitly
19
- python3 figma-update.py --file-key ps4xrJzhn7WNbfkZs895Bi
20
-
21
- # Scan every unique fileKey across the registry
22
- python3 figma-update.py --all
23
-
24
- # Dry-run (no writes, prints what would change)
25
- python3 figma-update.py --all --dry-run
26
-
27
- # Limit the number of registry entries touched (for smoke tests)
28
- python3 figma-update.py --all --limit 10
29
-
30
- Token: FIGMA_ACCESS_TOKEN env var, or macOS Keychain FIGMA_ACCESS_TOKEN.
31
-
32
- Exit codes:
33
- 0 - Success
34
- 1 - Error (network, auth, bad registry files, etc.)
35
- """
36
-
37
- import argparse
38
- import http.client
39
- import json
40
- import os
41
- import subprocess
42
- import sys
43
- from pathlib import Path
44
- from typing import Optional
45
-
46
- sys.path.insert(0, str(Path(__file__).parent))
47
- from lib.registry_writer import mark_stale # noqa: E402
48
-
49
-
50
- FIGMA_API_HOST = "api.figma.com"
51
-
52
-
53
- # --------------------------------------------------------------------
54
- # Repo layout helpers
55
- # --------------------------------------------------------------------
56
-
57
- def find_repo_root() -> Path:
58
- p = Path.cwd()
59
- while p != p.parent:
60
- if (p / ".git").exists():
61
- return p
62
- p = p.parent
63
- return Path.cwd()
64
-
65
-
66
- def default_registry_dir() -> Path:
67
- return (
68
- find_repo_root()
69
- / "{repos.packagesContainer}"
70
- / "Packages"
71
- / "{project.slug}-common"
72
- / "Shared"
73
- / "Figma"
74
- / "Components"
75
- )
76
-
77
-
78
- # --------------------------------------------------------------------
79
- # Figma API (injectable for tests)
80
- # --------------------------------------------------------------------
81
-
82
- def load_figma_token() -> Optional[str]:
83
- """FIGMA_ACCESS_TOKEN env var first, then macOS Keychain."""
84
- env_token = os.environ.get("FIGMA_ACCESS_TOKEN")
85
- if env_token:
86
- return env_token
87
-
88
- try:
89
- result = subprocess.run(
90
- [
91
- "security", "find-generic-password",
92
- "-a", os.environ.get("USER", ""),
93
- "-s", "FIGMA_ACCESS_TOKEN",
94
- "-w",
95
- ],
96
- capture_output=True,
97
- text=True,
98
- timeout=5,
99
- )
100
- if result.returncode == 0 and result.stdout.strip():
101
- return result.stdout.strip()
102
- except (subprocess.SubprocessError, FileNotFoundError):
103
- pass
104
- return None
105
-
106
-
107
- def fetch_file_last_modified(file_key: str, token: str) -> str:
108
- """Call Figma REST API and return the file's lastModified timestamp.
109
-
110
- Uses the `depth=1` parameter to minimize payload - we only need the
111
- top-level metadata, not the full node tree. Figma returns a JSON
112
- object with a top-level `lastModified` ISO 8601 string.
113
-
114
- Raises:
115
- RuntimeError on HTTP error, missing field, or auth failure.
116
- """
117
- conn = http.client.HTTPSConnection(FIGMA_API_HOST, timeout=30)
118
- try:
119
- conn.request(
120
- "GET",
121
- f"/v1/files/{file_key}?depth=1",
122
- headers={"X-Figma-Token": token},
123
- )
124
- response = conn.getresponse()
125
- body = response.read().decode("utf-8")
126
- if response.status != 200:
127
- raise RuntimeError(
128
- f"Figma API returned {response.status}: {body[:200]}"
129
- )
130
- data = json.loads(body)
131
- last_modified = data.get("lastModified")
132
- if not last_modified:
133
- raise RuntimeError("Figma response missing lastModified field")
134
- return last_modified
135
- finally:
136
- conn.close()
137
-
138
-
139
- # --------------------------------------------------------------------
140
- # Core logic (pure, no network)
141
- # --------------------------------------------------------------------
142
-
143
- def scan_registry_for_file_keys(registry_dir: Path) -> dict[str, list[str]]:
144
- """Walk the registry directory and return { fileKey: [node_id, ...] }.
145
-
146
- Skips files that are not valid JSON or lack nodeId/fileKey.
147
- """
148
- by_file: dict[str, list[str]] = {}
149
- if not registry_dir.exists():
150
- return by_file
151
-
152
- for path in sorted(registry_dir.glob("*.json")):
153
- try:
154
- data = json.loads(path.read_text(encoding="utf-8"))
155
- except (json.JSONDecodeError, OSError):
156
- continue
157
- node_id = data.get("nodeId")
158
- file_key = data.get("fileKey")
159
- if not node_id or not file_key:
160
- continue
161
- by_file.setdefault(file_key, []).append(node_id)
162
- return by_file
163
-
164
-
165
- def update_file_components(
166
- registry_dir: Path,
167
- file_key: str,
168
- node_ids: list[str],
169
- new_last_modified: str,
170
- dry_run: bool,
171
- limit: Optional[int] = None,
172
- ) -> dict:
173
- """For every node in `node_ids`, call mark_stale if not a dry run.
174
-
175
- Returns a summary dict: { updated: [node_id...], skipped: [node_id...] }.
176
- """
177
- updated: list[str] = []
178
- skipped: list[str] = []
179
-
180
- to_process = node_ids if limit is None else node_ids[:limit]
181
-
182
- for node_id in to_process:
183
- node_id_dash = node_id.replace(":", "-")
184
- path = registry_dir / f"{node_id_dash}.json"
185
- if not path.exists():
186
- skipped.append(node_id)
187
- continue
188
-
189
- if dry_run:
190
- # Read and compare without writing
191
- try:
192
- data = json.loads(path.read_text(encoding="utf-8"))
193
- except (json.JSONDecodeError, OSError):
194
- skipped.append(node_id)
195
- continue
196
- prev = data.get("figmaUpdatedAt")
197
- if prev and prev >= new_last_modified:
198
- skipped.append(node_id)
199
- else:
200
- updated.append(node_id)
201
- else:
202
- changed = mark_stale(registry_dir, node_id, new_last_modified)
203
- if changed:
204
- updated.append(node_id)
205
- else:
206
- skipped.append(node_id)
207
-
208
- return {
209
- "fileKey": file_key,
210
- "lastModified": new_last_modified,
211
- "updated": updated,
212
- "skipped": skipped,
213
- "limit_applied": limit,
214
- }
215
-
216
-
217
- # --------------------------------------------------------------------
218
- # CLI
219
- # --------------------------------------------------------------------
220
-
221
- def main() -> int:
222
- parser = argparse.ArgumentParser(
223
- description="Scan registry and mark components stale when Figma has newer data",
224
- )
225
- scope = parser.add_mutually_exclusive_group(required=True)
226
- scope.add_argument("--file-key", help="Check one specific Figma file")
227
- scope.add_argument("--all", action="store_true", help="Check every unique fileKey in the registry")
228
-
229
- parser.add_argument("--registry-dir", help="Override registry directory path")
230
- parser.add_argument("--dry-run", action="store_true", help="Print changes without writing")
231
- parser.add_argument("--limit", type=int, help="Max nodes to touch per file (smoke-test safety)")
232
- parser.add_argument("--token", help="Figma API token (overrides env var and keychain)")
233
- args = parser.parse_args()
234
-
235
- registry_dir = Path(args.registry_dir) if args.registry_dir else default_registry_dir()
236
- if not registry_dir.exists():
237
- print(f"ERROR: registry directory does not exist: {registry_dir}", file=sys.stderr)
238
- return 1
239
-
240
- token = args.token or load_figma_token()
241
- if not token:
242
- print("ERROR: no Figma token found (set FIGMA_ACCESS_TOKEN or use --token)", file=sys.stderr)
243
- return 1
244
-
245
- if args.file_key:
246
- by_file: dict[str, list[str]] = scan_registry_for_file_keys(registry_dir)
247
- nodes_in_file = by_file.get(args.file_key, [])
248
- if not nodes_in_file:
249
- print(
250
- json.dumps({"status": "ok", "message": f"no registry entries for {args.file_key}"}),
251
- )
252
- return 0
253
- file_keys = {args.file_key: nodes_in_file}
254
- else:
255
- file_keys = scan_registry_for_file_keys(registry_dir)
256
-
257
- summary: list[dict] = []
258
- total_updated = 0
259
- total_skipped = 0
260
-
261
- for fk, nodes in file_keys.items():
262
- try:
263
- last_modified = fetch_file_last_modified(fk, token)
264
- except RuntimeError as e:
265
- print(f"ERROR fetching {fk}: {e}", file=sys.stderr)
266
- return 1
267
-
268
- file_summary = update_file_components(
269
- registry_dir=registry_dir,
270
- file_key=fk,
271
- node_ids=nodes,
272
- new_last_modified=last_modified,
273
- dry_run=args.dry_run,
274
- limit=args.limit,
275
- )
276
- summary.append(file_summary)
277
- total_updated += len(file_summary["updated"])
278
- total_skipped += len(file_summary["skipped"])
279
-
280
- print(json.dumps({
281
- "status": "ok",
282
- "dry_run": args.dry_run,
283
- "total_files": len(summary),
284
- "total_updated": total_updated,
285
- "total_skipped": total_skipped,
286
- "files": summary,
287
- }, indent=2, ensure_ascii=False))
288
- return 0
289
-
290
-
291
- if __name__ == "__main__":
292
- sys.exit(main())
@@ -1 +0,0 @@
1
- """Shared helpers for figma-to-swiftui pipeline scripts."""