@codeyam/codeyam-cli 0.1.0-staging.b147f46 → 0.1.0-staging.b49802d

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 (217) hide show
  1. package/analyzer-template/.build-info.json +8 -8
  2. package/analyzer-template/log.txt +3 -3
  3. package/analyzer-template/package.json +2 -2
  4. package/analyzer-template/packages/aws/package.json +5 -5
  5. package/analyzer-template/packages/database/package.json +1 -1
  6. package/analyzer-template/packages/database/src/lib/loadAnalysis.ts +7 -1
  7. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.d.ts.map +1 -1
  8. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js +7 -1
  9. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js.map +1 -1
  10. package/analyzer-template/project/runMultiScenarioServer.ts +26 -3
  11. package/background/src/lib/virtualized/project/runMultiScenarioServer.js +23 -3
  12. package/background/src/lib/virtualized/project/runMultiScenarioServer.js.map +1 -1
  13. package/codeyam-cli/src/commands/__tests__/editor.designSystem.test.js +30 -0
  14. package/codeyam-cli/src/commands/__tests__/editor.designSystem.test.js.map +1 -0
  15. package/codeyam-cli/src/commands/__tests__/editor.statePersistence.test.js +55 -0
  16. package/codeyam-cli/src/commands/__tests__/editor.statePersistence.test.js.map +1 -0
  17. package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js +39 -3
  18. package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js.map +1 -1
  19. package/codeyam-cli/src/commands/editor.js +773 -117
  20. package/codeyam-cli/src/commands/editor.js.map +1 -1
  21. package/codeyam-cli/src/commands/init.js +20 -0
  22. package/codeyam-cli/src/commands/init.js.map +1 -1
  23. package/codeyam-cli/src/data/designSystems.js +27 -0
  24. package/codeyam-cli/src/data/designSystems.js.map +1 -0
  25. package/codeyam-cli/src/data/techStacks.js +1 -1
  26. package/codeyam-cli/src/utils/__tests__/devServerState.test.js +93 -1
  27. package/codeyam-cli/src/utils/__tests__/devServerState.test.js.map +1 -1
  28. package/codeyam-cli/src/utils/__tests__/editorApi.test.js +44 -0
  29. package/codeyam-cli/src/utils/__tests__/editorApi.test.js.map +1 -1
  30. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +146 -0
  31. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
  32. package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js +98 -1
  33. package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js.map +1 -1
  34. package/codeyam-cli/src/utils/__tests__/editorRoadmap.test.js +1108 -0
  35. package/codeyam-cli/src/utils/__tests__/editorRoadmap.test.js.map +1 -0
  36. package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js +120 -0
  37. package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js.map +1 -1
  38. package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js +84 -0
  39. package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js.map +1 -1
  40. package/codeyam-cli/src/utils/__tests__/envFile.test.js +125 -0
  41. package/codeyam-cli/src/utils/__tests__/envFile.test.js.map +1 -0
  42. package/codeyam-cli/src/utils/__tests__/handoffContext.test.js +500 -0
  43. package/codeyam-cli/src/utils/__tests__/handoffContext.test.js.map +1 -0
  44. package/codeyam-cli/src/utils/__tests__/testRunner.test.js +0 -1
  45. package/codeyam-cli/src/utils/__tests__/testRunner.test.js.map +1 -1
  46. package/codeyam-cli/src/utils/__tests__/webappDetection.test.js +6 -0
  47. package/codeyam-cli/src/utils/__tests__/webappDetection.test.js.map +1 -1
  48. package/codeyam-cli/src/utils/designSystemShowcase.js +810 -0
  49. package/codeyam-cli/src/utils/designSystemShowcase.js.map +1 -0
  50. package/codeyam-cli/src/utils/devServerState.js +32 -0
  51. package/codeyam-cli/src/utils/devServerState.js.map +1 -1
  52. package/codeyam-cli/src/utils/editorApi.js +16 -0
  53. package/codeyam-cli/src/utils/editorApi.js.map +1 -1
  54. package/codeyam-cli/src/utils/editorAudit.js +28 -6
  55. package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
  56. package/codeyam-cli/src/utils/editorRoadmap.js +574 -0
  57. package/codeyam-cli/src/utils/editorRoadmap.js.map +1 -0
  58. package/codeyam-cli/src/utils/editorScenarioSwitch.js +27 -12
  59. package/codeyam-cli/src/utils/editorScenarioSwitch.js.map +1 -1
  60. package/codeyam-cli/src/utils/editorScenarios.js +11 -0
  61. package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
  62. package/codeyam-cli/src/utils/editorSeedAdapter.js +27 -14
  63. package/codeyam-cli/src/utils/editorSeedAdapter.js.map +1 -1
  64. package/codeyam-cli/src/utils/entityChangeStatus.server.js +15 -0
  65. package/codeyam-cli/src/utils/entityChangeStatus.server.js.map +1 -1
  66. package/codeyam-cli/src/utils/envFile.js +90 -0
  67. package/codeyam-cli/src/utils/envFile.js.map +1 -0
  68. package/codeyam-cli/src/utils/handoffContext.js +257 -0
  69. package/codeyam-cli/src/utils/handoffContext.js.map +1 -0
  70. package/codeyam-cli/src/utils/install-skills.js +36 -6
  71. package/codeyam-cli/src/utils/install-skills.js.map +1 -1
  72. package/codeyam-cli/src/utils/queue/__tests__/job.interactiveStart.test.js +159 -0
  73. package/codeyam-cli/src/utils/queue/__tests__/job.interactiveStart.test.js.map +1 -0
  74. package/codeyam-cli/src/utils/queue/job.js +9 -1
  75. package/codeyam-cli/src/utils/queue/job.js.map +1 -1
  76. package/codeyam-cli/src/utils/scenariosManifest.js +8 -2
  77. package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -1
  78. package/codeyam-cli/src/utils/techStackConfig.js +38 -0
  79. package/codeyam-cli/src/utils/techStackConfig.js.map +1 -0
  80. package/codeyam-cli/src/utils/techStackConfig.test.js +85 -0
  81. package/codeyam-cli/src/utils/techStackConfig.test.js.map +1 -0
  82. package/codeyam-cli/src/utils/testResultCache.js +53 -0
  83. package/codeyam-cli/src/utils/testResultCache.js.map +1 -0
  84. package/codeyam-cli/src/utils/testResultCache.server.js +81 -0
  85. package/codeyam-cli/src/utils/testResultCache.server.js.map +1 -0
  86. package/codeyam-cli/src/utils/testResultCache.server.test.js +187 -0
  87. package/codeyam-cli/src/utils/testResultCache.server.test.js.map +1 -0
  88. package/codeyam-cli/src/utils/testResultCache.test.js +230 -0
  89. package/codeyam-cli/src/utils/testResultCache.test.js.map +1 -0
  90. package/codeyam-cli/src/utils/testRunner.js +1 -7
  91. package/codeyam-cli/src/utils/testRunner.js.map +1 -1
  92. package/codeyam-cli/src/utils/webappDetection.js +4 -2
  93. package/codeyam-cli/src/utils/webappDetection.js.map +1 -1
  94. package/codeyam-cli/src/webserver/__tests__/api.interactive-switch-scenario.test.js +99 -0
  95. package/codeyam-cli/src/webserver/__tests__/api.interactive-switch-scenario.test.js.map +1 -0
  96. package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js +119 -1
  97. package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js.map +1 -1
  98. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js +354 -1
  99. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js.map +1 -1
  100. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js +32 -0
  101. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js.map +1 -1
  102. package/codeyam-cli/src/webserver/app/lib/database.js.map +1 -1
  103. package/codeyam-cli/src/webserver/app/routes/api.interactive-switch-scenario.js +34 -0
  104. package/codeyam-cli/src/webserver/app/routes/api.interactive-switch-scenario.js.map +1 -0
  105. package/codeyam-cli/src/webserver/build/client/assets/{CopyButton-CLe80MMu.js → CopyButton-DTBZZfSk.js} +1 -1
  106. package/codeyam-cli/src/webserver/build/client/assets/{EntityItem-Crt_KN_U.js → EntityItem-BxclONWq.js} +1 -1
  107. package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeIcon-CD7lGABo.js → EntityTypeIcon-BsnEOJZ_.js} +1 -1
  108. package/codeyam-cli/src/webserver/build/client/assets/{InlineSpinner-CgTNOhnu.js → InlineSpinner-ByaELMbv.js} +1 -1
  109. package/codeyam-cli/src/webserver/build/client/assets/{InteractivePreview-CKeQT5Ty.js → InteractivePreview-6WjVfhxX.js} +2 -2
  110. package/codeyam-cli/src/webserver/build/client/assets/{LibraryFunctionPreview-D3s1MFkb.js → LibraryFunctionPreview-ChX-Hp7W.js} +1 -1
  111. package/codeyam-cli/src/webserver/build/client/assets/{LogViewer-CM5zg40N.js → LogViewer-C-9zQdXg.js} +1 -1
  112. package/codeyam-cli/src/webserver/build/client/assets/{MiniClaudeChat-CQENLSrF.js → MiniClaudeChat-Bs2_Oua4.js} +2 -2
  113. package/codeyam-cli/src/webserver/build/client/assets/{ReportIssueModal-C2PLkej3.js → ReportIssueModal-DQsceHVv.js} +1 -1
  114. package/codeyam-cli/src/webserver/build/client/assets/{SafeScreenshot-DanvyBPb.js → SafeScreenshot-DThcm_9M.js} +1 -1
  115. package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-DUMfcNVK.js → ScenarioViewer-Cl4oOA3A.js} +1 -1
  116. package/codeyam-cli/src/webserver/build/client/assets/Spinner-CIil5-gb.js +34 -0
  117. package/codeyam-cli/src/webserver/build/client/assets/{ViewportInspectBar-BA_Ry-rs.js → ViewportInspectBar-BqkA9zyZ.js} +1 -1
  118. package/codeyam-cli/src/webserver/build/client/assets/{_index-BAWd-Xjf.js → _index-DnOgyseQ.js} +1 -1
  119. package/codeyam-cli/src/webserver/build/client/assets/{activity.(_tab)-BOARiB-g.js → activity.(_tab)-DqM9hbNE.js} +1 -1
  120. package/codeyam-cli/src/webserver/build/client/assets/{addon-web-links-CHx25PAe.js → addon-web-links-C58dYPwR.js} +1 -1
  121. package/codeyam-cli/src/webserver/build/client/assets/{agent-transcripts-Bg3e7q4S.js → agent-transcripts-B8NCeOrm.js} +1 -1
  122. package/codeyam-cli/src/webserver/build/client/assets/api.editor-database-verify-l0sNRNKZ.js +1 -0
  123. package/codeyam-cli/src/webserver/build/client/assets/api.editor-github-verify-l0sNRNKZ.js +1 -0
  124. package/codeyam-cli/src/webserver/build/client/assets/api.editor-handoff-l0sNRNKZ.js +1 -0
  125. package/codeyam-cli/src/webserver/build/client/assets/api.editor-hosting-verify-l0sNRNKZ.js +1 -0
  126. package/codeyam-cli/src/webserver/build/client/assets/api.editor-roadmap-l0sNRNKZ.js +1 -0
  127. package/codeyam-cli/src/webserver/build/client/assets/api.editor-verify-routes-l0sNRNKZ.js +1 -0
  128. package/codeyam-cli/src/webserver/build/client/assets/api.interactive-switch-scenario-l0sNRNKZ.js +1 -0
  129. package/codeyam-cli/src/webserver/build/client/assets/{book-open-CL-lMgHh.js → book-open-BFSIqZgO.js} +1 -1
  130. package/codeyam-cli/src/webserver/build/client/assets/{chevron-down-GmAjGS9-.js → chevron-down-B9fDzFVh.js} +1 -1
  131. package/codeyam-cli/src/webserver/build/client/assets/chunk-UVKPFVEO-Bmq2apuh.js +43 -0
  132. package/codeyam-cli/src/webserver/build/client/assets/{circle-check-DFcQkN5j.js → circle-check-DLPObLUx.js} +1 -1
  133. package/codeyam-cli/src/webserver/build/client/assets/{copy-C6iF61Xs.js → copy-DXEmO0TD.js} +1 -1
  134. package/codeyam-cli/src/webserver/build/client/assets/{createLucideIcon-4ImjHTVC.js → createLucideIcon-BwyFiRot.js} +1 -1
  135. package/codeyam-cli/src/webserver/build/client/assets/{dev.empty-C8y4mmyv.js → dev.empty-iRhRIFlp.js} +1 -1
  136. package/codeyam-cli/src/webserver/build/client/assets/editor._tab-BZPBzV73.js +1 -0
  137. package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-DhtVC4aI.js +161 -0
  138. package/codeyam-cli/src/webserver/build/client/assets/{editorPreview-CluPkvXJ.js → editorPreview-C6fEYHrh.js} +6 -6
  139. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-ByHz6rAQ.js → entity._sha._-pc-vc6wO.js} +1 -1
  140. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.dev-CmLO432x.js → entity._sha.scenarios._scenarioId.dev-C8AyYgYT.js} +1 -1
  141. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.fullscreen-Bz9sCUF_.js → entity._sha.scenarios._scenarioId.fullscreen-DziaVQX1.js} +1 -1
  142. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.create-scenario-DQM8E7L4.js → entity._sha_.create-scenario-BTcpgIpC.js} +1 -1
  143. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-CAoXLsQr.js → entity._sha_.edit._scenarioId-D_O_ajfZ.js} +1 -1
  144. package/codeyam-cli/src/webserver/build/client/assets/{entry.client-SuW9syRS.js → entry.client-j1Vi0bco.js} +6 -6
  145. package/codeyam-cli/src/webserver/build/client/assets/{files-D-xGrg29.js → files-kuny2Q_s.js} +1 -1
  146. package/codeyam-cli/src/webserver/build/client/assets/{git-Bq_fbXP5.js → git-DgCZPMie.js} +1 -1
  147. package/codeyam-cli/src/webserver/build/client/assets/globals-L-aUIeux.css +1 -0
  148. package/codeyam-cli/src/webserver/build/client/assets/{index-Bp1l4hSv.js → index-BliGSSpl.js} +1 -1
  149. package/codeyam-cli/src/webserver/build/client/assets/{index-DE3jI_dv.js → index-SqjQKTdH.js} +1 -1
  150. package/codeyam-cli/src/webserver/build/client/assets/{index-CWV9XZiG.js → index-vyrZD2g4.js} +1 -1
  151. package/codeyam-cli/src/webserver/build/client/assets/{labs-B_IX45ih.js → labs-c3yLxSEp.js} +1 -1
  152. package/codeyam-cli/src/webserver/build/client/assets/{loader-circle-De-7qQ2u.js → loader-circle-D-q28GLF.js} +1 -1
  153. package/codeyam-cli/src/webserver/build/client/assets/manifest-79d0d81a.js +1 -0
  154. package/codeyam-cli/src/webserver/build/client/assets/{memory-Cx2xEx7s.js → memory-CEWIUC4t.js} +1 -1
  155. package/codeyam-cli/src/webserver/build/client/assets/{pause-CFxEKL1u.js → pause-BP6fitdh.js} +1 -1
  156. package/codeyam-cli/src/webserver/build/client/assets/{root-D2_tktnk.js → root-L2V0jea7.js} +4 -4
  157. package/codeyam-cli/src/webserver/build/client/assets/{search-BdBb5aqc.js → search-BooqacKS.js} +1 -1
  158. package/codeyam-cli/src/webserver/build/client/assets/{settings-DdE-Untf.js → settings-BM0nbryO.js} +1 -1
  159. package/codeyam-cli/src/webserver/build/client/assets/{simulations-DSCdE99u.js → simulations-ovy6FjRY.js} +1 -1
  160. package/codeyam-cli/src/webserver/build/client/assets/{terminal-CrplD4b1.js → terminal-DHemCJIs.js} +1 -1
  161. package/codeyam-cli/src/webserver/build/client/assets/{triangle-alert-DqJ0j69l.js → triangle-alert-D87ekDl8.js} +1 -1
  162. package/codeyam-cli/src/webserver/build/client/assets/{useCustomSizes-DhXHbEjP.js → useCustomSizes-Dk0Tciqg.js} +1 -1
  163. package/codeyam-cli/src/webserver/build/client/assets/useLastLogLine-C8QvIe05.js +2 -0
  164. package/codeyam-cli/src/webserver/build/client/assets/{useReportContext-Cy5Qg_UR.js → useReportContext-jkCytuYz.js} +1 -1
  165. package/codeyam-cli/src/webserver/build/client/assets/{useToast-5HR2j9ZE.js → useToast-BgqkixU9.js} +1 -1
  166. package/codeyam-cli/src/webserver/build/server/assets/{analysisRunner-By5slFjw.js → analysisRunner-QgInFGdU.js} +1 -1
  167. package/codeyam-cli/src/webserver/build/server/assets/{index-DXaOwBnm.js → index-zblh9auj.js} +1 -1
  168. package/codeyam-cli/src/webserver/build/server/assets/init-DaE0CBjk.js +14 -0
  169. package/codeyam-cli/src/webserver/build/server/assets/server-build-CNvgz1cC.js +853 -0
  170. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  171. package/codeyam-cli/src/webserver/build-info.json +5 -5
  172. package/codeyam-cli/src/webserver/editorProxy.js +255 -20
  173. package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
  174. package/codeyam-cli/src/webserver/idleDetector.js +12 -3
  175. package/codeyam-cli/src/webserver/idleDetector.js.map +1 -1
  176. package/codeyam-cli/src/webserver/server.js +67 -0
  177. package/codeyam-cli/src/webserver/server.js.map +1 -1
  178. package/codeyam-cli/src/webserver/terminalServer.js +106 -14
  179. package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
  180. package/codeyam-cli/templates/codeyam-editor-codex.md +61 -0
  181. package/codeyam-cli/templates/codeyam-editor-gemini.md +59 -0
  182. package/codeyam-cli/templates/design-systems/clean-dashboard-design-system.md +255 -0
  183. package/codeyam-cli/templates/design-systems/editorial-design-system.md +267 -0
  184. package/codeyam-cli/templates/design-systems/mono-brutalist-design-system.md +256 -0
  185. package/codeyam-cli/templates/design-systems/neo-brutalist-design-system.md +294 -0
  186. package/codeyam-cli/templates/editor-step-hook.py +4 -4
  187. package/codeyam-cli/templates/expo-react-native/MOBILE_SETUP.md +204 -5
  188. package/codeyam-cli/templates/expo-react-native/__tests__/.gitkeep +0 -0
  189. package/codeyam-cli/templates/expo-react-native/app/_layout.tsx +6 -3
  190. package/codeyam-cli/templates/expo-react-native/app/index.tsx +36 -0
  191. package/codeyam-cli/templates/expo-react-native/app.json +11 -0
  192. package/codeyam-cli/templates/expo-react-native/babel.config.js +1 -0
  193. package/codeyam-cli/templates/expo-react-native/gitignore +2 -0
  194. package/codeyam-cli/templates/expo-react-native/global.css +7 -0
  195. package/codeyam-cli/templates/expo-react-native/lib/theme.ts +73 -0
  196. package/codeyam-cli/templates/expo-react-native/package.json +32 -16
  197. package/codeyam-cli/templates/expo-react-native/patches/expo-modules-autolinking+3.0.24.patch +29 -0
  198. package/codeyam-cli/templates/isolation-route/expo-router.tsx.template +54 -0
  199. package/codeyam-cli/templates/nextjs-prisma-sqlite/gitignore +1 -0
  200. package/codeyam-cli/templates/nextjs-prisma-sqlite/seed-adapter.ts +10 -5
  201. package/codeyam-cli/templates/seed-adapters/supabase.ts +271 -78
  202. package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +15 -0
  203. package/package.json +1 -1
  204. package/packages/database/src/lib/loadAnalysis.js +7 -1
  205. package/packages/database/src/lib/loadAnalysis.js.map +1 -1
  206. package/codeyam-cli/src/webserver/build/client/assets/Spinner-D0LgAaSa.js +0 -34
  207. package/codeyam-cli/src/webserver/build/client/assets/chunk-JZWAC4HX-BAdwhyCx.js +0 -43
  208. package/codeyam-cli/src/webserver/build/client/assets/editor._tab-Gbk_i5Js.js +0 -1
  209. package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-DMv5ESGo.js +0 -96
  210. package/codeyam-cli/src/webserver/build/client/assets/globals-oyPmV37k.css +0 -1
  211. package/codeyam-cli/src/webserver/build/client/assets/manifest-1a45e154.js +0 -1
  212. package/codeyam-cli/src/webserver/build/client/assets/useLastLogLine-BNd5hYuW.js +0 -2
  213. package/codeyam-cli/src/webserver/build/server/assets/init-CLG1LjQM.js +0 -10
  214. package/codeyam-cli/src/webserver/build/server/assets/server-build-NZmUqQv6.js +0 -688
  215. package/codeyam-cli/templates/expo-react-native/app/(tabs)/_layout.tsx +0 -33
  216. package/codeyam-cli/templates/expo-react-native/app/(tabs)/index.tsx +0 -12
  217. package/codeyam-cli/templates/expo-react-native/app/(tabs)/settings.tsx +0 -12
