mcp-lint 0.7.0 → 0.9.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/README.md +34 -7
- package/dist/audit/format.d.ts +5 -0
- package/dist/audit/format.js +246 -0
- package/dist/audit/format.js.map +1 -0
- package/dist/audit/index.d.ts +6 -0
- package/dist/audit/index.js +4 -0
- package/dist/audit/index.js.map +1 -0
- package/dist/audit/report.d.ts +268 -0
- package/dist/audit/report.js +327 -0
- package/dist/audit/report.js.map +1 -0
- package/dist/audit/tokens.d.ts +53 -0
- package/dist/audit/tokens.js +131 -0
- package/dist/audit/tokens.js.map +1 -0
- package/dist/cli/commands/check.js +45 -2
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/compat.js +4 -2
- package/dist/cli/commands/compat.js.map +1 -1
- package/dist/cli/commands/fix.js +6 -5
- package/dist/cli/commands/fix.js.map +1 -1
- package/dist/cli/commands/init.js +1 -2
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/report.d.ts +12 -0
- package/dist/cli/commands/report.js +76 -0
- package/dist/cli/commands/report.js.map +1 -0
- package/dist/core/compat-matrix.d.ts +1 -0
- package/dist/core/compat-matrix.js +2 -1
- package/dist/core/compat-matrix.js.map +1 -1
- package/dist/core/diagnostic.d.ts +3 -1
- package/dist/core/engine.d.ts +1 -0
- package/dist/core/engine.js +33 -17
- package/dist/core/engine.js.map +1 -1
- package/dist/core/fixer.js +4 -3
- package/dist/core/fixer.js.map +1 -1
- package/dist/core/quality-score.d.ts +19 -0
- package/dist/core/quality-score.js +52 -13
- package/dist/core/quality-score.js.map +1 -1
- package/dist/core/rule.d.ts +1 -0
- package/dist/discover/files.d.ts +3 -0
- package/dist/discover/files.js +55 -0
- package/dist/discover/files.js.map +1 -0
- package/dist/discover/format.d.ts +6 -0
- package/dist/discover/format.js +20 -0
- package/dist/discover/format.js.map +1 -0
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/installed/config.js +15 -0
- package/dist/installed/config.js.map +1 -1
- package/dist/installed/lint.d.ts +2 -1
- package/dist/installed/lint.js +15 -0
- package/dist/installed/lint.js.map +1 -1
- package/dist/loaders/map-tools.d.ts +1 -0
- package/dist/loaders/map-tools.js +2 -0
- package/dist/loaders/map-tools.js.map +1 -1
- package/dist/loaders/remote-loader.js +2 -1
- package/dist/loaders/remote-loader.js.map +1 -1
- package/dist/loaders/stdio-loader.js +2 -1
- package/dist/loaders/stdio-loader.js.map +1 -1
- package/dist/mcp-server.js +1 -1
- package/dist/mcp-server.js.map +1 -1
- package/dist/preflight/engine.js +27 -0
- package/dist/preflight/engine.js.map +1 -1
- package/dist/recover/probe.js +2 -1
- package/dist/recover/probe.js.map +1 -1
- package/dist/rules/client-specific/claude.js +1 -0
- package/dist/rules/client-specific/claude.js.map +1 -1
- package/dist/rules/client-specific/cline.js +2 -0
- package/dist/rules/client-specific/cline.js.map +1 -1
- package/dist/rules/client-specific/cursor.js +9 -0
- package/dist/rules/client-specific/cursor.js.map +1 -1
- package/dist/rules/client-specific/openai.js +1 -0
- package/dist/rules/client-specific/openai.js.map +1 -1
- package/dist/rules/index.js +3 -1
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/no-content-encoding.js +1 -0
- package/dist/rules/no-content-encoding.js.map +1 -1
- package/dist/rules/no-empty-enum.js +1 -0
- package/dist/rules/no-empty-enum.js.map +1 -1
- package/dist/rules/no-required-false.js +1 -0
- package/dist/rules/no-required-false.js.map +1 -1
- package/dist/rules/no-unsupported-formats.js +1 -0
- package/dist/rules/no-unsupported-formats.js.map +1 -1
- package/dist/rules/tool-name.d.ts +9 -1
- package/dist/rules/tool-name.js +67 -0
- package/dist/rules/tool-name.js.map +1 -1
- package/dist/version.d.ts +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/examples/github-action.yml +2 -1
- package/package.json +13 -3
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { computeQualityReport } from '../core/quality-score.js';
|
|
3
|
+
import { buildTokenBudget, DEFAULT_TOKEN_THRESHOLDS, } from './tokens.js';
|
|
4
|
+
import { VERSION } from '../version.js';
|
|
5
|
+
export const AUDIT_REPORT_SCHEMA_VERSION = 1;
|
|
6
|
+
export const QUALITY_ALGORITHM = 'quality-v2';
|
|
7
|
+
export class AuditInputError extends Error {
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = 'AuditInputError';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
const severitySchema = z.enum(['error', 'warning', 'info']);
|
|
14
|
+
const findingSchema = z.object({
|
|
15
|
+
ruleId: z.string().min(1),
|
|
16
|
+
severity: severitySchema,
|
|
17
|
+
message: z.string(),
|
|
18
|
+
toolName: z.string(),
|
|
19
|
+
path: z.string(),
|
|
20
|
+
clients: z.array(z.string()),
|
|
21
|
+
fixable: z.boolean(),
|
|
22
|
+
}).strict();
|
|
23
|
+
const auditToolSchema = z.object({
|
|
24
|
+
name: z.string().min(1),
|
|
25
|
+
hasDescription: z.boolean(),
|
|
26
|
+
hasInputSchema: z.boolean(),
|
|
27
|
+
hasOutputSchema: z.boolean(),
|
|
28
|
+
}).strict();
|
|
29
|
+
const tokenBudgetSchema = z.object({
|
|
30
|
+
method: z.literal('chars-div-4'),
|
|
31
|
+
approximate: z.literal(true),
|
|
32
|
+
chars: z.number().int().nonnegative(),
|
|
33
|
+
tokens: z.number().int().nonnegative(),
|
|
34
|
+
toolCount: z.number().int().nonnegative(),
|
|
35
|
+
thresholds: z.object({
|
|
36
|
+
warn: z.number().int().positive(),
|
|
37
|
+
over: z.number().int().positive(),
|
|
38
|
+
schemaWarn: z.number().int().positive(),
|
|
39
|
+
}).strict(),
|
|
40
|
+
level: z.enum(['ok', 'warn', 'over']),
|
|
41
|
+
tools: z.array(z.object({
|
|
42
|
+
name: z.string(),
|
|
43
|
+
chars: z.number().int().nonnegative(),
|
|
44
|
+
tokens: z.number().int().nonnegative(),
|
|
45
|
+
schemaTokens: z.number().int().nonnegative(),
|
|
46
|
+
oversizedSchema: z.boolean(),
|
|
47
|
+
}).strict()),
|
|
48
|
+
repeatedDescriptions: z.array(z.object({
|
|
49
|
+
description: z.string(),
|
|
50
|
+
tools: z.array(z.string()),
|
|
51
|
+
}).strict()),
|
|
52
|
+
note: z.string().min(1),
|
|
53
|
+
}).strict();
|
|
54
|
+
const qualitySchema = z.object({
|
|
55
|
+
algorithm: z.literal(QUALITY_ALGORITHM),
|
|
56
|
+
overall: z.number().int().min(0).max(100),
|
|
57
|
+
grade: z.enum(['A', 'B', 'C', 'D', 'F']),
|
|
58
|
+
weights: z.object({
|
|
59
|
+
description: z.literal(25),
|
|
60
|
+
inputSchema: z.literal(25),
|
|
61
|
+
outputSchema: z.literal(10),
|
|
62
|
+
lintClean: z.literal(40),
|
|
63
|
+
error: z.literal(15),
|
|
64
|
+
warning: z.literal(5),
|
|
65
|
+
}).strict(),
|
|
66
|
+
coverage: z.object({
|
|
67
|
+
toolCount: z.number().int().nonnegative(),
|
|
68
|
+
withDescription: z.number().int().nonnegative(),
|
|
69
|
+
withInputSchema: z.number().int().nonnegative(),
|
|
70
|
+
withOutputSchema: z.number().int().nonnegative(),
|
|
71
|
+
}).strict(),
|
|
72
|
+
confidence: z.enum(['low', 'medium']),
|
|
73
|
+
note: z.string().min(1),
|
|
74
|
+
}).strict();
|
|
75
|
+
function sectionSchema(data) {
|
|
76
|
+
return z.object({
|
|
77
|
+
status: z.enum(['ran', 'not_run', 'partial', 'failed']),
|
|
78
|
+
reason: z.string().min(1).optional(),
|
|
79
|
+
data: data.optional(),
|
|
80
|
+
}).strict().superRefine((value, ctx) => {
|
|
81
|
+
if (value.status === 'ran' && value.data === undefined) {
|
|
82
|
+
ctx.addIssue({ code: 'custom', message: 'A ran section needs data.' });
|
|
83
|
+
}
|
|
84
|
+
if ((value.status === 'not_run' || value.status === 'failed') && (value.reason === undefined || value.data !== undefined)) {
|
|
85
|
+
ctx.addIssue({ code: 'custom', message: 'A not_run or failed section needs a reason and no data.' });
|
|
86
|
+
}
|
|
87
|
+
if (value.status === 'partial' && (value.reason === undefined || value.data === undefined)) {
|
|
88
|
+
ctx.addIssue({ code: 'custom', message: 'A partial section needs a reason and data.' });
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
const notRunSchema = z.object({
|
|
93
|
+
status: z.enum(['not_run', 'failed']),
|
|
94
|
+
reason: z.string().min(1),
|
|
95
|
+
}).strict();
|
|
96
|
+
export const AuditReportSchema = z.object({
|
|
97
|
+
schemaVersion: z.literal(AUDIT_REPORT_SCHEMA_VERSION),
|
|
98
|
+
generatedAt: z.string().min(1),
|
|
99
|
+
tool: z.object({ name: z.literal('mcp-lint'), version: z.string().min(1) }).strict(),
|
|
100
|
+
scope: z.object({
|
|
101
|
+
source: z.string().min(1),
|
|
102
|
+
mode: z.literal('offline-tools'),
|
|
103
|
+
liveServer: z.literal(false),
|
|
104
|
+
toolsInvoked: z.literal(false),
|
|
105
|
+
}).strict(),
|
|
106
|
+
tools: z.array(auditToolSchema),
|
|
107
|
+
duplicateToolNames: z.array(z.string()),
|
|
108
|
+
findings: sectionSchema(z.array(findingSchema)),
|
|
109
|
+
severity: sectionSchema(z.object({
|
|
110
|
+
error: z.number().int().nonnegative(),
|
|
111
|
+
warning: z.number().int().nonnegative(),
|
|
112
|
+
info: z.number().int().nonnegative(),
|
|
113
|
+
}).strict()),
|
|
114
|
+
compatibility: notRunSchema,
|
|
115
|
+
tokenBudget: sectionSchema(tokenBudgetSchema),
|
|
116
|
+
quality: sectionSchema(qualitySchema),
|
|
117
|
+
recovery: notRunSchema,
|
|
118
|
+
preflight: notRunSchema,
|
|
119
|
+
recommendations: z.array(z.string()),
|
|
120
|
+
coverage: z.object({
|
|
121
|
+
ran: z.array(z.string()),
|
|
122
|
+
notRun: z.array(z.string()),
|
|
123
|
+
confidence: z.enum(['low', 'medium', 'high']),
|
|
124
|
+
}).strict(),
|
|
125
|
+
}).strict();
|
|
126
|
+
function notRun(reason) {
|
|
127
|
+
return { status: 'not_run', reason };
|
|
128
|
+
}
|
|
129
|
+
function isRecord(value) {
|
|
130
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
131
|
+
}
|
|
132
|
+
function requireTools(value) {
|
|
133
|
+
if (!Array.isArray(value)) {
|
|
134
|
+
throw new AuditInputError('tools must be an array.');
|
|
135
|
+
}
|
|
136
|
+
return value.map((tool, index) => {
|
|
137
|
+
if (!isRecord(tool))
|
|
138
|
+
throw new AuditInputError(`tools[${index}] must be an object.`);
|
|
139
|
+
if (typeof tool.name !== 'string' || tool.name.trim() === '') {
|
|
140
|
+
throw new AuditInputError(`tools[${index}].name must be a non-empty string.`);
|
|
141
|
+
}
|
|
142
|
+
if (tool.description !== undefined && typeof tool.description !== 'string') {
|
|
143
|
+
throw new AuditInputError(`tools[${index}].description must be a string.`);
|
|
144
|
+
}
|
|
145
|
+
if (tool.inputSchema !== undefined && !isRecord(tool.inputSchema)) {
|
|
146
|
+
throw new AuditInputError(`tools[${index}].inputSchema must be an object.`);
|
|
147
|
+
}
|
|
148
|
+
if (tool.outputSchema !== undefined && !isRecord(tool.outputSchema)) {
|
|
149
|
+
throw new AuditInputError(`tools[${index}].outputSchema must be an object.`);
|
|
150
|
+
}
|
|
151
|
+
return tool;
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
function duplicateNames(tools) {
|
|
155
|
+
const counts = new Map();
|
|
156
|
+
for (const tool of tools) {
|
|
157
|
+
const name = tool.name;
|
|
158
|
+
counts.set(name, (counts.get(name) ?? 0) + 1);
|
|
159
|
+
}
|
|
160
|
+
return [...counts.entries()]
|
|
161
|
+
.filter(([, count]) => count > 1)
|
|
162
|
+
.map(([name]) => name)
|
|
163
|
+
.sort((a, b) => a.localeCompare(b));
|
|
164
|
+
}
|
|
165
|
+
function findingsFrom(diagnostics) {
|
|
166
|
+
return diagnostics
|
|
167
|
+
.map((diagnostic) => ({
|
|
168
|
+
ruleId: diagnostic.ruleId,
|
|
169
|
+
severity: diagnostic.severity,
|
|
170
|
+
message: diagnostic.message,
|
|
171
|
+
toolName: diagnostic.toolName,
|
|
172
|
+
path: diagnostic.path,
|
|
173
|
+
clients: [...diagnostic.clients].sort((a, b) => a.localeCompare(b)),
|
|
174
|
+
fixable: diagnostic.fix != null,
|
|
175
|
+
}))
|
|
176
|
+
.sort((a, b) => a.toolName.localeCompare(b.toolName)
|
|
177
|
+
|| a.path.localeCompare(b.path)
|
|
178
|
+
|| a.ruleId.localeCompare(b.ruleId)
|
|
179
|
+
|| a.message.localeCompare(b.message));
|
|
180
|
+
}
|
|
181
|
+
function recommendations(input) {
|
|
182
|
+
const lines = [];
|
|
183
|
+
if (!input.findingsRan) {
|
|
184
|
+
lines.push('Schema lint did not run. A missing finding list is not a pass.');
|
|
185
|
+
}
|
|
186
|
+
else if ((input.counts?.error ?? 0) > 0) {
|
|
187
|
+
lines.push('Fix error findings before shipping this tools list.');
|
|
188
|
+
}
|
|
189
|
+
if (input.duplicates.length > 0) {
|
|
190
|
+
lines.push(`Rename duplicate tool names: ${input.duplicates.join(', ')}.`);
|
|
191
|
+
}
|
|
192
|
+
if (input.missingDescriptions > 0) {
|
|
193
|
+
lines.push('Add a description to every tool. Clients use it to choose a call.');
|
|
194
|
+
}
|
|
195
|
+
if (input.missingSchemas > 0) {
|
|
196
|
+
lines.push('Add inputSchema to every tool. An absent schema was not scored as valid.');
|
|
197
|
+
}
|
|
198
|
+
if (input.tokenLevel === 'over' || input.tokenLevel === 'warn') {
|
|
199
|
+
lines.push('The approximate token estimate is at or above a configured threshold.');
|
|
200
|
+
}
|
|
201
|
+
if (input.repeated > 0) {
|
|
202
|
+
lines.push('Repeated descriptions hide which tool the agent should call.');
|
|
203
|
+
}
|
|
204
|
+
if (input.toolCount === 0) {
|
|
205
|
+
lines.push('No tools were supplied, so the quality score was withheld.');
|
|
206
|
+
}
|
|
207
|
+
else if (input.findingsRan && (input.counts?.error ?? 0) === 0 && lines.length === 0) {
|
|
208
|
+
lines.push('No schema errors in this run. Recovery, preflight, and live inspection were not measured.');
|
|
209
|
+
}
|
|
210
|
+
return lines;
|
|
211
|
+
}
|
|
212
|
+
export function buildAuditReport(input) {
|
|
213
|
+
if (input.source.trim() === '')
|
|
214
|
+
throw new AuditInputError('source must be a non-empty string.');
|
|
215
|
+
const tools = requireTools(input.tools);
|
|
216
|
+
const inventory = tools.map((tool) => ({
|
|
217
|
+
name: tool.name,
|
|
218
|
+
hasDescription: typeof tool.description === 'string' && tool.description.trim() !== '',
|
|
219
|
+
hasInputSchema: isRecord(tool.inputSchema),
|
|
220
|
+
hasOutputSchema: isRecord(tool.outputSchema),
|
|
221
|
+
}));
|
|
222
|
+
const duplicates = duplicateNames(tools);
|
|
223
|
+
const findingsRan = input.diagnostics !== undefined;
|
|
224
|
+
const findings = findingsRan ? findingsFrom(input.diagnostics ?? []) : undefined;
|
|
225
|
+
const counts = findings
|
|
226
|
+
? {
|
|
227
|
+
error: findings.filter((finding) => finding.severity === 'error').length,
|
|
228
|
+
warning: findings.filter((finding) => finding.severity === 'warning').length,
|
|
229
|
+
info: findings.filter((finding) => finding.severity === 'info').length,
|
|
230
|
+
}
|
|
231
|
+
: undefined;
|
|
232
|
+
let tokenSection;
|
|
233
|
+
try {
|
|
234
|
+
tokenSection = {
|
|
235
|
+
status: 'ran',
|
|
236
|
+
data: buildTokenBudget(tools, input.thresholds ?? DEFAULT_TOKEN_THRESHOLDS),
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
catch (err) {
|
|
240
|
+
tokenSection = {
|
|
241
|
+
status: 'failed',
|
|
242
|
+
reason: err instanceof Error ? err.message : 'Token estimate failed.',
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
let quality;
|
|
246
|
+
if (!findingsRan) {
|
|
247
|
+
quality = notRun('Schema lint did not run, so the quality score was withheld.');
|
|
248
|
+
}
|
|
249
|
+
else if (tools.length === 0) {
|
|
250
|
+
quality = notRun('No tools were supplied, so the quality score was withheld.');
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
const scored = computeQualityReport(tools, input.diagnostics ?? []);
|
|
254
|
+
quality = {
|
|
255
|
+
status: 'ran',
|
|
256
|
+
data: {
|
|
257
|
+
algorithm: QUALITY_ALGORITHM,
|
|
258
|
+
overall: scored.overall,
|
|
259
|
+
grade: scored.grade,
|
|
260
|
+
weights: scored.weights,
|
|
261
|
+
coverage: scored.coverage,
|
|
262
|
+
confidence: scored.confidence,
|
|
263
|
+
note: 'quality-v2 adds points only for a description, an input schema, an output schema, and a clean lint. Missing fields add nothing. This is not a security guarantee.',
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
const tokenLevel = tokenSection.status === 'ran' ? tokenSection.data.level : undefined;
|
|
268
|
+
const lines = recommendations({
|
|
269
|
+
findingsRan,
|
|
270
|
+
counts,
|
|
271
|
+
duplicates,
|
|
272
|
+
missingDescriptions: inventory.filter((tool) => !tool.hasDescription).length,
|
|
273
|
+
missingSchemas: inventory.filter((tool) => !tool.hasInputSchema).length,
|
|
274
|
+
toolCount: tools.length,
|
|
275
|
+
tokenLevel,
|
|
276
|
+
repeated: tokenSection.status === 'ran' ? tokenSection.data.repeatedDescriptions.length : 0,
|
|
277
|
+
});
|
|
278
|
+
const ran = ['tools'];
|
|
279
|
+
if (tokenSection.status === 'ran')
|
|
280
|
+
ran.push('tokenBudget');
|
|
281
|
+
if (findingsRan)
|
|
282
|
+
ran.push('findings', 'severity');
|
|
283
|
+
if (quality.status === 'ran')
|
|
284
|
+
ran.push('quality');
|
|
285
|
+
const notRunNames = ['compatibility', 'recovery', 'preflight'];
|
|
286
|
+
if (!findingsRan)
|
|
287
|
+
notRunNames.push('findings', 'severity');
|
|
288
|
+
if (quality.status !== 'ran')
|
|
289
|
+
notRunNames.push('quality');
|
|
290
|
+
if (tokenSection.status !== 'ran')
|
|
291
|
+
notRunNames.push('tokenBudget');
|
|
292
|
+
const confidence = tools.length === 0 || !findingsRan
|
|
293
|
+
? 'low'
|
|
294
|
+
: quality.status === 'ran' && tokenSection.status === 'ran'
|
|
295
|
+
? 'medium'
|
|
296
|
+
: 'low';
|
|
297
|
+
return {
|
|
298
|
+
schemaVersion: AUDIT_REPORT_SCHEMA_VERSION,
|
|
299
|
+
generatedAt: input.generatedAt ?? new Date().toISOString(),
|
|
300
|
+
tool: { name: 'mcp-lint', version: input.version ?? VERSION },
|
|
301
|
+
scope: {
|
|
302
|
+
source: input.source,
|
|
303
|
+
mode: 'offline-tools',
|
|
304
|
+
liveServer: false,
|
|
305
|
+
toolsInvoked: false,
|
|
306
|
+
},
|
|
307
|
+
tools: inventory,
|
|
308
|
+
duplicateToolNames: duplicates,
|
|
309
|
+
findings: findings ? { status: 'ran', data: findings } : notRun('Schema lint did not run.'),
|
|
310
|
+
severity: counts ? { status: 'ran', data: counts } : notRun('Schema lint did not run.'),
|
|
311
|
+
compatibility: notRun('Client compatibility was not re-checked for this report.'),
|
|
312
|
+
tokenBudget: tokenSection,
|
|
313
|
+
quality,
|
|
314
|
+
recovery: notRun('Recovery was not scored.'),
|
|
315
|
+
preflight: notRun('Preflight was not evaluated.'),
|
|
316
|
+
recommendations: lines,
|
|
317
|
+
coverage: {
|
|
318
|
+
ran,
|
|
319
|
+
notRun: notRunNames,
|
|
320
|
+
confidence,
|
|
321
|
+
},
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
export function parseAuditReport(value) {
|
|
325
|
+
return AuditReportSchema.parse(value);
|
|
326
|
+
}
|
|
327
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/audit/report.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAEhE,OAAO,EACL,gBAAgB,EAChB,wBAAwB,GAGzB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAU,CAAC;AACtD,MAAM,CAAC,MAAM,iBAAiB,GAAG,YAAqB,CAAC;AAEvD,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACxC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AAuGD,MAAM,cAAc,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;AAE5D,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACzB,QAAQ,EAAE,cAAc;IACxB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC5B,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;CACrB,CAAC,CAAC,MAAM,EAAE,CAAC;AAEZ,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,cAAc,EAAE,CAAC,CAAC,OAAO,EAAE;IAC3B,cAAc,EAAE,CAAC,CAAC,OAAO,EAAE;IAC3B,eAAe,EAAE,CAAC,CAAC,OAAO,EAAE;CAC7B,CAAC,CAAC,MAAM,EAAE,CAAC;AAEZ,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC;IAChC,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;IAC5B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IACrC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IACtC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IACzC,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC;QACnB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;QACjC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;QACjC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;KACxC,CAAC,CAAC,MAAM,EAAE;IACX,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;QACtB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;QACrC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;QACtC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;QAC5C,eAAe,EAAE,CAAC,CAAC,OAAO,EAAE;KAC7B,CAAC,CAAC,MAAM,EAAE,CAAC;IACZ,oBAAoB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;QACrC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;QACvB,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;KAC3B,CAAC,CAAC,MAAM,EAAE,CAAC;IACZ,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CACxB,CAAC,CAAC,MAAM,EAAE,CAAC;AAEZ,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7B,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC;IACvC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;IACzC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IACxC,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC;QAChB,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1B,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1B,YAAY,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;QACxB,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;QACpB,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;KACtB,CAAC,CAAC,MAAM,EAAE;IACX,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC;QACjB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;QACzC,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;QAC/C,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;QAC/C,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;KACjD,CAAC,CAAC,MAAM,EAAE;IACX,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACrC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CACxB,CAAC,CAAC,MAAM,EAAE,CAAC;AAEZ,SAAS,aAAa,CAAsB,IAAO;IACjD,OAAO,CAAC,CAAC,MAAM,CAAC;QACd,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;QACvD,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;QACpC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE;KACtB,CAAC,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACrC,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACvD,GAAG,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,2BAA2B,EAAE,CAAC,CAAC;QACzE,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,EAAE,CAAC;YAC1H,GAAG,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,yDAAyD,EAAE,CAAC,CAAC;QACvG,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,EAAE,CAAC;YAC3F,GAAG,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,4CAA4C,EAAE,CAAC,CAAC;QAC1F,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5B,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACrC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CAC1B,CAAC,CAAC,MAAM,EAAE,CAAC;AAEZ,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,aAAa,EAAE,CAAC,CAAC,OAAO,CAAC,2BAA2B,CAAC;IACrD,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9B,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE;IACpF,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC;QACd,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QACzB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC;QAChC,UAAU,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;QAC5B,YAAY,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;KAC/B,CAAC,CAAC,MAAM,EAAE;IACX,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC;IAC/B,kBAAkB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACvC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAC/C,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC;QAC/B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;QACrC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;QACvC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;KACrC,CAAC,CAAC,MAAM,EAAE,CAAC;IACZ,aAAa,EAAE,YAAY;IAC3B,WAAW,EAAE,aAAa,CAAC,iBAAiB,CAAC;IAC7C,OAAO,EAAE,aAAa,CAAC,aAAa,CAAC;IACrC,QAAQ,EAAE,YAAY;IACtB,SAAS,EAAE,YAAY;IACvB,eAAe,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACpC,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC;QACjB,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QACxB,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QAC3B,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;KAC9C,CAAC,CAAC,MAAM,EAAE;CACZ,CAAC,CAAC,MAAM,EAAE,CAAC;AAEZ,SAAS,MAAM,CAAC,MAAc;IAC5B,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;AACvC,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,eAAe,CAAC,yBAAyB,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QAC/B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,eAAe,CAAC,SAAS,KAAK,sBAAsB,CAAC,CAAC;QACrF,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YAC7D,MAAM,IAAI,eAAe,CAAC,SAAS,KAAK,oCAAoC,CAAC,CAAC;QAChF,CAAC;QACD,IAAI,IAAI,CAAC,WAAW,KAAK,SAAS,IAAI,OAAO,IAAI,CAAC,WAAW,KAAK,QAAQ,EAAE,CAAC;YAC3E,MAAM,IAAI,eAAe,CAAC,SAAS,KAAK,iCAAiC,CAAC,CAAC;QAC7E,CAAC;QACD,IAAI,IAAI,CAAC,WAAW,KAAK,SAAS,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;YAClE,MAAM,IAAI,eAAe,CAAC,SAAS,KAAK,kCAAkC,CAAC,CAAC;QAC9E,CAAC;QACD,IAAI,IAAI,CAAC,YAAY,KAAK,SAAS,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;YACpE,MAAM,IAAI,eAAe,CAAC,SAAS,KAAK,mCAAmC,CAAC,CAAC;QAC/E,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,cAAc,CAAC,KAA6C;IACnE,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAc,CAAC;QACjC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,CAAC,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;SACzB,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC;SAChC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC;SACrB,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;AACxC,CAAC;AAED,SAAS,YAAY,CAAC,WAAyB;IAC7C,OAAO,WAAW;SACf,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QACpB,MAAM,EAAE,UAAU,CAAC,MAAM;QACzB,QAAQ,EAAE,UAAU,CAAC,QAAQ;QAC7B,OAAO,EAAE,UAAU,CAAC,OAAO;QAC3B,QAAQ,EAAE,UAAU,CAAC,QAAQ;QAC7B,IAAI,EAAE,UAAU,CAAC,IAAI;QACrB,OAAO,EAAE,CAAC,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;QACnE,OAAO,EAAE,UAAU,CAAC,GAAG,IAAI,IAAI;KAChC,CAAC,CAAC;SACF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACb,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC;WACjC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC;WAC5B,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC;WAChC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CACtC,CAAC;AACN,CAAC;AAED,SAAS,eAAe,CAAC,KASxB;IACC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,gEAAgE,CAAC,CAAC;IAC/E,CAAC;SAAM,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1C,KAAK,CAAC,IAAI,CAAC,qDAAqD,CAAC,CAAC;IACpE,CAAC;IACD,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,gCAAgC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC7E,CAAC;IACD,IAAI,KAAK,CAAC,mBAAmB,GAAG,CAAC,EAAE,CAAC;QAClC,KAAK,CAAC,IAAI,CAAC,mEAAmE,CAAC,CAAC;IAClF,CAAC;IACD,IAAI,KAAK,CAAC,cAAc,GAAG,CAAC,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,0EAA0E,CAAC,CAAC;IACzF,CAAC;IACD,IAAI,KAAK,CAAC,UAAU,KAAK,MAAM,IAAI,KAAK,CAAC,UAAU,KAAK,MAAM,EAAE,CAAC;QAC/D,KAAK,CAAC,IAAI,CAAC,uEAAuE,CAAC,CAAC;IACtF,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,8DAA8D,CAAC,CAAC;IAC7E,CAAC;IACD,IAAI,KAAK,CAAC,SAAS,KAAK,CAAC,EAAE,CAAC;QAC1B,KAAK,CAAC,IAAI,CAAC,4DAA4D,CAAC,CAAC;IAC3E,CAAC;SAAM,IAAI,KAAK,CAAC,WAAW,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvF,KAAK,CAAC,IAAI,CAAC,2FAA2F,CAAC,CAAC;IAC1G,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAA4B;IAC3D,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,MAAM,IAAI,eAAe,CAAC,oCAAoC,CAAC,CAAC;IAChG,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,SAAS,GAAgB,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAClD,IAAI,EAAE,IAAI,CAAC,IAAc;QACzB,cAAc,EAAE,OAAO,IAAI,CAAC,WAAW,KAAK,QAAQ,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE;QACtF,cAAc,EAAE,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC;QAC1C,eAAe,EAAE,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC;KAC7C,CAAC,CAAC,CAAC;IACJ,MAAM,UAAU,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IACzC,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,KAAK,SAAS,CAAC;IACpD,MAAM,QAAQ,GAAG,WAAW,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACjF,MAAM,MAAM,GAAG,QAAQ;QACrB,CAAC,CAAC;YACE,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,MAAM;YACxE,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,MAAM;YAC5E,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,MAAM;SACvE;QACH,CAAC,CAAC,SAAS,CAAC;IAEd,IAAI,YAAkC,CAAC;IACvC,IAAI,CAAC;QACH,YAAY,GAAG;YACb,MAAM,EAAE,KAAK;YACb,IAAI,EAAE,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,IAAI,wBAAwB,CAAC;SAC5E,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,YAAY,GAAG;YACb,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB;SACtE,CAAC;IACJ,CAAC;IAED,IAAI,OAAoC,CAAC;IACzC,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,GAAG,MAAM,CAAC,6DAA6D,CAAC,CAAC;IAClF,CAAC;SAAM,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,GAAG,MAAM,CAAC,4DAA4D,CAAC,CAAC;IACjF,CAAC;SAAM,CAAC;QACN,MAAM,MAAM,GAAG,oBAAoB,CAAC,KAA6B,EAAE,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;QAC5F,OAAO,GAAG;YACR,MAAM,EAAE,KAAK;YACb,IAAI,EAAE;gBACJ,SAAS,EAAE,iBAAiB;gBAC5B,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,IAAI,EAAE,mKAAmK;aAC1K;SACF,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,YAAY,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IACvF,MAAM,KAAK,GAAG,eAAe,CAAC;QAC5B,WAAW;QACX,MAAM;QACN,UAAU;QACV,mBAAmB,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,MAAM;QAC5E,cAAc,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,MAAM;QACvE,SAAS,EAAE,KAAK,CAAC,MAAM;QACvB,UAAU;QACV,QAAQ,EAAE,YAAY,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;KAC5F,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC;IACtB,IAAI,YAAY,CAAC,MAAM,KAAK,KAAK;QAAE,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC3D,IAAI,WAAW;QAAE,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAClD,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK;QAAE,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAClD,MAAM,WAAW,GAAG,CAAC,eAAe,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;IAC/D,IAAI,CAAC,WAAW;QAAE,WAAW,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAC3D,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK;QAAE,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC1D,IAAI,YAAY,CAAC,MAAM,KAAK,KAAK;QAAE,WAAW,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAEnE,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,WAAW;QACnD,CAAC,CAAC,KAAK;QACP,CAAC,CAAC,OAAO,CAAC,MAAM,KAAK,KAAK,IAAI,YAAY,CAAC,MAAM,KAAK,KAAK;YACzD,CAAC,CAAC,QAAQ;YACV,CAAC,CAAC,KAAK,CAAC;IAEZ,OAAO;QACL,aAAa,EAAE,2BAA2B;QAC1C,WAAW,EAAE,KAAK,CAAC,WAAW,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAC1D,IAAI,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,OAAO,EAAE;QAC7D,KAAK,EAAE;YACL,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,IAAI,EAAE,eAAe;YACrB,UAAU,EAAE,KAAK;YACjB,YAAY,EAAE,KAAK;SACpB;QACD,KAAK,EAAE,SAAS;QAChB,kBAAkB,EAAE,UAAU;QAC9B,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,0BAA0B,CAAC;QAC3F,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,0BAA0B,CAAC;QACvF,aAAa,EAAE,MAAM,CAAC,0DAA0D,CAAC;QACjF,WAAW,EAAE,YAAY;QACzB,OAAO;QACP,QAAQ,EAAE,MAAM,CAAC,0BAA0B,CAAC;QAC5C,SAAS,EAAE,MAAM,CAAC,8BAA8B,CAAC;QACjD,eAAe,EAAE,KAAK;QACtB,QAAQ,EAAE;YACR,GAAG;YACH,MAAM,EAAE,WAAW;YACnB,UAAU;SACX;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,OAAO,iBAAiB,CAAC,KAAK,CAAC,KAAK,CAAmB,CAAC;AAC1D,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export declare const TOKEN_ESTIMATE_METHOD: "chars-div-4";
|
|
2
|
+
export declare class TokenEstimateError extends Error {
|
|
3
|
+
constructor(message: string);
|
|
4
|
+
}
|
|
5
|
+
export interface TokenEstimate {
|
|
6
|
+
method: typeof TOKEN_ESTIMATE_METHOD;
|
|
7
|
+
approximate: true;
|
|
8
|
+
chars: number;
|
|
9
|
+
tokens: number;
|
|
10
|
+
}
|
|
11
|
+
export interface TokenThresholds {
|
|
12
|
+
warn: number;
|
|
13
|
+
over: number;
|
|
14
|
+
schemaWarn: number;
|
|
15
|
+
}
|
|
16
|
+
export interface ToolTokenRank {
|
|
17
|
+
name: string;
|
|
18
|
+
chars: number;
|
|
19
|
+
tokens: number;
|
|
20
|
+
schemaTokens: number;
|
|
21
|
+
oversizedSchema: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface RepeatedDescription {
|
|
24
|
+
description: string;
|
|
25
|
+
tools: string[];
|
|
26
|
+
}
|
|
27
|
+
export interface TokenBudget {
|
|
28
|
+
method: typeof TOKEN_ESTIMATE_METHOD;
|
|
29
|
+
approximate: true;
|
|
30
|
+
chars: number;
|
|
31
|
+
tokens: number;
|
|
32
|
+
toolCount: number;
|
|
33
|
+
thresholds: TokenThresholds;
|
|
34
|
+
level: 'ok' | 'warn' | 'over';
|
|
35
|
+
tools: ToolTokenRank[];
|
|
36
|
+
repeatedDescriptions: RepeatedDescription[];
|
|
37
|
+
note: string;
|
|
38
|
+
}
|
|
39
|
+
export interface TokenBudgetDiff {
|
|
40
|
+
method: typeof TOKEN_ESTIMATE_METHOD;
|
|
41
|
+
approximate: true;
|
|
42
|
+
beforeTokens: number;
|
|
43
|
+
afterTokens: number;
|
|
44
|
+
deltaTokens: number;
|
|
45
|
+
addedTools: string[];
|
|
46
|
+
removedTools: string[];
|
|
47
|
+
grew: string[];
|
|
48
|
+
}
|
|
49
|
+
export declare const DEFAULT_TOKEN_THRESHOLDS: TokenThresholds;
|
|
50
|
+
export declare function estimateTokens(value: unknown): TokenEstimate;
|
|
51
|
+
export declare function assertThresholds(thresholds: TokenThresholds): void;
|
|
52
|
+
export declare function buildTokenBudget(tools: ReadonlyArray<Record<string, unknown>>, thresholds?: TokenThresholds): TokenBudget;
|
|
53
|
+
export declare function compareTokenBudgets(before: TokenBudget, after: TokenBudget): TokenBudgetDiff;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
export const TOKEN_ESTIMATE_METHOD = 'chars-div-4';
|
|
2
|
+
export class TokenEstimateError extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = 'TokenEstimateError';
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export const DEFAULT_TOKEN_THRESHOLDS = {
|
|
9
|
+
warn: 4000,
|
|
10
|
+
over: 8000,
|
|
11
|
+
schemaWarn: 800,
|
|
12
|
+
};
|
|
13
|
+
const PROJECTION_KEYS = ['name', 'description', 'inputSchema', 'outputSchema'];
|
|
14
|
+
export function estimateTokens(value) {
|
|
15
|
+
let json;
|
|
16
|
+
try {
|
|
17
|
+
json = JSON.stringify(value);
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
throw new TokenEstimateError(err instanceof Error ? err.message : 'Value could not be serialized.');
|
|
21
|
+
}
|
|
22
|
+
if (typeof json !== 'string') {
|
|
23
|
+
throw new TokenEstimateError('Value could not be serialized.');
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
method: TOKEN_ESTIMATE_METHOD,
|
|
27
|
+
approximate: true,
|
|
28
|
+
chars: json.length,
|
|
29
|
+
tokens: Math.ceil(json.length / 4),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function assertThresholds(thresholds) {
|
|
33
|
+
for (const [key, value] of Object.entries(thresholds)) {
|
|
34
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
35
|
+
throw new TokenEstimateError(`Token threshold "${key}" must be a positive integer.`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (thresholds.warn > thresholds.over) {
|
|
39
|
+
throw new TokenEstimateError('Token warn threshold cannot be above the over threshold.');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function isRecord(value) {
|
|
43
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
44
|
+
}
|
|
45
|
+
function projectTool(tool) {
|
|
46
|
+
const projected = {};
|
|
47
|
+
for (const key of PROJECTION_KEYS) {
|
|
48
|
+
if (tool[key] !== undefined)
|
|
49
|
+
projected[key] = tool[key];
|
|
50
|
+
}
|
|
51
|
+
return projected;
|
|
52
|
+
}
|
|
53
|
+
function normalizeDescription(value) {
|
|
54
|
+
if (typeof value !== 'string')
|
|
55
|
+
return undefined;
|
|
56
|
+
const normalized = value.trim().replace(/\s+/g, ' ').toLowerCase();
|
|
57
|
+
return normalized.length >= 12 ? normalized : undefined;
|
|
58
|
+
}
|
|
59
|
+
export function buildTokenBudget(tools, thresholds = DEFAULT_TOKEN_THRESHOLDS) {
|
|
60
|
+
assertThresholds(thresholds);
|
|
61
|
+
const projected = tools.map(projectTool);
|
|
62
|
+
const total = estimateTokens(projected);
|
|
63
|
+
const ranked = tools.map((tool) => {
|
|
64
|
+
const name = typeof tool.name === 'string' ? tool.name : '';
|
|
65
|
+
const whole = estimateTokens(projectTool(tool));
|
|
66
|
+
const schema = estimateTokens(isRecord(tool.inputSchema) ? tool.inputSchema : {});
|
|
67
|
+
return {
|
|
68
|
+
name,
|
|
69
|
+
chars: whole.chars,
|
|
70
|
+
tokens: whole.tokens,
|
|
71
|
+
schemaTokens: schema.tokens,
|
|
72
|
+
oversizedSchema: schema.tokens >= thresholds.schemaWarn,
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
ranked.sort((a, b) => b.tokens - a.tokens || a.name.localeCompare(b.name));
|
|
76
|
+
const groups = new Map();
|
|
77
|
+
for (const tool of tools) {
|
|
78
|
+
if (typeof tool.description !== 'string' || typeof tool.name !== 'string')
|
|
79
|
+
continue;
|
|
80
|
+
const normalized = normalizeDescription(tool.description);
|
|
81
|
+
if (!normalized)
|
|
82
|
+
continue;
|
|
83
|
+
const group = groups.get(normalized) ?? {
|
|
84
|
+
description: tool.description.trim().replace(/\s+/g, ' '),
|
|
85
|
+
tools: [],
|
|
86
|
+
};
|
|
87
|
+
group.tools.push(tool.name);
|
|
88
|
+
groups.set(normalized, group);
|
|
89
|
+
}
|
|
90
|
+
const repeatedDescriptions = [...groups.values()]
|
|
91
|
+
.filter((group) => group.tools.length > 1)
|
|
92
|
+
.map((group) => ({ description: group.description, tools: [...group.tools].sort((a, b) => a.localeCompare(b)) }))
|
|
93
|
+
.sort((a, b) => a.description.localeCompare(b.description));
|
|
94
|
+
const level = total.tokens >= thresholds.over ? 'over' : total.tokens >= thresholds.warn ? 'warn' : 'ok';
|
|
95
|
+
const note = tools.length === 0
|
|
96
|
+
? 'Empty tool list. Zero tokens is not a healthy server. Estimate is ceil(JSON chars / 4), not a tokenizer.'
|
|
97
|
+
: 'Estimate is ceil(JSON chars / 4) of name, description, inputSchema, and outputSchema. It is not a tokenizer count. Per-tool tokens do not sum to the total.';
|
|
98
|
+
return {
|
|
99
|
+
method: TOKEN_ESTIMATE_METHOD,
|
|
100
|
+
approximate: true,
|
|
101
|
+
chars: total.chars,
|
|
102
|
+
tokens: total.tokens,
|
|
103
|
+
toolCount: tools.length,
|
|
104
|
+
thresholds: { ...thresholds },
|
|
105
|
+
level,
|
|
106
|
+
tools: ranked,
|
|
107
|
+
repeatedDescriptions,
|
|
108
|
+
note,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
export function compareTokenBudgets(before, after) {
|
|
112
|
+
const beforeNames = new Set(before.tools.map((tool) => tool.name));
|
|
113
|
+
const afterNames = new Map(after.tools.map((tool) => [tool.name, tool.tokens]));
|
|
114
|
+
const beforeTokens = new Map(before.tools.map((tool) => [tool.name, tool.tokens]));
|
|
115
|
+
const addedTools = [...afterNames.keys()].filter((name) => !beforeNames.has(name)).sort((a, b) => a.localeCompare(b));
|
|
116
|
+
const removedTools = [...beforeNames].filter((name) => !afterNames.has(name)).sort((a, b) => a.localeCompare(b));
|
|
117
|
+
const grew = [...afterNames.keys()]
|
|
118
|
+
.filter((name) => beforeTokens.has(name) && (afterNames.get(name) ?? 0) > (beforeTokens.get(name) ?? 0))
|
|
119
|
+
.sort((a, b) => a.localeCompare(b));
|
|
120
|
+
return {
|
|
121
|
+
method: TOKEN_ESTIMATE_METHOD,
|
|
122
|
+
approximate: true,
|
|
123
|
+
beforeTokens: before.tokens,
|
|
124
|
+
afterTokens: after.tokens,
|
|
125
|
+
deltaTokens: after.tokens - before.tokens,
|
|
126
|
+
addedTools,
|
|
127
|
+
removedTools,
|
|
128
|
+
grew,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../src/audit/tokens.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { watch } from 'node:fs';
|
|
2
|
+
import { stat } from 'node:fs/promises';
|
|
2
3
|
import { homedir } from 'node:os';
|
|
4
|
+
import { resolve } from 'node:path';
|
|
5
|
+
import { discoverToolFiles, missingToolsMessage } from '../../discover/files.js';
|
|
6
|
+
import { formatDiscovered } from '../../discover/format.js';
|
|
3
7
|
import { loadFile } from '../../loaders/file-loader.js';
|
|
4
8
|
import { loadStdio } from '../../loaders/stdio-loader.js';
|
|
5
9
|
import { loadRemote } from '../../loaders/remote-loader.js';
|
|
@@ -23,7 +27,7 @@ function filterBySeverity(diagnostics, min) {
|
|
|
23
27
|
}
|
|
24
28
|
export function registerCheckCommand(program) {
|
|
25
29
|
program
|
|
26
|
-
.command('check [input]')
|
|
30
|
+
.command('check [input]', { isDefault: true })
|
|
27
31
|
.description('Check MCP tool schemas for compatibility issues')
|
|
28
32
|
.option('--format <format>', 'Output format: terminal|json|markdown', 'terminal')
|
|
29
33
|
.option('--no-color', 'Disable terminal colors')
|
|
@@ -87,6 +91,7 @@ export function registerCheckCommand(program) {
|
|
|
87
91
|
const reports = await lintInstalledServers(collected.servers, {
|
|
88
92
|
remote: Boolean(options.remote),
|
|
89
93
|
timeoutMs,
|
|
94
|
+
rules: config.rules,
|
|
90
95
|
lint: (found) => filterBySeverity(engine.lint(found), floor),
|
|
91
96
|
});
|
|
92
97
|
for (const problem of collected.errors) {
|
|
@@ -118,6 +123,26 @@ export function registerCheckCommand(program) {
|
|
|
118
123
|
throw new Error('--server http requires --url <url>');
|
|
119
124
|
tools = await loadRemote(options.url, 'http', optionalTimeout(options.timeout));
|
|
120
125
|
}
|
|
126
|
+
else if (await shouldDiscover(input)) {
|
|
127
|
+
const dir = input ? resolve(input) : process.cwd();
|
|
128
|
+
const files = await discoverToolFiles(dir);
|
|
129
|
+
if (files.length === 0)
|
|
130
|
+
throw new Error(missingToolsMessage(dir));
|
|
131
|
+
const pluginRules = await loadPluginRules(fileConfig);
|
|
132
|
+
const engine = new LintEngine([...allRules, ...pluginRules], config);
|
|
133
|
+
const groups = [];
|
|
134
|
+
const discoveredTools = [];
|
|
135
|
+
const discoveredDiagnostics = [];
|
|
136
|
+
for (const file of files) {
|
|
137
|
+
const found = await loadFile(file);
|
|
138
|
+
const foundDiagnostics = filterBySeverity(engine.lint(found), floor);
|
|
139
|
+
discoveredTools.push(...found);
|
|
140
|
+
discoveredDiagnostics.push(...foundDiagnostics);
|
|
141
|
+
groups.push({ file, diagnostics: foundDiagnostics });
|
|
142
|
+
}
|
|
143
|
+
await emit(formatDiscovered(groups, options.format, !options.color), options, discoveredTools, discoveredDiagnostics);
|
|
144
|
+
return discoveredDiagnostics.some((item) => item.severity === 'error');
|
|
145
|
+
}
|
|
121
146
|
else {
|
|
122
147
|
if (!input)
|
|
123
148
|
throw new Error('Specify an input file, --server stdio|sse|http, --installed, or --from <mcp.json>');
|
|
@@ -166,6 +191,18 @@ export function registerCheckCommand(program) {
|
|
|
166
191
|
}
|
|
167
192
|
});
|
|
168
193
|
}
|
|
194
|
+
async function shouldDiscover(input) {
|
|
195
|
+
if (!input)
|
|
196
|
+
return true;
|
|
197
|
+
try {
|
|
198
|
+
return (await stat(input)).isDirectory();
|
|
199
|
+
}
|
|
200
|
+
catch (err) {
|
|
201
|
+
if (err.code === 'ENOENT')
|
|
202
|
+
return false;
|
|
203
|
+
throw err;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
169
206
|
function collectPath(value, previous) {
|
|
170
207
|
previous.push(value);
|
|
171
208
|
return previous;
|
|
@@ -222,6 +259,12 @@ async function emit(output, options, tools, diagnostics) {
|
|
|
222
259
|
for (const tool of report.tools) {
|
|
223
260
|
process.stdout.write(` ${tool.toolName.padEnd(30)} ${String(tool.score).padStart(3)}/100 ${gradeColor(tool.grade)} (${tool.errors} errors, ${tool.warnings} warnings)\n`);
|
|
224
261
|
}
|
|
225
|
-
|
|
262
|
+
const coverage = `${report.coverage.withDescription}/${report.coverage.toolCount} descriptions, ${report.coverage.withInputSchema}/${report.coverage.toolCount} input schemas, ${report.coverage.withOutputSchema}/${report.coverage.toolCount} output schemas`;
|
|
263
|
+
process.stdout.write(`\n Overall: ${chalk.bold(String(report.overall))}/100 ${gradeColor(report.grade)} confidence ${report.confidence}\n`);
|
|
264
|
+
process.stdout.write(` ${coverage}\n`);
|
|
265
|
+
if (!report.measured) {
|
|
266
|
+
process.stdout.write(' No tools were scored. An empty list is not a pass.\n');
|
|
267
|
+
}
|
|
268
|
+
process.stdout.write('\n');
|
|
226
269
|
}
|
|
227
270
|
//# sourceMappingURL=check.js.map
|