@codeyam/codeyam-cli 0.1.18 → 0.1.20
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.
- package/analyzer-template/.build-info.json +7 -7
- package/analyzer-template/log.txt +3 -3
- package/analyzer-template/package.json +1 -1
- package/analyzer-template/packages/analyze/src/lib/files/analyze/findOrCreateEntity.ts +9 -6
- package/analyzer-template/packages/analyze/src/lib/files/analyze/gatherEntityMap.ts +9 -12
- package/analyzer-template/packages/database/package.json +1 -1
- package/analyzer-template/packages/database/src/lib/loadAnalysis.ts +19 -15
- package/analyzer-template/packages/database/src/lib/loadEntity.ts +8 -4
- package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.d.ts.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/loadEntity.d.ts.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/loadEntity.js +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/loadEntity.js.map +1 -1
- package/analyzer-template/project/analyzeFileEntities.ts +26 -0
- package/background/src/lib/virtualized/project/analyzeFileEntities.js +22 -0
- package/background/src/lib/virtualized/project/analyzeFileEntities.js.map +1 -1
- package/codeyam-cli/src/cli.js +15 -0
- package/codeyam-cli/src/cli.js.map +1 -1
- package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js +9 -9
- package/codeyam-cli/src/commands/editor.js +570 -300
- package/codeyam-cli/src/commands/editor.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +273 -1
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorGuardMiddleware.test.js +67 -0
- package/codeyam-cli/src/utils/__tests__/editorGuardMiddleware.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +20 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/journalCaptureStabilization.test.js +16 -1
- package/codeyam-cli/src/utils/__tests__/journalCaptureStabilization.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/screenshotHash.test.js +84 -0
- package/codeyam-cli/src/utils/__tests__/screenshotHash.test.js.map +1 -0
- package/codeyam-cli/src/utils/analysisRunner.js +8 -6
- package/codeyam-cli/src/utils/analysisRunner.js.map +1 -1
- package/codeyam-cli/src/utils/editorAudit.js +73 -4
- package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
- package/codeyam-cli/src/utils/editorGuard.js +36 -0
- package/codeyam-cli/src/utils/editorGuard.js.map +1 -0
- package/codeyam-cli/src/utils/editorScenarios.js +37 -5
- package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
- package/codeyam-cli/src/utils/queue/job.js +6 -3
- package/codeyam-cli/src/utils/queue/job.js.map +1 -1
- package/codeyam-cli/src/utils/screenshotHash.js +26 -0
- package/codeyam-cli/src/utils/screenshotHash.js.map +1 -0
- package/codeyam-cli/src/utils/simulationGateMiddleware.js +9 -0
- package/codeyam-cli/src/utils/simulationGateMiddleware.js.map +1 -1
- package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js +28 -1
- package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js.map +1 -1
- package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js +38 -7
- package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js.map +1 -1
- package/codeyam-cli/src/webserver/__tests__/stripClaudeCommand.test.js +135 -0
- package/codeyam-cli/src/webserver/__tests__/stripClaudeCommand.test.js.map +1 -0
- package/codeyam-cli/src/webserver/app/lib/clientErrors.js +12 -0
- package/codeyam-cli/src/webserver/app/lib/clientErrors.js.map +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{editor.entity.(_sha)-Bnx7yUP0.js → editor.entity.(_sha)-DII1pg_z.js} +13 -13
- package/codeyam-cli/src/webserver/build/client/assets/globals-Yn9W3zp3.css +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{manifest-b9d4d267.js → manifest-cdf2c0a7.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{root-DB3O9_9j.js → root-BxUQigda.js} +5 -5
- package/codeyam-cli/src/webserver/build/server/assets/analysisRunner-B_PsTAb1.js +13 -0
- package/codeyam-cli/src/webserver/build/server/assets/{index-D4meMKy3.js → index-CjLhfz6Z.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/{init-odGJ_c2-.js → init-BEqlbI84.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/{server-build-TmPfF7pT.js → server-build-YI63xTu4.js} +112 -111
- package/codeyam-cli/src/webserver/build/server/index.js +1 -1
- package/codeyam-cli/src/webserver/build-info.json +5 -5
- package/codeyam-cli/src/webserver/scripts/journalCapture.ts +17 -0
- package/codeyam-cli/src/webserver/server.js +52 -14
- package/codeyam-cli/src/webserver/server.js.map +1 -1
- package/codeyam-cli/src/webserver/terminalServer.js +129 -22
- package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
- package/codeyam-cli/templates/__tests__/editor-step-hook.prompt-capture.test.ts +118 -0
- package/codeyam-cli/templates/codeyam-editor-claude.md +2 -0
- package/codeyam-cli/templates/codeyam-editor-reference.md +1 -1
- package/codeyam-cli/templates/editor-step-hook.py +93 -46
- package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +19 -1
- package/package.json +1 -1
- package/packages/analyze/src/lib/files/analyze/findOrCreateEntity.js +2 -2
- package/packages/analyze/src/lib/files/analyze/findOrCreateEntity.js.map +1 -1
- package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js +9 -7
- package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js.map +1 -1
- package/packages/database/src/lib/loadAnalysis.js +1 -1
- package/packages/database/src/lib/loadAnalysis.js.map +1 -1
- package/packages/database/src/lib/loadEntity.js +1 -1
- package/packages/database/src/lib/loadEntity.js.map +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/globals-fAqOD9ex.css +0 -1
- package/codeyam-cli/src/webserver/build/server/assets/analysisRunner-CGwTN3V2.js +0 -13
|
@@ -16,7 +16,7 @@ import { APP_FORMATS, TECH_STACKS } from "../data/techStacks.js";
|
|
|
16
16
|
import { getProjectRoot as getStateProjectRoot } from "../state.js";
|
|
17
17
|
import initCommand from "./init.js";
|
|
18
18
|
import { scanScenarioFiles, syncScenarioFilesToDatabase, backfillScenarioMetadata, migrateScenarioFormats, } from "../utils/scenariosManifest.js";
|
|
19
|
-
import { clearEditorState,
|
|
19
|
+
import { clearEditorState, validateStepTransition, backfillEntityShaOnScenarios, } from "../utils/editorScenarios.js";
|
|
20
20
|
import { validateSeedData, detectSeedAdapter, validateSeedKeysAgainstPrisma, } from "../utils/editorSeedAdapter.js";
|
|
21
21
|
import { deleteScenarioViaCli } from "../utils/editorDeleteScenario.js";
|
|
22
22
|
import { buildEditorApiRequest, callEditorApi, EDITOR_API_SUBCOMMANDS, } from "../utils/editorApi.js";
|
|
@@ -27,21 +27,23 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
27
27
|
const __dirname = path.dirname(__filename);
|
|
28
28
|
const STEP_LABELS = {
|
|
29
29
|
1: 'Plan',
|
|
30
|
-
2: '
|
|
31
|
-
3: '
|
|
32
|
-
4: '
|
|
33
|
-
5: '
|
|
34
|
-
6: '
|
|
35
|
-
7: '
|
|
36
|
-
8: '
|
|
37
|
-
9: '
|
|
38
|
-
10: '
|
|
39
|
-
11: '
|
|
40
|
-
12: '
|
|
41
|
-
13: '
|
|
42
|
-
14: '
|
|
43
|
-
15: '
|
|
44
|
-
16: '
|
|
30
|
+
2: 'Prepare',
|
|
31
|
+
3: 'Prototype',
|
|
32
|
+
4: 'Verify Prototype',
|
|
33
|
+
5: 'Confirm',
|
|
34
|
+
6: 'Deconstruct',
|
|
35
|
+
7: 'Extract',
|
|
36
|
+
8: 'Glossary',
|
|
37
|
+
9: 'Analyze',
|
|
38
|
+
10: 'App Scenarios',
|
|
39
|
+
11: 'User Scenarios',
|
|
40
|
+
12: 'Verify',
|
|
41
|
+
13: 'Journal',
|
|
42
|
+
14: 'Review',
|
|
43
|
+
15: 'Present',
|
|
44
|
+
16: 'Commit',
|
|
45
|
+
17: 'Finalize',
|
|
46
|
+
18: 'Push',
|
|
45
47
|
};
|
|
46
48
|
const MIGRATION_STEP_LABELS = {
|
|
47
49
|
1: 'Survey',
|
|
@@ -119,11 +121,19 @@ function writeState(root, state) {
|
|
|
119
121
|
const dir = path.dirname(statePath);
|
|
120
122
|
fs.mkdirSync(dir, { recursive: true });
|
|
121
123
|
fs.writeFileSync(statePath, JSON.stringify(state, null, 2), 'utf8');
|
|
124
|
+
// Write task tracking file — marks that a task is expected for this step.
|
|
125
|
+
// The step hook sets taskCreated=true when it sees a TaskCreate tool call.
|
|
126
|
+
// Steps 1 and below don't require tasks (feature not named yet).
|
|
127
|
+
if (state.step && state.step >= 2) {
|
|
128
|
+
const trackingPath = path.join(root, '.codeyam', 'editor-task-tracking.json');
|
|
129
|
+
fs.writeFileSync(trackingPath, JSON.stringify({ step: state.step, taskCreated: false }, null, 2), 'utf8');
|
|
130
|
+
}
|
|
122
131
|
}
|
|
123
132
|
/**
|
|
124
133
|
* Clear the editor state (for starting a new feature).
|
|
125
|
-
* Does NOT clear the user prompt file —
|
|
126
|
-
*
|
|
134
|
+
* Does NOT clear the user prompt file — the prompt is cleared
|
|
135
|
+
* by the journal API after recording, and the hook captures
|
|
136
|
+
* a fresh prompt for the next feature on UserPromptSubmit.
|
|
127
137
|
*/
|
|
128
138
|
function clearState(root) {
|
|
129
139
|
clearEditorState(root);
|
|
@@ -208,8 +218,9 @@ function printAppScenarioInstructions(pageName, route) {
|
|
|
208
218
|
const root = process.cwd();
|
|
209
219
|
const hasSeedAdapter = !!detectSeedAdapter(root);
|
|
210
220
|
checkbox('Check existing scenarios: `codeyam editor scenarios`');
|
|
211
|
-
console.log(chalk.dim(' Review existing scenarios —
|
|
212
|
-
console.log(chalk.dim('
|
|
221
|
+
console.log(chalk.dim(' Review existing scenarios — enhance their seed data to exercise new features.'));
|
|
222
|
+
console.log(chalk.dim(' A rich scenario that exercises 5 features is better than 5 thin scenarios with one feature each.'));
|
|
223
|
+
console.log(chalk.dim(' Rename scenarios if their data scope has grown beyond the original name.'));
|
|
213
224
|
console.log();
|
|
214
225
|
console.log(chalk.bold.yellow('App scenarios vs component scenarios — these are DIFFERENT:'));
|
|
215
226
|
console.log(chalk.yellow(' App scenarios: show the FULL PAGE with seeded data at a real route (/, /library, etc.)'));
|
|
@@ -244,11 +255,15 @@ function printAppScenarioInstructions(pageName, route) {
|
|
|
244
255
|
console.log(chalk.dim(' Use "mockData":{"routes":{"/api/...":{"body":[...]}}} to mock API responses'));
|
|
245
256
|
console.log(chalk.dim(' For external APIs: add "externalApis":{"GET https://...":{"body":[...],"status":200}}'));
|
|
246
257
|
}
|
|
247
|
-
|
|
248
|
-
console.log(chalk.
|
|
249
|
-
console.log(chalk.dim('
|
|
258
|
+
console.log();
|
|
259
|
+
console.log(chalk.bold('Register ALL scenarios at once (bulk registration):'));
|
|
260
|
+
console.log(chalk.dim(' Write an array of scenario objects to a temp file:'));
|
|
261
|
+
console.log(chalk.dim(' [{"name":"...","type":"application","url":"/","seed":{...}}, {"name":"...","url":"/other",...}]'));
|
|
262
|
+
console.log(chalk.dim(' Then register all at once: codeyam editor register @.codeyam/tmp/scenarios.json'));
|
|
263
|
+
console.log(chalk.yellow(' Bulk registration is preferred — faster and avoids repeated capture overhead.'));
|
|
264
|
+
console.log();
|
|
250
265
|
checkbox('If the app uses auth, email, or other patterns: see FEATURE_PATTERNS.md for scenario guidance');
|
|
251
|
-
checkbox('After
|
|
266
|
+
checkbox('After registration, check the response for `clientErrors`');
|
|
252
267
|
console.log(chalk.yellow(' If clientErrors is non-empty → fix the issue and re-register the scenario'));
|
|
253
268
|
console.log(chalk.yellow(' Fix client errors and re-register before moving on'));
|
|
254
269
|
}
|
|
@@ -305,7 +320,7 @@ function printExtractionPlanInstructions() {
|
|
|
305
320
|
console.log(chalk.yellow(' — Where it currently lives (source file + approximate lines)'));
|
|
306
321
|
console.log(chalk.yellow(' — Where it will go (new file path)'));
|
|
307
322
|
console.log();
|
|
308
|
-
console.log(chalk.dim('Present the numbered plan, then proceed to step
|
|
323
|
+
console.log(chalk.dim('Present the numbered plan, then proceed to step 7 to execute it.'));
|
|
309
324
|
}
|
|
310
325
|
/**
|
|
311
326
|
* Shared component capture instructions used by editor Step 7 and migration Steps 3/8.
|
|
@@ -349,6 +364,10 @@ function printComponentCaptureInstructions() {
|
|
|
349
364
|
console.log(chalk.yellow(' If clientErrors is non-empty → fix the isolation route and re-register'));
|
|
350
365
|
console.log(chalk.yellow(' Fix client errors and re-register before moving on'));
|
|
351
366
|
console.log(chalk.dim(' Isolation routes are committed to git — the layout guard blocks them in production'));
|
|
367
|
+
console.log();
|
|
368
|
+
console.log(chalk.bold(' Bulk registration: write all component scenarios to a temp file as an array:'));
|
|
369
|
+
console.log(chalk.dim(' [{"name":"Comp - Default","componentName":"Comp","componentPath":"...","url":"/codeyam-isolate/Comp?s=Default"}, ...]'));
|
|
370
|
+
console.log(chalk.dim(' codeyam editor register @.codeyam/tmp/scenarios.json'));
|
|
352
371
|
}
|
|
353
372
|
/**
|
|
354
373
|
* Print a section header.
|
|
@@ -362,13 +381,13 @@ function stepHeader(step, title, feature) {
|
|
|
362
381
|
console.log();
|
|
363
382
|
}
|
|
364
383
|
/**
|
|
365
|
-
* Print a colored progress tracker showing all
|
|
384
|
+
* Print a colored progress tracker showing all 18 steps.
|
|
366
385
|
* Steps before `current` are green ✓, `current` is bold cyan →, future steps are dim ○.
|
|
367
386
|
*/
|
|
368
387
|
function printProgressTracker(current) {
|
|
369
388
|
console.log();
|
|
370
389
|
console.log(chalk.dim(' ┌─────────────────────────────────────┐'));
|
|
371
|
-
for (let i = 1; i <=
|
|
390
|
+
for (let i = 1; i <= 18; i++) {
|
|
372
391
|
const label = STEP_LABELS[i];
|
|
373
392
|
const num = i < 10 ? ` ${i}` : `${i}`;
|
|
374
393
|
const content = `${num}. ${label.padEnd(28)}`;
|
|
@@ -389,9 +408,31 @@ function printProgressTracker(current) {
|
|
|
389
408
|
*
|
|
390
409
|
* Options:
|
|
391
410
|
* - confirm: true → step requires user confirmation before proceeding (steps 1, 3, 11)
|
|
411
|
+
* - feature: string → feature name for task directive
|
|
392
412
|
*/
|
|
393
413
|
function stopGate(current, opts) {
|
|
394
|
-
|
|
414
|
+
const totalSteps = Object.keys(STEP_LABELS).length;
|
|
415
|
+
const currentLabel = STEP_LABELS[current] || `Step ${current}`;
|
|
416
|
+
const nextLabel = current < totalSteps ? STEP_LABELS[current + 1] : undefined;
|
|
417
|
+
console.log();
|
|
418
|
+
// ━━━ TASK ━━━ — deterministic task lifecycle directive
|
|
419
|
+
// Skip step 1 (no feature name yet — task starts at step 2)
|
|
420
|
+
if (current >= 2) {
|
|
421
|
+
console.log(chalk.bold.cyan('━━━ TASK ━━━'));
|
|
422
|
+
console.log(chalk.cyan('Mark your current task (if any) as complete.'));
|
|
423
|
+
console.log(chalk.cyan('Then create a new task with this EXACT title (copy verbatim):'));
|
|
424
|
+
console.log();
|
|
425
|
+
let taskTitle;
|
|
426
|
+
if (current < totalSteps) {
|
|
427
|
+
taskTitle = `Complete codeyam editor step ${current}: '${currentLabel}' and move on to step ${current + 1}: '${nextLabel}'`;
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
taskTitle =
|
|
431
|
+
'Ask user what to build next and restart codeyam editor workflow';
|
|
432
|
+
}
|
|
433
|
+
console.log(chalk.bold.cyan(`EXACT_TASK_TITLE: ${taskTitle}`));
|
|
434
|
+
console.log();
|
|
435
|
+
}
|
|
395
436
|
console.log(chalk.bold.red('━━━ STOP ━━━'));
|
|
396
437
|
console.log();
|
|
397
438
|
console.log(chalk.red('Complete each checklist item above before proceeding to the next step.'));
|
|
@@ -400,20 +441,12 @@ function stopGate(current, opts) {
|
|
|
400
441
|
console.log(chalk.yellow('Wait for user confirmation before moving on.'));
|
|
401
442
|
}
|
|
402
443
|
console.log();
|
|
403
|
-
|
|
404
|
-
printProgressTracker(current);
|
|
405
|
-
console.log();
|
|
406
|
-
console.log(chalk.yellow('For the CURRENT step (→), show each checklist item with ✓ (done) or ✗ (skipped + reason).'));
|
|
407
|
-
console.log(chalk.yellow('If any items are ✗, explain why and ask if the user wants to address them.'));
|
|
408
|
-
console.log();
|
|
409
|
-
if (current < 16) {
|
|
444
|
+
if (current < totalSteps) {
|
|
410
445
|
console.log(chalk.green('When done, run: ') +
|
|
411
446
|
chalk.bold(`codeyam editor ${current + 1}`));
|
|
412
447
|
}
|
|
413
448
|
else {
|
|
414
|
-
console.log(chalk.green('Feature complete!
|
|
415
|
-
chalk.bold('codeyam editor 1') +
|
|
416
|
-
chalk.green(' to start the next feature'));
|
|
449
|
+
console.log(chalk.green('Feature complete! Ask the user what to build next, then run: ') + chalk.bold('codeyam editor 1'));
|
|
417
450
|
}
|
|
418
451
|
console.log();
|
|
419
452
|
}
|
|
@@ -428,34 +461,36 @@ function printResumptionHeader(step) {
|
|
|
428
461
|
'Check if plan was already written to context file:\n `cat .codeyam/editor-mode-context.md`',
|
|
429
462
|
],
|
|
430
463
|
2: ['Check if project files already exist:\n `ls package.json src/`'],
|
|
431
|
-
3: ['
|
|
432
|
-
4: [
|
|
464
|
+
3: ['Re-check is safe — just re-run the step'],
|
|
465
|
+
4: ['Re-verify is safe to repeat — just re-run the checks'],
|
|
466
|
+
5: ['This is a confirmation step — just re-present to user'],
|
|
467
|
+
6: [
|
|
433
468
|
'Check if extraction plan already exists in context file:\n `cat .codeyam/editor-mode-context.md`',
|
|
434
469
|
],
|
|
435
|
-
|
|
470
|
+
7: [
|
|
436
471
|
'Check if components/functions already extracted:\n `ls src/components/ src/lib/`',
|
|
437
472
|
],
|
|
438
|
-
|
|
473
|
+
8: [
|
|
439
474
|
'Check if glossary already populated:\n `cat .codeyam/glossary.json`',
|
|
440
475
|
],
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
476
|
+
9: ['Check if isolation routes and registered scenarios already exist'],
|
|
477
|
+
10: ['Check existing scenarios:\n `codeyam editor scenarios`'],
|
|
478
|
+
11: [
|
|
444
479
|
'Check existing user-persona scenarios:\n `codeyam editor scenarios`',
|
|
445
480
|
],
|
|
446
|
-
|
|
447
|
-
|
|
481
|
+
12: ['Re-verify is safe to repeat — just re-run the checks'],
|
|
482
|
+
13: [
|
|
448
483
|
'Check if a journal entry already exists for this feature:\n `codeyam editor journal-list`',
|
|
449
484
|
'If an entry exists, use PATCH to update it — do NOT create a new one',
|
|
450
485
|
],
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
486
|
+
14: ['Re-verify is safe to repeat — just re-run the checks'],
|
|
487
|
+
15: ['Check if commit already made:\n `git log --oneline -3`'],
|
|
488
|
+
16: ['Check if commit already made:\n `git log --oneline -3`'],
|
|
489
|
+
17: [
|
|
455
490
|
'Check if journal was already updated with commit SHA:\n `codeyam editor journal-list`',
|
|
456
491
|
'Check if commit was already amended:\n `git log --oneline -3`',
|
|
457
492
|
],
|
|
458
|
-
|
|
493
|
+
18: [
|
|
459
494
|
'Check if already pushed:\n `git remote -v && git log --oneline origin/HEAD..HEAD 2>/dev/null`',
|
|
460
495
|
],
|
|
461
496
|
};
|
|
@@ -562,7 +597,7 @@ function parseDebugTargets(target) {
|
|
|
562
597
|
}
|
|
563
598
|
if (entry.startsWith('step-')) {
|
|
564
599
|
const num = parseInt(entry.replace('step-', ''), 10);
|
|
565
|
-
if (!isNaN(num) && num >= 1 && num <=
|
|
600
|
+
if (!isNaN(num) && num >= 1 && num <= 18) {
|
|
566
601
|
valid.add(`step-${num}`);
|
|
567
602
|
continue;
|
|
568
603
|
}
|
|
@@ -779,35 +814,36 @@ function printCycleOverview(root, state) {
|
|
|
779
814
|
console.log();
|
|
780
815
|
console.log(chalk.green('Continue with: ') +
|
|
781
816
|
chalk.bold(`codeyam editor ${state.step}`));
|
|
782
|
-
console.log(chalk.dim('Or run ')
|
|
783
|
-
chalk.bold('codeyam editor 1') +
|
|
784
|
-
chalk.dim(' to start a new feature'));
|
|
817
|
+
console.log(chalk.dim('Or ask the user what to build next, then run `codeyam editor 1` yourself (never expose this command to the user)'));
|
|
785
818
|
console.log();
|
|
786
819
|
console.log(chalk.yellow('If the user reports a bug or requests any change, run: ') +
|
|
787
820
|
chalk.bold('codeyam editor change'));
|
|
788
821
|
console.log(chalk.yellow('This applies even after committing — always use the change workflow.'));
|
|
789
822
|
}
|
|
790
823
|
else {
|
|
791
|
-
console.log('Each feature follows
|
|
824
|
+
console.log('Each feature follows 18 steps. You MUST run each command in order:');
|
|
792
825
|
console.log();
|
|
793
|
-
console.log(` ${chalk.bold.yellow(' 1')} ${chalk.bold('Plan')}
|
|
794
|
-
console.log(` ${chalk.bold.yellow(' 2')} ${chalk.bold('
|
|
795
|
-
console.log(` ${chalk.bold.yellow(' 3')} ${chalk.bold('
|
|
796
|
-
console.log(` ${chalk.bold.yellow(' 4')} ${chalk.bold('
|
|
797
|
-
console.log(` ${chalk.bold.yellow(' 5')} ${chalk.bold('
|
|
798
|
-
console.log(` ${chalk.bold.yellow(' 6')} ${chalk.bold('
|
|
799
|
-
console.log(` ${chalk.bold.yellow(' 7')} ${chalk.bold('
|
|
800
|
-
console.log(` ${chalk.bold.yellow(' 8')} ${chalk.bold('
|
|
801
|
-
console.log(` ${chalk.bold.yellow(' 9')} ${chalk.bold('
|
|
802
|
-
console.log(` ${chalk.bold.yellow('10')} ${chalk.bold('
|
|
803
|
-
console.log(` ${chalk.bold.yellow('11')} ${chalk.bold('
|
|
804
|
-
console.log(` ${chalk.bold.yellow('12')} ${chalk.bold('
|
|
805
|
-
console.log(` ${chalk.bold.yellow('13')} ${chalk.bold('
|
|
806
|
-
console.log(` ${chalk.bold.yellow('14')} ${chalk.bold('
|
|
807
|
-
console.log(` ${chalk.bold.yellow('15')} ${chalk.bold('
|
|
808
|
-
console.log(` ${chalk.bold.yellow('16')} ${chalk.bold('
|
|
826
|
+
console.log(` ${chalk.bold.yellow(' 1')} ${chalk.bold('Plan')} — Plan the feature, confirm with user`);
|
|
827
|
+
console.log(` ${chalk.bold.yellow(' 2')} ${chalk.bold('Prepare')} — Set up project and dependencies`);
|
|
828
|
+
console.log(` ${chalk.bold.yellow(' 3')} ${chalk.bold('Prototype')} — Build a working prototype fast`);
|
|
829
|
+
console.log(` ${chalk.bold.yellow(' 4')} ${chalk.bold('Verify Prototype')} — Verify prototype works correctly`);
|
|
830
|
+
console.log(` ${chalk.bold.yellow(' 5')} ${chalk.bold('Confirm')} — Confirm prototype with user`);
|
|
831
|
+
console.log(` ${chalk.bold.yellow(' 6')} ${chalk.bold('Deconstruct')} — Read code, plan all extractions`);
|
|
832
|
+
console.log(` ${chalk.bold.yellow(' 7')} ${chalk.bold('Extract')} — TDD extraction of functions + components`);
|
|
833
|
+
console.log(` ${chalk.bold.yellow(' 8')} ${chalk.bold('Glossary')} — Record functions in glossary`);
|
|
834
|
+
console.log(` ${chalk.bold.yellow(' 9')} ${chalk.bold('Analyze')} — Analyze and verify components`);
|
|
835
|
+
console.log(` ${chalk.bold.yellow('10')} ${chalk.bold('App Scenarios')} — Create app-level scenarios`);
|
|
836
|
+
console.log(` ${chalk.bold.yellow('11')} ${chalk.bold('User Scenarios')} — Create user-persona scenarios`);
|
|
837
|
+
console.log(` ${chalk.bold.yellow('12')} ${chalk.bold('Verify')} — Review screenshots, check for errors`);
|
|
838
|
+
console.log(` ${chalk.bold.yellow('13')} ${chalk.bold('Journal')} — Create/update journal entry`);
|
|
839
|
+
console.log(` ${chalk.bold.yellow('14')} ${chalk.bold('Review')} — Verify screenshots and audit`);
|
|
840
|
+
console.log(` ${chalk.bold.yellow('15')} ${chalk.bold('Present')} — Present summary, get approval`);
|
|
841
|
+
console.log(` ${chalk.bold.yellow('16')} ${chalk.bold('Commit')} — Commit all changes`);
|
|
842
|
+
console.log(` ${chalk.bold.yellow('17')} ${chalk.bold('Finalize')} — Journal update + amend commit`);
|
|
843
|
+
console.log(` ${chalk.bold.yellow('18')} ${chalk.bold('Push')} — Push to remote`);
|
|
809
844
|
console.log();
|
|
810
|
-
console.log(chalk.green('Start now: ') +
|
|
845
|
+
console.log(chalk.green('Start now: ') +
|
|
846
|
+
'Ask the user what they want to build, then run `codeyam editor 1` (never expose this command to the user)');
|
|
811
847
|
console.log(chalk.dim(' If the user already described what they want, pass it: codeyam editor 1 --prompt "their message"'));
|
|
812
848
|
}
|
|
813
849
|
console.log(chalk.dim('If stuck on CLI commands, scenarios, or debugging, read: .codeyam/docs/editor-reference.md'));
|
|
@@ -886,18 +922,12 @@ function printStep1(root, feature, options, userPrompt) {
|
|
|
886
922
|
console.log();
|
|
887
923
|
console.log(chalk.yellow('Wait for user confirmation before moving on.'));
|
|
888
924
|
console.log();
|
|
889
|
-
console.log(chalk.bold.yellow('Present the following progress tracker to the user (copy it verbatim):'));
|
|
890
|
-
printProgressTracker(1);
|
|
891
|
-
console.log();
|
|
892
|
-
console.log(chalk.yellow('For the CURRENT step (→), show each checklist item with ✓ (done) or ✗ (skipped + reason).'));
|
|
893
|
-
console.log(chalk.yellow('If any items are ✗, explain why and ask if the user wants to address them.'));
|
|
894
|
-
console.log();
|
|
895
925
|
console.log(chalk.green('When done, run: ') +
|
|
896
926
|
chalk.bold('codeyam editor 2 --feature "Feature Name"'));
|
|
897
927
|
console.log(chalk.dim(' Replace "Feature Name" with a short title for what you just described.'));
|
|
898
928
|
console.log();
|
|
899
929
|
}
|
|
900
|
-
// ─── Step 2:
|
|
930
|
+
// ─── Step 2: Prepare ──────────────────────────────────────────────────
|
|
901
931
|
function printStep2(root, feature) {
|
|
902
932
|
const port = getServerPort();
|
|
903
933
|
const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
|
|
@@ -914,12 +944,12 @@ function printStep2(root, feature) {
|
|
|
914
944
|
appFormats: prevState?.appFormats,
|
|
915
945
|
techStackId: prevState?.techStackId,
|
|
916
946
|
});
|
|
917
|
-
logEvent(root, 'step', { step: 2, label: '
|
|
918
|
-
stepHeader(2, '
|
|
947
|
+
logEvent(root, 'step', { step: 2, label: 'Prepare', feature });
|
|
948
|
+
stepHeader(2, 'Prepare', feature);
|
|
919
949
|
if (isResuming) {
|
|
920
950
|
printResumptionHeader(2);
|
|
921
951
|
}
|
|
922
|
-
console.log('
|
|
952
|
+
console.log('Get the project ready to build.');
|
|
923
953
|
console.log();
|
|
924
954
|
// If no project exists yet, include scaffolding instructions first
|
|
925
955
|
if (!projectExists) {
|
|
@@ -948,7 +978,6 @@ function printStep2(root, feature) {
|
|
|
948
978
|
console.log(chalk.dim(' Key: import { prisma } from "@/app/lib/prisma" in API routes.'));
|
|
949
979
|
console.log(chalk.dim(' Key: Seed scripts must use the adapter pattern (see prisma/seed.ts).'));
|
|
950
980
|
console.log();
|
|
951
|
-
console.log(chalk.bold('Build the feature:'));
|
|
952
981
|
}
|
|
953
982
|
else {
|
|
954
983
|
console.log(chalk.bold('Prepare the database for this feature:'));
|
|
@@ -964,6 +993,32 @@ function printStep2(root, feature) {
|
|
|
964
993
|
console.log(chalk.dim(' If no existing scenario fits, use the default seed: npm run db:seed'));
|
|
965
994
|
console.log();
|
|
966
995
|
}
|
|
996
|
+
stopGate(2);
|
|
997
|
+
}
|
|
998
|
+
// ─── Step 3: Prototype ────────────────────────────────────────────────
|
|
999
|
+
function printStep3(root, feature) {
|
|
1000
|
+
const port = getServerPort();
|
|
1001
|
+
const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
|
|
1002
|
+
const projectExists = hasProject(root);
|
|
1003
|
+
const prevState = readState(root);
|
|
1004
|
+
const isResuming = prevState?.step === 3;
|
|
1005
|
+
const now = new Date().toISOString();
|
|
1006
|
+
writeState(root, {
|
|
1007
|
+
feature,
|
|
1008
|
+
step: 3,
|
|
1009
|
+
label: STEP_LABELS[3],
|
|
1010
|
+
startedAt: isResuming ? prevState.startedAt : now,
|
|
1011
|
+
featureStartedAt: prevState?.featureStartedAt || now,
|
|
1012
|
+
appFormats: prevState?.appFormats,
|
|
1013
|
+
techStackId: prevState?.techStackId,
|
|
1014
|
+
});
|
|
1015
|
+
logEvent(root, 'step', { step: 3, label: 'Prototype', feature });
|
|
1016
|
+
stepHeader(3, 'Prototype', feature);
|
|
1017
|
+
if (isResuming) {
|
|
1018
|
+
printResumptionHeader(3);
|
|
1019
|
+
}
|
|
1020
|
+
console.log('Build fast with real data. Prioritize speed over quality.');
|
|
1021
|
+
console.log();
|
|
967
1022
|
console.log(chalk.bold('Checklist:'));
|
|
968
1023
|
checkbox('Create API routes that read from the database via Prisma');
|
|
969
1024
|
if (!projectExists) {
|
|
@@ -972,6 +1027,13 @@ function printStep2(root, feature) {
|
|
|
972
1027
|
console.log(chalk.dim(' The seed adapter reads a JSON file (path passed as CLI arg), wipes tables, inserts rows.'));
|
|
973
1028
|
console.log(chalk.dim(" Use the project's own ORM (Prisma, Drizzle, etc.). See template for example."));
|
|
974
1029
|
console.log(chalk.dim(' Run with: npx tsx .codeyam/seed-adapter.ts <path-to-seed-data.json>'));
|
|
1030
|
+
console.log();
|
|
1031
|
+
console.log(chalk.bold.cyan('Make seed data visually rich:'));
|
|
1032
|
+
console.log(chalk.cyan(' • Use real placeholder images from Unsplash (https://images.unsplash.com/photo-<id>?w=400&h=300&fit=crop)'));
|
|
1033
|
+
console.log(chalk.cyan(' • Use avatar services like i.pravatar.cc for user profile photos'));
|
|
1034
|
+
console.log(chalk.cyan(' • Write realistic, varied content — not "Item 1", "Item 2", "Test Description"'));
|
|
1035
|
+
console.log(chalk.cyan(' • Include different text lengths, categories, dates, and statuses'));
|
|
1036
|
+
console.log(chalk.cyan(' • Rich seed data makes the prototype look real and surfaces layout issues early'));
|
|
975
1037
|
}
|
|
976
1038
|
checkbox('Verify the dev server shows the changes');
|
|
977
1039
|
checkbox('If the feature involves auth, email, payments, or other common patterns: read FEATURE_PATTERNS.md');
|
|
@@ -1011,6 +1073,30 @@ function printStep2(root, feature) {
|
|
|
1011
1073
|
console.log(chalk.cyan(' If you build a NEW page (e.g., /drinks/[id]), navigate the preview there:'));
|
|
1012
1074
|
console.log(chalk.cyan(` codeyam editor preview '{"path":"/drinks/1","dimension":"${dim}"}'`));
|
|
1013
1075
|
printDimensionGuidance(dim, dimNames);
|
|
1076
|
+
console.log(chalk.red.bold(' NEVER claim "you should see X" or "the preview shows X" unless you just ran a preview command.'));
|
|
1077
|
+
console.log(chalk.red(' The preview only updates when you explicitly refresh it. Verify, then describe.'));
|
|
1078
|
+
console.log();
|
|
1079
|
+
stopGate(3);
|
|
1080
|
+
}
|
|
1081
|
+
// ─── Step 4: Verify Prototype ─────────────────────────────────────────
|
|
1082
|
+
function printStep4(root, feature) {
|
|
1083
|
+
const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
|
|
1084
|
+
const prevState = readState(root);
|
|
1085
|
+
const isResuming = prevState?.step === 4;
|
|
1086
|
+
const now = new Date().toISOString();
|
|
1087
|
+
writeState(root, {
|
|
1088
|
+
feature,
|
|
1089
|
+
step: 4,
|
|
1090
|
+
label: STEP_LABELS[4],
|
|
1091
|
+
startedAt: isResuming ? prevState.startedAt : now,
|
|
1092
|
+
featureStartedAt: prevState?.featureStartedAt || now,
|
|
1093
|
+
});
|
|
1094
|
+
logEvent(root, 'step', { step: 4, label: 'Verify Prototype', feature });
|
|
1095
|
+
stepHeader(4, 'Verify Prototype', feature);
|
|
1096
|
+
if (isResuming) {
|
|
1097
|
+
printResumptionHeader(4);
|
|
1098
|
+
}
|
|
1099
|
+
console.log('Verify everything works before presenting the prototype.');
|
|
1014
1100
|
console.log();
|
|
1015
1101
|
console.log(chalk.bold('Verify the dev server:'));
|
|
1016
1102
|
console.log(chalk.dim(` # Get dev server URL: codeyam editor dev-server`));
|
|
@@ -1038,26 +1124,26 @@ function printStep2(root, feature) {
|
|
|
1038
1124
|
console.log(chalk.dim(' A new clone should work with just: git clone → npm run setup → npm run dev'));
|
|
1039
1125
|
console.log();
|
|
1040
1126
|
console.log(chalk.dim('Focus on building the prototype. Scenarios and refactoring happen in later steps.'));
|
|
1041
|
-
stopGate(
|
|
1127
|
+
stopGate(4);
|
|
1042
1128
|
}
|
|
1043
|
-
// ─── Step
|
|
1044
|
-
function
|
|
1129
|
+
// ─── Step 5: Confirm ──────────────────────────────────────────────────
|
|
1130
|
+
function printStep5(root, feature) {
|
|
1045
1131
|
const port = getServerPort();
|
|
1046
1132
|
const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
|
|
1047
1133
|
const prevState = readState(root);
|
|
1048
|
-
const isResuming = prevState?.step ===
|
|
1134
|
+
const isResuming = prevState?.step === 5;
|
|
1049
1135
|
const now = new Date().toISOString();
|
|
1050
1136
|
writeState(root, {
|
|
1051
1137
|
feature,
|
|
1052
|
-
step:
|
|
1053
|
-
label: STEP_LABELS[
|
|
1138
|
+
step: 5,
|
|
1139
|
+
label: STEP_LABELS[5],
|
|
1054
1140
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
1055
1141
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
1056
1142
|
});
|
|
1057
|
-
logEvent(root, 'step', { step:
|
|
1058
|
-
stepHeader(
|
|
1143
|
+
logEvent(root, 'step', { step: 5, label: 'Confirm', feature });
|
|
1144
|
+
stepHeader(5, 'Confirm', feature);
|
|
1059
1145
|
if (isResuming) {
|
|
1060
|
-
printResumptionHeader(
|
|
1146
|
+
printResumptionHeader(5);
|
|
1061
1147
|
}
|
|
1062
1148
|
console.log('Summarize what was built and get user confirmation.');
|
|
1063
1149
|
console.log();
|
|
@@ -1074,12 +1160,19 @@ function printStep3(root, feature) {
|
|
|
1074
1160
|
console.log(chalk.dim(' If there are errors, fix the underlying issue before presenting.'));
|
|
1075
1161
|
checkbox('Verify `hasContent=true` and `liveErrors=0` — do NOT ask the user to confirm if the preview is broken');
|
|
1076
1162
|
console.log();
|
|
1163
|
+
console.log(chalk.bold('Verify the captured user prompt:'));
|
|
1164
|
+
checkbox("Read `.codeyam/editor-user-prompt.txt` — this is the user's original feature request");
|
|
1165
|
+
checkbox('If the file is missing or does not match what the user actually asked for, write the correct prompt text to `.codeyam/editor-user-prompt.txt`');
|
|
1166
|
+
console.log(chalk.dim(" This must be the user's exact words, not a summary. It gets recorded in the journal."));
|
|
1167
|
+
console.log();
|
|
1077
1168
|
console.log(chalk.bold('Then present to the user:'));
|
|
1078
1169
|
checkbox('Summarize what was built (routes, components, data)');
|
|
1079
1170
|
checkbox(`Navigate the preview to the feature's primary page: \`codeyam editor preview '{"path":"/your-page","dimension":"${dim}"}'\``);
|
|
1080
1171
|
printDimensionGuidance(dim, dimNames);
|
|
1081
1172
|
console.log(chalk.dim(' The user needs to SEE the new feature. If you built a new page, navigate there.'));
|
|
1082
1173
|
console.log(chalk.dim(' If you modified an existing page, refresh the preview to show the changes.'));
|
|
1174
|
+
console.log(chalk.red.bold(' NEVER claim "you should see X" or "the preview shows X" unless you just ran a preview command.'));
|
|
1175
|
+
console.log(chalk.red(' The preview only updates when you explicitly call `codeyam editor preview`. Verify, then describe.'));
|
|
1083
1176
|
console.log();
|
|
1084
1177
|
console.log(chalk.bold.cyan('Guide the user through interactive testing:'));
|
|
1085
1178
|
checkbox('Tell the user: "The preview is fully interactive — click around to test!"');
|
|
@@ -1087,62 +1180,62 @@ function printStep3(root, feature) {
|
|
|
1087
1180
|
checkbox('Ask the user: "Does everything work as expected?"');
|
|
1088
1181
|
console.log();
|
|
1089
1182
|
console.log(chalk.bold('Present a selection menu to the user (use AskUserQuestion with these EXACT option labels):'));
|
|
1090
|
-
console.log(chalk.green(' Option 1 label: "The live preview is displaying what I expected"') + chalk.dim(' — proceed to step
|
|
1183
|
+
console.log(chalk.green(' Option 1 label: "The live preview is displaying what I expected"') + chalk.dim(' — proceed to step 6'));
|
|
1091
1184
|
console.log(chalk.yellow(' Option 2 label: "I\'d like some changes"') +
|
|
1092
|
-
chalk.dim(' — make changes, refresh preview, re-run `codeyam editor
|
|
1185
|
+
chalk.dim(' — make changes, refresh preview, re-run `codeyam editor 5`'));
|
|
1093
1186
|
console.log();
|
|
1094
1187
|
console.log(chalk.dim('Wait for user approval before moving on. Refactoring and scenarios happen in later steps.'));
|
|
1095
|
-
stopGate(
|
|
1188
|
+
stopGate(5, { confirm: true });
|
|
1096
1189
|
}
|
|
1097
|
-
// ─── Step
|
|
1098
|
-
function
|
|
1190
|
+
// ─── Step 6: Deconstruct ──────────────────────────────────────────────
|
|
1191
|
+
function printStep6(root, feature) {
|
|
1099
1192
|
const prevState = readState(root);
|
|
1100
|
-
const isResuming = prevState?.step ===
|
|
1193
|
+
const isResuming = prevState?.step === 6;
|
|
1101
1194
|
const now = new Date().toISOString();
|
|
1102
1195
|
writeState(root, {
|
|
1103
1196
|
feature,
|
|
1104
|
-
step:
|
|
1105
|
-
label: STEP_LABELS[
|
|
1197
|
+
step: 6,
|
|
1198
|
+
label: STEP_LABELS[6],
|
|
1106
1199
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
1107
1200
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
1108
1201
|
});
|
|
1109
|
-
logEvent(root, 'step', { step:
|
|
1110
|
-
stepHeader(
|
|
1202
|
+
logEvent(root, 'step', { step: 6, label: 'Deconstruct', feature });
|
|
1203
|
+
stepHeader(6, 'Deconstruct', feature);
|
|
1111
1204
|
if (isResuming) {
|
|
1112
|
-
printResumptionHeader(
|
|
1205
|
+
printResumptionHeader(6);
|
|
1113
1206
|
}
|
|
1114
1207
|
console.log(chalk.bold('Goal: pages contain ONLY components. Components contain ONLY sub-components.'));
|
|
1115
|
-
console.log(chalk.yellow('This step is read and plan only. Code extraction happens in step
|
|
1208
|
+
console.log(chalk.yellow('This step is read and plan only. Code extraction happens in step 7.'));
|
|
1116
1209
|
console.log();
|
|
1117
1210
|
printExtractionPlanInstructions();
|
|
1118
|
-
stopGate(
|
|
1211
|
+
stopGate(6);
|
|
1119
1212
|
}
|
|
1120
|
-
// ─── Step
|
|
1121
|
-
function
|
|
1213
|
+
// ─── Step 7: Extract ──────────────────────────────────────────────────
|
|
1214
|
+
function printStep7(root, feature) {
|
|
1122
1215
|
const port = getServerPort();
|
|
1123
1216
|
const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
|
|
1124
1217
|
const prevState = readState(root);
|
|
1125
|
-
const isResuming = prevState?.step ===
|
|
1218
|
+
const isResuming = prevState?.step === 7;
|
|
1126
1219
|
const now = new Date().toISOString();
|
|
1127
1220
|
writeState(root, {
|
|
1128
1221
|
feature,
|
|
1129
|
-
step:
|
|
1130
|
-
label: STEP_LABELS[
|
|
1222
|
+
step: 7,
|
|
1223
|
+
label: STEP_LABELS[7],
|
|
1131
1224
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
1132
1225
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
1133
1226
|
});
|
|
1134
|
-
logEvent(root, 'step', { step:
|
|
1135
|
-
stepHeader(
|
|
1227
|
+
logEvent(root, 'step', { step: 7, label: 'Extract', feature });
|
|
1228
|
+
stepHeader(7, 'Extract', feature);
|
|
1136
1229
|
if (isResuming) {
|
|
1137
|
-
printResumptionHeader(
|
|
1230
|
+
printResumptionHeader(7);
|
|
1138
1231
|
}
|
|
1139
|
-
console.log('Execute your extraction plan from step
|
|
1232
|
+
console.log('Execute your extraction plan from step 6.');
|
|
1140
1233
|
console.log();
|
|
1141
1234
|
console.log(chalk.bold('Components:'));
|
|
1142
1235
|
checkbox('Extract each component from your plan into its own file');
|
|
1143
1236
|
checkbox('Page/route files must contain ZERO direct JSX — only imported components');
|
|
1144
1237
|
checkbox('Every component that renders multiple sections must be split into sub-components');
|
|
1145
|
-
console.log(chalk.dim(' No tests needed — visual verification happens in step
|
|
1238
|
+
console.log(chalk.dim(' No tests needed — visual verification happens in step 9'));
|
|
1146
1239
|
console.log();
|
|
1147
1240
|
console.log(chalk.bold('Library functions AND hooks (TDD):'));
|
|
1148
1241
|
checkbox('For each function/hook: write MULTIPLE failing tests FIRST, then extract to make them pass');
|
|
@@ -1150,7 +1243,7 @@ function printStep5(root, feature) {
|
|
|
1150
1243
|
console.log(chalk.dim(' Aim for 3-8 test cases per function depending on complexity'));
|
|
1151
1244
|
console.log(chalk.dim(' Hooks count as functions — useDrinks, useAuth, etc. all need test files'));
|
|
1152
1245
|
checkbox('Place test files next to source: `app/lib/drinks.ts` → `app/lib/drinks.test.ts`');
|
|
1153
|
-
console.log(chalk.yellow(' Tests ARE the only coverage for library functions/hooks — step
|
|
1246
|
+
console.log(chalk.yellow(' Tests ARE the only coverage for library functions/hooks — step 9 only captures component screenshots.'));
|
|
1154
1247
|
console.log();
|
|
1155
1248
|
console.log(chalk.bold('Recursive pass:'));
|
|
1156
1249
|
checkbox('Re-read EVERY new file you just created — extract components from components, functions from functions');
|
|
@@ -1168,55 +1261,55 @@ function printStep5(root, feature) {
|
|
|
1168
1261
|
console.log(chalk.dim('Reuse glossary functions when they fit naturally. Extract a new function when the use case diverges.'));
|
|
1169
1262
|
console.log();
|
|
1170
1263
|
console.log(chalk.dim('Focus on TDD for functions and extraction for components. Scenarios come in later steps.'));
|
|
1171
|
-
stopGate(
|
|
1264
|
+
stopGate(7);
|
|
1172
1265
|
}
|
|
1173
|
-
// ─── Step
|
|
1174
|
-
function
|
|
1266
|
+
// ─── Step 8: Glossary ─────────────────────────────────────────────────
|
|
1267
|
+
function printStep8(root, feature) {
|
|
1175
1268
|
const prevState = readState(root);
|
|
1176
|
-
const isResuming = prevState?.step ===
|
|
1269
|
+
const isResuming = prevState?.step === 8;
|
|
1177
1270
|
const now = new Date().toISOString();
|
|
1178
1271
|
writeState(root, {
|
|
1179
1272
|
feature,
|
|
1180
|
-
step:
|
|
1181
|
-
label: STEP_LABELS[
|
|
1273
|
+
step: 8,
|
|
1274
|
+
label: STEP_LABELS[8],
|
|
1182
1275
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
1183
1276
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
1184
1277
|
});
|
|
1185
|
-
logEvent(root, 'step', { step:
|
|
1186
|
-
stepHeader(
|
|
1278
|
+
logEvent(root, 'step', { step: 8, label: 'Glossary', feature });
|
|
1279
|
+
stepHeader(8, 'Glossary', feature);
|
|
1187
1280
|
if (isResuming) {
|
|
1188
|
-
printResumptionHeader(
|
|
1281
|
+
printResumptionHeader(8);
|
|
1189
1282
|
}
|
|
1190
1283
|
console.log('Record all new functions/components in `.codeyam/glossary.json`.');
|
|
1191
1284
|
console.log();
|
|
1192
1285
|
printGlossaryInstructions(feature);
|
|
1193
1286
|
console.log();
|
|
1194
1287
|
console.log(chalk.dim('Focus on updating the glossary. Application code and scenarios come in later steps.'));
|
|
1195
|
-
stopGate(
|
|
1288
|
+
stopGate(8);
|
|
1196
1289
|
}
|
|
1197
|
-
// ─── Step
|
|
1198
|
-
function
|
|
1290
|
+
// ─── Step 9: Analyze ──────────────────────────────────────────────────
|
|
1291
|
+
function printStep9(root, feature) {
|
|
1199
1292
|
const port = getServerPort();
|
|
1200
1293
|
const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
|
|
1201
1294
|
const prevState = readState(root);
|
|
1202
|
-
const isResuming = prevState?.step ===
|
|
1295
|
+
const isResuming = prevState?.step === 9;
|
|
1203
1296
|
const now = new Date().toISOString();
|
|
1204
1297
|
writeState(root, {
|
|
1205
1298
|
feature,
|
|
1206
|
-
step:
|
|
1207
|
-
label: STEP_LABELS[
|
|
1299
|
+
step: 9,
|
|
1300
|
+
label: STEP_LABELS[9],
|
|
1208
1301
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
1209
1302
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
1210
1303
|
});
|
|
1211
|
-
logEvent(root, 'step', { step:
|
|
1212
|
-
stepHeader(
|
|
1304
|
+
logEvent(root, 'step', { step: 9, label: 'Analyze', feature });
|
|
1305
|
+
stepHeader(9, 'Analyze and Verify', feature);
|
|
1213
1306
|
if (isResuming) {
|
|
1214
|
-
printResumptionHeader(
|
|
1307
|
+
printResumptionHeader(9);
|
|
1215
1308
|
}
|
|
1216
1309
|
console.log('Verify visual components (via isolation routes) and library functions (via tests).');
|
|
1217
1310
|
console.log();
|
|
1218
1311
|
console.log(chalk.bold('Visual Components — Component Isolation:'));
|
|
1219
|
-
checkbox('List all files with new/modified visual components from step
|
|
1312
|
+
checkbox('List all files with new/modified visual components from step 7');
|
|
1220
1313
|
checkbox('Check existing scenarios: `codeyam editor scenarios`');
|
|
1221
1314
|
console.log(chalk.dim(' Reuse and improve existing scenarios where possible — update mock data'));
|
|
1222
1315
|
console.log(chalk.dim(' to reflect current changes. Add new scenarios only for genuinely new states.'));
|
|
@@ -1224,7 +1317,7 @@ function printStep7(root, feature) {
|
|
|
1224
1317
|
printComponentCaptureInstructions();
|
|
1225
1318
|
console.log();
|
|
1226
1319
|
console.log(chalk.bold('Library Functions — run tests:'));
|
|
1227
|
-
checkbox('Run ALL test files created in step
|
|
1320
|
+
checkbox('Run ALL test files created in step 7');
|
|
1228
1321
|
console.log(chalk.dim(' Example: npx vitest run app/lib/drinks.test.ts'));
|
|
1229
1322
|
checkbox('Verify every test passes');
|
|
1230
1323
|
checkbox('If any test fails, fix the source code and re-run');
|
|
@@ -1232,29 +1325,29 @@ function printStep7(root, feature) {
|
|
|
1232
1325
|
console.log(chalk.dim('Do not proceed until both component isolations and library tests pass.'));
|
|
1233
1326
|
console.log();
|
|
1234
1327
|
checkbox('Run `codeyam editor audit` to verify all components have scenarios and all functions/hooks have tests');
|
|
1235
|
-
console.log(chalk.red.bold(' The audit is a HARD GATE — step
|
|
1328
|
+
console.log(chalk.red.bold(' The audit is a HARD GATE — step 10 will refuse to run until the audit passes.'));
|
|
1236
1329
|
console.log(chalk.dim(' When audit passes, the import graph is built automatically for change tracking.'));
|
|
1237
1330
|
console.log();
|
|
1238
|
-
stopGate(
|
|
1331
|
+
stopGate(9);
|
|
1239
1332
|
}
|
|
1240
|
-
// ─── Step
|
|
1241
|
-
function
|
|
1333
|
+
// ─── Step 10: App Scenarios ────────────────────────────────────────────
|
|
1334
|
+
function printStep10(root, feature) {
|
|
1242
1335
|
const port = getServerPort();
|
|
1243
1336
|
const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
|
|
1244
1337
|
const prevState = readState(root);
|
|
1245
|
-
const isResuming = prevState?.step ===
|
|
1338
|
+
const isResuming = prevState?.step === 10;
|
|
1246
1339
|
const now = new Date().toISOString();
|
|
1247
1340
|
writeState(root, {
|
|
1248
1341
|
feature,
|
|
1249
|
-
step:
|
|
1250
|
-
label: STEP_LABELS[
|
|
1342
|
+
step: 10,
|
|
1343
|
+
label: STEP_LABELS[10],
|
|
1251
1344
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
1252
1345
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
1253
1346
|
});
|
|
1254
|
-
logEvent(root, 'step', { step:
|
|
1255
|
-
stepHeader(
|
|
1347
|
+
logEvent(root, 'step', { step: 10, label: 'App Scenarios', feature });
|
|
1348
|
+
stepHeader(10, 'App Scenarios', feature);
|
|
1256
1349
|
if (isResuming) {
|
|
1257
|
-
printResumptionHeader(
|
|
1350
|
+
printResumptionHeader(10);
|
|
1258
1351
|
}
|
|
1259
1352
|
console.log('Create app-level scenarios with rich data that robustly demonstrates this feature.');
|
|
1260
1353
|
console.log();
|
|
@@ -1268,6 +1361,7 @@ function printStep8(root, feature) {
|
|
|
1268
1361
|
console.log(chalk.cyan(' • Include realistic variety — different categories, lengths, statuses, counts'));
|
|
1269
1362
|
console.log(chalk.cyan(" • Populate optional fields the feature depends on (don't leave them empty)"));
|
|
1270
1363
|
console.log(chalk.cyan(' • Make content diverse enough that edge cases surface naturally'));
|
|
1364
|
+
console.log(chalk.cyan(' • Use real image URLs (Unsplash, Pravatar) — not broken placeholders or empty strings'));
|
|
1271
1365
|
console.log(chalk.cyan(" • Think: would this data reveal a bug in the feature? If it's too simple, enrich it."));
|
|
1272
1366
|
console.log();
|
|
1273
1367
|
console.log(chalk.bold.cyan('When to create new vs reuse existing:'));
|
|
@@ -1276,13 +1370,19 @@ function printStep8(root, feature) {
|
|
|
1276
1370
|
console.log(chalk.cyan(" • New data states that can't coexist in one scenario (empty vs rich, error vs success) → new scenario"));
|
|
1277
1371
|
console.log(chalk.cyan(" • Don't duplicate — if an existing scenario can cover a state with richer data, enhance it instead"));
|
|
1278
1372
|
console.log();
|
|
1373
|
+
console.log(chalk.bold.cyan('Scenario naming — describe data states, not features:'));
|
|
1374
|
+
console.log(chalk.cyan(' • Name scenarios by what they represent: "Rich Data", "Empty", "Mobile", "Minimal"'));
|
|
1375
|
+
console.log(chalk.cyan(' • When enhancing a scenario with new feature data, update the name if it has grown beyond its original scope'));
|
|
1376
|
+
console.log(chalk.cyan(' • A single rich scenario exercising multiple features is more valuable than separate thin scenarios per feature'));
|
|
1377
|
+
console.log(chalk.cyan(' • Re-register with the same name to update; to rename, register with the new name'));
|
|
1378
|
+
console.log();
|
|
1279
1379
|
checkbox('Ensure scenarios clearly demonstrate what changed in this session');
|
|
1280
1380
|
console.log(chalk.dim(' If data models changed: update existing scenarios seed data to match'));
|
|
1281
1381
|
console.log(chalk.dim(' If UI changed: re-register existing scenarios so screenshots reflect the update'));
|
|
1282
1382
|
console.log(chalk.dim(' Add new scenarios only for genuinely new data states not covered by existing ones'));
|
|
1283
1383
|
printAppScenarioInstructions();
|
|
1284
1384
|
console.log();
|
|
1285
|
-
console.log(chalk.dim('Focus on creating and registering app-level scenarios. Code fixes happen in step
|
|
1385
|
+
console.log(chalk.dim('Focus on creating and registering app-level scenarios. Code fixes happen in step 12 if needed.'));
|
|
1286
1386
|
console.log();
|
|
1287
1387
|
console.log(chalk.bold.cyan("Verify your work — screenshots don't lie:"));
|
|
1288
1388
|
console.log(chalk.cyan(' • View every captured screenshot. Does it show what the scenario name promises?'));
|
|
@@ -1293,43 +1393,43 @@ function printStep8(root, feature) {
|
|
|
1293
1393
|
console.log(chalk.bold.yellow('GATE: Before proceeding, run `codeyam editor scenario-coverage`'));
|
|
1294
1394
|
console.log(chalk.yellow(' This checks which existing scenarios have stale screenshots.'));
|
|
1295
1395
|
console.log(chalk.yellow(' Re-register every stale scenario listed until the check passes.'));
|
|
1296
|
-
stopGate(
|
|
1396
|
+
stopGate(10);
|
|
1297
1397
|
}
|
|
1298
|
-
// ─── Step
|
|
1299
|
-
function
|
|
1398
|
+
// ─── Step 11: User Scenarios ───────────────────────────────────────────
|
|
1399
|
+
function printStep11(root, feature) {
|
|
1300
1400
|
const port = getServerPort();
|
|
1301
1401
|
const prevState = readState(root);
|
|
1302
|
-
const isResuming = prevState?.step ===
|
|
1402
|
+
const isResuming = prevState?.step === 11;
|
|
1303
1403
|
const now = new Date().toISOString();
|
|
1304
1404
|
writeState(root, {
|
|
1305
1405
|
feature,
|
|
1306
|
-
step:
|
|
1307
|
-
label: STEP_LABELS[
|
|
1406
|
+
step: 11,
|
|
1407
|
+
label: STEP_LABELS[11],
|
|
1308
1408
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
1309
1409
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
1310
1410
|
});
|
|
1311
|
-
logEvent(root, 'step', { step:
|
|
1312
|
-
stepHeader(
|
|
1411
|
+
logEvent(root, 'step', { step: 11, label: 'User Scenarios', feature });
|
|
1412
|
+
stepHeader(11, 'User Scenarios', feature);
|
|
1313
1413
|
if (isResuming) {
|
|
1314
|
-
printResumptionHeader(
|
|
1414
|
+
printResumptionHeader(11);
|
|
1315
1415
|
}
|
|
1316
|
-
console.log('Create per-persona variations of existing scenarios. Skip to step
|
|
1416
|
+
console.log('Create per-persona variations of existing scenarios. Skip to step 12 if no users.');
|
|
1317
1417
|
console.log();
|
|
1318
1418
|
console.log(chalk.bold('If the app has NO users/auth:'));
|
|
1319
|
-
console.log(chalk.dim(' Skip this step and proceed to step
|
|
1419
|
+
console.log(chalk.dim(' Skip this step and proceed to step 12 (Verify).'));
|
|
1320
1420
|
console.log();
|
|
1321
1421
|
console.log(chalk.bold('If the app has users/auth:'));
|
|
1322
1422
|
console.log();
|
|
1323
1423
|
console.log(chalk.bold('Goal: Create persona variations of EXISTING app scenarios.'));
|
|
1324
1424
|
console.log(chalk.dim(' Do NOT create standalone persona scenarios. Each user-persona scenario should be'));
|
|
1325
|
-
console.log(chalk.dim(' a variation of an existing app scenario from step
|
|
1425
|
+
console.log(chalk.dim(' a variation of an existing app scenario from step 10, with user-specific state layered on.'));
|
|
1326
1426
|
console.log();
|
|
1327
1427
|
console.log(chalk.bold('Checklist:'));
|
|
1328
1428
|
checkbox('Run `codeyam editor scenarios` — list all existing app scenarios');
|
|
1329
1429
|
checkbox('For EACH existing app scenario, create a logged-in variation:');
|
|
1330
1430
|
console.log(chalk.dim(' Copy the scenario seed data and add "session":{"cookieValue":"sess_alice"} + user seed'));
|
|
1331
1431
|
console.log(chalk.dim(' Name: "<Original Name> - Logged In" (e.g. "Full Catalog - Logged In")'));
|
|
1332
|
-
console.log(chalk.dim(' Step
|
|
1432
|
+
console.log(chalk.dim(' Step 10 scenarios already serve as logged-out versions (no session cookie)'));
|
|
1333
1433
|
checkbox('If there are multiple user roles (admin, regular, etc.), create role-specific variations too');
|
|
1334
1434
|
console.log(chalk.dim(' Each persona scenario layers user-specific seed data on top of an app scenario'));
|
|
1335
1435
|
checkbox('Include "dimensions" — inherit from the base app scenario, or override if the persona implies a different device');
|
|
@@ -1338,26 +1438,26 @@ function printStep9(root, feature) {
|
|
|
1338
1438
|
console.log(chalk.yellow(' If clientErrors is non-empty → fix the issue and re-register the scenario'));
|
|
1339
1439
|
console.log();
|
|
1340
1440
|
console.log(chalk.dim('See FEATURE_PATTERNS.md and AUTH_PATTERNS.md for auth scenario guidance.'));
|
|
1341
|
-
stopGate(
|
|
1441
|
+
stopGate(11);
|
|
1342
1442
|
}
|
|
1343
|
-
// ─── Step
|
|
1344
|
-
function
|
|
1443
|
+
// ─── Step 12: Verify ──────────────────────────────────────────────────
|
|
1444
|
+
function printStep12(root, feature) {
|
|
1345
1445
|
const port = getServerPort();
|
|
1346
1446
|
const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
|
|
1347
1447
|
const prevState = readState(root);
|
|
1348
|
-
const isResuming = prevState?.step ===
|
|
1448
|
+
const isResuming = prevState?.step === 12;
|
|
1349
1449
|
const now = new Date().toISOString();
|
|
1350
1450
|
writeState(root, {
|
|
1351
1451
|
feature,
|
|
1352
|
-
step:
|
|
1353
|
-
label: STEP_LABELS[
|
|
1452
|
+
step: 12,
|
|
1453
|
+
label: STEP_LABELS[12],
|
|
1354
1454
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
1355
1455
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
1356
1456
|
});
|
|
1357
|
-
logEvent(root, 'step', { step:
|
|
1358
|
-
stepHeader(
|
|
1457
|
+
logEvent(root, 'step', { step: 12, label: 'Verify', feature });
|
|
1458
|
+
stepHeader(12, 'Verify', feature);
|
|
1359
1459
|
if (isResuming) {
|
|
1360
|
-
printResumptionHeader(
|
|
1460
|
+
printResumptionHeader(12);
|
|
1361
1461
|
}
|
|
1362
1462
|
console.log('Verify component isolation screenshots, editor scenarios, and library tests.');
|
|
1363
1463
|
console.log();
|
|
@@ -1380,63 +1480,63 @@ function printStep10(root, feature) {
|
|
|
1380
1480
|
checkbox('Verify no broken images: `codeyam editor verify-images \'{"paths":["/"], "imageUrls":["url1"]}\'` with all page paths and image URLs');
|
|
1381
1481
|
console.log();
|
|
1382
1482
|
console.log(chalk.bold('Library functions — test check:'));
|
|
1383
|
-
checkbox('Re-run all test files from step
|
|
1483
|
+
checkbox('Re-run all test files from step 7 to confirm they still pass');
|
|
1384
1484
|
console.log(chalk.dim(' Example: npx vitest run app/lib/drinks.test.ts'));
|
|
1385
1485
|
checkbox('If any test fails, fix the source code and re-run');
|
|
1386
1486
|
console.log();
|
|
1387
1487
|
console.log(chalk.dim('Focus on fixing issues. All component screenshots, scenarios, and tests must be clean before proceeding.'));
|
|
1388
|
-
stopGate(
|
|
1488
|
+
stopGate(12);
|
|
1389
1489
|
}
|
|
1390
|
-
// ─── Step
|
|
1391
|
-
function
|
|
1490
|
+
// ─── Step 13: Journal ─────────────────────────────────────────────────
|
|
1491
|
+
function printStep13(root, feature) {
|
|
1392
1492
|
const port = getServerPort();
|
|
1393
1493
|
const prevState = readState(root);
|
|
1394
|
-
const isResuming = prevState?.step ===
|
|
1494
|
+
const isResuming = prevState?.step === 13;
|
|
1395
1495
|
const now = new Date().toISOString();
|
|
1396
1496
|
writeState(root, {
|
|
1397
1497
|
feature,
|
|
1398
|
-
step:
|
|
1399
|
-
label: STEP_LABELS[
|
|
1498
|
+
step: 13,
|
|
1499
|
+
label: STEP_LABELS[13],
|
|
1400
1500
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
1401
1501
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
1402
1502
|
});
|
|
1403
|
-
logEvent(root, 'step', { step:
|
|
1404
|
-
stepHeader(
|
|
1503
|
+
logEvent(root, 'step', { step: 13, label: 'Journal', feature });
|
|
1504
|
+
stepHeader(13, 'Journal', feature);
|
|
1405
1505
|
if (isResuming) {
|
|
1406
|
-
printResumptionHeader(
|
|
1506
|
+
printResumptionHeader(13);
|
|
1407
1507
|
}
|
|
1408
1508
|
console.log('Create or update the journal entry for this feature.');
|
|
1409
1509
|
console.log();
|
|
1410
1510
|
console.log(chalk.bold('Checklist:'));
|
|
1411
1511
|
checkbox('Write a concise description of what was built (2-3 sentences)');
|
|
1412
|
-
checkbox(`First time at step
|
|
1512
|
+
checkbox(`First time at step 13 — create journal entry with ALL session screenshots:`);
|
|
1413
1513
|
console.log(chalk.dim(` codeyam editor journal '{"title":"...","type":"feature","description":"...","includeSessionScenarios":true}'`));
|
|
1414
1514
|
console.log(chalk.dim(' includeSessionScenarios auto-discovers component + app + user persona screenshots'));
|
|
1415
|
-
checkbox(`Returning to step
|
|
1515
|
+
checkbox(`Returning to step 13 (before commit) — update the existing journal entry:`);
|
|
1416
1516
|
console.log(chalk.dim(` codeyam editor journal-update '{"time":"<journal entry time>","description":"<updated>","includeSessionScenarios":true}'`));
|
|
1417
1517
|
console.log(chalk.dim(' Note: PATCH only works before the entry is committed. After commit, use POST to create a new entry.'));
|
|
1418
1518
|
console.log();
|
|
1419
|
-
console.log(chalk.dim('Focus on creating or updating the journal entry. Summary and presentation happen in step
|
|
1420
|
-
stopGate(
|
|
1519
|
+
console.log(chalk.dim('Focus on creating or updating the journal entry. Summary and presentation happen in step 15.'));
|
|
1520
|
+
stopGate(13);
|
|
1421
1521
|
}
|
|
1422
|
-
// ─── Step
|
|
1423
|
-
function
|
|
1522
|
+
// ─── Step 14: Review ──────────────────────────────────────────────────
|
|
1523
|
+
function printStep14(root, feature) {
|
|
1424
1524
|
const port = getServerPort();
|
|
1425
1525
|
const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
|
|
1426
1526
|
const prevState = readState(root);
|
|
1427
|
-
const isResuming = prevState?.step ===
|
|
1527
|
+
const isResuming = prevState?.step === 14;
|
|
1428
1528
|
const now = new Date().toISOString();
|
|
1429
1529
|
writeState(root, {
|
|
1430
1530
|
feature,
|
|
1431
|
-
step:
|
|
1432
|
-
label: STEP_LABELS[
|
|
1531
|
+
step: 14,
|
|
1532
|
+
label: STEP_LABELS[14],
|
|
1433
1533
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
1434
1534
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
1435
1535
|
});
|
|
1436
|
-
logEvent(root, 'step', { step:
|
|
1437
|
-
stepHeader(
|
|
1536
|
+
logEvent(root, 'step', { step: 14, label: 'Review', feature });
|
|
1537
|
+
stepHeader(14, 'Review', feature);
|
|
1438
1538
|
if (isResuming) {
|
|
1439
|
-
printResumptionHeader(
|
|
1539
|
+
printResumptionHeader(14);
|
|
1440
1540
|
}
|
|
1441
1541
|
console.log('Verify all screenshots and checks pass before presenting to the user.');
|
|
1442
1542
|
console.log();
|
|
@@ -1452,31 +1552,31 @@ function printStep12(root, feature) {
|
|
|
1452
1552
|
checkbox('Recapture stale scenarios: `codeyam editor recapture-stale`');
|
|
1453
1553
|
checkbox('Run `codeyam editor audit` to verify completeness of scenarios and tests');
|
|
1454
1554
|
checkbox('Do not proceed until all checks pass');
|
|
1455
|
-
stopGate(
|
|
1555
|
+
stopGate(14);
|
|
1456
1556
|
}
|
|
1457
|
-
// ─── Step
|
|
1458
|
-
function
|
|
1557
|
+
// ─── Step 15: Present ─────────────────────────────────────────────────
|
|
1558
|
+
function printStep15(root, feature) {
|
|
1459
1559
|
const prevState = readState(root);
|
|
1460
|
-
const isResuming = prevState?.step ===
|
|
1560
|
+
const isResuming = prevState?.step === 15;
|
|
1461
1561
|
const now = new Date().toISOString();
|
|
1462
1562
|
writeState(root, {
|
|
1463
1563
|
feature,
|
|
1464
|
-
step:
|
|
1465
|
-
label: STEP_LABELS[
|
|
1564
|
+
step: 15,
|
|
1565
|
+
label: STEP_LABELS[15],
|
|
1466
1566
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
1467
1567
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
1468
1568
|
});
|
|
1469
|
-
logEvent(root, 'step', { step:
|
|
1470
|
-
stepHeader(
|
|
1569
|
+
logEvent(root, 'step', { step: 15, label: 'Present', feature });
|
|
1570
|
+
stepHeader(15, 'Present', feature);
|
|
1471
1571
|
if (isResuming) {
|
|
1472
|
-
printResumptionHeader(
|
|
1572
|
+
printResumptionHeader(15);
|
|
1473
1573
|
}
|
|
1474
1574
|
console.log('Present the results to the user and get their approval.');
|
|
1475
1575
|
console.log();
|
|
1476
1576
|
console.log(chalk.bold('Checklist:'));
|
|
1477
1577
|
checkbox(`Show the results panel: \`codeyam editor show-results\``);
|
|
1478
1578
|
console.log(chalk.dim(' This opens a visual panel below the terminal showing all scenarios with screenshots.'));
|
|
1479
|
-
console.log(chalk.dim(' The
|
|
1579
|
+
console.log(chalk.dim(' The first scenario is automatically selected and its live preview is shown.'));
|
|
1480
1580
|
console.log(chalk.dim(' The user can click scenarios to switch the live preview.'));
|
|
1481
1581
|
checkbox('Write a 1-2 sentence summary of what was built');
|
|
1482
1582
|
checkbox('Report test count and audit status (one line)');
|
|
@@ -1488,7 +1588,7 @@ function printStep13(root, feature) {
|
|
|
1488
1588
|
chalk.dim(' — describe changes, then re-verify'));
|
|
1489
1589
|
console.log();
|
|
1490
1590
|
console.log(chalk.bold('If the user chooses "Save & commit":'));
|
|
1491
|
-
checkbox('Advance to the commit step: `codeyam editor
|
|
1591
|
+
checkbox('Advance to the commit step: `codeyam editor 16`');
|
|
1492
1592
|
console.log();
|
|
1493
1593
|
console.log(chalk.bold('If the user chooses "Make changes" (or asks for ANY change, even as a question):'));
|
|
1494
1594
|
checkbox(`Hide the results panel: \`codeyam editor hide-results\``);
|
|
@@ -1496,7 +1596,7 @@ function printStep13(root, feature) {
|
|
|
1496
1596
|
checkbox(`Run: \`codeyam editor change "${feature}"\` — this gives you the change checklist`);
|
|
1497
1597
|
checkbox('THEN make the requested changes and follow the checklist');
|
|
1498
1598
|
console.log(chalk.red.bold(' IMPORTANT: Always run the change command BEFORE writing any code.'));
|
|
1499
|
-
stopGate(
|
|
1599
|
+
stopGate(15, { confirm: true });
|
|
1500
1600
|
}
|
|
1501
1601
|
// ─── Migration Mode ──────────────────────────────────────────────────
|
|
1502
1602
|
/**
|
|
@@ -1716,7 +1816,7 @@ function printMigrateStep4(root) {
|
|
|
1716
1816
|
checkbox('Check for client errors: `codeyam editor client-errors`');
|
|
1717
1817
|
checkbox('If any issues: fix the scenario data, re-register affected scenarios');
|
|
1718
1818
|
checkbox('Show results: `codeyam editor show-results`');
|
|
1719
|
-
console.log(chalk.dim(' The
|
|
1819
|
+
console.log(chalk.dim(' The first scenario is automatically selected and its live preview is shown.'));
|
|
1720
1820
|
console.log();
|
|
1721
1821
|
console.log(chalk.dim('The user should now see their existing page with live screenshots in the preview.'));
|
|
1722
1822
|
migrationStopGate(4, pageName, pageIndex, totalPages);
|
|
@@ -1883,7 +1983,7 @@ function printMigrateStep10(root) {
|
|
|
1883
1983
|
console.log();
|
|
1884
1984
|
console.log(chalk.bold('Checklist:'));
|
|
1885
1985
|
checkbox('Show results: `codeyam editor show-results`');
|
|
1886
|
-
console.log(chalk.dim(' The
|
|
1986
|
+
console.log(chalk.dim(' The first scenario is automatically selected and its live preview is shown.'));
|
|
1887
1987
|
checkbox('Write a 1-2 sentence summary of what was migrated');
|
|
1888
1988
|
console.log();
|
|
1889
1989
|
console.log(chalk.bold('Present a selection menu (AskUserQuestion with these EXACT option labels):'));
|
|
@@ -2103,22 +2203,22 @@ function handleMigrateCommand(root, subArg) {
|
|
|
2103
2203
|
};
|
|
2104
2204
|
stepFns[step](root);
|
|
2105
2205
|
}
|
|
2106
|
-
// ─── Step
|
|
2107
|
-
function
|
|
2206
|
+
// ─── Step 16: Commit ─────────────────────────────────────────────────
|
|
2207
|
+
function printStep16(root, feature) {
|
|
2108
2208
|
const prevState = readState(root);
|
|
2109
|
-
const isResuming = prevState?.step ===
|
|
2209
|
+
const isResuming = prevState?.step === 16;
|
|
2110
2210
|
const now = new Date().toISOString();
|
|
2111
2211
|
writeState(root, {
|
|
2112
2212
|
feature,
|
|
2113
|
-
step:
|
|
2114
|
-
label: STEP_LABELS[
|
|
2213
|
+
step: 16,
|
|
2214
|
+
label: STEP_LABELS[16],
|
|
2115
2215
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
2116
2216
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
2117
2217
|
});
|
|
2118
|
-
logEvent(root, 'step', { step:
|
|
2119
|
-
stepHeader(
|
|
2218
|
+
logEvent(root, 'step', { step: 16, label: 'Commit', feature });
|
|
2219
|
+
stepHeader(16, 'Commit', feature);
|
|
2120
2220
|
if (isResuming) {
|
|
2121
|
-
printResumptionHeader(
|
|
2221
|
+
printResumptionHeader(16);
|
|
2122
2222
|
}
|
|
2123
2223
|
console.log('Commit all changes for this feature.');
|
|
2124
2224
|
console.log();
|
|
@@ -2127,24 +2227,24 @@ function printStep14(root, feature) {
|
|
|
2127
2227
|
checkbox(`Hide the results panel: \`codeyam editor hide-results\``);
|
|
2128
2228
|
checkbox(`Git commit using the journal description: \`codeyam editor commit '{"message":"feat: <title>\\n\\n<journal description>"}'\``);
|
|
2129
2229
|
console.log(chalk.dim(' The commit message body MUST match the journal description exactly'));
|
|
2130
|
-
stopGate(
|
|
2230
|
+
stopGate(16);
|
|
2131
2231
|
}
|
|
2132
|
-
// ─── Step
|
|
2133
|
-
function
|
|
2232
|
+
// ─── Step 17: Finalize ───────────────────────────────────────────────
|
|
2233
|
+
function printStep17(root, feature) {
|
|
2134
2234
|
const prevState = readState(root);
|
|
2135
|
-
const isResuming = prevState?.step ===
|
|
2235
|
+
const isResuming = prevState?.step === 17;
|
|
2136
2236
|
const now = new Date().toISOString();
|
|
2137
2237
|
writeState(root, {
|
|
2138
2238
|
feature,
|
|
2139
|
-
step:
|
|
2140
|
-
label: STEP_LABELS[
|
|
2239
|
+
step: 17,
|
|
2240
|
+
label: STEP_LABELS[17],
|
|
2141
2241
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
2142
2242
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
2143
2243
|
});
|
|
2144
|
-
logEvent(root, 'step', { step:
|
|
2145
|
-
stepHeader(
|
|
2244
|
+
logEvent(root, 'step', { step: 17, label: 'Finalize', feature });
|
|
2245
|
+
stepHeader(17, 'Finalize', feature);
|
|
2146
2246
|
if (isResuming) {
|
|
2147
|
-
printResumptionHeader(
|
|
2247
|
+
printResumptionHeader(17);
|
|
2148
2248
|
}
|
|
2149
2249
|
console.log('Update the journal with the commit SHA and amend the commit.');
|
|
2150
2250
|
console.log();
|
|
@@ -2152,24 +2252,24 @@ function printStep15(root, feature) {
|
|
|
2152
2252
|
checkbox(`Update journal with commit SHA: \`codeyam editor journal-update '{"time":"<journal entry time>","commitSha":"<sha>","commitMessage":"feat: <title>"}'\``);
|
|
2153
2253
|
checkbox('Amend the commit to include the journal update: `git add .codeyam/journal/ && git commit --amend --no-edit`');
|
|
2154
2254
|
console.log(chalk.dim(' The journal-update modifies journal files after the commit — amend to keep the tree clean.'));
|
|
2155
|
-
stopGate(
|
|
2255
|
+
stopGate(17);
|
|
2156
2256
|
}
|
|
2157
|
-
// ─── Step
|
|
2158
|
-
function
|
|
2257
|
+
// ─── Step 18: Push ───────────────────────────────────────────────────
|
|
2258
|
+
function printStep18(root, feature) {
|
|
2159
2259
|
const prevState = readState(root);
|
|
2160
|
-
const isResuming = prevState?.step ===
|
|
2260
|
+
const isResuming = prevState?.step === 18;
|
|
2161
2261
|
const now = new Date().toISOString();
|
|
2162
2262
|
writeState(root, {
|
|
2163
2263
|
feature,
|
|
2164
|
-
step:
|
|
2165
|
-
label: STEP_LABELS[
|
|
2264
|
+
step: 18,
|
|
2265
|
+
label: STEP_LABELS[18],
|
|
2166
2266
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
2167
2267
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
2168
2268
|
});
|
|
2169
|
-
logEvent(root, 'step', { step:
|
|
2170
|
-
stepHeader(
|
|
2269
|
+
logEvent(root, 'step', { step: 18, label: 'Push', feature });
|
|
2270
|
+
stepHeader(18, 'Push', feature);
|
|
2171
2271
|
if (isResuming) {
|
|
2172
|
-
printResumptionHeader(
|
|
2272
|
+
printResumptionHeader(18);
|
|
2173
2273
|
}
|
|
2174
2274
|
console.log('Push the commit to the remote repository.');
|
|
2175
2275
|
console.log();
|
|
@@ -2180,12 +2280,15 @@ function printStep16(root, feature) {
|
|
|
2180
2280
|
console.log(chalk.dim(' If the user says yes, run: `git push`'));
|
|
2181
2281
|
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"'));
|
|
2182
2282
|
console.log(chalk.dim(' If the user wants help, guide them through `gh repo create` or manual GitHub setup, then push.'));
|
|
2183
|
-
|
|
2184
|
-
console.log();
|
|
2185
|
-
console.log(chalk.
|
|
2186
|
-
console.log(chalk.
|
|
2187
|
-
console.log(chalk.
|
|
2188
|
-
|
|
2283
|
+
console.log(chalk.dim(' Task management is handled by the ━━━ TASK ━━━ directive below.'));
|
|
2284
|
+
console.log();
|
|
2285
|
+
console.log(chalk.bold.yellow('IMPORTANT: After this step, the current feature is DONE.'));
|
|
2286
|
+
console.log(chalk.yellow(' Any new user request — even if it sounds related — is a NEW feature.'));
|
|
2287
|
+
console.log(chalk.yellow(' Do NOT use `codeyam editor change` or start building directly.'));
|
|
2288
|
+
console.log(chalk.yellow(' Ask the user what they want to build. Once they tell you, run `codeyam editor 1` yourself.'));
|
|
2289
|
+
console.log(chalk.yellow(' NEVER tell the user to run `codeyam editor` commands — these are internal. Just ask what to build.'));
|
|
2290
|
+
console.log(chalk.yellow(' The change workflow is ONLY for modifications during steps 1-15, not after commit.'));
|
|
2291
|
+
stopGate(18, { confirm: true });
|
|
2189
2292
|
}
|
|
2190
2293
|
// ─── Command definition ───────────────────────────────────────────────
|
|
2191
2294
|
// ─── Analyze-imports subcommand ────────────────────────────────────────
|
|
@@ -2205,7 +2308,7 @@ async function handleAnalyzeImports(options = {}) {
|
|
|
2205
2308
|
return;
|
|
2206
2309
|
}
|
|
2207
2310
|
console.error(chalk.red('Error: .codeyam/glossary.json not found.'));
|
|
2208
|
-
console.error(chalk.dim(' Run codeyam editor
|
|
2311
|
+
console.error(chalk.dim(' Run codeyam editor 8 to create the glossary first.'));
|
|
2209
2312
|
process.exit(1);
|
|
2210
2313
|
}
|
|
2211
2314
|
let glossaryEntries;
|
|
@@ -2627,11 +2730,13 @@ async function handleRegister(jsonArg) {
|
|
|
2627
2730
|
}
|
|
2628
2731
|
// Normalize to array for uniform handling
|
|
2629
2732
|
const items = Array.isArray(parsed.body) ? parsed.body : [parsed.body];
|
|
2733
|
+
const root = getProjectRoot();
|
|
2630
2734
|
const port = getServerPort();
|
|
2631
2735
|
const url = `http://localhost:${port}/api/editor-register-scenario`;
|
|
2632
2736
|
const isBatch = items.length > 1;
|
|
2633
2737
|
let succeeded = 0;
|
|
2634
2738
|
let failed = 0;
|
|
2739
|
+
const screenshotEntries = [];
|
|
2635
2740
|
for (let i = 0; i < items.length; i++) {
|
|
2636
2741
|
const body = items[i];
|
|
2637
2742
|
const prefix = isBatch ? chalk.dim(`[${i + 1}/${items.length}] `) : '';
|
|
@@ -2659,8 +2764,20 @@ async function handleRegister(jsonArg) {
|
|
|
2659
2764
|
else {
|
|
2660
2765
|
parts.push(`errors=0`);
|
|
2661
2766
|
}
|
|
2662
|
-
if (data.
|
|
2663
|
-
|
|
2767
|
+
if (data.visibleText) {
|
|
2768
|
+
const truncated = data.visibleText.length > 100
|
|
2769
|
+
? data.visibleText.substring(0, 97) + '...'
|
|
2770
|
+
: data.visibleText;
|
|
2771
|
+
parts.push(`visibleText="${truncated}"`);
|
|
2772
|
+
}
|
|
2773
|
+
if (data.seedResult) {
|
|
2774
|
+
if (data.seedResult.success) {
|
|
2775
|
+
parts.push(`seed=ok`);
|
|
2776
|
+
}
|
|
2777
|
+
else {
|
|
2778
|
+
parts.push(chalk.red(`seed=FAILED${data.seedResult.error ? ` (${data.seedResult.error})` : ''}`));
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2664
2781
|
if (data.captureError)
|
|
2665
2782
|
parts.push(chalk.yellow(`captureError="${data.captureError}"`));
|
|
2666
2783
|
console.log(prefix + parts.join(' '));
|
|
@@ -2694,6 +2811,16 @@ async function handleRegister(jsonArg) {
|
|
|
2694
2811
|
}
|
|
2695
2812
|
else {
|
|
2696
2813
|
succeeded++;
|
|
2814
|
+
// Collect screenshot paths for duplicate detection
|
|
2815
|
+
if (data.screenshotCaptured &&
|
|
2816
|
+
data.scenario?.screenshotPath &&
|
|
2817
|
+
data.scenario?.name) {
|
|
2818
|
+
const absPath = path.join(root, '.codeyam', 'editor-scenarios', data.scenario.screenshotPath);
|
|
2819
|
+
screenshotEntries.push({
|
|
2820
|
+
scenarioName: data.scenario.name,
|
|
2821
|
+
screenshotAbsPath: absPath,
|
|
2822
|
+
});
|
|
2823
|
+
}
|
|
2697
2824
|
}
|
|
2698
2825
|
}
|
|
2699
2826
|
catch (error) {
|
|
@@ -2704,6 +2831,33 @@ async function handleRegister(jsonArg) {
|
|
|
2704
2831
|
failed++;
|
|
2705
2832
|
}
|
|
2706
2833
|
}
|
|
2834
|
+
// Detect duplicate screenshots in the batch
|
|
2835
|
+
if (isBatch && screenshotEntries.length > 1) {
|
|
2836
|
+
const { computeScreenshotHash, findDuplicateScreenshots } = await import('../utils/screenshotHash');
|
|
2837
|
+
const hashEntries = screenshotEntries
|
|
2838
|
+
.map((e) => {
|
|
2839
|
+
const hash = computeScreenshotHash(e.screenshotAbsPath);
|
|
2840
|
+
return hash
|
|
2841
|
+
? {
|
|
2842
|
+
scenarioName: e.scenarioName,
|
|
2843
|
+
screenshotPath: e.screenshotAbsPath,
|
|
2844
|
+
hash,
|
|
2845
|
+
}
|
|
2846
|
+
: null;
|
|
2847
|
+
})
|
|
2848
|
+
.filter((e) => e !== null);
|
|
2849
|
+
const duplicates = findDuplicateScreenshots(hashEntries);
|
|
2850
|
+
if (duplicates.size > 0) {
|
|
2851
|
+
console.log('');
|
|
2852
|
+
console.log(chalk.yellow.bold('WARNING: Identical screenshots detected:'));
|
|
2853
|
+
for (const [, names] of duplicates) {
|
|
2854
|
+
const quoted = names.map((n) => `"${n}"`);
|
|
2855
|
+
console.log(chalk.yellow(` ${quoted.join(' and ')} produced the same screenshot`));
|
|
2856
|
+
}
|
|
2857
|
+
console.log(chalk.yellow(" This usually means the app's view state depends on something scenarios can't control"));
|
|
2858
|
+
console.log(chalk.yellow(' (e.g., a hardcoded function return value, or identical localStorage not differentiating views).'));
|
|
2859
|
+
}
|
|
2860
|
+
}
|
|
2707
2861
|
if (isBatch) {
|
|
2708
2862
|
console.log(chalk.dim(`\nBatch complete: ${succeeded}/${items.length} succeeded`));
|
|
2709
2863
|
}
|
|
@@ -2879,7 +3033,7 @@ async function handleDependents(entityName) {
|
|
|
2879
3033
|
*
|
|
2880
3034
|
* Prints a condensed post-change checklist that guides Claude through
|
|
2881
3035
|
* re-verifying after user-requested modifications. When called from
|
|
2882
|
-
* step
|
|
3036
|
+
* step 15+, this loops back to step 15 (present). When called from an
|
|
2883
3037
|
* earlier step, it returns to that step so the normal flow continues.
|
|
2884
3038
|
*/
|
|
2885
3039
|
function handleChange(feature) {
|
|
@@ -2897,7 +3051,7 @@ function handleChange(feature) {
|
|
|
2897
3051
|
process.exit(1);
|
|
2898
3052
|
}
|
|
2899
3053
|
}
|
|
2900
|
-
const currentStep = state?.step ??
|
|
3054
|
+
const currentStep = state?.step ?? 15;
|
|
2901
3055
|
const port = getServerPort();
|
|
2902
3056
|
console.log();
|
|
2903
3057
|
console.log(chalk.bold.cyan('━━━ Change Loop ━━━'));
|
|
@@ -2919,6 +3073,8 @@ function handleChange(feature) {
|
|
|
2919
3073
|
console.log(chalk.cyan(` codeyam editor preview '{"dimension":"${dim}"}'`));
|
|
2920
3074
|
printDimensionGuidance(dim, dimNames);
|
|
2921
3075
|
console.log(chalk.cyan(' The user is watching the preview. Let them see progress incrementally.'));
|
|
3076
|
+
console.log(chalk.red.bold(' NEVER claim "you should see X" or "the preview shows X" unless you just ran a preview command.'));
|
|
3077
|
+
console.log(chalk.red(' The preview only updates when you explicitly refresh it. Verify, then describe.'));
|
|
2922
3078
|
console.log();
|
|
2923
3079
|
console.log(chalk.bold('0. Close the results panel:'));
|
|
2924
3080
|
checkbox(`Hide results: \`codeyam editor hide-results\``);
|
|
@@ -2945,6 +3101,7 @@ function handleChange(feature) {
|
|
|
2945
3101
|
console.log(chalk.dim(' re-register "Home - Default", "Catalog - Full", "Detail - WithReviews", etc.'));
|
|
2946
3102
|
checkbox("Enrich existing scenario data to exercise the change — don't just re-register unchanged data");
|
|
2947
3103
|
console.log(chalk.dim(' Add data that demonstrates what changed: new fields, relationships, states, content variety.'));
|
|
3104
|
+
console.log(chalk.dim(' If the enriched data makes the scenario name too narrow, rename it to reflect its broader coverage.'));
|
|
2948
3105
|
checkbox('After each re-registration, view the screenshot to verify data is visible');
|
|
2949
3106
|
console.log(chalk.dim(" If the screenshot doesn't show the data you put in, the scenario is broken."));
|
|
2950
3107
|
console.log();
|
|
@@ -2968,10 +3125,10 @@ function handleChange(feature) {
|
|
|
2968
3125
|
console.log(chalk.dim(' Always update the existing uncommitted entry — do NOT create a new one.'));
|
|
2969
3126
|
console.log(chalk.dim(' Only create a new entry (POST) if no uncommitted entry exists for this feature.'));
|
|
2970
3127
|
console.log();
|
|
2971
|
-
// If the change was initiated from a step before
|
|
2972
|
-
// instead of jumping to step
|
|
2973
|
-
// step
|
|
2974
|
-
if (currentStep <
|
|
3128
|
+
// If the change was initiated from a step before 15, return to that step
|
|
3129
|
+
// instead of jumping to step 15. The change workflow should only loop to
|
|
3130
|
+
// step 15 when changes are requested FROM step 15.
|
|
3131
|
+
if (currentStep < 15) {
|
|
2975
3132
|
console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
2976
3133
|
console.log(chalk.red.bold(' REQUIRED: Return to current step'));
|
|
2977
3134
|
console.log(chalk.red.bold(' When all checks pass, you MUST run: ') +
|
|
@@ -2982,7 +3139,7 @@ function handleChange(feature) {
|
|
|
2982
3139
|
console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
2983
3140
|
console.log(chalk.red.bold(' REQUIRED: Show Results'));
|
|
2984
3141
|
console.log(chalk.red.bold(' When all checks pass, you MUST run: ') +
|
|
2985
|
-
chalk.bold(`codeyam editor
|
|
3142
|
+
chalk.bold(`codeyam editor 15`));
|
|
2986
3143
|
console.log(chalk.red.bold(' The user ALWAYS expects to see results after changes. DO NOT skip this step.'));
|
|
2987
3144
|
console.log(chalk.red(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
2988
3145
|
}
|
|
@@ -3019,14 +3176,33 @@ async function checkAuditGate() {
|
|
|
3019
3176
|
return true; // Server unreachable — don't block
|
|
3020
3177
|
if (data.summary?.allPassing === true)
|
|
3021
3178
|
return true;
|
|
3022
|
-
// If incomplete entities are the only issue,
|
|
3179
|
+
// If incomplete entities / unassociated scenarios are the only issue,
|
|
3180
|
+
// auto-fix with analyze-imports + entity SHA backfill (once)
|
|
3023
3181
|
const { isAutoRemediable } = await import('../utils/editorAudit.js');
|
|
3024
3182
|
if (isAutoRemediable(data.summary, false)) {
|
|
3025
3183
|
try {
|
|
3026
3184
|
await handleAnalyzeImports({ silent: true });
|
|
3027
3185
|
}
|
|
3028
3186
|
catch {
|
|
3029
|
-
|
|
3187
|
+
// Fall through to backfill attempt
|
|
3188
|
+
}
|
|
3189
|
+
// Backfill entity_sha on scenarios registered before entities existed
|
|
3190
|
+
try {
|
|
3191
|
+
const entities = await loadEntities({});
|
|
3192
|
+
if (entities && entities.length > 0) {
|
|
3193
|
+
const { getDatabase } = await import('../../../packages/database/index.js');
|
|
3194
|
+
const db = getDatabase();
|
|
3195
|
+
await backfillEntityShaOnScenarios(db, entities.map((e) => ({
|
|
3196
|
+
sha: e.sha,
|
|
3197
|
+
name: e.name,
|
|
3198
|
+
filePath: e.filePath || '',
|
|
3199
|
+
isDefaultExport: e.metadata?.notExported === false &&
|
|
3200
|
+
e.metadata?.namedExport === false,
|
|
3201
|
+
})));
|
|
3202
|
+
}
|
|
3203
|
+
}
|
|
3204
|
+
catch {
|
|
3205
|
+
// Fall through
|
|
3030
3206
|
}
|
|
3031
3207
|
// Re-check after fix
|
|
3032
3208
|
const retry = await fetchAuditResult();
|
|
@@ -3103,6 +3279,13 @@ function printAuditGateFailures(data) {
|
|
|
3103
3279
|
issues.push(`${s.incompleteEntities} entity/entities need import analysis`);
|
|
3104
3280
|
}
|
|
3105
3281
|
}
|
|
3282
|
+
if (s.unassociatedScenarios > 0) {
|
|
3283
|
+
issues.push(`${s.unassociatedScenarios} component(s) have scenarios with no entity link — run \`codeyam editor analyze-imports\` then re-run audit`);
|
|
3284
|
+
const unassociated = data.unassociatedScenarios || [];
|
|
3285
|
+
for (const u of unassociated) {
|
|
3286
|
+
issues.push(` → ${u.name} (${u.filePath}) — ${u.scenarioCount} scenario${u.scenarioCount !== 1 ? 's' : ''}`);
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
3106
3289
|
if (s.miscategorizedScenarios > 0)
|
|
3107
3290
|
issues.push(`${s.miscategorizedScenarios} component(s) using page URLs instead of isolation routes`);
|
|
3108
3291
|
if (s.scenariosNeedingRecapture > 0)
|
|
@@ -3129,8 +3312,6 @@ function printAuditGateFailures(data) {
|
|
|
3129
3312
|
}
|
|
3130
3313
|
}
|
|
3131
3314
|
console.error(chalk.yellow('\nFix: Fix the code errors above, then re-capture the affected scenarios.'));
|
|
3132
|
-
console.error(chalk.yellow('If errors reference browser APIs (localStorage, sessionStorage, window, document),'));
|
|
3133
|
-
console.error(chalk.yellow('create a universal mock: codeyam detect-universal-mocks'));
|
|
3134
3315
|
}
|
|
3135
3316
|
}
|
|
3136
3317
|
console.error(chalk.dim('\nRun `codeyam editor audit` for full details.\n'));
|
|
@@ -3149,30 +3330,50 @@ async function handleAudit() {
|
|
|
3149
3330
|
console.error(chalk.red('Error: Could not reach the CodeYam server. Is it running?'));
|
|
3150
3331
|
process.exit(1);
|
|
3151
3332
|
}
|
|
3152
|
-
// Auto-fix incomplete entities — but only once.
|
|
3153
|
-
// If analyze-imports runs and
|
|
3333
|
+
// Auto-fix incomplete entities and unassociated scenarios — but only once.
|
|
3334
|
+
// If analyze-imports + entity SHA sync runs and issues persist, report clearly
|
|
3154
3335
|
// instead of retrying. The analysis may have errors (e.g., stale entity
|
|
3155
3336
|
// references from refactoring) that can't be fixed by re-running.
|
|
3156
3337
|
const incompleteBeforeFix = data.incompleteEntities || [];
|
|
3338
|
+
const unassociatedBeforeFix = data.unassociatedScenarios || [];
|
|
3157
3339
|
let autoRemediationFailed = false;
|
|
3158
|
-
if (incompleteBeforeFix.length > 0) {
|
|
3159
|
-
|
|
3340
|
+
if (incompleteBeforeFix.length > 0 || unassociatedBeforeFix.length > 0) {
|
|
3341
|
+
const issueCount = incompleteBeforeFix.length + unassociatedBeforeFix.length;
|
|
3342
|
+
console.log(chalk.dim(`Auto-fixing ${issueCount} entity association issue${issueCount !== 1 ? 's' : ''}...`));
|
|
3160
3343
|
try {
|
|
3161
3344
|
await handleAnalyzeImports({ silent: true });
|
|
3162
3345
|
}
|
|
3163
3346
|
catch {
|
|
3164
|
-
// Fall through — the audit will still report them
|
|
3347
|
+
// Fall through — the audit will still report them
|
|
3348
|
+
}
|
|
3349
|
+
// Backfill entity_sha on scenarios that were registered before entities existed
|
|
3350
|
+
try {
|
|
3351
|
+
const entities = await loadEntities({});
|
|
3352
|
+
if (entities && entities.length > 0) {
|
|
3353
|
+
const { getDatabase } = await import('../../../packages/database/index.js');
|
|
3354
|
+
const db = getDatabase();
|
|
3355
|
+
await backfillEntityShaOnScenarios(db, entities.map((e) => ({
|
|
3356
|
+
sha: e.sha,
|
|
3357
|
+
name: e.name,
|
|
3358
|
+
filePath: e.filePath || '',
|
|
3359
|
+
isDefaultExport: e.metadata?.notExported === false &&
|
|
3360
|
+
e.metadata?.namedExport === false,
|
|
3361
|
+
})));
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
catch {
|
|
3365
|
+
// Fall through — re-fetch will show remaining issues
|
|
3165
3366
|
}
|
|
3166
3367
|
// Re-fetch audit results after the fix
|
|
3167
3368
|
data = await fetchAuditResult();
|
|
3168
3369
|
if (!data) {
|
|
3169
|
-
console.error(chalk.red('Error: Could not reach the CodeYam server after
|
|
3370
|
+
console.error(chalk.red('Error: Could not reach the CodeYam server after auto-fix.'));
|
|
3170
3371
|
process.exit(1);
|
|
3171
3372
|
}
|
|
3172
|
-
// If
|
|
3173
|
-
// flag it so we can show a clear message instead of looping
|
|
3373
|
+
// If issues persist after auto-fix, flag it so we show clear guidance
|
|
3174
3374
|
const incompleteAfterFix = data.incompleteEntities || [];
|
|
3175
|
-
|
|
3375
|
+
const unassociatedAfterFix = data.unassociatedScenarios || [];
|
|
3376
|
+
if (incompleteAfterFix.length > 0 || unassociatedAfterFix.length > 0) {
|
|
3176
3377
|
autoRemediationFailed = true;
|
|
3177
3378
|
}
|
|
3178
3379
|
}
|
|
@@ -3219,14 +3420,6 @@ async function handleAudit() {
|
|
|
3219
3420
|
if (c.clientErrors.length > 3) {
|
|
3220
3421
|
console.log(chalk.dim(` … and ${c.clientErrors.length - 3} more`));
|
|
3221
3422
|
}
|
|
3222
|
-
// Detect browser API errors and provide actionable guidance
|
|
3223
|
-
const browserApiPattern = /\b(localStorage|sessionStorage|window\.|document\.|navigator\.|indexedDB|matchMedia|ResizeObserver|IntersectionObserver|MutationObserver)\b/;
|
|
3224
|
-
const hasBrowserApiErrors = c.clientErrors.some((err) => browserApiPattern.test(err));
|
|
3225
|
-
if (hasBrowserApiErrors) {
|
|
3226
|
-
console.log(chalk.yellow(` ⚠ These errors are caused by browser APIs that don't exist during server-side analysis.`));
|
|
3227
|
-
console.log(chalk.yellow(` Fix: Create a universal mock to stub the missing API. Run: codeyam detect-universal-mocks`));
|
|
3228
|
-
console.log(chalk.yellow(` DO NOT re-run the audit or analyze-imports — the error will persist until a mock is created.`));
|
|
3229
|
-
}
|
|
3230
3423
|
}
|
|
3231
3424
|
}
|
|
3232
3425
|
console.log();
|
|
@@ -3284,17 +3477,33 @@ async function handleAudit() {
|
|
|
3284
3477
|
console.log(` ${chalk.red('✗')} ${e.name} — ${e.scenarioCount} scenario${e.scenarioCount !== 1 ? 's' : ''} but entity not analyzed`);
|
|
3285
3478
|
}
|
|
3286
3479
|
if (autoRemediationFailed) {
|
|
3287
|
-
console.log(chalk.
|
|
3288
|
-
console.log(chalk.
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
console.log(chalk.yellow('
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3480
|
+
console.log(chalk.yellow(' analyze-imports ran automatically but could not resolve these.'));
|
|
3481
|
+
console.log(chalk.yellow(' Run `codeyam editor analyze-imports` to see the full error output.'));
|
|
3482
|
+
}
|
|
3483
|
+
else {
|
|
3484
|
+
console.log(chalk.yellow(' Run `codeyam editor analyze-imports` to analyze these entities.'));
|
|
3485
|
+
}
|
|
3486
|
+
console.log();
|
|
3487
|
+
}
|
|
3488
|
+
// Unassociated scenarios (NULL entity_sha with file paths)
|
|
3489
|
+
const unassociatedScenarios = data.unassociatedScenarios || [];
|
|
3490
|
+
if (unassociatedScenarios.length > 0) {
|
|
3491
|
+
console.log(chalk.bold('Unassociated scenarios (missing entity link):'));
|
|
3492
|
+
for (const u of unassociatedScenarios) {
|
|
3493
|
+
console.log(` ${chalk.red('✗')} ${u.name} ${chalk.dim(`(${u.filePath})`)} — ${u.scenarioCount} scenario${u.scenarioCount !== 1 ? 's' : ''} with no entity_sha`);
|
|
3494
|
+
for (const sn of u.scenarioNames.slice(0, 3)) {
|
|
3495
|
+
console.log(chalk.dim(` "${sn}"`));
|
|
3496
|
+
}
|
|
3497
|
+
if (u.scenarioNames.length > 3) {
|
|
3498
|
+
console.log(chalk.dim(` … and ${u.scenarioNames.length - 3} more`));
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3501
|
+
if (autoRemediationFailed) {
|
|
3502
|
+
console.log(chalk.yellow(' analyze-imports ran automatically but could not resolve these.'));
|
|
3503
|
+
console.log(chalk.yellow(' Run `codeyam editor analyze-imports` to see the full error output.'));
|
|
3295
3504
|
}
|
|
3296
3505
|
else {
|
|
3297
|
-
console.log(chalk.yellow(' Run `codeyam editor analyze-imports` to
|
|
3506
|
+
console.log(chalk.yellow(' Run `codeyam editor analyze-imports` to create entity records, then re-run audit to backfill.'));
|
|
3298
3507
|
}
|
|
3299
3508
|
console.log();
|
|
3300
3509
|
}
|
|
@@ -3371,6 +3580,9 @@ async function handleAudit() {
|
|
|
3371
3580
|
if (summary.incompleteEntities > 0) {
|
|
3372
3581
|
parts.push(`${summary.incompleteEntities} entit${summary.incompleteEntities !== 1 ? 'ies' : 'y'} need import analysis`);
|
|
3373
3582
|
}
|
|
3583
|
+
if (summary.unassociatedScenarios > 0) {
|
|
3584
|
+
parts.push(`${summary.unassociatedScenarios} component${summary.unassociatedScenarios !== 1 ? 's' : ''} with scenarios missing entity link`);
|
|
3585
|
+
}
|
|
3374
3586
|
if (summary.miscategorizedScenarios > 0) {
|
|
3375
3587
|
parts.push(`${summary.miscategorizedScenarios} component${summary.miscategorizedScenarios !== 1 ? 's' : ''} using page URLs instead of isolation routes`);
|
|
3376
3588
|
}
|
|
@@ -3745,7 +3957,7 @@ async function handleTemplate() {
|
|
|
3745
3957
|
console.log(chalk.green(' Git initialized.'));
|
|
3746
3958
|
}
|
|
3747
3959
|
// 4. Run codeyam init
|
|
3748
|
-
console.log(chalk.bold('
|
|
3960
|
+
console.log(chalk.bold('Initializing project...'));
|
|
3749
3961
|
await initCommand.handler({
|
|
3750
3962
|
force: true,
|
|
3751
3963
|
'keep-server': true,
|
|
@@ -3824,7 +4036,7 @@ async function handleSync() {
|
|
|
3824
4036
|
// fall through
|
|
3825
4037
|
}
|
|
3826
4038
|
if (!projectSlug) {
|
|
3827
|
-
console.error(chalk.red('Error: No project slug found. Run codeyam
|
|
4039
|
+
console.error(chalk.red('Error: No project slug found. Run `codeyam editor template` to initialize the project.'));
|
|
3828
4040
|
process.exit(1);
|
|
3829
4041
|
}
|
|
3830
4042
|
const connectionOk = await withoutSpinner(() => testEnvironment());
|
|
@@ -4008,7 +4220,7 @@ function handleEditorDebug(args) {
|
|
|
4008
4220
|
scenarios.push({
|
|
4009
4221
|
id: 'overview-with-state',
|
|
4010
4222
|
title: 'Cycle overview (project, with state)',
|
|
4011
|
-
render: () => withTempRoot(true, (tempRoot) => captureOutput(() => printCycleOverview(tempRoot, makeState(
|
|
4223
|
+
render: () => withTempRoot(true, (tempRoot) => captureOutput(() => printCycleOverview(tempRoot, makeState(6, feature)))),
|
|
4012
4224
|
});
|
|
4013
4225
|
}
|
|
4014
4226
|
const stepFns = {
|
|
@@ -4028,8 +4240,10 @@ function handleEditorDebug(args) {
|
|
|
4028
4240
|
14: printStep14,
|
|
4029
4241
|
15: printStep15,
|
|
4030
4242
|
16: printStep16,
|
|
4243
|
+
17: printStep17,
|
|
4244
|
+
18: printStep18,
|
|
4031
4245
|
};
|
|
4032
|
-
for (let step = 1; step <=
|
|
4246
|
+
for (let step = 1; step <= 18; step++) {
|
|
4033
4247
|
const stepId = `step-${step}`;
|
|
4034
4248
|
if (!wants(stepId))
|
|
4035
4249
|
continue;
|
|
@@ -4049,7 +4263,7 @@ function handleEditorDebug(args) {
|
|
|
4049
4263
|
if (step === 2) {
|
|
4050
4264
|
scenarios.push({
|
|
4051
4265
|
id: 'step-2-scaffold',
|
|
4052
|
-
title: 'Step 2 (
|
|
4266
|
+
title: 'Step 2 (Prepare) — scaffold flow (no project)',
|
|
4053
4267
|
render: () => withTempRoot(false, (tempRoot) => captureOutput(() => printStep2(tempRoot, feature))),
|
|
4054
4268
|
});
|
|
4055
4269
|
}
|
|
@@ -4108,8 +4322,8 @@ const editorCommand = {
|
|
|
4108
4322
|
describe: 'Editor mode guided workflow',
|
|
4109
4323
|
builder: (yargs) => {
|
|
4110
4324
|
const stepDescription = IS_INTERNAL_BUILD
|
|
4111
|
-
? 'Step number (1-
|
|
4112
|
-
: 'Step number (1-
|
|
4325
|
+
? 'Step number (1-18) or subcommand (template, register, isolate, analyze-imports, dependents, audit, scenarios, scenario-coverage, recapture-stale, change, sync, debug, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors, task-ontrack)'
|
|
4326
|
+
: 'Step number (1-18) or subcommand (template, register, isolate, analyze-imports, dependents, audit, scenarios, scenario-coverage, recapture-stale, change, sync, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors, task-ontrack)';
|
|
4113
4327
|
let builder = yargs
|
|
4114
4328
|
.positional('step', {
|
|
4115
4329
|
type: 'string',
|
|
@@ -4145,7 +4359,7 @@ const editorCommand = {
|
|
|
4145
4359
|
builder = builder
|
|
4146
4360
|
.option('target', {
|
|
4147
4361
|
type: 'string',
|
|
4148
|
-
describe: 'Debug target (setup, overview, overview-with-state, step-1..step-
|
|
4362
|
+
describe: 'Debug target (setup, overview, overview-with-state, step-1..step-18, or comma-separated list)',
|
|
4149
4363
|
})
|
|
4150
4364
|
.option('resume', {
|
|
4151
4365
|
type: 'boolean',
|
|
@@ -4170,6 +4384,18 @@ const editorCommand = {
|
|
|
4170
4384
|
// API subcommands: preview, show-results, hide-results, commit,
|
|
4171
4385
|
// journal, journal-update, dev-server, client-errors
|
|
4172
4386
|
if (argv.step && EDITOR_API_SUBCOMMANDS.includes(argv.step)) {
|
|
4387
|
+
// Guard: commit requires step 16 to have been run first.
|
|
4388
|
+
// Without this, Claude shortcuts the process by running
|
|
4389
|
+
// `codeyam editor commit` directly from step 15, skipping
|
|
4390
|
+
// steps 16-18 entirely.
|
|
4391
|
+
if (argv.step === 'commit') {
|
|
4392
|
+
const state = readState(root);
|
|
4393
|
+
if (state && state.step !== 16) {
|
|
4394
|
+
console.error(chalk.red('Error: Run `codeyam editor 16` before committing.'));
|
|
4395
|
+
console.error(chalk.dim(` Current step: ${state.step} (${state.label || 'unknown'}). Step 16 (Commit) must be run first.`));
|
|
4396
|
+
process.exit(1);
|
|
4397
|
+
}
|
|
4398
|
+
}
|
|
4173
4399
|
const port = getServerPort();
|
|
4174
4400
|
try {
|
|
4175
4401
|
const request = buildEditorApiRequest(argv.step, argv.json || undefined);
|
|
@@ -4189,6 +4415,12 @@ const editorCommand = {
|
|
|
4189
4415
|
console.log(JSON.stringify(result.data, null, 2));
|
|
4190
4416
|
}
|
|
4191
4417
|
}
|
|
4418
|
+
// After a successful commit, remind Claude to continue to step 17
|
|
4419
|
+
if (argv.step === 'commit' && result.ok) {
|
|
4420
|
+
console.log();
|
|
4421
|
+
console.log(chalk.green('Commit done. Now run: ') +
|
|
4422
|
+
chalk.bold('codeyam editor 17'));
|
|
4423
|
+
}
|
|
4192
4424
|
}
|
|
4193
4425
|
catch (err) {
|
|
4194
4426
|
console.error(chalk.red('Error: Could not reach the CodeYam server. Is it running?'));
|
|
@@ -4207,6 +4439,46 @@ const editorCommand = {
|
|
|
4207
4439
|
await handleGlossaryAdd(argv.json || '');
|
|
4208
4440
|
return;
|
|
4209
4441
|
}
|
|
4442
|
+
// Subcommand: codeyam editor task-ontrack
|
|
4443
|
+
// Corrective command when Claude advanced without creating a task.
|
|
4444
|
+
if (argv.step === 'task-ontrack') {
|
|
4445
|
+
const state = readState(root);
|
|
4446
|
+
if (!state?.step) {
|
|
4447
|
+
console.error(chalk.red('No editor state found. Run `codeyam editor 1` to start.'));
|
|
4448
|
+
process.exit(1);
|
|
4449
|
+
}
|
|
4450
|
+
const currentLabel = STEP_LABELS[state.step] || `Step ${state.step}`;
|
|
4451
|
+
const totalSteps = Object.keys(STEP_LABELS).length;
|
|
4452
|
+
const nextLabel = state.step < totalSteps ? STEP_LABELS[state.step + 1] : undefined;
|
|
4453
|
+
console.log();
|
|
4454
|
+
console.log(chalk.bold.yellow('━━━ GETTING BACK ON TRACK ━━━'));
|
|
4455
|
+
console.log();
|
|
4456
|
+
console.log(chalk.yellow('You went off-track by not creating a task. Create the task below to continue.'));
|
|
4457
|
+
console.log();
|
|
4458
|
+
// Print the TASK directive for the current step
|
|
4459
|
+
let taskTitle;
|
|
4460
|
+
if (state.step < totalSteps) {
|
|
4461
|
+
taskTitle = `Complete codeyam editor step ${state.step}: '${currentLabel}' and move on to step ${state.step + 1}: '${nextLabel}'`;
|
|
4462
|
+
}
|
|
4463
|
+
else {
|
|
4464
|
+
taskTitle =
|
|
4465
|
+
'Ask user what to build next and restart codeyam editor workflow';
|
|
4466
|
+
}
|
|
4467
|
+
console.log(chalk.bold.cyan('━━━ TASK ━━━'));
|
|
4468
|
+
console.log(chalk.cyan('Mark your current task (if any) as complete.'));
|
|
4469
|
+
console.log(chalk.cyan('Then create a new task with this EXACT title (copy verbatim):'));
|
|
4470
|
+
console.log();
|
|
4471
|
+
console.log(chalk.bold.cyan(`EXACT_TASK_TITLE: ${taskTitle}`));
|
|
4472
|
+
console.log();
|
|
4473
|
+
console.log(chalk.green(`After creating the task, re-run: codeyam editor ${state.step + 1}`));
|
|
4474
|
+
console.log();
|
|
4475
|
+
// Mark task as expected-created so the next step can proceed.
|
|
4476
|
+
// The hook will set taskCreated=true when it sees the actual TaskCreate call.
|
|
4477
|
+
// But we also allow advancement now since task-ontrack itself is the corrective action.
|
|
4478
|
+
const trackingPath = path.join(root, '.codeyam', 'editor-task-tracking.json');
|
|
4479
|
+
fs.writeFileSync(trackingPath, JSON.stringify({ step: state.step, taskCreated: true }, null, 2), 'utf8');
|
|
4480
|
+
return;
|
|
4481
|
+
}
|
|
4210
4482
|
// Subcommand: codeyam editor analyze-imports
|
|
4211
4483
|
if (argv.step === 'analyze-imports') {
|
|
4212
4484
|
await handleAnalyzeImports();
|
|
@@ -4296,18 +4568,13 @@ const editorCommand = {
|
|
|
4296
4568
|
}
|
|
4297
4569
|
else {
|
|
4298
4570
|
const state = readState(root);
|
|
4299
|
-
// Clear prompt file when feature is done (step 16) so the hook
|
|
4300
|
-
// can capture the next feature request from the user.
|
|
4301
|
-
if (state?.step === 16) {
|
|
4302
|
-
clearEditorUserPrompt(root);
|
|
4303
|
-
}
|
|
4304
4571
|
printCycleOverview(root, state);
|
|
4305
4572
|
}
|
|
4306
4573
|
return;
|
|
4307
4574
|
}
|
|
4308
4575
|
const step = argv.step ? parseInt(argv.step, 10) : undefined;
|
|
4309
|
-
if (step != null && (isNaN(step) || step < 1 || step >
|
|
4310
|
-
console.error(chalk.red(`Error: Invalid step "${argv.step}". Must be 1-
|
|
4576
|
+
if (step != null && (isNaN(step) || step < 1 || step > 18)) {
|
|
4577
|
+
console.error(chalk.red(`Error: Invalid step "${argv.step}". Must be 1-18.`));
|
|
4311
4578
|
process.exit(1);
|
|
4312
4579
|
}
|
|
4313
4580
|
if (step == null) {
|
|
@@ -4331,7 +4598,7 @@ const editorCommand = {
|
|
|
4331
4598
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
4332
4599
|
const { projectSlug } = config;
|
|
4333
4600
|
if (!projectSlug) {
|
|
4334
|
-
errorLog('Missing project slug. Try reinitializing with: codeyam
|
|
4601
|
+
errorLog('Missing project slug. Try reinitializing with: `codeyam editor template`');
|
|
4335
4602
|
return;
|
|
4336
4603
|
}
|
|
4337
4604
|
const connectionOk = await withoutSpinner(() => testEnvironment());
|
|
@@ -4728,7 +4995,7 @@ const editorCommand = {
|
|
|
4728
4995
|
// Step 1 is planning-only and may not persist state (no --feature flag).
|
|
4729
4996
|
const skipValidation = step === 2 && argv.feature;
|
|
4730
4997
|
if (!skipValidation) {
|
|
4731
|
-
const stepError = validateStepTransition(step, state?.step ?? null);
|
|
4998
|
+
const stepError = validateStepTransition(step, state?.step ?? null, state?.startedAt, root);
|
|
4732
4999
|
if (stepError) {
|
|
4733
5000
|
console.error(chalk.red(`Error: ${stepError}`));
|
|
4734
5001
|
process.exit(1);
|
|
@@ -4768,19 +5035,20 @@ const editorCommand = {
|
|
|
4768
5035
|
case 13:
|
|
4769
5036
|
case 14:
|
|
4770
5037
|
case 15:
|
|
4771
|
-
case 16:
|
|
5038
|
+
case 16:
|
|
5039
|
+
case 17:
|
|
5040
|
+
case 18: {
|
|
4772
5041
|
const feature = argv.feature || state?.feature;
|
|
4773
5042
|
if (!feature) {
|
|
4774
5043
|
console.error(chalk.red('Error: No feature in progress. Run codeyam editor 1 first.'));
|
|
4775
5044
|
process.exit(1);
|
|
4776
5045
|
}
|
|
4777
|
-
// Hard gate: steps
|
|
4778
|
-
if (step >=
|
|
5046
|
+
// Hard gate: steps 10+ require audit to have passed
|
|
5047
|
+
if (step >= 10) {
|
|
4779
5048
|
const auditOk = await checkAuditGate();
|
|
4780
5049
|
if (!auditOk) {
|
|
4781
5050
|
// checkAuditGate() already printed specific failure details above
|
|
4782
5051
|
console.error(chalk.red.bold('BLOCKED: The audit has not passed. Fix the issues listed above before proceeding.'));
|
|
4783
|
-
console.error(chalk.yellow('DO NOT re-run the audit in a loop — fix the underlying issues first.'));
|
|
4784
5052
|
process.exit(1);
|
|
4785
5053
|
}
|
|
4786
5054
|
}
|
|
@@ -4799,6 +5067,8 @@ const editorCommand = {
|
|
|
4799
5067
|
14: printStep14,
|
|
4800
5068
|
15: printStep15,
|
|
4801
5069
|
16: printStep16,
|
|
5070
|
+
17: printStep17,
|
|
5071
|
+
18: printStep18,
|
|
4802
5072
|
};
|
|
4803
5073
|
stepFns[step](root, feature);
|
|
4804
5074
|
break;
|