@codeyam/codeyam-cli 0.1.10 → 0.1.12

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 (207) 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 +42 -16
  8. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts +3 -1
  9. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts.map +1 -1
  10. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js +44 -16
  11. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
  12. package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js +11 -0
  13. package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js.map +1 -1
  14. package/codeyam-cli/src/commands/editor.js +1214 -217
  15. package/codeyam-cli/src/commands/editor.js.map +1 -1
  16. package/codeyam-cli/src/commands/init.js +1 -0
  17. package/codeyam-cli/src/commands/init.js.map +1 -1
  18. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +893 -1
  19. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
  20. package/codeyam-cli/src/utils/__tests__/editorBroadcastViewport.test.js +76 -0
  21. package/codeyam-cli/src/utils/__tests__/editorBroadcastViewport.test.js.map +1 -0
  22. package/codeyam-cli/src/utils/__tests__/editorDeleteScenario.test.js +100 -0
  23. package/codeyam-cli/src/utils/__tests__/editorDeleteScenario.test.js.map +1 -0
  24. package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js +6 -3
  25. package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js.map +1 -1
  26. package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js +261 -0
  27. package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js.map +1 -0
  28. package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js +75 -1
  29. package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js.map +1 -1
  30. package/codeyam-cli/src/utils/__tests__/editorMigration.test.js +435 -0
  31. package/codeyam-cli/src/utils/__tests__/editorMigration.test.js.map +1 -0
  32. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +441 -17
  33. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
  34. package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js +67 -0
  35. package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js.map +1 -1
  36. package/codeyam-cli/src/utils/__tests__/editorSeedAdapterPrismaValidation.test.js +143 -0
  37. package/codeyam-cli/src/utils/__tests__/editorSeedAdapterPrismaValidation.test.js.map +1 -0
  38. package/codeyam-cli/src/utils/__tests__/editorSessionFilter.test.js +66 -0
  39. package/codeyam-cli/src/utils/__tests__/editorSessionFilter.test.js.map +1 -0
  40. package/codeyam-cli/src/utils/__tests__/editorShouldRevalidate.test.js +53 -0
  41. package/codeyam-cli/src/utils/__tests__/editorShouldRevalidate.test.js.map +1 -0
  42. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js +125 -10
  43. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js.map +1 -1
  44. package/codeyam-cli/src/utils/__tests__/routePatternMatching.test.js +118 -0
  45. package/codeyam-cli/src/utils/__tests__/routePatternMatching.test.js.map +1 -0
  46. package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js +40 -1
  47. package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js.map +1 -1
  48. package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js +1 -0
  49. package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js.map +1 -1
  50. package/codeyam-cli/src/utils/analysisRunner.js +3 -1
  51. package/codeyam-cli/src/utils/analysisRunner.js.map +1 -1
  52. package/codeyam-cli/src/utils/editorAudit.js +145 -0
  53. package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
  54. package/codeyam-cli/src/utils/editorBroadcastViewport.js +26 -0
  55. package/codeyam-cli/src/utils/editorBroadcastViewport.js.map +1 -0
  56. package/codeyam-cli/src/utils/editorDeleteScenario.js +67 -0
  57. package/codeyam-cli/src/utils/editorDeleteScenario.js.map +1 -0
  58. package/codeyam-cli/src/utils/editorEntityChangeStatus.js +1 -1
  59. package/codeyam-cli/src/utils/editorEntityChangeStatus.js.map +1 -1
  60. package/codeyam-cli/src/utils/editorEntityHelpers.js +129 -0
  61. package/codeyam-cli/src/utils/editorEntityHelpers.js.map +1 -0
  62. package/codeyam-cli/src/utils/editorLoaderHelpers.js +40 -1
  63. package/codeyam-cli/src/utils/editorLoaderHelpers.js.map +1 -1
  64. package/codeyam-cli/src/utils/editorMigration.js +224 -0
  65. package/codeyam-cli/src/utils/editorMigration.js.map +1 -0
  66. package/codeyam-cli/src/utils/editorScenarios.js +163 -2
  67. package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
  68. package/codeyam-cli/src/utils/editorSeedAdapter.js +253 -4
  69. package/codeyam-cli/src/utils/editorSeedAdapter.js.map +1 -1
  70. package/codeyam-cli/src/utils/editorShouldRevalidate.js +21 -0
  71. package/codeyam-cli/src/utils/editorShouldRevalidate.js.map +1 -0
  72. package/codeyam-cli/src/utils/entityChangeStatus.js +19 -2
  73. package/codeyam-cli/src/utils/entityChangeStatus.js.map +1 -1
  74. package/codeyam-cli/src/utils/entityChangeStatus.server.js +41 -3
  75. package/codeyam-cli/src/utils/entityChangeStatus.server.js.map +1 -1
  76. package/codeyam-cli/src/utils/fileWatcher.js +38 -0
  77. package/codeyam-cli/src/utils/fileWatcher.js.map +1 -1
  78. package/codeyam-cli/src/utils/install-skills.js +9 -0
  79. package/codeyam-cli/src/utils/install-skills.js.map +1 -1
  80. package/codeyam-cli/src/utils/routePatternMatching.js +129 -0
  81. package/codeyam-cli/src/utils/routePatternMatching.js.map +1 -0
  82. package/codeyam-cli/src/utils/scenarioCoverage.js +8 -9
  83. package/codeyam-cli/src/utils/scenarioCoverage.js.map +1 -1
  84. package/codeyam-cli/src/utils/scenariosManifest.js +18 -10
  85. package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -1
  86. package/codeyam-cli/src/utils/setupClaudeCodeSettings.js +1 -0
  87. package/codeyam-cli/src/utils/setupClaudeCodeSettings.js.map +1 -1
  88. package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js +35 -0
  89. package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js.map +1 -0
  90. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js +33 -0
  91. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js.map +1 -1
  92. package/codeyam-cli/src/webserver/app/types/editor.js +8 -0
  93. package/codeyam-cli/src/webserver/app/types/editor.js.map +1 -0
  94. package/codeyam-cli/src/webserver/backgroundServer.js +18 -4
  95. package/codeyam-cli/src/webserver/backgroundServer.js.map +1 -1
  96. package/codeyam-cli/src/webserver/build/client/assets/CopyButton-CzTDWkF2.js +1 -0
  97. package/codeyam-cli/src/webserver/build/client/assets/{EntityItem-BcgbViKV.js → EntityItem-BFbq6iFk.js} +3 -3
  98. package/codeyam-cli/src/webserver/build/client/assets/EntityTypeBadge-CQgyEGV-.js +1 -0
  99. package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeIcon-CQIG2qda.js → EntityTypeIcon-B6OMi58N.js} +1 -1
  100. package/codeyam-cli/src/webserver/build/client/assets/InlineSpinner-DuYodzo1.js +1 -0
  101. package/codeyam-cli/src/webserver/build/client/assets/InteractivePreview-CXo9EeCl.js +25 -0
  102. package/codeyam-cli/src/webserver/build/client/assets/LibraryFunctionPreview-DYCNb2It.js +3 -0
  103. package/codeyam-cli/src/webserver/build/client/assets/{LoadingDots-BU_OAEMP.js → LoadingDots-By5zI316.js} +1 -1
  104. package/codeyam-cli/src/webserver/build/client/assets/{LogViewer-ceAyBX-H.js → LogViewer-CZgY3sxX.js} +3 -3
  105. package/codeyam-cli/src/webserver/build/client/assets/{ReportIssueModal-BzHcG7SE.js → ReportIssueModal-CnYYwRDw.js} +2 -2
  106. package/codeyam-cli/src/webserver/build/client/assets/SafeScreenshot-CDoF7ZpU.js +1 -0
  107. package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-TSD3C211.js → ScenarioViewer-DrnfvaLL.js} +3 -3
  108. package/codeyam-cli/src/webserver/build/client/assets/Spinner-Df3UCi8k.js +34 -0
  109. package/codeyam-cli/src/webserver/build/client/assets/TruncatedFilePath-CK7-NaPZ.js +1 -0
  110. package/codeyam-cli/src/webserver/build/client/assets/ViewportInspectBar-DRKR9T0U.js +1 -0
  111. package/codeyam-cli/src/webserver/build/client/assets/{_index-DLxKhri3.js → _index-ClR-g3tY.js} +2 -2
  112. package/codeyam-cli/src/webserver/build/client/assets/{activity.(_tab)-BcY3q6nt.js → activity.(_tab)-DTH6ydEA.js} +3 -3
  113. package/codeyam-cli/src/webserver/build/client/assets/{addon-web-links-Duc5hnl7.js → addon-web-links-74hnHF59.js} +1 -1
  114. package/codeyam-cli/src/webserver/build/client/assets/{agent-transcripts-Bni3iiUj.js → agent-transcripts-B8CYhCO9.js} +3 -3
  115. package/codeyam-cli/src/webserver/build/client/assets/api.editor-rename-scenario-l0sNRNKZ.js +1 -0
  116. package/codeyam-cli/src/webserver/build/client/assets/api.editor-save-seed-state-l0sNRNKZ.js +1 -0
  117. package/codeyam-cli/src/webserver/build/client/assets/api.editor-scenario-prompt-l0sNRNKZ.js +1 -0
  118. package/codeyam-cli/src/webserver/build/client/assets/api.editor-session-l0sNRNKZ.js +1 -0
  119. package/codeyam-cli/src/webserver/build/client/assets/{book-open-BYOypzCa.js → book-open-CLaoh4ac.js} +1 -1
  120. package/codeyam-cli/src/webserver/build/client/assets/{chevron-down-C_Pmso5S.js → chevron-down-BZ2DZxbW.js} +1 -1
  121. package/codeyam-cli/src/webserver/build/client/assets/{chunk-JZWAC4HX-C4pqxYJB.js → chunk-JZWAC4HX-BBXArFPl.js} +13 -21
  122. package/codeyam-cli/src/webserver/build/client/assets/{circle-check-BVMi9VA5.js → circle-check-CT4unAk-.js} +1 -1
  123. package/codeyam-cli/src/webserver/build/client/assets/{copy-n2FB0_Sw.js → copy-zK0B6Nu-.js} +1 -1
  124. package/codeyam-cli/src/webserver/build/client/assets/{createLucideIcon-CC6AbExI.js → createLucideIcon-DJB0YQJL.js} +1 -1
  125. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-CkXFP_i-.js +1 -0
  126. package/codeyam-cli/src/webserver/build/client/assets/editor._tab-DPw7NZHc.js +1 -0
  127. package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-DmBK1JBK.js +58 -0
  128. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-DBa7T2FK.js +41 -0
  129. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-DwCV5__E.js → entity._sha._-BqAN7hyG.js} +2 -2
  130. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-BOi8kpwd.js +6 -0
  131. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-Dg1NhIms.js +6 -0
  132. package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.create-scenario-CJX6kkkV.js +6 -0
  133. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-BMvVHNXU.js → entity._sha_.edit._scenarioId-BhVjZhKg.js} +2 -2
  134. package/codeyam-cli/src/webserver/build/client/assets/{entry.client-DTvKq3TY.js → entry.client-_gzKltPN.js} +6 -6
  135. package/codeyam-cli/src/webserver/build/client/assets/fileTableUtils-Daa96Fr1.js +1 -0
  136. package/codeyam-cli/src/webserver/build/client/assets/files-CV_17tZS.js +1 -0
  137. package/codeyam-cli/src/webserver/build/client/assets/git-D-YXmMbR.js +1 -0
  138. package/codeyam-cli/src/webserver/build/client/assets/globals-CGrDAxj0.css +1 -0
  139. package/codeyam-cli/src/webserver/build/client/assets/{index-yHOVb4rc.js → index-Blo6EK8G.js} +1 -1
  140. package/codeyam-cli/src/webserver/build/client/assets/{index-10oVnAAH.js → index-BsX0F-9C.js} +1 -1
  141. package/codeyam-cli/src/webserver/build/client/assets/{index-BcvgDzbZ.js → index-CCrgCshv.js} +1 -1
  142. package/codeyam-cli/src/webserver/build/client/assets/jsx-runtime-D_zvdyIk.js +9 -0
  143. package/codeyam-cli/src/webserver/build/client/assets/labs-Byazq8Pv.js +1 -0
  144. package/codeyam-cli/src/webserver/build/client/assets/{loader-circle-DaAZ_H2w.js → loader-circle-DVQ0oHR7.js} +1 -1
  145. package/codeyam-cli/src/webserver/build/client/assets/manifest-b3f77062.js +1 -0
  146. package/codeyam-cli/src/webserver/build/client/assets/{memory-9gnxSZlb.js → memory-b-VmA2Vj.js} +2 -2
  147. package/codeyam-cli/src/webserver/build/client/assets/{pause-f5-1lKBt.js → pause-DGcndCAa.js} +1 -1
  148. package/codeyam-cli/src/webserver/build/client/assets/{root-BWAyuj0r.js → root-D5Zi3U2Z.js} +4 -4
  149. package/codeyam-cli/src/webserver/build/client/assets/{search-Di64LWVb.js → search-C0Uw0bcK.js} +1 -1
  150. package/codeyam-cli/src/webserver/build/client/assets/settings-OoNgHIfW.js +1 -0
  151. package/codeyam-cli/src/webserver/build/client/assets/simulations-Bcemfu8a.js +1 -0
  152. package/codeyam-cli/src/webserver/build/client/assets/{terminal-Br7MOqts.js → terminal-BgMmG7R9.js} +1 -1
  153. package/codeyam-cli/src/webserver/build/client/assets/{triangle-alert-BLdiCuG-.js → triangle-alert-Cs87hJYK.js} +1 -1
  154. package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-BR3Rs7JY.js +1 -0
  155. package/codeyam-cli/src/webserver/build/client/assets/{useLastLogLine-C14nCb1q.js → useLastLogLine-BxxP_XF9.js} +1 -1
  156. package/codeyam-cli/src/webserver/build/client/assets/useReportContext-BermyNU5.js +1 -0
  157. package/codeyam-cli/src/webserver/build/client/assets/useToast-a_QN_W9_.js +1 -0
  158. package/codeyam-cli/src/webserver/build/server/assets/analysisRunner-yTyb36j3.js +13 -0
  159. package/codeyam-cli/src/webserver/build/server/assets/{index-ChX0hPcu.js → index-Cr7d_IsG.js} +1 -1
  160. package/codeyam-cli/src/webserver/build/server/assets/init-M_wqNAfu.js +10 -0
  161. package/codeyam-cli/src/webserver/build/server/assets/progress-CHTtrxFG.js +1 -0
  162. package/codeyam-cli/src/webserver/build/server/assets/server-build-_ybRgrlc.js +551 -0
  163. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  164. package/codeyam-cli/src/webserver/build-info.json +5 -5
  165. package/codeyam-cli/src/webserver/editorProxy.js +93 -11
  166. package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
  167. package/codeyam-cli/src/webserver/mockStateEvents.js +28 -0
  168. package/codeyam-cli/src/webserver/mockStateEvents.js.map +1 -0
  169. package/codeyam-cli/src/webserver/server.js +9 -0
  170. package/codeyam-cli/src/webserver/server.js.map +1 -1
  171. package/codeyam-cli/src/webserver/terminalServer.js +105 -9
  172. package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
  173. package/codeyam-cli/templates/editor-step-hook.py +104 -20
  174. package/codeyam-cli/templates/nextjs-prisma-sqlite/seed-adapter.ts +42 -7
  175. package/codeyam-cli/templates/seed-adapters/supabase.ts +282 -0
  176. package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +62 -0
  177. package/package.json +1 -1
  178. package/packages/database/src/lib/kysely/tables/editorScenariosTable.js +44 -16
  179. package/packages/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
  180. package/codeyam-cli/src/webserver/build/client/assets/CopyButton-BPXZwM4t.js +0 -1
  181. package/codeyam-cli/src/webserver/build/client/assets/EntityTypeBadge-g3saevPb.js +0 -1
  182. package/codeyam-cli/src/webserver/build/client/assets/InlineSpinner-Bu6c6aDe.js +0 -1
  183. package/codeyam-cli/src/webserver/build/client/assets/InteractivePreview-DYFW3lDD.js +0 -25
  184. package/codeyam-cli/src/webserver/build/client/assets/LibraryFunctionPreview-DLeucoVX.js +0 -3
  185. package/codeyam-cli/src/webserver/build/client/assets/SafeScreenshot-BED4B6sP.js +0 -1
  186. package/codeyam-cli/src/webserver/build/client/assets/Spinner-Bb5uFQ5V.js +0 -34
  187. package/codeyam-cli/src/webserver/build/client/assets/TruncatedFilePath-C8OKAR5x.js +0 -1
  188. package/codeyam-cli/src/webserver/build/client/assets/ViewportInspectBar-oAf2Kqsf.js +0 -1
  189. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-Ii3inc0_.js +0 -1
  190. package/codeyam-cli/src/webserver/build/client/assets/editor-COWCNVyV.js +0 -10
  191. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-CNB06EIa.js +0 -41
  192. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-CXSi2aeZ.js +0 -6
  193. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-CHMiAog3.js +0 -6
  194. package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.create-scenario-p9hhkjJM.js +0 -6
  195. package/codeyam-cli/src/webserver/build/client/assets/fileTableUtils-cPo8LiG3.js +0 -1
  196. package/codeyam-cli/src/webserver/build/client/assets/files-BZrlFE1F.js +0 -1
  197. package/codeyam-cli/src/webserver/build/client/assets/git-DdZcvjGh.js +0 -1
  198. package/codeyam-cli/src/webserver/build/client/assets/globals-phvmGvat.css +0 -1
  199. package/codeyam-cli/src/webserver/build/client/assets/labs-Zk7ryIM1.js +0 -1
  200. package/codeyam-cli/src/webserver/build/client/assets/manifest-6134dc40.js +0 -1
  201. package/codeyam-cli/src/webserver/build/client/assets/settings-0OrEMU6J.js +0 -1
  202. package/codeyam-cli/src/webserver/build/client/assets/simulations-DWT-CvLy.js +0 -1
  203. package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-C-_hOl_g.js +0 -1
  204. package/codeyam-cli/src/webserver/build/client/assets/useReportContext-O-jkvSPx.js +0 -1
  205. package/codeyam-cli/src/webserver/build/client/assets/useToast-9FIWuYfK.js +0 -1
  206. package/codeyam-cli/src/webserver/build/server/assets/init-kSNsMjj8.js +0 -10
  207. package/codeyam-cli/src/webserver/build/server/assets/server-build-Bm2xIhmh.js +0 -439
