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

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 (317) 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 +9 -9
  4. package/analyzer-template/packages/ai/package.json +1 -1
  5. package/analyzer-template/packages/ai/src/lib/generateExecutionFlows.ts +0 -33
  6. package/analyzer-template/packages/analyze/src/lib/ProjectAnalyzer.ts +13 -7
  7. package/analyzer-template/packages/analyze/src/lib/asts/index.ts +7 -2
  8. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.ts +0 -98
  9. package/analyzer-template/packages/aws/package.json +2 -2
  10. package/analyzer-template/packages/database/package.json +3 -3
  11. package/analyzer-template/packages/database/src/lib/kysely/tables/editorScenariosTable.ts +20 -0
  12. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts +4 -0
  13. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts.map +1 -1
  14. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js +20 -0
  15. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
  16. package/analyzer-template/packages/github/dist/types/src/enums/ProjectFramework.d.ts +2 -0
  17. package/analyzer-template/packages/github/dist/types/src/enums/ProjectFramework.d.ts.map +1 -1
  18. package/analyzer-template/packages/github/dist/types/src/enums/ProjectFramework.js +2 -0
  19. package/analyzer-template/packages/github/dist/types/src/enums/ProjectFramework.js.map +1 -1
  20. package/analyzer-template/packages/github/package.json +1 -1
  21. package/analyzer-template/packages/types/src/enums/ProjectFramework.ts +2 -0
  22. package/analyzer-template/packages/ui-components/package.json +1 -1
  23. package/analyzer-template/packages/utils/dist/types/src/enums/ProjectFramework.d.ts +2 -0
  24. package/analyzer-template/packages/utils/dist/types/src/enums/ProjectFramework.d.ts.map +1 -1
  25. package/analyzer-template/packages/utils/dist/types/src/enums/ProjectFramework.js +2 -0
  26. package/analyzer-template/packages/utils/dist/types/src/enums/ProjectFramework.js.map +1 -1
  27. package/codeyam-cli/src/__tests__/memory-scripts/filter-session.test.js +196 -0
  28. package/codeyam-cli/src/__tests__/memory-scripts/filter-session.test.js.map +1 -0
  29. package/codeyam-cli/src/__tests__/memory-scripts/read-json-field.test.js +114 -0
  30. package/codeyam-cli/src/__tests__/memory-scripts/read-json-field.test.js.map +1 -0
  31. package/codeyam-cli/src/__tests__/memory-scripts/ripgrep-fallback.test.js +149 -0
  32. package/codeyam-cli/src/__tests__/memory-scripts/ripgrep-fallback.test.js.map +1 -0
  33. package/codeyam-cli/src/commands/default.js +3 -46
  34. package/codeyam-cli/src/commands/default.js.map +1 -1
  35. package/codeyam-cli/src/commands/editor.js +1893 -215
  36. package/codeyam-cli/src/commands/editor.js.map +1 -1
  37. package/codeyam-cli/src/commands/init.js +6 -1
  38. package/codeyam-cli/src/commands/init.js.map +1 -1
  39. package/codeyam-cli/src/data/techStacks.js +82 -0
  40. package/codeyam-cli/src/data/techStacks.js.map +1 -0
  41. package/codeyam-cli/src/utils/__tests__/devServerState.test.js +134 -0
  42. package/codeyam-cli/src/utils/__tests__/devServerState.test.js.map +1 -0
  43. package/codeyam-cli/src/utils/__tests__/editorApi.test.js +127 -0
  44. package/codeyam-cli/src/utils/__tests__/editorApi.test.js.map +1 -0
  45. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +635 -0
  46. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -0
  47. package/codeyam-cli/src/utils/__tests__/editorCapture.test.js +93 -0
  48. package/codeyam-cli/src/utils/__tests__/editorCapture.test.js.map +1 -0
  49. package/codeyam-cli/src/utils/__tests__/editorDevServer.test.js +279 -0
  50. package/codeyam-cli/src/utils/__tests__/editorDevServer.test.js.map +1 -0
  51. package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js +121 -0
  52. package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js.map +1 -0
  53. package/codeyam-cli/src/utils/__tests__/editorImageVerifier.test.js +294 -0
  54. package/codeyam-cli/src/utils/__tests__/editorImageVerifier.test.js.map +1 -0
  55. package/codeyam-cli/src/utils/__tests__/editorJournal.test.js +542 -0
  56. package/codeyam-cli/src/utils/__tests__/editorJournal.test.js.map +1 -0
  57. package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js +393 -0
  58. package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js.map +1 -0
  59. package/codeyam-cli/src/utils/__tests__/editorMockState.test.js +270 -0
  60. package/codeyam-cli/src/utils/__tests__/editorMockState.test.js.map +1 -0
  61. package/codeyam-cli/src/utils/__tests__/editorPreloadHelpers.test.js +217 -0
  62. package/codeyam-cli/src/utils/__tests__/editorPreloadHelpers.test.js.map +1 -0
  63. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js +266 -0
  64. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js.map +1 -0
  65. package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js +107 -0
  66. package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js.map +1 -0
  67. package/codeyam-cli/src/utils/__tests__/editorScenarioLookup.test.js +139 -0
  68. package/codeyam-cli/src/utils/__tests__/editorScenarioLookup.test.js.map +1 -0
  69. package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js +221 -0
  70. package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js.map +1 -0
  71. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +221 -0
  72. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -0
  73. package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js +213 -0
  74. package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js.map +1 -0
  75. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js +1737 -0
  76. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js.map +1 -0
  77. package/codeyam-cli/src/utils/__tests__/git.editor.test.js +134 -0
  78. package/codeyam-cli/src/utils/__tests__/git.editor.test.js.map +1 -0
  79. package/codeyam-cli/src/utils/__tests__/journalCaptureStabilization.test.js +107 -0
  80. package/codeyam-cli/src/utils/__tests__/journalCaptureStabilization.test.js.map +1 -0
  81. package/codeyam-cli/src/utils/__tests__/parseRegisterArg.test.js +101 -0
  82. package/codeyam-cli/src/utils/__tests__/parseRegisterArg.test.js.map +1 -0
  83. package/codeyam-cli/src/utils/__tests__/project.test.js +65 -0
  84. package/codeyam-cli/src/utils/__tests__/project.test.js.map +1 -0
  85. package/codeyam-cli/src/utils/__tests__/scenarioMarkers.test.js +121 -0
  86. package/codeyam-cli/src/utils/__tests__/scenarioMarkers.test.js.map +1 -0
  87. package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js +246 -0
  88. package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js.map +1 -0
  89. package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js +25 -5
  90. package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js.map +1 -1
  91. package/codeyam-cli/src/utils/__tests__/templateConsistency.test.js +51 -0
  92. package/codeyam-cli/src/utils/__tests__/templateConsistency.test.js.map +1 -0
  93. package/codeyam-cli/src/utils/__tests__/webappDetection.test.js +142 -0
  94. package/codeyam-cli/src/utils/__tests__/webappDetection.test.js.map +1 -0
  95. package/codeyam-cli/src/utils/backgroundServer.js +2 -2
  96. package/codeyam-cli/src/utils/backgroundServer.js.map +1 -1
  97. package/codeyam-cli/src/utils/buildFlags.js +4 -0
  98. package/codeyam-cli/src/utils/buildFlags.js.map +1 -0
  99. package/codeyam-cli/src/utils/devServerState.js +71 -0
  100. package/codeyam-cli/src/utils/devServerState.js.map +1 -0
  101. package/codeyam-cli/src/utils/editorApi.js +73 -0
  102. package/codeyam-cli/src/utils/editorApi.js.map +1 -0
  103. package/codeyam-cli/src/utils/editorAudit.js +159 -0
  104. package/codeyam-cli/src/utils/editorAudit.js.map +1 -0
  105. package/codeyam-cli/src/utils/editorCapture.js +102 -0
  106. package/codeyam-cli/src/utils/editorCapture.js.map +1 -0
  107. package/codeyam-cli/src/utils/editorDevServer.js +193 -0
  108. package/codeyam-cli/src/utils/editorDevServer.js.map +1 -0
  109. package/codeyam-cli/src/utils/editorEntityChangeStatus.js +44 -0
  110. package/codeyam-cli/src/utils/editorEntityChangeStatus.js.map +1 -0
  111. package/codeyam-cli/src/utils/editorImageVerifier.js +155 -0
  112. package/codeyam-cli/src/utils/editorImageVerifier.js.map +1 -0
  113. package/codeyam-cli/src/utils/editorJournal.js +225 -0
  114. package/codeyam-cli/src/utils/editorJournal.js.map +1 -0
  115. package/codeyam-cli/src/utils/editorLoaderHelpers.js +81 -0
  116. package/codeyam-cli/src/utils/editorLoaderHelpers.js.map +1 -0
  117. package/codeyam-cli/src/utils/editorMockState.js +248 -0
  118. package/codeyam-cli/src/utils/editorMockState.js.map +1 -0
  119. package/codeyam-cli/src/utils/editorPreloadHelpers.js +135 -0
  120. package/codeyam-cli/src/utils/editorPreloadHelpers.js.map +1 -0
  121. package/codeyam-cli/src/utils/editorPreview.js +106 -0
  122. package/codeyam-cli/src/utils/editorPreview.js.map +1 -0
  123. package/codeyam-cli/src/utils/editorScenarioSwitch.js +112 -0
  124. package/codeyam-cli/src/utils/editorScenarioSwitch.js.map +1 -0
  125. package/codeyam-cli/src/utils/editorScenarios.js +96 -0
  126. package/codeyam-cli/src/utils/editorScenarios.js.map +1 -0
  127. package/codeyam-cli/src/utils/editorSeedAdapter.js +173 -0
  128. package/codeyam-cli/src/utils/editorSeedAdapter.js.map +1 -0
  129. package/codeyam-cli/src/utils/entityChangeStatus.js +347 -0
  130. package/codeyam-cli/src/utils/entityChangeStatus.js.map +1 -0
  131. package/codeyam-cli/src/utils/entityChangeStatus.server.js +158 -0
  132. package/codeyam-cli/src/utils/entityChangeStatus.server.js.map +1 -0
  133. package/codeyam-cli/src/utils/git.js +51 -0
  134. package/codeyam-cli/src/utils/git.js.map +1 -1
  135. package/codeyam-cli/src/utils/install-skills.js +28 -17
  136. package/codeyam-cli/src/utils/install-skills.js.map +1 -1
  137. package/codeyam-cli/src/utils/parseRegisterArg.js +31 -0
  138. package/codeyam-cli/src/utils/parseRegisterArg.js.map +1 -0
  139. package/codeyam-cli/src/utils/project.js +15 -5
  140. package/codeyam-cli/src/utils/project.js.map +1 -1
  141. package/codeyam-cli/src/utils/scenarioMarkers.js +134 -0
  142. package/codeyam-cli/src/utils/scenarioMarkers.js.map +1 -0
  143. package/codeyam-cli/src/utils/scenariosManifest.js +112 -0
  144. package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -0
  145. package/codeyam-cli/src/utils/setupClaudeCodeSettings.js +46 -16
  146. package/codeyam-cli/src/utils/setupClaudeCodeSettings.js.map +1 -1
  147. package/codeyam-cli/src/utils/testRunner.js +1 -1
  148. package/codeyam-cli/src/utils/testRunner.js.map +1 -1
  149. package/codeyam-cli/src/utils/webappDetection.js +21 -0
  150. package/codeyam-cli/src/utils/webappDetection.js.map +1 -1
  151. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js +178 -0
  152. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js.map +1 -0
  153. package/codeyam-cli/src/webserver/app/lib/git.js +396 -0
  154. package/codeyam-cli/src/webserver/app/lib/git.js.map +1 -0
  155. package/codeyam-cli/src/webserver/build/client/assets/{CopyButton-DmJveP3T.js → CopyButton-BPXZwM4t.js} +1 -1
  156. package/codeyam-cli/src/webserver/build/client/assets/{EntityItem-C76mRRiF.js → EntityItem-BcgbViKV.js} +3 -3
  157. package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeIcon-CobE682z.js → EntityTypeIcon-CQIG2qda.js} +9 -9
  158. package/codeyam-cli/src/webserver/build/client/assets/{ReportIssueModal-djPLI-WV.js → ReportIssueModal-BzHcG7SE.js} +3 -3
  159. package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-B76aig_2.js → ScenarioViewer-0DY_NKil.js} +3 -3
  160. package/codeyam-cli/src/webserver/build/client/assets/ViewportInspectBar-oAf2Kqsf.js +1 -0
  161. package/codeyam-cli/src/webserver/build/client/assets/{_index-C96V0n15.js → _index-DLxKhri3.js} +3 -3
  162. package/codeyam-cli/src/webserver/build/client/assets/{activity.(_tab)-BpKzcsJz.js → activity.(_tab)-BcY3q6nt.js} +6 -6
  163. package/codeyam-cli/src/webserver/build/client/assets/addon-canvas-DpzMmAy5.js +1 -0
  164. package/codeyam-cli/src/webserver/build/client/assets/addon-fit-YJmn1quW.js +12 -0
  165. package/codeyam-cli/src/webserver/build/client/assets/addon-webgl-DI8QOUvO.js +58 -0
  166. package/codeyam-cli/src/webserver/build/client/assets/{agent-transcripts-D9hemwl6.js → agent-transcripts-Bni3iiUj.js} +5 -5
  167. package/codeyam-cli/src/webserver/build/client/assets/api.editor-audit-l0sNRNKZ.js +1 -0
  168. package/codeyam-cli/src/webserver/build/client/assets/api.editor-file-diff-l0sNRNKZ.js +1 -0
  169. package/codeyam-cli/src/webserver/build/client/assets/api.editor-file-l0sNRNKZ.js +1 -0
  170. package/codeyam-cli/src/webserver/build/client/assets/api.editor-load-commit-l0sNRNKZ.js +1 -0
  171. package/codeyam-cli/src/webserver/build/client/assets/api.editor-project-info-l0sNRNKZ.js +1 -0
  172. package/codeyam-cli/src/webserver/build/client/assets/{book-open-D_nMCFmP.js → book-open-BYOypzCa.js} +2 -2
  173. package/codeyam-cli/src/webserver/build/client/assets/{chevron-down-BH2h1Ea2.js → chevron-down-C_Pmso5S.js} +2 -2
  174. package/codeyam-cli/src/webserver/build/client/assets/{circle-check-DyIKORY6.js → circle-check-BVMi9VA5.js} +2 -2
  175. package/codeyam-cli/src/webserver/build/client/assets/{copy-NDbZjXao.js → copy-n2FB0_Sw.js} +3 -3
  176. package/codeyam-cli/src/webserver/build/client/assets/createLucideIcon-CC6AbExI.js +41 -0
  177. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-Csi0_PMl.js +1 -0
  178. package/codeyam-cli/src/webserver/build/client/assets/editor-BuT_Huj0.js +10 -0
  179. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-B7ztwLut.js +41 -0
  180. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-CrjR3zZW.js → entity._sha._-BF4oLwaE.js} +3 -3
  181. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-D5rYBT5x.js +6 -0
  182. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-CF164ouH.js +6 -0
  183. package/codeyam-cli/src/webserver/build/client/assets/{files-DO4CZ16O.js → files-BZrlFE1F.js} +1 -1
  184. package/codeyam-cli/src/webserver/build/client/assets/git-DdZcvjGh.js +1 -0
  185. package/codeyam-cli/src/webserver/build/client/assets/globals-BkWJ_UNc.css +1 -0
  186. package/codeyam-cli/src/webserver/build/client/assets/index-yHOVb4rc.js +15 -0
  187. package/codeyam-cli/src/webserver/build/client/assets/{loader-circle-BAXYRVEO.js → loader-circle-DaAZ_H2w.js} +2 -2
  188. package/codeyam-cli/src/webserver/build/client/assets/manifest-b0f1372e.js +1 -0
  189. package/codeyam-cli/src/webserver/build/client/assets/{memory-FweZHj5U.js → memory-Bl2rpw8u.js} +13 -10
  190. package/codeyam-cli/src/webserver/build/client/assets/{pause-DTAcYxBt.js → pause-f5-1lKBt.js} +3 -3
  191. package/codeyam-cli/src/webserver/build/client/assets/{root-DiRdBreB.js → root-B_X8HS1x.js} +18 -18
  192. package/codeyam-cli/src/webserver/build/client/assets/{search-fKo7v0Zo.js → search-Di64LWVb.js} +2 -2
  193. package/codeyam-cli/src/webserver/build/client/assets/{settings-DfuTtcJP.js → settings-0OrEMU6J.js} +1 -1
  194. package/codeyam-cli/src/webserver/build/client/assets/{simulations-B3aOzpCZ.js → simulations-DWT-CvLy.js} +1 -1
  195. package/codeyam-cli/src/webserver/build/client/assets/{terminal-BG4heKCG.js → terminal-Br7MOqts.js} +3 -3
  196. package/codeyam-cli/src/webserver/build/client/assets/{triangle-alert-DtSmdtM4.js → triangle-alert-BLdiCuG-.js} +2 -2
  197. package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-CrAK28Bc.js +1 -0
  198. package/codeyam-cli/src/webserver/build/client/assets/xterm-BqvuqXEL.js +27 -0
  199. package/codeyam-cli/src/webserver/build/server/assets/{index-BzAbACSx.js → index-CbF6h3dj.js} +1 -1
  200. package/codeyam-cli/src/webserver/build/server/assets/server-build-DRFwTJqO.js +367 -0
  201. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  202. package/codeyam-cli/src/webserver/build-info.json +5 -5
  203. package/codeyam-cli/src/webserver/editorProxy.js +383 -50
  204. package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
  205. package/codeyam-cli/src/webserver/scripts/codeyam-preload.mjs +414 -0
  206. package/codeyam-cli/src/webserver/scripts/journalCapture.ts +94 -4
  207. package/codeyam-cli/src/webserver/server.js +93 -12
  208. package/codeyam-cli/src/webserver/server.js.map +1 -1
  209. package/codeyam-cli/src/webserver/terminalServer.js +65 -112
  210. package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
  211. package/codeyam-cli/templates/chrome-extension-react/EXTENSION_SETUP.md +75 -0
  212. package/codeyam-cli/templates/chrome-extension-react/gitignore +15 -0
  213. package/codeyam-cli/templates/chrome-extension-react/index.html +12 -0
  214. package/codeyam-cli/templates/chrome-extension-react/package.json +26 -0
  215. package/codeyam-cli/templates/chrome-extension-react/popup.html +12 -0
  216. package/codeyam-cli/templates/chrome-extension-react/public/manifest.json +15 -0
  217. package/codeyam-cli/templates/chrome-extension-react/src/background/service-worker.ts +7 -0
  218. package/codeyam-cli/templates/chrome-extension-react/src/globals.css +6 -0
  219. package/codeyam-cli/templates/chrome-extension-react/src/lib/storage.ts +37 -0
  220. package/codeyam-cli/templates/chrome-extension-react/src/popup/App.tsx +12 -0
  221. package/codeyam-cli/templates/chrome-extension-react/src/popup/main.tsx +10 -0
  222. package/codeyam-cli/templates/chrome-extension-react/tsconfig.json +24 -0
  223. package/codeyam-cli/templates/chrome-extension-react/vite.config.ts +35 -0
  224. package/codeyam-cli/templates/editor-step-hook.py +95 -9
  225. package/codeyam-cli/templates/expo-react-native/MOBILE_SETUP.md +89 -0
  226. package/codeyam-cli/templates/expo-react-native/app/(tabs)/_layout.tsx +33 -0
  227. package/codeyam-cli/templates/expo-react-native/app/(tabs)/index.tsx +12 -0
  228. package/codeyam-cli/templates/expo-react-native/app/(tabs)/settings.tsx +12 -0
  229. package/codeyam-cli/templates/expo-react-native/app/_layout.tsx +12 -0
  230. package/codeyam-cli/templates/expo-react-native/app.json +18 -0
  231. package/codeyam-cli/templates/expo-react-native/babel.config.js +9 -0
  232. package/codeyam-cli/templates/expo-react-native/gitignore +12 -0
  233. package/codeyam-cli/templates/expo-react-native/global.css +3 -0
  234. package/codeyam-cli/templates/expo-react-native/lib/storage.ts +32 -0
  235. package/codeyam-cli/templates/expo-react-native/metro.config.js +6 -0
  236. package/codeyam-cli/templates/expo-react-native/nativewind-env.d.ts +1 -0
  237. package/codeyam-cli/templates/expo-react-native/package.json +37 -0
  238. package/codeyam-cli/templates/expo-react-native/tailwind.config.js +10 -0
  239. package/codeyam-cli/templates/expo-react-native/tsconfig.json +10 -0
  240. package/codeyam-cli/templates/nextjs-prisma-sqlite/AUTH_PATTERNS.md +308 -0
  241. package/codeyam-cli/templates/nextjs-prisma-sqlite/AUTH_UPGRADE.md +304 -0
  242. package/codeyam-cli/templates/nextjs-prisma-sqlite/DATABASE.md +112 -0
  243. package/codeyam-cli/templates/nextjs-prisma-sqlite/FEATURE_PATTERNS.md +37 -0
  244. package/codeyam-cli/templates/nextjs-prisma-sqlite/app/codeyam-isolate/layout.tsx +12 -0
  245. package/codeyam-cli/templates/nextjs-prisma-sqlite/app/lib/prisma.ts +9 -4
  246. package/codeyam-cli/templates/nextjs-prisma-sqlite/env +4 -0
  247. package/codeyam-cli/templates/nextjs-prisma-sqlite/gitignore +21 -0
  248. package/codeyam-cli/templates/nextjs-prisma-sqlite/package.json +4 -1
  249. package/codeyam-cli/templates/nextjs-prisma-sqlite/prisma/seed.ts +4 -1
  250. package/codeyam-cli/templates/nextjs-prisma-sqlite/seed-adapter.ts +92 -0
  251. package/codeyam-cli/templates/nextjs-prisma-sqlite/vitest.config.ts +13 -0
  252. package/codeyam-cli/templates/{nextjs-prisma-sqlite/PRISMA_SETUP.md → nextjs-prisma-supabase/SUPABASE_SETUP.md} +37 -17
  253. package/codeyam-cli/templates/nextjs-prisma-supabase/app/api/todos/route.ts +17 -0
  254. package/codeyam-cli/templates/nextjs-prisma-supabase/app/globals.css +26 -0
  255. package/codeyam-cli/templates/nextjs-prisma-supabase/app/layout.tsx +34 -0
  256. package/codeyam-cli/templates/nextjs-prisma-supabase/app/lib/prisma.ts +20 -0
  257. package/codeyam-cli/templates/nextjs-prisma-supabase/app/lib/supabase.ts +12 -0
  258. package/codeyam-cli/templates/nextjs-prisma-supabase/app/page.tsx +10 -0
  259. package/codeyam-cli/templates/nextjs-prisma-supabase/env +9 -0
  260. package/codeyam-cli/templates/nextjs-prisma-supabase/eslint.config.mjs +11 -0
  261. package/codeyam-cli/templates/nextjs-prisma-supabase/gitignore +40 -0
  262. package/codeyam-cli/templates/nextjs-prisma-supabase/next.config.ts +11 -0
  263. package/codeyam-cli/templates/nextjs-prisma-supabase/package.json +36 -0
  264. package/codeyam-cli/templates/nextjs-prisma-supabase/postcss.config.mjs +7 -0
  265. package/codeyam-cli/templates/nextjs-prisma-supabase/prisma/schema.prisma +27 -0
  266. package/codeyam-cli/templates/nextjs-prisma-supabase/prisma/seed.ts +39 -0
  267. package/codeyam-cli/templates/nextjs-prisma-supabase/prisma.config.ts +12 -0
  268. package/codeyam-cli/templates/nextjs-prisma-supabase/tsconfig.json +34 -0
  269. package/codeyam-cli/templates/{codeyam-dev-mode.md → skills/codeyam-dev-mode/SKILL.md} +2 -2
  270. package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +145 -0
  271. package/codeyam-cli/templates/{codeyam-memory.md → skills/codeyam-memory/SKILL.md} +215 -0
  272. package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/deprecated-prompt.md +100 -0
  273. package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/detect-deprecated-patterns.mjs +139 -0
  274. package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/find-exports.mjs +52 -0
  275. package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/misleading-api-prompt.md +117 -0
  276. package/codeyam-cli/templates/skills/codeyam-memory/scripts/lib/read-json-field.mjs +61 -0
  277. package/codeyam-cli/templates/skills/codeyam-memory/scripts/lib/ripgrep-fallback.mjs +155 -0
  278. package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/analyze-prompt.md +46 -0
  279. package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/cleanup.mjs +13 -0
  280. package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/filter-session.mjs +95 -0
  281. package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/preprocess.mjs +160 -0
  282. package/package.json +15 -10
  283. package/packages/ai/src/lib/generateExecutionFlows.js +0 -11
  284. package/packages/ai/src/lib/generateExecutionFlows.js.map +1 -1
  285. package/packages/analyze/src/lib/ProjectAnalyzer.js +10 -4
  286. package/packages/analyze/src/lib/ProjectAnalyzer.js.map +1 -1
  287. package/packages/analyze/src/lib/asts/index.js +4 -2
  288. package/packages/analyze/src/lib/asts/index.js.map +1 -1
  289. package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js +0 -40
  290. package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js.map +1 -1
  291. package/packages/database/src/lib/kysely/tables/editorScenariosTable.js +20 -0
  292. package/packages/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
  293. package/packages/types/src/enums/ProjectFramework.js +2 -0
  294. package/packages/types/src/enums/ProjectFramework.js.map +1 -1
  295. package/scripts/npm-post-install.cjs +34 -0
  296. package/codeyam-cli/src/webserver/build/client/assets/Terminal-CcG8YTLx.js +0 -41
  297. package/codeyam-cli/src/webserver/build/client/assets/addon-fit-CUXOrorO.js +0 -1
  298. package/codeyam-cli/src/webserver/build/client/assets/createLucideIcon-CMT1jU2q.js +0 -21
  299. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-BiM6z3Do.js +0 -1
  300. package/codeyam-cli/src/webserver/build/client/assets/editor-W_IGJ2Kd.js +0 -7
  301. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-D6SEzMCu.js +0 -6
  302. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-C28BiQzt.js +0 -6
  303. package/codeyam-cli/src/webserver/build/client/assets/git-CFCTYk9I.js +0 -15
  304. package/codeyam-cli/src/webserver/build/client/assets/globals-BZB_H1w2.css +0 -1
  305. package/codeyam-cli/src/webserver/build/client/assets/manifest-8daa4147.js +0 -1
  306. package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-ByhSyh0W.js +0 -1
  307. package/codeyam-cli/src/webserver/build/client/assets/xterm-DMSzMhqy.js +0 -9
  308. package/codeyam-cli/src/webserver/build/server/assets/server-build-OdUocH6P.js +0 -362
  309. package/codeyam-cli/templates/codeyam-editor.md +0 -68
  310. package/scripts/finalize-analyzer.cjs +0 -13
  311. /package/codeyam-cli/templates/{codeyam-diagnose.md → commands/codeyam-diagnose.md} +0 -0
  312. /package/codeyam-cli/templates/{codeyam-debug.md → skills/codeyam-debug/SKILL.md} +0 -0
  313. /package/codeyam-cli/templates/{codeyam-new-rule.md → skills/codeyam-new-rule/SKILL.md} +0 -0
  314. /package/codeyam-cli/templates/{codeyam-setup.md → skills/codeyam-setup/SKILL.md} +0 -0
  315. /package/codeyam-cli/templates/{codeyam-sim.md → skills/codeyam-sim/SKILL.md} +0 -0
  316. /package/codeyam-cli/templates/{codeyam-test.md → skills/codeyam-test/SKILL.md} +0 -0
  317. /package/codeyam-cli/templates/{codeyam-verify.md → skills/codeyam-verify/SKILL.md} +0 -0
