qaa-agent 1.8.0 → 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/.mcp.json +4 -0
- package/CHANGELOG.md +15 -0
- package/README.md +26 -44
- package/bin/install.cjs +253 -0
- package/package.json +3 -2
package/.mcp.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,21 @@
|
|
|
3
3
|
|
|
4
4
|
All notable changes to QAA (QA Automation Agent) are documented here.
|
|
5
5
|
|
|
6
|
+
## [1.8.1] - 2026-04-16
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- **Context7 MCP integration** — `@upstash/context7-mcp` is now bundled alongside Playwright MCP. The installer registers both MCP servers in the user-scope config (`~/.claude.json`) so they're available in every project on the machine, not just in the QAA repo. Context7 gives every QAA agent on-demand access to up-to-date library documentation (Playwright, Cypress, Jest, Vitest, pytest, and any other framework), keeping generated tests aligned with current APIs instead of outdated training data.
|
|
11
|
+
- **`bin/install.cjs` installer script** — the file was referenced in `package.json` but didn't actually exist on npm, causing `npx qaa-agent` to fail silently (`No bin file found at bin/install.cjs`). The installer now performs three steps on every run: (1) copies agents, commands, skills, templates, workflows, docs, and config files into the chosen scope (`~/.claude/qaa` for global, `./.claude/qaa` for local), (2) registers both MCP servers in `~/.claude.json` with idempotency — existing entries are not duplicated, and (3) deep-merges the QAA permissions into the user's `settings.json` without overwriting their existing settings.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- **MCP registration is now user-scope by default** — previously MCPs were defined only in the project-level `.mcp.json`, which meant they only activated when the user opened the QAA repo itself. They now register in `~/.claude.json`, making them available in every Claude Code project on the user's machine. The project-level `.mcp.json` is kept for QAA development purposes but is no longer the source of truth for end users.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **Silent `npx qaa-agent` failure** — users who installed QAA via npm before this release did not get Playwright or Context7 MCPs registered because the installer script was missing from the published package. Publishing 1.8.1 restores the expected behavior: a single `npx qaa-agent` command copies all files and registers both MCPs globally.
|
|
20
|
+
|
|
6
21
|
## [1.8.0] - 2026-04-13
|
|
7
22
|
|
|
8
23
|
### Added
|
package/README.md
CHANGED
|
@@ -43,7 +43,9 @@ npx qaa-agent
|
|
|
43
43
|
The interactive installer:
|
|
44
44
|
|
|
45
45
|
1. Copies agents, commands, skills, templates, and workflows into your runtime directory
|
|
46
|
-
2.
|
|
46
|
+
2. Registers **two MCP servers** in your user-scope config (`~/.claude.json`) so they're available in **all projects**:
|
|
47
|
+
- [Playwright MCP](https://www.npmjs.com/package/@playwright/mcp) — live browser control for E2E tests and locator extraction
|
|
48
|
+
- [Context7 MCP](https://www.npmjs.com/package/@upstash/context7-mcp) — up-to-date library documentation on demand
|
|
47
49
|
3. Merges required permissions into `settings.json`
|
|
48
50
|
|
|
49
51
|
**Supported runtimes:** Claude Code, OpenCode
|
|
@@ -55,48 +57,34 @@ The interactive installer:
|
|
|
55
57
|
- [Node.js](https://nodejs.org/) 18+
|
|
56
58
|
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code) installed
|
|
57
59
|
|
|
58
|
-
###
|
|
60
|
+
### Bundled MCP servers
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
Both MCP servers are **registered automatically** in `~/.claude.json` when you run `npx qaa-agent`. No manual setup required — once installed, they're available in every Claude Code project on your machine.
|
|
61
63
|
|
|
62
|
-
|
|
64
|
+
#### Playwright MCP — live browser control
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
<summary><strong>VS Code (Claude Code extension)</strong></summary>
|
|
66
|
+
Uses [`@playwright/mcp`](https://www.npmjs.com/package/@playwright/mcp) to:
|
|
66
67
|
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
- Open a real browser and navigate your running app
|
|
69
|
+
- Extract actual locators (`data-testid`, ARIA roles, labels) from live pages
|
|
70
|
+
- Run E2E tests, capture failures, and auto-fix locator mismatches
|
|
71
|
+
- Build a persistent **Locator Registry** (`.qa-output/locators/`) that caches real locators across features
|
|
69
72
|
|
|
70
|
-
|
|
71
|
-
{
|
|
72
|
-
"claude-code.mcpServers": {
|
|
73
|
-
"playwright": {
|
|
74
|
-
"command": "npx",
|
|
75
|
-
"args": ["@playwright/mcp@latest"]
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
```
|
|
73
|
+
#### Context7 MCP — up-to-date library docs
|
|
80
74
|
|
|
81
|
-
|
|
75
|
+
Uses [`@upstash/context7-mcp`](https://www.npmjs.com/package/@upstash/context7-mcp) to:
|
|
82
76
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
"playwright": {
|
|
87
|
-
"command": "npx",
|
|
88
|
-
"args": ["@playwright/mcp@latest"]
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
```
|
|
77
|
+
- Fetch the latest documentation for Playwright, Cypress, Jest, Vitest, pytest, and any other library the agent is working with
|
|
78
|
+
- Keep generated tests aligned with current framework APIs instead of outdated training data
|
|
79
|
+
- Free tier: ~60 requests/hour, ~3,300 tokens/query
|
|
93
80
|
|
|
94
|
-
|
|
81
|
+
#### Verifying the MCPs are connected
|
|
95
82
|
|
|
96
|
-
|
|
97
|
-
<summary><strong>Claude Code CLI</strong></summary>
|
|
83
|
+
Open Claude Code in any project and type `/mcp`. You should see both `playwright` and `context7` listed as connected.
|
|
98
84
|
|
|
99
|
-
|
|
85
|
+
#### Manual config (fallback)
|
|
86
|
+
|
|
87
|
+
If for any reason the automatic registration fails, you can add the servers manually to `~/.claude.json`:
|
|
100
88
|
|
|
101
89
|
```json
|
|
102
90
|
{
|
|
@@ -104,21 +92,15 @@ Add to `~/.claude.json` (user-scope, all projects):
|
|
|
104
92
|
"playwright": {
|
|
105
93
|
"command": "npx",
|
|
106
94
|
"args": ["@playwright/mcp@latest"]
|
|
95
|
+
},
|
|
96
|
+
"context7": {
|
|
97
|
+
"command": "npx",
|
|
98
|
+
"args": ["-y", "@upstash/context7-mcp@latest"]
|
|
107
99
|
}
|
|
108
100
|
}
|
|
109
101
|
}
|
|
110
102
|
```
|
|
111
103
|
|
|
112
|
-
Or add a `.mcp.json` file in your project root for project-scope only.
|
|
113
|
-
|
|
114
|
-
</details>
|
|
115
|
-
|
|
116
|
-
Once configured, Playwright MCP enables QAA to:
|
|
117
|
-
- Open a real browser and navigate your running app
|
|
118
|
-
- Extract actual locators (`data-testid`, ARIA roles, labels) from live pages
|
|
119
|
-
- Run E2E tests, capture failures, and auto-fix locator mismatches
|
|
120
|
-
- Build a persistent **Locator Registry** (`.qa-output/locators/`) that caches real locators across features
|
|
121
|
-
|
|
122
104
|
---
|
|
123
105
|
|
|
124
106
|
## Quick Start
|
|
@@ -328,7 +310,7 @@ qaa-agent/
|
|
|
328
310
|
bin/ # Installer and CLI tools
|
|
329
311
|
docs/ # User documentation
|
|
330
312
|
CLAUDE.md # QA standards (read by every agent)
|
|
331
|
-
.mcp.json # Playwright MCP server config
|
|
313
|
+
.mcp.json # Playwright + Context7 MCP server config
|
|
332
314
|
settings.json # Claude Code permissions
|
|
333
315
|
```
|
|
334
316
|
|
package/bin/install.cjs
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
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
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qaa-agent",
|
|
3
|
-
"version": "1.8.
|
|
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/",
|