@@ -13,6 +13,7 @@ import { installClaudeCodeSkills } from "../utils/install-skills.js";
13
13
  import { setupClaudeCodeSettings } from "../utils/setupClaudeCodeSettings.js";
14
14
  import { ensureAnalyzerFinalized, } from "../utils/analyzerFinalization.js";
15
15
  import { APP_FORMATS, TECH_STACKS } from "../data/techStacks.js";
16
+ import { DESIGN_SYSTEMS } from "../data/designSystems.js";
16
17
  import { getProjectRoot as getStateProjectRoot } from "../state.js";
17
18
  import initCommand from "./init.js";
18
19
  import { scanScenarioFiles, syncScenarioFilesToDatabase, backfillScenarioMetadata, migrateScenarioFormats, } from "../utils/scenariosManifest.js";
@@ -23,7 +24,9 @@ import { buildEditorApiRequest, callEditorApi, EDITOR_API_SUBCOMMANDS, } from ".
23
24
  import { parseRegisterArg } from "../utils/parseRegisterArg.js";
24
25
  import { classifyRegistrationResult } from "../utils/registerScenarioResult.js";
25
26
  import { sanitizeGlossaryEntries } from "../utils/editorLoaderHelpers.js";
27
+ import { updateHandoffProgress } from "../utils/handoffContext.js";
26
28
  import { readMigrationState, writeMigrationState, advanceToNextPage, completePage, getMigrationResumeInfo, } from "../utils/editorMigration.js";
29
+ import { getDisplayVersion } from "../utils/versionInfo.js";
27
30
  const __filename = fileURLToPath(import.meta.url);
28
31
  const __dirname = path.dirname(__filename);
29
32
  const STEP_LABELS = {
@@ -188,6 +191,181 @@ function getProjectDimensions(root) {
188
191
  return { defaultName: 'Desktop', names: [] };
189
192
  }
190
193
  }
