@codeyam/codeyam-cli 0.1.0-staging.dbc742d → 0.1.0-staging.e057775

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 (55) hide show
  1. package/analyzer-template/.build-info.json +6 -6
  2. package/analyzer-template/log.txt +3 -3
  3. package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js +45 -0
  4. package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js.map +1 -0
  5. package/codeyam-cli/src/commands/editor.js +177 -80
  6. package/codeyam-cli/src/commands/editor.js.map +1 -1
  7. package/codeyam-cli/src/utils/__tests__/analyzerFinalization.test.js +144 -0
  8. package/codeyam-cli/src/utils/__tests__/analyzerFinalization.test.js.map +1 -0
  9. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +221 -1
  10. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
  11. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js +51 -1
  12. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js.map +1 -1
  13. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +14 -0
  14. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
  15. package/codeyam-cli/src/utils/analyzerFinalization.js +96 -0
  16. package/codeyam-cli/src/utils/analyzerFinalization.js.map +1 -0
  17. package/codeyam-cli/src/utils/editorAudit.js +17 -0
  18. package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
  19. package/codeyam-cli/src/utils/editorPreview.js +26 -0
  20. package/codeyam-cli/src/utils/editorPreview.js.map +1 -1
  21. package/codeyam-cli/src/utils/editorScenarios.js +4 -0
  22. package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
  23. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js +17 -0
  24. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js.map +1 -1
  25. package/codeyam-cli/src/webserver/app/lib/git.js +3 -2
  26. package/codeyam-cli/src/webserver/app/lib/git.js.map +1 -1
  27. package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-0DY_NKil.js → ScenarioViewer-Bd-hxofb.js} +1 -1
  28. package/codeyam-cli/src/webserver/build/client/assets/{dev.empty-Csi0_PMl.js → dev.empty-BsDh6TSF.js} +1 -1
  29. package/codeyam-cli/src/webserver/build/client/assets/editor-PBc_6L9R.js +10 -0
  30. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-4FzHlcNn.js +41 -0
  31. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-BF4oLwaE.js → entity._sha._-BsDXNp45.js} +1 -1
  32. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.dev-C7YX6r3H.js → entity._sha.scenarios._scenarioId.dev-BgAqUtTZ.js} +1 -1
  33. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-Bmshgrij.js +6 -0
  34. package/codeyam-cli/src/webserver/build/client/assets/globals-B8vTTNy2.css +1 -0
  35. package/codeyam-cli/src/webserver/build/client/assets/manifest-65850841.js +1 -0
  36. package/codeyam-cli/src/webserver/build/client/assets/{root-CHOdrM6Y.js → root-BwX8YgFb.js} +8 -8
  37. package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-BE43Hjti.js +1 -0
  38. package/codeyam-cli/src/webserver/build/server/assets/{index-BWoRb5RY.js → index-DEEQf4pi.js} +1 -1
  39. package/codeyam-cli/src/webserver/build/server/assets/{init-DbChSUQP.js → init-CkWmyFY2.js} +1 -1
  40. package/codeyam-cli/src/webserver/build/server/assets/server-build-BHi-9O8W.js +439 -0
  41. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  42. package/codeyam-cli/src/webserver/build-info.json +5 -5
  43. package/codeyam-cli/src/webserver/editorProxy.js +26 -4
  44. package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
  45. package/codeyam-cli/src/webserver/terminalServer.js +3 -3
  46. package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
  47. package/codeyam-cli/templates/editor-step-hook.py +11 -6
  48. package/package.json +1 -1
  49. package/codeyam-cli/src/webserver/build/client/assets/editor-BBAGP_mE.js +0 -10
  50. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-BLQMSKZa.js +0 -41
  51. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-CF164ouH.js +0 -6
  52. package/codeyam-cli/src/webserver/build/client/assets/globals-COUSHTyZ.css +0 -1
  53. package/codeyam-cli/src/webserver/build/client/assets/manifest-9c70d1f3.js +0 -1
  54. package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-CrAK28Bc.js +0 -1
  55. package/codeyam-cli/src/webserver/build/server/assets/server-build-BtbLQkKd.js +0 -433
