@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/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-lint.log +9 -1
- package/.turbo/turbo-test.log +43 -4
- package/dist/chunk-LTUQDJPO.mjs +229 -0
- package/dist/chunk-N56YAZVN.mjs +194 -0
- package/dist/chunk-YBZKPKW3.mjs +161 -0
- package/dist/cli.js +237 -910
- package/dist/cli.mjs +92 -584
- package/dist/index.js +149 -369
- 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/.aiready/aiready-report-20260307-094831.json +50609 -0
- package/src/.aiready/aiready-report-20260307-100539.json +50609 -0
- package/src/.aiready/aiready-report-20260307-103508.json +51329 -0
- package/src/__tests__/cli.test.ts +55 -29
- package/src/commands/scan.ts +116 -700
- package/src/index.ts +191 -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,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
|
-
//
|
|
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;
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
109
|
-
|
|
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:
|
|
102
|
+
toolsRun: [],
|
|
114
103
|
executionTime: 0,
|
|
115
104
|
},
|
|
116
105
|
};
|
|
117
106
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
157
|
+
if (options.progressCallback) {
|
|
158
|
+
options.progressCallback({ tool: provider.id, data: output });
|
|
159
|
+
}
|
|
204
160
|
|
|
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
|
-
}
|
|
161
|
+
result[provider.id] = output;
|
|
162
|
+
result.summary.toolsRun.push(provider.id);
|
|
226
163
|
|
|
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),
|
|
164
|
+
const issueCount = output.results.reduce(
|
|
165
|
+
(sum: number, file: any) => sum + (file.issues?.length || 0),
|
|
248
166
|
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
|
-
}
|
|
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
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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
|
-
|
|
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
|
-
});
|
|
206
|
+
for (const toolId of results.summary.toolsRun) {
|
|
207
|
+
const provider = ToolRegistry.get(toolId as ToolName);
|
|
208
|
+
if (!provider) continue;
|
|
377
209
|
|
|
378
|
-
|
|
379
|
-
|
|
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
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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`;
|
|
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;
|