@aiready/cli 0.10.6 โ†’ 0.12.1

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/src/index.ts CHANGED
@@ -1,28 +1,32 @@
1
- import { analyzePatterns } from '@aiready/pattern-detect';
2
- import { analyzeContext } from '@aiready/context-analyzer';
3
- import { analyzeConsistency } from '@aiready/consistency';
4
- import type { AnalysisResult, ScanOptions, SpokeOutput } from '@aiready/core';
5
1
  import {
2
+ ToolRegistry,
3
+ ToolName,
6
4
  calculateOverallScore,
7
- type ToolScoringOutput,
8
- type ScoringResult,
9
5
  calculateTokenBudget,
10
- ToolName,
11
6
  } from '@aiready/core';
7
+ import type {
8
+ AnalysisResult,
9
+ ScanOptions,
10
+ SpokeOutput,
11
+ ToolScoringOutput,
12
+ ScoringResult,
13
+ } from '@aiready/core';
14
+
15
+ // Pre-import all tool providers to ensure they are registered by default
16
+ import '@aiready/pattern-detect';
17
+ import '@aiready/context-analyzer';
18
+ import '@aiready/consistency';
19
+ import '@aiready/ai-signal-clarity';
20
+ import '@aiready/agent-grounding';
21
+ import '@aiready/testability';
22
+ import '@aiready/doc-drift';
23
+ import '@aiready/deps';
24
+ import '@aiready/change-amplification';
25
+
12
26
  export type { ToolScoringOutput, ScoringResult };
13
27
 
