ultra11y 2.24.0 → 2.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +22 -0
- package/.claude-plugin/plugin.json +9 -0
- package/README.md +46 -1
- package/hooks/hooks.json +16 -0
- package/hooks/pre-tool-use.mjs +47 -0
- package/package.json +4 -2
- package/scripts/sync-version.mjs +11 -1
- package/scripts/ultra11y.mjs +3815 -1625
- package/scripts/vendor/wcag-2.2-fr.json +1 -1
- package/scripts/vendor/wcag-2.2-sc-universe.json +1 -1
- package/scripts/verify-skill-bundle.mjs +104 -7
- package/skills/review-a11y/SKILL.md +9 -3
- package/skills/review-a11y/scripts/ultra11y.mjs +3815 -1625
- package/skills/ultra11y/SKILL.md +5 -2
- package/skills/ultra11y/references/automation.md +37 -0
- package/skills/ultra11y/scripts/ultra11y.mjs +3815 -1625
- package/src/cli.ts +31 -0
- package/src/config.ts +4 -0
- package/src/data/wcag-universe.json +1 -1
- package/src/hook.ts +0 -0
- package/src/types.ts +1 -1
- package/src/vendor/codeindex-engine.d.mts +227 -31
- package/src/vendor/codeindex-engine.mjs +3045 -1033
- package/src/vendor/engine.meta.json +5 -5
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ultra11y",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "maxgfr",
|
|
5
|
+
"url": "https://github.com/maxgfr"
|
|
6
|
+
},
|
|
7
|
+
"description": "Accessibility tooling for coding agents: WCAG 2.2 AA and country standards, engine-decided where it can be, agent-adjudicated where it must be.",
|
|
8
|
+
"plugins": [
|
|
9
|
+
{
|
|
10
|
+
"name": "ultra11y",
|
|
11
|
+
"source": "./",
|
|
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": "2.26.0",
|
|
14
|
+
"author": { "name": "maxgfr" },
|
|
15
|
+
"homepage": "https://github.com/maxgfr/ultra11y",
|
|
16
|
+
"repository": "https://github.com/maxgfr/ultra11y",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"category": "quality",
|
|
19
|
+
"keywords": ["accessibility", "a11y", "wcag", "rgaa", "code-review", "audit"]
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ultra11y",
|
|
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": "2.26.0",
|
|
5
|
+
"author": { "name": "maxgfr" },
|
|
6
|
+
"homepage": "https://github.com/maxgfr/ultra11y",
|
|
7
|
+
"repository": "https://github.com/maxgfr/ultra11y",
|
|
8
|
+
"license": "MIT"
|
|
9
|
+
}
|
package/README.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# ultra11y
|
|
2
2
|
|
|
3
|
+
[](https://github.com/maxgfr/ultra11y/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/ultra11y)
|
|
5
|
+
[](#add-to-claude-code)
|
|
6
|
+
[](https://nodejs.org)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+
### Add to Claude Code
|
|
10
|
+
|
|
11
|
+
Two lines, and the accessibility review runs **by itself** before every commit, push and
|
|
12
|
+
pull request — you never type the skill's name:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
/plugin marketplace add maxgfr/ultra11y
|
|
16
|
+
/plugin install ultra11y@ultra11y
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
<sub>Prefer the skills only, without the automatic hook? `npx skills add maxgfr/ultra11y`.
|
|
20
|
+
Full details, thresholds and off-switches under [Install](#install).</sub>
|
|
21
|
+
|
|
3
22
|
> Audit HTML/CSS/JSX against **WCAG 2.2 AA** accessibility and produce a dated compliance report — or author/review accessible markup without regressions. A [skills.sh](https://skills.sh) agent skill: a deterministic, zero-dependency static engine **plus** the agent's judgment, with `check`/`verify` gates against hallucinated non-conformities. **The central deliverable is the auditor conformance block** — theme, criterion + official wording, test(s), WCAG mapping + level, finding, expected state, verification, `file:line` occurrences — rendered identically by the `report` (compliance doc), the `prd` backlog and the GitHub issues, in the active standard's vocabulary and **in your language** (`--lang auto` follows the conversation/repo). **WCAG is the worldwide core; country standards (RGAA, …) are pluggable in-repo packs.**
|
|
4
23
|
|
|
5
24
|
ultra11y is built around an honest **division of labour**. Automated tools only catch a fraction of accessibility problems, so the engine does the *mechanical* work — 78 machine-detectable static checks tied to the WCAG 2.2 success criteria — and is explicit about everything it can't decide statically. What it can't, the **AI agent adjudicates** (statically, from the evidence, gated by `verify`/`check`) — not a deferral to a human:
|
|
@@ -45,6 +64,32 @@ install-free engine.
|
|
|
45
64
|
npx skills add maxgfr/ultra11y
|
|
46
65
|
```
|
|
47
66
|
|
|
67
|
+
**As a Claude Code plugin** — the same two skills, plus the hook that makes the review
|
|
68
|
+
**automatic**. Installed this way, a pending `git commit`, `git push` or `gh pr create`
|
|
69
|
+
that carries accessibility findings is stopped once and the agent is handed the findings,
|
|
70
|
+
so `review-a11y` runs before the work goes out — you never type the skill's name.
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
/plugin marketplace add maxgfr/ultra11y
|
|
74
|
+
/plugin install ultra11y@ultra11y
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Three things worth knowing, because they bound what "automatic" means here:
|
|
78
|
+
|
|
79
|
+
- A hook **cannot force** a skill to be invoked. It blocks the command and hands over the
|
|
80
|
+
reason and the findings; the agent is what invokes `review-a11y`. In practice a motivated
|
|
81
|
+
`deny` is enough — it is not a guarantee.
|
|
82
|
+
- **Hooks do not enable themselves.** Installing the plugin is what turns them on.
|
|
83
|
+
- Claude Code has **no git event** (no `PreCommit`, no `PRCreated`), which is why the hook
|
|
84
|
+
listens on `PreToolUse` for the Bash tool and recognises the commands that publish work.
|
|
85
|
+
|
|
86
|
+
The gate never blocks twice for the same findings, so a retry always lands. It stays out of
|
|
87
|
+
the way when git's own `no-verify` bypass is used, outside a repository, and whenever the
|
|
88
|
+
engine cannot answer.
|
|
89
|
+
Disable it per-repo with `"hook": { "failOn": "off" }` in `.ultra11yrc.json`, per-command
|
|
90
|
+
with `SKIP_A11Y=1`, or per-session with `ULTRA11Y_HOOK=off`; raise or lower the bar with
|
|
91
|
+
`ULTRA11Y_HOOK_FAIL_ON=blocking|major|minor` (default `blocking`).
|
|
92
|
+
|
|
48
93
|
**As an MCP server** — so an agent drives the engine as tools instead of shelling out:
|
|
49
94
|
|
|
50
95
|
```sh
|
|
@@ -159,7 +204,7 @@ yours**.
|
|
|
159
204
|
|
|
160
205
|
### Resources — the skill's own documentation
|
|
161
206
|
|
|
162
|
-
`SKILL.md` and all
|
|
207
|
+
`SKILL.md` and all 32 `references/*.md` are served under `skill://`, read off
|
|
163
208
|
disk at request time — so a documentation fix reaches every client without a
|
|
164
209
|
rebuild.
|
|
165
210
|
|
package/hooks/hooks.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// The plugin's PreToolUse guard — deliberately tiny.
|
|
3
|
+
//
|
|
4
|
+
// Claude Code has no git-aware hook event, so this runs on EVERY Bash tool call. The
|
|
5
|
+
// engine next to it is a 2.4 MB bundle; parsing that before each shell command would be
|
|
6
|
+
// an unacceptable tax on a session. So the split is: this file pays only a bare Node
|
|
7
|
+
// start and answers "could this possibly be a publishing command?", and the engine —
|
|
8
|
+
// spawned only when the answer is yes — makes the real decision (src/hook.ts).
|
|
9
|
+
//
|
|
10
|
+
// The prefilter is intentionally OVER-permissive: it matches any command mentioning git
|
|
11
|
+
// or gh at all. Precision lives in one place, `matchGitIntent`, so the two cannot drift.
|
|
12
|
+
//
|
|
13
|
+
// Contract with Claude Code: print the hook JSON on stdout to act, print nothing to stay
|
|
14
|
+
// out of the way. Exit code is ALWAYS 0 — a guard that errors out on a user's `git push`
|
|
15
|
+
// is worse than no guard, so every failure path here is silence.
|
|
16
|
+
import { spawnSync } from "node:child_process";
|
|
17
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
18
|
+
import { dirname, join } from "node:path";
|
|
19
|
+
import { fileURLToPath } from "node:url";
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const payload = JSON.parse(readFileSync(0, "utf8"));
|
|
23
|
+
if (payload.tool_name !== "Bash") process.exit(0);
|
|
24
|
+
const command = payload.tool_input?.command ?? "";
|
|
25
|
+
// Prefilter: no mention of git or gh ⇒ nothing to do, and we never touched the engine.
|
|
26
|
+
if (!/\bgit\b|\bgh\b/.test(command)) process.exit(0);
|
|
27
|
+
|
|
28
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
29
|
+
// The engine ships inside the review-a11y skill of this same plugin; the repo checkout
|
|
30
|
+
// keeps its own copy at the root. Both are the same bytes (scripts/copy-bundle.mjs).
|
|
31
|
+
const engine = [join(here, "..", "skills", "review-a11y", "scripts", "ultra11y.mjs"), join(here, "..", "scripts", "ultra11y.mjs")].find((p) => existsSync(p));
|
|
32
|
+
if (!engine) process.exit(0);
|
|
33
|
+
|
|
34
|
+
const run = spawnSync(process.execPath, [engine, "hook", "--claude-code"], {
|
|
35
|
+
input: JSON.stringify(payload),
|
|
36
|
+
encoding: "utf8",
|
|
37
|
+
timeout: 20_000,
|
|
38
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
39
|
+
});
|
|
40
|
+
const out = (run.stdout ?? "").trim();
|
|
41
|
+
// Only ever forward something that parses as the expected decision — a stray warning on
|
|
42
|
+
// stdout must not reach Claude Code as a malformed hook response.
|
|
43
|
+
if (out.startsWith("{") && out.includes("hookSpecificOutput")) process.stdout.write(out);
|
|
44
|
+
} catch {
|
|
45
|
+
/* unreadable payload, spawn failure, timeout — say nothing */
|
|
46
|
+
}
|
|
47
|
+
process.exit(0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultra11y",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.26.0",
|
|
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",
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
"scripts",
|
|
12
12
|
"src",
|
|
13
13
|
"skills",
|
|
14
|
-
"docker"
|
|
14
|
+
"docker",
|
|
15
|
+
".claude-plugin",
|
|
16
|
+
"hooks"
|
|
15
17
|
],
|
|
16
18
|
"engines": {
|
|
17
19
|
"node": ">=22.18.0"
|
package/scripts/sync-version.mjs
CHANGED
|
@@ -35,9 +35,19 @@ edit("src/types.ts", (s) => s.replace(/(export const VERSION = ")[^"]+(";)/, `$1
|
|
|
35
35
|
// Each skill's SKILL.md — the indented `version:` under the `metadata:` block.
|
|
36
36
|
// BOTH skills ship from this repo; forgetting one leaves its published
|
|
37
37
|
// frontmatter (and, without the matching .releaserc asset, its bundle) stale.
|
|
38
|
+
// NOTE: setVersionField rewrites the FIRST indented `version:` line, so any new
|
|
39
|
+
// frontmatter key added above `metadata:` must not itself be an indented
|
|
40
|
+
// `version:` — verify-skill-bundle.mjs also asserts the shape.
|
|
38
41
|
edit("skills/ultra11y/SKILL.md", setVersionField);
|
|
39
42
|
edit("skills/review-a11y/SKILL.md", setVersionField);
|
|
40
43
|
|
|
44
|
+
// The Claude Code plugin manifests — what carries the automatic-review hook. A stale
|
|
45
|
+
// version here is what makes an installed plugin report the wrong engine; both list it,
|
|
46
|
+
// so both are rewritten (verify-skill-bundle.mjs fails the build if they drift).
|
|
47
|
+
const setJsonVersion = (s) => s.replace(/("version":\s*")[^"]+(")/, `$1${version}$2`);
|
|
48
|
+
edit(".claude-plugin/plugin.json", setJsonVersion);
|
|
49
|
+
edit(".claude-plugin/marketplace.json", setJsonVersion);
|
|
50
|
+
|
|
41
51
|
console.log(
|
|
42
|
-
`sync-version: set ${version} in package.json, src/types.ts, skills/ultra11y/SKILL.md, skills/review-a11y/SKILL.md`,
|
|
52
|
+
`sync-version: set ${version} in package.json, src/types.ts, skills/ultra11y/SKILL.md, skills/review-a11y/SKILL.md, .claude-plugin/plugin.json, .claude-plugin/marketplace.json`,
|
|
43
53
|
);
|