qaa-agent 1.6.2 → 1.7.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 (78) hide show
  1. package/.mcp.json +8 -8
  2. package/CHANGELOG.md +93 -71
  3. package/CLAUDE.md +553 -553
  4. package/agents/qa-pipeline-orchestrator.md +1378 -1378
  5. package/agents/qaa-analyzer.md +539 -524
  6. package/agents/qaa-bug-detective.md +479 -446
  7. package/agents/qaa-codebase-mapper.md +935 -935
  8. package/agents/qaa-discovery.md +384 -0
  9. package/agents/qaa-e2e-runner.md +416 -415
  10. package/agents/qaa-executor.md +651 -651
  11. package/agents/qaa-planner.md +405 -390
  12. package/agents/qaa-project-researcher.md +319 -319
  13. package/agents/qaa-scanner.md +424 -424
  14. package/agents/qaa-testid-injector.md +643 -585
  15. package/agents/qaa-validator.md +490 -452
  16. package/bin/install.cjs +200 -198
  17. package/bin/lib/commands.cjs +709 -709
  18. package/bin/lib/config.cjs +307 -307
  19. package/bin/lib/core.cjs +497 -497
  20. package/bin/lib/frontmatter.cjs +299 -299
  21. package/bin/lib/init.cjs +989 -989
  22. package/bin/lib/milestone.cjs +241 -241
  23. package/bin/lib/model-profiles.cjs +60 -60
  24. package/bin/lib/phase.cjs +911 -911
  25. package/bin/lib/roadmap.cjs +306 -306
  26. package/bin/lib/state.cjs +748 -748
  27. package/bin/lib/template.cjs +222 -222
  28. package/bin/lib/verify.cjs +842 -842
  29. package/bin/qaa-tools.cjs +607 -607
  30. package/commands/qa-audit.md +119 -0
  31. package/commands/qa-create-test.md +288 -0
  32. package/commands/qa-fix.md +147 -0
  33. package/commands/qa-map.md +137 -0
  34. package/{.claude/commands → commands}/qa-pr.md +23 -23
  35. package/{.claude/commands → commands}/qa-start.md +22 -22
  36. package/{.claude/commands → commands}/qa-testid.md +19 -19
  37. package/docs/COMMANDS.md +341 -341
  38. package/docs/DEMO.md +182 -182
  39. package/docs/TESTING.md +156 -156
  40. package/package.json +6 -7
  41. package/{.claude/settings.json → settings.json} +1 -2
  42. package/templates/failure-classification.md +391 -391
  43. package/templates/gap-analysis.md +409 -409
  44. package/templates/pr-template.md +48 -48
  45. package/templates/qa-analysis.md +381 -381
  46. package/templates/qa-audit-report.md +465 -465
  47. package/templates/qa-repo-blueprint.md +636 -636
  48. package/templates/scan-manifest.md +312 -312
  49. package/templates/test-inventory.md +582 -582
  50. package/templates/testid-audit-report.md +354 -354
  51. package/templates/validation-report.md +243 -243
  52. package/workflows/qa-analyze.md +296 -296
  53. package/workflows/qa-from-ticket.md +536 -536
  54. package/workflows/qa-gap.md +309 -303
  55. package/workflows/qa-pr.md +389 -389
  56. package/workflows/qa-start.md +1192 -1168
  57. package/workflows/qa-testid.md +384 -356
  58. package/workflows/qa-validate.md +299 -295
  59. package/.claude/commands/create-test.md +0 -164
  60. package/.claude/commands/qa-audit.md +0 -37
  61. package/.claude/commands/qa-blueprint.md +0 -54
  62. package/.claude/commands/qa-fix.md +0 -36
  63. package/.claude/commands/qa-from-ticket.md +0 -24
  64. package/.claude/commands/qa-gap.md +0 -20
  65. package/.claude/commands/qa-map.md +0 -47
  66. package/.claude/commands/qa-pom.md +0 -36
  67. package/.claude/commands/qa-pyramid.md +0 -37
  68. package/.claude/commands/qa-report.md +0 -38
  69. package/.claude/commands/qa-research.md +0 -33
  70. package/.claude/commands/qa-validate.md +0 -42
  71. package/.claude/commands/update-test.md +0 -58
  72. package/.claude/skills/qa-learner/SKILL.md +0 -150
  73. /package/{.claude/skills → skills}/qa-bug-detective/SKILL.md +0 -0
  74. /package/{.claude/skills → skills}/qa-repo-analyzer/SKILL.md +0 -0
  75. /package/{.claude/skills → skills}/qa-self-validator/SKILL.md +0 -0
  76. /package/{.claude/skills → skills}/qa-template-engine/SKILL.md +0 -0
  77. /package/{.claude/skills → skills}/qa-testid-injector/SKILL.md +0 -0
  78. /package/{.claude/skills → skills}/qa-workflow-documenter/SKILL.md +0 -0