@@ -1,11 +1,25 @@
1
1
  import * as fs from 'fs';
2
2
  import * as path from 'path';
3
+ import * as os from 'os';
3
4
  import { fileURLToPath } from 'url';
4
5
  import chalk from 'chalk';
5
6
  import { runAnalysisForEntities } from "../utils/analysisRunner.js";
6
- import { ProgressReporter } from "../utils/progress.js";
7
- import { initializeEnvironment } from "../utils/database.js";
8
- import { loadEntities, loadAnalyses } from "../../../packages/database/index.js";
7
+ import { error as errorLog, ProgressReporter, withoutSpinner, } from "../utils/progress.js";
8
+ import { initializeEnvironment, requireBranchAndProject, testEnvironment, } from "../utils/database.js";
9
+ import { loadAnalyses, loadEntities, updateProjectMetadata, } from "../../../packages/database/index.js";
10
+ import { IS_INTERNAL_BUILD } from "../utils/buildFlags.js";
11
+ import { startBackgroundServer } from "../utils/backgroundServer.js";
12
+ import { installClaudeCodeSkills } from "../utils/install-skills.js";
13
+ import { setupClaudeCodeSettings } from "../utils/setupClaudeCodeSettings.js";
14
+ import { getAnalyzerTemplatePath, isAnalyzerFinalized, } from "../utils/analyzer.js";
15
+ import { APP_FORMATS, TECH_STACKS } from "../data/techStacks.js";
16
+ import { getProjectRoot as getStateProjectRoot } from "../state.js";
17
+ import initCommand from "./init.js";
18
+ import { readManifest, syncManifestToDatabase, } from "../utils/scenariosManifest.js";
19
+ import { clearEditorState, clearEditorUserPrompt, } from "../utils/editorScenarios.js";
20
+ import { validateSeedData, detectSeedAdapter, } from "../utils/editorSeedAdapter.js";
21
+ import { buildEditorApiRequest, callEditorApi, EDITOR_API_SUBCOMMANDS, } from "../utils/editorApi.js";
22
+ import { parseRegisterArg } from "../utils/parseRegisterArg.js";
9
23
  const __filename = fileURLToPath(import.meta.url);
10
24
  const __dirname = path.dirname(__filename);
