@diegovelasquezweb/a11y-engine 0.8.5 → 0.10.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/CHANGELOG.md CHANGED
@@ -5,6 +5,47 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.9.0] — 2026-03-16
9
+
10
+ ### Changed
11
+
12
+ - **Knowledge API consolidated** — `getScannerHelp`, `getPersonaReference`, `getUiHelp`, `getConformanceLevels`, `getWcagPrinciples`, and `getSeverityLevels` are no longer part of the public API. They remain as internal helpers consumed by `getKnowledge`. `getUiHelp` renamed to `getConceptsAndGlossary` internally. `getKnowledge` is the single exported entry point for all knowledge data.
13
+ - TypeScript declarations (`src/index.d.mts`) updated to remove the six individual knowledge functions.
14
+ - `tests/knowledge-api.test.mjs` updated to reflect the consolidated API shape.
15
+
16
+ ---
17
+
18
+ ## [0.8.5] — 2026-03-16
19
+
20
+ ### Fixed
21
+
22
+ - **pa11y merge no longer drops findings with shared selectors** — the merge step was discarding pa11y findings whenever any prior finding (from axe or CDP) targeted the same selector, regardless of rule. Now pa11y findings are only de-duplicated when the exact same `rule_id + selector` combination already exists.
23
+
24
+ ---
25
+
26
+ ## [0.8.4] — 2026-03-15
27
+
28
+ ### Added
29
+
30
+ - **`DEFAULT_AI_SYSTEM_PROMPT` exported** — the default Claude system prompt is now part of the public API, allowing consumers to read, log, or extend it.
31
+ - **`VIEWPORT_PRESETS` exported** — four ready-made viewport presets (`Desktop`, `Laptop`, `Tablet`, `Mobile`) exported from the package root for use in scanner UI option pickers.
32
+ - **`dependabot.yml`** — automated dependency update configuration added.
33
+ - **Effort fallback** — `getFindings` now infers `effort` after intelligence enrichment so findings that gain a `fixCode` from the intelligence database are correctly rated `"low"`.
34
+
35
+ ---
36
+
37
+ ## [0.8.3] — 2026-03-15
38
+
39
+ ### Fixed
40
+
41
+ - **`actual` field no longer contains axe preamble** — the `"Fix any of the following:"` prefix from axe `failureSummary` strings is now stripped in `analyzer.mjs`, producing a cleaner violation description.
42
+
43
+ ### Added
44
+
45
+ - `SECURITY.md` — security policy and vulnerability reporting process.
46
+
47
+ ---
48
+
8
49
  ## [0.8.2] — 2026-03-16
9
50
 
10
51
  ### Changed
package/README.md CHANGED
@@ -15,7 +15,7 @@ Accessibility automation engine for web applications. It orchestrates multi engi
15
15
  | **AI enrichment** | Optional Claude-powered analysis that adds contextual fix suggestions based on detected stack, repo structure, and finding patterns |
16
16
  | **Report generation** | Produces HTML dashboard, PDF compliance report, manual testing checklist, and Markdown remediation guide |
17
17
  | **Source code scanning** | Static regex analysis of project source for accessibility patterns that runtime engines cannot detect. Works with local paths or remote GitHub repos |
18
- | **Knowledge API** | Exposes WCAG conformance levels, severity definitions, persona profiles, glossary, scanner help, and documentation so frontends and agents can render guidance from engine-owned data |
18
+ | **Knowledge API** | Exposes WCAG conformance levels, severity definitions, persona profiles, glossary, scanner help, and documentation |
19
19
 
20
20
  ## Installation
21
21
 
@@ -41,12 +41,6 @@ import {
41
41
  getChecklist,
42
42
  getRemediationGuide,
43
43
  getSourcePatterns,
44
- getScannerHelp,
45
- getPersonaReference,
46
- getUiHelp,
47
- getConformanceLevels,
48
- getWcagPrinciples,
49
- getSeverityLevels,
50
44
  getKnowledge,
51
45
  } from "@diegovelasquezweb/a11y-engine";
52
46
  ```
@@ -100,12 +94,18 @@ Computes severity totals, compliance score, WCAG pass/fail status, persona impac
100
94
  ```ts
101
95
  const summary = getOverview(findings, payload);
102
96
  // summary.score -> 72
103
- // summary.label -> "Good"
104
- // summary.wcagStatus -> "Fail"
97
+ // summary.label -> "Good" // "Excellent" | "Good" | "Fair" | "Poor" | "Critical"
98
+ // summary.wcagStatus -> "Fail" // "Pass" | "Conditional Pass" | "Fail"
105
99
  // summary.totals -> { Critical: 1, Serious: 3, Moderate: 5, Minor: 2 }
106
- // summary.personaGroups -> { screenReader: {...}, keyboard: {...}, ... }
107
- // summary.quickWins -> [top 3 fixable Critical/Serious findings]
100
+ // summary.personaGroups -> {
101
+ // screenReader: { label: "Screen Readers", count: 4, icon: "screenReader" },
102
+ // keyboard: { label: "Keyboard Only", count: 2, icon: "keyboard" },
103
+ // ...
104
+ // }
105
+ // summary.quickWins -> [top 3 Critical/Serious findings with fixCode]
106
+ // summary.targetUrl -> "https://example.com"
108
107
  // summary.detectedStack -> { framework: "nextjs", cms: null, uiLibraries: [] }
