@codeyam/codeyam-cli 0.1.0-staging.ad88eeb → 0.1.0-staging.b6c4c78

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 (279) hide show
  1. package/analyzer-template/.build-info.json +7 -7
  2. package/analyzer-template/log.txt +3 -3
  3. package/analyzer-template/package.json +2 -2
  4. package/analyzer-template/packages/ai/package.json +1 -1
  5. package/analyzer-template/packages/aws/package.json +1 -1
  6. package/analyzer-template/packages/database/package.json +1 -1
  7. package/analyzer-template/packages/database/src/lib/kysely/tables/editorScenariosTable.ts +72 -0
  8. package/analyzer-template/packages/database/src/lib/loadEntities.ts +0 -6
  9. package/analyzer-template/packages/database/src/lib/updateCommitMetadata.ts +0 -65
  10. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts +6 -0
  11. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts.map +1 -1
  12. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js +72 -0
  13. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
  14. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.d.ts.map +1 -1
  15. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.js +0 -6
  16. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.js.map +1 -1
  17. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.d.ts.map +1 -1
  18. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.js +0 -25
  19. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.js.map +1 -1
  20. package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js +45 -0
  21. package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js.map +1 -0
  22. package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js +101 -47
  23. package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js.map +1 -1
  24. package/codeyam-cli/src/commands/editor.js +1810 -386
  25. package/codeyam-cli/src/commands/editor.js.map +1 -1
  26. package/codeyam-cli/src/commands/init.js +68 -34
  27. package/codeyam-cli/src/commands/init.js.map +1 -1
  28. package/codeyam-cli/src/data/techStacks.js +2 -7
  29. package/codeyam-cli/src/data/techStacks.js.map +1 -1
  30. package/codeyam-cli/src/utils/__tests__/analyzerFinalization.test.js +173 -0
  31. package/codeyam-cli/src/utils/__tests__/analyzerFinalization.test.js.map +1 -0
  32. package/codeyam-cli/src/utils/__tests__/backgroundServer.test.js +46 -0
  33. package/codeyam-cli/src/utils/__tests__/backgroundServer.test.js.map +1 -0
  34. package/codeyam-cli/src/utils/__tests__/editorApi.test.js +18 -8
  35. package/codeyam-cli/src/utils/__tests__/editorApi.test.js.map +1 -1
  36. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +353 -1
  37. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
  38. package/codeyam-cli/src/utils/__tests__/editorBroadcastViewport.test.js +76 -0
  39. package/codeyam-cli/src/utils/__tests__/editorBroadcastViewport.test.js.map +1 -0
  40. package/codeyam-cli/src/utils/__tests__/editorDevServer.test.js +27 -2
  41. package/codeyam-cli/src/utils/__tests__/editorDevServer.test.js.map +1 -1
  42. package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js +6 -3
  43. package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js.map +1 -1
  44. package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js +223 -0
  45. package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js.map +1 -0
  46. package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js +163 -1
  47. package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js.map +1 -1
  48. package/codeyam-cli/src/utils/__tests__/editorMigration.test.js +430 -0
  49. package/codeyam-cli/src/utils/__tests__/editorMigration.test.js.map +1 -0
  50. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js +88 -1
  51. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js.map +1 -1
  52. package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js +47 -1
  53. package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js.map +1 -1
  54. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +968 -1
  55. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
  56. package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js +67 -0
  57. package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js.map +1 -1
  58. package/codeyam-cli/src/utils/__tests__/editorSessionFilter.test.js +66 -0
  59. package/codeyam-cli/src/utils/__tests__/editorSessionFilter.test.js.map +1 -0
  60. package/codeyam-cli/src/utils/__tests__/editorShouldRevalidate.test.js +53 -0
  61. package/codeyam-cli/src/utils/__tests__/editorShouldRevalidate.test.js.map +1 -0
  62. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js +102 -10
  63. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js.map +1 -1
  64. package/codeyam-cli/src/utils/__tests__/parseRegisterArg.test.js +30 -2
  65. package/codeyam-cli/src/utils/__tests__/parseRegisterArg.test.js.map +1 -1
  66. package/codeyam-cli/src/utils/__tests__/routePatternMatching.test.js +118 -0
  67. package/codeyam-cli/src/utils/__tests__/routePatternMatching.test.js.map +1 -0
  68. package/codeyam-cli/src/utils/__tests__/scenarioCoverage.test.js +227 -0
  69. package/codeyam-cli/src/utils/__tests__/scenarioCoverage.test.js.map +1 -0
  70. package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js +426 -218
  71. package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js.map +1 -1
  72. package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js +1 -0
  73. package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js.map +1 -1
  74. package/codeyam-cli/src/utils/analysisRunner.js +3 -1
  75. package/codeyam-cli/src/utils/analysisRunner.js.map +1 -1
  76. package/codeyam-cli/src/utils/analyzer.js +9 -0
  77. package/codeyam-cli/src/utils/analyzer.js.map +1 -1
  78. package/codeyam-cli/src/utils/analyzerFinalization.js +100 -0
  79. package/codeyam-cli/src/utils/analyzerFinalization.js.map +1 -0
  80. package/codeyam-cli/src/utils/backgroundServer.js +92 -16
  81. package/codeyam-cli/src/utils/backgroundServer.js.map +1 -1
  82. package/codeyam-cli/src/utils/database.js +37 -2
  83. package/codeyam-cli/src/utils/database.js.map +1 -1
  84. package/codeyam-cli/src/utils/editorApi.js +11 -5
  85. package/codeyam-cli/src/utils/editorApi.js.map +1 -1
  86. package/codeyam-cli/src/utils/editorAudit.js +51 -0
  87. package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
  88. package/codeyam-cli/src/utils/editorBroadcastViewport.js +26 -0
  89. package/codeyam-cli/src/utils/editorBroadcastViewport.js.map +1 -0
  90. package/codeyam-cli/src/utils/editorDevServer.js +5 -1
  91. package/codeyam-cli/src/utils/editorDevServer.js.map +1 -1
  92. package/codeyam-cli/src/utils/editorEntityChangeStatus.js +1 -1
  93. package/codeyam-cli/src/utils/editorEntityChangeStatus.js.map +1 -1
  94. package/codeyam-cli/src/utils/editorEntityHelpers.js +129 -0
  95. package/codeyam-cli/src/utils/editorEntityHelpers.js.map +1 -0
  96. package/codeyam-cli/src/utils/editorLoaderHelpers.js +60 -1
  97. package/codeyam-cli/src/utils/editorLoaderHelpers.js.map +1 -1
  98. package/codeyam-cli/src/utils/editorMigration.js +224 -0
  99. package/codeyam-cli/src/utils/editorMigration.js.map +1 -0
  100. package/codeyam-cli/src/utils/editorPreview.js +31 -0
  101. package/codeyam-cli/src/utils/editorPreview.js.map +1 -1
  102. package/codeyam-cli/src/utils/editorScenarios.js +309 -0
  103. package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
  104. package/codeyam-cli/src/utils/editorSeedAdapter.js +183 -4
  105. package/codeyam-cli/src/utils/editorSeedAdapter.js.map +1 -1
  106. package/codeyam-cli/src/utils/editorShouldRevalidate.js +21 -0
  107. package/codeyam-cli/src/utils/editorShouldRevalidate.js.map +1 -0
  108. package/codeyam-cli/src/utils/entityChangeStatus.js +16 -3
  109. package/codeyam-cli/src/utils/entityChangeStatus.js.map +1 -1
  110. package/codeyam-cli/src/utils/entityChangeStatus.server.js +41 -3
  111. package/codeyam-cli/src/utils/entityChangeStatus.server.js.map +1 -1
  112. package/codeyam-cli/src/utils/install-skills.js +9 -0
  113. package/codeyam-cli/src/utils/install-skills.js.map +1 -1
  114. package/codeyam-cli/src/utils/parseRegisterArg.js.map +1 -1
  115. package/codeyam-cli/src/utils/progress.js +2 -2
  116. package/codeyam-cli/src/utils/progress.js.map +1 -1
  117. package/codeyam-cli/src/utils/routePatternMatching.js +129 -0
  118. package/codeyam-cli/src/utils/routePatternMatching.js.map +1 -0
  119. package/codeyam-cli/src/utils/scenarioCoverage.js +74 -0
  120. package/codeyam-cli/src/utils/scenarioCoverage.js.map +1 -0
  121. package/codeyam-cli/src/utils/scenariosManifest.js +207 -75
  122. package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -1
  123. package/codeyam-cli/src/utils/serverState.js +30 -0
  124. package/codeyam-cli/src/utils/serverState.js.map +1 -1
  125. package/codeyam-cli/src/utils/setupClaudeCodeSettings.js +1 -0
  126. package/codeyam-cli/src/utils/setupClaudeCodeSettings.js.map +1 -1
  127. package/codeyam-cli/src/utils/simulationGateMiddleware.js +8 -1
  128. package/codeyam-cli/src/utils/simulationGateMiddleware.js.map +1 -1
  129. package/codeyam-cli/src/utils/slugUtils.js +25 -0
  130. package/codeyam-cli/src/utils/slugUtils.js.map +1 -0
  131. package/codeyam-cli/src/utils/syncMocksMiddleware.js +2 -2
  132. package/codeyam-cli/src/utils/syncMocksMiddleware.js.map +1 -1
  133. package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js +40 -0
  134. package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js.map +1 -0
  135. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js +389 -0
  136. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js.map +1 -1
  137. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js +146 -0
  138. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js.map +1 -0
  139. package/codeyam-cli/src/webserver/app/lib/clientErrors.js +65 -0
  140. package/codeyam-cli/src/webserver/app/lib/clientErrors.js.map +1 -0
  141. package/codeyam-cli/src/webserver/app/lib/git.js +3 -2
  142. package/codeyam-cli/src/webserver/app/lib/git.js.map +1 -1
  143. package/codeyam-cli/src/webserver/app/types/editor.js +8 -0
  144. package/codeyam-cli/src/webserver/app/types/editor.js.map +1 -0
  145. package/codeyam-cli/src/webserver/backgroundServer.js +18 -4
  146. package/codeyam-cli/src/webserver/backgroundServer.js.map +1 -1
  147. package/codeyam-cli/src/webserver/build/client/assets/CopyButton-CzTDWkF2.js +1 -0
  148. package/codeyam-cli/src/webserver/build/client/assets/{EntityItem-BcgbViKV.js → EntityItem-BFbq6iFk.js} +3 -3
  149. package/codeyam-cli/src/webserver/build/client/assets/EntityTypeBadge-CQgyEGV-.js +1 -0
  150. package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeIcon-CQIG2qda.js → EntityTypeIcon-B6OMi58N.js} +1 -1
  151. package/codeyam-cli/src/webserver/build/client/assets/InlineSpinner-DuYodzo1.js +1 -0
  152. package/codeyam-cli/src/webserver/build/client/assets/InteractivePreview-CXo9EeCl.js +25 -0
  153. package/codeyam-cli/src/webserver/build/client/assets/LibraryFunctionPreview-DYCNb2It.js +3 -0
  154. package/codeyam-cli/src/webserver/build/client/assets/{LoadingDots-BU_OAEMP.js → LoadingDots-By5zI316.js} +1 -1
  155. package/codeyam-cli/src/webserver/build/client/assets/{LogViewer-ceAyBX-H.js → LogViewer-CZgY3sxX.js} +3 -3
  156. package/codeyam-cli/src/webserver/build/client/assets/{ReportIssueModal-BzHcG7SE.js → ReportIssueModal-CnYYwRDw.js} +2 -2
  157. package/codeyam-cli/src/webserver/build/client/assets/SafeScreenshot-CDoF7ZpU.js +1 -0
  158. package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-0DY_NKil.js → ScenarioViewer-DrnfvaLL.js} +3 -3
  159. package/codeyam-cli/src/webserver/build/client/assets/Spinner-Df3UCi8k.js +34 -0
  160. package/codeyam-cli/src/webserver/build/client/assets/TruncatedFilePath-CK7-NaPZ.js +1 -0
  161. package/codeyam-cli/src/webserver/build/client/assets/ViewportInspectBar-DRKR9T0U.js +1 -0
  162. package/codeyam-cli/src/webserver/build/client/assets/{_index-DLxKhri3.js → _index-ClR-g3tY.js} +2 -2
  163. package/codeyam-cli/src/webserver/build/client/assets/{activity.(_tab)-BcY3q6nt.js → activity.(_tab)-DTH6ydEA.js} +3 -3
  164. package/codeyam-cli/src/webserver/build/client/assets/{addon-web-links-Duc5hnl7.js → addon-web-links-74hnHF59.js} +1 -1
  165. package/codeyam-cli/src/webserver/build/client/assets/{agent-transcripts-Bni3iiUj.js → agent-transcripts-B8CYhCO9.js} +3 -3
  166. package/codeyam-cli/src/webserver/build/client/assets/api.editor-rename-scenario-l0sNRNKZ.js +1 -0
  167. package/codeyam-cli/src/webserver/build/client/assets/api.editor-save-seed-state-l0sNRNKZ.js +1 -0
  168. package/codeyam-cli/src/webserver/build/client/assets/api.editor-scenario-coverage-l0sNRNKZ.js +1 -0
  169. package/codeyam-cli/src/webserver/build/client/assets/api.editor-scenario-prompt-l0sNRNKZ.js +1 -0
  170. package/codeyam-cli/src/webserver/build/client/assets/api.editor-session-l0sNRNKZ.js +1 -0
  171. package/codeyam-cli/src/webserver/build/client/assets/{book-open-BYOypzCa.js → book-open-CLaoh4ac.js} +1 -1
  172. package/codeyam-cli/src/webserver/build/client/assets/{chevron-down-C_Pmso5S.js → chevron-down-BZ2DZxbW.js} +1 -1
  173. package/codeyam-cli/src/webserver/build/client/assets/{chunk-JZWAC4HX-C4pqxYJB.js → chunk-JZWAC4HX-BBXArFPl.js} +13 -21
  174. package/codeyam-cli/src/webserver/build/client/assets/{circle-check-BVMi9VA5.js → circle-check-CT4unAk-.js} +1 -1
  175. package/codeyam-cli/src/webserver/build/client/assets/{copy-n2FB0_Sw.js → copy-zK0B6Nu-.js} +1 -1
  176. package/codeyam-cli/src/webserver/build/client/assets/{createLucideIcon-CC6AbExI.js → createLucideIcon-DJB0YQJL.js} +1 -1
  177. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-CkXFP_i-.js +1 -0
  178. package/codeyam-cli/src/webserver/build/client/assets/editor._tab-DPw7NZHc.js +1 -0
  179. package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-y_5LB2iU.js +58 -0
  180. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-DBa7T2FK.js +41 -0
  181. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-BF4oLwaE.js → entity._sha._-BqAN7hyG.js} +2 -2
  182. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-BOi8kpwd.js +6 -0
  183. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-Dg1NhIms.js +6 -0
  184. package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.create-scenario-CJX6kkkV.js +6 -0
  185. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-BMvVHNXU.js → entity._sha_.edit._scenarioId-BhVjZhKg.js} +2 -2
  186. package/codeyam-cli/src/webserver/build/client/assets/{entry.client-DTvKq3TY.js → entry.client-_gzKltPN.js} +6 -6
  187. package/codeyam-cli/src/webserver/build/client/assets/fileTableUtils-Daa96Fr1.js +1 -0
  188. package/codeyam-cli/src/webserver/build/client/assets/files-CV_17tZS.js +1 -0
  189. package/codeyam-cli/src/webserver/build/client/assets/git-D-YXmMbR.js +1 -0
  190. package/codeyam-cli/src/webserver/build/client/assets/globals-BCTpZEY8.css +1 -0
  191. package/codeyam-cli/src/webserver/build/client/assets/{index-yHOVb4rc.js → index-Blo6EK8G.js} +1 -1
  192. package/codeyam-cli/src/webserver/build/client/assets/{index-10oVnAAH.js → index-BsX0F-9C.js} +1 -1
  193. package/codeyam-cli/src/webserver/build/client/assets/{index-BcvgDzbZ.js → index-CCrgCshv.js} +1 -1
  194. package/codeyam-cli/src/webserver/build/client/assets/jsx-runtime-D_zvdyIk.js +9 -0
  195. package/codeyam-cli/src/webserver/build/client/assets/labs-Byazq8Pv.js +1 -0
  196. package/codeyam-cli/src/webserver/build/client/assets/{loader-circle-DaAZ_H2w.js → loader-circle-DVQ0oHR7.js} +1 -1
  197. package/codeyam-cli/src/webserver/build/client/assets/manifest-5f1c29f5.js +1 -0
  198. package/codeyam-cli/src/webserver/build/client/assets/memory-b-VmA2Vj.js +101 -0
  199. package/codeyam-cli/src/webserver/build/client/assets/{pause-f5-1lKBt.js → pause-DGcndCAa.js} +1 -1
  200. package/codeyam-cli/src/webserver/build/client/assets/root-BBCQJ_ZM.js +67 -0
  201. package/codeyam-cli/src/webserver/build/client/assets/{search-Di64LWVb.js → search-C0Uw0bcK.js} +1 -1
  202. package/codeyam-cli/src/webserver/build/client/assets/settings-OoNgHIfW.js +1 -0
  203. package/codeyam-cli/src/webserver/build/client/assets/simulations-Bcemfu8a.js +1 -0
  204. package/codeyam-cli/src/webserver/build/client/assets/{terminal-Br7MOqts.js → terminal-BgMmG7R9.js} +1 -1
  205. package/codeyam-cli/src/webserver/build/client/assets/{triangle-alert-BLdiCuG-.js → triangle-alert-Cs87hJYK.js} +1 -1
  206. package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-BR3Rs7JY.js +1 -0
  207. package/codeyam-cli/src/webserver/build/client/assets/{useLastLogLine-C14nCb1q.js → useLastLogLine-BxxP_XF9.js} +1 -1
  208. package/codeyam-cli/src/webserver/build/client/assets/useReportContext-BermyNU5.js +1 -0
  209. package/codeyam-cli/src/webserver/build/client/assets/useToast-a_QN_W9_.js +1 -0
  210. package/codeyam-cli/src/webserver/build/client/sound-test.html +98 -0
  211. package/codeyam-cli/src/webserver/build/server/assets/index-BLKsJR3o.js +1 -0
  212. package/codeyam-cli/src/webserver/build/server/assets/init-C2iMAqYu.js +10 -0
  213. package/codeyam-cli/src/webserver/build/server/assets/server-build-DR42Xd5a.js +489 -0
  214. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  215. package/codeyam-cli/src/webserver/build-info.json +5 -5
  216. package/codeyam-cli/src/webserver/editorProxy.js +309 -13
  217. package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
  218. package/codeyam-cli/src/webserver/idleDetector.js +73 -0
  219. package/codeyam-cli/src/webserver/idleDetector.js.map +1 -0
  220. package/codeyam-cli/src/webserver/mockStateEvents.js +28 -0
  221. package/codeyam-cli/src/webserver/mockStateEvents.js.map +1 -0
  222. package/codeyam-cli/src/webserver/public/sound-test.html +98 -0
  223. package/codeyam-cli/src/webserver/server.js +55 -4
  224. package/codeyam-cli/src/webserver/server.js.map +1 -1
  225. package/codeyam-cli/src/webserver/terminalServer.js +153 -33
  226. package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
  227. package/codeyam-cli/templates/chrome-extension-react/README.md +46 -0
  228. package/codeyam-cli/templates/chrome-extension-react/package.json +1 -0
  229. package/codeyam-cli/templates/chrome-extension-react/vite.config.ts +6 -0
  230. package/codeyam-cli/templates/codeyam-editor-claude.md +84 -5
  231. package/codeyam-cli/templates/editor-step-hook.py +107 -24
  232. package/codeyam-cli/templates/expo-react-native/README.md +41 -0
  233. package/codeyam-cli/templates/expo-react-native/package.json +1 -0
  234. package/codeyam-cli/templates/nextjs-prisma-sqlite/DATABASE.md +14 -0
  235. package/codeyam-cli/templates/nextjs-prisma-sqlite/README.md +53 -0
  236. package/codeyam-cli/templates/nextjs-prisma-sqlite/package.json +1 -0
  237. package/codeyam-cli/templates/nextjs-prisma-sqlite/seed-adapter.ts +42 -7
  238. package/codeyam-cli/templates/nextjs-prisma-supabase/README.md +52 -0
  239. package/codeyam-cli/templates/nextjs-prisma-supabase/package.json +1 -0
  240. package/codeyam-cli/templates/seed-adapters/supabase.ts +282 -0
  241. package/codeyam-cli/templates/skills/codeyam-dev-mode/SKILL.md +1 -1
  242. package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +76 -10
  243. package/package.json +1 -1
  244. package/packages/database/src/lib/kysely/tables/editorScenariosTable.js +72 -0
  245. package/packages/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
  246. package/packages/database/src/lib/loadEntities.js +0 -6
  247. package/packages/database/src/lib/loadEntities.js.map +1 -1
  248. package/packages/database/src/lib/updateCommitMetadata.js +0 -25
  249. package/packages/database/src/lib/updateCommitMetadata.js.map +1 -1
  250. package/codeyam-cli/src/webserver/build/client/assets/CopyButton-BPXZwM4t.js +0 -1
  251. package/codeyam-cli/src/webserver/build/client/assets/EntityTypeBadge-g3saevPb.js +0 -1
  252. package/codeyam-cli/src/webserver/build/client/assets/InlineSpinner-Bu6c6aDe.js +0 -1
  253. package/codeyam-cli/src/webserver/build/client/assets/InteractivePreview-DYFW3lDD.js +0 -25
  254. package/codeyam-cli/src/webserver/build/client/assets/LibraryFunctionPreview-DLeucoVX.js +0 -3
  255. package/codeyam-cli/src/webserver/build/client/assets/SafeScreenshot-BED4B6sP.js +0 -1
  256. package/codeyam-cli/src/webserver/build/client/assets/Spinner-Bb5uFQ5V.js +0 -34
  257. package/codeyam-cli/src/webserver/build/client/assets/TruncatedFilePath-C8OKAR5x.js +0 -1
  258. package/codeyam-cli/src/webserver/build/client/assets/ViewportInspectBar-oAf2Kqsf.js +0 -1
  259. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-Csi0_PMl.js +0 -1
  260. package/codeyam-cli/src/webserver/build/client/assets/editor-BuT_Huj0.js +0 -10
  261. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-B7ztwLut.js +0 -41
  262. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-D5rYBT5x.js +0 -6
  263. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-CF164ouH.js +0 -6
  264. package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.create-scenario-p9hhkjJM.js +0 -6
  265. package/codeyam-cli/src/webserver/build/client/assets/fileTableUtils-cPo8LiG3.js +0 -1
  266. package/codeyam-cli/src/webserver/build/client/assets/files-BZrlFE1F.js +0 -1
  267. package/codeyam-cli/src/webserver/build/client/assets/git-DdZcvjGh.js +0 -1
  268. package/codeyam-cli/src/webserver/build/client/assets/globals-BkWJ_UNc.css +0 -1
  269. package/codeyam-cli/src/webserver/build/client/assets/labs-Zk7ryIM1.js +0 -1
  270. package/codeyam-cli/src/webserver/build/client/assets/manifest-b0f1372e.js +0 -1
  271. package/codeyam-cli/src/webserver/build/client/assets/memory-Bl2rpw8u.js +0 -96
  272. package/codeyam-cli/src/webserver/build/client/assets/root-B_X8HS1x.js +0 -67
  273. package/codeyam-cli/src/webserver/build/client/assets/settings-0OrEMU6J.js +0 -1
  274. package/codeyam-cli/src/webserver/build/client/assets/simulations-DWT-CvLy.js +0 -1
  275. package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-CrAK28Bc.js +0 -1
  276. package/codeyam-cli/src/webserver/build/client/assets/useReportContext-O-jkvSPx.js +0 -1
  277. package/codeyam-cli/src/webserver/build/client/assets/useToast-9FIWuYfK.js +0 -1
  278. package/codeyam-cli/src/webserver/build/server/assets/index-CbF6h3dj.js +0 -1
  279. package/codeyam-cli/src/webserver/build/server/assets/server-build-DRFwTJqO.js +0 -367
@@ -11,15 +11,17 @@ import { IS_INTERNAL_BUILD } from "../utils/buildFlags.js";
11
11
  import { startBackgroundServer } from "../utils/backgroundServer.js";
12
12
  import { installClaudeCodeSkills } from "../utils/install-skills.js";
13
13
  import { setupClaudeCodeSettings } from "../utils/setupClaudeCodeSettings.js";
14
- import { getAnalyzerTemplatePath, isAnalyzerFinalized, } from "../utils/analyzer.js";
14
+ import { ensureAnalyzerFinalized, } from "../utils/analyzerFinalization.js";
15
15
  import { APP_FORMATS, TECH_STACKS } from "../data/techStacks.js";
16
16
  import { getProjectRoot as getStateProjectRoot } from "../state.js";
17
17
  import initCommand from "./init.js";
18
- import { readManifest, syncManifestToDatabase, } from "../utils/scenariosManifest.js";
19
- import { clearEditorState, clearEditorUserPrompt, } from "../utils/editorScenarios.js";
18
+ import { scanScenarioFiles, syncScenarioFilesToDatabase, backfillScenarioMetadata, migrateScenarioFormats, } from "../utils/scenariosManifest.js";
19
+ import { clearEditorState, clearEditorUserPrompt, validateStepTransition, } from "../utils/editorScenarios.js";
20
20
  import { validateSeedData, detectSeedAdapter, } from "../utils/editorSeedAdapter.js";