@@ -16,10 +16,13 @@ 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
18
  import { scanScenarioFiles, syncScenarioFilesToDatabase, backfillScenarioMetadata, migrateScenarioFormats, } from "../utils/scenariosManifest.js";
19
- import { clearEditorState, clearEditorUserPrompt, validateStepTransition, } from "../utils/editorScenarios.js";
20
- import { validateSeedData, detectSeedAdapter, } from "../utils/editorSeedAdapter.js";
19
+ import { clearEditorState, clearEditorUserPrompt, validateStepTransition, backfillEntityShaOnScenarios, } from "../utils/editorScenarios.js";
20
+ import { validateSeedData, detectSeedAdapter, validateSeedKeysAgainstPrisma, } from "../utils/editorSeedAdapter.js";
21
+ import { deleteScenarioViaCli } from "../utils/editorDeleteScenario.js";
21
22
  import { buildEditorApiRequest, callEditorApi, EDITOR_API_SUBCOMMANDS, } from "../utils/editorApi.js";
22
23
  import { parseRegisterArg } from "../utils/parseRegisterArg.js";
24
+ import { sanitizeGlossaryEntries } from "../utils/editorLoaderHelpers.js";
25
+ import { readMigrationState, writeMigrationState, advanceToNextPage, completePage, getMigrationResumeInfo, } from "../utils/editorMigration.js";
23
26
  const __filename = fileURLToPath(import.meta.url);
24
27
  const __dirname = path.dirname(__filename);
25
28
  const STEP_LABELS = {
@@ -40,6 +43,18 @@ const STEP_LABELS = {
40
43
  15: 'Finalize',
41
44
  16: 'Push',
42
45
  };
46
+ const MIGRATION_STEP_LABELS = {
47
+ 1: 'Survey',
48
+ 2: 'App Scenarios',
49
+ 3: 'Component Scenarios',
50
+ 4: 'Preview',
51
+ 5: 'Discuss',
52
+ 6: 'Decompose',
53
+ 7: 'Extract',
54
+ 8: 'Recapture',
55
+ 9: 'Journal',
56
+ 10: 'Present',
57
+ };
43
58
  /**
44
59
  * Append a JSONL log entry to .codeyam/logs/editor-log.jsonl
45
60
  */
@@ -185,6 +200,146 @@ function checkbox(text) {
185
200
  const highlighted = text.replace(/`([^`]+)`/g, (_m, code) => chalk.cyan(code));
186
201
  console.log(` ${chalk.dim('[ ]')} ${highlighted}`);
187
202
  }
203
+ /**
204
+ * Shared app scenario registration instructions used by editor Step 8 and migration Steps 1/6.
205
+ * Prints seed-based scenarios, mock-based fallback, @ file prefix, externalApis, url requirement, and error checking.
206
+ */
207
+ function printAppScenarioInstructions(pageName, route) {
208
+ checkbox('Check existing scenarios: `codeyam editor scenarios`');
209
+ console.log(chalk.dim(' Review existing scenarios — reuse and update their data rather than'));
210
+ console.log(chalk.dim(' creating duplicates. Re-register with the same name to update a scenario.'));
211
+ console.log();
212
+ console.log(chalk.bold.yellow('App scenarios vs component scenarios — these are DIFFERENT:'));
213
+ console.log(chalk.yellow(' App scenarios: show the FULL PAGE with seeded data at a real route (/, /library, etc.)'));
214
+ console.log(chalk.yellow(' Component scenarios: show ONE COMPONENT in isolation at /isolated-components/...'));
215
+ console.log(chalk.yellow(' This step is about APP scenarios. Do NOT set "componentName" — that makes it a component scenario.'));
216
+ console.log();
217
+ checkbox('Identify every page/route in the app and ensure each has app-level scenarios');
218
+ console.log(chalk.dim(" Check the app's router/entry points for all distinct pages"));
219
+ console.log(chalk.yellow(' Every page needs at least 2-3 app scenarios — not just the main page'));
220
+ if (pageName) {
221
+ console.log(chalk.dim(` Example: "${pageName} - Full Data", "${pageName} - Empty State"`));
222
+ }
223
+ else {
224
+ console.log(chalk.dim(' Example: "Page - Full Data", "Page - Empty State", "Page - Error State"'));
225
+ }
226
+ console.log();
227
+ checkbox('Register each scenario with type "application", the real page URL, and pageFilePath:');
228
+ console.log(chalk.dim(` codeyam editor register '{"name":"${pageName || 'Page'} - Full Data",`));
229
+ console.log(chalk.dim(` "type":"application","url":"${route || '/'}","pageFilePath":"src/path/to/Page.tsx",...}'`));
230
+ console.log(chalk.yellow(' Required fields: "type":"application", "url" (real route), "pageFilePath" (source file)'));
231
+ console.log(chalk.yellow(' Do NOT include "componentName" — that would make it a component scenario'));
232
+ console.log();
233
+ checkbox('Choose the right data approach for scenarios:');
234
+ console.log(chalk.dim(' With seed adapter: add "seed":{...} to populate the database for each state'));
235
+ console.log(chalk.dim(' Without seed adapter: add "mockData":{"routes":{"/api/...":{"body":[...]}}} to mock API responses'));
236
+ console.log(chalk.dim(' For external APIs (Stripe, weather, etc.): add "externalApis":{"GET https://...":{"body":[...],"status":200}}'));
237
+ checkbox('For large seed/mock data, write JSON to a temp file and use @ prefix:');
238
+ console.log(chalk.dim(' Write JSON to .codeyam/tmp/scenario.json then register with:'));
239
+ console.log(chalk.dim(' codeyam editor register @.codeyam/tmp/scenario.json'));
240
+ checkbox('If the app uses auth, email, or other patterns: see FEATURE_PATTERNS.md for scenario guidance');
241
+ checkbox('After each registration, check the response for `clientErrors`');
242
+ console.log(chalk.yellow(' If clientErrors is non-empty → fix the issue and re-register the scenario'));
243
+ console.log(chalk.yellow(' Fix client errors and re-register before moving on'));
244
+ }
245
+ /**
246
+ * Shared glossary instructions used by editor Step 6 and migration Step 8.
247
+ * Prints format, required fields, and example.
248
+ */
249
+ function printGlossaryInstructions(feature) {
250
+ console.log(chalk.bold.yellow('IMPORTANT: glossary.json is a plain JSON array — NOT an object wrapper.'));
251
+ checkbox("Read `.codeyam/glossary.json` (create if it doesn't exist)");
252
+ checkbox('Add an entry for each new function/component extracted');
253
+ checkbox('Each entry should have: name, filePath, description, parameters, returnType, tags, feature');
254
+ checkbox('For each function with a test file, set `testFile` to the relative path');
255
+ console.log();
256
+ console.log(chalk.bold('File format:'));
257
+ console.log(chalk.dim(' ['));
258
+ console.log(chalk.dim(' { "name": "calculateTotal", "filePath": "app/utils/pricing.ts",'));
259
+ console.log(chalk.dim(' "description": "Calculates total price including tax and discounts",'));
260
+ console.log(chalk.dim(' "parameters": [{ "name": "items", "type": "CartItem[]" }],'));
261
+ console.log(chalk.dim(' "returnType": "number", "tags": ["pricing"],'));
262
+ console.log(chalk.dim(' "testFile": "app/utils/pricing.test.ts",'));
263
+ console.log(chalk.dim(` "feature": "${feature || '...'}", "createdAt": "..." }`));
264
+ console.log(chalk.dim(' ]'));
265
+ }
266
+ /**
267
+ * Shared extraction plan instructions used by editor Step 4 and migration Step 6.
268
+ * Prints THE RULE, extractable categories, plan format requirements.
269
+ */
270
+ function printExtractionPlanInstructions() {
271
+ console.log(chalk.bold.red('THE RULE: No direct JSX in page files.'));
272
+ console.log(chalk.yellow(' After extraction, a page/route file should import and compose components — nothing else.'));
273
+ console.log(chalk.yellow(' Every distinct visual section in the page is its own component.'));
274
+ console.log(chalk.yellow(' Every component that renders multiple distinct sections should be split into sub-components.'));
275
+ console.log(chalk.yellow(' If a component has N visually distinct parts, it should compose N sub-components.'));
276
+ console.log();
277
+ console.log(chalk.bold('Checklist:'));
278
+ checkbox('Read `.codeyam/glossary.json` — note reusable functions/components');
279
+ checkbox('Read EVERY file used by this page/feature');
280
+ console.log(chalk.yellow(' For EACH file, identify EVERY extractable piece:'));
281
+ console.log(chalk.yellow(' Components: headers, nav, loading states, empty states, error states,'));
282
+ console.log(chalk.yellow(' badges/pills, image containers, card sections, form fields, modals,'));
283
+ console.log(chalk.yellow(' titles, descriptions, rating displays, status indicators, buttons'));
284
+ console.log(chalk.yellow(' Functions: data transforms, calculations, formatting, validation,'));
285
+ console.log(chalk.yellow(' API response shaping, any logic that is not directly about rendering'));
286
+ console.log(chalk.yellow(' Hooks: data fetching, state management, side effects'));
287
+ console.log();
288
+ checkbox('Write a numbered extraction plan listing EVERYTHING you will extract');
289
+ console.log(chalk.yellow(' The end state: every page file is ONLY imports + component composition.'));
290
+ console.log(chalk.yellow(' No raw <div>, <span>, <h1>, <p>, <img>, or <ul> in page files.'));
291
+ console.log(chalk.yellow(' Every visual section in every component is itself a component.'));
292
+ console.log();
293
+ console.log(chalk.yellow(' For each item in the plan, note:'));
294
+ console.log(chalk.yellow(' — What it is (component, function, hook)'));
295
+ console.log(chalk.yellow(' — Where it currently lives (source file + approximate lines)'));
296
+ console.log(chalk.yellow(' — Where it will go (new file path)'));
297
+ console.log();
298
+ console.log(chalk.dim('Present the numbered plan, then proceed to step 5 to execute it.'));
299
+ }
300
+ /**
301
+ * Shared component capture instructions used by editor Step 7 and migration Steps 3/8.
302
+ * Prints the full isolation route setup, codeyam-capture wrapper, register command, and error checking.
303
+ */
304
+ function printComponentCaptureInstructions() {
305
+ checkbox('Create isolation route dirs: `codeyam editor isolate ComponentA ComponentB ...`');
306
+ console.log(chalk.dim(' This creates app/codeyam-isolate/layout.tsx (with production notFound() guard) and'));
307
+ console.log(chalk.dim(' a directory per component. List ALL components that need isolation routes.'));
308
+ checkbox('For each visual component:');
309
+ console.log(chalk.dim(' 1. Read the source AND find where it is used in the app to understand:'));
310
+ console.log(chalk.dim(' — Props/interface'));
311
+ console.log(chalk.dim(' — Container width in the real app (e.g. card in a 3-col grid → max-w-sm)'));
312
+ console.log(chalk.dim(' 2. Plan multiple scenarios that exercise the component like tests:'));
313
+ console.log(chalk.dim(' — Default/happy path with typical data'));
314
+ console.log(chalk.dim(' — Edge cases: empty/missing data, long text, maximum items, zero counts'));
315
+ console.log(chalk.dim(' — Different visual states: loading, error, disabled, selected, hover'));
316
+ console.log(chalk.dim(' — Boundary values: single item vs many, min/max ratings, very long names'));
317
+ console.log(chalk.dim(' 3. Create ONE isolation route per component with a scenarios map and ?s= query param:'));
318
+ console.log(chalk.dim(' Remix: app/routes/codeyam-isolate.ComponentName.tsx → /codeyam-isolate/ComponentName'));
319
+ console.log(chalk.dim(' Next.js: app/codeyam-isolate/ComponentName/page.tsx → /codeyam-isolate/ComponentName'));
320
+ console.log(chalk.dim(' The route defines a `scenarios` object mapping scenario names to props,'));
321
+ console.log(chalk.dim(' reads `?s=ScenarioName` from the URL, and renders the component with those props.'));
322
+ console.log(chalk.dim(' Wrap the component in a capture container with id="codeyam-capture":'));
323
+ console.log(chalk.dim(' <div id="codeyam-capture" style={{ display:"inline-block", padding:"20px" }}>'));
324
+ console.log(chalk.dim(' <div style={{ width:"100%", maxWidth:"..." }}> ← match the app\'s container width'));
325
+ console.log(chalk.dim(' e.g. card in a 3-col grid → maxWidth:"24rem", full-width component → omit maxWidth'));
326
+ console.log(chalk.dim(' The screenshot captures just this wrapper, so the component fills the image.'));
327
+ console.log(chalk.dim(' Center the wrapper on the page (flexbox center both axes) and set a page background'));
328
+ console.log(chalk.dim(' color that matches where the component normally appears (e.g. white for light UIs).'));
329
+ console.log(chalk.dim(' 4. Wait 2 seconds for HMR, then register + capture each scenario:'));
330
+ console.log(chalk.dim(` codeyam editor register '{"name":"ComponentName - Scenario",`));
331
+ console.log(chalk.dim(` "componentName":"ComponentName","componentPath":"path/to/file.tsx",`));
332
+ console.log(chalk.dim(` "url":"/codeyam-isolate/ComponentName?s=Scenario",`));
333
+ console.log(chalk.dim(` "mockData":{"routes":{"/api/...":{"body":[...]}}}}'`));
334
+ console.log(chalk.yellow(' IMPORTANT: url MUST be an isolation route (/codeyam-isolate/... or /isolated-components/...).'));
335
+ console.log(chalk.yellow(' Do NOT use real page URLs (like /library) for component scenarios.'));
336
+ console.log(chalk.dim(' mockData.routes provides data for API calls the component makes internally'));
337
+ console.log(chalk.dim(' (omit mockData if the component has no internal API calls)'));
338
+ console.log(chalk.yellow(' 5. IMPORTANT: Check the register response for `clientErrors` array'));
339
+ console.log(chalk.yellow(' If clientErrors is non-empty → fix the isolation route and re-register'));
340
+ console.log(chalk.yellow(' Fix client errors and re-register before moving on'));
341
+ console.log(chalk.dim(' Isolation routes are committed to git — the layout guard blocks them in production'));
342
+ }
188
343
  /**
189
344
  * Print a section header.
190
345
  */
