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.
@@ -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
+ [![CI](https://github.com/maxgfr/ultra11y/actions/workflows/ci.yml/badge.svg)](https://github.com/maxgfr/ultra11y/actions/workflows/ci.yml)
4
+ [![npm](https://img.shields.io/npm/v/ultra11y?logo=npm)](https://www.npmjs.com/package/ultra11y)
5
+ [![Claude Code plugin](https://img.shields.io/badge/Claude_Code-plugin-6b46c1)](#add-to-claude-code)
6
+ [![node](https://img.shields.io/node/v/ultra11y)](https://nodejs.org)
7
+ [![license](https://img.shields.io/npm/l/ultra11y)](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 28 `references/*.md` are served under `skill://`, read off
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
 
@@ -0,0 +1,16 @@
1
+ {
2
+ "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "matcher": "Bash",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/pre-tool-use.mjs\"",
10
+ "timeout": 30
11
+ }
12
+ ]
13
+ }
14
+ ]
15
+ }
16
+ }
@@ -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.24.0",
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"
@@ -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
  );