qa-engineer 0.9.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 (232) hide show
  1. package/COMPATIBILITY.md +71 -0
  2. package/LICENSE +21 -0
  3. package/README.md +508 -0
  4. package/package.json +90 -0
  5. package/packages/installer/README.md +46 -0
  6. package/packages/installer/bin/qa.mjs +136 -0
  7. package/packages/installer/lib/agents/registry.mjs +209 -0
  8. package/packages/installer/lib/cli/commands.mjs +29 -0
  9. package/packages/installer/lib/cli/flags.mjs +64 -0
  10. package/packages/installer/lib/commands/doctor.mjs +190 -0
  11. package/packages/installer/lib/commands/install.mjs +291 -0
  12. package/packages/installer/lib/commands/onboard.mjs +163 -0
  13. package/packages/installer/lib/commands/repair.mjs +68 -0
  14. package/packages/installer/lib/commands/self-test.mjs +45 -0
  15. package/packages/installer/lib/commands/uninstall.mjs +167 -0
  16. package/packages/installer/lib/commands/update.mjs +69 -0
  17. package/packages/installer/lib/commands/verify.mjs +62 -0
  18. package/packages/installer/lib/constants.mjs +34 -0
  19. package/packages/installer/lib/core/bundle.mjs +124 -0
  20. package/packages/installer/lib/core/config.mjs +73 -0
  21. package/packages/installer/lib/core/conflict.mjs +29 -0
  22. package/packages/installer/lib/core/errors.mjs +29 -0
  23. package/packages/installer/lib/core/fs-safe.mjs +236 -0
  24. package/packages/installer/lib/core/hash.mjs +19 -0
  25. package/packages/installer/lib/core/lockfile.mjs +75 -0
  26. package/packages/installer/lib/core/logger.mjs +46 -0
  27. package/packages/installer/lib/core/manifest.mjs +89 -0
  28. package/packages/installer/lib/core/paths.mjs +74 -0
  29. package/packages/installer/lib/core/schema-validate.mjs +142 -0
  30. package/packages/installer/lib/core/skill-meta.mjs +75 -0
  31. package/packages/installer/lib/core/validate-install.mjs +152 -0
  32. package/packages/installer/lib/core/wrappers.mjs +91 -0
  33. package/packages/installer/lib/detect/environment.mjs +54 -0
  34. package/packages/installer/lib/detect/frameworks.mjs +94 -0
  35. package/packages/installer/lib/detect/project.mjs +126 -0
  36. package/packages/installer/lib/detect/recommend.mjs +85 -0
  37. package/packages/installer/lib/detect/scan.mjs +48 -0
  38. package/packages/installer/lib/ui/progress.mjs +32 -0
  39. package/packages/installer/lib/ui/theme.mjs +81 -0
  40. package/packages/installer/lib/version.mjs +47 -0
  41. package/packages/installer/package.json +28 -0
  42. package/packages/installer/schemas/qa-lock.schema.json +89 -0
  43. package/packages/installer/schemas/qa.config.schema.json +91 -0
  44. package/shared/analysis/lib/qa_analysis/__init__.py +11 -0
  45. package/shared/analysis/lib/qa_analysis/branding.json +9 -0
  46. package/shared/analysis/lib/qa_analysis/branding.py +175 -0
  47. package/shared/analysis/lib/qa_analysis/cli.py +129 -0
  48. package/shared/analysis/lib/qa_analysis/context.py +233 -0
  49. package/shared/analysis/lib/qa_analysis/contracts.py +158 -0
  50. package/shared/analysis/lib/qa_analysis/diff_guard.py +327 -0
  51. package/shared/analysis/lib/qa_analysis/discovery.py +113 -0
  52. package/shared/analysis/lib/qa_analysis/evidence.py +128 -0
  53. package/shared/analysis/lib/qa_analysis/har.py +58 -0
  54. package/shared/analysis/lib/qa_analysis/junit.py +80 -0
  55. package/shared/analysis/lib/qa_analysis/redaction.py +99 -0
  56. package/shared/analysis/lib/qa_analysis/taxonomy.py +98 -0
  57. package/shared/analysis/schemas/context.schema.json +82 -0
  58. package/shared/diagnostics/lib/qa_diagnostics/__init__.py +13 -0
  59. package/shared/diagnostics/lib/qa_diagnostics/cli.py +124 -0
  60. package/shared/diagnostics/lib/qa_diagnostics/engine.py +143 -0
  61. package/shared/diagnostics/lib/qa_diagnostics/internal_contracts.py +75 -0
  62. package/shared/diagnostics/lib/qa_diagnostics/prioritization.py +101 -0
  63. package/shared/diagnostics/lib/qa_diagnostics/repair.py +72 -0
  64. package/shared/diagnostics/lib/qa_diagnostics/root_cause.py +89 -0
  65. package/shared/diagnostics/lib/qa_diagnostics/timeline.py +71 -0
  66. package/shared/diagnostics/schemas/internal/analysis-result.schema.json +28 -0
  67. package/shared/diagnostics/schemas/internal/diagnosis.schema.json +55 -0
  68. package/shared/diagnostics/schemas/internal/execution-result-min.schema.json +34 -0
  69. package/shared/frameworks/cypress/lib/cypress_analysis.py +27 -0
  70. package/shared/frameworks/playwright/lib/playwright_analysis.py +167 -0
  71. package/shared/frameworks/registry.json +150 -0
  72. package/shared/frameworks/registry.mjs +37 -0
  73. package/shared/frameworks/registry.schema.json +74 -0
  74. package/shared/frameworks/selenium/lib/selenium_analysis.py +28 -0
  75. package/shared/frameworks/webdriverio/lib/webdriverio_analysis.py +24 -0
  76. package/shared/tooling/qa_tool.py +127 -0
  77. package/skills/README.md +31 -0
  78. package/skills/qa/README.md +19 -0
  79. package/skills/qa/SKILL.md +52 -0
  80. package/skills/qa/examples/routing.md +86 -0
  81. package/skills/qa/references/routing-map.md +33 -0
  82. package/skills/qa-api/README.md +19 -0
  83. package/skills/qa-api/SKILL.md +68 -0
  84. package/skills/qa-api/contracts/api-result.schema.json +51 -0
  85. package/skills/qa-api/examples/graphql-review.md +43 -0
  86. package/skills/qa-api/references/authentication.md +45 -0
  87. package/skills/qa-api/references/deterministic-tooling.md +128 -0
  88. package/skills/qa-api/references/evidence-and-reporting.md +66 -0
  89. package/skills/qa-api/references/graphql.md +44 -0
  90. package/skills/qa-api/references/rest.md +45 -0
  91. package/skills/qa-api/references/websocket.md +44 -0
  92. package/skills/qa-audit/README.md +19 -0
  93. package/skills/qa-audit/SKILL.md +70 -0
  94. package/skills/qa-audit/contracts/audit-result.schema.json +63 -0
  95. package/skills/qa-audit/examples/accessibility-audit.md +46 -0
  96. package/skills/qa-audit/references/accessibility.md +44 -0
  97. package/skills/qa-audit/references/deterministic-tooling.md +128 -0
  98. package/skills/qa-audit/references/evidence-and-reporting.md +66 -0
  99. package/skills/qa-audit/references/performance.md +44 -0
  100. package/skills/qa-audit/references/security.md +43 -0
  101. package/skills/qa-audit/references/visual-testing.md +44 -0
  102. package/skills/qa-debug/README.md +19 -0
  103. package/skills/qa-debug/SKILL.md +76 -0
  104. package/skills/qa-debug/contracts/debug-result.schema.json +121 -0
  105. package/skills/qa-debug/examples/failed-login.md +61 -0
  106. package/skills/qa-debug/examples/locator-break.md +59 -0
  107. package/skills/qa-debug/examples/network-timeout.md +61 -0
  108. package/skills/qa-debug/examples/successful-debug.md +62 -0
  109. package/skills/qa-debug/references/confidence-model.md +26 -0
  110. package/skills/qa-debug/references/deterministic-tooling.md +128 -0
  111. package/skills/qa-debug/references/diagnostic-engine.md +36 -0
  112. package/skills/qa-debug/references/evidence-and-reporting.md +66 -0
  113. package/skills/qa-debug/references/evidence-model.md +41 -0
  114. package/skills/qa-debug/references/failure-taxonomy.md +44 -0
  115. package/skills/qa-debug/references/finding-prioritization.md +43 -0
  116. package/skills/qa-debug/references/investigation-workflow.md +48 -0
  117. package/skills/qa-debug/references/recommendation-ranking.md +34 -0
  118. package/skills/qa-debug/references/root-cause-analysis.md +49 -0
  119. package/skills/qa-debug/references/timeline-builder.md +37 -0
  120. package/skills/qa-example/README.md +19 -0
  121. package/skills/qa-example/SKILL.md +60 -0
  122. package/skills/qa-example/contracts/self-check-report.schema.json +65 -0
  123. package/skills/qa-example/examples/self-check.md +53 -0
  124. package/skills/qa-example/references/example-domain.md +28 -0
  125. package/skills/qa-example/references/skill-format-notes.md +26 -0
  126. package/skills/qa-explore/README.md +22 -0
  127. package/skills/qa-explore/SKILL.md +87 -0
  128. package/skills/qa-explore/contracts/explore-result.schema.json +249 -0
  129. package/skills/qa-explore/examples/attached-test-cases.md +70 -0
  130. package/skills/qa-explore/examples/screenshot-proof-finding.md +50 -0
  131. package/skills/qa-explore/examples/url-smoke-explore.md +80 -0
  132. package/skills/qa-explore/references/accessibility.md +44 -0
  133. package/skills/qa-explore/references/api-replay.md +48 -0
  134. package/skills/qa-explore/references/browser-adapters.md +51 -0
  135. package/skills/qa-explore/references/evidence-and-reporting.md +66 -0
  136. package/skills/qa-explore/references/evidence-capture.md +54 -0
  137. package/skills/qa-explore/references/exploratory-qa.md +52 -0
  138. package/skills/qa-explore/references/finding-taxonomy.md +47 -0
  139. package/skills/qa-explore/references/performance.md +44 -0
  140. package/skills/qa-explore/references/pipeline.md +74 -0
  141. package/skills/qa-explore/references/report-pipeline.md +69 -0
  142. package/skills/qa-explore/references/security.md +43 -0
  143. package/skills/qa-explore/references/test-case-intake.md +44 -0
  144. package/skills/qa-fix/README.md +19 -0
  145. package/skills/qa-fix/SKILL.md +70 -0
  146. package/skills/qa-fix/contracts/fix-result.schema.json +132 -0
  147. package/skills/qa-fix/examples/repair-plan.md +56 -0
  148. package/skills/qa-fix/references/deterministic-tooling.md +128 -0
  149. package/skills/qa-fix/references/diagnostic-engine.md +36 -0
  150. package/skills/qa-fix/references/evidence-and-reporting.md +66 -0
  151. package/skills/qa-fix/references/repair-strategy.md +48 -0
  152. package/skills/qa-fix/references/root-cause-analysis.md +49 -0
  153. package/skills/qa-fix/references/suite-extension.md +73 -0
  154. package/skills/qa-flaky/README.md +19 -0
  155. package/skills/qa-flaky/SKILL.md +67 -0
  156. package/skills/qa-flaky/contracts/flaky-result.schema.json +62 -0
  157. package/skills/qa-flaky/examples/flaky-locator.md +46 -0
  158. package/skills/qa-flaky/references/deterministic-tooling.md +128 -0
  159. package/skills/qa-flaky/references/evidence-and-reporting.md +66 -0
  160. package/skills/qa-flaky/references/flakiness.md +48 -0
  161. package/skills/qa-flaky/references/retry.md +44 -0
  162. package/skills/qa-flaky/references/waiting-strategies.md +44 -0
  163. package/skills/qa-generate/README.md +42 -0
  164. package/skills/qa-generate/SKILL.md +83 -0
  165. package/skills/qa-generate/contracts/generation-result.schema.json +124 -0
  166. package/skills/qa-generate/examples/bootstrap-new-framework.md +24 -0
  167. package/skills/qa-generate/examples/extend-existing-suite.md +26 -0
  168. package/skills/qa-generate/references/code-style.md +29 -0
  169. package/skills/qa-generate/references/evidence-and-reporting.md +66 -0
  170. package/skills/qa-generate/references/framework-selection.md +88 -0
  171. package/skills/qa-generate/references/generation-strategy.md +48 -0
  172. package/skills/qa-generate/references/naming-conventions.md +27 -0
  173. package/skills/qa-generate/references/playwright-conventions.md +23 -0
  174. package/skills/qa-generate/references/playwright-generation.md +41 -0
  175. package/skills/qa-generate/references/playwright-project-discovery.md +42 -0
  176. package/skills/qa-generate/references/project-bootstrap.md +54 -0
  177. package/skills/qa-generate/references/repository-analysis.md +43 -0
  178. package/skills/qa-generate/references/suite-extension.md +73 -0
  179. package/skills/qa-generate/references/template-selection.md +48 -0
  180. package/skills/qa-generate/templates/playwright/api.ts +36 -0
  181. package/skills/qa-generate/templates/playwright/base-page.ts +14 -0
  182. package/skills/qa-generate/templates/playwright/data.ts +25 -0
  183. package/skills/qa-generate/templates/playwright/env.example +16 -0
  184. package/skills/qa-generate/templates/playwright/example.spec.ts +14 -0
  185. package/skills/qa-generate/templates/playwright/fixtures.ts +28 -0
  186. package/skills/qa-generate/templates/playwright/framework-readme.md +39 -0
  187. package/skills/qa-generate/templates/playwright/login.page.ts +26 -0
  188. package/skills/qa-generate/templates/playwright/playwright.config.ts +28 -0
  189. package/skills/qa-generate/templates/playwright/utils.ts +18 -0
  190. package/skills/qa-init/README.md +20 -0
  191. package/skills/qa-init/SKILL.md +72 -0
  192. package/skills/qa-init/examples/initialize-a-repo.md +91 -0
  193. package/skills/qa-init/references/detection-guide.md +76 -0
  194. package/skills/qa-init/references/deterministic-tooling.md +128 -0
  195. package/skills/qa-init/references/evidence-and-reporting.md +66 -0
  196. package/skills/qa-init/templates/context.md +68 -0
  197. package/skills/qa-report/README.md +19 -0
  198. package/skills/qa-report/SKILL.md +72 -0
  199. package/skills/qa-report/contracts/report-result.schema.json +186 -0
  200. package/skills/qa-report/examples/release-report.md +68 -0
  201. package/skills/qa-report/references/deterministic-tooling.md +128 -0
  202. package/skills/qa-report/references/diagnostic-engine.md +36 -0
  203. package/skills/qa-report/references/evidence-and-reporting.md +66 -0
  204. package/skills/qa-report/references/finding-prioritization.md +43 -0
  205. package/skills/qa-report/references/recommendation-ranking.md +34 -0
  206. package/skills/qa-report/references/report-aggregation.md +44 -0
  207. package/skills/qa-review/README.md +19 -0
  208. package/skills/qa-review/SKILL.md +58 -0
  209. package/skills/qa-review/contracts/review-result.schema.json +59 -0
  210. package/skills/qa-review/examples/suite-review.md +51 -0
  211. package/skills/qa-review/references/anti-patterns.md +49 -0
  212. package/skills/qa-review/references/assertion-patterns.md +45 -0
  213. package/skills/qa-review/references/evidence-and-reporting.md +66 -0
  214. package/skills/qa-review/references/fixtures.md +45 -0
  215. package/skills/qa-review/references/page-objects.md +45 -0
  216. package/skills/qa-run/README.md +22 -0
  217. package/skills/qa-run/SKILL.md +92 -0
  218. package/skills/qa-run/contracts/execution-plan.schema.json +132 -0
  219. package/skills/qa-run/contracts/execution-result.schema.json +204 -0
  220. package/skills/qa-run/examples/execute-playwright.md +89 -0
  221. package/skills/qa-run/examples/plan-a-run.md +83 -0
  222. package/skills/qa-run/references/artifact-collector.md +40 -0
  223. package/skills/qa-run/references/browser-launch.md +39 -0
  224. package/skills/qa-run/references/command-builder.md +39 -0
  225. package/skills/qa-run/references/deterministic-tooling.md +128 -0
  226. package/skills/qa-run/references/environment-detection.md +37 -0
  227. package/skills/qa-run/references/evidence-and-reporting.md +66 -0
  228. package/skills/qa-run/references/execution-strategy.md +54 -0
  229. package/skills/qa-run/references/playwright-artifacts.md +29 -0
  230. package/skills/qa-run/references/playwright-execution.md +48 -0
  231. package/skills/qa-run/references/playwright-project-discovery.md +42 -0
  232. package/skills/qa-run/references/report-normalization.md +49 -0
