@codeyam/codeyam-cli 0.1.20 → 0.1.21

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 (70) hide show
  1. package/analyzer-template/.build-info.json +7 -7
  2. package/analyzer-template/log.txt +3 -3
  3. package/analyzer-template/package.json +1 -1
  4. package/analyzer-template/packages/ai/src/lib/dataStructure/ScopeDataStructure.ts +5 -1
  5. package/analyzer-template/packages/analyze/src/lib/files/analyze/findOrCreateEntity.ts +1 -0
  6. package/analyzer-template/packages/analyze/src/lib/files/analyzeChange.ts +4 -0
  7. package/analyzer-template/packages/analyze/src/lib/files/analyzeInitial.ts +4 -0
  8. package/analyzer-template/packages/aws/package.json +1 -1
  9. package/analyzer-template/packages/database/src/lib/loadEntity.ts +11 -4
  10. package/analyzer-template/packages/github/dist/database/src/lib/loadEntity.d.ts +4 -1
  11. package/analyzer-template/packages/github/dist/database/src/lib/loadEntity.d.ts.map +1 -1
  12. package/analyzer-template/packages/github/dist/database/src/lib/loadEntity.js +4 -4
  13. package/analyzer-template/packages/github/dist/database/src/lib/loadEntity.js.map +1 -1
  14. package/analyzer-template/packages/utils/dist/utils/src/lib/fs/rsyncCopy.d.ts +3 -1
  15. package/analyzer-template/packages/utils/dist/utils/src/lib/fs/rsyncCopy.d.ts.map +1 -1
  16. package/analyzer-template/packages/utils/dist/utils/src/lib/fs/rsyncCopy.js +22 -1
  17. package/analyzer-template/packages/utils/dist/utils/src/lib/fs/rsyncCopy.js.map +1 -1
  18. package/analyzer-template/packages/utils/src/lib/fs/rsyncCopy.ts +27 -0
  19. package/codeyam-cli/src/commands/editor.js +185 -59
  20. package/codeyam-cli/src/commands/editor.js.map +1 -1
  21. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +630 -23
  22. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
  23. package/codeyam-cli/src/utils/__tests__/registerScenarioResult.test.js +127 -0
  24. package/codeyam-cli/src/utils/__tests__/registerScenarioResult.test.js.map +1 -0
  25. package/codeyam-cli/src/utils/analyzer.js +8 -0
  26. package/codeyam-cli/src/utils/analyzer.js.map +1 -1
  27. package/codeyam-cli/src/utils/editorAudit.js +113 -12
  28. package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
  29. package/codeyam-cli/src/utils/registerScenarioResult.js +52 -0
  30. package/codeyam-cli/src/utils/registerScenarioResult.js.map +1 -0
  31. package/codeyam-cli/src/webserver/app/lib/clientErrors.js +3 -0
  32. package/codeyam-cli/src/webserver/app/lib/clientErrors.js.map +1 -1
  33. package/codeyam-cli/src/webserver/build/client/assets/api.editor-save-scenario-data-l0sNRNKZ.js +1 -0
  34. package/codeyam-cli/src/webserver/build/client/assets/api.editor-schema-l0sNRNKZ.js +1 -0
  35. package/codeyam-cli/src/webserver/build/client/assets/{cy-logo-cli-CCKUIm0S.svg → cy-logo-cli-CJzc4vOH.svg} +2 -2
  36. package/codeyam-cli/src/webserver/build/client/assets/cy-logo-cli-DODLxLcw.js +1 -0
  37. package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-Dx-h1rJK.js +130 -0
  38. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-NTuLi4Xg.js +41 -0
  39. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.dev-KTQuL0aj.js → entity._sha.scenarios._scenarioId.dev-BA5L8bU-.js} +1 -1
  40. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.fullscreen-C6eeL24i.js → entity._sha.scenarios._scenarioId.fullscreen-D4dmRgvO.js} +1 -1
  41. package/codeyam-cli/src/webserver/build/client/assets/globals-BrPXT1iR.css +1 -0
  42. package/codeyam-cli/src/webserver/build/client/assets/manifest-5025e428.js +1 -0
  43. package/codeyam-cli/src/webserver/build/client/assets/{root-BxUQigda.js → root-BCx1S8Z3.js} +26 -13
  44. package/codeyam-cli/src/webserver/build/server/assets/{analysisRunner-B_PsTAb1.js → analysisRunner-C1kjC9UJ.js} +1 -1
  45. package/codeyam-cli/src/webserver/build/server/assets/{index-CjLhfz6Z.js → index-C91yWWCI.js} +1 -1
  46. package/codeyam-cli/src/webserver/build/server/assets/{init-BEqlbI84.js → init-Dkas-RUS.js} +1 -1
  47. package/codeyam-cli/src/webserver/build/server/assets/server-build-pulXLTrG.js +640 -0
  48. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  49. package/codeyam-cli/src/webserver/build-info.json +5 -5
  50. package/codeyam-cli/src/webserver/terminalServer.js +10 -3
  51. package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
  52. package/package.json +1 -1
  53. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js +4 -1
  54. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js.map +1 -1
  55. package/packages/analyze/src/lib/files/analyze/findOrCreateEntity.js +1 -0
  56. package/packages/analyze/src/lib/files/analyze/findOrCreateEntity.js.map +1 -1
  57. package/packages/analyze/src/lib/files/analyzeChange.js +1 -0
  58. package/packages/analyze/src/lib/files/analyzeChange.js.map +1 -1
  59. package/packages/analyze/src/lib/files/analyzeInitial.js +1 -0
  60. package/packages/analyze/src/lib/files/analyzeInitial.js.map +1 -1
  61. package/packages/database/src/lib/loadEntity.js +4 -4
  62. package/packages/database/src/lib/loadEntity.js.map +1 -1
  63. package/packages/utils/src/lib/fs/rsyncCopy.js +22 -1
  64. package/packages/utils/src/lib/fs/rsyncCopy.js.map +1 -1
  65. package/codeyam-cli/src/webserver/build/client/assets/cy-logo-cli-DcX-ZS3p.js +0 -1
  66. package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-DII1pg_z.js +0 -58
  67. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-oepecPae.js +0 -41
  68. package/codeyam-cli/src/webserver/build/client/assets/globals-Yn9W3zp3.css +0 -1
  69. package/codeyam-cli/src/webserver/build/client/assets/manifest-cdf2c0a7.js +0 -1
  70. package/codeyam-cli/src/webserver/build/server/assets/server-build-YI63xTu4.js +0 -553
