@codeyam/codeyam-cli 0.1.0-staging.8778565 → 0.1.0-staging.8b51541

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 (152) hide show
  1. package/analyzer-template/.build-info.json +7 -7
  2. package/analyzer-template/log.txt +3 -3
  3. package/analyzer-template/packages/database/src/lib/kysely/tables/editorScenariosTable.ts +56 -0
  4. package/analyzer-template/packages/database/src/lib/loadEntities.ts +0 -6
  5. package/analyzer-template/packages/database/src/lib/updateCommitMetadata.ts +0 -65
  6. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts +3 -0
  7. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts.map +1 -1
  8. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js +56 -0
  9. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
  10. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.d.ts.map +1 -1
  11. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.js +0 -6
  12. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.js.map +1 -1
  13. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.d.ts.map +1 -1
  14. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.js +0 -25
  15. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.js.map +1 -1
  16. package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js +45 -0
  17. package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js.map +1 -0
  18. package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js +101 -47
  19. package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js.map +1 -1
  20. package/codeyam-cli/src/commands/editor.js +799 -211
  21. package/codeyam-cli/src/commands/editor.js.map +1 -1
  22. package/codeyam-cli/src/commands/init.js +68 -34
  23. package/codeyam-cli/src/commands/init.js.map +1 -1
  24. package/codeyam-cli/src/utils/__tests__/analyzerFinalization.test.js +173 -0
  25. package/codeyam-cli/src/utils/__tests__/analyzerFinalization.test.js.map +1 -0
  26. package/codeyam-cli/src/utils/__tests__/editorApi.test.js +18 -8
  27. package/codeyam-cli/src/utils/__tests__/editorApi.test.js.map +1 -1
  28. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +353 -1
  29. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
  30. package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js +128 -1
  31. package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js.map +1 -1
  32. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js +88 -1
  33. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js.map +1 -1
  34. package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js +47 -1
  35. package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js.map +1 -1
  36. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +785 -1
  37. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
  38. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js +63 -1
  39. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js.map +1 -1
  40. package/codeyam-cli/src/utils/__tests__/parseRegisterArg.test.js +30 -2
  41. package/codeyam-cli/src/utils/__tests__/parseRegisterArg.test.js.map +1 -1
  42. package/codeyam-cli/src/utils/__tests__/scenarioCoverage.test.js +227 -0
  43. package/codeyam-cli/src/utils/__tests__/scenarioCoverage.test.js.map +1 -0
  44. package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js +426 -218
  45. package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js.map +1 -1
  46. package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js +1 -0
  47. package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js.map +1 -1
  48. package/codeyam-cli/src/utils/analyzer.js +9 -0
  49. package/codeyam-cli/src/utils/analyzer.js.map +1 -1
  50. package/codeyam-cli/src/utils/analyzerFinalization.js +100 -0
  51. package/codeyam-cli/src/utils/analyzerFinalization.js.map +1 -0
  52. package/codeyam-cli/src/utils/backgroundServer.js +2 -8
  53. package/codeyam-cli/src/utils/backgroundServer.js.map +1 -1
  54. package/codeyam-cli/src/utils/database.js +37 -2
  55. package/codeyam-cli/src/utils/database.js.map +1 -1
  56. package/codeyam-cli/src/utils/editorApi.js +11 -5
  57. package/codeyam-cli/src/utils/editorApi.js.map +1 -1
  58. package/codeyam-cli/src/utils/editorAudit.js +51 -0
  59. package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
  60. package/codeyam-cli/src/utils/editorLoaderHelpers.js +32 -0
  61. package/codeyam-cli/src/utils/editorLoaderHelpers.js.map +1 -1
  62. package/codeyam-cli/src/utils/editorPreview.js +31 -0
  63. package/codeyam-cli/src/utils/editorPreview.js.map +1 -1
  64. package/codeyam-cli/src/utils/editorScenarios.js +261 -0
  65. package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
  66. package/codeyam-cli/src/utils/entityChangeStatus.js +4 -2
  67. package/codeyam-cli/src/utils/entityChangeStatus.js.map +1 -1
  68. package/codeyam-cli/src/utils/entityChangeStatus.server.js +34 -0
  69. package/codeyam-cli/src/utils/entityChangeStatus.server.js.map +1 -1
  70. package/codeyam-cli/src/utils/parseRegisterArg.js.map +1 -1
  71. package/codeyam-cli/src/utils/progress.js +2 -2
  72. package/codeyam-cli/src/utils/progress.js.map +1 -1
  73. package/codeyam-cli/src/utils/scenarioCoverage.js +75 -0
  74. package/codeyam-cli/src/utils/scenarioCoverage.js.map +1 -0
  75. package/codeyam-cli/src/utils/scenariosManifest.js +204 -75
  76. package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -1
  77. package/codeyam-cli/src/utils/setupClaudeCodeSettings.js +1 -0
  78. package/codeyam-cli/src/utils/setupClaudeCodeSettings.js.map +1 -1
  79. package/codeyam-cli/src/utils/simulationGateMiddleware.js +8 -1
  80. package/codeyam-cli/src/utils/simulationGateMiddleware.js.map +1 -1
  81. package/codeyam-cli/src/utils/slugUtils.js +25 -0
  82. package/codeyam-cli/src/utils/slugUtils.js.map +1 -0
  83. package/codeyam-cli/src/utils/syncMocksMiddleware.js +2 -2
  84. package/codeyam-cli/src/utils/syncMocksMiddleware.js.map +1 -1
  85. package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js +40 -0
  86. package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js.map +1 -0
  87. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js +157 -0
  88. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js.map +1 -1
  89. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js +146 -0
  90. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js.map +1 -0
  91. package/codeyam-cli/src/webserver/app/lib/clientErrors.js +65 -0
  92. package/codeyam-cli/src/webserver/app/lib/clientErrors.js.map +1 -0
  93. package/codeyam-cli/src/webserver/app/lib/git.js +3 -2
  94. package/codeyam-cli/src/webserver/app/lib/git.js.map +1 -1
  95. package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-0DY_NKil.js → ScenarioViewer-TSD3C211.js} +1 -1
  96. package/codeyam-cli/src/webserver/build/client/assets/api.editor-scenario-coverage-l0sNRNKZ.js +1 -0
  97. package/codeyam-cli/src/webserver/build/client/assets/api.editor-session-l0sNRNKZ.js +1 -0
  98. package/codeyam-cli/src/webserver/build/client/assets/{dev.empty-Csi0_PMl.js → dev.empty-Ii3inc0_.js} +1 -1
  99. package/codeyam-cli/src/webserver/build/client/assets/editor-16o0AIFV.js +15 -0
  100. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-7Uga8I59.js +41 -0
  101. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-BF4oLwaE.js → entity._sha._-DwCV5__E.js} +1 -1
  102. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.dev-C7YX6r3H.js → entity._sha.scenarios._scenarioId.dev-BwKcai0j.js} +1 -1
  103. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-CHMiAog3.js +6 -0
  104. package/codeyam-cli/src/webserver/build/client/assets/globals-CQPR0pFR.css +1 -0
  105. package/codeyam-cli/src/webserver/build/client/assets/manifest-76e7b62c.js +1 -0
  106. package/codeyam-cli/src/webserver/build/client/assets/memory-9gnxSZlb.js +101 -0
  107. package/codeyam-cli/src/webserver/build/client/assets/{root-ClvYBUSA.js → root-DBjt6o04.js} +3 -3
  108. package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-C-_hOl_g.js +1 -0
  109. package/codeyam-cli/src/webserver/build/client/sound-test.html +98 -0
  110. package/codeyam-cli/src/webserver/build/server/assets/index-DsZjKspK.js +1 -0
  111. package/codeyam-cli/src/webserver/build/server/assets/init-DdqKD2p4.js +10 -0
  112. package/codeyam-cli/src/webserver/build/server/assets/server-build-CKKeWtVK.js +444 -0
  113. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  114. package/codeyam-cli/src/webserver/build-info.json +5 -5
  115. package/codeyam-cli/src/webserver/editorProxy.js +99 -7
  116. package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
  117. package/codeyam-cli/src/webserver/idleDetector.js +73 -0
  118. package/codeyam-cli/src/webserver/idleDetector.js.map +1 -0
  119. package/codeyam-cli/src/webserver/public/sound-test.html +98 -0
  120. package/codeyam-cli/src/webserver/server.js +46 -4
  121. package/codeyam-cli/src/webserver/server.js.map +1 -1
  122. package/codeyam-cli/src/webserver/terminalServer.js +68 -29
  123. package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
  124. package/codeyam-cli/templates/chrome-extension-react/README.md +46 -0
  125. package/codeyam-cli/templates/chrome-extension-react/package.json +1 -0
  126. package/codeyam-cli/templates/codeyam-editor-claude.md +84 -5
  127. package/codeyam-cli/templates/editor-step-hook.py +14 -8
  128. package/codeyam-cli/templates/expo-react-native/README.md +41 -0
  129. package/codeyam-cli/templates/expo-react-native/package.json +1 -0
  130. package/codeyam-cli/templates/nextjs-prisma-sqlite/DATABASE.md +14 -0
  131. package/codeyam-cli/templates/nextjs-prisma-sqlite/README.md +53 -0
  132. package/codeyam-cli/templates/nextjs-prisma-sqlite/package.json +1 -0
  133. package/codeyam-cli/templates/nextjs-prisma-supabase/README.md +52 -0
  134. package/codeyam-cli/templates/nextjs-prisma-supabase/package.json +1 -0
  135. package/codeyam-cli/templates/skills/codeyam-dev-mode/SKILL.md +1 -1
  136. package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +14 -10
  137. package/package.json +1 -1
  138. package/packages/database/src/lib/kysely/tables/editorScenariosTable.js +56 -0
  139. package/packages/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
  140. package/packages/database/src/lib/loadEntities.js +0 -6
  141. package/packages/database/src/lib/loadEntities.js.map +1 -1
  142. package/packages/database/src/lib/updateCommitMetadata.js +0 -25
  143. package/packages/database/src/lib/updateCommitMetadata.js.map +1 -1
  144. package/codeyam-cli/src/webserver/build/client/assets/editor-DgN1LTTt.js +0 -10
  145. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-BLQMSKZa.js +0 -41
  146. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-CF164ouH.js +0 -6
  147. package/codeyam-cli/src/webserver/build/client/assets/globals-BkWJ_UNc.css +0 -1
  148. package/codeyam-cli/src/webserver/build/client/assets/manifest-c26eb85b.js +0 -1
  149. package/codeyam-cli/src/webserver/build/client/assets/memory-Bl2rpw8u.js +0 -96
  150. package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-CrAK28Bc.js +0 -1
  151. package/codeyam-cli/src/webserver/build/server/assets/index-DflIr5SD.js +0 -1
  152. package/codeyam-cli/src/webserver/build/server/assets/server-build-OhKy839M.js +0 -416
@@ -11,12 +11,12 @@ import { IS_INTERNAL_BUILD } from "../utils/buildFlags.js";
11
11
  import { startBackgroundServer } from "../utils/backgroundServer.js";
12
12
  import { installClaudeCodeSkills } from "../utils/install-skills.js";
13
13
  import { setupClaudeCodeSettings } from "../utils/setupClaudeCodeSettings.js";
14
- import { getAnalyzerTemplatePath, isAnalyzerFinalized, } from "../utils/analyzer.js";
14
+ import { ensureAnalyzerFinalized, } from "../utils/analyzerFinalization.js";
15
15
  import { APP_FORMATS, TECH_STACKS } from "../data/techStacks.js";
16
16
  import { getProjectRoot as getStateProjectRoot } from "../state.js";
17
17
  import initCommand from "./init.js";
18
- import { readManifest, syncManifestToDatabase, } from "../utils/scenariosManifest.js";
19
- import { clearEditorState, clearEditorUserPrompt, } from "../utils/editorScenarios.js";
18
+ import { scanScenarioFiles, syncScenarioFilesToDatabase, backfillScenarioMetadata, migrateScenarioFormats, } from "../utils/scenariosManifest.js";
19
+ import { clearEditorState, clearEditorUserPrompt, validateStepTransition, } from "../utils/editorScenarios.js";
20
20
  import { validateSeedData, detectSeedAdapter, } from "../utils/editorSeedAdapter.js";
21
21
  import { buildEditorApiRequest, callEditorApi, EDITOR_API_SUBCOMMANDS, } from "../utils/editorApi.js";
22
22
  import { parseRegisterArg } from "../utils/parseRegisterArg.js";
@@ -36,6 +36,9 @@ const STEP_LABELS = {
36
36
  11: 'Journal',
37
37
  12: 'Review',
38
38
  13: 'Present',
39
+ 14: 'Commit',
40
+ 15: 'Finalize',
41
+ 16: 'Push',
39
42
  };
40
43
  /**
41
44
  * Append a JSONL log entry to .codeyam/logs/editor-log.jsonl
@@ -56,6 +59,18 @@ function logEvent(root, event, data) {
56
59
  // Logging is best-effort
57
60
  }
58
61
  }
62
+ /**
63
+ * Read the design system file if it exists.
64
+ */
65
+ function readDesignSystem(root) {
66
+ const designSystemPath = path.join(root, '.codeyam', 'design-system.md');
67
+ try {
68
+ return fs.readFileSync(designSystemPath, 'utf8');
69
+ }
70
+ catch {
71
+ return null;
72
+ }
73
+ }
59
74
  /**
60
75
  * Get the project root (where .codeyam/ lives) or cwd.
61
76
  */
@@ -128,6 +143,39 @@ function getServerPort() {
128
143
  }
129
144
  return process.env.CODEYAM_PORT || '3111';
130
145
  }
