ultra11y 2.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +223 -0
  3. package/docker/Dockerfile +7 -0
  4. package/docker/package.json +9 -0
  5. package/docker/runner.mjs +23 -0
  6. package/package.json +73 -0
  7. package/scripts/bench.mjs +151 -0
  8. package/scripts/build-act-corpus.mjs +143 -0
  9. package/scripts/build-adjudication.mjs +503 -0
  10. package/scripts/build-pack-rgaa.mjs +397 -0
  11. package/scripts/build-standards.mjs +548 -0
  12. package/scripts/copy-bundle.mjs +39 -0
  13. package/scripts/import-pack.mjs +107 -0
  14. package/scripts/sync-engine.mjs +84 -0
  15. package/scripts/sync-version.mjs +43 -0
  16. package/scripts/ultra11y.mjs +52739 -0
  17. package/scripts/vendor/act-testcases.json +12297 -0
  18. package/scripts/vendor/rgaa/SOURCE.json +8 -0
  19. package/scripts/vendor/rgaa/criteres.json +4144 -0
  20. package/scripts/vendor/rgaa/glossaire.json +480 -0
  21. package/scripts/vendor/wcag-2.2-fr.json +82 -0
  22. package/scripts/vendor/wcag-2.2-sc-universe.json +950 -0
  23. package/scripts/vendor/wcag-2.2-sc.json +574 -0
  24. package/scripts/verify-rgaa-source.mjs +126 -0
  25. package/scripts/verify-skill-bundle.mjs +91 -0
  26. package/skills/review-a11y/SKILL.md +123 -0
  27. package/skills/review-a11y/scripts/ultra11y.mjs +52739 -0
  28. package/skills/ultra11y/SKILL.md +292 -0
  29. package/skills/ultra11y/references/act.md +163 -0
  30. package/skills/ultra11y/references/adjudication.md +599 -0
  31. package/skills/ultra11y/references/audit.md +100 -0
  32. package/skills/ultra11y/references/authoring.md +37 -0
  33. package/skills/ultra11y/references/automation.md +113 -0
  34. package/skills/ultra11y/references/correction.md +41 -0
  35. package/skills/ultra11y/references/criteria.md +129 -0
  36. package/skills/ultra11y/references/cross-file.md +82 -0
  37. package/skills/ultra11y/references/dynamic.md +152 -0
  38. package/skills/ultra11y/references/false-positives.md +81 -0
  39. package/skills/ultra11y/references/fix.md +46 -0
  40. package/skills/ultra11y/references/focus-and-logic.md +64 -0
  41. package/skills/ultra11y/references/forbidden-patterns.md +61 -0
  42. package/skills/ultra11y/references/forms-and-errors.md +147 -0
  43. package/skills/ultra11y/references/guidance.md +55 -0
  44. package/skills/ultra11y/references/judgment.md +90 -0
  45. package/skills/ultra11y/references/media-and-motion.md +116 -0
  46. package/skills/ultra11y/references/methodology.md +72 -0
  47. package/skills/ultra11y/references/naming.md +117 -0
  48. package/skills/ultra11y/references/orchestration.md +44 -0
  49. package/skills/ultra11y/references/packs.md +193 -0
  50. package/skills/ultra11y/references/prd.md +104 -0
  51. package/skills/ultra11y/references/rendered.md +110 -0
  52. package/skills/ultra11y/references/scale.md +49 -0
  53. package/skills/ultra11y/references/standards.md +97 -0
  54. package/skills/ultra11y/references/structure.md +138 -0
  55. package/skills/ultra11y/references/verify.md +88 -0
  56. package/skills/ultra11y/references/widgets.md +106 -0
  57. package/skills/ultra11y/scripts/ultra11y.mjs +52739 -0
  58. package/src/act.ts +441 -0
  59. package/src/adjudicate.ts +630 -0
  60. package/src/aria.ts +230 -0
  61. package/src/audit.ts +396 -0
  62. package/src/auditor.ts +399 -0
  63. package/src/autofill.ts +102 -0
  64. package/src/axe-map.ts +294 -0
  65. package/src/baseline.ts +75 -0
  66. package/src/capture.ts +182 -0
  67. package/src/check.ts +243 -0
  68. package/src/cli.ts +1927 -0
  69. package/src/color.ts +102 -0
  70. package/src/config.ts +182 -0
  71. package/src/crawl.ts +75 -0
  72. package/src/criteria.ts +183 -0
  73. package/src/data/adjudication.json +770 -0
  74. package/src/data/guidance/rgaa.json +2395 -0
  75. package/src/data/standards/rgaa.glossary.json +478 -0
  76. package/src/data/standards/rgaa.json +3454 -0
  77. package/src/data/wcag-universe.json +530 -0
  78. package/src/data/wcag.json +1178 -0
  79. package/src/discover.ts +203 -0
  80. package/src/fix/codemods.ts +76 -0
  81. package/src/fix/diff.ts +64 -0
  82. package/src/fix/edits.ts +126 -0
  83. package/src/fix.ts +215 -0
  84. package/src/gh.ts +214 -0
  85. package/src/glob.ts +206 -0
  86. package/src/graph/build.ts +114 -0
  87. package/src/graph/engine-resolve.ts +0 -0
  88. package/src/graph/graph.ts +126 -0
  89. package/src/graph/imports.ts +359 -0
  90. package/src/graph/resolve.ts +73 -0
  91. package/src/graph/tsconfig.ts +113 -0
  92. package/src/grounding.ts +119 -0
  93. package/src/guidance/index.ts +45 -0
  94. package/src/guidance/types.ts +36 -0
  95. package/src/init.ts +113 -0
  96. package/src/mcp/handlers.ts +388 -0
  97. package/src/mcp/http.ts +260 -0
  98. package/src/mcp/prompts.ts +183 -0
  99. package/src/mcp/protocol.ts +187 -0
  100. package/src/mcp/resources.ts +136 -0
  101. package/src/mcp/server.ts +228 -0
  102. package/src/mcp/stdio.ts +119 -0
  103. package/src/mcp/tools.ts +347 -0
  104. package/src/messages.ts +1011 -0
  105. package/src/name.ts +174 -0
  106. package/src/orchestrate-templates.ts +231 -0
  107. package/src/orchestrate.ts +186 -0
  108. package/src/output.ts +108 -0
  109. package/src/pack.ts +187 -0
  110. package/src/parse/html.ts +258 -0
  111. package/src/parse/jsx-ast.ts +67 -0
  112. package/src/parse/jsx-bridge.ts +247 -0
  113. package/src/parse/jsx.ts +19 -0
  114. package/src/parse/source.ts +75 -0
  115. package/src/prd.ts +406 -0
  116. package/src/project-lock.ts +42 -0
  117. package/src/render.ts +307 -0
  118. package/src/report.ts +399 -0
  119. package/src/rules/colors.ts +125 -0
  120. package/src/rules/cross-registry.ts +264 -0
  121. package/src/rules/cross-rule.ts +83 -0
  122. package/src/rules/forms.ts +590 -0
  123. package/src/rules/frames.ts +33 -0
  124. package/src/rules/headings.ts +227 -0
  125. package/src/rules/images.ts +200 -0
  126. package/src/rules/links.ts +193 -0
  127. package/src/rules/mandatory.ts +173 -0
  128. package/src/rules/multimedia.ts +77 -0
  129. package/src/rules/navigation.ts +190 -0
  130. package/src/rules/presentation.ts +75 -0
  131. package/src/rules/registry.ts +57 -0
  132. package/src/rules/rule.ts +122 -0
  133. package/src/rules/scripts-aria.ts +636 -0
  134. package/src/rules/tables.ts +264 -0
  135. package/src/rules/timing.ts +55 -0
  136. package/src/sample.ts +176 -0
  137. package/src/scan-local.ts +866 -0
  138. package/src/scan.ts +538 -0
  139. package/src/standards/derive.ts +195 -0
  140. package/src/standards/index.ts +31 -0
  141. package/src/standards/pack-rules.ts +111 -0
  142. package/src/standards/pack.ts +81 -0
  143. package/src/standards/registry.ts +100 -0
  144. package/src/standards/types.ts +202 -0
  145. package/src/standards/validate.ts +544 -0
  146. package/src/standards/vocabulary.ts +75 -0
  147. package/src/types.ts +355 -0
  148. package/src/util.ts +32 -0
  149. package/src/vendor/codeindex-engine.d.mts +1009 -0
  150. package/src/vendor/codeindex-engine.mjs +13701 -0
  151. package/src/vendor/engine.meta.json +9 -0
  152. package/src/verify.ts +275 -0
  153. package/src/wcag.ts +128 -0
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env node
2
+ // DEV-ONLY (not in `bin`). The deterministic FAST-PATH scaffolder for the AI-assisted
3
+ // pack ingestion workflow (see skills/ultra11y/references/packs.md). It reads a directory
4
+ // of SocialGouv-style RGAA rule markdown files (frontmatter `title/impact/tags` + a
5
+ // non-compliant ```html block + a compliant ```html block + a reference link to the
6
+ // official criterion) and emits a DRAFT guidance dataset (src/data/guidance/<key>.json):
7
+ // every machine-extractable field filled, and `criterionId` resolved from the rule's
8
+ // reference link where unambiguous, else left null. It NEVER finalizes — the agent
9
+ // resolves the remaining nulls / summaries, then `pack check --guidance` gates the result
10
+ // (a null criterionId or a fabricated SC fails). This keeps the AI honest: it drafts, the
11
+ // deterministic gate refuses fabrication.
12
+ //
13
+ // node scripts/import-pack.mjs --from <dir-of-rules.md> [--pack rgaa] [--out src/data/guidance/rgaa.json]
14
+ import { readFileSync, writeFileSync, readdirSync, existsSync, mkdirSync } from "node:fs";
15
+ import { dirname, join, basename } from "node:path";
16
+ import { fileURLToPath } from "node:url";
17
+
18
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
19
+
20
+ function arg(name, def) {
21
+ const i = process.argv.indexOf(`--${name}`);
22
+ return i !== -1 && process.argv[i + 1] ? process.argv[i + 1] : def;
23
+ }
24
+
25
+ const fromDir = arg("from", null);
26
+ const packKey = arg("pack", "rgaa");
27
+ const outPath = arg("out", join(root, "src", "data", "guidance", `${packKey}.json`));
28
+
29
+ if (!fromDir || !existsSync(fromDir)) {
30
+ console.error("import-pack: --from <dir-of-rule-markdown> is required (e.g. a clone of SocialGouv/skills/skills/rgaa-html-css/rules).");
31
+ process.exit(1);
32
+ }
33
+
34
+ // Load the standards pack so we can map a resolved criterion id → its WCAG SCs.
35
+ const packPath = join(root, "src", "data", "standards", `${packKey}.json`);
36
+ if (!existsSync(packPath)) {
37
+ console.error(`import-pack: standards pack not found: ${packPath}. Build it first (e.g. build:pack:${packKey}).`);
38
+ process.exit(1);
39
+ }
40
+ const pack = JSON.parse(readFileSync(packPath, "utf8"));
41
+ const wcagByCriterion = new Map(pack.criteria.map((c) => [c.id, c.wcag]));
42
+
43
+ // --- tiny markdown parsers (no deps) -------------------------------------------------
44
+ function frontmatter(md) {
45
+ const m = /^---\n([\s\S]*?)\n---/.exec(md);
46
+ if (!m) return {};
47
+ const fm = {};
48
+ for (const line of m[1].split("\n")) {
49
+ const kv = /^([A-Za-z][\w-]*):\s*(.*)$/.exec(line.trim());
50
+ if (kv) fm[kv[1]] = kv[2].replace(/^["']|["']$/g, "");
51
+ }
52
+ return fm;
53
+ }
54
+
55
+ // First two fenced code blocks → { bad, good } (convention: non-compliant first).
56
+ function codeBlocks(md) {
57
+ const blocks = [];
58
+ const re = /```[a-zA-Z]*\n([\s\S]*?)```/g;
59
+ let m;
60
+ while ((m = re.exec(md)) && blocks.length < 4) blocks.push(m[1].replace(/\n$/, ""));
61
+ return { bad: blocks[0], good: blocks[1] };
62
+ }
63
+
64
+ // Resolve an RGAA criterion id from a reference link or "Critère X.Y" mention.
65
+ function criterionId(md) {
66
+ const hash = /criteres?-et-tests[^)\s]*#(\d+\.\d+)\b/i.exec(md) || /#(\d+\.\d+)\b/.exec(md);
67
+ if (hash && wcagByCriterion.has(hash[1])) return hash[1];
68
+ const crit = /crit[eè]re\s+(\d+\.\d+)\b/i.exec(md);
69
+ if (crit && wcagByCriterion.has(crit[1])) return crit[1];
70
+ return null;
71
+ }
72
+
73
+ const files = readdirSync(fromDir).filter((f) => f.endsWith(".md") && !f.startsWith("_"));
74
+ const entries = [];
75
+ let unresolved = 0;
76
+ for (const f of files.sort()) {
77
+ const md = readFileSync(join(fromDir, f), "utf8");
78
+ const fm = frontmatter(md);
79
+ const { bad, good } = codeBlocks(md);
80
+ const id = criterionId(md);
81
+ if (!id) unresolved++;
82
+ const impact = (fm.impact || "").toLowerCase();
83
+ entries.push({
84
+ id: basename(f, ".md"),
85
+ criterionId: id, // null ⇒ the agent must resolve it; `pack check` fails until then
86
+ wcag: id ? wcagByCriterion.get(id) : [],
87
+ title: { fr: fm.title || basename(f, ".md") },
88
+ summary: { fr: fm.impactDescription || fm.title || "" },
89
+ ...(["high", "medium", "low"].includes(impact) ? { impact } : {}),
90
+ examples: bad || good ? [{ lang: "html", ...(bad ? { bad } : {}), ...(good ? { good } : {}) }] : [],
91
+ reference: (md.match(/https?:\/\/[^)\s]*criteres?-et-tests[^)\s]*/i) || [])[0] || "",
92
+ });
93
+ }
94
+
95
+ const dataset = {
96
+ pack: packKey,
97
+ source: `imported from ${fromDir}`,
98
+ license: pack.license,
99
+ attribution: pack.attribution,
100
+ entries,
101
+ };
102
+
103
+ mkdirSync(dirname(outPath), { recursive: true });
104
+ writeFileSync(outPath, JSON.stringify(dataset, null, 2) + "\n");
105
+ console.log(`import-pack: ${entries.length} draft entries → ${outPath}`);
106
+ console.log(`import-pack: ${unresolved} entr${unresolved === 1 ? "y" : "ies"} have an unresolved criterionId (null) — resolve them, then run:`);
107
+ console.log(` node scripts/ultra11y.mjs pack check ${packPath} --guidance ${outPath}`);
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env node
2
+ // Vendor the codeindex engine (https://github.com/maxgfr/codeindex) into
3
+ // src/vendor/, pinned to a release tag. The engine is a self-contained
4
+ // zero-dependency ESM bundle; tsup inlines it into scripts/ultra11y.mjs at
5
+ // build time, so the skill still ships as one file.
6
+ //
7
+ // node scripts/sync-engine.mjs --ref v2.0.1 # fetch + pin
8
+ // node scripts/sync-engine.mjs --check # offline drift/tamper gate (CI)
9
+ //
10
+ // The fetched bytes are written UNMODIFIED (byte-identical to upstream) —
11
+ // engine.meta.json carries the pin ({ tag, engineVersion, sha256, syncedAt }),
12
+ // and --check re-hashes the vendored files against it.
13
+ import { createHash } from "node:crypto";
14
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
15
+ import { dirname, join } from "node:path";
16
+ import { fileURLToPath } from "node:url";
17
+
18
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
19
+ const vendorDir = join(root, "src", "vendor");
20
+ const files = [
21
+ { remote: "scripts/engine.mjs", local: "codeindex-engine.mjs" },
22
+ { remote: "scripts/engine.d.mts", local: "codeindex-engine.d.mts" },
23
+ ];
24
+ const metaPath = join(vendorDir, "engine.meta.json");
25
+
26
+ const sha256 = (buf) => createHash("sha256").update(buf).digest("hex");
27
+
28
+ const args = process.argv.slice(2);
29
+ if (args[0] === "--check") {
30
+ let meta;
31
+ try {
32
+ meta = JSON.parse(readFileSync(metaPath, "utf8"));
33
+ } catch {
34
+ console.error("sync-engine: no engine.meta.json — run `node scripts/sync-engine.mjs --ref <tag>` first");
35
+ process.exit(1);
36
+ }
37
+ let ok = true;
38
+ for (const f of files) {
39
+ const actual = sha256(readFileSync(join(vendorDir, f.local)));
40
+ if (actual !== meta.sha256[f.local]) {
41
+ console.error(`sync-engine: DRIFT in src/vendor/${f.local} — vendored bytes differ from the ${meta.tag} pin`);
42
+ ok = false;
43
+ }
44
+ }
45
+ if (!ok) process.exit(1);
46
+ console.log(`sync-engine: vendored engine matches the ${meta.tag} pin (${meta.engineVersion})`);
47
+ process.exit(0);
48
+ }
49
+
50
+ const refIdx = args.indexOf("--ref");
51
+ const ref = refIdx !== -1 ? args[refIdx + 1] : undefined;
52
+ if (!ref) {
53
+ console.error("usage: sync-engine.mjs --ref <tag> | sync-engine.mjs --check");
54
+ process.exit(1);
55
+ }
56
+
57
+ mkdirSync(vendorDir, { recursive: true });
58
+ const sums = {};
59
+ for (const f of files) {
60
+ const url = `https://raw.githubusercontent.com/maxgfr/codeindex/${ref}/${f.remote}`;
61
+ const res = await fetch(url);
62
+ if (!res.ok) {
63
+ console.error(`sync-engine: ${url} -> HTTP ${res.status}`);
64
+ process.exit(1);
65
+ }
66
+ const buf = Buffer.from(await res.arrayBuffer());
67
+ writeFileSync(join(vendorDir, f.local), buf);
68
+ sums[f.local] = sha256(buf);
69
+ console.log(`sync-engine: src/vendor/${f.local} (${buf.length} bytes)`);
70
+ }
71
+
72
+ // The bundle embeds its version greppably — refuse a tag/content mismatch.
73
+ const bundle = readFileSync(join(vendorDir, files[0].local), "utf8");
74
+ const version = bundle.match(/ENGINE_VERSION = "([^"]+)"/)?.[1];
75
+ if (!version || `v${version}` !== ref) {
76
+ console.error(`sync-engine: bundle says ENGINE_VERSION=${version ?? "?"} but the pinned ref is ${ref}`);
77
+ process.exit(1);
78
+ }
79
+
80
+ writeFileSync(
81
+ metaPath,
82
+ JSON.stringify({ tag: ref, engineVersion: version, sha256: sums, syncedAt: new Date().toISOString() }, null, 2) + "\n",
83
+ );
84
+ console.log(`sync-engine: pinned codeindex ${ref}`);
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+ // Sync the release version across every place it lives, then let the caller
3
+ // rebuild the bundle. Invoked by @semantic-release/exec (prepareCmd):
4
+ // node scripts/sync-version.mjs <version>
5
+ //
6
+ // The version is duplicated in package.json, src/types.ts (the value the bundle
7
+ // embeds) and the skill's SKILL.md frontmatter; semantic-release computes it
8
+ // from the Conventional Commits, so this keeps them all in lockstep. CHANGELOG.md
9
+ // is owned by @semantic-release/changelog and is NOT touched here.
10
+ import { readFileSync, writeFileSync } from "node:fs";
11
+
12
+ const version = process.argv[2];
13
+ if (!version || !/^\d+\.\d+\.\d+(?:-[\w.]+)?$/.test(version)) {
14
+ console.error(`sync-version: expected a semver version, got "${version ?? ""}"`);
15
+ process.exit(1);
16
+ }
17
+
18
+ function edit(path, transform) {
19
+ const before = readFileSync(path, "utf8");
20
+ const after = transform(before);
21
+ if (after === before) {
22
+ console.error(`sync-version: WARNING — no change applied to ${path}`);
23
+ }
24
+ writeFileSync(path, after);
25
+ }
26
+
27
+ const setVersionField = (s) => s.replace(/(\n[ \t]+version:[ \t]*)[^\n]+/, `$1${version}`);
28
+
29
+ // package.json — the top-level "version" field.
30
+ edit("package.json", (s) => s.replace(/("version":\s*")[^"]+(")/, `$1${version}$2`));
31
+
32
+ // src/types.ts — the VERSION constant the CLI/bundle reports.
33
+ edit("src/types.ts", (s) => s.replace(/(export const VERSION = ")[^"]+(";)/, `$1${version}$2`));
34
+
35
+ // Each skill's SKILL.md — the indented `version:` under the `metadata:` block.
36
+ // BOTH skills ship from this repo; forgetting one leaves its published
37
+ // frontmatter (and, without the matching .releaserc asset, its bundle) stale.
38
+ edit("skills/ultra11y/SKILL.md", setVersionField);
39
+ edit("skills/review-a11y/SKILL.md", setVersionField);
40
+
41
+ console.log(
42
+ `sync-version: set ${version} in package.json, src/types.ts, skills/ultra11y/SKILL.md, skills/review-a11y/SKILL.md`,
43
+ );