qaa-agent 1.7.4 → 1.8.1

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/bin/install.cjs CHANGED
@@ -1,212 +1,253 @@
1
- #!/usr/bin/env node
2
- /**
3
- * QAA - QA Automation Agent Installer
4
- * Run with: npx qaa-agent
5
- */
6
-
7
- const fs = require('fs');
8
- const path = require('path');
9
- const readline = require('readline');
10
-
11
- const VERSION = require('../package.json').version;
12
- const ROOT = path.resolve(__dirname, '..');
13
- const HOME = process.env.HOME || process.env.USERPROFILE;
14
-
15
- // Runtime configs
16
- const RUNTIMES = {
17
- '1': { name: 'Claude Code', dir: path.join(HOME, '.claude') },
18
- '2': { name: 'OpenCode', dir: path.join(HOME, '.config', 'opencode') },
19
- };
20
-
21
- function ask(question, defaultVal) {
22
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
23
- return new Promise(resolve => {
24
- rl.question(question, answer => {
25
- rl.close();
26
- resolve(answer.trim() || defaultVal);
27
- });
28
- });
29
- }
30
-
31
- function copyDir(src, dest) {
32
- if (!fs.existsSync(src)) return 0;
33
- fs.mkdirSync(dest, { recursive: true });
34
- let count = 0;
35
- for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
36
- const srcPath = path.join(src, entry.name);
37
- const destPath = path.join(dest, entry.name);
38
- if (entry.isDirectory()) {
39
- count += copyDir(srcPath, destPath);
40
- } else {
41
- fs.copyFileSync(srcPath, destPath);
42
- count++;
43
- }
44
- }
45
- return count;
46
- }
47
-
48
- function copyFile(src, dest) {
49
- if (!fs.existsSync(src)) return false;
50
- fs.mkdirSync(path.dirname(dest), { recursive: true });
51
- fs.copyFileSync(src, dest);
52
- return true;
53
- }
54
-
55
- function countEntries(dir, type) {
56
- if (!fs.existsSync(dir)) return 0;
57
- const entries = fs.readdirSync(dir, { withFileTypes: true });
58
- if (type === 'dirs') return entries.filter(e => e.isDirectory()).length;
59
- return entries.filter(e => e.isFile()).length;
60
- }
61
-
62
- function ok(msg) { console.log(` \x1b[32m✓\x1b[0m ${msg}`); }
63
- function info(msg) { console.log(` ${msg}`); }
64
-
65
- async function main() {
66
- console.log('');
67
- console.log(' \x1b[36m ██████╗ █████╗ █████╗ \x1b[0m');
68
- console.log(' \x1b[36m██╔═══██╗██╔══██╗██╔══██╗\x1b[0m');
69
- console.log(' \x1b[36m██║ ██║███████║███████║\x1b[0m');
70
- console.log(' \x1b[36m██║▄▄ ██║██╔══██║██╔══██║\x1b[0m');
71
- console.log(' \x1b[36m╚██████╔╝██║ ██║██║ ██║\x1b[0m');
72
- console.log(' \x1b[36m ╚══▀▀═╝ ╚═╝ ╚═╝╚═╝ ╚═╝\x1b[0m');
73
- console.log('');
74
- console.log(` \x1b[1mQA Automation Agent\x1b[0m v${VERSION}`);
75
- console.log(' Multi-agent QA pipeline for Claude Code.');
76
- console.log(' Analyzes repos, generates tests, validates, and creates PRs.');
77
- console.log('');
78
-
79
- // Ask runtime
80
- console.log(' Which runtime would you like to install for?');
81
- console.log('');
82
- console.log(' 1) Claude Code (~/.claude)');
83
- console.log(' 2) OpenCode (~/.config/opencode)');
84
- console.log('');
85
- const runtimeChoice = await ask(' Choice [1]: ', '1');
86
- const runtime = RUNTIMES[runtimeChoice] || RUNTIMES['1'];
87
-
88
- // Ask scope
89
- console.log('');
90
- console.log(' Where would you like to install?');
91
- console.log('');
92
- console.log(` 1) Global (~/${path.relative(HOME, runtime.dir)}) - available in all projects`);
93
- console.log(' 2) Local (./.claude) - this project only');
94
- console.log('');
95
- const scopeChoice = await ask(' Choice [1]: ', '1');
96
- const isGlobal = scopeChoice !== '2';
97
- const baseDir = isGlobal ? runtime.dir : path.join(process.cwd(), '.claude');
98
- const qaaDir = isGlobal ? path.join(runtime.dir, 'qaa') : path.join(process.cwd(), '.claude', 'qaa');
99
-
100
- console.log('');
101
- console.log(` Installing for ${runtime.name} to ${isGlobal ? '~/' + path.relative(HOME, runtime.dir) : './.claude'}`);
102
- console.log('');
103
-
104
- // Install commands (from commands/ in package root to ~/.claude/commands/)
105
- const commandsSrc = path.join(ROOT, 'commands');
106
- const commandsDest = path.join(baseDir, 'commands');
107
- const cmdCount = copyDir(commandsSrc, commandsDest);
108
- ok(`Installed ${cmdCount} slash commands`);
109
-
110
- // Install skills (from skills/ in package root to ~/.claude/skills/)
111
- const skillsSrc = path.join(ROOT, 'skills');
112
- const skillsDest = path.join(baseDir, 'skills');
113
- const skillCount = copyDir(skillsSrc, skillsDest);
114
- const skillDirCount = countEntries(skillsSrc, 'dirs');
115
- ok(`Installed ${skillDirCount} skills (${skillCount} files)`);
116
-
117
- // Install workflows
118
- const workflowsSrc = path.join(ROOT, 'workflows');
119
- const workflowsDest = path.join(qaaDir, 'workflows');
120
- const wfCount = copyDir(workflowsSrc, workflowsDest);
121
- ok(`Installed ${wfCount} workflows`);
122
-
123
- // Install agents
124
- const agentsSrc = path.join(ROOT, 'agents');
125
- const agentsDest = path.join(qaaDir, 'agents');
126
- const agentCount = copyDir(agentsSrc, agentsDest);
127
- ok(`Installed ${agentCount} agent definitions`);
128
-
129
- // Install templates
130
- const templatesSrc = path.join(ROOT, 'templates');
131
- const templatesDest = path.join(qaaDir, 'templates');
132
- const templateCount = copyDir(templatesSrc, templatesDest);
133
- ok(`Installed ${templateCount} templates`);
134
-
135
- // Install bin
136
- const binSrc = path.join(ROOT, 'bin');
137
- const binDest = path.join(qaaDir, 'bin');
138
- const binCount = copyDir(binSrc, binDest);
139
- try { fs.unlinkSync(path.join(binDest, 'install.cjs')); } catch {}
140
- ok(`Installed CLI tooling`);
141
-
142
- // Install CLAUDE.md
143
- copyFile(path.join(ROOT, 'CLAUDE.md'), path.join(qaaDir, 'CLAUDE.md'));
144
- ok('Installed QA standards (CLAUDE.md)');
145
-
146
- // Install .mcp.json (Playwright MCP server config)
147
- const mcpSrc = path.join(ROOT, '.mcp.json');
148
- if (fs.existsSync(mcpSrc)) {
149
- // Copy to qaa dir for reference
150
- copyFile(mcpSrc, path.join(qaaDir, '.mcp.json'));
151
-
152
- // Merge MCP servers into ~/.claude.json (user-scope) so they're available in ALL projects
153
- // Note: ~/.claude/.mcp.json is project-scope for ~/.claude/ only — NOT global
154
- const userConfigPath = path.join(HOME, '.claude.json');
155
- let userConfig = {};
156
- if (fs.existsSync(userConfigPath)) {
157
- try { userConfig = JSON.parse(fs.readFileSync(userConfigPath, 'utf8')); } catch {}
158
- }
159
- userConfig.mcpServers = userConfig.mcpServers || {};
160
- const qaaMcp = JSON.parse(fs.readFileSync(mcpSrc, 'utf8'));
161
- Object.assign(userConfig.mcpServers, qaaMcp.mcpServers);
162
- fs.writeFileSync(userConfigPath, JSON.stringify(userConfig, null, 2));
163
- ok('Installed Playwright MCP server config (user-scope available in all projects)');
164
- }
165
-
166
- // Write version
167
- fs.writeFileSync(path.join(qaaDir, 'VERSION'), VERSION);
168
- ok(`Wrote VERSION (${VERSION})`);
169
-
170
- // Merge settings (from settings.json in package root)
171
- const settingsSrc = path.join(ROOT, 'settings.json');
172
- const settingsDest = path.join(baseDir, 'settings.json');
173
- if (fs.existsSync(settingsSrc)) {
174
- let existing = {};
175
- if (fs.existsSync(settingsDest)) {
176
- try { existing = JSON.parse(fs.readFileSync(settingsDest, 'utf8')); } catch {}
177
- }
178
- const qaaSettings = JSON.parse(fs.readFileSync(settingsSrc, 'utf8'));
179
- if (qaaSettings.permissions) {
180
- existing.permissions = existing.permissions || {};
181
- existing.permissions.allow = [...new Set([
182
- ...(existing.permissions.allow || []),
183
- ...(qaaSettings.permissions.allow || [])
184
- ])];
185
- }
186
- fs.writeFileSync(settingsDest, JSON.stringify(existing, null, 2));
187
- ok('Merged permissions into settings.json');
188
- }
189
-
190
- // Done
191
- const total = cmdCount + skillCount + agentCount + templateCount + wfCount + binCount;
192
- console.log('');
193
- console.log(` \x1b[32m✓ Done!\x1b[0m Installed ${total} files.`);
194
- console.log('');
195
- console.log(' Open Claude Code in any project and run:');
196
- console.log('');
197
- console.log(' \x1b[1m/qa-start\x1b[0m Full QA pipeline (multi-agent)');
198
- console.log(' \x1b[1m/qa-map\x1b[0m Codebase map + analysis');
199
- console.log(' \x1b[1m/qa-create-test\x1b[0m Tests for a feature/ticket');
200
- console.log(' \x1b[1m/qa-audit\x1b[0m Audit existing tests');
201
- console.log(' \x1b[1m/qa-fix\x1b[0m Fix broken tests');
202
- console.log(' \x1b[1m/qa-testid\x1b[0m Inject data-testid attributes');
203
- console.log(' \x1b[1m/qa-pr\x1b[0m Create QA pull request');
204
- console.log('');
205
- console.log(` ${cmdCount} commands + ${skillDirCount} skills + ${agentCount} agents ready.`);
206
- console.log('');
207
- }
208
-
209
- main().catch(err => {
210
- console.error('Installation failed:', err.message);
211
- process.exit(1);
212
- });
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * QAA Agent Installer
5
+ *
6
+ * Installs QAA (QA Automation Agent) into the user's Claude Code environment.
7
+ *
8
+ * What it does:
9
+ * 1. Copies agents, commands, skills, templates, workflows, docs, bin, and config files
10
+ * to the chosen install directory (global ~/.claude/qaa or local ./.claude/qaa)
11
+ * 2. Registers Playwright MCP and Context7 MCP as global MCP servers
12
+ * 3. Merges required permissions into Claude Code settings.json
13
+ *
14
+ * Usage:
15
+ * npx qaa-agent
16
+ */
17
+
18
+ const fs = require('fs');
19
+ const path = require('path');
20
+ const readline = require('readline');
21
+ const { execSync } = require('child_process');
22
+
23
+ // ── Helpers ──────────────────────────────────────────────────────────────────
24
+
25
+ function log(msg) { console.log(` ${msg}`); }
26
+ function success(msg) { console.log(` ✓ ${msg}`); }
27
+ function warn(msg) { console.log(` ⚠ ${msg}`); }
28
+ function fail(msg) { console.error(` ✗ ${msg}`); }
29
+
30
+ function ask(question) {
31
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
32
+ return new Promise(resolve => {
33
+ rl.question(` ${question} `, answer => {
34
+ rl.close();
35
+ resolve(answer.trim());
36
+ });
37
+ });
38
+ }
39
+
40
+ function copyDirRecursive(src, dest) {
41
+ if (!fs.existsSync(src)) return 0;
42
+ fs.mkdirSync(dest, { recursive: true });
43
+ let count = 0;
44
+ const entries = fs.readdirSync(src, { withFileTypes: true });
45
+ for (const entry of entries) {
46
+ const srcPath = path.join(src, entry.name);
47
+ const destPath = path.join(dest, entry.name);
48
+ if (entry.isDirectory()) {
49
+ count += copyDirRecursive(srcPath, destPath);
50
+ } else {
51
+ fs.copyFileSync(srcPath, destPath);
52
+ count++;
53
+ }
54
+ }
55
+ return count;
56
+ }
57
+
58
+ function deepMerge(target, source) {
59
+ for (const key of Object.keys(source)) {
60
+ if (
61
+ source[key] && typeof source[key] === 'object' && !Array.isArray(source[key]) &&
62
+ target[key] && typeof target[key] === 'object' && !Array.isArray(target[key])
63
+ ) {
64
+ deepMerge(target[key], source[key]);
65
+ } else if (Array.isArray(source[key]) && Array.isArray(target[key])) {
66
+ // Merge arrays without duplicates
67
+ const merged = [...new Set([...target[key], ...source[key]])];
68
+ target[key] = merged;
69
+ } else {
70
+ target[key] = source[key];
71
+ }
72
+ }
73
+ return target;
74
+ }
75
+
76
+ // ── MCP Registration ─────────────────────────────────────────────────────────
77
+
78
+ function registerMcpServers(claudeJsonPath) {
79
+ const mcpServers = {
80
+ playwright: {
81
+ command: 'npx',
82
+ args: ['@playwright/mcp@latest']
83
+ },
84
+ context7: {
85
+ command: 'npx',
86
+ args: ['-y', '@upstash/context7-mcp@latest']
87
+ }
88
+ };
89
+
90
+ let config = {};
91
+ if (fs.existsSync(claudeJsonPath)) {
92
+ try {
93
+ config = JSON.parse(fs.readFileSync(claudeJsonPath, 'utf-8'));
94
+ } catch {
95
+ config = {};
96
+ }
97
+ }
98
+
99
+ if (!config.mcpServers) config.mcpServers = {};
100
+
101
+ let added = [];
102
+ for (const [name, serverConfig] of Object.entries(mcpServers)) {
103
+ if (!config.mcpServers[name]) {
104
+ config.mcpServers[name] = serverConfig;
105
+ added.push(name);
106
+ }
107
+ }
108
+
109
+ fs.writeFileSync(claudeJsonPath, JSON.stringify(config, null, 2) + '\n');
110
+ return added;
111
+ }
112
+
113
+ // ── Settings Merge ───────────────────────────────────────────────────────────
114
+
115
+ function mergeSettings(installDir, packageDir) {
116
+ const srcSettings = path.join(packageDir, 'settings.json');
117
+ if (!fs.existsSync(srcSettings)) return false;
118
+
119
+ const claudeDir = path.dirname(installDir);
120
+ const destSettings = path.join(claudeDir, 'settings.json');
121
+
122
+ const source = JSON.parse(fs.readFileSync(srcSettings, 'utf-8'));
123
+
124
+ let target = {};
125
+ if (fs.existsSync(destSettings)) {
126
+ try {
127
+ target = JSON.parse(fs.readFileSync(destSettings, 'utf-8'));
128
+ } catch {
129
+ target = {};
130
+ }
131
+ }
132
+
133
+ deepMerge(target, source);
134
+ fs.writeFileSync(destSettings, JSON.stringify(target, null, 2) + '\n');
135
+ return true;
136
+ }
137
+
138
+ // ── Main ─────────────────────────────────────────────────────────────────────
139
+
140
+ async function main() {
141
+ console.log('');
142
+ console.log(' ╔═══════════════════════════════════════╗');
143
+ console.log(' ║ QAA QA Automation Agent Installer ║');
144
+ console.log(' ╚═══════════════════════════════════════╝');
145
+ console.log('');
146
+
147
+ // Determine package root (where the npm package files are)
148
+ const packageDir = path.resolve(__dirname, '..');
149
+
150
+ // Check that package files exist
151
+ const requiredDirs = ['agents', 'commands', 'skills'];
152
+ const missing = requiredDirs.filter(d => !fs.existsSync(path.join(packageDir, d)));
153
+ if (missing.length > 0) {
154
+ fail(`Package incomplete missing: ${missing.join(', ')}`);
155
+ process.exit(1);
156
+ }
157
+
158
+ // Ask install scope
159
+ console.log(' Install scope:');
160
+ console.log(' 1) Global — ~/.claude/qaa (available in all projects)');
161
+ console.log(' 2) Local — ./.claude/qaa (this project only)');
162
+ console.log('');
163
+ const scopeChoice = await ask('Choose [1/2] (default: 1):');
164
+ const isGlobal = scopeChoice !== '2';
165
+
166
+ const homeDir = process.env.HOME || process.env.USERPROFILE;
167
+ const claudeDir = isGlobal
168
+ ? path.join(homeDir, '.claude')
169
+ : path.join(process.cwd(), '.claude');
170
+ const installDir = path.join(claudeDir, 'qaa');
171
+
172
+ // Check for existing installation
173
+ if (fs.existsSync(installDir)) {
174
+ const overwrite = await ask('QAA already installed at this location. Overwrite? [y/N]:');
175
+ if (overwrite.toLowerCase() !== 'y') {
176
+ log('Installation cancelled.');
177
+ process.exit(0);
178
+ }
179
+ }
180
+
181
+ console.log('');
182
+ log(`Installing to: ${installDir}`);
183
+ console.log('');
184
+
185
+ // ── Step 1: Copy files ──────────────────────────────────────────────────
186
+
187
+ const dirsToCopy = ['agents', 'commands', 'skills', 'templates', 'workflows', 'docs', 'bin'];
188
+ const filesToCopy = ['CLAUDE.md', 'CHANGELOG.md', '.mcp.json', 'package.json'];
189
+
190
+ let totalFiles = 0;
191
+
192
+ for (const dir of dirsToCopy) {
193
+ const src = path.join(packageDir, dir);
194
+ const dest = path.join(installDir, dir);
195
+ if (fs.existsSync(src)) {
196
+ const count = copyDirRecursive(src, dest);
197
+ success(`${dir}/ ${count} files`);
198
+ totalFiles += count;
199
+ }
200
+ }
201
+
202
+ for (const file of filesToCopy) {
203
+ const src = path.join(packageDir, file);
204
+ const dest = path.join(installDir, file);
205
+ if (fs.existsSync(src)) {
206
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
207
+ fs.copyFileSync(src, dest);
208
+ success(file);
209
+ totalFiles++;
210
+ }
211
+ }
212
+
213
+ console.log('');
214
+
215
+ // ── Step 2: Register MCP servers ────────────────────────────────────────
216
+
217
+ const claudeJsonPath = path.join(homeDir, '.claude.json');
218
+ const addedMcps = registerMcpServers(claudeJsonPath);
219
+
220
+ if (addedMcps.length > 0) {
221
+ success(`MCP servers registered: ${addedMcps.join(', ')} → ${claudeJsonPath}`);
222
+ } else {
223
+ success('MCP servers already configured (playwright, context7)');
224
+ }
225
+
226
+ // ── Step 3: Merge settings ──────────────────────────────────────────────
227
+
228
+ const settingsMerged = mergeSettings(installDir, packageDir);
229
+ if (settingsMerged) {
230
+ success('Permissions merged into settings.json');
231
+ }
232
+
233
+ // ── Done ────────────────────────────────────────────────────────────────
234
+
235
+ console.log('');
236
+ console.log(' ╔═══════════════════════════════════════╗');
237
+ console.log(' ║ Installation complete! ║');
238
+ console.log(' ╚═══════════════════════════════════════╝');
239
+ console.log('');
240
+ log(`${totalFiles} files installed to ${installDir}`);
241
+ log('MCP servers: playwright, context7');
242
+ log('');
243
+ log('Restart Claude Code, then run any QAA command:');
244
+ log(' /qa-start --dev-repo ./your-project');
245
+ log(' /qa-create-test login');
246
+ log(' /qa-map');
247
+ console.log('');
248
+ }
249
+
250
+ main().catch(err => {
251
+ fail(err.message);
252
+ process.exit(1);
253
+ });
@@ -42,6 +42,7 @@ else:
42
42
  Scores across 6 dimensions: Locator Quality (20%), Assertion Specificity (20%), POM Compliance (15%), Test Coverage (20%), Naming Convention (15%), Test Data Management (10%).
