@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,1089 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Phase 3 scriptable sub-phases: 3B (tokens), 3E (assets), 3F (utilities), 3G (coverage).
3
-
4
- Usage:
5
- python3 phase3-scripts.py 3b --artifact-path /path/to/artifacts
6
- python3 phase3-scripts.py 3e --artifact-path /path/to/artifacts
7
- python3 phase3-scripts.py 3f --artifact-path /path/to/artifacts
8
- python3 phase3-scripts.py 3g --artifact-path /path/to/artifacts
9
- python3 phase3-scripts.py wave1 --artifact-path /path/to/artifacts # runs 3b+3e+3f
10
- """
11
-
12
- from __future__ import annotations
13
-
14
- import argparse
15
- import json
16
- import os
17
- import re
18
- import subprocess
19
- import sys
20
- from datetime import datetime, timezone
21
- from pathlib import Path
22
- from typing import Optional
23
-
24
- # --- Constants ---
25
-
26
- SCRIPT_DIR = Path(__file__).parent
27
- REPO_ROOT = SCRIPT_DIR.parent.parent.parent.parent # .instructions/figma/figma-to-swiftui/scripts -> repo root
28
- CONFIG_PKG = REPO_ROOT / "{repos.packagesContainer}" / "Packages" / "{project.slug}-common"
29
- CLI_BINARY = CONFIG_PKG / ".build" / "debug" / "FigmaRegistryUtility"
30
- ASSET_CATALOG_ROOT = CONFIG_PKG / "iOS" / "Sources" / "UIAssets" / "Generated" / "Resources" / "Images"
31
-
32
- ISO_DATE = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
33
- DATE_SHORT = datetime.now(timezone.utc).strftime("%Y-%m-%d")
34
-
35
-
36
- # --- CLI helpers ---
37
-
38
- def run_cli(command: str, args: list[str]) -> dict | None:
39
- """Run FigmaRegistryUtility command from config package dir."""
40
- cmd = [str(CLI_BINARY), command] + args
41
- try:
42
- result = subprocess.run(
43
- cmd, capture_output=True, text=True, timeout=30,
44
- cwd=str(CONFIG_PKG)
45
- )
46
- if result.returncode == 0 and result.stdout.strip():
47
- return json.loads(result.stdout)
48
- except (subprocess.TimeoutExpired, json.JSONDecodeError, FileNotFoundError) as e:
49
- print(f" [warn] CLI {command} failed: {e}", file=sys.stderr)
50
- return None
51
-
52
-
53
- def ensure_cli():
54
- """Build CLI if binary doesn't exist."""
55
- if not CLI_BINARY.exists():
56
- print(" Building FigmaRegistryUtility...", file=sys.stderr)
57
- subprocess.run(
58
- ["swift", "build", "--product", "FigmaRegistryUtility"],
59
- cwd=str(CONFIG_PKG), capture_output=True, timeout=120
60
- )
61
-
62
-
63
- # --- 3B: Token Mapping ---
64
-
65
- def classify_variable(name: str, value: str) -> str | None:
66
- """Classify a variable_defs entry by type."""
67
- if value.startswith("#") and len(value) in (4, 7, 9):
68
- return "color"
69
- if value.startswith("Font("):
70
- return "typography_composite"
71
- if name.startswith("Font/") or name.startswith("[TBD]Font/"):
72
- return "font_primitive"
73
- if name.startswith("Spacing/"):
74
- return "spacing"
75
- if name.startswith("Radius/"):
76
- return "radius"
77
- # Try to parse as number
78
- try:
79
- float(value)
80
- return "number"
81
- except ValueError:
82
- pass
83
- # String values (font family names etc)
84
- if not value.startswith("{") and not value.startswith("["):
85
- return "font_family"
86
- return None
87
-
88
-
89
- def run_3b(artifact_path: str) -> bool:
90
- """Phase 3B: Variable Token Mapping."""
91
- print("Phase 3B: Token Mapping")
92
- ap = Path(artifact_path)
93
-
94
- # Read inputs
95
- var_defs_path = ap / "pass1" / "variable_defs_response.json"
96
- if not var_defs_path.exists():
97
- print(" [error] variable_defs_response.json not found", file=sys.stderr)
98
- return False
99
-
100
- with open(var_defs_path) as f:
101
- var_defs = json.load(f)
102
-
103
- # Get component name from component_properties.json
104
- comp_name = "(from artifacts)"
105
- comp_props_path = ap / "pass1" / "component_properties.json"
106
- if comp_props_path.exists():
107
- with open(comp_props_path) as f:
108
- comp_name = json.load(f).get("componentName", comp_name)
109
-
110
- # Classify variables
111
- colors, spacings, radii, font_prims, typo_composites, font_families = [], [], [], [], [], []
112
- lookup_vars = []
113
-
114
- for name, value in var_defs.items():
115
- vtype = classify_variable(name, value)
116
- if vtype == "color":
117
- colors.append(name)
118
- lookup_vars.append(name)
119
- elif vtype == "spacing":
120
- spacings.append(name)
121
- lookup_vars.append(name)
122
- elif vtype == "radius":
123
- radii.append(name)
124
- lookup_vars.append(name)
125
- elif vtype == "number":
126
- lookup_vars.append(name)
127
- elif vtype == "typography_composite":
128
- typo_composites.append(name)
129
- elif vtype == "font_primitive":
130
- font_prims.append(name)
131
- lookup_vars.append(name)
132
- elif vtype == "font_family":
133
- font_families.append(name)
134
-
135
- # Batch CLI lookup for all non-composite variables
136
- token_map = {}
137
- if lookup_vars:
138
- result = run_cli("ui-token-lookup", ["--variables", ",".join(lookup_vars), "--json"])
139
- if result:
140
- token_map = result
141
-
142
- # Typography lookup for composite styles - pass the raw Figma variable names
143
- # through untransformed. Resolution is the CLI's job, not ours.
144
- typo_map = {}
145
- if typo_composites:
146
- result = run_cli("typography-lookup", ["--styles", ",".join(typo_composites), "--json"])
147
- if result:
148
- typo_map = result
149
-
150
- # Build output sections
151
- lines = [
152
- f"# Token Mapping\n",
153
- f"Component: {comp_name}",
154
- f"Generated: {ISO_DATE}",
155
- f"Source: pass1/variable_defs_response.json, pass1/design_context_response.json\n",
156
- ]
157
-
158
- # Typography section
159
- lines.append("## Typography\n")
160
- typo_count = 0
161
- lines.append("| Figma Style | Token Type | Swift Modifier | Swift Font |")
162
- lines.append("|-------------|------------|----------------|------------|")
163
- for tc_name in typo_composites:
164
- entry = typo_map.get(tc_name)
165
- if entry and entry.get("status") == "found":
166
- swift_token = entry["swiftToken"] # e.g. ".smallBold"
167
- lines.append(f"| {tc_name} | typography | `.typographyStyle({swift_token})` | `Font{swift_token}` |")
168
- typo_count += 1
169
- else:
170
- lines.append(f"| {tc_name} | typography | ⚠️ not found | ⚠️ not found |")
171
- if not typo_composites:
172
- lines.append("| (none) | - | - | - |")
173
- lines.append("")
174
-
175
- # Colors section
176
- lines.append("## Colors\n")
177
- lines.append("| Figma Variable | Token Type | Swift Usage |")
178
- lines.append("|----------------|------------|-------------|")
179
- color_count = 0
180
- for var_name in colors:
181
- token = token_map.get(var_name)
182
- if token:
183
- # token format: "color:.Text.textSuccess"
184
- type_prefix, swift_path = token.split(":", 1)
185
- lines.append(f"| {var_name} | {type_prefix} | `Color{swift_path}` |")
186
- color_count += 1
187
- else:
188
- lines.append(f"| {var_name} | color | ⚠️ not found |")
189
- if not colors:
190
- lines.append("| (none) | - | - |")
191
- lines.append("")
192
-
193
- # Spacing section
194
- lines.append("## Spacing\n")
195
- lines.append("| Figma Variable | Token Type | Swift Token |")
196
- lines.append("|----------------|------------|-------------|")
197
- spacing_count = 0
198
- for var_name in spacings:
199
- token = token_map.get(var_name)
200
- if token:
201
- type_prefix, swift_path = token.split(":", 1)
202
- lines.append(f"| {var_name} | {type_prefix} | `{swift_path}` |")
203
- spacing_count += 1
204
- else:
205
- lines.append(f"| {var_name} | number | ⚠️ not found |")
206
- if not spacings:
207
- lines.append("| (none) | - | - |")
208
- lines.append("")
209
-
210
- # Radius section
211
- lines.append("## Radius\n")
212
- lines.append("| Figma Variable | Token Type | Swift Token |")
213
- lines.append("|----------------|------------|-------------|")
214
- radius_count = 0
215
- for var_name in radii:
216
- token = token_map.get(var_name)
217
- if token:
218
- type_prefix, swift_path = token.split(":", 1)
219
- lines.append(f"| {var_name} | {type_prefix} | `{swift_path}` |")
220
- radius_count += 1
221
- else:
222
- lines.append(f"| {var_name} | number | ⚠️ not found |")
223
- if not radii:
224
- lines.append("| (none) | - | - |")
225
- lines.append("")
226
-
227
- # Images section
228
- lines.append("## Images / Icons\n")
229
- lines.append("None detected in variable definitions.\n")
230
-
231
- # Font primitives section
232
- if font_prims:
233
- lines.append("## Font Primitives (Sub-tokens)\n")
234
- lines.append("These are typography sub-properties. Not used directly - encapsulated by composite typography tokens above.\n")
235
- lines.append("| Figma Variable | Token Type | Swift Token | Note |")
236
- lines.append("|----------------|------------|-------------|------|")
237
- for var_name in font_prims:
238
- token = token_map.get(var_name)
239
- if token:
240
- type_prefix, swift_path = token.split(":", 1)
241
- lines.append(f"| {var_name} | {type_prefix} | `{swift_path}` | Font primitive (use composite token instead) |")
242
- else:
243
- lines.append(f"| {var_name} | - | N/A | Font primitive - not a standalone token |")
244
- lines.append("")
245
-
246
- # Font families
247
- if font_families:
248
- lines.append("## Font Families\n")
249
- for name in font_families:
250
- lines.append(f"- `{name}`: \"{var_defs[name]}\" - font family reference, not a standalone token\n")
251
-
252
- # Missing tokens - exclude font primitives and font families (encapsulated by composite)
253
- missing = []
254
- for var_name in lookup_vars:
255
- token = token_map.get(var_name)
256
- if token is None:
257
- vtype = classify_variable(var_name, var_defs[var_name])
258
- # Font family refs and font primitives are encapsulated by composite typography
259
- if vtype in ("font_family", "font_primitive"):
260
- continue
261
- missing.append((var_name, vtype or "unknown"))
262
- for tc_name in typo_composites:
263
- entry = typo_map.get(tc_name)
264
- if not entry or entry.get("status") != "found":
265
- missing.append((tc_name, "typography"))
266
-
267
- # Font primitives/families that returned null are NOT missing - they're encapsulated
268
- font_null_note = []
269
- for var_name in lookup_vars:
270
- token = token_map.get(var_name)
271
- if token is None:
272
- vtype = classify_variable(var_name, var_defs[var_name])
273
- if vtype in ("font_family", "font_primitive"):
274
- font_null_note.append((var_name, vtype))
275
-
276
- lines.append("## Missing Tokens\n")
277
- if missing:
278
- lines.append("| Token | Type | Resolution |")
279
- lines.append("|-------|------|------------|")
280
- for name, ttype in missing:
281
- lines.append(f"| {name} | {ttype} | See reference/missing-tokens.md |")
282
- else:
283
- if font_null_note:
284
- lines.append(f"All tokens resolved. ({len(font_null_note)} font primitive/family references excluded - encapsulated by composite typography token.)\n")
285
- else:
286
- lines.append("All tokens resolved.\n")
287
-
288
- # Summary
289
- mapped_prims = sum(1 for v in font_prims if token_map.get(v) is not None)
290
- actionable_missing = [m for m in missing if m[1] != "font_family"]
291
-
292
- lines.append("## Summary\n")
293
- lines.append(f"- Typography: {typo_count} mapped")
294
- lines.append(f"- Colors: {color_count} mapped")
295
- lines.append(f"- Spacing: {spacing_count} mapped")
296
- lines.append(f"- Radius: {radius_count} mapped")
297
- lines.append(f"- Images: 0 mapped")
298
- if font_prims:
299
- lines.append(f"- Font Primitives: {mapped_prims} mapped (sub-tokens of typography)")
300
- lines.append(f"- Missing: {len(actionable_missing)} actionable")
301
-
302
- # Write
303
- os.makedirs(ap / "pass3", exist_ok=True)
304
- out_path = ap / "pass3" / "02_token_mapping.md"
305
- with open(out_path, "w") as f:
306
- f.write("\n".join(lines) + "\n")
307
-
308
- print(f" ✅ {out_path} ({typo_count + color_count + spacing_count + radius_count} tokens mapped, {len(actionable_missing)} missing)")
309
- return True
310
-
311
-
312
- # --- 3E: Asset Inventory ---
313
-
314
- def parse_asset_filename(filename: str) -> str:
315
- """Try to identify an asset from its filename (hash or named)."""
316
- # Strip extension
317
- stem = Path(filename).stem
318
- # If it's a hex hash, we need to inspect SVG content or check code_connect_map
319
- if re.match(r'^[a-f0-9]{30,}$', stem):
320
- return stem # hash-named, needs further resolution
321
- return stem
322
-
323
-
324
- def run_3e(artifact_path: str) -> bool:
325
- """Phase 3E: Asset Inventory."""
326
- print("Phase 3E: Asset Inventory")
327
- ap = Path(artifact_path)
328
-
329
- assets_txt = ap / "pass1" / "design_context_assets.txt"
330
- if not assets_txt.exists() or assets_txt.stat().st_size == 0:
331
- # Write placeholder
332
- os.makedirs(ap / "pass3", exist_ok=True)
333
- out_path = ap / "pass3" / "06_assets.md"
334
- with open(out_path, "w") as f:
335
- f.write(f"# Asset Inventory\n\nNo downloadable assets found for this component.\nGenerated: {ISO_DATE}\n")
336
- print(f" ⏭️ No assets - placeholder written")
337
- return True
338
-
339
- # Parse asset entries
340
- assets = []
341
- with open(assets_txt) as f:
342
- for line in f:
343
- line = line.strip()
344
- if not line or line.startswith("#"):
345
- continue
346
- # Format: "filename | url" or "filename | local:path"
347
- parts = line.split("|", 1)
348
- filename = parts[0].strip()
349
- url = parts[1].strip() if len(parts) > 1 else ""
350
- assets.append({"filename": filename, "url": url})
351
-
352
- if not assets:
353
- os.makedirs(ap / "pass3", exist_ok=True)
354
- out_path = ap / "pass3" / "06_assets.md"
355
- with open(out_path, "w") as f:
356
- f.write(f"# Asset Inventory\n\nNo downloadable assets found for this component.\nGenerated: {ISO_DATE}\n")
357
- print(f" ⏭️ No assets - placeholder written")
358
- return True
359
-
360
- # Try to identify assets via code_connect_map (for icon names)
361
- # Icon entries have snippet like "Image.plusCircle" with empty source
362
- icon_names = set()
363
- ccm_path = ap / "pass1" / "code_connect_map_response.json"
364
- if ccm_path.exists():
365
- with open(ccm_path) as f:
366
- ccm = json.load(f)
367
- for entry in ccm.values():
368
- # Check componentName field
369
- name = entry.get("componentName", "")
370
- if name.startswith("Image."):
371
- icon_names.add(name.replace("Image.", ""))
372
- # Also check snippet field for "Image.xxx" patterns (icon entries use this)
373
- snippet = entry.get("snippet", "")
374
- m = re.match(r'^Image\.(\w+)$', snippet.strip())
375
- if m:
376
- icon_names.add(m.group(1))
377
-
378
- # Check each asset against registry and catalog
379
- results = []
380
- for asset in assets:
381
- filename = asset["filename"]
382
- stem = parse_asset_filename(filename)
383
- ext = Path(filename).suffix.lstrip(".")
384
-
385
- # Check SVG content for icon identification (if local file exists)
386
- local_path = asset["url"].replace("local:", "") if asset["url"].startswith("local:") else None
387
- svg_identified_name = None
388
-
389
- # Try registry lookup for known icon names from code_connect_map
390
- status = "Unknown"
391
- target = "N/A"
392
- identified_name = stem
393
- swift_accessor = None
394
-
395
- for icon_name in icon_names:
396
- lookup = run_cli("ui-token-lookup", ["--variables", icon_name, "--json"])
397
- if lookup:
398
- val = lookup.get(icon_name)
399
- if val and val.startswith("image:"):
400
- swift_path = val.split(":", 1)[1]
401
- identified_name = icon_name
402
- swift_accessor = f"Image{swift_path}"
403
- status = "Existing"
404
- break
405
-
406
- # Also search asset catalog directly if hash-named
407
- if status == "Unknown" and re.match(r'^[a-f0-9]{30,}$', stem):
408
- # Check if any icon from code_connect_map exists in catalog
409
- for icon_name in icon_names:
410
- # Convert camelCase to kebab-case for catalog search
411
- kebab = re.sub(r'([A-Z])', r'-\1', icon_name).lower().lstrip("-")
412
- matches = list(ASSET_CATALOG_ROOT.rglob(f"*{kebab}*")) if ASSET_CATALOG_ROOT.exists() else []
413
- if matches:
414
- identified_name = kebab
415
- status = "Existing"
416
- target = str(matches[0].relative_to(CONFIG_PKG))
417
- break
418
-
419
- results.append({
420
- "filename": filename,
421
- "ext": ext,
422
- "identified_name": identified_name,
423
- "status": status,
424
- "target": target,
425
- "swift_accessor": swift_accessor,
426
- })
427
-
428
- # Write output
429
- new_count = sum(1 for r in results if r["status"] == "New")
430
- existing_count = sum(1 for r in results if r["status"] == "Existing")
431
- unknown_count = sum(1 for r in results if r["status"] == "Unknown")
432
-
433
- lines = [
434
- f"# Asset Inventory\n",
435
- f"Generated: {ISO_DATE}",
436
- f"Source: pass1/design_context_assets.txt\n",
437
- "## Assets\n",
438
- "| File | Type | Identified As | Status | Swift Accessor |",
439
- "|------|------|---------------|--------|----------------|",
440
- ]
441
-
442
- for r in results:
443
- accessor = r["swift_accessor"] or " - "
444
- lines.append(f"| {r['filename']} | {r['ext']} | {r['identified_name']} | {r['status']} | `{accessor}` |" if r["swift_accessor"] else
445
- f"| {r['filename']} | {r['ext']} | {r['identified_name']} | {r['status']} | - |")
446
-
447
- lines.extend([
448
- "",
449
- "## Summary\n",
450
- f"- Total assets: {len(results)}",
451
- f"- New (to add): {new_count}",
452
- f"- Existing (no action): {existing_count}",
453
- f"- Unknown (check manually): {unknown_count}",
454
- ])
455
-
456
- if new_count > 0:
457
- lines.extend(["", "## Action Items\n"])
458
- for r in results:
459
- if r["status"] == "New":
460
- lines.append(f"- [ ] Add `{r['identified_name']}` to asset catalog")
461
-
462
- if new_count == 0:
463
- lines.extend(["", "## Action Items\n", "No new assets need to be added."])
464
-
465
- os.makedirs(ap / "pass3", exist_ok=True)
466
- out_path = ap / "pass3" / "06_assets.md"
467
- with open(out_path, "w") as f:
468
- f.write("\n".join(lines) + "\n")
469
-
470
- print(f" ✅ {out_path} ({new_count} new, {existing_count} existing, {unknown_count} unknown)")
471
- return True
472
-
473
-
474
- # --- 3F: Utility Integration ---
475
-
476
- def run_3f(artifact_path: str) -> bool:
477
- """Phase 3F: Utility Integration Summary."""
478
- print("Phase 3F: Utility Integration")
479
- ap = Path(artifact_path)
480
-
481
- # Primary source: component-utilities.json (structured, from Phase 2)
482
- utils_json = ap / "pass2" / "component-utilities.json"
483
- if not utils_json.exists():
484
- print(f" [error] pass2/component-utilities.json not found", file=sys.stderr)
485
- return False
486
-
487
- with open(utils_json) as f:
488
- utils = json.load(f)
489
-
490
- component_name = utils.get("componentName", "Unknown")
491
- if isinstance(component_name, dict):
492
- component_name = component_name.get("ios", "Unknown")
493
-
494
- testing_ids = utils.get("testingIdentifiers", [])
495
- loc_keys = utils.get("localizationKeys", [])
496
- a11y_keys = utils.get("accessibilityKeys", [])
497
- analytics = utils.get("analyticsEvents", [])
498
-
499
- # Build element-to-utility mapping table
500
- # Collect all unique elements from all utility categories
501
- elements = {} # element_name -> {testing, localization, accessibility, analytics}
502
-
503
- for tid in testing_ids:
504
- key = tid.get("key", "")
505
- elem = tid.get("usage", key.split(".")[-1] if "." in key else key)
506
- if elem not in elements:
507
- elements[elem] = {"testing": None, "localization": None, "accessibility": None, "analytics": None}
508
- elements[elem]["testing"] = f"`.{key}`"
509
-
510
- for lk in loc_keys:
511
- key = lk.get("key", "")
512
- elem = lk.get("usage", key.split(".")[-1] if "." in key else key)
513
- if elem not in elements:
514
- elements[elem] = {"testing": None, "localization": None, "accessibility": None, "analytics": None}
515
- elements[elem]["localization"] = f"`.{key}`"
516
-
517
- for ak in a11y_keys:
518
- key = ak.get("key", "")
519
- elem = ak.get("usage", ak.get("purpose", key.split(".")[-1] if "." in key else key))
520
- if elem not in elements:
521
- elements[elem] = {"testing": None, "localization": None, "accessibility": None, "analytics": None}
522
- elements[elem]["accessibility"] = f"`.{key}`"
523
-
524
- for ae in analytics:
525
- elem = ae.get("element", "unknown")
526
- if elem not in elements:
527
- elements[elem] = {"testing": None, "localization": None, "accessibility": None, "analytics": None}
528
- elements[elem]["analytics"] = ae.get("event", " - ")
529
-
530
- lines = [
531
- f"# Utility Integration\n",
532
- f"Component: {component_name}",
533
- f"Generated: {ISO_DATE}",
534
- f"Source: pass2/component-utilities.json\n",
535
- ]
536
-
537
- # Element mapping table
538
- if elements:
539
- lines.append("## Element-to-Utility Mapping\n")
540
- lines.append("| Element | Testing ID | Localization Key | Accessibility | Analytics |")
541
- lines.append("|---------|------------|------------------|---------------|-----------|")
542
- for elem, utils_map in elements.items():
543
- t = utils_map["testing"] or " - "
544
- l = utils_map["localization"] or " - "
545
- a = utils_map["accessibility"] or " - "
546
- an = utils_map["analytics"] or " - "
547
- lines.append(f"| {elem} | {t} | {l} | {a} | {an} |")
548
- lines.append("")
549
-
550
- # Testing Identifiers
551
- lines.append("## Testing Identifiers\n")
552
- if testing_ids:
553
- lines.append("| Key | Value | Usage |")
554
- lines.append("|-----|-------|-------|")
555
- for tid in testing_ids:
556
- lines.append(f"| {tid.get('key', '')} | {tid.get('value', '')} | `.accessibilityIdentifier(.{tid.get('key', '')})` |")
557
- else:
558
- lines.append("No testing identifiers.")
559
- lines.append("")
560
-
561
- # Localization Keys
562
- lines.append("## Localization Keys\n")
563
- if loc_keys:
564
- lines.append("| Key | Pattern |")
565
- lines.append("|-----|---------|")
566
- for lk in loc_keys:
567
- lines.append(f"| {lk.get('key', '')} | {lk.get('pattern', '')} |")
568
- else:
569
- lines.append("No localization keys (dynamic text only).")
570
- lines.append("")
571
-
572
- # Accessibility
573
- lines.append("## Accessibility\n")
574
- if a11y_keys:
575
- lines.append("| Key | Purpose |")
576
- lines.append("|-----|---------|")
577
- for ak in a11y_keys:
578
- lines.append(f"| {ak.get('key', '')} | {ak.get('purpose', '')} |")
579
- else:
580
- lines.append("No dedicated keys - using runtime accessibility patterns.")
581
- lines.append("")
582
-
583
- # Analytics
584
- lines.append("## Analytics Events\n")
585
- if analytics:
586
- lines.append("| Element | Event | Identifier Key |")
587
- lines.append("|---------|-------|----------------|")
588
- for ae in analytics:
589
- lines.append(f"| {ae.get('element', '')} | {ae.get('event', '')} | {ae.get('identifierKey', '')} |")
590
- else:
591
- lines.append("No analytics events (no interactive elements).")
592
- lines.append("")
593
-
594
- # Summary
595
- lines.append("## Summary\n")
596
- lines.append("| Category | Count |")
597
- lines.append("|----------|-------|")
598
- lines.append(f"| Testing Identifiers | {len(testing_ids)} |")
599
- lines.append(f"| Localization Keys | {len(loc_keys)} |")
600
- lines.append(f"| Accessibility Keys | {len(a11y_keys)} |")
601
- lines.append(f"| Analytics Events | {len(analytics)} |")
602
-
603
- os.makedirs(ap / "pass3", exist_ok=True)
604
- out_path = ap / "pass3" / "08_utility_integration.md"
605
- with open(out_path, "w") as f:
606
- f.write("\n".join(lines) + "\n")
607
-
608
- print(f" ✅ {out_path} ({len(testing_ids)} testing, {len(loc_keys)} loc, {len(a11y_keys)} a11y, {len(analytics)} analytics)")
609
- return True
610
-
611
-
612
- # --- 3G: Property Coverage ---
613
-
614
- def run_3g(artifact_path: str) -> bool:
615
- """Phase 3G: Property Coverage Verification."""
616
- print("Phase 3G: Property Coverage")
617
- ap = Path(artifact_path)
618
-
619
- # Read component_properties.json (Phase 1 output)
620
- comp_props_path = ap / "pass1" / "component_properties.json"
621
- if not comp_props_path.exists():
622
- print(f" [error] component_properties.json not found", file=sys.stderr)
623
- return False
624
-
625
- with open(comp_props_path) as f:
626
- comp_props = json.load(f)
627
-
628
- # Read 3D's structured JSON output
629
- arch_json_path = ap / "pass3" / "04b_component_architecture.json"
630
- if not arch_json_path.exists():
631
- print(f" [error] 04b_component_architecture.json not found (3D must emit this)", file=sys.stderr)
632
- return False
633
-
634
- with open(arch_json_path) as f:
635
- arch = json.load(f)
636
-
637
- # Extract all Figma properties
638
- prop_defs = comp_props.get("componentPropertyDefinitions", {})
639
- arch_props = {p["figmaKey"]: p for p in arch.get("properties", [])}
640
-
641
- component_name = comp_props.get("componentName", "Unknown")
642
-
643
- # Match each Figma property against architecture
644
- properties = []
645
- unmapped = []
646
-
647
- for figma_key, figma_def in prop_defs.items():
648
- prop_type = figma_def.get("type", "UNKNOWN")
649
- # Clean display name from figma_key (remove #nodeId suffix)
650
- display_name = re.sub(r'#\d+:\d+$', '', figma_key).strip()
651
- # Remove leading arrow for instance swaps
652
- display_name_clean = display_name.lstrip("↪ ").strip()
653
-
654
- if figma_key in arch_props:
655
- ap_entry = arch_props[figma_key]
656
- properties.append({
657
- "name": display_name,
658
- "type": prop_type,
659
- "figmaKey": figma_key,
660
- "status": ap_entry.get("status", "IMPLEMENT"),
661
- "swiftType": ap_entry.get("swiftType"),
662
- "reason": ap_entry.get("reason"),
663
- })
664
- else:
665
- # Not found in architecture - unmapped
666
- unmapped.append({
667
- "name": display_name,
668
- "type": prop_type,
669
- "figmaKey": figma_key,
670
- })
671
-
672
- # Include LAYER_TEXT entries from architecture (dynamic layer texts not in Figma properties)
673
- for arch_prop in arch.get("properties", []):
674
- if arch_prop.get("type") == "LAYER_TEXT" and arch_prop.get("status") == "IMPLEMENT":
675
- properties.append({
676
- "name": arch_prop["name"],
677
- "type": "LAYER_TEXT",
678
- "figmaKey": None,
679
- "status": "IMPLEMENT",
680
- "swiftType": arch_prop.get("swiftType"),
681
- "reason": None,
682
- })
683
-
684
- # Counts
685
- implemented = sum(1 for p in properties if p["status"] == "IMPLEMENT")
686
- skipped = sum(1 for p in properties if p["status"] == "SKIP")
687
- deferred = sum(1 for p in properties if p["status"] == "DEFER")
688
- total = len(properties)
689
-
690
- # Validate
691
- if unmapped:
692
- print(f" [error] {len(unmapped)} unmapped properties:", file=sys.stderr)
693
- for u in unmapped:
694
- print(f" - {u['name']} ({u['type']}): {u['figmaKey']}", file=sys.stderr)
695
- # Still write the file but include unmapped
696
-
697
- # Validate SKIP/DEFER have reasons
698
- missing_reasons = [p for p in properties if p["status"] in ("SKIP", "DEFER") and not p.get("reason")]
699
- if missing_reasons:
700
- print(f" [warn] {len(missing_reasons)} SKIP/DEFER properties missing reasons", file=sys.stderr)
701
-
702
- # Write output
703
- output = {
704
- "componentName": component_name,
705
- "generated": DATE_SHORT,
706
- "totalProperties": total,
707
- "implemented": implemented,
708
- "skipped": skipped,
709
- "deferred": deferred,
710
- "properties": properties,
711
- "unmapped": unmapped,
712
- }
713
-
714
- os.makedirs(ap / "pass3", exist_ok=True)
715
- out_path = ap / "pass3" / "00_property_coverage.json"
716
- with open(out_path, "w") as f:
717
- json.dump(output, f, indent=2, ensure_ascii=False)
718
-
719
- status = "✅" if not unmapped else "⚠️"
720
- print(f" {status} {out_path} ({implemented} implement, {skipped} skip, {deferred} defer, {len(unmapped)} unmapped)")
721
- return len(unmapped) == 0
722
-
723
-
724
- # --- spec: Implementation Spec Compilation ---
725
-
726
- def extract_md_value(filepath: Path, field: str) -> str:
727
- """Extract a value from a markdown table row like '| Field | Value |'."""
728
- if not filepath.exists():
729
- return ""
730
- with open(filepath) as f:
731
- for line in f:
732
- if f"| {field} |" in line or f"| {field.lower()} |" in line.lower():
733
- parts = [p.strip() for p in line.split("|")]
734
- if len(parts) >= 3:
735
- return parts[2]
736
- return ""
737
-
738
-
739
- def extract_md_section_lines(filepath: Path, heading: str) -> list[str]:
740
- """Extract lines under a markdown ## heading until next heading."""
741
- if not filepath.exists():
742
- return []
743
- lines = []
744
- in_section = False
745
- with open(filepath) as f:
746
- for line in f:
747
- if line.startswith("## ") and heading.lower() in line.lower():
748
- in_section = True
749
- continue
750
- elif line.startswith("## ") and in_section:
751
- break
752
- elif in_section:
753
- lines.append(line.rstrip())
754
- return lines
755
-
756
-
757
- def generate_api_shape_code(arch: dict) -> str:
758
- """Generate Swift API shape code from architecture JSON."""
759
- lines = []
760
- lines.append("```swift")
761
-
762
- # Enums
763
- for enum in arch.get("enums", []):
764
- cases = ", ".join(f".{c}" for c in enum["cases"])
765
- lines.append(f"public enum {enum['name']}: String, CaseIterable, Sendable {{")
766
- for case in enum["cases"]:
767
- lines.append(f" case {case}")
768
- lines.append("}")
769
- lines.append("")
770
-
771
- # Configuration struct (if COMPLEX)
772
- api = arch.get("apiDesign", {})
773
- config = api.get("configurationStruct")
774
- if config:
775
- lines.append(f"public struct {config['name']}: Sendable {{")
776
- for prop in config.get("properties", []):
777
- lines.append(f" public let {prop['name']}: {prop['swiftType']}")
778
- lines.append("")
779
- # Init
780
- init_params = []
781
- for prop in config.get("properties", []):
782
- default = prop.get("default")
783
- if default:
784
- init_params.append(f" {prop['name']}: {prop['swiftType']} = {default}")
785
- else:
786
- init_params.append(f" {prop['name']}: {prop['swiftType']}")
787
- lines.append(" public init(")
788
- lines.append(",\n".join(init_params))
789
- lines.append(" ) { ... }")
790
- lines.append("}")
791
- lines.append("")
792
-
793
- # Component struct
794
- comp = api.get("componentStruct", {})
795
- comp_name = comp.get("name", arch.get("componentName", "Component"))
796
- lines.append(f"public struct {comp_name}: View {{")
797
- if config:
798
- lines.append(f" private let configuration: {config['name']}")
799
- lines.append("")
800
- lines.append(f" public init(configuration: {config['name']}) {{")
801
- lines.append(" self.configuration = configuration")
802
- lines.append(" }")
803
- else:
804
- # SIMPLE - direct init params
805
- for param in comp.get("initParameters", []):
806
- lines.append(f" private let {param['name']}: {param['swiftType']}")
807
- lines.append("}")
808
- lines.append("```")
809
-
810
- return "\n".join(lines)
811
-
812
-
813
- def run_spec(artifact_path: str) -> bool:
814
- """Compile 07_implementation_spec.md from pass3 structured files."""
815
- print("Phase 3 Spec: Compiling implementation spec")
816
- ap = Path(artifact_path)
817
-
818
- # Read architecture JSON (primary data source)
819
- arch_path = ap / "pass3" / "04b_component_architecture.json"
820
- if not arch_path.exists():
821
- print(" [error] 04b_component_architecture.json not found", file=sys.stderr)
822
- return False
823
-
824
- with open(arch_path) as f:
825
- arch = json.load(f)
826
-
827
- comp_name = arch.get("componentName", "Unknown")
828
- complexity = arch.get("complexity", "SIMPLE")
829
- prop_count = arch.get("propertyCount", 0)
830
-
831
- # Read component_summary.json for dimensions
832
- summary_path = ap / "pass1" / "component_summary.json"
833
- dimensions = "unknown"
834
- node_id = ""
835
- if summary_path.exists():
836
- with open(summary_path) as f:
837
- summary = json.load(f)
838
- w = summary.get("dimensions", {}).get("width", "?")
839
- h = summary.get("dimensions", {}).get("height", "?")
840
- dimensions = f"{w} x {h} (component set)"
841
- node_id = summary.get("nodeId", "")
842
-
843
- # Read component location
844
- loc_path = ap / "pass3" / "01_component_location.md"
845
- comp_location = extract_md_value(loc_path, "Location")
846
- if not comp_location:
847
- comp_location = "(see 01_component_location.md)"
848
-
849
- # Read property coverage
850
- cov_path = ap / "pass3" / "00_property_coverage.json"
851
- if cov_path.exists():
852
- with open(cov_path) as f:
853
- cov = json.load(f)
854
- else:
855
- cov = {}
856
-
857
- # Generate API shape code
858
- api_code = generate_api_shape_code(arch)
859
-
860
- # Build variant mapping table
861
- variant_lines = []
862
- variant_lines.append("| Figma Property | Swift Property | Type |")
863
- variant_lines.append("|----------------|---------------|------|")
864
-
865
- all_props = arch.get("properties", []) + arch.get("implicitProperties", [])
866
- for prop in all_props:
867
- status = prop.get("status", "IMPLEMENT")
868
- if status == "SKIP":
869
- # Show merged info
870
- reason = prop.get("reason", "")
871
- swift_type = " - "
872
- swift_name = " - "
873
- if "merged" in reason.lower():
874
- # Find the implicit property this was merged into
875
- for ip in arch.get("implicitProperties", []):
876
- if ip.get("figmaKey") == prop.get("figmaKey"):
877
- swift_name = ip["name"]
878
- swift_type = f"{ip['swiftType']} (merged - nil = hidden)"
879
- break
880
- variant_lines.append(f"| {prop['name']} ({prop['type']}) | {swift_name} | {swift_type} |")
881
- else:
882
- swift_type = prop.get("swiftType", "?")
883
- figma_key = prop.get("figmaKey", prop.get("name", ""))
884
- # Swift property name
885
- swift_name = prop.get("name", "")
886
- # camelCase the name
887
- if swift_name and " " in swift_name:
888
- parts = swift_name.split()
889
- swift_name = parts[0].lower() + "".join(p.capitalize() for p in parts[1:])
890
- elif swift_name:
891
- swift_name = swift_name[0].lower() + swift_name[1:]
892
-
893
- if prop.get("type") == "BOOLEAN_MERGED":
894
- continue # Already shown via the SKIP entry
895
- type_desc = f"{swift_type} enum" if prop.get("values") else swift_type
896
- if prop.get("values"):
897
- vals = "/".join(f".{v}" for v in (arch.get("enums", [{}])[0].get("cases", []) if False else []))
898
- # Find the matching enum
899
- for e in arch.get("enums", []):
900
- if e.get("name") == swift_type:
901
- vals = "/".join(f".{c}" for c in e["cases"])
902
- type_desc = f"{swift_type} enum ({vals})"
903
- break
904
- if swift_type == "Bool" and prop.get("values"):
905
- type_desc = "Bool (Yes→true, No→false)"
906
-
907
- variant_lines.append(f"| {prop['name']} ({prop['type']}) | {swift_name} | {type_desc} |")
908
-
909
- # Nested dependencies
910
- nested_path = ap / "pass3" / "03_nested_components.md"
911
- nested_lines = extract_md_section_lines(nested_path, "Connected")
912
- if not nested_lines or all(not l.strip() or l.startswith("|--") for l in nested_lines):
913
- nested_section = "Self-contained component. No nested dependencies."
914
- else:
915
- nested_section = "\n".join(nested_lines)
916
-
917
- # Check for inline elements mention
918
- nested_full = ""
919
- if nested_path.exists():
920
- with open(nested_path) as f:
921
- nested_full = f.read()
922
- inline_mention = ""
923
- if "inline" in nested_full.lower():
924
- for line in nested_full.split("\n"):
925
- if "inline" in line.lower() and not line.startswith("#"):
926
- inline_mention = line.strip().lstrip("- ")
927
- break
928
-
929
- # Assets
930
- assets_path = ap / "pass3" / "06_assets.md"
931
- asset_summary_lines = extract_md_section_lines(assets_path, "Summary")
932
- asset_summary = "\n".join(l for l in asset_summary_lines if l.strip())
933
- if not asset_summary:
934
- asset_summary = "No assets."
935
-
936
- # Token summary
937
- token_path = ap / "pass3" / "02_token_mapping.md"
938
- token_summary_lines = extract_md_section_lines(token_path, "Summary")
939
- token_summary = "\n".join(l for l in token_summary_lines if l.strip())
940
-
941
- # Missing tokens
942
- missing_lines = extract_md_section_lines(token_path, "Missing Tokens")
943
- missing_count = sum(1 for l in missing_lines if l.strip().startswith("|") and "⚠️" not in l and "Token" not in l and "---" not in l)
944
- # Parse from summary
945
- token_mapped = 0
946
- token_missing = 0
947
- for l in token_summary_lines:
948
- if "mapped" in l.lower():
949
- m = re.search(r'(\d+)\s+mapped', l)
950
- if m:
951
- token_mapped += int(m.group(1))
952
- if "missing" in l.lower():
953
- m = re.search(r'(\d+)\s+(?:actionable|missing)', l)
954
- if m:
955
- token_missing += int(m.group(1))
956
-
957
- # Data models
958
- api_design = arch.get("apiDesign", {})
959
- sub_configs = api_design.get("subConfigurations", [])
960
- list_types = api_design.get("listTypes", [])
961
- if not sub_configs and not list_types:
962
- data_models = "No list inference. No sub-configurations. All properties are flat within the configuration struct."
963
- else:
964
- parts = []
965
- if sub_configs:
966
- parts.append(f"Sub-configurations: {', '.join(sc.get('name', '?') for sc in sub_configs)}")
967
- if list_types:
968
- parts.append(f"List types: {', '.join(lt.get('name', '?') for lt in list_types)}")
969
- data_models = " ".join(parts)
970
-
971
- # Files to create
972
- files_to_create = [f"- `{comp_name}.swift` (main view)"]
973
- if complexity == "COMPLEX":
974
- config_name = arch.get("configurationStruct", f"{comp_name}Configuration")
975
- files_to_create.append(f"- `{config_name}.swift` (configuration struct + enums)")
976
- files_to_create.append("- `FIGMA.md` (documentation)")
977
-
978
- # Workflow
979
- if complexity == "COMPLEX":
980
- config_name = arch.get("configurationStruct", f"{comp_name}Configuration")
981
- workflow = f"COMPLEX → Configuration struct pattern. {config_name} wraps all {prop_count} parameters."
982
- else:
983
- workflow = f"SIMPLE → Direct init parameters. {prop_count} parameters passed to init."
984
-
985
- # --- COMPILE SPEC ---
986
- spec = f"""# Implementation Spec
987
-
988
- Component: {comp_name}
989
- Generated: {DATE_SHORT}
990
-
991
- ## Component
992
-
993
- | Field | Value |
994
- |-------|-------|
995
- | Name | {comp_name} |
996
- | Path | {comp_location} |
997
- | Node ID | {node_id} |
998
- | Dimensions | {dimensions} |
999
- | Complexity | {complexity} |
1000
- | Properties | {prop_count} (after pattern reduction) |
1001
-
1002
- ## Files to Create
1003
-
1004
- {chr(10).join(files_to_create)}
1005
-
1006
- ## API Shape
1007
-
1008
- {api_code}
1009
-
1010
- ## Variant Mapping
1011
-
1012
- {chr(10).join(variant_lines)}
1013
-
1014
- ## Data Models
1015
-
1016
- {data_models}
1017
-
1018
- ## Nested Dependencies
1019
-
1020
- {nested_section}
1021
- {f'{chr(10)}{inline_mention}' if inline_mention else ''}
1022
-
1023
- ## New Assets
1024
-
1025
- {asset_summary}
1026
-
1027
- ## Token Mapping
1028
-
1029
- - Mapped: {token_mapped}
1030
- - Missing: {token_missing}
1031
-
1032
- ## Workflow
1033
-
1034
- {workflow}
1035
- """
1036
-
1037
- out_path = ap / "pass3" / "07_implementation_spec.md"
1038
- with open(out_path, "w") as f:
1039
- f.write(spec)
1040
-
1041
- print(f" ✅ {out_path}")
1042
- return True
1043
-
1044
-
1045
- # --- Main ---
1046
-
1047
- def main():
1048
- parser = argparse.ArgumentParser(description="Phase 3 scriptable sub-phases")
1049
- parser.add_argument("subphase", choices=["3b", "3e", "3f", "3g", "spec", "wave1", "all"],
1050
- help="Which sub-phase to run")
1051
- parser.add_argument("--artifact-path", required=True, help="Path to artifact directory")
1052
-
1053
- args = parser.parse_args()
1054
-
1055
- ensure_cli()
1056
-
1057
- results = {}
1058
-
1059
- if args.subphase in ("3b", "wave1", "all"):
1060
- results["3b"] = run_3b(args.artifact_path)
1061
-
1062
- if args.subphase in ("3e", "wave1", "all"):
1063
- results["3e"] = run_3e(args.artifact_path)
1064
-
1065
- if args.subphase in ("3f", "wave1", "all"):
1066
- results["3f"] = run_3f(args.artifact_path)
1067
-
1068
- if args.subphase in ("3g", "all"):
1069
- results["3g"] = run_3g(args.artifact_path)
1070
-
1071
- if args.subphase in ("spec", "all"):
1072
- results["spec"] = run_spec(args.artifact_path)
1073
-
1074
- # Summary
1075
- print(f"\n{'='*50}")
1076
- for phase, ok in results.items():
1077
- status = "✅ PASS" if ok else "❌ FAIL"
1078
- print(f" {phase}: {status}")
1079
-
1080
- if all(results.values()):
1081
- print(f"\nAll sub-phases complete.")
1082
- else:
1083
- failed = [k for k, v in results.items() if not v]
1084
- print(f"\nFailed: {', '.join(failed)}", file=sys.stderr)
1085
- sys.exit(1)
1086
-
1087
-
1088
- if __name__ == "__main__":
1089
- main()