@@ -1,10 +1,10 @@
1
1
  {
2
- "buildTimestamp": "2026-03-11T23:48:31.827Z",
3
- "buildTime": 1773272911827,
4
- "gitCommit": "dbc742d16ca0f4ce4f172093f89fa5dfdcaebd9e",
2
+ "buildTimestamp": "2026-03-12T17:48:53.671Z",
3
+ "buildTime": 1773337733671,
4
+ "gitCommit": "e05777544c54b403dfcde166b28b6ec249aec06a",
5
5
  "nodeVersion": "v20.20.0",
6
6
  "contentHash": "c6e453b5eb8471fdcb3e34085216f7d6523809504f12ddb1c6ee11665149d827",
7
- "buildNumber": 952,
8
- "semanticVersion": "0.1.952",
9
- "version": "0.1.952 (2026-03-11T23:48+c6e453b)"
7
+ "buildNumber": 962,
8
+ "semanticVersion": "0.1.962",
9
+ "version": "0.1.962 (2026-03-12T17:48+c6e453b)"
10
10
  }
@@ -1,7 +1,7 @@
1
1
 
2
- [3/11/2026, 11:48:31 PM] > codeyam-combo@1.0.0 mergeDependencies
3
- [3/11/2026, 11:48:31 PM] > node ./scripts/mergePackageJsonFiles.cjs
2
+ [3/12/2026, 5:48:53 PM] > codeyam-combo@1.0.0 mergeDependencies
3
+ [3/12/2026, 5:48:53 PM] > node ./scripts/mergePackageJsonFiles.cjs
4
4
 
5
5
 
6
- [3/11/2026, 11:48:31 PM] Merged dependencies into root package.json
6
+ [3/12/2026, 5:48:53 PM] Merged dependencies into root package.json
7
7
 
@@ -0,0 +1,45 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ /**
4
+ * Structural test: the CLI dispatch switch in editor.ts must have case labels
5
+ * for every step 1–16. Without this test, it's easy to add new steps
6
+ * (e.g. 14, 15, 16) to the printStep functions and STEP_LABELS but forget
7
+ * to add matching case labels to the switch statement — causing `codeyam editor N`
8
+ * to silently do nothing for the missing steps.
9
+ *
10
+ * This test reads the source file and parses the switch block that dispatches
11
+ * step numbers to their handlers.
12
+ */
13
+ describe('editor CLI step dispatch', () => {
14
+ const editorSource = fs.readFileSync(path.join(__dirname, '..', 'editor.ts'), 'utf8');
15
+ it('should have switch case labels for all steps 1–16', () => {
16
+ // Find the switch(step) block in the CLI dispatch section.
17
+ // There are two stepFns definitions in editor.ts:
18
+ // 1. Test/debug scenario generator (~line 4658) — uses a for-loop, not a switch
19
+ // 2. CLI dispatch (~line 5263) — uses switch(step) { case N: ... }
20
+ //
21
+ // We need to verify the second one (CLI dispatch) has cases for all 16 steps.
22
+ // Find all `case N:` labels in the file. The CLI dispatch switch is the
23
+ // only switch on `step` that uses numbered cases.
24
+ const switchMatch = editorSource.match(/switch\s*\(step\)\s*\{([\s\S]*?)\n\s{4}\}/);
25
+ expect(switchMatch).not.toBeNull();
26
+ const switchBody = switchMatch[1];
27
+ for (let step = 1; step <= 16; step++) {
28
+ const casePattern = new RegExp(`case\\s+${step}\\s*[:{]`);
29
+ expect(switchBody).toMatch(casePattern);
30
+ }
31
+ });
32
+ it('should have STEP_LABELS entries for all steps 1–16', () => {
33
+ for (let step = 1; step <= 16; step++) {
34
+ const labelPattern = new RegExp(`${step}:\\s*'[^']+'`);
35
+ expect(editorSource).toMatch(labelPattern);
36
+ }
37
+ });
38
+ it('should have printStep functions defined for all steps 1–16', () => {
39
+ for (let step = 1; step <= 16; step++) {
40
+ const fnPattern = new RegExp(`function\\s+printStep${step}\\s*\\(`);
41
+ expect(editorSource).toMatch(fnPattern);
42
+ }
43
+ });
44
+ });
45
+ //# sourceMappingURL=editor.stepDispatch.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editor.stepDispatch.test.js","sourceRoot":"","sources":["../../../../../src/commands/__tests__/editor.stepDispatch.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB;;;;;;;;;GASG;AACH,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,MAAM,YAAY,GAAG,EAAE,CAAC,YAAY,CAClC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,WAAW,CAAC,EACvC,MAAM,CACP,CAAC;IAEF,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,2DAA2D;QAC3D,kDAAkD;QAClD,kFAAkF;QAClF,qEAAqE;QACrE,EAAE;QACF,8EAA8E;QAE9E,wEAAwE;QACxE,kDAAkD;QAClD,MAAM,WAAW,GAAG,YAAY,CAAC,KAAK,CACpC,2CAA2C,CAC5C,CAAC;QACF,MAAM,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QAEnC,MAAM,UAAU,GAAG,WAAY,CAAC,CAAC,CAAC,CAAC;QAEnC,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC;YACtC,MAAM,WAAW,GAAG,IAAI,MAAM,CAAC,WAAW,IAAI,UAAU,CAAC,CAAC;YAC1D,MAAM,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC;YACtC,MAAM,YAAY,GAAG,IAAI,MAAM,CAAC,GAAG,IAAI,cAAc,CAAC,CAAC;YACvD,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC;YACtC,MAAM,SAAS,GAAG,IAAI,MAAM,CAAC,wBAAwB,IAAI,SAAS,CAAC,CAAC;YACpE,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -11,7 +11,7 @@ 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";
@@ -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
@@ -161,13 +164,13 @@ function stepHeader(step, title, feature) {
161
164
  console.log();
162
165
  }
