ultra11y 5.5.3 → 5.5.4
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 +13 -3
- package/skills/review-a11y/SKILL.md +1 -1
- package/skills/review-a11y/scripts/ultra11y.mjs +13 -3
- package/skills/ultra11y/SKILL.md +1 -1
- package/skills/ultra11y/scripts/ultra11y.mjs +13 -3
- package/src/adjudicate.ts +24 -2
- package/src/ledger.ts +4 -2
- 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.5.
|
|
13
|
+
"version": "5.5.4",
|
|
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.5.
|
|
4
|
+
"version": "5.5.4",
|
|
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.5.
|
|
4
|
+
"version": "5.5.4",
|
|
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.5.
|
|
30
|
+
const ULTRA11Y_PLUGIN_VERSION = "5.5.4";
|
|
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.5.
|
|
3
|
+
"version": "5.5.4",
|
|
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 join51, 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.5.
|
|
9
|
+
var VERSION = "5.5.4";
|
|
10
10
|
var SCHEMA_VERSION = 2;
|
|
11
11
|
|
|
12
12
|
// src/audit.ts
|
|
@@ -57288,6 +57288,14 @@ function buildAdjudicationWorklist(audit2, opts = {}) {
|
|
|
57288
57288
|
(r) => blankItem(r.criteriaId, r.automatability, scTitle(r.criteriaId) ?? void 0, harvestSubjects(subjectsForSc(r.criteriaId), docs))
|
|
57289
57289
|
);
|
|
57290
57290
|
}
|
|
57291
|
+
function readCitation(c2) {
|
|
57292
|
+
if (typeof c2 !== "string") return c2;
|
|
57293
|
+
const at = c2.lastIndexOf(":");
|
|
57294
|
+
if (at <= 0) return null;
|
|
57295
|
+
const line = Number.parseInt(c2.slice(at + 1), 10);
|
|
57296
|
+
if (!Number.isFinite(line)) return null;
|
|
57297
|
+
return { file: c2.slice(0, at), line, selector: "", snippet: "" };
|
|
57298
|
+
}
|
|
57291
57299
|
function auditFiles(audit2, cwd) {
|
|
57292
57300
|
const inputs = audit2.scope.inputs.filter((i2) => i2 !== "-" && i2 !== "<stdin>");
|
|
57293
57301
|
if (!inputs.length) return /* @__PURE__ */ new Set();
|
|
@@ -57408,7 +57416,7 @@ function applyAdjudication(audit2, adj, opts = {}) {
|
|
|
57408
57416
|
blame(it.criteriaId, `criterion ${it.criteriaId}: unadjudicated (verdict is null)`);
|
|
57409
57417
|
} else if (v === "C" || v === "NA") {
|
|
57410
57418
|
if (!it.justification || !it.justification.trim()) blame(it.criteriaId, `criterion ${it.criteriaId}: a ${v} verdict requires a justification`);
|
|
57411
|
-
const cites = it.citations ?? [];
|
|
57419
|
+
const cites = (it.citations ?? []).map(readCitation).filter((c2) => c2 !== null);
|
|
57412
57420
|
if (v === "C" && it.evidenceComplete === false) {
|
|
57413
57421
|
blame(
|
|
57414
57422
|
it.criteriaId,
|
|
@@ -57843,7 +57851,9 @@ function entriesFrom(adj, accepted, date) {
|
|
|
57843
57851
|
verdict: it.verdict,
|
|
57844
57852
|
...it.justification?.trim() ? { justification: it.justification.trim() } : {},
|
|
57845
57853
|
...it.reason ? { reason: it.reason } : {},
|
|
57846
|
-
|
|
57854
|
+
// Normalised here too: a citation may have arrived as a bare "file:line" string, and a
|
|
57855
|
+
// ledger entry has to hold the object form so a replay can re-anchor and re-ground it.
|
|
57856
|
+
...it.citations?.length ? { citations: it.citations.map(readCitation).filter((c2) => c2 !== null) } : {},
|
|
57847
57857
|
...it.findings?.length ? { findings: it.findings } : {},
|
|
57848
57858
|
...it.recommendations?.length ? { recommendations: it.recommendations } : {},
|
|
57849
57859
|
evidenceFingerprint: evidenceFingerprint(it.evidence),
|
|
@@ -5,7 +5,7 @@ when_to_use: "Invoke WITHOUT being asked whenever changed front-end code should
|
|
|
5
5
|
allowed-tools: Bash(node ${CLAUDE_SKILL_DIR}/scripts/ultra11y.mjs *), Read, Grep, Glob
|
|
6
6
|
license: MIT
|
|
7
7
|
metadata:
|
|
8
|
-
version: 5.5.
|
|
8
|
+
version: 5.5.4
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# review-a11y — accessibility review of changed code
|
|
@@ -6,7 +6,7 @@ import { join as join51, 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.5.
|
|
9
|
+
var VERSION = "5.5.4";
|
|
10
10
|
var SCHEMA_VERSION = 2;
|
|
11
11
|
|
|
12
12
|
// src/audit.ts
|
|
@@ -57288,6 +57288,14 @@ function buildAdjudicationWorklist(audit2, opts = {}) {
|
|
|
57288
57288
|
(r) => blankItem(r.criteriaId, r.automatability, scTitle(r.criteriaId) ?? void 0, harvestSubjects(subjectsForSc(r.criteriaId), docs))
|
|
57289
57289
|
);
|
|
57290
57290
|
}
|
|
57291
|
+
function readCitation(c2) {
|
|
57292
|
+
if (typeof c2 !== "string") return c2;
|
|
57293
|
+
const at = c2.lastIndexOf(":");
|
|
57294
|
+
if (at <= 0) return null;
|
|
57295
|
+
const line = Number.parseInt(c2.slice(at + 1), 10);
|
|
57296
|
+
if (!Number.isFinite(line)) return null;
|
|
57297
|
+
return { file: c2.slice(0, at), line, selector: "", snippet: "" };
|
|
57298
|
+
}
|
|
57291
57299
|
function auditFiles(audit2, cwd) {
|
|
57292
57300
|
const inputs = audit2.scope.inputs.filter((i2) => i2 !== "-" && i2 !== "<stdin>");
|
|
57293
57301
|
if (!inputs.length) return /* @__PURE__ */ new Set();
|
|
@@ -57408,7 +57416,7 @@ function applyAdjudication(audit2, adj, opts = {}) {
|
|
|
57408
57416
|
blame(it.criteriaId, `criterion ${it.criteriaId}: unadjudicated (verdict is null)`);
|
|
57409
57417
|
} else if (v === "C" || v === "NA") {
|
|
57410
57418
|
if (!it.justification || !it.justification.trim()) blame(it.criteriaId, `criterion ${it.criteriaId}: a ${v} verdict requires a justification`);
|
|
57411
|
-
const cites = it.citations ?? [];
|
|
57419
|
+
const cites = (it.citations ?? []).map(readCitation).filter((c2) => c2 !== null);
|
|
57412
57420
|
if (v === "C" && it.evidenceComplete === false) {
|
|
57413
57421
|
blame(
|
|
57414
57422
|
it.criteriaId,
|
|
@@ -57843,7 +57851,9 @@ function entriesFrom(adj, accepted, date) {
|
|
|
57843
57851
|
verdict: it.verdict,
|
|
57844
57852
|
...it.justification?.trim() ? { justification: it.justification.trim() } : {},
|
|
57845
57853
|
...it.reason ? { reason: it.reason } : {},
|
|
57846
|
-
|
|
57854
|
+
// Normalised here too: a citation may have arrived as a bare "file:line" string, and a
|
|
57855
|
+
// ledger entry has to hold the object form so a replay can re-anchor and re-ground it.
|
|
57856
|
+
...it.citations?.length ? { citations: it.citations.map(readCitation).filter((c2) => c2 !== null) } : {},
|
|
57847
57857
|
...it.findings?.length ? { findings: it.findings } : {},
|
|
57848
57858
|
...it.recommendations?.length ? { recommendations: it.recommendations } : {},
|
|
57849
57859
|
evidenceFingerprint: evidenceFingerprint(it.evidence),
|
package/skills/ultra11y/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ description: "Use to AUDIT existing HTML/CSS/JSX against WCAG 2.2 AA accessibili
|
|
|
4
4
|
when_to_use: "Invoke when the user asks for an accessibility AUDIT or a formal deliverable: audit a repo, site or page against WCAG 2.2 AA or a country standard (RGAA, Section 508, EN 301 549); produce a dated conformance report, a criterion grid, a PRD backlog, or tickets (GitHub/GitLab/Jira); author accessible markup from scratch; wire the repo gate (init --hook/--ci) or a rendered-DOM capture pipeline. For a review of the code UNDER CHANGE (diff, branch, PR), use the `review-a11y` skill instead."
|
|
5
5
|
license: MIT
|
|
6
6
|
metadata:
|
|
7
|
-
version: 5.5.
|
|
7
|
+
version: 5.5.4
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# ultra11y — audit WCAG 2.2 AA and write accessible markup
|
|
@@ -6,7 +6,7 @@ import { join as join51, 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.5.
|
|
9
|
+
var VERSION = "5.5.4";
|
|
10
10
|
var SCHEMA_VERSION = 2;
|
|
11
11
|
|
|
12
12
|
// src/audit.ts
|
|
@@ -57288,6 +57288,14 @@ function buildAdjudicationWorklist(audit2, opts = {}) {
|
|
|
57288
57288
|
(r) => blankItem(r.criteriaId, r.automatability, scTitle(r.criteriaId) ?? void 0, harvestSubjects(subjectsForSc(r.criteriaId), docs))
|
|
57289
57289
|
);
|
|
57290
57290
|
}
|
|
57291
|
+
function readCitation(c2) {
|
|
57292
|
+
if (typeof c2 !== "string") return c2;
|
|
57293
|
+
const at = c2.lastIndexOf(":");
|
|
57294
|
+
if (at <= 0) return null;
|
|
57295
|
+
const line = Number.parseInt(c2.slice(at + 1), 10);
|
|
57296
|
+
if (!Number.isFinite(line)) return null;
|
|
57297
|
+
return { file: c2.slice(0, at), line, selector: "", snippet: "" };
|
|
57298
|
+
}
|
|
57291
57299
|
function auditFiles(audit2, cwd) {
|
|
57292
57300
|
const inputs = audit2.scope.inputs.filter((i2) => i2 !== "-" && i2 !== "<stdin>");
|
|
57293
57301
|
if (!inputs.length) return /* @__PURE__ */ new Set();
|
|
@@ -57408,7 +57416,7 @@ function applyAdjudication(audit2, adj, opts = {}) {
|
|
|
57408
57416
|
blame(it.criteriaId, `criterion ${it.criteriaId}: unadjudicated (verdict is null)`);
|
|
57409
57417
|
} else if (v === "C" || v === "NA") {
|
|
57410
57418
|
if (!it.justification || !it.justification.trim()) blame(it.criteriaId, `criterion ${it.criteriaId}: a ${v} verdict requires a justification`);
|
|
57411
|
-
const cites = it.citations ?? [];
|
|
57419
|
+
const cites = (it.citations ?? []).map(readCitation).filter((c2) => c2 !== null);
|
|
57412
57420
|
if (v === "C" && it.evidenceComplete === false) {
|
|
57413
57421
|
blame(
|
|
57414
57422
|
it.criteriaId,
|
|
@@ -57843,7 +57851,9 @@ function entriesFrom(adj, accepted, date) {
|
|
|
57843
57851
|
verdict: it.verdict,
|
|
57844
57852
|
...it.justification?.trim() ? { justification: it.justification.trim() } : {},
|
|
57845
57853
|
...it.reason ? { reason: it.reason } : {},
|
|
57846
|
-
|
|
57854
|
+
// Normalised here too: a citation may have arrived as a bare "file:line" string, and a
|
|
57855
|
+
// ledger entry has to hold the object form so a replay can re-anchor and re-ground it.
|
|
57856
|
+
...it.citations?.length ? { citations: it.citations.map(readCitation).filter((c2) => c2 !== null) } : {},
|
|
57847
57857
|
...it.findings?.length ? { findings: it.findings } : {},
|
|
57848
57858
|
...it.recommendations?.length ? { recommendations: it.recommendations } : {},
|
|
57849
57859
|
evidenceFingerprint: evidenceFingerprint(it.evidence),
|
package/src/adjudicate.ts
CHANGED
|
@@ -139,7 +139,10 @@ export interface AdjudicationItem {
|
|
|
139
139
|
// is what turns "cite something real" into "cite the evidence you were shown". When the
|
|
140
140
|
// harvester found no evidence at all there is nothing to clear, and the honest verdicts
|
|
141
141
|
// are `manual` or `NA`, never `C`.
|
|
142
|
-
|
|
142
|
+
// Objects are what the contract asks for; a bare `"file:line"` string is accepted too,
|
|
143
|
+
// because that is the shape the worklist's own `alsoAt` uses and a real run reached for it.
|
|
144
|
+
// See `readCitation` — the gate is identical either way, a string just carries no snippet.
|
|
145
|
+
citations?: (Evidence | string)[];
|
|
143
146
|
// Non-normative good practices the agent noted on this criterion — folded back into the
|
|
144
147
|
// audit as ADVISORY findings (grounded exactly like an NC finding, but never affecting
|
|
145
148
|
// status: they cannot flip the criterion to NC nor enter conformancePct). Optional.
|
|
@@ -334,6 +337,25 @@ export function buildAdjudicationWorklist(audit: AuditResult, opts: { cwd?: stri
|
|
|
334
337
|
);
|
|
335
338
|
}
|
|
336
339
|
|
|
340
|
+
/** Read a citation whichever way it was written.
|
|
341
|
+
*
|
|
342
|
+
* The contract asks for `{file, line, …}`, and a real run wrote `"src/Foo.tsx:15"` instead —
|
|
343
|
+
* 63 verdicts refused for a shape, not for a claim. It is an unambiguous form and the
|
|
344
|
+
* worklist itself is full of it (`alsoAt` is exactly `file:line`), so an adjudicator
|
|
345
|
+
* reaching for it is being consistent, not careless.
|
|
346
|
+
*
|
|
347
|
+
* Nothing about the gate moves: membership and grounding run on the result either way. A
|
|
348
|
+
* string simply carries no snippet, so grounding falls back to its selector/line checks —
|
|
349
|
+
* weaker evidence, and the reason the object form stays what the contract asks for. */
|
|
350
|
+
export function readCitation(c: Evidence | string): Evidence | null {
|
|
351
|
+
if (typeof c !== "string") return c;
|
|
352
|
+
const at = c.lastIndexOf(":");
|
|
353
|
+
if (at <= 0) return null;
|
|
354
|
+
const line = Number.parseInt(c.slice(at + 1), 10);
|
|
355
|
+
if (!Number.isFinite(line)) return null;
|
|
356
|
+
return { file: c.slice(0, at), line, selector: "", snippet: "" };
|
|
357
|
+
}
|
|
358
|
+
|
|
337
359
|
/** The files this audit actually read. Computed from the audit's own scope inputs, so it is
|
|
338
360
|
* the same set the harvest walked — `discover` only globs, it does not parse, so this costs
|
|
339
361
|
* little and is memoised per fold. */
|
|
@@ -580,7 +602,7 @@ export function applyAdjudication(
|
|
|
580
602
|
// A clearing verdict is gated exactly like an accusing one. Before this, the only
|
|
581
603
|
// check was "the justification is a non-empty string", so `"x"` cleared a criterion —
|
|
582
604
|
// and a model answering C to everything published a conformance nobody had assessed.
|
|
583
|
-
const cites = it.citations ?? [];
|
|
605
|
+
const cites = (it.citations ?? []).map(readCitation).filter((c): c is Evidence => c !== null);
|
|
584
606
|
// An INCOMPLETE reading may report a failure it saw; it may never clear what it never
|
|
585
607
|
// looked at. Before class-based harvesting the evidence was a 30-anchor sample of a
|
|
586
608
|
// population that ran to thousands, so a `C` was routinely a conformity claim over
|
package/src/ledger.ts
CHANGED
|
@@ -30,7 +30,7 @@ import { createHash } from "node:crypto";
|
|
|
30
30
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
31
31
|
import { dirname, join } from "node:path";
|
|
32
32
|
|
|
33
|
-
import { type AdjudicationFile, type AdjudicationItem, type AgentFinding, buildAdjudicationWorklist, type Evidence } from "./adjudicate.js";
|
|
33
|
+
import { type AdjudicationFile, type AdjudicationItem, type AgentFinding, buildAdjudicationWorklist, type Evidence, readCitation } from "./adjudicate.js";
|
|
34
34
|
import { CORE, type StandardId } from "./standards/index.js";
|
|
35
35
|
import type { AuditResult } from "./types.js";
|
|
36
36
|
import { SCHEMA_VERSION } from "./types.js";
|
|
@@ -148,7 +148,9 @@ export function entriesFrom(adj: AdjudicationFile, accepted: ReadonlySet<string>
|
|
|
148
148
|
verdict: it.verdict,
|
|
149
149
|
...(it.justification?.trim() ? { justification: it.justification.trim() } : {}),
|
|
150
150
|
...(it.reason ? { reason: it.reason } : {}),
|
|
151
|
-
|
|
151
|
+
// Normalised here too: a citation may have arrived as a bare "file:line" string, and a
|
|
152
|
+
// ledger entry has to hold the object form so a replay can re-anchor and re-ground it.
|
|
153
|
+
...(it.citations?.length ? { citations: it.citations.map(readCitation).filter((c): c is Evidence => c !== null) } : {}),
|
|
152
154
|
...(it.findings?.length ? { findings: it.findings } : {}),
|
|
153
155
|
...(it.recommendations?.length ? { recommendations: it.recommendations } : {}),
|
|
154
156
|
evidenceFingerprint: evidenceFingerprint(it.evidence),
|
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.5.
|
|
3
|
+
export const VERSION = "5.5.4";
|
|
4
4
|
export const SCHEMA_VERSION = 2;
|
|
5
5
|
|
|
6
6
|
// Lang = the UI FRAME's language — the `L` localization tables each module (report,
|