@codeyam/codeyam-cli 0.1.21 → 0.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/analyzer-template/.build-info.json +7 -7
  2. package/analyzer-template/log.txt +3 -3
  3. package/analyzer-template/packages/ai/src/lib/dataStructure/ScopeDataStructure.ts +31 -8
  4. package/analyzer-template/packages/ai/src/lib/dataStructure/equivalencyManagers/ParentScopeManager.ts +10 -3
  5. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.ts +16 -6
  6. package/analyzer-template/packages/analyze/index.ts +4 -1
  7. package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.ts +28 -2
  8. package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities.ts +5 -36
  9. package/analyzer-template/packages/analyze/src/lib/files/analyze/trackEntityCircularDependencies.ts +21 -0
  10. package/analyzer-template/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.ts +82 -10
  11. package/analyzer-template/packages/analyze/src/lib/files/analyzeNextRoute.ts +8 -3
  12. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateDataStructure.ts +235 -58
  13. package/analyzer-template/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.ts +170 -26
  14. package/codeyam-cli/src/commands/__tests__/editor.auditNoAutoAnalysis.test.js +63 -0
  15. package/codeyam-cli/src/commands/__tests__/editor.auditNoAutoAnalysis.test.js.map +1 -0
  16. package/codeyam-cli/src/commands/editor.js +412 -78
  17. package/codeyam-cli/src/commands/editor.js.map +1 -1
  18. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +354 -1
  19. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
  20. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js +11 -3
  21. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js.map +1 -1
  22. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js +33 -1
  23. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js.map +1 -1
  24. package/codeyam-cli/src/utils/__tests__/manualEntityAnalysis.test.js +302 -0
  25. package/codeyam-cli/src/utils/__tests__/manualEntityAnalysis.test.js.map +1 -0
  26. package/codeyam-cli/src/utils/__tests__/testRunner.test.js +217 -0
  27. package/codeyam-cli/src/utils/__tests__/testRunner.test.js.map +1 -0
  28. package/codeyam-cli/src/utils/analysisRunner.js +28 -1
  29. package/codeyam-cli/src/utils/analysisRunner.js.map +1 -1
  30. package/codeyam-cli/src/utils/analyzer.js +4 -2
  31. package/codeyam-cli/src/utils/analyzer.js.map +1 -1
  32. package/codeyam-cli/src/utils/editorAudit.js +98 -3
  33. package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
  34. package/codeyam-cli/src/utils/editorPreview.js +5 -3
  35. package/codeyam-cli/src/utils/editorPreview.js.map +1 -1
  36. package/codeyam-cli/src/utils/entityChangeStatus.server.js +16 -0
  37. package/codeyam-cli/src/utils/entityChangeStatus.server.js.map +1 -1
  38. package/codeyam-cli/src/utils/manualEntityAnalysis.js +196 -0
  39. package/codeyam-cli/src/utils/manualEntityAnalysis.js.map +1 -0
  40. package/codeyam-cli/src/utils/queue/job.js +20 -2
  41. package/codeyam-cli/src/utils/queue/job.js.map +1 -1
  42. package/codeyam-cli/src/utils/testRunner.js +199 -1
  43. package/codeyam-cli/src/utils/testRunner.js.map +1 -1
  44. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js +35 -0
  45. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js.map +1 -1
  46. package/codeyam-cli/src/webserver/build/client/assets/MiniClaudeChat-CQENLSrF.js +36 -0
  47. package/codeyam-cli/src/webserver/build/client/assets/cy-logo-cli-Coe5NhbS.js +1 -0
  48. package/codeyam-cli/src/webserver/build/client/assets/{cy-logo-cli-CJzc4vOH.svg → cy-logo-cli-DoA97ML3.svg} +2 -2
  49. package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-aIHKLB-m.js +96 -0
  50. package/codeyam-cli/src/webserver/build/client/assets/{editorPreview-NTuLi4Xg.js → editorPreview-CluPkvXJ.js} +6 -6
  51. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-Blfy9UlN.js → entity._sha._-ByHz6rAQ.js} +13 -12
  52. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.dev-BA5L8bU-.js → entity._sha.scenarios._scenarioId.dev-CmLO432x.js} +1 -1
  53. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.fullscreen-D4dmRgvO.js → entity._sha.scenarios._scenarioId.fullscreen-Bz9sCUF_.js} +1 -1
  54. package/codeyam-cli/src/webserver/build/client/assets/globals-oyPmV37k.css +1 -0
  55. package/codeyam-cli/src/webserver/build/client/assets/{manifest-5025e428.js → manifest-bcbb3d49.js} +1 -1
  56. package/codeyam-cli/src/webserver/build/client/assets/{root-BCx1S8Z3.js → root-D2_tktnk.js} +6 -6
  57. package/codeyam-cli/src/webserver/build/server/assets/analysisRunner-DjF-soOH.js +16 -0
  58. package/codeyam-cli/src/webserver/build/server/assets/{index-C91yWWCI.js → index-nAvHGWbz.js} +1 -1
  59. package/codeyam-cli/src/webserver/build/server/assets/{init-Dkas-RUS.js → init-XhpIt-OT.js} +1 -1
  60. package/codeyam-cli/src/webserver/build/server/assets/server-build-DVwiibFu.js +644 -0
  61. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  62. package/codeyam-cli/src/webserver/build-info.json +5 -5
  63. package/codeyam-cli/src/webserver/idleDetector.js +15 -0
  64. package/codeyam-cli/src/webserver/idleDetector.js.map +1 -1
  65. package/codeyam-cli/src/webserver/terminalServer.js +8 -2
  66. package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
  67. package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +2 -2
  68. package/package.json +1 -1
  69. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js +23 -9
  70. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js.map +1 -1
  71. package/packages/ai/src/lib/dataStructure/equivalencyManagers/ParentScopeManager.js +9 -2
  72. package/packages/ai/src/lib/dataStructure/equivalencyManagers/ParentScopeManager.js.map +1 -1
  73. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js +14 -4
  74. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js.map +1 -1
  75. package/packages/analyze/index.js +1 -1
  76. package/packages/analyze/index.js.map +1 -1
  77. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js +16 -2
  78. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js.map +1 -1
  79. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js +6 -26
  80. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js.map +1 -1
  81. package/packages/analyze/src/lib/files/analyze/trackEntityCircularDependencies.js +14 -0
  82. package/packages/analyze/src/lib/files/analyze/trackEntityCircularDependencies.js.map +1 -1
  83. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js +44 -11
  84. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js.map +1 -1
  85. package/packages/analyze/src/lib/files/analyzeNextRoute.js +5 -1
  86. package/packages/analyze/src/lib/files/analyzeNextRoute.js.map +1 -1
  87. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js +116 -28
  88. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js.map +1 -1
  89. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js +139 -24
  90. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js.map +1 -1
  91. package/codeyam-cli/src/webserver/build/client/assets/cy-logo-cli-DODLxLcw.js +0 -1
  92. package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-Dx-h1rJK.js +0 -130
  93. package/codeyam-cli/src/webserver/build/client/assets/globals-BrPXT1iR.css +0 -1
  94. package/codeyam-cli/src/webserver/build/server/assets/analysisRunner-C1kjC9UJ.js +0 -13
  95. package/codeyam-cli/src/webserver/build/server/assets/server-build-pulXLTrG.js +0 -640
