@codeyam/codeyam-cli 0.1.0-staging.9574237 → 0.1.0-staging.a77070e

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 (153) hide show
  1. package/analyzer-template/.build-info.json +6 -6
  2. package/analyzer-template/log.txt +3 -3
  3. package/codeyam-cli/src/cli.js +9 -0
  4. package/codeyam-cli/src/cli.js.map +1 -1
  5. package/codeyam-cli/src/commands/__tests__/editor.isolateArgs.test.js +51 -0
  6. package/codeyam-cli/src/commands/__tests__/editor.isolateArgs.test.js.map +1 -0
  7. package/codeyam-cli/src/commands/editor.js +481 -72
  8. package/codeyam-cli/src/commands/editor.js.map +1 -1
  9. package/codeyam-cli/src/commands/editorIsolateArgs.js +25 -0
  10. package/codeyam-cli/src/commands/editorIsolateArgs.js.map +1 -0
  11. package/codeyam-cli/src/commands/init.js +1 -0
  12. package/codeyam-cli/src/commands/init.js.map +1 -1
  13. package/codeyam-cli/src/commands/telemetry.js +37 -0
  14. package/codeyam-cli/src/commands/telemetry.js.map +1 -0
  15. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +1534 -1
  16. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
  17. package/codeyam-cli/src/utils/__tests__/editorCaptureScenarioSeeding.test.js +137 -0
  18. package/codeyam-cli/src/utils/__tests__/editorCaptureScenarioSeeding.test.js.map +1 -0
  19. package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js +70 -0
  20. package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js.map +1 -1
  21. package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js +97 -4
  22. package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js.map +1 -1
  23. package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js +70 -0
  24. package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js.map +1 -1
  25. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +185 -7
  26. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
  27. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js +48 -1
  28. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js.map +1 -1
  29. package/codeyam-cli/src/utils/__tests__/glossaryAdd.test.js +177 -0
  30. package/codeyam-cli/src/utils/__tests__/glossaryAdd.test.js.map +1 -0
  31. package/codeyam-cli/src/utils/__tests__/scenarioCoverage.test.js +57 -0
  32. package/codeyam-cli/src/utils/__tests__/scenarioCoverage.test.js.map +1 -1
  33. package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js +180 -1
  34. package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js.map +1 -1
  35. package/codeyam-cli/src/utils/__tests__/telemetry.test.js +159 -0
  36. package/codeyam-cli/src/utils/__tests__/telemetry.test.js.map +1 -0
  37. package/codeyam-cli/src/utils/backgroundServer.js +1 -1
  38. package/codeyam-cli/src/utils/backgroundServer.js.map +1 -1
  39. package/codeyam-cli/src/utils/editorAudit.js +287 -1
  40. package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
  41. package/codeyam-cli/src/utils/editorEntityChangeStatus.js +13 -7
  42. package/codeyam-cli/src/utils/editorEntityChangeStatus.js.map +1 -1
  43. package/codeyam-cli/src/utils/editorEntityHelpers.js +18 -3
  44. package/codeyam-cli/src/utils/editorEntityHelpers.js.map +1 -1
  45. package/codeyam-cli/src/utils/editorScenarioSwitch.js +24 -2
  46. package/codeyam-cli/src/utils/editorScenarioSwitch.js.map +1 -1
  47. package/codeyam-cli/src/utils/editorScenarios.js +73 -12
  48. package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
  49. package/codeyam-cli/src/utils/entityChangeStatus.js +9 -3
  50. package/codeyam-cli/src/utils/entityChangeStatus.js.map +1 -1
  51. package/codeyam-cli/src/utils/fileWatcher.js +38 -0
  52. package/codeyam-cli/src/utils/fileWatcher.js.map +1 -1
  53. package/codeyam-cli/src/utils/glossaryAdd.js +74 -0
  54. package/codeyam-cli/src/utils/glossaryAdd.js.map +1 -0
  55. package/codeyam-cli/src/utils/install-skills.js +5 -0
  56. package/codeyam-cli/src/utils/install-skills.js.map +1 -1
  57. package/codeyam-cli/src/utils/scenarioCoverage.js +4 -1
  58. package/codeyam-cli/src/utils/scenarioCoverage.js.map +1 -1
  59. package/codeyam-cli/src/utils/scenariosManifest.js +36 -0
  60. package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -1
  61. package/codeyam-cli/src/utils/telemetry.js +106 -0
  62. package/codeyam-cli/src/utils/telemetry.js.map +1 -0
  63. package/codeyam-cli/src/utils/telemetryMiddleware.js +22 -0
  64. package/codeyam-cli/src/utils/telemetryMiddleware.js.map +1 -0
  65. package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js +40 -0
  66. package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js.map +1 -1
  67. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js +61 -0
  68. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js.map +1 -1
  69. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js +92 -21
  70. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js.map +1 -1
  71. package/codeyam-cli/src/webserver/app/lib/clientErrors.js +7 -1
  72. package/codeyam-cli/src/webserver/app/lib/clientErrors.js.map +1 -1
  73. package/codeyam-cli/src/webserver/backgroundServer.js +42 -57
  74. package/codeyam-cli/src/webserver/backgroundServer.js.map +1 -1
  75. package/codeyam-cli/src/webserver/build/client/assets/{CopyButton-CzTDWkF2.js → CopyButton-CLe80MMu.js} +1 -1
  76. package/codeyam-cli/src/webserver/build/client/assets/{EntityItem-BFbq6iFk.js → EntityItem-Crt_KN_U.js} +1 -1
  77. package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeIcon-B6OMi58N.js → EntityTypeIcon-CD7lGABo.js} +1 -1
  78. package/codeyam-cli/src/webserver/build/client/assets/{InlineSpinner-DuYodzo1.js → InlineSpinner-CgTNOhnu.js} +1 -1
  79. package/codeyam-cli/src/webserver/build/client/assets/{InteractivePreview-CXo9EeCl.js → InteractivePreview-CKeQT5Ty.js} +2 -2
  80. package/codeyam-cli/src/webserver/build/client/assets/{LibraryFunctionPreview-DYCNb2It.js → LibraryFunctionPreview-D3s1MFkb.js} +1 -1
  81. package/codeyam-cli/src/webserver/build/client/assets/{LogViewer-CZgY3sxX.js → LogViewer-CM5zg40N.js} +1 -1
  82. package/codeyam-cli/src/webserver/build/client/assets/{ReportIssueModal-CnYYwRDw.js → ReportIssueModal-C2PLkej3.js} +1 -1
  83. package/codeyam-cli/src/webserver/build/client/assets/{SafeScreenshot-CDoF7ZpU.js → SafeScreenshot-DanvyBPb.js} +1 -1
  84. package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-DrnfvaLL.js → ScenarioViewer-DUMfcNVK.js} +1 -1
  85. package/codeyam-cli/src/webserver/build/client/assets/{Spinner-Df3UCi8k.js → Spinner-D0LgAaSa.js} +1 -1
  86. package/codeyam-cli/src/webserver/build/client/assets/{ViewportInspectBar-DRKR9T0U.js → ViewportInspectBar-BA_Ry-rs.js} +1 -1
  87. package/codeyam-cli/src/webserver/build/client/assets/{_index-ClR-g3tY.js → _index-BAWd-Xjf.js} +1 -1
  88. package/codeyam-cli/src/webserver/build/client/assets/{activity.(_tab)-DTH6ydEA.js → activity.(_tab)-BOARiB-g.js} +1 -1
  89. package/codeyam-cli/src/webserver/build/client/assets/{addon-web-links-74hnHF59.js → addon-web-links-CHx25PAe.js} +1 -1
  90. package/codeyam-cli/src/webserver/build/client/assets/{agent-transcripts-B8CYhCO9.js → agent-transcripts-Bg3e7q4S.js} +1 -1
  91. package/codeyam-cli/src/webserver/build/client/assets/{book-open-CLaoh4ac.js → book-open-CL-lMgHh.js} +1 -1
  92. package/codeyam-cli/src/webserver/build/client/assets/{chevron-down-BZ2DZxbW.js → chevron-down-GmAjGS9-.js} +1 -1
  93. package/codeyam-cli/src/webserver/build/client/assets/{chunk-JZWAC4HX-BBXArFPl.js → chunk-JZWAC4HX-BAdwhyCx.js} +11 -11
  94. package/codeyam-cli/src/webserver/build/client/assets/{circle-check-CT4unAk-.js → circle-check-DFcQkN5j.js} +1 -1
  95. package/codeyam-cli/src/webserver/build/client/assets/{copy-zK0B6Nu-.js → copy-C6iF61Xs.js} +1 -1
  96. package/codeyam-cli/src/webserver/build/client/assets/{createLucideIcon-DJB0YQJL.js → createLucideIcon-4ImjHTVC.js} +1 -1
  97. package/codeyam-cli/src/webserver/build/client/assets/{dev.empty-CkXFP_i-.js → dev.empty-C8y4mmyv.js} +1 -1
  98. package/codeyam-cli/src/webserver/build/client/assets/editor._tab-Gbk_i5Js.js +1 -0
  99. package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-Bnx7yUP0.js +58 -0
  100. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-oepecPae.js +41 -0
  101. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-BqAN7hyG.js → entity._sha._-Blfy9UlN.js} +1 -1
  102. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.dev-BOi8kpwd.js → entity._sha.scenarios._scenarioId.dev-KTQuL0aj.js} +1 -1
  103. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.fullscreen-Dg1NhIms.js → entity._sha.scenarios._scenarioId.fullscreen-C6eeL24i.js} +1 -1
  104. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.create-scenario-CJX6kkkV.js → entity._sha_.create-scenario-DQM8E7L4.js} +1 -1
  105. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-BhVjZhKg.js → entity._sha_.edit._scenarioId-CAoXLsQr.js} +1 -1
  106. package/codeyam-cli/src/webserver/build/client/assets/{entry.client-_gzKltPN.js → entry.client-SuW9syRS.js} +1 -1
  107. package/codeyam-cli/src/webserver/build/client/assets/{files-CV_17tZS.js → files-D-xGrg29.js} +1 -1
  108. package/codeyam-cli/src/webserver/build/client/assets/{git-D-YXmMbR.js → git-Bq_fbXP5.js} +1 -1
  109. package/codeyam-cli/src/webserver/build/client/assets/globals-fAqOD9ex.css +1 -0
  110. package/codeyam-cli/src/webserver/build/client/assets/{index-CCrgCshv.js → index-Bp1l4hSv.js} +1 -1
  111. package/codeyam-cli/src/webserver/build/client/assets/{index-BsX0F-9C.js → index-CWV9XZiG.js} +1 -1
  112. package/codeyam-cli/src/webserver/build/client/assets/{index-Blo6EK8G.js → index-DE3jI_dv.js} +1 -1
  113. package/codeyam-cli/src/webserver/build/client/assets/{labs-Byazq8Pv.js → labs-B_IX45ih.js} +1 -1
  114. package/codeyam-cli/src/webserver/build/client/assets/{loader-circle-DVQ0oHR7.js → loader-circle-De-7qQ2u.js} +1 -1
  115. package/codeyam-cli/src/webserver/build/client/assets/manifest-3157d6b8.js +1 -0
  116. package/codeyam-cli/src/webserver/build/client/assets/{memory-b-VmA2Vj.js → memory-Cx2xEx7s.js} +1 -1
  117. package/codeyam-cli/src/webserver/build/client/assets/{pause-DGcndCAa.js → pause-CFxEKL1u.js} +1 -1
  118. package/codeyam-cli/src/webserver/build/client/assets/root-DB3O9_9j.js +67 -0
  119. package/codeyam-cli/src/webserver/build/client/assets/{search-C0Uw0bcK.js → search-BdBb5aqc.js} +1 -1
  120. package/codeyam-cli/src/webserver/build/client/assets/{settings-OoNgHIfW.js → settings-DdE-Untf.js} +1 -1
  121. package/codeyam-cli/src/webserver/build/client/assets/{simulations-Bcemfu8a.js → simulations-DSCdE99u.js} +1 -1
  122. package/codeyam-cli/src/webserver/build/client/assets/{terminal-BgMmG7R9.js → terminal-CrplD4b1.js} +1 -1
  123. package/codeyam-cli/src/webserver/build/client/assets/{triangle-alert-Cs87hJYK.js → triangle-alert-DqJ0j69l.js} +1 -1
  124. package/codeyam-cli/src/webserver/build/client/assets/{useCustomSizes-BR3Rs7JY.js → useCustomSizes-DhXHbEjP.js} +1 -1
  125. package/codeyam-cli/src/webserver/build/client/assets/{useLastLogLine-BxxP_XF9.js → useLastLogLine-BNd5hYuW.js} +1 -1
  126. package/codeyam-cli/src/webserver/build/client/assets/{useReportContext-BermyNU5.js → useReportContext-Cy5Qg_UR.js} +1 -1
  127. package/codeyam-cli/src/webserver/build/client/assets/{useToast-a_QN_W9_.js → useToast-5HR2j9ZE.js} +1 -1
  128. package/codeyam-cli/src/webserver/build/server/assets/{analysisRunner-DXQyOV0G.js → analysisRunner-BMmkgAkg.js} +1 -1
  129. package/codeyam-cli/src/webserver/build/server/assets/{index-CHSrVJtC.js → index-DxB0pOSt.js} +1 -1
  130. package/codeyam-cli/src/webserver/build/server/assets/{init-DL8vWZ6m.js → init-DLYLaqqP.js} +2 -2
  131. package/codeyam-cli/src/webserver/build/server/assets/server-build-CcyitQLQ.js +551 -0
  132. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  133. package/codeyam-cli/src/webserver/build-info.json +5 -5
  134. package/codeyam-cli/src/webserver/editorProxy.js +78 -3
  135. package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
  136. package/codeyam-cli/src/webserver/idleDetector.js +41 -8
  137. package/codeyam-cli/src/webserver/idleDetector.js.map +1 -1
  138. package/codeyam-cli/src/webserver/scripts/journalCapture.ts +36 -0
  139. package/codeyam-cli/src/webserver/server.js +32 -0
  140. package/codeyam-cli/src/webserver/server.js.map +1 -1
  141. package/codeyam-cli/src/webserver/terminalServer.js +5 -5
  142. package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
  143. package/codeyam-cli/templates/codeyam-editor-reference.md +214 -0
  144. package/codeyam-cli/templates/nextjs-prisma-sqlite/package.json +1 -1
  145. package/codeyam-cli/templates/nextjs-prisma-supabase/package.json +1 -1
  146. package/package.json +2 -1
  147. package/codeyam-cli/src/webserver/build/client/assets/editor._tab-DPw7NZHc.js +0 -1
  148. package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-Dmg9cGK3.js +0 -58
  149. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-DBa7T2FK.js +0 -41
  150. package/codeyam-cli/src/webserver/build/client/assets/globals-Bqg9V6XV.css +0 -1
  151. package/codeyam-cli/src/webserver/build/client/assets/manifest-422a3551.js +0 -1
  152. package/codeyam-cli/src/webserver/build/client/assets/root-ue8uWVRS.js +0 -67
  153. package/codeyam-cli/src/webserver/build/server/assets/server-build-BUKVjBSZ.js +0 -501
