@mmerterden/multi-agent-pipeline 11.1.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 (342) hide show
  1. package/CHANGELOG.md +58 -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/_common.mjs +9 -1
  7. package/install/_dev-only-files.mjs +0 -6
  8. package/install/claude.mjs +45 -18
  9. package/install/copilot.mjs +16 -14
  10. package/install/templates/copilot-instructions.md +3 -3
  11. package/package.json +1 -1
  12. package/pipeline/commands/{multi-agent.md → multi-agent/SKILL.md} +39 -39
  13. package/pipeline/commands/multi-agent/{analysis.md → analysis/SKILL.md} +18 -18
  14. package/pipeline/commands/multi-agent/{analysis-resolve.md → analysis-resolve/SKILL.md} +4 -4
  15. package/pipeline/commands/multi-agent/{autopilot.md → autopilot/SKILL.md} +4 -4
  16. package/pipeline/commands/multi-agent/{build-optimize.md → build-optimize/SKILL.md} +1 -1
  17. package/pipeline/commands/multi-agent/{channels.md → channels/SKILL.md} +12 -12
  18. package/pipeline/commands/multi-agent/{dev.md → dev/SKILL.md} +4 -4
  19. package/pipeline/commands/multi-agent/{dev-autopilot.md → dev-autopilot/SKILL.md} +4 -4
  20. package/pipeline/commands/multi-agent/{dev-local.md → dev-local/SKILL.md} +5 -5
  21. package/pipeline/commands/multi-agent/{dev-local-autopilot.md → dev-local-autopilot/SKILL.md} +4 -4
  22. package/pipeline/commands/multi-agent/{finish.md → finish/SKILL.md} +6 -6
  23. package/pipeline/commands/multi-agent/{generate.md → generate/SKILL.md} +3 -3
  24. package/pipeline/commands/multi-agent/{help.md → help/SKILL.md} +4 -4
  25. package/pipeline/commands/multi-agent/{local.md → local/SKILL.md} +5 -5
  26. package/pipeline/commands/multi-agent/{local-autopilot.md → local-autopilot/SKILL.md} +5 -5
  27. package/pipeline/commands/multi-agent/{manual-test.md → manual-test/SKILL.md} +1 -1
  28. package/pipeline/commands/multi-agent/refactor/SKILL.md +257 -0
  29. package/pipeline/commands/multi-agent/{resume.md → resume/SKILL.md} +1 -1
  30. package/pipeline/commands/multi-agent/{review.md → review/SKILL.md} +5 -5
  31. package/pipeline/commands/multi-agent/{setup.md → setup/SKILL.md} +1 -1
  32. package/pipeline/commands/multi-agent/{sync.md → sync/SKILL.md} +20 -58
  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-stack-plugins.mjs +1 -1
  66. package/pipeline/scripts/fixtures/install-layout.tsv +6 -5
  67. package/pipeline/scripts/gen-mode-dispatch.mjs +3 -3
  68. package/pipeline/scripts/phase-banner.sh +2 -2
  69. package/pipeline/scripts/review-scope.mjs +94 -0
  70. package/pipeline/scripts/scan-agent-config.sh +2 -2
  71. package/pipeline/scripts/smoke-agent-log-cost.sh +1 -1
  72. package/pipeline/scripts/smoke-agent-model-routing.sh +4 -4
  73. package/pipeline/scripts/smoke-autopilot-circuit-breaker.sh +2 -2
  74. package/pipeline/scripts/smoke-channels-approval-gate.sh +2 -2
  75. package/pipeline/scripts/smoke-channels-flow.sh +5 -5
  76. package/pipeline/scripts/smoke-clarify.sh +1 -1
  77. package/pipeline/scripts/smoke-commands-skills-parity.sh +3 -3
  78. package/pipeline/scripts/smoke-community-gates.sh +1 -1
  79. package/pipeline/scripts/smoke-compliance-skills.sh +2 -2
  80. package/pipeline/scripts/smoke-cost-summary.sh +1 -1
  81. package/pipeline/scripts/smoke-cross-cli-behavior.sh +17 -43
  82. package/pipeline/scripts/smoke-cross-phase-cohesion.sh +1 -1
  83. package/pipeline/scripts/smoke-dev-critic.sh +1 -1
  84. package/pipeline/scripts/smoke-diff-explain.sh +1 -1
  85. package/pipeline/scripts/smoke-diff-risk.sh +1 -1
  86. package/pipeline/scripts/smoke-evidence-gate.sh +1 -1
  87. package/pipeline/scripts/smoke-figma-dispatch.sh +46 -57
  88. package/pipeline/scripts/smoke-gate-hooks.sh +1 -1
  89. package/pipeline/scripts/smoke-generate-issue.sh +8 -8
  90. package/pipeline/scripts/smoke-handoff-contract.sh +3 -3
  91. package/pipeline/scripts/smoke-identity-isolation.sh +2 -2
  92. package/pipeline/scripts/smoke-intent-guard.sh +1 -1
  93. package/pipeline/scripts/smoke-issue-comment-template.sh +2 -2
  94. package/pipeline/scripts/smoke-issue-jira-triad.sh +4 -4
  95. package/pipeline/scripts/smoke-language-axis.sh +6 -6
  96. package/pipeline/scripts/smoke-learnings-ledger.sh +3 -3
  97. package/pipeline/scripts/smoke-mode-dispatch-drift.sh +16 -16
  98. package/pipeline/scripts/smoke-model-fallback.sh +3 -3
  99. package/pipeline/scripts/smoke-multi-repo-integration.sh +2 -2
  100. package/pipeline/scripts/smoke-multi-repo-worktree.sh +1 -1
  101. package/pipeline/scripts/smoke-no-token-prompt.sh +5 -5
  102. package/pipeline/scripts/smoke-phase-0-multi-repo.sh +1 -1
  103. package/pipeline/scripts/smoke-phase-6-multi.sh +1 -1
  104. package/pipeline/scripts/smoke-phase4-gates.sh +45 -0
  105. package/pipeline/scripts/smoke-plan-approval-gate.sh +5 -5
  106. package/pipeline/scripts/smoke-plan-todos.sh +3 -3
  107. package/pipeline/scripts/smoke-pr-review-actions.sh +3 -3
  108. package/pipeline/scripts/smoke-prefs-language.sh +1 -1
  109. package/pipeline/scripts/smoke-progress-contract.sh +2 -2
  110. package/pipeline/scripts/smoke-push-retry.sh +1 -1
  111. package/pipeline/scripts/smoke-repo-map.sh +2 -2
  112. package/pipeline/scripts/smoke-shadow-git.sh +1 -1
  113. package/pipeline/scripts/smoke-skill-authoring.sh +4 -9
  114. package/pipeline/scripts/smoke-tasklist-ordering.sh +10 -10
  115. package/pipeline/scripts/smoke-test-gap.sh +1 -1
  116. package/pipeline/scripts/smoke-token-budget.sh +1 -1
  117. package/pipeline/scripts/smoke-token-preflight.sh +2 -2
  118. package/pipeline/scripts/smoke-tracker-contract.sh +13 -13
  119. package/pipeline/scripts/smoke-tracker-tokens-invocation.sh +5 -5
  120. package/pipeline/scripts/smoke-triage-memory.sh +2 -2
  121. package/pipeline/scripts/smoke-update-check.sh +1 -1
  122. package/pipeline/scripts/smoke-url-enrichment.sh +2 -2
  123. package/pipeline/scripts/smoke-validator-gates.sh +1 -1
  124. package/pipeline/scripts/smoke-verify-by-test.sh +3 -3
  125. package/pipeline/scripts/smoke-wiki-integration.sh +9 -33
  126. package/pipeline/scripts/smoke-work-summary.sh +1 -1
  127. package/pipeline/scripts/smoke-worktree-path-convention.sh +4 -4
  128. package/pipeline/scripts/test-integrity-gate.mjs +83 -0
  129. package/pipeline/scripts/uninstall.mjs +2 -2
  130. package/pipeline/skills/.skill-manifest.json +40 -160
  131. package/pipeline/skills/.skills-index.json +21 -372
  132. package/pipeline/skills/shared/README.md +1 -1
  133. package/pipeline/skills/shared/core/multi-agent/SKILL.md +1 -1
  134. package/pipeline/skills/shared/core/multi-agent-analysis/SKILL.md +2 -2
  135. package/pipeline/skills/shared/core/multi-agent-analysis-resolve/SKILL.md +3 -3
  136. package/pipeline/skills/shared/core/multi-agent-build-optimize/SKILL.md +1 -1
  137. package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +1 -1
  138. package/pipeline/skills/shared/core/multi-agent-finish/SKILL.md +1 -1
  139. package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +1 -1
  140. package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +116 -51
  141. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +8 -30
  142. package/pipeline/skills/shared/external/backlog/BACKLOG.md +32 -0
  143. package/pipeline/skills/shared/external/backlog/SKILL.md +49 -0
  144. package/pipeline/skills/shared/external/compose-components/SKILL.md +14 -14
  145. package/pipeline/skills/shared/external/skill-creator/SKILL.md +48 -0
  146. package/pipeline/skills/shared/external/skill-creator/audit.md +59 -0
  147. package/pipeline/skills/shared/external/skill-creator/checklist.md +32 -0
  148. package/pipeline/skills/shared/external/skill-creator/examples.md +65 -0
  149. package/pipeline/skills/shared/external/skill-creator/label-check.md +43 -0
  150. package/pipeline/skills/shared/external/skill-creator/scripts/audit-panel.js +83 -0
  151. package/pipeline/skills/shared/external/skill-creator/template.md +67 -0
  152. package/pipeline/skills/skills-index.md +4 -43
  153. package/pipeline/commands/multi-agent/refactor.md +0 -188
  154. package/pipeline/commands/multi-agent/refs/component-dispatch.md +0 -92
  155. package/pipeline/scripts/smoke-existing-discovery-gate.sh +0 -71
  156. package/pipeline/scripts/smoke-figma-android-parity.sh +0 -148
  157. package/pipeline/scripts/smoke-figma-credential-store.sh +0 -105
  158. package/pipeline/scripts/smoke-figma-cross-cli-inventory.sh +0 -177
  159. package/pipeline/scripts/smoke-figma-sync.sh +0 -149
  160. package/pipeline/scripts/smoke-sync-delegation.sh +0 -74
  161. package/pipeline/skills/figma-android/README.md +0 -38
  162. package/pipeline/skills/figma-android/figma-component-code-connect/SKILL.md +0 -62
  163. package/pipeline/skills/figma-android/figma-component-implement/SKILL.md +0 -158
  164. package/pipeline/skills/figma-android/figma-component-test/SKILL.md +0 -120
  165. package/pipeline/skills/figma-android/figma-component-wiki/SKILL.md +0 -35
  166. package/pipeline/skills/figma-android/figma-to-component/SKILL.md +0 -124
  167. package/pipeline/skills/figma-common/README.md +0 -64
  168. package/pipeline/skills/figma-common/figma-bottom-sheets/SKILL.md +0 -152
  169. package/pipeline/skills/figma-common/figma-cli-iterate/SKILL.md +0 -282
  170. package/pipeline/skills/figma-common/figma-cli-iterate-mend/SKILL.md +0 -503
  171. package/pipeline/skills/figma-common/figma-cli-lean-iterate/SKILL.md +0 -288
  172. package/pipeline/skills/figma-common/figma-cli-skip/SKILL.md +0 -362
  173. package/pipeline/skills/figma-common/figma-commit/COMMON_REBASE.md +0 -206
  174. package/pipeline/skills/figma-common/figma-commit/REVIEW.md +0 -337
  175. package/pipeline/skills/figma-common/figma-commit/SKILL.md +0 -216
  176. package/pipeline/skills/figma-common/figma-component-confluence-sync/SKILL.md +0 -223
  177. package/pipeline/skills/figma-common/figma-component-start/SKILL.md +0 -251
  178. package/pipeline/skills/figma-common/figma-component-status-update/SKILL.md +0 -78
  179. package/pipeline/skills/figma-common/figma-evolve-component/SKILL.md +0 -61
  180. package/pipeline/skills/figma-common/figma-fix/SKILL.md +0 -321
  181. package/pipeline/skills/figma-common/figma-form-integration/SKILL.md +0 -542
  182. package/pipeline/skills/figma-common/figma-issue/SKILL.md +0 -750
  183. package/pipeline/skills/figma-common/figma-iterate/SKILL.md +0 -208
  184. package/pipeline/skills/figma-common/figma-iteration-commit/SKILL.md +0 -1020
  185. package/pipeline/skills/figma-common/figma-mend/SKILL.md +0 -331
  186. package/pipeline/skills/figma-common/figma-navigation/SKILL.md +0 -156
  187. package/pipeline/skills/figma-common/figma-overlays/SKILL.md +0 -142
  188. package/pipeline/skills/figma-common/figma-price-integration/SKILL.md +0 -398
  189. package/pipeline/skills/figma-common/figma-remote-mcp-auth/SKILL.md +0 -104
  190. package/pipeline/skills/figma-common/figma-review/SKILL.md +0 -400
  191. package/pipeline/skills/figma-common/figma-setup/SKILL.md +0 -514
  192. package/pipeline/skills/figma-common/figma-setup/scripts/fetch-mcp-token.py +0 -592
  193. package/pipeline/skills/figma-common/figma-skip/SKILL.md +0 -129
  194. package/pipeline/skills/figma-common/figma-ui-patterns/SKILL.md +0 -105
  195. package/pipeline/skills/figma-common/figma-ui-patterns/patterns/animated-gradient-border.md +0 -116
  196. package/pipeline/skills/figma-common/figma-utility/SKILL.md +0 -274
  197. package/pipeline/skills/figma-common/figma-utility/scripts/figma-utility.py +0 -808
  198. package/pipeline/skills/figma-common/figma-validate/SKILL.md +0 -633
  199. package/pipeline/skills/figma-common/performance-iteration-commit-all/SKILL.md +0 -716
  200. package/pipeline/skills/figma-common/performance-review-next/SKILL.md +0 -238
  201. package/pipeline/skills/figma-common/performance-start/SKILL.md +0 -430
  202. package/pipeline/skills/figma-common/performance-swiftui/SKILL.md +0 -706
  203. package/pipeline/skills/figma-common/performance-tour/SKILL.md +0 -423
  204. package/pipeline/skills/figma-ios/REVIEW_CHECKLIST.md +0 -67
  205. package/pipeline/skills/figma-ios/figma-component-code-connect/SKILL.md +0 -178
  206. package/pipeline/skills/figma-ios/figma-component-implement/SKILL.md +0 -184
  207. package/pipeline/skills/figma-ios/figma-component-test/SKILL.md +0 -219
  208. package/pipeline/skills/figma-ios/figma-component-wiki/SKILL.md +0 -274
  209. package/pipeline/skills/figma-ios/figma-to-component/SKILL.md +0 -416
  210. package/pipeline/skills/figma-ios/figma-to-component/halt-return-protocol.md +0 -57
  211. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-0-init.md +0 -307
  212. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-1-gathering.md +0 -119
  213. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-1.5-existing-discovery.md +0 -174
  214. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2-orchestrator.md +0 -333
  215. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2a-testing-identifiers.md +0 -368
  216. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2b-localization.md +0 -393
  217. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2c-accessibility.md +0 -617
  218. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2d-analytics.md +0 -352
  219. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3-orchestrator.md +0 -337
  220. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3a-location.md +0 -206
  221. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3b-tokens.md +0 -235
  222. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3c-nested.md +0 -214
  223. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3d-patterns.md +0 -902
  224. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3e-assets.md +0 -156
  225. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3f-utilities.md +0 -175
  226. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3g-property-coverage.md +0 -176
  227. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3h-variant-config.md +0 -333
  228. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4-orchestrator.md +0 -412
  229. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4a-configuration.md +0 -336
  230. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4b-view.md +0 -705
  231. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4c-documentation.md +0 -332
  232. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4d-preview.md +0 -380
  233. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4e-modifiers.md +0 -262
  234. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5-orchestrator.md +0 -482
  235. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5a-viewinspector.md +0 -274
  236. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5b-snapshot.md +0 -636
  237. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5c-unit.md +0 -142
  238. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-6-code-connect.md +0 -547
  239. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-7-wiki.md +0 -39
  240. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-7a-confluence-generate.md +0 -659
  241. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-7a-wiki-generate.md +0 -580
  242. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-8-cleanup.md +0 -51
  243. package/pipeline/skills/figma-ios/figma-to-component/reference/accessibility.md +0 -183
  244. package/pipeline/skills/figma-ios/figma-to-component/reference/analytics-events.md +0 -64
  245. package/pipeline/skills/figma-ios/figma-to-component/reference/code-connect.md +0 -531
  246. package/pipeline/skills/figma-ios/figma-to-component/reference/confluence-api.md +0 -89
  247. package/pipeline/skills/figma-ios/figma-to-component/reference/confluence-xhtml.md +0 -155
  248. package/pipeline/skills/figma-ios/figma-to-component/reference/figma-to-swiftui-effects.md +0 -196
  249. package/pipeline/skills/figma-ios/figma-to-component/reference/halt-return-protocol.md +0 -57
  250. package/pipeline/skills/figma-ios/figma-to-component/reference/localization-naming.md +0 -89
  251. package/pipeline/skills/figma-ios/figma-to-component/reference/macros.md +0 -227
  252. package/pipeline/skills/figma-ios/figma-to-component/reference/missing-tokens.md +0 -157
  253. package/pipeline/skills/figma-ios/figma-to-component/reference/orchestrator-discipline.md +0 -90
  254. package/pipeline/skills/figma-ios/figma-to-component/reference/registry.md +0 -116
  255. package/pipeline/skills/figma-ios/figma-to-component/reference/remote-mcp-script.md +0 -153
  256. package/pipeline/skills/figma-ios/figma-to-component/reference/rest-api-script.md +0 -130
  257. package/pipeline/skills/figma-ios/figma-to-component/reference/scripts-inventory.md +0 -218
  258. package/pipeline/skills/figma-ios/figma-to-component/reference/snapshot-testing.md +0 -188
  259. package/pipeline/skills/figma-ios/figma-to-component/reference/subcomponent-graph.md +0 -93
  260. package/pipeline/skills/figma-ios/figma-to-component/reference/testing-identifiers-naming.md +0 -98
  261. package/pipeline/skills/figma-ios/figma-to-component/reference/tools.md +0 -261
  262. package/pipeline/skills/figma-ios/figma-to-component/reference/viewinspector.md +0 -147
  263. package/pipeline/skills/figma-ios/figma-to-component/reference/wiki-to-confluence-mapping.md +0 -182
  264. package/pipeline/skills/figma-ios/figma-to-component/scripts/apply-author-login-map.py +0 -185
  265. package/pipeline/skills/figma-ios/figma-to-component/scripts/backfill-status.py +0 -609
  266. package/pipeline/skills/figma-ios/figma-to-component/scripts/build-author-registry.py +0 -332
  267. package/pipeline/skills/figma-ios/figma-to-component/scripts/bulk-sync-issues.py +0 -261
  268. package/pipeline/skills/figma-ios/figma-to-component/scripts/code-connect-data-gather.py +0 -184
  269. package/pipeline/skills/figma-ios/figma-to-component/scripts/code-connect-publish.sh +0 -188
  270. package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-component-status-upload.py +0 -768
  271. package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-component-status.py +0 -191
  272. package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-data-gather.py +0 -420
  273. package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-page-ids.example.json +0 -9
  274. package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-publish.py +0 -336
  275. package/pipeline/skills/figma-ios/figma-to-component/scripts/figma-subcomponent-graph.py +0 -391
  276. package/pipeline/skills/figma-ios/figma-to-component/scripts/figma-update.py +0 -292
  277. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/__init__.py +0 -1
  278. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/issue_sync_propagate.py +0 -93
  279. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/registry_writer.py +0 -299
  280. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_backfill_status.py +0 -343
  281. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_figma_update.py +0 -206
  282. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_figma_update_http.py +0 -149
  283. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_phase_clis.py +0 -281
  284. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_registry_writer.py +0 -332
  285. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_skill_figma_issue.py +0 -176
  286. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_skill_figma_review.py +0 -98
  287. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_update_issue.py +0 -298
  288. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_update_issue_gh.py +0 -195
  289. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase1-gather.py +0 -1298
  290. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase2-finalize.py +0 -228
  291. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase3-scripts.py +0 -1089
  292. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase4-finalize.py +0 -141
  293. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase5-finalize.py +0 -106
  294. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase6-finalize.py +0 -162
  295. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase7-finalize.py +0 -105
  296. package/pipeline/skills/figma-ios/figma-to-component/scripts/register-icons-codeconnect.py +0 -179
  297. package/pipeline/skills/figma-ios/figma-to-component/scripts/remote-mcp-fetch.py +0 -260
  298. package/pipeline/skills/figma-ios/figma-to-component/scripts/resolve-author-logins.py +0 -260
  299. package/pipeline/skills/figma-ios/figma-to-component/scripts/run-uicomponents-tests.sh +0 -86
  300. package/pipeline/skills/figma-ios/figma-to-component/scripts/sidebar-generator.py +0 -321
  301. package/pipeline/skills/figma-ios/figma-to-component/scripts/update-issue-from-registry.py +0 -1470
  302. package/pipeline/skills/figma-ios/figma-to-component/scripts/validate-phase4.sh +0 -176
  303. package/pipeline/skills/figma-ios/figma-to-component/scripts/validate-phase6.sh +0 -147
  304. package/pipeline/skills/figma-ios/figma-to-component/scripts/validate-phase7a.py +0 -629
  305. /package/pipeline/commands/multi-agent/{delete.md → delete/SKILL.md} +0 -0
  306. /package/pipeline/commands/multi-agent/{diff-explain.md → diff-explain/SKILL.md} +0 -0
  307. /package/pipeline/commands/multi-agent/{garbage-collect.md → garbage-collect/SKILL.md} +0 -0
  308. /package/pipeline/commands/multi-agent/{issue.md → issue/SKILL.md} +0 -0
  309. /package/pipeline/commands/multi-agent/{jira.md → jira/SKILL.md} +0 -0
  310. /package/pipeline/commands/multi-agent/{kill.md → kill/SKILL.md} +0 -0
  311. /package/pipeline/commands/multi-agent/{language.md → language/SKILL.md} +0 -0
  312. /package/pipeline/commands/multi-agent/{log.md → log/SKILL.md} +0 -0
  313. /package/pipeline/commands/multi-agent/{prune-logs.md → prune-logs/SKILL.md} +0 -0
  314. /package/pipeline/commands/multi-agent/{purge.md → purge/SKILL.md} +0 -0
  315. /package/pipeline/commands/multi-agent/{scan.md → scan/SKILL.md} +0 -0
  316. /package/pipeline/commands/multi-agent/{search.md → search/SKILL.md} +0 -0
  317. /package/pipeline/commands/multi-agent/{stack.md → stack/SKILL.md} +0 -0
  318. /package/pipeline/commands/multi-agent/{status.md → status/SKILL.md} +0 -0
  319. /package/pipeline/commands/multi-agent/{test.md → test/SKILL.md} +0 -0
  320. /package/pipeline/commands/multi-agent/{update.md → update/SKILL.md} +0 -0
  321. /package/pipeline/{commands/multi-agent → multi-agent-refs}/_input-parser.md +0 -0
  322. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/analysis-template.md +0 -0
  323. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/audit-guide.md +0 -0
  324. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/backend-guide.md +0 -0
  325. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/confluence.md +0 -0
  326. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/issue-comment.md +0 -0
  327. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/pr-review-actions.md +0 -0
  328. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/pr.md +0 -0
  329. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/conventions-defaults.md +0 -0
  330. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/autopilot-circuit-breaker.md +0 -0
  331. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/dev-critic.md +0 -0
  332. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/external-context-injection.md +0 -0
  333. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/model-fallback.md +0 -0
  334. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/plan-todos.md +0 -0
  335. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/prior-fix-detection.md +0 -0
  336. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/repo-map.md +0 -0
  337. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/shadow-git.md +0 -0
  338. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/frontend-guide.md +0 -0
  339. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/knowledge.md +0 -0
  340. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/multi-repo-integration-build.md +0 -0
  341. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/picker-contract.md +0 -0
  342. /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."""