qaa-agent 1.6.2 → 1.6.3

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 (73) hide show
  1. package/.claude/commands/create-test.md +164 -164
  2. package/.claude/commands/qa-audit.md +37 -37
  3. package/.claude/commands/qa-blueprint.md +54 -54
  4. package/.claude/commands/qa-fix.md +36 -36
  5. package/.claude/commands/qa-from-ticket.md +24 -24
  6. package/.claude/commands/qa-gap.md +20 -20
  7. package/.claude/commands/qa-map.md +47 -47
  8. package/.claude/commands/qa-pom.md +36 -36
  9. package/.claude/commands/qa-pr.md +23 -23
  10. package/.claude/commands/qa-pyramid.md +37 -37
  11. package/.claude/commands/qa-report.md +38 -38
  12. package/.claude/commands/qa-research.md +33 -33
  13. package/.claude/commands/qa-start.md +22 -22
  14. package/.claude/commands/qa-testid.md +19 -19
  15. package/.claude/commands/qa-validate.md +42 -42
  16. package/.claude/commands/update-test.md +58 -58
  17. package/.claude/settings.json +20 -20
  18. package/.claude/skills/qa-bug-detective/SKILL.md +122 -122
  19. package/.claude/skills/qa-learner/SKILL.md +150 -150
  20. package/.claude/skills/qa-repo-analyzer/SKILL.md +88 -88
  21. package/.claude/skills/qa-self-validator/SKILL.md +109 -109
  22. package/.claude/skills/qa-template-engine/SKILL.md +113 -113
  23. package/.claude/skills/qa-testid-injector/SKILL.md +93 -93
  24. package/.claude/skills/qa-workflow-documenter/SKILL.md +87 -87
  25. package/.mcp.json +8 -8
  26. package/CHANGELOG.md +71 -71
  27. package/CLAUDE.md +553 -553
  28. package/agents/qa-pipeline-orchestrator.md +1378 -1378
  29. package/agents/qaa-analyzer.md +524 -524
  30. package/agents/qaa-bug-detective.md +446 -446
  31. package/agents/qaa-codebase-mapper.md +935 -935
  32. package/agents/qaa-e2e-runner.md +415 -415
  33. package/agents/qaa-executor.md +651 -651
  34. package/agents/qaa-planner.md +390 -390
  35. package/agents/qaa-project-researcher.md +319 -319
  36. package/agents/qaa-scanner.md +424 -424
  37. package/agents/qaa-testid-injector.md +585 -585
  38. package/agents/qaa-validator.md +452 -452
  39. package/bin/install.cjs +198 -198
  40. package/bin/lib/commands.cjs +709 -709
  41. package/bin/lib/config.cjs +307 -307
  42. package/bin/lib/core.cjs +497 -497
  43. package/bin/lib/frontmatter.cjs +299 -299
  44. package/bin/lib/init.cjs +989 -989
  45. package/bin/lib/milestone.cjs +241 -241
  46. package/bin/lib/model-profiles.cjs +60 -60
  47. package/bin/lib/phase.cjs +911 -911
  48. package/bin/lib/roadmap.cjs +306 -306
  49. package/bin/lib/state.cjs +748 -748
  50. package/bin/lib/template.cjs +222 -222
  51. package/bin/lib/verify.cjs +842 -842
  52. package/bin/qaa-tools.cjs +607 -607
  53. package/docs/COMMANDS.md +341 -341
  54. package/docs/DEMO.md +182 -182
  55. package/docs/TESTING.md +156 -156
  56. package/package.json +41 -41
  57. package/templates/failure-classification.md +391 -391
  58. package/templates/gap-analysis.md +409 -409
  59. package/templates/pr-template.md +48 -48
  60. package/templates/qa-analysis.md +381 -381
  61. package/templates/qa-audit-report.md +465 -465
  62. package/templates/qa-repo-blueprint.md +636 -636
  63. package/templates/scan-manifest.md +312 -312
  64. package/templates/test-inventory.md +582 -582
  65. package/templates/testid-audit-report.md +354 -354
  66. package/templates/validation-report.md +243 -243
  67. package/workflows/qa-analyze.md +296 -296
  68. package/workflows/qa-from-ticket.md +536 -536
  69. package/workflows/qa-gap.md +303 -303
  70. package/workflows/qa-pr.md +389 -389
  71. package/workflows/qa-start.md +1168 -1168
  72. package/workflows/qa-testid.md +356 -356
  73. package/workflows/qa-validate.md +295 -295
package/bin/install.cjs CHANGED
@@ -1,198 +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
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
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
+ });