11
25
  const STEP_LABELS = {
@@ -19,7 +33,9 @@ const STEP_LABELS = {
19
33
  8: 'App Scenarios',
20
34
  9: 'User Scenarios',
21
35
  10: 'Verify',
22
- 11: 'Review',
36
+ 11: 'Journal',
37
+ 12: 'Review',
38
+ 13: 'Present',
23
39
  };
24
40
  /**
25
41
  * Append a JSONL log entry to .codeyam/logs/editor-log.jsonl
@@ -76,15 +92,11 @@ function writeState(root, state) {
76
92
  }
77
93
  /**
78
94
  * Clear the editor state (for starting a new feature).
95
+ * Does NOT clear the user prompt file — that's handled separately
96
+ * via clearEditorUserPrompt when the previous feature commits.
79
97
  */
80
98
  function clearState(root) {
81
- const statePath = getStatePath(root);
82
- try {
83
- fs.unlinkSync(statePath);
84
- }
85
- catch {
86
- // File doesn't exist, that's fine
87
- }
99
+ clearEditorState(root);
88
100
  }
89
101
  /**
90
102
  * Check if a project has been scaffolded (package.json exists).
@@ -119,13 +131,13 @@ function stepHeader(step, title, feature) {
119
131
  console.log();
120
132
  }
121
133
  /**
122
- * Print a colored progress tracker showing all 11 steps.
134
+ * Print a colored progress tracker showing all 13 steps.
123
135
  * Steps before `current` are green ✓, `current` is bold cyan →, future steps are dim ○.
124
136
  */
125
137
  function printProgressTracker(current) {
126
138
  console.log();
127
139
  console.log(chalk.dim('┌─────────────────────────────────────┐'));
128
- for (let i = 1; i <= 11; i++) {
140
+ for (let i = 1; i <= 13; i++) {
129
141
  const label = STEP_LABELS[i];
130
142
  const num = i < 10 ? ` ${i}` : `${i}`;
131
143
  const content = `${num}. ${label.padEnd(28)}`;
@@ -163,7 +175,7 @@ function stopGate(current, opts) {
163
175
  console.log(chalk.yellow('For the CURRENT step (→), show each checklist item with ✓ (done) or ✗ (skipped + reason).'));
164
176
  console.log(chalk.yellow('If any items are ✗, explain why and ask if the user wants to address them.'));
165
177
  console.log();
166
- if (current < 10) {
178
+ if (current < 13) {
167
179
  console.log(chalk.green('When done, run: ') +
168
180
  chalk.bold(`codeyam editor ${current + 1}`));
169
181
  }
@@ -174,6 +186,218 @@ function stopGate(current, opts) {
174
186
  }
175
187
  console.log();
176
188
  }
189
+ /**
190
+ * Print a RESUMING header with step-specific investigation instructions.
191
+ * Called when a step is re-entered (prevState.step === current step).
192
+ */
193
+ function printResumptionHeader(step) {
194
+ const port = getServerPort();
195
+ const checks = {
196
+ 1: [
197
+ 'Check if plan was already written to context file:\n `cat .codeyam/editor-mode-context.md`',
198
+ ],
199
+ 2: ['Check if project files already exist:\n `ls package.json src/`'],
200
+ 3: ['This is a confirmation step — just re-present to user'],
201
+ 4: [
202
+ 'Check if extraction plan already exists in context file:\n `cat .codeyam/editor-mode-context.md`',
203
+ ],
204
+ 5: [
205
+ 'Check if components/functions already extracted:\n `ls src/components/ src/lib/`',
206
+ ],
207
+ 6: [
208
+ 'Check if glossary already populated:\n `cat .codeyam/glossary.json`',
209
+ ],
210
+ 7: ['Check if isolation routes and registered scenarios already exist'],
211
+ 8: ['Check existing scenarios:\n `codeyam editor scenarios`'],
212
+ 9: [
213
+ 'Check existing user-persona scenarios:\n `codeyam editor scenarios`',
214
+ ],
215
+ 10: ['Re-verify is safe to repeat — just re-run the checks'],
216
+ 11: [
217
+ 'Check if a journal entry already exists for this feature:\n `codeyam editor journal-list`',
218
+ 'If an entry exists, use PATCH to update it — do NOT create a new one',
219
+ ],
220
+ 12: ['Re-verify is safe to repeat — just re-run the checks'],
221
+ 13: ['Check if commit already made:\n `git log --oneline -3`'],
222
+ };
223
+ const label = STEP_LABELS[step] || 'Unknown';
224
+ console.log(chalk.bold.yellow(`━━━ RESUMING Step ${step}: ${label} ━━━`));
225
+ console.log(chalk.yellow('This step was already started. Before repeating any actions, investigate:'));
226
+ const items = checks[step] || [];
227
+ for (const item of items) {
228
+ checkbox(item);
229
+ }
230
+ console.log();
231
+ }
232
+ function captureOutput(fn) {
233
+ const stdoutWrite = process.stdout.write.bind(process.stdout);
234
+ const stderrWrite = process.stderr.write.bind(process.stderr);
235
+ const chunks = [];
236
+ const captureWrite = (chunk, encoding, cb) => {
237
+ const text = typeof chunk === 'string'
238
+ ? chunk
239
+ : chunk instanceof Buffer
240
+ ? chunk.toString(typeof encoding === 'string' ? encoding : undefined)
241
+ : String(chunk);
242
+ chunks.push(text);
243
+ if (typeof encoding === 'function') {
244
+ encoding();
245
+ }
246
+ if (typeof cb === 'function') {
247
+ cb();
248
+ }
249
+ return true;
250
+ };
251
+ process.stdout.write = captureWrite;
252
+ process.stderr.write = captureWrite;
253
+ try {
254
+ fn();
255
+ }
256
+ finally {
257
+ process.stdout.write = stdoutWrite;
258
+ process.stderr.write = stderrWrite;
259
+ }
260
+ return chunks.join('');
261
+ }
262
+ function withTempRoot(hasProject, fn) {
263
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'codeyam-editor-debug-'));
264
+ if (hasProject) {
265
+ fs.writeFileSync(path.join(root, 'package.json'), '{"name":"codeyam-editor-debug","private":true}', 'utf8');
266
+ }
267
+ try {
268
+ return fn(root);
269
+ }
270
+ finally {
271
+ try {
272
+ fs.rmSync(root, { recursive: true, force: true });
273
+ }
274
+ catch {
275
+ // Best-effort cleanup
276
+ }
277
+ }
278
+ }
279
+ function makeState(step, feature) {
280
+ const now = new Date().toISOString();
281
+ return {
282
+ feature,
283
+ step,
284
+ label: STEP_LABELS[step],
285
+ startedAt: now,
286
+ featureStartedAt: now,
287
+ };
288
+ }
289
+ function normalizeDebugTarget(raw) {
290
+ const value = raw.trim().toLowerCase();
291
+ if (!value)
292
+ return '';
293
+ if (value === 'setup')
294
+ return 'setup';
295
+ if (value === 'overview')
296
+ return 'overview';
297
+ if (value === 'overview-with-state' || value === 'overview-state') {
298
+ return 'overview-with-state';
299
+ }
300
+ if (/^\d+$/.test(value)) {
301
+ return `step-${value}`;
302
+ }
303
+ if (/^step-?\d+$/.test(value)) {
304
+ const num = value.replace('step', '').replace('-', '');
305
+ return `step-${num}`;
306
+ }
307
+ return value;
308
+ }
309
+ function parseDebugTargets(target) {
310
+ if (!target || target.trim().toLowerCase() === 'all')
311
+ return null;
312
+ const rawTargets = target
313
+ .split(',')
314
+ .map((t) => normalizeDebugTarget(t))
315
+ .filter(Boolean);
316
+ const valid = new Set();
317
+ for (const entry of rawTargets) {
318
+ if (entry === 'setup' ||
319
+ entry === 'overview' ||
320
+ entry === 'overview-with-state') {
321
+ valid.add(entry);
322
+ continue;
323
+ }
324
+ if (entry.startsWith('step-')) {
325
+ const num = parseInt(entry.replace('step-', ''), 10);
326
+ if (!isNaN(num) && num >= 1 && num <= 13) {
327
+ valid.add(`step-${num}`);
328
+ continue;
329
+ }
330
+ }
331
+ throw new Error(`Invalid debug target: "${entry}"`);
332
+ }
333
+ return valid;
334
+ }
335
+ function writeContextSnapshot(root, outDir) {
336
+ const contextDir = path.join(outDir, 'context');
337
+ fs.mkdirSync(contextDir, { recursive: true });
338
+ const entries = [];
339
+ const skillPath = path.join(root, '.claude', 'skills', 'codeyam-editor', 'SKILL.md');
340
+ const skillFallback = path.join(__dirname, '..', '..', 'templates', 'codeyam-editor.md');
341
+ const skillSource = fs.existsSync(skillPath) ? skillPath : skillFallback;
342
+ const skillDest = path.join(contextDir, 'codeyam-editor-skill.md');
343
+ fs.copyFileSync(skillSource, skillDest);
344
+ entries.push({
345
+ label: 'codeyam-editor skill',
346
+ source: skillSource,
347
+ file: path.relative(outDir, skillDest),
348
+ status: fs.existsSync(skillPath) ? 'installed' : 'template',
349
+ });
350
+ const claudePath = path.join(root, 'CLAUDE.md');
351
+ if (fs.existsSync(claudePath)) {
352
+ const dest = path.join(contextDir, 'CLAUDE.md');
353
+ fs.copyFileSync(claudePath, dest);
354
+ entries.push({
355
+ label: 'CLAUDE.md',
356
+ source: claudePath,
357
+ file: path.relative(outDir, dest),
358
+ status: 'project',
359
+ });
360
+ }
361
+ else {
362
+ const fallback = path.join(__dirname, '..', '..', 'templates', 'codeyam-editor-claude.md');
363
+ if (fs.existsSync(fallback)) {
364
+ const dest = path.join(contextDir, 'CLAUDE.md');
365
+ fs.copyFileSync(fallback, dest);
366
+ entries.push({
367
+ label: 'CLAUDE.md',
368
+ source: fallback,
369
+ file: path.relative(outDir, dest),
370
+ status: 'template',
371
+ });
372
+ }
373
+ else {
374
+ entries.push({
375
+ label: 'CLAUDE.md',
376
+ file: path.relative(outDir, path.join(contextDir, 'CLAUDE.md')),
377
+ status: 'missing',
378
+ });
379
+ }
380
+ }
381
+ const contextPath = path.join(root, '.codeyam', 'editor-mode-context.md');
382
+ if (fs.existsSync(contextPath)) {
383
+ const dest = path.join(contextDir, 'editor-mode-context.md');
384
+ fs.copyFileSync(contextPath, dest);
385
+ entries.push({
386
+ label: 'editor-mode-context.md',
387
+ source: contextPath,
388
+ file: path.relative(outDir, dest),
389
+ status: 'project',
390
+ });
391
+ }
392
+ else {
393
+ entries.push({
394
+ label: 'editor-mode-context.md',
395
+ file: path.relative(outDir, path.join(contextDir, 'editor-mode-context.md')),
396
+ status: 'missing',
397
+ });
398
+ }
399
+ return entries;
400
+ }
177
401
  // ─── Setup (no args, no project) ──────────────────────────────────────
178
402
  function printSetup(root) {
179
403
  const port = getServerPort();
@@ -187,12 +411,68 @@ function printSetup(root) {
187
411
  checkbox('Read `.codeyam/editor-mode-context.md` for session state');
188
412
  checkbox('Ask the user what they want to build');
189
413
  console.log();
414
+ // ── App Format Selection ───────────────────────────────────────────
415
+ console.log(chalk.bold('App Format Selection:'));
416
+ console.log(chalk.dim(' After the user describes their project, ask which app formats they want to target.'));
417
+ console.log(chalk.dim(' Use AskUserQuestion with CHECKBOXES (multiple selections allowed):'));
418
+ console.log();
419
+ for (const format of APP_FORMATS) {
420
+ console.log(chalk.yellow(` [ ] ${format.label}`) +
421
+ chalk.dim(` — ${format.description}`));
422
+ }
423
+ console.log();
424
+ // ── Tech Stack Selection ───────────────────────────────────────────
425
+ console.log(chalk.bold('Tech Stack Selection:'));
426
+ console.log(chalk.dim(' Based on the selected formats, present ONLY the matching tech stacks.'));
427
+ console.log(chalk.dim(' A stack matches if ANY of the user\'s selected formats appears in its "Supports" list.'));
428
+ console.log(chalk.dim(' Show ALL matching stacks even if there is only one. Mark the recommended option.'));
429
+ console.log(chalk.dim(' Use AskUserQuestion to let the user pick one.'));
430
+ console.log();
431
+ console.log(chalk.bold(' Available Tech Stacks:'));
432
+ for (const stack of TECH_STACKS) {
433
+ const recommended = stack.recommended ? chalk.green(' (Recommended)') : '';
434
+ const formatLabels = stack.supportedFormats
435
+ .map((f) => APP_FORMATS.find((af) => af.id === f)?.label)
436
+ .join(', ');
437
+ console.log(chalk.bold.yellow(` ${stack.name}`) +
438
+ recommended +
439
+ chalk.dim(` [id: ${stack.id}]`));
440
+ console.log(chalk.dim(` ${stack.description}`));
441
+ console.log(chalk.dim(' Technologies:'));
442
+ for (const tech of stack.technologies) {
443
+ console.log(chalk.dim(` - ${tech}`));
444
+ }
445
+ console.log(chalk.dim(` Supports: ${formatLabels}`));
446
+ console.log();
447
+ }
448
+ console.log(chalk.dim(' If NO stacks match the selected formats, tell the user that support for that format is coming soon'));
449
+ console.log(chalk.dim(' and suggest they pick a supported format for now.'));
450
+ console.log();
451
+ // ── Default Screen Size Selection ──────────────────────────────────
452
+ console.log(chalk.bold('Default Screen Size:'));
453
+ console.log(chalk.dim(' After selecting a tech stack, ask the user to choose the default screen size for previews and captures.'));
454
+ console.log(chalk.dim(' Use AskUserQuestion with RADIO buttons (single selection):'));
455
+ console.log();
456
+ console.log(chalk.yellow(' ( ) Desktop') + chalk.dim(' — 1440 × 900'));
457
+ console.log(chalk.yellow(' ( ) Laptop') + chalk.dim(' — 1024 × 768'));
458
+ console.log(chalk.yellow(' ( ) Tablet') + chalk.dim(' — 768 × 1024'));
459
+ console.log(chalk.yellow(' ( ) Mobile') + chalk.dim(' — 375 × 667'));
460
+ console.log(chalk.yellow(' ( ) Custom') + chalk.dim(' — ask for width × height'));
461
+ console.log();
462
+ console.log(chalk.dim(' Pre-select based on app format: mobile-app/mobile-responsive-web-app → Mobile, chrome-extension → Custom (400×600), web-app/desktop-app → Desktop.'));
463
+ console.log(chalk.dim(' If only one obvious choice, confirm it rather than asking.'));
464
+ console.log(chalk.dim(' Save the choice via: curl -s -X POST http://localhost:PORT/api/editor-project-info -H "Content-Type: application/json" -d \'{"defaultScreenSize":{"name":"Desktop","width":1440,"height":900}}\''));
465
+ console.log();
190
466
  console.log(chalk.bold.red('━━━ STOP ━━━'));
191
467
  console.log();
192
- console.log(chalk.red('Ask the user what they want to build, then run:'));
468
+ console.log(chalk.red('Ask the user what they want to build, then ask about app formats, then present tech stacks, then choose a default screen size. Then run:'));
193
469
  console.log();
194
- console.log(chalk.green(' ') + chalk.bold('codeyam editor 1 --feature "Feature Name"'));
470
+ console.log(chalk.green(' ') +
471
+ chalk.bold('codeyam editor 1 --feature "Feature Name" --app-formats "FORMAT_IDS" --tech-stack "STACK_ID" --prompt "the user\'s original message"'));
195
472
  console.log(chalk.dim(' Replace "Feature Name" with a short title for what the user described.'));
473
+ console.log(chalk.dim(' Replace FORMAT_IDS with the comma-separated format IDs the user selected (e.g., "chrome-extension" or "web-app,mobile-responsive-web-app").'));
474
+ console.log(chalk.dim(' Replace STACK_ID with the tech stack id shown in [brackets] above (e.g., "nextjs-prisma-sqlite" or "chrome-extension-react").'));
475
+ console.log(chalk.dim(" Pass --prompt with the user's exact original request so it appears in the journal."));
196
476
  console.log(chalk.dim(' Step 1 will guide you through planning and getting user confirmation before any code is written.'));
197
477
  console.log();
198
478
  }
@@ -210,9 +490,13 @@ function printCycleOverview(root, state) {
210
490
  console.log(chalk.dim('Or run ') +
211
491
  chalk.bold('codeyam editor 1') +
212
492
  chalk.dim(' to start a new feature'));
493
+ console.log();
494
+ console.log(chalk.yellow('If the user reports a bug or requests any change, run: ') +
495
+ chalk.bold('codeyam editor change'));
496
+ console.log(chalk.yellow('This applies even after committing — always use the change workflow.'));
213
497
  }
214
498
  else {
215
- console.log('Each feature follows 11 steps. You MUST run each command in order:');
499
+ console.log('Each feature follows 13 steps. You MUST run each command in order:');
216
500
  console.log();
217
501
  console.log(` ${chalk.bold.yellow(' 1')} ${chalk.bold('Plan')} — Plan the feature, confirm with user`);
218
502
  console.log(` ${chalk.bold.yellow(' 2')} ${chalk.bold('Prototype')} — Build a working prototype fast`);
@@ -224,15 +508,23 @@ function printCycleOverview(root, state) {
224
508
  console.log(` ${chalk.bold.yellow(' 8')} ${chalk.bold('App Scenarios')} — Create app-level scenarios`);
225
509
  console.log(` ${chalk.bold.yellow(' 9')} ${chalk.bold('User Scenarios')} — Create user-persona scenarios`);
226
510
  console.log(` ${chalk.bold.yellow('10')} ${chalk.bold('Verify')} — Review screenshots, check for errors`);
227
- console.log(` ${chalk.bold.yellow('11')} ${chalk.bold('Review')} Present summary, get approval`);
511
+ console.log(` ${chalk.bold.yellow('11')} ${chalk.bold('Journal')} Create/update journal entry`);
512
+ console.log(` ${chalk.bold.yellow('12')} ${chalk.bold('Review')} — Verify screenshots and audit`);
513
+ console.log(` ${chalk.bold.yellow('13')} ${chalk.bold('Present')} — Present summary, get approval`);
228
514
  console.log();
229
515
  console.log(chalk.green('Start now: ') + chalk.bold('codeyam editor 1'));
516
+ console.log(chalk.dim(' If the user already described what they want, pass it: codeyam editor 1 --prompt "their message"'));
230
517
  }
231
518
  console.log();
232
519
  }
233
520
  // ─── Step 1: Plan ─────────────────────────────────────────────────────
234
- function printStep1(root, feature) {
235
- clearState(root);
521
+ function printStep1(root, feature, options, userPrompt) {
522
+ const port = getServerPort();
523
+ const prevState = readState(root);
524
+ const isResuming = prevState?.step === 1;
525
+ if (!isResuming) {
526
+ clearState(root);
527
+ }
236
528
  // If feature is provided, save initial state so step 2 can pick it up
237
529
  if (feature) {
238
530
  const now = new Date().toISOString();
@@ -240,12 +532,23 @@ function printStep1(root, feature) {
240
532
  feature,
241
533
  step: 1,
242
534
  label: STEP_LABELS[1],
243
- startedAt: now,
244
- featureStartedAt: now,
535
+ startedAt: isResuming ? prevState.startedAt : now,
536
+ featureStartedAt: isResuming ? prevState.featureStartedAt : now,
537
+ appFormats: options?.appFormats || prevState?.appFormats,
538
+ techStackId: options?.techStackId || prevState?.techStackId,
245
539
  });
246
540
  }
541
+ // Save the user's original prompt to a separate file
542
+ if (userPrompt) {
543
+ const promptPath = path.join(root, '.codeyam', 'editor-user-prompt.txt');
544
+ fs.mkdirSync(path.dirname(promptPath), { recursive: true });
545
+ fs.writeFileSync(promptPath, userPrompt, 'utf8');
546
+ }
247
547
  logEvent(root, 'step', { step: 1, label: 'Plan', feature });
248
548
  stepHeader(1, 'Plan', feature);
549
+ if (isResuming) {
550
+ printResumptionHeader(1);
551
+ }
249
552
  console.log('Plan the feature before writing ANY code.');
250
553
  console.log();
251
554
  console.log(chalk.bold('Checklist:'));
@@ -254,15 +557,19 @@ function printStep1(root, feature) {
254
557
  checkbox('Ask clarifying questions using `AskUserQuestion` with selectable options');
255
558
  console.log(chalk.dim(' Use AskUserQuestion for EVERY clarifying question — give 2-4 concrete options the user can pick from.'));
256
559
  console.log(chalk.dim(' Focus on what the USER will see and do, not on databases, APIs, or components.'));
560
+ console.log(chalk.dim(' Do NOT ask about tech stack, frameworks, libraries, or implementation details — only ask about user-facing choices.'));
257
561
  console.log(chalk.dim(' Good: "What should happen when there are no results?" → options: "Show empty state message", "Show suggestions"'));
258
562
  console.log(chalk.dim(' Bad: Free-form text asking "What do you think about the data model?"'));
259
563
  console.log(chalk.dim(' You can ask up to 4 questions at once. Bundle related questions into a single AskUserQuestion call.'));
260
564
  checkbox("Summarize what you'll build in plain language the user can verify against their vision");
261
- console.log();
262
- console.log(chalk.bold('Present a selection menu to the user (use AskUserQuestion):'));
263
- console.log(chalk.green(' "Looks good, start building!"') +
264
- chalk.dim(' proceed to step 2'));
265
- console.log(chalk.yellow(' "I\'d like some changes"') +
565
+ checkbox('Set the project title and description for the App tab:');
566
+ console.log(chalk.dim(` curl -s -X POST http://localhost:${port}/api/editor-project-info \\`));
567
+ console.log(chalk.dim(' -H "Content-Type: application/json" \\'));
568
+ console.log(chalk.dim(' -d \'{"projectTitle":"My App","projectDescription":"A short description of what this app does"}\''));
569
+ console.log();
570
+ console.log(chalk.bold('Present a selection menu to the user (use AskUserQuestion with these EXACT option labels):'));
571
+ console.log(chalk.green(' Option 1 label: "This plan is accurate, let\'s prototype it!"') + chalk.dim(' — proceed to step 2'));
572
+ console.log(chalk.yellow(' Option 2 label: "I\'d like some changes"') +
266
573
  chalk.dim(' — user describes changes, you revise the plan, then re-present'));
267
574
  console.log();
268
575
  console.log(chalk.dim('This step is for understanding user goals and getting buy-in. Code comes in Step 2.'));
@@ -288,82 +595,68 @@ function printStep1(root, feature) {
288
595
  function printStep2(root, feature) {
289
596
  const port = getServerPort();
290
597
  const projectExists = hasProject(root);
598
+ const prevState = readState(root);
599
+ const isResuming = prevState?.step === 2;
291
600
  const now = new Date().toISOString();
292
601
  writeState(root, {
293
602
  feature,
294
603
  step: 2,
295
604
  label: STEP_LABELS[2],
296
- startedAt: now,
297
- featureStartedAt: now,
605
+ startedAt: isResuming ? prevState.startedAt : now,
606
+ featureStartedAt: isResuming ? prevState.featureStartedAt : now,
607
+ appFormats: prevState?.appFormats,
608
+ techStackId: prevState?.techStackId,
298
609
  });
299
610
  logEvent(root, 'step', { step: 2, label: 'Prototype', feature });
300
611
  stepHeader(2, 'Prototype', feature);
612
+ if (isResuming) {
613
+ printResumptionHeader(2);
614
+ }
301
615
  console.log('Build fast with real data. Prioritize speed over quality.');
302
616
  console.log();
303
617
  // If no project exists yet, include scaffolding instructions first
304
618
  if (!projectExists) {
305
- const templateDir = path.join(__dirname, '..', '..', 'templates', 'nextjs-prisma-sqlite');
306
- const hasTemplate = fs.existsSync(templateDir);
307
619
  console.log(chalk.bold('Scaffold the project:'));
308
- if (hasTemplate) {
309
- checkbox('Copy the project template and install dependencies');
310
- console.log();
311
- console.log(chalk.dim(' Copy template (Next.js + Prisma 7 + SQLite, pre-configured):'));
312
- console.log(chalk.dim(` cp -r ${templateDir}/* .`));
313
- console.log(chalk.dim(` cp ${templateDir}/.env .`));
314
- console.log(chalk.dim(` cp ${templateDir}/gitignore .gitignore`));
315
- console.log(chalk.dim(' npm install'));
316
- console.log();
317
- checkbox('Define your data models in `prisma/schema.prisma`');
318
- console.log(chalk.dim(" Replace the placeholder Todo model with your app's models"));
319
- console.log();
320
- checkbox('Push schema and seed the database');
321
- console.log(chalk.dim(' npm run db:push'));
322
- console.log(chalk.dim(' # Edit prisma/seed.ts with your seed data, then:'));
323
- console.log(chalk.dim(' npm run db:seed'));
324
- console.log();
325
- console.log(chalk.dim(' See PRISMA_SETUP.md for Prisma patterns and important warnings.'));
326
- console.log(chalk.dim(' Key: import { prisma } from "@/app/lib/prisma" in API routes.'));
327
- console.log(chalk.dim(' Key: Seed scripts must use the adapter pattern (see prisma/seed.ts).'));
328
- }
329
- else {
330
- checkbox('Scaffold the project (Next.js recommended) using temp-dir-rsync pattern');
331
- console.log();
332
- console.log(chalk.dim(' Scaffolding pattern (avoids .claude/ conflicts):'));
333
- console.log(chalk.dim(' SCAFFOLD_DIR="/tmp/codeyam-scaffold-$$" && mkdir -p "$SCAFFOLD_DIR"'));
334
- console.log(chalk.dim(' npx create-next-app@latest "$SCAFFOLD_DIR" --typescript --tailwind --eslint \\'));
335
- console.log(chalk.dim(' --app --no-src-dir --no-import-alias --no-react-compiler --turbopack'));
336
- console.log(chalk.dim(' rsync -a --exclude=\'.git\' "$SCAFFOLD_DIR/" . && rm -rf "$SCAFFOLD_DIR"'));
337
- console.log();
338
- checkbox('Set up Prisma + SQLite database');
339
- console.log(chalk.dim(' npm install prisma @prisma/client @prisma/adapter-better-sqlite3 better-sqlite3'));
340
- console.log(chalk.dim(' npm install -D @types/better-sqlite3'));
341
- console.log(chalk.dim(' npx prisma init --datasource-provider sqlite'));
342
- console.log(chalk.dim(' # Define schema, then: npx prisma db push'));
343
- }
620
+ checkbox('Run `codeyam editor template` to scaffold, install dependencies, init git, and configure CodeYam');
621
+ console.log(chalk.dim(' This copies the Next.js + Prisma 7 + SQLite template, runs npm install,'));
622
+ console.log(chalk.dim(' initializes git, runs codeyam init, and refreshes the editor — all in one command.'));
344
623
  console.log();
345
- checkbox('Initialize git so analysis and commits work');
346
- console.log(chalk.dim(' git init && git add -A && git commit -m "Initial commit"'));
624
+ checkbox('Define your data models in `prisma/schema.prisma`');
625
+ console.log(chalk.dim(" Replace the placeholder Todo model with your app's models"));
347
626
  console.log();
348
- checkbox('Run `codeyam init --force --keep-server` to detect the project and configure it');
349
- console.log(chalk.dim(' This auto-detects webapps and configures the start command for analysis'));
627
+ checkbox('Push schema and seed the database');
628
+ console.log(chalk.dim(' npm run db:push'));
629
+ console.log(chalk.dim(' # Edit prisma/seed.ts with your seed data, then:'));
630
+ console.log(chalk.dim(' npm run db:seed'));
631
+ console.log(chalk.dim(` # After re-seeding, restart the dev server to pick up fresh data:`));
632
+ console.log(chalk.dim(` # codeyam editor dev-server '{"action":"restart"}'`));
350
633
  console.log();
351
- checkbox('Verify `.codeyam/config.json` has `startCommand` in the webapp entry');
352
- console.log(chalk.dim(' Should look like: "startCommand": { "command": "sh", "args": ["-c", "npm run dev -- --port $PORT"] }'));
353
- console.log(chalk.dim(' If missing, add it manually. $PORT is replaced at runtime by the analyzer.'));
354
- console.log();
355
- checkbox(`Notify CodeYam: \`curl -s -X POST http://localhost:${port}/api/editor-refresh\``);
634
+ console.log(chalk.dim(' See PRISMA_SETUP.md for Prisma patterns and important warnings.'));
635
+ console.log(chalk.dim(' Key: import { prisma } from "@/app/lib/prisma" in API routes.'));
636
+ console.log(chalk.dim(' Key: Seed scripts must use the adapter pattern (see prisma/seed.ts).'));
356
637
  console.log();
357
638
  console.log(chalk.bold('Build the feature:'));
358
639
  }
359
640
  console.log(chalk.bold('Checklist:'));
360
- checkbox('Create/update API routes (real database reads via Prisma)');
641
+ checkbox('Create API routes that read from the database via Prisma');
361
642
  checkbox('Seed the database with demo data');
362
- checkbox('Build the page/feature UI components');
643
+ checkbox('Create `.codeyam/seed-adapter.ts` so CodeYam can seed the database for scenarios');
644
+ console.log(chalk.dim(' The seed adapter reads a JSON file (path passed as CLI arg), wipes tables, inserts rows.'));
645
+ console.log(chalk.dim(" Use the project's own ORM (Prisma, Drizzle, etc.). See template for example."));
646
+ console.log(chalk.dim(' Run with: npx tsx .codeyam/seed-adapter.ts <path-to-seed-data.json>'));
363
647
  checkbox('Verify the dev server shows the changes');
648
+ checkbox('If the feature involves auth, email, payments, or other common patterns: read FEATURE_PATTERNS.md');
649
+ console.log();
650
+ console.log(chalk.bold.cyan('Keep the preview moving:'));
651
+ console.log(chalk.cyan(' The user is watching the preview. Refresh it after each meaningful change:'));
652
+ console.log(chalk.cyan(` codeyam editor preview`));
653
+ console.log(chalk.cyan(' Refresh after: first visible page, adding each UI section, seeding data, styling.'));
654
+ console.log(chalk.cyan(' Aim for 4-8+ refreshes during prototyping — not one big reveal at the end.'));
655
+ console.log(chalk.cyan(' If you build a NEW page (e.g., /drinks/[id]), navigate the preview there:'));
656
+ console.log(chalk.cyan(` codeyam editor preview '{"path":"/drinks/1"}'`));
364
657
  console.log();
365
658
  console.log(chalk.bold('Verify the dev server:'));
366
- console.log(chalk.dim(` # Get dev server URL: curl -s http://localhost:${port}/api/editor-dev-server`));
659
+ console.log(chalk.dim(` # Get dev server URL: codeyam editor dev-server`));
367
660
  console.log(chalk.dim(' # Check page loads: curl -s -o /dev/null -w "%{http_code}" http://localhost:<dev-port>'));
368
661
  console.log(chalk.dim(' # Check API routes: curl -s http://localhost:<dev-port>/api/your-route'));
369
662
  console.log();
@@ -371,7 +664,10 @@ function printStep2(root, feature) {
371
664
  console.log(chalk.yellow(' Verify everything works before presenting the prototype to the user.'));
372
665
  checkbox('Verify the page loads: curl the dev server URL and confirm HTTP 200 (not an error page)');
373
666
  checkbox('Verify API routes return valid JSON: curl each route and confirm no error responses');
374
- checkbox('Check for broken images: look at any <img> src attributes and verify the assets exist');
667
+ checkbox('Check for broken images: `codeyam editor verify-images \'{"paths":["/"], "imageUrls":["url1","url2"]}\'`');
668
+ console.log(chalk.dim(' Pass ALL page paths and ALL image URLs you used in seed data / API responses.'));
669
+ console.log(chalk.dim(' Client-rendered pages need imageUrls — the HTML shell has no images to scan.'));
670
+ console.log(chalk.dim(' Fix or replace any broken image URLs in the seed data before proceeding.'));
375
671
  checkbox('Check the dev server terminal output for runtime errors (missing modules, failed imports)');
376
672
  console.log(chalk.dim(' If any check fails, fix the issue and re-verify before proceeding.'));
377
673
  console.log();
@@ -382,32 +678,48 @@ function printStep2(root, feature) {
382
678
  function printStep3(root, feature) {
383
679
  const port = getServerPort();
384
680
  const prevState = readState(root);
681
+ const isResuming = prevState?.step === 3;
682
+ const now = new Date().toISOString();
385
683
  writeState(root, {
386
684
  feature,
387
685
  step: 3,
388
686
  label: STEP_LABELS[3],
389
- startedAt: new Date().toISOString(),
390
- featureStartedAt: prevState?.featureStartedAt || new Date().toISOString(),
687
+ startedAt: isResuming ? prevState.startedAt : now,
688
+ featureStartedAt: prevState?.featureStartedAt || now,
391
689
  });
392
690
  logEvent(root, 'step', { step: 3, label: 'Confirm', feature });
393
691
  stepHeader(3, 'Confirm', feature);
692
+ if (isResuming) {
693
+ printResumptionHeader(3);
694
+ }
394
695
  console.log('Summarize what was built and get user confirmation.');
395
696
  console.log();
396
697
  console.log(chalk.bold('Before presenting — verify everything works:'));
397
- checkbox(`Refresh the preview: \`curl -s -X POST http://localhost:${port}/api/editor-refresh\``);
398
- checkbox('Verify the page loads without errors (no error pages, no blank screens)');
399
- checkbox('Verify all images and assets render correctly (no broken images)');
698
+ checkbox(`Refresh the preview: \`codeyam editor preview\` check the \`preview\` field for \`healthy: false\``);
400
699
  checkbox('Verify API routes return valid data (curl each route)');
401
- console.log(chalk.yellow(' Do not present to the user until all checks pass. Fix issues first.'));
700
+ console.log();
701
+ console.log(chalk.bold.red(' Verify EVERY image loads (this is the #1 source of broken prototypes):'));
702
+ checkbox('Run `codeyam editor verify-images \'{"paths":["/"], "imageUrls":["url1","url2"]}\'`');
703
+ console.log(chalk.dim(' Include ALL page paths and ALL image URLs from seed data / API responses.'));
704
+ console.log(chalk.dim(' Client-rendered pages need imageUrls — the HTML shell has no images.'));
705
+ checkbox('Fix or remove any image that returns non-200 before continuing');
706
+ checkbox('Check for client-side errors: `codeyam editor client-errors`');
707
+ console.log(chalk.dim(' If there are errors, fix the underlying issue before presenting.'));
402
708
  console.log();
403
709
  console.log(chalk.bold('Then present to the user:'));
404
710
  checkbox('Summarize what was built (routes, components, data)');
405
- checkbox('Show the user the current state of the prototype');
406
- console.log();
407
- console.log(chalk.bold('Present a selection menu to the user (use AskUserQuestion):'));
408
- console.log(chalk.green(' "Yes, this looks right!"') +
409
- chalk.dim(' proceed to step 4'));
410
- console.log(chalk.yellow(' "I\'d like some changes"') +
711
+ checkbox('Navigate the preview to the feature\'s primary page: `codeyam editor preview \'{"path":"/your-page"}\'`');
712
+ console.log(chalk.dim(' The user needs to SEE the new feature. If you built a new page, navigate there.'));
713
+ console.log(chalk.dim(' If you modified an existing page, refresh the preview to show the changes.'));
714
+ console.log();
715
+ console.log(chalk.bold.cyan('Guide the user through interactive testing:'));
716
+ checkbox('Tell the user: "The preview is fully interactive — click around to test!"');
717
+ checkbox('Prompt the user to try key interactions: forms, navigation, buttons, etc.');
718
+ checkbox('Ask the user: "Does everything work as expected?"');
719
+ console.log();
720
+ console.log(chalk.bold('Present a selection menu to the user (use AskUserQuestion with these EXACT option labels):'));
721
+ console.log(chalk.green(' Option 1 label: "The live preview is displaying what I expected"') + chalk.dim(' — proceed to step 4'));
722
+ console.log(chalk.yellow(' Option 2 label: "I\'d like some changes"') +
411
723
  chalk.dim(' — user describes changes, you make them, then re-present'));
412
724
  console.log();
413
725
  console.log(chalk.dim('Wait for user approval before moving on. Refactoring and scenarios happen in later steps.'));
@@ -416,15 +728,20 @@ function printStep3(root, feature) {
416
728
  // ─── Step 4: Deconstruct ──────────────────────────────────────────────
417
729
  function printStep4(root, feature) {
418
730
  const prevState = readState(root);
731
+ const isResuming = prevState?.step === 4;
732
+ const now = new Date().toISOString();
419
733
  writeState(root, {
420
734
  feature,
421
735
  step: 4,
422
736
  label: STEP_LABELS[4],
423
- startedAt: new Date().toISOString(),
424
- featureStartedAt: prevState?.featureStartedAt || new Date().toISOString(),
737
+ startedAt: isResuming ? prevState.startedAt : now,
738
+ featureStartedAt: prevState?.featureStartedAt || now,
425
739
  });
426
740
  logEvent(root, 'step', { step: 4, label: 'Deconstruct', feature });
427
741
  stepHeader(4, 'Deconstruct', feature);
742
+ if (isResuming) {
743
+ printResumptionHeader(4);
744
+ }
428
745
  console.log(chalk.bold('Goal: pages contain ONLY components. Components contain ONLY sub-components.'));
429
746
  console.log(chalk.yellow('This step is read and plan only. Code extraction happens in step 5.'));
430
747
  console.log();
@@ -460,16 +777,22 @@ function printStep4(root, feature) {
460
777
  }
461
778
  // ─── Step 5: Extract ──────────────────────────────────────────────────
462
779
  function printStep5(root, feature) {
780
+ const port = getServerPort();
463
781
  const prevState = readState(root);
782
+ const isResuming = prevState?.step === 5;
783
+ const now = new Date().toISOString();
464
784
  writeState(root, {
465
785
  feature,
466
786
  step: 5,
467
787
  label: STEP_LABELS[5],
468
- startedAt: new Date().toISOString(),
469
- featureStartedAt: prevState?.featureStartedAt || new Date().toISOString(),
788
+ startedAt: isResuming ? prevState.startedAt : now,
789
+ featureStartedAt: prevState?.featureStartedAt || now,
470
790
  });
471
791
  logEvent(root, 'step', { step: 5, label: 'Extract', feature });
472
792
  stepHeader(5, 'Extract', feature);
793
+ if (isResuming) {
794
+ printResumptionHeader(5);
795
+ }
473
796
  console.log('Execute your extraction plan from step 4.');
474
797
  console.log();
475
798
  console.log(chalk.bold('Components:'));
@@ -495,6 +818,8 @@ function printStep5(root, feature) {
495
818
  checkbox('Run all tests and verify they pass');
496
819
  checkbox('Page files contain ONLY imports + component composition — no raw HTML tags');
497
820
  checkbox('Every component renders ONE thing or composes sub-components — no multi-section JSX');
821
+ checkbox(`Refresh the preview after each batch of extractions: \`codeyam editor preview\``);
822
+ console.log(chalk.dim(' The user should see the preview stay healthy as you refactor — refresh periodically, not just at the end.'));
498
823
  console.log(chalk.dim('Reuse glossary functions when they fit naturally. Extract a new function when the use case diverges.'));
499
824
  console.log();
500
825
  console.log(chalk.dim('Focus on TDD for functions and extraction for components. Scenarios come in later steps.'));
@@ -503,15 +828,20 @@ function printStep5(root, feature) {
503
828
  // ─── Step 6: Glossary ─────────────────────────────────────────────────
504
829
  function printStep6(root, feature) {
505
830
  const prevState = readState(root);
831
+ const isResuming = prevState?.step === 6;
832
+ const now = new Date().toISOString();
506
833
  writeState(root, {
507
834
  feature,
508
835
  step: 6,
509
836
  label: STEP_LABELS[6],
510
- startedAt: new Date().toISOString(),
511
- featureStartedAt: prevState?.featureStartedAt || new Date().toISOString(),
837
+ startedAt: isResuming ? prevState.startedAt : now,
838
+ featureStartedAt: prevState?.featureStartedAt || now,
512
839
  });
513
840
  logEvent(root, 'step', { step: 6, label: 'Glossary', feature });
514
841
  stepHeader(6, 'Glossary', feature);
842
+ if (isResuming) {
843
+ printResumptionHeader(6);
844
+ }
515
845
  console.log('Record all new functions/components in `.codeyam/glossary.json`.');
516
846
  console.log();
517
847
  console.log(chalk.bold('Checklist:'));
@@ -535,20 +865,31 @@ function printStep6(root, feature) {
535
865
  function printStep7(root, feature) {
536
866
  const port = getServerPort();
537
867
  const prevState = readState(root);
868
+ const isResuming = prevState?.step === 7;
869
+ const now = new Date().toISOString();
538
870
  writeState(root, {
539
871
  feature,
540
872
  step: 7,
541
873
  label: STEP_LABELS[7],
542
- startedAt: new Date().toISOString(),
543
- featureStartedAt: prevState?.featureStartedAt || new Date().toISOString(),
874
+ startedAt: isResuming ? prevState.startedAt : now,
875
+ featureStartedAt: prevState?.featureStartedAt || now,
544
876
  });
545
877
  logEvent(root, 'step', { step: 7, label: 'Analyze', feature });
546
878
  stepHeader(7, 'Analyze and Verify', feature);
879
+ if (isResuming) {
880
+ printResumptionHeader(7);
881
+ }
547
882
  console.log('Verify visual components (via isolation routes) and library functions (via tests).');
548
883
  console.log();
549
884
  console.log(chalk.bold('Visual Components — Component Isolation:'));
550
885
  checkbox('List all files with new/modified visual components from step 5');
551
- checkbox('Ensure `.gitignore` includes `**/codeyam-isolate*` (add if missing)');
886
+ checkbox('Create isolation route dirs: `codeyam editor isolate ComponentA ComponentB ...`');
887
+ console.log(chalk.dim(' This creates app/isolated-components/layout.tsx (with production notFound() guard) and'));
888
+ console.log(chalk.dim(' a directory per component. List ALL components that need isolation routes.'));
889
+ checkbox('Check existing scenarios: `codeyam editor scenarios`');
890
+ console.log(chalk.dim(' Reuse and improve existing scenarios where possible — update mock data'));
891
+ console.log(chalk.dim(' to reflect current changes. Add new scenarios only for genuinely new states.'));
892
+ console.log(chalk.dim(' Ensure at least one scenario clearly demonstrates what changed in this session.'));
552
893
  checkbox('For each visual component:');
553
894
  console.log(chalk.dim(' 1. Read the source AND find where it is used in the app to understand:'));
554
895
  console.log(chalk.dim(' — Props/interface'));
@@ -559,8 +900,8 @@ function printStep7(root, feature) {
559
900
  console.log(chalk.dim(' — Different visual states: loading, error, disabled, selected, hover'));
560
901
  console.log(chalk.dim(' — Boundary values: single item vs many, min/max ratings, very long names'));
561
902
  console.log(chalk.dim(' 3. Create ONE isolation route per component with a scenarios map and ?s= query param:'));
562
- console.log(chalk.dim(' Remix: app/routes/codeyam-isolate.ComponentName.tsx → /codeyam-isolate/ComponentName'));
563
- console.log(chalk.dim(' Next.js: app/codeyam-isolate/ComponentName/page.tsx → /codeyam-isolate/ComponentName'));
903
+ console.log(chalk.dim(' Remix: app/routes/isolated-components.ComponentName.tsx → /isolated-components/ComponentName'));
904
+ console.log(chalk.dim(' Next.js: app/isolated-components/ComponentName/page.tsx → /isolated-components/ComponentName'));
564
905
  console.log(chalk.dim(' The route defines a `scenarios` object mapping scenario names to props,'));
565
906
  console.log(chalk.dim(' reads `?s=ScenarioName` from the URL, and renders the component with those props.'));
566
907
  console.log(chalk.dim(' Wrap the component in a capture container with id="codeyam-capture":'));
@@ -573,15 +914,17 @@ function printStep7(root, feature) {
573
914
  console.log(chalk.dim(' 4. Wait 2 seconds for HMR, then register + capture each scenario:'));
574
915
  console.log(chalk.dim(` codeyam editor register '{"name":"ComponentName - Scenario",`));
575
916
  console.log(chalk.dim(` "componentName":"ComponentName","componentPath":"path/to/file.tsx",`));
576
- console.log(chalk.dim(` "url":"/codeyam-isolate/ComponentName?s=Scenario",`));
917
+ console.log(chalk.dim(` "url":"/isolated-components/ComponentName?s=Scenario",`));
577
918
  console.log(chalk.dim(` "mockData":{"routes":{"/api/...":{"body":[...]}}}}'`));
919
+ console.log(chalk.dim(' Optional: add "viewportWidth" and "viewportHeight" to override the project default screen size'));
920
+ console.log(chalk.dim(' If omitted, captures use the project default from setup. Only override for scenarios that need a different size.'));
578
921
  console.log(chalk.dim(' url is a PATH (starts with /) — the proxy routes it and intercepts API calls'));
579
922
  console.log(chalk.dim(' mockData.routes provides data for API calls the component makes internally'));
580
923
  console.log(chalk.dim(' (omit mockData if the component has no internal API calls)'));
581
924
  console.log(chalk.yellow(' 5. IMPORTANT: Check the register response for `clientErrors` array'));
582
925
  console.log(chalk.yellow(' If clientErrors is non-empty → fix the isolation route and re-register'));
583
926
  console.log(chalk.yellow(' Fix client errors and re-register before moving on'));
584
- console.log(chalk.dim(' Isolation routes stay in the project (in .gitignore) so the editor preview can display them'));
927
+ console.log(chalk.dim(' Isolation routes are committed to git the layout guard blocks them in production'));
585
928
  console.log();
586
929
  console.log(chalk.bold('Library Functions — run tests:'));
587
930
  checkbox('Run ALL test files created in step 5');
@@ -590,29 +933,65 @@ function printStep7(root, feature) {
590
933
  checkbox('If any test fails, fix the source code and re-run');
591
934
  console.log();
592
935
  console.log(chalk.dim('Do not proceed until both component isolations and library tests pass.'));
936
+ console.log();
937
+ checkbox('Run `codeyam editor audit` to verify all components have scenarios and all functions have tests');
938
+ console.log();
939
+ console.log(chalk.bold('Build import graph (for change tracking):'));
940
+ checkbox('Run `codeyam editor analyze-imports`');
941
+ console.log(chalk.dim(' This populates the import graph so the system can detect impacted entities when code changes.'));
942
+ console.log(chalk.dim(' It must run AFTER the audit passes so the glossary and entity data are complete.'));
593
943
  stopGate(7);
594
944
  }
595
945
  // ─── Step 8: App Scenarios ────────────────────────────────────────────
596
946
  function printStep8(root, feature) {
597
947
  const port = getServerPort();
598
948
  const prevState = readState(root);
949
+ const isResuming = prevState?.step === 8;
950
+ const now = new Date().toISOString();
599
951
  writeState(root, {
600
952
  feature,
601
953
  step: 8,
602
954
  label: STEP_LABELS[8],
603
- startedAt: new Date().toISOString(),
604
- featureStartedAt: prevState?.featureStartedAt || new Date().toISOString(),
955
+ startedAt: isResuming ? prevState.startedAt : now,
956
+ featureStartedAt: prevState?.featureStartedAt || now,
605
957
  });
606
958
  logEvent(root, 'step', { step: 8, label: 'App Scenarios', feature });
607
959
  stepHeader(8, 'App Scenarios', feature);
960
+ if (isResuming) {
961
+ printResumptionHeader(8);
962
+ }
608
963
  console.log('Create app-level scenarios representing different data states.');
609
964
  console.log();
610
965
  console.log(chalk.bold('Checklist:'));
611
- checkbox('Create scenarios for key data states (empty, loaded, error)');
612
- console.log(chalk.dim(' Each scenario provides data across ALL API routes for that state'));
613
- console.log(chalk.dim(' Name app scenarios with "App - " prefix: "App - Empty", "App - Full Catalog", "App - API Error"'));
614
- checkbox('Register each scenario (auto-captures screenshot):');
615
- console.log(chalk.dim(` codeyam editor register '{"name":"App - Empty","description":"...","mockData":{"routes":{"/api/...":{"body":[...]}}}}'`));
966
+ checkbox('Check existing scenarios: `codeyam editor scenarios`');
967
+ console.log(chalk.dim(' Review existing scenarios reuse and update their data rather than'));
968
+ console.log(chalk.dim(' creating duplicates. Re-register with the same name to update a scenario.'));
969
+ checkbox('Ensure scenarios clearly demonstrate what changed in this session');
970
+ console.log(chalk.dim(' If data models changed: update existing scenarios seed data to match'));
971
+ console.log(chalk.dim(' If UI changed: re-register existing scenarios so screenshots reflect the update'));
972
+ console.log(chalk.dim(' Add new scenarios only for genuinely new data states not covered by existing ones'));
973
+ checkbox('Cover key data states for EVERY page/route (at least 2-3 scenarios per page)');
974
+ console.log(chalk.yellow(' Each page needs its own scenarios — a detail page needs different data than the catalog'));
975
+ console.log(chalk.dim(' Catalog: "Full Catalog", "Empty Catalog", "Single Category"'));
976
+ console.log(chalk.dim(' Detail: "Detail - With Reviews", "Detail - No Reviews", "Detail - High Rated"'));
977
+ console.log(chalk.dim(' Each scenario provides seed data to populate the database for that state'));
978
+ checkbox('If the project has a database + seed adapter, use seed-based scenarios:');
979
+ console.log(chalk.dim(` codeyam editor register '{"name":"Full Catalog","type":"application","url":"/","seed":{"products":[...],"categories":[...]}}'`));
980
+ console.log(chalk.dim(' Seed data is written to the DB via the seed adapter. Real app renders real data.'));
981
+ checkbox('Optional: add "viewportWidth" and "viewportHeight" to override the project default screen size');
982
+ console.log(chalk.dim(' If omitted, captures use the project default from setup. Only override for scenarios that need a different size.'));
983
+ checkbox('IMPORTANT: Always include "url" — the page path to screenshot for this scenario');
984
+ console.log(chalk.dim(' Use "/" for home page, "/drinks/1" for detail pages, etc.'));
985
+ console.log(chalk.dim(' Without url, the screenshot captures the root page regardless of the scenario.'));
986
+ console.log(chalk.yellow(' Create separate scenarios for each page that shows different data.'));
987
+ checkbox('For large seed data, write JSON to a project temp file and use @ prefix:');
988
+ console.log(chalk.dim(' Write JSON to .codeyam/tmp/scenario.json then register with:'));
989
+ console.log(chalk.dim(' codeyam editor register @.codeyam/tmp/scenario.json'));
990
+ checkbox('For external API mocks (Stripe, weather, etc.), add externalApis:');
991
+ console.log(chalk.dim(` "externalApis":{"GET https://api.stripe.com/v1/prices":{"body":[...],"status":200}}`));
992
+ checkbox('If the app uses auth, email, or other patterns: see FEATURE_PATTERNS.md for scenario guidance');
993
+ checkbox('If no database, use component-style mock scenarios (unchanged):');
994
+ console.log(chalk.dim(` codeyam editor register '{"name":"Empty","description":"...","url":"/","mockData":{"routes":{"/api/...":{"body":[...]}}}}'`));
616
995
  checkbox('After each registration, check the response for `clientErrors`');
617
996
  console.log(chalk.yellow(' If clientErrors is non-empty → fix the issue and re-register the scenario'));
618
997
  console.log(chalk.yellow(' Fix client errors and re-register before moving on'));
@@ -624,21 +1003,34 @@ function printStep8(root, feature) {
624
1003
  function printStep9(root, feature) {
625
1004
  const port = getServerPort();
626
1005
  const prevState = readState(root);
1006
+ const isResuming = prevState?.step === 9;
1007
+ const now = new Date().toISOString();
627
1008
  writeState(root, {
628
1009
  feature,
629
1010
  step: 9,
630
1011
  label: STEP_LABELS[9],
631
- startedAt: new Date().toISOString(),
632
- featureStartedAt: prevState?.featureStartedAt || new Date().toISOString(),
1012
+ startedAt: isResuming ? prevState.startedAt : now,
1013
+ featureStartedAt: prevState?.featureStartedAt || now,
633
1014
  });
634
1015
  logEvent(root, 'step', { step: 9, label: 'User Scenarios', feature });
635
1016
  stepHeader(9, 'User Scenarios', feature);
1017
+ if (isResuming) {
1018
+ printResumptionHeader(9);
1019
+ }
636
1020
  console.log('Create per-persona scenarios if the app has users. Skip to step 10 if no users.');
637
1021
  console.log();
638
1022
  console.log(chalk.bold('If the app has users:'));
639
- checkbox('Create scenarios for each user persona (admin, regular user, new user)');
640
- console.log(chalk.dim(' Each persona scenario provides data across ALL API routes for that user type'));
641
- checkbox('Register each persona scenario (auto-captures screenshot):');
1023
+ checkbox('Check existing scenarios: `codeyam editor scenarios`');
1024
+ console.log(chalk.dim(' Reuse existing persona scenarios update data to reflect current changes.'));
1025
+ console.log(chalk.dim(' Re-register with the same name to update. Only add new personas if needed.'));
1026
+ checkbox('Ensure scenarios for each user persona (admin, regular user, new user)');
1027
+ console.log(chalk.dim(' Each persona scenario layers user-specific seed data on top of an app scenario'));
1028
+ checkbox('If using seed-based scenarios, register with type "user" and baseScenario:');
1029
+ console.log(chalk.dim(` codeyam editor register '{"name":"Admin User","type":"user","url":"/","baseScenario":"<app-scenario-id>","seed":{"users":[{"id":1,"role":"admin"}]}}'`));
1030
+ console.log(chalk.dim(' Always include "url" — the page to screenshot (e.g. "/", "/dashboard", "/drinks/1")'));
1031
+ console.log(chalk.dim(" The base scenario's seed data is merged with this scenario's seed data (overlay wins)."));
1032
+ checkbox('If the app uses auth or other patterns: see FEATURE_PATTERNS.md for per-persona scenario guidance');
1033
+ checkbox('If using mock-based scenarios, register with mockData as before:');
642
1034
  console.log(chalk.dim(` codeyam editor register '{"name":"...","description":"...","mockData":{"routes":{"/api/...":{"body":[...]}}}}'`));
643
1035
  checkbox('After each registration, check the response for `clientErrors`');
644
1036
  console.log(chalk.yellow(' If clientErrors is non-empty → fix the issue and re-register the scenario'));
@@ -654,15 +1046,20 @@ function printStep9(root, feature) {
654
1046
  function printStep10(root, feature) {
655
1047
  const port = getServerPort();
656
1048
  const prevState = readState(root);
1049
+ const isResuming = prevState?.step === 10;
1050
+ const now = new Date().toISOString();
657
1051
  writeState(root, {
658
1052
  feature,
659
1053
  step: 10,
660
1054
  label: STEP_LABELS[10],
661
- startedAt: new Date().toISOString(),
662
- featureStartedAt: prevState?.featureStartedAt || new Date().toISOString(),
1055
+ startedAt: isResuming ? prevState.startedAt : now,
1056
+ featureStartedAt: prevState?.featureStartedAt || now,
663
1057
  });
664
1058
  logEvent(root, 'step', { step: 10, label: 'Verify', feature });
665
1059
  stepHeader(10, 'Verify', feature);
1060
+ if (isResuming) {
1061
+ printResumptionHeader(10);
1062
+ }
666
1063
  console.log('Verify component isolation screenshots, editor scenarios, and library tests.');
667
1064
  console.log();
668
1065
  console.log(chalk.bold('Component isolation screenshots — verify:'));
@@ -672,13 +1069,15 @@ function printStep10(root, feature) {
672
1069
  console.log(chalk.dim(` codeyam editor register '{"name":"ComponentName - Scenario",...}'`));
673
1070
  console.log();
674
1071
  console.log(chalk.bold('Editor scenarios (App tab) — visual + error check:'));
675
- checkbox(`Refresh the preview: \`curl -s -X POST http://localhost:${port}/api/editor-refresh\``);
1072
+ checkbox(`Refresh the preview: \`codeyam editor preview\``);
676
1073
  checkbox('Click through each app-level and user-persona scenario in the preview');
677
- checkbox(`Check for client-side errors: \`curl -s http://localhost:${port}/api/editor-client-errors\``);
1074
+ checkbox('For seed-based scenarios: verify data renders correctly after switching');
1075
+ console.log(chalk.dim(' Switch between scenarios and confirm the app reflects the seeded data each time'));
1076
+ checkbox(`Check for client-side errors: \`codeyam editor client-errors\``);
678
1077
  console.log(chalk.yellow(' If `hasErrors` is true: list each scenario with errors, fix the source code,'));
679
1078
  console.log(chalk.yellow(' re-register the affected scenarios, and re-check until hasErrors is false'));
680
1079
  console.log(chalk.dim(' Common errors: React errors, failed fetches, undefined references, hydration mismatches'));
681
- checkbox('Verify no broken images in any scenario screenshots');
1080
+ checkbox('Verify no broken images: `codeyam editor verify-images \'{"paths":["/"], "imageUrls":["url1"]}\'` with all page paths and image URLs');
682
1081
  console.log();
683
1082
  console.log(chalk.bold('Library functions — test check:'));
684
1083
  checkbox('Re-run all test files from step 5 to confirm they still pass');
@@ -688,99 +1087,131 @@ function printStep10(root, feature) {
688
1087
  console.log(chalk.dim('Focus on fixing issues. All component screenshots, scenarios, and tests must be clean before proceeding.'));
689
1088
  stopGate(10);
690
1089
  }
691
- // ─── Step 11: Review ──────────────────────────────────────────────────
1090
+ // ─── Step 11: Journal ─────────────────────────────────────────────────
692
1091
  function printStep11(root, feature) {
693
1092
  const port = getServerPort();
694
1093
  const prevState = readState(root);
1094
+ const isResuming = prevState?.step === 11;
1095
+ const now = new Date().toISOString();
695
1096
  writeState(root, {
696
1097
  feature,
697
1098
  step: 11,
698
1099
  label: STEP_LABELS[11],
699
- startedAt: new Date().toISOString(),
700
- featureStartedAt: prevState?.featureStartedAt || new Date().toISOString(),
1100
+ startedAt: isResuming ? prevState.startedAt : now,
1101
+ featureStartedAt: prevState?.featureStartedAt || now,
701
1102
  });
702
- logEvent(root, 'step', { step: 11, label: 'Review', feature });
703
- stepHeader(11, 'Review', feature);
704
- console.log('Present a full summary, verify screenshots, then let the user save or revise.');
705
- console.log();
706
- console.log(chalk.bold('Phase 1 — Summary:'));
707
- checkbox('Summarize the feature that was built');
708
- checkbox('List all components and functions created');
709
- checkbox('List glossary entries added in step 6');
710
- checkbox('List all scenarios (app-level and user-persona)');
711
- checkbox('Report analysis status (completed/in-progress from step 7)');
712
- checkbox('Report verification status from step 10 (all clean?)');
713
- checkbox(`Refresh the preview: \`curl -s -X POST http://localhost:${port}/api/dev-mode-refresh\``);
714
- checkbox(`Get scenario list: \`curl -s http://localhost:${port}/api/editor-scenarios\``);
715
- checkbox('Present scenarios in a markdown table — one scenario per row');
716
- console.log(chalk.yellow(' EVERY scenario must use {{scenario:Name:ID}} — the terminal makes these clickable.'));
717
- console.log(chalk.yellow(' Strip the component prefix: "DrinkCard - Default" → just "Default".'));
718
- console.log(chalk.yellow(' Component name on first row of each group, blank for subsequent rows.'));
719
- console.log();
720
- console.log(chalk.dim(' Example:'));
721
- console.log(chalk.dim(' | Component | Scenario |'));
722
- console.log(chalk.dim(' |---|---|'));
723
- console.log(chalk.dim(' | **App** | {{scenario:Full Catalog:abc}} |'));
724
- console.log(chalk.dim(' | | {{scenario:Empty:def}} |'));
725
- console.log(chalk.dim(' | | {{scenario:Error:ghi}} |'));
726
- console.log(chalk.dim(' | **StarRating** | {{scenario:Perfect:jkl}} |'));
727
- console.log(chalk.dim(' | | {{scenario:Medium:mno}} |'));
728
- console.log();
729
- console.log(chalk.bold('Phase 2 — Screenshot & error verification:'));
730
- console.log(chalk.yellow(' CRITICAL: Verify screenshots AND check for client-side errors.'));
731
- console.log();
732
- console.log(chalk.bold(' Component isolation screenshots:'));
733
- checkbox('Verify each component has screenshots in the App tab (grouped under Components)');
734
- checkbox('If any are missing, re-register them using `codeyam editor register`');
735
- console.log();
736
- console.log(chalk.bold(' Client-side errors:'));
737
- checkbox(`Check for errors: \`curl -s http://localhost:${port}/api/editor-client-errors\``);
738
- checkbox('If `hasErrors` is true, list the errors and fix them in the source code');
739
- checkbox('After fixing, re-capture affected scenarios to clear the errors');
740
- console.log();
741
- checkbox('Do not proceed until all components have screenshots and client errors are resolved');
742
- console.log();
743
- checkbox('Switch the active scenario to the best demo state for the feature');
744
- console.log(chalk.dim(` curl -s -X POST http://localhost:${port}/api/editor-switch-scenario \\`));
745
- console.log(chalk.dim(` -H 'Content-Type: application/json' -d '{"scenarioSlug":"...","scenarioId":"..."}'`));
746
- console.log(chalk.dim(' Pick the scenario that best showcases the feature (not an error/empty state)'));
747
- checkbox('Present the summary (with screenshot and error status) to the user');
1103
+ logEvent(root, 'step', { step: 11, label: 'Journal', feature });
1104
+ stepHeader(11, 'Journal', feature);
1105
+ if (isResuming) {
1106
+ printResumptionHeader(11);
1107
+ }
1108
+ console.log('Create or update the journal entry for this feature.');
748
1109
  console.log();
749
- console.log(chalk.bold('Phase 3 — Journal entry (create or update):'));
1110
+ console.log(chalk.bold('Checklist:'));
750
1111
  checkbox('Write a concise description of what was built (2-3 sentences)');
751
1112
  checkbox(`First time at step 11 — create journal entry with ALL session screenshots:`);
752
- console.log(chalk.dim(` curl -s -X POST http://localhost:${port}/api/editor-journal-entry \\`));
753
- console.log(chalk.dim(` -H 'Content-Type: application/json' \\`));
754
- console.log(chalk.dim(` -d '{"title":"...","type":"feature","description":"...","includeSessionScenarios":true}'`));
1113
+ console.log(chalk.dim(` codeyam editor journal '{"title":"...","type":"feature","description":"...","includeSessionScenarios":true}'`));
755
1114
  console.log(chalk.dim(' includeSessionScenarios auto-discovers component + app + user persona screenshots'));
756
- checkbox(`Returning after changes — update the existing journal entry:`);
757
- console.log(chalk.dim(` curl -s -X PATCH http://localhost:${port}/api/editor-journal-update \\`));
758
- console.log(chalk.dim(` -H 'Content-Type: application/json' \\`));
759
- console.log(chalk.dim(` -d '{"time":"<journal entry time>","description":"<updated description>"}'`));
1115
+ checkbox(`Returning to step 11 (before commit) — update the existing journal entry:`);
1116
+ console.log(chalk.dim(` codeyam editor journal-update '{"time":"<journal entry time>","description":"<updated>","includeSessionScenarios":true}'`));
1117
+ console.log(chalk.dim(' Note: PATCH only works before the entry is committed. After commit, use POST to create a new entry.'));
1118
+ console.log();
1119
+ console.log(chalk.dim('Focus on creating or updating the journal entry. Summary and presentation happen in step 13.'));
1120
+ stopGate(11);
1121
+ }
1122
+ // ─── Step 12: Review ──────────────────────────────────────────────────
1123
+ function printStep12(root, feature) {
1124
+ const port = getServerPort();
1125
+ const prevState = readState(root);
1126
+ const isResuming = prevState?.step === 12;
1127
+ const now = new Date().toISOString();
1128
+ writeState(root, {
1129
+ feature,
1130
+ step: 12,
1131
+ label: STEP_LABELS[12],
1132
+ startedAt: isResuming ? prevState.startedAt : now,
1133
+ featureStartedAt: prevState?.featureStartedAt || now,
1134
+ });
1135
+ logEvent(root, 'step', { step: 12, label: 'Review', feature });
1136
+ stepHeader(12, 'Review', feature);
1137
+ if (isResuming) {
1138
+ printResumptionHeader(12);
1139
+ }
1140
+ console.log('Verify all screenshots and checks pass before presenting to the user.');
1141
+ console.log();
1142
+ console.log(chalk.bold('Checklist (do all of this silently):'));
1143
+ checkbox(`Refresh the preview: \`codeyam editor preview\``);
1144
+ checkbox('Verify each component has screenshots in the App tab (grouped under Components)');
1145
+ checkbox('If any are missing, re-register them using `codeyam editor register`');
1146
+ checkbox(`Check for client errors: \`codeyam editor client-errors\``);
1147
+ checkbox('If `hasErrors` is true, fix them and re-capture affected scenarios');
1148
+ checkbox('Run `codeyam editor verify-images \'{"paths":["/"], "imageUrls":["url1"]}\'` with all page paths and image URLs');
1149
+ checkbox('Fix or remove any broken images before continuing');
1150
+ checkbox('Run `codeyam editor audit` to verify completeness of scenarios and tests');
1151
+ checkbox('Do not proceed until all checks pass');
1152
+ stopGate(12);
1153
+ }
1154
+ // ─── Step 13: Present ─────────────────────────────────────────────────
1155
+ function printStep13(root, feature) {
1156
+ const port = getServerPort();
1157
+ const prevState = readState(root);
1158
+ const isResuming = prevState?.step === 13;
1159
+ const now = new Date().toISOString();
1160
+ writeState(root, {
1161
+ feature,
1162
+ step: 13,
1163
+ label: STEP_LABELS[13],
1164
+ startedAt: isResuming ? prevState.startedAt : now,
1165
+ featureStartedAt: prevState?.featureStartedAt || now,
1166
+ });
1167
+ logEvent(root, 'step', { step: 13, label: 'Present', feature });
1168
+ stepHeader(13, 'Present', feature);
1169
+ if (isResuming) {
1170
+ printResumptionHeader(13);
1171
+ }
1172
+ console.log('Present the results to the user and get their approval.');
760
1173
  console.log();
761
- console.log(chalk.bold('Phase 4 — Present a selection menu to the user (use AskUserQuestion):'));
762
- console.log(chalk.green(' "Save & commit"') +
1174
+ console.log(chalk.bold('Checklist:'));
1175
+ checkbox('Fetch all scenarios: `codeyam editor scenarios`');
1176
+ console.log(chalk.dim(' Each scenario has a `changeStatus` field: "new", "edited", "impacted", or null.'));
1177
+ checkbox('Pick the best scenario to showcase from this working session:');
1178
+ console.log(chalk.dim(' Prefer scenarios with changeStatus "new" or "edited" (directly changed in this session).'));
1179
+ console.log(chalk.dim(' For app-level scenarios, prefer those showing the new/changed functionality.'));
1180
+ console.log(chalk.dim(' NEVER pick a scenario with changeStatus null — those are unchanged from a previous commit.'));
1181
+ checkbox('Switch the preview to that scenario using its `id`:');
1182
+ console.log(chalk.dim(` codeyam editor preview '{"scenarioId":"<id>"}'`));
1183
+ checkbox(`Show the results panel: \`codeyam editor show-results\``);
1184
+ console.log(chalk.dim(' This opens a visual panel below the terminal showing all scenarios with screenshots.'));
1185
+ console.log(chalk.dim(' The user can click scenarios to switch the live preview.'));
1186
+ checkbox('Write a 1-2 sentence summary of what was built');
1187
+ checkbox('Report test count and audit status (one line)');
1188
+ console.log();
1189
+ console.log(chalk.bold('Present a selection menu to the user (use AskUserQuestion with these EXACT option labels):'));
1190
+ console.log(chalk.green(' Option 1 label: "Save & commit"') +
763
1191
  chalk.dim(' — git commit all changes and record in journal'));
764
- console.log(chalk.yellow(' "I\'d like to make some changes"') +
1192
+ console.log(chalk.yellow(' Option 2 label: "I\'d like to make some changes"') +
765
1193
  chalk.dim(' — describe changes, then re-verify'));
766
1194
  console.log();
767
1195
  console.log(chalk.bold('If the user chooses "Save & commit":'));
768
- checkbox(`Git commit using the journal description: \`curl -s -X POST http://localhost:${port}/api/editor-commit -H 'Content-Type: application/json' -d '{"message":"feat: <title>\\n\\n<journal description>"}'\``);
1196
+ checkbox(`Hide the results panel first: \`codeyam editor hide-results\``);
1197
+ checkbox(`Git commit using the journal description: \`codeyam editor commit '{"message":"feat: <title>\\n\\n<journal description>"}'\``);
769
1198
  console.log(chalk.dim(' The commit message body MUST match the journal description exactly'));
770
- checkbox(`Update journal with commit SHA: \`curl -s -X PATCH http://localhost:${port}/api/editor-journal-update -H 'Content-Type: application/json' -d '{"time":"<journal entry time>","commitSha":"<sha>","commitMessage":"feat: <title>"}'\``);
1199
+ checkbox(`Update journal with commit SHA: \`codeyam editor journal-update '{"time":"<journal entry time>","commitSha":"<sha>","commitMessage":"feat: <title>"}'\``);
771
1200
  console.log(chalk.green(' Then run: ') +
772
- chalk.bold('codeyam editor 1') +
1201
+ chalk.bold('codeyam editor steps') +
773
1202
  chalk.green(' to start the next feature'));
774
1203
  console.log();
775
- console.log(chalk.bold('If the user chooses "Make changes":'));
776
- checkbox('Ask what changes the user wants');
777
- checkbox('Make the requested changes');
778
- console.log(chalk.yellow(' Then run: ') +
779
- chalk.bold('codeyam editor 10') +
780
- chalk.yellow(' to re-verify, which flows back to step 11'));
781
- console.log();
782
- console.log(chalk.dim('Complete all phases in order: summary, screenshot verification, journal entry, user menu.'));
783
- stopGate(11, { confirm: true });
1204
+ console.log(chalk.red.bold(' If the user reports a bug or requests a fix after committing:'));
1205
+ console.log(chalk.red.bold(' You MUST still run `codeyam editor change` before making any changes.'));
1206
+ console.log(chalk.red.bold(' The change workflow applies to ALL changes — post-commit fixes are not an exception.'));
1207
+ console.log();
1208
+ console.log(chalk.bold('If the user chooses "Make changes" (or asks for ANY change, even as a question):'));
1209
+ checkbox(`Hide the results panel: \`codeyam editor hide-results\``);
1210
+ checkbox('Ask what changes the user wants (if not already clear)');
1211
+ checkbox(`Run: \`codeyam editor change "${feature}"\` this gives you the change checklist`);
1212
+ checkbox('THEN make the requested changes and follow the checklist');
1213
+ console.log(chalk.red.bold(' IMPORTANT: Always run the change command BEFORE writing any code.'));
1214
+ stopGate(13, { confirm: true });
784
1215
  }
785
1216
  // ─── Command definition ───────────────────────────────────────────────
786
1217
  // ─── Analyze-imports subcommand ────────────────────────────────────────
@@ -837,8 +1268,9 @@ async function handleAnalyzeImports() {
837
1268
  await initializeEnvironment();
838
1269
  const entities = await loadEntities({});
839
1270
  if (!entities || entities.length === 0) {
840
- progress.fail('No entities found in database');
841
- process.exit(1);
1271
+ progress.succeed('No entities found in database yet — skipping import analysis. This is normal on the first feature.');
1272
+ console.log(JSON.stringify({ imports: {}, entities: {} }));
1273
+ return;
842
1274
  }
843
1275
  // Deduplicate to latest versions
844
1276
  const latestByKey = new Map();
@@ -894,7 +1326,161 @@ async function handleAnalyzeImports() {
894
1326
  const output = { imports, entities: entityData };
895
1327
  console.log(JSON.stringify(output, null, 2));
896
1328
  }
1329
+ // ─── Validate-seed subcommand ─────────────────────────────────────────
1330
+ /**
1331
+ * Validate seed data structure and check for a seed adapter.
1332
+ * Usage: codeyam editor validate-seed '{"products":[...]}'
1333
+ */
1334
+ function handleValidateSeed(jsonArg) {
1335
+ const root = process.cwd();
1336
+ // Check for seed adapter
1337
+ const adapterPath = detectSeedAdapter(root);
1338
+ if (adapterPath) {
1339
+ console.log(chalk.green(`Seed adapter found: ${adapterPath}`));
1340
+ }
1341
+ else {
1342
+ console.log(chalk.yellow('No seed adapter found. Create .codeyam/seed-adapter.ts to use seed-based scenarios.'));
1343
+ }
1344
+ if (!jsonArg) {
1345
+ return;
1346
+ }
1347
+ let data;
1348
+ try {
1349
+ data = JSON.parse(jsonArg);
1350
+ }
1351
+ catch {
1352
+ console.error(chalk.red('Error: Invalid JSON.'));
1353
+ process.exit(1);
1354
+ }
1355
+ const result = validateSeedData(data);
1356
+ if (result.valid) {
1357
+ const tables = Object.keys(data);
1358
+ const totalRows = tables.reduce((sum, t) => sum + (Array.isArray(data[t]) ? data[t].length : 0), 0);
1359
+ console.log(chalk.green(`Seed data valid: ${tables.length} table(s), ${totalRows} total row(s)`));
1360
+ for (const table of tables) {
1361
+ const rows = Array.isArray(data[table])
1362
+ ? data[table].length
1363
+ : 0;
1364
+ console.log(chalk.dim(` ${table}: ${rows} row(s)`));
1365
+ }
1366
+ }
1367
+ else {
1368
+ console.error(chalk.red('Seed data validation failed:'));
1369
+ for (const err of result.errors) {
1370
+ console.error(chalk.red(` - ${err}`));
1371
+ }
1372
+ process.exit(1);
1373
+ }
1374
+ }
1375
+ // ─── Isolate subcommand ───────────────────────────────────────────────
1376
+ /**
1377
+ * `codeyam editor isolate StarRating CategoryBadge DrinkCard`
1378
+ *
1379
+ * Creates isolation route directories and the layout guard file.
1380
+ * This avoids brace-expansion permission prompts in the embedded terminal.
1381
+ */
1382
+ function handleIsolate(componentNames) {
1383
+ const root = process.cwd();
1384
+ if (componentNames.length === 0) {
1385
+ console.error(chalk.red('Usage: codeyam editor isolate ComponentA ComponentB ...'));
1386
+ process.exit(1);
1387
+ }
1388
+ const isolateDir = path.join(root, 'app', 'codeyam-isolate');
1389
+ // Create layout.tsx with production guard if missing
1390
+ const layoutPath = path.join(isolateDir, 'layout.tsx');
1391
+ if (!fs.existsSync(layoutPath)) {
1392
+ fs.mkdirSync(isolateDir, { recursive: true });
1393
+ fs.writeFileSync(layoutPath, [
1394
+ 'import { notFound } from "next/navigation";',
1395
+ '',
1396
+ 'export default function CaptureLayout({ children }: { children: React.ReactNode }) {',
1397
+ ' if (process.env.NODE_ENV === "production") notFound();',
1398
+ ' return <>{children}</>;',
1399
+ '}',
1400
+ '',
1401
+ ].join('\n'), 'utf8');
1402
+ console.log(chalk.green(`Created layout guard: app/codeyam-isolate/layout.tsx`));
1403
+ }
1404
+ // Create a directory for each component
1405
+ const created = [];
1406
+ const existed = [];
1407
+ for (const name of componentNames) {
1408
+ const dir = path.join(isolateDir, name);
1409
+ if (fs.existsSync(dir)) {
1410
+ existed.push(name);
1411
+ }
1412
+ else {
1413
+ fs.mkdirSync(dir, { recursive: true });
1414
+ created.push(name);
1415
+ }
1416
+ }
1417
+ if (created.length > 0) {
1418
+ console.log(chalk.green(`Created ${created.length} isolation route dir(s): ${created.join(', ')}`));
1419
+ }
1420
+ if (existed.length > 0) {
1421
+ console.log(chalk.dim(`Already existed: ${existed.join(', ')}`));
1422
+ }
1423
+ }
897
1424
  // ─── Register subcommand ──────────────────────────────────────────────
1425
+ /**
1426
+ * Format API subcommand results as concise one-line summaries.
1427
+ * Prevents Claude from piping output to python3 just to extract key fields.
1428
+ * Returns null for subcommands that should keep raw JSON output.
1429
+ */
1430
+ function formatApiSubcommandResult(subcommand, data) {
1431
+ if (!data || typeof data !== 'object')
1432
+ return null;
1433
+ switch (subcommand) {
1434
+ case 'journal': {
1435
+ const parts = [`success=${data.success}`];
1436
+ if (data.entry?.time)
1437
+ parts.push(`time="${data.entry.time}"`);
1438
+ if (data.entry?.title)
1439
+ parts.push(`title="${data.entry.title}"`);
1440
+ if (data.scenarioScreenshotsFound != null) {
1441
+ parts.push(`screenshots=${data.scenarioScreenshotsFound}`);
1442
+ }
1443
+ return parts.join(' ');
1444
+ }
1445
+ case 'journal-update': {
1446
+ const parts = [`success=${data.success}`];
1447
+ if (data.entry?.time)
1448
+ parts.push(`time="${data.entry.time}"`);
1449
+ if (data.scenarioScreenshotsFound != null) {
1450
+ parts.push(`screenshots=${data.scenarioScreenshotsFound}`);
1451
+ }
1452
+ return parts.join(' ');
1453
+ }
1454
+ case 'commit': {
1455
+ const parts = [`success=${data.success}`];
1456
+ if (data.sha)
1457
+ parts.push(`sha=${data.sha}`);
1458
+ if (data.message)
1459
+ parts.push(`message="${data.message.split('\n')[0]}"`);
1460
+ return parts.join(' ');
1461
+ }
1462
+ case 'preview': {
1463
+ const parts = [
1464
+ `success=${!!data.ok || data.success !== false}`,
1465
+ ];
1466
+ if (data.path)
1467
+ parts.push(`path="${data.path}"`);
1468
+ if (data.scenarioId)
1469
+ parts.push(`scenarioId="${data.scenarioId}"`);
1470
+ if (data.sessionsNotified != null)
1471
+ parts.push(`notified=${data.sessionsNotified}`);
1472
+ return parts.join(' ');
1473
+ }
1474
+ case 'dev-server': {
1475
+ if (data.status) {
1476
+ return `status=${data.status} pid=${data.pid || 'none'} port=${data.port || 'none'}`;
1477
+ }
1478
+ return null;
1479
+ }
1480
+ default:
1481
+ return null; // journal-list, client-errors, show/hide-results: keep full JSON
1482
+ }
1483
+ }
898
1484
  /**
899
1485
  * `codeyam editor register '{"name":"...","componentName":"...",...}'`
900
1486
  *
@@ -904,18 +1490,19 @@ async function handleAnalyzeImports() {
904
1490
  async function handleRegister(jsonArg) {
905
1491
  if (!jsonArg) {
906
1492
  console.error(chalk.red('Error: JSON argument required.'));
907
- console.error(chalk.dim(' Usage: codeyam editor register \'{"name":"DrinkCard - Default","componentName":"DrinkCard","url":"/codeyam-isolate/DrinkCard?s=Default"}\''));
1493
+ console.error(chalk.dim(' Usage: codeyam editor register \'{"name":"DrinkCard - Default","componentName":"DrinkCard","url":"/isolated-components/DrinkCard?s=Default"}\''));
1494
+ console.error(chalk.dim(' For large payloads: codeyam editor register @/tmp/scenario.json'));
908
1495
  process.exit(1);
909
1496
  }
910
- let body;
911
- try {
912
- body = JSON.parse(jsonArg);
913
- }
914
- catch {
915
- console.error(chalk.red('Error: Invalid JSON.'));
916
- console.error(chalk.dim(` Received: ${jsonArg.slice(0, 200)}`));
1497
+ const parsed = parseRegisterArg(jsonArg);
1498
+ if (parsed.error) {
1499
+ console.error(chalk.red(`Error: ${parsed.error}`));
1500
+ if (parsed.error === 'Invalid JSON') {
1501
+ console.error(chalk.dim(` Received: ${jsonArg.slice(0, 200)}`));
1502
+ }
917
1503
  process.exit(1);
918
1504
  }
1505
+ const body = parsed.body;
919
1506
  const port = getServerPort();
920
1507
  const url = `http://localhost:${port}/api/editor-register-scenario`;
921
1508
  try {
@@ -925,8 +1512,34 @@ async function handleRegister(jsonArg) {
925
1512
  body: JSON.stringify(body),
926
1513
  });
927
1514
  const data = await res.json();
928
- console.log(JSON.stringify(data, null, 2));
1515
+ // Print concise summary instead of raw JSON so Claude doesn't need python3
1516
+ const parts = [];
1517
+ parts.push(`success=${data.success}`);
1518
+ if (data.scenario?.name)
1519
+ parts.push(`name="${data.scenario.name}"`);
1520
+ parts.push(`screenshot=${!!data.screenshotCaptured}`);
1521
+ if (data.clientErrors?.length > 0) {
1522
+ parts.push(chalk.red(`errors=${data.clientErrors.length}`));
1523
+ }
1524
+ else {
1525
+ parts.push(`errors=0`);
1526
+ }
1527
+ if (data.seedResult)
1528
+ parts.push(`seed=${data.seedResult.success}`);
1529
+ if (data.captureError)
1530
+ parts.push(chalk.yellow(`captureError="${data.captureError}"`));
1531
+ console.log(parts.join(' '));
1532
+ // Surface client errors prominently so they can't be missed
1533
+ if (data.clientErrors && data.clientErrors.length > 0) {
1534
+ console.log(chalk.red.bold(`⚠ WARNING: ${data.clientErrors.length} client error${data.clientErrors.length !== 1 ? 's' : ''} detected during capture:`));
1535
+ for (const err of data.clientErrors) {
1536
+ console.log(chalk.red(` → ${err}`));
1537
+ }
1538
+ console.log(chalk.yellow(' The screenshot may show an error screen instead of the actual component.'));
1539
+ console.log(chalk.yellow(' Fix the issue and re-register this scenario.'));
1540
+ }
929
1541
  if (!res.ok) {
1542
+ console.error(chalk.dim(JSON.stringify(data, null, 2)));
930
1543
  process.exit(1);
931
1544
  }
932
1545
  }
@@ -938,15 +1551,775 @@ async function handleRegister(jsonArg) {
938
1551
  process.exit(1);
939
1552
  }
940
1553
  }
1554
+ // ─── Dependents subcommand ────────────────────────────────────────────
1555
+ /**
1556
+ * `codeyam editor dependents <EntityName>`
1557
+ *
1558
+ * Walks the importedBy reverse dependency tree to find all ancestors of a
1559
+ * given entity — i.e., every component or page that transitively imports it.
1560
+ * Used after code changes to determine what needs recapturing.
1561
+ */
1562
+ async function handleDependents(entityName) {
1563
+ if (!entityName) {
1564
+ console.error(chalk.red('Error: Entity name required.'));
1565
+ console.error(chalk.dim(' Usage: codeyam editor dependents DrinkCard'));
1566
+ process.exit(1);
1567
+ }
1568
+ const progress = new ProgressReporter();
1569
+ progress.start('Loading entities from database...');
1570
+ await initializeEnvironment();
1571
+ const allEntities = await loadEntities({});
1572
+ if (!allEntities || allEntities.length === 0) {
1573
+ progress.fail('No entities found in database');
1574
+ console.error(chalk.dim(' Run codeyam editor analyze-imports first to populate entity data.'));
1575
+ process.exit(1);
1576
+ }
1577
+ // Find the target entity by name (case-insensitive match)
1578
+ const targetEntities = allEntities.filter((e) => e.name.toLowerCase() === entityName.toLowerCase());
1579
+ if (targetEntities.length === 0) {
1580
+ progress.fail(`Entity "${entityName}" not found in database`);
1581
+ const names = [...new Set(allEntities.map((e) => e.name))].sort();
1582
+ console.error(chalk.dim(` Available entities: ${names.join(', ')}`));
1583
+ process.exit(1);
1584
+ }
1585
+ progress.succeed('Entities loaded');
1586
+ // Build lookup: entityName -> Set<entityName> of entities that import it.
1587
+ // Each entity's metadata.importedBy is the pre-computed reverse graph:
1588
+ // { [filePath]: { [importerName]: { shas: string[] } } }
1589
+ const importedByName = new Map();
1590
+ for (const entity of allEntities) {
1591
+ const importedBy = entity.metadata?.importedBy;
1592
+ if (!importedBy || typeof importedBy !== 'object')
1593
+ continue;
1594
+ const importers = new Set();
1595
+ for (const filePath of Object.keys(importedBy)) {
1596
+ for (const importerName of Object.keys(importedBy[filePath])) {
1597
+ importers.add(importerName);
1598
+ }
1599
+ }
1600
+ if (importers.size > 0) {
1601
+ importedByName.set(entity.name, importers);
1602
+ }
1603
+ }
1604
+ // BFS walk up the dependency tree from the changed entity
1605
+ const visited = new Set();
1606
+ const result = [];
1607
+ const queue = [
1608
+ { name: entityName, depth: 0 },
1609
+ ];
1610
+ visited.add(entityName.toLowerCase());
1611
+ // Collect entity types for display
1612
+ const entityTypes = new Map();
1613
+ for (const e of allEntities) {
1614
+ if (!entityTypes.has(e.name)) {
1615
+ entityTypes.set(e.name, e.entityType || 'unknown');
1616
+ }
1617
+ }
1618
+ while (queue.length > 0) {
1619
+ const { name, depth } = queue.shift();
1620
+ result.push({
1621
+ name,
1622
+ entityType: entityTypes.get(name) || 'unknown',
1623
+ depth,
1624
+ });
1625
+ // Find all entities that import this one
1626
+ const importers = importedByName.get(name);
1627
+ if (!importers)
1628
+ continue;
1629
+ for (const importerName of importers) {
1630
+ if (visited.has(importerName.toLowerCase()))
1631
+ continue;
1632
+ visited.add(importerName.toLowerCase());
1633
+ queue.push({ name: importerName, depth: depth + 1 });
1634
+ }
1635
+ }
1636
+ // Output
1637
+ if (result.length <= 1) {
1638
+ console.log(chalk.yellow(`No dependents found for "${entityName}".`));
1639
+ console.log(chalk.dim(' This entity is not imported by any other known entities.'));
1640
+ console.log(chalk.dim(' Run codeyam editor analyze-imports to populate import data.'));
1641
+ return;
1642
+ }
1643
+ console.log(chalk.bold(`Dependency tree for ${entityName} (${result.length} entities):`));
1644
+ console.log();
1645
+ for (const entry of result) {
1646
+ const indent = ' '.repeat(entry.depth);
1647
+ const prefix = entry.depth === 0 ? chalk.cyan('●') : chalk.dim('├──');
1648
+ const label = entry.depth === 0
1649
+ ? chalk.cyan(`${entry.name} (changed)`)
1650
+ : chalk.white(entry.name);
1651
+ const type = chalk.dim(`[${entry.entityType}]`);
1652
+ console.log(`${indent}${prefix} ${label} ${type}`);
1653
+ }
1654
+ console.log();
1655
+ console.log(chalk.dim('All listed entities need their scenarios recaptured after changes.'));
1656
+ }
1657
+ // ─── Change subcommand ───────────────────────────────────────────────
1658
+ /**
1659
+ * `codeyam editor change <feature>`
1660
+ *
1661
+ * Prints a condensed post-change checklist that guides Claude through
1662
+ * re-verifying after user-requested modifications. This is the "change
1663
+ * loop" — it replaces the freeform "make changes" path with structured
1664
+ * instructions that always loop back to step 13 present.
1665
+ */
1666
+ function handleChange(feature) {
1667
+ const root = getProjectRoot();
1668
+ if (!feature) {
1669
+ // Try to read feature from state
1670
+ const state = readState(root);
1671
+ if (state?.feature) {
1672
+ feature = state.feature;
1673
+ }
1674
+ else {
1675
+ console.error(chalk.red('Error: Feature name required.'));
1676
+ console.error(chalk.dim(' Usage: codeyam editor change "My Feature"'));
1677
+ process.exit(1);
1678
+ }
1679
+ }
1680
+ const port = getServerPort();
1681
+ console.log();
1682
+ console.log(chalk.bold.cyan('━━━ Change Loop ━━━'));
1683
+ console.log(chalk.dim(`Feature: ${feature}`));
1684
+ console.log();
1685
+ console.log('The user has requested changes. Follow this checklist after making them.');
1686
+ console.log();
1687
+ console.log(chalk.bold.cyan('Keep the preview moving:'));
1688
+ console.log(chalk.cyan(` Refresh after EACH individual change — not after all changes are done:`));
1689
+ console.log(chalk.cyan(` codeyam editor preview`));
1690
+ console.log(chalk.cyan(' The user is watching the preview. Let them see progress incrementally.'));
1691
+ console.log();
1692
+ console.log(chalk.bold('0. Close the results panel:'));
1693
+ checkbox(`Hide results: \`codeyam editor hide-results\``);
1694
+ console.log();
1695
+ console.log(chalk.bold('1. Re-register affected component scenarios:'));
1696
+ checkbox('For each component you modified, re-register ALL its scenarios');
1697
+ console.log(chalk.dim(` codeyam editor register '{"name":"ComponentName - ScenarioName","componentName":"ComponentName","componentPath":"app/components/ComponentName.tsx","url":"/isolated-components/ComponentName?s=ScenarioName"}'`));
1698
+ checkbox('For any NEW components: `codeyam editor isolate NewComponent` then create isolation routes and register scenarios');
1699
+ console.log();
1700
+ console.log(chalk.bold('2. Re-run affected tests:'));
1701
+ checkbox('Re-run test files for any functions you modified');
1702
+ console.log(chalk.dim(' Example: npx vitest run app/lib/drinks.test.ts'));
1703
+ checkbox('If any test fails, fix the source code and re-run');
1704
+ checkbox('If you re-seeded the database, restart the dev server to pick up fresh data');
1705
+ console.log(chalk.dim(` codeyam editor dev-server '{"action":"restart"}'`));
1706
+ console.log();
1707
+ console.log(chalk.bold('3. Re-capture app-level scenarios:'));
1708
+ checkbox('Run `codeyam editor analyze-imports` to refresh the import graph after code changes');
1709
+ checkbox('If component changes affect app-level pages, re-register affected app scenarios');
1710
+ checkbox(`Use \`codeyam editor dependents ComponentName\` to find what needs recapturing`);
1711
+ console.log();
1712
+ console.log(chalk.bold('4. Verify completeness:'));
1713
+ checkbox(`Refresh the preview: \`codeyam editor preview\``);
1714
+ checkbox(`Navigate to key pages to verify changes: \`codeyam editor preview '{"path":"/your-route"}'\``);
1715
+ checkbox('Run `codeyam editor audit` — all checks must pass');
1716
+ checkbox(`Check for client-side errors: \`codeyam editor client-errors\``);
1717
+ checkbox('Fix any errors, then re-register affected scenarios');
1718
+ console.log();
1719
+ console.log(chalk.bold('5. Update the existing journal entry:'));
1720
+ checkbox('Get existing entry: `codeyam editor journal-list`');
1721
+ checkbox('Find the uncommitted entry (commitSha is null) from this feature session');
1722
+ checkbox(`Update it: \`codeyam editor journal-update '{"time":"<entry time>","description":"<updated description including changes>","includeSessionScenarios":true}'\``);
1723
+ console.log(chalk.dim(' Always update the existing uncommitted entry — do NOT create a new one.'));
1724
+ console.log(chalk.dim(' Only create a new entry (POST) if no uncommitted entry exists for this feature.'));
1725
+ console.log();
1726
+ console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
1727
+ console.log(chalk.red.bold(' REQUIRED: Show Results'));
1728
+ console.log(chalk.red.bold(' When all checks pass, you MUST run: ') +
1729
+ chalk.bold(`codeyam editor 13`));
1730
+ console.log(chalk.red.bold(' The user ALWAYS expects to see results after changes. DO NOT skip this step.'));
1731
+ console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
1732
+ console.log();
1733
+ }
1734
+ // ─── Audit subcommand ────────────────────────────────────────────────
1735
+ /**
1736
+ * `codeyam editor audit`
1737
+ *
1738
+ * Fetches the /api/editor-audit endpoint and prints a checklist showing
1739
+ * which glossary components have registered scenarios and which functions
1740
+ * have test files. Exits with code 1 if anything is missing.
1741
+ */
1742
+ async function handleAudit() {
1743
+ const port = getServerPort();
1744
+ const url = `http://localhost:${port}/api/editor-audit`;
1745
+ let data;
1746
+ try {
1747
+ const res = await fetch(url);
1748
+ if (!res.ok) {
1749
+ console.error(chalk.red(`Error: Audit endpoint returned ${res.status}`));
1750
+ process.exit(1);
1751
+ }
1752
+ data = await res.json();
1753
+ }
1754
+ catch (err) {
1755
+ console.error(chalk.red('Error: Could not reach the CodeYam server. Is it running?'));
1756
+ console.error(chalk.dim(` ${err.message}`));
1757
+ process.exit(1);
1758
+ }
1759
+ const { components, functions, summary } = data;
1760
+ console.log();
1761
+ console.log(chalk.bold.cyan('━━━ Editor Audit ━━━'));
1762
+ console.log();
1763
+ // Components
1764
+ if (components.length > 0) {
1765
+ console.log(chalk.bold('Components (scenarios):'));
1766
+ for (const c of components) {
1767
+ const icon = c.status === 'ok' ? chalk.green('✓') : chalk.red('✗');
1768
+ let detail;
1769
+ if (c.status === 'has_errors') {
1770
+ detail = chalk.red(` — ${c.scenarioCount} scenario${c.scenarioCount !== 1 ? 's' : ''} but has client errors`);
1771
+ }
1772
+ else if (c.status === 'ok') {
1773
+ detail = chalk.dim(` (${c.scenarioCount} scenario${c.scenarioCount !== 1 ? 's' : ''})`);
1774
+ }
1775
+ else {
1776
+ detail = chalk.red(' — no scenarios registered');
1777
+ }
1778
+ console.log(` ${icon} ${c.name}${detail}`);
1779
+ if (c.clientErrors && c.clientErrors.length > 0) {
1780
+ for (const err of c.clientErrors.slice(0, 3)) {
1781
+ console.log(chalk.red(` → ${err}`));
1782
+ }
1783
+ if (c.clientErrors.length > 3) {
1784
+ console.log(chalk.dim(` … and ${c.clientErrors.length - 3} more`));
1785
+ }
1786
+ }
1787
+ }
1788
+ console.log();
1789
+ }
1790
+ // Functions
1791
+ if (functions.length > 0) {
1792
+ console.log(chalk.bold('Functions (test files):'));
1793
+ for (const f of functions) {
1794
+ const icon = f.status === 'ok' ? chalk.green('✓') : chalk.red('✗');
1795
+ let detail;
1796
+ switch (f.status) {
1797
+ case 'ok':
1798
+ detail = chalk.dim(` (${f.testFile})`);
1799
+ break;
1800
+ case 'failing':
1801
+ detail = chalk.red(` — tests failing: ${f.testFile}`);
1802
+ break;
1803
+ case 'name_mismatch':
1804
+ detail = chalk.red(` — tests pass but missing top-level describe("${f.name}", ...) — tests won't display in UI`);
1805
+ break;
1806
+ case 'missing':
1807
+ default:
1808
+ detail = f.testFile
1809
+ ? chalk.red(` — test file missing: ${f.testFile}`)
1810
+ : chalk.red(' — no test file specified');
1811
+ break;
1812
+ }
1813
+ console.log(` ${icon} ${f.name}${detail}`);
1814
+ }
1815
+ console.log();
1816
+ }
1817
+ // Summary
1818
+ const allOk = summary.allPassing;
1819
+ if (allOk) {
1820
+ console.log(chalk.green.bold('All checks passed!') +
1821
+ chalk.dim(` (${summary.totalComponents} component${summary.totalComponents !== 1 ? 's' : ''}, ${summary.totalFunctions} function${summary.totalFunctions !== 1 ? 's' : ''})`));
1822
+ }
1823
+ else {
1824
+ const parts = [];
1825
+ if (summary.componentsMissing > 0) {
1826
+ parts.push(`${summary.componentsMissing} component${summary.componentsMissing !== 1 ? 's' : ''} missing scenarios`);
1827
+ }
1828
+ if (summary.componentsWithErrors > 0) {
1829
+ parts.push(`${summary.componentsWithErrors} component${summary.componentsWithErrors !== 1 ? 's' : ''} with client errors`);
1830
+ }
1831
+ if (summary.functionsMissing > 0) {
1832
+ parts.push(`${summary.functionsMissing} function${summary.functionsMissing !== 1 ? 's' : ''} missing tests`);
1833
+ }
1834
+ if (summary.functionsFailing > 0) {
1835
+ parts.push(`${summary.functionsFailing} function${summary.functionsFailing !== 1 ? 's' : ''} with failing tests`);
1836
+ }
1837
+ if (summary.functionsNameMismatch > 0) {
1838
+ parts.push(`${summary.functionsNameMismatch} function${summary.functionsNameMismatch !== 1 ? 's' : ''} with test name mismatch (missing top-level describe)`);
1839
+ }
1840
+ console.log(chalk.red.bold('Audit failed: ') + parts.join(', '));
1841
+ }
1842
+ console.log();
1843
+ if (!allOk) {
1844
+ process.exit(1);
1845
+ }
1846
+ }
1847
+ // ─── Scenarios subcommand ─────────────────────────────────────────────
1848
+ async function handleScenarios() {
1849
+ const port = getServerPort();
1850
+ const url = `http://localhost:${port}/api/editor-scenarios`;
1851
+ let data;
1852
+ try {
1853
+ const res = await fetch(url);
1854
+ if (!res.ok) {
1855
+ console.error(chalk.red(`Error: Scenarios endpoint returned ${res.status}`));
1856
+ process.exit(1);
1857
+ }
1858
+ data = await res.json();
1859
+ }
1860
+ catch (err) {
1861
+ console.error(chalk.red('Error: Could not reach the CodeYam server. Is it running?'));
1862
+ console.error(chalk.dim(` ${err.message}`));
1863
+ process.exit(1);
1864
+ }
1865
+ const scenarios = data.scenarios;
1866
+ if (scenarios.length === 0) {
1867
+ console.log();
1868
+ console.log(chalk.yellow('No scenarios found for this feature session.'));
1869
+ console.log();
1870
+ return;
1871
+ }
1872
+ // Group by componentName (null → "Uncategorized")
1873
+ const groups = new Map();
1874
+ for (const s of scenarios) {
1875
+ const key = s.componentName || 'Uncategorized';
1876
+ if (!groups.has(key))
1877
+ groups.set(key, []);
1878
+ groups.get(key).push(s);
1879
+ }
1880
+ // Strip component prefix from names (e.g. "DrinkCard - Default" → "Default")
1881
+ function stripPrefix(name, componentName) {
1882
+ if (!componentName)
1883
+ return name;
1884
+ const prefix = `${componentName} - `;
1885
+ return name.startsWith(prefix) ? name.slice(prefix.length) : name;
1886
+ }
1887
+ // Calculate column widths
1888
+ let maxCompLen = 'Component'.length;
1889
+ let maxScenLen = 'Scenario'.length;
1890
+ for (const [comp, items] of groups) {
1891
+ maxCompLen = Math.max(maxCompLen, comp.length);
1892
+ for (const s of items) {
1893
+ const display = stripPrefix(s.name, s.componentName);
1894
+ maxScenLen = Math.max(maxScenLen, display.length);
1895
+ }
1896
+ }
1897
+ const pad = (str, len) => str + ' '.repeat(Math.max(0, len - str.length));
1898
+ // Build OSC 8 hyperlink (display length = visible text only)
1899
+ const osc8 = (url, text) => `\x1b]8;;${url}\x07${text}\x1b]8;;\x07`;
1900
+ // Print table
1901
+ console.log();
1902
+ console.log(chalk.bold.cyan('━━━ Scenario Table ━━━'));
1903
+ console.log();
1904
+ const divComp = '─'.repeat(maxCompLen + 2);
1905
+ const divScen = '─'.repeat(maxScenLen + 2);
1906
+ // Header
1907
+ console.log(`┌${divComp}┬${divScen}┐`);
1908
+ console.log(`│ ${chalk.bold(pad('Component', maxCompLen))} │ ${chalk.bold(pad('Scenario', maxScenLen))} │`);
1909
+ console.log(`├${divComp}┼${divScen}┤`);
1910
+ // Rows
1911
+ let groupIndex = 0;
1912
+ for (const [comp, items] of groups) {
1913
+ if (groupIndex > 0) {
1914
+ console.log(`├${divComp}┼${divScen}┤`);
1915
+ }
1916
+ for (let i = 0; i < items.length; i++) {
1917
+ const s = items[i];
1918
+ const compCell = i === 0 ? chalk.bold(pad(comp, maxCompLen)) : pad('', maxCompLen);
1919
+ const display = stripPrefix(s.name, s.componentName);
1920
+ const linked = osc8(s.link, display);
1921
+ // linked has invisible escape chars; pad based on display length
1922
+ const scenPad = ' '.repeat(Math.max(0, maxScenLen - display.length));
1923
+ console.log(`│ ${compCell} │ ${linked}${scenPad} │`);
1924
+ }
1925
+ groupIndex++;
1926
+ }
1927
+ // Footer
1928
+ console.log(`└${divComp}┴${divScen}┘`);
1929
+ console.log();
1930
+ const total = scenarios.length;
1931
+ const groupCount = groups.size;
1932
+ console.log(chalk.dim(`${total} scenario${total !== 1 ? 's' : ''} across ${groupCount} component${groupCount !== 1 ? 's' : ''}`));
1933
+ console.log();
1934
+ }
1935
+ // ─── Template subcommand ─────────────────────────────────────────────
1936
+ async function handleTemplate() {
1937
+ const root = getProjectRoot();
1938
+ const port = getServerPort();
1939
+ if (hasProject(root)) {
1940
+ console.log(chalk.yellow('Project already exists (package.json found). Skipping.'));
1941
+ return;
1942
+ }
1943
+ // Determine which template to use from editor state
1944
+ const state = readState(root);
1945
+ const techStackId = state?.techStackId;
1946
+ const stack = techStackId
1947
+ ? TECH_STACKS.find((s) => s.id === techStackId)
1948
+ : undefined;
1949
+ const templateName = stack?.templateDir || 'nextjs-prisma-sqlite';
1950
+ const templateDir = path.join(__dirname, '..', '..', 'templates', templateName);
1951
+ if (!fs.existsSync(templateDir)) {
1952
+ console.error(chalk.red('Error: Template directory not found at ' + templateDir));
1953
+ process.exit(1);
1954
+ }
1955
+ const { execSync } = await import('child_process');
1956
+ // 1. Copy template files
1957
+ console.log(chalk.bold(`Copying ${stack?.name || templateName} template files...`));
1958
+ execSync(`cp -r ${templateDir}/* .`, { cwd: root, stdio: 'inherit' });
1959
+ // Copy dotfiles that are stored without the dot prefix in templates
1960
+ const gitignorePath = path.join(templateDir, 'gitignore');
1961
+ if (fs.existsSync(gitignorePath)) {
1962
+ execSync(`cp ${templateDir}/gitignore .gitignore`, {
1963
+ cwd: root,
1964
+ stdio: 'inherit',
1965
+ });
1966
+ }
1967
+ const envPath = path.join(templateDir, 'env');
1968
+ if (fs.existsSync(envPath)) {
1969
+ execSync(`cp ${templateDir}/env .env`, { cwd: root, stdio: 'inherit' });
1970
+ }
1971
+ // Copy seed adapter into .codeyam/ (stored outside .codeyam/ in the template
1972
+ // to avoid gitignore issues — .codeyam/ is selectively ignored in user projects)
1973
+ const seedAdapterSrc = path.join(templateDir, 'seed-adapter.ts');
1974
+ if (fs.existsSync(seedAdapterSrc)) {
1975
+ const codeyamDir = path.join(root, '.codeyam');
1976
+ if (!fs.existsSync(codeyamDir)) {
1977
+ fs.mkdirSync(codeyamDir, { recursive: true });
1978
+ }
1979
+ fs.copyFileSync(seedAdapterSrc, path.join(codeyamDir, 'seed-adapter.ts'));
1980
+ }
1981
+ console.log(chalk.green(' Template files copied.'));
1982
+ // 2. Install dependencies
1983
+ console.log(chalk.bold('Installing dependencies...'));
1984
+ execSync('npm install', { cwd: root, stdio: 'inherit' });
1985
+ console.log(chalk.green(' Dependencies installed.'));
1986
+ // 3. Initialize git
1987
+ const hasGit = fs.existsSync(path.join(root, '.git'));
1988
+ if (!hasGit) {
1989
+ console.log(chalk.bold('Initializing git...'));
1990
+ execSync('git init && git add -A && git commit -m "Initial commit"', {
1991
+ cwd: root,
1992
+ stdio: 'inherit',
1993
+ });
1994
+ console.log(chalk.green(' Git initialized.'));
1995
+ }
1996
+ // 4. Run codeyam init
1997
+ console.log(chalk.bold('Running codeyam init...'));
1998
+ await initCommand.handler({
1999
+ force: true,
2000
+ 'keep-server': true,
2001
+ autoInit: false,
2002
+ $0: '',
2003
+ _: [],
2004
+ });
2005
+ console.log(chalk.green(' CodeYam initialized.'));
2006
+ // 5. Verify config has startCommand
2007
+ const configPath = path.join(root, '.codeyam', 'config.json');
2008
+ if (fs.existsSync(configPath)) {
2009
+ try {
2010
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
2011
+ const webapps = config.webapps || [];
2012
+ const hasStartCommand = webapps.some((w) => w.startCommand);
2013
+ if (!hasStartCommand) {
2014
+ console.log(chalk.yellow(' Warning: No startCommand found in .codeyam/config.json webapps.'));
2015
+ console.log(chalk.dim(' You may need to add: "startCommand": { "command": "sh", "args": ["-c", "npm run dev -- --port $PORT"] }'));
2016
+ }
2017
+ }
2018
+ catch {
2019
+ // Config parse error is non-fatal
2020
+ }
2021
+ }
2022
+ // 6. Trigger editor-refresh so the server picks up the new project
2023
+ console.log(chalk.bold('Refreshing editor...'));
2024
+ try {
2025
+ await fetch(`http://localhost:${port}/api/editor-refresh`, {
2026
+ method: 'POST',
2027
+ });
2028
+ console.log(chalk.green(' Editor refreshed.'));
2029
+ }
2030
+ catch {
2031
+ console.log(chalk.yellow(' Could not reach the CodeYam server for refresh. You may need to restart it.'));
2032
+ }
2033
+ console.log();
2034
+ console.log(chalk.green.bold('Project scaffolded and ready!'));
2035
+ console.log(chalk.dim('Next: Define your Prisma models, push the schema, seed the database, and build your feature.'));
2036
+ }
2037
+ // ─── Sync subcommand ─────────────────────────────────────────────────
2038
+ /**
2039
+ * `codeyam editor sync`
2040
+ * Import scenarios from scenarios-manifest.json into the local database.
2041
+ */
2042
+ async function handleSync() {
2043
+ const root = getProjectRoot();
2044
+ const manifest = readManifest(root);
2045
+ if (!manifest) {
2046
+ console.log(chalk.yellow('No scenarios-manifest.json found. Nothing to sync.'));
2047
+ return;
2048
+ }
2049
+ if (manifest.scenarios.length === 0) {
2050
+ console.log(chalk.dim('Manifest is empty. Nothing to sync.'));
2051
+ return;
2052
+ }
2053
+ const configPath = path.join(root, '.codeyam', 'config.json');
2054
+ let projectSlug = null;
2055
+ try {
2056
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
2057
+ projectSlug = config.projectSlug || null;
2058
+ }
2059
+ catch {
2060
+ // fall through
2061
+ }
2062
+ if (!projectSlug) {
2063
+ console.error(chalk.red('Error: No project slug found. Run codeyam init first.'));
2064
+ process.exit(1);
2065
+ }
2066
+ const connectionOk = await withoutSpinner(() => testEnvironment());
2067
+ if (!connectionOk) {
2068
+ errorLog('Environment validation failed');
2069
+ return;
2070
+ }
2071
+ const { project } = await requireBranchAndProject(projectSlug);
2072
+ const { getDatabase } = await import('../../../packages/database/index.js');
2073
+ const db = getDatabase();
2074
+ // Fetch existing editor scenarios
2075
+ const existingRows = await db
2076
+ .selectFrom('editor_scenarios')
2077
+ .select(['id', 'updated_at'])
2078
+ .where('project_id', '=', project.id)
2079
+ .execute();
2080
+ const result = await syncManifestToDatabase(root, project.id, existingRows, async (row) => {
2081
+ await db
2082
+ .insertInto('editor_scenarios')
2083
+ .values(row)
2084
+ .execute();
2085
+ }, async (id, row) => {
2086
+ await db
2087
+ .updateTable('editor_scenarios')
2088
+ .set(row)
2089
+ .where('id', '=', id)
2090
+ .execute();
2091
+ });
2092
+ if (result.inserted === 0 && result.updated === 0) {
2093
+ console.log(chalk.dim('All scenarios up to date.'));
2094
+ }
2095
+ else {
2096
+ const parts = [];
2097
+ if (result.inserted > 0)
2098
+ parts.push(`${result.inserted} imported`);
2099
+ if (result.updated > 0)
2100
+ parts.push(`${result.updated} updated`);
2101
+ if (result.skipped > 0)
2102
+ parts.push(`${result.skipped} unchanged`);
2103
+ console.log(chalk.green(`Synced scenarios: ${parts.join(', ')}`));
2104
+ }
2105
+ }
2106
+ // ─── Verify Images subcommand ─────────────────────────────────────────
2107
+ async function handleVerifyImages(jsonArg) {
2108
+ const port = getServerPort();
2109
+ const { parseVerifyImagesInput, extractImageUrls, resolveImageUrl, verifyImageUrls, buildVerifyImagesReport, extractImageUrlsFromScenarioFiles, } = await import('../utils/editorImageVerifier.js');
2110
+ let paths;
2111
+ let explicitImageUrls;
2112
+ try {
2113
+ const input = parseVerifyImagesInput(jsonArg);
2114
+ paths = input.paths;
2115
+ explicitImageUrls = input.imageUrls;
2116
+ }
2117
+ catch {
2118
+ console.error(chalk.red('Error: Invalid JSON argument'));
2119
+ process.exit(1);
2120
+ }
2121
+ // Get dev server URL
2122
+ let devServerUrl;
2123
+ try {
2124
+ const res = await fetch(`http://localhost:${port}/api/editor-dev-server`);
2125
+ const data = await res.json();
2126
+ devServerUrl = data.url || `http://localhost:${data.port || 3000}`;
2127
+ }
2128
+ catch {
2129
+ console.error(chalk.red('Error: Could not reach the CodeYam server. Is it running?'));
2130
+ process.exit(1);
2131
+ }
2132
+ const allUrls = new Set();
2133
+ let pagesChecked = 0;
2134
+ for (const pagePath of paths) {
2135
+ const pageUrl = `${devServerUrl}${pagePath}`;
2136
+ try {
2137
+ const res = await fetch(pageUrl);
2138
+ if (!res.ok) {
2139
+ console.error(chalk.red(` ✗ ${pagePath} — HTTP ${res.status}`));
2140
+ continue;
2141
+ }
2142
+ const html = await res.text();
2143
+ const imageUrls = extractImageUrls(html);
2144
+ for (const url of imageUrls) {
2145
+ allUrls.add(resolveImageUrl(url, devServerUrl));
2146
+ }
2147
+ pagesChecked++;
2148
+ }
2149
+ catch (err) {
2150
+ console.error(chalk.red(` ✗ ${pagePath} — ${err.message}`));
2151
+ }
2152
+ }
2153
+ // Add explicitly-provided image URLs (Claude passes these directly)
2154
+ for (const url of explicitImageUrls) {
2155
+ allUrls.add(resolveImageUrl(url, devServerUrl));
2156
+ }
2157
+ // Also scan scenario mock data files for image URLs (client-rendered pages)
2158
+ const scenariosDir = path.join(getProjectRoot(), '.codeyam', 'editor-scenarios');
2159
+ const { urls: scenarioUrls, filesScanned: scenarioFilesScanned } = extractImageUrlsFromScenarioFiles(scenariosDir);
2160
+ for (const url of scenarioUrls) {
2161
+ allUrls.add(resolveImageUrl(url, devServerUrl));
2162
+ }
2163
+ const urlList = [...allUrls];
2164
+ const results = urlList.length > 0 ? await verifyImageUrls(urlList) : [];
2165
+ const report = buildVerifyImagesReport({
2166
+ pagesChecked,
2167
+ imageUrls: urlList,
2168
+ results,
2169
+ scenarioFilesScanned,
2170
+ });
2171
+ console.log(JSON.stringify(report, null, 2));
2172
+ if (report.failures.length > 0) {
2173
+ process.exit(1);
2174
+ }
2175
+ }
2176
+ // ─── Debug subcommand ────────────────────────────────────────────────
2177
+ function handleEditorDebug(args) {
2178
+ const root = getProjectRoot();
2179
+ const feature = args.feature || 'Sample Feature';
2180
+ const includeContext = args.includeContext !== false;
2181
+ let targets;
2182
+ try {
2183
+ targets = parseDebugTargets(args.target);
2184
+ }
2185
+ catch (err) {
2186
+ const msg = err instanceof Error ? err.message : String(err);
2187
+ console.error(chalk.red(`Error: ${msg}`));
2188
+ process.exit(1);
2189
+ }
2190
+ const includeResume = typeof args.resume === 'boolean' ? args.resume : true;
2191
+ const includeNormal = typeof args.resume === 'boolean' ? !args.resume : true;
2192
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
2193
+ const outputDir = args.output
2194
+ ? path.resolve(args.output)
2195
+ : path.join(root, '.codeyam', 'logs', 'editor-debug', timestamp);
2196
+ fs.mkdirSync(outputDir, { recursive: true });
2197
+ const contextEntries = includeContext
2198
+ ? writeContextSnapshot(root, outputDir)
2199
+ : [];
2200
+ const scenarios = [];
2201
+ const wants = (id) => (targets ? targets.has(id) : true);
2202
+ if (wants('setup')) {
2203
+ scenarios.push({
2204
+ id: 'setup',
2205
+ title: 'Setup (no project)',
2206
+ render: () => withTempRoot(false, (tempRoot) => captureOutput(() => printSetup(tempRoot))),
2207
+ });
2208
+ }
2209
+ if (wants('overview')) {
2210
+ scenarios.push({
2211
+ id: 'overview',
2212
+ title: 'Cycle overview (project, no state)',
2213
+ render: () => withTempRoot(true, (tempRoot) => captureOutput(() => printCycleOverview(tempRoot, null))),
2214
+ });
2215
+ }
2216
+ if (wants('overview-with-state')) {
2217
+ scenarios.push({
2218
+ id: 'overview-with-state',
2219
+ title: 'Cycle overview (project, with state)',
2220
+ render: () => withTempRoot(true, (tempRoot) => captureOutput(() => printCycleOverview(tempRoot, makeState(4, feature)))),
2221
+ });
2222
+ }
2223
+ const stepFns = {
2224
+ 1: (r, f) => printStep1(r, f),
2225
+ 2: printStep2,
2226
+ 3: printStep3,
2227
+ 4: printStep4,
2228
+ 5: printStep5,
2229
+ 6: printStep6,
2230
+ 7: printStep7,
2231
+ 8: printStep8,
2232
+ 9: printStep9,
2233
+ 10: printStep10,
2234
+ 11: printStep11,
2235
+ 12: printStep12,
2236
+ 13: printStep13,
2237
+ };
2238
+ for (let step = 1; step <= 13; step++) {
2239
+ const stepId = `step-${step}`;
2240
+ if (!wants(stepId))
2241
+ continue;
2242
+ if (includeNormal) {
2243
+ scenarios.push({
2244
+ id: stepId,
2245
+ title: `Step ${step} (${STEP_LABELS[step]})`,
2246
+ render: () => withTempRoot(true, (tempRoot) => captureOutput(() => stepFns[step](tempRoot, feature))),
2247
+ });
2248
+ if (step === 1 && !args.feature) {
2249
+ scenarios.push({
2250
+ id: 'step-1-no-feature',
2251
+ title: 'Step 1 (Plan) — no feature provided',
2252
+ render: () => withTempRoot(true, (tempRoot) => captureOutput(() => printStep1(tempRoot, undefined))),
2253
+ });
2254
+ }
2255
+ if (step === 2) {
2256
+ scenarios.push({
2257
+ id: 'step-2-scaffold',
2258
+ title: 'Step 2 (Prototype) — scaffold flow (no project)',
2259
+ render: () => withTempRoot(false, (tempRoot) => captureOutput(() => printStep2(tempRoot, feature))),
2260
+ });
2261
+ }
2262
+ }
2263
+ if (includeResume) {
2264
+ scenarios.push({
2265
+ id: `${stepId}-resume`,
2266
+ title: `Step ${step} (${STEP_LABELS[step]}) — resuming`,
2267
+ render: () => withTempRoot(true, (tempRoot) => {
2268
+ writeState(tempRoot, makeState(step, feature));
2269
+ return captureOutput(() => stepFns[step](tempRoot, feature));
2270
+ }),
2271
+ });
2272
+ }
2273
+ }
2274
+ const indexLines = [
2275
+ '# CodeYam Editor Debug Bundle',
2276
+ '',
2277
+ `Generated: ${new Date().toISOString()}`,
2278
+ `Feature: "${feature}"`,
2279
+ `Resume variants: ${includeResume ? 'included' : 'skipped'}`,
2280
+ 'Note: Output files preserve ANSI color codes (as printed to the terminal).',
2281
+ '',
2282
+ ];
2283
+ if (includeContext) {
2284
+ indexLines.push('## Context snapshot');
2285
+ if (contextEntries.length === 0) {
2286
+ indexLines.push('- (none)');
2287
+ }
2288
+ else {
2289
+ for (const entry of contextEntries) {
2290
+ const source = entry.source ? ` ← ${entry.source}` : '';
2291
+ indexLines.push(`- ${entry.label}: ${entry.file} (${entry.status})${source}`);
2292
+ }
2293
+ }
2294
+ indexLines.push('');
2295
+ }
2296
+ indexLines.push('## Scenarios');
2297
+ for (const scenario of scenarios) {
2298
+ const fileName = `${scenario.id}.txt`;
2299
+ const output = scenario.render();
2300
+ fs.writeFileSync(path.join(outputDir, fileName), output, 'utf8');
2301
+ indexLines.push(`- ${scenario.title}: ${fileName}`);
2302
+ }
2303
+ fs.writeFileSync(path.join(outputDir, 'index.md'), indexLines.join('\n'), 'utf8');
2304
+ console.log();
2305
+ console.log(chalk.bold.cyan('━━━ Editor Debug Bundle ━━━'));
2306
+ console.log();
2307
+ console.log(chalk.green('Wrote debug bundle to: ') + chalk.bold(outputDir));
2308
+ console.log(chalk.dim(' Open index.md for the full list of scenario outputs.'));
2309
+ console.log();
2310
+ }
941
2311
  // ─── Command definition ───────────────────────────────────────────────
942
2312
  const editorCommand = {
943
2313
  command: 'editor [step] [json]',
944
2314
  describe: 'Editor mode guided workflow',
945
2315
  builder: (yargs) => {
946
- return yargs
2316
+ const stepDescription = IS_INTERNAL_BUILD
2317
+ ? 'Step number (1-13) or subcommand (template, register, isolate, analyze-imports, dependents, audit, scenarios, change, sync, debug, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors)'
2318
+ : 'Step number (1-13) or subcommand (template, register, isolate, analyze-imports, dependents, audit, scenarios, change, sync, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors)';
2319
+ let builder = yargs
947
2320
  .positional('step', {
948
2321
  type: 'string',
949
- describe: 'Step number (1-11) or subcommand (register, analyze-imports)',
2322
+ describe: stepDescription,
950
2323
  })
951
2324
  .positional('json', {
952
2325
  type: 'string',
@@ -955,10 +2328,79 @@ const editorCommand = {
955
2328
  .option('feature', {
956
2329
  type: 'string',
957
2330
  describe: 'Feature name (required for step 2)',
2331
+ })
2332
+ .option('app-formats', {
2333
+ type: 'string',
2334
+ describe: 'Comma-separated app formats (web-app, mobile-responsive-web-app, desktop-app, mobile-app)',
2335
+ })
2336
+ .option('tech-stack', {
2337
+ type: 'string',
2338
+ describe: 'Selected tech stack ID (e.g., nextjs-prisma-sqlite)',
2339
+ })
2340
+ .option('prompt', {
2341
+ type: 'string',
2342
+ describe: "User's original prompt (captured for journal/results)",
2343
+ })
2344
+ .option('port', {
2345
+ type: 'number',
2346
+ alias: 'p',
2347
+ describe: 'Port to run the web server on',
2348
+ default: 3111,
958
2349
  });
2350
+ if (IS_INTERNAL_BUILD) {
2351
+ builder = builder
2352
+ .option('target', {
2353
+ type: 'string',
2354
+ describe: 'Debug target (setup, overview, overview-with-state, step-1..step-13, or comma-separated list)',
2355
+ })
2356
+ .option('resume', {
2357
+ type: 'boolean',
2358
+ describe: 'Debug: only render resume variants (use --no-resume for only normal)',
2359
+ })
2360
+ .option('include-context', {
2361
+ type: 'boolean',
2362
+ default: true,
2363
+ describe: 'Debug: include CLAUDE.md, skill, and editor-mode-context snapshots',
2364
+ })
2365
+ .option('output', {
2366
+ type: 'string',
2367
+ describe: 'Debug: output directory for the bundle',
2368
+ });
2369
+ }
2370
+ return builder;
959
2371
  },
960
2372
  handler: async (argv) => {
961
2373
  const root = getProjectRoot();
2374
+ // API subcommands: preview, show-results, hide-results, commit,
2375
+ // journal, journal-update, dev-server, client-errors
2376
+ if (argv.step && EDITOR_API_SUBCOMMANDS.includes(argv.step)) {
2377
+ const port = getServerPort();
2378
+ try {
2379
+ const request = buildEditorApiRequest(argv.step, argv.json || undefined);
2380
+ const result = await callEditorApi(request, port);
2381
+ if (!result.ok) {
2382
+ console.error(chalk.red(`Error: ${request.endpoint} returned ${result.status}`));
2383
+ if (result.data)
2384
+ console.error(JSON.stringify(result.data, null, 2));
2385
+ process.exit(1);
2386
+ }
2387
+ if (result.data != null) {
2388
+ const summary = formatApiSubcommandResult(argv.step, result.data);
2389
+ if (summary) {
2390
+ console.log(summary);
2391
+ }
2392
+ else {
2393
+ console.log(JSON.stringify(result.data, null, 2));
2394
+ }
2395
+ }
2396
+ }
2397
+ catch (err) {
2398
+ console.error(chalk.red('Error: Could not reach the CodeYam server. Is it running?'));
2399
+ console.error(chalk.dim(` ${err.message}`));
2400
+ process.exit(1);
2401
+ }
2402
+ return;
2403
+ }
962
2404
  // Subcommand: codeyam editor register '{"name":"..."}'
963
2405
  if (argv.step === 'register') {
964
2406
  await handleRegister(argv.json || '');
@@ -969,27 +2411,259 @@ const editorCommand = {
969
2411
  await handleAnalyzeImports();
970
2412
  return;
971
2413
  }
972
- const step = argv.step ? parseInt(argv.step, 10) : undefined;
973
- if (step != null && (isNaN(step) || step < 1 || step > 11)) {
974
- console.error(chalk.red(`Error: Invalid step "${argv.step}". Must be 1-11.`));
975
- process.exit(1);
2414
+ // Subcommand: codeyam editor dependents <EntityName>
2415
+ if (argv.step === 'dependents') {
2416
+ await handleDependents(argv.json || '');
2417
+ return;
976
2418
  }
977
- if (step == null) {
978
- // No step specified: setup or overview
2419
+ // Subcommand: codeyam editor audit
2420
+ if (argv.step === 'audit') {
2421
+ await handleAudit();
2422
+ return;
2423
+ }
2424
+ // Subcommand: codeyam editor scenarios
2425
+ if (argv.step === 'scenarios') {
2426
+ await handleScenarios();
2427
+ return;
2428
+ }
2429
+ // Subcommand: codeyam editor change <feature>
2430
+ if (argv.step === 'change') {
2431
+ handleChange(argv.json || '');
2432
+ return;
2433
+ }
2434
+ // Subcommand: codeyam editor verify-images '{"paths":["/","/drinks/1"]}'
2435
+ if (argv.step === 'verify-images') {
2436
+ await handleVerifyImages(argv.json || '');
2437
+ return;
2438
+ }
2439
+ // Subcommand: codeyam editor validate-seed '{"products":[...]}'
2440
+ if (argv.step === 'validate-seed') {
2441
+ await handleValidateSeed(argv.json || '');
2442
+ return;
2443
+ }
2444
+ // Subcommand: codeyam editor isolate StarRating CategoryBadge DrinkCard
2445
+ if (argv.step === 'isolate') {
2446
+ const names = [];
2447
+ if (argv.json)
2448
+ names.push(...argv.json.split(/[\s,]+/).filter(Boolean));
2449
+ // Collect any extra positional args (yargs puts them in argv._)
2450
+ const extras = argv._.filter((a) => typeof a === 'string' && a !== 'editor');
2451
+ names.push(...extras);
2452
+ handleIsolate(names);
2453
+ return;
2454
+ }
2455
+ // Subcommand: codeyam editor template — scaffold project from template
2456
+ if (argv.step === 'template') {
2457
+ await handleTemplate();
2458
+ return;
2459
+ }
2460
+ // Subcommand: codeyam editor sync — import scenarios from manifest
2461
+ if (argv.step === 'sync') {
2462
+ await handleSync();
2463
+ return;
2464
+ }
2465
+ // Subcommand: codeyam editor debug [--target ...]
2466
+ if (argv.step === 'debug') {
2467
+ if (!IS_INTERNAL_BUILD) {
2468
+ console.error(chalk.red('Error: "codeyam editor debug" is internal-only.'));
2469
+ process.exit(1);
2470
+ }
2471
+ await handleEditorDebug(argv);
2472
+ return;
2473
+ }
2474
+ // Subcommand: codeyam editor steps — show setup or cycle overview
2475
+ if (argv.step === 'steps') {
979
2476
  if (!hasProject(root)) {
980
2477
  printSetup(root);
981
2478
  }
982
2479
  else {
983
2480
  const state = readState(root);
2481
+ // Clear prompt file when feature is done (step 13) so the hook
2482
+ // can capture the next feature request from the user.
2483
+ if (state?.step === 13) {
2484
+ clearEditorUserPrompt(root);
2485
+ }
984
2486
  printCycleOverview(root, state);
985
2487
  }
986
2488
  return;
987
2489
  }
2490
+ const step = argv.step ? parseInt(argv.step, 10) : undefined;
2491
+ if (step != null && (isNaN(step) || step < 1 || step > 13)) {
2492
+ console.error(chalk.red(`Error: Invalid step "${argv.step}". Must be 1-13.`));
2493
+ process.exit(1);
2494
+ }
2495
+ if (step == null) {
2496
+ // No step specified: launch editor server + open browser
2497
+ // Auto-init if needed
2498
+ let projectRoot = getStateProjectRoot();
2499
+ if (!projectRoot) {
2500
+ await initCommand.handler({
2501
+ force: false,
2502
+ autoInit: true,
2503
+ $0: '',
2504
+ _: [],
2505
+ });
2506
+ projectRoot = getStateProjectRoot();
2507
+ if (!projectRoot) {
2508
+ return;
2509
+ }
2510
+ }
2511
+ const configPath = path.join(projectRoot, '.codeyam', 'config.json');
2512
+ try {
2513
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
2514
+ const { projectSlug } = config;
2515
+ if (!projectSlug) {
2516
+ errorLog('Missing project slug. Try reinitializing with: codeyam init --force');
2517
+ return;
2518
+ }
2519
+ const connectionOk = await withoutSpinner(() => testEnvironment());
2520
+ if (!connectionOk) {
2521
+ errorLog('Environment validation failed');
2522
+ return;
2523
+ }
2524
+ const { project, branch } = await requireBranchAndProject(projectSlug);
2525
+ // Auto-sync scenarios from manifest if it exists
2526
+ const manifestData = readManifest(projectRoot);
2527
+ if (manifestData && manifestData.scenarios.length > 0) {
2528
+ try {
2529
+ const { getDatabase } = await import('../../../packages/database/index.js');
2530
+ const db = getDatabase();
2531
+ const existingRows = await db
2532
+ .selectFrom('editor_scenarios')
2533
+ .select(['id', 'updated_at'])
2534
+ .where('project_id', '=', project.id)
2535
+ .execute();
2536
+ const syncResult = await syncManifestToDatabase(projectRoot, project.id, existingRows, async (row) => {
2537
+ await db
2538
+ .insertInto('editor_scenarios')
2539
+ .values(row)
2540
+ .execute();
2541
+ }, async (id, row) => {
2542
+ await db
2543
+ .updateTable('editor_scenarios')
2544
+ .set(row)
2545
+ .where('id', '=', id)
2546
+ .execute();
2547
+ });
2548
+ if (syncResult.inserted > 0 || syncResult.updated > 0) {
2549
+ const parts = [];
2550
+ if (syncResult.inserted > 0)
2551
+ parts.push(`${syncResult.inserted} imported`);
2552
+ if (syncResult.updated > 0)
2553
+ parts.push(`${syncResult.updated} updated`);
2554
+ console.log(chalk.green(` Synced scenarios from manifest: ${parts.join(', ')}`));
2555
+ }
2556
+ }
2557
+ catch {
2558
+ // Non-fatal — sync failure shouldn't block editor startup
2559
+ }
2560
+ }
2561
+ // `codeyam editor` (no step) always implies editor mode.
2562
+ // The empty-folder heuristic is no longer needed here — running this
2563
+ // command IS the signal. We still detect empty folders so that
2564
+ // the very first `codeyam editor` in a bare directory works before
2565
+ // metadata has been persisted.
2566
+ const editorMode = true;
2567
+ if (editorMode && !project.metadata?.labs?.simulations) {
2568
+ try {
2569
+ await updateProjectMetadata({
2570
+ projectSlug,
2571
+ metadataUpdate: {
2572
+ editorMode: true,
2573
+ labs: { simulations: true },
2574
+ },
2575
+ });
2576
+ const refreshed = await requireBranchAndProject(projectSlug);
2577
+ Object.assign(project, refreshed.project);
2578
+ }
2579
+ catch {
2580
+ // Non-fatal
2581
+ }
2582
+ }
2583
+ // Install skills/hooks
2584
+ const simulationsEnabled = project.metadata?.labs?.simulations ?? false;
2585
+ const skillMode = simulationsEnabled
2586
+ ? 'full'
2587
+ : editorMode
2588
+ ? 'editor'
2589
+ : 'memory';
2590
+ await installClaudeCodeSkills(projectRoot, {
2591
+ mode: skillMode,
2592
+ editorMode,
2593
+ });
2594
+ setupClaudeCodeSettings(projectRoot, {
2595
+ mode: skillMode,
2596
+ editorMode,
2597
+ });
2598
+ // Auto-finalize analyzer so codeyam analyze works
2599
+ if (editorMode && !isAnalyzerFinalized()) {
2600
+ try {
2601
+ const { execSync } = await import('child_process');
2602
+ const templatePath = getAnalyzerTemplatePath();
2603
+ if (fs.existsSync(templatePath)) {
2604
+ console.log(' Setting up simulations (first time only)...');
2605
+ execSync('npm install --include=dev', {
2606
+ cwd: templatePath,
2607
+ stdio: 'pipe',
2608
+ });
2609
+ execSync('npx playwright install chromium', {
2610
+ cwd: templatePath,
2611
+ stdio: 'pipe',
2612
+ });
2613
+ execSync('npm run build', {
2614
+ cwd: templatePath,
2615
+ stdio: 'pipe',
2616
+ });
2617
+ fs.writeFileSync(path.join(templatePath, '.finalized'), new Date().toISOString());
2618
+ }
2619
+ }
2620
+ catch {
2621
+ // Non-fatal
2622
+ }
2623
+ }
2624
+ // Start background server
2625
+ const { url } = await startBackgroundServer({
2626
+ port: argv.port || 3111,
2627
+ rootPath: projectRoot,
2628
+ project,
2629
+ branch,
2630
+ });
2631
+ console.log();
2632
+ console.log(` Dashboard: ${url}`);
2633
+ console.log(' Run "codeyam --help" for all commands');
2634
+ console.log(chalk.bold(' Run "codeyam editor" to launch the editor at any time'));
2635
+ console.log();
2636
+ // Open browser to /editor
2637
+ try {
2638
+ const { execSync } = await import('child_process');
2639
+ const openCommand = process.platform === 'darwin'
2640
+ ? 'open'
2641
+ : process.platform === 'win32'
2642
+ ? 'start ""'
2643
+ : 'xdg-open';
2644
+ execSync(`${openCommand} "${url}/editor"`, { stdio: 'ignore' });
2645
+ }
2646
+ catch {
2647
+ // Silently fail if open command doesn't work
2648
+ }
2649
+ }
2650
+ catch (err) {
2651
+ errorLog('Failed to start CodeYam editor server');
2652
+ errorLog(err);
2653
+ process.exit(1);
2654
+ }
2655
+ return;
2656
+ }
988
2657
  const state = readState(root);
989
2658
  switch (step) {
990
2659
  case 1: {
991
2660
  const feature = argv.feature || undefined;
992
- printStep1(root, feature);
2661
+ const appFormats = argv['app-formats']
2662
+ ? argv['app-formats'].split(',').map((f) => f.trim())
2663
+ : undefined;
2664
+ const techStackId = argv['tech-stack'] || undefined;
2665
+ const prompt = argv.prompt || undefined;
2666
+ printStep1(root, feature, { appFormats, techStackId }, prompt);
993
2667
  break;
994
2668
  }
995
2669
  case 2: {
@@ -1010,7 +2684,9 @@ const editorCommand = {
1010
2684
  case 8:
1011
2685
  case 9:
1012
2686
  case 10:
1013
- case 11: {
2687
+ case 11:
2688
+ case 12:
2689
+ case 13: {
1014
2690
  const feature = argv.feature || state?.feature;
1015
2691
  if (!feature) {
1016
2692
  console.error(chalk.red('Error: No feature in progress. Run codeyam editor 1 first.'));
@@ -1026,6 +2702,8 @@ const editorCommand = {
1026
2702
  9: printStep9,
1027
2703
  10: printStep10,
1028
2704
  11: printStep11,
2705
+ 12: printStep12,
2706
+ 13: printStep13,
1029
2707
  };
1030
2708
  stepFns[step](root, feature);
1031
2709
  break;