@codeyam/codeyam-cli 0.1.0-staging.9574237 → 0.1.0-staging.a77070e
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 +6 -6
- package/analyzer-template/log.txt +3 -3
- package/codeyam-cli/src/cli.js +9 -0
- package/codeyam-cli/src/cli.js.map +1 -1
- package/codeyam-cli/src/commands/__tests__/editor.isolateArgs.test.js +51 -0
- package/codeyam-cli/src/commands/__tests__/editor.isolateArgs.test.js.map +1 -0
- package/codeyam-cli/src/commands/editor.js +481 -72
- package/codeyam-cli/src/commands/editor.js.map +1 -1
- package/codeyam-cli/src/commands/editorIsolateArgs.js +25 -0
- package/codeyam-cli/src/commands/editorIsolateArgs.js.map +1 -0
- package/codeyam-cli/src/commands/init.js +1 -0
- package/codeyam-cli/src/commands/init.js.map +1 -1
- package/codeyam-cli/src/commands/telemetry.js +37 -0
- package/codeyam-cli/src/commands/telemetry.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +1534 -1
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorCaptureScenarioSeeding.test.js +137 -0
- package/codeyam-cli/src/utils/__tests__/editorCaptureScenarioSeeding.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js +70 -0
- package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js +97 -4
- package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js +70 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +185 -7
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js +48 -1
- package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/glossaryAdd.test.js +177 -0
- package/codeyam-cli/src/utils/__tests__/glossaryAdd.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/scenarioCoverage.test.js +57 -0
- package/codeyam-cli/src/utils/__tests__/scenarioCoverage.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js +180 -1
- package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/telemetry.test.js +159 -0
- package/codeyam-cli/src/utils/__tests__/telemetry.test.js.map +1 -0
- package/codeyam-cli/src/utils/backgroundServer.js +1 -1
- package/codeyam-cli/src/utils/backgroundServer.js.map +1 -1
- package/codeyam-cli/src/utils/editorAudit.js +287 -1
- package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
- package/codeyam-cli/src/utils/editorEntityChangeStatus.js +13 -7
- package/codeyam-cli/src/utils/editorEntityChangeStatus.js.map +1 -1
- package/codeyam-cli/src/utils/editorEntityHelpers.js +18 -3
- package/codeyam-cli/src/utils/editorEntityHelpers.js.map +1 -1
- package/codeyam-cli/src/utils/editorScenarioSwitch.js +24 -2
- package/codeyam-cli/src/utils/editorScenarioSwitch.js.map +1 -1
- package/codeyam-cli/src/utils/editorScenarios.js +73 -12
- package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
- package/codeyam-cli/src/utils/entityChangeStatus.js +9 -3
- package/codeyam-cli/src/utils/entityChangeStatus.js.map +1 -1
- package/codeyam-cli/src/utils/fileWatcher.js +38 -0
- package/codeyam-cli/src/utils/fileWatcher.js.map +1 -1
- package/codeyam-cli/src/utils/glossaryAdd.js +74 -0
- package/codeyam-cli/src/utils/glossaryAdd.js.map +1 -0
- package/codeyam-cli/src/utils/install-skills.js +5 -0
- package/codeyam-cli/src/utils/install-skills.js.map +1 -1
- package/codeyam-cli/src/utils/scenarioCoverage.js +4 -1
- package/codeyam-cli/src/utils/scenarioCoverage.js.map +1 -1
- package/codeyam-cli/src/utils/scenariosManifest.js +36 -0
- package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -1
- package/codeyam-cli/src/utils/telemetry.js +106 -0
- package/codeyam-cli/src/utils/telemetry.js.map +1 -0
- package/codeyam-cli/src/utils/telemetryMiddleware.js +22 -0
- package/codeyam-cli/src/utils/telemetryMiddleware.js.map +1 -0
- package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js +40 -0
- package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js.map +1 -1
- package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js +61 -0
- package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js.map +1 -1
- package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js +92 -21
- package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js.map +1 -1
- package/codeyam-cli/src/webserver/app/lib/clientErrors.js +7 -1
- package/codeyam-cli/src/webserver/app/lib/clientErrors.js.map +1 -1
- package/codeyam-cli/src/webserver/backgroundServer.js +42 -57
- package/codeyam-cli/src/webserver/backgroundServer.js.map +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{CopyButton-CzTDWkF2.js → CopyButton-CLe80MMu.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{EntityItem-BFbq6iFk.js → EntityItem-Crt_KN_U.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeIcon-B6OMi58N.js → EntityTypeIcon-CD7lGABo.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{InlineSpinner-DuYodzo1.js → InlineSpinner-CgTNOhnu.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{InteractivePreview-CXo9EeCl.js → InteractivePreview-CKeQT5Ty.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/{LibraryFunctionPreview-DYCNb2It.js → LibraryFunctionPreview-D3s1MFkb.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{LogViewer-CZgY3sxX.js → LogViewer-CM5zg40N.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{ReportIssueModal-CnYYwRDw.js → ReportIssueModal-C2PLkej3.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{SafeScreenshot-CDoF7ZpU.js → SafeScreenshot-DanvyBPb.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-DrnfvaLL.js → ScenarioViewer-DUMfcNVK.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{Spinner-Df3UCi8k.js → Spinner-D0LgAaSa.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{ViewportInspectBar-DRKR9T0U.js → ViewportInspectBar-BA_Ry-rs.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{_index-ClR-g3tY.js → _index-BAWd-Xjf.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{activity.(_tab)-DTH6ydEA.js → activity.(_tab)-BOARiB-g.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{addon-web-links-74hnHF59.js → addon-web-links-CHx25PAe.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{agent-transcripts-B8CYhCO9.js → agent-transcripts-Bg3e7q4S.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{book-open-CLaoh4ac.js → book-open-CL-lMgHh.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{chevron-down-BZ2DZxbW.js → chevron-down-GmAjGS9-.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{chunk-JZWAC4HX-BBXArFPl.js → chunk-JZWAC4HX-BAdwhyCx.js} +11 -11
- package/codeyam-cli/src/webserver/build/client/assets/{circle-check-CT4unAk-.js → circle-check-DFcQkN5j.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{copy-zK0B6Nu-.js → copy-C6iF61Xs.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{createLucideIcon-DJB0YQJL.js → createLucideIcon-4ImjHTVC.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{dev.empty-CkXFP_i-.js → dev.empty-C8y4mmyv.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/editor._tab-Gbk_i5Js.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-Bnx7yUP0.js +58 -0
- package/codeyam-cli/src/webserver/build/client/assets/editorPreview-oepecPae.js +41 -0
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-BqAN7hyG.js → entity._sha._-Blfy9UlN.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.dev-BOi8kpwd.js → entity._sha.scenarios._scenarioId.dev-KTQuL0aj.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.fullscreen-Dg1NhIms.js → entity._sha.scenarios._scenarioId.fullscreen-C6eeL24i.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.create-scenario-CJX6kkkV.js → entity._sha_.create-scenario-DQM8E7L4.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-BhVjZhKg.js → entity._sha_.edit._scenarioId-CAoXLsQr.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entry.client-_gzKltPN.js → entry.client-SuW9syRS.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{files-CV_17tZS.js → files-D-xGrg29.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{git-D-YXmMbR.js → git-Bq_fbXP5.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/globals-fAqOD9ex.css +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{index-CCrgCshv.js → index-Bp1l4hSv.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{index-BsX0F-9C.js → index-CWV9XZiG.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{index-Blo6EK8G.js → index-DE3jI_dv.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{labs-Byazq8Pv.js → labs-B_IX45ih.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{loader-circle-DVQ0oHR7.js → loader-circle-De-7qQ2u.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/manifest-3157d6b8.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{memory-b-VmA2Vj.js → memory-Cx2xEx7s.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{pause-DGcndCAa.js → pause-CFxEKL1u.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/root-DB3O9_9j.js +67 -0
- package/codeyam-cli/src/webserver/build/client/assets/{search-C0Uw0bcK.js → search-BdBb5aqc.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{settings-OoNgHIfW.js → settings-DdE-Untf.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{simulations-Bcemfu8a.js → simulations-DSCdE99u.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{terminal-BgMmG7R9.js → terminal-CrplD4b1.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{triangle-alert-Cs87hJYK.js → triangle-alert-DqJ0j69l.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{useCustomSizes-BR3Rs7JY.js → useCustomSizes-DhXHbEjP.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{useLastLogLine-BxxP_XF9.js → useLastLogLine-BNd5hYuW.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{useReportContext-BermyNU5.js → useReportContext-Cy5Qg_UR.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{useToast-a_QN_W9_.js → useToast-5HR2j9ZE.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/{analysisRunner-DXQyOV0G.js → analysisRunner-BMmkgAkg.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/{index-CHSrVJtC.js → index-DxB0pOSt.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/{init-DL8vWZ6m.js → init-DLYLaqqP.js} +2 -2
- package/codeyam-cli/src/webserver/build/server/assets/server-build-CcyitQLQ.js +551 -0
- 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/editorProxy.js +78 -3
- package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
- package/codeyam-cli/src/webserver/idleDetector.js +41 -8
- package/codeyam-cli/src/webserver/idleDetector.js.map +1 -1
- package/codeyam-cli/src/webserver/scripts/journalCapture.ts +36 -0
- package/codeyam-cli/src/webserver/server.js +32 -0
- package/codeyam-cli/src/webserver/server.js.map +1 -1
- package/codeyam-cli/src/webserver/terminalServer.js +5 -5
- package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
- package/codeyam-cli/templates/codeyam-editor-reference.md +214 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/package.json +1 -1
- package/codeyam-cli/templates/nextjs-prisma-supabase/package.json +1 -1
- package/package.json +2 -1
- package/codeyam-cli/src/webserver/build/client/assets/editor._tab-DPw7NZHc.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-Dmg9cGK3.js +0 -58
- package/codeyam-cli/src/webserver/build/client/assets/editorPreview-DBa7T2FK.js +0 -41
- package/codeyam-cli/src/webserver/build/client/assets/globals-Bqg9V6XV.css +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/manifest-422a3551.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/root-ue8uWVRS.js +0 -67
- package/codeyam-cli/src/webserver/build/server/assets/server-build-BUKVjBSZ.js +0 -501
|
@@ -205,28 +205,45 @@ function checkbox(text) {
|
|
|
205
205
|
* Prints seed-based scenarios, mock-based fallback, @ file prefix, externalApis, url requirement, and error checking.
|
|
206
206
|
*/
|
|
207
207
|
function printAppScenarioInstructions(pageName, route) {
|
|
208
|
+
const root = process.cwd();
|
|
209
|
+
const hasSeedAdapter = !!detectSeedAdapter(root);
|
|
208
210
|
checkbox('Check existing scenarios: `codeyam editor scenarios`');
|
|
209
211
|
console.log(chalk.dim(' Review existing scenarios — reuse and update their data rather than'));
|
|
210
212
|
console.log(chalk.dim(' creating duplicates. Re-register with the same name to update a scenario.'));
|
|
211
|
-
|
|
212
|
-
console.log(chalk.yellow('
|
|
213
|
+
console.log();
|
|
214
|
+
console.log(chalk.bold.yellow('App scenarios vs component scenarios — these are DIFFERENT:'));
|
|
215
|
+
console.log(chalk.yellow(' App scenarios: show the FULL PAGE with seeded data at a real route (/, /library, etc.)'));
|
|
216
|
+
console.log(chalk.yellow(' Component scenarios: show ONE COMPONENT in isolation at /isolated-components/...'));
|
|
217
|
+
console.log(chalk.yellow(' This step is about APP scenarios. Do NOT set "componentName" — that makes it a component scenario.'));
|
|
218
|
+
console.log();
|
|
219
|
+
checkbox('Identify every page/route in the app and ensure each has app-level scenarios');
|
|
220
|
+
console.log(chalk.dim(" Check the app's router/entry points for all distinct pages"));
|
|
221
|
+
console.log(chalk.yellow(' Every page needs at least 2-3 app scenarios — not just the main page'));
|
|
213
222
|
if (pageName) {
|
|
214
223
|
console.log(chalk.dim(` Example: "${pageName} - Full Data", "${pageName} - Empty State"`));
|
|
215
224
|
}
|
|
216
225
|
else {
|
|
217
|
-
console.log(chalk.dim('
|
|
218
|
-
console.log(chalk.dim(' Detail: "Detail - With Reviews", "Detail - No Reviews", "Detail - High Rated"'));
|
|
226
|
+
console.log(chalk.dim(' Example: "Page - Full Data", "Page - Empty State", "Page - Error State"'));
|
|
219
227
|
}
|
|
220
228
|
console.log();
|
|
221
|
-
checkbox('Register each scenario with type "application"
|
|
222
|
-
console.log(chalk.dim(` codeyam editor register '{"name":"${pageName || 'Page'} - Full Data"
|
|
223
|
-
console.log(chalk.
|
|
224
|
-
console.log(chalk.yellow('
|
|
229
|
+
checkbox('Register each scenario with type "application", the real page URL, and pageFilePath:');
|
|
230
|
+
console.log(chalk.dim(` codeyam editor register '{"name":"${pageName || 'Page'} - Full Data",`));
|
|
231
|
+
console.log(chalk.dim(` "type":"application","url":"${route || '/'}","pageFilePath":"src/path/to/Page.tsx",...}'`));
|
|
232
|
+
console.log(chalk.yellow(' Required fields: "type":"application", "url" (real route), "pageFilePath" (source file)'));
|
|
233
|
+
console.log(chalk.yellow(' Do NOT include "componentName" — that would make it a component scenario'));
|
|
225
234
|
console.log();
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
235
|
+
if (hasSeedAdapter) {
|
|
236
|
+
checkbox(chalk.bold.red('REQUIRED: Every app scenario MUST include "seed" data — without it the page will be empty!'));
|
|
237
|
+
console.log(chalk.yellow(' A seed adapter exists — you MUST include "seed":{...} in every app scenario registration.'));
|
|
238
|
+
console.log(chalk.yellow(' An app scenario without seed data will render an empty page (no database rows = nothing to show).'));
|
|
239
|
+
console.log(chalk.dim(' "seed" maps table names to arrays of rows: "seed":{"user":[{"id":1,"name":"Alice"}],"article":[...]}'));
|
|
240
|
+
console.log(chalk.dim(' For external APIs: also add "externalApis":{"GET https://...":{"body":[...],"status":200}}'));
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
checkbox('Include data in every app scenario — without it the page will be empty:');
|
|
244
|
+
console.log(chalk.dim(' Use "mockData":{"routes":{"/api/...":{"body":[...]}}} to mock API responses'));
|
|
245
|
+
console.log(chalk.dim(' For external APIs: add "externalApis":{"GET https://...":{"body":[...],"status":200}}'));
|
|
246
|
+
}
|
|
230
247
|
checkbox('For large seed/mock data, write JSON to a temp file and use @ prefix:');
|
|
231
248
|
console.log(chalk.dim(' Write JSON to .codeyam/tmp/scenario.json then register with:'));
|
|
232
249
|
console.log(chalk.dim(' codeyam editor register @.codeyam/tmp/scenario.json'));
|
|
@@ -313,7 +330,7 @@ function printComponentCaptureInstructions() {
|
|
|
313
330
|
console.log(chalk.dim(' The route defines a `scenarios` object mapping scenario names to props,'));
|
|
314
331
|
console.log(chalk.dim(' reads `?s=ScenarioName` from the URL, and renders the component with those props.'));
|
|
315
332
|
console.log(chalk.dim(' Wrap the component in a capture container with id="codeyam-capture":'));
|
|
316
|
-
console.log(chalk.dim(' <div id="codeyam-capture" style={{ display:"inline-block"
|
|
333
|
+
console.log(chalk.dim(' <div id="codeyam-capture" style={{ display:"inline-block" }}>'));
|
|
317
334
|
console.log(chalk.dim(' <div style={{ width:"100%", maxWidth:"..." }}> ← match the app\'s container width'));
|
|
318
335
|
console.log(chalk.dim(' e.g. card in a 3-col grid → maxWidth:"24rem", full-width component → omit maxWidth'));
|
|
319
336
|
console.log(chalk.dim(' The screenshot captures just this wrapper, so the component fills the image.'));
|
|
@@ -324,7 +341,8 @@ function printComponentCaptureInstructions() {
|
|
|
324
341
|
console.log(chalk.dim(` "componentName":"ComponentName","componentPath":"path/to/file.tsx",`));
|
|
325
342
|
console.log(chalk.dim(` "url":"/codeyam-isolate/ComponentName?s=Scenario",`));
|
|
326
343
|
console.log(chalk.dim(` "mockData":{"routes":{"/api/...":{"body":[...]}}}}'`));
|
|
327
|
-
console.log(chalk.
|
|
344
|
+
console.log(chalk.yellow(' IMPORTANT: url MUST be an isolation route (/codeyam-isolate/... or /isolated-components/...).'));
|
|
345
|
+
console.log(chalk.yellow(' Do NOT use real page URLs (like /library) for component scenarios.'));
|
|
328
346
|
console.log(chalk.dim(' mockData.routes provides data for API calls the component makes internally'));
|
|
329
347
|
console.log(chalk.dim(' (omit mockData if the component has no internal API calls)'));
|
|
330
348
|
console.log(chalk.yellow(' 5. IMPORTANT: Check the register response for `clientErrors` array'));
|
|
@@ -792,6 +810,7 @@ function printCycleOverview(root, state) {
|
|
|
792
810
|
console.log(chalk.green('Start now: ') + chalk.bold('codeyam editor 1'));
|
|
793
811
|
console.log(chalk.dim(' If the user already described what they want, pass it: codeyam editor 1 --prompt "their message"'));
|
|
794
812
|
}
|
|
813
|
+
console.log(chalk.dim('If stuck on CLI commands, scenarios, or debugging, read: .codeyam/docs/editor-reference.md'));
|
|
795
814
|
console.log();
|
|
796
815
|
}
|
|
797
816
|
// ─── Step 1: Plan ─────────────────────────────────────────────────────
|
|
@@ -931,13 +950,29 @@ function printStep2(root, feature) {
|
|
|
931
950
|
console.log();
|
|
932
951
|
console.log(chalk.bold('Build the feature:'));
|
|
933
952
|
}
|
|
953
|
+
else {
|
|
954
|
+
console.log(chalk.bold('Prepare the database for this feature:'));
|
|
955
|
+
checkbox('List existing scenarios: `codeyam editor scenarios`');
|
|
956
|
+
console.log(chalk.dim(' Review existing scenarios to find seed data that best demonstrates the feature.'));
|
|
957
|
+
console.log(chalk.dim(" Don't create throwaway seed data — use what's already been curated."));
|
|
958
|
+
checkbox('Pick the scenario with seed data most relevant to this feature');
|
|
959
|
+
console.log(chalk.dim(" Think: which existing data state best exercises the feature you're building?"));
|
|
960
|
+
console.log(chalk.dim(' A scenario with diverse, realistic data is usually the best starting point.'));
|
|
961
|
+
checkbox('Load that scenario to seed the database and preview it:');
|
|
962
|
+
console.log(chalk.dim(` codeyam editor preview '{"scenarioId":"<id>","dimension":"${dim}"}'`));
|
|
963
|
+
console.log(chalk.dim(' This switches the active scenario, runs the seed adapter, and refreshes the preview.'));
|
|
964
|
+
console.log(chalk.dim(' If no existing scenario fits, use the default seed: npm run db:seed'));
|
|
965
|
+
console.log();
|
|
966
|
+
}
|
|
934
967
|
console.log(chalk.bold('Checklist:'));
|
|
935
968
|
checkbox('Create API routes that read from the database via Prisma');
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
969
|
+
if (!projectExists) {
|
|
970
|
+
checkbox('Seed the database with demo data');
|
|
971
|
+
checkbox('Create `.codeyam/seed-adapter.ts` so CodeYam can seed the database for scenarios');
|
|
972
|
+
console.log(chalk.dim(' The seed adapter reads a JSON file (path passed as CLI arg), wipes tables, inserts rows.'));
|
|
973
|
+
console.log(chalk.dim(" Use the project's own ORM (Prisma, Drizzle, etc.). See template for example."));
|
|
974
|
+
console.log(chalk.dim(' Run with: npx tsx .codeyam/seed-adapter.ts <path-to-seed-data.json>'));
|
|
975
|
+
}
|
|
941
976
|
checkbox('Verify the dev server shows the changes');
|
|
942
977
|
checkbox('If the feature involves auth, email, payments, or other common patterns: read FEATURE_PATTERNS.md');
|
|
943
978
|
// Responsive design guidance when building a mobile-responsive web app
|
|
@@ -1420,8 +1455,6 @@ function printStep12(root, feature) {
|
|
|
1420
1455
|
}
|
|
1421
1456
|
// ─── Step 13: Present ─────────────────────────────────────────────────
|
|
1422
1457
|
function printStep13(root, feature) {
|
|
1423
|
-
const port = getServerPort();
|
|
1424
|
-
const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
|
|
1425
1458
|
const prevState = readState(root);
|
|
1426
1459
|
const isResuming = prevState?.step === 13;
|
|
1427
1460
|
const now = new Date().toISOString();
|
|
@@ -1440,18 +1473,9 @@ function printStep13(root, feature) {
|
|
|
1440
1473
|
console.log('Present the results to the user and get their approval.');
|
|
1441
1474
|
console.log();
|
|
1442
1475
|
console.log(chalk.bold('Checklist:'));
|
|
1443
|
-
checkbox('Fetch all scenarios: `codeyam editor scenarios`');
|
|
1444
|
-
console.log(chalk.dim(' Each scenario has a `changeStatus` field: "new", "edited", "impacted", or null.'));
|
|
1445
|
-
checkbox('Pick the best scenario to showcase from this working session:');
|
|
1446
|
-
console.log(chalk.dim(' Prefer scenarios with changeStatus "new" or "edited" (directly changed in this session).'));
|
|
1447
|
-
console.log(chalk.dim(' For app-level scenarios, prefer those showing the new/changed functionality.'));
|
|
1448
|
-
console.log(chalk.dim(' NEVER pick a scenario with changeStatus null — those are unchanged from a previous commit.'));
|
|
1449
|
-
checkbox('Switch the preview to that scenario using its `id` and `dimension`:');
|
|
1450
|
-
console.log(chalk.dim(` codeyam editor preview '{"scenarioId":"<id>","dimension":"${dim}"}'`));
|
|
1451
|
-
console.log(chalk.dim(' Use the dimension that matches the scenario — check its registered dimensions.'));
|
|
1452
|
-
printDimensionGuidance(dim, dimNames);
|
|
1453
1476
|
checkbox(`Show the results panel: \`codeyam editor show-results\``);
|
|
1454
1477
|
console.log(chalk.dim(' This opens a visual panel below the terminal showing all scenarios with screenshots.'));
|
|
1478
|
+
console.log(chalk.dim(' The preview automatically switches to the first app-level scenario.'));
|
|
1455
1479
|
console.log(chalk.dim(' The user can click scenarios to switch the live preview.'));
|
|
1456
1480
|
checkbox('Write a 1-2 sentence summary of what was built');
|
|
1457
1481
|
checkbox('Report test count and audit status (one line)');
|
|
@@ -1681,7 +1705,6 @@ function printMigrateStep4(root) {
|
|
|
1681
1705
|
}
|
|
1682
1706
|
const { pageName, pageIndex, totalPages } = pageInfo;
|
|
1683
1707
|
writeMigrationStepState(root, 4, pageName, pageIndex, totalPages);
|
|
1684
|
-
const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
|
|
1685
1708
|
console.log();
|
|
1686
1709
|
console.log(chalk.bold.cyan(`━━━ Migration Step 4: Preview — ${pageName} (${pageIndex + 1}/${totalPages}) ━━━`));
|
|
1687
1710
|
console.log();
|
|
@@ -1691,11 +1714,8 @@ function printMigrateStep4(root) {
|
|
|
1691
1714
|
checkbox('Review all scenario screenshots for this page — do they look correct?');
|
|
1692
1715
|
checkbox('Check for client errors: `codeyam editor client-errors`');
|
|
1693
1716
|
checkbox('If any issues: fix the scenario data, re-register affected scenarios');
|
|
1694
|
-
checkbox('Fetch all scenarios: `codeyam editor scenarios`');
|
|
1695
|
-
checkbox('Pick the best scenario to showcase:');
|
|
1696
|
-
console.log(chalk.dim(` codeyam editor preview '{"scenarioId":"<id>","dimension":"${dim}"}'`));
|
|
1697
|
-
printDimensionGuidance(dim, dimNames);
|
|
1698
1717
|
checkbox('Show results: `codeyam editor show-results`');
|
|
1718
|
+
console.log(chalk.dim(' The preview automatically switches to the first app-level scenario.'));
|
|
1699
1719
|
console.log();
|
|
1700
1720
|
console.log(chalk.dim('The user should now see their existing page with live screenshots in the preview.'));
|
|
1701
1721
|
migrationStopGate(4, pageName, pageIndex, totalPages);
|
|
@@ -1855,18 +1875,14 @@ function printMigrateStep10(root) {
|
|
|
1855
1875
|
}
|
|
1856
1876
|
const { pageName, pageIndex, totalPages } = pageInfo;
|
|
1857
1877
|
writeMigrationStepState(root, 10, pageName, pageIndex, totalPages);
|
|
1858
|
-
const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
|
|
1859
1878
|
console.log();
|
|
1860
1879
|
console.log(chalk.bold.cyan(`━━━ Migration Step 10: Present — ${pageName} (${pageIndex + 1}/${totalPages}) ━━━`));
|
|
1861
1880
|
console.log();
|
|
1862
1881
|
console.log("Show results and commit this page's migration.");
|
|
1863
1882
|
console.log();
|
|
1864
1883
|
console.log(chalk.bold('Checklist:'));
|
|
1865
|
-
checkbox('Fetch all scenarios: `codeyam editor scenarios`');
|
|
1866
|
-
checkbox('Pick the best scenario to showcase:');
|
|
1867
|
-
console.log(chalk.dim(` codeyam editor preview '{"scenarioId":"<id>","dimension":"${dim}"}'`));
|
|
1868
|
-
printDimensionGuidance(dim, dimNames);
|
|
1869
1884
|
checkbox('Show results: `codeyam editor show-results`');
|
|
1885
|
+
console.log(chalk.dim(' The preview automatically switches to the first app-level scenario.'));
|
|
1870
1886
|
checkbox('Write a 1-2 sentence summary of what was migrated');
|
|
1871
1887
|
console.log();
|
|
1872
1888
|
console.log(chalk.bold('Present a selection menu (AskUserQuestion with these EXACT option labels):'));
|
|
@@ -2339,6 +2355,8 @@ async function handleAnalyzeImports(options = {}) {
|
|
|
2339
2355
|
sha: e.sha,
|
|
2340
2356
|
name: e.name,
|
|
2341
2357
|
filePath: e.filePath || '',
|
|
2358
|
+
isDefaultExport: e.metadata?.notExported === false &&
|
|
2359
|
+
e.metadata?.namedExport === false,
|
|
2342
2360
|
})));
|
|
2343
2361
|
if (backfillResult.updated > 0 && !options.silent) {
|
|
2344
2362
|
console.log(chalk.green(`Linked ${backfillResult.updated} scenario(s) to their entities.`));
|
|
@@ -2644,6 +2662,13 @@ async function handleRegister(jsonArg) {
|
|
|
2644
2662
|
if (data.captureError)
|
|
2645
2663
|
parts.push(chalk.yellow(`captureError="${data.captureError}"`));
|
|
2646
2664
|
console.log(prefix + parts.join(' '));
|
|
2665
|
+
// Warn when application scenario is missing seed data
|
|
2666
|
+
if (data.missingSeedWarning) {
|
|
2667
|
+
console.log(chalk.red.bold('WARNING: No seed data in this application scenario!'));
|
|
2668
|
+
console.log(chalk.yellow(' This scenario has type "application" but no "seed" data was provided.'));
|
|
2669
|
+
console.log(chalk.yellow(' The page will render with an empty database — nothing will be visible.'));
|
|
2670
|
+
console.log(chalk.yellow(' Re-register with "seed":{...} containing the database rows this page needs.'));
|
|
2671
|
+
}
|
|
2647
2672
|
// Surface client errors prominently so they can't be missed
|
|
2648
2673
|
if (data.clientErrors && data.clientErrors.length > 0) {
|
|
2649
2674
|
console.log(chalk.red.bold(`⚠ WARNING: ${data.clientErrors.length} client error${data.clientErrors.length !== 1 ? 's' : ''} detected during capture:`));
|
|
@@ -2651,6 +2676,14 @@ async function handleRegister(jsonArg) {
|
|
|
2651
2676
|
console.log(chalk.red(` → ${err}`));
|
|
2652
2677
|
}
|
|
2653
2678
|
console.log(chalk.yellow(' The screenshot may show an error screen instead of the actual component.'));
|
|
2679
|
+
// Provide actionable debugging hints for HTTP/API errors
|
|
2680
|
+
const hasApiResponseError = data.clientErrors.some((e) => e.includes('API response error:'));
|
|
2681
|
+
const hasHttpError = data.clientErrors.some((e) => e.includes('HTTP error:'));
|
|
2682
|
+
if (hasHttpError || hasApiResponseError) {
|
|
2683
|
+
console.log(chalk.yellow(' To debug: look at the "API response error" lines above — they show the exact API endpoint'));
|
|
2684
|
+
console.log(chalk.yellow(' that failed and what the server returned. Then check the scenario seed data to ensure'));
|
|
2685
|
+
console.log(chalk.yellow(' all IDs referenced in the URL exist in the seed, and that the route is correct.'));
|
|
2686
|
+
}
|
|
2654
2687
|
console.log(chalk.yellow(' Fix the issue and re-register this scenario.'));
|
|
2655
2688
|
}
|
|
2656
2689
|
if (!res.ok) {
|
|
@@ -2676,6 +2709,53 @@ async function handleRegister(jsonArg) {
|
|
|
2676
2709
|
process.exit(1);
|
|
2677
2710
|
}
|
|
2678
2711
|
}
|
|
2712
|
+
// ─── Glossary-add subcommand ──────────────────────────────────────────
|
|
2713
|
+
/**
|
|
2714
|
+
* `codeyam editor glossary-add '{"name":"...", "filePath":"...", "description":"..."}'`
|
|
2715
|
+
*
|
|
2716
|
+
* Safely adds/updates entries in .codeyam/glossary.json via the CLI,
|
|
2717
|
+
* avoiding hand-editing that breaks on unicode characters.
|
|
2718
|
+
*/
|
|
2719
|
+
async function handleGlossaryAdd(jsonArg) {
|
|
2720
|
+
if (!jsonArg) {
|
|
2721
|
+
console.error(chalk.red('Error: JSON argument required.'));
|
|
2722
|
+
console.error(chalk.dim(' Usage: codeyam editor glossary-add \'{"name":"DrinkCard","filePath":"app/components/DrinkCard.tsx","description":"Displays a drink item"}\''));
|
|
2723
|
+
console.error(chalk.dim(' For large payloads: codeyam editor glossary-add @.codeyam/tmp/entry.json'));
|
|
2724
|
+
process.exit(1);
|
|
2725
|
+
}
|
|
2726
|
+
const parsed = parseRegisterArg(jsonArg);
|
|
2727
|
+
if (parsed.error) {
|
|
2728
|
+
console.error(chalk.red(`Error: ${parsed.error}`));
|
|
2729
|
+
process.exit(1);
|
|
2730
|
+
}
|
|
2731
|
+
// Normalize to array
|
|
2732
|
+
const items = Array.isArray(parsed.body) ? parsed.body : [parsed.body];
|
|
2733
|
+
// Read existing glossary
|
|
2734
|
+
const root = getProjectRoot();
|
|
2735
|
+
const glossaryPath = path.join(root, '.codeyam', 'glossary.json');
|
|
2736
|
+
let existing = [];
|
|
2737
|
+
try {
|
|
2738
|
+
const raw = JSON.parse(fs.readFileSync(glossaryPath, 'utf8'));
|
|
2739
|
+
existing = sanitizeGlossaryEntries(raw);
|
|
2740
|
+
}
|
|
2741
|
+
catch {
|
|
2742
|
+
// Glossary doesn't exist yet or can't be parsed — start fresh
|
|
2743
|
+
}
|
|
2744
|
+
// Merge
|
|
2745
|
+
const { validateGlossaryEntry, mergeGlossaryEntries } = await import('../utils/glossaryAdd.js');
|
|
2746
|
+
const result = mergeGlossaryEntries(existing, items);
|
|
2747
|
+
// Report validation errors
|
|
2748
|
+
for (const err of result.errors) {
|
|
2749
|
+
console.error(chalk.red(`Error at index ${err.index}: ${err.message}`));
|
|
2750
|
+
}
|
|
2751
|
+
if (result.added === 0 && result.updated === 0 && result.errors.length > 0) {
|
|
2752
|
+
process.exit(1);
|
|
2753
|
+
}
|
|
2754
|
+
// Write back with utf8 encoding to safely handle unicode
|
|
2755
|
+
fs.mkdirSync(path.dirname(glossaryPath), { recursive: true });
|
|
2756
|
+
fs.writeFileSync(glossaryPath, JSON.stringify(result.entries, null, 2), 'utf8');
|
|
2757
|
+
console.log(`added=${result.added} updated=${result.updated} total=${result.entries.length}`);
|
|
2758
|
+
}
|
|
2679
2759
|
// ─── Dependents subcommand ────────────────────────────────────────────
|
|
2680
2760
|
/**
|
|
2681
2761
|
* `codeyam editor dependents <EntityName>`
|
|
@@ -2907,23 +2987,150 @@ function handleChange(feature) {
|
|
|
2907
2987
|
}
|
|
2908
2988
|
// ─── Audit gate ─────────────────────────────────────────────────────
|
|
2909
2989
|
/**
|
|
2910
|
-
*
|
|
2911
|
-
*
|
|
2912
|
-
* Used as a hard gate before steps 8+.
|
|
2990
|
+
* Fetch the audit result from the server.
|
|
2991
|
+
* Returns null if the server is unreachable.
|
|
2913
2992
|
*/
|
|
2914
|
-
async function
|
|
2993
|
+
async function fetchAuditResult() {
|
|
2915
2994
|
const port = getServerPort();
|
|
2916
2995
|
try {
|
|
2917
2996
|
const res = await fetch(`http://localhost:${port}/api/editor-audit`);
|
|
2918
2997
|
if (!res.ok)
|
|
2919
|
-
return
|
|
2920
|
-
|
|
2921
|
-
return data?.summary?.allPassing === true;
|
|
2998
|
+
return null;
|
|
2999
|
+
return await res.json();
|
|
2922
3000
|
}
|
|
2923
3001
|
catch {
|
|
2924
|
-
|
|
3002
|
+
return null;
|
|
3003
|
+
}
|
|
3004
|
+
}
|
|
3005
|
+
/**
|
|
3006
|
+
* Silently check whether the audit passes. Returns true if allPassing,
|
|
3007
|
+
* false if any issues remain or the server is unreachable.
|
|
3008
|
+
* Used as a hard gate before steps 8+.
|
|
3009
|
+
*
|
|
3010
|
+
* Auto-runs analyze-imports if the only failure is incomplete entities,
|
|
3011
|
+
* then re-checks once.
|
|
3012
|
+
*/
|
|
3013
|
+
async function checkAuditGate() {
|
|
3014
|
+
const data = await fetchAuditResult();
|
|
3015
|
+
if (!data)
|
|
3016
|
+
return true; // Server unreachable — don't block
|
|
3017
|
+
if (data.summary?.allPassing === true)
|
|
2925
3018
|
return true;
|
|
3019
|
+
// If incomplete entities are the only issue, auto-fix with analyze-imports (once)
|
|
3020
|
+
const { isAutoRemediable } = await import('../utils/editorAudit.js');
|
|
3021
|
+
if (isAutoRemediable(data.summary, false)) {
|
|
3022
|
+
try {
|
|
3023
|
+
await handleAnalyzeImports({ silent: true });
|
|
3024
|
+
}
|
|
3025
|
+
catch {
|
|
3026
|
+
return false;
|
|
3027
|
+
}
|
|
3028
|
+
// Re-check after fix
|
|
3029
|
+
const retry = await fetchAuditResult();
|
|
3030
|
+
if (retry?.summary?.allPassing === true)
|
|
3031
|
+
return true;
|
|
3032
|
+
}
|
|
3033
|
+
// Print specific failures so Claude knows what to fix without running audit separately
|
|
3034
|
+
printAuditGateFailures(data);
|
|
3035
|
+
return false;
|
|
3036
|
+
}
|
|
3037
|
+
/**
|
|
3038
|
+
* Print a concise summary of audit failures for the gate block message.
|
|
3039
|
+
* This gives Claude immediate context about what to fix without needing
|
|
3040
|
+
* to run `codeyam editor audit` as a separate step.
|
|
3041
|
+
*/
|
|
3042
|
+
function printAuditGateFailures(data) {
|
|
3043
|
+
const s = data.summary;
|
|
3044
|
+
if (!s)
|
|
3045
|
+
return;
|
|
3046
|
+
const issues = [];
|
|
3047
|
+
if (s.componentsMissing > 0) {
|
|
3048
|
+
issues.push(`${s.componentsMissing} component(s) missing scenarios`);
|
|
3049
|
+
const missing = (data.components || []).filter((c) => c.status === 'missing');
|
|
3050
|
+
for (const c of missing) {
|
|
3051
|
+
issues.push(` → ${c.name}${c.filePath ? ` (${c.filePath})` : ''}`);
|
|
3052
|
+
}
|
|
3053
|
+
}
|
|
3054
|
+
if (s.componentsWithErrors > 0) {
|
|
3055
|
+
issues.push(`${s.componentsWithErrors} component(s) with client errors (browser API or runtime errors in captured scenarios)`);
|
|
3056
|
+
const withErrors = (data.components || []).filter((c) => c.status === 'has_errors');
|
|
3057
|
+
for (const c of withErrors) {
|
|
3058
|
+
issues.push(` → ${c.name}${c.filePath ? ` (${c.filePath})` : ''}`);
|
|
3059
|
+
}
|
|
3060
|
+
}
|
|
3061
|
+
if (s.functionsMissing > 0) {
|
|
3062
|
+
issues.push(`${s.functionsMissing} function(s) missing test files`);
|
|
3063
|
+
const missing = (data.functions || []).filter((f) => f.status === 'missing');
|
|
3064
|
+
for (const f of missing) {
|
|
3065
|
+
issues.push(` → ${f.name}${f.filePath ? ` (${f.filePath})` : ''}`);
|
|
3066
|
+
}
|
|
3067
|
+
}
|
|
3068
|
+
if (s.functionsFailing > 0) {
|
|
3069
|
+
issues.push(`${s.functionsFailing} function(s) with failing tests`);
|
|
3070
|
+
const failing = (data.functions || []).filter((f) => f.status === 'failing');
|
|
3071
|
+
for (const f of failing) {
|
|
3072
|
+
issues.push(` → ${f.name}${f.testFile ? ` (${f.testFile})` : ''}`);
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
if (s.functionsRunnerError > 0) {
|
|
3076
|
+
issues.push(`${s.functionsRunnerError} function(s) with test runner errors (the runner crashed — not a test failure)`);
|
|
3077
|
+
const runnerErrors = (data.functions || []).filter((f) => f.status === 'runner_error');
|
|
3078
|
+
for (const f of runnerErrors) {
|
|
3079
|
+
issues.push(` → ${f.name}${f.testFile ? ` (${f.testFile})` : ''}`);
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
3082
|
+
if (s.functionsNameMismatch > 0) {
|
|
3083
|
+
issues.push(`${s.functionsNameMismatch} function(s) with test name mismatch`);
|
|
3084
|
+
const mismatch = (data.functions || []).filter((f) => f.status === 'name_mismatch');
|
|
3085
|
+
for (const f of mismatch) {
|
|
3086
|
+
issues.push(` → ${f.name}${f.testFile ? ` (${f.testFile})` : ''}`);
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
if (s.missingFromGlossary > 0)
|
|
3090
|
+
issues.push(`${s.missingFromGlossary} file(s) with scenarios not in glossary`);
|
|
3091
|
+
if (s.incompleteEntities > 0) {
|
|
3092
|
+
const preCount = s.preExistingIncompleteEntities || 0;
|
|
3093
|
+
if (preCount > 0 && preCount === s.incompleteEntities) {
|
|
3094
|
+
issues.push(`${s.incompleteEntities} entity/entities need import analysis (pre-existing — not caused by your current changes, but must be fixed before proceeding)`);
|
|
3095
|
+
}
|
|
3096
|
+
else if (preCount > 0) {
|
|
3097
|
+
issues.push(`${s.incompleteEntities} entity/entities need import analysis (${preCount} pre-existing — not from your changes)`);
|
|
3098
|
+
}
|
|
3099
|
+
else {
|
|
3100
|
+
issues.push(`${s.incompleteEntities} entity/entities need import analysis`);
|
|
3101
|
+
}
|
|
2926
3102
|
}
|
|
3103
|
+
if (s.miscategorizedScenarios > 0)
|
|
3104
|
+
issues.push(`${s.miscategorizedScenarios} component(s) using page URLs instead of isolation routes`);
|
|
3105
|
+
if (s.scenariosNeedingRecapture > 0)
|
|
3106
|
+
issues.push(`${s.scenariosNeedingRecapture} scenario(s) need recapture (dependency tree changed)`);
|
|
3107
|
+
if (issues.length > 0) {
|
|
3108
|
+
console.error(chalk.yellow('\nAudit failures:'));
|
|
3109
|
+
for (const issue of issues) {
|
|
3110
|
+
console.error(chalk.yellow(` • ${issue}`));
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
3113
|
+
// Surface client error details inline — these are the most common cause of
|
|
3114
|
+
// Claude looping because the errors require code fixes, not audit re-runs.
|
|
3115
|
+
if (data.components) {
|
|
3116
|
+
const withErrors = data.components.filter((c) => c.status === 'has_errors' && c.clientErrors?.length > 0);
|
|
3117
|
+
if (withErrors.length > 0) {
|
|
3118
|
+
console.error(chalk.yellow('\nClient errors found:'));
|
|
3119
|
+
for (const c of withErrors) {
|
|
3120
|
+
console.error(chalk.red(` ${c.name}:`));
|
|
3121
|
+
for (const err of c.clientErrors.slice(0, 3)) {
|
|
3122
|
+
console.error(chalk.red(` → ${err}`));
|
|
3123
|
+
}
|
|
3124
|
+
if (c.clientErrors.length > 3) {
|
|
3125
|
+
console.error(chalk.dim(` … and ${c.clientErrors.length - 3} more`));
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
3128
|
+
console.error(chalk.yellow('\nFix: Fix the code errors above, then re-capture the affected scenarios.'));
|
|
3129
|
+
console.error(chalk.yellow('If errors reference browser APIs (localStorage, sessionStorage, window, document),'));
|
|
3130
|
+
console.error(chalk.yellow('create a universal mock: codeyam detect-universal-mocks'));
|
|
3131
|
+
}
|
|
3132
|
+
}
|
|
3133
|
+
console.error(chalk.dim('\nRun `codeyam editor audit` for full details.\n'));
|
|
2927
3134
|
}
|
|
2928
3135
|
// ─── Audit subcommand ────────────────────────────────────────────────
|
|
2929
3136
|
/**
|
|
@@ -2934,28 +3141,49 @@ async function checkAuditGate() {
|
|
|
2934
3141
|
* have test files. Exits with code 1 if anything is missing.
|
|
2935
3142
|
*/
|
|
2936
3143
|
async function handleAudit() {
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
let data;
|
|
2940
|
-
try {
|
|
2941
|
-
const res = await fetch(url);
|
|
2942
|
-
if (!res.ok) {
|
|
2943
|
-
console.error(chalk.red(`Error: Audit endpoint returned ${res.status}`));
|
|
2944
|
-
process.exit(1);
|
|
2945
|
-
}
|
|
2946
|
-
data = await res.json();
|
|
2947
|
-
}
|
|
2948
|
-
catch (err) {
|
|
3144
|
+
let data = await fetchAuditResult();
|
|
3145
|
+
if (!data) {
|
|
2949
3146
|
console.error(chalk.red('Error: Could not reach the CodeYam server. Is it running?'));
|
|
2950
|
-
console.error(chalk.dim(` ${err.message}`));
|
|
2951
3147
|
process.exit(1);
|
|
2952
3148
|
}
|
|
3149
|
+
// Auto-fix incomplete entities — but only once.
|
|
3150
|
+
// If analyze-imports runs and entities are STILL incomplete, report clearly
|
|
3151
|
+
// instead of retrying. The analysis may have errors (e.g., stale entity
|
|
3152
|
+
// references from refactoring) that can't be fixed by re-running.
|
|
3153
|
+
const incompleteBeforeFix = data.incompleteEntities || [];
|
|
3154
|
+
let autoRemediationFailed = false;
|
|
3155
|
+
if (incompleteBeforeFix.length > 0) {
|
|
3156
|
+
console.log(chalk.dim(`Running import analysis for ${incompleteBeforeFix.length} incomplete entit${incompleteBeforeFix.length !== 1 ? 'ies' : 'y'}...`));
|
|
3157
|
+
try {
|
|
3158
|
+
await handleAnalyzeImports({ silent: true });
|
|
3159
|
+
}
|
|
3160
|
+
catch {
|
|
3161
|
+
// Fall through — the audit will still report them as incomplete
|
|
3162
|
+
}
|
|
3163
|
+
// Re-fetch audit results after the fix
|
|
3164
|
+
data = await fetchAuditResult();
|
|
3165
|
+
if (!data) {
|
|
3166
|
+
console.error(chalk.red('Error: Could not reach the CodeYam server after analyze-imports.'));
|
|
3167
|
+
process.exit(1);
|
|
3168
|
+
}
|
|
3169
|
+
// If entities are still incomplete after running analyze-imports,
|
|
3170
|
+
// flag it so we can show a clear message instead of looping
|
|
3171
|
+
const incompleteAfterFix = data.incompleteEntities || [];
|
|
3172
|
+
if (incompleteAfterFix.length > 0) {
|
|
3173
|
+
autoRemediationFailed = true;
|
|
3174
|
+
}
|
|
3175
|
+
}
|
|
2953
3176
|
const { components, functions, summary } = data;
|
|
2954
3177
|
console.log();
|
|
2955
3178
|
console.log(chalk.bold.cyan('━━━ Editor Audit ━━━'));
|
|
2956
3179
|
console.log();
|
|
2957
3180
|
// Components
|
|
2958
3181
|
if (components.length > 0) {
|
|
3182
|
+
// Build name frequency map for disambiguation
|
|
3183
|
+
const componentNameCounts = new Map();
|
|
3184
|
+
for (const c of components) {
|
|
3185
|
+
componentNameCounts.set(c.name, (componentNameCounts.get(c.name) || 0) + 1);
|
|
3186
|
+
}
|
|
2959
3187
|
console.log(chalk.bold('Components (scenarios):'));
|
|
2960
3188
|
for (const c of components) {
|
|
2961
3189
|
const icon = c.status === 'ok' ? chalk.green('✓') : chalk.red('✗');
|
|
@@ -2969,7 +3197,11 @@ async function handleAudit() {
|
|
|
2969
3197
|
else {
|
|
2970
3198
|
detail = chalk.red(' — no scenarios registered');
|
|
2971
3199
|
}
|
|
2972
|
-
|
|
3200
|
+
// Show file path for failing components always, for OK only when name is ambiguous
|
|
3201
|
+
const isDuplicate = (componentNameCounts.get(c.name) || 0) > 1;
|
|
3202
|
+
const showPath = c.status !== 'ok' || isDuplicate;
|
|
3203
|
+
const pathSuffix = showPath && c.filePath ? chalk.dim(` (${c.filePath})`) : '';
|
|
3204
|
+
console.log(` ${icon} ${c.name}${pathSuffix}${detail}`);
|
|
2973
3205
|
if (c.clientErrors && c.clientErrors.length > 0) {
|
|
2974
3206
|
for (const err of c.clientErrors.slice(0, 3)) {
|
|
2975
3207
|
console.log(chalk.red(` → ${err}`));
|
|
@@ -2977,6 +3209,14 @@ async function handleAudit() {
|
|
|
2977
3209
|
if (c.clientErrors.length > 3) {
|
|
2978
3210
|
console.log(chalk.dim(` … and ${c.clientErrors.length - 3} more`));
|
|
2979
3211
|
}
|
|
3212
|
+
// Detect browser API errors and provide actionable guidance
|
|
3213
|
+
const browserApiPattern = /\b(localStorage|sessionStorage|window\.|document\.|navigator\.|indexedDB|matchMedia|ResizeObserver|IntersectionObserver|MutationObserver)\b/;
|
|
3214
|
+
const hasBrowserApiErrors = c.clientErrors.some((err) => browserApiPattern.test(err));
|
|
3215
|
+
if (hasBrowserApiErrors) {
|
|
3216
|
+
console.log(chalk.yellow(` ⚠ These errors are caused by browser APIs that don't exist during server-side analysis.`));
|
|
3217
|
+
console.log(chalk.yellow(` Fix: Create a universal mock to stub the missing API. Run: codeyam detect-universal-mocks`));
|
|
3218
|
+
console.log(chalk.yellow(` DO NOT re-run the audit or analyze-imports — the error will persist until a mock is created.`));
|
|
3219
|
+
}
|
|
2980
3220
|
}
|
|
2981
3221
|
}
|
|
2982
3222
|
console.log();
|
|
@@ -2991,6 +3231,14 @@ async function handleAudit() {
|
|
|
2991
3231
|
case 'ok':
|
|
2992
3232
|
detail = chalk.dim(` (${f.testFile})`);
|
|
2993
3233
|
break;
|
|
3234
|
+
case 'runner_error':
|
|
3235
|
+
detail = chalk.red(` — test runner crashed: ${f.testFile}`);
|
|
3236
|
+
if (f.errorMessage) {
|
|
3237
|
+
detail += `\n ${chalk.red(f.errorMessage)}`;
|
|
3238
|
+
detail += `\n ${chalk.yellow('Note: This is NOT a test failure — the test runner itself could not execute.')}`;
|
|
3239
|
+
detail += `\n ${chalk.yellow('Try running the test manually: npx vitest run ' + f.testFile)}`;
|
|
3240
|
+
}
|
|
3241
|
+
break;
|
|
2994
3242
|
case 'failing':
|
|
2995
3243
|
detail = chalk.red(` — tests failing: ${f.testFile}`);
|
|
2996
3244
|
break;
|
|
@@ -3018,6 +3266,69 @@ async function handleAudit() {
|
|
|
3018
3266
|
console.log(chalk.yellow(' Add these to .codeyam/glossary.json and run `codeyam editor analyze-imports`'));
|
|
3019
3267
|
console.log();
|
|
3020
3268
|
}
|
|
3269
|
+
// Incomplete entities (scenarios without analyses)
|
|
3270
|
+
const incompleteEntities = data.incompleteEntities || [];
|
|
3271
|
+
if (incompleteEntities.length > 0) {
|
|
3272
|
+
console.log(chalk.bold('Incomplete entities (need import analysis):'));
|
|
3273
|
+
for (const e of incompleteEntities) {
|
|
3274
|
+
console.log(` ${chalk.red('✗')} ${e.name} — ${e.scenarioCount} scenario${e.scenarioCount !== 1 ? 's' : ''} but entity not analyzed`);
|
|
3275
|
+
}
|
|
3276
|
+
if (autoRemediationFailed) {
|
|
3277
|
+
console.log(chalk.red(' analyze-imports was run automatically but these entities are STILL incomplete.'));
|
|
3278
|
+
console.log(chalk.red(' DO NOT re-run analyze-imports or the audit in a loop — the result will be the same.'));
|
|
3279
|
+
console.log(chalk.yellow(' This means the analysis failed for these entities. Common causes:'));
|
|
3280
|
+
console.log(chalk.yellow(' • Entity code uses browser APIs (localStorage, window, document) — create a universal mock'));
|
|
3281
|
+
console.log(chalk.yellow(' • Source file was renamed/deleted but glossary still references the old path'));
|
|
3282
|
+
console.log(chalk.yellow(' • Entity has import errors that prevent analysis'));
|
|
3283
|
+
console.log(chalk.yellow(' To investigate: run `codeyam editor analyze-imports` (without --silent) to see the full error.'));
|
|
3284
|
+
console.log(chalk.yellow(' To fix browser API issues: run `codeyam detect-universal-mocks`'));
|
|
3285
|
+
}
|
|
3286
|
+
else {
|
|
3287
|
+
console.log(chalk.yellow(' Run `codeyam editor analyze-imports` to analyze these entities'));
|
|
3288
|
+
}
|
|
3289
|
+
console.log();
|
|
3290
|
+
}
|
|
3291
|
+
// Miscategorized scenarios (component scenarios using real page URLs)
|
|
3292
|
+
const miscategorizedScenarios = data.miscategorizedScenarios || [];
|
|
3293
|
+
if (miscategorizedScenarios.length > 0) {
|
|
3294
|
+
console.log(chalk.bold('Miscategorized scenarios (component with page URL):'));
|
|
3295
|
+
for (const m of miscategorizedScenarios) {
|
|
3296
|
+
console.log(` ${chalk.red('✗')} ${m.componentName} at ${chalk.dim(m.url)} — ${m.scenarioNames.length} scenario${m.scenarioNames.length !== 1 ? 's' : ''}`);
|
|
3297
|
+
for (const name of m.scenarioNames) {
|
|
3298
|
+
console.log(chalk.dim(` "${name}"`));
|
|
3299
|
+
}
|
|
3300
|
+
}
|
|
3301
|
+
console.log(chalk.yellow(' Component scenarios must use isolation routes (/isolated-components/...).'));
|
|
3302
|
+
console.log(chalk.yellow(' Either re-register as app scenarios (remove componentName, add pageFilePath)'));
|
|
3303
|
+
console.log(chalk.yellow(' or re-register with an isolation URL.'));
|
|
3304
|
+
console.log();
|
|
3305
|
+
}
|
|
3306
|
+
// Scenarios needing recapture (entity or dependency tree changed)
|
|
3307
|
+
const scenariosNeedingRecapture = data.scenariosNeedingRecapture || [];
|
|
3308
|
+
if (scenariosNeedingRecapture.length > 0) {
|
|
3309
|
+
console.log(chalk.bold('Scenarios needing recapture (dependency tree changed):'));
|
|
3310
|
+
for (const s of scenariosNeedingRecapture) {
|
|
3311
|
+
const reason = s.status.status === 'impacted' && s.status.impactedBy?.length
|
|
3312
|
+
? `impacted by: ${s.status.impactedBy.map((d) => `${d.name} [${d.changeType}]`).join(', ')}`
|
|
3313
|
+
: `${s.status.status}`;
|
|
3314
|
+
console.log(` ${chalk.red('✗')} ${s.scenarioName} ${chalk.dim(`(${s.entityName} — ${reason})`)}`);
|
|
3315
|
+
}
|
|
3316
|
+
console.log(chalk.yellow(' Re-register these scenarios to capture updated screenshots.'));
|
|
3317
|
+
console.log();
|
|
3318
|
+
}
|
|
3319
|
+
// Duplicate glossary names (warning, not a failure)
|
|
3320
|
+
const duplicateNames = data.duplicateNames || [];
|
|
3321
|
+
if (duplicateNames.length > 0) {
|
|
3322
|
+
console.log(chalk.bold('Duplicate names in glossary (confusing for audit):'));
|
|
3323
|
+
for (const dn of duplicateNames) {
|
|
3324
|
+
console.log(` ${chalk.yellow('⚠')} "${dn.name}" appears ${dn.filePaths.length} times:`);
|
|
3325
|
+
for (const fp of dn.filePaths) {
|
|
3326
|
+
console.log(` ${fp}`);
|
|
3327
|
+
}
|
|
3328
|
+
}
|
|
3329
|
+
console.log(chalk.yellow(' Fix: remove duplicate entries or rename them to be unique in .codeyam/glossary.json'));
|
|
3330
|
+
console.log();
|
|
3331
|
+
}
|
|
3021
3332
|
// Summary
|
|
3022
3333
|
const allOk = summary.allPassing;
|
|
3023
3334
|
if (allOk) {
|
|
@@ -3038,12 +3349,24 @@ async function handleAudit() {
|
|
|
3038
3349
|
if (summary.functionsFailing > 0) {
|
|
3039
3350
|
parts.push(`${summary.functionsFailing} function${summary.functionsFailing !== 1 ? 's' : ''} with failing tests`);
|
|
3040
3351
|
}
|
|
3352
|
+
if (summary.functionsRunnerError > 0) {
|
|
3353
|
+
parts.push(`${summary.functionsRunnerError} function${summary.functionsRunnerError !== 1 ? 's' : ''} with test runner errors (not test failures — see details above)`);
|
|
3354
|
+
}
|
|
3041
3355
|
if (summary.functionsNameMismatch > 0) {
|
|
3042
3356
|
parts.push(`${summary.functionsNameMismatch} function${summary.functionsNameMismatch !== 1 ? 's' : ''} with test name mismatch (missing top-level describe)`);
|
|
3043
3357
|
}
|
|
3044
3358
|
if (summary.missingFromGlossary > 0) {
|
|
3045
3359
|
parts.push(`${summary.missingFromGlossary} file${summary.missingFromGlossary !== 1 ? 's' : ''} with scenarios not in glossary`);
|
|
3046
3360
|
}
|
|
3361
|
+
if (summary.incompleteEntities > 0) {
|
|
3362
|
+
parts.push(`${summary.incompleteEntities} entit${summary.incompleteEntities !== 1 ? 'ies' : 'y'} need import analysis`);
|
|
3363
|
+
}
|
|
3364
|
+
if (summary.miscategorizedScenarios > 0) {
|
|
3365
|
+
parts.push(`${summary.miscategorizedScenarios} component${summary.miscategorizedScenarios !== 1 ? 's' : ''} using page URLs instead of isolation routes`);
|
|
3366
|
+
}
|
|
3367
|
+
if (summary.scenariosNeedingRecapture > 0) {
|
|
3368
|
+
parts.push(`${summary.scenariosNeedingRecapture} scenario${summary.scenariosNeedingRecapture !== 1 ? 's' : ''} need recapture`);
|
|
3369
|
+
}
|
|
3047
3370
|
console.log(chalk.red.bold('Audit failed: ') + parts.join(', '));
|
|
3048
3371
|
}
|
|
3049
3372
|
console.log();
|
|
@@ -3178,6 +3501,8 @@ async function handleScenarioCoverage() {
|
|
|
3178
3501
|
sha: e.sha,
|
|
3179
3502
|
name: e.name,
|
|
3180
3503
|
filePath: e.filePath || '',
|
|
3504
|
+
isDefaultExport: e.metadata?.notExported === false &&
|
|
3505
|
+
e.metadata?.namedExport === false,
|
|
3181
3506
|
})));
|
|
3182
3507
|
}
|
|
3183
3508
|
}
|
|
@@ -3723,7 +4048,9 @@ const editorCommand = {
|
|
|
3723
4048
|
describe: 'Debug: output directory for the bundle',
|
|
3724
4049
|
});
|
|
3725
4050
|
}
|
|
3726
|
-
|
|
4051
|
+
// Allow extra positional args for subcommands like `isolate A B C`
|
|
4052
|
+
// without yargs rejecting them as unknown.
|
|
4053
|
+
return builder.strict(false);
|
|
3727
4054
|
},
|
|
3728
4055
|
handler: async (argv) => {
|
|
3729
4056
|
const root = getProjectRoot();
|
|
@@ -3762,6 +4089,11 @@ const editorCommand = {
|
|
|
3762
4089
|
await handleRegister(argv.json || '');
|
|
3763
4090
|
return;
|
|
3764
4091
|
}
|
|
4092
|
+
// Subcommand: codeyam editor glossary-add '{"name":"...", ...}'
|
|
4093
|
+
if (argv.step === 'glossary-add') {
|
|
4094
|
+
await handleGlossaryAdd(argv.json || '');
|
|
4095
|
+
return;
|
|
4096
|
+
}
|
|
3765
4097
|
// Subcommand: codeyam editor analyze-imports
|
|
3766
4098
|
if (argv.step === 'analyze-imports') {
|
|
3767
4099
|
await handleAnalyzeImports();
|
|
@@ -3808,13 +4140,10 @@ const editorCommand = {
|
|
|
3808
4140
|
return;
|
|
3809
4141
|
}
|
|
3810
4142
|
// Subcommand: codeyam editor isolate "StarRating CategoryBadge DrinkCard"
|
|
4143
|
+
// Also supports: codeyam editor isolate StarRating CategoryBadge DrinkCard
|
|
3811
4144
|
if (argv.step === 'isolate') {
|
|
3812
|
-
const
|
|
3813
|
-
|
|
3814
|
-
names.push(...argv.json.split(/[\s,]+/).filter(Boolean));
|
|
3815
|
-
// Collect any extra positional args (yargs puts them in argv._)
|
|
3816
|
-
const extras = argv._.filter((a) => typeof a === 'string' && a !== 'editor');
|
|
3817
|
-
names.push(...extras);
|
|
4145
|
+
const { parseIsolateArgs } = await import('./editorIsolateArgs.js');
|
|
4146
|
+
const names = parseIsolateArgs(argv.json, argv._);
|
|
3818
4147
|
handleIsolate(names);
|
|
3819
4148
|
return;
|
|
3820
4149
|
}
|
|
@@ -4002,6 +4331,41 @@ const editorCommand = {
|
|
|
4002
4331
|
catch {
|
|
4003
4332
|
// Non-fatal — migration failure shouldn't block editor startup
|
|
4004
4333
|
}
|
|
4334
|
+
// Auto-seed on fresh clone: if no scenario has ever been activated
|
|
4335
|
+
// (active-scenario.json doesn't exist), seed the application database
|
|
4336
|
+
// with the first application scenario that has seed data.
|
|
4337
|
+
const activeScenarioPath = path.join(projectRoot, '.codeyam', 'active-scenario.json');
|
|
4338
|
+
if (!fs.existsSync(activeScenarioPath)) {
|
|
4339
|
+
try {
|
|
4340
|
+
const { getDatabase: getDb } = await import('../../../packages/database/index.js');
|
|
4341
|
+
const seedDb = getDb();
|
|
4342
|
+
const appScenario = await seedDb
|
|
4343
|
+
.selectFrom('editor_scenarios')
|
|
4344
|
+
.select(['id', 'name', 'type'])
|
|
4345
|
+
.where('project_id', '=', project.id)
|
|
4346
|
+
.where('type', '=', 'application')
|
|
4347
|
+
.orderBy('created_at', 'asc')
|
|
4348
|
+
.executeTakeFirst();
|
|
4349
|
+
if (appScenario) {
|
|
4350
|
+
const seedFile = path.join(projectRoot, '.codeyam', 'editor-scenarios', `${appScenario.id}.seed.json`);
|
|
4351
|
+
if (fs.existsSync(seedFile)) {
|
|
4352
|
+
const { switchActiveScenario } = await import('../utils/editorScenarioSwitch.js');
|
|
4353
|
+
const seedResult = await switchActiveScenario({
|
|
4354
|
+
scenarioId: appScenario.id,
|
|
4355
|
+
scenarioName: appScenario.name || undefined,
|
|
4356
|
+
scenarioType: appScenario.type || undefined,
|
|
4357
|
+
projectRoot,
|
|
4358
|
+
});
|
|
4359
|
+
if (seedResult.seedResult?.success) {
|
|
4360
|
+
console.log(chalk.green(` Auto-seeded database with scenario: ${appScenario.name || appScenario.id}`));
|
|
4361
|
+
}
|
|
4362
|
+
}
|
|
4363
|
+
}
|
|
4364
|
+
}
|
|
4365
|
+
catch {
|
|
4366
|
+
// Non-fatal — auto-seed is best-effort
|
|
4367
|
+
}
|
|
4368
|
+
}
|
|
4005
4369
|
// `codeyam editor` (no step) always implies editor mode.
|
|
4006
4370
|
// The empty-folder heuristic is no longer needed here — running this
|
|
4007
4371
|
// command IS the signal. We still detect empty folders so that
|
|
@@ -4146,6 +4510,48 @@ const editorCommand = {
|
|
|
4146
4510
|
}
|
|
4147
4511
|
}
|
|
4148
4512
|
}
|
|
4513
|
+
// Backfill page_file_path for application scenarios that have a URL but
|
|
4514
|
+
// no page_file_path. This resolves the file from the URL using Next.js
|
|
4515
|
+
// routing conventions, enabling syncScenarioEntityShas to link them to
|
|
4516
|
+
// entities. Covers fresh clones where JSON files lack pageFilePath.
|
|
4517
|
+
try {
|
|
4518
|
+
const { getDatabase: getDb } = await import('../../../packages/database/index.js');
|
|
4519
|
+
const pfpDb = getDb();
|
|
4520
|
+
const unresolved = await pfpDb
|
|
4521
|
+
.selectFrom('editor_scenarios')
|
|
4522
|
+
.select(['id', 'url'])
|
|
4523
|
+
.where('project_id', '=', project.id)
|
|
4524
|
+
.where('component_name', 'is', null)
|
|
4525
|
+
.where('page_file_path', 'is', null)
|
|
4526
|
+
.where('url', 'is not', null)
|
|
4527
|
+
.execute();
|
|
4528
|
+
if (unresolved.length > 0) {
|
|
4529
|
+
const { scanPageFilePaths: scanPfp } = await import('../utils/entityChangeStatus.server.js');
|
|
4530
|
+
const { matchUrlToPageFile } = await import('../utils/routePatternMatching');
|
|
4531
|
+
const { allFiles: pfpFiles } = scanPfp(projectRoot);
|
|
4532
|
+
let pfpResolved = 0;
|
|
4533
|
+
for (const row of unresolved) {
|
|
4534
|
+
const r = row;
|
|
4535
|
+
if (!r.url)
|
|
4536
|
+
continue;
|
|
4537
|
+
const matched = matchUrlToPageFile(r.url, pfpFiles);
|
|
4538
|
+
if (matched) {
|
|
4539
|
+
await pfpDb
|
|
4540
|
+
.updateTable('editor_scenarios')
|
|
4541
|
+
.set({ page_file_path: matched })
|
|
4542
|
+
.where('id', '=', r.id)
|
|
4543
|
+
.execute();
|
|
4544
|
+
pfpResolved++;
|
|
4545
|
+
}
|
|
4546
|
+
}
|
|
4547
|
+
if (pfpResolved > 0) {
|
|
4548
|
+
console.log(chalk.green(` Resolved page_file_path for ${pfpResolved} scenario(s) from URL`));
|
|
4549
|
+
}
|
|
4550
|
+
}
|
|
4551
|
+
}
|
|
4552
|
+
catch {
|
|
4553
|
+
/* Non-fatal — page_file_path backfill from URL */
|
|
4554
|
+
}
|
|
4149
4555
|
// Backfill entity_sha on scenarios that were synced before entities existed.
|
|
4150
4556
|
// This runs independently of analyze-imports so fresh clones and file-synced
|
|
4151
4557
|
// scenarios get linked to their entities even when auto-analyze doesn't trigger.
|
|
@@ -4158,6 +4564,8 @@ const editorCommand = {
|
|
|
4158
4564
|
sha: e.sha,
|
|
4159
4565
|
name: e.name,
|
|
4160
4566
|
filePath: e.filePath || '',
|
|
4567
|
+
isDefaultExport: e.metadata?.notExported === false &&
|
|
4568
|
+
e.metadata?.namedExport === false,
|
|
4161
4569
|
})));
|
|
4162
4570
|
if (result.updated > 0) {
|
|
4163
4571
|
console.log(chalk.green(` Linked ${result.updated} scenario(s) to their entities`));
|
|
@@ -4252,8 +4660,9 @@ const editorCommand = {
|
|
|
4252
4660
|
if (step >= 8) {
|
|
4253
4661
|
const auditOk = await checkAuditGate();
|
|
4254
4662
|
if (!auditOk) {
|
|
4255
|
-
|
|
4256
|
-
console.error(chalk.
|
|
4663
|
+
// checkAuditGate() already printed specific failure details above
|
|
4664
|
+
console.error(chalk.red.bold('BLOCKED: The audit has not passed. Fix the issues listed above before proceeding.'));
|
|
4665
|
+
console.error(chalk.yellow('DO NOT re-run the audit in a loop — fix the underlying issues first.'));
|
|
4257
4666
|
process.exit(1);
|
|
4258
4667
|
}
|
|
4259
4668
|
}
|