@aiready/cli 0.10.6 โ 0.12.0
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-lint.log +9 -1
- package/.turbo/turbo-test.log +16 -4
- package/dist/chunk-N56YAZVN.mjs +194 -0
- package/dist/chunk-YBZKPKW3.mjs +161 -0
- package/dist/cli.js +198 -910
- package/dist/cli.mjs +86 -582
- package/dist/index.js +116 -371
- package/dist/index.mjs +1 -1
- package/package.json +12 -12
- package/src/.aiready/aiready-report-20260307-092852.json +50609 -0
- package/src/.aiready/aiready-report-20260307-094301.json +50609 -0
- package/src/__tests__/cli.test.ts +55 -29
- package/src/commands/scan.ts +107 -699
- package/src/index.ts +154 -455
- package/dist/chunk-EQ2HQSTJ.mjs +0 -414
- package/dist/chunk-R3O7QPKD.mjs +0 -419
- package/dist/chunk-VQCWYJYJ.mjs +0 -438
- package/dist/chunk-VUCNUYI7.mjs +0 -417
package/src/index.ts
CHANGED
|
@@ -1,28 +1,21 @@
|
|
|
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
|
+
|
|
12
15
|
export type { ToolScoringOutput, ScoringResult };
|
|
13
16
|
|
|
14
17
|
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
|
-
)[];
|
|
18
|
+
tools?: string[];
|
|
26
19
|
minSimilarity?: number;
|
|
27
20
|
minLines?: number;
|
|
28
21
|
maxCandidatesPerBlock?: number;
|
|
@@ -33,461 +26,189 @@ export interface UnifiedAnalysisOptions extends ScanOptions {
|
|
|
33
26
|
}
|
|
34
27
|
|
|
35
28
|
export interface UnifiedAnalysisResult {
|
|
36
|
-
//
|
|
37
|
-
[
|
|
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;
|
|
29
|
+
// Dynamic keys based on ToolName
|
|
30
|
+
[key: string]: any;
|
|
46
31
|
|
|
47
|
-
// Legacy/Internal metadata
|
|
48
32
|
summary: {
|
|
49
33
|
totalIssues: number;
|
|
50
34
|
toolsRun: string[];
|
|
51
35
|
executionTime: number;
|
|
52
36
|
};
|
|
53
37
|
scoring?: ScoringResult;
|
|
54
|
-
// Compatibility fallbacks (deprecated)
|
|
55
|
-
/** @deprecated use [ToolName.PatternDetect] */
|
|
56
|
-
patternDetect?: any;
|
|
57
|
-
/** @deprecated use [ToolName.ContextAnalyzer] */
|
|
58
|
-
contextAnalyzer?: any;
|
|
59
38
|
}
|
|
60
39
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Mapping between ToolName and @aiready/ package names.
|
|
42
|
+
* Used for dynamic registration on-demand.
|
|
43
|
+
*/
|
|
44
|
+
const TOOL_PACKAGE_MAP: Record<string, string> = {
|
|
45
|
+
[ToolName.PatternDetect]: '@aiready/pattern-detect',
|
|
46
|
+
[ToolName.ContextAnalyzer]: '@aiready/context-analyzer',
|
|
47
|
+
[ToolName.NamingConsistency]: '@aiready/consistency',
|
|
48
|
+
[ToolName.AiSignalClarity]: '@aiready/ai-signal-clarity',
|
|
49
|
+
[ToolName.AgentGrounding]: '@aiready/agent-grounding',
|
|
50
|
+
[ToolName.TestabilityIndex]: '@aiready/testability',
|
|
51
|
+
[ToolName.DocDrift]: '@aiready/doc-drift',
|
|
52
|
+
[ToolName.DependencyHealth]: '@aiready/deps',
|
|
53
|
+
[ToolName.ChangeAmplification]: '@aiready/change-amplification',
|
|
54
|
+
// Aliases handled by registry
|
|
55
|
+
patterns: '@aiready/pattern-detect',
|
|
56
|
+
context: '@aiready/context-analyzer',
|
|
57
|
+
consistency: '@aiready/consistency',
|
|
67
58
|
};
|
|
68
59
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
60
|
+
/**
|
|
61
|
+
* AIReady Unified Analysis
|
|
62
|
+
* Orchestrates all registered tools via the ToolRegistry.
|
|
63
|
+
*/
|
|
104
64
|
export async function analyzeUnified(
|
|
105
65
|
options: UnifiedAnalysisOptions
|
|
106
66
|
): Promise<UnifiedAnalysisResult> {
|
|
107
67
|
const startTime = Date.now();
|
|
108
|
-
const
|
|
109
|
-
|
|
68
|
+
const requestedTools = options.tools || [
|
|
69
|
+
'patterns',
|
|
70
|
+
'context',
|
|
71
|
+
'consistency',
|
|
72
|
+
];
|
|
73
|
+
|
|
110
74
|
const result: UnifiedAnalysisResult = {
|
|
111
75
|
summary: {
|
|
112
76
|
totalIssues: 0,
|
|
113
|
-
toolsRun:
|
|
77
|
+
toolsRun: [],
|
|
114
78
|
executionTime: 0,
|
|
115
79
|
},
|
|
116
80
|
};
|
|
117
81
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
82
|
+
for (const toolName of requestedTools) {
|
|
83
|
+
let provider = ToolRegistry.find(toolName);
|
|
84
|
+
|
|
85
|
+
// Dynamic Loading: If provider not found, attempt to import the package
|
|
86
|
+
if (!provider) {
|
|
87
|
+
const packageName =
|
|
88
|
+
TOOL_PACKAGE_MAP[toolName] ||
|
|
89
|
+
(toolName.startsWith('@aiready/') ? toolName : `@aiready/${toolName}`);
|
|
90
|
+
try {
|
|
91
|
+
await import(packageName);
|
|
92
|
+
provider = ToolRegistry.find(toolName);
|
|
93
|
+
if (provider) {
|
|
94
|
+
console.log(
|
|
95
|
+
`โ
Successfully loaded tool provider: ${toolName} from ${packageName}`
|
|
96
|
+
);
|
|
97
|
+
} else {
|
|
98
|
+
console.log(
|
|
99
|
+
`โ ๏ธ Loaded ${packageName} but provider ${toolName} still not found in registry.`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
} catch (err: any) {
|
|
103
|
+
console.log(
|
|
104
|
+
`โ Failed to dynamically load tool ${toolName} (${packageName}):`,
|
|
105
|
+
err.message
|
|
106
|
+
);
|
|
107
|
+
}
|
|
123
108
|
}
|
|
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
109
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
110
|
+
if (!provider) {
|
|
111
|
+
console.warn(
|
|
112
|
+
`โ ๏ธ Warning: Tool provider for '${toolName}' not found. Skipping.`
|
|
113
|
+
);
|
|
114
|
+
continue;
|
|
143
115
|
}
|
|
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
|
-
|
|
161
|
-
result.summary.totalIssues += sorted.length;
|
|
162
|
-
}
|
|
163
116
|
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
}
|
|
117
|
+
try {
|
|
118
|
+
const output = await provider.analyze(options);
|
|
182
119
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
}
|
|
120
|
+
if (options.progressCallback) {
|
|
121
|
+
options.progressCallback({ tool: provider.id, data: output });
|
|
122
|
+
}
|
|
204
123
|
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
}
|
|
124
|
+
result[provider.id] = output;
|
|
125
|
+
result.summary.toolsRun.push(provider.id);
|
|
226
126
|
|
|
227
|
-
|
|
228
|
-
|
|
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),
|
|
127
|
+
const issueCount = output.results.reduce(
|
|
128
|
+
(sum: number, file: any) => sum + (file.issues?.length || 0),
|
|
248
129
|
0
|
|
249
|
-
)
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
-
}
|
|
130
|
+
);
|
|
131
|
+
result.summary.totalIssues += issueCount;
|
|
132
|
+
|
|
133
|
+
// Robust backward compatibility fallbacks
|
|
134
|
+
// 1. Add all aliases as keys (e.g., 'patterns', 'context', 'consistency')
|
|
135
|
+
if (provider.alias && Array.isArray(provider.alias)) {
|
|
136
|
+
for (const alias of provider.alias) {
|
|
137
|
+
if (!result[alias]) {
|
|
138
|
+
(result as any)[alias] = output;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
299
142
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
});
|
|
310
|
-
if (options.progressCallback) {
|
|
311
|
-
options.progressCallback({ tool: 'change-amplification', data: report });
|
|
143
|
+
// 2. Add camelCase version of canonical ID (e.g., 'patternDetect', 'contextAnalyzer')
|
|
144
|
+
const camelCaseId = provider.id.replace(/-([a-z])/g, (g) =>
|
|
145
|
+
g[1].toUpperCase()
|
|
146
|
+
);
|
|
147
|
+
if (camelCaseId !== provider.id && !result[camelCaseId]) {
|
|
148
|
+
(result as any)[camelCaseId] = output;
|
|
149
|
+
}
|
|
150
|
+
} catch (err) {
|
|
151
|
+
console.error(`โ Error running tool '${provider.id}':`, err);
|
|
312
152
|
}
|
|
313
|
-
result[ToolName.ChangeAmplification] = {
|
|
314
|
-
results: report.results || [],
|
|
315
|
-
summary: report.summary || {},
|
|
316
|
-
};
|
|
317
|
-
result.summary.totalIssues += report.summary?.totalIssues || 0;
|
|
318
153
|
}
|
|
319
154
|
|
|
320
155
|
result.summary.executionTime = Date.now() - startTime;
|
|
321
156
|
return result;
|
|
322
157
|
}
|
|
323
158
|
|
|
159
|
+
/**
|
|
160
|
+
* AIReady Unified Scoring
|
|
161
|
+
* Calculates scores for all analyzed tools.
|
|
162
|
+
*/
|
|
324
163
|
export async function scoreUnified(
|
|
325
164
|
results: UnifiedAnalysisResult,
|
|
326
165
|
options: UnifiedAnalysisOptions
|
|
327
166
|
): Promise<ScoringResult> {
|
|
328
167
|
const toolScores: Map<string, ToolScoringOutput> = new Map();
|
|
329
168
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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
|
-
});
|
|
169
|
+
for (const toolId of results.summary.toolsRun) {
|
|
170
|
+
const provider = ToolRegistry.get(toolId as ToolName);
|
|
171
|
+
if (!provider) continue;
|
|
377
172
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
void err;
|
|
381
|
-
}
|
|
382
|
-
}
|
|
173
|
+
const output = results[toolId];
|
|
174
|
+
if (!output) continue;
|
|
383
175
|
|
|
384
|
-
// Consistency score
|
|
385
|
-
if (results[ToolName.NamingConsistency]) {
|
|
386
|
-
const data = results[ToolName.NamingConsistency];
|
|
387
|
-
const { calculateConsistencyScore } = await import('@aiready/consistency');
|
|
388
176
|
try {
|
|
389
|
-
const
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
const agScore = calculateGroundingScore(results[ToolName.AgentGrounding]);
|
|
418
|
-
toolScores.set(ToolName.AgentGrounding, agScore);
|
|
419
|
-
} catch (err) {
|
|
420
|
-
void err;
|
|
421
|
-
}
|
|
422
|
-
}
|
|
177
|
+
const toolScore = provider.score(output, options);
|
|
178
|
+
|
|
179
|
+
// Special handling for token budget calculation if not provided by tool
|
|
180
|
+
if (!toolScore.tokenBudget) {
|
|
181
|
+
if (toolId === ToolName.PatternDetect && (output as any).duplicates) {
|
|
182
|
+
const wastedTokens = (output as any).duplicates.reduce(
|
|
183
|
+
(sum: number, d: any) => sum + (d.tokenCost || 0),
|
|
184
|
+
0
|
|
185
|
+
);
|
|
186
|
+
toolScore.tokenBudget = calculateTokenBudget({
|
|
187
|
+
totalContextTokens: wastedTokens * 2,
|
|
188
|
+
wastedTokens: {
|
|
189
|
+
duplication: wastedTokens,
|
|
190
|
+
fragmentation: 0,
|
|
191
|
+
chattiness: 0,
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
} else if (toolId === ToolName.ContextAnalyzer && output.summary) {
|
|
195
|
+
toolScore.tokenBudget = calculateTokenBudget({
|
|
196
|
+
totalContextTokens: output.summary.totalTokens,
|
|
197
|
+
wastedTokens: {
|
|
198
|
+
duplication: 0,
|
|
199
|
+
fragmentation: output.summary.totalPotentialSavings || 0,
|
|
200
|
+
chattiness: 0,
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
423
205
|
|
|
424
|
-
|
|
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);
|
|
206
|
+
toolScores.set(toolId, toolScore);
|
|
432
207
|
} catch (err) {
|
|
433
|
-
|
|
208
|
+
console.error(`โ Error scoring tool '${toolId}':`, err);
|
|
434
209
|
}
|
|
435
210
|
}
|
|
436
211
|
|
|
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
212
|
// Handle case where toolScores is empty
|
|
492
213
|
if (toolScores.size === 0) {
|
|
493
214
|
return {
|
|
@@ -507,6 +228,9 @@ export async function scoreUnified(
|
|
|
507
228
|
return calculateOverallScore(toolScores, options, undefined);
|
|
508
229
|
}
|
|
509
230
|
|
|
231
|
+
/**
|
|
232
|
+
* Generate human-readable summary of unified results
|
|
233
|
+
*/
|
|
510
234
|
export function generateUnifiedSummary(result: UnifiedAnalysisResult): string {
|
|
511
235
|
const { summary } = result;
|
|
512
236
|
let output = `๐ AIReady Analysis Complete\n\n`;
|
|
@@ -515,40 +239,15 @@ export function generateUnifiedSummary(result: UnifiedAnalysisResult): string {
|
|
|
515
239
|
output += ` Total issues found: ${summary.totalIssues}\n`;
|
|
516
240
|
output += ` Execution time: ${(summary.executionTime / 1000).toFixed(2)}s\n\n`;
|
|
517
241
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
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`;
|
|
242
|
+
for (const provider of ToolRegistry.getAll()) {
|
|
243
|
+
const toolResult = result[provider.id];
|
|
244
|
+
if (toolResult) {
|
|
245
|
+
const issueCount = toolResult.results.reduce(
|
|
246
|
+
(sum: number, r: any) => sum + (r.issues?.length || 0),
|
|
247
|
+
0
|
|
248
|
+
);
|
|
249
|
+
output += `โข ${provider.id}: ${issueCount} issues\n`;
|
|
250
|
+
}
|
|
552
251
|
}
|
|
553
252
|
|
|
554
253
|
return output;
|