@codeyam/codeyam-cli 0.1.0-staging.5370992 → 0.1.0-staging.56e2dca

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 (160) hide show
  1. package/analyzer-template/.build-info.json +7 -7
  2. package/analyzer-template/log.txt +3 -3
  3. package/analyzer-template/package.json +2 -2
  4. package/analyzer-template/packages/aws/package.json +5 -5
  5. package/analyzer-template/packages/database/package.json +1 -1
  6. package/codeyam-cli/src/commands/__tests__/editor.statePersistence.test.js +55 -0
  7. package/codeyam-cli/src/commands/__tests__/editor.statePersistence.test.js.map +1 -0
  8. package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js +39 -3
  9. package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js.map +1 -1
  10. package/codeyam-cli/src/commands/editor.js +426 -13
  11. package/codeyam-cli/src/commands/editor.js.map +1 -1
  12. package/codeyam-cli/src/commands/init.js +20 -0
  13. package/codeyam-cli/src/commands/init.js.map +1 -1
  14. package/codeyam-cli/src/data/techStacks.js +1 -1
  15. package/codeyam-cli/src/utils/__tests__/devServerState.test.js +93 -1
  16. package/codeyam-cli/src/utils/__tests__/devServerState.test.js.map +1 -1
  17. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +146 -0
  18. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
  19. package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js +98 -1
  20. package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js.map +1 -1
  21. package/codeyam-cli/src/utils/__tests__/editorRoadmap.test.js +1108 -0
  22. package/codeyam-cli/src/utils/__tests__/editorRoadmap.test.js.map +1 -0
  23. package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js +120 -0
  24. package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js.map +1 -1
  25. package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js +84 -0
  26. package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js.map +1 -1
  27. package/codeyam-cli/src/utils/__tests__/envFile.test.js +125 -0
  28. package/codeyam-cli/src/utils/__tests__/envFile.test.js.map +1 -0
  29. package/codeyam-cli/src/utils/__tests__/handoffContext.test.js +500 -0
  30. package/codeyam-cli/src/utils/__tests__/handoffContext.test.js.map +1 -0
  31. package/codeyam-cli/src/utils/__tests__/testRunner.test.js +0 -1
  32. package/codeyam-cli/src/utils/__tests__/testRunner.test.js.map +1 -1
  33. package/codeyam-cli/src/utils/designSystemShowcase.js +810 -0
  34. package/codeyam-cli/src/utils/designSystemShowcase.js.map +1 -0
  35. package/codeyam-cli/src/utils/devServerState.js +32 -0
  36. package/codeyam-cli/src/utils/devServerState.js.map +1 -1
  37. package/codeyam-cli/src/utils/editorAudit.js +28 -6
  38. package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
  39. package/codeyam-cli/src/utils/editorRoadmap.js +574 -0
  40. package/codeyam-cli/src/utils/editorRoadmap.js.map +1 -0
  41. package/codeyam-cli/src/utils/editorScenarioSwitch.js +27 -12
  42. package/codeyam-cli/src/utils/editorScenarioSwitch.js.map +1 -1
  43. package/codeyam-cli/src/utils/editorScenarios.js +10 -0
  44. package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
  45. package/codeyam-cli/src/utils/editorSeedAdapter.js +27 -14
  46. package/codeyam-cli/src/utils/editorSeedAdapter.js.map +1 -1
  47. package/codeyam-cli/src/utils/envFile.js +90 -0
  48. package/codeyam-cli/src/utils/envFile.js.map +1 -0
  49. package/codeyam-cli/src/utils/handoffContext.js +257 -0
  50. package/codeyam-cli/src/utils/handoffContext.js.map +1 -0
  51. package/codeyam-cli/src/utils/install-skills.js +36 -6
  52. package/codeyam-cli/src/utils/install-skills.js.map +1 -1
  53. package/codeyam-cli/src/utils/techStackConfig.js +38 -0
  54. package/codeyam-cli/src/utils/techStackConfig.js.map +1 -0
  55. package/codeyam-cli/src/utils/techStackConfig.test.js +85 -0
  56. package/codeyam-cli/src/utils/techStackConfig.test.js.map +1 -0
  57. package/codeyam-cli/src/utils/testRunner.js +1 -7
  58. package/codeyam-cli/src/utils/testRunner.js.map +1 -1
  59. package/codeyam-cli/src/webserver/__tests__/api.interactive-switch-scenario.test.js +1 -0
  60. package/codeyam-cli/src/webserver/__tests__/api.interactive-switch-scenario.test.js.map +1 -1
  61. package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js +119 -1
  62. package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js.map +1 -1
  63. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js +354 -1
  64. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js.map +1 -1
  65. package/codeyam-cli/src/webserver/app/lib/database.js.map +1 -1
  66. package/codeyam-cli/src/webserver/build/client/assets/{CopyButton-CLe80MMu.js → CopyButton-DTBZZfSk.js} +1 -1
  67. package/codeyam-cli/src/webserver/build/client/assets/{EntityItem-Crt_KN_U.js → EntityItem-BxclONWq.js} +1 -1
  68. package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeIcon-CD7lGABo.js → EntityTypeIcon-BsnEOJZ_.js} +1 -1
  69. package/codeyam-cli/src/webserver/build/client/assets/{InlineSpinner-CgTNOhnu.js → InlineSpinner-ByaELMbv.js} +1 -1
  70. package/codeyam-cli/src/webserver/build/client/assets/{InteractivePreview-DtYTSPL2.js → InteractivePreview-6WjVfhxX.js} +2 -2
  71. package/codeyam-cli/src/webserver/build/client/assets/{LibraryFunctionPreview-D3s1MFkb.js → LibraryFunctionPreview-ChX-Hp7W.js} +1 -1
  72. package/codeyam-cli/src/webserver/build/client/assets/{LogViewer-CM5zg40N.js → LogViewer-C-9zQdXg.js} +1 -1
  73. package/codeyam-cli/src/webserver/build/client/assets/{MiniClaudeChat-CQENLSrF.js → MiniClaudeChat-Bs2_Oua4.js} +2 -2
  74. package/codeyam-cli/src/webserver/build/client/assets/{ReportIssueModal-C2PLkej3.js → ReportIssueModal-DQsceHVv.js} +1 -1
  75. package/codeyam-cli/src/webserver/build/client/assets/{SafeScreenshot-DanvyBPb.js → SafeScreenshot-DThcm_9M.js} +1 -1
  76. package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-CefgqbCr.js → ScenarioViewer-Cl4oOA3A.js} +1 -1
  77. package/codeyam-cli/src/webserver/build/client/assets/{Spinner-Bc8BG-Lw.js → Spinner-CIil5-gb.js} +1 -1
  78. package/codeyam-cli/src/webserver/build/client/assets/{ViewportInspectBar-BA_Ry-rs.js → ViewportInspectBar-BqkA9zyZ.js} +1 -1
  79. package/codeyam-cli/src/webserver/build/client/assets/{_index-C1YkzTAV.js → _index-DnOgyseQ.js} +1 -1
  80. package/codeyam-cli/src/webserver/build/client/assets/{activity.(_tab)-yH46LLUz.js → activity.(_tab)-DqM9hbNE.js} +1 -1
  81. package/codeyam-cli/src/webserver/build/client/assets/{addon-web-links-CHx25PAe.js → addon-web-links-C58dYPwR.js} +1 -1
  82. package/codeyam-cli/src/webserver/build/client/assets/{agent-transcripts-Bg3e7q4S.js → agent-transcripts-B8NCeOrm.js} +1 -1
  83. package/codeyam-cli/src/webserver/build/client/assets/api.editor-database-verify-l0sNRNKZ.js +1 -0
  84. package/codeyam-cli/src/webserver/build/client/assets/api.editor-github-verify-l0sNRNKZ.js +1 -0
  85. package/codeyam-cli/src/webserver/build/client/assets/api.editor-handoff-l0sNRNKZ.js +1 -0
  86. package/codeyam-cli/src/webserver/build/client/assets/api.editor-hosting-verify-l0sNRNKZ.js +1 -0
  87. package/codeyam-cli/src/webserver/build/client/assets/api.editor-roadmap-l0sNRNKZ.js +1 -0
  88. package/codeyam-cli/src/webserver/build/client/assets/{book-open-CL-lMgHh.js → book-open-BFSIqZgO.js} +1 -1
  89. package/codeyam-cli/src/webserver/build/client/assets/{chevron-down-GmAjGS9-.js → chevron-down-B9fDzFVh.js} +1 -1
  90. package/codeyam-cli/src/webserver/build/client/assets/chunk-UVKPFVEO-Bmq2apuh.js +43 -0
  91. package/codeyam-cli/src/webserver/build/client/assets/{circle-check-DFcQkN5j.js → circle-check-DLPObLUx.js} +1 -1
  92. package/codeyam-cli/src/webserver/build/client/assets/{copy-C6iF61Xs.js → copy-DXEmO0TD.js} +1 -1
  93. package/codeyam-cli/src/webserver/build/client/assets/{createLucideIcon-4ImjHTVC.js → createLucideIcon-BwyFiRot.js} +1 -1
  94. package/codeyam-cli/src/webserver/build/client/assets/{dev.empty-CRepiabR.js → dev.empty-iRhRIFlp.js} +1 -1
  95. package/codeyam-cli/src/webserver/build/client/assets/editor._tab-BZPBzV73.js +1 -0
  96. package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-DhtVC4aI.js +161 -0
  97. package/codeyam-cli/src/webserver/build/client/assets/{editorPreview-CluPkvXJ.js → editorPreview-C6fEYHrh.js} +6 -6
  98. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-DYJRGiDI.js → entity._sha._-pc-vc6wO.js} +1 -1
  99. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.dev-wdiwx5-Z.js → entity._sha.scenarios._scenarioId.dev-C8AyYgYT.js} +1 -1
  100. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.fullscreen-BrkN-40Y.js → entity._sha.scenarios._scenarioId.fullscreen-DziaVQX1.js} +1 -1
  101. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.create-scenario-DxfhekTZ.js → entity._sha_.create-scenario-BTcpgIpC.js} +1 -1
  102. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-CRXJWmpB.js → entity._sha_.edit._scenarioId-D_O_ajfZ.js} +1 -1
  103. package/codeyam-cli/src/webserver/build/client/assets/{entry.client-SuW9syRS.js → entry.client-j1Vi0bco.js} +6 -6
  104. package/codeyam-cli/src/webserver/build/client/assets/{files-D-xGrg29.js → files-kuny2Q_s.js} +1 -1
  105. package/codeyam-cli/src/webserver/build/client/assets/{git-Bq_fbXP5.js → git-DgCZPMie.js} +1 -1
  106. package/codeyam-cli/src/webserver/build/client/assets/globals-L-aUIeux.css +1 -0
  107. package/codeyam-cli/src/webserver/build/client/assets/{index-Bp1l4hSv.js → index-BliGSSpl.js} +1 -1
  108. package/codeyam-cli/src/webserver/build/client/assets/{index-DE3jI_dv.js → index-SqjQKTdH.js} +1 -1
  109. package/codeyam-cli/src/webserver/build/client/assets/{index-CWV9XZiG.js → index-vyrZD2g4.js} +1 -1
  110. package/codeyam-cli/src/webserver/build/client/assets/{labs-B_IX45ih.js → labs-c3yLxSEp.js} +1 -1
  111. package/codeyam-cli/src/webserver/build/client/assets/{loader-circle-De-7qQ2u.js → loader-circle-D-q28GLF.js} +1 -1
  112. package/codeyam-cli/src/webserver/build/client/assets/manifest-79d0d81a.js +1 -0
  113. package/codeyam-cli/src/webserver/build/client/assets/{memory-Cx2xEx7s.js → memory-CEWIUC4t.js} +1 -1
  114. package/codeyam-cli/src/webserver/build/client/assets/{pause-CFxEKL1u.js → pause-BP6fitdh.js} +1 -1
  115. package/codeyam-cli/src/webserver/build/client/assets/{root-DGtly3mb.js → root-L2V0jea7.js} +4 -4
  116. package/codeyam-cli/src/webserver/build/client/assets/{search-BdBb5aqc.js → search-BooqacKS.js} +1 -1
  117. package/codeyam-cli/src/webserver/build/client/assets/{settings-DdE-Untf.js → settings-BM0nbryO.js} +1 -1
  118. package/codeyam-cli/src/webserver/build/client/assets/{simulations-DSCdE99u.js → simulations-ovy6FjRY.js} +1 -1
  119. package/codeyam-cli/src/webserver/build/client/assets/{terminal-CrplD4b1.js → terminal-DHemCJIs.js} +1 -1
  120. package/codeyam-cli/src/webserver/build/client/assets/{triangle-alert-DqJ0j69l.js → triangle-alert-D87ekDl8.js} +1 -1
  121. package/codeyam-cli/src/webserver/build/client/assets/{useCustomSizes-DhXHbEjP.js → useCustomSizes-Dk0Tciqg.js} +1 -1
  122. package/codeyam-cli/src/webserver/build/client/assets/{useLastLogLine-D9QZKaLJ.js → useLastLogLine-C8QvIe05.js} +1 -1
  123. package/codeyam-cli/src/webserver/build/client/assets/{useReportContext-Cy5Qg_UR.js → useReportContext-jkCytuYz.js} +1 -1
  124. package/codeyam-cli/src/webserver/build/client/assets/{useToast-5HR2j9ZE.js → useToast-BgqkixU9.js} +1 -1
  125. package/codeyam-cli/src/webserver/build/server/assets/{analysisRunner-CO8xocj3.js → analysisRunner-QgInFGdU.js} +1 -1
  126. package/codeyam-cli/src/webserver/build/server/assets/{index-QKPqlUgg.js → index-zblh9auj.js} +1 -1
  127. package/codeyam-cli/src/webserver/build/server/assets/init-DaE0CBjk.js +14 -0
  128. package/codeyam-cli/src/webserver/build/server/assets/server-build-CNvgz1cC.js +853 -0
  129. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  130. package/codeyam-cli/src/webserver/build-info.json +5 -5
  131. package/codeyam-cli/src/webserver/editorProxy.js +255 -20
  132. package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
  133. package/codeyam-cli/src/webserver/server.js +67 -0
  134. package/codeyam-cli/src/webserver/server.js.map +1 -1
  135. package/codeyam-cli/src/webserver/terminalServer.js +102 -11
  136. package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
  137. package/codeyam-cli/templates/codeyam-editor-codex.md +61 -0
  138. package/codeyam-cli/templates/codeyam-editor-gemini.md +59 -0
  139. package/codeyam-cli/templates/editor-step-hook.py +4 -4
  140. package/codeyam-cli/templates/expo-react-native/MOBILE_SETUP.md +89 -5
  141. package/codeyam-cli/templates/expo-react-native/app/_layout.tsx +4 -2
  142. package/codeyam-cli/templates/expo-react-native/app/index.tsx +36 -0
  143. package/codeyam-cli/templates/expo-react-native/app.json +11 -0
  144. package/codeyam-cli/templates/expo-react-native/babel.config.js +1 -0
  145. package/codeyam-cli/templates/expo-react-native/gitignore +2 -0
  146. package/codeyam-cli/templates/expo-react-native/package.json +24 -18
  147. package/codeyam-cli/templates/expo-react-native/patches/expo-modules-autolinking+3.0.24.patch +29 -0
  148. package/codeyam-cli/templates/nextjs-prisma-sqlite/gitignore +1 -0
  149. package/codeyam-cli/templates/seed-adapters/supabase.ts +261 -77
  150. package/package.json +1 -1
  151. package/codeyam-cli/src/webserver/build/client/assets/chunk-JZWAC4HX-BAdwhyCx.js +0 -43
  152. package/codeyam-cli/src/webserver/build/client/assets/editor._tab-Gbk_i5Js.js +0 -1
  153. package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-DLM1-ZMt.js +0 -96
  154. package/codeyam-cli/src/webserver/build/client/assets/globals-9EkC9j9I.css +0 -1
  155. package/codeyam-cli/src/webserver/build/client/assets/manifest-7e749098.js +0 -1
  156. package/codeyam-cli/src/webserver/build/server/assets/init-DlspChIk.js +0 -10
  157. package/codeyam-cli/src/webserver/build/server/assets/server-build-ChzicV-B.js +0 -689
  158. package/codeyam-cli/templates/expo-react-native/app/(tabs)/_layout.tsx +0 -33
  159. package/codeyam-cli/templates/expo-react-native/app/(tabs)/index.tsx +0 -12
  160. package/codeyam-cli/templates/expo-react-native/app/(tabs)/settings.tsx +0 -12