@@ -357,7 +357,7 @@ function printExtractionPlanInstructions() {
357
357
  */
358
358
  function printComponentCaptureInstructions() {
359
359
  checkbox('Create isolation route dirs: `codeyam editor isolate ComponentA ComponentB ...`');
360
- console.log(chalk.dim(' This creates app/codeyam-isolate/layout.tsx (with production notFound() guard) and'));
360
+ console.log(chalk.dim(' This creates app/isolated-components/layout.tsx (with production notFound() guard) and'));
361
361
  console.log(chalk.dim(' a directory per component. List ALL components that need isolation routes.'));
362
362
  checkbox('For each visual component:');
363
363
  console.log(chalk.dim(' 1. Read the source AND find where it is used in the app to understand:'));
@@ -369,8 +369,8 @@ function printComponentCaptureInstructions() {
369
369
  console.log(chalk.dim(' — Different visual states: loading, error, disabled, selected, hover'));
370
370
  console.log(chalk.dim(' — Boundary values: single item vs many, min/max ratings, very long names'));
371
371
  console.log(chalk.dim(' 3. Create ONE isolation route per component with a scenarios map and ?s= query param:'));
372
- console.log(chalk.dim(' Remix: app/routes/codeyam-isolate.ComponentName.tsx → /codeyam-isolate/ComponentName'));
373
- console.log(chalk.dim(' Next.js: app/codeyam-isolate/ComponentName/page.tsx → /codeyam-isolate/ComponentName'));
372
+ console.log(chalk.dim(' Remix: app/routes/isolated-components.ComponentName.tsx → /isolated-components/ComponentName'));
373
+ console.log(chalk.dim(' Next.js: app/isolated-components/ComponentName/page.tsx → /isolated-components/ComponentName'));
374
374
  console.log(chalk.dim(' The route defines a `scenarios` object mapping scenario names to props,'));
375
375
  console.log(chalk.dim(' reads `?s=ScenarioName` from the URL, and renders the component with those props.'));
376
376
  console.log(chalk.dim(' Wrap the component in a capture container with id="codeyam-capture":'));
@@ -383,9 +383,9 @@ function printComponentCaptureInstructions() {
383
383
  console.log(chalk.dim(' 4. Wait 2 seconds for HMR, then register + capture each scenario:'));
384
384
  console.log(chalk.dim(` codeyam editor register '{"name":"ComponentName - Scenario",`));
385
385
  console.log(chalk.dim(` "componentName":"ComponentName","componentPath":"path/to/file.tsx",`));
386
- console.log(chalk.dim(` "url":"/codeyam-isolate/ComponentName?s=Scenario",`));
386
+ console.log(chalk.dim(` "url":"/isolated-components/ComponentName?s=Scenario",`));
387
387
  console.log(chalk.dim(` "mockData":{"routes":{"/api/...":{"body":[...]}}}}'`));
388
- console.log(chalk.yellow(' IMPORTANT: url MUST be an isolation route (/codeyam-isolate/... or /isolated-components/...).'));
388
+ console.log(chalk.yellow(' IMPORTANT: url MUST be an isolation route (/isolated-components/...).'));
389
389
  console.log(chalk.yellow(' Do NOT use real page URLs (like /library) for component scenarios.'));
390
390
  console.log(chalk.dim(' mockData.routes provides data for API calls the component makes internally'));
391
391
  console.log(chalk.dim(' (omit mockData if the component has no internal API calls)'));
@@ -395,7 +395,7 @@ function printComponentCaptureInstructions() {
395
395
  console.log(chalk.dim(' Isolation routes are committed to git — the layout guard blocks them in production'));
396
396
  console.log();
397
397
  console.log(chalk.bold(' Bulk registration: write all component scenarios to a temp file as an array:'));
398
- console.log(chalk.dim(' [{"name":"Comp - Default","componentName":"Comp","componentPath":"...","url":"/codeyam-isolate/Comp?s=Default"}, ...]'));
398
+ console.log(chalk.dim(' [{"name":"Comp - Default","componentName":"Comp","componentPath":"...","url":"/isolated-components/Comp?s=Default"}, ...]'));
399
399
  console.log(chalk.dim(' codeyam editor register @.codeyam/tmp/scenarios.json'));
400
400
  }
401
401
  /**
@@ -448,7 +448,9 @@ function stopGate(current, opts) {
448
448
  // Skip step 1 (no feature name yet — task starts at step 2)
449
449
  if (current >= 2) {
450
450
  console.log(chalk.bold.cyan('━━━ TASK ━━━'));
451
- console.log(chalk.cyan('Mark your current task (if any) as complete.'));
451
+ console.log(chalk.cyan('Before creating the task below, delete ALL existing tasks:'));
452
+ console.log(chalk.cyan(' 1. Run TaskList to find all tasks'));
453
+ console.log(chalk.cyan(' 2. Run TaskUpdate with status "deleted" for EACH task returned'));
452
454
  console.log(chalk.cyan('Then create a new task with this EXACT title (copy verbatim):'));
453
455
  console.log();
454
456
  let taskTitle;
@@ -1361,6 +1363,9 @@ function printStep9(root, feature) {
1361
1363
  console.log(chalk.dim(' If the audit reports issues (including pre-existing ones), fix ALL of them — do not skip.'));
1362
1364
  console.log(chalk.dim(' Re-run `codeyam editor audit` until all checks pass. Do NOT run `codeyam editor analyze-imports`'));
1363
1365
  console.log(chalk.dim(' — the audit runs targeted analysis automatically and is much faster.'));
1366
+ console.log(chalk.dim(' If the audit reports "MANUAL ANALYSIS REQUIRED" for any entities,'));
1367
+ console.log(chalk.dim(' follow the manual analysis steps in the audit output.'));
1368
+ console.log(chalk.dim(' Do NOT re-run analyze-imports for those entities — it will fail again.'));
1364
1369
  console.log();
1365
1370
  stopGate(9);
1366
1371
  }
@@ -1962,7 +1967,7 @@ function printMigrateStep8(root) {
1962
1967
  console.log(chalk.bold.red('Re-register App Scenarios (REQUIRED):'));
1963
1968
  console.log(chalk.yellow(` You MUST re-register application scenarios for "${page.route}" to get fresh screenshots.`));
1964
1969
  checkbox('Fetch existing scenarios: `codeyam editor scenarios`');
1965
- checkbox('Find all scenarios that use the real page route (not /codeyam-isolate/ paths)');
1970
+ checkbox('Find all scenarios that use the real page route (not /isolated-components/ paths)');
1966
1971
  checkbox('Re-register each one with the SAME name to update its screenshot');
1967
1972
  console.log(chalk.dim(` Example: codeyam editor register '{"name":"${pageName} - Full Data","type":"application","url":"${page.route}",...}'`));
1968
1973
  checkbox('After each registration, check the response for `clientErrors`');
@@ -2444,6 +2449,84 @@ async function handleAnalyzeImports(options = {}) {
2444
2449
  }
2445
2450
  progress.succeed('Analysis complete');
2446
2451
  }
2452
+ // Surface analysis errors if any occurred
2453
+ const errorReportPath = path.join(root, '.codeyam', 'analysis-errors.txt');
2454
+ if (fs.existsSync(errorReportPath)) {
2455
+ if (!options.silent) {
2456
+ console.log(chalk.yellow('\nSome entity analyses failed. Full details in .codeyam/analysis-errors.txt'));
2457
+ console.log(chalk.yellow('Send this file to the CodeYam team for diagnosis.'));
2458
+ console.log(chalk.dim('Affected entities will be missing from the import graph.'));
2459
+ }
2460
+ // Write structured analysis-failures.json for the audit to detect.
2461
+ // Parse the error report to find which entities failed, and cross-reference
2462
+ // with the files we tried to analyze.
2463
+ try {
2464
+ const { readAnalysisFailures, writeAnalysisFailures } = await import('../utils/manualEntityAnalysis.js');
2465
+ const errorContent = fs.readFileSync(errorReportPath, 'utf8');
2466
+ const existingFailures = readAnalysisFailures(root);
2467
+ // Parse error messages to identify failed file paths.
2468
+ // Error format: "Error on step <StepName> for entity <Name> (<filePath>)"
2469
+ // or more generic "CodeYam Error: <message>"
2470
+ const entityErrorRegex = /(?:Error on step \w+ for entity (\w+))|(?:entity[: ]+["']?(\w+)["']?)/gi;
2471
+ const failedEntityNames = new Set();
2472
+ let match;
2473
+ while ((match = entityErrorRegex.exec(errorContent)) !== null) {
2474
+ const name = match[1] || match[2];
2475
+ if (name)
2476
+ failedEntityNames.add(name);
2477
+ }
2478
+ // Map failed entity names back to file paths from targetFilePaths
2479
+ const glossaryPath = path.join(root, '.codeyam', 'glossary.json');
2480
+ let glossary = [];
2481
+ try {
2482
+ glossary = sanitizeGlossaryEntries(JSON.parse(fs.readFileSync(glossaryPath, 'utf8')));
2483
+ }
2484
+ catch {
2485
+ // Non-fatal
2486
+ }
2487
+ // Also: any targetFilePaths that didn't produce entities are failures
2488
+ const now = new Date().toISOString();
2489
+ const updatedFailures = { ...existingFailures };
2490
+ if (failedEntityNames.size > 0) {
2491
+ for (const name of failedEntityNames) {
2492
+ const entry = glossary.find((e) => e.name === name);
2493
+ if (entry) {
2494
+ updatedFailures[entry.filePath] = {
2495
+ entityName: name,
2496
+ error: `Automated analysis failed — see .codeyam/analysis-errors.txt`,
2497
+ failedAt: now,
2498
+ };
2499
+ }
2500
+ }
2501
+ }
2502
+ else if (targetFilePaths.length > 0) {
2503
+ // Couldn't parse specific entity names — mark all target files
2504
+ // that we attempted to analyze as potentially failed
2505
+ for (const fp of targetFilePaths) {
2506
+ const entry = glossary.find((e) => e.filePath === fp);
2507
+ updatedFailures[fp] = {
2508
+ entityName: entry?.name || path.basename(fp, path.extname(fp)),
2509
+ error: `Automated analysis failed — see .codeyam/analysis-errors.txt`,
2510
+ failedAt: now,
2511
+ };
2512
+ }
2513
+ }
2514
+ writeAnalysisFailures(root, updatedFailures);
2515
+ }
2516
+ catch {
2517
+ // Non-fatal — failure tracking is best-effort
2518
+ }
2519
+ }
2520
+ else {
2521
+ // No errors — clear any stale failure tracking
2522
+ try {
2523
+ const { writeAnalysisFailures } = await import('../utils/manualEntityAnalysis.js');
2524
+ writeAnalysisFailures(root, {});
2525
+ }
2526
+ catch {
2527
+ // Non-fatal
2528
+ }
2529
+ }
2447
2530
  // Load entities WITH metadata from database
2448
2531
  progress.start('Loading entity metadata...');
2449
2532
  await initializeEnvironment();
@@ -2905,7 +2988,7 @@ async function handleRegister(jsonArg) {
2905
2988
  }
2906
2989
  // Detect duplicate screenshots in the batch
2907
2990
  if (isBatch && screenshotEntries.length > 1) {
2908
- const { computeScreenshotHash, findDuplicateScreenshots } = await import('../utils/screenshotHash');
2991
+ const { computeScreenshotHash, findDuplicateScreenshots } = await import('../utils/screenshotHash.js');
2909
2992
  const hashEntries = screenshotEntries
2910
2993
  .map((e) => {
2911
2994
  const hash = computeScreenshotHash(e.screenshotAbsPath);
@@ -3235,10 +3318,11 @@ function handleChange(feature) {
3235
3318
  * Fetch the audit result from the server.
3236
3319
  * Returns null if the server is unreachable.
3237
3320
  */
3238
- async function fetchAuditResult() {
3321
+ async function fetchAuditResult(options) {
3239
3322
  const port = getServerPort();
3323
+ const params = options?.skipTests ? '?skipTests=true' : '';
3240
3324
  try {
3241
- const res = await fetch(`http://localhost:${port}/api/editor-audit`);
3325
+ const res = await fetch(`http://localhost:${port}/api/editor-audit${params}`);
3242
3326
  if (!res.ok)
3243
3327
  return null;
3244
3328
  return await res.json();
@@ -3287,8 +3371,8 @@ async function checkAuditGate() {
3287
3371
  catch {
3288
3372
  // Fall through
3289
3373
  }
3290
- // Re-check after backfill
3291
- const retry = await fetchAuditResult();
3374
+ // Re-check after backfill — skip re-running tests (backfill is DB-only)
3375
+ const retry = await fetchAuditResult({ skipTests: true });
3292
3376
  if (retry?.summary?.allPassing === true)
3293
3377
  return true;
3294
3378
  }
@@ -3349,6 +3433,10 @@ function printAuditGateFailures(data) {
3349
3433
  const runnerErrors = (data.functions || []).filter((f) => f.status === 'runner_error');
3350
3434
  for (const f of runnerErrors) {
3351
3435
  issues.push(` → ${f.name}${f.testFile ? ` (${f.testFile})` : ''}`);
3436
+ if (f.hint)
3437
+ issues.push(` ${f.hint}`);
3438
+ else if (f.errorMessage)
3439
+ issues.push(` Error: ${f.errorMessage}`);
3352
3440
  }
3353
3441
  }
3354
3442
  if (s.functionsNameMismatch > 0) {
@@ -3356,13 +3444,36 @@ function printAuditGateFailures(data) {
3356
3444
  const mismatch = (data.functions || []).filter((f) => f.status === 'name_mismatch');
3357
3445
  for (const f of mismatch) {
3358
3446
  issues.push(` → ${f.name}${f.testFile ? ` (${f.testFile})` : ''}`);
3447
+ if (f.hint)
3448
+ issues.push(` Fix: ${f.hint}`);
3359
3449
  }
3360
3450
  }
3361
- if (s.missingFromGlossary > 0)
3451
+ if (s.missingFromGlossary > 0) {
3362
3452
  issues.push(`${s.missingFromGlossary} file(s) with scenarios not in glossary`);
3453
+ const missingGlossary = data.missingFromGlossary || [];
3454
+ for (const m of missingGlossary) {
3455
+ issues.push(` → ${m.name} (${m.filePath})`);
3456
+ }
3457
+ issues.push(` Fix: Run \`codeyam editor analyze-imports\` to add these files to the glossary`);
3458
+ }
3363
3459
  if (s.incompleteEntities > 0) {
3460
+ // Check for persistent analysis failures
3461
+ let analysisFailures = {};
3462
+ try {
3463
+ const failuresPath = path.join(getProjectRoot(), '.codeyam', 'analysis-failures.json');
3464
+ if (fs.existsSync(failuresPath)) {
3465
+ analysisFailures = JSON.parse(fs.readFileSync(failuresPath, 'utf8'));
3466
+ }
3467
+ }
3468
+ catch {
3469
+ // Non-fatal
3470
+ }
3364
3471
  const preCount = s.preExistingIncompleteEntities || 0;
3365
- if (preCount > 0 && preCount === s.incompleteEntities) {
3472
+ const hasFailures = Object.keys(analysisFailures).length > 0;
3473
+ if (hasFailures) {
3474
+ issues.push(`${s.incompleteEntities} entity/entities — automated analysis failed, manual analysis required`);
3475
+ }
3476
+ else if (preCount > 0 && preCount === s.incompleteEntities) {
3366
3477
  issues.push(`${s.incompleteEntities} entity/entities need import analysis (pre-existing — not caused by your current changes, but must be fixed before proceeding)`);
3367
3478
  }
3368
3479
  else if (preCount > 0) {
@@ -3371,6 +3482,20 @@ function printAuditGateFailures(data) {
3371
3482
  else {
3372
3483
  issues.push(`${s.incompleteEntities} entity/entities need import analysis`);
3373
3484
  }
3485
+ const incomplete = data.incompleteEntities || [];
3486
+ for (const e of incomplete) {
3487
+ const failureEntry = Object.values(analysisFailures).find((f) => f.entityName === e.name);
3488
+ if (failureEntry) {
3489
+ issues.push(` → ${e.name} — MANUAL ANALYSIS REQUIRED (automated analysis failed)`);
3490
+ issues.push(` Run \`codeyam editor audit\` for step-by-step manual analysis instructions`);
3491
+ }
3492
+ else {
3493
+ issues.push(` → ${e.name} (${e.scenarioCount} scenario${e.scenarioCount !== 1 ? 's' : ''})${e.preExisting ? ' [pre-existing]' : ''}`);
3494
+ }
3495
+ }
3496
+ if (!hasFailures) {
3497
+ issues.push(` Fix: Run \`codeyam editor analyze-imports\` to build import graphs`);
3498
+ }
3374
3499
  }
3375
3500
  if (s.unassociatedScenarios > 0) {
3376
3501
  issues.push(`${s.unassociatedScenarios} component(s) have scenarios with no entity link — run \`codeyam editor analyze-imports\` then re-run audit`);
@@ -3379,10 +3504,22 @@ function printAuditGateFailures(data) {
3379
3504
  issues.push(` → ${u.name} (${u.filePath}) — ${u.scenarioCount} scenario${u.scenarioCount !== 1 ? 's' : ''}`);
3380
3505
  }
3381
3506
  }
3382
- if (s.miscategorizedScenarios > 0)
3507
+ if (s.miscategorizedScenarios > 0) {
3383
3508
  issues.push(`${s.miscategorizedScenarios} component(s) using page URLs instead of isolation routes`);
3384
- if (s.scenariosNeedingRecapture > 0)
3509
+ const miscategorized = data.miscategorizedScenarios || [];
3510
+ for (const m of miscategorized) {
3511
+ issues.push(` → ${m.componentName} at ${m.url} (${m.scenarioNames.length} scenario${m.scenarioNames.length !== 1 ? 's' : ''})`);
3512
+ }
3513
+ issues.push(` Fix: Re-register these as app-level scenarios (with pageFilePath, no componentName) or use isolation routes`);
3514
+ }
3515
+ if (s.scenariosNeedingRecapture > 0) {
3385
3516
  issues.push(`${s.scenariosNeedingRecapture} scenario(s) need recapture (dependency tree changed)`);
3517
+ const recapture = data.scenariosNeedingRecapture || [];
3518
+ for (const r of recapture) {
3519
+ issues.push(` → "${r.scenarioName}" (entity: ${r.entityName}, ${r.status?.status || 'changed'})`);
3520
+ }
3521
+ issues.push(` Fix: Re-capture affected scenarios to update screenshots after code changes`);
3522
+ }
3386
3523
  if (issues.length > 0) {
3387
3524
  console.error(chalk.yellow('\nAudit failures:'));
3388
3525
  for (const issue of issues) {
@@ -3425,7 +3562,7 @@ async function handleAudit() {
3425
3562
  }
3426
3563
  // Lightweight auto-fix: backfill entity_sha on scenarios that were
3427
3564
  // registered before entity records existed. This is fast (DB-only).
3428
- // We do NOT run handleAnalyzeImports here — it scans all files and takes
3565
+ // Import analysis is NOT triggered here — it scans all files and takes
3429
3566
  // minutes on large projects. Users should run it separately if needed.
3430
3567
  const incompleteBeforeFix = data.incompleteEntities || [];
3431
3568
  const unassociatedBeforeFix = data.unassociatedScenarios || [];
@@ -3451,8 +3588,9 @@ async function handleAudit() {
3451
3588
  catch {
3452
3589
  // Fall through — re-fetch will show remaining issues
3453
3590
  }
3454
- // Re-fetch audit results after the backfill
3455
- data = await fetchAuditResult();
3591
+ // Re-fetch audit results after the backfill — skip re-running tests
3592
+ // since they haven't changed (backfill is DB-only).
3593
+ data = await fetchAuditResult({ skipTests: true });
3456
3594
  if (!data) {
3457
3595
  console.error(chalk.red('Error: Could not reach the CodeYam server after backfill.'));
3458
3596
  process.exit(1);
@@ -3566,48 +3704,42 @@ async function handleAudit() {
3566
3704
  console.log(chalk.yellow(' Add these to .codeyam/glossary.json and run `codeyam editor analyze-imports`'));
3567
3705
  console.log();
3568
3706
  }
3569
- // Incomplete entities (scenarios without analyses) — auto-fix by running
3570
- // targeted analysis on just the affected files (fast: 2 files vs 117+).
3571
- let incompleteEntities = data.incompleteEntities || [];
3707
+ // Incomplete entities (scenarios without analyses) — report with guidance.
3708
+ // We intentionally do NOT run analysis here: it starts the analyzer
3709
+ // template which is slow even for a few files. Users should run
3710
+ // `codeyam editor analyze-imports` separately if needed.
3711
+ const incompleteEntities = data.incompleteEntities || [];
3572
3712
  if (incompleteEntities.length > 0) {
3573
- const { getIncompleteEntityFilePaths } = await import('../utils/editorAudit.js');
3574
- try {
3575
- const { getDatabase } = await import('../../../packages/database/index.js');
3576
- const db = getDatabase();
3577
- const filePaths = await getIncompleteEntityFilePaths(db, incompleteEntities);
3578
- if (filePaths.length > 0) {
3579
- console.log(chalk.dim(`Running targeted import analysis for ${filePaths.length} incomplete entit${filePaths.length !== 1 ? 'ies' : 'y'}...`));
3580
- const root = getProjectRoot();
3581
- try {
3582
- const { runAnalysisForEntities } = await import('../utils/analysisRunner.js');
3583
- await runAnalysisForEntities({
3584
- projectRoot: root,
3585
- filePaths,
3586
- progress: new ProgressReporter(),
3587
- onlyDataStructure: true,
3588
- });
3589
- // Re-fetch audit results after targeted fix
3590
- const refreshed = await fetchAuditResult();
3591
- if (refreshed) {
3592
- data = refreshed;
3593
- incompleteEntities = data.incompleteEntities || [];
3594
- }
3595
- }
3596
- catch {
3597
- // Fall through — report remaining incomplete entities below
3713
+ const { formatIncompleteEntityGuidance, formatManualAnalysisGuidance } = await import('../utils/editorAudit.js');
3714
+ const { readAnalysisFailures } = await import('../utils/manualEntityAnalysis.js');
3715
+ // Check for persistent analysis failures
3716
+ const analysisFailures = readAnalysisFailures(getProjectRoot());
3717
+ console.log(chalk.bold('Incomplete entities (need import analysis):'));
3718
+ for (const e of incompleteEntities) {
3719
+ // Check if this entity has a persistent failure
3720
+ const failureEntry = Object.values(analysisFailures).find((f) => f.entityName === e.name);
3721
+ if (failureEntry) {
3722
+ // Show manual analysis instructions instead of "run analyze-imports"
3723
+ const filePath = Object.entries(analysisFailures).find(([, f]) => f.entityName === e.name)?.[0];
3724
+ const guidance = formatManualAnalysisGuidance({
3725
+ name: e.name,
3726
+ filePath: filePath || '',
3727
+ scenarioCount: e.scenarioCount,
3728
+ error: failureEntry.error,
3729
+ });
3730
+ // Print each line with proper indentation
3731
+ for (const line of guidance.split('\n')) {
3732
+ console.log(` ${chalk.red('✗')} ${line}`);
3598
3733
  }
3599
3734
  }
3735
+ else {
3736
+ const guidance = formatIncompleteEntityGuidance(e);
3737
+ console.log(` ${chalk.red('✗')} ${guidance}`);
3738
+ }
3600
3739
  }
3601
- catch {
3602
- // DB not available — fall through to reporting
3603
- }
3604
- }
3605
- if (incompleteEntities.length > 0) {
3606
- const { formatIncompleteEntityGuidance } = await import('../utils/editorAudit.js');
3607
- console.log(chalk.bold('Incomplete entities (need import analysis):'));
3608
- for (const e of incompleteEntities) {
3609
- const guidance = formatIncompleteEntityGuidance(e);
3610
- console.log(` ${chalk.red('✗')} ${guidance}`);
3740
+ const incompleteErrorReportPath = path.join(getProjectRoot(), '.codeyam', 'analysis-errors.txt');
3741
+ if (fs.existsSync(incompleteErrorReportPath)) {
3742
+ console.log(chalk.dim(' See .codeyam/analysis-errors.txt for error details.'));
3611
3743
  }
3612
3744
  console.log();
3613
3745
  }
@@ -3631,6 +3763,10 @@ async function handleAudit() {
3631
3763
  else {
3632
3764
  console.log(chalk.yellow(' Run `codeyam editor analyze-imports` to create entity records, then re-run audit to backfill.'));
3633
3765
  }
3766
+ const unassocErrorReportPath = path.join(getProjectRoot(), '.codeyam', 'analysis-errors.txt');
3767
+ if (fs.existsSync(unassocErrorReportPath)) {
3768
+ console.log(chalk.dim(' See .codeyam/analysis-errors.txt for error details.'));
3769
+ }
3634
3770
  console.log();
3635
3771
  }
3636
3772
  // Miscategorized scenarios (component scenarios using real page URLs)
@@ -3721,17 +3857,6 @@ async function handleAudit() {
3721
3857
  if (!allOk) {
3722
3858
  process.exit(1);
3723
3859
  }
3724
- // Auto-run analyze-imports when audit passes — this builds the import graph
3725
- // so the App tab can show component/function dependencies for each page.
3726
- // Previously this was a manual step that Claude had to remember to run.
3727
- console.log(chalk.dim('Building import graph...'));
3728
- try {
3729
- await handleAnalyzeImports({ silent: true });
3730
- }
3731
- catch {
3732
- // Non-fatal — audit passed, import graph is a bonus
3733
- console.log(chalk.yellow('Warning: Could not build import graph. Run `codeyam editor analyze-imports` manually.'));
3734
- }
3735
3860
  }
3736
3861
  // ─── Recapture-stale subcommand ────────────────────────────────────────
3737
3862
  /**
@@ -3938,14 +4063,14 @@ async function handleScenarioCoverage() {
3938
4063
  // Safety net: heal any scenarios with null entity_sha before checking coverage
3939
4064
  try {
3940
4065
  const { getDatabase } = await import('../../../packages/database/index.js');
3941
- const { countScenariosNeedingEntityBackfill } = await import('../utils/editorScenarios');
4066
+ const { countScenariosNeedingEntityBackfill } = await import('../utils/editorScenarios.js');
3942
4067
  const db = getDatabase();
3943
4068
  const backfillCount = await countScenariosNeedingEntityBackfill(db);
3944
4069
  if (backfillCount > 0) {
3945
4070
  console.log(chalk.dim(` Healing ${backfillCount} scenario(s) with unlinked entities...`));
3946
4071
  await handleAnalyzeImports({ silent: true });
3947
4072
  // Run backfill after analysis
3948
- const { backfillEntityShaOnScenarios } = await import('../utils/editorScenarios');
4073
+ const { backfillEntityShaOnScenarios } = await import('../utils/editorScenarios.js');
3949
4074
  const entities = await loadEntities({});
3950
4075
  if (entities && entities.length > 0) {
3951
4076
  await backfillEntityShaOnScenarios(db, entities.map((e) => ({
@@ -4442,14 +4567,205 @@ function handleEditorDebug(args) {
4442
4567
  console.log(chalk.dim(' Open index.md for the full list of scenario outputs.'));
4443
4568
  console.log();
4444
4569
  }
4570
+ // ─── Manual entity analysis ───────────────────────────────────────────
4571
+ /**
4572
+ * `codeyam editor manual-entity <JSON|@file>`
4573
+ *
4574
+ * Creates entity and analysis records from Claude-provided metadata when
4575
+ * automated analyze-imports fails. This unblocks the audit gate without
4576
+ * needing the analyzer template to parse the source file.
4577
+ */
4578
+ async function handleManualEntity(jsonArg) {
4579
+ const root = getProjectRoot();
4580
+ // Parse JSON input (supports @file.json convention)
4581
+ const parsed = parseRegisterArg(jsonArg);
4582
+ if (parsed.error || !parsed.body) {
4583
+ console.error(chalk.red(`Error: ${parsed.error || 'Invalid input'}`));
4584
+ console.error(chalk.dim(' Usage: codeyam editor manual-entity \'{"name":"Header","filePath":"src/components/Header.tsx","entityType":"visual",...}\''));
4585
+ console.error(chalk.dim(' Or: codeyam editor manual-entity @.codeyam/tmp/manual-entity.json'));
4586
+ process.exit(1);
4587
+ }
4588
+ const input = parsed.body;
4589
+ // Validate input
4590
+ const { validateManualEntityInput, convertTypeInfoToDataForMocks, readAnalysisFailures, clearFailureForPath, writeAnalysisFailures, } = await import('../utils/manualEntityAnalysis.js');
4591
+ // Read glossary for validation
4592
+ const glossaryPath = path.join(root, '.codeyam', 'glossary.json');
4593
+ let glossaryEntries = [];
4594
+ try {
4595
+ glossaryEntries = sanitizeGlossaryEntries(JSON.parse(fs.readFileSync(glossaryPath, 'utf8')));
4596
+ }
4597
+ catch {
4598
+ // Empty glossary — validation will still check file existence
4599
+ }
4600
+ const errors = validateManualEntityInput(input, glossaryEntries, {
4601
+ fileExists: (p) => fs.existsSync(path.join(root, p)),
4602
+ });
4603
+ if (errors.length > 0) {
4604
+ console.error(chalk.red('Validation errors:'));
4605
+ for (const err of errors) {
4606
+ console.error(chalk.red(` • ${err}`));
4607
+ }
4608
+ process.exit(1);
4609
+ }
4610
+ // Read source file and compute SHA
4611
+ const sourceContent = fs.readFileSync(path.join(root, input.filePath), 'utf8');
4612
+ const { generateSha } = await import('../../../packages/database/index.js');
4613
+ const entitySha = generateSha(input.filePath, input.name, sourceContent);
4614
+ // Get project and branch
4615
+ await initializeEnvironment();
4616
+ const configPath = path.join(root, '.codeyam', 'config.json');
4617
+ let projectSlug;
4618
+ try {
4619
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
4620
+ projectSlug = config.projectSlug;
4621
+ }
4622
+ catch {
4623
+ console.error(chalk.red('Error: .codeyam/config.json not found or invalid.'));
4624
+ process.exit(1);
4625
+ }
4626
+ const { project, branch } = await requireBranchAndProject(projectSlug);
4627
+ const { getDatabase } = await import('../../../packages/database/index.js');
4628
+ const db = getDatabase();
4629
+ // Convert type info to dataForMocks format
4630
+ const dataForMocks = convertTypeInfoToDataForMocks(input.typeInfo);
4631
+ // Create entity record
4632
+ const entityMetadata = {
4633
+ importedExports: (input.importedExports || []).map((ie) => ({
4634
+ name: ie.name,
4635
+ filePath: ie.filePath,
4636
+ })),
4637
+ manuallyAnalyzed: true,
4638
+ };
4639
+ // Check if entity already exists
4640
+ const existingEntity = await db
4641
+ .selectFrom('entities')
4642
+ .select('sha')
4643
+ .where('sha', '=', entitySha)
4644
+ .executeTakeFirst();
4645
+ if (!existingEntity) {
4646
+ await db
4647
+ .insertInto('entities')
4648
+ .values({
4649
+ sha: entitySha,
4650
+ project_id: project.id,
4651
+ name: input.name,
4652
+ entity_type: input.entityType,
4653
+ file_path: input.filePath,
4654
+ metadata: JSON.stringify(entityMetadata),
4655
+ })
4656
+ .onConflict((oc) => oc.column('sha').doNothing())
4657
+ .execute();
4658
+ console.log(chalk.dim(`Created entity record: ${input.name} (${entitySha.slice(0, 8)}...)`));
4659
+ }
4660
+ else {
4661
+ // Update metadata on existing entity
4662
+ await db
4663
+ .updateTable('entities')
4664
+ .set({
4665
+ metadata: JSON.stringify(entityMetadata),
4666
+ })
4667
+ .where('sha', '=', entitySha)
4668
+ .execute();
4669
+ console.log(chalk.dim(`Updated existing entity: ${input.name} (${entitySha.slice(0, 8)}...)`));
4670
+ }
4671
+ // Create entity_branch record
4672
+ await db
4673
+ .insertInto('entity_branches')
4674
+ .values({
4675
+ entity_sha: entitySha,
4676
+ branch_id: branch.id,
4677
+ active: true,
4678
+ })
4679
+ .onConflict((oc) => oc.columns(['entity_sha', 'branch_id']).doUpdateSet({ active: true }))
4680
+ .execute();
4681
+ // Create analysis record
4682
+ const { randomUUID } = await import('crypto');
4683
+ const analysisId = randomUUID();
4684
+ const now = new Date().toISOString();
4685
+ const analysisMetadata = {
4686
+ scenariosDataStructure: {
4687
+ dataForMocks: Object.keys(dataForMocks).length > 0 ? dataForMocks : null,
4688
+ },
4689
+ mergedDataStructure: {
4690
+ dependencySchemas: null,
4691
+ },
4692
+ manuallyAnalyzed: true,
4693
+ };
4694
+ const analysisStatus = {
4695
+ startedAt: now,
4696
+ finishedAt: now,
4697
+ steps: [],
4698
+ errors: [],
4699
+ };
4700
+ // Check if analysis already exists for this entity
4701
+ const existingAnalysis = await db
4702
+ .selectFrom('analyses')
4703
+ .select('id')
4704
+ .where('entity_sha', '=', entitySha)
4705
+ .executeTakeFirst();
4706
+ if (!existingAnalysis) {
4707
+ await db
4708
+ .insertInto('analyses')
4709
+ .values({
4710
+ id: analysisId,
4711
+ project_id: project.id,
4712
+ entity_sha: entitySha,
4713
+ entity_name: input.name,
4714
+ entity_type: input.entityType,
4715
+ file_path: input.filePath,
4716
+ status: JSON.stringify(analysisStatus),
4717
+ metadata: JSON.stringify(analysisMetadata),
4718
+ })
4719
+ .execute();
4720
+ console.log(chalk.dim(`Created analysis record: ${analysisId.slice(0, 8)}...`));
4721
+ }
4722
+ else {
4723
+ // Update existing analysis with manual metadata
4724
+ await db
4725
+ .updateTable('analyses')
4726
+ .set({
4727
+ metadata: JSON.stringify(analysisMetadata),
4728
+ status: JSON.stringify(analysisStatus),
4729
+ })
4730
+ .where('entity_sha', '=', entitySha)
4731
+ .execute();
4732
+ console.log(chalk.dim(`Updated existing analysis for ${input.name}`));
4733
+ }
4734
+ // Backfill entity_sha on scenarios
4735
+ const backfillResult = await backfillEntityShaOnScenarios(db, [
4736
+ {
4737
+ sha: entitySha,
4738
+ name: input.name,
4739
+ filePath: input.filePath,
4740
+ isDefaultExport: false,
4741
+ },
4742
+ ]);
4743
+ if (backfillResult.updated > 0) {
4744
+ console.log(chalk.dim(`Linked ${backfillResult.updated} scenario(s) to entity`));
4745
+ }
4746
+ // Clear from analysis failures tracker
4747
+ const failures = readAnalysisFailures(root);
4748
+ if (failures[input.filePath]) {
4749
+ const updated = clearFailureForPath(failures, input.filePath);
4750
+ writeAnalysisFailures(root, updated);
4751
+ console.log(chalk.dim(`Cleared analysis failure for ${input.filePath}`));
4752
+ }
4753
+ console.log();
4754
+ console.log(chalk.green.bold(`✓ Manual entity analysis complete: ${input.name}`));
4755
+ console.log(chalk.dim(` Entity SHA: ${entitySha.slice(0, 12)}...`));
4756
+ console.log(chalk.dim(` Imports: ${(input.importedExports || []).length} glossary entities`));
4757
+ console.log(chalk.dim(` Type info: ${Object.keys(dataForMocks).length} fields`));
4758
+ console.log();
4759
+ console.log(chalk.dim('Re-run `codeyam editor audit` to verify.'));
4760
+ }
4445
4761
  // ─── Command definition ───────────────────────────────────────────────
4446
4762
  const editorCommand = {
4447
4763
  command: 'editor [step] [json]',
4448
4764
  describe: 'Editor mode guided workflow',
4449
4765
  builder: (yargs) => {
4450
4766
  const stepDescription = IS_INTERNAL_BUILD
4451
- ? '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)'
4452
- : '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)';
4767
+ ? 'Step number (1-18) or subcommand (template, register, isolate, analyze-imports, manual-entity, 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)'
4768
+ : 'Step number (1-18) or subcommand (template, register, isolate, analyze-imports, manual-entity, 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)';
4453
4769
  let builder = yargs
4454
4770
  .positional('step', {
4455
4771
  type: 'string',
@@ -4591,7 +4907,9 @@ const editorCommand = {
4591
4907
  'Ask user what to build next and restart codeyam editor workflow';
4592
4908
  }
4593
4909
  console.log(chalk.bold.cyan('━━━ TASK ━━━'));
4594
- console.log(chalk.cyan('Mark your current task (if any) as complete.'));
4910
+ console.log(chalk.cyan('Before creating the task below, delete ALL existing tasks:'));
4911
+ console.log(chalk.cyan(' 1. Run TaskList to find all tasks'));
4912
+ console.log(chalk.cyan(' 2. Run TaskUpdate with status "deleted" for EACH task returned'));
4595
4913
  console.log(chalk.cyan('Then create a new task with this EXACT title (copy verbatim):'));
4596
4914
  console.log();
4597
4915
  console.log(chalk.bold.cyan(`EXACT_TASK_TITLE: ${taskTitle}`));
@@ -4610,6 +4928,11 @@ const editorCommand = {
4610
4928
  await handleAnalyzeImports();
4611
4929
  return;
4612
4930
  }
4931
+ // Subcommand: codeyam editor manual-entity <JSON|@file>
4932
+ if (argv.step === 'manual-entity') {
4933
+ await handleManualEntity(argv.json || '');
4934
+ return;
4935
+ }
4613
4936
  // Subcommand: codeyam editor dependents <EntityName>
4614
4937
  if (argv.step === 'dependents') {
4615
4938
  await handleDependents(argv.json || '');
@@ -4850,13 +5173,24 @@ const editorCommand = {
4850
5173
  try {
4851
5174
  const { getDatabase: getDb } = await import('../../../packages/database/index.js');
4852
5175
  const seedDb = getDb();
4853
- const appScenario = await seedDb
5176
+ // Prefer the home page scenario (url "/") since the App tab
5177
+ // sorts "Home" first — fall back to any application scenario.
5178
+ const homeScenario = await seedDb
4854
5179
  .selectFrom('editor_scenarios')
4855
5180
  .select(['id', 'name', 'type'])
4856
5181
  .where('project_id', '=', project.id)
4857
- .where('type', '=', 'application')
5182
+ .where('url', '=', '/')
5183
+ .where('component_name', 'is', null)
4858
5184
  .orderBy('created_at', 'asc')
4859
5185
  .executeTakeFirst();
5186
+ const appScenario = homeScenario ||
5187
+ (await seedDb
5188
+ .selectFrom('editor_scenarios')
5189
+ .select(['id', 'name', 'type'])
5190
+ .where('project_id', '=', project.id)
5191
+ .where('type', '=', 'application')
5192
+ .orderBy('created_at', 'asc')
5193
+ .executeTakeFirst());
4860
5194
  if (appScenario) {
4861
5195
  const seedFile = path.join(projectRoot, '.codeyam', 'editor-scenarios', `${appScenario.id}.seed.json`);
4862
5196
  if (fs.existsSync(seedFile)) {
@@ -4999,7 +5333,7 @@ const editorCommand = {
4999
5333
  if (!needsAnalysis) {
5000
5334
  try {
5001
5335
  const { getDatabase } = await import('../../../packages/database/index.js');
5002
- const { countScenariosNeedingEntityBackfill } = await import('../utils/editorScenarios');
5336
+ const { countScenariosNeedingEntityBackfill } = await import('../utils/editorScenarios.js');
5003
5337
  const db = getDatabase();
5004
5338
  const backfillCount = await countScenariosNeedingEntityBackfill(db);
5005
5339
  if (backfillCount > 0) {
@@ -5038,7 +5372,7 @@ const editorCommand = {
5038
5372
  .execute();
5039
5373
  if (unresolved.length > 0) {
5040
5374
  const { scanPageFilePaths: scanPfp } = await import('../utils/entityChangeStatus.server.js');
5041
- const { matchUrlToPageFile } = await import('../utils/routePatternMatching');
5375
+ const { matchUrlToPageFile } = await import('../utils/routePatternMatching.js');
5042
5376
  const { allFiles: pfpFiles } = scanPfp(projectRoot);
5043
5377
  let pfpResolved = 0;
5044
5378
  for (const row of unresolved) {