146
+ /**
147
+ * Read the project's default dimension name and available screen size names.
148
+ * Used by step instructions to show project-specific dimension examples
149
+ * instead of hardcoded "Desktop".
150
+ */
151
+ function getProjectDimensions(root) {
152
+ try {
153
+ const configPath = path.join(root, '.codeyam', 'config.json');
154
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
155
+ const defaultName = config.defaultScreenSize?.name ||
156
+ (config.screenSizes ? Object.keys(config.screenSizes)[0] : null) ||
157
+ 'Desktop';
158
+ const names = config.screenSizes ? Object.keys(config.screenSizes) : [];
159
+ return { defaultName, names };
160
+ }
161
+ catch {
162
+ return { defaultName: 'Desktop', names: [] };
163
+ }
164
+ }
165
+ /**
166
+ * Print dimension guidance when the project has multiple screen sizes.
167
+ * Tells Claude to pick the right dimension for the content being previewed
168
+ * instead of blindly using the default for everything.
169
+ */
170
+ function printDimensionGuidance(defaultName, names) {
171
+ if (names.length <= 1)
172
+ return;
173
+ // Find a non-default dimension to suggest as the "other" option
174
+ const otherName = names.find((n) => n !== defaultName) || names[1];
175
+ console.log(chalk.yellow(` IMPORTANT: Choose the dimension that matches what you're previewing. Available: ${names.map((n) => `"${n}"`).join(', ')}.`));
176
+ console.log(chalk.yellow(` Do NOT always use "${defaultName}". Full pages, standalone views, and desktop layouts should use "${otherName}".`));
177
+ console.log(chalk.yellow(` Think about the CONTENT — a full-page library view needs a large viewport, not a popup-sized one.`));
178
+ }
131
179
  /**
132
180
  * Print a checklist item.
133
181
  * Inline backtick-wrapped text is highlighted in cyan for visibility.
@@ -149,13 +197,13 @@ function stepHeader(step, title, feature) {
149
197
  console.log();
150
198
  }
151
199
  /**
152
- * Print a colored progress tracker showing all 13 steps.
200
+ * Print a colored progress tracker showing all 16 steps.
153
201
  * Steps before `current` are green ✓, `current` is bold cyan →, future steps are dim ○.
154
202
  */
