@codeyam/codeyam-cli 0.1.0-staging.a890816 → 0.1.0-staging.ae0de75

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 (106) hide show
  1. package/analyzer-template/.build-info.json +6 -6
  2. package/analyzer-template/log.txt +3 -3
  3. package/analyzer-template/package.json +6 -6
  4. package/analyzer-template/packages/ai/package.json +1 -1
  5. package/analyzer-template/packages/aws/package.json +1 -1
  6. package/analyzer-template/packages/database/package.json +3 -3
  7. package/analyzer-template/packages/github/package.json +1 -1
  8. package/codeyam-cli/src/commands/editor.js +892 -90
  9. package/codeyam-cli/src/commands/editor.js.map +1 -1
  10. package/codeyam-cli/src/commands/init.js +6 -1
  11. package/codeyam-cli/src/commands/init.js.map +1 -1
  12. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +246 -0
  13. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -0
  14. package/codeyam-cli/src/utils/__tests__/editorDevServer.test.js +126 -0
  15. package/codeyam-cli/src/utils/__tests__/editorDevServer.test.js.map +1 -0
  16. package/codeyam-cli/src/utils/__tests__/editorJournal.test.js +295 -0
  17. package/codeyam-cli/src/utils/__tests__/editorJournal.test.js.map +1 -0
  18. package/codeyam-cli/src/utils/__tests__/editorMockState.test.js +270 -0
  19. package/codeyam-cli/src/utils/__tests__/editorMockState.test.js.map +1 -0
  20. package/codeyam-cli/src/utils/__tests__/editorPreloadHelpers.test.js +100 -0
  21. package/codeyam-cli/src/utils/__tests__/editorPreloadHelpers.test.js.map +1 -0
  22. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js +147 -0
  23. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js.map +1 -0
  24. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +76 -0
  25. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -0
  26. package/codeyam-cli/src/utils/__tests__/git.editor.test.js +134 -0
  27. package/codeyam-cli/src/utils/__tests__/git.editor.test.js.map +1 -0
  28. package/codeyam-cli/src/utils/__tests__/project.test.js +65 -0
  29. package/codeyam-cli/src/utils/__tests__/project.test.js.map +1 -0
  30. package/codeyam-cli/src/utils/__tests__/scenarioMarkers.test.js +121 -0
  31. package/codeyam-cli/src/utils/__tests__/scenarioMarkers.test.js.map +1 -0
  32. package/codeyam-cli/src/utils/buildFlags.js +4 -0
  33. package/codeyam-cli/src/utils/buildFlags.js.map +1 -0
  34. package/codeyam-cli/src/utils/editorAudit.js +82 -0
  35. package/codeyam-cli/src/utils/editorAudit.js.map +1 -0
  36. package/codeyam-cli/src/utils/editorDevServer.js +98 -0
  37. package/codeyam-cli/src/utils/editorDevServer.js.map +1 -0
  38. package/codeyam-cli/src/utils/editorJournal.js +137 -0
  39. package/codeyam-cli/src/utils/editorJournal.js.map +1 -0
  40. package/codeyam-cli/src/utils/editorMockState.js +248 -0
  41. package/codeyam-cli/src/utils/editorMockState.js.map +1 -0
  42. package/codeyam-cli/src/utils/editorPreloadHelpers.js +64 -0
  43. package/codeyam-cli/src/utils/editorPreloadHelpers.js.map +1 -0
  44. package/codeyam-cli/src/utils/editorPreview.js +66 -0
  45. package/codeyam-cli/src/utils/editorPreview.js.map +1 -0
  46. package/codeyam-cli/src/utils/editorScenarios.js +56 -0
  47. package/codeyam-cli/src/utils/editorScenarios.js.map +1 -0
  48. package/codeyam-cli/src/utils/git.js +51 -0
  49. package/codeyam-cli/src/utils/git.js.map +1 -1
  50. package/codeyam-cli/src/utils/install-skills.js +28 -17
  51. package/codeyam-cli/src/utils/install-skills.js.map +1 -1
  52. package/codeyam-cli/src/utils/project.js +15 -5
  53. package/codeyam-cli/src/utils/project.js.map +1 -1
  54. package/codeyam-cli/src/utils/scenarioMarkers.js +134 -0
  55. package/codeyam-cli/src/utils/scenarioMarkers.js.map +1 -0
  56. package/codeyam-cli/src/utils/testRunner.js +1 -1
  57. package/codeyam-cli/src/utils/testRunner.js.map +1 -1
  58. package/codeyam-cli/src/webserver/build/client/assets/Terminal-wkqC0AQk.js +41 -0
  59. package/codeyam-cli/src/webserver/build/client/assets/api.editor-audit-l0sNRNKZ.js +1 -0
  60. package/codeyam-cli/src/webserver/build/client/assets/api.editor-load-commit-l0sNRNKZ.js +1 -0
  61. package/codeyam-cli/src/webserver/build/client/assets/editor-CdjF_fX6.js +8 -0
  62. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-D8ILZMR0.js +6 -0
  63. package/codeyam-cli/src/webserver/build/client/assets/globals-B17TBSS6.css +1 -0
  64. package/codeyam-cli/src/webserver/build/client/assets/manifest-b8fd6b07.js +1 -0
  65. package/codeyam-cli/src/webserver/build/client/assets/{root-DiRdBreB.js → root-DUKqhFlb.js} +7 -7
  66. package/codeyam-cli/src/webserver/build/client/assets/xterm-BqvuqXEL.js +27 -0
  67. package/codeyam-cli/src/webserver/build/server/assets/{index-BzAbACSx.js → index-BLhjL9Xi.js} +1 -1
  68. package/codeyam-cli/src/webserver/build/server/assets/server-build-DyMuI5mU.js +363 -0
  69. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  70. package/codeyam-cli/src/webserver/build-info.json +5 -5
  71. package/codeyam-cli/src/webserver/editorProxy.js +182 -14
  72. package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
  73. package/codeyam-cli/src/webserver/scripts/codeyam-preload.mjs +175 -0
  74. package/codeyam-cli/src/webserver/server.js +61 -12
  75. package/codeyam-cli/src/webserver/server.js.map +1 -1
  76. package/codeyam-cli/src/webserver/terminalServer.js +29 -103
  77. package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
  78. package/codeyam-cli/templates/editor-step-hook.py +6 -4
  79. package/codeyam-cli/templates/{codeyam-dev-mode.md → skills/codeyam-dev-mode/SKILL.md} +1 -1
  80. package/codeyam-cli/templates/{codeyam-editor.md → skills/codeyam-editor/SKILL.md} +5 -4
  81. package/codeyam-cli/templates/{codeyam-memory.md → skills/codeyam-memory/SKILL.md} +215 -0
  82. package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/deprecated-prompt.md +100 -0
  83. package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/detect-deprecated-patterns.sh +108 -0
  84. package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/find-exports.sh +69 -0
  85. package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/misleading-api-prompt.md +117 -0
  86. package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/analyze-prompt.md +46 -0
  87. package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/cleanup.sh +12 -0
  88. package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/filter.jq +45 -0
  89. package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/preprocess.sh +139 -0
  90. package/package.json +2 -2
  91. package/scripts/npm-post-install.cjs +12 -0
  92. package/codeyam-cli/src/webserver/build/client/assets/Terminal-CcG8YTLx.js +0 -41
  93. package/codeyam-cli/src/webserver/build/client/assets/editor-W_IGJ2Kd.js +0 -7
  94. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-D6SEzMCu.js +0 -6
  95. package/codeyam-cli/src/webserver/build/client/assets/globals-BZB_H1w2.css +0 -1
  96. package/codeyam-cli/src/webserver/build/client/assets/manifest-8daa4147.js +0 -1
  97. package/codeyam-cli/src/webserver/build/client/assets/xterm-DMSzMhqy.js +0 -9
  98. package/codeyam-cli/src/webserver/build/server/assets/server-build-OdUocH6P.js +0 -362
  99. package/scripts/finalize-analyzer.cjs +0 -13
  100. /package/codeyam-cli/templates/{codeyam-diagnose.md → commands/codeyam-diagnose.md} +0 -0
  101. /package/codeyam-cli/templates/{codeyam-debug.md → skills/codeyam-debug/SKILL.md} +0 -0
  102. /package/codeyam-cli/templates/{codeyam-new-rule.md → skills/codeyam-new-rule/SKILL.md} +0 -0
  103. /package/codeyam-cli/templates/{codeyam-setup.md → skills/codeyam-setup/SKILL.md} +0 -0
  104. /package/codeyam-cli/templates/{codeyam-sim.md → skills/codeyam-sim/SKILL.md} +0 -0
  105. /package/codeyam-cli/templates/{codeyam-test.md → skills/codeyam-test/SKILL.md} +0 -0
  106. /package/codeyam-cli/templates/{codeyam-verify.md → skills/codeyam-verify/SKILL.md} +0 -0
@@ -1,11 +1,13 @@
1
1
  import * as fs from 'fs';
2
2
  import * as path from 'path';
3
+ import * as os from 'os';
3
4
  import { fileURLToPath } from 'url';
4
5
  import chalk from 'chalk';
5
6
  import { runAnalysisForEntities } from "../utils/analysisRunner.js";
6
7
  import { ProgressReporter } from "../utils/progress.js";
7
8
  import { initializeEnvironment } from "../utils/database.js";
8
9
  import { loadEntities, loadAnalyses } from "../../../packages/database/index.js";
