@codeyam/codeyam-cli 0.1.17 → 0.1.19

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 (54) hide show
  1. package/analyzer-template/.build-info.json +6 -6
  2. package/analyzer-template/log.txt +3 -3
  3. package/codeyam-cli/src/cli.js +15 -0
  4. package/codeyam-cli/src/cli.js.map +1 -1
  5. package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js +9 -9
  6. package/codeyam-cli/src/commands/editor.js +455 -257
  7. package/codeyam-cli/src/commands/editor.js.map +1 -1
  8. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +160 -0
  9. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
  10. package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js +66 -0
  11. package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js.map +1 -1
  12. package/codeyam-cli/src/utils/__tests__/editorGuardMiddleware.test.js +67 -0
  13. package/codeyam-cli/src/utils/__tests__/editorGuardMiddleware.test.js.map +1 -0
  14. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js +213 -0
  15. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js.map +1 -1
  16. package/codeyam-cli/src/utils/__tests__/journalCaptureStabilization.test.js +16 -1
  17. package/codeyam-cli/src/utils/__tests__/journalCaptureStabilization.test.js.map +1 -1
  18. package/codeyam-cli/src/utils/editorAudit.js +44 -14
  19. package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
  20. package/codeyam-cli/src/utils/editorGuard.js +36 -0
  21. package/codeyam-cli/src/utils/editorGuard.js.map +1 -0
  22. package/codeyam-cli/src/utils/editorRecapture.js +109 -0
  23. package/codeyam-cli/src/utils/editorRecapture.js.map +1 -0
  24. package/codeyam-cli/src/utils/editorScenarios.js +1 -1
  25. package/codeyam-cli/src/utils/entityChangeStatus.js +30 -2
  26. package/codeyam-cli/src/utils/entityChangeStatus.js.map +1 -1
  27. package/codeyam-cli/src/utils/scenariosManifest.js +22 -0
  28. package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -1
  29. package/codeyam-cli/src/utils/simulationGateMiddleware.js +9 -0
  30. package/codeyam-cli/src/utils/simulationGateMiddleware.js.map +1 -1
  31. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js +17 -6
  32. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js.map +1 -1
  33. package/codeyam-cli/src/webserver/__tests__/stripClaudeCommand.test.js +79 -0
  34. package/codeyam-cli/src/webserver/__tests__/stripClaudeCommand.test.js.map +1 -0
  35. package/codeyam-cli/src/webserver/build/client/assets/api.editor-recapture-stale-l0sNRNKZ.js +1 -0
  36. package/codeyam-cli/src/webserver/build/client/assets/{editor.entity.(_sha)-Bnx7yUP0.js → editor.entity.(_sha)-CGzKlIHg.js} +13 -13
  37. package/codeyam-cli/src/webserver/build/client/assets/globals-Yn9W3zp3.css +1 -0
  38. package/codeyam-cli/src/webserver/build/client/assets/manifest-2ef99f38.js +1 -0
  39. package/codeyam-cli/src/webserver/build/client/assets/{root-DB3O9_9j.js → root-BxUQigda.js} +5 -5
  40. package/codeyam-cli/src/webserver/build/server/assets/{analysisRunner-BMmkgAkg.js → analysisRunner-BPmOG9bE.js} +1 -1
  41. package/codeyam-cli/src/webserver/build/server/assets/{index-DxB0pOSt.js → index-Cd-ufawF.js} +1 -1
  42. package/codeyam-cli/src/webserver/build/server/assets/{init-DLYLaqqP.js → init-CzeBGOto.js} +1 -1
  43. package/codeyam-cli/src/webserver/build/server/assets/{server-build-CcyitQLQ.js → server-build-Dht7CKXY.js} +114 -113
  44. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  45. package/codeyam-cli/src/webserver/build-info.json +5 -5
  46. package/codeyam-cli/src/webserver/terminalServer.js +85 -19
  47. package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
  48. package/codeyam-cli/templates/__tests__/editor-step-hook.prompt-capture.test.ts +118 -0
  49. package/codeyam-cli/templates/codeyam-editor-claude.md +2 -0
  50. package/codeyam-cli/templates/codeyam-editor-reference.md +1 -1
  51. package/codeyam-cli/templates/editor-step-hook.py +72 -46
  52. package/package.json +1 -1
  53. package/codeyam-cli/src/webserver/build/client/assets/globals-fAqOD9ex.css +0 -1
  54. package/codeyam-cli/src/webserver/build/client/assets/manifest-3157d6b8.js +0 -1
@@ -16,7 +16,7 @@ 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
18
  import { scanScenarioFiles, syncScenarioFilesToDatabase, backfillScenarioMetadata, migrateScenarioFormats, } from "../utils/scenariosManifest.js";
19
- import { clearEditorState, clearEditorUserPrompt, validateStepTransition, backfillEntityShaOnScenarios, } from "../utils/editorScenarios.js";
19
+ import { clearEditorState, validateStepTransition, backfillEntityShaOnScenarios, } from "../utils/editorScenarios.js";
20
20
  import { validateSeedData, detectSeedAdapter, validateSeedKeysAgainstPrisma, } from "../utils/editorSeedAdapter.js";
21
21
  import { deleteScenarioViaCli } from "../utils/editorDeleteScenario.js";
22
22
  import { buildEditorApiRequest, callEditorApi, EDITOR_API_SUBCOMMANDS, } from "../utils/editorApi.js";
@@ -27,21 +27,23 @@ const __filename = fileURLToPath(import.meta.url);
27
27
  const __dirname = path.dirname(__filename);
28
28
  const STEP_LABELS = {
29
29
  1: 'Plan',
30
- 2: 'Prototype',
31
- 3: 'Confirm',
32
- 4: 'Deconstruct',
33
- 5: 'Extract',
34
- 6: 'Glossary',
35
- 7: 'Analyze',
36
- 8: 'App Scenarios',
37
- 9: 'User Scenarios',
38
- 10: 'Verify',
39
- 11: 'Journal',
40
- 12: 'Review',
41
- 13: 'Present',
42
- 14: 'Commit',
43
- 15: 'Finalize',
44
- 16: 'Push',
30
+ 2: 'Prepare',
31
+ 3: 'Prototype',
32
+ 4: 'Verify Prototype',
33
+ 5: 'Confirm',
34
+ 6: 'Deconstruct',
35
+ 7: 'Extract',
36
+ 8: 'Glossary',
37
+ 9: 'Analyze',
38
+ 10: 'App Scenarios',
39
+ 11: 'User Scenarios',
40
+ 12: 'Verify',
41
+ 13: 'Journal',
42
+ 14: 'Review',
43
+ 15: 'Present',
44
+ 16: 'Commit',
45
+ 17: 'Finalize',
46
+ 18: 'Push',
45
47
  };
