@codeyam/codeyam-cli 0.1.22 → 0.1.23

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 (52) 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/astScopes/methodSemantics.ts +135 -0
  4. package/analyzer-template/packages/ai/src/lib/astScopes/nodeToSource.ts +19 -0
  5. package/analyzer-template/packages/ai/src/lib/astScopes/paths.ts +11 -4
  6. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateDataStructure.ts +5 -1
  7. package/analyzer-template/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.ts +1632 -1554
  8. package/codeyam-cli/src/commands/__tests__/editor.analyzeImportsArgs.test.js +47 -0
  9. package/codeyam-cli/src/commands/__tests__/editor.analyzeImportsArgs.test.js.map +1 -0
  10. package/codeyam-cli/src/commands/__tests__/editor.auditNoAutoAnalysis.test.js +17 -9
  11. package/codeyam-cli/src/commands/__tests__/editor.auditNoAutoAnalysis.test.js.map +1 -1
  12. package/codeyam-cli/src/commands/editor.js +135 -18
  13. package/codeyam-cli/src/commands/editor.js.map +1 -1
  14. package/codeyam-cli/src/commands/editorAnalyzeImportsArgs.js +23 -0
  15. package/codeyam-cli/src/commands/editorAnalyzeImportsArgs.js.map +1 -0
  16. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +103 -1
  17. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
  18. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +140 -1
  19. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
  20. package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js +50 -1
  21. package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js.map +1 -1
  22. package/codeyam-cli/src/utils/editorAudit.js +38 -2
  23. package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
  24. package/codeyam-cli/src/utils/editorScenarios.js +60 -0
  25. package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
  26. package/codeyam-cli/src/utils/editorSeedAdapter.js +42 -2
  27. package/codeyam-cli/src/utils/editorSeedAdapter.js.map +1 -1
  28. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js +30 -11
  29. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js.map +1 -1
  30. package/codeyam-cli/src/webserver/build/client/assets/{editor.entity.(_sha)-aIHKLB-m.js → editor.entity.(_sha)-DMv5ESGo.js} +18 -18
  31. package/codeyam-cli/src/webserver/build/client/assets/{manifest-bcbb3d49.js → manifest-1a45e154.js} +1 -1
  32. package/codeyam-cli/src/webserver/build/server/assets/{analysisRunner-DjF-soOH.js → analysisRunner-By5slFjw.js} +1 -1
  33. package/codeyam-cli/src/webserver/build/server/assets/{index-nAvHGWbz.js → index-DXaOwBnm.js} +1 -1
  34. package/codeyam-cli/src/webserver/build/server/assets/{init-XhpIt-OT.js → init-CLG1LjQM.js} +1 -1
  35. package/codeyam-cli/src/webserver/build/server/assets/{server-build-DVwiibFu.js → server-build-NZmUqQv6.js} +155 -111
  36. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  37. package/codeyam-cli/src/webserver/build-info.json +5 -5
  38. package/codeyam-cli/src/webserver/editorProxy.js +55 -3
  39. package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
  40. package/codeyam-cli/templates/codeyam-editor-reference.md +8 -6
  41. package/codeyam-cli/templates/nextjs-prisma-sqlite/seed-adapter.ts +42 -34
  42. package/package.json +1 -1
  43. package/packages/ai/src/lib/astScopes/methodSemantics.js +99 -0
  44. package/packages/ai/src/lib/astScopes/methodSemantics.js.map +1 -1
  45. package/packages/ai/src/lib/astScopes/nodeToSource.js +16 -0
  46. package/packages/ai/src/lib/astScopes/nodeToSource.js.map +1 -1
  47. package/packages/ai/src/lib/astScopes/paths.js +12 -3
  48. package/packages/ai/src/lib/astScopes/paths.js.map +1 -1
  49. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js +5 -1
  50. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js.map +1 -1
  51. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js +1330 -1270
  52. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js.map +1 -1
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Tests that `codeyam editor analyze-imports` correctly parses optional
3
+ * file path arguments to scope analysis to specific files.
4
+ *
5
+ * Without file paths, analyze-imports analyzes all glossary entities.
6
+ * With file paths, it scopes analysis to only those files.
7
+ */
8
+ import { parseAnalyzeImportsArgs } from "../editorAnalyzeImportsArgs.js";
9
+ describe('parseAnalyzeImportsArgs', () => {
10
+ it('should return empty array when no args provided', () => {
11
+ expect(parseAnalyzeImportsArgs(undefined, [])).toEqual([]);
12
+ expect(parseAnalyzeImportsArgs('', [])).toEqual([]);
13
+ });
14
+ it('should parse a single file path from json arg', () => {
15
+ // codeyam editor analyze-imports app/components/Foo.tsx
16
+ expect(parseAnalyzeImportsArgs('app/components/Foo.tsx', [])).toEqual([
17
+ 'app/components/Foo.tsx',
18
+ ]);
19
+ });
20
+ it('should collect extra positional args from argv._', () => {
21
+ // codeyam editor analyze-imports app/components/Foo.tsx app/components/Bar.tsx
22
+ // yargs: json="app/components/Foo.tsx", argv._=["editor", "app/components/Bar.tsx"]
23
+ expect(parseAnalyzeImportsArgs('app/components/Foo.tsx', [
24
+ 'editor',
25
+ 'app/components/Bar.tsx',
26
+ ])).toEqual(['app/components/Foo.tsx', 'app/components/Bar.tsx']);
27
+ });
28
+ it('should filter out "editor" from argv._', () => {
29
+ expect(parseAnalyzeImportsArgs(undefined, [
30
+ 'editor',
31
+ 'app/components/Foo.tsx',
32
+ 'app/lib/utils.ts',
33
+ ])).toEqual(['app/components/Foo.tsx', 'app/lib/utils.ts']);
34
+ });
35
+ it('should deduplicate file paths', () => {
36
+ expect(parseAnalyzeImportsArgs('app/components/Foo.tsx', [
37
+ 'editor',
38
+ 'app/components/Foo.tsx',
39
+ 'app/components/Bar.tsx',
40
+ ])).toEqual(['app/components/Foo.tsx', 'app/components/Bar.tsx']);
41
+ });
42
+ it('should handle numeric extras gracefully', () => {
43
+ // yargs can put numbers in argv._
44
+ expect(parseAnalyzeImportsArgs('app/components/Foo.tsx', [42, 'editor'])).toEqual(['app/components/Foo.tsx']);
45
+ });
46
+ });
47
+ //# sourceMappingURL=editor.analyzeImportsArgs.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editor.analyzeImportsArgs.test.js","sourceRoot":"","sources":["../../../../../src/commands/__tests__/editor.analyzeImportsArgs.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAEtE,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACvC,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,CAAC,uBAAuB,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC3D,MAAM,CAAC,uBAAuB,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,wDAAwD;QACxD,MAAM,CAAC,uBAAuB,CAAC,wBAAwB,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;YACpE,wBAAwB;SACzB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,+EAA+E;QAC/E,oFAAoF;QACpF,MAAM,CACJ,uBAAuB,CAAC,wBAAwB,EAAE;YAChD,QAAQ;YACR,wBAAwB;SACzB,CAAC,CACH,CAAC,OAAO,CAAC,CAAC,wBAAwB,EAAE,wBAAwB,CAAC,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,CACJ,uBAAuB,CAAC,SAAS,EAAE;YACjC,QAAQ;YACR,wBAAwB;YACxB,kBAAkB;SACnB,CAAC,CACH,CAAC,OAAO,CAAC,CAAC,wBAAwB,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,CACJ,uBAAuB,CAAC,wBAAwB,EAAE;YAChD,QAAQ;YACR,wBAAwB;YACxB,wBAAwB;SACzB,CAAC,CACH,CAAC,OAAO,CAAC,CAAC,wBAAwB,EAAE,wBAAwB,CAAC,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,kCAAkC;QAClC,MAAM,CACJ,uBAAuB,CAAC,wBAAwB,EAAE,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,CAClE,CAAC,OAAO,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -3,11 +3,12 @@ import * as path from 'path';
3
3
  /**
4
4
  * Structural test: handleAudit must NOT call runAnalysisForEntities.
5
5
  *
6
- * Previously, handleAudit auto-ran targeted analysis for incomplete entities,
7
- * which was slow (analyzer startup + AST scan) and contradicted the design
8
- * intent (comment at line ~6255 says "We do NOT run handleAnalyzeImports here").
6
+ * Previously, handleAudit auto-ran full analysis for incomplete entities,
7
+ * which was slow (analyzer startup + AST scan for all glossary entries).
9
8
  *
10
- * This test ensures the auto-analysis doesn't creep back in.
9
+ * Targeted analysis (with filePaths for 1-3 specific files) is acceptable,
10
+ * but a full scan (without filePaths) must never be used here.
11
+ * This test ensures the full auto-analysis doesn't creep back in.
11
12
  */