108
+ // summary.totalFindings -> 11
109
109
  ```
110
110
 
111
111
  ### Output API
@@ -122,42 +122,32 @@ These functions render final artifacts from scan payload data.
122
122
 
123
123
  ### Knowledge API
124
124
 
125
- These functions expose scanner help content, persona explanations, conformance levels, and UI copy so frontends or agents can render guidance from engine-owned data.
125
+ Returns all accessibility knowledge in a single call: scanner help, persona profiles, concepts, glossary, documentation, conformance levels, WCAG principles, and severity definitions.
126
126
 
127
- | Function | Returns | Description |
128
- | :--- | :--- | :--- |
129
- | `getScannerHelp(options?)` | `{ locale, version, title, engines, options }` | Scanner option and engine help metadata |
130
- | `getPersonaReference(options?)` | `{ locale, version, personas }` | Persona labels, descriptions, and mapping hints |
131
- | `getUiHelp(options?)` | `{ locale, version, concepts, glossary }` | Shared concept definitions and glossary entries |
132
- | `getConformanceLevels(options?)` | `{ locale, version, conformanceLevels }` | WCAG conformance level definitions with axe tag mappings |
133
- | `getWcagPrinciples(options?)` | `{ locale, version, wcagPrinciples }` | The four WCAG principles with criterion prefix patterns |
134
- | `getSeverityLevels(options?)` | `{ locale, version, severityLevels }` | Severity level definitions with labels and ordering |
135
- | `getKnowledge(options?)` | Full knowledge pack | Combines all knowledge APIs into a single response for UI or agent flows |
127
+ ```ts
128
+ const knowledge = getKnowledge({ locale: "en" });
129
+ // knowledge.scanner → engine and option descriptions
130
+ // knowledge.personas persona labels and descriptions
131
+ // knowledge.concepts concept definitions
132
+ // knowledge.glossary → accessibility glossary
133
+ // knowledge.docs → documentation articles
134
+ // knowledge.conformanceLevels A/AA/AAA with axe tag mappings
135
+ // knowledge.wcagPrinciples → the four WCAG principles
136
+ // knowledge.severityLevels → Critical/Serious/Moderate/Minor definitions
137
+ ```
136
138
 
137
- See [API Reference](docs/api-reference.md) for exact options and return types.
139
+ See [API Reference](docs/api-reference.md) for the full `EngineKnowledge` shape.
138
140
 
139
141
  ## CLI
140
142
 
141
143
  The package exposes an `a11y-audit` binary for terminal execution. See the [CLI Handbook](docs/cli-handbook.md) for all flags, env vars, and examples.
142
144
 
143
- ## AI enrichment
144
-
145
- When `ANTHROPIC_API_KEY` is set, the engine runs a post-scan enrichment step that sends Critical and Serious findings to Claude. Claude generates:
146
-
147
- - A specific fix description referencing the actual selector, colors, and violation data
148
- - A production-quality code snippet in the correct framework syntax
149
- - Context-aware suggestions when repo source files are available
150
-
151
- AI output is stored in separate fields (`ai_fix_description`, `ai_fix_code`). The original engine fixes are always preserved. Findings improved by AI are flagged with `aiEnhanced: true`.
152
-
153
- The system prompt is fully customizable via `options.ai.systemPrompt` (programmatic API) or the `AI_SYSTEM_PROMPT` env var (CLI).
154
-
155
145
  ## Documentation
156
146
 
157
147
  | Resource | Description |
158
148
  | :--- | :--- |
159
149
  | [Architecture](docs/architecture.md) | Multi-engine pipeline, merge logic, and execution model |
160
- | [API Reference](docs/api-reference.md) | Function signatures, options, and return contracts |
150
+ | [API Reference](docs/api-reference.md) | Function signatures, options, return contracts, and exported constants |
161
151
  | [CLI Handbook](docs/cli-handbook.md) | Full flag reference and usage examples |
162
152
  | [Output Artifacts](docs/outputs.md) | Schema and structure of every generated file |
163
153
  | [Engine Manifest](docs/engine-manifest.md) | Current inventory of source modules, assets, and tests |
@@ -21,12 +21,9 @@
21
21
  - [getSourcePatterns](#getsourcepatternsprojdir-options)
22
22
  - [Knowledge API](#knowledge-api)
23
23
  - [getKnowledge](#getknowledgeoptions)
24
- - [getScannerHelp](#getscannerhelpoptions)
25
- - [getPersonaReference](#getpersonareferenceoptions)
26
- - [getUiHelp](#getuihelpoptions)
27
- - [getConformanceLevels](#getconformancelevelsoptions)
28
- - [getWcagPrinciples](#getwcagprinciplesoptions)
29
- - [getSeverityLevels](#getseveritylevelsoptions)
24
+ - [Constants](#constants)
25
+ - [VIEWPORT_PRESETS](#viewport_presets)
26
+ - [DEFAULT_AI_SYSTEM_PROMPT](#default_ai_system_prompt)
30
27
 
31
28
  ---
32
29
 
@@ -53,12 +50,8 @@ import {
53
50
  getRemediationGuide,
54
51
  getSourcePatterns,
55
52
  getKnowledge,
56
- getScannerHelp,
57
- getPersonaReference,
58
- getUiHelp,
59
- getConformanceLevels,
60
- getWcagPrinciples,
61
- getSeverityLevels,
53
+ VIEWPORT_PRESETS,
54
+ DEFAULT_AI_SYSTEM_PROMPT,
62
55
  } from "@diegovelasquezweb/a11y-engine";
63
56
  ```
64
57
 
