@aiready/cli 0.12.12 → 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/dist/index.js CHANGED
@@ -57,16 +57,30 @@ var TOOL_PACKAGE_MAP = {
57
57
  "deps-health": "@aiready/deps",
58
58
  "change-amp": "@aiready/change-amplification"
59
59
  };
60
- function sanitizeToolConfig(config) {
61
- if (!config || typeof config !== "object") return config;
62
- const sanitized = { ...config };
63
- import_core.GLOBAL_INFRA_OPTIONS.forEach((key) => {
64
- if (key !== "rootDir") {
65
- delete sanitized[key];
60
+ function sanitizeConfigRecursive(obj) {
61
+ if (!obj || typeof obj !== "object" || Array.isArray(obj)) return obj;
62
+ const sanitized = {};
63
+ const infraToStrip = [
64
+ "rootDir",
65
+ "onProgress",
66
+ "progressCallback",
67
+ "streamResults",
68
+ "batchSize",
69
+ "useSmartDefaults"
70
+ ];
71
+ for (const [key, value] of Object.entries(obj)) {
72
+ if (infraToStrip.includes(key)) continue;
73
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
74
+ sanitized[key] = sanitizeConfigRecursive(value);
75
+ } else {
76
+ sanitized[key] = value;
66
77
  }
67
- });
78
+ }
68
79
  return sanitized;
69
80
  }
81
+ function sanitizeToolConfig(config) {
82
+ return sanitizeConfigRecursive(config);
83
+ }
70
84
  async function analyzeUnified(options) {
71
85
  const startTime = Date.now();
72
86
  const requestedTools = options.tools || [
@@ -118,22 +132,24 @@ async function analyzeUnified(options) {
118
132
  continue;
119
133
  }
120
134
  try {
121
- const sanitizedConfig = { ...options };
122
- delete sanitizedConfig.onProgress;
123
- delete sanitizedConfig.progressCallback;
135
+ const sanitizedOptions = { ...options };
136
+ delete sanitizedOptions.onProgress;
137
+ delete sanitizedOptions.progressCallback;
124
138
  const toolOptions = {
125
139
  rootDir: options.rootDir
126
140
  // Always include rootDir
127
141
  };
128
142
  [...import_core.GLOBAL_INFRA_OPTIONS, ...import_core.COMMON_FINE_TUNING_OPTIONS].forEach(
129
143
  (key) => {
130
- if (key in options && key !== "toolConfigs") {
144
+ if (key in options && key !== "toolConfigs" && key !== "tools") {
131
145
  toolOptions[key] = options[key];
132
146
  }
133
147
  }
134
148
  );
135
149
  if (options.toolConfigs?.[provider.id]) {
136
150
  Object.assign(toolOptions, options.toolConfigs[provider.id]);
151
+ } else if (options.tools && !Array.isArray(options.tools) && typeof options.tools === "object" && options.tools[provider.id]) {
152
+ Object.assign(toolOptions, options.tools[provider.id]);
137
153
  } else if (options[provider.id]) {
138
154
  Object.assign(toolOptions, options[provider.id]);
139
155
  }
@@ -194,10 +210,15 @@ async function analyzeUnified(options) {
194
210
  console.error(`\u274C Error running tool '${provider.id}':`, err);
195
211
  }
196
212
  }
197
- result.summary.config = {
198
- ...options,
199
- toolConfigs: result.summary.toolConfigs
200
- };
213
+ result.summary.config = sanitizeConfigRecursive({
214
+ scan: {
215
+ tools: requestedTools,
216
+ include: options.include,
217
+ exclude: options.exclude
218
+ },
219
+ // Use 'tools' for tool-specific configurations to match AIReadyConfig
220
+ tools: result.summary.toolConfigs
221
+ });
201
222
  result.summary.executionTime = Date.now() - startTime;
202
223
  return result;
203
224
  }
package/dist/index.mjs CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  analyzeUnified,
3
3
  generateUnifiedSummary,
4
4
  scoreUnified
5
- } from "./chunk-OOQTTRPG.mjs";
5
+ } from "./chunk-2QOU5KKW.mjs";
6
6
  export {
7
7
  analyzeUnified,
8
8
  generateUnifiedSummary,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/cli",
3
- "version": "0.12.12",
3
+ "version": "0.12.14",
4
4
  "description": "Unified CLI for AIReady analysis tools",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -11,17 +11,17 @@
11
11
  "dependencies": {
12
12
  "chalk": "^5.3.0",
13
13
  "commander": "^14.0.0",
14
- "@aiready/agent-grounding": "0.11.11",
15
- "@aiready/consistency": "0.18.11",
16
- "@aiready/core": "0.21.11",
17
- "@aiready/deps": "0.11.11",
18
- "@aiready/doc-drift": "0.11.11",
19
- "@aiready/context-analyzer": "0.19.11",
20
- "@aiready/change-amplification": "0.11.11",
21
- "@aiready/pattern-detect": "0.14.11",
22
- "@aiready/visualizer": "0.4.11",
23
- "@aiready/testability": "0.4.11",
24
- "@aiready/ai-signal-clarity": "0.11.11"
14
+ "@aiready/agent-grounding": "0.11.13",
15
+ "@aiready/consistency": "0.18.13",
16
+ "@aiready/context-analyzer": "0.19.13",
17
+ "@aiready/deps": "0.11.13",
18
+ "@aiready/core": "0.21.13",
19
+ "@aiready/doc-drift": "0.11.13",
20
+ "@aiready/ai-signal-clarity": "0.11.13",
21
+ "@aiready/visualizer": "0.4.13",
22
+ "@aiready/change-amplification": "0.11.13",
23
+ "@aiready/pattern-detect": "0.14.13",
24
+ "@aiready/testability": "0.4.13"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/node": "^24.0.0",
@@ -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
+ });
@@ -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
- * Sanitize tool configuration by removing global options (except rootDir)
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 sanitizeToolConfig(config: any): any {
98
- if (!config || typeof config !== 'object') return config;
99
- const sanitized = { ...config };
100
- GLOBAL_INFRA_OPTIONS.forEach((key: string) => {
101
- if (key !== 'rootDir') {
102
- delete (sanitized as any)[key];
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 sanitizedConfig = { ...options };
173
- delete (sanitizedConfig as any).onProgress;
174
- delete (sanitizedConfig as any).progressCallback;
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 = {
@@ -286,8 +311,9 @@ export async function analyzeUnified(
286
311
  }
287
312
  }
288
313
 
289
- // Finalize configuration for metadata to match AIReadyConfig structure
290
- result.summary.config = {
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({
291
317
  scan: {
292
318
  tools: requestedTools,
293
319
  include: options.include,
@@ -295,10 +321,7 @@ export async function analyzeUnified(
295
321
  },
296
322
  // Use 'tools' for tool-specific configurations to match AIReadyConfig
297
323
  tools: result.summary.toolConfigs,
298
- // Keep top-level options for backward compatibility
299
- rootDir: options.rootDir,
300
- useSmartDefaults: options.useSmartDefaults,
301
- };
324
+ });
302
325
 
303
326
  result.summary.executionTime = Date.now() - startTime;
304
327
  return result;