@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.
- package/analyzer-template/.build-info.json +7 -7
- package/analyzer-template/log.txt +3 -3
- package/analyzer-template/packages/ai/src/lib/astScopes/methodSemantics.ts +135 -0
- package/analyzer-template/packages/ai/src/lib/astScopes/nodeToSource.ts +19 -0
- package/analyzer-template/packages/ai/src/lib/astScopes/paths.ts +11 -4
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateDataStructure.ts +5 -1
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.ts +1632 -1554
- package/codeyam-cli/src/commands/__tests__/editor.analyzeImportsArgs.test.js +47 -0
- package/codeyam-cli/src/commands/__tests__/editor.analyzeImportsArgs.test.js.map +1 -0
- package/codeyam-cli/src/commands/__tests__/editor.auditNoAutoAnalysis.test.js +17 -9
- package/codeyam-cli/src/commands/__tests__/editor.auditNoAutoAnalysis.test.js.map +1 -1
- package/codeyam-cli/src/commands/editor.js +135 -18
- package/codeyam-cli/src/commands/editor.js.map +1 -1
- package/codeyam-cli/src/commands/editorAnalyzeImportsArgs.js +23 -0
- package/codeyam-cli/src/commands/editorAnalyzeImportsArgs.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +103 -1
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +140 -1
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js +50 -1
- package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js.map +1 -1
- package/codeyam-cli/src/utils/editorAudit.js +38 -2
- package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
- package/codeyam-cli/src/utils/editorScenarios.js +60 -0
- package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
- package/codeyam-cli/src/utils/editorSeedAdapter.js +42 -2
- package/codeyam-cli/src/utils/editorSeedAdapter.js.map +1 -1
- package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js +30 -11
- package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js.map +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{editor.entity.(_sha)-aIHKLB-m.js → editor.entity.(_sha)-DMv5ESGo.js} +18 -18
- package/codeyam-cli/src/webserver/build/client/assets/{manifest-bcbb3d49.js → manifest-1a45e154.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/{analysisRunner-DjF-soOH.js → analysisRunner-By5slFjw.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/{index-nAvHGWbz.js → index-DXaOwBnm.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/{init-XhpIt-OT.js → init-CLG1LjQM.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/{server-build-DVwiibFu.js → server-build-NZmUqQv6.js} +155 -111
- package/codeyam-cli/src/webserver/build/server/index.js +1 -1
- package/codeyam-cli/src/webserver/build-info.json +5 -5
- package/codeyam-cli/src/webserver/editorProxy.js +55 -3
- package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
- package/codeyam-cli/templates/codeyam-editor-reference.md +8 -6
- package/codeyam-cli/templates/nextjs-prisma-sqlite/seed-adapter.ts +42 -34
- package/package.json +1 -1
- package/packages/ai/src/lib/astScopes/methodSemantics.js +99 -0
- package/packages/ai/src/lib/astScopes/methodSemantics.js.map +1 -1
- package/packages/ai/src/lib/astScopes/nodeToSource.js +16 -0
- package/packages/ai/src/lib/astScopes/nodeToSource.js.map +1 -1
- package/packages/ai/src/lib/astScopes/paths.js +12 -3
- package/packages/ai/src/lib/astScopes/paths.js.map +1 -1
- package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js +5 -1
- package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js.map +1 -1
- package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js +1330 -1270
- 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
|
|
7
|
-
* which was slow (analyzer startup + AST scan
|
|
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
|
-
*
|
|
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
|
|
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
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
|
|
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":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B
|
|
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
|
-
|
|
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
|
-
//
|
|
3564
|
-
//
|
|
3565
|
-
//
|
|
3566
|
-
//
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
3852
|
+
console.log(chalk.yellow(` Run \`${uaCmd}\` to see the full error output.`));
|
|
3762
3853
|
}
|
|
3763
3854
|
else {
|
|
3764
|
-
console.log(chalk.yellow(
|
|
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
|
-
|
|
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
|
|
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
|