12
13
  describe('handleAudit - no auto-analysis', () => {
13
14
  it('should not call runAnalysisForEntities inside handleAudit', () => {
@@ -43,7 +44,7 @@ describe('handleAudit - no auto-analysis', () => {
43
44
  expect(handleAuditBody).toContain('incompleteEntities');
44
45
  expect(handleAuditBody).toContain('formatIncompleteEntityGuidance');
45
46
  });
46
- it('should not call handleAnalyzeImports inside handleAudit', () => {
47
+ it('should only call handleAnalyzeImports with filePaths (targeted, not full scan)', () => {
47
48
  const editorTsPath = path.join(__dirname, '..', 'editor.ts');
48
49
  const source = fs.readFileSync(editorTsPath, 'utf8');
49
50
  const handleAuditStart = source.indexOf('async function handleAudit');
@@ -54,10 +55,17 @@ describe('handleAudit - no auto-analysis', () => {
54
55
  ? handleAuditStart + 1 + nextFnMatch.index
55
56
  : source.length;
56
57
  const handleAuditBody = source.slice(handleAuditStart, handleAuditEnd);
57
- // handleAnalyzeImports calls runAnalysisForEntities internally
58
- // it takes minutes on large projects and contradicts the audit's
59
- // read-only design intent.
60
- expect(handleAuditBody).not.toContain('handleAnalyzeImports');
58
+ // Targeted handleAnalyzeImports (with filePaths) is OK it only
59
+ // analyzes 1-3 specific files. A full scan (without filePaths) takes
60
+ // minutes on large projects and must never be used here.
61
+ const calls = [...handleAuditBody.matchAll(/handleAnalyzeImports\s*\(/g)];
62
+ for (const call of calls) {
63
+ const afterCall = handleAuditBody.slice(call.index + call[0].length);
64
+ // Each call must include filePaths in its arguments
65
+ const closingParen = afterCall.indexOf(')');
66
+ const callArgs = afterCall.slice(0, closingParen);
67
+ expect(callArgs).toContain('filePaths');
68
+ }
61
69
  });
62
70
  });
63
71
  //# sourceMappingURL=editor.auditNoAutoAnalysis.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"editor.auditNoAutoAnalysis.test.js","sourceRoot":"","sources":["../../../../../src/commands/__tests__/editor.auditNoAutoAnalysis.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"editor.auditNoAutoAnalysis.test.js","sourceRoot":"","sources":["../../../../../src/commands/__tests__/editor.auditNoAutoAnalysis.test.ts"],"names":[],"mappings":"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"}
@@ -1978,7 +1978,7 @@ function printMigrateStep8(root) {
1978
1978
  printComponentCaptureInstructions();
1979
1979
  console.log();
1980
1980
  console.log(chalk.bold('Verify:'));
1981
- checkbox('Run `codeyam editor analyze-imports` to populate import graph');
1981
+ checkbox('Run `codeyam editor analyze-imports` to populate import graph (or `codeyam editor analyze-imports path/to/file.tsx ...` for specific files)');
1982
1982
  checkbox('Run library function tests: `npx jest --testPathPattern="<relevant>" --maxWorkers=2`');
1983
1983
  checkbox('Run `codeyam editor audit` to check completeness');
1984
1984
  checkbox('Check for client errors: `codeyam editor client-errors`');
@@ -2356,10 +2356,14 @@ async function handleAnalyzeImports(options = {}) {
2356
2356
  glossaryEntries = sanitizeGlossaryEntries(parsed);
2357
2357
  }
2358
2358
  catch {
2359
+ if (options.silent)
2360
+ return;
2359
2361
  console.error(chalk.red('Error: Could not parse .codeyam/glossary.json.'));
2360
2362
  process.exit(1);
2361
2363
  }
2362
2364
  if (glossaryEntries.length === 0) {
2365
+ if (options.silent)
2366
+ return;
2363
2367
  console.error(chalk.red('Error: glossary.json is empty.'));
2364
2368
  process.exit(1);
2365
2369
  }
@@ -2428,6 +2432,15 @@ async function handleAnalyzeImports(options = {}) {
2428
2432
  catch {
2429
2433
  // Non-fatal — fall back to analyzing all files
2430
2434
  }
2435
+ // If specific file paths were requested, scope analysis to only those files.
2436
+ // The full filePaths set is still used for the import graph below.
2437
+ if (options.filePaths && options.filePaths.length > 0) {
2438
+ const requested = new Set(options.filePaths);
2439
+ targetFilePaths = targetFilePaths.filter((fp) => requested.has(fp));
2440
+ if (targetFilePaths.length === 0 && !options.silent) {
2441
+ console.log(chalk.dim('Requested file(s) already analyzed or not in glossary — skipping analysis.'));
2442
+ }
2443
+ }
2431
2444
  // Run data-structure-only analysis for entities that need it.
2432
2445
  // Don't pass entityNames — entities may not exist yet (fresh clone).
2433
2446
  // The analyzer will discover and create them from file paths.
@@ -2444,6 +2457,10 @@ async function handleAnalyzeImports(options = {}) {
2444
2457
  catch (err) {
2445
2458
  progress.fail('Analysis failed');
2446
2459
  const msg = err instanceof Error ? err.message : String(err);
2460
+ if (options.silent) {
2461
+ // Internal caller — don't kill the process, let the caller handle it
2462
+ return;
2463
+ }
2447
2464
  console.error(chalk.red(`Error: ${msg}`));
2448
2465
  process.exit(1);
2449
2466
  }
@@ -3454,7 +3471,15 @@ function printAuditGateFailures(data) {
3454
3471
  for (const m of missingGlossary) {
3455
3472
  issues.push(` → ${m.name} (${m.filePath})`);
3456
3473
  }
3457
- issues.push(` Fix: Run \`codeyam editor analyze-imports\` to add these files to the glossary`);
3474
+ const missingPaths = missingGlossary
3475
+ .map((m) => m.filePath)
3476
+ .filter(Boolean);
3477
+ if (missingPaths.length > 0) {
3478
+ issues.push(` Fix: Run \`codeyam editor analyze-imports ${missingPaths.join(' ')}\``);
3479
+ }
3480
+ else {
3481
+ issues.push(` Fix: Run \`codeyam editor analyze-imports\` to add these files to the glossary`);
3482
+ }
3458
3483
  }
3459
3484
  if (s.incompleteEntities > 0) {
3460
3485
  // Check for persistent analysis failures
@@ -3498,8 +3523,16 @@ function printAuditGateFailures(data) {
3498
3523
  }
3499
3524
  }
3500
3525
  if (s.unassociatedScenarios > 0) {
3501
- issues.push(`${s.unassociatedScenarios} component(s) have scenarios with no entity link — run \`codeyam editor analyze-imports\` then re-run audit`);
3502
3526
  const unassociated = data.unassociatedScenarios || [];
3527
+ const unassocPaths = unassociated
3528
+ .map((u) => u.filePath)
3529
+ .filter(Boolean);
3530
+ if (unassocPaths.length > 0) {
3531
+ issues.push(`${s.unassociatedScenarios} component(s) have scenarios with no entity link — run \`codeyam editor analyze-imports ${unassocPaths.join(' ')}\` then re-run audit`);
3532
+ }
3533
+ else {
3534
+ issues.push(`${s.unassociatedScenarios} component(s) have scenarios with no entity link — run \`codeyam editor analyze-imports\` then re-run audit`);
3535
+ }
3503
3536
  for (const u of unassociated) {
3504
3537
  issues.push(` → ${u.name} (${u.filePath}) — ${u.scenarioCount} scenario${u.scenarioCount !== 1 ? 's' : ''}`);
3505
3538
  }
@@ -3554,16 +3587,16 @@ function printAuditGateFailures(data) {
3554
3587
  * which glossary components have registered scenarios and which functions
3555
3588
  * have test files. Exits with code 1 if anything is missing.
3556
3589
  */
3557
- async function handleAudit() {
3590
+ async function handleAudit(options) {
3558
3591
  let data = await fetchAuditResult();
3559
3592
  if (!data) {
3560
3593
  console.error(chalk.red('Error: Could not reach the CodeYam server. Is it running?'));
3561
3594
  process.exit(1);
3562
3595
  }
3563
- // Lightweight auto-fix: backfill entity_sha on scenarios that were
3564
- // registered before entity records existed. This is fast (DB-only).
3565
- // Import analysis is NOT triggered here it scans all files and takes
3566
- // minutes on large projects. Users should run it separately if needed.
3596
+ // Two-phase auto-fix for entity associations:
3597
+ // Phase 1: DB-only backfill fast, matches existing entities to scenarios.
3598
+ // Phase 2: If backfill fails, targeted analyze-imports for only the
3599
+ // specific unresolved files (1-3 files, not the full glossary scan).
3567
3600
  const incompleteBeforeFix = data.incompleteEntities || [];
3568
3601
  const unassociatedBeforeFix = data.unassociatedScenarios || [];
3569
3602
  let autoRemediationFailed = false;
@@ -3595,14 +3628,58 @@ async function handleAudit() {
3595
3628
  console.error(chalk.red('Error: Could not reach the CodeYam server after backfill.'));
3596
3629
  process.exit(1);
3597
3630
  }
3598
- // If issues persist after backfill, flag it so we show clear guidance
3631
+ // If issues persist after DB-only backfill, run targeted analyze-imports
3632
+ // for ONLY the specific files that need entities. This is fast (1-3 files)
3633
+ // unlike full analyze-imports which scans all glossary entries.
3634
+ const incompleteAfterBackfill = data.incompleteEntities || [];
3635
+ const unassociatedAfterBackfill = data.unassociatedScenarios || [];
3636
+ if (incompleteAfterBackfill.length > 0 ||
3637
+ unassociatedAfterBackfill.length > 0) {
3638
+ const { determineTargetedAnalysisPaths } = await import('../utils/editorAudit.js');
3639
+ const targetPaths = determineTargetedAnalysisPaths({
3640
+ unassociatedScenarios: unassociatedAfterBackfill,
3641
+ incompleteEntities: incompleteAfterBackfill,
3642
+ });
3643
+ if (targetPaths.length > 0) {
3644
+ console.log(chalk.dim(`Running targeted analysis for ${targetPaths.length} file${targetPaths.length !== 1 ? 's' : ''}...`));
3645
+ try {
3646
+ await handleAnalyzeImports({
3647
+ silent: true,
3648
+ filePaths: targetPaths,
3649
+ });
3650
+ // Retry backfill after analysis created entities
3651
+ const entities = await loadEntities({});
3652
+ if (entities && entities.length > 0) {
3653
+ const { getDatabase: getDb } = await import('../../../packages/database/index.js');
3654
+ const freshDb = getDb();
3655
+ await backfillEntityShaOnScenarios(freshDb, entities.map((e) => ({
3656
+ sha: e.sha,
3657
+ name: e.name,
3658
+ filePath: e.filePath || '',
3659
+ isDefaultExport: e.metadata?.notExported === false &&
3660
+ e.metadata?.namedExport === false,
3661
+ })));
3662
+ }
3663
+ // Re-fetch audit results after targeted analysis
3664
+ data = await fetchAuditResult({ skipTests: true });
3665
+ if (!data) {
3666
+ console.error(chalk.red('Error: Could not reach the CodeYam server after analysis.'));
3667
+ process.exit(1);
3668
+ }
3669
+ }
3670
+ catch {
3671
+ // Targeted analysis failed — fall through to show remaining issues
3672
+ }
3673
+ }
3674
+ }
3675
+ // Check if issues persist after all remediation attempts
3599
3676
  const incompleteAfterFix = data.incompleteEntities || [];
3600
3677
  const unassociatedAfterFix = data.unassociatedScenarios || [];
3601
3678
  if (incompleteAfterFix.length > 0 || unassociatedAfterFix.length > 0) {
3602
3679
  autoRemediationFailed = true;
3603
3680
  }
3604
3681
  }
3605
- const { components, functions, summary } = data;
3682
+ let { components, functions, summary } = data;
3606
3683
  console.log();
3607
3684
  console.log(chalk.bold.cyan('━━━ Editor Audit ━━━'));
3608
3685
  console.log();
@@ -3701,7 +3778,15 @@ async function handleAudit() {
3701
3778
  for (const m of missingFromGlossary) {
3702
3779
  console.log(` ${chalk.red('✗')} ${m.name} ${chalk.dim(`(${m.filePath})`)} — ${m.scenarioCount} scenario${m.scenarioCount !== 1 ? 's' : ''} but not in glossary`);
3703
3780
  }
3704
- console.log(chalk.yellow(' Add these to .codeyam/glossary.json and run `codeyam editor analyze-imports`'));
3781
+ const mgPaths = missingFromGlossary
3782
+ .map((m) => m.filePath)
3783
+ .filter(Boolean);
3784
+ if (mgPaths.length > 0) {
3785
+ console.log(chalk.yellow(` Add these to .codeyam/glossary.json and run \`codeyam editor analyze-imports ${mgPaths.join(' ')}\``));
3786
+ }
3787
+ else {
3788
+ console.log(chalk.yellow(' Add these to .codeyam/glossary.json and run `codeyam editor analyze-imports`'));
3789
+ }
3705
3790
  console.log();
3706
3791
  }
3707
3792
  // Incomplete entities (scenarios without analyses) — report with guidance.
@@ -3756,12 +3841,18 @@ async function handleAudit() {
3756
3841
  console.log(chalk.dim(` … and ${u.scenarioNames.length - 3} more`));
3757
3842
  }
3758
3843
  }
3844
+ const uaPaths = unassociatedScenarios
3845
+ .map((u) => u.filePath)
3846
+ .filter(Boolean);
3847
+ const uaCmd = uaPaths.length > 0
3848
+ ? `codeyam editor analyze-imports ${uaPaths.join(' ')}`
3849
+ : 'codeyam editor analyze-imports';
3759
3850
  if (autoRemediationFailed) {
3760
3851
  console.log(chalk.yellow(' analyze-imports ran automatically but could not resolve these.'));
3761
- console.log(chalk.yellow(' Run `codeyam editor analyze-imports` to see the full error output.'));
3852
+ console.log(chalk.yellow(` Run \`${uaCmd}\` to see the full error output.`));
3762
3853
  }
3763
3854
  else {
3764
- console.log(chalk.yellow(' Run `codeyam editor analyze-imports` to create entity records, then re-run audit to backfill.'));
3855
+ console.log(chalk.yellow(` Run \`${uaCmd}\` to create entity records, then re-run audit to backfill.`));
3765
3856
  }
3766
3857
  const unassocErrorReportPath = path.join(getProjectRoot(), '.codeyam', 'analysis-errors.txt');
3767
3858
  if (fs.existsSync(unassocErrorReportPath)) {
@@ -3794,7 +3885,26 @@ async function handleAudit() {
3794
3885
  : `${s.status.status}`;
3795
3886
  console.log(` ${chalk.red('✗')} ${s.scenarioName} ${chalk.dim(`(${s.entityName} — ${reason})`)}`);
3796
3887
  }
3797
- console.log(chalk.yellow(' Run: codeyam editor recapture-stale'));
3888
+ if (options?.fix) {
3889
+ // --fix: auto-recapture stale scenarios instead of just reporting them
3890
+ const { shouldAutoRecapture } = await import('../utils/editorAudit.js');
3891
+ if (shouldAutoRecapture({ fix: true, scenariosNeedingRecapture })) {
3892
+ console.log(chalk.cyan(' --fix: auto-recapturing stale scenarios...'));
3893
+ console.log();
3894
+ await handleRecaptureStale();
3895
+ // Re-fetch audit results so the summary and exit code reflect
3896
+ // the post-fix state, not the pre-fix state.
3897
+ const refreshed = await fetchAuditResult({ skipTests: true });
3898
+ if (refreshed) {
3899
+ data = refreshed;
3900
+ ({ components, functions, summary } = data);
3901
+ }
3902
+ }
3903
+ }
3904
+ else {
3905
+ console.log(chalk.yellow(' Run: codeyam editor recapture-stale'));
3906
+ console.log(chalk.dim(' Or: codeyam editor audit --fix (to auto-recapture)'));
3907
+ }
3798
3908
  console.log();
3799
3909
  }
3800
3910
  // Duplicate glossary names (warning, not a failure)
@@ -4796,6 +4906,11 @@ const editorCommand = {
4796
4906
  alias: 'p',
4797
4907
  describe: 'Port to run the web server on',
4798
4908
  default: 3111,
4909
+ })
4910
+ .option('fix', {
4911
+ type: 'boolean',
4912
+ describe: 'For audit: also recapture stale scenarios after displaying results',
4913
+ default: false,
4799
4914
  });
4800
4915
  if (IS_INTERNAL_BUILD) {
4801
4916
  builder = builder
@@ -4923,9 +5038,11 @@ const editorCommand = {
4923
5038
  fs.writeFileSync(trackingPath, JSON.stringify({ step: state.step, taskCreated: true }, null, 2), 'utf8');
4924
5039
  return;
4925
5040
  }
4926
- // Subcommand: codeyam editor analyze-imports
5041
+ // Subcommand: codeyam editor analyze-imports [file1.tsx file2.tsx ...]
4927
5042
  if (argv.step === 'analyze-imports') {
4928
- await handleAnalyzeImports();
5043
+ const { parseAnalyzeImportsArgs } = await import('./editorAnalyzeImportsArgs.js');
5044
+ const requestedPaths = parseAnalyzeImportsArgs(argv.json, argv._);
5045
+ await handleAnalyzeImports(requestedPaths.length > 0 ? { filePaths: requestedPaths } : {});
4929
5046
  return;
4930
5047
  }
4931
5048
  // Subcommand: codeyam editor manual-entity <JSON|@file>
@@ -4938,9 +5055,9 @@ const editorCommand = {
4938
5055
  await handleDependents(argv.json || '');
4939
5056
  return;
4940
5057
  }
4941
- // Subcommand: codeyam editor audit
5058
+ // Subcommand: codeyam editor audit [--fix]
4942
5059
  if (argv.step === 'audit') {
4943
- await handleAudit();
5060
+ await handleAudit({ fix: argv.fix || false });
4944
5061
  return;
4945
5062
  }
4946
5063
  // Subcommand: codeyam editor scenarios