@@ -21,6 +21,7 @@ import { validateSeedData, detectSeedAdapter, validateSeedKeysAgainstPrisma, } f
21
21
  import { deleteScenarioViaCli } from "../utils/editorDeleteScenario.js";
22
22
  import { buildEditorApiRequest, callEditorApi, EDITOR_API_SUBCOMMANDS, } from "../utils/editorApi.js";
23
23
  import { parseRegisterArg } from "../utils/parseRegisterArg.js";
24
+ import { classifyRegistrationResult } from "../utils/registerScenarioResult.js";
24
25
  import { sanitizeGlossaryEntries } from "../utils/editorLoaderHelpers.js";
25
26
  import { readMigrationState, writeMigrationState, advanceToNextPage, completePage, getMigrationResumeInfo, } from "../utils/editorMigration.js";
26
27
  const __filename = fileURLToPath(import.meta.url);
@@ -210,6 +211,34 @@ function checkbox(text) {
210
211
  const highlighted = text.replace(/`([^`]+)`/g, (_m, code) => chalk.cyan(code));
211
212
  console.log(` ${chalk.dim('[ ]')} ${highlighted}`);
212
213
  }
214
+ /**
215
+ * Instructions for creating/updating .codeyam/data-structure.json.
216
+ * Only prints creation instructions if the file doesn't exist yet.
217
+ * If it exists, reminds Claude to update it if data models changed.
218
+ */
219
+ function printDataStructureInstructions() {
220
+ const root = getProjectRoot();
221
+ const dsPath = path.join(root, '.codeyam', 'data-structure.json');
222
+ const exists = fs.existsSync(dsPath);
223
+ if (!exists) {
224
+ console.log(chalk.bold('Create the data structure config:'));
225
+ checkbox('Create `.codeyam/data-structure.json` describing all data sources');
226
+ console.log(chalk.dim(" This file tells the editor what data the app uses and how it's stored."));
227
+ console.log(chalk.dim(' The file is a JSON array. Each entry describes one data source:'));
228
+ console.log(chalk.dim(' { "name": "Drink", "description": "...", "category": "datastore", "order": 1,'));
229
+ console.log(chalk.dim(' "fields": [{ "name": "id", "type": "number", "isId": true, "required": true }, ...] }'));
230
+ console.log();
231
+ console.log(chalk.dim(' category: "datastore" for persisted data (DB, localStorage)'));
232
+ console.log(chalk.dim(' "mock-api" for mocked external API responses'));
233
+ console.log(chalk.dim(' order: 1 = primary data store, 2 = secondary, etc.'));
234
+ console.log(chalk.dim(' Do NOT include types only used as component props.'));
235
+ }
236
+ else {
237
+ checkbox('If this feature changes data models, update `.codeyam/data-structure.json`');
238
+ console.log(chalk.dim(' Add new tables, update fields, or change descriptions to match.'));
239
+ }
240
+ console.log();
241
+ }
213
242
  /**
214
243
  * Shared app scenario registration instructions used by editor Step 8 and migration Steps 1/6.
215
244
  * Prints seed-based scenarios, mock-based fallback, @ file prefix, externalApis, url requirement, and error checking.
@@ -978,6 +1007,7 @@ function printStep2(root, feature) {
978
1007
  console.log(chalk.dim(' Key: import { prisma } from "@/app/lib/prisma" in API routes.'));
979
1008
  console.log(chalk.dim(' Key: Seed scripts must use the adapter pattern (see prisma/seed.ts).'));
980
1009
  console.log();
1010
+ printDataStructureInstructions();
981
1011
  }
982
1012
  else {
983
1013
  console.log(chalk.bold('Prepare the database for this feature:'));
@@ -992,6 +1022,7 @@ function printStep2(root, feature) {
992
1022
  console.log(chalk.dim(' This switches the active scenario, runs the seed adapter, and refreshes the preview.'));
993
1023
  console.log(chalk.dim(' If no existing scenario fits, use the default seed: npm run db:seed'));
994
1024
  console.log();
1025
+ printDataStructureInstructions();
995
1026
  }
996
1027
  stopGate(2);
997
1028
  }
@@ -1326,7 +1357,10 @@ function printStep9(root, feature) {
1326
1357
  console.log();
1327
1358
  checkbox('Run `codeyam editor audit` to verify all components have scenarios and all functions/hooks have tests');
1328
1359
  console.log(chalk.red.bold(' The audit is a HARD GATE — step 10 will refuse to run until the audit passes.'));
1329
- console.log(chalk.dim(' When audit passes, the import graph is built automatically for change tracking.'));
1360
+ console.log(chalk.dim(' The audit auto-fixes incomplete entities by running targeted analysis on just the affected files.'));
1361
+ console.log(chalk.dim(' If the audit reports issues (including pre-existing ones), fix ALL of them — do not skip.'));
1362
+ console.log(chalk.dim(' Re-run `codeyam editor audit` until all checks pass. Do NOT run `codeyam editor analyze-imports`'));
1363
+ console.log(chalk.dim(' — the audit runs targeted analysis automatically and is much faster.'));
1330
1364
  console.log();
1331
1365
  stopGate(9);
1332
1366
  }
@@ -2362,25 +2396,54 @@ async function handleAnalyzeImports(options = {}) {
2362
2396
  // Non-fatal — scenario file paths just won't be included
2363
2397
  }
2364
2398
  const progress = new ProgressReporter();
2365
- // Run data-structure-only analysis for all entities (glossary + pages)
2366
- // Don't pass entityNames entities may not exist yet (fresh clone).
2367
- // The analyzer will discover and create them from file paths.
2368
- progress.start('Running import analysis for all entities...');
2399
+ // Filter to only files that actually need analysis avoids re-analyzing
2400
+ // ~120 files when only 2 are incomplete.
2401
+ let targetFilePaths = filePaths;
2369
2402
  try {
2370
- await runAnalysisForEntities({
2371
- projectRoot: root,
2372
- filePaths,
2373
- progress,
2374
- onlyDataStructure: true,
2375
- });
2403
+ const { getDatabase } = await import('../../../packages/database/index.js');
2404
+ const db = getDatabase();
2405
+ const { requireBranchAndProject: rbp } = await import('../utils/database.js');
2406
+ const { project } = await rbp(JSON.parse(fs.readFileSync(path.join(root, '.codeyam', 'config.json'), 'utf8')).projectSlug);
2407
+ const { filterToIncompleteFilePaths } = await import('../utils/editorAudit.js');
2408
+ const incomplete = await filterToIncompleteFilePaths(db, project.id, filePaths);
2409
+ if (incomplete.length < filePaths.length && incomplete.length > 0) {
2410
+ if (!options.silent) {
2411
+ console.log(chalk.dim(`Skipping ${filePaths.length - incomplete.length} already-analyzed files, analyzing ${incomplete.length} remaining...`));
2412
+ }
2413
+ targetFilePaths = incomplete;
2414
+ }
2415
+ else if (incomplete.length === 0) {
2416
+ if (!options.silent) {
2417
+ console.log(chalk.green('All entities already have analyses — nothing to do.'));
2418
+ }
2419
+ // Still need to run the import graph + backfill below
2420
+ targetFilePaths = [];
2421
+ }
2376
2422
  }
2377
- catch (err) {
2378
- progress.fail('Analysis failed');
2379
- const msg = err instanceof Error ? err.message : String(err);
2380
- console.error(chalk.red(`Error: ${msg}`));
2381
- process.exit(1);
2423
+ catch {
2424
+ // Non-fatal — fall back to analyzing all files
2425
+ }
2426
+ // Run data-structure-only analysis for entities that need it.
2427
+ // Don't pass entityNames — entities may not exist yet (fresh clone).
2428
+ // The analyzer will discover and create them from file paths.
2429
+ if (targetFilePaths.length > 0) {
2430
+ progress.start(`Running import analysis for ${targetFilePaths.length} entit${targetFilePaths.length !== 1 ? 'ies' : 'y'}...`);
2431
+ try {
2432
+ await runAnalysisForEntities({
2433
+ projectRoot: root,
2434
+ filePaths: targetFilePaths,
2435
+ progress,
2436
+ onlyDataStructure: true,
2437
+ });
2438
+ }
2439
+ catch (err) {
2440
+ progress.fail('Analysis failed');
2441
+ const msg = err instanceof Error ? err.message : String(err);
2442
+ console.error(chalk.red(`Error: ${msg}`));
2443
+ process.exit(1);
2444
+ }
2445
+ progress.succeed('Analysis complete');
2382
2446
  }
2383
- progress.succeed('Analysis complete');
2384
2447
  // Load entities WITH metadata from database
2385
2448
  progress.start('Loading entity metadata...');
2386
2449
  await initializeEnvironment();
@@ -2736,6 +2799,8 @@ async function handleRegister(jsonArg) {
2736
2799
  const isBatch = items.length > 1;
2737
2800
  let succeeded = 0;
2738
2801
  let failed = 0;
2802
+ let warnings = 0;
2803
+ const issues = [];
2739
2804
  const screenshotEntries = [];
2740
2805
  for (let i = 0; i < items.length; i++) {
2741
2806
  const body = items[i];
@@ -2805,11 +2870,14 @@ async function handleRegister(jsonArg) {
2805
2870
  }
2806
2871
  console.log(chalk.yellow(' Fix the issue and re-register this scenario.'));
2807
2872
  }
2873
+ const resultIssues = classifyRegistrationResult(res.ok, res.status, data, String(body.name || `item ${i + 1}`));
2808
2874
  if (!res.ok) {
2809
2875
  console.error(chalk.dim(JSON.stringify(data, null, 2)));
2810
2876
  failed++;
2811
2877
  }
2812
2878
  else {
2879
+ if (resultIssues.length > 0)
2880
+ warnings++;
2813
2881
  succeeded++;
2814
2882
  // Collect screenshot paths for duplicate detection
2815
2883
  if (data.screenshotCaptured &&
@@ -2822,6 +2890,10 @@ async function handleRegister(jsonArg) {
2822
2890
  });
2823
2891
  }
2824
2892
  }
2893
+ // Collect issues from this registration
2894
+ for (const issue of resultIssues) {
2895
+ issues.push(`"${issue.scenarioName}": ${issue.message}`);
2896
+ }
2825
2897
  }
2826
2898
  catch (error) {
2827
2899
  const msg = error instanceof Error ? error.message : String(error);
@@ -2859,9 +2931,22 @@ async function handleRegister(jsonArg) {
2859
2931
  }
2860
2932
  }
2861
2933
  if (isBatch) {
2862
- console.log(chalk.dim(`\nBatch complete: ${succeeded}/${items.length} succeeded`));
2934
+ console.log('');
2935
+ if (failed > 0 || warnings > 0) {
2936
+ console.log(chalk.red.bold(`ERROR: ${failed} failed, ${warnings} with warnings out of ${items.length} scenarios`));
2937
+ console.log('');
2938
+ console.log(chalk.red.bold('Issues that MUST be fixed:'));
2939
+ for (const issue of issues) {
2940
+ console.log(chalk.red(` ✗ ${issue}`));
2941
+ }
2942
+ console.log('');
2943
+ console.log(chalk.red('Do NOT skip these errors. Fix each issue and re-register the affected scenarios.'));
2944
+ }
2945
+ else {
2946
+ console.log(chalk.green(`✓ Batch complete: ${succeeded}/${items.length} succeeded with no issues`));
2947
+ }
2863
2948
  }
2864
- if (failed > 0) {
2949
+ if (failed > 0 || warnings > 0) {
2865
2950
  process.exit(1);
2866
2951
  }
2867
2952
  }
@@ -3176,17 +3261,15 @@ async function checkAuditGate() {
3176
3261
  return true; // Server unreachable — don't block
3177
3262
  if (data.summary?.allPassing === true)
3178
3263
  return true;
3179
- // If incomplete entities / unassociated scenarios are the only issue,
3180
- // auto-fix with analyze-imports + entity SHA backfill (once)
3181
- const { isAutoRemediable } = await import('../utils/editorAudit.js');
3182
- if (isAutoRemediable(data.summary, false)) {
3183
- try {
3184
- await handleAnalyzeImports({ silent: true });
3185
- }
3186
- catch {
3187
- // Fall through to backfill attempt
3188
- }
3189
- // Backfill entity_sha on scenarios registered before entities existed
3264
+ // Lightweight auto-fix: backfill entity_sha (DB-only, fast).
3265
+ // Never run handleAnalyzeImports here it takes minutes on large projects.
3266
+ const { isOnlyIncompleteEntities, isOnlyPreExistingIncomplete } = await import('../utils/editorAudit.js');
3267
+ // If the only failures are pre-existing incomplete entities (not caused by
3268
+ // this session), don't block — the audit text already calls these "non-blocking".
3269
+ if (isOnlyPreExistingIncomplete(data.summary, data.incompleteEntities)) {
3270
+ return true;
3271
+ }
3272
+ if (isOnlyIncompleteEntities(data.summary)) {
3190
3273
  try {
3191
3274
  const entities = await loadEntities({});
3192
3275
  if (entities && entities.length > 0) {
@@ -3204,7 +3287,7 @@ async function checkAuditGate() {
3204
3287
  catch {
3205
3288
  // Fall through
3206
3289
  }
3207
- // Re-check after fix
3290
+ // Re-check after backfill
3208
3291
  const retry = await fetchAuditResult();
3209
3292
  if (retry?.summary?.allPassing === true)
3210
3293
  return true;
@@ -3228,6 +3311,8 @@ function printAuditGateFailures(data) {
3228
3311
  const missing = (data.components || []).filter((c) => c.status === 'missing');
3229
3312
  for (const c of missing) {
3230
3313
  issues.push(` → ${c.name}${c.filePath ? ` (${c.filePath})` : ''}`);
3314
+ if (c.hint)
3315
+ issues.push(` Fix: ${c.hint}`);
3231
3316
  }
3232
3317
  }
3233
3318
  if (s.componentsWithErrors > 0) {
@@ -3241,7 +3326,15 @@ function printAuditGateFailures(data) {
3241
3326
  issues.push(`${s.functionsMissing} function(s) missing test files`);
3242
3327
  const missing = (data.functions || []).filter((f) => f.status === 'missing');
3243
3328
  for (const f of missing) {
3244
- issues.push(` → ${f.name}${f.filePath ? ` (${f.filePath})` : ''}`);
3329
+ if (f.testFile) {
3330
+ issues.push(` → ${f.name} — test file missing: ${f.testFile}`);
3331
+ }
3332
+ else {
3333
+ issues.push(` → ${f.name} (${f.filePath}) — no testFile in glossary`);
3334
+ if (f.suggestedTestFile) {
3335
+ issues.push(` Fix: Create ${f.suggestedTestFile} or add "testFile": "${f.suggestedTestFile}" to .codeyam/glossary.json`);
3336
+ }
3337
+ }
3245
3338
  }
3246
3339
  }
3247
3340
  if (s.functionsFailing > 0) {
@@ -3330,22 +3423,16 @@ async function handleAudit() {
3330
3423
  console.error(chalk.red('Error: Could not reach the CodeYam server. Is it running?'));
3331
3424
  process.exit(1);
3332
3425
  }
3333
- // Auto-fix incomplete entities and unassociated scenarios but only once.
3334
- // If analyze-imports + entity SHA sync runs and issues persist, report clearly
3335
- // instead of retrying. The analysis may have errors (e.g., stale entity
3336
- // references from refactoring) that can't be fixed by re-running.
3426
+ // Lightweight auto-fix: backfill entity_sha on scenarios that were
3427
+ // registered before entity records existed. This is fast (DB-only).
3428
+ // We do NOT run handleAnalyzeImports here it scans all files and takes
3429
+ // minutes on large projects. Users should run it separately if needed.
3337
3430
  const incompleteBeforeFix = data.incompleteEntities || [];
3338
3431
  const unassociatedBeforeFix = data.unassociatedScenarios || [];
3339
3432
  let autoRemediationFailed = false;
3340
3433
  if (incompleteBeforeFix.length > 0 || unassociatedBeforeFix.length > 0) {
3341
3434
  const issueCount = incompleteBeforeFix.length + unassociatedBeforeFix.length;
3342
- console.log(chalk.dim(`Auto-fixing ${issueCount} entity association issue${issueCount !== 1 ? 's' : ''}...`));
3343
- try {
3344
- await handleAnalyzeImports({ silent: true });
3345
- }
3346
- catch {
3347
- // Fall through — the audit will still report them
3348
- }
3435
+ console.log(chalk.dim(`Backfilling ${issueCount} entity association issue${issueCount !== 1 ? 's' : ''}...`));
3349
3436
  // Backfill entity_sha on scenarios that were registered before entities existed
3350
3437
  try {
3351
3438
  const entities = await loadEntities({});
@@ -3364,13 +3451,13 @@ async function handleAudit() {
3364
3451
  catch {
3365
3452
  // Fall through — re-fetch will show remaining issues
3366
3453
  }
3367
- // Re-fetch audit results after the fix
3454
+ // Re-fetch audit results after the backfill
3368
3455
  data = await fetchAuditResult();
3369
3456
  if (!data) {
3370
- console.error(chalk.red('Error: Could not reach the CodeYam server after auto-fix.'));
3457
+ console.error(chalk.red('Error: Could not reach the CodeYam server after backfill.'));
3371
3458
  process.exit(1);
3372
3459
  }
3373
- // If issues persist after auto-fix, flag it so we show clear guidance
3460
+ // If issues persist after backfill, flag it so we show clear guidance
3374
3461
  const incompleteAfterFix = data.incompleteEntities || [];
3375
3462
  const unassociatedAfterFix = data.unassociatedScenarios || [];
3376
3463
  if (incompleteAfterFix.length > 0 || unassociatedAfterFix.length > 0) {
@@ -3407,6 +3494,9 @@ async function handleAudit() {
3407
3494
  }
3408
3495
  else {
3409
3496
  detail = chalk.red(' — no scenarios registered');
3497
+ if (c.hint) {
3498
+ detail += `\n ${chalk.yellow('Fix: ' + c.hint)}`;
3499
+ }
3410
3500
  }
3411
3501
  // Show file path for failing components always, for OK only when name is ambiguous
3412
3502
  const isDuplicate = (componentNameCounts.get(c.name) || 0) > 1;
@@ -3450,9 +3540,16 @@ async function handleAudit() {
3450
3540
  break;
3451
3541
  case 'missing':
3452
3542
  default:
3453
- detail = f.testFile
3454
- ? chalk.red(` — test file missing: ${f.testFile}`)
3455
- : chalk.red(' — no test file specified');
3543
+ if (f.testFile) {
3544
+ detail = chalk.red(` — test file missing: ${f.testFile}`);
3545
+ }
3546
+ else {
3547
+ detail = chalk.red(` — no test file specified in glossary`);
3548
+ detail += chalk.dim(` (source: ${f.filePath})`);
3549
+ if (f.suggestedTestFile) {
3550
+ detail += `\n ${chalk.yellow(`Fix: Either create ${f.suggestedTestFile} or add "testFile": "${f.suggestedTestFile}" to this entry in .codeyam/glossary.json`)}`;
3551
+ }
3552
+ }
3456
3553
  break;
3457
3554
  }
3458
3555
  console.log(` ${icon} ${f.name}${detail}`);
@@ -3469,19 +3566,48 @@ async function handleAudit() {
3469
3566
  console.log(chalk.yellow(' Add these to .codeyam/glossary.json and run `codeyam editor analyze-imports`'));
3470
3567
  console.log();
3471
3568
  }
3472
- // Incomplete entities (scenarios without analyses)
3473
- const incompleteEntities = data.incompleteEntities || [];
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 || [];
3474
3572
  if (incompleteEntities.length > 0) {
3475
- console.log(chalk.bold('Incomplete entities (need import analysis):'));
3476
- for (const e of incompleteEntities) {
3477
- console.log(` ${chalk.red('✗')} ${e.name} ${e.scenarioCount} scenario${e.scenarioCount !== 1 ? 's' : ''} but entity not analyzed`);
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
3598
+ }
3599
+ }
3478
3600
  }
3479
- if (autoRemediationFailed) {
3480
- console.log(chalk.yellow(' analyze-imports ran automatically but could not resolve these.'));
3481
- console.log(chalk.yellow(' Run `codeyam editor analyze-imports` to see the full error output.'));
3601
+ catch {
3602
+ // DB not available fall through to reporting
3482
3603
  }
3483
- else {
3484
- console.log(chalk.yellow(' Run `codeyam editor analyze-imports` to analyze these entities.'));
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}`);
3485
3611
  }
3486
3612
  console.log();
3487
3613
  }