@mmerterden/multi-agent-pipeline 11.2.0 → 11.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (331) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +2 -2
  3. package/docs/FIGMA_PIPELINE.md +12 -25
  4. package/docs/architecture.md +3 -5
  5. package/docs/features.md +8 -8
  6. package/install/_dev-only-files.mjs +0 -6
  7. package/install/claude.mjs +33 -15
  8. package/install/copilot.mjs +16 -14
  9. package/install/templates/copilot-instructions.md +3 -3
  10. package/package.json +1 -1
  11. package/pipeline/commands/{multi-agent.md → multi-agent/SKILL.md} +39 -39
  12. package/pipeline/commands/multi-agent/{analysis.md → analysis/SKILL.md} +18 -18
  13. package/pipeline/commands/multi-agent/{analysis-resolve.md → analysis-resolve/SKILL.md} +4 -4
  14. package/pipeline/commands/multi-agent/{autopilot.md → autopilot/SKILL.md} +4 -4
  15. package/pipeline/commands/multi-agent/{build-optimize.md → build-optimize/SKILL.md} +1 -1
  16. package/pipeline/commands/multi-agent/{channels.md → channels/SKILL.md} +12 -12
  17. package/pipeline/commands/multi-agent/{dev.md → dev/SKILL.md} +4 -4
  18. package/pipeline/commands/multi-agent/{dev-autopilot.md → dev-autopilot/SKILL.md} +4 -4
  19. package/pipeline/commands/multi-agent/{dev-local.md → dev-local/SKILL.md} +5 -5
  20. package/pipeline/commands/multi-agent/{dev-local-autopilot.md → dev-local-autopilot/SKILL.md} +4 -4
  21. package/pipeline/commands/multi-agent/{finish.md → finish/SKILL.md} +6 -6
  22. package/pipeline/commands/multi-agent/{generate.md → generate/SKILL.md} +3 -3
  23. package/pipeline/commands/multi-agent/{help.md → help/SKILL.md} +4 -4
  24. package/pipeline/commands/multi-agent/{local.md → local/SKILL.md} +5 -5
  25. package/pipeline/commands/multi-agent/{local-autopilot.md → local-autopilot/SKILL.md} +5 -5
  26. package/pipeline/commands/multi-agent/{manual-test.md → manual-test/SKILL.md} +1 -1
  27. package/pipeline/commands/multi-agent/refactor/SKILL.md +257 -0
  28. package/pipeline/commands/multi-agent/{resume.md → resume/SKILL.md} +1 -1
  29. package/pipeline/commands/multi-agent/{review.md → review/SKILL.md} +5 -5
  30. package/pipeline/commands/multi-agent/{setup.md → setup/SKILL.md} +1 -1
  31. package/pipeline/commands/multi-agent/{sync.md → sync/SKILL.md} +20 -58
  32. package/pipeline/{commands/multi-agent → multi-agent-refs}/_account-picker.md +2 -2
  33. package/pipeline/{commands/multi-agent → multi-agent-refs}/_dev-context.md +2 -2
  34. package/pipeline/{commands/multi-agent → multi-agent-refs}/_repo-picker.md +2 -2
  35. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/android-guide.md +1 -1
  36. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/jira.md +1 -1
  37. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/wiki.md +7 -7
  38. package/pipeline/multi-agent-refs/component-dispatch.md +92 -0
  39. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/cross-cli-contract.md +10 -77
  40. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/verify-by-test.md +1 -1
  41. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/generate-issue.md +2 -2
  42. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/issue-jira-triad.md +1 -1
  43. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/keychain.md +1 -1
  44. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/log-format.md +1 -1
  45. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/modes.md +2 -2
  46. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/operations.md +1 -1
  47. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-0-init.md +10 -10
  48. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-1-analysis.md +6 -6
  49. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-2-planning.md +3 -3
  50. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-3-dev.md +10 -10
  51. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-4-review.md +8 -8
  52. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-5-test.md +3 -3
  53. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-6-commit.md +4 -4
  54. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-7-report.md +6 -6
  55. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases.md +12 -12
  56. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/progress-contract.md +4 -4
  57. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/swiftui-guide.md +1 -1
  58. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/tracker-contract.md +3 -3
  59. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/wiki-capture.md +4 -4
  60. package/pipeline/preferences-template.json +17 -1
  61. package/pipeline/rules/figma-pipeline.md +2 -2
  62. package/pipeline/schemas/prefs.schema.json +0 -34
  63. package/pipeline/scripts/README.md +1 -3
  64. package/pipeline/scripts/fixtures/install-layout.tsv +6 -5
  65. package/pipeline/scripts/gen-mode-dispatch.mjs +3 -3
  66. package/pipeline/scripts/phase-banner.sh +2 -2
  67. package/pipeline/scripts/review-scope.mjs +94 -0
  68. package/pipeline/scripts/scan-agent-config.sh +2 -2
  69. package/pipeline/scripts/smoke-agent-log-cost.sh +1 -1
  70. package/pipeline/scripts/smoke-agent-model-routing.sh +4 -4
  71. package/pipeline/scripts/smoke-autopilot-circuit-breaker.sh +2 -2
  72. package/pipeline/scripts/smoke-channels-approval-gate.sh +2 -2
  73. package/pipeline/scripts/smoke-channels-flow.sh +5 -5
  74. package/pipeline/scripts/smoke-clarify.sh +1 -1
  75. package/pipeline/scripts/smoke-commands-skills-parity.sh +3 -3
  76. package/pipeline/scripts/smoke-community-gates.sh +1 -1
  77. package/pipeline/scripts/smoke-compliance-skills.sh +2 -2
  78. package/pipeline/scripts/smoke-cost-summary.sh +1 -1
  79. package/pipeline/scripts/smoke-cross-cli-behavior.sh +17 -43
  80. package/pipeline/scripts/smoke-cross-phase-cohesion.sh +1 -1
  81. package/pipeline/scripts/smoke-dev-critic.sh +1 -1
  82. package/pipeline/scripts/smoke-diff-explain.sh +1 -1
  83. package/pipeline/scripts/smoke-diff-risk.sh +1 -1
  84. package/pipeline/scripts/smoke-evidence-gate.sh +1 -1
  85. package/pipeline/scripts/smoke-figma-dispatch.sh +46 -57
  86. package/pipeline/scripts/smoke-gate-hooks.sh +1 -1
  87. package/pipeline/scripts/smoke-generate-issue.sh +8 -8
  88. package/pipeline/scripts/smoke-handoff-contract.sh +3 -3
  89. package/pipeline/scripts/smoke-identity-isolation.sh +2 -2
  90. package/pipeline/scripts/smoke-intent-guard.sh +1 -1
  91. package/pipeline/scripts/smoke-issue-comment-template.sh +2 -2
  92. package/pipeline/scripts/smoke-issue-jira-triad.sh +4 -4
  93. package/pipeline/scripts/smoke-language-axis.sh +6 -6
  94. package/pipeline/scripts/smoke-learnings-ledger.sh +3 -3
  95. package/pipeline/scripts/smoke-mode-dispatch-drift.sh +16 -16
  96. package/pipeline/scripts/smoke-model-fallback.sh +3 -3
  97. package/pipeline/scripts/smoke-multi-repo-integration.sh +2 -2
  98. package/pipeline/scripts/smoke-multi-repo-worktree.sh +1 -1
  99. package/pipeline/scripts/smoke-no-token-prompt.sh +5 -5
  100. package/pipeline/scripts/smoke-phase-0-multi-repo.sh +1 -1
  101. package/pipeline/scripts/smoke-phase-6-multi.sh +1 -1
  102. package/pipeline/scripts/smoke-phase4-gates.sh +45 -0
  103. package/pipeline/scripts/smoke-plan-approval-gate.sh +5 -5
  104. package/pipeline/scripts/smoke-plan-todos.sh +3 -3
  105. package/pipeline/scripts/smoke-pr-review-actions.sh +3 -3
  106. package/pipeline/scripts/smoke-prefs-language.sh +1 -1
  107. package/pipeline/scripts/smoke-progress-contract.sh +2 -2
  108. package/pipeline/scripts/smoke-push-retry.sh +1 -1
  109. package/pipeline/scripts/smoke-repo-map.sh +2 -2
  110. package/pipeline/scripts/smoke-shadow-git.sh +1 -1
  111. package/pipeline/scripts/smoke-skill-authoring.sh +4 -9
  112. package/pipeline/scripts/smoke-tasklist-ordering.sh +10 -10
  113. package/pipeline/scripts/smoke-test-gap.sh +1 -1
  114. package/pipeline/scripts/smoke-token-budget.sh +1 -1
  115. package/pipeline/scripts/smoke-token-preflight.sh +2 -2
  116. package/pipeline/scripts/smoke-tracker-contract.sh +13 -13
  117. package/pipeline/scripts/smoke-tracker-tokens-invocation.sh +5 -5
  118. package/pipeline/scripts/smoke-triage-memory.sh +2 -2
  119. package/pipeline/scripts/smoke-update-check.sh +1 -1
  120. package/pipeline/scripts/smoke-url-enrichment.sh +2 -2
  121. package/pipeline/scripts/smoke-validator-gates.sh +1 -1
  122. package/pipeline/scripts/smoke-verify-by-test.sh +3 -3
  123. package/pipeline/scripts/smoke-wiki-integration.sh +9 -33
  124. package/pipeline/scripts/smoke-work-summary.sh +1 -1
  125. package/pipeline/scripts/smoke-worktree-path-convention.sh +4 -4
  126. package/pipeline/scripts/test-integrity-gate.mjs +83 -0
  127. package/pipeline/scripts/uninstall.mjs +2 -2
  128. package/pipeline/skills/.skill-manifest.json +40 -160
  129. package/pipeline/skills/.skills-index.json +3 -372
  130. package/pipeline/skills/shared/README.md +1 -1
  131. package/pipeline/skills/shared/core/multi-agent/SKILL.md +1 -1
  132. package/pipeline/skills/shared/core/multi-agent-analysis/SKILL.md +2 -2
  133. package/pipeline/skills/shared/core/multi-agent-analysis-resolve/SKILL.md +3 -3
  134. package/pipeline/skills/shared/core/multi-agent-build-optimize/SKILL.md +1 -1
  135. package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +1 -1
  136. package/pipeline/skills/shared/core/multi-agent-finish/SKILL.md +1 -1
  137. package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +1 -1
  138. package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +116 -51
  139. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +8 -30
  140. package/pipeline/skills/shared/external/compose-components/SKILL.md +14 -14
  141. package/pipeline/skills/skills-index.md +2 -43
  142. package/pipeline/commands/multi-agent/refactor.md +0 -188
  143. package/pipeline/commands/multi-agent/refs/component-dispatch.md +0 -92
  144. package/pipeline/scripts/smoke-existing-discovery-gate.sh +0 -71
  145. package/pipeline/scripts/smoke-figma-android-parity.sh +0 -148
  146. package/pipeline/scripts/smoke-figma-credential-store.sh +0 -105
  147. package/pipeline/scripts/smoke-figma-cross-cli-inventory.sh +0 -177
  148. package/pipeline/scripts/smoke-figma-sync.sh +0 -149
  149. package/pipeline/scripts/smoke-sync-delegation.sh +0 -74
  150. package/pipeline/skills/figma-android/README.md +0 -38
  151. package/pipeline/skills/figma-android/figma-component-code-connect/SKILL.md +0 -62
  152. package/pipeline/skills/figma-android/figma-component-implement/SKILL.md +0 -158
  153. package/pipeline/skills/figma-android/figma-component-test/SKILL.md +0 -120
  154. package/pipeline/skills/figma-android/figma-component-wiki/SKILL.md +0 -35
  155. package/pipeline/skills/figma-android/figma-to-component/SKILL.md +0 -124
  156. package/pipeline/skills/figma-common/README.md +0 -64
  157. package/pipeline/skills/figma-common/figma-bottom-sheets/SKILL.md +0 -152
  158. package/pipeline/skills/figma-common/figma-cli-iterate/SKILL.md +0 -282
  159. package/pipeline/skills/figma-common/figma-cli-iterate-mend/SKILL.md +0 -503
  160. package/pipeline/skills/figma-common/figma-cli-lean-iterate/SKILL.md +0 -288
  161. package/pipeline/skills/figma-common/figma-cli-skip/SKILL.md +0 -362
  162. package/pipeline/skills/figma-common/figma-commit/COMMON_REBASE.md +0 -206
  163. package/pipeline/skills/figma-common/figma-commit/REVIEW.md +0 -337
  164. package/pipeline/skills/figma-common/figma-commit/SKILL.md +0 -216
  165. package/pipeline/skills/figma-common/figma-component-confluence-sync/SKILL.md +0 -223
  166. package/pipeline/skills/figma-common/figma-component-start/SKILL.md +0 -251
  167. package/pipeline/skills/figma-common/figma-component-status-update/SKILL.md +0 -78
  168. package/pipeline/skills/figma-common/figma-evolve-component/SKILL.md +0 -61
  169. package/pipeline/skills/figma-common/figma-fix/SKILL.md +0 -321
  170. package/pipeline/skills/figma-common/figma-form-integration/SKILL.md +0 -542
  171. package/pipeline/skills/figma-common/figma-issue/SKILL.md +0 -750
  172. package/pipeline/skills/figma-common/figma-iterate/SKILL.md +0 -208
  173. package/pipeline/skills/figma-common/figma-iteration-commit/SKILL.md +0 -1020
  174. package/pipeline/skills/figma-common/figma-mend/SKILL.md +0 -331
  175. package/pipeline/skills/figma-common/figma-navigation/SKILL.md +0 -156
  176. package/pipeline/skills/figma-common/figma-overlays/SKILL.md +0 -142
  177. package/pipeline/skills/figma-common/figma-price-integration/SKILL.md +0 -398
  178. package/pipeline/skills/figma-common/figma-remote-mcp-auth/SKILL.md +0 -104
  179. package/pipeline/skills/figma-common/figma-review/SKILL.md +0 -400
  180. package/pipeline/skills/figma-common/figma-setup/SKILL.md +0 -514
  181. package/pipeline/skills/figma-common/figma-setup/scripts/fetch-mcp-token.py +0 -592
  182. package/pipeline/skills/figma-common/figma-skip/SKILL.md +0 -129
  183. package/pipeline/skills/figma-common/figma-ui-patterns/SKILL.md +0 -105
  184. package/pipeline/skills/figma-common/figma-ui-patterns/patterns/animated-gradient-border.md +0 -116
  185. package/pipeline/skills/figma-common/figma-utility/SKILL.md +0 -274
  186. package/pipeline/skills/figma-common/figma-utility/scripts/figma-utility.py +0 -808
  187. package/pipeline/skills/figma-common/figma-validate/SKILL.md +0 -633
  188. package/pipeline/skills/figma-common/performance-iteration-commit-all/SKILL.md +0 -716
  189. package/pipeline/skills/figma-common/performance-review-next/SKILL.md +0 -238
  190. package/pipeline/skills/figma-common/performance-start/SKILL.md +0 -430
  191. package/pipeline/skills/figma-common/performance-swiftui/SKILL.md +0 -706
  192. package/pipeline/skills/figma-common/performance-tour/SKILL.md +0 -423
  193. package/pipeline/skills/figma-ios/REVIEW_CHECKLIST.md +0 -67
  194. package/pipeline/skills/figma-ios/figma-component-code-connect/SKILL.md +0 -178
  195. package/pipeline/skills/figma-ios/figma-component-implement/SKILL.md +0 -184
  196. package/pipeline/skills/figma-ios/figma-component-test/SKILL.md +0 -219
  197. package/pipeline/skills/figma-ios/figma-component-wiki/SKILL.md +0 -274
  198. package/pipeline/skills/figma-ios/figma-to-component/SKILL.md +0 -416
  199. package/pipeline/skills/figma-ios/figma-to-component/halt-return-protocol.md +0 -57
  200. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-0-init.md +0 -307
  201. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-1-gathering.md +0 -119
  202. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-1.5-existing-discovery.md +0 -174
  203. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2-orchestrator.md +0 -333
  204. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2a-testing-identifiers.md +0 -368
  205. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2b-localization.md +0 -393
  206. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2c-accessibility.md +0 -617
  207. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2d-analytics.md +0 -352
  208. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3-orchestrator.md +0 -337
  209. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3a-location.md +0 -206
  210. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3b-tokens.md +0 -235
  211. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3c-nested.md +0 -214
  212. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3d-patterns.md +0 -902
  213. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3e-assets.md +0 -156
  214. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3f-utilities.md +0 -175
  215. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3g-property-coverage.md +0 -176
  216. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3h-variant-config.md +0 -333
  217. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4-orchestrator.md +0 -412
  218. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4a-configuration.md +0 -336
  219. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4b-view.md +0 -705
  220. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4c-documentation.md +0 -332
  221. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4d-preview.md +0 -380
  222. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4e-modifiers.md +0 -262
  223. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5-orchestrator.md +0 -482
  224. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5a-viewinspector.md +0 -274
  225. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5b-snapshot.md +0 -636
  226. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5c-unit.md +0 -142
  227. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-6-code-connect.md +0 -547
  228. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-7-wiki.md +0 -39
  229. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-7a-confluence-generate.md +0 -659
  230. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-7a-wiki-generate.md +0 -580
  231. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-8-cleanup.md +0 -51
  232. package/pipeline/skills/figma-ios/figma-to-component/reference/accessibility.md +0 -183
  233. package/pipeline/skills/figma-ios/figma-to-component/reference/analytics-events.md +0 -64
  234. package/pipeline/skills/figma-ios/figma-to-component/reference/code-connect.md +0 -531
  235. package/pipeline/skills/figma-ios/figma-to-component/reference/confluence-api.md +0 -89
  236. package/pipeline/skills/figma-ios/figma-to-component/reference/confluence-xhtml.md +0 -155
  237. package/pipeline/skills/figma-ios/figma-to-component/reference/figma-to-swiftui-effects.md +0 -196
  238. package/pipeline/skills/figma-ios/figma-to-component/reference/halt-return-protocol.md +0 -57
  239. package/pipeline/skills/figma-ios/figma-to-component/reference/localization-naming.md +0 -89
  240. package/pipeline/skills/figma-ios/figma-to-component/reference/macros.md +0 -227
  241. package/pipeline/skills/figma-ios/figma-to-component/reference/missing-tokens.md +0 -157
  242. package/pipeline/skills/figma-ios/figma-to-component/reference/orchestrator-discipline.md +0 -90
  243. package/pipeline/skills/figma-ios/figma-to-component/reference/registry.md +0 -116
  244. package/pipeline/skills/figma-ios/figma-to-component/reference/remote-mcp-script.md +0 -153
  245. package/pipeline/skills/figma-ios/figma-to-component/reference/rest-api-script.md +0 -130
  246. package/pipeline/skills/figma-ios/figma-to-component/reference/scripts-inventory.md +0 -218
  247. package/pipeline/skills/figma-ios/figma-to-component/reference/snapshot-testing.md +0 -188
  248. package/pipeline/skills/figma-ios/figma-to-component/reference/subcomponent-graph.md +0 -93
  249. package/pipeline/skills/figma-ios/figma-to-component/reference/testing-identifiers-naming.md +0 -98
  250. package/pipeline/skills/figma-ios/figma-to-component/reference/tools.md +0 -261
  251. package/pipeline/skills/figma-ios/figma-to-component/reference/viewinspector.md +0 -147
  252. package/pipeline/skills/figma-ios/figma-to-component/reference/wiki-to-confluence-mapping.md +0 -182
  253. package/pipeline/skills/figma-ios/figma-to-component/scripts/apply-author-login-map.py +0 -185
  254. package/pipeline/skills/figma-ios/figma-to-component/scripts/backfill-status.py +0 -609
  255. package/pipeline/skills/figma-ios/figma-to-component/scripts/build-author-registry.py +0 -332
  256. package/pipeline/skills/figma-ios/figma-to-component/scripts/bulk-sync-issues.py +0 -261
  257. package/pipeline/skills/figma-ios/figma-to-component/scripts/code-connect-data-gather.py +0 -184
  258. package/pipeline/skills/figma-ios/figma-to-component/scripts/code-connect-publish.sh +0 -188
  259. package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-component-status-upload.py +0 -768
  260. package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-component-status.py +0 -191
  261. package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-data-gather.py +0 -420
  262. package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-page-ids.example.json +0 -9
  263. package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-publish.py +0 -336
  264. package/pipeline/skills/figma-ios/figma-to-component/scripts/figma-subcomponent-graph.py +0 -391
  265. package/pipeline/skills/figma-ios/figma-to-component/scripts/figma-update.py +0 -292
  266. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/__init__.py +0 -1
  267. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/issue_sync_propagate.py +0 -93
  268. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/registry_writer.py +0 -299
  269. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_backfill_status.py +0 -343
  270. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_figma_update.py +0 -206
  271. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_figma_update_http.py +0 -149
  272. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_phase_clis.py +0 -281
  273. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_registry_writer.py +0 -332
  274. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_skill_figma_issue.py +0 -176
  275. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_skill_figma_review.py +0 -98
  276. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_update_issue.py +0 -298
  277. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_update_issue_gh.py +0 -195
  278. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase1-gather.py +0 -1298
  279. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase2-finalize.py +0 -228
  280. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase3-scripts.py +0 -1089
  281. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase4-finalize.py +0 -141
  282. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase5-finalize.py +0 -106
  283. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase6-finalize.py +0 -162
  284. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase7-finalize.py +0 -105
  285. package/pipeline/skills/figma-ios/figma-to-component/scripts/register-icons-codeconnect.py +0 -179
  286. package/pipeline/skills/figma-ios/figma-to-component/scripts/remote-mcp-fetch.py +0 -260
  287. package/pipeline/skills/figma-ios/figma-to-component/scripts/resolve-author-logins.py +0 -260
  288. package/pipeline/skills/figma-ios/figma-to-component/scripts/run-uicomponents-tests.sh +0 -86
  289. package/pipeline/skills/figma-ios/figma-to-component/scripts/sidebar-generator.py +0 -321
  290. package/pipeline/skills/figma-ios/figma-to-component/scripts/update-issue-from-registry.py +0 -1470
  291. package/pipeline/skills/figma-ios/figma-to-component/scripts/validate-phase4.sh +0 -176
  292. package/pipeline/skills/figma-ios/figma-to-component/scripts/validate-phase6.sh +0 -147
  293. package/pipeline/skills/figma-ios/figma-to-component/scripts/validate-phase7a.py +0 -629
  294. /package/pipeline/commands/multi-agent/{delete.md → delete/SKILL.md} +0 -0
  295. /package/pipeline/commands/multi-agent/{diff-explain.md → diff-explain/SKILL.md} +0 -0
  296. /package/pipeline/commands/multi-agent/{garbage-collect.md → garbage-collect/SKILL.md} +0 -0
  297. /package/pipeline/commands/multi-agent/{issue.md → issue/SKILL.md} +0 -0
  298. /package/pipeline/commands/multi-agent/{jira.md → jira/SKILL.md} +0 -0
  299. /package/pipeline/commands/multi-agent/{kill.md → kill/SKILL.md} +0 -0
  300. /package/pipeline/commands/multi-agent/{language.md → language/SKILL.md} +0 -0
  301. /package/pipeline/commands/multi-agent/{log.md → log/SKILL.md} +0 -0
  302. /package/pipeline/commands/multi-agent/{prune-logs.md → prune-logs/SKILL.md} +0 -0
  303. /package/pipeline/commands/multi-agent/{purge.md → purge/SKILL.md} +0 -0
  304. /package/pipeline/commands/multi-agent/{scan.md → scan/SKILL.md} +0 -0
  305. /package/pipeline/commands/multi-agent/{search.md → search/SKILL.md} +0 -0
  306. /package/pipeline/commands/multi-agent/{stack.md → stack/SKILL.md} +0 -0
  307. /package/pipeline/commands/multi-agent/{status.md → status/SKILL.md} +0 -0
  308. /package/pipeline/commands/multi-agent/{test.md → test/SKILL.md} +0 -0
  309. /package/pipeline/commands/multi-agent/{update.md → update/SKILL.md} +0 -0
  310. /package/pipeline/{commands/multi-agent → multi-agent-refs}/_input-parser.md +0 -0
  311. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/analysis-template.md +0 -0
  312. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/audit-guide.md +0 -0
  313. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/backend-guide.md +0 -0
  314. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/confluence.md +0 -0
  315. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/issue-comment.md +0 -0
  316. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/pr-review-actions.md +0 -0
  317. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/pr.md +0 -0
  318. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/conventions-defaults.md +0 -0
  319. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/autopilot-circuit-breaker.md +0 -0
  320. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/dev-critic.md +0 -0
  321. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/external-context-injection.md +0 -0
  322. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/model-fallback.md +0 -0
  323. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/plan-todos.md +0 -0
  324. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/prior-fix-detection.md +0 -0
  325. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/repo-map.md +0 -0
  326. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/shadow-git.md +0 -0
  327. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/frontend-guide.md +0 -0
  328. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/knowledge.md +0 -0
  329. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/multi-repo-integration-build.md +0 -0
  330. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/picker-contract.md +0 -0
  331. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/rules.md +0 -0