194
+ function getTechStackContext(root) {
195
+ const state = readState(root);
196
+ const techStackId = state?.techStackId || '';
197
+ const stack = TECH_STACKS.find((s) => s.id === techStackId);
198
+ const isExpo = techStackId === 'expo-react-native';
199
+ const isChromeExt = techStackId === 'chrome-extension-react';
200
+ const isNextjs = techStackId.startsWith('nextjs-') || (!isExpo && !isChromeExt);
201
+ return {
202
+ id: techStackId || 'nextjs-prisma-sqlite',
203
+ isExpo,
204
+ isNextjs,
205
+ isChromeExt,
206
+ hasDatabase: isNextjs,
207
+ testRunner: isNextjs ? 'vitest' : 'jest',
208
+ testRunCommand: isNextjs ? 'npx vitest run' : 'npx jest',
209
+ storageType: isExpo
210
+ ? 'asyncStorage'
211
+ : isChromeExt
212
+ ? 'chromeStorage'
213
+ : 'prisma',
214
+ routerImport: isExpo
215
+ ? 'expo-router'
216
+ : isChromeExt
217
+ ? 'react-router-dom'
218
+ : 'next/navigation',
219
+ componentPrimitives: isExpo
220
+ ? '<View>, <Text>, <ScrollView>'
221
+ : '<div>, <span>, <h1>',
222
+ rawPrimitivesList: isExpo
223
+ ? '<View>, <Text>, <Image>, <ScrollView>, <FlatList>'
224
+ : '<div>, <span>, <h1>, <p>, <img>, <ul>',
225
+ patternsFile: isExpo
226
+ ? 'MOBILE_SETUP.md'
227
+ : isChromeExt
228
+ ? 'EXTENSION_SETUP.md'
229
+ : 'FEATURE_PATTERNS.md',
230
+ };
231
+ }
232
+ /**
233
+ * Returns a pre-populated tech stack for a given template ID.
234
+ * Written to .codeyam/config.json during `codeyam editor template`.
235
+ */
236
+ function getTechStackForTemplate(templateId) {
237
+ switch (templateId) {
238
+ case 'nextjs-prisma-sqlite':
239
+ return {
240
+ languages: [
241
+ {
242
+ name: 'TypeScript',
243
+ url: 'https://typescriptlang.org',
244
+ description: 'Statically typed JavaScript superset',
245
+ version: '5',
246
+ },
247
+ ],
248
+ frameworks: [
249
+ {
250
+ name: 'Next.js',
251
+ url: 'https://nextjs.org',
252
+ description: 'Full-stack React framework with App Router, SSR, and API routes',
253
+ version: '15',
254
+ },
255
+ {
256
+ name: 'React',
257
+ url: 'https://react.dev',
258
+ description: 'Component-based UI library',
259
+ version: '19',
260
+ },
261
+ ],
262
+ databases: [
263
+ {
264
+ name: 'SQLite',
265
+ url: 'https://sqlite.org',
266
+ description: 'Embedded relational database — zero config, upgradeable to hosted DB',
267
+ },
268
+ ],
269
+ libraries: [
270
+ {
271
+ name: 'Prisma',
272
+ url: 'https://prisma.io',
273
+ description: 'Type-safe database ORM and query builder',
274
+ version: '7',
275
+ },
276
+ {
277
+ name: 'Tailwind CSS',
278
+ url: 'https://tailwindcss.com',
279
+ description: 'Utility-first CSS framework',
280
+ version: '4',
281
+ },
282
+ ],
283
+ };
284
+ case 'chrome-extension-react':
285
+ return {
286
+ languages: [
287
+ {
288
+ name: 'TypeScript',
289
+ url: 'https://typescriptlang.org',
290
+ description: 'Statically typed JavaScript superset',
291
+ version: '5',
292
+ },
293
+ ],
294
+ frameworks: [
295
+ {
296
+ name: 'React',
297
+ url: 'https://react.dev',
298
+ description: 'Component-based UI library for the popup and options pages',
299
+ version: '19',
300
+ },
301
+ {
302
+ name: 'Vite',
303
+ url: 'https://vite.dev',
304
+ description: 'Fast build tool and dev server',
305
+ version: '6',
306
+ },
307
+ ],
308
+ libraries: [
309
+ {
310
+ name: 'Tailwind CSS',
311
+ url: 'https://tailwindcss.com',
312
+ description: 'Utility-first CSS framework',
313
+ version: '4',
314
+ },
315
+ ],
316
+ infrastructure: [
317
+ {
318
+ name: 'Chrome Manifest V3',
319
+ url: 'https://developer.chrome.com/docs/extensions/develop/migrate/what-is-mv3',
320
+ description: 'Extension platform with service workers, declarative APIs, and chrome.storage',
321
+ },
322
+ ],
323
+ };
324
+ case 'expo-react-native':
325
+ return {
326
+ languages: [
327
+ {
328
+ name: 'TypeScript',
329
+ url: 'https://typescriptlang.org',
330
+ description: 'Statically typed JavaScript superset',
331
+ version: '5',
332
+ },
333
+ ],
334
+ frameworks: [
335
+ {
336
+ name: 'Expo',
337
+ url: 'https://expo.dev',
338
+ description: 'React Native development platform with managed workflow and OTA updates',
339
+ version: 'SDK 54',
340
+ },
341
+ {
342
+ name: 'React Native',
343
+ url: 'https://reactnative.dev',
344
+ description: 'Cross-platform mobile UI framework',
345
+ },
346
+ {
347
+ name: 'Expo Router',
348
+ url: 'https://docs.expo.dev/router/introduction/',
349
+ description: 'File-based navigation for React Native apps',
350
+ },
351
+ ],
352
+ libraries: [
353
+ {
354
+ name: 'NativeWind',
355
+ url: 'https://www.nativewind.dev',
356
+ description: 'Tailwind CSS for React Native',
357
+ },
358
+ {
359
+ name: 'AsyncStorage',
360
+ url: 'https://react-native-async-storage.github.io/async-storage/',
361
+ description: 'Persistent key-value storage for React Native',
362
+ },
363
+ ],
364
+ };
365
+ default:
366
+ return {};
367
+ }
368
+ }
191
369
  /**
192
370
  * Print dimension guidance when the project has multiple screen sizes.
193
371
  * Tells Claude to pick the right dimension for the content being previewed
@@ -211,11 +389,63 @@ function checkbox(text) {
211
389
  const highlighted = text.replace(/`([^`]+)`/g, (_m, code) => chalk.cyan(code));
212
390
  console.log(` ${chalk.dim('[ ]')} ${highlighted}`);
213
391
  }
392
+ /**
393
+ * Print a checklist item for the handoff summary.
394
+ */
395
+ function checkboxHandoff() {
396
+ checkbox('Update the handoff summary in `.codeyam/config.json` for the next session/AI: `codeyam editor handoff \'{"summary":"..."}\'`');
397
+ }
398
+ /**
399
+ * Print the handoff context from a previous session or AI provider.
400
+ * Prefers the auto-generated handoff-context.md over the raw config summary.
401
+ */
402
+ function printHandoffContext(root) {
403
+ try {
404
+ // Check for auto-generated handoff context (richer, includes progress timeline)
405
+ const handoffContextPath = path.join(root, '.codeyam', 'handoff-context.md');
406
+ if (fs.existsSync(handoffContextPath)) {
407
+ const content = fs.readFileSync(handoffContextPath, 'utf8');
408
+ console.log();
409
+ console.log(chalk.bold.magenta('━━━ PROVIDER HANDOFF CONTEXT ━━━'));
410
+ console.log(chalk.magenta(content));
411
+ console.log(chalk.bold.magenta('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
412
+ console.log();
413
+ return;
414
+ }
415
+ // Fall back to config.json handoff summary
416
+ const configPath = path.join(root, '.codeyam', 'config.json');
417
+ if (!fs.existsSync(configPath))
418
+ return;
419
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
420
+ const handoff = config.handoff;
421
+ if (!handoff || !handoff.summary)
422
+ return;
423
+ console.log();
424
+ console.log(chalk.bold.magenta(`━━━ HANDOFF FROM ${String(handoff.lastProvider || 'unknown').toUpperCase()} (Step ${handoff.lastStep || 'unknown'}) ━━━`));
425
+ console.log(chalk.magenta(handoff.summary));
426
+ if (handoff.lastUpdated) {
427
+ console.log(chalk.dim(` Updated: ${handoff.lastUpdated}`));
428
+ }
429
+ console.log(chalk.bold.magenta('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
430
+ console.log();
431
+ }
432
+ catch {
433
+ // Non-fatal
434
+ }
435
+ }
214
436
  /**
215
437
  * Instructions for creating/updating .codeyam/data-structure.json.
216
438
  * Only prints creation instructions if the file doesn't exist yet.
217
439
  * If it exists, reminds Claude to update it if data models changed.
218
440
  */
441
+ function printServiceRecordingReminder(port) {
442
+ console.log(chalk.bold('Third-party services:'));
443
+ checkbox('When integrating any external service (auth, email, payments, storage, telemetry), update the tech stack');
444
+ console.log(chalk.dim(' Include the service name, URL, description, and envKeys (environment variable names it requires).'));
445
+ console.log(chalk.dim(` curl -s -X POST http://localhost:${port}/api/editor-project-info -H "Content-Type: application/json" \\`));
446
+ console.log(chalk.dim(` -d '{"techStack": {"services": [{"name":"Stripe","url":"https://stripe.com","description":"Payments","envKeys":["STRIPE_SECRET_KEY","STRIPE_PUBLISHABLE_KEY"]}]}}'`));
447
+ console.log(chalk.dim(' This ensures the deploy roadmap tracks all services that need production credentials.'));
448
+ }
219
449
  function printDataStructureInstructions() {
220
450
  const root = getProjectRoot();
221
451
  const dsPath = path.join(root, '.codeyam', 'data-structure.json');
@@ -266,6 +496,10 @@ function printAppScenarioInstructions(pageName, route) {
266
496
  console.log(chalk.dim(' Example: "Page - Full Data", "Page - Empty State", "Page - Error State"'));
267
497
  }
268
498
  console.log();
499
+ checkbox('Ensure every page file used in app scenarios has a glossary entry with its filePath:');
500
+ console.log(chalk.dim(' The register command validates pageFilePath against the glossary — add missing pages now.'));
501
+ console.log(chalk.dim(' Example: {"name":"CounterScreen","filePath":"app/(tabs)/index.tsx","type":"page","description":"Main counter page"}'));
502
+ console.log();
269
503
  checkbox('Register each scenario with type "application", the real page URL, and pageFilePath:');
270
504
  console.log(chalk.dim(` codeyam editor register '{"name":"${pageName || 'Page'} - Full Data",`));
271
505
  console.log(chalk.dim(` "type":"application","url":"${route || '/'}","pageFilePath":"src/path/to/Page.tsx",...}'`));
@@ -280,7 +514,12 @@ function printAppScenarioInstructions(pageName, route) {
280
514
  console.log(chalk.dim(' For external APIs: also add "externalApis":{"GET https://...":{"body":[...],"status":200}}'));
281
515
  }
282
516
  else {
517
+ const appCtx = getTechStackContext(root);
283
518
  checkbox('Include data in every app scenario — without it the page will be empty:');
519
+ if (appCtx.isExpo) {
520
+ console.log(chalk.dim(' Use "localStorage":{"items":"[...]"} to pre-populate AsyncStorage (values are JSON strings)'));
521
+ console.log(chalk.dim(" AsyncStorage uses localStorage on web — CodeYam's injection works automatically."));
522
+ }
284
523
  console.log(chalk.dim(' Use "mockData":{"routes":{"/api/...":{"body":[...]}}} to mock API responses'));
285
524
  console.log(chalk.dim(' For external APIs: add "externalApis":{"GET https://...":{"body":[...],"status":200}}'));
286
525
  }
@@ -328,6 +567,12 @@ function printExtractionPlanInstructions() {
328
567
  console.log(chalk.yellow(' Every component that renders multiple distinct sections should be split into sub-components.'));
329
568
  console.log(chalk.yellow(' If a component has N visually distinct parts, it should compose N sub-components.'));
330
569
  console.log();
570
+ console.log(chalk.bold.red('THE DECOMPOSITION RULE: All code should do one thing.'));
571
+ console.log(chalk.yellow(' Code either contains logic OR brings together smaller pieces to form a coordinated whole.'));
572
+ console.log(chalk.yellow(' Every opportunity to extract code into a sensible function, helper, or sub-component MUST be taken.'));
573
+ console.log(chalk.yellow(' Then extract AGAIN from the extracted code — keep going until each piece does one clearly defined thing.'));
574
+ console.log(chalk.yellow(' This applies to ALL code: backend routes, business logic, frontend components, utilities — everything.'));
575
+ console.log();
331
576
  console.log(chalk.bold('Checklist:'));
332
577
  checkbox('Read `.codeyam/glossary.json` — note reusable functions/components');
333
578
  checkbox('Read EVERY file used by this page/feature');
@@ -338,6 +583,12 @@ function printExtractionPlanInstructions() {
338
583
  console.log(chalk.yellow(' Functions: data transforms, calculations, formatting, validation,'));
339
584
  console.log(chalk.yellow(' API response shaping, any logic that is not directly about rendering'));
340
585
  console.log(chalk.yellow(' Hooks: data fetching, state management, side effects'));
586
+ console.log(chalk.yellow(' Inline logic — backend AND frontend (MUST be extracted as named functions):'));
587
+ console.log(chalk.yellow(' conditionals/ternaries, computed/derived values, data transformations,'));
588
+ console.log(chalk.yellow(' string formatting/interpolation, array filtering/sorting/mapping callbacks,'));
589
+ console.log(chalk.yellow(' default value logic, null/undefined guards, date/number formatting,'));
590
+ console.log(chalk.yellow(' permission/role checks, status derivation (e.g. isOverdue(task)),'));
591
+ console.log(chalk.yellow(' request/response shaping, error message construction, config lookups'));
341
592
  console.log();
342
593
  checkbox('Write a numbered extraction plan listing EVERYTHING you will extract');
343
594
  console.log(chalk.yellow(' The end state: every page file is ONLY imports + component composition.'));
@@ -348,6 +599,15 @@ function printExtractionPlanInstructions() {
348
599
  console.log(chalk.yellow(' — What it is (component, function, hook)'));
349
600
  console.log(chalk.yellow(' — Where it currently lives (source file + approximate lines)'));
350
601
  console.log(chalk.yellow(' — Where it will go (new file path)'));
602
+ console.log(chalk.yellow(' — How you will test it (key inputs/outputs and edge cases)'));
603
+ console.log(chalk.yellow(' — What logic inside it can be further extracted as a separate testable function'));
604
+ console.log();
605
+ console.log(chalk.bold.red('BEFORE FINALIZING THE PLAN:'));
606
+ console.log(chalk.yellow(' Re-read every file ONE MORE TIME. For each piece of code, ask:'));
607
+ console.log(chalk.yellow(' 1. Does this do more than one thing? → Split it into pieces that each do one thing.'));
608
+ console.log(chalk.yellow(' 2. Is there logic here that could be its own function with clear inputs/outputs? → Extract it.'));
609
+ console.log(chalk.yellow(' 3. Can the extracted code itself be broken down further? → Keep extracting.'));
610
+ console.log(chalk.yellow(' If your plan has fewer functions than components, you probably missed extractable logic.'));
351
611
  console.log();
352
612
  console.log(chalk.dim('Present the numbered plan, then proceed to step 7 to execute it.'));
353
613
  }
@@ -355,10 +615,18 @@ function printExtractionPlanInstructions() {
355
615
  * Shared component capture instructions used by editor Step 7 and migration Steps 3/8.
356
616
  * Prints the full isolation route setup, codeyam-capture wrapper, register command, and error checking.
357
617
  */
358
- function printComponentCaptureInstructions() {
359
- checkbox('Create isolation route dirs: `codeyam editor isolate ComponentA ComponentB ...`');
360
- console.log(chalk.dim(' This creates app/isolated-components/layout.tsx (with production notFound() guard) and'));
361
- console.log(chalk.dim(' a directory per component. List ALL components that need isolation routes.'));
618
+ function printComponentCaptureInstructions(root) {
619
+ const ctx = root ? getTechStackContext(root) : undefined;
620
+ const isExpo = ctx?.isExpo ?? false;
621
+ checkbox('Set up isolation routes: `codeyam editor isolate ComponentA ComponentB ...`');
622
+ if (isExpo) {
623
+ console.log(chalk.dim(' This creates app/isolated-components/_layout.tsx (with __DEV__ guard).'));
624
+ console.log(chalk.dim(' You then create a flat .tsx file per component (NOT subdirectories — Expo Router uses flat files).'));
625
+ }
626
+ else {
627
+ console.log(chalk.dim(` This creates app/isolated-components/layout.tsx (with notFound() guard) and`));
628
+ console.log(chalk.dim(' a directory per component. List ALL components that need isolation routes.'));
629
+ }
362
630
  checkbox('For each visual component:');
363
631
  console.log(chalk.dim(' 1. Read the source AND find where it is used in the app to understand:'));
364
632
  console.log(chalk.dim(' — Props/interface'));
@@ -369,14 +637,28 @@ function printComponentCaptureInstructions() {
369
637
  console.log(chalk.dim(' — Different visual states: loading, error, disabled, selected, hover'));
370
638
  console.log(chalk.dim(' — Boundary values: single item vs many, min/max ratings, very long names'));
371
639
  console.log(chalk.dim(' 3. Create ONE isolation route per component with a scenarios map and ?s= query param:'));
372
- console.log(chalk.dim(' Remix: app/routes/isolated-components.ComponentName.tsx → /isolated-components/ComponentName'));
373
- console.log(chalk.dim(' Next.js: app/isolated-components/ComponentName/page.tsx → /isolated-components/ComponentName'));
640
+ if (isExpo) {
641
+ console.log(chalk.dim(' Expo: app/isolated-components/ComponentName.tsx → /isolated-components/ComponentName'));
642
+ console.log(chalk.dim(' Use useLocalSearchParams() from expo-router to read ?s=ScenarioName.'));
643
+ }
644
+ else {
645
+ console.log(chalk.dim(' Remix: app/routes/isolated-components.ComponentName.tsx → /isolated-components/ComponentName'));
646
+ console.log(chalk.dim(' Next.js: app/isolated-components/ComponentName/page.tsx → /isolated-components/ComponentName'));
647
+ }
374
648
  console.log(chalk.dim(' The route defines a `scenarios` object mapping scenario names to props,'));
375
649
  console.log(chalk.dim(' reads `?s=ScenarioName` from the URL, and renders the component with those props.'));
376
- console.log(chalk.dim(' Wrap the component in a capture container with id="codeyam-capture":'));
377
- console.log(chalk.dim(' <div id="codeyam-capture" style={{ display:"inline-block" }}>'));
378
- console.log(chalk.dim(' <div style={{ width:"100%", maxWidth:"..." }}> ← match the app\'s container width'));
379
- console.log(chalk.dim(' e.g. card in a 3-col grid → maxWidth:"24rem", full-width component → omit maxWidth'));
650
+ if (isExpo) {
651
+ console.log(chalk.dim(' Wrap the component in a capture container with nativeID="codeyam-capture":'));
652
+ console.log(chalk.dim(' <View nativeID="codeyam-capture" style={{ display:"flex" }}>'));
653
+ }
654
+ else {
655
+ console.log(chalk.dim(' Wrap the component in a capture container with id="codeyam-capture":'));
656
+ console.log(chalk.dim(' <div id="codeyam-capture" style={{ display:"inline-block" }}>'));
657
+ }
658
+ console.log(chalk.dim(isExpo
659
+ ? ' <View style={{ width:"100%", maxWidth:... }}> ← match the app\'s container width'
660
+ : ' <div style={{ width:"100%", maxWidth:"..." }}> ← match the app\'s container width'));
661
+ console.log(chalk.dim(' e.g. card in a 3-col grid → maxWidth: 384, full-width component → omit maxWidth'));
380
662
  console.log(chalk.dim(' The screenshot captures just this wrapper, so the component fills the image.'));
381
663
  console.log(chalk.dim(' Center the wrapper on the page (flexbox center both axes) and set a page background'));
382
664
  console.log(chalk.dim(' color that matches where the component normally appears (e.g. white for light UIs).'));
@@ -714,11 +996,17 @@ function printSetup(root) {
714
996
  console.log();
715
997
  // ── Design System ────────────────────────────────────────────────
716
998
  console.log(chalk.bold('Design System (ask FIRST):'));
717
- console.log(chalk.dim(' Ask: "Do you have a design system, brand guidelines, or style preferences you\'d like me to follow?"'));
999
+ console.log(chalk.dim(' Ask: "What visual style do you want? Pick a built-in design system or bring your own."'));
718
1000
  console.log(chalk.dim(' Use AskUserQuestion with these EXACT option labels:'));
719
- console.log(chalk.yellow(' Option 1 label: "Yes, I\'ll paste my design system"'));
1001
+ console.log();
1002
+ for (const ds of DESIGN_SYSTEMS) {
1003
+ console.log(chalk.yellow(` Option label: "${ds.name}"`) +
1004
+ chalk.dim(` — ${ds.description}`));
1005
+ console.log(chalk.dim(` → Run: codeyam editor design-system ${ds.id}`));
1006
+ }
1007
+ console.log(chalk.yellow(' Option label: "I\'ll paste my own design system"'));
720
1008
  console.log(chalk.dim(' → Wait for paste, save to .codeyam/design-system.md, confirm with brief summary'));
721
- console.log(chalk.yellow(' Option 2 label: "No, use sensible defaults"'));
1009
+ console.log(chalk.yellow(' Option label: "Skip use sensible defaults"'));
722
1010
  console.log(chalk.dim(' → Skip'));
723
1011
  console.log();
724
1012
  console.log(chalk.bold('Checklist:'));
@@ -739,8 +1027,8 @@ function printSetup(root) {
739
1027
  console.log(chalk.bold('Tech Stack Selection:'));
740
1028
  console.log(chalk.dim(' Based on the selected formats, present ONLY the matching tech stacks.'));
741
1029
  console.log(chalk.dim(' A stack matches if ANY of the user\'s selected formats appears in its "Supports" list.'));
742
- console.log(chalk.dim(' Show ALL matching stacks even if there is only one. Mark the recommended option.'));
743
- console.log(chalk.dim(' Use AskUserQuestion to let the user pick one.'));
1030
+ console.log(chalk.dim(' If only ONE stack matches, confirm it directly (e.g. "Expo + React Native is the only stack for mobile apps — using that.").'));
1031
+ console.log(chalk.dim(' If MULTIPLE stacks match, use AskUserQuestion to let the user pick one.'));
744
1032
  console.log();
745
1033
  console.log(chalk.bold(' Available Tech Stacks:'));
746
1034
  for (const stack of TECH_STACKS) {
@@ -773,7 +1061,7 @@ function printSetup(root) {
773
1061
  console.log(chalk.yellow(' ( ) Mobile') + chalk.dim(' — 375 × 667'));
774
1062
  console.log(chalk.yellow(' ( ) Custom') + chalk.dim(' — ask for width × height'));
775
1063
  console.log();
776
- console.log(chalk.dim(' Pre-select based on app format: mobile-responsive-web-app/desktop-app → Desktop, mobile-app → Mobile, chrome-extension → Custom (400×600).'));
1064
+ console.log(chalk.dim(' Pre-select based on app format: mobile-responsive-web-app/desktop-app → Desktop, mobile-app → iPhone 16 (393×852), chrome-extension → Custom (400×600).'));
777
1065
  console.log(chalk.dim(' If only one obvious choice, confirm it rather than asking.'));
778
1066
  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}}'`));
779
1067
  console.log();
@@ -932,6 +1220,15 @@ function printStep1(root, feature, options, userPrompt) {
932
1220
  console.log(chalk.dim(' -H "Content-Type: application/json" \\'));
933
1221
  console.log(chalk.dim(' -d \'{"projectTitle":"My App","projectDescription":"A short description of what this app does"}\''));
934
1222
  console.log();
1223
+ checkbox('Detect and set the project tech stack:');
1224
+ console.log(chalk.dim(' Scan package.json, framework configs (.env, .env.example, tsconfig.json, next.config.*, prisma/schema.prisma, etc.)'));
1225
+ console.log(chalk.dim(' to detect languages, frameworks, databases, services, libraries, and infrastructure.'));
1226
+ console.log(chalk.dim(' Each item MUST have name, url, and description. version and envKeys are optional.'));
1227
+ console.log(chalk.dim(` curl -s -X POST http://localhost:${port}/api/editor-project-info \\`));
1228
+ console.log(chalk.dim(' -H "Content-Type: application/json" \\'));
1229
+ console.log(chalk.dim(' -d \'{"techStack":{"languages":[{"name":"TypeScript","url":"https://typescriptlang.org","description":"Statically typed JavaScript superset","version":"5"}],"frameworks":[{"name":"Next.js","url":"https://nextjs.org","description":"Full-stack React framework with SSR and API routes","version":"15"}],"databases":[{"name":"SQLite","url":"https://sqlite.org","description":"Embedded relational database"}],"services":[{"name":"Stripe","url":"https://stripe.com","description":"Payment processing and billing","envKeys":["STRIPE_SECRET_KEY"]}],"libraries":[{"name":"Prisma","url":"https://prisma.io","description":"Type-safe database ORM","version":"7"}],"infrastructure":[]}}\''));
1230
+ console.log(chalk.dim(' Replace the example values with what you detect in this project. Omit empty categories.'));
1231
+ console.log();
935
1232
  const designSystem = readDesignSystem(root);
936
1233
  if (designSystem) {
937
1234
  console.log(chalk.bold.magenta('Design System (from .codeyam/design-system.md):'));
@@ -945,6 +1242,8 @@ function printStep1(root, feature, options, userPrompt) {
945
1242
  console.log(chalk.yellow(' Option 2 label: "I\'d like some changes"') +
946
1243
  chalk.dim(' — user describes changes, you revise the plan, then re-present'));
947
1244
  console.log();
1245
+ checkboxHandoff();
1246
+ console.log();
948
1247
  console.log(chalk.dim('This step is for understanding user goals and getting buy-in. Code comes in Step 2.'));
949
1248
  console.log();
950
1249
  console.log(chalk.bold.red('━━━ STOP ━━━'));
@@ -982,33 +1281,79 @@ function printStep2(root, feature) {
982
1281
  }
983
1282
  console.log('Get the project ready to build.');
984
1283
  console.log();
1284
+ const ctx = getTechStackContext(root);
985
1285
  // If no project exists yet, include scaffolding instructions first
986
1286
  if (!projectExists) {
987
1287
  console.log(chalk.bold('Scaffold the project:'));
988
1288
  checkbox('Run `codeyam editor template` to scaffold, install dependencies, init git, and configure CodeYam');
989
- console.log(chalk.dim(' This copies the Next.js + Prisma 7 + SQLite template, runs npm install,'));
1289
+ if (ctx.isExpo) {
1290
+ console.log(chalk.dim(' This copies the Expo + React Native template, runs npm install,'));
1291
+ }
1292
+ else if (ctx.isChromeExt) {
1293
+ console.log(chalk.dim(' This copies the Chrome Extension + React template, runs npm install,'));
1294
+ }
1295
+ else {
1296
+ console.log(chalk.dim(' This copies the Next.js + Prisma 7 + SQLite template, runs npm install,'));
1297
+ }
990
1298
  console.log(chalk.dim(' initializes git, runs codeyam init, and refreshes the editor — all in one command.'));
991
1299
  console.log();
992
- checkbox('Define your data models in `prisma/schema.prisma`');
993
- console.log(chalk.dim(" Replace the placeholder Todo model with your app's models"));
994
- console.log();
995
- checkbox('Push schema and seed the database');
996
- console.log(chalk.dim(' npm run db:push'));
997
- console.log(chalk.dim(' # Edit prisma/seed.ts with your seed data, then:'));
998
- console.log(chalk.dim(' npm run db:seed'));
999
- console.log(chalk.dim(` # After re-seeding, restart the dev server to pick up fresh data:`));
1000
- console.log(chalk.dim(` # codeyam editor dev-server '{"action":"restart"}'`));
1001
- console.log();
1002
- console.log(chalk.yellow(' IMPORTANT: When adding new required columns to existing tables,'));
1003
- console.log(chalk.yellow(' provide a @default(...) value so `db push` can fill existing rows.'));
1004
- console.log(chalk.dim(' Example: userId String @default("anonymous") existing rows get "anonymous"'));
1005
- console.log(chalk.dim(' Without a default, Prisma requires --force-reset which drops ALL data.'));
1006
- console.log(chalk.dim(' NEVER use --force-reset — it is blocked in this environment.'));
1007
- console.log();
1008
- console.log(chalk.dim(' See DATABASE.md for Prisma patterns and important warnings.'));
1009
- console.log(chalk.dim(' Key: import { prisma } from "@/app/lib/prisma" in API routes.'));
1010
- console.log(chalk.dim(' Key: Seed scripts must use the adapter pattern (see prisma/seed.ts).'));
1011
- console.log();
1300
+ if (ctx.isExpo) {
1301
+ // Expo: no database, use AsyncStorage + theme
1302
+ checkbox('Define your data types in `lib/types.ts`');
1303
+ console.log(chalk.dim(" Create TypeScript interfaces for your app's data models"));
1304
+ console.log();
1305
+ checkbox('Set up initial data using the storage helper in `lib/storage.ts`');
1306
+ console.log(chalk.dim(' import { storage } from "@/lib/storage";'));
1307
+ console.log(chalk.dim(' await storage.set("items", [{ id: "1", title: "First item" }]);'));
1308
+ console.log();
1309
+ console.log(chalk.dim(' Read MOBILE_SETUP.md for data storage, navigation, and testing patterns.'));
1310
+ console.log();
1311
+ checkbox('Ask the user what to name the app (use AskUserQuestion), then update app.json');
1312
+ console.log(chalk.dim(' Update name, slug, scheme, ios.bundleIdentifier (com.codeyam.<slug>), and android.package'));
1313
+ console.log(chalk.dim(' Also set the projectTitle via: curl -s -X POST http://localhost:' +
1314
+ port +
1315
+ '/api/editor-project-info -H "Content-Type: application/json" -d \'{"projectTitle":"<name>"}\''));
1316
+ console.log();
1317
+ checkbox('Generate an app icon using `sharp`');
1318
+ console.log(chalk.dim(' Create `scripts/generate-icon.mjs` and `mkdir -p assets`'));
1319
+ console.log(chalk.yellow(' ICON DESIGN GOAL: The icon must look distinctive on a home screen full of other app icons.'));
1320
+ console.log(chalk.yellow(' Use multiple bold colors from the design system and complex, overlapping shapes.'));
1321
+ console.log(chalk.yellow(' NO simple/minimal icons — no single glyph on a flat background, no plain text, no basic circles.'));
1322
+ console.log(chalk.yellow(' Make it colorful, layered, and dense. Use gradients, multiple contrasting fills, and depth.'));
1323
+ console.log(chalk.dim(' Use sharp to render SVG to 1024x1024 PNG. iOS: flatten onto background (no alpha channel).'));
1324
+ console.log(chalk.dim(' Generate: icon.png, adaptive-icon.png, favicon.png (48x48). Run `node scripts/generate-icon.mjs`'));
1325
+ console.log();
1326
+ }
1327
+ else if (ctx.isChromeExt) {
1328
+ // Chrome Extension: use chrome.storage
1329
+ checkbox('Set up data storage using chrome.storage (with localStorage fallback for dev)');
1330
+ console.log();
1331
+ console.log(chalk.dim(' Read EXTENSION_SETUP.md for storage, messaging, and manifest patterns.'));
1332
+ console.log();
1333
+ }
1334
+ else {
1335
+ // Next.js: Prisma + database
1336
+ checkbox('Define your data models in `prisma/schema.prisma`');
1337
+ console.log(chalk.dim(" Replace the placeholder Todo model with your app's models"));
1338
+ console.log();
1339
+ checkbox('Push schema and seed the database');
1340
+ console.log(chalk.dim(' npm run db:push'));
1341
+ console.log(chalk.dim(' # Edit prisma/seed.ts with your seed data, then:'));
1342
+ console.log(chalk.dim(' npm run db:seed'));
1343
+ console.log(chalk.dim(` # After re-seeding, restart the dev server to pick up fresh data:`));
1344
+ console.log(chalk.dim(` # codeyam editor dev-server '{"action":"restart"}'`));
1345
+ console.log();
1346
+ console.log(chalk.yellow(' IMPORTANT: When adding new required columns to existing tables,'));
1347
+ console.log(chalk.yellow(' provide a @default(...) value so `db push` can fill existing rows.'));
1348
+ console.log(chalk.dim(' Example: userId String @default("anonymous") — existing rows get "anonymous"'));
1349
+ console.log(chalk.dim(' Without a default, Prisma requires --force-reset which drops ALL data.'));
1350
+ console.log(chalk.dim(' NEVER use --force-reset — it is blocked in this environment.'));
1351
+ console.log();
1352
+ console.log(chalk.dim(' See DATABASE.md for Prisma patterns and important warnings.'));
1353
+ console.log(chalk.dim(' Key: import { prisma } from "@/app/lib/prisma" in API routes.'));
1354
+ console.log(chalk.dim(' Key: Seed scripts must use the adapter pattern (see prisma/seed.ts).'));
1355
+ console.log();
1356
+ }
1012
1357
  printDataStructureInstructions();
1013
1358
  }
1014
1359
  else {
@@ -1026,6 +1371,10 @@ function printStep2(root, feature) {
1026
1371
  console.log();
1027
1372
  printDataStructureInstructions();
1028
1373
  }
1374
+ console.log();
1375
+ printServiceRecordingReminder(port);
1376
+ console.log();
1377
+ checkboxHandoff();
1029
1378
  stopGate(2);
1030
1379
  }
1031
1380
  // ─── Step 3: Prototype ────────────────────────────────────────────────
@@ -1050,26 +1399,42 @@ function printStep3(root, feature) {
1050
1399
  if (isResuming) {
1051
1400
  printResumptionHeader(3);
1052
1401
  }
1402
+ const ctx = getTechStackContext(root);
1053
1403
  console.log('Build fast with real data. Prioritize speed over quality.');
1054
1404
  console.log();
1055
1405
  console.log(chalk.bold('Checklist:'));
1056
- checkbox('Create API routes that read from the database via Prisma');
1057
- if (!projectExists) {
1058
- checkbox('Seed the database with demo data');
1059
- checkbox('Create `.codeyam/seed-adapter.ts` so CodeYam can seed the database for scenarios');
1060
- console.log(chalk.dim(' The seed adapter reads a JSON file (path passed as CLI arg), wipes tables, inserts rows.'));
1061
- console.log(chalk.dim(" Use the project's own ORM (Prisma, Drizzle, etc.). See template for example."));
1062
- console.log(chalk.dim(' Run with: npx tsx .codeyam/seed-adapter.ts <path-to-seed-data.json>'));
1063
- console.log();
1064
- console.log(chalk.bold.cyan('Make seed data visually rich:'));
1065
- console.log(chalk.cyan(' • Use real placeholder images from Unsplash (https://images.unsplash.com/photo-<id>?w=400&h=300&fit=crop)'));
1066
- console.log(chalk.cyan(' • Use avatar services like i.pravatar.cc for user profile photos'));
1067
- console.log(chalk.cyan(' • Write realistic, varied content — not "Item 1", "Item 2", "Test Description"'));
1068
- console.log(chalk.cyan(' • Include different text lengths, categories, dates, and statuses'));
1069
- console.log(chalk.cyan(' • Rich seed data makes the prototype look real and surfaces layout issues early'));
1406
+ if (ctx.isExpo) {
1407
+ checkbox('Build screens that read from AsyncStorage via `lib/storage.ts` or fetch from APIs');
1408
+ if (!projectExists) {
1409
+ checkbox('Populate initial data in AsyncStorage for development');
1410
+ console.log(chalk.dim(' import { storage } from "@/lib/storage";'));
1411
+ console.log(chalk.dim(' await storage.set("items", [{ id: "1", title: "Buy groceries" }]);'));
1412
+ console.log();
1413
+ console.log(chalk.bold.cyan('Make data visually rich:'));
1414
+ console.log(chalk.cyan(' Write realistic, varied content — not "Item 1", "Item 2", "Test Description"'));
1415
+ console.log(chalk.cyan(' • Include different text lengths, categories, dates, and statuses'));
1416
+ console.log(chalk.cyan(' • Rich data makes the prototype look real and surfaces layout issues early'));
1417
+ }
1418
+ }
1419
+ else {
1420
+ checkbox('Create API routes that read from the database via Prisma');
1421
+ if (!projectExists) {
1422
+ checkbox('Seed the database with demo data');
1423
+ checkbox('Create `.codeyam/seed-adapter.ts` so CodeYam can seed the database for scenarios');
1424
+ console.log(chalk.dim(' The seed adapter reads a JSON file (path passed as CLI arg), wipes tables, inserts rows.'));
1425
+ console.log(chalk.dim(" Use the project's own ORM (Prisma, Drizzle, etc.). See template for example."));
1426
+ console.log(chalk.dim(' Run with: npx tsx .codeyam/seed-adapter.ts <path-to-seed-data.json>'));
1427
+ console.log();
1428
+ console.log(chalk.bold.cyan('Make seed data visually rich:'));
1429
+ console.log(chalk.cyan(' • Use real placeholder images from Unsplash (https://images.unsplash.com/photo-<id>?w=400&h=300&fit=crop)'));
1430
+ console.log(chalk.cyan(' • Use avatar services like i.pravatar.cc for user profile photos'));
1431
+ console.log(chalk.cyan(' • Write realistic, varied content — not "Item 1", "Item 2", "Test Description"'));
1432
+ console.log(chalk.cyan(' • Include different text lengths, categories, dates, and statuses'));
1433
+ console.log(chalk.cyan(' • Rich seed data makes the prototype look real and surfaces layout issues early'));
1434
+ }
1070
1435
  }
1071
1436
  checkbox('Verify the dev server shows the changes');
1072
- checkbox('If the feature involves auth, email, payments, or other common patterns: read FEATURE_PATTERNS.md');
1437
+ checkbox(`If the feature involves auth, email, payments, or other common patterns: read ${ctx.patternsFile}`);
1073
1438
  // Responsive design guidance when building a mobile-responsive web app
1074
1439
  if (prevState?.appFormats?.includes('mobile-responsive-web-app')) {
1075
1440
  console.log();
@@ -1086,16 +1451,33 @@ function printStep3(root, feature) {
1086
1451
  console.log();
1087
1452
  console.log(designSystem);
1088
1453
  console.log();
1089
- checkbox('Define ALL design tokens as CSS custom properties in globals.css — not just colors');
1090
- console.log(chalk.dim(' Colors: --bg-surface, --text-primary, --accent-green-a, etc.'));
1091
- console.log(chalk.dim(' Typography: --text-xs, --text-sm, --text-lg, --text-2xl (font-size values)'));
1092
- console.log(chalk.dim(' Font weights: --font-weight-normal, --font-weight-medium, --font-weight-semibold'));
1093
- console.log(chalk.dim(' Spacing: --spacing-xs, --spacing-sm, --spacing-md, --spacing-lg, etc.'));
1094
- console.log(chalk.dim(' Border radius, shadows, transitions — every value the design system defines.'));
1095
- checkbox('Reference tokens from components — ZERO hardcoded px values for font-size, spacing, or colors');
1096
- console.log(chalk.dim(' Bad: fontSize: 14, padding: "12px 16px", gap: 8'));
1097
- console.log(chalk.dim(' Good: fontSize: "var(--text-sm)", padding: "var(--spacing-md) var(--spacing-lg)", gap: "var(--spacing-sm)"'));
1098
- console.log(chalk.dim(' This ensures the entire app updates when the design system changes.'));
1454
+ if (ctx.isExpo) {
1455
+ checkbox('Define ALL design tokens in `lib/theme.ts` — this is the single source of truth');
1456
+ console.log(chalk.dim(' Colors: theme.colors.bgSurface, theme.colors.textPrimary, etc.'));
1457
+ console.log(chalk.dim(' Typography: theme.fontSize.sm, theme.fontSize.lg, theme.fontFamily.mono'));
1458
+ console.log(chalk.dim(' Spacing: theme.spacing.sm, theme.spacing.md, theme.spacing.lg, etc.'));
1459
+ console.log(chalk.dim(' Border radius: theme.borderRadius.sm, theme.borderRadius.lg, etc.'));
1460
+ checkbox('Import theme in every component — ZERO hardcoded color strings or pixel values');
1461
+ console.log(chalk.dim(' Bad: color: "#333", fontSize: 14, padding: 12'));
1462
+ console.log(chalk.dim(' Good: color: theme.colors.textPrimary, fontSize: theme.fontSize.sm, padding: theme.spacing.md'));
1463
+ console.log(chalk.dim(' Do NOT use CSS custom properties (var(--token)) they do not work in React Native.'));
1464
+ checkbox('Buttons: put backgroundColor, borderRadius, borderStyle on a wrapping <View>, NOT on <Pressable>');
1465
+ console.log(chalk.dim(' Pressable renders these styles on web but FAILS SILENTLY on native devices.'));
1466
+ console.log(chalk.dim(' Use a <View> with overflow:"hidden" for the visual shell, <Pressable> inside for touch only.'));
1467
+ console.log(chalk.dim(' For press feedback: use onPressIn/onPressOut + useState to toggle opacity, not function-style style.'));
1468
+ }
1469
+ else {
1470
+ checkbox('Define ALL design tokens as CSS custom properties in globals.css — not just colors');
1471
+ console.log(chalk.dim(' Colors: --bg-surface, --text-primary, --accent-green-a, etc.'));
1472
+ console.log(chalk.dim(' Typography: --text-xs, --text-sm, --text-lg, --text-2xl (font-size values)'));
1473
+ console.log(chalk.dim(' Font weights: --font-weight-normal, --font-weight-medium, --font-weight-semibold'));
1474
+ console.log(chalk.dim(' Spacing: --spacing-xs, --spacing-sm, --spacing-md, --spacing-lg, etc.'));
1475
+ console.log(chalk.dim(' Border radius, shadows, transitions — every value the design system defines.'));
1476
+ checkbox('Reference tokens from components — ZERO hardcoded px values for font-size, spacing, or colors');
1477
+ console.log(chalk.dim(' Bad: fontSize: 14, padding: "12px 16px", gap: 8'));
1478
+ console.log(chalk.dim(' Good: fontSize: "var(--text-sm)", padding: "var(--spacing-md) var(--spacing-lg)", gap: "var(--spacing-sm)"'));
1479
+ console.log(chalk.dim(' This ensures the entire app updates when the design system changes.'));
1480
+ }
1099
1481
  }
1100
1482
  console.log();
1101
1483
  console.log(chalk.bold.cyan('Keep the preview moving:'));
@@ -1109,6 +1491,9 @@ function printStep3(root, feature) {
1109
1491
  console.log(chalk.red.bold(' NEVER claim "you should see X" or "the preview shows X" unless you just ran a preview command.'));
1110
1492
  console.log(chalk.red(' The preview only updates when you explicitly refresh it. Verify, then describe.'));
1111
1493
  console.log();
1494
+ printServiceRecordingReminder(port);
1495
+ console.log();
1496
+ checkboxHandoff();
1112
1497
  stopGate(3);
1113
1498
  }
1114
1499
  // ─── Step 4: Verify Prototype ─────────────────────────────────────────
@@ -1123,6 +1508,8 @@ function printStep4(root, feature) {
1123
1508
  label: STEP_LABELS[4],
1124
1509
  startedAt: isResuming ? prevState.startedAt : now,
1125
1510
  featureStartedAt: prevState?.featureStartedAt || now,
1511
+ appFormats: prevState?.appFormats,
1512
+ techStackId: prevState?.techStackId,
1126
1513
  });
1127
1514
  logEvent(root, 'step', { step: 4, label: 'Verify Prototype', feature });
1128
1515
  stepHeader(4, 'Verify Prototype', feature);
@@ -1132,14 +1519,13 @@ function printStep4(root, feature) {
1132
1519
  console.log('Verify everything works before presenting the prototype.');
1133
1520
  console.log();
1134
1521
  console.log(chalk.bold('Verify the dev server:'));
1135
- console.log(chalk.dim(` # Get dev server URL: codeyam editor dev-server`));
1136
- console.log(chalk.dim(' # Check page loads: curl -s -o /dev/null -w "%{http_code}" http://localhost:<dev-port>'));
1137
- console.log(chalk.dim(' # Check API routes: curl -s http://localhost:<dev-port>/api/your-route'));
1522
+ console.log(chalk.dim(' # Verify pages and API routes load:'));
1523
+ console.log(chalk.dim(` codeyam editor verify-routes '{"paths":["/your-page"],"apiRoutes":["/api/your-route"]}'`));
1138
1524
  console.log();
1139
1525
  console.log(chalk.bold('Verify before proceeding:'));
1140
1526
  console.log(chalk.yellow(' Verify everything works before presenting the prototype to the user.'));
1141
- checkbox('Verify the page loads: curl the dev server URL and confirm HTTP 200 (not an error page)');
1142
- checkbox('Verify API routes return valid JSON: curl each route and confirm no error responses');
1527
+ checkbox('Verify page and API routes: `codeyam editor verify-routes \'{"paths":["/"],"apiRoutes":["/api/your-route"]}\'`');
1528
+ console.log(chalk.dim(' Include ALL page paths you built and ALL API routes they depend on.'));
1143
1529
  checkbox('Check for broken images: `codeyam editor verify-images \'{"paths":["/"], "imageUrls":["url1","url2"]}\'`');
1144
1530
  console.log(chalk.dim(' Pass ALL page paths and ALL image URLs you used in seed data / API responses.'));
1145
1531
  console.log(chalk.dim(' Client-rendered pages need imageUrls — the HTML shell has no images to scan.'));
@@ -1150,6 +1536,14 @@ function printStep4(root, feature) {
1150
1536
  console.log(chalk.dim(' The user is looking at the preview — a blank page means something is broken.'));
1151
1537
  console.log(chalk.dim(' If any check fails, fix the issue and re-verify before proceeding.'));
1152
1538
  console.log();
1539
+ const ctx4 = getTechStackContext(root);
1540
+ if (ctx4.isExpo) {
1541
+ console.log(chalk.magenta.bold(' EXPO WEB PREVIEW NOTE:'));
1542
+ console.log(chalk.magenta(' The preview renders via react-native-web in a browser. Some differences'));
1543
+ console.log(chalk.magenta(' from native devices are expected (fonts, SafeAreaView, shadows, Platform.OS).'));
1544
+ console.log(chalk.magenta(' The preview is for layout and data verification. Test final polish on device.'));
1545
+ console.log();
1546
+ }
1153
1547
  console.log(chalk.bold('Update README and setup script:'));
1154
1548
  checkbox('Update `README.md`: set the project name, write a one-line description, and list any setup prerequisites');
1155
1549
  checkbox('Update `npm run setup` in `package.json` if setup requires extra steps (e.g. env vars, external services)');
@@ -1157,6 +1551,8 @@ function printStep4(root, feature) {
1157
1551
  console.log(chalk.dim(' A new clone should work with just: git clone → npm run setup → npm run dev'));
1158
1552
  console.log();
1159
1553
  console.log(chalk.dim('Focus on building the prototype. Scenarios and refactoring happen in later steps.'));
1554
+ console.log();
1555
+ checkboxHandoff();
1160
1556
  stopGate(4);
1161
1557
  }
1162
1558
  // ─── Step 5: Confirm ──────────────────────────────────────────────────
@@ -1172,6 +1568,8 @@ function printStep5(root, feature) {
1172
1568
  label: STEP_LABELS[5],
1173
1569
  startedAt: isResuming ? prevState.startedAt : now,
1174
1570
  featureStartedAt: prevState?.featureStartedAt || now,
1571
+ appFormats: prevState?.appFormats,
1572
+ techStackId: prevState?.techStackId,
1175
1573
  });
1176
1574
  logEvent(root, 'step', { step: 5, label: 'Confirm', feature });
1177
1575
  stepHeader(5, 'Confirm', feature);
@@ -1182,7 +1580,7 @@ function printStep5(root, feature) {
1182
1580
  console.log();
1183
1581
  console.log(chalk.bold('Before presenting — verify everything works:'));
1184
1582
  checkbox(`Refresh the preview: \`codeyam editor preview '{"dimension":"${dim}"}'\` — check the \`preview\` field for \`healthy: false\``);
1185
- checkbox('Verify API routes return valid data (curl each route)');
1583
+ checkbox('Verify routes: `codeyam editor verify-routes \'{"paths":["/"],"apiRoutes":["/api/your-route"]}\'`');
1186
1584
  console.log();
1187
1585
  console.log(chalk.bold.red(' Verify EVERY image loads (this is the #1 source of broken prototypes):'));
1188
1586
  checkbox('Run `codeyam editor verify-images \'{"paths":["/"], "imageUrls":["url1","url2"]}\'`');
@@ -1218,6 +1616,8 @@ function printStep5(root, feature) {
1218
1616
  chalk.dim(' — make changes, refresh preview, re-run `codeyam editor 5`'));
1219
1617
  console.log();
1220
1618
  console.log(chalk.dim('Wait for user approval before moving on. Refactoring and scenarios happen in later steps.'));
1619
+ console.log();
1620
+ checkboxHandoff();
1221
1621
  stopGate(5, { confirm: true });
1222
1622
  }
1223
1623
  // ─── Step 6: Deconstruct ──────────────────────────────────────────────
@@ -1231,6 +1631,8 @@ function printStep6(root, feature) {
1231
1631
  label: STEP_LABELS[6],
1232
1632
  startedAt: isResuming ? prevState.startedAt : now,
1233
1633
  featureStartedAt: prevState?.featureStartedAt || now,
1634
+ appFormats: prevState?.appFormats,
1635
+ techStackId: prevState?.techStackId,
1234
1636
  });
1235
1637
  logEvent(root, 'step', { step: 6, label: 'Deconstruct', feature });
1236
1638
  stepHeader(6, 'Deconstruct', feature);
@@ -1241,6 +1643,8 @@ function printStep6(root, feature) {
1241
1643
  console.log(chalk.yellow('This step is read and plan only. Code extraction happens in step 7.'));
1242
1644
  console.log();
1243
1645
  printExtractionPlanInstructions();
1646
+ console.log();
1647
+ checkboxHandoff();
1244
1648
  stopGate(6);
1245
1649
  }
1246
1650
  // ─── Step 7: Extract ──────────────────────────────────────────────────
@@ -1256,12 +1660,15 @@ function printStep7(root, feature) {
1256
1660
  label: STEP_LABELS[7],
1257
1661
  startedAt: isResuming ? prevState.startedAt : now,
1258
1662
  featureStartedAt: prevState?.featureStartedAt || now,
1663
+ appFormats: prevState?.appFormats,
1664
+ techStackId: prevState?.techStackId,
1259
1665
  });
1260
1666
  logEvent(root, 'step', { step: 7, label: 'Extract', feature });
1261
1667
  stepHeader(7, 'Extract', feature);
1262
1668
  if (isResuming) {
1263
1669
  printResumptionHeader(7);
1264
1670
  }
1671
+ const ctx = getTechStackContext(root);
1265
1672
  console.log('Execute your extraction plan from step 6.');
1266
1673
  console.log();
1267
1674
  console.log(chalk.bold('Components:'));
@@ -1274,19 +1681,45 @@ function printStep7(root, feature) {
1274
1681
  checkbox('For each function/hook: write MULTIPLE failing tests FIRST, then extract to make them pass');
1275
1682
  console.log(chalk.dim(' Cover: typical inputs, edge cases, empty/null inputs, error conditions'));
1276
1683
  console.log(chalk.dim(' Aim for 3-8 test cases per function depending on complexity'));
1684
+ console.log(chalk.dim(' EVERY conditional branch in the function MUST have a test that exercises it'));
1685
+ console.log(chalk.dim(' EVERY return path MUST be tested — if a function can return 3 different things, write 3+ tests'));
1686
+ console.log(chalk.dim(' Boundary values: 0, 1, -1, empty string, empty array, undefined, null'));
1277
1687
  console.log(chalk.dim(' Hooks count as functions — useDrinks, useAuth, etc. all need test files'));
1278
- checkbox('Place test files next to source: `app/lib/drinks.ts` `app/lib/drinks.test.ts`');
1688
+ console.log(chalk.dim(' Wrap all tests in a describe("FunctionName", ...) block — the audit matches on this name'));
1689
+ if (ctx.isExpo) {
1690
+ checkbox('Place test files next to source but OUTSIDE `app/` (Expo Router treats all files in app/ as routes): `lib/storage.ts` → `lib/storage.test.ts`, `app/hooks/useCounter.ts` → `__tests__/hooks/useCounter.test.ts`');
1691
+ }
1692
+ else {
1693
+ checkbox('Place test files next to source: `app/lib/drinks.ts` → `app/lib/drinks.test.ts`');
1694
+ }
1279
1695
  console.log(chalk.yellow(' Tests ARE the only coverage for library functions/hooks — step 9 only captures component screenshots.'));
1280
1696
  console.log();
1281
1697
  console.log(chalk.bold('Recursive pass:'));
1282
1698
  checkbox('Re-read EVERY new file you just created — extract components from components, functions from functions');
1283
1699
  checkbox('Keep going until every file is a thin shell: just imports and composition');
1284
- console.log(chalk.yellow(' Check: does any file contain raw <div>, <span>, <h1>, <p>, <img>, or <ul>?'));
1700
+ console.log(chalk.yellow(` Check: does any file contain raw ${ctx.rawPrimitivesList}?`));
1285
1701
  console.log(chalk.yellow(' If yes → that JSX section is a component waiting to be extracted.'));
1286
1702
  console.log();
1703
+ console.log(chalk.bold('Decomposition pass (backend AND frontend):'));
1704
+ checkbox('Re-read EVERY file you created or modified. For each, extract a function/helper/sub-component for:');
1705
+ console.log(chalk.yellow(' — Conditionals (e.g. `isOverdue ? "red" : "green"` → `getStatusColor(date)`)'));
1706
+ console.log(chalk.yellow(' — Computed values (e.g. `items.filter(...).length` → `countActiveItems(items)`)'));
1707
+ console.log(chalk.yellow(' — Formatting (e.g. `${price.toFixed(2)}` → `formatPrice(price)`)'));
1708
+ console.log(chalk.yellow(' — Data transforms, validation, request/response shaping'));
1709
+ console.log(chalk.yellow(' — Anything doing more than one thing → split until each piece does one clearly defined thing'));
1710
+ checkbox('Then look at what you just extracted — can IT be broken down further? Keep going.');
1711
+ checkbox('Write tests for every extracted function (same TDD: failing tests FIRST)');
1712
+ console.log();
1713
+ console.log(chalk.bold.red('TEST QUALITY SELF-CHECK (before proceeding):'));
1714
+ checkbox('For EACH test file: count the test cases. Fewer than 3 tests for any function is a red flag.');
1715
+ checkbox('For EACH tested function: read the function, count the conditional branches. Every branch MUST have a test.');
1716
+ checkbox('For EACH tested function: verify you test at least one error/edge case, not just happy paths.');
1717
+ console.log(chalk.yellow(' If a function has an if/else, switch, or ternary — each path needs its own test case.'));
1718
+ console.log(chalk.yellow(' If a function handles null/undefined/empty — test those inputs explicitly.'));
1719
+ console.log();
1287
1720
  console.log(chalk.bold('Verify before proceeding:'));
1288
1721
  checkbox('Run all tests and verify they pass');
1289
- checkbox('Page files contain ONLY imports + component composition — no raw HTML tags');
1722
+ checkbox(`Page files contain ONLY imports + component composition — no raw ${ctx.isExpo ? 'React Native primitives' : 'HTML tags'}`);
1290
1723
  checkbox('Every component renders ONE thing or composes sub-components — no multi-section JSX');
1291
1724
  checkbox(`Refresh the preview after each batch of extractions: \`codeyam editor preview '{"dimension":"${dim}"}'\``);
1292
1725
  printDimensionGuidance(dim, dimNames);
@@ -1294,6 +1727,8 @@ function printStep7(root, feature) {
1294
1727
  console.log(chalk.dim('Reuse glossary functions when they fit naturally. Extract a new function when the use case diverges.'));
1295
1728
  console.log();
1296
1729
  console.log(chalk.dim('Focus on TDD for functions and extraction for components. Scenarios come in later steps.'));
1730
+ console.log();
1731
+ checkboxHandoff();
1297
1732
  stopGate(7);
1298
1733
  }
1299
1734
  // ─── Step 8: Glossary ─────────────────────────────────────────────────
@@ -1307,6 +1742,8 @@ function printStep8(root, feature) {
1307
1742
  label: STEP_LABELS[8],
1308
1743
  startedAt: isResuming ? prevState.startedAt : now,
1309
1744
  featureStartedAt: prevState?.featureStartedAt || now,
1745
+ appFormats: prevState?.appFormats,
1746
+ techStackId: prevState?.techStackId,
1310
1747
  });
1311
1748
  logEvent(root, 'step', { step: 8, label: 'Glossary', feature });
1312
1749
  stepHeader(8, 'Glossary', feature);
@@ -1333,6 +1770,8 @@ function printStep9(root, feature) {
1333
1770
  label: STEP_LABELS[9],
1334
1771
  startedAt: isResuming ? prevState.startedAt : now,
1335
1772
  featureStartedAt: prevState?.featureStartedAt || now,
1773
+ appFormats: prevState?.appFormats,
1774
+ techStackId: prevState?.techStackId,
1336
1775
  });
1337
1776
  logEvent(root, 'step', { step: 9, label: 'Analyze', feature });
1338
1777
  stepHeader(9, 'Analyze and Verify', feature);
@@ -1347,16 +1786,26 @@ function printStep9(root, feature) {
1347
1786
  console.log(chalk.dim(' Reuse and improve existing scenarios where possible — update mock data'));
1348
1787
  console.log(chalk.dim(' to reflect current changes. Add new scenarios only for genuinely new states.'));
1349
1788
  console.log(chalk.dim(' Ensure at least one scenario clearly demonstrates what changed in this session.'));
1350
- printComponentCaptureInstructions();
1789
+ const ctx9 = getTechStackContext(root);
1790
+ printComponentCaptureInstructions(root);
1351
1791
  console.log();
1352
1792
  console.log(chalk.bold('Library Functions — run tests:'));
1353
1793
  checkbox('Run ALL test files created in step 7');
1354
- console.log(chalk.dim(' Example: npx vitest run app/lib/drinks.test.ts'));
1794
+ console.log(chalk.dim(` Example: ${ctx9.testRunCommand} app/lib/drinks.test.ts`));
1355
1795
  checkbox('Verify every test passes');
1356
1796
  checkbox('If any test fails, fix the source code and re-run');
1357
1797
  console.log();
1358
1798
  console.log(chalk.dim('Do not proceed until both component isolations and library tests pass.'));
1359
1799
  console.log();
1800
+ console.log(chalk.bold.red('DECOMPOSITION & COVERAGE REVIEW (before running audit):'));
1801
+ checkbox('Re-read every file. Extract any logic that could be its own function/helper/sub-component.');
1802
+ console.log(chalk.yellow(' Code either contains logic OR brings together smaller pieces.'));
1803
+ console.log(chalk.yellow(' Keep extracting until each piece does one clearly defined thing.'));
1804
+ console.log(chalk.yellow(' For each extraction: write failing test first, then extract, then make test pass.'));
1805
+ checkbox('Open each test file and verify: ≥3 test cases per function, happy path + edge case + error case');
1806
+ checkbox('If any function has fewer tests than conditional branches, add the missing tests NOW');
1807
+ checkbox('Update `.codeyam/glossary.json` with any new functions before running audit');
1808
+ console.log();
1360
1809
  checkbox('Run `codeyam editor audit` to verify all components have scenarios and all functions/hooks have tests');
1361
1810
  console.log(chalk.red.bold(' The audit is a HARD GATE — step 10 will refuse to run until the audit passes.'));
1362
1811
  console.log(chalk.dim(' The audit auto-fixes incomplete entities by running targeted analysis on just the affected files.'));
@@ -1382,6 +1831,8 @@ function printStep10(root, feature) {
1382
1831
  label: STEP_LABELS[10],
1383
1832
  startedAt: isResuming ? prevState.startedAt : now,
1384
1833
  featureStartedAt: prevState?.featureStartedAt || now,
1834
+ appFormats: prevState?.appFormats,
1835
+ techStackId: prevState?.techStackId,
1385
1836
  });
1386
1837
  logEvent(root, 'step', { step: 10, label: 'App Scenarios', feature });
1387
1838
  stepHeader(10, 'App Scenarios', feature);
@@ -1446,6 +1897,8 @@ function printStep11(root, feature) {
1446
1897
  label: STEP_LABELS[11],
1447
1898
  startedAt: isResuming ? prevState.startedAt : now,
1448
1899
  featureStartedAt: prevState?.featureStartedAt || now,
1900
+ appFormats: prevState?.appFormats,
1901
+ techStackId: prevState?.techStackId,
1449
1902
  });
1450
1903
  logEvent(root, 'step', { step: 11, label: 'User Scenarios', feature });
1451
1904
  stepHeader(11, 'User Scenarios', feature);
@@ -1492,6 +1945,8 @@ function printStep12(root, feature) {
1492
1945
  label: STEP_LABELS[12],
1493
1946
  startedAt: isResuming ? prevState.startedAt : now,
1494
1947
  featureStartedAt: prevState?.featureStartedAt || now,
1948
+ appFormats: prevState?.appFormats,
1949
+ techStackId: prevState?.techStackId,
1495
1950
  });
1496
1951
  logEvent(root, 'step', { step: 12, label: 'Verify', feature });
1497
1952
  stepHeader(12, 'Verify', feature);
@@ -1538,6 +1993,8 @@ function printStep13(root, feature) {
1538
1993
  label: STEP_LABELS[13],
1539
1994
  startedAt: isResuming ? prevState.startedAt : now,
1540
1995
  featureStartedAt: prevState?.featureStartedAt || now,
1996
+ appFormats: prevState?.appFormats,
1997
+ techStackId: prevState?.techStackId,
1541
1998
  });
1542
1999
  logEvent(root, 'step', { step: 13, label: 'Journal', feature });
1543
2000
  stepHeader(13, 'Journal', feature);
@@ -1571,6 +2028,8 @@ function printStep14(root, feature) {
1571
2028
  label: STEP_LABELS[14],
1572
2029
  startedAt: isResuming ? prevState.startedAt : now,
1573
2030
  featureStartedAt: prevState?.featureStartedAt || now,
2031
+ appFormats: prevState?.appFormats,
2032
+ techStackId: prevState?.techStackId,
1574
2033
  });
1575
2034
  logEvent(root, 'step', { step: 14, label: 'Review', feature });
1576
2035
  stepHeader(14, 'Review', feature);
@@ -1590,6 +2049,8 @@ function printStep14(root, feature) {
1590
2049
  checkbox('Fix or remove any broken images before continuing');
1591
2050
  checkbox('Recapture stale scenarios: `codeyam editor recapture-stale`');
1592
2051
  checkbox('Run `codeyam editor audit` to verify completeness of scenarios and tests');
2052
+ checkbox('Verify tech stack is up to date: check for any new service imports (e.g. Stripe, Resend, Supabase) and ensure they appear in the tech stack with `envKeys`');
2053
+ console.log(chalk.dim(` Update via: curl -s -X POST http://localhost:${port}/api/editor-project-info -H "Content-Type: application/json" -d '{"techStack": {...}}'`));
1593
2054
  checkbox('Do not proceed until all checks pass');
1594
2055
  stopGate(14);
1595
2056
  }
@@ -1604,6 +2065,8 @@ function printStep15(root, feature) {
1604
2065
  label: STEP_LABELS[15],
1605
2066
  startedAt: isResuming ? prevState.startedAt : now,
1606
2067
  featureStartedAt: prevState?.featureStartedAt || now,
2068
+ appFormats: prevState?.appFormats,
2069
+ techStackId: prevState?.techStackId,
1607
2070
  });
1608
2071
  logEvent(root, 'step', { step: 15, label: 'Present', feature });
1609
2072
  stepHeader(15, 'Present', feature);
@@ -1832,7 +2295,7 @@ function printMigrateStep3(root) {
1832
2295
  console.log(chalk.dim(' Component scenarios use isolation routes with a codeyam-capture wrapper for tight screenshots.'));
1833
2296
  console.log(chalk.dim(' These supplement the app scenarios from step 2 with focused component-level views.'));
1834
2297
  console.log();
1835
- printComponentCaptureInstructions();
2298
+ printComponentCaptureInstructions(root);
1836
2299
  console.log();
1837
2300
  migrationStopGate(3, pageName, pageIndex, totalPages);
1838
2301
  }
@@ -1975,7 +2438,7 @@ function printMigrateStep8(root) {
1975
2438
  console.log();
1976
2439
  console.log(chalk.bold('Component Scenarios:'));
1977
2440
  console.log(chalk.dim(' For each visual component extracted in step 7, create isolation routes and register scenarios.'));
1978
- printComponentCaptureInstructions();
2441
+ printComponentCaptureInstructions(root);
1979
2442
  console.log();
1980
2443
  console.log(chalk.bold('Verify:'));
1981
2444
  checkbox('Run `codeyam editor analyze-imports` to populate import graph (or `codeyam editor analyze-imports path/to/file.tsx ...` for specific files)');
@@ -2253,6 +2716,8 @@ function printStep16(root, feature) {
2253
2716
  label: STEP_LABELS[16],
2254
2717
  startedAt: isResuming ? prevState.startedAt : now,
2255
2718
  featureStartedAt: prevState?.featureStartedAt || now,
2719
+ appFormats: prevState?.appFormats,
2720
+ techStackId: prevState?.techStackId,
2256
2721
  });
2257
2722
  logEvent(root, 'step', { step: 16, label: 'Commit', feature });
2258
2723
  stepHeader(16, 'Commit', feature);
@@ -2279,6 +2744,8 @@ function printStep17(root, feature) {
2279
2744
  label: STEP_LABELS[17],
2280
2745
  startedAt: isResuming ? prevState.startedAt : now,
2281
2746
  featureStartedAt: prevState?.featureStartedAt || now,
2747
+ appFormats: prevState?.appFormats,
2748
+ techStackId: prevState?.techStackId,
2282
2749
  });
2283
2750
  logEvent(root, 'step', { step: 17, label: 'Finalize', feature });
2284
2751
  stepHeader(17, 'Finalize', feature);
@@ -2304,6 +2771,8 @@ function printStep18(root, feature) {
2304
2771
  label: STEP_LABELS[18],
2305
2772
  startedAt: isResuming ? prevState.startedAt : now,
2306
2773
  featureStartedAt: prevState?.featureStartedAt || now,
2774
+ appFormats: prevState?.appFormats,
2775
+ techStackId: prevState?.techStackId,
2307
2776
  });
2308
2777
  logEvent(root, 'step', { step: 18, label: 'Push', feature });
2309
2778
  stepHeader(18, 'Push', feature);
@@ -2321,11 +2790,11 @@ function printStep18(root, feature) {
2321
2790
  console.log(chalk.dim(' If the user wants help, guide them through `gh repo create` or manual GitHub setup, then push.'));
2322
2791
  console.log(chalk.dim(' Task management is handled by the ━━━ TASK ━━━ directive below.'));
2323
2792
  console.log();
2324
- console.log(chalk.bold.yellow('IMPORTANT: After this step, the current feature is DONE.'));
2325
- console.log(chalk.yellow(' Any new user request even if it sounds related is a NEW feature.'));
2793
+ const port = getServerPort();
2794
+ console.log(chalk.bold.yellow('IMPORTANT: After the push succeeds (or the user skips it), the feature is DONE.'));
2795
+ console.log(chalk.yellow(` Signal the UI by running: curl -s -X POST http://localhost:${port}/api/editor-feature-complete`));
2796
+ console.log(chalk.yellow(' Then STOP and wait. Do NOT ask the user what to build next — the UI handles the transition.'));
2326
2797
  console.log(chalk.yellow(' Do NOT use `codeyam editor change` or start building directly.'));
2327
- console.log(chalk.yellow(' Ask the user what they want to build. Once they tell you, run `codeyam editor 1` yourself.'));
2328
- console.log(chalk.yellow(' NEVER tell the user to run `codeyam editor` commands — these are internal. Just ask what to build.'));
2329
2798
  console.log(chalk.yellow(' The change workflow is ONLY for modifications during steps 1-15, not after commit.'));
2330
2799
  stopGate(18, { confirm: true });
2331
2800
  }
@@ -2516,18 +2985,10 @@ async function handleAnalyzeImports(options = {}) {
2516
2985
  }
2517
2986
  }
2518
2987
  }
2519
- else if (targetFilePaths.length > 0) {
2520
- // Couldn't parse specific entity names mark all target files
2521
- // that we attempted to analyze as potentially failed
2522
- for (const fp of targetFilePaths) {
2523
- const entry = glossary.find((e) => e.filePath === fp);
2524
- updatedFailures[fp] = {
2525
- entityName: entry?.name || path.basename(fp, path.extname(fp)),
2526
- error: `Automated analysis failed — see .codeyam/analysis-errors.txt`,
2527
- failedAt: now,
2528
- };
2529
- }
2530
- }
2988
+ // When we can't parse specific entity names from the error, DON'T mark
2989
+ // all target files as failed. The error may be non-fatal (e.g., a cache
2990
+ // miss logged as "CodeYam Error") and blanket-marking every file as
2991
+ // permanently failed blocks future audits from resolving them.
2531
2992
  writeAnalysisFailures(root, updatedFailures);
2532
2993
  }
2533
2994
  catch {
@@ -2726,43 +3187,98 @@ async function handleDelete(scenarioId) {
2726
3187
  * Creates isolation route directories and the layout guard file.
2727
3188
  * This avoids brace-expansion permission prompts in the embedded terminal.
2728
3189
  */
3190
+ function handleDesignSystem(designSystemId) {
3191
+ const root = process.cwd();
3192
+ const ds = DESIGN_SYSTEMS.find((d) => d.id === designSystemId);
3193
+ if (!ds) {
3194
+ console.error(chalk.red(`Error: Unknown design system "${designSystemId}". Valid options: ${DESIGN_SYSTEMS.map((d) => d.id).join(', ')}`));
3195
+ process.exit(1);
3196
+ }
3197
+ const srcPath = path.join(__dirname, '..', '..', 'templates', 'design-systems', ds.fileName);
3198
+ if (!fs.existsSync(srcPath)) {
3199
+ console.error(chalk.red(`Error: Design system file not found: ${srcPath}`));
3200
+ process.exit(1);
3201
+ }
3202
+ const destDir = path.join(root, '.codeyam');
3203
+ fs.mkdirSync(destDir, { recursive: true });
3204
+ const destPath = path.join(destDir, 'design-system.md');
3205
+ fs.copyFileSync(srcPath, destPath);
3206
+ console.log(chalk.green(`Installed "${ds.name}" design system → .codeyam/design-system.md`));
3207
+ }
2729
3208
  function handleIsolate(componentNames) {
2730
3209
  const root = process.cwd();
3210
+ const ctx = getTechStackContext(root);
2731
3211
  if (componentNames.length === 0) {
2732
3212
  console.error(chalk.red('Usage: codeyam editor isolate "ComponentA ComponentB ..."'));
2733
3213
  process.exit(1);
2734
3214
  }
2735
3215
  const isolateDir = path.join(root, 'app', 'isolated-components');
2736
- // Create layout.tsx with production guard if missing
2737
- const layoutPath = path.join(isolateDir, 'layout.tsx');
3216
+ // Create the framework-appropriate layout guard if missing.
3217
+ // Clean up wrong-framework layout from a previous CLI version.
3218
+ const layoutPath = path.join(isolateDir, ctx.isExpo ? '_layout.tsx' : 'layout.tsx');
3219
+ const wrongLayoutPath = path.join(isolateDir, ctx.isExpo ? 'layout.tsx' : '_layout.tsx');
3220
+ if (fs.existsSync(wrongLayoutPath)) {
3221
+ fs.unlinkSync(wrongLayoutPath);
3222
+ }
2738
3223
  if (!fs.existsSync(layoutPath)) {
2739
3224
  fs.mkdirSync(isolateDir, { recursive: true });
2740
- fs.writeFileSync(layoutPath, [
2741
- 'import { notFound } from "next/navigation";',
2742
- '',
2743
- 'export default function CaptureLayout({ children }: { children: React.ReactNode }) {',
2744
- ' if (process.env.NODE_ENV === "production") notFound();',
2745
- ' return <>{children}</>;',
2746
- '}',
2747
- '',
2748
- ].join('\n'), 'utf8');
2749
- console.log(chalk.green(`Created layout guard: app/isolated-components/layout.tsx`));
2750
- }
2751
- // Create a directory for each component
3225
+ if (ctx.isExpo) {
3226
+ fs.writeFileSync(layoutPath, [
3227
+ 'import { Redirect, Slot } from "expo-router";',
3228
+ '',
3229
+ 'export default function CaptureLayout() {',
3230
+ ' if (!__DEV__) return <Redirect href="/" />;',
3231
+ ' return <Slot />;',
3232
+ '}',
3233
+ '',
3234
+ ].join('\n'), 'utf8');
3235
+ }
3236
+ else {
3237
+ fs.writeFileSync(layoutPath, [
3238
+ 'import { notFound } from "next/navigation";',
3239
+ '',
3240
+ 'export default function CaptureLayout({ children }: { children: React.ReactNode }) {',
3241
+ ' if (process.env.NODE_ENV === "production") notFound();',
3242
+ ' return <>{children}</>;',
3243
+ '}',
3244
+ '',
3245
+ ].join('\n'), 'utf8');
3246
+ }
3247
+ console.log(chalk.green(`Created layout guard: app/isolated-components/${ctx.isExpo ? '_layout.tsx' : 'layout.tsx'}`));
3248
+ }
3249
+ // Create isolation route for each component.
3250
+ // Expo Router uses flat files (ComponentName.tsx), Next.js uses subdirectories (ComponentName/page.tsx).
2752
3251
  const created = [];
2753
3252
  const existed = [];
2754
3253
  for (const name of componentNames) {
2755
- const dir = path.join(isolateDir, name);
2756
- if (fs.existsSync(dir)) {
2757
- existed.push(name);
3254
+ if (ctx.isExpo) {
3255
+ const filePath = path.join(isolateDir, `${name}.tsx`);
3256
+ if (fs.existsSync(filePath)) {
3257
+ existed.push(name);
3258
+ }
3259
+ else {
3260
+ created.push(name);
3261
+ }
2758
3262
  }
2759
3263
  else {
2760
- fs.mkdirSync(dir, { recursive: true });
2761
- created.push(name);
3264
+ const dir = path.join(isolateDir, name);
3265
+ if (fs.existsSync(dir)) {
3266
+ existed.push(name);
3267
+ }
3268
+ else {
3269
+ fs.mkdirSync(dir, { recursive: true });
3270
+ created.push(name);
3271
+ }
2762
3272
  }
2763
3273
  }
2764
3274
  if (created.length > 0) {
2765
- console.log(chalk.green(`Created ${created.length} isolation route dir(s): ${created.join(', ')}`));
3275
+ if (ctx.isExpo) {
3276
+ console.log(chalk.green(`Ready for ${created.length} isolation route(s): ${created.map((n) => `app/isolated-components/${n}.tsx`).join(', ')}`));
3277
+ console.log(chalk.dim(' Create each file with useLocalSearchParams, a scenarios map, and nativeID="codeyam-capture"'));
3278
+ }
3279
+ else {
3280
+ console.log(chalk.green(`Created ${created.length} isolation route dir(s): ${created.join(', ')}`));
3281
+ }
2766
3282
  }
2767
3283
  if (existed.length > 0) {
2768
3284
  console.log(chalk.dim(`Already existed: ${existed.join(', ')}`));
@@ -2816,6 +3332,12 @@ function formatApiSubcommandResult(subcommand, data) {
2816
3332
  parts.push(`scenarioId="${data.scenarioId}"`);
2817
3333
  if (data.sessionsNotified != null)
2818
3334
  parts.push(`notified=${data.sessionsNotified}`);
3335
+ // Surface the HTTP health check from the dev server
3336
+ if (data.preview) {
3337
+ parts.push(`healthy=${data.preview.healthy}`);
3338
+ if (data.preview.error)
3339
+ parts.push(`error="${data.preview.error}"`);
3340
+ }
2819
3341
  return parts.join(' ');
2820
3342
  }
2821
3343
  case 'dev-server': {
@@ -2865,10 +3387,82 @@ function formatApiSubcommandResult(subcommand, data) {
2865
3387
  }
2866
3388
  return parts.join(' ');
2867
3389
  }
3390
+ case 'verify-routes': {
3391
+ const lines = [];
3392
+ lines.push(chalk.bold.yellow('━━━ Route Verification ━━━'));
3393
+ for (const [route, info] of Object.entries(data.routes || {})) {
3394
+ const r = info;
3395
+ if (r.ok) {
3396
+ lines.push(chalk.green(` ✓ ${route} — HTTP ${r.status}`));
3397
+ }
3398
+ else {
3399
+ lines.push(chalk.red(` ✗ ${route} — ${r.error || `HTTP ${r.status}`}`));
3400
+ }
3401
+ }
3402
+ for (const [route, info] of Object.entries(data.apiRoutes || {})) {
3403
+ const r = info;
3404
+ if (r.ok && r.isJSON) {
3405
+ lines.push(chalk.green(` ✓ ${route} — HTTP ${r.status}, valid JSON`));
3406
+ }
3407
+ else if (r.ok) {
3408
+ lines.push(chalk.yellow(` ⚠ ${route} — HTTP ${r.status}, not valid JSON`));
3409
+ }
3410
+ else {
3411
+ lines.push(chalk.red(` ✗ ${route} — ${r.error || `HTTP ${r.status}`}`));
3412
+ }
3413
+ }
3414
+ lines.push('');
3415
+ lines.push(data.ok ? chalk.green(data.summary) : chalk.red(data.summary));
3416
+ return lines.join('\n');
3417
+ }
2868
3418
  default:
2869
3419
  return null; // journal-list, show/hide-results: keep full JSON
2870
3420
  }
2871
3421
  }
3422
+ /**
3423
+ * `codeyam editor handoff '{"summary":"..."}'`
3424
+ *
3425
+ * Update the handoff summary in .codeyam/config.json for other AI providers.
3426
+ */
3427
+ function handleHandoff(jsonArg) {
3428
+ if (!jsonArg) {
3429
+ console.error(chalk.red('Error: JSON argument required.'));
3430
+ console.error(chalk.dim(' Usage: codeyam editor handoff \'{"summary":"Built the X component..."}\''));
3431
+ process.exit(1);
3432
+ }
3433
+ let summary;
3434
+ try {
3435
+ const parsed = JSON.parse(jsonArg);
3436
+ summary = parsed.summary;
3437
+ }
3438
+ catch {
3439
+ console.error(chalk.red('Error: Invalid JSON.'));
3440
+ process.exit(1);
3441
+ }
3442
+ if (!summary) {
3443
+ console.error(chalk.red('Error: "summary" field is required.'));
3444
+ process.exit(1);
3445
+ }
3446
+ const root = getProjectRoot();
3447
+ const configPath = path.join(root, '.codeyam', 'config.json');
3448
+ try {
3449
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
3450
+ const state = readState(root);
3451
+ const provider = config.provider || 'claude';
3452
+ config.handoff = {
3453
+ summary,
3454
+ lastProvider: provider,
3455
+ lastStep: state?.step,
3456
+ lastUpdated: new Date().toISOString(),
3457
+ };
3458
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8');
3459
+ console.log(chalk.green('✓ Handoff summary updated in .codeyam/config.json'));
3460
+ }
3461
+ catch (err) {
3462
+ console.error(chalk.red(`Error: Could not update config.json: ${err.message}`));
3463
+ process.exit(1);
3464
+ }
3465
+ }
2872
3466
  /**
2873
3467
  * `codeyam editor register '{"name":"...","componentName":"...",...}'`
2874
3468
  *
@@ -3737,14 +4331,20 @@ async function handleAudit(options) {
3737
4331
  let detail;
3738
4332
  switch (f.status) {
3739
4333
  case 'ok':
3740
- detail = chalk.dim(` (${f.testFile})`);
4334
+ if (f.testCaseCount !== undefined && f.testCaseCount < 3) {
4335
+ detail = chalk.yellow(` (${f.testFile}) — ⚠ only ${f.testCaseCount} test case${f.testCaseCount !== 1 ? 's' : ''}, consider adding more`);
4336
+ }
4337
+ else {
4338
+ detail = chalk.dim(` (${f.testFile}${f.testCaseCount !== undefined ? `, ${f.testCaseCount} tests` : ''})`);
4339
+ }
3741
4340
  break;
3742
4341
  case 'runner_error':
3743
4342
  detail = chalk.red(` — test runner crashed: ${f.testFile}`);
3744
4343
  if (f.errorMessage) {
3745
4344
  detail += `\n ${chalk.red(f.errorMessage)}`;
3746
4345
  detail += `\n ${chalk.yellow('Note: This is NOT a test failure — the test runner itself could not execute.')}`;
3747
- detail += `\n ${chalk.yellow('Try running the test manually: npx vitest run ' + f.testFile)}`;
4346
+ const ctx = getTechStackContext(process.cwd());
4347
+ detail += `\n ${chalk.yellow('Try running the test manually: ' + ctx.testRunCommand + ' ' + f.testFile)}`;
3748
4348
  }
3749
4349
  break;
3750
4350
  case 'failing':
@@ -3769,6 +4369,9 @@ async function handleAudit(options) {
3769
4369
  }
3770
4370
  console.log(` ${icon} ${f.name}${detail}`);
3771
4371
  }
4372
+ if (summary.functionsThinCoverage > 0) {
4373
+ console.log(chalk.yellow.bold(` ⚠ ${summary.functionsThinCoverage} function${summary.functionsThinCoverage !== 1 ? 's' : ''} with thin test coverage (< 3 test cases). Add more tests to cover all branches.`));
4374
+ }
3772
4375
  console.log();
3773
4376
  }
3774
4377
  // Missing from glossary
@@ -4327,7 +4930,7 @@ async function handleTemplate() {
4327
4930
  _: [],
4328
4931
  });
4329
4932
  console.log(chalk.green(' CodeYam initialized.'));
4330
- // 5. Verify config has startCommand
4933
+ // 5. Verify config has startCommand and set format-specific defaults
4331
4934
  const configPath = path.join(root, '.codeyam', 'config.json');
4332
4935
  if (fs.existsSync(configPath)) {
4333
4936
  try {
@@ -4338,6 +4941,30 @@ async function handleTemplate() {
4338
4941
  console.log(chalk.yellow(' Warning: No startCommand found in .codeyam/config.json webapps.'));
4339
4942
  console.log(chalk.dim(' You may need to add: "startCommand": { "command": "sh", "args": ["-c", "npm run dev -- --port $PORT"] }'));
4340
4943
  }
4944
+ // Store appFormats from the tech stack so the editor UI can adapt
4945
+ if (stack?.supportedFormats) {
4946
+ config.appFormats = stack.supportedFormats;
4947
+ }
4948
+ // Pre-populate tech stack from template
4949
+ if (stack) {
4950
+ config.techStack = getTechStackForTemplate(stack.id);
4951
+ }
4952
+ // Set mobile-first defaults for mobile-app projects
4953
+ if (stack?.supportedFormats?.includes('mobile-app')) {
4954
+ config.defaultScreenSize = {
4955
+ name: 'iPhone 16',
4956
+ width: 393,
4957
+ height: 852,
4958
+ };
4959
+ config.screenSizes = {
4960
+ 'iPhone 16': { width: 393, height: 852 },
4961
+ 'iPhone 16 Pro Max': { width: 430, height: 932 },
4962
+ 'iPhone SE': { width: 375, height: 667 },
4963
+ 'Pixel 8': { width: 412, height: 915 },
4964
+ 'iPad mini': { width: 744, height: 1133 },
4965
+ };
4966
+ }
4967
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
4341
4968
  }
4342
4969
  catch {
4343
4970
  // Config parse error is non-fatal
@@ -4372,7 +4999,15 @@ async function handleTemplate() {
4372
4999
  }
4373
5000
  console.log();
4374
5001
  console.log(chalk.green.bold('Project scaffolded and ready!'));
4375
- console.log(chalk.dim('Next: Define your Prisma models, push the schema, seed the database, and build your feature.'));
5002
+ if (stack?.id === 'expo-react-native') {
5003
+ console.log(chalk.dim('Next: Set up your data types, configure the theme in lib/theme.ts, and build your feature.'));
5004
+ }
5005
+ else if (stack?.id === 'chrome-extension-react') {
5006
+ console.log(chalk.dim('Next: Configure your extension manifest and build your feature.'));
5007
+ }
5008
+ else {
5009
+ console.log(chalk.dim('Next: Define your Prisma models, push the schema, seed the database, and build your feature.'));
5010
+ }
4376
5011
  }
4377
5012
  // ─── Sync subcommand ─────────────────────────────────────────────────
4378
5013
  /**
@@ -4874,8 +5509,8 @@ const editorCommand = {
4874
5509
  describe: 'Editor mode guided workflow',
4875
5510
  builder: (yargs) => {
4876
5511
  const stepDescription = IS_INTERNAL_BUILD
4877
- ? 'Step number (1-18) or subcommand (template, register, isolate, analyze-imports, manual-entity, dependents, audit, scenarios, scenario-coverage, recapture-stale, change, sync, debug, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors, task-ontrack)'
4878
- : 'Step number (1-18) or subcommand (template, register, isolate, analyze-imports, manual-entity, dependents, audit, scenarios, scenario-coverage, recapture-stale, change, sync, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors, task-ontrack)';
5512
+ ? 'Step number (1-18) or subcommand (template, register, isolate, analyze-imports, manual-entity, dependents, audit, scenarios, scenario-coverage, recapture-stale, change, sync, debug, design-system, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors, task-ontrack)'
5513
+ : 'Step number (1-18) or subcommand (template, register, isolate, analyze-imports, manual-entity, dependents, audit, scenarios, scenario-coverage, recapture-stale, change, sync, design-system, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors, task-ontrack)';
4879
5514
  let builder = yargs
4880
5515
  .positional('step', {
4881
5516
  type: 'string',
@@ -4986,6 +5621,11 @@ const editorCommand = {
4986
5621
  }
4987
5622
  return;
4988
5623
  }
5624
+ // Subcommand: codeyam editor handoff '{"summary":"..."}'
5625
+ if (argv.step === 'handoff') {
5626
+ await handleHandoff(argv.json || '');
5627
+ return;
5628
+ }
4989
5629
  // Subcommand: codeyam editor register '{"name":"..."}'
4990
5630
  if (argv.step === 'register') {
4991
5631
  await handleRegister(argv.json || '');
@@ -5090,6 +5730,11 @@ const editorCommand = {
5090
5730
  await handleValidateSeed(argv.json || '');
5091
5731
  return;
5092
5732
  }
5733
+ // Subcommand: codeyam editor design-system <id>
5734
+ if (argv.step === 'design-system') {
5735
+ handleDesignSystem(argv.json || '');
5736
+ return;
5737
+ }
5093
5738
  // Subcommand: codeyam editor delete <scenarioId>
5094
5739
  if (argv.step === 'delete') {
5095
5740
  await handleDelete(argv.json || '');
@@ -5163,10 +5808,18 @@ const editorCommand = {
5163
5808
  try {
5164
5809
  const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
5165
5810
  const { projectSlug } = config;
5811
+ // Detect provider switch and inform the user
5812
+ const handoff = config.handoff;
5813
+ const currentProvider = config.provider || 'claude';
5814
+ if (handoff?.lastProvider && handoff.lastProvider !== currentProvider) {
5815
+ console.log(chalk.yellow(` Provider changed: ${handoff.lastProvider} → ${currentProvider}`));
5816
+ console.log(chalk.dim(' The editor will offer to continue with handoff context.'));
5817
+ }
5166
5818
  if (!projectSlug) {
5167
5819
  errorLog('Missing project slug. Try reinitializing with: `codeyam editor template`');
5168
5820
  return;
5169
5821
  }
5822
+ console.log(chalk.dim(` CodeYam Editor v${getDisplayVersion()}`));
5170
5823
  const connectionOk = await withoutSpinner(() => testEnvironment());
5171
5824
  if (!connectionOk) {
5172
5825
  errorLog('Environment validation failed');
@@ -5578,6 +6231,9 @@ const editorCommand = {
5578
6231
  process.exit(1);
5579
6232
  }
5580
6233
  }
6234
+ printHandoffContext(root);
6235
+ // Track step progress automatically for provider handoff
6236
+ updateHandoffProgress(root, step);
5581
6237
  switch (step) {
5582
6238
  case 1: {
5583
6239
  const feature = argv.feature || undefined;