wcag-a11y 0.3.6 → 0.4.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/README.md CHANGED
@@ -2,17 +2,17 @@
2
2
 
3
3
  [![CI](https://github.com/Dannyplusplus12/WCAG-A11y/actions/workflows/ci.yml/badge.svg)](https://github.com/Dannyplusplus12/WCAG-A11y/actions/workflows/ci.yml)
4
4
 
5
- Most accessibility auditors stop at detection — they tell you *what* is broken and leave the rest to you. `wcag-a11y` goes further. It crawls your running dev server with Playwright, runs 40+ WCAG 2.1/2.2 checks, and uses AI to generate ready-to-paste fix prompts **or write the fixes directly into your source files**.
5
+ Most accessibility auditors stop at detection — they tell you *what* is broken and leave the rest to you. `wcag-a11y` crawls your running dev server with Playwright, runs 40+ WCAG 2.1/2.2 checks, and uses AI to generate ready-to-paste fix prompts **or write the fixes directly into your source files**.
6
6
 
7
7
  Two modes:
8
- - **`scan`** — find violations + generate AI prompts you paste into Cursor, Copilot, or Claude
8
+ - **`scan`** — find violations + get AI prompts you paste into Cursor, Copilot, or Claude
9
9
  - **`fix`** — find violations + patch source files automatically (dry-run by default, `--apply` to write)
10
10
 
11
11
  ---
12
12
 
13
13
  ## Try it instantly
14
14
 
15
- No dev server, no config, no setup:
15
+ No dev server, no config:
16
16
 
17
17
  ```bash
18
18
  npx wcag-a11y demo
@@ -22,41 +22,50 @@ npx wcag-a11y demo
22
22
 
23
23
  ## What the output looks like
24
24
 
25
- Running a scan prints a violation summary per page, then AI-generated fixes for each rule:
26
-
27
25
  ```
28
- Scanning http://localhost:3000...
29
-
30
- /
31
- ✖ critical img-alt 3 violations
32
- ✖ serious color-contrast-text 2 violations
33
- ✖ serious label-missing 1 violation
34
- ✖ moderate no-positive-tabindex 1 violation
35
-
36
- 7 violations across 1 page
37
-
38
- Generating AI fixes for 7 violations...
39
-
40
- ────────────────────────────────────────────
41
- [img-alt] 3 elements affected
42
- #hero-img #logo #banner
43
-
44
- Why it matters:
45
- Screen readers cannot describe the image to blind users without an alt attribute.
46
- Users relying on assistive technology receive no information about the image content.
47
-
48
- Fixed HTML:
49
- <img src="banner.jpg" alt="Summer sale — up to 50% off">
50
-
51
- Prompt for your AI editor:
52
- Fix accessibility: 3 <img> elements (#hero-img, #logo, #banner) are missing alt
53
- attributes, violating WCAG 1.1.1. Add descriptive alt text to each image.
54
- ────────────────────────────────────────────
26
+ WCAG A11y — scan complete
27
+ ────────────────────────────────────────────────────────────
28
+
29
+ http://localhost:3000 3 critical 4 serious 3 moderate
30
+
31
+ [CRITICAL] Images must have an alt attribute WCAG 1.1.1
32
+ img
33
+ [CRITICAL] Form inputs must have an associated label WCAG 1.3.1
34
+ input[type="email"]
35
+ [CRITICAL] Buttons must have an accessible name WCAG 4.1.2
36
+ button[type="submit"]
37
+ [SERIOUS] Normal text must meet 4.5:1 contrast ratio WCAG 1.4.3
38
+ → p
39
+ [SERIOUS] Links must have descriptive text WCAG 2.4.4
40
+ → a[href="/sale"]
41
+ … and 5 more
42
+
43
+ ────────────────────────────────────────────────────────────
44
+ Total: 3 critical · 4 serious · 3 moderate
45
+
46
+ AI Fix Prompts — paste any of these into Cursor, Copilot, or Claude
47
+ ────────────────────────────────────────────────────────────
48
+
49
+ [img-alt]
50
+ img
51
+ Screen reader users hear nothing for this image branding, instructions,
52
+ or data it conveys is completely invisible to them.
53
+ ┌─ Copy this prompt ──────────────────────────────────────
54
+ │ Fix WCAG 1.1.1 (Level A) — img is missing an alt attribute
55
+
56
+ │ Current HTML:
57
+ │ <img src="banner.jpg">
58
+
59
+ │ How to fix:
60
+ │ Add alt text describing the image content.
61
+ │ Use alt="" if the image is purely decorative.
62
+ │ Example: <img src="banner.jpg" alt="Summer sale — 50% off">
63
+ └─────────────────────────────────────────────────────────
64
+
65
+ … 9 more prompts — full report saved to a11y-report.md
55
66
  ```
56
67
 
57
- The **prompt** at the end of each fix is what you copy into Cursor, Copilot, or Claude. It includes the affected selectors, the WCAG rule, and exactly what needs to change no rewriting needed.
58
-
59
- With `--report`, the full output is also saved to `a11y-report.md`.
68
+ Each prompt tells you the WCAG criterion, shows the broken element, and gives the exact fix ready to paste into your AI editor.
60
69
 
61
70
  ---
62
71
 
@@ -66,56 +75,21 @@ With `--report`, the full output is also saved to `a11y-report.md`.
66
75
  npm install -g wcag-a11y
67
76
  ```
68
77
 
69
- ## Setup
78
+ ## Quick start
70
79
 
71
80
  ```bash
72
- wcag-a11y init # Gemini (free, default)
73
- wcag-a11y init --provider openai # OpenAI
74
- wcag-a11y init --provider ollama # Local — no API key needed
75
- ```
81
+ # 1. Configure your AI provider (Gemini is free, no credit card)
82
+ wcag-a11y init
76
83
 
77
- Each command creates an `a11y.config.json` pre-wired for that provider. Fill in your API key, then scan.
78
-
79
- **Get a free Gemini key:** https://aistudio.google.com
80
- **Get an OpenAI key:** https://platform.openai.com/api-keys
81
- **Ollama (local):** install from https://ollama.com, then run `ollama serve`
82
-
83
- ---
84
-
85
- ## Commands
86
-
87
- ### `wcag-a11y demo`
88
-
89
- Scan a built-in page with 10 intentional violations. No dev server or config required — useful for trying the tool before pointing it at your own project.
90
-
91
- ```bash
92
- wcag-a11y demo # violations + AI fixes (default, requires config)
93
- wcag-a11y demo --no-ai # violations only, no AI — faster
94
- wcag-a11y demo --report # + save a11y-report.md
84
+ # 2. Start your dev server, then scan
85
+ wcag-a11y scan -u http://localhost:3000
95
86
  ```
96
87
 
97
- | Flag | Description |
98
- |---|---|
99
- | `--no-ai` | Skip AI fix generation — prints violations only, no prompts |
100
- | `-r, --report` | Save the full report to `a11y-report.md` in the current directory |
88
+ Add `--pages / /about /contact` to scan specific routes, or `--crawl` to follow links automatically.
101
89
 
102
90
  ---
103
91
 
104
- ### `wcag-a11y init`
105
-
106
- Create `a11y.config.json` in the current directory, pre-configured for your chosen provider.
107
-
108
- ```bash
109
- wcag-a11y init # Gemini (default)
110
- wcag-a11y init --provider openai # OpenAI
111
- wcag-a11y init --provider ollama # Ollama (local)
112
- ```
113
-
114
- | Flag | Description |
115
- |---|---|
116
- | `--provider <name>` | Which provider to configure: `gemini` (default), `openai`, or `ollama`. Determines which fields are written to the config file. |
117
-
118
- ---
92
+ ## Commands
119
93
 
120
94
  ### `wcag-a11y scan`
121
95
 
@@ -124,21 +98,24 @@ Scan a running dev server for accessibility violations.
124
98
  ```bash
125
99
  wcag-a11y scan -u http://localhost:3000
126
100
  wcag-a11y scan -u http://localhost:3000 --pages / /about /contact
127
- wcag-a11y scan -u http://localhost:3000 --crawl --report
101
+ wcag-a11y scan -u http://localhost:3000 --crawl
128
102
  wcag-a11y scan -u http://localhost:3000 --no-ai --ci
103
+ wcag-a11y scan -u http://localhost:3000 --terminal --fast-mode
129
104
  ```
130
105
 
131
106
  | Flag | Default | Description |
132
107
  |---|---|---|
133
108
  | `-u, --url <url>` | required | Base URL of your running dev server |
134
- | `-p, --pages <pages...>` | `/` | One or more paths to scan. Separate with spaces: `--pages / /about /contact` |
135
- | `-c, --crawl` | off | Follow same-origin links and scan all reachable pages automatically |
136
- | `-r, --report` | off | Save the full scan output to `a11y-report.md` |
137
- | `--no-ai` | on | Skip AI fix generation — scan runs faster and prints violations only |
138
- | `--no-explain` | off | Print only the ready-to-paste prompt for each fix, without the AI explanation |
139
- | `--group <strategy>` | `rule` | `rule` (default) groups all violations of the same type into one fix prompt. `none` produces a separate prompt per element. Use `none` when violations of the same rule need different fixes |
140
- | `--ci` | off | Exit with code `1` if any violations are found. Use this to fail a CI pipeline |
141
- | `--provider <name>` | from config | Override the AI provider for this run: `gemini`, `openai`, or `ollama`. Does not modify the config file |
109
+ | `-p, --pages <paths...>` | `/` | Paths to scan. Space-separated: `--pages / /about /contact` |
110
+ | `-c, --crawl` | off | Follow same-origin links and scan all reachable pages |
111
+ | `--no-ai` | | Skip AI fix generation — scan runs faster, violations only |
112
+ | `--no-report` | | Skip saving `a11y-report.md` |
113
+ | `--no-explain` | | Omit explanations, show prompts only |
114
+ | `--terminal` | off | Print violations and AI prompts to terminal |
115
+ | `--fast-mode` | off | Output only the raw prompts no summaries or decoration |
116
+ | `--group <strategy>` | `rule` | `rule`: one prompt per rule type. `none`: one prompt per element |
117
+ | `--ci` | off | Exit with code `1` if any violations are found |
118
+ | `--provider <name>` | from config | Override AI provider for this run |
142
119
 
143
120
  ---
144
121
 
@@ -147,34 +124,13 @@ wcag-a11y scan -u http://localhost:3000 --no-ai --ci
147
124
  Scan for violations and apply AI-generated patches directly to your source files. Works with any framework — React, Vue, Angular, Svelte, or plain HTML.
148
125
 
149
126
  ```bash
150
- # Dry-run: scan and show what would change (safe, no files written)
151
- wcag-a11y fix -u http://localhost:3000
152
-
153
- # Preview specific pages
154
- wcag-a11y fix -u http://localhost:3000 --pages / /about /contact
155
-
156
- # Write fixes to disk
157
- wcag-a11y fix -u http://localhost:3000 --apply
158
-
159
- # Auto-discover pages + write fixes
160
- wcag-a11y fix -u http://localhost:3000 --crawl --apply
161
-
162
- # Skip rescanning — load violations from an existing report
163
- wcag-a11y fix --from-report
164
- wcag-a11y fix --from-report ./reports/a11y-report.md --apply
127
+ wcag-a11y fix -u http://localhost:3000 # dry-run: show diff, nothing written
128
+ wcag-a11y fix -u http://localhost:3000 --apply # write fixes to disk
129
+ wcag-a11y fix --from-report --apply # patch from an existing report
165
130
  ```
166
131
 
167
- **How it works:**
168
-
169
- 1. Runs the same scan as `wcag-a11y scan` (or loads an existing report with `--from-report`)
170
- 2. For each violation, locates the source file — checks `violation.source` (React dev mode) first, then falls back to grepping `./src` for unique identifiers in the HTML snippet (`id=`, `name=`, `for=`, local `src=`, text content)
171
- 3. Groups violations by file (multiple violations in the same file → one AI call)
172
- 4. Sends the full file content + violation list to your configured AI provider and asks for the corrected file
173
- 5. Shows a colored diff before writing anything
174
- 6. With `--apply`, overwrites the file; without it, only prints the diff
175
-
176
132
  ```
177
- src/components/Navbar.jsx — 2 violation(s)
133
+ src/components/Navbar.jsx — 2 violations
178
134
  · [button-name] Buttons must have an accessible name
179
135
  · [aria-valid-role] Elements must use valid ARIA roles
180
136
 
@@ -188,39 +144,85 @@ src/components/Navbar.jsx — 2 violation(s)
188
144
  + <li>Home</li>
189
145
  ```
190
146
 
147
+ **Common workflow:** scan first to review, then patch:
148
+
149
+ ```bash
150
+ wcag-a11y scan -u http://localhost:3000 # generates a11y-report.md
151
+ wcag-a11y fix --from-report --apply # patches files from that report, no second crawl
152
+ ```
153
+
191
154
  | Flag | Default | Description |
192
155
  |---|---|---|
193
- | `-u, --url <url>` | — | Base URL of your running dev server. Required unless `--from-report` is used |
194
- | `-p, --pages <pages...>` | `/` | Specific pages to scan |
156
+ | `-u, --url <url>` | — | Base URL. Required unless `--from-report` is used |
157
+ | `-p, --pages <paths...>` | `/` | Paths to scan |
195
158
  | `-c, --crawl` | off | Auto-discover pages by following same-origin links |
196
- | `--from-report [path]` | `a11y-report.md` | Load violations from an existing report instead of scanning. Useful when you already ran `scan --report` and just want to apply fixes |
197
- | `--apply` | off | Write patched files to disk (dry-run without this flag) |
198
- | `--provider <name>` | from config | Override AI provider for this run: `gemini`, `openai`, or `ollama` |
159
+ | `--from-report [path]` | `a11y-report.md` | Load violations from an existing report instead of rescanning |
160
+ | `--apply` | off | Write fixes to disk (dry-run without this flag) |
161
+ | `--provider <name>` | from config | Override AI provider for this run |
162
+
163
+ ---
199
164
 
200
- > **Tip:** Always run without `--apply` first to review the diff. The dry-run is safe — nothing is written to disk.
165
+ ### `wcag-a11y init`
201
166
 
202
- **Common workflow:** run `scan --report` to generate a report for review, then run `fix --from-report --apply` to patch the files — no second browser crawl needed.
167
+ Create `a11y.config.json` pre-configured for your chosen provider.
203
168
 
204
169
  ```bash
205
- wcag-a11y scan -u http://localhost:3000 --report # review a11y-report.md
206
- wcag-a11y fix --from-report --apply # patch files from that report
170
+ wcag-a11y init # Gemini (free, default)
171
+ wcag-a11y init --provider openai
172
+ wcag-a11y init --provider anthropic
173
+ wcag-a11y init --provider ollama # local — no API key needed
174
+ # … and 8 more providers
175
+ ```
176
+
177
+ ---
178
+
179
+ ### `wcag-a11y demo`
180
+
181
+ Scan a built-in page with 10 intentional violations. No dev server or config required — useful for trying the tool before pointing it at your own project.
182
+
183
+ ```bash
184
+ wcag-a11y demo # violations + AI fix prompts (requires config)
185
+ wcag-a11y demo --no-ai # violations only, no AI
207
186
  ```
208
187
 
209
188
  ---
210
189
 
211
190
  ## AI Providers
212
191
 
213
- | Provider | Model | Cost | API Key |
214
- |---|---|---|---|
215
- | `gemini` (default) | `gemini-2.5-flash` | Free tier | [aistudio.google.com](https://aistudio.google.com) |
216
- | `openai` | `gpt-4o-mini` | Pay-per-use | [platform.openai.com](https://platform.openai.com/api-keys) |
217
- | `ollama` | `llama3` | Free (local) | None — run `ollama serve` |
192
+ 12 providers supported. Configure once in `a11y.config.json`, or override per-run with `--provider`.
218
193
 
219
- Set your provider in `a11y.config.json` or override it per-run with `--provider`. If the AI response is unparseable, the tool generates a fix prompt directly from the violation data so you always get something actionable.
194
+ | Provider | `--provider` | Default model | Notes |
195
+ |---|---|---|---|
196
+ | Google Gemini | `gemini` *(default)* | `gemini-2.5-flash` | Free tier available |
197
+ | OpenAI | `openai` | `gpt-4o-mini` | |
198
+ | Anthropic | `anthropic` | `claude-sonnet-4-6` | |
199
+ | Mistral | `mistral` | `mistral-large-latest` | |
200
+ | Groq | `groq` | `llama-3.3-70b-versatile` | Fast inference |
201
+ | Cohere | `cohere` | `command-r-plus` | |
202
+ | xAI | `xai` | `grok-2` | |
203
+ | DeepSeek | `deepseek` | `deepseek-chat` | |
204
+ | Together AI | `together` | `meta-llama/Llama-3-70b-chat-hf` | Open-source models |
205
+ | Perplexity | `perplexity` | `llama-3.1-sonar-large-128k-online` | |
206
+ | Azure OpenAI | `azure-openai` | *(your deployment)* | |
207
+ | Ollama | `ollama` | `llama3` | Local — no API key |
208
+
209
+ All models are configurable. If the AI response is unparseable, the tool generates a fix prompt directly from the violation data — you always get something actionable.
220
210
 
221
211
  ---
222
212
 
223
- ## Config (`a11y.config.json`)
213
+ ## Config
214
+
215
+ Run `wcag-a11y init` to generate `a11y.config.json`. Only fill in the fields for your chosen provider. This file is gitignored by default.
216
+
217
+ ```json
218
+ {
219
+ "provider": "gemini",
220
+ "apiKey": "YOUR_GEMINI_API_KEY"
221
+ }
222
+ ```
223
+
224
+ <details>
225
+ <summary>Full config reference (all 12 providers)</summary>
224
226
 
225
227
  ```json
226
228
  {
@@ -232,18 +234,50 @@ Set your provider in `a11y.config.json` or override it per-run with `--provider`
232
234
  "openaiApiKey": "YOUR_OPENAI_API_KEY",
233
235
  "openaiModel": "gpt-4o-mini",
234
236
 
237
+ "anthropicApiKey": "YOUR_ANTHROPIC_API_KEY",
238
+ "anthropicModel": "claude-sonnet-4-6",
239
+
240
+ "mistralApiKey": "YOUR_MISTRAL_API_KEY",
241
+ "mistralModel": "mistral-large-latest",
242
+
243
+ "groqApiKey": "YOUR_GROQ_API_KEY",
244
+ "groqModel": "llama-3.3-70b-versatile",
245
+
246
+ "cohereApiKey": "YOUR_COHERE_API_KEY",
247
+ "cohereModel": "command-r-plus",
248
+
249
+ "xaiApiKey": "YOUR_XAI_API_KEY",
250
+ "xaiModel": "grok-2",
251
+
252
+ "deepseekApiKey": "YOUR_DEEPSEEK_API_KEY",
253
+ "deepseekModel": "deepseek-chat",
254
+
255
+ "togetherApiKey": "YOUR_TOGETHER_API_KEY",
256
+ "togetherModel": "meta-llama/Llama-3-70b-chat-hf",
257
+
258
+ "perplexityApiKey": "YOUR_PERPLEXITY_API_KEY",
259
+ "perplexityModel": "llama-3.1-sonar-large-128k-online",
260
+
261
+ "azureOpenaiApiKey": "YOUR_AZURE_KEY",
262
+ "azureOpenaiEndpoint": "https://YOUR_RESOURCE.openai.azure.com",
263
+ "azureOpenaiDeployment": "YOUR_DEPLOYMENT_NAME",
264
+ "azureOpenaiApiVersion": "2024-10-01-preview",
265
+
235
266
  "ollamaBaseUrl": "http://localhost:11434",
236
267
  "ollamaModel": "llama3"
237
268
  }
238
269
  ```
239
270
 
240
- Only the fields for your active provider are required. This file is gitignored by default.
271
+ </details>
241
272
 
242
273
  ---
243
274
 
244
275
  ## What it checks
245
276
 
246
- 40+ rules across 10 categories, mapped to WCAG 2.1/2.2 success criteria.
277
+ 40+ rules across 10 WCAG 2.1/2.2 categories: Text Alternatives, Color Contrast, Forms, Keyboard, ARIA, Structure, Links, Media, Tables, and Language.
278
+
279
+ <details>
280
+ <summary>Full rule list</summary>
247
281
 
248
282
  ### Text Alternatives — WCAG 1.1.1
249
283
 
@@ -346,6 +380,8 @@ Only the fields for your active provider are required. This file is gitignored b
346
380
  | `html-lang` | serious | `<html>` must have a `lang` attribute |
347
381
  | `html-lang-valid` | serious | `lang` attribute must be a valid BCP 47 language tag |
348
382
 
383
+ </details>
384
+
349
385
  ---
350
386
 
351
387
  ## Use in CI/CD
@@ -0,0 +1,59 @@
1
+ import { buildPrompt } from './prompt.js';
2
+ import { buildPatchPrompt } from './patch-prompt.js';
3
+ import { groupViolations } from './group.js';
4
+ import { BaseAIProvider } from './base.js';
5
+ export class AnthropicProvider extends BaseAIProvider {
6
+ apiKey;
7
+ model;
8
+ constructor(apiKey, model = 'claude-sonnet-4-6') {
9
+ super();
10
+ this.apiKey = apiKey;
11
+ this.model = model;
12
+ }
13
+ get headers() {
14
+ return {
15
+ 'Content-Type': 'application/json',
16
+ 'x-api-key': this.apiKey,
17
+ 'anthropic-version': '2023-06-01',
18
+ };
19
+ }
20
+ async generateFixes(violations, strategy = 'rule', framework) {
21
+ if (violations.length === 0)
22
+ return [];
23
+ const groups = groupViolations(violations, strategy);
24
+ const prompt = buildPrompt(groups, framework);
25
+ const response = await fetch('https://api.anthropic.com/v1/messages', {
26
+ method: 'POST',
27
+ headers: this.headers,
28
+ body: JSON.stringify({
29
+ model: this.model,
30
+ max_tokens: 8192,
31
+ temperature: 0.2,
32
+ messages: [{ role: 'user', content: prompt }],
33
+ }),
34
+ });
35
+ if (!response.ok) {
36
+ throw new Error(`Anthropic API error: ${response.status} ${await response.text()}`);
37
+ }
38
+ const data = await response.json();
39
+ const text = data.content?.[0]?.text ?? '[]';
40
+ return this.parse(text, groups, framework);
41
+ }
42
+ async generateFilePatch(fileContent, violations, filePath, framework) {
43
+ const prompt = buildPatchPrompt(fileContent, violations, filePath, framework);
44
+ const response = await fetch('https://api.anthropic.com/v1/messages', {
45
+ method: 'POST',
46
+ headers: this.headers,
47
+ body: JSON.stringify({
48
+ model: this.model,
49
+ max_tokens: 16384,
50
+ temperature: 0.1,
51
+ messages: [{ role: 'user', content: prompt }],
52
+ }),
53
+ });
54
+ if (!response.ok)
55
+ throw new Error(`Anthropic API error: ${response.status} ${await response.text()}`);
56
+ const data = await response.json();
57
+ return data.content?.[0]?.text ?? '';
58
+ }
59
+ }
@@ -0,0 +1,17 @@
1
+ import { OpenAICompatProvider } from './openai-compat.js';
2
+ export class AzureOpenAIProvider extends OpenAICompatProvider {
3
+ apiVersion;
4
+ constructor(endpoint, apiKey, deployment, apiVersion = '2024-10-01-preview') {
5
+ super(`${endpoint}/openai/deployments/${deployment}`, apiKey, deployment, 'Azure OpenAI');
6
+ this.apiVersion = apiVersion;
7
+ }
8
+ buildUrl(path) {
9
+ return `${this.baseUrl}${path}?api-version=${this.apiVersion}`;
10
+ }
11
+ buildHeaders() {
12
+ return {
13
+ 'Content-Type': 'application/json',
14
+ 'api-key': this.apiKey,
15
+ };
16
+ }
17
+ }
@@ -0,0 +1,45 @@
1
+ import { fallbackExplanation } from './fallback-explanation.js';
2
+ import { getFix, getFixCategory } from './fallback-fix.js';
3
+ export class BaseAIProvider {
4
+ parse(text, groups, framework) {
5
+ try {
6
+ const jsonMatch = text.match(/\[[\s\S]*\]/);
7
+ const fixes = jsonMatch ? JSON.parse(jsonMatch[0]) : [];
8
+ return groups.map((g) => {
9
+ const found = fixes.find((f) => f.ruleId === g.ruleId);
10
+ return found
11
+ ? { ...found, selectors: g.selectors, instanceCount: g.count, fixCategory: getFixCategory(g.ruleId) }
12
+ : this.fallbackFix(g, framework);
13
+ });
14
+ }
15
+ catch {
16
+ return groups.map((g) => this.fallbackFix(g, framework));
17
+ }
18
+ }
19
+ fallback(groups, framework) {
20
+ return groups.map((g) => this.fallbackFix(g, framework));
21
+ }
22
+ fallbackFix(g, framework) {
23
+ const v = g.representative;
24
+ const selectorList = g.selectors.map((s) => `- \`${s}\``).join('\n');
25
+ const explanation = fallbackExplanation(g.ruleId, g.description, g.wcag, g.level);
26
+ const fwNote = framework ? `This project uses ${framework}.\n\n` : '';
27
+ const fixInstructions = getFix(g.ruleId);
28
+ const fixSection = fixInstructions ? `\n\nHow to fix:\n${fixInstructions}` : '';
29
+ const prompt = g.count > 1
30
+ ? `${fwNote}Fix WCAG 2.1 SC ${g.wcag} (Level ${g.level}) — ${g.description}\n\nAffected elements (${g.count} instances):\n${selectorList}\n\nRepresentative HTML:\n\`\`\`html\n${v.html.slice(0, 400)}\n\`\`\`${fixSection}\n\nApply this fix to all ${g.count} instances across the codebase.`
31
+ : `${fwNote}Fix WCAG 2.1 SC ${g.wcag} (Level ${g.level}) — ${g.description}\n\nAffected element:\n- Selector: \`${g.selectors[0]}\`\n\nCurrent HTML:\n\`\`\`html\n${v.html.slice(0, 400)}\n\`\`\`${fixSection}`;
32
+ const category = getFixCategory(g.ruleId);
33
+ const fixedCode = category === 'edit-element' || !category ? v.html : undefined;
34
+ return {
35
+ ruleId: g.ruleId,
36
+ selectors: g.selectors,
37
+ instanceCount: g.count,
38
+ explanation,
39
+ fixedCode,
40
+ fixCategory: getFixCategory(g.ruleId),
41
+ wcagReference: `WCAG 2.1 SC ${g.wcag}`,
42
+ optimalPrompt: prompt,
43
+ };
44
+ }
45
+ }
@@ -0,0 +1,58 @@
1
+ import { buildPrompt } from './prompt.js';
2
+ import { buildPatchPrompt } from './patch-prompt.js';
3
+ import { groupViolations } from './group.js';
4
+ import { BaseAIProvider } from './base.js';
5
+ export class CohereProvider extends BaseAIProvider {
6
+ apiKey;
7
+ model;
8
+ constructor(apiKey, model = 'command-r-plus') {
9
+ super();
10
+ this.apiKey = apiKey;
11
+ this.model = model;
12
+ }
13
+ get headers() {
14
+ return {
15
+ 'Content-Type': 'application/json',
16
+ 'Authorization': `Bearer ${this.apiKey}`,
17
+ };
18
+ }
19
+ async generateFixes(violations, strategy = 'rule', framework) {
20
+ if (violations.length === 0)
21
+ return [];
22
+ const groups = groupViolations(violations, strategy);
23
+ const prompt = buildPrompt(groups, framework);
24
+ const response = await fetch('https://api.cohere.com/v2/chat', {
25
+ method: 'POST',
26
+ headers: this.headers,
27
+ body: JSON.stringify({
28
+ model: this.model,
29
+ messages: [{ role: 'user', content: prompt }],
30
+ max_tokens: 8192,
31
+ temperature: 0.2,
32
+ }),
33
+ });
34
+ if (!response.ok) {
35
+ throw new Error(`Cohere API error: ${response.status} ${await response.text()}`);
36
+ }
37
+ const data = await response.json();
38
+ const text = data.message?.content?.[0]?.text ?? '[]';
39
+ return this.parse(text, groups, framework);
40
+ }
41
+ async generateFilePatch(fileContent, violations, filePath, framework) {
42
+ const prompt = buildPatchPrompt(fileContent, violations, filePath, framework);
43
+ const response = await fetch('https://api.cohere.com/v2/chat', {
44
+ method: 'POST',
45
+ headers: this.headers,
46
+ body: JSON.stringify({
47
+ model: this.model,
48
+ messages: [{ role: 'user', content: prompt }],
49
+ max_tokens: 16384,
50
+ temperature: 0.1,
51
+ }),
52
+ });
53
+ if (!response.ok)
54
+ throw new Error(`Cohere API error: ${response.status} ${await response.text()}`);
55
+ const data = await response.json();
56
+ return data.message?.content?.[0]?.text ?? '';
57
+ }
58
+ }
@@ -0,0 +1,6 @@
1
+ import { OpenAICompatProvider } from './openai-compat.js';
2
+ export class DeepSeekProvider extends OpenAICompatProvider {
3
+ constructor(apiKey, model = 'deepseek-chat') {
4
+ super('https://api.deepseek.com/v1', apiKey, model, 'DeepSeek');
5
+ }
6
+ }