ultra11y 5.36.0 → 5.36.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.opencode/plugins/ultra11y.js +1 -1
- package/package.json +1 -1
- package/scripts/ultra11y.mjs +11 -69
- package/skills/review-a11y/SKILL.md +1 -1
- package/skills/review-a11y/scripts/ultra11y.mjs +11 -69
- package/skills/ultra11y/SKILL.md +1 -1
- package/skills/ultra11y/scripts/ultra11y.mjs +11 -69
- package/src/adjudicate.ts +19 -6
- package/src/annotate.ts +7 -71
- package/src/types.ts +1 -1
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"name": "ultra11y",
|
|
11
11
|
"source": "./",
|
|
12
12
|
"description": "Automatic accessibility review of the code under change before every commit, push and pull request, plus full WCAG 2.2 AA / RGAA audits and conformance deliverables on demand.",
|
|
13
|
-
"version": "5.36.
|
|
13
|
+
"version": "5.36.1",
|
|
14
14
|
"author": { "name": "maxgfr" },
|
|
15
15
|
"homepage": "https://github.com/maxgfr/ultra11y",
|
|
16
16
|
"repository": "https://github.com/maxgfr/ultra11y",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultra11y",
|
|
3
3
|
"description": "Accessibility for WCAG 2.2 AA and country standards (RGAA…): an automatic review of the code under change before every commit, push and pull request, plus full audits and conformance deliverables on demand.",
|
|
4
|
-
"version": "5.36.
|
|
4
|
+
"version": "5.36.1",
|
|
5
5
|
"author": { "name": "maxgfr" },
|
|
6
6
|
"homepage": "https://github.com/maxgfr/ultra11y",
|
|
7
7
|
"repository": "https://github.com/maxgfr/ultra11y",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultra11y",
|
|
3
3
|
"description": "Accessibility for WCAG 2.2 AA and country standards (RGAA…): an automatic review of the code under change before every commit, push and pull request, plus full audits and conformance deliverables on demand.",
|
|
4
|
-
"version": "5.36.
|
|
4
|
+
"version": "5.36.1",
|
|
5
5
|
"author": { "name": "maxgfr", "url": "https://github.com/maxgfr" },
|
|
6
6
|
"homepage": "https://github.com/maxgfr/ultra11y",
|
|
7
7
|
"repository": "https://github.com/maxgfr/ultra11y",
|
|
@@ -27,7 +27,7 @@ import { fileURLToPath } from "node:url";
|
|
|
27
27
|
// Ownership marker: `ultra11y install --opencode` refuses to overwrite a plugin file that
|
|
28
28
|
// does not carry it, so a user's own ultra11y.js is never clobbered.
|
|
29
29
|
const ULTRA11Y_OPENCODE_PLUGIN = "ULTRA11Y_OPENCODE_PLUGIN";
|
|
30
|
-
const ULTRA11Y_PLUGIN_VERSION = "5.36.
|
|
30
|
+
const ULTRA11Y_PLUGIN_VERSION = "5.36.1";
|
|
31
31
|
|
|
32
32
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
33
33
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultra11y",
|
|
3
|
-
"version": "5.36.
|
|
3
|
+
"version": "5.36.1",
|
|
4
4
|
"description": "Audit HTML/CSS/JSX against WCAG 2.2 AA and produce a dated compliance report, or author/review accessible markup (native-HTML-first). A skills.sh agent skill: an install-free static engine (bundled, no npm install) plus the agent's judgment, with check/verify gates against hallucinated non-conformities, and pluggable in-repo standards packs (RGAA France first; Section 508 & EN 301 549 next).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@10.33.0",
|
package/scripts/ultra11y.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { join as join53, relative as relative5, resolve as resolve16, sep as sep
|
|
|
6
6
|
import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL3 } from "url";
|
|
7
7
|
|
|
8
8
|
// src/types.ts
|
|
9
|
-
var VERSION = "5.36.
|
|
9
|
+
var VERSION = "5.36.1";
|
|
10
10
|
var SCHEMA_VERSION = 2;
|
|
11
11
|
var INAPPLICABLE_STATUS = "C";
|
|
12
12
|
|
|
@@ -58261,41 +58261,6 @@ function exhaustiveGrid(groups, standard, lang) {
|
|
|
58261
58261
|
out2.push("");
|
|
58262
58262
|
return out2;
|
|
58263
58263
|
}
|
|
58264
|
-
function splitReportSections(md) {
|
|
58265
|
-
const lines = md.split("\n");
|
|
58266
|
-
const preamble = [];
|
|
58267
|
-
const sections = [];
|
|
58268
|
-
let current = null;
|
|
58269
|
-
let fence = null;
|
|
58270
|
-
const push = (l) => {
|
|
58271
|
-
if (current) current.push(l);
|
|
58272
|
-
else preamble.push(l);
|
|
58273
|
-
};
|
|
58274
|
-
for (const line of lines) {
|
|
58275
|
-
const f = /^\s*(```+|~~~+)/.exec(line);
|
|
58276
|
-
if (f) {
|
|
58277
|
-
const mark = f[1];
|
|
58278
|
-
if (fence === null) fence = mark[0].repeat(mark.length);
|
|
58279
|
-
else if (mark.startsWith(fence[0]) && mark.length >= fence.length) fence = null;
|
|
58280
|
-
push(line);
|
|
58281
|
-
continue;
|
|
58282
|
-
}
|
|
58283
|
-
if (fence === null && line.startsWith("## ")) {
|
|
58284
|
-
current = [line];
|
|
58285
|
-
const own = current;
|
|
58286
|
-
sections.push({
|
|
58287
|
-
heading: line,
|
|
58288
|
-
lines: own,
|
|
58289
|
-
get text() {
|
|
58290
|
-
return own.join("\n");
|
|
58291
|
-
}
|
|
58292
|
-
});
|
|
58293
|
-
continue;
|
|
58294
|
-
}
|
|
58295
|
-
push(line);
|
|
58296
|
-
}
|
|
58297
|
-
return { preamble, sections };
|
|
58298
|
-
}
|
|
58299
58264
|
function renderPageRates(r, pages, standard, lang) {
|
|
58300
58265
|
if (!pages.length) return [];
|
|
58301
58266
|
const s = L5[lang];
|
|
@@ -61363,13 +61328,18 @@ function subjectsForPackCriterion2(standard, id, scs) {
|
|
|
61363
61328
|
for (const sc of scs) for (const subject of subjectsForSc2(sc)) if (!out2.includes(subject)) out2.push(subject);
|
|
61364
61329
|
return out2;
|
|
61365
61330
|
}
|
|
61331
|
+
function packResultNeedsAdjudication(pc, criterion) {
|
|
61332
|
+
if (pc.status === "manual") return true;
|
|
61333
|
+
const hasJudgmentTest = Object.values(criterion?.automation?.tests ?? {}).includes("judgment");
|
|
61334
|
+
return pc.status === INAPPLICABLE_STATUS && pc.inapplicable === true && pc.decidedBy !== "agent" && hasJudgmentTest;
|
|
61335
|
+
}
|
|
61366
61336
|
function buildAdjudicationWorklist(audit2, opts = {}) {
|
|
61367
61337
|
const docs = docsForAudit(audit2, opts.cwd);
|
|
61368
61338
|
const standard = opts.standard;
|
|
61369
61339
|
const limits = adjudicationLimits(opts.cwd);
|
|
61370
61340
|
if (standard !== void 0 && !isCore(standard)) {
|
|
61371
61341
|
const pack = loadPack(standard);
|
|
61372
|
-
return derivePackResults(audit2, standard).filter((pc) => pc
|
|
61342
|
+
return derivePackResults(audit2, standard).filter((pc) => packResultNeedsAdjudication(pc, getCriterion(pack, pc.id))).map((pc) => {
|
|
61373
61343
|
const crit = getCriterion(pack, pc.id);
|
|
61374
61344
|
const scs = crit?.wcag ?? pc.scs;
|
|
61375
61345
|
const item = blankItem(
|
|
@@ -61570,8 +61540,9 @@ function applyAdjudication(audit2, adj, opts = {}) {
|
|
|
61570
61540
|
const packMode = !isCore(adj.standard);
|
|
61571
61541
|
const open = /* @__PURE__ */ new Set();
|
|
61572
61542
|
if (packMode) {
|
|
61543
|
+
const pack = loadPack(adj.standard);
|
|
61573
61544
|
for (const pc of derivePackResults(audit2, adj.standard)) {
|
|
61574
|
-
if (pc
|
|
61545
|
+
if (!packResultNeedsAdjudication(pc, getCriterion(pack, pc.id))) continue;
|
|
61575
61546
|
open.add(pc.id);
|
|
61576
61547
|
if (byId2.has(pc.id)) continue;
|
|
61577
61548
|
if (Object.hasOwn(expected, pc.id)) uncovered(pc.id);
|
|
@@ -66436,7 +66407,7 @@ var S = {
|
|
|
66436
66407
|
noRenderedExecuted: "0 page rendue : aucun test rendered n'a \xE9t\xE9 ex\xE9cut\xE9",
|
|
66437
66408
|
staticTested: (tests, ids) => `${tests} test(s) static \u2014 crit\xE8res : ${ids || "\u2014"}`,
|
|
66438
66409
|
renderedContract: (tests, ids) => `${tests} test(s) rendered pr\xE9vus \u2014 crit\xE8res : ${ids || "\u2014"}`,
|
|
66439
|
-
judgmentContract: (tests, criteria) => `${tests} test(s) judgment sur ${criteria} crit\xE8re(s), transmis \xE0 l'IA
|
|
66410
|
+
judgmentContract: (tests, criteria) => `${tests} test(s) judgment sur ${criteria} crit\xE8re(s), tous transmis \xE0 l'IA tant qu'ils ne sont pas tranch\xE9s`
|
|
66440
66411
|
},
|
|
66441
66412
|
en: {
|
|
66442
66413
|
title: "ultra11y accessibility audit",
|
|
@@ -66512,7 +66483,7 @@ var S = {
|
|
|
66512
66483
|
noRenderedExecuted: "0 rendered pages: no rendered test was executed",
|
|
66513
66484
|
staticTested: (tests, ids) => `${tests} static test(s) \u2014 criteria: ${ids || "\u2014"}`,
|
|
66514
66485
|
renderedContract: (tests, ids) => `${tests} planned rendered test(s) \u2014 criteria: ${ids || "\u2014"}`,
|
|
66515
|
-
judgmentContract: (tests, criteria) => `${tests} judgment test(s) across ${criteria} criterion(ia), sent to AI
|
|
66486
|
+
judgmentContract: (tests, criteria) => `${tests} judgment test(s) across ${criteria} criterion(ia), all sent to AI while undecided`
|
|
66516
66487
|
}
|
|
66517
66488
|
};
|
|
66518
66489
|
var MAX_ROWS = 50;
|
|
@@ -66663,29 +66634,6 @@ function stepSummary(result, opts = {}) {
|
|
|
66663
66634
|
if (unanchored) out2.push(`> ${s.unanchored(unanchored)}`, "");
|
|
66664
66635
|
return out2.join("\n");
|
|
66665
66636
|
}
|
|
66666
|
-
function reportSectionsBody(result, standard, lang, budget, prefer) {
|
|
66667
|
-
let md;
|
|
66668
|
-
try {
|
|
66669
|
-
md = isCore(standard) ? renderReport(result, lang) : renderPackReport(result, loadPack(standard), lang);
|
|
66670
|
-
} catch {
|
|
66671
|
-
return { body: [], dropped: [] };
|
|
66672
|
-
}
|
|
66673
|
-
const { sections } = splitReportSections(md);
|
|
66674
|
-
const ordered = prefer ? [...sections.filter((x) => prefer.test(x.heading)), ...sections.filter((x) => !prefer.test(x.heading))] : sections;
|
|
66675
|
-
const body3 = [];
|
|
66676
|
-
const dropped = [];
|
|
66677
|
-
let spent = 0;
|
|
66678
|
-
for (const section of ordered) {
|
|
66679
|
-
const text = section.text.trimEnd();
|
|
66680
|
-
if (dropped.length || spent + sizeOf(text) + 2 > budget) {
|
|
66681
|
-
dropped.push(section.heading.replace(/^##\s*/, ""));
|
|
66682
|
-
continue;
|
|
66683
|
-
}
|
|
66684
|
-
body3.push(text, "");
|
|
66685
|
-
spent += sizeOf(text) + 2;
|
|
66686
|
-
}
|
|
66687
|
-
return { body: body3, dropped };
|
|
66688
|
-
}
|
|
66689
66637
|
function prComment(result, opts = {}) {
|
|
66690
66638
|
const standard = opts.standard ?? CORE2;
|
|
66691
66639
|
const lang = opts.lang ?? "en";
|
|
@@ -66709,12 +66657,6 @@ function prComment(result, opts = {}) {
|
|
|
66709
66657
|
const tail = [];
|
|
66710
66658
|
if (opts.artifactName) tail.push(s.artifact(opts.artifactName), "");
|
|
66711
66659
|
if (opts.runUrl) tail.push(s.runLink(opts.runUrl), "");
|
|
66712
|
-
const fixed = sizeOf([...head, ...tail].join("\n"));
|
|
66713
|
-
const { body: body3, dropped } = reportSectionsBody(result, standard, lang, Math.max(0, COMMENT_LIMIT - fixed - 512));
|
|
66714
|
-
const notes = [];
|
|
66715
|
-
if (dropped.length) notes.push(s.sectionsDropped(dropped), "");
|
|
66716
|
-
const assembled = [...head, ...body3, ...notes, ...tail].join("\n").trimEnd();
|
|
66717
|
-
if (sizeOf(assembled) <= COMMENT_LIMIT) return assembled;
|
|
66718
66660
|
const assemble = (rows2) => {
|
|
66719
66661
|
const digest = [];
|
|
66720
66662
|
if (criteria.length) {
|
|
@@ -3,7 +3,7 @@ name: review-a11y
|
|
|
3
3
|
description: "Use to REVIEW changed frontend code for accessibility: staged files, a working diff, branch, or PR; prompts such as 'review a11y', 'is this accessible?', or 'anything to fix before merge?'; and hook or CI findings on a change. Run the bundled install-free engine with cross-file JSX/TSX analysis, then adjudicate candidates and return a severity-ranked WCAG 2.2 AA review with file:line fixes, residual rendering risks, and a verdict. Use `ultra11y` instead for whole-repo audits, dated conformance reports, country standards, PRD backlogs, or authoring accessible markup."
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
|
-
version: 5.36.
|
|
6
|
+
version: 5.36.1
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# review-a11y — accessibility review of changed code
|
|
@@ -6,7 +6,7 @@ import { join as join53, relative as relative5, resolve as resolve16, sep as sep
|
|
|
6
6
|
import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL3 } from "url";
|
|
7
7
|
|
|
8
8
|
// src/types.ts
|
|
9
|
-
var VERSION = "5.36.
|
|
9
|
+
var VERSION = "5.36.1";
|
|
10
10
|
var SCHEMA_VERSION = 2;
|
|
11
11
|
var INAPPLICABLE_STATUS = "C";
|
|
12
12
|
|
|
@@ -58261,41 +58261,6 @@ function exhaustiveGrid(groups, standard, lang) {
|
|
|
58261
58261
|
out2.push("");
|
|
58262
58262
|
return out2;
|
|
58263
58263
|
}
|
|
58264
|
-
function splitReportSections(md) {
|
|
58265
|
-
const lines = md.split("\n");
|
|
58266
|
-
const preamble = [];
|
|
58267
|
-
const sections = [];
|
|
58268
|
-
let current = null;
|
|
58269
|
-
let fence = null;
|
|
58270
|
-
const push = (l) => {
|
|
58271
|
-
if (current) current.push(l);
|
|
58272
|
-
else preamble.push(l);
|
|
58273
|
-
};
|
|
58274
|
-
for (const line of lines) {
|
|
58275
|
-
const f = /^\s*(```+|~~~+)/.exec(line);
|
|
58276
|
-
if (f) {
|
|
58277
|
-
const mark = f[1];
|
|
58278
|
-
if (fence === null) fence = mark[0].repeat(mark.length);
|
|
58279
|
-
else if (mark.startsWith(fence[0]) && mark.length >= fence.length) fence = null;
|
|
58280
|
-
push(line);
|
|
58281
|
-
continue;
|
|
58282
|
-
}
|
|
58283
|
-
if (fence === null && line.startsWith("## ")) {
|
|
58284
|
-
current = [line];
|
|
58285
|
-
const own = current;
|
|
58286
|
-
sections.push({
|
|
58287
|
-
heading: line,
|
|
58288
|
-
lines: own,
|
|
58289
|
-
get text() {
|
|
58290
|
-
return own.join("\n");
|
|
58291
|
-
}
|
|
58292
|
-
});
|
|
58293
|
-
continue;
|
|
58294
|
-
}
|
|
58295
|
-
push(line);
|
|
58296
|
-
}
|
|
58297
|
-
return { preamble, sections };
|
|
58298
|
-
}
|
|
58299
58264
|
function renderPageRates(r, pages, standard, lang) {
|
|
58300
58265
|
if (!pages.length) return [];
|
|
58301
58266
|
const s = L5[lang];
|
|
@@ -61363,13 +61328,18 @@ function subjectsForPackCriterion2(standard, id, scs) {
|
|
|
61363
61328
|
for (const sc of scs) for (const subject of subjectsForSc2(sc)) if (!out2.includes(subject)) out2.push(subject);
|
|
61364
61329
|
return out2;
|
|
61365
61330
|
}
|
|
61331
|
+
function packResultNeedsAdjudication(pc, criterion) {
|
|
61332
|
+
if (pc.status === "manual") return true;
|
|
61333
|
+
const hasJudgmentTest = Object.values(criterion?.automation?.tests ?? {}).includes("judgment");
|
|
61334
|
+
return pc.status === INAPPLICABLE_STATUS && pc.inapplicable === true && pc.decidedBy !== "agent" && hasJudgmentTest;
|
|
61335
|
+
}
|
|
61366
61336
|
function buildAdjudicationWorklist(audit2, opts = {}) {
|
|
61367
61337
|
const docs = docsForAudit(audit2, opts.cwd);
|
|
61368
61338
|
const standard = opts.standard;
|
|
61369
61339
|
const limits = adjudicationLimits(opts.cwd);
|
|
61370
61340
|
if (standard !== void 0 && !isCore(standard)) {
|
|
61371
61341
|
const pack = loadPack(standard);
|
|
61372
|
-
return derivePackResults(audit2, standard).filter((pc) => pc
|
|
61342
|
+
return derivePackResults(audit2, standard).filter((pc) => packResultNeedsAdjudication(pc, getCriterion(pack, pc.id))).map((pc) => {
|
|
61373
61343
|
const crit = getCriterion(pack, pc.id);
|
|
61374
61344
|
const scs = crit?.wcag ?? pc.scs;
|
|
61375
61345
|
const item = blankItem(
|
|
@@ -61570,8 +61540,9 @@ function applyAdjudication(audit2, adj, opts = {}) {
|
|
|
61570
61540
|
const packMode = !isCore(adj.standard);
|
|
61571
61541
|
const open = /* @__PURE__ */ new Set();
|
|
61572
61542
|
if (packMode) {
|
|
61543
|
+
const pack = loadPack(adj.standard);
|
|
61573
61544
|
for (const pc of derivePackResults(audit2, adj.standard)) {
|
|
61574
|
-
if (pc
|
|
61545
|
+
if (!packResultNeedsAdjudication(pc, getCriterion(pack, pc.id))) continue;
|
|
61575
61546
|
open.add(pc.id);
|
|
61576
61547
|
if (byId2.has(pc.id)) continue;
|
|
61577
61548
|
if (Object.hasOwn(expected, pc.id)) uncovered(pc.id);
|
|
@@ -66436,7 +66407,7 @@ var S = {
|
|
|
66436
66407
|
noRenderedExecuted: "0 page rendue : aucun test rendered n'a \xE9t\xE9 ex\xE9cut\xE9",
|
|
66437
66408
|
staticTested: (tests, ids) => `${tests} test(s) static \u2014 crit\xE8res : ${ids || "\u2014"}`,
|
|
66438
66409
|
renderedContract: (tests, ids) => `${tests} test(s) rendered pr\xE9vus \u2014 crit\xE8res : ${ids || "\u2014"}`,
|
|
66439
|
-
judgmentContract: (tests, criteria) => `${tests} test(s) judgment sur ${criteria} crit\xE8re(s), transmis \xE0 l'IA
|
|
66410
|
+
judgmentContract: (tests, criteria) => `${tests} test(s) judgment sur ${criteria} crit\xE8re(s), tous transmis \xE0 l'IA tant qu'ils ne sont pas tranch\xE9s`
|
|
66440
66411
|
},
|
|
66441
66412
|
en: {
|
|
66442
66413
|
title: "ultra11y accessibility audit",
|
|
@@ -66512,7 +66483,7 @@ var S = {
|
|
|
66512
66483
|
noRenderedExecuted: "0 rendered pages: no rendered test was executed",
|
|
66513
66484
|
staticTested: (tests, ids) => `${tests} static test(s) \u2014 criteria: ${ids || "\u2014"}`,
|
|
66514
66485
|
renderedContract: (tests, ids) => `${tests} planned rendered test(s) \u2014 criteria: ${ids || "\u2014"}`,
|
|
66515
|
-
judgmentContract: (tests, criteria) => `${tests} judgment test(s) across ${criteria} criterion(ia), sent to AI
|
|
66486
|
+
judgmentContract: (tests, criteria) => `${tests} judgment test(s) across ${criteria} criterion(ia), all sent to AI while undecided`
|
|
66516
66487
|
}
|
|
66517
66488
|
};
|
|
66518
66489
|
var MAX_ROWS = 50;
|
|
@@ -66663,29 +66634,6 @@ function stepSummary(result, opts = {}) {
|
|
|
66663
66634
|
if (unanchored) out2.push(`> ${s.unanchored(unanchored)}`, "");
|
|
66664
66635
|
return out2.join("\n");
|
|
66665
66636
|
}
|
|
66666
|
-
function reportSectionsBody(result, standard, lang, budget, prefer) {
|
|
66667
|
-
let md;
|
|
66668
|
-
try {
|
|
66669
|
-
md = isCore(standard) ? renderReport(result, lang) : renderPackReport(result, loadPack(standard), lang);
|
|
66670
|
-
} catch {
|
|
66671
|
-
return { body: [], dropped: [] };
|
|
66672
|
-
}
|
|
66673
|
-
const { sections } = splitReportSections(md);
|
|
66674
|
-
const ordered = prefer ? [...sections.filter((x) => prefer.test(x.heading)), ...sections.filter((x) => !prefer.test(x.heading))] : sections;
|
|
66675
|
-
const body3 = [];
|
|
66676
|
-
const dropped = [];
|
|
66677
|
-
let spent = 0;
|
|
66678
|
-
for (const section of ordered) {
|
|
66679
|
-
const text = section.text.trimEnd();
|
|
66680
|
-
if (dropped.length || spent + sizeOf(text) + 2 > budget) {
|
|
66681
|
-
dropped.push(section.heading.replace(/^##\s*/, ""));
|
|
66682
|
-
continue;
|
|
66683
|
-
}
|
|
66684
|
-
body3.push(text, "");
|
|
66685
|
-
spent += sizeOf(text) + 2;
|
|
66686
|
-
}
|
|
66687
|
-
return { body: body3, dropped };
|
|
66688
|
-
}
|
|
66689
66637
|
function prComment(result, opts = {}) {
|
|
66690
66638
|
const standard = opts.standard ?? CORE2;
|
|
66691
66639
|
const lang = opts.lang ?? "en";
|
|
@@ -66709,12 +66657,6 @@ function prComment(result, opts = {}) {
|
|
|
66709
66657
|
const tail = [];
|
|
66710
66658
|
if (opts.artifactName) tail.push(s.artifact(opts.artifactName), "");
|
|
66711
66659
|
if (opts.runUrl) tail.push(s.runLink(opts.runUrl), "");
|
|
66712
|
-
const fixed = sizeOf([...head, ...tail].join("\n"));
|
|
66713
|
-
const { body: body3, dropped } = reportSectionsBody(result, standard, lang, Math.max(0, COMMENT_LIMIT - fixed - 512));
|
|
66714
|
-
const notes = [];
|
|
66715
|
-
if (dropped.length) notes.push(s.sectionsDropped(dropped), "");
|
|
66716
|
-
const assembled = [...head, ...body3, ...notes, ...tail].join("\n").trimEnd();
|
|
66717
|
-
if (sizeOf(assembled) <= COMMENT_LIMIT) return assembled;
|
|
66718
66660
|
const assemble = (rows2) => {
|
|
66719
66661
|
const digest = [];
|
|
66720
66662
|
if (criteria.length) {
|
package/skills/ultra11y/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: ultra11y
|
|
|
3
3
|
description: "Use to AUDIT a repository, site, or page against WCAG 2.2 AA or a country standard such as RGAA; produce a dated conformance report, criterion grid, PRD backlog, or tickets; AUTHOR accessible markup; fix accessibility; or wire a repo gate and rendered-DOM capture pipeline. The bundled install-free engine runs 81 static checks tied to success criteria, while the agent adjudicates judgment criteria and routes rendering criteria to browser scans; check/verify reject invented non-conformities. Use `review-a11y` instead for a review scoped only to a diff, branch, or PR."
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
|
-
version: 5.36.
|
|
6
|
+
version: 5.36.1
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# ultra11y — audit WCAG 2.2 AA and write accessible markup
|
|
@@ -6,7 +6,7 @@ import { join as join53, relative as relative5, resolve as resolve16, sep as sep
|
|
|
6
6
|
import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL3 } from "url";
|
|
7
7
|
|
|
8
8
|
// src/types.ts
|
|
9
|
-
var VERSION = "5.36.
|
|
9
|
+
var VERSION = "5.36.1";
|
|
10
10
|
var SCHEMA_VERSION = 2;
|
|
11
11
|
var INAPPLICABLE_STATUS = "C";
|
|
12
12
|
|
|
@@ -58261,41 +58261,6 @@ function exhaustiveGrid(groups, standard, lang) {
|
|
|
58261
58261
|
out2.push("");
|
|
58262
58262
|
return out2;
|
|
58263
58263
|
}
|
|
58264
|
-
function splitReportSections(md) {
|
|
58265
|
-
const lines = md.split("\n");
|
|
58266
|
-
const preamble = [];
|
|
58267
|
-
const sections = [];
|
|
58268
|
-
let current = null;
|
|
58269
|
-
let fence = null;
|
|
58270
|
-
const push = (l) => {
|
|
58271
|
-
if (current) current.push(l);
|
|
58272
|
-
else preamble.push(l);
|
|
58273
|
-
};
|
|
58274
|
-
for (const line of lines) {
|
|
58275
|
-
const f = /^\s*(```+|~~~+)/.exec(line);
|
|
58276
|
-
if (f) {
|
|
58277
|
-
const mark = f[1];
|
|
58278
|
-
if (fence === null) fence = mark[0].repeat(mark.length);
|
|
58279
|
-
else if (mark.startsWith(fence[0]) && mark.length >= fence.length) fence = null;
|
|
58280
|
-
push(line);
|
|
58281
|
-
continue;
|
|
58282
|
-
}
|
|
58283
|
-
if (fence === null && line.startsWith("## ")) {
|
|
58284
|
-
current = [line];
|
|
58285
|
-
const own = current;
|
|
58286
|
-
sections.push({
|
|
58287
|
-
heading: line,
|
|
58288
|
-
lines: own,
|
|
58289
|
-
get text() {
|
|
58290
|
-
return own.join("\n");
|
|
58291
|
-
}
|
|
58292
|
-
});
|
|
58293
|
-
continue;
|
|
58294
|
-
}
|
|
58295
|
-
push(line);
|
|
58296
|
-
}
|
|
58297
|
-
return { preamble, sections };
|
|
58298
|
-
}
|
|
58299
58264
|
function renderPageRates(r, pages, standard, lang) {
|
|
58300
58265
|
if (!pages.length) return [];
|
|
58301
58266
|
const s = L5[lang];
|
|
@@ -61363,13 +61328,18 @@ function subjectsForPackCriterion2(standard, id, scs) {
|
|
|
61363
61328
|
for (const sc of scs) for (const subject of subjectsForSc2(sc)) if (!out2.includes(subject)) out2.push(subject);
|
|
61364
61329
|
return out2;
|
|
61365
61330
|
}
|
|
61331
|
+
function packResultNeedsAdjudication(pc, criterion) {
|
|
61332
|
+
if (pc.status === "manual") return true;
|
|
61333
|
+
const hasJudgmentTest = Object.values(criterion?.automation?.tests ?? {}).includes("judgment");
|
|
61334
|
+
return pc.status === INAPPLICABLE_STATUS && pc.inapplicable === true && pc.decidedBy !== "agent" && hasJudgmentTest;
|
|
61335
|
+
}
|
|
61366
61336
|
function buildAdjudicationWorklist(audit2, opts = {}) {
|
|
61367
61337
|
const docs = docsForAudit(audit2, opts.cwd);
|
|
61368
61338
|
const standard = opts.standard;
|
|
61369
61339
|
const limits = adjudicationLimits(opts.cwd);
|
|
61370
61340
|
if (standard !== void 0 && !isCore(standard)) {
|
|
61371
61341
|
const pack = loadPack(standard);
|
|
61372
|
-
return derivePackResults(audit2, standard).filter((pc) => pc
|
|
61342
|
+
return derivePackResults(audit2, standard).filter((pc) => packResultNeedsAdjudication(pc, getCriterion(pack, pc.id))).map((pc) => {
|
|
61373
61343
|
const crit = getCriterion(pack, pc.id);
|
|
61374
61344
|
const scs = crit?.wcag ?? pc.scs;
|
|
61375
61345
|
const item = blankItem(
|
|
@@ -61570,8 +61540,9 @@ function applyAdjudication(audit2, adj, opts = {}) {
|
|
|
61570
61540
|
const packMode = !isCore(adj.standard);
|
|
61571
61541
|
const open = /* @__PURE__ */ new Set();
|
|
61572
61542
|
if (packMode) {
|
|
61543
|
+
const pack = loadPack(adj.standard);
|
|
61573
61544
|
for (const pc of derivePackResults(audit2, adj.standard)) {
|
|
61574
|
-
if (pc
|
|
61545
|
+
if (!packResultNeedsAdjudication(pc, getCriterion(pack, pc.id))) continue;
|
|
61575
61546
|
open.add(pc.id);
|
|
61576
61547
|
if (byId2.has(pc.id)) continue;
|
|
61577
61548
|
if (Object.hasOwn(expected, pc.id)) uncovered(pc.id);
|
|
@@ -66436,7 +66407,7 @@ var S = {
|
|
|
66436
66407
|
noRenderedExecuted: "0 page rendue : aucun test rendered n'a \xE9t\xE9 ex\xE9cut\xE9",
|
|
66437
66408
|
staticTested: (tests, ids) => `${tests} test(s) static \u2014 crit\xE8res : ${ids || "\u2014"}`,
|
|
66438
66409
|
renderedContract: (tests, ids) => `${tests} test(s) rendered pr\xE9vus \u2014 crit\xE8res : ${ids || "\u2014"}`,
|
|
66439
|
-
judgmentContract: (tests, criteria) => `${tests} test(s) judgment sur ${criteria} crit\xE8re(s), transmis \xE0 l'IA
|
|
66410
|
+
judgmentContract: (tests, criteria) => `${tests} test(s) judgment sur ${criteria} crit\xE8re(s), tous transmis \xE0 l'IA tant qu'ils ne sont pas tranch\xE9s`
|
|
66440
66411
|
},
|
|
66441
66412
|
en: {
|
|
66442
66413
|
title: "ultra11y accessibility audit",
|
|
@@ -66512,7 +66483,7 @@ var S = {
|
|
|
66512
66483
|
noRenderedExecuted: "0 rendered pages: no rendered test was executed",
|
|
66513
66484
|
staticTested: (tests, ids) => `${tests} static test(s) \u2014 criteria: ${ids || "\u2014"}`,
|
|
66514
66485
|
renderedContract: (tests, ids) => `${tests} planned rendered test(s) \u2014 criteria: ${ids || "\u2014"}`,
|
|
66515
|
-
judgmentContract: (tests, criteria) => `${tests} judgment test(s) across ${criteria} criterion(ia), sent to AI
|
|
66486
|
+
judgmentContract: (tests, criteria) => `${tests} judgment test(s) across ${criteria} criterion(ia), all sent to AI while undecided`
|
|
66516
66487
|
}
|
|
66517
66488
|
};
|
|
66518
66489
|
var MAX_ROWS = 50;
|
|
@@ -66663,29 +66634,6 @@ function stepSummary(result, opts = {}) {
|
|
|
66663
66634
|
if (unanchored) out2.push(`> ${s.unanchored(unanchored)}`, "");
|
|
66664
66635
|
return out2.join("\n");
|
|
66665
66636
|
}
|
|
66666
|
-
function reportSectionsBody(result, standard, lang, budget, prefer) {
|
|
66667
|
-
let md;
|
|
66668
|
-
try {
|
|
66669
|
-
md = isCore(standard) ? renderReport(result, lang) : renderPackReport(result, loadPack(standard), lang);
|
|
66670
|
-
} catch {
|
|
66671
|
-
return { body: [], dropped: [] };
|
|
66672
|
-
}
|
|
66673
|
-
const { sections } = splitReportSections(md);
|
|
66674
|
-
const ordered = prefer ? [...sections.filter((x) => prefer.test(x.heading)), ...sections.filter((x) => !prefer.test(x.heading))] : sections;
|
|
66675
|
-
const body3 = [];
|
|
66676
|
-
const dropped = [];
|
|
66677
|
-
let spent = 0;
|
|
66678
|
-
for (const section of ordered) {
|
|
66679
|
-
const text = section.text.trimEnd();
|
|
66680
|
-
if (dropped.length || spent + sizeOf(text) + 2 > budget) {
|
|
66681
|
-
dropped.push(section.heading.replace(/^##\s*/, ""));
|
|
66682
|
-
continue;
|
|
66683
|
-
}
|
|
66684
|
-
body3.push(text, "");
|
|
66685
|
-
spent += sizeOf(text) + 2;
|
|
66686
|
-
}
|
|
66687
|
-
return { body: body3, dropped };
|
|
66688
|
-
}
|
|
66689
66637
|
function prComment(result, opts = {}) {
|
|
66690
66638
|
const standard = opts.standard ?? CORE2;
|
|
66691
66639
|
const lang = opts.lang ?? "en";
|
|
@@ -66709,12 +66657,6 @@ function prComment(result, opts = {}) {
|
|
|
66709
66657
|
const tail = [];
|
|
66710
66658
|
if (opts.artifactName) tail.push(s.artifact(opts.artifactName), "");
|
|
66711
66659
|
if (opts.runUrl) tail.push(s.runLink(opts.runUrl), "");
|
|
66712
|
-
const fixed = sizeOf([...head, ...tail].join("\n"));
|
|
66713
|
-
const { body: body3, dropped } = reportSectionsBody(result, standard, lang, Math.max(0, COMMENT_LIMIT - fixed - 512));
|
|
66714
|
-
const notes = [];
|
|
66715
|
-
if (dropped.length) notes.push(s.sectionsDropped(dropped), "");
|
|
66716
|
-
const assembled = [...head, ...body3, ...notes, ...tail].join("\n").trimEnd();
|
|
66717
|
-
if (sizeOf(assembled) <= COMMENT_LIMIT) return assembled;
|
|
66718
66660
|
const assemble = (rows2) => {
|
|
66719
66661
|
const digest = [];
|
|
66720
66662
|
if (criteria.length) {
|
package/src/adjudicate.ts
CHANGED
|
@@ -400,12 +400,25 @@ function subjectsForPackCriterion(standard: StandardId, id: string, scs: string[
|
|
|
400
400
|
return out;
|
|
401
401
|
}
|
|
402
402
|
|
|
403
|
+
/** Whether a pack result still needs an agent ruling.
|
|
404
|
+
*
|
|
405
|
+
* Manual rows are the ordinary residual set. A judgment criterion provisionally closed as
|
|
406
|
+
* inapplicable is included as well: absence of a harvested subject is useful deterministic
|
|
407
|
+
* evidence, but a FULL audit still asks the agent to confirm that absence and the criterion's
|
|
408
|
+
* particular cases. A prior agent decision is never reopened. */
|
|
409
|
+
function packResultNeedsAdjudication(pc: ReturnType<typeof derivePackResults>[number], criterion?: PackCriterion): boolean {
|
|
410
|
+
if (pc.status === "manual") return true;
|
|
411
|
+
const hasJudgmentTest = Object.values(criterion?.automation?.tests ?? {}).includes("judgment");
|
|
412
|
+
return pc.status === INAPPLICABLE_STATUS && pc.inapplicable === true && pc.decidedBy !== "agent" && hasJudgmentTest;
|
|
413
|
+
}
|
|
414
|
+
|
|
403
415
|
/** Build the adjudication worklist.
|
|
404
416
|
*
|
|
405
417
|
* For the WCAG core: one item per residual-risk (manual) success criterion.
|
|
406
418
|
*
|
|
407
|
-
* For a COUNTRY STANDARD: one item per PACK criterion that derives `manual
|
|
408
|
-
*
|
|
419
|
+
* For a COUNTRY STANDARD: one item per PACK criterion that derives `manual`, plus every
|
|
420
|
+
* judgment criterion provisionally closed for absence — 97 of RGAA's 106 criteria carry
|
|
421
|
+
* judgment tests, and all 97 pass through the agent unless already decided or definitively NC.
|
|
409
422
|
* Keying by the pack's own criteria is not cosmetic: it is what lets an item carry the
|
|
410
423
|
* criterion's numbered tests, and therefore what lets `normativeRefResolves` check a citation
|
|
411
424
|
* against THIS criterion's tests instead of accepting any id of the right shape. */
|
|
@@ -417,7 +430,7 @@ export function buildAdjudicationWorklist(audit: AuditResult, opts: { cwd?: stri
|
|
|
417
430
|
if (standard !== undefined && !isCore(standard)) {
|
|
418
431
|
const pack = loadPack(standard);
|
|
419
432
|
return derivePackResults(audit, standard)
|
|
420
|
-
.filter((pc) => pc
|
|
433
|
+
.filter((pc) => packResultNeedsAdjudication(pc, getCriterion(pack, pc.id)))
|
|
421
434
|
.map((pc) => {
|
|
422
435
|
const crit = getCriterion(pack, pc.id);
|
|
423
436
|
const scs = crit?.wcag ?? pc.scs;
|
|
@@ -816,13 +829,13 @@ export function applyAdjudication(
|
|
|
816
829
|
const byId = new Map(adj.items.map((it) => [it.criteriaId, it]));
|
|
817
830
|
|
|
818
831
|
// Coverage. Under the core that means every residual success criterion; under a pack it
|
|
819
|
-
//
|
|
820
|
-
// what the worklist was built at.
|
|
832
|
+
// mirrors `buildAdjudicationWorklist`, including provisionally-inapplicable judgment rows.
|
|
821
833
|
const packMode = !isCore(adj.standard);
|
|
822
834
|
const open = new Set<string>();
|
|
823
835
|
if (packMode) {
|
|
836
|
+
const pack = loadPack(adj.standard);
|
|
824
837
|
for (const pc of derivePackResults(audit, adj.standard)) {
|
|
825
|
-
if (pc
|
|
838
|
+
if (!packResultNeedsAdjudication(pc, getCriterion(pack, pc.id))) continue;
|
|
826
839
|
open.add(pc.id);
|
|
827
840
|
if (byId.has(pc.id)) continue;
|
|
828
841
|
if (Object.hasOwn(expected, pc.id)) uncovered(pc.id);
|
package/src/annotate.ts
CHANGED
|
@@ -22,7 +22,7 @@ import { resolveMessage, resolveRemediation } from "./messages.js";
|
|
|
22
22
|
import { findingsForStandard } from "./standards/derive.js";
|
|
23
23
|
import { packCriterionLabel } from "./standards/document.js";
|
|
24
24
|
import { CORE, type StandardId, isCore, loadPack } from "./standards/index.js";
|
|
25
|
-
import { automationOverview, packReportGroups,
|
|
25
|
+
import { automationOverview, packReportGroups, reportCoverage, reportGroups } from "./report.js";
|
|
26
26
|
import type { AuditResult, Finding, Lang, PageResult, Severity, Status } from "./types.js";
|
|
27
27
|
import { isUrlPath, repoRelative } from "./util.js";
|
|
28
28
|
import {
|
|
@@ -192,7 +192,7 @@ const S = {
|
|
|
192
192
|
staticTested: (tests: number, ids: string) => `${tests} test(s) static — critères : ${ids || "—"}`,
|
|
193
193
|
renderedContract: (tests: number, ids: string) => `${tests} test(s) rendered prévus — critères : ${ids || "—"}`,
|
|
194
194
|
judgmentContract: (tests: number, criteria: number) =>
|
|
195
|
-
`${tests} test(s) judgment sur ${criteria} critère(s), transmis à l'IA
|
|
195
|
+
`${tests} test(s) judgment sur ${criteria} critère(s), tous transmis à l'IA tant qu'ils ne sont pas tranchés`,
|
|
196
196
|
},
|
|
197
197
|
en: {
|
|
198
198
|
title: "ultra11y accessibility audit",
|
|
@@ -281,8 +281,7 @@ const S = {
|
|
|
281
281
|
noRenderedExecuted: "0 rendered pages: no rendered test was executed",
|
|
282
282
|
staticTested: (tests: number, ids: string) => `${tests} static test(s) — criteria: ${ids || "—"}`,
|
|
283
283
|
renderedContract: (tests: number, ids: string) => `${tests} planned rendered test(s) — criteria: ${ids || "—"}`,
|
|
284
|
-
judgmentContract: (tests: number, criteria: number) =>
|
|
285
|
-
`${tests} judgment test(s) across ${criteria} criterion(ia), sent to AI only while applicable and undecided`,
|
|
284
|
+
judgmentContract: (tests: number, criteria: number) => `${tests} judgment test(s) across ${criteria} criterion(ia), all sent to AI while undecided`,
|
|
286
285
|
},
|
|
287
286
|
} as const;
|
|
288
287
|
|
|
@@ -541,60 +540,6 @@ export function stepSummary(result: AuditResult, opts: AnnotateOptions = {}): st
|
|
|
541
540
|
return out.join("\n");
|
|
542
541
|
}
|
|
543
542
|
|
|
544
|
-
/** THE COMMENT'S BODY IS THE REPORT'S OWN SECTIONS.
|
|
545
|
-
*
|
|
546
|
-
* A pull-request comment used to be a document of its own — a digest, written separately from
|
|
547
|
-
* the audit it summarised. Two documents about one run drift, and a reader who opens the
|
|
548
|
-
* artifact after reading the comment should recognise what they are looking at.
|
|
549
|
-
*
|
|
550
|
-
* So the body is the report, rendered once and split into its `##` sections, kept in report
|
|
551
|
-
* order while the budget lasts. What does not fit is dropped WHOLE and NAMED — a heading a
|
|
552
|
-
* reader can go and find in the artifact — never sliced at a byte offset, which lands
|
|
553
|
-
* mid-table or inside a fence.
|
|
554
|
-
*
|
|
555
|
-
* The report's own preamble is skipped: the comment's head already carries the date, the
|
|
556
|
-
* scope and the rate, and says them in the verdict's voice.
|
|
557
|
-
*
|
|
558
|
-
* `budget` is what is left after the head and the tail, both of which are never candidates
|
|
559
|
-
* for dropping — a comment that fits but says nothing about where to look is worse than no
|
|
560
|
-
* comment at all. */
|
|
561
|
-
function reportSectionsBody(
|
|
562
|
-
result: AuditResult,
|
|
563
|
-
standard: StandardId,
|
|
564
|
-
lang: Lang,
|
|
565
|
-
budget: number,
|
|
566
|
-
/** Headings to take FIRST, still in report order. Not a reordering of the audit: the two
|
|
567
|
-
* comments answer different questions of the same document, and the one that answers
|
|
568
|
-
* "which pages conform" must not lose the page sections to a budget spent on the defect
|
|
569
|
-
* list. What is dropped is named either way. */
|
|
570
|
-
prefer?: RegExp,
|
|
571
|
-
): { body: string[]; dropped: string[] } {
|
|
572
|
-
let md: string;
|
|
573
|
-
try {
|
|
574
|
-
md = isCore(standard) ? renderReport(result, lang) : renderPackReport(result, loadPack(standard), lang);
|
|
575
|
-
} catch {
|
|
576
|
-
// A rendering failure must never cost the comment. The caller still has its head and tail,
|
|
577
|
-
// which carry the verdict and the link — the two things a reviewer cannot do without.
|
|
578
|
-
return { body: [], dropped: [] };
|
|
579
|
-
}
|
|
580
|
-
const { sections } = splitReportSections(md);
|
|
581
|
-
const ordered = prefer ? [...sections.filter((x) => prefer.test(x.heading)), ...sections.filter((x) => !prefer.test(x.heading))] : sections;
|
|
582
|
-
const body: string[] = [];
|
|
583
|
-
const dropped: string[] = [];
|
|
584
|
-
let spent = 0;
|
|
585
|
-
for (const section of ordered) {
|
|
586
|
-
const text = section.text.trimEnd();
|
|
587
|
-
// +2 for the blank line that joins it to what precedes.
|
|
588
|
-
if (dropped.length || spent + sizeOf(text) + 2 > budget) {
|
|
589
|
-
dropped.push(section.heading.replace(/^##\s*/, ""));
|
|
590
|
-
continue;
|
|
591
|
-
}
|
|
592
|
-
body.push(text, "");
|
|
593
|
-
spent += sizeOf(text) + 2;
|
|
594
|
-
}
|
|
595
|
-
return { body, dropped };
|
|
596
|
-
}
|
|
597
|
-
|
|
598
543
|
/** The pull-request digest.
|
|
599
544
|
*
|
|
600
545
|
* Deliberately NOT the job summary. A reviewer wants the verdict, how much of the standard
|
|
@@ -636,19 +581,10 @@ export function prComment(result: AuditResult, opts: AnnotateOptions & { runUrl?
|
|
|
636
581
|
// that comes down until the whole document fits, and the verdict, the rate and the link are
|
|
637
582
|
// never candidates: a comment that fits but says nothing about where to look is worse than
|
|
638
583
|
// no comment at all.
|
|
639
|
-
//
|
|
640
|
-
//
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
const notes: string[] = [];
|
|
644
|
-
if (dropped.length) notes.push(s.sectionsDropped(dropped), "");
|
|
645
|
-
|
|
646
|
-
const assembled = [...head, ...body, ...notes, ...tail].join("\n").trimEnd();
|
|
647
|
-
if (sizeOf(assembled) <= COMMENT_LIMIT) return assembled;
|
|
648
|
-
|
|
649
|
-
// The report could not be cut small enough — a single section larger than the whole budget.
|
|
650
|
-
// Fall back to the digest this comment carried before: the distinct defects and a link, which
|
|
651
|
-
// is the least a reviewer needs. Never a byte-slice of the report.
|
|
584
|
+
// Keep the comment deliberately compact: scope + tested criteria + actionable defects. The
|
|
585
|
+
// exhaustive 106-row grid belongs to the artifact, where it is searchable and does not bury
|
|
586
|
+
// the PR verdict. Whole criterion blocks are removed from the end if GitHub's limit requires
|
|
587
|
+
// it; the verdict, execution scope and artifact/run links always survive.
|
|
652
588
|
const assemble = (rows: number): string => {
|
|
653
589
|
const digest: string[] = [];
|
|
654
590
|
if (criteria.length) {
|
package/src/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Single source of truth for shared types + the embedded version. sync-version.mjs
|
|
2
2
|
// rewrites VERSION at release time (kept in lockstep with package.json + SKILL.md).
|
|
3
|
-
export const VERSION = "5.36.
|
|
3
|
+
export const VERSION = "5.36.1";
|
|
4
4
|
export const SCHEMA_VERSION = 2;
|
|
5
5
|
|
|
6
6
|
// Lang = the UI FRAME's language — the `L` localization tables each module (report,
|