package/bin/install.cjs CHANGED
@@ -1,198 +1,200 @@
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
105
- const commandsSrc = path.join(ROOT, '.claude', 'commands');
106
- const commandsDest = path.join(baseDir, 'commands');
107
- const cmdCount = copyDir(commandsSrc, commandsDest);
108
- ok(`Installed ${cmdCount} slash commands`);
109
-
110
- // Install skills (only to baseDir -- Claude Code reads from ~/.claude/skills/)
111
- const skillsSrc = path.join(ROOT, '.claude', '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
- const mcpDest = path.join(qaaDir, '.mcp.json');
150
- copyFile(mcpSrc, mcpDest);
151
- ok('Installed Playwright MCP server config (.mcp.json)');
152
- }
153
-
154
- // Write version
155
- fs.writeFileSync(path.join(qaaDir, 'VERSION'), VERSION);
156
- ok(`Wrote VERSION (${VERSION})`);
157
-
158
- // Merge settings
159
- const settingsSrc = path.join(ROOT, '.claude', 'settings.json');
160
- const settingsDest = path.join(baseDir, 'settings.json');
161
- if (fs.existsSync(settingsSrc)) {
162
- let existing = {};
163
- if (fs.existsSync(settingsDest)) {
164
- try { existing = JSON.parse(fs.readFileSync(settingsDest, 'utf8')); } catch {}
165
- }
166
- const qaaSettings = JSON.parse(fs.readFileSync(settingsSrc, 'utf8'));
167
- if (qaaSettings.permissions) {
168
- existing.permissions = existing.permissions || {};
169
- existing.permissions.allow = [...new Set([
170
- ...(existing.permissions.allow || []),
171
- ...(qaaSettings.permissions.allow || [])
172
- ])];
173
- }
174
- fs.writeFileSync(settingsDest, JSON.stringify(existing, null, 2));
175
- ok('Merged permissions into settings.json');
176
- }
177
-
178
- // Done
179
- const total = cmdCount + skillCount + agentCount + templateCount + wfCount + binCount;
180
- console.log('');
181
- console.log(` \x1b[32m✓ Done!\x1b[0m Installed ${total} files.`);
182
- console.log('');
183
- console.log(' Open Claude Code in any project and run:');
184
- console.log('');
185
- console.log(' \x1b[1m/qa-start\x1b[0m Full QA pipeline (multi-agent)');
186
- console.log(' \x1b[1m/qa-map\x1b[0m Codebase map + analysis');
187
- console.log(' \x1b[1m/create-test\x1b[0m Tests for a feature');
188
- console.log(' \x1b[1m/qa-from-ticket\x1b[0m Tests from a Jira/Linear ticket');
189
- console.log(' \x1b[1m/qa-validate\x1b[0m Validate existing tests');
190
- console.log('');
191
- console.log(` ${cmdCount} commands + ${skillDirCount} skills + ${agentCount} agents ready.`);
192
- console.log('');
193
- }
194
-
195
- main().catch(err => {
196
- console.error('Installation failed:', err.message);
197
- process.exit(1);
198
- });
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
+ const mcpDest = path.join(qaaDir, '.mcp.json');
150
+ copyFile(mcpSrc, mcpDest);
151
+ ok('Installed Playwright MCP server config (.mcp.json)');
152
+ }
153
+
154
+ // Write version
155
+ fs.writeFileSync(path.join(qaaDir, 'VERSION'), VERSION);
156
+ ok(`Wrote VERSION (${VERSION})`);
157
+
158
+ // Merge settings (from settings.json in package root)
159
+ const settingsSrc = path.join(ROOT, 'settings.json');
160
+ const settingsDest = path.join(baseDir, 'settings.json');
161
+ if (fs.existsSync(settingsSrc)) {
162
+ let existing = {};
163
+ if (fs.existsSync(settingsDest)) {
164
+ try { existing = JSON.parse(fs.readFileSync(settingsDest, 'utf8')); } catch {}
165
+ }
166
+ const qaaSettings = JSON.parse(fs.readFileSync(settingsSrc, 'utf8'));
167
+ if (qaaSettings.permissions) {
168
+ existing.permissions = existing.permissions || {};
169
+ existing.permissions.allow = [...new Set([
170
+ ...(existing.permissions.allow || []),
171
+ ...(qaaSettings.permissions.allow || [])
172
+ ])];
173
+ }
174
+ fs.writeFileSync(settingsDest, JSON.stringify(existing, null, 2));
175
+ ok('Merged permissions into settings.json');
176
+ }
177
+
178
+ // Done
179
+ const total = cmdCount + skillCount + agentCount + templateCount + wfCount + binCount;
180
+ console.log('');
181
+ console.log(` \x1b[32m✓ Done!\x1b[0m Installed ${total} files.`);
182
+ console.log('');
183
+ console.log(' Open Claude Code in any project and run:');
184
+ console.log('');
185
+ console.log(' \x1b[1m/qa-start\x1b[0m Full QA pipeline (multi-agent)');
186
+ console.log(' \x1b[1m/qa-map\x1b[0m Codebase map + analysis');
187
+ console.log(' \x1b[1m/qa-create-test\x1b[0m Tests for a feature/ticket');
188
+ console.log(' \x1b[1m/qa-audit\x1b[0m Audit existing tests');
189
+ console.log(' \x1b[1m/qa-fix\x1b[0m Fix broken tests');
190
+ console.log(' \x1b[1m/qa-testid\x1b[0m Inject data-testid attributes');
191
+ console.log(' \x1b[1m/qa-pr\x1b[0m Create QA pull request');
192
+ console.log('');
193
+ console.log(` ${cmdCount} commands + ${skillDirCount} skills + ${agentCount} agents ready.`);
194
+ console.log('');
195
+ }
196
+
197
+ main().catch(err => {
198
+ console.error('Installation failed:', err.message);
199
+ process.exit(1);
200
+ });