@agentxm/client-core 0.3.1 → 0.3.3
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/LICENSE +1 -1
- package/dist/src/unstable/agents/detection.d.ts.map +1 -1
- package/dist/src/unstable/agents/detection.js +2 -1
- package/dist/src/unstable/agents/detection.js.map +1 -1
- package/dist/src/unstable/cli-renderer/ansi-chrome.d.ts.map +1 -1
- package/dist/src/unstable/cli-renderer/ansi-chrome.js +4 -1
- package/dist/src/unstable/cli-renderer/ansi-chrome.js.map +1 -1
- package/dist/src/unstable/extensions/index.d.ts +1 -0
- package/dist/src/unstable/extensions/index.d.ts.map +1 -1
- package/dist/src/unstable/extensions/index.js +1 -0
- package/dist/src/unstable/extensions/index.js.map +1 -1
- package/dist/src/unstable/extensions/universal-skills-dir.d.ts +54 -0
- package/dist/src/unstable/extensions/universal-skills-dir.d.ts.map +1 -0
- package/dist/src/unstable/extensions/universal-skills-dir.js +68 -0
- package/dist/src/unstable/extensions/universal-skills-dir.js.map +1 -0
- package/dist/src/unstable/lint/catalog/pack/manifest-keys-recognized.js +1 -1
- package/dist/src/unstable/lint/catalog/pack/manifest-keys-recognized.js.map +1 -1
- package/dist/src/unstable/lint/catalog/pack/manifest-present.d.ts.map +1 -1
- package/dist/src/unstable/lint/catalog/pack/manifest-present.js +2 -5
- package/dist/src/unstable/lint/catalog/pack/manifest-present.js.map +1 -1
- package/dist/src/unstable/lint/catalog/pack/manifest-schema-valid.js +1 -1
- package/dist/src/unstable/lint/catalog/pack/manifest-schema-valid.js.map +1 -1
- package/dist/src/unstable/lint/catalog/shared/schema-rule.d.ts.map +1 -1
- package/dist/src/unstable/lint/catalog/shared/schema-rule.js +12 -5
- package/dist/src/unstable/lint/catalog/shared/schema-rule.js.map +1 -1
- package/dist/src/unstable/lint/catalog/skill/frontmatter-parseable.js +8 -11
- package/dist/src/unstable/lint/catalog/skill/frontmatter-parseable.js.map +1 -1
- package/dist/src/unstable/lint/catalog/skill/manifest-keys-recognized.js +1 -1
- package/dist/src/unstable/lint/catalog/skill/manifest-keys-recognized.js.map +1 -1
- package/dist/src/unstable/lint/catalog/skill/manifest-present.d.ts.map +1 -1
- package/dist/src/unstable/lint/catalog/skill/manifest-present.js +2 -5
- package/dist/src/unstable/lint/catalog/skill/manifest-present.js.map +1 -1
- package/dist/src/unstable/lint/catalog/skill/manifest-schema-valid.js +1 -1
- package/dist/src/unstable/lint/catalog/skill/manifest-schema-valid.js.map +1 -1
- package/dist/src/unstable/lint/catalog/skill/skill-md-present.js +2 -3
- package/dist/src/unstable/lint/catalog/skill/skill-md-present.js.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace/agents-detected-declared.d.ts.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace/agents-detected-declared.js +5 -15
- package/dist/src/unstable/lint/catalog/workspace/agents-detected-declared.js.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace/agents-recognized.d.ts.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace/agents-recognized.js +4 -15
- package/dist/src/unstable/lint/catalog/workspace/agents-recognized.js.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace/helpers/decode.d.ts +14 -0
- package/dist/src/unstable/lint/catalog/workspace/helpers/decode.d.ts.map +1 -0
- package/dist/src/unstable/lint/catalog/workspace/helpers/decode.js +28 -0
- package/dist/src/unstable/lint/catalog/workspace/helpers/decode.js.map +1 -0
- package/dist/src/unstable/lint/catalog/workspace/helpers/finding.d.ts +10 -0
- package/dist/src/unstable/lint/catalog/workspace/helpers/finding.d.ts.map +1 -0
- package/dist/src/unstable/lint/catalog/workspace/helpers/finding.js +11 -0
- package/dist/src/unstable/lint/catalog/workspace/helpers/finding.js.map +1 -0
- package/dist/src/unstable/lint/catalog/workspace/helpers/retained-skills.d.ts +24 -0
- package/dist/src/unstable/lint/catalog/workspace/helpers/retained-skills.d.ts.map +1 -0
- package/dist/src/unstable/lint/catalog/workspace/helpers/retained-skills.js +41 -0
- package/dist/src/unstable/lint/catalog/workspace/helpers/retained-skills.js.map +1 -0
- package/dist/src/unstable/lint/catalog/workspace/helpers/source-categorize.d.ts +16 -0
- package/dist/src/unstable/lint/catalog/workspace/helpers/source-categorize.d.ts.map +1 -0
- package/dist/src/unstable/lint/catalog/workspace/helpers/source-categorize.js +37 -0
- package/dist/src/unstable/lint/catalog/workspace/helpers/source-categorize.js.map +1 -0
- package/dist/src/unstable/lint/catalog/workspace/initialized.js +3 -5
- package/dist/src/unstable/lint/catalog/workspace/initialized.js.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace/lockfile-valid.d.ts.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace/lockfile-valid.js +8 -21
- package/dist/src/unstable/lint/catalog/workspace/lockfile-valid.js.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace/packs-declarations-valid.d.ts.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace/packs-declarations-valid.js +12 -49
- package/dist/src/unstable/lint/catalog/workspace/packs-declarations-valid.js.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace/packs-dependencies-resolved.d.ts.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace/packs-dependencies-resolved.js +23 -17
- package/dist/src/unstable/lint/catalog/workspace/packs-dependencies-resolved.js.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace/packs-members-retained.d.ts.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace/packs-members-retained.js +24 -29
- package/dist/src/unstable/lint/catalog/workspace/packs-members-retained.js.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace/settings-schema-valid.d.ts.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace/settings-schema-valid.js +3 -6
- package/dist/src/unstable/lint/catalog/workspace/settings-schema-valid.js.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace/skills-artifacts-correct.d.ts +3 -1
- package/dist/src/unstable/lint/catalog/workspace/skills-artifacts-correct.d.ts.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace/skills-artifacts-correct.js +142 -55
- package/dist/src/unstable/lint/catalog/workspace/skills-artifacts-correct.js.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace/skills-declarations-valid.d.ts +3 -2
- package/dist/src/unstable/lint/catalog/workspace/skills-declarations-valid.d.ts.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace/skills-declarations-valid.js +13 -56
- package/dist/src/unstable/lint/catalog/workspace/skills-declarations-valid.js.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace/skills-integrity-valid.d.ts +3 -2
- package/dist/src/unstable/lint/catalog/workspace/skills-integrity-valid.d.ts.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace/skills-integrity-valid.js +71 -54
- package/dist/src/unstable/lint/catalog/workspace/skills-integrity-valid.js.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace/skills-lockfile-aligned.d.ts.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace/skills-lockfile-aligned.js +76 -150
- package/dist/src/unstable/lint/catalog/workspace/skills-lockfile-aligned.js.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace/skills-managed.d.ts +15 -0
- package/dist/src/unstable/lint/catalog/workspace/skills-managed.d.ts.map +1 -0
- package/dist/src/unstable/lint/catalog/workspace/skills-managed.js +102 -0
- package/dist/src/unstable/lint/catalog/workspace/skills-managed.js.map +1 -0
- package/dist/src/unstable/lint/catalog/workspace-accessor/platform.d.ts.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace-accessor/platform.js +2 -1
- package/dist/src/unstable/lint/catalog/workspace-accessor/platform.js.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace.d.ts +9 -5
- package/dist/src/unstable/lint/catalog/workspace.d.ts.map +1 -1
- package/dist/src/unstable/lint/catalog/workspace.js +20 -11
- package/dist/src/unstable/lint/catalog/workspace.js.map +1 -1
- package/dist/src/unstable/lint/cli.d.ts +45 -27
- package/dist/src/unstable/lint/cli.d.ts.map +1 -1
- package/dist/src/unstable/lint/cli.js +819 -59
- package/dist/src/unstable/lint/cli.js.map +1 -1
- package/dist/src/unstable/lint/index.d.ts +1 -1
- package/dist/src/unstable/lint/index.d.ts.map +1 -1
- package/dist/src/unstable/lint/index.js +1 -1
- package/dist/src/unstable/lint/index.js.map +1 -1
- package/dist/src/unstable/lint/issues-to-findings.d.ts +5 -5
- package/dist/src/unstable/lint/issues-to-findings.js +78 -15
- package/dist/src/unstable/lint/issues-to-findings.js.map +1 -1
- package/dist/src/unstable/lint/rule.d.ts +0 -8
- package/dist/src/unstable/lint/rule.d.ts.map +1 -1
- package/dist/src/unstable/skills/operations/install.d.ts.map +1 -1
- package/dist/src/unstable/skills/operations/install.js +2 -0
- package/dist/src/unstable/skills/operations/install.js.map +1 -1
- package/dist/src/unstable/workspace/classifier-records.d.ts.map +1 -1
- package/dist/src/unstable/workspace/classifier-records.js +1 -0
- package/dist/src/unstable/workspace/classifier-records.js.map +1 -1
- package/dist/src/unstable/workspace/classifier.d.ts +13 -2
- package/dist/src/unstable/workspace/classifier.d.ts.map +1 -1
- package/dist/src/unstable/workspace/classifier.js +14 -2
- package/dist/src/unstable/workspace/classifier.js.map +1 -1
- package/dist/src/unstable/workspace/service.d.ts.map +1 -1
- package/dist/src/unstable/workspace/service.js +29 -8
- package/dist/src/unstable/workspace/service.js.map +1 -1
- package/dist/src/unstable/workspace/taxonomy-types.d.ts +1 -0
- package/dist/src/unstable/workspace/taxonomy-types.d.ts.map +1 -1
- package/package.json +3 -3
- package/site-content/INSTALL.md +4 -4
- package/site-content/__generated__/schemas/axm-lock.schema.json +727 -1124
- package/site-content/__generated__/schemas/axm-package-meta.schema.json +3 -16
- package/site-content/__generated__/schemas/command.schema.json +29 -130
- package/site-content/__generated__/schemas/extension-pack.schema.json +36 -154
- package/site-content/__generated__/schemas/mcp-server.schema.json +21 -134
- package/site-content/__generated__/schemas/settings.schema.json +111 -291
- package/site-content/__generated__/schemas/skill.schema.json +23 -143
- package/dist/src/unstable/lint/catalog/workspace/skills-artifacts-clean.d.ts +0 -30
- package/dist/src/unstable/lint/catalog/workspace/skills-artifacts-clean.d.ts.map +0 -1
- package/dist/src/unstable/lint/catalog/workspace/skills-artifacts-clean.js +0 -232
- package/dist/src/unstable/lint/catalog/workspace/skills-artifacts-clean.js.map +0 -1
|
@@ -222,83 +222,844 @@ export const detectPublishGateDrift = (config) => {
|
|
|
222
222
|
visitCatalog(packRules);
|
|
223
223
|
return weakened;
|
|
224
224
|
};
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
lines.push("DRIFT: The registry will still block publish on these rules:");
|
|
251
|
-
for (const id of summary.driftBanner) {
|
|
252
|
-
lines.push(` - ${id}`);
|
|
225
|
+
const defaultHumanReporter = "grouped";
|
|
226
|
+
const compareRenderedFindings = (left, right) => {
|
|
227
|
+
const byPath = left.path.localeCompare(right.path);
|
|
228
|
+
if (byPath !== 0) {
|
|
229
|
+
return byPath;
|
|
230
|
+
}
|
|
231
|
+
const bySeverity = severityOrder(left.finding.severity) - severityOrder(right.finding.severity);
|
|
232
|
+
if (bySeverity !== 0) {
|
|
233
|
+
return bySeverity;
|
|
234
|
+
}
|
|
235
|
+
const byRuleId = left.finding.ruleId.localeCompare(right.finding.ruleId);
|
|
236
|
+
if (byRuleId !== 0) {
|
|
237
|
+
return byRuleId;
|
|
238
|
+
}
|
|
239
|
+
return left.finding.message.localeCompare(right.finding.message);
|
|
240
|
+
};
|
|
241
|
+
const pluralize = (n, singular, plural) => n === 1 ? singular : plural;
|
|
242
|
+
const splitSentences = (message) => {
|
|
243
|
+
const out = [];
|
|
244
|
+
let remaining = message.trim();
|
|
245
|
+
while (remaining.length > 0) {
|
|
246
|
+
const match = /^(.+?\.(?=\s+[A-Z`]))\s+(.+)$/.exec(remaining);
|
|
247
|
+
if (match === null) {
|
|
248
|
+
out.push(remaining);
|
|
249
|
+
break;
|
|
253
250
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
251
|
+
const head = match[1];
|
|
252
|
+
const tail = match[2];
|
|
253
|
+
if (head === undefined || tail === undefined) {
|
|
254
|
+
out.push(remaining);
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
out.push(head);
|
|
258
|
+
remaining = tail;
|
|
259
|
+
}
|
|
260
|
+
return out;
|
|
261
|
+
};
|
|
262
|
+
const splitDetailClause = (message) => {
|
|
263
|
+
const marker = " Detail: ";
|
|
264
|
+
const index = message.indexOf(marker);
|
|
265
|
+
if (index === -1) {
|
|
266
|
+
return {
|
|
267
|
+
lead: message,
|
|
268
|
+
detail: undefined,
|
|
269
|
+
trailing: [],
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
const lead = message.slice(0, index);
|
|
273
|
+
const rest = message.slice(index + marker.length);
|
|
274
|
+
const sentences = splitSentences(rest);
|
|
275
|
+
const detail = sentences[0];
|
|
276
|
+
return {
|
|
277
|
+
lead,
|
|
278
|
+
detail,
|
|
279
|
+
trailing: sentences.slice(1),
|
|
280
|
+
};
|
|
281
|
+
};
|
|
282
|
+
const parseFindingMessage = (message) => {
|
|
283
|
+
const detailSplit = splitDetailClause(message);
|
|
284
|
+
const leadSentences = splitSentences(detailSplit.lead);
|
|
285
|
+
const title = leadSentences[0] ?? message.trim();
|
|
286
|
+
const details = detailSplit.detail === undefined ? [] : [detailSplit.detail];
|
|
287
|
+
return {
|
|
288
|
+
title,
|
|
289
|
+
details,
|
|
290
|
+
helps: [...leadSentences.slice(1), ...detailSplit.trailing],
|
|
291
|
+
};
|
|
292
|
+
};
|
|
293
|
+
const dirnamePosix = (path) => {
|
|
294
|
+
if (path === "." || path === "..") {
|
|
295
|
+
return path;
|
|
296
|
+
}
|
|
297
|
+
const index = path.lastIndexOf("/");
|
|
298
|
+
if (index <= 0) {
|
|
299
|
+
return path;
|
|
300
|
+
}
|
|
301
|
+
return path.slice(0, index);
|
|
302
|
+
};
|
|
303
|
+
const groupDisplayPath = (entry) => {
|
|
304
|
+
switch (entry.finding.ruleId) {
|
|
305
|
+
case "workspace/skills-managed":
|
|
306
|
+
return dirnamePosix(entry.path);
|
|
307
|
+
default:
|
|
308
|
+
return entry.path;
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
const bucketForFinding = (entry, parsed) => {
|
|
312
|
+
switch (entry.finding.ruleId) {
|
|
313
|
+
case "workspace/lockfile-valid":
|
|
314
|
+
if (parsed.title.startsWith("Lockfile is missing required field `")) {
|
|
315
|
+
return "missing-required-field";
|
|
316
|
+
}
|
|
317
|
+
if (parsed.title.startsWith("The lockfile is not valid YAML.")) {
|
|
318
|
+
return "invalid-yaml";
|
|
319
|
+
}
|
|
320
|
+
return "validation";
|
|
321
|
+
case "workspace/skills-managed":
|
|
322
|
+
if (parsed.title.includes("but it is not managed by this workspace.")) {
|
|
323
|
+
return "unmanaged";
|
|
324
|
+
}
|
|
325
|
+
return "managed";
|
|
326
|
+
case "workspace/skills-artifacts-correct":
|
|
327
|
+
return "artifact-state";
|
|
328
|
+
case "workspace/skills-lockfile-aligned":
|
|
329
|
+
if (parsed.title.includes("missing from the lockfile.")) {
|
|
330
|
+
return "missing";
|
|
331
|
+
}
|
|
332
|
+
if (parsed.title.includes("listed in the lockfile but not in settings.skills.")) {
|
|
333
|
+
return "orphan";
|
|
334
|
+
}
|
|
335
|
+
if (parsed.title.includes("lockfile version does not match the declared version.")) {
|
|
336
|
+
return "version";
|
|
337
|
+
}
|
|
338
|
+
return "alignment";
|
|
339
|
+
case "workspace/skills-integrity-valid":
|
|
340
|
+
return "integrity";
|
|
341
|
+
default:
|
|
342
|
+
return entry.finding.ruleId;
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
const parseHumanFinding = (entry) => {
|
|
346
|
+
const parsed = parseFindingMessage(entry.finding.message);
|
|
347
|
+
return {
|
|
348
|
+
path: groupDisplayPath(entry),
|
|
349
|
+
bucket: bucketForFinding(entry, parsed),
|
|
350
|
+
severity: entry.finding.severity,
|
|
351
|
+
ruleId: entry.finding.ruleId,
|
|
352
|
+
title: parsed.title,
|
|
353
|
+
details: parsed.details,
|
|
354
|
+
helps: parsed.helps,
|
|
355
|
+
fixable: entry.finding.kind === "autofixable",
|
|
356
|
+
};
|
|
357
|
+
};
|
|
358
|
+
const matchSingleQuoted = (message) => {
|
|
359
|
+
const match = /'([^']+)'/.exec(message);
|
|
360
|
+
return match?.[1];
|
|
361
|
+
};
|
|
362
|
+
const uniqueStrings = (values) => {
|
|
363
|
+
const out = [];
|
|
364
|
+
const seen = new Set();
|
|
365
|
+
for (const value of values) {
|
|
366
|
+
if (seen.has(value)) {
|
|
264
367
|
continue;
|
|
265
368
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
369
|
+
seen.add(value);
|
|
370
|
+
out.push(value);
|
|
371
|
+
}
|
|
372
|
+
return out;
|
|
373
|
+
};
|
|
374
|
+
const sortStrings = (values) => [...values].sort();
|
|
375
|
+
const uniquePaths = (findings) => uniqueStrings(findings.map((finding) => finding.path));
|
|
376
|
+
const mergedRuleHelps = (findings, autofixHelp) => {
|
|
377
|
+
if (findings.every((finding) => finding.fixable)) {
|
|
378
|
+
return [autofixHelp];
|
|
379
|
+
}
|
|
380
|
+
const helps = uniqueStrings(findings.flatMap((finding) => finding.helps));
|
|
381
|
+
return helps.length > 0
|
|
382
|
+
? helps
|
|
383
|
+
: findings.some((finding) => finding.fixable)
|
|
384
|
+
? [autofixHelp]
|
|
385
|
+
: [];
|
|
386
|
+
};
|
|
387
|
+
const summarizeSkillByDetail = (finding) => {
|
|
388
|
+
const name = matchSingleQuoted(finding.title);
|
|
389
|
+
const detail = finding.details[0];
|
|
390
|
+
if (name === undefined) {
|
|
391
|
+
return detail ?? finding.title;
|
|
392
|
+
}
|
|
393
|
+
if (detail === undefined) {
|
|
394
|
+
return name;
|
|
395
|
+
}
|
|
396
|
+
return `${name}: ${detail}`;
|
|
397
|
+
};
|
|
398
|
+
const compressDetails = (details, limit = 10) => {
|
|
399
|
+
if (details.length <= limit) {
|
|
400
|
+
return details;
|
|
401
|
+
}
|
|
402
|
+
const remaining = details.length - limit;
|
|
403
|
+
return [...details.slice(0, limit), `... and ${remaining} more`];
|
|
404
|
+
};
|
|
405
|
+
const previewList = (values, limit = 3) => {
|
|
406
|
+
if (values.length === 0) {
|
|
407
|
+
return "";
|
|
408
|
+
}
|
|
409
|
+
if (values.length <= limit) {
|
|
410
|
+
return values.join(", ");
|
|
411
|
+
}
|
|
412
|
+
const remaining = values.length - limit;
|
|
413
|
+
return `${values.slice(0, limit).join(", ")}, ... and ${remaining} more`;
|
|
414
|
+
};
|
|
415
|
+
const groupFindingsByPath = (findings, extract) => {
|
|
416
|
+
const grouped = new Map();
|
|
417
|
+
for (const finding of findings) {
|
|
418
|
+
const current = grouped.get(finding.path);
|
|
419
|
+
const value = extract(finding);
|
|
420
|
+
if (current === undefined) {
|
|
421
|
+
grouped.set(finding.path, [value]);
|
|
422
|
+
}
|
|
423
|
+
else {
|
|
424
|
+
current.push(value);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return [...grouped.entries()].sort(([left], [right]) => left.localeCompare(right));
|
|
428
|
+
};
|
|
429
|
+
const coalesceFullDiagnostic = (findings) => {
|
|
430
|
+
const [first] = findings;
|
|
431
|
+
if (first === undefined) {
|
|
432
|
+
return {
|
|
433
|
+
severity: "info",
|
|
434
|
+
ruleId: "",
|
|
435
|
+
title: "",
|
|
436
|
+
details: [],
|
|
437
|
+
helps: [],
|
|
438
|
+
fixable: false,
|
|
439
|
+
paths: [],
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
if (findings.length === 1) {
|
|
443
|
+
return {
|
|
444
|
+
severity: first.severity,
|
|
445
|
+
ruleId: first.ruleId,
|
|
446
|
+
title: first.title,
|
|
447
|
+
details: first.details,
|
|
448
|
+
helps: first.helps,
|
|
449
|
+
fixable: first.fixable,
|
|
450
|
+
paths: [first.path],
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
const allHelps = uniqueStrings(findings.flatMap((finding) => finding.helps));
|
|
454
|
+
const paths = uniquePaths(findings);
|
|
455
|
+
switch (`${first.ruleId}:${first.bucket}`) {
|
|
456
|
+
case "workspace/lockfile-valid:missing-required-field": {
|
|
457
|
+
const fields = findings.flatMap((finding) => {
|
|
458
|
+
const match = /Lockfile is missing required field `([^`]+)`\./.exec(finding.title);
|
|
459
|
+
return match?.[1] === undefined ? [] : [match[1]];
|
|
460
|
+
});
|
|
461
|
+
return {
|
|
462
|
+
severity: first.severity,
|
|
463
|
+
ruleId: first.ruleId,
|
|
464
|
+
title: "Lockfile is missing required fields.",
|
|
465
|
+
details: compressDetails(fields),
|
|
466
|
+
helps: [
|
|
467
|
+
"Regenerate `.axm/axm-lock.yaml` from `.axm/settings.json` by reinstalling the declared extensions.",
|
|
468
|
+
],
|
|
469
|
+
fixable: false,
|
|
470
|
+
paths,
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
case "workspace/skills-managed:unmanaged": {
|
|
474
|
+
const names = findings.flatMap((finding) => {
|
|
475
|
+
const name = matchSingleQuoted(finding.title);
|
|
476
|
+
return name === undefined ? [] : [name];
|
|
477
|
+
});
|
|
478
|
+
return {
|
|
479
|
+
severity: first.severity,
|
|
480
|
+
ruleId: first.ruleId,
|
|
481
|
+
title: `${names.length} ${pluralize(names.length, "skill is", "skills are")} present here but not managed by this workspace.`,
|
|
482
|
+
details: compressDetails(names),
|
|
483
|
+
helps: [
|
|
484
|
+
"To keep them: run `axm skills install <source>` for each skill you want axm to manage.",
|
|
485
|
+
"To remove them: run `axm prune` or `axm skills prune <name>`.",
|
|
486
|
+
],
|
|
487
|
+
fixable: false,
|
|
488
|
+
paths,
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
case "workspace/skills-artifacts-correct:enabled-missing":
|
|
492
|
+
case "workspace/skills-artifacts-correct:disabled-present":
|
|
493
|
+
case "workspace/skills-artifacts-correct:inconsistent":
|
|
494
|
+
case "workspace/skills-artifacts-correct:artifact-state":
|
|
495
|
+
return {
|
|
496
|
+
severity: first.severity,
|
|
497
|
+
ruleId: first.ruleId,
|
|
498
|
+
title: `${findings.length} ${pluralize(findings.length, "skill is", "skills are")} inconsistent across the declared agents.`,
|
|
499
|
+
details: compressDetails(findings.map(summarizeSkillByDetail)),
|
|
500
|
+
helps: mergedRuleHelps(findings, "Run `axm lint --fix` to reconcile the declared agent artifacts."),
|
|
501
|
+
fixable: findings.some((finding) => finding.fixable),
|
|
502
|
+
paths,
|
|
503
|
+
};
|
|
504
|
+
case "workspace/skills-lockfile-aligned:missing":
|
|
505
|
+
case "workspace/skills-lockfile-aligned:orphan":
|
|
506
|
+
case "workspace/skills-lockfile-aligned:version":
|
|
507
|
+
case "workspace/skills-lockfile-aligned:alignment":
|
|
508
|
+
return {
|
|
509
|
+
severity: first.severity,
|
|
510
|
+
ruleId: first.ruleId,
|
|
511
|
+
title: "Skill declarations and lockfile entries are out of sync.",
|
|
512
|
+
details: compressDetails(findings.map(summarizeSkillByDetail)),
|
|
513
|
+
helps: ["Run `axm lint --fix` to reconcile settings.skills with the lockfile."],
|
|
514
|
+
fixable: true,
|
|
515
|
+
paths,
|
|
516
|
+
};
|
|
517
|
+
case "workspace/skills-integrity-valid:integrity":
|
|
518
|
+
return {
|
|
519
|
+
severity: first.severity,
|
|
520
|
+
ruleId: first.ruleId,
|
|
521
|
+
title: "Installed skill sources do not match their lockfile entries.",
|
|
522
|
+
details: compressDetails(findings.map(summarizeSkillByDetail)),
|
|
523
|
+
helps: mergedRuleHelps(findings, "Run `axm lint --fix` to reinstall the affected skills."),
|
|
524
|
+
fixable: findings.some((finding) => finding.fixable),
|
|
525
|
+
paths,
|
|
526
|
+
};
|
|
527
|
+
default:
|
|
528
|
+
return {
|
|
529
|
+
severity: first.severity,
|
|
530
|
+
ruleId: first.ruleId,
|
|
531
|
+
title: `${findings.length} findings reported for this rule.`,
|
|
532
|
+
details: compressDetails(findings.map((finding) => finding.details.length === 0
|
|
533
|
+
? finding.title
|
|
534
|
+
: `${finding.title}: ${finding.details.join("; ")}`)),
|
|
535
|
+
helps: allHelps,
|
|
536
|
+
fixable: findings.some((finding) => finding.fixable),
|
|
537
|
+
paths,
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
const coalesceGroupedDiagnostic = (findings) => {
|
|
542
|
+
const [first] = findings;
|
|
543
|
+
if (first === undefined) {
|
|
544
|
+
return {
|
|
545
|
+
severity: "info",
|
|
546
|
+
ruleId: "",
|
|
547
|
+
title: "",
|
|
548
|
+
details: [],
|
|
549
|
+
helps: [],
|
|
550
|
+
fixable: false,
|
|
551
|
+
paths: [],
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
if (findings.length === 1) {
|
|
555
|
+
return {
|
|
556
|
+
severity: first.severity,
|
|
557
|
+
ruleId: first.ruleId,
|
|
558
|
+
title: first.title,
|
|
559
|
+
details: first.details,
|
|
560
|
+
helps: first.helps,
|
|
561
|
+
fixable: first.fixable,
|
|
562
|
+
paths: [first.path],
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
const paths = uniquePaths(findings);
|
|
566
|
+
const allHelps = uniqueStrings(findings.flatMap((finding) => finding.helps));
|
|
567
|
+
switch (`${first.ruleId}:${first.bucket}`) {
|
|
568
|
+
case "workspace/lockfile-valid:missing-required-field": {
|
|
569
|
+
const fields = sortStrings(uniqueStrings(findings.flatMap((finding) => {
|
|
570
|
+
const match = /Lockfile is missing required field `([^`]+)`\./.exec(finding.title);
|
|
571
|
+
return match?.[1] === undefined ? [] : [match[1]];
|
|
572
|
+
})));
|
|
573
|
+
return {
|
|
574
|
+
severity: first.severity,
|
|
575
|
+
ruleId: first.ruleId,
|
|
576
|
+
title: "Lockfile is missing fields required by the current schema.",
|
|
577
|
+
details: [`Missing fields include: ${previewList(fields, 4)}`],
|
|
578
|
+
helps: [
|
|
579
|
+
"Fix: Regenerate `.axm/axm-lock.yaml` from `.axm/settings.json` by reinstalling the declared extensions.",
|
|
580
|
+
],
|
|
581
|
+
fixable: false,
|
|
582
|
+
paths,
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
case "workspace/skills-managed:unmanaged": {
|
|
586
|
+
const perPath = groupFindingsByPath(findings, (finding) => matchSingleQuoted(finding.title) ?? finding.title);
|
|
587
|
+
return {
|
|
588
|
+
severity: first.severity,
|
|
589
|
+
ruleId: first.ruleId,
|
|
590
|
+
title: `Unmanaged skills are present in ${paths.length} ${pluralize(paths.length, "skill directory", "skill directories")}.`,
|
|
591
|
+
details: compressDetails(perPath.map(([path, names]) => {
|
|
592
|
+
const sorted = sortStrings(uniqueStrings(names));
|
|
593
|
+
return `${path}: ${sorted.length} unmanaged ${pluralize(sorted.length, "skill", "skills")} (${previewList(sorted, 3)})`;
|
|
594
|
+
}), 8),
|
|
595
|
+
helps: [
|
|
596
|
+
"To keep them: run `axm skills install <source>` for each skill you want axm to manage.",
|
|
597
|
+
"To remove them: run `axm prune` or `axm skills prune <name>`.",
|
|
598
|
+
],
|
|
599
|
+
fixable: false,
|
|
600
|
+
paths,
|
|
601
|
+
};
|
|
270
602
|
}
|
|
271
|
-
|
|
603
|
+
case "workspace/skills-artifacts-correct:enabled-missing":
|
|
604
|
+
case "workspace/skills-artifacts-correct:disabled-present":
|
|
605
|
+
case "workspace/skills-artifacts-correct:inconsistent":
|
|
606
|
+
case "workspace/skills-artifacts-correct:artifact-state":
|
|
607
|
+
return {
|
|
608
|
+
severity: first.severity,
|
|
609
|
+
ruleId: first.ruleId,
|
|
610
|
+
title: `${findings.length} ${pluralize(findings.length, "skill is", "skills are")} inconsistent across the declared agents.`,
|
|
611
|
+
details: compressDetails(findings.map(summarizeSkillByDetail)),
|
|
612
|
+
helps: mergedRuleHelps(findings, "Run `axm lint --fix` to reconcile the declared agent artifacts."),
|
|
613
|
+
fixable: findings.some((finding) => finding.fixable),
|
|
614
|
+
paths,
|
|
615
|
+
};
|
|
616
|
+
case "workspace/skills-lockfile-aligned:missing":
|
|
617
|
+
case "workspace/skills-lockfile-aligned:orphan":
|
|
618
|
+
case "workspace/skills-lockfile-aligned:version":
|
|
619
|
+
case "workspace/skills-lockfile-aligned:alignment":
|
|
620
|
+
return {
|
|
621
|
+
severity: first.severity,
|
|
622
|
+
ruleId: first.ruleId,
|
|
623
|
+
title: "`settings.skills` and the lockfile are out of sync.",
|
|
624
|
+
details: compressDetails(findings.map(summarizeSkillByDetail)),
|
|
625
|
+
helps: ["Run `axm lint --fix` to reconcile `settings.skills` with the lockfile."],
|
|
626
|
+
fixable: true,
|
|
627
|
+
paths,
|
|
628
|
+
};
|
|
629
|
+
case "workspace/skills-integrity-valid:integrity":
|
|
630
|
+
return {
|
|
631
|
+
severity: first.severity,
|
|
632
|
+
ruleId: first.ruleId,
|
|
633
|
+
title: "Installed skill sources do not match their lockfile entries.",
|
|
634
|
+
details: compressDetails(findings.map(summarizeSkillByDetail)),
|
|
635
|
+
helps: mergedRuleHelps(findings, "Run `axm lint --fix` to reinstall the affected skills."),
|
|
636
|
+
fixable: findings.some((finding) => finding.fixable),
|
|
637
|
+
paths,
|
|
638
|
+
};
|
|
639
|
+
default:
|
|
640
|
+
return {
|
|
641
|
+
severity: first.severity,
|
|
642
|
+
ruleId: first.ruleId,
|
|
643
|
+
title: findings.length === 1
|
|
644
|
+
? first.title
|
|
645
|
+
: `${findings.length} related findings were reported.`,
|
|
646
|
+
details: compressDetails(findings.map((finding) => {
|
|
647
|
+
const detail = finding.details.length === 0
|
|
648
|
+
? finding.title
|
|
649
|
+
: `${finding.title}: ${finding.details.join("; ")}`;
|
|
650
|
+
return paths.length === 1 ? detail : `${finding.path}: ${detail}`;
|
|
651
|
+
})),
|
|
652
|
+
helps: allHelps,
|
|
653
|
+
fixable: findings.some((finding) => finding.fixable),
|
|
654
|
+
paths,
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
const joinList = (values) => {
|
|
659
|
+
if (values.length === 0) {
|
|
660
|
+
return "";
|
|
661
|
+
}
|
|
662
|
+
if (values.length === 1) {
|
|
663
|
+
return values[0] ?? "";
|
|
272
664
|
}
|
|
273
|
-
if (
|
|
274
|
-
|
|
665
|
+
if (values.length === 2) {
|
|
666
|
+
const first = values[0] ?? "";
|
|
667
|
+
const second = values[1] ?? "";
|
|
668
|
+
return `${first} and ${second}`;
|
|
669
|
+
}
|
|
670
|
+
const head = values.slice(0, -1).join(", ");
|
|
671
|
+
const tail = values[values.length - 1] ?? "";
|
|
672
|
+
return `${head}, and ${tail}`;
|
|
673
|
+
};
|
|
674
|
+
const formatFullOverviewSentence = (args) => {
|
|
675
|
+
const parts = [];
|
|
676
|
+
if (args.counts.errors > 0) {
|
|
677
|
+
parts.push(`${args.counts.errors} ${pluralize(args.counts.errors, "error", "errors")}`);
|
|
678
|
+
}
|
|
679
|
+
if (args.counts.warnings > 0) {
|
|
680
|
+
parts.push(`${args.counts.warnings} ${pluralize(args.counts.warnings, "warning", "warnings")}`);
|
|
681
|
+
}
|
|
682
|
+
if (args.counts.infos > 0) {
|
|
683
|
+
parts.push(`${args.counts.infos} ${pluralize(args.counts.infos, "info", "infos")}`);
|
|
684
|
+
}
|
|
685
|
+
const locations = `${args.locationCount} ${pluralize(args.locationCount, "location", "locations")}`;
|
|
686
|
+
const base = `Found ${joinList(parts)} in ${locations}.`;
|
|
687
|
+
if (args.fixableCount === 0) {
|
|
688
|
+
return base;
|
|
689
|
+
}
|
|
690
|
+
return `${base} ${args.fixableCount} ${pluralize(args.fixableCount, "finding can", "findings can")} be auto-fixed.`;
|
|
691
|
+
};
|
|
692
|
+
const formatGroupedOverviewSentence = (args) => {
|
|
693
|
+
const parts = [`${args.diagnosticCount} ${pluralize(args.diagnosticCount, "issue", "issues")}.`];
|
|
694
|
+
if (args.fixableCount > 0) {
|
|
695
|
+
parts.push(`${args.fixableCount} ${pluralize(args.fixableCount, "can", "can")} be fixed automatically.`);
|
|
696
|
+
}
|
|
697
|
+
const manualCount = args.diagnosticCount - args.fixableCount;
|
|
698
|
+
if (manualCount > 0) {
|
|
699
|
+
parts.push(`${manualCount} ${pluralize(manualCount, "needs", "need")} manual attention.`);
|
|
700
|
+
}
|
|
701
|
+
return parts.join(" ");
|
|
702
|
+
};
|
|
703
|
+
const buildFullDiagnostics = (parsed) => {
|
|
704
|
+
const pathOrder = [];
|
|
705
|
+
const byPath = new Map();
|
|
706
|
+
for (const entry of parsed) {
|
|
707
|
+
const current = byPath.get(entry.path);
|
|
708
|
+
if (current === undefined) {
|
|
709
|
+
byPath.set(entry.path, [entry]);
|
|
710
|
+
pathOrder.push(entry.path);
|
|
711
|
+
}
|
|
712
|
+
else {
|
|
713
|
+
current.push(entry);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
const blocks = [];
|
|
717
|
+
pathOrder.forEach((path, index) => {
|
|
718
|
+
const pathEntries = byPath.get(path);
|
|
719
|
+
if (pathEntries === undefined) {
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
const groups = new Map();
|
|
723
|
+
const groupOrder = [];
|
|
724
|
+
for (const entry of pathEntries) {
|
|
725
|
+
const key = `${severityOrder(entry.severity)}:${entry.ruleId}:${entry.bucket}`;
|
|
726
|
+
const current = groups.get(key);
|
|
727
|
+
if (current === undefined) {
|
|
728
|
+
groups.set(key, [entry]);
|
|
729
|
+
groupOrder.push(key);
|
|
730
|
+
}
|
|
731
|
+
else {
|
|
732
|
+
current.push(entry);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
blocks.push({
|
|
736
|
+
kind: "pathGroup",
|
|
737
|
+
path,
|
|
738
|
+
diagnostics: groupOrder.flatMap((key) => {
|
|
739
|
+
const grouped = groups.get(key);
|
|
740
|
+
return grouped === undefined ? [] : [coalesceFullDiagnostic(grouped)];
|
|
741
|
+
}),
|
|
742
|
+
});
|
|
743
|
+
if (index < pathOrder.length - 1) {
|
|
744
|
+
blocks.push({ kind: "blank" });
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
return blocks;
|
|
748
|
+
};
|
|
749
|
+
const groupedBucketKey = (entry) => {
|
|
750
|
+
switch (entry.ruleId) {
|
|
751
|
+
case "workspace/skills-managed":
|
|
752
|
+
return `${entry.ruleId}:${entry.bucket}`;
|
|
753
|
+
default:
|
|
754
|
+
return `${entry.path}:${entry.ruleId}:${entry.bucket}`;
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
const buildGroupedDiagnostics = (parsed) => {
|
|
758
|
+
const groups = new Map();
|
|
759
|
+
const order = [];
|
|
760
|
+
for (const entry of parsed) {
|
|
761
|
+
const key = groupedBucketKey(entry);
|
|
762
|
+
const current = groups.get(key);
|
|
763
|
+
if (current === undefined) {
|
|
764
|
+
groups.set(key, [entry]);
|
|
765
|
+
order.push(key);
|
|
766
|
+
}
|
|
767
|
+
else {
|
|
768
|
+
current.push(entry);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
return order.flatMap((key) => {
|
|
772
|
+
const grouped = groups.get(key);
|
|
773
|
+
return grouped === undefined ? [] : [coalesceGroupedDiagnostic(grouped)];
|
|
774
|
+
});
|
|
775
|
+
};
|
|
776
|
+
const appendDiagnosticSection = (blocks, title, diagnostics, note) => {
|
|
777
|
+
if (diagnostics.length === 0) {
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
if (blocks.length > 0) {
|
|
781
|
+
blocks.push({ kind: "blank" });
|
|
782
|
+
}
|
|
783
|
+
blocks.push(note === undefined ? { kind: "section", title } : { kind: "section", title, note });
|
|
784
|
+
diagnostics.forEach((diagnostic, index) => {
|
|
785
|
+
blocks.push({ kind: "diagnostic", diagnostic });
|
|
786
|
+
if (index < diagnostics.length - 1) {
|
|
787
|
+
blocks.push({ kind: "blank" });
|
|
788
|
+
}
|
|
789
|
+
});
|
|
790
|
+
};
|
|
791
|
+
const stripFixHelps = (diagnostic) => {
|
|
792
|
+
const filtered = diagnostic.helps.filter((h) => !h.includes("axm lint --fix"));
|
|
793
|
+
return filtered.length === diagnostic.helps.length
|
|
794
|
+
? diagnostic
|
|
795
|
+
: { ...diagnostic, helps: filtered };
|
|
796
|
+
};
|
|
797
|
+
const buildSectionedDiagnostics = (args) => {
|
|
798
|
+
const fixable = args.diagnostics.filter((diagnostic) => diagnostic.fixable);
|
|
799
|
+
const manual = args.diagnostics.filter((diagnostic) => diagnostic.severity === "error" && !diagnostic.fixable);
|
|
800
|
+
const warnings = args.diagnostics.filter((diagnostic) => diagnostic.severity === "warning");
|
|
801
|
+
const infos = args.diagnostics.filter((diagnostic) => diagnostic.severity === "info");
|
|
802
|
+
appendDiagnosticSection(args.blocks, "Auto-fixable", fixable.map(stripFixHelps), "run `axm lint --fix`");
|
|
803
|
+
appendDiagnosticSection(args.blocks, "Requires manual attention", manual);
|
|
804
|
+
appendDiagnosticSection(args.blocks, "Warnings", warnings);
|
|
805
|
+
appendDiagnosticSection(args.blocks, "Information", infos);
|
|
806
|
+
};
|
|
807
|
+
const makeSummaryDiagnostic = (diagnostic) => ({
|
|
808
|
+
...diagnostic,
|
|
809
|
+
details: diagnostic.paths.length === 1
|
|
810
|
+
? ["1 affected location"]
|
|
811
|
+
: [
|
|
812
|
+
`${diagnostic.paths.length} affected ${pluralize(diagnostic.paths.length, "location", "locations")}`,
|
|
813
|
+
],
|
|
814
|
+
helps: [],
|
|
815
|
+
});
|
|
816
|
+
const toFullLintHumanBlocks = (args) => {
|
|
817
|
+
const blocks = [];
|
|
818
|
+
const { summary, fixSummary } = args;
|
|
819
|
+
if (summary.findings.length === 0) {
|
|
820
|
+
blocks.push(summary.driftBanner.length === 0
|
|
821
|
+
? { kind: "empty", message: "No findings." }
|
|
822
|
+
: { kind: "empty", message: "No local findings." });
|
|
275
823
|
}
|
|
276
824
|
else {
|
|
277
|
-
|
|
825
|
+
const parsed = [...summary.findings].sort(compareRenderedFindings).map(parseHumanFinding);
|
|
826
|
+
const locationCount = uniqueStrings(parsed.map((finding) => finding.path)).length;
|
|
827
|
+
const fixableCount = summary.findings.filter((finding) => finding.finding.kind === "autofixable").length;
|
|
828
|
+
blocks.push({
|
|
829
|
+
kind: "overview",
|
|
830
|
+
message: formatFullOverviewSentence({
|
|
831
|
+
counts: summary.counts,
|
|
832
|
+
locationCount,
|
|
833
|
+
fixableCount,
|
|
834
|
+
}),
|
|
835
|
+
counts: summary.counts,
|
|
836
|
+
notes: fixableCount > 0 && fixSummary === undefined
|
|
837
|
+
? ["Next step: Run `axm lint --fix` for the auto-fixable findings."]
|
|
838
|
+
: [],
|
|
839
|
+
});
|
|
840
|
+
if (summary.driftBanner.length > 0) {
|
|
841
|
+
blocks.push({ kind: "blank" });
|
|
842
|
+
blocks.push({
|
|
843
|
+
kind: "driftBanner",
|
|
844
|
+
title: "The registry will still block publish on these rules:",
|
|
845
|
+
ruleIds: summary.driftBanner,
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
const diagnostics = buildFullDiagnostics(parsed);
|
|
849
|
+
if (diagnostics.length > 0) {
|
|
850
|
+
blocks.push({ kind: "blank" });
|
|
851
|
+
blocks.push(...diagnostics);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
if (summary.findings.length === 0 && summary.driftBanner.length > 0) {
|
|
855
|
+
blocks.push({ kind: "blank" });
|
|
856
|
+
blocks.push({
|
|
857
|
+
kind: "driftBanner",
|
|
858
|
+
title: "The registry will still block publish on these rules:",
|
|
859
|
+
ruleIds: summary.driftBanner,
|
|
860
|
+
});
|
|
278
861
|
}
|
|
279
862
|
if (fixSummary !== undefined) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
863
|
+
blocks.push({ kind: "blank" });
|
|
864
|
+
blocks.push({
|
|
865
|
+
kind: "fixSummary",
|
|
866
|
+
message: formatFixSummary(fixSummary),
|
|
867
|
+
summary: fixSummary,
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
return blocks;
|
|
871
|
+
};
|
|
872
|
+
const toGroupedLintHumanBlocks = (args) => {
|
|
873
|
+
const blocks = [];
|
|
874
|
+
const { summary, fixSummary } = args;
|
|
875
|
+
if (summary.findings.length === 0) {
|
|
876
|
+
blocks.push(summary.driftBanner.length === 0
|
|
877
|
+
? { kind: "empty", message: "No findings." }
|
|
878
|
+
: { kind: "empty", message: "No local findings." });
|
|
879
|
+
}
|
|
880
|
+
else {
|
|
881
|
+
const parsed = [...summary.findings].sort(compareRenderedFindings).map(parseHumanFinding);
|
|
882
|
+
const diagnostics = buildGroupedDiagnostics(parsed);
|
|
883
|
+
const fixableCount = diagnostics.filter((diagnostic) => diagnostic.fixable).length;
|
|
884
|
+
blocks.push({
|
|
885
|
+
kind: "overview",
|
|
886
|
+
message: formatGroupedOverviewSentence({
|
|
887
|
+
diagnosticCount: diagnostics.length,
|
|
888
|
+
fixableCount,
|
|
889
|
+
}),
|
|
890
|
+
counts: summary.counts,
|
|
891
|
+
notes: [],
|
|
892
|
+
});
|
|
893
|
+
if (summary.driftBanner.length > 0) {
|
|
894
|
+
blocks.push({ kind: "blank" });
|
|
895
|
+
blocks.push({
|
|
896
|
+
kind: "driftBanner",
|
|
897
|
+
title: "The registry will still block publish on these rules:",
|
|
898
|
+
ruleIds: summary.driftBanner,
|
|
899
|
+
});
|
|
283
900
|
}
|
|
901
|
+
buildSectionedDiagnostics({ blocks, diagnostics });
|
|
284
902
|
}
|
|
285
|
-
|
|
903
|
+
if (summary.findings.length === 0 && summary.driftBanner.length > 0) {
|
|
904
|
+
blocks.push({ kind: "blank" });
|
|
905
|
+
blocks.push({
|
|
906
|
+
kind: "driftBanner",
|
|
907
|
+
title: "The registry will still block publish on these rules:",
|
|
908
|
+
ruleIds: summary.driftBanner,
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
if (fixSummary !== undefined) {
|
|
912
|
+
blocks.push({ kind: "blank" });
|
|
913
|
+
blocks.push({
|
|
914
|
+
kind: "fixSummary",
|
|
915
|
+
message: formatFixSummary(fixSummary),
|
|
916
|
+
summary: fixSummary,
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
if (summary.findings.length > 0) {
|
|
920
|
+
blocks.push({ kind: "blank" });
|
|
921
|
+
blocks.push({
|
|
922
|
+
kind: "footer",
|
|
923
|
+
message: "More output: `axm lint --details` | `axm lint --json`",
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
return blocks;
|
|
286
927
|
};
|
|
287
|
-
const
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
928
|
+
const toSummaryLintHumanBlocks = (args) => {
|
|
929
|
+
const blocks = [];
|
|
930
|
+
const { summary, fixSummary } = args;
|
|
931
|
+
if (summary.findings.length === 0) {
|
|
932
|
+
blocks.push(summary.driftBanner.length === 0
|
|
933
|
+
? { kind: "empty", message: "No findings." }
|
|
934
|
+
: { kind: "empty", message: "No local findings." });
|
|
935
|
+
}
|
|
936
|
+
else {
|
|
937
|
+
const parsed = [...summary.findings].sort(compareRenderedFindings).map(parseHumanFinding);
|
|
938
|
+
const diagnostics = buildGroupedDiagnostics(parsed).map(makeSummaryDiagnostic);
|
|
939
|
+
const fixableCount = diagnostics.filter((diagnostic) => diagnostic.fixable).length;
|
|
940
|
+
blocks.push({
|
|
941
|
+
kind: "overview",
|
|
942
|
+
message: formatGroupedOverviewSentence({
|
|
943
|
+
diagnosticCount: diagnostics.length,
|
|
944
|
+
fixableCount,
|
|
945
|
+
}),
|
|
946
|
+
counts: summary.counts,
|
|
947
|
+
notes: fixableCount > 0 && fixSummary === undefined
|
|
948
|
+
? ["Run `axm lint --fix` to apply available fixes."]
|
|
949
|
+
: [],
|
|
950
|
+
});
|
|
951
|
+
if (summary.driftBanner.length > 0) {
|
|
952
|
+
blocks.push({ kind: "blank" });
|
|
953
|
+
blocks.push({
|
|
954
|
+
kind: "driftBanner",
|
|
955
|
+
title: "The registry will still block publish on these rules:",
|
|
956
|
+
ruleIds: summary.driftBanner,
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
buildSectionedDiagnostics({ blocks, diagnostics });
|
|
960
|
+
}
|
|
961
|
+
if (summary.findings.length === 0 && summary.driftBanner.length > 0) {
|
|
962
|
+
blocks.push({ kind: "blank" });
|
|
963
|
+
blocks.push({
|
|
964
|
+
kind: "driftBanner",
|
|
965
|
+
title: "The registry will still block publish on these rules:",
|
|
966
|
+
ruleIds: summary.driftBanner,
|
|
967
|
+
});
|
|
293
968
|
}
|
|
294
|
-
|
|
969
|
+
if (fixSummary !== undefined) {
|
|
970
|
+
blocks.push({ kind: "blank" });
|
|
971
|
+
blocks.push({
|
|
972
|
+
kind: "fixSummary",
|
|
973
|
+
message: formatFixSummary(fixSummary),
|
|
974
|
+
summary: fixSummary,
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
return blocks;
|
|
978
|
+
};
|
|
979
|
+
export const toLintHumanBlocks = (args) => {
|
|
980
|
+
switch (args.reporter ?? defaultHumanReporter) {
|
|
981
|
+
case "full":
|
|
982
|
+
return toFullLintHumanBlocks(args);
|
|
983
|
+
case "summary":
|
|
984
|
+
return toSummaryLintHumanBlocks(args);
|
|
985
|
+
case "grouped":
|
|
986
|
+
return toGroupedLintHumanBlocks(args);
|
|
987
|
+
}
|
|
988
|
+
};
|
|
989
|
+
export const renderFindingsText = (args) => {
|
|
990
|
+
const lines = [];
|
|
991
|
+
for (const block of toLintHumanBlocks(args)) {
|
|
992
|
+
switch (block.kind) {
|
|
993
|
+
case "overview":
|
|
994
|
+
lines.push(block.message);
|
|
995
|
+
for (const note of block.notes) {
|
|
996
|
+
lines.push(note);
|
|
997
|
+
}
|
|
998
|
+
break;
|
|
999
|
+
case "driftBanner":
|
|
1000
|
+
lines.push(`DRIFT: ${block.title}`);
|
|
1001
|
+
for (const id of block.ruleIds) {
|
|
1002
|
+
lines.push(` - ${id}`);
|
|
1003
|
+
}
|
|
1004
|
+
break;
|
|
1005
|
+
case "section": {
|
|
1006
|
+
const label = block.note !== undefined ? `${block.title} (${block.note})` : block.title;
|
|
1007
|
+
lines.push(label);
|
|
1008
|
+
break;
|
|
1009
|
+
}
|
|
1010
|
+
case "diagnostic": {
|
|
1011
|
+
const location = block.diagnostic.paths.length === 1
|
|
1012
|
+
? (block.diagnostic.paths[0] ?? "")
|
|
1013
|
+
: block.diagnostic.paths.length > 1
|
|
1014
|
+
? `(${block.diagnostic.paths.length} locations)`
|
|
1015
|
+
: "(workspace)";
|
|
1016
|
+
lines.push(` [${block.diagnostic.severity}] ${location}`);
|
|
1017
|
+
lines.push(` rule: ${block.diagnostic.ruleId}${block.diagnostic.fixable ? " (auto-fixable)" : ""}`);
|
|
1018
|
+
lines.push(` ${block.diagnostic.title}`);
|
|
1019
|
+
for (const detail of block.diagnostic.details) {
|
|
1020
|
+
lines.push(` - ${detail}`);
|
|
1021
|
+
}
|
|
1022
|
+
for (const help of block.diagnostic.helps) {
|
|
1023
|
+
lines.push(` ${help}`);
|
|
1024
|
+
}
|
|
1025
|
+
break;
|
|
1026
|
+
}
|
|
1027
|
+
case "pathGroup":
|
|
1028
|
+
lines.push(block.path);
|
|
1029
|
+
for (const diagnostic of block.diagnostics) {
|
|
1030
|
+
lines.push(` [${diagnostic.severity}] ${diagnostic.ruleId}${diagnostic.fixable ? " (auto-fixable)" : ""}: ${diagnostic.title}`);
|
|
1031
|
+
for (const detail of diagnostic.details) {
|
|
1032
|
+
lines.push(` - ${detail}`);
|
|
1033
|
+
}
|
|
1034
|
+
for (const help of diagnostic.helps) {
|
|
1035
|
+
lines.push(` ${help}`);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
break;
|
|
1039
|
+
case "blank":
|
|
1040
|
+
lines.push("");
|
|
1041
|
+
break;
|
|
1042
|
+
case "empty":
|
|
1043
|
+
lines.push(block.message);
|
|
1044
|
+
break;
|
|
1045
|
+
case "footer":
|
|
1046
|
+
lines.push(block.message);
|
|
1047
|
+
break;
|
|
1048
|
+
case "fixSummary":
|
|
1049
|
+
lines.push(block.message);
|
|
1050
|
+
for (const warning of block.summary.warnings) {
|
|
1051
|
+
lines.push(` warning: ${warning}`);
|
|
1052
|
+
}
|
|
1053
|
+
break;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
return lines;
|
|
295
1057
|
};
|
|
296
1058
|
const formatFixSummary = (fix) => {
|
|
297
1059
|
const appliedLabel = pluralize(fix.applied, "fix", "fixes");
|
|
298
1060
|
const warningsLabel = pluralize(fix.warnings.length, "warning", "warnings");
|
|
299
1061
|
return `Applied ${fix.applied} ${appliedLabel}; ${fix.warnings.length} ${warningsLabel}, ${fix.failed} failed.`;
|
|
300
1062
|
};
|
|
301
|
-
const pluralize = (n, singular, plural) => n === 1 ? singular : plural;
|
|
302
1063
|
const toJsonFinding = (entry) => {
|
|
303
1064
|
const base = {
|
|
304
1065
|
group: entry.group,
|
|
@@ -308,7 +1069,6 @@ const toJsonFinding = (entry) => {
|
|
|
308
1069
|
message: entry.finding.message,
|
|
309
1070
|
displayRoot: entry.displayRoot,
|
|
310
1071
|
path: entry.path,
|
|
311
|
-
suggestions: [...entry.finding.suggestions],
|
|
312
1072
|
};
|
|
313
1073
|
if (entry.finding.location === undefined) {
|
|
314
1074
|
return base;
|