@mmerterden/multi-agent-pipeline 11.2.0 → 11.3.1

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