@@ -1,768 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Generates and uploads a Component Implementation Status page to Confluence.
4
-
5
- Step 1: Build a unified component-index.json from:
6
- - component-catalog.json (catalog: names, variants, figma URLs, file/page grouping)
7
- - Components/{nodeId}.json (registry: codeConnect, confluence links, skip, componentName)
8
- - sub-component-graph.json (graph: sub-component dependencies)
9
- Step 2: Generate XHTML from the index.
10
- Step 3: Upload to Confluence (delete + recreate, no version bloat).
11
-
12
- Usage:
13
- python3 .instructions/figma/figma-to-swiftui/scripts/confluence-component-status.py [--dry-run] [--output status.html]
14
- """
15
-
16
- import argparse
17
- import json
18
- import os
19
- import re
20
- import subprocess
21
- import sys
22
- import urllib.parse
23
- import urllib.request
24
-
25
- BASE_URL = "{confluence.baseUrl}"
26
- SPACE = "DK"
27
- PAGE_TITLE = "Component Implementation Status"
28
-
29
-
30
- def get_token():
31
- r = subprocess.run(
32
- ["security", "find-generic-password", "-a", os.environ["USER"],
33
- "-s", "CONFLUENCE_TOKEN", "-w"],
34
- capture_output=True, text=True, check=True
35
- )
36
- return r.stdout.strip()
37
-
38
-
39
- def xml_escape(t):
40
- return t.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
41
-
42
-
43
- def node_anchor(node_id):
44
- """Convert nodeId like '6893:6879' to anchor-safe 'comp-6893-6879'."""
45
- return "comp-" + node_id.replace(":", "-")
46
-
47
-
48
- # -------------------------------------------------------------------
49
- # STEP 1: Build unified component index
50
- # -------------------------------------------------------------------
51
-
52
- def build_component_index(figma_dir):
53
- """
54
- Merge catalog + registry + graph into one index.
55
- Returns: {
56
- "components": { nodeId: { ...merged fields... } },
57
- "files": [ { fileLabel, pages: [ { pageName, nodeIds: [...] } ] } ],
58
- "summary": { ... }
59
- }
60
- Also builds name → nodeId lookup for sub-component resolution.
61
- """
62
- catalog_path = os.path.join(figma_dir, "Generated", "component-catalog.json")
63
- graph_path = os.path.join(figma_dir, "sub-component-graph.json")
64
- components_dir = os.path.join(figma_dir, "Components")
65
-
66
- # --- Load catalog ---
67
- with open(catalog_path) as f:
68
- catalog = json.load(f)
69
-
70
- # Flat component dict keyed by nodeId
71
- components = {}
72
- files = []
73
- for file_entry in catalog["files"]:
74
- file_pages = []
75
- for page in file_entry["pages"]:
76
- page_node_ids = []
77
- for comp in page["components"]:
78
- nid = comp["nodeId"]
79
- components[nid] = {
80
- "nodeId": nid,
81
- "name": comp["name"],
82
- "figmaName": comp.get("figmaName", ""),
83
- "figmaUrl": comp.get("figmaUrl", ""),
84
- "variantCount": comp.get("variantCount", 0),
85
- "codeConnect": comp.get("codeConnect", {}),
86
- "subComponents": [],
87
- # Registry fields - filled below
88
- "componentName": {},
89
- "skip": False,
90
- "confluence": {},
91
- }
92
- page_node_ids.append(nid)
93
- file_pages.append({
94
- "pageName": page["pageName"],
95
- "nodeIds": page_node_ids,
96
- })
97
- files.append({
98
- "fileLabel": file_entry["fileLabel"],
99
- "totalComponents": file_entry["totalComponents"],
100
- "pages": file_pages,
101
- })
102
-
103
- # --- Load ALL registry files (source of truth for codeConnect, skip, confluence, componentName) ---
104
- registry_count = 0
105
- if os.path.isdir(components_dir):
106
- for fname in os.listdir(components_dir):
107
- if not fname.endswith(".json"):
108
- continue
109
- fpath = os.path.join(components_dir, fname)
110
- try:
111
- with open(fpath) as f:
112
- reg = json.load(f)
113
- except (json.JSONDecodeError, OSError):
114
- continue
115
- nid = reg.get("nodeId", fname[:-5].replace("-", ":"))
116
- registry_count += 1
117
-
118
- if nid in components:
119
- # Merge registry into existing catalog entry
120
- c = components[nid]
121
- # Registry is source of truth for these fields
122
- c["codeConnect"] = reg.get("codeConnect", c["codeConnect"])
123
- c["componentName"] = reg.get("componentName", {})
124
- c["skip"] = reg.get("skip", False)
125
- c["confluence"] = reg.get("confluence", {})
126
- else:
127
- # Registry-only component (e.g. icons not in catalog)
128
- ios_name = reg.get("componentName", {}).get("ios", "")
129
- components[nid] = {
130
- "nodeId": nid,
131
- "name": ios_name or nid,
132
- "figmaName": "",
133
- "figmaUrl": "",
134
- "variantCount": 0,
135
- "codeConnect": reg.get("codeConnect", {}),
136
- "subComponents": [],
137
- "componentName": reg.get("componentName", {}),
138
- "skip": reg.get("skip", False),
139
- "confluence": reg.get("confluence", {}),
140
- "registryOnly": True, # not in catalog
141
- }
142
-
143
- # --- Build name → nodeId lookup (multiple aliases per component) ---
144
- name_to_nid = {}
145
- # Secondary fuzzy lookup keyed by figmaName with separators (`/ _ -`) and
146
- # whitespace stripped, lowercased. The Figma sub-component graph emits
147
- # plain instance-names like "CardsFareAvailabilityFareTypes" - those won't
148
- # match a catalog `figmaName` of "Cards/Fare/Availability/FareTypes" via
149
- # the exact-match map below, but they collapse to the same key here.
150
- # Used as a fallback when an exact lookup misses.
151
- def _strip_key(s):
152
- if not s:
153
- return ""
154
- return re.sub(r"[\s/_\-]+", "", s).lower()
155
- fuzzy_to_nid = {}
156
- for nid, c in components.items():
157
- # Catalog display name
158
- if c["name"]:
159
- name_to_nid[c["name"]] = nid
160
- # Figma name (e.g. "User", "CaretRight")
161
- if c["figmaName"] and c["figmaName"] not in name_to_nid:
162
- name_to_nid[c["figmaName"]] = nid
163
- # iOS component name suffix: "Image.caretRight" → "caretRight"
164
- cn = c.get("componentName", {})
165
- ios_name = cn.get("ios", "") if isinstance(cn, dict) else cn or ""
166
- if ios_name:
167
- name_to_nid[ios_name] = nid
168
- # Also strip "Image." prefix for matching graph names
169
- if "." in ios_name:
170
- suffix = ios_name.split(".", 1)[1]
171
- if suffix not in name_to_nid:
172
- name_to_nid[suffix] = nid
173
- # Case-insensitive: "caretRight" should match "CaretRight"
174
- if suffix[0:1].upper() + suffix[1:] not in name_to_nid:
175
- name_to_nid[suffix[0:1].upper() + suffix[1:]] = nid
176
- # Populate fuzzy lookup from every alias so collapse-keyed matches work.
177
- for alias in (c["name"], c.get("figmaName"), ios_name):
178
- k = _strip_key(alias)
179
- if k and k not in fuzzy_to_nid:
180
- fuzzy_to_nid[k] = nid
181
-
182
- # --- Load sub-component graph and resolve names → nodeIds ---
183
- graph_merged = 0
184
- fuzzy_resolved = 0
185
- if os.path.isfile(graph_path):
186
- with open(graph_path) as f:
187
- graph_data = json.load(f)
188
- graph = graph_data.get("graph", {})
189
- for nid, gdata in graph.items():
190
- sub_names = gdata.get("subComponents", [])
191
- if not sub_names:
192
- continue
193
- # Resolve sub-component names to nodeIds - exact match first,
194
- # then fall back to the stripped/lowercased key. The fuzzy pass
195
- # turns the previous {nodeId: null} entries (which silently
196
- # bypass the readiness algorithm downstream) into real refs.
197
- resolved_subs = []
198
- for sname in sub_names:
199
- sub_nid = name_to_nid.get(sname)
200
- if not sub_nid:
201
- sub_nid = fuzzy_to_nid.get(_strip_key(sname))
202
- if sub_nid:
203
- fuzzy_resolved += 1
204
- if sub_nid:
205
- resolved_subs.append({"nodeId": sub_nid, "name": sname})
206
- else:
207
- resolved_subs.append({"nodeId": None, "name": sname})
208
- if nid in components:
209
- components[nid]["subComponents"] = resolved_subs
210
- graph_merged += 1
211
- if fuzzy_resolved:
212
- print(f" fuzzy-resolved {fuzzy_resolved} sub-component refs that exact-match would have left as null")
213
-
214
- # --- Summary ---
215
- total = sum(1 for c in components.values() if not c.get("registryOnly"))
216
- total_variants = catalog["summary"].get("totalVariants", 0)
217
-
218
- index = {
219
- "components": components,
220
- "nameLookup": name_to_nid,
221
- "files": files,
222
- "summary": {
223
- "totalComponents": total,
224
- "totalVariants": total_variants,
225
- "registryFiles": registry_count,
226
- "graphMerged": graph_merged,
227
- "resolvedNames": len(name_to_nid),
228
- },
229
- }
230
- return index
231
-
232
-
233
- # -------------------------------------------------------------------
234
- # STEP 2: Readiness + XHTML generation
235
- # -------------------------------------------------------------------
236
-
237
- READINESS_ICON = {"implemented": "✅", "ready": "🟢", "blocked": "🔴", "skipped": "⏭️"}
238
- READINESS_COLOR = {
239
- "implemented": "#2196F3", # blue
240
- "ready": "#4CAF50", # green
241
- "blocked": "#F44336", # red
242
- "skipped": "#9E9E9E", # gray
243
- }
244
- READINESS_ORDER = {"implemented": 0, "ready": 1, "blocked": 2, "skipped": 3}
245
-
246
-
247
- def compute_readiness(comp, components, platform="ios"):
248
- """Readiness from the unified index."""
249
- if comp.get("skip"):
250
- return "skipped"
251
- if comp.get("codeConnect", {}).get(platform, False):
252
- return "implemented"
253
- subs = comp.get("subComponents", [])
254
- if not subs:
255
- return "ready"
256
- for sub in subs:
257
- sub_nid = sub.get("nodeId")
258
- if not sub_nid:
259
- continue # Unresolved name (not in catalog or registry) - doesn't block
260
- sub_comp = components.get(sub_nid)
261
- if not sub_comp:
262
- continue
263
- if sub_comp.get("skip"):
264
- continue
265
- if not sub_comp.get("codeConnect", {}).get(platform, False):
266
- return "blocked"
267
- return "ready"
268
-
269
-
270
- def make_anchor(anchor_id):
271
- return (f'<ac:structured-macro ac:name="anchor">'
272
- f'<ac:parameter ac:name="">{anchor_id}</ac:parameter>'
273
- f'</ac:structured-macro>')
274
-
275
-
276
- def make_anchor_link(anchor_id, text, color=None):
277
- """Confluence link-to-anchor on same page."""
278
- if color:
279
- return (f'<ac:link ac:anchor="{anchor_id}">'
280
- f'<ac:link-body>'
281
- f'<span style="color:{color}">{xml_escape(text)}</span>'
282
- f'</ac:link-body>'
283
- f'</ac:link>')
284
- return (f'<ac:link ac:anchor="{anchor_id}">'
285
- f'<ac:plain-text-link-body>'
286
- f'<![CDATA[{text}]]>'
287
- f'</ac:plain-text-link-body>'
288
- f'</ac:link>')
289
-
290
-
291
- def platform_cell(comp, components, platform):
292
- """Build iOS/Android cell: readiness icon + component name if connected."""
293
- r = compute_readiness(comp, components, platform)
294
- icon = READINESS_ICON.get(r, "⬜")
295
- if r == "implemented":
296
- display = comp.get("componentName", {}).get(platform) or comp["name"]
297
- return f'{icon}&nbsp;&nbsp;<code>{xml_escape(display)}</code>'
298
- if r == "skipped":
299
- return f'{icon} <span style="color:#9E9E9E"><s>{xml_escape(comp["name"])}</s></span>'
300
- return icon
301
-
302
-
303
- def generate_xhtml(index):
304
- components = index["components"]
305
- files = index["files"]
306
- summary = index["summary"]
307
- total = summary["totalComponents"]
308
- total_variants = summary["totalVariants"]
309
-
310
- # Collect catalog components (not registry-only) for counting
311
- catalog_comps = [c for c in components.values() if not c.get("registryOnly")]
312
-
313
- ios_impl = sum(1 for c in catalog_comps if compute_readiness(c, components, "ios") == "implemented")
314
- ios_ready = sum(1 for c in catalog_comps if compute_readiness(c, components, "ios") == "ready")
315
- ios_blocked = sum(1 for c in catalog_comps if compute_readiness(c, components, "ios") == "blocked")
316
- ios_skipped = sum(1 for c in catalog_comps if compute_readiness(c, components, "ios") == "skipped")
317
- android_impl = sum(1 for c in catalog_comps if compute_readiness(c, components, "android") == "implemented")
318
- android_ready = sum(1 for c in catalog_comps if compute_readiness(c, components, "android") == "ready")
319
- android_blocked = sum(1 for c in catalog_comps if compute_readiness(c, components, "android") == "blocked")
320
- android_skipped = sum(1 for c in catalog_comps if compute_readiness(c, components, "android") == "skipped")
321
-
322
- p = []
323
-
324
- p.append(f'<p><em>Component implementation status - generated from unified component index '
325
- f'({summary["resolvedNames"]} name aliases, {summary["registryFiles"]} registry files).</em></p>')
326
- p.append(f'<p><strong>Legend:</strong> '
327
- f'✅ Implemented (Code Connected) - shows component name | '
328
- f'🟢 Ready for Development | '
329
- f'🔴 Blocked (has unconnected sub-components) | '
330
- f'⏭️ Skipped (will not be implemented)<br/>'
331
- f'<strong>Sub-component colors:</strong> '
332
- f'<span style="color:#2196F3">Blue = implemented</span> | '
333
- f'<span style="color:#4CAF50">Green = ready</span> | '
334
- f'<span style="color:#F44336">Red = blocked</span> | '
335
- f'<span style="color:#9E9E9E">Gray = skipped or unresolved</span></p>')
336
-
337
- # Script: find anchor (outside expand), open the nearest expand-container sibling, scroll
338
- p.append('<ac:structured-macro ac:name="html">')
339
- p.append('<ac:plain-text-body><![CDATA[<script>')
340
- p.append("""(function(){
341
- function openExpand(container){
342
- if(!container||!container.classList.contains('expand-container'))return;
343
- var body=container.querySelector('.expand-content');
344
- if(body){body.style.display='block';body.classList.remove('expand-hidden');}
345
- var ctrl=container.querySelector('.expand-control');
346
- if(ctrl)ctrl.classList.add('expanded');
347
- }
348
- function expandAndScroll(id){
349
- var el=document.getElementById(id);
350
- if(!el)el=document.querySelector('.confluence-anchor-link[id="'+id+'"]');
351
- if(!el)return false;
352
- var sib=el;
353
- while(sib){
354
- if(sib.classList&&sib.classList.contains('expand-container')){openExpand(sib);break;}
355
- sib=sib.nextElementSibling;
356
- }
357
- if(!sib){
358
- var parent=el.parentElement;
359
- while(parent){
360
- var expands=parent.querySelectorAll('.expand-container');
361
- if(expands.length>0){for(var i=0;i<expands.length;i++)openExpand(expands[i]);break;}
362
- parent=parent.parentElement;
363
- }
364
- }
365
- setTimeout(function(){el.scrollIntoView({behavior:'smooth',block:'start'});},150);
366
- return true;
367
- }
368
- function onHash(){
369
- var h=window.location.hash;
370
- if(h&&h.length>1)expandAndScroll(decodeURIComponent(h.substring(1)));
371
- }
372
- window.addEventListener('hashchange',onHash);
373
- if(document.readyState==='complete')setTimeout(onHash,500);
374
- else window.addEventListener('load',function(){setTimeout(onHash,500);});
375
- })();""")
376
- p.append('</script>]]></ac:plain-text-body>')
377
- p.append('</ac:structured-macro>')
378
-
379
- p.append('<hr/>')
380
-
381
- p.append('<ac:structured-macro ac:name="toc">')
382
- p.append('<ac:parameter ac:name="printable">true</ac:parameter>')
383
- p.append('<ac:parameter ac:name="style">disc</ac:parameter>')
384
- p.append('<ac:parameter ac:name="maxLevel">3</ac:parameter>')
385
- p.append('<ac:parameter ac:name="minLevel">2</ac:parameter>')
386
- p.append('</ac:structured-macro>')
387
- p.append('<hr/>')
388
-
389
- # --- Summary table ---
390
- p.append('<h2>Summary</h2>')
391
- p.append('<table><tbody>')
392
- p.append('<tr><th>Metric</th><th>iOS</th><th>Android</th></tr>')
393
- p.append(f'<tr><td>Total Components</td><td colspan="2">{total}</td></tr>')
394
- p.append(f'<tr><td>✅ Implemented</td><td>{ios_impl}</td><td>{android_impl}</td></tr>')
395
- p.append(f'<tr><td>🟢 Ready</td><td>{ios_ready}</td><td>{android_ready}</td></tr>')
396
- p.append(f'<tr><td>🔴 Blocked</td><td>{ios_blocked}</td><td>{android_blocked}</td></tr>')
397
- p.append(f'<tr><td>⏭️ Skipped</td><td>{ios_skipped}</td><td>{android_skipped}</td></tr>')
398
- p.append(f'<tr><td>Total Variants</td><td colspan="2">{total_variants}</td></tr>')
399
- effective_total = total - ios_skipped
400
- effective_total_android = total - android_skipped
401
- ios_pct = (100 * ios_impl // max(effective_total, 1))
402
- android_pct = (100 * android_impl // max(effective_total_android, 1))
403
- p.append(f'<tr><td>Progress</td><td>{ios_impl}/{effective_total} ({ios_pct}%)</td>'
404
- f'<td>{android_impl}/{effective_total_android} ({android_pct}%)</td></tr>')
405
- p.append('</tbody></table>')
406
- p.append('<hr/>')
407
-
408
- # --- Per file > page > component tables ---
409
- for file_entry in files:
410
- file_label = file_entry["fileLabel"]
411
- file_total = file_entry["totalComponents"]
412
-
413
- file_comps = [components[nid] for pg in file_entry["pages"] for nid in pg["nodeIds"]]
414
- file_ios_impl = sum(1 for c in file_comps if compute_readiness(c, components, "ios") == "implemented")
415
- file_ios_skipped = sum(1 for c in file_comps if compute_readiness(c, components, "ios") == "skipped")
416
- file_ios_eff = file_total - file_ios_skipped
417
- file_android_impl = sum(1 for c in file_comps if compute_readiness(c, components, "android") == "implemented")
418
- file_android_skipped = sum(1 for c in file_comps if compute_readiness(c, components, "android") == "skipped")
419
- file_android_eff = file_total - file_android_skipped
420
-
421
- file_label_esc = xml_escape(file_label)
422
- file_stats = f'iOS {file_ios_impl}/{file_ios_eff} / Android {file_android_impl}/{file_android_eff}'
423
- if file_ios_eff == 0 and file_android_eff == 0:
424
- # All skipped - no TOC entry, just a styled paragraph
425
- p.append(f'<p><strong style="font-size:20px"><s>{file_label_esc} ({file_stats})</s></strong></p>')
426
- else:
427
- p.append(f'<h2>{file_label_esc} ({file_stats})</h2>')
428
-
429
- for page_entry in file_entry["pages"]:
430
- page_name = page_entry["pageName"]
431
- page_comps = [components[nid] for nid in page_entry["nodeIds"]]
432
- pg_ios_impl = sum(1 for c in page_comps if compute_readiness(c, components, "ios") == "implemented")
433
- pg_ios_skipped = sum(1 for c in page_comps if compute_readiness(c, components, "ios") == "skipped")
434
- pg_ios_eff = len(page_comps) - pg_ios_skipped
435
- pg_android_impl = sum(1 for c in page_comps if compute_readiness(c, components, "android") == "implemented")
436
- pg_android_skipped = sum(1 for c in page_comps if compute_readiness(c, components, "android") == "skipped")
437
- pg_android_eff = len(page_comps) - pg_android_skipped
438
-
439
- page_name_esc = xml_escape(page_name)
440
- pg_stats = f'iOS {pg_ios_impl}/{pg_ios_eff} / Android {pg_android_impl}/{pg_android_eff}'
441
- if pg_ios_eff == 0 and pg_android_eff == 0:
442
- # All skipped - no TOC entry, just a styled paragraph
443
- p.append(f'<p><strong style="font-size:16px"><s>{page_name_esc} ({pg_stats})</s></strong></p>')
444
- else:
445
- p.append(f'<h3>{page_name_esc} ({pg_stats})</h3>')
446
-
447
- sorted_comps = sorted(page_comps, key=lambda c: (
448
- READINESS_ORDER.get(compute_readiness(c, components, "ios"), 9), c["name"]))
449
-
450
- # Anchors OUTSIDE expand - Confluence can't resolve anchors inside collapsed expand
451
- for c in sorted_comps:
452
- p.append(make_anchor(node_anchor(c["nodeId"])))
453
-
454
- p.append('<ac:structured-macro ac:name="expand">')
455
- p.append(f'<ac:parameter ac:name="title">{page_name_esc} - '
456
- f'iOS {pg_ios_impl}/{pg_ios_eff} / Android {pg_android_impl}/{pg_android_eff} '
457
- f'({len(page_comps)} components)</ac:parameter>')
458
- p.append('<ac:rich-text-body>')
459
- p.append('<table><tbody>')
460
- p.append('<tr>'
461
- '<th>iOS</th>'
462
- '<th>Android</th>'
463
- '<th>Component</th>'
464
- '<th>Figma Component</th>'
465
- '<th>Figma Name</th>'
466
- '<th>Variants</th>'
467
- '<th>Sub-Components</th>'
468
- '</tr>')
469
-
470
- for c in sorted_comps:
471
- name_esc = xml_escape(c["name"])
472
- figma_esc = xml_escape(c.get("figmaName", ""))
473
- is_skipped = c.get("skip", False)
474
-
475
- # Component column
476
- conf_url = c.get("confluence", {}).get("page")
477
- if is_skipped:
478
- comp_cell = f'<span style="color:#9E9E9E"><s>{name_esc}</s></span>'
479
- elif conf_url:
480
- comp_cell = f'<a href="{conf_url}">{name_esc}</a>'
481
- else:
482
- comp_cell = name_esc
483
-
484
- # Platform columns
485
- ios_cell = platform_cell(c, components, "ios")
486
- android_cell = platform_cell(c, components, "android")
487
-
488
- # Figma Component column
489
- if c.get("figmaUrl"):
490
- figma_cell = f'<a href="{c["figmaUrl"]}">{name_esc}</a>'
491
- else:
492
- figma_cell = name_esc
493
-
494
- # Variant count
495
- vc = c.get("variantCount", 0)
496
- variant_cell = str(vc) if vc > 0 else " - "
497
-
498
- # Sub-components - colored links (blue/green/red/gray), NO emoji
499
- subs = c.get("subComponents", [])
500
- if subs:
501
- sub_links = []
502
- for sub in subs:
503
- sub_nid = sub.get("nodeId")
504
- sub_name = sub["name"]
505
- if sub_nid:
506
- sub_comp = components.get(sub_nid)
507
- if sub_comp:
508
- s_anchor = node_anchor(sub_nid)
509
- s_readiness = compute_readiness(sub_comp, components, "ios")
510
- s_color = READINESS_COLOR.get(s_readiness, "#999")
511
- sub_links.append(make_anchor_link(s_anchor, sub_name, s_color))
512
- else:
513
- sub_links.append(f'<span style="color:#999">{xml_escape(sub_name)}</span>')
514
- else:
515
- sub_links.append(f'<span style="color:#999">{xml_escape(sub_name)}</span>')
516
- subs_cell = ", ".join(sub_links)
517
- else:
518
- subs_cell = " - "
519
-
520
- row_style = ' style="background:#f5f5f5; color:#9E9E9E"' if is_skipped else ''
521
- p.append(
522
- f'<tr{row_style}>'
523
- f'<td>{ios_cell}</td>'
524
- f'<td>{android_cell}</td>'
525
- f'<td>{comp_cell}</td>'
526
- f'<td>{figma_cell}</td>'
527
- f'<td><code>{figma_esc}</code></td>'
528
- f'<td>{variant_cell}</td>'
529
- f'<td>{subs_cell}</td>'
530
- f'</tr>'
531
- )
532
-
533
- p.append('</tbody></table>')
534
- p.append('</ac:rich-text-body>')
535
- p.append('</ac:structured-macro>')
536
-
537
- p.append('<hr/>')
538
-
539
- # --- Connected Components - iOS ---
540
- ios_connected = [c for c in catalog_comps if c.get("codeConnect", {}).get("ios")]
541
- p.append('<h2>Connected Components - iOS</h2>')
542
- if ios_connected:
543
- p.append('<table><tbody>')
544
- p.append('<tr><th>Component</th><th>Figma Name</th></tr>')
545
- for c in sorted(ios_connected, key=lambda c: c["name"]):
546
- anchor = node_anchor(c["nodeId"])
547
- figma_esc = xml_escape(c.get("figmaName", ""))
548
- p.append(
549
- f'<tr>'
550
- f'<td>{make_anchor_link(anchor, c["name"])}</td>'
551
- f'<td><code>{figma_esc}</code></td>'
552
- f'</tr>'
553
- )
554
- p.append('</tbody></table>')
555
- else:
556
- p.append('<p><em>No iOS-connected components yet.</em></p>')
557
-
558
- # --- Connected Components - Android ---
559
- android_connected = [c for c in catalog_comps if c.get("codeConnect", {}).get("android")]
560
- p.append('<h2>Connected Components - Android</h2>')
561
- if android_connected:
562
- p.append('<table><tbody>')
563
- p.append('<tr><th>Component</th><th>Figma Name</th></tr>')
564
- for c in sorted(android_connected, key=lambda c: c["name"]):
565
- anchor = node_anchor(c["nodeId"])
566
- figma_esc = xml_escape(c.get("figmaName", ""))
567
- p.append(
568
- f'<tr>'
569
- f'<td>{make_anchor_link(anchor, c["name"])}</td>'
570
- f'<td><code>{figma_esc}</code></td>'
571
- f'</tr>'
572
- )
573
- p.append('</tbody></table>')
574
- else:
575
- p.append('<p><em>No Android-connected components yet.</em></p>')
576
-
577
- p.append('<hr/>')
578
- p.append(f'<p><em>Index: {len(components)} components, '
579
- f'{summary["resolvedNames"]} name aliases, '
580
- f'{summary["registryFiles"]} registry files</em></p>')
581
-
582
- return "\n".join(p)
583
-
584
-
585
- # -------------------------------------------------------------------
586
- # STEP 3: Confluence upload
587
- # -------------------------------------------------------------------
588
-
589
- def lookup_page(token, title, parent_id=None):
590
- encoded = urllib.parse.quote(title)
591
- url = f"{BASE_URL}/rest/api/content?spaceKey={SPACE}&title={encoded}&expand=version,ancestors"
592
- req = urllib.request.Request(url)
593
- req.add_header("Authorization", f"Bearer {token}")
594
- with urllib.request.urlopen(req) as resp:
595
- d = json.loads(resp.read())
596
- for r in d.get("results", []):
597
- if parent_id:
598
- anc = r.get("ancestors", [])
599
- pid = anc[-1]["id"] if anc else None
600
- if str(pid) != str(parent_id):
601
- continue
602
- return {"id": r["id"], "version": r["version"]["number"]}
603
- return None
604
-
605
-
606
- def delete_page(token, page_id):
607
- """Delete page and purge from trash to avoid version/storage bloat."""
608
- req = urllib.request.Request(
609
- f"{BASE_URL}/rest/api/content/{page_id}", method="DELETE"
610
- )
611
- req.add_header("Authorization", f"Bearer {token}")
612
- urllib.request.urlopen(req)
613
- print(f"Deleted: id={page_id}")
614
- try:
615
- req = urllib.request.Request(
616
- f"{BASE_URL}/rest/api/content/{page_id}?status=trashed", method="DELETE"
617
- )
618
- req.add_header("Authorization", f"Bearer {token}")
619
- urllib.request.urlopen(req)
620
- print(f"Purged from trash: id={page_id}")
621
- except urllib.error.HTTPError:
622
- print(f"Note: Could not purge from trash (may need admin rights)")
623
-
624
-
625
- def create_page(token, title, content, parent_id):
626
- """Create a fresh page (always v1)."""
627
- payload = {
628
- "type": "page", "title": title,
629
- "ancestors": [{"id": str(parent_id)}],
630
- "space": {"key": SPACE},
631
- "body": {"storage": {"value": content, "representation": "storage"}}
632
- }
633
- body = json.dumps(payload, ensure_ascii=False).encode("utf-8")
634
- req = urllib.request.Request(f"{BASE_URL}/rest/api/content", data=body, method="POST")
635
- req.add_header("Authorization", f"Bearer {token}")
636
- req.add_header("Content-Type", "application/json; charset=utf-8")
637
- try:
638
- with urllib.request.urlopen(req) as resp:
639
- d = json.loads(resp.read())
640
- except urllib.error.HTTPError as e:
641
- raise RuntimeError(f"Create failed: {e.read().decode()[:500]}")
642
- print(f"Created: \"{title}\" -> {d['id']} (v1, fresh)")
643
- return d["id"]
644
-
645
-
646
- def update_page(token, page_id, title, content, version):
647
- """Update an existing page in-place (fallback when delete is forbidden)."""
648
- payload = {
649
- "type": "page", "title": title,
650
- "version": {"number": version + 1},
651
- "body": {"storage": {"value": content, "representation": "storage"}}
652
- }
653
- body = json.dumps(payload, ensure_ascii=False).encode("utf-8")
654
- req = urllib.request.Request(
655
- f"{BASE_URL}/rest/api/content/{page_id}", data=body, method="PUT"
656
- )
657
- req.add_header("Authorization", f"Bearer {token}")
658
- req.add_header("Content-Type", "application/json; charset=utf-8")
659
- try:
660
- with urllib.request.urlopen(req) as resp:
661
- d = json.loads(resp.read())
662
- except urllib.error.HTTPError as e:
663
- raise RuntimeError(f"Update failed: {e.read().decode()[:500]}")
664
- print(f"Updated: \"{title}\" -> {d['id']} (v{version + 1})")
665
- return d["id"]
666
-
667
-
668
- def upload(token, title, content, parent_id, dry_run=False):
669
- """Delete existing page + recreate fresh, or update in-place as fallback."""
670
- existing = lookup_page(token, title, parent_id)
671
- if existing:
672
- if dry_run:
673
- print(f"[DRY RUN] Would delete+recreate: \"{title}\" (id={existing['id']}, was v{existing['version']})")
674
- return existing["id"]
675
- try:
676
- delete_page(token, existing["id"])
677
- except urllib.error.HTTPError:
678
- print(f"Delete forbidden, updating in-place instead...")
679
- return update_page(token, existing["id"], title, content, existing["version"])
680
- elif dry_run:
681
- print(f"[DRY RUN] Would create: \"{title}\"")
682
- return "DRY"
683
- return create_page(token, title, content, parent_id)
684
-
685
-
686
- # -------------------------------------------------------------------
687
- # Main
688
- # -------------------------------------------------------------------
689
-
690
- def main():
691
- parser = argparse.ArgumentParser()
692
- parser.add_argument("--dry-run", action="store_true")
693
- parser.add_argument("--output", help="Write XHTML to file instead of uploading")
694
- args = parser.parse_args()
695
-
696
- # Find Shared/Figma/ directory
697
- for base in [".", "{repos.common.path}"]:
698
- cat = os.path.join(base, "Shared/Figma/Generated/component-catalog.json")
699
- if os.path.isfile(cat):
700
- figma_dir = os.path.dirname(os.path.dirname(cat))
701
- break
702
- else:
703
- print("ERROR: component-catalog.json not found")
704
- print("Run: FigmaRegistryUtility catalog")
705
- sys.exit(1)
706
-
707
- print(f"Figma dir: {figma_dir}")
708
-
709
- # STEP 1: Build unified index
710
- index = build_component_index(figma_dir)
711
- s = index["summary"]
712
- print(f"Components: {s['totalComponents']} (catalog) + "
713
- f"{sum(1 for c in index['components'].values() if c.get('registryOnly'))} (registry-only)")
714
- print(f"Registry: {s['registryFiles']} files")
715
- print(f"Names: {s['resolvedNames']} aliases indexed")
716
- print(f"Graph: {s['graphMerged']} components with sub-component data")
717
-
718
- # Write index to disk for inspection
719
- index_path = os.path.join(figma_dir, "Generated", "component-index.json")
720
- # Write a slim version (no nameLookup - it's huge)
721
- slim = {
722
- "components": index["components"],
723
- "files": index["files"],
724
- "summary": index["summary"],
725
- }
726
- with open(index_path, "w") as f:
727
- json.dump(slim, f, indent=2, ensure_ascii=False)
728
- f.write("\n")
729
- print(f"Index: {index_path}")
730
-
731
- # Count resolved vs unresolved sub-component names
732
- resolved = 0
733
- unresolved = 0
734
- for c in index["components"].values():
735
- for sub in c.get("subComponents", []):
736
- if sub.get("nodeId"):
737
- resolved += 1
738
- else:
739
- unresolved += 1
740
- print(f"Sub-comps: {resolved} resolved, {unresolved} unresolved")
741
-
742
- # STEP 2: Generate XHTML
743
- xhtml = generate_xhtml(index)
744
-
745
- if args.output:
746
- with open(args.output, "w") as f:
747
- f.write(xhtml)
748
- print(f"Written: {args.output} ({len(xhtml)} bytes)")
749
- return
750
-
751
- # STEP 3: Upload
752
- script_dir = os.path.dirname(os.path.abspath(__file__))
753
- ids_file = os.path.join(script_dir, "confluence-page-ids.json")
754
- with open(ids_file) as f:
755
- page_ids = json.load(f)
756
-
757
- parent_id = page_ids.get("Figma Components")
758
- if not parent_id:
759
- print("ERROR: Figma Components page ID not found")
760
- sys.exit(1)
761
-
762
- token = get_token()
763
- upload(token, PAGE_TITLE, xhtml, parent_id, args.dry_run)
764
- print("Done!")
765
-
766
-
767
- if __name__ == "__main__":
768
- main()