@@ -24,7 +24,9 @@ import { buildEditorApiRequest, callEditorApi, EDITOR_API_SUBCOMMANDS, } from ".
24
24
  import { parseRegisterArg } from "../utils/parseRegisterArg.js";
25
25
  import { classifyRegistrationResult } from "../utils/registerScenarioResult.js";
26
26
  import { sanitizeGlossaryEntries } from "../utils/editorLoaderHelpers.js";
27
+ import { updateHandoffProgress } from "../utils/handoffContext.js";
27
28
  import { readMigrationState, writeMigrationState, advanceToNextPage, completePage, getMigrationResumeInfo, } from "../utils/editorMigration.js";
29
+ import { getDisplayVersion } from "../utils/versionInfo.js";
28
30
  const __filename = fileURLToPath(import.meta.url);
29
31
  const __dirname = path.dirname(__filename);
30
32
  const STEP_LABELS = {
@@ -227,6 +229,143 @@ function getTechStackContext(root) {
227
229
  : 'FEATURE_PATTERNS.md',
228
230
  };
229
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
+ }
230
369
  /**
231
370
  * Print dimension guidance when the project has multiple screen sizes.
232
371
  * Tells Claude to pick the right dimension for the content being previewed
@@ -250,11 +389,63 @@ function checkbox(text) {
250
389
  const highlighted = text.replace(/`([^`]+)`/g, (_m, code) => chalk.cyan(code));
251
390
  console.log(` ${chalk.dim('[ ]')} ${highlighted}`);
252
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
+ }
253
436
  /**
254
437
  * Instructions for creating/updating .codeyam/data-structure.json.
255
438
  * Only prints creation instructions if the file doesn't exist yet.
256
439
  * If it exists, reminds Claude to update it if data models changed.
257
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
+ }
258
449
  function printDataStructureInstructions() {
259
450
  const root = getProjectRoot();
260
451
  const dsPath = path.join(root, '.codeyam', 'data-structure.json');
@@ -305,6 +496,10 @@ function printAppScenarioInstructions(pageName, route) {
305
496
  console.log(chalk.dim(' Example: "Page - Full Data", "Page - Empty State", "Page - Error State"'));
306
497
  }
307
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();
308
503
  checkbox('Register each scenario with type "application", the real page URL, and pageFilePath:');
309
504
  console.log(chalk.dim(` codeyam editor register '{"name":"${pageName || 'Page'} - Full Data",`));
310
505
  console.log(chalk.dim(` "type":"application","url":"${route || '/'}","pageFilePath":"src/path/to/Page.tsx",...}'`));
@@ -372,6 +567,12 @@ function printExtractionPlanInstructions() {
372
567
  console.log(chalk.yellow(' Every component that renders multiple distinct sections should be split into sub-components.'));
373
568
  console.log(chalk.yellow(' If a component has N visually distinct parts, it should compose N sub-components.'));
374
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();
375
576
  console.log(chalk.bold('Checklist:'));
376
577
  checkbox('Read `.codeyam/glossary.json` — note reusable functions/components');
377
578
  checkbox('Read EVERY file used by this page/feature');
@@ -382,6 +583,12 @@ function printExtractionPlanInstructions() {
382
583
  console.log(chalk.yellow(' Functions: data transforms, calculations, formatting, validation,'));
383
584
  console.log(chalk.yellow(' API response shaping, any logic that is not directly about rendering'));
384
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'));
385
592
  console.log();
386
593
  checkbox('Write a numbered extraction plan listing EVERYTHING you will extract');
387
594
  console.log(chalk.yellow(' The end state: every page file is ONLY imports + component composition.'));
@@ -392,6 +599,15 @@ function printExtractionPlanInstructions() {
392
599
  console.log(chalk.yellow(' — What it is (component, function, hook)'));
393
600
  console.log(chalk.yellow(' — Where it currently lives (source file + approximate lines)'));
394
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.'));
395
611
  console.log();
396
612
  console.log(chalk.dim('Present the numbered plan, then proceed to step 7 to execute it.'));
397
613
  }
@@ -402,9 +618,15 @@ function printExtractionPlanInstructions() {
402
618
  function printComponentCaptureInstructions(root) {
403
619
  const ctx = root ? getTechStackContext(root) : undefined;
404
620
  const isExpo = ctx?.isExpo ?? false;
405
- checkbox('Create isolation route dirs: `codeyam editor isolate ComponentA ComponentB ...`');
406
- console.log(chalk.dim(` This creates app/isolated-components/layout.tsx (with production ${isExpo ? '__DEV__' : 'notFound()'} guard) and`));
407
- console.log(chalk.dim(' a directory per component. List ALL components that need isolation routes.'));
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
+ }
408
630
  checkbox('For each visual component:');
409
631
  console.log(chalk.dim(' 1. Read the source AND find where it is used in the app to understand:'));
410
632
  console.log(chalk.dim(' — Props/interface'));
@@ -805,8 +1027,8 @@ function printSetup(root) {
805
1027
  console.log(chalk.bold('Tech Stack Selection:'));
806
1028
  console.log(chalk.dim(' Based on the selected formats, present ONLY the matching tech stacks.'));
807
1029
  console.log(chalk.dim(' A stack matches if ANY of the user\'s selected formats appears in its "Supports" list.'));
808
- console.log(chalk.dim(' Show ALL matching stacks even if there is only one. Mark the recommended option.'));
809
- 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.'));
810
1032
  console.log();
811
1033
  console.log(chalk.bold(' Available Tech Stacks:'));
812
1034
  for (const stack of TECH_STACKS) {
@@ -998,6 +1220,15 @@ function printStep1(root, feature, options, userPrompt) {
998
1220
  console.log(chalk.dim(' -H "Content-Type: application/json" \\'));
999
1221
  console.log(chalk.dim(' -d \'{"projectTitle":"My App","projectDescription":"A short description of what this app does"}\''));
1000
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();
1001
1232
  const designSystem = readDesignSystem(root);
1002
1233
  if (designSystem) {
1003
1234
  console.log(chalk.bold.magenta('Design System (from .codeyam/design-system.md):'));
@@ -1011,6 +1242,8 @@ function printStep1(root, feature, options, userPrompt) {
1011
1242
  console.log(chalk.yellow(' Option 2 label: "I\'d like some changes"') +
1012
1243
  chalk.dim(' — user describes changes, you revise the plan, then re-present'));
1013
1244
  console.log();
1245
+ checkboxHandoff();
1246
+ console.log();
1014
1247
  console.log(chalk.dim('This step is for understanding user goals and getting buy-in. Code comes in Step 2.'));
1015
1248
  console.log();
1016
1249
  console.log(chalk.bold.red('━━━ STOP ━━━'));
@@ -1075,6 +1308,21 @@ function printStep2(root, feature) {
1075
1308
  console.log();
1076
1309
  console.log(chalk.dim(' Read MOBILE_SETUP.md for data storage, navigation, and testing patterns.'));
1077
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();
1078
1326
  }
1079
1327
  else if (ctx.isChromeExt) {
1080
1328
  // Chrome Extension: use chrome.storage
@@ -1123,6 +1371,10 @@ function printStep2(root, feature) {
1123
1371
  console.log();
1124
1372
  printDataStructureInstructions();
1125
1373
  }
1374
+ console.log();
1375
+ printServiceRecordingReminder(port);
1376
+ console.log();
1377
+ checkboxHandoff();
1126
1378
  stopGate(2);
1127
1379
  }
1128
1380
  // ─── Step 3: Prototype ────────────────────────────────────────────────
@@ -1209,6 +1461,10 @@ function printStep3(root, feature) {
1209
1461
  console.log(chalk.dim(' Bad: color: "#333", fontSize: 14, padding: 12'));
1210
1462
  console.log(chalk.dim(' Good: color: theme.colors.textPrimary, fontSize: theme.fontSize.sm, padding: theme.spacing.md'));
1211
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.'));
1212
1468
  }
1213
1469
  else {
1214
1470
  checkbox('Define ALL design tokens as CSS custom properties in globals.css — not just colors');
@@ -1235,6 +1491,9 @@ function printStep3(root, feature) {
1235
1491
  console.log(chalk.red.bold(' NEVER claim "you should see X" or "the preview shows X" unless you just ran a preview command.'));
1236
1492
  console.log(chalk.red(' The preview only updates when you explicitly refresh it. Verify, then describe.'));
1237
1493
  console.log();
1494
+ printServiceRecordingReminder(port);
1495
+ console.log();
1496
+ checkboxHandoff();
1238
1497
  stopGate(3);
1239
1498
  }
1240
1499
  // ─── Step 4: Verify Prototype ─────────────────────────────────────────
@@ -1249,6 +1508,8 @@ function printStep4(root, feature) {
1249
1508
  label: STEP_LABELS[4],
1250
1509
  startedAt: isResuming ? prevState.startedAt : now,
1251
1510
  featureStartedAt: prevState?.featureStartedAt || now,
1511
+ appFormats: prevState?.appFormats,
1512
+ techStackId: prevState?.techStackId,
1252
1513
  });
1253
1514
  logEvent(root, 'step', { step: 4, label: 'Verify Prototype', feature });
1254
1515
  stepHeader(4, 'Verify Prototype', feature);
@@ -1290,6 +1551,8 @@ function printStep4(root, feature) {
1290
1551
  console.log(chalk.dim(' A new clone should work with just: git clone → npm run setup → npm run dev'));
1291
1552
  console.log();
1292
1553
  console.log(chalk.dim('Focus on building the prototype. Scenarios and refactoring happen in later steps.'));
1554
+ console.log();
1555
+ checkboxHandoff();
1293
1556
  stopGate(4);
1294
1557
  }
1295
1558
  // ─── Step 5: Confirm ──────────────────────────────────────────────────
@@ -1305,6 +1568,8 @@ function printStep5(root, feature) {
1305
1568
  label: STEP_LABELS[5],
1306
1569
  startedAt: isResuming ? prevState.startedAt : now,
1307
1570
  featureStartedAt: prevState?.featureStartedAt || now,
1571
+ appFormats: prevState?.appFormats,
1572
+ techStackId: prevState?.techStackId,
1308
1573
  });
1309
1574
  logEvent(root, 'step', { step: 5, label: 'Confirm', feature });
1310
1575
  stepHeader(5, 'Confirm', feature);
@@ -1351,6 +1616,8 @@ function printStep5(root, feature) {
1351
1616
  chalk.dim(' — make changes, refresh preview, re-run `codeyam editor 5`'));
1352
1617
  console.log();
1353
1618
  console.log(chalk.dim('Wait for user approval before moving on. Refactoring and scenarios happen in later steps.'));
1619
+ console.log();
1620
+ checkboxHandoff();
1354
1621
  stopGate(5, { confirm: true });
1355
1622
  }
1356
1623
  // ─── Step 6: Deconstruct ──────────────────────────────────────────────
@@ -1364,6 +1631,8 @@ function printStep6(root, feature) {
1364
1631
  label: STEP_LABELS[6],
1365
1632
  startedAt: isResuming ? prevState.startedAt : now,
1366
1633
  featureStartedAt: prevState?.featureStartedAt || now,
1634
+ appFormats: prevState?.appFormats,
1635
+ techStackId: prevState?.techStackId,
1367
1636
  });
1368
1637
  logEvent(root, 'step', { step: 6, label: 'Deconstruct', feature });
1369
1638
  stepHeader(6, 'Deconstruct', feature);
@@ -1374,6 +1643,8 @@ function printStep6(root, feature) {
1374
1643
  console.log(chalk.yellow('This step is read and plan only. Code extraction happens in step 7.'));
1375
1644
  console.log();
1376
1645
  printExtractionPlanInstructions();
1646
+ console.log();
1647
+ checkboxHandoff();
1377
1648
  stopGate(6);
1378
1649
  }
1379
1650
  // ─── Step 7: Extract ──────────────────────────────────────────────────
@@ -1389,6 +1660,8 @@ function printStep7(root, feature) {
1389
1660
  label: STEP_LABELS[7],
1390
1661
  startedAt: isResuming ? prevState.startedAt : now,
1391
1662
  featureStartedAt: prevState?.featureStartedAt || now,
1663
+ appFormats: prevState?.appFormats,
1664
+ techStackId: prevState?.techStackId,
1392
1665
  });
1393
1666
  logEvent(root, 'step', { step: 7, label: 'Extract', feature });
1394
1667
  stepHeader(7, 'Extract', feature);
@@ -1408,7 +1681,11 @@ function printStep7(root, feature) {
1408
1681
  checkbox('For each function/hook: write MULTIPLE failing tests FIRST, then extract to make them pass');
1409
1682
  console.log(chalk.dim(' Cover: typical inputs, edge cases, empty/null inputs, error conditions'));
1410
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'));
1411
1687
  console.log(chalk.dim(' Hooks count as functions — useDrinks, useAuth, etc. all need test files'));
1688
+ console.log(chalk.dim(' Wrap all tests in a describe("FunctionName", ...) block — the audit matches on this name'));
1412
1689
  if (ctx.isExpo) {
1413
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`');
1414
1691
  }
@@ -1423,6 +1700,23 @@ function printStep7(root, feature) {
1423
1700
  console.log(chalk.yellow(` Check: does any file contain raw ${ctx.rawPrimitivesList}?`));
1424
1701
  console.log(chalk.yellow(' If yes → that JSX section is a component waiting to be extracted.'));
1425
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();
1426
1720
  console.log(chalk.bold('Verify before proceeding:'));
1427
1721
  checkbox('Run all tests and verify they pass');
1428
1722
  checkbox(`Page files contain ONLY imports + component composition — no raw ${ctx.isExpo ? 'React Native primitives' : 'HTML tags'}`);
@@ -1433,6 +1727,8 @@ function printStep7(root, feature) {
1433
1727
  console.log(chalk.dim('Reuse glossary functions when they fit naturally. Extract a new function when the use case diverges.'));
1434
1728
  console.log();
1435
1729
  console.log(chalk.dim('Focus on TDD for functions and extraction for components. Scenarios come in later steps.'));
1730
+ console.log();
1731
+ checkboxHandoff();
1436
1732
  stopGate(7);
1437
1733
  }
1438
1734
  // ─── Step 8: Glossary ─────────────────────────────────────────────────
@@ -1446,6 +1742,8 @@ function printStep8(root, feature) {
1446
1742
  label: STEP_LABELS[8],
1447
1743
  startedAt: isResuming ? prevState.startedAt : now,
1448
1744
  featureStartedAt: prevState?.featureStartedAt || now,
1745
+ appFormats: prevState?.appFormats,
1746
+ techStackId: prevState?.techStackId,
1449
1747
  });
1450
1748
  logEvent(root, 'step', { step: 8, label: 'Glossary', feature });
1451
1749
  stepHeader(8, 'Glossary', feature);
@@ -1472,6 +1770,8 @@ function printStep9(root, feature) {
1472
1770
  label: STEP_LABELS[9],
1473
1771
  startedAt: isResuming ? prevState.startedAt : now,
1474
1772
  featureStartedAt: prevState?.featureStartedAt || now,
1773
+ appFormats: prevState?.appFormats,
1774
+ techStackId: prevState?.techStackId,
1475
1775
  });
1476
1776
  logEvent(root, 'step', { step: 9, label: 'Analyze', feature });
1477
1777
  stepHeader(9, 'Analyze and Verify', feature);
@@ -1497,6 +1797,15 @@ function printStep9(root, feature) {
1497
1797
  console.log();
1498
1798
  console.log(chalk.dim('Do not proceed until both component isolations and library tests pass.'));
1499
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();
1500
1809
  checkbox('Run `codeyam editor audit` to verify all components have scenarios and all functions/hooks have tests');
1501
1810
  console.log(chalk.red.bold(' The audit is a HARD GATE — step 10 will refuse to run until the audit passes.'));
1502
1811
  console.log(chalk.dim(' The audit auto-fixes incomplete entities by running targeted analysis on just the affected files.'));
@@ -1522,6 +1831,8 @@ function printStep10(root, feature) {
1522
1831
  label: STEP_LABELS[10],
1523
1832
  startedAt: isResuming ? prevState.startedAt : now,
1524
1833
  featureStartedAt: prevState?.featureStartedAt || now,
1834
+ appFormats: prevState?.appFormats,
1835
+ techStackId: prevState?.techStackId,
1525
1836
  });
1526
1837
  logEvent(root, 'step', { step: 10, label: 'App Scenarios', feature });
1527
1838
  stepHeader(10, 'App Scenarios', feature);
@@ -1586,6 +1897,8 @@ function printStep11(root, feature) {
1586
1897
  label: STEP_LABELS[11],
1587
1898
  startedAt: isResuming ? prevState.startedAt : now,
1588
1899
  featureStartedAt: prevState?.featureStartedAt || now,
1900
+ appFormats: prevState?.appFormats,
1901
+ techStackId: prevState?.techStackId,
1589
1902
  });
1590
1903
  logEvent(root, 'step', { step: 11, label: 'User Scenarios', feature });
1591
1904
  stepHeader(11, 'User Scenarios', feature);
@@ -1632,6 +1945,8 @@ function printStep12(root, feature) {
1632
1945
  label: STEP_LABELS[12],
1633
1946
  startedAt: isResuming ? prevState.startedAt : now,
1634
1947
  featureStartedAt: prevState?.featureStartedAt || now,
1948
+ appFormats: prevState?.appFormats,
1949
+ techStackId: prevState?.techStackId,
1635
1950
  });
1636
1951
  logEvent(root, 'step', { step: 12, label: 'Verify', feature });
1637
1952
  stepHeader(12, 'Verify', feature);
@@ -1678,6 +1993,8 @@ function printStep13(root, feature) {
1678
1993
  label: STEP_LABELS[13],
1679
1994
  startedAt: isResuming ? prevState.startedAt : now,
1680
1995
  featureStartedAt: prevState?.featureStartedAt || now,
1996
+ appFormats: prevState?.appFormats,
1997
+ techStackId: prevState?.techStackId,
1681
1998
  });
1682
1999
  logEvent(root, 'step', { step: 13, label: 'Journal', feature });
1683
2000
  stepHeader(13, 'Journal', feature);
@@ -1711,6 +2028,8 @@ function printStep14(root, feature) {
1711
2028
  label: STEP_LABELS[14],
1712
2029
  startedAt: isResuming ? prevState.startedAt : now,
1713
2030
  featureStartedAt: prevState?.featureStartedAt || now,
2031
+ appFormats: prevState?.appFormats,
2032
+ techStackId: prevState?.techStackId,
1714
2033
  });
1715
2034
  logEvent(root, 'step', { step: 14, label: 'Review', feature });
1716
2035
  stepHeader(14, 'Review', feature);
@@ -1730,6 +2049,8 @@ function printStep14(root, feature) {
1730
2049
  checkbox('Fix or remove any broken images before continuing');
1731
2050
  checkbox('Recapture stale scenarios: `codeyam editor recapture-stale`');
1732
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": {...}}'`));
1733
2054
  checkbox('Do not proceed until all checks pass');
1734
2055
  stopGate(14);
1735
2056
  }
@@ -1744,6 +2065,8 @@ function printStep15(root, feature) {
1744
2065
  label: STEP_LABELS[15],
1745
2066
  startedAt: isResuming ? prevState.startedAt : now,
1746
2067
  featureStartedAt: prevState?.featureStartedAt || now,
2068
+ appFormats: prevState?.appFormats,
2069
+ techStackId: prevState?.techStackId,
1747
2070
  });
1748
2071
  logEvent(root, 'step', { step: 15, label: 'Present', feature });
1749
2072
  stepHeader(15, 'Present', feature);
@@ -2393,6 +2716,8 @@ function printStep16(root, feature) {
2393
2716
  label: STEP_LABELS[16],
2394
2717
  startedAt: isResuming ? prevState.startedAt : now,
2395
2718
  featureStartedAt: prevState?.featureStartedAt || now,
2719
+ appFormats: prevState?.appFormats,
2720
+ techStackId: prevState?.techStackId,
2396
2721
  });
2397
2722
  logEvent(root, 'step', { step: 16, label: 'Commit', feature });
2398
2723
  stepHeader(16, 'Commit', feature);
@@ -2419,6 +2744,8 @@ function printStep17(root, feature) {
2419
2744
  label: STEP_LABELS[17],
2420
2745
  startedAt: isResuming ? prevState.startedAt : now,
2421
2746
  featureStartedAt: prevState?.featureStartedAt || now,
2747
+ appFormats: prevState?.appFormats,
2748
+ techStackId: prevState?.techStackId,
2422
2749
  });
2423
2750
  logEvent(root, 'step', { step: 17, label: 'Finalize', feature });
2424
2751
  stepHeader(17, 'Finalize', feature);
@@ -2444,6 +2771,8 @@ function printStep18(root, feature) {
2444
2771
  label: STEP_LABELS[18],
2445
2772
  startedAt: isResuming ? prevState.startedAt : now,
2446
2773
  featureStartedAt: prevState?.featureStartedAt || now,
2774
+ appFormats: prevState?.appFormats,
2775
+ techStackId: prevState?.techStackId,
2447
2776
  });
2448
2777
  logEvent(root, 'step', { step: 18, label: 'Push', feature });
2449
2778
  stepHeader(18, 'Push', feature);
@@ -2461,11 +2790,11 @@ function printStep18(root, feature) {
2461
2790
  console.log(chalk.dim(' If the user wants help, guide them through `gh repo create` or manual GitHub setup, then push.'));
2462
2791
  console.log(chalk.dim(' Task management is handled by the ━━━ TASK ━━━ directive below.'));
2463
2792
  console.log();
2464
- console.log(chalk.bold.yellow('IMPORTANT: After this step, the current feature is DONE.'));
2465
- 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.'));
2466
2797
  console.log(chalk.yellow(' Do NOT use `codeyam editor change` or start building directly.'));
2467
- console.log(chalk.yellow(' Ask the user what they want to build. Once they tell you, run `codeyam editor 1` yourself.'));
2468
- console.log(chalk.yellow(' NEVER tell the user to run `codeyam editor` commands — these are internal. Just ask what to build.'));
2469
2798
  console.log(chalk.yellow(' The change workflow is ONLY for modifications during steps 1-15, not after commit.'));
2470
2799
  stopGate(18, { confirm: true });
2471
2800
  }
@@ -2884,8 +3213,13 @@ function handleIsolate(componentNames) {
2884
3213
  process.exit(1);
2885
3214
  }
2886
3215
  const isolateDir = path.join(root, 'app', 'isolated-components');
2887
- // Create layout.tsx with production guard if missing
3216
+ // Create the framework-appropriate layout guard if missing.
3217
+ // Clean up wrong-framework layout from a previous CLI version.
2888
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
+ }
2889
3223
  if (!fs.existsSync(layoutPath)) {
2890
3224
  fs.mkdirSync(isolateDir, { recursive: true });
2891
3225
  if (ctx.isExpo) {
@@ -2938,7 +3272,13 @@ function handleIsolate(componentNames) {
2938
3272
  }
2939
3273
  }
2940
3274
  if (created.length > 0) {
2941
- console.log(chalk.green(`Created ${created.length} isolation route(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
+ }
2942
3282
  }
2943
3283
  if (existed.length > 0) {
2944
3284
  console.log(chalk.dim(`Already existed: ${existed.join(', ')}`));
@@ -3079,6 +3419,50 @@ function formatApiSubcommandResult(subcommand, data) {
3079
3419
  return null; // journal-list, show/hide-results: keep full JSON
3080
3420
  }
3081
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
+ }
3082
3466
  /**
3083
3467
  * `codeyam editor register '{"name":"...","componentName":"...",...}'`
3084
3468
  *
@@ -3947,14 +4331,20 @@ async function handleAudit(options) {
3947
4331
  let detail;
3948
4332
  switch (f.status) {
3949
4333
  case 'ok':
3950
- 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
+ }
3951
4340
  break;
3952
4341
  case 'runner_error':
3953
4342
  detail = chalk.red(` — test runner crashed: ${f.testFile}`);
3954
4343
  if (f.errorMessage) {
3955
4344
  detail += `\n ${chalk.red(f.errorMessage)}`;
3956
4345
  detail += `\n ${chalk.yellow('Note: This is NOT a test failure — the test runner itself could not execute.')}`;
3957
- 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)}`;
3958
4348
  }
3959
4349
  break;
3960
4350
  case 'failing':
@@ -3979,6 +4369,9 @@ async function handleAudit(options) {
3979
4369
  }
3980
4370
  console.log(` ${icon} ${f.name}${detail}`);
3981
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
+ }
3982
4375
  console.log();
3983
4376
  }
3984
4377
  // Missing from glossary
@@ -4552,6 +4945,10 @@ async function handleTemplate() {
4552
4945
  if (stack?.supportedFormats) {
4553
4946
  config.appFormats = stack.supportedFormats;
4554
4947
  }
4948
+ // Pre-populate tech stack from template
4949
+ if (stack) {
4950
+ config.techStack = getTechStackForTemplate(stack.id);
4951
+ }
4555
4952
  // Set mobile-first defaults for mobile-app projects
4556
4953
  if (stack?.supportedFormats?.includes('mobile-app')) {
4557
4954
  config.defaultScreenSize = {
@@ -5224,6 +5621,11 @@ const editorCommand = {
5224
5621
  }
5225
5622
  return;
5226
5623
  }
5624
+ // Subcommand: codeyam editor handoff '{"summary":"..."}'
5625
+ if (argv.step === 'handoff') {
5626
+ await handleHandoff(argv.json || '');
5627
+ return;
5628
+ }
5227
5629
  // Subcommand: codeyam editor register '{"name":"..."}'
5228
5630
  if (argv.step === 'register') {
5229
5631
  await handleRegister(argv.json || '');
@@ -5406,10 +5808,18 @@ const editorCommand = {
5406
5808
  try {
5407
5809
  const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
5408
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
+ }
5409
5818
  if (!projectSlug) {
5410
5819
  errorLog('Missing project slug. Try reinitializing with: `codeyam editor template`');
5411
5820
  return;
5412
5821
  }
5822
+ console.log(chalk.dim(` CodeYam Editor v${getDisplayVersion()}`));
5413
5823
  const connectionOk = await withoutSpinner(() => testEnvironment());
5414
5824
  if (!connectionOk) {
5415
5825
  errorLog('Environment validation failed');
@@ -5821,6 +6231,9 @@ const editorCommand = {
5821
6231
  process.exit(1);
5822
6232
  }
5823
6233
  }
6234
+ printHandoffContext(root);
6235
+ // Track step progress automatically for provider handoff
6236
+ updateHandoffProgress(root, step);
5824
6237
  switch (step) {
5825
6238
  case 1: {
5826
6239
  const feature = argv.feature || undefined;