14
28
  export interface UnifiedAnalysisOptions extends ScanOptions {
15
- tools?: (
16
- | 'patterns'
17
- | 'context'
18
- | 'consistency'
19
- | 'doc-drift'
20
- | 'deps-health'
21
- | 'ai-signal-clarity'
22
- | 'agent-grounding'
23
- | 'testability'
24
- | 'change-amplification'
25
- )[];
29
+ tools?: string[];
26
30
  minSimilarity?: number;
27
31
  minLines?: number;
28
32
  maxCandidatesPerBlock?: number;
@@ -33,461 +37,215 @@ export interface UnifiedAnalysisOptions extends ScanOptions {
33
37
  }
34
38
 
35
39
  export interface UnifiedAnalysisResult {
36
- // Canonical keys matching ToolName enum members
37
- [ToolName.PatternDetect]?: SpokeOutput & { duplicates: any[] };
38
- [ToolName.ContextAnalyzer]?: SpokeOutput;
39
- [ToolName.NamingConsistency]?: SpokeOutput;
40
- [ToolName.DocDrift]?: SpokeOutput;
41
- [ToolName.DependencyHealth]?: SpokeOutput;
42
- [ToolName.AiSignalClarity]?: any;
43
- [ToolName.AgentGrounding]?: any;
44
- [ToolName.TestabilityIndex]?: any;
45
- [ToolName.ChangeAmplification]?: SpokeOutput;
40
+ // Dynamic keys based on ToolName
41
+ [key: string]: any;
46
42
 
47
- // Legacy/Internal metadata
48
43
  summary: {
49
44
  totalIssues: number;
50
45
  toolsRun: string[];
51
46
  executionTime: number;
52
47
  };
53
48
  scoring?: ScoringResult;
54
- // Compatibility fallbacks (deprecated)
55
- /** @deprecated use [ToolName.PatternDetect] */
56
- patternDetect?: any;
57
- /** @deprecated use [ToolName.ContextAnalyzer] */
58
- contextAnalyzer?: any;
59
49
  }
60
50
 
61
- // Severity ordering (higher number = more severe)
62
- const severityOrder: Record<string, number> = {
63
- critical: 4,
64
- major: 3,
65
- minor: 2,
66
- info: 1,
51
+ /**
52
+ * Mapping between ToolName and @aiready/ package names.
53
+ * Used for dynamic registration on-demand.
54
+ */
55
+ const TOOL_PACKAGE_MAP: Record<string, string> = {
56
+ [ToolName.PatternDetect]: '@aiready/pattern-detect',
57
+ [ToolName.ContextAnalyzer]: '@aiready/context-analyzer',
58
+ [ToolName.NamingConsistency]: '@aiready/consistency',
59
+ [ToolName.AiSignalClarity]: '@aiready/ai-signal-clarity',
60
+ [ToolName.AgentGrounding]: '@aiready/agent-grounding',
61
+ [ToolName.TestabilityIndex]: '@aiready/testability',
62
+ [ToolName.DocDrift]: '@aiready/doc-drift',
63
+ [ToolName.DependencyHealth]: '@aiready/deps',
64
+ [ToolName.ChangeAmplification]: '@aiready/change-amplification',
65
+ // Aliases handled by registry
66
+ patterns: '@aiready/pattern-detect',
67
+ duplicates: '@aiready/pattern-detect',
68
+ context: '@aiready/context-analyzer',
69
+ fragmentation: '@aiready/context-analyzer',
70
+ consistency: '@aiready/consistency',
71
+ 'naming-consistency': '@aiready/consistency',
72
+ 'ai-signal': '@aiready/ai-signal-clarity',
73
+ 'ai-signal-clarity': '@aiready/ai-signal-clarity',
74
+ grounding: '@aiready/agent-grounding',
75
+ 'agent-grounding': '@aiready/agent-grounding',
76
+ testability: '@aiready/testability',
77
+ 'testability-index': '@aiready/testability',
78
+ 'doc-drift': '@aiready/doc-drift',
79
+ 'deps-health': '@aiready/deps',
80
+ 'dependency-health': '@aiready/deps',
81
+ 'change-amp': '@aiready/change-amplification',
82
+ 'change-amplification': '@aiready/change-amplification',
67
83
  };
68
84
 
69
- function sortBySeverity(results: AnalysisResult[]): AnalysisResult[] {
70
- return results
71
- .map((file) => {
72
- // Sort issues within each file by severity (most severe first)
73
- const sortedIssues = [...file.issues].sort((a, b) => {
74
- const severityDiff =
75
- (severityOrder[b.severity] || 0) - (severityOrder[a.severity] || 0);
76
- if (severityDiff !== 0) return severityDiff;
77
- // If same severity, sort by line number
78
- return (a.location?.line || 0) - (b.location?.line || 0);
79
- });
80
- return { ...file, issues: sortedIssues };
81
- })
82
- .sort((a, b) => {
83
- // Sort files by most severe issue first
84
- const aMaxSeverity = Math.max(
85
- ...a.issues.map((i) => severityOrder[i.severity] || 0),
86
- 0
87
- );
88
- const bMaxSeverity = Math.max(
89
- ...b.issues.map((i) => severityOrder[i.severity] || 0),
90
- 0
91
- );
92
- if (aMaxSeverity !== bMaxSeverity) {
93
- return bMaxSeverity - aMaxSeverity;
94
- }
95
- // If same max severity, sort by number of issues
96
- if (a.issues.length !== b.issues.length) {
97
- return b.issues.length - a.issues.length;
98
- }
99
- // Finally, sort alphabetically by filename
100
- return a.fileName.localeCompare(b.fileName);
101
- });
102
- }
103
-
85
+ /**
86
+ * AIReady Unified Analysis
87
+ * Orchestrates all registered tools via the ToolRegistry.
88
+ */
104
89
  export async function analyzeUnified(
105
90
  options: UnifiedAnalysisOptions
106
91
  ): Promise<UnifiedAnalysisResult> {
107
92
  const startTime = Date.now();
108
- const tools = options.tools || ['patterns', 'context', 'consistency'];
109
- // Tools requested and effective options are used from `options`
93
+ const requestedTools = options.tools || [
94
+ 'patterns',
95
+ 'context',
96
+ 'consistency',
97
+ ];
98
+
110
99
  const result: UnifiedAnalysisResult = {
111
100
  summary: {
112
101
  totalIssues: 0,
113
- toolsRun: tools,
102
+ toolsRun: [],
114
103
  executionTime: 0,
115
104
  },
116
105
  };
117
106
 
118
- // Run pattern detection
119
- if (tools.includes('patterns')) {
120
- const patternResult = await analyzePatterns(options);
121
- if (options.progressCallback) {
122
- options.progressCallback({ tool: 'patterns', data: patternResult });
107
+ for (const toolName of requestedTools) {
108
+ let provider = ToolRegistry.find(toolName);
109
+
110
+ // Dynamic Loading: If provider not found, attempt to import the package
111
+ if (!provider) {
112
+ const packageName =
113
+ TOOL_PACKAGE_MAP[toolName] ||
114
+ (toolName.startsWith('@aiready/') ? toolName : `@aiready/${toolName}`);
115
+ try {
116
+ await import(packageName);
117
+ provider = ToolRegistry.find(toolName);
118
+ if (provider) {
119
+ console.log(
120
+ `โœ… Successfully loaded tool provider: ${toolName} from ${packageName}`
121
+ );
122
+ } else {
123
+ console.log(
124
+ `โš ๏ธ Loaded ${packageName} but provider ${toolName} still not found in registry.`
125
+ );
126
+ }
127
+ } catch (err: any) {
128
+ console.log(
129
+ `โŒ Failed to dynamically load tool ${toolName} (${packageName}):`,
130
+ err.message
131
+ );
132
+ }
123
133
  }
124
- const output = {
125
- results: sortBySeverity(patternResult.results),
126
- summary: patternResult.summary || {},
127
- duplicates: patternResult.duplicates || [],
128
- };
129
- result[ToolName.PatternDetect] = output;
130
- result.patternDetect = output; // Compatibility fallback
131
-
132
- result.summary.totalIssues += patternResult.results.reduce(
133
- (sum, file) => sum + file.issues.length,
134
- 0
135
- );
136
- }
137
134
 
138
- // Run context analysis
139
- if (tools.includes('context')) {
140
- const contextResults = await analyzeContext(options);
141
- if (options.progressCallback) {
142
- options.progressCallback({ tool: 'context', data: contextResults });
135
+ if (!provider) {
136
+ console.warn(
137
+ `โš ๏ธ Warning: Tool provider for '${toolName}' not found. Skipping.`
138
+ );
139
+ continue;
143
140
  }
144
- const sorted = contextResults.sort((a, b) => {
145
- const severityDiff =
146
- (severityOrder[b.severity] || 0) - (severityOrder[a.severity] || 0);
147
- if (severityDiff !== 0) return severityDiff;
148
- if (a.tokenCost !== b.tokenCost) return b.tokenCost - a.tokenCost;
149
- return b.fragmentationScore - a.fragmentationScore;
150
- });
151
-
152
- const { generateSummary: genContextSummary } =
153
- await import('@aiready/context-analyzer');
154
- const output = {
155
- results: sorted,
156
- summary: genContextSummary(sorted),
157
- };
158
- result[ToolName.ContextAnalyzer] = output;
159
- result.contextAnalyzer = output; // Compatibility fallback
160
141
 
161
- result.summary.totalIssues += sorted.length;
162
- }
163
-
164
- // Run consistency analysis
165
- if (tools.includes('consistency')) {
166
- const consistencyOptions = {
167
- rootDir: options.rootDir,
168
- include: options.include,
169
- exclude: options.exclude,
170
- ...(options.consistency || {}),
171
- };
172
- const report = await analyzeConsistency(consistencyOptions);
173
- if (options.progressCallback) {
174
- options.progressCallback({ tool: 'consistency', data: report });
175
- }
176
- result[ToolName.NamingConsistency] = {
177
- results: report.results ? sortBySeverity(report.results) : [],
178
- summary: report.summary,
179
- };
180
- result.summary.totalIssues += report.summary.totalIssues;
181
- }
142
+ try {
143
+ const output = await provider.analyze({
144
+ ...options,
145
+ onProgress: (processed: number, total: number, message: string) => {
146
+ if (options.progressCallback) {
147
+ options.progressCallback({
148
+ tool: provider!.id,
149
+ processed,
150
+ total,
151
+ message,
152
+ });
153
+ }
154
+ },
155
+ });
182
156
 
183
- // Run Documentation Drift analysis
184
- if (tools.includes('doc-drift')) {
185
- const { analyzeDocDrift } = await import('@aiready/doc-drift');
186
- const report = await analyzeDocDrift({
187
- rootDir: options.rootDir,
188
- include: options.include,
189
- exclude: options.exclude,
190
- onProgress: options.onProgress,
191
- });
192
- if (options.progressCallback) {
193
- options.progressCallback({ tool: 'doc-drift', data: report });
194
- }
195
- result[ToolName.DocDrift] = {
196
- results: (report as any).results || (report as any).issues || [],
197
- summary: report.summary || {},
198
- };
199
- const issueCount =
200
- (report as any).issues?.length ||
201
- ((report as any).results ? (report as any).results.length : 0);
202
- result.summary.totalIssues += issueCount;
203
- }
157
+ if (options.progressCallback) {
158
+ options.progressCallback({ tool: provider.id, data: output });
159
+ }
204
160
 
205
- // Run Dependency Health analysis
206
- if (tools.includes('deps-health')) {
207
- const { analyzeDeps } = await import('@aiready/deps');
208
- const report = await analyzeDeps({
209
- rootDir: options.rootDir,
210
- include: options.include,
211
- exclude: options.exclude,
212
- onProgress: options.onProgress,
213
- });
214
- if (options.progressCallback) {
215
- options.progressCallback({ tool: 'deps-health', data: report });
216
- }
217
- result[ToolName.DependencyHealth] = {
218
- results: (report as any).results || (report as any).issues || [],
219
- summary: report.summary || {},
220
- };
221
- const issueCount =
222
- (report as any).issues?.length ||
223
- ((report as any).results ? (report as any).results.length : 0);
224
- result.summary.totalIssues += issueCount;
225
- }
161
+ result[provider.id] = output;
162
+ result.summary.toolsRun.push(provider.id);
226
163
 
227
- // Run AI Signal Clarity analysis
228
- if (tools.includes('ai-signal-clarity')) {
229
- const { analyzeAiSignalClarity } =
230
- await import('@aiready/ai-signal-clarity');
231
- const report = await analyzeAiSignalClarity({
232
- rootDir: options.rootDir,
233
- include: options.include,
234
- exclude: options.exclude,
235
- onProgress: options.onProgress,
236
- });
237
- if (options.progressCallback) {
238
- options.progressCallback({ tool: 'ai-signal-clarity', data: report });
239
- }
240
- result[ToolName.AiSignalClarity] = {
241
- ...report,
242
- results: report.results || report.issues || [],
243
- summary: report.summary || {},
244
- };
245
- result.summary.totalIssues +=
246
- (report.results || report.issues)?.reduce(
247
- (sum: number, r: any) => sum + (r.issues?.length || 1),
164
+ const issueCount = output.results.reduce(
165
+ (sum: number, file: any) => sum + (file.issues?.length || 0),
248
166
  0
249
- ) || 0;
250
- }
251
-
252
- // Run Agent Grounding analysis
253
- if (tools.includes('agent-grounding')) {
254
- const { analyzeAgentGrounding } = await import('@aiready/agent-grounding');
255
- const report = await analyzeAgentGrounding({
256
- rootDir: options.rootDir,
257
- include: options.include,
258
- exclude: options.exclude,
259
- onProgress: options.onProgress,
260
- });
261
- if (options.progressCallback) {
262
- options.progressCallback({ tool: 'agent-grounding', data: report });
263
- }
264
- result[ToolName.AgentGrounding] = {
265
- ...(report as any),
266
- results: (report as any).results || (report as any).issues || [],
267
- summary: report.summary || {},
268
- };
269
- result.summary.totalIssues += (
270
- (report as any).issues ||
271
- (report as any).results ||
272
- []
273
- ).length;
274
- }
275
-
276
- // Run Testability analysis
277
- if (tools.includes('testability')) {
278
- const { analyzeTestability } = await import('@aiready/testability');
279
- const report = await analyzeTestability({
280
- rootDir: options.rootDir,
281
- include: options.include,
282
- exclude: options.exclude,
283
- onProgress: options.onProgress,
284
- });
285
- if (options.progressCallback) {
286
- options.progressCallback({ tool: 'testability', data: report });
287
- }
288
- result[ToolName.TestabilityIndex] = {
289
- ...(report as any),
290
- results: (report as any).results || (report as any).issues || [],
291
- summary: report.summary || {},
292
- };
293
- result.summary.totalIssues += (
294
- (report as any).issues ||
295
- (report as any).results ||
296
- []
297
- ).length;
298
- }
167
+ );
168
+ result.summary.totalIssues += issueCount;
169
+
170
+ // Robust backward compatibility fallbacks
171
+ // 1. Add all aliases as keys (e.g., 'patterns', 'context', 'consistency')
172
+ if (provider.alias && Array.isArray(provider.alias)) {
173
+ for (const alias of provider.alias) {
174
+ if (!result[alias]) {
175
+ (result as any)[alias] = output;
176
+ }
177
+ }
178
+ }
299
179
 
300
- // Run Change Amplification analysis
301
- if (tools.includes('change-amplification')) {
302
- const { analyzeChangeAmplification } =
303
- await import('@aiready/change-amplification');
304
- const report = await analyzeChangeAmplification({
305
- rootDir: options.rootDir,
306
- include: options.include,
307
- exclude: options.exclude,
308
- onProgress: options.onProgress,
309
- });
310
- if (options.progressCallback) {
311
- options.progressCallback({ tool: 'change-amplification', data: report });
180
+ // 2. Add camelCase version of canonical ID (e.g., 'patternDetect', 'contextAnalyzer')
181
+ const camelCaseId = provider.id.replace(/-([a-z])/g, (g) =>
182
+ g[1].toUpperCase()
183
+ );
184
+ if (camelCaseId !== provider.id && !result[camelCaseId]) {
185
+ (result as any)[camelCaseId] = output;
186
+ }
187
+ } catch (err) {
188
+ console.error(`โŒ Error running tool '${provider.id}':`, err);
312
189
  }
313
- result[ToolName.ChangeAmplification] = {
314
- results: report.results || [],
315
- summary: report.summary || {},
316
- };
317
- result.summary.totalIssues += report.summary?.totalIssues || 0;
318
190
  }
319
191
 
320
192
  result.summary.executionTime = Date.now() - startTime;
321
193
  return result;
322
194
  }
323
195
 
196
+ /**
197
+ * AIReady Unified Scoring
198
+ * Calculates scores for all analyzed tools.
199
+ */
324
200
  export async function scoreUnified(
325
201
  results: UnifiedAnalysisResult,
326
202
  options: UnifiedAnalysisOptions
327
203
  ): Promise<ScoringResult> {
328
204
  const toolScores: Map<string, ToolScoringOutput> = new Map();
329
205
 
330
- // Patterns score
331
- if (results[ToolName.PatternDetect]) {
332
- const data = results[ToolName.PatternDetect];
333
- const { calculatePatternScore } = await import('@aiready/pattern-detect');
334
- try {
335
- const patternScore = calculatePatternScore(
336
- data.duplicates,
337
- data.results?.length || 0
338
- );
339
-
340
- // Calculate token budget for patterns (waste = duplication)
341
- const wastedTokens = data.duplicates.reduce(
342
- (sum: number, d: any) => sum + (d.tokenCost || 0),
343
- 0
344
- );
345
- patternScore.tokenBudget = calculateTokenBudget({
346
- totalContextTokens: wastedTokens * 2, // Estimated context
347
- wastedTokens: {
348
- duplication: wastedTokens,
349
- fragmentation: 0,
350
- chattiness: 0,
351
- },
352
- });
353
-
354
- toolScores.set(ToolName.PatternDetect, patternScore);
355
- } catch (err) {
356
- void err;
357
- }
358
- }
359
-
360
- // Context score
361
- if (results[ToolName.ContextAnalyzer]) {
362
- const data = results[ToolName.ContextAnalyzer];
363
- const { calculateContextScore } = await import('@aiready/context-analyzer');
364
- try {
365
- const ctxSummary = data.summary;
366
- const contextScore = calculateContextScore(ctxSummary);
367
-
368
- // Calculate token budget for context (waste = fragmentation + depth overhead)
369
- contextScore.tokenBudget = calculateTokenBudget({
370
- totalContextTokens: ctxSummary.totalTokens,
371
- wastedTokens: {
372
- duplication: 0,
373
- fragmentation: ctxSummary.totalPotentialSavings || 0,
374
- chattiness: 0,
375
- },
376
- });
206
+ for (const toolId of results.summary.toolsRun) {
207
+ const provider = ToolRegistry.get(toolId as ToolName);
208
+ if (!provider) continue;
377
209
 
378
- toolScores.set(ToolName.ContextAnalyzer, contextScore);
379
- } catch (err) {
380
- void err;
381
- }
382
- }
210
+ const output = results[toolId];
211
+ if (!output) continue;
383
212
 
384
- // Consistency score
385
- if (results[ToolName.NamingConsistency]) {
386
- const data = results[ToolName.NamingConsistency];
387
- const { calculateConsistencyScore } = await import('@aiready/consistency');
388
213
  try {
389
- const issues = data.results?.flatMap((r: any) => r.issues) || [];
390
- const totalFiles = data.summary?.filesAnalyzed || 0;
391
- const consistencyScore = calculateConsistencyScore(issues, totalFiles);
392
- toolScores.set(ToolName.NamingConsistency, consistencyScore);
393
- } catch (err) {
394
- void err;
395
- }
396
- }
214
+ const toolScore = provider.score(output, options);
215
+
216
+ // Special handling for token budget calculation if not provided by tool
217
+ if (!toolScore.tokenBudget) {
218
+ if (toolId === ToolName.PatternDetect && (output as any).duplicates) {
219
+ const wastedTokens = (output as any).duplicates.reduce(
220
+ (sum: number, d: any) => sum + (d.tokenCost || 0),
221
+ 0
222
+ );
223
+ toolScore.tokenBudget = calculateTokenBudget({
224
+ totalContextTokens: wastedTokens * 2,
225
+ wastedTokens: {
226
+ duplication: wastedTokens,
227
+ fragmentation: 0,
228
+ chattiness: 0,
229
+ },
230
+ });
231
+ } else if (toolId === ToolName.ContextAnalyzer && output.summary) {
232
+ toolScore.tokenBudget = calculateTokenBudget({
233
+ totalContextTokens: output.summary.totalTokens,
234
+ wastedTokens: {
235
+ duplication: 0,
236
+ fragmentation: output.summary.totalPotentialSavings || 0,
237
+ chattiness: 0,
238
+ },
239
+ });
240
+ }
241
+ }
397
242
 
398
- // AI signal clarity score
399
- if (results[ToolName.AiSignalClarity]) {
400
- const { calculateAiSignalClarityScore } =
401
- await import('@aiready/ai-signal-clarity');
402
- try {
403
- const hrScore = calculateAiSignalClarityScore(
404
- results[ToolName.AiSignalClarity]
405
- );
406
- toolScores.set(ToolName.AiSignalClarity, hrScore);
243
+ toolScores.set(toolId, toolScore);
407
244
  } catch (err) {
408
- void err;
245
+ console.error(`โŒ Error scoring tool '${toolId}':`, err);
409
246
  }
410
247
  }
411
248
 
412
- // Agent grounding score
413
- if (results[ToolName.AgentGrounding]) {
414
- const { calculateGroundingScore } =
415
- await import('@aiready/agent-grounding');
416
- try {
417
- const agScore = calculateGroundingScore(results[ToolName.AgentGrounding]);
418
- toolScores.set(ToolName.AgentGrounding, agScore);
419
- } catch (err) {
420
- void err;
421
- }
422
- }
423
-
424
- // Testability score
425
- if (results[ToolName.TestabilityIndex]) {
426
- const { calculateTestabilityScore } = await import('@aiready/testability');
427
- try {
428
- const tbScore = calculateTestabilityScore(
429
- results[ToolName.TestabilityIndex]
430
- );
431
- toolScores.set(ToolName.TestabilityIndex, tbScore);
432
- } catch (err) {
433
- void err;
434
- }
435
- }
436
-
437
- // Documentation Drift score
438
- if (results[ToolName.DocDrift]) {
439
- const data = results[ToolName.DocDrift];
440
- toolScores.set(ToolName.DocDrift, {
441
- toolName: ToolName.DocDrift,
442
- score: data.summary.score || data.summary.totalScore || 0,
443
- rawMetrics: data.summary,
444
- factors: [],
445
- recommendations: (data.summary.recommendations || []).map(
446
- (action: string) => ({
447
- action,
448
- estimatedImpact: 5,
449
- priority: 'medium',
450
- })
451
- ),
452
- });
453
- }
454
-
455
- // Dependency Health score
456
- if (results[ToolName.DependencyHealth]) {
457
- const data = results[ToolName.DependencyHealth];
458
- toolScores.set(ToolName.DependencyHealth, {
459
- toolName: ToolName.DependencyHealth,
460
- score: data.summary.score || 0,
461
- rawMetrics: data.summary,
462
- factors: [],
463
- recommendations: (data.summary.recommendations || []).map(
464
- (action: string) => ({
465
- action,
466
- estimatedImpact: 5,
467
- priority: 'medium',
468
- })
469
- ),
470
- });
471
- }
472
-
473
- // Change Amplification score
474
- if (results[ToolName.ChangeAmplification]) {
475
- const data = results[ToolName.ChangeAmplification];
476
- toolScores.set(ToolName.ChangeAmplification, {
477
- toolName: ToolName.ChangeAmplification,
478
- score: data.summary.score || 0,
479
- rawMetrics: data.summary,
480
- factors: [],
481
- recommendations: (data.summary.recommendations || []).map(
482
- (action: string) => ({
483
- action,
484
- estimatedImpact: 5,
485
- priority: 'medium',
486
- })
487
- ),
488
- });
489
- }
490
-
491
249
  // Handle case where toolScores is empty
492
250
  if (toolScores.size === 0) {
493
251
  return {
@@ -507,6 +265,9 @@ export async function scoreUnified(
507
265
  return calculateOverallScore(toolScores, options, undefined);
508
266
  }
509
267
 
268
+ /**
269
+ * Generate human-readable summary of unified results
270
+ */
510
271
  export function generateUnifiedSummary(result: UnifiedAnalysisResult): string {
511
272
  const { summary } = result;
512
273
  let output = `๐Ÿš€ AIReady Analysis Complete\n\n`;
@@ -515,40 +276,15 @@ export function generateUnifiedSummary(result: UnifiedAnalysisResult): string {
515
276
  output += ` Total issues found: ${summary.totalIssues}\n`;
516
277
  output += ` Execution time: ${(summary.executionTime / 1000).toFixed(2)}s\n\n`;
517
278
 
518
- if (result[ToolName.PatternDetect]) {
519
- output += `๐Ÿ” Pattern Analysis: ${result[ToolName.PatternDetect].results.length} issues\n`;
520
- }
521
-
522
- if (result[ToolName.ContextAnalyzer]) {
523
- output += `๐Ÿง  Context Analysis: ${result[ToolName.ContextAnalyzer].results.length} issues\n`;
524
- }
525
-
526
- if (result[ToolName.NamingConsistency]) {
527
- output += `๐Ÿท๏ธ Consistency Analysis: ${result[ToolName.NamingConsistency].summary.totalIssues} issues\n`;
528
- }
529
-
530
- if (result[ToolName.DocDrift]) {
531
- output += `๐Ÿ“ Doc Drift Analysis: ${result[ToolName.DocDrift].results?.length || 0} issues\n`;
532
- }
533
-
534
- if (result[ToolName.DependencyHealth]) {
535
- output += `๐Ÿ“ฆ Dependency Health: ${result[ToolName.DependencyHealth].results?.length || 0} issues\n`;
536
- }
537
-
538
- if (result[ToolName.AiSignalClarity]) {
539
- output += `๐Ÿง  AI Signal Clarity: ${result[ToolName.AiSignalClarity].summary?.totalSignals || 0} signals\n`;
540
- }
541
-
542
- if (result[ToolName.AgentGrounding]) {
543
- output += `๐Ÿงญ Agent Grounding: ${result[ToolName.AgentGrounding].results?.length || 0} issues\n`;
544
- }
545
-
546
- if (result[ToolName.TestabilityIndex]) {
547
- output += `๐Ÿงช Testability Index: ${result[ToolName.TestabilityIndex].results?.length || 0} issues\n`;
548
- }
549
-
550
- if (result[ToolName.ChangeAmplification]) {
551
- output += `๐Ÿ’ฅ Change Amplification: ${result[ToolName.ChangeAmplification].summary?.totalIssues || 0} cascading risks\n`;
279
+ for (const provider of ToolRegistry.getAll()) {
280
+ const toolResult = result[provider.id];
281
+ if (toolResult) {
282
+ const issueCount = toolResult.results.reduce(
283
+ (sum: number, r: any) => sum + (r.issues?.length || 0),
284
+ 0
285
+ );
286
+ output += `โ€ข ${provider.id}: ${issueCount} issues\n`;
287
+ }
552
288
  }
553
289
 
554
290
  return output;