@aiready/cli 0.14.1 ā 0.14.3
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 +7 -7
- package/.turbo/turbo-test.log +19 -110
- package/dist/cli.js +149 -125
- package/dist/cli.mjs +69 -45
- 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 +30713 -0
- package/src/.aiready/aiready-report-20260314-203736.json +30713 -0
- package/src/.aiready/aiready-report-20260314-203857.json +30713 -0
- package/src/.aiready/aiready-report-20260314-204047.json +30713 -0
- package/src/__tests__/config-shape.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 -37
- package/src/commands/__tests__/scan.test.ts +3 -1
- package/src/commands/__tests__/visualize.test.ts +3 -7
- package/src/commands/ai-signal-clarity.ts +1 -56
- package/src/commands/deps-health.ts +1 -65
- package/src/commands/doc-drift.ts +1 -62
- package/src/commands/init.ts +62 -2
- package/src/commands/scan.ts +11 -8
- package/src/commands/shared/configured-tool-action.ts +35 -0
- package/src/commands/shared/standard-tool-actions.ts +126 -0
- package/src/commands/visualize.ts +2 -3
- package/src/utils/helpers.ts +85 -36
- package/vitest.config.ts +5 -12
|
@@ -76,7 +76,7 @@ describe('CLI Configuration Shape', () => {
|
|
|
76
76
|
rootDir: '/test',
|
|
77
77
|
tools: [ToolName.PatternDetect],
|
|
78
78
|
useSmartDefaults: true,
|
|
79
|
-
// @ts-
|
|
79
|
+
// @ts-expect-error - testing internal key stripping
|
|
80
80
|
batchSize: 50,
|
|
81
81
|
});
|
|
82
82
|
|
package/src/cli.ts
CHANGED
|
@@ -132,9 +132,10 @@ program
|
|
|
132
132
|
'--js',
|
|
133
133
|
'Generate configuration as a JavaScript file (aiready.config.js)'
|
|
134
134
|
)
|
|
135
|
+
.option('--full', 'Generate a full configuration with all available options')
|
|
135
136
|
.action(async (options) => {
|
|
136
137
|
const format = options.js ? 'js' : 'json';
|
|
137
|
-
await initAction({ force: options.force, format });
|
|
138
|
+
await initAction({ force: options.force, format, full: options.full });
|
|
138
139
|
});
|
|
139
140
|
|
|
140
141
|
// Patterns command - Detect duplicate code patterns
|
|
@@ -60,6 +60,7 @@ describe('Consistency CLI Action', () => {
|
|
|
60
60
|
consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
61
61
|
vi.mocked(core.loadMergedConfig).mockResolvedValue({
|
|
62
62
|
output: { format: 'console' },
|
|
63
|
+
rootDir: '/test',
|
|
63
64
|
});
|
|
64
65
|
});
|
|
65
66
|
|
|
@@ -75,6 +76,7 @@ describe('Consistency CLI Action', () => {
|
|
|
75
76
|
it('supports JSON output', async () => {
|
|
76
77
|
vi.mocked(core.loadMergedConfig).mockResolvedValue({
|
|
77
78
|
output: { format: 'json' },
|
|
79
|
+
rootDir: '/test',
|
|
78
80
|
});
|
|
79
81
|
await consistencyAction('.', {});
|
|
80
82
|
expect(core.handleJSONOutput).toHaveBeenCalled();
|
|
@@ -83,6 +85,7 @@ describe('Consistency CLI Action', () => {
|
|
|
83
85
|
it('supports Markdown output', async () => {
|
|
84
86
|
vi.mocked(core.loadMergedConfig).mockResolvedValue({
|
|
85
87
|
output: { format: 'markdown' },
|
|
88
|
+
rootDir: '/test',
|
|
86
89
|
});
|
|
87
90
|
await consistencyAction('.', {});
|
|
88
91
|
expect(fs.writeFileSync).toHaveBeenCalled();
|
|
@@ -18,7 +18,7 @@ vi.mock('@aiready/core', async () => {
|
|
|
18
18
|
.fn()
|
|
19
19
|
.mockImplementation((c, d) => ({ ...d, ...c })),
|
|
20
20
|
loadMergedConfig: vi.fn().mockResolvedValue({
|
|
21
|
-
rootDir: '
|
|
21
|
+
rootDir: '/test',
|
|
22
22
|
output: { format: 'console' },
|
|
23
23
|
checkNaming: true,
|
|
24
24
|
checkPatterns: true,
|
|
@@ -33,13 +33,11 @@ vi.mock('@aiready/core', async () => {
|
|
|
33
33
|
|
|
34
34
|
// Mock all spokes
|
|
35
35
|
vi.mock('@aiready/change-amplification', () => ({
|
|
36
|
-
analyzeChangeAmplification: vi
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
recommendations: [],
|
|
42
|
-
}),
|
|
36
|
+
analyzeChangeAmplification: vi.fn().mockResolvedValue({
|
|
37
|
+
results: [],
|
|
38
|
+
summary: { rating: 'contained' },
|
|
39
|
+
recommendations: [],
|
|
40
|
+
}),
|
|
43
41
|
calculateChangeAmplificationScore: vi.fn().mockReturnValue({ score: 80 }),
|
|
44
42
|
}));
|
|
45
43
|
vi.mock('@aiready/agent-grounding', () => ({
|
|
@@ -91,39 +89,33 @@ vi.mock('@aiready/deps', () => ({
|
|
|
91
89
|
calculateDepsScore: vi.fn().mockReturnValue({ score: 80 }),
|
|
92
90
|
}));
|
|
93
91
|
vi.mock('@aiready/pattern-detect', () => ({
|
|
94
|
-
analyzePatterns: vi
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
totalTokenCost: 0,
|
|
106
|
-
patternsByType: {},
|
|
107
|
-
topDuplicates: [],
|
|
108
|
-
}),
|
|
92
|
+
analyzePatterns: vi.fn().mockResolvedValue({
|
|
93
|
+
results: [],
|
|
94
|
+
summary: { totalPatterns: 0 },
|
|
95
|
+
config: {},
|
|
96
|
+
}),
|
|
97
|
+
generateSummary: vi.fn().mockReturnValue({
|
|
98
|
+
totalPatterns: 0,
|
|
99
|
+
totalTokenCost: 0,
|
|
100
|
+
patternsByType: {},
|
|
101
|
+
topDuplicates: [],
|
|
102
|
+
}),
|
|
109
103
|
getSmartDefaults: vi.fn().mockResolvedValue({}),
|
|
110
104
|
}));
|
|
111
105
|
vi.mock('@aiready/context-analyzer', () => ({
|
|
112
106
|
analyzeContext: vi.fn().mockResolvedValue([]),
|
|
113
|
-
generateSummary: vi
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
totalPotentialSavings: 0,
|
|
126
|
-
}),
|
|
107
|
+
generateSummary: vi.fn().mockReturnValue({
|
|
108
|
+
score: 80,
|
|
109
|
+
rating: 'good',
|
|
110
|
+
totalFiles: 0,
|
|
111
|
+
totalTokens: 0,
|
|
112
|
+
avgImportDepth: 0,
|
|
113
|
+
maxImportDepth: 0,
|
|
114
|
+
avgFragmentation: 0,
|
|
115
|
+
criticalIssues: 0,
|
|
116
|
+
majorIssues: 0,
|
|
117
|
+
totalPotentialSavings: 0,
|
|
118
|
+
}),
|
|
127
119
|
getSmartDefaults: vi.fn().mockResolvedValue({}),
|
|
128
120
|
}));
|
|
129
121
|
|
|
@@ -58,6 +58,7 @@ describe('Scan CLI Action', () => {
|
|
|
58
58
|
vi.mocked(core.loadMergedConfig).mockResolvedValue({
|
|
59
59
|
tools: ['pattern-detect'],
|
|
60
60
|
output: { format: 'console' },
|
|
61
|
+
rootDir: '/test',
|
|
61
62
|
});
|
|
62
63
|
vi.mocked(index.analyzeUnified).mockResolvedValue({
|
|
63
64
|
summary: {
|
|
@@ -126,7 +127,7 @@ describe('Scan CLI Action', () => {
|
|
|
126
127
|
|
|
127
128
|
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
128
129
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
129
|
-
expect.stringContaining('
|
|
130
|
+
expect.stringContaining('SCAN FAILED')
|
|
130
131
|
);
|
|
131
132
|
// Verify annotations are emitted
|
|
132
133
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
@@ -144,6 +145,7 @@ describe('Scan CLI Action', () => {
|
|
|
144
145
|
vi.mocked(core.loadMergedConfig).mockResolvedValue({
|
|
145
146
|
tools: ['pattern-detect'],
|
|
146
147
|
output: { format: 'json', file: 'out.json' },
|
|
148
|
+
rootDir: '/test',
|
|
147
149
|
});
|
|
148
150
|
await scanAction('.', {});
|
|
149
151
|
expect(core.handleJSONOutput).toHaveBeenCalled();
|
|
@@ -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';
|
|
@@ -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,87 @@ 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
|
+
output: {
|
|
117
|
+
format: fileExt,
|
|
118
|
+
file: 'aiready-report.json',
|
|
119
|
+
},
|
|
120
|
+
visualizer: {
|
|
121
|
+
groupingDirs: ['packages', 'src', 'lib'],
|
|
122
|
+
graph: {
|
|
123
|
+
maxNodes: 5000,
|
|
124
|
+
maxEdges: 10000,
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
}
|
|
128
|
+
: {}),
|
|
71
129
|
};
|
|
72
130
|
|
|
73
|
-
|
|
131
|
+
const defaultConfig = baseConfig;
|
|
132
|
+
|
|
133
|
+
let content: string;
|
|
74
134
|
if (fileExt === 'js') {
|
|
75
135
|
content = `/** @type {import('@aiready/core').AIReadyConfig} */\nmodule.exports = ${JSON.stringify(
|
|
76
136
|
defaultConfig,
|
package/src/commands/scan.ts
CHANGED
|
@@ -151,7 +151,7 @@ export async function scanAction(directory: string, options: ScanOptions) {
|
|
|
151
151
|
)) as any;
|
|
152
152
|
|
|
153
153
|
// Apply smart defaults for pattern detection if requested
|
|
154
|
-
|
|
154
|
+
const finalOptions = { ...baseOptions };
|
|
155
155
|
if (
|
|
156
156
|
baseOptions.tools.includes(ToolName.PatternDetect) ||
|
|
157
157
|
baseOptions.tools.includes('patterns')
|
|
@@ -469,20 +469,20 @@ export async function scanAction(directory: string, options: ScanOptions) {
|
|
|
469
469
|
}
|
|
470
470
|
await warnIfGraphCapExceeded(outputData, resolvedDir);
|
|
471
471
|
|
|
472
|
-
//
|
|
473
|
-
|
|
474
|
-
if (isCI && scoringResult) {
|
|
472
|
+
// Score Check & Gatekeeper logic
|
|
473
|
+
if (scoringResult) {
|
|
475
474
|
const threshold = options.threshold
|
|
476
475
|
? parseInt(options.threshold)
|
|
477
476
|
: undefined;
|
|
478
477
|
const failOnLevel = options.failOn || 'critical';
|
|
478
|
+
const isCI = options.ci || process.env.CI === 'true';
|
|
479
479
|
|
|
480
480
|
let shouldFail = false;
|
|
481
481
|
let failReason = '';
|
|
482
482
|
|
|
483
|
-
// Emit annotations
|
|
483
|
+
// Emit annotations only in CI
|
|
484
484
|
const report = mapToUnifiedReport(results, scoringResult);
|
|
485
|
-
if (report.results && report.results.length > 0) {
|
|
485
|
+
if (isCI && report.results && report.results.length > 0) {
|
|
486
486
|
console.log(
|
|
487
487
|
chalk.cyan(
|
|
488
488
|
`\nš Emitting GitHub Action annotations for ${report.results.length} issues...`
|
|
@@ -495,6 +495,9 @@ export async function scanAction(directory: string, options: ScanOptions) {
|
|
|
495
495
|
shouldFail = true;
|
|
496
496
|
failReason = `Score ${scoringResult.overall} < threshold ${threshold}`;
|
|
497
497
|
}
|
|
498
|
+
|
|
499
|
+
// If failOnLevel is set (default 'critical'), check for issues
|
|
500
|
+
// But only fail if not 'none'
|
|
498
501
|
if (failOnLevel !== 'none') {
|
|
499
502
|
if (failOnLevel === 'critical' && report.summary.criticalIssues > 0) {
|
|
500
503
|
shouldFail = true;
|
|
@@ -509,10 +512,10 @@ export async function scanAction(directory: string, options: ScanOptions) {
|
|
|
509
512
|
}
|
|
510
513
|
|
|
511
514
|
if (shouldFail) {
|
|
512
|
-
console.log(chalk.red(`\nš«
|
|
515
|
+
console.log(chalk.red(`\nš« SCAN FAILED: ${failReason}`));
|
|
513
516
|
process.exit(1);
|
|
514
517
|
} else {
|
|
515
|
-
console.log(chalk.green('\nā
|
|
518
|
+
console.log(chalk.green('\nā
SCAN PASSED'));
|
|
516
519
|
}
|
|
517
520
|
}
|
|
518
521
|
} catch (error) {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ToolScoringOutput } from '@aiready/core';
|
|
2
|
+
import { runConfiguredToolCommand } from '../../utils/helpers';
|
|
3
|
+
|
|
4
|
+
export interface ConfiguredToolActionConfig<TReport> {
|
|
5
|
+
defaults: Record<string, unknown>;
|
|
6
|
+
analyze: (scanOptions: any) => Promise<TReport>;
|
|
7
|
+
getExtras: (
|
|
8
|
+
options: any,
|
|
9
|
+
merged: Record<string, unknown>
|
|
10
|
+
) => Record<string, unknown>;
|
|
11
|
+
score: (report: TReport) => ToolScoringOutput;
|
|
12
|
+
render: (report: TReport, scoring: ToolScoringOutput) => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function runConfiguredToolAction<TReport>(
|
|
16
|
+
directory: string,
|
|
17
|
+
options: any,
|
|
18
|
+
config: ConfiguredToolActionConfig<TReport>
|
|
19
|
+
): Promise<ToolScoringOutput | undefined> {
|
|
20
|
+
const { report, scoring } = await runConfiguredToolCommand({
|
|
21
|
+
directory,
|
|
22
|
+
options,
|
|
23
|
+
defaults: config.defaults,
|
|
24
|
+
analyze: config.analyze,
|
|
25
|
+
getExtras: config.getExtras,
|
|
26
|
+
score: config.score,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
if (options.output === 'json') {
|
|
30
|
+
return scoring;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
config.render(report, scoring);
|
|
34
|
+
return scoring;
|
|
35
|
+
}
|