46
48
  const MIGRATION_STEP_LABELS = {
47
49
  1: 'Survey',
@@ -122,8 +124,9 @@ function writeState(root, state) {
122
124
  }
123
125
  /**
124
126
  * Clear the editor state (for starting a new feature).
125
- * Does NOT clear the user prompt file — that's handled separately
126
- * via clearEditorUserPrompt when the previous feature commits.
127
+ * Does NOT clear the user prompt file — the prompt is cleared
128
+ * by the journal API after recording, and the hook captures
129
+ * a fresh prompt for the next feature on UserPromptSubmit.
127
130
  */
128
131
  function clearState(root) {
129
132
  clearEditorState(root);
@@ -208,8 +211,9 @@ function printAppScenarioInstructions(pageName, route) {
208
211
  const root = process.cwd();
209
212
  const hasSeedAdapter = !!detectSeedAdapter(root);
210
213
  checkbox('Check existing scenarios: `codeyam editor scenarios`');
211
- console.log(chalk.dim(' Review existing scenarios — reuse and update their data rather than'));
212
- console.log(chalk.dim(' creating duplicates. Re-register with the same name to update a scenario.'));
214
+ console.log(chalk.dim(' Review existing scenarios — enhance their seed data to exercise new features.'));
215
+ console.log(chalk.dim(' A rich scenario that exercises 5 features is better than 5 thin scenarios with one feature each.'));
216
+ console.log(chalk.dim(' Rename scenarios if their data scope has grown beyond the original name.'));
213
217
  console.log();
214
218
  console.log(chalk.bold.yellow('App scenarios vs component scenarios — these are DIFFERENT:'));
215
219
  console.log(chalk.yellow(' App scenarios: show the FULL PAGE with seeded data at a real route (/, /library, etc.)'));
@@ -305,7 +309,7 @@ function printExtractionPlanInstructions() {
305
309
  console.log(chalk.yellow(' — Where it currently lives (source file + approximate lines)'));
306
310
  console.log(chalk.yellow(' — Where it will go (new file path)'));
307
311
  console.log();
308
- console.log(chalk.dim('Present the numbered plan, then proceed to step 5 to execute it.'));
312
+ console.log(chalk.dim('Present the numbered plan, then proceed to step 7 to execute it.'));
309
313
  }
310
314
  /**
311
315
  * Shared component capture instructions used by editor Step 7 and migration Steps 3/8.
@@ -362,13 +366,13 @@ function stepHeader(step, title, feature) {
362
366
  console.log();
363
367
  }
364
368
  /**
365
- * Print a colored progress tracker showing all 16 steps.
369
+ * Print a colored progress tracker showing all 18 steps.
366
370
  * Steps before `current` are green ✓, `current` is bold cyan →, future steps are dim ○.
367
371
  */
368
372
  function printProgressTracker(current) {
369
373
  console.log();
370
374
  console.log(chalk.dim(' ┌─────────────────────────────────────┐'));
371
- for (let i = 1; i <= 16; i++) {
375
+ for (let i = 1; i <= 18; i++) {
372
376
  const label = STEP_LABELS[i];
373
377
  const num = i < 10 ? ` ${i}` : `${i}`;
374
378
  const content = `${num}. ${label.padEnd(28)}`;
@@ -406,13 +410,13 @@ function stopGate(current, opts) {
406
410
  console.log(chalk.yellow('For the CURRENT step (→), show each checklist item with ✓ (done) or ✗ (skipped + reason).'));
407
411
  console.log(chalk.yellow('If any items are ✗, explain why and ask if the user wants to address them.'));
408
412
  console.log();
409
- if (current < 16) {
413
+ if (current < 18) {
410
414
  console.log(chalk.green('When done, run: ') +
411
415
  chalk.bold(`codeyam editor ${current + 1}`));
412
416
  }
413
417
  else {
414
418
  console.log(chalk.green('Feature complete! Run: ') +
415
- chalk.bold('codeyam editor 1') +
419
+ chalk.bold('codeyam editor steps') +
416
420
  chalk.green(' to start the next feature'));
417
421
  }
418
422
  console.log();
@@ -428,34 +432,36 @@ function printResumptionHeader(step) {
428
432
  'Check if plan was already written to context file:\n `cat .codeyam/editor-mode-context.md`',
429
433
  ],
430
434
  2: ['Check if project files already exist:\n `ls package.json src/`'],
431
- 3: ['This is a confirmation step — just re-present to user'],
432
- 4: [
435
+ 3: ['Re-check is safe — just re-run the step'],
436
+ 4: ['Re-verify is safe to repeat — just re-run the checks'],
437
+ 5: ['This is a confirmation step — just re-present to user'],
438
+ 6: [
433
439
  'Check if extraction plan already exists in context file:\n `cat .codeyam/editor-mode-context.md`',
434
440
  ],
435
- 5: [
441
+ 7: [
436
442
  'Check if components/functions already extracted:\n `ls src/components/ src/lib/`',
437
443
  ],
438
- 6: [
444
+ 8: [
439
445
  'Check if glossary already populated:\n `cat .codeyam/glossary.json`',
440
446
  ],
441
- 7: ['Check if isolation routes and registered scenarios already exist'],
442
- 8: ['Check existing scenarios:\n `codeyam editor scenarios`'],
443
- 9: [
447
+ 9: ['Check if isolation routes and registered scenarios already exist'],
448
+ 10: ['Check existing scenarios:\n `codeyam editor scenarios`'],
449
+ 11: [
444
450
  'Check existing user-persona scenarios:\n `codeyam editor scenarios`',
445
451
  ],
446
- 10: ['Re-verify is safe to repeat — just re-run the checks'],
447
- 11: [
452
+ 12: ['Re-verify is safe to repeat — just re-run the checks'],
453
+ 13: [
448
454
  'Check if a journal entry already exists for this feature:\n `codeyam editor journal-list`',
449
455
  'If an entry exists, use PATCH to update it — do NOT create a new one',
450
456
  ],
451
- 12: ['Re-verify is safe to repeat — just re-run the checks'],
452
- 13: ['Check if commit already made:\n `git log --oneline -3`'],
453
- 14: ['Check if commit already made:\n `git log --oneline -3`'],
454
- 15: [
457
+ 14: ['Re-verify is safe to repeat — just re-run the checks'],
458
+ 15: ['Check if commit already made:\n `git log --oneline -3`'],
459
+ 16: ['Check if commit already made:\n `git log --oneline -3`'],
460
+ 17: [
455
461
  'Check if journal was already updated with commit SHA:\n `codeyam editor journal-list`',
456
462
  'Check if commit was already amended:\n `git log --oneline -3`',
457
463
  ],
458
- 16: [
464
+ 18: [
459
465
  'Check if already pushed:\n `git remote -v && git log --oneline origin/HEAD..HEAD 2>/dev/null`',
460
466
  ],
461
467
  };
@@ -562,7 +568,7 @@ function parseDebugTargets(target) {
562
568
  }
563
569
  if (entry.startsWith('step-')) {
564
570
  const num = parseInt(entry.replace('step-', ''), 10);
565
- if (!isNaN(num) && num >= 1 && num <= 16) {
571
+ if (!isNaN(num) && num >= 1 && num <= 18) {
566
572
  valid.add(`step-${num}`);
567
573
  continue;
568
574
  }
@@ -788,24 +794,26 @@ function printCycleOverview(root, state) {
788
794
  console.log(chalk.yellow('This applies even after committing — always use the change workflow.'));
789
795
  }
790
796
  else {
791
- console.log('Each feature follows 16 steps. You MUST run each command in order:');
797
+ console.log('Each feature follows 18 steps. You MUST run each command in order:');
792
798
  console.log();
793
- console.log(` ${chalk.bold.yellow(' 1')} ${chalk.bold('Plan')} — Plan the feature, confirm with user`);
794
- console.log(` ${chalk.bold.yellow(' 2')} ${chalk.bold('Prototype')} Build a working prototype fast`);
795
- console.log(` ${chalk.bold.yellow(' 3')} ${chalk.bold('Confirm')} Confirm prototype with user`);
796
- console.log(` ${chalk.bold.yellow(' 4')} ${chalk.bold('Deconstruct')} Read code, plan all extractions`);
797
- console.log(` ${chalk.bold.yellow(' 5')} ${chalk.bold('Extract')} TDD extraction of functions + components`);
798
- console.log(` ${chalk.bold.yellow(' 6')} ${chalk.bold('Glossary')} — Record functions in glossary`);
799
- console.log(` ${chalk.bold.yellow(' 7')} ${chalk.bold('Analyze')} Analyze and verify components`);
800
- console.log(` ${chalk.bold.yellow(' 8')} ${chalk.bold('App Scenarios')} Create app-level scenarios`);
801
- console.log(` ${chalk.bold.yellow(' 9')} ${chalk.bold('User Scenarios')} Create user-persona scenarios`);
802
- console.log(` ${chalk.bold.yellow('10')} ${chalk.bold('Verify')} Review screenshots, check for errors`);
803
- console.log(` ${chalk.bold.yellow('11')} ${chalk.bold('Journal')} — Create/update journal entry`);
804
- console.log(` ${chalk.bold.yellow('12')} ${chalk.bold('Review')} Verify screenshots and audit`);
805
- console.log(` ${chalk.bold.yellow('13')} ${chalk.bold('Present')} Present summary, get approval`);
806
- console.log(` ${chalk.bold.yellow('14')} ${chalk.bold('Commit')} Commit all changes`);
807
- console.log(` ${chalk.bold.yellow('15')} ${chalk.bold('Finalize')} Journal update + amend commit`);
808
- console.log(` ${chalk.bold.yellow('16')} ${chalk.bold('Push')} Push to remote`);
799
+ console.log(` ${chalk.bold.yellow(' 1')} ${chalk.bold('Plan')} — Plan the feature, confirm with user`);
800
+ console.log(` ${chalk.bold.yellow(' 2')} ${chalk.bold('Prepare')} Set up project and dependencies`);
801
+ console.log(` ${chalk.bold.yellow(' 3')} ${chalk.bold('Prototype')} Build a working prototype fast`);
802
+ console.log(` ${chalk.bold.yellow(' 4')} ${chalk.bold('Verify Prototype')} Verify prototype works correctly`);
803
+ console.log(` ${chalk.bold.yellow(' 5')} ${chalk.bold('Confirm')} Confirm prototype with user`);
804
+ console.log(` ${chalk.bold.yellow(' 6')} ${chalk.bold('Deconstruct')} — Read code, plan all extractions`);
805
+ console.log(` ${chalk.bold.yellow(' 7')} ${chalk.bold('Extract')} TDD extraction of functions + components`);
806
+ console.log(` ${chalk.bold.yellow(' 8')} ${chalk.bold('Glossary')} Record functions in glossary`);
807
+ console.log(` ${chalk.bold.yellow(' 9')} ${chalk.bold('Analyze')} Analyze and verify components`);
808
+ console.log(` ${chalk.bold.yellow('10')} ${chalk.bold('App Scenarios')} Create app-level scenarios`);
809
+ console.log(` ${chalk.bold.yellow('11')} ${chalk.bold('User Scenarios')} — Create user-persona scenarios`);
810
+ console.log(` ${chalk.bold.yellow('12')} ${chalk.bold('Verify')} Review screenshots, check for errors`);
811
+ console.log(` ${chalk.bold.yellow('13')} ${chalk.bold('Journal')} Create/update journal entry`);
812
+ console.log(` ${chalk.bold.yellow('14')} ${chalk.bold('Review')} Verify screenshots and audit`);
813
+ console.log(` ${chalk.bold.yellow('15')} ${chalk.bold('Present')} Present summary, get approval`);
814
+ console.log(` ${chalk.bold.yellow('16')} ${chalk.bold('Commit')} Commit all changes`);
815
+ console.log(` ${chalk.bold.yellow('17')} ${chalk.bold('Finalize')} — Journal update + amend commit`);
816
+ console.log(` ${chalk.bold.yellow('18')} ${chalk.bold('Push')} — Push to remote`);
809
817
  console.log();
810
818
  console.log(chalk.green('Start now: ') + chalk.bold('codeyam editor 1'));
811
819
  console.log(chalk.dim(' If the user already described what they want, pass it: codeyam editor 1 --prompt "their message"'));
@@ -897,7 +905,7 @@ function printStep1(root, feature, options, userPrompt) {
897
905
  console.log(chalk.dim(' Replace "Feature Name" with a short title for what you just described.'));
898
906
  console.log();
899
907
  }
900
- // ─── Step 2: Prototype ────────────────────────────────────────────────
908
+ // ─── Step 2: Prepare ──────────────────────────────────────────────────
901
909
  function printStep2(root, feature) {
902
910
  const port = getServerPort();
903
911
  const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
@@ -914,12 +922,19 @@ function printStep2(root, feature) {
914
922
  appFormats: prevState?.appFormats,
915
923
  techStackId: prevState?.techStackId,
916
924
  });
917
- logEvent(root, 'step', { step: 2, label: 'Prototype', feature });
918
- stepHeader(2, 'Prototype', feature);
925
+ logEvent(root, 'step', { step: 2, label: 'Prepare', feature });
926
+ stepHeader(2, 'Prepare', feature);
919
927
  if (isResuming) {
920
928
  printResumptionHeader(2);
921
929
  }
922
- console.log('Build fast with real data. Prioritize speed over quality.');
930
+ console.log(chalk.bold.red('━━━ MANDATORY: CREATE TASK NOW ━━━'));
931
+ console.log();
932
+ console.log(chalk.red(`Run TaskCreate with title: "Use \`codeyam editor\` to work on ${feature}"`));
933
+ console.log(chalk.red('DO NOT read files, scaffold, write code, or do ANYTHING else until this task exists.'));
934
+ console.log(chalk.red('This is not optional. Create the task, then continue below.'));
935
+ console.log(chalk.bold.red('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
936
+ console.log();
937
+ console.log('Get the project ready to build.');
923
938
  console.log();
924
939
  // If no project exists yet, include scaffolding instructions first
925
940
  if (!projectExists) {
@@ -948,7 +963,6 @@ function printStep2(root, feature) {
948
963
  console.log(chalk.dim(' Key: import { prisma } from "@/app/lib/prisma" in API routes.'));
949
964
  console.log(chalk.dim(' Key: Seed scripts must use the adapter pattern (see prisma/seed.ts).'));
950
965
  console.log();
951
- console.log(chalk.bold('Build the feature:'));
952
966
  }
953
967
  else {
954
968
  console.log(chalk.bold('Prepare the database for this feature:'));
@@ -964,6 +978,32 @@ function printStep2(root, feature) {
964
978
  console.log(chalk.dim(' If no existing scenario fits, use the default seed: npm run db:seed'));
965
979
  console.log();
966
980
  }
981
+ stopGate(2);
982
+ }
983
+ // ─── Step 3: Prototype ────────────────────────────────────────────────
984
+ function printStep3(root, feature) {
985
+ const port = getServerPort();
986
+ const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
987
+ const projectExists = hasProject(root);
988
+ const prevState = readState(root);
989
+ const isResuming = prevState?.step === 3;
990
+ const now = new Date().toISOString();
991
+ writeState(root, {
992
+ feature,
993
+ step: 3,
994
+ label: STEP_LABELS[3],
995
+ startedAt: isResuming ? prevState.startedAt : now,
996
+ featureStartedAt: prevState?.featureStartedAt || now,
997
+ appFormats: prevState?.appFormats,
998
+ techStackId: prevState?.techStackId,
999
+ });
1000
+ logEvent(root, 'step', { step: 3, label: 'Prototype', feature });
1001
+ stepHeader(3, 'Prototype', feature);
1002
+ if (isResuming) {
1003
+ printResumptionHeader(3);
1004
+ }
1005
+ console.log('Build fast with real data. Prioritize speed over quality.');
1006
+ console.log();
967
1007
  console.log(chalk.bold('Checklist:'));
968
1008
  checkbox('Create API routes that read from the database via Prisma');
969
1009
  if (!projectExists) {
@@ -1012,6 +1052,28 @@ function printStep2(root, feature) {
1012
1052
  console.log(chalk.cyan(` codeyam editor preview '{"path":"/drinks/1","dimension":"${dim}"}'`));
1013
1053
  printDimensionGuidance(dim, dimNames);
1014
1054
  console.log();
1055
+ stopGate(3);
1056
+ }
1057
+ // ─── Step 4: Verify Prototype ─────────────────────────────────────────
1058
+ function printStep4(root, feature) {
1059
+ const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
1060
+ const prevState = readState(root);
1061
+ const isResuming = prevState?.step === 4;
1062
+ const now = new Date().toISOString();
1063
+ writeState(root, {
1064
+ feature,
1065
+ step: 4,
1066
+ label: STEP_LABELS[4],
1067
+ startedAt: isResuming ? prevState.startedAt : now,
1068
+ featureStartedAt: prevState?.featureStartedAt || now,
1069
+ });
1070
+ logEvent(root, 'step', { step: 4, label: 'Verify Prototype', feature });
1071
+ stepHeader(4, 'Verify Prototype', feature);
1072
+ if (isResuming) {
1073
+ printResumptionHeader(4);
1074
+ }
1075
+ console.log('Verify everything works before presenting the prototype.');
1076
+ console.log();
1015
1077
  console.log(chalk.bold('Verify the dev server:'));
1016
1078
  console.log(chalk.dim(` # Get dev server URL: codeyam editor dev-server`));
1017
1079
  console.log(chalk.dim(' # Check page loads: curl -s -o /dev/null -w "%{http_code}" http://localhost:<dev-port>'));
@@ -1038,26 +1100,26 @@ function printStep2(root, feature) {
1038
1100
  console.log(chalk.dim(' A new clone should work with just: git clone → npm run setup → npm run dev'));
1039
1101
  console.log();
1040
1102
  console.log(chalk.dim('Focus on building the prototype. Scenarios and refactoring happen in later steps.'));
1041
- stopGate(2);
1103
+ stopGate(4);
1042
1104
  }
1043
- // ─── Step 3: Confirm ──────────────────────────────────────────────────
1044
- function printStep3(root, feature) {
1105
+ // ─── Step 5: Confirm ──────────────────────────────────────────────────
1106
+ function printStep5(root, feature) {
1045
1107
  const port = getServerPort();
1046
1108
  const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
1047
1109
  const prevState = readState(root);
1048
- const isResuming = prevState?.step === 3;
1110
+ const isResuming = prevState?.step === 5;
1049
1111
  const now = new Date().toISOString();
1050
1112
  writeState(root, {
1051
1113
  feature,
1052
- step: 3,
1053
- label: STEP_LABELS[3],
1114
+ step: 5,
1115
+ label: STEP_LABELS[5],
1054
1116
  startedAt: isResuming ? prevState.startedAt : now,
1055
1117
  featureStartedAt: prevState?.featureStartedAt || now,
1056
1118
  });
1057
- logEvent(root, 'step', { step: 3, label: 'Confirm', feature });
1058
- stepHeader(3, 'Confirm', feature);
1119
+ logEvent(root, 'step', { step: 5, label: 'Confirm', feature });
1120
+ stepHeader(5, 'Confirm', feature);
1059
1121
  if (isResuming) {
1060
- printResumptionHeader(3);
1122
+ printResumptionHeader(5);
1061
1123
  }
1062
1124
  console.log('Summarize what was built and get user confirmation.');
1063
1125
  console.log();
@@ -1074,6 +1136,11 @@ function printStep3(root, feature) {
1074
1136
  console.log(chalk.dim(' If there are errors, fix the underlying issue before presenting.'));
1075
1137
  checkbox('Verify `hasContent=true` and `liveErrors=0` — do NOT ask the user to confirm if the preview is broken');
1076
1138
  console.log();
1139
+ console.log(chalk.bold('Verify the captured user prompt:'));
1140
+ checkbox("Read `.codeyam/editor-user-prompt.txt` — this is the user's original feature request");
1141
+ checkbox('If the file is missing or does not match what the user actually asked for, write the correct prompt text to `.codeyam/editor-user-prompt.txt`');
1142
+ console.log(chalk.dim(" This must be the user's exact words, not a summary. It gets recorded in the journal."));
1143
+ console.log();
1077
1144
  console.log(chalk.bold('Then present to the user:'));
1078
1145
  checkbox('Summarize what was built (routes, components, data)');
1079
1146
  checkbox(`Navigate the preview to the feature's primary page: \`codeyam editor preview '{"path":"/your-page","dimension":"${dim}"}'\``);
@@ -1087,62 +1154,62 @@ function printStep3(root, feature) {
1087
1154
  checkbox('Ask the user: "Does everything work as expected?"');
1088
1155
  console.log();
1089
1156
  console.log(chalk.bold('Present a selection menu to the user (use AskUserQuestion with these EXACT option labels):'));
1090
- console.log(chalk.green(' Option 1 label: "The live preview is displaying what I expected"') + chalk.dim(' — proceed to step 4'));
1157
+ console.log(chalk.green(' Option 1 label: "The live preview is displaying what I expected"') + chalk.dim(' — proceed to step 6'));
1091
1158
  console.log(chalk.yellow(' Option 2 label: "I\'d like some changes"') +
1092
- chalk.dim(' — make changes, refresh preview, re-run `codeyam editor 3`'));
1159
+ chalk.dim(' — make changes, refresh preview, re-run `codeyam editor 5`'));
1093
1160
  console.log();
1094
1161
  console.log(chalk.dim('Wait for user approval before moving on. Refactoring and scenarios happen in later steps.'));
1095
- stopGate(3, { confirm: true });
1162
+ stopGate(5, { confirm: true });
1096
1163
  }
1097
- // ─── Step 4: Deconstruct ──────────────────────────────────────────────
1098
- function printStep4(root, feature) {
1164
+ // ─── Step 6: Deconstruct ──────────────────────────────────────────────
1165
+ function printStep6(root, feature) {
1099
1166
  const prevState = readState(root);
1100
- const isResuming = prevState?.step === 4;
1167
+ const isResuming = prevState?.step === 6;
1101
1168
  const now = new Date().toISOString();
1102
1169
  writeState(root, {
1103
1170
  feature,
1104
- step: 4,
1105
- label: STEP_LABELS[4],
1171
+ step: 6,
1172
+ label: STEP_LABELS[6],
1106
1173
  startedAt: isResuming ? prevState.startedAt : now,
1107
1174
  featureStartedAt: prevState?.featureStartedAt || now,
1108
1175
  });
1109
- logEvent(root, 'step', { step: 4, label: 'Deconstruct', feature });
1110
- stepHeader(4, 'Deconstruct', feature);
1176
+ logEvent(root, 'step', { step: 6, label: 'Deconstruct', feature });
1177
+ stepHeader(6, 'Deconstruct', feature);
1111
1178
  if (isResuming) {
1112
- printResumptionHeader(4);
1179
+ printResumptionHeader(6);
1113
1180
  }
1114
1181
  console.log(chalk.bold('Goal: pages contain ONLY components. Components contain ONLY sub-components.'));
1115
- console.log(chalk.yellow('This step is read and plan only. Code extraction happens in step 5.'));
1182
+ console.log(chalk.yellow('This step is read and plan only. Code extraction happens in step 7.'));
1116
1183
  console.log();
1117
1184
  printExtractionPlanInstructions();
1118
- stopGate(4);
1185
+ stopGate(6);
1119
1186
  }
1120
- // ─── Step 5: Extract ──────────────────────────────────────────────────
1121
- function printStep5(root, feature) {
1187
+ // ─── Step 7: Extract ──────────────────────────────────────────────────
1188
+ function printStep7(root, feature) {
1122
1189
  const port = getServerPort();
1123
1190
  const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
1124
1191
  const prevState = readState(root);
1125
- const isResuming = prevState?.step === 5;
1192
+ const isResuming = prevState?.step === 7;
1126
1193
  const now = new Date().toISOString();
1127
1194
  writeState(root, {
1128
1195
  feature,
1129
- step: 5,
1130
- label: STEP_LABELS[5],
1196
+ step: 7,
1197
+ label: STEP_LABELS[7],
1131
1198
  startedAt: isResuming ? prevState.startedAt : now,
1132
1199
  featureStartedAt: prevState?.featureStartedAt || now,
1133
1200
  });
1134
- logEvent(root, 'step', { step: 5, label: 'Extract', feature });
1135
- stepHeader(5, 'Extract', feature);
1201
+ logEvent(root, 'step', { step: 7, label: 'Extract', feature });
1202
+ stepHeader(7, 'Extract', feature);
1136
1203
  if (isResuming) {
1137
- printResumptionHeader(5);
1204
+ printResumptionHeader(7);
1138
1205
  }
1139
- console.log('Execute your extraction plan from step 4.');
1206
+ console.log('Execute your extraction plan from step 6.');
1140
1207
  console.log();
1141
1208
  console.log(chalk.bold('Components:'));
1142
1209
  checkbox('Extract each component from your plan into its own file');
1143
1210
  checkbox('Page/route files must contain ZERO direct JSX — only imported components');
1144
1211
  checkbox('Every component that renders multiple sections must be split into sub-components');
1145
- console.log(chalk.dim(' No tests needed — visual verification happens in step 7'));
1212
+ console.log(chalk.dim(' No tests needed — visual verification happens in step 9'));
1146
1213
  console.log();
1147
1214
  console.log(chalk.bold('Library functions AND hooks (TDD):'));
1148
1215
  checkbox('For each function/hook: write MULTIPLE failing tests FIRST, then extract to make them pass');
@@ -1150,7 +1217,7 @@ function printStep5(root, feature) {
1150
1217
  console.log(chalk.dim(' Aim for 3-8 test cases per function depending on complexity'));
1151
1218
  console.log(chalk.dim(' Hooks count as functions — useDrinks, useAuth, etc. all need test files'));
1152
1219
  checkbox('Place test files next to source: `app/lib/drinks.ts` → `app/lib/drinks.test.ts`');
1153
- console.log(chalk.yellow(' Tests ARE the only coverage for library functions/hooks — step 7 only captures component screenshots.'));
1220
+ console.log(chalk.yellow(' Tests ARE the only coverage for library functions/hooks — step 9 only captures component screenshots.'));
1154
1221
  console.log();
1155
1222
  console.log(chalk.bold('Recursive pass:'));
1156
1223
  checkbox('Re-read EVERY new file you just created — extract components from components, functions from functions');
@@ -1168,55 +1235,55 @@ function printStep5(root, feature) {
1168
1235
  console.log(chalk.dim('Reuse glossary functions when they fit naturally. Extract a new function when the use case diverges.'));
1169
1236
  console.log();
1170
1237
  console.log(chalk.dim('Focus on TDD for functions and extraction for components. Scenarios come in later steps.'));
1171
- stopGate(5);
1238
+ stopGate(7);
1172
1239
  }
1173
- // ─── Step 6: Glossary ─────────────────────────────────────────────────
1174
- function printStep6(root, feature) {
1240
+ // ─── Step 8: Glossary ─────────────────────────────────────────────────
1241
+ function printStep8(root, feature) {
1175
1242
  const prevState = readState(root);
1176
- const isResuming = prevState?.step === 6;
1243
+ const isResuming = prevState?.step === 8;
1177
1244
  const now = new Date().toISOString();
1178
1245
  writeState(root, {
1179
1246
  feature,
1180
- step: 6,
1181
- label: STEP_LABELS[6],
1247
+ step: 8,
1248
+ label: STEP_LABELS[8],
1182
1249
  startedAt: isResuming ? prevState.startedAt : now,
1183
1250
  featureStartedAt: prevState?.featureStartedAt || now,
1184
1251
  });
1185
- logEvent(root, 'step', { step: 6, label: 'Glossary', feature });
1186
- stepHeader(6, 'Glossary', feature);
1252
+ logEvent(root, 'step', { step: 8, label: 'Glossary', feature });
1253
+ stepHeader(8, 'Glossary', feature);
1187
1254
  if (isResuming) {
1188
- printResumptionHeader(6);
1255
+ printResumptionHeader(8);
1189
1256
  }
1190
1257
  console.log('Record all new functions/components in `.codeyam/glossary.json`.');
1191
1258
  console.log();
1192
1259
  printGlossaryInstructions(feature);
1193
1260
  console.log();
1194
1261
  console.log(chalk.dim('Focus on updating the glossary. Application code and scenarios come in later steps.'));
1195
- stopGate(6);
1262
+ stopGate(8);
1196
1263
  }
1197
- // ─── Step 7: Analyze ──────────────────────────────────────────────────
1198
- function printStep7(root, feature) {
1264
+ // ─── Step 9: Analyze ──────────────────────────────────────────────────
1265
+ function printStep9(root, feature) {
1199
1266
  const port = getServerPort();
1200
1267
  const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
1201
1268
  const prevState = readState(root);
1202
- const isResuming = prevState?.step === 7;
1269
+ const isResuming = prevState?.step === 9;
1203
1270
  const now = new Date().toISOString();
1204
1271
  writeState(root, {
1205
1272
  feature,
1206
- step: 7,
1207
- label: STEP_LABELS[7],
1273
+ step: 9,
1274
+ label: STEP_LABELS[9],
1208
1275
  startedAt: isResuming ? prevState.startedAt : now,
1209
1276
  featureStartedAt: prevState?.featureStartedAt || now,
1210
1277
  });
1211
- logEvent(root, 'step', { step: 7, label: 'Analyze', feature });
1212
- stepHeader(7, 'Analyze and Verify', feature);
1278
+ logEvent(root, 'step', { step: 9, label: 'Analyze', feature });
1279
+ stepHeader(9, 'Analyze and Verify', feature);
1213
1280
  if (isResuming) {
1214
- printResumptionHeader(7);
1281
+ printResumptionHeader(9);
1215
1282
  }
1216
1283
  console.log('Verify visual components (via isolation routes) and library functions (via tests).');
1217
1284
  console.log();
1218
1285
  console.log(chalk.bold('Visual Components — Component Isolation:'));
1219
- checkbox('List all files with new/modified visual components from step 5');
1286
+ checkbox('List all files with new/modified visual components from step 7');
1220
1287
  checkbox('Check existing scenarios: `codeyam editor scenarios`');
1221
1288
  console.log(chalk.dim(' Reuse and improve existing scenarios where possible — update mock data'));
1222
1289
  console.log(chalk.dim(' to reflect current changes. Add new scenarios only for genuinely new states.'));
@@ -1224,7 +1291,7 @@ function printStep7(root, feature) {
1224
1291
  printComponentCaptureInstructions();
1225
1292
  console.log();
1226
1293
  console.log(chalk.bold('Library Functions — run tests:'));
1227
- checkbox('Run ALL test files created in step 5');
1294
+ checkbox('Run ALL test files created in step 7');
1228
1295
  console.log(chalk.dim(' Example: npx vitest run app/lib/drinks.test.ts'));
1229
1296
  checkbox('Verify every test passes');
1230
1297
  checkbox('If any test fails, fix the source code and re-run');
@@ -1232,29 +1299,29 @@ function printStep7(root, feature) {
1232
1299
  console.log(chalk.dim('Do not proceed until both component isolations and library tests pass.'));
1233
1300
  console.log();
1234
1301
  checkbox('Run `codeyam editor audit` to verify all components have scenarios and all functions/hooks have tests');
1235
- console.log(chalk.red.bold(' The audit is a HARD GATE — step 8 will refuse to run until the audit passes.'));
1302
+ console.log(chalk.red.bold(' The audit is a HARD GATE — step 10 will refuse to run until the audit passes.'));
1236
1303
  console.log(chalk.dim(' When audit passes, the import graph is built automatically for change tracking.'));
1237
1304
  console.log();
1238
- stopGate(7);
1305
+ stopGate(9);
1239
1306
  }
1240
- // ─── Step 8: App Scenarios ────────────────────────────────────────────
1241
- function printStep8(root, feature) {
1307
+ // ─── Step 10: App Scenarios ────────────────────────────────────────────
1308
+ function printStep10(root, feature) {
1242
1309
  const port = getServerPort();
1243
1310
  const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
1244
1311
  const prevState = readState(root);
1245
- const isResuming = prevState?.step === 8;
1312
+ const isResuming = prevState?.step === 10;
1246
1313
  const now = new Date().toISOString();
1247
1314
  writeState(root, {
1248
1315
  feature,
1249
- step: 8,
1250
- label: STEP_LABELS[8],
1316
+ step: 10,
1317
+ label: STEP_LABELS[10],
1251
1318
  startedAt: isResuming ? prevState.startedAt : now,
1252
1319
  featureStartedAt: prevState?.featureStartedAt || now,
1253
1320
  });
1254
- logEvent(root, 'step', { step: 8, label: 'App Scenarios', feature });
1255
- stepHeader(8, 'App Scenarios', feature);
1321
+ logEvent(root, 'step', { step: 10, label: 'App Scenarios', feature });
1322
+ stepHeader(10, 'App Scenarios', feature);
1256
1323
  if (isResuming) {
1257
- printResumptionHeader(8);
1324
+ printResumptionHeader(10);
1258
1325
  }
1259
1326
  console.log('Create app-level scenarios with rich data that robustly demonstrates this feature.');
1260
1327
  console.log();
@@ -1276,13 +1343,19 @@ function printStep8(root, feature) {
1276
1343
  console.log(chalk.cyan(" • New data states that can't coexist in one scenario (empty vs rich, error vs success) → new scenario"));
1277
1344
  console.log(chalk.cyan(" • Don't duplicate — if an existing scenario can cover a state with richer data, enhance it instead"));
1278
1345
  console.log();
1346
+ console.log(chalk.bold.cyan('Scenario naming — describe data states, not features:'));
1347
+ console.log(chalk.cyan(' • Name scenarios by what they represent: "Rich Data", "Empty", "Mobile", "Minimal"'));
1348
+ console.log(chalk.cyan(' • When enhancing a scenario with new feature data, update the name if it has grown beyond its original scope'));
1349
+ console.log(chalk.cyan(' • A single rich scenario exercising multiple features is more valuable than separate thin scenarios per feature'));
1350
+ console.log(chalk.cyan(' • Re-register with the same name to update; to rename, register with the new name'));
1351
+ console.log();
1279
1352
  checkbox('Ensure scenarios clearly demonstrate what changed in this session');
1280
1353
  console.log(chalk.dim(' If data models changed: update existing scenarios seed data to match'));
1281
1354
  console.log(chalk.dim(' If UI changed: re-register existing scenarios so screenshots reflect the update'));
1282
1355
  console.log(chalk.dim(' Add new scenarios only for genuinely new data states not covered by existing ones'));
1283
1356
  printAppScenarioInstructions();
1284
1357
  console.log();
1285
- console.log(chalk.dim('Focus on creating and registering app-level scenarios. Code fixes happen in step 10 if needed.'));
1358
+ console.log(chalk.dim('Focus on creating and registering app-level scenarios. Code fixes happen in step 12 if needed.'));
1286
1359
  console.log();
1287
1360
  console.log(chalk.bold.cyan("Verify your work — screenshots don't lie:"));
1288
1361
  console.log(chalk.cyan(' • View every captured screenshot. Does it show what the scenario name promises?'));
@@ -1293,43 +1366,43 @@ function printStep8(root, feature) {
1293
1366
  console.log(chalk.bold.yellow('GATE: Before proceeding, run `codeyam editor scenario-coverage`'));
1294
1367
  console.log(chalk.yellow(' This checks which existing scenarios have stale screenshots.'));
1295
1368
  console.log(chalk.yellow(' Re-register every stale scenario listed until the check passes.'));
1296
- stopGate(8);
1369
+ stopGate(10);
1297
1370
  }
1298
- // ─── Step 9: User Scenarios ───────────────────────────────────────────
1299
- function printStep9(root, feature) {
1371
+ // ─── Step 11: User Scenarios ───────────────────────────────────────────
1372
+ function printStep11(root, feature) {
1300
1373
  const port = getServerPort();
1301
1374
  const prevState = readState(root);
1302
- const isResuming = prevState?.step === 9;
1375
+ const isResuming = prevState?.step === 11;
1303
1376
  const now = new Date().toISOString();
1304
1377
  writeState(root, {
1305
1378
  feature,
1306
- step: 9,
1307
- label: STEP_LABELS[9],
1379
+ step: 11,
1380
+ label: STEP_LABELS[11],
1308
1381
  startedAt: isResuming ? prevState.startedAt : now,
1309
1382
  featureStartedAt: prevState?.featureStartedAt || now,
1310
1383
  });
1311
- logEvent(root, 'step', { step: 9, label: 'User Scenarios', feature });
1312
- stepHeader(9, 'User Scenarios', feature);
1384
+ logEvent(root, 'step', { step: 11, label: 'User Scenarios', feature });
1385
+ stepHeader(11, 'User Scenarios', feature);
1313
1386
  if (isResuming) {
1314
- printResumptionHeader(9);
1387
+ printResumptionHeader(11);
1315
1388
  }
1316
- console.log('Create per-persona variations of existing scenarios. Skip to step 10 if no users.');
1389
+ console.log('Create per-persona variations of existing scenarios. Skip to step 12 if no users.');
1317
1390
  console.log();
1318
1391
  console.log(chalk.bold('If the app has NO users/auth:'));
1319
- console.log(chalk.dim(' Skip this step and proceed to step 10 (Verify).'));
1392
+ console.log(chalk.dim(' Skip this step and proceed to step 12 (Verify).'));
1320
1393
  console.log();
1321
1394
  console.log(chalk.bold('If the app has users/auth:'));
1322
1395
  console.log();
1323
1396
  console.log(chalk.bold('Goal: Create persona variations of EXISTING app scenarios.'));
1324
1397
  console.log(chalk.dim(' Do NOT create standalone persona scenarios. Each user-persona scenario should be'));
1325
- console.log(chalk.dim(' a variation of an existing app scenario from step 8, with user-specific state layered on.'));
1398
+ console.log(chalk.dim(' a variation of an existing app scenario from step 10, with user-specific state layered on.'));
1326
1399
  console.log();
1327
1400
  console.log(chalk.bold('Checklist:'));
1328
1401
  checkbox('Run `codeyam editor scenarios` — list all existing app scenarios');
1329
1402
  checkbox('For EACH existing app scenario, create a logged-in variation:');
1330
1403
  console.log(chalk.dim(' Copy the scenario seed data and add "session":{"cookieValue":"sess_alice"} + user seed'));
1331
1404
  console.log(chalk.dim(' Name: "<Original Name> - Logged In" (e.g. "Full Catalog - Logged In")'));
1332
- console.log(chalk.dim(' Step 8 scenarios already serve as logged-out versions (no session cookie)'));
1405
+ console.log(chalk.dim(' Step 10 scenarios already serve as logged-out versions (no session cookie)'));
1333
1406
  checkbox('If there are multiple user roles (admin, regular, etc.), create role-specific variations too');
1334
1407
  console.log(chalk.dim(' Each persona scenario layers user-specific seed data on top of an app scenario'));
1335
1408
  checkbox('Include "dimensions" — inherit from the base app scenario, or override if the persona implies a different device');
@@ -1338,26 +1411,26 @@ function printStep9(root, feature) {
1338
1411
  console.log(chalk.yellow(' If clientErrors is non-empty → fix the issue and re-register the scenario'));
1339
1412
  console.log();
1340
1413
  console.log(chalk.dim('See FEATURE_PATTERNS.md and AUTH_PATTERNS.md for auth scenario guidance.'));
1341
- stopGate(9);
1414
+ stopGate(11);
1342
1415
  }
1343
- // ─── Step 10: Verify ──────────────────────────────────────────────────
1344
- function printStep10(root, feature) {
1416
+ // ─── Step 12: Verify ──────────────────────────────────────────────────
1417
+ function printStep12(root, feature) {
1345
1418
  const port = getServerPort();
1346
1419
  const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
1347
1420
  const prevState = readState(root);
1348
- const isResuming = prevState?.step === 10;
1421
+ const isResuming = prevState?.step === 12;
1349
1422
  const now = new Date().toISOString();
1350
1423
  writeState(root, {
1351
1424
  feature,
1352
- step: 10,
1353
- label: STEP_LABELS[10],
1425
+ step: 12,
1426
+ label: STEP_LABELS[12],
1354
1427
  startedAt: isResuming ? prevState.startedAt : now,
1355
1428
  featureStartedAt: prevState?.featureStartedAt || now,
1356
1429
  });
1357
- logEvent(root, 'step', { step: 10, label: 'Verify', feature });
1358
- stepHeader(10, 'Verify', feature);
1430
+ logEvent(root, 'step', { step: 12, label: 'Verify', feature });
1431
+ stepHeader(12, 'Verify', feature);
1359
1432
  if (isResuming) {
1360
- printResumptionHeader(10);
1433
+ printResumptionHeader(12);
1361
1434
  }
1362
1435
  console.log('Verify component isolation screenshots, editor scenarios, and library tests.');
1363
1436
  console.log();
@@ -1380,63 +1453,63 @@ function printStep10(root, feature) {
1380
1453
  checkbox('Verify no broken images: `codeyam editor verify-images \'{"paths":["/"], "imageUrls":["url1"]}\'` with all page paths and image URLs');
1381
1454
  console.log();
1382
1455
  console.log(chalk.bold('Library functions — test check:'));
1383
- checkbox('Re-run all test files from step 5 to confirm they still pass');
1456
+ checkbox('Re-run all test files from step 7 to confirm they still pass');
1384
1457
  console.log(chalk.dim(' Example: npx vitest run app/lib/drinks.test.ts'));
1385
1458
  checkbox('If any test fails, fix the source code and re-run');
1386
1459
  console.log();
1387
1460
  console.log(chalk.dim('Focus on fixing issues. All component screenshots, scenarios, and tests must be clean before proceeding.'));
1388
- stopGate(10);
1461
+ stopGate(12);
1389
1462
  }
1390
- // ─── Step 11: Journal ─────────────────────────────────────────────────
1391
- function printStep11(root, feature) {
1463
+ // ─── Step 13: Journal ─────────────────────────────────────────────────
1464
+ function printStep13(root, feature) {
1392
1465
  const port = getServerPort();
1393
1466
  const prevState = readState(root);
1394
- const isResuming = prevState?.step === 11;
1467
+ const isResuming = prevState?.step === 13;
1395
1468
  const now = new Date().toISOString();
1396
1469
  writeState(root, {
1397
1470
  feature,
1398
- step: 11,
1399
- label: STEP_LABELS[11],
1471
+ step: 13,
1472
+ label: STEP_LABELS[13],
1400
1473
  startedAt: isResuming ? prevState.startedAt : now,
1401
1474
  featureStartedAt: prevState?.featureStartedAt || now,
1402
1475
  });
1403
- logEvent(root, 'step', { step: 11, label: 'Journal', feature });
1404
- stepHeader(11, 'Journal', feature);
1476
+ logEvent(root, 'step', { step: 13, label: 'Journal', feature });
1477
+ stepHeader(13, 'Journal', feature);
1405
1478
  if (isResuming) {
1406
- printResumptionHeader(11);
1479
+ printResumptionHeader(13);
1407
1480
  }
1408
1481
  console.log('Create or update the journal entry for this feature.');
1409
1482
  console.log();
1410
1483
  console.log(chalk.bold('Checklist:'));
1411
1484
  checkbox('Write a concise description of what was built (2-3 sentences)');
1412
- checkbox(`First time at step 11 — create journal entry with ALL session screenshots:`);
1485
+ checkbox(`First time at step 13 — create journal entry with ALL session screenshots:`);
1413
1486
  console.log(chalk.dim(` codeyam editor journal '{"title":"...","type":"feature","description":"...","includeSessionScenarios":true}'`));
1414
1487
  console.log(chalk.dim(' includeSessionScenarios auto-discovers component + app + user persona screenshots'));
1415
- checkbox(`Returning to step 11 (before commit) — update the existing journal entry:`);
1488
+ checkbox(`Returning to step 13 (before commit) — update the existing journal entry:`);
1416
1489
  console.log(chalk.dim(` codeyam editor journal-update '{"time":"<journal entry time>","description":"<updated>","includeSessionScenarios":true}'`));
1417
1490
  console.log(chalk.dim(' Note: PATCH only works before the entry is committed. After commit, use POST to create a new entry.'));
1418
1491
  console.log();
1419
- console.log(chalk.dim('Focus on creating or updating the journal entry. Summary and presentation happen in step 13.'));
1420
- stopGate(11);
1492
+ console.log(chalk.dim('Focus on creating or updating the journal entry. Summary and presentation happen in step 15.'));
1493
+ stopGate(13);
1421
1494
  }
1422
- // ─── Step 12: Review ──────────────────────────────────────────────────
1423
- function printStep12(root, feature) {
1495
+ // ─── Step 14: Review ──────────────────────────────────────────────────
1496
+ function printStep14(root, feature) {
1424
1497
  const port = getServerPort();
1425
1498
  const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
1426
1499
  const prevState = readState(root);
1427
- const isResuming = prevState?.step === 12;
1500
+ const isResuming = prevState?.step === 14;
1428
1501
  const now = new Date().toISOString();
1429
1502
  writeState(root, {
1430
1503
  feature,
1431
- step: 12,
1432
- label: STEP_LABELS[12],
1504
+ step: 14,
1505
+ label: STEP_LABELS[14],
1433
1506
  startedAt: isResuming ? prevState.startedAt : now,
1434
1507
  featureStartedAt: prevState?.featureStartedAt || now,
1435
1508
  });
1436
- logEvent(root, 'step', { step: 12, label: 'Review', feature });
1437
- stepHeader(12, 'Review', feature);
1509
+ logEvent(root, 'step', { step: 14, label: 'Review', feature });
1510
+ stepHeader(14, 'Review', feature);
1438
1511
  if (isResuming) {
1439
- printResumptionHeader(12);
1512
+ printResumptionHeader(14);
1440
1513
  }
1441
1514
  console.log('Verify all screenshots and checks pass before presenting to the user.');
1442
1515
  console.log();
@@ -1449,26 +1522,27 @@ function printStep12(root, feature) {
1449
1522
  checkbox('If `hasErrors` is true, fix them and re-capture affected scenarios');
1450
1523
  checkbox('Run `codeyam editor verify-images \'{"paths":["/"], "imageUrls":["url1"]}\'` with all page paths and image URLs');
1451
1524
  checkbox('Fix or remove any broken images before continuing');
1525
+ checkbox('Recapture stale scenarios: `codeyam editor recapture-stale`');
1452
1526
  checkbox('Run `codeyam editor audit` to verify completeness of scenarios and tests');
1453
1527
  checkbox('Do not proceed until all checks pass');
1454
- stopGate(12);
1528
+ stopGate(14);
1455
1529
  }
1456
- // ─── Step 13: Present ─────────────────────────────────────────────────
1457
- function printStep13(root, feature) {
1530
+ // ─── Step 15: Present ─────────────────────────────────────────────────
1531
+ function printStep15(root, feature) {
1458
1532
  const prevState = readState(root);
1459
- const isResuming = prevState?.step === 13;
1533
+ const isResuming = prevState?.step === 15;
1460
1534
  const now = new Date().toISOString();
1461
1535
  writeState(root, {
1462
1536
  feature,
1463
- step: 13,
1464
- label: STEP_LABELS[13],
1537
+ step: 15,
1538
+ label: STEP_LABELS[15],
1465
1539
  startedAt: isResuming ? prevState.startedAt : now,
1466
1540
  featureStartedAt: prevState?.featureStartedAt || now,
1467
1541
  });
1468
- logEvent(root, 'step', { step: 13, label: 'Present', feature });
1469
- stepHeader(13, 'Present', feature);
1542
+ logEvent(root, 'step', { step: 15, label: 'Present', feature });
1543
+ stepHeader(15, 'Present', feature);
1470
1544
  if (isResuming) {
1471
- printResumptionHeader(13);
1545
+ printResumptionHeader(15);
1472
1546
  }
1473
1547
  console.log('Present the results to the user and get their approval.');
1474
1548
  console.log();
@@ -1487,7 +1561,7 @@ function printStep13(root, feature) {
1487
1561
  chalk.dim(' — describe changes, then re-verify'));
1488
1562
  console.log();
1489
1563
  console.log(chalk.bold('If the user chooses "Save & commit":'));
1490
- checkbox('Advance to the commit step: `codeyam editor 14`');
1564
+ checkbox('Advance to the commit step: `codeyam editor 16`');
1491
1565
  console.log();
1492
1566
  console.log(chalk.bold('If the user chooses "Make changes" (or asks for ANY change, even as a question):'));
1493
1567
  checkbox(`Hide the results panel: \`codeyam editor hide-results\``);
@@ -1495,7 +1569,7 @@ function printStep13(root, feature) {
1495
1569
  checkbox(`Run: \`codeyam editor change "${feature}"\` — this gives you the change checklist`);
1496
1570
  checkbox('THEN make the requested changes and follow the checklist');
1497
1571
  console.log(chalk.red.bold(' IMPORTANT: Always run the change command BEFORE writing any code.'));
1498
- stopGate(13, { confirm: true });
1572
+ stopGate(15, { confirm: true });
1499
1573
  }
1500
1574
  // ─── Migration Mode ──────────────────────────────────────────────────
1501
1575
  /**
@@ -2102,47 +2176,48 @@ function handleMigrateCommand(root, subArg) {
2102
2176
  };
2103
2177
  stepFns[step](root);
2104
2178
  }
2105
- // ─── Step 14: Commit ─────────────────────────────────────────────────
2106
- function printStep14(root, feature) {
2179
+ // ─── Step 16: Commit ─────────────────────────────────────────────────
2180
+ function printStep16(root, feature) {
2107
2181
  const prevState = readState(root);
2108
- const isResuming = prevState?.step === 14;
2182
+ const isResuming = prevState?.step === 16;
2109
2183
  const now = new Date().toISOString();
2110
2184
  writeState(root, {
2111
2185
  feature,
2112
- step: 14,
2113
- label: STEP_LABELS[14],
2186
+ step: 16,
2187
+ label: STEP_LABELS[16],
2114
2188
  startedAt: isResuming ? prevState.startedAt : now,
2115
2189
  featureStartedAt: prevState?.featureStartedAt || now,
2116
2190
  });
2117
- logEvent(root, 'step', { step: 14, label: 'Commit', feature });
2118
- stepHeader(14, 'Commit', feature);
2191
+ logEvent(root, 'step', { step: 16, label: 'Commit', feature });
2192
+ stepHeader(16, 'Commit', feature);
2119
2193
  if (isResuming) {
2120
- printResumptionHeader(14);
2194
+ printResumptionHeader(16);
2121
2195
  }
2122
2196
  console.log('Commit all changes for this feature.');
2123
2197
  console.log();
2124
2198
  console.log(chalk.bold('Checklist:'));
2199
+ checkbox('Ensure all screenshots are fresh: `codeyam editor recapture-stale`');
2125
2200
  checkbox(`Hide the results panel: \`codeyam editor hide-results\``);
2126
2201
  checkbox(`Git commit using the journal description: \`codeyam editor commit '{"message":"feat: <title>\\n\\n<journal description>"}'\``);
2127
2202
  console.log(chalk.dim(' The commit message body MUST match the journal description exactly'));
2128
- stopGate(14);
2203
+ stopGate(16);
2129
2204
  }
2130
- // ─── Step 15: Finalize ───────────────────────────────────────────────
2131
- function printStep15(root, feature) {
2205
+ // ─── Step 17: Finalize ───────────────────────────────────────────────
2206
+ function printStep17(root, feature) {
2132
2207
  const prevState = readState(root);
2133
- const isResuming = prevState?.step === 15;
2208
+ const isResuming = prevState?.step === 17;
2134
2209
  const now = new Date().toISOString();
2135
2210
  writeState(root, {
2136
2211
  feature,
2137
- step: 15,
2138
- label: STEP_LABELS[15],
2212
+ step: 17,
2213
+ label: STEP_LABELS[17],
2139
2214
  startedAt: isResuming ? prevState.startedAt : now,
2140
2215
  featureStartedAt: prevState?.featureStartedAt || now,
2141
2216
  });
2142
- logEvent(root, 'step', { step: 15, label: 'Finalize', feature });
2143
- stepHeader(15, 'Finalize', feature);
2217
+ logEvent(root, 'step', { step: 17, label: 'Finalize', feature });
2218
+ stepHeader(17, 'Finalize', feature);
2144
2219
  if (isResuming) {
2145
- printResumptionHeader(15);
2220
+ printResumptionHeader(17);
2146
2221
  }
2147
2222
  console.log('Update the journal with the commit SHA and amend the commit.');
2148
2223
  console.log();
@@ -2150,24 +2225,24 @@ function printStep15(root, feature) {
2150
2225
  checkbox(`Update journal with commit SHA: \`codeyam editor journal-update '{"time":"<journal entry time>","commitSha":"<sha>","commitMessage":"feat: <title>"}'\``);
2151
2226
  checkbox('Amend the commit to include the journal update: `git add .codeyam/journal/ && git commit --amend --no-edit`');
2152
2227
  console.log(chalk.dim(' The journal-update modifies journal files after the commit — amend to keep the tree clean.'));
2153
- stopGate(15);
2228
+ stopGate(17);
2154
2229
  }
2155
- // ─── Step 16: Push ───────────────────────────────────────────────────
2156
- function printStep16(root, feature) {
2230
+ // ─── Step 18: Push ───────────────────────────────────────────────────
2231
+ function printStep18(root, feature) {
2157
2232
  const prevState = readState(root);
2158
- const isResuming = prevState?.step === 16;
2233
+ const isResuming = prevState?.step === 18;
2159
2234
  const now = new Date().toISOString();
2160
2235
  writeState(root, {
2161
2236
  feature,
2162
- step: 16,
2163
- label: STEP_LABELS[16],
2237
+ step: 18,
2238
+ label: STEP_LABELS[18],
2164
2239
  startedAt: isResuming ? prevState.startedAt : now,
2165
2240
  featureStartedAt: prevState?.featureStartedAt || now,
2166
2241
  });
2167
- logEvent(root, 'step', { step: 16, label: 'Push', feature });
2168
- stepHeader(16, 'Push', feature);
2242
+ logEvent(root, 'step', { step: 18, label: 'Push', feature });
2243
+ stepHeader(18, 'Push', feature);
2169
2244
  if (isResuming) {
2170
- printResumptionHeader(16);
2245
+ printResumptionHeader(18);
2171
2246
  }
2172
2247
  console.log('Push the commit to the remote repository.');
2173
2248
  console.log();
@@ -2178,12 +2253,17 @@ function printStep16(root, feature) {
2178
2253
  console.log(chalk.dim(' If the user says yes, run: `git push`'));
2179
2254
  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"'));
2180
2255
  console.log(chalk.dim(' If the user wants help, guide them through `gh repo create` or manual GitHub setup, then push.'));
2181
- checkbox('After the user responds, run: `codeyam editor steps` to start the next feature');
2182
- console.log();
2183
- console.log(chalk.red.bold(' If the user reports a bug or requests a fix after committing:'));
2184
- console.log(chalk.red.bold(' You MUST still run `codeyam editor change` before making any changes.'));
2185
- console.log(chalk.red.bold(' The change workflow applies to ALL changes — post-commit fixes are not an exception.'));
2186
- stopGate(16, { confirm: true });
2256
+ checkbox('Mark the current build task as complete');
2257
+ checkbox('Create a new task with EXACTLY this title: "Ask the user what to build next, then run `codeyam editor 1` to start the next feature"');
2258
+ console.log(chalk.dim(' There must ALWAYS be an active task. Never leave the task list empty.'));
2259
+ console.log(chalk.dim(' This task reminds you to use the editor workflow for the next feature.'));
2260
+ console.log();
2261
+ console.log(chalk.bold.yellow('IMPORTANT: After this step, the current feature is DONE.'));
2262
+ console.log(chalk.yellow(' Any new user request — even if it sounds related — is a NEW feature.'));
2263
+ console.log(chalk.yellow(' Do NOT use `codeyam editor change` or start building directly.'));
2264
+ console.log(chalk.yellow(' The next feature MUST start with `codeyam editor 1`.'));
2265
+ console.log(chalk.yellow(' The change workflow is ONLY for modifications during steps 1-15, not after commit.'));
2266
+ stopGate(18, { confirm: true });
2187
2267
  }
2188
2268
  // ─── Command definition ───────────────────────────────────────────────
2189
2269
  // ─── Analyze-imports subcommand ────────────────────────────────────────
@@ -2203,7 +2283,7 @@ async function handleAnalyzeImports(options = {}) {
2203
2283
  return;
2204
2284
  }
2205
2285
  console.error(chalk.red('Error: .codeyam/glossary.json not found.'));
2206
- console.error(chalk.dim(' Run codeyam editor 6 to create the glossary first.'));
2286
+ console.error(chalk.dim(' Run codeyam editor 8 to create the glossary first.'));
2207
2287
  process.exit(1);
2208
2288
  }
2209
2289
  let glossaryEntries;
@@ -2657,8 +2737,14 @@ async function handleRegister(jsonArg) {
2657
2737
  else {
2658
2738
  parts.push(`errors=0`);
2659
2739
  }
2660
- if (data.seedResult)
2661
- parts.push(`seed=${data.seedResult.success}`);
2740
+ if (data.seedResult) {
2741
+ if (data.seedResult.success) {
2742
+ parts.push(`seed=ok`);
2743
+ }
2744
+ else {
2745
+ parts.push(chalk.red(`seed=FAILED${data.seedResult.error ? ` (${data.seedResult.error})` : ''}`));
2746
+ }
2747
+ }
2662
2748
  if (data.captureError)
2663
2749
  parts.push(chalk.yellow(`captureError="${data.captureError}"`));
2664
2750
  console.log(prefix + parts.join(' '));
@@ -2877,7 +2963,7 @@ async function handleDependents(entityName) {
2877
2963
  *
2878
2964
  * Prints a condensed post-change checklist that guides Claude through
2879
2965
  * re-verifying after user-requested modifications. When called from
2880
- * step 13+, this loops back to step 13 (present). When called from an
2966
+ * step 15+, this loops back to step 15 (present). When called from an
2881
2967
  * earlier step, it returns to that step so the normal flow continues.
2882
2968
  */
2883
2969
  function handleChange(feature) {
@@ -2895,7 +2981,7 @@ function handleChange(feature) {
2895
2981
  process.exit(1);
2896
2982
  }
2897
2983
  }
2898
- const currentStep = state?.step ?? 13;
2984
+ const currentStep = state?.step ?? 15;
2899
2985
  const port = getServerPort();
2900
2986
  console.log();
2901
2987
  console.log(chalk.bold.cyan('━━━ Change Loop ━━━'));
@@ -2943,6 +3029,7 @@ function handleChange(feature) {
2943
3029
  console.log(chalk.dim(' re-register "Home - Default", "Catalog - Full", "Detail - WithReviews", etc.'));
2944
3030
  checkbox("Enrich existing scenario data to exercise the change — don't just re-register unchanged data");
2945
3031
  console.log(chalk.dim(' Add data that demonstrates what changed: new fields, relationships, states, content variety.'));
3032
+ console.log(chalk.dim(' If the enriched data makes the scenario name too narrow, rename it to reflect its broader coverage.'));
2946
3033
  checkbox('After each re-registration, view the screenshot to verify data is visible');
2947
3034
  console.log(chalk.dim(" If the screenshot doesn't show the data you put in, the scenario is broken."));
2948
3035
  console.log();
@@ -2951,6 +3038,7 @@ function handleChange(feature) {
2951
3038
  checkbox(`Navigate to key pages to verify changes: \`codeyam editor preview '{"path":"/your-route","dimension":"${dim}"}'\``);
2952
3039
  printDimensionGuidance(dim, dimNames);
2953
3040
  checkbox('Run `codeyam editor scenario-coverage` — all affected scenarios must be fresh');
3041
+ checkbox('Recapture stale scenarios: `codeyam editor recapture-stale`');
2954
3042
  checkbox('Run `codeyam editor audit` — all checks must pass');
2955
3043
  checkbox(`Check for client-side errors: \`codeyam editor client-errors\``);
2956
3044
  console.log(chalk.dim(' If `hasContent=false`, the preview is blank — fix the code before proceeding.'));
@@ -2965,10 +3053,10 @@ function handleChange(feature) {
2965
3053
  console.log(chalk.dim(' Always update the existing uncommitted entry — do NOT create a new one.'));
2966
3054
  console.log(chalk.dim(' Only create a new entry (POST) if no uncommitted entry exists for this feature.'));
2967
3055
  console.log();
2968
- // If the change was initiated from a step before 13, return to that step
2969
- // instead of jumping to step 13. The change workflow should only loop to
2970
- // step 13 when changes are requested FROM step 13.
2971
- if (currentStep < 13) {
3056
+ // If the change was initiated from a step before 15, return to that step
3057
+ // instead of jumping to step 15. The change workflow should only loop to
3058
+ // step 15 when changes are requested FROM step 15.
3059
+ if (currentStep < 15) {
2972
3060
  console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
2973
3061
  console.log(chalk.red.bold(' REQUIRED: Return to current step'));
2974
3062
  console.log(chalk.red.bold(' When all checks pass, you MUST run: ') +
@@ -2979,7 +3067,7 @@ function handleChange(feature) {
2979
3067
  console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
2980
3068
  console.log(chalk.red.bold(' REQUIRED: Show Results'));
2981
3069
  console.log(chalk.red.bold(' When all checks pass, you MUST run: ') +
2982
- chalk.bold(`codeyam editor 13`));
3070
+ chalk.bold(`codeyam editor 15`));
2983
3071
  console.log(chalk.red.bold(' The user ALWAYS expects to see results after changes. DO NOT skip this step.'));
2984
3072
  console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
2985
3073
  }
@@ -3126,8 +3214,6 @@ function printAuditGateFailures(data) {
3126
3214
  }
3127
3215
  }
3128
3216
  console.error(chalk.yellow('\nFix: Fix the code errors above, then re-capture the affected scenarios.'));
3129
- console.error(chalk.yellow('If errors reference browser APIs (localStorage, sessionStorage, window, document),'));
3130
- console.error(chalk.yellow('create a universal mock: codeyam detect-universal-mocks'));
3131
3217
  }
3132
3218
  }
3133
3219
  console.error(chalk.dim('\nRun `codeyam editor audit` for full details.\n'));
@@ -3186,11 +3272,18 @@ async function handleAudit() {
3186
3272
  }
3187
3273
  console.log(chalk.bold('Components (scenarios):'));
3188
3274
  for (const c of components) {
3189
- const icon = c.status === 'ok' ? chalk.green('✓') : chalk.red('✗');
3275
+ const icon = c.status === 'ok'
3276
+ ? chalk.green('✓')
3277
+ : c.status === 'needs_recapture'
3278
+ ? chalk.yellow('↻')
3279
+ : chalk.red('✗');
3190
3280
  let detail;
3191
3281
  if (c.status === 'has_errors') {
3192
3282
  detail = chalk.red(` — ${c.scenarioCount} scenario${c.scenarioCount !== 1 ? 's' : ''} but has client errors`);
3193
3283
  }
3284
+ else if (c.status === 'needs_recapture') {
3285
+ detail = chalk.yellow(` — ${c.scenarioCount} scenario${c.scenarioCount !== 1 ? 's' : ''}, needs recapture`);
3286
+ }
3194
3287
  else if (c.status === 'ok') {
3195
3288
  detail = chalk.dim(` (${c.scenarioCount} scenario${c.scenarioCount !== 1 ? 's' : ''})`);
3196
3289
  }
@@ -3199,7 +3292,7 @@ async function handleAudit() {
3199
3292
  }
3200
3293
  // Show file path for failing components always, for OK only when name is ambiguous
3201
3294
  const isDuplicate = (componentNameCounts.get(c.name) || 0) > 1;
3202
- const showPath = c.status !== 'ok' || isDuplicate;
3295
+ const showPath = (c.status !== 'ok' && c.status !== 'needs_recapture') || isDuplicate;
3203
3296
  const pathSuffix = showPath && c.filePath ? chalk.dim(` (${c.filePath})`) : '';
3204
3297
  console.log(` ${icon} ${c.name}${pathSuffix}${detail}`);
3205
3298
  if (c.clientErrors && c.clientErrors.length > 0) {
@@ -3209,14 +3302,6 @@ async function handleAudit() {
3209
3302
  if (c.clientErrors.length > 3) {
3210
3303
  console.log(chalk.dim(` … and ${c.clientErrors.length - 3} more`));
3211
3304
  }
3212
- // Detect browser API errors and provide actionable guidance
3213
- const browserApiPattern = /\b(localStorage|sessionStorage|window\.|document\.|navigator\.|indexedDB|matchMedia|ResizeObserver|IntersectionObserver|MutationObserver)\b/;
3214
- const hasBrowserApiErrors = c.clientErrors.some((err) => browserApiPattern.test(err));
3215
- if (hasBrowserApiErrors) {
3216
- console.log(chalk.yellow(` ⚠ These errors are caused by browser APIs that don't exist during server-side analysis.`));
3217
- console.log(chalk.yellow(` Fix: Create a universal mock to stub the missing API. Run: codeyam detect-universal-mocks`));
3218
- console.log(chalk.yellow(` DO NOT re-run the audit or analyze-imports — the error will persist until a mock is created.`));
3219
- }
3220
3305
  }
3221
3306
  }
3222
3307
  console.log();
@@ -3277,11 +3362,9 @@ async function handleAudit() {
3277
3362
  console.log(chalk.red(' analyze-imports was run automatically but these entities are STILL incomplete.'));
3278
3363
  console.log(chalk.red(' DO NOT re-run analyze-imports or the audit in a loop — the result will be the same.'));
3279
3364
  console.log(chalk.yellow(' This means the analysis failed for these entities. Common causes:'));
3280
- console.log(chalk.yellow(' • Entity code uses browser APIs (localStorage, window, document) — create a universal mock'));
3281
3365
  console.log(chalk.yellow(' • Source file was renamed/deleted but glossary still references the old path'));
3282
3366
  console.log(chalk.yellow(' • Entity has import errors that prevent analysis'));
3283
3367
  console.log(chalk.yellow(' To investigate: run `codeyam editor analyze-imports` (without --silent) to see the full error.'));
3284
- console.log(chalk.yellow(' To fix browser API issues: run `codeyam detect-universal-mocks`'));
3285
3368
  }
3286
3369
  else {
3287
3370
  console.log(chalk.yellow(' Run `codeyam editor analyze-imports` to analyze these entities'));
@@ -3313,7 +3396,7 @@ async function handleAudit() {
3313
3396
  : `${s.status.status}`;
3314
3397
  console.log(` ${chalk.red('✗')} ${s.scenarioName} ${chalk.dim(`(${s.entityName} — ${reason})`)}`);
3315
3398
  }
3316
- console.log(chalk.yellow(' Re-register these scenarios to capture updated screenshots.'));
3399
+ console.log(chalk.yellow(' Run: codeyam editor recapture-stale'));
3317
3400
  console.log();
3318
3401
  }
3319
3402
  // Duplicate glossary names (warning, not a failure)
@@ -3385,6 +3468,109 @@ async function handleAudit() {
3385
3468
  console.log(chalk.yellow('Warning: Could not build import graph. Run `codeyam editor analyze-imports` manually.'));
3386
3469
  }
3387
3470
  }
3471
+ // ─── Recapture-stale subcommand ────────────────────────────────────────
3472
+ /**
3473
+ * `codeyam editor recapture-stale`
3474
+ *
3475
+ * Identifies all scenarios whose entity (or dependency) has changed but
3476
+ * whose screenshot hasn't been recaptured this session, then recaptures
3477
+ * them in batch.
3478
+ */
3479
+ async function handleRecaptureStale() {
3480
+ const port = getServerPort();
3481
+ const url = `http://localhost:${port}/api/editor-recapture-stale`;
3482
+ console.log();
3483
+ console.log(chalk.bold.cyan('━━━ Recapture Stale Scenarios ━━━'));
3484
+ console.log();
3485
+ let res;
3486
+ try {
3487
+ res = await fetch(url, { method: 'POST' });
3488
+ }
3489
+ catch (err) {
3490
+ console.error(chalk.red('Error: Could not reach the CodeYam server. Is it running?'));
3491
+ console.error(chalk.dim(` ${err.message}`));
3492
+ process.exit(1);
3493
+ }
3494
+ if (!res.ok) {
3495
+ const body = await res.json().catch(() => null);
3496
+ console.error(chalk.red(`Error: Recapture endpoint returned ${res.status}`));
3497
+ if (body?.error)
3498
+ console.error(chalk.red(` ${body.error}`));
3499
+ process.exit(1);
3500
+ }
3501
+ // Handle JSON response (early returns like "no changes" / "all up to date")
3502
+ const contentType = res.headers.get('content-type') || '';
3503
+ if (contentType.includes('application/json')) {
3504
+ const data = await res.json();
3505
+ if (data.note) {
3506
+ console.log(chalk.dim(data.note));
3507
+ }
3508
+ else if (data.total === 0) {
3509
+ console.log(chalk.green('All scenarios are up to date.'));
3510
+ }
3511
+ console.log();
3512
+ return;
3513
+ }
3514
+ // Stream NDJSON progress events
3515
+ let total = 0;
3516
+ let recapturedCount = 0;
3517
+ let failedCount = 0;
3518
+ const reader = res.body?.getReader();
3519
+ if (!reader) {
3520
+ console.error(chalk.red('Error: No response body'));
3521
+ process.exit(1);
3522
+ }
3523
+ const decoder = new TextDecoder();
3524
+ let buffer = '';
3525
+ while (true) {
3526
+ const { done, value } = await reader.read();
3527
+ if (done)
3528
+ break;
3529
+ buffer += decoder.decode(value, { stream: true });
3530
+ const lines = buffer.split('\n');
3531
+ buffer = lines.pop() || ''; // Keep incomplete last line in buffer
3532
+ for (const line of lines) {
3533
+ if (!line.trim())
3534
+ continue;
3535
+ try {
3536
+ const event = JSON.parse(line);
3537
+ switch (event.type) {
3538
+ case 'start':
3539
+ total = event.total;
3540
+ console.log(`Found ${total} stale scenario(s). Recapturing...\n`);
3541
+ break;
3542
+ case 'capturing':
3543
+ process.stdout.write(chalk.dim(` … ${event.name}`));
3544
+ break;
3545
+ case 'success':
3546
+ // Clear the "capturing" line and print success
3547
+ process.stdout.write('\r\x1b[K');
3548
+ console.log(` ${chalk.green('✓')} ${event.name}`);
3549
+ recapturedCount++;
3550
+ break;
3551
+ case 'failure':
3552
+ process.stdout.write('\r\x1b[K');
3553
+ console.log(` ${chalk.red('✗')} ${event.name} — ${chalk.dim(event.error)}`);
3554
+ failedCount++;
3555
+ break;
3556
+ case 'done':
3557
+ // Final summary
3558
+ console.log();
3559
+ const color = failedCount > 0 ? chalk.yellow : chalk.green;
3560
+ console.log(color(`Recaptured ${recapturedCount}/${total} scenario(s).`));
3561
+ console.log();
3562
+ break;
3563
+ }
3564
+ }
3565
+ catch {
3566
+ // Skip unparseable lines
3567
+ }
3568
+ }
3569
+ }
3570
+ if (failedCount > 0) {
3571
+ process.exit(1);
3572
+ }
3573
+ }
3388
3574
  // ─── Scenarios subcommand ─────────────────────────────────────────────
3389
3575
  async function handleScenarios() {
3390
3576
  const port = getServerPort();
@@ -3632,7 +3818,7 @@ async function handleTemplate() {
3632
3818
  console.log(chalk.green(' Git initialized.'));
3633
3819
  }
3634
3820
  // 4. Run codeyam init
3635
- console.log(chalk.bold('Running codeyam init...'));
3821
+ console.log(chalk.bold('Initializing project...'));
3636
3822
  await initCommand.handler({
3637
3823
  force: true,
3638
3824
  'keep-server': true,
@@ -3711,7 +3897,7 @@ async function handleSync() {
3711
3897
  // fall through
3712
3898
  }
3713
3899
  if (!projectSlug) {
3714
- console.error(chalk.red('Error: No project slug found. Run codeyam init first.'));
3900
+ console.error(chalk.red('Error: No project slug found. Run `codeyam editor template` to initialize the project.'));
3715
3901
  process.exit(1);
3716
3902
  }
3717
3903
  const connectionOk = await withoutSpinner(() => testEnvironment());
@@ -3895,7 +4081,7 @@ function handleEditorDebug(args) {
3895
4081
  scenarios.push({
3896
4082
  id: 'overview-with-state',
3897
4083
  title: 'Cycle overview (project, with state)',
3898
- render: () => withTempRoot(true, (tempRoot) => captureOutput(() => printCycleOverview(tempRoot, makeState(4, feature)))),
4084
+ render: () => withTempRoot(true, (tempRoot) => captureOutput(() => printCycleOverview(tempRoot, makeState(6, feature)))),
3899
4085
  });
3900
4086
  }
3901
4087
  const stepFns = {
@@ -3915,8 +4101,10 @@ function handleEditorDebug(args) {
3915
4101
  14: printStep14,
3916
4102
  15: printStep15,
3917
4103
  16: printStep16,
4104
+ 17: printStep17,
4105
+ 18: printStep18,
3918
4106
  };
3919
- for (let step = 1; step <= 16; step++) {
4107
+ for (let step = 1; step <= 18; step++) {
3920
4108
  const stepId = `step-${step}`;
3921
4109
  if (!wants(stepId))
3922
4110
  continue;
@@ -3936,7 +4124,7 @@ function handleEditorDebug(args) {
3936
4124
  if (step === 2) {
3937
4125
  scenarios.push({
3938
4126
  id: 'step-2-scaffold',
3939
- title: 'Step 2 (Prototype) — scaffold flow (no project)',
4127
+ title: 'Step 2 (Prepare) — scaffold flow (no project)',
3940
4128
  render: () => withTempRoot(false, (tempRoot) => captureOutput(() => printStep2(tempRoot, feature))),
3941
4129
  });
3942
4130
  }
@@ -3995,8 +4183,8 @@ const editorCommand = {
3995
4183
  describe: 'Editor mode guided workflow',
3996
4184
  builder: (yargs) => {
3997
4185
  const stepDescription = IS_INTERNAL_BUILD
3998
- ? '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)'
3999
- : '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)';
4186
+ ? 'Step number (1-18) or subcommand (template, register, isolate, analyze-imports, dependents, audit, scenarios, scenario-coverage, recapture-stale, change, sync, debug, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors)'
4187
+ : 'Step number (1-18) or subcommand (template, register, isolate, analyze-imports, dependents, audit, scenarios, scenario-coverage, recapture-stale, change, sync, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors)';
4000
4188
  let builder = yargs
4001
4189
  .positional('step', {
4002
4190
  type: 'string',
@@ -4032,7 +4220,7 @@ const editorCommand = {
4032
4220
  builder = builder
4033
4221
  .option('target', {
4034
4222
  type: 'string',
4035
- describe: 'Debug target (setup, overview, overview-with-state, step-1..step-16, or comma-separated list)',
4223
+ describe: 'Debug target (setup, overview, overview-with-state, step-1..step-18, or comma-separated list)',
4036
4224
  })
4037
4225
  .option('resume', {
4038
4226
  type: 'boolean',
@@ -4076,6 +4264,12 @@ const editorCommand = {
4076
4264
  console.log(JSON.stringify(result.data, null, 2));
4077
4265
  }
4078
4266
  }
4267
+ // After a successful commit, remind Claude to continue to step 17
4268
+ if (argv.step === 'commit' && result.ok) {
4269
+ console.log();
4270
+ console.log(chalk.green('Commit done. Now run: ') +
4271
+ chalk.bold('codeyam editor 17'));
4272
+ }
4079
4273
  }
4080
4274
  catch (err) {
4081
4275
  console.error(chalk.red('Error: Could not reach the CodeYam server. Is it running?'));
@@ -4119,6 +4313,11 @@ const editorCommand = {
4119
4313
  await handleScenarioCoverage();
4120
4314
  return;
4121
4315
  }
4316
+ // Subcommand: codeyam editor recapture-stale
4317
+ if (argv.step === 'recapture-stale') {
4318
+ await handleRecaptureStale();
4319
+ return;
4320
+ }
4122
4321
  // Subcommand: codeyam editor change <feature>
4123
4322
  if (argv.step === 'change') {
4124
4323
  handleChange(argv.json || '');
@@ -4178,18 +4377,13 @@ const editorCommand = {
4178
4377
  }
4179
4378
  else {
4180
4379
  const state = readState(root);
4181
- // Clear prompt file when feature is done (step 16) so the hook
4182
- // can capture the next feature request from the user.
4183
- if (state?.step === 16) {
4184
- clearEditorUserPrompt(root);
4185
- }
4186
4380
  printCycleOverview(root, state);
4187
4381
  }
4188
4382
  return;
4189
4383
  }
4190
4384
  const step = argv.step ? parseInt(argv.step, 10) : undefined;
4191
- if (step != null && (isNaN(step) || step < 1 || step > 16)) {
4192
- console.error(chalk.red(`Error: Invalid step "${argv.step}". Must be 1-16.`));
4385
+ if (step != null && (isNaN(step) || step < 1 || step > 18)) {
4386
+ console.error(chalk.red(`Error: Invalid step "${argv.step}". Must be 1-18.`));
4193
4387
  process.exit(1);
4194
4388
  }
4195
4389
  if (step == null) {
@@ -4213,7 +4407,7 @@ const editorCommand = {
4213
4407
  const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
4214
4408
  const { projectSlug } = config;
4215
4409
  if (!projectSlug) {
4216
- errorLog('Missing project slug. Try reinitializing with: codeyam init --force');
4410
+ errorLog('Missing project slug. Try reinitializing with: `codeyam editor template`');
4217
4411
  return;
4218
4412
  }
4219
4413
  const connectionOk = await withoutSpinner(() => testEnvironment());
@@ -4650,14 +4844,16 @@ const editorCommand = {
4650
4844
  case 13:
4651
4845
  case 14:
4652
4846
  case 15:
4653
- case 16: {
4847
+ case 16:
4848
+ case 17:
4849
+ case 18: {
4654
4850
  const feature = argv.feature || state?.feature;
4655
4851
  if (!feature) {
4656
4852
  console.error(chalk.red('Error: No feature in progress. Run codeyam editor 1 first.'));
4657
4853
  process.exit(1);
4658
4854
  }
4659
- // Hard gate: steps 8+ require audit to have passed
4660
- if (step >= 8) {
4855
+ // Hard gate: steps 10+ require audit to have passed
4856
+ if (step >= 10) {
4661
4857
  const auditOk = await checkAuditGate();
4662
4858
  if (!auditOk) {
4663
4859
  // checkAuditGate() already printed specific failure details above
@@ -4681,6 +4877,8 @@ const editorCommand = {
4681
4877
  14: printStep14,
4682
4878
  15: printStep15,
4683
4879
  16: printStep16,
4880
+ 17: printStep17,
4881
+ 18: printStep18,
4684
4882
  };
4685
4883
  stepFns[step](root, feature);
4686
4884
  break;