@diegovelasquezweb/a11y-engine 0.11.36 → 0.11.38

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegovelasquezweb/a11y-engine",
3
- "version": "0.11.36",
3
+ "version": "0.11.38",
4
4
  "description": "WCAG 2.2 accessibility audit engine — scanner, analyzer, and report builders",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -150,7 +150,7 @@ function getPatternCandidateFile(projectDir, finding) {
150
150
  return { abs, rel: finding.file, content };
151
151
  }
152
152
 
153
- function buildPatternAiInput({ finding, candidate }) {
153
+ function buildPatternAiInput({ finding, candidate, projectHints }) {
154
154
  // Extract the exact line(s) containing the pattern match so Claude has an
155
155
  // unambiguous search anchor instead of inferring it from the full file.
156
156
  const fileLines = candidate.content.split("\n");
@@ -181,6 +181,7 @@ function buildPatternAiInput({ finding, candidate }) {
181
181
  fixCode: finding.fix_code || "",
182
182
  },
183
183
  files: [{ filePath: candidate.rel, content: candidate.content.slice(0, 12000) }],
184
+ ...(projectHints ? { projectContext: projectHints } : {}),
184
185
  };
185
186
  }
186
187
 
@@ -385,7 +386,28 @@ function parseJsonBlock(text) {
385
386
  }
386
387
  }
387
388
 
388
- async function callClaudeForPatch({ apiKey, model, aiInput }) {
389
+ function extractRemediationContext(remediationPath) {
390
+ if (!remediationPath || !fs.existsSync(remediationPath)) return null;
391
+ try {
392
+ const content = fs.readFileSync(remediationPath, "utf8");
393
+ const sections = [];
394
+ const guardrailsMatch = content.match(/## Agent Operating Procedures \(Guardrails\)([\s\S]*?)(?=\n---|\n##|$)/);
395
+ if (guardrailsMatch) {
396
+ sections.push("## Project Guardrails (from audit)\n" + guardrailsMatch[1].trim());
397
+ }
398
+ const sourceMatch = content.match(/## Source File Locations([\s\S]*?)(?=\n---|\n##|$)/);
399
+ if (sourceMatch) {
400
+ sections.push("## Source File Locations (from audit)\n" + sourceMatch[1].trim());
401
+ }
402
+ return sections.length > 0 ? sections.join("\n\n") : null;
403
+ } catch {
404
+ return null;
405
+ }
406
+ }
407
+
408
+ async function callClaudeForPatch({ apiKey, model, aiInput, remediationPath }) {
409
+ const remediationContext = extractRemediationContext(remediationPath);
410
+
389
411
  const system = [
390
412
  "You are an accessibility fix engine.",
391
413
  "Return JSON only — no markdown fences, no extra text.",
@@ -397,6 +419,7 @@ async function callClaudeForPatch({ apiKey, model, aiInput }) {
397
419
  "For insertions (new element not yet in the file), anchor the search on the nearest existing surrounding element and include it in both search and replace.",
398
420
  "Do not create new files. Only write changes for filePaths listed in the files array.",
399
421
  "CSS files are never in the files array. Fix visual issues (touch targets, sizing) using inline style attributes or markup changes in the HTML file — never reference or create .css files.",
422
+ ...(remediationContext ? ["", "## Project Context (from audit report)", remediationContext] : []),
400
423
  "Schema:",
401
424
  "{\"changes\":[{\"filePath\":\"...\",\"search\":\"...\",\"replace\":\"...\"}],\"verifyRule\":\"...\",\"verifyRoute\":\"...\",\"notes\":\"...\"}",
402
425
  ].join("\n");
@@ -521,6 +544,7 @@ export async function applyFindingFix(input) {
521
544
  const findingId = typeof input.findingId === "string" ? input.findingId.trim() : "";
522
545
  const projectDir = typeof input.projectDir === "string" ? input.projectDir.trim() : "";
523
546
  const projectHints = typeof input.projectHints === "string" ? input.projectHints.trim() : "";
547
+ const remediationPath = typeof input.remediationPath === "string" ? input.remediationPath.trim() : (process.env.REMEDIATION_PATH || "");
524
548
 
525
549
  if (!findingId || !projectDir) {
526
550
  return buildResult({
@@ -573,14 +597,14 @@ export async function applyFindingFix(input) {
573
597
  });
574
598
  }
575
599
 
576
- const aiInput = buildPatternAiInput({ finding, candidate });
600
+ const aiInput = buildPatternAiInput({ finding, candidate, projectHints });
577
601
  const candidateSet = new Set([candidate.rel]);
578
602
 
579
603
  let patchOutput = null;
580
604
  let claudeUsage = { input_tokens: 0, output_tokens: 0 };
581
605
  if (apiKey) {
582
606
  try {
583
- const { patch, usage } = await callClaudeForPatch({ apiKey, model, aiInput });
607
+ const { patch, usage } = await callClaudeForPatch({ apiKey, model, aiInput, remediationPath });
584
608
  patchOutput = patch;
585
609
  claudeUsage = usage;
586
610
  } catch {
@@ -781,6 +805,7 @@ export async function applyFindingsFix(input) {
781
805
  : [];
782
806
  const projectDir = typeof input.projectDir === "string" ? input.projectDir.trim() : "";
783
807
  const projectHints = typeof input.projectHints === "string" ? input.projectHints.trim() : "";
808
+ const remediationPath = typeof input.remediationPath === "string" ? input.remediationPath.trim() : (process.env.REMEDIATION_PATH || "");
784
809
  const apiKey = input.ai?.apiKey || process.env.ANTHROPIC_API_KEY || "";
785
810
  const model = input.ai?.model || DEFAULT_MODEL;
786
811
 
@@ -889,7 +914,7 @@ export async function applyFindingsFix(input) {
889
914
  let claudeUsage = { input_tokens: 0, output_tokens: 0 };
890
915
  if (apiKey) {
891
916
  try {
892
- const { patch, usage } = await callClaudeForPatch({ apiKey, model, aiInput });
917
+ const { patch, usage } = await callClaudeForPatch({ apiKey, model, aiInput, remediationPath });
893
918
  patchOutput = patch;
894
919
  claudeUsage = usage;
895
920
  } catch {