10
+ import { IS_INTERNAL_BUILD } from "../utils/buildFlags.js";
9
11
  const __filename = fileURLToPath(import.meta.url);
10
12
  const __dirname = path.dirname(__filename);
11
13
  const STEP_LABELS = {
@@ -19,7 +21,8 @@ const STEP_LABELS = {
19
21
  8: 'App Scenarios',
20
22
  9: 'User Scenarios',
21
23
  10: 'Verify',
22
- 11: 'Review',
24
+ 11: 'Journal',
25
+ 12: 'Review',
23
26
  };
24
27
  /**
25
28
  * Append a JSONL log entry to .codeyam/logs/editor-log.jsonl
@@ -125,7 +128,7 @@ function stepHeader(step, title, feature) {
125
128
  function printProgressTracker(current) {
126
129
  console.log();
127
130
  console.log(chalk.dim('┌─────────────────────────────────────┐'));
128
- for (let i = 1; i <= 11; i++) {
131
+ for (let i = 1; i <= 12; i++) {
129
132
  const label = STEP_LABELS[i];
130
133
  const num = i < 10 ? ` ${i}` : `${i}`;
131
134
  const content = `${num}. ${label.padEnd(28)}`;
@@ -163,7 +166,7 @@ function stopGate(current, opts) {
163
166
  console.log(chalk.yellow('For the CURRENT step (→), show each checklist item with ✓ (done) or ✗ (skipped + reason).'));
164
167
  console.log(chalk.yellow('If any items are ✗, explain why and ask if the user wants to address them.'));
165
168
  console.log();
166
- if (current < 10) {
169
+ if (current < 12) {
167
170
  console.log(chalk.green('When done, run: ') +
168
171
  chalk.bold(`codeyam editor ${current + 1}`));
169
172
  }
@@ -174,6 +177,219 @@ function stopGate(current, opts) {
174
177
  }
175
178
  console.log();
176
179
  }
180
+ /**
181
+ * Print a RESUMING header with step-specific investigation instructions.
182
+ * Called when a step is re-entered (prevState.step === current step).
183
+ */
184
+ function printResumptionHeader(step) {
185
+ const port = getServerPort();
186
+ const checks = {
187
+ 1: [
188
+ 'Check if plan was already written to context file:\n `cat .codeyam/editor-mode-context.md`',
189
+ ],
190
+ 2: ['Check if project files already exist:\n `ls package.json src/`'],
191
+ 3: ['This is a confirmation step — just re-present to user'],
192
+ 4: [
193
+ 'Check if extraction plan already exists in context file:\n `cat .codeyam/editor-mode-context.md`',
194
+ ],
195
+ 5: [
196
+ 'Check if components/functions already extracted:\n `ls src/components/ src/lib/`',
197
+ ],
198
+ 6: [
199
+ 'Check if glossary already populated:\n `cat .codeyam/glossary.json`',
200
+ ],
201
+ 7: ['Check if isolation routes and registered scenarios already exist'],
202
+ 8: [
203
+ `Check existing scenarios:\n \`curl -s http://localhost:${port}/api/editor-scenarios\``,
204
+ ],
205
+ 9: [
206
+ `Check existing user-persona scenarios:\n \`curl -s http://localhost:${port}/api/editor-scenarios\``,
207
+ ],
208
+ 10: ['Re-verify is safe to repeat — just re-run the checks'],
209
+ 11: [
210
+ `Check if a journal entry already exists for this feature:\n \`curl -s http://localhost:${port}/api/editor-journal\``,
211
+ 'If an entry exists, use PATCH to update it — do NOT create a new one',
212
+ ],
213
+ 12: ['Check if commit already made:\n `git log --oneline -3`'],
214
+ };
215
+ const label = STEP_LABELS[step] || 'Unknown';
216
+ console.log(chalk.bold.yellow(`━━━ RESUMING Step ${step}: ${label} ━━━`));
217
+ console.log(chalk.yellow('This step was already started. Before repeating any actions, investigate:'));
218
+ const items = checks[step] || [];
219
+ for (const item of items) {
220
+ checkbox(item);
221
+ }
222
+ console.log();
223
+ }
224
+ function captureOutput(fn) {
225
+ const stdoutWrite = process.stdout.write.bind(process.stdout);
226
+ const stderrWrite = process.stderr.write.bind(process.stderr);
227
+ const chunks = [];
228
+ const captureWrite = (chunk, encoding, cb) => {
229
+ const text = typeof chunk === 'string'
230
+ ? chunk
231
+ : chunk instanceof Buffer
232
+ ? chunk.toString(typeof encoding === 'string' ? encoding : undefined)
233
+ : String(chunk);
234
+ chunks.push(text);
235
+ if (typeof encoding === 'function') {
236
+ encoding();
237
+ }
238
+ if (typeof cb === 'function') {
239
+ cb();
240
+ }
241
+ return true;
242
+ };
243
+ process.stdout.write = captureWrite;
244
+ process.stderr.write = captureWrite;
245
+ try {
246
+ fn();
247
+ }
248
+ finally {
249
+ process.stdout.write = stdoutWrite;
250
+ process.stderr.write = stderrWrite;
251
+ }
252
+ return chunks.join('');
253
+ }
254
+ function withTempRoot(hasProject, fn) {
255
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'codeyam-editor-debug-'));
256
+ if (hasProject) {
257
+ fs.writeFileSync(path.join(root, 'package.json'), '{"name":"codeyam-editor-debug","private":true}', 'utf8');
258
+ }
259
+ try {
260
+ return fn(root);
261
+ }
262
+ finally {
263
+ try {
264
+ fs.rmSync(root, { recursive: true, force: true });
265
+ }
266
+ catch {
267
+ // Best-effort cleanup
268
+ }
269
+ }
270
+ }
271
+ function makeState(step, feature) {
272
+ const now = new Date().toISOString();
273
+ return {
274
+ feature,
275
+ step,
276
+ label: STEP_LABELS[step],
277
+ startedAt: now,
278
+ featureStartedAt: now,
279
+ };
280
+ }
281
+ function normalizeDebugTarget(raw) {
282
+ const value = raw.trim().toLowerCase();
283
+ if (!value)
284
+ return '';
285
+ if (value === 'setup')
286
+ return 'setup';
287
+ if (value === 'overview')
288
+ return 'overview';
289
+ if (value === 'overview-with-state' || value === 'overview-state') {
290
+ return 'overview-with-state';
291
+ }
292
+ if (/^\d+$/.test(value)) {
293
+ return `step-${value}`;
294
+ }
295
+ if (/^step-?\d+$/.test(value)) {
296
+ const num = value.replace('step', '').replace('-', '');
297
+ return `step-${num}`;
298
+ }
299
+ return value;
300
+ }
301
+ function parseDebugTargets(target) {
302
+ if (!target || target.trim().toLowerCase() === 'all')
303
+ return null;
304
+ const rawTargets = target
305
+ .split(',')
306
+ .map((t) => normalizeDebugTarget(t))
307
+ .filter(Boolean);
308
+ const valid = new Set();
309
+ for (const entry of rawTargets) {
310
+ if (entry === 'setup' ||
311
+ entry === 'overview' ||
312
+ entry === 'overview-with-state') {
313
+ valid.add(entry);
314
+ continue;
315
+ }
316
+ if (entry.startsWith('step-')) {
317
+ const num = parseInt(entry.replace('step-', ''), 10);
318
+ if (!isNaN(num) && num >= 1 && num <= 12) {
319
+ valid.add(`step-${num}`);
320
+ continue;
321
+ }
322
+ }
323
+ throw new Error(`Invalid debug target: "${entry}"`);
324
+ }
325
+ return valid;
326
+ }
327
+ function writeContextSnapshot(root, outDir) {
328
+ const contextDir = path.join(outDir, 'context');
329
+ fs.mkdirSync(contextDir, { recursive: true });
330
+ const entries = [];
331
+ const skillPath = path.join(root, '.claude', 'skills', 'codeyam-editor', 'SKILL.md');
332
+ const skillFallback = path.join(__dirname, '..', '..', 'templates', 'codeyam-editor.md');
333
+ const skillSource = fs.existsSync(skillPath) ? skillPath : skillFallback;
334
+ const skillDest = path.join(contextDir, 'codeyam-editor-skill.md');
335
+ fs.copyFileSync(skillSource, skillDest);
336
+ entries.push({
337
+ label: 'codeyam-editor skill',
338
+ source: skillSource,
339
+ file: path.relative(outDir, skillDest),
340
+ status: fs.existsSync(skillPath) ? 'installed' : 'template',
341
+ });
342
+ const claudePath = path.join(root, 'CLAUDE.md');
343
+ if (fs.existsSync(claudePath)) {
344
+ const dest = path.join(contextDir, 'CLAUDE.md');
345
+ fs.copyFileSync(claudePath, dest);
346
+ entries.push({
347
+ label: 'CLAUDE.md',
348
+ source: claudePath,
349
+ file: path.relative(outDir, dest),
350
+ status: 'project',
351
+ });
352
+ }
353
+ else {
354
+ const fallback = path.join(__dirname, '..', '..', 'templates', 'codeyam-editor-claude.md');
355
+ if (fs.existsSync(fallback)) {
356
+ const dest = path.join(contextDir, 'CLAUDE.md');
357
+ fs.copyFileSync(fallback, dest);
358
+ entries.push({
359
+ label: 'CLAUDE.md',
360
+ source: fallback,
361
+ file: path.relative(outDir, dest),
362
+ status: 'template',
363
+ });
364
+ }
365
+ else {
366
+ entries.push({
367
+ label: 'CLAUDE.md',
368
+ file: path.relative(outDir, path.join(contextDir, 'CLAUDE.md')),
369
+ status: 'missing',
370
+ });
371
+ }
372
+ }
373
+ const contextPath = path.join(root, '.codeyam', 'editor-mode-context.md');
374
+ if (fs.existsSync(contextPath)) {
375
+ const dest = path.join(contextDir, 'editor-mode-context.md');
376
+ fs.copyFileSync(contextPath, dest);
377
+ entries.push({
378
+ label: 'editor-mode-context.md',
379
+ source: contextPath,
380
+ file: path.relative(outDir, dest),
381
+ status: 'project',
382
+ });
383
+ }
384
+ else {
385
+ entries.push({
386
+ label: 'editor-mode-context.md',
387
+ file: path.relative(outDir, path.join(contextDir, 'editor-mode-context.md')),
388
+ status: 'missing',
389
+ });
390
+ }
391
+ return entries;
392
+ }
177
393
  // ─── Setup (no args, no project) ──────────────────────────────────────