@@ -0,0 +1,126 @@
1
+ // Detect project characteristics: GraphQL, REST, Docker, CI, monorepo, React.
2
+
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+
6
+ function readPackageJson(root) {
7
+ const p = path.join(root, 'package.json');
8
+ if (!fs.existsSync(p)) return null;
9
+ try {
10
+ return JSON.parse(fs.readFileSync(p, 'utf8'));
11
+ } catch {
12
+ return null;
13
+ }
14
+ }
15
+
16
+ function depsOf(pkg) {
17
+ if (!pkg) return new Set();
18
+ return new Set([
19
+ ...Object.keys(pkg.dependencies ?? {}),
20
+ ...Object.keys(pkg.devDependencies ?? {}),
21
+ ...Object.keys(pkg.peerDependencies ?? {}),
22
+ ]);
23
+ }
24
+
25
+ function anyExists(root, names) {
26
+ return names.some((n) => fs.existsSync(path.join(root, n)));
27
+ }
28
+
29
+ function hasGraphqlFiles(root) {
30
+ try {
31
+ const entries = fs.readdirSync(root);
32
+ if (entries.some((n) => n.endsWith('.graphql') || n.endsWith('.gql'))) return true;
33
+ } catch {
34
+ /* ignore */
35
+ }
36
+ return anyExists(root, ['schema.graphql', 'schema.gql']);
37
+ }
38
+
39
+ function isMonorepo(root) {
40
+ return anyExists(root, [
41
+ 'pnpm-workspace.yaml',
42
+ 'lerna.json',
43
+ 'nx.json',
44
+ 'turbo.json',
45
+ 'rush.json',
46
+ ]);
47
+ }
48
+
49
+ function hasDocker(root) {
50
+ return anyExists(root, [
51
+ 'Dockerfile',
52
+ 'Dockerfile.dev',
53
+ 'docker-compose.yml',
54
+ 'docker-compose.yaml',
55
+ 'compose.yml',
56
+ ]);
57
+ }
58
+
59
+ function hasGithubActions(root) {
60
+ const dir = path.join(root, '.github', 'workflows');
61
+ if (!fs.existsSync(dir)) return false;
62
+ try {
63
+ return fs.readdirSync(dir).some((f) => f.endsWith('.yml') || f.endsWith('.yaml'));
64
+ } catch {
65
+ return false;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * @param {string} projectRoot
71
+ * @returns {{
72
+ * name: string,
73
+ * graphql: boolean,
74
+ * rest: boolean,
75
+ * docker: boolean,
76
+ * githubActions: boolean,
77
+ * monorepo: boolean,
78
+ * react: boolean,
79
+ * features: string[],
80
+ * }}
81
+ */
82
+ export function detectProject(projectRoot) {
83
+ const pkg = readPackageJson(projectRoot);
84
+ const deps = depsOf(pkg);
85
+ const name = pkg?.name || path.basename(projectRoot);
86
+
87
+ const graphql =
88
+ hasGraphqlFiles(projectRoot) ||
89
+ deps.has('graphql') ||
90
+ deps.has('@apollo/client') ||
91
+ deps.has('apollo-server') ||
92
+ deps.has('@nestjs/graphql');
93
+
94
+ const rest =
95
+ deps.has('express') ||
96
+ deps.has('fastify') ||
97
+ deps.has('@nestjs/core') ||
98
+ deps.has('hono') ||
99
+ deps.has('koa') ||
100
+ anyExists(projectRoot, ['openapi.yaml', 'openapi.yml', 'swagger.yaml', 'swagger.json']);
101
+
102
+ const react =
103
+ deps.has('react') || deps.has('next') || deps.has('@angular/core') || deps.has('vue');
104
+ const docker = hasDocker(projectRoot);
105
+ const githubActions = hasGithubActions(projectRoot);
106
+ const monorepo = isMonorepo(projectRoot);
107
+
108
+ const features = [];
109
+ if (react) features.push('ui');
110
+ if (graphql) features.push('graphql');
111
+ if (rest) features.push('rest');
112
+ if (docker) features.push('docker');
113
+ if (githubActions) features.push('github-actions');
114
+ if (monorepo) features.push('monorepo');
115
+
116
+ return {
117
+ name,
118
+ graphql,
119
+ rest,
120
+ docker,
121
+ githubActions,
122
+ monorepo,
123
+ react,
124
+ features,
125
+ };
126
+ }
@@ -0,0 +1,85 @@
1
+ // Map environment detections to recommended pack capabilities (existing skills only).
2
+
3
+ /**
4
+ * @param {{
5
+ * frameworks: string[],
6
+ * languages: string[],
7
+ * project: { graphql: boolean, rest: boolean, react: boolean },
8
+ * agents: string[],
9
+ * }} scan
10
+ * @returns {Array<{ id: string, label: string, reason: string, recommended: boolean }>}
11
+ */
12
+ export function recommendCapabilities(scan) {
13
+ const hasPw = scan.frameworks.includes('playwright');
14
+ const hasAnyFw = scan.frameworks.length > 0;
15
+ const { graphql, rest, react } = scan.project;
16
+
17
+ /** @type {Array<{ id: string, label: string, reason: string, recommended: boolean }>} */
18
+ const items = [
19
+ {
20
+ id: 'skills',
21
+ label: 'QA Skills',
22
+ reason: 'Installs /qa-* Agent Skills into your coding assistant',
23
+ recommended: true,
24
+ },
25
+ {
26
+ id: 'playwright',
27
+ label: 'Playwright Integration',
28
+ reason: hasPw
29
+ ? 'Playwright detected — live run and generate are Production-ready'
30
+ : 'No Playwright yet — /qa-generate can bootstrap a suite when you are ready',
31
+ recommended: hasPw || !hasAnyFw,
32
+ },
33
+ {
34
+ id: 'repo-analysis',
35
+ label: 'Repository Analysis',
36
+ reason: '/qa-init profiles the project for every later command',
37
+ recommended: true,
38
+ },
39
+ {
40
+ id: 'ui-bugs',
41
+ label: 'UI Bug Detection',
42
+ reason: react
43
+ ? 'UI stack detected — /qa-explore and /qa-audit cover live pages'
44
+ : 'Use /qa-explore against any URL for product QA',
45
+ recommended: react || hasPw,
46
+ },
47
+ {
48
+ id: 'api',
49
+ label: 'API Validation',
50
+ reason: rest
51
+ ? 'REST stack detected — /qa-api validates HTTP behavior'
52
+ : 'Available via /qa-api when you have API tests or OpenAPI',
53
+ recommended: rest,
54
+ },
55
+ {
56
+ id: 'graphql',
57
+ label: 'GraphQL Validation',
58
+ reason: graphql
59
+ ? 'GraphQL detected — /qa-api covers GraphQL operations'
60
+ : 'Enable when GraphQL appears in the project',
61
+ recommended: graphql,
62
+ },
63
+ {
64
+ id: 'generation',
65
+ label: 'Test Generation',
66
+ reason: hasPw
67
+ ? 'Extend or bootstrap Playwright suites with /qa-generate'
68
+ : 'Bootstrap Playwright automation with /qa-generate',
69
+ recommended: true,
70
+ },
71
+ {
72
+ id: 'engine',
73
+ label: 'Deterministic Engine',
74
+ reason: 'Bundles analysis/diagnostics libraries used by debug, fix, and report',
75
+ recommended: true,
76
+ },
77
+ ];
78
+
79
+ return items;
80
+ }
81
+
82
+ /** Capabilities that should always be installed (the full skill pack). */
83
+ export function alwaysInstallIds() {
84
+ return ['skills', 'engine', 'repo-analysis', 'generation'];
85
+ }
@@ -0,0 +1,48 @@
1
+ // Full project scan: agents + environment + frameworks + project + recommendations.
2
+
3
+ import path from 'node:path';
4
+ import { AGENTS, resolveInstallTargets } from '../agents/registry.mjs';
5
+ import { detectEnvironment } from './environment.mjs';
6
+ import { detectFrameworks } from './frameworks.mjs';
7
+ import { detectProject } from './project.mjs';
8
+ import { recommendCapabilities } from './recommend.mjs';
9
+
10
+ /**
11
+ * @param {string} projectRoot
12
+ * @param {string[]} [explicitAgentIds]
13
+ */
14
+ export function scanProject(projectRoot, explicitAgentIds = []) {
15
+ const root = path.resolve(projectRoot);
16
+ const environment = detectEnvironment(root);
17
+ const frameworks = detectFrameworks(root);
18
+ const project = detectProject(root);
19
+ const detectedAgents = AGENTS.filter((a) => a.detect(root)).map((a) => ({
20
+ id: a.id,
21
+ name: a.name,
22
+ }));
23
+ const installTargets = resolveInstallTargets(root, explicitAgentIds).map((a) => ({
24
+ id: a.id,
25
+ name: a.name,
26
+ skillsDir: a.skillsDir,
27
+ }));
28
+
29
+ const recommendations = recommendCapabilities({
30
+ frameworks: frameworks.frameworks,
31
+ languages: frameworks.languages,
32
+ project,
33
+ agents: detectedAgents.map((a) => a.id),
34
+ });
35
+
36
+ return {
37
+ projectRoot: root,
38
+ projectName: project.name,
39
+ environment,
40
+ frameworks: frameworks.frameworks,
41
+ languages: frameworks.languages,
42
+ frameworkDetails: frameworks.details,
43
+ project,
44
+ detectedAgents,
45
+ installTargets,
46
+ recommendations,
47
+ };
48
+ }
@@ -0,0 +1,32 @@
1
+ // Install progress step labels for the polished install experience.
2
+
3
+ export const INSTALL_STEPS = [
4
+ { id: 'skills', label: 'Installing skills' },
5
+ { id: 'knowledge', label: 'Synchronizing shared knowledge' },
6
+ { id: 'engine', label: 'Bundling deterministic engine' },
7
+ { id: 'contracts', label: 'Validating contracts' },
8
+ { id: 'eval', label: 'Installing evaluation platform' },
9
+ { id: 'config', label: 'Configuration complete' },
10
+ ];
11
+
12
+ /**
13
+ * Run labeled progress steps around an async install body.
14
+ * @param {{ start: Function, stop: Function, message?: Function }} spinner
15
+ * @param {(step: { id: string, label: string }) => Promise<void>|void} [onStep]
16
+ */
17
+ export async function runProgressSteps(spinner, onStep) {
18
+ for (const step of INSTALL_STEPS) {
19
+ spinner.message?.(step.label);
20
+ spinner.start?.(step.label);
21
+ if (onStep) await onStep(step);
22
+ spinner.stop?.(step.label);
23
+ }
24
+ }
25
+
26
+ /** Simple text progress bar for non-Clack contexts. */
27
+ export function progressBar(done, total, width = 18) {
28
+ const ratio = total === 0 ? 1 : Math.min(1, done / total);
29
+ const filled = Math.round(ratio * width);
30
+ const bar = `${'█'.repeat(filled)}${'░'.repeat(width - filled)}`;
31
+ return `${bar} ${Math.round(ratio * 100)}%`;
32
+ }
@@ -0,0 +1,81 @@
1
+ // Clack-based UX helpers. Human progress stays on stderr; machine JSON on stdout.
2
+
3
+ import * as p from '@clack/prompts';
4
+ import { createLogger } from '../core/logger.mjs';
5
+
6
+ export function isCancel(value) {
7
+ return p.isCancel(value);
8
+ }
9
+
10
+ export function createUi({ quiet = false, json = false } = {}) {
11
+ const log = createLogger({
12
+ level: quiet || json ? 'error' : process.env.QA_LOG_LEVEL || 'info',
13
+ });
14
+
15
+ return {
16
+ log,
17
+ intro(message = 'Welcome to QA Automation Pack') {
18
+ if (quiet || json) return;
19
+ p.intro(message);
20
+ },
21
+ outro(message) {
22
+ if (quiet || json) return;
23
+ p.outro(message);
24
+ },
25
+ note(message, title) {
26
+ if (quiet || json) return;
27
+ p.note(message, title);
28
+ },
29
+ logLine(message) {
30
+ if (quiet || json) return;
31
+ p.log.step(message);
32
+ },
33
+ success(message) {
34
+ if (quiet || json) return;
35
+ p.log.success(message);
36
+ },
37
+ warn(message) {
38
+ if (quiet || json) return;
39
+ p.log.warn(message);
40
+ },
41
+ error(message) {
42
+ if (!json) p.log.error(message);
43
+ else log.error(message);
44
+ },
45
+ async confirm(message, { initialValue = true } = {}) {
46
+ return p.confirm({ message, initialValue });
47
+ },
48
+ async multiselect(message, options, { required = true } = {}) {
49
+ return p.multiselect({
50
+ message,
51
+ options,
52
+ required,
53
+ });
54
+ },
55
+ async text(message, opts = {}) {
56
+ return p.text({ message, ...opts });
57
+ },
58
+ spinner() {
59
+ if (quiet || json) {
60
+ return {
61
+ start() {},
62
+ stop() {},
63
+ message() {},
64
+ };
65
+ }
66
+ return p.spinner();
67
+ },
68
+ cancel(message = 'Installation cancelled.') {
69
+ if (!quiet && !json) p.cancel(message);
70
+ },
71
+ };
72
+ }
73
+
74
+ /** Format a QaError (or generic Error) for end users — no stack unless debug. */
75
+ export function formatUserError(error, { debug = false } = {}) {
76
+ const lines = [];
77
+ lines.push(error?.message || String(error));
78
+ if (error?.hint) lines.push(`Next step: ${error.hint}`);
79
+ if (debug && error?.stack) lines.push(error.stack);
80
+ return lines.join('\n');
81
+ }
@@ -0,0 +1,47 @@
1
+ // Single source of truth for the pack version the installer reports and writes
2
+ // into lockfiles. Read from a package.json at runtime so the two can never
3
+ // drift. Works on any Node 18+ without JSON import attributes.
4
+ //
5
+ // Two layouts must both work, and one of them used to fail silently:
6
+ //
7
+ // repository packages/installer/lib/version.mjs -> ../package.json
8
+ // published the same relative path, IF packages/installer/package.json is
9
+ // in the `files` allowlist — it was not, so the published CLI
10
+ // reported 0.0.0 and wrote 0.0.0 into every lockfile.
11
+ //
12
+ // Both candidates are now tried, and an unresolvable version is loudly wrong
13
+ // (`0.0.0-unknown`) rather than plausibly wrong. `scripts/release/validate-release.mjs`
14
+ // asserts the manifest actually ships, so this fallback should never be reached.
15
+
16
+ import fs from 'node:fs';
17
+ import path from 'node:path';
18
+ import { fileURLToPath } from 'node:url';
19
+
20
+ const here = path.dirname(fileURLToPath(import.meta.url));
21
+
22
+ const MANIFEST_CANDIDATES = [
23
+ // The workspace member's own manifest.
24
+ path.join(here, '..', 'package.json'),
25
+ // The pack root manifest — always present in a published tarball.
26
+ path.join(here, '..', '..', '..', 'package.json'),
27
+ ];
28
+
29
+ function readVersion() {
30
+ for (const manifest of MANIFEST_CANDIDATES) {
31
+ try {
32
+ const version = JSON.parse(fs.readFileSync(manifest, 'utf8')).version;
33
+ if (typeof version === 'string' && version.length > 0) return version;
34
+ } catch {
35
+ /* try the next candidate */
36
+ }
37
+ }
38
+ return '0.0.0-unknown';
39
+ }
40
+
41
+ export const VERSION = readVersion();
42
+
43
+ // The Agent Skills specification revision this pack validated against. The
44
+ // specification is a living document without formal releases; this records the
45
+ // revision each install was made against so `verify` and support can reason
46
+ // about it. Bump when the pack is re-validated against a newer revision.
47
+ export const SPEC_REVISION = '2026-07';
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@qa-engineer/installer",
3
+ "version": "0.9.0",
4
+ "description": "The qa-engineer installer: agent detection, copy-based install, lockfile integrity, and wrapper generation.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "exports": {
8
+ ".": "./bin/qa.mjs"
9
+ },
10
+ "bin": {
11
+ "qa": "bin/qa.mjs"
12
+ },
13
+ "engines": {
14
+ "node": ">=18.18.0"
15
+ },
16
+ "files": [
17
+ "bin",
18
+ "lib",
19
+ "schemas",
20
+ "README.md"
21
+ ],
22
+ "dependencies": {
23
+ "@clack/prompts": "^0.11.0"
24
+ },
25
+ "scripts": {
26
+ "test": "node --test test"
27
+ }
28
+ }
@@ -0,0 +1,89 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft-07/schema#",
3
+ "$id": "urn:qa-pack:config:qa-lock:1",
4
+ "title": "QA Automation Pack install lockfile",
5
+ "description": "qa-lock.json records exactly what was installed and a hash per file, so an install can be verified, updated, or removed without guessing. Written by the installer; not hand-edited.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["lockfileVersion", "pack", "installer", "generatedAt", "agents", "files"],
9
+ "properties": {
10
+ "lockfileVersion": {
11
+ "type": "integer",
12
+ "const": 1
13
+ },
14
+ "pack": {
15
+ "type": "object",
16
+ "additionalProperties": false,
17
+ "required": ["name", "version", "specRevision"],
18
+ "properties": {
19
+ "name": { "type": "string" },
20
+ "version": { "type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+" },
21
+ "specRevision": { "type": "string" }
22
+ }
23
+ },
24
+ "installer": {
25
+ "type": "string",
26
+ "pattern": "^\\d+\\.\\d+\\.\\d+"
27
+ },
28
+ "generatedAt": {
29
+ "type": "string",
30
+ "format": "date-time"
31
+ },
32
+ "agents": {
33
+ "type": "array",
34
+ "minItems": 1,
35
+ "items": {
36
+ "type": "object",
37
+ "additionalProperties": false,
38
+ "required": ["id", "name", "tier"],
39
+ "properties": {
40
+ "id": { "type": "string", "pattern": "^[a-z][a-z0-9-]*$" },
41
+ "name": { "type": "string" },
42
+ "tier": {
43
+ "type": ["integer", "null"],
44
+ "minimum": 1,
45
+ "maximum": 2,
46
+ "description": "Support tier, or null when the host is unknown — the shared Agent Skills path was installed without identifying a product."
47
+ },
48
+ "skillsDir": { "type": "string" },
49
+ "detected": {
50
+ "type": "boolean",
51
+ "description": "True only when project markers identified this host. False when it was requested explicitly or stands in as the unknown-agent path. Optional for lockfiles written before this field existed."
52
+ }
53
+ }
54
+ }
55
+ },
56
+ "files": {
57
+ "type": "array",
58
+ "description": "Every file the installer wrote, each with the hash it wrote, so verify detects drift and uninstall removes exactly these.",
59
+ "items": {
60
+ "type": "object",
61
+ "additionalProperties": false,
62
+ "required": ["path", "sha256", "bytes", "owner"],
63
+ "properties": {
64
+ "path": {
65
+ "type": "string",
66
+ "minLength": 1,
67
+ "description": "Project-relative POSIX path. Must not be absolute and must not contain a '..' segment: a lockfile travels with a cloned repository, and uninstall deletes what it lists.",
68
+ "pattern": "^(?!/)(?!.*(^|/)\\.\\.($|/)).+$"
69
+ },
70
+ "sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
71
+ "bytes": { "type": "integer", "minimum": 0 },
72
+ "owner": { "type": "string", "enum": ["skill", "wrapper", "config"] },
73
+ "skill": { "type": "string" },
74
+ "agent": { "type": "string" }
75
+ }
76
+ }
77
+ },
78
+ "summary": {
79
+ "type": "object",
80
+ "additionalProperties": false,
81
+ "properties": {
82
+ "skills": { "type": "integer", "minimum": 0 },
83
+ "files": { "type": "integer", "minimum": 0 },
84
+ "wrappers": { "type": "integer", "minimum": 0 },
85
+ "directories": { "type": "integer", "minimum": 0 }
86
+ }
87
+ }
88
+ }
89
+ }
@@ -0,0 +1,91 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft-07/schema#",
3
+ "$id": "urn:qa-pack:config:qa-config:1",
4
+ "title": "QA Automation Pack project configuration",
5
+ "description": "The optional qa.config.json written at a project root. Every field has a documented default; the file exists only to override those defaults. See docs/architecture/ADR-0015-configuration-contract.md.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["version"],
9
+ "properties": {
10
+ "version": {
11
+ "type": "integer",
12
+ "const": 1,
13
+ "description": "Configuration contract major version. Bumped only by a breaking change to this schema."
14
+ },
15
+ "frameworks": {
16
+ "type": "object",
17
+ "additionalProperties": false,
18
+ "description": "Test-framework preferences. Advisory hints the skills read; they never override what a repository actually contains.",
19
+ "properties": {
20
+ "preferred": {
21
+ "type": "string",
22
+ "enum": ["playwright", "selenium", "cypress", "webdriverio"],
23
+ "description": "Framework to prefer when a project has none yet, or when detection is ambiguous."
24
+ }
25
+ }
26
+ },
27
+ "browser": {
28
+ "type": "object",
29
+ "additionalProperties": false,
30
+ "description": "Default browser preferences for execution and audit skills.",
31
+ "properties": {
32
+ "default": {
33
+ "type": "string",
34
+ "enum": ["chromium", "firefox", "webkit", "chrome", "edge"],
35
+ "description": "Browser used when a command does not name one. Defaults to chromium."
36
+ }
37
+ }
38
+ },
39
+ "output": {
40
+ "type": "object",
41
+ "additionalProperties": false,
42
+ "description": "Where skills write machine-readable results and evidence.",
43
+ "properties": {
44
+ "directory": {
45
+ "type": "string",
46
+ "minLength": 1,
47
+ "description": "Artifact output directory, relative to the project root. Defaults to qa-artifacts."
48
+ }
49
+ }
50
+ },
51
+ "logging": {
52
+ "type": "object",
53
+ "additionalProperties": false,
54
+ "description": "Verbosity of the installer CLI and skill progress reporting.",
55
+ "properties": {
56
+ "level": {
57
+ "type": "string",
58
+ "enum": ["silent", "error", "warn", "info", "debug"],
59
+ "description": "Minimum level to emit. Defaults to info."
60
+ }
61
+ }
62
+ },
63
+ "telemetry": {
64
+ "type": "object",
65
+ "additionalProperties": false,
66
+ "description": "Reserved. The pack implements no telemetry sink and transmits nothing (see SECURITY.md); this field exists only so that any future, opt-in telemetry is off by contract.",
67
+ "properties": {
68
+ "enabled": {
69
+ "type": "boolean",
70
+ "description": "Always defaults to false. No code path currently reads a true value to transmit anything."
71
+ }
72
+ }
73
+ },
74
+ "plugins": {
75
+ "type": "array",
76
+ "description": "Reserved for future third-party knowledge or framework plugins. Currently unused; declared plugins are ignored with a warning.",
77
+ "items": {
78
+ "type": "string",
79
+ "minLength": 1
80
+ }
81
+ },
82
+ "agents": {
83
+ "type": "array",
84
+ "description": "Explicit list of agent ids to install for. When omitted, the installer auto-detects. Values must be known agent ids (see qa doctor).",
85
+ "items": {
86
+ "type": "string",
87
+ "pattern": "^[a-z][a-z0-9-]*$"
88
+ }
89
+ }
90
+ }
91
+ }
@@ -0,0 +1,11 @@
1
+ """qa_analysis — the pack's deterministic analysis core.
2
+
3
+ Framework-agnostic tooling shared by every diagnostic skill: redaction,
4
+ the evidence and finding model, the failure taxonomy, artifact discovery,
5
+ format parsers (JUnit, HAR), contract validation, and the diff guard.
6
+
7
+ Standard library only. Knows nothing about any test framework; framework
8
+ specifics live in shared/frameworks/<name>/lib and depend on this package.
9
+ """
10
+
11
+ __version__ = "0.1.0"
@@ -0,0 +1,9 @@
1
+ {
2
+ "$comment": "The single source of truth for product attribution in human-readable reports. Every renderer reads this file; no branding string is hardcoded anywhere else in the repository, and scripts/check-branding.mjs fails if one appears. Changing the footer means editing this file only.",
3
+ "projectName": "QA Automation Pack",
4
+ "tagline": "AI-First Deterministic QA Engineering Platform",
5
+ "author": "Abisheik",
6
+ "website": "https://abisheik.dev",
7
+ "attributionPrefix": "Powered by",
8
+ "authorPrefix": "Designed & Developed by"
9
+ }