@aiready/cli 0.12.11 → 0.12.14
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/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-test.log +57 -20
- package/dist/chunk-2QOU5KKW.mjs +301 -0
- package/dist/chunk-55ZUD52M.mjs +290 -0
- package/dist/chunk-BD7XZYET.mjs +302 -0
- package/dist/cli.js +39 -15
- package/dist/cli.mjs +4 -1
- package/dist/index.js +36 -15
- package/dist/index.mjs +1 -1
- package/package.json +12 -12
- package/src/.aiready/aiready-report-20260307-155544.json +41501 -0
- package/src/__tests__/config-shape.test.ts +106 -0
- package/src/commands/scan.ts +4 -1
- package/src/index.ts +59 -17
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { analyzeUnified } from '../index';
|
|
3
|
+
import { ToolRegistry, ToolName, SpokeOutputSchema } from '@aiready/core';
|
|
4
|
+
|
|
5
|
+
describe('CLI Configuration Shape', () => {
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
ToolRegistry.clear();
|
|
8
|
+
|
|
9
|
+
// Register a mock provider that returns its input config in metadata
|
|
10
|
+
ToolRegistry.register({
|
|
11
|
+
id: ToolName.PatternDetect,
|
|
12
|
+
alias: ['patterns'],
|
|
13
|
+
analyze: async (options) =>
|
|
14
|
+
SpokeOutputSchema.parse({
|
|
15
|
+
results: [],
|
|
16
|
+
summary: { config: options },
|
|
17
|
+
metadata: {
|
|
18
|
+
toolName: ToolName.PatternDetect,
|
|
19
|
+
version: '1.0.0',
|
|
20
|
+
config: options,
|
|
21
|
+
},
|
|
22
|
+
}),
|
|
23
|
+
score: () => ({
|
|
24
|
+
toolName: ToolName.PatternDetect,
|
|
25
|
+
score: 80,
|
|
26
|
+
factors: [],
|
|
27
|
+
recommendations: [],
|
|
28
|
+
rawMetrics: {},
|
|
29
|
+
}),
|
|
30
|
+
defaultWeight: 10,
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
ToolRegistry.clear();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should generate a strictly portable AIReadyConfig in summary', async () => {
|
|
39
|
+
const results = await analyzeUnified({
|
|
40
|
+
rootDir: '/tmp/fake-repo',
|
|
41
|
+
tools: [ToolName.PatternDetect],
|
|
42
|
+
exclude: ['**/node_modules/**'],
|
|
43
|
+
// Pass a tool-specific override
|
|
44
|
+
toolConfigs: {
|
|
45
|
+
[ToolName.PatternDetect]: {
|
|
46
|
+
minSimilarity: 0.9,
|
|
47
|
+
// This should be stripped
|
|
48
|
+
rootDir: '/tmp/fake-repo',
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const config = results.summary.config;
|
|
54
|
+
|
|
55
|
+
// 1. Check top-level structure
|
|
56
|
+
expect(config).toHaveProperty('scan');
|
|
57
|
+
expect(config).toHaveProperty('tools');
|
|
58
|
+
|
|
59
|
+
// 2. Ensure rootDir is STRIPPED from top level
|
|
60
|
+
expect(config).not.toHaveProperty('rootDir');
|
|
61
|
+
|
|
62
|
+
// 3. Ensure internal keys are stripped from scan section
|
|
63
|
+
expect(config.scan).toHaveProperty('tools');
|
|
64
|
+
expect(config.scan).toHaveProperty('exclude');
|
|
65
|
+
expect(config.scan).not.toHaveProperty('rootDir');
|
|
66
|
+
|
|
67
|
+
// 4. Ensure recursive stripping in tools section
|
|
68
|
+
const patternConfig = config.tools[ToolName.PatternDetect];
|
|
69
|
+
expect(patternConfig).toHaveProperty('minSimilarity', 0.9);
|
|
70
|
+
expect(patternConfig).not.toHaveProperty('rootDir');
|
|
71
|
+
expect(patternConfig).not.toHaveProperty('onProgress');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should strip internal keys like useSmartDefaults and batchSize', async () => {
|
|
75
|
+
const results = await analyzeUnified({
|
|
76
|
+
rootDir: '/test',
|
|
77
|
+
tools: [ToolName.PatternDetect],
|
|
78
|
+
useSmartDefaults: true,
|
|
79
|
+
// @ts-ignore - testing internal key stripping
|
|
80
|
+
batchSize: 50,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const config = results.summary.config;
|
|
84
|
+
|
|
85
|
+
expect(config).not.toHaveProperty('useSmartDefaults');
|
|
86
|
+
expect(config.scan).not.toHaveProperty('useSmartDefaults');
|
|
87
|
+
|
|
88
|
+
// Check tool level too
|
|
89
|
+
const patternConfig = config.tools[ToolName.PatternDetect];
|
|
90
|
+
expect(patternConfig).not.toHaveProperty('useSmartDefaults');
|
|
91
|
+
expect(patternConfig).not.toHaveProperty('batchSize');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should produce a config that is compatible with tool specific collection', async () => {
|
|
95
|
+
// This test ensures that the toolConfigs collected from individual tools
|
|
96
|
+
// are also sanitized before being merged into the final report.
|
|
97
|
+
const results = await analyzeUnified({
|
|
98
|
+
rootDir: '/test',
|
|
99
|
+
tools: [ToolName.PatternDetect],
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const toolConfigs = results.summary.toolConfigs;
|
|
103
|
+
expect(toolConfigs).toBeDefined();
|
|
104
|
+
expect(toolConfigs![ToolName.PatternDetect]).not.toHaveProperty('rootDir');
|
|
105
|
+
});
|
|
106
|
+
});
|
package/src/commands/scan.ts
CHANGED
|
@@ -184,7 +184,7 @@ export async function scanAction(directory: string, options: ScanOptions) {
|
|
|
184
184
|
const results = await analyzeUnified({
|
|
185
185
|
...finalOptions,
|
|
186
186
|
progressCallback,
|
|
187
|
-
onProgress: () => {
|
|
187
|
+
onProgress: () => {},
|
|
188
188
|
suppressToolConfig: true,
|
|
189
189
|
});
|
|
190
190
|
|
|
@@ -192,6 +192,9 @@ export async function scanAction(directory: string, options: ScanOptions) {
|
|
|
192
192
|
console.log(
|
|
193
193
|
` Total issues (all tools): ${chalk.bold(String(results.summary.totalIssues || 0))}`
|
|
194
194
|
);
|
|
195
|
+
console.log(
|
|
196
|
+
` Execution time: ${chalk.bold(((Date.now() - startTime) / 1000).toFixed(2) + 's')}`
|
|
197
|
+
);
|
|
195
198
|
|
|
196
199
|
let scoringResult: ScoringResult | undefined;
|
|
197
200
|
if (options.score || finalOptions.scoring?.showBreakdown) {
|
package/src/index.ts
CHANGED
|
@@ -92,19 +92,44 @@ const TOOL_PACKAGE_MAP: Record<string, string> = {
|
|
|
92
92
|
};
|
|
93
93
|
|
|
94
94
|
/**
|
|
95
|
-
*
|
|
95
|
+
* Deeply sanitizes a configuration object by removing infrastructure keys like rootDir.
|
|
96
|
+
* Works recursively to clean up nested tool configurations to ensure compatibility
|
|
97
|
+
* with the AIReadyConfig schema for portable configuration files.
|
|
96
98
|
*/
|
|
97
|
-
function
|
|
98
|
-
if (!
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
function sanitizeConfigRecursive(obj: any): any {
|
|
100
|
+
if (!obj || typeof obj !== 'object' || Array.isArray(obj)) return obj;
|
|
101
|
+
|
|
102
|
+
const sanitized: any = {};
|
|
103
|
+
const infraToStrip = [
|
|
104
|
+
'rootDir',
|
|
105
|
+
'onProgress',
|
|
106
|
+
'progressCallback',
|
|
107
|
+
'streamResults',
|
|
108
|
+
'batchSize',
|
|
109
|
+
'useSmartDefaults',
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
113
|
+
// Skip infrastructure keys that shouldn't be in a portable config file
|
|
114
|
+
if (infraToStrip.includes(key)) continue;
|
|
115
|
+
|
|
116
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
117
|
+
sanitized[key] = sanitizeConfigRecursive(value);
|
|
118
|
+
} else {
|
|
119
|
+
sanitized[key] = value;
|
|
103
120
|
}
|
|
104
|
-
}
|
|
121
|
+
}
|
|
122
|
+
|
|
105
123
|
return sanitized;
|
|
106
124
|
}
|
|
107
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Sanitize tool configuration by removing global options to match AIReadyConfig schema
|
|
128
|
+
*/
|
|
129
|
+
function sanitizeToolConfig(config: any): any {
|
|
130
|
+
return sanitizeConfigRecursive(config);
|
|
131
|
+
}
|
|
132
|
+
|
|
108
133
|
/**
|
|
109
134
|
* AIReady Unified Analysis
|
|
110
135
|
* Orchestrates all registered tools via the ToolRegistry.
|
|
@@ -169,9 +194,9 @@ export async function analyzeUnified(
|
|
|
169
194
|
|
|
170
195
|
try {
|
|
171
196
|
// Sanitize options for metadata tracking (remove functions/internal keys)
|
|
172
|
-
const
|
|
173
|
-
delete (
|
|
174
|
-
delete (
|
|
197
|
+
const sanitizedOptions = { ...options };
|
|
198
|
+
delete (sanitizedOptions as any).onProgress;
|
|
199
|
+
delete (sanitizedOptions as any).progressCallback;
|
|
175
200
|
|
|
176
201
|
// 1. Start with sanitized global subset
|
|
177
202
|
const toolOptions: any = {
|
|
@@ -181,15 +206,26 @@ export async function analyzeUnified(
|
|
|
181
206
|
// 1. Pass through all known infra and fine-tuning options from root level
|
|
182
207
|
[...GLOBAL_INFRA_OPTIONS, ...COMMON_FINE_TUNING_OPTIONS].forEach(
|
|
183
208
|
(key) => {
|
|
184
|
-
if (key in options && key !== 'toolConfigs') {
|
|
209
|
+
if (key in options && key !== 'toolConfigs' && key !== 'tools') {
|
|
185
210
|
toolOptions[key] = (options as any)[key];
|
|
186
211
|
}
|
|
187
212
|
}
|
|
188
213
|
);
|
|
189
214
|
|
|
190
215
|
// 3. Add tool-specific overrides
|
|
216
|
+
// Check priority:
|
|
217
|
+
// a) options.toolConfigs[toolId]
|
|
218
|
+
// b) options.tools[toolId] (if tools is an object, not the array of tool names)
|
|
219
|
+
// c) options[toolId] (legacy)
|
|
191
220
|
if (options.toolConfigs?.[provider.id]) {
|
|
192
221
|
Object.assign(toolOptions, options.toolConfigs[provider.id]);
|
|
222
|
+
} else if (
|
|
223
|
+
options.tools &&
|
|
224
|
+
!Array.isArray(options.tools) &&
|
|
225
|
+
typeof options.tools === 'object' &&
|
|
226
|
+
(options.tools as any)[provider.id]
|
|
227
|
+
) {
|
|
228
|
+
Object.assign(toolOptions, (options.tools as any)[provider.id]);
|
|
193
229
|
} else if ((options as any)[provider.id]) {
|
|
194
230
|
// Fallback for legacy tool-specific keys
|
|
195
231
|
Object.assign(toolOptions, (options as any)[provider.id]);
|
|
@@ -275,11 +311,17 @@ export async function analyzeUnified(
|
|
|
275
311
|
}
|
|
276
312
|
}
|
|
277
313
|
|
|
278
|
-
// Finalize configuration for metadata
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
314
|
+
// Finalize configuration for metadata to match AIReadyConfig schema
|
|
315
|
+
// We use sanitizeConfigRecursive to ensure no internal keys (like rootDir) remain
|
|
316
|
+
result.summary.config = sanitizeConfigRecursive({
|
|
317
|
+
scan: {
|
|
318
|
+
tools: requestedTools,
|
|
319
|
+
include: options.include,
|
|
320
|
+
exclude: options.exclude,
|
|
321
|
+
},
|
|
322
|
+
// Use 'tools' for tool-specific configurations to match AIReadyConfig
|
|
323
|
+
tools: result.summary.toolConfigs,
|
|
324
|
+
});
|
|
283
325
|
|
|
284
326
|
result.summary.executionTime = Date.now() - startTime;
|
|
285
327
|
return result;
|