@@ -205,28 +205,45 @@ function checkbox(text) {
205
205
  * Prints seed-based scenarios, mock-based fallback, @ file prefix, externalApis, url requirement, and error checking.
206
206
  */
207
207
  function printAppScenarioInstructions(pageName, route) {
208
+ const root = process.cwd();
209
+ const hasSeedAdapter = !!detectSeedAdapter(root);
208
210
  checkbox('Check existing scenarios: `codeyam editor scenarios`');
209
211
  console.log(chalk.dim(' Review existing scenarios — reuse and update their data rather than'));
210
212
  console.log(chalk.dim(' creating duplicates. Re-register with the same name to update a scenario.'));
211
- checkbox('Cover key data states (at least 2-3 scenarios per page)');
212
- console.log(chalk.yellow(' Each page needs its own scenarios — a detail page needs different data than the catalog'));
213
+ console.log();
214
+ console.log(chalk.bold.yellow('App scenarios vs component scenarios — these are DIFFERENT:'));
215
+ console.log(chalk.yellow(' App scenarios: show the FULL PAGE with seeded data at a real route (/, /library, etc.)'));
216
+ console.log(chalk.yellow(' Component scenarios: show ONE COMPONENT in isolation at /isolated-components/...'));
217
+ console.log(chalk.yellow(' This step is about APP scenarios. Do NOT set "componentName" — that makes it a component scenario.'));
218
+ console.log();
219
+ checkbox('Identify every page/route in the app and ensure each has app-level scenarios');
220
+ console.log(chalk.dim(" Check the app's router/entry points for all distinct pages"));
221
+ console.log(chalk.yellow(' Every page needs at least 2-3 app scenarios — not just the main page'));
213
222
  if (pageName) {
214
223
  console.log(chalk.dim(` Example: "${pageName} - Full Data", "${pageName} - Empty State"`));
215
224
  }
216
225
  else {
217
- console.log(chalk.dim(' Catalog: "Full Catalog", "Empty Catalog", "Single Category"'));
218
- console.log(chalk.dim(' Detail: "Detail - With Reviews", "Detail - No Reviews", "Detail - High Rated"'));
226
+ console.log(chalk.dim(' Example: "Page - Full Data", "Page - Empty State", "Page - Error State"'));
219
227
  }
220
228
  console.log();
221
- checkbox('Register each scenario with type "application" and the real page url:');
222
- console.log(chalk.dim(` codeyam editor register '{"name":"${pageName || 'Page'} - Full Data","type":"application","url":"${route || '/'}",...}'`));
223
- console.log(chalk.yellow(' IMPORTANT: "url" MUST be the real page route without it, the wrong page gets screenshotted.'));
224
- console.log(chalk.yellow(' IMPORTANT: include "pageFilePath" with the source file that renders this route (e.g., "src/App.tsx").'));
229
+ checkbox('Register each scenario with type "application", the real page URL, and pageFilePath:');
230
+ console.log(chalk.dim(` codeyam editor register '{"name":"${pageName || 'Page'} - Full Data",`));
231
+ console.log(chalk.dim(` "type":"application","url":"${route || '/'}","pageFilePath":"src/path/to/Page.tsx",...}'`));
232
+ console.log(chalk.yellow(' Required fields: "type":"application", "url" (real route), "pageFilePath" (source file)'));
233
+ console.log(chalk.yellow(' Do NOT include "componentName" — that would make it a component scenario'));
225
234
  console.log();
226
- checkbox('Choose the right data approach for scenarios:');
227
- console.log(chalk.dim(' With seed adapter: add "seed":{...} to populate the database for each state'));
228
- console.log(chalk.dim(' Without seed adapter: add "mockData":{"routes":{"/api/...":{"body":[...]}}} to mock API responses'));
229
- console.log(chalk.dim(' For external APIs (Stripe, weather, etc.): add "externalApis":{"GET https://...":{"body":[...],"status":200}}'));
235
+ if (hasSeedAdapter) {
236
+ checkbox(chalk.bold.red('REQUIRED: Every app scenario MUST include "seed" data without it the page will be empty!'));
237
+ console.log(chalk.yellow(' A seed adapter exists — you MUST include "seed":{...} in every app scenario registration.'));
238
+ console.log(chalk.yellow(' An app scenario without seed data will render an empty page (no database rows = nothing to show).'));
239
+ console.log(chalk.dim(' "seed" maps table names to arrays of rows: "seed":{"user":[{"id":1,"name":"Alice"}],"article":[...]}'));
240
+ console.log(chalk.dim(' For external APIs: also add "externalApis":{"GET https://...":{"body":[...],"status":200}}'));
241
+ }
242
+ else {
243
+ checkbox('Include data in every app scenario — without it the page will be empty:');
244
+ console.log(chalk.dim(' Use "mockData":{"routes":{"/api/...":{"body":[...]}}} to mock API responses'));
245
+ console.log(chalk.dim(' For external APIs: add "externalApis":{"GET https://...":{"body":[...],"status":200}}'));
246
+ }
230
247
  checkbox('For large seed/mock data, write JSON to a temp file and use @ prefix:');
231
248
  console.log(chalk.dim(' Write JSON to .codeyam/tmp/scenario.json then register with:'));
232
249
  console.log(chalk.dim(' codeyam editor register @.codeyam/tmp/scenario.json'));
@@ -313,7 +330,7 @@ function printComponentCaptureInstructions() {
313
330
  console.log(chalk.dim(' The route defines a `scenarios` object mapping scenario names to props,'));
314
331
  console.log(chalk.dim(' reads `?s=ScenarioName` from the URL, and renders the component with those props.'));
315
332
  console.log(chalk.dim(' Wrap the component in a capture container with id="codeyam-capture":'));
316
- console.log(chalk.dim(' <div id="codeyam-capture" style={{ display:"inline-block", padding:"20px" }}>'));
333
+ console.log(chalk.dim(' <div id="codeyam-capture" style={{ display:"inline-block" }}>'));
317
334
  console.log(chalk.dim(' <div style={{ width:"100%", maxWidth:"..." }}> ← match the app\'s container width'));
318
335
  console.log(chalk.dim(' e.g. card in a 3-col grid → maxWidth:"24rem", full-width component → omit maxWidth'));
319
336
  console.log(chalk.dim(' The screenshot captures just this wrapper, so the component fills the image.'));
@@ -324,7 +341,8 @@ function printComponentCaptureInstructions() {
324
341
  console.log(chalk.dim(` "componentName":"ComponentName","componentPath":"path/to/file.tsx",`));
325
342
  console.log(chalk.dim(` "url":"/codeyam-isolate/ComponentName?s=Scenario",`));
326
343
  console.log(chalk.dim(` "mockData":{"routes":{"/api/...":{"body":[...]}}}}'`));
327
- console.log(chalk.dim(' url is a PATH (starts with /) — the proxy routes it and intercepts API calls'));
344
+ console.log(chalk.yellow(' IMPORTANT: url MUST be an isolation route (/codeyam-isolate/... or /isolated-components/...).'));
345
+ console.log(chalk.yellow(' Do NOT use real page URLs (like /library) for component scenarios.'));
328
346
  console.log(chalk.dim(' mockData.routes provides data for API calls the component makes internally'));
329
347
  console.log(chalk.dim(' (omit mockData if the component has no internal API calls)'));
330
348
  console.log(chalk.yellow(' 5. IMPORTANT: Check the register response for `clientErrors` array'));
@@ -792,6 +810,7 @@ function printCycleOverview(root, state) {
792
810
  console.log(chalk.green('Start now: ') + chalk.bold('codeyam editor 1'));
793
811
  console.log(chalk.dim(' If the user already described what they want, pass it: codeyam editor 1 --prompt "their message"'));
794
812
  }
813
+ console.log(chalk.dim('If stuck on CLI commands, scenarios, or debugging, read: .codeyam/docs/editor-reference.md'));
795
814
  console.log();
796
815
  }
797
816
  // ─── Step 1: Plan ─────────────────────────────────────────────────────
@@ -931,13 +950,29 @@ function printStep2(root, feature) {
931
950
  console.log();
932
951
  console.log(chalk.bold('Build the feature:'));
933
952
  }
953
+ else {
954
+ console.log(chalk.bold('Prepare the database for this feature:'));
955
+ checkbox('List existing scenarios: `codeyam editor scenarios`');
956
+ console.log(chalk.dim(' Review existing scenarios to find seed data that best demonstrates the feature.'));
957
+ console.log(chalk.dim(" Don't create throwaway seed data — use what's already been curated."));
958
+ checkbox('Pick the scenario with seed data most relevant to this feature');
959
+ console.log(chalk.dim(" Think: which existing data state best exercises the feature you're building?"));
960
+ console.log(chalk.dim(' A scenario with diverse, realistic data is usually the best starting point.'));
961
+ checkbox('Load that scenario to seed the database and preview it:');
962
+ console.log(chalk.dim(` codeyam editor preview '{"scenarioId":"<id>","dimension":"${dim}"}'`));
963
+ console.log(chalk.dim(' This switches the active scenario, runs the seed adapter, and refreshes the preview.'));
964
+ console.log(chalk.dim(' If no existing scenario fits, use the default seed: npm run db:seed'));
965
+ console.log();
966
+ }
934
967
  console.log(chalk.bold('Checklist:'));
935
968
  checkbox('Create API routes that read from the database via Prisma');
936
- checkbox('Seed the database with demo data');
937
- checkbox('Create `.codeyam/seed-adapter.ts` so CodeYam can seed the database for scenarios');
938
- console.log(chalk.dim(' The seed adapter reads a JSON file (path passed as CLI arg), wipes tables, inserts rows.'));
939
- console.log(chalk.dim(" Use the project's own ORM (Prisma, Drizzle, etc.). See template for example."));
940
- console.log(chalk.dim(' Run with: npx tsx .codeyam/seed-adapter.ts <path-to-seed-data.json>'));
969
+ if (!projectExists) {
970
+ checkbox('Seed the database with demo data');
971
+ checkbox('Create `.codeyam/seed-adapter.ts` so CodeYam can seed the database for scenarios');
972
+ console.log(chalk.dim(' The seed adapter reads a JSON file (path passed as CLI arg), wipes tables, inserts rows.'));
973
+ console.log(chalk.dim(" Use the project's own ORM (Prisma, Drizzle, etc.). See template for example."));
974
+ console.log(chalk.dim(' Run with: npx tsx .codeyam/seed-adapter.ts <path-to-seed-data.json>'));
975
+ }
941
976
  checkbox('Verify the dev server shows the changes');
942
977
  checkbox('If the feature involves auth, email, payments, or other common patterns: read FEATURE_PATTERNS.md');
943
978
  // Responsive design guidance when building a mobile-responsive web app
@@ -1420,8 +1455,6 @@ function printStep12(root, feature) {
1420
1455
  }
1421
1456
  // ─── Step 13: Present ─────────────────────────────────────────────────
1422
1457
  function printStep13(root, feature) {
1423
- const port = getServerPort();
1424
- const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
1425
1458
  const prevState = readState(root);
1426
1459
  const isResuming = prevState?.step === 13;
1427
1460
  const now = new Date().toISOString();
@@ -1440,18 +1473,9 @@ function printStep13(root, feature) {
1440
1473
  console.log('Present the results to the user and get their approval.');
1441
1474
  console.log();
1442
1475
  console.log(chalk.bold('Checklist:'));
1443
- checkbox('Fetch all scenarios: `codeyam editor scenarios`');
1444
- console.log(chalk.dim(' Each scenario has a `changeStatus` field: "new", "edited", "impacted", or null.'));
1445
- checkbox('Pick the best scenario to showcase from this working session:');
1446
- console.log(chalk.dim(' Prefer scenarios with changeStatus "new" or "edited" (directly changed in this session).'));
1447
- console.log(chalk.dim(' For app-level scenarios, prefer those showing the new/changed functionality.'));
1448
- console.log(chalk.dim(' NEVER pick a scenario with changeStatus null — those are unchanged from a previous commit.'));
1449
- checkbox('Switch the preview to that scenario using its `id` and `dimension`:');
1450
- console.log(chalk.dim(` codeyam editor preview '{"scenarioId":"<id>","dimension":"${dim}"}'`));
1451
- console.log(chalk.dim(' Use the dimension that matches the scenario — check its registered dimensions.'));
1452
- printDimensionGuidance(dim, dimNames);
1453
1476
  checkbox(`Show the results panel: \`codeyam editor show-results\``);
1454
1477
  console.log(chalk.dim(' This opens a visual panel below the terminal showing all scenarios with screenshots.'));
1478
+ console.log(chalk.dim(' The preview automatically switches to the first app-level scenario.'));
1455
1479
  console.log(chalk.dim(' The user can click scenarios to switch the live preview.'));
1456
1480
  checkbox('Write a 1-2 sentence summary of what was built');
1457
1481
  checkbox('Report test count and audit status (one line)');
@@ -1681,7 +1705,6 @@ function printMigrateStep4(root) {
1681
1705
  }
1682
1706
  const { pageName, pageIndex, totalPages } = pageInfo;
1683
1707
  writeMigrationStepState(root, 4, pageName, pageIndex, totalPages);
1684
- const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
1685
1708
  console.log();
1686
1709
  console.log(chalk.bold.cyan(`━━━ Migration Step 4: Preview — ${pageName} (${pageIndex + 1}/${totalPages}) ━━━`));
1687
1710
  console.log();
@@ -1691,11 +1714,8 @@ function printMigrateStep4(root) {
1691
1714
  checkbox('Review all scenario screenshots for this page — do they look correct?');
1692
1715
  checkbox('Check for client errors: `codeyam editor client-errors`');
1693
1716
  checkbox('If any issues: fix the scenario data, re-register affected scenarios');
1694
- checkbox('Fetch all scenarios: `codeyam editor scenarios`');
1695
- checkbox('Pick the best scenario to showcase:');
1696
- console.log(chalk.dim(` codeyam editor preview '{"scenarioId":"<id>","dimension":"${dim}"}'`));
1697
- printDimensionGuidance(dim, dimNames);
1698
1717
  checkbox('Show results: `codeyam editor show-results`');
1718
+ console.log(chalk.dim(' The preview automatically switches to the first app-level scenario.'));
1699
1719
  console.log();
1700
1720
  console.log(chalk.dim('The user should now see their existing page with live screenshots in the preview.'));
1701
1721
  migrationStopGate(4, pageName, pageIndex, totalPages);
@@ -1855,18 +1875,14 @@ function printMigrateStep10(root) {
1855
1875
  }
1856
1876
  const { pageName, pageIndex, totalPages } = pageInfo;
1857
1877
  writeMigrationStepState(root, 10, pageName, pageIndex, totalPages);
1858
- const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
1859
1878
  console.log();
1860
1879
  console.log(chalk.bold.cyan(`━━━ Migration Step 10: Present — ${pageName} (${pageIndex + 1}/${totalPages}) ━━━`));
1861
1880
  console.log();
1862
1881
  console.log("Show results and commit this page's migration.");
1863
1882
  console.log();
1864
1883
  console.log(chalk.bold('Checklist:'));
1865
- checkbox('Fetch all scenarios: `codeyam editor scenarios`');
1866
- checkbox('Pick the best scenario to showcase:');
1867
- console.log(chalk.dim(` codeyam editor preview '{"scenarioId":"<id>","dimension":"${dim}"}'`));
1868
- printDimensionGuidance(dim, dimNames);
1869
1884
  checkbox('Show results: `codeyam editor show-results`');
1885
+ console.log(chalk.dim(' The preview automatically switches to the first app-level scenario.'));
1870
1886
  checkbox('Write a 1-2 sentence summary of what was migrated');
1871
1887
  console.log();
1872
1888
  console.log(chalk.bold('Present a selection menu (AskUserQuestion with these EXACT option labels):'));
@@ -2339,6 +2355,8 @@ async function handleAnalyzeImports(options = {}) {
2339
2355
  sha: e.sha,
2340
2356
  name: e.name,
2341
2357
  filePath: e.filePath || '',
2358
+ isDefaultExport: e.metadata?.notExported === false &&
2359
+ e.metadata?.namedExport === false,
2342
2360
  })));
2343
2361
  if (backfillResult.updated > 0 && !options.silent) {
2344
2362
  console.log(chalk.green(`Linked ${backfillResult.updated} scenario(s) to their entities.`));
@@ -2644,6 +2662,13 @@ async function handleRegister(jsonArg) {
2644
2662
  if (data.captureError)
2645
2663
  parts.push(chalk.yellow(`captureError="${data.captureError}"`));
2646
2664
  console.log(prefix + parts.join(' '));
2665
+ // Warn when application scenario is missing seed data
2666
+ if (data.missingSeedWarning) {
2667
+ console.log(chalk.red.bold('WARNING: No seed data in this application scenario!'));
2668
+ console.log(chalk.yellow(' This scenario has type "application" but no "seed" data was provided.'));
2669
+ console.log(chalk.yellow(' The page will render with an empty database — nothing will be visible.'));
2670
+ console.log(chalk.yellow(' Re-register with "seed":{...} containing the database rows this page needs.'));
2671
+ }
2647
2672
  // Surface client errors prominently so they can't be missed
2648
2673
  if (data.clientErrors && data.clientErrors.length > 0) {
2649
2674
  console.log(chalk.red.bold(`⚠ WARNING: ${data.clientErrors.length} client error${data.clientErrors.length !== 1 ? 's' : ''} detected during capture:`));
@@ -2651,6 +2676,14 @@ async function handleRegister(jsonArg) {
2651
2676
  console.log(chalk.red(` → ${err}`));
2652
2677
  }
2653
2678
  console.log(chalk.yellow(' The screenshot may show an error screen instead of the actual component.'));
2679
+ // Provide actionable debugging hints for HTTP/API errors
2680
+ const hasApiResponseError = data.clientErrors.some((e) => e.includes('API response error:'));
2681
+ const hasHttpError = data.clientErrors.some((e) => e.includes('HTTP error:'));
2682
+ if (hasHttpError || hasApiResponseError) {
2683
+ console.log(chalk.yellow(' To debug: look at the "API response error" lines above — they show the exact API endpoint'));
2684
+ console.log(chalk.yellow(' that failed and what the server returned. Then check the scenario seed data to ensure'));
2685
+ console.log(chalk.yellow(' all IDs referenced in the URL exist in the seed, and that the route is correct.'));
2686
+ }
2654
2687
  console.log(chalk.yellow(' Fix the issue and re-register this scenario.'));
2655
2688
  }
2656
2689
  if (!res.ok) {
@@ -2676,6 +2709,53 @@ async function handleRegister(jsonArg) {
2676
2709
  process.exit(1);
2677
2710
  }
2678
2711
  }
2712
+ // ─── Glossary-add subcommand ──────────────────────────────────────────
2713
+ /**
2714
+ * `codeyam editor glossary-add '{"name":"...", "filePath":"...", "description":"..."}'`
2715
+ *
2716
+ * Safely adds/updates entries in .codeyam/glossary.json via the CLI,
2717
+ * avoiding hand-editing that breaks on unicode characters.
2718
+ */
2719
+ async function handleGlossaryAdd(jsonArg) {
2720
+ if (!jsonArg) {
2721
+ console.error(chalk.red('Error: JSON argument required.'));
2722
+ console.error(chalk.dim(' Usage: codeyam editor glossary-add \'{"name":"DrinkCard","filePath":"app/components/DrinkCard.tsx","description":"Displays a drink item"}\''));
2723
+ console.error(chalk.dim(' For large payloads: codeyam editor glossary-add @.codeyam/tmp/entry.json'));
2724
+ process.exit(1);
2725
+ }
2726
+ const parsed = parseRegisterArg(jsonArg);
2727
+ if (parsed.error) {
2728
+ console.error(chalk.red(`Error: ${parsed.error}`));
2729
+ process.exit(1);
2730
+ }
2731
+ // Normalize to array
2732
+ const items = Array.isArray(parsed.body) ? parsed.body : [parsed.body];
2733
+ // Read existing glossary
2734
+ const root = getProjectRoot();
2735
+ const glossaryPath = path.join(root, '.codeyam', 'glossary.json');
2736
+ let existing = [];
2737
+ try {
2738
+ const raw = JSON.parse(fs.readFileSync(glossaryPath, 'utf8'));
2739
+ existing = sanitizeGlossaryEntries(raw);
2740
+ }
2741
+ catch {
2742
+ // Glossary doesn't exist yet or can't be parsed — start fresh
2743
+ }
2744
+ // Merge
2745
+ const { validateGlossaryEntry, mergeGlossaryEntries } = await import('../utils/glossaryAdd.js');
2746
+ const result = mergeGlossaryEntries(existing, items);
2747
+ // Report validation errors
2748
+ for (const err of result.errors) {
2749
+ console.error(chalk.red(`Error at index ${err.index}: ${err.message}`));
2750
+ }
2751
+ if (result.added === 0 && result.updated === 0 && result.errors.length > 0) {
2752
+ process.exit(1);
2753
+ }
2754
+ // Write back with utf8 encoding to safely handle unicode
2755
+ fs.mkdirSync(path.dirname(glossaryPath), { recursive: true });
2756
+ fs.writeFileSync(glossaryPath, JSON.stringify(result.entries, null, 2), 'utf8');
2757
+ console.log(`added=${result.added} updated=${result.updated} total=${result.entries.length}`);
2758
+ }
2679
2759
  // ─── Dependents subcommand ────────────────────────────────────────────
2680
2760
  /**
2681
2761
  * `codeyam editor dependents <EntityName>`
@@ -2907,23 +2987,150 @@ function handleChange(feature) {
2907
2987
  }
2908
2988
  // ─── Audit gate ─────────────────────────────────────────────────────
2909
2989
  /**
2910
- * Silently check whether the audit passes. Returns true if allPassing,
2911
- * false if any issues remain or the server is unreachable.
2912
- * Used as a hard gate before steps 8+.
2990
+ * Fetch the audit result from the server.
2991
+ * Returns null if the server is unreachable.
2913
2992
  */
2914
- async function checkAuditGate() {
2993
+ async function fetchAuditResult() {
2915
2994
  const port = getServerPort();
2916
2995
  try {
2917
2996
  const res = await fetch(`http://localhost:${port}/api/editor-audit`);
2918
2997
  if (!res.ok)
2919
- return false;
2920
- const data = await res.json();
2921
- return data?.summary?.allPassing === true;
2998
+ return null;
2999
+ return await res.json();
2922
3000
  }
2923
3001
  catch {
2924
- // Server not running or unreachable — can't verify, so don't block
3002
+ return null;
3003
+ }
3004
+ }
3005
+ /**
3006
+ * Silently check whether the audit passes. Returns true if allPassing,
3007
+ * false if any issues remain or the server is unreachable.
3008
+ * Used as a hard gate before steps 8+.
3009
+ *
3010
+ * Auto-runs analyze-imports if the only failure is incomplete entities,
3011
+ * then re-checks once.
3012
+ */
3013
+ async function checkAuditGate() {
3014
+ const data = await fetchAuditResult();
3015
+ if (!data)
3016
+ return true; // Server unreachable — don't block
3017
+ if (data.summary?.allPassing === true)
2925
3018
  return true;
3019
+ // If incomplete entities are the only issue, auto-fix with analyze-imports (once)
3020
+ const { isAutoRemediable } = await import('../utils/editorAudit.js');
3021
+ if (isAutoRemediable(data.summary, false)) {
3022
+ try {
3023
+ await handleAnalyzeImports({ silent: true });
3024
+ }
3025
+ catch {
3026
+ return false;
3027
+ }
3028
+ // Re-check after fix
3029
+ const retry = await fetchAuditResult();
3030
+ if (retry?.summary?.allPassing === true)
3031
+ return true;
3032
+ }
3033
+ // Print specific failures so Claude knows what to fix without running audit separately
3034
+ printAuditGateFailures(data);
3035
+ return false;
3036
+ }
3037
+ /**
3038
+ * Print a concise summary of audit failures for the gate block message.
3039
+ * This gives Claude immediate context about what to fix without needing
3040
+ * to run `codeyam editor audit` as a separate step.
3041
+ */
3042
+ function printAuditGateFailures(data) {
3043
+ const s = data.summary;
3044
+ if (!s)
3045
+ return;
3046
+ const issues = [];
3047
+ if (s.componentsMissing > 0) {
3048
+ issues.push(`${s.componentsMissing} component(s) missing scenarios`);
3049
+ const missing = (data.components || []).filter((c) => c.status === 'missing');
3050
+ for (const c of missing) {
3051
+ issues.push(` → ${c.name}${c.filePath ? ` (${c.filePath})` : ''}`);
3052
+ }
3053
+ }
3054
+ if (s.componentsWithErrors > 0) {
3055
+ issues.push(`${s.componentsWithErrors} component(s) with client errors (browser API or runtime errors in captured scenarios)`);
3056
+ const withErrors = (data.components || []).filter((c) => c.status === 'has_errors');
3057
+ for (const c of withErrors) {
3058
+ issues.push(` → ${c.name}${c.filePath ? ` (${c.filePath})` : ''}`);
3059
+ }
3060
+ }
3061
+ if (s.functionsMissing > 0) {
3062
+ issues.push(`${s.functionsMissing} function(s) missing test files`);
3063
+ const missing = (data.functions || []).filter((f) => f.status === 'missing');
3064
+ for (const f of missing) {
3065
+ issues.push(` → ${f.name}${f.filePath ? ` (${f.filePath})` : ''}`);
3066
+ }
3067
+ }
3068
+ if (s.functionsFailing > 0) {
3069
+ issues.push(`${s.functionsFailing} function(s) with failing tests`);
3070
+ const failing = (data.functions || []).filter((f) => f.status === 'failing');
3071
+ for (const f of failing) {
3072
+ issues.push(` → ${f.name}${f.testFile ? ` (${f.testFile})` : ''}`);
3073
+ }
3074
+ }
3075
+ if (s.functionsRunnerError > 0) {
3076
+ issues.push(`${s.functionsRunnerError} function(s) with test runner errors (the runner crashed — not a test failure)`);
3077
+ const runnerErrors = (data.functions || []).filter((f) => f.status === 'runner_error');
3078
+ for (const f of runnerErrors) {
3079
+ issues.push(` → ${f.name}${f.testFile ? ` (${f.testFile})` : ''}`);
3080
+ }
3081
+ }
3082
+ if (s.functionsNameMismatch > 0) {
3083
+ issues.push(`${s.functionsNameMismatch} function(s) with test name mismatch`);
3084
+ const mismatch = (data.functions || []).filter((f) => f.status === 'name_mismatch');
3085
+ for (const f of mismatch) {
3086
+ issues.push(` → ${f.name}${f.testFile ? ` (${f.testFile})` : ''}`);
3087
+ }
3088
+ }
3089
+ if (s.missingFromGlossary > 0)
3090
+ issues.push(`${s.missingFromGlossary} file(s) with scenarios not in glossary`);
3091
+ if (s.incompleteEntities > 0) {
3092
+ const preCount = s.preExistingIncompleteEntities || 0;
3093
+ if (preCount > 0 && preCount === s.incompleteEntities) {
3094
+ issues.push(`${s.incompleteEntities} entity/entities need import analysis (pre-existing — not caused by your current changes, but must be fixed before proceeding)`);
3095
+ }
3096
+ else if (preCount > 0) {
3097
+ issues.push(`${s.incompleteEntities} entity/entities need import analysis (${preCount} pre-existing — not from your changes)`);
3098
+ }
3099
+ else {
3100
+ issues.push(`${s.incompleteEntities} entity/entities need import analysis`);
3101
+ }
2926
3102
  }
3103
+ if (s.miscategorizedScenarios > 0)
3104
+ issues.push(`${s.miscategorizedScenarios} component(s) using page URLs instead of isolation routes`);
3105
+ if (s.scenariosNeedingRecapture > 0)
3106
+ issues.push(`${s.scenariosNeedingRecapture} scenario(s) need recapture (dependency tree changed)`);
3107
+ if (issues.length > 0) {
3108
+ console.error(chalk.yellow('\nAudit failures:'));
3109
+ for (const issue of issues) {
3110
+ console.error(chalk.yellow(` • ${issue}`));
3111
+ }
3112
+ }
3113
+ // Surface client error details inline — these are the most common cause of
3114
+ // Claude looping because the errors require code fixes, not audit re-runs.
3115
+ if (data.components) {
3116
+ const withErrors = data.components.filter((c) => c.status === 'has_errors' && c.clientErrors?.length > 0);
3117
+ if (withErrors.length > 0) {
3118
+ console.error(chalk.yellow('\nClient errors found:'));
3119
+ for (const c of withErrors) {
3120
+ console.error(chalk.red(` ${c.name}:`));
3121
+ for (const err of c.clientErrors.slice(0, 3)) {
3122
+ console.error(chalk.red(` → ${err}`));
3123
+ }
3124
+ if (c.clientErrors.length > 3) {
3125
+ console.error(chalk.dim(` … and ${c.clientErrors.length - 3} more`));
3126
+ }
3127
+ }
3128
+ console.error(chalk.yellow('\nFix: Fix the code errors above, then re-capture the affected scenarios.'));
3129
+ console.error(chalk.yellow('If errors reference browser APIs (localStorage, sessionStorage, window, document),'));
3130
+ console.error(chalk.yellow('create a universal mock: codeyam detect-universal-mocks'));
3131
+ }
3132
+ }
3133
+ console.error(chalk.dim('\nRun `codeyam editor audit` for full details.\n'));
2927
3134
  }
2928
3135
  // ─── Audit subcommand ────────────────────────────────────────────────
2929
3136
  /**
@@ -2934,28 +3141,49 @@ async function checkAuditGate() {
2934
3141
  * have test files. Exits with code 1 if anything is missing.
2935
3142
  */
2936
3143
  async function handleAudit() {
2937
- const port = getServerPort();
2938
- const url = `http://localhost:${port}/api/editor-audit`;
2939
- let data;
2940
- try {
2941
- const res = await fetch(url);
2942
- if (!res.ok) {
2943
- console.error(chalk.red(`Error: Audit endpoint returned ${res.status}`));
2944
- process.exit(1);
2945
- }
2946
- data = await res.json();
2947
- }
2948
- catch (err) {
3144
+ let data = await fetchAuditResult();
3145
+ if (!data) {
2949
3146
  console.error(chalk.red('Error: Could not reach the CodeYam server. Is it running?'));
2950
- console.error(chalk.dim(` ${err.message}`));
2951
3147
  process.exit(1);
2952
3148
  }
3149
+ // Auto-fix incomplete entities — but only once.
3150
+ // If analyze-imports runs and entities are STILL incomplete, report clearly
3151
+ // instead of retrying. The analysis may have errors (e.g., stale entity
3152
+ // references from refactoring) that can't be fixed by re-running.
3153
+ const incompleteBeforeFix = data.incompleteEntities || [];
3154
+ let autoRemediationFailed = false;
3155
+ if (incompleteBeforeFix.length > 0) {
3156
+ console.log(chalk.dim(`Running import analysis for ${incompleteBeforeFix.length} incomplete entit${incompleteBeforeFix.length !== 1 ? 'ies' : 'y'}...`));
3157
+ try {
3158
+ await handleAnalyzeImports({ silent: true });
3159
+ }
3160
+ catch {
3161
+ // Fall through — the audit will still report them as incomplete
3162
+ }
3163
+ // Re-fetch audit results after the fix
3164
+ data = await fetchAuditResult();
3165
+ if (!data) {
3166
+ console.error(chalk.red('Error: Could not reach the CodeYam server after analyze-imports.'));
3167
+ process.exit(1);
3168
+ }
3169
+ // If entities are still incomplete after running analyze-imports,
3170
+ // flag it so we can show a clear message instead of looping
3171
+ const incompleteAfterFix = data.incompleteEntities || [];
3172
+ if (incompleteAfterFix.length > 0) {
3173
+ autoRemediationFailed = true;
3174
+ }
3175
+ }
2953
3176
  const { components, functions, summary } = data;
2954
3177
  console.log();
2955
3178
  console.log(chalk.bold.cyan('━━━ Editor Audit ━━━'));
2956
3179
  console.log();
2957
3180
  // Components
2958
3181
  if (components.length > 0) {
3182
+ // Build name frequency map for disambiguation
3183
+ const componentNameCounts = new Map();
3184
+ for (const c of components) {
3185
+ componentNameCounts.set(c.name, (componentNameCounts.get(c.name) || 0) + 1);
3186
+ }
2959
3187
  console.log(chalk.bold('Components (scenarios):'));
2960
3188
  for (const c of components) {
2961
3189
  const icon = c.status === 'ok' ? chalk.green('✓') : chalk.red('✗');
@@ -2969,7 +3197,11 @@ async function handleAudit() {
2969
3197
  else {
2970
3198
  detail = chalk.red(' — no scenarios registered');
2971
3199
  }
2972
- console.log(` ${icon} ${c.name}${detail}`);
3200
+ // Show file path for failing components always, for OK only when name is ambiguous
3201
+ const isDuplicate = (componentNameCounts.get(c.name) || 0) > 1;
3202
+ const showPath = c.status !== 'ok' || isDuplicate;
3203
+ const pathSuffix = showPath && c.filePath ? chalk.dim(` (${c.filePath})`) : '';
3204
+ console.log(` ${icon} ${c.name}${pathSuffix}${detail}`);
2973
3205
  if (c.clientErrors && c.clientErrors.length > 0) {
2974
3206
  for (const err of c.clientErrors.slice(0, 3)) {
2975
3207
  console.log(chalk.red(` → ${err}`));
@@ -2977,6 +3209,14 @@ async function handleAudit() {
2977
3209
  if (c.clientErrors.length > 3) {
2978
3210
  console.log(chalk.dim(` … and ${c.clientErrors.length - 3} more`));
2979
3211
  }
3212
+ // Detect browser API errors and provide actionable guidance
3213
+ const browserApiPattern = /\b(localStorage|sessionStorage|window\.|document\.|navigator\.|indexedDB|matchMedia|ResizeObserver|IntersectionObserver|MutationObserver)\b/;
3214
+ const hasBrowserApiErrors = c.clientErrors.some((err) => browserApiPattern.test(err));
3215
+ if (hasBrowserApiErrors) {
3216
+ console.log(chalk.yellow(` ⚠ These errors are caused by browser APIs that don't exist during server-side analysis.`));
3217
+ console.log(chalk.yellow(` Fix: Create a universal mock to stub the missing API. Run: codeyam detect-universal-mocks`));
3218
+ console.log(chalk.yellow(` DO NOT re-run the audit or analyze-imports — the error will persist until a mock is created.`));
3219
+ }
2980
3220
  }
2981
3221
  }
2982
3222
  console.log();
@@ -2991,6 +3231,14 @@ async function handleAudit() {
2991
3231
  case 'ok':
2992
3232
  detail = chalk.dim(` (${f.testFile})`);
2993
3233
  break;
3234
+ case 'runner_error':
3235
+ detail = chalk.red(` — test runner crashed: ${f.testFile}`);
3236
+ if (f.errorMessage) {
3237
+ detail += `\n ${chalk.red(f.errorMessage)}`;
3238
+ detail += `\n ${chalk.yellow('Note: This is NOT a test failure — the test runner itself could not execute.')}`;
3239
+ detail += `\n ${chalk.yellow('Try running the test manually: npx vitest run ' + f.testFile)}`;
3240
+ }
3241
+ break;
2994
3242
  case 'failing':
2995
3243
  detail = chalk.red(` — tests failing: ${f.testFile}`);
2996
3244
  break;
@@ -3018,6 +3266,69 @@ async function handleAudit() {
3018
3266
  console.log(chalk.yellow(' Add these to .codeyam/glossary.json and run `codeyam editor analyze-imports`'));
3019
3267
  console.log();
3020
3268
  }
3269
+ // Incomplete entities (scenarios without analyses)
3270
+ const incompleteEntities = data.incompleteEntities || [];
3271
+ if (incompleteEntities.length > 0) {
3272
+ console.log(chalk.bold('Incomplete entities (need import analysis):'));
3273
+ for (const e of incompleteEntities) {
3274
+ console.log(` ${chalk.red('✗')} ${e.name} — ${e.scenarioCount} scenario${e.scenarioCount !== 1 ? 's' : ''} but entity not analyzed`);
3275
+ }
3276
+ if (autoRemediationFailed) {
3277
+ console.log(chalk.red(' analyze-imports was run automatically but these entities are STILL incomplete.'));
3278
+ console.log(chalk.red(' DO NOT re-run analyze-imports or the audit in a loop — the result will be the same.'));
3279
+ console.log(chalk.yellow(' This means the analysis failed for these entities. Common causes:'));
3280
+ console.log(chalk.yellow(' • Entity code uses browser APIs (localStorage, window, document) — create a universal mock'));
3281
+ console.log(chalk.yellow(' • Source file was renamed/deleted but glossary still references the old path'));
3282
+ console.log(chalk.yellow(' • Entity has import errors that prevent analysis'));
3283
+ console.log(chalk.yellow(' To investigate: run `codeyam editor analyze-imports` (without --silent) to see the full error.'));
3284
+ console.log(chalk.yellow(' To fix browser API issues: run `codeyam detect-universal-mocks`'));
3285
+ }
3286
+ else {
3287
+ console.log(chalk.yellow(' Run `codeyam editor analyze-imports` to analyze these entities'));
3288
+ }
3289
+ console.log();
3290
+ }
3291
+ // Miscategorized scenarios (component scenarios using real page URLs)
3292
+ const miscategorizedScenarios = data.miscategorizedScenarios || [];
3293
+ if (miscategorizedScenarios.length > 0) {
3294
+ console.log(chalk.bold('Miscategorized scenarios (component with page URL):'));
3295
+ for (const m of miscategorizedScenarios) {
3296
+ console.log(` ${chalk.red('✗')} ${m.componentName} at ${chalk.dim(m.url)} — ${m.scenarioNames.length} scenario${m.scenarioNames.length !== 1 ? 's' : ''}`);
3297
+ for (const name of m.scenarioNames) {
3298
+ console.log(chalk.dim(` "${name}"`));
3299
+ }
3300
+ }
3301
+ console.log(chalk.yellow(' Component scenarios must use isolation routes (/isolated-components/...).'));
3302
+ console.log(chalk.yellow(' Either re-register as app scenarios (remove componentName, add pageFilePath)'));
3303
+ console.log(chalk.yellow(' or re-register with an isolation URL.'));
3304
+ console.log();
3305
+ }
3306
+ // Scenarios needing recapture (entity or dependency tree changed)
3307
+ const scenariosNeedingRecapture = data.scenariosNeedingRecapture || [];
3308
+ if (scenariosNeedingRecapture.length > 0) {
3309
+ console.log(chalk.bold('Scenarios needing recapture (dependency tree changed):'));
3310
+ for (const s of scenariosNeedingRecapture) {
3311
+ const reason = s.status.status === 'impacted' && s.status.impactedBy?.length
3312
+ ? `impacted by: ${s.status.impactedBy.map((d) => `${d.name} [${d.changeType}]`).join(', ')}`
3313
+ : `${s.status.status}`;
3314
+ console.log(` ${chalk.red('✗')} ${s.scenarioName} ${chalk.dim(`(${s.entityName} — ${reason})`)}`);
3315
+ }
3316
+ console.log(chalk.yellow(' Re-register these scenarios to capture updated screenshots.'));
3317
+ console.log();
3318
+ }
3319
+ // Duplicate glossary names (warning, not a failure)
3320
+ const duplicateNames = data.duplicateNames || [];
3321
+ if (duplicateNames.length > 0) {
3322
+ console.log(chalk.bold('Duplicate names in glossary (confusing for audit):'));
3323
+ for (const dn of duplicateNames) {
3324
+ console.log(` ${chalk.yellow('⚠')} "${dn.name}" appears ${dn.filePaths.length} times:`);
3325
+ for (const fp of dn.filePaths) {
3326
+ console.log(` ${fp}`);
3327
+ }
3328
+ }
3329
+ console.log(chalk.yellow(' Fix: remove duplicate entries or rename them to be unique in .codeyam/glossary.json'));
3330
+ console.log();
3331
+ }
3021
3332
  // Summary
3022
3333
  const allOk = summary.allPassing;
3023
3334
  if (allOk) {
@@ -3038,12 +3349,24 @@ async function handleAudit() {
3038
3349
  if (summary.functionsFailing > 0) {
3039
3350
  parts.push(`${summary.functionsFailing} function${summary.functionsFailing !== 1 ? 's' : ''} with failing tests`);
3040
3351
  }
3352
+ if (summary.functionsRunnerError > 0) {
3353
+ parts.push(`${summary.functionsRunnerError} function${summary.functionsRunnerError !== 1 ? 's' : ''} with test runner errors (not test failures — see details above)`);
3354
+ }
3041
3355
  if (summary.functionsNameMismatch > 0) {
3042
3356
  parts.push(`${summary.functionsNameMismatch} function${summary.functionsNameMismatch !== 1 ? 's' : ''} with test name mismatch (missing top-level describe)`);
3043
3357
  }
3044
3358
  if (summary.missingFromGlossary > 0) {
3045
3359
  parts.push(`${summary.missingFromGlossary} file${summary.missingFromGlossary !== 1 ? 's' : ''} with scenarios not in glossary`);
3046
3360
  }
3361
+ if (summary.incompleteEntities > 0) {
3362
+ parts.push(`${summary.incompleteEntities} entit${summary.incompleteEntities !== 1 ? 'ies' : 'y'} need import analysis`);
3363
+ }
3364
+ if (summary.miscategorizedScenarios > 0) {
3365
+ parts.push(`${summary.miscategorizedScenarios} component${summary.miscategorizedScenarios !== 1 ? 's' : ''} using page URLs instead of isolation routes`);
3366
+ }
3367
+ if (summary.scenariosNeedingRecapture > 0) {
3368
+ parts.push(`${summary.scenariosNeedingRecapture} scenario${summary.scenariosNeedingRecapture !== 1 ? 's' : ''} need recapture`);
3369
+ }
3047
3370
  console.log(chalk.red.bold('Audit failed: ') + parts.join(', '));
3048
3371
  }
3049
3372
  console.log();
@@ -3178,6 +3501,8 @@ async function handleScenarioCoverage() {
3178
3501
  sha: e.sha,
3179
3502
  name: e.name,
3180
3503
  filePath: e.filePath || '',
3504
+ isDefaultExport: e.metadata?.notExported === false &&
3505
+ e.metadata?.namedExport === false,
3181
3506
  })));
3182
3507
  }
3183
3508
  }
@@ -3723,7 +4048,9 @@ const editorCommand = {
3723
4048
  describe: 'Debug: output directory for the bundle',
3724
4049
  });
3725
4050
  }
3726
- return builder;
4051
+ // Allow extra positional args for subcommands like `isolate A B C`
4052
+ // without yargs rejecting them as unknown.
4053
+ return builder.strict(false);
3727
4054
  },
3728
4055
  handler: async (argv) => {
3729
4056
  const root = getProjectRoot();
@@ -3762,6 +4089,11 @@ const editorCommand = {
3762
4089
  await handleRegister(argv.json || '');
3763
4090
  return;
3764
4091
  }
4092
+ // Subcommand: codeyam editor glossary-add '{"name":"...", ...}'
4093
+ if (argv.step === 'glossary-add') {
4094
+ await handleGlossaryAdd(argv.json || '');
4095
+ return;
4096
+ }
3765
4097
  // Subcommand: codeyam editor analyze-imports
3766
4098
  if (argv.step === 'analyze-imports') {
3767
4099
  await handleAnalyzeImports();
@@ -3808,13 +4140,10 @@ const editorCommand = {
3808
4140
  return;
3809
4141
  }
3810
4142
  // Subcommand: codeyam editor isolate "StarRating CategoryBadge DrinkCard"
4143
+ // Also supports: codeyam editor isolate StarRating CategoryBadge DrinkCard
3811
4144
  if (argv.step === 'isolate') {
3812
- const names = [];
3813
- if (argv.json)
3814
- names.push(...argv.json.split(/[\s,]+/).filter(Boolean));
3815
- // Collect any extra positional args (yargs puts them in argv._)
3816
- const extras = argv._.filter((a) => typeof a === 'string' && a !== 'editor');
3817
- names.push(...extras);
4145
+ const { parseIsolateArgs } = await import('./editorIsolateArgs.js');
4146
+ const names = parseIsolateArgs(argv.json, argv._);
3818
4147
  handleIsolate(names);
3819
4148
  return;
3820
4149
  }
@@ -4002,6 +4331,41 @@ const editorCommand = {
4002
4331
  catch {
4003
4332
  // Non-fatal — migration failure shouldn't block editor startup
4004
4333
  }
4334
+ // Auto-seed on fresh clone: if no scenario has ever been activated
4335
+ // (active-scenario.json doesn't exist), seed the application database
4336
+ // with the first application scenario that has seed data.
4337
+ const activeScenarioPath = path.join(projectRoot, '.codeyam', 'active-scenario.json');
4338
+ if (!fs.existsSync(activeScenarioPath)) {
4339
+ try {
4340
+ const { getDatabase: getDb } = await import('../../../packages/database/index.js');
4341
+ const seedDb = getDb();
4342
+ const appScenario = await seedDb
4343
+ .selectFrom('editor_scenarios')
4344
+ .select(['id', 'name', 'type'])
4345
+ .where('project_id', '=', project.id)
4346
+ .where('type', '=', 'application')
4347
+ .orderBy('created_at', 'asc')
4348
+ .executeTakeFirst();
4349
+ if (appScenario) {
4350
+ const seedFile = path.join(projectRoot, '.codeyam', 'editor-scenarios', `${appScenario.id}.seed.json`);
4351
+ if (fs.existsSync(seedFile)) {
4352
+ const { switchActiveScenario } = await import('../utils/editorScenarioSwitch.js');
4353
+ const seedResult = await switchActiveScenario({
4354
+ scenarioId: appScenario.id,
4355
+ scenarioName: appScenario.name || undefined,
4356
+ scenarioType: appScenario.type || undefined,
4357
+ projectRoot,
4358
+ });
4359
+ if (seedResult.seedResult?.success) {
4360
+ console.log(chalk.green(` Auto-seeded database with scenario: ${appScenario.name || appScenario.id}`));
4361
+ }
4362
+ }
4363
+ }
4364
+ }
4365
+ catch {
4366
+ // Non-fatal — auto-seed is best-effort
4367
+ }
4368
+ }
4005
4369
  // `codeyam editor` (no step) always implies editor mode.
4006
4370
  // The empty-folder heuristic is no longer needed here — running this
4007
4371
  // command IS the signal. We still detect empty folders so that
@@ -4146,6 +4510,48 @@ const editorCommand = {
4146
4510
  }
4147
4511
  }
4148
4512
  }
4513
+ // Backfill page_file_path for application scenarios that have a URL but
4514
+ // no page_file_path. This resolves the file from the URL using Next.js
4515
+ // routing conventions, enabling syncScenarioEntityShas to link them to
4516
+ // entities. Covers fresh clones where JSON files lack pageFilePath.
4517
+ try {
4518
+ const { getDatabase: getDb } = await import('../../../packages/database/index.js');
4519
+ const pfpDb = getDb();
4520
+ const unresolved = await pfpDb
4521
+ .selectFrom('editor_scenarios')
4522
+ .select(['id', 'url'])
4523
+ .where('project_id', '=', project.id)
4524
+ .where('component_name', 'is', null)
4525
+ .where('page_file_path', 'is', null)
4526
+ .where('url', 'is not', null)
4527
+ .execute();
4528
+ if (unresolved.length > 0) {
4529
+ const { scanPageFilePaths: scanPfp } = await import('../utils/entityChangeStatus.server.js');
4530
+ const { matchUrlToPageFile } = await import('../utils/routePatternMatching');
4531
+ const { allFiles: pfpFiles } = scanPfp(projectRoot);
4532
+ let pfpResolved = 0;
4533
+ for (const row of unresolved) {
4534
+ const r = row;
4535
+ if (!r.url)
4536
+ continue;
4537
+ const matched = matchUrlToPageFile(r.url, pfpFiles);
4538
+ if (matched) {
4539
+ await pfpDb
4540
+ .updateTable('editor_scenarios')
4541
+ .set({ page_file_path: matched })
4542
+ .where('id', '=', r.id)
4543
+ .execute();
4544
+ pfpResolved++;
4545
+ }
4546
+ }
4547
+ if (pfpResolved > 0) {
4548
+ console.log(chalk.green(` Resolved page_file_path for ${pfpResolved} scenario(s) from URL`));
4549
+ }
4550
+ }
4551
+ }
4552
+ catch {
4553
+ /* Non-fatal — page_file_path backfill from URL */
4554
+ }
4149
4555
  // Backfill entity_sha on scenarios that were synced before entities existed.
4150
4556
  // This runs independently of analyze-imports so fresh clones and file-synced
4151
4557
  // scenarios get linked to their entities even when auto-analyze doesn't trigger.
@@ -4158,6 +4564,8 @@ const editorCommand = {
4158
4564
  sha: e.sha,
4159
4565
  name: e.name,
4160
4566
  filePath: e.filePath || '',
4567
+ isDefaultExport: e.metadata?.notExported === false &&
4568
+ e.metadata?.namedExport === false,
4161
4569
  })));
4162
4570
  if (result.updated > 0) {
4163
4571
  console.log(chalk.green(` Linked ${result.updated} scenario(s) to their entities`));
@@ -4252,8 +4660,9 @@ const editorCommand = {
4252
4660
  if (step >= 8) {
4253
4661
  const auditOk = await checkAuditGate();
4254
4662
  if (!auditOk) {
4255
- console.error(chalk.red.bold('BLOCKED: The audit has not passed. Run `codeyam editor audit` and fix all issues before proceeding.'));
4256
- console.error(chalk.yellow('Every function and hook must have a test file. Every component must have scenarios.'));
4663
+ // checkAuditGate() already printed specific failure details above
4664
+ console.error(chalk.red.bold('BLOCKED: The audit has not passed. Fix the issues listed above before proceeding.'));
4665
+ console.error(chalk.yellow('DO NOT re-run the audit in a loop — fix the underlying issues first.'));
4257
4666
  process.exit(1);
4258
4667
  }
4259
4668
  }