43
43
 
44
44
  1. Read `CLAUDE.md` — quality gates, locator tiers, assertion rules, POM rules, naming conventions.
45
+ 1b. Read `~/.claude/qaa/MY_PREFERENCES.md` if it exists — user/company preferences override CLAUDE.md defaults.
45
46
  2. Invoke validator agent in audit mode:
46
47
 
47
48
  Task(
@@ -50,6 +51,8 @@ Task(
50
51
  <execution_context>@agents/qaa-validator.md</execution_context>
51
52
  <files_to_read>
52
53
  - CLAUDE.md
54
+ - ~/.claude/qaa/MY_PREFERENCES.md (if exists)
55
+ - .qa-output/locators/LOCATOR_REGISTRY.md (if exists)
53
56
  </files_to_read>
54
57
  <parameters>
55
58
  user_input: $ARGUMENTS
@@ -68,6 +71,7 @@ Task(
68
71
  Analyze test distribution against the ideal testing pyramid from CLAUDE.md (Unit 60-70%, Integration 10-15%, API 20-25%, E2E 3-5%). Compares actual percentages to targets and produces an action plan.
69
72
 
70
73
  1. Read `CLAUDE.md` — testing pyramid target percentages.
74
+ 1b. Read `~/.claude/qaa/MY_PREFERENCES.md` if it exists — user/company preferences override CLAUDE.md defaults.
71
75
  2. Invoke analyzer agent for pyramid analysis:
72
76
 
73
77
  Task(
@@ -76,6 +80,7 @@ Task(
76
80
  <execution_context>@agents/qaa-analyzer.md</execution_context>
77
81
  <files_to_read>
78
82
  - CLAUDE.md
83
+ - ~/.claude/qaa/MY_PREFERENCES.md (if exists)
79
84
  </files_to_read>
80
85
  <parameters>
81
86
  user_input: $ARGUMENTS
@@ -98,6 +103,7 @@ Generate a summary report of current QA status. Adapts detail level to audience.
98
103
  - `client` — coverage summary, confidence level, test pass rates, risk mitigation status
99
104
 
100
105
  1. Read `CLAUDE.md` — testing pyramid targets, quality gates.
106
+ 1b. Read `~/.claude/qaa/MY_PREFERENCES.md` if it exists — user/company preferences override CLAUDE.md defaults.
101
107
  2. Invoke analyzer agent for status reporting:
102
108
 
103
109
  Task(
@@ -106,6 +112,7 @@ Task(
106
112
  <execution_context>@agents/qaa-analyzer.md</execution_context>
107
113
  <files_to_read>
108
114
  - CLAUDE.md
115
+ - ~/.claude/qaa/MY_PREFERENCES.md (if exists)
109
116
  </files_to_read>
110
117
  <parameters>
111
118
  user_input: $ARGUMENTS
@@ -81,6 +81,7 @@ App URL: {url or "auto-detect"}
81
81
  ### FROM CODE MODE
82
82
 
83
83
  1. Read `CLAUDE.md` — POM rules, locator tiers, assertion rules, naming conventions, quality gates.
84
+ 1b. Read `~/.claude/qaa/MY_PREFERENCES.md` if it exists — user's personal QA preferences override CLAUDE.md defaults when there is a conflict.
84
85
  2. Read existing analysis artifacts if available:
85
86
  - `.qa-output/QA_ANALYSIS.md` — architecture context
86
87
  - `.qa-output/TEST_INVENTORY.md` — pre-defined test cases for this feature
@@ -127,6 +128,7 @@ Task(
127
128
  <execution_context>@agents/qaa-executor.md</execution_context>
128
129
  <files_to_read>
129
130
  - CLAUDE.md
131
+ - ~/.claude/qaa/MY_PREFERENCES.md (if exists)
130
132
  - .qa-output/locators/LOCATOR_REGISTRY.md (if exists)
131
133
  - .qa-output/locators/{feature}.locators.md (if exists)
132
134
  - .qa-output/codebase/CODE_PATTERNS.md (if exists)
@@ -151,6 +153,8 @@ Task(
151
153
  <execution_context>@agents/qaa-e2e-runner.md</execution_context>
152
154
  <files_to_read>
153
155
  - CLAUDE.md
156
+ - ~/.claude/qaa/MY_PREFERENCES.md (if exists)
157
+ - .qa-output/locators/LOCATOR_REGISTRY.md (if exists)
154
158
  - {generated E2E test files from executor return}
155
159
  - {generated POM files from executor return}
156
160
  </files_to_read>
@@ -210,6 +214,7 @@ Task(
210
214
  <execution_context>@agents/qaa-validator.md</execution_context>
211
215
  <files_to_read>
212
216
  - CLAUDE.md
217
+ - ~/.claude/qaa/MY_PREFERENCES.md (if exists)
213
218
  </files_to_read>
214
219
  <parameters>
215
220
  user_input: $ARGUMENTS
@@ -228,6 +233,7 @@ Task(
228
233
  <execution_context>@agents/qaa-executor.md</execution_context>
229
234
  <files_to_read>
230
235
  - CLAUDE.md
236
+ - ~/.claude/qaa/MY_PREFERENCES.md (if exists)
231
237
  - .qa-output/QA_AUDIT_REPORT.md
232
238
  - .qa-output/locators/LOCATOR_REGISTRY.md (if exists)
233
239
  </files_to_read>
@@ -262,6 +268,7 @@ Task(
262
268
  <execution_context>@agents/qaa-executor.md</execution_context>
263
269
  <files_to_read>
264
270
  - CLAUDE.md
271
+ - ~/.claude/qaa/MY_PREFERENCES.md (if exists)
265
272
  - .qa-output/locators/LOCATOR_REGISTRY.md (if exists)
266
273
  </files_to_read>
267
274
  <parameters>
@@ -286,3 +293,26 @@ Task(
286
293
  - Every POM extends BasePage
287
294
 
288
295
  $ARGUMENTS
296
+
297
+ ## MANDATORY verification — run ALL commands below, no exceptions, no skipping
298
+
299
+ Before returning control, copy-paste and run this ENTIRE block. Do NOT decide which commands "apply" — run all of them every time. The output confirms what happened; you do not get to assume the answer.
300
+
301
+ ```bash
302
+ echo "=== CHECKLIST START ==="
303
+ echo "1. Locator Registry:"
304
+ ls .qa-output/locators/ 2>/dev/null || echo "NO_LOCATORS_FOUND"
305
+ echo "2. MY_PREFERENCES.md:"
306
+ cat ~/.claude/qaa/MY_PREFERENCES.md 2>/dev/null || echo "FILE_NOT_FOUND"
307
+ echo "3. Generated test files:"
308
+ find tests/ pages/ fixtures/ -type f 2>/dev/null | head -20 || echo "NO_TEST_FILES_FOUND"
309
+ echo "4. MCP evidence (if browser was used):"
310
+ ls .qa-output/mcp-evidence/ 2>/dev/null || echo "NO_MCP_EVIDENCE"
311
+ echo "=== CHECKLIST END ==="
312
+ ```
313
+
314
+ **Rules:**
315
+ - Run the block AS-IS. Do not modify it. Do not split it. Do not skip lines.
316
+ - If any output shows a problem (NO_LOCATORS when MCP was used, NO_TEST_FILES after generation), fix it before returning.
317
+ - If output shows expected "not found" results (e.g., NO_MCP_EVIDENCE when no browser was used), that is fine — the point is you RAN the command instead of assuming the answer.
318
+ - Do NOT mark this task as complete until the block has been executed and you have read every line of output.
@@ -335,6 +335,8 @@ Task(
335
335
  <execution_context>@agents/qaa-e2e-runner.md</execution_context>
336
336
  <files_to_read>
337
337
  - CLAUDE.md
338
+ - ~/.claude/qaa/MY_PREFERENCES.md (if exists)
339
+ - .qa-output/locators/LOCATOR_REGISTRY.md (if exists)
338
340
  - {E2E test files from validated directory}
339
341
  - {POM files from validated directory}
340
342
  </files_to_read>
@@ -364,6 +366,8 @@ Task(
364
366
  <execution_context>@agents/qaa-bug-detective.md</execution_context>
365
367
  <files_to_read>
366
368
  - CLAUDE.md
369
+ - ~/.claude/qaa/MY_PREFERENCES.md (if exists)
370
+ - .qa-output/locators/LOCATOR_REGISTRY.md (if exists)
367
371
  </files_to_read>
368
372
  <parameters>
369
373
  user_input: $ARGUMENTS
@@ -99,6 +99,7 @@ Task(
99
99
  <execution_context>@agents/qaa-scanner.md</execution_context>
100
100
  <files_to_read>
101
101
  - CLAUDE.md
102
+ - ~/.claude/qaa/MY_PREFERENCES.md (if exists)
102
103
  </files_to_read>
103
104
  <parameters>
104
105
  user_input: $ARGUMENTS
@@ -114,6 +115,7 @@ Task(
114
115
  <execution_context>@agents/qaa-analyzer.md</execution_context>
115
116
  <files_to_read>
116
117
  - CLAUDE.md
118
+ - ~/.claude/qaa/MY_PREFERENCES.md (if exists)
117
119
  - .qa-output/SCAN_MANIFEST.md
118
120
  - .qa-output/codebase/TESTABILITY.md (if exists)
119
121
  - .qa-output/codebase/RISK_MAP.md (if exists)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qaa-agent",
3
- "version": "1.7.4",
3
+ "version": "1.8.1",
4
4
  "description": "QA Automation Agent for Claude Code — multi-agent pipeline that analyzes repos, generates tests, validates, and creates PRs",
5
5
  "bin": {
6
6
  "qaa-agent": "./bin/install.cjs"
@@ -22,7 +22,8 @@
22
22
  "author": "Backhaus7997",
23
23
  "license": "MIT",
24
24
  "dependencies": {
25
- "@playwright/mcp": "latest"
25
+ "@playwright/mcp": "latest",
26
+ "@upstash/context7-mcp": "latest"
26
27
  },
27
28
  "files": [
28
29
  "bin/",