@haystackeditor/cli 0.8.1 → 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/README.md +93 -87
- package/dist/assets/hooks/llm-rules-template.md +21 -0
- package/dist/assets/hooks/package.json +2 -2
- package/dist/assets/hooks/scripts/pre-push.sh +20 -0
- package/dist/assets/skills/prepare-haystack.md +323 -0
- package/dist/assets/skills/secrets.md +164 -0
- package/dist/assets/skills/setup-external-sandbox.md +243 -0
- package/dist/assets/skills/setup-haystack.md +639 -0
- package/dist/assets/skills/submit.md +154 -0
- package/dist/assets/templates/CLAUDE.md.snippet +42 -0
- package/dist/assets/templates/haystack.yml +193 -0
- package/dist/commands/check-pending.d.ts +19 -0
- package/dist/commands/check-pending.js +217 -0
- package/dist/commands/config.d.ts +13 -21
- package/dist/commands/config.js +278 -92
- package/dist/commands/dismiss.d.ts +17 -0
- package/dist/commands/dismiss.js +201 -0
- package/dist/commands/init.js +25 -28
- package/dist/commands/install-session-hooks.d.ts +16 -0
- package/dist/commands/install-session-hooks.js +302 -0
- package/dist/commands/login.js +1 -1
- package/dist/commands/policy.d.ts +31 -0
- package/dist/commands/policy.js +365 -0
- package/dist/commands/pr-status.d.ts +16 -0
- package/dist/commands/pr-status.js +188 -0
- package/dist/commands/setup.d.ts +13 -0
- package/dist/commands/setup.js +496 -0
- package/dist/commands/skills.d.ts +2 -2
- package/dist/commands/skills.js +51 -186
- package/dist/commands/submit.d.ts +23 -0
- package/dist/commands/submit.js +456 -0
- package/dist/commands/triage.d.ts +16 -0
- package/dist/commands/triage.js +354 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +344 -4
- package/dist/tools/detect.d.ts +50 -0
- package/dist/tools/detect.js +853 -0
- package/dist/tools/fixtures.d.ts +38 -0
- package/dist/tools/fixtures.js +199 -0
- package/dist/tools/setup.d.ts +43 -0
- package/dist/tools/setup.js +597 -0
- package/dist/triage/prompts.d.ts +31 -0
- package/dist/triage/prompts.js +296 -0
- package/dist/triage/runner.d.ts +21 -0
- package/dist/triage/runner.js +339 -0
- package/dist/triage/traces.d.ts +20 -0
- package/dist/triage/traces.js +305 -0
- package/dist/triage/types.d.ts +47 -0
- package/dist/triage/types.js +7 -0
- package/dist/types.d.ts +1387 -191
- package/dist/types.js +254 -2
- package/dist/utils/analysis-api.d.ts +108 -0
- package/dist/utils/analysis-api.js +194 -0
- package/dist/utils/config.js +1 -1
- package/dist/utils/git.d.ts +80 -0
- package/dist/utils/git.js +302 -0
- package/dist/utils/github-api.d.ts +83 -0
- package/dist/utils/github-api.js +266 -0
- package/dist/utils/pending-state.d.ts +40 -0
- package/dist/utils/pending-state.js +86 -0
- package/dist/utils/secrets.js +3 -3
- package/dist/utils/skill.js +257 -0
- package/package.json +11 -9
package/README.md
CHANGED
|
@@ -1,68 +1,43 @@
|
|
|
1
1
|
# @haystackeditor/cli
|
|
2
2
|
|
|
3
|
-
Set up Haystack
|
|
3
|
+
Set up Haystack for your project. When PRs are opened, Haystack automatically analyzes them for bugs, intent drift, and rule violations.
|
|
4
4
|
|
|
5
5
|
## Quick Start
|
|
6
6
|
|
|
7
|
-
### Fastest: AI-Assisted Setup
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
npx @haystackeditor/cli skills install
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
This auto-detects your coding CLI (Claude Code, Codex, Cursor) and installs the Haystack MCP server.
|
|
14
|
-
|
|
15
|
-
Then invoke the setup in your coding CLI:
|
|
16
|
-
|
|
17
|
-
| CLI | How to invoke |
|
|
18
|
-
|-----|---------------|
|
|
19
|
-
| **Claude Code** | `/setup-haystack` |
|
|
20
|
-
| **Codex CLI** | `/setup-haystack` or ask "set up haystack verification" |
|
|
21
|
-
| **Cursor** | `/setup-haystack` (in Composer) |
|
|
22
|
-
|
|
23
|
-
The AI will analyze your codebase, create `.haystack.json`, and configure verification flows.
|
|
24
|
-
|
|
25
|
-
### Alternative: Direct CLI Setup
|
|
26
|
-
|
|
27
|
-
For quick setup without AI assistance:
|
|
28
|
-
|
|
29
7
|
```bash
|
|
30
|
-
|
|
8
|
+
npm install -g @haystackeditor/cli
|
|
9
|
+
haystack login
|
|
10
|
+
haystack setup
|
|
31
11
|
```
|
|
32
12
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
### Additional Skills
|
|
13
|
+
The `setup` command walks you through an interactive wizard:
|
|
36
14
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
15
|
+
1. **Select repositories** to configure
|
|
16
|
+
2. **Scan for coding rules** (conventions your team follows)
|
|
17
|
+
3. **Scan for CI/bot signals** (checks to wait for before merging)
|
|
18
|
+
4. **Scan for review policies** (who should review what)
|
|
19
|
+
5. **Review and toggle** discovered items
|
|
20
|
+
6. **Write `.haystack.json`** to your repos
|
|
42
21
|
|
|
43
22
|
---
|
|
44
23
|
|
|
45
24
|
## CLI Commands
|
|
46
25
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
### `haystack skills`
|
|
26
|
+
### `haystack setup`
|
|
50
27
|
|
|
51
|
-
|
|
28
|
+
Interactive onboarding wizard — scan your repos and generate `.haystack.json`:
|
|
52
29
|
|
|
53
30
|
```bash
|
|
54
|
-
|
|
55
|
-
npx @haystackeditor/cli skills install --cli codex # Install for Codex
|
|
56
|
-
npx @haystackeditor/cli skills install --cli cursor # Install for Cursor
|
|
57
|
-
npx @haystackeditor/cli skills install --cli manual # Show manual setup
|
|
58
|
-
npx @haystackeditor/cli skills list # List available skills
|
|
31
|
+
haystack setup
|
|
59
32
|
```
|
|
60
33
|
|
|
61
34
|
### `haystack init`
|
|
62
35
|
|
|
36
|
+
Quick local setup — auto-detects your project and creates `.haystack.json` without scanning:
|
|
37
|
+
|
|
63
38
|
```bash
|
|
64
|
-
|
|
65
|
-
|
|
39
|
+
haystack init # Auto-detect and create config
|
|
40
|
+
haystack init --force # Overwrite existing .haystack.json
|
|
66
41
|
```
|
|
67
42
|
|
|
68
43
|
### `haystack status`
|
|
@@ -70,22 +45,22 @@ npx @haystackeditor/cli init -y # Accept all defaults
|
|
|
70
45
|
Check if your project is configured:
|
|
71
46
|
|
|
72
47
|
```bash
|
|
73
|
-
|
|
48
|
+
haystack status
|
|
74
49
|
```
|
|
75
50
|
|
|
76
51
|
### `haystack login`
|
|
77
52
|
|
|
78
|
-
Authenticate with GitHub (required for secrets
|
|
53
|
+
Authenticate with GitHub (required for setup and secrets):
|
|
79
54
|
|
|
80
55
|
```bash
|
|
81
|
-
|
|
56
|
+
haystack login
|
|
82
57
|
```
|
|
83
58
|
|
|
84
59
|
This uses GitHub's device flow - you'll get a code to enter at github.com/login/device.
|
|
85
60
|
|
|
86
61
|
```bash
|
|
87
62
|
# Log out (removes stored credentials)
|
|
88
|
-
|
|
63
|
+
haystack logout
|
|
89
64
|
```
|
|
90
65
|
|
|
91
66
|
### `haystack secrets`
|
|
@@ -94,13 +69,13 @@ Manage secrets that will be injected into your sandbox environment:
|
|
|
94
69
|
|
|
95
70
|
```bash
|
|
96
71
|
# List all secrets (keys only, values are never shown)
|
|
97
|
-
|
|
72
|
+
haystack secrets list
|
|
98
73
|
|
|
99
74
|
# Set a secret
|
|
100
|
-
|
|
75
|
+
haystack secrets set OPENAI_API_KEY sk-xxx
|
|
101
76
|
|
|
102
77
|
# Delete a secret
|
|
103
|
-
|
|
78
|
+
haystack secrets delete OPENAI_API_KEY
|
|
104
79
|
```
|
|
105
80
|
|
|
106
81
|
Secrets are encrypted and stored securely. They're automatically injected as environment variables when the sandbox runs your app.
|
|
@@ -109,59 +84,90 @@ Secrets are encrypted and stored securely. They're automatically injected as env
|
|
|
109
84
|
|
|
110
85
|
```bash
|
|
111
86
|
# Org-scoped (available to all repos in the org)
|
|
112
|
-
|
|
87
|
+
haystack secrets set API_KEY xxx --scope org --scope-id myorg
|
|
113
88
|
|
|
114
89
|
# Repo-scoped (available only to this repo)
|
|
115
|
-
|
|
90
|
+
haystack secrets set API_KEY xxx --scope repo --scope-id owner/repo
|
|
116
91
|
```
|
|
117
92
|
|
|
118
|
-
|
|
93
|
+
### `haystack submit`
|
|
119
94
|
|
|
120
|
-
|
|
95
|
+
Create a PR from current changes. Runs pre-PR triage (code review, rules validation, intent drift), pushes your branch, and opens the PR.
|
|
121
96
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
dev_server:
|
|
129
|
-
command: pnpm dev
|
|
130
|
-
port: 3000
|
|
131
|
-
ready_pattern: "Local:"
|
|
132
|
-
env:
|
|
133
|
-
SKIP_AUTH: "true"
|
|
134
|
-
|
|
135
|
-
verification:
|
|
136
|
-
commands:
|
|
137
|
-
- name: build
|
|
138
|
-
run: pnpm build
|
|
139
|
-
- name: lint
|
|
140
|
-
run: pnpm lint
|
|
97
|
+
```bash
|
|
98
|
+
haystack submit # Triage -> create PR -> wait for analysis
|
|
99
|
+
haystack submit --title "Fix auth" # Custom PR title
|
|
100
|
+
haystack submit --draft # Create as draft PR
|
|
101
|
+
haystack submit --force # Skip triage checks
|
|
102
|
+
haystack submit --no-wait # Don't wait for analysis results
|
|
141
103
|
```
|
|
142
104
|
|
|
143
|
-
|
|
105
|
+
**Review routing**: By default, PRs go to the auto-merge queue -- if analysis passes, the PR is merged automatically. Use `--review` to route it for human review instead:
|
|
144
106
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
| API calls needing auth | Fixtures to mock responses |
|
|
107
|
+
```bash
|
|
108
|
+
haystack submit --review # Needs review (goes to assignment queue)
|
|
109
|
+
haystack submit --review octocat # Request review from a specific teammate
|
|
110
|
+
```
|
|
150
111
|
|
|
151
|
-
|
|
112
|
+
When `--review` is used without a username, the PR is labeled `haystack:needs-review` and appears in your team's assignment queue. When a username is provided, that person is also requested as a reviewer on GitHub.
|
|
113
|
+
|
|
114
|
+
### `haystack triage`
|
|
115
|
+
|
|
116
|
+
View Haystack analysis results for any PR. Shows the same data as the Haystack web feed: rating, verdict, structured findings with details, verified bugs, human review reasons, and agent fix prompts.
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
haystack triage 42 # Current repo, PR #42
|
|
120
|
+
haystack triage owner/repo#99 # Fully qualified
|
|
121
|
+
haystack triage https://github.com/o/r/pull/1 # From GitHub URL
|
|
122
|
+
haystack triage 42 --json # Machine-readable JSON output
|
|
123
|
+
haystack triage 42 --no-wait # Don't wait if analysis is pending
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The `--json` output includes `agentFixPrompt` fields -- ready-to-paste instructions for coding agents to fix each finding.
|
|
127
|
+
|
|
128
|
+
### `haystack check-pending`
|
|
129
|
+
|
|
130
|
+
Check the status of a previously submitted PR's analysis (from the current repo):
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
haystack check-pending # Rich terminal output
|
|
134
|
+
haystack check-pending --hook # Minimal output (for session-start hooks)
|
|
135
|
+
haystack check-pending --json # Machine-readable JSON
|
|
136
|
+
haystack check-pending --clear # Clear pending state
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Configuration
|
|
142
|
+
|
|
143
|
+
The `setup` wizard writes `.haystack.json` to your repos with discovered rules, signals, and policies. You can also create a base config locally with `haystack init`:
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"version": "1",
|
|
148
|
+
"name": "my-app",
|
|
149
|
+
"dev_server": {
|
|
150
|
+
"command": "pnpm dev",
|
|
151
|
+
"port": 3000,
|
|
152
|
+
"ready_pattern": "Local:",
|
|
153
|
+
"env": {
|
|
154
|
+
"SKIP_AUTH": "true"
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
152
159
|
|
|
153
160
|
---
|
|
154
161
|
|
|
155
162
|
## How It Works
|
|
156
163
|
|
|
157
|
-
1. Run `
|
|
158
|
-
2.
|
|
159
|
-
3.
|
|
160
|
-
|
|
161
|
-
- Spins up your app in a cloud sandbox
|
|
162
|
-
- Runs verification commands
|
|
163
|
-
- Captures screenshots and evidence
|
|
164
|
+
1. Run `haystack setup` to configure your repos (or `haystack init` for local-only config)
|
|
165
|
+
2. Install the [Haystack GitHub App](https://haystackeditor.com/github-app)
|
|
166
|
+
3. When PRs are opened, Haystack automatically:
|
|
167
|
+
- Analyzes the code for bugs, intent drift, and rule violations
|
|
164
168
|
- Reports results on the PR
|
|
169
|
+
- Routes the PR to the right inbox tab (Good to Merge, Issues Found, etc.)
|
|
170
|
+
- If auto-merge is enabled, clean PRs merge automatically
|
|
165
171
|
|
|
166
172
|
## License
|
|
167
173
|
|
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
These rules govern how LLM agents should interact with this codebase. They are enforced by a pre-commit hook.
|
|
4
4
|
|
|
5
|
+
## Creating Pull Requests
|
|
6
|
+
|
|
7
|
+
**Always use `haystack submit` instead of `gh pr create` or GitHub API.**
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Auto-merge queue (default) - PR merges automatically if Haystack approves
|
|
11
|
+
haystack submit
|
|
12
|
+
|
|
13
|
+
# Request Haystack review - for complex/uncertain changes
|
|
14
|
+
haystack submit --review
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
This routes your PR through the Haystack auto-merge queue:
|
|
18
|
+
1. Haystack analyzes your changes
|
|
19
|
+
2. If approved: auto-merged
|
|
20
|
+
3. If issues found: routed for review with feedback
|
|
21
|
+
|
|
22
|
+
Do NOT use `gh pr create` - it bypasses the auto-merge queue.
|
|
23
|
+
|
|
24
|
+
After submitting, use `haystack triage <pr-number>` to check analysis results (rating, findings, agent fix prompts). Use `--json` for machine-readable output.
|
|
25
|
+
|
|
5
26
|
- When you write or edit LLM prompts in response to a failure running against some test data, be certain that your edit is not specific to the test data, and is actually addressing the more generic problem that caused the failure.
|
|
6
27
|
- If the user specifies a model, follow it. Your understanding of current LLM models is outdated.
|
|
7
28
|
- Do not add silent fallbacks.
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Git hooks for AI agent quality checks (installed by @haystackeditor/cli)",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"tree-sitter": "
|
|
9
|
-
"tree-sitter-typescript": "
|
|
8
|
+
"tree-sitter": "0.22.4",
|
|
9
|
+
"tree-sitter-typescript": "0.23.2"
|
|
10
10
|
}
|
|
11
11
|
}
|
|
@@ -2,4 +2,24 @@
|
|
|
2
2
|
# Haystack hooks (powered by Entire)
|
|
3
3
|
# Pre-push hook: push session logs alongside user's push
|
|
4
4
|
# $1 is the remote name (e.g., "origin")
|
|
5
|
+
|
|
6
|
+
# Check if pushing a non-haystack branch and remind about haystack submit
|
|
7
|
+
current_branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
|
|
8
|
+
if [ -n "$current_branch" ] && [ "$current_branch" != "main" ] && [ "$current_branch" != "master" ]; then
|
|
9
|
+
case "$current_branch" in
|
|
10
|
+
haystack/*)
|
|
11
|
+
# Already a haystack branch, no reminder needed
|
|
12
|
+
;;
|
|
13
|
+
*)
|
|
14
|
+
# Check if this is an AI agent session (set by agent-context detector)
|
|
15
|
+
if [ -n "$HAYSTACK_AGENT_SESSION" ] || [ -n "$CLAUDE_CODE" ] || [ -n "$CODEX_CLI" ]; then
|
|
16
|
+
echo ""
|
|
17
|
+
echo "💡 Tip: Use 'haystack submit' to create PRs through the auto-merge queue."
|
|
18
|
+
echo " This enables automatic analysis and merge when approved."
|
|
19
|
+
echo ""
|
|
20
|
+
fi
|
|
21
|
+
;;
|
|
22
|
+
esac
|
|
23
|
+
fi
|
|
24
|
+
|
|
5
25
|
"$HOME/.haystack/bin/entire" hooks git pre-push "$1" || true
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
# Prepare Codebase for Verification
|
|
2
|
+
|
|
3
|
+
> **PRO Feature**: Verification Flow requires a Haystack PRO subscription to run cloud sandboxes. Free users can still prepare their codebase — it will be ready when they upgrade. See https://haystackeditor.com/pricing for details.
|
|
4
|
+
|
|
5
|
+
**Your job**: Make this codebase easy to verify by adding semantic identifiers that the verification system can target.
|
|
6
|
+
|
|
7
|
+
The verification Planner needs to find UI elements by selectors. Generic selectors like `div` or `.flex` are useless. Your job is to add meaningful identifiers throughout the codebase.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## What to Add
|
|
12
|
+
|
|
13
|
+
### 1. `aria-label` on Interactive Elements
|
|
14
|
+
|
|
15
|
+
Every clickable/interactive element should have an aria-label describing what it does:
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
// Before
|
|
19
|
+
<button onClick={onSave}>💾</button>
|
|
20
|
+
<button onClick={() => setOpen(true)}>
|
|
21
|
+
<MenuIcon />
|
|
22
|
+
</button>
|
|
23
|
+
|
|
24
|
+
// After
|
|
25
|
+
<button onClick={onSave} aria-label="Save document">💾</button>
|
|
26
|
+
<button onClick={() => setOpen(true)} aria-label="Open menu">
|
|
27
|
+
<MenuIcon />
|
|
28
|
+
</button>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Target elements:**
|
|
32
|
+
- Buttons (especially icon-only buttons)
|
|
33
|
+
- Links without descriptive text
|
|
34
|
+
- Toggle switches
|
|
35
|
+
- Dropdown triggers
|
|
36
|
+
- Modal open/close buttons
|
|
37
|
+
- Form submit buttons
|
|
38
|
+
|
|
39
|
+
### 2. `data-testid` on Key Sections
|
|
40
|
+
|
|
41
|
+
Major UI sections should have data-testid for easy targeting:
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
// Before
|
|
45
|
+
<div className="flex flex-col p-4">
|
|
46
|
+
<h1>Dashboard</h1>
|
|
47
|
+
{/* content */}
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
// After
|
|
51
|
+
<div className="flex flex-col p-4" data-testid="dashboard-container">
|
|
52
|
+
<h1>Dashboard</h1>
|
|
53
|
+
{/* content */}
|
|
54
|
+
</div>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Target sections:**
|
|
58
|
+
- Page containers (dashboard, settings, profile)
|
|
59
|
+
- Navigation bars/sidebars
|
|
60
|
+
- Modal/dialog content
|
|
61
|
+
- Form containers
|
|
62
|
+
- Data tables/lists
|
|
63
|
+
- Card components
|
|
64
|
+
- Loading states
|
|
65
|
+
- Error states
|
|
66
|
+
- Empty states
|
|
67
|
+
|
|
68
|
+
### 3. `role` Attributes for Semantic Structure
|
|
69
|
+
|
|
70
|
+
Add ARIA roles where HTML semantics aren't clear:
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
// Before
|
|
74
|
+
<div className="modal-overlay">
|
|
75
|
+
<div className="modal-content">
|
|
76
|
+
|
|
77
|
+
// After
|
|
78
|
+
<div className="modal-overlay" role="presentation">
|
|
79
|
+
<div className="modal-content" role="dialog" aria-modal="true">
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Common roles:**
|
|
83
|
+
- `role="dialog"` - Modals/dialogs
|
|
84
|
+
- `role="navigation"` - Nav sections
|
|
85
|
+
- `role="main"` - Main content area
|
|
86
|
+
- `role="alert"` - Error/success messages
|
|
87
|
+
- `role="status"` - Loading indicators
|
|
88
|
+
- `role="tablist"`, `role="tab"`, `role="tabpanel"` - Tab interfaces
|
|
89
|
+
|
|
90
|
+
### 4. State Indicators
|
|
91
|
+
|
|
92
|
+
Add attributes that indicate UI state:
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
// Before
|
|
96
|
+
<button onClick={toggle}>
|
|
97
|
+
{isOpen ? 'Close' : 'Open'}
|
|
98
|
+
</button>
|
|
99
|
+
|
|
100
|
+
// After
|
|
101
|
+
<button
|
|
102
|
+
onClick={toggle}
|
|
103
|
+
aria-expanded={isOpen}
|
|
104
|
+
aria-label={isOpen ? 'Close panel' : 'Open panel'}
|
|
105
|
+
>
|
|
106
|
+
{isOpen ? 'Close' : 'Open'}
|
|
107
|
+
</button>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**State attributes:**
|
|
111
|
+
- `aria-expanded` - Collapsible sections
|
|
112
|
+
- `aria-selected` - Selected items in lists
|
|
113
|
+
- `aria-checked` - Checkboxes/toggles
|
|
114
|
+
- `aria-disabled` - Disabled elements
|
|
115
|
+
- `aria-busy` - Loading states
|
|
116
|
+
- `data-state="loading|error|success"` - Custom states
|
|
117
|
+
|
|
118
|
+
### 5. Form Accessibility
|
|
119
|
+
|
|
120
|
+
Forms should have proper labels and descriptions:
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
// Before
|
|
124
|
+
<input type="email" placeholder="Email" />
|
|
125
|
+
<span className="text-red-500">{error}</span>
|
|
126
|
+
|
|
127
|
+
// After
|
|
128
|
+
<input
|
|
129
|
+
type="email"
|
|
130
|
+
placeholder="Email"
|
|
131
|
+
aria-label="Email address"
|
|
132
|
+
aria-describedby={error ? "email-error" : undefined}
|
|
133
|
+
aria-invalid={!!error}
|
|
134
|
+
/>
|
|
135
|
+
{error && <span id="email-error" role="alert" className="text-red-500">{error}</span>}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Step 1: Scan for Missing Identifiers
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# Find buttons without aria-label
|
|
144
|
+
grep -rn "<button" src/ --include="*.tsx" | grep -v "aria-label" | head -20
|
|
145
|
+
|
|
146
|
+
# Find icon-only buttons (likely missing labels)
|
|
147
|
+
grep -rn "<button.*Icon\|<button.*>.*</.*Icon>" src/ --include="*.tsx" | head -20
|
|
148
|
+
|
|
149
|
+
# Find modals/dialogs without role
|
|
150
|
+
grep -rn "modal\|dialog\|Modal\|Dialog" src/ --include="*.tsx" | grep -v "role=" | head -20
|
|
151
|
+
|
|
152
|
+
# Find forms without proper labeling
|
|
153
|
+
grep -rn "<input\|<select\|<textarea" src/ --include="*.tsx" | grep -v "aria-label\|id=" | head -20
|
|
154
|
+
|
|
155
|
+
# Find major components (likely need data-testid)
|
|
156
|
+
ls src/components/ src/pages/ 2>/dev/null
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Step 2: Prioritize by Impact
|
|
162
|
+
|
|
163
|
+
Focus on elements the verification system is most likely to need:
|
|
164
|
+
|
|
165
|
+
**High Priority (do first):**
|
|
166
|
+
1. Navigation elements (header, sidebar, menu buttons)
|
|
167
|
+
2. Primary actions (submit buttons, save buttons, CTAs)
|
|
168
|
+
3. Modal triggers and dialogs
|
|
169
|
+
4. Form inputs and submit buttons
|
|
170
|
+
5. Page-level containers
|
|
171
|
+
|
|
172
|
+
**Medium Priority:**
|
|
173
|
+
1. Toggle switches and checkboxes
|
|
174
|
+
2. Dropdown menus
|
|
175
|
+
3. Tab interfaces
|
|
176
|
+
4. Cards and list items
|
|
177
|
+
5. Loading/error states
|
|
178
|
+
|
|
179
|
+
**Lower Priority:**
|
|
180
|
+
1. Decorative elements
|
|
181
|
+
2. Static content sections
|
|
182
|
+
3. Footer links
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Step 3: Add Identifiers Systematically
|
|
187
|
+
|
|
188
|
+
Go component by component. For each component:
|
|
189
|
+
|
|
190
|
+
1. **Check the component's purpose** - What does it DO?
|
|
191
|
+
2. **Add aria-label** to interactive elements describing the ACTION
|
|
192
|
+
3. **Add data-testid** to the container if it's a major section
|
|
193
|
+
4. **Add role** if the semantic HTML isn't clear
|
|
194
|
+
5. **Add state attributes** if the component has dynamic states
|
|
195
|
+
|
|
196
|
+
### Naming Conventions
|
|
197
|
+
|
|
198
|
+
**aria-label**: Describe the action, not the element
|
|
199
|
+
- ✅ `aria-label="Close modal"`
|
|
200
|
+
- ✅ `aria-label="Submit contact form"`
|
|
201
|
+
- ❌ `aria-label="Button"`
|
|
202
|
+
- ❌ `aria-label="Click here"`
|
|
203
|
+
|
|
204
|
+
**data-testid**: Use kebab-case, describe the section
|
|
205
|
+
- ✅ `data-testid="user-profile-card"`
|
|
206
|
+
- ✅ `data-testid="search-results-list"`
|
|
207
|
+
- ❌ `data-testid="div1"`
|
|
208
|
+
- ❌ `data-testid="container"`
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## Step 4: Verify Coverage
|
|
213
|
+
|
|
214
|
+
After adding identifiers, check coverage:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
# Count aria-labels added
|
|
218
|
+
grep -r "aria-label" src/ --include="*.tsx" | wc -l
|
|
219
|
+
|
|
220
|
+
# Count data-testid added
|
|
221
|
+
grep -r "data-testid" src/ --include="*.tsx" | wc -l
|
|
222
|
+
|
|
223
|
+
# Count role attributes
|
|
224
|
+
grep -r "role=" src/ --include="*.tsx" | wc -l
|
|
225
|
+
|
|
226
|
+
# List all data-testid values (check for meaningful names)
|
|
227
|
+
grep -oh 'data-testid="[^"]*"' src/ -r --include="*.tsx" | sort -u
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Step 5: Commit
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
git add src/
|
|
236
|
+
git commit -m "Add accessibility attributes for verification
|
|
237
|
+
|
|
238
|
+
- Added aria-labels to interactive elements
|
|
239
|
+
- Added data-testid to major sections
|
|
240
|
+
- Added ARIA roles for semantic structure
|
|
241
|
+
- Added state indicators (aria-expanded, etc.)"
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Quick Reference: Common Patterns
|
|
247
|
+
|
|
248
|
+
### Icon Button
|
|
249
|
+
```tsx
|
|
250
|
+
<button onClick={onAction} aria-label="Descriptive action name">
|
|
251
|
+
<Icon />
|
|
252
|
+
</button>
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Modal
|
|
256
|
+
```tsx
|
|
257
|
+
<div role="dialog" aria-modal="true" aria-labelledby="modal-title" data-testid="settings-modal">
|
|
258
|
+
<h2 id="modal-title">Settings</h2>
|
|
259
|
+
<button onClick={onClose} aria-label="Close settings">×</button>
|
|
260
|
+
</div>
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Navigation
|
|
264
|
+
```tsx
|
|
265
|
+
<nav aria-label="Main navigation" data-testid="main-nav">
|
|
266
|
+
<a href="/dashboard" aria-current={isActive ? "page" : undefined}>Dashboard</a>
|
|
267
|
+
</nav>
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Toggle
|
|
271
|
+
```tsx
|
|
272
|
+
<button
|
|
273
|
+
onClick={toggle}
|
|
274
|
+
aria-pressed={isOn}
|
|
275
|
+
aria-label={`${isOn ? 'Disable' : 'Enable'} notifications`}
|
|
276
|
+
>
|
|
277
|
+
{isOn ? 'On' : 'Off'}
|
|
278
|
+
</button>
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Loading State
|
|
282
|
+
```tsx
|
|
283
|
+
<div data-testid="content-area" aria-busy={isLoading}>
|
|
284
|
+
{isLoading ? (
|
|
285
|
+
<div role="status" aria-label="Loading content">
|
|
286
|
+
<Spinner />
|
|
287
|
+
</div>
|
|
288
|
+
) : (
|
|
289
|
+
content
|
|
290
|
+
)}
|
|
291
|
+
</div>
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Form Field
|
|
295
|
+
```tsx
|
|
296
|
+
<div data-testid="email-field">
|
|
297
|
+
<label htmlFor="email">Email</label>
|
|
298
|
+
<input
|
|
299
|
+
id="email"
|
|
300
|
+
type="email"
|
|
301
|
+
aria-describedby={error ? "email-error" : "email-hint"}
|
|
302
|
+
aria-invalid={!!error}
|
|
303
|
+
/>
|
|
304
|
+
<span id="email-hint">We'll never share your email</span>
|
|
305
|
+
{error && <span id="email-error" role="alert">{error}</span>}
|
|
306
|
+
</div>
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Expandable Section
|
|
310
|
+
```tsx
|
|
311
|
+
<div data-testid="faq-section">
|
|
312
|
+
<button
|
|
313
|
+
onClick={() => setExpanded(!expanded)}
|
|
314
|
+
aria-expanded={expanded}
|
|
315
|
+
aria-controls="faq-content"
|
|
316
|
+
>
|
|
317
|
+
FAQ
|
|
318
|
+
</button>
|
|
319
|
+
<div id="faq-content" hidden={!expanded}>
|
|
320
|
+
{content}
|
|
321
|
+
</div>
|
|
322
|
+
</div>
|
|
323
|
+
```
|