@@ -570,6 +725,42 @@ function printSetup(root) {
570
725
  // ─── Cycle overview (no args, has project) ────────────────────────────
571
726
  function printCycleOverview(root, state) {
572
727
  logEvent(root, 'overview', state ? { feature: state.feature, step: state.step } : {});
728
+ // Detect active migration (uses both editor-step.json and migration-state.json)
729
+ const migState = readMigrationState(root);
730
+ const resumeInfo = getMigrationResumeInfo(state
731
+ ? { step: state.step, label: state.label, migration: state.migration }
732
+ : null, migState);
733
+ if (resumeInfo.mode === 'survey') {
734
+ console.log();
735
+ console.log(chalk.bold.cyan('━━━ Editor Mode: Project Migration ━━━'));
736
+ console.log();
737
+ console.log(chalk.yellow('Migration survey in progress — Claude needs to explore the project.'));
738
+ console.log();
739
+ console.log(chalk.green('Continue with: ') + chalk.bold(resumeInfo.resumeCommand));
740
+ console.log();
741
+ return;
742
+ }
743
+ if (resumeInfo.mode === 'page-in-progress') {
744
+ console.log();
745
+ console.log(chalk.bold.cyan('━━━ Editor Mode: Project Migration ━━━'));
746
+ console.log();
747
+ console.log(chalk.yellow(`Migration: Page ${resumeInfo.pageIndex + 1}/${resumeInfo.totalPages} (${resumeInfo.pageName})` +
748
+ (resumeInfo.step
749
+ ? `, Step ${resumeInfo.step} (${resumeInfo.stepLabel})`
750
+ : '')));
751
+ console.log();
752
+ console.log(chalk.green('Continue with: ') + chalk.bold(resumeInfo.resumeCommand));
753
+ console.log();
754
+ return;
755
+ }
756
+ if (resumeInfo.mode === 'complete') {
757
+ console.log();
758
+ console.log(chalk.bold.cyan('━━━ Editor Mode: Feature Cycle ━━━'));
759
+ console.log();
760
+ console.log(chalk.green('Migration complete! Start building features:'));
761
+ console.log();
762
+ // Fall through to normal cycle display
763
+ }
573
764
  console.log();
574
765
  console.log(chalk.bold.cyan('━━━ Editor Mode: Feature Cycle ━━━'));
575
766
  console.log();
@@ -896,34 +1087,7 @@ function printStep4(root, feature) {
896
1087
  console.log(chalk.bold('Goal: pages contain ONLY components. Components contain ONLY sub-components.'));
897
1088
  console.log(chalk.yellow('This step is read and plan only. Code extraction happens in step 5.'));
898
1089
  console.log();
899
- console.log(chalk.bold.red('THE RULE: No direct JSX in page files.'));
900
- console.log(chalk.yellow(' After extraction, a page/route file should import and compose components — nothing else.'));
901
- console.log(chalk.yellow(' Every distinct visual section in the page is its own component.'));
902
- console.log(chalk.yellow(' Every component that renders multiple distinct sections should be split into sub-components.'));
903
- console.log(chalk.yellow(' If a component has N visually distinct parts, it should compose N sub-components.'));
904
- console.log();
905
- console.log(chalk.bold('Checklist:'));
906
- checkbox('Read `.codeyam/glossary.json` — note reusable functions/components');
907
- checkbox('Read EVERY file created or modified in this session');
908
- console.log(chalk.yellow(' For EACH file, identify EVERY extractable piece:'));
909
- console.log(chalk.yellow(' Components: headers, nav, loading states, empty states, error states,'));
910
- console.log(chalk.yellow(' badges/pills, image containers, card sections, form fields, modals,'));
911
- console.log(chalk.yellow(' titles, descriptions, rating displays, status indicators, buttons'));
912
- console.log(chalk.yellow(' Functions: data transforms, calculations, formatting, validation,'));
913
- console.log(chalk.yellow(' API response shaping, any logic that is not directly about rendering'));
914
- console.log(chalk.yellow(' Hooks: data fetching, state management, side effects'));
915
- console.log();
916
- checkbox('Write a numbered extraction plan listing EVERYTHING you will extract');
917
- console.log(chalk.yellow(' The end state: every page file is ONLY imports + component composition.'));
918
- console.log(chalk.yellow(' No raw <div>, <span>, <h1>, <p>, <img>, or <ul> in page files.'));
919
- console.log(chalk.yellow(' Every visual section in every component is itself a component.'));
920
- console.log();
921
- console.log(chalk.yellow(' For each item in the plan, note:'));
922
- console.log(chalk.yellow(' — What it is (component, function, hook)'));
923
- console.log(chalk.yellow(' — Where it currently lives (source file + approximate lines)'));
924
- console.log(chalk.yellow(' — Where it will go (new file path)'));
925
- console.log();
926
- console.log(chalk.dim('Present the numbered plan, then proceed to step 5 to execute it.'));
1090
+ printExtractionPlanInstructions();
927
1091
  stopGate(4);
928
1092
  }
929
1093
  // ─── Step 5: Extract ──────────────────────────────────────────────────
@@ -998,20 +1162,7 @@ function printStep6(root, feature) {
998
1162
  }
999
1163
  console.log('Record all new functions/components in `.codeyam/glossary.json`.');
1000
1164
  console.log();
1001
- console.log(chalk.bold('Checklist:'));
1002
- checkbox("Read `.codeyam/glossary.json` (create if it doesn't exist)");
1003
- checkbox('Add an entry for each new function/component extracted in step 5');
1004
- checkbox('Each entry should have: name, filePath, description, parameters, returnType, tags, feature');
1005
- checkbox('Every non-component entry MUST have `testFile` set — hooks and functions all need tests');
1006
- console.log(chalk.yellow(' If a function/hook has no test yet, go back and write one (TDD). The audit will block you.'));
1007
- console.log();
1008
- console.log(chalk.bold('Entry format:'));
1009
- console.log(chalk.dim(' { "name": "calculateTotal", "filePath": "app/utils/pricing.ts",'));
1010
- console.log(chalk.dim(' "description": "Calculates total price including tax and discounts",'));
1011
- console.log(chalk.dim(' "parameters": [{ "name": "items", "type": "CartItem[]" }],'));
1012
- console.log(chalk.dim(' "returnType": "number", "tags": ["pricing"],'));
1013
- console.log(chalk.dim(' "testFile": "app/utils/pricing.test.ts",'));
1014
- console.log(chalk.dim(` "feature": "${feature}", "createdAt": "..." }`));
1165
+ printGlossaryInstructions(feature);
1015
1166
  console.log();
1016
1167
  console.log(chalk.dim('Focus on updating the glossary. Application code and scenarios come in later steps.'));
1017
1168
  stopGate(6);
@@ -1039,56 +1190,11 @@ function printStep7(root, feature) {
1039
1190
  console.log();
1040
1191
  console.log(chalk.bold('Visual Components — Component Isolation:'));
1041
1192
  checkbox('List all files with new/modified visual components from step 5');
1042
- checkbox('Create isolation route dirs: `codeyam editor isolate "ComponentA ComponentB ..."`');
1043
- console.log(chalk.dim(' This creates app/isolated-components/layout.tsx (with production notFound() guard) and'));
1044
- console.log(chalk.dim(' a directory per component. List ALL components that need isolation routes.'));
1045
1193
  checkbox('Check existing scenarios: `codeyam editor scenarios`');
1046
1194
  console.log(chalk.dim(' Reuse and improve existing scenarios where possible — update mock data'));
1047
1195
  console.log(chalk.dim(' to reflect current changes. Add new scenarios only for genuinely new states.'));
1048
1196
  console.log(chalk.dim(' Ensure at least one scenario clearly demonstrates what changed in this session.'));
1049
- checkbox('For each visual component:');
1050
- console.log(chalk.dim(' 1. Read the source AND find where it is used in the app to understand:'));
1051
- console.log(chalk.dim(' — Props/interface'));
1052
- console.log(chalk.dim(' — Container width in the real app (e.g. card in a 3-col grid → max-w-sm)'));
1053
- console.log(chalk.dim(' 2. Plan multiple scenarios that exercise the component like tests:'));
1054
- console.log(chalk.dim(' — Default/happy path with typical data'));
1055
- console.log(chalk.dim(' — Edge cases: empty/missing data, long text, maximum items, zero counts'));
1056
- console.log(chalk.dim(' — Different visual states: loading, error, disabled, selected, hover'));
1057
- console.log(chalk.dim(' — Boundary values: single item vs many, min/max ratings, very long names'));
1058
- console.log(chalk.dim(' 3. Create ONE isolation route per component with a scenarios map and ?s= query param:'));
1059
- console.log(chalk.dim(' Remix: app/routes/isolated-components.ComponentName.tsx → /isolated-components/ComponentName'));
1060
- console.log(chalk.dim(' Next.js: app/isolated-components/ComponentName/page.tsx → /isolated-components/ComponentName'));
1061
- console.log(chalk.dim(' The route defines a `scenarios` object mapping scenario names to props,'));
1062
- console.log(chalk.dim(' reads `?s=ScenarioName` from the URL, and renders the component with those props.'));
1063
- console.log(chalk.dim(' Wrap the component in a capture container with id="codeyam-capture":'));
1064
- console.log(chalk.dim(' <div id="codeyam-capture" style={{ display:"inline-block", padding:"20px" }}>'));
1065
- console.log(chalk.dim(' <div style={{ width:"100%", maxWidth:"..." }}> ← match the app\'s container width'));
1066
- console.log(chalk.dim(' e.g. card in a 3-col grid → maxWidth:"24rem", full-width component → omit maxWidth'));
1067
- console.log(chalk.dim(' The screenshot captures just this wrapper, so the component fills the image.'));
1068
- console.log(chalk.dim(' Center the wrapper on the page (flexbox center both axes) and set a page background'));
1069
- console.log(chalk.dim(' color that matches where the component normally appears (e.g. white for light UIs).'));
1070
- console.log(chalk.dim(' 4. Wait 2 seconds for HMR, then register + capture each scenario:'));
1071
- console.log(chalk.dim(` codeyam editor register '{"name":"ComponentName - Scenario",`));
1072
- console.log(chalk.dim(` "componentName":"ComponentName","componentPath":"path/to/file.tsx",`));
1073
- console.log(chalk.dim(` "url":"/isolated-components/ComponentName?s=Scenario",`));
1074
- console.log(chalk.dim(` "dimensions":["${dim}"],`));
1075
- console.log(chalk.dim(` "mockData":{"routes":{"/api/...":{"body":[...]}}}}'`));
1076
- console.log(chalk.yellow(' ALWAYS include "dimensions" — use the named screen size that matches the component\'s context.'));
1077
- console.log(chalk.dim(dimNames.length > 0
1078
- ? ` Available dimensions: ${dimNames.map((n) => `"${n}"`).join(', ')}. Default: "${dim}".`
1079
- : ` Use "${dim}" for most components. Names must match a key in screenSizes from setup.`));
1080
- console.log(chalk.dim(' Names must match a key in screenSizes from setup. If you need a new size, add it first:'));
1081
- console.log(chalk.dim(` curl -s -X POST http://localhost:${port}/api/editor-project-info -H "Content-Type: application/json" \\`));
1082
- console.log(chalk.dim(` -d '{"screenSizes":{...existing..., "New Name":{"width":W,"height":H}}}' (replaces all — include every size)`));
1083
- console.log(chalk.dim(' url is a PATH (starts with /) — the proxy routes it and intercepts API calls'));
1084
- console.log(chalk.dim(' mockData.routes provides data for API calls the component makes internally'));
1085
- console.log(chalk.dim(' (omit mockData if the component has no internal API calls)'));
1086
- console.log(chalk.dim(' For large JSON: use your Write tool to create .codeyam/tmp/scenario.json,'));
1087
- console.log(chalk.dim(' then: codeyam editor register @.codeyam/tmp/scenario.json'));
1088
- console.log(chalk.yellow(' 5. IMPORTANT: Check the register response for `clientErrors` array'));
1089
- console.log(chalk.yellow(' If clientErrors is non-empty → fix the isolation route and re-register'));
1090
- console.log(chalk.yellow(' Fix client errors and re-register before moving on'));
1091
- console.log(chalk.dim(' Isolation routes are committed to git — the layout guard blocks them in production'));
1197
+ printComponentCaptureInstructions();
1092
1198
  console.log();
1093
1199
  console.log(chalk.bold('Library Functions — run tests:'));
1094
1200
  checkbox('Run ALL test files created in step 5');
@@ -1143,51 +1249,11 @@ function printStep8(root, feature) {
1143
1249
  console.log(chalk.cyan(" • New data states that can't coexist in one scenario (empty vs rich, error vs success) → new scenario"));
1144
1250
  console.log(chalk.cyan(" • Don't duplicate — if an existing scenario can cover a state with richer data, enhance it instead"));
1145
1251
  console.log();
1146
- console.log(chalk.bold('Checklist:'));
1147
- checkbox('Run `codeyam editor scenarios` review all existing scenarios');
1148
- checkbox("Create new scenarios for any pages that don't have scenarios yet");
1149
- console.log(chalk.yellow(' Each page needs its own scenarios a detail page needs different data than the catalog'));
1150
- checkbox('Re-register every existing scenario whose page was affected by this feature');
1151
- console.log(chalk.dim(' Use the SAME name to update. Enhance seed data to showcase this feature where relevant.'));
1152
- console.log(chalk.yellow(" Don't just re-register unchanged data — enrich it so the feature is thoroughly demonstrated."));
1153
- checkbox('ALWAYS include "dimensions" — pick the viewport that best demonstrates the page');
1154
- console.log(chalk.yellow(' Every app scenario MUST have a "dimensions" field referencing a named screen size from setup.'));
1155
- console.log(chalk.dim(dimNames.length > 0
1156
- ? ` Available dimensions: ${dimNames.map((n) => `"${n}"`).join(', ')}. Default: "${dim}".`
1157
- : ` Use "${dim}" for most scenarios. Names must match a key in screenSizes from setup.`));
1158
- if (dimNames.length > 1) {
1159
- const otherDim = dimNames.find((n) => n !== dim) || dimNames[1];
1160
- console.log(chalk.yellow(` Think about what fits each scenario: full pages/standalone views → "${otherDim}", popups/widgets → "${dim}".`));
1161
- }
1162
- console.log(chalk.dim(' For responsive apps, include multiple dimensions: "dimensions":["Desktop","Mobile"] captures both viewports.'));
1163
- console.log(chalk.dim(' If you need a new named size mid-workflow, add it via editor-project-info API (include ALL existing sizes — the API replaces, not merges).'));
1164
- checkbox('If the project has a database + seed adapter, use seed-based scenarios:');
1165
- console.log(chalk.dim(` codeyam editor register '{"name":"Full Catalog","type":"application","url":"/","dimensions":["${dim}"],"seed":{"products":[...],"categories":[...]}}'`));
1166
- console.log(chalk.dim(' Seed data is written to the DB via the seed adapter. Real app renders real data.'));
1167
- checkbox('IMPORTANT: Always include "url" — the page path to screenshot for this scenario');
1168
- console.log(chalk.dim(' Use "/" for home page, "/drinks/1" for detail pages, etc.'));
1169
- console.log(chalk.dim(' Without url, the screenshot captures the root page regardless of the scenario.'));
1170
- console.log(chalk.yellow(' Create separate scenarios for each page that shows different data.'));
1171
- checkbox('For large seed data, use your Write tool to create .codeyam/tmp/scenario.json, then:');
1172
- console.log(chalk.dim(' codeyam editor register @.codeyam/tmp/scenario.json'));
1173
- console.log(chalk.yellow(' IMPORTANT: Use the Write tool — NOT cat/heredoc — to avoid permission prompts'));
1174
- checkbox('For external API mocks (Stripe, weather, etc.), add externalApis:');
1175
- console.log(chalk.dim(` "externalApis":{"GET https://api.stripe.com/v1/prices":{"body":[...],"status":200}}`));
1176
- checkbox('If the app uses auth, email, or other patterns: see FEATURE_PATTERNS.md for scenario guidance');
1177
- checkbox('If the app uses localStorage/sessionStorage instead of a database, use localStorage scenarios:');
1178
- console.log(chalk.dim(` codeyam editor register '{"name":"Full Library","type":"application","url":"/","dimensions":["${dim}"],"localStorage":{"articles":[...],"collections":[...]}}'`));
1179
- console.log(chalk.dim(' The proxy injects data into localStorage before the app loads. Fully interactive — the app can read and write normally.'));
1180
- console.log(chalk.dim(' For an empty state, register with an empty localStorage or omit it entirely.'));
1181
- checkbox('If no database and no localStorage, use component-style mock scenarios:');
1182
- console.log(chalk.dim(` codeyam editor register '{"name":"Empty","description":"...","url":"/","dimensions":["${dim}"],"mockData":{"routes":{"/api/...":{"body":[...]}}}}'`));
1183
- checkbox('After each registration, check the response for `clientErrors`');
1184
- console.log(chalk.yellow(' If clientErrors is non-empty → fix the issue and re-register the scenario'));
1185
- console.log(chalk.yellow(' Fix client errors and re-register before moving on'));
1186
- checkbox('CRITICAL: After EACH registration, view the captured screenshot to verify it');
1187
- console.log(chalk.yellow(' Read the screenshot file path from the registration response and view it.'));
1188
- console.log(chalk.yellow(' Does the screenshot actually SHOW the data you put in? If you seeded 5 articles'));
1189
- console.log(chalk.yellow(' but the screenshot shows an empty page, the scenario is broken — fix the code or data.'));
1190
- console.log(chalk.yellow(' Do NOT create scenarios with data the app cannot yet render.'));
1252
+ checkbox('Ensure scenarios clearly demonstrate what changed in this session');
1253
+ console.log(chalk.dim(' If data models changed: update existing scenarios seed data to match'));
1254
+ console.log(chalk.dim(' If UI changed: re-register existing scenarios so screenshots reflect the update'));
1255
+ console.log(chalk.dim(' Add new scenarios only for genuinely new data states not covered by existing ones'));
1256
+ printAppScenarioInstructions();
1191
1257
  console.log();
1192
1258
  console.log(chalk.dim('Focus on creating and registering app-level scenarios. Code fixes happen in step 10 if needed.'));
1193
1259
  console.log();
@@ -1347,73 +1413,686 @@ function printStep12(root, feature) {
1347
1413
  }
1348
1414
  console.log('Verify all screenshots and checks pass before presenting to the user.');
1349
1415
  console.log();
1350
- console.log(chalk.bold('Checklist (do all of this silently):'));
1351
- checkbox(`Refresh the preview: \`codeyam editor preview '{"dimension":"${dim}"}'\``);
1352
- printDimensionGuidance(dim, dimNames);
1353
- checkbox('Verify each component has screenshots in the App tab (grouped under Components)');
1354
- checkbox('If any are missing, re-register them using `codeyam editor register`');
1355
- checkbox(`Check for client errors: \`codeyam editor client-errors\``);
1356
- checkbox('If `hasErrors` is true, fix them and re-capture affected scenarios');
1357
- checkbox('Run `codeyam editor verify-images \'{"paths":["/"], "imageUrls":["url1"]}\'` with all page paths and image URLs');
1358
- checkbox('Fix or remove any broken images before continuing');
1359
- checkbox('Run `codeyam editor audit` to verify completeness of scenarios and tests');
1360
- checkbox('Do not proceed until all checks pass');
1361
- stopGate(12);
1416
+ console.log(chalk.bold('Checklist (do all of this silently):'));
1417
+ checkbox(`Refresh the preview: \`codeyam editor preview '{"dimension":"${dim}"}'\``);
1418
+ printDimensionGuidance(dim, dimNames);
1419
+ checkbox('Verify each component has screenshots in the App tab (grouped under Components)');
1420
+ checkbox('If any are missing, re-register them using `codeyam editor register`');
1421
+ checkbox(`Check for client errors: \`codeyam editor client-errors\``);
1422
+ checkbox('If `hasErrors` is true, fix them and re-capture affected scenarios');
1423
+ checkbox('Run `codeyam editor verify-images \'{"paths":["/"], "imageUrls":["url1"]}\'` with all page paths and image URLs');
1424
+ checkbox('Fix or remove any broken images before continuing');
1425
+ checkbox('Run `codeyam editor audit` to verify completeness of scenarios and tests');
1426
+ checkbox('Do not proceed until all checks pass');
1427
+ stopGate(12);
1428
+ }
1429
+ // ─── Step 13: Present ─────────────────────────────────────────────────
1430
+ function printStep13(root, feature) {
1431
+ const port = getServerPort();
1432
+ const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
1433
+ const prevState = readState(root);
1434
+ const isResuming = prevState?.step === 13;
1435
+ const now = new Date().toISOString();
1436
+ writeState(root, {
1437
+ feature,
1438
+ step: 13,
1439
+ label: STEP_LABELS[13],
1440
+ startedAt: isResuming ? prevState.startedAt : now,
1441
+ featureStartedAt: prevState?.featureStartedAt || now,
1442
+ });
1443
+ logEvent(root, 'step', { step: 13, label: 'Present', feature });
1444
+ stepHeader(13, 'Present', feature);
1445
+ if (isResuming) {
1446
+ printResumptionHeader(13);
1447
+ }
1448
+ console.log('Present the results to the user and get their approval.');
1449
+ console.log();
1450
+ console.log(chalk.bold('Checklist:'));
1451
+ checkbox('Fetch all scenarios: `codeyam editor scenarios`');
1452
+ console.log(chalk.dim(' Each scenario has a `changeStatus` field: "new", "edited", "impacted", or null.'));
1453
+ checkbox('Pick the best scenario to showcase from this working session:');
1454
+ console.log(chalk.dim(' Prefer scenarios with changeStatus "new" or "edited" (directly changed in this session).'));
1455
+ console.log(chalk.dim(' For app-level scenarios, prefer those showing the new/changed functionality.'));
1456
+ console.log(chalk.dim(' NEVER pick a scenario with changeStatus null — those are unchanged from a previous commit.'));
1457
+ checkbox('Switch the preview to that scenario using its `id` and `dimension`:');
1458
+ console.log(chalk.dim(` codeyam editor preview '{"scenarioId":"<id>","dimension":"${dim}"}'`));
1459
+ console.log(chalk.dim(' Use the dimension that matches the scenario — check its registered dimensions.'));
1460
+ printDimensionGuidance(dim, dimNames);
1461
+ checkbox(`Show the results panel: \`codeyam editor show-results\``);
1462
+ console.log(chalk.dim(' This opens a visual panel below the terminal showing all scenarios with screenshots.'));
1463
+ console.log(chalk.dim(' The user can click scenarios to switch the live preview.'));
1464
+ checkbox('Write a 1-2 sentence summary of what was built');
1465
+ checkbox('Report test count and audit status (one line)');
1466
+ console.log();
1467
+ console.log(chalk.bold('Present a selection menu to the user (use AskUserQuestion with these EXACT option labels):'));
1468
+ console.log(chalk.green(' Option 1 label: "Save & commit"') +
1469
+ chalk.dim(' — git commit all changes and record in journal'));
1470
+ console.log(chalk.yellow(' Option 2 label: "I\'d like to make some changes"') +
1471
+ chalk.dim(' — describe changes, then re-verify'));
1472
+ console.log();
1473
+ console.log(chalk.bold('If the user chooses "Save & commit":'));
1474
+ checkbox('Advance to the commit step: `codeyam editor 14`');
1475
+ console.log();
1476
+ console.log(chalk.bold('If the user chooses "Make changes" (or asks for ANY change, even as a question):'));
1477
+ checkbox(`Hide the results panel: \`codeyam editor hide-results\``);
1478
+ checkbox('Ask what changes the user wants (if not already clear)');
1479
+ checkbox(`Run: \`codeyam editor change "${feature}"\` — this gives you the change checklist`);
1480
+ checkbox('THEN make the requested changes and follow the checklist');
1481
+ console.log(chalk.red.bold(' IMPORTANT: Always run the change command BEFORE writing any code.'));
1482
+ stopGate(13, { confirm: true });
1483
+ }
1484
+ // ─── Migration Mode ──────────────────────────────────────────────────
1485
+ /**
1486
+ * Print a progress tracker for the 8 migration steps.
1487
+ */
1488
+ function printMigrationProgressTracker(current, pageName, pageIndex, totalPages) {
1489
+ console.log();
1490
+ console.log(chalk.dim(` Migration: Page ${pageIndex + 1}/${totalPages} (${pageName})`));
1491
+ console.log(chalk.dim(' ┌─────────────────────────────────────┐'));
1492
+ for (let i = 1; i <= 10; i++) {
1493
+ const label = MIGRATION_STEP_LABELS[i];
1494
+ const num = i < 10 ? ` ${i}` : `${i}`;
1495
+ const content = `${num}. ${label.padEnd(28)}`;
1496
+ if (i < current) {
1497
+ console.log(chalk.dim(' │') + chalk.green(` ✓ ${content}`) + chalk.dim('│'));
1498
+ }
1499
+ else if (i === current) {
1500
+ console.log(chalk.dim(' │') + chalk.bold.cyan(` → ${content}`) + chalk.dim('│'));
1501
+ }
1502
+ else {
1503
+ console.log(chalk.dim(` │ ○ ${content}│`));
1504
+ }
1505
+ }
1506
+ console.log(chalk.dim(' └─────────────────────────────────────┘'));
1507
+ }
1508
+ /**
1509
+ * Print a STOP gate for migration steps.
1510
+ */
1511
+ function migrationStopGate(current, pageName, pageIndex, totalPages, opts) {
1512
+ console.log();
1513
+ console.log(chalk.bold.red('━━━ STOP ━━━'));
1514
+ console.log();
1515
+ console.log(chalk.red('Complete each checklist item above before proceeding to the next step.'));
1516
+ if (opts?.confirm) {
1517
+ console.log();
1518
+ console.log(chalk.yellow('Wait for user confirmation before moving on.'));
1519
+ }
1520
+ console.log();
1521
+ console.log(chalk.bold.yellow('Present the following progress tracker to the user (copy it verbatim):'));
1522
+ printMigrationProgressTracker(current, pageName, pageIndex, totalPages);
1523
+ console.log();
1524
+ console.log(chalk.yellow('For the CURRENT step (→), show each checklist item with ✓ (done) or ✗ (skipped + reason).'));
1525
+ console.log();
1526
+ if (current === 5) {
1527
+ // Step 5 (Discuss) can skip to step 9 if user declines decomposition
1528
+ console.log(chalk.green('If decomposing, run: ') +
1529
+ chalk.bold('codeyam editor migrate 6'));
1530
+ console.log(chalk.green('If skipping decomposition, run: ') +
1531
+ chalk.bold('codeyam editor migrate 9'));
1532
+ }
1533
+ else if (current < 10) {
1534
+ console.log(chalk.green('When done, run: ') +
1535
+ chalk.bold(`codeyam editor migrate ${current + 1}`));
1536
+ }
1537
+ else {
1538
+ console.log(chalk.green('Page complete! Run: ') +
1539
+ chalk.bold('codeyam editor migrate next') +
1540
+ chalk.green(' to start the next page'));
1541
+ }
1542
+ console.log();
1543
+ }
1544
+ /**
1545
+ * Write migration-aware editor state.
1546
+ */
1547
+ function writeMigrationStepState(root, step, pageName, pageIndex, totalPages) {
1548
+ const prevState = readState(root);
1549
+ const isResuming = prevState?.step === step && !!prevState?.migration;
1550
+ const now = new Date().toISOString();
1551
+ writeState(root, {
1552
+ feature: `Migration: ${pageName}`,
1553
+ step,
1554
+ label: MIGRATION_STEP_LABELS[step],
1555
+ startedAt: isResuming ? prevState.startedAt : now,
1556
+ featureStartedAt: prevState?.featureStartedAt || now,
1557
+ migration: {
1558
+ pageName,
1559
+ pageIndex,
1560
+ totalPages,
1561
+ },
1562
+ });
1563
+ logEvent(root, 'step', {
1564
+ step,
1565
+ label: MIGRATION_STEP_LABELS[step],
1566
+ feature: `Migration: ${pageName}`,
1567
+ migration: true,
1568
+ });
1569
+ }
1570
+ /**
1571
+ * Get the current migration page info from state, or return null.
1572
+ */
1573
+ function getCurrentMigrationPage(root) {
1574
+ const migState = readMigrationState(root);
1575
+ if (!migState || migState.pages.length === 0)
1576
+ return null;
1577
+ const page = migState.pages[migState.currentPageIndex];
1578
+ if (!page)
1579
+ return null;
1580
+ return {
1581
+ pageName: page.name,
1582
+ pageIndex: migState.currentPageIndex,
1583
+ totalPages: migState.pages.length,
1584
+ };
1585
+ }
1586
+ // ─── Migration Step 1: Survey ───────────────────────────────────────
1587
+ function printMigrateStep1(root) {
1588
+ const pageInfo = getCurrentMigrationPage(root);
1589
+ if (!pageInfo) {
1590
+ console.error(chalk.red('Error: No migration in progress. Run `codeyam editor migrate` first.'));
1591
+ process.exit(1);
1592
+ }
1593
+ const { pageName, pageIndex, totalPages } = pageInfo;
1594
+ writeMigrationStepState(root, 1, pageName, pageIndex, totalPages);
1595
+ const migState = readMigrationState(root);
1596
+ const page = migState.pages[pageIndex];
1597
+ console.log();
1598
+ console.log(chalk.bold.cyan(`━━━ Migration Step 1: Survey — ${pageName} (${pageIndex + 1}/${totalPages}) ━━━`));
1599
+ console.log();
1600
+ console.log(chalk.bold('Goal: Survey the page, understand its structure, and start the dev server.'));
1601
+ console.log();
1602
+ console.log(chalk.bold('Survey:'));
1603
+ checkbox(`Read the page file: \`${page.filePath}\``);
1604
+ checkbox('Follow every import — read all local components, hooks, utilities used by this page');
1605
+ checkbox('Map the data flow: where does data come from? Server actions? API routes? Props?');
1606
+ checkbox('Check `.codeyam/glossary.json` for components already extracted from previous pages');
1607
+ checkbox('Check `.codeyam/migration-state.json` sharedComponents for reusable pieces');
1608
+ checkbox('Note the page route and any dynamic segments');
1609
+ console.log();
1610
+ console.log(chalk.bold('Dev Server:'));
1611
+ checkbox('If the dev server is not running yet, figure out how to start it (check package.json scripts)');
1612
+ checkbox('FIRST: Verify dependencies are installed (check if node_modules/ exists and is non-empty)');
1613
+ console.log(chalk.yellow(' If node_modules is missing or sparse, run the package manager install command first'));
1614
+ console.log(chalk.dim(' Missing deps cause cryptic 500 errors at render time — install BEFORE starting the server'));
1615
+ checkbox('Start the dev server: `codeyam editor dev-server \'{"action":"start"}\'`');
1616
+ checkbox("Verify it's running: check for successful startup output");
1617
+ console.log();
1618
+ migrationStopGate(1, pageName, pageIndex, totalPages);
1619
+ }
1620
+ // ─── Migration Step 2: App Scenarios ────────────────────────────────
1621
+ function printMigrateStep2(root) {
1622
+ const pageInfo = getCurrentMigrationPage(root);
1623
+ if (!pageInfo) {
1624
+ console.error(chalk.red('Error: No migration in progress. Run `codeyam editor migrate` first.'));
1625
+ process.exit(1);
1626
+ }
1627
+ const { pageName, pageIndex, totalPages } = pageInfo;
1628
+ writeMigrationStepState(root, 2, pageName, pageIndex, totalPages);
1629
+ const migState = readMigrationState(root);
1630
+ const page = migState.pages[pageIndex];
1631
+ console.log();
1632
+ console.log(chalk.bold.cyan(`━━━ Migration Step 2: App Scenarios — ${pageName} (${pageIndex + 1}/${totalPages}) ━━━`));
1633
+ console.log();
1634
+ console.log(chalk.bold(`Goal: Register application scenarios that screenshot the ACTUAL page at "${page.route}".`));
1635
+ console.log();
1636
+ console.log(chalk.yellow(' App scenarios capture how the full page looks with different data states.'));
1637
+ console.log(chalk.yellow(` Every scenario MUST use the real page route ("${page.route}") — NOT isolation routes.`));
1638
+ console.log(chalk.yellow(' These are the primary scenarios the user will see for this page.'));
1639
+ console.log();
1640
+ console.log(chalk.bold('Seed Adapter Setup:'));
1641
+ checkbox('Check if a seed adapter exists: `ls .codeyam/seed-adapter.ts 2>/dev/null`');
1642
+ console.log(chalk.yellow(' If NO seed adapter exists, create one. The seed adapter wipes and re-seeds the database'));
1643
+ console.log(chalk.yellow(' for each scenario, enabling different data states on the real page.'));
1644
+ console.log();
1645
+ console.log(chalk.dim(' How to create a seed adapter:'));
1646
+ console.log(chalk.dim(' 1. Identify the database technology from your step 1 survey (Prisma, Supabase, Drizzle, etc.)'));
1647
+ console.log(chalk.dim(' 2. Check for a matching template: `ls .codeyam/seed-adapters/`'));
1648
+ console.log(chalk.dim(' 3. For Supabase: `cp .codeyam/seed-adapters/supabase.ts .codeyam/seed-adapter.ts`'));
1649
+ console.log(chalk.dim(' The seed adapter auto-detects Supabase credentials by scanning env var VALUES (not names).'));
1650
+ console.log(chalk.dim(' It reads .env / .env.local and finds: *.supabase.co URLs, sb_secret_* keys, and legacy JWTs.'));
1651
+ console.log(chalk.dim(' Do NOT grep for specific env var names — just register a scenario and try it.'));
1652
+ console.log(chalk.dim(' If it fails, the error message will say exactly what credentials are missing.'));
1653
+ console.log(chalk.dim(' 4. For Prisma: write a seed adapter that uses your Prisma client to delete/insert rows'));
1654
+ console.log(chalk.dim(' 5. For other databases: write a .codeyam/seed-adapter.ts that reads a JSON file (argv[2]),'));
1655
+ console.log(chalk.dim(' deletes all rows from each table, then inserts the seed rows. Format: {"table": [{...}]}'));
1656
+ console.log(chalk.dim(' 6. Test it: write a small test seed JSON and run `npx tsx .codeyam/seed-adapter.ts test.json`'));
1657
+ console.log();
1658
+ printAppScenarioInstructions(pageName, page.route);
1659
+ console.log();
1660
+ migrationStopGate(2, pageName, pageIndex, totalPages);
1661
+ }
1662
+ // ─── Migration Step 3: Component Scenarios ──────────────────────────
1663
+ function printMigrateStep3(root) {
1664
+ const pageInfo = getCurrentMigrationPage(root);
1665
+ if (!pageInfo) {
1666
+ console.error(chalk.red('Error: No migration in progress. Run `codeyam editor migrate` first.'));
1667
+ process.exit(1);
1668
+ }
1669
+ const { pageName, pageIndex, totalPages } = pageInfo;
1670
+ writeMigrationStepState(root, 3, pageName, pageIndex, totalPages);
1671
+ console.log();
1672
+ console.log(chalk.bold.cyan(`━━━ Migration Step 3: Component Scenarios — ${pageName} (${pageIndex + 1}/${totalPages}) ━━━`));
1673
+ console.log();
1674
+ console.log(chalk.bold('Goal: Capture individual component scenarios for major visual components on this page.'));
1675
+ console.log();
1676
+ console.log(chalk.dim(' Component scenarios use isolation routes with a codeyam-capture wrapper for tight screenshots.'));
1677
+ console.log(chalk.dim(' These supplement the app scenarios from step 2 with focused component-level views.'));
1678
+ console.log();
1679
+ printComponentCaptureInstructions();
1680
+ console.log();
1681
+ migrationStopGate(3, pageName, pageIndex, totalPages);
1682
+ }
1683
+ // ─── Migration Step 4: Preview ──────────────────────────────────────
1684
+ function printMigrateStep4(root) {
1685
+ const pageInfo = getCurrentMigrationPage(root);
1686
+ if (!pageInfo) {
1687
+ console.error(chalk.red('Error: No migration in progress.'));
1688
+ process.exit(1);
1689
+ }
1690
+ const { pageName, pageIndex, totalPages } = pageInfo;
1691
+ writeMigrationStepState(root, 4, pageName, pageIndex, totalPages);
1692
+ const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
1693
+ console.log();
1694
+ console.log(chalk.bold.cyan(`━━━ Migration Step 4: Preview — ${pageName} (${pageIndex + 1}/${totalPages}) ━━━`));
1695
+ console.log();
1696
+ console.log(chalk.bold('Goal: Verify screenshots look correct and show results to the user.'));
1697
+ console.log();
1698
+ console.log(chalk.bold('Checklist:'));
1699
+ checkbox('Review all scenario screenshots for this page — do they look correct?');
1700
+ checkbox('Check for client errors: `codeyam editor client-errors`');
1701
+ checkbox('If any issues: fix the scenario data, re-register affected scenarios');
1702
+ checkbox('Fetch all scenarios: `codeyam editor scenarios`');
1703
+ checkbox('Pick the best scenario to showcase:');
1704
+ console.log(chalk.dim(` codeyam editor preview '{"scenarioId":"<id>","dimension":"${dim}"}'`));
1705
+ printDimensionGuidance(dim, dimNames);
1706
+ checkbox('Show results: `codeyam editor show-results`');
1707
+ console.log();
1708
+ console.log(chalk.dim('The user should now see their existing page with live screenshots in the preview.'));
1709
+ migrationStopGate(4, pageName, pageIndex, totalPages);
1710
+ }
1711
+ // ─── Migration Step 5: Discuss ──────────────────────────────────────
1712
+ function printMigrateStep5(root) {
1713
+ const pageInfo = getCurrentMigrationPage(root);
1714
+ if (!pageInfo) {
1715
+ console.error(chalk.red('Error: No migration in progress.'));
1716
+ process.exit(1);
1717
+ }
1718
+ const { pageName, pageIndex, totalPages } = pageInfo;
1719
+ writeMigrationStepState(root, 5, pageName, pageIndex, totalPages);
1720
+ console.log();
1721
+ console.log(chalk.bold.cyan(`━━━ Migration Step 5: Discuss — ${pageName} (${pageIndex + 1}/${totalPages}) ━━━`));
1722
+ console.log();
1723
+ console.log(chalk.bold('Goal: Assess page complexity and ask the user if decomposition is worth it.'));
1724
+ console.log();
1725
+ console.log(chalk.bold('Checklist:'));
1726
+ checkbox('Hide results: `codeyam editor hide-results`');
1727
+ checkbox("Assess the page's complexity:");
1728
+ console.log(chalk.dim(' — How many inline components exist?'));
1729
+ console.log(chalk.dim(' — How much business logic is embedded in the page file?'));
1730
+ console.log(chalk.dim(' — Are there reusable pieces that other pages could share?'));
1731
+ console.log(chalk.dim(' — Would extraction improve testability or maintainability?'));
1732
+ checkbox('Present your assessment to the user');
1733
+ console.log();
1734
+ console.log(chalk.bold('Present a selection menu (AskUserQuestion with these EXACT option labels):'));
1735
+ console.log(chalk.green(' Option 1 label: "Yes, decompose this page"') +
1736
+ chalk.dim(' — continue to step 6 (Decompose)'));
1737
+ console.log(chalk.yellow(' Option 2 label: "No, skip decomposition"') +
1738
+ chalk.dim(' — skip to step 9 (Journal)'));
1739
+ console.log();
1740
+ console.log(chalk.bold.yellow('Routing:'));
1741
+ console.log(chalk.yellow(' If user chooses decomposition: ') +
1742
+ chalk.bold('codeyam editor migrate 6'));
1743
+ console.log(chalk.yellow(' If user skips: ') + chalk.bold('codeyam editor migrate 9'));
1744
+ migrationStopGate(5, pageName, pageIndex, totalPages, { confirm: true });
1745
+ }
1746
+ // ─── Migration Step 6: Decompose ────────────────────────────────────
1747
+ function printMigrateStep6(root) {
1748
+ const pageInfo = getCurrentMigrationPage(root);
1749
+ if (!pageInfo) {
1750
+ console.error(chalk.red('Error: No migration in progress.'));
1751
+ process.exit(1);
1752
+ }
1753
+ const { pageName, pageIndex, totalPages } = pageInfo;
1754
+ writeMigrationStepState(root, 6, pageName, pageIndex, totalPages);
1755
+ console.log();
1756
+ console.log(chalk.bold.cyan(`━━━ Migration Step 6: Decompose — ${pageName} (${pageIndex + 1}/${totalPages}) ━━━`));
1757
+ console.log();
1758
+ console.log(chalk.bold('Goal: Plan all extractions. Mark already-extracted shared components as REUSE.'));
1759
+ console.log(chalk.yellow('This step is read and plan only. Code extraction happens in step 7.'));
1760
+ console.log();
1761
+ console.log(chalk.bold.yellow('Migration note: check glossary for reuse opportunities'));
1762
+ console.log(chalk.yellow(' If a component exists in the glossary from a previous page, mark it as REUSE in your plan.'));
1763
+ console.log(chalk.yellow(' Only add it to the extraction plan if it needs modifications for this page.'));
1764
+ console.log();
1765
+ printExtractionPlanInstructions();
1766
+ migrationStopGate(6, pageName, pageIndex, totalPages);
1767
+ }
1768
+ // ─── Migration Step 7: Extract ──────────────────────────────────────
1769
+ function printMigrateStep7(root) {
1770
+ const pageInfo = getCurrentMigrationPage(root);
1771
+ if (!pageInfo) {
1772
+ console.error(chalk.red('Error: No migration in progress.'));
1773
+ process.exit(1);
1774
+ }
1775
+ const { pageName, pageIndex, totalPages } = pageInfo;
1776
+ writeMigrationStepState(root, 7, pageName, pageIndex, totalPages);
1777
+ console.log();
1778
+ console.log(chalk.bold.cyan(`━━━ Migration Step 7: Extract — ${pageName} (${pageIndex + 1}/${totalPages}) ━━━`));
1779
+ console.log();
1780
+ console.log(chalk.bold('Goal: Execute the extraction plan. Components first, then functions via TDD.'));
1781
+ console.log();
1782
+ console.log(chalk.bold('Checklist:'));
1783
+ checkbox('Extract visual components (no tests needed for components)');
1784
+ checkbox('Extract library functions via TDD (write failing test first, then implement)');
1785
+ checkbox('Extract hooks as needed');
1786
+ checkbox('Recursive pass: check each extracted component for further extraction');
1787
+ console.log(chalk.yellow(' Each component should be a thin shell composing sub-components.'));
1788
+ checkbox('Verify the page file is ONLY imports + component composition');
1789
+ checkbox('Run tests to confirm nothing is broken: `npx jest --testPathPattern="<relevant>" --maxWorkers=2`');
1790
+ console.log();
1791
+ console.log(chalk.dim('After extraction, the page should be a thin shell. Move to step 8 to recapture.'));
1792
+ migrationStopGate(7, pageName, pageIndex, totalPages);
1793
+ }
1794
+ // ─── Migration Step 8: Recapture ────────────────────────────────────
1795
+ function printMigrateStep8(root) {
1796
+ const pageInfo = getCurrentMigrationPage(root);
1797
+ if (!pageInfo) {
1798
+ console.error(chalk.red('Error: No migration in progress.'));
1799
+ process.exit(1);
1800
+ }
1801
+ const { pageName, pageIndex, totalPages } = pageInfo;
1802
+ writeMigrationStepState(root, 8, pageName, pageIndex, totalPages);
1803
+ const migState = readMigrationState(root);
1804
+ const page = migState.pages[pageIndex];
1805
+ console.log();
1806
+ console.log(chalk.bold.cyan(`━━━ Migration Step 8: Recapture — ${pageName} (${pageIndex + 1}/${totalPages}) ━━━`));
1807
+ console.log();
1808
+ console.log(chalk.bold('Goal: Update glossary, re-register ALL scenarios after code changes, and verify.'));
1809
+ console.log();
1810
+ console.log(chalk.bold('Glossary:'));
1811
+ printGlossaryInstructions();
1812
+ console.log(chalk.yellow(' Skip entries already in the glossary from previous page migrations.'));
1813
+ checkbox('Update sharedComponents in `.codeyam/migration-state.json` if any extracted components are shared');
1814
+ console.log();
1815
+ console.log(chalk.bold.red('Re-register App Scenarios (REQUIRED):'));
1816
+ console.log(chalk.yellow(` You MUST re-register application scenarios for "${page.route}" to get fresh screenshots.`));
1817
+ checkbox('Fetch existing scenarios: `codeyam editor scenarios`');
1818
+ checkbox('Find all scenarios that use the real page route (not /codeyam-isolate/ paths)');
1819
+ checkbox('Re-register each one with the SAME name to update its screenshot');
1820
+ console.log(chalk.dim(` Example: codeyam editor register '{"name":"${pageName} - Full Data","type":"application","url":"${page.route}",...}'`));
1821
+ checkbox('After each registration, check the response for `clientErrors`');
1822
+ console.log(chalk.yellow(' Fix client errors and re-register before moving on'));
1823
+ console.log();
1824
+ console.log(chalk.bold('Component Scenarios:'));
1825
+ console.log(chalk.dim(' For each visual component extracted in step 7, create isolation routes and register scenarios.'));
1826
+ printComponentCaptureInstructions();
1827
+ console.log();
1828
+ console.log(chalk.bold('Verify:'));
1829
+ checkbox('Run `codeyam editor analyze-imports` to populate import graph');
1830
+ checkbox('Run library function tests: `npx jest --testPathPattern="<relevant>" --maxWorkers=2`');
1831
+ checkbox('Run `codeyam editor audit` to check completeness');
1832
+ checkbox('Check for client errors: `codeyam editor client-errors`');
1833
+ checkbox('Fix any issues before proceeding');
1834
+ console.log();
1835
+ migrationStopGate(8, pageName, pageIndex, totalPages);
1836
+ }
1837
+ // ─── Migration Step 9: Journal ──────────────────────────────────────
1838
+ function printMigrateStep9(root) {
1839
+ const pageInfo = getCurrentMigrationPage(root);
1840
+ if (!pageInfo) {
1841
+ console.error(chalk.red('Error: No migration in progress.'));
1842
+ process.exit(1);
1843
+ }
1844
+ const { pageName, pageIndex, totalPages } = pageInfo;
1845
+ writeMigrationStepState(root, 9, pageName, pageIndex, totalPages);
1846
+ console.log();
1847
+ console.log(chalk.bold.cyan(`━━━ Migration Step 9: Journal — ${pageName} (${pageIndex + 1}/${totalPages}) ━━━`));
1848
+ console.log();
1849
+ console.log(`Create a journal entry documenting the migration of the ${pageName} page.`);
1850
+ console.log();
1851
+ console.log(chalk.bold('Checklist:'));
1852
+ checkbox('Write a concise description of what was migrated (2-3 sentences)');
1853
+ checkbox(`Create journal: \`codeyam editor journal '{"title":"Migration: ${pageName}","type":"migration","description":"...","includeSessionScenarios":true}'\``);
1854
+ console.log();
1855
+ migrationStopGate(9, pageName, pageIndex, totalPages);
1362
1856
  }
1363
- // ─── Step 13: Present ─────────────────────────────────────────────────
1364
- function printStep13(root, feature) {
1365
- const port = getServerPort();
1366
- const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
1367
- const prevState = readState(root);
1368
- const isResuming = prevState?.step === 13;
1369
- const now = new Date().toISOString();
1370
- writeState(root, {
1371
- feature,
1372
- step: 13,
1373
- label: STEP_LABELS[13],
1374
- startedAt: isResuming ? prevState.startedAt : now,
1375
- featureStartedAt: prevState?.featureStartedAt || now,
1376
- });
1377
- logEvent(root, 'step', { step: 13, label: 'Present', feature });
1378
- stepHeader(13, 'Present', feature);
1379
- if (isResuming) {
1380
- printResumptionHeader(13);
1857
+ // ─── Migration Step 10: Present ─────────────────────────────────────
1858
+ function printMigrateStep10(root) {
1859
+ const pageInfo = getCurrentMigrationPage(root);
1860
+ if (!pageInfo) {
1861
+ console.error(chalk.red('Error: No migration in progress.'));
1862
+ process.exit(1);
1381
1863
  }
1382
- console.log('Present the results to the user and get their approval.');
1864
+ const { pageName, pageIndex, totalPages } = pageInfo;
1865
+ writeMigrationStepState(root, 10, pageName, pageIndex, totalPages);
1866
+ const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
1867
+ console.log();
1868
+ console.log(chalk.bold.cyan(`━━━ Migration Step 10: Present — ${pageName} (${pageIndex + 1}/${totalPages}) ━━━`));
1869
+ console.log();
1870
+ console.log("Show results and commit this page's migration.");
1383
1871
  console.log();
1384
1872
  console.log(chalk.bold('Checklist:'));
1385
1873
  checkbox('Fetch all scenarios: `codeyam editor scenarios`');
1386
- console.log(chalk.dim(' Each scenario has a `changeStatus` field: "new", "edited", "impacted", or null.'));
1387
- checkbox('Pick the best scenario to showcase from this working session:');
1388
- console.log(chalk.dim(' Prefer scenarios with changeStatus "new" or "edited" (directly changed in this session).'));
1389
- console.log(chalk.dim(' For app-level scenarios, prefer those showing the new/changed functionality.'));
1390
- console.log(chalk.dim(' NEVER pick a scenario with changeStatus null — those are unchanged from a previous commit.'));
1391
- checkbox('Switch the preview to that scenario using its `id` and `dimension`:');
1874
+ checkbox('Pick the best scenario to showcase:');
1392
1875
  console.log(chalk.dim(` codeyam editor preview '{"scenarioId":"<id>","dimension":"${dim}"}'`));
1393
- console.log(chalk.dim(' Use the dimension that matches the scenario — check its registered dimensions.'));
1394
1876
  printDimensionGuidance(dim, dimNames);
1395
- checkbox(`Show the results panel: \`codeyam editor show-results\``);
1396
- console.log(chalk.dim(' This opens a visual panel below the terminal showing all scenarios with screenshots.'));
1397
- console.log(chalk.dim(' The user can click scenarios to switch the live preview.'));
1398
- checkbox('Write a 1-2 sentence summary of what was built');
1399
- checkbox('Report test count and audit status (one line)');
1877
+ checkbox('Show results: `codeyam editor show-results`');
1878
+ checkbox('Write a 1-2 sentence summary of what was migrated');
1400
1879
  console.log();
1401
- console.log(chalk.bold('Present a selection menu to the user (use AskUserQuestion with these EXACT option labels):'));
1880
+ console.log(chalk.bold('Present a selection menu (AskUserQuestion with these EXACT option labels):'));
1402
1881
  console.log(chalk.green(' Option 1 label: "Save & commit"') +
1403
- chalk.dim('git commit all changes and record in journal'));
1882
+ chalk.dim(" — commit this page's migration"));
1404
1883
  console.log(chalk.yellow(' Option 2 label: "I\'d like to make some changes"') +
1405
- chalk.dim(' — describe changes, then re-verify'));
1884
+ chalk.dim(' — make changes, then re-verify'));
1406
1885
  console.log();
1407
1886
  console.log(chalk.bold('If the user chooses "Save & commit":'));
1408
- checkbox('Advance to the commit step: `codeyam editor 14`');
1887
+ checkbox('Hide results: `codeyam editor hide-results`');
1888
+ checkbox(`Commit: \`codeyam editor commit '{"message":"migration: ${pageName} page\\n\\n<description>"}'\``);
1889
+ checkbox('Mark page complete: `codeyam editor migrate complete`');
1890
+ const remaining = pageInfo.totalPages - pageIndex - 1;
1891
+ if (remaining > 0) {
1892
+ checkbox('Advance to next page: `codeyam editor migrate next`');
1893
+ console.log();
1894
+ console.log(chalk.bold.green(`Page ${pageIndex + 1}/${totalPages} complete! ${remaining} page(s) remaining.`));
1895
+ }
1896
+ else {
1897
+ console.log();
1898
+ console.log(chalk.bold.green('Migration complete! All pages processed.'));
1899
+ console.log(chalk.green('The project is now fully migrated. Future sessions use the normal feature workflow.'));
1900
+ console.log(chalk.green('Start building: ') + chalk.bold('codeyam editor steps'));
1901
+ }
1409
1902
  console.log();
1410
- console.log(chalk.bold('If the user chooses "Make changes" (or asks for ANY change, even as a question):'));
1411
- checkbox(`Hide the results panel: \`codeyam editor hide-results\``);
1412
- checkbox('Ask what changes the user wants (if not already clear)');
1413
- checkbox(`Run: \`codeyam editor change "${feature}"\` — this gives you the change checklist`);
1414
- checkbox('THEN make the requested changes and follow the checklist');
1415
- console.log(chalk.red.bold(' IMPORTANT: Always run the change command BEFORE writing any code.'));
1416
- stopGate(13, { confirm: true });
1903
+ console.log(chalk.bold('If the user chooses "Make changes":'));
1904
+ checkbox('Hide results: `codeyam editor hide-results`');
1905
+ checkbox('Ask what changes the user wants');
1906
+ checkbox(`Run: \`codeyam editor change "Migration: ${pageName}"\` — follow the change checklist`);
1907
+ console.log();
1908
+ migrationStopGate(10, pageName, pageIndex, totalPages, { confirm: true });
1909
+ }
1910
+ // ─── Migration Survey ───────────────────────────────────────────────
1911
+ function printMigrateSurvey(root) {
1912
+ console.log();
1913
+ console.log(chalk.bold.cyan('━━━ Project Migration Survey ━━━'));
1914
+ console.log();
1915
+ console.log('Survey the existing project, present a migration plan, and confirm the order with the user.');
1916
+ console.log(chalk.dim('See the "Migration Survey" section in SKILL.md for the full checklist and migration-state.json format.'));
1917
+ console.log();
1918
+ // Write editor state so hooks and printCycleOverview detect migration
1919
+ const now = new Date().toISOString();
1920
+ writeState(root, {
1921
+ feature: 'Migration: Survey',
1922
+ step: 0,
1923
+ label: 'Survey',
1924
+ startedAt: now,
1925
+ featureStartedAt: now,
1926
+ migration: {
1927
+ pageName: 'Survey',
1928
+ pageIndex: -1,
1929
+ totalPages: 0,
1930
+ },
1931
+ });
1932
+ logEvent(root, 'migration-survey', {});
1933
+ console.log(chalk.bold.red('━━━ STOP ━━━'));
1934
+ console.log();
1935
+ console.log(chalk.red('Survey the project and confirm the migration order with the user.'));
1936
+ console.log(chalk.green('After confirmation and writing migration-state.json: ') +
1937
+ chalk.bold('codeyam editor migrate 1'));
1938
+ console.log();
1939
+ }
1940
+ // ─── Migration Status ───────────────────────────────────────────────
1941
+ function printMigrationStatus(root) {
1942
+ const state = readMigrationState(root);
1943
+ if (!state) {
1944
+ console.log(chalk.dim('No migration in progress.'));
1945
+ return;
1946
+ }
1947
+ console.log();
1948
+ console.log(chalk.bold.cyan('━━━ Migration Progress ━━━'));
1949
+ console.log();
1950
+ const done = state.pages.filter((p) => p.status === 'complete').length;
1951
+ const total = state.pages.length;
1952
+ const pct = total > 0 ? Math.round((done / total) * 100) : 0;
1953
+ console.log(` Status: ${chalk.bold(state.status)} — ${done}/${total} pages (${pct}%)`);
1954
+ console.log();
1955
+ for (let i = 0; i < state.pages.length; i++) {
1956
+ const p = state.pages[i];
1957
+ let icon;
1958
+ let color;
1959
+ if (p.status === 'complete') {
1960
+ icon = '✓';
1961
+ color = chalk.green;
1962
+ }
1963
+ else if (p.status === 'in-progress') {
1964
+ icon = '→';
1965
+ color = chalk.bold.cyan;
1966
+ }
1967
+ else {
1968
+ icon = '○';
1969
+ color = chalk.dim;
1970
+ }
1971
+ const extra = p.status === 'complete'
1972
+ ? chalk.dim(` (${p.extractedComponents.length} components, ${p.extractedFunctions.length} functions, ${p.scenarioCount} scenarios)`)
1973
+ : '';
1974
+ console.log(` ${color(`${icon} ${i + 1}. ${p.name}`)} ${chalk.dim(`(${p.route})`)}${extra}`);
1975
+ }
1976
+ if (state.sharedComponents.length > 0) {
1977
+ console.log();
1978
+ console.log(chalk.bold('Shared Components:'));
1979
+ for (const sc of state.sharedComponents) {
1980
+ console.log(` ${chalk.cyan(sc.name)} ${chalk.dim(`— from ${sc.extractedFromPage}, used in: ${sc.usedInPages.join(', ')}`)}`);
1981
+ }
1982
+ }
1983
+ console.log();
1984
+ }
1985
+ // ─── Migration Command Dispatcher ───────────────────────────────────
1986
+ function handleMigrateCommand(root, subArg) {
1987
+ // No subcommand or explicit empty → run initial survey
1988
+ if (!subArg) {
1989
+ const migState = readMigrationState(root);
1990
+ if (migState?.status === 'in-progress') {
1991
+ // Already in progress — show status and resume hint
1992
+ printMigrationStatus(root);
1993
+ const page = migState.pages[migState.currentPageIndex];
1994
+ if (page) {
1995
+ const editorState = readState(root);
1996
+ const currentStep = editorState?.migration ? editorState.step : 1;
1997
+ console.log(chalk.green('Continue with: ') +
1998
+ chalk.bold(`codeyam editor migrate ${currentStep}`));
1999
+ console.log();
2000
+ }
2001
+ return;
2002
+ }
2003
+ if (migState?.status === 'complete') {
2004
+ console.log(chalk.green('Migration is complete! Use the normal feature workflow: `codeyam editor steps`'));
2005
+ return;
2006
+ }
2007
+ printMigrateSurvey(root);
2008
+ return;
2009
+ }
2010
+ // Parse subcommand
2011
+ if (subArg === 'next') {
2012
+ const result = advanceToNextPage(root);
2013
+ if (!result) {
2014
+ const migState = readMigrationState(root);
2015
+ if (migState?.status === 'complete') {
2016
+ console.log();
2017
+ console.log(chalk.bold.green('🎉 Migration complete!'));
2018
+ console.log();
2019
+ const done = migState.pages.filter((p) => p.status === 'complete');
2020
+ const totalComponents = done.reduce((sum, p) => sum + p.extractedComponents.length, 0);
2021
+ const totalFunctions = done.reduce((sum, p) => sum + p.extractedFunctions.length, 0);
2022
+ const totalScenarios = done.reduce((sum, p) => sum + p.scenarioCount, 0);
2023
+ console.log(` Pages migrated: ${done.length}`);
2024
+ console.log(` Components extracted: ${totalComponents}`);
2025
+ console.log(` Functions extracted: ${totalFunctions}`);
2026
+ console.log(` Scenarios created: ${totalScenarios}`);
2027
+ console.log(` Shared components: ${migState.sharedComponents.length}`);
2028
+ console.log();
2029
+ console.log(chalk.green('The project is fully migrated. Start building features: `codeyam editor steps`'));
2030
+ console.log();
2031
+ }
2032
+ else {
2033
+ console.log(chalk.red('No pending pages found. Run `codeyam editor migrate status` to check progress.'));
2034
+ }
2035
+ return;
2036
+ }
2037
+ // Start M1 for the next page
2038
+ printMigrateStep1(root);
2039
+ return;
2040
+ }
2041
+ if (subArg === 'status') {
2042
+ printMigrationStatus(root);
2043
+ return;
2044
+ }
2045
+ if (subArg === 'complete') {
2046
+ const migState = readMigrationState(root);
2047
+ if (!migState) {
2048
+ console.error(chalk.red('Error: No migration in progress.'));
2049
+ process.exit(1);
2050
+ }
2051
+ const page = migState.pages[migState.currentPageIndex];
2052
+ if (!page) {
2053
+ console.error(chalk.red('Error: No active migration page.'));
2054
+ process.exit(1);
2055
+ }
2056
+ completePage(root, {
2057
+ extractedComponents: page.extractedComponents,
2058
+ extractedFunctions: page.extractedFunctions,
2059
+ scenarioCount: page.scenarioCount,
2060
+ });
2061
+ console.log(chalk.green(`Page "${page.name}" marked complete.`));
2062
+ return;
2063
+ }
2064
+ // Numeric step: 1-8
2065
+ const step = parseInt(subArg, 10);
2066
+ if (isNaN(step) || step < 1 || step > 10) {
2067
+ console.error(chalk.red(`Error: Invalid migration step "${subArg}". Must be 1-10, "next", "complete", or "status".`));
2068
+ process.exit(1);
2069
+ }
2070
+ // Ensure migration is active and first page started
2071
+ const migState = readMigrationState(root);
2072
+ if (!migState) {
2073
+ console.error(chalk.red('Error: No migration state. Run `codeyam editor migrate` first.'));
2074
+ process.exit(1);
2075
+ }
2076
+ // If still in survey state and step 1 requested, start first page
2077
+ if (migState.status === 'surveyed' && step === 1) {
2078
+ migState.status = 'in-progress';
2079
+ migState.pages[0].status = 'in-progress';
2080
+ migState.pages[0].startedAt = new Date().toISOString();
2081
+ writeMigrationState(root, migState);
2082
+ }
2083
+ const stepFns = {
2084
+ 1: printMigrateStep1,
2085
+ 2: printMigrateStep2,
2086
+ 3: printMigrateStep3,
2087
+ 4: printMigrateStep4,
2088
+ 5: printMigrateStep5,
2089
+ 6: printMigrateStep6,
2090
+ 7: printMigrateStep7,
2091
+ 8: printMigrateStep8,
2092
+ 9: printMigrateStep9,
2093
+ 10: printMigrateStep10,
2094
+ };
2095
+ stepFns[step](root);
1417
2096
  }
1418
2097
  // ─── Step 14: Commit ─────────────────────────────────────────────────
1419
2098
  function printStep14(root, feature) {
@@ -1521,26 +2200,54 @@ async function handleAnalyzeImports(options = {}) {
1521
2200
  }
1522
2201
  let glossaryEntries;
1523
2202
  try {
1524
- glossaryEntries = JSON.parse(fs.readFileSync(glossaryPath, 'utf8'));
2203
+ const parsed = JSON.parse(fs.readFileSync(glossaryPath, 'utf8'));
2204
+ glossaryEntries = sanitizeGlossaryEntries(parsed);
1525
2205
  }
1526
2206
  catch {
1527
2207
  console.error(chalk.red('Error: Could not parse .codeyam/glossary.json.'));
1528
2208
  process.exit(1);
1529
2209
  }
1530
- if (!Array.isArray(glossaryEntries) || glossaryEntries.length === 0) {
2210
+ if (glossaryEntries.length === 0) {
1531
2211
  console.error(chalk.red('Error: glossary.json is empty.'));
1532
2212
  process.exit(1);
1533
2213
  }
1534
2214
  const filePaths = glossaryEntries.map((e) => e.filePath);
1535
2215
  // Include page files so pages get analyzed as entities too.
1536
2216
  // This allows the import graph to map page names to their dependencies.
2217
+ // Use allFiles (not map values) to include ALL pages — multiple routes
2218
+ // can share a page name (e.g., /feedback/[id] and /feedback/new both
2219
+ // map to "Feedback") but each needs its own entity analysis.
1537
2220
  const { scanPageFilePaths } = await import('../utils/entityChangeStatus.server.js');
1538
- const pageFilePaths = scanPageFilePaths(root);
1539
- for (const pageFile of Object.values(pageFilePaths)) {
2221
+ const { allFiles: allPageFiles } = scanPageFilePaths(root);
2222
+ for (const pageFile of allPageFiles) {
1540
2223
  if (!filePaths.includes(pageFile)) {
1541
2224
  filePaths.push(pageFile);
1542
2225
  }
1543
2226
  }
2227
+ // Include file paths from editor scenarios (both component_path and
2228
+ // page_file_path) so that all components and pages with scenarios get
2229
+ // entities — critical for non-Next.js apps where scanPageFilePaths
2230
+ // doesn't find page.tsx files.
2231
+ try {
2232
+ const { getDatabase } = await import('../../../packages/database/index.js');
2233
+ const db = getDatabase();
2234
+ const scenarioFiles = await db
2235
+ .selectFrom('editor_scenarios')
2236
+ .select(['component_path', 'page_file_path'])
2237
+ .distinct()
2238
+ .execute();
2239
+ for (const row of scenarioFiles) {
2240
+ const r = row;
2241
+ for (const fp of [r.component_path, r.page_file_path]) {
2242
+ if (fp && !filePaths.includes(fp)) {
2243
+ filePaths.push(fp);
2244
+ }
2245
+ }
2246
+ }
2247
+ }
2248
+ catch {
2249
+ // Non-fatal — scenario file paths just won't be included
2250
+ }
1544
2251
  const progress = new ProgressReporter();
1545
2252
  // Run data-structure-only analysis for all entities (glossary + pages)
1546
2253
  // Don't pass entityNames — entities may not exist yet (fresh clone).
@@ -1630,6 +2337,24 @@ async function handleAnalyzeImports(options = {}) {
1630
2337
  });
1631
2338
  console.log(summary || JSON.stringify({ imports, entities: entityData }));
1632
2339
  }
2340
+ // Backfill entity_sha on scenarios registered before entities existed.
2341
+ // This fixes the "missing data" UI state when scenarios were registered
2342
+ // while analyze-imports was still running in the background.
2343
+ try {
2344
+ const { getDatabase } = await import('../../../packages/database/index.js');
2345
+ const db = getDatabase();
2346
+ const backfillResult = await backfillEntityShaOnScenarios(db, latestEntities.map((e) => ({
2347
+ sha: e.sha,
2348
+ name: e.name,
2349
+ filePath: e.filePath || '',
2350
+ })));
2351
+ if (backfillResult.updated > 0 && !options.silent) {
2352
+ console.log(chalk.green(`Linked ${backfillResult.updated} scenario(s) to their entities.`));
2353
+ }
2354
+ }
2355
+ catch {
2356
+ /* non-fatal */
2357
+ }
1633
2358
  }
1634
2359
  // ─── Validate-seed subcommand ─────────────────────────────────────────
1635
2360
  /**
@@ -1668,6 +2393,24 @@ function handleValidateSeed(jsonArg) {
1668
2393
  : 0;
1669
2394
  console.log(chalk.dim(` ${table}: ${rows} row(s)`));
1670
2395
  }
2396
+ // Check seed keys against Prisma schema if available
2397
+ const schemaPath = path.join(root, 'prisma', 'schema.prisma');
2398
+ try {
2399
+ if (fs.existsSync(schemaPath)) {
2400
+ const schemaContent = fs.readFileSync(schemaPath, 'utf-8');
2401
+ const warnings = validateSeedKeysAgainstPrisma(tables, schemaContent);
2402
+ if (warnings.length > 0) {
2403
+ console.log();
2404
+ console.log(chalk.yellow('Prisma model name warnings:'));
2405
+ for (const w of warnings) {
2406
+ console.log(chalk.yellow(` ⚠ ${w}`));
2407
+ }
2408
+ }
2409
+ }
2410
+ }
2411
+ catch {
2412
+ // Schema not readable — skip Prisma validation
2413
+ }
1671
2414
  }
1672
2415
  else {
1673
2416
  console.error(chalk.red('Seed data validation failed:'));
@@ -1677,6 +2420,27 @@ function handleValidateSeed(jsonArg) {
1677
2420
  process.exit(1);
1678
2421
  }
1679
2422
  }
2423
+ // ─── Delete subcommand ────────────────────────────────────────────────
2424
+ /**
2425
+ * `codeyam editor delete <scenarioId>`
2426
+ *
2427
+ * Delete a scenario by ID via the running editor server.
2428
+ */
2429
+ async function handleDelete(scenarioId) {
2430
+ if (!scenarioId) {
2431
+ console.error(chalk.red('Error: Missing scenario ID. Usage: codeyam editor delete <scenarioId>'));
2432
+ process.exit(1);
2433
+ }
2434
+ const port = getServerPort();
2435
+ const result = await deleteScenarioViaCli(scenarioId, port);
2436
+ if (result.success) {
2437
+ console.log(chalk.green(result.message));
2438
+ }
2439
+ else {
2440
+ console.error(chalk.red(result.message));
2441
+ process.exit(1);
2442
+ }
2443
+ }
1680
2444
  // ─── Isolate subcommand ───────────────────────────────────────────────
1681
2445
  /**
1682
2446
  * `codeyam editor isolate "StarRating CategoryBadge DrinkCard"`
@@ -2151,23 +2915,49 @@ function handleChange(feature) {
2151
2915
  }
2152
2916
  // ─── Audit gate ─────────────────────────────────────────────────────
2153
2917
  /**
2154
- * Silently check whether the audit passes. Returns true if allPassing,
2155
- * false if any issues remain or the server is unreachable.
2156
- * Used as a hard gate before steps 8+.
2918
+ * Fetch the audit result from the server.
2919
+ * Returns null if the server is unreachable.
2157
2920
  */
2158
- async function checkAuditGate() {
2921
+ async function fetchAuditResult() {
2159
2922
  const port = getServerPort();
2160
2923
  try {
2161
2924
  const res = await fetch(`http://localhost:${port}/api/editor-audit`);
2162
2925
  if (!res.ok)
2163
- return false;
2164
- const data = await res.json();
2165
- return data?.summary?.allPassing === true;
2926
+ return null;
2927
+ return await res.json();
2166
2928
  }
2167
2929
  catch {
2168
- // Server not running or unreachable — can't verify, so don't block
2930
+ return null;
2931
+ }
2932
+ }
2933
+ /**
2934
+ * Silently check whether the audit passes. Returns true if allPassing,
2935
+ * false if any issues remain or the server is unreachable.
2936
+ * Used as a hard gate before steps 8+.
2937
+ *
2938
+ * Auto-runs analyze-imports if the only failure is incomplete entities,
2939
+ * then re-checks once.
2940
+ */
2941
+ async function checkAuditGate() {
2942
+ const data = await fetchAuditResult();
2943
+ if (!data)
2944
+ return true; // Server unreachable — don't block
2945
+ if (data.summary?.allPassing === true)
2169
2946
  return true;
2947
+ // If incomplete entities are the only issue, auto-fix with analyze-imports (once)
2948
+ const { isAutoRemediable } = await import('../utils/editorAudit.js');
2949
+ if (isAutoRemediable(data.summary, false)) {
2950
+ try {
2951
+ await handleAnalyzeImports({ silent: true });
2952
+ }
2953
+ catch {
2954
+ return false;
2955
+ }
2956
+ // Re-check after fix
2957
+ const retry = await fetchAuditResult();
2958
+ return retry?.summary?.allPassing === true;
2170
2959
  }
2960
+ return false;
2171
2961
  }
2172
2962
  // ─── Audit subcommand ────────────────────────────────────────────────
2173
2963
  /**
@@ -2178,22 +2968,38 @@ async function checkAuditGate() {
2178
2968
  * have test files. Exits with code 1 if anything is missing.
2179
2969
  */
2180
2970
  async function handleAudit() {
2181
- const port = getServerPort();
2182
- const url = `http://localhost:${port}/api/editor-audit`;
2183
- let data;
2184
- try {
2185
- const res = await fetch(url);
2186
- if (!res.ok) {
2187
- console.error(chalk.red(`Error: Audit endpoint returned ${res.status}`));
2188
- process.exit(1);
2189
- }
2190
- data = await res.json();
2191
- }
2192
- catch (err) {
2971
+ let data = await fetchAuditResult();
2972
+ if (!data) {
2193
2973
  console.error(chalk.red('Error: Could not reach the CodeYam server. Is it running?'));
2194
- console.error(chalk.dim(` ${err.message}`));
2195
2974
  process.exit(1);
2196
2975
  }
2976
+ // Auto-fix incomplete entities — but only once.
2977
+ // If analyze-imports runs and entities are STILL incomplete, report clearly
2978
+ // instead of retrying. The analysis may have errors (e.g., stale entity
2979
+ // references from refactoring) that can't be fixed by re-running.
2980
+ const incompleteBeforeFix = data.incompleteEntities || [];
2981
+ let autoRemediationFailed = false;
2982
+ if (incompleteBeforeFix.length > 0) {
2983
+ console.log(chalk.dim(`Running import analysis for ${incompleteBeforeFix.length} incomplete entit${incompleteBeforeFix.length !== 1 ? 'ies' : 'y'}...`));
2984
+ try {
2985
+ await handleAnalyzeImports({ silent: true });
2986
+ }
2987
+ catch {
2988
+ // Fall through — the audit will still report them as incomplete
2989
+ }
2990
+ // Re-fetch audit results after the fix
2991
+ data = await fetchAuditResult();
2992
+ if (!data) {
2993
+ console.error(chalk.red('Error: Could not reach the CodeYam server after analyze-imports.'));
2994
+ process.exit(1);
2995
+ }
2996
+ // If entities are still incomplete after running analyze-imports,
2997
+ // flag it so we can show a clear message instead of looping
2998
+ const incompleteAfterFix = data.incompleteEntities || [];
2999
+ if (incompleteAfterFix.length > 0) {
3000
+ autoRemediationFailed = true;
3001
+ }
3002
+ }
2197
3003
  const { components, functions, summary } = data;
2198
3004
  console.log();
2199
3005
  console.log(chalk.bold.cyan('━━━ Editor Audit ━━━'));
@@ -2252,6 +3058,49 @@ async function handleAudit() {
2252
3058
  }
2253
3059
  console.log();
2254
3060
  }
3061
+ // Missing from glossary
3062
+ const missingFromGlossary = data.missingFromGlossary || [];
3063
+ if (missingFromGlossary.length > 0) {
3064
+ console.log(chalk.bold('Missing from glossary:'));
3065
+ for (const m of missingFromGlossary) {
3066
+ console.log(` ${chalk.red('✗')} ${m.name} ${chalk.dim(`(${m.filePath})`)} — ${m.scenarioCount} scenario${m.scenarioCount !== 1 ? 's' : ''} but not in glossary`);
3067
+ }
3068
+ console.log(chalk.yellow(' Add these to .codeyam/glossary.json and run `codeyam editor analyze-imports`'));
3069
+ console.log();
3070
+ }
3071
+ // Incomplete entities (scenarios without analyses)
3072
+ const incompleteEntities = data.incompleteEntities || [];
3073
+ if (incompleteEntities.length > 0) {
3074
+ console.log(chalk.bold('Incomplete entities (need import analysis):'));
3075
+ for (const e of incompleteEntities) {
3076
+ console.log(` ${chalk.red('✗')} ${e.name} — ${e.scenarioCount} scenario${e.scenarioCount !== 1 ? 's' : ''} but entity not analyzed`);
3077
+ }
3078
+ if (autoRemediationFailed) {
3079
+ console.log(chalk.red(' analyze-imports was run automatically but these entities are STILL incomplete.'));
3080
+ console.log(chalk.red(' This usually means the analysis had errors — check the output above for details.'));
3081
+ console.log(chalk.yellow(' Common causes: stale entity references from refactoring, missing source files.'));
3082
+ console.log(chalk.yellow(' Try: run `codeyam editor analyze-imports` manually to see the full error output.'));
3083
+ }
3084
+ else {
3085
+ console.log(chalk.yellow(' Run `codeyam editor analyze-imports` to analyze these entities'));
3086
+ }
3087
+ console.log();
3088
+ }
3089
+ // Miscategorized scenarios (component scenarios using real page URLs)
3090
+ const miscategorizedScenarios = data.miscategorizedScenarios || [];
3091
+ if (miscategorizedScenarios.length > 0) {
3092
+ console.log(chalk.bold('Miscategorized scenarios (component with page URL):'));
3093
+ for (const m of miscategorizedScenarios) {
3094
+ console.log(` ${chalk.red('✗')} ${m.componentName} at ${chalk.dim(m.url)} — ${m.scenarioNames.length} scenario${m.scenarioNames.length !== 1 ? 's' : ''}`);
3095
+ for (const name of m.scenarioNames) {
3096
+ console.log(chalk.dim(` "${name}"`));
3097
+ }
3098
+ }
3099
+ console.log(chalk.yellow(' Component scenarios must use isolation routes (/isolated-components/...).'));
3100
+ console.log(chalk.yellow(' Either re-register as app scenarios (remove componentName, add pageFilePath)'));
3101
+ console.log(chalk.yellow(' or re-register with an isolation URL.'));
3102
+ console.log();
3103
+ }
2255
3104
  // Summary
2256
3105
  const allOk = summary.allPassing;
2257
3106
  if (allOk) {
@@ -2275,6 +3124,15 @@ async function handleAudit() {
2275
3124
  if (summary.functionsNameMismatch > 0) {
2276
3125
  parts.push(`${summary.functionsNameMismatch} function${summary.functionsNameMismatch !== 1 ? 's' : ''} with test name mismatch (missing top-level describe)`);
2277
3126
  }
3127
+ if (summary.missingFromGlossary > 0) {
3128
+ parts.push(`${summary.missingFromGlossary} file${summary.missingFromGlossary !== 1 ? 's' : ''} with scenarios not in glossary`);
3129
+ }
3130
+ if (summary.incompleteEntities > 0) {
3131
+ parts.push(`${summary.incompleteEntities} entit${summary.incompleteEntities !== 1 ? 'ies' : 'y'} need import analysis`);
3132
+ }
3133
+ if (summary.miscategorizedScenarios > 0) {
3134
+ parts.push(`${summary.miscategorizedScenarios} component${summary.miscategorizedScenarios !== 1 ? 's' : ''} using page URLs instead of isolation routes`);
3135
+ }
2278
3136
  console.log(chalk.red.bold('Audit failed: ') + parts.join(', '));
2279
3137
  }
2280
3138
  console.log();
@@ -2392,6 +3250,30 @@ async function handleScenarios() {
2392
3250
  }
2393
3251
  // ─── Scenario Coverage subcommand ───────────────────────────────────
2394
3252
  async function handleScenarioCoverage() {
3253
+ // Safety net: heal any scenarios with null entity_sha before checking coverage
3254
+ try {
3255
+ const { getDatabase } = await import('../../../packages/database/index.js');
3256
+ const { countScenariosNeedingEntityBackfill } = await import('../utils/editorScenarios');
3257
+ const db = getDatabase();
3258
+ const backfillCount = await countScenariosNeedingEntityBackfill(db);
3259
+ if (backfillCount > 0) {
3260
+ console.log(chalk.dim(` Healing ${backfillCount} scenario(s) with unlinked entities...`));
3261
+ await handleAnalyzeImports({ silent: true });
3262
+ // Run backfill after analysis
3263
+ const { backfillEntityShaOnScenarios } = await import('../utils/editorScenarios');
3264
+ const entities = await loadEntities({});
3265
+ if (entities && entities.length > 0) {
3266
+ await backfillEntityShaOnScenarios(db, entities.map((e) => ({
3267
+ sha: e.sha,
3268
+ name: e.name,
3269
+ filePath: e.filePath || '',
3270
+ })));
3271
+ }
3272
+ }
3273
+ }
3274
+ catch {
3275
+ // Non-fatal — proceed with coverage check regardless
3276
+ }
2395
3277
  const port = getServerPort();
2396
3278
  const url = `http://localhost:${port}/api/editor-scenario-coverage`;
2397
3279
  let data;
@@ -2539,7 +3421,20 @@ async function handleTemplate() {
2539
3421
  // Config parse error is non-fatal
2540
3422
  }
2541
3423
  }
2542
- // 5b. Write a fresh prototypeId so the proxy clears stale localStorage
3424
+ // 5b. Mark the project as template-scaffolded so migration detection
3425
+ // doesn't treat it as a pre-existing project that needs migration.
3426
+ const now = new Date().toISOString();
3427
+ const existingState = readState(root);
3428
+ writeState(root, {
3429
+ feature: '',
3430
+ step: 0,
3431
+ label: '',
3432
+ startedAt: now,
3433
+ featureStartedAt: now,
3434
+ ...existingState,
3435
+ scaffolded: true,
3436
+ });
3437
+ // 5c. Write a fresh prototypeId so the proxy clears stale localStorage
2543
3438
  const activeScenarioPath = path.join(root, '.codeyam', 'active-scenario.json');
2544
3439
  fs.writeFileSync(activeScenarioPath, JSON.stringify({ prototypeId: Date.now().toString() }), 'utf-8');
2545
3440
  // 6. Trigger editor-refresh so the server picks up the new project
@@ -2996,6 +3891,11 @@ const editorCommand = {
2996
3891
  await handleValidateSeed(argv.json || '');
2997
3892
  return;
2998
3893
  }
3894
+ // Subcommand: codeyam editor delete <scenarioId>
3895
+ if (argv.step === 'delete') {
3896
+ await handleDelete(argv.json || '');
3897
+ return;
3898
+ }
2999
3899
  // Subcommand: codeyam editor isolate "StarRating CategoryBadge DrinkCard"
3000
3900
  if (argv.step === 'isolate') {
3001
3901
  const names = [];
@@ -3026,6 +3926,11 @@ const editorCommand = {
3026
3926
  await handleEditorDebug(argv);
3027
3927
  return;
3028
3928
  }
3929
+ // Subcommand: codeyam editor migrate [subArg]
3930
+ if (argv.step === 'migrate') {
3931
+ handleMigrateCommand(root, argv.json || undefined);
3932
+ return;
3933
+ }
3029
3934
  // Subcommand: codeyam editor steps — show setup or cycle overview
3030
3935
  if (argv.step === 'steps') {
3031
3936
  if (!hasProject(root)) {
@@ -3095,6 +4000,9 @@ const editorCommand = {
3095
4000
  'screenshot_path',
3096
4001
  'viewport_width',
3097
4002
  'viewport_height',
4003
+ 'dimensions',
4004
+ 'screenshot_paths',
4005
+ 'page_file_path',
3098
4006
  'created_at',
3099
4007
  'updated_at',
3100
4008
  ])
@@ -3248,11 +4156,77 @@ const editorCommand = {
3248
4156
  project,
3249
4157
  branch,
3250
4158
  });
3251
- // Build import graph on first startup if glossary exists but no entities yet
4159
+ // Build import graph if glossary exists but entities are missing.
4160
+ // Runs on first startup (no entities at all) AND when page files or
4161
+ // scenario component files lack entity coverage.
3252
4162
  const glossaryPath = path.join(projectRoot, '.codeyam', 'glossary.json');
3253
4163
  if (fs.existsSync(glossaryPath)) {
4164
+ let needsAnalysis = false;
3254
4165
  const entities = await loadEntities({});
3255
4166
  if (!entities || entities.length === 0) {
4167
+ needsAnalysis = true;
4168
+ }
4169
+ else {
4170
+ const entityFilePaths = new Set(entities.map((e) => e.filePath));
4171
+ // Check if any page files are missing entities (Next.js apps)
4172
+ try {
4173
+ const { scanPageFilePaths } = await import('../utils/entityChangeStatus.server.js');
4174
+ const { allFiles } = scanPageFilePaths(projectRoot);
4175
+ const pageFiles = allFiles.filter((f) => f.endsWith('/page.tsx') || f.endsWith('/page.js'));
4176
+ const missingPages = pageFiles.filter((f) => !entityFilePaths.has(f));
4177
+ if (missingPages.length > 0) {
4178
+ console.log(chalk.dim(` Found ${missingPages.length} page(s) without entity analysis — running import analysis...`));
4179
+ needsAnalysis = true;
4180
+ }
4181
+ }
4182
+ catch {
4183
+ // Non-fatal — page file check failed
4184
+ }
4185
+ // Check if any scenario files (component_path or page_file_path)
4186
+ // are missing entities — covers non-Next.js apps
4187
+ if (!needsAnalysis) {
4188
+ try {
4189
+ const { getDatabase } = await import('../../../packages/database/index.js');
4190
+ const db = getDatabase();
4191
+ const scenarioFiles = await db
4192
+ .selectFrom('editor_scenarios')
4193
+ .select(['component_path', 'page_file_path'])
4194
+ .where('project_id', '=', project.id)
4195
+ .distinct()
4196
+ .execute();
4197
+ const missingCount = scenarioFiles.filter((row) => {
4198
+ const cp = row.component_path;
4199
+ const pfp = row.page_file_path;
4200
+ return ((cp && !entityFilePaths.has(cp)) ||
4201
+ (pfp && !entityFilePaths.has(pfp)));
4202
+ }).length;
4203
+ if (missingCount > 0) {
4204
+ console.log(chalk.dim(` Found ${missingCount} scenario file(s) without entity analysis — running import analysis...`));
4205
+ needsAnalysis = true;
4206
+ }
4207
+ }
4208
+ catch {
4209
+ // Non-fatal
4210
+ }
4211
+ }
4212
+ // Check if any scenarios have null entity_sha with file paths — heal on startup
4213
+ if (!needsAnalysis) {
4214
+ try {
4215
+ const { getDatabase } = await import('../../../packages/database/index.js');
4216
+ const { countScenariosNeedingEntityBackfill } = await import('../utils/editorScenarios');
4217
+ const db = getDatabase();
4218
+ const backfillCount = await countScenariosNeedingEntityBackfill(db);
4219
+ if (backfillCount > 0) {
4220
+ console.log(chalk.dim(` Found ${backfillCount} scenario(s) with unlinked entities — running import analysis...`));
4221
+ needsAnalysis = true;
4222
+ }
4223
+ }
4224
+ catch {
4225
+ // Non-fatal
4226
+ }
4227
+ }
4228
+ }
4229
+ if (needsAnalysis) {
3256
4230
  try {
3257
4231
  await handleAnalyzeImports({ silent: true });
3258
4232
  }
@@ -3261,6 +4235,27 @@ const editorCommand = {
3261
4235
  }
3262
4236
  }
3263
4237
  }
4238
+ // Backfill entity_sha on scenarios that were synced before entities existed.
4239
+ // This runs independently of analyze-imports so fresh clones and file-synced
4240
+ // scenarios get linked to their entities even when auto-analyze doesn't trigger.
4241
+ try {
4242
+ const entities = await loadEntities({});
4243
+ if (entities && entities.length > 0) {
4244
+ const { getDatabase } = await import('../../../packages/database/index.js');
4245
+ const db = getDatabase();
4246
+ const result = await backfillEntityShaOnScenarios(db, entities.map((e) => ({
4247
+ sha: e.sha,
4248
+ name: e.name,
4249
+ filePath: e.filePath || '',
4250
+ })));
4251
+ if (result.updated > 0) {
4252
+ console.log(chalk.green(` Linked ${result.updated} scenario(s) to their entities`));
4253
+ }
4254
+ }
4255
+ }
4256
+ catch {
4257
+ /* Non-fatal */
4258
+ }
3264
4259
  console.log();
3265
4260
  console.log(` Dashboard: ${url}`);
3266
4261
  console.log(' Run "codeyam --help" for all commands');
@@ -3274,7 +4269,9 @@ const editorCommand = {
3274
4269
  : process.platform === 'win32'
3275
4270
  ? 'start ""'
3276
4271
  : 'xdg-open';
3277
- execSync(`${openCommand} "${url}/editor"`, { stdio: 'ignore' });
4272
+ execSync(`${openCommand} "${url}/editor"`, {
4273
+ stdio: 'ignore',
4274
+ });
3278
4275
  }
3279
4276
  catch {
3280
4277
  // Silently fail if open command doesn't work