21
21
  import { buildEditorApiRequest, callEditorApi, EDITOR_API_SUBCOMMANDS, } from "../utils/editorApi.js";
22
22
  import { parseRegisterArg } from "../utils/parseRegisterArg.js";
23
+ import { sanitizeGlossaryEntries } from "../utils/editorLoaderHelpers.js";
24
+ import { readMigrationState, writeMigrationState, advanceToNextPage, completePage, getMigrationResumeInfo, } from "../utils/editorMigration.js";
23
25
  const __filename = fileURLToPath(import.meta.url);
24
26
  const __dirname = path.dirname(__filename);
25
27
  const STEP_LABELS = {
@@ -36,6 +38,21 @@ const STEP_LABELS = {
36
38
  11: 'Journal',
37
39
  12: 'Review',
38
40
  13: 'Present',
41
+ 14: 'Commit',
42
+ 15: 'Finalize',
43
+ 16: 'Push',
44
+ };
45
+ const MIGRATION_STEP_LABELS = {
46
+ 1: 'Survey',
47
+ 2: 'App Scenarios',
48
+ 3: 'Component Scenarios',
49
+ 4: 'Preview',
50
+ 5: 'Discuss',
51
+ 6: 'Decompose',
52
+ 7: 'Extract',
53
+ 8: 'Recapture',
54
+ 9: 'Journal',
55
+ 10: 'Present',
39
56
  };
40
57
  /**
41
58
  * Append a JSONL log entry to .codeyam/logs/editor-log.jsonl
@@ -56,6 +73,18 @@ function logEvent(root, event, data) {
56
73
  // Logging is best-effort
57
74
  }
58
75
  }
76
+ /**
77
+ * Read the design system file if it exists.
78
+ */
79
+ function readDesignSystem(root) {
80
+ const designSystemPath = path.join(root, '.codeyam', 'design-system.md');
81
+ try {
82
+ return fs.readFileSync(designSystemPath, 'utf8');
83
+ }
84
+ catch {
85
+ return null;
86
+ }
87
+ }
59
88
  /**
60
89
  * Get the project root (where .codeyam/ lives) or cwd.
61
90
  */
@@ -105,11 +134,62 @@ function hasProject(root) {
105
134
  return fs.existsSync(path.join(root, 'package.json'));
106
135
  }
107
136
  /**
108
- * Get the CodeYam server port from env or default.
137
+ * Get the CodeYam server port.
138
+ * Reads from the server state file first (actual running port), then falls
139
+ * back to CODEYAM_PORT env var, then to the default 3111.
140
+ * This is critical when the server auto-selected a different port because
141
+ * the default was occupied by another project's server.
109
142
  */
110
143
  function getServerPort() {
144
+ try {
145
+ const root = getProjectRoot();
146
+ if (root) {
147
+ const statePath = path.join(root, '.codeyam', 'server.json');
148
+ if (fs.existsSync(statePath)) {
149
+ const state = JSON.parse(fs.readFileSync(statePath, 'utf8'));
150
+ if (state.port)
151
+ return String(state.port);
152
+ }
153
+ }
154
+ }
155
+ catch {
156
+ // Fall through to defaults
157
+ }
111
158
  return process.env.CODEYAM_PORT || '3111';
112
159
  }
160
+ /**
161
+ * Read the project's default dimension name and available screen size names.
162
+ * Used by step instructions to show project-specific dimension examples
163
+ * instead of hardcoded "Desktop".
164
+ */
165
+ function getProjectDimensions(root) {
166
+ try {
167
+ const configPath = path.join(root, '.codeyam', 'config.json');
168
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
169
+ const defaultName = config.defaultScreenSize?.name ||
170
+ (config.screenSizes ? Object.keys(config.screenSizes)[0] : null) ||
171
+ 'Desktop';
172
+ const names = config.screenSizes ? Object.keys(config.screenSizes) : [];
173
+ return { defaultName, names };
174
+ }
175
+ catch {
176
+ return { defaultName: 'Desktop', names: [] };
177
+ }
178
+ }
179
+ /**
180
+ * Print dimension guidance when the project has multiple screen sizes.
181
+ * Tells Claude to pick the right dimension for the content being previewed
182
+ * instead of blindly using the default for everything.
183
+ */
184
+ function printDimensionGuidance(defaultName, names) {
185
+ if (names.length <= 1)
186
+ return;
187
+ // Find a non-default dimension to suggest as the "other" option
188
+ const otherName = names.find((n) => n !== defaultName) || names[1];
189
+ console.log(chalk.yellow(` IMPORTANT: Choose the dimension that matches what you're previewing. Available: ${names.map((n) => `"${n}"`).join(', ')}.`));
190
+ console.log(chalk.yellow(` Do NOT always use "${defaultName}". Full pages, standalone views, and desktop layouts should use "${otherName}".`));
191
+ console.log(chalk.yellow(` Think about the CONTENT — a full-page library view needs a large viewport, not a popup-sized one.`));
192
+ }
113
193
  /**
114
194
  * Print a checklist item.
115
195
  * Inline backtick-wrapped text is highlighted in cyan for visibility.
@@ -119,6 +199,138 @@ function checkbox(text) {
119
199
  const highlighted = text.replace(/`([^`]+)`/g, (_m, code) => chalk.cyan(code));
120
200
  console.log(` ${chalk.dim('[ ]')} ${highlighted}`);
121
201
  }
202
+ /**
203
+ * Shared app scenario registration instructions used by editor Step 8 and migration Steps 1/6.
204
+ * Prints seed-based scenarios, mock-based fallback, @ file prefix, externalApis, url requirement, and error checking.
205
+ */
206
+ function printAppScenarioInstructions(pageName, route) {
207
+ checkbox('Check existing scenarios: `codeyam editor scenarios`');
208
+ console.log(chalk.dim(' Review existing scenarios — reuse and update their data rather than'));
209
+ console.log(chalk.dim(' creating duplicates. Re-register with the same name to update a scenario.'));
210
+ checkbox('Cover key data states (at least 2-3 scenarios per page)');
211
+ console.log(chalk.yellow(' Each page needs its own scenarios — a detail page needs different data than the catalog'));
212
+ if (pageName) {
213
+ console.log(chalk.dim(` Example: "${pageName} - Full Data", "${pageName} - Empty State"`));
214
+ }
215
+ else {
216
+ console.log(chalk.dim(' Catalog: "Full Catalog", "Empty Catalog", "Single Category"'));
217
+ console.log(chalk.dim(' Detail: "Detail - With Reviews", "Detail - No Reviews", "Detail - High Rated"'));
218
+ }
219
+ console.log();
220
+ checkbox('Register each scenario with type "application" and the real page url:');
221
+ console.log(chalk.dim(` codeyam editor register '{"name":"${pageName || 'Page'} - Full Data","type":"application","url":"${route || '/'}",...}'`));
222
+ console.log(chalk.yellow(' IMPORTANT: "url" MUST be the real page route — without it, the wrong page gets screenshotted.'));
223
+ console.log(chalk.yellow(' IMPORTANT: include "pageFilePath" with the source file that renders this route (e.g., "src/App.tsx").'));
224
+ console.log();
225
+ checkbox('Choose the right data approach for scenarios:');
226
+ console.log(chalk.dim(' With seed adapter: add "seed":{...} to populate the database for each state'));
227
+ console.log(chalk.dim(' Without seed adapter: add "mockData":{"routes":{"/api/...":{"body":[...]}}} to mock API responses'));
228
+ console.log(chalk.dim(' For external APIs (Stripe, weather, etc.): add "externalApis":{"GET https://...":{"body":[...],"status":200}}'));
229
+ checkbox('For large seed/mock data, write JSON to a temp file and use @ prefix:');
230
+ console.log(chalk.dim(' Write JSON to .codeyam/tmp/scenario.json then register with:'));
231
+ console.log(chalk.dim(' codeyam editor register @.codeyam/tmp/scenario.json'));
232
+ checkbox('If the app uses auth, email, or other patterns: see FEATURE_PATTERNS.md for scenario guidance');
233
+ checkbox('After each registration, check the response for `clientErrors`');
234
+ console.log(chalk.yellow(' If clientErrors is non-empty → fix the issue and re-register the scenario'));
235
+ console.log(chalk.yellow(' Fix client errors and re-register before moving on'));
236
+ }
237
+ /**
238
+ * Shared glossary instructions used by editor Step 6 and migration Step 8.
239
+ * Prints format, required fields, and example.
240
+ */
241
+ function printGlossaryInstructions(feature) {
242
+ console.log(chalk.bold.yellow('IMPORTANT: glossary.json is a plain JSON array — NOT an object wrapper.'));
243
+ checkbox("Read `.codeyam/glossary.json` (create if it doesn't exist)");
244
+ checkbox('Add an entry for each new function/component extracted');
245
+ checkbox('Each entry should have: name, filePath, description, parameters, returnType, tags, feature');
246
+ checkbox('For each function with a test file, set `testFile` to the relative path');
247
+ console.log();
248
+ console.log(chalk.bold('File format:'));
249
+ console.log(chalk.dim(' ['));
250
+ console.log(chalk.dim(' { "name": "calculateTotal", "filePath": "app/utils/pricing.ts",'));
251
+ console.log(chalk.dim(' "description": "Calculates total price including tax and discounts",'));
252
+ console.log(chalk.dim(' "parameters": [{ "name": "items", "type": "CartItem[]" }],'));
253
+ console.log(chalk.dim(' "returnType": "number", "tags": ["pricing"],'));
254
+ console.log(chalk.dim(' "testFile": "app/utils/pricing.test.ts",'));
255
+ console.log(chalk.dim(` "feature": "${feature || '...'}", "createdAt": "..." }`));
256
+ console.log(chalk.dim(' ]'));
257
+ }
258
+ /**
259
+ * Shared extraction plan instructions used by editor Step 4 and migration Step 6.
260
+ * Prints THE RULE, extractable categories, plan format requirements.
261
+ */
262
+ function printExtractionPlanInstructions() {
263
+ console.log(chalk.bold.red('THE RULE: No direct JSX in page files.'));
264
+ console.log(chalk.yellow(' After extraction, a page/route file should import and compose components — nothing else.'));
265
+ console.log(chalk.yellow(' Every distinct visual section in the page is its own component.'));
266
+ console.log(chalk.yellow(' Every component that renders multiple distinct sections should be split into sub-components.'));
267
+ console.log(chalk.yellow(' If a component has N visually distinct parts, it should compose N sub-components.'));
268
+ console.log();
269
+ console.log(chalk.bold('Checklist:'));
270
+ checkbox('Read `.codeyam/glossary.json` — note reusable functions/components');
271
+ checkbox('Read EVERY file used by this page/feature');
272
+ console.log(chalk.yellow(' For EACH file, identify EVERY extractable piece:'));
273
+ console.log(chalk.yellow(' Components: headers, nav, loading states, empty states, error states,'));
274
+ console.log(chalk.yellow(' badges/pills, image containers, card sections, form fields, modals,'));
275
+ console.log(chalk.yellow(' titles, descriptions, rating displays, status indicators, buttons'));
276
+ console.log(chalk.yellow(' Functions: data transforms, calculations, formatting, validation,'));
277
+ console.log(chalk.yellow(' API response shaping, any logic that is not directly about rendering'));
278
+ console.log(chalk.yellow(' Hooks: data fetching, state management, side effects'));
279
+ console.log();
280
+ checkbox('Write a numbered extraction plan listing EVERYTHING you will extract');
281
+ console.log(chalk.yellow(' The end state: every page file is ONLY imports + component composition.'));
282
+ console.log(chalk.yellow(' No raw <div>, <span>, <h1>, <p>, <img>, or <ul> in page files.'));
283
+ console.log(chalk.yellow(' Every visual section in every component is itself a component.'));
284
+ console.log();
285
+ console.log(chalk.yellow(' For each item in the plan, note:'));
286
+ console.log(chalk.yellow(' — What it is (component, function, hook)'));
287
+ console.log(chalk.yellow(' — Where it currently lives (source file + approximate lines)'));
288
+ console.log(chalk.yellow(' — Where it will go (new file path)'));
289
+ console.log();
290
+ console.log(chalk.dim('Present the numbered plan, then proceed to step 5 to execute it.'));
291
+ }
292
+ /**
293
+ * Shared component capture instructions used by editor Step 7 and migration Steps 3/8.
294
+ * Prints the full isolation route setup, codeyam-capture wrapper, register command, and error checking.
295
+ */
296
+ function printComponentCaptureInstructions() {
297
+ checkbox('Create isolation route dirs: `codeyam editor isolate ComponentA ComponentB ...`');
298
+ console.log(chalk.dim(' This creates app/codeyam-isolate/layout.tsx (with production notFound() guard) and'));
299
+ console.log(chalk.dim(' a directory per component. List ALL components that need isolation routes.'));
300
+ checkbox('For each visual component:');
301
+ console.log(chalk.dim(' 1. Read the source AND find where it is used in the app to understand:'));
302
+ console.log(chalk.dim(' — Props/interface'));
303
+ console.log(chalk.dim(' — Container width in the real app (e.g. card in a 3-col grid → max-w-sm)'));
304
+ console.log(chalk.dim(' 2. Plan multiple scenarios that exercise the component like tests:'));
305
+ console.log(chalk.dim(' — Default/happy path with typical data'));
306
+ console.log(chalk.dim(' — Edge cases: empty/missing data, long text, maximum items, zero counts'));
307
+ console.log(chalk.dim(' — Different visual states: loading, error, disabled, selected, hover'));
308
+ console.log(chalk.dim(' — Boundary values: single item vs many, min/max ratings, very long names'));
309
+ console.log(chalk.dim(' 3. Create ONE isolation route per component with a scenarios map and ?s= query param:'));
310
+ console.log(chalk.dim(' Remix: app/routes/codeyam-isolate.ComponentName.tsx → /codeyam-isolate/ComponentName'));
311
+ console.log(chalk.dim(' Next.js: app/codeyam-isolate/ComponentName/page.tsx → /codeyam-isolate/ComponentName'));
312
+ console.log(chalk.dim(' The route defines a `scenarios` object mapping scenario names to props,'));
313
+ console.log(chalk.dim(' reads `?s=ScenarioName` from the URL, and renders the component with those props.'));
314
+ console.log(chalk.dim(' Wrap the component in a capture container with id="codeyam-capture":'));
315
+ console.log(chalk.dim(' <div id="codeyam-capture" style={{ display:"inline-block", padding:"20px" }}>'));
316
+ console.log(chalk.dim(' <div style={{ width:"100%", maxWidth:"..." }}> ← match the app\'s container width'));
317
+ console.log(chalk.dim(' e.g. card in a 3-col grid → maxWidth:"24rem", full-width component → omit maxWidth'));
318
+ console.log(chalk.dim(' The screenshot captures just this wrapper, so the component fills the image.'));
319
+ console.log(chalk.dim(' Center the wrapper on the page (flexbox center both axes) and set a page background'));
320
+ console.log(chalk.dim(' color that matches where the component normally appears (e.g. white for light UIs).'));
321
+ console.log(chalk.dim(' 4. Wait 2 seconds for HMR, then register + capture each scenario:'));
322
+ console.log(chalk.dim(` codeyam editor register '{"name":"ComponentName - Scenario",`));
323
+ console.log(chalk.dim(` "componentName":"ComponentName","componentPath":"path/to/file.tsx",`));
324
+ console.log(chalk.dim(` "url":"/codeyam-isolate/ComponentName?s=Scenario",`));
325
+ console.log(chalk.dim(` "mockData":{"routes":{"/api/...":{"body":[...]}}}}'`));
326
+ console.log(chalk.dim(' url is a PATH (starts with /) — the proxy routes it and intercepts API calls'));
327
+ console.log(chalk.dim(' mockData.routes provides data for API calls the component makes internally'));
328
+ console.log(chalk.dim(' (omit mockData if the component has no internal API calls)'));
329
+ console.log(chalk.yellow(' 5. IMPORTANT: Check the register response for `clientErrors` array'));
330
+ console.log(chalk.yellow(' If clientErrors is non-empty → fix the isolation route and re-register'));
331
+ console.log(chalk.yellow(' Fix client errors and re-register before moving on'));
332
+ console.log(chalk.dim(' Isolation routes are committed to git — the layout guard blocks them in production'));
333
+ }
122
334
  /**
123
335
  * Print a section header.
124
336
  */
@@ -131,13 +343,13 @@ function stepHeader(step, title, feature) {
131
343
  console.log();
132
344
  }
133
345
  /**
134
- * Print a colored progress tracker showing all 13 steps.
346
+ * Print a colored progress tracker showing all 16 steps.
135
347
  * Steps before `current` are green ✓, `current` is bold cyan →, future steps are dim ○.
136
348
  */
137
349
  function printProgressTracker(current) {
138
350
  console.log();
139
- console.log(chalk.dim('┌─────────────────────────────────────┐'));
140
- for (let i = 1; i <= 13; i++) {
351
+ console.log(chalk.dim(' ┌─────────────────────────────────────┐'));
352
+ for (let i = 1; i <= 16; i++) {
141
353
  const label = STEP_LABELS[i];
142
354
  const num = i < 10 ? ` ${i}` : `${i}`;
143
355
  const content = `${num}. ${label.padEnd(28)}`;
@@ -175,7 +387,7 @@ function stopGate(current, opts) {
175
387
  console.log(chalk.yellow('For the CURRENT step (→), show each checklist item with ✓ (done) or ✗ (skipped + reason).'));
176
388
  console.log(chalk.yellow('If any items are ✗, explain why and ask if the user wants to address them.'));
177
389
  console.log();
178
- if (current < 13) {
390
+ if (current < 16) {
179
391
  console.log(chalk.green('When done, run: ') +
180
392
  chalk.bold(`codeyam editor ${current + 1}`));
181
393
  }
@@ -219,6 +431,14 @@ function printResumptionHeader(step) {
219
431
  ],
220
432
  12: ['Re-verify is safe to repeat — just re-run the checks'],
221
433
  13: ['Check if commit already made:\n `git log --oneline -3`'],
434
+ 14: ['Check if commit already made:\n `git log --oneline -3`'],
435
+ 15: [
436
+ 'Check if journal was already updated with commit SHA:\n `codeyam editor journal-list`',
437
+ 'Check if commit was already amended:\n `git log --oneline -3`',
438
+ ],
439
+ 16: [
440
+ 'Check if already pushed:\n `git remote -v && git log --oneline origin/HEAD..HEAD 2>/dev/null`',
441
+ ],
222
442
  };
223
443
  const label = STEP_LABELS[step] || 'Unknown';
224
444
  console.log(chalk.bold.yellow(`━━━ RESUMING Step ${step}: ${label} ━━━`));
@@ -323,7 +543,7 @@ function parseDebugTargets(target) {
323
543
  }
324
544
  if (entry.startsWith('step-')) {
325
545
  const num = parseInt(entry.replace('step-', ''), 10);
326
- if (!isNaN(num) && num >= 1 && num <= 13) {
546
+ if (!isNaN(num) && num >= 1 && num <= 16) {
327
547
  valid.add(`step-${num}`);
328
548
  continue;
329
549
  }
@@ -407,6 +627,15 @@ function printSetup(root) {
407
627
  console.log();
408
628
  console.log("No project detected. Let's get started.");
409
629
  console.log();
630
+ // ── Design System ────────────────────────────────────────────────
631
+ console.log(chalk.bold('Design System (ask FIRST):'));
632
+ console.log(chalk.dim(' Ask: "Do you have a design system, brand guidelines, or style preferences you\'d like me to follow?"'));
633
+ console.log(chalk.dim(' Use AskUserQuestion with these EXACT option labels:'));
634
+ console.log(chalk.yellow(' Option 1 label: "Yes, I\'ll paste my design system"'));
635
+ console.log(chalk.dim(' → Wait for paste, save to .codeyam/design-system.md, confirm with brief summary'));
636
+ console.log(chalk.yellow(' Option 2 label: "No, use sensible defaults"'));
637
+ console.log(chalk.dim(' → Skip'));
638
+ console.log();
410
639
  console.log(chalk.bold('Checklist:'));
411
640
  checkbox('Read `.codeyam/editor-mode-context.md` for session state');
412
641
  checkbox('Ask the user what they want to build');
@@ -459,9 +688,17 @@ function printSetup(root) {
459
688
  console.log(chalk.yellow(' ( ) Mobile') + chalk.dim(' — 375 × 667'));
460
689
  console.log(chalk.yellow(' ( ) Custom') + chalk.dim(' — ask for width × height'));
461
690
  console.log();
462
- console.log(chalk.dim(' Pre-select based on app format: mobile-app/mobile-responsive-web-app → Mobile, chrome-extension → Custom (400×600), web-app/desktop-app → Desktop.'));
691
+ console.log(chalk.dim(' Pre-select based on app format: mobile-responsive-web-app/desktop-appDesktop, mobile-app → Mobile, chrome-extension → Custom (400×600).'));
463
692
  console.log(chalk.dim(' If only one obvious choice, confirm it rather than asking.'));
464
- console.log(chalk.dim(' Save the choice via: curl -s -X POST http://localhost:PORT/api/editor-project-info -H "Content-Type: application/json" -d \'{"defaultScreenSize":{"name":"Desktop","width":1440,"height":900}}\''));
693
+ console.log(chalk.dim(` Save the choice via: curl -s -X POST http://localhost:${port}/api/editor-project-info -H "Content-Type: application/json" -d '{"defaultScreenSize":{"name":"Desktop","width":1440,"height":900}}'`));
694
+ console.log();
695
+ console.log(chalk.bold('Named Screen Sizes (for multi-resolution apps):'));
696
+ console.log(chalk.dim(' For mobile-responsive web apps or apps that need screenshots at multiple resolutions,'));
697
+ console.log(chalk.dim(' also save named screen sizes. Each scenario can reference a dimension name.'));
698
+ console.log(chalk.dim(` curl -s -X POST http://localhost:${port}/api/editor-project-info -H "Content-Type: application/json" \\`));
699
+ console.log(chalk.dim(' -d \'{"screenSizes":{"Desktop":{"width":1440,"height":900},"Mobile":{"width":375,"height":667}}}\''));
700
+ console.log(chalk.dim(' If you discover a new viewport is needed mid-workflow, add it to screenSizes the same way and reference by name.'));
701
+ console.log(chalk.dim(' NOTE: This REPLACES all screenSizes — always include every named size, not just the new one.'));
465
702
  console.log();
466
703
  console.log(chalk.bold.red('━━━ STOP ━━━'));
467
704
  console.log();
@@ -470,7 +707,7 @@ function printSetup(root) {
470
707
  console.log(chalk.green(' ') +
471
708
  chalk.bold('codeyam editor 1 --feature "Feature Name" --app-formats "FORMAT_IDS" --tech-stack "STACK_ID" --prompt "the user\'s original message"'));
472
709
  console.log(chalk.dim(' Replace "Feature Name" with a short title for what the user described.'));
473
- console.log(chalk.dim(' Replace FORMAT_IDS with the comma-separated format IDs the user selected (e.g., "chrome-extension" or "web-app,mobile-responsive-web-app").'));
710
+ console.log(chalk.dim(' Replace FORMAT_IDS with the comma-separated format IDs the user selected (e.g., "chrome-extension" or "mobile-responsive-web-app").'));
474
711
  console.log(chalk.dim(' Replace STACK_ID with the tech stack id shown in [brackets] above (e.g., "nextjs-prisma-sqlite" or "chrome-extension-react").'));
475
712
  console.log(chalk.dim(" Pass --prompt with the user's exact original request so it appears in the journal."));
476
713
  console.log(chalk.dim(' Step 1 will guide you through planning and getting user confirmation before any code is written.'));
@@ -479,6 +716,42 @@ function printSetup(root) {
479
716
  // ─── Cycle overview (no args, has project) ────────────────────────────
480
717
  function printCycleOverview(root, state) {
481
718
  logEvent(root, 'overview', state ? { feature: state.feature, step: state.step } : {});
719
+ // Detect active migration (uses both editor-step.json and migration-state.json)
720
+ const migState = readMigrationState(root);
721
+ const resumeInfo = getMigrationResumeInfo(state
722
+ ? { step: state.step, label: state.label, migration: state.migration }
723
+ : null, migState);
724
+ if (resumeInfo.mode === 'survey') {
725
+ console.log();
726
+ console.log(chalk.bold.cyan('━━━ Editor Mode: Project Migration ━━━'));
727
+ console.log();
728
+ console.log(chalk.yellow('Migration survey in progress — Claude needs to explore the project.'));
729
+ console.log();
730
+ console.log(chalk.green('Continue with: ') + chalk.bold(resumeInfo.resumeCommand));
731
+ console.log();
732
+ return;
733
+ }
734
+ if (resumeInfo.mode === 'page-in-progress') {
735
+ console.log();
736
+ console.log(chalk.bold.cyan('━━━ Editor Mode: Project Migration ━━━'));
737
+ console.log();
738
+ console.log(chalk.yellow(`Migration: Page ${resumeInfo.pageIndex + 1}/${resumeInfo.totalPages} (${resumeInfo.pageName})` +
739
+ (resumeInfo.step
740
+ ? `, Step ${resumeInfo.step} (${resumeInfo.stepLabel})`
741
+ : '')));
742
+ console.log();
743
+ console.log(chalk.green('Continue with: ') + chalk.bold(resumeInfo.resumeCommand));
744
+ console.log();
745
+ return;
746
+ }
747
+ if (resumeInfo.mode === 'complete') {
748
+ console.log();
749
+ console.log(chalk.bold.cyan('━━━ Editor Mode: Feature Cycle ━━━'));
750
+ console.log();
751
+ console.log(chalk.green('Migration complete! Start building features:'));
752
+ console.log();
753
+ // Fall through to normal cycle display
754
+ }
482
755
  console.log();
483
756
  console.log(chalk.bold.cyan('━━━ Editor Mode: Feature Cycle ━━━'));
484
757
  console.log();
@@ -496,7 +769,7 @@ function printCycleOverview(root, state) {
496
769
  console.log(chalk.yellow('This applies even after committing — always use the change workflow.'));
497
770
  }
498
771
  else {
499
- console.log('Each feature follows 13 steps. You MUST run each command in order:');
772
+ console.log('Each feature follows 16 steps. You MUST run each command in order:');
500
773
  console.log();
501
774
  console.log(` ${chalk.bold.yellow(' 1')} ${chalk.bold('Plan')} — Plan the feature, confirm with user`);
502
775
  console.log(` ${chalk.bold.yellow(' 2')} ${chalk.bold('Prototype')} — Build a working prototype fast`);
@@ -511,6 +784,9 @@ function printCycleOverview(root, state) {
511
784
  console.log(` ${chalk.bold.yellow('11')} ${chalk.bold('Journal')} — Create/update journal entry`);
512
785
  console.log(` ${chalk.bold.yellow('12')} ${chalk.bold('Review')} — Verify screenshots and audit`);
513
786
  console.log(` ${chalk.bold.yellow('13')} ${chalk.bold('Present')} — Present summary, get approval`);
787
+ console.log(` ${chalk.bold.yellow('14')} ${chalk.bold('Commit')} — Commit all changes`);
788
+ console.log(` ${chalk.bold.yellow('15')} ${chalk.bold('Finalize')} — Journal update + amend commit`);
789
+ console.log(` ${chalk.bold.yellow('16')} ${chalk.bold('Push')} — Push to remote`);
514
790
  console.log();
515
791
  console.log(chalk.green('Start now: ') + chalk.bold('codeyam editor 1'));
516
792
  console.log(chalk.dim(' If the user already described what they want, pass it: codeyam editor 1 --prompt "their message"'));
@@ -525,19 +801,19 @@ function printStep1(root, feature, options, userPrompt) {
525
801
  if (!isResuming) {
526
802
  clearState(root);
527
803
  }
528
- // If feature is provided, save initial state so step 2 can pick it up
529
- if (feature) {
530
- const now = new Date().toISOString();
531
- writeState(root, {
532
- feature,
533
- step: 1,
534
- label: STEP_LABELS[1],
535
- startedAt: isResuming ? prevState.startedAt : now,
536
- featureStartedAt: isResuming ? prevState.featureStartedAt : now,
537
- appFormats: options?.appFormats || prevState?.appFormats,
538
- techStackId: options?.techStackId || prevState?.techStackId,
539
- });
540
- }
804
+ // Always persist state so step 2's validation sees that step 1 ran.
805
+ // The feature name may not be known yet (it's an output of planning)
806
+ // use an empty string as placeholder; step 2 requires --feature anyway.
807
+ const now = new Date().toISOString();
808
+ writeState(root, {
809
+ feature: feature || prevState?.feature || '',
810
+ step: 1,
811
+ label: STEP_LABELS[1],
812
+ startedAt: isResuming ? prevState.startedAt : now,
813
+ featureStartedAt: isResuming ? prevState.featureStartedAt : now,
814
+ appFormats: options?.appFormats || prevState?.appFormats,
815
+ techStackId: options?.techStackId || prevState?.techStackId,
816
+ });
541
817
  // Save the user's original prompt to a separate file
542
818
  if (userPrompt) {
543
819
  const promptPath = path.join(root, '.codeyam', 'editor-user-prompt.txt');
@@ -562,11 +838,21 @@ function printStep1(root, feature, options, userPrompt) {
562
838
  console.log(chalk.dim(' Bad: Free-form text asking "What do you think about the data model?"'));
563
839
  console.log(chalk.dim(' You can ask up to 4 questions at once. Bundle related questions into a single AskUserQuestion call.'));
564
840
  checkbox("Summarize what you'll build in plain language the user can verify against their vision");
841
+ checkbox('Include a brief note on what interesting data states the scenarios should demonstrate');
842
+ console.log(chalk.dim(' Think: what seed data would put this feature through its paces? Diverse content, edge cases, empty vs rich.'));
565
843
  checkbox('Set the project title and description for the App tab:');
566
844
  console.log(chalk.dim(` curl -s -X POST http://localhost:${port}/api/editor-project-info \\`));
567
845
  console.log(chalk.dim(' -H "Content-Type: application/json" \\'));
568
846
  console.log(chalk.dim(' -d \'{"projectTitle":"My App","projectDescription":"A short description of what this app does"}\''));
569
847
  console.log();
848
+ const designSystem = readDesignSystem(root);
849
+ if (designSystem) {
850
+ console.log(chalk.bold.magenta('Design System (from .codeyam/design-system.md):'));
851
+ console.log(chalk.dim(' Keep these design tokens in mind during planning.'));
852
+ console.log();
853
+ console.log(designSystem);
854
+ console.log();
855
+ }
570
856
  console.log(chalk.bold('Present a selection menu to the user (use AskUserQuestion with these EXACT option labels):'));
571
857
  console.log(chalk.green(' Option 1 label: "This plan is accurate, let\'s prototype it!"') + chalk.dim(' — proceed to step 2'));
572
858
  console.log(chalk.yellow(' Option 2 label: "I\'d like some changes"') +
@@ -594,6 +880,7 @@ function printStep1(root, feature, options, userPrompt) {
594
880
  // ─── Step 2: Prototype ────────────────────────────────────────────────
595
881
  function printStep2(root, feature) {
596
882
  const port = getServerPort();
883
+ const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
597
884
  const projectExists = hasProject(root);
598
885
  const prevState = readState(root);
599
886
  const isResuming = prevState?.step === 2;
@@ -631,7 +918,13 @@ function printStep2(root, feature) {
631
918
  console.log(chalk.dim(` # After re-seeding, restart the dev server to pick up fresh data:`));
632
919
  console.log(chalk.dim(` # codeyam editor dev-server '{"action":"restart"}'`));
633
920
  console.log();
634
- console.log(chalk.dim(' See PRISMA_SETUP.md for Prisma patterns and important warnings.'));
921
+ console.log(chalk.yellow(' IMPORTANT: When adding new required columns to existing tables,'));
922
+ console.log(chalk.yellow(' provide a @default(...) value so `db push` can fill existing rows.'));
923
+ console.log(chalk.dim(' Example: userId String @default("anonymous") — existing rows get "anonymous"'));
924
+ console.log(chalk.dim(' Without a default, Prisma requires --force-reset which drops ALL data.'));
925
+ console.log(chalk.dim(' NEVER use --force-reset — it is blocked in this environment.'));
926
+ console.log();
927
+ console.log(chalk.dim(' See DATABASE.md for Prisma patterns and important warnings.'));
635
928
  console.log(chalk.dim(' Key: import { prisma } from "@/app/lib/prisma" in API routes.'));
636
929
  console.log(chalk.dim(' Key: Seed scripts must use the adapter pattern (see prisma/seed.ts).'));
637
930
  console.log();
@@ -646,14 +939,42 @@ function printStep2(root, feature) {
646
939
  console.log(chalk.dim(' Run with: npx tsx .codeyam/seed-adapter.ts <path-to-seed-data.json>'));
647
940
  checkbox('Verify the dev server shows the changes');
648
941
  checkbox('If the feature involves auth, email, payments, or other common patterns: read FEATURE_PATTERNS.md');
942
+ // Responsive design guidance when building a mobile-responsive web app
943
+ if (prevState?.appFormats?.includes('mobile-responsive-web-app')) {
944
+ console.log();
945
+ console.log(chalk.bold.magenta('Responsive Design (mobile-responsive-web-app):'));
946
+ console.log(chalk.magenta(' This app must look great on BOTH desktop AND mobile. It is NOT a mobile-only app.'));
947
+ console.log(chalk.magenta(' Design desktop-first with a full-width layout, then ensure it adapts gracefully to mobile.'));
948
+ console.log(chalk.dim(' Use Tailwind responsive prefixes (sm:, md:, lg:) for layout shifts.'));
949
+ console.log(chalk.dim(' Test at both Desktop (1280×800) and Mobile (390×844) sizes before presenting.'));
950
+ }
951
+ const designSystem = readDesignSystem(root);
952
+ if (designSystem) {
953
+ console.log();
954
+ console.log(chalk.bold.magenta('Design System (from .codeyam/design-system.md):'));
955
+ console.log();
956
+ console.log(designSystem);
957
+ console.log();
958
+ checkbox('Define ALL design tokens as CSS custom properties in globals.css — not just colors');
959
+ console.log(chalk.dim(' Colors: --bg-surface, --text-primary, --accent-green-a, etc.'));
960
+ console.log(chalk.dim(' Typography: --text-xs, --text-sm, --text-lg, --text-2xl (font-size values)'));
961
+ console.log(chalk.dim(' Font weights: --font-weight-normal, --font-weight-medium, --font-weight-semibold'));
962
+ console.log(chalk.dim(' Spacing: --spacing-xs, --spacing-sm, --spacing-md, --spacing-lg, etc.'));
963
+ console.log(chalk.dim(' Border radius, shadows, transitions — every value the design system defines.'));
964
+ checkbox('Reference tokens from components — ZERO hardcoded px values for font-size, spacing, or colors');
965
+ console.log(chalk.dim(' Bad: fontSize: 14, padding: "12px 16px", gap: 8'));
966
+ console.log(chalk.dim(' Good: fontSize: "var(--text-sm)", padding: "var(--spacing-md) var(--spacing-lg)", gap: "var(--spacing-sm)"'));
967
+ console.log(chalk.dim(' This ensures the entire app updates when the design system changes.'));
968
+ }
649
969
  console.log();
650
970
  console.log(chalk.bold.cyan('Keep the preview moving:'));
651
971
  console.log(chalk.cyan(' The user is watching the preview. Refresh it after each meaningful change:'));
652
- console.log(chalk.cyan(` codeyam editor preview`));
972
+ console.log(chalk.cyan(` codeyam editor preview '{"dimension":"${dim}"}'`));
653
973
  console.log(chalk.cyan(' Refresh after: first visible page, adding each UI section, seeding data, styling.'));
654
974
  console.log(chalk.cyan(' Aim for 4-8+ refreshes during prototyping — not one big reveal at the end.'));
655
975
  console.log(chalk.cyan(' If you build a NEW page (e.g., /drinks/[id]), navigate the preview there:'));
656
- console.log(chalk.cyan(` codeyam editor preview '{"path":"/drinks/1"}'`));
976
+ console.log(chalk.cyan(` codeyam editor preview '{"path":"/drinks/1","dimension":"${dim}"}'`));
977
+ printDimensionGuidance(dim, dimNames);
657
978
  console.log();
658
979
  console.log(chalk.bold('Verify the dev server:'));
659
980
  console.log(chalk.dim(` # Get dev server URL: codeyam editor dev-server`));
@@ -669,14 +990,24 @@ function printStep2(root, feature) {
669
990
  console.log(chalk.dim(' Client-rendered pages need imageUrls — the HTML shell has no images to scan.'));
670
991
  console.log(chalk.dim(' Fix or replace any broken image URLs in the seed data before proceeding.'));
671
992
  checkbox('Check the dev server terminal output for runtime errors (missing modules, failed imports)');
993
+ checkbox('Verify the live preview renders: `codeyam editor client-errors`');
994
+ console.log(chalk.dim(' If `hasContent=false` or `liveErrors>0`, the preview is broken — fix the issue before proceeding.'));
995
+ console.log(chalk.dim(' The user is looking at the preview — a blank page means something is broken.'));
672
996
  console.log(chalk.dim(' If any check fails, fix the issue and re-verify before proceeding.'));
673
997
  console.log();
998
+ console.log(chalk.bold('Update README and setup script:'));
999
+ checkbox('Update `README.md`: set the project name, write a one-line description, and list any setup prerequisites');
1000
+ checkbox('Update `npm run setup` in `package.json` if setup requires extra steps (e.g. env vars, external services)');
1001
+ console.log(chalk.dim(' The README and setup script must stay accurate as you make changes.'));
1002
+ console.log(chalk.dim(' A new clone should work with just: git clone → npm run setup → npm run dev'));
1003
+ console.log();
674
1004
  console.log(chalk.dim('Focus on building the prototype. Scenarios and refactoring happen in later steps.'));
675
1005
  stopGate(2);
676
1006
  }
677
1007
  // ─── Step 3: Confirm ──────────────────────────────────────────────────
678
1008
  function printStep3(root, feature) {
679
1009
  const port = getServerPort();
1010
+ const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
680
1011
  const prevState = readState(root);
681
1012
  const isResuming = prevState?.step === 3;
682
1013
  const now = new Date().toISOString();
@@ -695,7 +1026,7 @@ function printStep3(root, feature) {
695
1026
  console.log('Summarize what was built and get user confirmation.');
696
1027
  console.log();
697
1028
  console.log(chalk.bold('Before presenting — verify everything works:'));
698
- checkbox(`Refresh the preview: \`codeyam editor preview\` — check the \`preview\` field for \`healthy: false\``);
1029
+ checkbox(`Refresh the preview: \`codeyam editor preview '{"dimension":"${dim}"}'\` — check the \`preview\` field for \`healthy: false\``);
699
1030
  checkbox('Verify API routes return valid data (curl each route)');
700
1031
  console.log();
701
1032
  console.log(chalk.bold.red(' Verify EVERY image loads (this is the #1 source of broken prototypes):'));
@@ -705,10 +1036,12 @@ function printStep3(root, feature) {
705
1036
  checkbox('Fix or remove any image that returns non-200 before continuing');
706
1037
  checkbox('Check for client-side errors: `codeyam editor client-errors`');
707
1038
  console.log(chalk.dim(' If there are errors, fix the underlying issue before presenting.'));
1039
+ checkbox('Verify `hasContent=true` and `liveErrors=0` — do NOT ask the user to confirm if the preview is broken');
708
1040
  console.log();
709
1041
  console.log(chalk.bold('Then present to the user:'));
710
1042
  checkbox('Summarize what was built (routes, components, data)');
711
- checkbox('Navigate the preview to the feature\'s primary page: `codeyam editor preview \'{"path":"/your-page"}\'`');
1043
+ checkbox(`Navigate the preview to the feature's primary page: \`codeyam editor preview '{"path":"/your-page","dimension":"${dim}"}'\``);
1044
+ printDimensionGuidance(dim, dimNames);
712
1045
  console.log(chalk.dim(' The user needs to SEE the new feature. If you built a new page, navigate there.'));
713
1046
  console.log(chalk.dim(' If you modified an existing page, refresh the preview to show the changes.'));
714
1047
  console.log();
@@ -720,7 +1053,7 @@ function printStep3(root, feature) {
720
1053
  console.log(chalk.bold('Present a selection menu to the user (use AskUserQuestion with these EXACT option labels):'));
721
1054
  console.log(chalk.green(' Option 1 label: "The live preview is displaying what I expected"') + chalk.dim(' — proceed to step 4'));
722
1055
  console.log(chalk.yellow(' Option 2 label: "I\'d like some changes"') +
723
- chalk.dim(' — user describes changes, you make them, then re-present'));
1056
+ chalk.dim(' — make changes, refresh preview, re-run `codeyam editor 3`'));
724
1057
  console.log();
725
1058
  console.log(chalk.dim('Wait for user approval before moving on. Refactoring and scenarios happen in later steps.'));
726
1059
  stopGate(3, { confirm: true });
@@ -745,39 +1078,13 @@ function printStep4(root, feature) {
745
1078
  console.log(chalk.bold('Goal: pages contain ONLY components. Components contain ONLY sub-components.'));
746
1079
  console.log(chalk.yellow('This step is read and plan only. Code extraction happens in step 5.'));
747
1080
  console.log();
748
- console.log(chalk.bold.red('THE RULE: No direct JSX in page files.'));
749
- console.log(chalk.yellow(' After extraction, a page/route file should import and compose components — nothing else.'));
750
- console.log(chalk.yellow(' Every distinct visual section in the page is its own component.'));
751
- console.log(chalk.yellow(' Every component that renders multiple distinct sections should be split into sub-components.'));
752
- console.log(chalk.yellow(' If a component has N visually distinct parts, it should compose N sub-components.'));
753
- console.log();
754
- console.log(chalk.bold('Checklist:'));
755
- checkbox('Read `.codeyam/glossary.json` — note reusable functions/components');
756
- checkbox('Read EVERY file created or modified in this session');
757
- console.log(chalk.yellow(' For EACH file, identify EVERY extractable piece:'));
758
- console.log(chalk.yellow(' Components: headers, nav, loading states, empty states, error states,'));
759
- console.log(chalk.yellow(' badges/pills, image containers, card sections, form fields, modals,'));
760
- console.log(chalk.yellow(' titles, descriptions, rating displays, status indicators, buttons'));
761
- console.log(chalk.yellow(' Functions: data transforms, calculations, formatting, validation,'));
762
- console.log(chalk.yellow(' API response shaping, any logic that is not directly about rendering'));
763
- console.log(chalk.yellow(' Hooks: data fetching, state management, side effects'));
764
- console.log();
765
- checkbox('Write a numbered extraction plan listing EVERYTHING you will extract');
766
- console.log(chalk.yellow(' The end state: every page file is ONLY imports + component composition.'));
767
- console.log(chalk.yellow(' No raw <div>, <span>, <h1>, <p>, <img>, or <ul> in page files.'));
768
- console.log(chalk.yellow(' Every visual section in every component is itself a component.'));
769
- console.log();
770
- console.log(chalk.yellow(' For each item in the plan, note:'));
771
- console.log(chalk.yellow(' — What it is (component, function, hook)'));
772
- console.log(chalk.yellow(' — Where it currently lives (source file + approximate lines)'));
773
- console.log(chalk.yellow(' — Where it will go (new file path)'));
774
- console.log();
775
- console.log(chalk.dim('Present the numbered plan, then proceed to step 5 to execute it.'));
1081
+ printExtractionPlanInstructions();
776
1082
  stopGate(4);
777
1083
  }
778
1084
  // ─── Step 5: Extract ──────────────────────────────────────────────────
779
1085
  function printStep5(root, feature) {
780
1086
  const port = getServerPort();
1087
+ const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
781
1088
  const prevState = readState(root);
782
1089
  const isResuming = prevState?.step === 5;
783
1090
  const now = new Date().toISOString();
@@ -801,12 +1108,13 @@ function printStep5(root, feature) {
801
1108
  checkbox('Every component that renders multiple sections must be split into sub-components');
802
1109
  console.log(chalk.dim(' No tests needed — visual verification happens in step 7'));
803
1110
  console.log();
804
- console.log(chalk.bold('Library functions (TDD):'));
805
- checkbox('For each function: write MULTIPLE failing tests FIRST, then extract to make them pass');
1111
+ console.log(chalk.bold('Library functions AND hooks (TDD):'));
1112
+ checkbox('For each function/hook: write MULTIPLE failing tests FIRST, then extract to make them pass');
806
1113
  console.log(chalk.dim(' Cover: typical inputs, edge cases, empty/null inputs, error conditions'));
807
1114
  console.log(chalk.dim(' Aim for 3-8 test cases per function depending on complexity'));
1115
+ console.log(chalk.dim(' Hooks count as functions — useDrinks, useAuth, etc. all need test files'));
808
1116
  checkbox('Place test files next to source: `app/lib/drinks.ts` → `app/lib/drinks.test.ts`');
809
- console.log(chalk.yellow(' Tests ARE the only coverage for library functions — step 7 only captures component screenshots.'));
1117
+ console.log(chalk.yellow(' Tests ARE the only coverage for library functions/hooks — step 7 only captures component screenshots.'));
810
1118
  console.log();
811
1119
  console.log(chalk.bold('Recursive pass:'));
812
1120
  checkbox('Re-read EVERY new file you just created — extract components from components, functions from functions');
@@ -818,7 +1126,8 @@ function printStep5(root, feature) {
818
1126
  checkbox('Run all tests and verify they pass');
819
1127
  checkbox('Page files contain ONLY imports + component composition — no raw HTML tags');
820
1128
  checkbox('Every component renders ONE thing or composes sub-components — no multi-section JSX');
821
- checkbox(`Refresh the preview after each batch of extractions: \`codeyam editor preview\``);
1129
+ checkbox(`Refresh the preview after each batch of extractions: \`codeyam editor preview '{"dimension":"${dim}"}'\``);
1130
+ printDimensionGuidance(dim, dimNames);
822
1131
  console.log(chalk.dim(' The user should see the preview stay healthy as you refactor — refresh periodically, not just at the end.'));
823
1132
  console.log(chalk.dim('Reuse glossary functions when they fit naturally. Extract a new function when the use case diverges.'));
824
1133
  console.log();
@@ -844,19 +1153,7 @@ function printStep6(root, feature) {
844
1153
  }
845
1154
  console.log('Record all new functions/components in `.codeyam/glossary.json`.');
846
1155
  console.log();
847
- console.log(chalk.bold('Checklist:'));
848
- checkbox("Read `.codeyam/glossary.json` (create if it doesn't exist)");
849
- checkbox('Add an entry for each new function/component extracted in step 5');
850
- checkbox('Each entry should have: name, filePath, description, parameters, returnType, tags, feature');
851
- checkbox('For each function with a test file from step 5, set `testFile` to the relative path');
852
- console.log();
853
- console.log(chalk.bold('Entry format:'));
854
- console.log(chalk.dim(' { "name": "calculateTotal", "filePath": "app/utils/pricing.ts",'));
855
- console.log(chalk.dim(' "description": "Calculates total price including tax and discounts",'));
856
- console.log(chalk.dim(' "parameters": [{ "name": "items", "type": "CartItem[]" }],'));
857
- console.log(chalk.dim(' "returnType": "number", "tags": ["pricing"],'));
858
- console.log(chalk.dim(' "testFile": "app/utils/pricing.test.ts",'));
859
- console.log(chalk.dim(` "feature": "${feature}", "createdAt": "..." }`));
1156
+ printGlossaryInstructions(feature);
860
1157
  console.log();
861
1158
  console.log(chalk.dim('Focus on updating the glossary. Application code and scenarios come in later steps.'));
862
1159
  stopGate(6);
@@ -864,6 +1161,7 @@ function printStep6(root, feature) {
864
1161
  // ─── Step 7: Analyze ──────────────────────────────────────────────────
865
1162
  function printStep7(root, feature) {
866
1163
  const port = getServerPort();
1164
+ const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
867
1165
  const prevState = readState(root);
868
1166
  const isResuming = prevState?.step === 7;
869
1167
  const now = new Date().toISOString();
@@ -883,48 +1181,11 @@ function printStep7(root, feature) {
883
1181
  console.log();
884
1182
  console.log(chalk.bold('Visual Components — Component Isolation:'));
885
1183
  checkbox('List all files with new/modified visual components from step 5');
886
- checkbox('Create isolation route dirs: `codeyam editor isolate ComponentA ComponentB ...`');
887
- console.log(chalk.dim(' This creates app/isolated-components/layout.tsx (with production notFound() guard) and'));
888
- console.log(chalk.dim(' a directory per component. List ALL components that need isolation routes.'));
889
1184
  checkbox('Check existing scenarios: `codeyam editor scenarios`');
890
1185
  console.log(chalk.dim(' Reuse and improve existing scenarios where possible — update mock data'));
891
1186
  console.log(chalk.dim(' to reflect current changes. Add new scenarios only for genuinely new states.'));
892
1187
  console.log(chalk.dim(' Ensure at least one scenario clearly demonstrates what changed in this session.'));
893
- checkbox('For each visual component:');
894
- console.log(chalk.dim(' 1. Read the source AND find where it is used in the app to understand:'));
895
- console.log(chalk.dim(' — Props/interface'));
896
- console.log(chalk.dim(' — Container width in the real app (e.g. card in a 3-col grid → max-w-sm)'));
897
- console.log(chalk.dim(' 2. Plan multiple scenarios that exercise the component like tests:'));
898
- console.log(chalk.dim(' — Default/happy path with typical data'));
899
- console.log(chalk.dim(' — Edge cases: empty/missing data, long text, maximum items, zero counts'));
900
- console.log(chalk.dim(' — Different visual states: loading, error, disabled, selected, hover'));
901
- console.log(chalk.dim(' — Boundary values: single item vs many, min/max ratings, very long names'));
902
- console.log(chalk.dim(' 3. Create ONE isolation route per component with a scenarios map and ?s= query param:'));
903
- console.log(chalk.dim(' Remix: app/routes/isolated-components.ComponentName.tsx → /isolated-components/ComponentName'));
904
- console.log(chalk.dim(' Next.js: app/isolated-components/ComponentName/page.tsx → /isolated-components/ComponentName'));
905
- console.log(chalk.dim(' The route defines a `scenarios` object mapping scenario names to props,'));
906
- console.log(chalk.dim(' reads `?s=ScenarioName` from the URL, and renders the component with those props.'));
907
- console.log(chalk.dim(' Wrap the component in a capture container with id="codeyam-capture":'));
908
- console.log(chalk.dim(' <div id="codeyam-capture" style={{ display:"inline-block", padding:"20px" }}>'));
909
- console.log(chalk.dim(' <div style={{ width:"100%", maxWidth:"..." }}> ← match the app\'s container width'));
910
- console.log(chalk.dim(' e.g. card in a 3-col grid → maxWidth:"24rem", full-width component → omit maxWidth'));
911
- console.log(chalk.dim(' The screenshot captures just this wrapper, so the component fills the image.'));
912
- console.log(chalk.dim(' Center the wrapper on the page (flexbox center both axes) and set a page background'));
913
- console.log(chalk.dim(' color that matches where the component normally appears (e.g. white for light UIs).'));
914
- console.log(chalk.dim(' 4. Wait 2 seconds for HMR, then register + capture each scenario:'));
915
- console.log(chalk.dim(` codeyam editor register '{"name":"ComponentName - Scenario",`));
916
- console.log(chalk.dim(` "componentName":"ComponentName","componentPath":"path/to/file.tsx",`));
917
- console.log(chalk.dim(` "url":"/isolated-components/ComponentName?s=Scenario",`));
918
- console.log(chalk.dim(` "mockData":{"routes":{"/api/...":{"body":[...]}}}}'`));
919
- console.log(chalk.dim(' Optional: add "viewportWidth" and "viewportHeight" to override the project default screen size'));
920
- console.log(chalk.dim(' If omitted, captures use the project default from setup. Only override for scenarios that need a different size.'));
921
- console.log(chalk.dim(' url is a PATH (starts with /) — the proxy routes it and intercepts API calls'));
922
- console.log(chalk.dim(' mockData.routes provides data for API calls the component makes internally'));
923
- console.log(chalk.dim(' (omit mockData if the component has no internal API calls)'));
924
- console.log(chalk.yellow(' 5. IMPORTANT: Check the register response for `clientErrors` array'));
925
- console.log(chalk.yellow(' If clientErrors is non-empty → fix the isolation route and re-register'));
926
- console.log(chalk.yellow(' Fix client errors and re-register before moving on'));
927
- console.log(chalk.dim(' Isolation routes are committed to git — the layout guard blocks them in production'));
1188
+ printComponentCaptureInstructions();
928
1189
  console.log();
929
1190
  console.log(chalk.bold('Library Functions — run tests:'));
930
1191
  checkbox('Run ALL test files created in step 5');
@@ -934,17 +1195,16 @@ function printStep7(root, feature) {
934
1195
  console.log();
935
1196
  console.log(chalk.dim('Do not proceed until both component isolations and library tests pass.'));
936
1197
  console.log();
937
- checkbox('Run `codeyam editor audit` to verify all components have scenarios and all functions have tests');
1198
+ checkbox('Run `codeyam editor audit` to verify all components have scenarios and all functions/hooks have tests');
1199
+ console.log(chalk.red.bold(' The audit is a HARD GATE — step 8 will refuse to run until the audit passes.'));
1200
+ console.log(chalk.dim(' When audit passes, the import graph is built automatically for change tracking.'));
938
1201
  console.log();
939
- console.log(chalk.bold('Build import graph (for change tracking):'));
940
- checkbox('Run `codeyam editor analyze-imports`');
941
- console.log(chalk.dim(' This populates the import graph so the system can detect impacted entities when code changes.'));
942
- console.log(chalk.dim(' It must run AFTER the audit passes so the glossary and entity data are complete.'));
943
1202
  stopGate(7);
944
1203
  }
945
1204
  // ─── Step 8: App Scenarios ────────────────────────────────────────────
946
1205
  function printStep8(root, feature) {
947
1206
  const port = getServerPort();
1207
+ const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
948
1208
  const prevState = readState(root);
949
1209
  const isResuming = prevState?.step === 8;
950
1210
  const now = new Date().toISOString();
@@ -960,43 +1220,43 @@ function printStep8(root, feature) {
960
1220
  if (isResuming) {
961
1221
  printResumptionHeader(8);
962
1222
  }
963
- console.log('Create app-level scenarios representing different data states.');
1223
+ console.log('Create app-level scenarios with rich data that robustly demonstrates this feature.');
1224
+ console.log();
1225
+ console.log(chalk.bold('Goal: Every scenario should thoroughly exercise the feature with realistic data.'));
1226
+ console.log(chalk.dim(' Scenarios with minimal or generic data ("Test Item 1") won\'t reveal whether the feature works.'));
1227
+ console.log(chalk.dim(' Use rich, diverse seed data that puts the feature through its paces.'));
1228
+ console.log();
1229
+ console.log(chalk.bold.cyan('Make seed data work hard:'));
1230
+ console.log(chalk.cyan(' Every scenario — new or existing — should have data that EXERCISES the feature:'));
1231
+ console.log(chalk.cyan(' • Add data that uses new fields, relationships, or states the feature introduces'));
1232
+ console.log(chalk.cyan(' • Include realistic variety — different categories, lengths, statuses, counts'));
1233
+ console.log(chalk.cyan(" • Populate optional fields the feature depends on (don't leave them empty)"));
1234
+ console.log(chalk.cyan(' • Make content diverse enough that edge cases surface naturally'));
1235
+ console.log(chalk.cyan(" • Think: would this data reveal a bug in the feature? If it's too simple, enrich it."));
1236
+ console.log();
1237
+ console.log(chalk.bold.cyan('When to create new vs reuse existing:'));
1238
+ console.log(chalk.cyan(' • New pages or pages with no scenarios yet → create new scenarios'));
1239
+ console.log(chalk.cyan(' • Existing scenarios on affected pages → re-register with enhanced data'));
1240
+ console.log(chalk.cyan(" • New data states that can't coexist in one scenario (empty vs rich, error vs success) → new scenario"));
1241
+ console.log(chalk.cyan(" • Don't duplicate — if an existing scenario can cover a state with richer data, enhance it instead"));
964
1242
  console.log();
965
- console.log(chalk.bold('Checklist:'));
966
- checkbox('Check existing scenarios: `codeyam editor scenarios`');
967
- console.log(chalk.dim(' Review existing scenarios — reuse and update their data rather than'));
968
- console.log(chalk.dim(' creating duplicates. Re-register with the same name to update a scenario.'));
969
1243
  checkbox('Ensure scenarios clearly demonstrate what changed in this session');
970
1244
  console.log(chalk.dim(' If data models changed: update existing scenarios seed data to match'));
971
1245
  console.log(chalk.dim(' If UI changed: re-register existing scenarios so screenshots reflect the update'));
972
1246
  console.log(chalk.dim(' Add new scenarios only for genuinely new data states not covered by existing ones'));
973
- checkbox('Cover key data states for EVERY page/route (at least 2-3 scenarios per page)');
974
- console.log(chalk.yellow(' Each page needs its own scenarios — a detail page needs different data than the catalog'));
975
- console.log(chalk.dim(' Catalog: "Full Catalog", "Empty Catalog", "Single Category"'));
976
- console.log(chalk.dim(' Detail: "Detail - With Reviews", "Detail - No Reviews", "Detail - High Rated"'));
977
- console.log(chalk.dim(' Each scenario provides seed data to populate the database for that state'));
978
- checkbox('If the project has a database + seed adapter, use seed-based scenarios:');
979
- console.log(chalk.dim(` codeyam editor register '{"name":"Full Catalog","type":"application","url":"/","seed":{"products":[...],"categories":[...]}}'`));
980
- console.log(chalk.dim(' Seed data is written to the DB via the seed adapter. Real app renders real data.'));
981
- checkbox('Optional: add "viewportWidth" and "viewportHeight" to override the project default screen size');
982
- console.log(chalk.dim(' If omitted, captures use the project default from setup. Only override for scenarios that need a different size.'));
983
- checkbox('IMPORTANT: Always include "url" — the page path to screenshot for this scenario');
984
- console.log(chalk.dim(' Use "/" for home page, "/drinks/1" for detail pages, etc.'));
985
- console.log(chalk.dim(' Without url, the screenshot captures the root page regardless of the scenario.'));
986
- console.log(chalk.yellow(' Create separate scenarios for each page that shows different data.'));
987
- checkbox('For large seed data, write JSON to a project temp file and use @ prefix:');
988
- console.log(chalk.dim(' Write JSON to .codeyam/tmp/scenario.json then register with:'));
989
- console.log(chalk.dim(' codeyam editor register @.codeyam/tmp/scenario.json'));
990
- checkbox('For external API mocks (Stripe, weather, etc.), add externalApis:');
991
- console.log(chalk.dim(` "externalApis":{"GET https://api.stripe.com/v1/prices":{"body":[...],"status":200}}`));
992
- checkbox('If the app uses auth, email, or other patterns: see FEATURE_PATTERNS.md for scenario guidance');
993
- checkbox('If no database, use component-style mock scenarios (unchanged):');
994
- console.log(chalk.dim(` codeyam editor register '{"name":"Empty","description":"...","url":"/","mockData":{"routes":{"/api/...":{"body":[...]}}}}'`));
995
- checkbox('After each registration, check the response for `clientErrors`');
996
- console.log(chalk.yellow(' If clientErrors is non-empty → fix the issue and re-register the scenario'));
997
- console.log(chalk.yellow(' Fix client errors and re-register before moving on'));
1247
+ printAppScenarioInstructions();
998
1248
  console.log();
999
1249
  console.log(chalk.dim('Focus on creating and registering app-level scenarios. Code fixes happen in step 10 if needed.'));
1250
+ console.log();
1251
+ console.log(chalk.bold.cyan("Verify your work — screenshots don't lie:"));
1252
+ console.log(chalk.cyan(' • View every captured screenshot. Does it show what the scenario name promises?'));
1253
+ console.log(chalk.cyan(' • A "Library with Articles" screenshot showing an empty page means the scenario is BROKEN.'));
1254
+ console.log(chalk.cyan(' • Is the seed data diverse — different lengths, categories, counts — or just placeholders?'));
1255
+ console.log(chalk.cyan(' • Only create scenarios for states the CURRENT code can render.'));
1256
+ console.log();
1257
+ console.log(chalk.bold.yellow('GATE: Before proceeding, run `codeyam editor scenario-coverage`'));
1258
+ console.log(chalk.yellow(' This checks which existing scenarios have stale screenshots.'));
1259
+ console.log(chalk.yellow(' Re-register every stale scenario listed until the check passes.'));
1000
1260
  stopGate(8);
1001
1261
  }
1002
1262
  // ─── Step 9: User Scenarios ───────────────────────────────────────────
@@ -1017,34 +1277,37 @@ function printStep9(root, feature) {
1017
1277
  if (isResuming) {
1018
1278
  printResumptionHeader(9);
1019
1279
  }
1020
- console.log('Create per-persona scenarios if the app has users. Skip to step 10 if no users.');
1280
+ console.log('Create per-persona variations of existing scenarios. Skip to step 10 if no users.');
1021
1281
  console.log();
1022
- console.log(chalk.bold('If the app has users:'));
1023
- checkbox('Check existing scenarios: `codeyam editor scenarios`');
1024
- console.log(chalk.dim(' Reuse existing persona scenarios — update data to reflect current changes.'));
1025
- console.log(chalk.dim(' Re-register with the same name to update. Only add new personas if needed.'));
1026
- checkbox('Ensure scenarios for each user persona (admin, regular user, new user)');
1282
+ console.log(chalk.bold('If the app has NO users/auth:'));
1283
+ console.log(chalk.dim(' Skip this step and proceed to step 10 (Verify).'));
1284
+ console.log();
1285
+ console.log(chalk.bold('If the app has users/auth:'));
1286
+ console.log();
1287
+ console.log(chalk.bold('Goal: Create persona variations of EXISTING app scenarios.'));
1288
+ console.log(chalk.dim(' Do NOT create standalone persona scenarios. Each user-persona scenario should be'));
1289
+ console.log(chalk.dim(' a variation of an existing app scenario from step 8, with user-specific state layered on.'));
1290
+ console.log();
1291
+ console.log(chalk.bold('Checklist:'));
1292
+ checkbox('Run `codeyam editor scenarios` — list all existing app scenarios');
1293
+ checkbox('For EACH existing app scenario, create a logged-in variation:');
1294
+ console.log(chalk.dim(' Copy the scenario seed data and add "session":{"cookieValue":"sess_alice"} + user seed'));
1295
+ console.log(chalk.dim(' Name: "<Original Name> - Logged In" (e.g. "Full Catalog - Logged In")'));
1296
+ console.log(chalk.dim(' Step 8 scenarios already serve as logged-out versions (no session cookie)'));
1297
+ checkbox('If there are multiple user roles (admin, regular, etc.), create role-specific variations too');
1027
1298
  console.log(chalk.dim(' Each persona scenario layers user-specific seed data on top of an app scenario'));
1028
- checkbox('If using seed-based scenarios, register with type "user" and baseScenario:');
1029
- console.log(chalk.dim(` codeyam editor register '{"name":"Admin User","type":"user","url":"/","baseScenario":"<app-scenario-id>","seed":{"users":[{"id":1,"role":"admin"}]}}'`));
1030
- console.log(chalk.dim(' Always include "url" — the page to screenshot (e.g. "/", "/dashboard", "/drinks/1")'));
1031
- console.log(chalk.dim(" The base scenario's seed data is merged with this scenario's seed data (overlay wins)."));
1032
- checkbox('If the app uses auth or other patterns: see FEATURE_PATTERNS.md for per-persona scenario guidance');
1033
- checkbox('If using mock-based scenarios, register with mockData as before:');
1034
- console.log(chalk.dim(` codeyam editor register '{"name":"...","description":"...","mockData":{"routes":{"/api/...":{"body":[...]}}}}'`));
1299
+ checkbox('Include "dimensions" inherit from the base app scenario, or override if the persona implies a different device');
1300
+ console.log(chalk.dim(' e.g. a mobile-first user persona should use "dimensions":["Mobile"] even if the base scenario is Desktop.'));
1035
1301
  checkbox('After each registration, check the response for `clientErrors`');
1036
1302
  console.log(chalk.yellow(' If clientErrors is non-empty → fix the issue and re-register the scenario'));
1037
- console.log(chalk.yellow(' Fix client errors and re-register before moving on'));
1038
- console.log();
1039
- console.log(chalk.bold('If the app has NO users:'));
1040
- console.log(chalk.dim(' Skip this step and proceed to step 10 (Verify).'));
1041
1303
  console.log();
1042
- console.log(chalk.dim('Focus on creating user-persona scenarios (or skip if no users). Code fixes happen in step 10 if needed.'));
1304
+ console.log(chalk.dim('See FEATURE_PATTERNS.md and AUTH_PATTERNS.md for auth scenario guidance.'));
1043
1305
  stopGate(9);
1044
1306
  }
1045
1307
  // ─── Step 10: Verify ──────────────────────────────────────────────────
1046
1308
  function printStep10(root, feature) {
1047
1309
  const port = getServerPort();
1310
+ const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
1048
1311
  const prevState = readState(root);
1049
1312
  const isResuming = prevState?.step === 10;
1050
1313
  const now = new Date().toISOString();
@@ -1069,7 +1332,8 @@ function printStep10(root, feature) {
1069
1332
  console.log(chalk.dim(` codeyam editor register '{"name":"ComponentName - Scenario",...}'`));
1070
1333
  console.log();
1071
1334
  console.log(chalk.bold('Editor scenarios (App tab) — visual + error check:'));
1072
- checkbox(`Refresh the preview: \`codeyam editor preview\``);
1335
+ checkbox(`Refresh the preview: \`codeyam editor preview '{"dimension":"${dim}"}'\``);
1336
+ printDimensionGuidance(dim, dimNames);
1073
1337
  checkbox('Click through each app-level and user-persona scenario in the preview');
1074
1338
  checkbox('For seed-based scenarios: verify data renders correctly after switching');
1075
1339
  console.log(chalk.dim(' Switch between scenarios and confirm the app reflects the seeded data each time'));
@@ -1087,131 +1351,818 @@ function printStep10(root, feature) {
1087
1351
  console.log(chalk.dim('Focus on fixing issues. All component screenshots, scenarios, and tests must be clean before proceeding.'));
1088
1352
  stopGate(10);
1089
1353
  }
1090
- // ─── Step 11: Journal ─────────────────────────────────────────────────
1091
- function printStep11(root, feature) {
1092
- const port = getServerPort();
1354
+ // ─── Step 11: Journal ─────────────────────────────────────────────────
1355
+ function printStep11(root, feature) {
1356
+ const port = getServerPort();
1357
+ const prevState = readState(root);
1358
+ const isResuming = prevState?.step === 11;
1359
+ const now = new Date().toISOString();
1360
+ writeState(root, {
1361
+ feature,
1362
+ step: 11,
1363
+ label: STEP_LABELS[11],
1364
+ startedAt: isResuming ? prevState.startedAt : now,
1365
+ featureStartedAt: prevState?.featureStartedAt || now,
1366
+ });
1367
+ logEvent(root, 'step', { step: 11, label: 'Journal', feature });
1368
+ stepHeader(11, 'Journal', feature);
1369
+ if (isResuming) {
1370
+ printResumptionHeader(11);
1371
+ }
1372
+ console.log('Create or update the journal entry for this feature.');
1373
+ console.log();
1374
+ console.log(chalk.bold('Checklist:'));
1375
+ checkbox('Write a concise description of what was built (2-3 sentences)');
1376
+ checkbox(`First time at step 11 — create journal entry with ALL session screenshots:`);
1377
+ console.log(chalk.dim(` codeyam editor journal '{"title":"...","type":"feature","description":"...","includeSessionScenarios":true}'`));
1378
+ console.log(chalk.dim(' includeSessionScenarios auto-discovers component + app + user persona screenshots'));
1379
+ checkbox(`Returning to step 11 (before commit) — update the existing journal entry:`);
1380
+ console.log(chalk.dim(` codeyam editor journal-update '{"time":"<journal entry time>","description":"<updated>","includeSessionScenarios":true}'`));
1381
+ console.log(chalk.dim(' Note: PATCH only works before the entry is committed. After commit, use POST to create a new entry.'));
1382
+ console.log();
1383
+ console.log(chalk.dim('Focus on creating or updating the journal entry. Summary and presentation happen in step 13.'));
1384
+ stopGate(11);
1385
+ }
1386
+ // ─── Step 12: Review ──────────────────────────────────────────────────
1387
+ function printStep12(root, feature) {
1388
+ const port = getServerPort();
1389
+ const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
1390
+ const prevState = readState(root);
1391
+ const isResuming = prevState?.step === 12;
1392
+ const now = new Date().toISOString();
1393
+ writeState(root, {
1394
+ feature,
1395
+ step: 12,
1396
+ label: STEP_LABELS[12],
1397
+ startedAt: isResuming ? prevState.startedAt : now,
1398
+ featureStartedAt: prevState?.featureStartedAt || now,
1399
+ });
1400
+ logEvent(root, 'step', { step: 12, label: 'Review', feature });
1401
+ stepHeader(12, 'Review', feature);
1402
+ if (isResuming) {
1403
+ printResumptionHeader(12);
1404
+ }
1405
+ console.log('Verify all screenshots and checks pass before presenting to the user.');
1406
+ console.log();
1407
+ console.log(chalk.bold('Checklist (do all of this silently):'));
1408
+ checkbox(`Refresh the preview: \`codeyam editor preview '{"dimension":"${dim}"}'\``);
1409
+ printDimensionGuidance(dim, dimNames);
1410
+ checkbox('Verify each component has screenshots in the App tab (grouped under Components)');
1411
+ checkbox('If any are missing, re-register them using `codeyam editor register`');
1412
+ checkbox(`Check for client errors: \`codeyam editor client-errors\``);
1413
+ checkbox('If `hasErrors` is true, fix them and re-capture affected scenarios');
1414
+ checkbox('Run `codeyam editor verify-images \'{"paths":["/"], "imageUrls":["url1"]}\'` with all page paths and image URLs');
1415
+ checkbox('Fix or remove any broken images before continuing');
1416
+ checkbox('Run `codeyam editor audit` to verify completeness of scenarios and tests');
1417
+ checkbox('Do not proceed until all checks pass');
1418
+ stopGate(12);
1419
+ }
1420
+ // ─── Step 13: Present ─────────────────────────────────────────────────
1421
+ function printStep13(root, feature) {
1422
+ const port = getServerPort();
1423
+ const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
1424
+ const prevState = readState(root);
1425
+ const isResuming = prevState?.step === 13;
1426
+ const now = new Date().toISOString();
1427
+ writeState(root, {
1428
+ feature,
1429
+ step: 13,
1430
+ label: STEP_LABELS[13],
1431
+ startedAt: isResuming ? prevState.startedAt : now,
1432
+ featureStartedAt: prevState?.featureStartedAt || now,
1433
+ });
1434
+ logEvent(root, 'step', { step: 13, label: 'Present', feature });
1435
+ stepHeader(13, 'Present', feature);
1436
+ if (isResuming) {
1437
+ printResumptionHeader(13);
1438
+ }
1439
+ console.log('Present the results to the user and get their approval.');
1440
+ console.log();
1441
+ console.log(chalk.bold('Checklist:'));
1442
+ checkbox('Fetch all scenarios: `codeyam editor scenarios`');
1443
+ console.log(chalk.dim(' Each scenario has a `changeStatus` field: "new", "edited", "impacted", or null.'));
1444
+ checkbox('Pick the best scenario to showcase from this working session:');
1445
+ console.log(chalk.dim(' Prefer scenarios with changeStatus "new" or "edited" (directly changed in this session).'));
1446
+ console.log(chalk.dim(' For app-level scenarios, prefer those showing the new/changed functionality.'));
1447
+ console.log(chalk.dim(' NEVER pick a scenario with changeStatus null — those are unchanged from a previous commit.'));
1448
+ checkbox('Switch the preview to that scenario using its `id` and `dimension`:');
1449
+ console.log(chalk.dim(` codeyam editor preview '{"scenarioId":"<id>","dimension":"${dim}"}'`));
1450
+ console.log(chalk.dim(' Use the dimension that matches the scenario — check its registered dimensions.'));
1451
+ printDimensionGuidance(dim, dimNames);
1452
+ checkbox(`Show the results panel: \`codeyam editor show-results\``);
1453
+ console.log(chalk.dim(' This opens a visual panel below the terminal showing all scenarios with screenshots.'));
1454
+ console.log(chalk.dim(' The user can click scenarios to switch the live preview.'));
1455
+ checkbox('Write a 1-2 sentence summary of what was built');
1456
+ checkbox('Report test count and audit status (one line)');
1457
+ console.log();
1458
+ console.log(chalk.bold('Present a selection menu to the user (use AskUserQuestion with these EXACT option labels):'));
1459
+ console.log(chalk.green(' Option 1 label: "Save & commit"') +
1460
+ chalk.dim(' — git commit all changes and record in journal'));
1461
+ console.log(chalk.yellow(' Option 2 label: "I\'d like to make some changes"') +
1462
+ chalk.dim(' — describe changes, then re-verify'));
1463
+ console.log();
1464
+ console.log(chalk.bold('If the user chooses "Save & commit":'));
1465
+ checkbox('Advance to the commit step: `codeyam editor 14`');
1466
+ console.log();
1467
+ console.log(chalk.bold('If the user chooses "Make changes" (or asks for ANY change, even as a question):'));
1468
+ checkbox(`Hide the results panel: \`codeyam editor hide-results\``);
1469
+ checkbox('Ask what changes the user wants (if not already clear)');
1470
+ checkbox(`Run: \`codeyam editor change "${feature}"\` — this gives you the change checklist`);
1471
+ checkbox('THEN make the requested changes and follow the checklist');
1472
+ console.log(chalk.red.bold(' IMPORTANT: Always run the change command BEFORE writing any code.'));
1473
+ stopGate(13, { confirm: true });
1474
+ }
1475
+ // ─── Migration Mode ──────────────────────────────────────────────────
1476
+ /**
1477
+ * Print a progress tracker for the 8 migration steps.
1478
+ */
1479
+ function printMigrationProgressTracker(current, pageName, pageIndex, totalPages) {
1480
+ console.log();
1481
+ console.log(chalk.dim(` Migration: Page ${pageIndex + 1}/${totalPages} (${pageName})`));
1482
+ console.log(chalk.dim(' ┌─────────────────────────────────────┐'));
1483
+ for (let i = 1; i <= 10; i++) {
1484
+ const label = MIGRATION_STEP_LABELS[i];
1485
+ const num = i < 10 ? ` ${i}` : `${i}`;
1486
+ const content = `${num}. ${label.padEnd(28)}`;
1487
+ if (i < current) {
1488
+ console.log(chalk.dim(' │') + chalk.green(` ✓ ${content}`) + chalk.dim('│'));
1489
+ }
1490
+ else if (i === current) {
1491
+ console.log(chalk.dim(' │') + chalk.bold.cyan(` → ${content}`) + chalk.dim('│'));
1492
+ }
1493
+ else {
1494
+ console.log(chalk.dim(` │ ○ ${content}│`));
1495
+ }
1496
+ }
1497
+ console.log(chalk.dim(' └─────────────────────────────────────┘'));
1498
+ }
1499
+ /**
1500
+ * Print a STOP gate for migration steps.
1501
+ */
1502
+ function migrationStopGate(current, pageName, pageIndex, totalPages, opts) {
1503
+ console.log();
1504
+ console.log(chalk.bold.red('━━━ STOP ━━━'));
1505
+ console.log();
1506
+ console.log(chalk.red('Complete each checklist item above before proceeding to the next step.'));
1507
+ if (opts?.confirm) {
1508
+ console.log();
1509
+ console.log(chalk.yellow('Wait for user confirmation before moving on.'));
1510
+ }
1511
+ console.log();
1512
+ console.log(chalk.bold.yellow('Present the following progress tracker to the user (copy it verbatim):'));
1513
+ printMigrationProgressTracker(current, pageName, pageIndex, totalPages);
1514
+ console.log();
1515
+ console.log(chalk.yellow('For the CURRENT step (→), show each checklist item with ✓ (done) or ✗ (skipped + reason).'));
1516
+ console.log();
1517
+ if (current === 5) {
1518
+ // Step 5 (Discuss) can skip to step 9 if user declines decomposition
1519
+ console.log(chalk.green('If decomposing, run: ') +
1520
+ chalk.bold('codeyam editor migrate 6'));
1521
+ console.log(chalk.green('If skipping decomposition, run: ') +
1522
+ chalk.bold('codeyam editor migrate 9'));
1523
+ }
1524
+ else if (current < 10) {
1525
+ console.log(chalk.green('When done, run: ') +
1526
+ chalk.bold(`codeyam editor migrate ${current + 1}`));
1527
+ }
1528
+ else {
1529
+ console.log(chalk.green('Page complete! Run: ') +
1530
+ chalk.bold('codeyam editor migrate next') +
1531
+ chalk.green(' to start the next page'));
1532
+ }
1533
+ console.log();
1534
+ }
1535
+ /**
1536
+ * Write migration-aware editor state.
1537
+ */
1538
+ function writeMigrationStepState(root, step, pageName, pageIndex, totalPages) {
1539
+ const prevState = readState(root);
1540
+ const isResuming = prevState?.step === step && !!prevState?.migration;
1541
+ const now = new Date().toISOString();
1542
+ writeState(root, {
1543
+ feature: `Migration: ${pageName}`,
1544
+ step,
1545
+ label: MIGRATION_STEP_LABELS[step],
1546
+ startedAt: isResuming ? prevState.startedAt : now,
1547
+ featureStartedAt: prevState?.featureStartedAt || now,
1548
+ migration: {
1549
+ pageName,
1550
+ pageIndex,
1551
+ totalPages,
1552
+ },
1553
+ });
1554
+ logEvent(root, 'step', {
1555
+ step,
1556
+ label: MIGRATION_STEP_LABELS[step],
1557
+ feature: `Migration: ${pageName}`,
1558
+ migration: true,
1559
+ });
1560
+ }
1561
+ /**
1562
+ * Get the current migration page info from state, or return null.
1563
+ */
1564
+ function getCurrentMigrationPage(root) {
1565
+ const migState = readMigrationState(root);
1566
+ if (!migState || migState.pages.length === 0)
1567
+ return null;
1568
+ const page = migState.pages[migState.currentPageIndex];
1569
+ if (!page)
1570
+ return null;
1571
+ return {
1572
+ pageName: page.name,
1573
+ pageIndex: migState.currentPageIndex,
1574
+ totalPages: migState.pages.length,
1575
+ };
1576
+ }
1577
+ // ─── Migration Step 1: Survey ───────────────────────────────────────
1578
+ function printMigrateStep1(root) {
1579
+ const pageInfo = getCurrentMigrationPage(root);
1580
+ if (!pageInfo) {
1581
+ console.error(chalk.red('Error: No migration in progress. Run `codeyam editor migrate` first.'));
1582
+ process.exit(1);
1583
+ }
1584
+ const { pageName, pageIndex, totalPages } = pageInfo;
1585
+ writeMigrationStepState(root, 1, pageName, pageIndex, totalPages);
1586
+ const migState = readMigrationState(root);
1587
+ const page = migState.pages[pageIndex];
1588
+ console.log();
1589
+ console.log(chalk.bold.cyan(`━━━ Migration Step 1: Survey — ${pageName} (${pageIndex + 1}/${totalPages}) ━━━`));
1590
+ console.log();
1591
+ console.log(chalk.bold('Goal: Survey the page, understand its structure, and start the dev server.'));
1592
+ console.log();
1593
+ console.log(chalk.bold('Survey:'));
1594
+ checkbox(`Read the page file: \`${page.filePath}\``);
1595
+ checkbox('Follow every import — read all local components, hooks, utilities used by this page');
1596
+ checkbox('Map the data flow: where does data come from? Server actions? API routes? Props?');
1597
+ checkbox('Check `.codeyam/glossary.json` for components already extracted from previous pages');
1598
+ checkbox('Check `.codeyam/migration-state.json` sharedComponents for reusable pieces');
1599
+ checkbox('Note the page route and any dynamic segments');
1600
+ console.log();
1601
+ console.log(chalk.bold('Dev Server:'));
1602
+ checkbox('If the dev server is not running yet, figure out how to start it (check package.json scripts)');
1603
+ checkbox('FIRST: Verify dependencies are installed (check if node_modules/ exists and is non-empty)');
1604
+ console.log(chalk.yellow(' If node_modules is missing or sparse, run the package manager install command first'));
1605
+ console.log(chalk.dim(' Missing deps cause cryptic 500 errors at render time — install BEFORE starting the server'));
1606
+ checkbox('Start the dev server: `codeyam editor dev-server \'{"action":"start"}\'`');
1607
+ checkbox("Verify it's running: check for successful startup output");
1608
+ console.log();
1609
+ migrationStopGate(1, pageName, pageIndex, totalPages);
1610
+ }
1611
+ // ─── Migration Step 2: App Scenarios ────────────────────────────────
1612
+ function printMigrateStep2(root) {
1613
+ const pageInfo = getCurrentMigrationPage(root);
1614
+ if (!pageInfo) {
1615
+ console.error(chalk.red('Error: No migration in progress. Run `codeyam editor migrate` first.'));
1616
+ process.exit(1);
1617
+ }
1618
+ const { pageName, pageIndex, totalPages } = pageInfo;
1619
+ writeMigrationStepState(root, 2, pageName, pageIndex, totalPages);
1620
+ const migState = readMigrationState(root);
1621
+ const page = migState.pages[pageIndex];
1622
+ console.log();
1623
+ console.log(chalk.bold.cyan(`━━━ Migration Step 2: App Scenarios — ${pageName} (${pageIndex + 1}/${totalPages}) ━━━`));
1624
+ console.log();
1625
+ console.log(chalk.bold(`Goal: Register application scenarios that screenshot the ACTUAL page at "${page.route}".`));
1626
+ console.log();
1627
+ console.log(chalk.yellow(' App scenarios capture how the full page looks with different data states.'));
1628
+ console.log(chalk.yellow(` Every scenario MUST use the real page route ("${page.route}") — NOT isolation routes.`));
1629
+ console.log(chalk.yellow(' These are the primary scenarios the user will see for this page.'));
1630
+ console.log();
1631
+ console.log(chalk.bold('Seed Adapter Setup:'));
1632
+ checkbox('Check if a seed adapter exists: `ls .codeyam/seed-adapter.ts 2>/dev/null`');
1633
+ console.log(chalk.yellow(' If NO seed adapter exists, create one. The seed adapter wipes and re-seeds the database'));
1634
+ console.log(chalk.yellow(' for each scenario, enabling different data states on the real page.'));
1635
+ console.log();
1636
+ console.log(chalk.dim(' How to create a seed adapter:'));
1637
+ console.log(chalk.dim(' 1. Identify the database technology from your step 1 survey (Prisma, Supabase, Drizzle, etc.)'));
1638
+ console.log(chalk.dim(' 2. Check for a matching template: `ls .codeyam/seed-adapters/`'));
1639
+ console.log(chalk.dim(' 3. For Supabase: `cp .codeyam/seed-adapters/supabase.ts .codeyam/seed-adapter.ts`'));
1640
+ console.log(chalk.dim(' The seed adapter auto-detects Supabase credentials by scanning env var VALUES (not names).'));
1641
+ console.log(chalk.dim(' It reads .env / .env.local and finds: *.supabase.co URLs, sb_secret_* keys, and legacy JWTs.'));
1642
+ console.log(chalk.dim(' Do NOT grep for specific env var names — just register a scenario and try it.'));
1643
+ console.log(chalk.dim(' If it fails, the error message will say exactly what credentials are missing.'));
1644
+ console.log(chalk.dim(' 4. For Prisma: write a seed adapter that uses your Prisma client to delete/insert rows'));
1645
+ console.log(chalk.dim(' 5. For other databases: write a .codeyam/seed-adapter.ts that reads a JSON file (argv[2]),'));
1646
+ console.log(chalk.dim(' deletes all rows from each table, then inserts the seed rows. Format: {"table": [{...}]}'));
1647
+ console.log(chalk.dim(' 6. Test it: write a small test seed JSON and run `npx tsx .codeyam/seed-adapter.ts test.json`'));
1648
+ console.log();
1649
+ printAppScenarioInstructions(pageName, page.route);
1650
+ console.log();
1651
+ migrationStopGate(2, pageName, pageIndex, totalPages);
1652
+ }
1653
+ // ─── Migration Step 3: Component Scenarios ──────────────────────────
1654
+ function printMigrateStep3(root) {
1655
+ const pageInfo = getCurrentMigrationPage(root);
1656
+ if (!pageInfo) {
1657
+ console.error(chalk.red('Error: No migration in progress. Run `codeyam editor migrate` first.'));
1658
+ process.exit(1);
1659
+ }
1660
+ const { pageName, pageIndex, totalPages } = pageInfo;
1661
+ writeMigrationStepState(root, 3, pageName, pageIndex, totalPages);
1662
+ console.log();
1663
+ console.log(chalk.bold.cyan(`━━━ Migration Step 3: Component Scenarios — ${pageName} (${pageIndex + 1}/${totalPages}) ━━━`));
1664
+ console.log();
1665
+ console.log(chalk.bold('Goal: Capture individual component scenarios for major visual components on this page.'));
1666
+ console.log();
1667
+ console.log(chalk.dim(' Component scenarios use isolation routes with a codeyam-capture wrapper for tight screenshots.'));
1668
+ console.log(chalk.dim(' These supplement the app scenarios from step 2 with focused component-level views.'));
1669
+ console.log();
1670
+ printComponentCaptureInstructions();
1671
+ console.log();
1672
+ migrationStopGate(3, pageName, pageIndex, totalPages);
1673
+ }
1674
+ // ─── Migration Step 4: Preview ──────────────────────────────────────
1675
+ function printMigrateStep4(root) {
1676
+ const pageInfo = getCurrentMigrationPage(root);
1677
+ if (!pageInfo) {
1678
+ console.error(chalk.red('Error: No migration in progress.'));
1679
+ process.exit(1);
1680
+ }
1681
+ const { pageName, pageIndex, totalPages } = pageInfo;
1682
+ writeMigrationStepState(root, 4, pageName, pageIndex, totalPages);
1683
+ console.log();
1684
+ console.log(chalk.bold.cyan(`━━━ Migration Step 4: Preview — ${pageName} (${pageIndex + 1}/${totalPages}) ━━━`));
1685
+ console.log();
1686
+ console.log(chalk.bold('Goal: Verify screenshots look correct and show results to the user.'));
1687
+ console.log();
1688
+ console.log(chalk.bold('Checklist:'));
1689
+ checkbox('Review all scenario screenshots for this page — do they look correct?');
1690
+ checkbox('Check for client errors: `codeyam editor client-errors`');
1691
+ checkbox('If any issues: fix the scenario data, re-register affected scenarios');
1692
+ checkbox('Fetch all scenarios: `codeyam editor scenarios`');
1693
+ checkbox('Pick the best scenario to showcase:');
1694
+ console.log(chalk.dim(` codeyam editor preview '{"scenarioId":"<id>"}'`));
1695
+ checkbox('Show results: `codeyam editor show-results`');
1696
+ console.log();
1697
+ console.log(chalk.dim('The user should now see their existing page with live screenshots in the preview.'));
1698
+ migrationStopGate(4, pageName, pageIndex, totalPages);
1699
+ }
1700
+ // ─── Migration Step 5: Discuss ──────────────────────────────────────
1701
+ function printMigrateStep5(root) {
1702
+ const pageInfo = getCurrentMigrationPage(root);
1703
+ if (!pageInfo) {
1704
+ console.error(chalk.red('Error: No migration in progress.'));
1705
+ process.exit(1);
1706
+ }
1707
+ const { pageName, pageIndex, totalPages } = pageInfo;
1708
+ writeMigrationStepState(root, 5, pageName, pageIndex, totalPages);
1709
+ console.log();
1710
+ console.log(chalk.bold.cyan(`━━━ Migration Step 5: Discuss — ${pageName} (${pageIndex + 1}/${totalPages}) ━━━`));
1711
+ console.log();
1712
+ console.log(chalk.bold('Goal: Assess page complexity and ask the user if decomposition is worth it.'));
1713
+ console.log();
1714
+ console.log(chalk.bold('Checklist:'));
1715
+ checkbox('Hide results: `codeyam editor hide-results`');
1716
+ checkbox("Assess the page's complexity:");
1717
+ console.log(chalk.dim(' — How many inline components exist?'));
1718
+ console.log(chalk.dim(' — How much business logic is embedded in the page file?'));
1719
+ console.log(chalk.dim(' — Are there reusable pieces that other pages could share?'));
1720
+ console.log(chalk.dim(' — Would extraction improve testability or maintainability?'));
1721
+ checkbox('Present your assessment to the user');
1722
+ console.log();
1723
+ console.log(chalk.bold('Present a selection menu (AskUserQuestion with these EXACT option labels):'));
1724
+ console.log(chalk.green(' Option 1 label: "Yes, decompose this page"') +
1725
+ chalk.dim(' — continue to step 6 (Decompose)'));
1726
+ console.log(chalk.yellow(' Option 2 label: "No, skip decomposition"') +
1727
+ chalk.dim(' — skip to step 9 (Journal)'));
1728
+ console.log();
1729
+ console.log(chalk.bold.yellow('Routing:'));
1730
+ console.log(chalk.yellow(' If user chooses decomposition: ') +
1731
+ chalk.bold('codeyam editor migrate 6'));
1732
+ console.log(chalk.yellow(' If user skips: ') + chalk.bold('codeyam editor migrate 9'));
1733
+ migrationStopGate(5, pageName, pageIndex, totalPages, { confirm: true });
1734
+ }
1735
+ // ─── Migration Step 6: Decompose ────────────────────────────────────
1736
+ function printMigrateStep6(root) {
1737
+ const pageInfo = getCurrentMigrationPage(root);
1738
+ if (!pageInfo) {
1739
+ console.error(chalk.red('Error: No migration in progress.'));
1740
+ process.exit(1);
1741
+ }
1742
+ const { pageName, pageIndex, totalPages } = pageInfo;
1743
+ writeMigrationStepState(root, 6, pageName, pageIndex, totalPages);
1744
+ console.log();
1745
+ console.log(chalk.bold.cyan(`━━━ Migration Step 6: Decompose — ${pageName} (${pageIndex + 1}/${totalPages}) ━━━`));
1746
+ console.log();
1747
+ console.log(chalk.bold('Goal: Plan all extractions. Mark already-extracted shared components as REUSE.'));
1748
+ console.log(chalk.yellow('This step is read and plan only. Code extraction happens in step 7.'));
1749
+ console.log();
1750
+ console.log(chalk.bold.yellow('Migration note: check glossary for reuse opportunities'));
1751
+ console.log(chalk.yellow(' If a component exists in the glossary from a previous page, mark it as REUSE in your plan.'));
1752
+ console.log(chalk.yellow(' Only add it to the extraction plan if it needs modifications for this page.'));
1753
+ console.log();
1754
+ printExtractionPlanInstructions();
1755
+ migrationStopGate(6, pageName, pageIndex, totalPages);
1756
+ }
1757
+ // ─── Migration Step 7: Extract ──────────────────────────────────────
1758
+ function printMigrateStep7(root) {
1759
+ const pageInfo = getCurrentMigrationPage(root);
1760
+ if (!pageInfo) {
1761
+ console.error(chalk.red('Error: No migration in progress.'));
1762
+ process.exit(1);
1763
+ }
1764
+ const { pageName, pageIndex, totalPages } = pageInfo;
1765
+ writeMigrationStepState(root, 7, pageName, pageIndex, totalPages);
1766
+ console.log();
1767
+ console.log(chalk.bold.cyan(`━━━ Migration Step 7: Extract — ${pageName} (${pageIndex + 1}/${totalPages}) ━━━`));
1768
+ console.log();
1769
+ console.log(chalk.bold('Goal: Execute the extraction plan. Components first, then functions via TDD.'));
1770
+ console.log();
1771
+ console.log(chalk.bold('Checklist:'));
1772
+ checkbox('Extract visual components (no tests needed for components)');
1773
+ checkbox('Extract library functions via TDD (write failing test first, then implement)');
1774
+ checkbox('Extract hooks as needed');
1775
+ checkbox('Recursive pass: check each extracted component for further extraction');
1776
+ console.log(chalk.yellow(' Each component should be a thin shell composing sub-components.'));
1777
+ checkbox('Verify the page file is ONLY imports + component composition');
1778
+ checkbox('Run tests to confirm nothing is broken: `npx jest --testPathPattern="<relevant>" --maxWorkers=2`');
1779
+ console.log();
1780
+ console.log(chalk.dim('After extraction, the page should be a thin shell. Move to step 8 to recapture.'));
1781
+ migrationStopGate(7, pageName, pageIndex, totalPages);
1782
+ }
1783
+ // ─── Migration Step 8: Recapture ────────────────────────────────────
1784
+ function printMigrateStep8(root) {
1785
+ const pageInfo = getCurrentMigrationPage(root);
1786
+ if (!pageInfo) {
1787
+ console.error(chalk.red('Error: No migration in progress.'));
1788
+ process.exit(1);
1789
+ }
1790
+ const { pageName, pageIndex, totalPages } = pageInfo;
1791
+ writeMigrationStepState(root, 8, pageName, pageIndex, totalPages);
1792
+ const migState = readMigrationState(root);
1793
+ const page = migState.pages[pageIndex];
1794
+ console.log();
1795
+ console.log(chalk.bold.cyan(`━━━ Migration Step 8: Recapture — ${pageName} (${pageIndex + 1}/${totalPages}) ━━━`));
1796
+ console.log();
1797
+ console.log(chalk.bold('Goal: Update glossary, re-register ALL scenarios after code changes, and verify.'));
1798
+ console.log();
1799
+ console.log(chalk.bold('Glossary:'));
1800
+ printGlossaryInstructions();
1801
+ console.log(chalk.yellow(' Skip entries already in the glossary from previous page migrations.'));
1802
+ checkbox('Update sharedComponents in `.codeyam/migration-state.json` if any extracted components are shared');
1803
+ console.log();
1804
+ console.log(chalk.bold.red('Re-register App Scenarios (REQUIRED):'));
1805
+ console.log(chalk.yellow(` You MUST re-register application scenarios for "${page.route}" to get fresh screenshots.`));
1806
+ checkbox('Fetch existing scenarios: `codeyam editor scenarios`');
1807
+ checkbox('Find all scenarios that use the real page route (not /codeyam-isolate/ paths)');
1808
+ checkbox('Re-register each one with the SAME name to update its screenshot');
1809
+ console.log(chalk.dim(` Example: codeyam editor register '{"name":"${pageName} - Full Data","type":"application","url":"${page.route}",...}'`));
1810
+ checkbox('After each registration, check the response for `clientErrors`');
1811
+ console.log(chalk.yellow(' Fix client errors and re-register before moving on'));
1812
+ console.log();
1813
+ console.log(chalk.bold('Component Scenarios:'));
1814
+ console.log(chalk.dim(' For each visual component extracted in step 7, create isolation routes and register scenarios.'));
1815
+ printComponentCaptureInstructions();
1816
+ console.log();
1817
+ console.log(chalk.bold('Verify:'));
1818
+ checkbox('Run `codeyam editor analyze-imports` to populate import graph');
1819
+ checkbox('Run library function tests: `npx jest --testPathPattern="<relevant>" --maxWorkers=2`');
1820
+ checkbox('Run `codeyam editor audit` to check completeness');
1821
+ checkbox('Check for client errors: `codeyam editor client-errors`');
1822
+ checkbox('Fix any issues before proceeding');
1823
+ console.log();
1824
+ migrationStopGate(8, pageName, pageIndex, totalPages);
1825
+ }
1826
+ // ─── Migration Step 9: Journal ──────────────────────────────────────
1827
+ function printMigrateStep9(root) {
1828
+ const pageInfo = getCurrentMigrationPage(root);
1829
+ if (!pageInfo) {
1830
+ console.error(chalk.red('Error: No migration in progress.'));
1831
+ process.exit(1);
1832
+ }
1833
+ const { pageName, pageIndex, totalPages } = pageInfo;
1834
+ writeMigrationStepState(root, 9, pageName, pageIndex, totalPages);
1835
+ console.log();
1836
+ console.log(chalk.bold.cyan(`━━━ Migration Step 9: Journal — ${pageName} (${pageIndex + 1}/${totalPages}) ━━━`));
1837
+ console.log();
1838
+ console.log(`Create a journal entry documenting the migration of the ${pageName} page.`);
1839
+ console.log();
1840
+ console.log(chalk.bold('Checklist:'));
1841
+ checkbox('Write a concise description of what was migrated (2-3 sentences)');
1842
+ checkbox(`Create journal: \`codeyam editor journal '{"title":"Migration: ${pageName}","type":"migration","description":"...","includeSessionScenarios":true}'\``);
1843
+ console.log();
1844
+ migrationStopGate(9, pageName, pageIndex, totalPages);
1845
+ }
1846
+ // ─── Migration Step 10: Present ─────────────────────────────────────
1847
+ function printMigrateStep10(root) {
1848
+ const pageInfo = getCurrentMigrationPage(root);
1849
+ if (!pageInfo) {
1850
+ console.error(chalk.red('Error: No migration in progress.'));
1851
+ process.exit(1);
1852
+ }
1853
+ const { pageName, pageIndex, totalPages } = pageInfo;
1854
+ writeMigrationStepState(root, 10, pageName, pageIndex, totalPages);
1855
+ console.log();
1856
+ console.log(chalk.bold.cyan(`━━━ Migration Step 10: Present — ${pageName} (${pageIndex + 1}/${totalPages}) ━━━`));
1857
+ console.log();
1858
+ console.log("Show results and commit this page's migration.");
1859
+ console.log();
1860
+ console.log(chalk.bold('Checklist:'));
1861
+ checkbox('Fetch all scenarios: `codeyam editor scenarios`');
1862
+ checkbox('Pick the best scenario to showcase:');
1863
+ console.log(chalk.dim(` codeyam editor preview '{"scenarioId":"<id>"}'`));
1864
+ checkbox('Show results: `codeyam editor show-results`');
1865
+ checkbox('Write a 1-2 sentence summary of what was migrated');
1866
+ console.log();
1867
+ console.log(chalk.bold('Present a selection menu (AskUserQuestion with these EXACT option labels):'));
1868
+ console.log(chalk.green(' Option 1 label: "Save & commit"') +
1869
+ chalk.dim(" — commit this page's migration"));
1870
+ console.log(chalk.yellow(' Option 2 label: "I\'d like to make some changes"') +
1871
+ chalk.dim(' — make changes, then re-verify'));
1872
+ console.log();
1873
+ console.log(chalk.bold('If the user chooses "Save & commit":'));
1874
+ checkbox('Hide results: `codeyam editor hide-results`');
1875
+ checkbox(`Commit: \`codeyam editor commit '{"message":"migration: ${pageName} page\\n\\n<description>"}'\``);
1876
+ checkbox('Mark page complete: `codeyam editor migrate complete`');
1877
+ const remaining = pageInfo.totalPages - pageIndex - 1;
1878
+ if (remaining > 0) {
1879
+ checkbox('Advance to next page: `codeyam editor migrate next`');
1880
+ console.log();
1881
+ console.log(chalk.bold.green(`Page ${pageIndex + 1}/${totalPages} complete! ${remaining} page(s) remaining.`));
1882
+ }
1883
+ else {
1884
+ console.log();
1885
+ console.log(chalk.bold.green('Migration complete! All pages processed.'));
1886
+ console.log(chalk.green('The project is now fully migrated. Future sessions use the normal feature workflow.'));
1887
+ console.log(chalk.green('Start building: ') + chalk.bold('codeyam editor steps'));
1888
+ }
1889
+ console.log();
1890
+ console.log(chalk.bold('If the user chooses "Make changes":'));
1891
+ checkbox('Hide results: `codeyam editor hide-results`');
1892
+ checkbox('Ask what changes the user wants');
1893
+ checkbox(`Run: \`codeyam editor change "Migration: ${pageName}"\` — follow the change checklist`);
1894
+ console.log();
1895
+ migrationStopGate(10, pageName, pageIndex, totalPages, { confirm: true });
1896
+ }
1897
+ // ─── Migration Survey ───────────────────────────────────────────────
1898
+ function printMigrateSurvey(root) {
1899
+ console.log();
1900
+ console.log(chalk.bold.cyan('━━━ Project Migration Survey ━━━'));
1901
+ console.log();
1902
+ console.log('Survey the existing project, present a migration plan, and confirm the order with the user.');
1903
+ console.log(chalk.dim('See the "Migration Survey" section in SKILL.md for the full checklist and migration-state.json format.'));
1904
+ console.log();
1905
+ // Write editor state so hooks and printCycleOverview detect migration
1906
+ const now = new Date().toISOString();
1907
+ writeState(root, {
1908
+ feature: 'Migration: Survey',
1909
+ step: 0,
1910
+ label: 'Survey',
1911
+ startedAt: now,
1912
+ featureStartedAt: now,
1913
+ migration: {
1914
+ pageName: 'Survey',
1915
+ pageIndex: -1,
1916
+ totalPages: 0,
1917
+ },
1918
+ });
1919
+ logEvent(root, 'migration-survey', {});
1920
+ console.log(chalk.bold.red('━━━ STOP ━━━'));
1921
+ console.log();
1922
+ console.log(chalk.red('Survey the project and confirm the migration order with the user.'));
1923
+ console.log(chalk.green('After confirmation and writing migration-state.json: ') +
1924
+ chalk.bold('codeyam editor migrate 1'));
1925
+ console.log();
1926
+ }
1927
+ // ─── Migration Status ───────────────────────────────────────────────
1928
+ function printMigrationStatus(root) {
1929
+ const state = readMigrationState(root);
1930
+ if (!state) {
1931
+ console.log(chalk.dim('No migration in progress.'));
1932
+ return;
1933
+ }
1934
+ console.log();
1935
+ console.log(chalk.bold.cyan('━━━ Migration Progress ━━━'));
1936
+ console.log();
1937
+ const done = state.pages.filter((p) => p.status === 'complete').length;
1938
+ const total = state.pages.length;
1939
+ const pct = total > 0 ? Math.round((done / total) * 100) : 0;
1940
+ console.log(` Status: ${chalk.bold(state.status)} — ${done}/${total} pages (${pct}%)`);
1941
+ console.log();
1942
+ for (let i = 0; i < state.pages.length; i++) {
1943
+ const p = state.pages[i];
1944
+ let icon;
1945
+ let color;
1946
+ if (p.status === 'complete') {
1947
+ icon = '✓';
1948
+ color = chalk.green;
1949
+ }
1950
+ else if (p.status === 'in-progress') {
1951
+ icon = '→';
1952
+ color = chalk.bold.cyan;
1953
+ }
1954
+ else {
1955
+ icon = '○';
1956
+ color = chalk.dim;
1957
+ }
1958
+ const extra = p.status === 'complete'
1959
+ ? chalk.dim(` (${p.extractedComponents.length} components, ${p.extractedFunctions.length} functions, ${p.scenarioCount} scenarios)`)
1960
+ : '';
1961
+ console.log(` ${color(`${icon} ${i + 1}. ${p.name}`)} ${chalk.dim(`(${p.route})`)}${extra}`);
1962
+ }
1963
+ if (state.sharedComponents.length > 0) {
1964
+ console.log();
1965
+ console.log(chalk.bold('Shared Components:'));
1966
+ for (const sc of state.sharedComponents) {
1967
+ console.log(` ${chalk.cyan(sc.name)} ${chalk.dim(`— from ${sc.extractedFromPage}, used in: ${sc.usedInPages.join(', ')}`)}`);
1968
+ }
1969
+ }
1970
+ console.log();
1971
+ }
1972
+ // ─── Migration Command Dispatcher ───────────────────────────────────
1973
+ function handleMigrateCommand(root, subArg) {
1974
+ // No subcommand or explicit empty → run initial survey
1975
+ if (!subArg) {
1976
+ const migState = readMigrationState(root);
1977
+ if (migState?.status === 'in-progress') {
1978
+ // Already in progress — show status and resume hint
1979
+ printMigrationStatus(root);
1980
+ const page = migState.pages[migState.currentPageIndex];
1981
+ if (page) {
1982
+ const editorState = readState(root);
1983
+ const currentStep = editorState?.migration ? editorState.step : 1;
1984
+ console.log(chalk.green('Continue with: ') +
1985
+ chalk.bold(`codeyam editor migrate ${currentStep}`));
1986
+ console.log();
1987
+ }
1988
+ return;
1989
+ }
1990
+ if (migState?.status === 'complete') {
1991
+ console.log(chalk.green('Migration is complete! Use the normal feature workflow: `codeyam editor steps`'));
1992
+ return;
1993
+ }
1994
+ printMigrateSurvey(root);
1995
+ return;
1996
+ }
1997
+ // Parse subcommand
1998
+ if (subArg === 'next') {
1999
+ const result = advanceToNextPage(root);
2000
+ if (!result) {
2001
+ const migState = readMigrationState(root);
2002
+ if (migState?.status === 'complete') {
2003
+ console.log();
2004
+ console.log(chalk.bold.green('🎉 Migration complete!'));
2005
+ console.log();
2006
+ const done = migState.pages.filter((p) => p.status === 'complete');
2007
+ const totalComponents = done.reduce((sum, p) => sum + p.extractedComponents.length, 0);
2008
+ const totalFunctions = done.reduce((sum, p) => sum + p.extractedFunctions.length, 0);
2009
+ const totalScenarios = done.reduce((sum, p) => sum + p.scenarioCount, 0);
2010
+ console.log(` Pages migrated: ${done.length}`);
2011
+ console.log(` Components extracted: ${totalComponents}`);
2012
+ console.log(` Functions extracted: ${totalFunctions}`);
2013
+ console.log(` Scenarios created: ${totalScenarios}`);
2014
+ console.log(` Shared components: ${migState.sharedComponents.length}`);
2015
+ console.log();
2016
+ console.log(chalk.green('The project is fully migrated. Start building features: `codeyam editor steps`'));
2017
+ console.log();
2018
+ }
2019
+ else {
2020
+ console.log(chalk.red('No pending pages found. Run `codeyam editor migrate status` to check progress.'));
2021
+ }
2022
+ return;
2023
+ }
2024
+ // Start M1 for the next page
2025
+ printMigrateStep1(root);
2026
+ return;
2027
+ }
2028
+ if (subArg === 'status') {
2029
+ printMigrationStatus(root);
2030
+ return;
2031
+ }
2032
+ if (subArg === 'complete') {
2033
+ const migState = readMigrationState(root);
2034
+ if (!migState) {
2035
+ console.error(chalk.red('Error: No migration in progress.'));
2036
+ process.exit(1);
2037
+ }
2038
+ const page = migState.pages[migState.currentPageIndex];
2039
+ if (!page) {
2040
+ console.error(chalk.red('Error: No active migration page.'));
2041
+ process.exit(1);
2042
+ }
2043
+ completePage(root, {
2044
+ extractedComponents: page.extractedComponents,
2045
+ extractedFunctions: page.extractedFunctions,
2046
+ scenarioCount: page.scenarioCount,
2047
+ });
2048
+ console.log(chalk.green(`Page "${page.name}" marked complete.`));
2049
+ return;
2050
+ }
2051
+ // Numeric step: 1-8
2052
+ const step = parseInt(subArg, 10);
2053
+ if (isNaN(step) || step < 1 || step > 10) {
2054
+ console.error(chalk.red(`Error: Invalid migration step "${subArg}". Must be 1-10, "next", "complete", or "status".`));
2055
+ process.exit(1);
2056
+ }
2057
+ // Ensure migration is active and first page started
2058
+ const migState = readMigrationState(root);
2059
+ if (!migState) {
2060
+ console.error(chalk.red('Error: No migration state. Run `codeyam editor migrate` first.'));
2061
+ process.exit(1);
2062
+ }
2063
+ // If still in survey state and step 1 requested, start first page
2064
+ if (migState.status === 'surveyed' && step === 1) {
2065
+ migState.status = 'in-progress';
2066
+ migState.pages[0].status = 'in-progress';
2067
+ migState.pages[0].startedAt = new Date().toISOString();
2068
+ writeMigrationState(root, migState);
2069
+ }
2070
+ const stepFns = {
2071
+ 1: printMigrateStep1,
2072
+ 2: printMigrateStep2,
2073
+ 3: printMigrateStep3,
2074
+ 4: printMigrateStep4,
2075
+ 5: printMigrateStep5,
2076
+ 6: printMigrateStep6,
2077
+ 7: printMigrateStep7,
2078
+ 8: printMigrateStep8,
2079
+ 9: printMigrateStep9,
2080
+ 10: printMigrateStep10,
2081
+ };
2082
+ stepFns[step](root);
2083
+ }
2084
+ // ─── Step 14: Commit ─────────────────────────────────────────────────
2085
+ function printStep14(root, feature) {
1093
2086
  const prevState = readState(root);
1094
- const isResuming = prevState?.step === 11;
2087
+ const isResuming = prevState?.step === 14;
1095
2088
  const now = new Date().toISOString();
1096
2089
  writeState(root, {
1097
2090
  feature,
1098
- step: 11,
1099
- label: STEP_LABELS[11],
2091
+ step: 14,
2092
+ label: STEP_LABELS[14],
1100
2093
  startedAt: isResuming ? prevState.startedAt : now,
1101
2094
  featureStartedAt: prevState?.featureStartedAt || now,
1102
2095
  });
1103
- logEvent(root, 'step', { step: 11, label: 'Journal', feature });
1104
- stepHeader(11, 'Journal', feature);
2096
+ logEvent(root, 'step', { step: 14, label: 'Commit', feature });
2097
+ stepHeader(14, 'Commit', feature);
1105
2098
  if (isResuming) {
1106
- printResumptionHeader(11);
2099
+ printResumptionHeader(14);
1107
2100
  }
1108
- console.log('Create or update the journal entry for this feature.');
2101
+ console.log('Commit all changes for this feature.');
1109
2102
  console.log();
1110
2103
  console.log(chalk.bold('Checklist:'));
1111
- checkbox('Write a concise description of what was built (2-3 sentences)');
1112
- checkbox(`First time at step 11 create journal entry with ALL session screenshots:`);
1113
- console.log(chalk.dim(` codeyam editor journal '{"title":"...","type":"feature","description":"...","includeSessionScenarios":true}'`));
1114
- console.log(chalk.dim(' includeSessionScenarios auto-discovers component + app + user persona screenshots'));
1115
- checkbox(`Returning to step 11 (before commit) — update the existing journal entry:`);
1116
- console.log(chalk.dim(` codeyam editor journal-update '{"time":"<journal entry time>","description":"<updated>","includeSessionScenarios":true}'`));
1117
- console.log(chalk.dim(' Note: PATCH only works before the entry is committed. After commit, use POST to create a new entry.'));
1118
- console.log();
1119
- console.log(chalk.dim('Focus on creating or updating the journal entry. Summary and presentation happen in step 13.'));
1120
- stopGate(11);
2104
+ checkbox(`Hide the results panel: \`codeyam editor hide-results\``);
2105
+ checkbox(`Git commit using the journal description: \`codeyam editor commit '{"message":"feat: <title>\\n\\n<journal description>"}'\``);
2106
+ console.log(chalk.dim(' The commit message body MUST match the journal description exactly'));
2107
+ stopGate(14);
1121
2108
  }
1122
- // ─── Step 12: Review ──────────────────────────────────────────────────
1123
- function printStep12(root, feature) {
1124
- const port = getServerPort();
2109
+ // ─── Step 15: Finalize ───────────────────────────────────────────────
2110
+ function printStep15(root, feature) {
1125
2111
  const prevState = readState(root);
1126
- const isResuming = prevState?.step === 12;
2112
+ const isResuming = prevState?.step === 15;
1127
2113
  const now = new Date().toISOString();
1128
2114
  writeState(root, {
1129
2115
  feature,
1130
- step: 12,
1131
- label: STEP_LABELS[12],
2116
+ step: 15,
2117
+ label: STEP_LABELS[15],
1132
2118
  startedAt: isResuming ? prevState.startedAt : now,
1133
2119
  featureStartedAt: prevState?.featureStartedAt || now,
1134
2120
  });
1135
- logEvent(root, 'step', { step: 12, label: 'Review', feature });
1136
- stepHeader(12, 'Review', feature);
2121
+ logEvent(root, 'step', { step: 15, label: 'Finalize', feature });
2122
+ stepHeader(15, 'Finalize', feature);
1137
2123
  if (isResuming) {
1138
- printResumptionHeader(12);
2124
+ printResumptionHeader(15);
1139
2125
  }
1140
- console.log('Verify all screenshots and checks pass before presenting to the user.');
2126
+ console.log('Update the journal with the commit SHA and amend the commit.');
1141
2127
  console.log();
1142
- console.log(chalk.bold('Checklist (do all of this silently):'));
1143
- checkbox(`Refresh the preview: \`codeyam editor preview\``);
1144
- checkbox('Verify each component has screenshots in the App tab (grouped under Components)');
1145
- checkbox('If any are missing, re-register them using `codeyam editor register`');
1146
- checkbox(`Check for client errors: \`codeyam editor client-errors\``);
1147
- checkbox('If `hasErrors` is true, fix them and re-capture affected scenarios');
1148
- checkbox('Run `codeyam editor verify-images \'{"paths":["/"], "imageUrls":["url1"]}\'` with all page paths and image URLs');
1149
- checkbox('Fix or remove any broken images before continuing');
1150
- checkbox('Run `codeyam editor audit` to verify completeness of scenarios and tests');
1151
- checkbox('Do not proceed until all checks pass');
1152
- stopGate(12);
2128
+ console.log(chalk.bold('Checklist:'));
2129
+ checkbox(`Update journal with commit SHA: \`codeyam editor journal-update '{"time":"<journal entry time>","commitSha":"<sha>","commitMessage":"feat: <title>"}'\``);
2130
+ checkbox('Amend the commit to include the journal update: `git add .codeyam/journal/ && git commit --amend --no-edit`');
2131
+ console.log(chalk.dim(' The journal-update modifies journal files after the commit amend to keep the tree clean.'));
2132
+ stopGate(15);
1153
2133
  }
1154
- // ─── Step 13: Present ─────────────────────────────────────────────────
1155
- function printStep13(root, feature) {
1156
- const port = getServerPort();
2134
+ // ─── Step 16: Push ───────────────────────────────────────────────────
2135
+ function printStep16(root, feature) {
1157
2136
  const prevState = readState(root);
1158
- const isResuming = prevState?.step === 13;
2137
+ const isResuming = prevState?.step === 16;
1159
2138
  const now = new Date().toISOString();
1160
2139
  writeState(root, {
1161
2140
  feature,
1162
- step: 13,
1163
- label: STEP_LABELS[13],
2141
+ step: 16,
2142
+ label: STEP_LABELS[16],
1164
2143
  startedAt: isResuming ? prevState.startedAt : now,
1165
2144
  featureStartedAt: prevState?.featureStartedAt || now,
1166
2145
  });
1167
- logEvent(root, 'step', { step: 13, label: 'Present', feature });
1168
- stepHeader(13, 'Present', feature);
2146
+ logEvent(root, 'step', { step: 16, label: 'Push', feature });
2147
+ stepHeader(16, 'Push', feature);
1169
2148
  if (isResuming) {
1170
- printResumptionHeader(13);
2149
+ printResumptionHeader(16);
1171
2150
  }
1172
- console.log('Present the results to the user and get their approval.');
2151
+ console.log('Push the commit to the remote repository.');
1173
2152
  console.log();
1174
2153
  console.log(chalk.bold('Checklist:'));
1175
- checkbox('Fetch all scenarios: `codeyam editor scenarios`');
1176
- console.log(chalk.dim(' Each scenario has a `changeStatus` field: "new", "edited", "impacted", or null.'));
1177
- checkbox('Pick the best scenario to showcase from this working session:');
1178
- console.log(chalk.dim(' Prefer scenarios with changeStatus "new" or "edited" (directly changed in this session).'));
1179
- console.log(chalk.dim(' For app-level scenarios, prefer those showing the new/changed functionality.'));
1180
- console.log(chalk.dim(' NEVER pick a scenario with changeStatus null those are unchanged from a previous commit.'));
1181
- checkbox('Switch the preview to that scenario using its `id`:');
1182
- console.log(chalk.dim(` codeyam editor preview '{"scenarioId":"<id>"}'`));
1183
- checkbox(`Show the results panel: \`codeyam editor show-results\``);
1184
- console.log(chalk.dim(' This opens a visual panel below the terminal showing all scenarios with screenshots.'));
1185
- console.log(chalk.dim(' The user can click scenarios to switch the live preview.'));
1186
- checkbox('Write a 1-2 sentence summary of what was built');
1187
- checkbox('Report test count and audit status (one line)');
1188
- console.log();
1189
- console.log(chalk.bold('Present a selection menu to the user (use AskUserQuestion with these EXACT option labels):'));
1190
- console.log(chalk.green(' Option 1 label: "Save & commit"') +
1191
- chalk.dim(' — git commit all changes and record in journal'));
1192
- console.log(chalk.yellow(' Option 2 label: "I\'d like to make some changes"') +
1193
- chalk.dim(' — describe changes, then re-verify'));
1194
- console.log();
1195
- console.log(chalk.bold('If the user chooses "Save & commit":'));
1196
- checkbox(`Hide the results panel first: \`codeyam editor hide-results\``);
1197
- checkbox(`Git commit using the journal description: \`codeyam editor commit '{"message":"feat: <title>\\n\\n<journal description>"}'\``);
1198
- console.log(chalk.dim(' The commit message body MUST match the journal description exactly'));
1199
- checkbox(`Update journal with commit SHA: \`codeyam editor journal-update '{"time":"<journal entry time>","commitSha":"<sha>","commitMessage":"feat: <title>"}'\``);
1200
- console.log(chalk.green(' Then run: ') +
1201
- chalk.bold('codeyam editor steps') +
1202
- chalk.green(' to start the next feature'));
2154
+ checkbox('Check if a git remote is configured: `git remote -v`');
2155
+ checkbox("Offer to push to remote (AskUserQuestion STOP and wait for the user's answer before proceeding):");
2156
+ console.log(chalk.dim(' If a remote exists, ask (AskUserQuestion): "Would you like me to push this commit to the remote?" with options "Yes, push" and "No, skip"'));
2157
+ console.log(chalk.dim(' If the user says yes, run: `git push`'));
2158
+ console.log(chalk.dim(' If NO remote exists, ask (AskUserQuestion): "This project doesn\'t have a git remote yet. Would you like help setting one up? I can walk you through creating a GitHub repository." with options "Yes, set up remote" and "No, skip"'));
2159
+ console.log(chalk.dim(' If the user wants help, guide them through `gh repo create` or manual GitHub setup, then push.'));
2160
+ checkbox('After the user responds, run: `codeyam editor steps` to start the next feature');
1203
2161
  console.log();
1204
2162
  console.log(chalk.red.bold(' If the user reports a bug or requests a fix after committing:'));
1205
2163
  console.log(chalk.red.bold(' You MUST still run `codeyam editor change` before making any changes.'));
1206
2164
  console.log(chalk.red.bold(' The change workflow applies to ALL changes — post-commit fixes are not an exception.'));
1207
- console.log();
1208
- console.log(chalk.bold('If the user chooses "Make changes" (or asks for ANY change, even as a question):'));
1209
- checkbox(`Hide the results panel: \`codeyam editor hide-results\``);
1210
- checkbox('Ask what changes the user wants (if not already clear)');
1211
- checkbox(`Run: \`codeyam editor change "${feature}"\` — this gives you the change checklist`);
1212
- checkbox('THEN make the requested changes and follow the checklist');
1213
- console.log(chalk.red.bold(' IMPORTANT: Always run the change command BEFORE writing any code.'));
1214
- stopGate(13, { confirm: true });
2165
+ stopGate(16, { confirm: true });
1215
2166
  }
1216
2167
  // ─── Command definition ───────────────────────────────────────────────
1217
2168
  // ─── Analyze-imports subcommand ────────────────────────────────────────
@@ -1221,37 +2172,78 @@ function printStep13(root, feature) {
1221
2172
  * Runs data-structure-only analysis for all glossary entities, then outputs
1222
2173
  * an import graph and entity data structures as JSON to stdout.
1223
2174
  */
1224
- async function handleAnalyzeImports() {
2175
+ async function handleAnalyzeImports(options = {}) {
1225
2176
  const root = getProjectRoot();
1226
2177
  // Read glossary
1227
2178
  const glossaryPath = path.join(root, '.codeyam', 'glossary.json');
1228
2179
  if (!fs.existsSync(glossaryPath)) {
2180
+ if (options.silent) {
2181
+ // Internal caller — glossary doesn't exist yet, nothing to analyze
2182
+ return;
2183
+ }
1229
2184
  console.error(chalk.red('Error: .codeyam/glossary.json not found.'));
1230
2185
  console.error(chalk.dim(' Run codeyam editor 6 to create the glossary first.'));
1231
2186
  process.exit(1);
1232
2187
  }
1233
2188
  let glossaryEntries;
1234
2189
  try {
1235
- glossaryEntries = JSON.parse(fs.readFileSync(glossaryPath, 'utf8'));
2190
+ const parsed = JSON.parse(fs.readFileSync(glossaryPath, 'utf8'));
2191
+ glossaryEntries = sanitizeGlossaryEntries(parsed);
1236
2192
  }
1237
2193
  catch {
1238
2194
  console.error(chalk.red('Error: Could not parse .codeyam/glossary.json.'));
1239
2195
  process.exit(1);
1240
2196
  }
1241
- if (!Array.isArray(glossaryEntries) || glossaryEntries.length === 0) {
2197
+ if (glossaryEntries.length === 0) {
1242
2198
  console.error(chalk.red('Error: glossary.json is empty.'));
1243
2199
  process.exit(1);
1244
2200
  }
1245
2201
  const filePaths = glossaryEntries.map((e) => e.filePath);
1246
- const entityNames = glossaryEntries.map((e) => e.name);
2202
+ // Include page files so pages get analyzed as entities too.
2203
+ // This allows the import graph to map page names to their dependencies.
2204
+ // Use allFiles (not map values) to include ALL pages — multiple routes
2205
+ // can share a page name (e.g., /feedback/[id] and /feedback/new both
2206
+ // map to "Feedback") but each needs its own entity analysis.
2207
+ const { scanPageFilePaths } = await import('../utils/entityChangeStatus.server.js');
2208
+ const { allFiles: allPageFiles } = scanPageFilePaths(root);
2209
+ for (const pageFile of allPageFiles) {
2210
+ if (!filePaths.includes(pageFile)) {
2211
+ filePaths.push(pageFile);
2212
+ }
2213
+ }
2214
+ // Include file paths from editor scenarios (both component_path and
2215
+ // page_file_path) so that all components and pages with scenarios get
2216
+ // entities — critical for non-Next.js apps where scanPageFilePaths
2217
+ // doesn't find page.tsx files.
2218
+ try {
2219
+ const { getDatabase } = await import('../../../packages/database/index.js');
2220
+ const db = getDatabase();
2221
+ const scenarioFiles = await db
2222
+ .selectFrom('editor_scenarios')
2223
+ .select(['component_path', 'page_file_path'])
2224
+ .distinct()
2225
+ .execute();
2226
+ for (const row of scenarioFiles) {
2227
+ const r = row;
2228
+ for (const fp of [r.component_path, r.page_file_path]) {
2229
+ if (fp && !filePaths.includes(fp)) {
2230
+ filePaths.push(fp);
2231
+ }
2232
+ }
2233
+ }
2234
+ }
2235
+ catch {
2236
+ // Non-fatal — scenario file paths just won't be included
2237
+ }
1247
2238
  const progress = new ProgressReporter();
1248
- // Run data-structure-only analysis for all entities
1249
- progress.start('Running import analysis for all glossary entities...');
2239
+ // Run data-structure-only analysis for all entities (glossary + pages)
2240
+ // Don't pass entityNames entities may not exist yet (fresh clone).
2241
+ // The analyzer will discover and create them from file paths.
2242
+ progress.start('Running import analysis for all entities...');
1250
2243
  try {
1251
2244
  await runAnalysisForEntities({
1252
2245
  projectRoot: root,
1253
2246
  filePaths,
1254
- entityNames,
1255
2247
  progress,
1256
2248
  onlyDataStructure: true,
1257
2249
  });
@@ -1269,7 +2261,9 @@ async function handleAnalyzeImports() {
1269
2261
  const entities = await loadEntities({});
1270
2262
  if (!entities || entities.length === 0) {
1271
2263
  progress.succeed('No entities found in database yet — skipping import analysis. This is normal on the first feature.');
1272
- console.log(JSON.stringify({ imports: {}, entities: {} }));
2264
+ if (!options.silent) {
2265
+ console.log(JSON.stringify({ imports: {}, entities: {} }));
2266
+ }
1273
2267
  return;
1274
2268
  }
1275
2269
  // Deduplicate to latest versions
@@ -1284,7 +2278,7 @@ async function handleAnalyzeImports() {
1284
2278
  latestByKey.set(key, entity);
1285
2279
  }
1286
2280
  }
1287
- const entityNameSet = new Set(entityNames);
2281
+ const entityNameSet = new Set(glossaryEntries.map((e) => e.name));
1288
2282
  const latestEntities = [...latestByKey.values()].filter((e) => entityNameSet.has(e.name));
1289
2283
  // Build import graph from importedExports metadata
1290
2284
  const imports = {};
@@ -1322,9 +2316,14 @@ async function handleAnalyzeImports() {
1322
2316
  };
1323
2317
  }
1324
2318
  progress.succeed('Done');
1325
- // Output combined JSON
1326
- const output = { imports, entities: entityData };
1327
- console.log(JSON.stringify(output, null, 2));
2319
+ // Output combined JSON (suppressed when called internally during startup)
2320
+ if (!options.silent) {
2321
+ const summary = formatApiSubcommandResult('analyze-imports', {
2322
+ imports,
2323
+ entities: entityData,
2324
+ });
2325
+ console.log(summary || JSON.stringify({ imports, entities: entityData }));
2326
+ }
1328
2327
  }
1329
2328
  // ─── Validate-seed subcommand ─────────────────────────────────────────
1330
2329
  /**
@@ -1374,7 +2373,7 @@ function handleValidateSeed(jsonArg) {
1374
2373
  }
1375
2374
  // ─── Isolate subcommand ───────────────────────────────────────────────
1376
2375
  /**
1377
- * `codeyam editor isolate StarRating CategoryBadge DrinkCard`
2376
+ * `codeyam editor isolate "StarRating CategoryBadge DrinkCard"`
1378
2377
  *
1379
2378
  * Creates isolation route directories and the layout guard file.
1380
2379
  * This avoids brace-expansion permission prompts in the embedded terminal.
@@ -1382,10 +2381,10 @@ function handleValidateSeed(jsonArg) {
1382
2381
  function handleIsolate(componentNames) {
1383
2382
  const root = process.cwd();
1384
2383
  if (componentNames.length === 0) {
1385
- console.error(chalk.red('Usage: codeyam editor isolate ComponentA ComponentB ...'));
2384
+ console.error(chalk.red('Usage: codeyam editor isolate "ComponentA ComponentB ..."'));
1386
2385
  process.exit(1);
1387
2386
  }
1388
- const isolateDir = path.join(root, 'app', 'codeyam-isolate');
2387
+ const isolateDir = path.join(root, 'app', 'isolated-components');
1389
2388
  // Create layout.tsx with production guard if missing
1390
2389
  const layoutPath = path.join(isolateDir, 'layout.tsx');
1391
2390
  if (!fs.existsSync(layoutPath)) {
@@ -1399,7 +2398,7 @@ function handleIsolate(componentNames) {
1399
2398
  '}',
1400
2399
  '',
1401
2400
  ].join('\n'), 'utf8');
1402
- console.log(chalk.green(`Created layout guard: app/codeyam-isolate/layout.tsx`));
2401
+ console.log(chalk.green(`Created layout guard: app/isolated-components/layout.tsx`));
1403
2402
  }
1404
2403
  // Create a directory for each component
1405
2404
  const created = [];
@@ -1473,12 +2472,53 @@ function formatApiSubcommandResult(subcommand, data) {
1473
2472
  }
1474
2473
  case 'dev-server': {
1475
2474
  if (data.status) {
1476
- return `status=${data.status} pid=${data.pid || 'none'} port=${data.port || 'none'}`;
2475
+ const parts = [`status=${data.status}`, `pid=${data.pid || 'none'}`];
2476
+ if (data.url)
2477
+ parts.push(`url=${data.url}`);
2478
+ if (data.proxyUrl)
2479
+ parts.push(`proxyUrl=${data.proxyUrl}`);
2480
+ if (data.errorMessage)
2481
+ parts.push(`error: ${data.errorMessage}`);
2482
+ return parts.join(' ');
1477
2483
  }
1478
2484
  return null;
1479
2485
  }
2486
+ case 'client-errors': {
2487
+ const parts = [`hasErrors=${data.hasErrors}`];
2488
+ parts.push(`totalErrors=${data.totalErrors ?? 0}`);
2489
+ if (data.livePreview) {
2490
+ const lp = data.livePreview;
2491
+ parts.push(`livePreview=${lp.loaded ? 'loaded' : 'not-loaded'}`);
2492
+ if (lp.loaded) {
2493
+ parts.push(`hasContent=${lp.hasContent}`);
2494
+ }
2495
+ const liveErrors = lp.errors?.length ?? 0;
2496
+ parts.push(`liveErrors=${liveErrors}`);
2497
+ if (liveErrors > 0) {
2498
+ // Show first few error messages for context
2499
+ for (const err of lp.errors.slice(0, 3)) {
2500
+ parts.push(` error: ${err.message}`);
2501
+ }
2502
+ }
2503
+ }
2504
+ else {
2505
+ parts.push('livePreview=not-loaded');
2506
+ }
2507
+ return parts.join(' ');
2508
+ }
2509
+ case 'analyze-imports': {
2510
+ const importEntries = Object.entries(data.imports || {});
2511
+ const entityEntries = Object.entries(data.entities || {});
2512
+ const parts = [];
2513
+ parts.push(`entities=${entityEntries.length}`);
2514
+ parts.push(`withImports=${importEntries.length}`);
2515
+ if (importEntries.length > 0) {
2516
+ parts.push(`imports: ${importEntries.map(([name, deps]) => `${name}→[${deps.join(',')}]`).join(' ')}`);
2517
+ }
2518
+ return parts.join(' ');
2519
+ }
1480
2520
  default:
1481
- return null; // journal-list, client-errors, show/hide-results: keep full JSON
2521
+ return null; // journal-list, show/hide-results: keep full JSON
1482
2522
  }
1483
2523
  }
1484
2524
  /**
@@ -1489,8 +2529,9 @@ function formatApiSubcommandResult(subcommand, data) {
1489
2529
  */
1490
2530
  async function handleRegister(jsonArg) {
1491
2531
  if (!jsonArg) {
2532
+ const { defaultName: dim } = getProjectDimensions(getProjectRoot());
1492
2533
  console.error(chalk.red('Error: JSON argument required.'));
1493
- console.error(chalk.dim(' Usage: codeyam editor register \'{"name":"DrinkCard - Default","componentName":"DrinkCard","url":"/isolated-components/DrinkCard?s=Default"}\''));
2534
+ console.error(chalk.dim(` Usage: codeyam editor register '{"name":"DrinkCard - Default","componentName":"DrinkCard","url":"/isolated-components/DrinkCard?s=Default","dimensions":["${dim}"]}'`));
1494
2535
  console.error(chalk.dim(' For large payloads: codeyam editor register @/tmp/scenario.json'));
1495
2536
  process.exit(1);
1496
2537
  }
@@ -1502,52 +2543,74 @@ async function handleRegister(jsonArg) {
1502
2543
  }
1503
2544
  process.exit(1);
1504
2545
  }
1505
- const body = parsed.body;
2546
+ // Normalize to array for uniform handling
2547
+ const items = Array.isArray(parsed.body) ? parsed.body : [parsed.body];
1506
2548
  const port = getServerPort();
1507
2549
  const url = `http://localhost:${port}/api/editor-register-scenario`;
1508
- try {
1509
- const res = await fetch(url, {
1510
- method: 'POST',
1511
- headers: { 'Content-Type': 'application/json' },
1512
- body: JSON.stringify(body),
1513
- });
1514
- const data = await res.json();
1515
- // Print concise summary instead of raw JSON so Claude doesn't need python3
1516
- const parts = [];
1517
- parts.push(`success=${data.success}`);
1518
- if (data.scenario?.name)
1519
- parts.push(`name="${data.scenario.name}"`);
1520
- parts.push(`screenshot=${!!data.screenshotCaptured}`);
1521
- if (data.clientErrors?.length > 0) {
1522
- parts.push(chalk.red(`errors=${data.clientErrors.length}`));
1523
- }
1524
- else {
1525
- parts.push(`errors=0`);
1526
- }
1527
- if (data.seedResult)
1528
- parts.push(`seed=${data.seedResult.success}`);
1529
- if (data.captureError)
1530
- parts.push(chalk.yellow(`captureError="${data.captureError}"`));
1531
- console.log(parts.join(' '));
1532
- // Surface client errors prominently so they can't be missed
1533
- if (data.clientErrors && data.clientErrors.length > 0) {
1534
- console.log(chalk.red.bold(`⚠ WARNING: ${data.clientErrors.length} client error${data.clientErrors.length !== 1 ? 's' : ''} detected during capture:`));
1535
- for (const err of data.clientErrors) {
1536
- console.log(chalk.red(` → ${err}`));
2550
+ const isBatch = items.length > 1;
2551
+ let succeeded = 0;
2552
+ let failed = 0;
2553
+ for (let i = 0; i < items.length; i++) {
2554
+ const body = items[i];
2555
+ const prefix = isBatch ? chalk.dim(`[${i + 1}/${items.length}] `) : '';
2556
+ try {
2557
+ const res = await fetch(url, {
2558
+ method: 'POST',
2559
+ headers: { 'Content-Type': 'application/json' },
2560
+ body: JSON.stringify(body),
2561
+ });
2562
+ const data = await res.json();
2563
+ // Print concise summary instead of raw JSON so Claude doesn't need python3
2564
+ const parts = [];
2565
+ parts.push(`success=${data.success}`);
2566
+ if (data.scenario?.name)
2567
+ parts.push(`name="${data.scenario.name}"`);
2568
+ parts.push(`screenshot=${!!data.screenshotCaptured}`);
2569
+ if (data.capturedViewport) {
2570
+ parts.push(`viewport=${data.capturedViewport.width}×${data.capturedViewport.height}`);
2571
+ }
2572
+ if (data.scenario?.dimension)
2573
+ parts.push(`dimension="${data.scenario.dimension}"`);
2574
+ if (data.clientErrors?.length > 0) {
2575
+ parts.push(chalk.red(`errors=${data.clientErrors.length}`));
2576
+ }
2577
+ else {
2578
+ parts.push(`errors=0`);
2579
+ }
2580
+ if (data.seedResult)
2581
+ parts.push(`seed=${data.seedResult.success}`);
2582
+ if (data.captureError)
2583
+ parts.push(chalk.yellow(`captureError="${data.captureError}"`));
2584
+ console.log(prefix + parts.join(' '));
2585
+ // Surface client errors prominently so they can't be missed
2586
+ if (data.clientErrors && data.clientErrors.length > 0) {
2587
+ console.log(chalk.red.bold(`⚠ WARNING: ${data.clientErrors.length} client error${data.clientErrors.length !== 1 ? 's' : ''} detected during capture:`));
2588
+ for (const err of data.clientErrors) {
2589
+ console.log(chalk.red(` → ${err}`));
2590
+ }
2591
+ console.log(chalk.yellow(' The screenshot may show an error screen instead of the actual component.'));
2592
+ console.log(chalk.yellow(' Fix the issue and re-register this scenario.'));
2593
+ }
2594
+ if (!res.ok) {
2595
+ console.error(chalk.dim(JSON.stringify(data, null, 2)));
2596
+ failed++;
2597
+ }
2598
+ else {
2599
+ succeeded++;
1537
2600
  }
1538
- console.log(chalk.yellow(' The screenshot may show an error screen instead of the actual component.'));
1539
- console.log(chalk.yellow(' Fix the issue and re-register this scenario.'));
1540
2601
  }
1541
- if (!res.ok) {
1542
- console.error(chalk.dim(JSON.stringify(data, null, 2)));
1543
- process.exit(1);
2602
+ catch (error) {
2603
+ const msg = error instanceof Error ? error.message : String(error);
2604
+ console.error(prefix + chalk.red(`Error: Could not reach editor server at ${url}`));
2605
+ console.error(chalk.dim(` ${msg}`));
2606
+ console.error(chalk.dim(' Is the editor running? Start it with: codeyam editor'));
2607
+ failed++;
1544
2608
  }
1545
2609
  }
1546
- catch (error) {
1547
- const msg = error instanceof Error ? error.message : String(error);
1548
- console.error(chalk.red(`Error: Could not reach editor server at ${url}`));
1549
- console.error(chalk.dim(` ${msg}`));
1550
- console.error(chalk.dim(' Is the editor running? Start it with: codeyam editor'));
2610
+ if (isBatch) {
2611
+ console.log(chalk.dim(`\nBatch complete: ${succeeded}/${items.length} succeeded`));
2612
+ }
2613
+ if (failed > 0) {
1551
2614
  process.exit(1);
1552
2615
  }
1553
2616
  }
@@ -1568,11 +2631,23 @@ async function handleDependents(entityName) {
1568
2631
  const progress = new ProgressReporter();
1569
2632
  progress.start('Loading entities from database...');
1570
2633
  await initializeEnvironment();
1571
- const allEntities = await loadEntities({});
2634
+ let allEntities = await loadEntities({});
1572
2635
  if (!allEntities || allEntities.length === 0) {
1573
- progress.fail('No entities found in database');
1574
- console.error(chalk.dim(' Run codeyam editor analyze-imports first to populate entity data.'));
1575
- process.exit(1);
2636
+ progress.succeed('No entities found running analyze-imports first...');
2637
+ try {
2638
+ await handleAnalyzeImports({ silent: true });
2639
+ }
2640
+ catch {
2641
+ console.error(chalk.red('Could not build import graph. Run `codeyam editor analyze-imports` manually.'));
2642
+ process.exit(1);
2643
+ }
2644
+ // Reload entities after analysis
2645
+ const reloaded = await loadEntities({});
2646
+ if (!reloaded || reloaded.length === 0) {
2647
+ progress.fail('No entities found even after analyze-imports');
2648
+ process.exit(1);
2649
+ }
2650
+ allEntities = reloaded;
1576
2651
  }
1577
2652
  // Find the target entity by name (case-insensitive match)
1578
2653
  const targetEntities = allEntities.filter((e) => e.name.toLowerCase() === entityName.toLowerCase());
@@ -1659,15 +2734,16 @@ async function handleDependents(entityName) {
1659
2734
  * `codeyam editor change <feature>`
1660
2735
  *
1661
2736
  * Prints a condensed post-change checklist that guides Claude through
1662
- * re-verifying after user-requested modifications. This is the "change
1663
- * loop" it replaces the freeform "make changes" path with structured
1664
- * instructions that always loop back to step 13 present.
2737
+ * re-verifying after user-requested modifications. When called from
2738
+ * step 13+, this loops back to step 13 (present). When called from an
2739
+ * earlier step, it returns to that step so the normal flow continues.
1665
2740
  */
1666
2741
  function handleChange(feature) {
1667
2742
  const root = getProjectRoot();
2743
+ const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
2744
+ const state = readState(root);
1668
2745
  if (!feature) {
1669
2746
  // Try to read feature from state
1670
- const state = readState(root);
1671
2747
  if (state?.feature) {
1672
2748
  feature = state.feature;
1673
2749
  }
@@ -1677,6 +2753,7 @@ function handleChange(feature) {
1677
2753
  process.exit(1);
1678
2754
  }
1679
2755
  }
2756
+ const currentStep = state?.step ?? 13;
1680
2757
  const port = getServerPort();
1681
2758
  console.log();
1682
2759
  console.log(chalk.bold.cyan('━━━ Change Loop ━━━'));
@@ -1684,9 +2761,19 @@ function handleChange(feature) {
1684
2761
  console.log();
1685
2762
  console.log('The user has requested changes. Follow this checklist after making them.');
1686
2763
  console.log();
2764
+ const designSystem = readDesignSystem(root);
2765
+ if (designSystem) {
2766
+ console.log(chalk.bold.magenta('Design System (active):'));
2767
+ console.log(chalk.magenta(' Use existing CSS custom property tokens when making visual changes — no hardcoded px or hex values.'));
2768
+ console.log(chalk.magenta(' Use var(--text-sm) not 14, var(--spacing-lg) not 16px, var(--text-primary) not #1A1B25.'));
2769
+ console.log();
2770
+ console.log(designSystem);
2771
+ console.log();
2772
+ }
1687
2773
  console.log(chalk.bold.cyan('Keep the preview moving:'));
1688
2774
  console.log(chalk.cyan(` Refresh after EACH individual change — not after all changes are done:`));
1689
- console.log(chalk.cyan(` codeyam editor preview`));
2775
+ console.log(chalk.cyan(` codeyam editor preview '{"dimension":"${dim}"}'`));
2776
+ printDimensionGuidance(dim, dimNames);
1690
2777
  console.log(chalk.cyan(' The user is watching the preview. Let them see progress incrementally.'));
1691
2778
  console.log();
1692
2779
  console.log(chalk.bold('0. Close the results panel:'));
@@ -1694,7 +2781,7 @@ function handleChange(feature) {
1694
2781
  console.log();
1695
2782
  console.log(chalk.bold('1. Re-register affected component scenarios:'));
1696
2783
  checkbox('For each component you modified, re-register ALL its scenarios');
1697
- console.log(chalk.dim(` codeyam editor register '{"name":"ComponentName - ScenarioName","componentName":"ComponentName","componentPath":"app/components/ComponentName.tsx","url":"/isolated-components/ComponentName?s=ScenarioName"}'`));
2784
+ console.log(chalk.dim(` codeyam editor register '{"name":"ComponentName - ScenarioName","componentName":"ComponentName","componentPath":"app/components/ComponentName.tsx","url":"/isolated-components/ComponentName?s=ScenarioName","dimensions":["${dim}"]}'`));
1698
2785
  checkbox('For any NEW components: `codeyam editor isolate NewComponent` then create isolation routes and register scenarios');
1699
2786
  console.log();
1700
2787
  console.log(chalk.bold('2. Re-run affected tests:'));
@@ -1705,16 +2792,29 @@ function handleChange(feature) {
1705
2792
  console.log(chalk.dim(` codeyam editor dev-server '{"action":"restart"}'`));
1706
2793
  console.log();
1707
2794
  console.log(chalk.bold('3. Re-capture app-level scenarios:'));
1708
- checkbox('Run `codeyam editor analyze-imports` to refresh the import graph after code changes');
1709
- checkbox('If component changes affect app-level pages, re-register affected app scenarios');
1710
- checkbox(`Use \`codeyam editor dependents ComponentName\` to find what needs recapturing`);
2795
+ checkbox('For each changed component, run `codeyam editor dependents ComponentName` to find affected pages');
2796
+ checkbox('Run `codeyam editor scenarios` to list all existing scenarios');
2797
+ checkbox('For EACH page listed by dependents: find its existing scenarios in the list and re-register every one');
2798
+ console.log(chalk.dim(' Shared components (headers, navbars, layouts) affect MANY pages — re-register all of them.'));
2799
+ console.log(chalk.dim(' Re-register with the SAME name to update — do NOT create new/duplicate scenarios.'));
2800
+ console.log(chalk.dim(' Example: if Header changed and Home, Catalog, Detail pages use it,'));
2801
+ console.log(chalk.dim(' re-register "Home - Default", "Catalog - Full", "Detail - WithReviews", etc.'));
2802
+ checkbox("Enrich existing scenario data to exercise the change — don't just re-register unchanged data");
2803
+ console.log(chalk.dim(' Add data that demonstrates what changed: new fields, relationships, states, content variety.'));
2804
+ checkbox('After each re-registration, view the screenshot to verify data is visible');
2805
+ console.log(chalk.dim(" If the screenshot doesn't show the data you put in, the scenario is broken."));
1711
2806
  console.log();
1712
2807
  console.log(chalk.bold('4. Verify completeness:'));
1713
- checkbox(`Refresh the preview: \`codeyam editor preview\``);
1714
- checkbox(`Navigate to key pages to verify changes: \`codeyam editor preview '{"path":"/your-route"}'\``);
2808
+ checkbox(`Refresh the preview: \`codeyam editor preview '{"dimension":"${dim}"}'\``);
2809
+ checkbox(`Navigate to key pages to verify changes: \`codeyam editor preview '{"path":"/your-route","dimension":"${dim}"}'\``);
2810
+ printDimensionGuidance(dim, dimNames);
2811
+ checkbox('Run `codeyam editor scenario-coverage` — all affected scenarios must be fresh');
1715
2812
  checkbox('Run `codeyam editor audit` — all checks must pass');
1716
2813
  checkbox(`Check for client-side errors: \`codeyam editor client-errors\``);
2814
+ console.log(chalk.dim(' If `hasContent=false`, the preview is blank — fix the code before proceeding.'));
2815
+ console.log(chalk.dim(' If `liveErrors>0`, there are JS errors in the preview — fix them.'));
1717
2816
  checkbox('Fix any errors, then re-register affected scenarios');
2817
+ checkbox('If changes affect setup, new dependencies, or new scripts: update `README.md` and `npm run setup`');
1718
2818
  console.log();
1719
2819
  console.log(chalk.bold('5. Update the existing journal entry:'));
1720
2820
  checkbox('Get existing entry: `codeyam editor journal-list`');
@@ -1723,14 +2823,46 @@ function handleChange(feature) {
1723
2823
  console.log(chalk.dim(' Always update the existing uncommitted entry — do NOT create a new one.'));
1724
2824
  console.log(chalk.dim(' Only create a new entry (POST) if no uncommitted entry exists for this feature.'));
1725
2825
  console.log();
1726
- console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
1727
- console.log(chalk.red.bold(' REQUIRED: Show Results'));
1728
- console.log(chalk.red.bold(' When all checks pass, you MUST run: ') +
1729
- chalk.bold(`codeyam editor 13`));
1730
- console.log(chalk.red.bold(' The user ALWAYS expects to see results after changes. DO NOT skip this step.'));
1731
- console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
2826
+ // If the change was initiated from a step before 13, return to that step
2827
+ // instead of jumping to step 13. The change workflow should only loop to
2828
+ // step 13 when changes are requested FROM step 13.
2829
+ if (currentStep < 13) {
2830
+ console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
2831
+ console.log(chalk.red.bold(' REQUIRED: Return to current step'));
2832
+ console.log(chalk.red.bold(' When all checks pass, you MUST run: ') +
2833
+ chalk.bold(`codeyam editor ${currentStep}`));
2834
+ console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
2835
+ }
2836
+ else {
2837
+ console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
2838
+ console.log(chalk.red.bold(' REQUIRED: Show Results'));
2839
+ console.log(chalk.red.bold(' When all checks pass, you MUST run: ') +
2840
+ chalk.bold(`codeyam editor 13`));
2841
+ console.log(chalk.red.bold(' The user ALWAYS expects to see results after changes. DO NOT skip this step.'));
2842
+ console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
2843
+ }
1732
2844
  console.log();
1733
2845
  }
2846
+ // ─── Audit gate ─────────────────────────────────────────────────────
2847
+ /**
2848
+ * Silently check whether the audit passes. Returns true if allPassing,
2849
+ * false if any issues remain or the server is unreachable.
2850
+ * Used as a hard gate before steps 8+.
2851
+ */
2852
+ async function checkAuditGate() {
2853
+ const port = getServerPort();
2854
+ try {
2855
+ const res = await fetch(`http://localhost:${port}/api/editor-audit`);
2856
+ if (!res.ok)
2857
+ return false;
2858
+ const data = await res.json();
2859
+ return data?.summary?.allPassing === true;
2860
+ }
2861
+ catch {
2862
+ // Server not running or unreachable — can't verify, so don't block
2863
+ return true;
2864
+ }
2865
+ }
1734
2866
  // ─── Audit subcommand ────────────────────────────────────────────────
1735
2867
  /**
1736
2868
  * `codeyam editor audit`
@@ -1814,6 +2946,16 @@ async function handleAudit() {
1814
2946
  }
1815
2947
  console.log();
1816
2948
  }
2949
+ // Missing from glossary
2950
+ const missingFromGlossary = data.missingFromGlossary || [];
2951
+ if (missingFromGlossary.length > 0) {
2952
+ console.log(chalk.bold('Missing from glossary:'));
2953
+ for (const m of missingFromGlossary) {
2954
+ console.log(` ${chalk.red('✗')} ${m.name} ${chalk.dim(`(${m.filePath})`)} — ${m.scenarioCount} scenario${m.scenarioCount !== 1 ? 's' : ''} but not in glossary`);
2955
+ }
2956
+ console.log(chalk.yellow(' Add these to .codeyam/glossary.json and run `codeyam editor analyze-imports`'));
2957
+ console.log();
2958
+ }
1817
2959
  // Summary
1818
2960
  const allOk = summary.allPassing;
1819
2961
  if (allOk) {
@@ -1837,12 +2979,26 @@ async function handleAudit() {
1837
2979
  if (summary.functionsNameMismatch > 0) {
1838
2980
  parts.push(`${summary.functionsNameMismatch} function${summary.functionsNameMismatch !== 1 ? 's' : ''} with test name mismatch (missing top-level describe)`);
1839
2981
  }
2982
+ if (summary.missingFromGlossary > 0) {
2983
+ parts.push(`${summary.missingFromGlossary} file${summary.missingFromGlossary !== 1 ? 's' : ''} with scenarios not in glossary`);
2984
+ }
1840
2985
  console.log(chalk.red.bold('Audit failed: ') + parts.join(', '));
1841
2986
  }
1842
2987
  console.log();
1843
2988
  if (!allOk) {
1844
2989
  process.exit(1);
1845
2990
  }
2991
+ // Auto-run analyze-imports when audit passes — this builds the import graph
2992
+ // so the App tab can show component/function dependencies for each page.
2993
+ // Previously this was a manual step that Claude had to remember to run.
2994
+ console.log(chalk.dim('Building import graph...'));
2995
+ try {
2996
+ await handleAnalyzeImports({ silent: true });
2997
+ }
2998
+ catch {
2999
+ // Non-fatal — audit passed, import graph is a bonus
3000
+ console.log(chalk.yellow('Warning: Could not build import graph. Run `codeyam editor analyze-imports` manually.'));
3001
+ }
1846
3002
  }
1847
3003
  // ─── Scenarios subcommand ─────────────────────────────────────────────
1848
3004
  async function handleScenarios() {
@@ -1930,6 +3086,77 @@ async function handleScenarios() {
1930
3086
  const total = scenarios.length;
1931
3087
  const groupCount = groups.size;
1932
3088
  console.log(chalk.dim(`${total} scenario${total !== 1 ? 's' : ''} across ${groupCount} component${groupCount !== 1 ? 's' : ''}`));
3089
+ // Show screenshot paths so Claude can verify images with the Read tool
3090
+ const withScreenshots = scenarios.filter((s) => s.screenshotPath);
3091
+ if (withScreenshots.length > 0) {
3092
+ console.log();
3093
+ console.log(chalk.bold.cyan('Screenshots:'));
3094
+ for (const s of withScreenshots) {
3095
+ console.log(chalk.dim(` ${s.name}: ${s.screenshotPath}`));
3096
+ }
3097
+ }
3098
+ console.log();
3099
+ }
3100
+ // ─── Scenario Coverage subcommand ───────────────────────────────────
3101
+ async function handleScenarioCoverage() {
3102
+ const port = getServerPort();
3103
+ const url = `http://localhost:${port}/api/editor-scenario-coverage`;
3104
+ let data;
3105
+ try {
3106
+ const res = await fetch(url);
3107
+ if (!res.ok) {
3108
+ console.error(chalk.red(`Error: Scenario coverage endpoint returned ${res.status}`));
3109
+ process.exit(1);
3110
+ }
3111
+ data = await res.json();
3112
+ }
3113
+ catch (err) {
3114
+ console.error(chalk.red('Error: Could not reach the CodeYam server. Is it running?'));
3115
+ console.error(chalk.dim(` ${err.message}`));
3116
+ process.exit(1);
3117
+ }
3118
+ console.log();
3119
+ console.log(chalk.bold.cyan('━━━ Scenario Coverage ━━━'));
3120
+ console.log();
3121
+ if (data.note) {
3122
+ console.log(chalk.yellow(data.note));
3123
+ console.log();
3124
+ return;
3125
+ }
3126
+ // Fresh scenarios (already recaptured this feature)
3127
+ if (data.freshScenarios.length > 0) {
3128
+ console.log(chalk.green(`✓ ${data.freshScenarios.length} scenario(s) captured this feature:`));
3129
+ for (const s of data.freshScenarios) {
3130
+ console.log(chalk.dim(` ${s.name} (${s.url || '/'})`));
3131
+ }
3132
+ console.log();
3133
+ }
3134
+ // Stale scenarios (need recapturing)
3135
+ if (data.staleScenarios.length > 0) {
3136
+ console.log(chalk.red(`✗ ${data.staleScenarios.length} scenario(s) need re-registering (stale screenshots):`));
3137
+ for (const s of data.staleScenarios) {
3138
+ console.log(chalk.yellow(` ${s.name} (${s.url || '/'}) — ${s.changeStatus} but not recaptured`));
3139
+ }
3140
+ console.log();
3141
+ console.log(chalk.yellow('Re-register each stale scenario with the SAME name to update its screenshot.'));
3142
+ console.log(chalk.yellow("Enhance the seed data to reflect this feature's changes where relevant."));
3143
+ console.log();
3144
+ }
3145
+ // Uncovered pages (changed but no scenarios at all)
3146
+ if (data.uncoveredPages.length > 0) {
3147
+ console.log(chalk.red(`✗ ${data.uncoveredPages.length} page(s) affected by changes with no scenarios:`));
3148
+ for (const p of data.uncoveredPages) {
3149
+ console.log(chalk.yellow(` ${p.entityName} — ${p.changeStatus}`));
3150
+ }
3151
+ console.log();
3152
+ }
3153
+ // Summary
3154
+ if (data.pass) {
3155
+ console.log(chalk.green.bold('PASS — all affected scenarios are fresh'));
3156
+ }
3157
+ else {
3158
+ console.log(chalk.red.bold('FAIL — re-register stale scenarios before proceeding'));
3159
+ }
1933
3160
  console.log();
1934
3161
  }
1935
3162
  // ─── Template subcommand ─────────────────────────────────────────────
@@ -2019,6 +3246,9 @@ async function handleTemplate() {
2019
3246
  // Config parse error is non-fatal
2020
3247
  }
2021
3248
  }
3249
+ // 5b. Write a fresh prototypeId so the proxy clears stale localStorage
3250
+ const activeScenarioPath = path.join(root, '.codeyam', 'active-scenario.json');
3251
+ fs.writeFileSync(activeScenarioPath, JSON.stringify({ prototypeId: Date.now().toString() }), 'utf-8');
2022
3252
  // 6. Trigger editor-refresh so the server picks up the new project
2023
3253
  console.log(chalk.bold('Refreshing editor...'));
2024
3254
  try {
@@ -2037,17 +3267,14 @@ async function handleTemplate() {
2037
3267
  // ─── Sync subcommand ─────────────────────────────────────────────────
2038
3268
  /**
2039
3269
  * `codeyam editor sync`
2040
- * Import scenarios from scenarios-manifest.json into the local database.
3270
+ * Import scenarios from editor-scenarios/ files into the local database.
3271
+ * Falls back to legacy scenarios-manifest.json if no _metadata files found.
2041
3272
  */
2042
3273
  async function handleSync() {
2043
3274
  const root = getProjectRoot();
2044
- const manifest = readManifest(root);
2045
- if (!manifest) {
2046
- console.log(chalk.yellow('No scenarios-manifest.json found. Nothing to sync.'));
2047
- return;
2048
- }
2049
- if (manifest.scenarios.length === 0) {
2050
- console.log(chalk.dim('Manifest is empty. Nothing to sync.'));
3275
+ const entries = scanScenarioFiles(root);
3276
+ if (entries.length === 0) {
3277
+ console.log(chalk.yellow('No scenario files with metadata found. Nothing to sync.'));
2051
3278
  return;
2052
3279
  }
2053
3280
  const configPath = path.join(root, '.codeyam', 'config.json');
@@ -2071,13 +3298,12 @@ async function handleSync() {
2071
3298
  const { project } = await requireBranchAndProject(projectSlug);
2072
3299
  const { getDatabase } = await import('../../../packages/database/index.js');
2073
3300
  const db = getDatabase();
2074
- // Fetch existing editor scenarios
2075
3301
  const existingRows = await db
2076
3302
  .selectFrom('editor_scenarios')
2077
3303
  .select(['id', 'updated_at'])
2078
3304
  .where('project_id', '=', project.id)
2079
3305
  .execute();
2080
- const result = await syncManifestToDatabase(root, project.id, existingRows, async (row) => {
3306
+ const result = await syncScenarioFilesToDatabase(root, project.id, existingRows, async (row) => {
2081
3307
  await db
2082
3308
  .insertInto('editor_scenarios')
2083
3309
  .values(row)
@@ -2102,6 +3328,34 @@ async function handleSync() {
2102
3328
  parts.push(`${result.skipped} unchanged`);
2103
3329
  console.log(chalk.green(`Synced scenarios: ${parts.join(', ')}`));
2104
3330
  }
3331
+ // Migrate legacy scenario formats after sync, then re-sync if anything was fixed
3332
+ try {
3333
+ const migrateResult = migrateScenarioFormats(root);
3334
+ if (migrateResult.fixed > 0) {
3335
+ console.log(chalk.green(`Migrated ${migrateResult.fixed} scenario file${migrateResult.fixed > 1 ? 's' : ''} to current format`));
3336
+ // Re-sync so the DB reflects the corrected file metadata
3337
+ const refreshedRows = await db
3338
+ .selectFrom('editor_scenarios')
3339
+ .select(['id', 'updated_at'])
3340
+ .where('project_id', '=', project.id)
3341
+ .execute();
3342
+ await syncScenarioFilesToDatabase(root, project.id, refreshedRows, async (row) => {
3343
+ await db
3344
+ .insertInto('editor_scenarios')
3345
+ .values(row)
3346
+ .execute();
3347
+ }, async (id, row) => {
3348
+ await db
3349
+ .updateTable('editor_scenarios')
3350
+ .set(row)
3351
+ .where('id', '=', id)
3352
+ .execute();
3353
+ });
3354
+ }
3355
+ }
3356
+ catch {
3357
+ // Non-fatal
3358
+ }
2105
3359
  }
2106
3360
  // ─── Verify Images subcommand ─────────────────────────────────────────
2107
3361
  async function handleVerifyImages(jsonArg) {
@@ -2234,8 +3488,11 @@ function handleEditorDebug(args) {
2234
3488
  11: printStep11,
2235
3489
  12: printStep12,
2236
3490
  13: printStep13,
3491
+ 14: printStep14,
3492
+ 15: printStep15,
3493
+ 16: printStep16,
2237
3494
  };
2238
- for (let step = 1; step <= 13; step++) {
3495
+ for (let step = 1; step <= 16; step++) {
2239
3496
  const stepId = `step-${step}`;
2240
3497
  if (!wants(stepId))
2241
3498
  continue;
@@ -2314,8 +3571,8 @@ const editorCommand = {
2314
3571
  describe: 'Editor mode guided workflow',
2315
3572
  builder: (yargs) => {
2316
3573
  const stepDescription = IS_INTERNAL_BUILD
2317
- ? 'Step number (1-13) or subcommand (template, register, isolate, analyze-imports, dependents, audit, scenarios, change, sync, debug, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors)'
2318
- : 'Step number (1-13) or subcommand (template, register, isolate, analyze-imports, dependents, audit, scenarios, change, sync, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors)';
3574
+ ? 'Step number (1-16) or subcommand (template, register, isolate, analyze-imports, dependents, audit, scenarios, scenario-coverage, change, sync, debug, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors)'
3575
+ : 'Step number (1-16) or subcommand (template, register, isolate, analyze-imports, dependents, audit, scenarios, scenario-coverage, change, sync, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors)';
2319
3576
  let builder = yargs
2320
3577
  .positional('step', {
2321
3578
  type: 'string',
@@ -2331,7 +3588,7 @@ const editorCommand = {
2331
3588
  })
2332
3589
  .option('app-formats', {
2333
3590
  type: 'string',
2334
- describe: 'Comma-separated app formats (web-app, mobile-responsive-web-app, desktop-app, mobile-app)',
3591
+ describe: 'Comma-separated app formats (mobile-responsive-web-app, desktop-app, mobile-app)',
2335
3592
  })
2336
3593
  .option('tech-stack', {
2337
3594
  type: 'string',
@@ -2351,7 +3608,7 @@ const editorCommand = {
2351
3608
  builder = builder
2352
3609
  .option('target', {
2353
3610
  type: 'string',
2354
- describe: 'Debug target (setup, overview, overview-with-state, step-1..step-13, or comma-separated list)',
3611
+ describe: 'Debug target (setup, overview, overview-with-state, step-1..step-16, or comma-separated list)',
2355
3612
  })
2356
3613
  .option('resume', {
2357
3614
  type: 'boolean',
@@ -2426,6 +3683,11 @@ const editorCommand = {
2426
3683
  await handleScenarios();
2427
3684
  return;
2428
3685
  }
3686
+ // Subcommand: codeyam editor scenario-coverage
3687
+ if (argv.step === 'scenario-coverage') {
3688
+ await handleScenarioCoverage();
3689
+ return;
3690
+ }
2429
3691
  // Subcommand: codeyam editor change <feature>
2430
3692
  if (argv.step === 'change') {
2431
3693
  handleChange(argv.json || '');
@@ -2441,7 +3703,7 @@ const editorCommand = {
2441
3703
  await handleValidateSeed(argv.json || '');
2442
3704
  return;
2443
3705
  }
2444
- // Subcommand: codeyam editor isolate StarRating CategoryBadge DrinkCard
3706
+ // Subcommand: codeyam editor isolate "StarRating CategoryBadge DrinkCard"
2445
3707
  if (argv.step === 'isolate') {
2446
3708
  const names = [];
2447
3709
  if (argv.json)
@@ -2471,6 +3733,11 @@ const editorCommand = {
2471
3733
  await handleEditorDebug(argv);
2472
3734
  return;
2473
3735
  }
3736
+ // Subcommand: codeyam editor migrate [subArg]
3737
+ if (argv.step === 'migrate') {
3738
+ handleMigrateCommand(root, argv.json || undefined);
3739
+ return;
3740
+ }
2474
3741
  // Subcommand: codeyam editor steps — show setup or cycle overview
2475
3742
  if (argv.step === 'steps') {
2476
3743
  if (!hasProject(root)) {
@@ -2478,9 +3745,9 @@ const editorCommand = {
2478
3745
  }
2479
3746
  else {
2480
3747
  const state = readState(root);
2481
- // Clear prompt file when feature is done (step 13) so the hook
3748
+ // Clear prompt file when feature is done (step 16) so the hook
2482
3749
  // can capture the next feature request from the user.
2483
- if (state?.step === 13) {
3750
+ if (state?.step === 16) {
2484
3751
  clearEditorUserPrompt(root);
2485
3752
  }
2486
3753
  printCycleOverview(root, state);
@@ -2488,8 +3755,8 @@ const editorCommand = {
2488
3755
  return;
2489
3756
  }
2490
3757
  const step = argv.step ? parseInt(argv.step, 10) : undefined;
2491
- if (step != null && (isNaN(step) || step < 1 || step > 13)) {
2492
- console.error(chalk.red(`Error: Invalid step "${argv.step}". Must be 1-13.`));
3758
+ if (step != null && (isNaN(step) || step < 1 || step > 16)) {
3759
+ console.error(chalk.red(`Error: Invalid step "${argv.step}". Must be 1-16.`));
2493
3760
  process.exit(1);
2494
3761
  }
2495
3762
  if (step == null) {
@@ -2522,9 +3789,42 @@ const editorCommand = {
2522
3789
  return;
2523
3790
  }
2524
3791
  const { project, branch } = await requireBranchAndProject(projectSlug);
2525
- // Auto-sync scenarios from manifest if it exists
2526
- const manifestData = readManifest(projectRoot);
2527
- if (manifestData && manifestData.scenarios.length > 0) {
3792
+ // Backfill _metadata into existing scenario files that predate the embedded metadata feature.
3793
+ // This reads metadata from the DB and writes it into files that don't have _metadata yet.
3794
+ try {
3795
+ const { getDatabase } = await import('../../../packages/database/index.js');
3796
+ const db = getDatabase();
3797
+ const dbScenarios = await db
3798
+ .selectFrom('editor_scenarios')
3799
+ .select([
3800
+ 'id',
3801
+ 'name',
3802
+ 'description',
3803
+ 'component_name',
3804
+ 'component_path',
3805
+ 'url',
3806
+ 'type',
3807
+ 'screenshot_path',
3808
+ 'viewport_width',
3809
+ 'viewport_height',
3810
+ 'created_at',
3811
+ 'updated_at',
3812
+ ])
3813
+ .where('project_id', '=', project.id)
3814
+ .execute();
3815
+ if (dbScenarios.length > 0) {
3816
+ const backfilled = backfillScenarioMetadata(projectRoot, dbScenarios);
3817
+ if (backfilled > 0) {
3818
+ console.log(chalk.green(` Backfilled metadata into ${backfilled} scenario file${backfilled > 1 ? 's' : ''}`));
3819
+ }
3820
+ }
3821
+ }
3822
+ catch {
3823
+ // Non-fatal — backfill is best-effort
3824
+ }
3825
+ // Auto-sync scenario files (with _metadata) to database
3826
+ const scenarioEntries = scanScenarioFiles(projectRoot);
3827
+ if (scenarioEntries.length > 0) {
2528
3828
  try {
2529
3829
  const { getDatabase } = await import('../../../packages/database/index.js');
2530
3830
  const db = getDatabase();
@@ -2533,7 +3833,7 @@ const editorCommand = {
2533
3833
  .select(['id', 'updated_at'])
2534
3834
  .where('project_id', '=', project.id)
2535
3835
  .execute();
2536
- const syncResult = await syncManifestToDatabase(projectRoot, project.id, existingRows, async (row) => {
3836
+ const syncResult = await syncScenarioFilesToDatabase(projectRoot, project.id, existingRows, async (row) => {
2537
3837
  await db
2538
3838
  .insertInto('editor_scenarios')
2539
3839
  .values(row)
@@ -2551,13 +3851,50 @@ const editorCommand = {
2551
3851
  parts.push(`${syncResult.inserted} imported`);
2552
3852
  if (syncResult.updated > 0)
2553
3853
  parts.push(`${syncResult.updated} updated`);
2554
- console.log(chalk.green(` Synced scenarios from manifest: ${parts.join(', ')}`));
3854
+ console.log(chalk.green(` Synced scenarios from files: ${parts.join(', ')}`));
2555
3855
  }
2556
3856
  }
2557
3857
  catch {
2558
3858
  // Non-fatal — sync failure shouldn't block editor startup
2559
3859
  }
2560
3860
  }
3861
+ // Migrate legacy scenario formats: resolve null viewports, populate
3862
+ // dimensions arrays, and build screenshotPaths maps from single values.
3863
+ // If files were fixed, re-sync to update the database with corrected values.
3864
+ try {
3865
+ const migrateResult = migrateScenarioFormats(projectRoot);
3866
+ if (migrateResult.fixed > 0) {
3867
+ console.log(chalk.green(` Migrated ${migrateResult.fixed} scenario file${migrateResult.fixed > 1 ? 's' : ''} to current format`));
3868
+ // Re-sync so the DB reflects the fixed file metadata
3869
+ try {
3870
+ const { getDatabase } = await import('../../../packages/database/index.js');
3871
+ const db = getDatabase();
3872
+ const rows = await db
3873
+ .selectFrom('editor_scenarios')
3874
+ .select(['id', 'updated_at'])
3875
+ .where('project_id', '=', project.id)
3876
+ .execute();
3877
+ await syncScenarioFilesToDatabase(projectRoot, project.id, rows, async (row) => {
3878
+ await db
3879
+ .insertInto('editor_scenarios')
3880
+ .values(row)
3881
+ .execute();
3882
+ }, async (id, row) => {
3883
+ await db
3884
+ .updateTable('editor_scenarios')
3885
+ .set(row)
3886
+ .where('id', '=', id)
3887
+ .execute();
3888
+ });
3889
+ }
3890
+ catch {
3891
+ // Non-fatal — DB re-sync failure shouldn't block startup
3892
+ }
3893
+ }
3894
+ }
3895
+ catch {
3896
+ // Non-fatal — migration failure shouldn't block editor startup
3897
+ }
2561
3898
  // `codeyam editor` (no step) always implies editor mode.
2562
3899
  // The empty-folder heuristic is no longer needed here — running this
2563
3900
  // command IS the signal. We still detect empty folders so that
@@ -2596,38 +3933,96 @@ const editorCommand = {
2596
3933
  editorMode,
2597
3934
  });
2598
3935
  // Auto-finalize analyzer so codeyam analyze works
2599
- if (editorMode && !isAnalyzerFinalized()) {
2600
- try {
2601
- const { execSync } = await import('child_process');
2602
- const templatePath = getAnalyzerTemplatePath();
2603
- if (fs.existsSync(templatePath)) {
2604
- console.log(' Setting up simulations (first time only)...');
2605
- execSync('npm install --include=dev', {
2606
- cwd: templatePath,
2607
- stdio: 'pipe',
2608
- });
2609
- execSync('npx playwright install chromium', {
2610
- cwd: templatePath,
2611
- stdio: 'pipe',
2612
- });
2613
- execSync('npm run build', {
2614
- cwd: templatePath,
2615
- stdio: 'pipe',
2616
- });
2617
- fs.writeFileSync(path.join(templatePath, '.finalized'), new Date().toISOString());
3936
+ if (editorMode) {
3937
+ const stepLabels = {
3938
+ 'npm-install': 'Installing simulation dependencies...',
3939
+ 'playwright-install': 'Installing browser (Chromium)...',
3940
+ build: 'Building simulation engine...',
3941
+ };
3942
+ const simProgress = new ProgressReporter();
3943
+ const finalization = ensureAnalyzerFinalized({
3944
+ onProgress: (step) => simProgress.start(stepLabels[step]),
3945
+ });
3946
+ if (finalization.errors.length > 0) {
3947
+ for (const err of finalization.errors) {
3948
+ simProgress.warn(`${stepLabels[err.step].replace('...', '')} failed: ${err.message}`);
2618
3949
  }
2619
3950
  }
2620
- catch {
2621
- // Non-fatal
3951
+ else if (finalization.needed) {
3952
+ simProgress.succeed('Simulation engine ready');
2622
3953
  }
2623
3954
  }
2624
- // Start background server
3955
+ // Start background server (handles killing existing servers internally)
3956
+ const editorPort = argv.port || 3111;
2625
3957
  const { url } = await startBackgroundServer({
2626
- port: argv.port || 3111,
3958
+ port: editorPort,
2627
3959
  rootPath: projectRoot,
2628
3960
  project,
2629
3961
  branch,
2630
3962
  });
3963
+ // Build import graph if glossary exists but entities are missing.
3964
+ // Runs on first startup (no entities at all) AND when page files or
3965
+ // scenario component files lack entity coverage.
3966
+ const glossaryPath = path.join(projectRoot, '.codeyam', 'glossary.json');
3967
+ if (fs.existsSync(glossaryPath)) {
3968
+ let needsAnalysis = false;
3969
+ const entities = await loadEntities({});
3970
+ if (!entities || entities.length === 0) {
3971
+ needsAnalysis = true;
3972
+ }
3973
+ else {
3974
+ const entityFilePaths = new Set(entities.map((e) => e.filePath));
3975
+ // Check if any page files are missing entities (Next.js apps)
3976
+ try {
3977
+ const { scanPageFilePaths } = await import('../utils/entityChangeStatus.server.js');
3978
+ const { allFiles } = scanPageFilePaths(projectRoot);
3979
+ const pageFiles = allFiles.filter((f) => f.endsWith('/page.tsx') || f.endsWith('/page.js'));
3980
+ const missingPages = pageFiles.filter((f) => !entityFilePaths.has(f));
3981
+ if (missingPages.length > 0) {
3982
+ console.log(chalk.dim(` Found ${missingPages.length} page(s) without entity analysis — running import analysis...`));
3983
+ needsAnalysis = true;
3984
+ }
3985
+ }
3986
+ catch {
3987
+ // Non-fatal — page file check failed
3988
+ }
3989
+ // Check if any scenario files (component_path or page_file_path)
3990
+ // are missing entities — covers non-Next.js apps
3991
+ if (!needsAnalysis) {
3992
+ try {
3993
+ const { getDatabase } = await import('../../../packages/database/index.js');
3994
+ const db = getDatabase();
3995
+ const scenarioFiles = await db
3996
+ .selectFrom('editor_scenarios')
3997
+ .select(['component_path', 'page_file_path'])
3998
+ .where('project_id', '=', project.id)
3999
+ .distinct()
4000
+ .execute();
4001
+ const missingCount = scenarioFiles.filter((row) => {
4002
+ const cp = row.component_path;
4003
+ const pfp = row.page_file_path;
4004
+ return ((cp && !entityFilePaths.has(cp)) ||
4005
+ (pfp && !entityFilePaths.has(pfp)));
4006
+ }).length;
4007
+ if (missingCount > 0) {
4008
+ console.log(chalk.dim(` Found ${missingCount} scenario file(s) without entity analysis — running import analysis...`));
4009
+ needsAnalysis = true;
4010
+ }
4011
+ }
4012
+ catch {
4013
+ // Non-fatal
4014
+ }
4015
+ }
4016
+ }
4017
+ if (needsAnalysis) {
4018
+ try {
4019
+ await handleAnalyzeImports({ silent: true });
4020
+ }
4021
+ catch {
4022
+ // Non-fatal
4023
+ }
4024
+ }
4025
+ }
2631
4026
  console.log();
2632
4027
  console.log(` Dashboard: ${url}`);
2633
4028
  console.log(' Run "codeyam --help" for all commands');
@@ -2641,7 +4036,9 @@ const editorCommand = {
2641
4036
  : process.platform === 'win32'
2642
4037
  ? 'start ""'
2643
4038
  : 'xdg-open';
2644
- execSync(`${openCommand} "${url}/editor"`, { stdio: 'ignore' });
4039
+ execSync(`${openCommand} "${url}/editor"`, {
4040
+ stdio: 'ignore',
4041
+ });
2645
4042
  }
2646
4043
  catch {
2647
4044
  // Silently fail if open command doesn't work
@@ -2655,6 +4052,18 @@ const editorCommand = {
2655
4052
  return;
2656
4053
  }
2657
4054
  const state = readState(root);
4055
+ // Validate step transition — prevent skipping steps.
4056
+ // Exception: step 2 with --feature is always allowed because step 1's
4057
+ // instructions explicitly tell Claude to run `codeyam editor 2 --feature "..."`.
4058
+ // Step 1 is planning-only and may not persist state (no --feature flag).
4059
+ const skipValidation = step === 2 && argv.feature;
4060
+ if (!skipValidation) {
4061
+ const stepError = validateStepTransition(step, state?.step ?? null);
4062
+ if (stepError) {
4063
+ console.error(chalk.red(`Error: ${stepError}`));
4064
+ process.exit(1);
4065
+ }
4066
+ }
2658
4067
  switch (step) {
2659
4068
  case 1: {
2660
4069
  const feature = argv.feature || undefined;
@@ -2686,12 +4095,24 @@ const editorCommand = {
2686
4095
  case 10:
2687
4096
  case 11:
2688
4097
  case 12:
2689
- case 13: {
4098
+ case 13:
4099
+ case 14:
4100
+ case 15:
4101
+ case 16: {
2690
4102
  const feature = argv.feature || state?.feature;
2691
4103
  if (!feature) {
2692
4104
  console.error(chalk.red('Error: No feature in progress. Run codeyam editor 1 first.'));
2693
4105
  process.exit(1);
2694
4106
  }
4107
+ // Hard gate: steps 8+ require audit to have passed
4108
+ if (step >= 8) {
4109
+ const auditOk = await checkAuditGate();
4110
+ if (!auditOk) {
4111
+ console.error(chalk.red.bold('BLOCKED: The audit has not passed. Run `codeyam editor audit` and fix all issues before proceeding.'));
4112
+ console.error(chalk.yellow('Every function and hook must have a test file. Every component must have scenarios.'));
4113
+ process.exit(1);
4114
+ }
4115
+ }
2695
4116
  const stepFns = {
2696
4117
  3: printStep3,
2697
4118
  4: printStep4,
@@ -2704,6 +4125,9 @@ const editorCommand = {
2704
4125
  11: printStep11,
2705
4126
  12: printStep12,
2706
4127
  13: printStep13,
4128
+ 14: printStep14,
4129
+ 15: printStep15,
4130
+ 16: printStep16,
2707
4131
  };
2708
4132
  stepFns[step](root, feature);
2709
4133
  break;