@codeyam/codeyam-cli 0.1.19 → 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/commands/editor.js +252 -62
- 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__/editorScenarios.test.js +20 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarios.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/editorScenarios.js +36 -4
- 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/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 +22 -2
- package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js.map +1 -1
- package/codeyam-cli/src/webserver/__tests__/stripClaudeCommand.test.js +57 -1
- package/codeyam-cli/src/webserver/__tests__/stripClaudeCommand.test.js.map +1 -1
- 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)-CGzKlIHg.js → editor.entity.(_sha)-DII1pg_z.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/{manifest-2ef99f38.js → manifest-cdf2c0a7.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/analysisRunner-B_PsTAb1.js +13 -0
- package/codeyam-cli/src/webserver/build/server/assets/{index-Cd-ufawF.js → index-CjLhfz6Z.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/{init-CzeBGOto.js → init-BEqlbI84.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/{server-build-Dht7CKXY.js → server-build-YI63xTu4.js} +94 -93
- 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 +51 -10
- package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
- package/codeyam-cli/templates/editor-step-hook.py +21 -0
- 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/server/assets/analysisRunner-BPmOG9bE.js +0 -13
|
@@ -121,6 +121,13 @@ function writeState(root, state) {
|
|
|
121
121
|
const dir = path.dirname(statePath);
|
|
122
122
|
fs.mkdirSync(dir, { recursive: true });
|
|
123
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
|
+
}
|
|
124
131
|
}
|
|
125
132
|
/**
|
|
126
133
|
* Clear the editor state (for starting a new feature).
|
|
@@ -248,11 +255,15 @@ function printAppScenarioInstructions(pageName, route) {
|
|
|
248
255
|
console.log(chalk.dim(' Use "mockData":{"routes":{"/api/...":{"body":[...]}}} to mock API responses'));
|
|
249
256
|
console.log(chalk.dim(' For external APIs: add "externalApis":{"GET https://...":{"body":[...],"status":200}}'));
|
|
250
257
|
}
|
|
251
|
-
|
|
252
|
-
console.log(chalk.
|
|
253
|
-
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();
|
|
254
265
|
checkbox('If the app uses auth, email, or other patterns: see FEATURE_PATTERNS.md for scenario guidance');
|
|
255
|
-
checkbox('After
|
|
266
|
+
checkbox('After registration, check the response for `clientErrors`');
|
|
256
267
|
console.log(chalk.yellow(' If clientErrors is non-empty → fix the issue and re-register the scenario'));
|
|
257
268
|
console.log(chalk.yellow(' Fix client errors and re-register before moving on'));
|
|
258
269
|
}
|
|
@@ -353,6 +364,10 @@ function printComponentCaptureInstructions() {
|
|
|
353
364
|
console.log(chalk.yellow(' If clientErrors is non-empty → fix the isolation route and re-register'));
|
|
354
365
|
console.log(chalk.yellow(' Fix client errors and re-register before moving on'));
|
|
355
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'));
|
|
356
371
|
}
|
|
357
372
|
/**
|
|
358
373
|
* Print a section header.
|
|
@@ -393,9 +408,31 @@ function printProgressTracker(current) {
|
|
|
393
408
|
*
|
|
394
409
|
* Options:
|
|
395
410
|
* - confirm: true → step requires user confirmation before proceeding (steps 1, 3, 11)
|
|
411
|
+
* - feature: string → feature name for task directive
|
|
396
412
|
*/
|
|
397
413
|
function stopGate(current, opts) {
|
|
398
|
-
|
|
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
|
+
}
|
|
399
436
|
console.log(chalk.bold.red('━━━ STOP ━━━'));
|
|
400
437
|
console.log();
|
|
401
438
|
console.log(chalk.red('Complete each checklist item above before proceeding to the next step.'));
|
|
@@ -404,20 +441,12 @@ function stopGate(current, opts) {
|
|
|
404
441
|
console.log(chalk.yellow('Wait for user confirmation before moving on.'));
|
|
405
442
|
}
|
|
406
443
|
console.log();
|
|
407
|
-
|
|
408
|
-
printProgressTracker(current);
|
|
409
|
-
console.log();
|
|
410
|
-
console.log(chalk.yellow('For the CURRENT step (→), show each checklist item with ✓ (done) or ✗ (skipped + reason).'));
|
|
411
|
-
console.log(chalk.yellow('If any items are ✗, explain why and ask if the user wants to address them.'));
|
|
412
|
-
console.log();
|
|
413
|
-
if (current < 18) {
|
|
444
|
+
if (current < totalSteps) {
|
|
414
445
|
console.log(chalk.green('When done, run: ') +
|
|
415
446
|
chalk.bold(`codeyam editor ${current + 1}`));
|
|
416
447
|
}
|
|
417
448
|
else {
|
|
418
|
-
console.log(chalk.green('Feature complete!
|
|
419
|
-
chalk.bold('codeyam editor steps') +
|
|
420
|
-
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'));
|
|
421
450
|
}
|
|
422
451
|
console.log();
|
|
423
452
|
}
|
|
@@ -785,9 +814,7 @@ function printCycleOverview(root, state) {
|
|
|
785
814
|
console.log();
|
|
786
815
|
console.log(chalk.green('Continue with: ') +
|
|
787
816
|
chalk.bold(`codeyam editor ${state.step}`));
|
|
788
|
-
console.log(chalk.dim('Or run ')
|
|
789
|
-
chalk.bold('codeyam editor 1') +
|
|
790
|
-
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)'));
|
|
791
818
|
console.log();
|
|
792
819
|
console.log(chalk.yellow('If the user reports a bug or requests any change, run: ') +
|
|
793
820
|
chalk.bold('codeyam editor change'));
|
|
@@ -815,7 +842,8 @@ function printCycleOverview(root, state) {
|
|
|
815
842
|
console.log(` ${chalk.bold.yellow('17')} ${chalk.bold('Finalize')} — Journal update + amend commit`);
|
|
816
843
|
console.log(` ${chalk.bold.yellow('18')} ${chalk.bold('Push')} — Push to remote`);
|
|
817
844
|
console.log();
|
|
818
|
-
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)');
|
|
819
847
|
console.log(chalk.dim(' If the user already described what they want, pass it: codeyam editor 1 --prompt "their message"'));
|
|
820
848
|
}
|
|
821
849
|
console.log(chalk.dim('If stuck on CLI commands, scenarios, or debugging, read: .codeyam/docs/editor-reference.md'));
|
|
@@ -894,12 +922,6 @@ function printStep1(root, feature, options, userPrompt) {
|
|
|
894
922
|
console.log();
|
|
895
923
|
console.log(chalk.yellow('Wait for user confirmation before moving on.'));
|
|
896
924
|
console.log();
|
|
897
|
-
console.log(chalk.bold.yellow('Present the following progress tracker to the user (copy it verbatim):'));
|
|
898
|
-
printProgressTracker(1);
|
|
899
|
-
console.log();
|
|
900
|
-
console.log(chalk.yellow('For the CURRENT step (→), show each checklist item with ✓ (done) or ✗ (skipped + reason).'));
|
|
901
|
-
console.log(chalk.yellow('If any items are ✗, explain why and ask if the user wants to address them.'));
|
|
902
|
-
console.log();
|
|
903
925
|
console.log(chalk.green('When done, run: ') +
|
|
904
926
|
chalk.bold('codeyam editor 2 --feature "Feature Name"'));
|
|
905
927
|
console.log(chalk.dim(' Replace "Feature Name" with a short title for what you just described.'));
|
|
@@ -927,13 +949,6 @@ function printStep2(root, feature) {
|
|
|
927
949
|
if (isResuming) {
|
|
928
950
|
printResumptionHeader(2);
|
|
929
951
|
}
|
|
930
|
-
console.log(chalk.bold.red('━━━ MANDATORY: CREATE TASK NOW ━━━'));
|
|
931
|
-
console.log();
|
|
932
|
-
console.log(chalk.red(`Run TaskCreate with title: "Use \`codeyam editor\` to work on ${feature}"`));
|
|
933
|
-
console.log(chalk.red('DO NOT read files, scaffold, write code, or do ANYTHING else until this task exists.'));
|
|
934
|
-
console.log(chalk.red('This is not optional. Create the task, then continue below.'));
|
|
935
|
-
console.log(chalk.bold.red('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
936
|
-
console.log();
|
|
937
952
|
console.log('Get the project ready to build.');
|
|
938
953
|
console.log();
|
|
939
954
|
// If no project exists yet, include scaffolding instructions first
|
|
@@ -1012,6 +1027,13 @@ function printStep3(root, feature) {
|
|
|
1012
1027
|
console.log(chalk.dim(' The seed adapter reads a JSON file (path passed as CLI arg), wipes tables, inserts rows.'));
|
|
1013
1028
|
console.log(chalk.dim(" Use the project's own ORM (Prisma, Drizzle, etc.). See template for example."));
|
|
1014
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'));
|
|
1015
1037
|
}
|
|
1016
1038
|
checkbox('Verify the dev server shows the changes');
|
|
1017
1039
|
checkbox('If the feature involves auth, email, payments, or other common patterns: read FEATURE_PATTERNS.md');
|
|
@@ -1051,6 +1073,8 @@ function printStep3(root, feature) {
|
|
|
1051
1073
|
console.log(chalk.cyan(' If you build a NEW page (e.g., /drinks/[id]), navigate the preview there:'));
|
|
1052
1074
|
console.log(chalk.cyan(` codeyam editor preview '{"path":"/drinks/1","dimension":"${dim}"}'`));
|
|
1053
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.'));
|
|
1054
1078
|
console.log();
|
|
1055
1079
|
stopGate(3);
|
|
1056
1080
|
}
|
|
@@ -1147,6 +1171,8 @@ function printStep5(root, feature) {
|
|
|
1147
1171
|
printDimensionGuidance(dim, dimNames);
|
|
1148
1172
|
console.log(chalk.dim(' The user needs to SEE the new feature. If you built a new page, navigate there.'));
|
|
1149
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.'));
|
|
1150
1176
|
console.log();
|
|
1151
1177
|
console.log(chalk.bold.cyan('Guide the user through interactive testing:'));
|
|
1152
1178
|
checkbox('Tell the user: "The preview is fully interactive — click around to test!"');
|
|
@@ -1335,6 +1361,7 @@ function printStep10(root, feature) {
|
|
|
1335
1361
|
console.log(chalk.cyan(' • Include realistic variety — different categories, lengths, statuses, counts'));
|
|
1336
1362
|
console.log(chalk.cyan(" • Populate optional fields the feature depends on (don't leave them empty)"));
|
|
1337
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'));
|
|
1338
1365
|
console.log(chalk.cyan(" • Think: would this data reveal a bug in the feature? If it's too simple, enrich it."));
|
|
1339
1366
|
console.log();
|
|
1340
1367
|
console.log(chalk.bold.cyan('When to create new vs reuse existing:'));
|
|
@@ -1549,7 +1576,7 @@ function printStep15(root, feature) {
|
|
|
1549
1576
|
console.log(chalk.bold('Checklist:'));
|
|
1550
1577
|
checkbox(`Show the results panel: \`codeyam editor show-results\``);
|
|
1551
1578
|
console.log(chalk.dim(' This opens a visual panel below the terminal showing all scenarios with screenshots.'));
|
|
1552
|
-
console.log(chalk.dim(' The
|
|
1579
|
+
console.log(chalk.dim(' The first scenario is automatically selected and its live preview is shown.'));
|
|
1553
1580
|
console.log(chalk.dim(' The user can click scenarios to switch the live preview.'));
|
|
1554
1581
|
checkbox('Write a 1-2 sentence summary of what was built');
|
|
1555
1582
|
checkbox('Report test count and audit status (one line)');
|
|
@@ -1789,7 +1816,7 @@ function printMigrateStep4(root) {
|
|
|
1789
1816
|
checkbox('Check for client errors: `codeyam editor client-errors`');
|
|
1790
1817
|
checkbox('If any issues: fix the scenario data, re-register affected scenarios');
|
|
1791
1818
|
checkbox('Show results: `codeyam editor show-results`');
|
|
1792
|
-
console.log(chalk.dim(' The
|
|
1819
|
+
console.log(chalk.dim(' The first scenario is automatically selected and its live preview is shown.'));
|
|
1793
1820
|
console.log();
|
|
1794
1821
|
console.log(chalk.dim('The user should now see their existing page with live screenshots in the preview.'));
|
|
1795
1822
|
migrationStopGate(4, pageName, pageIndex, totalPages);
|
|
@@ -1956,7 +1983,7 @@ function printMigrateStep10(root) {
|
|
|
1956
1983
|
console.log();
|
|
1957
1984
|
console.log(chalk.bold('Checklist:'));
|
|
1958
1985
|
checkbox('Show results: `codeyam editor show-results`');
|
|
1959
|
-
console.log(chalk.dim(' The
|
|
1986
|
+
console.log(chalk.dim(' The first scenario is automatically selected and its live preview is shown.'));
|
|
1960
1987
|
checkbox('Write a 1-2 sentence summary of what was migrated');
|
|
1961
1988
|
console.log();
|
|
1962
1989
|
console.log(chalk.bold('Present a selection menu (AskUserQuestion with these EXACT option labels):'));
|
|
@@ -2253,15 +2280,13 @@ function printStep18(root, feature) {
|
|
|
2253
2280
|
console.log(chalk.dim(' If the user says yes, run: `git push`'));
|
|
2254
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"'));
|
|
2255
2282
|
console.log(chalk.dim(' If the user wants help, guide them through `gh repo create` or manual GitHub setup, then push.'));
|
|
2256
|
-
|
|
2257
|
-
checkbox('Create a new task with EXACTLY this title: "Ask the user what to build next, then run `codeyam editor 1` to start the next feature"');
|
|
2258
|
-
console.log(chalk.dim(' There must ALWAYS be an active task. Never leave the task list empty.'));
|
|
2259
|
-
console.log(chalk.dim(' This task reminds you to use the editor workflow for the next feature.'));
|
|
2283
|
+
console.log(chalk.dim(' Task management is handled by the ━━━ TASK ━━━ directive below.'));
|
|
2260
2284
|
console.log();
|
|
2261
2285
|
console.log(chalk.bold.yellow('IMPORTANT: After this step, the current feature is DONE.'));
|
|
2262
2286
|
console.log(chalk.yellow(' Any new user request — even if it sounds related — is a NEW feature.'));
|
|
2263
2287
|
console.log(chalk.yellow(' Do NOT use `codeyam editor change` or start building directly.'));
|
|
2264
|
-
console.log(chalk.yellow('
|
|
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.'));
|
|
2265
2290
|
console.log(chalk.yellow(' The change workflow is ONLY for modifications during steps 1-15, not after commit.'));
|
|
2266
2291
|
stopGate(18, { confirm: true });
|
|
2267
2292
|
}
|
|
@@ -2705,11 +2730,13 @@ async function handleRegister(jsonArg) {
|
|
|
2705
2730
|
}
|
|
2706
2731
|
// Normalize to array for uniform handling
|
|
2707
2732
|
const items = Array.isArray(parsed.body) ? parsed.body : [parsed.body];
|
|
2733
|
+
const root = getProjectRoot();
|
|
2708
2734
|
const port = getServerPort();
|
|
2709
2735
|
const url = `http://localhost:${port}/api/editor-register-scenario`;
|
|
2710
2736
|
const isBatch = items.length > 1;
|
|
2711
2737
|
let succeeded = 0;
|
|
2712
2738
|
let failed = 0;
|
|
2739
|
+
const screenshotEntries = [];
|
|
2713
2740
|
for (let i = 0; i < items.length; i++) {
|
|
2714
2741
|
const body = items[i];
|
|
2715
2742
|
const prefix = isBatch ? chalk.dim(`[${i + 1}/${items.length}] `) : '';
|
|
@@ -2737,6 +2764,12 @@ async function handleRegister(jsonArg) {
|
|
|
2737
2764
|
else {
|
|
2738
2765
|
parts.push(`errors=0`);
|
|
2739
2766
|
}
|
|
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
|
+
}
|
|
2740
2773
|
if (data.seedResult) {
|
|
2741
2774
|
if (data.seedResult.success) {
|
|
2742
2775
|
parts.push(`seed=ok`);
|
|
@@ -2778,6 +2811,16 @@ async function handleRegister(jsonArg) {
|
|
|
2778
2811
|
}
|
|
2779
2812
|
else {
|
|
2780
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
|
+
}
|
|
2781
2824
|
}
|
|
2782
2825
|
}
|
|
2783
2826
|
catch (error) {
|
|
@@ -2788,6 +2831,33 @@ async function handleRegister(jsonArg) {
|
|
|
2788
2831
|
failed++;
|
|
2789
2832
|
}
|
|
2790
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
|
+
}
|
|
2791
2861
|
if (isBatch) {
|
|
2792
2862
|
console.log(chalk.dim(`\nBatch complete: ${succeeded}/${items.length} succeeded`));
|
|
2793
2863
|
}
|
|
@@ -3003,6 +3073,8 @@ function handleChange(feature) {
|
|
|
3003
3073
|
console.log(chalk.cyan(` codeyam editor preview '{"dimension":"${dim}"}'`));
|
|
3004
3074
|
printDimensionGuidance(dim, dimNames);
|
|
3005
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.'));
|
|
3006
3078
|
console.log();
|
|
3007
3079
|
console.log(chalk.bold('0. Close the results panel:'));
|
|
3008
3080
|
checkbox(`Hide results: \`codeyam editor hide-results\``);
|
|
@@ -3104,14 +3176,33 @@ async function checkAuditGate() {
|
|
|
3104
3176
|
return true; // Server unreachable — don't block
|
|
3105
3177
|
if (data.summary?.allPassing === true)
|
|
3106
3178
|
return true;
|
|
3107
|
-
// 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)
|
|
3108
3181
|
const { isAutoRemediable } = await import('../utils/editorAudit.js');
|
|
3109
3182
|
if (isAutoRemediable(data.summary, false)) {
|
|
3110
3183
|
try {
|
|
3111
3184
|
await handleAnalyzeImports({ silent: true });
|
|
3112
3185
|
}
|
|
3113
3186
|
catch {
|
|
3114
|
-
|
|
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
|
|
3115
3206
|
}
|
|
3116
3207
|
// Re-check after fix
|
|
3117
3208
|
const retry = await fetchAuditResult();
|
|
@@ -3188,6 +3279,13 @@ function printAuditGateFailures(data) {
|
|
|
3188
3279
|
issues.push(`${s.incompleteEntities} entity/entities need import analysis`);
|
|
3189
3280
|
}
|
|
3190
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
|
+
}
|
|
3191
3289
|
if (s.miscategorizedScenarios > 0)
|
|
3192
3290
|
issues.push(`${s.miscategorizedScenarios} component(s) using page URLs instead of isolation routes`);
|
|
3193
3291
|
if (s.scenariosNeedingRecapture > 0)
|
|
@@ -3232,30 +3330,50 @@ async function handleAudit() {
|
|
|
3232
3330
|
console.error(chalk.red('Error: Could not reach the CodeYam server. Is it running?'));
|
|
3233
3331
|
process.exit(1);
|
|
3234
3332
|
}
|
|
3235
|
-
// Auto-fix incomplete entities — but only once.
|
|
3236
|
-
// 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
|
|
3237
3335
|
// instead of retrying. The analysis may have errors (e.g., stale entity
|
|
3238
3336
|
// references from refactoring) that can't be fixed by re-running.
|
|
3239
3337
|
const incompleteBeforeFix = data.incompleteEntities || [];
|
|
3338
|
+
const unassociatedBeforeFix = data.unassociatedScenarios || [];
|
|
3240
3339
|
let autoRemediationFailed = false;
|
|
3241
|
-
if (incompleteBeforeFix.length > 0) {
|
|
3242
|
-
|
|
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' : ''}...`));
|
|
3243
3343
|
try {
|
|
3244
3344
|
await handleAnalyzeImports({ silent: true });
|
|
3245
3345
|
}
|
|
3246
3346
|
catch {
|
|
3247
|
-
// 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
|
|
3248
3366
|
}
|
|
3249
3367
|
// Re-fetch audit results after the fix
|
|
3250
3368
|
data = await fetchAuditResult();
|
|
3251
3369
|
if (!data) {
|
|
3252
|
-
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.'));
|
|
3253
3371
|
process.exit(1);
|
|
3254
3372
|
}
|
|
3255
|
-
// If
|
|
3256
|
-
// 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
|
|
3257
3374
|
const incompleteAfterFix = data.incompleteEntities || [];
|
|
3258
|
-
|
|
3375
|
+
const unassociatedAfterFix = data.unassociatedScenarios || [];
|
|
3376
|
+
if (incompleteAfterFix.length > 0 || unassociatedAfterFix.length > 0) {
|
|
3259
3377
|
autoRemediationFailed = true;
|
|
3260
3378
|
}
|
|
3261
3379
|
}
|
|
@@ -3359,15 +3477,33 @@ async function handleAudit() {
|
|
|
3359
3477
|
console.log(` ${chalk.red('✗')} ${e.name} — ${e.scenarioCount} scenario${e.scenarioCount !== 1 ? 's' : ''} but entity not analyzed`);
|
|
3360
3478
|
}
|
|
3361
3479
|
if (autoRemediationFailed) {
|
|
3362
|
-
console.log(chalk.
|
|
3363
|
-
console.log(chalk.
|
|
3364
|
-
console.log(chalk.yellow(' This means the analysis failed for these entities. Common causes:'));
|
|
3365
|
-
console.log(chalk.yellow(' • Source file was renamed/deleted but glossary still references the old path'));
|
|
3366
|
-
console.log(chalk.yellow(' • Entity has import errors that prevent analysis'));
|
|
3367
|
-
console.log(chalk.yellow(' To investigate: run `codeyam editor analyze-imports` (without --silent) to see the full error.'));
|
|
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.'));
|
|
3368
3482
|
}
|
|
3369
3483
|
else {
|
|
3370
|
-
console.log(chalk.yellow(' Run `codeyam editor analyze-imports` to analyze these entities'));
|
|
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.'));
|
|
3504
|
+
}
|
|
3505
|
+
else {
|
|
3506
|
+
console.log(chalk.yellow(' Run `codeyam editor analyze-imports` to create entity records, then re-run audit to backfill.'));
|
|
3371
3507
|
}
|
|
3372
3508
|
console.log();
|
|
3373
3509
|
}
|
|
@@ -3444,6 +3580,9 @@ async function handleAudit() {
|
|
|
3444
3580
|
if (summary.incompleteEntities > 0) {
|
|
3445
3581
|
parts.push(`${summary.incompleteEntities} entit${summary.incompleteEntities !== 1 ? 'ies' : 'y'} need import analysis`);
|
|
3446
3582
|
}
|
|
3583
|
+
if (summary.unassociatedScenarios > 0) {
|
|
3584
|
+
parts.push(`${summary.unassociatedScenarios} component${summary.unassociatedScenarios !== 1 ? 's' : ''} with scenarios missing entity link`);
|
|
3585
|
+
}
|
|
3447
3586
|
if (summary.miscategorizedScenarios > 0) {
|
|
3448
3587
|
parts.push(`${summary.miscategorizedScenarios} component${summary.miscategorizedScenarios !== 1 ? 's' : ''} using page URLs instead of isolation routes`);
|
|
3449
3588
|
}
|
|
@@ -4183,8 +4322,8 @@ const editorCommand = {
|
|
|
4183
4322
|
describe: 'Editor mode guided workflow',
|
|
4184
4323
|
builder: (yargs) => {
|
|
4185
4324
|
const stepDescription = IS_INTERNAL_BUILD
|
|
4186
|
-
? 'Step number (1-18) or subcommand (template, register, isolate, analyze-imports, dependents, audit, scenarios, scenario-coverage, recapture-stale, change, sync, debug, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors)'
|
|
4187
|
-
: 'Step number (1-18) or subcommand (template, register, isolate, analyze-imports, dependents, audit, scenarios, scenario-coverage, recapture-stale, change, sync, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors)';
|
|
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)';
|
|
4188
4327
|
let builder = yargs
|
|
4189
4328
|
.positional('step', {
|
|
4190
4329
|
type: 'string',
|
|
@@ -4245,6 +4384,18 @@ const editorCommand = {
|
|
|
4245
4384
|
// API subcommands: preview, show-results, hide-results, commit,
|
|
4246
4385
|
// journal, journal-update, dev-server, client-errors
|
|
4247
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
|
+
}
|
|
4248
4399
|
const port = getServerPort();
|
|
4249
4400
|
try {
|
|
4250
4401
|
const request = buildEditorApiRequest(argv.step, argv.json || undefined);
|
|
@@ -4288,6 +4439,46 @@ const editorCommand = {
|
|
|
4288
4439
|
await handleGlossaryAdd(argv.json || '');
|
|
4289
4440
|
return;
|
|
4290
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
|
+
}
|
|
4291
4482
|
// Subcommand: codeyam editor analyze-imports
|
|
4292
4483
|
if (argv.step === 'analyze-imports') {
|
|
4293
4484
|
await handleAnalyzeImports();
|
|
@@ -4804,7 +4995,7 @@ const editorCommand = {
|
|
|
4804
4995
|
// Step 1 is planning-only and may not persist state (no --feature flag).
|
|
4805
4996
|
const skipValidation = step === 2 && argv.feature;
|
|
4806
4997
|
if (!skipValidation) {
|
|
4807
|
-
const stepError = validateStepTransition(step, state?.step ?? null);
|
|
4998
|
+
const stepError = validateStepTransition(step, state?.step ?? null, state?.startedAt, root);
|
|
4808
4999
|
if (stepError) {
|
|
4809
5000
|
console.error(chalk.red(`Error: ${stepError}`));
|
|
4810
5001
|
process.exit(1);
|
|
@@ -4858,7 +5049,6 @@ const editorCommand = {
|
|
|
4858
5049
|
if (!auditOk) {
|
|
4859
5050
|
// checkAuditGate() already printed specific failure details above
|
|
4860
5051
|
console.error(chalk.red.bold('BLOCKED: The audit has not passed. Fix the issues listed above before proceeding.'));
|
|
4861
|
-
console.error(chalk.yellow('DO NOT re-run the audit in a loop — fix the underlying issues first.'));
|
|
4862
5052
|
process.exit(1);
|
|
4863
5053
|
}
|
|
4864
5054
|
}
|