155
203
  function printProgressTracker(current) {
156
204
  console.log();
157
- console.log(chalk.dim('┌─────────────────────────────────────┐'));
158
- for (let i = 1; i <= 13; i++) {
205
+ console.log(chalk.dim(' ┌─────────────────────────────────────┐'));
206
+ for (let i = 1; i <= 16; i++) {
159
207
  const label = STEP_LABELS[i];
160
208
  const num = i < 10 ? ` ${i}` : `${i}`;
161
209
  const content = `${num}. ${label.padEnd(28)}`;
@@ -193,7 +241,7 @@ function stopGate(current, opts) {
193
241
  console.log(chalk.yellow('For the CURRENT step (→), show each checklist item with ✓ (done) or ✗ (skipped + reason).'));
194
242
  console.log(chalk.yellow('If any items are ✗, explain why and ask if the user wants to address them.'));
195
243
  console.log();
196
- if (current < 13) {
244
+ if (current < 16) {
197
245
  console.log(chalk.green('When done, run: ') +
198
246
  chalk.bold(`codeyam editor ${current + 1}`));
199
247
  }
@@ -237,6 +285,14 @@ function printResumptionHeader(step) {
237
285
  ],
238
286
  12: ['Re-verify is safe to repeat — just re-run the checks'],
239
287
  13: ['Check if commit already made:\n `git log --oneline -3`'],
288
+ 14: ['Check if commit already made:\n `git log --oneline -3`'],
289
+ 15: [
290
+ 'Check if journal was already updated with commit SHA:\n `codeyam editor journal-list`',
291
+ 'Check if commit was already amended:\n `git log --oneline -3`',
292
+ ],
293
+ 16: [
294
+ 'Check if already pushed:\n `git remote -v && git log --oneline origin/HEAD..HEAD 2>/dev/null`',
295
+ ],
240
296
  };
241
297
  const label = STEP_LABELS[step] || 'Unknown';
242
298
  console.log(chalk.bold.yellow(`━━━ RESUMING Step ${step}: ${label} ━━━`));
@@ -341,7 +397,7 @@ function parseDebugTargets(target) {
341
397
  }
342
398
  if (entry.startsWith('step-')) {
343
399
  const num = parseInt(entry.replace('step-', ''), 10);
344
- if (!isNaN(num) && num >= 1 && num <= 13) {
400
+ if (!isNaN(num) && num >= 1 && num <= 16) {
345
401
  valid.add(`step-${num}`);
346
402
  continue;
347
403
  }
@@ -425,6 +481,15 @@ function printSetup(root) {
425
481
  console.log();
426
482
  console.log("No project detected. Let's get started.");
427
483
  console.log();
484
+ // ── Design System ────────────────────────────────────────────────
485
+ console.log(chalk.bold('Design System (ask FIRST):'));
486
+ console.log(chalk.dim(' Ask: "Do you have a design system, brand guidelines, or style preferences you\'d like me to follow?"'));
487
+ console.log(chalk.dim(' Use AskUserQuestion with these EXACT option labels:'));
488
+ console.log(chalk.yellow(' Option 1 label: "Yes, I\'ll paste my design system"'));
489
+ console.log(chalk.dim(' → Wait for paste, save to .codeyam/design-system.md, confirm with brief summary'));
490
+ console.log(chalk.yellow(' Option 2 label: "No, use sensible defaults"'));
491
+ console.log(chalk.dim(' → Skip'));
492
+ console.log();
428
493
  console.log(chalk.bold('Checklist:'));
429
494
  checkbox('Read `.codeyam/editor-mode-context.md` for session state');
430
495
  checkbox('Ask the user what they want to build');
@@ -479,7 +544,15 @@ function printSetup(root) {
479
544
  console.log();
480
545
  console.log(chalk.dim(' Pre-select based on app format: mobile-responsive-web-app/desktop-app → Desktop, mobile-app → Mobile, chrome-extension → Custom (400×600).'));
481
546
  console.log(chalk.dim(' If only one obvious choice, confirm it rather than asking.'));
482
- console.log(chalk.dim(' Save the choice via: curl -s -X POST http://localhost:PORT/api/editor-project-info -H "Content-Type: application/json" -d \'{"defaultScreenSize":{"name":"Desktop","width":1440,"height":900}}\''));
547
+ console.log(chalk.dim(` Save the choice via: curl -s -X POST http://localhost:${port}/api/editor-project-info -H "Content-Type: application/json" -d '{"defaultScreenSize":{"name":"Desktop","width":1440,"height":900}}'`));
548
+ console.log();
549
+ console.log(chalk.bold('Named Screen Sizes (for multi-resolution apps):'));
550
+ console.log(chalk.dim(' For mobile-responsive web apps or apps that need screenshots at multiple resolutions,'));
551
+ console.log(chalk.dim(' also save named screen sizes. Each scenario can reference a dimension name.'));
552
+ console.log(chalk.dim(` curl -s -X POST http://localhost:${port}/api/editor-project-info -H "Content-Type: application/json" \\`));
553
+ console.log(chalk.dim(' -d \'{"screenSizes":{"Desktop":{"width":1440,"height":900},"Mobile":{"width":375,"height":667}}}\''));
554
+ console.log(chalk.dim(' If you discover a new viewport is needed mid-workflow, add it to screenSizes the same way and reference by name.'));
555
+ console.log(chalk.dim(' NOTE: This REPLACES all screenSizes — always include every named size, not just the new one.'));
483
556
  console.log();
484
557
  console.log(chalk.bold.red('━━━ STOP ━━━'));
485
558
  console.log();
@@ -514,7 +587,7 @@ function printCycleOverview(root, state) {
514
587
  console.log(chalk.yellow('This applies even after committing — always use the change workflow.'));
515
588
  }
516
589
  else {
517
- console.log('Each feature follows 13 steps. You MUST run each command in order:');
590
+ console.log('Each feature follows 16 steps. You MUST run each command in order:');
518
591
  console.log();
519
592
  console.log(` ${chalk.bold.yellow(' 1')} ${chalk.bold('Plan')} — Plan the feature, confirm with user`);
520
593
  console.log(` ${chalk.bold.yellow(' 2')} ${chalk.bold('Prototype')} — Build a working prototype fast`);
@@ -529,6 +602,9 @@ function printCycleOverview(root, state) {
529
602
  console.log(` ${chalk.bold.yellow('11')} ${chalk.bold('Journal')} — Create/update journal entry`);
530
603
  console.log(` ${chalk.bold.yellow('12')} ${chalk.bold('Review')} — Verify screenshots and audit`);
531
604
  console.log(` ${chalk.bold.yellow('13')} ${chalk.bold('Present')} — Present summary, get approval`);
605
+ console.log(` ${chalk.bold.yellow('14')} ${chalk.bold('Commit')} — Commit all changes`);
606
+ console.log(` ${chalk.bold.yellow('15')} ${chalk.bold('Finalize')} — Journal update + amend commit`);
607
+ console.log(` ${chalk.bold.yellow('16')} ${chalk.bold('Push')} — Push to remote`);
532
608
  console.log();
533
609
  console.log(chalk.green('Start now: ') + chalk.bold('codeyam editor 1'));
534
610
  console.log(chalk.dim(' If the user already described what they want, pass it: codeyam editor 1 --prompt "their message"'));
@@ -543,19 +619,19 @@ function printStep1(root, feature, options, userPrompt) {
543
619
  if (!isResuming) {
544
620
  clearState(root);
545
621
  }
546
- // If feature is provided, save initial state so step 2 can pick it up
547
- if (feature) {
548
- const now = new Date().toISOString();
549
- writeState(root, {
550
- feature,
551
- step: 1,
552
- label: STEP_LABELS[1],
553
- startedAt: isResuming ? prevState.startedAt : now,
554
- featureStartedAt: isResuming ? prevState.featureStartedAt : now,
555
- appFormats: options?.appFormats || prevState?.appFormats,
556
- techStackId: options?.techStackId || prevState?.techStackId,
557
- });
558
- }
622
+ // Always persist state so step 2's validation sees that step 1 ran.
623
+ // The feature name may not be known yet (it's an output of planning)
624
+ // use an empty string as placeholder; step 2 requires --feature anyway.
625
+ const now = new Date().toISOString();
626
+ writeState(root, {
627
+ feature: feature || prevState?.feature || '',
628
+ step: 1,
629
+ label: STEP_LABELS[1],
630
+ startedAt: isResuming ? prevState.startedAt : now,
631
+ featureStartedAt: isResuming ? prevState.featureStartedAt : now,
632
+ appFormats: options?.appFormats || prevState?.appFormats,
633
+ techStackId: options?.techStackId || prevState?.techStackId,
634
+ });
559
635
  // Save the user's original prompt to a separate file
560
636
  if (userPrompt) {
561
637
  const promptPath = path.join(root, '.codeyam', 'editor-user-prompt.txt');
@@ -580,11 +656,21 @@ function printStep1(root, feature, options, userPrompt) {
580
656
  console.log(chalk.dim(' Bad: Free-form text asking "What do you think about the data model?"'));
581
657
  console.log(chalk.dim(' You can ask up to 4 questions at once. Bundle related questions into a single AskUserQuestion call.'));
582
658
  checkbox("Summarize what you'll build in plain language the user can verify against their vision");
659
+ checkbox('Include a brief note on what interesting data states the scenarios should demonstrate');
660
+ console.log(chalk.dim(' Think: what seed data would put this feature through its paces? Diverse content, edge cases, empty vs rich.'));
583
661
  checkbox('Set the project title and description for the App tab:');
584
662
  console.log(chalk.dim(` curl -s -X POST http://localhost:${port}/api/editor-project-info \\`));
585
663
  console.log(chalk.dim(' -H "Content-Type: application/json" \\'));
586
664
  console.log(chalk.dim(' -d \'{"projectTitle":"My App","projectDescription":"A short description of what this app does"}\''));
587
665
  console.log();
666
+ const designSystem = readDesignSystem(root);
667
+ if (designSystem) {
668
+ console.log(chalk.bold.magenta('Design System (from .codeyam/design-system.md):'));
669
+ console.log(chalk.dim(' Keep these design tokens in mind during planning.'));
670
+ console.log();
671
+ console.log(designSystem);
672
+ console.log();
673
+ }
588
674
  console.log(chalk.bold('Present a selection menu to the user (use AskUserQuestion with these EXACT option labels):'));
589
675
  console.log(chalk.green(' Option 1 label: "This plan is accurate, let\'s prototype it!"') + chalk.dim(' — proceed to step 2'));
590
676
  console.log(chalk.yellow(' Option 2 label: "I\'d like some changes"') +
@@ -612,6 +698,7 @@ function printStep1(root, feature, options, userPrompt) {
612
698
  // ─── Step 2: Prototype ────────────────────────────────────────────────
613
699
  function printStep2(root, feature) {
614
700
  const port = getServerPort();
701
+ const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
615
702
  const projectExists = hasProject(root);
616
703
  const prevState = readState(root);
617
704
  const isResuming = prevState?.step === 2;
@@ -649,7 +736,13 @@ function printStep2(root, feature) {
649
736
  console.log(chalk.dim(` # After re-seeding, restart the dev server to pick up fresh data:`));
650
737
  console.log(chalk.dim(` # codeyam editor dev-server '{"action":"restart"}'`));
651
738
  console.log();
652
- console.log(chalk.dim(' See PRISMA_SETUP.md for Prisma patterns and important warnings.'));
739
+ console.log(chalk.yellow(' IMPORTANT: When adding new required columns to existing tables,'));
740
+ console.log(chalk.yellow(' provide a @default(...) value so `db push` can fill existing rows.'));
741
+ console.log(chalk.dim(' Example: userId String @default("anonymous") — existing rows get "anonymous"'));
742
+ console.log(chalk.dim(' Without a default, Prisma requires --force-reset which drops ALL data.'));
743
+ console.log(chalk.dim(' NEVER use --force-reset — it is blocked in this environment.'));
744
+ console.log();
745
+ console.log(chalk.dim(' See DATABASE.md for Prisma patterns and important warnings.'));
653
746
  console.log(chalk.dim(' Key: import { prisma } from "@/app/lib/prisma" in API routes.'));
654
747
  console.log(chalk.dim(' Key: Seed scripts must use the adapter pattern (see prisma/seed.ts).'));
655
748
  console.log();
@@ -673,14 +766,33 @@ function printStep2(root, feature) {
673
766
  console.log(chalk.dim(' Use Tailwind responsive prefixes (sm:, md:, lg:) for layout shifts.'));
674
767
  console.log(chalk.dim(' Test at both Desktop (1280×800) and Mobile (390×844) sizes before presenting.'));
675
768
  }
769
+ const designSystem = readDesignSystem(root);
770
+ if (designSystem) {
771
+ console.log();
772
+ console.log(chalk.bold.magenta('Design System (from .codeyam/design-system.md):'));
773
+ console.log();
774
+ console.log(designSystem);
775
+ console.log();
776
+ checkbox('Define ALL design tokens as CSS custom properties in globals.css — not just colors');
777
+ console.log(chalk.dim(' Colors: --bg-surface, --text-primary, --accent-green-a, etc.'));
778
+ console.log(chalk.dim(' Typography: --text-xs, --text-sm, --text-lg, --text-2xl (font-size values)'));
779
+ console.log(chalk.dim(' Font weights: --font-weight-normal, --font-weight-medium, --font-weight-semibold'));
780
+ console.log(chalk.dim(' Spacing: --spacing-xs, --spacing-sm, --spacing-md, --spacing-lg, etc.'));
781
+ console.log(chalk.dim(' Border radius, shadows, transitions — every value the design system defines.'));
782
+ checkbox('Reference tokens from components — ZERO hardcoded px values for font-size, spacing, or colors');
783
+ console.log(chalk.dim(' Bad: fontSize: 14, padding: "12px 16px", gap: 8'));
784
+ console.log(chalk.dim(' Good: fontSize: "var(--text-sm)", padding: "var(--spacing-md) var(--spacing-lg)", gap: "var(--spacing-sm)"'));
785
+ console.log(chalk.dim(' This ensures the entire app updates when the design system changes.'));
786
+ }
676
787
  console.log();
677
788
  console.log(chalk.bold.cyan('Keep the preview moving:'));
678
789
  console.log(chalk.cyan(' The user is watching the preview. Refresh it after each meaningful change:'));
679
- console.log(chalk.cyan(` codeyam editor preview`));
790
+ console.log(chalk.cyan(` codeyam editor preview '{"dimension":"${dim}"}'`));
680
791
  console.log(chalk.cyan(' Refresh after: first visible page, adding each UI section, seeding data, styling.'));
681
792
  console.log(chalk.cyan(' Aim for 4-8+ refreshes during prototyping — not one big reveal at the end.'));
682
793
  console.log(chalk.cyan(' If you build a NEW page (e.g., /drinks/[id]), navigate the preview there:'));
683
- console.log(chalk.cyan(` codeyam editor preview '{"path":"/drinks/1"}'`));
794
+ console.log(chalk.cyan(` codeyam editor preview '{"path":"/drinks/1","dimension":"${dim}"}'`));
795
+ printDimensionGuidance(dim, dimNames);
684
796
  console.log();
685
797
  console.log(chalk.bold('Verify the dev server:'));
686
798
  console.log(chalk.dim(` # Get dev server URL: codeyam editor dev-server`));
@@ -701,12 +813,19 @@ function printStep2(root, feature) {
701
813
  console.log(chalk.dim(' The user is looking at the preview — a blank page means something is broken.'));
702
814
  console.log(chalk.dim(' If any check fails, fix the issue and re-verify before proceeding.'));
703
815
  console.log();
816
+ console.log(chalk.bold('Update README and setup script:'));
817
+ checkbox('Update `README.md`: set the project name, write a one-line description, and list any setup prerequisites');
818
+ checkbox('Update `npm run setup` in `package.json` if setup requires extra steps (e.g. env vars, external services)');
819
+ console.log(chalk.dim(' The README and setup script must stay accurate as you make changes.'));
820
+ console.log(chalk.dim(' A new clone should work with just: git clone → npm run setup → npm run dev'));
821
+ console.log();
704
822
  console.log(chalk.dim('Focus on building the prototype. Scenarios and refactoring happen in later steps.'));
705
823
  stopGate(2);
706
824
  }
707
825
  // ─── Step 3: Confirm ──────────────────────────────────────────────────
708
826
  function printStep3(root, feature) {
709
827
  const port = getServerPort();
828
+ const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
710
829
  const prevState = readState(root);
711
830
  const isResuming = prevState?.step === 3;
712
831
  const now = new Date().toISOString();
@@ -725,7 +844,7 @@ function printStep3(root, feature) {
725
844
  console.log('Summarize what was built and get user confirmation.');
726
845
  console.log();
727
846
  console.log(chalk.bold('Before presenting — verify everything works:'));
728
- checkbox(`Refresh the preview: \`codeyam editor preview\` — check the \`preview\` field for \`healthy: false\``);
847
+ checkbox(`Refresh the preview: \`codeyam editor preview '{"dimension":"${dim}"}'\` — check the \`preview\` field for \`healthy: false\``);
729
848
  checkbox('Verify API routes return valid data (curl each route)');
730
849
  console.log();
731
850
  console.log(chalk.bold.red(' Verify EVERY image loads (this is the #1 source of broken prototypes):'));
@@ -739,7 +858,8 @@ function printStep3(root, feature) {
739
858
  console.log();
740
859
  console.log(chalk.bold('Then present to the user:'));
741
860
  checkbox('Summarize what was built (routes, components, data)');
742
- checkbox('Navigate the preview to the feature\'s primary page: `codeyam editor preview \'{"path":"/your-page"}\'`');
861
+ checkbox(`Navigate the preview to the feature's primary page: \`codeyam editor preview '{"path":"/your-page","dimension":"${dim}"}'\``);
862
+ printDimensionGuidance(dim, dimNames);
743
863
  console.log(chalk.dim(' The user needs to SEE the new feature. If you built a new page, navigate there.'));
744
864
  console.log(chalk.dim(' If you modified an existing page, refresh the preview to show the changes.'));
745
865
  console.log();
@@ -751,7 +871,7 @@ function printStep3(root, feature) {
751
871
  console.log(chalk.bold('Present a selection menu to the user (use AskUserQuestion with these EXACT option labels):'));
752
872
  console.log(chalk.green(' Option 1 label: "The live preview is displaying what I expected"') + chalk.dim(' — proceed to step 4'));
753
873
  console.log(chalk.yellow(' Option 2 label: "I\'d like some changes"') +
754
- chalk.dim(' — user describes changes, you make them, then re-present'));
874
+ chalk.dim(' — make changes, refresh preview, re-run `codeyam editor 3`'));
755
875
  console.log();
756
876
  console.log(chalk.dim('Wait for user approval before moving on. Refactoring and scenarios happen in later steps.'));
757
877
  stopGate(3, { confirm: true });
@@ -809,6 +929,7 @@ function printStep4(root, feature) {
809
929
  // ─── Step 5: Extract ──────────────────────────────────────────────────
810
930
  function printStep5(root, feature) {
811
931
  const port = getServerPort();
932
+ const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
812
933
  const prevState = readState(root);
813
934
  const isResuming = prevState?.step === 5;
814
935
  const now = new Date().toISOString();
@@ -832,12 +953,13 @@ function printStep5(root, feature) {
832
953
  checkbox('Every component that renders multiple sections must be split into sub-components');
833
954
  console.log(chalk.dim(' No tests needed — visual verification happens in step 7'));
834
955
  console.log();
835
- console.log(chalk.bold('Library functions (TDD):'));
836
- checkbox('For each function: write MULTIPLE failing tests FIRST, then extract to make them pass');
956
+ console.log(chalk.bold('Library functions AND hooks (TDD):'));
957
+ checkbox('For each function/hook: write MULTIPLE failing tests FIRST, then extract to make them pass');
837
958
  console.log(chalk.dim(' Cover: typical inputs, edge cases, empty/null inputs, error conditions'));
838
959
  console.log(chalk.dim(' Aim for 3-8 test cases per function depending on complexity'));
960
+ console.log(chalk.dim(' Hooks count as functions — useDrinks, useAuth, etc. all need test files'));
839
961
  checkbox('Place test files next to source: `app/lib/drinks.ts` → `app/lib/drinks.test.ts`');
840
- console.log(chalk.yellow(' Tests ARE the only coverage for library functions — step 7 only captures component screenshots.'));
962
+ console.log(chalk.yellow(' Tests ARE the only coverage for library functions/hooks — step 7 only captures component screenshots.'));
841
963
  console.log();
842
964
  console.log(chalk.bold('Recursive pass:'));
843
965
  checkbox('Re-read EVERY new file you just created — extract components from components, functions from functions');
@@ -849,7 +971,8 @@ function printStep5(root, feature) {
849
971
  checkbox('Run all tests and verify they pass');
850
972
  checkbox('Page files contain ONLY imports + component composition — no raw HTML tags');
851
973
  checkbox('Every component renders ONE thing or composes sub-components — no multi-section JSX');
852
- checkbox(`Refresh the preview after each batch of extractions: \`codeyam editor preview\``);
974
+ checkbox(`Refresh the preview after each batch of extractions: \`codeyam editor preview '{"dimension":"${dim}"}'\``);
975
+ printDimensionGuidance(dim, dimNames);
853
976
  console.log(chalk.dim(' The user should see the preview stay healthy as you refactor — refresh periodically, not just at the end.'));
854
977
  console.log(chalk.dim('Reuse glossary functions when they fit naturally. Extract a new function when the use case diverges.'));
855
978
  console.log();
@@ -879,7 +1002,8 @@ function printStep6(root, feature) {
879
1002
  checkbox("Read `.codeyam/glossary.json` (create if it doesn't exist)");
880
1003
  checkbox('Add an entry for each new function/component extracted in step 5');
881
1004
  checkbox('Each entry should have: name, filePath, description, parameters, returnType, tags, feature');
882
- checkbox('For each function with a test file from step 5, set `testFile` to the relative path');
1005
+ checkbox('Every non-component entry MUST have `testFile` set hooks and functions all need tests');
1006
+ console.log(chalk.yellow(' If a function/hook has no test yet, go back and write one (TDD). The audit will block you.'));
883
1007
  console.log();
884
1008
  console.log(chalk.bold('Entry format:'));
885
1009
  console.log(chalk.dim(' { "name": "calculateTotal", "filePath": "app/utils/pricing.ts",'));
@@ -895,6 +1019,7 @@ function printStep6(root, feature) {
895
1019
  // ─── Step 7: Analyze ──────────────────────────────────────────────────
896
1020
  function printStep7(root, feature) {
897
1021
  const port = getServerPort();
1022
+ const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
898
1023
  const prevState = readState(root);
899
1024
  const isResuming = prevState?.step === 7;
900
1025
  const now = new Date().toISOString();
@@ -946,12 +1071,20 @@ function printStep7(root, feature) {
946
1071
  console.log(chalk.dim(` codeyam editor register '{"name":"ComponentName - Scenario",`));
947
1072
  console.log(chalk.dim(` "componentName":"ComponentName","componentPath":"path/to/file.tsx",`));
948
1073
  console.log(chalk.dim(` "url":"/isolated-components/ComponentName?s=Scenario",`));
1074
+ console.log(chalk.dim(` "dimensions":["${dim}"],`));
949
1075
  console.log(chalk.dim(` "mockData":{"routes":{"/api/...":{"body":[...]}}}}'`));
950
- console.log(chalk.dim(' Optional: add "viewportWidth" and "viewportHeight" to override the project default screen size'));
951
- console.log(chalk.dim(' If omitted, captures use the project default from setup. Only override for scenarios that need a different size.'));
1076
+ console.log(chalk.yellow(' ALWAYS include "dimensions" use the named screen size that matches the component\'s context.'));
1077
+ console.log(chalk.dim(dimNames.length > 0
1078
+ ? ` Available dimensions: ${dimNames.map((n) => `"${n}"`).join(', ')}. Default: "${dim}".`
1079
+ : ` Use "${dim}" for most components. Names must match a key in screenSizes from setup.`));
1080
+ console.log(chalk.dim(' Names must match a key in screenSizes from setup. If you need a new size, add it first:'));
1081
+ console.log(chalk.dim(` curl -s -X POST http://localhost:${port}/api/editor-project-info -H "Content-Type: application/json" \\`));
1082
+ console.log(chalk.dim(` -d '{"screenSizes":{...existing..., "New Name":{"width":W,"height":H}}}' (replaces all — include every size)`));
952
1083
  console.log(chalk.dim(' url is a PATH (starts with /) — the proxy routes it and intercepts API calls'));
953
1084
  console.log(chalk.dim(' mockData.routes provides data for API calls the component makes internally'));
954
1085
  console.log(chalk.dim(' (omit mockData if the component has no internal API calls)'));
1086
+ console.log(chalk.dim(' For large JSON: use your Write tool to create .codeyam/tmp/scenario.json,'));
1087
+ console.log(chalk.dim(' then: codeyam editor register @.codeyam/tmp/scenario.json'));
955
1088
  console.log(chalk.yellow(' 5. IMPORTANT: Check the register response for `clientErrors` array'));
956
1089
  console.log(chalk.yellow(' If clientErrors is non-empty → fix the isolation route and re-register'));
957
1090
  console.log(chalk.yellow(' Fix client errors and re-register before moving on'));
@@ -965,17 +1098,16 @@ function printStep7(root, feature) {
965
1098
  console.log();
966
1099
  console.log(chalk.dim('Do not proceed until both component isolations and library tests pass.'));
967
1100
  console.log();
968
- checkbox('Run `codeyam editor audit` to verify all components have scenarios and all functions have tests');
1101
+ checkbox('Run `codeyam editor audit` to verify all components have scenarios and all functions/hooks have tests');
1102
+ console.log(chalk.red.bold(' The audit is a HARD GATE — step 8 will refuse to run until the audit passes.'));
1103
+ console.log(chalk.dim(' When audit passes, the import graph is built automatically for change tracking.'));
969
1104
  console.log();
970
- console.log(chalk.bold('Build import graph (for change tracking):'));
971
- checkbox('Run `codeyam editor analyze-imports`');
972
- console.log(chalk.dim(' This populates the import graph so the system can detect impacted entities when code changes.'));
973
- console.log(chalk.dim(' It must run AFTER the audit passes so the glossary and entity data are complete.'));
974
1105
  stopGate(7);
975
1106
  }
976
1107
  // ─── Step 8: App Scenarios ────────────────────────────────────────────
977
1108
  function printStep8(root, feature) {
978
1109
  const port = getServerPort();
1110
+ const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
979
1111
  const prevState = readState(root);
980
1112
  const isResuming = prevState?.step === 8;
981
1113
  const now = new Date().toISOString();
@@ -991,43 +1123,83 @@ function printStep8(root, feature) {
991
1123
  if (isResuming) {
992
1124
  printResumptionHeader(8);
993
1125
  }
994
- console.log('Create app-level scenarios representing different data states.');
1126
+ console.log('Create app-level scenarios with rich data that robustly demonstrates this feature.');
1127
+ console.log();
1128
+ console.log(chalk.bold('Goal: Every scenario should thoroughly exercise the feature with realistic data.'));
1129
+ console.log(chalk.dim(' Scenarios with minimal or generic data ("Test Item 1") won\'t reveal whether the feature works.'));
1130
+ console.log(chalk.dim(' Use rich, diverse seed data that puts the feature through its paces.'));
1131
+ console.log();
1132
+ console.log(chalk.bold.cyan('Make seed data work hard:'));
1133
+ console.log(chalk.cyan(' Every scenario — new or existing — should have data that EXERCISES the feature:'));
1134
+ console.log(chalk.cyan(' • Add data that uses new fields, relationships, or states the feature introduces'));
1135
+ console.log(chalk.cyan(' • Include realistic variety — different categories, lengths, statuses, counts'));
1136
+ console.log(chalk.cyan(" • Populate optional fields the feature depends on (don't leave them empty)"));
1137
+ console.log(chalk.cyan(' • Make content diverse enough that edge cases surface naturally'));
1138
+ console.log(chalk.cyan(" • Think: would this data reveal a bug in the feature? If it's too simple, enrich it."));
1139
+ console.log();
1140
+ console.log(chalk.bold.cyan('When to create new vs reuse existing:'));
1141
+ console.log(chalk.cyan(' • New pages or pages with no scenarios yet → create new scenarios'));
1142
+ console.log(chalk.cyan(' • Existing scenarios on affected pages → re-register with enhanced data'));
1143
+ console.log(chalk.cyan(" • New data states that can't coexist in one scenario (empty vs rich, error vs success) → new scenario"));
1144
+ console.log(chalk.cyan(" • Don't duplicate — if an existing scenario can cover a state with richer data, enhance it instead"));
995
1145
  console.log();
996
1146
  console.log(chalk.bold('Checklist:'));
997
- checkbox('Check existing scenarios: `codeyam editor scenarios`');
998
- console.log(chalk.dim(' Review existing scenarios reuse and update their data rather than'));
999
- console.log(chalk.dim(' creating duplicates. Re-register with the same name to update a scenario.'));
1000
- checkbox('Ensure scenarios clearly demonstrate what changed in this session');
1001
- console.log(chalk.dim(' If data models changed: update existing scenarios seed data to match'));
1002
- console.log(chalk.dim(' If UI changed: re-register existing scenarios so screenshots reflect the update'));
1003
- console.log(chalk.dim(' Add new scenarios only for genuinely new data states not covered by existing ones'));
1004
- checkbox('Cover key data states for EVERY page/route (at least 2-3 scenarios per page)');
1147
+ checkbox('Run `codeyam editor scenarios` — review all existing scenarios');
1148
+ checkbox("Create new scenarios for any pages that don't have scenarios yet");
1005
1149
  console.log(chalk.yellow(' Each page needs its own scenarios — a detail page needs different data than the catalog'));
1006
- console.log(chalk.dim(' Catalog: "Full Catalog", "Empty Catalog", "Single Category"'));
1007
- console.log(chalk.dim(' Detail: "Detail - With Reviews", "Detail - No Reviews", "Detail - High Rated"'));
1008
- console.log(chalk.dim(' Each scenario provides seed data to populate the database for that state'));
1150
+ checkbox('Re-register every existing scenario whose page was affected by this feature');
1151
+ console.log(chalk.dim(' Use the SAME name to update. Enhance seed data to showcase this feature where relevant.'));
1152
+ console.log(chalk.yellow(" Don't just re-register unchanged data enrich it so the feature is thoroughly demonstrated."));
1153
+ checkbox('ALWAYS include "dimensions" — pick the viewport that best demonstrates the page');
1154
+ console.log(chalk.yellow(' Every app scenario MUST have a "dimensions" field referencing a named screen size from setup.'));
1155
+ console.log(chalk.dim(dimNames.length > 0
1156
+ ? ` Available dimensions: ${dimNames.map((n) => `"${n}"`).join(', ')}. Default: "${dim}".`
1157
+ : ` Use "${dim}" for most scenarios. Names must match a key in screenSizes from setup.`));
1158
+ if (dimNames.length > 1) {
1159
+ const otherDim = dimNames.find((n) => n !== dim) || dimNames[1];
1160
+ console.log(chalk.yellow(` Think about what fits each scenario: full pages/standalone views → "${otherDim}", popups/widgets → "${dim}".`));
1161
+ }
1162
+ console.log(chalk.dim(' For responsive apps, include multiple dimensions: "dimensions":["Desktop","Mobile"] captures both viewports.'));
1163
+ console.log(chalk.dim(' If you need a new named size mid-workflow, add it via editor-project-info API (include ALL existing sizes — the API replaces, not merges).'));
1009
1164
  checkbox('If the project has a database + seed adapter, use seed-based scenarios:');
1010
- console.log(chalk.dim(` codeyam editor register '{"name":"Full Catalog","type":"application","url":"/","seed":{"products":[...],"categories":[...]}}'`));
1165
+ console.log(chalk.dim(` codeyam editor register '{"name":"Full Catalog","type":"application","url":"/","dimensions":["${dim}"],"seed":{"products":[...],"categories":[...]}}'`));
1011
1166
  console.log(chalk.dim(' Seed data is written to the DB via the seed adapter. Real app renders real data.'));
1012
- checkbox('Optional: add "viewportWidth" and "viewportHeight" to override the project default screen size');
1013
- console.log(chalk.dim(' If omitted, captures use the project default from setup. Only override for scenarios that need a different size.'));
1014
1167
  checkbox('IMPORTANT: Always include "url" — the page path to screenshot for this scenario');
1015
1168
  console.log(chalk.dim(' Use "/" for home page, "/drinks/1" for detail pages, etc.'));
1016
1169
  console.log(chalk.dim(' Without url, the screenshot captures the root page regardless of the scenario.'));
1017
1170
  console.log(chalk.yellow(' Create separate scenarios for each page that shows different data.'));
1018
- checkbox('For large seed data, write JSON to a project temp file and use @ prefix:');
1019
- console.log(chalk.dim(' Write JSON to .codeyam/tmp/scenario.json then register with:'));
1171
+ checkbox('For large seed data, use your Write tool to create .codeyam/tmp/scenario.json, then:');
1020
1172
  console.log(chalk.dim(' codeyam editor register @.codeyam/tmp/scenario.json'));
1173
+ console.log(chalk.yellow(' IMPORTANT: Use the Write tool — NOT cat/heredoc — to avoid permission prompts'));
1021
1174
  checkbox('For external API mocks (Stripe, weather, etc.), add externalApis:');
1022
1175
  console.log(chalk.dim(` "externalApis":{"GET https://api.stripe.com/v1/prices":{"body":[...],"status":200}}`));
1023
1176
  checkbox('If the app uses auth, email, or other patterns: see FEATURE_PATTERNS.md for scenario guidance');
1024
- checkbox('If no database, use component-style mock scenarios (unchanged):');
1025
- console.log(chalk.dim(` codeyam editor register '{"name":"Empty","description":"...","url":"/","mockData":{"routes":{"/api/...":{"body":[...]}}}}'`));
1177
+ checkbox('If the app uses localStorage/sessionStorage instead of a database, use localStorage scenarios:');
1178
+ console.log(chalk.dim(` codeyam editor register '{"name":"Full Library","type":"application","url":"/","dimensions":["${dim}"],"localStorage":{"articles":[...],"collections":[...]}}'`));
1179
+ console.log(chalk.dim(' The proxy injects data into localStorage before the app loads. Fully interactive — the app can read and write normally.'));
1180
+ console.log(chalk.dim(' For an empty state, register with an empty localStorage or omit it entirely.'));
1181
+ checkbox('If no database and no localStorage, use component-style mock scenarios:');
1182
+ console.log(chalk.dim(` codeyam editor register '{"name":"Empty","description":"...","url":"/","dimensions":["${dim}"],"mockData":{"routes":{"/api/...":{"body":[...]}}}}'`));
1026
1183
  checkbox('After each registration, check the response for `clientErrors`');
1027
1184
  console.log(chalk.yellow(' If clientErrors is non-empty → fix the issue and re-register the scenario'));
1028
1185
  console.log(chalk.yellow(' Fix client errors and re-register before moving on'));
1186
+ checkbox('CRITICAL: After EACH registration, view the captured screenshot to verify it');
1187
+ console.log(chalk.yellow(' Read the screenshot file path from the registration response and view it.'));
1188
+ console.log(chalk.yellow(' Does the screenshot actually SHOW the data you put in? If you seeded 5 articles'));
1189
+ console.log(chalk.yellow(' but the screenshot shows an empty page, the scenario is broken — fix the code or data.'));
1190
+ console.log(chalk.yellow(' Do NOT create scenarios with data the app cannot yet render.'));
1029
1191
  console.log();
1030
1192
  console.log(chalk.dim('Focus on creating and registering app-level scenarios. Code fixes happen in step 10 if needed.'));
1193
+ console.log();
1194
+ console.log(chalk.bold.cyan("Verify your work — screenshots don't lie:"));
1195
+ console.log(chalk.cyan(' • View every captured screenshot. Does it show what the scenario name promises?'));
1196
+ console.log(chalk.cyan(' • A "Library with Articles" screenshot showing an empty page means the scenario is BROKEN.'));
1197
+ console.log(chalk.cyan(' • Is the seed data diverse — different lengths, categories, counts — or just placeholders?'));
1198
+ console.log(chalk.cyan(' • Only create scenarios for states the CURRENT code can render.'));
1199
+ console.log();
1200
+ console.log(chalk.bold.yellow('GATE: Before proceeding, run `codeyam editor scenario-coverage`'));
1201
+ console.log(chalk.yellow(' This checks which existing scenarios have stale screenshots.'));
1202
+ console.log(chalk.yellow(' Re-register every stale scenario listed until the check passes.'));
1031
1203
  stopGate(8);
1032
1204
  }
1033
1205
  // ─── Step 9: User Scenarios ───────────────────────────────────────────
@@ -1048,34 +1220,37 @@ function printStep9(root, feature) {
1048
1220
  if (isResuming) {
1049
1221
  printResumptionHeader(9);
1050
1222
  }
1051
- console.log('Create per-persona scenarios if the app has users. Skip to step 10 if no users.');
1223
+ console.log('Create per-persona variations of existing scenarios. Skip to step 10 if no users.');
1052
1224
  console.log();
1053
- console.log(chalk.bold('If the app has users:'));
1054
- checkbox('Check existing scenarios: `codeyam editor scenarios`');
1055
- console.log(chalk.dim(' Reuse existing persona scenarios — update data to reflect current changes.'));
1056
- console.log(chalk.dim(' Re-register with the same name to update. Only add new personas if needed.'));
1057
- checkbox('Ensure scenarios for each user persona (admin, regular user, new user)');
1225
+ console.log(chalk.bold('If the app has NO users/auth:'));
1226
+ console.log(chalk.dim(' Skip this step and proceed to step 10 (Verify).'));
1227
+ console.log();
1228
+ console.log(chalk.bold('If the app has users/auth:'));
1229
+ console.log();
1230
+ console.log(chalk.bold('Goal: Create persona variations of EXISTING app scenarios.'));
1231
+ console.log(chalk.dim(' Do NOT create standalone persona scenarios. Each user-persona scenario should be'));
1232
+ console.log(chalk.dim(' a variation of an existing app scenario from step 8, with user-specific state layered on.'));
1233
+ console.log();
1234
+ console.log(chalk.bold('Checklist:'));
1235
+ checkbox('Run `codeyam editor scenarios` — list all existing app scenarios');
1236
+ checkbox('For EACH existing app scenario, create a logged-in variation:');
1237
+ console.log(chalk.dim(' Copy the scenario seed data and add "session":{"cookieValue":"sess_alice"} + user seed'));
1238
+ console.log(chalk.dim(' Name: "<Original Name> - Logged In" (e.g. "Full Catalog - Logged In")'));
1239
+ console.log(chalk.dim(' Step 8 scenarios already serve as logged-out versions (no session cookie)'));
1240
+ checkbox('If there are multiple user roles (admin, regular, etc.), create role-specific variations too');
1058
1241
  console.log(chalk.dim(' Each persona scenario layers user-specific seed data on top of an app scenario'));
1059
- checkbox('If using seed-based scenarios, register with type "user" and baseScenario:');
1060
- console.log(chalk.dim(` codeyam editor register '{"name":"Admin User","type":"user","url":"/","baseScenario":"<app-scenario-id>","seed":{"users":[{"id":1,"role":"admin"}]}}'`));
1061
- console.log(chalk.dim(' Always include "url" — the page to screenshot (e.g. "/", "/dashboard", "/drinks/1")'));
1062
- console.log(chalk.dim(" The base scenario's seed data is merged with this scenario's seed data (overlay wins)."));
1063
- checkbox('If the app uses auth or other patterns: see FEATURE_PATTERNS.md for per-persona scenario guidance');
1064
- checkbox('If using mock-based scenarios, register with mockData as before:');
1065
- console.log(chalk.dim(` codeyam editor register '{"name":"...","description":"...","mockData":{"routes":{"/api/...":{"body":[...]}}}}'`));
1242
+ checkbox('Include "dimensions" inherit from the base app scenario, or override if the persona implies a different device');
1243
+ console.log(chalk.dim(' e.g. a mobile-first user persona should use "dimensions":["Mobile"] even if the base scenario is Desktop.'));
1066
1244
  checkbox('After each registration, check the response for `clientErrors`');
1067
1245
  console.log(chalk.yellow(' If clientErrors is non-empty → fix the issue and re-register the scenario'));
1068
- console.log(chalk.yellow(' Fix client errors and re-register before moving on'));
1069
1246
  console.log();
1070
- console.log(chalk.bold('If the app has NO users:'));
1071
- console.log(chalk.dim(' Skip this step and proceed to step 10 (Verify).'));
1072
- console.log();
1073
- console.log(chalk.dim('Focus on creating user-persona scenarios (or skip if no users). Code fixes happen in step 10 if needed.'));
1247
+ console.log(chalk.dim('See FEATURE_PATTERNS.md and AUTH_PATTERNS.md for auth scenario guidance.'));
1074
1248
  stopGate(9);
1075
1249
  }
1076
1250
  // ─── Step 10: Verify ──────────────────────────────────────────────────
1077
1251
  function printStep10(root, feature) {
1078
1252
  const port = getServerPort();
1253
+ const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
1079
1254
  const prevState = readState(root);
1080
1255
  const isResuming = prevState?.step === 10;
1081
1256
  const now = new Date().toISOString();
@@ -1100,7 +1275,8 @@ function printStep10(root, feature) {
1100
1275
  console.log(chalk.dim(` codeyam editor register '{"name":"ComponentName - Scenario",...}'`));
1101
1276
  console.log();
1102
1277
  console.log(chalk.bold('Editor scenarios (App tab) — visual + error check:'));
1103
- checkbox(`Refresh the preview: \`codeyam editor preview\``);
1278
+ checkbox(`Refresh the preview: \`codeyam editor preview '{"dimension":"${dim}"}'\``);
1279
+ printDimensionGuidance(dim, dimNames);
1104
1280
  checkbox('Click through each app-level and user-persona scenario in the preview');
1105
1281
  checkbox('For seed-based scenarios: verify data renders correctly after switching');
1106
1282
  console.log(chalk.dim(' Switch between scenarios and confirm the app reflects the seeded data each time'));
@@ -1153,6 +1329,7 @@ function printStep11(root, feature) {
1153
1329
  // ─── Step 12: Review ──────────────────────────────────────────────────
1154
1330
  function printStep12(root, feature) {
1155
1331
  const port = getServerPort();
1332
+ const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
1156
1333
  const prevState = readState(root);
1157
1334
  const isResuming = prevState?.step === 12;
1158
1335
  const now = new Date().toISOString();
@@ -1171,7 +1348,8 @@ function printStep12(root, feature) {
1171
1348
  console.log('Verify all screenshots and checks pass before presenting to the user.');
1172
1349
  console.log();
1173
1350
  console.log(chalk.bold('Checklist (do all of this silently):'));
1174
- checkbox(`Refresh the preview: \`codeyam editor preview\``);
1351
+ checkbox(`Refresh the preview: \`codeyam editor preview '{"dimension":"${dim}"}'\``);
1352
+ printDimensionGuidance(dim, dimNames);
1175
1353
  checkbox('Verify each component has screenshots in the App tab (grouped under Components)');
1176
1354
  checkbox('If any are missing, re-register them using `codeyam editor register`');
1177
1355
  checkbox(`Check for client errors: \`codeyam editor client-errors\``);
@@ -1185,6 +1363,7 @@ function printStep12(root, feature) {
1185
1363
  // ─── Step 13: Present ─────────────────────────────────────────────────
1186
1364
  function printStep13(root, feature) {
1187
1365
  const port = getServerPort();
1366
+ const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
1188
1367
  const prevState = readState(root);
1189
1368
  const isResuming = prevState?.step === 13;
1190
1369
  const now = new Date().toISOString();
@@ -1209,8 +1388,10 @@ function printStep13(root, feature) {
1209
1388
  console.log(chalk.dim(' Prefer scenarios with changeStatus "new" or "edited" (directly changed in this session).'));
1210
1389
  console.log(chalk.dim(' For app-level scenarios, prefer those showing the new/changed functionality.'));
1211
1390
  console.log(chalk.dim(' NEVER pick a scenario with changeStatus null — those are unchanged from a previous commit.'));
1212
- checkbox('Switch the preview to that scenario using its `id`:');
1213
- console.log(chalk.dim(` codeyam editor preview '{"scenarioId":"<id>"}'`));
1391
+ checkbox('Switch the preview to that scenario using its `id` and `dimension`:');
1392
+ console.log(chalk.dim(` codeyam editor preview '{"scenarioId":"<id>","dimension":"${dim}"}'`));
1393
+ console.log(chalk.dim(' Use the dimension that matches the scenario — check its registered dimensions.'));
1394
+ printDimensionGuidance(dim, dimNames);
1214
1395
  checkbox(`Show the results panel: \`codeyam editor show-results\``);
1215
1396
  console.log(chalk.dim(' This opens a visual panel below the terminal showing all scenarios with screenshots.'));
1216
1397
  console.log(chalk.dim(' The user can click scenarios to switch the live preview.'));
@@ -1224,17 +1405,7 @@ function printStep13(root, feature) {
1224
1405
  chalk.dim(' — describe changes, then re-verify'));
1225
1406
  console.log();
1226
1407
  console.log(chalk.bold('If the user chooses "Save & commit":'));
1227
- checkbox(`Hide the results panel first: \`codeyam editor hide-results\``);
1228
- checkbox(`Git commit using the journal description: \`codeyam editor commit '{"message":"feat: <title>\\n\\n<journal description>"}'\``);
1229
- console.log(chalk.dim(' The commit message body MUST match the journal description exactly'));
1230
- checkbox(`Update journal with commit SHA: \`codeyam editor journal-update '{"time":"<journal entry time>","commitSha":"<sha>","commitMessage":"feat: <title>"}'\``);
1231
- console.log(chalk.green(' Then run: ') +
1232
- chalk.bold('codeyam editor steps') +
1233
- chalk.green(' to start the next feature'));
1234
- console.log();
1235
- console.log(chalk.red.bold(' If the user reports a bug or requests a fix after committing:'));
1236
- console.log(chalk.red.bold(' You MUST still run `codeyam editor change` before making any changes.'));
1237
- console.log(chalk.red.bold(' The change workflow applies to ALL changes — post-commit fixes are not an exception.'));
1408
+ checkbox('Advance to the commit step: `codeyam editor 14`');
1238
1409
  console.log();
1239
1410
  console.log(chalk.bold('If the user chooses "Make changes" (or asks for ANY change, even as a question):'));
1240
1411
  checkbox(`Hide the results panel: \`codeyam editor hide-results\``);
@@ -1244,6 +1415,89 @@ function printStep13(root, feature) {
1244
1415
  console.log(chalk.red.bold(' IMPORTANT: Always run the change command BEFORE writing any code.'));
1245
1416
  stopGate(13, { confirm: true });
1246
1417
  }
1418
+ // ─── Step 14: Commit ─────────────────────────────────────────────────
1419
+ function printStep14(root, feature) {
1420
+ const prevState = readState(root);
1421
+ const isResuming = prevState?.step === 14;
1422
+ const now = new Date().toISOString();
1423
+ writeState(root, {
1424
+ feature,
1425
+ step: 14,
1426
+ label: STEP_LABELS[14],
1427
+ startedAt: isResuming ? prevState.startedAt : now,
1428
+ featureStartedAt: prevState?.featureStartedAt || now,
1429
+ });
1430
+ logEvent(root, 'step', { step: 14, label: 'Commit', feature });
1431
+ stepHeader(14, 'Commit', feature);
1432
+ if (isResuming) {
1433
+ printResumptionHeader(14);
1434
+ }
1435
+ console.log('Commit all changes for this feature.');
1436
+ console.log();
1437
+ console.log(chalk.bold('Checklist:'));
1438
+ checkbox(`Hide the results panel: \`codeyam editor hide-results\``);
1439
+ checkbox(`Git commit using the journal description: \`codeyam editor commit '{"message":"feat: <title>\\n\\n<journal description>"}'\``);
1440
+ console.log(chalk.dim(' The commit message body MUST match the journal description exactly'));
1441
+ stopGate(14);
1442
+ }
1443
+ // ─── Step 15: Finalize ───────────────────────────────────────────────
1444
+ function printStep15(root, feature) {
1445
+ const prevState = readState(root);
1446
+ const isResuming = prevState?.step === 15;
1447
+ const now = new Date().toISOString();
1448
+ writeState(root, {
1449
+ feature,
1450
+ step: 15,
1451
+ label: STEP_LABELS[15],
1452
+ startedAt: isResuming ? prevState.startedAt : now,
1453
+ featureStartedAt: prevState?.featureStartedAt || now,
1454
+ });
1455
+ logEvent(root, 'step', { step: 15, label: 'Finalize', feature });
1456
+ stepHeader(15, 'Finalize', feature);
1457
+ if (isResuming) {
1458
+ printResumptionHeader(15);
1459
+ }
1460
+ console.log('Update the journal with the commit SHA and amend the commit.');
1461
+ console.log();
1462
+ console.log(chalk.bold('Checklist:'));
1463
+ checkbox(`Update journal with commit SHA: \`codeyam editor journal-update '{"time":"<journal entry time>","commitSha":"<sha>","commitMessage":"feat: <title>"}'\``);
1464
+ checkbox('Amend the commit to include the journal update: `git add .codeyam/journal/ && git commit --amend --no-edit`');
1465
+ console.log(chalk.dim(' The journal-update modifies journal files after the commit — amend to keep the tree clean.'));
1466
+ stopGate(15);
1467
+ }
1468
+ // ─── Step 16: Push ───────────────────────────────────────────────────
1469
+ function printStep16(root, feature) {
1470
+ const prevState = readState(root);
1471
+ const isResuming = prevState?.step === 16;
1472
+ const now = new Date().toISOString();
1473
+ writeState(root, {
1474
+ feature,
1475
+ step: 16,
1476
+ label: STEP_LABELS[16],
1477
+ startedAt: isResuming ? prevState.startedAt : now,
1478
+ featureStartedAt: prevState?.featureStartedAt || now,
1479
+ });
1480
+ logEvent(root, 'step', { step: 16, label: 'Push', feature });
1481
+ stepHeader(16, 'Push', feature);
1482
+ if (isResuming) {
1483
+ printResumptionHeader(16);
1484
+ }
1485
+ console.log('Push the commit to the remote repository.');
1486
+ console.log();
1487
+ console.log(chalk.bold('Checklist:'));
1488
+ checkbox('Check if a git remote is configured: `git remote -v`');
1489
+ checkbox("Offer to push to remote (AskUserQuestion — STOP and wait for the user's answer before proceeding):");
1490
+ console.log(chalk.dim(' If a remote exists, ask (AskUserQuestion): "Would you like me to push this commit to the remote?" with options "Yes, push" and "No, skip"'));
1491
+ console.log(chalk.dim(' If the user says yes, run: `git push`'));
1492
+ console.log(chalk.dim(' If NO remote exists, ask (AskUserQuestion): "This project doesn\'t have a git remote yet. Would you like help setting one up? I can walk you through creating a GitHub repository." with options "Yes, set up remote" and "No, skip"'));
1493
+ console.log(chalk.dim(' If the user wants help, guide them through `gh repo create` or manual GitHub setup, then push.'));
1494
+ checkbox('After the user responds, run: `codeyam editor steps` to start the next feature');
1495
+ console.log();
1496
+ console.log(chalk.red.bold(' If the user reports a bug or requests a fix after committing:'));
1497
+ console.log(chalk.red.bold(' You MUST still run `codeyam editor change` before making any changes.'));
1498
+ console.log(chalk.red.bold(' The change workflow applies to ALL changes — post-commit fixes are not an exception.'));
1499
+ stopGate(16, { confirm: true });
1500
+ }
1247
1501
  // ─── Command definition ───────────────────────────────────────────────
1248
1502
  // ─── Analyze-imports subcommand ────────────────────────────────────────
1249
1503
  /**
@@ -1252,11 +1506,15 @@ function printStep13(root, feature) {
1252
1506
  * Runs data-structure-only analysis for all glossary entities, then outputs
1253
1507
  * an import graph and entity data structures as JSON to stdout.
1254
1508
  */
1255
- async function handleAnalyzeImports() {
1509
+ async function handleAnalyzeImports(options = {}) {
1256
1510
  const root = getProjectRoot();
1257
1511
  // Read glossary
1258
1512
  const glossaryPath = path.join(root, '.codeyam', 'glossary.json');
1259
1513
  if (!fs.existsSync(glossaryPath)) {
1514
+ if (options.silent) {
1515
+ // Internal caller — glossary doesn't exist yet, nothing to analyze
1516
+ return;
1517
+ }
1260
1518
  console.error(chalk.red('Error: .codeyam/glossary.json not found.'));
1261
1519
  console.error(chalk.dim(' Run codeyam editor 6 to create the glossary first.'));
1262
1520
  process.exit(1);
@@ -1274,15 +1532,24 @@ async function handleAnalyzeImports() {
1274
1532
  process.exit(1);
1275
1533
  }
1276
1534
  const filePaths = glossaryEntries.map((e) => e.filePath);
1277
- const entityNames = glossaryEntries.map((e) => e.name);
1535
+ // Include page files so pages get analyzed as entities too.
1536
+ // This allows the import graph to map page names to their dependencies.
1537
+ const { scanPageFilePaths } = await import('../utils/entityChangeStatus.server.js');
1538
+ const pageFilePaths = scanPageFilePaths(root);
1539
+ for (const pageFile of Object.values(pageFilePaths)) {
1540
+ if (!filePaths.includes(pageFile)) {
1541
+ filePaths.push(pageFile);
1542
+ }
1543
+ }
1278
1544
  const progress = new ProgressReporter();
1279
- // Run data-structure-only analysis for all entities
1280
- progress.start('Running import analysis for all glossary entities...');
1545
+ // Run data-structure-only analysis for all entities (glossary + pages)
1546
+ // Don't pass entityNames entities may not exist yet (fresh clone).
1547
+ // The analyzer will discover and create them from file paths.
1548
+ progress.start('Running import analysis for all entities...');
1281
1549
  try {
1282
1550
  await runAnalysisForEntities({
1283
1551
  projectRoot: root,
1284
1552
  filePaths,
1285
- entityNames,
1286
1553
  progress,
1287
1554
  onlyDataStructure: true,
1288
1555
  });
@@ -1300,7 +1567,9 @@ async function handleAnalyzeImports() {
1300
1567
  const entities = await loadEntities({});
1301
1568
  if (!entities || entities.length === 0) {
1302
1569
  progress.succeed('No entities found in database yet — skipping import analysis. This is normal on the first feature.');
1303
- console.log(JSON.stringify({ imports: {}, entities: {} }));
1570
+ if (!options.silent) {
1571
+ console.log(JSON.stringify({ imports: {}, entities: {} }));
1572
+ }
1304
1573
  return;
1305
1574
  }
1306
1575
  // Deduplicate to latest versions
@@ -1315,7 +1584,7 @@ async function handleAnalyzeImports() {
1315
1584
  latestByKey.set(key, entity);
1316
1585
  }
1317
1586
  }
1318
- const entityNameSet = new Set(entityNames);
1587
+ const entityNameSet = new Set(glossaryEntries.map((e) => e.name));
1319
1588
  const latestEntities = [...latestByKey.values()].filter((e) => entityNameSet.has(e.name));
1320
1589
  // Build import graph from importedExports metadata
1321
1590
  const imports = {};
@@ -1353,9 +1622,14 @@ async function handleAnalyzeImports() {
1353
1622
  };
1354
1623
  }
1355
1624
  progress.succeed('Done');
1356
- // Output combined JSON
1357
- const output = { imports, entities: entityData };
1358
- console.log(JSON.stringify(output, null, 2));
1625
+ // Output combined JSON (suppressed when called internally during startup)
1626
+ if (!options.silent) {
1627
+ const summary = formatApiSubcommandResult('analyze-imports', {
1628
+ imports,
1629
+ entities: entityData,
1630
+ });
1631
+ console.log(summary || JSON.stringify({ imports, entities: entityData }));
1632
+ }
1359
1633
  }
1360
1634
  // ─── Validate-seed subcommand ─────────────────────────────────────────
1361
1635
  /**
@@ -1538,6 +1812,17 @@ function formatApiSubcommandResult(subcommand, data) {
1538
1812
  }
1539
1813
  return parts.join(' ');
1540
1814
  }
1815
+ case 'analyze-imports': {
1816
+ const importEntries = Object.entries(data.imports || {});
1817
+ const entityEntries = Object.entries(data.entities || {});
1818
+ const parts = [];
1819
+ parts.push(`entities=${entityEntries.length}`);
1820
+ parts.push(`withImports=${importEntries.length}`);
1821
+ if (importEntries.length > 0) {
1822
+ parts.push(`imports: ${importEntries.map(([name, deps]) => `${name}→[${deps.join(',')}]`).join(' ')}`);
1823
+ }
1824
+ return parts.join(' ');
1825
+ }
1541
1826
  default:
1542
1827
  return null; // journal-list, show/hide-results: keep full JSON
1543
1828
  }
@@ -1550,8 +1835,9 @@ function formatApiSubcommandResult(subcommand, data) {
1550
1835
  */
1551
1836
  async function handleRegister(jsonArg) {
1552
1837
  if (!jsonArg) {
1838
+ const { defaultName: dim } = getProjectDimensions(getProjectRoot());
1553
1839
  console.error(chalk.red('Error: JSON argument required.'));
1554
- console.error(chalk.dim(' Usage: codeyam editor register \'{"name":"DrinkCard - Default","componentName":"DrinkCard","url":"/isolated-components/DrinkCard?s=Default"}\''));
1840
+ console.error(chalk.dim(` Usage: codeyam editor register '{"name":"DrinkCard - Default","componentName":"DrinkCard","url":"/isolated-components/DrinkCard?s=Default","dimensions":["${dim}"]}'`));
1555
1841
  console.error(chalk.dim(' For large payloads: codeyam editor register @/tmp/scenario.json'));
1556
1842
  process.exit(1);
1557
1843
  }
@@ -1563,55 +1849,74 @@ async function handleRegister(jsonArg) {
1563
1849
  }
1564
1850
  process.exit(1);
1565
1851
  }
1566
- const body = parsed.body;
1852
+ // Normalize to array for uniform handling
1853
+ const items = Array.isArray(parsed.body) ? parsed.body : [parsed.body];
1567
1854
  const port = getServerPort();
1568
1855
  const url = `http://localhost:${port}/api/editor-register-scenario`;
1569
- try {
1570
- const res = await fetch(url, {
1571
- method: 'POST',
1572
- headers: { 'Content-Type': 'application/json' },
1573
- body: JSON.stringify(body),
1574
- });
1575
- const data = await res.json();
1576
- // Print concise summary instead of raw JSON so Claude doesn't need python3
1577
- const parts = [];
1578
- parts.push(`success=${data.success}`);
1579
- if (data.scenario?.name)
1580
- parts.push(`name="${data.scenario.name}"`);
1581
- parts.push(`screenshot=${!!data.screenshotCaptured}`);
1582
- if (data.capturedViewport) {
1583
- parts.push(`viewport=${data.capturedViewport.width}×${data.capturedViewport.height}`);
1584
- }
1585
- if (data.clientErrors?.length > 0) {
1586
- parts.push(chalk.red(`errors=${data.clientErrors.length}`));
1587
- }
1588
- else {
1589
- parts.push(`errors=0`);
1590
- }
1591
- if (data.seedResult)
1592
- parts.push(`seed=${data.seedResult.success}`);
1593
- if (data.captureError)
1594
- parts.push(chalk.yellow(`captureError="${data.captureError}"`));
1595
- console.log(parts.join(' '));
1596
- // Surface client errors prominently so they can't be missed
1597
- if (data.clientErrors && data.clientErrors.length > 0) {
1598
- console.log(chalk.red.bold(`⚠ WARNING: ${data.clientErrors.length} client error${data.clientErrors.length !== 1 ? 's' : ''} detected during capture:`));
1599
- for (const err of data.clientErrors) {
1600
- console.log(chalk.red(` → ${err}`));
1856
+ const isBatch = items.length > 1;
1857
+ let succeeded = 0;
1858
+ let failed = 0;
1859
+ for (let i = 0; i < items.length; i++) {
1860
+ const body = items[i];
1861
+ const prefix = isBatch ? chalk.dim(`[${i + 1}/${items.length}] `) : '';
1862
+ try {
1863
+ const res = await fetch(url, {
1864
+ method: 'POST',
1865
+ headers: { 'Content-Type': 'application/json' },
1866
+ body: JSON.stringify(body),
1867
+ });
1868
+ const data = await res.json();
1869
+ // Print concise summary instead of raw JSON so Claude doesn't need python3
1870
+ const parts = [];
1871
+ parts.push(`success=${data.success}`);
1872
+ if (data.scenario?.name)
1873
+ parts.push(`name="${data.scenario.name}"`);
1874
+ parts.push(`screenshot=${!!data.screenshotCaptured}`);
1875
+ if (data.capturedViewport) {
1876
+ parts.push(`viewport=${data.capturedViewport.width}×${data.capturedViewport.height}`);
1877
+ }
1878
+ if (data.scenario?.dimension)
1879
+ parts.push(`dimension="${data.scenario.dimension}"`);
1880
+ if (data.clientErrors?.length > 0) {
1881
+ parts.push(chalk.red(`errors=${data.clientErrors.length}`));
1882
+ }
1883
+ else {
1884
+ parts.push(`errors=0`);
1885
+ }
1886
+ if (data.seedResult)
1887
+ parts.push(`seed=${data.seedResult.success}`);
1888
+ if (data.captureError)
1889
+ parts.push(chalk.yellow(`captureError="${data.captureError}"`));
1890
+ console.log(prefix + parts.join(' '));
1891
+ // Surface client errors prominently so they can't be missed
1892
+ if (data.clientErrors && data.clientErrors.length > 0) {
1893
+ console.log(chalk.red.bold(`⚠ WARNING: ${data.clientErrors.length} client error${data.clientErrors.length !== 1 ? 's' : ''} detected during capture:`));
1894
+ for (const err of data.clientErrors) {
1895
+ console.log(chalk.red(` → ${err}`));
1896
+ }
1897
+ console.log(chalk.yellow(' The screenshot may show an error screen instead of the actual component.'));
1898
+ console.log(chalk.yellow(' Fix the issue and re-register this scenario.'));
1899
+ }
1900
+ if (!res.ok) {
1901
+ console.error(chalk.dim(JSON.stringify(data, null, 2)));
1902
+ failed++;
1903
+ }
1904
+ else {
1905
+ succeeded++;
1601
1906
  }
1602
- console.log(chalk.yellow(' The screenshot may show an error screen instead of the actual component.'));
1603
- console.log(chalk.yellow(' Fix the issue and re-register this scenario.'));
1604
1907
  }
1605
- if (!res.ok) {
1606
- console.error(chalk.dim(JSON.stringify(data, null, 2)));
1607
- process.exit(1);
1908
+ catch (error) {
1909
+ const msg = error instanceof Error ? error.message : String(error);
1910
+ console.error(prefix + chalk.red(`Error: Could not reach editor server at ${url}`));
1911
+ console.error(chalk.dim(` ${msg}`));
1912
+ console.error(chalk.dim(' Is the editor running? Start it with: codeyam editor'));
1913
+ failed++;
1608
1914
  }
1609
1915
  }
1610
- catch (error) {
1611
- const msg = error instanceof Error ? error.message : String(error);
1612
- console.error(chalk.red(`Error: Could not reach editor server at ${url}`));
1613
- console.error(chalk.dim(` ${msg}`));
1614
- console.error(chalk.dim(' Is the editor running? Start it with: codeyam editor'));
1916
+ if (isBatch) {
1917
+ console.log(chalk.dim(`\nBatch complete: ${succeeded}/${items.length} succeeded`));
1918
+ }
1919
+ if (failed > 0) {
1615
1920
  process.exit(1);
1616
1921
  }
1617
1922
  }
@@ -1632,11 +1937,23 @@ async function handleDependents(entityName) {
1632
1937
  const progress = new ProgressReporter();
1633
1938
  progress.start('Loading entities from database...');
1634
1939
  await initializeEnvironment();
1635
- const allEntities = await loadEntities({});
1940
+ let allEntities = await loadEntities({});
1636
1941
  if (!allEntities || allEntities.length === 0) {
1637
- progress.fail('No entities found in database');
1638
- console.error(chalk.dim(' Run codeyam editor analyze-imports first to populate entity data.'));
1639
- process.exit(1);
1942
+ progress.succeed('No entities found running analyze-imports first...');
1943
+ try {
1944
+ await handleAnalyzeImports({ silent: true });
1945
+ }
1946
+ catch {
1947
+ console.error(chalk.red('Could not build import graph. Run `codeyam editor analyze-imports` manually.'));
1948
+ process.exit(1);
1949
+ }
1950
+ // Reload entities after analysis
1951
+ const reloaded = await loadEntities({});
1952
+ if (!reloaded || reloaded.length === 0) {
1953
+ progress.fail('No entities found even after analyze-imports');
1954
+ process.exit(1);
1955
+ }
1956
+ allEntities = reloaded;
1640
1957
  }
1641
1958
  // Find the target entity by name (case-insensitive match)
1642
1959
  const targetEntities = allEntities.filter((e) => e.name.toLowerCase() === entityName.toLowerCase());
@@ -1723,15 +2040,16 @@ async function handleDependents(entityName) {
1723
2040
  * `codeyam editor change <feature>`
1724
2041
  *
1725
2042
  * Prints a condensed post-change checklist that guides Claude through
1726
- * re-verifying after user-requested modifications. This is the "change
1727
- * loop" it replaces the freeform "make changes" path with structured
1728
- * instructions that always loop back to step 13 present.
2043
+ * re-verifying after user-requested modifications. When called from
2044
+ * step 13+, this loops back to step 13 (present). When called from an
2045
+ * earlier step, it returns to that step so the normal flow continues.
1729
2046
  */
1730
2047
  function handleChange(feature) {
1731
2048
  const root = getProjectRoot();
2049
+ const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
2050
+ const state = readState(root);
1732
2051
  if (!feature) {
1733
2052
  // Try to read feature from state
1734
- const state = readState(root);
1735
2053
  if (state?.feature) {
1736
2054
  feature = state.feature;
1737
2055
  }
@@ -1741,6 +2059,7 @@ function handleChange(feature) {
1741
2059
  process.exit(1);
1742
2060
  }
1743
2061
  }
2062
+ const currentStep = state?.step ?? 13;
1744
2063
  const port = getServerPort();
1745
2064
  console.log();
1746
2065
  console.log(chalk.bold.cyan('━━━ Change Loop ━━━'));
@@ -1748,9 +2067,19 @@ function handleChange(feature) {
1748
2067
  console.log();
1749
2068
  console.log('The user has requested changes. Follow this checklist after making them.');
1750
2069
  console.log();
2070
+ const designSystem = readDesignSystem(root);
2071
+ if (designSystem) {
2072
+ console.log(chalk.bold.magenta('Design System (active):'));
2073
+ console.log(chalk.magenta(' Use existing CSS custom property tokens when making visual changes — no hardcoded px or hex values.'));
2074
+ console.log(chalk.magenta(' Use var(--text-sm) not 14, var(--spacing-lg) not 16px, var(--text-primary) not #1A1B25.'));
2075
+ console.log();
2076
+ console.log(designSystem);
2077
+ console.log();
2078
+ }
1751
2079
  console.log(chalk.bold.cyan('Keep the preview moving:'));
1752
2080
  console.log(chalk.cyan(` Refresh after EACH individual change — not after all changes are done:`));
1753
- console.log(chalk.cyan(` codeyam editor preview`));
2081
+ console.log(chalk.cyan(` codeyam editor preview '{"dimension":"${dim}"}'`));
2082
+ printDimensionGuidance(dim, dimNames);
1754
2083
  console.log(chalk.cyan(' The user is watching the preview. Let them see progress incrementally.'));
1755
2084
  console.log();
1756
2085
  console.log(chalk.bold('0. Close the results panel:'));
@@ -1758,7 +2087,7 @@ function handleChange(feature) {
1758
2087
  console.log();
1759
2088
  console.log(chalk.bold('1. Re-register affected component scenarios:'));
1760
2089
  checkbox('For each component you modified, re-register ALL its scenarios');
1761
- console.log(chalk.dim(` codeyam editor register '{"name":"ComponentName - ScenarioName","componentName":"ComponentName","componentPath":"app/components/ComponentName.tsx","url":"/isolated-components/ComponentName?s=ScenarioName"}'`));
2090
+ console.log(chalk.dim(` codeyam editor register '{"name":"ComponentName - ScenarioName","componentName":"ComponentName","componentPath":"app/components/ComponentName.tsx","url":"/isolated-components/ComponentName?s=ScenarioName","dimensions":["${dim}"]}'`));
1762
2091
  checkbox('For any NEW components: `codeyam editor isolate NewComponent` then create isolation routes and register scenarios');
1763
2092
  console.log();
1764
2093
  console.log(chalk.bold('2. Re-run affected tests:'));
@@ -1769,7 +2098,6 @@ function handleChange(feature) {
1769
2098
  console.log(chalk.dim(` codeyam editor dev-server '{"action":"restart"}'`));
1770
2099
  console.log();
1771
2100
  console.log(chalk.bold('3. Re-capture app-level scenarios:'));
1772
- checkbox('Run `codeyam editor analyze-imports` to refresh the import graph after code changes');
1773
2101
  checkbox('For each changed component, run `codeyam editor dependents ComponentName` to find affected pages');
1774
2102
  checkbox('Run `codeyam editor scenarios` to list all existing scenarios');
1775
2103
  checkbox('For EACH page listed by dependents: find its existing scenarios in the list and re-register every one');
@@ -1777,15 +2105,22 @@ function handleChange(feature) {
1777
2105
  console.log(chalk.dim(' Re-register with the SAME name to update — do NOT create new/duplicate scenarios.'));
1778
2106
  console.log(chalk.dim(' Example: if Header changed and Home, Catalog, Detail pages use it,'));
1779
2107
  console.log(chalk.dim(' re-register "Home - Default", "Catalog - Full", "Detail - WithReviews", etc.'));
2108
+ checkbox("Enrich existing scenario data to exercise the change — don't just re-register unchanged data");
2109
+ console.log(chalk.dim(' Add data that demonstrates what changed: new fields, relationships, states, content variety.'));
2110
+ checkbox('After each re-registration, view the screenshot to verify data is visible');
2111
+ console.log(chalk.dim(" If the screenshot doesn't show the data you put in, the scenario is broken."));
1780
2112
  console.log();
1781
2113
  console.log(chalk.bold('4. Verify completeness:'));
1782
- checkbox(`Refresh the preview: \`codeyam editor preview\``);
1783
- checkbox(`Navigate to key pages to verify changes: \`codeyam editor preview '{"path":"/your-route"}'\``);
2114
+ checkbox(`Refresh the preview: \`codeyam editor preview '{"dimension":"${dim}"}'\``);
2115
+ checkbox(`Navigate to key pages to verify changes: \`codeyam editor preview '{"path":"/your-route","dimension":"${dim}"}'\``);
2116
+ printDimensionGuidance(dim, dimNames);
2117
+ checkbox('Run `codeyam editor scenario-coverage` — all affected scenarios must be fresh');
1784
2118
  checkbox('Run `codeyam editor audit` — all checks must pass');
1785
2119
  checkbox(`Check for client-side errors: \`codeyam editor client-errors\``);
1786
2120
  console.log(chalk.dim(' If `hasContent=false`, the preview is blank — fix the code before proceeding.'));
1787
2121
  console.log(chalk.dim(' If `liveErrors>0`, there are JS errors in the preview — fix them.'));
1788
2122
  checkbox('Fix any errors, then re-register affected scenarios');
2123
+ checkbox('If changes affect setup, new dependencies, or new scripts: update `README.md` and `npm run setup`');
1789
2124
  console.log();
1790
2125
  console.log(chalk.bold('5. Update the existing journal entry:'));
1791
2126
  checkbox('Get existing entry: `codeyam editor journal-list`');
@@ -1794,14 +2129,46 @@ function handleChange(feature) {
1794
2129
  console.log(chalk.dim(' Always update the existing uncommitted entry — do NOT create a new one.'));
1795
2130
  console.log(chalk.dim(' Only create a new entry (POST) if no uncommitted entry exists for this feature.'));
1796
2131
  console.log();
1797
- console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
1798
- console.log(chalk.red.bold(' REQUIRED: Show Results'));
1799
- console.log(chalk.red.bold(' When all checks pass, you MUST run: ') +
1800
- chalk.bold(`codeyam editor 13`));
1801
- console.log(chalk.red.bold(' The user ALWAYS expects to see results after changes. DO NOT skip this step.'));
1802
- console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
2132
+ // If the change was initiated from a step before 13, return to that step
2133
+ // instead of jumping to step 13. The change workflow should only loop to
2134
+ // step 13 when changes are requested FROM step 13.
2135
+ if (currentStep < 13) {
2136
+ console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
2137
+ console.log(chalk.red.bold(' REQUIRED: Return to current step'));
2138
+ console.log(chalk.red.bold(' When all checks pass, you MUST run: ') +
2139
+ chalk.bold(`codeyam editor ${currentStep}`));
2140
+ console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
2141
+ }
2142
+ else {
2143
+ console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
2144
+ console.log(chalk.red.bold(' REQUIRED: Show Results'));
2145
+ console.log(chalk.red.bold(' When all checks pass, you MUST run: ') +
2146
+ chalk.bold(`codeyam editor 13`));
2147
+ console.log(chalk.red.bold(' The user ALWAYS expects to see results after changes. DO NOT skip this step.'));
2148
+ console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
2149
+ }
1803
2150
  console.log();
1804
2151
  }
2152
+ // ─── Audit gate ─────────────────────────────────────────────────────
2153
+ /**
2154
+ * Silently check whether the audit passes. Returns true if allPassing,
2155
+ * false if any issues remain or the server is unreachable.
2156
+ * Used as a hard gate before steps 8+.
2157
+ */
2158
+ async function checkAuditGate() {
2159
+ const port = getServerPort();
2160
+ try {
2161
+ const res = await fetch(`http://localhost:${port}/api/editor-audit`);
2162
+ if (!res.ok)
2163
+ return false;
2164
+ const data = await res.json();
2165
+ return data?.summary?.allPassing === true;
2166
+ }
2167
+ catch {
2168
+ // Server not running or unreachable — can't verify, so don't block
2169
+ return true;
2170
+ }
2171
+ }
1805
2172
  // ─── Audit subcommand ────────────────────────────────────────────────
1806
2173
  /**
1807
2174
  * `codeyam editor audit`
@@ -1914,6 +2281,17 @@ async function handleAudit() {
1914
2281
  if (!allOk) {
1915
2282
  process.exit(1);
1916
2283
  }
2284
+ // Auto-run analyze-imports when audit passes — this builds the import graph
2285
+ // so the App tab can show component/function dependencies for each page.
2286
+ // Previously this was a manual step that Claude had to remember to run.
2287
+ console.log(chalk.dim('Building import graph...'));
2288
+ try {
2289
+ await handleAnalyzeImports({ silent: true });
2290
+ }
2291
+ catch {
2292
+ // Non-fatal — audit passed, import graph is a bonus
2293
+ console.log(chalk.yellow('Warning: Could not build import graph. Run `codeyam editor analyze-imports` manually.'));
2294
+ }
1917
2295
  }
1918
2296
  // ─── Scenarios subcommand ─────────────────────────────────────────────
1919
2297
  async function handleScenarios() {
@@ -2001,6 +2379,77 @@ async function handleScenarios() {
2001
2379
  const total = scenarios.length;
2002
2380
  const groupCount = groups.size;
2003
2381
  console.log(chalk.dim(`${total} scenario${total !== 1 ? 's' : ''} across ${groupCount} component${groupCount !== 1 ? 's' : ''}`));
2382
+ // Show screenshot paths so Claude can verify images with the Read tool
2383
+ const withScreenshots = scenarios.filter((s) => s.screenshotPath);
2384
+ if (withScreenshots.length > 0) {
2385
+ console.log();
2386
+ console.log(chalk.bold.cyan('Screenshots:'));
2387
+ for (const s of withScreenshots) {
2388
+ console.log(chalk.dim(` ${s.name}: ${s.screenshotPath}`));
2389
+ }
2390
+ }
2391
+ console.log();
2392
+ }
2393
+ // ─── Scenario Coverage subcommand ───────────────────────────────────
2394
+ async function handleScenarioCoverage() {
2395
+ const port = getServerPort();
2396
+ const url = `http://localhost:${port}/api/editor-scenario-coverage`;
2397
+ let data;
2398
+ try {
2399
+ const res = await fetch(url);
2400
+ if (!res.ok) {
2401
+ console.error(chalk.red(`Error: Scenario coverage endpoint returned ${res.status}`));
2402
+ process.exit(1);
2403
+ }
2404
+ data = await res.json();
2405
+ }
2406
+ catch (err) {
2407
+ console.error(chalk.red('Error: Could not reach the CodeYam server. Is it running?'));
2408
+ console.error(chalk.dim(` ${err.message}`));
2409
+ process.exit(1);
2410
+ }
2411
+ console.log();
2412
+ console.log(chalk.bold.cyan('━━━ Scenario Coverage ━━━'));
2413
+ console.log();
2414
+ if (data.note) {
2415
+ console.log(chalk.yellow(data.note));
2416
+ console.log();
2417
+ return;
2418
+ }
2419
+ // Fresh scenarios (already recaptured this feature)
2420
+ if (data.freshScenarios.length > 0) {
2421
+ console.log(chalk.green(`✓ ${data.freshScenarios.length} scenario(s) captured this feature:`));
2422
+ for (const s of data.freshScenarios) {
2423
+ console.log(chalk.dim(` ${s.name} (${s.url || '/'})`));
2424
+ }
2425
+ console.log();
2426
+ }
2427
+ // Stale scenarios (need recapturing)
2428
+ if (data.staleScenarios.length > 0) {
2429
+ console.log(chalk.red(`✗ ${data.staleScenarios.length} scenario(s) need re-registering (stale screenshots):`));
2430
+ for (const s of data.staleScenarios) {
2431
+ console.log(chalk.yellow(` ${s.name} (${s.url || '/'}) — ${s.changeStatus} but not recaptured`));
2432
+ }
2433
+ console.log();
2434
+ console.log(chalk.yellow('Re-register each stale scenario with the SAME name to update its screenshot.'));
2435
+ console.log(chalk.yellow("Enhance the seed data to reflect this feature's changes where relevant."));
2436
+ console.log();
2437
+ }
2438
+ // Uncovered pages (changed but no scenarios at all)
2439
+ if (data.uncoveredPages.length > 0) {
2440
+ console.log(chalk.red(`✗ ${data.uncoveredPages.length} page(s) affected by changes with no scenarios:`));
2441
+ for (const p of data.uncoveredPages) {
2442
+ console.log(chalk.yellow(` ${p.entityName} — ${p.changeStatus}`));
2443
+ }
2444
+ console.log();
2445
+ }
2446
+ // Summary
2447
+ if (data.pass) {
2448
+ console.log(chalk.green.bold('PASS — all affected scenarios are fresh'));
2449
+ }
2450
+ else {
2451
+ console.log(chalk.red.bold('FAIL — re-register stale scenarios before proceeding'));
2452
+ }
2004
2453
  console.log();
2005
2454
  }
2006
2455
  // ─── Template subcommand ─────────────────────────────────────────────
@@ -2090,6 +2539,9 @@ async function handleTemplate() {
2090
2539
  // Config parse error is non-fatal
2091
2540
  }
2092
2541
  }
2542
+ // 5b. Write a fresh prototypeId so the proxy clears stale localStorage
2543
+ const activeScenarioPath = path.join(root, '.codeyam', 'active-scenario.json');
2544
+ fs.writeFileSync(activeScenarioPath, JSON.stringify({ prototypeId: Date.now().toString() }), 'utf-8');
2093
2545
  // 6. Trigger editor-refresh so the server picks up the new project
2094
2546
  console.log(chalk.bold('Refreshing editor...'));
2095
2547
  try {
@@ -2108,17 +2560,14 @@ async function handleTemplate() {
2108
2560
  // ─── Sync subcommand ─────────────────────────────────────────────────
2109
2561
  /**
2110
2562
  * `codeyam editor sync`
2111
- * Import scenarios from scenarios-manifest.json into the local database.
2563
+ * Import scenarios from editor-scenarios/ files into the local database.
2564
+ * Falls back to legacy scenarios-manifest.json if no _metadata files found.
2112
2565
  */
2113
2566
  async function handleSync() {
2114
2567
  const root = getProjectRoot();
2115
- const manifest = readManifest(root);
2116
- if (!manifest) {
2117
- console.log(chalk.yellow('No scenarios-manifest.json found. Nothing to sync.'));
2118
- return;
2119
- }
2120
- if (manifest.scenarios.length === 0) {
2121
- console.log(chalk.dim('Manifest is empty. Nothing to sync.'));
2568
+ const entries = scanScenarioFiles(root);
2569
+ if (entries.length === 0) {
2570
+ console.log(chalk.yellow('No scenario files with metadata found. Nothing to sync.'));
2122
2571
  return;
2123
2572
  }
2124
2573
  const configPath = path.join(root, '.codeyam', 'config.json');
@@ -2142,13 +2591,12 @@ async function handleSync() {
2142
2591
  const { project } = await requireBranchAndProject(projectSlug);
2143
2592
  const { getDatabase } = await import('../../../packages/database/index.js');
2144
2593
  const db = getDatabase();
2145
- // Fetch existing editor scenarios
2146
2594
  const existingRows = await db
2147
2595
  .selectFrom('editor_scenarios')
2148
2596
  .select(['id', 'updated_at'])
2149
2597
  .where('project_id', '=', project.id)
2150
2598
  .execute();
2151
- const result = await syncManifestToDatabase(root, project.id, existingRows, async (row) => {
2599
+ const result = await syncScenarioFilesToDatabase(root, project.id, existingRows, async (row) => {
2152
2600
  await db
2153
2601
  .insertInto('editor_scenarios')
2154
2602
  .values(row)
@@ -2173,6 +2621,34 @@ async function handleSync() {
2173
2621
  parts.push(`${result.skipped} unchanged`);
2174
2622
  console.log(chalk.green(`Synced scenarios: ${parts.join(', ')}`));
2175
2623
  }
2624
+ // Migrate legacy scenario formats after sync, then re-sync if anything was fixed
2625
+ try {
2626
+ const migrateResult = migrateScenarioFormats(root);
2627
+ if (migrateResult.fixed > 0) {
2628
+ console.log(chalk.green(`Migrated ${migrateResult.fixed} scenario file${migrateResult.fixed > 1 ? 's' : ''} to current format`));
2629
+ // Re-sync so the DB reflects the corrected file metadata
2630
+ const refreshedRows = await db
2631
+ .selectFrom('editor_scenarios')
2632
+ .select(['id', 'updated_at'])
2633
+ .where('project_id', '=', project.id)
2634
+ .execute();
2635
+ await syncScenarioFilesToDatabase(root, project.id, refreshedRows, async (row) => {
2636
+ await db
2637
+ .insertInto('editor_scenarios')
2638
+ .values(row)
2639
+ .execute();
2640
+ }, async (id, row) => {
2641
+ await db
2642
+ .updateTable('editor_scenarios')
2643
+ .set(row)
2644
+ .where('id', '=', id)
2645
+ .execute();
2646
+ });
2647
+ }
2648
+ }
2649
+ catch {
2650
+ // Non-fatal
2651
+ }
2176
2652
  }
2177
2653
  // ─── Verify Images subcommand ─────────────────────────────────────────
2178
2654
  async function handleVerifyImages(jsonArg) {
@@ -2305,8 +2781,11 @@ function handleEditorDebug(args) {
2305
2781
  11: printStep11,
2306
2782
  12: printStep12,
2307
2783
  13: printStep13,
2784
+ 14: printStep14,
2785
+ 15: printStep15,
2786
+ 16: printStep16,
2308
2787
  };
2309
- for (let step = 1; step <= 13; step++) {
2788
+ for (let step = 1; step <= 16; step++) {
2310
2789
  const stepId = `step-${step}`;
2311
2790
  if (!wants(stepId))
2312
2791
  continue;
@@ -2385,8 +2864,8 @@ const editorCommand = {
2385
2864
  describe: 'Editor mode guided workflow',
2386
2865
  builder: (yargs) => {
2387
2866
  const stepDescription = IS_INTERNAL_BUILD
2388
- ? 'Step number (1-13) or subcommand (template, register, isolate, analyze-imports, dependents, audit, scenarios, change, sync, debug, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors)'
2389
- : 'Step number (1-13) or subcommand (template, register, isolate, analyze-imports, dependents, audit, scenarios, change, sync, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors)';
2867
+ ? 'Step number (1-16) or subcommand (template, register, isolate, analyze-imports, dependents, audit, scenarios, scenario-coverage, change, sync, debug, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors)'
2868
+ : 'Step number (1-16) or subcommand (template, register, isolate, analyze-imports, dependents, audit, scenarios, scenario-coverage, change, sync, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors)';
2390
2869
  let builder = yargs
2391
2870
  .positional('step', {
2392
2871
  type: 'string',
@@ -2422,7 +2901,7 @@ const editorCommand = {
2422
2901
  builder = builder
2423
2902
  .option('target', {
2424
2903
  type: 'string',
2425
- describe: 'Debug target (setup, overview, overview-with-state, step-1..step-13, or comma-separated list)',
2904
+ describe: 'Debug target (setup, overview, overview-with-state, step-1..step-16, or comma-separated list)',
2426
2905
  })
2427
2906
  .option('resume', {
2428
2907
  type: 'boolean',
@@ -2497,6 +2976,11 @@ const editorCommand = {
2497
2976
  await handleScenarios();
2498
2977
  return;
2499
2978
  }
2979
+ // Subcommand: codeyam editor scenario-coverage
2980
+ if (argv.step === 'scenario-coverage') {
2981
+ await handleScenarioCoverage();
2982
+ return;
2983
+ }
2500
2984
  // Subcommand: codeyam editor change <feature>
2501
2985
  if (argv.step === 'change') {
2502
2986
  handleChange(argv.json || '');
@@ -2549,9 +3033,9 @@ const editorCommand = {
2549
3033
  }
2550
3034
  else {
2551
3035
  const state = readState(root);
2552
- // Clear prompt file when feature is done (step 13) so the hook
3036
+ // Clear prompt file when feature is done (step 16) so the hook
2553
3037
  // can capture the next feature request from the user.
2554
- if (state?.step === 13) {
3038
+ if (state?.step === 16) {
2555
3039
  clearEditorUserPrompt(root);
2556
3040
  }
2557
3041
  printCycleOverview(root, state);
@@ -2559,8 +3043,8 @@ const editorCommand = {
2559
3043
  return;
2560
3044
  }
2561
3045
  const step = argv.step ? parseInt(argv.step, 10) : undefined;
2562
- if (step != null && (isNaN(step) || step < 1 || step > 13)) {
2563
- console.error(chalk.red(`Error: Invalid step "${argv.step}". Must be 1-13.`));
3046
+ if (step != null && (isNaN(step) || step < 1 || step > 16)) {
3047
+ console.error(chalk.red(`Error: Invalid step "${argv.step}". Must be 1-16.`));
2564
3048
  process.exit(1);
2565
3049
  }
2566
3050
  if (step == null) {
@@ -2593,9 +3077,42 @@ const editorCommand = {
2593
3077
  return;
2594
3078
  }
2595
3079
  const { project, branch } = await requireBranchAndProject(projectSlug);
2596
- // Auto-sync scenarios from manifest if it exists
2597
- const manifestData = readManifest(projectRoot);
2598
- if (manifestData && manifestData.scenarios.length > 0) {
3080
+ // Backfill _metadata into existing scenario files that predate the embedded metadata feature.
3081
+ // This reads metadata from the DB and writes it into files that don't have _metadata yet.
3082
+ try {
3083
+ const { getDatabase } = await import('../../../packages/database/index.js');
3084
+ const db = getDatabase();
3085
+ const dbScenarios = await db
3086
+ .selectFrom('editor_scenarios')
3087
+ .select([
3088
+ 'id',
3089
+ 'name',
3090
+ 'description',
3091
+ 'component_name',
3092
+ 'component_path',
3093
+ 'url',
3094
+ 'type',
3095
+ 'screenshot_path',
3096
+ 'viewport_width',
3097
+ 'viewport_height',
3098
+ 'created_at',
3099
+ 'updated_at',
3100
+ ])
3101
+ .where('project_id', '=', project.id)
3102
+ .execute();
3103
+ if (dbScenarios.length > 0) {
3104
+ const backfilled = backfillScenarioMetadata(projectRoot, dbScenarios);
3105
+ if (backfilled > 0) {
3106
+ console.log(chalk.green(` Backfilled metadata into ${backfilled} scenario file${backfilled > 1 ? 's' : ''}`));
3107
+ }
3108
+ }
3109
+ }
3110
+ catch {
3111
+ // Non-fatal — backfill is best-effort
3112
+ }
3113
+ // Auto-sync scenario files (with _metadata) to database
3114
+ const scenarioEntries = scanScenarioFiles(projectRoot);
3115
+ if (scenarioEntries.length > 0) {
2599
3116
  try {
2600
3117
  const { getDatabase } = await import('../../../packages/database/index.js');
2601
3118
  const db = getDatabase();
@@ -2604,7 +3121,7 @@ const editorCommand = {
2604
3121
  .select(['id', 'updated_at'])
2605
3122
  .where('project_id', '=', project.id)
2606
3123
  .execute();
2607
- const syncResult = await syncManifestToDatabase(projectRoot, project.id, existingRows, async (row) => {
3124
+ const syncResult = await syncScenarioFilesToDatabase(projectRoot, project.id, existingRows, async (row) => {
2608
3125
  await db
2609
3126
  .insertInto('editor_scenarios')
2610
3127
  .values(row)
@@ -2622,13 +3139,50 @@ const editorCommand = {
2622
3139
  parts.push(`${syncResult.inserted} imported`);
2623
3140
  if (syncResult.updated > 0)
2624
3141
  parts.push(`${syncResult.updated} updated`);
2625
- console.log(chalk.green(` Synced scenarios from manifest: ${parts.join(', ')}`));
3142
+ console.log(chalk.green(` Synced scenarios from files: ${parts.join(', ')}`));
2626
3143
  }
2627
3144
  }
2628
3145
  catch {
2629
3146
  // Non-fatal — sync failure shouldn't block editor startup
2630
3147
  }
2631
3148
  }
3149
+ // Migrate legacy scenario formats: resolve null viewports, populate
3150
+ // dimensions arrays, and build screenshotPaths maps from single values.
3151
+ // If files were fixed, re-sync to update the database with corrected values.
3152
+ try {
3153
+ const migrateResult = migrateScenarioFormats(projectRoot);
3154
+ if (migrateResult.fixed > 0) {
3155
+ console.log(chalk.green(` Migrated ${migrateResult.fixed} scenario file${migrateResult.fixed > 1 ? 's' : ''} to current format`));
3156
+ // Re-sync so the DB reflects the fixed file metadata
3157
+ try {
3158
+ const { getDatabase } = await import('../../../packages/database/index.js');
3159
+ const db = getDatabase();
3160
+ const rows = await db
3161
+ .selectFrom('editor_scenarios')
3162
+ .select(['id', 'updated_at'])
3163
+ .where('project_id', '=', project.id)
3164
+ .execute();
3165
+ await syncScenarioFilesToDatabase(projectRoot, project.id, rows, async (row) => {
3166
+ await db
3167
+ .insertInto('editor_scenarios')
3168
+ .values(row)
3169
+ .execute();
3170
+ }, async (id, row) => {
3171
+ await db
3172
+ .updateTable('editor_scenarios')
3173
+ .set(row)
3174
+ .where('id', '=', id)
3175
+ .execute();
3176
+ });
3177
+ }
3178
+ catch {
3179
+ // Non-fatal — DB re-sync failure shouldn't block startup
3180
+ }
3181
+ }
3182
+ }
3183
+ catch {
3184
+ // Non-fatal — migration failure shouldn't block editor startup
3185
+ }
2632
3186
  // `codeyam editor` (no step) always implies editor mode.
2633
3187
  // The empty-folder heuristic is no longer needed here — running this
2634
3188
  // command IS the signal. We still detect empty folders so that
@@ -2667,29 +3221,23 @@ const editorCommand = {
2667
3221
  editorMode,
2668
3222
  });
2669
3223
  // Auto-finalize analyzer so codeyam analyze works
2670
- if (editorMode && !isAnalyzerFinalized()) {
2671
- try {
2672
- const { execSync } = await import('child_process');
2673
- const templatePath = getAnalyzerTemplatePath();
2674
- if (fs.existsSync(templatePath)) {
2675
- console.log(' Setting up simulations (first time only)...');
2676
- execSync('npm install --include=dev', {
2677
- cwd: templatePath,
2678
- stdio: 'pipe',
2679
- });
2680
- execSync('npx playwright install chromium', {
2681
- cwd: templatePath,
2682
- stdio: 'pipe',
2683
- });
2684
- execSync('npm run build', {
2685
- cwd: templatePath,
2686
- stdio: 'pipe',
2687
- });
2688
- fs.writeFileSync(path.join(templatePath, '.finalized'), new Date().toISOString());
3224
+ if (editorMode) {
3225
+ const stepLabels = {
3226
+ 'npm-install': 'Installing simulation dependencies...',
3227
+ 'playwright-install': 'Installing browser (Chromium)...',
3228
+ build: 'Building simulation engine...',
3229
+ };
3230
+ const simProgress = new ProgressReporter();
3231
+ const finalization = ensureAnalyzerFinalized({
3232
+ onProgress: (step) => simProgress.start(stepLabels[step]),
3233
+ });
3234
+ if (finalization.errors.length > 0) {
3235
+ for (const err of finalization.errors) {
3236
+ simProgress.warn(`${stepLabels[err.step].replace('...', '')} failed: ${err.message}`);
2689
3237
  }
2690
3238
  }
2691
- catch {
2692
- // Non-fatal
3239
+ else if (finalization.needed) {
3240
+ simProgress.succeed('Simulation engine ready');
2693
3241
  }
2694
3242
  }
2695
3243
  // Start background server (handles killing existing servers internally)
@@ -2700,6 +3248,19 @@ const editorCommand = {
2700
3248
  project,
2701
3249
  branch,
2702
3250
  });
3251
+ // Build import graph on first startup if glossary exists but no entities yet
3252
+ const glossaryPath = path.join(projectRoot, '.codeyam', 'glossary.json');
3253
+ if (fs.existsSync(glossaryPath)) {
3254
+ const entities = await loadEntities({});
3255
+ if (!entities || entities.length === 0) {
3256
+ try {
3257
+ await handleAnalyzeImports({ silent: true });
3258
+ }
3259
+ catch {
3260
+ // Non-fatal
3261
+ }
3262
+ }
3263
+ }
2703
3264
  console.log();
2704
3265
  console.log(` Dashboard: ${url}`);
2705
3266
  console.log(' Run "codeyam --help" for all commands');
@@ -2727,6 +3288,18 @@ const editorCommand = {
2727
3288
  return;
2728
3289
  }
2729
3290
  const state = readState(root);
3291
+ // Validate step transition — prevent skipping steps.
3292
+ // Exception: step 2 with --feature is always allowed because step 1's
3293
+ // instructions explicitly tell Claude to run `codeyam editor 2 --feature "..."`.
3294
+ // Step 1 is planning-only and may not persist state (no --feature flag).
3295
+ const skipValidation = step === 2 && argv.feature;
3296
+ if (!skipValidation) {
3297
+ const stepError = validateStepTransition(step, state?.step ?? null);
3298
+ if (stepError) {
3299
+ console.error(chalk.red(`Error: ${stepError}`));
3300
+ process.exit(1);
3301
+ }
3302
+ }
2730
3303
  switch (step) {
2731
3304
  case 1: {
2732
3305
  const feature = argv.feature || undefined;
@@ -2758,12 +3331,24 @@ const editorCommand = {
2758
3331
  case 10:
2759
3332
  case 11:
2760
3333
  case 12:
2761
- case 13: {
3334
+ case 13:
3335
+ case 14:
3336
+ case 15:
3337
+ case 16: {
2762
3338
  const feature = argv.feature || state?.feature;
2763
3339
  if (!feature) {
2764
3340
  console.error(chalk.red('Error: No feature in progress. Run codeyam editor 1 first.'));
2765
3341
  process.exit(1);
2766
3342
  }
3343
+ // Hard gate: steps 8+ require audit to have passed
3344
+ if (step >= 8) {
3345
+ const auditOk = await checkAuditGate();
3346
+ if (!auditOk) {
3347
+ console.error(chalk.red.bold('BLOCKED: The audit has not passed. Run `codeyam editor audit` and fix all issues before proceeding.'));
3348
+ console.error(chalk.yellow('Every function and hook must have a test file. Every component must have scenarios.'));
3349
+ process.exit(1);
3350
+ }
3351
+ }
2767
3352
  const stepFns = {
2768
3353
  3: printStep3,
2769
3354
  4: printStep4,
@@ -2776,6 +3361,9 @@ const editorCommand = {
2776
3361
  11: printStep11,
2777
3362
  12: printStep12,
2778
3363
  13: printStep13,
3364
+ 14: printStep14,
3365
+ 15: printStep15,
3366
+ 16: printStep16,
2779
3367
  };
2780
3368
  stepFns[step](root, feature);
2781
3369
  break;