@mmerterden/multi-agent-pipeline 11.1.0 → 11.3.0

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