@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.
Files changed (63) hide show
  1. package/README.md +93 -87
  2. package/dist/assets/hooks/llm-rules-template.md +21 -0
  3. package/dist/assets/hooks/package.json +2 -2
  4. package/dist/assets/hooks/scripts/pre-push.sh +20 -0
  5. package/dist/assets/skills/prepare-haystack.md +323 -0
  6. package/dist/assets/skills/secrets.md +164 -0
  7. package/dist/assets/skills/setup-external-sandbox.md +243 -0
  8. package/dist/assets/skills/setup-haystack.md +639 -0
  9. package/dist/assets/skills/submit.md +154 -0
  10. package/dist/assets/templates/CLAUDE.md.snippet +42 -0
  11. package/dist/assets/templates/haystack.yml +193 -0
  12. package/dist/commands/check-pending.d.ts +19 -0
  13. package/dist/commands/check-pending.js +217 -0
  14. package/dist/commands/config.d.ts +13 -21
  15. package/dist/commands/config.js +278 -92
  16. package/dist/commands/dismiss.d.ts +17 -0
  17. package/dist/commands/dismiss.js +201 -0
  18. package/dist/commands/init.js +25 -28
  19. package/dist/commands/install-session-hooks.d.ts +16 -0
  20. package/dist/commands/install-session-hooks.js +302 -0
  21. package/dist/commands/login.js +1 -1
  22. package/dist/commands/policy.d.ts +31 -0
  23. package/dist/commands/policy.js +365 -0
  24. package/dist/commands/pr-status.d.ts +16 -0
  25. package/dist/commands/pr-status.js +188 -0
  26. package/dist/commands/setup.d.ts +13 -0
  27. package/dist/commands/setup.js +496 -0
  28. package/dist/commands/skills.d.ts +2 -2
  29. package/dist/commands/skills.js +51 -186
  30. package/dist/commands/submit.d.ts +23 -0
  31. package/dist/commands/submit.js +456 -0
  32. package/dist/commands/triage.d.ts +16 -0
  33. package/dist/commands/triage.js +354 -0
  34. package/dist/index.d.ts +7 -0
  35. package/dist/index.js +344 -4
  36. package/dist/tools/detect.d.ts +50 -0
  37. package/dist/tools/detect.js +853 -0
  38. package/dist/tools/fixtures.d.ts +38 -0
  39. package/dist/tools/fixtures.js +199 -0
  40. package/dist/tools/setup.d.ts +43 -0
  41. package/dist/tools/setup.js +597 -0
  42. package/dist/triage/prompts.d.ts +31 -0
  43. package/dist/triage/prompts.js +296 -0
  44. package/dist/triage/runner.d.ts +21 -0
  45. package/dist/triage/runner.js +339 -0
  46. package/dist/triage/traces.d.ts +20 -0
  47. package/dist/triage/traces.js +305 -0
  48. package/dist/triage/types.d.ts +47 -0
  49. package/dist/triage/types.js +7 -0
  50. package/dist/types.d.ts +1387 -191
  51. package/dist/types.js +254 -2
  52. package/dist/utils/analysis-api.d.ts +108 -0
  53. package/dist/utils/analysis-api.js +194 -0
  54. package/dist/utils/config.js +1 -1
  55. package/dist/utils/git.d.ts +80 -0
  56. package/dist/utils/git.js +302 -0
  57. package/dist/utils/github-api.d.ts +83 -0
  58. package/dist/utils/github-api.js +266 -0
  59. package/dist/utils/pending-state.d.ts +40 -0
  60. package/dist/utils/pending-state.js +86 -0
  61. package/dist/utils/secrets.js +3 -3
  62. package/dist/utils/skill.js +257 -0
  63. package/package.json +11 -9
package/README.md CHANGED
@@ -1,68 +1,43 @@
1
1
  # @haystackeditor/cli
2
2
 
3
- Set up Haystack verification for your project. When PRs are opened, an AI agent spins up your app in a sandbox and verifies changes work correctly.
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
- npx @haystackeditor/cli init
8
+ npm install -g @haystackeditor/cli
9
+ haystack login
10
+ haystack setup
31
11
  ```
32
12
 
33
- This runs an interactive wizard that auto-detects your project and creates `.haystack.json`.
34
-
35
- ### Additional Skills
13
+ The `setup` command walks you through an interactive wizard:
36
14
 
37
- | Skill | Purpose |
38
- |-------|---------|
39
- | `/setup-haystack` | **Start here** - diagnoses project, creates config |
40
- | `/prepare-haystack` | Add aria-labels and data-testid for browser automation |
41
- | `/setup-haystack-secrets` | Configure API keys, LLM credentials, secrets |
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
- All commands can be run directly:
48
-
49
- ### `haystack skills`
26
+ ### `haystack setup`
50
27
 
51
- Install AI skills into your coding CLI (auto-detects Claude Code, Codex, Cursor):
28
+ Interactive onboarding wizard scan your repos and generate `.haystack.json`:
52
29
 
53
30
  ```bash
54
- npx @haystackeditor/cli skills install # Auto-detect CLI
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
- npx @haystackeditor/cli init # Interactive wizard
65
- npx @haystackeditor/cli init -y # Accept all defaults
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
- npx @haystackeditor/cli status
48
+ haystack status
74
49
  ```
75
50
 
76
51
  ### `haystack login`
77
52
 
78
- Authenticate with GitHub (required for secrets management):
53
+ Authenticate with GitHub (required for setup and secrets):
79
54
 
80
55
  ```bash
81
- npx @haystackeditor/cli login
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
- npx @haystackeditor/cli logout
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
- npx @haystackeditor/cli secrets list
72
+ haystack secrets list
98
73
 
99
74
  # Set a secret
100
- npx @haystackeditor/cli secrets set OPENAI_API_KEY sk-xxx
75
+ haystack secrets set OPENAI_API_KEY sk-xxx
101
76
 
102
77
  # Delete a secret
103
- npx @haystackeditor/cli secrets delete OPENAI_API_KEY
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
- npx @haystackeditor/cli secrets set API_KEY xxx --scope org --scope-id myorg
87
+ haystack secrets set API_KEY xxx --scope org --scope-id myorg
113
88
 
114
89
  # Repo-scoped (available only to this repo)
115
- npx @haystackeditor/cli secrets set API_KEY xxx --scope repo --scope-id owner/repo
90
+ haystack secrets set API_KEY xxx --scope repo --scope-id owner/repo
116
91
  ```
117
92
 
118
- ---
93
+ ### `haystack submit`
119
94
 
120
- ## Configuration
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
- The `init` command creates `.haystack.json`:
123
-
124
- ```yaml
125
- version: "1"
126
- name: my-app
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
- ### Customizing After Init
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
- | If your app has... | Add this |
146
- |-------------------|----------|
147
- | Login/authentication | Auth bypass env var in `dev_server.env` |
148
- | Key user journeys | Flows describing what to verify |
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
- Use `/setup-haystack` in Claude Code for AI-assisted configuration of flows and fixtures.
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 `npx @haystackeditor/cli init` or use `/setup-haystack` in Claude Code
158
- 2. Commit the generated `.haystack.json`
159
- 3. Install the [Haystack GitHub App](https://haystackeditor.com/github-app)
160
- 4. When PRs are opened, Haystack's AI agent:
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": "^0.22.4",
9
- "tree-sitter-typescript": "^0.23.2"
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
+ ```