@@ -81,9 +74,9 @@ const payload = await runAudit({
81
74
  const findings = getFindings(payload);
82
75
 
83
76
  // 3. Get compliance summary
84
- const { score, scoreLabel, wcagStatus, totals, quickWins } = getOverview(findings, payload);
77
+ const { score, label, wcagStatus, totals, quickWins } = getOverview(findings, payload);
85
78
 
86
- console.log(`Score: ${score}/100 (${scoreLabel})`);
79
+ console.log(`Score: ${score}/100 (${label})`);
87
80
  console.log(`WCAG Status: ${wcagStatus}`);
88
81
  console.log(`Critical: ${totals.Critical}, Serious: ${totals.Serious}`);
89
82
  ```
@@ -169,6 +162,42 @@ const payload = await runAudit({
169
162
 
170
163
  Returns: `Promise<ScanPayload>`
171
164
 
165
+ **`ScanPayload` shape:**
166
+
167
+ ```ts
168
+ {
169
+ findings: RawFinding[], // Raw findings from axe/CDP/pa11y merge
170
+ metadata: {
171
+ target_url: string, // The baseUrl that was scanned
172
+ scanned_at: string, // ISO 8601 timestamp
173
+ engines: { // Which engines actually ran
174
+ axe: boolean,
175
+ cdp: boolean,
176
+ pa11y: boolean,
177
+ },
178
+ projectContext: { // Auto-detected or overridden stack
179
+ framework: string | null, // "nextjs" | "react" | "vue" | etc.
180
+ cms: string | null, // "wordpress" | "shopify" | etc.
181
+ uiLibraries: string[], // ["radix-ui", "tailwindcss", ...]
182
+ },
183
+ routes_scanned: number, // How many pages were actually scanned
184
+ discovery_method: string, // "crawl" | "explicit"
185
+ },
186
+ incomplete_findings?: RawFinding[], // axe "incomplete" results (needs-review)
187
+ patternFindings?: { // Only present if projectDir/repoUrl + !skipPatterns
188
+ generated_at: string,
189
+ project_dir: string, // Local path or repo URL
190
+ findings: SourcePatternFinding[],
191
+ summary: {
192
+ total: number,
193
+ confirmed: number,
194
+ potential: number,
195
+ },
196
+ },
197
+ ai_enriched_findings?: EnrichedFinding[], // Only present if ai.enabled + ai.apiKey
198
+ }
199
+ ```
200
+
172
201
  > **`ai_enriched_findings` fast path**: When AI enrichment runs, `getFindings()` uses `payload.ai_enriched_findings` directly instead of re-normalizing the raw findings array.
173
202
 
174
203
  ---
@@ -207,6 +236,85 @@ const findings = getFindings(payload, {
207
236
 
208
237
  Returns: `EnrichedFinding[]`
209
238
 
239
+ **`EnrichedFinding` shape:**
240
+
241
+ ```ts
242
+ {
243
+ // Identity
244
+ id: string, // "A11Y-001", "A11Y-002", ...
245
+ ruleId: string, // Canonical rule ID (e.g. "color-contrast")
246
+ source: string, // "axe" | "cdp" | "pa11y"
247
+ sourceRuleId: string | null, // Original engine rule ID before canonicalization
248
+
249
+ // Classification
250
+ title: string, // Human-readable issue title
251
+ severity: string, // "Critical" | "Serious" | "Moderate" | "Minor"
252
+ category: string | null, // "color", "forms", "structure", "aria", ...
253
+ wcag: string, // WCAG criterion (e.g. "1.4.3")
254
+ wcagCriterionId: string | null, // Full criterion ID (e.g. "1.4.3")
255
+ wcagClassification: string | null, // "A" | "AA" | "AAA" | "Best Practice"
256
+
257
+ // Location
258
+ area: string, // Page path (e.g. "/about")
259
+ url: string, // Full page URL
260
+ selector: string, // CSS selector of the violating element
261
+ primarySelector: string, // Preferred selector for targeting
262
+
263
+ // Problem description
264
+ actual: string, // What the engine found
265
+ expected: string, // What WCAG requires
266
+ impactedUsers: string, // "Screen reader users", "Keyboard users", etc.
267
+ primaryFailureMode: string | null, // "missing-label" | "low-contrast" | ...
268
+ relationshipHint: string | null, // How this relates to other findings
269
+
270
+ // Evidence
271
+ evidence: object[], // Raw evidence from the engine
272
+ failureChecks: object[], // axe check details
273
+ relatedContext: object[], // Related DOM elements
274
+ totalInstances: number | null, // How many elements are affected
275
+ pagesAffected: number | null, // How many pages have this issue
276
+ affectedUrls: string[] | null, // Specific URLs affected
277
+
278
+ // Fix guidance
279
+ fixDescription: string | null, // Human-readable fix explanation
280
+ fixCode: string | null, // Code snippet to fix the issue
281
+ fixCodeLang: string, // "html" | "css" | "jsx" | ...
282
+ recommendedFix: string, // Short fix summary
283
+ mdn: string | null, // MDN reference URL
284
+ effort: string, // "low" (has fixCode) | "high" (no fixCode)
285
+ fixDifficultyNotes: object | null, // Detailed difficulty breakdown
286
+
287
+ // Framework / CMS context
288
+ frameworkNotes: string | null, // Framework-specific fix guidance
289
+ cmsNotes: string | null, // CMS-specific fix guidance
290
+ managedByLibrary: string | null, // If the element is from a 3rd-party lib
291
+ componentHint: string | null, // Likely component name
292
+ fileSearchPattern: string | null, // Glob pattern to find source file
293
+
294
+ // Ownership & search
295
+ ownershipStatus: string, // "own" | "third-party" | "unknown"
296
+ ownershipReason: string | null, // Why it was classified that way
297
+ primarySourceScope: string[], // Directories to search for source
298
+ searchStrategy: string, // "verify_ownership_before_search" | ...
299
+
300
+ // Verification
301
+ verificationCommand: string | null, // CLI command to verify the fix
302
+ verificationCommandFallback: string | null,
303
+ screenshotPath: string | null, // Path or URL to element screenshot
304
+
305
+ // Metadata
306
+ relatedRules: string[], // Related axe rule IDs
307
+ falsePositiveRisk: string | null, // "low" | "medium" | "high"
308
+ guardrails: object | null, // Guardrail metadata from the engine
309
+ checkData: object | null, // Raw check data from the engine
310
+
311
+ // AI enrichment (only when ai.enabled ran)
312
+ aiEnhanced?: boolean, // true when AI enriched this finding
313
+ aiFixDescription?: string, // Claude-generated fix explanation
314
+ aiFixCode?: string, // Claude-generated code snippet
315
+ }
316
+ ```
317
+
210
318
  ---
211
319
 
212
320
  ### `getOverview(findings, payload?)`
@@ -218,27 +326,39 @@ import { getFindings, getOverview } from "@diegovelasquezweb/a11y-engine";
218
326
 
219
327
  const findings = getFindings(payload);
220
328
  const overview = getOverview(findings, payload);
221
-
222
- // overview example:
223
- // {
224
- // score: 72, // 0–100. Formula: 100 - (Critical×15) - (Serious×5) - (Moderate×2) - (Minor×0.5)
225
- // label: "Fair", // "Excellent" (90–100) | "Good" (75–89) | "Fair" (55–74) | "Poor" (35–54) | "Critical" (0–34)
226
- // wcagStatus: "Fail", // "Pass" | "Conditional Pass" | "Fail"
227
- // totals: { Critical: 1, Serious: 3, Moderate: 5, Minor: 2 },
228
- // personaGroups: {
229
- // screenReader: { count: 4, findings: [...] },
230
- // keyboard: { count: 2, findings: [...] },
231
- // vision: { count: 3, findings: [...] },
232
- // cognitive: { count: 1, findings: [...] },
233
- // },
234
- // quickWins: [...], // top Critical/Serious findings with fix code ready
235
- // targetUrl: "https://example.com",
236
- // detectedStack: { framework: "nextjs", cms: null, uiLibraries: ["radix-ui"] },
237
- // }
238
329
  ```
239
330
 
240
331
  Returns: `AuditSummary`
241
332
 
333
+ **`AuditSummary` shape:**
334
+
335
+ ```ts
336
+ {
337
+ score: number, // 0–100. Formula: 100 - (Critical×15) - (Serious×5) - (Moderate×2) - (Minor×0.5)
338
+ label: string, // "Excellent" (90–100) | "Good" (75–89) | "Fair" (55–74) | "Poor" (35–54) | "Critical" (0–34)
339
+ wcagStatus: string, // "Pass" | "Conditional Pass" | "Fail"
340
+ totals: {
341
+ Critical: number,
342
+ Serious: number,
343
+ Moderate: number,
344
+ Minor: number,
345
+ },
346
+ personaGroups: Record<string, { // Keyed by persona ID
347
+ label: string, // "Screen Readers", "Keyboard Only", ...
348
+ count: number, // Findings affecting this persona
349
+ icon: string, // Same as persona ID
350
+ }>,
351
+ quickWins: EnrichedFinding[], // Top 3 Critical/Serious findings with fixCode
352
+ targetUrl: string, // The scanned URL
353
+ detectedStack: {
354
+ framework: string | null, // "nextjs" | "react" | etc.
355
+ cms: string | null, // "wordpress" | "shopify" | etc.
356
+ uiLibraries: string[], // ["radix-ui", "tailwindcss", ...]
357
+ },
358
+ totalFindings: number, // Total enriched findings count
359
+ }
360
+ ```
361
+
242
362
  ---
243
363
 
244
364
  ## Output API
@@ -254,12 +374,18 @@ const { buffer, contentType } = await getPDFReport(payload, {
254
374
  baseUrl: "https://example.com",
255
375
  target: "WCAG 2.2 AA",
256
376
  });
257
-
258
- // In a Next.js API route:
259
- return new Response(buffer, { headers: { "Content-Type": contentType } });
260
377
  ```
261
378
 
262
- Returns: `Promise<{ buffer: Buffer, contentType: string }>`
379
+ Returns: `Promise<PDFReportResult>`
380
+
381
+ **`PDFReportResult` shape:**
382
+
383
+ ```ts
384
+ {
385
+ buffer: Buffer, // Raw PDF binary data
386
+ contentType: "application/pdf", // MIME type for response headers
387
+ }
388
+ ```
263
389
 
264
390
  ---
265
391
 
@@ -276,7 +402,16 @@ const { html, contentType } = await getHTMLReport(payload, {
276
402
  });
277
403
  ```
278
404
 
279
- Returns: `Promise<{ html: string, contentType: string }>`
405
+ Returns: `Promise<HTMLReportResult>`
406
+
407
+ **`HTMLReportResult` shape:**
408
+
409
+ ```ts
410
+ {
411
+ html: string, // Self-contained HTML document string
412
+ contentType: "text/html", // MIME type for response headers
413
+ }
414
+ ```
280
415
 
281
416
  ---
282
417
 
@@ -292,7 +427,16 @@ const { html, contentType } = await getChecklist({
292
427
  });
293
428
  ```
294
429
 
295
- Returns: `Promise<{ html: string, contentType: string }>`
430
+ Returns: `Promise<ChecklistResult>`
431
+
432
+ **`ChecklistResult` shape:**
433
+
434
+ ```ts
435
+ {
436
+ html: string, // Self-contained HTML with interactive checklist
437
+ contentType: "text/html", // MIME type for response headers
438
+ }
439
+ ```
296
440
 
297
441
  ---
298
442
 
@@ -307,11 +451,18 @@ const { markdown, contentType } = await getRemediationGuide(payload, {
307
451
  baseUrl: "https://example.com",
308
452
  patternFindings: payload.patternFindings ?? null,
309
453
  });
310
-
311
- // Write to disk or return as download
312
454
  ```
313
455
 
314
- Returns: `Promise<{ markdown: string, contentType: string }>`
456
+ Returns: `Promise<RemediationGuideResult>`
457
+
458
+ **`RemediationGuideResult` shape:**
459
+
460
+ ```ts
461
+ {
462
+ markdown: string, // Full Markdown document with remediation roadmap
463
+ contentType: "text/markdown", // MIME type for response headers
464
+ }
465
+ ```
315
466
 
316
467
  ---
317
468
 
@@ -326,122 +477,181 @@ const result = await getSourcePatterns("./", {
326
477
  framework: "nextjs", // optional — scopes scan to framework source dirs
327
478
  onlyPattern: "placeholder-only-label", // optional — run a single pattern
328
479
  });
329
-
330
- // result example:
331
- // {
332
- // findings: [
333
- // {
334
- // id: "PAT-a1b2c3",
335
- // pattern_id: "placeholder-only-label",
336
- // title: "Input uses placeholder as its only label",
337
- // severity: "Critical",
338
- // status: "confirmed",
339
- // file: "src/components/SearchBar.tsx",
340
- // line: 12,
341
- // match: ' <input placeholder="Search..." />',
342
- // context: "...",
343
- // fix_description: "Add an aria-label or visible <label> element",
344
- // }
345
- // ],
346
- // summary: { total: 3, confirmed: 2, potential: 1 }
347
- // }
348
480
  ```
349
481
 
350
482
  Returns: `Promise<SourcePatternResult>`
351
483
 
484
+ **`SourcePatternResult` shape:**
485
+
486
+ ```ts
487
+ {
488
+ findings: {
489
+ id: string, // "PAT-a1b2c3" — unique pattern finding ID
490
+ pattern_id: string, // Pattern definition ID (e.g. "placeholder-only-label")
491
+ title: string, // Human-readable issue title
492
+ severity: string, // "Critical" | "Serious" | "Moderate" | "Minor"
493
+ status: string, // "confirmed" | "potential"
494
+ file: string, // Relative file path (e.g. "src/components/SearchBar.tsx")
495
+ line: number, // Line number where the pattern was found
496
+ match: string, // The matching source code line
497
+ context: string, // Surrounding code for context
498
+ fix_description: string, // How to fix the pattern
499
+ }[],
500
+ summary: {
501
+ total: number, // Total findings found
502
+ confirmed: number, // Definite accessibility issues
503
+ potential: number, // Likely issues that need manual review
504
+ },
505
+ }
506
+ ```
507
+
352
508
  ---
353
509
 
354
510
  ## Knowledge API
355
511
 
356
- These functions expose engine-owned content for UIs and agents to render. All accept an optional `{ locale?: string }` option (default: `"en"`).
357
-
358
512
  ### `getKnowledge(options?)`
359
513
 
360
- Returns the full knowledge pack combines all knowledge functions into one call. Useful for pre-loading UI help content.
514
+ Returns all accessibility knowledge in a single call. Accepts an optional `{ locale?: string }` option (default: `"en"`).
515
+
516
+ This is the **only exported Knowledge API function**. The data it returns covers scanner help, persona profiles, concepts, glossary, docs, conformance levels, WCAG principles, and severity definitions — all in one call.
361
517
 
362
518
  ```ts
363
519
  import { getKnowledge } from "@diegovelasquezweb/a11y-engine";
364
520
 
365
521
  const knowledge = getKnowledge({ locale: "en" });
366
-
367
- // knowledge.scanner → scan options help and engine descriptions
368
- // knowledge.personas → persona labels, icons, descriptions
369
- // knowledge.concepts → UI concept definitions
370
- // knowledge.glossary → accessibility glossary
371
- // knowledge.conformanceLevels → WCAG A/AA/AAA definitions with axe tags
372
- // knowledge.wcagPrinciples → the 4 WCAG principles
373
- // knowledge.severityLevels → Critical/Serious/Moderate/Minor definitions
374
522
  ```
375
523
 
376
- Returns: `EngineKnowledge`
377
-
378
- ---
524
+ **Returns:** `EngineKnowledge`
379
525
 
380
- ### `getScannerHelp(options?)`
381
-
382
- Returns scan option descriptions, allowed values, and engine metadata — used to render Advanced Settings UI.
526
+ **`EngineKnowledge` shape:**
383
527
 
384
528
  ```ts
385
- const help = getScannerHelp();
386
- // help.engines → [{ id: "axe", label: "axe-core", description: "..." }, ...]
387
- // help.options → [{ id: "maxRoutes", label: "Max Routes", type: "number", ... }, ...]
529
+ {
530
+ locale: string, // "en"
531
+ version: string, // "1.0.0"
532
+
533
+ scanner: {
534
+ title: string, // "Scanner Help"
535
+ engines: { // Engine descriptions
536
+ id: string, // "axe" | "cdp" | "pa11y"
537
+ label: string,
538
+ description: string,
539
+ }[],
540
+ options: { // CLI/API option descriptions
541
+ name: string, // "maxRoutes"
542
+ type: string, // "number" | "string" | "boolean"
543
+ default: string | number | boolean,
544
+ description: string,
545
+ values?: string[], // Allowed values if enum-like
546
+ }[],
547
+ },
548
+
549
+ personas: { // Disability persona profiles
550
+ id: string, // "screenReader" | "keyboard" | "vision" | "cognitive"
551
+ icon: string, // Same as id, used for icon lookup
552
+ label: string, // "Screen Readers"
553
+ description: string, // Explanation of the persona
554
+ keywords: string[], // Keywords for matching findings
555
+ mappedRules: string[], // axe rule IDs mapped to this persona
556
+ }[],
557
+
558
+ concepts: Record<string, { // Concept definitions keyed by ID
559
+ title: string,
560
+ body: string,
561
+ context?: string, // When/where this concept applies
562
+ }>,
563
+
564
+ glossary: { // Accessibility term definitions
565
+ term: string,
566
+ definition: string,
567
+ }[],
568
+
569
+ docs: { // Documentation articles
570
+ sections: {
571
+ id: string,
572
+ title: string,
573
+ groups: {
574
+ id: string,
575
+ title: string,
576
+ articles: {
577
+ id: string,
578
+ title: string,
579
+ body: string,
580
+ }[],
581
+ }[],
582
+ }[],
583
+ },
584
+
585
+ conformanceLevels: { // WCAG A/AA/AAA definitions
586
+ level: string, // "A" | "AA" | "AAA"
587
+ label: string,
588
+ description: string,
589
+ axeTags: string[], // ["wcag2a", "wcag21a", "wcag22a"]
590
+ }[],
591
+
592
+ wcagPrinciples: { // The four WCAG principles
593
+ id: string, // "perceivable" | "operable" | "understandable" | "robust"
594
+ label: string,
595
+ description: string,
596
+ criterionPrefix: string, // "1." | "2." | "3." | "4."
597
+ }[],
598
+
599
+ severityLevels: { // Severity definitions
600
+ level: string, // "Critical" | "Serious" | "Moderate" | "Minor"
601
+ label: string,
602
+ description: string,
603
+ order: number, // 1 (Critical) – 4 (Minor)
604
+ }[],
605
+ }
388
606
  ```
389
607
 
390
608
  ---
391
609
 
392
- ### `getPersonaReference(options?)`
610
+ ## Constants
393
611
 
394
- Returns persona labels, descriptions, and disability group definitions.
612
+ ### `VIEWPORT_PRESETS`
395
613
 
396
- ```ts
397
- const ref = getPersonaReference();
398
- // ref.personas → [{ id: "screenReader", label: "Screen Readers", icon: "...", description: "..." }, ...]
399
- ```
400
-
401
- ---
402
-
403
- ### `getUiHelp(options?)`
404
-
405
- Returns shared concept definitions and a glossary of accessibility terms.
614
+ Ready-made viewport dimensions for common device classes. Useful when building scanner UI option pickers.
406
615
 
407
616
  ```ts
408
- const ui = getUiHelp();
409
- // ui.concepts → { wcag: "...", aria: "...", ... }
410
- // ui.glossary → [{ term: "ARIA", definition: "..." }, ...]
617
+ import { VIEWPORT_PRESETS } from "@diegovelasquezweb/a11y-engine";
618
+
619
+ // VIEWPORT_PRESETS:
620
+ // [
621
+ // { label: "Desktop", width: 1280, height: 800 },
622
+ // { label: "Laptop", width: 1440, height: 900 },
623
+ // { label: "Tablet", width: 768, height: 1024 },
624
+ // { label: "Mobile", width: 375, height: 812 },
625
+ // ]
411
626
  ```
412
627
 
413
- ---
414
-
415
- ### `getConformanceLevels(options?)`
416
-
417
- Returns WCAG conformance level definitions with their corresponding axe-core tag sets.
418
-
419
- ```ts
420
- const { conformanceLevels } = getConformanceLevels();
421
- // conformanceLevels[0] → { id: "AA", label: "WCAG 2.2 AA", axeTags: ["wcag2a", "wcag2aa", ...] }
422
- ```
628
+ Type: `ViewportPreset[]` — `{ label: string; width: number; height: number }[]`
423
629
 
424
630
  ---
425
631
 
426
- ### `getWcagPrinciples(options?)`
632
+ ### `DEFAULT_AI_SYSTEM_PROMPT`
427
633
 
428
- Returns the four WCAG principles (Perceivable, Operable, Understandable, Robust) with criterion prefix patterns.
634
+ The default system prompt passed to Claude for AI enrichment. Exported so consumers can read, log, or extend it when building custom AI workflows.
429
635
 
430
636
  ```ts
431
- const { wcagPrinciples } = getWcagPrinciples();
432
- // wcagPrinciples[0] → { id: "perceivable", label: "Perceivable", prefix: "1.", description: "..." }
433
- ```
637
+ import { DEFAULT_AI_SYSTEM_PROMPT } from "@diegovelasquezweb/a11y-engine";
434
638
 
435
- ---
639
+ // Override for a specific scan:
640
+ await runAudit({
641
+ baseUrl: "https://example.com",
642
+ ai: {
643
+ enabled: true,
644
+ apiKey: process.env.ANTHROPIC_API_KEY,
645
+ systemPrompt: DEFAULT_AI_SYSTEM_PROMPT + "\n\nFocus on Vue 3 Composition API patterns.",
646
+ },
647
+ });
648
+ ```
436
649
 
437
- ### `getSeverityLevels(options?)`
650
+ Type: `string`
438
651
 
439
- Returns severity level definitions with labels, descriptions, and ordering.
652
+ ---
440
653
 
441
- ```ts
442
- const { severityLevels } = getSeverityLevels();
443
- // severityLevels[0] → { id: "Critical", label: "Critical", order: 0, description: "..." }
444
- ```
654
+ > **Note on `ai_enriched_findings` fast path**: When `getFindings()` receives a payload that contains `ai_enriched_findings` and no `screenshotUrlBuilder` option is provided, it returns `ai_enriched_findings` directly without re-normalizing the raw `findings` array. If a `screenshotUrlBuilder` is provided, normalization always runs so paths can be rewritten.
445
655
 
446
656
  ---
447
657
 
@@ -119,7 +119,7 @@ Output shape:
119
119
 
120
120
  Core artifacts generated by the pipeline:
121
121
 
122
- - `progress.json`: step status (`page`, `axe`, `cdp`, `pa11y`, `merge`, `intelligence`)
122
+ - `progress.json`: step status `page`, `axe`, `cdp`, `pa11y`, `merge`, `intelligence`, `repo` (remote package.json fetch), `patterns` (source pattern scan), `ai` (Claude enrichment)
123
123
  - `a11y-scan-results.json`: merged runtime scan output per route
124
124
  - `a11y-findings.json`: enriched findings payload used by reports and API consumers
125
125
 
@@ -49,6 +49,7 @@ This document is the current technical inventory of the engine package.
49
49
  | `assets/remediation/code-patterns.mjs` | Source code pattern definitions |
50
50
  | `assets/remediation/source-boundaries.mjs` | Framework source boundaries |
51
51
  | `assets/remediation/axe-check-maps.mjs` | axe check-to-rule mappings |
52
+ | `assets/knowledge/knowledge.mjs` | Knowledge API data — scanner help, personas, concepts, glossary, docs, conformance levels, WCAG principles, severity definitions |
52
53
  | `assets/reporting/compliance-config.mjs` | Compliance scoring configuration |
53
54
  | `assets/reporting/wcag-reference.mjs` | WCAG + persona mapping reference |
54
55
  | `assets/reporting/manual-checks.mjs` | Manual checklist data |
@@ -62,6 +63,7 @@ Current files:
62
63
  - `tests/asset-loader.test.mjs`
63
64
  - `tests/audit-summary.test.mjs`
64
65
  - `tests/enriched-findings.test.mjs`
66
+ - `tests/knowledge-api.test.mjs`
65
67
  - `tests/reports-api.test.mjs`
66
68
  - `tests/reports-paths.test.mjs`
67
69
  - `tests/run-audit.integration.test.mjs`
@@ -1,6 +1,6 @@
1
1
  # Intelligence & Enrichment
2
2
 
3
- **Navigation**: [Home](../README.md) • [Architecture](architecture.md) • [API Reference](api-reference.md) • [CLI Handbook](cli-handbook.md) • [Output Artifacts](outputs.md) • [Engine Manifest](engine-manifest.md) • [Intelligence](intelligence.md) • [Testing](testing.md)
3
+ **Navigation**: [Home](../README.md) • [Architecture](architecture.md) • [Intelligence](intelligence.md) • [API Reference](api-reference.md) • [CLI Handbook](cli-handbook.md) • [Output Artifacts](outputs.md) • [Engine Manifest](engine-manifest.md) • [Testing](testing.md)
4
4
 
5
5
  ---
6
6
 
package/docs/outputs.md CHANGED
@@ -63,6 +63,10 @@ Real-time scan progress written by `src/pipeline/dom-scanner.mjs` as each engine
63
63
  | `cdp` | CDP | Chrome DevTools Protocol accessibility tree check. `found` = issue count. |
64
64
  | `pa11y` | pa11y | HTML CodeSniffer scan. `found` = issue count. |
65
65
  | `merge` | — | Cross-engine merge and deduplication. `merged` = final unique count. |
66
+ | `repo` | — | Remote `package.json` fetch via GitHub API for stack detection. Only emitted when `repoUrl` is set. |
67
+ | `intelligence` | — | Analyzer enrichment step (fix intelligence, ownership, scoring). |
68
+ | `patterns` | — | Source code pattern scan (local or remote). Only emitted when `projectDir` or `repoUrl` is set. `total` / `confirmed` / `potential` counts in `extra`. |
69
+ | `ai` | Claude | AI enrichment via Anthropic API. Only emitted when `ai.enabled` is `true` and `ai.apiKey` is set. |
66
70
 
67
71
  ### Step statuses
68
72
 
package/docs/testing.md CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  - **Framework**: Vitest
16
16
  - **Command**: `pnpm test`
17
- - **Current suite**: 8 files, 26 tests
17
+ - **Current suite**: 9 files, 38 tests
18
18
 
19
19
  The suite focuses on regression protection for architecture changes, public API contracts, and critical report-generation paths.
20
20
 
@@ -32,19 +32,24 @@ The suite focuses on regression protection for architecture changes, public API
32
32
  - `tests/audit-summary.test.mjs`
33
33
  - Verifies canonicalization, normalization, sorting, effort inference, quick wins, and detected stack output.
34
34
 
35
- ### 3) Report API and import safety
35
+ ### 3) Knowledge API
36
+
37
+ - `tests/knowledge-api.test.mjs`
38
+ - Verifies `getKnowledge()` returns the full expected shape: scanner, personas, concepts, glossary, docs, conformance levels, WCAG principles, and severity levels.
39
+
40
+ ### 4) Report API and import safety
36
41
 
37
42
  - `tests/reports-api.test.mjs`
38
43
  - `tests/reports-paths.test.mjs`
39
44
  - Verifies report APIs return expected output types and protects against broken relative imports after refactors.
40
45
 
41
- ### 4) Source-pattern behavior
46
+ ### 5) Source-pattern behavior
42
47
 
43
48
  - `tests/source-patterns.test.mjs`
44
49
  - `tests/source-scanner-utils.test.mjs`
45
50
  - Verifies edge behavior for pattern filtering and source scanner utility functions.
46
51
 
47
- ### 5) Integration tests (no network)
52
+ ### 6) Integration tests (no network)
48
53
 
49
54
  - `tests/run-audit.integration.test.mjs`
50
55
  - Mocks scanner/analyzer modules to verify:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegovelasquezweb/a11y-engine",
3
- "version": "0.8.5",
3
+ "version": "0.10.0",
4
4
  "description": "WCAG 2.2 accessibility audit engine — scanner, analyzer, and report builders",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/cli/audit.mjs CHANGED
@@ -34,6 +34,7 @@ Audit Intelligence:
34
34
  --axe-tags <csv> Comma-separated axe tags (e.g., wcag2a,wcag2aa).
35
35
  --only-rule <id> Only check for this specific rule ID.
36
36
  --ignore-findings <csv> Ignore specific rule IDs.
37
+ --include-incomplete Include axe "incomplete" items as findings.
37
38
  --exclude-selectors <csv> Exclude CSS selectors from scan.
38
39
 
39
40
  Execution & Emulation:
@@ -187,6 +188,7 @@ async function main() {
187
188
  const skipPatterns = argv.includes("--skip-patterns");
188
189
  const affectedOnly = argv.includes("--affected-only");
189
190
  const ignoreFindings = getArgValue("ignore-findings");
191
+ const includeIncomplete = argv.includes("--include-incomplete");
190
192
  const excludeSelectors = getArgValue("exclude-selectors");
191
193
 
192
194
  const waitUntil = getArgValue("wait-until");
@@ -290,6 +292,7 @@ async function main() {
290
292
 
291
293
  const analyzerArgs = [];
292
294
  if (ignoreFindings) analyzerArgs.push("--ignore-findings", ignoreFindings);
295
+ if (includeIncomplete) analyzerArgs.push("--include-incomplete");
293
296
  const resolvedFramework = framework || detectedFramework;
294
297
  if (resolvedFramework) analyzerArgs.push("--framework", resolvedFramework);
295
298
  await runScript("../enrichment/analyzer.mjs", analyzerArgs);
@@ -257,6 +257,7 @@ function printUsage() {
257
257
  Options:
258
258
  --output <path> Output findings JSON path (default: .audit/a11y-findings.json)
259
259
  --ignore-findings <csv> Ignore specific rule IDs (overrides config)
260
+ --include-incomplete Include axe "incomplete" items as findings
260
261
  -h, --help Show this help
261
262
  `);
262
263
  }
@@ -276,10 +277,15 @@ function parseArgs(argv) {
276
277
  input: getInternalPath("a11y-scan-results.json"),
277
278
  output: getInternalPath("a11y-findings.json"),
278
279
  ignoreFindings: [],
280
+ includeIncomplete: false,
279
281
  };
280
282
 
281
283
  for (let i = 0; i < argv.length; i += 1) {
282
284
  const key = argv[i];
285
+ if (key === "--include-incomplete") {
286
+ args.includeIncomplete = true;
287
+ continue;
288
+ }
283
289
  const value = argv[i + 1];
284
290
  if (!key.startsWith("--") || value === undefined) continue;
285
291
 
@@ -897,6 +903,7 @@ function buildFindings(inputPayload, cliArgs) {
897
903
  primary_source_scope: ownership.primarySourceScope,
898
904
  search_strategy: ownership.searchStrategy,
899
905
  component_hint: extractComponentHint(bestSelector) ?? derivePageHint(route.path),
906
+ needs_verification: !!v._fromIncomplete,
900
907
  verification_command: `pnpm a11y --base-url ${route.url} --routes ${route.path} --only-rule ${v.id} --max-routes 1`,
901
908
  verification_command_fallback: `node scripts/audit.mjs --base-url ${route.url} --routes ${route.path} --only-rule ${v.id} --max-routes 1`,
902
909
  });
@@ -966,12 +973,33 @@ export function collectIncompleteFindings(routes) {
966
973
  /**
967
974
  * Runs the analyzer programmatically on a scan payload.
968
975
  * @param {Object} scanPayload - The raw scan output from dom-scanner ({ routes, base_url, projectContext, ... }).
969
- * @param {{ ignoreFindings?: string[], framework?: string, output?: string }} [options={}]
976
+ * @param {{ ignoreFindings?: string[], framework?: string, output?: string, includeIncomplete?: boolean }} [options={}]
970
977
  * @returns {Object} The enriched findings payload { findings, incomplete_findings, metadata, ... }.
971
978
  */
972
979
  export function runAnalyzer(scanPayload, options = {}) {
973
980
  if (!scanPayload) throw new Error("Missing scan payload");
974
981
 
982
+ const sourceRoutes = scanPayload.routes || [];
983
+ const routesForAnalysis = options.includeIncomplete
984
+ ? sourceRoutes.map((route) => {
985
+ const incomplete = Array.isArray(route.incomplete) ? route.incomplete : [];
986
+ const violations = Array.isArray(route.violations) ? route.violations : [];
987
+ if (incomplete.length === 0) return route;
988
+ return {
989
+ ...route,
990
+ violations: [
991
+ ...violations,
992
+ ...incomplete.map((item) => ({ ...item, _fromIncomplete: true })),
993
+ ],
994
+ };
995
+ })
996
+ : sourceRoutes;
997
+
998
+ const scanPayloadForAnalysis =
999
+ routesForAnalysis === sourceRoutes
1000
+ ? scanPayload
1001
+ : { ...scanPayload, routes: routesForAnalysis };
1002
+
975
1003
  const args = {
976
1004
  input: null,
977
1005
  output: options.output || getInternalPath("a11y-findings.json"),
@@ -980,7 +1008,7 @@ export function runAnalyzer(scanPayload, options = {}) {
980
1008
  };
981
1009
 
982
1010
  const ignoredRules = new Set(args.ignoreFindings);
983
- const result = buildFindings(scanPayload, args);
1011
+ const result = buildFindings(scanPayloadForAnalysis, args);
984
1012
 
985
1013
  if (ignoredRules.size > 0) {
986
1014
  const knownIds = new Set(
@@ -1056,6 +1084,7 @@ function main() {
1056
1084
  runAnalyzer(payload, {
1057
1085
  ignoreFindings: args.ignoreFindings,
1058
1086
  framework: args.framework,
1087
+ includeIncomplete: args.includeIncomplete,
1059
1088
  output: args.output,
1060
1089
  });
1061
1090
  }
package/src/index.d.mts CHANGED
@@ -51,6 +51,7 @@ export interface Finding {
51
51
  source_rule_id?: string | null;
52
52
  pages_affected?: number | null;
53
53
  affected_urls?: string[] | null;
54
+ needs_verification?: boolean;
54
55
  }
55
56
 
56
57
  export interface EnrichedFinding {
@@ -102,6 +103,7 @@ export interface EnrichedFinding {
102
103
  checkData: Record<string, unknown> | null;
103
104
  pagesAffected: number | null;
104
105
  affectedUrls: string[] | null;
106
+ needsVerification?: boolean;
105
107
  }
106
108
 
107
109
  export interface SeverityTotals {
@@ -142,6 +144,7 @@ export interface AuditSummary {
142
144
  export interface ScanPayload {
143
145
  findings: Finding[] | Record<string, unknown>[];
144
146
  metadata?: Record<string, unknown>;
147
+ incomplete_findings?: unknown[];
145
148
  }
146
149
 
147
150
  export interface ReportOptions {
@@ -397,6 +400,7 @@ export interface RunAuditOptions {
397
400
  repoUrl?: string;
398
401
  githubToken?: string;
399
402
  skipPatterns?: boolean;
403
+ includeIncomplete?: boolean;
400
404
  screenshotsDir?: string;
401
405
  engines?: EngineSelection;
402
406
  ai?: AiOptions;
@@ -449,18 +453,6 @@ export function getSourcePatterns(
449
453
  options?: SourcePatternOptions
450
454
  ): Promise<SourcePatternResult>;
451
455
 
452
- export function getScannerHelp(options?: KnowledgeOptions): ScannerHelp;
453
-
454
- export function getPersonaReference(options?: KnowledgeOptions): PersonaReference;
455
-
456
- export function getUiHelp(options?: KnowledgeOptions): UiHelp;
457
-
458
- export function getConformanceLevels(options?: KnowledgeOptions): ConformanceLevelsResult;
459
-
460
- export function getWcagPrinciples(options?: KnowledgeOptions): WcagPrinciplesResult;
461
-
462
- export function getSeverityLevels(options?: KnowledgeOptions): SeverityLevelsResult;
463
-
464
456
  export function getKnowledge(options?: KnowledgeOptions): EngineKnowledge;
465
457
 
466
458
  export const DEFAULT_AI_SYSTEM_PROMPT: string;
package/src/index.mjs CHANGED
@@ -161,6 +161,7 @@ function normalizeSingleFinding(item, index, screenshotUrlBuilder) {
161
161
  check_data: item.check_data && typeof item.check_data === "object" ? item.check_data : null,
162
162
  pages_affected: typeof item.pages_affected === "number" ? item.pages_affected : null,
163
163
  affected_urls: Array.isArray(item.affected_urls) ? item.affected_urls : null,
164
+ needs_verification: Boolean(item.needs_verification),
164
165
  };
165
166
  }
166
167
 
@@ -247,6 +248,7 @@ export function getFindings(input, options = {}) {
247
248
  checkData: finding.check_data,
248
249
  pagesAffected: finding.pages_affected,
249
250
  affectedUrls: finding.affected_urls,
251
+ needsVerification: finding.needs_verification,
250
252
  };
251
253
 
252
254
  // Enrich from intelligence if no fix data exists yet
@@ -379,8 +381,9 @@ function getPersonaGroups(findings) {
379
381
  * @returns {object} Full audit summary.
380
382
  */
381
383
  export function getOverview(findings, payload = null) {
384
+ const scorableFindings = findings.filter((f) => !f.needsVerification);
382
385
  const totals = { Critical: 0, Serious: 0, Moderate: 0, Minor: 0 };
383
- for (const f of findings) {
386
+ for (const f of scorableFindings) {
384
387
  const severity = f.severity || "";
385
388
  if (severity in totals) totals[severity] += 1;
386
389
  }
@@ -434,7 +437,7 @@ export function getOverview(findings, payload = null) {
434
437
  * @param {{ locale?: string }} [options={}]
435
438
  * @returns {{ locale: string, version: string, title: string, engines: object[], options: object[] }}
436
439
  */
437
- export function getScannerHelp(options = {}) {
440
+ function getScannerHelp(options = {}) {
438
441
  const locale = resolveKnowledgeLocale(options.locale || "en");
439
442
  const payload = getKnowledgeData();
440
443
  const scanner = payload.locales[locale]?.scanner || { title: "Scanner Help", engines: [], options: [] };
@@ -455,7 +458,7 @@ export function getScannerHelp(options = {}) {
455
458
  * @param {{ locale?: string }} [options={}]
456
459
  * @returns {{ locale: string, version: string, personas: object[] }}
457
460
  */
458
- export function getPersonaReference(options = {}) {
461
+ function getPersonaReference(options = {}) {
459
462
  const locale = resolveKnowledgeLocale(options.locale || "en");
460
463
  const payload = getKnowledgeData();
461
464
  const wcagRef = getWcagReference();
@@ -492,7 +495,7 @@ export function getPersonaReference(options = {}) {
492
495
  * @param {{ locale?: string }} [options={}]
493
496
  * @returns {{ locale: string, version: string, tooltips: Record<string, object>, glossary: object[] }}
494
497
  */
495
- export function getUiHelp(options = {}) {
498
+ function getConceptsAndGlossary(options = {}) {
496
499
  const locale = resolveKnowledgeLocale(options.locale || "en");
497
500
  const payload = getKnowledgeData();
498
501
  const localePayload = payload.locales[locale] || {};
@@ -518,7 +521,7 @@ export function getUiHelp(options = {}) {
518
521
  * @param {{ locale?: string }} [options={}]
519
522
  * @returns {{ locale: string, version: string, conformanceLevels: object[] }}
520
523
  */
521
- export function getConformanceLevels(options = {}) {
524
+ function getConformanceLevels(options = {}) {
522
525
  const locale = resolveKnowledgeLocale(options.locale || "en");
523
526
  const payload = getKnowledgeData();
524
527
  const levels = payload.locales[locale]?.conformanceLevels || [];
@@ -535,7 +538,7 @@ export function getConformanceLevels(options = {}) {
535
538
  * @param {{ locale?: string }} [options={}]
536
539
  * @returns {{ locale: string, version: string, wcagPrinciples: object[] }}
537
540
  */
538
- export function getWcagPrinciples(options = {}) {
541
+ function getWcagPrinciples(options = {}) {
539
542
  const locale = resolveKnowledgeLocale(options.locale || "en");
540
543
  const payload = getKnowledgeData();
541
544
  const principles = payload.locales[locale]?.wcagPrinciples || [];
@@ -552,7 +555,7 @@ export function getWcagPrinciples(options = {}) {
552
555
  * @param {{ locale?: string }} [options={}]
553
556
  * @returns {{ locale: string, version: string, severityLevels: object[] }}
554
557
  */
555
- export function getSeverityLevels(options = {}) {
558
+ function getSeverityLevels(options = {}) {
556
559
  const locale = resolveKnowledgeLocale(options.locale || "en");
557
560
  const payload = getKnowledgeData();
558
561
  const levels = payload.locales[locale]?.severityLevels || [];
@@ -573,7 +576,7 @@ export const VIEWPORT_PRESETS = [
573
576
  export function getKnowledge(options = {}) {
574
577
  const scanner = getScannerHelp(options);
575
578
  const personas = getPersonaReference(options);
576
- const ui = getUiHelp(options);
579
+ const ui = getConceptsAndGlossary(options);
577
580
  const conformance = getConformanceLevels(options);
578
581
  const principles = getWcagPrinciples(options);
579
582
  const severity = getSeverityLevels(options);
@@ -622,6 +625,7 @@ export function getKnowledge(options = {}) {
622
625
  * framework?: string,
623
626
  * projectDir?: string,
624
627
  * skipPatterns?: boolean,
628
+ * includeIncomplete?: boolean,
625
629
  * engines?: { axe?: boolean, cdp?: boolean, pa11y?: boolean },
626
630
  * onProgress?: (step: string, status: string, extra?: object) => void,
627
631
  * }} options
@@ -691,6 +695,7 @@ export async function runAudit(options) {
691
695
  const findingsPayload = runAnalyzer(scanPayload, {
692
696
  ignoreFindings: options.ignoreFindings,
693
697
  framework: options.framework,
698
+ includeIncomplete: options.includeIncomplete,
694
699
  });
695
700
 
696
701
  // Step 3: Source patterns (optional) — works with local projectDir or remote repoUrl