178
394
  function printSetup(root) {
179
395
  const port = getServerPort();
@@ -212,7 +428,7 @@ function printCycleOverview(root, state) {
212
428
  chalk.dim(' to start a new feature'));
213
429
  }
214
430
  else {
215
- console.log('Each feature follows 11 steps. You MUST run each command in order:');
431
+ console.log('Each feature follows 12 steps. You MUST run each command in order:');
216
432
  console.log();
217
433
  console.log(` ${chalk.bold.yellow(' 1')} ${chalk.bold('Plan')} — Plan the feature, confirm with user`);
218
434
  console.log(` ${chalk.bold.yellow(' 2')} ${chalk.bold('Prototype')} — Build a working prototype fast`);
@@ -224,7 +440,8 @@ function printCycleOverview(root, state) {
224
440
  console.log(` ${chalk.bold.yellow(' 8')} ${chalk.bold('App Scenarios')} — Create app-level scenarios`);
225
441
  console.log(` ${chalk.bold.yellow(' 9')} ${chalk.bold('User Scenarios')} — Create user-persona scenarios`);
226
442
  console.log(` ${chalk.bold.yellow('10')} ${chalk.bold('Verify')} — Review screenshots, check for errors`);
227
- console.log(` ${chalk.bold.yellow('11')} ${chalk.bold('Review')} Present summary, get approval`);
443
+ console.log(` ${chalk.bold.yellow('11')} ${chalk.bold('Journal')} Create/update journal entry`);
444
+ console.log(` ${chalk.bold.yellow('12')} ${chalk.bold('Review')} — Present summary, get approval`);
228
445
  console.log();
229
446
  console.log(chalk.green('Start now: ') + chalk.bold('codeyam editor 1'));
230
447
  }
@@ -232,7 +449,11 @@ function printCycleOverview(root, state) {
232
449
  }
233
450
  // ─── Step 1: Plan ─────────────────────────────────────────────────────
234
451
  function printStep1(root, feature) {
235
- clearState(root);
452
+ const prevState = readState(root);
453
+ const isResuming = prevState?.step === 1;
454
+ if (!isResuming) {
455
+ clearState(root);
456
+ }
236
457
  // If feature is provided, save initial state so step 2 can pick it up
237
458
  if (feature) {
238
459
  const now = new Date().toISOString();
@@ -240,12 +461,15 @@ function printStep1(root, feature) {
240
461
  feature,
241
462
  step: 1,
242
463
  label: STEP_LABELS[1],
243
- startedAt: now,
244
- featureStartedAt: now,
464
+ startedAt: isResuming ? prevState.startedAt : now,
465
+ featureStartedAt: isResuming ? prevState.featureStartedAt : now,
245
466
  });
246
467
  }
247
468
  logEvent(root, 'step', { step: 1, label: 'Plan', feature });
248
469
  stepHeader(1, 'Plan', feature);
470
+ if (isResuming) {
471
+ printResumptionHeader(1);
472
+ }
249
473
  console.log('Plan the feature before writing ANY code.');
250
474
  console.log();
251
475
  console.log(chalk.bold('Checklist:'));
@@ -254,6 +478,7 @@ function printStep1(root, feature) {
254
478
  checkbox('Ask clarifying questions using `AskUserQuestion` with selectable options');
255
479
  console.log(chalk.dim(' Use AskUserQuestion for EVERY clarifying question — give 2-4 concrete options the user can pick from.'));
256
480
  console.log(chalk.dim(' Focus on what the USER will see and do, not on databases, APIs, or components.'));
481
+ console.log(chalk.dim(' Do NOT ask about tech stack, frameworks, libraries, or implementation details — only ask about user-facing choices.'));
257
482
  console.log(chalk.dim(' Good: "What should happen when there are no results?" → options: "Show empty state message", "Show suggestions"'));
258
483
  console.log(chalk.dim(' Bad: Free-form text asking "What do you think about the data model?"'));
259
484
  console.log(chalk.dim(' You can ask up to 4 questions at once. Bundle related questions into a single AskUserQuestion call.'));
@@ -288,16 +513,21 @@ function printStep1(root, feature) {
288
513
  function printStep2(root, feature) {
289
514
  const port = getServerPort();
290
515
  const projectExists = hasProject(root);
516
+ const prevState = readState(root);
517
+ const isResuming = prevState?.step === 2;
291
518
  const now = new Date().toISOString();
292
519
  writeState(root, {
293
520
  feature,
294
521
  step: 2,
295
522
  label: STEP_LABELS[2],
296
- startedAt: now,
297
- featureStartedAt: now,
523
+ startedAt: isResuming ? prevState.startedAt : now,
524
+ featureStartedAt: isResuming ? prevState.featureStartedAt : now,
298
525
  });
299
526
  logEvent(root, 'step', { step: 2, label: 'Prototype', feature });
300
527
  stepHeader(2, 'Prototype', feature);
528
+ if (isResuming) {
529
+ printResumptionHeader(2);
530
+ }
301
531
  console.log('Build fast with real data. Prioritize speed over quality.');
302
532
  console.log();
303
533
  // If no project exists yet, include scaffolding instructions first
@@ -327,7 +557,7 @@ function printStep2(root, feature) {
327
557
  console.log(chalk.dim(' Key: Seed scripts must use the adapter pattern (see prisma/seed.ts).'));
328
558
  }
329
559
  else {
330
- checkbox('Scaffold the project (Next.js recommended) using temp-dir-rsync pattern');
560
+ checkbox('Scaffold the project (Next.js App Router) using temp-dir-rsync pattern');
331
561
  console.log();
332
562
  console.log(chalk.dim(' Scaffolding pattern (avoids .claude/ conflicts):'));
333
563
  console.log(chalk.dim(' SCAFFOLD_DIR="/tmp/codeyam-scaffold-$$" && mkdir -p "$SCAFFOLD_DIR"'));
@@ -382,22 +612,35 @@ function printStep2(root, feature) {
382
612
  function printStep3(root, feature) {
383
613
  const port = getServerPort();
384
614
  const prevState = readState(root);
615
+ const isResuming = prevState?.step === 3;
616
+ const now = new Date().toISOString();
385
617
  writeState(root, {
386
618
  feature,
387
619
  step: 3,
388
620
  label: STEP_LABELS[3],
389
- startedAt: new Date().toISOString(),
390
- featureStartedAt: prevState?.featureStartedAt || new Date().toISOString(),
621
+ startedAt: isResuming ? prevState.startedAt : now,
622
+ featureStartedAt: prevState?.featureStartedAt || now,
391
623
  });
392
624
  logEvent(root, 'step', { step: 3, label: 'Confirm', feature });
393
625
  stepHeader(3, 'Confirm', feature);
626
+ if (isResuming) {
627
+ printResumptionHeader(3);
628
+ }
394
629
  console.log('Summarize what was built and get user confirmation.');
395
630
  console.log();
396
631
  console.log(chalk.bold('Before presenting — verify everything works:'));
397
632
  checkbox(`Refresh the preview: \`curl -s -X POST http://localhost:${port}/api/editor-refresh\``);
398
- checkbox('Verify the page loads without errors (no error pages, no blank screens)');
399
- checkbox('Verify all images and assets render correctly (no broken images)');
633
+ checkbox(`Refresh and check for SSR errors: \`curl -s -X POST http://localhost:${port}/api/dev-mode-preview\` — look at the \`preview\` field for \`healthy: false\``);
400
634
  checkbox('Verify API routes return valid data (curl each route)');
635
+ checkbox(`Navigate to each page and check health: \`curl -s -X POST http://localhost:${port}/api/dev-mode-preview -H 'Content-Type: application/json' -d '{"path":"/your-route"}'\``);
636
+ console.log();
637
+ console.log(chalk.bold(' Check for broken images on each page:'));
638
+ checkbox('Get the dev server URL from `curl -s http://localhost:' +
639
+ port +
640
+ '/api/editor-dev-server` (the `url` field)');
641
+ checkbox('For each page: `curl -s <dev-server-url>/your-route` and inspect `<img>` src attributes');
642
+ checkbox('Verify every image src is a valid path (not a missing file, placeholder URL, or broken reference)');
643
+ checkbox('If any images are broken: fix the src (use real assets, placeholders from a CDN, or remove the image)');
401
644
  console.log(chalk.yellow(' Do not present to the user until all checks pass. Fix issues first.'));
402
645
  console.log();
403
646
  console.log(chalk.bold('Then present to the user:'));
@@ -416,15 +659,20 @@ function printStep3(root, feature) {
416
659
  // ─── Step 4: Deconstruct ──────────────────────────────────────────────
417
660
  function printStep4(root, feature) {
418
661
  const prevState = readState(root);
662
+ const isResuming = prevState?.step === 4;
663
+ const now = new Date().toISOString();
419
664
  writeState(root, {
420
665
  feature,
421
666
  step: 4,
422
667
  label: STEP_LABELS[4],
423
- startedAt: new Date().toISOString(),
424
- featureStartedAt: prevState?.featureStartedAt || new Date().toISOString(),
668
+ startedAt: isResuming ? prevState.startedAt : now,
669
+ featureStartedAt: prevState?.featureStartedAt || now,
425
670
  });
426
671
  logEvent(root, 'step', { step: 4, label: 'Deconstruct', feature });
427
672
  stepHeader(4, 'Deconstruct', feature);
673
+ if (isResuming) {
674
+ printResumptionHeader(4);
675
+ }
428
676
  console.log(chalk.bold('Goal: pages contain ONLY components. Components contain ONLY sub-components.'));
429
677
  console.log(chalk.yellow('This step is read and plan only. Code extraction happens in step 5.'));
430
678
  console.log();
@@ -461,15 +709,20 @@ function printStep4(root, feature) {
461
709
  // ─── Step 5: Extract ──────────────────────────────────────────────────
462
710
  function printStep5(root, feature) {
463
711
  const prevState = readState(root);
712
+ const isResuming = prevState?.step === 5;
713
+ const now = new Date().toISOString();
464
714
  writeState(root, {
465
715
  feature,
466
716
  step: 5,
467
717
  label: STEP_LABELS[5],
468
- startedAt: new Date().toISOString(),
469
- featureStartedAt: prevState?.featureStartedAt || new Date().toISOString(),
718
+ startedAt: isResuming ? prevState.startedAt : now,
719
+ featureStartedAt: prevState?.featureStartedAt || now,
470
720
  });
471
721
  logEvent(root, 'step', { step: 5, label: 'Extract', feature });
472
722
  stepHeader(5, 'Extract', feature);
723
+ if (isResuming) {
724
+ printResumptionHeader(5);
725
+ }
473
726
  console.log('Execute your extraction plan from step 4.');
474
727
  console.log();
475
728
  console.log(chalk.bold('Components:'));
@@ -503,15 +756,20 @@ function printStep5(root, feature) {
503
756
  // ─── Step 6: Glossary ─────────────────────────────────────────────────
504
757
  function printStep6(root, feature) {
505
758
  const prevState = readState(root);
759
+ const isResuming = prevState?.step === 6;
760
+ const now = new Date().toISOString();
506
761
  writeState(root, {
507
762
  feature,
508
763
  step: 6,
509
764
  label: STEP_LABELS[6],
510
- startedAt: new Date().toISOString(),
511
- featureStartedAt: prevState?.featureStartedAt || new Date().toISOString(),
765
+ startedAt: isResuming ? prevState.startedAt : now,
766
+ featureStartedAt: prevState?.featureStartedAt || now,
512
767
  });
513
768
  logEvent(root, 'step', { step: 6, label: 'Glossary', feature });
514
769
  stepHeader(6, 'Glossary', feature);
770
+ if (isResuming) {
771
+ printResumptionHeader(6);
772
+ }
515
773
  console.log('Record all new functions/components in `.codeyam/glossary.json`.');
516
774
  console.log();
517
775
  console.log(chalk.bold('Checklist:'));
@@ -535,15 +793,20 @@ function printStep6(root, feature) {
535
793
  function printStep7(root, feature) {
536
794
  const port = getServerPort();
537
795
  const prevState = readState(root);
796
+ const isResuming = prevState?.step === 7;
797
+ const now = new Date().toISOString();
538
798
  writeState(root, {
539
799
  feature,
540
800
  step: 7,
541
801
  label: STEP_LABELS[7],
542
- startedAt: new Date().toISOString(),
543
- featureStartedAt: prevState?.featureStartedAt || new Date().toISOString(),
802
+ startedAt: isResuming ? prevState.startedAt : now,
803
+ featureStartedAt: prevState?.featureStartedAt || now,
544
804
  });
545
805
  logEvent(root, 'step', { step: 7, label: 'Analyze', feature });
546
806
  stepHeader(7, 'Analyze and Verify', feature);
807
+ if (isResuming) {
808
+ printResumptionHeader(7);
809
+ }
547
810
  console.log('Verify visual components (via isolation routes) and library functions (via tests).');
548
811
  console.log();
549
812
  console.log(chalk.bold('Visual Components — Component Isolation:'));
@@ -590,21 +853,28 @@ function printStep7(root, feature) {
590
853
  checkbox('If any test fails, fix the source code and re-run');
591
854
  console.log();
592
855
  console.log(chalk.dim('Do not proceed until both component isolations and library tests pass.'));
856
+ console.log();
857
+ checkbox('Run `codeyam editor audit` to verify all components have scenarios and all functions have tests');
593
858
  stopGate(7);
594
859
  }
595
860
  // ─── Step 8: App Scenarios ────────────────────────────────────────────
596
861
  function printStep8(root, feature) {
597
862
  const port = getServerPort();
598
863
  const prevState = readState(root);
864
+ const isResuming = prevState?.step === 8;
865
+ const now = new Date().toISOString();
599
866
  writeState(root, {
600
867
  feature,
601
868
  step: 8,
602
869
  label: STEP_LABELS[8],
603
- startedAt: new Date().toISOString(),
604
- featureStartedAt: prevState?.featureStartedAt || new Date().toISOString(),
870
+ startedAt: isResuming ? prevState.startedAt : now,
871
+ featureStartedAt: prevState?.featureStartedAt || now,
605
872
  });
606
873
  logEvent(root, 'step', { step: 8, label: 'App Scenarios', feature });
607
874
  stepHeader(8, 'App Scenarios', feature);
875
+ if (isResuming) {
876
+ printResumptionHeader(8);
877
+ }
608
878
  console.log('Create app-level scenarios representing different data states.');
609
879
  console.log();
610
880
  console.log(chalk.bold('Checklist:'));
@@ -624,15 +894,20 @@ function printStep8(root, feature) {
624
894
  function printStep9(root, feature) {
625
895
  const port = getServerPort();
626
896
  const prevState = readState(root);
897
+ const isResuming = prevState?.step === 9;
898
+ const now = new Date().toISOString();
627
899
  writeState(root, {
628
900
  feature,
629
901
  step: 9,
630
902
  label: STEP_LABELS[9],
631
- startedAt: new Date().toISOString(),
632
- featureStartedAt: prevState?.featureStartedAt || new Date().toISOString(),
903
+ startedAt: isResuming ? prevState.startedAt : now,
904
+ featureStartedAt: prevState?.featureStartedAt || now,
633
905
  });
634
906
  logEvent(root, 'step', { step: 9, label: 'User Scenarios', feature });
635
907
  stepHeader(9, 'User Scenarios', feature);
908
+ if (isResuming) {
909
+ printResumptionHeader(9);
910
+ }
636
911
  console.log('Create per-persona scenarios if the app has users. Skip to step 10 if no users.');
637
912
  console.log();
638
913
  console.log(chalk.bold('If the app has users:'));
@@ -654,15 +929,20 @@ function printStep9(root, feature) {
654
929
  function printStep10(root, feature) {
655
930
  const port = getServerPort();
656
931
  const prevState = readState(root);
932
+ const isResuming = prevState?.step === 10;
933
+ const now = new Date().toISOString();
657
934
  writeState(root, {
658
935
  feature,
659
936
  step: 10,
660
937
  label: STEP_LABELS[10],
661
- startedAt: new Date().toISOString(),
662
- featureStartedAt: prevState?.featureStartedAt || new Date().toISOString(),
938
+ startedAt: isResuming ? prevState.startedAt : now,
939
+ featureStartedAt: prevState?.featureStartedAt || now,
663
940
  });
664
941
  logEvent(root, 'step', { step: 10, label: 'Verify', feature });
665
942
  stepHeader(10, 'Verify', feature);
943
+ if (isResuming) {
944
+ printResumptionHeader(10);
945
+ }
666
946
  console.log('Verify component isolation screenshots, editor scenarios, and library tests.');
667
947
  console.log();
668
948
  console.log(chalk.bold('Component isolation screenshots — verify:'));
@@ -688,65 +968,27 @@ function printStep10(root, feature) {
688
968
  console.log(chalk.dim('Focus on fixing issues. All component screenshots, scenarios, and tests must be clean before proceeding.'));
689
969
  stopGate(10);
690
970
  }
691
- // ─── Step 11: Review ──────────────────────────────────────────────────
971
+ // ─── Step 11: Journal ─────────────────────────────────────────────────
692
972
  function printStep11(root, feature) {
693
973
  const port = getServerPort();
694
974
  const prevState = readState(root);
975
+ const isResuming = prevState?.step === 11;
976
+ const now = new Date().toISOString();
695
977
  writeState(root, {
696
978
  feature,
697
979
  step: 11,
698
980
  label: STEP_LABELS[11],
699
- startedAt: new Date().toISOString(),
700
- featureStartedAt: prevState?.featureStartedAt || new Date().toISOString(),
981
+ startedAt: isResuming ? prevState.startedAt : now,
982
+ featureStartedAt: prevState?.featureStartedAt || now,
701
983
  });
702
- logEvent(root, 'step', { step: 11, label: 'Review', feature });
703
- stepHeader(11, 'Review', feature);
704
- console.log('Present a full summary, verify screenshots, then let the user save or revise.');
705
- console.log();
706
- console.log(chalk.bold('Phase 1 — Summary:'));
707
- checkbox('Summarize the feature that was built');
708
- checkbox('List all components and functions created');
709
- checkbox('List glossary entries added in step 6');
710
- checkbox('List all scenarios (app-level and user-persona)');
711
- checkbox('Report analysis status (completed/in-progress from step 7)');
712
- checkbox('Report verification status from step 10 (all clean?)');
713
- checkbox(`Refresh the preview: \`curl -s -X POST http://localhost:${port}/api/dev-mode-refresh\``);
714
- checkbox(`Get scenario list: \`curl -s http://localhost:${port}/api/editor-scenarios\``);
715
- checkbox('Present scenarios in a markdown table — one scenario per row');
716
- console.log(chalk.yellow(' EVERY scenario must use {{scenario:Name:ID}} — the terminal makes these clickable.'));
717
- console.log(chalk.yellow(' Strip the component prefix: "DrinkCard - Default" → just "Default".'));
718
- console.log(chalk.yellow(' Component name on first row of each group, blank for subsequent rows.'));
719
- console.log();
720
- console.log(chalk.dim(' Example:'));
721
- console.log(chalk.dim(' | Component | Scenario |'));
722
- console.log(chalk.dim(' |---|---|'));
723
- console.log(chalk.dim(' | **App** | {{scenario:Full Catalog:abc}} |'));
724
- console.log(chalk.dim(' | | {{scenario:Empty:def}} |'));
725
- console.log(chalk.dim(' | | {{scenario:Error:ghi}} |'));
726
- console.log(chalk.dim(' | **StarRating** | {{scenario:Perfect:jkl}} |'));
727
- console.log(chalk.dim(' | | {{scenario:Medium:mno}} |'));
728
- console.log();
729
- console.log(chalk.bold('Phase 2 — Screenshot & error verification:'));
730
- console.log(chalk.yellow(' CRITICAL: Verify screenshots AND check for client-side errors.'));
731
- console.log();
732
- console.log(chalk.bold(' Component isolation screenshots:'));
733
- checkbox('Verify each component has screenshots in the App tab (grouped under Components)');
734
- checkbox('If any are missing, re-register them using `codeyam editor register`');
735
- console.log();
736
- console.log(chalk.bold(' Client-side errors:'));
737
- checkbox(`Check for errors: \`curl -s http://localhost:${port}/api/editor-client-errors\``);
738
- checkbox('If `hasErrors` is true, list the errors and fix them in the source code');
739
- checkbox('After fixing, re-capture affected scenarios to clear the errors');
740
- console.log();
741
- checkbox('Do not proceed until all components have screenshots and client errors are resolved');
742
- console.log();
743
- checkbox('Switch the active scenario to the best demo state for the feature');
744
- console.log(chalk.dim(` curl -s -X POST http://localhost:${port}/api/editor-switch-scenario \\`));
745
- console.log(chalk.dim(` -H 'Content-Type: application/json' -d '{"scenarioSlug":"...","scenarioId":"..."}'`));
746
- console.log(chalk.dim(' Pick the scenario that best showcases the feature (not an error/empty state)'));
747
- checkbox('Present the summary (with screenshot and error status) to the user');
984
+ logEvent(root, 'step', { step: 11, label: 'Journal', feature });
985
+ stepHeader(11, 'Journal', feature);
986
+ if (isResuming) {
987
+ printResumptionHeader(11);
988
+ }
989
+ console.log('Create or update the journal entry for this feature.');
748
990
  console.log();
749
- console.log(chalk.bold('Phase 3 — Journal entry (create or update):'));
991
+ console.log(chalk.bold('Checklist:'));
750
992
  checkbox('Write a concise description of what was built (2-3 sentences)');
751
993
  checkbox(`First time at step 11 — create journal entry with ALL session screenshots:`);
752
994
  console.log(chalk.dim(` curl -s -X POST http://localhost:${port}/api/editor-journal-entry \\`));
@@ -756,9 +998,51 @@ function printStep11(root, feature) {
756
998
  checkbox(`Returning after changes — update the existing journal entry:`);
757
999
  console.log(chalk.dim(` curl -s -X PATCH http://localhost:${port}/api/editor-journal-update \\`));
758
1000
  console.log(chalk.dim(` -H 'Content-Type: application/json' \\`));
759
- console.log(chalk.dim(` -d '{"time":"<journal entry time>","description":"<updated description>"}'`));
1001
+ console.log(chalk.dim(` -d '{"time":"<journal entry time>","description":"<updated>","includeSessionScenarios":true}'`));
1002
+ console.log();
1003
+ console.log(chalk.dim('Focus on creating or updating the journal entry. Summary and review happen in step 12.'));
1004
+ stopGate(11);
1005
+ }
1006
+ // ─── Step 12: Review ──────────────────────────────────────────────────
1007
+ function printStep12(root, feature) {
1008
+ const port = getServerPort();
1009
+ const prevState = readState(root);
1010
+ const isResuming = prevState?.step === 12;
1011
+ const now = new Date().toISOString();
1012
+ writeState(root, {
1013
+ feature,
1014
+ step: 12,
1015
+ label: STEP_LABELS[12],
1016
+ startedAt: isResuming ? prevState.startedAt : now,
1017
+ featureStartedAt: prevState?.featureStartedAt || now,
1018
+ });
1019
+ logEvent(root, 'step', { step: 12, label: 'Review', feature });
1020
+ stepHeader(12, 'Review', feature);
1021
+ if (isResuming) {
1022
+ printResumptionHeader(12);
1023
+ }
1024
+ console.log('Verify everything, then present the scenario table to the user.');
1025
+ console.log();
1026
+ console.log(chalk.bold('Phase 1 — Verify (do all of this silently):'));
1027
+ checkbox(`Refresh the preview: \`curl -s -X POST http://localhost:${port}/api/dev-mode-preview\``);
1028
+ checkbox('Verify each component has screenshots in the App tab (grouped under Components)');
1029
+ checkbox('If any are missing, re-register them using `codeyam editor register`');
1030
+ checkbox(`Check for client errors: \`curl -s http://localhost:${port}/api/editor-client-errors\``);
1031
+ checkbox('If `hasErrors` is true, fix them and re-capture affected scenarios');
1032
+ checkbox('Run `codeyam editor audit` to verify completeness of scenarios and tests');
1033
+ checkbox('Do not proceed until all checks pass');
1034
+ console.log();
1035
+ console.log(chalk.bold('Phase 2 — Present to the user:'));
1036
+ checkbox('Write a 1-2 sentence summary of what was built');
1037
+ checkbox('Report test count and audit status (one line)');
1038
+ checkbox('Switch the active scenario to the best demo state for the feature:');
1039
+ console.log(chalk.dim(` curl -s -X POST http://localhost:${port}/api/dev-mode-preview \\`));
1040
+ console.log(chalk.dim(` -H 'Content-Type: application/json' -d '{"scenarioSlug":"...","scenarioId":"..."}'`));
1041
+ checkbox(`Show the results panel: \`curl -s -X POST http://localhost:${port}/api/editor-show-results\``);
1042
+ console.log(chalk.dim(' This opens a visual panel below the terminal showing all scenarios with screenshots.'));
1043
+ console.log(chalk.dim(' The user can click scenarios to switch the live preview.'));
760
1044
  console.log();
761
- console.log(chalk.bold('Phase 4 — Present a selection menu to the user (use AskUserQuestion):'));
1045
+ console.log(chalk.bold('Phase 3 — Present a selection menu to the user (use AskUserQuestion):'));
762
1046
  console.log(chalk.green(' "Save & commit"') +
763
1047
  chalk.dim(' — git commit all changes and record in journal'));
764
1048
  console.log(chalk.yellow(' "I\'d like to make some changes"') +
@@ -776,11 +1060,13 @@ function printStep11(root, feature) {
776
1060
  checkbox('Ask what changes the user wants');
777
1061
  checkbox('Make the requested changes');
778
1062
  console.log(chalk.yellow(' Then run: ') +
779
- chalk.bold('codeyam editor 10') +
780
- chalk.yellow(' to re-verify, which flows back to step 11'));
1063
+ chalk.bold(`codeyam editor change "${feature}"`) +
1064
+ chalk.yellow(' this prints a post-change checklist to re-register scenarios,'));
1065
+ console.log(chalk.yellow(' re-run tests, update journal screenshots, and loop back to this review step.'));
1066
+ console.log(chalk.red.bold(' IMPORTANT: You MUST run the change command. Do not skip this or try to re-verify manually.'));
781
1067
  console.log();
782
- console.log(chalk.dim('Complete all phases in order: summary, screenshot verification, journal entry, user menu.'));
783
- stopGate(11, { confirm: true });
1068
+ console.log(chalk.dim('Complete all phases in order: summary, screenshot verification, user menu.'));
1069
+ stopGate(12, { confirm: true });
784
1070
  }
785
1071
  // ─── Command definition ───────────────────────────────────────────────
786
1072
  // ─── Analyze-imports subcommand ────────────────────────────────────────
@@ -938,15 +1224,479 @@ async function handleRegister(jsonArg) {
938
1224
  process.exit(1);
939
1225
  }
940
1226
  }
1227
+ // ─── Dependents subcommand ────────────────────────────────────────────
1228
+ /**
1229
+ * `codeyam editor dependents <EntityName>`
1230
+ *
1231
+ * Walks the importedBy reverse dependency tree to find all ancestors of a
1232
+ * given entity — i.e., every component or page that transitively imports it.
1233
+ * Used after code changes to determine what needs recapturing.
1234
+ */
1235
+ async function handleDependents(entityName) {
1236
+ if (!entityName) {
1237
+ console.error(chalk.red('Error: Entity name required.'));
1238
+ console.error(chalk.dim(' Usage: codeyam editor dependents DrinkCard'));
1239
+ process.exit(1);
1240
+ }
1241
+ const progress = new ProgressReporter();
1242
+ progress.start('Loading entities from database...');
1243
+ await initializeEnvironment();
1244
+ const allEntities = await loadEntities({});
1245
+ if (!allEntities || allEntities.length === 0) {
1246
+ progress.fail('No entities found in database');
1247
+ console.error(chalk.dim(' Run codeyam editor analyze-imports first to populate entity data.'));
1248
+ process.exit(1);
1249
+ }
1250
+ // Find the target entity by name (case-insensitive match)
1251
+ const targetEntities = allEntities.filter((e) => e.name.toLowerCase() === entityName.toLowerCase());
1252
+ if (targetEntities.length === 0) {
1253
+ progress.fail(`Entity "${entityName}" not found in database`);
1254
+ const names = [...new Set(allEntities.map((e) => e.name))].sort();
1255
+ console.error(chalk.dim(` Available entities: ${names.join(', ')}`));
1256
+ process.exit(1);
1257
+ }
1258
+ progress.succeed('Entities loaded');
1259
+ // Build lookup: entityName -> Set<entityName> of entities that import it.
1260
+ // Each entity's metadata.importedBy is the pre-computed reverse graph:
1261
+ // { [filePath]: { [importerName]: { shas: string[] } } }
1262
+ const importedByName = new Map();
1263
+ for (const entity of allEntities) {
1264
+ const importedBy = entity.metadata?.importedBy;
1265
+ if (!importedBy || typeof importedBy !== 'object')
1266
+ continue;
1267
+ const importers = new Set();
1268
+ for (const filePath of Object.keys(importedBy)) {
1269
+ for (const importerName of Object.keys(importedBy[filePath])) {
1270
+ importers.add(importerName);
1271
+ }
1272
+ }
1273
+ if (importers.size > 0) {
1274
+ importedByName.set(entity.name, importers);
1275
+ }
1276
+ }
1277
+ // BFS walk up the dependency tree from the changed entity
1278
+ const visited = new Set();
1279
+ const result = [];
1280
+ const queue = [
1281
+ { name: entityName, depth: 0 },
1282
+ ];
1283
+ visited.add(entityName.toLowerCase());
1284
+ // Collect entity types for display
1285
+ const entityTypes = new Map();
1286
+ for (const e of allEntities) {
1287
+ if (!entityTypes.has(e.name)) {
1288
+ entityTypes.set(e.name, e.entityType || 'unknown');
1289
+ }
1290
+ }
1291
+ while (queue.length > 0) {
1292
+ const { name, depth } = queue.shift();
1293
+ result.push({
1294
+ name,
1295
+ entityType: entityTypes.get(name) || 'unknown',
1296
+ depth,
1297
+ });
1298
+ // Find all entities that import this one
1299
+ const importers = importedByName.get(name);
1300
+ if (!importers)
1301
+ continue;
1302
+ for (const importerName of importers) {
1303
+ if (visited.has(importerName.toLowerCase()))
1304
+ continue;
1305
+ visited.add(importerName.toLowerCase());
1306
+ queue.push({ name: importerName, depth: depth + 1 });
1307
+ }
1308
+ }
1309
+ // Output
1310
+ if (result.length <= 1) {
1311
+ console.log(chalk.yellow(`No dependents found for "${entityName}".`));
1312
+ console.log(chalk.dim(' This entity is not imported by any other known entities.'));
1313
+ console.log(chalk.dim(' Run codeyam editor analyze-imports to populate import data.'));
1314
+ return;
1315
+ }
1316
+ console.log(chalk.bold(`Dependency tree for ${entityName} (${result.length} entities):`));
1317
+ console.log();
1318
+ for (const entry of result) {
1319
+ const indent = ' '.repeat(entry.depth);
1320
+ const prefix = entry.depth === 0 ? chalk.cyan('●') : chalk.dim('├──');
1321
+ const label = entry.depth === 0
1322
+ ? chalk.cyan(`${entry.name} (changed)`)
1323
+ : chalk.white(entry.name);
1324
+ const type = chalk.dim(`[${entry.entityType}]`);
1325
+ console.log(`${indent}${prefix} ${label} ${type}`);
1326
+ }
1327
+ console.log();
1328
+ console.log(chalk.dim('All listed entities need their scenarios recaptured after changes.'));
1329
+ }
1330
+ // ─── Change subcommand ───────────────────────────────────────────────
1331
+ /**
1332
+ * `codeyam editor change <feature>`
1333
+ *
1334
+ * Prints a condensed post-change checklist that guides Claude through
1335
+ * re-verifying after user-requested modifications. This is the "change
1336
+ * loop" — it replaces the freeform "make changes" path with structured
1337
+ * instructions that always loop back to step 12 review.
1338
+ */
1339
+ function handleChange(feature) {
1340
+ const root = getProjectRoot();
1341
+ if (!feature) {
1342
+ // Try to read feature from state
1343
+ const state = readState(root);
1344
+ if (state?.feature) {
1345
+ feature = state.feature;
1346
+ }
1347
+ else {
1348
+ console.error(chalk.red('Error: Feature name required.'));
1349
+ console.error(chalk.dim(' Usage: codeyam editor change "My Feature"'));
1350
+ process.exit(1);
1351
+ }
1352
+ }
1353
+ const port = getServerPort();
1354
+ console.log();
1355
+ console.log(chalk.bold.cyan('━━━ Change Loop ━━━'));
1356
+ console.log(chalk.dim(`Feature: ${feature}`));
1357
+ console.log();
1358
+ console.log('The user has requested changes. Follow this checklist after making them.');
1359
+ console.log();
1360
+ console.log(chalk.bold('1. Re-register affected component scenarios:'));
1361
+ checkbox('For each component you modified, re-register ALL its scenarios');
1362
+ console.log(chalk.dim(` codeyam editor register '{"name":"ComponentName - ScenarioName","description":"...","componentName":"ComponentName","componentPath":"app/components/ComponentName.tsx"}'`));
1363
+ checkbox('For any NEW components, create isolation routes and register scenarios');
1364
+ console.log();
1365
+ console.log(chalk.bold('2. Re-run affected tests:'));
1366
+ checkbox('Re-run test files for any functions you modified');
1367
+ console.log(chalk.dim(' Example: npx vitest run app/lib/drinks.test.ts'));
1368
+ checkbox('If any test fails, fix the source code and re-run');
1369
+ console.log();
1370
+ console.log(chalk.bold('3. Re-capture app-level scenarios:'));
1371
+ checkbox('If component changes affect app-level pages, re-register affected app scenarios');
1372
+ checkbox(`Use \`codeyam editor dependents ComponentName\` to find what needs recapturing`);
1373
+ console.log();
1374
+ console.log(chalk.bold('4. Verify completeness:'));
1375
+ checkbox(`Refresh the preview: \`curl -s -X POST http://localhost:${port}/api/dev-mode-preview\``);
1376
+ checkbox(`Navigate to key pages to verify changes: \`curl -s -X POST http://localhost:${port}/api/dev-mode-preview -H 'Content-Type: application/json' -d '{"path":"/your-route"}'\``);
1377
+ checkbox('Run `codeyam editor audit` — all checks must pass');
1378
+ checkbox(`Check for client-side errors: \`curl -s http://localhost:${port}/api/editor-client-errors\``);
1379
+ checkbox('Fix any errors, then re-register affected scenarios');
1380
+ console.log();
1381
+ console.log(chalk.bold('5. Update the journal:'));
1382
+ checkbox(`Update journal entry with new screenshots: \`curl -s -X PATCH http://localhost:${port}/api/editor-journal-update -H 'Content-Type: application/json' -d '{"time":"<journal entry time>","description":"<updated>","includeSessionScenarios":true}'\``);
1383
+ console.log(chalk.dim(' includeSessionScenarios refreshes ALL screenshots to reflect the changes'));
1384
+ console.log();
1385
+ console.log(chalk.bold.green('When all checks pass, run: ') +
1386
+ chalk.bold(`codeyam editor 12`));
1387
+ console.log(chalk.dim(' This re-enters the review step to present the updated summary to the user.'));
1388
+ console.log();
1389
+ }
1390
+ // ─── Audit subcommand ────────────────────────────────────────────────
1391
+ /**
1392
+ * `codeyam editor audit`
1393
+ *
1394
+ * Fetches the /api/editor-audit endpoint and prints a checklist showing
1395
+ * which glossary components have registered scenarios and which functions
1396
+ * have test files. Exits with code 1 if anything is missing.
1397
+ */
1398
+ async function handleAudit() {
1399
+ const port = getServerPort();
1400
+ const url = `http://localhost:${port}/api/editor-audit`;
1401
+ let data;
1402
+ try {
1403
+ const res = await fetch(url);
1404
+ if (!res.ok) {
1405
+ console.error(chalk.red(`Error: Audit endpoint returned ${res.status}`));
1406
+ process.exit(1);
1407
+ }
1408
+ data = await res.json();
1409
+ }
1410
+ catch (err) {
1411
+ console.error(chalk.red('Error: Could not reach the CodeYam server. Is it running?'));
1412
+ console.error(chalk.dim(` ${err.message}`));
1413
+ process.exit(1);
1414
+ }
1415
+ const { components, functions, summary } = data;
1416
+ console.log();
1417
+ console.log(chalk.bold.cyan('━━━ Editor Audit ━━━'));
1418
+ console.log();
1419
+ // Components
1420
+ if (components.length > 0) {
1421
+ console.log(chalk.bold('Components (scenarios):'));
1422
+ for (const c of components) {
1423
+ const icon = c.status === 'ok' ? chalk.green('✓') : chalk.red('✗');
1424
+ const count = c.status === 'ok'
1425
+ ? chalk.dim(` (${c.scenarioCount} scenario${c.scenarioCount !== 1 ? 's' : ''})`)
1426
+ : chalk.red(' — no scenarios registered');
1427
+ console.log(` ${icon} ${c.name}${count}`);
1428
+ }
1429
+ console.log();
1430
+ }
1431
+ // Functions
1432
+ if (functions.length > 0) {
1433
+ console.log(chalk.bold('Functions (test files):'));
1434
+ for (const f of functions) {
1435
+ const icon = f.status === 'ok' ? chalk.green('✓') : chalk.red('✗');
1436
+ const detail = f.status === 'ok'
1437
+ ? chalk.dim(` (${f.testFile})`)
1438
+ : f.testFile
1439
+ ? chalk.red(` — test file missing: ${f.testFile}`)
1440
+ : chalk.red(' — no test file specified');
1441
+ console.log(` ${icon} ${f.name}${detail}`);
1442
+ }
1443
+ console.log();
1444
+ }
1445
+ // Summary
1446
+ const allOk = summary.allPassing;
1447
+ if (allOk) {
1448
+ console.log(chalk.green.bold('All checks passed!') +
1449
+ chalk.dim(` (${summary.totalComponents} component${summary.totalComponents !== 1 ? 's' : ''}, ${summary.totalFunctions} function${summary.totalFunctions !== 1 ? 's' : ''})`));
1450
+ }
1451
+ else {
1452
+ const parts = [];
1453
+ if (summary.componentsMissing > 0) {
1454
+ parts.push(`${summary.componentsMissing} component${summary.componentsMissing !== 1 ? 's' : ''} missing scenarios`);
1455
+ }
1456
+ if (summary.functionsMissing > 0) {
1457
+ parts.push(`${summary.functionsMissing} function${summary.functionsMissing !== 1 ? 's' : ''} missing tests`);
1458
+ }
1459
+ console.log(chalk.red.bold('Audit failed: ') + parts.join(', '));
1460
+ }
1461
+ console.log();
1462
+ if (!allOk) {
1463
+ process.exit(1);
1464
+ }
1465
+ }
1466
+ // ─── Scenarios subcommand ─────────────────────────────────────────────
1467
+ async function handleScenarios() {
1468
+ const port = getServerPort();
1469
+ const url = `http://localhost:${port}/api/editor-scenarios`;
1470
+ let data;
1471
+ try {
1472
+ const res = await fetch(url);
1473
+ if (!res.ok) {
1474
+ console.error(chalk.red(`Error: Scenarios endpoint returned ${res.status}`));
1475
+ process.exit(1);
1476
+ }
1477
+ data = await res.json();
1478
+ }
1479
+ catch (err) {
1480
+ console.error(chalk.red('Error: Could not reach the CodeYam server. Is it running?'));
1481
+ console.error(chalk.dim(` ${err.message}`));
1482
+ process.exit(1);
1483
+ }
1484
+ const scenarios = data.scenarios;
1485
+ if (scenarios.length === 0) {
1486
+ console.log();
1487
+ console.log(chalk.yellow('No scenarios found for this feature session.'));
1488
+ console.log();
1489
+ return;
1490
+ }
1491
+ // Group by componentName (null → "Uncategorized")
1492
+ const groups = new Map();
1493
+ for (const s of scenarios) {
1494
+ const key = s.componentName || 'Uncategorized';
1495
+ if (!groups.has(key))
1496
+ groups.set(key, []);
1497
+ groups.get(key).push(s);
1498
+ }
1499
+ // Strip component prefix from names (e.g. "DrinkCard - Default" → "Default")
1500
+ function stripPrefix(name, componentName) {
1501
+ if (!componentName)
1502
+ return name;
1503
+ const prefix = `${componentName} - `;
1504
+ return name.startsWith(prefix) ? name.slice(prefix.length) : name;
1505
+ }
1506
+ // Calculate column widths
1507
+ let maxCompLen = 'Component'.length;
1508
+ let maxScenLen = 'Scenario'.length;
1509
+ for (const [comp, items] of groups) {
1510
+ maxCompLen = Math.max(maxCompLen, comp.length);
1511
+ for (const s of items) {
1512
+ const display = stripPrefix(s.name, s.componentName);
1513
+ maxScenLen = Math.max(maxScenLen, display.length);
1514
+ }
1515
+ }
1516
+ const pad = (str, len) => str + ' '.repeat(Math.max(0, len - str.length));
1517
+ // Build OSC 8 hyperlink (display length = visible text only)
1518
+ const osc8 = (url, text) => `\x1b]8;;${url}\x07${text}\x1b]8;;\x07`;
1519
+ // Print table
1520
+ console.log();
1521
+ console.log(chalk.bold.cyan('━━━ Scenario Table ━━━'));
1522
+ console.log();
1523
+ const divComp = '─'.repeat(maxCompLen + 2);
1524
+ const divScen = '─'.repeat(maxScenLen + 2);
1525
+ // Header
1526
+ console.log(`┌${divComp}┬${divScen}┐`);
1527
+ console.log(`│ ${chalk.bold(pad('Component', maxCompLen))} │ ${chalk.bold(pad('Scenario', maxScenLen))} │`);
1528
+ console.log(`├${divComp}┼${divScen}┤`);
1529
+ // Rows
1530
+ let groupIndex = 0;
1531
+ for (const [comp, items] of groups) {
1532
+ if (groupIndex > 0) {
1533
+ console.log(`├${divComp}┼${divScen}┤`);
1534
+ }
1535
+ for (let i = 0; i < items.length; i++) {
1536
+ const s = items[i];
1537
+ const compCell = i === 0 ? chalk.bold(pad(comp, maxCompLen)) : pad('', maxCompLen);
1538
+ const display = stripPrefix(s.name, s.componentName);
1539
+ const linked = osc8(s.link, display);
1540
+ // linked has invisible escape chars; pad based on display length
1541
+ const scenPad = ' '.repeat(Math.max(0, maxScenLen - display.length));
1542
+ console.log(`│ ${compCell} │ ${linked}${scenPad} │`);
1543
+ }
1544
+ groupIndex++;
1545
+ }
1546
+ // Footer
1547
+ console.log(`└${divComp}┴${divScen}┘`);
1548
+ console.log();
1549
+ const total = scenarios.length;
1550
+ const groupCount = groups.size;
1551
+ console.log(chalk.dim(`${total} scenario${total !== 1 ? 's' : ''} across ${groupCount} component${groupCount !== 1 ? 's' : ''}`));
1552
+ console.log();
1553
+ }
1554
+ // ─── Debug subcommand ────────────────────────────────────────────────
1555
+ function handleEditorDebug(args) {
1556
+ const root = getProjectRoot();
1557
+ const feature = args.feature || 'Sample Feature';
1558
+ const includeContext = args.includeContext !== false;
1559
+ let targets;
1560
+ try {
1561
+ targets = parseDebugTargets(args.target);
1562
+ }
1563
+ catch (err) {
1564
+ const msg = err instanceof Error ? err.message : String(err);
1565
+ console.error(chalk.red(`Error: ${msg}`));
1566
+ process.exit(1);
1567
+ }
1568
+ const includeResume = typeof args.resume === 'boolean' ? args.resume : true;
1569
+ const includeNormal = typeof args.resume === 'boolean' ? !args.resume : true;
1570
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
1571
+ const outputDir = args.output
1572
+ ? path.resolve(args.output)
1573
+ : path.join(root, '.codeyam', 'logs', 'editor-debug', timestamp);
1574
+ fs.mkdirSync(outputDir, { recursive: true });
1575
+ const contextEntries = includeContext
1576
+ ? writeContextSnapshot(root, outputDir)
1577
+ : [];
1578
+ const scenarios = [];
1579
+ const wants = (id) => (targets ? targets.has(id) : true);
1580
+ if (wants('setup')) {
1581
+ scenarios.push({
1582
+ id: 'setup',
1583
+ title: 'Setup (no project)',
1584
+ render: () => withTempRoot(false, (tempRoot) => captureOutput(() => printSetup(tempRoot))),
1585
+ });
1586
+ }
1587
+ if (wants('overview')) {
1588
+ scenarios.push({
1589
+ id: 'overview',
1590
+ title: 'Cycle overview (project, no state)',
1591
+ render: () => withTempRoot(true, (tempRoot) => captureOutput(() => printCycleOverview(tempRoot, null))),
1592
+ });
1593
+ }
1594
+ if (wants('overview-with-state')) {
1595
+ scenarios.push({
1596
+ id: 'overview-with-state',
1597
+ title: 'Cycle overview (project, with state)',
1598
+ render: () => withTempRoot(true, (tempRoot) => captureOutput(() => printCycleOverview(tempRoot, makeState(4, feature)))),
1599
+ });
1600
+ }
1601
+ const stepFns = {
1602
+ 1: (r, f) => printStep1(r, f),
1603
+ 2: printStep2,
1604
+ 3: printStep3,
1605
+ 4: printStep4,
1606
+ 5: printStep5,
1607
+ 6: printStep6,
1608
+ 7: printStep7,
1609
+ 8: printStep8,
1610
+ 9: printStep9,
1611
+ 10: printStep10,
1612
+ 11: printStep11,
1613
+ 12: printStep12,
1614
+ };
1615
+ for (let step = 1; step <= 12; step++) {
1616
+ const stepId = `step-${step}`;
1617
+ if (!wants(stepId))
1618
+ continue;
1619
+ if (includeNormal) {
1620
+ scenarios.push({
1621
+ id: stepId,
1622
+ title: `Step ${step} (${STEP_LABELS[step]})`,
1623
+ render: () => withTempRoot(true, (tempRoot) => captureOutput(() => stepFns[step](tempRoot, feature))),
1624
+ });
1625
+ if (step === 1 && !args.feature) {
1626
+ scenarios.push({
1627
+ id: 'step-1-no-feature',
1628
+ title: 'Step 1 (Plan) — no feature provided',
1629
+ render: () => withTempRoot(true, (tempRoot) => captureOutput(() => printStep1(tempRoot, undefined))),
1630
+ });
1631
+ }
1632
+ if (step === 2) {
1633
+ scenarios.push({
1634
+ id: 'step-2-scaffold',
1635
+ title: 'Step 2 (Prototype) — scaffold flow (no project)',
1636
+ render: () => withTempRoot(false, (tempRoot) => captureOutput(() => printStep2(tempRoot, feature))),
1637
+ });
1638
+ }
1639
+ }
1640
+ if (includeResume) {
1641
+ scenarios.push({
1642
+ id: `${stepId}-resume`,
1643
+ title: `Step ${step} (${STEP_LABELS[step]}) — resuming`,
1644
+ render: () => withTempRoot(true, (tempRoot) => {
1645
+ writeState(tempRoot, makeState(step, feature));
1646
+ return captureOutput(() => stepFns[step](tempRoot, feature));
1647
+ }),
1648
+ });
1649
+ }
1650
+ }
1651
+ const indexLines = [
1652
+ '# CodeYam Editor Debug Bundle',
1653
+ '',
1654
+ `Generated: ${new Date().toISOString()}`,
1655
+ `Feature: "${feature}"`,
1656
+ `Resume variants: ${includeResume ? 'included' : 'skipped'}`,
1657
+ 'Note: Output files preserve ANSI color codes (as printed to the terminal).',
1658
+ '',
1659
+ ];
1660
+ if (includeContext) {
1661
+ indexLines.push('## Context snapshot');
1662
+ if (contextEntries.length === 0) {
1663
+ indexLines.push('- (none)');
1664
+ }
1665
+ else {
1666
+ for (const entry of contextEntries) {
1667
+ const source = entry.source ? ` ← ${entry.source}` : '';
1668
+ indexLines.push(`- ${entry.label}: ${entry.file} (${entry.status})${source}`);
1669
+ }
1670
+ }
1671
+ indexLines.push('');
1672
+ }
1673
+ indexLines.push('## Scenarios');
1674
+ for (const scenario of scenarios) {
1675
+ const fileName = `${scenario.id}.txt`;
1676
+ const output = scenario.render();
1677
+ fs.writeFileSync(path.join(outputDir, fileName), output, 'utf8');
1678
+ indexLines.push(`- ${scenario.title}: ${fileName}`);
1679
+ }
1680
+ fs.writeFileSync(path.join(outputDir, 'index.md'), indexLines.join('\n'), 'utf8');
1681
+ console.log();
1682
+ console.log(chalk.bold.cyan('━━━ Editor Debug Bundle ━━━'));
1683
+ console.log();
1684
+ console.log(chalk.green('Wrote debug bundle to: ') + chalk.bold(outputDir));
1685
+ console.log(chalk.dim(' Open index.md for the full list of scenario outputs.'));
1686
+ console.log();
1687
+ }
941
1688
  // ─── Command definition ───────────────────────────────────────────────
942
1689
  const editorCommand = {
943
1690
  command: 'editor [step] [json]',
944
1691
  describe: 'Editor mode guided workflow',
945
1692
  builder: (yargs) => {
946
- return yargs
1693
+ const stepDescription = IS_INTERNAL_BUILD
1694
+ ? 'Step number (1-12) or subcommand (register, analyze-imports, dependents, audit, scenarios, change, debug)'
1695
+ : 'Step number (1-12) or subcommand (register, analyze-imports, dependents, audit, scenarios, change)';
1696
+ let builder = yargs
947
1697
  .positional('step', {
948
1698
  type: 'string',
949
- describe: 'Step number (1-11) or subcommand (register, analyze-imports)',
1699
+ describe: stepDescription,
950
1700
  })
951
1701
  .positional('json', {
952
1702
  type: 'string',
@@ -956,6 +1706,27 @@ const editorCommand = {
956
1706
  type: 'string',
957
1707
  describe: 'Feature name (required for step 2)',
958
1708
  });
1709
+ if (IS_INTERNAL_BUILD) {
1710
+ builder = builder
1711
+ .option('target', {
1712
+ type: 'string',
1713
+ describe: 'Debug target (setup, overview, overview-with-state, step-1..step-12, or comma-separated list)',
1714
+ })
1715
+ .option('resume', {
1716
+ type: 'boolean',
1717
+ describe: 'Debug: only render resume variants (use --no-resume for only normal)',
1718
+ })
1719
+ .option('include-context', {
1720
+ type: 'boolean',
1721
+ default: true,
1722
+ describe: 'Debug: include CLAUDE.md, skill, and editor-mode-context snapshots',
1723
+ })
1724
+ .option('output', {
1725
+ type: 'string',
1726
+ describe: 'Debug: output directory for the bundle',
1727
+ });
1728
+ }
1729
+ return builder;
959
1730
  },
960
1731
  handler: async (argv) => {
961
1732
  const root = getProjectRoot();
@@ -969,9 +1740,38 @@ const editorCommand = {
969
1740
  await handleAnalyzeImports();
970
1741
  return;
971
1742
  }
1743
+ // Subcommand: codeyam editor dependents <EntityName>
1744
+ if (argv.step === 'dependents') {
1745
+ await handleDependents(argv.json || '');
1746
+ return;
1747
+ }
1748
+ // Subcommand: codeyam editor audit
1749
+ if (argv.step === 'audit') {
1750
+ await handleAudit();
1751
+ return;
1752
+ }
1753
+ // Subcommand: codeyam editor scenarios
1754
+ if (argv.step === 'scenarios') {
1755
+ await handleScenarios();
1756
+ return;
1757
+ }
1758
+ // Subcommand: codeyam editor change <feature>
1759
+ if (argv.step === 'change') {
1760
+ handleChange(argv.json || '');
1761
+ return;
1762
+ }
1763
+ // Subcommand: codeyam editor debug [--target ...]
1764
+ if (argv.step === 'debug') {
1765
+ if (!IS_INTERNAL_BUILD) {
1766
+ console.error(chalk.red('Error: "codeyam editor debug" is internal-only.'));
1767
+ process.exit(1);
1768
+ }
1769
+ await handleEditorDebug(argv);
1770
+ return;
1771
+ }
972
1772
  const step = argv.step ? parseInt(argv.step, 10) : undefined;
973
- if (step != null && (isNaN(step) || step < 1 || step > 11)) {
974
- console.error(chalk.red(`Error: Invalid step "${argv.step}". Must be 1-11.`));
1773
+ if (step != null && (isNaN(step) || step < 1 || step > 12)) {
1774
+ console.error(chalk.red(`Error: Invalid step "${argv.step}". Must be 1-12.`));
975
1775
  process.exit(1);
976
1776
  }
977
1777
  if (step == null) {
@@ -1010,7 +1810,8 @@ const editorCommand = {
1010
1810
  case 8:
1011
1811
  case 9:
1012
1812
  case 10:
1013
- case 11: {
1813
+ case 11:
1814
+ case 12: {
1014
1815
  const feature = argv.feature || state?.feature;
1015
1816
  if (!feature) {
1016
1817
  console.error(chalk.red('Error: No feature in progress. Run codeyam editor 1 first.'));
@@ -1026,6 +1827,7 @@ const editorCommand = {
1026
1827
  9: printStep9,
1027
1828
  10: printStep10,
1028
1829
  11: printStep11,
1830
+ 12: printStep12,
1029
1831
  };
1030
1832
  stepFns[step](root, feature);
1031
1833
  break;