This is a submission for the GitHub Copilot CLI Challenge
What I Built
What if your linter could actually fix the problems it found?
That's a11y-pilot - a CLI that scans your frontend code for accessibility violations, then spawns GitHub Copilot CLI to fix each one. Not "here's a suggestion". it literally invokes copilot --prompt with a crafted fix instruction and lets the AI refactor your code in place.
I pointed it at a file with 12 accessibility issues. Ran a11y-pilot fix. Every single issue was resolved — <div onClick> became <button>, empty <img> got meaningful alt text inferred from the filename, unlabeled inputs got proper aria-label. Re-scanning the file: 0 issues. No manual edits.
Demo
npm: https://www.npmjs.com/package/a11y-pilot
GitHub: https://github.com/Safvan-tsy/a11y-pilot
How it works
Scan → Detect → Prompt Engineer → Copilot CLI Fixes → Done
Scan - Point it at any directory or file. It walks your project and parses JSX, TSX, HTML, Vue, Svelte, and Astro files using Babel AST (for JSX/TSX) and htmlparser2 (for HTML-like templates).
Detect - 15 accessibility rules run against every parsed element, checking for WCAG violations across 5 categories.
Auto-fix - For each issue, a11y-pilot builds a precise, context-rich prompt and spawns
copilot --prompt <text> --allow-all-tools. Copilot CLI reads the file, understands the surrounding code, and applies the minimum diff needed. No blind find-and-replace actual AI-driven refactoring.
The Copilot CLI bridge — the core of the project
This is not just a scanner that suggests fixes. The entire point is that Copilot CLI is the execution engine. When you run a11y-pilot fix ./src, here's what happens under the hood:
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ a11y-pilot │────▶│ Issue detected │────▶│ Build prompt │
│ scanner │ │ (rule engine) │ │ (context-rich) │
└─────────────────┘ └──────────────────┘ └────────┬────────┘
│
▼
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ File fixed! │◀────│ Copilot applies │◀────│ copilot CLI │
│ ✔ Report │ │ the fix │ │ invoked │
└─────────────────┘ └──────────────────┘ └─────────────────┘
Each rule carries a copilotPrompt field — a carefully crafted instruction that tells Copilot CLI exactly what's wrong and how to fix it. For example, the no-div-button rule generates prompts like:
"In file src/Hero.tsx at line 20, fix this accessibility issue:
<div>has a click handler but is missing role and tabIndex. Replace this<div>element with a native<button>element. Move the onClick handler to the button. Remove any cursor: pointer styling (buttons have it by default). Only modify the minimum code necessary."
Copilot CLI then reads the full file context, understands the surrounding JSX structure, and makes intelligent fixes — not just string replacements, but real refactoring. It adds meaningful alt text based on image filenames, converts <div onClick> to proper <button> elements, wraps navigation links in <nav> with appropriate aria-label, and more.
Category Dashboard
After scanning, a11y-pilot renders a visual breakdown dashboard right in the terminal:
✖ Found 46 issues (35 errors, 11 warnings) in 4 files (5 scanned)
📊 Issue Breakdown
──────────────────────────────────────────────────
♿ Accessibility ████████░░░░░░░░░░░░ 19 (41%) 19E
🏗️ Semantic HTML █████░░░░░░░░░░░░░░░ 11 (24%) 4E 7W
⌨️ Keyboard ███░░░░░░░░░░░░░░░░░ 7 (15%) 5E 2W
🏷️ ARIA ██░░░░░░░░░░░░░░░░░░ 5 (11%) 5E
👆 Interaction ██░░░░░░░░░░░░░░░░░░ 4 (9%) 2E 2W
──────────────────────────────────────────────────
14 rules triggered: img-alt, form-label, no-div-button, ...
This gives you an instant snapshot of where your accessibility debt lives — is it ARIA misuse? Keyboard traps? Missing semantics? You know exactly where to focus.
My Experience with GitHub Copilot CLI
GitHub Copilot CLI was central to this project in two distinct ways:
1. Copilot CLI as the product's engine
The marquee feature of a11y-pilot is --auto-fix. When triggered, it spawns copilot --prompt <text> --allow-all-tools for each detected issue. I discovered the --prompt flag enables non-interactive mode, and --allow-all-tools auto-approves tool use — this combination is what makes programmatic Copilot CLI invocation possible.
The quality of fixes was surprisingly excellent. Given a file with <div onClick={() => alert('clicked')}>, Copilot CLI didn't just slap a role="button" on it — it actually converted the entire element to a <button>, moved the handler, and preserved the className. For empty <a href="/profile"></a>, it added aria-label="Profile" — inferring the label from the URL. These aren't template fixes; they're context-aware refactoring.
The key insight was prompt engineering per rule. Each of the 15 rules carries a copilotPrompt field — a precisely crafted instruction that gives Copilot CLI enough context to understand the problem and the expected fix pattern, without being so prescriptive that it loses the ability to adapt to surrounding code. This prompt design is what turns a11y-pilot from "accessibility linter" into "accessibility linter + AI-powered fixer."
2. Copilot CLI as a development tool
Beyond the product itself, Copilot CLI was my primary development companion throughout the build:
Parser logic — Writing Babel AST traversal for JSX elements with normalized attribute handling required getting a lot of edge cases right (spread props, expression containers, member expressions). Copilot CLI helped iterate on the visitor pattern and handle the
@babel/traverseESM default export quirk (_traverse.default || _traverse).Rule implementation — For each of the 15 rules, I used Copilot CLI to reference WCAG criteria and ensure the
check()functions handle edge cases correctly — like not flagging<a>tags withouthrefin thearia-hidden-focusrule, or skippinginput[type="hidden"]in form-label checks.Debugging — When the copilot-bridge initially used
shell: trueand hit the Node.js DEP0190 deprecation warning, Copilot CLI helped me switch to direct binary resolution withexecFileSync('which', ['copilot'])and properspawn()without shell.
What impressed me
The thing that stood out most was Copilot CLI's ability to handle file-level context. When I pointed it at a file with 12 accessibility issues and said "fix the <div onClick> on line 20," it didn't break the other 11 problematic lines. It made surgical, minimal edits. That's the property that made the auto-fix feature viable — I could confidently fix issues one-by-one or in batches without worrying about cascading breakage.
Project links
-
npm:
npm install -g a11y-pilot - GitHub: https://github.com/Safvan-tsy/a11y-pilot
-
Try it now:
npx a11y-pilot scan ./src

Top comments (17)
For anyone curious about the Copilot CLI integration: the key discovery was
copilot --prompt "..." --allow-all-tools. The--promptflag runs it non-interactively, and--allow-all-toolsauto-approves file edits. Without these two flags, programmatic invocation wouldn't be possible. Happy to share more about the prompt engineering approach if anyone's interested.good work
good problem solver
Great Work!!
thanks
How is this different from eslint-plugin-jsx-a11y ?
eslint-plugin-jsx-a11y is excellent and covers similar ground for React projects.
key difference is:
Think of it as "what if your linter had an AI pair programmer attached."
Great idea. productivity win 🙌
thanks
Very cool!
Accessibility always matter. Great work 👏
This is seriously impressive. I recently ran an accessibility scan on one of my tool pages and fixing issues manually took hours — especially replacing clickable divs and adding proper labels. The idea of auto-fix with context-aware refactoring is a huge time saver. Curious — did you notice any cases where Copilot’s fix needed manual correction, or was it reliable most of the time?
nice terminal ui
Thanks, the terminal output uses chalk, gradient-string, and boxen.
good work safvan
This is seriously impressive 👏🔥