@aiready/cli 0.14.2 ā 0.14.4
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/.aiready/aiready-report-20260314-164626.json +2 -5
- package/.aiready/aiready-report-20260314-164741.json +2 -5
- package/.turbo/turbo-build.log +29 -28
- package/.turbo/turbo-lint.log +0 -32
- package/.turbo/turbo-test.log +35 -125
- package/aiready-report.json +30703 -0
- package/dist/cli.js +415 -378
- package/dist/cli.mjs +358 -320
- package/package.json +12 -12
- package/packages/core/src/.aiready/aiready-report-20260314-161145.json +4 -10
- package/packages/core/src/.aiready/aiready-report-20260314-161152.json +10 -28
- package/packages/pattern-detect/src/.aiready/aiready-report-20260314-161139.json +4 -10
- package/src/.aiready/aiready-report-20260312-103623.json +3 -9
- package/src/.aiready/aiready-report-20260312-110843.json +3 -9
- package/src/.aiready/aiready-report-20260312-110955.json +3 -9
- package/src/.aiready/aiready-report-20260314-203209.json +3 -9
- package/src/.aiready/aiready-report-20260314-203736.json +3 -9
- package/src/.aiready/aiready-report-20260314-203857.json +3 -9
- package/src/.aiready/aiready-report-20260314-204047.json +3 -9
- package/src/__tests__/cli.test.ts +1 -1
- package/src/__tests__/config-shape.test.ts +0 -1
- package/src/__tests__/unified.test.ts +1 -1
- package/src/cli.ts +2 -1
- package/src/commands/__tests__/consistency.test.ts +3 -0
- package/src/commands/__tests__/extra-commands.test.ts +29 -38
- package/src/commands/__tests__/init.test.ts +56 -0
- package/src/commands/__tests__/scan.test.ts +4 -2
- package/src/commands/__tests__/upload.test.ts +0 -1
- package/src/commands/__tests__/visualize.test.ts +3 -7
- package/src/commands/ai-signal-clarity.ts +1 -56
- package/src/commands/bug.ts +1 -2
- package/src/commands/deps-health.ts +1 -65
- package/src/commands/doc-drift.ts +1 -62
- package/src/commands/init.ts +58 -2
- package/src/commands/patterns.ts +3 -1
- package/src/commands/report-formatter.ts +128 -0
- package/src/commands/scan.ts +29 -120
- package/src/commands/shared/configured-tool-action.ts +35 -0
- package/src/commands/shared/standard-tool-actions.ts +126 -0
- package/src/commands/upload.ts +15 -13
- package/src/commands/visualize.ts +11 -4
- package/src/index.ts +18 -3
- package/src/utils/helpers.ts +86 -37
- package/vitest.config.ts +5 -12
|
@@ -2,7 +2,6 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
|
2
2
|
import { visualizeAction } from '../visualize';
|
|
3
3
|
import * as fs from 'fs';
|
|
4
4
|
import * as core from '@aiready/core';
|
|
5
|
-
import * as helpers from '../../utils/helpers';
|
|
6
5
|
import { spawn } from 'child_process';
|
|
7
6
|
|
|
8
7
|
vi.mock('fs', async () => {
|
|
@@ -29,10 +28,7 @@ vi.mock('@aiready/visualizer/graph', () => ({
|
|
|
29
28
|
vi.mock('@aiready/core', () => ({
|
|
30
29
|
handleCLIError: vi.fn(),
|
|
31
30
|
generateHTML: vi.fn().mockReturnValue('<html></html>'),
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
vi.mock('../../utils/helpers', () => ({
|
|
35
|
-
findLatestScanReport: vi.fn(),
|
|
31
|
+
findLatestReport: vi.fn(),
|
|
36
32
|
}));
|
|
37
33
|
|
|
38
34
|
describe('Visualize CLI Action', () => {
|
|
@@ -54,7 +50,7 @@ describe('Visualize CLI Action', () => {
|
|
|
54
50
|
});
|
|
55
51
|
|
|
56
52
|
it('should find latest report if none specified', async () => {
|
|
57
|
-
vi.
|
|
53
|
+
vi.mocked(core.findLatestReport).mockReturnValue('latest.json');
|
|
58
54
|
await visualizeAction('.', {});
|
|
59
55
|
expect(fs.readFileSync).toHaveBeenCalledWith(
|
|
60
56
|
expect.stringContaining('latest.json'),
|
|
@@ -64,7 +60,7 @@ describe('Visualize CLI Action', () => {
|
|
|
64
60
|
|
|
65
61
|
it('should handle missing reports', async () => {
|
|
66
62
|
vi.spyOn(fs, 'existsSync').mockReturnValue(false);
|
|
67
|
-
vi.
|
|
63
|
+
vi.mocked(core.findLatestReport).mockReturnValue(null);
|
|
68
64
|
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
69
65
|
|
|
70
66
|
await visualizeAction('.', { report: 'missing.json' });
|
|
@@ -1,56 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* AI signal clarity command for unified CLI
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import chalk from 'chalk';
|
|
6
|
-
import { loadConfig, mergeConfigWithDefaults } from '@aiready/core';
|
|
7
|
-
import type { ToolScoringOutput } from '@aiready/core';
|
|
8
|
-
|
|
9
|
-
export async function aiSignalClarityAction(
|
|
10
|
-
directory: string,
|
|
11
|
-
options: any
|
|
12
|
-
): Promise<ToolScoringOutput | undefined> {
|
|
13
|
-
const { analyzeAiSignalClarity, calculateAiSignalClarityScore } =
|
|
14
|
-
await import('@aiready/ai-signal-clarity');
|
|
15
|
-
|
|
16
|
-
const config = await loadConfig(directory);
|
|
17
|
-
const merged = mergeConfigWithDefaults(config, {
|
|
18
|
-
minSeverity: 'info',
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
const report = await analyzeAiSignalClarity({
|
|
22
|
-
rootDir: directory,
|
|
23
|
-
minSeverity: options.minSeverity ?? merged.minSeverity ?? 'info',
|
|
24
|
-
include: options.include,
|
|
25
|
-
exclude: options.exclude,
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
const scoring = calculateAiSignalClarityScore(report);
|
|
29
|
-
|
|
30
|
-
if (options.output === 'json') {
|
|
31
|
-
return scoring;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const { summary } = report;
|
|
35
|
-
const ratingColors: Record<string, (s: string) => string> = {
|
|
36
|
-
minimal: chalk.green,
|
|
37
|
-
low: chalk.cyan,
|
|
38
|
-
moderate: chalk.yellow,
|
|
39
|
-
high: chalk.red,
|
|
40
|
-
severe: chalk.bgRed.white,
|
|
41
|
-
};
|
|
42
|
-
const color = ratingColors[summary.rating] ?? chalk.white;
|
|
43
|
-
console.log(
|
|
44
|
-
` š§ AI Signal Clarity: ${chalk.bold(scoring.score + '/100')} (${color(summary.rating)})`
|
|
45
|
-
);
|
|
46
|
-
console.log(` Top Risk: ${chalk.italic(summary.topRisk)}`);
|
|
47
|
-
if (summary.totalSignals > 0) {
|
|
48
|
-
console.log(
|
|
49
|
-
chalk.dim(
|
|
50
|
-
` ${summary.criticalSignals} critical ${summary.majorSignals} major ${summary.minorSignals} minor signals`
|
|
51
|
-
)
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return scoring;
|
|
56
|
-
}
|
|
1
|
+
export { aiSignalClarityAction } from './shared/standard-tool-actions';
|
package/src/commands/bug.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import { Command } from 'commander';
|
|
3
2
|
|
|
4
3
|
import { execSync } from 'child_process';
|
|
5
4
|
|
|
@@ -38,7 +37,7 @@ Type: ${type}
|
|
|
38
37
|
console.log(chalk.green('ā
Issue Created Successfully!'));
|
|
39
38
|
console.log(chalk.cyan(output));
|
|
40
39
|
return;
|
|
41
|
-
} catch
|
|
40
|
+
} catch {
|
|
42
41
|
console.error(chalk.red('\nā Failed to submit via gh CLI.'));
|
|
43
42
|
console.log(
|
|
44
43
|
chalk.yellow(
|
|
@@ -1,65 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Dependency health command for unified CLI
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import chalk from 'chalk';
|
|
6
|
-
import { loadConfig, mergeConfigWithDefaults } from '@aiready/core';
|
|
7
|
-
import type { ToolScoringOutput } from '@aiready/core';
|
|
8
|
-
|
|
9
|
-
export async function depsHealthAction(
|
|
10
|
-
directory: string,
|
|
11
|
-
options: any
|
|
12
|
-
): Promise<ToolScoringOutput | undefined> {
|
|
13
|
-
const { analyzeDeps } = await import('@aiready/deps');
|
|
14
|
-
|
|
15
|
-
const config = await loadConfig(directory);
|
|
16
|
-
const merged = mergeConfigWithDefaults(config, {
|
|
17
|
-
trainingCutoffYear: 2023,
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
const report = await analyzeDeps({
|
|
21
|
-
rootDir: directory,
|
|
22
|
-
include: options.include,
|
|
23
|
-
exclude: options.exclude,
|
|
24
|
-
trainingCutoffYear:
|
|
25
|
-
options.trainingCutoffYear ?? merged.trainingCutoffYear ?? 2023,
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
const scoring: ToolScoringOutput = {
|
|
29
|
-
toolName: 'dependency-health',
|
|
30
|
-
score: report.summary.score,
|
|
31
|
-
rawMetrics: report.rawData,
|
|
32
|
-
factors: [],
|
|
33
|
-
recommendations: report.recommendations.map((action: string) => ({
|
|
34
|
-
action,
|
|
35
|
-
estimatedImpact: 5,
|
|
36
|
-
priority: 'medium',
|
|
37
|
-
})),
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
if (options.output === 'json') {
|
|
41
|
-
return scoring;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const { summary } = report;
|
|
45
|
-
const ratingColors: Record<string, (s: string) => string> = {
|
|
46
|
-
excellent: chalk.green,
|
|
47
|
-
good: chalk.blueBright,
|
|
48
|
-
moderate: chalk.yellow,
|
|
49
|
-
poor: chalk.red,
|
|
50
|
-
hazardous: chalk.bgRed.white,
|
|
51
|
-
};
|
|
52
|
-
const color = ratingColors[summary.rating] ?? chalk.white;
|
|
53
|
-
console.log(
|
|
54
|
-
` š¦ Dependency Health: ${chalk.bold(scoring.score + '/100 health')} (${color(summary.rating)})`
|
|
55
|
-
);
|
|
56
|
-
if (report.issues.length > 0) {
|
|
57
|
-
console.log(
|
|
58
|
-
chalk.dim(` Found ${report.issues.length} dependency issues.`)
|
|
59
|
-
);
|
|
60
|
-
} else {
|
|
61
|
-
console.log(chalk.dim(` Dependencies look healthy for AI assistance.`));
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return scoring;
|
|
65
|
-
}
|
|
1
|
+
export { depsHealthAction } from './shared/standard-tool-actions';
|
|
@@ -1,62 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Doc drift risk command for unified CLI
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import chalk from 'chalk';
|
|
6
|
-
import { loadConfig, mergeConfigWithDefaults } from '@aiready/core';
|
|
7
|
-
import type { ToolScoringOutput } from '@aiready/core';
|
|
8
|
-
|
|
9
|
-
export async function docDriftAction(
|
|
10
|
-
directory: string,
|
|
11
|
-
options: any
|
|
12
|
-
): Promise<ToolScoringOutput | undefined> {
|
|
13
|
-
const { analyzeDocDrift } = await import('@aiready/doc-drift');
|
|
14
|
-
|
|
15
|
-
const config = await loadConfig(directory);
|
|
16
|
-
const merged = mergeConfigWithDefaults(config, {
|
|
17
|
-
staleMonths: 6,
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
const report = await analyzeDocDrift({
|
|
21
|
-
rootDir: directory,
|
|
22
|
-
include: options.include,
|
|
23
|
-
exclude: options.exclude,
|
|
24
|
-
staleMonths: options.staleMonths ?? merged.staleMonths ?? 6,
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
const scoring: ToolScoringOutput = {
|
|
28
|
-
toolName: 'doc-drift',
|
|
29
|
-
score: report.summary.score,
|
|
30
|
-
rawMetrics: report.rawData,
|
|
31
|
-
factors: [],
|
|
32
|
-
recommendations: report.recommendations.map((action: string) => ({
|
|
33
|
-
action,
|
|
34
|
-
estimatedImpact: 5,
|
|
35
|
-
priority: 'medium',
|
|
36
|
-
})),
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
if (options.output === 'json') {
|
|
40
|
-
return scoring;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const { summary } = report;
|
|
44
|
-
const ratingColors: Record<string, (s: string) => string> = {
|
|
45
|
-
minimal: chalk.green,
|
|
46
|
-
low: chalk.cyan,
|
|
47
|
-
moderate: chalk.yellow,
|
|
48
|
-
high: chalk.red,
|
|
49
|
-
severe: chalk.bgRed.white,
|
|
50
|
-
};
|
|
51
|
-
const color = ratingColors[summary.rating] ?? chalk.white;
|
|
52
|
-
console.log(
|
|
53
|
-
` š Documentation Drift: ${chalk.bold(100 - scoring.score + '/100 health')} (${color(summary.rating)} risk)`
|
|
54
|
-
);
|
|
55
|
-
if (report.issues.length > 0) {
|
|
56
|
-
console.log(chalk.dim(` Found ${report.issues.length} drift issues.`));
|
|
57
|
-
} else {
|
|
58
|
-
console.log(chalk.dim(` No documentation drift detected.`));
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return scoring;
|
|
62
|
-
}
|
|
1
|
+
export { docDriftAction } from './shared/standard-tool-actions';
|
package/src/commands/init.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { ToolName } from '@aiready/core';
|
|
|
6
6
|
export async function initAction(options: {
|
|
7
7
|
force?: boolean;
|
|
8
8
|
format?: 'json' | 'js';
|
|
9
|
+
full?: boolean;
|
|
9
10
|
}) {
|
|
10
11
|
const fileExt = options.format === 'js' ? 'js' : 'json';
|
|
11
12
|
const fileName = fileExt === 'js' ? 'aiready.config.js' : 'aiready.json';
|
|
@@ -18,7 +19,7 @@ export async function initAction(options: {
|
|
|
18
19
|
process.exit(1);
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
const
|
|
22
|
+
const baseConfig = {
|
|
22
23
|
scan: {
|
|
23
24
|
include: [
|
|
24
25
|
'src/**/*.ts',
|
|
@@ -49,28 +50,83 @@ export async function initAction(options: {
|
|
|
49
50
|
[ToolName.PatternDetect]: {
|
|
50
51
|
minSimilarity: 0.8,
|
|
51
52
|
minLines: 5,
|
|
53
|
+
...(options.full
|
|
54
|
+
? {
|
|
55
|
+
batchSize: 50,
|
|
56
|
+
approx: true,
|
|
57
|
+
minSharedTokens: 10,
|
|
58
|
+
maxCandidatesPerBlock: 100,
|
|
59
|
+
}
|
|
60
|
+
: {}),
|
|
52
61
|
},
|
|
53
62
|
[ToolName.ContextAnalyzer]: {
|
|
54
63
|
maxContextBudget: 128000,
|
|
55
64
|
minCohesion: 0.6,
|
|
65
|
+
...(options.full
|
|
66
|
+
? {
|
|
67
|
+
maxDepth: 7,
|
|
68
|
+
maxFragmentation: 0.4,
|
|
69
|
+
focus: 'all',
|
|
70
|
+
includeNodeModules: false,
|
|
71
|
+
}
|
|
72
|
+
: {}),
|
|
56
73
|
},
|
|
57
74
|
[ToolName.NamingConsistency]: {
|
|
58
75
|
shortWords: ['id', 'db', 'ui', 'ai'],
|
|
76
|
+
...(options.full
|
|
77
|
+
? { acceptedAbbreviations: [], disableChecks: [] }
|
|
78
|
+
: {}),
|
|
59
79
|
},
|
|
60
80
|
[ToolName.AiSignalClarity]: {
|
|
61
81
|
checkMagicLiterals: true,
|
|
62
82
|
checkBooleanTraps: true,
|
|
63
83
|
checkAmbiguousNames: true,
|
|
64
84
|
checkUndocumentedExports: true,
|
|
85
|
+
...(options.full
|
|
86
|
+
? { checkImplicitSideEffects: false, checkDeepCallbacks: false }
|
|
87
|
+
: {}),
|
|
65
88
|
},
|
|
89
|
+
...(options.full
|
|
90
|
+
? {
|
|
91
|
+
[ToolName.AgentGrounding]: {
|
|
92
|
+
maxRecommendedDepth: 5,
|
|
93
|
+
readmeStaleDays: 30,
|
|
94
|
+
},
|
|
95
|
+
[ToolName.TestabilityIndex]: {
|
|
96
|
+
minCoverageRatio: 0.7,
|
|
97
|
+
testPatterns: ['**/*.test.ts', '**/__tests__/**'],
|
|
98
|
+
},
|
|
99
|
+
[ToolName.DocDrift]: {
|
|
100
|
+
maxCommits: 50,
|
|
101
|
+
staleMonths: 3,
|
|
102
|
+
},
|
|
103
|
+
[ToolName.DependencyHealth]: {
|
|
104
|
+
trainingCutoffYear: 2023,
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
: {}),
|
|
66
108
|
},
|
|
67
109
|
scoring: {
|
|
68
110
|
threshold: 70,
|
|
69
111
|
showBreakdown: true,
|
|
112
|
+
...(options.full ? { profile: 'default' } : {}),
|
|
70
113
|
},
|
|
114
|
+
...(options.full
|
|
115
|
+
? {
|
|
116
|
+
visualizer: {
|
|
117
|
+
groupingDirs: ['packages', 'src', 'lib'],
|
|
118
|
+
graph: {
|
|
119
|
+
maxNodes: 5000,
|
|
120
|
+
maxEdges: 10000,
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
}
|
|
124
|
+
: {}),
|
|
71
125
|
};
|
|
72
126
|
|
|
73
|
-
|
|
127
|
+
const defaultConfig = baseConfig;
|
|
128
|
+
|
|
129
|
+
let content: string;
|
|
74
130
|
if (fileExt === 'js') {
|
|
75
131
|
content = `/** @type {import('@aiready/core').AIReadyConfig} */\nmodule.exports = ${JSON.stringify(
|
|
76
132
|
defaultConfig,
|
package/src/commands/patterns.ts
CHANGED
|
@@ -86,7 +86,9 @@ export async function patternsAction(
|
|
|
86
86
|
const { analyzePatterns, generateSummary, calculatePatternScore } =
|
|
87
87
|
await import('@aiready/pattern-detect');
|
|
88
88
|
|
|
89
|
-
const { results, duplicates } = await analyzePatterns(
|
|
89
|
+
const { results, duplicates } = (await analyzePatterns(
|
|
90
|
+
finalOptions
|
|
91
|
+
)) as any;
|
|
90
92
|
|
|
91
93
|
const elapsedTime = getElapsedTime(startTime);
|
|
92
94
|
const summary = generateSummary(results);
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import {
|
|
3
|
+
Severity,
|
|
4
|
+
ScoringResult,
|
|
5
|
+
formatScore,
|
|
6
|
+
getRating,
|
|
7
|
+
getRatingDisplay,
|
|
8
|
+
} from '@aiready/core';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Handle console output for the scan results
|
|
12
|
+
*/
|
|
13
|
+
export function printScanSummary(results: any, startTime: number) {
|
|
14
|
+
console.log(chalk.cyan('\n=== AIReady Run Summary ==='));
|
|
15
|
+
console.log(
|
|
16
|
+
` Total issues (all tools): ${chalk.bold(String(results.summary.totalIssues || 0))}`
|
|
17
|
+
);
|
|
18
|
+
console.log(
|
|
19
|
+
` Execution time: ${chalk.bold(((Date.now() - startTime) / 1000).toFixed(2) + 's')}`
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Print business impact analysis
|
|
25
|
+
*/
|
|
26
|
+
export function printBusinessImpact(roi: any, unifiedBudget: any) {
|
|
27
|
+
console.log(chalk.bold('\nš° Business Impact Analysis (Monthly)'));
|
|
28
|
+
console.log(
|
|
29
|
+
` Potential Savings: ${chalk.green(chalk.bold('$' + roi.monthlySavings.toLocaleString()))}`
|
|
30
|
+
);
|
|
31
|
+
console.log(
|
|
32
|
+
` Productivity Gain: ${chalk.cyan(chalk.bold(roi.productivityGainHours + 'h'))} (est. dev time)`
|
|
33
|
+
);
|
|
34
|
+
console.log(
|
|
35
|
+
` Context Efficiency: ${chalk.yellow((unifiedBudget.efficiencyRatio * 100).toFixed(0) + '%')}`
|
|
36
|
+
);
|
|
37
|
+
console.log(
|
|
38
|
+
` Annual Value: ${chalk.bold('$' + roi.annualValue.toLocaleString())} (ROI Prediction)`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Print detailed scoring breakdown
|
|
44
|
+
*/
|
|
45
|
+
export function printScoring(
|
|
46
|
+
scoringResult: ScoringResult,
|
|
47
|
+
scoringProfile: string
|
|
48
|
+
) {
|
|
49
|
+
console.log(chalk.bold('\nš AI Readiness Overall Score'));
|
|
50
|
+
console.log(` ${formatScore(scoringResult)}`);
|
|
51
|
+
console.log(chalk.dim(` (Scoring Profile: ${scoringProfile})`));
|
|
52
|
+
|
|
53
|
+
if (scoringResult.breakdown) {
|
|
54
|
+
console.log(chalk.bold('\nTool breakdown:'));
|
|
55
|
+
scoringResult.breakdown.forEach((tool) => {
|
|
56
|
+
const rating = getRating(tool.score);
|
|
57
|
+
const emoji = getRatingDisplay(rating).emoji;
|
|
58
|
+
console.log(
|
|
59
|
+
` - ${tool.toolName}: ${tool.score}/100 (${rating}) ${emoji}`
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Top Actionable Recommendations
|
|
64
|
+
const allRecs = scoringResult.breakdown
|
|
65
|
+
.flatMap((t) =>
|
|
66
|
+
(t.recommendations || []).map((r) => ({ ...r, tool: t.toolName }))
|
|
67
|
+
)
|
|
68
|
+
.sort((a, b) => b.estimatedImpact - a.estimatedImpact)
|
|
69
|
+
.slice(0, 3);
|
|
70
|
+
|
|
71
|
+
if (allRecs.length > 0) {
|
|
72
|
+
console.log(chalk.bold('\nšÆ Top Actionable Recommendations:'));
|
|
73
|
+
allRecs.forEach((rec, i) => {
|
|
74
|
+
const priorityIcon =
|
|
75
|
+
rec.priority === 'high'
|
|
76
|
+
? 'š“'
|
|
77
|
+
: rec.priority === 'medium'
|
|
78
|
+
? 'š”'
|
|
79
|
+
: 'šµ';
|
|
80
|
+
console.log(` ${i + 1}. ${priorityIcon} ${chalk.bold(rec.action)}`);
|
|
81
|
+
console.log(
|
|
82
|
+
` Impact: ${chalk.green(`+${rec.estimatedImpact} points`)} to ${rec.tool}`
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Normalize report mapping (CLI logic)
|
|
91
|
+
*/
|
|
92
|
+
export function mapToUnifiedReport(
|
|
93
|
+
res: any,
|
|
94
|
+
scoring: ScoringResult | undefined
|
|
95
|
+
) {
|
|
96
|
+
const allResults: any[] = [];
|
|
97
|
+
const totalFilesSet = new Set<string>();
|
|
98
|
+
let criticalCount = 0;
|
|
99
|
+
let majorCount = 0;
|
|
100
|
+
|
|
101
|
+
res.summary.toolsRun.forEach((toolId: string) => {
|
|
102
|
+
const spokeRes = res[toolId];
|
|
103
|
+
if (!spokeRes || !spokeRes.results) return;
|
|
104
|
+
|
|
105
|
+
spokeRes.results.forEach((r: any) => {
|
|
106
|
+
totalFilesSet.add(r.fileName);
|
|
107
|
+
allResults.push(r);
|
|
108
|
+
r.issues?.forEach((i: any) => {
|
|
109
|
+
if (i.severity === Severity.Critical || i.severity === 'critical')
|
|
110
|
+
criticalCount++;
|
|
111
|
+
if (i.severity === Severity.Major || i.severity === 'major')
|
|
112
|
+
majorCount++;
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
...res,
|
|
119
|
+
results: allResults,
|
|
120
|
+
summary: {
|
|
121
|
+
...res.summary,
|
|
122
|
+
totalFiles: totalFilesSet.size,
|
|
123
|
+
criticalIssues: criticalCount,
|
|
124
|
+
majorIssues: majorCount,
|
|
125
|
+
},
|
|
126
|
+
scoring,
|
|
127
|
+
};
|
|
128
|
+
}
|