163
166
  /**
164
- * Print a colored progress tracker showing all 13 steps.
167
+ * Print a colored progress tracker showing all 16 steps.
165
168
  * Steps before `current` are green ✓, `current` is bold cyan →, future steps are dim ○.
166
169
  */
167
170
  function printProgressTracker(current) {
168
171
  console.log();
169
172
  console.log(chalk.dim(' ┌─────────────────────────────────────┐'));
170
- for (let i = 1; i <= 13; i++) {
173
+ for (let i = 1; i <= 16; i++) {
171
174
  const label = STEP_LABELS[i];
172
175
  const num = i < 10 ? ` ${i}` : `${i}`;
173
176
  const content = `${num}. ${label.padEnd(28)}`;
@@ -205,7 +208,7 @@ function stopGate(current, opts) {
205
208
  console.log(chalk.yellow('For the CURRENT step (→), show each checklist item with ✓ (done) or ✗ (skipped + reason).'));
206
209
  console.log(chalk.yellow('If any items are ✗, explain why and ask if the user wants to address them.'));
207
210
  console.log();
208
- if (current < 13) {
211
+ if (current < 16) {
209
212
  console.log(chalk.green('When done, run: ') +
210
213
  chalk.bold(`codeyam editor ${current + 1}`));
211
214
  }
@@ -249,6 +252,14 @@ function printResumptionHeader(step) {
249
252
  ],
250
253
  12: ['Re-verify is safe to repeat — just re-run the checks'],
251
254
  13: ['Check if commit already made:\n `git log --oneline -3`'],
255
+ 14: ['Check if commit already made:\n `git log --oneline -3`'],
256
+ 15: [
257
+ 'Check if journal was already updated with commit SHA:\n `codeyam editor journal-list`',
258
+ 'Check if commit was already amended:\n `git log --oneline -3`',
259
+ ],
260
+ 16: [
261
+ 'Check if already pushed:\n `git remote -v && git log --oneline origin/HEAD..HEAD 2>/dev/null`',
262
+ ],
252
263
  };
253
264
  const label = STEP_LABELS[step] || 'Unknown';
254
265
  console.log(chalk.bold.yellow(`━━━ RESUMING Step ${step}: ${label} ━━━`));
@@ -353,7 +364,7 @@ function parseDebugTargets(target) {
353
364
  }
354
365
  if (entry.startsWith('step-')) {
355
366
  const num = parseInt(entry.replace('step-', ''), 10);
356
- if (!isNaN(num) && num >= 1 && num <= 13) {
367
+ if (!isNaN(num) && num >= 1 && num <= 16) {
357
368
  valid.add(`step-${num}`);
358
369
  continue;
359
370
  }
@@ -541,7 +552,7 @@ function printCycleOverview(root, state) {
541
552
  console.log(chalk.yellow('This applies even after committing — always use the change workflow.'));
542
553
  }
543
554
  else {
544
- console.log('Each feature follows 13 steps. You MUST run each command in order:');
555
+ console.log('Each feature follows 16 steps. You MUST run each command in order:');
545
556
  console.log();
546
557
  console.log(` ${chalk.bold.yellow(' 1')} ${chalk.bold('Plan')} — Plan the feature, confirm with user`);
547
558
  console.log(` ${chalk.bold.yellow(' 2')} ${chalk.bold('Prototype')} — Build a working prototype fast`);
@@ -556,6 +567,9 @@ function printCycleOverview(root, state) {
556
567
  console.log(` ${chalk.bold.yellow('11')} ${chalk.bold('Journal')} — Create/update journal entry`);
557
568
  console.log(` ${chalk.bold.yellow('12')} ${chalk.bold('Review')} — Verify screenshots and audit`);
558
569
  console.log(` ${chalk.bold.yellow('13')} ${chalk.bold('Present')} — Present summary, get approval`);
570
+ console.log(` ${chalk.bold.yellow('14')} ${chalk.bold('Commit')} — Commit all changes`);
571
+ console.log(` ${chalk.bold.yellow('15')} ${chalk.bold('Finalize')} — Journal update + amend commit`);
572
+ console.log(` ${chalk.bold.yellow('16')} ${chalk.bold('Push')} — Push to remote`);
559
573
  console.log();
560
574
  console.log(chalk.green('Start now: ') + chalk.bold('codeyam editor 1'));
561
575
  console.log(chalk.dim(' If the user already described what they want, pass it: codeyam editor 1 --prompt "their message"'));
@@ -570,19 +584,19 @@ function printStep1(root, feature, options, userPrompt) {
570
584
  if (!isResuming) {
571
585
  clearState(root);
572
586
  }
573
- // If feature is provided, save initial state so step 2 can pick it up
574
- if (feature) {
575
- const now = new Date().toISOString();
576
- writeState(root, {
577
- feature,
578
- step: 1,
579
- label: STEP_LABELS[1],
580
- startedAt: isResuming ? prevState.startedAt : now,
581
- featureStartedAt: isResuming ? prevState.featureStartedAt : now,
582
- appFormats: options?.appFormats || prevState?.appFormats,
583
- techStackId: options?.techStackId || prevState?.techStackId,
584
- });
585
- }
587
+ // Always persist state so step 2's validation sees that step 1 ran.
588
+ // The feature name may not be known yet (it's an output of planning)
589
+ // use an empty string as placeholder; step 2 requires --feature anyway.
590
+ const now = new Date().toISOString();
591
+ writeState(root, {
592
+ feature: feature || prevState?.feature || '',
593
+ step: 1,
594
+ label: STEP_LABELS[1],
595
+ startedAt: isResuming ? prevState.startedAt : now,
596
+ featureStartedAt: isResuming ? prevState.featureStartedAt : now,
597
+ appFormats: options?.appFormats || prevState?.appFormats,
598
+ techStackId: options?.techStackId || prevState?.techStackId,
599
+ });
586
600
  // Save the user's original prompt to a separate file
587
601
  if (userPrompt) {
588
602
  const promptPath = path.join(root, '.codeyam', 'editor-user-prompt.txt');
@@ -818,7 +832,7 @@ function printStep3(root, feature) {
818
832
  console.log(chalk.bold('Present a selection menu to the user (use AskUserQuestion with these EXACT option labels):'));
819
833
  console.log(chalk.green(' Option 1 label: "The live preview is displaying what I expected"') + chalk.dim(' — proceed to step 4'));
820
834
  console.log(chalk.yellow(' Option 2 label: "I\'d like some changes"') +
821
- chalk.dim(' — user describes changes, you make them, then re-present'));
835
+ chalk.dim(' — make changes, refresh preview, re-run `codeyam editor 3`'));
822
836
  console.log();
823
837
  console.log(chalk.dim('Wait for user approval before moving on. Refactoring and scenarios happen in later steps.'));
824
838
  stopGate(3, { confirm: true });
@@ -1325,32 +1339,98 @@ function printStep13(root, feature) {
1325
1339
  chalk.dim(' — describe changes, then re-verify'));
1326
1340
  console.log();
1327
1341
  console.log(chalk.bold('If the user chooses "Save & commit":'));
1328
- checkbox(`Hide the results panel first: \`codeyam editor hide-results\``);
1342
+ checkbox('Advance to the commit step: `codeyam editor 14`');
1343
+ console.log();
1344
+ console.log(chalk.bold('If the user chooses "Make changes" (or asks for ANY change, even as a question):'));
1345
+ checkbox(`Hide the results panel: \`codeyam editor hide-results\``);
1346
+ checkbox('Ask what changes the user wants (if not already clear)');
1347
+ checkbox(`Run: \`codeyam editor change "${feature}"\` — this gives you the change checklist`);
1348
+ checkbox('THEN make the requested changes and follow the checklist');
1349
+ console.log(chalk.red.bold(' IMPORTANT: Always run the change command BEFORE writing any code.'));
1350
+ stopGate(13, { confirm: true });
1351
+ }
1352
+ // ─── Step 14: Commit ─────────────────────────────────────────────────
1353
+ function printStep14(root, feature) {
1354
+ const prevState = readState(root);
1355
+ const isResuming = prevState?.step === 14;
1356
+ const now = new Date().toISOString();
1357
+ writeState(root, {
1358
+ feature,
1359
+ step: 14,
1360
+ label: STEP_LABELS[14],
1361
+ startedAt: isResuming ? prevState.startedAt : now,
1362
+ featureStartedAt: prevState?.featureStartedAt || now,
1363
+ });
1364
+ logEvent(root, 'step', { step: 14, label: 'Commit', feature });
1365
+ stepHeader(14, 'Commit', feature);
1366
+ if (isResuming) {
1367
+ printResumptionHeader(14);
1368
+ }
1369
+ console.log('Commit all changes for this feature.');
1370
+ console.log();
1371
+ console.log(chalk.bold('Checklist:'));
1372
+ checkbox(`Hide the results panel: \`codeyam editor hide-results\``);
1329
1373
  checkbox(`Git commit using the journal description: \`codeyam editor commit '{"message":"feat: <title>\\n\\n<journal description>"}'\``);
1330
1374
  console.log(chalk.dim(' The commit message body MUST match the journal description exactly'));
1375
+ stopGate(14);
1376
+ }
1377
+ // ─── Step 15: Finalize ───────────────────────────────────────────────
1378
+ function printStep15(root, feature) {
1379
+ const prevState = readState(root);
1380
+ const isResuming = prevState?.step === 15;
1381
+ const now = new Date().toISOString();
1382
+ writeState(root, {
1383
+ feature,
1384
+ step: 15,
1385
+ label: STEP_LABELS[15],
1386
+ startedAt: isResuming ? prevState.startedAt : now,
1387
+ featureStartedAt: prevState?.featureStartedAt || now,
1388
+ });
1389
+ logEvent(root, 'step', { step: 15, label: 'Finalize', feature });
1390
+ stepHeader(15, 'Finalize', feature);
1391
+ if (isResuming) {
1392
+ printResumptionHeader(15);
1393
+ }
1394
+ console.log('Update the journal with the commit SHA and amend the commit.');
1395
+ console.log();
1396
+ console.log(chalk.bold('Checklist:'));
1331
1397
  checkbox(`Update journal with commit SHA: \`codeyam editor journal-update '{"time":"<journal entry time>","commitSha":"<sha>","commitMessage":"feat: <title>"}'\``);
1332
1398
  checkbox('Amend the commit to include the journal update: `git add .codeyam/journal/ && git commit --amend --no-edit`');
1333
1399
  console.log(chalk.dim(' The journal-update modifies journal files after the commit — amend to keep the tree clean.'));
1400
+ stopGate(15);
1401
+ }
1402
+ // ─── Step 16: Push ───────────────────────────────────────────────────
1403
+ function printStep16(root, feature) {
1404
+ const prevState = readState(root);
1405
+ const isResuming = prevState?.step === 16;
1406
+ const now = new Date().toISOString();
1407
+ writeState(root, {
1408
+ feature,
1409
+ step: 16,
1410
+ label: STEP_LABELS[16],
1411
+ startedAt: isResuming ? prevState.startedAt : now,
1412
+ featureStartedAt: prevState?.featureStartedAt || now,
1413
+ });
1414
+ logEvent(root, 'step', { step: 16, label: 'Push', feature });
1415
+ stepHeader(16, 'Push', feature);
1416
+ if (isResuming) {
1417
+ printResumptionHeader(16);
1418
+ }
1419
+ console.log('Push the commit to the remote repository.');
1420
+ console.log();
1421
+ console.log(chalk.bold('Checklist:'));
1334
1422
  checkbox('Check if a git remote is configured: `git remote -v`');
1335
- console.log(chalk.dim(' If a remote exists, ask the user: "Would you like me to push this commit to the remote?"'));
1423
+ checkbox("Offer to push to remote (AskUserQuestion STOP and wait for the user's answer before proceeding):");
1424
+ 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"'));
1336
1425
  console.log(chalk.dim(' If the user says yes, run: `git push`'));
1337
- console.log(chalk.dim(' If NO remote exists, ask: "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."'));
1426
+ 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"'));
1338
1427
  console.log(chalk.dim(' If the user wants help, guide them through `gh repo create` or manual GitHub setup, then push.'));
1339
- console.log(chalk.green(' Then run: ') +
1340
- chalk.bold('codeyam editor steps') +
1341
- chalk.green(' to start the next feature'));
1428
+ checkbox('After the user responds, run: `codeyam editor steps` to start the next feature');
1342
1429
  console.log();
1343
1430
  console.log(chalk.red.bold(' If the user reports a bug or requests a fix after committing:'));
1344
1431
  console.log(chalk.red.bold(' You MUST still run `codeyam editor change` before making any changes.'));
1345
1432
  console.log(chalk.red.bold(' The change workflow applies to ALL changes — post-commit fixes are not an exception.'));
1346
- console.log();
1347
- console.log(chalk.bold('If the user chooses "Make changes" (or asks for ANY change, even as a question):'));
1348
- checkbox(`Hide the results panel: \`codeyam editor hide-results\``);
1349
- checkbox('Ask what changes the user wants (if not already clear)');
1350
- checkbox(`Run: \`codeyam editor change "${feature}"\` — this gives you the change checklist`);
1351
- checkbox('THEN make the requested changes and follow the checklist');
1352
- console.log(chalk.red.bold(' IMPORTANT: Always run the change command BEFORE writing any code.'));
1353
- stopGate(13, { confirm: true });
1433
+ stopGate(16, { confirm: true });
1354
1434
  }
1355
1435
  // ─── Command definition ───────────────────────────────────────────────
1356
1436
  // ─── Analyze-imports subcommand ────────────────────────────────────────
@@ -1876,15 +1956,15 @@ async function handleDependents(entityName) {
1876
1956
  * `codeyam editor change <feature>`
1877
1957
  *
1878
1958
  * Prints a condensed post-change checklist that guides Claude through
1879
- * re-verifying after user-requested modifications. This is the "change
1880
- * loop" it replaces the freeform "make changes" path with structured
1881
- * instructions that always loop back to step 13 present.
1959
+ * re-verifying after user-requested modifications. When called from
1960
+ * step 13+, this loops back to step 13 (present). When called from an
1961
+ * earlier step, it returns to that step so the normal flow continues.
1882
1962
  */
1883
1963
  function handleChange(feature) {
1884
1964
  const root = getProjectRoot();
1965
+ const state = readState(root);
1885
1966
  if (!feature) {
1886
1967
  // Try to read feature from state
1887
- const state = readState(root);
1888
1968
  if (state?.feature) {
1889
1969
  feature = state.feature;
1890
1970
  }
@@ -1894,6 +1974,7 @@ function handleChange(feature) {
1894
1974
  process.exit(1);
1895
1975
  }
1896
1976
  }
1977
+ const currentStep = state?.step ?? 13;
1897
1978
  const port = getServerPort();
1898
1979
  console.log();
1899
1980
  console.log(chalk.bold.cyan('━━━ Change Loop ━━━'));
@@ -1961,12 +2042,24 @@ function handleChange(feature) {
1961
2042
  console.log(chalk.dim(' Always update the existing uncommitted entry — do NOT create a new one.'));
1962
2043
  console.log(chalk.dim(' Only create a new entry (POST) if no uncommitted entry exists for this feature.'));
1963
2044
  console.log();
1964
- console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
1965
- console.log(chalk.red.bold(' REQUIRED: Show Results'));
1966
- console.log(chalk.red.bold(' When all checks pass, you MUST run: ') +
1967
- chalk.bold(`codeyam editor 13`));
1968
- console.log(chalk.red.bold(' The user ALWAYS expects to see results after changes. DO NOT skip this step.'));
1969
- console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
2045
+ // If the change was initiated from a step before 13, return to that step
2046
+ // instead of jumping to step 13. The change workflow should only loop to
2047
+ // step 13 when changes are requested FROM step 13.
2048
+ if (currentStep < 13) {
2049
+ console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
2050
+ console.log(chalk.red.bold(' REQUIRED: Return to current step'));
2051
+ console.log(chalk.red.bold(' When all checks pass, you MUST run: ') +
2052
+ chalk.bold(`codeyam editor ${currentStep}`));
2053
+ console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
2054
+ }
2055
+ else {
2056
+ console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
2057
+ console.log(chalk.red.bold(' REQUIRED: Show Results'));
2058
+ console.log(chalk.red.bold(' When all checks pass, you MUST run: ') +
2059
+ chalk.bold(`codeyam editor 13`));
2060
+ console.log(chalk.red.bold(' The user ALWAYS expects to see results after changes. DO NOT skip this step.'));
2061
+ console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
2062
+ }
1970
2063
  console.log();
1971
2064
  }
1972
2065
  // ─── Audit gate ─────────────────────────────────────────────────────
@@ -2359,6 +2452,9 @@ async function handleTemplate() {
2359
2452
  // Config parse error is non-fatal
2360
2453
  }
2361
2454
  }
2455
+ // 5b. Write a fresh prototypeId so the proxy clears stale localStorage
2456
+ const activeScenarioPath = path.join(root, '.codeyam', 'active-scenario.json');
2457
+ fs.writeFileSync(activeScenarioPath, JSON.stringify({ prototypeId: Date.now().toString() }), 'utf-8');
2362
2458
  // 6. Trigger editor-refresh so the server picks up the new project
2363
2459
  console.log(chalk.bold('Refreshing editor...'));
2364
2460
  try {
@@ -2570,8 +2666,11 @@ function handleEditorDebug(args) {
2570
2666
  11: printStep11,
2571
2667
  12: printStep12,
2572
2668
  13: printStep13,
2669
+ 14: printStep14,
2670
+ 15: printStep15,
2671
+ 16: printStep16,
2573
2672
  };
2574
- for (let step = 1; step <= 13; step++) {
2673
+ for (let step = 1; step <= 16; step++) {
2575
2674
  const stepId = `step-${step}`;
2576
2675
  if (!wants(stepId))
2577
2676
  continue;
@@ -2650,8 +2749,8 @@ const editorCommand = {
2650
2749
  describe: 'Editor mode guided workflow',
2651
2750
  builder: (yargs) => {
2652
2751
  const stepDescription = IS_INTERNAL_BUILD
2653
- ? 'Step number (1-13) 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)'
2654
- : 'Step number (1-13) 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)';
2752
+ ? '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)'
2753
+ : '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)';
2655
2754
  let builder = yargs
2656
2755
  .positional('step', {
2657
2756
  type: 'string',
@@ -2687,7 +2786,7 @@ const editorCommand = {
2687
2786
  builder = builder
2688
2787
  .option('target', {
2689
2788
  type: 'string',
2690
- describe: 'Debug target (setup, overview, overview-with-state, step-1..step-13, or comma-separated list)',
2789
+ describe: 'Debug target (setup, overview, overview-with-state, step-1..step-16, or comma-separated list)',
2691
2790
  })
2692
2791
  .option('resume', {
2693
2792
  type: 'boolean',
@@ -2819,9 +2918,9 @@ const editorCommand = {
2819
2918
  }
2820
2919
  else {
2821
2920
  const state = readState(root);
2822
- // Clear prompt file when feature is done (step 13) so the hook
2921
+ // Clear prompt file when feature is done (step 16) so the hook
2823
2922
  // can capture the next feature request from the user.
2824
- if (state?.step === 13) {
2923
+ if (state?.step === 16) {
2825
2924
  clearEditorUserPrompt(root);
2826
2925
  }
2827
2926
  printCycleOverview(root, state);
@@ -2829,8 +2928,8 @@ const editorCommand = {
2829
2928
  return;
2830
2929
  }
2831
2930
  const step = argv.step ? parseInt(argv.step, 10) : undefined;
2832
- if (step != null && (isNaN(step) || step < 1 || step > 13)) {
2833
- console.error(chalk.red(`Error: Invalid step "${argv.step}". Must be 1-13.`));
2931
+ if (step != null && (isNaN(step) || step < 1 || step > 16)) {
2932
+ console.error(chalk.red(`Error: Invalid step "${argv.step}". Must be 1-16.`));
2834
2933
  process.exit(1);
2835
2934
  }
2836
2935
  if (step == null) {
@@ -2970,29 +3069,15 @@ const editorCommand = {
2970
3069
  editorMode,
2971
3070
  });
2972
3071
  // Auto-finalize analyzer so codeyam analyze works
2973
- if (editorMode && !isAnalyzerFinalized()) {
2974
- try {
2975
- const { execSync } = await import('child_process');
2976
- const templatePath = getAnalyzerTemplatePath();
2977
- if (fs.existsSync(templatePath)) {
2978
- console.log(' Setting up simulations (first time only)...');
2979
- execSync('npm install --include=dev', {
2980
- cwd: templatePath,
2981
- stdio: 'pipe',
2982
- });
2983
- execSync('npx playwright install chromium', {
2984
- cwd: templatePath,
2985
- stdio: 'pipe',
2986
- });
2987
- execSync('npm run build', {
2988
- cwd: templatePath,
2989
- stdio: 'pipe',
2990
- });
2991
- fs.writeFileSync(path.join(templatePath, '.finalized'), new Date().toISOString());
2992
- }
3072
+ if (editorMode) {
3073
+ const finalization = ensureAnalyzerFinalized();
3074
+ if (finalization.needed) {
3075
+ console.log(` Setting up simulations (${finalization.stepsRun.join(', ')})...`);
2993
3076
  }
2994
- catch {
2995
- // Non-fatal
3077
+ if (finalization.errors.length > 0) {
3078
+ for (const err of finalization.errors) {
3079
+ console.warn(` Warning: ${err.step} failed: ${err.message}`);
3080
+ }
2996
3081
  }
2997
3082
  }
2998
3083
  // Start background server (handles killing existing servers internally)
@@ -3043,11 +3128,17 @@ const editorCommand = {
3043
3128
  return;
3044
3129
  }
3045
3130
  const state = readState(root);
3046
- // Validate step transition — prevent skipping steps
3047
- const stepError = validateStepTransition(step, state?.step ?? null);
3048
- if (stepError) {
3049
- console.error(chalk.red(`Error: ${stepError}`));
3050
- process.exit(1);
3131
+ // Validate step transition — prevent skipping steps.
3132
+ // Exception: step 2 with --feature is always allowed because step 1's
3133
+ // instructions explicitly tell Claude to run `codeyam editor 2 --feature "..."`.
3134
+ // Step 1 is planning-only and may not persist state (no --feature flag).
3135
+ const skipValidation = step === 2 && argv.feature;
3136
+ if (!skipValidation) {
3137
+ const stepError = validateStepTransition(step, state?.step ?? null);
3138
+ if (stepError) {
3139
+ console.error(chalk.red(`Error: ${stepError}`));
3140
+ process.exit(1);
3141
+ }
3051
3142
  }
3052
3143
  switch (step) {
3053
3144
  case 1: {
@@ -3080,7 +3171,10 @@ const editorCommand = {
3080
3171
  case 10:
3081
3172
  case 11:
3082
3173
  case 12:
3083
- case 13: {
3174
+ case 13:
3175
+ case 14:
3176
+ case 15:
3177
+ case 16: {
3084
3178
  const feature = argv.feature || state?.feature;
3085
3179
  if (!feature) {
3086
3180
  console.error(chalk.red('Error: No feature in progress. Run codeyam editor 1 first.'));
@@ -3107,6 +3201,9 @@ const editorCommand = {
3107
3201
  11: printStep11,
3108
3202
  12: printStep12,
3109
3203
  13: printStep13,
3204
+ 14: printStep14,
3205
+ 15: printStep15,
3206
+ 16: printStep16,
3110
3207
  };
3111
3208
  stepFns[step](root, feature);
3112
3209
  break;